From 83fb1e36c79aee7f897e5ff0e6eeed1d9d526a14 Mon Sep 17 00:00:00 2001 From: Apple Date: Fri, 8 Jun 2012 19:27:26 +0000 Subject: [PATCH] mDNSResponder-379.27.tar.gz --- Clients/BonjourExample/stdafx.h | 18 +- Clients/ClientCommon.c | 60 +- Clients/DNS-SD.VisualStudio/resource.h | 2 +- Clients/ExplorerPlugin/About.h | 22 +- Clients/ExplorerPlugin/ClassFactory.h | 50 +- Clients/ExplorerPlugin/ExplorerBar.h | 158 +- Clients/ExplorerPlugin/ExplorerBarWindow.h | 414 +- Clients/ExplorerPlugin/ExplorerPlugin.h | 26 +- Clients/ExplorerPlugin/LoginDialog.h | 54 +- Clients/ExplorerPlugin/Resource.h | 4 +- Clients/ExplorerPlugin/StdAfx.h | 16 +- Clients/ExplorerPlugin/resource_dll.h | 2 +- Clients/ExplorerPlugin/resource_loc_res.h | 4 +- Clients/ExplorerPlugin/resource_res.h | 2 +- Clients/FirefoxExtension/CDNSSDService.h | 114 +- Clients/FirefoxExtension/IDNSSDService.h | 122 +- ...efox.png => _internal_bonjour4firefox.png} | Bin ...{listImage.png => _internal_listImage.png} | Bin .../extension/content/bonjour4firefox.xul | 4 +- .../FirefoxExtension/extension/install.rdf | 2 +- ...button.png => _internal_toobar-button.png} | Bin .../extension/skin-darwin/overlay.css | 2 +- ...button.png => _internal_toobar-button.png} | Bin .../extension/skin/overlay.css | 2 +- Clients/FirefoxExtension/resource.h | 2 +- Clients/PrinterSetupWizard/About.h | 12 +- Clients/PrinterSetupWizard/FirstPage.h | 28 +- Clients/PrinterSetupWizard/FourthPage.h | 42 +- Clients/PrinterSetupWizard/Logger.h | 62 +- .../PrinterSetupWizardApp.h | 22 +- .../PrinterSetupWizardSheet.h | 520 +- Clients/PrinterSetupWizard/SecondPage.h | 84 +- Clients/PrinterSetupWizard/ThirdPage.h | 222 +- Clients/PrinterSetupWizard/UtilTypes.h | 500 +- Clients/PrinterSetupWizard/resource.h | 6 +- Clients/PrinterSetupWizard/resource_exe.h | 22 +- Clients/PrinterSetupWizard/resource_loc_res.h | 20 +- Clients/PrinterSetupWizard/resource_res.h | 24 +- Clients/PrinterSetupWizard/stdafx.h | 30 +- Clients/PrinterSetupWizard/tcpxcv.h | 78 +- Clients/dns-sd.c | 2214 +- .../mDNSNetMonitor.VisualStudio/resource.h | 2 +- Makefile | 17 +- mDNSCore/CryptoAlg.c | 256 + mDNSCore/CryptoAlg.h | 58 + mDNSCore/DNSCommon.c | 6046 +++-- mDNSCore/DNSCommon.h | 172 +- mDNSCore/DNSDigest.c | 1850 +- mDNSCore/dnssec.c | 3135 +++ mDNSCore/dnssec.h | 125 + mDNSCore/mDNS.c | 21888 ++++++++-------- mDNSCore/mDNSDebug.h | 114 +- mDNSCore/mDNSEmbeddedAPI.h | 2993 ++- mDNSCore/nsec.c | 1131 + mDNSCore/nsec.h | 28 + mDNSCore/uDNS.c | 8887 +++---- mDNSCore/uDNS.h | 51 +- mDNSMacOS9/Responder.c | 232 +- mDNSMacOS9/Searcher.c | 364 +- mDNSMacOS9/ShowInitIcon.c | 148 +- mDNSMacOS9/SubTypeTester.c | 310 +- mDNSMacOS9/mDNSLibrary.c | 66 +- mDNSMacOS9/mDNSLibraryLoader.c | 72 +- mDNSMacOS9/mDNSMacOS9.c | 990 +- mDNSMacOS9/mDNSMacOS9.h | 46 +- mDNSMacOS9/mDNSPrefix.h | 4 +- mDNSMacOSX/BonjourEvents.c | 1519 +- mDNSMacOSX/CryptoSupport.c | 524 + mDNSMacOSX/{safe_vproc.h => CryptoSupport.h} | 16 +- mDNSMacOSX/DNSServiceDiscovery.c | 98 +- mDNSMacOSX/DNSServiceDiscovery.h | 170 +- mDNSMacOSX/DNSServiceDiscoveryDefines.h | 10 +- mDNSMacOSX/LaunchDaemonInfo.helper.plist | 2 + mDNSMacOSX/LaunchDaemonInfo.plist | 6 + mDNSMacOSX/LegacyNATTraversal.c | 1635 +- mDNSMacOSX/P2PPacketFilter.c | 185 +- mDNSMacOSX/P2PPacketFilter.h | 8 +- .../PreferencePane/ConfigurationAuthority.c | 186 +- .../PreferencePane/ConfigurationAuthority.h | 2 +- .../PreferencePane/ConfigurationRights.h | 4 +- .../PreferencePane/DNSServiceDiscoveryPref.h | 154 +- .../PreferencePane/DNSServiceDiscoveryPref.m | 5 +- .../PreferencePane/PrivilegedOperations.c | 278 +- .../PreferencePane/PrivilegedOperations.h | 28 +- mDNSMacOSX/README.privsep | 7 +- mDNSMacOSX/SamplemDNSClient.c | 441 - mDNSMacOSX/daemon.c | 5035 ++-- mDNSMacOSX/helper-error.h | 4 +- mDNSMacOSX/helper-main.c | 450 +- mDNSMacOSX/helper-server.h | 4 +- mDNSMacOSX/helper-stubs.c | 444 +- mDNSMacOSX/helper.c | 4521 ++-- mDNSMacOSX/helper.h | 73 +- mDNSMacOSX/helpermsg-types.h | 7 +- mDNSMacOSX/helpermsg.defs | 32 +- mDNSMacOSX/ipsec_strerror.h | 60 +- mDNSMacOSX/libpfkey.h | 40 +- mDNSMacOSX/mDNSMacOSX.c | 16095 ++++++------ mDNSMacOSX/mDNSMacOSX.h | 203 +- mDNSMacOSX/mDNSResponder-entitlements.plist | 8 + mDNSMacOSX/mDNSResponder.order | 3 - mDNSMacOSX/mDNSResponder.plist | 31 + mDNSMacOSX/mDNSResponder.sb | 189 +- mDNSMacOSX/mDNSResponder.txt | 55 + .../mDNSResponder.xcodeproj/project.pbxproj | 278 +- mDNSMacOSX/pfkey.c | 2958 +-- mDNSMacOSX/safe_vproc.c | 84 - mDNSPosix/Client.c | 142 +- mDNSPosix/ExampleClientApp.c | 124 +- mDNSPosix/ExampleClientApp.h | 4 +- mDNSPosix/Identify.c | 566 +- mDNSPosix/Makefile | 24 +- mDNSPosix/NetMonitor.c | 1705 +- mDNSPosix/PosixDaemon.c | 304 +- mDNSPosix/ProxyResponder.c | 458 +- mDNSPosix/ReadMe.txt | 10 + mDNSPosix/Responder.c | 780 +- mDNSPosix/Services.txt | 27 +- mDNSPosix/mDNSPosix.c | 2507 +- mDNSPosix/mDNSPosix.h | 36 +- mDNSPosix/mDNSUNP.c | 536 +- mDNSPosix/mDNSUNP.h | 55 +- mDNSPosix/nss_mdns.c | 3946 +-- mDNSShared/CommonServices.h | 2231 +- mDNSShared/DebugServices.c | 5204 ++-- mDNSShared/DebugServices.h | 2300 +- mDNSShared/GenLinkedList.c | 372 +- mDNSShared/GenLinkedList.h | 70 +- mDNSShared/Java/JNISupport.c | 1594 +- mDNSShared/PlatformCommon.c | 302 +- mDNSShared/PlatformCommon.h | 4 +- mDNSShared/dns-sd.1 | 86 +- mDNSShared/dns_sd.h | 703 +- mDNSShared/dnsextd.c | 4982 ++-- mDNSShared/dnsextd.h | 112 +- mDNSShared/dnssd_clientlib.c | 554 +- mDNSShared/dnssd_clientshim.c | 1244 +- mDNSShared/dnssd_clientstub.c | 3634 +-- mDNSShared/dnssd_ipc.c | 216 +- mDNSShared/dnssd_ipc.h | 163 +- mDNSShared/mDNS.1 | 198 - mDNSShared/mDNSDebug.c | 50 +- mDNSShared/mDNSResponder.8 | 6 +- mDNSShared/uds_daemon.c | 8460 +++--- mDNSShared/uds_daemon.h | 41 +- mDNSVxWorks/mDNSVxWorks.c | 3346 +-- mDNSVxWorks/mDNSVxWorks.h | 138 +- mDNSVxWorks/mDNSVxWorksIPv4Only.c | 3048 +-- mDNSVxWorks/mDNSVxWorksIPv4Only.h | 124 +- mDNSWindows/WinVersRes.h | 8 +- mDNSWindows/mDNSWin32.c | 83 +- 151 files changed, 74284 insertions(+), 66016 deletions(-) rename Clients/FirefoxExtension/extension/content/{bonjour4firefox.png => _internal_bonjour4firefox.png} (100%) rename Clients/FirefoxExtension/extension/content/{listImage.png => _internal_listImage.png} (100%) rename Clients/FirefoxExtension/extension/skin-darwin/{toolbar-button.png => _internal_toobar-button.png} (100%) rename Clients/FirefoxExtension/extension/skin/{toolbar-button.png => _internal_toobar-button.png} (100%) create mode 100644 mDNSCore/CryptoAlg.c create mode 100644 mDNSCore/CryptoAlg.h create mode 100644 mDNSCore/dnssec.c create mode 100644 mDNSCore/dnssec.h create mode 100644 mDNSCore/nsec.c create mode 100644 mDNSCore/nsec.h create mode 100644 mDNSMacOSX/CryptoSupport.c rename mDNSMacOSX/{safe_vproc.h => CryptoSupport.h} (72%) delete mode 100644 mDNSMacOSX/SamplemDNSClient.c create mode 100644 mDNSMacOSX/mDNSResponder-entitlements.plist create mode 100644 mDNSMacOSX/mDNSResponder.plist create mode 100644 mDNSMacOSX/mDNSResponder.txt delete mode 100644 mDNSMacOSX/safe_vproc.c delete mode 100644 mDNSShared/mDNS.1 diff --git a/Clients/BonjourExample/stdafx.h b/Clients/BonjourExample/stdafx.h index 2d51905..d820f4a 100644 --- a/Clients/BonjourExample/stdafx.h +++ b/Clients/BonjourExample/stdafx.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,12 +19,12 @@ #pragma once -#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers -#include -#include +#include +#include -#include -#include -#include -#include +#include +#include +#include +#include diff --git a/Clients/ClientCommon.c b/Clients/ClientCommon.c index 812efbe..68f354c 100644 --- a/Clients/ClientCommon.c +++ b/Clients/ClientCommon.c @@ -39,37 +39,37 @@ */ #include -#include // For stdout, stderr +#include // For stdout, stderr #include "ClientCommon.h" const char *GetNextLabel(const char *cstr, char label[64]) - { - char *ptr = label; - while (*cstr && *cstr != '.') // While we have characters in the label... - { - char c = *cstr++; - if (c == '\\' && *cstr) // If we have a backslash, and it's not the last character of the string - { - 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 valid three-digit decimal value, use it - // Note that although ascii nuls are possible in DNS labels - // we're building a C string here so we have no way to represent that - if (val == 0) val = '-'; - if (val <= 255) { c = (char)val; cstr += 2; } - } - } - *ptr++ = c; - if (ptr >= label+64) { label[63] = 0; return(NULL); } // Illegal label more than 63 bytes - } - *ptr = 0; // Null-terminate label text - if (ptr == label) return(NULL); // Illegal empty label - if (*cstr) cstr++; // Skip over the trailing dot (if present) - return(cstr); - } +{ + char *ptr = label; + while (*cstr && *cstr != '.') // While we have characters in the label... + { + char c = *cstr++; + if (c == '\\' && *cstr) // If we have a backslash, and it's not the last character of the string + { + 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 valid three-digit decimal value, use it + // Note that although ascii nuls are possible in DNS labels + // we're building a C string here so we have no way to represent that + if (val == 0) val = '-'; + if (val <= 255) { c = (char)val; cstr += 2; } + } + } + *ptr++ = c; + if (ptr >= label+64) { label[63] = 0; return(NULL); } // Illegal label more than 63 bytes + } + *ptr = 0; // Null-terminate label text + if (ptr == label) return(NULL); // Illegal empty label + if (*cstr) cstr++; // Skip over the trailing dot (if present) + return(cstr); +} diff --git a/Clients/DNS-SD.VisualStudio/resource.h b/Clients/DNS-SD.VisualStudio/resource.h index f51112e..593b5f9 100755 --- a/Clients/DNS-SD.VisualStudio/resource.h +++ b/Clients/DNS-SD.VisualStudio/resource.h @@ -3,7 +3,7 @@ // Used by dns-sd.rc // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 101 diff --git a/Clients/ExplorerPlugin/About.h b/Clients/ExplorerPlugin/About.h index f25270d..494d1f1 100644 --- a/Clients/ExplorerPlugin/About.h +++ b/Clients/ExplorerPlugin/About.h @@ -7,22 +7,22 @@ class CAbout : public CDialog { - DECLARE_DYNAMIC(CAbout) +DECLARE_DYNAMIC(CAbout) public: - CAbout(CWnd* pParent = NULL); // standard constructor - virtual ~CAbout(); +CAbout(CWnd* pParent = NULL); // standard constructor +virtual ~CAbout(); // Dialog Data - enum { IDD = IDD_ABOUT }; +enum { IDD = IDD_ABOUT }; protected: - virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support - virtual HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor); - virtual BOOL OnInitDialog(); - DECLARE_MESSAGE_MAP() +virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support +virtual HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor); +virtual BOOL OnInitDialog(); +DECLARE_MESSAGE_MAP() public: - CStatic m_componentCtrl; - CStatic m_legalCtrl; - CBrush m_bkBrush; +CStatic m_componentCtrl; +CStatic m_legalCtrl; +CBrush m_bkBrush; }; diff --git a/Clients/ExplorerPlugin/ClassFactory.h b/Clients/ExplorerPlugin/ClassFactory.h index 5eb4bbd..cbf5630 100644 --- a/Clients/ExplorerPlugin/ClassFactory.h +++ b/Clients/ExplorerPlugin/ClassFactory.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,34 +18,34 @@ #ifndef __CLASS_FACTORY__ #define __CLASS_FACTORY__ -#include "StdAfx.h" +#include "StdAfx.h" //=========================================================================================================================== // ClassFactory //=========================================================================================================================== -class ClassFactory : public IClassFactory +class ClassFactory : public IClassFactory { - protected: - - DWORD mRefCount; - CLSID mCLSIDObject; - - public: - - ClassFactory( CLSID inCLSID ); - ~ClassFactory( void ); - - // IUnknown methods - - STDMETHODIMP QueryInterface( REFIID inID, LPVOID *outResult ); - STDMETHODIMP_( DWORD ) AddRef( void ); - STDMETHODIMP_( DWORD ) Release( void ); - - // IClassFactory methods - - STDMETHODIMP CreateInstance( LPUNKNOWN inUnknown, REFIID inID, LPVOID *outObject ); - STDMETHODIMP LockServer( BOOL inLock ); +protected: + +DWORD mRefCount; +CLSID mCLSIDObject; + +public: + +ClassFactory( CLSID inCLSID ); +~ClassFactory( void ); + +// IUnknown methods + +STDMETHODIMP QueryInterface( REFIID inID, LPVOID *outResult ); +STDMETHODIMP_( DWORD ) AddRef( void ); +STDMETHODIMP_( DWORD ) Release( void ); + +// IClassFactory methods + +STDMETHODIMP CreateInstance( LPUNKNOWN inUnknown, REFIID inID, LPVOID *outObject ); +STDMETHODIMP LockServer( BOOL inLock ); }; -#endif // __CLASS_FACTORY__ +#endif // __CLASS_FACTORY__ diff --git a/Clients/ExplorerPlugin/ExplorerBar.h b/Clients/ExplorerPlugin/ExplorerBar.h index d1a8444..01d1bee 100644 --- a/Clients/ExplorerPlugin/ExplorerBar.h +++ b/Clients/ExplorerPlugin/ExplorerBar.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,90 +15,90 @@ * limitations under the License. */ -#ifndef __EXPLORER_BAR__ -#define __EXPLORER_BAR__ +#ifndef __EXPLORER_BAR__ +#define __EXPLORER_BAR__ -#include "StdAfx.h" +#include "StdAfx.h" -#include "ExplorerBarWindow.h" -#include "ExplorerPlugin.h" +#include "ExplorerBarWindow.h" +#include "ExplorerPlugin.h" //=========================================================================================================================== // ExplorerBar //=========================================================================================================================== -class ExplorerBar : public IDeskBand, - public IInputObject, - public IObjectWithSite, - public IPersistStream, - public IContextMenu +class ExplorerBar : public IDeskBand, + public IInputObject, + public IObjectWithSite, + public IPersistStream, + public IContextMenu { - protected: - - DWORD mRefCount; - IInputObjectSite * mSite; - IWebBrowser2 * mWebBrowser; - HWND mParentWindow; - BOOL mFocus; - DWORD mViewMode; - DWORD mBandID; - ExplorerBarWindow mWindow; - - public: - - ExplorerBar( void ); - ~ExplorerBar( void ); - - // IUnknown methods - - STDMETHODIMP QueryInterface( REFIID inID, LPVOID *outResult ); - STDMETHODIMP_( DWORD ) AddRef( void ); - STDMETHODIMP_( DWORD ) Release( void ); - - // IOleWindow methods - - STDMETHOD( GetWindow )( HWND *outWindow ); - STDMETHOD( ContextSensitiveHelp )( BOOL inEnterMode ); - - // IDockingWindow methods - - STDMETHOD( ShowDW )( BOOL inShow ); - STDMETHOD( CloseDW )( DWORD inReserved ); - STDMETHOD( ResizeBorderDW )( LPCRECT inBorder, IUnknown *inPunkSite, BOOL inReserved ); - - // IDeskBand methods - - STDMETHOD( GetBandInfo )( DWORD inBandID, DWORD inViewMode, DESKBANDINFO *outInfo ); - - // IInputObject methods - - STDMETHOD( UIActivateIO )( BOOL inActivate, LPMSG inMsg ); - STDMETHOD( HasFocusIO )( void ); - STDMETHOD( TranslateAcceleratorIO )( LPMSG inMsg ); - - // IObjectWithSite methods - - STDMETHOD( SetSite )( IUnknown *inPunkSite ); - STDMETHOD( GetSite )( REFIID inID, LPVOID *outResult ); - - // IPersistStream methods - - STDMETHOD( GetClassID )( LPCLSID outClassID ); - STDMETHOD( IsDirty )( void ); - STDMETHOD( Load )( LPSTREAM inStream ); - STDMETHOD( Save )( LPSTREAM inStream, BOOL inClearDirty ); - STDMETHOD( GetSizeMax )( ULARGE_INTEGER *outSizeMax ); - - // IContextMenu methods - - STDMETHOD( QueryContextMenu )( HMENU hContextMenu, UINT iContextMenuFirstItem, UINT idCmdFirst, UINT idCmdLast, UINT uFlags ); - STDMETHOD( GetCommandString )( UINT_PTR idCmd, UINT uType, UINT* pwReserved, LPSTR pszName, UINT cchMax ); - STDMETHOD( InvokeCommand )( LPCMINVOKECOMMANDINFO lpici ); - - // Other - - OSStatus SetupWindow( void ); - OSStatus GoToURL( const CString &inURL ); +protected: + +DWORD mRefCount; +IInputObjectSite * mSite; +IWebBrowser2 * mWebBrowser; +HWND mParentWindow; +BOOL mFocus; +DWORD mViewMode; +DWORD mBandID; +ExplorerBarWindow mWindow; + +public: + +ExplorerBar( void ); +~ExplorerBar( void ); + +// IUnknown methods + +STDMETHODIMP QueryInterface( REFIID inID, LPVOID *outResult ); +STDMETHODIMP_( DWORD ) AddRef( void ); +STDMETHODIMP_( DWORD ) Release( void ); + +// IOleWindow methods + +STDMETHOD( GetWindow ) ( HWND *outWindow ); +STDMETHOD( ContextSensitiveHelp ) ( BOOL inEnterMode ); + +// IDockingWindow methods + +STDMETHOD( ShowDW ) ( BOOL inShow ); +STDMETHOD( CloseDW ) ( DWORD inReserved ); +STDMETHOD( ResizeBorderDW ) ( LPCRECT inBorder, IUnknown *inPunkSite, BOOL inReserved ); + +// IDeskBand methods + +STDMETHOD( GetBandInfo ) ( DWORD inBandID, DWORD inViewMode, DESKBANDINFO *outInfo ); + +// IInputObject methods + +STDMETHOD( UIActivateIO ) ( BOOL inActivate, LPMSG inMsg ); +STDMETHOD( HasFocusIO ) ( void ); +STDMETHOD( TranslateAcceleratorIO ) ( LPMSG inMsg ); + +// IObjectWithSite methods + +STDMETHOD( SetSite ) ( IUnknown *inPunkSite ); +STDMETHOD( GetSite ) ( REFIID inID, LPVOID *outResult ); + +// IPersistStream methods + +STDMETHOD( GetClassID ) ( LPCLSID outClassID ); +STDMETHOD( IsDirty ) ( void ); +STDMETHOD( Load ) ( LPSTREAM inStream ); +STDMETHOD( Save ) ( LPSTREAM inStream, BOOL inClearDirty ); +STDMETHOD( GetSizeMax ) ( ULARGE_INTEGER *outSizeMax ); + +// IContextMenu methods + +STDMETHOD( QueryContextMenu ) ( HMENU hContextMenu, UINT iContextMenuFirstItem, UINT idCmdFirst, UINT idCmdLast, UINT uFlags ); +STDMETHOD( GetCommandString ) ( UINT_PTR idCmd, UINT uType, UINT* pwReserved, LPSTR pszName, UINT cchMax ); +STDMETHOD( InvokeCommand ) ( LPCMINVOKECOMMANDINFO lpici ); + +// Other + +OSStatus SetupWindow( void ); +OSStatus GoToURL( const CString &inURL ); }; -#endif // __EXPLORER_BAR__ +#endif // __EXPLORER_BAR__ diff --git a/Clients/ExplorerPlugin/ExplorerBarWindow.h b/Clients/ExplorerPlugin/ExplorerBarWindow.h index cda61ec..e816318 100644 --- a/Clients/ExplorerPlugin/ExplorerBarWindow.h +++ b/Clients/ExplorerPlugin/ExplorerBarWindow.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,15 +15,15 @@ * limitations under the License. */ -#ifndef __EXPLORER_BAR_WINDOW__ -#define __EXPLORER_BAR_WINDOW__ +#ifndef __EXPLORER_BAR_WINDOW__ +#define __EXPLORER_BAR_WINDOW__ #pragma once -#include "afxtempl.h" +#include "afxtempl.h" -#include "dns_sd.h" -#include +#include "dns_sd.h" +#include //=========================================================================================================================== // Structures @@ -31,234 +31,234 @@ // Forward Declarations -struct ServiceHandlerEntry; -class ExplorerBarWindow; +struct ServiceHandlerEntry; +class ExplorerBarWindow; // ServiceInfo -struct ServiceInfo +struct ServiceInfo { - CString displayName; - char * name; - char * type; - char * domain; - uint32_t ifi; - HTREEITEM item; - ServiceHandlerEntry * handler; - DWORD refs; - - ServiceInfo( void ) - { - item = NULL; - type = NULL; - domain = NULL; - handler = NULL; - } - - ~ServiceInfo( void ) - { - if( name ) - { - free( name ); - } - if( type ) - { - free( type ); - } - if( domain ) - { - free( domain ); - } - } + CString displayName; + char * name; + char * type; + char * domain; + uint32_t ifi; + HTREEITEM item; + ServiceHandlerEntry * handler; + DWORD refs; + + ServiceInfo( void ) + { + item = NULL; + type = NULL; + domain = NULL; + handler = NULL; + } + + ~ServiceInfo( void ) + { + if( name ) + { + free( name ); + } + if( type ) + { + free( type ); + } + if( domain ) + { + free( domain ); + } + } }; -typedef CArray < ServiceInfo *, ServiceInfo * > ServiceInfoArray; +typedef CArray < ServiceInfo *, ServiceInfo * > ServiceInfoArray; // TextRecord -struct TextRecord +struct TextRecord { - uint8_t * mData; - uint16_t mSize; - - TextRecord( void ) - { - mData = NULL; - mSize = 0; - } - - ~TextRecord( void ) - { - if( mData ) - { - free( mData ); - } - } - - void GetData( void *outData, uint16_t *outSize ) - { - if( outData ) - { - *( (void **) outData ) = mData; - } - if( outSize ) - { - *outSize = mSize; - } - } - - OSStatus SetData( const void *inData, uint16_t inSize ) - { - OSStatus err; - uint8_t * newData; - - newData = (uint8_t *) malloc( inSize ); - require_action( newData, exit, err = kNoMemoryErr ); - memcpy( newData, inData, inSize ); - - if( mData ) - { - free( mData ); - } - mData = newData; - mSize = inSize; - err = kNoErr; - - exit: - return( err ); - } + uint8_t * mData; + uint16_t mSize; + + TextRecord( void ) + { + mData = NULL; + mSize = 0; + } + + ~TextRecord( void ) + { + if( mData ) + { + free( mData ); + } + } + + void GetData( void *outData, uint16_t *outSize ) + { + if( outData ) + { + *( (void **) outData ) = mData; + } + if( outSize ) + { + *outSize = mSize; + } + } + + OSStatus SetData( const void *inData, uint16_t inSize ) + { + OSStatus err; + uint8_t * newData; + + newData = (uint8_t *) malloc( inSize ); + require_action( newData, exit, err = kNoMemoryErr ); + memcpy( newData, inData, inSize ); + + if( mData ) + { + free( mData ); + } + mData = newData; + mSize = inSize; + err = kNoErr; + +exit: + return( err ); + } }; // ResolveInfo -struct ResolveInfo +struct ResolveInfo { - CString host; - uint16_t port; - uint32_t ifi; - TextRecord txt; - ServiceHandlerEntry * handler; + CString host; + uint16_t port; + uint32_t ifi; + TextRecord txt; + ServiceHandlerEntry * handler; }; // ServiceHandlerEntry -struct ServiceHandlerEntry +struct ServiceHandlerEntry { - const char * type; - const char * urlScheme; - DNSServiceRef ref; - ServiceInfoArray array; - ExplorerBarWindow * obj; - bool needsLogin; - - ServiceHandlerEntry( void ) - { - type = NULL; - urlScheme = NULL; - ref = NULL; - obj = NULL; - needsLogin = false; - } - - ~ServiceHandlerEntry( void ) - { - int i; - int n; - - n = (int) array.GetSize(); - for( i = 0; i < n; ++i ) - { - delete array[ i ]; - } - } + const char * type; + const char * urlScheme; + DNSServiceRef ref; + ServiceInfoArray array; + ExplorerBarWindow * obj; + bool needsLogin; + + ServiceHandlerEntry( void ) + { + type = NULL; + urlScheme = NULL; + ref = NULL; + obj = NULL; + needsLogin = false; + } + + ~ServiceHandlerEntry( void ) + { + int i; + int n; + + n = (int) array.GetSize(); + for( i = 0; i < n; ++i ) + { + delete array[ i ]; + } + } }; -typedef CArray < ServiceHandlerEntry *, ServiceHandlerEntry * > ServiceHandlerArray; +typedef CArray < ServiceHandlerEntry *, ServiceHandlerEntry * > ServiceHandlerArray; //=========================================================================================================================== // ExplorerBarWindow //=========================================================================================================================== -class ExplorerBar; // Forward Declaration +class ExplorerBar; // Forward Declaration -class ExplorerBarWindow : public CWnd +class ExplorerBarWindow : public CWnd { - protected: - - ExplorerBar * mOwner; - CTreeCtrl mTree; - - ServiceHandlerArray mServiceHandlers; - DNSServiceRef mResolveServiceRef; - - public: - - ExplorerBarWindow( void ); - virtual ~ExplorerBarWindow( void ); - - protected: - - // General - - afx_msg int OnCreate( LPCREATESTRUCT inCreateStruct ); - afx_msg void OnDestroy( void ); - afx_msg void OnSize( UINT inType, int inX, int inY ); - afx_msg void OnDoubleClick( NMHDR *inNMHDR, LRESULT *outResult ); - afx_msg LRESULT OnServiceEvent( WPARAM inWParam, LPARAM inLParam ); - - // Browsing - - static void DNSSD_API - BrowseCallBack( - DNSServiceRef inRef, - DNSServiceFlags inFlags, - uint32_t inInterfaceIndex, - DNSServiceErrorType inErrorCode, - const char * inName, - const char * inType, - const char * inDomain, - void * inContext ); - LONG OnServiceAdd( ServiceInfo * service ); - LONG OnServiceRemove( ServiceInfo * service ); - - // Resolving - - OSStatus StartResolve( ServiceInfo *inService ); - void StopResolve( void ); - - - void Stop( DNSServiceRef ref ); - - - static void DNSSD_API - ResolveCallBack( - DNSServiceRef inRef, - DNSServiceFlags inFlags, - uint32_t inInterfaceIndex, - DNSServiceErrorType inErrorCode, - const char * inFullName, - const char * inHostName, - uint16_t inPort, - uint16_t inTXTSize, - const char * inTXT, - void * inContext ); - LONG OnResolve( ResolveInfo * resolve ); - - // Accessors - - public: - - ExplorerBar * GetOwner( void ) const { return( mOwner ); } - void SetOwner( ExplorerBar *inOwner ) { mOwner = inOwner; } - - DECLARE_MESSAGE_MAP() - private: - - typedef std::list< DNSServiceRef > ServiceRefList; - - HTREEITEM m_about; - ServiceRefList m_serviceRefs; - CImageList m_imageList; +protected: + +ExplorerBar * mOwner; +CTreeCtrl mTree; + +ServiceHandlerArray mServiceHandlers; +DNSServiceRef mResolveServiceRef; + +public: + +ExplorerBarWindow( void ); +virtual ~ExplorerBarWindow( void ); + +protected: + +// General + +afx_msg int OnCreate( LPCREATESTRUCT inCreateStruct ); +afx_msg void OnDestroy( void ); +afx_msg void OnSize( UINT inType, int inX, int inY ); +afx_msg void OnDoubleClick( NMHDR *inNMHDR, LRESULT *outResult ); +afx_msg LRESULT OnServiceEvent( WPARAM inWParam, LPARAM inLParam ); + +// Browsing + +static void DNSSD_API +BrowseCallBack( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + const char * inType, + const char * inDomain, + void * inContext ); +LONG OnServiceAdd( ServiceInfo * service ); +LONG OnServiceRemove( ServiceInfo * service ); + +// Resolving + +OSStatus StartResolve( ServiceInfo *inService ); +void StopResolve( void ); + + +void Stop( DNSServiceRef ref ); + + +static void DNSSD_API +ResolveCallBack( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inFullName, + const char * inHostName, + uint16_t inPort, + uint16_t inTXTSize, + const char * inTXT, + void * inContext ); +LONG OnResolve( ResolveInfo * resolve ); + +// Accessors + +public: + +ExplorerBar * GetOwner( void ) const { return( mOwner ); } +void SetOwner( ExplorerBar *inOwner ) { mOwner = inOwner; } + +DECLARE_MESSAGE_MAP() +private: + +typedef std::list< DNSServiceRef > ServiceRefList; + +HTREEITEM m_about; +ServiceRefList m_serviceRefs; +CImageList m_imageList; }; -#endif // __EXPLORER_BAR_WINDOW__ +#endif // __EXPLORER_BAR_WINDOW__ diff --git a/Clients/ExplorerPlugin/ExplorerPlugin.h b/Clients/ExplorerPlugin/ExplorerPlugin.h index 9b87d97..9fa56ae 100644 --- a/Clients/ExplorerPlugin/ExplorerPlugin.h +++ b/Clients/ExplorerPlugin/ExplorerPlugin.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,26 +22,26 @@ //=========================================================================================================================== // {9999A076-A9E2-4c99-8A2B-632FC9429223} -DEFINE_GUID(CLSID_ExplorerBar, -0x9999a076, 0xa9e2, 0x4c99, 0x8a, 0x2b, 0x63, 0x2f, 0xc9, 0x42, 0x92, 0x23); +DEFINE_GUID(CLSID_ExplorerBar, + 0x9999a076, 0xa9e2, 0x4c99, 0x8a, 0x2b, 0x63, 0x2f, 0xc9, 0x42, 0x92, 0x23); -extern HINSTANCE gInstance; -extern int gDLLRefCount; -extern HINSTANCE GetNonLocalizedResources(); -extern HINSTANCE GetLocalizedResources(); +extern HINSTANCE gInstance; +extern int gDLLRefCount; +extern HINSTANCE GetNonLocalizedResources(); +extern HINSTANCE GetLocalizedResources(); class CExplorerPluginApp : public CWinApp { public: - CExplorerPluginApp(); - virtual ~CExplorerPluginApp(); +CExplorerPluginApp(); +virtual ~CExplorerPluginApp(); protected: - virtual BOOL InitInstance(); - virtual int ExitInstance(); +virtual BOOL InitInstance(); +virtual int ExitInstance(); - DECLARE_DYNAMIC(CExplorerPluginApp); +DECLARE_DYNAMIC(CExplorerPluginApp); }; diff --git a/Clients/ExplorerPlugin/LoginDialog.h b/Clients/ExplorerPlugin/LoginDialog.h index e53beb6..3aeb696 100644 --- a/Clients/ExplorerPlugin/LoginDialog.h +++ b/Clients/ExplorerPlugin/LoginDialog.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,39 +15,39 @@ * limitations under the License. */ -#ifndef __LOGIN_DIALOG__ -#define __LOGIN_DIALOG__ +#ifndef __LOGIN_DIALOG__ +#define __LOGIN_DIALOG__ #pragma once -#include "Resource.h" +#include "Resource.h" //=========================================================================================================================== // LoginDialog //=========================================================================================================================== -class LoginDialog : public CDialog +class LoginDialog : public CDialog { - protected: - - CString mUsername; - CString mPassword; - - public: - - enum { IDD = IDD_LOGIN }; - - LoginDialog( CWnd *inParent = NULL ); - - virtual BOOL GetLogin( CString &outUsername, CString &outPassword ); - - protected: - - virtual BOOL OnInitDialog( void ); - virtual void DoDataExchange( CDataExchange *inDX ); - virtual void OnOK( void ); - - DECLARE_MESSAGE_MAP() +protected: + +CString mUsername; +CString mPassword; + +public: + +enum { IDD = IDD_LOGIN }; + +LoginDialog( CWnd *inParent = NULL ); + +virtual BOOL GetLogin( CString &outUsername, CString &outPassword ); + +protected: + +virtual BOOL OnInitDialog( void ); +virtual void DoDataExchange( CDataExchange *inDX ); +virtual void OnOK( void ); + +DECLARE_MESSAGE_MAP() }; -#endif // __LOGIN_DIALOG__ +#endif // __LOGIN_DIALOG__ diff --git a/Clients/ExplorerPlugin/Resource.h b/Clients/ExplorerPlugin/Resource.h index 7235f38..aff47a4 100644 --- a/Clients/ExplorerPlugin/Resource.h +++ b/Clients/ExplorerPlugin/Resource.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/Clients/ExplorerPlugin/StdAfx.h b/Clients/ExplorerPlugin/StdAfx.h index 69e8216..2960892 100644 --- a/Clients/ExplorerPlugin/StdAfx.h +++ b/Clients/ExplorerPlugin/StdAfx.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -21,21 +21,21 @@ #pragma once #ifndef VC_EXTRALEAN -#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers #endif #if !defined(_WSPIAPI_COUNTOF) -# define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0])) +# define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0])) #endif #include // MFC core and standard components #include // MFC extensions -#include // MFC support for Internet Explorer 4 Common Controls +#include // MFC support for Internet Explorer 4 Common Controls #ifndef _AFX_NO_AFXCMN_SUPPORT - #include // MFC support for Windows Common Controls + #include // MFC support for Windows Common Controls #endif #include -#include // MFC socket extensions +#include // MFC socket extensions -#endif // __STDAFX__ +#endif // __STDAFX__ diff --git a/Clients/ExplorerPlugin/resource_dll.h b/Clients/ExplorerPlugin/resource_dll.h index 952254f..fc3c0a9 100755 --- a/Clients/ExplorerPlugin/resource_dll.h +++ b/Clients/ExplorerPlugin/resource_dll.h @@ -15,7 +15,7 @@ #define ID_Menu 40001 // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 119 diff --git a/Clients/ExplorerPlugin/resource_loc_res.h b/Clients/ExplorerPlugin/resource_loc_res.h index a94b51c..6fc2d09 100755 --- a/Clients/ExplorerPlugin/resource_loc_res.h +++ b/Clients/ExplorerPlugin/resource_loc_res.h @@ -10,7 +10,7 @@ #define IDD_ABOUT 118 #define IDR_CONTEXT_MENU 120 #define IDD_LOGIN 145 -#define IDC_ABOUT_BACKGROUND 146 +#define IDC_ABOUT_BACKGROUND 146 #define IDS_ABOUT 147 #define IDS_ABOUT_URL 148 #define IDC_COMPONENT 1001 @@ -21,7 +21,7 @@ #define ID_Menu 40001 // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 119 diff --git a/Clients/ExplorerPlugin/resource_res.h b/Clients/ExplorerPlugin/resource_res.h index 0fcb36f..f2d4abd 100755 --- a/Clients/ExplorerPlugin/resource_res.h +++ b/Clients/ExplorerPlugin/resource_res.h @@ -19,7 +19,7 @@ #define ID_Menu 40001 // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 119 diff --git a/Clients/FirefoxExtension/CDNSSDService.h b/Clients/FirefoxExtension/CDNSSDService.h index f22ee7b..33eaa71 100755 --- a/Clients/FirefoxExtension/CDNSSDService.h +++ b/Clients/FirefoxExtension/CDNSSDService.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -39,65 +39,65 @@ class CDNSSDService : public IDNSSDService, nsIRunnable { public: - NS_DECL_ISUPPORTS - NS_DECL_IDNSSDSERVICE - NS_DECL_NSIRUNNABLE +NS_DECL_ISUPPORTS +NS_DECL_IDNSSDSERVICE +NS_DECL_NSIRUNNABLE - CDNSSDService(); - CDNSSDService( DNSServiceRef mainRef, nsISupports * listener ); +CDNSSDService(); +CDNSSDService( DNSServiceRef mainRef, nsISupports * listener ); + +virtual ~CDNSSDService(); - virtual ~CDNSSDService(); - private: - static void DNSSD_API - BrowseReply - ( - DNSServiceRef sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceErrorType errorCode, - const char * serviceName, - const char * regtype, - const char * replyDomain, - void * context - ); - - static void DNSSD_API - ResolveReply - ( - DNSServiceRef sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceErrorType errorCode, - const char * fullname, - const char * hosttarget, - uint16_t port, - uint16_t txtLen, - const unsigned char * txtRecord, - void * context - ); - - static void - Read - ( - void * arg - ); - - nsresult - SetupNotifications(); - - void - Cleanup(); - - char m_master; - PRThreadPool * m_threadPool; - DNSServiceRef m_mainRef; - DNSServiceRef m_subRef; - nsISupports * m_listener; - PRFileDesc * m_fileDesc; - PRJobIoDesc m_iod; - PRJob * m_job; +static void DNSSD_API +BrowseReply +( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char * serviceName, + const char * regtype, + const char * replyDomain, + void * context +); + +static void DNSSD_API +ResolveReply +( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char * fullname, + const char * hosttarget, + uint16_t port, + uint16_t txtLen, + const unsigned char * txtRecord, + void * context +); + +static void +Read +( + void * arg +); + +nsresult +SetupNotifications(); + +void +Cleanup(); + +char m_master; +PRThreadPool * m_threadPool; +DNSServiceRef m_mainRef; +DNSServiceRef m_subRef; +nsISupports * m_listener; +PRFileDesc * m_fileDesc; +PRJobIoDesc m_iod; +PRJob * m_job; }; diff --git a/Clients/FirefoxExtension/IDNSSDService.h b/Clients/FirefoxExtension/IDNSSDService.h index 0df0aec..e4784d4 100755 --- a/Clients/FirefoxExtension/IDNSSDService.h +++ b/Clients/FirefoxExtension/IDNSSDService.h @@ -21,32 +21,32 @@ class IDNSSDService; /* forward declaration */ #define IDNSSDBROWSELISTENER_IID_STR "27346495-a1ed-458a-a5bc-587df9a26b4f" #define IDNSSDBROWSELISTENER_IID \ - {0x27346495, 0xa1ed, 0x458a, \ - { 0xa5, 0xbc, 0x58, 0x7d, 0xf9, 0xa2, 0x6b, 0x4f }} + {0x27346495, 0xa1ed, 0x458a, \ + { 0xa5, 0xbc, 0x58, 0x7d, 0xf9, 0xa2, 0x6b, 0x4f }} class NS_NO_VTABLE NS_SCRIPTABLE IDNSSDBrowseListener : public nsISupports { - public: +public: - NS_DECLARE_STATIC_IID_ACCESSOR(IDNSSDBROWSELISTENER_IID) +NS_DECLARE_STATIC_IID_ACCESSOR(IDNSSDBROWSELISTENER_IID) - /* void onBrowse (in IDNSSDService service, in boolean add, in long interfaceIndex, in long error, in AString serviceName, in AString regtype, in AString domain); */ - NS_SCRIPTABLE NS_IMETHOD OnBrowse(IDNSSDService *service, PRBool add, PRInt32 interfaceIndex, PRInt32 error, const nsAString & serviceName, const nsAString & regtype, const nsAString & domain) = 0; +/* void onBrowse (in IDNSSDService service, in boolean add, in long interfaceIndex, in long error, in AString serviceName, in AString regtype, in AString domain); */ +NS_SCRIPTABLE NS_IMETHOD OnBrowse(IDNSSDService *service, PRBool add, PRInt32 interfaceIndex, PRInt32 error, const nsAString & serviceName, const nsAString & regtype, const nsAString & domain) = 0; }; - NS_DEFINE_STATIC_IID_ACCESSOR(IDNSSDBrowseListener, IDNSSDBROWSELISTENER_IID) +NS_DEFINE_STATIC_IID_ACCESSOR(IDNSSDBrowseListener, IDNSSDBROWSELISTENER_IID) /* Use this macro when declaring classes that implement this interface. */ #define NS_DECL_IDNSSDBROWSELISTENER \ - NS_SCRIPTABLE NS_IMETHOD OnBrowse(IDNSSDService *service, PRBool add, PRInt32 interfaceIndex, PRInt32 error, const nsAString & serviceName, const nsAString & regtype, const nsAString & domain); + NS_SCRIPTABLE NS_IMETHOD OnBrowse(IDNSSDService *service, PRBool add, PRInt32 interfaceIndex, PRInt32 error, const nsAString &serviceName, const nsAString ®type, const nsAString &domain); /* Use this macro to declare functions that forward the behavior of this interface to another object. */ #define NS_FORWARD_IDNSSDBROWSELISTENER(_to) \ - NS_SCRIPTABLE NS_IMETHOD OnBrowse(IDNSSDService *service, PRBool add, PRInt32 interfaceIndex, PRInt32 error, const nsAString & serviceName, const nsAString & regtype, const nsAString & domain) { return _to OnBrowse(service, add, interfaceIndex, error, serviceName, regtype, domain); } + NS_SCRIPTABLE NS_IMETHOD OnBrowse(IDNSSDService *service, PRBool add, PRInt32 interfaceIndex, PRInt32 error, const nsAString &serviceName, const nsAString ®type, const nsAString &domain) { return _to OnBrowse(service, add, interfaceIndex, error, serviceName, regtype, domain); } /* Use this macro to declare functions that forward the behavior of this interface to another object in a safe way. */ #define NS_FORWARD_SAFE_IDNSSDBROWSELISTENER(_to) \ - NS_SCRIPTABLE NS_IMETHOD OnBrowse(IDNSSDService *service, PRBool add, PRInt32 interfaceIndex, PRInt32 error, const nsAString & serviceName, const nsAString & regtype, const nsAString & domain) { return !_to ? NS_ERROR_NULL_POINTER : _to->OnBrowse(service, add, interfaceIndex, error, serviceName, regtype, domain); } + NS_SCRIPTABLE NS_IMETHOD OnBrowse(IDNSSDService *service, PRBool add, PRInt32 interfaceIndex, PRInt32 error, const nsAString &serviceName, const nsAString ®type, const nsAString &domain) { return !_to ? NS_ERROR_NULL_POINTER : _to->OnBrowse(service, add, interfaceIndex, error, serviceName, regtype, domain); } #if 0 /* Use the code below as a template for the implementation class for this interface. */ @@ -55,16 +55,16 @@ class NS_NO_VTABLE NS_SCRIPTABLE IDNSSDBrowseListener : public nsISupports { class _MYCLASS_ : public IDNSSDBrowseListener { public: - NS_DECL_ISUPPORTS - NS_DECL_IDNSSDBROWSELISTENER +NS_DECL_ISUPPORTS +NS_DECL_IDNSSDBROWSELISTENER - _MYCLASS_(); +_MYCLASS_(); private: - ~_MYCLASS_(); +~_MYCLASS_(); protected: - /* additional members */ +/* additional members */ }; /* Implementation file */ @@ -72,12 +72,12 @@ NS_IMPL_ISUPPORTS1(_MYCLASS_, IDNSSDBrowseListener) _MYCLASS_::_MYCLASS_() { - /* member initializers and constructor code */ + /* member initializers and constructor code */ } _MYCLASS_::~_MYCLASS_() { - /* destructor code */ + /* destructor code */ } /* void onBrowse (in IDNSSDService service, in boolean add, in long interfaceIndex, in long error, in AString serviceName, in AString regtype, in AString domain); */ @@ -94,32 +94,32 @@ NS_IMETHODIMP _MYCLASS_::OnBrowse(IDNSSDService *service, PRBool add, PRInt32 in #define IDNSSDRESOLVELISTENER_IID_STR "6620e18f-47f3-47c6-941f-126a5fd4fcf7" #define IDNSSDRESOLVELISTENER_IID \ - {0x6620e18f, 0x47f3, 0x47c6, \ - { 0x94, 0x1f, 0x12, 0x6a, 0x5f, 0xd4, 0xfc, 0xf7 }} + {0x6620e18f, 0x47f3, 0x47c6, \ + { 0x94, 0x1f, 0x12, 0x6a, 0x5f, 0xd4, 0xfc, 0xf7 }} class NS_NO_VTABLE NS_SCRIPTABLE IDNSSDResolveListener : public nsISupports { - public: +public: - NS_DECLARE_STATIC_IID_ACCESSOR(IDNSSDRESOLVELISTENER_IID) +NS_DECLARE_STATIC_IID_ACCESSOR(IDNSSDRESOLVELISTENER_IID) - /* void onResolve (in IDNSSDService service, in long interfaceIndex, in long error, in AString fullname, in AString host, in short port, in AString path); */ - NS_SCRIPTABLE NS_IMETHOD OnResolve(IDNSSDService *service, PRInt32 interfaceIndex, PRInt32 error, const nsAString & fullname, const nsAString & host, PRInt16 port, const nsAString & path) = 0; +/* void onResolve (in IDNSSDService service, in long interfaceIndex, in long error, in AString fullname, in AString host, in short port, in AString path); */ +NS_SCRIPTABLE NS_IMETHOD OnResolve(IDNSSDService *service, PRInt32 interfaceIndex, PRInt32 error, const nsAString & fullname, const nsAString & host, PRInt16 port, const nsAString & path) = 0; }; - NS_DEFINE_STATIC_IID_ACCESSOR(IDNSSDResolveListener, IDNSSDRESOLVELISTENER_IID) +NS_DEFINE_STATIC_IID_ACCESSOR(IDNSSDResolveListener, IDNSSDRESOLVELISTENER_IID) /* Use this macro when declaring classes that implement this interface. */ #define NS_DECL_IDNSSDRESOLVELISTENER \ - NS_SCRIPTABLE NS_IMETHOD OnResolve(IDNSSDService *service, PRInt32 interfaceIndex, PRInt32 error, const nsAString & fullname, const nsAString & host, PRInt16 port, const nsAString & path); + NS_SCRIPTABLE NS_IMETHOD OnResolve(IDNSSDService *service, PRInt32 interfaceIndex, PRInt32 error, const nsAString &fullname, const nsAString &host, PRInt16 port, const nsAString &path); /* Use this macro to declare functions that forward the behavior of this interface to another object. */ #define NS_FORWARD_IDNSSDRESOLVELISTENER(_to) \ - NS_SCRIPTABLE NS_IMETHOD OnResolve(IDNSSDService *service, PRInt32 interfaceIndex, PRInt32 error, const nsAString & fullname, const nsAString & host, PRInt16 port, const nsAString & path) { return _to OnResolve(service, interfaceIndex, error, fullname, host, port, path); } + NS_SCRIPTABLE NS_IMETHOD OnResolve(IDNSSDService *service, PRInt32 interfaceIndex, PRInt32 error, const nsAString &fullname, const nsAString &host, PRInt16 port, const nsAString &path) { return _to OnResolve(service, interfaceIndex, error, fullname, host, port, path); } /* Use this macro to declare functions that forward the behavior of this interface to another object in a safe way. */ #define NS_FORWARD_SAFE_IDNSSDRESOLVELISTENER(_to) \ - NS_SCRIPTABLE NS_IMETHOD OnResolve(IDNSSDService *service, PRInt32 interfaceIndex, PRInt32 error, const nsAString & fullname, const nsAString & host, PRInt16 port, const nsAString & path) { return !_to ? NS_ERROR_NULL_POINTER : _to->OnResolve(service, interfaceIndex, error, fullname, host, port, path); } + NS_SCRIPTABLE NS_IMETHOD OnResolve(IDNSSDService *service, PRInt32 interfaceIndex, PRInt32 error, const nsAString &fullname, const nsAString &host, PRInt16 port, const nsAString &path) { return !_to ? NS_ERROR_NULL_POINTER : _to->OnResolve(service, interfaceIndex, error, fullname, host, port, path); } #if 0 /* Use the code below as a template for the implementation class for this interface. */ @@ -128,16 +128,16 @@ class NS_NO_VTABLE NS_SCRIPTABLE IDNSSDResolveListener : public nsISupports { class _MYCLASS_ : public IDNSSDResolveListener { public: - NS_DECL_ISUPPORTS - NS_DECL_IDNSSDRESOLVELISTENER +NS_DECL_ISUPPORTS +NS_DECL_IDNSSDRESOLVELISTENER - _MYCLASS_(); +_MYCLASS_(); private: - ~_MYCLASS_(); +~_MYCLASS_(); protected: - /* additional members */ +/* additional members */ }; /* Implementation file */ @@ -145,12 +145,12 @@ NS_IMPL_ISUPPORTS1(_MYCLASS_, IDNSSDResolveListener) _MYCLASS_::_MYCLASS_() { - /* member initializers and constructor code */ + /* member initializers and constructor code */ } _MYCLASS_::~_MYCLASS_() { - /* destructor code */ + /* destructor code */ } /* void onResolve (in IDNSSDService service, in long interfaceIndex, in long error, in AString fullname, in AString host, in short port, in AString path); */ @@ -167,44 +167,44 @@ NS_IMETHODIMP _MYCLASS_::OnResolve(IDNSSDService *service, PRInt32 interfaceInde #define IDNSSDSERVICE_IID_STR "3a3539ff-f8d8-40b4-8d02-5ea73c51fa12" #define IDNSSDSERVICE_IID \ - {0x3a3539ff, 0xf8d8, 0x40b4, \ - { 0x8d, 0x02, 0x5e, 0xa7, 0x3c, 0x51, 0xfa, 0x12 }} + {0x3a3539ff, 0xf8d8, 0x40b4, \ + { 0x8d, 0x02, 0x5e, 0xa7, 0x3c, 0x51, 0xfa, 0x12 }} class NS_NO_VTABLE NS_SCRIPTABLE IDNSSDService : public nsISupports { - public: +public: - NS_DECLARE_STATIC_IID_ACCESSOR(IDNSSDSERVICE_IID) +NS_DECLARE_STATIC_IID_ACCESSOR(IDNSSDSERVICE_IID) - /* IDNSSDService browse (in long interfaceIndex, in AString regtype, in AString domain, in IDNSSDBrowseListener listener); */ - NS_SCRIPTABLE NS_IMETHOD Browse(PRInt32 interfaceIndex, const nsAString & regtype, const nsAString & domain, IDNSSDBrowseListener *listener, IDNSSDService **_retval NS_OUTPARAM) = 0; +/* IDNSSDService browse (in long interfaceIndex, in AString regtype, in AString domain, in IDNSSDBrowseListener listener); */ +NS_SCRIPTABLE NS_IMETHOD Browse(PRInt32 interfaceIndex, const nsAString & regtype, const nsAString & domain, IDNSSDBrowseListener *listener, IDNSSDService **_retval NS_OUTPARAM) = 0; - /* IDNSSDService resolve (in long interfaceIndex, in AString name, in AString regtype, in AString domain, in IDNSSDResolveListener listener); */ - NS_SCRIPTABLE NS_IMETHOD Resolve(PRInt32 interfaceIndex, const nsAString & name, const nsAString & regtype, const nsAString & domain, IDNSSDResolveListener *listener, IDNSSDService **_retval NS_OUTPARAM) = 0; +/* IDNSSDService resolve (in long interfaceIndex, in AString name, in AString regtype, in AString domain, in IDNSSDResolveListener listener); */ +NS_SCRIPTABLE NS_IMETHOD Resolve(PRInt32 interfaceIndex, const nsAString & name, const nsAString & regtype, const nsAString & domain, IDNSSDResolveListener *listener, IDNSSDService **_retval NS_OUTPARAM) = 0; - /* void stop (); */ - NS_SCRIPTABLE NS_IMETHOD Stop(void) = 0; +/* void stop (); */ +NS_SCRIPTABLE NS_IMETHOD Stop(void) = 0; }; - NS_DEFINE_STATIC_IID_ACCESSOR(IDNSSDService, IDNSSDSERVICE_IID) +NS_DEFINE_STATIC_IID_ACCESSOR(IDNSSDService, IDNSSDSERVICE_IID) /* Use this macro when declaring classes that implement this interface. */ #define NS_DECL_IDNSSDSERVICE \ - NS_SCRIPTABLE NS_IMETHOD Browse(PRInt32 interfaceIndex, const nsAString & regtype, const nsAString & domain, IDNSSDBrowseListener *listener, IDNSSDService **_retval NS_OUTPARAM); \ - NS_SCRIPTABLE NS_IMETHOD Resolve(PRInt32 interfaceIndex, const nsAString & name, const nsAString & regtype, const nsAString & domain, IDNSSDResolveListener *listener, IDNSSDService **_retval NS_OUTPARAM); \ - NS_SCRIPTABLE NS_IMETHOD Stop(void); + NS_SCRIPTABLE NS_IMETHOD Browse(PRInt32 interfaceIndex, const nsAString ®type, const nsAString &domain, IDNSSDBrowseListener *listener, IDNSSDService **_retval NS_OUTPARAM); \ + NS_SCRIPTABLE NS_IMETHOD Resolve(PRInt32 interfaceIndex, const nsAString &name, const nsAString ®type, const nsAString &domain, IDNSSDResolveListener *listener, IDNSSDService **_retval NS_OUTPARAM); \ + NS_SCRIPTABLE NS_IMETHOD Stop(void); /* Use this macro to declare functions that forward the behavior of this interface to another object. */ #define NS_FORWARD_IDNSSDSERVICE(_to) \ - NS_SCRIPTABLE NS_IMETHOD Browse(PRInt32 interfaceIndex, const nsAString & regtype, const nsAString & domain, IDNSSDBrowseListener *listener, IDNSSDService **_retval NS_OUTPARAM) { return _to Browse(interfaceIndex, regtype, domain, listener, _retval); } \ - NS_SCRIPTABLE NS_IMETHOD Resolve(PRInt32 interfaceIndex, const nsAString & name, const nsAString & regtype, const nsAString & domain, IDNSSDResolveListener *listener, IDNSSDService **_retval NS_OUTPARAM) { return _to Resolve(interfaceIndex, name, regtype, domain, listener, _retval); } \ - NS_SCRIPTABLE NS_IMETHOD Stop(void) { return _to Stop(); } + NS_SCRIPTABLE NS_IMETHOD Browse(PRInt32 interfaceIndex, const nsAString ®type, const nsAString &domain, IDNSSDBrowseListener *listener, IDNSSDService **_retval NS_OUTPARAM) { return _to Browse(interfaceIndex, regtype, domain, listener, _retval); } \ + NS_SCRIPTABLE NS_IMETHOD Resolve(PRInt32 interfaceIndex, const nsAString &name, const nsAString ®type, const nsAString &domain, IDNSSDResolveListener *listener, IDNSSDService **_retval NS_OUTPARAM) { return _to Resolve(interfaceIndex, name, regtype, domain, listener, _retval); } \ + NS_SCRIPTABLE NS_IMETHOD Stop(void) { return _to Stop(); } /* Use this macro to declare functions that forward the behavior of this interface to another object in a safe way. */ #define NS_FORWARD_SAFE_IDNSSDSERVICE(_to) \ - NS_SCRIPTABLE NS_IMETHOD Browse(PRInt32 interfaceIndex, const nsAString & regtype, const nsAString & domain, IDNSSDBrowseListener *listener, IDNSSDService **_retval NS_OUTPARAM) { return !_to ? NS_ERROR_NULL_POINTER : _to->Browse(interfaceIndex, regtype, domain, listener, _retval); } \ - NS_SCRIPTABLE NS_IMETHOD Resolve(PRInt32 interfaceIndex, const nsAString & name, const nsAString & regtype, const nsAString & domain, IDNSSDResolveListener *listener, IDNSSDService **_retval NS_OUTPARAM) { return !_to ? NS_ERROR_NULL_POINTER : _to->Resolve(interfaceIndex, name, regtype, domain, listener, _retval); } \ - NS_SCRIPTABLE NS_IMETHOD Stop(void) { return !_to ? NS_ERROR_NULL_POINTER : _to->Stop(); } + NS_SCRIPTABLE NS_IMETHOD Browse(PRInt32 interfaceIndex, const nsAString ®type, const nsAString &domain, IDNSSDBrowseListener *listener, IDNSSDService **_retval NS_OUTPARAM) { return !_to ? NS_ERROR_NULL_POINTER : _to->Browse(interfaceIndex, regtype, domain, listener, _retval); } \ + NS_SCRIPTABLE NS_IMETHOD Resolve(PRInt32 interfaceIndex, const nsAString &name, const nsAString ®type, const nsAString &domain, IDNSSDResolveListener *listener, IDNSSDService **_retval NS_OUTPARAM) { return !_to ? NS_ERROR_NULL_POINTER : _to->Resolve(interfaceIndex, name, regtype, domain, listener, _retval); } \ + NS_SCRIPTABLE NS_IMETHOD Stop(void) { return !_to ? NS_ERROR_NULL_POINTER : _to->Stop(); } #if 0 /* Use the code below as a template for the implementation class for this interface. */ @@ -213,16 +213,16 @@ class NS_NO_VTABLE NS_SCRIPTABLE IDNSSDService : public nsISupports { class _MYCLASS_ : public IDNSSDService { public: - NS_DECL_ISUPPORTS - NS_DECL_IDNSSDSERVICE +NS_DECL_ISUPPORTS +NS_DECL_IDNSSDSERVICE - _MYCLASS_(); +_MYCLASS_(); private: - ~_MYCLASS_(); +~_MYCLASS_(); protected: - /* additional members */ +/* additional members */ }; /* Implementation file */ @@ -230,12 +230,12 @@ NS_IMPL_ISUPPORTS1(_MYCLASS_, IDNSSDService) _MYCLASS_::_MYCLASS_() { - /* member initializers and constructor code */ + /* member initializers and constructor code */ } _MYCLASS_::~_MYCLASS_() { - /* destructor code */ + /* destructor code */ } /* IDNSSDService browse (in long interfaceIndex, in AString regtype, in AString domain, in IDNSSDBrowseListener listener); */ diff --git a/Clients/FirefoxExtension/extension/content/bonjour4firefox.png b/Clients/FirefoxExtension/extension/content/_internal_bonjour4firefox.png similarity index 100% rename from Clients/FirefoxExtension/extension/content/bonjour4firefox.png rename to Clients/FirefoxExtension/extension/content/_internal_bonjour4firefox.png diff --git a/Clients/FirefoxExtension/extension/content/listImage.png b/Clients/FirefoxExtension/extension/content/_internal_listImage.png similarity index 100% rename from Clients/FirefoxExtension/extension/content/listImage.png rename to Clients/FirefoxExtension/extension/content/_internal_listImage.png diff --git a/Clients/FirefoxExtension/extension/content/bonjour4firefox.xul b/Clients/FirefoxExtension/extension/content/bonjour4firefox.xul index 69e5efe..2be0c69 100755 --- a/Clients/FirefoxExtension/extension/content/bonjour4firefox.xul +++ b/Clients/FirefoxExtension/extension/content/bonjour4firefox.xul @@ -26,7 +26,7 @@ - + @@ -121,7 +121,7 @@ newTreeRow.setAttribute( 'properties', 'bonjourRow' ); var newTreeCell = document.createElement('treecell'); newTreeCell.setAttribute( 'label', serviceName ); - newTreeCell.setAttribute( 'src', 'chrome://bonjour4firefox/content/bonjour4firefox.png' ); + newTreeCell.setAttribute( 'src', 'chrome://bonjour4firefox/content/_internal_bonjour4firefox.png' ); newTreeItem.appendChild( newTreeRow ); newTreeRow.appendChild( newTreeCell ); diff --git a/Clients/FirefoxExtension/extension/install.rdf b/Clients/FirefoxExtension/extension/install.rdf index f977a0c..37c0955 100755 --- a/Clients/FirefoxExtension/extension/install.rdf +++ b/Clients/FirefoxExtension/extension/install.rdf @@ -7,7 +7,7 @@ 1.0 Apple Inc. Bonjour Browsing Extension for Firefox - chrome://bonjour4firefox/content/bonjour4firefox.png + chrome://bonjour4firefox/content/_internal_bonjour4firefox.png {ec8030f7-c20a-464f-9b0e-13a3a9e97384} diff --git a/Clients/FirefoxExtension/extension/skin-darwin/toolbar-button.png b/Clients/FirefoxExtension/extension/skin-darwin/_internal_toobar-button.png similarity index 100% rename from Clients/FirefoxExtension/extension/skin-darwin/toolbar-button.png rename to Clients/FirefoxExtension/extension/skin-darwin/_internal_toobar-button.png diff --git a/Clients/FirefoxExtension/extension/skin-darwin/overlay.css b/Clients/FirefoxExtension/extension/skin-darwin/overlay.css index e78635b..f5f4a28 100644 --- a/Clients/FirefoxExtension/extension/skin-darwin/overlay.css +++ b/Clients/FirefoxExtension/extension/skin-darwin/overlay.css @@ -4,7 +4,7 @@ } #bonjour4firefox-toolbar-button { - list-style-image: url("chrome://bonjour4firefox/skin/toolbar-button.png"); + list-style-image: url("chrome://bonjour4firefox/skin/_internal_toolbar-button.png"); -moz-image-region: rect(0px 36px 23px 0px); } #bonjour4firefox-toolbar-button[disabled="true"] diff --git a/Clients/FirefoxExtension/extension/skin/toolbar-button.png b/Clients/FirefoxExtension/extension/skin/_internal_toobar-button.png similarity index 100% rename from Clients/FirefoxExtension/extension/skin/toolbar-button.png rename to Clients/FirefoxExtension/extension/skin/_internal_toobar-button.png diff --git a/Clients/FirefoxExtension/extension/skin/overlay.css b/Clients/FirefoxExtension/extension/skin/overlay.css index b192b52..b286560 100755 --- a/Clients/FirefoxExtension/extension/skin/overlay.css +++ b/Clients/FirefoxExtension/extension/skin/overlay.css @@ -4,7 +4,7 @@ } #bonjour4firefox-toolbar-button { - list-style-image: url("chrome://bonjour4firefox/skin/toolbar-button.png"); + list-style-image: url("chrome://bonjour4firefox/skin/_internal_toolbar-button.png"); -moz-image-region: rect(0px 24px 24px 0px); } #bonjour4firefox-toolbar-button:hover diff --git a/Clients/FirefoxExtension/resource.h b/Clients/FirefoxExtension/resource.h index a723410..42710b8 100644 --- a/Clients/FirefoxExtension/resource.h +++ b/Clients/FirefoxExtension/resource.h @@ -16,7 +16,7 @@ #define IDS_MUTEX_TIMEOUT 401 // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 201 diff --git a/Clients/PrinterSetupWizard/About.h b/Clients/PrinterSetupWizard/About.h index 1d84b7f..72e893d 100644 --- a/Clients/PrinterSetupWizard/About.h +++ b/Clients/PrinterSetupWizard/About.h @@ -5,17 +5,17 @@ class CAbout : public CDialog { - DECLARE_DYNAMIC(CAbout) +DECLARE_DYNAMIC(CAbout) public: - CAbout(CWnd* pParent = NULL); // standard constructor - virtual ~CAbout(); +CAbout(CWnd* pParent = NULL); // standard constructor +virtual ~CAbout(); // Dialog Data - enum { IDD = IDD_DIALOG1 }; +enum { IDD = IDD_DIALOG1 }; protected: - virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support +virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support - DECLARE_MESSAGE_MAP() +DECLARE_MESSAGE_MAP() }; diff --git a/Clients/PrinterSetupWizard/FirstPage.h b/Clients/PrinterSetupWizard/FirstPage.h index 78fa77b..0186c02 100644 --- a/Clients/PrinterSetupWizard/FirstPage.h +++ b/Clients/PrinterSetupWizard/FirstPage.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -23,28 +23,28 @@ class CFirstPage : public CPropertyPage { - DECLARE_DYNAMIC(CFirstPage) +DECLARE_DYNAMIC(CFirstPage) public: - CFirstPage(); - virtual ~CFirstPage(); +CFirstPage(); +virtual ~CFirstPage(); // Dialog Data - enum { IDD = IDD_FIRST_PAGE }; +enum { IDD = IDD_FIRST_PAGE }; protected: - virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support - virtual BOOL OnSetActive(); - virtual BOOL OnKillActive(); - +virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support +virtual BOOL OnSetActive(); +virtual BOOL OnKillActive(); - DECLARE_MESSAGE_MAP() + +DECLARE_MESSAGE_MAP() private: - CFont m_largeFont; - +CFont m_largeFont; + public: - CStatic m_greeting; +CStatic m_greeting; }; diff --git a/Clients/PrinterSetupWizard/FourthPage.h b/Clients/PrinterSetupWizard/FourthPage.h index 9150df2..5b4ea99 100644 --- a/Clients/PrinterSetupWizard/FourthPage.h +++ b/Clients/PrinterSetupWizard/FourthPage.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -23,39 +23,39 @@ class CFourthPage : public CPropertyPage { - DECLARE_DYNAMIC(CFourthPage) +DECLARE_DYNAMIC(CFourthPage) public: - CFourthPage(); - virtual ~CFourthPage(); +CFourthPage(); +virtual ~CFourthPage(); // Dialog Data - enum { IDD = IDD_FOURTH_PAGE }; +enum { IDD = IDD_FOURTH_PAGE }; - virtual BOOL OnSetActive(); - virtual BOOL OnKillActive(); +virtual BOOL OnSetActive(); +virtual BOOL OnKillActive(); - BOOL StartActivityIndicator(); - BOOL StopActivityIndicator(); +BOOL StartActivityIndicator(); +BOOL StopActivityIndicator(); protected: - virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support +virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support - DECLARE_MESSAGE_MAP() +DECLARE_MESSAGE_MAP() private: - OSStatus OnInitPage(); - CFont m_largeFont; - bool m_initialized; +OSStatus OnInitPage(); +CFont m_largeFont; +bool m_initialized; public: - CStatic m_goodbye; +CStatic m_goodbye; private: - CStatic m_printerNameCtrl; - CStatic m_printerManufacturerCtrl; - CStatic m_printerModelCtrl; - CStatic m_printerProtocolCtrl; - CStatic m_printerDefault; +CStatic m_printerNameCtrl; +CStatic m_printerManufacturerCtrl; +CStatic m_printerModelCtrl; +CStatic m_printerProtocolCtrl; +CStatic m_printerDefault; }; diff --git a/Clients/PrinterSetupWizard/Logger.h b/Clients/PrinterSetupWizard/Logger.h index a715132..2f338c1 100644 --- a/Clients/PrinterSetupWizard/Logger.h +++ b/Clients/PrinterSetupWizard/Logger.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -26,38 +26,38 @@ class Logger : public std::ofstream { public: - Logger(); - ~Logger(); +Logger(); +~Logger(); - std::string - currentTime(); +std::string +currentTime(); }; -#define require_noerr_with_log( LOG, MESSAGE, ERR, LABEL ) \ - do \ - { \ - int_least32_t localErr; \ - localErr = (int_least32_t)( ERR ); \ - if( localErr != 0 ) \ - { \ - log << log.currentTime() << " [ERROR] " << MESSAGE << " returned " << ERR << std::endl; \ - log << log.currentTime() << " [WHERE] " << "\"" << __FILE__ << "\", \"" << __FUNCTION__ << "\", line " << __LINE__ << std::endl << std::endl; \ - goto LABEL; \ - } \ - } while( 0 ) - - -#define require_action_with_log( LOG, X, LABEL, ACTION ) \ - do \ - { \ - if( !( X ) ) \ - { \ - log << log.currentTime() << " [ERROR] " << #X << std::endl; \ - log << log.currentTime() << " [WHERE] " << "\"" << __FILE__ << "\", \"" << __FUNCTION__ << "\", line " << __LINE__ << std::endl << std::endl; \ - { ACTION; } \ - goto LABEL; \ - } \ - } while( 0 ) +#define require_noerr_with_log( LOG, MESSAGE, ERR, LABEL ) \ + do \ + { \ + int_least32_t localErr; \ + localErr = (int_least32_t)( ERR ); \ + if( localErr != 0 ) \ + { \ + log << log.currentTime() << " [ERROR] " << MESSAGE << " returned " << ERR << std::endl; \ + log << log.currentTime() << " [WHERE] " << "\"" << __FILE__ << "\", \"" << __FUNCTION__ << "\", line " << __LINE__ << std::endl << std::endl; \ + goto LABEL; \ + } \ + } while( 0 ) + + +#define require_action_with_log( LOG, X, LABEL, ACTION ) \ + do \ + { \ + if( !( X ) ) \ + { \ + log << log.currentTime() << " [ERROR] " << # X << std::endl; \ + log << log.currentTime() << " [WHERE] " << "\"" << __FILE__ << "\", \"" << __FUNCTION__ << "\", line " << __LINE__ << std::endl << std::endl; \ + { ACTION; } \ + goto LABEL; \ + } \ + } while( 0 ) #endif diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizardApp.h b/Clients/PrinterSetupWizard/PrinterSetupWizardApp.h index 4daba12..379c9e4 100644 --- a/Clients/PrinterSetupWizard/PrinterSetupWizardApp.h +++ b/Clients/PrinterSetupWizard/PrinterSetupWizardApp.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,10 +18,10 @@ #pragma once #ifndef __AFXWIN_H__ - #error include 'stdafx.h' before including this file for PCH + #error include 'stdafx.h' before including this file for PCH #endif -#include "resource.h" // main symbols +#include "resource.h" // main symbols // CWiz97_3App: @@ -31,18 +31,18 @@ class CPrinterSetupWizardApp : public CWinApp { public: - CPrinterSetupWizardApp(); +CPrinterSetupWizardApp(); // Overrides - public: - virtual BOOL InitInstance(); +public: +virtual BOOL InitInstance(); // Implementation - DECLARE_MESSAGE_MAP() +DECLARE_MESSAGE_MAP() }; -extern CPrinterSetupWizardApp theApp; -extern HINSTANCE GetNonLocalizedResources(); -extern HINSTANCE GetLocalizedResources(); +extern CPrinterSetupWizardApp theApp; +extern HINSTANCE GetNonLocalizedResources(); +extern HINSTANCE GetLocalizedResources(); diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.h b/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.h index 91ed39d..4511043 100644 --- a/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.h +++ b/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -33,320 +33,320 @@ using namespace PrinterSetupWizard; class CPrinterSetupWizardSheet : public CPropertySheet { - DECLARE_DYNAMIC(CPrinterSetupWizardSheet) +DECLARE_DYNAMIC(CPrinterSetupWizardSheet) public: - struct WizardException - { - CString text; - CString caption; - }; +struct WizardException +{ + CString text; + CString caption; +}; public: - CPrinterSetupWizardSheet(UINT nIDCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0); - virtual ~CPrinterSetupWizardSheet(); +CPrinterSetupWizardSheet(UINT nIDCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0); +virtual ~CPrinterSetupWizardSheet(); - CPropertyPage* - GetLastPage(); +CPropertyPage* +GetLastPage(); - void - SetLastPage(CPropertyPage * page ); +void +SetLastPage(CPropertyPage * page ); - void - SetSelectedPrinter(Printer * printer); +void +SetSelectedPrinter(Printer * printer); - Printer* - GetSelectedPrinter(); +Printer* +GetSelectedPrinter(); - OSStatus - LoadPrinterDriver(const CString & filename); +OSStatus +LoadPrinterDriver(const CString & filename); - HCURSOR - GetCursor(); +HCURSOR +GetCursor(); - // - // handles end of process event - // - virtual LRESULT - OnProcessEvent(WPARAM inWParam, LPARAM inLParam); - - virtual LRESULT - OnSocketEvent(WPARAM inWParam, LPARAM inLParam); +// +// handles end of process event +// +virtual LRESULT +OnProcessEvent(WPARAM inWParam, LPARAM inLParam); - virtual BOOL - OnCommand(WPARAM wParam, LPARAM lParam); +virtual LRESULT +OnSocketEvent(WPARAM inWParam, LPARAM inLParam); - virtual BOOL - OnInitDialog(); +virtual BOOL +OnCommand(WPARAM wParam, LPARAM lParam); - virtual BOOL - OnSetCursor(CWnd * pWnd, UINT nHitTest, UINT message); +virtual BOOL +OnInitDialog(); - virtual void - OnContextMenu(CWnd * pWnd, CPoint pos); +virtual BOOL +OnSetCursor(CWnd * pWnd, UINT nHitTest, UINT message); - afx_msg void - OnOK(); +virtual void +OnContextMenu(CWnd * pWnd, CPoint pos); - OSStatus - StartResolve( Printer * printer ); +afx_msg void +OnOK(); - OSStatus - StopResolve( Printer * printer ); +OSStatus +StartResolve( Printer * printer ); - Printers m_printers; +OSStatus +StopResolve( Printer * printer ); - HCURSOR m_active; - HCURSOR m_arrow; - HCURSOR m_wait; +Printers m_printers; + +HCURSOR m_active; +HCURSOR m_arrow; +HCURSOR m_wait; protected: - DECLARE_MESSAGE_MAP() - CSecondPage m_pgSecond; - CThirdPage m_pgThird; - CFourthPage m_pgFourth; +DECLARE_MESSAGE_MAP() +CSecondPage m_pgSecond; +CThirdPage m_pgThird; +CFourthPage m_pgFourth; - void - OnServiceResolved( - Service * service); +void +OnServiceResolved( + Service * service); - void Init(void); +void Init(void); private: - // This is from - typedef enum http_encryption_e /**** HTTP encryption values ****/ - { - HTTP_ENCRYPT_IF_REQUESTED, /* Encrypt if requested (TLS upgrade) */ - HTTP_ENCRYPT_NEVER, /* Never encrypt */ - HTTP_ENCRYPT_REQUIRED, /* Encryption is required (TLS upgrade) */ - HTTP_ENCRYPT_ALWAYS /* Always encrypt (SSL) */ - } http_encryption_t; - - typedef void* ( *httpConnectEncryptFunc )( const char* host, int port, http_encryption_t encryption ); - typedef http_encryption_t ( *cupsEncryptionFunc )( void ); - typedef void ( *cupsSetEncryptionFunc )( http_encryption_t e ); - typedef char* ( *cupsAdminCreateWindowsPPDFunc )( void * http, const char *dest, char *buffer, int bufsize ); - - class CUPSLibrary - { - public: - - CUPSLibrary() - : - httpConnectEncrypt( NULL ), - cupsEncryption( NULL ), - cupsSetEncryption( NULL ), - cupsAdminCreateWindowsPPD( NULL ), - library( NULL ) - { +// This is from +typedef enum http_encryption_e /**** HTTP encryption values ****/ +{ + HTTP_ENCRYPT_IF_REQUESTED, /* Encrypt if requested (TLS upgrade) */ + HTTP_ENCRYPT_NEVER, /* Never encrypt */ + HTTP_ENCRYPT_REQUIRED, /* Encryption is required (TLS upgrade) */ + HTTP_ENCRYPT_ALWAYS /* Always encrypt (SSL) */ +} http_encryption_t; + +typedef void* ( *httpConnectEncryptFunc )( const char* host, int port, http_encryption_t encryption ); +typedef http_encryption_t ( *cupsEncryptionFunc )( void ); +typedef void ( *cupsSetEncryptionFunc )( http_encryption_t e ); +typedef char* ( *cupsAdminCreateWindowsPPDFunc )( void * http, const char *dest, char *buffer, int bufsize ); + +class CUPSLibrary +{ +public: + +CUPSLibrary() + : + httpConnectEncrypt( NULL ), + cupsEncryption( NULL ), + cupsSetEncryption( NULL ), + cupsAdminCreateWindowsPPD( NULL ), + library( NULL ) +{ #if defined( LIBCUPS_ENABLED ) - if ( ( library = LoadLibrary( TEXT( "libcups2.dll" ) ) ) != NULL ) - { - httpConnectEncrypt = ( httpConnectEncryptFunc ) GetProcAddress( library, "httpConnectEncrypt" ); - cupsEncryption = ( cupsEncryptionFunc ) GetProcAddress( library, "cupsEncryption" ); - cupsSetEncryption = ( cupsSetEncryptionFunc ) GetProcAddress( library, "cupsSetEncryption" ); - cupsAdminCreateWindowsPPD = ( cupsAdminCreateWindowsPPDFunc ) GetProcAddress( library, "cupsAdminCreateWindowsPPD" ); - } + if ( ( library = LoadLibrary( TEXT( "libcups2.dll" ) ) ) != NULL ) + { + httpConnectEncrypt = ( httpConnectEncryptFunc ) GetProcAddress( library, "httpConnectEncrypt" ); + cupsEncryption = ( cupsEncryptionFunc ) GetProcAddress( library, "cupsEncryption" ); + cupsSetEncryption = ( cupsSetEncryptionFunc ) GetProcAddress( library, "cupsSetEncryption" ); + cupsAdminCreateWindowsPPD = ( cupsAdminCreateWindowsPPDFunc ) GetProcAddress( library, "cupsAdminCreateWindowsPPD" ); + } #endif - } - - ~CUPSLibrary() - { - if ( library ) - { - FreeLibrary( library ); - library = NULL; - } - } - - BOOL - IsInstalled() - { - return ( ( httpConnectEncrypt != NULL ) && ( cupsEncryption != NULL ) && ( cupsSetEncryption != NULL ) && ( cupsAdminCreateWindowsPPD != NULL ) ); - } - - httpConnectEncryptFunc httpConnectEncrypt; - cupsEncryptionFunc cupsEncryption; - cupsSetEncryptionFunc cupsSetEncryption; - cupsAdminCreateWindowsPPDFunc cupsAdminCreateWindowsPPD; - - private: - - HMODULE library; - }; - - - 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 ); - - static void DNSSD_API - OnQuery( - DNSServiceRef inRef, - DNSServiceFlags inFlags, - uint32_t inInterfaceIndex, - DNSServiceErrorType inErrorCode, - const char * inFullName, - uint16_t inRRType, - uint16_t inRRClass, - uint16_t inRDLen, - const void * inRData, - uint32_t inTTL, - void * inContext); - - Printer* - OnAddPrinter( - uint32_t inInterfaceIndex, - const char * inName, - const char * inType, - const char * inDomain, - bool moreComing); - - OSStatus - OnRemovePrinter( - Printer * printer, - bool moreComing); - - OSStatus - OnAddService( - Printer * printer, - uint32_t inInterfaceIndex, - const char * inName, - const char * inType, - const char * inDomain); - - OSStatus - OnRemoveService( - Service * service); - - void - OnResolveService( - Service * service ); - - static bool - OrderServiceFunc( const Service * a, const Service * b ); - - static bool - OrderQueueFunc( const Queue * q1, const Queue * q2 ); - - OSStatus - StartOperation( DNSServiceRef ref ); - - OSStatus - StopOperation( DNSServiceRef & ref ); - - OSStatus - StartBrowse(); - - OSStatus - StopBrowse(); - - OSStatus - StartResolve( Service * service ); - - OSStatus - StopResolve( Service * service ); - - OSStatus - ParseTextRecord( Service * service, Queue * q, uint16_t inTXTSize, const char * inTXT ); - - OSStatus - LoadPrinterNames(); - - Printer* - Lookup( const char * name ); - - OSStatus - InstallPrinter(Printer * printer); - - OSStatus - InstallPrinterPort( Printer * printer, Service * service, DWORD protocol, Logger & log ); - - OSStatus - InstallPrinterPDLAndLPR(Printer * printer, Service * service, Logger & log); - - OSStatus - InstallPrinterIPP(Printer * printer, Service * service, Logger & log); - - OSStatus - InstallPrinterCUPS( Printer * printer, Service * service, CUPSLibrary & cupsLib ); - - OSStatus - InstallPrinterCUPS(Printer * printer, Service * service, CUPSLibrary & cupsLib, TCHAR * env ); - - static unsigned WINAPI - InstallDriverThread( LPVOID inParam ); - - typedef std::list PrinterNames; - typedef std::list ServiceRefList; - static CPrinterSetupWizardSheet * m_self; - PrinterNames m_printerNames; - Printer * m_selectedPrinter; - bool m_driverThreadFinished; - DWORD m_driverThreadExitCode; - ServiceRefList m_serviceRefList; - DNSServiceRef m_pdlBrowser; - DNSServiceRef m_lprBrowser; - DNSServiceRef m_ippBrowser; - DNSServiceRef m_resolver; - - CPropertyPage * m_lastPage; +} + +~CUPSLibrary() +{ + if ( library ) + { + FreeLibrary( library ); + library = NULL; + } +} + +BOOL +IsInstalled() +{ + return ( ( httpConnectEncrypt != NULL ) && ( cupsEncryption != NULL ) && ( cupsSetEncryption != NULL ) && ( cupsAdminCreateWindowsPPD != NULL ) ); +} + +httpConnectEncryptFunc httpConnectEncrypt; +cupsEncryptionFunc cupsEncryption; +cupsSetEncryptionFunc cupsSetEncryption; +cupsAdminCreateWindowsPPDFunc cupsAdminCreateWindowsPPD; + +private: + +HMODULE library; +}; + + +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 ); + +static void DNSSD_API +OnQuery( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inFullName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDLen, + const void * inRData, + uint32_t inTTL, + void * inContext); + +Printer* +OnAddPrinter( + uint32_t inInterfaceIndex, + const char * inName, + const char * inType, + const char * inDomain, + bool moreComing); + +OSStatus +OnRemovePrinter( + Printer * printer, + bool moreComing); + +OSStatus +OnAddService( + Printer * printer, + uint32_t inInterfaceIndex, + const char * inName, + const char * inType, + const char * inDomain); + +OSStatus +OnRemoveService( + Service * service); + +void +OnResolveService( + Service * service ); + +static bool +OrderServiceFunc( const Service * a, const Service * b ); + +static bool +OrderQueueFunc( const Queue * q1, const Queue * q2 ); + +OSStatus +StartOperation( DNSServiceRef ref ); + +OSStatus +StopOperation( DNSServiceRef & ref ); + +OSStatus +StartBrowse(); + +OSStatus +StopBrowse(); + +OSStatus +StartResolve( Service * service ); + +OSStatus +StopResolve( Service * service ); + +OSStatus +ParseTextRecord( Service * service, Queue * q, uint16_t inTXTSize, const char * inTXT ); + +OSStatus +LoadPrinterNames(); + +Printer* +Lookup( const char * name ); + +OSStatus +InstallPrinter(Printer * printer); + +OSStatus +InstallPrinterPort( Printer * printer, Service * service, DWORD protocol, Logger & log ); + +OSStatus +InstallPrinterPDLAndLPR(Printer * printer, Service * service, Logger & log); + +OSStatus +InstallPrinterIPP(Printer * printer, Service * service, Logger & log); + +OSStatus +InstallPrinterCUPS( Printer * printer, Service * service, CUPSLibrary & cupsLib ); + +OSStatus +InstallPrinterCUPS(Printer * printer, Service * service, CUPSLibrary & cupsLib, TCHAR * env ); + +static unsigned WINAPI +InstallDriverThread( LPVOID inParam ); + +typedef std::list PrinterNames; +typedef std::list ServiceRefList; +static CPrinterSetupWizardSheet * m_self; +PrinterNames m_printerNames; +Printer * m_selectedPrinter; +bool m_driverThreadFinished; +DWORD m_driverThreadExitCode; +ServiceRefList m_serviceRefList; +DNSServiceRef m_pdlBrowser; +DNSServiceRef m_lprBrowser; +DNSServiceRef m_ippBrowser; +DNSServiceRef m_resolver; + +CPropertyPage * m_lastPage; }; inline Printer* CPrinterSetupWizardSheet::GetSelectedPrinter() -{ - return m_selectedPrinter; +{ + return m_selectedPrinter; } inline HCURSOR CPrinterSetupWizardSheet::GetCursor() { - return m_active; + return m_active; } inline CPropertyPage* CPrinterSetupWizardSheet::GetLastPage() { - return m_lastPage; + return m_lastPage; } inline void CPrinterSetupWizardSheet::SetLastPage(CPropertyPage * lastPage) { - m_lastPage = lastPage; + m_lastPage = lastPage; } // Service Types -#define kPDLServiceType "_pdl-datastream._tcp." -#define kLPRServiceType "_printer._tcp." -#define kIPPServiceType "_ipp._tcp." +#define kPDLServiceType "_pdl-datastream._tcp." +#define kLPRServiceType "_printer._tcp." +#define kIPPServiceType "_ipp._tcp." diff --git a/Clients/PrinterSetupWizard/SecondPage.h b/Clients/PrinterSetupWizard/SecondPage.h index c9c614d..3fb4c46 100644 --- a/Clients/PrinterSetupWizard/SecondPage.h +++ b/Clients/PrinterSetupWizard/SecondPage.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -31,75 +31,75 @@ using namespace PrinterSetupWizard; class CSecondPage : public CPropertyPage { - DECLARE_DYNAMIC(CSecondPage) +DECLARE_DYNAMIC(CSecondPage) public: - CSecondPage(); - virtual ~CSecondPage(); +CSecondPage(); +virtual ~CSecondPage(); // Dialog Data - enum { IDD = IDD_SECOND_PAGE }; +enum { IDD = IDD_SECOND_PAGE }; 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(); +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() +DECLARE_MESSAGE_MAP() public: - HTREEITEM m_emptyListItem; - bool m_selectOkay; - CTreeCtrl m_browseList; - bool m_initialized; - bool m_waiting; +HTREEITEM m_emptyListItem; +bool m_selectOkay; +CTreeCtrl m_browseList; +bool m_initialized; +bool m_waiting; - afx_msg void OnTvnSelchangedBrowseList(NMHDR *pNMHDR, LRESULT *pResult); - afx_msg void OnNmClickBrowseList(NMHDR * pNMHDR, LRESULT * pResult); - afx_msg void OnTvnKeyDownBrowseList(NMHDR * pNMHDR, LRESULT * pResult ); +afx_msg void OnTvnSelchangedBrowseList(NMHDR *pNMHDR, LRESULT *pResult); +afx_msg void OnNmClickBrowseList(NMHDR * pNMHDR, LRESULT * pResult); +afx_msg void OnTvnKeyDownBrowseList(NMHDR * pNMHDR, LRESULT * pResult ); - OSStatus - OnAddPrinter( - Printer * printer, - bool moreComing); +OSStatus +OnAddPrinter( + Printer * printer, + bool moreComing); - OSStatus - OnRemovePrinter( - Printer * printer, - bool moreComing); +OSStatus +OnRemovePrinter( + Printer * printer, + bool moreComing); - void - OnResolveService( Service * service ); +void +OnResolveService( Service * service ); private: - void - LoadTextAndDisableWindow( CString & text ); - - void - SetPrinterInformationState( BOOL state ); +void +LoadTextAndDisableWindow( CString & text ); + +void +SetPrinterInformationState( BOOL state ); - std::string m_selectedName; +std::string m_selectedName; private: - CStatic m_printerInformation; +CStatic m_printerInformation; - CStatic m_descriptionLabel; +CStatic m_descriptionLabel; - CStatic m_descriptionField; +CStatic m_descriptionField; - CStatic m_locationLabel; +CStatic m_locationLabel; - CStatic m_locationField; +CStatic m_locationField; - bool m_gotChoice; +bool m_gotChoice; }; diff --git a/Clients/PrinterSetupWizard/ThirdPage.h b/Clients/PrinterSetupWizard/ThirdPage.h index 63ad256..476e233 100644 --- a/Clients/PrinterSetupWizard/ThirdPage.h +++ b/Clients/PrinterSetupWizard/ThirdPage.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -29,131 +29,131 @@ class CThirdPage : public CPropertyPage { - DECLARE_DYNAMIC(CThirdPage) +DECLARE_DYNAMIC(CThirdPage) public: - CThirdPage(); - virtual ~CThirdPage(); +CThirdPage(); +virtual ~CThirdPage(); // Dialog Data - enum { IDD = IDD_THIRD_PAGE }; +enum { IDD = IDD_THIRD_PAGE }; protected: - virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support - virtual BOOL OnSetActive(); - virtual BOOL OnKillActive(); +virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support +virtual BOOL OnSetActive(); +virtual BOOL OnKillActive(); - DECLARE_MESSAGE_MAP() +DECLARE_MESSAGE_MAP() private: - // - // mDNS: Epson shows up twice in the list. Use case insensitive compare - // - struct compare_func - { - bool operator()( const CString & s1, const CString & s2 ) const - { - return s1.CompareNoCase( s2 ) < 0; - } - }; - - 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); - - // - // LoadGenericPrintDriversDefs - // - // Loads generic postscript and pcl print driver defs - // - OSStatus LoadGenericPrintDriverDefs( 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, Service * service, bool useCUPSWorkaround); - - // - // 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 ); - BOOL MatchGeneric( Manufacturers & manufacturers, Printer * printer, Service * service, Manufacturer ** manufacturer, Model ** model ); - void SelectMatch(Printer * printer, Service * service, Manufacturer * manufacturer, Model * model); - void SelectMatch(Manufacturers & manufacturers, Printer * printer, Service * service, Manufacturer * manufacturer, Model * model); - void CopyPrinterSettings(Printer * printer, Service * service, Manufacturer * manufacturer, Model * model); - // - // mDNS: Printers added using Bonjour should be set as the default printer. - // - BOOL DefaultPrinterExists(); - // - // mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle - // - void AutoScroll(CListCtrl & list, int nIndex); - - void FreeManufacturers( Manufacturers & manufacturers ); - - Manufacturers m_manufacturers; - - CListCtrl m_manufacturerListCtrl; - Manufacturer * m_manufacturerSelected; - - CListCtrl m_modelListCtrl; - Model * m_modelSelected; - - Model * m_genericPostscript; - Model * m_genericPCL; - - bool m_initialized; +// +// mDNS: Epson shows up twice in the list. Use case insensitive compare +// +struct compare_func +{ + bool operator()( const CString & s1, const CString & s2 ) const + { + return s1.CompareNoCase( s2 ) < 0; + } +}; + +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); + +// +// LoadGenericPrintDriversDefs +// +// Loads generic postscript and pcl print driver defs +// +OSStatus LoadGenericPrintDriverDefs( 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, Service * service, bool useCUPSWorkaround); + +// +// 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 ); +BOOL MatchGeneric( Manufacturers & manufacturers, Printer * printer, Service * service, Manufacturer ** manufacturer, Model ** model ); +void SelectMatch(Printer * printer, Service * service, Manufacturer * manufacturer, Model * model); +void SelectMatch(Manufacturers & manufacturers, Printer * printer, Service * service, Manufacturer * manufacturer, Model * model); +void CopyPrinterSettings(Printer * printer, Service * service, Manufacturer * manufacturer, Model * model); +// +// mDNS: Printers added using Bonjour should be set as the default printer. +// +BOOL DefaultPrinterExists(); +// +// mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle +// +void AutoScroll(CListCtrl & list, int nIndex); + +void FreeManufacturers( Manufacturers & manufacturers ); + +Manufacturers m_manufacturers; + +CListCtrl m_manufacturerListCtrl; +Manufacturer * m_manufacturerSelected; + +CListCtrl m_modelListCtrl; +Model * m_modelSelected; + +Model * m_genericPostscript; +Model * m_genericPCL; + +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(); +afx_msg void OnLvnItemchangedManufacturer(NMHDR *pNMHDR, LRESULT *pResult); +CStatic m_printerName; +afx_msg void OnLvnItemchangedPrinterModel(NMHDR *pNMHDR, LRESULT *pResult); +afx_msg void OnBnClickedDefaultPrinter(); private: - void - Split( const CString & string, TCHAR ch, CStringList & components ); +void +Split( const CString & string, TCHAR ch, CStringList & components ); - CButton m_defaultPrinterCtrl; +CButton m_defaultPrinterCtrl; public: - CStatic m_printerSelectionText; - CStatic * m_printerImage; - afx_msg void OnBnClickedHaveDisk(); +CStatic m_printerSelectionText; +CStatic * m_printerImage; +afx_msg void OnBnClickedHaveDisk(); }; diff --git a/Clients/PrinterSetupWizard/UtilTypes.h b/Clients/PrinterSetupWizard/UtilTypes.h index 084832c..dc0ae10 100644 --- a/Clients/PrinterSetupWizard/UtilTypes.h +++ b/Clients/PrinterSetupWizard/UtilTypes.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -24,257 +24,257 @@ class CPrinterSetupWizardSheet; -#define kDefaultPriority 50 -#define kDefaultQTotal 1 +#define kDefaultPriority 50 +#define kDefaultQTotal 1 namespace PrinterSetupWizard { - struct Printer; - struct Service; - struct Queue; - struct Manufacturer; - struct Model; - - typedef std::list Queues; - typedef std::list Printers; - typedef std::list Services; - typedef std::list Models; - - struct Printer - { - Printer(); - - ~Printer(); - - Service* - LookupService - ( - const std::string & type - ); - - CPrinterSetupWizardSheet * window; - HTREEITEM item; - - // - // These are from the browse reply - // - std::string name; - CString displayName; - CString actualName; - - // - // These keep track of the different services associated with this printer. - // the services are ordered according to preference. - // - Services services; - - // - // 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 displayModelName; - CString modelName; - CString portName; - bool deflt; - - // This let's us know that this printer was discovered via OSX Printer Sharing. - // We use this knowledge to workaround a problem with OS X Printer sharing. - - bool isCUPSPrinter; - - // - // state - // - unsigned resolving; - bool installed; - }; - - - struct Service - { - Service(); - - ~Service(); - - Queue* - SelectedQueue(); - - void - EmptyQueues(); - - Printer * printer; - uint32_t ifi; - std::string type; - std::string domain; - - // - // these are from the resolve - // - DNSServiceRef serviceRef; - CString hostname; - unsigned short portNumber; - CString protocol; - unsigned short qtotal; - - // - // There will usually one be one of these, however - // this will handle printers that have multiple - // queues. These are ordered according to preference. - // - Queues queues; - - // - // Reference count - // - unsigned refs; - }; - - - struct Queue - { - Queue(); - - ~Queue(); - - CString name; - uint32_t priority; - CString pdl; - CString usb_MFG; - CString usb_MDL; - CString description; - CString location; - CString product; - }; - - - struct Manufacturer - { - CString name; - Models models; - - Model* - find( const CString & name ); - }; - - - struct Model - { - bool driverInstalled; - CString infFileName; - CString displayName; - CString name; - }; - - - inline - Printer::Printer() - : - isCUPSPrinter( false ) - { - } - - inline - Printer::~Printer() - { - while ( services.size() > 0 ) - { - Service * service = services.front(); - services.pop_front(); - delete service; - } - } - - inline Service* - Printer::LookupService - ( - const std::string & type - ) - { - Services::iterator it; - - for ( it = services.begin(); it != services.end(); it++ ) - { - Service * service = *it; - - if ( strcmp(service->type.c_str(), type.c_str()) == 0 ) - { - return service; - } - } - - return NULL; - } - - inline - Service::Service() - : - qtotal(kDefaultQTotal) - { - } - - inline - Service::~Service() - { - check( serviceRef == NULL ); - - EmptyQueues(); - } - - inline Queue* - Service::SelectedQueue() - { - return queues.front(); - } - - inline void - Service::EmptyQueues() - { - while ( queues.size() > 0 ) - { - Queue * q = queues.front(); - queues.pop_front(); - delete q; - } - } - - inline - Queue::Queue() - : - priority(kDefaultPriority) - { - } - - inline - Queue::~Queue() - { - } - - inline Model* - Manufacturer::find( const CString & name ) - { - Models::iterator it; - - for ( it = models.begin(); it != models.end(); it++ ) - { - Model * model = *it; - - if ( model->name == name ) - { - return model; - } - } - - return NULL; - } +struct Printer; +struct Service; +struct Queue; +struct Manufacturer; +struct Model; + +typedef std::list Queues; +typedef std::list Printers; +typedef std::list Services; +typedef std::list Models; + +struct Printer +{ + Printer(); + + ~Printer(); + + Service* + LookupService + ( + const std::string & type + ); + + CPrinterSetupWizardSheet * window; + HTREEITEM item; + + // + // These are from the browse reply + // + std::string name; + CString displayName; + CString actualName; + + // + // These keep track of the different services associated with this printer. + // the services are ordered according to preference. + // + Services services; + + // + // 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 displayModelName; + CString modelName; + CString portName; + bool deflt; + + // This let's us know that this printer was discovered via OSX Printer Sharing. + // We use this knowledge to workaround a problem with OS X Printer sharing. + + bool isCUPSPrinter; + + // + // state + // + unsigned resolving; + bool installed; +}; + + +struct Service +{ + Service(); + + ~Service(); + + Queue* + SelectedQueue(); + + void + EmptyQueues(); + + Printer * printer; + uint32_t ifi; + std::string type; + std::string domain; + + // + // these are from the resolve + // + DNSServiceRef serviceRef; + CString hostname; + unsigned short portNumber; + CString protocol; + unsigned short qtotal; + + // + // There will usually one be one of these, however + // this will handle printers that have multiple + // queues. These are ordered according to preference. + // + Queues queues; + + // + // Reference count + // + unsigned refs; +}; + + +struct Queue +{ + Queue(); + + ~Queue(); + + CString name; + uint32_t priority; + CString pdl; + CString usb_MFG; + CString usb_MDL; + CString description; + CString location; + CString product; +}; + + +struct Manufacturer +{ + CString name; + Models models; + + Model* + find( const CString & name ); +}; + + +struct Model +{ + bool driverInstalled; + CString infFileName; + CString displayName; + CString name; +}; + + +inline +Printer::Printer() + : + isCUPSPrinter( false ) +{ +} + +inline +Printer::~Printer() +{ + while ( services.size() > 0 ) + { + Service * service = services.front(); + services.pop_front(); + delete service; + } +} + +inline Service* +Printer::LookupService +( + const std::string & type +) +{ + Services::iterator it; + + for ( it = services.begin(); it != services.end(); it++ ) + { + Service * service = *it; + + if ( strcmp(service->type.c_str(), type.c_str()) == 0 ) + { + return service; + } + } + + return NULL; +} + +inline +Service::Service() + : + qtotal(kDefaultQTotal) +{ +} + +inline +Service::~Service() +{ + check( serviceRef == NULL ); + + EmptyQueues(); +} + +inline Queue* +Service::SelectedQueue() +{ + return queues.front(); +} + +inline void +Service::EmptyQueues() +{ + while ( queues.size() > 0 ) + { + Queue * q = queues.front(); + queues.pop_front(); + delete q; + } +} + +inline +Queue::Queue() + : + priority(kDefaultPriority) +{ +} + +inline +Queue::~Queue() +{ +} + +inline Model* +Manufacturer::find( const CString & name ) +{ + Models::iterator it; + + for ( it = models.begin(); it != models.end(); it++ ) + { + Model * model = *it; + + if ( model->name == name ) + { + return model; + } + } + + return NULL; +} } diff --git a/Clients/PrinterSetupWizard/resource.h b/Clients/PrinterSetupWizard/resource.h index 945edc2..b1190fb 100644 --- a/Clients/PrinterSetupWizard/resource.h +++ b/Clients/PrinterSetupWizard/resource.h @@ -5,16 +5,16 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ - + // Include resources for Wizard app diff --git a/Clients/PrinterSetupWizard/resource_exe.h b/Clients/PrinterSetupWizard/resource_exe.h index f04cadf..33c0c28 100755 --- a/Clients/PrinterSetupWizard/resource_exe.h +++ b/Clients/PrinterSetupWizard/resource_exe.h @@ -45,14 +45,14 @@ #define IDD_DIALOG1 139 #define IDI_ICON2 141 #define IDI_PRINTER 141 -#define IDS_REINSTALL 142 -#define IDS_REINSTALL_CAPTION 143 -#define IDC_INFO 144 +#define IDS_REINSTALL 142 +#define IDS_REINSTALL_CAPTION 143 +#define IDC_INFO 144 #define IDS_PRINTER_UNAVAILABLE 145 -#define IDS_BAD_INF_FILE 150 -#define IDS_BAD_INF_FILE_CAPTION 151 -#define IDS_NO_MATCH_INF_FILE 152 -#define IDS_NO_MATCH_INF_FILE_CAPTION 153 +#define IDS_BAD_INF_FILE 150 +#define IDS_BAD_INF_FILE_CAPTION 151 +#define IDS_NO_MATCH_INF_FILE 152 +#define IDS_NO_MATCH_INF_FILE_CAPTION 153 #define IDC_BUTTON1 1000 #define IDC_LIST1 1000 #define IDC_BROWSE_LIST 1000 @@ -77,10 +77,10 @@ #define IDC_DESCRIPTION_FIELD 1030 #define IDC_LOCATION_FIELD 1032 #define IDC_DESCRIPTION_LABEL 1033 -#define IDC_COMPLETE1 1034 -#define IDC_COMPLETE2 1035 -#define IDC_INSTALLING 1036 -#define IDC_PROGRESS 1037 +#define IDC_COMPLETE1 1034 +#define IDC_COMPLETE2 1035 +#define IDC_INSTALLING 1036 +#define IDC_PROGRESS 1037 // Next default values for new objects // diff --git a/Clients/PrinterSetupWizard/resource_loc_res.h b/Clients/PrinterSetupWizard/resource_loc_res.h index f2437db..deb2e6e 100755 --- a/Clients/PrinterSetupWizard/resource_loc_res.h +++ b/Clients/PrinterSetupWizard/resource_loc_res.h @@ -48,12 +48,12 @@ #define IDI_PRINTER 141 #define IDS_REINSTALL 142 #define IDS_REINSTALL_CAPTION 143 -#define IDC_INFO 144 +#define IDC_INFO 144 #define IDS_PRINTER_UNAVAILABLE 145 -#define IDS_BAD_INF_FILE 150 -#define IDS_BAD_INF_FILE_CAPTION 151 -#define IDS_NO_MATCH_INF_FILE 152 -#define IDS_NO_MATCH_INF_FILE_CAPTION 153 +#define IDS_BAD_INF_FILE 150 +#define IDS_BAD_INF_FILE_CAPTION 151 +#define IDS_NO_MATCH_INF_FILE 152 +#define IDS_NO_MATCH_INF_FILE_CAPTION 153 #define IDC_BUTTON1 1000 #define IDC_LIST1 1000 #define IDC_BROWSE_LIST 1000 @@ -78,13 +78,13 @@ #define IDC_DESCRIPTION_FIELD 1030 #define IDC_LOCATION_FIELD 1032 #define IDC_DESCRIPTION_LABEL 1033 -#define IDC_COMPLETE1 1034 -#define IDC_COMPLETE2 1035 -#define IDC_INSTALLING 1036 -#define IDC_PROGRESS 1037 +#define IDC_COMPLETE1 1034 +#define IDC_COMPLETE2 1035 +#define IDC_INSTALLING 1036 +#define IDC_PROGRESS 1037 // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 142 diff --git a/Clients/PrinterSetupWizard/resource_res.h b/Clients/PrinterSetupWizard/resource_res.h index fe3bb8e..33c0c28 100755 --- a/Clients/PrinterSetupWizard/resource_res.h +++ b/Clients/PrinterSetupWizard/resource_res.h @@ -45,14 +45,14 @@ #define IDD_DIALOG1 139 #define IDI_ICON2 141 #define IDI_PRINTER 141 -#define IDS_REINSTALL 142 -#define IDS_REINSTALL_CAPTION 143 -#define IDC_INFO 144 +#define IDS_REINSTALL 142 +#define IDS_REINSTALL_CAPTION 143 +#define IDC_INFO 144 #define IDS_PRINTER_UNAVAILABLE 145 -#define IDS_BAD_INF_FILE 150 -#define IDS_BAD_INF_FILE_CAPTION 151 -#define IDS_NO_MATCH_INF_FILE 152 -#define IDS_NO_MATCH_INF_FILE_CAPTION 153 +#define IDS_BAD_INF_FILE 150 +#define IDS_BAD_INF_FILE_CAPTION 151 +#define IDS_NO_MATCH_INF_FILE 152 +#define IDS_NO_MATCH_INF_FILE_CAPTION 153 #define IDC_BUTTON1 1000 #define IDC_LIST1 1000 #define IDC_BROWSE_LIST 1000 @@ -77,13 +77,13 @@ #define IDC_DESCRIPTION_FIELD 1030 #define IDC_LOCATION_FIELD 1032 #define IDC_DESCRIPTION_LABEL 1033 -#define IDC_COMPLETE1 1034 -#define IDC_COMPLETE2 1035 -#define IDC_INSTALLING 1036 -#define IDC_PROGRESS 1037 +#define IDC_COMPLETE1 1034 +#define IDC_COMPLETE2 1035 +#define IDC_INSTALLING 1036 +#define IDC_PROGRESS 1037 // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 142 diff --git a/Clients/PrinterSetupWizard/stdafx.h b/Clients/PrinterSetupWizard/stdafx.h index 7c08a29..1eec4c3 100644 --- a/Clients/PrinterSetupWizard/stdafx.h +++ b/Clients/PrinterSetupWizard/stdafx.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,44 +18,44 @@ #pragma once #ifndef VC_EXTRALEAN -#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers +#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. +#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_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. +#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. +#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 +#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 #if !defined(_WSPIAPI_COUNTOF) -# define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0])) +# define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0])) #endif #include // MFC core and standard components #include // MFC extensions #include // MFC Automation classes -#include // MFC support for Internet Explorer 4 Common Controls +#include // MFC support for Internet Explorer 4 Common Controls #ifndef _AFX_NO_AFXCMN_SUPPORT -#include // MFC support for Windows Common Controls +#include // MFC support for Windows Common Controls #endif // _AFX_NO_AFXCMN_SUPPORT #include diff --git a/Clients/PrinterSetupWizard/tcpxcv.h b/Clients/PrinterSetupWizard/tcpxcv.h index a28e34a..a4b7b5f 100755 --- a/Clients/PrinterSetupWizard/tcpxcv.h +++ b/Clients/PrinterSetupWizard/tcpxcv.h @@ -1,14 +1,14 @@ /*++ -Copyright (c) 1997 - 1999 Hewlett-Packard Company. -Copyright (c) 1997 - 1999 Microsoft Corporation -All rights reserved + Copyright (c) 1997 - 1999 Hewlett-Packard Company. + Copyright (c) 1997 - 1999 Microsoft Corporation + All rights reserved -Module Name: + Module Name: tcpxcv.h ---*/ + --*/ /* * This file is contained in WinDDK 6001.18002 */ @@ -44,38 +44,38 @@ Module Name: typedef struct _PORT_DATA_1 { - WCHAR sztPortName[MAX_PORTNAME_LEN]; - DWORD dwVersion; - DWORD dwProtocol; - DWORD cbSize; - DWORD dwReserved; - WCHAR sztHostAddress[MAX_NETWORKNAME_LEN]; - WCHAR sztSNMPCommunity[MAX_SNMP_COMMUNITY_STR_LEN]; - DWORD dwDoubleSpool; - WCHAR sztQueue[MAX_QUEUENAME_LEN]; - WCHAR sztIPAddress[MAX_IPADDR_STR_LEN]; - BYTE Reserved[540]; - DWORD dwPortNumber; - DWORD dwSNMPEnabled; - DWORD dwSNMPDevIndex; + WCHAR sztPortName[MAX_PORTNAME_LEN]; + DWORD dwVersion; + DWORD dwProtocol; + DWORD cbSize; + DWORD dwReserved; + WCHAR sztHostAddress[MAX_NETWORKNAME_LEN]; + WCHAR sztSNMPCommunity[MAX_SNMP_COMMUNITY_STR_LEN]; + DWORD dwDoubleSpool; + WCHAR sztQueue[MAX_QUEUENAME_LEN]; + WCHAR sztIPAddress[MAX_IPADDR_STR_LEN]; + BYTE Reserved[540]; + DWORD dwPortNumber; + DWORD dwSNMPEnabled; + DWORD dwSNMPDevIndex; } PORT_DATA_1, *PPORT_DATA_1; typedef struct _PORT_DATA_2 { - WCHAR sztPortName[MAX_PORTNAME_LEN]; - DWORD dwVersion; - DWORD dwProtocol; - DWORD cbSize; - DWORD dwReserved; - WCHAR sztHostAddress [MAX_NETWORKNAME2_LEN]; - WCHAR sztSNMPCommunity[MAX_SNMP_COMMUNITY_STR_LEN]; - DWORD dwDoubleSpool; - WCHAR sztQueue[MAX_QUEUENAME_LEN]; - BYTE Reserved[514]; - DWORD dwPortNumber; - DWORD dwSNMPEnabled; - DWORD dwSNMPDevIndex; - DWORD dwPortMonitorMibIndex; + WCHAR sztPortName[MAX_PORTNAME_LEN]; + DWORD dwVersion; + DWORD dwProtocol; + DWORD cbSize; + DWORD dwReserved; + WCHAR sztHostAddress [MAX_NETWORKNAME2_LEN]; + WCHAR sztSNMPCommunity[MAX_SNMP_COMMUNITY_STR_LEN]; + DWORD dwDoubleSpool; + WCHAR sztQueue[MAX_QUEUENAME_LEN]; + BYTE Reserved[514]; + DWORD dwPortNumber; + DWORD dwSNMPEnabled; + DWORD dwSNMPDevIndex; + DWORD dwPortMonitorMibIndex; } PORT_DATA_2, *PPORT_DATA_2; @@ -89,17 +89,17 @@ typedef struct _PORT_DATA_LIST_1 typedef struct _DELETE_PORT_DATA_1 { - WCHAR psztPortName[MAX_PORTNAME_LEN]; - BYTE Reserved[98]; - DWORD dwVersion; - DWORD dwReserved; + WCHAR psztPortName[MAX_PORTNAME_LEN]; + BYTE Reserved[98]; + DWORD dwVersion; + DWORD dwReserved; } DELETE_PORT_DATA_1, *PDELETE_PORT_DATA_1; typedef struct _CONFIG_INFO_DATA_1 { - BYTE Reserved[128]; - DWORD dwVersion; + BYTE Reserved[128]; + DWORD dwVersion; } CONFIG_INFO_DATA_1, *PCONFIG_INFO_DATA_1; diff --git a/Clients/dns-sd.c b/Clients/dns-sd.c index c73c221..146562f 100644 --- a/Clients/dns-sd.c +++ b/Clients/dns-sd.c @@ -37,31 +37,18 @@ * (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 - * on C indentation can be found on the web, such as , - * but for the sake of brevity here I will say just this: Curly braces are not syntactially - * part of an "if" statement; they are the beginning and ending markers of a compound statement; - * therefore common sense dictates that if they are part of a compound statement then they - * should be indented to the same level as everything else in that compound statement. - * Indenting curly braces at the same level as the "if" implies that curly braces are - * part of the "if", which is false. (This is as misleading as people who write "char* x,y;" - * thinking that variables x and y are both of type "char*" -- and anyone who doesn't - * understand why variable y is not of type "char*" just proves the point that poor code - * layout leads people to unfortunate misunderstandings about how the C language really works.) - -To build this tool, copy and paste the following into a command line: - -OS X: -gcc dns-sd.c -o dns-sd - -POSIX systems: -gcc dns-sd.c -o dns-sd -I../mDNSShared -ldns_sd - -Windows: -cl dns-sd.c -I../mDNSShared -DNOT_HAVE_GETOPT ws2_32.lib ..\mDNSWindows\DLL\Release\dnssd.lib -(may require that you run a Visual Studio script such as vsvars32.bat first) -*/ + To build this tool, copy and paste the following into a command line: + + OS X: + gcc dns-sd.c -o dns-sd + + POSIX systems: + gcc dns-sd.c -o dns-sd -I../mDNSShared -ldns_sd + + Windows: + cl dns-sd.c -I../mDNSShared -DNOT_HAVE_GETOPT ws2_32.lib ..\mDNSWindows\DLL\Release\dnssd.lib + (may require that you run a Visual Studio script such as vsvars32.bat first) + */ // For testing changes to dnssd_clientstub.c, uncomment this line and the code will be compiled // with an embedded copy of the client stub instead of linking the system library version at runtime. @@ -79,101 +66,101 @@ cl dns-sd.c -I../mDNSShared -DNOT_HAVE_GETOPT ws2_32.lib ..\mDNSWindows\DLL\Rele #endif #include -#include // For stdout, stderr -#include // For exit() -#include // For strlen(), strcpy() -#include // For errno, EINTR +#include // For stdout, stderr +#include // For exit() +#include // For strlen(), strcpy() +#include // For errno, EINTR #include -#include // For u_char +#include // For u_char #ifdef _WIN32 - #include - #include - #include - #include - typedef int pid_t; - #define getpid _getpid - #define strcasecmp _stricmp - #define snprintf _snprintf - static const char kFilePathSep = '\\'; - #ifndef HeapEnableTerminationOnCorruption - # define HeapEnableTerminationOnCorruption (HEAP_INFORMATION_CLASS)1 - #endif - #if !defined(IFNAMSIZ) - #define IFNAMSIZ 16 + #include + #include + #include + #include +typedef int pid_t; + #define getpid _getpid + #define strcasecmp _stricmp + #define snprintf _snprintf +static const char kFilePathSep = '\\'; + #ifndef HeapEnableTerminationOnCorruption + # define HeapEnableTerminationOnCorruption (HEAP_INFORMATION_CLASS)1 + #endif + #if !defined(IFNAMSIZ) + #define IFNAMSIZ 16 #endif - #define if_nametoindex if_nametoindex_win - #define if_indextoname if_indextoname_win - - typedef PCHAR (WINAPI * if_indextoname_funcptr_t)(ULONG index, PCHAR name); - typedef ULONG (WINAPI * if_nametoindex_funcptr_t)(PCSTR name); - - unsigned if_nametoindex_win(const char *ifname) - { - HMODULE library; - unsigned index = 0; - - // Try and load the IP helper library dll - if ((library = LoadLibrary(TEXT("Iphlpapi")) ) != NULL ) - { - if_nametoindex_funcptr_t if_nametoindex_funcptr; - - // On Vista and above there is a Posix like implementation of if_nametoindex - if ((if_nametoindex_funcptr = (if_nametoindex_funcptr_t) GetProcAddress(library, "if_nametoindex")) != NULL ) - { - index = if_nametoindex_funcptr(ifname); - } - - FreeLibrary(library); - } - - return index; - } - - char * if_indextoname_win( unsigned ifindex, char *ifname) - { - HMODULE library; - char * name = NULL; - - // Try and load the IP helper library dll - if ((library = LoadLibrary(TEXT("Iphlpapi")) ) != NULL ) - { - if_indextoname_funcptr_t if_indextoname_funcptr; - - // On Vista and above there is a Posix like implementation of if_indextoname - if ((if_indextoname_funcptr = (if_indextoname_funcptr_t) GetProcAddress(library, "if_indextoname")) != NULL ) - { - name = if_indextoname_funcptr(ifindex, ifname); - } - - FreeLibrary(library); - } - - return name; - } - - static size_t _sa_len(const struct sockaddr *addr) - { - if (addr->sa_family == AF_INET) return (sizeof(struct sockaddr_in)); - else if (addr->sa_family == AF_INET6) return (sizeof(struct sockaddr_in6)); - else return (sizeof(struct sockaddr)); - } + #define if_nametoindex if_nametoindex_win + #define if_indextoname if_indextoname_win + +typedef PCHAR (WINAPI * if_indextoname_funcptr_t)(ULONG index, PCHAR name); +typedef ULONG (WINAPI * if_nametoindex_funcptr_t)(PCSTR name); + +unsigned if_nametoindex_win(const char *ifname) +{ + HMODULE library; + unsigned index = 0; + + // Try and load the IP helper library dll + if ((library = LoadLibrary(TEXT("Iphlpapi")) ) != NULL ) + { + if_nametoindex_funcptr_t if_nametoindex_funcptr; + + // On Vista and above there is a Posix like implementation of if_nametoindex + if ((if_nametoindex_funcptr = (if_nametoindex_funcptr_t) GetProcAddress(library, "if_nametoindex")) != NULL ) + { + index = if_nametoindex_funcptr(ifname); + } + + FreeLibrary(library); + } + + return index; +} + +char * if_indextoname_win( unsigned ifindex, char *ifname) +{ + HMODULE library; + char * name = NULL; + + // Try and load the IP helper library dll + if ((library = LoadLibrary(TEXT("Iphlpapi")) ) != NULL ) + { + if_indextoname_funcptr_t if_indextoname_funcptr; + + // On Vista and above there is a Posix like implementation of if_indextoname + if ((if_indextoname_funcptr = (if_indextoname_funcptr_t) GetProcAddress(library, "if_indextoname")) != NULL ) + { + name = if_indextoname_funcptr(ifindex, ifname); + } + + FreeLibrary(library); + } + + return name; +} + +static size_t _sa_len(const struct sockaddr *addr) +{ + if (addr->sa_family == AF_INET) return (sizeof(struct sockaddr_in)); + else if (addr->sa_family == AF_INET6) return (sizeof(struct sockaddr_in6)); + else return (sizeof(struct sockaddr)); +} # define SA_LEN(addr) (_sa_len(addr)) #else - #include // For getopt() and optind - #include // For getaddrinfo() - #include // For struct timeval - #include // For AF_INET - #include // For struct sockaddr_in() - #include // For inet_addr() - #include // For if_nametoindex() - static const char kFilePathSep = '/'; + #include // For getopt() and optind + #include // For getaddrinfo() + #include // For struct timeval + #include // For AF_INET + #include // For struct sockaddr_in() + #include // For inet_addr() + #include // For if_nametoindex() +static const char kFilePathSep = '/'; // #ifndef NOT_HAVE_SA_LEN -// #define SA_LEN(addr) ((addr)->sa_len) +// #define SA_LEN(addr) ((addr)->sa_len) // #else - #define SA_LEN(addr) (((addr)->sa_family == AF_INET6)? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) + #define SA_LEN(addr) (((addr)->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) // #endif #endif @@ -182,7 +169,7 @@ cl dns-sd.c -I../mDNSShared -DNOT_HAVE_GETOPT ws2_32.lib ..\mDNSWindows\DLL\Rele #endif // DNSServiceSetDispatchQueue is not supported on 10.6 & prior -#if ! TEST_NEW_CLIENTSTUB && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ - (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ % 10) <= 1060) +#if !TEST_NEW_CLIENTSTUB && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ - (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ % 10) <= 1060) #undef _DNS_SD_LIBDISPATCH #endif #include "dns_sd.h" @@ -210,8 +197,8 @@ typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16; static int operation; static uint32_t opinterface = kDNSServiceInterfaceIndexAny; static DNSServiceRef client = NULL; -static DNSServiceRef client_pa = NULL; // DNSServiceRef for RegisterProxyAddressRecord -static DNSServiceRef sc1, sc2, sc3; // DNSServiceRefs for kDNSServiceFlagsShareConnection testing +static DNSServiceRef client_pa = NULL; // DNSServiceRef for RegisterProxyAddressRecord +static DNSServiceRef sc1, sc2, sc3; // DNSServiceRefs for kDNSServiceFlagsShareConnection testing static int num_printed; static char addtest = 0; @@ -219,7 +206,7 @@ static DNSRecordRef record = NULL; static char myhinfoW[14] = "\002PC\012Windows XP"; static char myhinfoX[ 9] = "\003Mac\004OS X"; static char updatetest[3] = "\002AA"; -static char bigNULL[8192]; // 8K is maximum rdata we support +static char bigNULL[8192]; // 8K is maximum rdata we support #if _DNS_SD_LIBDISPATCH dispatch_queue_t main_queue; @@ -234,7 +221,7 @@ static volatile int timeOut = LONG_TIME; #if _DNS_SD_LIBDISPATCH #define EXIT_IF_LIBDISPATCH_FATAL_ERROR(E) \ - if (main_queue && (E) == kDNSServiceErr_ServiceNotRunning) { fprintf(stderr, "Error code %d\n", (E)); exit(0); } + if (main_queue && (E) == kDNSServiceErr_ServiceNotRunning) { fprintf(stderr, "Error code %d\n", (E)); exit(0); } #else #define EXIT_IF_LIBDISPATCH_FATAL_ERROR(E) #endif @@ -243,582 +230,631 @@ static volatile int timeOut = LONG_TIME; // Supporting Utility Functions 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)); - } +{ + 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, "dnskey" )) return(kDNSServiceType_DNSKEY); + else if (!strcasecmp(s, "ds" )) return(kDNSServiceType_DS); + else if (!strcasecmp(s, "rrsig" )) return(kDNSServiceType_RRSIG); + else if (!strcasecmp(s, "nsec" )) return(kDNSServiceType_NSEC); + else if (!strcasecmp(s, "ANY" )) return(kDNSServiceType_ANY); + else return(atoi(s)); +} #if HAS_NAT_PMP_API | HAS_ADDRINFO_API static DNSServiceProtocol GetProtocol(const char *s) - { - if (!strcasecmp(s, "v4" )) return(kDNSServiceProtocol_IPv4); - else if (!strcasecmp(s, "v6" )) return(kDNSServiceProtocol_IPv6); - else if (!strcasecmp(s, "v4v6" )) return(kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6); - else if (!strcasecmp(s, "v6v4" )) return(kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6); - else if (!strcasecmp(s, "udp" )) return(kDNSServiceProtocol_UDP); - else if (!strcasecmp(s, "tcp" )) return(kDNSServiceProtocol_TCP); - else if (!strcasecmp(s, "udptcp" )) return(kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP); - else if (!strcasecmp(s, "tcpudp" )) return(kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP); - else return(atoi(s)); - } +{ + if (!strcasecmp(s, "v4" )) return(kDNSServiceProtocol_IPv4); + else if (!strcasecmp(s, "v6" )) return(kDNSServiceProtocol_IPv6); + else if (!strcasecmp(s, "v4v6" )) return(kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6); + else if (!strcasecmp(s, "v6v4" )) return(kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6); + else if (!strcasecmp(s, "udp" )) return(kDNSServiceProtocol_UDP); + else if (!strcasecmp(s, "tcp" )) return(kDNSServiceProtocol_TCP); + else if (!strcasecmp(s, "udptcp" )) return(kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP); + else if (!strcasecmp(s, "tcpudp" )) return(kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP); + else return(atoi(s)); +} #endif //************************************************************************************************************* // Sample callback functions for each of the operation types static void printtimestamp(void) - { - struct tm tm; - int ms; +{ + struct tm tm; + int ms; + static char date[16]; + static char new_date[16]; #ifdef _WIN32 - SYSTEMTIME sysTime; - time_t uct = time(NULL); - tm = *localtime(&uct); - GetLocalTime(&sysTime); - ms = sysTime.wMilliseconds; + 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); - ms = tv.tv_usec/1000; + struct timeval tv; + gettimeofday(&tv, NULL); + localtime_r((time_t*)&tv.tv_sec, &tm); + ms = tv.tv_usec/1000; #endif - printf("%2d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, ms); - } + strftime(new_date, sizeof(new_date), "%a %d %b %Y", &tm); + if (strncmp(date, new_date, sizeof(new_date))) + { + printf("DATE: ---%s---\n", new_date); //display date only if it has changed + strncpy(date, new_date, sizeof(date)); + } + printf("%2d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, ms); +} + +static void print_usage(const char *arg0, int print_all) +{ + fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", arg0); + fprintf(stderr, "%s -F (Enumerate recommended browsing domains)\n", arg0); + fprintf(stderr, "%s -R [...] (Register a service)\n", arg0); + fprintf(stderr, "%s -B (Browse for services instances)\n", arg0); + fprintf(stderr, "%s -L (Look up a service instance)\n", arg0); + fprintf(stderr, "%s -P [...] (Proxy)\n", arg0); + fprintf(stderr, "%s -q (Generic query for any record type)\n", arg0); + fprintf(stderr, "%s -Z (Output results in Zone File format)\n", arg0); +#if HAS_ADDRINFO_API + fprintf(stderr, "%s -G v4/v6/v4v6 (Get address information for hostname)\n", arg0); +#endif + fprintf(stderr, "%s -V (Get version of currently running daemon / system service)\n", arg0); -#define DomainMsg(X) (((X) & kDNSServiceFlagsDefault) ? "(Default)" : \ - ((X) & kDNSServiceFlagsAdd) ? "Added" : "Removed") + if (print_all) //Print all available options for dns-sd tool + { + fprintf(stderr, "%s -C (Query; reconfirming each result)\n", arg0); +#if HAS_NAT_PMP_API + fprintf(stderr, "%s -X udp/tcp/udptcp (NAT Port Mapping)\n", arg0); +#endif + fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", arg0); + fprintf(stderr, "%s -U (Test updating a TXT record)\n", arg0); + fprintf(stderr, "%s -N (Test adding a large NULL record)\n", arg0); + fprintf(stderr, "%s -T (Test creating a large TXT record)\n", arg0); + fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", arg0); + fprintf(stderr, "%s -I (Test registering and then immediately updating TXT record)\n", arg0); + fprintf(stderr, "%s -S (Test multiple operations on a shared socket)\n", arg0); + fprintf(stderr, "%s -i (Run dns-sd cmd on a specific interface (en0/en1)\n", arg0); + fprintf(stderr, "%s -lo (Run dns-sd cmd using local only interface)\n", arg0); + fprintf(stderr, "%s -p2p (Use kDNSServiceInterfaceIndexP2P)\n", arg0); + fprintf(stderr, "%s -includep2p (Set kDNSServiceFlagsIncludeP2P flag)\n", arg0); + fprintf(stderr, "%s -includeAWDL (Set kDNSServiceFlagsIncludeAWDL flag)\n", arg0); + fprintf(stderr, "%s -tc (Set kDNSServiceFlagsBackgroundTrafficClass flag)\n", arg0); + } +} + +#define DomainMsg(X) (((X) &kDNSServiceFlagsDefault) ? "(Default)" : \ + ((X) &kDNSServiceFlagsAdd) ? "Added" : "Removed") #define MAX_LABELS 128 static void DNSSD_API enum_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, - DNSServiceErrorType errorCode, const char *replyDomain, void *context) - { - DNSServiceFlags partialflags = flags & ~(kDNSServiceFlagsMoreComing | kDNSServiceFlagsAdd | kDNSServiceFlagsDefault); - int labels = 0, depth = 0, i, initial = 0; - char text[64]; - const char *label[MAX_LABELS]; - - (void)sdref; // Unused - (void)ifIndex; // Unused - (void)context; // Unused - EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); - - // 1. Print the header - if (num_printed++ == 0) printf("Timestamp Recommended %s domain\n", operation == 'E' ? "Registration" : "Browsing"); - printtimestamp(); - if (errorCode) - printf("Error code %d\n", errorCode); - else if (!*replyDomain) - printf("Error: No reply domain\n"); - else - { - printf("%-10s", DomainMsg(flags)); - printf("%-8s", (flags & kDNSServiceFlagsMoreComing) ? "(More)" : ""); - if (partialflags) printf("Flags: %4X ", partialflags); - else printf(" "); - - // 2. Count the labels - while (replyDomain && *replyDomain && labels < MAX_LABELS) - { - 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); - } - } - - if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); - } + DNSServiceErrorType errorCode, const char *replyDomain, void *context) +{ + DNSServiceFlags partialflags = flags & ~(kDNSServiceFlagsMoreComing | kDNSServiceFlagsAdd | kDNSServiceFlagsDefault); + int labels = 0, depth = 0, i, initial = 0; + char text[64]; + const char *label[MAX_LABELS]; + + (void)sdref; // Unused + (void)ifIndex; // Unused + (void)context; // Unused + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); + + // 1. Print the header + if (num_printed++ == 0) printf("Timestamp Recommended %s domain\n", operation == 'E' ? "Registration" : "Browsing"); + printtimestamp(); + if (errorCode) + printf("Error code %d\n", errorCode); + else if (!*replyDomain) + printf("Error: No reply domain\n"); + else + { + printf("%-10s", DomainMsg(flags)); + printf("%-8s", (flags & kDNSServiceFlagsMoreComing) ? "(More)" : ""); + if (partialflags) printf("Flags: %4X ", partialflags); + else printf(" "); + + // 2. Count the labels + while (replyDomain && *replyDomain && labels < MAX_LABELS) + { + 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); + } + } + + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); +} static int CopyLabels(char *dst, const char *lim, const char **srcp, int labels) - { - const char *src = *srcp; - while (*src != '.' || --labels > 0) - { - if (*src == '\\') *dst++ = *src++; // Make sure "\." doesn't confuse us - if (!*src || dst >= lim) return -1; - *dst++ = *src++; - if (!*src || dst >= lim) return -1; - } - *dst++ = 0; - *srcp = src + 1; // skip over final dot - return 0; - } +{ + const char *src = *srcp; + while (*src != '.' || --labels > 0) + { + if (*src == '\\') *dst++ = *src++; // Make sure "\." doesn't confuse us + if (!*src || dst >= lim) return -1; + *dst++ = *src++; + if (!*src || dst >= lim) return -1; + } + *dst++ = 0; + *srcp = src + 1; // skip over final dot + return 0; +} static void DNSSD_API zonedata_resolve(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, - const char *fullname, const char *hosttarget, uint16_t opaqueport, uint16_t txtLen, const unsigned char *txt, void *context) - { - union { uint16_t s; u_char b[2]; } port = { opaqueport }; - uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1]; - - const char *p = fullname; - char n[kDNSServiceMaxDomainName]; - char t[kDNSServiceMaxDomainName]; - - const unsigned char *max = txt + txtLen; - - (void)sdref; // Unused - (void)ifIndex; // Unused - (void)context; // Unused - - //if (!(flags & kDNSServiceFlagsAdd)) return; - if (errorCode) { printf("Error code %d\n", errorCode); return; } - - if (CopyLabels(n, n + kDNSServiceMaxDomainName, &p, 3)) return; // Fetch name+type - p = fullname; - if (CopyLabels(t, t + kDNSServiceMaxDomainName, &p, 1)) return; // Skip first label - if (CopyLabels(t, t + kDNSServiceMaxDomainName, &p, 2)) return; // Fetch next two labels (service type) - - if (num_printed++ == 0) - { - printf("\n"); - printf("; To direct clients to browse a different domain, substitute that domain in place of '@'\n"); - printf("%-47s PTR %s\n", "lb._dns-sd._udp", "@"); - printf("\n"); - printf("; In the list of services below, the SRV records will typically reference dot-local Multicast DNS names.\n"); - printf("; When transferring this zone file data to your unicast DNS server, you'll need to replace those dot-local\n"); - printf("; names with the correct fully-qualified (unicast) domain name of the target host offering the service.\n"); - } - - printf("\n"); - printf("%-47s PTR %s\n", t, n); - printf("%-47s SRV 0 0 %d %s ; Replace with unicast FQDN of target host\n", n, PortAsNumber, hosttarget); - printf("%-47s TXT ", n); - - while (txt < max) - { - const unsigned char *const end = txt + 1 + txt[0]; - txt++; // Skip over length byte - printf(" \""); - while (txt max) { printf("<< invalid data >>"); break; } - if (++ptr < end) printf(" "); // As long as string is non-empty, begin with a space - while (ptr^()[]{}$", *ptr)) printf("\\"); - if (*ptr == '\\') printf("\\\\\\\\"); - else if (*ptr >= ' ' ) printf("%c", *ptr); - else printf("\\\\x%02X", *ptr); - ptr++; - } - } - } +{ + const unsigned char *ptr = txtRecord; + const unsigned char *max = txtRecord + txtLen; + while (ptr < max) + { + const unsigned char *const end = ptr + 1 + ptr[0]; + if (end > max) { printf("<< invalid data >>"); break; } + if (++ptr < end) printf(" "); // As long as string is non-empty, begin with a space + while (ptr^()[]{}$", *ptr)) printf("\\"); + if (*ptr == '\\') printf("\\\\\\\\"); + else if (*ptr >= ' ' ) printf("%c", *ptr); + else printf("\\\\x%02X", *ptr); + ptr++; + } + } +} static void DNSSD_API resolve_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, - const char *fullname, const char *hosttarget, uint16_t opaqueport, uint16_t txtLen, const unsigned char *txtRecord, void *context) - { - union { uint16_t s; u_char b[2]; } port = { opaqueport }; - uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1]; - - (void)sdref; // Unused - (void)ifIndex; // Unused - (void)context; // Unused - EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); - - if (errorCode) - printf("Error code %d\n", errorCode); - else - { - printtimestamp(); - printf("%s can be reached at %s:%u (interface %d)", fullname, hosttarget, PortAsNumber, ifIndex); - if (flags) printf(" Flags: %X", flags); - // Don't show degenerate TXT records containing nothing but a single empty string - if (txtLen > 1) { printf("\n"); ShowTXTRecord(txtLen, txtRecord); } - printf("\n"); - } - - if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); - } + const char *fullname, const char *hosttarget, uint16_t opaqueport, uint16_t txtLen, const unsigned char *txtRecord, void *context) +{ + union { uint16_t s; u_char b[2]; } port = { opaqueport }; + uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1]; + + (void)sdref; // Unused + (void)ifIndex; // Unused + (void)context; // Unused + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); + + if (errorCode) + printf("Error code %d\n", errorCode); + else + { + printtimestamp(); + printf("%s can be reached at %s:%u (interface %d)", fullname, hosttarget, PortAsNumber, ifIndex); + if (flags) printf(" Flags: %X", flags); + // Don't show degenerate TXT records containing nothing but a single empty string + if (txtLen > 1) { printf("\n"); ShowTXTRecord(txtLen, txtRecord); } + printf("\n"); + } + + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); +} static void myTimerCallBack(void) - { - DNSServiceErrorType err = kDNSServiceErr_Unknown; - - switch (operation) - { - case 'A': - { - switch (addtest) - { - case 0: printf("Adding Test HINFO record\n"); - 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], 0); - addtest = 2; - break; - case 2: printf("Removing Test HINFO record\n"); - err = DNSServiceRemoveRecord(client, record, 0); - addtest = 0; - break; - } - } - break; - - case 'U': - { - if (updatetest[1] != 'Z') updatetest[1]++; - else updatetest[1] = 'A'; - updatetest[0] = 3 - updatetest[0]; - updatetest[2] = updatetest[1]; - printtimestamp(); - printf("Updating Test TXT record to %c\n", updatetest[1]); - 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, kDNSServiceType_NULL, sizeof(bigNULL), &bigNULL[0], 0); - if (err) printf("Failed: %d\n", err); else printf("Succeeded\n"); - timeOut = LONG_TIME; +{ + DNSServiceErrorType err = kDNSServiceErr_Unknown; + + switch (operation) + { + case 'A': + { + switch (addtest) + { + case 0: printf("Adding Test HINFO record\n"); + 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], 0); + addtest = 2; + break; + case 2: printf("Removing Test HINFO record\n"); + err = DNSServiceRemoveRecord(client, record, 0); + addtest = 0; + break; + } + } + break; + + case 'U': + { + if (updatetest[1] != 'Z') updatetest[1]++; + else updatetest[1] = 'A'; + updatetest[0] = 3 - updatetest[0]; + updatetest[2] = updatetest[1]; + printtimestamp(); + printf("Updating Test TXT record to %c\n", updatetest[1]); + 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, kDNSServiceType_NULL, sizeof(bigNULL), &bigNULL[0], 0); + if (err) printf("Failed: %d\n", err);else printf("Succeeded\n"); + timeOut = LONG_TIME; #if _DNS_SD_LIBDISPATCH - if (timer_source) - dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (uint64_t)timeOut * NSEC_PER_SEC), - (uint64_t)timeOut * NSEC_PER_SEC, 0); + if (timer_source) + dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (uint64_t)timeOut * NSEC_PER_SEC), + (uint64_t)timeOut * NSEC_PER_SEC, 0); #endif - } - break; - } + } + break; + } - if (err != kDNSServiceErr_NoError) - { - fprintf(stderr, "DNSService add/update/remove failed %ld\n", (long int)err); - stopNow = 1; - } - } + if (err != kDNSServiceErr_NoError) + { + fprintf(stderr, "DNSService add/update/remove failed %ld\n", (long int)err); + stopNow = 1; + } +} static void DNSSD_API reg_reply(DNSServiceRef sdref, const DNSServiceFlags flags, DNSServiceErrorType errorCode, - const char *name, const char *regtype, const char *domain, void *context) - { - (void)sdref; // Unused - (void)flags; // Unused - (void)context; // Unused - EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); - - printtimestamp(); - printf("Got a reply for service %s.%s%s: ", name, regtype, domain); - - if (errorCode == kDNSServiceErr_NoError) - { - if (flags & kDNSServiceFlagsAdd) printf("Name now registered and active\n"); - else printf("Name registration removed\n"); - if (operation == 'A' || operation == 'U' || operation == 'N') - { - timeOut = 5; + const char *name, const char *regtype, const char *domain, void *context) +{ + (void)sdref; // Unused + (void)flags; // Unused + (void)context; // Unused + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); + + printtimestamp(); + printf("Got a reply for service %s.%s%s: ", name, regtype, domain); + + if (errorCode == kDNSServiceErr_NoError) + { + if (flags & kDNSServiceFlagsAdd) printf("Name now registered and active\n"); + else printf("Name registration removed\n"); + if (operation == 'A' || operation == 'U' || operation == 'N') + { + timeOut = 5; #if _DNS_SD_LIBDISPATCH - if (timer_source) - dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (uint64_t)timeOut * NSEC_PER_SEC), - (uint64_t)timeOut * NSEC_PER_SEC, 0); + if (timer_source) + dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (uint64_t)timeOut * NSEC_PER_SEC), + (uint64_t)timeOut * NSEC_PER_SEC, 0); #endif - } - } - else if (errorCode == kDNSServiceErr_NameConflict) - { - printf("Name in use, please choose another\n"); - exit(-1); - } - else - printf("Error %d\n", errorCode); - - if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); - } + } + } + else if (errorCode == kDNSServiceErr_NameConflict) + { + printf("Name in use, please choose another\n"); + exit(-1); + } + else + printf("Error %d\n", errorCode); + + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); +} // Output the wire-format domainname pointed to by rd static int snprintd(char *p, int max, const unsigned char **rd) - { - const char *const buf = p; - const char *const end = p + max; - while (**rd) { p += snprintf(p, end-p, "%.*s.", **rd, *rd+1); *rd += 1 + **rd; } - *rd += 1; // Advance over the final zero byte - return(p-buf); - } +{ + const char *const buf = p; + const char *const end = p + max; + while (**rd) { p += snprintf(p, end-p, "%.*s.", **rd, *rd+1); *rd += 1 + **rd; } + *rd += 1; // Advance over the final zero byte + return(p-buf); +} static void DNSSD_API qr_reply(DNSServiceRef sdref, const 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) - { - char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv"; - const unsigned char *rd = rdata; - const unsigned char *end = (const unsigned char *) rdata + rdlen; - char rdb[1000] = "", *p = rdb; - int unknowntype = 0; - - (void)sdref; // Unused - (void)flags; // Unused - (void)ifIndex; // Unused - (void)ttl; // Unused - (void)context; // Unused - EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); - - if (num_printed++ == 0) printf("Timestamp A/R Flags if %-30s%4s%4s Rdata\n", "Name", "T", "C"); - printtimestamp(); - - if (!errorCode) - { - switch (rrtype) - { - case kDNSServiceType_A: - snprintf(rdb, sizeof(rdb), "%d.%d.%d.%d", rd[0], rd[1], rd[2], rd[3]); - break; - - case kDNSServiceType_NS: - case kDNSServiceType_CNAME: - case kDNSServiceType_PTR: - case kDNSServiceType_DNAME: - p += snprintd(p, sizeof(rdb), &rd); - break; - - case kDNSServiceType_SOA: - p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // mname - p += snprintf(p, rdb + sizeof(rdb) - p, " "); - p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // rname - p += snprintf(p, rdb + sizeof(rdb) - p, " Ser %d Ref %d Ret %d Exp %d Min %d", - ntohl(((uint32_t*)rd)[0]), ntohl(((uint32_t*)rd)[1]), ntohl(((uint32_t*)rd)[2]), ntohl(((uint32_t*)rd)[3]), ntohl(((uint32_t*)rd)[4])); - break; - - case kDNSServiceType_AAAA: - snprintf(rdb, sizeof(rdb), "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X", - rd[0x0], rd[0x1], rd[0x2], rd[0x3], rd[0x4], rd[0x5], rd[0x6], rd[0x7], - rd[0x8], rd[0x9], rd[0xA], rd[0xB], rd[0xC], rd[0xD], rd[0xE], rd[0xF]); - break; - - case kDNSServiceType_SRV: - p += snprintf(p, rdb + sizeof(rdb) - p, "%d %d %d ", // priority, weight, port - ntohs(*(unsigned short*)rd), ntohs(*(unsigned short*)(rd+2)), ntohs(*(unsigned short*)(rd+4))); - rd += 6; - p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // target host - break; - - default : snprintf(rdb, sizeof(rdb), "%d bytes%s", rdlen, rdlen ? ":" : ""); unknowntype = 1; break; - } - } - - printf("%s%6X%3d %-30s%4d%4d %s", op, flags, ifIndex, fullname, rrtype, rrclass, rdb); - if (unknowntype) while (rd < end) printf(" %02X", *rd++); - if (errorCode) - { - if (errorCode == kDNSServiceErr_NoSuchRecord) printf("No Such Record"); - else if (errorCode == kDNSServiceErr_Timeout) - { - printf("No Such Record\n"); - printf("Query Timed Out\n"); - exit(1); - } - } - printf("\n"); - - if (operation == 'C') - if (flags & kDNSServiceFlagsAdd) - DNSServiceReconfirmRecord(flags, ifIndex, fullname, rrtype, rrclass, rdlen, rdata); - - if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); - } + const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context) +{ + char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv"; + const unsigned char *rd = rdata; + const unsigned char *end = (const unsigned char *) rdata + rdlen; + char rdb[1000] = "", *p = rdb; + int unknowntype = 0; + + (void)sdref; // Unused + (void)flags; // Unused + (void)ifIndex; // Unused + (void)ttl; // Unused + (void)context; // Unused + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); + + if (num_printed++ == 0) printf("Timestamp A/R Flags if %-30s%4s%4s Rdata\n", "Name", "T", "C"); + printtimestamp(); + + if (!errorCode) + { + switch (rrtype) + { + case kDNSServiceType_A: + snprintf(rdb, sizeof(rdb), "%d.%d.%d.%d", rd[0], rd[1], rd[2], rd[3]); + break; + + case kDNSServiceType_NS: + case kDNSServiceType_CNAME: + case kDNSServiceType_PTR: + case kDNSServiceType_DNAME: + p += snprintd(p, sizeof(rdb), &rd); + break; + + case kDNSServiceType_SOA: + p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // mname + p += snprintf(p, rdb + sizeof(rdb) - p, " "); + p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // rname + p += snprintf(p, rdb + sizeof(rdb) - p, " Ser %d Ref %d Ret %d Exp %d Min %d", + ntohl(((uint32_t*)rd)[0]), ntohl(((uint32_t*)rd)[1]), ntohl(((uint32_t*)rd)[2]), ntohl(((uint32_t*)rd)[3]), ntohl(((uint32_t*)rd)[4])); + break; + + case kDNSServiceType_AAAA: + snprintf(rdb, sizeof(rdb), "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X", + rd[0x0], rd[0x1], rd[0x2], rd[0x3], rd[0x4], rd[0x5], rd[0x6], rd[0x7], + rd[0x8], rd[0x9], rd[0xA], rd[0xB], rd[0xC], rd[0xD], rd[0xE], rd[0xF]); + break; + + case kDNSServiceType_SRV: + p += snprintf(p, rdb + sizeof(rdb) - p, "%d %d %d ", // priority, weight, port + ntohs(*(unsigned short*)rd), ntohs(*(unsigned short*)(rd+2)), ntohs(*(unsigned short*)(rd+4))); + rd += 6; + p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // target host + break; + + default: snprintf(rdb, sizeof(rdb), "%d bytes%s", rdlen, rdlen ? ":" : ""); unknowntype = 1; break; + } + } + + printf("%s%6X%3d %-30s%4d%4d %s", op, flags, ifIndex, fullname, rrtype, rrclass, rdb); + if (unknowntype) while (rd < end) printf(" %02X", *rd++); + if (errorCode) + { + if (errorCode == kDNSServiceErr_NoSuchRecord) printf("No Such Record"); + else if (errorCode == kDNSServiceErr_Timeout) + { + printf("No Such Record\n"); + printf("Query Timed Out\n"); + exit(1); + } + } + printf("\n"); + + if (operation == 'C') + if (flags & kDNSServiceFlagsAdd) + DNSServiceReconfirmRecord(flags, ifIndex, fullname, rrtype, rrclass, rdlen, rdata); + + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); +} #if HAS_NAT_PMP_API static void DNSSD_API port_mapping_create_reply(DNSServiceRef sdref, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, uint32_t publicAddress, uint32_t protocol, uint16_t privatePort, uint16_t publicPort, uint32_t ttl, void *context) - { - (void)sdref; // Unused - (void)flags; // Unused - (void)context; // Unused - EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); - - if (num_printed++ == 0) printf("Timestamp if %-20s %-15s %-15s %-15s %-6s\n", "External Address", "Protocol", "Internal Port", "External Port", "TTL"); - printtimestamp(); - if (errorCode && errorCode != kDNSServiceErr_DoubleNAT) printf("Error code %d\n", errorCode); - else - { - const unsigned char *digits = (const unsigned char *)&publicAddress; - char addr[256]; - - snprintf(addr, sizeof(addr), "%d.%d.%d.%d", digits[0], digits[1], digits[2], digits[3]); - printf("%-4d %-20s %-15d %-15d %-15d %-6d%s\n", ifIndex, addr, protocol, ntohs(privatePort), ntohs(publicPort), ttl, errorCode == kDNSServiceErr_DoubleNAT ? " Double NAT" : ""); - } - - if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); - } +{ + (void)sdref; // Unused + (void)flags; // Unused + (void)context; // Unused + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); + + if (num_printed++ == 0) printf("Timestamp if %-20s %-15s %-15s %-15s %-6s\n", "External Address", "Protocol", "Internal Port", "External Port", "TTL"); + printtimestamp(); + if (errorCode && errorCode != kDNSServiceErr_DoubleNAT) printf("Error code %d\n", errorCode); + else + { + const unsigned char *digits = (const unsigned char *)&publicAddress; + char addr[256]; + + snprintf(addr, sizeof(addr), "%d.%d.%d.%d", digits[0], digits[1], digits[2], digits[3]); + printf("%-4d %-20s %-15d %-15d %-15d %-6d%s\n", ifIndex, addr, protocol, ntohs(privatePort), ntohs(publicPort), ttl, errorCode == kDNSServiceErr_DoubleNAT ? " Double NAT" : ""); + } + + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); +} #endif #if HAS_ADDRINFO_API static void DNSSD_API addrinfo_reply(DNSServiceRef sdref, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *hostname, const struct sockaddr *address, uint32_t ttl, void *context) - { - char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv"; - char addr[256] = ""; - (void) sdref; - (void) context; - EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); - - if (num_printed++ == 0) printf("Timestamp A/R Flags if %-25s %-44s %s\n", "Hostname", "Address", "TTL"); - printtimestamp(); - - if (address && address->sa_family == AF_INET) - { - const unsigned char *b = (const unsigned char *) &((struct sockaddr_in *)address)->sin_addr; - snprintf(addr, sizeof(addr), "%d.%d.%d.%d", b[0], b[1], b[2], b[3]); - } - else if (address && address->sa_family == AF_INET6) - { - char if_name[IFNAMSIZ]; // Older Linux distributions don't define IF_NAMESIZE - const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *)address; - const unsigned char *b = (const unsigned char * )&s6->sin6_addr; - if (!if_indextoname(s6->sin6_scope_id, if_name)) - snprintf(if_name, sizeof(if_name), "<%d>", s6->sin6_scope_id); - snprintf(addr, sizeof(addr), "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X%%%s", - b[0x0], b[0x1], b[0x2], b[0x3], b[0x4], b[0x5], b[0x6], b[0x7], - b[0x8], b[0x9], b[0xA], b[0xB], b[0xC], b[0xD], b[0xE], b[0xF], if_name); - } - - printf("%s%6X%3d %-25s %-44s %d", op, flags, interfaceIndex, hostname, addr, ttl); - if (errorCode) - { - if (errorCode == kDNSServiceErr_NoSuchRecord) printf(" No Such Record"); - else printf(" Error code %d", errorCode); - } - printf("\n"); - - if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); - } +{ + char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv"; + char addr[256] = ""; + (void) sdref; + (void) context; + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); + + if (num_printed++ == 0) printf("Timestamp A/R Flags if %-25s %-44s %s\n", "Hostname", "Address", "TTL"); + printtimestamp(); + + if (address && address->sa_family == AF_INET) + { + const unsigned char *b = (const unsigned char *) &((struct sockaddr_in *)address)->sin_addr; + snprintf(addr, sizeof(addr), "%d.%d.%d.%d", b[0], b[1], b[2], b[3]); + } + else if (address && address->sa_family == AF_INET6) + { + char if_name[IFNAMSIZ]; // Older Linux distributions don't define IF_NAMESIZE + const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *)address; + const unsigned char *b = (const unsigned char * )&s6->sin6_addr; + if (!if_indextoname(s6->sin6_scope_id, if_name)) + snprintf(if_name, sizeof(if_name), "<%d>", s6->sin6_scope_id); + snprintf(addr, sizeof(addr), "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X%%%s", + b[0x0], b[0x1], b[0x2], b[0x3], b[0x4], b[0x5], b[0x6], b[0x7], + b[0x8], b[0x9], b[0xA], b[0xB], b[0xC], b[0xD], b[0xE], b[0xF], if_name); + } + + printf("%s%6X%3d %-25s %-44s %d", op, flags, interfaceIndex, hostname, addr, ttl); + if (errorCode) + { + if (errorCode == kDNSServiceErr_NoSuchRecord) printf(" No Such Record"); + else printf(" Error code %d", errorCode); + } + printf("\n"); + + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); +} #endif //************************************************************************************************************* @@ -826,516 +862,514 @@ static void DNSSD_API addrinfo_reply(DNSServiceRef sdref, DNSServiceFlags flags, static void HandleEvents(void) #if _DNS_SD_LIBDISPATCH - { - main_queue = dispatch_get_main_queue(); - if (client) DNSServiceSetDispatchQueue(client, main_queue); - if (client_pa) DNSServiceSetDispatchQueue(client_pa, main_queue); - if (operation == 'A' || operation == 'U' || operation == 'N') - { - timer_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, main_queue); - if (timer_source) - { - // Start the timer "timeout" seconds into the future and repeat it every "timeout" seconds - dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (uint64_t)timeOut * NSEC_PER_SEC), - (uint64_t)timeOut * NSEC_PER_SEC, 0); - dispatch_source_set_event_handler(timer_source, ^{myTimerCallBack();}); - dispatch_resume(timer_source); - } - } - dispatch_main(); - } +{ + main_queue = dispatch_get_main_queue(); + if (client) DNSServiceSetDispatchQueue(client, main_queue); + if (client_pa) DNSServiceSetDispatchQueue(client_pa, main_queue); + if (operation == 'A' || operation == 'U' || operation == 'N') + { + timer_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, main_queue); + if (timer_source) + { + // Start the timer "timeout" seconds into the future and repeat it every "timeout" seconds + dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (uint64_t)timeOut * NSEC_PER_SEC), + (uint64_t)timeOut * NSEC_PER_SEC, 0); + dispatch_source_set_event_handler(timer_source, ^{myTimerCallBack();}); + dispatch_resume(timer_source); + } + } + dispatch_main(); +} #else - { - int dns_sd_fd = client ? DNSServiceRefSockFD(client ) : -1; - int dns_sd_fd2 = client_pa ? DNSServiceRefSockFD(client_pa) : -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) - { - // 1. Set up the fd_set as usual here. - // This example client has no file descriptors of its own, - // but a real application would call FD_SET to add them to the set here - FD_ZERO(&readfds); - - // 2. Add the fd for our client(s) to the fd_set - if (client ) FD_SET(dns_sd_fd , &readfds); - if (client_pa) FD_SET(dns_sd_fd2, &readfds); - - // 3. Set up the timeout. - tv.tv_sec = timeOut; - tv.tv_usec = 0; - - result = select(nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv); - if (result > 0) - { - DNSServiceErrorType err = kDNSServiceErr_NoError; - if (client && FD_ISSET(dns_sd_fd , &readfds)) err = DNSServiceProcessResult(client ); - else if (client_pa && FD_ISSET(dns_sd_fd2, &readfds)) err = DNSServiceProcessResult(client_pa); - if (err) { fprintf(stderr, "DNSServiceProcessResult returned %d\n", err); stopNow = 1; } - } - else if (result == 0) - myTimerCallBack(); - else - { - printf("select() returned %d errno %d %s\n", result, errno, strerror(errno)); - if (errno != EINTR) stopNow = 1; - } - } - } +{ + int dns_sd_fd = client ? DNSServiceRefSockFD(client ) : -1; + int dns_sd_fd2 = client_pa ? DNSServiceRefSockFD(client_pa) : -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) + { + // 1. Set up the fd_set as usual here. + // This example client has no file descriptors of its own, + // but a real application would call FD_SET to add them to the set here + FD_ZERO(&readfds); + + // 2. Add the fd for our client(s) to the fd_set + if (client ) FD_SET(dns_sd_fd, &readfds); + if (client_pa) FD_SET(dns_sd_fd2, &readfds); + + // 3. Set up the timeout. + tv.tv_sec = timeOut; + tv.tv_usec = 0; + + result = select(nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv); + if (result > 0) + { + DNSServiceErrorType err = kDNSServiceErr_NoError; + if (client && FD_ISSET(dns_sd_fd, &readfds)) err = DNSServiceProcessResult(client ); + else if (client_pa && FD_ISSET(dns_sd_fd2, &readfds)) err = DNSServiceProcessResult(client_pa); + if (err) { fprintf(stderr, "DNSServiceProcessResult returned %d\n", err); stopNow = 1; } + } + else if (result == 0) + myTimerCallBack(); + else + { + printf("select() returned %d errno %d %s\n", result, errno, strerror(errno)); + if (errno != EINTR) stopNow = 1; + } + } +} #endif 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; - } +{ + 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 o = getopt(argc, (char *const *)argv, optstr); - *pOptInd = optind; - return o; - } +{ + int o = getopt(argc, (char *const *)argv, optstr); + *pOptInd = optind; + return o; +} #endif static void DNSSD_API MyRegisterRecordCallback(DNSServiceRef service, DNSRecordRef rec, const DNSServiceFlags flags, - DNSServiceErrorType errorCode, void *context) - { - char *name = (char *)context; - - (void)service; // Unused - (void)rec; // Unused - (void)flags; // Unused - EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); - - printtimestamp(); - printf("Got a reply for record %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); break; - } - if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); - // DNSServiceRemoveRecord(service, rec, 0); to test record removal - -#if 0 // To test updating of individual records registered via DNSServiceRegisterRecord - if (!errorCode) - { - int x = 0x11111111; - printf("Updating\n"); - DNSServiceUpdateRecord(service, rec, 0, sizeof(x), &x, 0); - } + DNSServiceErrorType errorCode, void *context) +{ + char *name = (char *)context; + + (void)service; // Unused + (void)rec; // Unused + (void)flags; // Unused + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); + + printtimestamp(); + printf("Got a reply for record %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); break; + } + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); + // DNSServiceRemoveRecord(service, rec, 0); to test record removal + +#if 0 // To test updating of individual records registered via DNSServiceRegisterRecord + if (!errorCode) + { + int x = 0x11111111; + printf("Updating\n"); + DNSServiceUpdateRecord(service, rec, 0, sizeof(x), &x, 0); + } #endif - if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); - } + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); +} static void getip(const char *const name, struct sockaddr_storage *result) - { - struct addrinfo *addrs = NULL; - int err = getaddrinfo(name, NULL, NULL, &addrs); - if (err) fprintf(stderr, "getaddrinfo error %d for %s", err, name); - else memcpy(result, addrs->ai_addr, SA_LEN(addrs->ai_addr)); - if (addrs) freeaddrinfo(addrs); - } +{ + struct addrinfo *addrs = NULL; + int err = getaddrinfo(name, NULL, NULL, &addrs); + if (err) fprintf(stderr, "getaddrinfo error %d for %s", err, name); + else memcpy(result, addrs->ai_addr, SA_LEN(addrs->ai_addr)); + if (addrs) freeaddrinfo(addrs); +} static DNSServiceErrorType RegisterProxyAddressRecord(DNSServiceRef sdref, const char *host, const char *ip, DNSServiceFlags flags) - { - // Call getip() after the call DNSServiceCreateConnection(). - // On the Win32 platform, WinSock must be initialized for getip() to succeed. - // Any DNSService* call will initialize WinSock for us, so we make sure - // DNSServiceCreateConnection() is called before getip() is. - struct sockaddr_storage hostaddr; - getip(ip, &hostaddr); - flags |= kDNSServiceFlagsUnique; - if (hostaddr.ss_family == AF_INET) - return(DNSServiceRegisterRecord(sdref, &record, flags, opinterface, host, - kDNSServiceType_A, kDNSServiceClass_IN, 4, &((struct sockaddr_in *)&hostaddr)->sin_addr, 240, MyRegisterRecordCallback, (void*)host)); - else if (hostaddr.ss_family == AF_INET6) - return(DNSServiceRegisterRecord(sdref, &record, flags, opinterface, host, - kDNSServiceType_AAAA, kDNSServiceClass_IN, 16, &((struct sockaddr_in6*)&hostaddr)->sin6_addr, 240, MyRegisterRecordCallback, (void*)host)); - else return(kDNSServiceErr_BadParam); - } +{ + // Call getip() after the call DNSServiceCreateConnection(). + // On the Win32 platform, WinSock must be initialized for getip() to succeed. + // Any DNSService* call will initialize WinSock for us, so we make sure + // DNSServiceCreateConnection() is called before getip() is. + struct sockaddr_storage hostaddr; + getip(ip, &hostaddr); + flags |= kDNSServiceFlagsUnique; + if (hostaddr.ss_family == AF_INET) + return(DNSServiceRegisterRecord(sdref, &record, flags, opinterface, host, + kDNSServiceType_A, kDNSServiceClass_IN, 4, &((struct sockaddr_in *)&hostaddr)->sin_addr, 240, MyRegisterRecordCallback, (void*)host)); + else if (hostaddr.ss_family == AF_INET6) + return(DNSServiceRegisterRecord(sdref, &record, flags, opinterface, host, + kDNSServiceType_AAAA, kDNSServiceClass_IN, 16, &((struct sockaddr_in6*)&hostaddr)->sin6_addr, 240, MyRegisterRecordCallback, (void*)host)); + else return(kDNSServiceErr_BadParam); +} #define HexVal(X) ( ((X) >= '0' && (X) <= '9') ? ((X) - '0' ) : \ - ((X) >= 'A' && (X) <= 'F') ? ((X) - 'A' + 10) : \ - ((X) >= 'a' && (X) <= 'f') ? ((X) - 'a' + 10) : 0) + ((X) >= 'A' && (X) <= 'F') ? ((X) - 'A' + 10) : \ + ((X) >= 'a' && (X) <= 'f') ? ((X) - 'a' + 10) : 0) #define HexPair(P) ((HexVal((P)[0]) << 4) | HexVal((P)[1])) static DNSServiceErrorType RegisterService(DNSServiceRef *sdref, - const char *nam, const char *typ, const char *dom, const char *host, const char *port, int argc, char **argv, DNSServiceFlags flags) - { - uint16_t PortAsNumber = atoi(port); - Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } }; - unsigned char txt[2048] = ""; - unsigned 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 - - printf("Registering Service %s.%s%s%s", nam[0] ? nam : "<>", typ, dom[0] ? "." : "", dom); - if (host && *host) printf(" host %s", host); - printf(" port %s", port); - - if (argc) - { - for (i = 0; i < argc; i++) - { - const char *p = argv[i]; - *ptr = 0; - while (*p && *ptr < 255 && ptr + 1 + *ptr < txt+sizeof(txt)) - { - if (p[0] != '\\' || p[1] == 0) { ptr[++*ptr] = *p; p+=1; } - else if (p[1] == 'x' && isxdigit(p[2]) && isxdigit(p[3])) { ptr[++*ptr] = HexPair(p+2); p+=4; } - else { ptr[++*ptr] = p[1]; p+=2; } - } - ptr += 1 + *ptr; - } - printf(" TXT"); - ShowTXTRecord(ptr-txt, txt); - } - printf("\n"); - - //flags |= kDNSServiceFlagsAllowRemoteQuery; - //flags |= kDNSServiceFlagsNoAutoRename; - - return(DNSServiceRegister(sdref, flags, opinterface, nam, typ, dom, host, registerPort.NotAnInteger, (uint16_t) (ptr-txt), txt, reg_reply, NULL)); - } + const char *nam, const char *typ, const char *dom, const char *host, const char *port, int argc, char **argv, DNSServiceFlags flags) +{ + uint16_t PortAsNumber = atoi(port); + Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } }; + unsigned char txt[2048] = ""; + unsigned 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 + + printf("Registering Service %s.%s%s%s", nam[0] ? nam : "<>", typ, dom[0] ? "." : "", dom); + if (host && *host) printf(" host %s", host); + printf(" port %s", port); + + if (argc) + { + for (i = 0; i < argc; i++) + { + const char *p = argv[i]; + *ptr = 0; + while (*p && *ptr < 255 && ptr + 1 + *ptr < txt+sizeof(txt)) + { + if (p[0] != '\\' || p[1] == 0) { ptr[++*ptr] = *p; p+=1; } + else if (p[1] == 'x' && isxdigit(p[2]) && isxdigit(p[3])) { ptr[++*ptr] = HexPair(p+2); p+=4; } + else { ptr[++*ptr] = p[1]; p+=2; } + } + ptr += 1 + *ptr; + } + printf(" TXT"); + ShowTXTRecord(ptr-txt, txt); + } + printf("\n"); + + //flags |= kDNSServiceFlagsAllowRemoteQuery; + //flags |= kDNSServiceFlagsNoAutoRename; + + return(DNSServiceRegister(sdref, flags, opinterface, nam, typ, dom, host, registerPort.NotAnInteger, (uint16_t) (ptr-txt), txt, reg_reply, NULL)); +} #define TypeBufferSize 80 static char *gettype(char *buffer, char *typ) - { - if (!typ || !*typ || (typ[0] == '.' && typ[1] == 0)) typ = "_http._tcp"; - if (!strchr(typ, '.')) { snprintf(buffer, TypeBufferSize, "%s._tcp", typ); typ = buffer; } - return(typ); - } +{ + if (!typ || !*typ || (typ[0] == '.' && typ[1] == 0)) typ = "_http._tcp"; + if (!strchr(typ, '.')) { snprintf(buffer, TypeBufferSize, "%s._tcp", typ); typ = buffer; } + return(typ); +} int main(int argc, char **argv) - { - DNSServiceErrorType err; - char buffer[TypeBufferSize], *typ, *dom; - int opi; - DNSServiceFlags flags = 0; - - // Extract the program name from argv[0], which by convention contains the path to this executable. - // Note that this is just a voluntary convention, not enforced by the kernel -- - // the process calling exec() can pass bogus data in argv[0] if it chooses to. - const char *a0 = strrchr(argv[0], kFilePathSep) + 1; - if (a0 == (const char *)1) a0 = argv[0]; +{ + DNSServiceErrorType err; + char buffer[TypeBufferSize], *typ, *dom; + int opi; + DNSServiceFlags flags = 0; + + // Extract the program name from argv[0], which by convention contains the path to this executable. + // Note that this is just a voluntary convention, not enforced by the kernel -- + // the process calling exec() can pass bogus data in argv[0] if it chooses to. + const char *a0 = strrchr(argv[0], kFilePathSep) + 1; + if (a0 == (const char *)1) a0 = argv[0]; #if defined(_WIN32) - HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); + HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); #endif #if TEST_NEW_CLIENTSTUB - printf("Using embedded copy of dnssd_clientstub instead of system library\n"); - if (sizeof(argv) == 8) printf("Running in 64-bit mode\n"); + printf("Using embedded copy of dnssd_clientstub instead of system library\n"); + if (sizeof(argv) == 8) printf("Running in 64-bit mode\n"); #endif - // Test code for TXTRecord functions - //TXTRecordRef txtRecord; - //TXTRecordCreate(&txtRecord, 0, NULL); - //TXTRecordSetValue(&txtRecord, "aaa", 1, "b"); - //printf("%d\n", TXTRecordContainsKey(TXTRecordGetLength(&txtRecord), TXTRecordGetBytesPtr(&txtRecord), "Aaa")); - - if (argc > 1 && !strcmp(argv[1], "-lo")) - { - argc--; - argv++; - opinterface = kDNSServiceInterfaceIndexLocalOnly; - printf("Using LocalOnly\n"); - } - - if (argc > 1 && (!strcmp(argv[1], "-p2p") || !strcmp(argv[1], "-P2P"))) - { - argc--; - argv++; - opinterface = kDNSServiceInterfaceIndexP2P; - printf("Using P2P\n"); - } - - if (argc > 1 && !strcasecmp(argv[1], "-includep2p")) - { - argc--; - argv++; - flags |= kDNSServiceFlagsIncludeP2P; - printf("Including P2P\n"); - } - - if (argc > 2 && !strcmp(argv[1], "-i")) - { - opinterface = if_nametoindex(argv[2]); - if (!opinterface) opinterface = atoi(argv[2]); - if (!opinterface) { fprintf(stderr, "Unknown interface %s\n", argv[2]); goto Fail; } - argc -= 2; - argv += 2; - } - - if (argc < 2) goto Fail; // Minimum command line is the command name and one argument - operation = getfirstoption(argc, argv, "EFBZLlRPQqtCAUNTMISV" - #if HAS_NAT_PMP_API - "X" - #endif - #if HAS_ADDRINFO_API - "G" - #endif - , &opi); - if (operation == -1) goto Fail; - - if (opinterface) printf("Using interface %d\n", opinterface); - - switch (operation) - { - case 'E': printf("Looking for recommended registration domains:\n"); - err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsRegistrationDomains, opinterface, enum_reply, NULL); - break; - - case 'F': printf("Looking for recommended browsing domains:\n"); - err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsBrowseDomains, opinterface, enum_reply, NULL); - //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "nicta.com.au.", NULL); - //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "bonjour.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': typ = (argc < opi+1) ? "" : argv[opi+0]; - dom = (argc < opi+2) ? "" : argv[opi+1]; // Missing domain argument is the same as empty string i.e. use system default(s) - typ = gettype(buffer, typ); - if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string - printf("Browsing for %s%s%s\n", typ, dom[0] ? "." : "", dom); - err = DNSServiceBrowse(&client, flags, opinterface, typ, dom, browse_reply, NULL); - break; - - case 'Z': typ = (argc < opi+1) ? "" : argv[opi+0]; - dom = (argc < opi+2) ? "" : argv[opi+1]; // Missing domain argument is the same as empty string i.e. use system default(s) - typ = gettype(buffer, typ); - if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string - printf("Browsing for %s%s%s\n", typ, dom[0] ? "." : "", dom); - err = DNSServiceCreateConnection(&client); - sc1 = client; - err = DNSServiceBrowse(&sc1, kDNSServiceFlagsShareConnection, opinterface, typ, dom, zonedata_browse, NULL); - break; - - case 'l': - case 'L': { - DNSServiceFlags rflags = 0; - if (argc < opi+2) goto Fail; - typ = (argc < opi+2) ? "" : argv[opi+1]; - dom = (argc < opi+3) ? "local" : argv[opi+2]; - typ = gettype(buffer, typ); - if (dom[0] == '.' && dom[1] == 0) dom = "local"; // We allow '.' on the command line as a synonym for "local" - printf("Lookup %s.%s.%s\n", argv[opi+0], typ, dom); - if (operation == 'l') rflags |= kDNSServiceFlagsWakeOnResolve; - err = DNSServiceResolve(&client, rflags, opinterface, argv[opi+0], typ, dom, resolve_reply, NULL); - break; - } - - case 'R': if (argc < opi+4) goto Fail; - typ = (argc < opi+2) ? "" : argv[opi+1]; - dom = (argc < opi+3) ? "" : argv[opi+2]; - typ = gettype(buffer, typ); - if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string - err = RegisterService(&client, argv[opi+0], typ, dom, NULL, argv[opi+3], argc-(opi+4), argv+(opi+4), flags); - break; - - case 'P': if (argc < opi+6) goto Fail; - err = DNSServiceCreateConnection(&client_pa); - if (err) { fprintf(stderr, "DNSServiceCreateConnection returned %d\n", err); return(err); } - err = RegisterProxyAddressRecord(client_pa, argv[opi+4], argv[opi+5], flags); - if (err) break; - err = RegisterService(&client, argv[opi+0], gettype(buffer, argv[opi+1]), argv[opi+2], argv[opi+4], argv[opi+3], argc-(opi+6), argv+(opi+6), flags); - break; - - case 't': - case 'q': - case 'Q': - case 'C': { - uint16_t rrtype, rrclass; - flags |= kDNSServiceFlagsReturnIntermediates; - if (operation == 'q') flags |= kDNSServiceFlagsSuppressUnusable; - if (operation == 't') flags |= (kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsTimeout); - if (argc < opi+1) goto Fail; - rrtype = (argc <= opi+1) ? kDNSServiceType_A : GetRRType(argv[opi+1]); - rrclass = (argc <= opi+2) ? kDNSServiceClass_IN : atoi(argv[opi+2]); - if (rrtype == kDNSServiceType_TXT || rrtype == kDNSServiceType_PTR) flags |= kDNSServiceFlagsLongLivedQuery; - err = DNSServiceQueryRecord(&client, flags, opinterface, argv[opi+0], rrtype, rrclass, qr_reply, NULL); - break; - } - - case 'A': - case 'U': - case 'N': { - Opaque16 registerPort = { { 0x12, 0x34 } }; - static const char TXT[] = "\xC" "First String" "\xD" "Second String" "\xC" "Third String"; - printf("Registering Service Test._testupdate._tcp.local.\n"); - err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testupdate._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT)-1, TXT, reg_reply, NULL); - break; - } - - case 'T': { - Opaque16 registerPort = { { 0x23, 0x45 } }; - char TXT[1024]; - unsigned int i; - for (i=0; i> 5); - printf("Registering Service Test._testlargetxt._tcp.local.\n"); - err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testlargetxt._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT), TXT, reg_reply, NULL); - break; - } - - case 'M': { - pid_t pid = getpid(); - Opaque16 registerPort = { { pid >> 8, pid & 0xFF } }; - static const char TXT1[] = "\xC" "First String" "\xD" "Second String" "\xC" "Third String"; - static const char TXT2[] = "\xD" "Fourth String" "\xC" "Fifth String" "\xC" "Sixth String"; - printf("Registering Service Test._testdualtxt._tcp.local.\n"); - err = DNSServiceRegister(&client, flags, opinterface, "Test", "_testdualtxt._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT1)-1, TXT1, reg_reply, NULL); - if (!err) err = DNSServiceAddRecord(client, &record, flags, kDNSServiceType_TXT, sizeof(TXT2)-1, TXT2, 0); - break; - } - - case 'I': { - pid_t pid = getpid(); - Opaque16 registerPort = { { pid >> 8, pid & 0xFF } }; - static const char TXT[] = "\x09" "Test Data"; - printf("Registering Service Test._testtxt._tcp.local.\n"); - err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testtxt._tcp.", "", NULL, registerPort.NotAnInteger, 0, NULL, reg_reply, NULL); - if (!err) err = DNSServiceUpdateRecord(client, NULL, 0, sizeof(TXT)-1, TXT, 0); - break; - } + // Test code for TXTRecord functions + //TXTRecordRef txtRecord; + //TXTRecordCreate(&txtRecord, 0, NULL); + //TXTRecordSetValue(&txtRecord, "aaa", 1, "b"); + //printf("%d\n", TXTRecordContainsKey(TXTRecordGetLength(&txtRecord), TXTRecordGetBytesPtr(&txtRecord), "Aaa")); + + if (argc > 1 && !strcmp(argv[1], "-lo")) + { + argc--; + argv++; + opinterface = kDNSServiceInterfaceIndexLocalOnly; + printf("Using LocalOnly\n"); + } + + if (argc > 1 && (!strcmp(argv[1], "-p2p") || !strcmp(argv[1], "-P2P"))) + { + argc--; + argv++; + opinterface = kDNSServiceInterfaceIndexP2P; + } + + if (argc > 1 && !strcasecmp(argv[1], "-includep2p")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsIncludeP2P; + printf("Setting kDNSServiceFlagsIncludeP2P\n"); + } + + if (argc > 1 && !strcasecmp(argv[1], "-includeAWDL")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsIncludeAWDL; + printf("Setting kDNSServiceFlagsIncludeAWDL\n"); + } + + if (argc > 1 && !strcasecmp(argv[1], "-tc")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsBackgroundTrafficClass; + printf("Setting kDNSServiceFlagsBackgroundTrafficClass\n"); + } + + if (argc > 2 && !strcmp(argv[1], "-i")) + { + opinterface = if_nametoindex(argv[2]); + if (!opinterface) opinterface = atoi(argv[2]); + if (!opinterface) { fprintf(stderr, "Unknown interface %s\n", argv[2]); goto Fail; } + argc -= 2; + argv += 2; + } + + if (argc < 2) goto Fail; // Minimum command line is the command name and one argument + operation = getfirstoption(argc, argv, "EFBZLlRPQqtCAUNTMISVHh" + #if HAS_NAT_PMP_API + "X" + #endif + #if HAS_ADDRINFO_API + "G" + #endif + , &opi); + if (operation == -1) goto Fail; + + if (opinterface) printf("Using interface %d\n", opinterface); + + switch (operation) + { + case 'E': printf("Looking for recommended registration domains:\n"); + err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsRegistrationDomains, opinterface, enum_reply, NULL); + break; + + case 'F': printf("Looking for recommended browsing domains:\n"); + err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsBrowseDomains, opinterface, enum_reply, NULL); + //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "nicta.com.au.", NULL); + //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "bonjour.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': typ = (argc < opi+1) ? "" : argv[opi+0]; + dom = (argc < opi+2) ? "" : argv[opi+1]; // Missing domain argument is the same as empty string i.e. use system default(s) + typ = gettype(buffer, typ); + if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string + printf("Browsing for %s%s%s\n", typ, dom[0] ? "." : "", dom); + err = DNSServiceBrowse(&client, flags, opinterface, typ, dom, browse_reply, NULL); + break; + + case 'Z': typ = (argc < opi+1) ? "" : argv[opi+0]; + dom = (argc < opi+2) ? "" : argv[opi+1]; // Missing domain argument is the same as empty string i.e. use system default(s) + typ = gettype(buffer, typ); + if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string + printf("Browsing for %s%s%s\n", typ, dom[0] ? "." : "", dom); + err = DNSServiceCreateConnection(&client); + sc1 = client; + err = DNSServiceBrowse(&sc1, kDNSServiceFlagsShareConnection, opinterface, typ, dom, zonedata_browse, NULL); + break; + + case 'l': + case 'L': { + if (argc < opi+2) goto Fail; + typ = (argc < opi+2) ? "" : argv[opi+1]; + dom = (argc < opi+3) ? "local" : argv[opi+2]; + typ = gettype(buffer, typ); + if (dom[0] == '.' && dom[1] == 0) dom = "local"; // We allow '.' on the command line as a synonym for "local" + printf("Lookup %s.%s.%s\n", argv[opi+0], typ, dom); + if (operation == 'l') flags |= kDNSServiceFlagsWakeOnResolve; + err = DNSServiceResolve(&client, flags, opinterface, argv[opi+0], typ, dom, resolve_reply, NULL); + break; + } + + case 'R': if (argc < opi+4) goto Fail; + typ = (argc < opi+2) ? "" : argv[opi+1]; + dom = (argc < opi+3) ? "" : argv[opi+2]; + typ = gettype(buffer, typ); + if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string + err = RegisterService(&client, argv[opi+0], typ, dom, NULL, argv[opi+3], argc-(opi+4), argv+(opi+4), flags); + break; + + case 'P': if (argc < opi+6) goto Fail; + err = DNSServiceCreateConnection(&client_pa); + if (err) { fprintf(stderr, "DNSServiceCreateConnection returned %d\n", err); return(err); } + err = RegisterProxyAddressRecord(client_pa, argv[opi+4], argv[opi+5], flags); + if (err) break; + err = RegisterService(&client, argv[opi+0], gettype(buffer, argv[opi+1]), argv[opi+2], argv[opi+4], argv[opi+3], argc-(opi+6), argv+(opi+6), flags); + break; + + case 't': + case 'q': + case 'Q': + case 'C': { + uint16_t rrtype, rrclass; + flags |= kDNSServiceFlagsReturnIntermediates; + if (operation == 'q') flags |= kDNSServiceFlagsSuppressUnusable; + if (operation == 't') flags |= (kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsTimeout); + if (argc < opi+1) goto Fail; + rrtype = (argc <= opi+1) ? kDNSServiceType_A : GetRRType(argv[opi+1]); + rrclass = (argc <= opi+2) ? kDNSServiceClass_IN : atoi(argv[opi+2]); + if (rrtype == kDNSServiceType_TXT || rrtype == kDNSServiceType_PTR) flags |= kDNSServiceFlagsLongLivedQuery; + err = DNSServiceQueryRecord(&client, flags, opinterface, argv[opi+0], rrtype, rrclass, qr_reply, NULL); + break; + } + + case 'A': + case 'U': + case 'N': { + Opaque16 registerPort = { { 0x12, 0x34 } }; + static const char TXT[] = "\xC" "First String" "\xD" "Second String" "\xC" "Third String"; + printf("Registering Service Test._testupdate._tcp.local.\n"); + err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testupdate._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT)-1, TXT, reg_reply, NULL); + break; + } + + case 'T': { + Opaque16 registerPort = { { 0x23, 0x45 } }; + char TXT[1024]; + unsigned int i; + for (i=0; i> 5); + printf("Registering Service Test._testlargetxt._tcp.local.\n"); + err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testlargetxt._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT), TXT, reg_reply, NULL); + break; + } + + case 'M': { + pid_t pid = getpid(); + Opaque16 registerPort = { { pid >> 8, pid & 0xFF } }; + static const char TXT1[] = "\xC" "First String" "\xD" "Second String" "\xC" "Third String"; + static const char TXT2[] = "\xD" "Fourth String" "\xC" "Fifth String" "\xC" "Sixth String"; + printf("Registering Service Test._testdualtxt._tcp.local.\n"); + err = DNSServiceRegister(&client, flags, opinterface, "Test", "_testdualtxt._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT1)-1, TXT1, reg_reply, NULL); + if (!err) err = DNSServiceAddRecord(client, &record, flags, kDNSServiceType_TXT, sizeof(TXT2)-1, TXT2, 0); + break; + } + + case 'I': { + pid_t pid = getpid(); + Opaque16 registerPort = { { pid >> 8, pid & 0xFF } }; + static const char TXT[] = "\x09" "Test Data"; + printf("Registering Service Test._testtxt._tcp.local.\n"); + err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testtxt._tcp.", "", NULL, registerPort.NotAnInteger, 0, NULL, reg_reply, NULL); + if (!err) err = DNSServiceUpdateRecord(client, NULL, 0, sizeof(TXT)-1, TXT, 0); + break; + } #if HAS_NAT_PMP_API - case 'X': { - if (argc == opi) // If no arguments, just fetch IP address - err = DNSServiceNATPortMappingCreate(&client, 0, 0, 0, 0, 0, 0, port_mapping_create_reply, NULL); - else if (argc >= opi+2 && atoi(argv[opi+0]) == 0) - { - DNSServiceProtocol prot = GetProtocol(argv[opi+0]); // Must specify TCP or UDP - uint16_t IntPortAsNumber = atoi(argv[opi+1]); // Must specify internal port - uint16_t ExtPortAsNumber = (argc < opi+3) ? 0 : atoi(argv[opi+2]); // Optional desired external port - uint32_t ttl = (argc < opi+4) ? 0 : atoi(argv[opi+3]); // Optional desired lease lifetime - Opaque16 intp = { { IntPortAsNumber >> 8, IntPortAsNumber & 0xFF } }; - Opaque16 extp = { { ExtPortAsNumber >> 8, ExtPortAsNumber & 0xFF } }; - err = DNSServiceNATPortMappingCreate(&client, 0, 0, prot, intp.NotAnInteger, extp.NotAnInteger, ttl, port_mapping_create_reply, NULL); - } - else goto Fail; - break; - } + case 'X': { + if (argc == opi) // If no arguments, just fetch IP address + err = DNSServiceNATPortMappingCreate(&client, 0, 0, 0, 0, 0, 0, port_mapping_create_reply, NULL); + else if (argc >= opi+2 && atoi(argv[opi+0]) == 0) + { + DNSServiceProtocol prot = GetProtocol(argv[opi+0]); // Must specify TCP or UDP + uint16_t IntPortAsNumber = atoi(argv[opi+1]); // Must specify internal port + uint16_t ExtPortAsNumber = (argc < opi+3) ? 0 : atoi(argv[opi+2]); // Optional desired external port + uint32_t ttl = (argc < opi+4) ? 0 : atoi(argv[opi+3]); // Optional desired lease lifetime + Opaque16 intp = { { IntPortAsNumber >> 8, IntPortAsNumber & 0xFF } }; + Opaque16 extp = { { ExtPortAsNumber >> 8, ExtPortAsNumber & 0xFF } }; + err = DNSServiceNATPortMappingCreate(&client, 0, 0, prot, intp.NotAnInteger, extp.NotAnInteger, ttl, port_mapping_create_reply, NULL); + } + else goto Fail; + break; + } #endif #if HAS_ADDRINFO_API - case 'G': { - if (argc != opi+2) goto Fail; - else err = DNSServiceGetAddrInfo(&client, kDNSServiceFlagsReturnIntermediates, opinterface, GetProtocol(argv[opi+0]), argv[opi+1], addrinfo_reply, NULL); - break; - } + case 'G': { + flags |= kDNSServiceFlagsReturnIntermediates; + if (argc != opi+2) goto Fail; + else err = DNSServiceGetAddrInfo(&client, flags, opinterface, GetProtocol(argv[opi+0]), argv[opi+1], addrinfo_reply, NULL); + break; + } #endif - case 'S': { - Opaque16 registerPort = { { 0x23, 0x45 } }; // 9029 decimal - unsigned char txtrec[16] = "\xF" "/path=test.html"; - DNSRecordRef rec; - unsigned char nulrec[4] = "1234"; + case 'S': { + Opaque16 registerPort = { { 0x23, 0x45 } }; // 9029 decimal + unsigned char txtrec[16] = "\xF" "/path=test.html"; + DNSRecordRef rec; + unsigned char nulrec[4] = "1234"; + + err = DNSServiceCreateConnection(&client); + if (err) { fprintf(stderr, "DNSServiceCreateConnection failed %ld\n", (long int)err); return (-1); } - err = DNSServiceCreateConnection(&client); - if (err) { fprintf(stderr, "DNSServiceCreateConnection failed %ld\n", (long int)err); return (-1); } + sc1 = client; + err = DNSServiceBrowse(&sc1, kDNSServiceFlagsShareConnection, opinterface, "_http._tcp", "", browse_reply, NULL); + if (err) { fprintf(stderr, "DNSServiceBrowse _http._tcp failed %ld\n", (long int)err); return (-1); } - sc1 = client; - err = DNSServiceBrowse(&sc1, kDNSServiceFlagsShareConnection, opinterface, "_http._tcp", "", browse_reply, NULL); - if (err) { fprintf(stderr, "DNSServiceBrowse _http._tcp failed %ld\n", (long int)err); return (-1); } + sc2 = client; + err = DNSServiceBrowse(&sc2, kDNSServiceFlagsShareConnection, opinterface, "_ftp._tcp", "", browse_reply, NULL); + if (err) { fprintf(stderr, "DNSServiceBrowse _ftp._tcp failed %ld\n", (long int)err); return (-1); } - sc2 = client; - err = DNSServiceBrowse(&sc2, kDNSServiceFlagsShareConnection, opinterface, "_ftp._tcp", "", browse_reply, NULL); - if (err) { fprintf(stderr, "DNSServiceBrowse _ftp._tcp failed %ld\n", (long int)err); return (-1); } + sc3 = client; + err = DNSServiceRegister(&sc3, kDNSServiceFlagsShareConnection, opinterface, "kDNSServiceFlagsShareConnection", + "_http._tcp", "local", NULL, registerPort.NotAnInteger, 0, NULL, reg_reply, NULL); + if (err) { fprintf(stderr, "SharedConnection DNSServiceRegister failed %ld\n", (long int)err); return (-1); } - sc3 = client; - err = DNSServiceRegister(&sc3, kDNSServiceFlagsShareConnection, opinterface, "kDNSServiceFlagsShareConnection", - "_http._tcp", "local", NULL, registerPort.NotAnInteger, 0, NULL, reg_reply, NULL); - if (err) { fprintf(stderr, "SharedConnection DNSServiceRegister failed %ld\n", (long int)err); return (-1); } + err = DNSServiceUpdateRecord(sc3, NULL, 0, sizeof(txtrec), txtrec, 0); + if (err) { fprintf(stderr, "SharedConnection DNSServiceUpdateRecord failed %ld\n", (long int)err); return (-1); } - err = DNSServiceUpdateRecord(sc3, NULL, 0, sizeof(txtrec), txtrec, 0); - if (err) { fprintf(stderr, "SharedConnection DNSServiceUpdateRecord failed %ld\n", (long int)err); return (-1); } + err = DNSServiceAddRecord(sc3, &rec, 0, kDNSServiceType_NULL, sizeof(nulrec), nulrec, 0); + if (err) { fprintf(stderr, "SharedConnection DNSServiceAddRecord failed %ld\n", (long int)err); return (-1); } - err = DNSServiceAddRecord(sc3, &rec, 0, kDNSServiceType_NULL, sizeof(nulrec), nulrec, 0); - if (err) { fprintf(stderr, "SharedConnection DNSServiceAddRecord failed %ld\n", (long int)err); return (-1); } + err = DNSServiceRemoveRecord(sc3, rec, 0); + if (err) { fprintf(stderr, "SharedConnection DNSServiceRemoveRecord failed %ld\n", (long int)err); return (-1); } - err = DNSServiceRemoveRecord(sc3, rec, 0); - if (err) { fprintf(stderr, "SharedConnection DNSServiceRemoveRecord failed %ld\n", (long int)err); return (-1); } + break; + } - break; - } + case 'V': { + uint32_t v; + uint32_t size = sizeof(v); + err = DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &v, &size); + if (err) fprintf(stderr, "DNSServiceGetProperty failed %ld\n", (long int)err); + else printf("Currently running daemon (system service) is version %d.%d\n", v / 10000, v / 100 % 100); + exit(0); + } - case 'V': { - uint32_t v; - uint32_t size = sizeof(v); - err = DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &v, &size); - if (err) fprintf(stderr, "DNSServiceGetProperty failed %ld\n", (long int)err); - else printf("Currently running daemon (system service) is version %d.%d\n", v / 10000, v / 100 % 100); - exit(0); - } + case 'H': goto Fail; - default: goto Fail; - } + default: goto Fail; + } - if (!client || err != kDNSServiceErr_NoError) { fprintf(stderr, "DNSService call failed %ld\n", (long int)err); return (-1); } - HandleEvents(); + if (!client || err != kDNSServiceErr_NoError) { fprintf(stderr, "DNSService call failed %ld\n", (long int)err); return (-1); } + printtimestamp(); + printf("...STARTING...\n"); + HandleEvents(); - // Be sure to deallocate the DNSServiceRef when you're finished - if (client ) DNSServiceRefDeallocate(client ); - if (client_pa) DNSServiceRefDeallocate(client_pa); - return 0; + // Be sure to deallocate the DNSServiceRef when you're finished + if (client ) DNSServiceRefDeallocate(client ); + if (client_pa) DNSServiceRefDeallocate(client_pa); + return 0; Fail: - fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", a0); - fprintf(stderr, "%s -F (Enumerate recommended browsing domains)\n", a0); - fprintf(stderr, "%s -B (Browse for services instances)\n", a0); - fprintf(stderr, "%s -L (Look up a service instance)\n", a0); - fprintf(stderr, "%s -R [...] (Register a service)\n", a0); - fprintf(stderr, "%s -P [...] (Proxy)\n", a0); - fprintf(stderr, "%s -Z (Output results in Zone File format)\n", a0); - fprintf(stderr, "%s -Q (Generic query for any record type)\n", a0); - fprintf(stderr, "%s -C (Query; reconfirming each result)\n", a0); -#if HAS_NAT_PMP_API - fprintf(stderr, "%s -X udp/tcp/udptcp (NAT Port Mapping)\n", a0); -#endif -#if HAS_ADDRINFO_API - fprintf(stderr, "%s -G v4/v6/v4v6 (Get address information for hostname)\n", a0); -#endif - fprintf(stderr, "%s -V (Get version of currently running daemon / system service)\n", a0); - - fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", a0); - fprintf(stderr, "%s -U (Test updating a TXT record)\n", a0); - fprintf(stderr, "%s -N (Test adding a large NULL record)\n", a0); - fprintf(stderr, "%s -T (Test creating a large TXT record)\n", a0); - fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", a0); - fprintf(stderr, "%s -I (Test registering and then immediately updating TXT record)\n", a0); - fprintf(stderr, "%s -S (Test multiple operations on a shared socket)\n", a0); - return 0; - } + if (operation == 'H') print_usage(a0,1); + else print_usage(a0,0); + return 0; + +} // 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) // NOT static -- otherwise the compiler may optimize it out @@ -1345,5 +1379,5 @@ const char VersionString_SCCS[] = "@(#) dns-sd " STRINGIFY(mDNSResponderVersion) #if _BUILDING_XCODE_PROJECT_ // If the process crashes, then this string will be magically included in the automatically-generated crash log const char *__crashreporter_info__ = VersionString_SCCS + 5; -asm(".desc ___crashreporter_info__, 0x10"); +asm (".desc ___crashreporter_info__, 0x10"); #endif diff --git a/Clients/mDNSNetMonitor.VisualStudio/resource.h b/Clients/mDNSNetMonitor.VisualStudio/resource.h index a37efd2..31ad9f0 100644 --- a/Clients/mDNSNetMonitor.VisualStudio/resource.h +++ b/Clients/mDNSNetMonitor.VisualStudio/resource.h @@ -3,7 +3,7 @@ // Used by mDNSNetMonitor.rc // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 101 diff --git a/Makefile b/Makefile index 830157b..2cc5f1b 100644 --- a/Makefile +++ b/Makefile @@ -14,20 +14,25 @@ # clean: # -include /Developer/Makefiles/pb_makefiles/platform.make +include $(MAKEFILEPATH)/pb_makefiles/platform.make -MVERS = "mDNSResponder-333.10" +MVERS = "mDNSResponder-379.27" DDNSWRITECONFIG = "$(DSTROOT)/Library/Application Support/Bonjour/ddnswriteconfig" +VER = +ifneq ($(strip $(GCC_VERSION)),) + VER = -- GCC_VERSION=$(GCC_VERSION) +endif +echo "VER = $(VER)" installSome: - cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild install OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT) -target Build\ Some -- GCC_VERSION=$(GCC_VERSION) + cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild install OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT) -target Build\ Some $(VER) SystemLibraries: - cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild install OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT) -target SystemLibraries -- GCC_VERSION=$(GCC_VERSION) + cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild install OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT) -target SystemLibraries $(VER) install: - cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild install OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT) -- GCC_VERSION=$(GCC_VERSION) + cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild install OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT) $(VER) # Make sure ddnswriteconfig is owned by root:wheel, then make it setuid root executable if test -e $(DDNSWRITECONFIG) ; then chown 0:80 $(DDNSWRITECONFIG) ; chmod 4555 $(DDNSWRITECONFIG) ; fi @@ -35,7 +40,7 @@ installsrc: ditto . "$(SRCROOT)" installhdrs:: - cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild installhdrs OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT) -target SystemLibraries -- GCC_VERSION=$(GCC_VERSION) + cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild installhdrs OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT) -target SystemLibraries $(VER) clean:: echo clean diff --git a/mDNSCore/CryptoAlg.c b/mDNSCore/CryptoAlg.c new file mode 100644 index 0000000..0fee771 --- /dev/null +++ b/mDNSCore/CryptoAlg.c @@ -0,0 +1,256 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// *************************************************************************** +// CryptoAlg.c: +// Interface to DNSSEC cryptographic algorithms. The crypto support itself is +// provided by the platform and the functions in this file just provide an +// interface to access them in a more generic way. +// *************************************************************************** + +#include "mDNSEmbeddedAPI.h" +#include "CryptoAlg.h" + +AlgFuncs *DigestAlgFuncs[DIGEST_TYPE_MAX]; +AlgFuncs *CryptoAlgFuncs[CRYPTO_ALG_MAX]; +AlgFuncs *EncAlgFuncs[ENC_ALG_MAX]; + +mDNSexport mStatus DigestAlgInit(mDNSu8 digestType, AlgFuncs *func) +{ + if (digestType >= DIGEST_TYPE_MAX) + { + LogMsg("DigestAlgInit: digestType %d exceeds bounds", digestType); + return mStatus_BadParamErr; + } + // As digestTypes may not be consecutive, check for specific digest types + // that we support + if (digestType != SHA1_DIGEST_TYPE && + digestType != SHA256_DIGEST_TYPE) + { + LogMsg("DigestAlgInit: digestType %d not supported", digestType); + return mStatus_BadParamErr; + } + DigestAlgFuncs[digestType] = func; + return mStatus_NoError; +} + +mDNSexport mStatus CryptoAlgInit(mDNSu8 alg, AlgFuncs *func) +{ + if (alg >= CRYPTO_ALG_MAX) + { + LogMsg("CryptoAlgInit: alg %d exceeds bounds", alg); + return mStatus_BadParamErr; + } + // As algs may not be consecutive, check for specific algorithms + // that we support + if (alg != CRYPTO_RSA_SHA1 && alg != CRYPTO_RSA_SHA256 && alg != CRYPTO_RSA_SHA512 && + alg != CRYPTO_DSA_NSEC3_SHA1 && alg != CRYPTO_RSA_NSEC3_SHA1) + { + LogMsg("CryptoAlgInit: alg %d not supported", alg); + return mStatus_BadParamErr; + } + + CryptoAlgFuncs[alg] = func; + return mStatus_NoError; +} + +mDNSexport mStatus EncAlgInit(mDNSu8 alg, AlgFuncs *func) +{ + if (alg >= ENC_ALG_MAX) + { + LogMsg("EncAlgInit: alg %d exceeds bounds", alg); + return mStatus_BadParamErr; + } + + // As algs may not be consecutive, check for specific algorithms + // that we support + if (alg != ENC_BASE32 && alg != ENC_BASE64) + { + LogMsg("EncAlgInit: alg %d not supported", alg); + return mStatus_BadParamErr; + } + + EncAlgFuncs[alg] = func; + return mStatus_NoError; +} + +mDNSexport AlgContext *AlgCreate(AlgType type, mDNSu8 alg) +{ + AlgFuncs *func = mDNSNULL; + AlgContext *ctx; + + if (type == CRYPTO_ALG) + { + if (alg >= CRYPTO_ALG_MAX) return mDNSNULL; + func = CryptoAlgFuncs[alg]; + } + else if (type == DIGEST_ALG) + { + if (alg >= DIGEST_TYPE_MAX) return mDNSNULL; + func = DigestAlgFuncs[alg]; + } + else if (type == ENC_ALG) + { + if (alg >= ENC_ALG_MAX) return mDNSNULL; + func = EncAlgFuncs[alg]; + } + + if (!func) + { + // If there is no support from the platform, this case can happen. + LogInfo("AlgCreate: func is NULL"); + return mDNSNULL; + } + + if (func->Create) + { + mStatus err; + ctx = mDNSPlatformMemAllocate(sizeof(AlgContext)); + if (!ctx) return mDNSNULL; + // Create expects ctx->alg to be initialized + ctx->alg = alg; + err = func->Create(ctx); + if (err == mStatus_NoError) + { + ctx->type = type; + return ctx; + } + mDNSPlatformMemFree(ctx); + } + return mDNSNULL; +} + +mDNSexport mStatus AlgDestroy(AlgContext *ctx) +{ + AlgFuncs *func = mDNSNULL; + + if (ctx->type == CRYPTO_ALG) + func = CryptoAlgFuncs[ctx->alg]; + else if (ctx->type == DIGEST_ALG) + func = DigestAlgFuncs[ctx->alg]; + else if (ctx->type == ENC_ALG) + func = EncAlgFuncs[ctx->alg]; + + if (!func) + { + LogMsg("AlgDestroy: ERROR!! func is NULL"); + mDNSPlatformMemFree(ctx); + return mStatus_BadParamErr; + } + + if (func->Destroy) + func->Destroy(ctx); + + mDNSPlatformMemFree(ctx); + return mStatus_NoError; +} + +mDNSexport mDNSu32 AlgLength(AlgContext *ctx) +{ + AlgFuncs *func = mDNSNULL; + + if (ctx->type == CRYPTO_ALG) + func = CryptoAlgFuncs[ctx->alg]; + else if (ctx->type == DIGEST_ALG) + func = DigestAlgFuncs[ctx->alg]; + else if (ctx->type == ENC_ALG) + func = EncAlgFuncs[ctx->alg]; + + // This should never happen as AlgCreate would have failed + if (!func) + { + LogMsg("AlgLength: ERROR!! func is NULL"); + return 0; + } + + if (func->Length) + return (func->Length(ctx)); + else + return 0; +} + +mDNSexport mStatus AlgAdd(AlgContext *ctx, void *data, mDNSu32 len) +{ + AlgFuncs *func = mDNSNULL; + + if (ctx->type == CRYPTO_ALG) + func = CryptoAlgFuncs[ctx->alg]; + else if (ctx->type == DIGEST_ALG) + func = DigestAlgFuncs[ctx->alg]; + else if (ctx->type == ENC_ALG) + func = EncAlgFuncs[ctx->alg]; + + // This should never happen as AlgCreate would have failed + if (!func) + { + LogMsg("AlgAdd: ERROR!! func is NULL"); + return mStatus_BadParamErr; + } + + if (func->Add) + return (func->Add(ctx, data, len)); + else + return mStatus_BadParamErr; +} + +mDNSexport mStatus AlgVerify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen) +{ + AlgFuncs *func = mDNSNULL; + + if (ctx->type == CRYPTO_ALG) + func = CryptoAlgFuncs[ctx->alg]; + else if (ctx->type == DIGEST_ALG) + func = DigestAlgFuncs[ctx->alg]; + else if (ctx->type == ENC_ALG) + func = EncAlgFuncs[ctx->alg]; + + // This should never happen as AlgCreate would have failed + if (!func) + { + LogMsg("AlgVerify: ERROR!! func is NULL"); + return mStatus_BadParamErr; + } + + if (func->Verify) + return (func->Verify(ctx, key, keylen, signature, siglen)); + else + return mStatus_BadParamErr; +} + +mDNSexport mDNSu8* AlgEncode(AlgContext *ctx) +{ + AlgFuncs *func = mDNSNULL; + + if (ctx->type == CRYPTO_ALG) + func = CryptoAlgFuncs[ctx->alg]; + else if (ctx->type == DIGEST_ALG) + func = DigestAlgFuncs[ctx->alg]; + else if (ctx->type == ENC_ALG) + func = EncAlgFuncs[ctx->alg]; + + // This should never happen as AlgCreate would have failed + if (!func) + { + LogMsg("AlgEncode: ERROR!! func is NULL"); + return mDNSNULL; + } + + if (func->Encode) + return (func->Encode(ctx)); + else + return mDNSNULL; +} diff --git a/mDNSCore/CryptoAlg.h b/mDNSCore/CryptoAlg.h new file mode 100644 index 0000000..4ed6879 --- /dev/null +++ b/mDNSCore/CryptoAlg.h @@ -0,0 +1,58 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __CRYPTO_ALG_H +#define __CRYPTO_ALG_H + +typedef enum +{ + CRYPTO_ALG, + DIGEST_ALG, + ENC_ALG, +} AlgType; + +typedef struct +{ + void *context; + AlgType type; + mDNSu8 alg; +} AlgContext; + +typedef struct +{ + mStatus (*Create)(AlgContext *ctx); + mStatus (*Destroy)(AlgContext *ctx); + mDNSu32 (*Length)(AlgContext *ctx); + mStatus (*Add)(AlgContext *ctx, void *data, mDNSu32 len); + // Verify the ctx using the key and compare it against signature/siglen + mStatus (*Verify)(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen); + // Encode the data and return the encoded data + mDNSu8* (*Encode)(AlgContext *ctx); +} AlgFuncs; + +mDNSexport mStatus DigestAlgInit(mDNSu8 digestType, AlgFuncs *func); +mDNSexport mStatus CryptoAlgInit(mDNSu8 algType, AlgFuncs *func); +mDNSexport mStatus EncAlgInit(mDNSu8 algType, AlgFuncs *func); + + +extern AlgContext *AlgCreate(AlgType type, mDNSu8 alg); +extern mStatus AlgDestroy(AlgContext *ctx); +extern mDNSu32 AlgLength(AlgContext *ctx); +extern mStatus AlgAdd(AlgContext *ctx, void *data, mDNSu32 len); +extern mStatus AlgVerify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen); +extern mDNSu8* AlgEncode(AlgContext *ctx); + +#endif // __CRYPTO_ALG_H diff --git a/mDNSCore/DNSCommon.c b/mDNSCore/DNSCommon.c index b75c128..99429ea 100644 --- a/mDNSCore/DNSCommon.c +++ b/mDNSCore/DNSCommon.c @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,16 +18,17 @@ // Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary #define mDNS_InstantiateInlines 1 #include "DNSCommon.h" +#include "CryptoAlg.h" // Disable certain benign warnings with Microsoft compilers #if (defined(_MSC_VER)) - // Disable "conditional expression is constant" warning for debug macros. - // Otherwise, this generates warnings for the perfectly natural construct "while(1)" - // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know - #pragma warning(disable:4127) - // 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) +// Disable "conditional expression is constant" warning for debug macros. +// Otherwise, this generates warnings for the perfectly natural construct "while(1)" +// If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know + #pragma warning(disable:4127) +// Disable "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 // *************************************************************************** @@ -53,10 +54,10 @@ mDNSexport const mDNSInterfaceID mDNSInterface_P2P = (mDNSInterfaceID)-4; #define UnicastDNSPortAsNumber 53 #define SSDPPortAsNumber 1900 #define IPSECPortAsNumber 4500 -#define NSIPCPortAsNumber 5030 // Port used for dnsextd to talk to local nameserver bound to loopback +#define NSIPCPortAsNumber 5030 // Port used for dnsextd to talk to local nameserver bound to loopback #define NATPMPAnnouncementPortAsNumber 5350 #define NATPMPPortAsNumber 5351 -#define DNSEXTPortAsNumber 5352 // Port used for end-to-end DNS operations like LLQ, Updates with Leases, etc. +#define DNSEXTPortAsNumber 5352 // Port used for end-to-end DNS operations like LLQ, Updates with Leases, etc. #define MulticastDNSPortAsNumber 5353 #define LoopbackIPCPortAsNumber 5354 //#define MulticastDNSPortAsNumber 5355 // LLMNR @@ -75,31 +76,32 @@ mDNSexport const mDNSIPPort MulticastDNSPort = { { MulticastDNSPortAsNumbe mDNSexport const mDNSIPPort LoopbackIPCPort = { { LoopbackIPCPortAsNumber >> 8, LoopbackIPCPortAsNumber & 0xFF } }; mDNSexport const mDNSIPPort PrivateDNSPort = { { PrivateDNSPortAsNumber >> 8, PrivateDNSPortAsNumber & 0xFF } }; -mDNSexport const OwnerOptData zeroOwner = { 0, 0, { { 0 } }, { { 0 } }, { { 0 } } }; - -mDNSexport const mDNSIPPort zeroIPPort = { { 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 mDNSEthAddr onesEthAddr = { { 255, 255, 255, 255, 255, 255 } }; -mDNSexport const mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} }; - -mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } }; -mDNSexport const mDNSv4Addr AllHosts_v4 = { { 224, 0, 0, 1 } }; // For NAT-PMP Annoucements -mDNSexport const mDNSv6Addr AllHosts_v6 = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01 } }; -mDNSexport const mDNSv6Addr NDP_prefix = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01, 0xFF,0x00,0x00,0xFB } }; // FF02:0:0:0:0:1:FF00::/104 +mDNSexport const OwnerOptData zeroOwner = { 0, 0, { { 0 } }, { { 0 } }, { { 0 } } }; + +mDNSexport const mDNSIPPort zeroIPPort = { { 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 mDNSEthAddr onesEthAddr = { { 255, 255, 255, 255, 255, 255 } }; +mDNSexport const mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} }; + +mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } }; +mDNSexport const mDNSv4Addr AllHosts_v4 = { { 224, 0, 0, 1 } }; // For NAT-PMP Annoucements +mDNSexport const mDNSv6Addr AllHosts_v6 = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01 } }; +mDNSexport const mDNSv6Addr NDP_prefix = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01, 0xFF,0x00,0x00,0xFB } }; // FF02:0:0:0:0:1:FF00::/104 mDNSexport const mDNSEthAddr AllHosts_v6_Eth = { { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 } }; -mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 251 } } } }; +mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 251 } } } }; //mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 252 } } } }; // LLMNR -mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } }; +mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } }; //mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x01,0x00,0x03 } } } }; // LLMNR mDNSexport const mDNSOpaque16 zeroID = { { 0, 0 } }; mDNSexport const mDNSOpaque16 onesID = { { 255, 255 } }; mDNSexport const mDNSOpaque16 QueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery, 0 } }; mDNSexport const mDNSOpaque16 uQueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery | kDNSFlag0_RD, 0 } }; +mDNSexport const mDNSOpaque16 DNSSecQFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery | kDNSFlag0_RD, kDNSFlag1_CD } }; mDNSexport const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } }; mDNSexport const mDNSOpaque16 UpdateReqFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_Update, 0 } }; mDNSexport const mDNSOpaque16 UpdateRespFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, 0 } }; @@ -114,215 +116,391 @@ mDNSexport const mDNSOpaque64 zeroOpaque64 = { { 0 } }; // return true for RFC1918 private addresses mDNSexport mDNSBool mDNSv4AddrIsRFC1918(mDNSv4Addr *addr) - { - return ((addr->b[0] == 10) || // 10/8 prefix - (addr->b[0] == 172 && (addr->b[1] & 0xF0) == 16) || // 172.16/12 - (addr->b[0] == 192 && addr->b[1] == 168)); // 192.168/16 - } +{ + return ((addr->b[0] == 10) || // 10/8 prefix + (addr->b[0] == 172 && (addr->b[1] & 0xF0) == 16) || // 172.16/12 + (addr->b[0] == 192 && addr->b[1] == 168)); // 192.168/16 +} mDNSexport NetworkInterfaceInfo *GetFirstActiveInterface(NetworkInterfaceInfo *intf) - { - while (intf && !intf->InterfaceActive) intf = intf->next; - return(intf); - } +{ + while (intf && !intf->InterfaceActive) intf = intf->next; + return(intf); +} mDNSexport mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf) - { - const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); - if (next) return(next->InterfaceID); else return(mDNSNULL); - } +{ + const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); + if (next) return(next->InterfaceID);else return(mDNSNULL); +} mDNSexport mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id) - { - mDNSu32 slot, used = 0; - CacheGroup *cg; - const CacheRecord *rr; - FORALL_CACHERECORDS(slot, cg, rr) - if (rr->resrec.InterfaceID == id) used++; - return(used); - } +{ + mDNSu32 slot, used = 0; + CacheGroup *cg; + const CacheRecord *rr; + FORALL_CACHERECORDS(slot, cg, rr) + { + if (rr->resrec.InterfaceID == id) + used++; + } + return(used); +} mDNSexport char *DNSTypeName(mDNSu16 rrtype) - { - switch (rrtype) - { - case kDNSType_A: return("Addr"); - case kDNSType_NS: return("NS"); - case kDNSType_CNAME:return("CNAME"); - case kDNSType_SOA: return("SOA"); - case kDNSType_NULL: return("NULL"); - case kDNSType_PTR: return("PTR"); - case kDNSType_HINFO:return("HINFO"); - case kDNSType_TXT: return("TXT"); - case kDNSType_AAAA: return("AAAA"); - case kDNSType_SRV: return("SRV"); - case kDNSType_OPT: return("OPT"); - case kDNSType_NSEC: return("NSEC"); - case kDNSType_TSIG: return("TSIG"); - case kDNSQType_ANY: return("ANY"); - default: { - static char buffer[16]; - mDNS_snprintf(buffer, sizeof(buffer), "(%d)", rrtype); - return(buffer); - } - } - } +{ + switch (rrtype) + { + case kDNSType_A: return("Addr"); + case kDNSType_NS: return("NS"); + case kDNSType_CNAME: return("CNAME"); + case kDNSType_SOA: return("SOA"); + case kDNSType_NULL: return("NULL"); + case kDNSType_PTR: return("PTR"); + case kDNSType_HINFO: return("HINFO"); + case kDNSType_TXT: return("TXT"); + case kDNSType_AAAA: return("AAAA"); + case kDNSType_SRV: return("SRV"); + case kDNSType_OPT: return("OPT"); + case kDNSType_NSEC: return("NSEC"); + case kDNSType_TSIG: return("TSIG"); + case kDNSType_RRSIG: return("RRSIG"); + case kDNSType_DNSKEY: return("DNSKEY"); + case kDNSType_DS: return("DS"); + case kDNSQType_ANY: return("ANY"); + default: { + static char buffer[16]; + mDNS_snprintf(buffer, sizeof(buffer), "TYPE%d", rrtype); + return(buffer); + } + } +} + +mDNSlocal char *DNSSECAlgName(mDNSu8 alg) +{ + switch (alg) + { + case CRYPTO_RSA_SHA1: return "RSA_SHA1"; + case CRYPTO_DSA_NSEC3_SHA1: return "DSA_NSEC3_SHA1"; + case CRYPTO_RSA_NSEC3_SHA1: return "RSA_NSEC3_SHA1"; + case CRYPTO_RSA_SHA256: return "RSA_SHA256"; + case CRYPTO_RSA_SHA512: return "RSA_SHA512"; + default: { + static char algbuffer[16]; + mDNS_snprintf(algbuffer, sizeof(algbuffer), "ALG%d", alg); + return(algbuffer); + } + } +} + +mDNSlocal char *DNSSECDigestName(mDNSu8 digest) +{ + switch (digest) + { + case SHA1_DIGEST_TYPE: return "SHA1"; + case SHA256_DIGEST_TYPE: return "SHA256"; + default: { + static char digbuffer[16]; + mDNS_snprintf(digbuffer, sizeof(digbuffer), "DIG%d", digest); + return(digbuffer); + } + } +} + +mDNSexport mDNSu32 swap32(mDNSu32 x) +{ + mDNSu8 *ptr = (mDNSu8 *)&x; + return (mDNSu32)((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]); +} + +mDNSexport mDNSu16 swap16(mDNSu16 x) +{ + mDNSu8 *ptr = (mDNSu8 *)&x; + return (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); +} + +// RFC 4034 Appendix B: Get the keyid of a DNS KEY. It is not transmitted +// explicitly on the wire. +// +// Note: This just helps narrow down the list of keys to look at. It is possible +// for two DNS keys to have the same ID i.e., key ID is not a unqiue tag. We ignore +// MD5 keys. +// +// 1st argument - the RDATA part of the DNSKEY RR +// 2nd argument - the RDLENGTH +// +mDNSlocal mDNSu32 keytag(mDNSu8 *key, mDNSu32 keysize) +{ + unsigned long ac; + unsigned int i; + + for (ac = 0, i = 0; i < keysize; ++i) + ac += (i & 1) ? key[i] : key[i] << 8; + ac += (ac >> 16) & 0xFFFF; + return ac & 0xFFFF; +} + +mDNSlocal int base64Encode(char *buffer, int blen, mDNSu8 *data, int len) +{ + AlgContext *ctx; + mDNSu8 *outputBuffer; + int length; + + ctx = AlgCreate(ENC_ALG, ENC_BASE64); + if (!ctx) + { + LogMsg("base64Encode: AlgCreate failed\n"); + return 0; + } + AlgAdd(ctx, data, len); + outputBuffer = AlgEncode(ctx); + length = 0; + if (outputBuffer) + length = mDNS_snprintf(buffer, blen, " %s", outputBuffer); + AlgDestroy(ctx); + return length; +} // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as // long as this routine is only used for debugging messages, it probably isn't a big problem. mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RDataBody *const rd1, char *const buffer) - { - const RDataBody2 *const rd = (RDataBody2 *)rd1; - #define RemSpc (MaxMsg-1-length) - char *ptr = buffer; - mDNSu32 length = mDNS_snprintf(buffer, MaxMsg-1, "%4d %##s %s ", rr->rdlength, rr->name->c, DNSTypeName(rr->rrtype)); - if (rr->RecordType == kDNSRecordTypePacketNegative) return(buffer); - if (!rr->rdlength) { mDNS_snprintf(buffer+length, RemSpc, "<< ZERO RDATA LENGTH >>"); return(buffer); } - - switch (rr->rrtype) - { - case kDNSType_A: mDNS_snprintf(buffer+length, RemSpc, "%.4a", &rd->ipv4); break; - - case kDNSType_NS: // Same as PTR - case kDNSType_CNAME:// Same as PTR - case kDNSType_PTR: mDNS_snprintf(buffer+length, RemSpc, "%##s", rd->name.c); break; - - case kDNSType_SOA: mDNS_snprintf(buffer+length, RemSpc, "%##s %##s %d %d %d %d %d", - rd->soa.mname.c, rd->soa.rname.c, - rd->soa.serial, rd->soa.refresh, rd->soa.retry, rd->soa.expire, rd->soa.min); - break; - - case kDNSType_HINFO:// Display this the same as TXT (show all constituent strings) - case kDNSType_TXT: { - const mDNSu8 *t = rd->txt.c; - while (t < rd->txt.c + rr->rdlength) - { - length += mDNS_snprintf(buffer+length, RemSpc, "%s%#s", t > rd->txt.c ? "¦" : "", t); - t += 1 + t[0]; - } - } break; - - case kDNSType_AAAA: mDNS_snprintf(buffer+length, RemSpc, "%.16a", &rd->ipv6); break; - case kDNSType_SRV: mDNS_snprintf(buffer+length, RemSpc, "%u %u %u %##s", - rd->srv.priority, rd->srv.weight, mDNSVal16(rd->srv.port), rd->srv.target.c); break; - - case kDNSType_OPT: { - const rdataOPT *opt; - const rdataOPT *const end = (const rdataOPT *)&rd->data[rr->rdlength]; - length += mDNS_snprintf(buffer+length, RemSpc, "Max %d", rr->rrclass); - for (opt = &rd->opt[0]; opt < end; opt++) - { - switch(opt->opt) - { - case kDNSOpt_LLQ: - length += mDNS_snprintf(buffer+length, RemSpc, " Vers %d", opt->u.llq.vers); - length += mDNS_snprintf(buffer+length, RemSpc, " Op %d", opt->u.llq.llqOp); - length += mDNS_snprintf(buffer+length, RemSpc, " Err/Port %d", opt->u.llq.err); - length += mDNS_snprintf(buffer+length, RemSpc, " ID %08X%08X", opt->u.llq.id.l[0], opt->u.llq.id.l[1]); - length += mDNS_snprintf(buffer+length, RemSpc, " Lease %d", opt->u.llq.llqlease); - break; - case kDNSOpt_Lease: - length += mDNS_snprintf(buffer+length, RemSpc, " Lease %d", opt->u.updatelease); - break; - case kDNSOpt_Owner: - length += mDNS_snprintf(buffer+length, RemSpc, " Vers %d", opt->u.owner.vers); - length += mDNS_snprintf(buffer+length, RemSpc, " Seq %3d", (mDNSu8)opt->u.owner.seq); // Display as unsigned - length += mDNS_snprintf(buffer+length, RemSpc, " MAC %.6a", opt->u.owner.HMAC.b); - if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4) - { - length += mDNS_snprintf(buffer+length, RemSpc, " I-MAC %.6a", opt->u.owner.IMAC.b); - if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4) - length += mDNS_snprintf(buffer+length, RemSpc, " Password %.6a", opt->u.owner.password.b); - } - break; - default: - length += mDNS_snprintf(buffer+length, RemSpc, " Unknown %d", opt->opt); - break; - } - } - } - break; - - case kDNSType_NSEC: { - mDNSu16 i; - for (i=0; i<255; i++) - if (rd->nsec.bitmap[i>>3] & (128 >> (i&7))) - length += mDNS_snprintf(buffer+length, RemSpc, "%s ", DNSTypeName(i)); - } - break; - - default: mDNS_snprintf(buffer+length, RemSpc, "RDLen %d: %s", rr->rdlength, rd->data); - // Really should scan buffer to check if text is valid UTF-8 and only replace with dots if not - for (ptr = buffer; *ptr; ptr++) if (*ptr < ' ') *ptr = '.'; - break; - } - return(buffer); - } +{ + const RDataBody2 *const rd = (RDataBody2 *)rd1; + #define RemSpc (MaxMsg-1-length) + char *ptr = buffer; + mDNSu32 length = mDNS_snprintf(buffer, MaxMsg-1, "%4d %##s %s ", rr->rdlength, rr->name->c, DNSTypeName(rr->rrtype)); + if (rr->RecordType == kDNSRecordTypePacketNegative) return(buffer); + if (!rr->rdlength) { mDNS_snprintf(buffer+length, RemSpc, "<< ZERO RDATA LENGTH >>"); return(buffer); } + + switch (rr->rrtype) + { + case kDNSType_A: mDNS_snprintf(buffer+length, RemSpc, "%.4a", &rd->ipv4); break; + + case kDNSType_NS: // Same as PTR + case kDNSType_CNAME: // Same as PTR + case kDNSType_PTR: mDNS_snprintf(buffer+length, RemSpc, "%##s", rd->name.c); break; + + case kDNSType_SOA: mDNS_snprintf(buffer+length, RemSpc, "%##s %##s %d %d %d %d %d", + rd->soa.mname.c, rd->soa.rname.c, + rd->soa.serial, rd->soa.refresh, rd->soa.retry, rd->soa.expire, rd->soa.min); + break; + + case kDNSType_HINFO: // Display this the same as TXT (show all constituent strings) + case kDNSType_TXT: { + const mDNSu8 *t = rd->txt.c; + while (t < rd->txt.c + rr->rdlength) + { + length += mDNS_snprintf(buffer+length, RemSpc, "%s%#s", t > rd->txt.c ? "¦" : "", t); + t += 1 + t[0]; + } + } break; + + case kDNSType_AAAA: mDNS_snprintf(buffer+length, RemSpc, "%.16a", &rd->ipv6); break; + case kDNSType_SRV: mDNS_snprintf(buffer+length, RemSpc, "%u %u %u %##s", + rd->srv.priority, rd->srv.weight, mDNSVal16(rd->srv.port), rd->srv.target.c); break; + + case kDNSType_OPT: { + const rdataOPT *opt; + const rdataOPT *const end = (const rdataOPT *)&rd->data[rr->rdlength]; + length += mDNS_snprintf(buffer+length, RemSpc, "Max %d", rr->rrclass); + for (opt = &rd->opt[0]; opt < end; opt++) + { + switch(opt->opt) + { + case kDNSOpt_LLQ: + length += mDNS_snprintf(buffer+length, RemSpc, " Vers %d", opt->u.llq.vers); + length += mDNS_snprintf(buffer+length, RemSpc, " Op %d", opt->u.llq.llqOp); + length += mDNS_snprintf(buffer+length, RemSpc, " Err/Port %d", opt->u.llq.err); + length += mDNS_snprintf(buffer+length, RemSpc, " ID %08X%08X", opt->u.llq.id.l[0], opt->u.llq.id.l[1]); + length += mDNS_snprintf(buffer+length, RemSpc, " Lease %d", opt->u.llq.llqlease); + break; + case kDNSOpt_Lease: + length += mDNS_snprintf(buffer+length, RemSpc, " Lease %d", opt->u.updatelease); + break; + case kDNSOpt_Owner: + length += mDNS_snprintf(buffer+length, RemSpc, " Vers %d", opt->u.owner.vers); + length += mDNS_snprintf(buffer+length, RemSpc, " Seq %3d", (mDNSu8)opt->u.owner.seq); // Display as unsigned + length += mDNS_snprintf(buffer+length, RemSpc, " MAC %.6a", opt->u.owner.HMAC.b); + if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4) + { + length += mDNS_snprintf(buffer+length, RemSpc, " I-MAC %.6a", opt->u.owner.IMAC.b); + if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4) + length += mDNS_snprintf(buffer+length, RemSpc, " Password %.6a", opt->u.owner.password.b); + } + break; + default: + length += mDNS_snprintf(buffer+length, RemSpc, " Unknown %d", opt->opt); + break; + } + } + } + break; + + case kDNSType_NSEC: { + domainname *next = (domainname *)rd->data; + int len, bitmaplen; + int win, wlen, type; + mDNSu8 *bmap; + len = DomainNameLength(next); + bitmaplen = rr->rdlength - len; + bmap = (mDNSu8 *)((mDNSu8 *)next + len); + + if (UNICAST_NSEC(rr)) + length += mDNS_snprintf(buffer+length, RemSpc, "%##s ", next->c); + + while (bitmaplen > 0) + { + int i; + + if (bitmaplen < 3) + { + LogMsg("GetRRDisplayString_rdb: malformed nsec, bitmaplen %d short", bitmaplen); + break; + } + + win = *bmap++; + wlen = *bmap++; + bitmaplen -= 2; + if (bitmaplen < wlen || wlen < 1 || wlen > 32) + { + LogInfo("GetRRDisplayString_rdb: malformed nsec, bitmaplen %d wlen %d", bitmaplen, wlen); + break; + } + if (win < 0 || win >= 256) + { + LogInfo("GetRRDisplayString_rdb: malformed nsec, bad window win %d", win); + break; + } + type = win * 256; + for (i = 0; i < wlen * 8; i++) + { + if (bmap[i>>3] & (128 >> (i&7))) + length += mDNS_snprintf(buffer+length, RemSpc, "%s ", DNSTypeName(type + i)); + } + bmap += wlen; + bitmaplen -= wlen; + } + } + break; + case kDNSType_RRSIG: { + rdataRRSig *rrsig = (rdataRRSig *)rd->data; + mDNSu8 expTimeBuf[64]; + mDNSu8 inceptTimeBuf[64]; + unsigned long inceptClock; + unsigned long expClock; + int len; + + expClock = (unsigned long)swap32(rrsig->sigExpireTime); + mDNSPlatformFormatTime(expClock, expTimeBuf, sizeof(expTimeBuf)); + + inceptClock = (unsigned long)swap32(rrsig->sigInceptTime); + mDNSPlatformFormatTime(inceptClock, inceptTimeBuf, sizeof(inceptTimeBuf)); + + length += mDNS_snprintf(buffer+length, RemSpc, "\t%s %s %d %d %s %s %d %##s", + DNSTypeName(swap16(rrsig->typeCovered)), DNSSECAlgName(rrsig->alg), rrsig->labels, swap32(rrsig->origTTL), + expTimeBuf, inceptTimeBuf, swap16(rrsig->keyTag), ((domainname *)(&rrsig->signerName))->c); + + len = DomainNameLength((domainname *)&rrsig->signerName); + length += base64Encode(buffer + length, RemSpc, (mDNSu8 *)(rd->data + len + RRSIG_FIXED_SIZE), + rr->rdlength - (len + RRSIG_FIXED_SIZE)); + } + break; + case kDNSType_DNSKEY: { + rdataDNSKey *rrkey = (rdataDNSKey *)rd->data; + length += mDNS_snprintf(buffer+length, RemSpc, "\t%d %d %s %u", swap16(rrkey->flags), rrkey->proto, + DNSSECAlgName(rrkey->alg), (unsigned int)keytag((mDNSu8 *)rrkey, rr->rdlength)); + length += base64Encode(buffer + length, RemSpc, (mDNSu8 *)(rd->data + DNSKEY_FIXED_SIZE), + rr->rdlength - DNSKEY_FIXED_SIZE); + } + break; + case kDNSType_DS: { + mDNSu8 *p; + int i; + rdataDS *rrds = (rdataDS *)rd->data; + + length += mDNS_snprintf(buffer+length, RemSpc, "\t%s\t%d\t%s ", DNSSECAlgName(rrds->alg), swap16(rrds->keyTag), + DNSSECDigestName(rrds->digestType)); + + p = (mDNSu8 *)(rd->data + DS_FIXED_SIZE); + for (i = 0; i < (rr->rdlength - DS_FIXED_SIZE); i++) + { + length += mDNS_snprintf(buffer+length, RemSpc, "%x", p[i]); + } + } + break; + + default: mDNS_snprintf(buffer+length, RemSpc, "RDLen %d: %s", rr->rdlength, rd->data); + // Really should scan buffer to check if text is valid UTF-8 and only replace with dots if not + for (ptr = buffer; *ptr; ptr++) if (*ptr < ' ') *ptr = '.'; + break; + } + return(buffer); +} // See comments in mDNSEmbeddedAPI.h #if _PLATFORM_HAS_STRONG_PRNG_ #define mDNSRandomNumber mDNSPlatformRandomNumber #else mDNSlocal mDNSu32 mDNSRandomFromSeed(mDNSu32 seed) - { - return seed * 21 + 1; - } +{ + return seed * 21 + 1; +} mDNSlocal mDNSu32 mDNSMixRandomSeed(mDNSu32 seed, mDNSu8 iteration) - { - return iteration ? mDNSMixRandomSeed(mDNSRandomFromSeed(seed), --iteration) : seed; - } +{ + return iteration ? mDNSMixRandomSeed(mDNSRandomFromSeed(seed), --iteration) : seed; +} mDNSlocal mDNSu32 mDNSRandomNumber() - { - static mDNSBool seeded = mDNSfalse; - static mDNSu32 seed = 0; - if (!seeded) - { - seed = mDNSMixRandomSeed(mDNSPlatformRandomSeed(), 100); - seeded = mDNStrue; - } - return (seed = mDNSRandomFromSeed(seed)); - } +{ + static mDNSBool seeded = mDNSfalse; + static mDNSu32 seed = 0; + if (!seeded) + { + seed = mDNSMixRandomSeed(mDNSPlatformRandomSeed(), 100); + seeded = mDNStrue; + } + return (seed = mDNSRandomFromSeed(seed)); +} #endif // ! _PLATFORM_HAS_STRONG_PRNG_ - -mDNSexport mDNSu32 mDNSRandom(mDNSu32 max) // Returns pseudo-random result from zero to max inclusive - { - mDNSu32 ret = 0; - mDNSu32 mask = 1; - while (mask < max) mask = (mask << 1) | 1; +mDNSexport mDNSu32 mDNSRandom(mDNSu32 max) // Returns pseudo-random result from zero to max inclusive +{ + mDNSu32 ret = 0; + mDNSu32 mask = 1; + + while (mask < max) mask = (mask << 1) | 1; - do ret = mDNSRandomNumber() & mask; - while (ret > max); + do ret = mDNSRandomNumber() & mask; + while (ret > max); - return ret; - } + return ret; +} mDNSexport mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2) - { - if (ip1->type == ip2->type) - { - 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)); - } - } - return(mDNSfalse); - } +{ + if (ip1->type == ip2->type) + { + 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)); + } + } + return(mDNSfalse); +} mDNSexport mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip) - { - switch(ip->type) - { - case mDNSAddrType_IPv4: return(mDNSBool)(mDNSSameIPv4Address(ip->ip.v4, AllDNSLinkGroup_v4.ip.v4)); - case mDNSAddrType_IPv6: return(mDNSBool)(mDNSSameIPv6Address(ip->ip.v6, AllDNSLinkGroup_v6.ip.v6)); - default: return(mDNSfalse); - } - } +{ + switch(ip->type) + { + case mDNSAddrType_IPv4: return (mDNSBool)(mDNSSameIPv4Address(ip->ip.v4, AllDNSLinkGroup_v4.ip.v4)); + case mDNSAddrType_IPv6: return (mDNSBool)(mDNSSameIPv6Address(ip->ip.v6, AllDNSLinkGroup_v6.ip.v6)); + default: return(mDNSfalse); + } +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -331,88 +509,88 @@ mDNSexport mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip) #endif mDNSexport mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b) - { - int i; - const int len = *a++; - - if (len > MAX_DOMAIN_LABEL) - { debugf("Malformed label (too long)"); return(mDNSfalse); } - - if (len != *b++) return(mDNSfalse); - for (i=0; i MAX_DOMAIN_LABEL) + { debugf("Malformed label (too long)"); return(mDNSfalse); } + + if (len != *b++) return(mDNSfalse); + for (i=0; ic; - const mDNSu8 * b = d2->c; - const mDNSu8 *const max = d1->c + MAX_DOMAIN_NAME; // Maximum that's valid - - while (*a || *b) - { - if (a + 1 + *a >= max) - { debugf("Malformed domain name (more than 256 characters)"); return(mDNSfalse); } - if (!SameDomainLabel(a, b)) return(mDNSfalse); - a += 1 + *a; - b += 1 + *b; - } - - return(mDNStrue); - } +{ + const mDNSu8 * a = d1->c; + const mDNSu8 * b = d2->c; + const mDNSu8 *const max = d1->c + MAX_DOMAIN_NAME; // Maximum that's valid + + while (*a || *b) + { + if (a + 1 + *a >= max) + { debugf("Malformed domain name (more than 256 characters)"); return(mDNSfalse); } + if (!SameDomainLabel(a, b)) return(mDNSfalse); + a += 1 + *a; + b += 1 + *b; + } + + return(mDNStrue); +} mDNSexport mDNSBool SameDomainNameCS(const domainname *const d1, const domainname *const d2) - { - mDNSu16 l1 = DomainNameLength(d1); - mDNSu16 l2 = DomainNameLength(d2); - return(l1 <= MAX_DOMAIN_NAME && l1 == l2 && mDNSPlatformMemSame(d1, d2, l1)); - } +{ + mDNSu16 l1 = DomainNameLength(d1); + mDNSu16 l2 = DomainNameLength(d2); + return(l1 <= MAX_DOMAIN_NAME && l1 == l2 && mDNSPlatformMemSame(d1, d2, l1)); +} mDNSexport mDNSBool IsLocalDomain(const domainname *d) - { - // Domains that are defined to be resolved via link-local multicast are: - // local., 254.169.in-addr.arpa., and {8,9,A,B}.E.F.ip6.arpa. - static const domainname *nL = (const domainname*)"\x5" "local"; - static const domainname *nR = (const domainname*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa"; - static const domainname *n8 = (const domainname*)"\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa"; - static const domainname *n9 = (const domainname*)"\x1" "9" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa"; - static const domainname *nA = (const domainname*)"\x1" "a" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa"; - static const domainname *nB = (const domainname*)"\x1" "b" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa"; - - const domainname *d1, *d2, *d3, *d4, *d5; // Top-level domain, second-level domain, etc. - d1 = d2 = d3 = d4 = d5 = mDNSNULL; - while (d->c[0]) - { - d5 = d4; d4 = d3; d3 = d2; d2 = d1; d1 = d; - d = (const domainname*)(d->c + 1 + d->c[0]); - } - - if (d1 && SameDomainName(d1, nL)) return(mDNStrue); - if (d4 && SameDomainName(d4, nR)) return(mDNStrue); - if (d5 && SameDomainName(d5, n8)) return(mDNStrue); - if (d5 && SameDomainName(d5, n9)) return(mDNStrue); - if (d5 && SameDomainName(d5, nA)) return(mDNStrue); - if (d5 && SameDomainName(d5, nB)) return(mDNStrue); - return(mDNSfalse); - } +{ + // Domains that are defined to be resolved via link-local multicast are: + // local., 254.169.in-addr.arpa., and {8,9,A,B}.E.F.ip6.arpa. + static const domainname *nL = (const domainname*)"\x5" "local"; + static const domainname *nR = (const domainname*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa"; + static const domainname *n8 = (const domainname*)"\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa"; + static const domainname *n9 = (const domainname*)"\x1" "9" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa"; + static const domainname *nA = (const domainname*)"\x1" "a" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa"; + static const domainname *nB = (const domainname*)"\x1" "b" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa"; + + const domainname *d1, *d2, *d3, *d4, *d5; // Top-level domain, second-level domain, etc. + d1 = d2 = d3 = d4 = d5 = mDNSNULL; + while (d->c[0]) + { + d5 = d4; d4 = d3; d3 = d2; d2 = d1; d1 = d; + d = (const domainname*)(d->c + 1 + d->c[0]); + } + + if (d1 && SameDomainName(d1, nL)) return(mDNStrue); + if (d4 && SameDomainName(d4, nR)) return(mDNStrue); + if (d5 && SameDomainName(d5, n8)) return(mDNStrue); + if (d5 && SameDomainName(d5, n9)) return(mDNStrue); + if (d5 && SameDomainName(d5, nA)) return(mDNStrue); + if (d5 && SameDomainName(d5, nB)) return(mDNStrue); + return(mDNSfalse); +} mDNSexport const mDNSu8 *LastLabel(const domainname *d) - { - const mDNSu8 *p = d->c; - while (d->c[0]) - { - p = d->c; - d = (const domainname*)(d->c + 1 + d->c[0]); - } - return(p); - } +{ + const mDNSu8 *p = d->c; + while (d->c[0]) + { + p = d->c; + d = (const domainname*)(d->c + 1 + d->c[0]); + } + return(p); +} // Returns length of a domain name INCLUDING the byte for the final null label // e.g. for the root label "." it returns one @@ -420,15 +598,15 @@ mDNSexport const mDNSu8 *LastLabel(const domainname *d) // Legal results are 1 (just root label) to 256 (MAX_DOMAIN_NAME) // If the given domainname is invalid, result is 257 (MAX_DOMAIN_NAME+1) mDNSexport mDNSu16 DomainNameLengthLimit(const domainname *const name, const mDNSu8 *limit) - { - const mDNSu8 *src = name->c; - while (src < limit && *src <= MAX_DOMAIN_LABEL) - { - if (*src == 0) return((mDNSu16)(src - name->c + 1)); - src += 1 + *src; - } - return(MAX_DOMAIN_NAME+1); - } +{ + const mDNSu8 *src = name->c; + while (src < limit && *src <= MAX_DOMAIN_LABEL) + { + if (*src == 0) return((mDNSu16)(src - name->c + 1)); + src += 1 + *src; + } + return(MAX_DOMAIN_NAME+1); +} // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte // for the final null label, e.g. for the root label "." it returns one. @@ -440,36 +618,36 @@ mDNSexport mDNSu16 DomainNameLengthLimit(const domainname *const name, const mDN // E.g. for the name "foo.com." with parent "com.", it returns 6 // (length, three data bytes, two-byte compression pointer). mDNSexport mDNSu16 CompressedDomainNameLength(const domainname *const name, const domainname *parent) - { - const mDNSu8 *src = name->c; - if (parent && parent->c[0] == 0) parent = mDNSNULL; - while (*src) - { - if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1); - if (parent && SameDomainName((const domainname *)src, parent)) return((mDNSu16)(src - name->c + 2)); - src += 1 + *src; - if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1); - } - return((mDNSu16)(src - name->c + 1)); - } +{ + const mDNSu8 *src = name->c; + if (parent && parent->c[0] == 0) parent = mDNSNULL; + while (*src) + { + if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1); + if (parent && SameDomainName((const domainname *)src, parent)) return((mDNSu16)(src - name->c + 2)); + src += 1 + *src; + if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1); + } + return((mDNSu16)(src - name->c + 1)); +} // CountLabels() returns number of labels in name, excluding final root label // (e.g. for "apple.com." CountLabels returns 2.) mDNSexport int CountLabels(const domainname *d) - { - int count = 0; - const mDNSu8 *ptr; - for (ptr = d->c; *ptr; ptr = ptr + ptr[0] + 1) count++; - return count; - } +{ + int count = 0; + const mDNSu8 *ptr; + for (ptr = d->c; *ptr; ptr = ptr + ptr[0] + 1) count++; + return count; +} // SkipLeadingLabels skips over the first 'skip' labels in the domainname, // returning a pointer to the suffix with 'skip' labels removed. mDNSexport const domainname *SkipLeadingLabels(const domainname *d, int skip) - { - while (skip > 0 && d->c[0]) { d = (const domainname *)(d->c + 1 + d->c[0]); skip--; } - return(d); - } +{ + while (skip > 0 && d->c[0]) { d = (const domainname *)(d->c + 1 + d->c[0]); skip--; } + return(d); +} // AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname. // The C string contains the label as-is, with no escaping, etc. @@ -479,19 +657,19 @@ mDNSexport const domainname *SkipLeadingLabels(const domainname *d, int skip) // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes) // AppendLiteralLabelString returns mDNSNULL. mDNSexport mDNSu8 *AppendLiteralLabelString(domainname *const name, const char *cstr) - { - mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name - const mDNSu8 *const lim1 = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero) - const mDNSu8 *const lim2 = ptr + 1 + MAX_DOMAIN_LABEL; - const mDNSu8 *const lim = (lim1 < lim2) ? lim1 : lim2; - mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go - - while (*cstr && ptr < lim) *ptr++ = (mDNSu8)*cstr++; // Copy the data - *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte - *ptr++ = 0; // Put the null root label on the end - if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input - else return(ptr); // Success: return new value of ptr - } +{ + mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name + const mDNSu8 *const lim1 = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero) + const mDNSu8 *const lim2 = ptr + 1 + MAX_DOMAIN_LABEL; + const mDNSu8 *const lim = (lim1 < lim2) ? lim1 : lim2; + mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go + + while (*cstr && ptr < lim) *ptr++ = (mDNSu8)*cstr++; // Copy the data + *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte + *ptr++ = 0; // Put the null root label on the end + if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input + else return(ptr); // Success: return new value of ptr +} // AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname. // The C string is in conventional DNS syntax: @@ -501,41 +679,41 @@ mDNSexport mDNSu8 *AppendLiteralLabelString(domainname *const name, const char * // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes) // AppendDNSNameString returns mDNSNULL. 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 - if (c == '\\') // If escape character, check next character - { - c = (mDNSu8)*cstr++; // Assume we'll just take the next character - if (mDNSIsDigit(cstr[-1]) && mDNSIsDigit(cstr[0]) && mDNSIsDigit(cstr[1])) - { // If three decimal digits, - int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal - int v1 = cstr[ 0] - '0'; - int v2 = cstr[ 1] - '0'; - int val = v0 * 100 + v1 * 10 + v2; - if (val <= 255) { c = (mDNSu8)val; cstr += 2; } // If valid three-digit decimal value, use it - } - } - *ptr++ = c; // Write the character - } - if (*cstr) cstr++; // Skip over the trailing dot (if present) - if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) // If illegal label, abort - return(mDNSNULL); - *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte - } - - *ptr++ = 0; // Put the null root label on the end - if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input - else return(ptr); // Success: return new value of ptr - } +{ + 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 + if (c == '\\') // If escape character, check next character + { + c = (mDNSu8)*cstr++; // Assume we'll just take the next character + if (mDNSIsDigit(cstr[-1]) && mDNSIsDigit(cstr[0]) && mDNSIsDigit(cstr[1])) + { // If three decimal digits, + int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal + int v1 = cstr[ 0] - '0'; + int v2 = cstr[ 1] - '0'; + int val = v0 * 100 + v1 * 10 + v2; + if (val <= 255) { c = (mDNSu8)val; cstr += 2; } // If valid three-digit decimal value, use it + } + } + *ptr++ = c; // Write the character + } + if (*cstr) cstr++; // Skip over the trailing dot (if present) + if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) // If illegal label, abort + return(mDNSNULL); + *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte + } + + *ptr++ = 0; // Put the null root label on the end + if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input + else return(ptr); // Success: return new value of ptr +} // AppendDomainLabel appends a single label to a name. // If successful, AppendDomainLabel returns a pointer to the next unused byte @@ -543,36 +721,36 @@ mDNSexport mDNSu8 *AppendDNSNameString(domainname *const name, const char *cstri // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes) // AppendDomainLabel returns mDNSNULL. mDNSexport mDNSu8 *AppendDomainLabel(domainname *const name, const domainlabel *const label) - { - int i; - mDNSu8 *ptr = name->c + DomainNameLength(name) - 1; +{ + int i; + mDNSu8 *ptr = name->c + DomainNameLength(name) - 1; - // Check label is legal - if (label->c[0] > MAX_DOMAIN_LABEL) return(mDNSNULL); + // Check label is legal + if (label->c[0] > MAX_DOMAIN_LABEL) return(mDNSNULL); - // Check that ptr + length byte + data bytes + final zero does not exceed our limit - if (ptr + 1 + label->c[0] + 1 > name->c + MAX_DOMAIN_NAME) return(mDNSNULL); + // Check that ptr + length byte + data bytes + final zero does not exceed our limit + if (ptr + 1 + label->c[0] + 1 > name->c + MAX_DOMAIN_NAME) return(mDNSNULL); - for (i=0; i<=label->c[0]; i++) *ptr++ = label->c[i]; // Copy the label data - *ptr++ = 0; // Put the null root label on the end - return(ptr); - } + for (i=0; i<=label->c[0]; i++) *ptr++ = label->c[i]; // Copy the label data + *ptr++ = 0; // Put the null root label on the end + return(ptr); +} mDNSexport mDNSu8 *AppendDomainName(domainname *const name, const domainname *const append) - { - mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name - const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero) - const mDNSu8 * src = append->c; - while (src[0]) - { - int i; - if (ptr + 1 + src[0] > lim) return(mDNSNULL); - for (i=0; i<=src[0]; i++) *ptr++ = src[i]; - *ptr = 0; // Put the null root label on the end - src += i; - } - return(ptr); - } +{ + mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name + const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero) + const mDNSu8 * src = append->c; + while (src[0]) + { + int i; + if (ptr + 1 + src[0] > lim) return(mDNSNULL); + for (i=0; i<=src[0]; i++) *ptr++ = src[i]; + *ptr = 0; // Put the null root label on the end + src += i; + } + return(ptr); +} // MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping). // If successful, MakeDomainLabelFromLiteralString returns mDNStrue. @@ -581,13 +759,13 @@ mDNSexport mDNSu8 *AppendDomainName(domainname *const name, const domainname *co // In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored. // In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result. mDNSexport mDNSBool MakeDomainLabelFromLiteralString(domainlabel *const label, const char *cstr) - { - mDNSu8 * ptr = label->c + 1; // Where we're putting it - const mDNSu8 *const limit = label->c + 1 + MAX_DOMAIN_LABEL; // The maximum we can put - while (*cstr && ptr < limit) *ptr++ = (mDNSu8)*cstr++; // Copy the label - label->c[0] = (mDNSu8)(ptr - label->c - 1); // Set the length byte - return(*cstr == 0); // Return mDNStrue if we successfully consumed all input - } +{ + mDNSu8 * ptr = label->c + 1; // Where we're putting it + const mDNSu8 *const limit = label->c + 1 + MAX_DOMAIN_LABEL; // The maximum we can put + while (*cstr && ptr < limit) *ptr++ = (mDNSu8)*cstr++; // Copy the label + label->c[0] = (mDNSu8)(ptr - label->c - 1); // Set the length byte + return(*cstr == 0); // Return mDNStrue if we successfully consumed all input +} // MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string. // The C string is in conventional DNS syntax: @@ -597,58 +775,58 @@ mDNSexport mDNSBool MakeDomainLabelFromLiteralString(domainlabel *const label, c // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes) // MakeDomainNameFromDNSNameString returns mDNSNULL. mDNSexport mDNSu8 *MakeDomainNameFromDNSNameString(domainname *const name, const char *cstr) - { - name->c[0] = 0; // Make an empty domain name - return(AppendDNSNameString(name, cstr)); // And then add this string to it - } +{ + name->c[0] = 0; // Make an empty domain name + return(AppendDNSNameString(name, cstr)); // And then add this string to it +} mDNSexport char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc) - { - const mDNSu8 * src = label->c; // Domain label we're reading - const mDNSu8 len = *src++; // Read length of this (non-null) label - const mDNSu8 *const end = src + len; // Work out where the label ends - if (len > MAX_DOMAIN_LABEL) return(mDNSNULL); // If illegal label, abort - while (src < end) // While we have characters in the label - { - mDNSu8 c = *src++; - if (esc) - { - if (c == '.' || c == esc) // If character is a dot or the escape character - *ptr++ = esc; // Output escape character - else if (c <= ' ') // If non-printing ascii, - { // Output decimal escape sequence - *ptr++ = esc; - *ptr++ = (char) ('0' + (c / 100) ); - *ptr++ = (char) ('0' + (c / 10) % 10); - c = (mDNSu8)('0' + (c ) % 10); - } - } - *ptr++ = (char)c; // Copy the character - } - *ptr = 0; // Null-terminate the string - return(ptr); // and return - } +{ + const mDNSu8 * src = label->c; // Domain label we're reading + const mDNSu8 len = *src++; // Read length of this (non-null) label + const mDNSu8 *const end = src + len; // Work out where the label ends + if (len > MAX_DOMAIN_LABEL) return(mDNSNULL); // If illegal label, abort + while (src < end) // While we have characters in the label + { + mDNSu8 c = *src++; + if (esc) + { + if (c == '.' || c == esc) // If character is a dot or the escape character + *ptr++ = esc; // Output escape character + else if (c <= ' ') // If non-printing ascii, + { // Output decimal escape sequence + *ptr++ = esc; + *ptr++ = (char) ('0' + (c / 100) ); + *ptr++ = (char) ('0' + (c / 10) % 10); + c = (mDNSu8)('0' + (c ) % 10); + } + } + *ptr++ = (char)c; // Copy the character + } + *ptr = 0; // Null-terminate the string + return(ptr); // and return +} // Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1009 bytes) mDNSexport char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc) - { - const mDNSu8 *src = name->c; // Domain name we're reading - const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid +{ + const mDNSu8 *src = name->c; // Domain name we're reading + const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid - if (*src == 0) *ptr++ = '.'; // Special case: For root, just write a dot + if (*src == 0) *ptr++ = '.'; // Special case: For root, just write a dot - while (*src) // While more characters in the domain name - { - if (src + 1 + *src >= max) return(mDNSNULL); - ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc); - if (!ptr) return(mDNSNULL); - src += 1 + *src; - *ptr++ = '.'; // Write the dot after the label - } + while (*src) // While more characters in the domain name + { + if (src + 1 + *src >= max) return(mDNSNULL); + ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc); + if (!ptr) return(mDNSNULL); + src += 1 + *src; + *ptr++ = '.'; // Write the dot after the label + } - *ptr++ = 0; // Null-terminate the string - return(ptr); // and return - } + *ptr++ = 0; // Null-terminate the string + return(ptr); // and return +} // RFC 1034 rules: // Host names must start with a letter, end with a letter or digit, @@ -656,145 +834,145 @@ mDNSexport char *ConvertDomainNameToCString_withescape(const domainname *const n // This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit mDNSexport void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel) - { - const mDNSu8 * src = &UTF8Name[1]; - const mDNSu8 *const end = &UTF8Name[1] + UTF8Name[0]; - mDNSu8 * ptr = &hostlabel->c[1]; - const mDNSu8 *const lim = &hostlabel->c[1] + MAX_DOMAIN_LABEL; - while (src < end) - { - // Delete apostrophes from source name - if (src[0] == '\'') { src++; continue; } // Standard straight single quote - if (src + 2 < end && src[0] == 0xE2 && src[1] == 0x80 && src[2] == 0x99) - { src += 3; continue; } // Unicode curly apostrophe - if (ptr < lim) - { - if (mDNSValidHostChar(*src, (ptr > &hostlabel->c[1]), (src < end-1))) *ptr++ = *src; - else if (ptr > &hostlabel->c[1] && ptr[-1] != '-') *ptr++ = '-'; - } - src++; - } - while (ptr > &hostlabel->c[1] && ptr[-1] == '-') ptr--; // Truncate trailing '-' marks - hostlabel->c[0] = (mDNSu8)(ptr - &hostlabel->c[1]); - } +{ + const mDNSu8 * src = &UTF8Name[1]; + const mDNSu8 *const end = &UTF8Name[1] + UTF8Name[0]; + mDNSu8 * ptr = &hostlabel->c[1]; + const mDNSu8 *const lim = &hostlabel->c[1] + MAX_DOMAIN_LABEL; + while (src < end) + { + // Delete apostrophes from source name + if (src[0] == '\'') { src++; continue; } // Standard straight single quote + if (src + 2 < end && src[0] == 0xE2 && src[1] == 0x80 && src[2] == 0x99) + { src += 3; continue; } // Unicode curly apostrophe + if (ptr < lim) + { + if (mDNSValidHostChar(*src, (ptr > &hostlabel->c[1]), (src < end-1))) *ptr++ = *src; + else if (ptr > &hostlabel->c[1] && ptr[-1] != '-') *ptr++ = '-'; + } + src++; + } + while (ptr > &hostlabel->c[1] && ptr[-1] == '-') ptr--; // Truncate trailing '-' marks + hostlabel->c[0] = (mDNSu8)(ptr - &hostlabel->c[1]); +} #define ValidTransportProtocol(X) ( (X)[0] == 4 && (X)[1] == '_' && \ - ((((X)[2] | 0x20) == 'u' && ((X)[3] | 0x20) == 'd') || (((X)[2] | 0x20) == 't' && ((X)[3] | 0x20) == 'c')) && \ - ((X)[4] | 0x20) == 'p') + ((((X)[2] | 0x20) == 'u' && ((X)[3] | 0x20) == 'd') || (((X)[2] | 0x20) == 't' && ((X)[3] | 0x20) == 'c')) && \ + ((X)[4] | 0x20) == 'p') mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn, - const domainlabel *name, const domainname *type, const domainname *const domain) - { - int i, len; - mDNSu8 *dst = fqdn->c; - const mDNSu8 *src; - const char *errormsg; + const domainlabel *name, const domainname *type, const domainname *const domain) +{ + int i, len; + mDNSu8 *dst = fqdn->c; + const mDNSu8 *src; + const char *errormsg; #if APPLE_OSX_mDNSResponder - mDNSBool loggedUnderscore = mDNSfalse; - static char typeBuf[MAX_ESCAPED_DOMAIN_NAME]; + mDNSBool loggedUnderscore = mDNSfalse; + static char typeBuf[MAX_ESCAPED_DOMAIN_NAME]; #endif - // In the case where there is no name (and ONLY in that case), - // a single-label subtype is allowed as the first label of a three-part "type" - if (!name && type) - { - const mDNSu8 *s0 = type->c; - if (s0[0] && s0[0] < 0x40) // If legal first label (at least one character, and no more than 63) - { - const mDNSu8 * s1 = s0 + 1 + s0[0]; - if (s1[0] && s1[0] < 0x40) // and legal second label (at least one character, and no more than 63) - { - const mDNSu8 *s2 = s1 + 1 + s1[0]; - if (s2[0] && s2[0] < 0x40 && s2[1+s2[0]] == 0) // and we have three and only three labels - { - static const mDNSu8 SubTypeLabel[5] = "\x04_sub"; - src = s0; // Copy the first label - len = *src; - for (i=0; i <= len; i++) *dst++ = *src++; - for (i=0; i < (int)sizeof(SubTypeLabel); i++) *dst++ = SubTypeLabel[i]; - type = (const domainname *)s1; - - // Special support to enable the DNSServiceBrowse call made by Bonjour Browser - // For these queries, we retract the "._sub" we just added between the subtype and the main type - // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse - if (SameDomainName((domainname*)s0, (const domainname*)"\x09_services\x07_dns-sd\x04_udp")) - dst -= sizeof(SubTypeLabel); - } - } - } - } - - if (name && name->c[0]) - { - src = name->c; // Put the service name into the domain name - len = *src; - if (len >= 0x40) { errormsg = "Service instance name too long"; goto fail; } - for (i=0; i<=len; i++) *dst++ = *src++; - } - else - name = (domainlabel*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below - - src = type->c; // Put the service type into the domain name - len = *src; - if (len < 2 || len > 16) - { - LogMsg("Bad service type in %#s.%##s%##s Application protocol name must be underscore plus 1-15 characters. " - "See ", name->c, type->c, domain->c); + // In the case where there is no name (and ONLY in that case), + // a single-label subtype is allowed as the first label of a three-part "type" + if (!name && type) + { + const mDNSu8 *s0 = type->c; + if (s0[0] && s0[0] < 0x40) // If legal first label (at least one character, and no more than 63) + { + const mDNSu8 * s1 = s0 + 1 + s0[0]; + if (s1[0] && s1[0] < 0x40) // and legal second label (at least one character, and no more than 63) + { + const mDNSu8 *s2 = s1 + 1 + s1[0]; + if (s2[0] && s2[0] < 0x40 && s2[1+s2[0]] == 0) // and we have three and only three labels + { + static const mDNSu8 SubTypeLabel[5] = "\x04_sub"; + src = s0; // Copy the first label + len = *src; + for (i=0; i <= len; i++) *dst++ = *src++; + for (i=0; i < (int)sizeof(SubTypeLabel); i++) *dst++ = SubTypeLabel[i]; + type = (const domainname *)s1; + + // Special support to enable the DNSServiceBrowse call made by Bonjour Browser + // For these queries, we retract the "._sub" we just added between the subtype and the main type + // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse + if (SameDomainName((domainname*)s0, (const domainname*)"\x09_services\x07_dns-sd\x04_udp")) + dst -= sizeof(SubTypeLabel); + } + } + } + } + + if (name && name->c[0]) + { + src = name->c; // Put the service name into the domain name + len = *src; + if (len >= 0x40) { errormsg = "Service instance name too long"; goto fail; } + for (i=0; i<=len; i++) *dst++ = *src++; + } + else + name = (domainlabel*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below + + src = type->c; // Put the service type into the domain name + len = *src; + if (len < 2 || len > 16) + { + LogMsg("Bad service type in %#s.%##s%##s Application protocol name must be underscore plus 1-15 characters. " + "See ", name->c, type->c, domain->c); #if APPLE_OSX_mDNSResponder - ConvertDomainNameToCString(type, typeBuf); - mDNSASLLog(mDNSNULL, "serviceType.nameTooLong", "noop", typeBuf, ""); + ConvertDomainNameToCString(type, typeBuf); + mDNSASLLog(mDNSNULL, "serviceType.nameTooLong", "noop", typeBuf, ""); #endif - } - if (len < 2 || len >= 0x40 || (len > 16 && !SameDomainName(domain, &localdomain))) return(mDNSNULL); - if (src[1] != '_') { errormsg = "Application protocol name must begin with underscore"; goto fail; } - for (i=2; i<=len; i++) - { - // Letters and digits are allowed anywhere - if (mDNSIsLetter(src[i]) || mDNSIsDigit(src[i])) continue; - // Hyphens are only allowed as interior characters - // Underscores are not supposed to be allowed at all, but for backwards compatibility with some old products we do allow them, - // with the same rule as hyphens - if ((src[i] == '-' || src[i] == '_') && i > 2 && i < len) - { + } + if (len < 2 || len >= 0x40 || (len > 16 && !SameDomainName(domain, &localdomain))) return(mDNSNULL); + if (src[1] != '_') { errormsg = "Application protocol name must begin with underscore"; goto fail; } + for (i=2; i<=len; i++) + { + // Letters and digits are allowed anywhere + if (mDNSIsLetter(src[i]) || mDNSIsDigit(src[i])) continue; + // Hyphens are only allowed as interior characters + // Underscores are not supposed to be allowed at all, but for backwards compatibility with some old products we do allow them, + // with the same rule as hyphens + if ((src[i] == '-' || src[i] == '_') && i > 2 && i < len) + { #if APPLE_OSX_mDNSResponder - if (src[i] == '_' && loggedUnderscore == mDNSfalse) - { - ConvertDomainNameToCString(type, typeBuf); - mDNSASLLog(mDNSNULL, "serviceType.nameWithUnderscore", "noop", typeBuf, ""); - loggedUnderscore = mDNStrue; - } + if (src[i] == '_' && loggedUnderscore == mDNSfalse) + { + ConvertDomainNameToCString(type, typeBuf); + mDNSASLLog(mDNSNULL, "serviceType.nameWithUnderscore", "noop", typeBuf, ""); + loggedUnderscore = mDNStrue; + } #endif - continue; - } - errormsg = "Application protocol name must contain only letters, digits, and hyphens"; + continue; + } + errormsg = "Application protocol name must contain only letters, digits, and hyphens"; #if APPLE_OSX_mDNSResponder - { - ConvertDomainNameToCString(type, typeBuf); - mDNSASLLog(mDNSNULL, "serviceType.nameWithIllegalCharacters", "noop", typeBuf, ""); - } + { + ConvertDomainNameToCString(type, typeBuf); + mDNSASLLog(mDNSNULL, "serviceType.nameWithIllegalCharacters", "noop", typeBuf, ""); + } #endif - goto fail; - } - for (i=0; i<=len; i++) *dst++ = *src++; + goto fail; + } + for (i=0; i<=len; i++) *dst++ = *src++; - len = *src; - if (!ValidTransportProtocol(src)) { errormsg = "Transport protocol name must be _udp or _tcp"; goto fail; } - for (i=0; i<=len; i++) *dst++ = *src++; + len = *src; + if (!ValidTransportProtocol(src)) { 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; } + 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, (const 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); + *dst = 0; + if (!domain->c[0]) { errormsg = "Service domain must be non-empty"; goto fail; } + if (SameDomainName(domain, (const 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); fail: - LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg, name->c, type->c, domain->c); - return(mDNSNULL); - } + LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg, name->c, type->c, domain->c); + return(mDNSNULL); +} // A service name has the form: instance.application-protocol.transport-protocol.domain // DeconstructServiceName is currently fairly forgiving: It doesn't try to enforce character @@ -802,47 +980,47 @@ fail: // However, if the given FQDN doesn't contain at least three labels, // DeconstructServiceName will reject it and return mDNSfalse. mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn, - domainlabel *const name, domainname *const type, domainname *const domain) - { - int i, len; - const mDNSu8 *src = fqdn->c; - const mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME; - mDNSu8 *dst; - - dst = name->c; // Extract the service name - len = *src; - if (!len) { debugf("DeconstructServiceName: FQDN empty!"); return(mDNSfalse); } - if (len >= 0x40) { debugf("DeconstructServiceName: Instance name too long"); return(mDNSfalse); } - for (i=0; i<=len; i++) *dst++ = *src++; - - dst = type->c; // Extract the service type - len = *src; - if (!len) { debugf("DeconstructServiceName: FQDN contains only one label!"); return(mDNSfalse); } - if (len >= 0x40) { debugf("DeconstructServiceName: Application protocol name too long"); return(mDNSfalse); } - if (src[1] != '_'){ debugf("DeconstructServiceName: No _ at start of application protocol"); return(mDNSfalse); } - for (i=0; i<=len; i++) *dst++ = *src++; - - len = *src; - if (!len) { debugf("DeconstructServiceName: FQDN contains only two labels!"); return(mDNSfalse); } - if (!ValidTransportProtocol(src)) - { debugf("DeconstructServiceName: Transport protocol must be _udp or _tcp"); return(mDNSfalse); } - for (i=0; i<=len; i++) *dst++ = *src++; - *dst++ = 0; // Put terminator on the end of service type - - dst = domain->c; // Extract the service domain - while (*src) - { - len = *src; - if (len >= 0x40) - { debugf("DeconstructServiceName: Label in service domain too long"); return(mDNSfalse); } - if (src + 1 + len + 1 >= max) - { debugf("DeconstructServiceName: Total service domain too long"); return(mDNSfalse); } - for (i=0; i<=len; i++) *dst++ = *src++; - } - *dst++ = 0; // Put the null root label on the end - - return(mDNStrue); - } + domainlabel *const name, domainname *const type, domainname *const domain) +{ + int i, len; + const mDNSu8 *src = fqdn->c; + const mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME; + mDNSu8 *dst; + + dst = name->c; // Extract the service name + len = *src; + if (!len) { debugf("DeconstructServiceName: FQDN empty!"); return(mDNSfalse); } + if (len >= 0x40) { debugf("DeconstructServiceName: Instance name too long"); return(mDNSfalse); } + for (i=0; i<=len; i++) *dst++ = *src++; + + dst = type->c; // Extract the service type + len = *src; + if (!len) { debugf("DeconstructServiceName: FQDN contains only one label!"); return(mDNSfalse); } + if (len >= 0x40) { debugf("DeconstructServiceName: Application protocol name too long"); return(mDNSfalse); } + if (src[1] != '_') { debugf("DeconstructServiceName: No _ at start of application protocol"); return(mDNSfalse); } + for (i=0; i<=len; i++) *dst++ = *src++; + + len = *src; + if (!len) { debugf("DeconstructServiceName: FQDN contains only two labels!"); return(mDNSfalse); } + if (!ValidTransportProtocol(src)) + { debugf("DeconstructServiceName: Transport protocol must be _udp or _tcp"); return(mDNSfalse); } + for (i=0; i<=len; i++) *dst++ = *src++; + *dst++ = 0; // Put terminator on the end of service type + + dst = domain->c; // Extract the service domain + while (*src) + { + len = *src; + if (len >= 0x40) + { debugf("DeconstructServiceName: Label in service domain too long"); return(mDNSfalse); } + if (src + 1 + len + 1 >= max) + { debugf("DeconstructServiceName: Total service domain too long"); return(mDNSfalse); } + for (i=0; i<=len; i++) *dst++ = *src++; + } + *dst++ = 0; // Put the null root label on the end + + return(mDNStrue); +} // Notes on UTF-8: // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F @@ -860,125 +1038,125 @@ mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn, // and the second is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8). mDNSexport mDNSu32 TruncateUTF8ToLength(mDNSu8 *string, mDNSu32 length, mDNSu32 max) - { - if (length > max) - { - mDNSu8 c1 = string[max]; // First byte after cut point - mDNSu8 c2 = (max+1 < length) ? string[max+1] : (mDNSu8)0xB0; // Second byte after cut point - length = max; // Trim length down - while (length > 0) - { - // Check if the byte right after the chop point is a UTF-8 continuation byte, - // or if the character right after the chop point is the second of a UTF-16 surrogate pair. - // If so, then we continue to chop more bytes until we get to a legal chop point. - mDNSBool continuation = ((c1 & 0xC0) == 0x80); - mDNSBool secondsurrogate = (c1 == 0xED && (c2 & 0xF0) == 0xB0); - if (!continuation && !secondsurrogate) break; - c2 = c1; - c1 = string[--length]; - } - // Having truncated characters off the end of our string, also cut off any residual white space - while (length > 0 && string[length-1] <= ' ') length--; - } - return(length); - } +{ + if (length > max) + { + mDNSu8 c1 = string[max]; // First byte after cut point + mDNSu8 c2 = (max+1 < length) ? string[max+1] : (mDNSu8)0xB0; // Second byte after cut point + length = max; // Trim length down + while (length > 0) + { + // Check if the byte right after the chop point is a UTF-8 continuation byte, + // or if the character right after the chop point is the second of a UTF-16 surrogate pair. + // If so, then we continue to chop more bytes until we get to a legal chop point. + mDNSBool continuation = ((c1 & 0xC0) == 0x80); + mDNSBool secondsurrogate = (c1 == 0xED && (c2 & 0xF0) == 0xB0); + if (!continuation && !secondsurrogate) break; + c2 = c1; + c1 = string[--length]; + } + // Having truncated characters off the end of our string, also cut off any residual white space + while (length > 0 && string[length-1] <= ' ') length--; + } + return(length); +} // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034 // name ends in "-nnn", where n is some decimal number. mDNSexport mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText) - { - mDNSu16 l = name->c[0]; - - if (RichText) - { - if (l < 4) return mDNSfalse; // Need at least " (2)" - if (name->c[l--] != ')') return mDNSfalse; // Last char must be ')' - if (!mDNSIsDigit(name->c[l])) return mDNSfalse; // Preceeded by a digit - l--; - while (l > 2 && mDNSIsDigit(name->c[l])) l--; // Strip off digits - return (name->c[l] == '(' && name->c[l - 1] == ' '); - } - else - { - if (l < 2) return mDNSfalse; // Need at least "-2" - if (!mDNSIsDigit(name->c[l])) return mDNSfalse; // Last char must be a digit - l--; - while (l > 2 && mDNSIsDigit(name->c[l])) l--; // Strip off digits - return (name->c[l] == '-'); - } - } +{ + mDNSu16 l = name->c[0]; + + if (RichText) + { + if (l < 4) return mDNSfalse; // Need at least " (2)" + if (name->c[l--] != ')') return mDNSfalse; // Last char must be ')' + if (!mDNSIsDigit(name->c[l])) return mDNSfalse; // Preceeded by a digit + l--; + while (l > 2 && mDNSIsDigit(name->c[l])) l--; // Strip off digits + return (name->c[l] == '(' && name->c[l - 1] == ' '); + } + else + { + if (l < 2) return mDNSfalse; // Need at least "-2" + if (!mDNSIsDigit(name->c[l])) return mDNSfalse; // Last char must be a digit + l--; + while (l > 2 && mDNSIsDigit(name->c[l])) l--; // Strip off digits + return (name->c[l] == '-'); + } +} // removes an auto-generated suffix (appended on a name collision) from a label. caller is // responsible for ensuring that the label does indeed contain a suffix. returns the number // from the suffix that was removed. mDNSexport mDNSu32 RemoveLabelSuffix(domainlabel *name, mDNSBool RichText) - { - mDNSu32 val = 0, multiplier = 1; +{ + mDNSu32 val = 0, multiplier = 1; - // Chop closing parentheses from RichText suffix - if (RichText && name->c[0] >= 1 && name->c[name->c[0]] == ')') name->c[0]--; + // Chop closing parentheses from RichText suffix + if (RichText && name->c[0] >= 1 && name->c[name->c[0]] == ')') name->c[0]--; - // Get any existing numerical suffix off the name - while (mDNSIsDigit(name->c[name->c[0]])) - { val += (name->c[name->c[0]] - '0') * multiplier; multiplier *= 10; name->c[0]--; } + // Get any existing numerical suffix off the name + while (mDNSIsDigit(name->c[name->c[0]])) + { val += (name->c[name->c[0]] - '0') * multiplier; multiplier *= 10; name->c[0]--; } - // Chop opening parentheses or dash from suffix - if (RichText) - { - if (name->c[0] >= 2 && name->c[name->c[0]] == '(' && name->c[name->c[0]-1] == ' ') name->c[0] -= 2; - } - else - { - if (name->c[0] >= 1 && name->c[name->c[0]] == '-') name->c[0] -= 1; - } + // Chop opening parentheses or dash from suffix + if (RichText) + { + if (name->c[0] >= 2 && name->c[name->c[0]] == '(' && name->c[name->c[0]-1] == ' ') name->c[0] -= 2; + } + else + { + if (name->c[0] >= 1 && name->c[name->c[0]] == '-') name->c[0] -= 1; + } - return(val); - } + return(val); +} // appends a numerical suffix to a label, with the number following a whitespace and enclosed // in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label). mDNSexport void AppendLabelSuffix(domainlabel *const name, mDNSu32 val, const mDNSBool RichText) - { - mDNSu32 divisor = 1, chars = 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2") - if (RichText) chars = 4; // Shortest possible RichText suffix is 4 characters (" (2)") +{ + mDNSu32 divisor = 1, chars = 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2") + if (RichText) chars = 4; // Shortest possible RichText suffix is 4 characters (" (2)") - // Truncate trailing spaces from RichText names - if (RichText) while (name->c[name->c[0]] == ' ') name->c[0]--; + // Truncate trailing spaces from RichText names + if (RichText) while (name->c[name->c[0]] == ' ') name->c[0]--; - while (divisor < 0xFFFFFFFFUL/10 && val >= divisor * 10) { divisor *= 10; chars++; } + while (divisor < 0xFFFFFFFFUL/10 && val >= divisor * 10) { divisor *= 10; chars++; } - name->c[0] = (mDNSu8) TruncateUTF8ToLength(name->c+1, name->c[0], MAX_DOMAIN_LABEL - chars); + name->c[0] = (mDNSu8) TruncateUTF8ToLength(name->c+1, name->c[0], MAX_DOMAIN_LABEL - chars); - if (RichText) { name->c[++name->c[0]] = ' '; name->c[++name->c[0]] = '('; } - else { name->c[++name->c[0]] = '-'; } + if (RichText) { name->c[++name->c[0]] = ' '; name->c[++name->c[0]] = '('; } + else { name->c[++name->c[0]] = '-'; } - while (divisor) - { - name->c[++name->c[0]] = (mDNSu8)('0' + val / divisor); - val %= divisor; - divisor /= 10; - } + while (divisor) + { + name->c[++name->c[0]] = (mDNSu8)('0' + val / divisor); + val %= divisor; + divisor /= 10; + } - if (RichText) name->c[++name->c[0]] = ')'; - } + if (RichText) name->c[++name->c[0]] = ')'; +} mDNSexport void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText) - { - mDNSu32 val = 0; +{ + mDNSu32 val = 0; - if (LabelContainsSuffix(name, RichText)) - val = RemoveLabelSuffix(name, RichText); + if (LabelContainsSuffix(name, RichText)) + val = RemoveLabelSuffix(name, RichText); - // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate. - // If existing suffix in the range 2-9, increment it. - // If we've had ten conflicts already, there are probably too many hosts trying to use the same name, - // so add a random increment to improve the chances of finding an available name next time. - if (val == 0) val = 2; - else if (val < 10) val++; - else val += 1 + mDNSRandom(99); + // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate. + // If existing suffix in the range 2-9, increment it. + // If we've had ten conflicts already, there are probably too many hosts trying to use the same name, + // so add a random increment to improve the chances of finding an available name next time. + if (val == 0) val = 2; + else if (val < 10) val++; + else val += 1 + mDNSRandom(99); - AppendLabelSuffix(name, val, RichText); - } + AppendLabelSuffix(name, val, RichText); +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -989,223 +1167,382 @@ mDNSexport void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText) // Set up a AuthRecord with sensible default values. // These defaults may be overwritten with new values before mDNS_Register is called mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID, - mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, AuthRecType artype, mDNSRecordCallback Callback, void *Context) - { - // - // LocalOnly auth record can be created with LocalOnly InterfaceID or a valid InterfaceID. - // Most of the applications normally create with LocalOnly InterfaceID and we store them as - // such, so that we can deliver the response to questions that specify LocalOnly InterfaceID. - // LocalOnly resource records can also be created with valid InterfaceID which happens today - // when we create LocalOnly records for /etc/hosts. - - if (InterfaceID == mDNSInterface_LocalOnly && artype != AuthRecordLocalOnly) - { - LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch LocalOnly record InterfaceID %p called with artype %d", InterfaceID, artype); - return; - } - else if (InterfaceID == mDNSInterface_P2P && artype != AuthRecordP2P) - { - LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch P2P record InterfaceID %p called with artype %d", InterfaceID, artype); - return; - } - else if (!InterfaceID && (artype == AuthRecordP2P || artype == AuthRecordLocalOnly)) - { - LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch InterfaceAny record InterfaceID %p called with artype %d", InterfaceID, artype); - return; - } - - // 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 = DefaultTTLforRRType(rrtype); - - // Field Group 1: The actual information pertaining to this resource record - rr->resrec.RecordType = RecordType; - rr->resrec.InterfaceID = InterfaceID; - rr->resrec.name = &rr->namestorage; - rr->resrec.rrtype = rrtype; - rr->resrec.rrclass = kDNSClass_IN; - rr->resrec.rroriginalttl = ttl; - rr->resrec.rDNSServer = mDNSNULL; + mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, AuthRecType artype, mDNSRecordCallback Callback, void *Context) +{ + // + // LocalOnly auth record can be created with LocalOnly InterfaceID or a valid InterfaceID. + // Most of the applications normally create with LocalOnly InterfaceID and we store them as + // such, so that we can deliver the response to questions that specify LocalOnly InterfaceID. + // LocalOnly resource records can also be created with valid InterfaceID which happens today + // when we create LocalOnly records for /etc/hosts. + + if (InterfaceID == mDNSInterface_LocalOnly && artype != AuthRecordLocalOnly) + { + LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch LocalOnly record InterfaceID %p called with artype %d", InterfaceID, artype); + return; + } + else if (InterfaceID == mDNSInterface_P2P && artype != AuthRecordP2P) + { + LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch P2P record InterfaceID %p called with artype %d", InterfaceID, artype); + return; + } + else if (!InterfaceID && (artype == AuthRecordP2P || artype == AuthRecordLocalOnly)) + { + LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch InterfaceAny record InterfaceID %p called with artype %d", InterfaceID, artype); + return; + } + + // 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 = DefaultTTLforRRType(rrtype); + + // Field Group 1: The actual information pertaining to this resource record + rr->resrec.RecordType = RecordType; + rr->resrec.InterfaceID = InterfaceID; + rr->resrec.name = &rr->namestorage; + rr->resrec.rrtype = rrtype; + rr->resrec.rrclass = kDNSClass_IN; + rr->resrec.rroriginalttl = ttl; + rr->resrec.rDNSServer = mDNSNULL; // rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal // rr->resrec.rdestimate = set in mDNS_Register_internal // rr->resrec.rdata = MUST be set by client - if (RDataStorage) - rr->resrec.rdata = RDataStorage; - else - { - rr->resrec.rdata = &rr->rdatastorage; - rr->resrec.rdata->MaxRDLength = sizeof(RDataBody); - } - - // Field Group 2: Persistent metadata for Authoritative Records - rr->Additional1 = mDNSNULL; - rr->Additional2 = mDNSNULL; - rr->DependentOn = mDNSNULL; - rr->RRSet = mDNSNULL; - rr->RecordCallback = Callback; - rr->RecordContext = Context; - - rr->AutoTarget = Target_Manual; - rr->AllowRemoteQuery = mDNSfalse; - rr->ForceMCast = mDNSfalse; - - rr->WakeUp = zeroOwner; - rr->AddressProxy = zeroAddr; - rr->TimeRcvd = 0; - rr->TimeExpire = 0; - rr->ARType = artype; - - // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal) - // Field Group 4: Transient uDNS state for Authoritative Records (set in mDNS_Register_internal) - - // For now, until the uDNS code is fully integrated, it's helpful to zero the uDNS state fields here too, just in case - // (e.g. uDNS_RegisterService short-circuits the usual mDNS_Register_internal record registration calls, so a bunch - // of fields don't get set up properly. In particular, if we don't zero rr->QueuedRData then the uDNS code crashes.) - rr->state = regState_Zero; - rr->uselease = 0; - rr->expire = 0; - rr->Private = 0; - rr->updateid = zeroID; - rr->zone = rr->resrec.name; - rr->nta = mDNSNULL; - rr->tcp = mDNSNULL; - rr->OrigRData = 0; - rr->OrigRDLen = 0; - rr->InFlightRData = 0; - rr->InFlightRDLen = 0; - rr->QueuedRData = 0; - rr->QueuedRDLen = 0; - mDNSPlatformMemZero(&rr->NATinfo, sizeof(rr->NATinfo)); - rr->SRVChanged = mDNSfalse; - rr->mState = mergeState_Zero; - - rr->namestorage.c[0] = 0; // MUST be set by client before calling mDNS_Register() - } + if (RDataStorage) + rr->resrec.rdata = RDataStorage; + else + { + rr->resrec.rdata = &rr->rdatastorage; + rr->resrec.rdata->MaxRDLength = sizeof(RDataBody); + } + + // Field Group 2: Persistent metadata for Authoritative Records + rr->Additional1 = mDNSNULL; + rr->Additional2 = mDNSNULL; + rr->DependentOn = mDNSNULL; + rr->RRSet = mDNSNULL; + rr->RecordCallback = Callback; + rr->RecordContext = Context; + + rr->AutoTarget = Target_Manual; + rr->AllowRemoteQuery = mDNSfalse; + rr->ForceMCast = mDNSfalse; + + rr->WakeUp = zeroOwner; + rr->AddressProxy = zeroAddr; + rr->TimeRcvd = 0; + rr->TimeExpire = 0; + rr->ARType = artype; + + // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal) + // Field Group 4: Transient uDNS state for Authoritative Records (set in mDNS_Register_internal) + + // For now, until the uDNS code is fully integrated, it's helpful to zero the uDNS state fields here too, just in case + // (e.g. uDNS_RegisterService short-circuits the usual mDNS_Register_internal record registration calls, so a bunch + // of fields don't get set up properly. In particular, if we don't zero rr->QueuedRData then the uDNS code crashes.) + rr->state = regState_Zero; + rr->uselease = 0; + rr->expire = 0; + rr->Private = 0; + rr->updateid = zeroID; + rr->zone = rr->resrec.name; + rr->nta = mDNSNULL; + rr->tcp = mDNSNULL; + rr->OrigRData = 0; + rr->OrigRDLen = 0; + rr->InFlightRData = 0; + rr->InFlightRDLen = 0; + rr->QueuedRData = 0; + rr->QueuedRDLen = 0; + mDNSPlatformMemZero(&rr->NATinfo, sizeof(rr->NATinfo)); + rr->SRVChanged = mDNSfalse; + rr->mState = mergeState_Zero; + + rr->namestorage.c[0] = 0; // MUST be set by client before calling mDNS_Register() +} mDNSexport void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID InterfaceID, const domainname *const name, - const mDNSu16 qtype, mDNSQuestionCallback *const callback, void *const context) - { - q->InterfaceID = InterfaceID; - q->Target = zeroAddr; - AssignDomainName(&q->qname, name); - q->qtype = qtype; - q->qclass = kDNSClass_IN; - q->LongLived = (qtype == kDNSType_PTR); - q->ExpectUnique = (qtype != kDNSType_PTR); - q->ForceMCast = mDNSfalse; - q->ReturnIntermed = mDNSfalse; - q->SuppressUnusable = mDNSfalse; - q->SearchListIndex = 0; - q->AppendSearchDomains = 0; - q->RetryWithSearchDomains = mDNSfalse; - q->TimeoutQuestion = 0; - q->WakeOnResolve = 0; - q->qnameOrig = mDNSNULL; - q->QuestionCallback = callback; - q->QuestionContext = context; - } + const mDNSu16 qtype, mDNSQuestionCallback *const callback, void *const context) +{ + q->InterfaceID = InterfaceID; + q->flags = 0; + q->Target = zeroAddr; + AssignDomainName(&q->qname, name); + q->qtype = qtype; + q->qclass = kDNSClass_IN; + q->LongLived = (qtype == kDNSType_PTR); + q->ExpectUnique = (qtype != kDNSType_PTR); + q->ForceMCast = mDNSfalse; + q->ReturnIntermed = mDNSfalse; + q->SuppressUnusable = mDNSfalse; + q->SearchListIndex = 0; + q->AppendSearchDomains = 0; + q->RetryWithSearchDomains = mDNSfalse; + q->TimeoutQuestion = 0; + q->WakeOnResolve = 0; + q->UseBrackgroundTrafficClass = mDNSfalse; + q->ValidationRequired = 0; + q->ValidatingResponse = 0; + q->qnameOrig = mDNSNULL; + q->QuestionCallback = callback; + q->QuestionContext = context; +} mDNSexport mDNSu32 RDataHashValue(const ResourceRecord *const rr) - { - int len = rr->rdlength; - const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; - switch(rr->rrtype) - { - case kDNSType_NS: - case kDNSType_CNAME: - case kDNSType_PTR: - case kDNSType_DNAME: return DomainNameHashValue(&rdb->name); - - case kDNSType_SOA: return rdb->soa.serial + - rdb->soa.refresh + - rdb->soa.retry + - rdb->soa.expire + - rdb->soa.min + - DomainNameHashValue(&rdb->soa.mname) + - DomainNameHashValue(&rdb->soa.rname); - - case kDNSType_MX: - case kDNSType_AFSDB: - case kDNSType_RT: - case kDNSType_KX: return DomainNameHashValue(&rdb->mx.exchange); - - case kDNSType_RP: return DomainNameHashValue(&rdb->rp.mbox) + DomainNameHashValue(&rdb->rp.txt); - - case kDNSType_PX: return DomainNameHashValue(&rdb->px.map822) + DomainNameHashValue(&rdb->px.mapx400); - - case kDNSType_SRV: return DomainNameHashValue(&rdb->srv.target); - - case kDNSType_OPT: return 0; // OPT is a pseudo-RR container structure; makes no sense to compare - - case kDNSType_NSEC: len = sizeof(rdataNSEC); // Use in-memory length of 32, and fall through default checksum computation below - - default: - { - mDNSu32 sum = 0; - int i; - for (i=0; i+1 < len; i+=2) - { - sum += (((mDNSu32)(rdb->data[i])) << 8) | rdb->data[i+1]; - sum = (sum<<3) | (sum>>29); - } - if (i < len) - { - sum += ((mDNSu32)(rdb->data[i])) << 8; - } - return(sum); - } - } - } +{ + int len = rr->rdlength; + const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; + const mDNSu8 *ptr = rdb->data; + mDNSu32 sum = 0; + + switch(rr->rrtype) + { + case kDNSType_NS: + case kDNSType_MD: + case kDNSType_MF: + case kDNSType_CNAME: + case kDNSType_MB: + case kDNSType_MG: + case kDNSType_MR: + case kDNSType_PTR: + case kDNSType_NSAP_PTR: + case kDNSType_DNAME: return DomainNameHashValue(&rdb->name); + + case kDNSType_SOA: return rdb->soa.serial + + rdb->soa.refresh + + rdb->soa.retry + + rdb->soa.expire + + rdb->soa.min + + DomainNameHashValue(&rdb->soa.mname) + + DomainNameHashValue(&rdb->soa.rname); + + case kDNSType_MX: + case kDNSType_AFSDB: + case kDNSType_RT: + case kDNSType_KX: return DomainNameHashValue(&rdb->mx.exchange); + + case kDNSType_MINFO: + case kDNSType_RP: return DomainNameHashValue(&rdb->rp.mbox) + DomainNameHashValue(&rdb->rp.txt); + + case kDNSType_PX: return DomainNameHashValue(&rdb->px.map822) + DomainNameHashValue(&rdb->px.mapx400); + + case kDNSType_SRV: return DomainNameHashValue(&rdb->srv.target); + + case kDNSType_OPT: return 0; // OPT is a pseudo-RR container structure; makes no sense to compare + + case kDNSType_NSEC: { + int dlen; + dlen = DomainNameLength((domainname *)rdb->data); + sum = DomainNameHashValue((domainname *)rdb->data); + ptr += dlen; + len -= dlen; + /* FALLTHROUGH */ + } + + default: + { + int i; + for (i=0; i+1 < len; i+=2) + { + sum += (((mDNSu32)(ptr[i])) << 8) | ptr[i+1]; + sum = (sum<<3) | (sum>>29); + } + if (i < len) + { + sum += ((mDNSu32)(ptr[i])) << 8; + } + return(sum); + } + } +} // r1 has to be a full ResourceRecord including rrtype and rdlength // r2 is just a bare RDataBody, which MUST be the same rrtype and rdlength as r1 mDNSexport mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2, DomainNameComparisonFn *samename) - { - const RDataBody2 *const b1 = (RDataBody2 *)r1->rdata->u.data; - const RDataBody2 *const b2 = (RDataBody2 *)r2; - switch(r1->rrtype) - { - case kDNSType_NS: - case kDNSType_CNAME: - case kDNSType_PTR: - case kDNSType_DNAME:return(SameDomainName(&b1->name, &b2->name)); - - case kDNSType_SOA: return(mDNSBool)( b1->soa.serial == b2->soa.serial && - b1->soa.refresh == b2->soa.refresh && - b1->soa.retry == b2->soa.retry && - b1->soa.expire == b2->soa.expire && - b1->soa.min == b2->soa.min && - samename(&b1->soa.mname, &b2->soa.mname) && - samename(&b1->soa.rname, &b2->soa.rname)); - - case kDNSType_MX: - case kDNSType_AFSDB: - case kDNSType_RT: - case kDNSType_KX: return(mDNSBool)( b1->mx.preference == b2->mx.preference && - samename(&b1->mx.exchange, &b2->mx.exchange)); - - case kDNSType_RP: return(mDNSBool)( samename(&b1->rp.mbox, &b2->rp.mbox) && - samename(&b1->rp.txt, &b2->rp.txt)); - - case kDNSType_PX: return(mDNSBool)( b1->px.preference == b2->px.preference && - samename(&b1->px.map822, &b2->px.map822) && - samename(&b1->px.mapx400, &b2->px.mapx400)); - - case kDNSType_SRV: return(mDNSBool)( b1->srv.priority == b2->srv.priority && - b1->srv.weight == b2->srv.weight && - mDNSSameIPPort(b1->srv.port, b2->srv.port) && - samename(&b1->srv.target, &b2->srv.target)); - - case kDNSType_OPT: return mDNSfalse; // OPT is a pseudo-RR container structure; makes no sense to compare - - case kDNSType_NSEC: return(mDNSPlatformMemSame(b1->data, b2->data, sizeof(rdataNSEC))); - - default: return(mDNSPlatformMemSame(b1->data, b2->data, r1->rdlength)); - } - } +{ + const RDataBody2 *const b1 = (RDataBody2 *)r1->rdata->u.data; + const RDataBody2 *const b2 = (RDataBody2 *)r2; + switch(r1->rrtype) + { + case kDNSType_NS: + case kDNSType_MD: + case kDNSType_MF: + case kDNSType_CNAME: + case kDNSType_MB: + case kDNSType_MG: + case kDNSType_MR: + case kDNSType_PTR: + case kDNSType_NSAP_PTR: + case kDNSType_DNAME: return(SameDomainName(&b1->name, &b2->name)); + + case kDNSType_SOA: return (mDNSBool)( b1->soa.serial == b2->soa.serial && + b1->soa.refresh == b2->soa.refresh && + b1->soa.retry == b2->soa.retry && + b1->soa.expire == b2->soa.expire && + b1->soa.min == b2->soa.min && + samename(&b1->soa.mname, &b2->soa.mname) && + samename(&b1->soa.rname, &b2->soa.rname)); + + case kDNSType_MX: + case kDNSType_AFSDB: + case kDNSType_RT: + case kDNSType_KX: return (mDNSBool)( b1->mx.preference == b2->mx.preference && + samename(&b1->mx.exchange, &b2->mx.exchange)); + + case kDNSType_MINFO: + case kDNSType_RP: return (mDNSBool)( samename(&b1->rp.mbox, &b2->rp.mbox) && + samename(&b1->rp.txt, &b2->rp.txt)); + + case kDNSType_PX: return (mDNSBool)( b1->px.preference == b2->px.preference && + samename(&b1->px.map822, &b2->px.map822) && + samename(&b1->px.mapx400, &b2->px.mapx400)); + + case kDNSType_SRV: return (mDNSBool)( b1->srv.priority == b2->srv.priority && + b1->srv.weight == b2->srv.weight && + mDNSSameIPPort(b1->srv.port, b2->srv.port) && + samename(&b1->srv.target, &b2->srv.target)); + + case kDNSType_OPT: return mDNSfalse; // OPT is a pseudo-RR container structure; makes no sense to compare + case kDNSType_NSEC: { + // If the "nxt" name changes in case, we want to delete the old + // and store just the new one. If the caller passes in SameDomainCS for "samename", + // we would return "false" when the only change between the two rdata is the case + // change in "nxt". + // + // Note: rdlength of both the RData are same (ensured by the caller) and hence we can + // use just r1->rdlength below + + int dlen1 = DomainNameLength((domainname *)b1->data); + int dlen2 = DomainNameLength((domainname *)b2->data); + return (mDNSBool)(dlen1 == dlen2 && + samename((domainname *)b1->data, (domainname *)b2->data) && + mDNSPlatformMemSame(b1->data + dlen1, b2->data + dlen2, r1->rdlength - dlen1)); + } + + default: return(mDNSPlatformMemSame(b1->data, b2->data, r1->rdlength)); + } +} + +// Don't call this function if the resource record is not NSEC. It will return false +// which means that the type does not exist. +mDNSexport mDNSBool RRAssertsExistence(const ResourceRecord *const rr, mDNSu16 type) +{ + const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; + mDNSu8 *nsec = (mDNSu8 *)rdb->data; + int len, bitmaplen; + int win, wlen; + mDNSu8 *bmap; + int wintype; + + if (rr->rrtype != kDNSType_NSEC) return mDNSfalse; + + len = DomainNameLength((domainname *)nsec); + + // The window that this type belongs to. NSEC has 256 windows that + // comprises of 256 types. + wintype = type >> 8; + + bitmaplen = rr->rdlength - len; + bmap = nsec + len; + while (bitmaplen > 0) + { + if (bitmaplen < 3) + { + LogInfo("RRAssertsExistence: malformed nsec, bitmaplen %d short", bitmaplen); + return mDNSfalse; + } + + win = *bmap++; + wlen = *bmap++; + bitmaplen -= 2; + if (bitmaplen < wlen || wlen < 1 || wlen > 32) + { + LogInfo("RRAssertsExistence: malformed nsec, bitmaplen %d wlen %d, win %d", bitmaplen, wlen, win); + return mDNSfalse; + } + if (win < 0 || win >= 256) + { + LogInfo("RRAssertsExistence: malformed nsec, wlen %d", wlen); + return mDNSfalse; + } + if (win == wintype) + { + // First byte in the window serves 0 to 7, the next one serves 8 to 15 and so on. + // Calculate the right byte offset first. + int boff = (type & 0xff ) >> 3; + if (wlen <= boff) + return mDNSfalse; + // The last three bits values 0 to 7 corresponds to bit positions + // within the byte. + return (bmap[boff] & (0x80 >> (type & 7))); + } + else + { + // If the windows are ordered, then we could check to see + // if wintype > win and then return early. + bmap += wlen; + bitmaplen -= wlen; + } + } + return mDNSfalse; +} + +// Don't call this function if the resource record is not NSEC. It will return false +// which means that the type exists. +mDNSexport mDNSBool RRAssertsNonexistence(const ResourceRecord *const rr, mDNSu16 type) +{ + if (rr->rrtype != kDNSType_NSEC) return mDNSfalse; + + return !RRAssertsExistence(rr, type); +} + +// Checks whether the RRSIG or NSEC record answers the question "q". +mDNSlocal mDNSBool DNSSECRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q, mDNSBool *checkType) +{ + *checkType = mDNStrue; + + // This function is called for all questions and as long as the type matches, + // return true. For the types (RRSIG and NSEC) that are specifically checked in + // this function, returning true still holds good. + if (q->qtype == rr->rrtype) + return mDNStrue; + + // If we are validating a response using DNSSEC, we might already have the records + // for the "q->qtype" in the cache but we issued a query with DO bit set + // to get the RRSIGs e.g., if you have two questions one of which does not require + // DNSSEC validation. When the RRSIG is added to the cache, we need to deliver + // the response to the question. The RRSIG type won't match the q->qtype and hence + // we need to bypass the check in that case. + if (rr->rrtype == kDNSType_RRSIG && q->ValidatingResponse) + { + const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; + rdataRRSig *rrsig = (rdataRRSig *)rdb->data; + if (q->qtype == kDNSType_CNAME || swap16(rrsig->typeCovered) != q->qtype) + { + debugf("DNSSECRecordAnswersQuestion: Question %##s (%s) did not match record %##s (RRSIG)", q->qname.c, + DNSTypeName(q->qtype), rr->name->c); + return mDNSfalse; + } + LogInfo("DNSSECRecordAnswersQuestion: Question %##s (%s) matched record %##s (RRSIG)", q->qname.c, + DNSTypeName(q->qtype), rr->name->c); + *checkType = mDNSfalse; + return mDNStrue; + } + // If the NSEC record asserts the non-existence of a name looked up by the question, we would + // typically answer that e.g., the bitmap asserts that q->qtype does not exist. If we have + // to prove the non-existence as required by ValidatingResponse and ValidationRequired question, + // then we should not answer that as it may not be the right one always. We may need more than + // one NSEC to prove the non-existence. + if (rr->rrtype == kDNSType_NSEC && DNSSECQuestion(q)) + { + debugf("DNSSECRecordAnswersQuestion: Question %##s (%s) matched record %##s (NSEC)", q->qname.c, + DNSTypeName(q->qtype), rr->name->c); + return mDNSfalse; + } + return mDNStrue; +} // ResourceRecordAnswersQuestion returns mDNStrue if the given resource record is a valid answer to the given question. // SameNameRecordAnswersQuestion is the same, except it skips the expensive SameDomainName() call. @@ -1215,57 +1552,54 @@ mDNSexport mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBod // SameDomainName() call because that's precisely the time when it's most expensive and least useful. mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) - { - // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records - // are handled in LocalOnlyRecordAnswersQuestion - if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P)) - { - LogMsg("SameNameRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID); - return mDNSfalse; - } - if (rr->InterfaceID && - q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && - rr->InterfaceID != q->InterfaceID) return(mDNSfalse); - - // Resource record received via unicast, the DNSServer entries should match ? - if (!rr->InterfaceID && rr->rDNSServer != q->qDNSServer) return(mDNSfalse); - - // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question - if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse); - - // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class. - if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse); - if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); - - return(mDNStrue); - } +{ + mDNSBool checkType = mDNStrue; + + // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records + // are handled in LocalOnlyRecordAnswersQuestion + if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P)) + { + LogMsg("SameNameRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID); + return mDNSfalse; + } + if (QuerySuppressed(q)) + return mDNSfalse; + + if (rr->InterfaceID && + q->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && + rr->InterfaceID != q->InterfaceID) return(mDNSfalse); + + // Resource record received via unicast, the resolver group ID should match ? + if (!rr->InterfaceID) + { + mDNSu16 idr = (rr->rDNSServer ? rr->rDNSServer->resGroupID : 0); + mDNSu16 idq = (q->qDNSServer ? q->qDNSServer->resGroupID : 0); + if (idr != idq) return(mDNSfalse); + if (!DNSSECRecordAnswersQuestion(rr, q, &checkType)) return mDNSfalse; + } + + // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question + if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse); + + // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class. + if (checkType && !RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse); + if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); -mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) - { - // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records - // are handled in LocalOnlyRecordAnswersQuestion - if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P)) - { - LogMsg("ResourceRecordAnswersQuestion: ERROR!! called with LocalOnly/P2P ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID); - return mDNSfalse; - } - - if (rr->InterfaceID && - q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && - rr->InterfaceID != q->InterfaceID) return(mDNSfalse); - - // Resource record received via unicast, the DNSServer entries should match ? - if (!rr->InterfaceID && rr->rDNSServer != q->qDNSServer) return(mDNSfalse); +#if APPLE_OSX_mDNSResponder + if (!mDNSPlatformValidRecordForQuestion(rr, q)) + return mDNSfalse; +#endif // APPLE_OSX_mDNSResponder - // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question. - if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse); + return(mDNStrue); +} - // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class. - if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse); - if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); +mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) +{ + if (!SameNameRecordAnswersQuestion(rr, q)) + return mDNSfalse; - return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); - } + return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); +} // We have a separate function to handle LocalOnly AuthRecords because they can be created with // a valid InterfaceID (e.g., scoped /etc/hosts) and can be used to answer unicast questions unlike @@ -1277,97 +1611,102 @@ mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr // they walk the hash table to answer LocalOnly/P2P questions // mDNSexport mDNSBool LocalOnlyRecordAnswersQuestion(AuthRecord *const ar, const DNSQuestion *const q) - { - ResourceRecord *rr = &ar->resrec; - - // mDNSInterface_Any questions can be answered with LocalOnly/P2P records in this function. AuthRecord_Any - // records are handled in ResourceRecordAnswersQuestion/SameNameRecordAnswersQuestion - if (RRAny(ar)) - { - LogMsg("LocalOnlyRecordAnswersQuestion: ERROR!! called with regular AuthRecordAny %##s", rr->name->c); - return mDNSfalse; - } - - // Questions with mDNSInterface_LocalOnly InterfaceID should be answered with all resource records that are - // *local* to the machine. These include resource records that have InterfaceID set to mDNSInterface_LocalOnly, - // mDNSInterface_Any and any other real InterfaceID. Hence, LocalOnly questions should not be checked against - // the InterfaceID in the resource record. - // - // mDNSInterface_Unicast does not indicate any scope and hence treat them like mDNSInterface_Any. - - if (rr->InterfaceID && - q->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && q->InterfaceID != mDNSInterface_Unicast && - rr->InterfaceID != q->InterfaceID) return(mDNSfalse); - - // Entries in /etc/hosts are added as LocalOnly resource records. The LocalOnly resource records - // may have a scope e.g., fe80::1%en0. The question may be scoped or not: the InterfaceID may be set - // to mDNSInterface_Any, mDNSInterface_LocalOnly or a real InterfaceID (scoped). - // - // 1) Question: Any, LocalOnly Record: no scope. This question should be answered with this record. - // - // 2) Question: Any, LocalOnly Record: scoped. This question should be answered with the record because - // traditionally applications never specify scope e.g., getaddrinfo, but need to be able - // to get to /etc/hosts entries. - // - // 3) Question: Scoped (LocalOnly or InterfaceID), LocalOnly Record: no scope. This is the inverse of (2). - // If we register a LocalOnly record, we need to answer a LocalOnly question. If the /etc/hosts has a - // non scoped entry, it may not make sense to answer a scoped question. But we can't tell these two - // cases apart. As we currently answer LocalOnly question with LocalOnly record, we continue to do so. - // - // 4) Question: Scoped (LocalOnly or InterfaceID), LocalOnly Record: scoped. LocalOnly questions should be - // answered with any resource record where as if it has a valid InterfaceID, the scope should match. - // - // (1) and (2) is bypassed because we check for a non-NULL InterfaceID above. For (3), the InterfaceID is NULL - // and hence bypassed above. For (4) we bypassed LocalOnly questions and checked the scope of the record - // against the question. - // - // For P2P, InterfaceIDs of the question and the record should match. - - // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question. - // LocalOnly authoritative answers are exempt. LocalOnly authoritative answers are used for /etc/host entries. - // We don't want a local process to be able to create a fake LocalOnly address record for "www.bigbank.com" which would then - // cause other applications (e.g. Safari) to connect to the wrong address. The rpc to register records filters out records - // with names that don't end in local and have mDNSInterface_LocalOnly set. - // - // Note: The check is bypassed for LocalOnly and for P2P it is not needed as only .local records are registered and for - // a question to match its names, it also has to end in .local and that question can't be a unicast question (See - // Question_uDNS macro and its usage). As P2P does not enforce .local only registrations we still make this check - // and also makes it future proof. - - if (ar->ARType != AuthRecordLocalOnly && rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse); - - // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class. - if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse); - if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); - - return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); - } +{ + ResourceRecord *rr = &ar->resrec; + + // mDNSInterface_Any questions can be answered with LocalOnly/P2P records in this function. AuthRecord_Any + // records are handled in ResourceRecordAnswersQuestion/SameNameRecordAnswersQuestion + if (RRAny(ar)) + { + LogMsg("LocalOnlyRecordAnswersQuestion: ERROR!! called with regular AuthRecordAny %##s", rr->name->c); + return mDNSfalse; + } + + // Questions with mDNSInterface_LocalOnly InterfaceID should be answered with all resource records that are + // *local* to the machine. These include resource records that have InterfaceID set to mDNSInterface_LocalOnly, + // mDNSInterface_Any and any other real InterfaceID. Hence, LocalOnly questions should not be checked against + // the InterfaceID in the resource record. + // + // mDNSInterface_Unicast does not indicate any scope and hence treat them like mDNSInterface_Any. + + if (rr->InterfaceID && + q->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && q->InterfaceID != mDNSInterface_Unicast && + rr->InterfaceID != q->InterfaceID) return(mDNSfalse); + + // Entries in /etc/hosts are added as LocalOnly resource records. The LocalOnly resource records + // may have a scope e.g., fe80::1%en0. The question may be scoped or not: the InterfaceID may be set + // to mDNSInterface_Any, mDNSInterface_LocalOnly or a real InterfaceID (scoped). + // + // 1) Question: Any, LocalOnly Record: no scope. This question should be answered with this record. + // + // 2) Question: Any, LocalOnly Record: scoped. This question should be answered with the record because + // traditionally applications never specify scope e.g., getaddrinfo, but need to be able + // to get to /etc/hosts entries. + // + // 3) Question: Scoped (LocalOnly or InterfaceID), LocalOnly Record: no scope. This is the inverse of (2). + // If we register a LocalOnly record, we need to answer a LocalOnly question. If the /etc/hosts has a + // non scoped entry, it may not make sense to answer a scoped question. But we can't tell these two + // cases apart. As we currently answer LocalOnly question with LocalOnly record, we continue to do so. + // + // 4) Question: Scoped (LocalOnly or InterfaceID), LocalOnly Record: scoped. LocalOnly questions should be + // answered with any resource record where as if it has a valid InterfaceID, the scope should match. + // + // (1) and (2) is bypassed because we check for a non-NULL InterfaceID above. For (3), the InterfaceID is NULL + // and hence bypassed above. For (4) we bypassed LocalOnly questions and checked the scope of the record + // against the question. + // + // For P2P, InterfaceIDs of the question and the record should match. + + // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question. + // LocalOnly authoritative answers are exempt. LocalOnly authoritative answers are used for /etc/host entries. + // We don't want a local process to be able to create a fake LocalOnly address record for "www.bigbank.com" which would then + // cause other applications (e.g. Safari) to connect to the wrong address. The rpc to register records filters out records + // with names that don't end in local and have mDNSInterface_LocalOnly set. + // + // Note: The check is bypassed for LocalOnly and for P2P it is not needed as only .local records are registered and for + // a question to match its names, it also has to end in .local and that question can't be a unicast question (See + // Question_uDNS macro and its usage). As P2P does not enforce .local only registrations we still make this check + // and also makes it future proof. + + if (ar->ARType != AuthRecordLocalOnly && rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse); + + // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class. + if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse); + if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); + + return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); +} mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) - { - // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records - // are handled in LocalOnlyRecordAnswersQuestion - if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P)) - { - LogMsg("AnyTypeRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID); - return mDNSfalse; - } - if (rr->InterfaceID && - q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && - rr->InterfaceID != q->InterfaceID) return(mDNSfalse); - - // Resource record received via unicast, the DNSServer entries should match ? - // Note that Auth Records are normally setup with NULL InterfaceID and - // both the DNSServers are assumed to be NULL in that case - if (!rr->InterfaceID && rr->rDNSServer != q->qDNSServer) return(mDNSfalse); - - // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question - if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse); - - if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); - - return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); - } +{ + // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records + // are handled in LocalOnlyRecordAnswersQuestion + if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P)) + { + LogMsg("AnyTypeRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID); + return mDNSfalse; + } + if (rr->InterfaceID && + q->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && + rr->InterfaceID != q->InterfaceID) return(mDNSfalse); + + // Resource record received via unicast, the resolver group ID should match ? + // Note that Auth Records are normally setup with NULL InterfaceID and + // both the DNSServers are assumed to be NULL in that case + if (!rr->InterfaceID) + { + mDNSu16 idr = (rr->rDNSServer ? rr->rDNSServer->resGroupID : 0); + mDNSu16 idq = (q->qDNSServer ? q->qDNSServer->resGroupID : 0); + if (idr != idq) return(mDNSfalse); + } + + // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question + if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse); + + if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); + + return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); +} // This is called with both unicast resource record and multicast resource record. The question that // received the unicast response could be the regular unicast response from a DNS server or a response @@ -1375,134 +1714,139 @@ mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const ResourceRecord *const rr, // question and the resource record because the resource record is not completely initialized in // mDNSCoreReceiveResponse when this function is called. mDNSexport mDNSBool ResourceRecordAnswersUnicastResponse(const ResourceRecord *const rr, const DNSQuestion *const q) - { - // For resource records created using multicast, the InterfaceIDs have to match - if (rr->InterfaceID && - q->InterfaceID && rr->InterfaceID != q->InterfaceID) return(mDNSfalse); +{ + mDNSBool checkType = mDNStrue; + + if (QuerySuppressed(q)) + return mDNSfalse; - // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question. - if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse); + // For resource records created using multicast, the InterfaceIDs have to match + if (rr->InterfaceID && + q->InterfaceID && rr->InterfaceID != q->InterfaceID) return(mDNSfalse); - // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class. - if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse); + // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question. + if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse); - if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); + if (!DNSSECRecordAnswersQuestion(rr, q, &checkType)) return mDNSfalse; - return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); - } + // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class. + if (checkType && !RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse); + + if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); + + return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); +} mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate) - { - const RDataBody2 *const rd = (RDataBody2 *)rr->rdata->u.data; - const domainname *const name = estimate ? rr->name : mDNSNULL; - if (rr->rrclass == kDNSQClass_ANY) return(rr->rdlength); // Used in update packets to mean "Delete An RRset" (RFC 2136) - else switch (rr->rrtype) - { - case kDNSType_A: return(sizeof(rd->ipv4)); - - case kDNSType_NS: - case kDNSType_CNAME: - case kDNSType_PTR: - case kDNSType_DNAME:return(CompressedDomainNameLength(&rd->name, name)); - - case kDNSType_SOA: return(mDNSu16)(CompressedDomainNameLength(&rd->soa.mname, name) + - CompressedDomainNameLength(&rd->soa.rname, name) + - 5 * sizeof(mDNSOpaque32)); - - case kDNSType_NULL: - case kDNSType_TSIG: - case kDNSType_TXT: - case kDNSType_X25: - case kDNSType_ISDN: - case kDNSType_LOC: - case kDNSType_DHCID:return(rr->rdlength); // Not self-describing, so have to just trust rdlength - - case kDNSType_HINFO:return(mDNSu16)(2 + (int)rd->data[0] + (int)rd->data[1 + (int)rd->data[0]]); - - case kDNSType_MX: - case kDNSType_AFSDB: - case kDNSType_RT: - case kDNSType_KX: return(mDNSu16)(2 + CompressedDomainNameLength(&rd->mx.exchange, name)); - - case kDNSType_RP: return(mDNSu16)(CompressedDomainNameLength(&rd->rp.mbox, name) + - CompressedDomainNameLength(&rd->rp.txt, name)); - - case kDNSType_PX: return(mDNSu16)(2 + CompressedDomainNameLength(&rd->px.map822, name) + - CompressedDomainNameLength(&rd->px.mapx400, name)); - - case kDNSType_AAAA: return(sizeof(rd->ipv6)); - - case kDNSType_SRV: return(mDNSu16)(6 + CompressedDomainNameLength(&rd->srv.target, name)); - - case kDNSType_OPT: return(rr->rdlength); - - case kDNSType_NSEC: { - int i; - for (i=sizeof(rdataNSEC); i>0; i--) if (rd->nsec.bitmap[i-1]) break; - // For our simplified use of NSEC synthetic records: - // nextname is always the record's own name, - // and if we have at least one record type that exists, - // - the block number is always 0, - // - the count byte is a value in the range 1-32, - // - followed by the 1-32 data bytes - return(mDNSu16)((estimate ? 2 : DomainNameLength(rr->name)) + (i ? (2 + i) : 0)); - } - - default: debugf("Warning! Don't know how to get length of resource type %d", rr->rrtype); - return(rr->rdlength); - } - } +{ + const RDataBody2 *const rd = (RDataBody2 *)rr->rdata->u.data; + const domainname *const name = estimate ? rr->name : mDNSNULL; + if (rr->rrclass == kDNSQClass_ANY) return(rr->rdlength); // Used in update packets to mean "Delete An RRset" (RFC 2136) + else switch (rr->rrtype) + { + case kDNSType_A: return(sizeof(rd->ipv4)); + + case kDNSType_NS: + case kDNSType_CNAME: + case kDNSType_PTR: + case kDNSType_DNAME: return(CompressedDomainNameLength(&rd->name, name)); + + case kDNSType_SOA: return (mDNSu16)(CompressedDomainNameLength(&rd->soa.mname, name) + + CompressedDomainNameLength(&rd->soa.rname, name) + + 5 * sizeof(mDNSOpaque32)); + + case kDNSType_NULL: + case kDNSType_TSIG: + case kDNSType_TXT: + case kDNSType_X25: + case kDNSType_ISDN: + case kDNSType_LOC: + case kDNSType_DHCID: return(rr->rdlength); // Not self-describing, so have to just trust rdlength + + case kDNSType_HINFO: return (mDNSu16)(2 + (int)rd->data[0] + (int)rd->data[1 + (int)rd->data[0]]); + + case kDNSType_MX: + case kDNSType_AFSDB: + case kDNSType_RT: + case kDNSType_KX: return (mDNSu16)(2 + CompressedDomainNameLength(&rd->mx.exchange, name)); + + case kDNSType_RP: return (mDNSu16)(CompressedDomainNameLength(&rd->rp.mbox, name) + + CompressedDomainNameLength(&rd->rp.txt, name)); + + case kDNSType_PX: return (mDNSu16)(2 + CompressedDomainNameLength(&rd->px.map822, name) + + CompressedDomainNameLength(&rd->px.mapx400, name)); + + case kDNSType_AAAA: return(sizeof(rd->ipv6)); + + case kDNSType_SRV: return (mDNSu16)(6 + CompressedDomainNameLength(&rd->srv.target, name)); + + case kDNSType_OPT: return(rr->rdlength); + + case kDNSType_NSEC: { + domainname *next = (domainname *)rd->data; + int dlen = DomainNameLength(next); + // + if (UNICAST_NSEC(rr)) + return (mDNSu16)(CompressedDomainNameLength(next, name) + rr->rdlength - dlen); + else + return (mDNSu16)((estimate ? 2 : dlen) + rr->rdlength - dlen); + } + + default: debugf("Warning! Don't know how to get length of resource type %d", rr->rrtype); + return(rr->rdlength); + } +} // When a local client registers (or updates) a record, we use this routine to do some simple validation checks // to help reduce the risk of bogus malformed data on the network mDNSexport mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd) - { - mDNSu16 len; - - switch(rrtype) - { - case kDNSType_A: return(rdlength == sizeof(mDNSv4Addr)); - - case kDNSType_NS: // Same as PTR - case kDNSType_MD: // Same as PTR - case kDNSType_MF: // Same as PTR - case kDNSType_CNAME:// Same as PTR - //case kDNSType_SOA not checked - case kDNSType_MB: // Same as PTR - case kDNSType_MG: // Same as PTR - case kDNSType_MR: // Same as PTR - //case kDNSType_NULL not checked (no specified format, so always valid) - //case kDNSType_WKS not checked - case kDNSType_PTR: len = DomainNameLengthLimit(&rd->u.name, rd->u.data + rdlength); - return(len <= MAX_DOMAIN_NAME && rdlength == len); - - case kDNSType_HINFO:// Same as TXT (roughly) - case kDNSType_MINFO:// Same as TXT (roughly) - case kDNSType_TXT: if (!rdlength) return(mDNSfalse); // TXT record has to be at least one byte (RFC 1035) - { - const mDNSu8 *ptr = rd->u.txt.c; - const mDNSu8 *end = rd->u.txt.c + rdlength; - while (ptr < end) ptr += 1 + ptr[0]; - return (ptr == end); - } - - case kDNSType_AAAA: return(rdlength == sizeof(mDNSv6Addr)); - - case kDNSType_MX: // Must be at least two-byte preference, plus domainname - // Call to DomainNameLengthLimit() implicitly enforces both requirements for us - len = DomainNameLengthLimit(&rd->u.mx.exchange, rd->u.data + rdlength); - return(len <= MAX_DOMAIN_NAME && rdlength == 2+len); - - case kDNSType_SRV: // Must be at least priority+weight+port, plus domainname - // Call to DomainNameLengthLimit() implicitly enforces both requirements for us - len = DomainNameLengthLimit(&rd->u.srv.target, rd->u.data + rdlength); - return(len <= MAX_DOMAIN_NAME && rdlength == 6+len); - - //case kDNSType_NSEC not checked - - default: return(mDNStrue); // Allow all other types without checking - } - } +{ + mDNSu16 len; + + switch(rrtype) + { + case kDNSType_A: return(rdlength == sizeof(mDNSv4Addr)); + + case kDNSType_NS: // Same as PTR + case kDNSType_MD: // Same as PTR + case kDNSType_MF: // Same as PTR + case kDNSType_CNAME: // Same as PTR + //case kDNSType_SOA not checked + case kDNSType_MB: // Same as PTR + case kDNSType_MG: // Same as PTR + case kDNSType_MR: // Same as PTR + //case kDNSType_NULL not checked (no specified format, so always valid) + //case kDNSType_WKS not checked + case kDNSType_PTR: len = DomainNameLengthLimit(&rd->u.name, rd->u.data + rdlength); + return(len <= MAX_DOMAIN_NAME && rdlength == len); + + case kDNSType_HINFO: // Same as TXT (roughly) + case kDNSType_MINFO: // Same as TXT (roughly) + case kDNSType_TXT: if (!rdlength) return(mDNSfalse); // TXT record has to be at least one byte (RFC 1035) + { + const mDNSu8 *ptr = rd->u.txt.c; + const mDNSu8 *end = rd->u.txt.c + rdlength; + while (ptr < end) ptr += 1 + ptr[0]; + return (ptr == end); + } + + case kDNSType_AAAA: return(rdlength == sizeof(mDNSv6Addr)); + + case kDNSType_MX: // Must be at least two-byte preference, plus domainname + // Call to DomainNameLengthLimit() implicitly enforces both requirements for us + len = DomainNameLengthLimit(&rd->u.mx.exchange, rd->u.data + rdlength); + return(len <= MAX_DOMAIN_NAME && rdlength == 2+len); + + case kDNSType_SRV: // Must be at least priority+weight+port, plus domainname + // Call to DomainNameLengthLimit() implicitly enforces both requirements for us + len = DomainNameLengthLimit(&rd->u.srv.target, rd->u.data + rdlength); + return(len <= MAX_DOMAIN_NAME && rdlength == 6+len); + + //case kDNSType_NSEC not checked + + default: return(mDNStrue); // Allow all other types without checking + } +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -1511,56 +1855,56 @@ mDNSexport mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, #endif mDNSexport void InitializeDNSMessage(DNSMessageHeader *h, mDNSOpaque16 id, mDNSOpaque16 flags) - { - h->id = id; - h->flags = flags; - h->numQuestions = 0; - h->numAnswers = 0; - h->numAuthorities = 0; - h->numAdditionals = 0; - } +{ + h->id = id; + h->flags = flags; + h->numQuestions = 0; + h->numAnswers = 0; + h->numAuthorities = 0; + h->numAdditionals = 0; +} mDNSexport const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const mDNSu8 *const end, const mDNSu8 *const domname) - { - const mDNSu8 *result = end - *domname - 1; - - if (*domname == 0) return(mDNSNULL); // There's no point trying to match just the root label - - // This loop examines each possible starting position in packet, starting end of the packet and working backwards - while (result >= base) - { - // If the length byte and first character of the label match, then check further to see - // if this location in the packet will yield a useful name compression pointer. - if (result[0] == domname[0] && result[1] == domname[1]) - { - const mDNSu8 *name = domname; - const mDNSu8 *targ = result; - while (targ + *name < end) - { - // First see if this label matches - int i; - const mDNSu8 *pointertarget; - for (i=0; i <= *name; i++) if (targ[i] != name[i]) break; - if (i <= *name) break; // If label did not match, bail out - targ += 1 + *name; // Else, did match, so advance target pointer - name += 1 + *name; // and proceed to check next label - if (*name == 0 && *targ == 0) return(result); // If no more labels, we found a match! - if (*name == 0) break; // If no more labels to match, we failed, so bail out - - // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches - if (targ[0] < 0x40) continue; // If length value, continue to check next label - if (targ[0] < 0xC0) break; // If 40-BF, not valid - if (targ+1 >= end) break; // Second byte not present! - pointertarget = base + (((mDNSu16)(targ[0] & 0x3F)) << 8) + targ[1]; - if (targ < pointertarget) break; // Pointertarget must point *backwards* in the packet - if (pointertarget[0] >= 0x40) break; // Pointertarget must point to a valid length byte - targ = pointertarget; - } - } - result--; // We failed to match at this search position, so back up the tentative result pointer and try again - } - return(mDNSNULL); - } +{ + const mDNSu8 *result = end - *domname - 1; + + if (*domname == 0) return(mDNSNULL); // There's no point trying to match just the root label + + // This loop examines each possible starting position in packet, starting end of the packet and working backwards + while (result >= base) + { + // If the length byte and first character of the label match, then check further to see + // if this location in the packet will yield a useful name compression pointer. + if (result[0] == domname[0] && result[1] == domname[1]) + { + const mDNSu8 *name = domname; + const mDNSu8 *targ = result; + while (targ + *name < end) + { + // First see if this label matches + int i; + const mDNSu8 *pointertarget; + for (i=0; i <= *name; i++) if (targ[i] != name[i]) break; + if (i <= *name) break; // If label did not match, bail out + targ += 1 + *name; // Else, did match, so advance target pointer + name += 1 + *name; // and proceed to check next label + if (*name == 0 && *targ == 0) return(result); // If no more labels, we found a match! + if (*name == 0) break; // If no more labels to match, we failed, so bail out + + // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches + if (targ[0] < 0x40) continue; // If length value, continue to check next label + if (targ[0] < 0xC0) break; // If 40-BF, not valid + if (targ+1 >= end) break; // Second byte not present! + pointertarget = base + (((mDNSu16)(targ[0] & 0x3F)) << 8) + targ[1]; + if (targ < pointertarget) break; // Pointertarget must point *backwards* in the packet + if (pointertarget[0] >= 0x40) break; // Pointertarget must point to a valid length byte + targ = pointertarget; + } + } + result--; // We failed to match at this search position, so back up the tentative result pointer and try again + } + return(mDNSNULL); +} // Put a string of dot-separated labels as length-prefixed labels // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't) @@ -1570,433 +1914,511 @@ mDNSexport const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const // limit points to one byte past the end of the buffer that we must not overrun // domainname is the name to put mDNSexport mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg, - mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name) - { - const mDNSu8 *const base = (const mDNSu8 *)msg; - const mDNSu8 * np = name->c; - const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid - const mDNSu8 * pointer = mDNSNULL; - const mDNSu8 *const searchlimit = ptr; - - if (!ptr) { LogMsg("putDomainNameAsLabels %##s ptr is null", name->c); return(mDNSNULL); } - - if (!*np) // If just writing one-byte root label, make sure we have space for that - { - if (ptr >= limit) return(mDNSNULL); - } - else // else, loop through writing labels and/or a compression offset - { - do { - if (*np > MAX_DOMAIN_LABEL) - { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name->c); return(mDNSNULL); } - - // This check correctly allows for the final trailing root label: - // e.g. - // Suppose our domain name is exactly 256 bytes long, including the final trailing root label. - // Suppose np is now at name->c[249], and we're about to write our last non-null label ("local"). - // We know that max will be at name->c[256] - // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our - // six bytes, then exit the loop, write the final terminating root label, and the domain - // name we've written is exactly 256 bytes long, exactly at the correct legal limit. - // If the name is one byte longer, then we fail the "if" test below, and correctly bail out. - if (np + 1 + *np >= max) - { LogMsg("Malformed domain name %##s (more than 256 bytes)", name->c); return(mDNSNULL); } - - if (base) pointer = FindCompressionPointer(base, searchlimit, np); - if (pointer) // Use a compression pointer if we can - { - const mDNSu16 offset = (mDNSu16)(pointer - base); - if (ptr+2 > limit) return(mDNSNULL); // If we don't have two bytes of space left, give up - *ptr++ = (mDNSu8)(0xC0 | (offset >> 8)); - *ptr++ = (mDNSu8)( offset & 0xFF); - return(ptr); - } - else // Else copy one label and try again - { - int i; - mDNSu8 len = *np++; - // If we don't at least have enough space for this label *plus* a terminating zero on the end, give up - if (ptr + 1 + len >= limit) return(mDNSNULL); - *ptr++ = len; - for (i=0; ic; + const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid + const mDNSu8 * pointer = mDNSNULL; + const mDNSu8 *const searchlimit = ptr; + + if (!ptr) { LogMsg("putDomainNameAsLabels %##s ptr is null", name->c); return(mDNSNULL); } + + if (!*np) // If just writing one-byte root label, make sure we have space for that + { + if (ptr >= limit) return(mDNSNULL); + } + else // else, loop through writing labels and/or a compression offset + { + do { + if (*np > MAX_DOMAIN_LABEL) + { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name->c); return(mDNSNULL); } + + // This check correctly allows for the final trailing root label: + // e.g. + // Suppose our domain name is exactly 256 bytes long, including the final trailing root label. + // Suppose np is now at name->c[249], and we're about to write our last non-null label ("local"). + // We know that max will be at name->c[256] + // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our + // six bytes, then exit the loop, write the final terminating root label, and the domain + // name we've written is exactly 256 bytes long, exactly at the correct legal limit. + // If the name is one byte longer, then we fail the "if" test below, and correctly bail out. + if (np + 1 + *np >= max) + { LogMsg("Malformed domain name %##s (more than 256 bytes)", name->c); return(mDNSNULL); } + + if (base) pointer = FindCompressionPointer(base, searchlimit, np); + if (pointer) // Use a compression pointer if we can + { + const mDNSu16 offset = (mDNSu16)(pointer - base); + if (ptr+2 > limit) return(mDNSNULL); // If we don't have two bytes of space left, give up + *ptr++ = (mDNSu8)(0xC0 | (offset >> 8)); + *ptr++ = (mDNSu8)( offset & 0xFF); + return(ptr); + } + else // Else copy one label and try again + { + int i; + mDNSu8 len = *np++; + // If we don't at least have enough space for this label *plus* a terminating zero on the end, give up + if (ptr + 1 + len >= limit) return(mDNSNULL); + *ptr++ = len; + for (i=0; i> 8 ) & 0xFF); - ptr[1] = (mDNSu8)((val ) & 0xFF); - return ptr + sizeof(mDNSOpaque16); - } +{ + ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF); + ptr[1] = (mDNSu8)((val ) & 0xFF); + 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); - } - -// msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers) +{ + 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); +} + +// Copy the RDATA information. The actual in memory storage for the data might be bigger than what the rdlength +// says. Hence, the only way to copy out the data from a resource record is to use putRData. +// msg points to the message we're building (pass mDNSNULL for "msg" if we don't want to use compression pointers) mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const ResourceRecord *const rr) - { - const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; - switch (rr->rrtype) - { - case kDNSType_A: if (rr->rdlength != 4) - { debugf("putRData: Illegal length %d for kDNSType_A", rr->rdlength); return(mDNSNULL); } - if (ptr + 4 > limit) return(mDNSNULL); - *ptr++ = rdb->ipv4.b[0]; - *ptr++ = rdb->ipv4.b[1]; - *ptr++ = rdb->ipv4.b[2]; - *ptr++ = rdb->ipv4.b[3]; - return(ptr); - - case kDNSType_NS: - case kDNSType_CNAME: - case kDNSType_PTR: - case kDNSType_DNAME:return(putDomainNameAsLabels(msg, ptr, limit, &rdb->name)); - - case kDNSType_SOA: ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->soa.mname); - if (!ptr) return(mDNSNULL); - ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->soa.rname); - if (!ptr || ptr + 20 > limit) return(mDNSNULL); - ptr = putVal32(ptr, rdb->soa.serial); - ptr = putVal32(ptr, rdb->soa.refresh); - ptr = putVal32(ptr, rdb->soa.retry); - ptr = putVal32(ptr, rdb->soa.expire); - ptr = putVal32(ptr, rdb->soa.min); - return(ptr); - - case kDNSType_NULL: - case kDNSType_HINFO: - case kDNSType_TSIG: - case kDNSType_TXT: - case kDNSType_X25: - case kDNSType_ISDN: - case kDNSType_LOC: - case kDNSType_DHCID:if (ptr + rr->rdlength > limit) return(mDNSNULL); - mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength); - return(ptr + rr->rdlength); - - case kDNSType_MX: - case kDNSType_AFSDB: - case kDNSType_RT: - case kDNSType_KX: if (ptr + 3 > limit) return(mDNSNULL); - ptr = putVal16(ptr, rdb->mx.preference); - return(putDomainNameAsLabels(msg, ptr, limit, &rdb->mx.exchange)); - - case kDNSType_RP: ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->rp.mbox); - if (!ptr) return(mDNSNULL); - ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->rp.txt); - return(ptr); - - case kDNSType_PX: if (ptr + 5 > limit) return(mDNSNULL); - ptr = putVal16(ptr, rdb->px.preference); - ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->px.map822); - if (!ptr) return(mDNSNULL); - ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->px.mapx400); - return(ptr); - - case kDNSType_AAAA: if (rr->rdlength != sizeof(rdb->ipv6)) - { debugf("putRData: Illegal length %d for kDNSType_AAAA", rr->rdlength); return(mDNSNULL); } - if (ptr + sizeof(rdb->ipv6) > limit) return(mDNSNULL); - mDNSPlatformMemCopy(ptr, &rdb->ipv6, sizeof(rdb->ipv6)); - return(ptr + sizeof(rdb->ipv6)); - - case kDNSType_SRV: if (ptr + 7 > limit) return(mDNSNULL); - *ptr++ = (mDNSu8)(rdb->srv.priority >> 8); - *ptr++ = (mDNSu8)(rdb->srv.priority & 0xFF); - *ptr++ = (mDNSu8)(rdb->srv.weight >> 8); - *ptr++ = (mDNSu8)(rdb->srv.weight & 0xFF); - *ptr++ = rdb->srv.port.b[0]; - *ptr++ = rdb->srv.port.b[1]; - return(putDomainNameAsLabels(msg, ptr, limit, &rdb->srv.target)); - - case kDNSType_OPT: { - int len = 0; - const rdataOPT *opt; - const rdataOPT *const end = (const rdataOPT *)&rr->rdata->u.data[rr->rdlength]; - for (opt = &rr->rdata->u.opt[0]; opt < end; opt++) len += DNSOpt_Data_Space(opt); - if (ptr + len > limit) { LogMsg("ERROR: putOptRData - out of space"); return mDNSNULL; } - - for (opt = &rr->rdata->u.opt[0]; opt < end; opt++) - { - const int space = DNSOpt_Data_Space(opt); - ptr = putVal16(ptr, opt->opt); - ptr = putVal16(ptr, (mDNSu16)space - 4); - switch (opt->opt) - { - case kDNSOpt_LLQ: - ptr = putVal16(ptr, opt->u.llq.vers); - ptr = putVal16(ptr, opt->u.llq.llqOp); - ptr = putVal16(ptr, opt->u.llq.err); - mDNSPlatformMemCopy(ptr, opt->u.llq.id.b, 8); // 8-byte id - ptr += 8; - ptr = putVal32(ptr, opt->u.llq.llqlease); - break; - case kDNSOpt_Lease: - ptr = putVal32(ptr, opt->u.updatelease); - break; - case kDNSOpt_Owner: - *ptr++ = opt->u.owner.vers; - *ptr++ = opt->u.owner.seq; - mDNSPlatformMemCopy(ptr, opt->u.owner.HMAC.b, 6); // 6-byte Host identifier - ptr += 6; - if (space >= DNSOpt_OwnerData_ID_Wake_Space) - { - mDNSPlatformMemCopy(ptr, opt->u.owner.IMAC.b, 6); // 6-byte interface MAC - ptr += 6; - if (space > DNSOpt_OwnerData_ID_Wake_Space) - { - mDNSPlatformMemCopy(ptr, opt->u.owner.password.b, space - DNSOpt_OwnerData_ID_Wake_Space); - ptr += space - DNSOpt_OwnerData_ID_Wake_Space; - } - } - break; - } - } - return ptr; - } - - case kDNSType_NSEC: { - // For our simplified use of NSEC synthetic records: - // nextname is always the record's own name, - // the block number is always 0, - // the count byte is a value in the range 1-32, - // followed by the 1-32 data bytes - int i, j; - for (i=sizeof(rdataNSEC); i>0; i--) if (rdb->nsec.bitmap[i-1]) break; - ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name); - if (!ptr) return(mDNSNULL); - if (i) // Only put a block if at least one type exists for this name - { - if (ptr + 2 + i > limit) return(mDNSNULL); - *ptr++ = 0; - *ptr++ = (mDNSu8)i; - for (j=0; jnsec.bitmap[j]; - } - return ptr; - } - - default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr->rrtype); - if (ptr + rr->rdlength > limit) return(mDNSNULL); - mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength); - return(ptr + rr->rdlength); - } - } +{ + const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; + switch (rr->rrtype) + { + case kDNSType_A: if (rr->rdlength != 4) + { debugf("putRData: Illegal length %d for kDNSType_A", rr->rdlength); return(mDNSNULL); } + if (ptr + 4 > limit) return(mDNSNULL); + *ptr++ = rdb->ipv4.b[0]; + *ptr++ = rdb->ipv4.b[1]; + *ptr++ = rdb->ipv4.b[2]; + *ptr++ = rdb->ipv4.b[3]; + return(ptr); + + case kDNSType_NS: + case kDNSType_CNAME: + case kDNSType_PTR: + case kDNSType_DNAME: return(putDomainNameAsLabels(msg, ptr, limit, &rdb->name)); + + case kDNSType_SOA: ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->soa.mname); + if (!ptr) return(mDNSNULL); + ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->soa.rname); + if (!ptr || ptr + 20 > limit) return(mDNSNULL); + ptr = putVal32(ptr, rdb->soa.serial); + ptr = putVal32(ptr, rdb->soa.refresh); + ptr = putVal32(ptr, rdb->soa.retry); + ptr = putVal32(ptr, rdb->soa.expire); + ptr = putVal32(ptr, rdb->soa.min); + return(ptr); + + case kDNSType_NULL: + case kDNSType_HINFO: + case kDNSType_TSIG: + case kDNSType_TXT: + case kDNSType_X25: + case kDNSType_ISDN: + case kDNSType_LOC: + case kDNSType_DHCID: if (ptr + rr->rdlength > limit) return(mDNSNULL); + mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength); + return(ptr + rr->rdlength); + + case kDNSType_MX: + case kDNSType_AFSDB: + case kDNSType_RT: + case kDNSType_KX: if (ptr + 3 > limit) return(mDNSNULL); + ptr = putVal16(ptr, rdb->mx.preference); + return(putDomainNameAsLabels(msg, ptr, limit, &rdb->mx.exchange)); + + case kDNSType_RP: ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->rp.mbox); + if (!ptr) return(mDNSNULL); + ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->rp.txt); + return(ptr); + + case kDNSType_PX: if (ptr + 5 > limit) return(mDNSNULL); + ptr = putVal16(ptr, rdb->px.preference); + ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->px.map822); + if (!ptr) return(mDNSNULL); + ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->px.mapx400); + return(ptr); + + case kDNSType_AAAA: if (rr->rdlength != sizeof(rdb->ipv6)) + { debugf("putRData: Illegal length %d for kDNSType_AAAA", rr->rdlength); return(mDNSNULL); } + if (ptr + sizeof(rdb->ipv6) > limit) return(mDNSNULL); + mDNSPlatformMemCopy(ptr, &rdb->ipv6, sizeof(rdb->ipv6)); + return(ptr + sizeof(rdb->ipv6)); + + case kDNSType_SRV: if (ptr + 7 > limit) return(mDNSNULL); + *ptr++ = (mDNSu8)(rdb->srv.priority >> 8); + *ptr++ = (mDNSu8)(rdb->srv.priority & 0xFF); + *ptr++ = (mDNSu8)(rdb->srv.weight >> 8); + *ptr++ = (mDNSu8)(rdb->srv.weight & 0xFF); + *ptr++ = rdb->srv.port.b[0]; + *ptr++ = rdb->srv.port.b[1]; + return(putDomainNameAsLabels(msg, ptr, limit, &rdb->srv.target)); + + case kDNSType_OPT: { + int len = 0; + const rdataOPT *opt; + const rdataOPT *const end = (const rdataOPT *)&rr->rdata->u.data[rr->rdlength]; + for (opt = &rr->rdata->u.opt[0]; opt < end; opt++) len += DNSOpt_Data_Space(opt); + if (ptr + len > limit) { LogMsg("ERROR: putOptRData - out of space"); return mDNSNULL; } + + for (opt = &rr->rdata->u.opt[0]; opt < end; opt++) + { + const int space = DNSOpt_Data_Space(opt); + ptr = putVal16(ptr, opt->opt); + ptr = putVal16(ptr, (mDNSu16)space - 4); + switch (opt->opt) + { + case kDNSOpt_LLQ: + ptr = putVal16(ptr, opt->u.llq.vers); + ptr = putVal16(ptr, opt->u.llq.llqOp); + ptr = putVal16(ptr, opt->u.llq.err); + mDNSPlatformMemCopy(ptr, opt->u.llq.id.b, 8); // 8-byte id + ptr += 8; + ptr = putVal32(ptr, opt->u.llq.llqlease); + break; + case kDNSOpt_Lease: + ptr = putVal32(ptr, opt->u.updatelease); + break; + case kDNSOpt_Owner: + *ptr++ = opt->u.owner.vers; + *ptr++ = opt->u.owner.seq; + mDNSPlatformMemCopy(ptr, opt->u.owner.HMAC.b, 6); // 6-byte Host identifier + ptr += 6; + if (space >= DNSOpt_OwnerData_ID_Wake_Space) + { + mDNSPlatformMemCopy(ptr, opt->u.owner.IMAC.b, 6); // 6-byte interface MAC + ptr += 6; + if (space > DNSOpt_OwnerData_ID_Wake_Space) + { + mDNSPlatformMemCopy(ptr, opt->u.owner.password.b, space - DNSOpt_OwnerData_ID_Wake_Space); + ptr += space - DNSOpt_OwnerData_ID_Wake_Space; + } + } + break; + } + } + return ptr; + } + + case kDNSType_NSEC: { + // For NSEC records, rdlength represents the exact number of bytes + // of in memory storage. + int len = rr->rdlength; + mDNSu8 *nsec = (mDNSu8 *)rdb->data; + domainname *name = (domainname *)nsec; + int dlen; + + dlen = DomainNameLength(name); + len -= dlen; + nsec += dlen; + // This function is called when we are sending a NSEC record as part of mDNS, + // or to copy the data to any other buffer needed which could be a mDNS or uDNS + // NSEC record. The only time compression is used that when we are sending it + // in mDNS (indicated by non-NULL "msg") and hence we handle mDNS case + // separately. + if (!UNICAST_NSEC(rr)) + { + mDNSu8 *save = ptr; + int i, j, wlen; + wlen = *(nsec + 1); + nsec += 2; // Skip the window number and len + len -= 2; + + // For our simplified use of NSEC synthetic records: + // + // nextname is always the record's own name, + // the block number is always 0, + // the count byte is a value in the range 1-32, + // followed by the 1-32 data bytes + // + // Note: When we send the NSEC record in mDNS, the window size is set to 32. + // We need to find out what the last non-NULL byte is. If we are copying out + // from an RDATA, we have the right length. As we need to handle both the case, + // we loop to find the right value instead of blindly using len to copy. + + for (i=wlen; i>0; i--) if (nsec[i-1]) break; + + ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name); + if (!ptr) { LogInfo("putRData: Can't put name, Length %d, record %##s", limit - save, rr->name->c); return(mDNSNULL); } + if (i) // Only put a block if at least one type exists for this name + { + if (ptr + 2 + i > limit) { LogInfo("putRData: Can't put window, Length %d, i %d, record %##s", limit - ptr, i, rr->name->c); return(mDNSNULL); } + *ptr++ = 0; + *ptr++ = (mDNSu8)i; + for (j=0; j 32) + { LogMsg("putRData: invalid window length %d", wlen); return mDNSNULL; } + if (win < 0 || win >= 256) + { LogMsg("putRData: invalid window %d", win); return mDNSNULL; } + + nsec += wlen; + len -= wlen; + } + if (ptr + rr->rdlength > limit) { LogMsg("putRData: NSEC rdlength beyond limit %##s (%s), ptr %p, rdlength %d, limit %p", rr->name->c, DNSTypeName(rr->rrtype), ptr, rr->rdlength, limit); return(mDNSNULL);} + + // No compression allowed for "nxt", just copy the data. + mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength); + return(ptr + rr->rdlength); + } + } + + default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr->rrtype); + if (ptr + rr->rdlength > limit) return(mDNSNULL); + mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength); + return(ptr + rr->rdlength); + } +} #define IsUnicastUpdate(X) (!mDNSOpaque16IsZero((X)->h.id) && ((X)->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update) mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit) - { - mDNSu8 *endofrdata; - mDNSu16 actualLength; - // When sending SRV to conventional DNS server (i.e. in DNS update requests) we should not do name compression on the rdata (RFC 2782) - const DNSMessage *const rdatacompressionbase = (IsUnicastUpdate(msg) && rr->rrtype == kDNSType_SRV) ? mDNSNULL : msg; - - if (rr->RecordType == kDNSRecordTypeUnregistered) - { - LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); - return(ptr); - } - - if (!ptr) { LogMsg("PutResourceRecordTTLWithLimit ptr is null"); return(mDNSNULL); } - - ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name); - if (!ptr || ptr + 10 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL - ptr[0] = (mDNSu8)(rr->rrtype >> 8); - ptr[1] = (mDNSu8)(rr->rrtype & 0xFF); - ptr[2] = (mDNSu8)(rr->rrclass >> 8); - ptr[3] = (mDNSu8)(rr->rrclass & 0xFF); - ptr[4] = (mDNSu8)((ttl >> 24) & 0xFF); - ptr[5] = (mDNSu8)((ttl >> 16) & 0xFF); - ptr[6] = (mDNSu8)((ttl >> 8) & 0xFF); - ptr[7] = (mDNSu8)( ttl & 0xFF); - // ptr[8] and ptr[9] filled in *after* we find out how much space the rdata takes - - endofrdata = putRData(rdatacompressionbase, ptr+10, limit, rr); - if (!endofrdata) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); return(mDNSNULL); } - - // Go back and fill in the actual number of data bytes we wrote - // (actualLength can be less than rdlength when domain name compression is used) - actualLength = (mDNSu16)(endofrdata - ptr - 10); - ptr[8] = (mDNSu8)(actualLength >> 8); - ptr[9] = (mDNSu8)(actualLength & 0xFF); - - if (count) (*count)++; - else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); - return(endofrdata); - } +{ + mDNSu8 *endofrdata; + mDNSu16 actualLength; + // When sending SRV to conventional DNS server (i.e. in DNS update requests) we should not do name compression on the rdata (RFC 2782) + const DNSMessage *const rdatacompressionbase = (IsUnicastUpdate(msg) && rr->rrtype == kDNSType_SRV) ? mDNSNULL : msg; + + if (rr->RecordType == kDNSRecordTypeUnregistered) + { + LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); + return(ptr); + } + + if (!ptr) { LogMsg("PutResourceRecordTTLWithLimit ptr is null"); return(mDNSNULL); } + + ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name); + if (!ptr || ptr + 10 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL + ptr[0] = (mDNSu8)(rr->rrtype >> 8); + ptr[1] = (mDNSu8)(rr->rrtype & 0xFF); + ptr[2] = (mDNSu8)(rr->rrclass >> 8); + ptr[3] = (mDNSu8)(rr->rrclass & 0xFF); + ptr[4] = (mDNSu8)((ttl >> 24) & 0xFF); + ptr[5] = (mDNSu8)((ttl >> 16) & 0xFF); + ptr[6] = (mDNSu8)((ttl >> 8) & 0xFF); + ptr[7] = (mDNSu8)( ttl & 0xFF); + // ptr[8] and ptr[9] filled in *after* we find out how much space the rdata takes + + endofrdata = putRData(rdatacompressionbase, ptr+10, limit, rr); + if (!endofrdata) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); return(mDNSNULL); } + + // Go back and fill in the actual number of data bytes we wrote + // (actualLength can be less than rdlength when domain name compression is used) + actualLength = (mDNSu16)(endofrdata - ptr - 10); + ptr[8] = (mDNSu8)(actualLength >> 8); + ptr[9] = (mDNSu8)(actualLength & 0xFF); + + if (count) (*count)++; + else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); + return(endofrdata); +} mDNSlocal mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, mDNSu16 *count, const AuthRecord *rr) - { - ptr = putDomainNameAsLabels(msg, ptr, limit, rr->resrec.name); - if (!ptr || ptr + 10 > limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL - ptr[0] = (mDNSu8)(rr->resrec.rrtype >> 8); // Put type - ptr[1] = (mDNSu8)(rr->resrec.rrtype & 0xFF); - ptr[2] = (mDNSu8)(rr->resrec.rrclass >> 8); // Put class - ptr[3] = (mDNSu8)(rr->resrec.rrclass & 0xFF); - ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // TTL is zero - ptr[8] = ptr[9] = 0; // RDATA length is zero - (*count)++; - return(ptr + 10); - } +{ + ptr = putDomainNameAsLabels(msg, ptr, limit, rr->resrec.name); + if (!ptr || ptr + 10 > limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL + ptr[0] = (mDNSu8)(rr->resrec.rrtype >> 8); // Put type + ptr[1] = (mDNSu8)(rr->resrec.rrtype & 0xFF); + ptr[2] = (mDNSu8)(rr->resrec.rrclass >> 8); // Put class + ptr[3] = (mDNSu8)(rr->resrec.rrclass & 0xFF); + ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // TTL is zero + ptr[8] = ptr[9] = 0; // RDATA length is zero + (*count)++; + return(ptr + 10); +} mDNSexport mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass) - { - ptr = putDomainNameAsLabels(msg, ptr, limit, name); - if (!ptr || ptr+4 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL - ptr[0] = (mDNSu8)(rrtype >> 8); - ptr[1] = (mDNSu8)(rrtype & 0xFF); - ptr[2] = (mDNSu8)(rrclass >> 8); - ptr[3] = (mDNSu8)(rrclass & 0xFF); - msg->h.numQuestions++; - return(ptr+4); - } +{ + ptr = putDomainNameAsLabels(msg, ptr, limit, name); + if (!ptr || ptr+4 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL + ptr[0] = (mDNSu8)(rrtype >> 8); + ptr[1] = (mDNSu8)(rrtype & 0xFF); + ptr[2] = (mDNSu8)(rrclass >> 8); + ptr[3] = (mDNSu8)(rrclass & 0xFF); + msg->h.numQuestions++; + return(ptr+4); +} // 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; - } +{ + 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(const domainname *const name, DNSMessage *const msg, mDNSu8 *const ptr, mDNSu8 *const end) - { - AuthRecord prereq; - mDNS_SetupResourceRecord(&prereq, mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL); - AssignDomainName(&prereq.namestorage, name); - prereq.resrec.rrtype = kDNSQType_ANY; - prereq.resrec.rrclass = kDNSClass_NONE; - return putEmptyResourceRecord(msg, ptr, end, &msg->h.mDNS_numPrereqs, &prereq); - } +{ + AuthRecord prereq; + mDNS_SetupResourceRecord(&prereq, mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL); + AssignDomainName(&prereq.namestorage, name); + prereq.resrec.rrtype = kDNSQType_ANY; + prereq.resrec.rrclass = kDNSClass_NONE; + return putEmptyResourceRecord(msg, ptr, end, &msg->h.mDNS_numPrereqs, &prereq); +} // for dynamic updates mDNSexport mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr) - { - // deletion: specify record w/ TTL 0, class NONE - const mDNSu16 origclass = rr->rrclass; - rr->rrclass = kDNSClass_NONE; - ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0); - rr->rrclass = origclass; - return ptr; - } +{ + // deletion: specify record w/ TTL 0, class NONE + const mDNSu16 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 *putDeletionRecordWithLimit(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr, mDNSu8 *limit) - { - // deletion: specify record w/ TTL 0, class NONE - const mDNSu16 origclass = rr->rrclass; - rr->rrclass = kDNSClass_NONE; - ptr = PutResourceRecordTTLWithLimit(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0, limit); - rr->rrclass = origclass; - return ptr; - } +{ + // deletion: specify record w/ TTL 0, class NONE + const mDNSu16 origclass = rr->rrclass; + rr->rrclass = kDNSClass_NONE; + ptr = PutResourceRecordTTLWithLimit(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0, limit); + rr->rrclass = origclass; + return ptr; +} mDNSexport mDNSu8 *putDeleteRRSetWithLimit(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype, mDNSu8 *limit) - { - mDNSu16 class = kDNSQClass_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; - } +{ + mDNSu16 class = kDNSQClass_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 *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; - } +{ + 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; - mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); - rr.resrec.rrclass = NormalMaxDNSMessageData; - rr.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record - rr.resrec.rdestimate = sizeof(rdataOPT); - rr.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease; - rr.resrec.rdata->u.opt[0].u.updatelease = lease; - end = PutResourceRecordTTLJumbo(msg, end, &msg->h.numAdditionals, &rr.resrec, 0); - if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL; } - return end; - } +{ + AuthRecord rr; + mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + rr.resrec.rrclass = NormalMaxDNSMessageData; + rr.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record + rr.resrec.rdestimate = sizeof(rdataOPT); + rr.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease; + rr.resrec.rdata->u.opt[0].u.updatelease = lease; + end = PutResourceRecordTTLJumbo(msg, end, &msg->h.numAdditionals, &rr.resrec, 0); + if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL; } + return end; +} // for dynamic updates mDNSexport mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease, mDNSu8 *limit) - { - AuthRecord rr; - mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); - rr.resrec.rrclass = NormalMaxDNSMessageData; - rr.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record - rr.resrec.rdestimate = sizeof(rdataOPT); - rr.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease; - rr.resrec.rdata->u.opt[0].u.updatelease = lease; - end = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &rr.resrec, 0, limit); - if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTLWithLimit"); return mDNSNULL; } - return end; - } +{ + AuthRecord rr; + mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + rr.resrec.rrclass = NormalMaxDNSMessageData; + rr.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record + rr.resrec.rdestimate = sizeof(rdataOPT); + rr.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease; + rr.resrec.rdata->u.opt[0].u.updatelease = lease; + end = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &rr.resrec, 0, limit); + if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTLWithLimit"); return mDNSNULL; } + return end; +} + +mDNSexport mDNSu8 *putDNSSECOption(DNSMessage *msg, mDNSu8 *end, mDNSu8 *limit) +{ + AuthRecord rr; + mDNSu32 ttl = 0; + + mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + // It is still not clear what the right size is. We will have to fine tune this once we do + // a lot of testing with DNSSEC. + rr.resrec.rrclass = 4096; + rr.resrec.rdlength = 0; + rr.resrec.rdestimate = 0; + // set the DO bit + ttl |= 0x8000; + end = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &rr.resrec, ttl, limit); + if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTLWithLimit"); return mDNSNULL; } + return end; +} mDNSexport mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end, DomainAuthInfo *authInfo, mDNSu8 *limit) - { - if (authInfo && authInfo->AutoTunnel) - { - AuthRecord hinfo; - mDNSu8 *h = hinfo.rdatastorage.u.data; - mDNSu16 len = 2 + m->HIHardware.c[0] + m->HISoftware.c[0]; - mDNSu8 *newptr; - mDNS_SetupResourceRecord(&hinfo, mDNSNULL, mDNSInterface_Any, kDNSType_HINFO, 0, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL); - AppendDomainLabel(&hinfo.namestorage, &m->hostlabel); - AppendDomainName (&hinfo.namestorage, &authInfo->domain); - hinfo.resrec.rroriginalttl = 0; - mDNSPlatformMemCopy(h, &m->HIHardware, 1 + (mDNSu32)m->HIHardware.c[0]); - h += 1 + (int)h[0]; - mDNSPlatformMemCopy(h, &m->HISoftware, 1 + (mDNSu32)m->HISoftware.c[0]); - hinfo.resrec.rdlength = len; - hinfo.resrec.rdestimate = len; - newptr = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &hinfo.resrec, 0, limit); - return newptr; - } - else - return end; - } +{ + if (authInfo && authInfo->AutoTunnel) + { + AuthRecord hinfo; + mDNSu8 *h = hinfo.rdatastorage.u.data; + mDNSu16 len = 2 + m->HIHardware.c[0] + m->HISoftware.c[0]; + mDNSu8 *newptr; + mDNS_SetupResourceRecord(&hinfo, mDNSNULL, mDNSInterface_Any, kDNSType_HINFO, 0, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + AppendDomainLabel(&hinfo.namestorage, &m->hostlabel); + AppendDomainName (&hinfo.namestorage, &authInfo->domain); + hinfo.resrec.rroriginalttl = 0; + mDNSPlatformMemCopy(h, &m->HIHardware, 1 + (mDNSu32)m->HIHardware.c[0]); + h += 1 + (int)h[0]; + mDNSPlatformMemCopy(h, &m->HISoftware, 1 + (mDNSu32)m->HISoftware.c[0]); + hinfo.resrec.rdlength = len; + hinfo.resrec.rdestimate = len; + newptr = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &hinfo.resrec, 0, limit); + return newptr; + } + else + return end; +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -2005,583 +2427,967 @@ mDNSexport mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 * #endif mDNSexport mDNSu32 DomainNameHashValue(const domainname *const name) - { - mDNSu32 sum = 0; - const mDNSu8 *c; - - for (c = name->c; c[0] != 0 && c[1] != 0; c += 2) - { - sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8) | - (mDNSIsUpperCase(c[1]) ? c[1] + 'a' - 'A' : c[1]); - sum = (sum<<3) | (sum>>29); - } - if (c[0]) sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8); - return(sum); - } +{ + mDNSu32 sum = 0; + const mDNSu8 *c; + + for (c = name->c; c[0] != 0 && c[1] != 0; c += 2) + { + sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8) | + (mDNSIsUpperCase(c[1]) ? c[1] + 'a' - 'A' : c[1]); + sum = (sum<<3) | (sum>>29); + } + if (c[0]) sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8); + return(sum); +} mDNSexport void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength) - { - domainname *target; - if (NewRData) - { - rr->rdata = NewRData; - rr->rdlength = rdlength; - } - // Must not try to get target pointer until after updating rr->rdata - target = GetRRDomainNameTarget(rr); - rr->rdlength = GetRDLength(rr, mDNSfalse); - rr->rdestimate = GetRDLength(rr, mDNStrue); - rr->rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(rr); - } +{ + domainname *target; + if (NewRData) + { + rr->rdata = NewRData; + rr->rdlength = rdlength; + } + // Must not try to get target pointer until after updating rr->rdata + target = GetRRDomainNameTarget(rr); + rr->rdlength = GetRDLength(rr, mDNSfalse); + rr->rdestimate = GetRDLength(rr, mDNStrue); + rr->rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(rr); +} mDNSexport const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end) - { - mDNSu16 total = 0; - - if (ptr < (mDNSu8*)msg || ptr >= end) - { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); } - - while (1) // Read sequence of labels - { - const mDNSu8 len = *ptr++; // Read length of this label - if (len == 0) return(ptr); // If length is zero, that means this name is complete - switch (len & 0xC0) - { - case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label - { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); } - if (total + 1 + len >= MAX_DOMAIN_NAME) // Remember: expect at least one more byte for the root label - { debugf("skipDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL); } - ptr += len; - total += 1 + len; - break; - - case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len); return(mDNSNULL); - case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len); return(mDNSNULL); - case 0xC0: return(ptr+1); - } - } - } +{ + mDNSu16 total = 0; + + if (ptr < (mDNSu8*)msg || ptr >= end) + { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); } + + while (1) // Read sequence of labels + { + const mDNSu8 len = *ptr++; // Read length of this label + if (len == 0) return(ptr); // If length is zero, that means this name is complete + switch (len & 0xC0) + { + case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label + { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); } + if (total + 1 + len >= MAX_DOMAIN_NAME) // Remember: expect at least one more byte for the root label + { debugf("skipDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL); } + ptr += len; + total += 1 + len; + break; + + case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len); return(mDNSNULL); + case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len); return(mDNSNULL); + case 0xC0: return(ptr+1); + } + } +} // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary. mDNSexport const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, - domainname *const name) - { - const mDNSu8 *nextbyte = mDNSNULL; // Record where we got to before we started following pointers - mDNSu8 *np = name->c; // Name pointer - const mDNSu8 *const limit = np + MAX_DOMAIN_NAME; // Limit so we don't overrun buffer - - if (ptr < (mDNSu8*)msg || ptr >= end) - { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); } - - *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels) - - while (1) // Read sequence of labels - { - const mDNSu8 len = *ptr++; // Read length of this label - if (len == 0) break; // If length is zero, that means this name is complete - switch (len & 0xC0) - { - int i; - mDNSu16 offset; - - case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label - { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); } - if (np + 1 + len >= limit) // Remember: expect at least one more byte for the root label - { debugf("getDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL); } - *np++ = len; - for (i=0; ic); - return(mDNSNULL); - - case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len, name->c); return(mDNSNULL); - - case 0xC0: offset = (mDNSu16)((((mDNSu16)(len & 0x3F)) << 8) | *ptr++); - if (!nextbyte) nextbyte = ptr; // Record where we got to before we started following pointers - ptr = (mDNSu8 *)msg + offset; - if (ptr < (mDNSu8*)msg || ptr >= end) - { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL); } - if (*ptr & 0xC0) - { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL); } - break; - } - } - - if (nextbyte) return(nextbyte); - else return(ptr); - } + domainname *const name) +{ + const mDNSu8 *nextbyte = mDNSNULL; // Record where we got to before we started following pointers + mDNSu8 *np = name->c; // Name pointer + const mDNSu8 *const limit = np + MAX_DOMAIN_NAME; // Limit so we don't overrun buffer + + if (ptr < (mDNSu8*)msg || ptr >= end) + { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); } + + *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels) + + while (1) // Read sequence of labels + { + const mDNSu8 len = *ptr++; // Read length of this label + if (len == 0) break; // If length is zero, that means this name is complete + switch (len & 0xC0) + { + int i; + mDNSu16 offset; + + case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label + { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); } + if (np + 1 + len >= limit) // Remember: expect at least one more byte for the root label + { debugf("getDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL); } + *np++ = len; + for (i=0; ic); + return(mDNSNULL); + + case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len, name->c); return(mDNSNULL); + + case 0xC0: offset = (mDNSu16)((((mDNSu16)(len & 0x3F)) << 8) | *ptr++); + if (!nextbyte) nextbyte = ptr; // Record where we got to before we started following pointers + ptr = (mDNSu8 *)msg + offset; + if (ptr < (mDNSu8*)msg || ptr >= end) + { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL); } + if (*ptr & 0xC0) + { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL); } + break; + } + } + + if (nextbyte) return(nextbyte); + else return(ptr); +} mDNSexport const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end) - { - mDNSu16 pktrdlength; +{ + mDNSu16 pktrdlength; - ptr = skipDomainName(msg, ptr, end); - if (!ptr) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL); } + ptr = skipDomainName(msg, ptr, end); + if (!ptr) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL); } - if (ptr + 10 > end) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); } - pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]); - ptr += 10; - if (ptr + pktrdlength > end) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); } + if (ptr + 10 > end) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); } + pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]); + ptr += 10; + if (ptr + pktrdlength > end) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); } - return(ptr + pktrdlength); - } + return(ptr + pktrdlength); +} + +// This function is called with "msg" when we receive a DNS message and needs to parse a single resource record +// pointed to by "ptr". Some resource records like SOA, SRV are converted to host order and also expanded +// (domainnames are expanded to 255 bytes) when stored in memory. +// +// This function can also be called with "NULL" msg to parse a single resource record pointed to by ptr. +// The caller can do this only if the names in the resource records are compressed and validity of the +// resource record has already been done before. DNSSEC currently uses it this way. +mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *end, + LargeCacheRecord *const largecr, mDNSu16 rdlength) +{ + CacheRecord *const rr = &largecr->r; + RDataBody2 *const rdb = (RDataBody2 *)rr->smallrdatastorage.data; + + switch (rr->resrec.rrtype) + { + case kDNSType_A: + if (rdlength != sizeof(mDNSv4Addr)) + goto fail; + rdb->ipv4.b[0] = ptr[0]; + rdb->ipv4.b[1] = ptr[1]; + rdb->ipv4.b[2] = ptr[2]; + rdb->ipv4.b[3] = ptr[3]; + break; + + case kDNSType_NS: + case kDNSType_MD: + case kDNSType_MF: + case kDNSType_CNAME: + case kDNSType_MB: + case kDNSType_MG: + case kDNSType_MR: + case kDNSType_PTR: + case kDNSType_NSAP_PTR: + case kDNSType_DNAME: + if (msg) + { + ptr = getDomainName(msg, ptr, end, &rdb->name); + } + else + { + AssignDomainName(&rdb->name, (domainname *)ptr); + ptr += DomainNameLength(&rdb->name); + } + if (ptr != end) + { + debugf("SetRData: Malformed CNAME/PTR RDATA name"); + goto fail; + } + break; + + case kDNSType_SOA: + if (msg) + { + ptr = getDomainName(msg, ptr, end, &rdb->soa.mname); + } + else + { + AssignDomainName(&rdb->soa.mname, (domainname *)ptr); + ptr += DomainNameLength(&rdb->soa.mname); + } + if (!ptr) + { + debugf("SetRData: Malformed SOA RDATA mname"); + goto fail; + } + if (msg) + { + ptr = getDomainName(msg, ptr, end, &rdb->soa.rname); + } + else + { + AssignDomainName(&rdb->soa.rname, (domainname *)ptr); + ptr += DomainNameLength(&rdb->soa.rname); + } + if (!ptr) + { + debugf("SetRData: Malformed SOA RDATA rname"); + goto fail; + } + if (ptr + 0x14 != end) + { + debugf("SetRData: Malformed SOA RDATA"); + goto fail; + } + rdb->soa.serial = (mDNSs32) ((mDNSs32)ptr[0x00] << 24 | (mDNSs32)ptr[0x01] << 16 | (mDNSs32)ptr[0x02] << 8 | ptr[0x03]); + rdb->soa.refresh = (mDNSu32) ((mDNSu32)ptr[0x04] << 24 | (mDNSu32)ptr[0x05] << 16 | (mDNSu32)ptr[0x06] << 8 | ptr[0x07]); + rdb->soa.retry = (mDNSu32) ((mDNSu32)ptr[0x08] << 24 | (mDNSu32)ptr[0x09] << 16 | (mDNSu32)ptr[0x0A] << 8 | ptr[0x0B]); + rdb->soa.expire = (mDNSu32) ((mDNSu32)ptr[0x0C] << 24 | (mDNSu32)ptr[0x0D] << 16 | (mDNSu32)ptr[0x0E] << 8 | ptr[0x0F]); + rdb->soa.min = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]); + break; + + case kDNSType_NULL: + case kDNSType_HINFO: + case kDNSType_TXT: + case kDNSType_X25: + case kDNSType_ISDN: + case kDNSType_LOC: + case kDNSType_DHCID: + rr->resrec.rdlength = rdlength; + mDNSPlatformMemCopy(rdb->data, ptr, rdlength); + break; + + case kDNSType_MX: + case kDNSType_AFSDB: + case kDNSType_RT: + case kDNSType_KX: + // Preference + domainname + if (rdlength < 3) + goto fail; + rdb->mx.preference = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + ptr += 2; + if (msg) + { + ptr = getDomainName(msg, ptr, end, &rdb->mx.exchange); + } + else + { + AssignDomainName(&rdb->mx.exchange, (domainname *)ptr); + ptr += DomainNameLength(&rdb->mx.exchange); + } + if (ptr != end) + { + debugf("SetRData: Malformed MX name"); + goto fail; + } + break; + + case kDNSType_MINFO: + case kDNSType_RP: + // Domainname + domainname + if (msg) + { + ptr = getDomainName(msg, ptr, end, &rdb->rp.mbox); + } + else + { + AssignDomainName(&rdb->rp.mbox, (domainname *)ptr); + ptr += DomainNameLength(&rdb->rp.mbox); + } + if (!ptr) + { + debugf("SetRData: Malformed RP mbox"); + goto fail; + } + if (msg) + { + ptr = getDomainName(msg, ptr, end, &rdb->rp.txt); + } + else + { + AssignDomainName(&rdb->rp.txt, (domainname *)ptr); + ptr += DomainNameLength(&rdb->rp.txt); + } + if (ptr != end) + { + debugf("SetRData: Malformed RP txt"); + goto fail; + } + break; + + case kDNSType_PX: + // Preference + domainname + domainname + if (rdlength < 4) + goto fail; + rdb->px.preference = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + ptr += 2; + if (msg) + { + ptr = getDomainName(msg, ptr, end, &rdb->px.map822); + } + else + { + AssignDomainName(&rdb->px.map822, (domainname *)ptr); + ptr += DomainNameLength(&rdb->px.map822); + } + if (!ptr) + { + debugf("SetRData: Malformed PX map822"); + goto fail; + } + if (msg) + { + ptr = getDomainName(msg, ptr, end, &rdb->px.mapx400); + } + else + { + AssignDomainName(&rdb->px.mapx400, (domainname *)ptr); + ptr += DomainNameLength(&rdb->px.mapx400); + } + if (ptr != end) + { + debugf("SetRData: Malformed PX mapx400"); + goto fail; + } + break; + + case kDNSType_AAAA: + if (rdlength != sizeof(mDNSv6Addr)) + goto fail; + mDNSPlatformMemCopy(&rdb->ipv6, ptr, sizeof(rdb->ipv6)); + break; + + case kDNSType_SRV: + // Priority + weight + port + domainname + if (rdlength < 7) + goto fail; + rdb->srv.priority = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + rdb->srv.weight = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); + rdb->srv.port.b[0] = ptr[4]; + rdb->srv.port.b[1] = ptr[5]; + ptr += 6; + if (msg) + { + ptr = getDomainName(msg, ptr, end, &rdb->srv.target); + } + else + { + AssignDomainName(&rdb->srv.target, (domainname *)ptr); + ptr += DomainNameLength(&rdb->srv.target); + } + if (ptr != end) + { + debugf("SetRData: Malformed SRV RDATA name"); + goto fail; + } + break; + + case kDNSType_NAPTR: + { + int savelen, len; + domainname name; + const mDNSu8 *orig = ptr; + const mDNSu8 *save; + + // Make sure the data is parseable and within the limits. DNSSEC code looks at + // the domain name in the end for a valid domainname. + // + // Fixed length: Order, preference (4 bytes) + // Variable length: flags, service, regexp, domainname + + if (rdlength < 8) + goto fail; + // Order, preference. + ptr += 4; + // Parse flags, Service and Regexp + // length in the first byte does not include the length byte itself + len = *ptr + 1; + ptr += len; + if (ptr >= end) + { + LogInfo("SetRData: Malformed NAPTR flags"); + goto fail; + } + + // Service + len = *ptr + 1; + ptr += len; + if (ptr >= end) + { + LogInfo("SetRData: Malformed NAPTR service"); + goto fail; + } + + // Regexp + len = *ptr + 1; + ptr += len; + if (ptr >= end) + { + LogInfo("SetRData: Malformed NAPTR regexp"); + goto fail; + } + + save = ptr; + savelen = ptr - orig; + + // RFC 2915 states that name compression is not allowed for this field. But RFC 3597 + // states that for NAPTR we should decompress. We make sure that we store the full + // name rather than the compressed name + if (msg) + { + ptr = getDomainName(msg, ptr, end, &name); + } + else + { + AssignDomainName(&name, (domainname *)ptr); + ptr += DomainNameLength(&name); + } + if (ptr != end) + { + LogInfo("SetRData: Malformed NAPTR RDATA name"); + goto fail; + } + + rr->resrec.rdlength = savelen + DomainNameLength(&name); + // The uncompressed size should not exceed the limits + if (rr->resrec.rdlength > MaximumRDSize) + { + LogInfo("SetRData: Malformed NAPTR rdlength %d, rr->resrec.rdlength %d, " + "bmaplen %d, name %##s", rdlength, rr->resrec.rdlength, name.c); + goto fail; + } + mDNSPlatformMemCopy(rdb->data, orig, savelen); + AssignDomainName((domainname *)(rdb->data + savelen), &name); + break; + } + case kDNSType_OPT: { + mDNSu8 *dataend = rr->resrec.rdata->u.data; + rdataOPT *opt = rr->resrec.rdata->u.opt; + rr->resrec.rdlength = 0; + while (ptr < end && (mDNSu8 *)(opt+1) < &dataend[MaximumRDSize]) + { + const rdataOPT *const currentopt = opt; + if (ptr + 4 > end) { LogInfo("SetRData: OPT RDATA ptr + 4 > end"); goto fail; } + opt->opt = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + opt->optlen = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); + ptr += 4; + if (ptr + opt->optlen > end) { LogInfo("SetRData: ptr + opt->optlen > end"); goto fail; } + switch (opt->opt) + { + case kDNSOpt_LLQ: + if (opt->optlen == DNSOpt_LLQData_Space - 4) + { + opt->u.llq.vers = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + opt->u.llq.llqOp = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); + opt->u.llq.err = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); + mDNSPlatformMemCopy(opt->u.llq.id.b, ptr+6, 8); + opt->u.llq.llqlease = (mDNSu32) ((mDNSu32)ptr[14] << 24 | (mDNSu32)ptr[15] << 16 | (mDNSu32)ptr[16] << 8 | ptr[17]); + if (opt->u.llq.llqlease > 0x70000000UL / mDNSPlatformOneSecond) + opt->u.llq.llqlease = 0x70000000UL / mDNSPlatformOneSecond; + opt++; + } + break; + case kDNSOpt_Lease: + if (opt->optlen == DNSOpt_LeaseData_Space - 4) + { + opt->u.updatelease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]); + if (opt->u.updatelease > 0x70000000UL / mDNSPlatformOneSecond) + opt->u.updatelease = 0x70000000UL / mDNSPlatformOneSecond; + opt++; + } + break; + case kDNSOpt_Owner: + if (ValidOwnerLength(opt->optlen)) + { + opt->u.owner.vers = ptr[0]; + opt->u.owner.seq = ptr[1]; + mDNSPlatformMemCopy(opt->u.owner.HMAC.b, ptr+2, 6); // 6-byte MAC address + mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+2, 6); // 6-byte MAC address + opt->u.owner.password = zeroEthAddr; + if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4) + { + mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+8, 6); // 6-byte MAC address + // This mDNSPlatformMemCopy is safe because the ValidOwnerLength(opt->optlen) check above + // ensures that opt->optlen is no more than DNSOpt_OwnerData_ID_Wake_PW6_Space - 4 + if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4) + mDNSPlatformMemCopy(opt->u.owner.password.b, ptr+14, opt->optlen - (DNSOpt_OwnerData_ID_Wake_Space-4)); + } + opt++; + } + break; + } + ptr += currentopt->optlen; + } + rr->resrec.rdlength = (mDNSu16)((mDNSu8*)opt - rr->resrec.rdata->u.data); + if (ptr != end) { LogInfo("SetRData: Malformed OptRdata"); goto fail; } + break; + } + + case kDNSType_NSEC: { + domainname name; + int win, wlen; + int len = rdlength; + int bmaplen, dlen; + const mDNSu8 *orig = ptr; + const mDNSu8 *bmap; + + if (msg) + { + ptr = getDomainName(msg, ptr, end, &name); + } + else + { + AssignDomainName(&name, (domainname *)ptr); + ptr += DomainNameLength(&name); + } + if (!ptr) + { + LogInfo("SetRData: Malformed NSEC nextname"); + goto fail; + } + + dlen = DomainNameLength(&name); + + // Multicast NSECs use name compression for this field unlike the unicast case which + // does not use compression. And multicast case always succeeds in compression. So, + // the rdlength includes only the compressed space in that case. So, can't + // use the DomainNameLength of name to reduce the length here. + len -= (ptr - orig); + bmaplen = len; // Save the length of the bitmap + bmap = ptr; + // Sanity check whether the bitmap is good + while (ptr < end) + { + if (len < 3) + { + LogInfo("SetRData: invalid length %d", len); + goto fail; + } + + win = *ptr++; + wlen = *ptr++; + len -= 2; + if (len < wlen || wlen < 1 || wlen > 32) + { + LogInfo("SetRData: invalid window length %d", wlen); + goto fail; + } + if (win < 0 || win >= 256) + { + LogInfo("SetRData: invalid window %d", win); + goto fail; + } + + ptr += wlen; + len -= wlen; + } + if (ptr != end) + { + LogInfo("SetRData: Malformed NSEC length not right"); + goto fail; + } + + // Initialize the right length here. When we call SetNewRData below which in turn calls + // GetRDLength and for NSEC case, it assumes that rdlength is intitialized + rr->resrec.rdlength = DomainNameLength(&name) + bmaplen; + + // Do we have space after the name expansion ? + if (rr->resrec.rdlength > MaximumRDSize) + { + LogInfo("SetRData: Malformed NSEC rdlength %d, rr->resrec.rdlength %d, " + "bmaplen %d, name %##s", rdlength, rr->resrec.rdlength, name.c); + goto fail; + } + AssignDomainName((domainname *)rdb->data, &name); + mDNSPlatformMemCopy(rdb->data + dlen, bmap, bmaplen); + break; + } + case kDNSType_TKEY: + case kDNSType_TSIG: + { + domainname name; + int dlen, rlen; + + // The name should not be compressed. But we take the conservative approach + // and uncompress the name before we store it. + if (msg) + { + ptr = getDomainName(msg, ptr, end, &name); + } + else + { + AssignDomainName(&name, (domainname *)ptr); + ptr += DomainNameLength(&name); + } + if (!ptr) + { + LogInfo("SetRData: Malformed name for TSIG/TKEY type %d", rr->resrec.rrtype); + goto fail; + } + dlen = DomainNameLength(&name); + rlen = end - ptr; + rr->resrec.rdlength = dlen + rlen; + AssignDomainName((domainname *)rdb->data, &name); + mDNSPlatformMemCopy(rdb->data + dlen, ptr, rlen); + break; + } + case kDNSType_RRSIG: + { + const mDNSu8 *sig = ptr + RRSIG_FIXED_SIZE; + const mDNSu8 *orig = sig; + domainname name; + if (rdlength < RRSIG_FIXED_SIZE + 1) + { + LogInfo("SetRData: RRSIG too small length %d", rdlength); + goto fail; + } + if (msg) + { + sig = getDomainName(msg, sig, end, &name); + } + else + { + AssignDomainName(&name, (domainname *)sig); + sig += DomainNameLength(&name); + } + if (!sig) + { + LogInfo("SetRData: Malformed RRSIG record"); + goto fail; + } + + if ((sig - orig) != DomainNameLength(&name)) + { + LogInfo("SetRData: Malformed RRSIG record, signer name compression"); + goto fail; + } + // Just ensure that we have at least one byte of the signature + if (sig + 1 >= end) + { + LogInfo("SetRData: Not enough bytes for signature type %d", rr->resrec.rrtype); + goto fail; + } + rr->resrec.rdlength = rdlength; + mDNSPlatformMemCopy(rdb->data, ptr, rdlength); + break; + } + case kDNSType_DNSKEY: + { + if (rdlength < DNSKEY_FIXED_SIZE + 1) + { + LogInfo("SetRData: DNSKEY too small length %d", rdlength); + goto fail; + } + rr->resrec.rdlength = rdlength; + mDNSPlatformMemCopy(rdb->data, ptr, rdlength); + break; + } + case kDNSType_DS: + { + if (rdlength < DS_FIXED_SIZE + 1) + { + LogInfo("SetRData: DS too small length %d", rdlength); + goto fail; + } + rr->resrec.rdlength = rdlength; + mDNSPlatformMemCopy(rdb->data, ptr, rdlength); + break; + } + + default: + debugf("SetRData: Warning! Reading resource type %d (%s) as opaque data", + rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype)); + // Note: Just because we don't understand the record type, that doesn't + // mean we fail. The DNS protocol specifies rdlength, so we can + // safely skip over unknown records and ignore them. + // We also grab a binary copy of the rdata anyway, since the caller + // might know how to interpret it even if we don't. + rr->resrec.rdlength = rdlength; + mDNSPlatformMemCopy(rdb->data, ptr, rdlength); + break; + } + return mDNStrue; +fail: + return mDNSfalse; +} mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr, - const mDNSu8 *end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *const largecr) - { - CacheRecord *const rr = &largecr->r; - RDataBody2 *const rdb = (RDataBody2 *)rr->smallrdatastorage.data; - mDNSu16 pktrdlength; - - if (largecr == &m->rec && m->rec.r.resrec.RecordType) - { - LogMsg("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r)); + const mDNSu8 *end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *const largecr) +{ + CacheRecord *const rr = &largecr->r; + mDNSu16 pktrdlength; + + if (largecr == &m->rec && m->rec.r.resrec.RecordType) + { + LogMsg("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r)); #if ForceAlerts - *(long*)0 = 0; + *(long*)0 = 0; #endif - } - - rr->next = mDNSNULL; - rr->resrec.name = &largecr->namestorage; - - rr->NextInKAList = mDNSNULL; - 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 SetNextCacheCheckTimeForRecord() - rr->LastUsed = m ? m->timenow : 0; - rr->CRActiveQuestion = mDNSNULL; - rr->UnansweredQueries = 0; - rr->LastUnansweredTime= 0; + } + + rr->next = mDNSNULL; + rr->resrec.name = &largecr->namestorage; + + rr->NextInKAList = mDNSNULL; + 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 SetNextCacheCheckTimeForRecord() + rr->LastUsed = m ? m->timenow : 0; + rr->CRActiveQuestion = mDNSNULL; + rr->UnansweredQueries = 0; + rr->LastUnansweredTime= 0; #if ENABLE_MULTI_PACKET_QUERY_SNOOPING - rr->MPUnansweredQ = 0; - rr->MPLastUnansweredQT= 0; - rr->MPUnansweredKA = 0; - rr->MPExpectingKA = mDNSfalse; + rr->MPUnansweredQ = 0; + rr->MPLastUnansweredQT= 0; + rr->MPUnansweredKA = 0; + rr->MPExpectingKA = mDNSfalse; #endif - rr->NextInCFList = mDNSNULL; - - rr->resrec.InterfaceID = InterfaceID; - rr->resrec.rDNSServer = mDNSNULL; - - ptr = getDomainName(msg, ptr, end, &largecr->namestorage); // Will bail out correctly if ptr is NULL - if (!ptr) { debugf("GetLargeResourceRecord: Malformed RR name"); return(mDNSNULL); } - rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); - - if (ptr + 10 > end) { debugf("GetLargeResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); } - - rr->resrec.rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]); - rr->resrec.rrclass = (mDNSu16)(((mDNSu16)ptr[2] << 8 | ptr[3]) & kDNSClass_Mask); - rr->resrec.rroriginalttl = (mDNSu32) ((mDNSu32)ptr[4] << 24 | (mDNSu32)ptr[5] << 16 | (mDNSu32)ptr[6] << 8 | ptr[7]); - if (rr->resrec.rroriginalttl > 0x70000000UL / mDNSPlatformOneSecond && (mDNSs32)rr->resrec.rroriginalttl != -1) - rr->resrec.rroriginalttl = 0x70000000UL / mDNSPlatformOneSecond; - // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for - // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly. - pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]); - - // If mDNS record has cache-flush bit set, we mark it unique - // For uDNS records, all are implicitly deemed unique (a single DNS server is always - // authoritative for the entire RRSet), unless this is a truncated response - if (ptr[2] & (kDNSClass_UniqueRRSet >> 8) || (!InterfaceID && !(msg->h.flags.b[0] & kDNSFlag0_TC))) - RecordType |= kDNSRecordTypePacketUniqueMask; - ptr += 10; - if (ptr + pktrdlength > end) { debugf("GetLargeResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); } - end = ptr + pktrdlength; // Adjust end to indicate the end of the rdata for this resource record - - rr->resrec.rdata = (RData*)&rr->smallrdatastorage; - rr->resrec.rdata->MaxRDLength = MaximumRDSize; - - if (!RecordType) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr->resrec.name->c); - - // IMPORTANT: Any record type we understand and unpack into a structure containing domainnames needs to have corresponding - // cases in SameRDataBody() and RDataHashValue() to do a semantic comparison (or checksum) of the structure instead of a blind - // bitwise memory compare (or sum). This is because a domainname is a fixed size structure holding variable-length data. - // Any bytes past the logical end of the name are undefined, and a blind bitwise memory compare may indicate that - // two domainnames are different when semantically they are the same name and it's only the unused bytes that differ. - if (rr->resrec.rrclass == kDNSQClass_ANY && pktrdlength == 0) // Used in update packets to mean "Delete An RRset" (RFC 2136) - rr->resrec.rdlength = 0; - else switch (rr->resrec.rrtype) - { - case kDNSType_A: if (pktrdlength != sizeof(mDNSv4Addr)) goto fail; - rdb->ipv4.b[0] = ptr[0]; - rdb->ipv4.b[1] = ptr[1]; - rdb->ipv4.b[2] = ptr[2]; - rdb->ipv4.b[3] = ptr[3]; - break; - - case kDNSType_NS: - case kDNSType_CNAME: - case kDNSType_PTR: - case kDNSType_DNAME:ptr = getDomainName(msg, ptr, end, &rdb->name); - if (ptr != end) { debugf("GetLargeResourceRecord: Malformed CNAME/PTR RDATA name"); goto fail; } - //debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rdb->name.c, pktrdlength); - break; - - case kDNSType_SOA: ptr = getDomainName(msg, ptr, end, &rdb->soa.mname); - if (!ptr) { debugf("GetLargeResourceRecord: Malformed SOA RDATA mname"); goto fail; } - ptr = getDomainName(msg, ptr, end, &rdb->soa.rname); - if (!ptr) { debugf("GetLargeResourceRecord: Malformed SOA RDATA rname"); goto fail; } - if (ptr + 0x14 != end) { debugf("GetLargeResourceRecord: Malformed SOA RDATA"); goto fail; } - rdb->soa.serial = (mDNSs32) ((mDNSs32)ptr[0x00] << 24 | (mDNSs32)ptr[0x01] << 16 | (mDNSs32)ptr[0x02] << 8 | ptr[0x03]); - rdb->soa.refresh = (mDNSu32) ((mDNSu32)ptr[0x04] << 24 | (mDNSu32)ptr[0x05] << 16 | (mDNSu32)ptr[0x06] << 8 | ptr[0x07]); - rdb->soa.retry = (mDNSu32) ((mDNSu32)ptr[0x08] << 24 | (mDNSu32)ptr[0x09] << 16 | (mDNSu32)ptr[0x0A] << 8 | ptr[0x0B]); - rdb->soa.expire = (mDNSu32) ((mDNSu32)ptr[0x0C] << 24 | (mDNSu32)ptr[0x0D] << 16 | (mDNSu32)ptr[0x0E] << 8 | ptr[0x0F]); - rdb->soa.min = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]); - break; - - case kDNSType_NULL: - case kDNSType_HINFO: - case kDNSType_TSIG: - case kDNSType_TXT: - case kDNSType_X25: - case kDNSType_ISDN: - case kDNSType_LOC: - case kDNSType_DHCID:if (pktrdlength > rr->resrec.rdata->MaxRDLength) - { - debugf("GetLargeResourceRecord: %s rdata size (%d) exceeds storage (%d)", - DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength); - goto fail; - } - rr->resrec.rdlength = pktrdlength; - mDNSPlatformMemCopy(rdb->data, ptr, pktrdlength); - break; - - case kDNSType_MX: - case kDNSType_AFSDB: - case kDNSType_RT: - case kDNSType_KX: if (pktrdlength < 3) goto fail; // Preference + domainname - rdb->mx.preference = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); - ptr = getDomainName(msg, ptr+2, end, &rdb->mx.exchange); - if (ptr != end) { debugf("GetLargeResourceRecord: Malformed MX name"); goto fail; } - //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rdb->srv.target.c, pktrdlength); - break; - - case kDNSType_RP: ptr = getDomainName(msg, ptr, end, &rdb->rp.mbox); // Domainname + domainname - if (!ptr) { debugf("GetLargeResourceRecord: Malformed RP mbox"); goto fail; } - ptr = getDomainName(msg, ptr, end, &rdb->rp.txt); - if (ptr != end) { debugf("GetLargeResourceRecord: Malformed RP txt"); goto fail; } - break; - - case kDNSType_PX: if (pktrdlength < 4) goto fail; // Preference + domainname + domainname - rdb->px.preference = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); - ptr = getDomainName(msg, ptr, end, &rdb->px.map822); - if (!ptr) { debugf("GetLargeResourceRecord: Malformed PX map822"); goto fail; } - ptr = getDomainName(msg, ptr, end, &rdb->px.mapx400); - if (ptr != end) { debugf("GetLargeResourceRecord: Malformed PX mapx400"); goto fail; } - break; - - case kDNSType_AAAA: if (pktrdlength != sizeof(mDNSv6Addr)) goto fail; - mDNSPlatformMemCopy(&rdb->ipv6, ptr, sizeof(rdb->ipv6)); - break; - - case kDNSType_SRV: if (pktrdlength < 7) goto fail; // Priority + weight + port + domainname - rdb->srv.priority = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); - rdb->srv.weight = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); - rdb->srv.port.b[0] = ptr[4]; - rdb->srv.port.b[1] = ptr[5]; - ptr = getDomainName(msg, ptr+6, end, &rdb->srv.target); - if (ptr != end) { debugf("GetLargeResourceRecord: Malformed SRV RDATA name"); goto fail; } - //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rdb->srv.target.c, pktrdlength); - break; - - case kDNSType_OPT: { - rdataOPT *opt = rr->resrec.rdata->u.opt; - rr->resrec.rdlength = 0; - while (ptr < end && (mDNSu8 *)(opt+1) < &rr->resrec.rdata->u.data[MaximumRDSize]) - { - const rdataOPT *const currentopt = opt; - if (ptr + 4 > end) { LogInfo("GetLargeResourceRecord: OPT RDATA ptr + 4 > end"); goto fail; } - opt->opt = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); - opt->optlen = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); - ptr += 4; - if (ptr + opt->optlen > end) { LogInfo("GetLargeResourceRecord: ptr + opt->optlen > end"); goto fail; } - switch (opt->opt) - { - case kDNSOpt_LLQ: - if (opt->optlen == DNSOpt_LLQData_Space - 4) - { - opt->u.llq.vers = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); - opt->u.llq.llqOp = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); - opt->u.llq.err = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); - mDNSPlatformMemCopy(opt->u.llq.id.b, ptr+6, 8); - opt->u.llq.llqlease = (mDNSu32) ((mDNSu32)ptr[14] << 24 | (mDNSu32)ptr[15] << 16 | (mDNSu32)ptr[16] << 8 | ptr[17]); - if (opt->u.llq.llqlease > 0x70000000UL / mDNSPlatformOneSecond) - opt->u.llq.llqlease = 0x70000000UL / mDNSPlatformOneSecond; - opt++; - } - break; - case kDNSOpt_Lease: - if (opt->optlen == DNSOpt_LeaseData_Space - 4) - { - opt->u.updatelease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]); - if (opt->u.updatelease > 0x70000000UL / mDNSPlatformOneSecond) - opt->u.updatelease = 0x70000000UL / mDNSPlatformOneSecond; - opt++; - } - break; - case kDNSOpt_Owner: - if (ValidOwnerLength(opt->optlen)) - { - opt->u.owner.vers = ptr[0]; - opt->u.owner.seq = ptr[1]; - mDNSPlatformMemCopy(opt->u.owner.HMAC.b, ptr+2, 6); // 6-byte MAC address - mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+2, 6); // 6-byte MAC address - opt->u.owner.password = zeroEthAddr; - if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4) - { - mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+8, 6); // 6-byte MAC address - // This mDNSPlatformMemCopy is safe because the ValidOwnerLength(opt->optlen) check above - // ensures that opt->optlen is no more than DNSOpt_OwnerData_ID_Wake_PW6_Space - 4 - if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4) - mDNSPlatformMemCopy(opt->u.owner.password.b, ptr+14, opt->optlen - (DNSOpt_OwnerData_ID_Wake_Space-4)); - } - opt++; - } - break; - } - ptr += currentopt->optlen; - } - rr->resrec.rdlength = (mDNSu16)((mDNSu8*)opt - rr->resrec.rdata->u.data); - if (ptr != end) { LogInfo("GetLargeResourceRecord: Malformed OptRdata"); goto fail; } - break; - } - - case kDNSType_NSEC: { - unsigned int i, j; - domainname d; - ptr = getDomainName(msg, ptr, end, &d); // Ignored for our simplified use of NSEC synthetic records - if (!ptr) { LogInfo("GetLargeResourceRecord: Malformed NSEC nextname"); goto fail; } - mDNSPlatformMemZero(rdb->nsec.bitmap, sizeof(rdb->nsec.bitmap)); - if (ptr < end) - { - if (*ptr++ != 0) { debugf("GetLargeResourceRecord: We only handle block zero NSECs"); goto fail; } - i = *ptr++; - if (i > sizeof(rdataNSEC)) { debugf("GetLargeResourceRecord: invalid block length %d", i); goto fail; } - for (j=0; jnsec.bitmap[j] = *ptr++; - } - if (ptr != end) { debugf("GetLargeResourceRecord: Malformed NSEC"); goto fail; } - break; - } - - default: if (pktrdlength > rr->resrec.rdata->MaxRDLength) - { - debugf("GetLargeResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)", - rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength); - goto fail; - } - debugf("GetLargeResourceRecord: Warning! Reading resource type %d (%s) as opaque data", - rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype)); - // Note: Just because we don't understand the record type, that doesn't - // mean we fail. The DNS protocol specifies rdlength, so we can - // safely skip over unknown records and ignore them. - // We also grab a binary copy of the rdata anyway, since the caller - // might know how to interpret it even if we don't. - rr->resrec.rdlength = pktrdlength; - mDNSPlatformMemCopy(rdb->data, ptr, pktrdlength); - break; - } - - SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rdlength, rdestimate, rdatahash for us - - // Success! Now fill in RecordType to show this record contains valid data - rr->resrec.RecordType = RecordType; - return(end); + rr->NextInCFList = mDNSNULL; + + rr->resrec.InterfaceID = InterfaceID; + rr->resrec.rDNSServer = mDNSNULL; + + ptr = getDomainName(msg, ptr, end, &largecr->namestorage); // Will bail out correctly if ptr is NULL + if (!ptr) { debugf("GetLargeResourceRecord: Malformed RR name"); return(mDNSNULL); } + rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); + + if (ptr + 10 > end) { debugf("GetLargeResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); } + + rr->resrec.rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]); + rr->resrec.rrclass = (mDNSu16)(((mDNSu16)ptr[2] << 8 | ptr[3]) & kDNSClass_Mask); + rr->resrec.rroriginalttl = (mDNSu32) ((mDNSu32)ptr[4] << 24 | (mDNSu32)ptr[5] << 16 | (mDNSu32)ptr[6] << 8 | ptr[7]); + if (rr->resrec.rroriginalttl > 0x70000000UL / mDNSPlatformOneSecond && (mDNSs32)rr->resrec.rroriginalttl != -1) + rr->resrec.rroriginalttl = 0x70000000UL / mDNSPlatformOneSecond; + // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for + // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly. + pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]); + + // If mDNS record has cache-flush bit set, we mark it unique + // For uDNS records, all are implicitly deemed unique (a single DNS server is always + // authoritative for the entire RRSet), unless this is a truncated response + if (ptr[2] & (kDNSClass_UniqueRRSet >> 8) || (!InterfaceID && !(msg->h.flags.b[0] & kDNSFlag0_TC))) + RecordType |= kDNSRecordTypePacketUniqueMask; + ptr += 10; + if (ptr + pktrdlength > end) { debugf("GetLargeResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); } + end = ptr + pktrdlength; // Adjust end to indicate the end of the rdata for this resource record + + rr->resrec.rdata = (RData*)&rr->smallrdatastorage; + rr->resrec.rdata->MaxRDLength = MaximumRDSize; + + if (pktrdlength > MaximumRDSize) + { + LogInfo("GetLargeResourceRecord: %s rdata size (%d) exceeds storage (%d)", + DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength); + goto fail; + } + + if (!RecordType) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr->resrec.name->c); + + // IMPORTANT: Any record type we understand and unpack into a structure containing domainnames needs to have corresponding + // cases in SameRDataBody() and RDataHashValue() to do a semantic comparison (or checksum) of the structure instead of a blind + // bitwise memory compare (or sum). This is because a domainname is a fixed size structure holding variable-length data. + // Any bytes past the logical end of the name are undefined, and a blind bitwise memory compare may indicate that + // two domainnames are different when semantically they are the same name and it's only the unused bytes that differ. + if (rr->resrec.rrclass == kDNSQClass_ANY && pktrdlength == 0) // Used in update packets to mean "Delete An RRset" (RFC 2136) + rr->resrec.rdlength = 0; + else if (!SetRData(msg, ptr, end, largecr, pktrdlength)) + goto fail; + + SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rdlength, rdestimate, rdatahash for us + + // Success! Now fill in RecordType to show this record contains valid data + rr->resrec.RecordType = RecordType; + return(end); fail: - // If we were unable to parse the rdata in this record, we indicate that by - // returing a 'kDNSRecordTypePacketNegative' record with rdlength set to zero - rr->resrec.RecordType = kDNSRecordTypePacketNegative; - rr->resrec.rdlength = 0; - rr->resrec.rdestimate = 0; - rr->resrec.rdatahash = 0; - return(end); - } + // If we were unable to parse the rdata in this record, we indicate that by + // returing a 'kDNSRecordTypePacketNegative' record with rdlength set to zero + rr->resrec.RecordType = kDNSRecordTypePacketNegative; + rr->resrec.rdlength = 0; + rr->resrec.rdestimate = 0; + rr->resrec.rdatahash = 0; + return(end); +} mDNSexport const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end) - { - ptr = skipDomainName(msg, ptr, end); - if (!ptr) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL); } - if (ptr+4 > end) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL); } - return(ptr+4); - } +{ + ptr = skipDomainName(msg, ptr, end); + if (!ptr) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL); } + if (ptr+4 > end) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL); } + return(ptr+4); +} mDNSexport const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID, - DNSQuestion *question) - { - mDNSPlatformMemZero(question, sizeof(*question)); - question->InterfaceID = InterfaceID; - if (!InterfaceID) question->TargetQID = onesID; // In DNSQuestions we use TargetQID as the indicator of whether it's unicast or multicast - ptr = getDomainName(msg, ptr, end, &question->qname); - if (!ptr) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL); } - if (ptr+4 > end) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL); } - - question->qnamehash = DomainNameHashValue(&question->qname); - question->qtype = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); // Get type - question->qclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); // and class - return(ptr+4); - } + DNSQuestion *question) +{ + mDNSPlatformMemZero(question, sizeof(*question)); + question->InterfaceID = InterfaceID; + if (!InterfaceID) question->TargetQID = onesID; // In DNSQuestions we use TargetQID as the indicator of whether it's unicast or multicast + ptr = getDomainName(msg, ptr, end, &question->qname); + if (!ptr) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL); } + if (ptr+4 > end) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL); } + + question->qnamehash = DomainNameHashValue(&question->qname); + question->qtype = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); // Get type + question->qclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); // and class + return(ptr+4); +} mDNSexport const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end) - { - int i; - const mDNSu8 *ptr = msg->data; - for (i = 0; i < msg->h.numQuestions && ptr; i++) ptr = skipQuestion(msg, ptr, end); - return(ptr); - } +{ + int i; + const mDNSu8 *ptr = msg->data; + for (i = 0; i < msg->h.numQuestions && ptr; i++) ptr = skipQuestion(msg, ptr, end); + return(ptr); +} mDNSexport const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end) - { - int i; - const mDNSu8 *ptr = LocateAnswers(msg, end); - for (i = 0; i < msg->h.numAnswers && ptr; i++) ptr = skipResourceRecord(msg, ptr, end); - return(ptr); - } +{ + int i; + const mDNSu8 *ptr = LocateAnswers(msg, end); + for (i = 0; i < msg->h.numAnswers && ptr; i++) ptr = skipResourceRecord(msg, ptr, end); + return(ptr); +} mDNSexport const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 *const end) - { - int i; - const mDNSu8 *ptr = LocateAuthorities(msg, end); - for (i = 0; i < msg->h.numAuthorities; i++) ptr = skipResourceRecord(msg, ptr, end); - return (ptr); - } +{ + int i; + const mDNSu8 *ptr = LocateAuthorities(msg, end); + for (i = 0; i < msg->h.numAuthorities; i++) ptr = skipResourceRecord(msg, ptr, end); + return (ptr); +} mDNSexport const mDNSu8 *LocateOptRR(const DNSMessage *const msg, const mDNSu8 *const end, int minsize) - { - int i; - const mDNSu8 *ptr = LocateAdditionals(msg, end); - - // Locate the OPT record. - // According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response." - // This implies that there may be *at most* one OPT record per DNS message, in the Additional Section, - // but not necessarily the *last* entry in the Additional Section. - for (i = 0; ptr && i < msg->h.numAdditionals; i++) - { - if (ptr + DNSOpt_Header_Space + minsize <= end && // Make sure we have 11+minsize bytes of data - ptr[0] == 0 && // Name must be root label - ptr[1] == (kDNSType_OPT >> 8 ) && // rrtype OPT - ptr[2] == (kDNSType_OPT & 0xFF) && - ((mDNSu16)ptr[9] << 8 | (mDNSu16)ptr[10]) >= (mDNSu16)minsize) - return(ptr); - else - ptr = skipResourceRecord(msg, ptr, end); - } - return(mDNSNULL); - } +{ + int i; + const mDNSu8 *ptr = LocateAdditionals(msg, end); + + // Locate the OPT record. + // According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response." + // This implies that there may be *at most* one OPT record per DNS message, in the Additional Section, + // but not necessarily the *last* entry in the Additional Section. + for (i = 0; ptr && i < msg->h.numAdditionals; i++) + { + if (ptr + DNSOpt_Header_Space + minsize <= end && // Make sure we have 11+minsize bytes of data + ptr[0] == 0 && // Name must be root label + ptr[1] == (kDNSType_OPT >> 8 ) && // rrtype OPT + ptr[2] == (kDNSType_OPT & 0xFF) && + ((mDNSu16)ptr[9] << 8 | (mDNSu16)ptr[10]) >= (mDNSu16)minsize) + return(ptr); + else + ptr = skipResourceRecord(msg, ptr, end); + } + return(mDNSNULL); +} // On success, GetLLQOptData returns pointer to storage within shared "m->rec"; // it is caller's responsibilty to clear m->rec.r.resrec.RecordType after use // Note: An OPT RDataBody actually contains one or more variable-length rdataOPT objects packed together // The code that currently calls this assumes there's only one, instead of iterating through the set mDNSexport const rdataOPT *GetLLQOptData(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end) - { - const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LLQData_Space); - if (ptr) - { - ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec); - if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative) return(&m->rec.r.resrec.rdata->u.opt[0]); - } - return(mDNSNULL); - } +{ + const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LLQData_Space); + if (ptr) + { + ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec); + if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative) return(&m->rec.r.resrec.rdata->u.opt[0]); + } + return(mDNSNULL); +} // Get the lease life of records in a dynamic update // returns 0 on error or if no lease present mDNSexport mDNSu32 GetPktLease(mDNS *m, DNSMessage *msg, const mDNSu8 *end) - { - mDNSu32 result = 0; - const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space); - if (ptr) ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec); - if (ptr && m->rec.r.resrec.rdlength >= DNSOpt_LeaseData_Space && m->rec.r.resrec.rdata->u.opt[0].opt == kDNSOpt_Lease) - result = m->rec.r.resrec.rdata->u.opt[0].u.updatelease; - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - return(result); - } +{ + mDNSu32 result = 0; + const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space); + if (ptr) ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec); + if (ptr && m->rec.r.resrec.rdlength >= DNSOpt_LeaseData_Space && m->rec.r.resrec.rdata->u.opt[0].opt == kDNSOpt_Lease) + result = m->rec.r.resrec.rdata->u.opt[0].u.updatelease; + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + return(result); +} mDNSlocal const mDNSu8 *DumpRecords(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, int count, char *label) - { - int i; - LogMsg("%2d %s", count, label); - for (i = 0; i < count && ptr; i++) - { - // This puts a LargeCacheRecord on the stack instead of using the shared m->rec storage, - // but since it's only used for debugging (and probably only on OS X, not on - // embedded systems) putting a 9kB object on the stack isn't a big problem. - LargeCacheRecord largecr; - ptr = GetLargeResourceRecord(m, msg, ptr, end, mDNSInterface_Any, kDNSRecordTypePacketAns, &largecr); - if (ptr) LogMsg("%2d TTL%8d %s", i, largecr.r.resrec.rroriginalttl, CRDisplayString(m, &largecr.r)); - } - if (!ptr) LogMsg("ERROR: Premature end of packet data"); - return(ptr); - } +{ + int i; + LogMsg("%2d %s", count, label); + for (i = 0; i < count && ptr; i++) + { + // This puts a LargeCacheRecord on the stack instead of using the shared m->rec storage, + // but since it's only used for debugging (and probably only on OS X, not on + // embedded systems) putting a 9kB object on the stack isn't a big problem. + LargeCacheRecord largecr; + ptr = GetLargeResourceRecord(m, msg, ptr, end, mDNSInterface_Any, kDNSRecordTypePacketAns, &largecr); + if (ptr) LogMsg("%2d TTL%8d %s", i, largecr.r.resrec.rroriginalttl, CRDisplayString(m, &largecr.r)); + } + if (!ptr) LogMsg("DumpRecords: ERROR: Premature end of packet data"); + return(ptr); +} #define DNS_OP_Name(X) ( \ - (X) == kDNSFlag0_OP_StdQuery ? "" : \ - (X) == kDNSFlag0_OP_Iquery ? "Iquery " : \ - (X) == kDNSFlag0_OP_Status ? "Status " : \ - (X) == kDNSFlag0_OP_Unused3 ? "Unused3 " : \ - (X) == kDNSFlag0_OP_Notify ? "Notify " : \ - (X) == kDNSFlag0_OP_Update ? "Update " : "?? " ) + (X) == kDNSFlag0_OP_StdQuery ? "" : \ + (X) == kDNSFlag0_OP_Iquery ? "Iquery " : \ + (X) == kDNSFlag0_OP_Status ? "Status " : \ + (X) == kDNSFlag0_OP_Unused3 ? "Unused3 " : \ + (X) == kDNSFlag0_OP_Notify ? "Notify " : \ + (X) == kDNSFlag0_OP_Update ? "Update " : "?? " ) #define DNS_RC_Name(X) ( \ - (X) == kDNSFlag1_RC_NoErr ? "NoErr" : \ - (X) == kDNSFlag1_RC_FormErr ? "FormErr" : \ - (X) == kDNSFlag1_RC_ServFail ? "ServFail" : \ - (X) == kDNSFlag1_RC_NXDomain ? "NXDomain" : \ - (X) == kDNSFlag1_RC_NotImpl ? "NotImpl" : \ - (X) == kDNSFlag1_RC_Refused ? "Refused" : \ - (X) == kDNSFlag1_RC_YXDomain ? "YXDomain" : \ - (X) == kDNSFlag1_RC_YXRRSet ? "YXRRSet" : \ - (X) == kDNSFlag1_RC_NXRRSet ? "NXRRSet" : \ - (X) == kDNSFlag1_RC_NotAuth ? "NotAuth" : \ - (X) == kDNSFlag1_RC_NotZone ? "NotZone" : "??" ) + (X) == kDNSFlag1_RC_NoErr ? "NoErr" : \ + (X) == kDNSFlag1_RC_FormErr ? "FormErr" : \ + (X) == kDNSFlag1_RC_ServFail ? "ServFail" : \ + (X) == kDNSFlag1_RC_NXDomain ? "NXDomain" : \ + (X) == kDNSFlag1_RC_NotImpl ? "NotImpl" : \ + (X) == kDNSFlag1_RC_Refused ? "Refused" : \ + (X) == kDNSFlag1_RC_YXDomain ? "YXDomain" : \ + (X) == kDNSFlag1_RC_YXRRSet ? "YXRRSet" : \ + (X) == kDNSFlag1_RC_NXRRSet ? "NXRRSet" : \ + (X) == kDNSFlag1_RC_NotAuth ? "NotAuth" : \ + (X) == kDNSFlag1_RC_NotZone ? "NotZone" : "??" ) // Note: DumpPacket expects the packet header fields in host byte order, not network byte order mDNSexport void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *transport, - const mDNSAddr *srcaddr, mDNSIPPort srcport, - const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end) - { - mDNSBool IsUpdate = ((msg->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update); - const mDNSu8 *ptr = msg->data; - int i; - DNSQuestion q; - char tbuffer[64], sbuffer[64], dbuffer[64] = ""; - if (!status) tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), sent ? "Sent" : "Received" )] = 0; - else tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), "ERROR %d %sing", status, sent ? "Send" : "Receiv")] = 0; - if (sent) sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "port " )] = 0; - else sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "%#a:", srcaddr)] = 0; - if (dstaddr || !mDNSIPPortIsZero(dstport)) - dbuffer[mDNS_snprintf(dbuffer, sizeof(dbuffer), " to %#a:%d", dstaddr, mDNSVal16(dstport))] = 0; - - LogMsg("-- %s %s DNS %s%s (flags %02X%02X) RCODE: %s (%d) %s%s%s%s%s%sID: %d %d bytes from %s%d%s%s --", - tbuffer, transport, - DNS_OP_Name(msg->h.flags.b[0] & kDNSFlag0_OP_Mask), - msg->h.flags.b[0] & kDNSFlag0_QR_Response ? "Response" : "Query", - msg->h.flags.b[0], msg->h.flags.b[1], - DNS_RC_Name(msg->h.flags.b[1] & kDNSFlag1_RC_Mask), - msg->h.flags.b[1] & kDNSFlag1_RC_Mask, - msg->h.flags.b[0] & kDNSFlag0_AA ? "AA " : "", - msg->h.flags.b[0] & kDNSFlag0_TC ? "TC " : "", - msg->h.flags.b[0] & kDNSFlag0_RD ? "RD " : "", - msg->h.flags.b[1] & kDNSFlag1_RA ? "RA " : "", - msg->h.flags.b[1] & kDNSFlag1_AD ? "AD " : "", - msg->h.flags.b[1] & kDNSFlag1_CD ? "CD " : "", - mDNSVal16(msg->h.id), - end - msg->data, - sbuffer, mDNSVal16(srcport), dbuffer, - (msg->h.flags.b[0] & kDNSFlag0_TC) ? " (truncated)" : "" - ); - - LogMsg("%2d %s", msg->h.numQuestions, IsUpdate ? "Zone" : "Questions"); - for (i = 0; i < msg->h.numQuestions && ptr; i++) - { - ptr = getQuestion(msg, ptr, end, mDNSInterface_Any, &q); - if (ptr) LogMsg("%2d %##s %s", i, q.qname.c, DNSTypeName(q.qtype)); - } - ptr = DumpRecords(m, msg, ptr, end, msg->h.numAnswers, IsUpdate ? "Prerequisites" : "Answers"); - ptr = DumpRecords(m, msg, ptr, end, msg->h.numAuthorities, IsUpdate ? "Updates" : "Authorities"); - ptr = DumpRecords(m, msg, ptr, end, msg->h.numAdditionals, "Additionals"); - LogMsg("--------------"); - } + const mDNSAddr *srcaddr, mDNSIPPort srcport, + const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end) +{ + mDNSBool IsUpdate = ((msg->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update); + const mDNSu8 *ptr = msg->data; + int i; + DNSQuestion q; + char tbuffer[64], sbuffer[64], dbuffer[64] = ""; + if (!status) tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), sent ? "Sent" : "Received" )] = 0; + else tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), "ERROR %d %sing", status, sent ? "Send" : "Receiv")] = 0; + if (sent) sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "port " )] = 0; + else sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "%#a:", srcaddr)] = 0; + if (dstaddr || !mDNSIPPortIsZero(dstport)) + dbuffer[mDNS_snprintf(dbuffer, sizeof(dbuffer), " to %#a:%d", dstaddr, mDNSVal16(dstport))] = 0; + + LogMsg("-- %s %s DNS %s%s (flags %02X%02X) RCODE: %s (%d) %s%s%s%s%s%sID: %d %d bytes from %s%d%s%s --", + tbuffer, transport, + DNS_OP_Name(msg->h.flags.b[0] & kDNSFlag0_OP_Mask), + msg->h.flags.b[0] & kDNSFlag0_QR_Response ? "Response" : "Query", + msg->h.flags.b[0], msg->h.flags.b[1], + DNS_RC_Name(msg->h.flags.b[1] & kDNSFlag1_RC_Mask), + msg->h.flags.b[1] & kDNSFlag1_RC_Mask, + msg->h.flags.b[0] & kDNSFlag0_AA ? "AA " : "", + msg->h.flags.b[0] & kDNSFlag0_TC ? "TC " : "", + msg->h.flags.b[0] & kDNSFlag0_RD ? "RD " : "", + msg->h.flags.b[1] & kDNSFlag1_RA ? "RA " : "", + msg->h.flags.b[1] & kDNSFlag1_AD ? "AD " : "", + msg->h.flags.b[1] & kDNSFlag1_CD ? "CD " : "", + mDNSVal16(msg->h.id), + end - msg->data, + sbuffer, mDNSVal16(srcport), dbuffer, + (msg->h.flags.b[0] & kDNSFlag0_TC) ? " (truncated)" : "" + ); + + LogMsg("%2d %s", msg->h.numQuestions, IsUpdate ? "Zone" : "Questions"); + for (i = 0; i < msg->h.numQuestions && ptr; i++) + { + ptr = getQuestion(msg, ptr, end, mDNSInterface_Any, &q); + if (ptr) LogMsg("%2d %##s %s", i, q.qname.c, DNSTypeName(q.qtype)); + } + ptr = DumpRecords(m, msg, ptr, end, msg->h.numAnswers, IsUpdate ? "Prerequisites" : "Answers"); + ptr = DumpRecords(m, msg, ptr, end, msg->h.numAuthorities, IsUpdate ? "Updates" : "Authorities"); + ptr = DumpRecords(m, msg, ptr, end, msg->h.numAdditionals, "Additionals"); + LogMsg("--------------"); +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -2593,67 +3399,69 @@ mDNSexport void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *t struct TCPSocket_struct { TCPSocketFlags flags; /* ... */ }; struct UDPSocket_struct - { - mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port - }; +{ + mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port +}; // Note: When we sign a DNS message using DNSDigest_SignMessage(), the current real-time clock value is used, which // is why we generally defer signing until we send the message, to ensure the signature is as fresh as possible. mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNSu8 *end, - mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, mDNSIPPort dstport, TCPSocket *sock, DomainAuthInfo *authInfo) - { - mStatus status = mStatus_NoError; - const mDNSu16 numAdditionals = msg->h.numAdditionals; - mDNSu8 *newend; - mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData; - - // Zero-length message data is okay (e.g. for a DNS Update ack, where all we need is an ID and an error code - if (end < msg->data || end - msg->data > AbsoluteMaxDNSMessageData) - { - LogMsg("mDNSSendDNSMessage: invalid message %p %p %d", msg->data, end, end - msg->data); - return mStatus_BadParamErr; - } - - newend = putHINFO(m, msg, end, authInfo, limit); - if (!newend) LogMsg("mDNSSendDNSMessage: putHINFO failed msg %p end %p, limit %p", msg->data, end, limit); // Not fatal - else end = newend; - - // Put all the integer values in IETF byte-order (MSB first, LSB second) - SwapDNSHeaderBytes(msg); - - if (authInfo) DNSDigest_SignMessage(msg, &end, authInfo, 0); // DNSDigest_SignMessage operates on message in network byte order - if (!end) { LogMsg("mDNSSendDNSMessage: DNSDigest_SignMessage failed"); status = mStatus_NoMemoryErr; } - else - { - // Send the packet on the wire - if (!sock) - status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, src, dst, dstport); - else - { - mDNSu16 msglen = (mDNSu16)(end - (mDNSu8 *)msg); - mDNSu8 lenbuf[2] = { (mDNSu8)(msglen >> 8), (mDNSu8)(msglen & 0xFF) }; - long nsent = mDNSPlatformWriteTCP(sock, (char*)lenbuf, 2); // Should do scatter/gather here -- this is probably going out as two packets - if (nsent != 2) { LogMsg("mDNSSendDNSMessage: write msg length failed %d/%d", nsent, 2); status = mStatus_ConnFailed; } - else - { - nsent = mDNSPlatformWriteTCP(sock, (char *)msg, msglen); - if (nsent != msglen) { LogMsg("mDNSSendDNSMessage: write msg body failed %d/%d", nsent, msglen); status = mStatus_ConnFailed; } - } - } - } - - // Swap the integer values back the way they were (remember that numAdditionals may have been changed by putHINFO and/or SignMessage) - SwapDNSHeaderBytes(msg); - - // Dump the packet with the HINFO and TSIG - if (mDNS_PacketLoggingEnabled && !mDNSOpaque16IsZero(msg->h.id)) - DumpPacket(m, status, mDNStrue, sock && (sock->flags & kTCPSocketFlags_UseTLS) ? "TLS" : sock ? "TCP" : "UDP", mDNSNULL, src ? src->port : MulticastDNSPort, dst, dstport, msg, end); - - // put the number of additionals back the way it was - msg->h.numAdditionals = numAdditionals; - - return(status); - } + mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, + mDNSIPPort dstport, TCPSocket *sock, DomainAuthInfo *authInfo, + mDNSBool useBackgroundTrafficClass) +{ + mStatus status = mStatus_NoError; + const mDNSu16 numAdditionals = msg->h.numAdditionals; + mDNSu8 *newend; + mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData; + + // Zero-length message data is okay (e.g. for a DNS Update ack, where all we need is an ID and an error code + if (end < msg->data || end - msg->data > AbsoluteMaxDNSMessageData) + { + LogMsg("mDNSSendDNSMessage: invalid message %p %p %d", msg->data, end, end - msg->data); + return mStatus_BadParamErr; + } + + newend = putHINFO(m, msg, end, authInfo, limit); + if (!newend) LogMsg("mDNSSendDNSMessage: putHINFO failed msg %p end %p, limit %p", msg->data, end, limit); // Not fatal + else end = newend; + + // Put all the integer values in IETF byte-order (MSB first, LSB second) + SwapDNSHeaderBytes(msg); + + if (authInfo) DNSDigest_SignMessage(msg, &end, authInfo, 0); // DNSDigest_SignMessage operates on message in network byte order + if (!end) { LogMsg("mDNSSendDNSMessage: DNSDigest_SignMessage failed"); status = mStatus_NoMemoryErr; } + else + { + // Send the packet on the wire + if (!sock) + status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, src, dst, dstport, useBackgroundTrafficClass); + else + { + mDNSu16 msglen = (mDNSu16)(end - (mDNSu8 *)msg); + mDNSu8 lenbuf[2] = { (mDNSu8)(msglen >> 8), (mDNSu8)(msglen & 0xFF) }; + long nsent = mDNSPlatformWriteTCP(sock, (char*)lenbuf, 2); // Should do scatter/gather here -- this is probably going out as two packets + if (nsent != 2) { LogMsg("mDNSSendDNSMessage: write msg length failed %d/%d", nsent, 2); status = mStatus_ConnFailed; } + else + { + nsent = mDNSPlatformWriteTCP(sock, (char *)msg, msglen); + if (nsent != msglen) { LogMsg("mDNSSendDNSMessage: write msg body failed %d/%d", nsent, msglen); status = mStatus_ConnFailed; } + } + } + } + + // Swap the integer values back the way they were (remember that numAdditionals may have been changed by putHINFO and/or SignMessage) + SwapDNSHeaderBytes(msg); + + // Dump the packet with the HINFO and TSIG + if (mDNS_PacketLoggingEnabled && !mDNSOpaque16IsZero(msg->h.id)) + DumpPacket(m, status, mDNStrue, sock && (sock->flags & kTCPSocketFlags_UseTLS) ? "TLS" : sock ? "TCP" : "UDP", mDNSNULL, src ? src->port : MulticastDNSPort, dst, dstport, msg, end); + + // put the number of additionals back the way it was + msg->h.numAdditionals = numAdditionals; + + return(status); +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -2662,185 +3470,191 @@ mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNS #endif mDNSexport void mDNS_Lock_(mDNS *const m, const char * const functionname) - { - // 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("%s: mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy); +{ + // 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("%s: mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy); #if ForceAlerts - *(long*)0 = 0; + *(long*)0 = 0; #endif - } - - // 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("%s: mDNS_Lock: m->timenow already set (%ld/%ld)", functionname, m->timenow, mDNS_TimeNow_NoLock(m)); - m->timenow = mDNS_TimeNow_NoLock(m); - if (m->timenow == 0) m->timenow = 1; - } - else if (m->timenow == 0) - { - LogMsg("%s: mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", functionname, m->mDNS_busy); - m->timenow = mDNS_TimeNow_NoLock(m); - if (m->timenow == 0) m->timenow = 1; - } - - if (m->timenow_last - m->timenow > 0) - { - m->timenow_adjust += m->timenow_last - m->timenow; - LogMsg("%s: mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", functionname, 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++; - } + } + + // 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("%s: mDNS_Lock: m->timenow already set (%ld/%ld)", functionname, m->timenow, mDNS_TimeNow_NoLock(m)); + m->timenow = mDNS_TimeNow_NoLock(m); + if (m->timenow == 0) m->timenow = 1; + } + else if (m->timenow == 0) + { + LogMsg("%s: mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", functionname, m->mDNS_busy); + m->timenow = mDNS_TimeNow_NoLock(m); + if (m->timenow == 0) m->timenow = 1; + } + + if (m->timenow_last - m->timenow > 0) + { + m->timenow_adjust += m->timenow_last - m->timenow; + LogMsg("%s: mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", functionname, m->timenow_last - m->timenow, m->timenow_adjust); + m->timenow = m->timenow_last; + } + m->timenow_last = m->timenow; + + // Increment mDNS_busy so we'll recognise re-entrant calls + m->mDNS_busy++; +} mDNSlocal AuthRecord *AnyLocalRecordReady(const mDNS *const m) - { - AuthRecord *rr; - for (rr = m->NewLocalRecords; rr; rr = rr->next) - if (LocalRecordReady(rr)) return rr; - return mDNSNULL; - } +{ + AuthRecord *rr; + for (rr = m->NewLocalRecords; rr; rr = rr->next) + if (LocalRecordReady(rr)) return rr; + return mDNSNULL; +} mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m) - { - mDNSs32 e = m->timenow + 0x78000000; - if (m->mDNSPlatformStatus != mStatus_NoError) 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->NewLocalRecords && AnyLocalRecordReady(m)) return(m->timenow); - if (m->NewLocalOnlyRecords) return(m->timenow); - if (m->SPSProxyListChanged) return(m->timenow); - if (m->LocalRemoveEvents) return(m->timenow); +{ + mDNSs32 e = m->timenow + 0x78000000; + if (m->mDNSPlatformStatus != mStatus_NoError) 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->NewLocalRecords && AnyLocalRecordReady(m)) return(m->timenow); + if (m->NewLocalOnlyRecords) return(m->timenow); + if (m->SPSProxyListChanged) return(m->timenow); + if (m->LocalRemoveEvents) return(m->timenow); #ifndef UNICAST_DISABLED - if (e - m->NextuDNSEvent > 0) e = m->NextuDNSEvent; - if (e - m->NextScheduledNATOp > 0) e = m->NextScheduledNATOp; - if (m->NextSRVUpdate && e - m->NextSRVUpdate > 0) e = m->NextSRVUpdate; + if (e - m->NextuDNSEvent > 0) e = m->NextuDNSEvent; + if (e - m->NextScheduledNATOp > 0) e = m->NextScheduledNATOp; + if (m->NextSRVUpdate && e - m->NextSRVUpdate > 0) e = m->NextSRVUpdate; #endif - if (e - m->NextCacheCheck > 0) e = m->NextCacheCheck; - if (e - m->NextScheduledSPS > 0) e = m->NextScheduledSPS; - // NextScheduledSPRetry only valid when DelaySleep not set - if (!m->DelaySleep && m->SleepLimit && e - m->NextScheduledSPRetry > 0) e = m->NextScheduledSPRetry; - if (m->DelaySleep && e - m->DelaySleep > 0) e = m->DelaySleep; - - if (m->SuppressSending) - { - if (e - m->SuppressSending > 0) e = m->SuppressSending; - } - else - { - if (e - m->NextScheduledQuery > 0) e = m->NextScheduledQuery; - if (e - m->NextScheduledProbe > 0) e = m->NextScheduledProbe; - if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse; - } - if (e - m->NextScheduledStopTime > 0) e = m->NextScheduledStopTime; - return(e); - } + if (e - m->NextCacheCheck > 0) e = m->NextCacheCheck; + if (e - m->NextScheduledSPS > 0) e = m->NextScheduledSPS; + if (e - m->NextScheduledKA > 0) e = m->NextScheduledKA; + if (m->clearIgnoreNA && (e - m->clearIgnoreNA > 0)) e = m->clearIgnoreNA; + + // NextScheduledSPRetry only valid when DelaySleep not set + if (!m->DelaySleep && m->SleepLimit && e - m->NextScheduledSPRetry > 0) e = m->NextScheduledSPRetry; + if (m->DelaySleep && e - m->DelaySleep > 0) e = m->DelaySleep; + + if (m->SuppressSending) + { + if (e - m->SuppressSending > 0) e = m->SuppressSending; + } + else + { + if (e - m->NextScheduledQuery > 0) e = m->NextScheduledQuery; + if (e - m->NextScheduledProbe > 0) e = m->NextScheduledProbe; + if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse; + } + if (e - m->NextScheduledStopTime > 0) e = m->NextScheduledStopTime; + return(e); +} mDNSexport void ShowTaskSchedulingError(mDNS *const m) - { - AuthRecord *rr; - mDNS_Lock(m); +{ + AuthRecord *rr; + mDNS_Lock(m); + + LogMsg("Task Scheduling Error: Continuously busy for more than a second"); + + // Note: To accurately diagnose *why* we're busy, the debugging code here needs to mirror the logic in GetNextScheduledEvent above - LogMsg("Task Scheduling Error: Continuously busy for more than a second"); - - // Note: To accurately diagnose *why* we're busy, the debugging code here needs to mirror the logic in GetNextScheduledEvent above + if (m->NewQuestions && (!m->NewQuestions->DelayAnswering || m->timenow - m->NewQuestions->DelayAnswering >= 0)) + LogMsg("Task Scheduling Error: NewQuestion %##s (%s)", + m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype)); - if (m->NewQuestions && (!m->NewQuestions->DelayAnswering || m->timenow - m->NewQuestions->DelayAnswering >= 0)) - LogMsg("Task Scheduling Error: NewQuestion %##s (%s)", - m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype)); + if (m->NewLocalOnlyQuestions) + LogMsg("Task Scheduling Error: NewLocalOnlyQuestions %##s (%s)", + m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype)); - if (m->NewLocalOnlyQuestions) - LogMsg("Task Scheduling Error: NewLocalOnlyQuestions %##s (%s)", - m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype)); + if (m->NewLocalRecords) + { + rr = AnyLocalRecordReady(m); + if (rr) LogMsg("Task Scheduling Error: NewLocalRecords %s", ARDisplayString(m, rr)); + } - if (m->NewLocalRecords) - { - rr = AnyLocalRecordReady(m); - if (rr) LogMsg("Task Scheduling Error: NewLocalRecords %s", ARDisplayString(m, rr)); - } - - if (m->NewLocalOnlyRecords) LogMsg("Task Scheduling Error: NewLocalOnlyRecords"); + if (m->NewLocalOnlyRecords) LogMsg("Task Scheduling Error: NewLocalOnlyRecords"); - if (m->SPSProxyListChanged) LogMsg("Task Scheduling Error: SPSProxyListChanged"); - if (m->LocalRemoveEvents) LogMsg("Task Scheduling Error: LocalRemoveEvents"); + if (m->SPSProxyListChanged) LogMsg("Task Scheduling Error: SPSProxyListChanged"); + if (m->LocalRemoveEvents) LogMsg("Task Scheduling Error: LocalRemoveEvents"); - if (m->timenow - m->NextScheduledEvent >= 0) - LogMsg("Task Scheduling Error: m->NextScheduledEvent %d", m->timenow - m->NextScheduledEvent); + if (m->timenow - m->NextScheduledEvent >= 0) + LogMsg("Task Scheduling Error: m->NextScheduledEvent %d", m->timenow - m->NextScheduledEvent); #ifndef UNICAST_DISABLED - if (m->timenow - m->NextuDNSEvent >= 0) - LogMsg("Task Scheduling Error: m->NextuDNSEvent %d", m->timenow - m->NextuDNSEvent); - if (m->timenow - m->NextScheduledNATOp >= 0) - LogMsg("Task Scheduling Error: m->NextScheduledNATOp %d", m->timenow - m->NextScheduledNATOp); - if (m->NextSRVUpdate && m->timenow - m->NextSRVUpdate >= 0) - LogMsg("Task Scheduling Error: m->NextSRVUpdate %d", m->timenow - m->NextSRVUpdate); + if (m->timenow - m->NextuDNSEvent >= 0) + LogMsg("Task Scheduling Error: m->NextuDNSEvent %d", m->timenow - m->NextuDNSEvent); + if (m->timenow - m->NextScheduledNATOp >= 0) + LogMsg("Task Scheduling Error: m->NextScheduledNATOp %d", m->timenow - m->NextScheduledNATOp); + if (m->NextSRVUpdate && m->timenow - m->NextSRVUpdate >= 0) + LogMsg("Task Scheduling Error: m->NextSRVUpdate %d", m->timenow - m->NextSRVUpdate); #endif - if (m->timenow - m->NextCacheCheck >= 0) - LogMsg("Task Scheduling Error: m->NextCacheCheck %d", m->timenow - m->NextCacheCheck); - if (m->timenow - m->NextScheduledSPS >= 0) - LogMsg("Task Scheduling Error: m->NextScheduledSPS %d", m->timenow - m->NextScheduledSPS); - if (!m->DelaySleep && m->SleepLimit && m->timenow - m->NextScheduledSPRetry >= 0) - LogMsg("Task Scheduling Error: m->NextScheduledSPRetry %d", m->timenow - m->NextScheduledSPRetry); - if (m->DelaySleep && m->timenow - m->DelaySleep >= 0) - LogMsg("Task Scheduling Error: m->DelaySleep %d", m->timenow - m->DelaySleep); - - if (m->SuppressSending && m->timenow - m->SuppressSending >= 0) - LogMsg("Task Scheduling Error: m->SuppressSending %d", m->timenow - m->SuppressSending); - if (m->timenow - m->NextScheduledQuery >= 0) - LogMsg("Task Scheduling Error: m->NextScheduledQuery %d", m->timenow - m->NextScheduledQuery); - if (m->timenow - m->NextScheduledProbe >= 0) - LogMsg("Task Scheduling Error: m->NextScheduledProbe %d", m->timenow - m->NextScheduledProbe); - if (m->timenow - m->NextScheduledResponse >= 0) - LogMsg("Task Scheduling Error: m->NextScheduledResponse %d", m->timenow - m->NextScheduledResponse); - - mDNS_Unlock(m); - } + if (m->timenow - m->NextCacheCheck >= 0) + LogMsg("Task Scheduling Error: m->NextCacheCheck %d", m->timenow - m->NextCacheCheck); + if (m->timenow - m->NextScheduledSPS >= 0) + LogMsg("Task Scheduling Error: m->NextScheduledSPS %d", m->timenow - m->NextScheduledSPS); + if (m->timenow - m->NextScheduledKA >= 0) + LogMsg("Task Scheduling Error: m->NextScheduledKA %d", m->timenow - m->NextScheduledKA); + if (!m->DelaySleep && m->SleepLimit && m->timenow - m->NextScheduledSPRetry >= 0) + LogMsg("Task Scheduling Error: m->NextScheduledSPRetry %d", m->timenow - m->NextScheduledSPRetry); + if (m->DelaySleep && m->timenow - m->DelaySleep >= 0) + LogMsg("Task Scheduling Error: m->DelaySleep %d", m->timenow - m->DelaySleep); + + if (m->SuppressSending && m->timenow - m->SuppressSending >= 0) + LogMsg("Task Scheduling Error: m->SuppressSending %d", m->timenow - m->SuppressSending); + if (m->timenow - m->NextScheduledQuery >= 0) + LogMsg("Task Scheduling Error: m->NextScheduledQuery %d", m->timenow - m->NextScheduledQuery); + if (m->timenow - m->NextScheduledProbe >= 0) + LogMsg("Task Scheduling Error: m->NextScheduledProbe %d", m->timenow - m->NextScheduledProbe); + if (m->timenow - m->NextScheduledResponse >= 0) + LogMsg("Task Scheduling Error: m->NextScheduledResponse %d", m->timenow - m->NextScheduledResponse); + if (m->clearIgnoreNA && m->timenow - m->clearIgnoreNA >= 0) + LogMsg("Task Scheduling Error: m->clearIgnoreNA %d", m->timenow - m->clearIgnoreNA); + mDNS_Unlock(m); +} mDNSexport void mDNS_Unlock_(mDNS *const m, const char * const functionname) - { - // Decrement mDNS_busy - m->mDNS_busy--; - - // Check for locking failures - if (m->mDNS_busy != m->mDNS_reentrancy) - { - LogMsg("%s: mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy); +{ + // Decrement mDNS_busy + m->mDNS_busy--; + + // Check for locking failures + if (m->mDNS_busy != m->mDNS_reentrancy) + { + LogMsg("%s: mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy); #if ForceAlerts - *(long*)0 = 0; + *(long*)0 = 0; #endif - } + } - // 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("%s: mDNS_Unlock: ERROR! m->timenow aready zero", functionname); - m->timenow = 0; - } + // 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("%s: mDNS_Unlock: ERROR! m->timenow aready zero", functionname); + m->timenow = 0; + } - // MUST release the platform lock LAST! - mDNSPlatformUnlock(m); - } + // MUST release the platform lock LAST! + mDNSPlatformUnlock(m); +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -2849,305 +3663,305 @@ mDNSexport void mDNS_Unlock_(mDNS *const m, const char * const functionname) #endif static const struct mDNSprintf_format - { - unsigned leftJustify : 1; - unsigned forceSign : 1; - unsigned zeroPad : 1; - unsigned havePrecision : 1; - unsigned hSize : 1; - unsigned lSize : 1; - char altForm; - char sign; // +, - or space - unsigned int fieldWidth; - unsigned int precision; - } mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +{ + unsigned leftJustify : 1; + unsigned forceSign : 1; + unsigned zeroPad : 1; + unsigned havePrecision : 1; + unsigned hSize : 1; + unsigned lSize : 1; + char altForm; + char sign; // +, - or space + unsigned int fieldWidth; + unsigned int precision; +} mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg) - { - mDNSu32 nwritten = 0; - int c; - if (buflen == 0) return(0); - buflen--; // Pre-reserve one space in the buffer for the terminating null - if (buflen == 0) goto exit; - - for (c = *fmt; c != 0; c = *++fmt) - { - if (c != '%') - { - *sbuffer++ = (char)c; - if (++nwritten >= buflen) goto exit; - } - else - { - unsigned int i=0, j; - // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for - // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc. - // The size needs to be enough for a 256-byte domain name plus some error text. - #define mDNS_VACB_Size 300 - char mDNS_VACB[mDNS_VACB_Size]; - #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size]) - #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s)) - char *s = mDNS_VACB_Lim, *digits; - struct mDNSprintf_format F = mDNSprintf_format_default; - - while (1) // decode flags - { - c = *++fmt; - if (c == '-') F.leftJustify = 1; - else if (c == '+') F.forceSign = 1; - else if (c == ' ') F.sign = ' '; - else if (c == '#') F.altForm++; - else if (c == '0') F.zeroPad = 1; - else break; - } - - if (c == '*') // decode field width - { - int f = va_arg(arg, int); - if (f < 0) { f = -f; F.leftJustify = 1; } - F.fieldWidth = (unsigned int)f; - c = *++fmt; - } - else - { - for (; c >= '0' && c <= '9'; c = *++fmt) - F.fieldWidth = (10 * F.fieldWidth) + (c - '0'); - } - - if (c == '.') // decode precision - { - if ((c = *++fmt) == '*') - { F.precision = va_arg(arg, unsigned int); c = *++fmt; } - else for (; c >= '0' && c <= '9'; c = *++fmt) - F.precision = (10 * F.precision) + (c - '0'); - F.havePrecision = 1; - } - - if (F.leftJustify) F.zeroPad = 0; - - conv: - switch (c) // perform appropriate conversion - { - unsigned long n; - case 'h' : F.hSize = 1; c = *++fmt; goto conv; - case 'l' : // fall through - case 'L' : F.lSize = 1; c = *++fmt; goto conv; - case 'd' : - case 'i' : if (F.lSize) n = (unsigned long)va_arg(arg, long); - else n = (unsigned long)va_arg(arg, int); - if (F.hSize) n = (short) n; - if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; } - else if (F.forceSign) F.sign = '+'; - goto decimal; - case 'u' : if (F.lSize) n = va_arg(arg, unsigned long); - else n = va_arg(arg, unsigned int); - if (F.hSize) n = (unsigned short) n; - F.sign = 0; - goto decimal; - decimal: if (!F.havePrecision) - { - if (F.zeroPad) - { - F.precision = F.fieldWidth; - if (F.sign) --F.precision; - } - if (F.precision < 1) F.precision = 1; - } - if (F.precision > mDNS_VACB_Size - 1) - F.precision = mDNS_VACB_Size - 1; - for (i = 0; n; n /= 10, i++) *--s = (char)(n % 10 + '0'); - for (; i < F.precision; i++) *--s = '0'; - if (F.sign) { *--s = F.sign; i++; } - break; - - case 'o' : if (F.lSize) n = va_arg(arg, unsigned long); - else n = va_arg(arg, unsigned int); - if (F.hSize) n = (unsigned short) n; - if (!F.havePrecision) - { - if (F.zeroPad) F.precision = F.fieldWidth; - if (F.precision < 1) F.precision = 1; - } - if (F.precision > mDNS_VACB_Size - 1) - F.precision = mDNS_VACB_Size - 1; - for (i = 0; n; n /= 8, i++) *--s = (char)(n % 8 + '0'); - if (F.altForm && i && *s != '0') { *--s = '0'; i++; } - for (; i < F.precision; i++) *--s = '0'; - break; - - case 'a' : { - unsigned char *a = va_arg(arg, unsigned char *); - if (!a) { static char emsg[] = "<>"; s = emsg; i = sizeof(emsg)-1; } - else - { - s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end - if (F.altForm) - { - mDNSAddr *ip = (mDNSAddr*)a; - switch (ip->type) - { - case mDNSAddrType_IPv4: F.precision = 4; a = (unsigned char *)&ip->ip.v4; break; - case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break; - default: F.precision = 0; break; - } - } - if (F.altForm && !F.precision) - i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "«ZERO ADDRESS»"); - else switch (F.precision) - { - case 4: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d", - a[0], a[1], a[2], a[3]); break; - case 6: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X", - a[0], a[1], a[2], a[3], a[4], a[5]); break; - case 16: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), - "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X", - a[0x0], a[0x1], a[0x2], a[0x3], a[0x4], a[0x5], a[0x6], a[0x7], - a[0x8], a[0x9], a[0xA], a[0xB], a[0xC], a[0xD], a[0xE], a[0xF]); break; - default: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify" - " address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break; - } - } - } - break; - - case 'p' : F.havePrecision = F.lSize = 1; - F.precision = sizeof(void*) * 2; // 8 characters on 32-bit; 16 characters on 64-bit - case 'X' : digits = "0123456789ABCDEF"; - goto hexadecimal; - case 'x' : digits = "0123456789abcdef"; - hexadecimal:if (F.lSize) n = va_arg(arg, unsigned long); - else n = va_arg(arg, unsigned int); - if (F.hSize) n = (unsigned short) n; - if (!F.havePrecision) - { - if (F.zeroPad) - { - F.precision = F.fieldWidth; - if (F.altForm) F.precision -= 2; - } - if (F.precision < 1) F.precision = 1; - } - if (F.precision > mDNS_VACB_Size - 1) - F.precision = mDNS_VACB_Size - 1; - for (i = 0; n; n /= 16, i++) *--s = digits[n % 16]; - for (; i < F.precision; i++) *--s = '0'; - if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; } - break; - - case 'c' : *--s = (char)va_arg(arg, int); i = 1; break; - - case 's' : s = va_arg(arg, char *); - if (!s) { static char emsg[] = "<>"; s = emsg; i = sizeof(emsg)-1; } - else switch (F.altForm) - { - case 0: i=0; - if (!F.havePrecision) // C string - while (s[i]) i++; - else - { - while ((i < F.precision) && s[i]) i++; - // Make sure we don't truncate in the middle of a UTF-8 character - // If last character we got was any kind of UTF-8 multi-byte character, - // then see if we have to back up. - // This is not as easy as the similar checks below, because - // here we can't assume it's safe to examine the *next* byte, so we - // have to confine ourselves to working only backwards in the string. - j = i; // Record where we got to - // Now, back up until we find first non-continuation-char - while (i>0 && (s[i-1] & 0xC0) == 0x80) i--; - // Now s[i-1] is the first non-continuation-char - // and (j-i) is the number of continuation-chars we found - if (i>0 && (s[i-1] & 0xC0) == 0xC0) // If we found a start-char - { - i--; // Tentatively eliminate this start-char as well - // Now (j-i) is the number of characters we're considering eliminating. - // To be legal UTF-8, the start-char must contain (j-i) one-bits, - // followed by a zero bit. If we shift it right by (7-(j-i)) bits - // (with sign extension) then the result has to be 0xFE. - // If this is right, then we reinstate the tentatively eliminated bytes. - if (((j-i) < 7) && (((s[i] >> (7-(j-i))) & 0xFF) == 0xFE)) i = j; - } - } - break; - case 1: i = (unsigned char) *s++; break; // Pascal string - case 2: { // DNS label-sequence name - unsigned char *a = (unsigned char *)s; - s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end - if (*a == 0) *s++ = '.'; // Special case for root DNS name - while (*a) - { - char buf[63*4+1]; - if (*a > 63) - { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<>", *a); break; } - if (s + *a >= &mDNS_VACB[254]) - { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<>"); break; } - // Need to use ConvertDomainLabelToCString to do proper escaping here, - // so it's clear what's a literal dot and what's a label separator - ConvertDomainLabelToCString((domainlabel*)a, buf); - s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "%s.", buf); - a += 1 + *a; - } - i = (mDNSu32)(s - mDNS_VACB); - s = mDNS_VACB; // Reset s back to the start of the buffer - break; - } - } - // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below) - if (F.havePrecision && i > F.precision) - { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; } - break; - - case 'n' : s = va_arg(arg, char *); - if (F.hSize) * (short *) s = (short)nwritten; - else if (F.lSize) * (long *) s = (long)nwritten; - else * (int *) s = (int)nwritten; - continue; - - default: s = mDNS_VACB; - i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "<>", c); - - case '%' : *sbuffer++ = (char)c; - if (++nwritten >= buflen) goto exit; - break; - } - - if (i < F.fieldWidth && !F.leftJustify) // Pad on the left - do { - *sbuffer++ = ' '; - if (++nwritten >= buflen) goto exit; - } while (i < --F.fieldWidth); - - // Make sure we don't truncate in the middle of a UTF-8 character. - // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the - // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half, - // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly - // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated). - if (i > buflen - nwritten) - { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--; } - for (j=0; j= buflen) goto exit; - - for (; i < F.fieldWidth; i++) // Pad on the right - { - *sbuffer++ = ' '; - if (++nwritten >= buflen) goto exit; - } - } - } - exit: - *sbuffer++ = 0; - return(nwritten); - } +{ + mDNSu32 nwritten = 0; + int c; + if (buflen == 0) return(0); + buflen--; // Pre-reserve one space in the buffer for the terminating null + if (buflen == 0) goto exit; + + for (c = *fmt; c != 0; c = *++fmt) + { + if (c != '%') + { + *sbuffer++ = (char)c; + if (++nwritten >= buflen) goto exit; + } + else + { + unsigned int i=0, j; + // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for + // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc. + // The size needs to be enough for a 256-byte domain name plus some error text. + #define mDNS_VACB_Size 300 + char mDNS_VACB[mDNS_VACB_Size]; + #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size]) + #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s)) + char *s = mDNS_VACB_Lim, *digits; + struct mDNSprintf_format F = mDNSprintf_format_default; + + while (1) // decode flags + { + c = *++fmt; + if (c == '-') F.leftJustify = 1; + else if (c == '+') F.forceSign = 1; + else if (c == ' ') F.sign = ' '; + else if (c == '#') F.altForm++; + else if (c == '0') F.zeroPad = 1; + else break; + } + + if (c == '*') // decode field width + { + int f = va_arg(arg, int); + if (f < 0) { f = -f; F.leftJustify = 1; } + F.fieldWidth = (unsigned int)f; + c = *++fmt; + } + else + { + for (; c >= '0' && c <= '9'; c = *++fmt) + F.fieldWidth = (10 * F.fieldWidth) + (c - '0'); + } + + if (c == '.') // decode precision + { + if ((c = *++fmt) == '*') + { F.precision = va_arg(arg, unsigned int); c = *++fmt; } + else for (; c >= '0' && c <= '9'; c = *++fmt) + F.precision = (10 * F.precision) + (c - '0'); + F.havePrecision = 1; + } + + if (F.leftJustify) F.zeroPad = 0; + +conv: + switch (c) // perform appropriate conversion + { + unsigned long n; + case 'h': F.hSize = 1; c = *++fmt; goto conv; + case 'l': // fall through + case 'L': F.lSize = 1; c = *++fmt; goto conv; + case 'd': + case 'i': if (F.lSize) n = (unsigned long)va_arg(arg, long); + else n = (unsigned long)va_arg(arg, int); + if (F.hSize) n = (short) n; + if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; } + else if (F.forceSign) F.sign = '+'; + goto decimal; + case 'u': if (F.lSize) n = va_arg(arg, unsigned long); + else n = va_arg(arg, unsigned int); + if (F.hSize) n = (unsigned short) n; + F.sign = 0; + goto decimal; +decimal: if (!F.havePrecision) + { + if (F.zeroPad) + { + F.precision = F.fieldWidth; + if (F.sign) --F.precision; + } + if (F.precision < 1) F.precision = 1; + } + if (F.precision > mDNS_VACB_Size - 1) + F.precision = mDNS_VACB_Size - 1; + for (i = 0; n; n /= 10, i++) *--s = (char)(n % 10 + '0'); + for (; i < F.precision; i++) *--s = '0'; + if (F.sign) { *--s = F.sign; i++; } + break; + + case 'o': if (F.lSize) n = va_arg(arg, unsigned long); + else n = va_arg(arg, unsigned int); + if (F.hSize) n = (unsigned short) n; + if (!F.havePrecision) + { + if (F.zeroPad) F.precision = F.fieldWidth; + if (F.precision < 1) F.precision = 1; + } + if (F.precision > mDNS_VACB_Size - 1) + F.precision = mDNS_VACB_Size - 1; + for (i = 0; n; n /= 8, i++) *--s = (char)(n % 8 + '0'); + if (F.altForm && i && *s != '0') { *--s = '0'; i++; } + for (; i < F.precision; i++) *--s = '0'; + break; + + case 'a': { + unsigned char *a = va_arg(arg, unsigned char *); + if (!a) { static char emsg[] = "<>"; s = emsg; i = sizeof(emsg)-1; } + else + { + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + if (F.altForm) + { + mDNSAddr *ip = (mDNSAddr*)a; + switch (ip->type) + { + case mDNSAddrType_IPv4: F.precision = 4; a = (unsigned char *)&ip->ip.v4; break; + case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break; + default: F.precision = 0; break; + } + } + if (F.altForm && !F.precision) + i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "«ZERO ADDRESS»"); + else switch (F.precision) + { + case 4: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d", + a[0], a[1], a[2], a[3]); break; + case 6: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X", + a[0], a[1], a[2], a[3], a[4], a[5]); break; + case 16: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), + "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X", + a[0x0], a[0x1], a[0x2], a[0x3], a[0x4], a[0x5], a[0x6], a[0x7], + a[0x8], a[0x9], a[0xA], a[0xB], a[0xC], a[0xD], a[0xE], a[0xF]); break; + default: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify" + " address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break; + } + } + } + break; + + case 'p': F.havePrecision = F.lSize = 1; + F.precision = sizeof(void*) * 2; // 8 characters on 32-bit; 16 characters on 64-bit + case 'X': digits = "0123456789ABCDEF"; + goto hexadecimal; + case 'x': digits = "0123456789abcdef"; +hexadecimal: if (F.lSize) n = va_arg(arg, unsigned long); + else n = va_arg(arg, unsigned int); + if (F.hSize) n = (unsigned short) n; + if (!F.havePrecision) + { + if (F.zeroPad) + { + F.precision = F.fieldWidth; + if (F.altForm) F.precision -= 2; + } + if (F.precision < 1) F.precision = 1; + } + if (F.precision > mDNS_VACB_Size - 1) + F.precision = mDNS_VACB_Size - 1; + for (i = 0; n; n /= 16, i++) *--s = digits[n % 16]; + for (; i < F.precision; i++) *--s = '0'; + if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; } + break; + + case 'c': *--s = (char)va_arg(arg, int); i = 1; break; + + case 's': s = va_arg(arg, char *); + if (!s) { static char emsg[] = "<>"; s = emsg; i = sizeof(emsg)-1; } + else switch (F.altForm) + { + case 0: i=0; + if (!F.havePrecision) // C string + while (s[i]) i++; + else + { + while ((i < F.precision) && s[i]) i++; + // Make sure we don't truncate in the middle of a UTF-8 character + // If last character we got was any kind of UTF-8 multi-byte character, + // then see if we have to back up. + // This is not as easy as the similar checks below, because + // here we can't assume it's safe to examine the *next* byte, so we + // have to confine ourselves to working only backwards in the string. + j = i; // Record where we got to + // Now, back up until we find first non-continuation-char + while (i>0 && (s[i-1] & 0xC0) == 0x80) i--; + // Now s[i-1] is the first non-continuation-char + // and (j-i) is the number of continuation-chars we found + if (i>0 && (s[i-1] & 0xC0) == 0xC0) // If we found a start-char + { + i--; // Tentatively eliminate this start-char as well + // Now (j-i) is the number of characters we're considering eliminating. + // To be legal UTF-8, the start-char must contain (j-i) one-bits, + // followed by a zero bit. If we shift it right by (7-(j-i)) bits + // (with sign extension) then the result has to be 0xFE. + // If this is right, then we reinstate the tentatively eliminated bytes. + if (((j-i) < 7) && (((s[i] >> (7-(j-i))) & 0xFF) == 0xFE)) i = j; + } + } + break; + case 1: i = (unsigned char) *s++; break; // Pascal string + case 2: { // DNS label-sequence name + unsigned char *a = (unsigned char *)s; + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + if (*a == 0) *s++ = '.'; // Special case for root DNS name + while (*a) + { + char buf[63*4+1]; + if (*a > 63) + { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<>", *a); break; } + if (s + *a >= &mDNS_VACB[254]) + { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<>"); break; } + // Need to use ConvertDomainLabelToCString to do proper escaping here, + // so it's clear what's a literal dot and what's a label separator + ConvertDomainLabelToCString((domainlabel*)a, buf); + s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "%s.", buf); + a += 1 + *a; + } + i = (mDNSu32)(s - mDNS_VACB); + s = mDNS_VACB; // Reset s back to the start of the buffer + break; + } + } + // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below) + if (F.havePrecision && i > F.precision) + { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--;} + break; + + case 'n': s = va_arg(arg, char *); + if (F.hSize) *(short *) s = (short)nwritten; + else if (F.lSize) *(long *) s = (long)nwritten; + else *(int *) s = (int)nwritten; + continue; + + default: s = mDNS_VACB; + i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "<>", c); + + case '%': *sbuffer++ = (char)c; + if (++nwritten >= buflen) goto exit; + break; + } + + if (i < F.fieldWidth && !F.leftJustify) // Pad on the left + do { + *sbuffer++ = ' '; + if (++nwritten >= buflen) goto exit; + } while (i < --F.fieldWidth); + + // Make sure we don't truncate in the middle of a UTF-8 character. + // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the + // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half, + // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly + // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated). + if (i > buflen - nwritten) + { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--;} + for (j=0; j= buflen) goto exit; + + for (; i < F.fieldWidth; i++) // Pad on the right + { + *sbuffer++ = ' '; + if (++nwritten >= buflen) goto exit; + } + } + } +exit: + *sbuffer++ = 0; + return(nwritten); +} mDNSexport mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...) - { - mDNSu32 length; - - va_list ptr; - va_start(ptr,fmt); - length = mDNS_vsnprintf(sbuffer, buflen, fmt, ptr); - va_end(ptr); - - return(length); - } +{ + mDNSu32 length; + + va_list ptr; + va_start(ptr,fmt); + length = mDNS_vsnprintf(sbuffer, buflen, fmt, ptr); + va_end(ptr); + + return(length); +} diff --git a/mDNSCore/DNSCommon.h b/mDNSCore/DNSCommon.h index 5df4ce4..fe8ccba 100644 --- a/mDNSCore/DNSCommon.h +++ b/mDNSCore/DNSCommon.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,8 +20,8 @@ #include "mDNSEmbeddedAPI.h" -#ifdef __cplusplus - extern "C" { +#ifdef __cplusplus +extern "C" { #endif //************************************************************************************************************* @@ -30,7 +30,7 @@ // 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) // *************************************************************************** @@ -39,50 +39,50 @@ #endif typedef enum - { - kDNSFlag0_QR_Mask = 0x80, // Query or response? - kDNSFlag0_QR_Query = 0x00, - kDNSFlag0_QR_Response = 0x80, - - kDNSFlag0_OP_Mask = 0x78, // Operation type - kDNSFlag0_OP_StdQuery = 0x00, - kDNSFlag0_OP_Iquery = 0x08, - kDNSFlag0_OP_Status = 0x10, - kDNSFlag0_OP_Unused3 = 0x18, - kDNSFlag0_OP_Notify = 0x20, - kDNSFlag0_OP_Update = 0x28, - - kDNSFlag0_QROP_Mask = kDNSFlag0_QR_Mask | kDNSFlag0_OP_Mask, - - kDNSFlag0_AA = 0x04, // Authoritative Answer? - kDNSFlag0_TC = 0x02, // Truncated? - kDNSFlag0_RD = 0x01, // Recursion Desired? - kDNSFlag1_RA = 0x80, // Recursion Available? - - kDNSFlag1_Zero = 0x40, // Reserved; must be zero - kDNSFlag1_AD = 0x20, // Authentic Data [RFC 2535] - kDNSFlag1_CD = 0x10, // Checking Disabled [RFC 2535] - - kDNSFlag1_RC_Mask = 0x0F, // Response code - kDNSFlag1_RC_NoErr = 0x00, - kDNSFlag1_RC_FormErr = 0x01, - kDNSFlag1_RC_ServFail = 0x02, - kDNSFlag1_RC_NXDomain = 0x03, - kDNSFlag1_RC_NotImpl = 0x04, - kDNSFlag1_RC_Refused = 0x05, - kDNSFlag1_RC_YXDomain = 0x06, - kDNSFlag1_RC_YXRRSet = 0x07, - kDNSFlag1_RC_NXRRSet = 0x08, - kDNSFlag1_RC_NotAuth = 0x09, - kDNSFlag1_RC_NotZone = 0x0A - } DNS_Flags; +{ + kDNSFlag0_QR_Mask = 0x80, // Query or response? + kDNSFlag0_QR_Query = 0x00, + kDNSFlag0_QR_Response = 0x80, + + kDNSFlag0_OP_Mask = 0x78, // Operation type + kDNSFlag0_OP_StdQuery = 0x00, + kDNSFlag0_OP_Iquery = 0x08, + kDNSFlag0_OP_Status = 0x10, + kDNSFlag0_OP_Unused3 = 0x18, + kDNSFlag0_OP_Notify = 0x20, + kDNSFlag0_OP_Update = 0x28, + + kDNSFlag0_QROP_Mask = kDNSFlag0_QR_Mask | kDNSFlag0_OP_Mask, + + kDNSFlag0_AA = 0x04, // Authoritative Answer? + kDNSFlag0_TC = 0x02, // Truncated? + kDNSFlag0_RD = 0x01, // Recursion Desired? + kDNSFlag1_RA = 0x80, // Recursion Available? + + kDNSFlag1_Zero = 0x40, // Reserved; must be zero + kDNSFlag1_AD = 0x20, // Authentic Data [RFC 2535] + kDNSFlag1_CD = 0x10, // Checking Disabled [RFC 2535] + + kDNSFlag1_RC_Mask = 0x0F, // Response code + kDNSFlag1_RC_NoErr = 0x00, + kDNSFlag1_RC_FormErr = 0x01, + kDNSFlag1_RC_ServFail = 0x02, + kDNSFlag1_RC_NXDomain = 0x03, + kDNSFlag1_RC_NotImpl = 0x04, + kDNSFlag1_RC_Refused = 0x05, + kDNSFlag1_RC_YXDomain = 0x06, + kDNSFlag1_RC_YXRRSet = 0x07, + kDNSFlag1_RC_NXRRSet = 0x08, + kDNSFlag1_RC_NotAuth = 0x09, + kDNSFlag1_RC_NotZone = 0x0A +} DNS_Flags; typedef enum - { - TSIG_ErrBadSig = 16, - TSIG_ErrBadKey = 17, - TSIG_ErrBadTime = 18 - } TSIG_ErrorCode; +{ + TSIG_ErrBadSig = 16, + TSIG_ErrBadKey = 17, + TSIG_ErrBadTime = 18 +} TSIG_ErrorCode; // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -93,7 +93,7 @@ typedef enum extern NetworkInterfaceInfo *GetFirstActiveInterface(NetworkInterfaceInfo *intf); extern mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf); -extern mDNSu32 mDNSRandom(mDNSu32 max); // Returns pseudo-random result from zero to max inclusive +extern mDNSu32 mDNSRandom(mDNSu32 max); // Returns pseudo-random result from zero to max inclusive // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -139,25 +139,26 @@ extern void AppendLabelSuffix(domainlabel *const name, mDNSu32 val, const mDNSBo // (99% of the time) and then bail out before we waste time on the expensive SameDomainName() check. #define IdenticalResourceRecord(r1,r2) ( \ - (r1)->rrtype == (r2)->rrtype && \ - (r1)->rrclass == (r2)->rrclass && \ - (r1)->namehash == (r2)->namehash && \ - (r1)->rdlength == (r2)->rdlength && \ - (r1)->rdatahash == (r2)->rdatahash && \ - SameRDataBody((r1), &(r2)->rdata->u, SameDomainName) && \ - SameDomainName((r1)->name, (r2)->name)) + (r1)->rrtype == (r2)->rrtype && \ + (r1)->rrclass == (r2)->rrclass && \ + (r1)->namehash == (r2)->namehash && \ + (r1)->rdlength == (r2)->rdlength && \ + (r1)->rdatahash == (r2)->rdatahash && \ + SameRDataBody((r1), &(r2)->rdata->u, SameDomainName) && \ + SameDomainName((r1)->name, (r2)->name)) #define IdenticalSameNameRecord(r1,r2) ( \ - (r1)->rrtype == (r2)->rrtype && \ - (r1)->rrclass == (r2)->rrclass && \ - (r1)->rdlength == (r2)->rdlength && \ - (r1)->rdatahash == (r2)->rdatahash && \ - SameRDataBody((r1), &(r2)->rdata->u, SameDomainName)) + (r1)->rrtype == (r2)->rrtype && \ + (r1)->rrclass == (r2)->rrclass && \ + (r1)->rdlength == (r2)->rdlength && \ + (r1)->rdatahash == (r2)->rdatahash && \ + SameRDataBody((r1), &(r2)->rdata->u, SameDomainName)) // A given RRType answers a QuestionType if RRType is CNAME, or types match, or QuestionType is ANY, // or the RRType is NSEC and positively asserts the nonexistence of the type being requested #define RRTypeAnswersQuestionType(R,Q) ((R)->rrtype == kDNSType_CNAME || (R)->rrtype == (Q) || (Q) == kDNSQType_ANY || RRAssertsNonexistence((R),(Q))) -#define RRAssertsNonexistence(R,T) ((R)->rrtype == kDNSType_NSEC && (T) < kDNSQType_ANY && !((R)->rdata->u.nsec.bitmap[(T)>>3] & (128 >> ((T)&7)))) +// Unicast NSEC records have the NSEC bit set whereas the multicast NSEC ones don't +#define UNICAST_NSEC(rr) ((rr)->rrtype == kDNSType_NSEC && RRAssertsExistence((rr), kDNSType_NSEC)) extern mDNSu32 RDataHashValue(const ResourceRecord *const rr); extern mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2, DomainNameComparisonFn *samename); @@ -170,9 +171,9 @@ extern mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate); extern mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd); #define GetRRDomainNameTarget(RR) ( \ - ((RR)->rrtype == kDNSType_NS || (RR)->rrtype == kDNSType_CNAME || (RR)->rrtype == kDNSType_PTR || (RR)->rrtype == kDNSType_DNAME) ? &(RR)->rdata->u.name : \ - ((RR)->rrtype == kDNSType_MX || (RR)->rrtype == kDNSType_AFSDB || (RR)->rrtype == kDNSType_RT || (RR)->rrtype == kDNSType_KX ) ? &(RR)->rdata->u.mx.exchange : \ - ((RR)->rrtype == kDNSType_SRV ) ? &(RR)->rdata->u.srv.target : mDNSNULL ) + ((RR)->rrtype == kDNSType_NS || (RR)->rrtype == kDNSType_CNAME || (RR)->rrtype == kDNSType_PTR || (RR)->rrtype == kDNSType_DNAME) ? &(RR)->rdata->u.name : \ + ((RR)->rrtype == kDNSType_MX || (RR)->rrtype == kDNSType_AFSDB || (RR)->rrtype == kDNSType_RT || (RR)->rrtype == kDNSType_KX ) ? &(RR)->rdata->u.mx.exchange : \ + ((RR)->rrtype == kDNSType_SRV ) ? &(RR)->rdata->u.srv.target : mDNSNULL ) #define LocalRecordReady(X) ((X)->resrec.RecordType != kDNSRecordTypeUnique) @@ -195,17 +196,17 @@ extern mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 * 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)->data + AllowedRRSpace(msg)) + PutResourceRecordTTLWithLimit((msg), (ptr), (count), (rr), (ttl), (msg)->data + AllowedRRSpace(msg)) #define PutResourceRecordTTLJumbo(msg, ptr, count, rr, ttl) \ - PutResourceRecordTTLWithLimit((msg), (ptr), (count), (rr), (ttl), (msg)->data + AbsoluteMaxDNSMessageData) + PutResourceRecordTTLWithLimit((msg), (ptr), (count), (rr), (ttl), (msg)->data + AbsoluteMaxDNSMessageData) #define PutResourceRecord(MSG, P, C, RR) PutResourceRecordTTL((MSG), (P), (C), (RR), (RR)->rroriginalttl) // The PutRR_OS variants assume a local variable 'm', put build the packet at m->omsg, // and assume a local variable 'OwnerRecordSpace' indicating how many bytes (if any) to reserve to add an OWNER option at the end #define PutRR_OS_TTL(ptr, count, rr, ttl) \ - PutResourceRecordTTLWithLimit(&m->omsg, (ptr), (count), (rr), (ttl), m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace) + PutResourceRecordTTLWithLimit(&m->omsg, (ptr), (count), (rr), (ttl), m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace) #define PutRR_OS(P, C, RR) PutRR_OS_TTL((P), (C), (RR), (RR)->rroriginalttl) @@ -220,6 +221,7 @@ extern mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease); extern mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *ptr, mDNSu32 lease, mDNSu8 *limit); extern mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *ptr, DomainAuthInfo *authInfo, mDNSu8 *limit); +mDNSexport mDNSu8 *putDNSSECOption(DNSMessage *msg, mDNSu8 *end, mDNSu8 *limit); // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -233,13 +235,15 @@ extern mDNSu32 DomainNameHashValue(const domainname *const name); extern void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength); extern const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end); extern const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, - domainname *const name); + domainname *const name); extern const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end); extern const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage * const msg, const mDNSu8 *ptr, - const mDNSu8 * end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *const largecr); + const mDNSu8 * end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *const largecr); +extern mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *end, + LargeCacheRecord *const largecr, mDNSu16 rdlength); extern const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end); extern const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID, - DNSQuestion *question); + DNSQuestion *question); extern const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end); extern const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end); extern const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 *const end); @@ -247,8 +251,13 @@ extern const mDNSu8 *LocateOptRR(const DNSMessage *const msg, const mDNSu8 *cons extern const rdataOPT *GetLLQOptData(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end); extern mDNSu32 GetPktLease(mDNS *m, DNSMessage *msg, const mDNSu8 *end); extern void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *transport, - const mDNSAddr *srcaddr, mDNSIPPort srcport, - const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end); + const mDNSAddr *srcaddr, mDNSIPPort srcport, + const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end); +extern mDNSBool RRAssertsNonexistence(const ResourceRecord *const rr, mDNSu16 type); +extern mDNSBool RRAssertsExistence(const ResourceRecord *const rr, mDNSu16 type); + +extern mDNSu16 swap16(mDNSu16 x); +extern mDNSu32 swap32(mDNSu32 x); // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -257,7 +266,9 @@ extern void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *trans #endif extern mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNSu8 *end, - mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, mDNSIPPort dstport, TCPSocket *sock, DomainAuthInfo *authInfo); + mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, + mDNSIPPort dstport, TCPSocket *sock, DomainAuthInfo *authInfo, + mDNSBool useBackgroundTrafficClass); // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -277,16 +288,19 @@ extern void mDNS_Unlock_(mDNS *const m, const char * const functionname); #define mDNS_Unlock(X) mDNS_Unlock_((X), __func__) +#define mDNS_CheckLock(X) { if ((X)->mDNS_busy != (X)->mDNS_reentrancy+1) \ + LogMsg("%s: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", __func__, (X)->mDNS_busy, (X)->mDNS_reentrancy); } + #define mDNS_DropLockBeforeCallback() do { m->mDNS_reentrancy++; \ - if (m->mDNS_busy != m->mDNS_reentrancy) LogMsg("%s: Locking Failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", __func__, m->mDNS_busy, m->mDNS_reentrancy); \ - } while (0) + if (m->mDNS_busy != m->mDNS_reentrancy) LogMsg("%s: Locking Failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", __func__, m->mDNS_busy, m->mDNS_reentrancy); \ +} while (0) #define mDNS_ReclaimLockAfterCallback() do { \ - if (m->mDNS_busy != m->mDNS_reentrancy) LogMsg("%s: Unlocking Failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", __func__, m->mDNS_busy, m->mDNS_reentrancy); \ - m->mDNS_reentrancy--; } while (0) + if (m->mDNS_busy != m->mDNS_reentrancy) LogMsg("%s: Unlocking Failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", __func__, m->mDNS_busy, m->mDNS_reentrancy); \ + m->mDNS_reentrancy--; } while (0) -#ifdef __cplusplus - } +#ifdef __cplusplus +} #endif #endif // __DNSCOMMON_H_ diff --git a/mDNSCore/DNSDigest.c b/mDNSCore/DNSDigest.c index 98d23db..1ad8d0c 100644 --- a/mDNSCore/DNSDigest.c +++ b/mDNSCore/DNSDigest.c @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -24,30 +24,30 @@ extern "C" { #include "DNSCommon.h" // Disable certain benign warnings with Microsoft compilers -#if(defined(_MSC_VER)) - // Disable "conditional expression is constant" warning for debug macros. - // Otherwise, this generates warnings for the perfectly natural construct "while(1)" - // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know - #pragma warning(disable:4127) +#if (defined(_MSC_VER)) +// Disable "conditional expression is constant" warning for debug macros. +// Otherwise, this generates warnings for the perfectly natural construct "while(1)" +// If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know + #pragma warning(disable:4127) #endif - // *************************************************************************** +// *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - Byte Swapping Functions #endif mDNSlocal mDNSu16 NToH16(mDNSu8 * bytes) - { - return (mDNSu16)((mDNSu16)bytes[0] << 8 | (mDNSu16)bytes[1]); - } +{ + return (mDNSu16)((mDNSu16)bytes[0] << 8 | (mDNSu16)bytes[1]); +} mDNSlocal mDNSu32 NToH32(mDNSu8 * bytes) - { - return (mDNSu32)((mDNSu32) bytes[0] << 24 | (mDNSu32) bytes[1] << 16 | (mDNSu32) bytes[2] << 8 | (mDNSu32)bytes[3]); - } +{ + return (mDNSu32)((mDNSu32) bytes[0] << 24 | (mDNSu32) bytes[1] << 16 | (mDNSu32) bytes[2] << 8 | (mDNSu32)bytes[3]); +} - // *************************************************************************** +// *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - MD5 Hash Functions #endif @@ -65,7 +65,7 @@ mDNSlocal mDNSu32 NToH32(mDNSu8 * bytes) * to aid in platform-specific optimizations and debugging. * Sources originally distributed under the following license headers: * CommonDigest.h - APSL - * + * * md32_Common.h * ==================================================================== * Copyright (c) 1999-2002 The OpenSSL Project. All rights reserved. @@ -75,7 +75,7 @@ mDNSlocal mDNSu32 NToH32(mDNSu8 * bytes) * are met: * * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. + * 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 @@ -128,21 +128,21 @@ mDNSlocal mDNSu32 NToH32(mDNSu8 * bytes) * This package is an SSL implementation written * by Eric Young (eay@cryptsoft.com). * The implementation was written so as to conform with Netscapes SSL. - * + * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@cryptsoft.com). - * + * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -157,10 +157,10 @@ mDNSlocal mDNSu32 NToH32(mDNSu8 * bytes) * Eric Young (eay@cryptsoft.com)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). - * 4. If you include any Windows specific code (or a derivative thereof) from + * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" - * + * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -172,7 +172,7 @@ mDNSlocal mDNSu32 NToH32(mDNSu8 * bytes) * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * + * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence @@ -182,28 +182,14 @@ mDNSlocal mDNSu32 NToH32(mDNSu8 * bytes) //from CommonDigest.h -#define MD5_DIGEST_LENGTH 16 /* digest length in bytes */ -#define MD5_BLOCK_BYTES 64 /* block size in bytes */ -#define MD5_BLOCK_LONG (MD5_BLOCK_BYTES / sizeof(mDNSu32)) - -typedef struct MD5state_st -{ - mDNSu32 A,B,C,D; - mDNSu32 Nl,Nh; - mDNSu32 data[MD5_BLOCK_LONG]; - int num; -} MD5_CTX; // from openssl/md5.h -#define MD5_CBLOCK 64 -#define MD5_LBLOCK (MD5_CBLOCK/4) +#define MD5_CBLOCK 64 +#define MD5_LBLOCK (MD5_CBLOCK/4) #define MD5_DIGEST_LENGTH 16 -int MD5_Init(MD5_CTX *c); -int MD5_Update(MD5_CTX *c, const void *data, unsigned long len); -int MD5_Final(unsigned char *md, MD5_CTX *c); void MD5_Transform(MD5_CTX *c, const unsigned char *b); // From md5_locl.h @@ -216,7 +202,7 @@ void MD5_Transform(MD5_CTX *c, const unsigned char *b); # if defined(__i386) || defined(__i386__) || defined(_M_IX86) || defined(__INTEL__) # define md5_block_host_order md5_block_asm_host_order # elif defined(__sparc) && defined(OPENSSL_SYS_ULTRASPARC) - void md5_block_asm_data_order_aligned (MD5_CTX *c, const mDNSu32 *p,int num); +void md5_block_asm_data_order_aligned (MD5_CTX *c, const mDNSu32 *p,int num); # define HASH_BLOCK_DATA_ORDER_ALIGNED md5_block_asm_data_order_aligned # endif #endif @@ -251,26 +237,26 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num); #define DATA_ORDER_IS_LITTLE_ENDIAN -#define HASH_LONG mDNSu32 -#define HASH_LONG_LOG2 MD5_LONG_LOG2 -#define HASH_CTX MD5_CTX -#define HASH_CBLOCK MD5_CBLOCK -#define HASH_LBLOCK MD5_LBLOCK - -#define HASH_UPDATE MD5_Update -#define HASH_TRANSFORM MD5_Transform -#define HASH_FINAL MD5_Final - -#define HASH_MAKE_STRING(c,s) do { \ - unsigned long ll; \ - ll=(c)->A; HOST_l2c(ll,(s)); \ - ll=(c)->B; HOST_l2c(ll,(s)); \ - ll=(c)->C; HOST_l2c(ll,(s)); \ - ll=(c)->D; HOST_l2c(ll,(s)); \ - } while (0) -#define HASH_BLOCK_HOST_ORDER md5_block_host_order +#define HASH_LONG mDNSu32 +#define HASH_LONG_LOG2 MD5_LONG_LOG2 +#define HASH_CTX MD5_CTX +#define HASH_CBLOCK MD5_CBLOCK +#define HASH_LBLOCK MD5_LBLOCK + +#define HASH_UPDATE MD5_Update +#define HASH_TRANSFORM MD5_Transform +#define HASH_FINAL MD5_Final + +#define HASH_MAKE_STRING(c,s) do { \ + unsigned long ll; \ + ll=(c)->A; HOST_l2c(ll,(s)); \ + ll=(c)->B; HOST_l2c(ll,(s)); \ + ll=(c)->C; HOST_l2c(ll,(s)); \ + ll=(c)->D; HOST_l2c(ll,(s)); \ +} while (0) +#define HASH_BLOCK_HOST_ORDER md5_block_host_order #if !defined(L_ENDIAN) || defined(md5_block_data_order) -#define HASH_BLOCK_DATA_ORDER md5_block_data_order +#define HASH_BLOCK_DATA_ORDER md5_block_data_order /* * Little-endians (Intel and Alpha) feel better without this. * It looks like memcpy does better job than generic @@ -401,11 +387,11 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num); #endif #ifndef HASH_LBLOCK -#define HASH_LBLOCK (HASH_CBLOCK/4) +#define HASH_LBLOCK (HASH_CBLOCK/4) #endif #ifndef HASH_LONG_LOG2 -#define HASH_LONG_LOG2 2 +#define HASH_LONG_LOG2 2 #endif /* @@ -414,46 +400,46 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num); #undef ROTATE #ifndef PEDANTIC # if 0 /* defined(_MSC_VER) */ -# define ROTATE(a,n) _lrotl(a,n) +# define ROTATE(a,n) _lrotl(a,n) # elif defined(__MWERKS__) # if defined(__POWERPC__) -# define ROTATE(a,n) (unsigned MD32_REG_T)__rlwinm((int)a,n,0,31) +# define ROTATE(a,n) (unsigned MD32_REG_T)__rlwinm((int)a,n,0,31) # elif defined(__MC68K__) - /* Motorola specific tweak. */ -# define ROTATE(a,n) (n<24 ? __rol(a,n) : __ror(a,32-n)) +/* Motorola specific tweak. */ +# define ROTATE(a,n) (n<24 ? __rol(a,n) : __ror(a,32-n)) # else -# define ROTATE(a,n) __rol(a,n) +# define ROTATE(a,n) __rol(a,n) # endif # elif defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) - /* - * Some GNU C inline assembler templates. Note that these are - * rotates by *constant* number of bits! But that's exactly - * what we need here... - * - * - */ - /* - * LLVM is more strict about compatibility of types between input & output constraints, - * but we want these to be rotations of 32 bits, not 64, so we explicitly drop the - * most significant bytes by casting to an unsigned int. - */ +/* + * Some GNU C inline assembler templates. Note that these are + * rotates by *constant* number of bits! But that's exactly + * what we need here... + * + * + */ +/* + * LLVM is more strict about compatibility of types between input & output constraints, + * but we want these to be rotations of 32 bits, not 64, so we explicitly drop the + * most significant bytes by casting to an unsigned int. + */ # if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__) -# define ROTATE(a,n) ({ register unsigned int ret; \ - asm ( \ - "roll %1,%0" \ - : "=r"(ret) \ - : "I"(n), "0"((unsigned int)a) \ - : "cc"); \ - ret; \ - }) +# define ROTATE(a,n) ({ register unsigned int ret; \ + asm ( \ + "roll %1,%0" \ + : "=r" (ret) \ + : "I" (n), "0" ((unsigned int)a) \ + : "cc"); \ + ret; \ + }) # elif defined(__powerpc) || defined(__ppc) -# define ROTATE(a,n) ({ register unsigned int ret; \ - asm ( \ - "rlwinm %0,%1,%2,0,31" \ - : "=r"(ret) \ - : "r"(a), "I"(n)); \ - ret; \ - }) +# define ROTATE(a,n) ({ register unsigned int ret; \ + asm ( \ + "rlwinm %0,%1,%2,0,31" \ + : "=r" (ret) \ + : "r" (a), "I" (n)); \ + ret; \ + }) # endif # endif @@ -462,50 +448,50 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num); * intrinsic function if available. */ # if defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) - /* some GNU C inline assembler templates by */ +/* some GNU C inline assembler templates by */ # if (defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__)) && !defined(I386_ONLY) -# define BE_FETCH32(a) ({ register unsigned int l=(a);\ - asm ( \ - "bswapl %0" \ - : "=r"(l) : "0"(l)); \ - l; \ - }) +# define BE_FETCH32(a) ({ register unsigned int l=(a); \ + asm ( \ + "bswapl %0" \ + : "=r" (l) : "0" (l)); \ + l; \ + }) # elif defined(__powerpc) -# define LE_FETCH32(a) ({ register unsigned int l; \ - asm ( \ - "lwbrx %0,0,%1" \ - : "=r"(l) \ - : "r"(a)); \ - l; \ - }) +# define LE_FETCH32(a) ({ register unsigned int l; \ + asm ( \ + "lwbrx %0,0,%1" \ + : "=r" (l) \ + : "r" (a)); \ + l; \ + }) # elif defined(__sparc) && defined(OPENSSL_SYS_ULTRASPARC) -# define LE_FETCH32(a) ({ register unsigned int l; \ - asm ( \ - "lda [%1]#ASI_PRIMARY_LITTLE,%0"\ - : "=r"(l) \ - : "r"(a)); \ - l; \ - }) +# define LE_FETCH32(a) ({ register unsigned int l; \ + asm ( \ + "lda [%1]#ASI_PRIMARY_LITTLE,%0" \ + : "=r" (l) \ + : "r" (a)); \ + l; \ + }) # endif # endif #endif /* PEDANTIC */ -#if HASH_LONG_LOG2==2 /* Engage only if sizeof(HASH_LONG)== 4 */ +#if HASH_LONG_LOG2==2 /* Engage only if sizeof(HASH_LONG)== 4 */ /* A nice byte order reversal from Wei Dai */ #ifdef ROTATE /* 5 instructions with rotate instruction, else 9 */ -#define REVERSE_FETCH32(a,l) ( \ - l=*(const HASH_LONG *)(a), \ - ((ROTATE(l,8)&0x00FF00FF)|(ROTATE((l&0x00FF00FF),24))) \ - ) +#define REVERSE_FETCH32(a,l) ( \ + l=*(const HASH_LONG *)(a), \ + ((ROTATE(l,8)&0x00FF00FF)|(ROTATE((l&0x00FF00FF),24))) \ + ) #else /* 6 instructions with rotate instruction, else 8 */ -#define REVERSE_FETCH32(a,l) ( \ - l=*(const HASH_LONG *)(a), \ - l=(((l>>8)&0x00FF00FF)|((l&0x00FF00FF)<<8)), \ - ROTATE(l,16) \ - ) +#define REVERSE_FETCH32(a,l) ( \ + l=*(const HASH_LONG *)(a), \ + l=(((l>>8)&0x00FF00FF)|((l&0x00FF00FF)<<8)), \ + ROTATE(l,16) \ + ) /* * Originally the middle line started with l=(((l&0xFF00FF00)>>8)|... * It's rewritten as above for two reasons: @@ -538,28 +524,28 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num); #if defined(B_ENDIAN) # if defined(DATA_ORDER_IS_BIG_ENDIAN) # if !defined(HASH_BLOCK_DATA_ORDER_ALIGNED) && HASH_LONG_LOG2==2 -# define HASH_BLOCK_DATA_ORDER_ALIGNED HASH_BLOCK_HOST_ORDER +# define HASH_BLOCK_DATA_ORDER_ALIGNED HASH_BLOCK_HOST_ORDER # endif # elif defined(DATA_ORDER_IS_LITTLE_ENDIAN) # ifndef HOST_FETCH32 # ifdef LE_FETCH32 -# define HOST_FETCH32(p,l) LE_FETCH32(p) +# define HOST_FETCH32(p,l) LE_FETCH32(p) # elif defined(REVERSE_FETCH32) -# define HOST_FETCH32(p,l) REVERSE_FETCH32(p,l) +# define HOST_FETCH32(p,l) REVERSE_FETCH32(p,l) # endif # endif # endif #elif defined(L_ENDIAN) # if defined(DATA_ORDER_IS_LITTLE_ENDIAN) # if !defined(HASH_BLOCK_DATA_ORDER_ALIGNED) && HASH_LONG_LOG2==2 -# define HASH_BLOCK_DATA_ORDER_ALIGNED HASH_BLOCK_HOST_ORDER +# define HASH_BLOCK_DATA_ORDER_ALIGNED HASH_BLOCK_HOST_ORDER # endif # elif defined(DATA_ORDER_IS_BIG_ENDIAN) # ifndef HOST_FETCH32 # ifdef BE_FETCH32 -# define HOST_FETCH32(p,l) BE_FETCH32(p) +# define HOST_FETCH32(p,l) BE_FETCH32(p) # elif defined(REVERSE_FETCH32) -# define HOST_FETCH32(p,l) REVERSE_FETCH32(p,l) +# define HOST_FETCH32(p,l) REVERSE_FETCH32(p,l) # endif # endif # endif @@ -580,75 +566,75 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num); #if defined(DATA_ORDER_IS_BIG_ENDIAN) -#define _HOST_c2l(c,l) (l =(((unsigned long)(*((c)++)))<<24), \ - l|=(((unsigned long)(*((c)++)))<<16), \ - l|=(((unsigned long)(*((c)++)))<< 8), \ - l|=(((unsigned long)(*((c)++))) ), \ - l) -#define HOST_p_c2l(c,l,n) { \ - switch (n) { \ - case 0: l =((unsigned long)(*((c)++)))<<24; \ - case 1: l|=((unsigned long)(*((c)++)))<<16; \ - case 2: l|=((unsigned long)(*((c)++)))<< 8; \ - case 3: l|=((unsigned long)(*((c)++))); \ - } } -#define HOST_p_c2l_p(c,l,sc,len) { \ - switch (sc) { \ - case 0: l =((unsigned long)(*((c)++)))<<24; \ - if (--len == 0) break; \ - case 1: l|=((unsigned long)(*((c)++)))<<16; \ - if (--len == 0) break; \ - case 2: l|=((unsigned long)(*((c)++)))<< 8; \ - } } +#define _HOST_c2l(c,l) (l =(((unsigned long)(*((c)++)))<<24), \ + l|=(((unsigned long)(*((c)++)))<<16), \ + l|=(((unsigned long)(*((c)++)))<< 8), \ + l|=(((unsigned long)(*((c)++))) ), \ + l) +#define HOST_p_c2l(c,l,n) { \ + switch (n) { \ + case 0: l =((unsigned long)(*((c)++)))<<24; \ + case 1: l|=((unsigned long)(*((c)++)))<<16; \ + case 2: l|=((unsigned long)(*((c)++)))<< 8; \ + case 3: l|=((unsigned long)(*((c)++))); \ + } } +#define HOST_p_c2l_p(c,l,sc,len) { \ + switch (sc) { \ + case 0: l =((unsigned long)(*((c)++)))<<24; \ + if (--len == 0) break; \ + case 1: l|=((unsigned long)(*((c)++)))<<16; \ + if (--len == 0) break; \ + case 2: l|=((unsigned long)(*((c)++)))<< 8; \ + } } /* NOTE the pointer is not incremented at the end of this */ -#define HOST_c2l_p(c,l,n) { \ - l=0; (c)+=n; \ - switch (n) { \ - case 3: l =((unsigned long)(*(--(c))))<< 8; \ - case 2: l|=((unsigned long)(*(--(c))))<<16; \ - case 1: l|=((unsigned long)(*(--(c))))<<24; \ - } } -#define _HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l)>>24)&0xff), \ - *((c)++)=(unsigned char)(((l)>>16)&0xff), \ - *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ - *((c)++)=(unsigned char)(((l) )&0xff), \ - l) +#define HOST_c2l_p(c,l,n) { \ + l=0; (c)+=n; \ + switch (n) { \ + case 3: l =((unsigned long)(*(--(c))))<< 8; \ + case 2: l|=((unsigned long)(*(--(c))))<<16; \ + case 1: l|=((unsigned long)(*(--(c))))<<24; \ + } } +#define _HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l)>>24)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l) )&0xff), \ + l) #elif defined(DATA_ORDER_IS_LITTLE_ENDIAN) -#define _HOST_c2l(c,l) (l =(((unsigned long)(*((c)++))) ), \ - l|=(((unsigned long)(*((c)++)))<< 8), \ - l|=(((unsigned long)(*((c)++)))<<16), \ - l|=(((unsigned long)(*((c)++)))<<24), \ - l) -#define HOST_p_c2l(c,l,n) { \ - switch (n) { \ - case 0: l =((unsigned long)(*((c)++))); \ - case 1: l|=((unsigned long)(*((c)++)))<< 8; \ - case 2: l|=((unsigned long)(*((c)++)))<<16; \ - case 3: l|=((unsigned long)(*((c)++)))<<24; \ - } } -#define HOST_p_c2l_p(c,l,sc,len) { \ - switch (sc) { \ - case 0: l =((unsigned long)(*((c)++))); \ - if (--len == 0) break; \ - case 1: l|=((unsigned long)(*((c)++)))<< 8; \ - if (--len == 0) break; \ - case 2: l|=((unsigned long)(*((c)++)))<<16; \ - } } +#define _HOST_c2l(c,l) (l =(((unsigned long)(*((c)++))) ), \ + l|=(((unsigned long)(*((c)++)))<< 8), \ + l|=(((unsigned long)(*((c)++)))<<16), \ + l|=(((unsigned long)(*((c)++)))<<24), \ + l) +#define HOST_p_c2l(c,l,n) { \ + switch (n) { \ + case 0: l =((unsigned long)(*((c)++))); \ + case 1: l|=((unsigned long)(*((c)++)))<< 8; \ + case 2: l|=((unsigned long)(*((c)++)))<<16; \ + case 3: l|=((unsigned long)(*((c)++)))<<24; \ + } } +#define HOST_p_c2l_p(c,l,sc,len) { \ + switch (sc) { \ + case 0: l =((unsigned long)(*((c)++))); \ + if (--len == 0) break; \ + case 1: l|=((unsigned long)(*((c)++)))<< 8; \ + if (--len == 0) break; \ + case 2: l|=((unsigned long)(*((c)++)))<<16; \ + } } /* NOTE the pointer is not incremented at the end of this */ -#define HOST_c2l_p(c,l,n) { \ - l=0; (c)+=n; \ - switch (n) { \ - case 3: l =((unsigned long)(*(--(c))))<<16; \ - case 2: l|=((unsigned long)(*(--(c))))<< 8; \ - case 1: l|=((unsigned long)(*(--(c)))); \ - } } -#define _HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ - *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ - *((c)++)=(unsigned char)(((l)>>16)&0xff), \ - *((c)++)=(unsigned char)(((l)>>24)&0xff), \ - l) +#define HOST_c2l_p(c,l,n) { \ + l=0; (c)+=n; \ + switch (n) { \ + case 3: l =((unsigned long)(*(--(c))))<<16; \ + case 2: l|=((unsigned long)(*(--(c))))<< 8; \ + case 1: l|=((unsigned long)(*(--(c)))); \ + } } +#define _HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>>24)&0xff), \ + l) #endif @@ -657,205 +643,205 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num); */ int HASH_UPDATE (HASH_CTX *c, const void *data_, unsigned long len) - { - const unsigned char *data=(const unsigned char *)data_; - register HASH_LONG * p; - register unsigned long l; - int sw,sc,ew,ec; - - if (len==0) return 1; - - l=(c->Nl+(len<<3))&0xffffffffL; - /* 95-05-24 eay Fixed a bug with the overflow handling, thanks to - * Wei Dai for pointing it out. */ - if (l < c->Nl) /* overflow */ - c->Nh++; - c->Nh+=(len>>29); - c->Nl=l; - - if (c->num != 0) - { - p=c->data; - sw=c->num>>2; - sc=c->num&0x03; - - if ((c->num+len) >= HASH_CBLOCK) - { - l=p[sw]; HOST_p_c2l(data,l,sc); p[sw++]=l; - for (; swnum); - c->num=0; - /* drop through and do the rest */ - } - else - { - c->num+=len; - if ((sc+len) < 4) /* ugly, add char's to a word */ - { - l=p[sw]; HOST_p_c2l_p(data,l,sc,len); p[sw]=l; - } - else - { - ew=(c->num>>2); - ec=(c->num&0x03); - if (sc) - l=p[sw]; - HOST_p_c2l(data,l,sc); - p[sw++]=l; - for (; sw < ew; sw++) - { - HOST_c2l(data,l); p[sw]=l; - } - if (ec) - { - HOST_c2l_p(data,l,ec); p[sw]=l; - } - } - return 1; - } - } - - sw=(int)(len/HASH_CBLOCK); - if (sw > 0) - { +{ + const unsigned char *data=(const unsigned char *)data_; + register HASH_LONG * p; + register unsigned long l; + int sw,sc,ew,ec; + + if (len==0) return 1; + + l=(c->Nl+(len<<3))&0xffffffffL; + /* 95-05-24 eay Fixed a bug with the overflow handling, thanks to + * Wei Dai for pointing it out. */ + if (l < c->Nl) /* overflow */ + c->Nh++; + c->Nh+=(len>>29); + c->Nl=l; + + if (c->num != 0) + { + p=c->data; + sw=c->num>>2; + sc=c->num&0x03; + + if ((c->num+len) >= HASH_CBLOCK) + { + l=p[sw]; HOST_p_c2l(data,l,sc); p[sw++]=l; + for (; swnum); + c->num=0; + /* drop through and do the rest */ + } + else + { + c->num+=len; + if ((sc+len) < 4) /* ugly, add char's to a word */ + { + l=p[sw]; HOST_p_c2l_p(data,l,sc,len); p[sw]=l; + } + else + { + ew=(c->num>>2); + ec=(c->num&0x03); + if (sc) + l=p[sw]; + HOST_p_c2l(data,l,sc); + p[sw++]=l; + for (; sw < ew; sw++) + { + HOST_c2l(data,l); p[sw]=l; + } + if (ec) + { + HOST_c2l_p(data,l,ec); p[sw]=l; + } + } + return 1; + } + } + + sw=(int)(len/HASH_CBLOCK); + if (sw > 0) + { #if defined(HASH_BLOCK_DATA_ORDER_ALIGNED) - /* - * Note that HASH_BLOCK_DATA_ORDER_ALIGNED gets defined - * only if sizeof(HASH_LONG)==4. - */ - if ((((unsigned long)data)%4) == 0) - { - /* data is properly aligned so that we can cast it: */ - HASH_BLOCK_DATA_ORDER_ALIGNED (c,(HASH_LONG *)data,sw); - sw*=HASH_CBLOCK; - data+=sw; - len-=sw; - } - else + /* + * Note that HASH_BLOCK_DATA_ORDER_ALIGNED gets defined + * only if sizeof(HASH_LONG)==4. + */ + if ((((unsigned long)data)%4) == 0) + { + /* data is properly aligned so that we can cast it: */ + HASH_BLOCK_DATA_ORDER_ALIGNED (c,(HASH_LONG *)data,sw); + sw*=HASH_CBLOCK; + data+=sw; + len-=sw; + } + else #if !defined(HASH_BLOCK_DATA_ORDER) - while (sw--) - { - mDNSPlatformMemCopy(p=c->data,data,HASH_CBLOCK); - HASH_BLOCK_DATA_ORDER_ALIGNED(c,p,1); - data+=HASH_CBLOCK; - len-=HASH_CBLOCK; - } + while (sw--) + { + mDNSPlatformMemCopy(p=c->data,data,HASH_CBLOCK); + HASH_BLOCK_DATA_ORDER_ALIGNED(c,p,1); + data+=HASH_CBLOCK; + len-=HASH_CBLOCK; + } #endif #endif #if defined(HASH_BLOCK_DATA_ORDER) - { - HASH_BLOCK_DATA_ORDER(c,data,sw); - sw*=HASH_CBLOCK; - data+=sw; - len-=sw; - } + { + HASH_BLOCK_DATA_ORDER(c,data,sw); + sw*=HASH_CBLOCK; + data+=sw; + len-=sw; + } #endif - } - - if (len!=0) - { - p = c->data; - c->num = (int)len; - ew=(int)(len>>2); /* words to copy */ - ec=(int)(len&0x03); - for (; ew; ew--,p++) - { - HOST_c2l(data,l); *p=l; - } - HOST_c2l_p(data,l,ec); - *p=l; - } - return 1; - } + } + + if (len!=0) + { + p = c->data; + c->num = (int)len; + ew=(int)(len>>2); /* words to copy */ + ec=(int)(len&0x03); + for (; ew; ew--,p++) + { + HOST_c2l(data,l); *p=l; + } + HOST_c2l_p(data,l,ec); + *p=l; + } + return 1; +} void HASH_TRANSFORM (HASH_CTX *c, const unsigned char *data) - { +{ #if defined(HASH_BLOCK_DATA_ORDER_ALIGNED) - if ((((unsigned long)data)%4) == 0) - /* data is properly aligned so that we can cast it: */ - HASH_BLOCK_DATA_ORDER_ALIGNED (c,(HASH_LONG *)data,1); - else + if ((((unsigned long)data)%4) == 0) + /* data is properly aligned so that we can cast it: */ + HASH_BLOCK_DATA_ORDER_ALIGNED (c,(HASH_LONG *)data,1); + else #if !defined(HASH_BLOCK_DATA_ORDER) - { - mDNSPlatformMemCopy(c->data,data,HASH_CBLOCK); - HASH_BLOCK_DATA_ORDER_ALIGNED (c,c->data,1); - } + { + mDNSPlatformMemCopy(c->data,data,HASH_CBLOCK); + HASH_BLOCK_DATA_ORDER_ALIGNED (c,c->data,1); + } #endif #endif #if defined(HASH_BLOCK_DATA_ORDER) - HASH_BLOCK_DATA_ORDER (c,data,1); + HASH_BLOCK_DATA_ORDER (c,data,1); #endif - } +} int HASH_FINAL (unsigned char *md, HASH_CTX *c) - { - register HASH_LONG *p; - register unsigned long l; - register int i,j; - static const unsigned char end[4]={0x80,0x00,0x00,0x00}; - const unsigned char *cp=end; - - /* c->num should definitly have room for at least one more byte. */ - p=c->data; - i=c->num>>2; - j=c->num&0x03; +{ + register HASH_LONG *p; + register unsigned long l; + register int i,j; + static const unsigned char end[4]={0x80,0x00,0x00,0x00}; + const unsigned char *cp=end; + + /* c->num should definitly have room for at least one more byte. */ + p=c->data; + i=c->num>>2; + j=c->num&0x03; #if 0 - /* purify often complains about the following line as an - * Uninitialized Memory Read. While this can be true, the - * following p_c2l macro will reset l when that case is true. - * This is because j&0x03 contains the number of 'valid' bytes - * already in p[i]. If and only if j&0x03 == 0, the UMR will - * occur but this is also the only time p_c2l will do - * l= *(cp++) instead of l|= *(cp++) - * Many thanks to Alex Tang for pickup this - * 'potential bug' */ + /* purify often complains about the following line as an + * Uninitialized Memory Read. While this can be true, the + * following p_c2l macro will reset l when that case is true. + * This is because j&0x03 contains the number of 'valid' bytes + * already in p[i]. If and only if j&0x03 == 0, the UMR will + * occur but this is also the only time p_c2l will do + * l= *(cp++) instead of l|= *(cp++) + * Many thanks to Alex Tang for pickup this + * 'potential bug' */ #ifdef PURIFY - if (j==0) p[i]=0; /* Yeah, but that's not the way to fix it:-) */ + if (j==0) p[i]=0; /* Yeah, but that's not the way to fix it:-) */ #endif - l=p[i]; + l=p[i]; #else - l = (j==0) ? 0 : p[i]; + l = (j==0) ? 0 : p[i]; #endif - HOST_p_c2l(cp,l,j); p[i++]=l; /* i is the next 'undefined word' */ + HOST_p_c2l(cp,l,j); p[i++]=l; /* i is the next 'undefined word' */ - if (i>(HASH_LBLOCK-2)) /* save room for Nl and Nh */ - { - if (i(HASH_LBLOCK-2)) /* save room for Nl and Nh */ + { + if (iNh; - p[HASH_LBLOCK-1]=c->Nl; + p[HASH_LBLOCK-2]=c->Nh; + p[HASH_LBLOCK-1]=c->Nl; #elif defined(DATA_ORDER_IS_LITTLE_ENDIAN) - p[HASH_LBLOCK-2]=c->Nl; - p[HASH_LBLOCK-1]=c->Nh; + p[HASH_LBLOCK-2]=c->Nl; + p[HASH_LBLOCK-1]=c->Nh; #endif - HASH_BLOCK_HOST_ORDER (c,p,1); + HASH_BLOCK_HOST_ORDER (c,p,1); #ifndef HASH_MAKE_STRING #error "HASH_MAKE_STRING must be defined!" #else - HASH_MAKE_STRING(c,md); + HASH_MAKE_STRING(c,md); #endif - c->num=0; - /* clear stuff, HASH_BLOCK may be leaving some stuff on the stack - * but I'm not worried :-) - OPENSSL_cleanse((void *)c,sizeof(HASH_CTX)); - */ - return 1; - } + c->num=0; + /* clear stuff, HASH_BLOCK may be leaving some stuff on the stack + * but I'm not worried :-) + OPENSSL_cleanse((void *)c,sizeof(HASH_CTX)); + */ + return 1; +} #ifndef MD32_REG_T #define MD32_REG_T long @@ -872,7 +858,7 @@ int HASH_FINAL (unsigned char *md, HASH_CTX *c) * *either* case. Now declaring 'em long excuses the compiler * from keeping 32 MSBs zeroed resulting in 13% performance * improvement under SPARC Solaris7/64 and 5% under AlphaLinux. - * Well, to be honest it should say that this *prevents* + * Well, to be honest it should say that this *prevents* * performance degradation. * * Apparently there're LP64 compilers that generate better @@ -886,39 +872,39 @@ int HASH_FINAL (unsigned char *md, HASH_CTX *c) // from md5_locl.h (continued) /* -#define F(x,y,z) (((x) & (y)) | ((~(x)) & (z))) -#define G(x,y,z) (((x) & (z)) | ((y) & (~(z)))) -*/ + #define F(x,y,z) (((x) & (y)) | ((~(x)) & (z))) + #define G(x,y,z) (((x) & (z)) | ((y) & (~(z)))) + */ /* As pointed out by Wei Dai , the above can be * simplified to the code below. Wei attributes these optimizations * to Peter Gutmann's SHS code, and he attributes it to Rich Schroeppel. */ -#define F(b,c,d) ((((c) ^ (d)) & (b)) ^ (d)) -#define G(b,c,d) ((((b) ^ (c)) & (d)) ^ (c)) -#define H(b,c,d) ((b) ^ (c) ^ (d)) -#define I(b,c,d) (((~(d)) | (b)) ^ (c)) +#define F(b,c,d) ((((c) ^ (d)) & (b)) ^ (d)) +#define G(b,c,d) ((((b) ^ (c)) & (d)) ^ (c)) +#define H(b,c,d) ((b) ^ (c) ^ (d)) +#define I(b,c,d) (((~(d)) | (b)) ^ (c)) #define R0(a,b,c,d,k,s,t) { \ - a+=((k)+(t)+F((b),(c),(d))); \ - a=ROTATE(a,s); \ - a+=b; };\ + a+=((k)+(t)+F((b),(c),(d))); \ + a=ROTATE(a,s); \ + a+=b; }; \ #define R1(a,b,c,d,k,s,t) { \ - a+=((k)+(t)+G((b),(c),(d))); \ - a=ROTATE(a,s); \ - a+=b; }; + a+=((k)+(t)+G((b),(c),(d))); \ + a=ROTATE(a,s); \ + a+=b; }; #define R2(a,b,c,d,k,s,t) { \ - a+=((k)+(t)+H((b),(c),(d))); \ - a=ROTATE(a,s); \ - a+=b; }; + a+=((k)+(t)+H((b),(c),(d))); \ + a=ROTATE(a,s); \ + a+=b; }; #define R3(a,b,c,d,k,s,t) { \ - a+=((k)+(t)+I((b),(c),(d))); \ - a=ROTATE(a,s); \ - a+=b; }; - + a+=((k)+(t)+I((b),(c),(d))); \ + a=ROTATE(a,s); \ + a+=b; }; + // from md5_dgst.c @@ -931,105 +917,105 @@ int HASH_FINAL (unsigned char *md, HASH_CTX *c) #define INIT_DATA_D (unsigned long)0x10325476L int MD5_Init(MD5_CTX *c) - { - c->A=INIT_DATA_A; - c->B=INIT_DATA_B; - c->C=INIT_DATA_C; - c->D=INIT_DATA_D; - c->Nl=0; - c->Nh=0; - c->num=0; - return 1; - } +{ + c->A=INIT_DATA_A; + c->B=INIT_DATA_B; + c->C=INIT_DATA_C; + c->D=INIT_DATA_D; + c->Nl=0; + c->Nh=0; + c->num=0; + return 1; +} #ifndef md5_block_host_order void md5_block_host_order (MD5_CTX *c, const void *data, int num) - { - const mDNSu32 *X=(const mDNSu32 *)data; - register unsigned MD32_REG_T A,B,C,D; - - A=c->A; - B=c->B; - C=c->C; - D=c->D; - - for (;num--;X+=HASH_LBLOCK) - { - /* Round 0 */ - R0(A,B,C,D,X[ 0], 7,0xd76aa478L); - R0(D,A,B,C,X[ 1],12,0xe8c7b756L); - R0(C,D,A,B,X[ 2],17,0x242070dbL); - R0(B,C,D,A,X[ 3],22,0xc1bdceeeL); - R0(A,B,C,D,X[ 4], 7,0xf57c0fafL); - R0(D,A,B,C,X[ 5],12,0x4787c62aL); - R0(C,D,A,B,X[ 6],17,0xa8304613L); - R0(B,C,D,A,X[ 7],22,0xfd469501L); - R0(A,B,C,D,X[ 8], 7,0x698098d8L); - R0(D,A,B,C,X[ 9],12,0x8b44f7afL); - R0(C,D,A,B,X[10],17,0xffff5bb1L); - R0(B,C,D,A,X[11],22,0x895cd7beL); - R0(A,B,C,D,X[12], 7,0x6b901122L); - R0(D,A,B,C,X[13],12,0xfd987193L); - R0(C,D,A,B,X[14],17,0xa679438eL); - R0(B,C,D,A,X[15],22,0x49b40821L); - /* Round 1 */ - R1(A,B,C,D,X[ 1], 5,0xf61e2562L); - R1(D,A,B,C,X[ 6], 9,0xc040b340L); - R1(C,D,A,B,X[11],14,0x265e5a51L); - R1(B,C,D,A,X[ 0],20,0xe9b6c7aaL); - R1(A,B,C,D,X[ 5], 5,0xd62f105dL); - R1(D,A,B,C,X[10], 9,0x02441453L); - R1(C,D,A,B,X[15],14,0xd8a1e681L); - R1(B,C,D,A,X[ 4],20,0xe7d3fbc8L); - R1(A,B,C,D,X[ 9], 5,0x21e1cde6L); - R1(D,A,B,C,X[14], 9,0xc33707d6L); - R1(C,D,A,B,X[ 3],14,0xf4d50d87L); - R1(B,C,D,A,X[ 8],20,0x455a14edL); - R1(A,B,C,D,X[13], 5,0xa9e3e905L); - R1(D,A,B,C,X[ 2], 9,0xfcefa3f8L); - R1(C,D,A,B,X[ 7],14,0x676f02d9L); - R1(B,C,D,A,X[12],20,0x8d2a4c8aL); - /* Round 2 */ - R2(A,B,C,D,X[ 5], 4,0xfffa3942L); - R2(D,A,B,C,X[ 8],11,0x8771f681L); - R2(C,D,A,B,X[11],16,0x6d9d6122L); - R2(B,C,D,A,X[14],23,0xfde5380cL); - R2(A,B,C,D,X[ 1], 4,0xa4beea44L); - R2(D,A,B,C,X[ 4],11,0x4bdecfa9L); - R2(C,D,A,B,X[ 7],16,0xf6bb4b60L); - R2(B,C,D,A,X[10],23,0xbebfbc70L); - R2(A,B,C,D,X[13], 4,0x289b7ec6L); - R2(D,A,B,C,X[ 0],11,0xeaa127faL); - R2(C,D,A,B,X[ 3],16,0xd4ef3085L); - R2(B,C,D,A,X[ 6],23,0x04881d05L); - R2(A,B,C,D,X[ 9], 4,0xd9d4d039L); - R2(D,A,B,C,X[12],11,0xe6db99e5L); - R2(C,D,A,B,X[15],16,0x1fa27cf8L); - R2(B,C,D,A,X[ 2],23,0xc4ac5665L); - /* Round 3 */ - R3(A,B,C,D,X[ 0], 6,0xf4292244L); - R3(D,A,B,C,X[ 7],10,0x432aff97L); - R3(C,D,A,B,X[14],15,0xab9423a7L); - R3(B,C,D,A,X[ 5],21,0xfc93a039L); - R3(A,B,C,D,X[12], 6,0x655b59c3L); - R3(D,A,B,C,X[ 3],10,0x8f0ccc92L); - R3(C,D,A,B,X[10],15,0xffeff47dL); - R3(B,C,D,A,X[ 1],21,0x85845dd1L); - R3(A,B,C,D,X[ 8], 6,0x6fa87e4fL); - R3(D,A,B,C,X[15],10,0xfe2ce6e0L); - R3(C,D,A,B,X[ 6],15,0xa3014314L); - R3(B,C,D,A,X[13],21,0x4e0811a1L); - R3(A,B,C,D,X[ 4], 6,0xf7537e82L); - R3(D,A,B,C,X[11],10,0xbd3af235L); - R3(C,D,A,B,X[ 2],15,0x2ad7d2bbL); - R3(B,C,D,A,X[ 9],21,0xeb86d391L); - - A = c->A += A; - B = c->B += B; - C = c->C += C; - D = c->D += D; - } - } +{ + const mDNSu32 *X=(const mDNSu32 *)data; + register unsigned MD32_REG_T A,B,C,D; + + A=c->A; + B=c->B; + C=c->C; + D=c->D; + + for (; num--; X+=HASH_LBLOCK) + { + /* Round 0 */ + R0(A,B,C,D,X[ 0], 7,0xd76aa478L); + R0(D,A,B,C,X[ 1],12,0xe8c7b756L); + R0(C,D,A,B,X[ 2],17,0x242070dbL); + R0(B,C,D,A,X[ 3],22,0xc1bdceeeL); + R0(A,B,C,D,X[ 4], 7,0xf57c0fafL); + R0(D,A,B,C,X[ 5],12,0x4787c62aL); + R0(C,D,A,B,X[ 6],17,0xa8304613L); + R0(B,C,D,A,X[ 7],22,0xfd469501L); + R0(A,B,C,D,X[ 8], 7,0x698098d8L); + R0(D,A,B,C,X[ 9],12,0x8b44f7afL); + R0(C,D,A,B,X[10],17,0xffff5bb1L); + R0(B,C,D,A,X[11],22,0x895cd7beL); + R0(A,B,C,D,X[12], 7,0x6b901122L); + R0(D,A,B,C,X[13],12,0xfd987193L); + R0(C,D,A,B,X[14],17,0xa679438eL); + R0(B,C,D,A,X[15],22,0x49b40821L); + /* Round 1 */ + R1(A,B,C,D,X[ 1], 5,0xf61e2562L); + R1(D,A,B,C,X[ 6], 9,0xc040b340L); + R1(C,D,A,B,X[11],14,0x265e5a51L); + R1(B,C,D,A,X[ 0],20,0xe9b6c7aaL); + R1(A,B,C,D,X[ 5], 5,0xd62f105dL); + R1(D,A,B,C,X[10], 9,0x02441453L); + R1(C,D,A,B,X[15],14,0xd8a1e681L); + R1(B,C,D,A,X[ 4],20,0xe7d3fbc8L); + R1(A,B,C,D,X[ 9], 5,0x21e1cde6L); + R1(D,A,B,C,X[14], 9,0xc33707d6L); + R1(C,D,A,B,X[ 3],14,0xf4d50d87L); + R1(B,C,D,A,X[ 8],20,0x455a14edL); + R1(A,B,C,D,X[13], 5,0xa9e3e905L); + R1(D,A,B,C,X[ 2], 9,0xfcefa3f8L); + R1(C,D,A,B,X[ 7],14,0x676f02d9L); + R1(B,C,D,A,X[12],20,0x8d2a4c8aL); + /* Round 2 */ + R2(A,B,C,D,X[ 5], 4,0xfffa3942L); + R2(D,A,B,C,X[ 8],11,0x8771f681L); + R2(C,D,A,B,X[11],16,0x6d9d6122L); + R2(B,C,D,A,X[14],23,0xfde5380cL); + R2(A,B,C,D,X[ 1], 4,0xa4beea44L); + R2(D,A,B,C,X[ 4],11,0x4bdecfa9L); + R2(C,D,A,B,X[ 7],16,0xf6bb4b60L); + R2(B,C,D,A,X[10],23,0xbebfbc70L); + R2(A,B,C,D,X[13], 4,0x289b7ec6L); + R2(D,A,B,C,X[ 0],11,0xeaa127faL); + R2(C,D,A,B,X[ 3],16,0xd4ef3085L); + R2(B,C,D,A,X[ 6],23,0x04881d05L); + R2(A,B,C,D,X[ 9], 4,0xd9d4d039L); + R2(D,A,B,C,X[12],11,0xe6db99e5L); + R2(C,D,A,B,X[15],16,0x1fa27cf8L); + R2(B,C,D,A,X[ 2],23,0xc4ac5665L); + /* Round 3 */ + R3(A,B,C,D,X[ 0], 6,0xf4292244L); + R3(D,A,B,C,X[ 7],10,0x432aff97L); + R3(C,D,A,B,X[14],15,0xab9423a7L); + R3(B,C,D,A,X[ 5],21,0xfc93a039L); + R3(A,B,C,D,X[12], 6,0x655b59c3L); + R3(D,A,B,C,X[ 3],10,0x8f0ccc92L); + R3(C,D,A,B,X[10],15,0xffeff47dL); + R3(B,C,D,A,X[ 1],21,0x85845dd1L); + R3(A,B,C,D,X[ 8], 6,0x6fa87e4fL); + R3(D,A,B,C,X[15],10,0xfe2ce6e0L); + R3(C,D,A,B,X[ 6],15,0xa3014314L); + R3(B,C,D,A,X[13],21,0x4e0811a1L); + R3(A,B,C,D,X[ 4], 6,0xf7537e82L); + R3(D,A,B,C,X[11],10,0xbd3af235L); + R3(C,D,A,B,X[ 2],15,0x2ad7d2bbL); + R3(B,C,D,A,X[ 9],21,0xeb86d391L); + + A = c->A += A; + B = c->B += B; + C = c->C += C; + D = c->D += D; + } +} #endif #ifndef md5_block_data_order @@ -1037,106 +1023,106 @@ void md5_block_host_order (MD5_CTX *c, const void *data, int num) #undef X #endif void md5_block_data_order (MD5_CTX *c, const void *data_, int num) - { - const unsigned char *data=data_; - register unsigned MD32_REG_T A,B,C,D,l; +{ + const unsigned char *data=data_; + register unsigned MD32_REG_T A,B,C,D,l; #ifndef MD32_XARRAY - /* See comment in crypto/sha/sha_locl.h for details. */ - unsigned MD32_REG_T XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7, - XX8, XX9,XX10,XX11,XX12,XX13,XX14,XX15; -# define X(i) XX##i + /* See comment in crypto/sha/sha_locl.h for details. */ + unsigned MD32_REG_T XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7, + XX8, XX9,XX10,XX11,XX12,XX13,XX14,XX15; +# define X(i) XX ## i #else - mDNSu32 XX[MD5_LBLOCK]; -# define X(i) XX[i] + mDNSu32 XX[MD5_LBLOCK]; +# define X(i) XX[i] #endif - A=c->A; - B=c->B; - C=c->C; - D=c->D; - - for (;num--;) - { - HOST_c2l(data,l); X( 0)=l; HOST_c2l(data,l); X( 1)=l; - /* Round 0 */ - R0(A,B,C,D,X( 0), 7,0xd76aa478L); HOST_c2l(data,l); X( 2)=l; - R0(D,A,B,C,X( 1),12,0xe8c7b756L); HOST_c2l(data,l); X( 3)=l; - R0(C,D,A,B,X( 2),17,0x242070dbL); HOST_c2l(data,l); X( 4)=l; - R0(B,C,D,A,X( 3),22,0xc1bdceeeL); HOST_c2l(data,l); X( 5)=l; - R0(A,B,C,D,X( 4), 7,0xf57c0fafL); HOST_c2l(data,l); X( 6)=l; - R0(D,A,B,C,X( 5),12,0x4787c62aL); HOST_c2l(data,l); X( 7)=l; - R0(C,D,A,B,X( 6),17,0xa8304613L); HOST_c2l(data,l); X( 8)=l; - R0(B,C,D,A,X( 7),22,0xfd469501L); HOST_c2l(data,l); X( 9)=l; - R0(A,B,C,D,X( 8), 7,0x698098d8L); HOST_c2l(data,l); X(10)=l; - R0(D,A,B,C,X( 9),12,0x8b44f7afL); HOST_c2l(data,l); X(11)=l; - R0(C,D,A,B,X(10),17,0xffff5bb1L); HOST_c2l(data,l); X(12)=l; - R0(B,C,D,A,X(11),22,0x895cd7beL); HOST_c2l(data,l); X(13)=l; - R0(A,B,C,D,X(12), 7,0x6b901122L); HOST_c2l(data,l); X(14)=l; - R0(D,A,B,C,X(13),12,0xfd987193L); HOST_c2l(data,l); X(15)=l; - R0(C,D,A,B,X(14),17,0xa679438eL); - R0(B,C,D,A,X(15),22,0x49b40821L); - /* Round 1 */ - R1(A,B,C,D,X( 1), 5,0xf61e2562L); - R1(D,A,B,C,X( 6), 9,0xc040b340L); - R1(C,D,A,B,X(11),14,0x265e5a51L); - R1(B,C,D,A,X( 0),20,0xe9b6c7aaL); - R1(A,B,C,D,X( 5), 5,0xd62f105dL); - R1(D,A,B,C,X(10), 9,0x02441453L); - R1(C,D,A,B,X(15),14,0xd8a1e681L); - R1(B,C,D,A,X( 4),20,0xe7d3fbc8L); - R1(A,B,C,D,X( 9), 5,0x21e1cde6L); - R1(D,A,B,C,X(14), 9,0xc33707d6L); - R1(C,D,A,B,X( 3),14,0xf4d50d87L); - R1(B,C,D,A,X( 8),20,0x455a14edL); - R1(A,B,C,D,X(13), 5,0xa9e3e905L); - R1(D,A,B,C,X( 2), 9,0xfcefa3f8L); - R1(C,D,A,B,X( 7),14,0x676f02d9L); - R1(B,C,D,A,X(12),20,0x8d2a4c8aL); - /* Round 2 */ - R2(A,B,C,D,X( 5), 4,0xfffa3942L); - R2(D,A,B,C,X( 8),11,0x8771f681L); - R2(C,D,A,B,X(11),16,0x6d9d6122L); - R2(B,C,D,A,X(14),23,0xfde5380cL); - R2(A,B,C,D,X( 1), 4,0xa4beea44L); - R2(D,A,B,C,X( 4),11,0x4bdecfa9L); - R2(C,D,A,B,X( 7),16,0xf6bb4b60L); - R2(B,C,D,A,X(10),23,0xbebfbc70L); - R2(A,B,C,D,X(13), 4,0x289b7ec6L); - R2(D,A,B,C,X( 0),11,0xeaa127faL); - R2(C,D,A,B,X( 3),16,0xd4ef3085L); - R2(B,C,D,A,X( 6),23,0x04881d05L); - R2(A,B,C,D,X( 9), 4,0xd9d4d039L); - R2(D,A,B,C,X(12),11,0xe6db99e5L); - R2(C,D,A,B,X(15),16,0x1fa27cf8L); - R2(B,C,D,A,X( 2),23,0xc4ac5665L); - /* Round 3 */ - R3(A,B,C,D,X( 0), 6,0xf4292244L); - R3(D,A,B,C,X( 7),10,0x432aff97L); - R3(C,D,A,B,X(14),15,0xab9423a7L); - R3(B,C,D,A,X( 5),21,0xfc93a039L); - R3(A,B,C,D,X(12), 6,0x655b59c3L); - R3(D,A,B,C,X( 3),10,0x8f0ccc92L); - R3(C,D,A,B,X(10),15,0xffeff47dL); - R3(B,C,D,A,X( 1),21,0x85845dd1L); - R3(A,B,C,D,X( 8), 6,0x6fa87e4fL); - R3(D,A,B,C,X(15),10,0xfe2ce6e0L); - R3(C,D,A,B,X( 6),15,0xa3014314L); - R3(B,C,D,A,X(13),21,0x4e0811a1L); - R3(A,B,C,D,X( 4), 6,0xf7537e82L); - R3(D,A,B,C,X(11),10,0xbd3af235L); - R3(C,D,A,B,X( 2),15,0x2ad7d2bbL); - R3(B,C,D,A,X( 9),21,0xeb86d391L); - - A = c->A += A; - B = c->B += B; - C = c->C += C; - D = c->D += D; - } - } + A=c->A; + B=c->B; + C=c->C; + D=c->D; + + for (; num--;) + { + HOST_c2l(data,l); X( 0)=l; HOST_c2l(data,l); X( 1)=l; + /* Round 0 */ + R0(A,B,C,D,X( 0), 7,0xd76aa478L); HOST_c2l(data,l); X( 2)=l; + R0(D,A,B,C,X( 1),12,0xe8c7b756L); HOST_c2l(data,l); X( 3)=l; + R0(C,D,A,B,X( 2),17,0x242070dbL); HOST_c2l(data,l); X( 4)=l; + R0(B,C,D,A,X( 3),22,0xc1bdceeeL); HOST_c2l(data,l); X( 5)=l; + R0(A,B,C,D,X( 4), 7,0xf57c0fafL); HOST_c2l(data,l); X( 6)=l; + R0(D,A,B,C,X( 5),12,0x4787c62aL); HOST_c2l(data,l); X( 7)=l; + R0(C,D,A,B,X( 6),17,0xa8304613L); HOST_c2l(data,l); X( 8)=l; + R0(B,C,D,A,X( 7),22,0xfd469501L); HOST_c2l(data,l); X( 9)=l; + R0(A,B,C,D,X( 8), 7,0x698098d8L); HOST_c2l(data,l); X(10)=l; + R0(D,A,B,C,X( 9),12,0x8b44f7afL); HOST_c2l(data,l); X(11)=l; + R0(C,D,A,B,X(10),17,0xffff5bb1L); HOST_c2l(data,l); X(12)=l; + R0(B,C,D,A,X(11),22,0x895cd7beL); HOST_c2l(data,l); X(13)=l; + R0(A,B,C,D,X(12), 7,0x6b901122L); HOST_c2l(data,l); X(14)=l; + R0(D,A,B,C,X(13),12,0xfd987193L); HOST_c2l(data,l); X(15)=l; + R0(C,D,A,B,X(14),17,0xa679438eL); + R0(B,C,D,A,X(15),22,0x49b40821L); + /* Round 1 */ + R1(A,B,C,D,X( 1), 5,0xf61e2562L); + R1(D,A,B,C,X( 6), 9,0xc040b340L); + R1(C,D,A,B,X(11),14,0x265e5a51L); + R1(B,C,D,A,X( 0),20,0xe9b6c7aaL); + R1(A,B,C,D,X( 5), 5,0xd62f105dL); + R1(D,A,B,C,X(10), 9,0x02441453L); + R1(C,D,A,B,X(15),14,0xd8a1e681L); + R1(B,C,D,A,X( 4),20,0xe7d3fbc8L); + R1(A,B,C,D,X( 9), 5,0x21e1cde6L); + R1(D,A,B,C,X(14), 9,0xc33707d6L); + R1(C,D,A,B,X( 3),14,0xf4d50d87L); + R1(B,C,D,A,X( 8),20,0x455a14edL); + R1(A,B,C,D,X(13), 5,0xa9e3e905L); + R1(D,A,B,C,X( 2), 9,0xfcefa3f8L); + R1(C,D,A,B,X( 7),14,0x676f02d9L); + R1(B,C,D,A,X(12),20,0x8d2a4c8aL); + /* Round 2 */ + R2(A,B,C,D,X( 5), 4,0xfffa3942L); + R2(D,A,B,C,X( 8),11,0x8771f681L); + R2(C,D,A,B,X(11),16,0x6d9d6122L); + R2(B,C,D,A,X(14),23,0xfde5380cL); + R2(A,B,C,D,X( 1), 4,0xa4beea44L); + R2(D,A,B,C,X( 4),11,0x4bdecfa9L); + R2(C,D,A,B,X( 7),16,0xf6bb4b60L); + R2(B,C,D,A,X(10),23,0xbebfbc70L); + R2(A,B,C,D,X(13), 4,0x289b7ec6L); + R2(D,A,B,C,X( 0),11,0xeaa127faL); + R2(C,D,A,B,X( 3),16,0xd4ef3085L); + R2(B,C,D,A,X( 6),23,0x04881d05L); + R2(A,B,C,D,X( 9), 4,0xd9d4d039L); + R2(D,A,B,C,X(12),11,0xe6db99e5L); + R2(C,D,A,B,X(15),16,0x1fa27cf8L); + R2(B,C,D,A,X( 2),23,0xc4ac5665L); + /* Round 3 */ + R3(A,B,C,D,X( 0), 6,0xf4292244L); + R3(D,A,B,C,X( 7),10,0x432aff97L); + R3(C,D,A,B,X(14),15,0xab9423a7L); + R3(B,C,D,A,X( 5),21,0xfc93a039L); + R3(A,B,C,D,X(12), 6,0x655b59c3L); + R3(D,A,B,C,X( 3),10,0x8f0ccc92L); + R3(C,D,A,B,X(10),15,0xffeff47dL); + R3(B,C,D,A,X( 1),21,0x85845dd1L); + R3(A,B,C,D,X( 8), 6,0x6fa87e4fL); + R3(D,A,B,C,X(15),10,0xfe2ce6e0L); + R3(C,D,A,B,X( 6),15,0xa3014314L); + R3(B,C,D,A,X(13),21,0x4e0811a1L); + R3(A,B,C,D,X( 4), 6,0xf7537e82L); + R3(D,A,B,C,X(11),10,0xbd3af235L); + R3(C,D,A,B,X( 2),15,0x2ad7d2bbL); + R3(B,C,D,A,X( 9),21,0xeb86d391L); + + A = c->A += A; + B = c->B += B; + C = c->C += C; + D = c->D += D; + } +} #endif - // *************************************************************************** +// *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - base64 -> binary conversion #endif @@ -1148,14 +1134,14 @@ static const char Pad64 = '='; #define mDNSisspace(x) (x == '\t' || x == '\n' || x == '\v' || x == '\f' || x == '\r' || x == ' ') mDNSlocal const char *mDNSstrchr(const char *s, int c) - { - while (1) - { - if (c == *s) return s; - if (!*s) return mDNSNULL; - s++; - } - } +{ + while (1) + { + if (c == *s) return s; + if (!*s) return mDNSNULL; + s++; + } +} // skips all whitespace anywhere. // converts characters, four at a time, starting at (or after) @@ -1164,123 +1150,123 @@ mDNSlocal const char *mDNSstrchr(const char *s, int c) // adapted from BIND sources mDNSlocal mDNSs32 DNSDigest_Base64ToBin(const char *src, mDNSu8 *target, mDNSu32 targsize) - { - int tarindex, state, ch; - const char *pos; - - state = 0; - tarindex = 0; - - while ((ch = *src++) != '\0') { - if (mDNSisspace(ch)) /* Skip whitespace anywhere. */ - continue; - - if (ch == Pad64) - break; - - pos = mDNSstrchr(Base64, ch); - if (pos == 0) /* A non-base64 character. */ - return (-1); - - switch (state) { - case 0: - if (target) { - if ((mDNSu32)tarindex >= targsize) - return (-1); - target[tarindex] = (mDNSu8)((pos - Base64) << 2); - } - state = 1; - break; - case 1: - if (target) { - if ((mDNSu32)tarindex + 1 >= targsize) - return (-1); - target[tarindex] |= (pos - Base64) >> 4; - target[tarindex+1] = (mDNSu8)(((pos - Base64) & 0x0f) << 4); - } - tarindex++; - state = 2; - break; - case 2: - if (target) { - if ((mDNSu32)tarindex + 1 >= targsize) - return (-1); - target[tarindex] |= (pos - Base64) >> 2; - target[tarindex+1] = (mDNSu8)(((pos - Base64) & 0x03) << 6); - } - tarindex++; - state = 3; - break; - case 3: - if (target) { - if ((mDNSu32)tarindex >= targsize) - return (-1); - target[tarindex] |= (pos - Base64); - } - tarindex++; - state = 0; - break; - default: - return -1; - } - } - - /* - * We are done decoding Base-64 chars. Let's see if we ended - * on a byte boundary, and/or with erroneous trailing characters. - */ - - if (ch == Pad64) { /* We got a pad char. */ - ch = *src++; /* Skip it, get next. */ - switch (state) { - case 0: /* Invalid = in first position */ - case 1: /* Invalid = in second position */ - return (-1); - - case 2: /* Valid, means one byte of info */ - /* Skip any number of spaces. */ - for ((void)mDNSNULL; ch != '\0'; ch = *src++) - if (!mDNSisspace(ch)) - break; - /* Make sure there is another trailing = sign. */ - if (ch != Pad64) - return (-1); - ch = *src++; /* Skip the = */ - /* Fall through to "single trailing =" case. */ - /* FALLTHROUGH */ - - case 3: /* Valid, means two bytes of info */ - /* - * We know this char is an =. Is there anything but - * whitespace after it? - */ - for ((void)mDNSNULL; ch != '\0'; ch = *src++) - if (!mDNSisspace(ch)) - return (-1); - - /* - * Now make sure for cases 2 and 3 that the "extra" - * bits that slopped past the last full byte were - * zeros. If we don't check them, they become a - * subliminal channel. - */ - if (target && target[tarindex] != 0) - return (-1); - } - } else { - /* - * We ended by seeing the end of the string. Make sure we - * have no partial bytes lying around. - */ - if (state != 0) - return (-1); - } - - return (tarindex); - } - - - // *************************************************************************** +{ + int tarindex, state, ch; + const char *pos; + + state = 0; + tarindex = 0; + + while ((ch = *src++) != '\0') { + if (mDNSisspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = mDNSstrchr(Base64, ch); + if (pos == 0) /* A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if ((mDNSu32)tarindex >= targsize) + return (-1); + target[tarindex] = (mDNSu8)((pos - Base64) << 2); + } + state = 1; + break; + case 1: + if (target) { + if ((mDNSu32)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex+1] = (mDNSu8)(((pos - Base64) & 0x0f) << 4); + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if ((mDNSu32)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex+1] = (mDNSu8)(((pos - Base64) & 0x03) << 6); + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if ((mDNSu32)tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + default: + return -1; + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for ((void)mDNSNULL; ch != '\0'; ch = *src++) + if (!mDNSisspace(ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for ((void)mDNSNULL; ch != '\0'; ch = *src++) + if (!mDNSisspace(ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); +} + + +// *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - API exported to mDNS Core #endif @@ -1293,290 +1279,290 @@ mDNSlocal mDNSs32 DNSDigest_Base64ToBin(const char *src, mDNSu8 *target, mDNSu32 #define HMAC_MD5_AlgName (*(const domainname*) "\010" "hmac-md5" "\007" "sig-alg" "\003" "reg" "\003" "int") // Adapted from Appendix, RFC 2104 -mDNSlocal void DNSDigest_ConstructHMACKey(DomainAuthInfo *info, const mDNSu8 *key, mDNSu32 len) - { - MD5_CTX k; - mDNSu8 buf[MD5_LEN]; - int i; - - // If key is longer than HMAC_LEN reset it to MD5(key) - if (len > HMAC_LEN) - { - MD5_Init(&k); - MD5_Update(&k, key, len); - MD5_Final(buf, &k); - key = buf; - len = MD5_LEN; - } - - // store key in pads - mDNSPlatformMemZero(info->keydata_ipad, HMAC_LEN); - mDNSPlatformMemZero(info->keydata_opad, HMAC_LEN); - mDNSPlatformMemCopy(info->keydata_ipad, key, len); - mDNSPlatformMemCopy(info->keydata_opad, key, len); - - // XOR key with ipad and opad values - for (i = 0; i < HMAC_LEN; i++) - { - info->keydata_ipad[i] ^= HMAC_IPAD; - info->keydata_opad[i] ^= HMAC_OPAD; - } - - } +mDNSlocal void DNSDigest_ConstructHMACKey(DomainAuthInfo *info, const mDNSu8 *key, mDNSu32 len) +{ + MD5_CTX k; + mDNSu8 buf[MD5_LEN]; + int i; + + // If key is longer than HMAC_LEN reset it to MD5(key) + if (len > HMAC_LEN) + { + MD5_Init(&k); + MD5_Update(&k, key, len); + MD5_Final(buf, &k); + key = buf; + len = MD5_LEN; + } + + // store key in pads + mDNSPlatformMemZero(info->keydata_ipad, HMAC_LEN); + mDNSPlatformMemZero(info->keydata_opad, HMAC_LEN); + mDNSPlatformMemCopy(info->keydata_ipad, key, len); + mDNSPlatformMemCopy(info->keydata_opad, key, len); + + // XOR key with ipad and opad values + for (i = 0; i < HMAC_LEN; i++) + { + info->keydata_ipad[i] ^= HMAC_IPAD; + info->keydata_opad[i] ^= HMAC_OPAD; + } + +} mDNSexport mDNSs32 DNSDigest_ConstructHMACKeyfromBase64(DomainAuthInfo *info, const char *b64key) - { - mDNSu8 keybuf[1024]; - mDNSs32 keylen = DNSDigest_Base64ToBin(b64key, keybuf, sizeof(keybuf)); - if (keylen < 0) return(keylen); - DNSDigest_ConstructHMACKey(info, keybuf, (mDNSu32)keylen); - return(keylen); - } +{ + mDNSu8 keybuf[1024]; + mDNSs32 keylen = DNSDigest_Base64ToBin(b64key, keybuf, sizeof(keybuf)); + if (keylen < 0) return(keylen); + DNSDigest_ConstructHMACKey(info, keybuf, (mDNSu32)keylen); + return(keylen); +} mDNSexport void DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, DomainAuthInfo *info, mDNSu16 tcode) - { - AuthRecord tsig; - mDNSu8 *rdata, *const countPtr = (mDNSu8 *)&msg->h.numAdditionals; // Get existing numAdditionals value - mDNSu32 utc32; - mDNSu8 utc48[6]; - mDNSu8 digest[MD5_LEN]; - mDNSu8 *ptr = *end; - mDNSu32 len; - mDNSOpaque16 buf; - MD5_CTX c; - mDNSu16 numAdditionals = (mDNSu16)((mDNSu16)countPtr[0] << 8 | countPtr[1]); - - // Init MD5 context, digest inner key pad and message +{ + AuthRecord tsig; + mDNSu8 *rdata, *const countPtr = (mDNSu8 *)&msg->h.numAdditionals; // Get existing numAdditionals value + mDNSu32 utc32; + mDNSu8 utc48[6]; + mDNSu8 digest[MD5_LEN]; + mDNSu8 *ptr = *end; + mDNSu32 len; + mDNSOpaque16 buf; + MD5_CTX c; + mDNSu16 numAdditionals = (mDNSu16)((mDNSu16)countPtr[0] << 8 | countPtr[1]); + + // Init MD5 context, digest inner key pad and message MD5_Init(&c); MD5_Update(&c, info->keydata_ipad, HMAC_LEN); - MD5_Update(&c, (mDNSu8 *)msg, (unsigned long)(*end - (mDNSu8 *)msg)); - - // Construct TSIG RR, digesting variables as apporpriate - mDNS_SetupResourceRecord(&tsig, mDNSNULL, 0, kDNSType_TSIG, 0, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); - - // key name - AssignDomainName(&tsig.namestorage, &info->keyname); - MD5_Update(&c, info->keyname.c, DomainNameLength(&info->keyname)); - - // class - tsig.resrec.rrclass = kDNSQClass_ANY; - buf = mDNSOpaque16fromIntVal(kDNSQClass_ANY); - MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); - - // ttl - tsig.resrec.rroriginalttl = 0; - MD5_Update(&c, (mDNSu8 *)&tsig.resrec.rroriginalttl, sizeof(tsig.resrec.rroriginalttl)); - - // alg name - 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 - // get UTC (universal time), convert to 48-bit unsigned in network byte order - utc32 = (mDNSu32)mDNSPlatformUTC(); - if (utc32 == (unsigned)-1) { LogMsg("ERROR: DNSDigest_SignMessage - mDNSPlatformUTC returned bad time -1"); *end = mDNSNULL; } - utc48[0] = 0; - utc48[1] = 0; - utc48[2] = (mDNSu8)((utc32 >> 24) & 0xff); - utc48[3] = (mDNSu8)((utc32 >> 16) & 0xff); - utc48[4] = (mDNSu8)((utc32 >> 8) & 0xff); - utc48[5] = (mDNSu8)( utc32 & 0xff); - - mDNSPlatformMemCopy(rdata, utc48, 6); - rdata += 6; - MD5_Update(&c, utc48, 6); - - // 300 sec is fudge recommended in RFC 2485 - rdata[0] = (mDNSu8)((300 >> 8) & 0xff); - rdata[1] = (mDNSu8)( 300 & 0xff); - MD5_Update(&c, rdata, sizeof(mDNSOpaque16)); - rdata += sizeof(mDNSOpaque16); - - // digest error (tcode) and other data len (zero) - we'll add them to the rdata later - buf.b[0] = (mDNSu8)((tcode >> 8) & 0xff); - buf.b[1] = (mDNSu8)( tcode & 0xff); - MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); // error - buf.NotAnInteger = 0; - MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); // other data len - - // finish the message & tsig var hash + MD5_Update(&c, (mDNSu8 *)msg, (unsigned long)(*end - (mDNSu8 *)msg)); + + // Construct TSIG RR, digesting variables as apporpriate + mDNS_SetupResourceRecord(&tsig, mDNSNULL, 0, kDNSType_TSIG, 0, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + + // key name + AssignDomainName(&tsig.namestorage, &info->keyname); + MD5_Update(&c, info->keyname.c, DomainNameLength(&info->keyname)); + + // class + tsig.resrec.rrclass = kDNSQClass_ANY; + buf = mDNSOpaque16fromIntVal(kDNSQClass_ANY); + MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); + + // ttl + tsig.resrec.rroriginalttl = 0; + MD5_Update(&c, (mDNSu8 *)&tsig.resrec.rroriginalttl, sizeof(tsig.resrec.rroriginalttl)); + + // alg name + 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 + // get UTC (universal time), convert to 48-bit unsigned in network byte order + utc32 = (mDNSu32)mDNSPlatformUTC(); + if (utc32 == (unsigned)-1) { LogMsg("ERROR: DNSDigest_SignMessage - mDNSPlatformUTC returned bad time -1"); *end = mDNSNULL; } + utc48[0] = 0; + utc48[1] = 0; + utc48[2] = (mDNSu8)((utc32 >> 24) & 0xff); + utc48[3] = (mDNSu8)((utc32 >> 16) & 0xff); + utc48[4] = (mDNSu8)((utc32 >> 8) & 0xff); + utc48[5] = (mDNSu8)( utc32 & 0xff); + + mDNSPlatformMemCopy(rdata, utc48, 6); + rdata += 6; + MD5_Update(&c, utc48, 6); + + // 300 sec is fudge recommended in RFC 2485 + rdata[0] = (mDNSu8)((300 >> 8) & 0xff); + rdata[1] = (mDNSu8)( 300 & 0xff); + MD5_Update(&c, rdata, sizeof(mDNSOpaque16)); + rdata += sizeof(mDNSOpaque16); + + // digest error (tcode) and other data len (zero) - we'll add them to the rdata later + buf.b[0] = (mDNSu8)((tcode >> 8) & 0xff); + buf.b[1] = (mDNSu8)( tcode & 0xff); + MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); // error + buf.NotAnInteger = 0; + MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); // other data len + + // finish the message & tsig var hash + MD5_Final(digest, &c); + + // perform outer MD5 (outer key pad, inner digest) + MD5_Init(&c); + MD5_Update(&c, info->keydata_opad, HMAC_LEN); + MD5_Update(&c, digest, MD5_LEN); MD5_Final(digest, &c); - - // perform outer MD5 (outer key pad, inner digest) - MD5_Init(&c); - MD5_Update(&c, info->keydata_opad, HMAC_LEN); - MD5_Update(&c, digest, MD5_LEN); - MD5_Final(digest, &c); - - // set remaining rdata fields - rdata[0] = (mDNSu8)((MD5_LEN >> 8) & 0xff); - rdata[1] = (mDNSu8)( MD5_LEN & 0xff); - rdata += sizeof(mDNSOpaque16); - mDNSPlatformMemCopy(rdata, digest, MD5_LEN); // MAC - rdata += MD5_LEN; - rdata[0] = msg->h.id.b[0]; // original ID - rdata[1] = msg->h.id.b[1]; - rdata[2] = (mDNSu8)((tcode >> 8) & 0xff); - rdata[3] = (mDNSu8)( tcode & 0xff); - rdata[4] = 0; // other data len - rdata[5] = 0; - rdata += 6; - - tsig.resrec.rdlength = (mDNSu16)(rdata - tsig.resrec.rdata->u.data); - *end = PutResourceRecordTTLJumbo(msg, ptr, &numAdditionals, &tsig.resrec, 0); - if (!*end) { LogMsg("ERROR: DNSDigest_SignMessage - could not put TSIG"); *end = mDNSNULL; return; } - - // Write back updated numAdditionals value - countPtr[0] = (mDNSu8)(numAdditionals >> 8); - countPtr[1] = (mDNSu8)(numAdditionals & 0xFF); - } + + // set remaining rdata fields + rdata[0] = (mDNSu8)((MD5_LEN >> 8) & 0xff); + rdata[1] = (mDNSu8)( MD5_LEN & 0xff); + rdata += sizeof(mDNSOpaque16); + mDNSPlatformMemCopy(rdata, digest, MD5_LEN); // MAC + rdata += MD5_LEN; + rdata[0] = msg->h.id.b[0]; // original ID + rdata[1] = msg->h.id.b[1]; + rdata[2] = (mDNSu8)((tcode >> 8) & 0xff); + rdata[3] = (mDNSu8)( tcode & 0xff); + rdata[4] = 0; // other data len + rdata[5] = 0; + rdata += 6; + + tsig.resrec.rdlength = (mDNSu16)(rdata - tsig.resrec.rdata->u.data); + *end = PutResourceRecordTTLJumbo(msg, ptr, &numAdditionals, &tsig.resrec, 0); + if (!*end) { LogMsg("ERROR: DNSDigest_SignMessage - could not put TSIG"); *end = mDNSNULL; return; } + + // Write back updated numAdditionals value + countPtr[0] = (mDNSu8)(numAdditionals >> 8); + countPtr[1] = (mDNSu8)(numAdditionals & 0xFF); +} mDNSexport mDNSBool DNSDigest_VerifyMessage(DNSMessage *msg, mDNSu8 *end, LargeCacheRecord * lcr, DomainAuthInfo *info, mDNSu16 * rcode, mDNSu16 * tcode) - { - mDNSu8 * ptr = (mDNSu8*) &lcr->r.resrec.rdata->u.data; - mDNSs32 now; - mDNSs32 then; - mDNSu8 thisDigest[MD5_LEN]; - mDNSu8 thatDigest[MD5_LEN]; - mDNSu32 macsize; - mDNSOpaque16 buf; - mDNSu8 utc48[6]; - mDNSs32 delta; - mDNSu16 fudge; - domainname * algo; - MD5_CTX c; - mDNSBool ok = mDNSfalse; +{ + mDNSu8 * ptr = (mDNSu8*) &lcr->r.resrec.rdata->u.data; + mDNSs32 now; + mDNSs32 then; + mDNSu8 thisDigest[MD5_LEN]; + mDNSu8 thatDigest[MD5_LEN]; + mDNSu32 macsize; + mDNSOpaque16 buf; + mDNSu8 utc48[6]; + mDNSs32 delta; + mDNSu16 fudge; + domainname * algo; + MD5_CTX c; + mDNSBool ok = mDNSfalse; + + // We only support HMAC-MD5 for now + + algo = (domainname*) ptr; + + if (!SameDomainName(algo, &HMAC_MD5_AlgName)) + { + LogMsg("ERROR: DNSDigest_VerifyMessage - TSIG algorithm not supported: %##s", algo->c); + *rcode = kDNSFlag1_RC_NotAuth; + *tcode = TSIG_ErrBadKey; + ok = mDNSfalse; + goto exit; + } + + ptr += DomainNameLength(algo); + + // Check the times - // We only support HMAC-MD5 for now + now = mDNSPlatformUTC(); + if (now == -1) + { + LogMsg("ERROR: DNSDigest_VerifyMessage - mDNSPlatformUTC returned bad time -1"); + *rcode = kDNSFlag1_RC_NotAuth; + *tcode = TSIG_ErrBadTime; + ok = mDNSfalse; + goto exit; + } - algo = (domainname*) ptr; + // Get the 48 bit time field, skipping over the first word - if (!SameDomainName(algo, &HMAC_MD5_AlgName)) - { - LogMsg("ERROR: DNSDigest_VerifyMessage - TSIG algorithm not supported: %##s", algo->c); - *rcode = kDNSFlag1_RC_NotAuth; - *tcode = TSIG_ErrBadKey; - ok = mDNSfalse; - goto exit; - } + utc48[0] = *ptr++; + utc48[1] = *ptr++; + utc48[2] = *ptr++; + utc48[3] = *ptr++; + utc48[4] = *ptr++; + utc48[5] = *ptr++; - ptr += DomainNameLength(algo); + then = (mDNSs32)NToH32(utc48 + sizeof(mDNSu16)); - // Check the times + fudge = NToH16(ptr); - now = mDNSPlatformUTC(); - if (now == -1) - { - LogMsg("ERROR: DNSDigest_VerifyMessage - mDNSPlatformUTC returned bad time -1"); - *rcode = kDNSFlag1_RC_NotAuth; - *tcode = TSIG_ErrBadTime; - ok = mDNSfalse; - goto exit; - } + ptr += sizeof(mDNSu16); - // Get the 48 bit time field, skipping over the first word + delta = (now > then) ? now - then : then - now; - utc48[0] = *ptr++; - utc48[1] = *ptr++; - utc48[2] = *ptr++; - utc48[3] = *ptr++; - utc48[4] = *ptr++; - utc48[5] = *ptr++; + if (delta > fudge) + { + LogMsg("ERROR: DNSDigest_VerifyMessage - time skew > %d", fudge); + *rcode = kDNSFlag1_RC_NotAuth; + *tcode = TSIG_ErrBadTime; + ok = mDNSfalse; + goto exit; + } - then = (mDNSs32)NToH32(utc48 + sizeof(mDNSu16)); + // MAC size - fudge = NToH16(ptr); + macsize = (mDNSu32) NToH16(ptr); - ptr += sizeof(mDNSu16); + ptr += sizeof(mDNSu16); - delta = (now > then) ? now - then : then - now; + // MAC - if (delta > fudge) - { - LogMsg("ERROR: DNSDigest_VerifyMessage - time skew > %d", fudge); - *rcode = kDNSFlag1_RC_NotAuth; - *tcode = TSIG_ErrBadTime; - ok = mDNSfalse; - goto exit; - } + mDNSPlatformMemCopy(thatDigest, ptr, MD5_LEN); - // MAC size + // Init MD5 context, digest inner key pad and message - macsize = (mDNSu32) NToH16(ptr); - - ptr += sizeof(mDNSu16); + MD5_Init(&c); + MD5_Update(&c, info->keydata_ipad, HMAC_LEN); + MD5_Update(&c, (mDNSu8*) msg, (unsigned long)(end - (mDNSu8*) msg)); - // MAC + // Key name - mDNSPlatformMemCopy(thatDigest, ptr, MD5_LEN); + MD5_Update(&c, lcr->r.resrec.name->c, DomainNameLength(lcr->r.resrec.name)); - // Init MD5 context, digest inner key pad and message + // Class name - MD5_Init(&c); - MD5_Update(&c, info->keydata_ipad, HMAC_LEN); - MD5_Update(&c, (mDNSu8*) msg, (unsigned long)(end - (mDNSu8*) msg)); - - // Key name + buf = mDNSOpaque16fromIntVal(lcr->r.resrec.rrclass); + MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); - MD5_Update(&c, lcr->r.resrec.name->c, DomainNameLength(lcr->r.resrec.name)); + // TTL - // Class name + MD5_Update(&c, (mDNSu8*) &lcr->r.resrec.rroriginalttl, sizeof(lcr->r.resrec.rroriginalttl)); - buf = mDNSOpaque16fromIntVal(lcr->r.resrec.rrclass); - MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); + // Algorithm - // TTL + MD5_Update(&c, algo->c, DomainNameLength(algo)); - MD5_Update(&c, (mDNSu8*) &lcr->r.resrec.rroriginalttl, sizeof(lcr->r.resrec.rroriginalttl)); - - // Algorithm - - MD5_Update(&c, algo->c, DomainNameLength(algo)); + // Time - // Time + MD5_Update(&c, utc48, 6); - MD5_Update(&c, utc48, 6); + // Fudge - // Fudge + buf = mDNSOpaque16fromIntVal(fudge); + MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); - buf = mDNSOpaque16fromIntVal(fudge); - MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); + // Digest error and other data len (both zero) - we'll add them to the rdata later - // Digest error and other data len (both zero) - we'll add them to the rdata later + buf.NotAnInteger = 0; + MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); // error + MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); // other data len - buf.NotAnInteger = 0; - MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); // error - MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); // other data len + // Finish the message & tsig var hash + + MD5_Final(thisDigest, &c); - // Finish the message & tsig var hash + // perform outer MD5 (outer key pad, inner digest) + MD5_Init(&c); + MD5_Update(&c, info->keydata_opad, HMAC_LEN); + MD5_Update(&c, thisDigest, MD5_LEN); MD5_Final(thisDigest, &c); - - // perform outer MD5 (outer key pad, inner digest) - - MD5_Init(&c); - MD5_Update(&c, info->keydata_opad, HMAC_LEN); - MD5_Update(&c, thisDigest, MD5_LEN); - MD5_Final(thisDigest, &c); - - if (!mDNSPlatformMemSame(thisDigest, thatDigest, MD5_LEN)) - { - LogMsg("ERROR: DNSDigest_VerifyMessage - bad signature"); - *rcode = kDNSFlag1_RC_NotAuth; - *tcode = TSIG_ErrBadSig; - ok = mDNSfalse; - goto exit; - } - - // set remaining rdata fields - ok = mDNStrue; + + if (!mDNSPlatformMemSame(thisDigest, thatDigest, MD5_LEN)) + { + LogMsg("ERROR: DNSDigest_VerifyMessage - bad signature"); + *rcode = kDNSFlag1_RC_NotAuth; + *tcode = TSIG_ErrBadSig; + ok = mDNSfalse; + goto exit; + } + + // set remaining rdata fields + ok = mDNStrue; exit: - return ok; - } + return ok; +} #ifdef __cplusplus diff --git a/mDNSCore/dnssec.c b/mDNSCore/dnssec.c new file mode 100644 index 0000000..5e582de --- /dev/null +++ b/mDNSCore/dnssec.c @@ -0,0 +1,3135 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "mDNSEmbeddedAPI.h" +#include "DNSCommon.h" +#include "dnssec.h" +#include "CryptoAlg.h" +#include "nsec.h" + +//#define DNSSEC_DEBUG + +#ifdef DNSSEC_DEBUG +#define debugdnssec LogMsg +#else +#define debugdnssec debug_noop +#endif +// +// Implementation Notes +// +// The entry point to DNSSEC Verification is VerifySignature. This function is called from the "core" when +// the answer delivered to the application needs DNSSEC validation. If a question needs DNSSEC +// validation, "ValidationRequired" would be set. As we need to issue more queries to validate the +// original question, we create another question as part of the verification process (question is part of +// DNSSECVerifier). This question sets "ValidatingResponse" to distinguish itself from the original +// question. Without this, it will be a duplicate and never sent out. The "core" almost treats both the +// types identically (like adding EDNS0 option with DO bit etc.) except for a few differences. When RRSIGs +// are added to the cache, "ValidatingResponse" question gets called back as long as the typeCovered matches +// the question's qtype. See the comment in DNSSECRecordAnswersQuestion for the details. The other big +// difference is that "ValidationRequired" question kicks off the verification process by calling into +// "VerifySignature" whereas ValidationResponse don't do that as it gets callback for its questions. +// +// VerifySignature does not retain the original question that started the verification process. It just +// remembers the name and the type. It takes a snapshot of the cache at that instance which will be +// verified using DNSSEC. If the cache changes subsequently e.g., network change etc., it will be detected +// when the validation is completed. If there is a change, it will be revalidated. +// +// The verification flow looks like this: +// +// VerifySignature -> StartDNSSECVerification - GetAllRRSetsForVerification -> FinishDNSSECVerification -> VerifySignature +// +// Verification is a recursive process. It stops when we find a trust anchor or if we have recursed too deep. +// +// If the original question resulted in NODATA/NXDOMAIN error, there should have been NSECs as part of the response. +// These nsecs are cached along with the negative cache record. These are validated using ValidateWithNSECS called +// from Verifysignature. +// +// The flow in this case looks like this: +// +// VerifySignature -> ValidateWithNSECS -> {NoDataProof, NameErrorProof} -> VerifyNSECS -> StartDNSSECVerification +// +// Once the DNSSEC verification is started, it is similar to the previous flow described above. When the verification +// is done, DNSSECPositiveValidationCB or DNSSECNegativeValidationCB will be called which will then deliver the +// validation results to the original question that started the validation. +// +// Forward declaration +mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord); +mDNSlocal mStatus TrustedKey(mDNS *const m, DNSSECVerifier *dv); +mDNSlocal mDNSBool TrustedKeyPresent(mDNS *const m, DNSSECVerifier *dv); +mDNSlocal mStatus ValidateDS(DNSSECVerifier *dv); +mDNSlocal void DNSSECNegativeValidationCB(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status); + +// Currently we use this to convert a RRVerifier to resource record so that we can +// use the standard DNS utility functions +LargeCacheRecord largerec; + +// Verification is a recursive process. We arbitrarily limit to 10 just to be cautious which should be +// removed in the future. +#define MAX_RECURSE_COUNT 10 + +// RFC 4034 Appendix B: Get the keyid of a DNS KEY. It is not transmitted +// explicitly on the wire. +// +// Note: This just helps narrow down the list of keys to look at. It is possible +// for two DNS keys to have the same ID i.e., key ID is not a unqiue tag +// +// 1st argument - the RDATA part of the DNSKEY RR +// 2nd argument - the RDLENGTH +// +mDNSlocal mDNSu32 keytag(mDNSu8 *key, mDNSu32 keysize) +{ + unsigned long ac; + unsigned int i; + + // DST_ALG_RSAMD5 will be rejected automatically as the keytag + // is calculated wrongly + + for (ac = 0, i = 0; i < keysize; ++i) + ac += (i & 1) ? key[i] : key[i] << 8; + ac += (ac >> 16) & 0xFFFF; + return ac & 0xFFFF; +} + +mDNSlocal int DNSMemCmp(mDNSu8 *const m1, mDNSu8 *const m2, int len) +{ + int res; + + res = mDNSPlatformMemCmp(m1, m2, len); + if (res != 0) + return (res < 0 ? -1 : 1); + return 0; +} + +mDNSlocal mStatus DNSNameToLowerCase(domainname *d, domainname *result) +{ + const mDNSu8 *a = d->c; + mDNSu8 *b = result->c; + const mDNSu8 *const max = d->c + MAX_DOMAIN_NAME; + int i, len; + + while (*a) + { + if (a + 1 + *a >= max) + { + LogMsg("DNSNameToLowerCase: ERROR!! Malformed Domain name"); + return mStatus_BadParamErr; + } + len = *a++; + *b++ = len; + for (i = 0; i < len; i++) + { + mDNSu8 ac = *a++; + if (mDNSIsUpperCase(ac)) ac += 'a' - 'A'; + *b++ = ac; + } + } + *b = 0; + + return mStatus_NoError; +} + +// Initialize the question enough so that it can be answered from the cache using SameNameRecordAnswersQuestion or +// ResourceRecordAnswersQuestion. +mDNSexport void InitializeQuestion(mDNS *const m, DNSQuestion *question, mDNSInterfaceID InterfaceID, const domainname *qname, + mDNSu16 qtype, mDNSQuestionCallback *callback, void *context) +{ + LogOperation("InitializeQuestion: Called for %##s (%s)", qname->c, DNSTypeName(qtype)); + + if (question->ThisQInterval != -1) mDNS_StopQuery(m, question); + + mDNS_SetupQuestion(question, InterfaceID, qname, qtype, callback, context); + question->qnamehash = DomainNameHashValue(qname); + question->ValidatingResponse = mDNStrue; + + // We need to set the DNS server appropriately to match the question against the cache record. + // Though not all callers of this function need it, we always do it to keep it simple. + SetValidDNSServers(m, question); + question->qDNSServer = GetServerForQuestion(m, question); + + // Make it look like unicast + question->TargetQID = onesID; + question->TimeoutQuestion = 1; + question->ReturnIntermed = 1; + // SetupQuestion sets LongLived if qtype == PTR + question->LongLived = 0; +} + +mDNSexport DNSSECVerifier *AllocateDNSSECVerifier(mDNS *const m, const domainname *name, mDNSu16 rrtype, mDNSInterfaceID InterfaceID, + DNSSECVerifierCallback dvcallback, mDNSQuestionCallback qcallback) +{ + DNSSECVerifier *dv; + + dv = (DNSSECVerifier *)mDNSPlatformMemAllocate(sizeof(DNSSECVerifier)); + if (!dv) { LogMsg("AllocateDNSSECVerifier: ERROR!! memory alloc failed"); return mDNSNULL; } + mDNSPlatformMemZero(dv, sizeof(*dv)); + + // Remember the question's name and type so that when we are done processing all + // the verifications, we can trace the original question back + AssignDomainName(&dv->origName, name); + dv->origType = rrtype; + dv->InterfaceID = InterfaceID; + dv->DVCallback = dvcallback; + dv->q.ThisQInterval = -1; + dv->ac = mDNSNULL; + dv->actail = &dv->ac; + // The verifier's question has to be initialized as some of the callers assume it + InitializeQuestion(m, &dv->q, InterfaceID, name, rrtype, qcallback, dv); + return dv; +} + +mDNSlocal void FreeDNSSECAuthChain(DNSSECVerifier *dv) +{ + RRVerifier *rrset; + RRVerifier *next; + AuthChain *ac, *acnext; + + LogDNSSEC("FreeDNSSECAuthChain: called"); + + ac = dv->ac; + + while (ac) + { + acnext = ac->next; + rrset = ac->rrset; + while (rrset) + { + next = rrset->next; + mDNSPlatformMemFree(rrset); + rrset = next; + } + ac->rrset = mDNSNULL; + + rrset = ac->rrsig; + while (rrset) + { + next = rrset->next; + mDNSPlatformMemFree(rrset); + rrset = next; + } + ac->rrsig = mDNSNULL; + + rrset = ac->key; + while (rrset) + { + next = rrset->next; + mDNSPlatformMemFree(rrset); + rrset = next; + } + ac->key = mDNSNULL; + + mDNSPlatformMemFree(ac); + ac = acnext; + } + dv->ac = mDNSNULL; +} + +mDNSlocal void FreeDNSSECVerifierRRSets(DNSSECVerifier *dv) +{ + RRVerifier *rrset; + RRVerifier *next; + + //debugdnssec("FreeDNSSECVerifierRRSets called %p", dv); + rrset = dv->rrset; + while (rrset) + { + next = rrset->next; + mDNSPlatformMemFree(rrset); + rrset = next; + } + dv->rrset = mDNSNULL; + + rrset = dv->rrsig; + while (rrset) + { + next = rrset->next; + mDNSPlatformMemFree(rrset); + rrset = next; + } + dv->rrsig = mDNSNULL; + + rrset = dv->key; + while (rrset) + { + next = rrset->next; + mDNSPlatformMemFree(rrset); + rrset = next; + } + dv->key = mDNSNULL; + + rrset = dv->rrsigKey; + while (rrset) + { + next = rrset->next; + mDNSPlatformMemFree(rrset); + rrset = next; + } + dv->rrsigKey = mDNSNULL; + + rrset = dv->ds; + while (rrset) + { + next = rrset->next; + mDNSPlatformMemFree(rrset); + rrset = next; + } + dv->ds = mDNSNULL; + if (dv->pendingNSEC) + { + mDNSPlatformMemFree(dv->pendingNSEC); + dv->pendingNSEC = mDNSNULL; + } +} + +mDNSexport void FreeDNSSECVerifier(mDNS *const m, DNSSECVerifier *dv) +{ + LogDNSSEC("FreeDNSSECVerifier called %p", dv); + if (dv->q.ThisQInterval != -1) mDNS_StopQuery(m, &dv->q); + FreeDNSSECVerifierRRSets(dv); + if (dv->ctx) AlgDestroy(dv->ctx); + if (dv->ac) FreeDNSSECAuthChain(dv); + if (dv->parent) + { + LogDNSSEC("FreeDNSSECVerifier freeing parent %p", dv->parent); + FreeDNSSECVerifier(m, dv->parent); + } + mDNSPlatformMemFree(dv); +} + +mDNSexport RRVerifier* AllocateRRVerifier(const ResourceRecord *const rr, mStatus *status) +{ + RRVerifier *r; + + r = mDNSPlatformMemAllocate(sizeof (RRVerifier) + rr->rdlength); + if (!r) + { + LogMsg("AllocateRRVerifier: memory failure"); + *status = mStatus_NoMemoryErr; + return mDNSNULL; + } + r->next = mDNSNULL; + r->rrtype = rr->rrtype; + r->rrclass = rr->rrclass; + r->rroriginalttl = rr->rroriginalttl; + r->rdlength = rr->rdlength; + r->namehash = rr->namehash; + r->rdatahash = rr->rdatahash; + AssignDomainName(&r->name, rr->name); + r->rdata = (mDNSu8*) ((mDNSu8 *)r + sizeof(RRVerifier)); + + // When we parsed the DNS response in GeLargeResourceRecord, for some records, we parse them into + // host order so that the rest of the code does not have to bother with converting from network order + // to host order. For signature verification, we need them back in network order. For DNSSEC records + // like DNSKEY and DS, we just copy over the data both in GetLargeResourceRecord and putRData. + + if (!putRData(mDNSNULL, r->rdata, r->rdata + rr->rdlength, rr)) + { + LogMsg("AllocateRRVerifier: putRData failed"); + *status = mStatus_BadParamErr; + return mDNSNULL; + } + *status = mStatus_NoError; + return r; +} + +mDNSexport mStatus AddRRSetToVerifier(DNSSECVerifier *dv, const ResourceRecord *const rr, RRVerifier *rv, RRVerifierSet set) +{ + RRVerifier *r; + RRVerifier **v; + mStatus status; + + if (!rv) + { + r = AllocateRRVerifier(rr, &status); + if (!r) return status; + } + else + r = rv; + + switch (set) + { + case RRVS_rr: + v = &dv->rrset; + break; + case RRVS_rrsig: + v = &dv->rrsig; + break; + case RRVS_key: + v = &dv->key; + break; + case RRVS_rrsig_key: + v = &dv->rrsigKey; + break; + case RRVS_ds: + v = &dv->ds; + break; + default: + LogMsg("AddRRSetToVerifier: ERROR!! default case %d", set); + return mStatus_BadParamErr; + } + while (*v) + v = &(*v)->next; + *v = r; + return mStatus_NoError; +} + +// Validate the RRSIG. "type" tells which RRSIG that we are supposed to validate. We fetch RRSIG for +// the rrset (type is RRVS_rrsig) and RRSIG for the key (type is RRVS_rrsig_key). +mDNSexport void ValidateRRSIG(DNSSECVerifier *dv, RRVerifierSet type, const ResourceRecord *const rr) +{ + RRVerifier *rv; + mDNSu32 currentTime; + rdataRRSig *rrsigRData = (rdataRRSig *)((mDNSu8 *)rr->rdata + sizeofRDataHeader); + + if (type == RRVS_rrsig) + { + rv = dv->rrset; + } + else if (type == RRVS_rrsig_key) + { + rv = dv->key; + } + else + { + LogMsg("ValidateRRSIG: ERROR!! type not valid %d", type); + return; + } + + // RFC 4035: + // For each authoritative RRset in a signed zone, there MUST be at least + // one RRSIG record that meets the following requirements: + // + // RRSet is defined by same name, class and type + // + // 1. The RRSIG RR and the RRset MUST have the same owner name and the same class. + if (!SameDomainName(&rv->name, rr->name) || (rr->rrclass != rv->rrclass)) + { + debugdnssec("ValidateRRSIG: name mismatch or class mismatch"); + return; + } + + // 2. The RRSIG RR's Type Covered field MUST equal the RRset's type. + if ((swap16(rrsigRData->typeCovered)) != rv->rrtype) + { + debugdnssec("ValidateRRSIG: typeCovered mismatch rrsig %d, rr type %d", swap16(rrsigRData->typeCovered), rv->rrtype); + return; + } + + // 3. The number of labels in the RRset owner name MUST be greater than or equal + // to the value in the RRSIG RR's Labels field. + if (rrsigRData->labels > CountLabels(&rv->name)) + { + debugdnssec("ValidateRRSIG: labels count problem rrsig %d, rr %d", rrsigRData->labels, CountLabels(&rv->name)); + return; + } + + // 4. The RRSIG RR's Signer's Name field MUST be the name of the zone that contains + // the RRset. For a stub resolver, this can't be done in a secure way. Hence we + // do it this way (discussed in dnsext mailing list) + switch (rv->rrtype) + { + case kDNSType_NS: + case kDNSType_SOA: + case kDNSType_DNSKEY: + //Signed by the owner + if (!SameDomainName(&rv->name, (domainname *)&rrsigRData->signerName)) + { + debugdnssec("ValidateRRSIG: Signer Name does not match the record name for %s", DNSTypeName(rv->rrtype)); + return; + } + break; + case kDNSType_DS: + // Should be signed by the parent + if (SameDomainName(&rv->name, (domainname *)&rrsigRData->signerName)) + { + debugdnssec("ValidateRRSIG: Signer Name matches the record name for %s", DNSTypeName(rv->rrtype)); + return; + } + // FALLTHROUGH + default: + { + int c1 = CountLabels(&rv->name); + int c2 = CountLabels((domainname *)&rrsigRData->signerName); + if (c1 < c2) + { + debugdnssec("ValidateRRSIG: Signer Name not a subdomain label count %d < %d ", c1, c2); + return; + } + domainname *d = (domainname *)SkipLeadingLabels(&rv->name, c1 - c2); + if (!SameDomainName(d, (domainname *)&rrsigRData->signerName)) + { + debugdnssec("ValidateRRSIG: Signer Name not a subdomain"); + return; + } + break; + } + } + + // 5. The validator's notion of the current time MUST be less than or equal to the + // time listed in the RRSIG RR's Expiration field. + // + // 6. The validator's notion of the current time MUST be greater than or equal to the + // time listed in the RRSIG RR's Inception field. + currentTime = mDNSPlatformUTC(); + + if (DNS_SERIAL_LT(swap32(rrsigRData->sigExpireTime), currentTime)) + { + LogDNSSEC("ValidateRRSIG: Expired: currentTime %d, ExpireTime %d", (int)currentTime, + swap32((int)rrsigRData->sigExpireTime)); + return; + } + if (DNS_SERIAL_LT(currentTime, swap32(rrsigRData->sigInceptTime))) + { + LogDNSSEC("ValidateRRSIG: Future: currentTime %d, InceptTime %d", (int)currentTime, + swap32((int)rrsigRData->sigInceptTime)); + return; + } + + if (AddRRSetToVerifier(dv, rr, mDNSNULL, type) != mStatus_NoError) + { + LogMsg("ValidateRRSIG: ERROR!! cannot allocate RRSet"); + return; + } +} + +mDNSlocal mStatus CheckRRSIGForRRSet(mDNS *const m, DNSSECVerifier *dv, CacheRecord **negcr) +{ + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *cr; + RRVerifier *rv; + + *negcr = mDNSNULL; + if (!dv->rrset) + { + LogMsg("CheckRRSIGForRRSet: ERROR!! rrset NULL for origName %##s (%s)", dv->origName.c, + DNSTypeName(dv->origType)); + return mStatus_BadParamErr; + } + + rv = dv->rrset; + slot = HashSlot(&rv->name); + cg = CacheGroupForName(m, slot, rv->namehash, &rv->name); + if (!cg) + { + debugdnssec("CheckRRSIGForRRSet: cg null"); + return mStatus_NoSuchRecord; + } + + for (cr=cg->members; cr; cr=cr->next) + { + debugdnssec("CheckRRSIGForRRSet: checking the validity of rrsig"); + if (cr->resrec.rrtype != kDNSType_RRSIG) continue; + if (cr->resrec.RecordType == kDNSRecordTypePacketNegative) + { + if (!(*negcr)) + { + LogDNSSEC("CheckRRSIGForRRSet: Negative cache record %s encountered for %##s (%s)", CRDisplayString(m, cr), + rv->name.c, rv->rrtype); + *negcr = cr; + } + else + { + LogMsg("CheckRRSIGForRRSet: ERROR!! Negative cache record %s already set for %##s (%s)", CRDisplayString(m, cr), + rv->name.c, rv->rrtype); + } + continue; + } + ValidateRRSIG(dv, RRVS_rrsig, &cr->resrec); + } + if (*negcr && dv->rrsig) + { + // Encountered both RRSIG and negative CR + LogMsg("CheckRRSIGForRRSet: ERROR!! Encountered negative cache record %s and RRSIG for %##s (%s)", + CRDisplayString(m, *negcr), rv->name.c, rv->rrtype); + return mStatus_BadParamErr; + } + if (dv->rrsig || *negcr) + return mStatus_NoError; + else + return mStatus_NoSuchRecord; +} + +mDNSlocal void CheckOneKeyForRRSIG(DNSSECVerifier *dv, const ResourceRecord *const rr) +{ + rdataRRSig *rrsig; + + if (!dv->rrsig) + { + LogMsg("CheckOneKeyForRRSIG: ERROR!! rrsig NULL"); + return; + } + rrsig = (rdataRRSig *)dv->rrsig->rdata; + if (!SameDomainName((domainname *)&rrsig->signerName, rr->name)) + { + debugdnssec("CheckOneKeyForRRSIG: name mismatch"); + return; + } + + // We store all the keys including the ZSK and KSK and use them appropriately + // later + if (AddRRSetToVerifier(dv, rr, mDNSNULL, RRVS_key) != mStatus_NoError) + { + LogMsg("CheckOneKeyForRRSIG: ERROR!! cannot allocate RRSet"); + return; + } +} + +mDNSlocal mStatus CheckKeyForRRSIG(mDNS *const m, DNSSECVerifier *dv, CacheRecord **negcr) +{ + mDNSu32 slot; + mDNSu32 namehash; + CacheGroup *cg; + CacheRecord *cr; + rdataRRSig *rrsig; + domainname *name; + + *negcr = mDNSNULL; + if (!dv->rrsig) + { + LogMsg("CheckKeyForRRSIG: ERROR!! rrsig NULL"); + return mStatus_BadParamErr; + } + + // Signer name should be the same on all rrsig ?? + rrsig = (rdataRRSig *)dv->rrsig->rdata; + name = (domainname *)&rrsig->signerName; + + slot = HashSlot(name); + namehash = DomainNameHashValue(name); + cg = CacheGroupForName(m, slot, namehash, name); + if (!cg) + { + debugdnssec("CheckKeyForRRSIG: cg null for %##s", name->c); + return mStatus_NoSuchRecord; + } + + for (cr=cg->members; cr; cr=cr->next) + { + if (cr->resrec.rrtype != kDNSType_DNSKEY) continue; + if (cr->resrec.RecordType == kDNSRecordTypePacketNegative) + { + if (!(*negcr)) + { + LogDNSSEC("CheckKeyForRRSIG: Negative cache record %s encountered for %##s (DNSKEY)", CRDisplayString(m, cr), + name->c); + *negcr = cr; + } + else + { + LogMsg("CheckKeyForRRSIG: ERROR!! Negative cache record %s already set for %##s (DNSKEY)", CRDisplayString(m, cr), + name->c); + } + continue; + } + debugdnssec("CheckKeyForRRSIG: checking the validity of key record"); + CheckOneKeyForRRSIG(dv, &cr->resrec); + } + if (*negcr && dv->key) + { + // Encountered both RRSIG and negative CR + LogMsg("CheckKeyForRRSIG: ERROR!! Encountered negative cache record %s and DNSKEY for %##s", + CRDisplayString(m, *negcr), name->c); + return mStatus_BadParamErr; + } + if (dv->key || *negcr) + return mStatus_NoError; + else + return mStatus_NoSuchRecord; +} + +mDNSlocal void CheckOneRRSIGForKey(DNSSECVerifier *dv, const ResourceRecord *const rr) +{ + rdataRRSig *rrsig; + if (!dv->rrsig) + { + LogMsg("CheckOneRRSIGForKey: ERROR!! rrsig NULL"); + return; + } + rrsig = (rdataRRSig *)dv->rrsig->rdata; + if (!SameDomainName((domainname *)&rrsig->signerName, rr->name)) + { + debugdnssec("CheckOneRRSIGForKey: name mismatch"); + return; + } + ValidateRRSIG(dv, RRVS_rrsig_key, rr); +} + +mDNSlocal mStatus CheckRRSIGForKey(mDNS *const m, DNSSECVerifier *dv, CacheRecord **negcr) +{ + mDNSu32 slot; + mDNSu32 namehash; + CacheGroup *cg; + CacheRecord *cr; + rdataRRSig *rrsig; + domainname *name; + + *negcr = mDNSNULL; + if (!dv->rrsig) + { + LogMsg("CheckRRSIGForKey: ERROR!! rrsig NULL"); + return mStatus_BadParamErr; + } + if (!dv->key) + { + LogMsg("CheckRRSIGForKey: ERROR!! key NULL"); + return mStatus_BadParamErr; + } + rrsig = (rdataRRSig *)dv->rrsig->rdata; + name = (domainname *)&rrsig->signerName; + + slot = HashSlot(name); + namehash = DomainNameHashValue(name); + cg = CacheGroupForName(m, slot, namehash, name); + if (!cg) + { + debugdnssec("CheckRRSIGForKey: cg null %##s", name->c); + return mStatus_NoSuchRecord; + } + for (cr=cg->members; cr; cr=cr->next) + { + if (cr->resrec.rrtype != kDNSType_RRSIG) continue; + if (cr->resrec.RecordType == kDNSRecordTypePacketNegative) + { + if (!(*negcr)) + { + LogDNSSEC("CheckRRSIGForKey: Negative cache record %s encountered for %##s (RRSIG)", CRDisplayString(m, cr), + name->c); + *negcr = cr; + } + else + { + LogMsg("CheckRRSIGForKey: ERROR!! Negative cache record %s already set for %##s (RRSIG)", CRDisplayString(m, cr), + name->c); + } + continue; + } + debugdnssec("CheckRRSIGForKey: checking the validity of rrsig"); + CheckOneRRSIGForKey(dv, &cr->resrec); + } + if (*negcr && dv->rrsigKey) + { + // Encountered both RRSIG and negative CR + LogMsg("CheckRRSIGForKey: ERROR!! Encountered negative cache record %s and DNSKEY for %##s", + CRDisplayString(m, *negcr), name->c); + return mStatus_BadParamErr; + } + if (dv->rrsigKey || *negcr) + return mStatus_NoError; + else + return mStatus_NoSuchRecord; +} + +mDNSlocal void CheckOneDSForKey(DNSSECVerifier *dv, const ResourceRecord *const rr) +{ + mDNSu16 tag; + rdataDS *DS; + RRVerifier *keyv; + rdataDNSKey *key; + rdataRRSig *rrsig; + + if (!dv->rrsig) + { + LogMsg("CheckOneDSForKey: ERROR!! rrsig NULL"); + return; + } + rrsig = (rdataRRSig *)dv->rrsig->rdata; + DS = (rdataDS *)((mDNSu8 *)rr->rdata + sizeofRDataHeader); + + if (!SameDomainName((domainname *)&rrsig->signerName, rr->name)) + { + debugdnssec("CheckOneDSForKey: name mismatch"); + return; + } + for (keyv = dv->key; keyv; keyv = keyv->next) + { + key = (rdataDNSKey *)keyv->rdata; + tag = (mDNSu16)keytag((mDNSu8 *)key, keyv->rdlength); + if (tag != swap16(DS->keyTag)) + { + debugdnssec("CheckOneDSForKey: keyTag mismatch keyTag %d, DStag %d", tag, swap16(DS->keyTag)); + continue; + } + if (key->alg != DS->alg) + { + debugdnssec("CheckOneDSForKey: alg mismatch key alg%d, DS alg %d", key->alg, swap16(DS->alg)); + continue; + } + if (AddRRSetToVerifier(dv, rr, mDNSNULL, RRVS_ds) != mStatus_NoError) + { + debugdnssec("CheckOneDSForKey: cannot allocate RRSet"); + } + } +} + +mDNSlocal mStatus CheckDSForKey(mDNS *const m, DNSSECVerifier *dv, CacheRecord **negcr) +{ + mDNSu32 slot; + mDNSu32 namehash; + CacheGroup *cg; + CacheRecord *cr; + rdataRRSig *rrsig; + domainname *name; + + *negcr = mDNSNULL; + if (!dv->rrsig) + { + LogMsg("CheckDSForKey: ERROR!! rrsig NULL"); + return mStatus_BadParamErr; + } + if (!dv->key) + { + LogMsg("CheckDSForKey: ERROR!! key NULL"); + return mStatus_BadParamErr; + } + rrsig = (rdataRRSig *)dv->rrsig->rdata; + name = (domainname *)&rrsig->signerName; + slot = HashSlot(name); + namehash = DomainNameHashValue(name); + cg = CacheGroupForName(m, slot, namehash, name); + if (!cg) + { + debugdnssec("CheckDSForKey: cg null for %s", name->c); + return mStatus_NoSuchRecord; + } + for (cr=cg->members; cr; cr=cr->next) + { + if (cr->resrec.rrtype != kDNSType_DS) continue; + if (cr->resrec.RecordType == kDNSRecordTypePacketNegative) + { + if (!(*negcr)) + { + LogDNSSEC("CheckDSForKey: Negative cache record %s encountered for %##s (DS)", CRDisplayString(m, cr), + name->c); + *negcr = cr; + } + else + { + LogMsg("CheckDSForKey: ERROR!! Negative cache record %s already set for %##s (DS)", CRDisplayString(m, cr), + name->c); + } + continue; + } + CheckOneDSForKey(dv, &cr->resrec); + } + if (*negcr && dv->ds) + { + // Encountered both RRSIG and negative CR + LogMsg("CheckDSForKey: ERROR!! Encountered negative cache record %s and DS for %##s", + CRDisplayString(m, *negcr), name->c); + return mStatus_BadParamErr; + } + if (dv->ds || *negcr) + return mStatus_NoError; + else + return mStatus_NoSuchRecord; + return (dv->ds ? mStatus_NoError : mStatus_NoSuchRecord); +} + +// It returns mDNStrue if we have all the rrsets for verification and mDNSfalse otherwise. +mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv) +{ + mStatus err; + CacheRecord *negcr; + rdataRRSig *rrsig; + + if (!dv->rrset) + { + LogMsg("GetAllRRSetsForVerification: ERROR!! rrset NULL"); + dv->DVCallback(m, dv, DNSSEC_Indeterminate); + return mDNSfalse; + } + + if (dv->next == RRVS_done) return mDNStrue; + + debugdnssec("GetAllRRSetsForVerification: next %d", dv->next); + switch (dv->next) + { + case RRVS_rrsig: + // If we can't find the RRSIG for the rrset, re-issue the query. + // + // NOTE: It is possible that the cache might answer partially e.g., RRSIGs match qtype but the + // whole set is not there. In that case the validation will fail. Ideally we should flush the + // cache and reissue the query (TBD). + err = CheckRRSIGForRRSet(m, dv, &negcr); + if (err != mStatus_NoSuchRecord && err != mStatus_NoError) + { + dv->DVCallback(m, dv, DNSSEC_Indeterminate); + return mDNSfalse; + } + // Need to initialize the question as if we end up in ValidateWithNSECS below, the nsec proofs + // looks in "dv->q" for the proof. Note that we have to use currQtype as the response could be + // a CNAME and dv->rrset->rrtype would be set to CNAME and not the original question type that + // resulted in CNAME. + InitializeQuestion(m, &dv->q, dv->InterfaceID, &dv->rrset->name, dv->currQtype, VerifySigCallback, dv); + // We may not have the NSECS if the previous query was a non-DNSSEC query + if (negcr && negcr->nsec) + { + dv->DVCallback = DNSSECNegativeValidationCB; + ValidateWithNSECS(m, dv, negcr); + return mDNSfalse; + } + + dv->next = RRVS_key; + if (!dv->rrsig) + { + // We already found the rrset to verify. Ideally we should just issue the query for the RRSIG. Unfortunately, + // that does not work well as the response may not contain the RRSIG whose typeCovered matches the + // rrset->rrtype (recursive server returns what is in its cache). Hence, we send the original query with the + // DO bit set again to get the RRSIG. Normally this would happen if there was question which did not require + // DNSSEC validation (ValidationRequied = 0) populated the cache and later when the ValidationRequired question + // comes along, we need to get the RRSIGs. If we started off with ValidationRequired question we would have + // already set the DO bit and not able to get RRSIGs e.g., bad CPE device, we would reissue the query here + // again once more. + // + // Also, if it is a wildcard expanded answer, we need to issue the query with the original type for it to + // elicit the right NSEC records. Just querying for RRSIG alone is not sufficient. + // + // Note: For this to work, the core needs to deliver RRSIGs when they are added to the cache even if the + // "qtype" is not RRSIG. + debugdnssec("GetAllRRSetsForVerification: Fetching RRSIGS for RRSET"); + mDNS_StartQuery(m, &dv->q); + return mDNSfalse; + } + // if we found the RRSIG, then fall through to find the DNSKEY + case RRVS_key: + err = CheckKeyForRRSIG(m, dv, &negcr); + if (err != mStatus_NoSuchRecord && err != mStatus_NoError) + { + dv->DVCallback(m, dv, DNSSEC_Indeterminate); + return mDNSfalse; + } + // Need to initialize the question as if we end up in ValidateWithNSECS below, the nsec proofs + // looks in "dv->q" for the proof. + rrsig = (rdataRRSig *)dv->rrsig->rdata; + InitializeQuestion(m, &dv->q, dv->InterfaceID, (domainname *)&rrsig->signerName, kDNSType_DNSKEY, VerifySigCallback, dv); + // We may not have the NSECS if the previous query was a non-DNSSEC query + if (negcr && negcr->nsec) + { + dv->DVCallback = DNSSECNegativeValidationCB; + ValidateWithNSECS(m, dv, negcr); + return mDNSfalse; + } + + dv->next = RRVS_rrsig_key; + if (!dv->key) + { + debugdnssec("GetAllRRSetsForVerification: Fetching DNSKEY for RRSET"); + mDNS_StartQuery(m, &dv->q); + return mDNSfalse; + } + // if we found the DNSKEY, then fall through to find the RRSIG for the DNSKEY + case RRVS_rrsig_key: + err = CheckRRSIGForKey(m, dv, &negcr); + // if we are falling through, then it is okay if we don't find the record + if (err != mStatus_NoSuchRecord && err != mStatus_NoError) + { + dv->DVCallback(m, dv, DNSSEC_Indeterminate); + return mDNSfalse; + } + // Need to initialize the question as if we end up in ValidateWithNSECS below, the nsec proofs + // looks in "dv->q" for the proof. + rrsig = (rdataRRSig *)dv->rrsig->rdata; + InitializeQuestion(m, &dv->q, dv->InterfaceID, (domainname *)&rrsig->signerName, kDNSType_DNSKEY, VerifySigCallback, dv); + // We may not have the NSECS if the previous query was a non-DNSSEC query + if (negcr && negcr->nsec) + { + dv->DVCallback = DNSSECNegativeValidationCB; + ValidateWithNSECS(m, dv, negcr); + return mDNSfalse; + } + dv->next = RRVS_ds; + debugdnssec("VerifySigCallback: RRVS_rrsig_key %p", dv->rrsigKey); + if (!dv->rrsigKey) + { + debugdnssec("GetAllRRSetsForVerification: Fetching RRSIGS for DNSKEY"); + mDNS_StartQuery(m, &dv->q); + return mDNSfalse; + } + // if we found RRSIG for the DNSKEY, then fall through to find the DS + case RRVS_ds: + { + domainname *qname; + rrsig = (rdataRRSig *)dv->rrsig->rdata; + qname = (domainname *)&rrsig->signerName; + + err = CheckDSForKey(m, dv, &negcr); + if (err != mStatus_NoSuchRecord && err != mStatus_NoError) + { + dv->DVCallback(m, dv, DNSSEC_Indeterminate); + return mDNSfalse; + } + // Need to initialize the question as if we end up in ValidateWithNSECS below, the nsec proofs + // looks in "dv->q" for the proof. + InitializeQuestion(m, &dv->q, dv->InterfaceID, qname, kDNSType_DS, VerifySigCallback, dv); + // We may not have the NSECS if the previous query was a non-DNSSEC query + if (negcr && negcr->nsec) + { + dv->DVCallback = DNSSECNegativeValidationCB; + ValidateWithNSECS(m, dv, negcr); + return mDNSfalse; + } + dv->next = RRVS_done; + // If we have a trust anchor, then don't bother looking up the DS record + if (!dv->ds && !TrustedKeyPresent(m, dv)) + { + // There is no DS for the root. Hence, if we don't have the trust + // anchor for root, just fail. + if (SameDomainName(qname, (const domainname *)"\000")) + { + LogDNSSEC("GetAllRRSetsForVerification: Reached root"); + dv->DVCallback(m, dv, DNSSEC_Bogus); + return mDNSfalse; + } + debugdnssec("GetAllRRSetsForVerification: Fetching DS"); + mDNS_StartQuery(m, &dv->q); + return mDNSfalse; + } + else + { + debugdnssec("GetAllRRSetsForVerification: Skipped fetching the DS"); + return mDNStrue; + } + } + default: + LogMsg("GetAllRRSetsForVerification: ERROR!! unknown next %d", dv->next); + dv->DVCallback(m, dv, DNSSEC_Bogus); + return mDNSfalse; + } +} + +#ifdef DNSSEC_DEBUG +mDNSlocal void PrintFixedSignInfo(rdataRRSig *rrsig, domainname *signerName, int sigNameLen, mDNSu8 *fixedPart, int fixedPartLen) +{ + int j; + char buf[RRSIG_FIXED_SIZE *3 + 1]; // 3 bytes count for %2x + 1 and the one byte for null at the end + char sig[sigNameLen * 3 + 1]; + char fp[fixedPartLen * 3 + 1]; + int length; + + length = 0; + for (j = 0; j < RRSIG_FIXED_SIZE; j++) + length += mDNS_snprintf(buf+length, sizeof(buf) - length - 1, "%2x ", ((mDNSu8 *)rrsig)[j]); + LogMsg("RRSIG(%d) %s", RRSIG_FIXED_SIZE, buf); + + + length = 0; + for (j = 0; j < sigNameLen; j++) + length += mDNS_snprintf(sig+length, sizeof(sig) - length - 1, "%2x ", signerName->c[j]); + LogMsg("SIGNAME(%d) %s", sigNameLen, sig); + + length = 0; + for (j = 0; j < fixedPartLen; j++) + length += mDNS_snprintf(fp+length, sizeof(fp) - length - 1, "%2x ", fixedPart[j]); + LogMsg("fixedPart(%d) %s", fixedPartLen, fp); +} + +mDNSlocal void PrintVarSignInfo(mDNSu16 rdlen, mDNSu8 *rdata) +{ + unsigned int j; + mDNSu8 *r; + unsigned int blen = swap16(rdlen); + char buf[blen * 3 + 1]; // 3 bytes count for %2x + 1 and the one byte for null at the end + int length; + + length = 0; + + r = (mDNSu8 *)&rdlen; + for (j = 0; j < sizeof(mDNSu16); j++) + length += mDNS_snprintf(buf+length, sizeof(buf) - length - 1, "%2x ", r[j]); + LogMsg("RDLENGTH(%d) %s", sizeof(mDNSu16), buf); + + length = 0; + for (j = 0; j < blen; j++) + length += mDNS_snprintf(buf+length, sizeof(buf) - length - 1, "%2x ", rdata[j]); + LogMsg("RDATA(%d) %s", blen, buf); +} +#else +mDNSlocal void PrintVarSignInfo(mDNSu16 rdlen, mDNSu8 *rdata) +{ + (void)rdlen; + (void)rdata; +} +mDNSlocal void PrintFixedSignInfo(rdataRRSig *rrsig, domainname *signerName, int sigNameLen, mDNSu8 *fixedPart, int fixedPartLen) +{ + (void)rrsig; + (void)signerName; + (void)sigNameLen; + (void)fixedPart; + (void)fixedPartLen; +} +#endif + +// Used for RDATA comparison +typedef struct +{ + mDNSu16 rdlength; + mDNSu16 rrtype; + mDNSu8 *rdata; +} rdataComp; + +mDNSlocal int rdata_compare(mDNSu8 *const rdata1, mDNSu8 *const rdata2, int rdlen1, int rdlen2) +{ + int len; + int ret; + + len = (rdlen1 < rdlen2) ? rdlen1 : rdlen2; + + ret = DNSMemCmp(rdata1, rdata2, len); + if (ret != 0) return ret; + + // RDATA is same at this stage. Consider them equal if they are of same length. Otherwise + // decide based on their lengths. + return ((rdlen1 == rdlen2) ? 0 : (rdlen1 < rdlen2) ? -1 : 1); +} + +mDNSlocal int name_compare(mDNSu8 *const rdata1, mDNSu8 *const rdata2, int rdlen1, int rdlen2) +{ + domainname *n1 = (domainname *)rdata1; + domainname *n2 = (domainname *)rdata2; + mDNSu8 *a = n1->c; + mDNSu8 *b = n2->c; + int count, c1, c2; + int i, j, len; + + c1 = CountLabels(n1); + c2 = CountLabels(n2); + + count = c1 < c2 ? c1 : c2; + + // We can't use SameDomainName as we need to know exactly which is greater/smaller + // for sorting purposes. Hence, we need to compare label by label + for (i = 0; i < count; i++) + { + // Are the lengths same ? + if (*a != *b) + { + debugdnssec("compare_name: returning c1 %d, c2 %d", *a, *b); + return ((*a < *b) ? -1 : 1); + } + len = *a; + rdlen1 -= (len + 1); + rdlen2 -= (len + 1); + if (rdlen1 < 0 || rdlen2 < 0) + { + LogMsg("name_compare: ERROR!! not enough data rdlen1 %d, rdlen2 %d", rdlen1, rdlen2); + return -1; + } + a++; b++; + for (j = 0; j < len; j++) + { + mDNSu8 ac = *a++; + mDNSu8 bc = *b++; + if (mDNSIsUpperCase(ac)) ac += 'a' - 'A'; + if (mDNSIsUpperCase(bc)) bc += 'a' - 'A'; + if (ac != bc) + { + debugdnssec("compare_name: returning ac %c, bc %c", ac, bc); + return ((ac < bc) ? -1 : 1); + } + } + } + + return 0; +} + +mDNSlocal int srv_compare(rdataComp *const r1, rdataComp *const r2) +{ + int res; + int length1, length2; + + length1 = r1->rdlength; + length2 = r2->rdlength; + // We should have at least priority, weight, port plus 1 byte + if (length1 < 7 || length2 < 7) + { + LogMsg("srv_compare: ERROR!! Length smaller than 7 bytes"); + return -1; + } + // Compare priority, weight and port + res = DNSMemCmp(r1->rdata, r2->rdata, 6); + if (res != 0) return res; + length1 -= 6; + length2 -= 6; + return (name_compare(r1->rdata + 6, r2->rdata + 6, length1, length2)); +} + +mDNSlocal int tsig_compare(rdataComp *const r1, rdataComp *const r2) +{ + int offset1, offset2; + int length1, length2; + int res, dlen; + + offset1 = offset2 = 0; + length1 = r1->rdlength; + length2 = r2->rdlength; + + // we should have at least one byte to start with + if (length1 < 1 || length2 < 1) + { + LogMsg("sig_compare: Length smaller than 18 bytes"); + return -1; + } + + res = name_compare(r1->rdata, r2->rdata, length1, length2); + if (res != 0) return res; + + dlen = DomainNameLength((domainname *)r1->rdata); + offset1 += dlen; + offset2 += dlen; + length1 -= dlen; + length2 -= dlen; + + if (length1 <= 1 || length2 <= 1) + { + LogMsg("tsig_compare: data too small to compare length1 %d, length2 %d", length1, length2); + return -1; + } + + return (rdata_compare(r1->rdata + offset1, r2->rdata + offset2, length1, length2)); +} + +// Compares types that conform to : +mDNSlocal int lenval_compare(mDNSu8 *d1, mDNSu8 *d2, int *len1, int *len2, int rem1, int rem2) +{ + int len; + int res; + + if (rem1 <= 1 || rem2 <= 1) + { + LogMsg("lenval_compare: data too small to compare length1 %d, length2 %d", rem1, rem2); + return -1; + } + *len1 = (int)d1[0]; + *len2 = (int)d2[0]; + len = (*len1 < *len2 ? *len1 : *len2); + res = DNSMemCmp(d1, d2, len + 1); + return res; +} + +// RFC 2915: Order (2) Preference(2) and variable length: Flags Service Regexp Replacement +mDNSlocal int naptr_compare(rdataComp *const r1, rdataComp *const r2) +{ + mDNSu8 *d1 = r1->rdata; + mDNSu8 *d2 = r2->rdata; + int len1, len2, res; + int length1, length2; + + length1 = r1->rdlength; + length2 = r2->rdlength; + + // Order, Preference plus at least 1 byte + if (length1 < 5 || length2 < 5) + { + LogMsg("naptr_compare: Length smaller than 18 bytes"); + return -1; + } + // Compare order and preference + res = DNSMemCmp(d1, d2, 4); + if (res != 0) return res; + + d1 += 4; + d2 += 4; + length1 -= 4; + length2 -= 4; + + // Compare Flags (including the length byte) + res = lenval_compare(d1, d2, &len1, &len2, length1, length2); + if (res != 0) return res; + d1 += (len1 + 1); + d2 += (len2 + 1); + length1 -= (len1 + 1); + length2 -= (len2 + 1); + + // Compare Service (including the length byte) + res = lenval_compare(d1, d2, &len1, &len2, length1, length2); + if (res != 0) return res; + d1 += (len1 + 1); + d2 += (len2 + 1); + length1 -= (len1 + 1); + length2 -= (len2 + 1); + + // Compare regexp (including the length byte) + res = lenval_compare(d1, d2, &len1, &len2, length1, length2); + if (res != 0) return res; + d1 += (len1 + 1); + d2 += (len2 + 1); + length1 -= (len1 + 1); + length2 -= (len2 + 1); + + // Compare Replacement + return name_compare(d1, d2, length1, length2); +} + +// RFC 1035: MINFO: Two domain names +// RFC 1183: RP: Two domain names +mDNSlocal int dom2_compare(mDNSu8 *d1, mDNSu8 *d2, int length1, int length2) +{ + int res, dlen; + + // We need at least one byte to start with + if (length1 < 1 || length2 < 1) + { + LogMsg("dom2_compare:1: data too small length1 %d, length2 %d", length1, length2); + return -1; + } + res = name_compare(d1, d2, length1, length2); + if (res != 0) return res; + dlen = DomainNameLength((domainname *)d1); + + length1 -= dlen; + length2 -= dlen; + // We need at least one byte to start with + if (length1 < 1 || length2 < 1) + { + LogMsg("dom2_compare:2: data too small length1 %d, length2 %d", length1, length2); + return -1; + } + + d1 += dlen; + d2 += dlen; + + return name_compare(d1, d2, length1, length2); +} + +// MX : preference (2 bytes), domainname +mDNSlocal int mx_compare(rdataComp *const r1, rdataComp *const r2) +{ + int res; + int length1, length2; + + length1 = r1->rdlength; + length2 = r2->rdlength; + + // We need at least two bytes + 1 extra byte for the domainname to start with + if (length1 < 3 || length2 < 3) + { + LogMsg("mx_compare: data too small length1 %d, length2 %d", length1, length2); + return -1; + } + + res = DNSMemCmp(r1->rdata, r2->rdata, 2); + if (res != 0) return res; + length1 -= 2; + length2 -= 2; + return name_compare(r1->rdata + 2, r2->rdata + 2, length1, length2); +} + +// RFC 2163 (PX) : preference (2 bytes), map822. mapx400 (domainnames) +mDNSlocal int px_compare(rdataComp *const r1, rdataComp *const r2) +{ + int res; + + // We need at least two bytes + 1 extra byte for the domainname to start with + if (r1->rdlength < 3 || r2->rdlength < 3) + { + LogMsg("px_compare: data too small length1 %d, length2 %d", r1->rdlength, r2->rdlength); + return -1; + } + + res = DNSMemCmp(r1->rdata, r2->rdata, 2); + if (res != 0) return res; + + return dom2_compare(r1->rdata + 2, r2->rdata + 2, r1->rdlength - 2, r2->rdlength - 2); +} + +mDNSlocal int soa_compare(rdataComp *r1, rdataComp *r2) +{ + int res, dlen; + int offset1, offset2; + int length1, length2; + + length1 = r1->rdlength; + length2 = r2->rdlength; + offset1 = offset2 = 0; + + // We need at least 20 bytes plus 1 byte for each domainname + if (length1 < 22 || length2 < 22) + { + LogMsg("soa_compare:1: data too small length1 %d, length2 %d", length1, length2); + return -1; + } + + // There are two domainnames followed by 20 bytes of serial, refresh, retry, expire and min + // Compare the names and then the rest of the bytes + + res = name_compare(r1->rdata, r2->rdata, length1, length2); + if (res != 0) return res; + + dlen = DomainNameLength((domainname *)r1->rdata); + + length1 -= dlen; + length2 -= dlen; + if (length1 < 1 || length2 < 1) + { + LogMsg("soa_compare:2: data too small length1 %d, length2 %d", length1, length2); + return -1; + } + offset1 += dlen; + offset2 += dlen; + + res = name_compare(r1->rdata + offset1, r2->rdata + offset2, length1, length2); + if (res != 0) return res; + + dlen = DomainNameLength((domainname *)r1->rdata); + length1 -= dlen; + length2 -= dlen; + if (length1 < 20 || length2 < 20) + { + LogMsg("soa_compare:3: data too small length1 %d, length2 %d", length1, length2); + return -1; + } + offset1 += dlen; + offset2 += dlen; + + return (rdata_compare(r1->rdata + offset1, r2->rdata + offset2, length1, length2)); +} + +// RFC 4034 Section 6.0 states that: +// +// A canonical RR form and ordering within an RRset are required in order to +// construct and verify RRSIG RRs. +// +// This function is called to order within an RRset. We can't just do a memcmp as +// as stated in 6.3. This function is responsible for the third bullet in 6.2, where +// the RDATA has to be converted to lower case if it has domain names. +mDNSlocal int RDATACompare(const void *rdata1, const void *rdata2) +{ + rdataComp *r1 = (rdataComp *)rdata1; + rdataComp *r2 = (rdataComp *)rdata2; + + if (r1->rrtype != r2->rrtype) + { + LogMsg("RDATACompare: ERROR!! comparing rdata of wrong types type1: %d, type2: %d", r1->rrtype, r2->rrtype); + return -1; + } + switch (r1->rrtype) + { + case kDNSType_A: // 1. Address Record + case kDNSType_NULL: // 10 NULL RR + case kDNSType_WKS: // 11 Well-known-service + case kDNSType_HINFO: // 13 Host information + case kDNSType_TXT: // 16 Arbitrary text string + case kDNSType_X25: // 19 X_25 calling address + case kDNSType_ISDN: // 20 ISDN calling address + case kDNSType_NSAP: // 22 NSAP address + case kDNSType_KEY: // 25 Security key + case kDNSType_GPOS: // 27 Geographical position (withdrawn) + case kDNSType_AAAA: // 28 IPv6 Address + case kDNSType_LOC: // 29 Location Information + case kDNSType_EID: // 31 Endpoint identifier + case kDNSType_NIMLOC: // 32 Nimrod Locator + case kDNSType_ATMA: // 34 ATM Address + case kDNSType_CERT: // 37 Certification record + case kDNSType_A6: // 38 IPv6 Address (deprecated) + case kDNSType_SINK: // 40 Kitchen sink (experimental) + case kDNSType_OPT: // 41 EDNS0 option (meta-RR) + case kDNSType_APL: // 42 Address Prefix List + case kDNSType_DS: // 43 Delegation Signer + case kDNSType_SSHFP: // 44 SSH Key Fingerprint + case kDNSType_IPSECKEY: // 45 IPSECKEY + case kDNSType_RRSIG: // 46 RRSIG + case kDNSType_NSEC: // 47 Denial of Existence + case kDNSType_DNSKEY: // 48 DNSKEY + case kDNSType_DHCID: // 49 DHCP Client Identifier + case kDNSType_NSEC3: // 50 Hashed Authenticated Denial of Existence + case kDNSType_NSEC3PARAM: // 51 Hashed Authenticated Denial of Existence + case kDNSType_HIP: // 55 Host Identity Protocol + case kDNSType_SPF: // 99 Sender Policy Framework for E-Mail + default: + return rdata_compare(r1->rdata, r2->rdata, r1->rdlength, r2->rdlength); + case kDNSType_NS: // 2 Name Server + case kDNSType_MD: // 3 Mail Destination + case kDNSType_MF: // 4 Mail Forwarder + case kDNSType_CNAME: // 5 Canonical Name + case kDNSType_MB: // 7 Mailbox + case kDNSType_MG: // 8 Mail Group + case kDNSType_MR: // 9 Mail Rename + case kDNSType_PTR: // 12 Domain name pointer + case kDNSType_NSAP_PTR: // 23 Reverse NSAP lookup (deprecated) + case kDNSType_DNAME: // 39 Non-terminal DNAME (for IPv6) + return name_compare(r1->rdata, r2->rdata, r1->rdlength, r2->rdlength); + case kDNSType_SRV: // 33 Service record + return srv_compare(r1, r2); + case kDNSType_SOA: // 6 Start of Authority + return soa_compare(r1, r2); + + case kDNSType_RP: // 17 Responsible person + case kDNSType_MINFO: // 14 Mailbox information + return dom2_compare(r1->rdata, r2->rdata, r1->rdlength, r2->rdlength); + case kDNSType_MX: // 15 Mail Exchanger + case kDNSType_AFSDB: // 18 AFS cell database + case kDNSType_RT: // 21 Router + case kDNSType_KX: // 36 Key Exchange + return mx_compare(r1, r2); + case kDNSType_PX: // 26 X.400 mail mapping + return px_compare(r1, r2); + case kDNSType_NAPTR: // 35 Naming Authority PoinTeR + return naptr_compare(r1, r2); + case kDNSType_TKEY: // 249 Transaction key + case kDNSType_TSIG: // 250 Transaction signature + // TSIG and TKEY have a domainname followed by data + return tsig_compare(r1, r2); + // TBD: We are comparing them as opaque types, perhaps not right + case kDNSType_SIG: // 24 Security signature + case kDNSType_NXT: // 30 Next domain (security) + LogMsg("RDATACompare: WARNING!! explicit support has not been added, using default"); + return rdata_compare(r1->rdata, r2->rdata, r1->rdlength, r2->rdlength); + } +} + + + +// RFC 4034 section 6.2 requirement for verifying signature. +// +// 3. if the type of the RR is NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR, +// HINFO, MINFO, MX, HINFO, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX, +// SRV, DNAME, A6, RRSIG, or NSEC, all uppercase US-ASCII letters in +// the DNS names contained within the RDATA are replaced by the +// corresponding lowercase US-ASCII letters; +// +// NSEC and HINFO is not needed as per dnssec-bis update. RRSIG is done elsewhere +// as part of signature verification +mDNSlocal void ConvertRDATAToCanonical(mDNSu16 rrtype, mDNSu16 rdlength, mDNSu8 *rdata) +{ + domainname name; + int len; + mDNSu8 *origRdata = rdata; + + // Ensure that we have at least one byte of data to examine and modify. + + if (!rdlength) { LogMsg("ConvertRDATAToCanonical: rdlength zero for rrtype %s", DNSTypeName(rrtype)); return; } + + switch (rrtype) + { + // Not adding suppot for A6 as it is deprecated + case kDNSType_A6: // 38 IPv6 Address (deprecated) + default: + debugdnssec("ConvertRDATAToCanonical: returning from default %s", DNSTypeName(rrtype)); + return; + case kDNSType_NS: // 2 Name Server + case kDNSType_MD: // 3 Mail Destination + case kDNSType_MF: // 4 Mail Forwarder + case kDNSType_CNAME: // 5 Canonical Name + case kDNSType_MB: // 7 Mailbox + case kDNSType_MG: // 8 Mail Group + case kDNSType_MR: // 9 Mail Rename + case kDNSType_PTR: // 12 Domain name pointer + case kDNSType_DNAME: // 39 Non-terminal DNAME (for IPv6) + case kDNSType_NXT: // 30 Next domain (security) + + // TSIG and TKEY are not mentioned in RFC 4034, but we just leave it here + case kDNSType_TSIG: // 250 Transaction signature + case kDNSType_TKEY: // 249 Transaction key + + if (DNSNameToLowerCase((domainname *)rdata, &name) != mStatus_NoError) + { + LogMsg("ConvertRDATAToCanonical: ERROR!! DNSNameToLowerCase failed"); + return; + } + AssignDomainName((domainname *)rdata, &name); + return; + case kDNSType_MX: // 15 Mail Exchanger + case kDNSType_AFSDB: // 18 AFS cell database + case kDNSType_RT: // 21 Router + case kDNSType_KX: // 36 Key Exchange + + // format: preference - 2 bytes, followed by name + // Ensure that we have at least 3 bytes (preference + 1 byte for the domain name) + if (rdlength <= 3) + { + LogMsg("ConvertRDATAToCanonical:MX: rdlength %d for rrtype %s too small", rdlength, DNSTypeName(rrtype)); + return; + } + if (DNSNameToLowerCase((domainname *)(rdata + 2), &name) != mStatus_NoError) + { + LogMsg("ConvertRDATAToCanonical: MX: ERROR!! DNSNameToLowerCase failed"); + return; + } + AssignDomainName((domainname *)(rdata + 2), &name); + return; + case kDNSType_SRV: // 33 Service record + // format : priority, weight and port - 6 bytes, followed by name + if (rdlength <= 7) + { + LogMsg("ConvertRDATAToCanonical:SRV: rdlength %d for rrtype %s too small", rdlength, DNSTypeName(rrtype)); + return; + } + if (DNSNameToLowerCase((domainname *)(rdata + 6), &name) != mStatus_NoError) + { + LogMsg("ConvertRDATAToCanonical: SRV: ERROR!! DNSNameToLowerCase failed"); + return; + } + AssignDomainName((domainname *)(rdata + 6), &name); + return; + case kDNSType_PX: // 26 X.400 mail mapping + if (rdlength <= 3) + { + LogMsg("ConvertRDATAToCanonical:PX: rdlength %d for rrtype %s too small", rdlength, DNSTypeName(rrtype)); + return; + } + // Preference followed by two domain names + rdata += 2; + /* FALLTHROUGH */ + case kDNSType_RP: // 17 Responsible person + case kDNSType_SOA: // 6 Start of Authority + case kDNSType_MINFO: // 14 Mailbox information + if (DNSNameToLowerCase((domainname *)rdata, &name) != mStatus_NoError) + { + LogMsg("ConvertRDATAToCanonical: SOA1: ERROR!! DNSNameToLowerCase failed"); + return; + } + + AssignDomainName((domainname *)rdata, &name); + len = DomainNameLength((domainname *)rdata); + if (rdlength <= len + 1) + { + LogMsg("ConvertRDATAToCanonical:RP: rdlength %d for rrtype %s too small", rdlength, DNSTypeName(rrtype)); + return; + } + rdata += len; + + if (DNSNameToLowerCase((domainname *)rdata, &name) != mStatus_NoError) + { + LogMsg("ConvertRDATAToCanonical: SOA2: ERROR!! DNSNameToLowerCase failed"); + return; + } + AssignDomainName((domainname *)rdata, &name); + return; + case kDNSType_NAPTR: // 35 Naming Authority Pointer + // order and preference + rdata += 4; + // Flags (including the length byte) + rdata += (((int) rdata[0]) + 1); + // Service (including the length byte) + rdata += (((int) rdata[0]) + 1); + // regexp (including the length byte) + rdata += (((int) rdata[0]) + 1); + + // Replacement field is a domainname. If we have at least one more byte, then we are okay. + if ((origRdata + rdlength) < rdata + 1) + { + LogMsg("ConvertRDATAToCanonical:NAPTR: origRdata %p, rdlength %d, rdata %p for rrtype %s too small", origRdata, rdlength, rdata, DNSTypeName(rrtype)); + return; + } + if (DNSNameToLowerCase((domainname *)rdata, &name) != mStatus_NoError) + { + LogMsg("ConvertRDATAToCanonical: NAPTR2: ERROR!! DNSNameToLowerCase failed"); + return; + } + AssignDomainName((domainname *)rdata, &name); + case kDNSType_SIG: // 24 Security signature + // format: <18 bytes> + if (rdlength <= 19) + { + LogMsg("ConvertRDATAToCanonical:SIG: rdlength %d for rrtype %s too small", rdlength, DNSTypeName(rrtype)); + return; + } + // Preference followed by two domain names + rdata += 18; + if (DNSNameToLowerCase((domainname *)rdata, &name) != mStatus_NoError) + { + LogMsg("ConvertRDATAToCanonical: SIG: ERROR!! DNSNameToLowerCase failed"); + return; + } + AssignDomainName((domainname *)rdata, &name); + return; + } +} + +mDNSlocal mDNSBool ValidateSignatureWithKey(DNSSECVerifier *dv, RRVerifier *rrset, RRVerifier *keyv, RRVerifier *sig) +{ + domainname name; + domainname signerName; + int labels; + mDNSu8 fixedPart[MAX_DOMAIN_NAME + 8]; // domainname + type + class + ttl + int fixedPartLen; + RRVerifier *tmp; + int nrrsets; + rdataComp *ptr, *start, *p; + rdataRRSig *rrsig; + rdataDNSKey *key; + int i; + int sigNameLen; + mDNSu16 temp; + mStatus algRet; + + + key = (rdataDNSKey *)keyv->rdata; + rrsig = (rdataRRSig *)sig->rdata; + + LogDNSSEC("ValidateSignatureWithKey: Validating signature with key with tag %d", (mDNSu16)keytag((mDNSu8 *)key, keyv->rdlength)); + + if (DNSNameToLowerCase((domainname *)&rrsig->signerName, &signerName) != mStatus_NoError) + { + LogMsg("ValidateSignatureWithKey: ERROR!! cannot convert signer name to lower case"); + return mDNSfalse; + } + + if (DNSNameToLowerCase((domainname *)&rrset->name, &name) != mStatus_NoError) + { + LogMsg("ValidateSignatureWithKey: ERROR!! cannot convert rrset name to lower case"); + return mDNSfalse; + } + + sigNameLen = DomainNameLength(&signerName); + labels = CountLabels(&name); + // RFC 4034: RRSIG validation + // + // signature = sign(RRSIG_RDATA | RR(1) | RR(2)... ) + // + // where RRSIG_RDATA excludes the signature and signer name in canonical form + + if (dv->ctx) AlgDestroy(dv->ctx); + dv->ctx = AlgCreate(CRYPTO_ALG, rrsig->alg); + if (!dv->ctx) + { + LogMsg("ValidateSignatureWithKey: ERROR!! No algorithm support for %d", rrsig->alg); + return mDNSfalse; + } + AlgAdd(dv->ctx, (mDNSu8 *)rrsig, RRSIG_FIXED_SIZE); + AlgAdd(dv->ctx, signerName.c, sigNameLen); + + if (labels - rrsig->labels > 0) + { + domainname *d; + LogDNSSEC("ValidateSignatureWithKey: ====splitting labels %d, rrsig->labels %d====", labels,rrsig->labels); + d = (domainname *)SkipLeadingLabels(&name, labels - rrsig->labels); + fixedPart[0] = 1; + fixedPart[1] = '*'; + AssignDomainName((domainname *)(fixedPart + 2), d); + fixedPartLen = DomainNameLength(d) + 2; + // See RFC 4034 section 3.1.3. If you are looking up *.example.com, + // the labels count in the RRSIG is 2, but this is not considered as + // a wildcard answer + if (name.c[0] != 1 || name.c[1] != '*') + { + LogDNSSEC("ValidateSignatureWithKey: Wildcard exapnded answer for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); + dv->flags |= WILDCARD_PROVES_ANSWER_EXPANDED; + dv->wildcardName = (domainname *)SkipLeadingLabels(&dv->origName, labels - rrsig->labels); + if (!dv->wildcardName) return mDNSfalse; + } + } + else + { + debugdnssec("ValidateSignatureWithKey: assigning domainname"); + AssignDomainName((domainname *)fixedPart, &name); + fixedPartLen = DomainNameLength(&name); + } + temp = swap16(rrset->rrtype); + mDNSPlatformMemCopy(fixedPart + fixedPartLen, (mDNSu8 *)&temp, sizeof(rrset->rrtype)); + fixedPartLen += sizeof(rrset->rrtype); + temp = swap16(rrset->rrclass); + mDNSPlatformMemCopy(fixedPart + fixedPartLen, (mDNSu8 *)&temp, sizeof(rrset->rrclass)); + fixedPartLen += sizeof(rrset->rrclass); + mDNSPlatformMemCopy(fixedPart + fixedPartLen, (mDNSu8 *)&rrsig->origTTL, sizeof(rrsig->origTTL)); + fixedPartLen += sizeof(rrsig->origTTL); + + + for (tmp = rrset, nrrsets = 0; tmp; tmp = tmp->next) + nrrsets++; + + tmp = rrset; + start = ptr = mDNSPlatformMemAllocate(nrrsets * sizeof (rdataComp)); + debugdnssec("ValidateSignatureWithKey: start %p, nrrsets %d", start, nrrsets); + if (ptr) + { + // Need to initialize for failure case below + mDNSPlatformMemZero(ptr, nrrsets * (sizeof (rdataComp))); + while (tmp) + { + ptr->rdlength = tmp->rdlength; + ptr->rrtype = tmp->rrtype; + if (ptr->rdlength) + { + ptr->rdata = mDNSPlatformMemAllocate(ptr->rdlength); + if (ptr->rdata) + mDNSPlatformMemCopy(ptr->rdata, tmp->rdata, tmp->rdlength); + else + { + for (i = 0; i < nrrsets; i++) + if (start[i].rdata) mDNSPlatformMemFree(start[i].rdata); + mDNSPlatformMemFree(start); + LogMsg("ValidateSignatureWithKey:1: ERROR!! RDATA memory alloation failure"); + return mDNSfalse; + } + } + ptr++; + tmp = tmp->next; + } + } + else + { + LogMsg("ValidateSignatureWithKey:2: ERROR!! RDATA memory alloation failure"); + return mDNSfalse; + } + + PrintFixedSignInfo(rrsig, &signerName, sigNameLen, fixedPart, fixedPartLen); + + mDNSPlatformQsort(start, nrrsets, sizeof(rdataComp), RDATACompare); + for (p = start, i = 0; i < nrrsets; p++, i++) + { + int rdlen; + + // The array is sorted and hence checking adjacent entries for duplicate is sufficient + if (i > 0) + { + rdataComp *q = p - 1; + if (!RDATACompare((void *)p, (void *)q)) continue; + } + + // Add the fixed part + AlgAdd(dv->ctx, fixedPart, fixedPartLen); + + // Add the rdlength + rdlen = swap16(p->rdlength); + AlgAdd(dv->ctx, (mDNSu8 *)&rdlen, sizeof(mDNSu16)); + + ConvertRDATAToCanonical(p->rrtype, p->rdlength, p->rdata); + + PrintVarSignInfo(rdlen, p->rdata); + AlgAdd(dv->ctx, p->rdata, p->rdlength); + } + // free the memory as we don't need it anymore + for (i = 0; i < nrrsets; i++) + if (start[i].rdata) mDNSPlatformMemFree(start[i].rdata); + mDNSPlatformMemFree(start); + + algRet = AlgVerify(dv->ctx, (mDNSu8 *)&key->data, keyv->rdlength - DNSKEY_FIXED_SIZE, (mDNSu8 *)(sig->rdata + sigNameLen + RRSIG_FIXED_SIZE), sig->rdlength - RRSIG_FIXED_SIZE - sigNameLen); + AlgDestroy(dv->ctx); + dv->ctx = mDNSNULL; + if (algRet != mStatus_NoError) + { + LogDNSSEC("ValidateSignatureWithKey: AlgVerify failed for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); + // Reset the state if we set any above. + if (dv->flags & WILDCARD_PROVES_ANSWER_EXPANDED) + { + dv->flags &= ~WILDCARD_PROVES_ANSWER_EXPANDED; + dv->wildcardName = mDNSNULL; + } + return mDNSfalse; + } + return mDNStrue; +} + +// Walk all the keys and for each key walk all the RRSIGS that signs the original rrset +mDNSlocal mStatus ValidateSignature(DNSSECVerifier *dv, RRVerifier **resultKey, RRVerifier **resultRRSIG) +{ + RRVerifier *rrset; + RRVerifier *keyv; + RRVerifier *rrsigv; + RRVerifier *sig; + rdataDNSKey *key; + rdataRRSig *rrsig; + mDNSu16 tag; + + rrset = dv->rrset; + sig = dv->rrsig; + + for (keyv = dv->key; keyv; keyv = keyv->next) + { + key = (rdataDNSKey *)keyv->rdata; + tag = (mDNSu16)keytag((mDNSu8 *)key, keyv->rdlength); + for (rrsigv = sig; rrsigv; rrsigv = rrsigv->next) + { + rrsig = (rdataRRSig *)rrsigv->rdata; + // 7. The RRSIG RR's Signer's Name, Algorithm, and Key Tag fields MUST match the owner + // name, algorithm, and key tag for some DNSKEY RR in the zone's apex DNSKEY RRset. + if (!SameDomainName((domainname *)&rrsig->signerName, &keyv->name)) + { + debugdnssec("ValidateSignature: name mismatch"); + continue; + } + if (key->alg != rrsig->alg) + { + debugdnssec("ValidateSignature: alg mismatch"); + continue; + } + if (tag != swap16(rrsig->keyTag)) + { + debugdnssec("ValidateSignature: keyTag mismatch rrsig tag %d(0x%x), keyTag %d(0x%x)", swap16(rrsig->keyTag), + swap16(rrsig->keyTag), tag, tag); + continue; + } + // 8. The matching DNSKEY RR MUST be present in the zone's apex DNSKEY RRset, and MUST + // have the Zone Flag bit (DNSKEY RDATA Flag bit 7) set. + if (!((swap16(key->flags)) & DNSKEY_ZONE_SIGN_KEY)) + { + debugdnssec("ValidateSignature: ZONE flag bit not set"); + continue; + } + debugdnssec("ValidateSignature:Found a key and RRSIG tag: %d", tag); + if (ValidateSignatureWithKey(dv, rrset, keyv, rrsigv)) + { + LogDNSSEC("ValidateSignature: Validated successfully with key tag %d", tag); + *resultKey = keyv; + *resultRRSIG = rrsigv; + return mStatus_NoError; + } + } + } + *resultKey = mDNSNULL; + *resultRRSIG = mDNSNULL; + return mStatus_NoSuchRecord; +} + +mDNSlocal mDNSBool ValidateSignatureWithKeyForAllRRSigs(DNSSECVerifier *dv, RRVerifier *rrset, RRVerifier *keyv, RRVerifier *sig) +{ + rdataRRSig *rrsig; + mDNSu16 tag; + + while (sig) + { + rrsig = (rdataRRSig *)sig->rdata; + tag = (mDNSu16)keytag(keyv->rdata, keyv->rdlength); + if (tag == swap16(rrsig->keyTag)) + { + if (ValidateSignatureWithKey(dv, rrset, keyv, sig)) + { + LogDNSSEC("ValidateSignatureWithKeyForAllRRSigs: Validated"); + return mDNStrue; + } + } + sig = sig->next; + } + return mDNSfalse; +} + +mDNSlocal mStatus ValidateDS(DNSSECVerifier *dv) +{ + mDNSu8 *digest; + int digestLen; + domainname name; + rdataRRSig *rrsig; + rdataDS *ds; + rdataDNSKey *key; + RRVerifier *keyv; + RRVerifier *dsv; + mStatus algRet; + + rrsig = (rdataRRSig *)dv->rrsig->rdata; + + // Walk all the DS Records to see if we have a matching DNS KEY record that verifies + // the hash. If we find one, verify that this key was used to sign the KEY rrsets in + // this zone. Loop till we find one. + for (dsv = dv->ds; dsv; dsv = dsv->next) + { + ds = (rdataDS *)dsv->rdata; + if ((ds->digestType != SHA1_DIGEST_TYPE) && (ds->digestType != SHA256_DIGEST_TYPE)) + { + LogDNSSEC("ValidateDS: Unsupported digest %d", ds->digestType); + return mStatus_BadParamErr; + } + else debugdnssec("ValidateDS: digest type %d", ds->digestType); + for (keyv = dv->key; keyv; keyv = keyv->next) + { + key = (rdataDNSKey *)keyv->rdata; + mDNSu16 tag = (mDNSu16)keytag((mDNSu8 *)key, keyv->rdlength); + if (tag != swap16(ds->keyTag)) + { + debugdnssec("ValidateDS:Not a valid keytag %d", tag); + continue; + } + + if (DNSNameToLowerCase((domainname *)&rrsig->signerName, &name) != mStatus_NoError) + { + LogMsg("ValidateDS: ERROR!! cannot convert to lower case"); + continue; + } + + if (dv->ctx) AlgDestroy(dv->ctx); + dv->ctx = AlgCreate(DIGEST_ALG, ds->digestType); + if (!dv->ctx) + { + LogMsg("ValidateDS: ERROR!! Cannot allocate context"); + continue; + } + digest = (mDNSu8 *)&ds->digest; + digestLen = dsv->rdlength - DS_FIXED_SIZE; + + AlgAdd(dv->ctx, name.c, DomainNameLength(&name)); + AlgAdd(dv->ctx, key, keyv->rdlength); + + algRet = AlgVerify(dv->ctx, mDNSNULL, 0, digest, digestLen); + AlgDestroy(dv->ctx); + dv->ctx = mDNSNULL; + if (algRet == mStatus_NoError) + { + LogDNSSEC("ValidateDS: DS Validated Successfully, need to verify the key %d", tag); + // We found the DNS KEY that is authenticated by the DS in our parent zone. Check to see if this key + // was used to sign the DNS KEY RRSET. If so, then the keys in our DNS KEY RRSET are valid + if (ValidateSignatureWithKeyForAllRRSigs(dv, dv->key, keyv, dv->rrsigKey)) + { + LogDNSSEC("ValidateDS: DS Validated Successfully %d", tag); + return mStatus_NoError; + } + } + } + } + return mStatus_NoSuchRecord; +} + +mDNSlocal mDNSBool UnlinkRRVerifier(DNSSECVerifier *dv, RRVerifier *elem, RRVerifierSet set) +{ + RRVerifier **v; + + switch (set) + { + case RRVS_rr: + v = &dv->rrset; + break; + case RRVS_rrsig: + v = &dv->rrsig; + break; + case RRVS_key: + v = &dv->key; + break; + case RRVS_rrsig_key: + v = &dv->rrsigKey; + break; + case RRVS_ds: + v = &dv->ds; + break; + default: + LogMsg("UnlinkRRVerifier: ERROR!! default case %d", set); + return mDNSfalse; + } + while (*v && *v != elem) + v = &(*v)->next; + if (!(*v)) + { + LogMsg("UnlinkRRVerifier: ERROR!! cannot find element in set %d", set); + return mDNSfalse; + } + *v = elem->next; // Cut this record from the list + elem->next = mDNSNULL; + return mDNStrue; +} + +// This can link a single AuthChain element or a list of AuthChain elements to +// DNSSECVerifier. The latter happens when we have multiple NSEC proofs and +// we gather up all the proofs in one place. +mDNSexport void AuthChainLink(DNSSECVerifier *dv, AuthChain *ae) +{ + AuthChain *head; + + LogDNSSEC("AuthChainLink: called"); + + head = ae; + // Get to the last element + while (ae->next) + ae = ae->next; + *(dv->actail) = head; // Append this record to tail of auth chain + dv->actail = &(ae->next); // Advance tail pointer +} + +mDNSlocal mDNSBool AuthChainAdd(DNSSECVerifier *dv, RRVerifier *resultKey, RRVerifier *resultRRSig) +{ + AuthChain *ae; + rdataDNSKey *key; + mDNSu16 tag; + + if (!dv->rrset || !resultKey || !resultRRSig) + { + LogMsg("AuthChainAdd: ERROR!! input argument NULL"); + return mDNSfalse; + } + + // Unlink resultKey and resultRRSig and store as part of AuthChain + if (!UnlinkRRVerifier(dv, resultKey, RRVS_key)) + { + LogMsg("AuthChainAdd: ERROR!! cannot unlink key"); + return mDNSfalse; + } + if (!UnlinkRRVerifier(dv, resultRRSig, RRVS_rrsig)) + { + LogMsg("AuthChainAdd: ERROR!! cannot unlink rrsig"); + return mDNSfalse; + } + + ae = mDNSPlatformMemAllocate(sizeof(AuthChain)); + if (!ae) + { + LogMsg("AuthChainAdd: AuthChain alloc failure"); + return mDNSfalse; + } + + ae->next = mDNSNULL; + ae->rrset = dv->rrset; + dv->rrset = mDNSNULL; + + ae->rrsig = resultRRSig; + ae->key = resultKey; + + key = (rdataDNSKey *)resultKey->rdata; + tag = (mDNSu16)keytag((mDNSu8 *)key, resultKey->rdlength); + LogDNSSEC("AuthChainAdd: inserting AuthChain element with rrset %##s (%s), DNSKEY tag %d", ae->rrset->name.c, DNSTypeName(ae->rrset->rrtype), tag); + + AuthChainLink(dv, ae); + return mDNStrue; +} + +// RFC 4035: Section 5.3.3 +// +// If the resolver accepts the RRset as authentic, the validator MUST set the TTL of +// the RRSIG RR and each RR in the authenticated RRset to a value no greater than the +// minimum of: +// +// o the RRset's TTL as received in the response; +// +// o the RRSIG RR's TTL as received in the response; +// +// o the value in the RRSIG RR's Original TTL field; and +// +// o the difference of the RRSIG RR's Signature Expiration time and the +// current time. +mDNSlocal void SetTTLRRSet(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status) +{ + DNSQuestion question; + CacheRecord *rr; + RRVerifier *rv; + rdataRRSig *rrsig; + mDNSu32 slot; + CacheGroup *cg; + int sigNameLen, len; + mDNSu8 *ptr; + mDNSu32 rrTTL, rrsigTTL, rrsigOrigTTL, rrsigTimeTTL; + domainname *qname; + mDNSu16 qtype; + CacheRecord *rrsigRR; + + debugdnssec("SetTTLRRSet called"); + + // TBD: Just handle secure for now + if (status != DNSSEC_Secure) return; + + // check to make sure we built a AuthChain as part of verification + if (!dv->ac || !dv->ac->rrset || !dv->ac->rrsig || !dv->ac->key) + { + LogMsg("SetTTLRRSet: ERROR!! NULL element in chain"); + FreeDNSSECVerifier(m, dv); + return; + } + + mDNSPlatformMemZero(&question, sizeof(DNSQuestion)); + rrTTL = rrsigTTL = rrsigOrigTTL = rrsigTimeTTL = 0; + + // 1. Locate the rrset name and get its TTL (take the first one as a representative + // of the rrset). + qname = &dv->origName; + qtype = dv->origType; + + question.ThisQInterval = -1; + InitializeQuestion(m, &question, dv->InterfaceID, qname, qtype, mDNSNULL, mDNSNULL); + slot = HashSlot(&question.qname); + cg = CacheGroupForName(m, slot, question.qnamehash, &question.qname); + + if (!cg) { LogMsg("SetTTLRRSet cg NULL"); return; } + for (rr = cg->members; rr; rr = rr->next) + if (SameNameRecordAnswersQuestion(&rr->resrec, &question)) + { + rrTTL = rr->resrec.rroriginalttl; + break; + } + + // Should we check to see if it matches the record in dv->ac->rrset ? + if (!rr) + { + LogMsg("SetTTLRRSet: ERROR!! cannot locate main rrset for %##s (%s)", qname->c, DNSTypeName(qtype)); + return; + } + + + // 2. Get the RRSIG ttl. For NSEC records we need to get the NSEC record's TTL as + // the negative cache record that we created may not be right. + + rv = dv->ac->rrsig; + rrsig = (rdataRRSig *)rv->rdata; + sigNameLen = DomainNameLength((domainname *)&rrsig->signerName); + // pointer to signature and the length + ptr = (mDNSu8 *)(rv->rdata + sigNameLen + RRSIG_FIXED_SIZE); + len = rv->rdlength - RRSIG_FIXED_SIZE - sigNameLen; + + rrsigRR = mDNSNULL; + if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) + { + CacheRecord *ncr; + rrTTL = 0; + for (ncr = rr->nsec; ncr; ncr = ncr->next) + { + if (ncr->resrec.rrtype == kDNSType_NSEC) + { + rrTTL = ncr->resrec.rroriginalttl; + debugdnssec("SetTTLRRSet: NSEC TTL %u", rrTTL); + } + // Note: we can't use dv->origName here as the NSEC record's RRSIG may not match + // the original name + if (ncr->resrec.rrtype == kDNSType_RRSIG && SameDomainName(ncr->resrec.name, &rv->name)) + { + RDataBody2 *rdb = (RDataBody2 *)ncr->resrec.rdata->u.data; + rdataRRSig *sig = (rdataRRSig *)rdb->data; + if (rv->rdlength != ncr->resrec.rdlength) + { + debugdnssec("SetTTLRRSet length mismatch"); + continue; + } + if (mDNSPlatformMemSame(sig, rrsig, rv->rdlength)) + { + rrsigTTL = ncr->resrec.rroriginalttl; + rrsigOrigTTL = swap32(rrsig->origTTL); + rrsigTimeTTL = swap32(rrsig->sigExpireTime) - swap32(rrsig->sigInceptTime); + } + } + if (rrTTL && rrsigTTL) break; + } + } + else + { + // Look for the matching RRSIG so that we can get its TTL + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + if (rr->resrec.rrtype == kDNSType_RRSIG && SameDomainName(rr->resrec.name, &rv->name)) + { + RDataBody2 *rdb = (RDataBody2 *)rr->resrec.rdata->u.data; + rdataRRSig *sig = (rdataRRSig *)rdb->data; + if (rv->rdlength != rr->resrec.rdlength) + { + debugdnssec("SetTTLRRSet length mismatch"); + continue; + } + if (mDNSPlatformMemSame(sig, rrsig, rv->rdlength)) + { + rrsigTTL = rr->resrec.rroriginalttl; + rrsigOrigTTL = swap32(rrsig->origTTL); + rrsigTimeTTL = swap32(rrsig->sigExpireTime) - swap32(rrsig->sigInceptTime); + rrsigRR = rr; + break; + } + } + } + + if (!rrTTL || !rrsigTTL || !rrsigOrigTTL || !rrsigTimeTTL) + { + LogMsg("SetTTLRRSet: ERROR!! Bad TTL rrtl %u, rrsigTTL %u, rrsigOrigTTL %u, rrsigTimeTTL %u for %##s (%s)", + rrTTL, rrsigTTL, rrsigOrigTTL, rrsigTimeTTL, qname->c, DNSTypeName(qtype)); + return; + } + else + { + LogDNSSEC("SetTTLRRSet: TTL rrtl %u, rrsigTTL %u, rrsigOrigTTL %u, rrsigTimeTTL %u for %##s (%s)", + rrTTL, rrsigTTL, rrsigOrigTTL, rrsigTimeTTL, qname->c, DNSTypeName(qtype)); + } + + if (rrsigTTL < rrTTL) + rrTTL = rrsigTTL; + if (rrsigOrigTTL < rrTTL) + rrTTL = rrsigOrigTTL; + if (rrsigTimeTTL < rrTTL) + rrTTL = rrsigTimeTTL; + + // Set the rrsig's TTL. For NSEC records, rrsigRR is NULL which means it expires when + // the negative cache record expires. + if (rrsigRR) + rrsigRR->resrec.rroriginalttl = rrTTL; + + // Find the RRset and set its TTL + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + { + if (SameNameRecordAnswersQuestion(&rr->resrec, &question)) + { + LogDNSSEC("SetTTLRRSet: Setting the TTL %d for %s, question %##s (%s)", rrTTL, CRDisplayString(m, rr), + question.qname.c, DNSTypeName(rr->resrec.rrtype)); + rr->resrec.rroriginalttl = rrTTL; + SetNextCacheCheckTimeForRecord(m, rr); + } + } +} + +mDNSlocal void FinishDNSSECVerification(mDNS *const m, DNSSECVerifier *dv) +{ + RRVerifier *resultKey; + RRVerifier *resultRRSig; + + LogDNSSEC("FinishDNSSECVerification: all rdata sets available for sig verification for %##s (%s)", + dv->origName.c, DNSTypeName(dv->origType)); + + mDNS_StopQuery(m, &dv->q); + if (ValidateSignature(dv, &resultKey, &resultRRSig) == mStatus_NoError) + { + rdataDNSKey *key; + mDNSu16 tag; + key = (rdataDNSKey *)resultKey->rdata; + tag = (mDNSu16)keytag((mDNSu8 *)key, resultKey->rdlength); + + LogDNSSEC("FinishDNSSECVerification: RRSIG validated by DNSKEY tag %d, %##s (%s)", tag, dv->rrset->name.c, + DNSTypeName(dv->rrset->rrtype)); + + if (TrustedKey(m, dv) == mStatus_NoError) + { + // Need to call this after we called TrustedKey, as AuthChainAdd + // unlinks the resultKey and resultRRSig + if (!AuthChainAdd(dv, resultKey, resultRRSig)) + { + dv->DVCallback(m, dv, DNSSEC_Indeterminate); + return; + } + // The callback will be called when NSEC verification is done. + if ((dv->flags & WILDCARD_PROVES_ANSWER_EXPANDED)) + { + WildcardAnswerProof(m, dv); + return; + } + else + { + dv->DVCallback(m, dv, DNSSEC_Secure); + return; + } + } + if (!ValidateDS(dv)) + { + // Need to call this after we called ValidateDS, as AuthChainAdd + // unlinks the resultKey and resultRRSig + if (!AuthChainAdd(dv, resultKey, resultRRSig)) + { + dv->DVCallback(m, dv, DNSSEC_Indeterminate); + return; + } + FreeDNSSECVerifierRRSets(dv); + dv->recursed++; + if (dv->recursed < MAX_RECURSE_COUNT) + { + LogDNSSEC("FinishDNSSECVerification: Recursion level %d for %##s (%s)", dv->recursed, dv->origName.c, + DNSTypeName(dv->origType)); + VerifySignature(m, dv, &dv->q); + return; + } + } + else + { + LogDNSSEC("FinishDNSSECVerification: ValidateDS failed %##s (%s)", dv->rrset->name.c, DNSTypeName(dv->rrset->rrtype)); + dv->DVCallback(m, dv, DNSSEC_Insecure); + return; + } + } + else + { + LogDNSSEC("FinishDNSSECVerification: Could not validate the rrset %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); + dv->DVCallback(m, dv, DNSSEC_Insecure); + return; + } +} + +mDNSexport void StartDNSSECVerification(mDNS *const m, DNSSECVerifier *dv) +{ + mDNSBool done; + + done = GetAllRRSetsForVerification(m, dv); + if (done) + { + if (dv->next != RRVS_done) + LogMsg("StartDNSSECVerification: ERROR!! dv->next is not done"); + else + LogDNSSEC("StartDNSSECVerification: all rdata sets available for sig verification"); + FinishDNSSECVerification(m, dv); + return; + } + else debugdnssec("StartDNSSECVerification: all rdata sets not available for sig verification next %d", dv->next); +} + +mDNSlocal char *DNSSECStatusName(DNSSECStatus status) +{ + switch (status) + { + case DNSSEC_Secure: return "Secure"; + case DNSSEC_Insecure: return "Insecure"; + case DNSSEC_Indeterminate: return "Indeterminate"; + case DNSSEC_Bogus: return "Bogus"; + default: return "Invalid"; + } +} + +// We could not use GenerateNegativeResponse as it assumes m->CurrentQuestion to be set. Even if +// we change that, we needs to fix its callers and so on. It is much simpler to call the callback. +mDNSlocal void DeliverDNSSECStatus(mDNS *const m, ResourceRecord *answer, DNSSECStatus status) +{ + + // Can't use m->CurrentQuestion as it may already be in use + if (m->ValidationQuestion) + LogMsg("DeliverDNSSECStatus: ERROR!! m->ValidationQuestion already set: %##s (%s)", + m->ValidationQuestion->qname.c, DNSTypeName(m->ValidationQuestion->qtype)); + + m->ValidationQuestion = m->Questions; + while (m->ValidationQuestion && m->ValidationQuestion != m->NewQuestions) + { + DNSQuestion *q = m->ValidationQuestion; + + if (q->ValidatingResponse || !q->ValidationRequired || + (q->ValidationState != DNSSECValInProgress) || !ResourceRecordAnswersQuestion(answer, q)) + { + m->ValidationQuestion = q->next; + continue; + } + + q->ValidationState = DNSSECValDone; + q->ValidationStatus = status; + + MakeNegativeCacheRecord(m, &largerec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any, mDNSNULL); + if (q->qtype == answer->rrtype || status != DNSSEC_Secure) + { + LogDNSSEC("DeliverDNSSECStatus: Generating dnssec status %s for %##s (%s)", DNSSECStatusName(status), + q->qname.c, DNSTypeName(q->qtype)); + if (q->QuestionCallback) q->QuestionCallback(m, q, &largerec.r.resrec, QC_dnssec); + } + else + { + LogDNSSEC("DeliverDNSSECStatus: Following CNAME dnssec status %s for %##s (%s)", DNSSECStatusName(status), + q->qname.c, DNSTypeName(q->qtype)); + mDNS_Lock(m); + AnswerQuestionByFollowingCNAME(m, q, answer); + mDNS_Unlock(m); + } + + if (m->ValidationQuestion == q) // If m->ValidationQuestion was not auto-advanced, do it ourselves now + m->ValidationQuestion = q->next; + } + m->ValidationQuestion = mDNSNULL; +} + +mDNSlocal void DNSSECPositiveValidationCB(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status) +{ + RRVerifier *rrset; + RRVerifier *rv; + CacheGroup *cg; + CacheRecord *cr; + mDNSu32 slot, namehash; + mDNSu16 rrtype, rrclass; + CacheRecord *const lrr = &largerec.r; + ResourceRecord *answer = mDNSNULL; + + LogDNSSEC("DNSSECPositiveValidationCB: called status %s", DNSSECStatusName(status)); + + // + // 1. Check to see if the rrset that was validated is the same as in cache. If they are not same, + // this validation result is not valid. When the rrset changed while the validation was in + // progress, the act of delivering the changed rrset again should have kicked off another + // verification. + // + // 2. Walk the question list to find the matching question. The original question that started + // the DNSSEC verification may or may not be there. As long as there is a matching question + // and waiting for the response, deliver the response. + // + // 3. If we are answering with CNAME, it is time to follow the CNAME if the response is secure + + slot = HashSlot(&dv->origName); + namehash = DomainNameHashValue(&dv->origName); + + cg = CacheGroupForName(m, (const mDNSu32)slot, namehash, &dv->origName); + if (!cg) + { + LogDNSSEC("DNSSECPositiveValidationCB: cg NULL for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); + goto done; + } + if (!dv->ac) + { + // If we don't have the AuthChain, it means we could not validate the rrset. Locate the + // original question based on dv->origName, dv->origType. + InitializeQuestion(m, &dv->q, dv->InterfaceID, &dv->origName, dv->origType, mDNSNULL, mDNSNULL); + // Need to be reset ValidatingResponse as we are looking for the cache record that would answer + // the original question + dv->q.ValidatingResponse = mDNSfalse; + for (cr = cg->members; cr; cr = cr->next) + { + if (SameNameRecordAnswersQuestion(&cr->resrec, &dv->q)) + { + answer = &cr->resrec; + break; + } + } + } + else + { + if (!dv->ac->rrset) + { + LogMsg("DNSSECPositiveValidationCB: ERROR!! Validated RRSET NULL"); + goto done; + } + + rrset = dv->ac->rrset; + rrtype = rrset->rrtype; + rrclass = rrset->rrclass; + + lrr->resrec.name = &largerec.namestorage; + + for (rv = dv->ac->rrset; rv; rv = rv->next) + rv->found = 0; + + // Check to see if we can find all the elements in the rrset + for (cr = cg ? cg->members : mDNSNULL; cr; cr = cr->next) + { + if (cr->resrec.rrtype == rrtype && cr->resrec.rrclass == rrclass) + { + for (rv = dv->ac->rrset; rv; rv = rv->next) + { + if (rv->rdlength == cr->resrec.rdlength && rv->rdatahash == cr->resrec.rdatahash) + { + lrr->resrec.namehash = rv->namehash; + lrr->resrec.rrtype = rv->rrtype; + lrr->resrec.rrclass = rv->rrclass; + lrr->resrec.rdata = (RData*)&lrr->smallrdatastorage; + lrr->resrec.rdata->MaxRDLength = MaximumRDSize; + + // Convert the "rdata" to a suitable form before we can call SameRDataBody which expects + // some of the resource records in host order and also domainnames fully expanded. We + // converted the resource records into network order for verification purpose and hence + // need to convert them back again before comparing them. + if (!SetRData(mDNSNULL, rv->rdata, rv->rdata + rv->rdlength, &largerec, rv->rdlength)) + { + LogMsg("DNSSECPositiveValidationCB: SetRData failed for %##s (%s)", rv->name.c, DNSTypeName(rv->rrtype)); + } + else if (SameRDataBody(&cr->resrec, &lrr->resrec.rdata->u, SameDomainName)) + { + answer = &cr->resrec; + rv->found = 1; + break; + } + } + } + if (!rv) + { + // The validated rrset does not have the element in the cache, re-validate + LogDNSSEC("DNSSECPositiveValidationCB: CacheRecord %s, not found in the validated set", CRDisplayString(m, cr)); + goto done; + } + } + } + // Check to see if we have elements that were not in the cache + for (rv = dv->ac->rrset; rv; rv = rv->next) + { + if (!rv->found) + { + // We had more elements in the validated set, re-validate + LogDNSSEC("DNSSECPositiveValidationCB: Record %##s (%s) not found in the cache", rv->name.c, DNSTypeName(rv->rrtype)); + goto done; + } + } + } + + // It is not an error for things to disappear underneath + if (!answer) + { + LogDNSSEC("DNSSECPositiveValidationCB: answer NULL"); + goto done; + } + + DeliverDNSSECStatus(m, answer, status); + SetTTLRRSet(m, dv, status); + +done: + FreeDNSSECVerifier(m, dv); +} + +mDNSlocal void DNSSECNegativeValidationCB(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status) +{ + RRVerifier *rv; + CacheGroup *cg; + CacheRecord *cr; + mDNSu32 slot, namehash; + mDNSu16 rrtype, rrclass; + ResourceRecord *answer = mDNSNULL; + AuthChain *ac; + + LogDNSSEC("DNSSECNegativeValidationCB: called %s", DNSSECStatusName(status)); + + // 1. Locate the negative cache record and check the cached NSEC records to see if it matches the + // NSECs that were valiated. If the cached NSECS changed while the validation was in progress, + // we ignore the validation results. + // + // 2. Walk the question list to find the matching question. The original question that started + // the DNSSEC verification may or may not be there. As long as there is a matching question + // and waiting for the response, deliver the response. + // + slot = HashSlot(&dv->origName); + namehash = DomainNameHashValue(&dv->origName); + + cg = CacheGroupForName(m, (const mDNSu32)slot, namehash, &dv->origName); + if (!cg) + { + LogDNSSEC("DNSSECNegativeValidationCB: cg NULL for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); + goto done; + } + if (!dv->ac) + { + // If we don't have the AuthChain, it means we could not validate the rrset. Locate the + // original question based on dv->origName, dv->origType. + InitializeQuestion(m, &dv->q, dv->InterfaceID, &dv->origName, dv->origType, mDNSNULL, mDNSNULL); + // Need to be reset ValidatingResponse as we are looking for the cache record that would answer + // the original question + dv->q.ValidatingResponse = mDNSfalse; + for (cr = cg->members; cr; cr = cr->next) + { + if (SameNameRecordAnswersQuestion(&cr->resrec, &dv->q)) + { + answer = &cr->resrec; + break; + } + } + } + else + { + if (!dv->ac->rrset) + { + LogMsg("DNSSECNegativeValidationCB: ERROR!! Validated RRSET NULL"); + goto done; + } + + rrtype = dv->origType; + rrclass = dv->ac->rrset->rrclass; + + for (ac = dv->ac; ac; ac = ac->next) + { + for (rv = ac->rrset; rv; rv = rv->next) + { + if (rv->rrtype == kDNSType_NSEC) + rv->found = 0; + } + } + + // Check to see if we can find all the elements in the rrset + for (cr = cg->members; cr; cr = cr->next) + { + if (cr->resrec.RecordType == kDNSRecordTypePacketNegative && + cr->resrec.rrtype == rrtype && cr->resrec.rrclass == rrclass) + { + CacheRecord *ncr; + for (ncr = cr->nsec; ncr; ncr = ncr->next) + { + // We have RRSIGs for the NSECs cached there too + if (ncr->resrec.rrtype != kDNSType_NSEC) + continue; + for (ac = dv->ac; ac; ac = ac->next) + { + for (rv = ac->rrset; rv; rv = rv->next) + { + if (rv->rrtype == kDNSType_NSEC && rv->rdlength == ncr->resrec.rdlength && + rv->rdatahash == ncr->resrec.rdatahash) + { + if (SameDomainName(ncr->resrec.name, &rv->name) && + SameRDataBody(&ncr->resrec, (const RDataBody *)rv->rdata, SameDomainName)) + { + LogDNSSEC("DNSSECNegativeValidationCB: setting found %s", CRDisplayString(m, ncr)); + answer = &cr->resrec; + rv->found = 1; + break; + } + } + } + if (rv) + break; + } + } + if (!rv) + { + // The validated rrset does not have the element in the cache, re-validate + LogDNSSEC("DNSSECNegativeValidationCB: CacheRecord %s, not found in the validated set", CRDisplayString(m, cr)); + goto done; + } + } + } + // Check to see if we have elements that were not in the cache + for (ac = dv->ac; ac; ac = ac->next) + { + for (rv = ac->rrset; rv; rv = rv->next) + { + if (rv->rrtype == kDNSType_NSEC) + { + if (!rv->found) + { + // We had more elements in the validated set, re-validate + LogDNSSEC("DNSSECNegativeValidationCB: Record %##s (%s) not found in the cache", rv->name.c, DNSTypeName(rv->rrtype)); + goto done; + } + rv->found = 0; + } + } + } + } + + // It is not an error for things to disappear underneath + if (!answer) + { + LogDNSSEC("DNSSECNegativeValidationCB: answer NULL"); + goto done; + } + + DeliverDNSSECStatus(m, answer, status); + SetTTLRRSet(m, dv, status); + +done: + FreeDNSSECVerifier(m, dv); +} + +mDNSexport void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *q) +{ + mDNSu32 slot = HashSlot(&q->qname); + CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + CacheRecord *rr; + + LogDNSSEC("VerifySignature called for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + if (!dv) + { + if (!q->qDNSServer || q->qDNSServer->cellIntf) + { + LogDNSSEC("VerifySignature: Disabled"); + return; + } + // We assume that the verifier's question has been initialized here so that ValidateWithNSECS below + // knows what it has prove the non-existence of. + dv = AllocateDNSSECVerifier(m, &q->qname, q->qtype, q->InterfaceID, DNSSECPositiveValidationCB, VerifySigCallback); + if (!dv) { LogMsg("VerifySignature: ERROR!! memory alloc failed"); return; } + } + + // If we find a CNAME response to the question, remember what qtype + // caused the CNAME response. origType is not sufficient as we + // recursively validate the response and origType is initialized above + // the first time this function is called. + dv->currQtype = q->qtype; + + // Walk the cache and get all the rrsets for verification. + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + if (SameNameRecordAnswersQuestion(&rr->resrec, q)) + { + // We also get called for RRSIGs which matches qtype. We don't need that here as we are + // building rrset for matching q->qname. Checking for RRSIG type is important as otherwise + // we would miss the CNAME answering any qtype. + if (rr->resrec.rrtype == kDNSType_RRSIG && rr->resrec.rrtype != q->qtype) + { + LogDNSSEC("VerifySignature: Question %##s (%s) answered with RRSIG record %s, not using it", q->qname.c, DNSTypeName(q->qtype), CRDisplayString(m, rr)); + continue; + } + + // See DNSSECRecordAnswersQuestion: This should never happen. NSEC records are + // answered directly only when the qtype is NSEC. Otherwise, NSEC records are + // used only for denial of existence and hence should go through negative cache + // entry. + if (rr->resrec.rrtype == kDNSType_NSEC && q->qtype != kDNSType_NSEC) + { + LogMsg("VerifySignature: ERROR!! Question %##s (%s) answered using NSEC record %s", q->qname.c, DNSTypeName(q->qtype), CRDisplayString(m, rr)); + continue; + } + + // We might get a NSEC response when we first send the query out from the "core" for ValidationRequired + // questions. Later as part of validating the response, we might get a NSEC response. + if (rr->resrec.RecordType == kDNSRecordTypePacketNegative && DNSSECQuestion(q)) + { + dv->DVCallback = DNSSECNegativeValidationCB; + // If we can't find the NSEC, we can't validate. This can happens if we are + // behind a non-DNSSEC aware CPE/server. + if (!rr->nsec) + { + LogDNSSEC("VerifySignature: No nsecs found for %s", CRDisplayString(m, rr)); + dv->DVCallback(m, dv, DNSSEC_Insecure); + return; + } + ValidateWithNSECS(m, dv, rr); + return; + } + + if (AddRRSetToVerifier(dv, &rr->resrec, mDNSNULL, RRVS_rr) != mStatus_NoError) + { + dv->DVCallback(m, dv, DNSSEC_Indeterminate); + return; + } + } + if (!dv->rrset) + { + LogMsg("VerifySignature: rrset mDNSNULL for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); + dv->DVCallback(m, dv, DNSSEC_Indeterminate); + return; + } + dv->next = RRVS_rrsig; + StartDNSSECVerification(m, dv); +} + + +mDNSlocal mDNSBool TrustedKeyPresent(mDNS *const m, DNSSECVerifier *dv) +{ + rdataRRSig *rrsig; + rdataDS *ds; + rdataDNSKey *key; + TrustAnchor *ta; + RRVerifier *keyv; + + rrsig = (rdataRRSig *)dv->rrsig->rdata; + + // Walk all our trusted DS Records to see if we have a matching DNS KEY record that verifies + // the hash. If we find one, verify that this key was used to sign the KEY rrsets in + // this zone. Loop till we find one. + for (ta = m->TrustAnchors; ta; ta = ta->next) + { + ds = (rdataDS *)&ta->rds; + if ((ds->digestType != SHA1_DIGEST_TYPE) && (ds->digestType != SHA256_DIGEST_TYPE)) + { + LogMsg("TrustedKeyPresent: Unsupported digest %d", ds->digestType); + continue; + } + else + { + debugdnssec("TrustedKeyPresent: digest type %d", ds->digestType); + } + for (keyv = dv->key; keyv; keyv = keyv->next) + { + key = (rdataDNSKey *)keyv->rdata; + mDNSu16 tag = (mDNSu16)keytag((mDNSu8 *)key, keyv->rdlength); + if (tag != ds->keyTag) + { + debugdnssec("TrustedKeyPresent:Not a valid keytag %d", tag); + continue; + } + if (!SameDomainName(&keyv->name, &ta->zone)) + { + debugdnssec("TrustedKeyPresent: domainame mismatch key %##s, ta %##s", keyv->name.c, ta->zone.c); + continue; + } + return mDNStrue; + } + } + return mDNSfalse; +} + +mDNSlocal mStatus TrustedKey(mDNS *const m, DNSSECVerifier *dv) +{ + mDNSu8 *digest; + int digestLen; + domainname name; + rdataRRSig *rrsig; + rdataDS *ds; + rdataDNSKey *key; + TrustAnchor *ta; + RRVerifier *keyv; + mStatus algRet; + mDNSu32 currTime = mDNSPlatformUTC(); + + rrsig = (rdataRRSig *)dv->rrsig->rdata; + + // Walk all our trusted DS Records to see if we have a matching DNS KEY record that verifies + // the hash. If we find one, verify that this key was used to sign the KEY rrsets in + // this zone. Loop till we find one. + for (ta = m->TrustAnchors; ta; ta = ta->next) + { + ds = (rdataDS *)&ta->rds; + if ((ds->digestType != SHA1_DIGEST_TYPE) && (ds->digestType != SHA256_DIGEST_TYPE)) + { + LogMsg("TrustedKey: Unsupported digest %d", ds->digestType); + continue; + } + else + { + debugdnssec("TrustedKey: Zone %##s, digest type %d, tag %d", ta->zone.c, ds->digestType, ds->keyTag); + } + for (keyv = dv->key; keyv; keyv = keyv->next) + { + key = (rdataDNSKey *)keyv->rdata; + mDNSu16 tag = (mDNSu16)keytag((mDNSu8 *)key, keyv->rdlength); + if (tag != ds->keyTag) + { + debugdnssec("TrustedKey:Not a valid keytag %d", tag); + continue; + } + if (!SameDomainName(&keyv->name, &ta->zone)) + { + debugdnssec("TrustedKey: domainame mismatch key %##s, ta %##s", keyv->name.c, ta->zone.c); + continue; + } + if (DNS_SERIAL_LT(ta->validUntil, currTime)) + { + LogDNSSEC("TrustedKey: Expired: currentTime %d, ExpireTime %d", (int)currTime, ta->validUntil); + continue; + } + if (DNS_SERIAL_LT(currTime, ta->validFrom)) + { + LogDNSSEC("TrustedKey: Future: currentTime %d, InceptTime %d", (int)currTime, ta->validFrom); + continue; + } + + if (DNSNameToLowerCase((domainname *)&rrsig->signerName, &name) != mStatus_NoError) + { + LogMsg("TrustedKey: ERROR!! cannot convert to lower case"); + continue; + } + + if (dv->ctx) AlgDestroy(dv->ctx); + dv->ctx = AlgCreate(DIGEST_ALG, ds->digestType); + if (!dv->ctx) + { + LogMsg("TrustedKey: ERROR!! No digest support"); + continue; + } + digest = ds->digest; + digestLen = ta->digestLen; + + AlgAdd(dv->ctx, name.c, DomainNameLength(&name)); + AlgAdd(dv->ctx, key, keyv->rdlength); + + algRet = AlgVerify(dv->ctx, mDNSNULL, 0, digest, digestLen); + AlgDestroy(dv->ctx); + dv->ctx = mDNSNULL; + if (algRet == mStatus_NoError) + { + LogDNSSEC("TrustedKey: DS Validated Successfully, need to verify the key %d", tag); + // We found the DNS KEY that is authenticated by the DS in our parent zone. Check to see if this key + // was used to sign the DNS KEY RRSET. If so, then the keys in our DNS KEY RRSET are valid + if (ValidateSignatureWithKeyForAllRRSigs(dv, dv->key, keyv, dv->rrsigKey)) + { + LogDNSSEC("TrustedKey: DS Validated Successfully %d", tag); + return mStatus_NoError; + } + } + } + } + return mStatus_NoSuchRecord; +} + +mDNSlocal CacheRecord* NegativeCacheRecordForRR(mDNS *const m, const ResourceRecord *const rr) +{ + mDNSu32 slot; + mDNSu32 namehash; + CacheGroup *cg; + CacheRecord *cr; + + slot = HashSlot(rr->name); + namehash = DomainNameHashValue(rr->name); + cg = CacheGroupForName(m, slot, namehash, rr->name); + if (!cg) + { + LogMsg("NegativeCacheRecordForRR: cg null %##s", rr->name->c); + return mDNSNULL; + } + for (cr=cg->members; cr; cr=cr->next) + { + if (cr->resrec.RecordType == kDNSRecordTypePacketNegative && (&cr->resrec == rr)) + return cr; + } + return mDNSNULL; +} + +mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +{ + DNSSECVerifier *dv = (DNSSECVerifier *)question->QuestionContext; + mDNSu16 rrtype; + CacheRecord *negcr; + + debugdnssec("VerifySigCallback: AddRecord %d, dv %p", AddRecord, dv); + + if (!AddRecord) return; + + LogDNSSEC("VerifySigCallback: Called with record %s", RRDisplayString(m, answer)); + + mDNS_Lock(m); + if ((m->timenow - question->StopTime) >= 0) + { + mDNS_Unlock(m); + LogDNSSEC("VerifySigCallback: Question %##s (%s) timed out", question->qname.c, DNSTypeName(question->qtype)); + dv->DVCallback(m, dv, DNSSEC_Indeterminate); + return; + } + mDNS_Unlock(m); + + if (answer->RecordType == kDNSRecordTypePacketNegative) + { + CacheRecord *cr; + LogDNSSEC("VerifySigCallback: Received a negative answer with record %s, AddRecord %d", + RRDisplayString(m, answer), AddRecord); + cr = NegativeCacheRecordForRR(m, answer); + if (cr && cr->nsec) + { + dv->DVCallback = DNSSECNegativeValidationCB; + ValidateWithNSECS(m, dv, cr); + } + else + { + LogDNSSEC("VerifySigCallback: Missing record (%s) Negative Cache Record %p", RRDisplayString(m, answer), cr); + dv->DVCallback(m, dv, DNSSEC_Bogus); + } + return; + } + + if (!dv->rrset) + { + LogMsg("VerifySigCallback: ERROR!! rrset NULL"); + dv->DVCallback(m, dv, DNSSEC_Indeterminate); + return; + } + + rrtype = answer->rrtype; + // Check whether we got any answers for the question. If there are no answers, we + // can't do the verification. + // + // We need to look at the whole rrset for verifying the signatures. This callback gets + // called back for each record in the rrset sequentially and we won't know when to start the + // verification. Hence, we look for all the records in the rrset ourselves using the + // CheckXXX function below. The caller has to ensure that all the records in the rrset are + // added to the cache before calling this callback which happens naturally because all + // unicast records are marked for DelayDelivery and hence added to the cache before the + // callback is done. + // + // We also need the RRSIGs for the rrset to do the validation. It is possible that the + // cache contains RRSIG records but it may not be a valid record when we filter them + // in CheckXXX function. For example, some application can query for RRSIG records which + // might come back with a partial set of RRSIG records from the recursive server and + // they may not be the right ones for the current validation. In this case, we still + // need to send the query out to get the right RRSIGs but the "core" should not answer + // this query with the same records that we checked and found them to be unusable. + // + // We handle this in two ways: + // + // 1) AnswerNewQuestion always sends the "ValidatingResponse" query out bypassing the cache. + // + // 2) DNSSECRecordAnswersQuestion does not answer a question with RRSIGs matching the + // same name as the query until the typeCovered also matches the query's type. + // + // NOTE: We use "next - 1" as next always points to what we are going to fetch next and not the one + // we are fetching currently + switch(dv->next - 1) + { + case RRVS_rr: + // Verification always starts at RRVS_rrsig (which means dv->next points at RRVS_key) as verification does + // not begin until we have the main rrset. + LogDNSSEC("VerifySigCallback: ERROR!! rrset %##s dv->next is RRVS_rr", dv->rrset->name.c); + return; + case RRVS_rrsig: + // We can get called back with rrtype matching qtype as new records are added to the cache + // triggered by other questions. This could potentially mean that the rrset that is being + // validated by this "dv" whose rrsets were initialized at the beginning of the verification + // may not be the right one. If this case happens, we will detect this at the end of validation + // and throw away the validation results. This should not be a common case. + if (rrtype != kDNSType_RRSIG) + { + LogDNSSEC("VerifySigCallback: RRVS_rrsig called with %s", RRDisplayString(m, answer)); + return; + } + if (CheckRRSIGForRRSet(m, dv, &negcr) != mStatus_NoError) + { + LogDNSSEC("VerifySigCallback: Unable to find RRSIG for %##s (%s), question %##s", dv->rrset->name.c, + DNSTypeName(dv->rrset->rrtype), question->qname.c); + dv->DVCallback(m, dv, DNSSEC_Bogus); + return; + } + break; + case RRVS_key: + // We are waiting for the DNSKEY record and hence dv->key should be NULL. If RRSIGs are being + // returned first, ignore them for now. + if (dv->key) + LogDNSSEC("VerifySigCallback: ERROR!! RRVS_key dv->key non-NULL for %##s", question->qname.c); + if (rrtype == kDNSType_RRSIG) + { + LogDNSSEC("VerifySigCallback: RRVS_key rrset type %s, %##s received before DNSKEY", DNSTypeName(rrtype), question->qname.c); + return; + } + if (rrtype != question->qtype) + { + LogDNSSEC("VerifySigCallback: ERROR!! RRVS_key rrset type %s, %##s not matching qtype %d", DNSTypeName(rrtype), question->qname.c, + question->qtype); + return; + } + if (CheckKeyForRRSIG(m, dv, &negcr) != mStatus_NoError) + { + LogDNSSEC("VerifySigCallback: Unable to find DNSKEY for %##s (%s), question %##s", dv->rrset->name.c, + DNSTypeName(dv->rrset->rrtype), question->qname.c); + dv->DVCallback(m, dv, DNSSEC_Indeterminate); + return; + } + break; + case RRVS_rrsig_key: + // If we are in RRVS_rrsig_key, it means that we already found the relevant DNSKEYs (dv->key should be non-NULL). + // If DNSKEY record is being returned i.e., it means it is being added to the cache, then it can't be in our + // list. + if (!dv->key) + LogDNSSEC("VerifySigCallback: ERROR!! RRVS_rrsig_key dv->key NULL for %##s", question->qname.c); + if (rrtype == question->qtype) + { + LogDNSSEC("VerifySigCallback: RRVS_rrsig_key rrset type %s, %##s", DNSTypeName(rrtype), question->qname.c); + CheckOneKeyForRRSIG(dv, answer); + return; + } + if (rrtype != kDNSType_RRSIG) + { + LogDNSSEC("VerifySigCallback: RRVS_rrsig_key rrset type %s, %##s not matching qtype %d", DNSTypeName(rrtype), question->qname.c, + question->qtype); + return; + } + if (CheckRRSIGForKey(m, dv, &negcr) != mStatus_NoError) + { + LogDNSSEC("VerifySigCallback: Unable to find RRSIG for %##s (%s), question %##s", dv->rrset->name.c, + DNSTypeName(dv->rrset->rrtype), question->qname.c); + dv->DVCallback(m, dv, DNSSEC_Bogus); + return; + } + break; + case RRVS_ds: + if (rrtype == question->qtype) + { + LogDNSSEC("VerifySigCallback: RRVS_ds rrset type %s, %##s", DNSTypeName(rrtype), question->qname.c); + } + else + { + LogDNSSEC("VerifySigCallback: RRVS_ds rrset type %s, %##s received before DS", DNSTypeName(rrtype), question->qname.c); + } + // It is not an error if we don't find the DS record as we could have + // a trusted key. Or this is not a secure delegation which will be handled + // below. + if (CheckDSForKey(m, dv, &negcr) != mStatus_NoError) + { + LogDNSSEC("VerifySigCallback: Unable find DS for %##s (%s), question %##s", dv->rrset->name.c, + DNSTypeName(dv->rrset->rrtype), question->qname.c); + } + // dv->next is already at RRVS_done, so if we "break" from here, we will end up + // in FinishDNSSECVerification. We should not do that if we receive a negative + // response. For all other cases above, GetAllRRSetsForVerification handles + // negative cache record + if (negcr) + { + if (!negcr->nsec) + { + LogDNSSEC("VerifySigCallback: No nsec records for %##s (DS)", dv->ds->name.c); + dv->DVCallback(m, dv, DNSSEC_Bogus); + return; + } + dv->DVCallback = DNSSECNegativeValidationCB; + ValidateWithNSECS(m, dv, negcr); + return; + } + break; + default: + LogDNSSEC("VerifySigCallback: ERROR!! default case rrset %##s question %##s", dv->rrset->name.c, question->qname.c); + dv->DVCallback(m, dv, DNSSEC_Bogus); + return; + } + if (dv->next != RRVS_done) + { + mDNSBool done = GetAllRRSetsForVerification(m, dv); + if (done) + { + if (dv->next != RRVS_done) + LogMsg("VerifySigCallback ERROR!! dv->next is not done"); + else + LogDNSSEC("VerifySigCallback: all rdata sets available for sig verification"); + } + else + { + LogDNSSEC("VerifySigCallback: all rdata sets not available for sig verification"); + return; + } + } + FinishDNSSECVerification(m, dv); +} diff --git a/mDNSCore/dnssec.h b/mDNSCore/dnssec.h new file mode 100644 index 0000000..91aabeb --- /dev/null +++ b/mDNSCore/dnssec.h @@ -0,0 +1,125 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __DNSSEC_H +#define __DNSSEC_H + +#include "CryptoAlg.h" +#include "mDNSDebug.h" + +typedef enum +{ + RRVS_rr, RRVS_rrsig, RRVS_key, RRVS_rrsig_key, RRVS_ds, RRVS_done, +} RRVerifierSet; + +typedef struct RRVerifier_struct RRVerifier; +typedef struct DNSSECVerifier_struct DNSSECVerifier; +typedef struct AuthChain_struct AuthChain; + +struct RRVerifier_struct +{ + RRVerifier *next; + mDNSu16 rrtype; + mDNSu16 rrclass; + mDNSu32 rroriginalttl; + mDNSu16 rdlength; + mDNSu16 found; + mDNSu32 namehash; + mDNSu32 rdatahash; + domainname name; + mDNSu8 *rdata; +}; + +// Each AuthChain element has one rrset (with multiple resource records of same type), rrsig and key +// that validates the rrset. +struct AuthChain_struct +{ + AuthChain *next; // Next element in the chain + RRVerifier *rrset; // RRSET that is authenticated + RRVerifier *rrsig; // Signature for that RRSET + RRVerifier *key; // Public key for that RRSET +}; + +typedef void DNSSECVerifierCallback (mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status); +// +// When we do a validation for a question, there might be additional validations that needs to be done e.g., +// wildcard expanded answer. It is also possible that in the case of nsec we need to prove both that a wildcard +// does not apply and the closest encloser proves that name does not exist. We identify these with the following +// flags. +// +// Note: In the following, by "marking the validation", we mean that as part of validation we need to prove +// the ones that are marked with. +// +// A wildcard may be used to answer a question. In that case, we need to verify that the right wildcard was +// used in answering the question. This is done by marking the validation with WILDCARD_PROVES_ANSWER_EXPANDED. +// +// Sometimes we get a NXDOMAIN response. In this case, we may have a wildcard where we need to prove +// that the wildcard proves that the name does not exist. This is done by marking the validation with +// WILDCARD_PROVES_NONAME_EXISTS. +// +// In the case of NODATA error, sometimes the name may exist but the query type does not exist. This is done by +// marking the validation with NSEC_PROVES_NOTYPE_EXISTS. +// +// In both NXDOMAIN and NODATA proofs, we may have to prove that the NAME does not exist. This is done by marking +// the validation with NSEC_PROVES_NONAME_EXISTS. +// +#define WILDCARD_PROVES_ANSWER_EXPANDED 0x00000001 +#define WILDCARD_PROVES_NONAME_EXISTS 0x00000002 +#define NSEC_PROVES_NOTYPE_EXISTS 0x00000004 +#define NSEC_PROVES_NONAME_EXISTS 0x00000008 + +struct DNSSECVerifier_struct +{ + domainname origName; // Original question name that needs verification + mDNSu16 origType; // Original question type corresponding to origName + mDNSu16 currQtype; // Current question type that is being verified + mDNSInterfaceID InterfaceID; // InterfaceID of the question + DNSQuestion q; + mDNSu8 recursed; // Number of times recursed during validation + mDNSu32 flags; + RRVerifierSet next; + domainname *wildcardName; // set if the answer is wildcard expanded + RRVerifier *pendingNSEC; + DNSSECVerifierCallback *DVCallback; + DNSSECVerifier *parent; + RRVerifier *rrset; // rrset for which we have to verify + RRVerifier *rrsig; // RRSIG for rrset + RRVerifier *key; // DNSKEY for rrset + RRVerifier *rrsigKey; // RRSIG for DNSKEY + RRVerifier *ds; // DS for DNSKEY set in parent zone + AuthChain *ac; + AuthChain **actail; + AlgContext *ctx; +}; + +#define LogDNSSEC LogOperation + +#define DNS_SERIAL_GT(a, b) ((int)((a) - (b)) > 0) +#define DNS_SERIAL_LT(a, b) ((int)((a) - (b)) < 0) + +extern void StartDNSSECVerification(mDNS *const m, DNSSECVerifier *dv); +extern RRVerifier* AllocateRRVerifier(const ResourceRecord *const rr, mStatus *status); +extern mStatus AddRRSetToVerifier(DNSSECVerifier *dv, const ResourceRecord *const rr, RRVerifier *rv, RRVerifierSet set); +extern void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *q); +extern void FreeDNSSECVerifier(mDNS *const m, DNSSECVerifier *dv); +extern DNSSECVerifier *AllocateDNSSECVerifier(mDNS *const m, const domainname *name, mDNSu16 rrtype, mDNSInterfaceID InterfaceID, + DNSSECVerifierCallback dvcallback, mDNSQuestionCallback qcallback); +extern void InitializeQuestion(mDNS *const m, DNSQuestion *question, mDNSInterfaceID InterfaceID, const domainname *qname, + mDNSu16 qtype, mDNSQuestionCallback *callback, void *context); +extern void ValidateRRSIG(DNSSECVerifier *dv, RRVerifierSet type, const ResourceRecord *const rr); +extern void AuthChainLink(DNSSECVerifier *dv, AuthChain *ae); + +#endif // __DNSSEC_H diff --git a/mDNSCore/mDNS.c b/mDNSCore/mDNS.c index d8be49a..4729d7a 100755 --- a/mDNSCore/mDNS.c +++ b/mDNSCore/mDNS.c @@ -1,13 +1,13 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2012 Apple Computer, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,49 +16,38 @@ * * This code is completely 100% portable C. It does not depend on any external header files * from outside the mDNS project -- all the types it expects to find are defined right here. - * + * * The previous point is very important: This file does not depend on any external * header files. It should compile on *any* platform that has a C compiler, without * making *any* assumptions about availability of so-called "standard" C functions, * routines, or types (which may or may not be present on any given platform). - - * Formatting notes: - * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion - * on C indentation can be found on the web, such as , - * but for the sake of brevity here I will say just this: Curly braces are not syntactially - * part of an "if" statement; they are the beginning and ending markers of a compound statement; - * therefore common sense dictates that if they are part of a compound statement then they - * should be indented to the same level as everything else in that compound statement. - * Indenting curly braces at the same level as the "if" implies that curly braces are - * part of the "if", which is false. (This is as misleading as people who write "char* x,y;" - * thinking that variables x and y are both of type "char*" -- and anyone who doesn't - * understand why variable y is not of type "char*" just proves the point that poor code - * layout leads people to unfortunate misunderstandings about how the C language really works.) */ #include "DNSCommon.h" // Defines general DNS untility routines -#include "uDNS.h" // Defines entry points into unicast-specific routines +#include "uDNS.h" // Defines entry points into unicast-specific routines +#include "nsec.h" +#include "dnssec.h" // Disable certain benign warnings with Microsoft compilers -#if(defined(_MSC_VER)) - // Disable "conditional expression is constant" warning for debug macros. - // Otherwise, this generates warnings for the perfectly natural construct "while(1)" - // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know - #pragma warning(disable:4127) - - // Disable "assignment within conditional expression". - // Other compilers understand the convention that if you place the assignment expression within an extra pair - // of parentheses, this signals to the compiler that you really intended an assignment and no warning is necessary. - // The Microsoft compiler doesn't understand this convention, so in the absense of any other way to signal - // to the compiler that the assignment is intentional, we have to just turn this warning off completely. - #pragma warning(disable:4706) +#if (defined(_MSC_VER)) +// Disable "conditional expression is constant" warning for debug macros. +// Otherwise, this generates warnings for the perfectly natural construct "while(1)" +// If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know + #pragma warning(disable:4127) + +// Disable "assignment within conditional expression". +// Other compilers understand the convention that if you place the assignment expression within an extra pair +// of parentheses, this signals to the compiler that you really intended an assignment and no warning is necessary. +// The Microsoft compiler doesn't understand this convention, so in the absense of any other way to signal +// to the compiler that the assignment is intentional, we have to just turn this warning off completely. + #pragma warning(disable:4706) #endif #if APPLE_OSX_mDNSResponder #include -#if ! NO_WCF +#if !NO_WCF WCFConnection *WCFConnectionNew(void) __attribute__((weak_import)); void WCFConnectionDealloc(WCFConnection* c) __attribute__((weak_import)); @@ -78,6 +67,12 @@ mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAdd mDNSlocal mDNSBool CacheRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q); mDNSlocal mDNSBool LocalRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q); mDNSlocal void mDNS_PurgeBeforeResolve(mDNS *const m, DNSQuestion *q); +mDNSlocal void mDNS_CheckForCachedNSECS(mDNS *const m, DNSQuestion *q); +mDNSlocal void mDNS_SendKeepalives(mDNS *const m); +mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSAddr *laddr, mDNSAddr *raddr, mDNSu32 *seq, + mDNSu32 *ack, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu16 *win); + +#define mDNS_KeepaliveRecord(rr) ((rr)->rrtype == kDNSType_NULL && SameDomainLabel(SecondLabel((rr)->name)->c, (mDNSu8 *)"\x0A_keepalive")) // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -94,13 +89,13 @@ mDNSlocal void mDNS_PurgeBeforeResolve(mDNS *const m, DNSQuestion *q); #define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6) mDNSexport const char *const mDNS_DomainTypeNames[] = - { - "b._dns-sd._udp.", // Browse - "db._dns-sd._udp.", // Default Browse - "lb._dns-sd._udp.", // Automatic Browse - "r._dns-sd._udp.", // Registration - "dr._dns-sd._udp." // Default Registration - }; +{ + "b._dns-sd._udp.", // Browse + "db._dns-sd._udp.", // Default Browse + "lb._dns-sd._udp.", // Automatic Browse + "r._dns-sd._udp.", // Registration + "dr._dns-sd._udp." // Default Registration +}; #ifdef UNICAST_DISABLED #define uDNS_IsActiveQuery(q, u) mDNSfalse @@ -113,402 +108,425 @@ mDNSexport const char *const mDNS_DomainTypeNames[] = #endif // If there is a authoritative LocalOnly record that answers questions of type A, AAAA and CNAME -// this returns true. Main use is to handle /etc/hosts records. +// this returns true. Main use is to handle /etc/hosts records. #define LORecordAnswersAddressType(rr) ((rr)->ARType == AuthRecordLocalOnly && \ - (rr)->resrec.RecordType & kDNSRecordTypeUniqueMask && \ - ((rr)->resrec.rrtype == kDNSType_A || (rr)->resrec.rrtype == kDNSType_AAAA || \ - (rr)->resrec.rrtype == kDNSType_CNAME)) + (rr)->resrec.RecordType & kDNSRecordTypeUniqueMask && \ + ((rr)->resrec.rrtype == kDNSType_A || (rr)->resrec.rrtype == kDNSType_AAAA || \ + (rr)->resrec.rrtype == kDNSType_CNAME)) -#define FollowCNAME(q, rr, AddRecord) (AddRecord && (q)->qtype != kDNSType_CNAME && \ - (rr)->RecordType != kDNSRecordTypePacketNegative && \ - (rr)->rrtype == kDNSType_CNAME) +#define FollowCNAME(q, rr, AddRecord) (AddRecord && (q)->qtype != kDNSType_CNAME && \ + (rr)->RecordType != kDNSRecordTypePacketNegative && \ + (rr)->rrtype == kDNSType_CNAME) mDNSlocal void SetNextQueryStopTime(mDNS *const m, const DNSQuestion *const q) - { - if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("SetNextQueryTime: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); +{ + if (m->mDNS_busy != m->mDNS_reentrancy+1) + LogMsg("SetNextQueryTime: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); #if ForceAlerts - if (m->mDNS_busy != m->mDNS_reentrancy+1) *(long*)0 = 0; + if (m->mDNS_busy != m->mDNS_reentrancy+1) *(long*)0 = 0; #endif - if (m->NextScheduledStopTime - q->StopTime > 0) - m->NextScheduledStopTime = q->StopTime; - } + if (m->NextScheduledStopTime - q->StopTime > 0) + m->NextScheduledStopTime = q->StopTime; +} mDNSexport void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q) - { - if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("SetNextQueryTime: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); +{ + if (m->mDNS_busy != m->mDNS_reentrancy+1) + LogMsg("SetNextQueryTime: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); #if ForceAlerts - if (m->mDNS_busy != m->mDNS_reentrancy+1) *(long*)0 = 0; + if (m->mDNS_busy != m->mDNS_reentrancy+1) *(long*)0 = 0; #endif - if (ActiveQuestion(q)) - { - // Depending on whether this is a multicast or unicast question we want to set either: - // m->NextScheduledQuery = NextQSendTime(q) or - // m->NextuDNSEvent = NextQSendTime(q) - mDNSs32 *const timer = mDNSOpaque16IsZero(q->TargetQID) ? &m->NextScheduledQuery : &m->NextuDNSEvent; - if (*timer - NextQSendTime(q) > 0) - *timer = NextQSendTime(q); - } - } + if (ActiveQuestion(q)) + { + // Depending on whether this is a multicast or unicast question we want to set either: + // m->NextScheduledQuery = NextQSendTime(q) or + // m->NextuDNSEvent = NextQSendTime(q) + mDNSs32 *const timer = mDNSOpaque16IsZero(q->TargetQID) ? &m->NextScheduledQuery : &m->NextuDNSEvent; + if (*timer - NextQSendTime(q) > 0) + *timer = NextQSendTime(q); + } +} mDNSlocal void ReleaseAuthEntity(AuthHash *r, AuthEntity *e) - { +{ #if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING >= 1 - unsigned int i; - for (i=0; inext = r->rrauth_free; - r->rrauth_free = e; - r->rrauth_totalused--; - } + e->next = r->rrauth_free; + r->rrauth_free = e; + r->rrauth_totalused--; +} mDNSlocal void ReleaseAuthGroup(AuthHash *r, AuthGroup **cp) - { - AuthEntity *e = (AuthEntity *)(*cp); - LogMsg("ReleaseAuthGroup: Releasing AuthGroup %##s", (*cp)->name->c); - if ((*cp)->rrauth_tail != &(*cp)->members) - LogMsg("ERROR: (*cp)->members == mDNSNULL but (*cp)->rrauth_tail != &(*cp)->members)"); - if ((*cp)->name != (domainname*)((*cp)->namestorage)) mDNSPlatformMemFree((*cp)->name); - (*cp)->name = mDNSNULL; - *cp = (*cp)->next; // Cut record from list - ReleaseAuthEntity(r, e); - } +{ + AuthEntity *e = (AuthEntity *)(*cp); + LogMsg("ReleaseAuthGroup: Releasing AuthGroup %##s", (*cp)->name->c); + if ((*cp)->rrauth_tail != &(*cp)->members) + LogMsg("ERROR: (*cp)->members == mDNSNULL but (*cp)->rrauth_tail != &(*cp)->members)"); + if ((*cp)->name != (domainname*)((*cp)->namestorage)) mDNSPlatformMemFree((*cp)->name); + (*cp)->name = mDNSNULL; + *cp = (*cp)->next; // Cut record from list + ReleaseAuthEntity(r, e); +} mDNSlocal AuthEntity *GetAuthEntity(AuthHash *r, const AuthGroup *const PreserveAG) - { - AuthEntity *e = mDNSNULL; - - if (r->rrauth_lock) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); } - r->rrauth_lock = 1; - - if (!r->rrauth_free) - { - // We allocate just one AuthEntity at a time because we need to be able - // free them all individually which normally happens when we parse /etc/hosts into - // AuthHash where we add the "new" entries and discard (free) the already added - // entries. If we allocate as chunks, we can't free them individually. - AuthEntity *storage = mDNSPlatformMemAllocate(sizeof(AuthEntity)); - storage->next = mDNSNULL; - r->rrauth_free = storage; - } - - // If we still have no free records, recycle all the records we can. - // Enumerating the entire auth is moderately expensive, so when we do it, we reclaim all the records we can in one pass. - if (!r->rrauth_free) - { - mDNSu32 oldtotalused = r->rrauth_totalused; - mDNSu32 slot; - for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) - { - AuthGroup **cp = &r->rrauth_hash[slot]; - while (*cp) - { - if ((*cp)->members || (*cp)==PreserveAG) cp=&(*cp)->next; - else ReleaseAuthGroup(r, cp); - } - } - LogInfo("GetAuthEntity: Recycled %d records to reduce auth cache from %d to %d", - oldtotalused - r->rrauth_totalused, oldtotalused, r->rrauth_totalused); - } - - if (r->rrauth_free) // If there are records in the free list, take one - { - e = r->rrauth_free; - r->rrauth_free = e->next; - if (++r->rrauth_totalused >= r->rrauth_report) - { - LogInfo("RR Auth now using %ld objects", r->rrauth_totalused); - if (r->rrauth_report < 100) r->rrauth_report += 10; - else if (r->rrauth_report < 1000) r->rrauth_report += 100; - else r->rrauth_report += 1000; - } - mDNSPlatformMemZero(e, sizeof(*e)); - } - - r->rrauth_lock = 0; - - return(e); - } +{ + AuthEntity *e = mDNSNULL; + + if (r->rrauth_lock) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); } + r->rrauth_lock = 1; + + if (!r->rrauth_free) + { + // We allocate just one AuthEntity at a time because we need to be able + // free them all individually which normally happens when we parse /etc/hosts into + // AuthHash where we add the "new" entries and discard (free) the already added + // entries. If we allocate as chunks, we can't free them individually. + AuthEntity *storage = mDNSPlatformMemAllocate(sizeof(AuthEntity)); + storage->next = mDNSNULL; + r->rrauth_free = storage; + } + + // If we still have no free records, recycle all the records we can. + // Enumerating the entire auth is moderately expensive, so when we do it, we reclaim all the records we can in one pass. + if (!r->rrauth_free) + { + mDNSu32 oldtotalused = r->rrauth_totalused; + mDNSu32 slot; + for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) + { + AuthGroup **cp = &r->rrauth_hash[slot]; + while (*cp) + { + if ((*cp)->members || (*cp)==PreserveAG) cp=&(*cp)->next; + else ReleaseAuthGroup(r, cp); + } + } + LogInfo("GetAuthEntity: Recycled %d records to reduce auth cache from %d to %d", + oldtotalused - r->rrauth_totalused, oldtotalused, r->rrauth_totalused); + } + + if (r->rrauth_free) // If there are records in the free list, take one + { + e = r->rrauth_free; + r->rrauth_free = e->next; + if (++r->rrauth_totalused >= r->rrauth_report) + { + LogInfo("RR Auth now using %ld objects", r->rrauth_totalused); + if (r->rrauth_report < 100) r->rrauth_report += 10; + else if (r->rrauth_report < 1000) r->rrauth_report += 100; + else r->rrauth_report += 1000; + } + mDNSPlatformMemZero(e, sizeof(*e)); + } + + r->rrauth_lock = 0; + + return(e); +} mDNSexport AuthGroup *AuthGroupForName(AuthHash *r, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name) - { - AuthGroup *ag; - for (ag = r->rrauth_hash[slot]; ag; ag=ag->next) - if (ag->namehash == namehash && SameDomainName(ag->name, name)) - break; - return(ag); - } +{ + AuthGroup *ag; + for (ag = r->rrauth_hash[slot]; ag; ag=ag->next) + if (ag->namehash == namehash && SameDomainName(ag->name, name)) + break; + return(ag); +} mDNSexport AuthGroup *AuthGroupForRecord(AuthHash *r, const mDNSu32 slot, const ResourceRecord *const rr) - { - return(AuthGroupForName(r, slot, rr->namehash, rr->name)); - } +{ + return(AuthGroupForName(r, slot, rr->namehash, rr->name)); +} mDNSlocal AuthGroup *GetAuthGroup(AuthHash *r, const mDNSu32 slot, const ResourceRecord *const rr) - { - mDNSu16 namelen = DomainNameLength(rr->name); - AuthGroup *ag = (AuthGroup*)GetAuthEntity(r, mDNSNULL); - if (!ag) { LogMsg("GetAuthGroup: Failed to allocate memory for %##s", rr->name->c); return(mDNSNULL); } - ag->next = r->rrauth_hash[slot]; - ag->namehash = rr->namehash; - ag->members = mDNSNULL; - ag->rrauth_tail = &ag->members; - ag->name = (domainname*)ag->namestorage; - ag->NewLocalOnlyRecords = mDNSNULL; - if (namelen > InlineCacheGroupNameSize) ag->name = mDNSPlatformMemAllocate(namelen); - if (!ag->name) - { - LogMsg("GetAuthGroup: Failed to allocate name storage for %##s", rr->name->c); - ReleaseAuthEntity(r, (AuthEntity*)ag); - return(mDNSNULL); - } - AssignDomainName(ag->name, rr->name); - - if (AuthGroupForRecord(r, slot, rr)) LogMsg("GetAuthGroup: Already have AuthGroup for %##s", rr->name->c); - r->rrauth_hash[slot] = ag; - if (AuthGroupForRecord(r, slot, rr) != ag) LogMsg("GetAuthGroup: Not finding AuthGroup for %##s", rr->name->c); - - return(ag); - } +{ + mDNSu16 namelen = DomainNameLength(rr->name); + AuthGroup *ag = (AuthGroup*)GetAuthEntity(r, mDNSNULL); + if (!ag) { LogMsg("GetAuthGroup: Failed to allocate memory for %##s", rr->name->c); return(mDNSNULL); } + ag->next = r->rrauth_hash[slot]; + ag->namehash = rr->namehash; + ag->members = mDNSNULL; + ag->rrauth_tail = &ag->members; + ag->NewLocalOnlyRecords = mDNSNULL; + if (namelen > sizeof(ag->namestorage)) + ag->name = mDNSPlatformMemAllocate(namelen); + else + ag->name = (domainname*)ag->namestorage; + if (!ag->name) + { + LogMsg("GetAuthGroup: Failed to allocate name storage for %##s", rr->name->c); + ReleaseAuthEntity(r, (AuthEntity*)ag); + return(mDNSNULL); + } + AssignDomainName(ag->name, rr->name); + + if (AuthGroupForRecord(r, slot, rr)) LogMsg("GetAuthGroup: Already have AuthGroup for %##s", rr->name->c); + r->rrauth_hash[slot] = ag; + if (AuthGroupForRecord(r, slot, rr) != ag) LogMsg("GetAuthGroup: Not finding AuthGroup for %##s", rr->name->c); + + return(ag); +} // Returns the AuthGroup in which the AuthRecord was inserted mDNSexport AuthGroup *InsertAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr) - { - AuthGroup *ag; - const mDNSu32 slot = AuthHashSlot(rr->resrec.name); - ag = AuthGroupForRecord(r, slot, &rr->resrec); - if (!ag) ag = GetAuthGroup(r, slot, &rr->resrec); // If we don't have a AuthGroup for this name, make one now - if (ag) - { - LogInfo("InsertAuthRecord: inserting auth record %s from table", ARDisplayString(m, rr)); - *(ag->rrauth_tail) = rr; // Append this record to tail of cache slot list - ag->rrauth_tail = &(rr->next); // Advance tail pointer - } - return ag; - } +{ + AuthGroup *ag; + const mDNSu32 slot = AuthHashSlot(rr->resrec.name); + ag = AuthGroupForRecord(r, slot, &rr->resrec); + if (!ag) ag = GetAuthGroup(r, slot, &rr->resrec); // If we don't have a AuthGroup for this name, make one now + if (ag) + { + LogInfo("InsertAuthRecord: inserting auth record %s from table", ARDisplayString(m, rr)); + *(ag->rrauth_tail) = rr; // Append this record to tail of cache slot list + ag->rrauth_tail = &(rr->next); // Advance tail pointer + } + return ag; +} mDNSexport AuthGroup *RemoveAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr) - { - AuthGroup *a; - AuthGroup **ag = &a; - AuthRecord **rp; - const mDNSu32 slot = AuthHashSlot(rr->resrec.name); - - a = AuthGroupForRecord(r, slot, &rr->resrec); - if (!a) { LogMsg("RemoveAuthRecord: ERROR!! AuthGroup not found for %s", ARDisplayString(m, rr)); return mDNSNULL; } - rp = &(*ag)->members; - while (*rp) - { - if (*rp != rr) - rp=&(*rp)->next; - else - { - // We don't break here, so that we can set the tail below without tracking "prev" pointers - - LogInfo("RemoveAuthRecord: removing auth record %s from table", ARDisplayString(m, rr)); - *rp = (*rp)->next; // Cut record from list - } - } - // TBD: If there are no more members, release authgroup ? - (*ag)->rrauth_tail = rp; - return a; - } +{ + AuthGroup *a; + AuthGroup **ag = &a; + AuthRecord **rp; + const mDNSu32 slot = AuthHashSlot(rr->resrec.name); + + a = AuthGroupForRecord(r, slot, &rr->resrec); + if (!a) { LogMsg("RemoveAuthRecord: ERROR!! AuthGroup not found for %s", ARDisplayString(m, rr)); return mDNSNULL; } + rp = &(*ag)->members; + while (*rp) + { + if (*rp != rr) + rp=&(*rp)->next; + else + { + // We don't break here, so that we can set the tail below without tracking "prev" pointers + + LogInfo("RemoveAuthRecord: removing auth record %s from table", ARDisplayString(m, rr)); + *rp = (*rp)->next; // Cut record from list + } + } + // TBD: If there are no more members, release authgroup ? + (*ag)->rrauth_tail = rp; + return a; +} mDNSexport CacheGroup *CacheGroupForName(const mDNS *const m, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name) - { - CacheGroup *cg; - for (cg = m->rrcache_hash[slot]; cg; cg=cg->next) - if (cg->namehash == namehash && SameDomainName(cg->name, name)) - break; - return(cg); - } +{ + CacheGroup *cg; + for (cg = m->rrcache_hash[slot]; cg; cg=cg->next) + if (cg->namehash == namehash && SameDomainName(cg->name, name)) + break; + return(cg); +} mDNSlocal CacheGroup *CacheGroupForRecord(const mDNS *const m, const mDNSu32 slot, const ResourceRecord *const rr) - { - return(CacheGroupForName(m, slot, rr->namehash, rr->name)); - } +{ + return(CacheGroupForName(m, slot, rr->namehash, rr->name)); +} mDNSexport mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr) - { - NetworkInterfaceInfo *intf; - - if (addr->type == mDNSAddrType_IPv4) - { - // Normally we resist touching the NotAnInteger fields, but here we're doing tricky bitwise masking so we make an exception - if (mDNSv4AddressIsLinkLocal(&addr->ip.v4)) return(mDNStrue); - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx) - if (((intf->ip.ip.v4.NotAnInteger ^ addr->ip.v4.NotAnInteger) & intf->mask.ip.v4.NotAnInteger) == 0) - return(mDNStrue); - } - - if (addr->type == mDNSAddrType_IPv6) - { - if (mDNSv6AddressIsLinkLocal(&addr->ip.v6)) return(mDNStrue); - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx) - 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); - } +{ + NetworkInterfaceInfo *intf; + + if (addr->type == mDNSAddrType_IPv4) + { + // Normally we resist touching the NotAnInteger fields, but here we're doing tricky bitwise masking so we make an exception + if (mDNSv4AddressIsLinkLocal(&addr->ip.v4)) return(mDNStrue); + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx) + if (((intf->ip.ip.v4.NotAnInteger ^ addr->ip.v4.NotAnInteger) & intf->mask.ip.v4.NotAnInteger) == 0) + return(mDNStrue); + } + + if (addr->type == mDNSAddrType_IPv6) + { + if (mDNSv6AddressIsLinkLocal(&addr->ip.v6)) return(mDNStrue); + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx) + 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 NetworkInterfaceInfo *FirstInterfaceForID(mDNS *const m, const mDNSInterfaceID InterfaceID) - { - NetworkInterfaceInfo *intf = m->HostInterfaces; - while (intf && intf->InterfaceID != InterfaceID) intf = intf->next; - return(intf); - } +{ + NetworkInterfaceInfo *intf = m->HostInterfaces; + while (intf && intf->InterfaceID != InterfaceID) intf = intf->next; + return(intf); +} mDNSexport char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID InterfaceID) - { - NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); - return(intf ? intf->ifname : mDNSNULL); - } +{ + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); + return(intf ? intf->ifname : mDNSNULL); +} // Caller should hold the lock mDNSlocal void GenerateNegativeResponse(mDNS *const m) - { - DNSQuestion *q; - if (!m->CurrentQuestion) { LogMsg("GenerateNegativeResponse: ERROR!! CurrentQuestion not set"); return; } - q = m->CurrentQuestion; - LogInfo("GenerateNegativeResponse: Generating negative response for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - - MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any, mDNSNULL); - AnswerCurrentQuestionWithResourceRecord(m, &m->rec.r, QC_addnocache); - if (m->CurrentQuestion == q) { q->ThisQInterval = 0; } // Deactivate this question - // Don't touch the question after this - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - } - -mDNSlocal void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, ResourceRecord *rr) - { - const mDNSBool selfref = SameDomainName(&q->qname, &rr->rdata->u.name); - if (q->CNAMEReferrals >= 10 || selfref) - LogMsg("AnswerQuestionByFollowingCNAME: %p %##s (%s) NOT following CNAME referral %d%s for %s", - q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, selfref ? " (Self-Referential)" : "", RRDisplayString(m, rr)); - else - { - const mDNSu32 c = q->CNAMEReferrals + 1; // Stash a copy of the new q->CNAMEReferrals value - - // The SameDomainName check above is to ignore bogus CNAME records that point right back at - // themselves. Without that check we can get into a case where we have two duplicate questions, - // A and B, and when we stop question A, UpdateQuestionDuplicates copies the value of CNAMEReferrals - // from A to B, and then A is re-appended to the end of the list as a duplicate of B (because - // the target name is still the same), and then when we stop question B, UpdateQuestionDuplicates - // copies the B's value of CNAMEReferrals back to A, and we end up not incrementing CNAMEReferrals - // for either of them. This is not a problem for CNAME loops of two or more records because in - // those cases the newly re-appended question A has a different target name and therefore cannot be - // a duplicate of any other question ('B') which was itself a duplicate of the previous question A. - - // Right now we just stop and re-use the existing query. If we really wanted to be 100% perfect, - // and track CNAMEs coming and going, we should really create a subordinate query here, - // which we would subsequently cancel and retract if the CNAME referral record were removed. - // In reality this is such a corner case we'll ignore it until someone actually needs it. - - LogInfo("AnswerQuestionByFollowingCNAME: %p %##s (%s) following CNAME referral %d for %s", - q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, RRDisplayString(m, rr)); - - mDNS_StopQuery_internal(m, q); // Stop old query - AssignDomainName(&q->qname, &rr->rdata->u.name); // Update qname - q->qnamehash = DomainNameHashValue(&q->qname); // and namehash - // If a unicast query results in a CNAME that points to a .local, we need to re-try - // this as unicast. Setting the mDNSInterface_Unicast tells mDNS_StartQuery_internal - // to try this as unicast query even though it is a .local name - if (!mDNSOpaque16IsZero(q->TargetQID) && IsLocalDomain(&q->qname)) - { - LogInfo("AnswerQuestionByFollowingCNAME: Resolving a .local CNAME %p %##s (%s) Record %s", - q, q->qname.c, DNSTypeName(q->qtype), RRDisplayString(m, rr)); - q->InterfaceID = mDNSInterface_Unicast; - } - mDNS_StartQuery_internal(m, q); // start new query - // Record how many times we've done this. We need to do this *after* mDNS_StartQuery_internal, - // because mDNS_StartQuery_internal re-initializes CNAMEReferrals to zero - q->CNAMEReferrals = c; - } - } +{ + DNSQuestion *q; + if (!m->CurrentQuestion) { LogMsg("GenerateNegativeResponse: ERROR!! CurrentQuestion not set"); return; } + q = m->CurrentQuestion; + LogInfo("GenerateNegativeResponse: Generating negative response for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + + MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any, mDNSNULL); + // We need to force the response through in the following cases + // + // a) SuppressUnusable questions that are suppressed + // b) Append search domains and retry the question + // + // The question may not have set Intermediates in which case we don't deliver negative responses. So, to force + // through we use "QC_forceresponse". + AnswerCurrentQuestionWithResourceRecord(m, &m->rec.r, QC_forceresponse); + if (m->CurrentQuestion == q) { q->ThisQInterval = 0; } // Deactivate this question + // Don't touch the question after this + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it +} + +mDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, ResourceRecord *rr) +{ + const mDNSBool selfref = SameDomainName(&q->qname, &rr->rdata->u.name); + if (q->CNAMEReferrals >= 10 || selfref) + LogMsg("AnswerQuestionByFollowingCNAME: %p %##s (%s) NOT following CNAME referral %d%s for %s", + q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, selfref ? " (Self-Referential)" : "", RRDisplayString(m, rr)); + else + { + const mDNSu32 c = q->CNAMEReferrals + 1; // Stash a copy of the new q->CNAMEReferrals value + + // The SameDomainName check above is to ignore bogus CNAME records that point right back at + // themselves. Without that check we can get into a case where we have two duplicate questions, + // A and B, and when we stop question A, UpdateQuestionDuplicates copies the value of CNAMEReferrals + // from A to B, and then A is re-appended to the end of the list as a duplicate of B (because + // the target name is still the same), and then when we stop question B, UpdateQuestionDuplicates + // copies the B's value of CNAMEReferrals back to A, and we end up not incrementing CNAMEReferrals + // for either of them. This is not a problem for CNAME loops of two or more records because in + // those cases the newly re-appended question A has a different target name and therefore cannot be + // a duplicate of any other question ('B') which was itself a duplicate of the previous question A. + + // Right now we just stop and re-use the existing query. If we really wanted to be 100% perfect, + // and track CNAMEs coming and going, we should really create a subordinate query here, + // which we would subsequently cancel and retract if the CNAME referral record were removed. + // In reality this is such a corner case we'll ignore it until someone actually needs it. + + LogInfo("AnswerQuestionByFollowingCNAME: %p %##s (%s) following CNAME referral %d for %s", + q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, RRDisplayString(m, rr)); + + mDNS_StopQuery_internal(m, q); // Stop old query + AssignDomainName(&q->qname, &rr->rdata->u.name); // Update qname + q->qnamehash = DomainNameHashValue(&q->qname); // and namehash + // If a unicast query results in a CNAME that points to a .local, we need to re-try + // this as unicast. Setting the mDNSInterface_Unicast tells mDNS_StartQuery_internal + // to try this as unicast query even though it is a .local name + if (!mDNSOpaque16IsZero(q->TargetQID) && IsLocalDomain(&q->qname)) + { + LogInfo("AnswerQuestionByFollowingCNAME: Resolving a .local CNAME %p %##s (%s) Record %s", + q, q->qname.c, DNSTypeName(q->qtype), RRDisplayString(m, rr)); + q->InterfaceID = mDNSInterface_Unicast; + } + mDNS_StartQuery_internal(m, q); // start new query + // Record how many times we've done this. We need to do this *after* mDNS_StartQuery_internal, + // because mDNS_StartQuery_internal re-initializes CNAMEReferrals to zero + q->CNAMEReferrals = c; + } +} // For a single given DNSQuestion pointed to by CurrentQuestion, deliver an add/remove result for the single given AuthRecord // Note: All the callers should use the m->CurrentQuestion to see if the question is still valid or not mDNSlocal void AnswerLocalQuestionWithLocalAuthRecord(mDNS *const m, AuthRecord *rr, QC_result AddRecord) - { - DNSQuestion *q = m->CurrentQuestion; - mDNSBool followcname; - - if (!q) - { - LogMsg("AnswerLocalQuestionWithLocalAuthRecord: ERROR!! CurrentQuestion NULL while answering with %s", ARDisplayString(m, rr)); - return; - } - - followcname = FollowCNAME(q, &rr->resrec, AddRecord); - - // We should not be delivering results for record types Unregistered, Deregistering, and (unverified) Unique - if (!(rr->resrec.RecordType & kDNSRecordTypeActiveMask)) - { - LogMsg("AnswerLocalQuestionWithLocalAuthRecord: *NOT* delivering %s event for local record type %X %s", - AddRecord ? "Add" : "Rmv", rr->resrec.RecordType, ARDisplayString(m, rr)); - return; - } - - // 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->AnsweredLocalQ = mDNStrue; - mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback - if (q->QuestionCallback && !q->NoAnswer) - { - q->CurrentAnswers += AddRecord ? 1 : -1; - if (LORecordAnswersAddressType(rr)) - { - if (!followcname || q->ReturnIntermed) - { - // Don't send this packet on the wire as we answered from /etc/hosts - q->ThisQInterval = 0; - q->LOAddressAnswers += AddRecord ? 1 : -1; - q->QuestionCallback(m, q, &rr->resrec, AddRecord); - } - mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again - // The callback above could have caused the question to stop. Detect that - // using m->CurrentQuestion - if (followcname && m->CurrentQuestion == q) - AnswerQuestionByFollowingCNAME(m, q, &rr->resrec); - return; - } - else - q->QuestionCallback(m, q, &rr->resrec, AddRecord); - } - mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again - } +{ + DNSQuestion *q = m->CurrentQuestion; + mDNSBool followcname; + + if (!q) + { + LogMsg("AnswerLocalQuestionWithLocalAuthRecord: ERROR!! CurrentQuestion NULL while answering with %s", ARDisplayString(m, rr)); + return; + } + + followcname = FollowCNAME(q, &rr->resrec, AddRecord); + + // We should not be delivering results for record types Unregistered, Deregistering, and (unverified) Unique + if (!(rr->resrec.RecordType & kDNSRecordTypeActiveMask)) + { + LogMsg("AnswerLocalQuestionWithLocalAuthRecord: *NOT* delivering %s event for local record type %X %s", + AddRecord ? "Add" : "Rmv", rr->resrec.RecordType, ARDisplayString(m, rr)); + return; + } + + // 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->AnsweredLocalQ = mDNStrue; + mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback + if (q->QuestionCallback && !q->NoAnswer) + { + q->CurrentAnswers += AddRecord ? 1 : -1; + if (LORecordAnswersAddressType(rr)) + { + if (!followcname || q->ReturnIntermed) + { + // Don't send this packet on the wire as we answered from /etc/hosts + q->ThisQInterval = 0; + q->LOAddressAnswers += AddRecord ? 1 : -1; + // We can't possibly validate the entries in /etc/hosts. Hence, we + // report it as insecure. + if (q->ValidationRequired) + { + q->ValidationStatus = DNSSEC_Insecure; + q->ValidationState = DNSSECValDone; + } + q->QuestionCallback(m, q, &rr->resrec, AddRecord); + } + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + // The callback above could have caused the question to stop. Detect that + // using m->CurrentQuestion + if (followcname && m->CurrentQuestion == q) + AnswerQuestionByFollowingCNAME(m, q, &rr->resrec); + return; + } + else + { + if (q->ValidationRequired) + { + q->ValidationStatus = DNSSEC_Insecure; + q->ValidationState = DNSSECValDone; + } + q->QuestionCallback(m, q, &rr->resrec, AddRecord); + } + } + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again +} mDNSlocal void AnswerInterfaceAnyQuestionsWithLocalAuthRecord(mDNS *const m, AuthRecord *rr, QC_result AddRecord) - { - if (m->CurrentQuestion) - LogMsg("AnswerInterfaceAnyQuestionsWithLocalAuthRecord: ERROR m->CurrentQuestion already set: %##s (%s)", - m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); - m->CurrentQuestion = m->Questions; - while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) - { - mDNSBool answered; - DNSQuestion *q = m->CurrentQuestion; - if (RRAny(rr)) - answered = ResourceRecordAnswersQuestion(&rr->resrec, q); - else - answered = LocalOnlyRecordAnswersQuestion(rr, q); - if (answered) - AnswerLocalQuestionWithLocalAuthRecord(m, rr, AddRecord); // MUST NOT dereference q again - if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now - m->CurrentQuestion = q->next; - } - m->CurrentQuestion = mDNSNULL; - } +{ + if (m->CurrentQuestion) + LogMsg("AnswerInterfaceAnyQuestionsWithLocalAuthRecord: ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) + { + mDNSBool answered; + DNSQuestion *q = m->CurrentQuestion; + if (RRAny(rr)) + answered = ResourceRecordAnswersQuestion(&rr->resrec, q); + else + answered = LocalOnlyRecordAnswersQuestion(rr, q); + if (answered) + AnswerLocalQuestionWithLocalAuthRecord(m, rr, AddRecord); // MUST NOT dereference q again + if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now + m->CurrentQuestion = q->next; + } + m->CurrentQuestion = mDNSNULL; +} // When a new local AuthRecord is created or deleted, AnswerAllLocalQuestionsWithLocalAuthRecord() // delivers the appropriate add/remove events to listening questions: @@ -522,34 +540,34 @@ mDNSlocal void AnswerInterfaceAnyQuestionsWithLocalAuthRecord(mDNS *const m, Aut // and by mDNS_Deregister_internal() mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRecord *rr, QC_result AddRecord) - { - if (m->CurrentQuestion) - LogMsg("AnswerAllLocalQuestionsWithLocalAuthRecord ERROR m->CurrentQuestion already set: %##s (%s)", - m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); - - m->CurrentQuestion = m->LocalOnlyQuestions; - while (m->CurrentQuestion && m->CurrentQuestion != m->NewLocalOnlyQuestions) - { - mDNSBool answered; - DNSQuestion *q = m->CurrentQuestion; - // We are called with both LocalOnly/P2P record or a regular AuthRecord - if (RRAny(rr)) - answered = ResourceRecordAnswersQuestion(&rr->resrec, q); - else - answered = LocalOnlyRecordAnswersQuestion(rr, q); - if (answered) - AnswerLocalQuestionWithLocalAuthRecord(m, rr, AddRecord); // MUST NOT dereference q again - if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now - m->CurrentQuestion = q->next; - } - - m->CurrentQuestion = mDNSNULL; - - // If this AuthRecord is marked LocalOnly or P2P, then we want to deliver it to all local 'mDNSInterface_Any' questions - if (rr->ARType == AuthRecordLocalOnly || rr->ARType == AuthRecordP2P) - AnswerInterfaceAnyQuestionsWithLocalAuthRecord(m, rr, AddRecord); - - } +{ + if (m->CurrentQuestion) + LogMsg("AnswerAllLocalQuestionsWithLocalAuthRecord ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + + m->CurrentQuestion = m->LocalOnlyQuestions; + while (m->CurrentQuestion && m->CurrentQuestion != m->NewLocalOnlyQuestions) + { + mDNSBool answered; + DNSQuestion *q = m->CurrentQuestion; + // We are called with both LocalOnly/P2P record or a regular AuthRecord + if (RRAny(rr)) + answered = ResourceRecordAnswersQuestion(&rr->resrec, q); + else + answered = LocalOnlyRecordAnswersQuestion(rr, q); + if (answered) + AnswerLocalQuestionWithLocalAuthRecord(m, rr, AddRecord); // MUST NOT dereference q again + if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now + m->CurrentQuestion = q->next; + } + + m->CurrentQuestion = mDNSNULL; + + // If this AuthRecord is marked LocalOnly or P2P, then we want to deliver it to all local 'mDNSInterface_Any' questions + if (rr->ARType == AuthRecordLocalOnly || rr->ARType == AuthRecordP2P) + AnswerInterfaceAnyQuestionsWithLocalAuthRecord(m, rr, AddRecord); + +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -559,14 +577,14 @@ mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRec #define RRTypeIsAddressType(T) ((T) == kDNSType_A || (T) == kDNSType_AAAA) -#define ResourceRecordIsValidAnswer(RR) ( ((RR)-> resrec.RecordType & kDNSRecordTypeActiveMask) && \ - ((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->resrec.RecordType & kDNSRecordTypeActiveMask)) && \ - ((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->resrec.RecordType & kDNSRecordTypeActiveMask)) && \ - ((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->resrec.RecordType & kDNSRecordTypeActiveMask)) ) +#define ResourceRecordIsValidAnswer(RR) ( ((RR)->resrec.RecordType & kDNSRecordTypeActiveMask) && \ + ((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->resrec.RecordType & kDNSRecordTypeActiveMask)) && \ + ((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->resrec.RecordType & kDNSRecordTypeActiveMask)) && \ + ((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->resrec.RecordType & kDNSRecordTypeActiveMask)) ) #define ResourceRecordIsValidInterfaceAnswer(RR, INTID) \ - (ResourceRecordIsValidAnswer(RR) && \ - ((RR)->resrec.InterfaceID == mDNSInterface_Any || (RR)->resrec.InterfaceID == (INTID))) + (ResourceRecordIsValidAnswer(RR) && \ + ((RR)->resrec.InterfaceID == mDNSInterface_Any || (RR)->resrec.InterfaceID == (INTID))) #define DefaultProbeCountForTypeUnique ((mDNSu8)3) #define DefaultProbeCountForRecordType(X) ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0) @@ -590,9 +608,9 @@ mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRec #define DefaultAnnounceIntervalForTypeShared (mDNSPlatformOneSecond/2) #define DefaultAnnounceIntervalForTypeUnique (mDNSPlatformOneSecond/2) -#define DefaultAPIntervalForRecordType(X) ((X) & kDNSRecordTypeActiveSharedMask ? DefaultAnnounceIntervalForTypeShared : \ - (X) & kDNSRecordTypeUnique ? DefaultProbeIntervalForTypeUnique : \ - (X) & kDNSRecordTypeActiveUniqueMask ? DefaultAnnounceIntervalForTypeUnique : 0) +#define DefaultAPIntervalForRecordType(X) ((X) &kDNSRecordTypeActiveSharedMask ? DefaultAnnounceIntervalForTypeShared : \ + (X) &kDNSRecordTypeUnique ? DefaultProbeIntervalForTypeUnique : \ + (X) &kDNSRecordTypeActiveUniqueMask ? DefaultAnnounceIntervalForTypeUnique : 0) #define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && (time) - ((RR)->LastAPTime + (RR)->ThisAPInterval) >= 0) #define TimeToSendThisRecord(RR,time) ((TimeToAnnounceThisRecord(RR,time) || (RR)->ImmedAnswer) && ResourceRecordIsValidAnswer(RR)) @@ -613,17 +631,17 @@ mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRec #define SameResourceRecordSignature(A,B) (A)->resrec.rrtype == (B)->resrec.rrtype && SameResourceRecordNameClassInterface((A),(B)) mDNSlocal mDNSBool SameResourceRecordNameClassInterface(const AuthRecord *const r1, const AuthRecord *const r2) - { - if (!r1) { LogMsg("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse); } - if (!r2) { LogMsg("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse); } - if (r1->resrec.InterfaceID && - r2->resrec.InterfaceID && - r1->resrec.InterfaceID != r2->resrec.InterfaceID) return(mDNSfalse); - return(mDNSBool)( - r1->resrec.rrclass == r2->resrec.rrclass && - r1->resrec.namehash == r2->resrec.namehash && - SameDomainName(r1->resrec.name, r2->resrec.name)); - } +{ + if (!r1) { LogMsg("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse); } + if (!r2) { LogMsg("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse); } + if (r1->resrec.InterfaceID && + r2->resrec.InterfaceID && + r1->resrec.InterfaceID != r2->resrec.InterfaceID) return(mDNSfalse); + return (mDNSBool)( + r1->resrec.rrclass == r2->resrec.rrclass && + r1->resrec.namehash == r2->resrec.namehash && + SameDomainName(r1->resrec.name, r2->resrec.name)); +} // 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 @@ -634,19 +652,19 @@ mDNSlocal mDNSBool SameResourceRecordNameClassInterface(const AuthRecord *const // For now, to make it easier to avoid false conflicts, we treat SPS Proxy records like shared records, // and require the rrtypes to match for the rdata to be considered potentially conflicting mDNSlocal mDNSBool PacketRRMatchesSignature(const CacheRecord *const pktrr, const AuthRecord *const authrr) - { - if (!pktrr) { LogMsg("PacketRRMatchesSignature ERROR: pktrr is NULL"); return(mDNSfalse); } - if (!authrr) { LogMsg("PacketRRMatchesSignature ERROR: authrr is NULL"); return(mDNSfalse); } - if (pktrr->resrec.InterfaceID && - authrr->resrec.InterfaceID && - pktrr->resrec.InterfaceID != authrr->resrec.InterfaceID) return(mDNSfalse); - if (!(authrr->resrec.RecordType & kDNSRecordTypeUniqueMask) || authrr->WakeUp.HMAC.l[0]) - if (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)); - } +{ + if (!pktrr) { LogMsg("PacketRRMatchesSignature ERROR: pktrr is NULL"); return(mDNSfalse); } + if (!authrr) { LogMsg("PacketRRMatchesSignature ERROR: authrr is NULL"); return(mDNSfalse); } + if (pktrr->resrec.InterfaceID && + authrr->resrec.InterfaceID && + pktrr->resrec.InterfaceID != authrr->resrec.InterfaceID) return(mDNSfalse); + if (!(authrr->resrec.RecordType & kDNSRecordTypeUniqueMask) || authrr->WakeUp.HMAC.l[0]) + if (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)); +} // CacheRecord *ka is the CacheRecord from the known answer list in the query. // This is the information that the requester believes to be correct. @@ -656,469 +674,469 @@ mDNSlocal mDNSBool PacketRRMatchesSignature(const CacheRecord *const pktrr, cons // (either the record is non-specific, or it is specific to this interface) // so now we just need to check the name, type, class, rdata and TTL. mDNSlocal mDNSBool ShouldSuppressKnownAnswer(const CacheRecord *const ka, const AuthRecord *const rr) - { - // If RR signature is different, or data is different, then don't suppress our answer - if (!IdenticalResourceRecord(&ka->resrec, &rr->resrec)) return(mDNSfalse); - - // If the requester's indicated TTL is less than half the real TTL, - // we need to give our answer before the requester's copy expires. - // If the requester's indicated TTL is at least half the real TTL, - // then we can suppress our answer this time. - // If the requester's indicated TTL is greater than the TTL we believe, - // then that's okay, and we don't need to do anything about it. - // (If two responders on the network are offering the same information, - // that's okay, and if they are offering the information with different TTLs, - // the one offering the lower TTL should defer to the one offering the higher TTL.) - return(mDNSBool)(ka->resrec.rroriginalttl >= rr->resrec.rroriginalttl / 2); - } +{ + // If RR signature is different, or data is different, then don't suppress our answer + if (!IdenticalResourceRecord(&ka->resrec, &rr->resrec)) return(mDNSfalse); + + // If the requester's indicated TTL is less than half the real TTL, + // we need to give our answer before the requester's copy expires. + // If the requester's indicated TTL is at least half the real TTL, + // then we can suppress our answer this time. + // If the requester's indicated TTL is greater than the TTL we believe, + // then that's okay, and we don't need to do anything about it. + // (If two responders on the network are offering the same information, + // that's okay, and if they are offering the information with different TTLs, + // the one offering the lower TTL should defer to the one offering the higher TTL.) + return (mDNSBool)(ka->resrec.rroriginalttl >= rr->resrec.rroriginalttl / 2); +} mDNSlocal void SetNextAnnounceProbeTime(mDNS *const m, const AuthRecord *const rr) - { - if (rr->resrec.RecordType == kDNSRecordTypeUnique) - { - if ((rr->LastAPTime + rr->ThisAPInterval) - m->timenow > mDNSPlatformOneSecond * 10) - { - LogMsg("SetNextAnnounceProbeTime: ProbeCount %d Next in %d %s", rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr)); - LogMsg("SetNextAnnounceProbeTime: m->SuppressProbes %d m->timenow %d diff %d", m->SuppressProbes, m->timenow, m->SuppressProbes - m->timenow); - } - if (m->NextScheduledProbe - (rr->LastAPTime + rr->ThisAPInterval) >= 0) - m->NextScheduledProbe = (rr->LastAPTime + rr->ThisAPInterval); - // Some defensive code: - // If (rr->LastAPTime + rr->ThisAPInterval) happens to be far in the past, we don't want to allow - // NextScheduledProbe to be set excessively in the past, because that can cause bad things to happen. - // See: mDNS: Sometimes advertising stops working and record interval is set to zero - if (m->NextScheduledProbe - m->timenow < 0) - m->NextScheduledProbe = m->timenow; - } - else if (rr->AnnounceCount && (ResourceRecordIsValidAnswer(rr) || rr->resrec.RecordType == kDNSRecordTypeDeregistering)) - { - if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0) - m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval); - } - } +{ + if (rr->resrec.RecordType == kDNSRecordTypeUnique) + { + if ((rr->LastAPTime + rr->ThisAPInterval) - m->timenow > mDNSPlatformOneSecond * 10) + { + LogMsg("SetNextAnnounceProbeTime: ProbeCount %d Next in %d %s", rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr)); + LogMsg("SetNextAnnounceProbeTime: m->SuppressProbes %d m->timenow %d diff %d", m->SuppressProbes, m->timenow, m->SuppressProbes - m->timenow); + } + if (m->NextScheduledProbe - (rr->LastAPTime + rr->ThisAPInterval) >= 0) + m->NextScheduledProbe = (rr->LastAPTime + rr->ThisAPInterval); + // Some defensive code: + // If (rr->LastAPTime + rr->ThisAPInterval) happens to be far in the past, we don't want to allow + // NextScheduledProbe to be set excessively in the past, because that can cause bad things to happen. + // See: mDNS: Sometimes advertising stops working and record interval is set to zero + if (m->NextScheduledProbe - m->timenow < 0) + m->NextScheduledProbe = m->timenow; + } + else if (rr->AnnounceCount && (ResourceRecordIsValidAnswer(rr) || rr->resrec.RecordType == kDNSRecordTypeDeregistering)) + { + if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0) + m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval); + } +} mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr) - { - // For reverse-mapping Sleep Proxy PTR records, probe interval is one second - rr->ThisAPInterval = rr->AddressProxy.type ? mDNSPlatformOneSecond : DefaultAPIntervalForRecordType(rr->resrec.RecordType); - - // * If this is a record type that's going to probe, then we use the m->SuppressProbes time. - // * Otherwise, if it's not going to probe, but m->SuppressProbes is set because we have other - // records that are going to probe, then we delay its first announcement so that it will - // go out synchronized with the first announcement for the other records that *are* probing. - // This is a minor performance tweak that helps keep groups of related records synchronized together. - // The addition of "interval / 2" is to make sure that, in the event that any of the probes are - // delayed by a few milliseconds, this announcement does not inadvertently go out *before* the probing is complete. - // When the probing is complete and those records begin to announce, these records will also be picked up and accelerated, - // because they will meet the criterion of being at least half-way to their scheduled announcement time. - // * If it's not going to probe and m->SuppressProbes is not already set then we should announce immediately. - - if (rr->ProbeCount) - { - // 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) - { - // To allow us to aggregate probes when a group of services are registered together, - // the first probe is delayed 1/4 second. This means the common-case behaviour is: - // 1/4 second wait; probe - // 1/4 second wait; probe - // 1/4 second wait; probe - // 1/4 second wait; announce (i.e. service is normally announced exactly one second after being registered) - m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique/2 + mDNSRandom(DefaultProbeIntervalForTypeUnique/2)); - - // 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 = NonZeroTime(m->NextScheduledProbe); - if (m->SuppressProbes - m->timenow < 0) // Make sure we don't set m->SuppressProbes excessively in the past - m->SuppressProbes = m->timenow; - - // If we already have a *query* scheduled to go out sooner, then use that time to get better aggregation - if (m->SuppressProbes - m->NextScheduledQuery >= 0) - m->SuppressProbes = NonZeroTime(m->NextScheduledQuery); - if (m->SuppressProbes - m->timenow < 0) // Make sure we don't set m->SuppressProbes excessively in the past - m->SuppressProbes = m->timenow; - - // except... don't expect to be able to send before the m->SuppressSending timer fires - if (m->SuppressSending && m->SuppressProbes - m->SuppressSending < 0) - m->SuppressProbes = NonZeroTime(m->SuppressSending); - - if (m->SuppressProbes - m->timenow > mDNSPlatformOneSecond * 8) - { - LogMsg("InitializeLastAPTime ERROR m->SuppressProbes %d m->NextScheduledProbe %d m->NextScheduledQuery %d m->SuppressSending %d %d", - m->SuppressProbes - m->timenow, - m->NextScheduledProbe - m->timenow, - m->NextScheduledQuery - m->timenow, - m->SuppressSending, - m->SuppressSending - m->timenow); - m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique/2 + mDNSRandom(DefaultProbeIntervalForTypeUnique/2)); - } - } - rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval; - } - else if (m->SuppressProbes && m->SuppressProbes - m->timenow >= 0) - rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval + DefaultProbeIntervalForTypeUnique * DefaultProbeCountForTypeUnique + rr->ThisAPInterval / 2; - else - rr->LastAPTime = m->timenow - rr->ThisAPInterval; - - // For reverse-mapping Sleep Proxy PTR records we don't want to start probing instantly -- we - // wait one second to give the client a chance to go to sleep, and then start our ARP/NDP probing. - // After three probes one second apart with no answer, we conclude the client is now sleeping - // and we can begin broadcasting our announcements to take over ownership of that IP address. - // If we don't wait for the client to go to sleep, then when the client sees our ARP Announcements there's a risk - // (depending on the OS and networking stack it's using) that it might interpret it as a conflict and change its IP address. - if (rr->AddressProxy.type) rr->LastAPTime = m->timenow; - - // Unsolicited Neighbor Advertisements (RFC 2461 Section 7.2.6) give us fast address cache updating, - // but some older IPv6 clients get confused by them, so for now we don't send them. Without Unsolicited - // Neighbor Advertisements we have to rely on Neighbor Unreachability Detection instead, which is slower. - // Given this, we'll do our best to wake for existing IPv6 connections, but we don't want to encourage - // new ones for sleeping clients, so we'll we send deletions for our SPS clients' AAAA records. - if (m->KnownBugs & mDNS_KnownBug_LimitedIPv6) - if (rr->WakeUp.HMAC.l[0] && rr->resrec.rrtype == kDNSType_AAAA) - rr->LastAPTime = m->timenow - rr->ThisAPInterval + mDNSPlatformOneSecond * 10; - - // Set LastMCTime to now, to inhibit multicast responses - // (no need to send additional multicast responses when we're announcing anyway) - rr->LastMCTime = m->timenow; - rr->LastMCInterface = mDNSInterfaceMark; - - SetNextAnnounceProbeTime(m, rr); - } +{ + // For reverse-mapping Sleep Proxy PTR records, probe interval is one second + rr->ThisAPInterval = rr->AddressProxy.type ? mDNSPlatformOneSecond : DefaultAPIntervalForRecordType(rr->resrec.RecordType); + + // * If this is a record type that's going to probe, then we use the m->SuppressProbes time. + // * Otherwise, if it's not going to probe, but m->SuppressProbes is set because we have other + // records that are going to probe, then we delay its first announcement so that it will + // go out synchronized with the first announcement for the other records that *are* probing. + // This is a minor performance tweak that helps keep groups of related records synchronized together. + // The addition of "interval / 2" is to make sure that, in the event that any of the probes are + // delayed by a few milliseconds, this announcement does not inadvertently go out *before* the probing is complete. + // When the probing is complete and those records begin to announce, these records will also be picked up and accelerated, + // because they will meet the criterion of being at least half-way to their scheduled announcement time. + // * If it's not going to probe and m->SuppressProbes is not already set then we should announce immediately. + + if (rr->ProbeCount) + { + // 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) + { + // To allow us to aggregate probes when a group of services are registered together, + // the first probe is delayed 1/4 second. This means the common-case behaviour is: + // 1/4 second wait; probe + // 1/4 second wait; probe + // 1/4 second wait; probe + // 1/4 second wait; announce (i.e. service is normally announced exactly one second after being registered) + m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique/2 + mDNSRandom(DefaultProbeIntervalForTypeUnique/2)); + + // 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 = NonZeroTime(m->NextScheduledProbe); + if (m->SuppressProbes - m->timenow < 0) // Make sure we don't set m->SuppressProbes excessively in the past + m->SuppressProbes = m->timenow; + + // If we already have a *query* scheduled to go out sooner, then use that time to get better aggregation + if (m->SuppressProbes - m->NextScheduledQuery >= 0) + m->SuppressProbes = NonZeroTime(m->NextScheduledQuery); + if (m->SuppressProbes - m->timenow < 0) // Make sure we don't set m->SuppressProbes excessively in the past + m->SuppressProbes = m->timenow; + + // except... don't expect to be able to send before the m->SuppressSending timer fires + if (m->SuppressSending && m->SuppressProbes - m->SuppressSending < 0) + m->SuppressProbes = NonZeroTime(m->SuppressSending); + + if (m->SuppressProbes - m->timenow > mDNSPlatformOneSecond * 8) + { + LogMsg("InitializeLastAPTime ERROR m->SuppressProbes %d m->NextScheduledProbe %d m->NextScheduledQuery %d m->SuppressSending %d %d", + m->SuppressProbes - m->timenow, + m->NextScheduledProbe - m->timenow, + m->NextScheduledQuery - m->timenow, + m->SuppressSending, + m->SuppressSending - m->timenow); + m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique/2 + mDNSRandom(DefaultProbeIntervalForTypeUnique/2)); + } + } + rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval; + } + else if (m->SuppressProbes && m->SuppressProbes - m->timenow >= 0) + rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval + DefaultProbeIntervalForTypeUnique * DefaultProbeCountForTypeUnique + rr->ThisAPInterval / 2; + else + rr->LastAPTime = m->timenow - rr->ThisAPInterval; + + // For reverse-mapping Sleep Proxy PTR records we don't want to start probing instantly -- we + // wait one second to give the client a chance to go to sleep, and then start our ARP/NDP probing. + // After three probes one second apart with no answer, we conclude the client is now sleeping + // and we can begin broadcasting our announcements to take over ownership of that IP address. + // If we don't wait for the client to go to sleep, then when the client sees our ARP Announcements there's a risk + // (depending on the OS and networking stack it's using) that it might interpret it as a conflict and change its IP address. + if (rr->AddressProxy.type) rr->LastAPTime = m->timenow; + + // Unsolicited Neighbor Advertisements (RFC 2461 Section 7.2.6) give us fast address cache updating, + // but some older IPv6 clients get confused by them, so for now we don't send them. Without Unsolicited + // Neighbor Advertisements we have to rely on Neighbor Unreachability Detection instead, which is slower. + // Given this, we'll do our best to wake for existing IPv6 connections, but we don't want to encourage + // new ones for sleeping clients, so we'll we send deletions for our SPS clients' AAAA records. + if (m->KnownBugs & mDNS_KnownBug_LimitedIPv6) + if (rr->WakeUp.HMAC.l[0] && rr->resrec.rrtype == kDNSType_AAAA) + rr->LastAPTime = m->timenow - rr->ThisAPInterval + mDNSPlatformOneSecond * 10; + + // Set LastMCTime to now, to inhibit multicast responses + // (no need to send additional multicast responses when we're announcing anyway) + rr->LastMCTime = m->timenow; + rr->LastMCInterface = mDNSInterfaceMark; + + SetNextAnnounceProbeTime(m, rr); +} mDNSlocal const domainname *SetUnicastTargetToHostName(mDNS *const m, AuthRecord *rr) - { - const domainname *target; - if (rr->AutoTarget) - { - // For autotunnel services pointing at our IPv6 ULA we don't need or want a NAT mapping, but for all other - // advertised services referencing our uDNS hostname, we want NAT mappings automatically created as appropriate, - // with the port number in our advertised SRV record automatically tracking the external mapped port. - DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name); - if (!AuthInfo || !AuthInfo->AutoTunnel) rr->AutoTarget = Target_AutoHostAndNATMAP; - } - - target = GetServiceTarget(m, rr); - if (!target || target->c[0] == 0) - { - // defer registration until we've got a target - LogInfo("SetUnicastTargetToHostName No target for %s", ARDisplayString(m, rr)); - rr->state = regState_NoTarget; - return mDNSNULL; - } - else - { - LogInfo("SetUnicastTargetToHostName target %##s for resource record %s", target->c, ARDisplayString(m,rr)); - return target; - } - } +{ + const domainname *target; + if (rr->AutoTarget) + { + // For autotunnel services pointing at our IPv6 ULA we don't need or want a NAT mapping, but for all other + // advertised services referencing our uDNS hostname, we want NAT mappings automatically created as appropriate, + // with the port number in our advertised SRV record automatically tracking the external mapped port. + DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name); + if (!AuthInfo || !AuthInfo->AutoTunnel) rr->AutoTarget = Target_AutoHostAndNATMAP; + } + + target = GetServiceTarget(m, rr); + if (!target || target->c[0] == 0) + { + // defer registration until we've got a target + LogInfo("SetUnicastTargetToHostName No target for %s", ARDisplayString(m, rr)); + rr->state = regState_NoTarget; + return mDNSNULL; + } + else + { + LogInfo("SetUnicastTargetToHostName target %##s for resource record %s", target->c, ARDisplayString(m,rr)); + return target; + } +} // Right now this only applies to mDNS (.local) services where the target host is always m->MulticastHostname // Eventually we should unify this with GetServiceTarget() in uDNS.c mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr) - { - domainname *const target = GetRRDomainNameTarget(&rr->resrec); - const domainname *newname = &m->MulticastHostname; - - if (!target) LogInfo("SetTargetToHostName: Don't know how to set the target of rrtype %s", DNSTypeName(rr->resrec.rrtype)); - - if (!(rr->ForceMCast || rr->ARType == AuthRecordLocalOnly || rr->ARType == AuthRecordP2P || IsLocalDomain(&rr->namestorage))) - { - const domainname *const n = SetUnicastTargetToHostName(m, rr); - if (n) newname = n; - else { target->c[0] = 0; SetNewRData(&rr->resrec, mDNSNULL, 0); return; } - } - - if (target && SameDomainName(target, newname)) - debugf("SetTargetToHostName: Target of %##s is already %##s", rr->resrec.name->c, target->c); - - if (target && !SameDomainName(target, newname)) - { - AssignDomainName(target, newname); - SetNewRData(&rr->resrec, mDNSNULL, 0); // Update rdlength, rdestimate, rdatahash - - // If we're in the middle of probing this record, we need to start again, - // because changing its rdata may change the outcome of the tie-breaker. - // (If the record type is kDNSRecordTypeUnique (unconfirmed unique) then DefaultProbeCountForRecordType is non-zero.) - rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); - - // If we've announced this record, we really should send a goodbye packet for the old rdata before - // changing to the new rdata. However, in practice, we only do SetTargetToHostName for unique records, - // so when we announce them we'll set the kDNSClass_UniqueRRSet and clear any stale data that way. - if (rr->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)); - - rr->AnnounceCount = InitialAnnounceCount; - rr->RequireGoodbye = mDNSfalse; - InitializeLastAPTime(m, rr); - } - } +{ + domainname *const target = GetRRDomainNameTarget(&rr->resrec); + const domainname *newname = &m->MulticastHostname; + + if (!target) LogInfo("SetTargetToHostName: Don't know how to set the target of rrtype %s", DNSTypeName(rr->resrec.rrtype)); + + if (!(rr->ForceMCast || rr->ARType == AuthRecordLocalOnly || rr->ARType == AuthRecordP2P || IsLocalDomain(&rr->namestorage))) + { + const domainname *const n = SetUnicastTargetToHostName(m, rr); + if (n) newname = n; + else { target->c[0] = 0; SetNewRData(&rr->resrec, mDNSNULL, 0); return; } + } + + if (target && SameDomainName(target, newname)) + debugf("SetTargetToHostName: Target of %##s is already %##s", rr->resrec.name->c, target->c); + + if (target && !SameDomainName(target, newname)) + { + AssignDomainName(target, newname); + SetNewRData(&rr->resrec, mDNSNULL, 0); // Update rdlength, rdestimate, rdatahash + + // If we're in the middle of probing this record, we need to start again, + // because changing its rdata may change the outcome of the tie-breaker. + // (If the record type is kDNSRecordTypeUnique (unconfirmed unique) then DefaultProbeCountForRecordType is non-zero.) + rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); + + // If we've announced this record, we really should send a goodbye packet for the old rdata before + // changing to the new rdata. However, in practice, we only do SetTargetToHostName for unique records, + // so when we announce them we'll set the kDNSClass_UniqueRRSet and clear any stale data that way. + if (rr->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)); + + rr->AnnounceCount = InitialAnnounceCount; + rr->RequireGoodbye = mDNSfalse; + InitializeLastAPTime(m, rr); + } +} mDNSlocal void AcknowledgeRecord(mDNS *const m, AuthRecord *const rr) - { - if (rr->RecordCallback) - { - // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function - // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. - rr->Acknowledged = mDNStrue; - mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback - rr->RecordCallback(m, rr, mStatus_NoError); - mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again - } - } +{ + if (rr->RecordCallback) + { + // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function + // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. + rr->Acknowledged = mDNStrue; + mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback + rr->RecordCallback(m, rr, mStatus_NoError); + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + } +} mDNSexport void ActivateUnicastRegistration(mDNS *const m, AuthRecord *const rr) - { - // Make sure that we don't activate the SRV record and associated service records, if it is in - // NoTarget state. First time when a service is being instantiated, SRV record may be in NoTarget state. - // We should not activate any of the other reords (PTR, TXT) that are part of the service. When - // the target becomes available, the records will be reregistered. - if (rr->resrec.rrtype != kDNSType_SRV) - { - AuthRecord *srvRR = mDNSNULL; - if (rr->resrec.rrtype == kDNSType_PTR) - srvRR = rr->Additional1; - else if (rr->resrec.rrtype == kDNSType_TXT) - srvRR = rr->DependentOn; - if (srvRR) - { - if (srvRR->resrec.rrtype != kDNSType_SRV) - { - LogMsg("ActivateUnicastRegistration: ERROR!! Resource record %s wrong, expecting SRV type", ARDisplayString(m, srvRR)); - } - else - { - LogInfo("ActivateUnicastRegistration: Found Service Record %s in state %d for %##s (%s)", - ARDisplayString(m, srvRR), srvRR->state, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); - rr->state = srvRR->state; - } - } - } - - if (rr->state == regState_NoTarget) - { - LogInfo("ActivateUnicastRegistration record %s in regState_NoTarget, not activating", ARDisplayString(m, rr)); - return; - } - // When we wake up from sleep, we call ActivateUnicastRegistration. It is possible that just before we went to sleep, - // the service/record was being deregistered. In that case, we should not try to register again. For the cases where - // the records are deregistered due to e.g., no target for the SRV record, we would have returned from above if it - // was already in NoTarget state. If it was in the process of deregistration but did not complete fully before we went - // to sleep, then it is okay to start in Pending state as we will go back to NoTarget state if we don't have a target. - if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) - { - LogInfo("ActivateUnicastRegistration: Resource record %s, current state %d, moving to DeregPending", ARDisplayString(m, rr), rr->state); - rr->state = regState_DeregPending; - } - else - { - LogInfo("ActivateUnicastRegistration: Resource record %s, current state %d, moving to Pending", ARDisplayString(m, rr), rr->state); - rr->state = regState_Pending; - } - rr->ProbeCount = 0; - rr->AnnounceCount = 0; - rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; - rr->LastAPTime = m->timenow - rr->ThisAPInterval; - rr->expire = 0; // Forget about all the leases, start fresh - rr->uselease = mDNStrue; - rr->updateid = zeroID; - rr->SRVChanged = mDNSfalse; - rr->updateError = mStatus_NoError; - // RestartRecordGetZoneData calls this function whenever a new interface gets registered with core. - // The records might already be registered with the server and hence could have NAT state. - if (rr->NATinfo.clientContext) - { - mDNS_StopNATOperation_internal(m, &rr->NATinfo); - rr->NATinfo.clientContext = mDNSNULL; - } - if (rr->nta) { CancelGetZoneData(m, rr->nta); rr->nta = mDNSNULL; } - if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } - if (m->NextuDNSEvent - (rr->LastAPTime + rr->ThisAPInterval) >= 0) - m->NextuDNSEvent = (rr->LastAPTime + rr->ThisAPInterval); - } +{ + // Make sure that we don't activate the SRV record and associated service records, if it is in + // NoTarget state. First time when a service is being instantiated, SRV record may be in NoTarget state. + // We should not activate any of the other reords (PTR, TXT) that are part of the service. When + // the target becomes available, the records will be reregistered. + if (rr->resrec.rrtype != kDNSType_SRV) + { + AuthRecord *srvRR = mDNSNULL; + if (rr->resrec.rrtype == kDNSType_PTR) + srvRR = rr->Additional1; + else if (rr->resrec.rrtype == kDNSType_TXT) + srvRR = rr->DependentOn; + if (srvRR) + { + if (srvRR->resrec.rrtype != kDNSType_SRV) + { + LogMsg("ActivateUnicastRegistration: ERROR!! Resource record %s wrong, expecting SRV type", ARDisplayString(m, srvRR)); + } + else + { + LogInfo("ActivateUnicastRegistration: Found Service Record %s in state %d for %##s (%s)", + ARDisplayString(m, srvRR), srvRR->state, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + rr->state = srvRR->state; + } + } + } + + if (rr->state == regState_NoTarget) + { + LogInfo("ActivateUnicastRegistration record %s in regState_NoTarget, not activating", ARDisplayString(m, rr)); + return; + } + // When we wake up from sleep, we call ActivateUnicastRegistration. It is possible that just before we went to sleep, + // the service/record was being deregistered. In that case, we should not try to register again. For the cases where + // the records are deregistered due to e.g., no target for the SRV record, we would have returned from above if it + // was already in NoTarget state. If it was in the process of deregistration but did not complete fully before we went + // to sleep, then it is okay to start in Pending state as we will go back to NoTarget state if we don't have a target. + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) + { + LogInfo("ActivateUnicastRegistration: Resource record %s, current state %d, moving to DeregPending", ARDisplayString(m, rr), rr->state); + rr->state = regState_DeregPending; + } + else + { + LogInfo("ActivateUnicastRegistration: Resource record %s, current state %d, moving to Pending", ARDisplayString(m, rr), rr->state); + rr->state = regState_Pending; + } + rr->ProbeCount = 0; + rr->AnnounceCount = 0; + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + rr->LastAPTime = m->timenow - rr->ThisAPInterval; + rr->expire = 0; // Forget about all the leases, start fresh + rr->uselease = mDNStrue; + rr->updateid = zeroID; + rr->SRVChanged = mDNSfalse; + rr->updateError = mStatus_NoError; + // RestartRecordGetZoneData calls this function whenever a new interface gets registered with core. + // The records might already be registered with the server and hence could have NAT state. + if (rr->NATinfo.clientContext) + { + mDNS_StopNATOperation_internal(m, &rr->NATinfo); + rr->NATinfo.clientContext = mDNSNULL; + } + if (rr->nta) { CancelGetZoneData(m, rr->nta); rr->nta = mDNSNULL; } + if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } + if (m->NextuDNSEvent - (rr->LastAPTime + rr->ThisAPInterval) >= 0) + m->NextuDNSEvent = (rr->LastAPTime + rr->ThisAPInterval); +} // Two records qualify to be local duplicates if: // (a) the RecordTypes are the same, or // (b) one is Unique and the other Verified // (c) either is in the process of deregistering #define RecordLDT(A,B) ((A)->resrec.RecordType == (B)->resrec.RecordType || \ - ((A)->resrec.RecordType | (B)->resrec.RecordType) == (kDNSRecordTypeUnique | kDNSRecordTypeVerified) || \ - ((A)->resrec.RecordType == kDNSRecordTypeDeregistering || (B)->resrec.RecordType == kDNSRecordTypeDeregistering)) + ((A)->resrec.RecordType | (B)->resrec.RecordType) == (kDNSRecordTypeUnique | kDNSRecordTypeVerified) || \ + ((A)->resrec.RecordType == kDNSRecordTypeDeregistering || (B)->resrec.RecordType == kDNSRecordTypeDeregistering)) #define RecordIsLocalDuplicate(A,B) \ - ((A)->resrec.InterfaceID == (B)->resrec.InterfaceID && RecordLDT((A),(B)) && IdenticalResourceRecord(&(A)->resrec, &(B)->resrec)) + ((A)->resrec.InterfaceID == (B)->resrec.InterfaceID && RecordLDT((A),(B)) && IdenticalResourceRecord(& (A)->resrec, & (B)->resrec)) mDNSlocal AuthRecord *CheckAuthIdenticalRecord(AuthHash *r, AuthRecord *rr) - { - AuthGroup *a; - AuthGroup **ag = &a; - AuthRecord **rp; - const mDNSu32 slot = AuthHashSlot(rr->resrec.name); - - a = AuthGroupForRecord(r, slot, &rr->resrec); - if (!a) return mDNSNULL; - rp = &(*ag)->members; - while (*rp) - { - if (!RecordIsLocalDuplicate(*rp, rr)) - rp=&(*rp)->next; - else - { - if ((*rp)->resrec.RecordType == kDNSRecordTypeDeregistering) - { - (*rp)->AnnounceCount = 0; - rp=&(*rp)->next; - } - else return *rp; - } - } - return (mDNSNULL); - } +{ + AuthGroup *a; + AuthGroup **ag = &a; + AuthRecord **rp; + const mDNSu32 slot = AuthHashSlot(rr->resrec.name); + + a = AuthGroupForRecord(r, slot, &rr->resrec); + if (!a) return mDNSNULL; + rp = &(*ag)->members; + while (*rp) + { + if (!RecordIsLocalDuplicate(*rp, rr)) + rp=&(*rp)->next; + else + { + if ((*rp)->resrec.RecordType == kDNSRecordTypeDeregistering) + { + (*rp)->AnnounceCount = 0; + rp=&(*rp)->next; + } + else return *rp; + } + } + return (mDNSNULL); +} mDNSlocal mDNSBool CheckAuthRecordConflict(AuthHash *r, AuthRecord *rr) - { - AuthGroup *a; - AuthGroup **ag = &a; - AuthRecord **rp; - const mDNSu32 slot = AuthHashSlot(rr->resrec.name); - - a = AuthGroupForRecord(r, slot, &rr->resrec); - if (!a) return mDNSfalse; - rp = &(*ag)->members; - while (*rp) - { - const AuthRecord *s1 = rr->RRSet ? rr->RRSet : rr; - const AuthRecord *s2 = (*rp)->RRSet ? (*rp)->RRSet : *rp; - if (s1 != s2 && SameResourceRecordSignature((*rp), rr) && !IdenticalSameNameRecord(&(*rp)->resrec, &rr->resrec)) - return mDNStrue; - else - rp=&(*rp)->next; - } - return (mDNSfalse); - } +{ + AuthGroup *a; + AuthGroup **ag = &a; + AuthRecord **rp; + const mDNSu32 slot = AuthHashSlot(rr->resrec.name); + + a = AuthGroupForRecord(r, slot, &rr->resrec); + if (!a) return mDNSfalse; + rp = &(*ag)->members; + while (*rp) + { + const AuthRecord *s1 = rr->RRSet ? rr->RRSet : rr; + const AuthRecord *s2 = (*rp)->RRSet ? (*rp)->RRSet : *rp; + if (s1 != s2 && SameResourceRecordSignature((*rp), rr) && !IdenticalSameNameRecord(&(*rp)->resrec, &rr->resrec)) + return mDNStrue; + else + rp=&(*rp)->next; + } + return (mDNSfalse); +} // checks to see if "rr" is already present mDNSlocal AuthRecord *CheckAuthSameRecord(AuthHash *r, AuthRecord *rr) - { - AuthGroup *a; - AuthGroup **ag = &a; - AuthRecord **rp; - const mDNSu32 slot = AuthHashSlot(rr->resrec.name); - - a = AuthGroupForRecord(r, slot, &rr->resrec); - if (!a) return mDNSNULL; - rp = &(*ag)->members; - while (*rp) - { - if (*rp != rr) - rp=&(*rp)->next; - else - { - return *rp; - } - } - return (mDNSNULL); - } +{ + AuthGroup *a; + AuthGroup **ag = &a; + AuthRecord **rp; + const mDNSu32 slot = AuthHashSlot(rr->resrec.name); + + a = AuthGroupForRecord(r, slot, &rr->resrec); + if (!a) return mDNSNULL; + rp = &(*ag)->members; + while (*rp) + { + if (*rp != rr) + rp=&(*rp)->next; + else + { + return *rp; + } + } + return (mDNSNULL); +} // Exported so uDNS.c can call this mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) - { - domainname *target = GetRRDomainNameTarget(&rr->resrec); - AuthRecord *r; - AuthRecord **p = &m->ResourceRecords; - AuthRecord **d = &m->DuplicateRecords; - - if ((mDNSs32)rr->resrec.rroriginalttl <= 0) - { LogMsg("mDNS_Register_internal: TTL %X should be 1 - 0x7FFFFFFF %s", rr->resrec.rroriginalttl, ARDisplayString(m, rr)); return(mStatus_BadParamErr); } - - if (!rr->resrec.RecordType) - { LogMsg("mDNS_Register_internal: RecordType must be non-zero %s", ARDisplayString(m, rr)); return(mStatus_BadParamErr); } - - if (m->ShutdownTime) - { LogMsg("mDNS_Register_internal: Shutting down, can't register %s", ARDisplayString(m, rr)); return(mStatus_ServiceNotRunning); } - - if (m->DivertMulticastAdvertisements && !AuthRecord_uDNS(rr)) - { - mDNSInterfaceID previousID = rr->resrec.InterfaceID; - if (rr->resrec.InterfaceID == mDNSInterface_Any || rr->resrec.InterfaceID == mDNSInterface_P2P) - { - rr->resrec.InterfaceID = mDNSInterface_LocalOnly; - rr->ARType = AuthRecordLocalOnly; - } - if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly) - { - NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID); - if (intf && !intf->Advertise){ rr->resrec.InterfaceID = mDNSInterface_LocalOnly; rr->ARType = AuthRecordLocalOnly; } - } - if (rr->resrec.InterfaceID != previousID) - LogInfo("mDNS_Register_internal: Diverting record to local-only %s", ARDisplayString(m, rr)); - } - - if (RRLocalOnly(rr)) - { - if (CheckAuthSameRecord(&m->rrauth, rr)) - { - LogMsg("mDNS_Register_internal: ERROR!! Tried to register LocalOnly AuthRecord %p %##s (%s) that's already in the list", - rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); - return(mStatus_AlreadyRegistered); - } - } - else - { - while (*p && *p != rr) p=&(*p)->next; - if (*p) - { - LogMsg("mDNS_Register_internal: ERROR!! Tried to register AuthRecord %p %##s (%s) that's already in the list", - rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); - return(mStatus_AlreadyRegistered); - } - } - - while (*d && *d != rr) d=&(*d)->next; - if (*d) - { - LogMsg("mDNS_Register_internal: ERROR!! Tried to register AuthRecord %p %##s (%s) that's already in the Duplicate list", - rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); - return(mStatus_AlreadyRegistered); - } - - if (rr->DependentOn) - { - if (rr->resrec.RecordType == kDNSRecordTypeUnique) - rr->resrec.RecordType = kDNSRecordTypeVerified; - else - { - LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn && RecordType != kDNSRecordTypeUnique", - rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); - return(mStatus_Invalid); - } - if (!(rr->DependentOn->resrec.RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique))) - { - LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn->RecordType bad type %X", - rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->DependentOn->resrec.RecordType); - return(mStatus_Invalid); - } - } - - // If this resource record is referencing a specific interface, make sure it exists. - // Skip checks for LocalOnly and P2P as they are not valid InterfaceIDs. Also, for scoped - // entries in /etc/hosts skip that check as that interface may not be valid at this time. - if (rr->resrec.InterfaceID && rr->ARType != AuthRecordLocalOnly && rr->ARType != AuthRecordP2P) - { - NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID); - if (!intf) - { - debugf("mDNS_Register_internal: Bogus InterfaceID %p in resource record", rr->resrec.InterfaceID); - return(mStatus_BadReferenceErr); - } - } - - rr->next = mDNSNULL; - - // Field Group 1: The actual information pertaining to this resource record - // Set up by client prior to call - - // Field Group 2: Persistent metadata for Authoritative Records +{ + domainname *target = GetRRDomainNameTarget(&rr->resrec); + AuthRecord *r; + AuthRecord **p = &m->ResourceRecords; + AuthRecord **d = &m->DuplicateRecords; + + if ((mDNSs32)rr->resrec.rroriginalttl <= 0) + { LogMsg("mDNS_Register_internal: TTL %X should be 1 - 0x7FFFFFFF %s", rr->resrec.rroriginalttl, ARDisplayString(m, rr)); return(mStatus_BadParamErr); } + + if (!rr->resrec.RecordType) + { LogMsg("mDNS_Register_internal: RecordType must be non-zero %s", ARDisplayString(m, rr)); return(mStatus_BadParamErr); } + + if (m->ShutdownTime) + { LogMsg("mDNS_Register_internal: Shutting down, can't register %s", ARDisplayString(m, rr)); return(mStatus_ServiceNotRunning); } + + if (m->DivertMulticastAdvertisements && !AuthRecord_uDNS(rr)) + { + mDNSInterfaceID previousID = rr->resrec.InterfaceID; + if (rr->resrec.InterfaceID == mDNSInterface_Any || rr->resrec.InterfaceID == mDNSInterface_P2P) + { + rr->resrec.InterfaceID = mDNSInterface_LocalOnly; + rr->ARType = AuthRecordLocalOnly; + } + if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly) + { + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID); + if (intf && !intf->Advertise) { rr->resrec.InterfaceID = mDNSInterface_LocalOnly; rr->ARType = AuthRecordLocalOnly; } + } + if (rr->resrec.InterfaceID != previousID) + LogInfo("mDNS_Register_internal: Diverting record to local-only %s", ARDisplayString(m, rr)); + } + + if (RRLocalOnly(rr)) + { + if (CheckAuthSameRecord(&m->rrauth, rr)) + { + LogMsg("mDNS_Register_internal: ERROR!! Tried to register LocalOnly AuthRecord %p %##s (%s) that's already in the list", + rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + return(mStatus_AlreadyRegistered); + } + } + else + { + while (*p && *p != rr) p=&(*p)->next; + if (*p) + { + LogMsg("mDNS_Register_internal: ERROR!! Tried to register AuthRecord %p %##s (%s) that's already in the list", + rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + return(mStatus_AlreadyRegistered); + } + } + + while (*d && *d != rr) d=&(*d)->next; + if (*d) + { + LogMsg("mDNS_Register_internal: ERROR!! Tried to register AuthRecord %p %##s (%s) that's already in the Duplicate list", + rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + return(mStatus_AlreadyRegistered); + } + + if (rr->DependentOn) + { + if (rr->resrec.RecordType == kDNSRecordTypeUnique) + rr->resrec.RecordType = kDNSRecordTypeVerified; + else + { + LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn && RecordType != kDNSRecordTypeUnique", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + return(mStatus_Invalid); + } + if (!(rr->DependentOn->resrec.RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique))) + { + LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn->RecordType bad type %X", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->DependentOn->resrec.RecordType); + return(mStatus_Invalid); + } + } + + // If this resource record is referencing a specific interface, make sure it exists. + // Skip checks for LocalOnly and P2P as they are not valid InterfaceIDs. Also, for scoped + // entries in /etc/hosts skip that check as that interface may not be valid at this time. + if (rr->resrec.InterfaceID && rr->ARType != AuthRecordLocalOnly && rr->ARType != AuthRecordP2P) + { + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID); + if (!intf) + { + debugf("mDNS_Register_internal: Bogus InterfaceID %p in resource record", rr->resrec.InterfaceID); + return(mStatus_BadReferenceErr); + } + } + + rr->next = mDNSNULL; + + // Field Group 1: The actual information pertaining to this resource record + // Set up by client prior to call + + // Field Group 2: Persistent metadata for Authoritative Records // rr->Additional1 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client // rr->Additional2 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client // rr->DependentOn = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client @@ -1128,60 +1146,61 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) // 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->AutoTarget && target) target->c[0] = 0; - - // Field Group 3: Transient state for Authoritative Records - rr->Acknowledged = mDNSfalse; - rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); - rr->AnnounceCount = InitialAnnounceCount; - rr->RequireGoodbye = mDNSfalse; - rr->AnsweredLocalQ = mDNSfalse; - rr->IncludeInProbe = mDNSfalse; - rr->ImmedUnicast = mDNSfalse; - rr->SendNSECNow = mDNSNULL; - rr->ImmedAnswer = mDNSNULL; - rr->ImmedAdditional = mDNSNULL; - rr->SendRNow = mDNSNULL; - rr->v4Requester = zerov4Addr; - rr->v6Requester = zerov6Addr; - rr->NextResponse = mDNSNULL; - rr->NR_AnswerTo = mDNSNULL; - rr->NR_AdditionalTo = mDNSNULL; - if (!rr->AutoTarget) InitializeLastAPTime(m, rr); + // Make sure target is not uninitialized data, or we may crash writing debugging log messages + if (rr->AutoTarget && target) target->c[0] = 0; + + // Field Group 3: Transient state for Authoritative Records + rr->Acknowledged = mDNSfalse; + rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); + rr->AnnounceCount = InitialAnnounceCount; + rr->RequireGoodbye = mDNSfalse; + rr->AnsweredLocalQ = mDNSfalse; + rr->IncludeInProbe = mDNSfalse; + rr->ImmedUnicast = mDNSfalse; + rr->SendNSECNow = mDNSNULL; + rr->ImmedAnswer = mDNSNULL; + rr->ImmedAdditional = mDNSNULL; + rr->SendRNow = mDNSNULL; + rr->v4Requester = zerov4Addr; + rr->v6Requester = zerov6Addr; + rr->NextResponse = mDNSNULL; + rr->NR_AnswerTo = mDNSNULL; + rr->NR_AdditionalTo = mDNSNULL; + if (!rr->AutoTarget) InitializeLastAPTime(m, rr); // rr->LastAPTime = Set for us in InitializeLastAPTime() // rr->LastMCTime = Set for us in InitializeLastAPTime() // rr->LastMCInterface = Set for us in InitializeLastAPTime() - rr->NewRData = mDNSNULL; - rr->newrdlength = 0; - rr->UpdateCallback = mDNSNULL; - rr->UpdateCredits = kMaxUpdateCredits; - rr->NextUpdateCredit = 0; - rr->UpdateBlocked = 0; - - // For records we're holding as proxy (except reverse-mapping PTR records) two announcements is sufficient - if (rr->WakeUp.HMAC.l[0] && !rr->AddressProxy.type) rr->AnnounceCount = 2; - - // Field Group 4: Transient uDNS state for Authoritative Records - rr->state = regState_Zero; - rr->uselease = 0; - rr->expire = 0; - rr->Private = 0; - rr->updateid = zeroID; - rr->zone = rr->resrec.name; - rr->nta = mDNSNULL; - rr->tcp = mDNSNULL; - rr->OrigRData = 0; - rr->OrigRDLen = 0; - rr->InFlightRData = 0; - rr->InFlightRDLen = 0; - rr->QueuedRData = 0; - rr->QueuedRDLen = 0; - //mDNSPlatformMemZero(&rr->NATinfo, sizeof(rr->NATinfo)); - // We should be recording the actual internal port for this service record here. Once we initiate our NAT mapping - // request we'll subsequently overwrite srv.port with the allocated external NAT port -- potentially multiple - // times with different values if the external NAT port changes during the lifetime of the service registration. - //if (rr->resrec.rrtype == kDNSType_SRV) rr->NATinfo.IntPort = rr->resrec.rdata->u.srv.port; + rr->NewRData = mDNSNULL; + rr->newrdlength = 0; + rr->UpdateCallback = mDNSNULL; + rr->UpdateCredits = kMaxUpdateCredits; + rr->NextUpdateCredit = 0; + rr->UpdateBlocked = 0; + + // For records we're holding as proxy (except reverse-mapping PTR records) two announcements is sufficient + if (rr->WakeUp.HMAC.l[0] && !rr->AddressProxy.type) rr->AnnounceCount = 2; + + // Field Group 4: Transient uDNS state for Authoritative Records + rr->state = regState_Zero; + rr->uselease = 0; + rr->expire = 0; + rr->Private = 0; + rr->updateid = zeroID; + rr->updateIntID = zeroOpaque64; + rr->zone = rr->resrec.name; + rr->nta = mDNSNULL; + rr->tcp = mDNSNULL; + rr->OrigRData = 0; + rr->OrigRDLen = 0; + rr->InFlightRData = 0; + rr->InFlightRDLen = 0; + rr->QueuedRData = 0; + rr->QueuedRDLen = 0; + //mDNSPlatformMemZero(&rr->NATinfo, sizeof(rr->NATinfo)); + // We should be recording the actual internal port for this service record here. Once we initiate our NAT mapping + // request we'll subsequently overwrite srv.port with the allocated external NAT port -- potentially multiple + // times with different values if the external NAT port changes during the lifetime of the service registration. + //if (rr->resrec.rrtype == kDNSType_SRV) rr->NATinfo.IntPort = rr->resrec.rdata->u.srv.port; // rr->resrec.interface = already set in mDNS_SetupResourceRecord // rr->resrec.name->c = MUST be set by client @@ -1190,411 +1209,431 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) // rr->resrec.rroriginalttl = already set in mDNS_SetupResourceRecord // rr->resrec.rdata = MUST be set by client, unless record type is CNAME or PTR and rr->HostTarget is set - // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct, - // since RFC 1035 specifies a TXT record as "One or more s", not "Zero or more s". - // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here. - if (rr->resrec.rrtype == kDNSType_TXT && rr->resrec.rdlength == 0) { rr->resrec.rdlength = 1; rr->resrec.rdata->u.txt.c[0] = 0; } + // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct, + // since RFC 1035 specifies a TXT record as "One or more s", not "Zero or more s". + // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here. + if (rr->resrec.rrtype == kDNSType_TXT && rr->resrec.rdlength == 0) { rr->resrec.rdlength = 1; rr->resrec.rdata->u.txt.c[0] = 0; } - if (rr->AutoTarget) - { - SetTargetToHostName(m, rr); // Also sets rdlength and rdestimate for us, and calls InitializeLastAPTime(); + if (rr->AutoTarget) + { + SetTargetToHostName(m, rr); // Also sets rdlength and rdestimate for us, and calls InitializeLastAPTime(); #ifndef UNICAST_DISABLED - // If we have no target record yet, SetTargetToHostName will set rr->state == regState_NoTarget - // In this case we leave the record half-formed in the list, and later we'll remove it from the list and re-add it properly. - if (rr->state == regState_NoTarget) - { - // Initialize the target so that we don't crash while logging etc. - domainname *tar = GetRRDomainNameTarget(&rr->resrec); - if (tar) tar->c[0] = 0; - LogInfo("mDNS_Register_internal: record %s in NoTarget state", ARDisplayString(m, rr)); - } + // If we have no target record yet, SetTargetToHostName will set rr->state == regState_NoTarget + // In this case we leave the record half-formed in the list, and later we'll remove it from the list and re-add it properly. + if (rr->state == regState_NoTarget) + { + // Initialize the target so that we don't crash while logging etc. + domainname *tar = GetRRDomainNameTarget(&rr->resrec); + if (tar) tar->c[0] = 0; + LogInfo("mDNS_Register_internal: record %s in NoTarget state", ARDisplayString(m, rr)); + } #endif - } - else - { - rr->resrec.rdlength = GetRDLength(&rr->resrec, mDNSfalse); - rr->resrec.rdestimate = GetRDLength(&rr->resrec, mDNStrue); - } - - if (!ValidateDomainName(rr->resrec.name)) - { LogMsg("Attempt to register record with invalid name: %s", 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", ARDisplayString(m, rr)); return(mStatus_Invalid); } - - rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); - rr->resrec.rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(&rr->resrec); - - if (RRLocalOnly(rr)) - { - // If this is supposed to be unique, make sure we don't have any name conflicts. - // If we found a conflict, we may still want to insert the record in the list but mark it appropriately - // (kDNSRecordTypeDeregistering) so that we deliver RMV events to the application. But this causes more - // complications and not clear whether there are any benefits. See rdar:9304275 for details. - // Hence, just bail out. - if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) - { - if (CheckAuthRecordConflict(&m->rrauth, rr)) - { - LogInfo("mDNS_Register_internal: Name conflict %s (%p), InterfaceID %p", ARDisplayString(m, rr), rr, rr->resrec.InterfaceID); - return mStatus_NameConflict; - } - } - } - - // For uDNS records, we don't support duplicate checks at this time. + } + else + { + rr->resrec.rdlength = GetRDLength(&rr->resrec, mDNSfalse); + rr->resrec.rdestimate = GetRDLength(&rr->resrec, mDNStrue); + } + + if (!ValidateDomainName(rr->resrec.name)) + { LogMsg("Attempt to register record with invalid name: %s", 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", ARDisplayString(m, rr)); return(mStatus_Invalid); } + + rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); + rr->resrec.rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(&rr->resrec); + + if (RRLocalOnly(rr)) + { + // If this is supposed to be unique, make sure we don't have any name conflicts. + // If we found a conflict, we may still want to insert the record in the list but mark it appropriately + // (kDNSRecordTypeDeregistering) so that we deliver RMV events to the application. But this causes more + // complications and not clear whether there are any benefits. See rdar:9304275 for details. + // Hence, just bail out. + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + { + if (CheckAuthRecordConflict(&m->rrauth, rr)) + { + LogInfo("mDNS_Register_internal: Name conflict %s (%p), InterfaceID %p", ARDisplayString(m, rr), rr, rr->resrec.InterfaceID); + return mStatus_NameConflict; + } + } + } + + // For uDNS records, we don't support duplicate checks at this time. #ifndef UNICAST_DISABLED - if (AuthRecord_uDNS(rr)) - { - if (!m->NewLocalRecords) m->NewLocalRecords = rr; - // When we called SetTargetToHostName, it may have caused mDNS_Register_internal to be re-entered, appending new - // records to the list, so we now need to update p to advance to the new end to the list before appending our new record. - // Note that for AutoTunnel this should never happen, but this check makes the code future-proof. - while (*p) p=&(*p)->next; - *p = rr; - if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified; - rr->ProbeCount = 0; - rr->AnnounceCount = 0; - if (rr->state != regState_NoTarget) ActivateUnicastRegistration(m, rr); - return(mStatus_NoError); // <--- Note: For unicast records, code currently bails out at this point - } + if (AuthRecord_uDNS(rr)) + { + if (!m->NewLocalRecords) m->NewLocalRecords = rr; + // When we called SetTargetToHostName, it may have caused mDNS_Register_internal to be re-entered, appending new + // records to the list, so we now need to update p to advance to the new end to the list before appending our new record. + // Note that for AutoTunnel this should never happen, but this check makes the code future-proof. + while (*p) p=&(*p)->next; + *p = rr; + if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified; + rr->ProbeCount = 0; + rr->AnnounceCount = 0; + if (rr->state != regState_NoTarget) ActivateUnicastRegistration(m, rr); + return(mStatus_NoError); // <--- Note: For unicast records, code currently bails out at this point + } #endif - // Now that we've finished building our new record, make sure it's not identical to one we already have - if (RRLocalOnly(rr)) - { - rr->ProbeCount = 0; - rr->AnnounceCount = 0; - r = CheckAuthIdenticalRecord(&m->rrauth, rr); - } - else - { - for (r = m->ResourceRecords; r; r=r->next) - if (RecordIsLocalDuplicate(r, rr)) - { - if (r->resrec.RecordType == kDNSRecordTypeDeregistering) r->AnnounceCount = 0; - else break; - } - } - - if (r) - { - debugf("mDNS_Register_internal:Adding to duplicate list %s", ARDisplayString(m,rr)); - *d = rr; - // If the previous copy of this record is already verified unique, - // then indicate that we should move this record promptly to kDNSRecordTypeUnique state. - // Setting ProbeCount to zero will cause SendQueries() to advance this record to - // kDNSRecordTypeVerified state and call the client callback at the next appropriate time. - if (rr->resrec.RecordType == kDNSRecordTypeUnique && r->resrec.RecordType == kDNSRecordTypeVerified) - rr->ProbeCount = 0; - } - else - { - debugf("mDNS_Register_internal: Adding to active record list %s", ARDisplayString(m,rr)); - if (RRLocalOnly(rr)) - { - AuthGroup *ag; - ag = InsertAuthRecord(m, &m->rrauth, rr); - if (ag && !ag->NewLocalOnlyRecords) { - m->NewLocalOnlyRecords = mDNStrue; - ag->NewLocalOnlyRecords = rr; - } - // No probing for LocalOnly records, Acknowledge them right away - if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified; - AcknowledgeRecord(m, rr); - return(mStatus_NoError); - } - else - { - if (!m->NewLocalRecords) m->NewLocalRecords = rr; - *p = rr; - } - } - - if (!AuthRecord_uDNS(rr)) // This check is superfluous, given that for unicast records we (currently) bail out above - { - // For records that are not going to probe, acknowledge them right away - if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering) - AcknowledgeRecord(m, rr); - - // Adding a record may affect whether or not we should sleep - mDNS_UpdateAllowSleep(m); - } - - return(mStatus_NoError); - } + // Now that we've finished building our new record, make sure it's not identical to one we already have + if (RRLocalOnly(rr)) + { + rr->ProbeCount = 0; + rr->AnnounceCount = 0; + r = CheckAuthIdenticalRecord(&m->rrauth, rr); + } + else + { + for (r = m->ResourceRecords; r; r=r->next) + if (RecordIsLocalDuplicate(r, rr)) + { + if (r->resrec.RecordType == kDNSRecordTypeDeregistering) r->AnnounceCount = 0; + else break; + } + } + + if (r) + { + debugf("mDNS_Register_internal:Adding to duplicate list %s", ARDisplayString(m,rr)); + *d = rr; + // If the previous copy of this record is already verified unique, + // then indicate that we should move this record promptly to kDNSRecordTypeUnique state. + // Setting ProbeCount to zero will cause SendQueries() to advance this record to + // kDNSRecordTypeVerified state and call the client callback at the next appropriate time. + if (rr->resrec.RecordType == kDNSRecordTypeUnique && r->resrec.RecordType == kDNSRecordTypeVerified) + rr->ProbeCount = 0; + } + else + { + debugf("mDNS_Register_internal: Adding to active record list %s", ARDisplayString(m,rr)); + if (RRLocalOnly(rr)) + { + AuthGroup *ag; + ag = InsertAuthRecord(m, &m->rrauth, rr); + if (ag && !ag->NewLocalOnlyRecords) { + m->NewLocalOnlyRecords = mDNStrue; + ag->NewLocalOnlyRecords = rr; + } + // No probing for LocalOnly records, Acknowledge them right away + if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified; + AcknowledgeRecord(m, rr); + return(mStatus_NoError); + } + else + { + if (!m->NewLocalRecords) m->NewLocalRecords = rr; + *p = rr; + } + } + + if (!AuthRecord_uDNS(rr)) // This check is superfluous, given that for unicast records we (currently) bail out above + { + // For records that are not going to probe, acknowledge them right away + if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering) + AcknowledgeRecord(m, rr); + + // Adding a record may affect whether or not we should sleep + mDNS_UpdateAllowSleep(m); + } + + return(mStatus_NoError); +} mDNSlocal void RecordProbeFailure(mDNS *const m, const AuthRecord *const rr) - { - m->ProbeFailTime = m->timenow; - m->NumFailedProbes++; - // 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 = 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)); - } - } +{ + m->ProbeFailTime = m->timenow; + m->NumFailedProbes++; + // 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 = 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)); + } +} mDNSlocal void CompleteRDataUpdate(mDNS *const m, AuthRecord *const rr) - { - RData *OldRData = rr->resrec.rdata; - mDNSu16 OldRDLen = rr->resrec.rdlength; - SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); // Update our rdata - rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... - if (rr->UpdateCallback) - rr->UpdateCallback(m, rr, OldRData, OldRDLen); // ... and let the client know - } +{ + RData *OldRData = rr->resrec.rdata; + mDNSu16 OldRDLen = rr->resrec.rdlength; + SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); // Update our rdata + rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... + if (rr->UpdateCallback) + rr->UpdateCallback(m, rr, OldRData, OldRDLen); // ... and let the client know +} // Note: mDNS_Deregister_internal can call a user callback, which may change the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. // Exported so uDNS.c can call this mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, mDNS_Dereg_type drt) - { - AuthRecord *r2; - mDNSu8 RecordType = rr->resrec.RecordType; - AuthRecord **p = &m->ResourceRecords; // Find this record in our list of active records - mDNSBool dupList = mDNSfalse; - - if (RRLocalOnly(rr)) - { - AuthGroup *a; - AuthGroup **ag = &a; - AuthRecord **rp; - const mDNSu32 slot = AuthHashSlot(rr->resrec.name); - - a = AuthGroupForRecord(&m->rrauth, slot, &rr->resrec); - if (!a) return mDNSfalse; - rp = &(*ag)->members; - while (*rp && *rp != rr) rp=&(*rp)->next; - p = rp; - } - else - { - while (*p && *p != rr) p=&(*p)->next; - } - - if (*p) - { - // We found our record on the main list. See if there are any duplicates that need special handling. - if (drt == mDNS_Dereg_conflict) // If this was a conflict, see that all duplicates get the same treatment - { - // Scan for duplicates of rr, and mark them for deregistration at the end of this routine, after we've finished - // deregistering rr. We need to do this scan *before* we give the client the chance to free and reuse the rr memory. - for (r2 = m->DuplicateRecords; r2; r2=r2->next) if (RecordIsLocalDuplicate(r2, rr)) r2->ProbeCount = 0xFF; - } - else - { - // Before we delete the record (and potentially send a goodbye packet) - // first see if we have a record on the duplicate list ready to take over from it. - AuthRecord **d = &m->DuplicateRecords; - while (*d && !RecordIsLocalDuplicate(*d, rr)) d=&(*d)->next; - if (*d) - { - AuthRecord *dup = *d; - debugf("mDNS_Register_internal: Duplicate record %p taking over from %p %##s (%s)", - dup, rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); - *d = dup->next; // Cut replacement record from DuplicateRecords list - if (RRLocalOnly(rr)) - { - dup->next = mDNSNULL; - if (!InsertAuthRecord(m, &m->rrauth, dup)) LogMsg("mDNS_Deregister_internal: ERROR!! cannot insert %s", ARDisplayString(m, dup)); - } - else - { - 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->RequireGoodbye = rr->RequireGoodbye; - dup->AnsweredLocalQ = rr->AnsweredLocalQ; - 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->LastAPTime = rr->LastAPTime; - dup->LastMCTime = rr->LastMCTime; - dup->LastMCInterface = rr->LastMCInterface; - dup->Private = rr->Private; - dup->state = rr->state; - rr->RequireGoodbye = mDNSfalse; - rr->AnsweredLocalQ = mDNSfalse; - } - } - } - else - { - // We didn't find our record on the main list; try the DuplicateRecords list instead. - p = &m->DuplicateRecords; - while (*p && *p != rr) p=&(*p)->next; - // If we found our record on the duplicate list, then make sure we don't send a goodbye for it - if (*p) { rr->RequireGoodbye = mDNSfalse; dupList = mDNStrue; } - if (*p) debugf("mDNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)", - rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); - } - - if (!*p) - { - // No need to log an error message if we already know this is a potentially repeated deregistration - if (drt != mDNS_Dereg_repeat) - LogMsg("mDNS_Deregister_internal: Record %p not found in list %s", rr, ARDisplayString(m,rr)); - 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 this is a record (including mDNSInterface_LocalOnly records) for which we've given local-only answers then - // it's tempting to just do "AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse)" here, but that would not not be safe. - // The AnswerAllLocalQuestionsWithLocalAuthRecord routine walks the question list invoking client callbacks, using the "m->CurrentQuestion" - // mechanism to cope with the client callback modifying the question list while that's happening. - // However, mDNS_Deregister could have been called from a client callback (e.g. from the domain enumeration callback FoundDomain) - // which means that the "m->CurrentQuestion" mechanism is already in use to protect that list, so we can't use it twice. - // More generally, if we invoke callbacks from within a client callback, then those callbacks could deregister other - // records, thereby invoking yet more callbacks, without limit. - // The solution is to defer delivering the "Remove" events until mDNS_Execute time, just like we do for sending - // actual goodbye packets. - +{ + AuthRecord *r2; + mDNSu8 RecordType = rr->resrec.RecordType; + AuthRecord **p = &m->ResourceRecords; // Find this record in our list of active records + mDNSBool dupList = mDNSfalse; + + if (RRLocalOnly(rr)) + { + AuthGroup *a; + AuthGroup **ag = &a; + AuthRecord **rp; + const mDNSu32 slot = AuthHashSlot(rr->resrec.name); + + a = AuthGroupForRecord(&m->rrauth, slot, &rr->resrec); + if (!a) return mDNSfalse; + rp = &(*ag)->members; + while (*rp && *rp != rr) rp=&(*rp)->next; + p = rp; + } + else + { + while (*p && *p != rr) p=&(*p)->next; + } + + if (*p) + { + // We found our record on the main list. See if there are any duplicates that need special handling. + if (drt == mDNS_Dereg_conflict) // If this was a conflict, see that all duplicates get the same treatment + { + // Scan for duplicates of rr, and mark them for deregistration at the end of this routine, after we've finished + // deregistering rr. We need to do this scan *before* we give the client the chance to free and reuse the rr memory. + for (r2 = m->DuplicateRecords; r2; r2=r2->next) if (RecordIsLocalDuplicate(r2, rr)) r2->ProbeCount = 0xFF; + } + else + { + // Before we delete the record (and potentially send a goodbye packet) + // first see if we have a record on the duplicate list ready to take over from it. + AuthRecord **d = &m->DuplicateRecords; + while (*d && !RecordIsLocalDuplicate(*d, rr)) d=&(*d)->next; + if (*d) + { + AuthRecord *dup = *d; + debugf("mDNS_Register_internal: Duplicate record %p taking over from %p %##s (%s)", + dup, rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + *d = dup->next; // Cut replacement record from DuplicateRecords list + if (RRLocalOnly(rr)) + { + dup->next = mDNSNULL; + if (!InsertAuthRecord(m, &m->rrauth, dup)) LogMsg("mDNS_Deregister_internal: ERROR!! cannot insert %s", ARDisplayString(m, dup)); + } + else + { + 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->RequireGoodbye = rr->RequireGoodbye; + dup->AnsweredLocalQ = rr->AnsweredLocalQ; + 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->LastAPTime = rr->LastAPTime; + dup->LastMCTime = rr->LastMCTime; + dup->LastMCInterface = rr->LastMCInterface; + dup->Private = rr->Private; + dup->state = rr->state; + rr->RequireGoodbye = mDNSfalse; + rr->AnsweredLocalQ = mDNSfalse; + } + } + } + else + { + // We didn't find our record on the main list; try the DuplicateRecords list instead. + p = &m->DuplicateRecords; + while (*p && *p != rr) p=&(*p)->next; + // If we found our record on the duplicate list, then make sure we don't send a goodbye for it + if (*p) + { + // Duplicate records are not used for sending wakeups or goodbyes. Hence, deregister them + // immediately. When there is a conflict, we deregister all the conflicting duplicate records + // also that have been marked above in this function. In that case, we come here and if we don't + // deregister (unilink from the DuplicateRecords list), we will be recursing infinitely. Hence, + // clear the HMAC which will cause it to deregister. See for + // details. + rr->WakeUp.HMAC = zeroEthAddr; + rr->RequireGoodbye = mDNSfalse; + dupList = mDNStrue; + } + if (*p) debugf("mDNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)", + rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + } + + if (!*p) + { + // No need to log an error message if we already know this is a potentially repeated deregistration + if (drt != mDNS_Dereg_repeat) + LogMsg("mDNS_Deregister_internal: Record %p not found in list %s", rr, ARDisplayString(m,rr)); + 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 this is a record (including mDNSInterface_LocalOnly records) for which we've given local-only answers then + // it's tempting to just do "AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse)" here, but that would not not be safe. + // The AnswerAllLocalQuestionsWithLocalAuthRecord routine walks the question list invoking client callbacks, using the "m->CurrentQuestion" + // mechanism to cope with the client callback modifying the question list while that's happening. + // However, mDNS_Deregister could have been called from a client callback (e.g. from the domain enumeration callback FoundDomain) + // which means that the "m->CurrentQuestion" mechanism is already in use to protect that list, so we can't use it twice. + // More generally, if we invoke callbacks from within a client callback, then those callbacks could deregister other + // records, thereby invoking yet more callbacks, without limit. + // The solution is to defer delivering the "Remove" events until mDNS_Execute time, just like we do for sending + // actual goodbye packets. + #ifndef UNICAST_DISABLED - if (AuthRecord_uDNS(rr)) - { - if (rr->RequireGoodbye) - { - if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } - rr->resrec.RecordType = kDNSRecordTypeDeregistering; - m->LocalRemoveEvents = mDNStrue; - uDNS_DeregisterRecord(m, rr); - // At this point unconditionally we bail out - // Either uDNS_DeregisterRecord will have completed synchronously, and called CompleteDeregistration, - // which calls us back here with RequireGoodbye set to false, or it will have initiated the deregistration - // process and will complete asynchronously. Either way we don't need to do anything more here. - return(mStatus_NoError); - } - // Sometimes the records don't complete proper deregistration i.e., don't wait for a response - // from the server. In that case, if the records have been part of a group update, clear the - // state here. Some recors e.g., AutoTunnel gets reused without ever being completely initialized - rr->updateid = zeroID; - - // We defer cleaning up NAT state only after sending goodbyes. This is important because - // RecordRegistrationGotZoneData guards against creating NAT state if clientContext is non-NULL. - // This happens today when we turn on/off interface where we get multiple network transitions - // and RestartRecordGetZoneData triggers re-registration of the resource records even though - // they may be in Registered state which causes NAT information to be setup multiple times. Defering - // the cleanup here keeps clientContext non-NULL and hence prevents that. Note that cleaning up - // NAT state here takes care of the case where we did not send goodbyes at all. - if (rr->NATinfo.clientContext) - { - mDNS_StopNATOperation_internal(m, &rr->NATinfo); - rr->NATinfo.clientContext = mDNSNULL; - } - if (rr->nta) { CancelGetZoneData(m, rr->nta); rr->nta = mDNSNULL; } - if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } - } + if (AuthRecord_uDNS(rr)) + { + if (rr->RequireGoodbye) + { + if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } + rr->resrec.RecordType = kDNSRecordTypeDeregistering; + m->LocalRemoveEvents = mDNStrue; + uDNS_DeregisterRecord(m, rr); + // At this point unconditionally we bail out + // Either uDNS_DeregisterRecord will have completed synchronously, and called CompleteDeregistration, + // which calls us back here with RequireGoodbye set to false, or it will have initiated the deregistration + // process and will complete asynchronously. Either way we don't need to do anything more here. + return(mStatus_NoError); + } + // Sometimes the records don't complete proper deregistration i.e., don't wait for a response + // from the server. In that case, if the records have been part of a group update, clear the + // state here. Some recors e.g., AutoTunnel gets reused without ever being completely initialized + rr->updateid = zeroID; + + // We defer cleaning up NAT state only after sending goodbyes. This is important because + // RecordRegistrationGotZoneData guards against creating NAT state if clientContext is non-NULL. + // This happens today when we turn on/off interface where we get multiple network transitions + // and RestartRecordGetZoneData triggers re-registration of the resource records even though + // they may be in Registered state which causes NAT information to be setup multiple times. Defering + // the cleanup here keeps clientContext non-NULL and hence prevents that. Note that cleaning up + // NAT state here takes care of the case where we did not send goodbyes at all. + if (rr->NATinfo.clientContext) + { + mDNS_StopNATOperation_internal(m, &rr->NATinfo); + rr->NATinfo.clientContext = mDNSNULL; + } + if (rr->nta) { CancelGetZoneData(m, rr->nta); rr->nta = mDNSNULL; } + if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } + } #endif // UNICAST_DISABLED - if (RecordType == kDNSRecordTypeUnregistered) - LogMsg("mDNS_Deregister_internal: %s already marked kDNSRecordTypeUnregistered", ARDisplayString(m, rr)); - else if (RecordType == kDNSRecordTypeDeregistering) - { - LogMsg("mDNS_Deregister_internal: %s already marked kDNSRecordTypeDeregistering", ARDisplayString(m, rr)); - return(mStatus_BadReferenceErr); - } - - // Local-only questions don't get remove events for unique records - // We may want to consider changing this code so that we generate local-only question "rmv" - // events (and maybe goodbye packets too) for unique records as well as for shared records - // Note: If we change the logic for this "if" statement, need to ensure that the code in - // CompleteDeregistration() sets the appropriate state variables to gaurantee that "else" - // clause will execute here and the record will be cut from the list. - if (rr->WakeUp.HMAC.l[0] || - (RecordType == kDNSRecordTypeShared && (rr->RequireGoodbye || rr->AnsweredLocalQ))) - { - verbosedebugf("mDNS_Deregister_internal: Starting deregistration for %s", ARDisplayString(m, rr)); - rr->resrec.RecordType = kDNSRecordTypeDeregistering; - rr->resrec.rroriginalttl = 0; - rr->AnnounceCount = rr->WakeUp.HMAC.l[0] ? WakeupCount : (drt == mDNS_Dereg_rapid) ? 1 : GoodbyeCount; - rr->ThisAPInterval = mDNSPlatformOneSecond * 2; - rr->LastAPTime = m->timenow - rr->ThisAPInterval; - m->LocalRemoveEvents = mDNStrue; - if (m->NextScheduledResponse - (m->timenow + mDNSPlatformOneSecond/10) >= 0) - m->NextScheduledResponse = (m->timenow + mDNSPlatformOneSecond/10); - } - else - { - if (!dupList && RRLocalOnly(rr)) - { - AuthGroup *ag = RemoveAuthRecord(m, &m->rrauth, rr); - if (ag->NewLocalOnlyRecords == rr) ag->NewLocalOnlyRecords = rr->next; - } - else - { - *p = rr->next; // Cut this record from the list - if (m->NewLocalRecords == rr) m->NewLocalRecords = rr->next; - } - // If someone is about to look at this, bump the pointer forward - if (m->CurrentRecord == rr) m->CurrentRecord = rr->next; - rr->next = mDNSNULL; - - // Should we generate local remove events here? - // i.e. something like: - // if (rr->AnsweredLocalQ) { AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse); rr->AnsweredLocalQ = mDNSfalse; } - - verbosedebugf("mDNS_Deregister_internal: Deleting record for %s", ARDisplayString(m, rr)); - rr->resrec.RecordType = kDNSRecordTypeUnregistered; - - if ((drt == mDNS_Dereg_conflict || drt == mDNS_Dereg_repeat) && RecordType == kDNSRecordTypeShared) - debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)", - rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); - - // If we have an update queued up which never executed, give the client a chance to free that memory - if (rr->NewRData) CompleteRDataUpdate(m, rr); // Update our rdata, clear the NewRData pointer, and return memory to the client - - - // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function - // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. - // In this case the likely client action to the mStatus_MemFree message is to free the memory, - // so any attempt to touch rr after this is likely to lead to a crash. - if (drt != mDNS_Dereg_conflict) - { - mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback - LogInfo("mDNS_Deregister_internal: mStatus_MemFree for %s", ARDisplayString(m, rr)); - if (rr->RecordCallback) - rr->RecordCallback(m, rr, mStatus_MemFree); // MUST NOT touch rr after this - mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again - } - else - { - RecordProbeFailure(m, rr); - mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback - if (rr->RecordCallback) - rr->RecordCallback(m, rr, mStatus_NameConflict); // MUST NOT touch rr after this - mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again - // Now that we've finished deregistering rr, check our DuplicateRecords list for any that we marked previously. - // Note that with all the client callbacks going on, by the time we get here all the - // records we marked may have been explicitly deregistered by the client anyway. - r2 = m->DuplicateRecords; - while (r2) - { - if (r2->ProbeCount != 0xFF) r2 = r2->next; - else { mDNS_Deregister_internal(m, r2, mDNS_Dereg_conflict); r2 = m->DuplicateRecords; } - } - } - } - mDNS_UpdateAllowSleep(m); - return(mStatus_NoError); - } + if (RecordType == kDNSRecordTypeUnregistered) + LogMsg("mDNS_Deregister_internal: %s already marked kDNSRecordTypeUnregistered", ARDisplayString(m, rr)); + else if (RecordType == kDNSRecordTypeDeregistering) + { + LogMsg("mDNS_Deregister_internal: %s already marked kDNSRecordTypeDeregistering", ARDisplayString(m, rr)); + return(mStatus_BadReferenceErr); + } + + // Local-only questions don't get remove events for unique records + // We may want to consider changing this code so that we generate local-only question "rmv" + // events (and maybe goodbye packets too) for unique records as well as for shared records + // Note: If we change the logic for this "if" statement, need to ensure that the code in + // CompleteDeregistration() sets the appropriate state variables to gaurantee that "else" + // clause will execute here and the record will be cut from the list. + if (rr->WakeUp.HMAC.l[0] || + (RecordType == kDNSRecordTypeShared && (rr->RequireGoodbye || rr->AnsweredLocalQ))) + { + verbosedebugf("mDNS_Deregister_internal: Starting deregistration for %s", ARDisplayString(m, rr)); + rr->resrec.RecordType = kDNSRecordTypeDeregistering; + rr->resrec.rroriginalttl = 0; + rr->AnnounceCount = rr->WakeUp.HMAC.l[0] ? WakeupCount : (drt == mDNS_Dereg_rapid) ? 1 : GoodbyeCount; + rr->ThisAPInterval = mDNSPlatformOneSecond * 2; + rr->LastAPTime = m->timenow - rr->ThisAPInterval; + m->LocalRemoveEvents = mDNStrue; + if (m->NextScheduledResponse - (m->timenow + mDNSPlatformOneSecond/10) >= 0) + m->NextScheduledResponse = (m->timenow + mDNSPlatformOneSecond/10); + } + else + { + if (!dupList && RRLocalOnly(rr)) + { + AuthGroup *ag = RemoveAuthRecord(m, &m->rrauth, rr); + if (ag->NewLocalOnlyRecords == rr) ag->NewLocalOnlyRecords = rr->next; + } + else + { + *p = rr->next; // Cut this record from the list + if (m->NewLocalRecords == rr) m->NewLocalRecords = rr->next; + } + // If someone is about to look at this, bump the pointer forward + if (m->CurrentRecord == rr) m->CurrentRecord = rr->next; + rr->next = mDNSNULL; + + // Should we generate local remove events here? + // i.e. something like: + // if (rr->AnsweredLocalQ) { AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse); rr->AnsweredLocalQ = mDNSfalse; } + + verbosedebugf("mDNS_Deregister_internal: Deleting record for %s", ARDisplayString(m, rr)); + rr->resrec.RecordType = kDNSRecordTypeUnregistered; + + if ((drt == mDNS_Dereg_conflict || drt == mDNS_Dereg_repeat) && RecordType == kDNSRecordTypeShared) + debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + + // If we have an update queued up which never executed, give the client a chance to free that memory + if (rr->NewRData) CompleteRDataUpdate(m, rr); // Update our rdata, clear the NewRData pointer, and return memory to the client + + + // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function + // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. + // In this case the likely client action to the mStatus_MemFree message is to free the memory, + // so any attempt to touch rr after this is likely to lead to a crash. + if (drt != mDNS_Dereg_conflict) + { + mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback + LogInfo("mDNS_Deregister_internal: mStatus_MemFree for %s", ARDisplayString(m, rr)); + if (rr->RecordCallback) + rr->RecordCallback(m, rr, mStatus_MemFree); // MUST NOT touch rr after this + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + } + else + { + RecordProbeFailure(m, rr); + mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback + if (rr->RecordCallback) + rr->RecordCallback(m, rr, mStatus_NameConflict); // MUST NOT touch rr after this + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + // Now that we've finished deregistering rr, check our DuplicateRecords list for any that we marked previously. + // Note that with all the client callbacks going on, by the time we get here all the + // records we marked may have been explicitly deregistered by the client anyway. + r2 = m->DuplicateRecords; + while (r2) + { + if (r2->ProbeCount != 0xFF) + { + r2 = r2->next; + } + else + { + mDNS_Deregister_internal(m, r2, mDNS_Dereg_conflict); + // As this is a duplicate record, it will be unlinked from the list + // immediately + r2 = m->DuplicateRecords; + } + } + } + } + mDNS_UpdateAllowSleep(m); + return(mStatus_NoError); +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -1603,150 +1642,161 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, #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)); - } +{ + 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.rdatahash == 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); - } - else if (RRTypeIsAddressType(rr->resrec.rrtype)) // For A or AAAA, put counterpart as additional - { - 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.namehash == rr2->resrec.namehash && // ... and have the same name - SameDomainName(rr->resrec.name, rr2->resrec.name)) - AddRecordToResponseList(nrpp, rr2, rr); - } - else if (rr->resrec.rrtype == kDNSType_PTR) // For service PTR, see if we want to add DeviceInfo record - { - if (ResourceRecordIsValidInterfaceAnswer(&m->DeviceInfo, InterfaceID) && - SameDomainLabel(rr->resrec.rdata->u.name.c, m->DeviceInfo.resrec.name->c)) - AddRecordToResponseList(nrpp, &m->DeviceInfo, rr); - } - } - } +{ + 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.rdatahash == 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); + } + else if (RRTypeIsAddressType(rr->resrec.rrtype)) // For A or AAAA, put counterpart as additional + { + 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.namehash == rr2->resrec.namehash && // ... and have the same name + SameDomainName(rr->resrec.name, rr2->resrec.name)) + AddRecordToResponseList(nrpp, rr2, rr); + } + else if (rr->resrec.rrtype == kDNSType_PTR) // For service PTR, see if we want to add DeviceInfo record + { + if (ResourceRecordIsValidInterfaceAnswer(&m->DeviceInfo, InterfaceID) && + SameDomainLabel(rr->resrec.rdata->u.name.c, m->DeviceInfo.resrec.name->c)) + AddRecordToResponseList(nrpp, &m->DeviceInfo, 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, InterfaceID, mDNSNULL, dest, MulticastDNSPort, mDNSNULL, mDNSNULL); - } - } +{ + AuthRecord *rr; + AuthRecord *ResponseRecords = mDNSNULL; + AuthRecord **nrp = &ResponseRecords; + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); + + // 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; + + // Only sent records registered for P2P over P2P interfaces + if (intf && !mDNSPlatformValidRecordForInterface(rr, intf)) + { + LogInfo("SendDelayedUnicastResponse: Not sending %s, on %s", ARDisplayString(m, rr), InterfaceNameForID(m, InterfaceID)); + continue; + } + + 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, InterfaceID, mDNSNULL, dest, MulticastDNSPort, mDNSNULL, mDNSNULL, mDNSfalse); + } +} // CompleteDeregistration guarantees that on exit the record will have been cut from the m->ResourceRecords list // and the client's mStatus_MemFree callback will have been invoked mDNSexport void CompleteDeregistration(mDNS *const m, AuthRecord *rr) - { - LogInfo("CompleteDeregistration: called for Resource record %s", ARDisplayString(m, rr)); - // Clearing rr->RequireGoodbye signals mDNS_Deregister_internal() that - // it should go ahead and immediately dispose of this registration - rr->resrec.RecordType = kDNSRecordTypeShared; - rr->RequireGoodbye = mDNSfalse; - rr->WakeUp.HMAC = zeroEthAddr; - if (rr->AnsweredLocalQ) { AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse); rr->AnsweredLocalQ = mDNSfalse; } - mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); // Don't touch rr after this - } +{ + LogInfo("CompleteDeregistration: called for Resource record %s", ARDisplayString(m, rr)); + // Clearing rr->RequireGoodbye signals mDNS_Deregister_internal() that + // it should go ahead and immediately dispose of this registration + rr->resrec.RecordType = kDNSRecordTypeShared; + rr->RequireGoodbye = mDNSfalse; + rr->WakeUp.HMAC = zeroEthAddr; + if (rr->AnsweredLocalQ) { AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse); rr->AnsweredLocalQ = mDNSfalse; } + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); // Don't touch rr after this +} // DiscardDeregistrations is used on shutdown and sleep to discard (forcibly and immediately) // any deregistering records that remain in the m->ResourceRecords list. @@ -1754,262 +1804,262 @@ mDNSexport void CompleteDeregistration(mDNS *const m, AuthRecord *rr) // which may change the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. mDNSlocal void DiscardDeregistrations(mDNS *const m) - { - if (m->CurrentRecord) - LogMsg("DiscardDeregistrations ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); - m->CurrentRecord = m->ResourceRecords; - - while (m->CurrentRecord) - { - AuthRecord *rr = m->CurrentRecord; - if (!AuthRecord_uDNS(rr) && rr->resrec.RecordType == kDNSRecordTypeDeregistering) - CompleteDeregistration(m, rr); // Don't touch rr after this - else - m->CurrentRecord = rr->next; - } - } +{ + if (m->CurrentRecord) + LogMsg("DiscardDeregistrations ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = m->ResourceRecords; + + while (m->CurrentRecord) + { + AuthRecord *rr = m->CurrentRecord; + if (!AuthRecord_uDNS(rr) && rr->resrec.RecordType == kDNSRecordTypeDeregistering) + CompleteDeregistration(m, rr); // Don't touch rr after this + else + m->CurrentRecord = rr->next; + } +} mDNSlocal mStatus GetLabelDecimalValue(const mDNSu8 *const src, mDNSu8 *dst) - { - int i, val = 0; - if (src[0] < 1 || src[0] > 3) return(mStatus_Invalid); - for (i=1; i<=src[0]; i++) - { - if (src[i] < '0' || src[i] > '9') return(mStatus_Invalid); - val = val * 10 + src[i] - '0'; - } - if (val > 255) return(mStatus_Invalid); - *dst = (mDNSu8)val; - return(mStatus_NoError); - } +{ + int i, val = 0; + if (src[0] < 1 || src[0] > 3) return(mStatus_Invalid); + for (i=1; i<=src[0]; i++) + { + if (src[i] < '0' || src[i] > '9') return(mStatus_Invalid); + val = val * 10 + src[i] - '0'; + } + if (val > 255) return(mStatus_Invalid); + *dst = (mDNSu8)val; + return(mStatus_NoError); +} mDNSlocal mStatus GetIPv4FromName(mDNSAddr *const a, const domainname *const name) - { - int skip = CountLabels(name) - 6; - if (skip < 0) { LogMsg("GetIPFromName: Need six labels in IPv4 reverse mapping name %##s", name); return mStatus_Invalid; } - if (GetLabelDecimalValue(SkipLeadingLabels(name, skip+3)->c, &a->ip.v4.b[0]) || - GetLabelDecimalValue(SkipLeadingLabels(name, skip+2)->c, &a->ip.v4.b[1]) || - GetLabelDecimalValue(SkipLeadingLabels(name, skip+1)->c, &a->ip.v4.b[2]) || - GetLabelDecimalValue(SkipLeadingLabels(name, skip+0)->c, &a->ip.v4.b[3])) return mStatus_Invalid; - a->type = mDNSAddrType_IPv4; - return(mStatus_NoError); - } +{ + int skip = CountLabels(name) - 6; + if (skip < 0) { LogMsg("GetIPFromName: Need six labels in IPv4 reverse mapping name %##s", name); return mStatus_Invalid; } + if (GetLabelDecimalValue(SkipLeadingLabels(name, skip+3)->c, &a->ip.v4.b[0]) || + GetLabelDecimalValue(SkipLeadingLabels(name, skip+2)->c, &a->ip.v4.b[1]) || + GetLabelDecimalValue(SkipLeadingLabels(name, skip+1)->c, &a->ip.v4.b[2]) || + GetLabelDecimalValue(SkipLeadingLabels(name, skip+0)->c, &a->ip.v4.b[3])) return mStatus_Invalid; + a->type = mDNSAddrType_IPv4; + return(mStatus_NoError); +} #define HexVal(X) ( ((X) >= '0' && (X) <= '9') ? ((X) - '0' ) : \ - ((X) >= 'A' && (X) <= 'F') ? ((X) - 'A' + 10) : \ - ((X) >= 'a' && (X) <= 'f') ? ((X) - 'a' + 10) : -1) + ((X) >= 'A' && (X) <= 'F') ? ((X) - 'A' + 10) : \ + ((X) >= 'a' && (X) <= 'f') ? ((X) - 'a' + 10) : -1) mDNSlocal mStatus GetIPv6FromName(mDNSAddr *const a, const domainname *const name) - { - int i, h, l; - const domainname *n; +{ + int i, h, l; + const domainname *n; - int skip = CountLabels(name) - 34; - if (skip < 0) { LogMsg("GetIPFromName: Need 34 labels in IPv6 reverse mapping name %##s", name); return mStatus_Invalid; } + int skip = CountLabels(name) - 34; + if (skip < 0) { LogMsg("GetIPFromName: Need 34 labels in IPv6 reverse mapping name %##s", name); return mStatus_Invalid; } - n = SkipLeadingLabels(name, skip); - for (i=0; i<16; i++) - { - if (n->c[0] != 1) return mStatus_Invalid; - l = HexVal(n->c[1]); - n = (const domainname *)(n->c + 2); + n = SkipLeadingLabels(name, skip); + for (i=0; i<16; i++) + { + if (n->c[0] != 1) return mStatus_Invalid; + l = HexVal(n->c[1]); + n = (const domainname *)(n->c + 2); - if (n->c[0] != 1) return mStatus_Invalid; - h = HexVal(n->c[1]); - n = (const domainname *)(n->c + 2); + if (n->c[0] != 1) return mStatus_Invalid; + h = HexVal(n->c[1]); + n = (const domainname *)(n->c + 2); - if (l<0 || h<0) return mStatus_Invalid; - a->ip.v6.b[15-i] = (mDNSu8)((h << 4) | l); - } + if (l<0 || h<0) return mStatus_Invalid; + a->ip.v6.b[15-i] = (mDNSu8)((h << 4) | l); + } - a->type = mDNSAddrType_IPv6; - return(mStatus_NoError); - } + a->type = mDNSAddrType_IPv6; + return(mStatus_NoError); +} mDNSlocal mDNSs32 ReverseMapDomainType(const domainname *const name) - { - int skip = CountLabels(name) - 2; - if (skip >= 0) - { - const domainname *suffix = SkipLeadingLabels(name, skip); - if (SameDomainName(suffix, (const domainname*)"\x7" "in-addr" "\x4" "arpa")) return mDNSAddrType_IPv4; - if (SameDomainName(suffix, (const domainname*)"\x3" "ip6" "\x4" "arpa")) return mDNSAddrType_IPv6; - } - return(mDNSAddrType_None); - } +{ + int skip = CountLabels(name) - 2; + if (skip >= 0) + { + const domainname *suffix = SkipLeadingLabels(name, skip); + if (SameDomainName(suffix, (const domainname*)"\x7" "in-addr" "\x4" "arpa")) return mDNSAddrType_IPv4; + if (SameDomainName(suffix, (const domainname*)"\x3" "ip6" "\x4" "arpa")) return mDNSAddrType_IPv6; + } + return(mDNSAddrType_None); +} mDNSlocal void SendARP(mDNS *const m, const mDNSu8 op, const AuthRecord *const rr, - const mDNSv4Addr *const spa, const mDNSEthAddr *const tha, const mDNSv4Addr *const tpa, const mDNSEthAddr *const dst) - { - int i; - mDNSu8 *ptr = m->omsg.data; - NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID); - if (!intf) { LogMsg("SendARP: No interface with InterfaceID %p found %s", rr->resrec.InterfaceID, ARDisplayString(m,rr)); return; } + const mDNSv4Addr *const spa, const mDNSEthAddr *const tha, const mDNSv4Addr *const tpa, const mDNSEthAddr *const dst) +{ + int i; + mDNSu8 *ptr = m->omsg.data; + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID); + if (!intf) { LogMsg("SendARP: No interface with InterfaceID %p found %s", rr->resrec.InterfaceID, ARDisplayString(m,rr)); return; } - // 0x00 Destination address - for (i=0; i<6; i++) *ptr++ = dst->b[i]; + // 0x00 Destination address + for (i=0; i<6; i++) *ptr++ = dst->b[i]; - // 0x06 Source address (Note: Since we don't currently set the BIOCSHDRCMPLT option, BPF will fill in the real interface address for us) - for (i=0; i<6; i++) *ptr++ = intf->MAC.b[0]; + // 0x06 Source address (Note: Since we don't currently set the BIOCSHDRCMPLT option, BPF will fill in the real interface address for us) + for (i=0; i<6; i++) *ptr++ = intf->MAC.b[0]; - // 0x0C ARP Ethertype (0x0806) - *ptr++ = 0x08; *ptr++ = 0x06; + // 0x0C ARP Ethertype (0x0806) + *ptr++ = 0x08; *ptr++ = 0x06; - // 0x0E ARP header - *ptr++ = 0x00; *ptr++ = 0x01; // Hardware address space; Ethernet = 1 - *ptr++ = 0x08; *ptr++ = 0x00; // Protocol address space; IP = 0x0800 - *ptr++ = 6; // Hardware address length - *ptr++ = 4; // Protocol address length - *ptr++ = 0x00; *ptr++ = op; // opcode; Request = 1, Response = 2 + // 0x0E ARP header + *ptr++ = 0x00; *ptr++ = 0x01; // Hardware address space; Ethernet = 1 + *ptr++ = 0x08; *ptr++ = 0x00; // Protocol address space; IP = 0x0800 + *ptr++ = 6; // Hardware address length + *ptr++ = 4; // Protocol address length + *ptr++ = 0x00; *ptr++ = op; // opcode; Request = 1, Response = 2 - // 0x16 Sender hardware address (our MAC address) - for (i=0; i<6; i++) *ptr++ = intf->MAC.b[i]; + // 0x16 Sender hardware address (our MAC address) + for (i=0; i<6; i++) *ptr++ = intf->MAC.b[i]; - // 0x1C Sender protocol address - for (i=0; i<4; i++) *ptr++ = spa->b[i]; + // 0x1C Sender protocol address + for (i=0; i<4; i++) *ptr++ = spa->b[i]; - // 0x20 Target hardware address - for (i=0; i<6; i++) *ptr++ = tha->b[i]; + // 0x20 Target hardware address + for (i=0; i<6; i++) *ptr++ = tha->b[i]; - // 0x26 Target protocol address - for (i=0; i<4; i++) *ptr++ = tpa->b[i]; + // 0x26 Target protocol address + for (i=0; i<4; i++) *ptr++ = tpa->b[i]; - // 0x2A Total ARP Packet length 42 bytes - mDNSPlatformSendRawPacket(m->omsg.data, ptr, rr->resrec.InterfaceID); - } + // 0x2A Total ARP Packet length 42 bytes + mDNSPlatformSendRawPacket(m->omsg.data, ptr, rr->resrec.InterfaceID); +} mDNSlocal mDNSu16 CheckSum(const void *const data, mDNSs32 length, mDNSu32 sum) - { - const mDNSu16 *ptr = data; - while (length > 0) { length -= 2; sum += *ptr++; } - sum = (sum & 0xFFFF) + (sum >> 16); - sum = (sum & 0xFFFF) + (sum >> 16); - return(sum != 0xFFFF ? sum : 0); - } +{ + const mDNSu16 *ptr = data; + while (length > 0) { length -= 2; sum += *ptr++; } + sum = (sum & 0xFFFF) + (sum >> 16); + sum = (sum & 0xFFFF) + (sum >> 16); + return(sum != 0xFFFF ? sum : 0); +} mDNSlocal mDNSu16 IPv6CheckSum(const mDNSv6Addr *const src, const mDNSv6Addr *const dst, const mDNSu8 protocol, const void *const data, const mDNSu32 length) - { - IPv6PseudoHeader ph; - ph.src = *src; - ph.dst = *dst; - ph.len.b[0] = length >> 24; - ph.len.b[1] = length >> 16; - ph.len.b[2] = length >> 8; - ph.len.b[3] = length; - ph.pro.b[0] = 0; - ph.pro.b[1] = 0; - ph.pro.b[2] = 0; - ph.pro.b[3] = protocol; - return CheckSum(&ph, sizeof(ph), CheckSum(data, length, 0)); - } +{ + IPv6PseudoHeader ph; + ph.src = *src; + ph.dst = *dst; + ph.len.b[0] = length >> 24; + ph.len.b[1] = length >> 16; + ph.len.b[2] = length >> 8; + ph.len.b[3] = length; + ph.pro.b[0] = 0; + ph.pro.b[1] = 0; + ph.pro.b[2] = 0; + ph.pro.b[3] = protocol; + return CheckSum(&ph, sizeof(ph), CheckSum(data, length, 0)); +} mDNSlocal void SendNDP(mDNS *const m, const mDNSu8 op, const mDNSu8 flags, const AuthRecord *const rr, - const mDNSv6Addr *const spa, const mDNSEthAddr *const tha, const mDNSv6Addr *const tpa, const mDNSEthAddr *const dst) - { - int i; - mDNSOpaque16 checksum; - mDNSu8 *ptr = m->omsg.data; - // Some recipient hosts seem to ignore Neighbor Solicitations if the IPv6-layer destination address is not the - // appropriate IPv6 solicited node multicast address, so we use that IPv6-layer destination address, even though - // at the Ethernet-layer we unicast the packet to the intended target, to avoid wasting network bandwidth. - const mDNSv6Addr mc = { { 0xFF,0x02,0x00,0x00, 0,0,0,0, 0,0,0,1, 0xFF,tpa->b[0xD],tpa->b[0xE],tpa->b[0xF] } }; - const mDNSv6Addr *const v6dst = (op == NDP_Sol) ? &mc : tpa; - NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID); - if (!intf) { LogMsg("SendNDP: No interface with InterfaceID %p found %s", rr->resrec.InterfaceID, ARDisplayString(m,rr)); return; } - - // 0x00 Destination address - for (i=0; i<6; i++) *ptr++ = dst->b[i]; - // Right now we only send Neighbor Solicitations to verify whether the host we're proxying for has gone to sleep yet. - // Since we know who we're looking for, we send it via Ethernet-layer unicast, rather than bothering every host on the - // link with a pointless link-layer multicast. - // Should we want to send traditional Neighbor Solicitations in the future, where we really don't know in advance what - // Ethernet-layer address we're looking for, we'll need to send to the appropriate Ethernet-layer multicast address: - // *ptr++ = 0x33; - // *ptr++ = 0x33; - // *ptr++ = 0xFF; - // *ptr++ = tpa->b[0xD]; - // *ptr++ = tpa->b[0xE]; - // *ptr++ = tpa->b[0xF]; - - // 0x06 Source address (Note: Since we don't currently set the BIOCSHDRCMPLT option, BPF will fill in the real interface address for us) - for (i=0; i<6; i++) *ptr++ = (tha ? *tha : intf->MAC).b[i]; - - // 0x0C IPv6 Ethertype (0x86DD) - *ptr++ = 0x86; *ptr++ = 0xDD; - - // 0x0E IPv6 header - *ptr++ = 0x60; *ptr++ = 0x00; *ptr++ = 0x00; *ptr++ = 0x00; // Version, Traffic Class, Flow Label - *ptr++ = 0x00; *ptr++ = 0x20; // Length - *ptr++ = 0x3A; // Protocol == ICMPv6 - *ptr++ = 0xFF; // Hop Limit - - // 0x16 Sender IPv6 address - for (i=0; i<16; i++) *ptr++ = spa->b[i]; - - // 0x26 Destination IPv6 address - for (i=0; i<16; i++) *ptr++ = v6dst->b[i]; - - // 0x36 NDP header - *ptr++ = op; // 0x87 == Neighbor Solicitation, 0x88 == Neighbor Advertisement - *ptr++ = 0x00; // Code - *ptr++ = 0x00; *ptr++ = 0x00; // Checksum placeholder (0x38, 0x39) - *ptr++ = flags; - *ptr++ = 0x00; *ptr++ = 0x00; *ptr++ = 0x00; - - if (op == NDP_Sol) // Neighbor Solicitation. The NDP "target" is the address we seek. - { - // 0x3E NDP target. - for (i=0; i<16; i++) *ptr++ = tpa->b[i]; - // 0x4E Source Link-layer Address - // - // MUST NOT be included when the source IP address is the unspecified address. - // Otherwise, on link layers that have addresses this option MUST be included - // in multicast solicitations and SHOULD be included in unicast solicitations. - if (!mDNSIPv6AddressIsZero(*spa)) - { - *ptr++ = NDP_SrcLL; // Option Type 1 == Source Link-layer Address - *ptr++ = 0x01; // Option length 1 (in units of 8 octets) - for (i=0; i<6; i++) *ptr++ = (tha ? *tha : intf->MAC).b[i]; - } - } - else // Neighbor Advertisement. The NDP "target" is the address we're giving information about. - { - // 0x3E NDP target. - for (i=0; i<16; i++) *ptr++ = spa->b[i]; - // 0x4E Target Link-layer Address - *ptr++ = NDP_TgtLL; // Option Type 2 == Target Link-layer Address - *ptr++ = 0x01; // Option length 1 (in units of 8 octets) - for (i=0; i<6; i++) *ptr++ = (tha ? *tha : intf->MAC).b[i]; - } - - // 0x4E or 0x56 Total NDP Packet length 78 or 86 bytes - m->omsg.data[0x13] = ptr - &m->omsg.data[0x36]; // Compute actual length - checksum.NotAnInteger = ~IPv6CheckSum(spa, v6dst, 0x3A, &m->omsg.data[0x36], m->omsg.data[0x13]); - m->omsg.data[0x38] = checksum.b[0]; - m->omsg.data[0x39] = checksum.b[1]; - - mDNSPlatformSendRawPacket(m->omsg.data, ptr, rr->resrec.InterfaceID); - } + const mDNSv6Addr *const spa, const mDNSEthAddr *const tha, const mDNSv6Addr *const tpa, const mDNSEthAddr *const dst) +{ + int i; + mDNSOpaque16 checksum; + mDNSu8 *ptr = m->omsg.data; + // Some recipient hosts seem to ignore Neighbor Solicitations if the IPv6-layer destination address is not the + // appropriate IPv6 solicited node multicast address, so we use that IPv6-layer destination address, even though + // at the Ethernet-layer we unicast the packet to the intended target, to avoid wasting network bandwidth. + const mDNSv6Addr mc = { { 0xFF,0x02,0x00,0x00, 0,0,0,0, 0,0,0,1, 0xFF,tpa->b[0xD],tpa->b[0xE],tpa->b[0xF] } }; + const mDNSv6Addr *const v6dst = (op == NDP_Sol) ? &mc : tpa; + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID); + if (!intf) { LogMsg("SendNDP: No interface with InterfaceID %p found %s", rr->resrec.InterfaceID, ARDisplayString(m,rr)); return; } + + // 0x00 Destination address + for (i=0; i<6; i++) *ptr++ = dst->b[i]; + // Right now we only send Neighbor Solicitations to verify whether the host we're proxying for has gone to sleep yet. + // Since we know who we're looking for, we send it via Ethernet-layer unicast, rather than bothering every host on the + // link with a pointless link-layer multicast. + // Should we want to send traditional Neighbor Solicitations in the future, where we really don't know in advance what + // Ethernet-layer address we're looking for, we'll need to send to the appropriate Ethernet-layer multicast address: + // *ptr++ = 0x33; + // *ptr++ = 0x33; + // *ptr++ = 0xFF; + // *ptr++ = tpa->b[0xD]; + // *ptr++ = tpa->b[0xE]; + // *ptr++ = tpa->b[0xF]; + + // 0x06 Source address (Note: Since we don't currently set the BIOCSHDRCMPLT option, BPF will fill in the real interface address for us) + for (i=0; i<6; i++) *ptr++ = (tha ? *tha : intf->MAC).b[i]; + + // 0x0C IPv6 Ethertype (0x86DD) + *ptr++ = 0x86; *ptr++ = 0xDD; + + // 0x0E IPv6 header + *ptr++ = 0x60; *ptr++ = 0x00; *ptr++ = 0x00; *ptr++ = 0x00; // Version, Traffic Class, Flow Label + *ptr++ = 0x00; *ptr++ = 0x20; // Length + *ptr++ = 0x3A; // Protocol == ICMPv6 + *ptr++ = 0xFF; // Hop Limit + + // 0x16 Sender IPv6 address + for (i=0; i<16; i++) *ptr++ = spa->b[i]; + + // 0x26 Destination IPv6 address + for (i=0; i<16; i++) *ptr++ = v6dst->b[i]; + + // 0x36 NDP header + *ptr++ = op; // 0x87 == Neighbor Solicitation, 0x88 == Neighbor Advertisement + *ptr++ = 0x00; // Code + *ptr++ = 0x00; *ptr++ = 0x00; // Checksum placeholder (0x38, 0x39) + *ptr++ = flags; + *ptr++ = 0x00; *ptr++ = 0x00; *ptr++ = 0x00; + + if (op == NDP_Sol) // Neighbor Solicitation. The NDP "target" is the address we seek. + { + // 0x3E NDP target. + for (i=0; i<16; i++) *ptr++ = tpa->b[i]; + // 0x4E Source Link-layer Address + // + // MUST NOT be included when the source IP address is the unspecified address. + // Otherwise, on link layers that have addresses this option MUST be included + // in multicast solicitations and SHOULD be included in unicast solicitations. + if (!mDNSIPv6AddressIsZero(*spa)) + { + *ptr++ = NDP_SrcLL; // Option Type 1 == Source Link-layer Address + *ptr++ = 0x01; // Option length 1 (in units of 8 octets) + for (i=0; i<6; i++) *ptr++ = (tha ? *tha : intf->MAC).b[i]; + } + } + else // Neighbor Advertisement. The NDP "target" is the address we're giving information about. + { + // 0x3E NDP target. + for (i=0; i<16; i++) *ptr++ = spa->b[i]; + // 0x4E Target Link-layer Address + *ptr++ = NDP_TgtLL; // Option Type 2 == Target Link-layer Address + *ptr++ = 0x01; // Option length 1 (in units of 8 octets) + for (i=0; i<6; i++) *ptr++ = (tha ? *tha : intf->MAC).b[i]; + } + + // 0x4E or 0x56 Total NDP Packet length 78 or 86 bytes + m->omsg.data[0x13] = ptr - &m->omsg.data[0x36]; // Compute actual length + checksum.NotAnInteger = ~IPv6CheckSum(spa, v6dst, 0x3A, &m->omsg.data[0x36], m->omsg.data[0x13]); + m->omsg.data[0x38] = checksum.b[0]; + m->omsg.data[0x39] = checksum.b[1]; + + mDNSPlatformSendRawPacket(m->omsg.data, ptr, rr->resrec.InterfaceID); +} mDNSlocal void SetupOwnerOpt(const mDNS *const m, const NetworkInterfaceInfo *const intf, rdataOPT *const owner) - { - owner->u.owner.vers = 0; - owner->u.owner.seq = m->SleepSeqNum; - owner->u.owner.HMAC = m->PrimaryMAC; - owner->u.owner.IMAC = intf->MAC; - owner->u.owner.password = zeroEthAddr; - - // Don't try to compute the optlen until *after* we've set up the data fields - // Right now the DNSOpt_Owner_Space macro does not depend on the owner->u.owner being set up correctly, but in the future it might - owner->opt = kDNSOpt_Owner; - owner->optlen = DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) - 4; - } +{ + owner->u.owner.vers = 0; + owner->u.owner.seq = m->SleepSeqNum; + owner->u.owner.HMAC = m->PrimaryMAC; + owner->u.owner.IMAC = intf->MAC; + owner->u.owner.password = zeroEthAddr; + + // Don't try to compute the optlen until *after* we've set up the data fields + // Right now the DNSOpt_Owner_Space macro does not depend on the owner->u.owner being set up correctly, but in the future it might + owner->opt = kDNSOpt_Owner; + owner->optlen = DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) - 4; +} mDNSlocal void GrantUpdateCredit(AuthRecord *rr) - { - if (++rr->UpdateCredits >= kMaxUpdateCredits) rr->NextUpdateCredit = 0; - else rr->NextUpdateCredit = NonZeroTime(rr->NextUpdateCredit + kUpdateCreditRefreshInterval); - } +{ + if (++rr->UpdateCredits >= kMaxUpdateCredits) rr->NextUpdateCredit = 0; + else rr->NextUpdateCredit = NonZeroTime(rr->NextUpdateCredit + kUpdateCreditRefreshInterval); +} // Note about acceleration of announcements to facilitate automatic coalescing of // multiple independent threads of announcements into a single synchronized thread: @@ -2028,443 +2078,457 @@ mDNSlocal void GrantUpdateCredit(AuthRecord *rr) // the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. mDNSlocal void SendResponses(mDNS *const m) - { - int pktcount = 0; - AuthRecord *rr, *r2; - mDNSs32 maxExistingAnnounceInterval = 0; - const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces); - - m->NextScheduledResponse = m->timenow + 0x78000000; - - if (m->SleepState == SleepState_Transferring) RetrySPSRegistrations(m); - - 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 - // *** - - // Run through our list of records, and decide which ones we're going to announce on all interfaces - for (rr = m->ResourceRecords; rr; rr=rr->next) - { - while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr); - if (TimeToAnnounceThisRecord(rr, m->timenow)) - { - if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) - { - if (!rr->WakeUp.HMAC.l[0]) - { - if (rr->AnnounceCount) rr->ImmedAnswer = mDNSInterfaceMark; // Send goodbye packet on all interfaces - } - else - { - LogSPS("SendResponses: Sending wakeup %2d for %.6a %s", rr->AnnounceCount-3, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); - SendWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.IMAC, &rr->WakeUp.password); - for (r2 = rr; r2; r2=r2->next) - if (r2->AnnounceCount && r2->resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&r2->WakeUp.IMAC, &rr->WakeUp.IMAC)) - { - // For now we only want to send a single Unsolicited Neighbor Advertisement restoring the address to the original - // owner, because these packets can cause some IPv6 stacks to falsely conclude that there's an address conflict. - if (r2->AddressProxy.type == mDNSAddrType_IPv6 && r2->AnnounceCount == WakeupCount) - { - LogSPS("NDP Announcement %2d Releasing traffic for H-MAC %.6a I-MAC %.6a %s", - r2->AnnounceCount-3, &r2->WakeUp.HMAC, &r2->WakeUp.IMAC, ARDisplayString(m,r2)); - SendNDP(m, NDP_Adv, NDP_Override, r2, &r2->AddressProxy.ip.v6, &r2->WakeUp.IMAC, &AllHosts_v6, &AllHosts_v6_Eth); - } - r2->LastAPTime = m->timenow; - // After 15 wakeups without success (maybe host has left the network) send three goodbyes instead - if (--r2->AnnounceCount <= GoodbyeCount) r2->WakeUp.HMAC = zeroEthAddr; - } - } - } - else if (ResourceRecordIsValidAnswer(rr)) - { - if (rr->AddressProxy.type) - { - rr->AnnounceCount--; - rr->ThisAPInterval *= 2; - rr->LastAPTime = m->timenow; - if (rr->AddressProxy.type == mDNSAddrType_IPv4) - { - LogSPS("ARP Announcement %2d Capturing traffic for H-MAC %.6a I-MAC %.6a %s", - rr->AnnounceCount, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr)); - SendARP(m, 1, rr, &rr->AddressProxy.ip.v4, &zeroEthAddr, &rr->AddressProxy.ip.v4, &onesEthAddr); - } - else if (rr->AddressProxy.type == mDNSAddrType_IPv6) - { - LogSPS("NDP Announcement %2d Capturing traffic for H-MAC %.6a I-MAC %.6a %s", - rr->AnnounceCount, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr)); - SendNDP(m, NDP_Adv, NDP_Override, rr, &rr->AddressProxy.ip.v6, mDNSNULL, &AllHosts_v6, &AllHosts_v6_Eth); - } - } - else - { - rr->ImmedAnswer = mDNSInterfaceMark; // Send on all interfaces - if (maxExistingAnnounceInterval < rr->ThisAPInterval) - maxExistingAnnounceInterval = rr->ThisAPInterval; - if (rr->UpdateBlocked) rr->UpdateBlocked = 0; - } - } - } - } - - // Any interface-specific records we're going to send are marked as being sent on all appropriate interfaces (which is just one) - // Eligible records that are more than half-way to their announcement time are accelerated - for (rr = m->ResourceRecords; rr; rr=rr->next) - if ((rr->resrec.InterfaceID && rr->ImmedAnswer) || - (rr->ThisAPInterval <= maxExistingAnnounceInterval && - TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2) && - !rr->AddressProxy.type && // Don't include ARP Annoucements when considering which records to accelerate - ResourceRecordIsValidAnswer(rr))) - rr->ImmedAnswer = mDNSInterfaceMark; // Send on all interfaces - - // When sending SRV records (particularly when announcing a new service) automatically add related Address record(s) as additionals - // Note: Currently all address records are interface-specific, so it's safe to set ImmedAdditional to their InterfaceID, - // which will be non-null. If by some chance there is an address record that's not interface-specific (should never happen) - // then all that means is that it won't get sent -- which would not be the end of the world. - for (rr = m->ResourceRecords; rr; rr=rr->next) - { - if (rr->ImmedAnswer && rr->resrec.rrtype == kDNSType_SRV) - for (r2=m->ResourceRecords; r2; r2=r2->next) // Scan list of resource records - if (RRTypeIsAddressType(r2->resrec.rrtype) && // For all address records (A/AAAA) ... - ResourceRecordIsValidAnswer(r2) && // ... which are valid for answer ... - rr->LastMCTime - r2->LastMCTime >= 0 && // ... which we have not sent recently ... - rr->resrec.rdatahash == r2->resrec.namehash && // ... whose name is the name of the SRV target - SameDomainName(&rr->resrec.rdata->u.srv.target, r2->resrec.name) && - (rr->ImmedAnswer == mDNSInterfaceMark || rr->ImmedAnswer == r2->resrec.InterfaceID)) - r2->ImmedAdditional = r2->resrec.InterfaceID; // ... then mark this address record for sending too - // We also make sure we send the DeviceInfo TXT record too, if necessary - // We check for RecordType == kDNSRecordTypeShared because we don't want to tag the - // DeviceInfo TXT record onto a goodbye packet (RecordType == kDNSRecordTypeDeregistering). - if (rr->ImmedAnswer && rr->resrec.RecordType == kDNSRecordTypeShared && rr->resrec.rrtype == kDNSType_PTR) - if (ResourceRecordIsValidAnswer(&m->DeviceInfo) && SameDomainLabel(rr->resrec.rdata->u.name.c, m->DeviceInfo.resrec.name->c)) - { - if (!m->DeviceInfo.ImmedAnswer) m->DeviceInfo.ImmedAnswer = rr->ImmedAnswer; - else m->DeviceInfo.ImmedAnswer = mDNSInterfaceMark; - } - } - - // If there's a record which is supposed to be unique that we're going to send, then make sure that we give - // the whole RRSet as an atomic unit. That means that if we have any other records with the same name/type/class - // then we need to mark them for sending too. Otherwise, if we set the kDNSClass_UniqueRRSet bit on a - // record, then other RRSet members that have not been sent recently will get flushed out of client caches. - // -- If a record is marked to be sent on a certain interface, make sure the whole set is marked to be sent on that interface - // -- If any record is marked to be sent on all interfaces, make sure the whole set is marked to be sent on all interfaces - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) - { - if (rr->ImmedAnswer) // If we're sending this as answer, see that its whole RRSet is similarly marked - { - for (r2 = m->ResourceRecords; r2; r2=r2->next) - if (ResourceRecordIsValidAnswer(r2)) - if (r2->ImmedAnswer != mDNSInterfaceMark && - r2->ImmedAnswer != rr->ImmedAnswer && SameResourceRecordSignature(r2, rr)) - r2->ImmedAnswer = !r2->ImmedAnswer ? rr->ImmedAnswer : mDNSInterfaceMark; - } - else if (rr->ImmedAdditional) // If we're sending this as additional, see that its whole RRSet is similarly marked - { - for (r2 = m->ResourceRecords; r2; r2=r2->next) - if (ResourceRecordIsValidAnswer(r2)) - if (r2->ImmedAdditional != rr->ImmedAdditional && SameResourceRecordSignature(r2, rr)) - r2->ImmedAdditional = rr->ImmedAdditional; - } - } - - // Now set SendRNow state appropriately - for (rr = m->ResourceRecords; rr; rr=rr->next) - { - if (rr->ImmedAnswer == mDNSInterfaceMark) // Sending this record on all appropriate interfaces - { - rr->SendRNow = !intf ? mDNSNULL : (rr->resrec.InterfaceID) ? rr->resrec.InterfaceID : intf->InterfaceID; - rr->ImmedAdditional = mDNSNULL; // No need to send as additional if sending as answer - rr->LastMCTime = m->timenow; - rr->LastMCInterface = rr->ImmedAnswer; - // If we're announcing this record, and it's at least half-way to its ordained time, then consider this announcement done - if (TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2)) - { - rr->AnnounceCount--; - if (rr->resrec.RecordType != kDNSRecordTypeDeregistering) - rr->ThisAPInterval *= 2; - rr->LastAPTime = m->timenow; - debugf("Announcing %##s (%s) %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->AnnounceCount); - } - } - else if (rr->ImmedAnswer) // Else, just respond to a single query on single interface: - { - rr->SendRNow = rr->ImmedAnswer; // Just respond on that interface - rr->ImmedAdditional = mDNSNULL; // No need to send as additional too - rr->LastMCTime = m->timenow; - rr->LastMCInterface = rr->ImmedAnswer; - } - SetNextAnnounceProbeTime(m, rr); - //if (rr->SendRNow) LogMsg("%-15.4a %s", &rr->v4Requester, ARDisplayString(m, rr)); - } - - // *** - // *** 2. Loop through interface list, sending records as appropriate - // *** - - while (intf) - { - const int OwnerRecordSpace = (m->AnnounceOwner && intf->MAC.l[0]) ? DNSOpt_Header_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) : 0; - int numDereg = 0; - int numAnnounce = 0; - int numAnswer = 0; - mDNSu8 *responseptr = m->omsg.data; - mDNSu8 *newptr; - InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags); - - // First Pass. Look for: - // 1. Deregistering records that need to send their goodbye packet - // 2. Updated records that need to retract their old data - // 3. Answers and announcements we need to send - for (rr = m->ResourceRecords; rr; rr=rr->next) - { - - // Skip this interface if the record InterfaceID is *Any and the record is not - // appropriate for the interface type. - if ((rr->SendRNow == intf->InterfaceID) && - ((rr->resrec.InterfaceID == mDNSInterface_Any) && !mDNSPlatformValidRecordForInterface(rr, intf))) - { - LogInfo("SendResponses: Not Sending %s, on %s", ARDisplayString(m, rr), InterfaceNameForID(m, rr->SendRNow)); - rr->SendRNow = GetNextActiveInterfaceID(intf); - } - else if (rr->SendRNow == intf->InterfaceID) - { - RData *OldRData = rr->resrec.rdata; - mDNSu16 oldrdlength = rr->resrec.rdlength; - mDNSu8 active = (mDNSu8) - (rr->resrec.RecordType != kDNSRecordTypeDeregistering && - (m->SleepState != SleepState_Sleeping || intf->SPSAddr[0].type || intf->SPSAddr[1].type || intf->SPSAddr[2].type)); - newptr = mDNSNULL; - if (rr->NewRData && active) - { - // See if we should send a courtesy "goodbye" for the old data before we replace it. - if (ResourceRecordIsValidAnswer(rr) && rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) - { - newptr = PutRR_OS_TTL(responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0); - if (newptr) { responseptr = newptr; numDereg++; rr->RequireGoodbye = mDNSfalse; } - else continue; // If this packet is already too full to hold the goodbye for this record, skip it for now and we'll retry later - } - SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); - } - - if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) - rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it - newptr = PutRR_OS_TTL(responseptr, &m->omsg.h.numAnswers, &rr->resrec, active ? rr->resrec.rroriginalttl : 0); - rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state - if (newptr) - { - responseptr = newptr; - rr->RequireGoodbye = active; - if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) numDereg++; - else if (rr->LastAPTime == m->timenow) numAnnounce++; else numAnswer++; - } - - if (rr->NewRData && active) - SetNewRData(&rr->resrec, OldRData, oldrdlength); - - // The first time through (pktcount==0), if this record is verified unique - // (i.e. typically A, AAAA, SRV, TXT and reverse-mapping PTR), set the flag to add an NSEC too. - if (!pktcount && active && (rr->resrec.RecordType & kDNSRecordTypeActiveUniqueMask) && !rr->SendNSECNow) - rr->SendNSECNow = mDNSInterfaceMark; - - if (newptr) // If succeeded in sending, advance to next interface - { - // If sending on all interfaces, go to next interface; else we're finished now - if (rr->ImmedAnswer == mDNSInterfaceMark && rr->resrec.InterfaceID == mDNSInterface_Any) - rr->SendRNow = GetNextActiveInterfaceID(intf); - else - rr->SendRNow = mDNSNULL; - } - } - } - - // Second Pass. Add additional records, if there's space. - newptr = responseptr; - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->ImmedAdditional == intf->InterfaceID) - if (ResourceRecordIsValidAnswer(rr)) - { - // 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)) - { - const AuthRecord *a; - for (a = m->ResourceRecords; a; a=a->next) - if (a->LastMCTime == m->timenow && - a->LastMCInterface == intf->InterfaceID && - SameResourceRecordSignature(a, rr)) { SendAdditional = mDNStrue; break; } - } - 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 - { - // The first time through (pktcount==0), if this record is verified unique - // (i.e. typically A, AAAA, SRV, TXT and reverse-mapping PTR), set the flag to add an NSEC too. - if (!pktcount && (rr->resrec.RecordType & kDNSRecordTypeActiveUniqueMask) && !rr->SendNSECNow) - rr->SendNSECNow = mDNSInterfaceMark; - - if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) - rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it - newptr = PutRR_OS(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; - } - } - } - - // Third Pass. Add NSEC records, if there's space. - // When we're generating an NSEC record in response to a specify query for that type - // (recognized by rr->SendNSECNow == intf->InterfaceID) we should really put the NSEC in the Answer Section, - // not Additional Section, but for now it's easier to handle both cases in this Additional Section loop here. - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->SendNSECNow == mDNSInterfaceMark || rr->SendNSECNow == intf->InterfaceID) - { - AuthRecord nsec; - mDNS_SetupResourceRecord(&nsec, mDNSNULL, mDNSInterface_Any, kDNSType_NSEC, rr->resrec.rroriginalttl, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL); - nsec.resrec.rrclass |= kDNSClass_UniqueRRSet; - AssignDomainName(&nsec.namestorage, rr->resrec.name); - mDNSPlatformMemZero(nsec.rdatastorage.u.nsec.bitmap, sizeof(nsec.rdatastorage.u.nsec.bitmap)); - for (r2 = m->ResourceRecords; r2; r2=r2->next) - if (ResourceRecordIsValidAnswer(r2) && SameResourceRecordNameClassInterface(r2, rr)) - { - if (r2->resrec.rrtype >= kDNSQType_ANY) { LogMsg("Can't create NSEC for record %s", ARDisplayString(m, r2)); break; } - else nsec.rdatastorage.u.nsec.bitmap[r2->resrec.rrtype >> 3] |= 128 >> (r2->resrec.rrtype & 7); - } - newptr = responseptr; - if (!r2) // If we successfully built our NSEC record, add it to the packet now - { - newptr = PutRR_OS(responseptr, &m->omsg.h.numAdditionals, &nsec.resrec); - if (newptr) responseptr = newptr; - } - - // If we successfully put the NSEC record, clear the SendNSECNow flag - // If we consider this NSEC optional, then we unconditionally clear the SendNSECNow flag, even if we fail to put this additional record - if (newptr || rr->SendNSECNow == mDNSInterfaceMark) - { - rr->SendNSECNow = mDNSNULL; - // Run through remainder of list clearing SendNSECNow flag for all other records which would generate the same NSEC - for (r2 = rr->next; r2; r2=r2->next) - if (SameResourceRecordNameClassInterface(r2, rr)) - if (r2->SendNSECNow == mDNSInterfaceMark || r2->SendNSECNow == intf->InterfaceID) - r2->SendNSECNow = mDNSNULL; - } - } - - if (m->omsg.h.numAnswers || m->omsg.h.numAdditionals) - { - // If we have data to send, add OWNER option if necessary, then send packet - - if (OwnerRecordSpace) - { - AuthRecord opt; - mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); - opt.resrec.rrclass = NormalMaxDNSMessageData; - opt.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record - opt.resrec.rdestimate = sizeof(rdataOPT); - SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]); - newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAdditionals, &opt.resrec); - if (newptr) { responseptr = newptr; LogSPS("SendResponses put %s", ARDisplayString(m, &opt)); } - else if (m->omsg.h.numAnswers + m->omsg.h.numAuthorities + m->omsg.h.numAdditionals == 1) - LogSPS("SendResponses: No space in packet for Owner OPT record (%d/%d/%d/%d) %s", - m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); - else - LogMsg("SendResponses: How did we fail to have space for Owner OPT record (%d/%d/%d/%d) %s", - m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); - } - - 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", - m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s", intf->InterfaceID); - - if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, mDNSNULL, &AllDNSLinkGroup_v4, MulticastDNSPort, mDNSNULL, mDNSNULL); - if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, mDNSNULL, &AllDNSLinkGroup_v6, MulticastDNSPort, mDNSNULL, 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 - { - const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); - #if MDNS_DEBUGMSGS && 0 - const char *const msg = next ? "SendResponses: Nothing more on %p; moving to %p" : "SendResponses: Nothing more on %p"; - debugf(msg, intf, next); - #endif - intf = next; - pktcount = 0; // When we move to a new interface, reset packet count back to zero -- NSEC generation logic uses it - } - } - - // *** - // *** 3. Cleanup: Now that everything is sent, call client callback functions, and reset state variables - // *** - - if (m->CurrentRecord) - LogMsg("SendResponses ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); - m->CurrentRecord = m->ResourceRecords; - while (m->CurrentRecord) - { - rr = m->CurrentRecord; - m->CurrentRecord = rr->next; - - if (rr->SendRNow) - { - if (rr->ARType != AuthRecordLocalOnly && rr->ARType != AuthRecordP2P) - LogMsg("SendResponses: No active interface %p to send: %p %02X %s", rr->SendRNow, rr->resrec.InterfaceID, rr->resrec.RecordType, ARDisplayString(m, rr)); - rr->SendRNow = mDNSNULL; - } - - if (rr->ImmedAnswer || rr->resrec.RecordType == kDNSRecordTypeDeregistering) - { - if (rr->NewRData) CompleteRDataUpdate(m, rr); // Update our rdata, clear the NewRData pointer, and return memory to the client - - if (rr->resrec.RecordType == kDNSRecordTypeDeregistering && rr->AnnounceCount == 0) - { - // For Unicast, when we get the response from the server, we will call CompleteDeregistration - if (!AuthRecord_uDNS(rr)) 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 %ld ticks", m->NextScheduledResponse - m->timenow); - } +{ + int pktcount = 0; + AuthRecord *rr, *r2; + mDNSs32 maxExistingAnnounceInterval = 0; + const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces); + + m->NextScheduledResponse = m->timenow + 0x78000000; + + if (m->SleepState == SleepState_Transferring) RetrySPSRegistrations(m); + + 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 + // *** + + // Run through our list of records, and decide which ones we're going to announce on all interfaces + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr); + if (TimeToAnnounceThisRecord(rr, m->timenow)) + { + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) + { + if (!rr->WakeUp.HMAC.l[0]) + { + if (rr->AnnounceCount) rr->ImmedAnswer = mDNSInterfaceMark; // Send goodbye packet on all interfaces + } + else + { + LogSPS("SendResponses: Sending wakeup %2d for %.6a %s", rr->AnnounceCount-3, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + SendWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.IMAC, &rr->WakeUp.password); + for (r2 = rr; r2; r2=r2->next) + if (r2->AnnounceCount && r2->resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&r2->WakeUp.IMAC, &rr->WakeUp.IMAC)) + { + // For now we only want to send a single Unsolicited Neighbor Advertisement restoring the address to the original + // owner, because these packets can cause some IPv6 stacks to falsely conclude that there's an address conflict. + if (r2->AddressProxy.type == mDNSAddrType_IPv6 && r2->AnnounceCount == WakeupCount) + { + LogSPS("NDP Announcement %2d Releasing traffic for H-MAC %.6a I-MAC %.6a %s", + r2->AnnounceCount-3, &r2->WakeUp.HMAC, &r2->WakeUp.IMAC, ARDisplayString(m,r2)); + SendNDP(m, NDP_Adv, NDP_Override, r2, &r2->AddressProxy.ip.v6, &r2->WakeUp.IMAC, &AllHosts_v6, &AllHosts_v6_Eth); + } + r2->LastAPTime = m->timenow; + // After 15 wakeups without success (maybe host has left the network) send three goodbyes instead + if (--r2->AnnounceCount <= GoodbyeCount) r2->WakeUp.HMAC = zeroEthAddr; + } + } + } + else if (ResourceRecordIsValidAnswer(rr)) + { + if (rr->AddressProxy.type) + { + rr->AnnounceCount--; + rr->ThisAPInterval *= 2; + rr->LastAPTime = m->timenow; + if (rr->AddressProxy.type == mDNSAddrType_IPv4) + { + LogSPS("ARP Announcement %2d Capturing traffic for H-MAC %.6a I-MAC %.6a %s", + rr->AnnounceCount, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr)); + SendARP(m, 1, rr, &rr->AddressProxy.ip.v4, &zeroEthAddr, &rr->AddressProxy.ip.v4, &onesEthAddr); + } + else if (rr->AddressProxy.type == mDNSAddrType_IPv6) + { + LogSPS("NDP Announcement %2d Capturing traffic for H-MAC %.6a I-MAC %.6a %s", + rr->AnnounceCount, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr)); + SendNDP(m, NDP_Adv, NDP_Override, rr, &rr->AddressProxy.ip.v6, mDNSNULL, &AllHosts_v6, &AllHosts_v6_Eth); + } + } + else + { + rr->ImmedAnswer = mDNSInterfaceMark; // Send on all interfaces + if (maxExistingAnnounceInterval < rr->ThisAPInterval) + maxExistingAnnounceInterval = rr->ThisAPInterval; + if (rr->UpdateBlocked) rr->UpdateBlocked = 0; + } + } + } + } + + // Any interface-specific records we're going to send are marked as being sent on all appropriate interfaces (which is just one) + // Eligible records that are more than half-way to their announcement time are accelerated + for (rr = m->ResourceRecords; rr; rr=rr->next) + if ((rr->resrec.InterfaceID && rr->ImmedAnswer) || + (rr->ThisAPInterval <= maxExistingAnnounceInterval && + TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2) && + !rr->AddressProxy.type && // Don't include ARP Annoucements when considering which records to accelerate + ResourceRecordIsValidAnswer(rr))) + rr->ImmedAnswer = mDNSInterfaceMark; // Send on all interfaces + + // When sending SRV records (particularly when announcing a new service) automatically add related Address record(s) as additionals + // Note: Currently all address records are interface-specific, so it's safe to set ImmedAdditional to their InterfaceID, + // which will be non-null. If by some chance there is an address record that's not interface-specific (should never happen) + // then all that means is that it won't get sent -- which would not be the end of the world. + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if (rr->ImmedAnswer && rr->resrec.rrtype == kDNSType_SRV) + for (r2=m->ResourceRecords; r2; r2=r2->next) // Scan list of resource records + if (RRTypeIsAddressType(r2->resrec.rrtype) && // For all address records (A/AAAA) ... + ResourceRecordIsValidAnswer(r2) && // ... which are valid for answer ... + rr->LastMCTime - r2->LastMCTime >= 0 && // ... which we have not sent recently ... + rr->resrec.rdatahash == r2->resrec.namehash && // ... whose name is the name of the SRV target + SameDomainName(&rr->resrec.rdata->u.srv.target, r2->resrec.name) && + (rr->ImmedAnswer == mDNSInterfaceMark || rr->ImmedAnswer == r2->resrec.InterfaceID)) + r2->ImmedAdditional = r2->resrec.InterfaceID; // ... then mark this address record for sending too + // We also make sure we send the DeviceInfo TXT record too, if necessary + // We check for RecordType == kDNSRecordTypeShared because we don't want to tag the + // DeviceInfo TXT record onto a goodbye packet (RecordType == kDNSRecordTypeDeregistering). + if (rr->ImmedAnswer && rr->resrec.RecordType == kDNSRecordTypeShared && rr->resrec.rrtype == kDNSType_PTR) + if (ResourceRecordIsValidAnswer(&m->DeviceInfo) && SameDomainLabel(rr->resrec.rdata->u.name.c, m->DeviceInfo.resrec.name->c)) + { + if (!m->DeviceInfo.ImmedAnswer) m->DeviceInfo.ImmedAnswer = rr->ImmedAnswer; + else m->DeviceInfo.ImmedAnswer = mDNSInterfaceMark; + } + } + + // If there's a record which is supposed to be unique that we're going to send, then make sure that we give + // the whole RRSet as an atomic unit. That means that if we have any other records with the same name/type/class + // then we need to mark them for sending too. Otherwise, if we set the kDNSClass_UniqueRRSet bit on a + // record, then other RRSet members that have not been sent recently will get flushed out of client caches. + // -- If a record is marked to be sent on a certain interface, make sure the whole set is marked to be sent on that interface + // -- If any record is marked to be sent on all interfaces, make sure the whole set is marked to be sent on all interfaces + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + { + if (rr->ImmedAnswer) // If we're sending this as answer, see that its whole RRSet is similarly marked + { + for (r2 = m->ResourceRecords; r2; r2=r2->next) + if (ResourceRecordIsValidAnswer(r2)) + if (r2->ImmedAnswer != mDNSInterfaceMark && + r2->ImmedAnswer != rr->ImmedAnswer && SameResourceRecordSignature(r2, rr)) + r2->ImmedAnswer = !r2->ImmedAnswer ? rr->ImmedAnswer : mDNSInterfaceMark; + } + else if (rr->ImmedAdditional) // If we're sending this as additional, see that its whole RRSet is similarly marked + { + for (r2 = m->ResourceRecords; r2; r2=r2->next) + if (ResourceRecordIsValidAnswer(r2)) + if (r2->ImmedAdditional != rr->ImmedAdditional && SameResourceRecordSignature(r2, rr)) + r2->ImmedAdditional = rr->ImmedAdditional; + } + } + + // Now set SendRNow state appropriately + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if (rr->ImmedAnswer == mDNSInterfaceMark) // Sending this record on all appropriate interfaces + { + rr->SendRNow = !intf ? mDNSNULL : (rr->resrec.InterfaceID) ? rr->resrec.InterfaceID : intf->InterfaceID; + rr->ImmedAdditional = mDNSNULL; // No need to send as additional if sending as answer + rr->LastMCTime = m->timenow; + rr->LastMCInterface = rr->ImmedAnswer; + // If we're announcing this record, and it's at least half-way to its ordained time, then consider this announcement done + if (TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2)) + { + rr->AnnounceCount--; + if (rr->resrec.RecordType != kDNSRecordTypeDeregistering) + rr->ThisAPInterval *= 2; + rr->LastAPTime = m->timenow; + debugf("Announcing %##s (%s) %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->AnnounceCount); + } + } + else if (rr->ImmedAnswer) // Else, just respond to a single query on single interface: + { + rr->SendRNow = rr->ImmedAnswer; // Just respond on that interface + rr->ImmedAdditional = mDNSNULL; // No need to send as additional too + rr->LastMCTime = m->timenow; + rr->LastMCInterface = rr->ImmedAnswer; + } + SetNextAnnounceProbeTime(m, rr); + //if (rr->SendRNow) LogMsg("%-15.4a %s", &rr->v4Requester, ARDisplayString(m, rr)); + } + + // *** + // *** 2. Loop through interface list, sending records as appropriate + // *** + + while (intf) + { + const int OwnerRecordSpace = (m->AnnounceOwner && intf->MAC.l[0]) ? DNSOpt_Header_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) : 0; + int numDereg = 0; + int numAnnounce = 0; + int numAnswer = 0; + mDNSu8 *responseptr = m->omsg.data; + mDNSu8 *newptr; + InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags); + + // First Pass. Look for: + // 1. Deregistering records that need to send their goodbye packet + // 2. Updated records that need to retract their old data + // 3. Answers and announcements we need to send + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + + // Skip this interface if the record InterfaceID is *Any and the record is not + // appropriate for the interface type. + if ((rr->SendRNow == intf->InterfaceID) && + ((rr->resrec.InterfaceID == mDNSInterface_Any) && !mDNSPlatformValidRecordForInterface(rr, intf))) + { + LogInfo("SendResponses: Not sending %s, on %s", ARDisplayString(m, rr), InterfaceNameForID(m, rr->SendRNow)); + rr->SendRNow = GetNextActiveInterfaceID(intf); + } + else if (rr->SendRNow == intf->InterfaceID) + { + RData *OldRData = rr->resrec.rdata; + mDNSu16 oldrdlength = rr->resrec.rdlength; + mDNSu8 active = (mDNSu8) + (rr->resrec.RecordType != kDNSRecordTypeDeregistering && + (m->SleepState != SleepState_Sleeping || intf->SPSAddr[0].type || intf->SPSAddr[1].type || intf->SPSAddr[2].type)); + newptr = mDNSNULL; + if (rr->NewRData && active) + { + // See if we should send a courtesy "goodbye" for the old data before we replace it. + if (ResourceRecordIsValidAnswer(rr) && rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) + { + newptr = PutRR_OS_TTL(responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0); + if (newptr) { responseptr = newptr; numDereg++; rr->RequireGoodbye = mDNSfalse; } + else continue; // If this packet is already too full to hold the goodbye for this record, skip it for now and we'll retry later + } + SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); + } + + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it + newptr = PutRR_OS_TTL(responseptr, &m->omsg.h.numAnswers, &rr->resrec, active ? rr->resrec.rroriginalttl : 0); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state + if (newptr) + { + responseptr = newptr; + rr->RequireGoodbye = active; + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) numDereg++; + else if (rr->LastAPTime == m->timenow) numAnnounce++;else numAnswer++; + } + + if (rr->NewRData && active) + SetNewRData(&rr->resrec, OldRData, oldrdlength); + + // The first time through (pktcount==0), if this record is verified unique + // (i.e. typically A, AAAA, SRV, TXT and reverse-mapping PTR), set the flag to add an NSEC too. + if (!pktcount && active && (rr->resrec.RecordType & kDNSRecordTypeActiveUniqueMask) && !rr->SendNSECNow) + rr->SendNSECNow = mDNSInterfaceMark; + + if (newptr) // If succeeded in sending, advance to next interface + { + // If sending on all interfaces, go to next interface; else we're finished now + if (rr->ImmedAnswer == mDNSInterfaceMark && rr->resrec.InterfaceID == mDNSInterface_Any) + rr->SendRNow = GetNextActiveInterfaceID(intf); + else + rr->SendRNow = mDNSNULL; + } + } + } + + // Second Pass. Add additional records, if there's space. + newptr = responseptr; + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->ImmedAdditional == intf->InterfaceID) + if (ResourceRecordIsValidAnswer(rr)) + { + // 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)) + { + const AuthRecord *a; + for (a = m->ResourceRecords; a; a=a->next) + if (a->LastMCTime == m->timenow && + a->LastMCInterface == intf->InterfaceID && + SameResourceRecordSignature(a, rr)) { SendAdditional = mDNStrue; break; } + } + 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 + { + // The first time through (pktcount==0), if this record is verified unique + // (i.e. typically A, AAAA, SRV, TXT and reverse-mapping PTR), set the flag to add an NSEC too. + if (!pktcount && (rr->resrec.RecordType & kDNSRecordTypeActiveUniqueMask) && !rr->SendNSECNow) + rr->SendNSECNow = mDNSInterfaceMark; + + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it + newptr = PutRR_OS(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; + } + } + } + + // Third Pass. Add NSEC records, if there's space. + // When we're generating an NSEC record in response to a specify query for that type + // (recognized by rr->SendNSECNow == intf->InterfaceID) we should really put the NSEC in the Answer Section, + // not Additional Section, but for now it's easier to handle both cases in this Additional Section loop here. + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->SendNSECNow == mDNSInterfaceMark || rr->SendNSECNow == intf->InterfaceID) + { + AuthRecord nsec; + mDNSu8 *ptr; + int len; + mDNS_SetupResourceRecord(&nsec, mDNSNULL, mDNSInterface_Any, kDNSType_NSEC, rr->resrec.rroriginalttl, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + nsec.resrec.rrclass |= kDNSClass_UniqueRRSet; + AssignDomainName(&nsec.namestorage, rr->resrec.name); + ptr = nsec.rdatastorage.u.data; + len = DomainNameLength(rr->resrec.name); + // We have a nxt name followed by window number, window length and a window bitmap + nsec.resrec.rdlength = len + 2 + NSEC_MCAST_WINDOW_SIZE; + if (nsec.resrec.rdlength <= StandardAuthRDSize) + { + mDNSPlatformMemZero(ptr, nsec.resrec.rdlength); + AssignDomainName((domainname *)ptr, rr->resrec.name); + ptr += len; + *ptr++ = 0; // window number + *ptr++ = NSEC_MCAST_WINDOW_SIZE; // window length + for (r2 = m->ResourceRecords; r2; r2=r2->next) + if (ResourceRecordIsValidAnswer(r2) && SameResourceRecordNameClassInterface(r2, rr)) + { + if (r2->resrec.rrtype >= kDNSQType_ANY) { LogMsg("SendResponses: Can't create NSEC for record %s", ARDisplayString(m, r2)); break; } + else ptr[r2->resrec.rrtype >> 3] |= 128 >> (r2->resrec.rrtype & 7); + } + newptr = responseptr; + if (!r2) // If we successfully built our NSEC record, add it to the packet now + { + newptr = PutRR_OS(responseptr, &m->omsg.h.numAdditionals, &nsec.resrec); + if (newptr) responseptr = newptr; + } + } + else LogMsg("SendResponses: not enough space (%d) in authrecord for nsec", nsec.resrec.rdlength); + + // If we successfully put the NSEC record, clear the SendNSECNow flag + // If we consider this NSEC optional, then we unconditionally clear the SendNSECNow flag, even if we fail to put this additional record + if (newptr || rr->SendNSECNow == mDNSInterfaceMark) + { + rr->SendNSECNow = mDNSNULL; + // Run through remainder of list clearing SendNSECNow flag for all other records which would generate the same NSEC + for (r2 = rr->next; r2; r2=r2->next) + if (SameResourceRecordNameClassInterface(r2, rr)) + if (r2->SendNSECNow == mDNSInterfaceMark || r2->SendNSECNow == intf->InterfaceID) + r2->SendNSECNow = mDNSNULL; + } + } + + if (m->omsg.h.numAnswers || m->omsg.h.numAdditionals) + { + // If we have data to send, add OWNER option if necessary, then send packet + + if (OwnerRecordSpace) + { + AuthRecord opt; + mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + opt.resrec.rrclass = NormalMaxDNSMessageData; + opt.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record + opt.resrec.rdestimate = sizeof(rdataOPT); + SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]); + newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAdditionals, &opt.resrec); + if (newptr) { responseptr = newptr; LogSPS("SendResponses put %s", ARDisplayString(m, &opt)); } + else if (m->omsg.h.numAnswers + m->omsg.h.numAuthorities + m->omsg.h.numAdditionals == 1) + LogSPS("SendResponses: No space in packet for Owner OPT record (%d/%d/%d/%d) %s", + m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); + else + LogMsg("SendResponses: How did we fail to have space for Owner OPT record (%d/%d/%d/%d) %s", + m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); + } + + 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", + m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s", intf->InterfaceID); + + if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, mDNSNULL, &AllDNSLinkGroup_v4, MulticastDNSPort, mDNSNULL, mDNSNULL, mDNSfalse); + if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, mDNSNULL, &AllDNSLinkGroup_v6, MulticastDNSPort, mDNSNULL, mDNSNULL, mDNSfalse); + 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 + { + const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); + #if MDNS_DEBUGMSGS && 0 + const char *const msg = next ? "SendResponses: Nothing more on %p; moving to %p" : "SendResponses: Nothing more on %p"; + debugf(msg, intf, next); + #endif + intf = next; + pktcount = 0; // When we move to a new interface, reset packet count back to zero -- NSEC generation logic uses it + } + } + + // *** + // *** 3. Cleanup: Now that everything is sent, call client callback functions, and reset state variables + // *** + + if (m->CurrentRecord) + LogMsg("SendResponses ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + + if (rr->SendRNow) + { + if (rr->ARType != AuthRecordLocalOnly && rr->ARType != AuthRecordP2P) + LogMsg("SendResponses: No active interface %p to send: %p %02X %s", rr->SendRNow, rr->resrec.InterfaceID, rr->resrec.RecordType, ARDisplayString(m, rr)); + rr->SendRNow = mDNSNULL; + } + + if (rr->ImmedAnswer || rr->resrec.RecordType == kDNSRecordTypeDeregistering) + { + if (rr->NewRData) CompleteRDataUpdate(m, rr); // Update our rdata, clear the NewRData pointer, and return memory to the client + + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering && rr->AnnounceCount == 0) + { + // For Unicast, when we get the response from the server, we will call CompleteDeregistration + if (!AuthRecord_uDNS(rr)) 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 %ld ticks", m->NextScheduledResponse - m->timenow); +} // Calling CheckCacheExpiration() is an expensive operation because it has to look at the entire cache, // so we want to be lazy about how frequently we do it. @@ -2479,42 +2543,42 @@ mDNSlocal void SendResponses(mDNS *const m) // 5. For records with rroriginalttl set to zero, that means we really want to delete them immediately // (we have a new record with DelayDelivery set, waiting for the old record to go away before we can notify clients). #define CacheCheckGracePeriod(RR) ( \ - ((RR)->CRActiveQuestion == mDNSNULL ) ? (60 * mDNSPlatformOneSecond) : \ - ((RR)->UnansweredQueries < MaxUnansweredQueries) ? (TicksTTL(rr)/50) : \ - ((RR)->resrec.rroriginalttl > 10 ) ? (mDNSPlatformOneSecond) : \ - ((RR)->resrec.rroriginalttl > 0 ) ? (mDNSPlatformOneSecond/10) : 0) + ((RR)->CRActiveQuestion == mDNSNULL ) ? (60 * mDNSPlatformOneSecond) : \ + ((RR)->UnansweredQueries < MaxUnansweredQueries) ? (TicksTTL(rr)/50) : \ + ((RR)->resrec.rroriginalttl > 10 ) ? (mDNSPlatformOneSecond) : \ + ((RR)->resrec.rroriginalttl > 0 ) ? (mDNSPlatformOneSecond/10) : 0) #define NextCacheCheckEvent(RR) ((RR)->NextRequiredQuery + CacheCheckGracePeriod(RR)) mDNSexport void ScheduleNextCacheCheckTime(mDNS *const m, const mDNSu32 slot, const mDNSs32 event) - { - if (m->rrcache_nextcheck[slot] - event > 0) - m->rrcache_nextcheck[slot] = event; - if (m->NextCacheCheck - event > 0) - m->NextCacheCheck = event; - } +{ + if (m->rrcache_nextcheck[slot] - event > 0) + m->rrcache_nextcheck[slot] = event; + if (m->NextCacheCheck - event > 0) + m->NextCacheCheck = event; +} // Note: MUST call SetNextCacheCheckTimeForRecord any time we change: // rr->TimeRcvd // rr->resrec.rroriginalttl // rr->UnansweredQueries // rr->CRActiveQuestion -mDNSlocal void SetNextCacheCheckTimeForRecord(mDNS *const m, CacheRecord *const rr) - { - rr->NextRequiredQuery = RRExpireTime(rr); - - // If we have an active question, then see if we want to schedule a refresher query for this record. - // Usually we expect to do four queries, at 80-82%, 85-87%, 90-92% and then 95-97% of the TTL. - if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) - { - rr->NextRequiredQuery -= TicksTTL(rr)/20 * (MaxUnansweredQueries - rr->UnansweredQueries); - rr->NextRequiredQuery += mDNSRandom((mDNSu32)TicksTTL(rr)/50); - verbosedebugf("SetNextCacheCheckTimeForRecord: NextRequiredQuery in %ld sec CacheCheckGracePeriod %d ticks for %s", - (rr->NextRequiredQuery - m->timenow) / mDNSPlatformOneSecond, CacheCheckGracePeriod(rr), CRDisplayString(m,rr)); - } - - ScheduleNextCacheCheckTime(m, HashSlot(rr->resrec.name), NextCacheCheckEvent(rr)); - } +mDNSexport void SetNextCacheCheckTimeForRecord(mDNS *const m, CacheRecord *const rr) +{ + rr->NextRequiredQuery = RRExpireTime(rr); + + // If we have an active question, then see if we want to schedule a refresher query for this record. + // Usually we expect to do four queries, at 80-82%, 85-87%, 90-92% and then 95-97% of the TTL. + if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) + { + rr->NextRequiredQuery -= TicksTTL(rr)/20 * (MaxUnansweredQueries - rr->UnansweredQueries); + rr->NextRequiredQuery += mDNSRandom((mDNSu32)TicksTTL(rr)/50); + verbosedebugf("SetNextCacheCheckTimeForRecord: NextRequiredQuery in %ld sec CacheCheckGracePeriod %d ticks for %s", + (rr->NextRequiredQuery - m->timenow) / mDNSPlatformOneSecond, CacheCheckGracePeriod(rr), CRDisplayString(m,rr)); + } + + ScheduleNextCacheCheckTime(m, HashSlot(rr->resrec.name), NextCacheCheckEvent(rr)); +} #define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5) #define kDefaultReconfirmTimeForWake ((mDNSu32)mDNSPlatformOneSecond * 5) @@ -2522,28 +2586,28 @@ mDNSlocal void SetNextCacheCheckTimeForRecord(mDNS *const m, CacheRecord *const #define kDefaultReconfirmTimeForFlappingInterface ((mDNSu32)mDNSPlatformOneSecond * 30) mDNSlocal mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSu32 interval) - { - if (interval < kMinimumReconfirmTime) - interval = kMinimumReconfirmTime; - if (interval > 0x10000000) // Make sure interval doesn't overflow when we multiply by four below - interval = 0x10000000; - - // If the expected expiration time for this record is more than interval+33%, then accelerate its expiration - if (RRExpireTime(rr) - m->timenow > (mDNSs32)((interval * 4) / 3)) - { - // Add a 33% random amount to the interval, to avoid synchronization between multiple hosts - // For all the reconfirmations in a given batch, we want to use the same random value - // so that the reconfirmation questions can be grouped into a single query packet - if (!m->RandomReconfirmDelay) m->RandomReconfirmDelay = 1 + mDNSRandom(0x3FFFFFFF); - interval += m->RandomReconfirmDelay % ((interval/3) + 1); - rr->TimeRcvd = m->timenow - (mDNSs32)interval * 3; - rr->resrec.rroriginalttl = (interval * 4 + mDNSPlatformOneSecond - 1) / mDNSPlatformOneSecond; - SetNextCacheCheckTimeForRecord(m, rr); - } - debugf("mDNS_Reconfirm_internal:%6ld ticks to go for %s %p", - RRExpireTime(rr) - m->timenow, CRDisplayString(m, rr), rr->CRActiveQuestion); - return(mStatus_NoError); - } +{ + if (interval < kMinimumReconfirmTime) + interval = kMinimumReconfirmTime; + if (interval > 0x10000000) // Make sure interval doesn't overflow when we multiply by four below + interval = 0x10000000; + + // If the expected expiration time for this record is more than interval+33%, then accelerate its expiration + if (RRExpireTime(rr) - m->timenow > (mDNSs32)((interval * 4) / 3)) + { + // Add a 33% random amount to the interval, to avoid synchronization between multiple hosts + // For all the reconfirmations in a given batch, we want to use the same random value + // so that the reconfirmation questions can be grouped into a single query packet + if (!m->RandomReconfirmDelay) m->RandomReconfirmDelay = 1 + mDNSRandom(0x3FFFFFFF); + interval += m->RandomReconfirmDelay % ((interval/3) + 1); + rr->TimeRcvd = m->timenow - (mDNSs32)interval * 3; + rr->resrec.rroriginalttl = (interval * 4 + mDNSPlatformOneSecond - 1) / mDNSPlatformOneSecond; + SetNextCacheCheckTimeForRecord(m, rr); + } + debugf("mDNS_Reconfirm_internal:%6ld ticks to go for %s %p", + RRExpireTime(rr) - m->timenow, CRDisplayString(m, rr), rr->CRActiveQuestion); + return(mStatus_NoError); +} #define MaxQuestionInterval (3600 * mDNSPlatformOneSecond) @@ -2551,76 +2615,76 @@ mDNSlocal mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, // It also appends to the list of known answer records that need to be included, // and updates the forcast for the size of the known answer section. mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **queryptr, DNSQuestion *q, - CacheRecord ***kalistptrptr, mDNSu32 *answerforecast) - { - mDNSBool ucast = (q->LargeAnswers || q->RequestUnicast) && m->CanReceiveUnicastOn5353; - mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); - const mDNSu8 *const limit = query->data + NormalMaxDNSMessageData; - mDNSu8 *newptr = putQuestion(query, *queryptr, limit - *answerforecast, &q->qname, q->qtype, (mDNSu16)(q->qclass | ucbit)); - if (!newptr) - { - debugf("BuildQuestion: No more space in this packet for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - return(mDNSfalse); - } - else - { - mDNSu32 forecast = *answerforecast; - const mDNSu32 slot = HashSlot(&q->qname); - const CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); - CacheRecord *rr; - CacheRecord **ka = *kalistptrptr; // Make a working copy of the pointer we're going to update - - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // If we have a resource record in our cache, - if (rr->resrec.InterfaceID == q->SendQNow && // received on this interface - !(rr->resrec.RecordType & kDNSRecordTypeUniqueMask) && // which is a shared (i.e. not unique) record type - rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList && // which is not already in the known answer list - rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet - SameNameRecordAnswersQuestion(&rr->resrec, q) && // which answers our question - rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow > // and its half-way-to-expiry time is at least 1 second away - mDNSPlatformOneSecond) // (also ensures we never include goodbye records with TTL=1) - { - // We don't want to include unique records in the Known Answer section. The Known Answer section - // is intended to suppress floods of shared-record replies from many other devices on the network. - // That concept really does not apply to unique records, and indeed if we do send a query for - // which we have a unique record already in our cache, then including that unique record as a - // Known Answer, so as to suppress the only answer we were expecting to get, makes little sense. - - *ka = rr; // Link this record into our known answer chain - ka = &rr->NextInKAList; - // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) - forecast += 12 + rr->resrec.rdestimate; - // If we're trying to put more than one question in this packet, and it doesn't fit - // then undo that last question and try again next time - if (query->h.numQuestions > 1 && newptr + forecast >= limit) - { - debugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d", - q->qname.c, DNSTypeName(q->qtype), newptr + forecast - query->data); - query->h.numQuestions--; - ka = *kalistptrptr; // Go back to where we started and retract these answer records - while (*ka) { CacheRecord *c = *ka; *ka = mDNSNULL; ka = &c->NextInKAList; } - return(mDNSfalse); // Return false, so we'll try again in the next packet - } - } - - // Success! Update our state pointers, increment UnansweredQueries as appropriate, and return - *queryptr = newptr; // Update the packet pointer - *answerforecast = forecast; // Update the forecast - *kalistptrptr = ka; // Update the known answer list pointer - if (ucast) q->ExpectUnicastResp = NonZeroTime(m->timenow); - - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // For every resource record in our cache, - if (rr->resrec.InterfaceID == q->SendQNow && // received on this interface - rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList && // which is not in the known answer list - SameNameRecordAnswersQuestion(&rr->resrec, q)) // which answers our question - { - rr->UnansweredQueries++; // indicate that we're expecting a response - rr->LastUnansweredTime = m->timenow; - SetNextCacheCheckTimeForRecord(m, rr); - } - - return(mDNStrue); - } - } + CacheRecord ***kalistptrptr, mDNSu32 *answerforecast) +{ + mDNSBool ucast = (q->LargeAnswers || q->RequestUnicast) && m->CanReceiveUnicastOn5353; + mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); + const mDNSu8 *const limit = query->data + NormalMaxDNSMessageData; + mDNSu8 *newptr = putQuestion(query, *queryptr, limit - *answerforecast, &q->qname, q->qtype, (mDNSu16)(q->qclass | ucbit)); + if (!newptr) + { + debugf("BuildQuestion: No more space in this packet for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + return(mDNSfalse); + } + else + { + mDNSu32 forecast = *answerforecast; + const mDNSu32 slot = HashSlot(&q->qname); + const CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + CacheRecord *rr; + CacheRecord **ka = *kalistptrptr; // Make a working copy of the pointer we're going to update + + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // If we have a resource record in our cache, + if (rr->resrec.InterfaceID == q->SendQNow && // received on this interface + !(rr->resrec.RecordType & kDNSRecordTypeUniqueMask) && // which is a shared (i.e. not unique) record type + rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList && // which is not already in the known answer list + rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet + SameNameRecordAnswersQuestion(&rr->resrec, q) && // which answers our question + rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow > // and its half-way-to-expiry time is at least 1 second away + mDNSPlatformOneSecond) // (also ensures we never include goodbye records with TTL=1) + { + // We don't want to include unique records in the Known Answer section. The Known Answer section + // is intended to suppress floods of shared-record replies from many other devices on the network. + // That concept really does not apply to unique records, and indeed if we do send a query for + // which we have a unique record already in our cache, then including that unique record as a + // Known Answer, so as to suppress the only answer we were expecting to get, makes little sense. + + *ka = rr; // Link this record into our known answer chain + ka = &rr->NextInKAList; + // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) + forecast += 12 + rr->resrec.rdestimate; + // If we're trying to put more than one question in this packet, and it doesn't fit + // then undo that last question and try again next time + if (query->h.numQuestions > 1 && newptr + forecast >= limit) + { + debugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d", + q->qname.c, DNSTypeName(q->qtype), newptr + forecast - query->data); + query->h.numQuestions--; + ka = *kalistptrptr; // Go back to where we started and retract these answer records + while (*ka) { CacheRecord *c = *ka; *ka = mDNSNULL; ka = &c->NextInKAList; } + return(mDNSfalse); // Return false, so we'll try again in the next packet + } + } + + // Success! Update our state pointers, increment UnansweredQueries as appropriate, and return + *queryptr = newptr; // Update the packet pointer + *answerforecast = forecast; // Update the forecast + *kalistptrptr = ka; // Update the known answer list pointer + if (ucast) q->ExpectUnicastResp = NonZeroTime(m->timenow); + + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // For every resource record in our cache, + if (rr->resrec.InterfaceID == q->SendQNow && // received on this interface + rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList && // which is not in the known answer list + SameNameRecordAnswersQuestion(&rr->resrec, q)) // which answers our question + { + rr->UnansweredQueries++; // indicate that we're expecting a response + rr->LastUnansweredTime = m->timenow; + SetNextCacheCheckTimeForRecord(m, rr); + } + + return(mDNStrue); + } +} // When we have a query looking for a specified name, but there appear to be no answers with // that name, ReconfirmAntecedents() is called with depth=0 to start the reconfirmation process @@ -2635,193 +2699,222 @@ mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **quer // Currently depths 4 and 5 are not expected to occur; if we did get to depth 5 we'd reconfim any records we // found referring to the given name, but not recursively descend any further reconfirm *their* antecedents. mDNSlocal void ReconfirmAntecedents(mDNS *const m, const domainname *const name, const mDNSu32 namehash, const int depth) - { - mDNSu32 slot; - CacheGroup *cg; - CacheRecord *cr; - debugf("ReconfirmAntecedents (depth=%d) for %##s", depth, name->c); - FORALL_CACHERECORDS(slot, cg, cr) - { - domainname *crtarget = GetRRDomainNameTarget(&cr->resrec); - if (crtarget && cr->resrec.rdatahash == namehash && SameDomainName(crtarget, name)) - { - LogInfo("ReconfirmAntecedents: Reconfirming (depth=%d) %s", depth, CRDisplayString(m, cr)); - mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); - if (depth < 5) ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, depth+1); - } - } - } +{ + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *cr; + debugf("ReconfirmAntecedents (depth=%d) for %##s", depth, name->c); + FORALL_CACHERECORDS(slot, cg, cr) + { + domainname *crtarget = GetRRDomainNameTarget(&cr->resrec); + if (crtarget && cr->resrec.rdatahash == namehash && SameDomainName(crtarget, name)) + { + LogInfo("ReconfirmAntecedents: Reconfirming (depth=%d) %s", depth, CRDisplayString(m, cr)); + mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); + if (depth < 5) + ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, depth+1); + } + } +} // If we get no answer for a AAAA query, then before doing an automatic implicit ReconfirmAntecedents // we check if we have an address record for the same name. If we do have an IPv4 address for a given // name but not an IPv6 address, that's okay (it just means the device doesn't do IPv6) so the failure // to get a AAAA response is not grounds to doubt the PTR/SRV chain that lead us to that name. mDNSlocal const CacheRecord *CacheHasAddressTypeForName(mDNS *const m, const domainname *const name, const mDNSu32 namehash) - { - CacheGroup *const cg = CacheGroupForName(m, HashSlot(name), namehash, name); - const CacheRecord *cr = cg ? cg->members : mDNSNULL; - while (cr && !RRTypeIsAddressType(cr->resrec.rrtype)) cr=cr->next; - return(cr); - } +{ + CacheGroup *const cg = CacheGroupForName(m, HashSlot(name), namehash, name); + const CacheRecord *cr = cg ? cg->members : mDNSNULL; + while (cr && !RRTypeIsAddressType(cr->resrec.rrtype)) cr=cr->next; + return(cr); +} mDNSlocal const CacheRecord *FindSPSInCache1(mDNS *const m, const DNSQuestion *const q, const CacheRecord *const c0, const CacheRecord *const c1) - { - CacheGroup *const cg = CacheGroupForName(m, HashSlot(&q->qname), q->qnamehash, &q->qname); - const CacheRecord *cr, *bestcr = mDNSNULL; - mDNSu32 bestmetric = 1000000; - for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) - if (cr->resrec.rrtype == kDNSType_PTR && cr->resrec.rdlength >= 6) // If record is PTR type, with long enough name, - if (cr != c0 && cr != c1) // that's not one we've seen before, - if (SameNameRecordAnswersQuestion(&cr->resrec, q)) // and answers our browse query, - if (!IdenticalSameNameRecord(&cr->resrec, &m->SPSRecords.RR_PTR.resrec)) // and is not our own advertised service... - { - mDNSu32 metric = SPSMetric(cr->resrec.rdata->u.name.c); - if (bestmetric > metric) { bestmetric = metric; bestcr = cr; } - } - return(bestcr); - } +{ + CacheGroup *const cg = CacheGroupForName(m, HashSlot(&q->qname), q->qnamehash, &q->qname); + const CacheRecord *cr, *bestcr = mDNSNULL; + mDNSu32 bestmetric = 1000000; + for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) + if (cr->resrec.rrtype == kDNSType_PTR && cr->resrec.rdlength >= 6) // If record is PTR type, with long enough name, + if (cr != c0 && cr != c1) // that's not one we've seen before, + if (SameNameRecordAnswersQuestion(&cr->resrec, q)) // and answers our browse query, + if (!IdenticalSameNameRecord(&cr->resrec, &m->SPSRecords.RR_PTR.resrec)) // and is not our own advertised service... + { + mDNSu32 metric = SPSMetric(cr->resrec.rdata->u.name.c); + if (bestmetric > metric) { bestmetric = metric; bestcr = cr; } + } + return(bestcr); +} + +mDNSlocal void CheckAndSwapSPS(const CacheRecord *sps1, const CacheRecord *sps2) +{ + const CacheRecord *swap_sps; + mDNSu32 metric1, metric2; + + if (!sps1 || !sps2) return; + metric1 = SPSMetric(sps1->resrec.rdata->u.name.c); + metric2 = SPSMetric(sps2->resrec.rdata->u.name.c); + if (!SPSFeatures(sps1->resrec.rdata->u.name.c) && SPSFeatures(sps2->resrec.rdata->u.name.c) && (metric2 >= metric1)) + { + swap_sps = sps1; + sps1 = sps2; + sps2 = swap_sps; + } +} + +mDNSlocal void ReorderSPSByFeature(const CacheRecord *sps[3]) +{ + CheckAndSwapSPS(sps[0], sps[1]); + CheckAndSwapSPS(sps[0], sps[2]); + CheckAndSwapSPS(sps[1], sps[2]); +} + // Finds the three best Sleep Proxies we currently have in our cache mDNSexport void FindSPSInCache(mDNS *const m, const DNSQuestion *const q, const CacheRecord *sps[3]) - { - sps[0] = FindSPSInCache1(m, q, mDNSNULL, mDNSNULL); - sps[1] = !sps[0] ? mDNSNULL : FindSPSInCache1(m, q, sps[0], mDNSNULL); - sps[2] = !sps[1] ? mDNSNULL : FindSPSInCache1(m, q, sps[0], sps[1]); - } +{ + sps[0] = FindSPSInCache1(m, q, mDNSNULL, mDNSNULL); + sps[1] = !sps[0] ? mDNSNULL : FindSPSInCache1(m, q, sps[0], mDNSNULL); + sps[2] = !sps[1] ? mDNSNULL : FindSPSInCache1(m, q, sps[0], sps[1]); + + // SPS is already sorted by metric. We want to move the entries to the beginning of the array + // only if they have equally good metric and support features. + ReorderSPSByFeature(sps); +} // Only DupSuppressInfos newer than the specified 'time' are allowed to remain active mDNSlocal void ExpireDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 time) - { - int i; - for (i=0; iIPv4Available; // If this interface doesn't do v4, we don't need to find a v4 duplicate of this query - mDNSBool v6 = !intf->IPv6Available; // If this interface doesn't do v6, we don't need to find a v6 duplicate of this query - for (i=0; iInterfaceID) - { - if (ds[i].Type == mDNSAddrType_IPv4) v4 = mDNStrue; - else if (ds[i].Type == mDNSAddrType_IPv6) v6 = mDNStrue; - if (v4 && v6) return(mDNStrue); - } - return(mDNSfalse); - } +{ + int i; + mDNSBool v4 = !intf->IPv4Available; // If this interface doesn't do v4, we don't need to find a v4 duplicate of this query + mDNSBool v6 = !intf->IPv6Available; // If this interface doesn't do v6, we don't need to find a v6 duplicate of this query + for (i=0; iInterfaceID) + { + if (ds[i].Type == mDNSAddrType_IPv4) v4 = mDNStrue; + else if (ds[i].Type == mDNSAddrType_IPv6) v6 = mDNStrue; + if (v4 && v6) return(mDNStrue); + } + return(mDNSfalse); +} mDNSlocal int RecordDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 Time, mDNSInterfaceID InterfaceID, mDNSs32 Type) - { - int i, j; - - // See if we have this one in our list somewhere already - for (i=0; i= DupSuppressInfoSize) - { - i = 0; - for (j=1; j= DupSuppressInfoSize) + { + i = 0; + for (j=1; jInterfaceID; - domainname *d = &q->qname; - - // We can't send magic packets without knowing which interface to send it on. - if (InterfaceID == mDNSInterface_Any || InterfaceID == mDNSInterface_LocalOnly || InterfaceID == mDNSInterface_P2P) - { - LogMsg("mDNSSendWakeOnResolve: ERROR!! Invalid InterfaceID %p for question %##s", InterfaceID, q->qname.c); - return; - } - - // Split MAC@IPAddress and pass them separately - len = d->c[0]; - i = 1; - cnt = 0; - for (i = 1; i < len; i++) - { - if (d->c[i] == '@') - { - char EthAddr[18]; // ethernet adddress : 12 bytes + 5 ":" + 1 NULL byte - char IPAddr[47]; // Max IP address len: 46 bytes (IPv6) + 1 NULL byte - if (cnt != 5) - { - LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed Ethernet address %##s, cnt %d", q->qname.c, cnt); - return; - } - if ((i - 1) > (int) (sizeof(EthAddr) - 1)) - { - LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed Ethernet address %##s, length %d", q->qname.c, i - 1); - return; - } - if ((len - i) > (int)(sizeof(IPAddr) - 1)) - { - LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed IP address %##s, length %d", q->qname.c, len - i); - return; - } - mDNSPlatformMemCopy(EthAddr, &d->c[1], i - 1); - EthAddr[i - 1] = 0; - mDNSPlatformMemCopy(IPAddr, &d->c[i + 1], len - i); - IPAddr[len - i] = 0; - mDNSPlatformSendWakeupPacket(m, InterfaceID, EthAddr, IPAddr, InitialWakeOnResolveCount - q->WakeOnResolveCount); - return; - } - else if (d->c[i] == ':') - cnt++; - } - LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed WakeOnResolve name %##s", q->qname.c); - } - +{ + int len, i, cnt; + mDNSInterfaceID InterfaceID = q->InterfaceID; + domainname *d = &q->qname; + + // We can't send magic packets without knowing which interface to send it on. + if (InterfaceID == mDNSInterface_Any || InterfaceID == mDNSInterface_LocalOnly || InterfaceID == mDNSInterface_P2P) + { + LogMsg("mDNSSendWakeOnResolve: ERROR!! Invalid InterfaceID %p for question %##s", InterfaceID, q->qname.c); + return; + } + + // Split MAC@IPAddress and pass them separately + len = d->c[0]; + i = 1; + cnt = 0; + for (i = 1; i < len; i++) + { + if (d->c[i] == '@') + { + char EthAddr[18]; // ethernet adddress : 12 bytes + 5 ":" + 1 NULL byte + char IPAddr[47]; // Max IP address len: 46 bytes (IPv6) + 1 NULL byte + if (cnt != 5) + { + LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed Ethernet address %##s, cnt %d", q->qname.c, cnt); + return; + } + if ((i - 1) > (int) (sizeof(EthAddr) - 1)) + { + LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed Ethernet address %##s, length %d", q->qname.c, i - 1); + return; + } + if ((len - i) > (int)(sizeof(IPAddr) - 1)) + { + LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed IP address %##s, length %d", q->qname.c, len - i); + return; + } + mDNSPlatformMemCopy(EthAddr, &d->c[1], i - 1); + EthAddr[i - 1] = 0; + mDNSPlatformMemCopy(IPAddr, &d->c[i + 1], len - i); + IPAddr[len - i] = 0; + mDNSPlatformSendWakeupPacket(m, InterfaceID, EthAddr, IPAddr, InitialWakeOnResolveCount - q->WakeOnResolveCount); + return; + } + else if (d->c[i] == ':') + cnt++; + } + LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed WakeOnResolve name %##s", q->qname.c); +} + mDNSlocal mDNSBool AccelerateThisQuery(mDNS *const m, DNSQuestion *q) - { - // If more than 90% of the way to the query time, we should unconditionally accelerate it - if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/10)) - return(mDNStrue); - - // If half-way to next scheduled query time, only accelerate if it will add less than 512 bytes to the packet - if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/2)) - { - // We forecast: qname (n) type (2) class (2) - mDNSu32 forecast = (mDNSu32)DomainNameLength(&q->qname) + 4; - const mDNSu32 slot = HashSlot(&q->qname); - const CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); - const CacheRecord *rr; - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // If we have a resource record in our cache, - if (rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet - SameNameRecordAnswersQuestion(&rr->resrec, q) && // which answers our question - rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow >= 0 && // and it is less than half-way to expiry - rr->NextRequiredQuery - (m->timenow + q->ThisQInterval) > 0)// and we'll ask at least once again before NextRequiredQuery - { - // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) - forecast += 12 + rr->resrec.rdestimate; - if (forecast >= 512) return(mDNSfalse); // If this would add 512 bytes or more to the packet, don't accelerate - } - return(mDNStrue); - } - - return(mDNSfalse); - } +{ + // If more than 90% of the way to the query time, we should unconditionally accelerate it + if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/10)) + return(mDNStrue); + + // If half-way to next scheduled query time, only accelerate if it will add less than 512 bytes to the packet + if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/2)) + { + // We forecast: qname (n) type (2) class (2) + mDNSu32 forecast = (mDNSu32)DomainNameLength(&q->qname) + 4; + const mDNSu32 slot = HashSlot(&q->qname); + const CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + const CacheRecord *rr; + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // If we have a resource record in our cache, + if (rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet + SameNameRecordAnswersQuestion(&rr->resrec, q) && // which answers our question + rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow >= 0 && // and it is less than half-way to expiry + rr->NextRequiredQuery - (m->timenow + q->ThisQInterval) > 0) // and we'll ask at least once again before NextRequiredQuery + { + // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) + forecast += 12 + rr->resrec.rdestimate; + if (forecast >= 512) return(mDNSfalse); // If this would add 512 bytes or more to the packet, don't accelerate + } + return(mDNStrue); + } + + return(mDNSfalse); +} // How Standard Queries are generated: // 1. The Question Section contains the question @@ -2836,448 +2929,483 @@ mDNSlocal mDNSBool AccelerateThisQuery(mDNS *const m, DNSQuestion *q) // planning, in order to apply the tie-breaking rule to see who gets to use the name and who doesn't. mDNSlocal void SendQueries(mDNS *const m) - { - mDNSu32 slot; - CacheGroup *cg; - CacheRecord *cr; - AuthRecord *ar; - int pktcount = 0; - DNSQuestion *q; - // For explanation of maxExistingQuestionInterval logic, see comments for maxExistingAnnounceInterval - mDNSs32 maxExistingQuestionInterval = 0; - const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces); - CacheRecord *KnownAnswerList = mDNSNULL; - - // 1. If time for a query, work out what we need to do - - // We're expecting to send a query anyway, so see if any expiring cache records are close enough - // to their NextRequiredQuery to be worth batching them together with this one - FORALL_CACHERECORDS(slot, cg, cr) - if (cr->CRActiveQuestion && cr->UnansweredQueries < MaxUnansweredQueries) - if (m->timenow + TicksTTL(cr)/50 - cr->NextRequiredQuery >= 0) - { - debugf("Sending %d%% cache expiration query for %s", 80 + 5 * cr->UnansweredQueries, CRDisplayString(m, cr)); - q = cr->CRActiveQuestion; - ExpireDupSuppressInfoOnInterface(q->DupSuppress, m->timenow - TicksTTL(cr)/20, cr->resrec.InterfaceID); - // For uDNS queries (TargetQID non-zero) we adjust LastQTime, - // and bump UnansweredQueries so that we don't spin trying to send the same cache expiration query repeatedly - if (q->Target.type) q->SendQNow = mDNSInterfaceMark; // If targeted query, mark it - else if (!mDNSOpaque16IsZero(q->TargetQID)) { q->LastQTime = m->timenow - q->ThisQInterval; cr->UnansweredQueries++; } - else if (q->SendQNow == mDNSNULL) q->SendQNow = cr->resrec.InterfaceID; - else if (q->SendQNow != cr->resrec.InterfaceID) q->SendQNow = mDNSInterfaceMark; - } - - // Scan our list of questions to see which: - // *WideArea* queries need to be sent - // *unicast* queries need to be sent - // *multicast* queries we're definitely going to send - if (m->CurrentQuestion) - LogMsg("SendQueries ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); - m->CurrentQuestion = m->Questions; - while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) - { - q = m->CurrentQuestion; - if (q->Target.type && (q->SendQNow || TimeToSendThisQuestion(q, m->timenow))) - { - mDNSu8 *qptr = m->omsg.data; - const mDNSu8 *const limit = m->omsg.data + sizeof(m->omsg.data); - - // If we fail to get a new on-demand socket (should only happen cases of the most extreme resource exhaustion), we'll try again next time - if (!q->LocalSocket) q->LocalSocket = mDNSPlatformUDPSocket(m, zeroIPPort); - if (q->LocalSocket) - { - 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->LocalSocket, &q->Target, q->TargetPort, mDNSNULL, mDNSNULL); - q->ThisQInterval *= QuestionIntervalStep; - } - if (q->ThisQInterval > MaxQuestionInterval) - q->ThisQInterval = MaxQuestionInterval; - q->LastQTime = m->timenow; - q->LastQTxTime = m->timenow; - q->RecentAnswerPkts = 0; - q->SendQNow = mDNSNULL; - q->ExpectUnicastResp = NonZeroTime(m->timenow); - } - else if (mDNSOpaque16IsZero(q->TargetQID) && !q->Target.type && TimeToSendThisQuestion(q, m->timenow)) - { - //LogInfo("Time to send %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), m->timenow - NextQSendTime(q)); - q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces - if (maxExistingQuestionInterval < q->ThisQInterval) - maxExistingQuestionInterval = q->ThisQInterval; - } - // If m->CurrentQuestion wasn't modified out from under us, advance it now - // We can't do this at the start of the loop because uDNS_CheckCurrentQuestion() depends on having - // m->CurrentQuestion point to the right question - if (q == m->CurrentQuestion) m->CurrentQuestion = m->CurrentQuestion->next; - } - while (m->CurrentQuestion) - { - LogInfo("SendQueries question loop 1: Skipping NewQuestion %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); - m->CurrentQuestion = m->CurrentQuestion->next; - } - m->CurrentQuestion = mDNSNULL; - - // Scan our list of questions - // (a) to see if there are any more that are worth accelerating, and - // (b) to update the state variables for *all* the questions we're going to send - // Note: Don't set NextScheduledQuery until here, because uDNS_CheckCurrentQuestion in the loop above can add new questions to the list, - // which causes NextScheduledQuery to get (incorrectly) set to m->timenow. Setting it here is the right place, because the very - // next thing we do is scan the list and call SetNextQueryTime() for every question we find, so we know we end up with the right value. - m->NextScheduledQuery = m->timenow + 0x78000000; - for (q = m->Questions; q && q != m->NewQuestions; q=q->next) - { - if (mDNSOpaque16IsZero(q->TargetQID) && (q->SendQNow || - (!q->Target.type && ActiveQuestion(q) && q->ThisQInterval <= maxExistingQuestionInterval && AccelerateThisQuery(m,q)))) - { - // If at least halfway to next query time, advance to next interval - // If less than halfway to next query time, then - // treat this as logically a repeat of the last transmission, without advancing the interval - if (m->timenow - (q->LastQTime + (q->ThisQInterval/2)) >= 0) - { - //LogInfo("Accelerating %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), m->timenow - NextQSendTime(q)); - q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces - debugf("SendQueries: %##s (%s) next interval %d seconds RequestUnicast = %d", - q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval / InitialQuestionInterval, q->RequestUnicast); - q->ThisQInterval *= QuestionIntervalStep; - if (q->ThisQInterval > MaxQuestionInterval) - q->ThisQInterval = MaxQuestionInterval; - else if (q->CurrentAnswers == 0 && q->ThisQInterval == InitialQuestionInterval * QuestionIntervalStep3 && !q->RequestUnicast && - !(RRTypeIsAddressType(q->qtype) && CacheHasAddressTypeForName(m, &q->qname, q->qnamehash))) - { - // Generally don't need to log this. - // It's not especially noteworthy if a query finds no results -- this usually happens for domain - // enumeration queries in the LL subdomain (e.g. "db._dns-sd._udp.0.0.254.169.in-addr.arpa") - // and when there simply happen to be no instances of the service the client is looking - // for (e.g. iTunes is set to look for RAOP devices, and the current network has none). - debugf("SendQueries: Zero current answers for %##s (%s); will reconfirm antecedents", - q->qname.c, DNSTypeName(q->qtype)); - // Sending third query, and no answers yet; time to begin doubting the source - ReconfirmAntecedents(m, &q->qname, q->qnamehash, 0); - } - } - - // Mark for sending. (If no active interfaces, then don't even try.) - q->SendOnAll = (q->SendQNow == mDNSInterfaceMark); - if (q->SendOnAll) - { - q->SendQNow = !intf ? mDNSNULL : (q->InterfaceID) ? q->InterfaceID : intf->InterfaceID; - q->LastQTime = m->timenow; - } - - // If we recorded a duplicate suppression for this question less than half an interval ago, - // then we consider it recent enough that we don't need to do an identical query ourselves. - ExpireDupSuppressInfo(q->DupSuppress, m->timenow - q->ThisQInterval/2); - - q->LastQTxTime = m->timenow; - q->RecentAnswerPkts = 0; - if (q->RequestUnicast) q->RequestUnicast--; - } - // For all questions (not just the ones we're sending) check what the next scheduled event will be - // We don't need to consider NewQuestions here because for those we'll set m->NextScheduledQuery in AnswerNewQuestion - SetNextQueryTime(m,q); - } - - // 2. Scan our authoritative RR list to see what probes we might need to send - - m->NextScheduledProbe = m->timenow + 0x78000000; - - if (m->CurrentRecord) - LogMsg("SendQueries ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); - m->CurrentRecord = m->ResourceRecords; - while (m->CurrentRecord) - { - ar = m->CurrentRecord; - m->CurrentRecord = ar->next; - if (!AuthRecord_uDNS(ar) && ar->resrec.RecordType == kDNSRecordTypeUnique) // For all records that are still probing... - { - // 1. If it's not reached its probe time, just make sure we update m->NextScheduledProbe correctly - if (m->timenow - (ar->LastAPTime + ar->ThisAPInterval) < 0) - { - SetNextAnnounceProbeTime(m, ar); - } - // 2. else, if it has reached its probe time, mark it for sending and then update m->NextScheduledProbe correctly - else if (ar->ProbeCount) - { - if (ar->AddressProxy.type == mDNSAddrType_IPv4) - { - LogSPS("SendQueries ARP Probe %d %s %s", ar->ProbeCount, InterfaceNameForID(m, ar->resrec.InterfaceID), ARDisplayString(m,ar)); - SendARP(m, 1, ar, &zerov4Addr, &zeroEthAddr, &ar->AddressProxy.ip.v4, &ar->WakeUp.IMAC); - } - else if (ar->AddressProxy.type == mDNSAddrType_IPv6) - { - LogSPS("SendQueries NDP Probe %d %s %s", ar->ProbeCount, InterfaceNameForID(m, ar->resrec.InterfaceID), ARDisplayString(m,ar)); - // IPv6 source = zero - // No target hardware address - // IPv6 target address is address we're probing - // Ethernet destination address is Ethernet interface address of the Sleep Proxy client we're probing - SendNDP(m, NDP_Sol, 0, ar, &zerov6Addr, mDNSNULL, &ar->AddressProxy.ip.v6, &ar->WakeUp.IMAC); - } - // Mark for sending. (If no active interfaces, then don't even try.) - ar->SendRNow = (!intf || ar->WakeUp.HMAC.l[0]) ? mDNSNULL : ar->resrec.InterfaceID ? ar->resrec.InterfaceID : intf->InterfaceID; - ar->LastAPTime = m->timenow; - // When we have a late conflict that resets a record to probing state we use a special marker value greater - // than DefaultProbeCountForTypeUnique. Here we detect that state and reset ar->ProbeCount back to the right value. - if (ar->ProbeCount > DefaultProbeCountForTypeUnique) - ar->ProbeCount = DefaultProbeCountForTypeUnique; - ar->ProbeCount--; - SetNextAnnounceProbeTime(m, ar); - if (ar->ProbeCount == 0) - { - // If this is the last probe for this record, then see if we have any matching records - // on our duplicate list which should similarly have their ProbeCount cleared to zero... - AuthRecord *r2; - for (r2 = m->DuplicateRecords; r2; r2=r2->next) - if (r2->resrec.RecordType == kDNSRecordTypeUnique && RecordIsLocalDuplicate(r2, ar)) - r2->ProbeCount = 0; - // ... then acknowledge this record to the client. - // We do this optimistically, just as we're about to send the third probe. - // This helps clients that both advertise and browse, and want to filter themselves - // from the browse results list, because it helps ensure that the registration - // confirmation will be delivered 1/4 second *before* the browse "add" event. - // A potential downside is that we could deliver a registration confirmation and then find out - // moments later that there's a name conflict, but applications have to be prepared to handle - // late conflicts anyway (e.g. on connection of network cable, etc.), so this is nothing new. - if (!ar->Acknowledged) AcknowledgeRecord(m, ar); - } - } - // else, if it has now finished probing, move it to state Verified, - // and update m->NextScheduledResponse so it will be announced - else - { - if (!ar->Acknowledged) AcknowledgeRecord(m, ar); // Defensive, just in case it got missed somehow - ar->resrec.RecordType = kDNSRecordTypeVerified; - ar->ThisAPInterval = DefaultAnnounceIntervalForTypeUnique; - ar->LastAPTime = m->timenow - DefaultAnnounceIntervalForTypeUnique; - SetNextAnnounceProbeTime(m, ar); - } - } - } - m->CurrentRecord = m->DuplicateRecords; - while (m->CurrentRecord) - { - ar = m->CurrentRecord; - m->CurrentRecord = ar->next; - if (ar->resrec.RecordType == kDNSRecordTypeUnique && ar->ProbeCount == 0 && !ar->Acknowledged) - AcknowledgeRecord(m, ar); - } - - // 3. Now we know which queries and probes we're sending, - // go through our interface list sending the appropriate queries on each interface - while (intf) - { - const int OwnerRecordSpace = (m->AnnounceOwner && intf->MAC.l[0]) ? DNSOpt_Header_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) : 0; - mDNSu8 *queryptr = m->omsg.data; - InitializeDNSMessage(&m->omsg.h, zeroID, QueryFlags); - if (KnownAnswerList) verbosedebugf("SendQueries: KnownAnswerList set... Will continue from previous packet"); - if (!KnownAnswerList) - { - // Start a new known-answer list - CacheRecord **kalistptr = &KnownAnswerList; - mDNSu32 answerforecast = OwnerRecordSpace; // We start by assuming we'll need at least enough space to put the Owner Option - - // Put query questions in this packet - for (q = m->Questions; q && q != m->NewQuestions; q=q->next) - { - if (mDNSOpaque16IsZero(q->TargetQID) && (q->SendQNow == intf->InterfaceID)) - { - 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 - 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, &m->omsg, &queryptr, q, &kalistptr, &answerforecast)) - { - q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf); - if (q->WakeOnResolveCount) - { - mDNSSendWakeOnResolve(m, q); - q->WakeOnResolveCount--; - } - } - } - } - - // Put probe questions in this packet - for (ar = m->ResourceRecords; ar; ar=ar->next) - if (ar->SendRNow == intf->InterfaceID) - { - mDNSBool ucast = (ar->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicastOn5353; - mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); - const mDNSu8 *const limit = m->omsg.data + (m->omsg.h.numQuestions ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData); - // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) - mDNSu32 forecast = answerforecast + 12 + ar->resrec.rdestimate; - mDNSu8 *newptr = putQuestion(&m->omsg, queryptr, limit - forecast, ar->resrec.name, kDNSQType_ANY, (mDNSu16)(ar->resrec.rrclass | ucbit)); - if (newptr) - { - queryptr = newptr; - answerforecast = forecast; - ar->SendRNow = (ar->resrec.InterfaceID) ? mDNSNULL : GetNextActiveInterfaceID(intf); - ar->IncludeInProbe = mDNStrue; - verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d", - ar->resrec.name->c, DNSTypeName(ar->resrec.rrtype), ar->ProbeCount); - } - } - } - - // Put our known answer list (either new one from this question or questions, or remainder of old one from last time) - while (KnownAnswerList) - { - CacheRecord *ka = KnownAnswerList; - mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - ka->TimeRcvd)) / mDNSPlatformOneSecond; - mDNSu8 *newptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAnswers, - &ka->resrec, ka->resrec.rroriginalttl - SecsSinceRcvd, m->omsg.data + NormalMaxDNSMessageData - OwnerRecordSpace); - if (newptr) - { - verbosedebugf("SendQueries: Put %##s (%s) at %d - %d", - ka->resrec.name->c, DNSTypeName(ka->resrec.rrtype), queryptr - m->omsg.data, newptr - m->omsg.data); - queryptr = newptr; - KnownAnswerList = ka->NextInKAList; - ka->NextInKAList = mDNSNULL; - } - else - { - // 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 (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; - } - } - - for (ar = m->ResourceRecords; ar; ar=ar->next) - if (ar->IncludeInProbe) - { - mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, &ar->resrec); - ar->IncludeInProbe = mDNSfalse; - if (newptr) queryptr = newptr; - else LogMsg("SendQueries: How did we fail to have space for the Update record %s", ARDisplayString(m,ar)); - } - - if (queryptr > m->omsg.data) - { - if (OwnerRecordSpace) - { - AuthRecord opt; - mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); - opt.resrec.rrclass = NormalMaxDNSMessageData; - opt.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record - opt.resrec.rdestimate = sizeof(rdataOPT); - SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]); - LogSPS("SendQueries putting %s", ARDisplayString(m, &opt)); - queryptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAdditionals, - &opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData); - if (!queryptr) - LogMsg("SendQueries: How did we fail to have space for the OPT record (%d/%d/%d/%d) %s", - m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); - if (queryptr > m->omsg.data + NormalMaxDNSMessageData) - if (m->omsg.h.numQuestions != 1 || m->omsg.h.numAnswers != 0 || m->omsg.h.numAuthorities != 1 || m->omsg.h.numAdditionals != 1) - LogMsg("SendQueries: Why did we generate oversized packet with OPT record %p %p %p (%d/%d/%d/%d) %s", - m->omsg.data, m->omsg.data + NormalMaxDNSMessageData, queryptr, - m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); - } - - 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", - 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, mDNSNULL, &AllDNSLinkGroup_v4, MulticastDNSPort, mDNSNULL, mDNSNULL); - if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, mDNSNULL, &AllDNSLinkGroup_v6, MulticastDNSPort, mDNSNULL, mDNSNULL); - if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/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 - // on this interface, so go around one more time and try again. - } - else // Nothing more to send on this interface; go to next - { - const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); - #if MDNS_DEBUGMSGS && 0 - const char *const msg = next ? "SendQueries: Nothing more on %p; moving to %p" : "SendQueries: Nothing more on %p"; - debugf(msg, intf, next); - #endif - intf = next; - } - } - - // 4. Final housekeeping - - // 4a. Debugging check: Make sure we announced all our records - for (ar = m->ResourceRecords; ar; ar=ar->next) - if (ar->SendRNow) - { - if (ar->ARType != AuthRecordLocalOnly && ar->ARType != AuthRecordP2P) - LogMsg("SendQueries: No active interface %p to send probe: %p %s", ar->SendRNow, ar->resrec.InterfaceID, ARDisplayString(m, ar)); - ar->SendRNow = mDNSNULL; - } - - // 4b. When we have lingering cache records that we're keeping around for a few seconds in the hope - // that their interface which went away might come back again, the logic will want to send queries - // for those records, but we can't because their interface isn't here any more, so to keep the - // state machine ticking over we just pretend we did so. - // If the interface does not come back in time, the cache record will expire naturally - FORALL_CACHERECORDS(slot, cg, cr) - if (cr->CRActiveQuestion && cr->UnansweredQueries < MaxUnansweredQueries) - if (m->timenow + TicksTTL(cr)/50 - cr->NextRequiredQuery >= 0) - { - cr->UnansweredQueries++; - cr->CRActiveQuestion->SendQNow = mDNSNULL; - SetNextCacheCheckTimeForRecord(m, cr); - } - - // 4c. Debugging check: Make sure we sent all our planned questions - // Do this AFTER the lingering cache records check above, because that will prevent spurious warnings for questions - // we legitimately couldn't send because the interface is no longer available - for (q = m->Questions; q; q=q->next) - if (q->SendQNow) - { - DNSQuestion *x; - for (x = m->NewQuestions; x; x=x->next) if (x == q) break; // Check if this question is a NewQuestion - LogMsg("SendQueries: No active interface %p to send %s question: %p %##s (%s)", q->SendQNow, x ? "new" : "old", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); - q->SendQNow = mDNSNULL; - } - } +{ + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *cr; + AuthRecord *ar; + int pktcount = 0; + DNSQuestion *q; + // For explanation of maxExistingQuestionInterval logic, see comments for maxExistingAnnounceInterval + mDNSs32 maxExistingQuestionInterval = 0; + const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces); + CacheRecord *KnownAnswerList = mDNSNULL; + + // 1. If time for a query, work out what we need to do + + // We're expecting to send a query anyway, so see if any expiring cache records are close enough + // to their NextRequiredQuery to be worth batching them together with this one + FORALL_CACHERECORDS(slot, cg, cr) + { + if (cr->CRActiveQuestion && cr->UnansweredQueries < MaxUnansweredQueries) + { + if (m->timenow + TicksTTL(cr)/50 - cr->NextRequiredQuery >= 0) + { + debugf("Sending %d%% cache expiration query for %s", 80 + 5 * cr->UnansweredQueries, CRDisplayString(m, cr)); + q = cr->CRActiveQuestion; + ExpireDupSuppressInfoOnInterface(q->DupSuppress, m->timenow - TicksTTL(cr)/20, cr->resrec.InterfaceID); + // For uDNS queries (TargetQID non-zero) we adjust LastQTime, + // and bump UnansweredQueries so that we don't spin trying to send the same cache expiration query repeatedly + if (q->Target.type) + { + q->SendQNow = mDNSInterfaceMark; // If targeted query, mark it + } + else if (!mDNSOpaque16IsZero(q->TargetQID)) + { + q->LastQTime = m->timenow - q->ThisQInterval; + cr->UnansweredQueries++; + } + else if (q->SendQNow == mDNSNULL) + { + q->SendQNow = cr->resrec.InterfaceID; + } + else if (q->SendQNow != cr->resrec.InterfaceID) + { + q->SendQNow = mDNSInterfaceMark; + } + } + } + } + + // Scan our list of questions to see which: + // *WideArea* queries need to be sent + // *unicast* queries need to be sent + // *multicast* queries we're definitely going to send + if (m->CurrentQuestion) + LogMsg("SendQueries ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) + { + q = m->CurrentQuestion; + if (q->Target.type && (q->SendQNow || TimeToSendThisQuestion(q, m->timenow))) + { + mDNSu8 *qptr = m->omsg.data; + const mDNSu8 *const limit = m->omsg.data + sizeof(m->omsg.data); + + // If we fail to get a new on-demand socket (should only happen cases of the most extreme resource exhaustion), we'll try again next time + if (!q->LocalSocket) q->LocalSocket = mDNSPlatformUDPSocket(m, zeroIPPort); + if (q->LocalSocket) + { + 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->LocalSocket, &q->Target, q->TargetPort, mDNSNULL, mDNSNULL, q->UseBrackgroundTrafficClass); + q->ThisQInterval *= QuestionIntervalStep; + } + if (q->ThisQInterval > MaxQuestionInterval) + q->ThisQInterval = MaxQuestionInterval; + q->LastQTime = m->timenow; + q->LastQTxTime = m->timenow; + q->RecentAnswerPkts = 0; + q->SendQNow = mDNSNULL; + q->ExpectUnicastResp = NonZeroTime(m->timenow); + } + else if (mDNSOpaque16IsZero(q->TargetQID) && !q->Target.type && TimeToSendThisQuestion(q, m->timenow)) + { + //LogInfo("Time to send %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), m->timenow - NextQSendTime(q)); + q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces + if (maxExistingQuestionInterval < q->ThisQInterval) + maxExistingQuestionInterval = q->ThisQInterval; + } + // If m->CurrentQuestion wasn't modified out from under us, advance it now + // We can't do this at the start of the loop because uDNS_CheckCurrentQuestion() depends on having + // m->CurrentQuestion point to the right question + if (q == m->CurrentQuestion) m->CurrentQuestion = m->CurrentQuestion->next; + } + while (m->CurrentQuestion) + { + LogInfo("SendQueries question loop 1: Skipping NewQuestion %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = m->CurrentQuestion->next; + } + m->CurrentQuestion = mDNSNULL; + + // Scan our list of questions + // (a) to see if there are any more that are worth accelerating, and + // (b) to update the state variables for *all* the questions we're going to send + // Note: Don't set NextScheduledQuery until here, because uDNS_CheckCurrentQuestion in the loop above can add new questions to the list, + // which causes NextScheduledQuery to get (incorrectly) set to m->timenow. Setting it here is the right place, because the very + // next thing we do is scan the list and call SetNextQueryTime() for every question we find, so we know we end up with the right value. + m->NextScheduledQuery = m->timenow + 0x78000000; + for (q = m->Questions; q && q != m->NewQuestions; q=q->next) + { + if (mDNSOpaque16IsZero(q->TargetQID) && (q->SendQNow || + (!q->Target.type && ActiveQuestion(q) && q->ThisQInterval <= maxExistingQuestionInterval && AccelerateThisQuery(m,q)))) + { + // If at least halfway to next query time, advance to next interval + // If less than halfway to next query time, then + // treat this as logically a repeat of the last transmission, without advancing the interval + if (m->timenow - (q->LastQTime + (q->ThisQInterval/2)) >= 0) + { + //LogInfo("Accelerating %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), m->timenow - NextQSendTime(q)); + q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces + debugf("SendQueries: %##s (%s) next interval %d seconds RequestUnicast = %d", + q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval / InitialQuestionInterval, q->RequestUnicast); + q->ThisQInterval *= QuestionIntervalStep; + if (q->ThisQInterval > MaxQuestionInterval) + q->ThisQInterval = MaxQuestionInterval; + else if (q->CurrentAnswers == 0 && q->ThisQInterval == InitialQuestionInterval * QuestionIntervalStep3 && !q->RequestUnicast && + !(RRTypeIsAddressType(q->qtype) && CacheHasAddressTypeForName(m, &q->qname, q->qnamehash))) + { + // Generally don't need to log this. + // It's not especially noteworthy if a query finds no results -- this usually happens for domain + // enumeration queries in the LL subdomain (e.g. "db._dns-sd._udp.0.0.254.169.in-addr.arpa") + // and when there simply happen to be no instances of the service the client is looking + // for (e.g. iTunes is set to look for RAOP devices, and the current network has none). + debugf("SendQueries: Zero current answers for %##s (%s); will reconfirm antecedents", + q->qname.c, DNSTypeName(q->qtype)); + // Sending third query, and no answers yet; time to begin doubting the source + ReconfirmAntecedents(m, &q->qname, q->qnamehash, 0); + } + } + + // Mark for sending. (If no active interfaces, then don't even try.) + q->SendOnAll = (q->SendQNow == mDNSInterfaceMark); + if (q->SendOnAll) + { + q->SendQNow = !intf ? mDNSNULL : (q->InterfaceID) ? q->InterfaceID : intf->InterfaceID; + q->LastQTime = m->timenow; + } + + // If we recorded a duplicate suppression for this question less than half an interval ago, + // then we consider it recent enough that we don't need to do an identical query ourselves. + ExpireDupSuppressInfo(q->DupSuppress, m->timenow - q->ThisQInterval/2); + + q->LastQTxTime = m->timenow; + q->RecentAnswerPkts = 0; + if (q->RequestUnicast) q->RequestUnicast--; + } + // For all questions (not just the ones we're sending) check what the next scheduled event will be + // We don't need to consider NewQuestions here because for those we'll set m->NextScheduledQuery in AnswerNewQuestion + SetNextQueryTime(m,q); + } + + // 2. Scan our authoritative RR list to see what probes we might need to send + + m->NextScheduledProbe = m->timenow + 0x78000000; + + if (m->CurrentRecord) + LogMsg("SendQueries ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + ar = m->CurrentRecord; + m->CurrentRecord = ar->next; + if (!AuthRecord_uDNS(ar) && ar->resrec.RecordType == kDNSRecordTypeUnique) // For all records that are still probing... + { + // 1. If it's not reached its probe time, just make sure we update m->NextScheduledProbe correctly + if (m->timenow - (ar->LastAPTime + ar->ThisAPInterval) < 0) + { + SetNextAnnounceProbeTime(m, ar); + } + // 2. else, if it has reached its probe time, mark it for sending and then update m->NextScheduledProbe correctly + else if (ar->ProbeCount) + { + if (ar->AddressProxy.type == mDNSAddrType_IPv4) + { + LogSPS("SendQueries ARP Probe %d %s %s", ar->ProbeCount, InterfaceNameForID(m, ar->resrec.InterfaceID), ARDisplayString(m,ar)); + SendARP(m, 1, ar, &zerov4Addr, &zeroEthAddr, &ar->AddressProxy.ip.v4, &ar->WakeUp.IMAC); + } + else if (ar->AddressProxy.type == mDNSAddrType_IPv6) + { + LogSPS("SendQueries NDP Probe %d %s %s", ar->ProbeCount, InterfaceNameForID(m, ar->resrec.InterfaceID), ARDisplayString(m,ar)); + // IPv6 source = zero + // No target hardware address + // IPv6 target address is address we're probing + // Ethernet destination address is Ethernet interface address of the Sleep Proxy client we're probing + SendNDP(m, NDP_Sol, 0, ar, &zerov6Addr, mDNSNULL, &ar->AddressProxy.ip.v6, &ar->WakeUp.IMAC); + } + // Mark for sending. (If no active interfaces, then don't even try.) + ar->SendRNow = (!intf || ar->WakeUp.HMAC.l[0]) ? mDNSNULL : ar->resrec.InterfaceID ? ar->resrec.InterfaceID : intf->InterfaceID; + ar->LastAPTime = m->timenow; + // When we have a late conflict that resets a record to probing state we use a special marker value greater + // than DefaultProbeCountForTypeUnique. Here we detect that state and reset ar->ProbeCount back to the right value. + if (ar->ProbeCount > DefaultProbeCountForTypeUnique) + ar->ProbeCount = DefaultProbeCountForTypeUnique; + ar->ProbeCount--; + SetNextAnnounceProbeTime(m, ar); + if (ar->ProbeCount == 0) + { + // If this is the last probe for this record, then see if we have any matching records + // on our duplicate list which should similarly have their ProbeCount cleared to zero... + AuthRecord *r2; + for (r2 = m->DuplicateRecords; r2; r2=r2->next) + if (r2->resrec.RecordType == kDNSRecordTypeUnique && RecordIsLocalDuplicate(r2, ar)) + r2->ProbeCount = 0; + // ... then acknowledge this record to the client. + // We do this optimistically, just as we're about to send the third probe. + // This helps clients that both advertise and browse, and want to filter themselves + // from the browse results list, because it helps ensure that the registration + // confirmation will be delivered 1/4 second *before* the browse "add" event. + // A potential downside is that we could deliver a registration confirmation and then find out + // moments later that there's a name conflict, but applications have to be prepared to handle + // late conflicts anyway (e.g. on connection of network cable, etc.), so this is nothing new. + if (!ar->Acknowledged) AcknowledgeRecord(m, ar); + } + } + // else, if it has now finished probing, move it to state Verified, + // and update m->NextScheduledResponse so it will be announced + else + { + if (!ar->Acknowledged) AcknowledgeRecord(m, ar); // Defensive, just in case it got missed somehow + ar->resrec.RecordType = kDNSRecordTypeVerified; + ar->ThisAPInterval = DefaultAnnounceIntervalForTypeUnique; + ar->LastAPTime = m->timenow - DefaultAnnounceIntervalForTypeUnique; + SetNextAnnounceProbeTime(m, ar); + } + } + } + m->CurrentRecord = m->DuplicateRecords; + while (m->CurrentRecord) + { + ar = m->CurrentRecord; + m->CurrentRecord = ar->next; + if (ar->resrec.RecordType == kDNSRecordTypeUnique && ar->ProbeCount == 0 && !ar->Acknowledged) + AcknowledgeRecord(m, ar); + } + + // 3. Now we know which queries and probes we're sending, + // go through our interface list sending the appropriate queries on each interface + while (intf) + { + const int OwnerRecordSpace = (m->AnnounceOwner && intf->MAC.l[0]) ? DNSOpt_Header_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) : 0; + mDNSu8 *queryptr = m->omsg.data; + mDNSBool useBackgroundTrafficClass = mDNSfalse; // set if we should use background traffic class + + InitializeDNSMessage(&m->omsg.h, zeroID, QueryFlags); + if (KnownAnswerList) verbosedebugf("SendQueries: KnownAnswerList set... Will continue from previous packet"); + if (!KnownAnswerList) + { + // Start a new known-answer list + CacheRecord **kalistptr = &KnownAnswerList; + mDNSu32 answerforecast = OwnerRecordSpace; // We start by assuming we'll need at least enough space to put the Owner Option + + // Put query questions in this packet + for (q = m->Questions; q && q != m->NewQuestions; q=q->next) + { + if (mDNSOpaque16IsZero(q->TargetQID) && (q->SendQNow == intf->InterfaceID)) + { + 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 - m->omsg.data, queryptr + answerforecast - m->omsg.data); + + // If interface is P2P type, verify that query should be sent over it. + if (!mDNSPlatformValidQuestionForInterface(q, intf)) + { + LogInfo("SendQueries: Not sending (%s) %##s on %s", DNSTypeName(q->qtype), q->qname.c, InterfaceNameForID(m, intf->InterfaceID)); + q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf); + } + // If we're suppressing this question, or we successfully put it, update its SendQNow state + else if (SuppressOnThisInterface(q->DupSuppress, intf) || + BuildQuestion(m, &m->omsg, &queryptr, q, &kalistptr, &answerforecast)) + { + q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf); + if (q->WakeOnResolveCount) + { + mDNSSendWakeOnResolve(m, q); + q->WakeOnResolveCount--; + } + + // use brackground traffic class if any included question requires it + if (q->UseBrackgroundTrafficClass) + { + useBackgroundTrafficClass = mDNStrue; + } + } + } + } + + // Put probe questions in this packet + for (ar = m->ResourceRecords; ar; ar=ar->next) + if (ar->SendRNow == intf->InterfaceID) + { + mDNSBool ucast = (ar->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicastOn5353; + mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); + const mDNSu8 *const limit = m->omsg.data + (m->omsg.h.numQuestions ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData); + // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) + mDNSu32 forecast = answerforecast + 12 + ar->resrec.rdestimate; + mDNSu8 *newptr = putQuestion(&m->omsg, queryptr, limit - forecast, ar->resrec.name, kDNSQType_ANY, (mDNSu16)(ar->resrec.rrclass | ucbit)); + if (newptr) + { + queryptr = newptr; + answerforecast = forecast; + ar->SendRNow = (ar->resrec.InterfaceID) ? mDNSNULL : GetNextActiveInterfaceID(intf); + ar->IncludeInProbe = mDNStrue; + verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d", + ar->resrec.name->c, DNSTypeName(ar->resrec.rrtype), ar->ProbeCount); + } + } + } + + // Put our known answer list (either new one from this question or questions, or remainder of old one from last time) + while (KnownAnswerList) + { + CacheRecord *ka = KnownAnswerList; + mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - ka->TimeRcvd)) / mDNSPlatformOneSecond; + mDNSu8 *newptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAnswers, + &ka->resrec, ka->resrec.rroriginalttl - SecsSinceRcvd, m->omsg.data + NormalMaxDNSMessageData - OwnerRecordSpace); + if (newptr) + { + verbosedebugf("SendQueries: Put %##s (%s) at %d - %d", + ka->resrec.name->c, DNSTypeName(ka->resrec.rrtype), queryptr - m->omsg.data, newptr - m->omsg.data); + queryptr = newptr; + KnownAnswerList = ka->NextInKAList; + ka->NextInKAList = mDNSNULL; + } + else + { + // 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 (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; + } + } + + for (ar = m->ResourceRecords; ar; ar=ar->next) + if (ar->IncludeInProbe) + { + mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, &ar->resrec); + ar->IncludeInProbe = mDNSfalse; + if (newptr) queryptr = newptr; + else LogMsg("SendQueries: How did we fail to have space for the Update record %s", ARDisplayString(m,ar)); + } + + if (queryptr > m->omsg.data) + { + if (OwnerRecordSpace) + { + AuthRecord opt; + mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + opt.resrec.rrclass = NormalMaxDNSMessageData; + opt.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record + opt.resrec.rdestimate = sizeof(rdataOPT); + SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]); + LogSPS("SendQueries putting %s", ARDisplayString(m, &opt)); + queryptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAdditionals, + &opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData); + if (!queryptr) + LogMsg("SendQueries: How did we fail to have space for the OPT record (%d/%d/%d/%d) %s", + m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); + if (queryptr > m->omsg.data + NormalMaxDNSMessageData) + if (m->omsg.h.numQuestions != 1 || m->omsg.h.numAnswers != 0 || m->omsg.h.numAuthorities != 1 || m->omsg.h.numAdditionals != 1) + LogMsg("SendQueries: Why did we generate oversized packet with OPT record %p %p %p (%d/%d/%d/%d) %s", + m->omsg.data, m->omsg.data + NormalMaxDNSMessageData, queryptr, + m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); + } + + 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", + 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, mDNSNULL, &AllDNSLinkGroup_v4, MulticastDNSPort, mDNSNULL, mDNSNULL, useBackgroundTrafficClass); + if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, mDNSNULL, &AllDNSLinkGroup_v6, MulticastDNSPort, mDNSNULL, mDNSNULL, useBackgroundTrafficClass); + if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/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 + // on this interface, so go around one more time and try again. + } + else // Nothing more to send on this interface; go to next + { + const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); + #if MDNS_DEBUGMSGS && 0 + const char *const msg = next ? "SendQueries: Nothing more on %p; moving to %p" : "SendQueries: Nothing more on %p"; + debugf(msg, intf, next); + #endif + intf = next; + } + } + + // 4. Final housekeeping + + // 4a. Debugging check: Make sure we announced all our records + for (ar = m->ResourceRecords; ar; ar=ar->next) + if (ar->SendRNow) + { + if (ar->ARType != AuthRecordLocalOnly && ar->ARType != AuthRecordP2P) + LogMsg("SendQueries: No active interface %p to send probe: %p %s", ar->SendRNow, ar->resrec.InterfaceID, ARDisplayString(m, ar)); + ar->SendRNow = mDNSNULL; + } + + // 4b. When we have lingering cache records that we're keeping around for a few seconds in the hope + // that their interface which went away might come back again, the logic will want to send queries + // for those records, but we can't because their interface isn't here any more, so to keep the + // state machine ticking over we just pretend we did so. + // If the interface does not come back in time, the cache record will expire naturally + FORALL_CACHERECORDS(slot, cg, cr) + { + if (cr->CRActiveQuestion && cr->UnansweredQueries < MaxUnansweredQueries) + { + if (m->timenow + TicksTTL(cr)/50 - cr->NextRequiredQuery >= 0) + { + cr->UnansweredQueries++; + cr->CRActiveQuestion->SendQNow = mDNSNULL; + SetNextCacheCheckTimeForRecord(m, cr); + } + } + } + + // 4c. Debugging check: Make sure we sent all our planned questions + // Do this AFTER the lingering cache records check above, because that will prevent spurious warnings for questions + // we legitimately couldn't send because the interface is no longer available + for (q = m->Questions; q; q=q->next) + if (q->SendQNow) + { + DNSQuestion *x; + for (x = m->NewQuestions; x; x=x->next) if (x == q) break; // Check if this question is a NewQuestion + LogMsg("SendQueries: No active interface %p to send %s question: %p %##s (%s)", q->SendQNow, x ? "new" : "old", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); + q->SendQNow = mDNSNULL; + } +} mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password) - { - int i, j; - mDNSu8 *ptr = m->omsg.data; - NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); - if (!intf) { LogMsg("SendARP: No interface with InterfaceID %p found", InterfaceID); return; } +{ + int i, j; + mDNSu8 *ptr = m->omsg.data; + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); + if (!intf) { LogMsg("SendARP: No interface with InterfaceID %p found", InterfaceID); return; } - // 0x00 Destination address - for (i=0; i<6; i++) *ptr++ = EthAddr->b[i]; + // 0x00 Destination address + for (i=0; i<6; i++) *ptr++ = EthAddr->b[i]; - // 0x06 Source address (Note: Since we don't currently set the BIOCSHDRCMPLT option, BPF will fill in the real interface address for us) - for (i=0; i<6; i++) *ptr++ = intf->MAC.b[0]; + // 0x06 Source address (Note: Since we don't currently set the BIOCSHDRCMPLT option, BPF will fill in the real interface address for us) + for (i=0; i<6; i++) *ptr++ = intf->MAC.b[0]; - // 0x0C Ethertype (0x0842) - *ptr++ = 0x08; - *ptr++ = 0x42; + // 0x0C Ethertype (0x0842) + *ptr++ = 0x08; + *ptr++ = 0x42; - // 0x0E Wakeup sync sequence - for (i=0; i<6; i++) *ptr++ = 0xFF; + // 0x0E Wakeup sync sequence + for (i=0; i<6; i++) *ptr++ = 0xFF; - // 0x14 Wakeup data - for (j=0; j<16; j++) for (i=0; i<6; i++) *ptr++ = EthAddr->b[i]; + // 0x14 Wakeup data + for (j=0; j<16; j++) for (i=0; i<6; i++) *ptr++ = EthAddr->b[i]; - // 0x74 Password - for (i=0; i<6; i++) *ptr++ = password->b[i]; + // 0x74 Password + for (i=0; i<6; i++) *ptr++ = password->b[i]; - mDNSPlatformSendRawPacket(m->omsg.data, ptr, InterfaceID); + mDNSPlatformSendRawPacket(m->omsg.data, ptr, InterfaceID); - // For Ethernet switches that don't flood-foward packets with unknown unicast destination MAC addresses, - // broadcast is the only reliable way to get a wakeup packet to the intended target machine. - // For 802.11 WPA networks, where a sleeping target machine may have missed a broadcast/multicast - // key rotation, unicast is the only way to get a wakeup packet to the intended target machine. - // So, we send one of each, unicast first, then broadcast second. - for (i=0; i<6; i++) m->omsg.data[i] = 0xFF; - mDNSPlatformSendRawPacket(m->omsg.data, ptr, InterfaceID); - } + // For Ethernet switches that don't flood-foward packets with unknown unicast destination MAC addresses, + // broadcast is the only reliable way to get a wakeup packet to the intended target machine. + // For 802.11 WPA networks, where a sleeping target machine may have missed a broadcast/multicast + // key rotation, unicast is the only way to get a wakeup packet to the intended target machine. + // So, we send one of each, unicast first, then broadcast second. + for (i=0; i<6; i++) m->omsg.data[i] = 0xFF; + mDNSPlatformSendRawPacket(m->omsg.data, ptr, InterfaceID); +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -3289,214 +3417,190 @@ mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAdd // the network repeatedly. This happens first time when we answer the question and // and later when we refresh the cache. mDNSlocal void ResetQuestionState(mDNS *const m, DNSQuestion *q) - { - q->LastQTime = m->timenow; - q->LastQTxTime = m->timenow; - q->RecentAnswerPkts = 0; - q->ThisQInterval = MaxQuestionInterval; - q->RequestUnicast = mDNSfalse; - // Reset unansweredQueries so that we don't penalize this server later when we - // start sending queries when the cache expires. - q->unansweredQueries = 0; - debugf("ResetQuestionState: Set MaxQuestionInterval for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - } +{ + q->LastQTime = m->timenow; + q->LastQTxTime = m->timenow; + q->RecentAnswerPkts = 0; + q->ThisQInterval = MaxQuestionInterval; + q->RequestUnicast = mDNSfalse; + // Reset unansweredQueries so that we don't penalize this server later when we + // start sending queries when the cache expires. + q->unansweredQueries = 0; + debugf("ResetQuestionState: Set MaxQuestionInterval for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); +} // Note: AnswerCurrentQuestionWithResourceRecord can call a user callback, which may change the record list and/or question list. // Any code walking either list must use the m->CurrentQuestion (and possibly m->CurrentRecord) mechanism to protect against this. // In fact, to enforce this, the routine will *only* answer the question currently pointed to by m->CurrentQuestion, // which will be auto-advanced (possibly to NULL) if the client callback cancels the question. mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheRecord *const rr, const QC_result AddRecord) - { - DNSQuestion *const q = m->CurrentQuestion; - mDNSBool followcname = FollowCNAME(q, &rr->resrec, AddRecord); - - verbosedebugf("AnswerCurrentQuestionWithResourceRecord:%4lu %s TTL %d %s", - q->CurrentAnswers, AddRecord ? "Add" : "Rmv", rr->resrec.rroriginalttl, CRDisplayString(m, rr)); - - // Normally we don't send out the unicast query if we have answered using our local only auth records e.g., /etc/hosts. - // But if the query for "A" record has a local answer but query for "AAAA" record has no local answer, we might - // send the AAAA query out which will come back with CNAME and will also answer the "A" query. To prevent that, - // we check to see if that query already has a unique local answer. - if (q->LOAddressAnswers) - { - LogInfo("AnswerCurrentQuestionWithResourceRecord: Question %p %##s (%s) not answering with record %s due to " - "LOAddressAnswers %d", q, q->qname.c, DNSTypeName(q->qtype), ARDisplayString(m, rr), - q->LOAddressAnswers); - return; - } - - if (QuerySuppressed(q)) - { - // If the query is suppressed, then we don't want to answer from the cache. But if this query is - // supposed to time out, we still want to callback the clients. We do this only for TimeoutQuestions - // that are timing out, which we know are answered with Negative cache record when timing out. - if (!q->TimeoutQuestion || rr->resrec.RecordType != kDNSRecordTypePacketNegative || (m->timenow - q->StopTime < 0)) - return; - } - - // Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerCurrentQuestionWithResourceRecord(... 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; - if (AddRecord == QC_add && !q->DuplicateOf && rr->CRActiveQuestion != q) - { - if (!rr->CRActiveQuestion) m->rrcache_active++; // If not previously active, increment rrcache_active count - debugf("AnswerCurrentQuestionWithResourceRecord: Updating CRActiveQuestion from %p to %p for cache record %s, CurrentAnswer %d", - rr->CRActiveQuestion, q, CRDisplayString(m,rr), q->CurrentAnswers); - rr->CRActiveQuestion = q; // We know q is non-null - SetNextCacheCheckTimeForRecord(m, rr); - } - - // If this is: - // (a) a no-cache add, where we've already done at least one 'QM' query, or - // (b) a normal add, where we have at least one unique-type answer, - // then there's no need to keep polling the network. - // (If we have an answer in the cache, then we'll automatically ask again in time to stop it expiring.) - // We do this for mDNS questions and uDNS one-shot questions, but not for - // uDNS LongLived questions, because that would mess up our LLQ lease renewal timing. - if ((AddRecord == QC_addnocache && !q->RequestUnicast) || - (AddRecord == QC_add && (q->ExpectUnique || (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)))) - if (ActiveQuestion(q) && (mDNSOpaque16IsZero(q->TargetQID) || !q->LongLived)) - { - ResetQuestionState(m, q); - } - - if (rr->DelayDelivery) return; // We'll come back later when CacheRecordDeferredAdd() calls us - - // Only deliver negative answers if client has explicitly requested them - if (rr->resrec.RecordType == kDNSRecordTypePacketNegative || (q->qtype != kDNSType_NSEC && RRAssertsNonexistence(&rr->resrec, q->qtype))) - if (!AddRecord || !q->ReturnIntermed) return; - - // For CNAME results to non-CNAME questions, only inform the client if they explicitly requested that - if (q->QuestionCallback && !q->NoAnswer && (!followcname || q->ReturnIntermed)) - { - mDNS_DropLockBeforeCallback(); // Allow client (and us) to legally make mDNS API calls - if (q->qtype != kDNSType_NSEC && RRAssertsNonexistence(&rr->resrec, q->qtype)) - { - CacheRecord neg; - MakeNegativeCacheRecord(m, &neg, &q->qname, q->qnamehash, q->qtype, q->qclass, 1, rr->resrec.InterfaceID, q->qDNSServer); - q->QuestionCallback(m, q, &neg.resrec, AddRecord); - } - else - q->QuestionCallback(m, q, &rr->resrec, AddRecord); - mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again - } - // Note: Proceed with caution here because client callback function is allowed to do anything, - // including starting/stopping queries, registering/deregistering records, etc. - - if (followcname && m->CurrentQuestion == q) - AnswerQuestionByFollowingCNAME(m, q, &rr->resrec); - } - -// New Questions are answered through AnswerNewQuestion. But there may not have been any -// matching cache records for the questions when it is called. There are two possibilities. -// -// 1) There are no cache records -// 2) There are cache records but the DNSServers between question and cache record don't match. -// -// In the case of (1), where there are no cache records and later we add them when we get a response, -// CacheRecordAdd/CacheRecordDeferredAdd will take care of adding the cache and delivering the ADD -// events to the application. If we already have a cache entry, then no ADD events are delivered -// unless the RDATA has changed -// -// In the case of (2) where we had the cache records and did not answer because of the DNSServer mismatch, -// we need to answer them whenever we change the DNSServer. But we can't do it at the instant the DNSServer -// changes because when we do the callback, the question can get deleted and the calling function would not -// know how to handle it. So, we run this function from mDNS_Execute to handle DNSServer changes on the -// question - -mDNSlocal void AnswerQuestionsForDNSServerChanges(mDNS *const m) - { - DNSQuestion *q; - DNSQuestion *qnext; - CacheRecord *rr; - mDNSu32 slot; - CacheGroup *cg; - - if (m->CurrentQuestion) - LogMsg("AnswerQuestionsForDNSServerChanges: ERROR m->CurrentQuestion already set: %##s (%s)", - m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); - - for (q = m->Questions; q && q != m->NewQuestions; q = qnext) - { - qnext = q->next; - - // multicast or DNSServers did not change. - if (mDNSOpaque16IsZero(q->TargetQID)) continue; - if (!q->deliverAddEvents) continue; - - // We are going to look through the cache for this question since it changed - // its DNSserver last time. Reset it so that we don't call them again. Calling - // them again will deliver duplicate events to the application - q->deliverAddEvents = mDNSfalse; - if (QuerySuppressed(q)) continue; - m->CurrentQuestion = q; - slot = HashSlot(&q->qname); - cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) - { - if (SameNameRecordAnswersQuestion(&rr->resrec, q)) - { - LogInfo("AnswerQuestionsForDNSServerChanges: Calling AnswerCurrentQuestionWithResourceRecord for question %p %##s using resource record %s", - q, q->qname.c, CRDisplayString(m, rr)); - // When this question penalizes a DNS server and has no more DNS servers to pick, we normally - // deliver a negative cache response and suspend the question for 60 seconds (see uDNS_CheckCurrentQuestion). - // But sometimes we may already find the negative cache entry and deliver that here as the process - // of changing DNS servers. When the cache entry is about to expire, we will resend the question and - // that time, we need to make sure that we have a valid DNS server. Otherwise, we will deliver - // a negative cache response without trying the server. - if (!q->qDNSServer && !q->DuplicateOf && rr->resrec.RecordType == kDNSRecordTypePacketNegative) - { - DNSQuestion *qptr; - SetValidDNSServers(m, q); - q->qDNSServer = GetServerForQuestion(m, q); - for (qptr = q->next ; qptr; qptr = qptr->next) - if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; } - } - q->CurrentAnswers++; - if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; - if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; - AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add); - if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here - } - } - } - m->CurrentQuestion = mDNSNULL; - } +{ + DNSQuestion *const q = m->CurrentQuestion; + mDNSBool followcname = FollowCNAME(q, &rr->resrec, AddRecord); + + verbosedebugf("AnswerCurrentQuestionWithResourceRecord:%4lu %s TTL %d %s", + q->CurrentAnswers, AddRecord ? "Add" : "Rmv", rr->resrec.rroriginalttl, CRDisplayString(m, rr)); + + // When the response for the question was validated, the entire rrset was validated. If we deliver + // a RMV for a single record in the rrset, we invalidate the response. If we deliver another add + // in the future, we will do the revalidation again. + // + // Also, if we deliver an ADD for a negative cache record and it has no NSECS, the ValidationStatus needs + // to be reset. This happens normally when we deliver a "secure" negative response followed by an insecure + // negative response which can happen e.g., when disconnecting from network. As we don't deliver RMVs for + // negative responses that were delivered before, we need to do it on the next ADD of a negative cache + // record. This ADD could be the result of a timeout, no DNS servers etc. If we don't reset the state, we + // will deliver this as a secure response. + if (q->ValidationRequired && ((AddRecord == QC_rmv) || + (rr->resrec.RecordType == kDNSRecordTypePacketNegative && !rr->nsec))) + { + q->ValidationStatus = 0; + q->ValidationState = DNSSECValRequired; + } + + // Normally we don't send out the unicast query if we have answered using our local only auth records e.g., /etc/hosts. + // But if the query for "A" record has a local answer but query for "AAAA" record has no local answer, we might + // send the AAAA query out which will come back with CNAME and will also answer the "A" query. To prevent that, + // we check to see if that query already has a unique local answer. + if (q->LOAddressAnswers) + { + LogInfo("AnswerCurrentQuestionWithResourceRecord: Question %p %##s (%s) not answering with record %s due to " + "LOAddressAnswers %d", q, q->qname.c, DNSTypeName(q->qtype), ARDisplayString(m, rr), + q->LOAddressAnswers); + return; + } + + if (QuerySuppressed(q)) + { + // If the query is suppressed, then we don't want to answer from the cache. But if this query is + // supposed to time out, we still want to callback the clients. We do this only for TimeoutQuestions + // that are timing out, which we know are answered with Negative cache record when timing out. + if (!q->TimeoutQuestion || rr->resrec.RecordType != kDNSRecordTypePacketNegative || (m->timenow - q->StopTime < 0)) + return; + } + + // Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerCurrentQuestionWithResourceRecord(... 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; + if (AddRecord == QC_add && !q->DuplicateOf && rr->CRActiveQuestion != q) + { + if (!rr->CRActiveQuestion) m->rrcache_active++; // If not previously active, increment rrcache_active count + debugf("AnswerCurrentQuestionWithResourceRecord: Updating CRActiveQuestion from %p to %p for cache record %s, CurrentAnswer %d", + rr->CRActiveQuestion, q, CRDisplayString(m,rr), q->CurrentAnswers); + rr->CRActiveQuestion = q; // We know q is non-null + SetNextCacheCheckTimeForRecord(m, rr); + } + + // If this is: + // (a) a no-cache add, where we've already done at least one 'QM' query, or + // (b) a normal add, where we have at least one unique-type answer, + // then there's no need to keep polling the network. + // (If we have an answer in the cache, then we'll automatically ask again in time to stop it expiring.) + // We do this for mDNS questions and uDNS one-shot questions, but not for + // uDNS LongLived questions, because that would mess up our LLQ lease renewal timing. + if ((AddRecord == QC_addnocache && !q->RequestUnicast) || + (AddRecord == QC_add && (q->ExpectUnique || (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)))) + if (ActiveQuestion(q) && (mDNSOpaque16IsZero(q->TargetQID) || !q->LongLived)) + { + ResetQuestionState(m, q); + } + + if (rr->DelayDelivery) return; // We'll come back later when CacheRecordDeferredAdd() calls us + + // Only deliver negative answers if client has explicitly requested them except when we are forcing a negative response + // for the purpose of retrying search domains + if (rr->resrec.RecordType == kDNSRecordTypePacketNegative || (q->qtype != kDNSType_NSEC && RRAssertsNonexistence(&rr->resrec, q->qtype))) + if (!AddRecord || (AddRecord != QC_forceresponse && !q->ReturnIntermed)) return; + + // For CNAME results to non-CNAME questions, only inform the client if they explicitly requested that + if (q->QuestionCallback && !q->NoAnswer && (!followcname || q->ReturnIntermed)) + { + mDNS_DropLockBeforeCallback(); // Allow client (and us) to legally make mDNS API calls + if (q->qtype != kDNSType_NSEC && RRAssertsNonexistence(&rr->resrec, q->qtype)) + { + CacheRecord neg; + MakeNegativeCacheRecord(m, &neg, &q->qname, q->qnamehash, q->qtype, q->qclass, 1, rr->resrec.InterfaceID, q->qDNSServer); + q->QuestionCallback(m, q, &neg.resrec, AddRecord); + } + else + q->QuestionCallback(m, q, &rr->resrec, AddRecord); + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + } + // If this is an "Add" operation and this question needs validation, validate the response. + // In the case of negative responses, extra care should be taken. Negative cache records are + // used for many purposes. For example, + // + // 1) Suppressing questions (SuppressUnusable) + // 2) Timeout questions + // 3) The name does not exist + // 4) No DNS servers are available and we need a quick response for the application + // + // (1) and (2) are handled by "QC_add" check as AddRecord would be "QC_forceresponse" in that case. + // For (3), it is possible that we don't get nsecs back but we still need to call VerifySignature so + // that we can deliver the appropriate DNSSEC result. There is no point in verifying signature for (4) + // and hence the explicit check for q->qDNSServer. + // + // Note: It is important that we avoid (4) here because once the state is set to DNSSECValInProgress, + // we won't verify the signature when it is really needed. For example, start a query (which is answered + // securely), disconnect from the network. (3) would happen now and if we start the verification, we + // move DNSSECValInProgress but never have a chance to go back to DNSSECValRequired as we don't deliver + // RMVs for negative response that was added before. + // + if (m->CurrentQuestion == q && (AddRecord == QC_add) && !q->ValidatingResponse && + q->ValidationState == DNSSECValRequired && q->qDNSServer) + { + q->ValidationState = DNSSECValInProgress; + // Treat it as callback call as that's what dnssec code expects + mDNS_DropLockBeforeCallback(); // Allow client (and us) to legally make mDNS API calls + VerifySignature(m, mDNSNULL, q); + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + return; + } + + // Note: Proceed with caution here because client callback function is allowed to do anything, + // including starting/stopping queries, registering/deregistering records, etc. + // + // If we get a CNAME back while we are validating the response (i.e., CNAME for DS, DNSKEY, RRSIG), + // don't follow them. If it is a ValidationRequired question, wait for the CNAME to be validated + // first before following it + if (!DNSSECQuestion(q) && followcname && m->CurrentQuestion == q) + AnswerQuestionByFollowingCNAME(m, q, &rr->resrec); +} mDNSlocal void CacheRecordDeferredAdd(mDNS *const m, CacheRecord *rr) - { - rr->DelayDelivery = 0; - if (m->CurrentQuestion) - LogMsg("CacheRecordDeferredAdd ERROR m->CurrentQuestion already set: %##s (%s)", - m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); - m->CurrentQuestion = m->Questions; - while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) - { - DNSQuestion *q = m->CurrentQuestion; - if (ResourceRecordAnswersQuestion(&rr->resrec, q)) - AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add); - if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now - m->CurrentQuestion = q->next; - } - m->CurrentQuestion = mDNSNULL; - } +{ + rr->DelayDelivery = 0; + if (m->CurrentQuestion) + LogMsg("CacheRecordDeferredAdd ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) + { + DNSQuestion *q = m->CurrentQuestion; + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add); + if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now + m->CurrentQuestion = q->next; + } + 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; - CacheGroup *cg = CacheGroupForName(m, slot, namehash, name); - const CacheRecord *rr; - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) - 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(NonZeroTime(delay)); - else return(0); - } +{ + 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; + CacheGroup *cg = CacheGroupForName(m, slot, namehash, name); + const CacheRecord *rr; + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + 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(NonZeroTime(delay)); + else return(0); +} // CacheRecordAdd is only called from CreateNewCacheEntry, *never* directly as a result of a client API call. // If new questions are created as a result of invoking client callbacks, they will be added to @@ -3507,72 +3611,72 @@ mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *c // which may change the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr) - { - DNSQuestion *q; - - // We stop when we get to NewQuestions -- if we increment their CurrentAnswers/LargeAnswers/UniqueAnswers - // counters here we'll end up double-incrementing them when we do it again in AnswerNewQuestion(). - for (q = m->Questions; q && q != m->NewQuestions; q=q->next) - { - if (ResourceRecordAnswersQuestion(&rr->resrec, q)) - { - // If this question is one that's actively sending queries, and it's received ten answers within one - // second of sending the last query packet, then that indicates some radical network topology change, - // so reset its exponential backoff back to the start. We must be at least at the eight-second interval - // to do this. If we're at the four-second interval, or less, there's not much benefit accelerating - // because we will anyway send another query within a few seconds. The first reset query is sent out - // randomized over the next four seconds to reduce possible synchronization between machines. - if (q->LastAnswerPktNum != m->PktNum) - { - q->LastAnswerPktNum = m->PktNum; - if (mDNSOpaque16IsZero(q->TargetQID) && ActiveQuestion(q) && ++q->RecentAnswerPkts >= 10 && - q->ThisQInterval > InitialQuestionInterval * QuestionIntervalStep3 && m->timenow - q->LastQTxTime < mDNSPlatformOneSecond) - { - LogMsg("CacheRecordAdd: %##s (%s) got immediate answer burst (%d); restarting exponential backoff sequence (%d)", - q->qname.c, DNSTypeName(q->qtype), q->RecentAnswerPkts, q->ThisQInterval); - q->LastQTime = m->timenow - InitialQuestionInterval + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*4); - q->ThisQInterval = InitialQuestionInterval; - SetNextQueryTime(m,q); - } - } - verbosedebugf("CacheRecordAdd %p %##s (%s) %lu %#a:%d question %p", rr, rr->resrec.name->c, - DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl, rr->resrec.rDNSServer ? - &rr->resrec.rDNSServer->addr : mDNSNULL, mDNSVal16(rr->resrec.rDNSServer ? - rr->resrec.rDNSServer->port : zeroIPPort), q); - q->CurrentAnswers++; - q->unansweredQueries = 0; - 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 = 0; - rr->UnansweredQueries = MaxUnansweredQueries; - } - } - } - - if (!rr->DelayDelivery) - { - if (m->CurrentQuestion) - LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); - m->CurrentQuestion = m->Questions; - while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) - { - q = m->CurrentQuestion; - if (ResourceRecordAnswersQuestion(&rr->resrec, q)) - AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add); - if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now - m->CurrentQuestion = q->next; - } - m->CurrentQuestion = mDNSNULL; - } - - SetNextCacheCheckTimeForRecord(m, rr); - } +{ + DNSQuestion *q; + + // We stop when we get to NewQuestions -- if we increment their CurrentAnswers/LargeAnswers/UniqueAnswers + // counters here we'll end up double-incrementing them when we do it again in AnswerNewQuestion(). + for (q = m->Questions; q && q != m->NewQuestions; q=q->next) + { + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + { + // If this question is one that's actively sending queries, and it's received ten answers within one + // second of sending the last query packet, then that indicates some radical network topology change, + // so reset its exponential backoff back to the start. We must be at least at the eight-second interval + // to do this. If we're at the four-second interval, or less, there's not much benefit accelerating + // because we will anyway send another query within a few seconds. The first reset query is sent out + // randomized over the next four seconds to reduce possible synchronization between machines. + if (q->LastAnswerPktNum != m->PktNum) + { + q->LastAnswerPktNum = m->PktNum; + if (mDNSOpaque16IsZero(q->TargetQID) && ActiveQuestion(q) && ++q->RecentAnswerPkts >= 10 && + q->ThisQInterval > InitialQuestionInterval * QuestionIntervalStep3 && m->timenow - q->LastQTxTime < mDNSPlatformOneSecond) + { + LogMsg("CacheRecordAdd: %##s (%s) got immediate answer burst (%d); restarting exponential backoff sequence (%d)", + q->qname.c, DNSTypeName(q->qtype), q->RecentAnswerPkts, q->ThisQInterval); + q->LastQTime = m->timenow - InitialQuestionInterval + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*4); + q->ThisQInterval = InitialQuestionInterval; + SetNextQueryTime(m,q); + } + } + verbosedebugf("CacheRecordAdd %p %##s (%s) %lu %#a:%d question %p", rr, rr->resrec.name->c, + DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl, rr->resrec.rDNSServer ? + &rr->resrec.rDNSServer->addr : mDNSNULL, mDNSVal16(rr->resrec.rDNSServer ? + rr->resrec.rDNSServer->port : zeroIPPort), q); + q->CurrentAnswers++; + q->unansweredQueries = 0; + 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 = 0; + rr->UnansweredQueries = MaxUnansweredQueries; + } + } + } + + if (!rr->DelayDelivery) + { + if (m->CurrentQuestion) + LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) + { + q = m->CurrentQuestion; + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add); + if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now + m->CurrentQuestion = q->next; + } + m->CurrentQuestion = mDNSNULL; + } + + SetNextCacheCheckTimeForRecord(m, rr); +} // NoCacheAnswer is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call. // If new questions are created as a result of invoking client callbacks, they will be added to @@ -3585,23 +3689,23 @@ mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr) // which may change the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. mDNSlocal void NoCacheAnswer(mDNS *const m, CacheRecord *rr) - { - LogMsg("No cache space: Delivering non-cached result for %##s", m->rec.r.resrec.name->c); - if (m->CurrentQuestion) - LogMsg("NoCacheAnswer ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); - m->CurrentQuestion = m->Questions; - // We do this for *all* questions, not stopping when we get to m->NewQuestions, - // since we're not caching the record and we'll get no opportunity to do this later - while (m->CurrentQuestion) - { - DNSQuestion *q = m->CurrentQuestion; - if (ResourceRecordAnswersQuestion(&rr->resrec, q)) - AnswerCurrentQuestionWithResourceRecord(m, rr, QC_addnocache); // QC_addnocache means "don't expect remove events for this" - if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now - m->CurrentQuestion = q->next; - } - m->CurrentQuestion = mDNSNULL; - } +{ + LogMsg("No cache space: Delivering non-cached result for %##s", m->rec.r.resrec.name->c); + if (m->CurrentQuestion) + LogMsg("NoCacheAnswer ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = m->Questions; + // We do this for *all* questions, not stopping when we get to m->NewQuestions, + // since we're not caching the record and we'll get no opportunity to do this later + while (m->CurrentQuestion) + { + DNSQuestion *q = m->CurrentQuestion; + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + AnswerCurrentQuestionWithResourceRecord(m, rr, QC_addnocache); // QC_addnocache means "don't expect remove events for this" + if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now + m->CurrentQuestion = q->next; + } + m->CurrentQuestion = mDNSNULL; +} // CacheRecordRmv is only called from CheckCacheExpiration, which is called from mDNS_Execute. // Note that CacheRecordRmv is *only* called for records that are referenced by at least one active question. @@ -3613,966 +3717,1035 @@ mDNSlocal void NoCacheAnswer(mDNS *const m, CacheRecord *rr) // which may change the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr) - { - if (m->CurrentQuestion) - LogMsg("CacheRecordRmv ERROR m->CurrentQuestion already set: %##s (%s)", - m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); - m->CurrentQuestion = m->Questions; - - // We stop when we get to NewQuestions -- for new questions their CurrentAnswers/LargeAnswers/UniqueAnswers counters - // will all still be zero because we haven't yet gone through the cache counting how many answers we have for them. - while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) - { - DNSQuestion *q = m->CurrentQuestion; - // When a question enters suppressed state, we generate RMV events and generate a negative - // response. A cache may be present that answers this question e.g., cache entry generated - // before the question became suppressed. We need to skip the suppressed questions here as - // the RMV event has already been generated. - if (!QuerySuppressed(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) - { - verbosedebugf("CacheRecordRmv %p %s", rr, CRDisplayString(m, rr)); - q->FlappingInterface1 = mDNSNULL; - q->FlappingInterface2 = mDNSNULL; - - // When a question changes DNS server, it is marked with deliverAddEvents if we find any - // cache entry corresponding to the new DNS server. Before we deliver the ADD event, the - // cache entry may be removed in which case CurrentAnswers can be zero. - if (q->deliverAddEvents && !q->CurrentAnswers) - { - LogInfo("CacheRecordRmv: Question %p %##s (%s) deliverAddEvents set, DNSServer %#a:%d", - q, q->qname.c, DNSTypeName(q->qtype), q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, - mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort)); - m->CurrentQuestion = q->next; - continue; - } - if (q->CurrentAnswers == 0) - LogMsg("CacheRecordRmv ERROR!!: How can CurrentAnswers already be zero for %p %##s (%s) DNSServer %#a:%d", - q, q->qname.c, DNSTypeName(q->qtype), q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, - mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort)); - else - { - q->CurrentAnswers--; - if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--; - if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--; - } - if (rr->resrec.rdata->MaxRDLength) // Never generate "remove" events for negative results - { - if (q->CurrentAnswers == 0) - { - LogInfo("CacheRecordRmv: Last answer for %##s (%s) expired from cache; will reconfirm antecedents", - q->qname.c, DNSTypeName(q->qtype)); - ReconfirmAntecedents(m, &q->qname, q->qnamehash, 0); - } - AnswerCurrentQuestionWithResourceRecord(m, rr, QC_rmv); - } - } - if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now - m->CurrentQuestion = q->next; - } - m->CurrentQuestion = mDNSNULL; - } +{ + if (m->CurrentQuestion) + LogMsg("CacheRecordRmv ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = m->Questions; + + // We stop when we get to NewQuestions -- for new questions their CurrentAnswers/LargeAnswers/UniqueAnswers counters + // will all still be zero because we haven't yet gone through the cache counting how many answers we have for them. + while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) + { + DNSQuestion *q = m->CurrentQuestion; + // When a question enters suppressed state, we generate RMV events and generate a negative + // response. A cache may be present that answers this question e.g., cache entry generated + // before the question became suppressed. We need to skip the suppressed questions here as + // the RMV event has already been generated. + if (!QuerySuppressed(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) + { + verbosedebugf("CacheRecordRmv %p %s", rr, CRDisplayString(m, rr)); + q->FlappingInterface1 = mDNSNULL; + q->FlappingInterface2 = mDNSNULL; + + if (q->CurrentAnswers == 0) + LogMsg("CacheRecordRmv ERROR!!: How can CurrentAnswers already be zero for %p %##s (%s) DNSServer %#a:%d", + q, q->qname.c, DNSTypeName(q->qtype), q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, + mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort)); + else + { + q->CurrentAnswers--; + if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--; + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--; + } + if (rr->resrec.rdata->MaxRDLength) // Never generate "remove" events for negative results + { + if (q->CurrentAnswers == 0) + { + LogInfo("CacheRecordRmv: Last answer for %##s (%s) expired from cache; will reconfirm antecedents", + q->qname.c, DNSTypeName(q->qtype)); + ReconfirmAntecedents(m, &q->qname, q->qnamehash, 0); + } + AnswerCurrentQuestionWithResourceRecord(m, rr, QC_rmv); + } + } + if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now + m->CurrentQuestion = q->next; + } + m->CurrentQuestion = mDNSNULL; +} mDNSlocal void ReleaseCacheEntity(mDNS *const m, CacheEntity *e) - { +{ #if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING >= 1 - unsigned int i; - for (i=0; inext = m->rrcache_free; - m->rrcache_free = e; - m->rrcache_totalused--; - } + e->next = m->rrcache_free; + m->rrcache_free = e; + m->rrcache_totalused--; +} mDNSlocal void ReleaseCacheGroup(mDNS *const m, CacheGroup **cp) - { - CacheEntity *e = (CacheEntity *)(*cp); - //LogMsg("ReleaseCacheGroup: Releasing CacheGroup for %p, %##s", (*cp)->name->c, (*cp)->name->c); - if ((*cp)->rrcache_tail != &(*cp)->members) - LogMsg("ERROR: (*cp)->members == mDNSNULL but (*cp)->rrcache_tail != &(*cp)->members)"); - //if ((*cp)->name != (domainname*)((*cp)->namestorage)) - // LogMsg("ReleaseCacheGroup: %##s, %p %p", (*cp)->name->c, (*cp)->name, (domainname*)((*cp)->namestorage)); - if ((*cp)->name != (domainname*)((*cp)->namestorage)) mDNSPlatformMemFree((*cp)->name); - (*cp)->name = mDNSNULL; - *cp = (*cp)->next; // Cut record from list - ReleaseCacheEntity(m, e); - } - -mDNSlocal void ReleaseCacheRecord(mDNS *const m, CacheRecord *r) - { - //LogMsg("ReleaseCacheRecord: Releasing %s", CRDisplayString(m, r)); - if (r->resrec.rdata && r->resrec.rdata != (RData*)&r->smallrdatastorage) mDNSPlatformMemFree(r->resrec.rdata); - r->resrec.rdata = mDNSNULL; - ReleaseCacheEntity(m, (CacheEntity *)r); - } +{ + CacheEntity *e = (CacheEntity *)(*cp); + //LogMsg("ReleaseCacheGroup: Releasing CacheGroup for %p, %##s", (*cp)->name->c, (*cp)->name->c); + if ((*cp)->rrcache_tail != &(*cp)->members) + LogMsg("ERROR: (*cp)->members == mDNSNULL but (*cp)->rrcache_tail != &(*cp)->members)"); + //if ((*cp)->name != (domainname*)((*cp)->namestorage)) + // LogMsg("ReleaseCacheGroup: %##s, %p %p", (*cp)->name->c, (*cp)->name, (domainname*)((*cp)->namestorage)); + if ((*cp)->name != (domainname*)((*cp)->namestorage)) mDNSPlatformMemFree((*cp)->name); + (*cp)->name = mDNSNULL; + *cp = (*cp)->next; // Cut record from list + ReleaseCacheEntity(m, e); +} + +mDNSexport void ReleaseCacheRecord(mDNS *const m, CacheRecord *r) +{ + CacheGroup *cg; + CacheRecord **rp; + const mDNSu32 slot = HashSlot(r->resrec.name); + + //LogMsg("ReleaseCacheRecord: Releasing %s", CRDisplayString(m, r)); + if (r->resrec.rdata && r->resrec.rdata != (RData*)&r->smallrdatastorage) mDNSPlatformMemFree(r->resrec.rdata); + r->resrec.rdata = mDNSNULL; + + cg = CacheGroupForRecord(m, slot, &r->resrec); + if (!cg) LogMsg("ReleaseCacheRecord: ERROR!! cg NULL for %##s (%s)", r->resrec.name->c, DNSTypeName(r->resrec.rrtype)); + + // When NSEC records are not added to the cache, it is usually cached at the "nsec" list + // of the CacheRecord. But sometimes they may be freed without adding to the "nsec" list + // (which is handled below) and in that case it should be freed here. + if (r->resrec.name && cg && r->resrec.name != cg->name) + { + LogInfo("ReleaseCacheRecord: freeing %##s (%s)", r->resrec.name->c, DNSTypeName(r->resrec.rrtype)); + mDNSPlatformMemFree((void *)r->resrec.name); + } + r->resrec.name = mDNSNULL; + + rp = &(r->nsec); + while (*rp) + { + CacheRecord *rr = *rp; + *rp = (*rp)->next; // Cut record from list + if (rr->resrec.rdata && rr->resrec.rdata != (RData*)&rr->smallrdatastorage) + { + mDNSPlatformMemFree(rr->resrec.rdata); + rr->resrec.rdata = mDNSNULL; + } + // NSEC records that are added to the "nsec" list does not share the name + // of the CacheGroup. + if (rr->resrec.name) + { + LogInfo("ReleaseCacheRecord: freeing cached nsec %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + mDNSPlatformMemFree((void *)rr->resrec.name); + rr->resrec.name = mDNSNULL; + } + ReleaseCacheEntity(m, (CacheEntity *)rr); + } + ReleaseCacheEntity(m, (CacheEntity *)r); +} // 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, CacheGroup *const cg) - { - CacheRecord **rp = &cg->members; - - if (m->lock_rrcache) { LogMsg("CheckCacheExpiration ERROR! Cache already locked!"); return; } - m->lock_rrcache = 1; - - while (*rp) - { - CacheRecord *const rr = *rp; - mDNSs32 event = RRExpireTime(rr); - if (m->timenow - event >= 0) // If expired, delete it - { - *rp = rr->next; // Cut it from the list - verbosedebugf("CheckCacheExpiration: Deleting%7d %7d %p %s", - m->timenow - rr->TimeRcvd, rr->resrec.rroriginalttl, rr->CRActiveQuestion, CRDisplayString(m, rr)); - if (rr->CRActiveQuestion) // If this record has one or more active questions, tell them it's going away - { - DNSQuestion *q = rr->CRActiveQuestion; - // When a cache record is about to expire, we expect to do four queries at 80-82%, 85-87%, 90-92% and - // then 95-97% of the TTL. If the DNS server does not respond, then we will remove the cache entry - // before we pick a new DNS server. As the question interval is set to MaxQuestionInterval, we may - // not send out a query anytime soon. Hence, we need to reset the question interval. If this is - // a normal deferred ADD case, then AnswerCurrentQuestionWithResourceRecord will reset it to - // MaxQuestionInterval. If we have inactive questions referring to negative cache entries, - // don't ressurect them as they will deliver duplicate "No such Record" ADD events - if (!mDNSOpaque16IsZero(q->TargetQID) && !q->LongLived && ActiveQuestion(q)) - { - q->ThisQInterval = InitialQuestionInterval; - q->LastQTime = m->timenow - q->ThisQInterval; - SetNextQueryTime(m, q); - } - CacheRecordRmv(m, rr); - m->rrcache_active--; - } - ReleaseCacheRecord(m, rr); - } - else // else, not expired; see if we need to query - { - // If waiting to delay delivery, do nothing until then - if (rr->DelayDelivery && rr->DelayDelivery - m->timenow > 0) - event = rr->DelayDelivery; - else - { - if (rr->DelayDelivery) CacheRecordDeferredAdd(m, rr); - if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) - { - if (m->timenow - rr->NextRequiredQuery < 0) // If not yet time for next query - event = NextCacheCheckEvent(rr); // 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 SetNextCacheCheckTimeForRecord(), - // which will correctly update m->NextCacheCheck for us. - event = m->timenow + 0x3FFFFFFF; - } - } - } - verbosedebugf("CheckCacheExpiration:%6d %5d %s", - (event - m->timenow) / mDNSPlatformOneSecond, CacheCheckGracePeriod(rr), CRDisplayString(m, rr)); - if (m->rrcache_nextcheck[slot] - event > 0) - m->rrcache_nextcheck[slot] = event; - rp = &rr->next; - } - } - if (cg->rrcache_tail != rp) verbosedebugf("CheckCacheExpiration: Updating CacheGroup tail from %p to %p", cg->rrcache_tail, rp); - cg->rrcache_tail = rp; - m->lock_rrcache = 0; - } +{ + CacheRecord **rp = &cg->members; + + if (m->lock_rrcache) { LogMsg("CheckCacheExpiration ERROR! Cache already locked!"); return; } + m->lock_rrcache = 1; + + while (*rp) + { + CacheRecord *const rr = *rp; + mDNSs32 event = RRExpireTime(rr); + if (m->timenow - event >= 0) // If expired, delete it + { + *rp = rr->next; // Cut it from the list + verbosedebugf("CheckCacheExpiration: Deleting%7d %7d %p %s", + m->timenow - rr->TimeRcvd, rr->resrec.rroriginalttl, rr->CRActiveQuestion, CRDisplayString(m, rr)); + if (rr->CRActiveQuestion) // If this record has one or more active questions, tell them it's going away + { + DNSQuestion *q = rr->CRActiveQuestion; + // When a cache record is about to expire, we expect to do four queries at 80-82%, 85-87%, 90-92% and + // then 95-97% of the TTL. If the DNS server does not respond, then we will remove the cache entry + // before we pick a new DNS server. As the question interval is set to MaxQuestionInterval, we may + // not send out a query anytime soon. Hence, we need to reset the question interval. If this is + // a normal deferred ADD case, then AnswerCurrentQuestionWithResourceRecord will reset it to + // MaxQuestionInterval. If we have inactive questions referring to negative cache entries, + // don't ressurect them as they will deliver duplicate "No such Record" ADD events + if (!mDNSOpaque16IsZero(q->TargetQID) && !q->LongLived && ActiveQuestion(q)) + { + q->ThisQInterval = InitialQuestionInterval; + q->LastQTime = m->timenow - q->ThisQInterval; + SetNextQueryTime(m, q); + } + CacheRecordRmv(m, rr); + m->rrcache_active--; + } + ReleaseCacheRecord(m, rr); + } + else // else, not expired; see if we need to query + { + // If waiting to delay delivery, do nothing until then + if (rr->DelayDelivery && rr->DelayDelivery - m->timenow > 0) + event = rr->DelayDelivery; + else + { + if (rr->DelayDelivery) CacheRecordDeferredAdd(m, rr); + if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) + { + if (m->timenow - rr->NextRequiredQuery < 0) // If not yet time for next query + event = NextCacheCheckEvent(rr); // 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 SetNextCacheCheckTimeForRecord(), + // which will correctly update m->NextCacheCheck for us. + event = m->timenow + 0x3FFFFFFF; + } + } + } + verbosedebugf("CheckCacheExpiration:%6d %5d %s", + (event - m->timenow) / mDNSPlatformOneSecond, CacheCheckGracePeriod(rr), CRDisplayString(m, rr)); + if (m->rrcache_nextcheck[slot] - event > 0) + m->rrcache_nextcheck[slot] = event; + rp = &rr->next; + } + } + if (cg->rrcache_tail != rp) verbosedebugf("CheckCacheExpiration: Updating CacheGroup tail from %p to %p", cg->rrcache_tail, rp); + cg->rrcache_tail = rp; + m->lock_rrcache = 0; +} mDNSlocal void AnswerNewQuestion(mDNS *const m) - { - mDNSBool ShouldQueryImmediately = mDNStrue; - DNSQuestion *const q = m->NewQuestions; // Grab the question we're going to answer - mDNSu32 slot = HashSlot(&q->qname); - CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); - AuthRecord *lr; - AuthGroup *ag; - mDNSBool AnsweredFromCache = mDNSfalse; - - verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - - if (cg) CheckCacheExpiration(m, slot, cg); - if (m->NewQuestions != q) { LogInfo("AnswerNewQuestion: Question deleted while doing CheckCacheExpiration"); goto exit; } - m->NewQuestions = q->next; - // Advance NewQuestions to the next *after* calling CheckCacheExpiration, because if we advance it first - // then CheckCacheExpiration may give this question add/remove callbacks, and it's not yet ready for that. - // - // Also, CheckCacheExpiration() calls CacheRecordDeferredAdd() and CacheRecordRmv(), which invoke - // client callbacks, which may delete their own or any other question. Our mechanism for detecting - // whether our current m->NewQuestions question got deleted by one of these callbacks is to store the - // value of m->NewQuestions in 'q' before calling CheckCacheExpiration(), and then verify afterwards - // that they're still the same. If m->NewQuestions has changed (because mDNS_StopQuery_internal - // advanced it), that means the question was deleted, so we no longer need to worry about answering - // it (and indeed 'q' is now a dangling pointer, so dereferencing it at all would be bad, and the - // values we computed for slot and cg are now stale and relate to a question that no longer exists). - // - // We can't use the usual m->CurrentQuestion mechanism for this because CacheRecordDeferredAdd() and - // CacheRecordRmv() both use that themselves when walking the list of (non-new) questions generating callbacks. - // Fortunately mDNS_StopQuery_internal auto-advances both m->CurrentQuestion *AND* m->NewQuestions when - // deleting a question, so luckily we have an easy alternative way of detecting if our question got deleted. - - if (m->lock_rrcache) LogMsg("AnswerNewQuestion ERROR! Cache already locked!"); - // This should be safe, because calling the client's question callback may cause the - // question list to be modified, but should not ever cause the rrcache list to be modified. - // If the client's question callback deletes the question, then m->CurrentQuestion will - // be advanced, and we'll exit out of the loop - m->lock_rrcache = 1; - if (m->CurrentQuestion) - LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set: %##s (%s)", - m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); - m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted - - if (q->NoAnswer == NoAnswer_Fail) - { - LogMsg("AnswerNewQuestion: NoAnswer_Fail %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any, q->qDNSServer); - q->NoAnswer = NoAnswer_Normal; // Temporarily turn off answer suppression - AnswerCurrentQuestionWithResourceRecord(m, &m->rec.r, QC_addnocache); - // Don't touch the question if it has been stopped already - if (m->CurrentQuestion == q) q->NoAnswer = NoAnswer_Fail; // Restore NoAnswer state - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - } - if (m->CurrentQuestion != q) { LogInfo("AnswerNewQuestion: Question deleted while generating NoAnswer_Fail response"); goto exit; } - - // See if we want to tell it about LocalOnly records - if (m->CurrentRecord) - LogMsg("AnswerNewQuestion ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); - slot = AuthHashSlot(&q->qname); - ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname); - if (ag) - { - m->CurrentRecord = ag->members; - while (m->CurrentRecord && m->CurrentRecord != ag->NewLocalOnlyRecords) - { - AuthRecord *rr = m->CurrentRecord; - m->CurrentRecord = rr->next; - // - // If the question is mDNSInterface_LocalOnly, all records local to the machine should be used - // to answer the query. This is handled in AnswerNewLocalOnlyQuestion. - // - // We handle mDNSInterface_Any and scoped questions here. See LocalOnlyRecordAnswersQuestion for more - // details on how we handle this case. For P2P we just handle "Interface_Any" questions. For LocalOnly - // we handle both mDNSInterface_Any and scoped questions. - - if (rr->ARType == AuthRecordLocalOnly || (rr->ARType == AuthRecordP2P && q->InterfaceID == mDNSInterface_Any)) - if (LocalOnlyRecordAnswersQuestion(rr, q)) - { - AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue); - if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here - } - } - } - m->CurrentRecord = mDNSNULL; - - if (m->CurrentQuestion != q) { LogInfo("AnswerNewQuestion: Question deleted while while giving LocalOnly record answers"); goto exit; } - - if (q->LOAddressAnswers) - { - LogInfo("AnswerNewQuestion: Question %p %##s (%s) answered using local auth records LOAddressAnswers %d", - q, q->qname.c, DNSTypeName(q->qtype), q->LOAddressAnswers); - goto exit; - } - - // Before we go check the cache and ship this query on the wire, we have to be sure that there are - // no local records that could possibly answer this question. As we did not check the NewLocalRecords, we - // need to just peek at them to see whether it will answer this question. If it would answer, pretend - // that we answered. AnswerAllLocalQuestionsWithLocalAuthRecord will answer shortly. This happens normally - // when we add new /etc/hosts entries and restart the question. It is a new question and also a new record. - if (ag) - { - lr = ag->NewLocalOnlyRecords; - while (lr) - { - if (LORecordAnswersAddressType(lr) && LocalOnlyRecordAnswersQuestion(lr, q)) - { - LogInfo("AnswerNewQuestion: Question %p %##s (%s) will be answered using new local auth records " - " LOAddressAnswers %d", q, q->qname.c, DNSTypeName(q->qtype), q->LOAddressAnswers); - goto exit; - } - lr = lr->next; - } - } - - - // If we are not supposed to answer this question, generate a negative response. - // Temporarily suspend the SuppressQuery so that AnswerCurrentQuestionWithResourceRecord can answer the question - if (QuerySuppressed(q)) { q->SuppressQuery = mDNSfalse; GenerateNegativeResponse(m); q->SuppressQuery = mDNStrue; } - else - { - CacheRecord *rr; - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) - if (SameNameRecordAnswersQuestion(&rr->resrec, q)) - { - // SecsSinceRcvd is whole number of elapsed seconds, rounded down - mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond; - if (rr->resrec.rroriginalttl <= SecsSinceRcvd) - { - LogMsg("AnswerNewQuestion: How is rr->resrec.rroriginalttl %lu <= SecsSinceRcvd %lu for %s %d %d", - rr->resrec.rroriginalttl, SecsSinceRcvd, CRDisplayString(m, rr), m->timenow, rr->TimeRcvd); - continue; // Go to next one in loop - } - - // If this record set is marked unique, then that means we can reasonably assume we have the whole set - // -- we don't need to rush out on the network and query immediately to see if there are more answers out there - if ((rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) || (q->ExpectUnique)) - ShouldQueryImmediately = mDNSfalse; - q->CurrentAnswers++; - if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; - if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; - AnsweredFromCache = mDNStrue; - AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add); - if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here - } - else if (RRTypeIsAddressType(rr->resrec.rrtype) && RRTypeIsAddressType(q->qtype)) - ShouldQueryImmediately = mDNSfalse; - } - // We don't use LogInfo for this "Question deleted" message because it happens so routinely that - // it's not remotely remarkable, and therefore unlikely to be of much help tracking down bugs. - if (m->CurrentQuestion != q) { debugf("AnswerNewQuestion: Question deleted while giving cache answers"); goto exit; } - - // Neither a local record nor a cache entry could answer this question. If this question need to be retried - // with search domains, generate a negative response which will now retry after appending search domains. - // If the query was suppressed above, we already generated a negative response. When it gets unsuppressed, - // we will retry with search domains. - if (!QuerySuppressed(q) && !AnsweredFromCache && q->RetryWithSearchDomains) - { - LogInfo("AnswerNewQuestion: Generating response for retrying with search domains %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - GenerateNegativeResponse(m); - } - - if (m->CurrentQuestion != q) { debugf("AnswerNewQuestion: Question deleted while giving negative answer"); goto exit; } - - // Note: When a query gets suppressed or retried with search domains, we de-activate the question. - // Hence we don't execute the following block of code for those cases. - if (ShouldQueryImmediately && ActiveQuestion(q)) - { - debugf("AnswerNewQuestion: ShouldQueryImmediately %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - q->ThisQInterval = InitialQuestionInterval; - q->LastQTime = m->timenow - q->ThisQInterval; - if (mDNSOpaque16IsZero(q->TargetQID)) // For mDNS, spread packets to avoid a burst of simultaneous queries - { - // Compute random delay in the range 1-6 seconds, then divide by 50 to get 20-120ms - if (!m->RandomQueryDelay) - m->RandomQueryDelay = (mDNSPlatformOneSecond + mDNSRandom(mDNSPlatformOneSecond*5) - 1) / 50 + 1; - q->LastQTime += m->RandomQueryDelay; - } - } - - // IN ALL CASES make sure that m->NextScheduledQuery is set appropriately. - // In cases where m->NewQuestions->DelayAnswering is set, we may have delayed generating our - // answers for this question until *after* its scheduled transmission time, in which case - // m->NextScheduledQuery may now be set to 'never', and in that case -- even though we're *not* doing - // ShouldQueryImmediately -- we still need to make sure we set m->NextScheduledQuery correctly. - SetNextQueryTime(m,q); +{ + mDNSBool ShouldQueryImmediately = mDNStrue; + DNSQuestion *const q = m->NewQuestions; // Grab the question we're going to answer + mDNSu32 slot = HashSlot(&q->qname); + CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + AuthRecord *lr; + AuthGroup *ag; + mDNSBool AnsweredFromCache = mDNSfalse; + + verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + + if (cg) CheckCacheExpiration(m, slot, cg); + if (m->NewQuestions != q) { LogInfo("AnswerNewQuestion: Question deleted while doing CheckCacheExpiration"); goto exit; } + m->NewQuestions = q->next; + // Advance NewQuestions to the next *after* calling CheckCacheExpiration, because if we advance it first + // then CheckCacheExpiration may give this question add/remove callbacks, and it's not yet ready for that. + // + // Also, CheckCacheExpiration() calls CacheRecordDeferredAdd() and CacheRecordRmv(), which invoke + // client callbacks, which may delete their own or any other question. Our mechanism for detecting + // whether our current m->NewQuestions question got deleted by one of these callbacks is to store the + // value of m->NewQuestions in 'q' before calling CheckCacheExpiration(), and then verify afterwards + // that they're still the same. If m->NewQuestions has changed (because mDNS_StopQuery_internal + // advanced it), that means the question was deleted, so we no longer need to worry about answering + // it (and indeed 'q' is now a dangling pointer, so dereferencing it at all would be bad, and the + // values we computed for slot and cg are now stale and relate to a question that no longer exists). + // + // We can't use the usual m->CurrentQuestion mechanism for this because CacheRecordDeferredAdd() and + // CacheRecordRmv() both use that themselves when walking the list of (non-new) questions generating callbacks. + // Fortunately mDNS_StopQuery_internal auto-advances both m->CurrentQuestion *AND* m->NewQuestions when + // deleting a question, so luckily we have an easy alternative way of detecting if our question got deleted. + + if (m->lock_rrcache) LogMsg("AnswerNewQuestion ERROR! Cache already locked!"); + // This should be safe, because calling the client's question callback may cause the + // question list to be modified, but should not ever cause the rrcache list to be modified. + // If the client's question callback deletes the question, then m->CurrentQuestion will + // be advanced, and we'll exit out of the loop + m->lock_rrcache = 1; + if (m->CurrentQuestion) + LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted + + if (q->NoAnswer == NoAnswer_Fail) + { + LogMsg("AnswerNewQuestion: NoAnswer_Fail %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any, q->qDNSServer); + q->NoAnswer = NoAnswer_Normal; // Temporarily turn off answer suppression + AnswerCurrentQuestionWithResourceRecord(m, &m->rec.r, QC_addnocache); + // Don't touch the question if it has been stopped already + if (m->CurrentQuestion == q) q->NoAnswer = NoAnswer_Fail; // Restore NoAnswer state + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + if (m->CurrentQuestion != q) { LogInfo("AnswerNewQuestion: Question deleted while generating NoAnswer_Fail response"); goto exit; } + + // See if we want to tell it about LocalOnly records + if (m->CurrentRecord) + LogMsg("AnswerNewQuestion ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + slot = AuthHashSlot(&q->qname); + ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname); + if (ag) + { + m->CurrentRecord = ag->members; + while (m->CurrentRecord && m->CurrentRecord != ag->NewLocalOnlyRecords) + { + AuthRecord *rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + // + // If the question is mDNSInterface_LocalOnly, all records local to the machine should be used + // to answer the query. This is handled in AnswerNewLocalOnlyQuestion. + // + // We handle mDNSInterface_Any and scoped questions here. See LocalOnlyRecordAnswersQuestion for more + // details on how we handle this case. For P2P we just handle "Interface_Any" questions. For LocalOnly + // we handle both mDNSInterface_Any and scoped questions. + + if (rr->ARType == AuthRecordLocalOnly || (rr->ARType == AuthRecordP2P && q->InterfaceID == mDNSInterface_Any)) + if (LocalOnlyRecordAnswersQuestion(rr, q)) + { + AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue); + if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here + } + } + } + m->CurrentRecord = mDNSNULL; + + if (m->CurrentQuestion != q) { LogInfo("AnswerNewQuestion: Question deleted while while giving LocalOnly record answers"); goto exit; } + + if (q->LOAddressAnswers) + { + LogInfo("AnswerNewQuestion: Question %p %##s (%s) answered using local auth records LOAddressAnswers %d", + q, q->qname.c, DNSTypeName(q->qtype), q->LOAddressAnswers); + goto exit; + } + + // Before we go check the cache and ship this query on the wire, we have to be sure that there are + // no local records that could possibly answer this question. As we did not check the NewLocalRecords, we + // need to just peek at them to see whether it will answer this question. If it would answer, pretend + // that we answered. AnswerAllLocalQuestionsWithLocalAuthRecord will answer shortly. This happens normally + // when we add new /etc/hosts entries and restart the question. It is a new question and also a new record. + if (ag) + { + lr = ag->NewLocalOnlyRecords; + while (lr) + { + if (LORecordAnswersAddressType(lr) && LocalOnlyRecordAnswersQuestion(lr, q)) + { + LogInfo("AnswerNewQuestion: Question %p %##s (%s) will be answered using new local auth records " + " LOAddressAnswers %d", q, q->qname.c, DNSTypeName(q->qtype), q->LOAddressAnswers); + goto exit; + } + lr = lr->next; + } + } + + + // If we are not supposed to answer this question, generate a negative response. + // Temporarily suspend the SuppressQuery so that AnswerCurrentQuestionWithResourceRecord can answer the question + // + // If it is a question trying to validate some response, it already checked the cache for a response. If it still + // reissues a question it means it could not find the RRSIGs. So, we need to bypass the cache check and send + // the question out. + if (QuerySuppressed(q)) { q->SuppressQuery = mDNSfalse; GenerateNegativeResponse(m); q->SuppressQuery = mDNStrue; } + else if (!q->ValidatingResponse) + { + CacheRecord *rr; + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + if (SameNameRecordAnswersQuestion(&rr->resrec, q)) + { + // SecsSinceRcvd is whole number of elapsed seconds, rounded down + mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond; + if (rr->resrec.rroriginalttl <= SecsSinceRcvd) + { + LogMsg("AnswerNewQuestion: How is rr->resrec.rroriginalttl %lu <= SecsSinceRcvd %lu for %s %d %d", + rr->resrec.rroriginalttl, SecsSinceRcvd, CRDisplayString(m, rr), m->timenow, rr->TimeRcvd); + continue; // Go to next one in loop + } + + // If this record set is marked unique, then that means we can reasonably assume we have the whole set + // -- we don't need to rush out on the network and query immediately to see if there are more answers out there + if ((rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) || (q->ExpectUnique)) + ShouldQueryImmediately = mDNSfalse; + q->CurrentAnswers++; + if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; + AnsweredFromCache = mDNStrue; + AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add); + if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here + } + else if (RRTypeIsAddressType(rr->resrec.rrtype) && RRTypeIsAddressType(q->qtype)) + ShouldQueryImmediately = mDNSfalse; + } + // We don't use LogInfo for this "Question deleted" message because it happens so routinely that + // it's not remotely remarkable, and therefore unlikely to be of much help tracking down bugs. + if (m->CurrentQuestion != q) { debugf("AnswerNewQuestion: Question deleted while giving cache answers"); goto exit; } + + // Neither a local record nor a cache entry could answer this question. If this question need to be retried + // with search domains, generate a negative response which will now retry after appending search domains. + // If the query was suppressed above, we already generated a negative response. When it gets unsuppressed, + // we will retry with search domains. + if (!QuerySuppressed(q) && !AnsweredFromCache && q->RetryWithSearchDomains) + { + LogInfo("AnswerNewQuestion: Generating response for retrying with search domains %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + GenerateNegativeResponse(m); + } + + if (m->CurrentQuestion != q) { debugf("AnswerNewQuestion: Question deleted while giving negative answer"); goto exit; } + + // Note: When a query gets suppressed or retried with search domains, we de-activate the question. + // Hence we don't execute the following block of code for those cases. + if (ShouldQueryImmediately && ActiveQuestion(q)) + { + debugf("AnswerNewQuestion: ShouldQueryImmediately %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->ThisQInterval = InitialQuestionInterval; + q->LastQTime = m->timenow - q->ThisQInterval; + if (mDNSOpaque16IsZero(q->TargetQID)) // For mDNS, spread packets to avoid a burst of simultaneous queries + { + // Compute random delay in the range 1-6 seconds, then divide by 50 to get 20-120ms + if (!m->RandomQueryDelay) + m->RandomQueryDelay = (mDNSPlatformOneSecond + mDNSRandom(mDNSPlatformOneSecond*5) - 1) / 50 + 1; + q->LastQTime += m->RandomQueryDelay; + } + } + + // IN ALL CASES make sure that m->NextScheduledQuery is set appropriately. + // In cases where m->NewQuestions->DelayAnswering is set, we may have delayed generating our + // answers for this question until *after* its scheduled transmission time, in which case + // m->NextScheduledQuery may now be set to 'never', and in that case -- even though we're *not* doing + // ShouldQueryImmediately -- we still need to make sure we set m->NextScheduledQuery correctly. + SetNextQueryTime(m,q); exit: - m->CurrentQuestion = mDNSNULL; - m->lock_rrcache = 0; - } + m->CurrentQuestion = mDNSNULL; + m->lock_rrcache = 0; +} // When a NewLocalOnlyQuestion is created, AnswerNewLocalOnlyQuestion runs though our ResourceRecords delivering any // appropriate answers, stopping if it reaches a NewLocalOnlyRecord -- these will be handled by AnswerAllLocalQuestionsWithLocalAuthRecord mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m) - { - mDNSu32 slot; - AuthGroup *ag; - DNSQuestion *q = m->NewLocalOnlyQuestions; // Grab the question we're going to answer - m->NewLocalOnlyQuestions = q->next; // Advance NewLocalOnlyQuestions to the next (if any) - - debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - - if (m->CurrentQuestion) - LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentQuestion already set: %##s (%s)", - m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); - 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 %s", ARDisplayString(m, m->CurrentRecord)); - - // 1. First walk the LocalOnly records answering the LocalOnly question - // 2. As LocalOnly questions should also be answered by any other Auth records local to the machine, - // walk the ResourceRecords list delivering the answers - slot = AuthHashSlot(&q->qname); - ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname); - if (ag) - { - m->CurrentRecord = ag->members; - while (m->CurrentRecord && m->CurrentRecord != ag->NewLocalOnlyRecords) - { - AuthRecord *rr = m->CurrentRecord; - m->CurrentRecord = rr->next; - if (LocalOnlyRecordAnswersQuestion(rr, q)) - { - AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue); - if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here - } - } - } - - if (m->CurrentQuestion == q) - { - m->CurrentRecord = m->ResourceRecords; - - while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords) - { - AuthRecord *rr = m->CurrentRecord; - m->CurrentRecord = rr->next; - if (ResourceRecordAnswersQuestion(&rr->resrec, q)) - { - AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue); - if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here - } - } - } - - m->CurrentQuestion = mDNSNULL; - m->CurrentRecord = mDNSNULL; - } +{ + mDNSu32 slot; + AuthGroup *ag; + DNSQuestion *q = m->NewLocalOnlyQuestions; // Grab the question we're going to answer + m->NewLocalOnlyQuestions = q->next; // Advance NewLocalOnlyQuestions to the next (if any) + + debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + + if (m->CurrentQuestion) + LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + 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 %s", ARDisplayString(m, m->CurrentRecord)); + + // 1. First walk the LocalOnly records answering the LocalOnly question + // 2. As LocalOnly questions should also be answered by any other Auth records local to the machine, + // walk the ResourceRecords list delivering the answers + slot = AuthHashSlot(&q->qname); + ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname); + if (ag) + { + m->CurrentRecord = ag->members; + while (m->CurrentRecord && m->CurrentRecord != ag->NewLocalOnlyRecords) + { + AuthRecord *rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + if (LocalOnlyRecordAnswersQuestion(rr, q)) + { + AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue); + if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here + } + } + } + + if (m->CurrentQuestion == q) + { + m->CurrentRecord = m->ResourceRecords; + + while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords) + { + AuthRecord *rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + { + AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue); + if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here + } + } + } + + m->CurrentQuestion = mDNSNULL; + m->CurrentRecord = mDNSNULL; +} mDNSlocal CacheEntity *GetCacheEntity(mDNS *const m, const CacheGroup *const PreserveCG) - { - CacheEntity *e = mDNSNULL; - - if (m->lock_rrcache) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); } - m->lock_rrcache = 1; - - // If we have no free records, ask the client layer to give us some more memory - if (!m->rrcache_free && m->MainCallback) - { - if (m->rrcache_totalused != m->rrcache_size) - LogMsg("GetFreeCacheRR: count mismatch: m->rrcache_totalused %lu != m->rrcache_size %lu", - m->rrcache_totalused, m->rrcache_size); - - // We don't want to be vulnerable to a malicious attacker flooding us with an infinite - // number of bogus records so that we keep growing our cache until the machine runs out of memory. - // To guard against this, if our cache grows above 512kB (approx 3168 records at 164 bytes each), - // and we're actively using less than 1/32 of that cache, then we purge all the unused records - // and recycle them, instead of allocating more memory. - if (m->rrcache_size > 5000 && m->rrcache_size / 32 > m->rrcache_active) - LogInfo("Possible denial-of-service attack in progress: m->rrcache_size %lu; m->rrcache_active %lu", - m->rrcache_size, m->rrcache_active); - else - { - mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback - m->MainCallback(m, mStatus_GrowCache); - mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again - } - } - - // If we still have no free records, recycle all the records we can. - // Enumerating the entire cache is moderately expensive, so when we do it, we reclaim all the records we can in one pass. - if (!m->rrcache_free) - { - mDNSu32 oldtotalused = m->rrcache_totalused; - mDNSu32 slot; - for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) - { - CacheGroup **cp = &m->rrcache_hash[slot]; - while (*cp) - { - CacheRecord **rp = &(*cp)->members; - while (*rp) - { - // Records that answer still-active questions are not candidates for recycling - // Records that are currently linked into the CacheFlushRecords list may not be recycled, or we'll crash - if ((*rp)->CRActiveQuestion || (*rp)->NextInCFList) - rp=&(*rp)->next; - else - { - CacheRecord *rr = *rp; - *rp = (*rp)->next; // Cut record from list - ReleaseCacheRecord(m, rr); - } - } - if ((*cp)->rrcache_tail != rp) - verbosedebugf("GetFreeCacheRR: Updating rrcache_tail[%lu] from %p to %p", slot, (*cp)->rrcache_tail, rp); - (*cp)->rrcache_tail = rp; - if ((*cp)->members || (*cp)==PreserveCG) cp=&(*cp)->next; - else ReleaseCacheGroup(m, cp); - } - } - LogInfo("GetCacheEntity recycled %d records to reduce cache from %d to %d", - oldtotalused - m->rrcache_totalused, oldtotalused, m->rrcache_totalused); - } - - if (m->rrcache_free) // If there are records in the free list, take one - { - e = m->rrcache_free; - m->rrcache_free = e->next; - if (++m->rrcache_totalused >= m->rrcache_report) - { - LogInfo("RR Cache now using %ld objects", m->rrcache_totalused); - if (m->rrcache_report < 100) m->rrcache_report += 10; - else if (m->rrcache_report < 1000) m->rrcache_report += 100; - else m->rrcache_report += 1000; - } - mDNSPlatformMemZero(e, sizeof(*e)); - } - - m->lock_rrcache = 0; - - return(e); - } +{ + CacheEntity *e = mDNSNULL; + + if (m->lock_rrcache) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); } + m->lock_rrcache = 1; + + // If we have no free records, ask the client layer to give us some more memory + if (!m->rrcache_free && m->MainCallback) + { + if (m->rrcache_totalused != m->rrcache_size) + LogMsg("GetFreeCacheRR: count mismatch: m->rrcache_totalused %lu != m->rrcache_size %lu", + m->rrcache_totalused, m->rrcache_size); + + // We don't want to be vulnerable to a malicious attacker flooding us with an infinite + // number of bogus records so that we keep growing our cache until the machine runs out of memory. + // To guard against this, if our cache grows above 512kB (approx 3168 records at 164 bytes each), + // and we're actively using less than 1/32 of that cache, then we purge all the unused records + // and recycle them, instead of allocating more memory. + if (m->rrcache_size > 5000 && m->rrcache_size / 32 > m->rrcache_active) + LogInfo("Possible denial-of-service attack in progress: m->rrcache_size %lu; m->rrcache_active %lu", + m->rrcache_size, m->rrcache_active); + else + { + mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback + m->MainCallback(m, mStatus_GrowCache); + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + } + } + + // If we still have no free records, recycle all the records we can. + // Enumerating the entire cache is moderately expensive, so when we do it, we reclaim all the records we can in one pass. + if (!m->rrcache_free) + { + mDNSu32 oldtotalused = m->rrcache_totalused; + mDNSu32 slot; + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + { + CacheGroup **cp = &m->rrcache_hash[slot]; + while (*cp) + { + CacheRecord **rp = &(*cp)->members; + while (*rp) + { + // Records that answer still-active questions are not candidates for recycling + // Records that are currently linked into the CacheFlushRecords list may not be recycled, or we'll crash + if ((*rp)->CRActiveQuestion || (*rp)->NextInCFList) + rp=&(*rp)->next; + else + { + CacheRecord *rr = *rp; + *rp = (*rp)->next; // Cut record from list + ReleaseCacheRecord(m, rr); + } + } + if ((*cp)->rrcache_tail != rp) + verbosedebugf("GetFreeCacheRR: Updating rrcache_tail[%lu] from %p to %p", slot, (*cp)->rrcache_tail, rp); + (*cp)->rrcache_tail = rp; + if ((*cp)->members || (*cp)==PreserveCG) cp=&(*cp)->next; + else ReleaseCacheGroup(m, cp); + } + } + LogInfo("GetCacheEntity recycled %d records to reduce cache from %d to %d", + oldtotalused - m->rrcache_totalused, oldtotalused, m->rrcache_totalused); + } + + if (m->rrcache_free) // If there are records in the free list, take one + { + e = m->rrcache_free; + m->rrcache_free = e->next; + if (++m->rrcache_totalused >= m->rrcache_report) + { + LogInfo("RR Cache now using %ld objects", m->rrcache_totalused); + if (m->rrcache_report < 100) m->rrcache_report += 10; + else if (m->rrcache_report < 1000) m->rrcache_report += 100; + else m->rrcache_report += 1000; + } + mDNSPlatformMemZero(e, sizeof(*e)); + } + + m->lock_rrcache = 0; + + return(e); +} mDNSlocal CacheRecord *GetCacheRecord(mDNS *const m, CacheGroup *cg, mDNSu16 RDLength) - { - CacheRecord *r = (CacheRecord *)GetCacheEntity(m, cg); - if (r) - { - r->resrec.rdata = (RData*)&r->smallrdatastorage; // By default, assume we're usually going to be using local storage - if (RDLength > InlineCacheRDSize) // If RDLength is too big, allocate extra storage - { - r->resrec.rdata = (RData*)mDNSPlatformMemAllocate(sizeofRDataHeader + RDLength); - if (r->resrec.rdata) r->resrec.rdata->MaxRDLength = r->resrec.rdlength = RDLength; - else { ReleaseCacheEntity(m, (CacheEntity*)r); r = mDNSNULL; } - } - } - return(r); - } +{ + CacheRecord *r = (CacheRecord *)GetCacheEntity(m, cg); + if (r) + { + r->resrec.rdata = (RData*)&r->smallrdatastorage; // By default, assume we're usually going to be using local storage + if (RDLength > InlineCacheRDSize) // If RDLength is too big, allocate extra storage + { + r->resrec.rdata = (RData*)mDNSPlatformMemAllocate(sizeofRDataHeader + RDLength); + if (r->resrec.rdata) r->resrec.rdata->MaxRDLength = r->resrec.rdlength = RDLength; + else { ReleaseCacheEntity(m, (CacheEntity*)r); r = mDNSNULL; } + } + } + return(r); +} mDNSlocal CacheGroup *GetCacheGroup(mDNS *const m, const mDNSu32 slot, const ResourceRecord *const rr) - { - mDNSu16 namelen = DomainNameLength(rr->name); - CacheGroup *cg = (CacheGroup*)GetCacheEntity(m, mDNSNULL); - if (!cg) { LogMsg("GetCacheGroup: Failed to allocate memory for %##s", rr->name->c); return(mDNSNULL); } - cg->next = m->rrcache_hash[slot]; - cg->namehash = rr->namehash; - cg->members = mDNSNULL; - cg->rrcache_tail = &cg->members; - cg->name = (domainname*)cg->namestorage; - //LogMsg("GetCacheGroup: %-10s %d-byte cache name %##s", - // (namelen > InlineCacheGroupNameSize) ? "Allocating" : "Inline", namelen, rr->name->c); - if (namelen > InlineCacheGroupNameSize) cg->name = mDNSPlatformMemAllocate(namelen); - if (!cg->name) - { - LogMsg("GetCacheGroup: Failed to allocate name storage for %##s", rr->name->c); - ReleaseCacheEntity(m, (CacheEntity*)cg); - return(mDNSNULL); - } - AssignDomainName(cg->name, rr->name); - - if (CacheGroupForRecord(m, slot, rr)) LogMsg("GetCacheGroup: Already have CacheGroup for %##s", rr->name->c); - m->rrcache_hash[slot] = cg; - if (CacheGroupForRecord(m, slot, rr) != cg) LogMsg("GetCacheGroup: Not finding CacheGroup for %##s", rr->name->c); - - return(cg); - } +{ + mDNSu16 namelen = DomainNameLength(rr->name); + CacheGroup *cg = (CacheGroup*)GetCacheEntity(m, mDNSNULL); + if (!cg) { LogMsg("GetCacheGroup: Failed to allocate memory for %##s", rr->name->c); return(mDNSNULL); } + cg->next = m->rrcache_hash[slot]; + cg->namehash = rr->namehash; + cg->members = mDNSNULL; + cg->rrcache_tail = &cg->members; + if (namelen > sizeof(cg->namestorage)) + cg->name = mDNSPlatformMemAllocate(namelen); + else + cg->name = (domainname*)cg->namestorage; + if (!cg->name) + { + LogMsg("GetCacheGroup: Failed to allocate name storage for %##s", rr->name->c); + ReleaseCacheEntity(m, (CacheEntity*)cg); + return(mDNSNULL); + } + AssignDomainName(cg->name, rr->name); + + if (CacheGroupForRecord(m, slot, rr)) LogMsg("GetCacheGroup: Already have CacheGroup for %##s", rr->name->c); + m->rrcache_hash[slot] = cg; + if (CacheGroupForRecord(m, slot, rr) != cg) LogMsg("GetCacheGroup: Not finding CacheGroup for %##s", rr->name->c); + + return(cg); +} mDNSexport void mDNS_PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr) - { - if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("mDNS_PurgeCacheResourceRecord: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); - // Make sure we mark this record as thoroughly expired -- we don't ever want to give - // a positive answer using an expired record (e.g. from an interface that has gone away). - // We don't want to clear CRActiveQuestion here, because that would leave the record subject to - // summary deletion without giving the proper callback to any questions that are monitoring it. - // By setting UnansweredQueries to MaxUnansweredQueries we ensure it won't trigger any further expiration queries. - rr->TimeRcvd = m->timenow - mDNSPlatformOneSecond * 60; - rr->UnansweredQueries = MaxUnansweredQueries; - rr->resrec.rroriginalttl = 0; - SetNextCacheCheckTimeForRecord(m, rr); - } +{ + if (m->mDNS_busy != m->mDNS_reentrancy+1) + LogMsg("mDNS_PurgeCacheResourceRecord: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + // Make sure we mark this record as thoroughly expired -- we don't ever want to give + // a positive answer using an expired record (e.g. from an interface that has gone away). + // We don't want to clear CRActiveQuestion here, because that would leave the record subject to + // summary deletion without giving the proper callback to any questions that are monitoring it. + // By setting UnansweredQueries to MaxUnansweredQueries we ensure it won't trigger any further expiration queries. + rr->TimeRcvd = m->timenow - mDNSPlatformOneSecond * 60; + rr->UnansweredQueries = MaxUnansweredQueries; + rr->resrec.rroriginalttl = 0; + SetNextCacheCheckTimeForRecord(m, rr); +} mDNSexport mDNSs32 mDNS_TimeNow(const mDNS *const m) - { - mDNSs32 time; - mDNSPlatformLock(m); - if (m->mDNS_busy) - { - 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); - } - - if (m->timenow) time = m->timenow; - else time = mDNS_TimeNow_NoLock(m); - mDNSPlatformUnlock(m); - return(time); - } +{ + mDNSs32 time; + mDNSPlatformLock(m); + if (m->mDNS_busy) + { + 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); + } + + if (m->timenow) time = m->timenow; + else time = mDNS_TimeNow_NoLock(m); + mDNSPlatformUnlock(m); + return(time); +} // To avoid pointless CPU thrash, we use SetSPSProxyListChanged(X) to record the last interface that // had its Sleep Proxy client list change, and defer to actual BPF reconfiguration to mDNS_Execute(). // (GetNextScheduledEvent() returns "now" when m->SPSProxyListChanged is set) #define SetSPSProxyListChanged(X) do { \ - if (m->SPSProxyListChanged && m->SPSProxyListChanged != (X)) mDNSPlatformUpdateProxyList(m, m->SPSProxyListChanged); \ - m->SPSProxyListChanged = (X); } while(0) + if (m->SPSProxyListChanged && m->SPSProxyListChanged != (X)) mDNSPlatformUpdateProxyList(m, m->SPSProxyListChanged); \ + m->SPSProxyListChanged = (X); } while(0) // Called from mDNS_Execute() to expire stale proxy records mDNSlocal void CheckProxyRecords(mDNS *const m, AuthRecord *list) - { - m->CurrentRecord = list; - while (m->CurrentRecord) - { - AuthRecord *rr = m->CurrentRecord; - if (rr->resrec.RecordType != kDNSRecordTypeDeregistering && rr->WakeUp.HMAC.l[0]) - { - // If m->SPSSocket is NULL that means we're not acting as a sleep proxy any more, - // so we need to cease proxying for *all* records we may have, expired or not. - if (m->SPSSocket && m->timenow - rr->TimeExpire < 0) // If proxy record not expired yet, update m->NextScheduledSPS - { - if (m->NextScheduledSPS - rr->TimeExpire > 0) - m->NextScheduledSPS = rr->TimeExpire; - } - else // else proxy record expired, so remove it - { - LogSPS("CheckProxyRecords: Removing %d H-MAC %.6a I-MAC %.6a %d %s", - m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, ARDisplayString(m, rr)); - SetSPSProxyListChanged(rr->resrec.InterfaceID); - mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); - // Don't touch rr after this -- memory may have been free'd - } - } - // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because - // new records could have been added to the end of the list as a result of that call. - if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now - m->CurrentRecord = rr->next; - } - } +{ + m->CurrentRecord = list; + while (m->CurrentRecord) + { + AuthRecord *rr = m->CurrentRecord; + if (rr->resrec.RecordType != kDNSRecordTypeDeregistering && rr->WakeUp.HMAC.l[0]) + { + // If m->SPSSocket is NULL that means we're not acting as a sleep proxy any more, + // so we need to cease proxying for *all* records we may have, expired or not. + if (m->SPSSocket && m->timenow - rr->TimeExpire < 0) // If proxy record not expired yet, update m->NextScheduledSPS + { + if (m->NextScheduledSPS - rr->TimeExpire > 0) + m->NextScheduledSPS = rr->TimeExpire; + } + else // else proxy record expired, so remove it + { + LogSPS("CheckProxyRecords: Removing %d H-MAC %.6a I-MAC %.6a %d %s", + m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, ARDisplayString(m, rr)); + SetSPSProxyListChanged(rr->resrec.InterfaceID); + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); + // Don't touch rr after this -- memory may have been free'd + } + } + // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because + // new records could have been added to the end of the list as a result of that call. + if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now + m->CurrentRecord = rr->next; + } +} mDNSlocal void CheckRmvEventsForLocalRecords(mDNS *const m) - { - while (m->CurrentRecord) - { - AuthRecord *rr = m->CurrentRecord; - if (rr->AnsweredLocalQ && rr->resrec.RecordType == kDNSRecordTypeDeregistering) - { - debugf("CheckRmvEventsForLocalRecords: Generating local RMV events for %s", ARDisplayString(m, rr)); - rr->resrec.RecordType = kDNSRecordTypeShared; - AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse); - if (m->CurrentRecord == rr) // If rr still exists in list, restore its state now - { - rr->resrec.RecordType = kDNSRecordTypeDeregistering; - rr->AnsweredLocalQ = mDNSfalse; - // SendResponses normally calls CompleteDeregistration after sending goodbyes. - // For LocalOnly records, we don't do that and hence we need to do that here. - if (RRLocalOnly(rr)) CompleteDeregistration(m, rr); - } - } - if (m->CurrentRecord == rr) // If m->CurrentRecord was not auto-advanced, do it ourselves now - m->CurrentRecord = rr->next; - } - } +{ + while (m->CurrentRecord) + { + AuthRecord *rr = m->CurrentRecord; + if (rr->AnsweredLocalQ && rr->resrec.RecordType == kDNSRecordTypeDeregistering) + { + debugf("CheckRmvEventsForLocalRecords: Generating local RMV events for %s", ARDisplayString(m, rr)); + rr->resrec.RecordType = kDNSRecordTypeShared; + AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse); + if (m->CurrentRecord == rr) // If rr still exists in list, restore its state now + { + rr->resrec.RecordType = kDNSRecordTypeDeregistering; + rr->AnsweredLocalQ = mDNSfalse; + // SendResponses normally calls CompleteDeregistration after sending goodbyes. + // For LocalOnly records, we don't do that and hence we need to do that here. + if (RRLocalOnly(rr)) CompleteDeregistration(m, rr); + } + } + if (m->CurrentRecord == rr) // If m->CurrentRecord was not auto-advanced, do it ourselves now + m->CurrentRecord = rr->next; + } +} mDNSlocal void TimeoutQuestions(mDNS *const m) - { - m->NextScheduledStopTime = m->timenow + 0x3FFFFFFF; - if (m->CurrentQuestion) - LogMsg("TimeoutQuestions ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, - DNSTypeName(m->CurrentQuestion->qtype)); - m->CurrentQuestion = m->Questions; - while (m->CurrentQuestion) - { - DNSQuestion *const q = m->CurrentQuestion; - if (q->StopTime) - { - if (m->timenow - q->StopTime >= 0) - { - LogInfo("TimeoutQuestions: question %##s timed out, time %d", q->qname.c, m->timenow - q->StopTime); - GenerateNegativeResponse(m); - if (m->CurrentQuestion == q) q->StopTime = 0; - } - else - { - if (m->NextScheduledStopTime - q->StopTime > 0) - m->NextScheduledStopTime = q->StopTime; - } - } - // If m->CurrentQuestion wasn't modified out from under us, advance it now - // We can't do this at the start of the loop because GenerateNegativeResponse - // depends on having m->CurrentQuestion point to the right question - if (m->CurrentQuestion == q) - m->CurrentQuestion = q->next; - } - m->CurrentQuestion = mDNSNULL; - } +{ + m->NextScheduledStopTime = m->timenow + 0x3FFFFFFF; + if (m->CurrentQuestion) + LogMsg("TimeoutQuestions ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, + DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion) + { + DNSQuestion *const q = m->CurrentQuestion; + if (q->StopTime) + { + if (m->timenow - q->StopTime >= 0) + { + LogInfo("TimeoutQuestions: question %##s timed out, time %d", q->qname.c, m->timenow - q->StopTime); + GenerateNegativeResponse(m); + if (m->CurrentQuestion == q) q->StopTime = 0; + } + else + { + if (m->NextScheduledStopTime - q->StopTime > 0) + m->NextScheduledStopTime = q->StopTime; + } + } + // If m->CurrentQuestion wasn't modified out from under us, advance it now + // We can't do this at the start of the loop because GenerateNegativeResponse + // depends on having m->CurrentQuestion point to the right question + if (m->CurrentQuestion == q) + m->CurrentQuestion = q->next; + } + m->CurrentQuestion = mDNSNULL; +} + +mDNSlocal void mDNSCoreFreeProxyRR(mDNS *const m) +{ + NetworkInterfaceInfo *intf = m->HostInterfaces; + AuthRecord *rrPtr = mDNSNULL, *rrNext = mDNSNULL; + + while (intf) + { + rrPtr = intf->SPSRRSet; + while (rrPtr) + { + rrNext = rrPtr->next; + mDNSPlatformMemFree(rrPtr); + rrPtr = rrNext; + } + intf->SPSRRSet = mDNSNULL; + intf = intf->next; + } +} 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; - AuthRecord *head, *tail; - mDNSu32 slot; - AuthGroup *ag; - - verbosedebugf("mDNS_Execute"); - - if (m->CurrentQuestion) - LogMsg("mDNS_Execute: ERROR m->CurrentQuestion already set: %##s (%s)", - m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); - - if (m->CurrentRecord) - LogMsg("mDNS_Execute: ERROR m->CurrentRecord already set: %s", ARDisplayString(m, m->CurrentRecord)); - - // 1. If we're past the probe suppression time, we can clear it - if (m->SuppressProbes && m->timenow - m->SuppressProbes >= 0) m->SuppressProbes = 0; - - // 2. If it's been more than ten seconds since the last probe failure, we can clear the counter - if (m->NumFailedProbes && m->timenow - m->ProbeFailTime >= mDNSPlatformOneSecond * 10) m->NumFailedProbes = 0; - - // 3. Purge our cache of stale old records - if (m->rrcache_size && m->timenow - m->NextCacheCheck >= 0) - { - mDNSu32 numchecked = 0; - m->NextCacheCheck = m->timenow + 0x3FFFFFFF; - for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) - { - if (m->timenow - m->rrcache_nextcheck[slot] >= 0) - { - CacheGroup **cp = &m->rrcache_hash[slot]; - m->rrcache_nextcheck[slot] = m->timenow + 0x3FFFFFFF; - while (*cp) - { - debugf("m->NextCacheCheck %4d Slot %3d %##s", numchecked, slot, *cp ? (*cp)->name : (domainname*)"\x04NULL"); - numchecked++; - CheckCacheExpiration(m, slot, *cp); - if ((*cp)->members) cp=&(*cp)->next; - else ReleaseCacheGroup(m, cp); - } - } - // Even if we didn't need to actually check this slot yet, still need to - // factor its nextcheck time into our overall NextCacheCheck value - if (m->NextCacheCheck - m->rrcache_nextcheck[slot] > 0) - m->NextCacheCheck = m->rrcache_nextcheck[slot]; - } - debugf("m->NextCacheCheck %4d checked, next in %d", numchecked, m->NextCacheCheck - m->timenow); - } - - if (m->timenow - m->NextScheduledSPS >= 0) - { - m->NextScheduledSPS = m->timenow + 0x3FFFFFFF; - CheckProxyRecords(m, m->DuplicateRecords); // Clear m->DuplicateRecords first, then m->ResourceRecords - CheckProxyRecords(m, m->ResourceRecords); - } - - SetSPSProxyListChanged(mDNSNULL); // Perform any deferred BPF reconfiguration now - - // Clear AnnounceOwner if necessary. (Do this *before* SendQueries() and SendResponses().) - if (m->AnnounceOwner && m->timenow - m->AnnounceOwner >= 0) m->AnnounceOwner = 0; - - if (m->DelaySleep && m->timenow - m->DelaySleep >= 0) - { - m->DelaySleep = 0; - if (m->SleepState == SleepState_Transferring) - { - LogSPS("Re-sleep delay passed; now checking for Sleep Proxy Servers"); - BeginSleepProcessing(m); - } - } - - // 4. See if we can answer any of our new local questions from the cache - for (i=0; m->NewQuestions && i<1000; i++) - { - if (m->NewQuestions->DelayAnswering && m->timenow - m->NewQuestions->DelayAnswering < 0) break; - AnswerNewQuestion(m); - } - if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewQuestion exceeded loop limit"); - - // Make sure we deliver *all* local RMV events, and clear the corresponding rr->AnsweredLocalQ flags, *before* - // we begin generating *any* new ADD events in the m->NewLocalOnlyQuestions and m->NewLocalRecords loops below. - for (i=0; i<1000 && m->LocalRemoveEvents; i++) - { - m->LocalRemoveEvents = mDNSfalse; - m->CurrentRecord = m->ResourceRecords; - CheckRmvEventsForLocalRecords(m); - // Walk the LocalOnly records and deliver the RMV events - for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) - for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next) - { - m->CurrentRecord = ag->members; - if (m->CurrentRecord) CheckRmvEventsForLocalRecords(m); - } - } - - if (i >= 1000) LogMsg("mDNS_Execute: m->LocalRemoveEvents exceeded loop limit"); - - for (i=0; m->NewLocalOnlyQuestions && i<1000; i++) AnswerNewLocalOnlyQuestion(m); - if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit"); - - head = tail = mDNSNULL; - for (i=0; i<1000 && m->NewLocalRecords && m->NewLocalRecords != head; i++) - { - AuthRecord *rr = m->NewLocalRecords; - m->NewLocalRecords = m->NewLocalRecords->next; - if (LocalRecordReady(rr)) - { - debugf("mDNS_Execute: Delivering Add event with LocalAuthRecord %s", ARDisplayString(m, rr)); - AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNStrue); - } - else if (!rr->next) - { - // If we have just one record that is not ready, we don't have to unlink and - // reinsert. As the NewLocalRecords will be NULL for this case, the loop will - // terminate and set the NewLocalRecords to rr. - debugf("mDNS_Execute: Just one LocalAuthRecord %s, breaking out of the loop early", ARDisplayString(m, rr)); - if (head != mDNSNULL || m->NewLocalRecords != mDNSNULL) - LogMsg("mDNS_Execute: ERROR!!: head %p, NewLocalRecords %p", head, m->NewLocalRecords); - - head = rr; - } - else - { - AuthRecord **p = &m->ResourceRecords; // Find this record in our list of active records - debugf("mDNS_Execute: Skipping LocalAuthRecord %s", ARDisplayString(m, rr)); - // if this is the first record we are skipping, move to the end of the list. - // if we have already skipped records before, append it at the end. - while (*p && *p != rr) p=&(*p)->next; - if (*p) *p = rr->next; // Cut this record from the list - else { LogMsg("mDNS_Execute: ERROR!! Cannot find record %s in ResourceRecords list", ARDisplayString(m, rr)); break; } - if (!head) - { - while (*p) p=&(*p)->next; - *p = rr; - head = tail = rr; - } - else - { - tail->next = rr; - tail = rr; - } - rr->next = mDNSNULL; - } - } - m->NewLocalRecords = head; - debugf("mDNS_Execute: Setting NewLocalRecords to %s", (head ? ARDisplayString(m, head) : "NULL")); - - if (i >= 1000) LogMsg("mDNS_Execute: m->NewLocalRecords exceeded loop limit"); - - // Check to see if we have any new LocalOnly/P2P records to examine for delivering - // to our local questions - if (m->NewLocalOnlyRecords) - { - m->NewLocalOnlyRecords = mDNSfalse; - for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) - for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next) - { - for (i=0; i<100 && ag->NewLocalOnlyRecords; i++) - { - AuthRecord *rr = ag->NewLocalOnlyRecords; - ag->NewLocalOnlyRecords = ag->NewLocalOnlyRecords->next; - // LocalOnly records should always be ready as they never probe - if (LocalRecordReady(rr)) - { - debugf("mDNS_Execute: Delivering Add event with LocalAuthRecord %s", ARDisplayString(m, rr)); - AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNStrue); - } - else LogMsg("mDNS_Execute: LocalOnlyRecord %s not ready", ARDisplayString(m, rr)); - } - // We limit about 100 per AuthGroup that can be serviced at a time - if (i >= 100) LogMsg("mDNS_Execute: ag->NewLocalOnlyRecords exceeded loop limit"); - } - } - - // 5. Some questions may have picked a new DNS server and the cache may answer these questions now. - AnswerQuestionsForDNSServerChanges(m); - - // 6. See what packets we need to send - if (m->mDNSPlatformStatus != mStatus_NoError || (m->SleepState == SleepState_Sleeping)) - DiscardDeregistrations(m); - if (m->mDNSPlatformStatus == mStatus_NoError && (m->SuppressSending == 0 || m->timenow - m->SuppressSending >= 0)) - { - // If the platform code is ready, and we're not suppressing packet generation right now - // then send our responses, probes, and questions. - // We check the cache first, because there might be records close to expiring that trigger questions to refresh them. - // We send queries next, because there might be final-stage probes that complete their probing here, causing - // them to advance to announcing state, and we want those to be included in any announcements we send out. - // Finally, we send responses, including the previously mentioned records that just completed probing. - m->SuppressSending = 0; - - // 7. Send Query packets. This may cause some probing records to advance to announcing state - if (m->timenow - m->NextScheduledQuery >= 0 || m->timenow - m->NextScheduledProbe >= 0) SendQueries(m); - if (m->timenow - m->NextScheduledQuery >= 0) - { - DNSQuestion *q; - LogMsg("mDNS_Execute: SendQueries didn't send all its queries (%d - %d = %d) will try again in one second", - m->timenow, m->NextScheduledQuery, m->timenow - m->NextScheduledQuery); - m->NextScheduledQuery = m->timenow + mDNSPlatformOneSecond; - for (q = m->Questions; q && q != m->NewQuestions; q=q->next) - if (ActiveQuestion(q) && m->timenow - NextQSendTime(q) >= 0) - LogMsg("mDNS_Execute: SendQueries didn't send %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - } - if (m->timenow - m->NextScheduledProbe >= 0) - { - LogMsg("mDNS_Execute: SendQueries didn't send all its probes (%d - %d = %d) will try again in one second", - m->timenow, m->NextScheduledProbe, m->timenow - m->NextScheduledProbe); - m->NextScheduledProbe = m->timenow + mDNSPlatformOneSecond; - } - - // 8. Send Response packets, including probing records just advanced to announcing state - if (m->timenow - m->NextScheduledResponse >= 0) SendResponses(m); - if (m->timenow - m->NextScheduledResponse >= 0) - { - LogMsg("mDNS_Execute: SendResponses didn't send all its responses; will try again in one second"); - m->NextScheduledResponse = m->timenow + mDNSPlatformOneSecond; - } - } - - // Clear RandomDelay values, ready to pick a new different value next time - m->RandomQueryDelay = 0; - m->RandomReconfirmDelay = 0; - - if (m->NextScheduledStopTime && m->timenow - m->NextScheduledStopTime >= 0) TimeoutQuestions(m); +{ + mDNS_Lock(m); // Must grab lock before trying to read m->timenow + + if (m->timenow - m->NextScheduledEvent >= 0) + { + int i; + AuthRecord *head, *tail; + mDNSu32 slot; + AuthGroup *ag; + + verbosedebugf("mDNS_Execute"); + + if (m->CurrentQuestion) + LogMsg("mDNS_Execute: ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + + if (m->CurrentRecord) + LogMsg("mDNS_Execute: ERROR m->CurrentRecord already set: %s", ARDisplayString(m, m->CurrentRecord)); + + // 1. If we're past the probe suppression time, we can clear it + if (m->SuppressProbes && m->timenow - m->SuppressProbes >= 0) m->SuppressProbes = 0; + + // 2. If it's been more than ten seconds since the last probe failure, we can clear the counter + if (m->NumFailedProbes && m->timenow - m->ProbeFailTime >= mDNSPlatformOneSecond * 10) m->NumFailedProbes = 0; + + // 3. Purge our cache of stale old records + if (m->rrcache_size && m->timenow - m->NextCacheCheck >= 0) + { + mDNSu32 numchecked = 0; + m->NextCacheCheck = m->timenow + 0x3FFFFFFF; + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + { + if (m->timenow - m->rrcache_nextcheck[slot] >= 0) + { + CacheGroup **cp = &m->rrcache_hash[slot]; + m->rrcache_nextcheck[slot] = m->timenow + 0x3FFFFFFF; + while (*cp) + { + debugf("m->NextCacheCheck %4d Slot %3d %##s", numchecked, slot, *cp ? (*cp)->name : (domainname*)"\x04NULL"); + numchecked++; + CheckCacheExpiration(m, slot, *cp); + if ((*cp)->members) cp=&(*cp)->next; + else ReleaseCacheGroup(m, cp); + } + } + // Even if we didn't need to actually check this slot yet, still need to + // factor its nextcheck time into our overall NextCacheCheck value + if (m->NextCacheCheck - m->rrcache_nextcheck[slot] > 0) + m->NextCacheCheck = m->rrcache_nextcheck[slot]; + } + debugf("m->NextCacheCheck %4d checked, next in %d", numchecked, m->NextCacheCheck - m->timenow); + } + + if (m->timenow - m->NextScheduledSPS >= 0) + { + m->NextScheduledSPS = m->timenow + 0x3FFFFFFF; + CheckProxyRecords(m, m->DuplicateRecords); // Clear m->DuplicateRecords first, then m->ResourceRecords + CheckProxyRecords(m, m->ResourceRecords); + } + + SetSPSProxyListChanged(mDNSNULL); // Perform any deferred BPF reconfiguration now + + // Check to see if we need to send any keepalives. Do this after we called CheckProxyRecords above + // as records could have expired during that check + if (m->timenow - m->NextScheduledKA >= 0) + { + m->NextScheduledKA = m->timenow + 0x3FFFFFFF; + mDNS_SendKeepalives(m); + } + + // After two seconds after the owner option is set, call the ioctl to clear the + // ignore neighbor advertisement flag. +#if APPLE_OSX_mDNSResponder + if (m->clearIgnoreNA && m->timenow - m->clearIgnoreNA >= 0) + { + mDNSPlatformToggleInterfaceAdvt(m, mDNSfalse); + m->clearIgnoreNA = 0; + } +#endif + // Clear AnnounceOwner if necessary. (Do this *before* SendQueries() and SendResponses().) + if (m->AnnounceOwner && m->timenow - m->AnnounceOwner >= 0) + { + m->AnnounceOwner = 0; + // Also free the stored records that we had registered with the sleep proxy + mDNSCoreFreeProxyRR(m); + } + + if (m->DelaySleep && m->timenow - m->DelaySleep >= 0) + { + m->DelaySleep = 0; + if (m->SleepState == SleepState_Transferring) + { + LogSPS("Re-sleep delay passed; now checking for Sleep Proxy Servers"); + BeginSleepProcessing(m); + } + } + + // 4. See if we can answer any of our new local questions from the cache + for (i=0; m->NewQuestions && i<1000; i++) + { + if (m->NewQuestions->DelayAnswering && m->timenow - m->NewQuestions->DelayAnswering < 0) break; + AnswerNewQuestion(m); + } + if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewQuestion exceeded loop limit"); + + // Make sure we deliver *all* local RMV events, and clear the corresponding rr->AnsweredLocalQ flags, *before* + // we begin generating *any* new ADD events in the m->NewLocalOnlyQuestions and m->NewLocalRecords loops below. + for (i=0; i<1000 && m->LocalRemoveEvents; i++) + { + m->LocalRemoveEvents = mDNSfalse; + m->CurrentRecord = m->ResourceRecords; + CheckRmvEventsForLocalRecords(m); + // Walk the LocalOnly records and deliver the RMV events + for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) + for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next) + { + m->CurrentRecord = ag->members; + if (m->CurrentRecord) CheckRmvEventsForLocalRecords(m); + } + } + + if (i >= 1000) LogMsg("mDNS_Execute: m->LocalRemoveEvents exceeded loop limit"); + + for (i=0; m->NewLocalOnlyQuestions && i<1000; i++) AnswerNewLocalOnlyQuestion(m); + if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit"); + + head = tail = mDNSNULL; + for (i=0; i<1000 && m->NewLocalRecords && m->NewLocalRecords != head; i++) + { + AuthRecord *rr = m->NewLocalRecords; + m->NewLocalRecords = m->NewLocalRecords->next; + if (LocalRecordReady(rr)) + { + debugf("mDNS_Execute: Delivering Add event with LocalAuthRecord %s", ARDisplayString(m, rr)); + AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNStrue); + } + else if (!rr->next) + { + // If we have just one record that is not ready, we don't have to unlink and + // reinsert. As the NewLocalRecords will be NULL for this case, the loop will + // terminate and set the NewLocalRecords to rr. + debugf("mDNS_Execute: Just one LocalAuthRecord %s, breaking out of the loop early", ARDisplayString(m, rr)); + if (head != mDNSNULL || m->NewLocalRecords != mDNSNULL) + LogMsg("mDNS_Execute: ERROR!!: head %p, NewLocalRecords %p", head, m->NewLocalRecords); + + head = rr; + } + else + { + AuthRecord **p = &m->ResourceRecords; // Find this record in our list of active records + debugf("mDNS_Execute: Skipping LocalAuthRecord %s", ARDisplayString(m, rr)); + // if this is the first record we are skipping, move to the end of the list. + // if we have already skipped records before, append it at the end. + while (*p && *p != rr) p=&(*p)->next; + if (*p) *p = rr->next; // Cut this record from the list + else { LogMsg("mDNS_Execute: ERROR!! Cannot find record %s in ResourceRecords list", ARDisplayString(m, rr)); break; } + if (!head) + { + while (*p) p=&(*p)->next; + *p = rr; + head = tail = rr; + } + else + { + tail->next = rr; + tail = rr; + } + rr->next = mDNSNULL; + } + } + m->NewLocalRecords = head; + debugf("mDNS_Execute: Setting NewLocalRecords to %s", (head ? ARDisplayString(m, head) : "NULL")); + + if (i >= 1000) LogMsg("mDNS_Execute: m->NewLocalRecords exceeded loop limit"); + + // Check to see if we have any new LocalOnly/P2P records to examine for delivering + // to our local questions + if (m->NewLocalOnlyRecords) + { + m->NewLocalOnlyRecords = mDNSfalse; + for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) + for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next) + { + for (i=0; i<100 && ag->NewLocalOnlyRecords; i++) + { + AuthRecord *rr = ag->NewLocalOnlyRecords; + ag->NewLocalOnlyRecords = ag->NewLocalOnlyRecords->next; + // LocalOnly records should always be ready as they never probe + if (LocalRecordReady(rr)) + { + debugf("mDNS_Execute: Delivering Add event with LocalAuthRecord %s", ARDisplayString(m, rr)); + AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNStrue); + } + else LogMsg("mDNS_Execute: LocalOnlyRecord %s not ready", ARDisplayString(m, rr)); + } + // We limit about 100 per AuthGroup that can be serviced at a time + if (i >= 100) LogMsg("mDNS_Execute: ag->NewLocalOnlyRecords exceeded loop limit"); + } + } + + // 5. See what packets we need to send + if (m->mDNSPlatformStatus != mStatus_NoError || (m->SleepState == SleepState_Sleeping)) + DiscardDeregistrations(m); + if (m->mDNSPlatformStatus == mStatus_NoError && (m->SuppressSending == 0 || m->timenow - m->SuppressSending >= 0)) + { + // If the platform code is ready, and we're not suppressing packet generation right now + // then send our responses, probes, and questions. + // We check the cache first, because there might be records close to expiring that trigger questions to refresh them. + // We send queries next, because there might be final-stage probes that complete their probing here, causing + // them to advance to announcing state, and we want those to be included in any announcements we send out. + // Finally, we send responses, including the previously mentioned records that just completed probing. + m->SuppressSending = 0; + + // 6. Send Query packets. This may cause some probing records to advance to announcing state + if (m->timenow - m->NextScheduledQuery >= 0 || m->timenow - m->NextScheduledProbe >= 0) SendQueries(m); + if (m->timenow - m->NextScheduledQuery >= 0) + { + DNSQuestion *q; + LogMsg("mDNS_Execute: SendQueries didn't send all its queries (%d - %d = %d) will try again in one second", + m->timenow, m->NextScheduledQuery, m->timenow - m->NextScheduledQuery); + m->NextScheduledQuery = m->timenow + mDNSPlatformOneSecond; + for (q = m->Questions; q && q != m->NewQuestions; q=q->next) + if (ActiveQuestion(q) && m->timenow - NextQSendTime(q) >= 0) + LogMsg("mDNS_Execute: SendQueries didn't send %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + } + if (m->timenow - m->NextScheduledProbe >= 0) + { + LogMsg("mDNS_Execute: SendQueries didn't send all its probes (%d - %d = %d) will try again in one second", + m->timenow, m->NextScheduledProbe, m->timenow - m->NextScheduledProbe); + m->NextScheduledProbe = m->timenow + mDNSPlatformOneSecond; + } + + // 7. Send Response packets, including probing records just advanced to announcing state + if (m->timenow - m->NextScheduledResponse >= 0) SendResponses(m); + if (m->timenow - m->NextScheduledResponse >= 0) + { + LogMsg("mDNS_Execute: SendResponses didn't send all its responses; will try again in one second"); + m->NextScheduledResponse = m->timenow + mDNSPlatformOneSecond; + } + } + + // Clear RandomDelay values, ready to pick a new different value next time + m->RandomQueryDelay = 0; + m->RandomReconfirmDelay = 0; + + if (m->NextScheduledStopTime && m->timenow - m->NextScheduledStopTime >= 0) TimeoutQuestions(m); #ifndef UNICAST_DISABLED - if (m->NextSRVUpdate && m->timenow - m->NextSRVUpdate >= 0) UpdateAllSRVRecords(m); - if (m->timenow - m->NextScheduledNATOp >= 0) CheckNATMappings(m); - if (m->timenow - m->NextuDNSEvent >= 0) uDNS_Tasks(m); + if (m->NextSRVUpdate && m->timenow - m->NextSRVUpdate >= 0) UpdateAllSRVRecords(m); + if (m->timenow - m->NextScheduledNATOp >= 0) CheckNATMappings(m); + if (m->timenow - m->NextuDNSEvent >= 0) uDNS_Tasks(m); #endif - } - - // Note about multi-threaded systems: - // On a multi-threaded system, some other thread could run right after the mDNS_Unlock(), - // performing mDNS API operations that change our next scheduled event time. - // - // On multi-threaded systems (like the current Windows implementation) that have a single main thread - // calling mDNS_Execute() (and other threads allowed to call mDNS API routines) it is the responsibility - // of the mDNSPlatformUnlock() routine to signal some kind of stateful condition variable that will - // signal whatever blocking primitive the main thread is using, so that it will wake up and execute one - // more iteration of its loop, and immediately call mDNS_Execute() again. The signal has to be stateful - // in the sense that if the main thread has not yet entered its blocking primitive, then as soon as it - // does, the state of the signal will be noticed, causing the blocking primitive to return immediately - // without blocking. This avoids the race condition between the signal from the other thread arriving - // just *before* or just *after* the main thread enters the blocking primitive. - // - // On multi-threaded systems (like the current Mac OS 9 implementation) that are entirely timer-driven, - // with no main mDNS_Execute() thread, it is the responsibility of the mDNSPlatformUnlock() routine to - // set the timer according to the m->NextScheduledEvent value, and then when the timer fires, the timer - // callback function should call mDNS_Execute() (and ignore the return value, which may already be stale - // by the time it gets to the timer callback function). - - mDNS_Unlock(m); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value - return(m->NextScheduledEvent); - } + } + + // Note about multi-threaded systems: + // On a multi-threaded system, some other thread could run right after the mDNS_Unlock(), + // performing mDNS API operations that change our next scheduled event time. + // + // On multi-threaded systems (like the current Windows implementation) that have a single main thread + // calling mDNS_Execute() (and other threads allowed to call mDNS API routines) it is the responsibility + // of the mDNSPlatformUnlock() routine to signal some kind of stateful condition variable that will + // signal whatever blocking primitive the main thread is using, so that it will wake up and execute one + // more iteration of its loop, and immediately call mDNS_Execute() again. The signal has to be stateful + // in the sense that if the main thread has not yet entered its blocking primitive, then as soon as it + // does, the state of the signal will be noticed, causing the blocking primitive to return immediately + // without blocking. This avoids the race condition between the signal from the other thread arriving + // just *before* or just *after* the main thread enters the blocking primitive. + // + // On multi-threaded systems (like the current Mac OS 9 implementation) that are entirely timer-driven, + // with no main mDNS_Execute() thread, it is the responsibility of the mDNSPlatformUnlock() routine to + // set the timer according to the m->NextScheduledEvent value, and then when the timer fires, the timer + // callback function should call mDNS_Execute() (and ignore the return value, which may already be stale + // by the time it gets to the timer callback function). + + mDNS_Unlock(m); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value + return(m->NextScheduledEvent); +} mDNSlocal void SuspendLLQs(mDNS *m) - { - DNSQuestion *q; - for (q = m->Questions; q; q = q->next) - if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID) && q->LongLived && q->state == LLQ_Established) - { q->ReqLease = 0; sendLLQRefresh(m, q); } - } +{ + DNSQuestion *q; + for (q = m->Questions; q; q = q->next) + if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID) && q->LongLived && q->state == LLQ_Established) + { q->ReqLease = 0; sendLLQRefresh(m, q); } +} mDNSlocal mDNSBool QuestionHasLocalAnswers(mDNS *const m, DNSQuestion *q) - { - AuthRecord *rr; - mDNSu32 slot; - AuthGroup *ag; - - slot = AuthHashSlot(&q->qname); - ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname); - if (ag) - { - for (rr = ag->members; rr; rr=rr->next) - // Filter the /etc/hosts records - LocalOnly, Unique, A/AAAA/CNAME - if (LORecordAnswersAddressType(rr) && LocalOnlyRecordAnswersQuestion(rr, q)) - { - LogInfo("QuestionHasLocalAnswers: Question %p %##s (%s) has local answer %s", q, q->qname.c, DNSTypeName(q->qtype), ARDisplayString(m, rr)); - return mDNStrue; - } - } - return mDNSfalse; - } +{ + AuthRecord *rr; + mDNSu32 slot; + AuthGroup *ag; + + slot = AuthHashSlot(&q->qname); + ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname); + if (ag) + { + for (rr = ag->members; rr; rr=rr->next) + // Filter the /etc/hosts records - LocalOnly, Unique, A/AAAA/CNAME + if (LORecordAnswersAddressType(rr) && LocalOnlyRecordAnswersQuestion(rr, q)) + { + LogInfo("QuestionHasLocalAnswers: Question %p %##s (%s) has local answer %s", q, q->qname.c, DNSTypeName(q->qtype), ARDisplayString(m, rr)); + return mDNStrue; + } + } + return mDNSfalse; +} // ActivateUnicastQuery() is called from three places: // 1. When a new question is created @@ -4581,188 +4754,213 @@ mDNSlocal mDNSBool QuestionHasLocalAnswers(mDNS *const m, DNSQuestion *q) // In case 1 we don't want to mess with our established ThisQInterval and LastQTime (ScheduleImmediately is false) // In cases 2 and 3 we do want to cause the question to be resent immediately (ScheduleImmediately is true) mDNSlocal void ActivateUnicastQuery(mDNS *const m, DNSQuestion *const question, mDNSBool ScheduleImmediately) - { - // For now this AutoTunnel stuff is specific to Mac OS X. - // In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer +{ + // For now this AutoTunnel stuff is specific to Mac OS X. + // In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer #if APPLE_OSX_mDNSResponder - // Even though BTMM client tunnels are only useful for AAAA queries, we need to treat v4 and v6 queries equally. - // Otherwise we can get the situation where the A query completes really fast (with an NXDOMAIN result) and the - // caller then gives up waiting for the AAAA result while we're still in the process of setting up the tunnel. - // To level the playing field, we block both A and AAAA queries while tunnel setup is in progress, and then - // returns results for both at the same time. If we are looking for the _autotunnel6 record, then skip this logic - // as this would trigger looking up _autotunnel6._autotunnel6 and end up failing the original query. - - if (RRTypeIsAddressType(question->qtype) && PrivateQuery(question) && - !SameDomainLabel(question->qname.c, (const mDNSu8 *)"\x0c_autotunnel6")&& question->QuestionCallback != AutoTunnelCallback) - { - question->NoAnswer = NoAnswer_Suspended; - AddNewClientTunnel(m, question); - return; - } + // Even though BTMM client tunnels are only useful for AAAA queries, we need to treat v4 and v6 queries equally. + // Otherwise we can get the situation where the A query completes really fast (with an NXDOMAIN result) and the + // caller then gives up waiting for the AAAA result while we're still in the process of setting up the tunnel. + // To level the playing field, we block both A and AAAA queries while tunnel setup is in progress, and then + // returns results for both at the same time. If we are looking for the _autotunnel6 record, then skip this logic + // as this would trigger looking up _autotunnel6._autotunnel6 and end up failing the original query. + + if (RRTypeIsAddressType(question->qtype) && PrivateQuery(question) && + !SameDomainLabel(question->qname.c, (const mDNSu8 *)"\x0c_autotunnel6")&& question->QuestionCallback != AutoTunnelCallback) + { + question->NoAnswer = NoAnswer_Suspended; + AddNewClientTunnel(m, question); + return; + } #endif // APPLE_OSX_mDNSResponder - if (!question->DuplicateOf) - { - debugf("ActivateUnicastQuery: %##s %s%s%s", - question->qname.c, DNSTypeName(question->qtype), PrivateQuery(question) ? " (Private)" : "", ScheduleImmediately ? " ScheduleImmediately" : ""); - question->CNAMEReferrals = 0; - if (question->nta) { CancelGetZoneData(m, question->nta); question->nta = mDNSNULL; } - if (question->LongLived) - { - question->state = LLQ_InitialRequest; - question->id = zeroOpaque64; - question->servPort = zeroIPPort; - if (question->tcp) { DisposeTCPConn(question->tcp); question->tcp = mDNSNULL; } - } - // If the question has local answers, then we don't want answers from outside - if (ScheduleImmediately && !QuestionHasLocalAnswers(m, question)) - { - question->ThisQInterval = InitialQuestionInterval; - question->LastQTime = m->timenow - question->ThisQInterval; - SetNextQueryTime(m, question); - } - } - } + if (!question->DuplicateOf) + { + debugf("ActivateUnicastQuery: %##s %s%s%s", + question->qname.c, DNSTypeName(question->qtype), PrivateQuery(question) ? " (Private)" : "", ScheduleImmediately ? " ScheduleImmediately" : ""); + question->CNAMEReferrals = 0; + if (question->nta) { CancelGetZoneData(m, question->nta); question->nta = mDNSNULL; } + if (question->LongLived) + { + question->state = LLQ_InitialRequest; + question->id = zeroOpaque64; + question->servPort = zeroIPPort; + if (question->tcp) { DisposeTCPConn(question->tcp); question->tcp = mDNSNULL; } + } + // If the question has local answers, then we don't want answers from outside + if (ScheduleImmediately && !QuestionHasLocalAnswers(m, question)) + { + question->ThisQInterval = InitialQuestionInterval; + question->LastQTime = m->timenow - question->ThisQInterval; + SetNextQueryTime(m, question); + } + } +} // Caller should hold the lock mDNSexport void mDNSCoreRestartAddressQueries(mDNS *const m, mDNSBool SearchDomainsChanged, FlushCache flushCacheRecords, - CallbackBeforeStartQuery BeforeStartCallback, void *context) - { - DNSQuestion *q; - DNSQuestion *restart = mDNSNULL; - - if (!m->mDNS_busy) LogMsg("mDNSCoreRestartAddressQueries: ERROR!! Lock not held"); - - // 1. Flush the cache records - if (flushCacheRecords) flushCacheRecords(m); - - // 2. Even though we may have purged the cache records above, before it can generate RMV event - // we are going to stop the question. Hence we need to deliver the RMV event before we - // stop the question. - // - // CurrentQuestion is used by RmvEventsForQuestion below. While delivering RMV events, the - // application callback can potentially stop the current question (detected by CurrentQuestion) or - // *any* other question which could be the next one that we may process here. RestartQuestion - // points to the "next" question which will be automatically advanced in mDNS_StopQuery_internal - // if the "next" question is stopped while the CurrentQuestion is stopped - - if (m->RestartQuestion) - LogMsg("mDNSCoreRestartAddressQueries: ERROR!! m->RestartQuestion already set: %##s (%s)", - m->RestartQuestion->qname.c, DNSTypeName(m->RestartQuestion->qtype)); - - m->RestartQuestion = m->Questions; - while (m->RestartQuestion) - { - q = m->RestartQuestion; - m->RestartQuestion = q->next; - // GetZoneData questions are referenced by other questions (original query that started the GetZoneData - // question) through their "nta" pointer. Normally when the original query stops, it stops the - // GetZoneData question and also frees the memory (See CancelGetZoneData). If we stop the GetZoneData - // question followed by the original query that refers to this GetZoneData question, we will end up - // freeing the GetZoneData question and then start the "freed" question at the end. - - if (IsGetZoneDataQuestion(q)) - { - DNSQuestion *refq = q->next; - LogInfo("mDNSCoreRestartAddressQueries: Skipping GetZoneDataQuestion %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); - // debug stuff, we just try to find the referencing question and don't do much with it - while (refq) - { - if (q == &refq->nta->question) - { - LogInfo("mDNSCoreRestartAddressQueries: Question %p %##s (%s) referring to GetZoneDataQuestion %p, not stopping", refq, refq->qname.c, DNSTypeName(refq->qtype), q); - } - refq = refq->next; - } - continue; - } - - // This function is called when /etc/hosts changes and that could affect A, AAAA and CNAME queries - if (q->qtype != kDNSType_A && q->qtype != kDNSType_AAAA && q->qtype != kDNSType_CNAME) continue; - - // If the search domains did not change, then we restart all the queries. Otherwise, only - // for queries for which we "might" have appended search domains ("might" because we may - // find results before we apply search domains even though AppendSearchDomains is set to 1) - if (!SearchDomainsChanged || q->AppendSearchDomains) - { - // NOTE: CacheRecordRmvEventsForQuestion will not generate RMV events for queries that have non-zero - // LOAddressAnswers. Hence it is important that we call CacheRecordRmvEventsForQuestion before - // LocalRecordRmvEventsForQuestion (which decrements LOAddressAnswers). Let us say that - // /etc/hosts has an A Record for web.apple.com. Any queries for web.apple.com will be answered locally. - // But this can't prevent a CNAME/AAAA query to not to be sent on the wire. When it is sent on the wire, - // it could create cache entries. When we are restarting queries, we can't deliver the cache RMV events - // for the original query using these cache entries as ADDs were never delivered using these cache - // entries and hence this order is needed. - - // If the query is suppressed, the RMV events won't be delivered - if (!CacheRecordRmvEventsForQuestion(m, q)) { LogInfo("mDNSCoreRestartAddressQueries: Question deleted while delivering Cache Record RMV events"); continue; } - - // SuppressQuery status does not affect questions that are answered using local records - if (!LocalRecordRmvEventsForQuestion(m, q)) { LogInfo("mDNSCoreRestartAddressQueries: Question deleted while delivering Local Record RMV events"); continue; } - - LogInfo("mDNSCoreRestartAddressQueries: Stop question %p %##s (%s), AppendSearchDomains %d, qnameOrig %p", q, - q->qname.c, DNSTypeName(q->qtype), q->AppendSearchDomains, q->qnameOrig); - mDNS_StopQuery_internal(m, q); - // Reset state so that it looks like it was in the beginning i.e it should look at /etc/hosts, cache - // and then search domains should be appended. At the beginning, qnameOrig was NULL. - if (q->qnameOrig) - { - LogInfo("mDNSCoreRestartAddressQueries: qnameOrig %##s", q->qnameOrig); - AssignDomainName(&q->qname, q->qnameOrig); - mDNSPlatformMemFree(q->qnameOrig); - q->qnameOrig = mDNSNULL; - q->RetryWithSearchDomains = ApplySearchDomainsFirst(q) ? 1 : 0; - } - q->SearchListIndex = 0; - q->next = restart; - restart = q; - } - } - - // 3. Callback before we start the query - if (BeforeStartCallback) BeforeStartCallback(m, context); - - // 4. Restart all the stopped queries - while (restart) - { - q = restart; - restart = restart->next; - q->next = mDNSNULL; - LogInfo("mDNSCoreRestartAddressQueries: Start question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); - mDNS_StartQuery_internal(m, q); - } - } + CallbackBeforeStartQuery BeforeStartCallback, void *context) +{ + DNSQuestion *q; + DNSQuestion *restart = mDNSNULL; + + if (!m->mDNS_busy) LogMsg("mDNSCoreRestartAddressQueries: ERROR!! Lock not held"); + + // 1. Flush the cache records + if (flushCacheRecords) flushCacheRecords(m); + + // 2. Even though we may have purged the cache records above, before it can generate RMV event + // we are going to stop the question. Hence we need to deliver the RMV event before we + // stop the question. + // + // CurrentQuestion is used by RmvEventsForQuestion below. While delivering RMV events, the + // application callback can potentially stop the current question (detected by CurrentQuestion) or + // *any* other question which could be the next one that we may process here. RestartQuestion + // points to the "next" question which will be automatically advanced in mDNS_StopQuery_internal + // if the "next" question is stopped while the CurrentQuestion is stopped + + if (m->RestartQuestion) + LogMsg("mDNSCoreRestartAddressQueries: ERROR!! m->RestartQuestion already set: %##s (%s)", + m->RestartQuestion->qname.c, DNSTypeName(m->RestartQuestion->qtype)); + + m->RestartQuestion = m->Questions; + while (m->RestartQuestion) + { + q = m->RestartQuestion; + m->RestartQuestion = q->next; + // GetZoneData questions are referenced by other questions (original query that started the GetZoneData + // question) through their "nta" pointer. Normally when the original query stops, it stops the + // GetZoneData question and also frees the memory (See CancelGetZoneData). If we stop the GetZoneData + // question followed by the original query that refers to this GetZoneData question, we will end up + // freeing the GetZoneData question and then start the "freed" question at the end. + + if (IsGetZoneDataQuestion(q)) + { + DNSQuestion *refq = q->next; + LogInfo("mDNSCoreRestartAddressQueries: Skipping GetZoneDataQuestion %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + // debug stuff, we just try to find the referencing question and don't do much with it + while (refq) + { + if (q == &refq->nta->question) + { + LogInfo("mDNSCoreRestartAddressQueries: Question %p %##s (%s) referring to GetZoneDataQuestion %p, not stopping", refq, refq->qname.c, DNSTypeName(refq->qtype), q); + } + refq = refq->next; + } + continue; + } + + // This function is called when /etc/hosts changes and that could affect A, AAAA and CNAME queries + if (q->qtype != kDNSType_A && q->qtype != kDNSType_AAAA && q->qtype != kDNSType_CNAME) continue; + + // If the search domains did not change, then we restart all the queries. Otherwise, only + // for queries for which we "might" have appended search domains ("might" because we may + // find results before we apply search domains even though AppendSearchDomains is set to 1) + if (!SearchDomainsChanged || q->AppendSearchDomains) + { + // NOTE: CacheRecordRmvEventsForQuestion will not generate RMV events for queries that have non-zero + // LOAddressAnswers. Hence it is important that we call CacheRecordRmvEventsForQuestion before + // LocalRecordRmvEventsForQuestion (which decrements LOAddressAnswers). Let us say that + // /etc/hosts has an A Record for web.apple.com. Any queries for web.apple.com will be answered locally. + // But this can't prevent a CNAME/AAAA query to not to be sent on the wire. When it is sent on the wire, + // it could create cache entries. When we are restarting queries, we can't deliver the cache RMV events + // for the original query using these cache entries as ADDs were never delivered using these cache + // entries and hence this order is needed. + + // If the query is suppressed, the RMV events won't be delivered + if (!CacheRecordRmvEventsForQuestion(m, q)) { LogInfo("mDNSCoreRestartAddressQueries: Question deleted while delivering Cache Record RMV events"); continue; } + + // SuppressQuery status does not affect questions that are answered using local records + if (!LocalRecordRmvEventsForQuestion(m, q)) { LogInfo("mDNSCoreRestartAddressQueries: Question deleted while delivering Local Record RMV events"); continue; } + + LogInfo("mDNSCoreRestartAddressQueries: Stop question %p %##s (%s), AppendSearchDomains %d, qnameOrig %p", q, + q->qname.c, DNSTypeName(q->qtype), q->AppendSearchDomains, q->qnameOrig); + mDNS_StopQuery_internal(m, q); + // Reset state so that it looks like it was in the beginning i.e it should look at /etc/hosts, cache + // and then search domains should be appended. At the beginning, qnameOrig was NULL. + if (q->qnameOrig) + { + LogInfo("mDNSCoreRestartAddressQueries: qnameOrig %##s", q->qnameOrig); + AssignDomainName(&q->qname, q->qnameOrig); + mDNSPlatformMemFree(q->qnameOrig); + q->qnameOrig = mDNSNULL; + q->RetryWithSearchDomains = ApplySearchDomainsFirst(q) ? 1 : 0; + } + q->SearchListIndex = 0; + q->next = restart; + restart = q; + } + } + + // 3. Callback before we start the query + if (BeforeStartCallback) BeforeStartCallback(m, context); + + // 4. Restart all the stopped queries + while (restart) + { + q = restart; + restart = restart->next; + q->next = mDNSNULL; + LogInfo("mDNSCoreRestartAddressQueries: Start question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + mDNS_StartQuery_internal(m, q); + } +} mDNSexport void mDNSCoreRestartQueries(mDNS *const m) - { - DNSQuestion *q; +{ + DNSQuestion *q; #ifndef UNICAST_DISABLED - // Retrigger all our uDNS questions - if (m->CurrentQuestion) - LogMsg("mDNSCoreRestartQueries: ERROR m->CurrentQuestion already set: %##s (%s)", - m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); - m->CurrentQuestion = m->Questions; - while (m->CurrentQuestion) - { - q = m->CurrentQuestion; - m->CurrentQuestion = m->CurrentQuestion->next; - if (!mDNSOpaque16IsZero(q->TargetQID) && ActiveQuestion(q)) ActivateUnicastQuery(m, q, mDNStrue); - } + // Retrigger all our uDNS questions + if (m->CurrentQuestion) + LogMsg("mDNSCoreRestartQueries: ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion) + { + q = m->CurrentQuestion; + m->CurrentQuestion = m->CurrentQuestion->next; + if (!mDNSOpaque16IsZero(q->TargetQID) && ActiveQuestion(q)) ActivateUnicastQuery(m, q, mDNStrue); + } #endif - // Retrigger all our mDNS questions - for (q = m->Questions; q; q=q->next) // Scan our list of questions - if (mDNSOpaque16IsZero(q->TargetQID) && ActiveQuestion(q)) - { - q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question - q->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it - q->LastQTime = m->timenow - q->ThisQInterval; - q->RecentAnswerPkts = 0; - ExpireDupSuppressInfo(q->DupSuppress, m->timenow); - m->NextScheduledQuery = m->timenow; - } - } + // Retrigger all our mDNS questions + for (q = m->Questions; q; q=q->next) // Scan our list of questions + mDNSCoreRestartQuestion(m, q); +} + +// restart question if it's multicast and currently active +mDNSexport void mDNSCoreRestartQuestion(mDNS *const m, DNSQuestion *q) +{ + if (mDNSOpaque16IsZero(q->TargetQID) && ActiveQuestion(q)) + { + q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question + q->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it + q->LastQTime = m->timenow - q->ThisQInterval; + q->RecentAnswerPkts = 0; + ExpireDupSuppressInfo(q->DupSuppress, m->timenow); + m->NextScheduledQuery = m->timenow; + } +} + +// restart the probe/announce cycle for multicast record +mDNSexport void mDNSCoreRestartRegistration(mDNS *const m, AuthRecord *rr, int announceCount) +{ + if (!AuthRecord_uDNS(rr)) + { + if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique; + rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); + + // announceCount < 0 indicates default announce count should be used + if (announceCount < 0) + announceCount = InitialAnnounceCount; + if (rr->AnnounceCount < announceCount) + rr->AnnounceCount = announceCount; + rr->AnnounceCount = InitialAnnounceCount; + rr->SendNSECNow = mDNSNULL; + InitializeLastAPTime(m, rr); + } +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -4771,640 +4969,899 @@ mDNSexport void mDNSCoreRestartQueries(mDNS *const m) #endif mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m) - { +{ #ifndef IDLESLEEPCONTROL_DISABLED - mDNSBool allowSleep = mDNStrue; - char reason[128]; - - reason[0] = 0; - - if (m->SystemSleepOnlyIfWakeOnLAN) - { - // Don't sleep if we are a proxy for any services - if (m->ProxyRecords) - { - allowSleep = mDNSfalse; - mDNS_snprintf(reason, sizeof(reason), "sleep proxy for %d records", m->ProxyRecords); - LogInfo("Sleep disabled because we are proxying %d records", m->ProxyRecords); - } - - if (allowSleep && mDNSCoreHaveAdvertisedMulticastServices(m)) - { - // Scan the list of active interfaces - NetworkInterfaceInfo *intf; - for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) - { - if (intf->McastTxRx && !intf->Loopback) - { - // Disallow sleep if this interface doesn't support NetWake - if (!intf->NetWake) - { - allowSleep = mDNSfalse; - mDNS_snprintf(reason, sizeof(reason), "%s does not support NetWake", intf->ifname); - LogInfo("Sleep disabled because %s does not support NetWake", intf->ifname); - break; - } - - // Disallow sleep if there is no sleep proxy server - if (FindSPSInCache1(m, &intf->NetWakeBrowse, mDNSNULL, mDNSNULL) == mDNSNULL) - { - allowSleep = mDNSfalse; - mDNS_snprintf(reason, sizeof(reason), "%s does not support NetWake", intf->ifname); - LogInfo("Sleep disabled because %s has no sleep proxy", intf->ifname); - break; - } - } - } - } - } - - // Call the platform code to enable/disable sleep - mDNSPlatformSetAllowSleep(m, allowSleep, reason); + mDNSBool allowSleep = mDNStrue; + char reason[128]; + + reason[0] = 0; + + if (m->SystemSleepOnlyIfWakeOnLAN) + { + // Don't sleep if we are a proxy for any services + if (m->ProxyRecords) + { + allowSleep = mDNSfalse; + mDNS_snprintf(reason, sizeof(reason), "sleep proxy for %d records", m->ProxyRecords); + LogInfo("Sleep disabled because we are proxying %d records", m->ProxyRecords); + } + + if (allowSleep && mDNSCoreHaveAdvertisedMulticastServices(m)) + { + // Scan the list of active interfaces + NetworkInterfaceInfo *intf; + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) + { + if (intf->McastTxRx && !intf->Loopback) + { + // Disallow sleep if this interface doesn't support NetWake + if (!intf->NetWake) + { + allowSleep = mDNSfalse; + mDNS_snprintf(reason, sizeof(reason), "%s does not support NetWake", intf->ifname); + LogInfo("Sleep disabled because %s does not support NetWake", intf->ifname); + break; + } + + // Disallow sleep if there is no sleep proxy server + if (FindSPSInCache1(m, &intf->NetWakeBrowse, mDNSNULL, mDNSNULL) == mDNSNULL) + { + allowSleep = mDNSfalse; + mDNS_snprintf(reason, sizeof(reason), "%s does not support NetWake", intf->ifname); + LogInfo("Sleep disabled because %s has no sleep proxy", intf->ifname); + break; + } + } + } + } + } + + // Call the platform code to enable/disable sleep + mDNSPlatformSetAllowSleep(m, allowSleep, reason); #endif /* !defined(IDLESLEEPCONTROL_DISABLED) */ - } +} + +mDNSlocal mDNSBool mDNSUpdateOkToSend(mDNS *const m, AuthRecord *rr, NetworkInterfaceInfo *const intf, mDNSu32 scopeid) +{ + // If it is not a uDNS record, check to see if the updateid is zero. "updateid" is cleared when we have + // sent the resource record on all the interfaces. If the update id is not zero, check to see if it is time + // to send. + if (AuthRecord_uDNS(rr) || mDNSOpaque16IsZero(rr->updateid) || m->timenow - (rr->LastAPTime + rr->ThisAPInterval) < 0) + return mDNSfalse; + + // If we have a pending registration for "scopeid", it is ok to send the update on that interface. + // If the scopeid is too big to check for validity, we don't check against updateIntID. When + // we successfully update on all the interfaces (with whatever set in "rr->updateIntID"), we clear + // updateid and we should have returned from above. + // + // Note: scopeid is the same as intf->InterfaceID. It is passed in so that we don't have to call the + // platform function to extract the value from "intf" everytime. + + if ((scopeid >= (sizeof(rr->updateIntID) * mDNSNBBY) || bit_get_opaque64(rr->updateIntID, scopeid)) && + (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == intf->InterfaceID)) + return mDNStrue; + + return mDNSfalse; +} + +mDNSlocal mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkInterfaceInfo *const intf) +{ + mDNSu16 newrdlength; + mDNSAddr laddr, raddr; + mDNSIPPort lport, rport; + mDNSu32 timeout, seq, ack; + mDNSu16 win; + UTF8str255 txt; + int rdsize; + RData *newrd; + mDNSTCPInfo mti; + mStatus ret; + + if (rr->NewRData) + { + RData *n = rr->NewRData; + + LogMsg("UpdateKeepaliveRData: Update was queued on %s", ARDisplayString(m, rr)); + + rr->NewRData = mDNSNULL; + if (rr->UpdateCallback) + rr->UpdateCallback(m, rr, n, rr->newrdlength); + } + + // Note: If we fail to update the DNS NULL record with additional information in this function, it will be registered + // with the SPS like any other record. SPS will not send keepalives if it does not have additional information. + + mDNS_ExtractKeepaliveInfo(rr, &timeout, &laddr, &raddr, &seq, &ack, &lport, &rport, &win); + if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || mDNSIPPortIsZero(lport) || + mDNSIPPortIsZero(rport)) + { + LogMsg("UpdateKeepaliveRData: not a valid record %s for keepalive %#a:%d %#a:%d", ARDisplayString(m, rr), &laddr, lport.NotAnInteger, &raddr, rport.NotAnInteger); + return mStatus_UnknownErr; + } + + // If this keepalive packet would be sent on a different interface than the current one that we are processing + // now, then we don't update the DNS NULL record. But we do not prevent it from registering with the SPS. When SPS sees + // this DNS NULL record, it does not send any keepalives as it does not have all the information + + ret = mDNSPlatformRetrieveTCPInfo(m, &laddr, &lport, &raddr, &rport, &mti); + if (ret != mStatus_NoError) + { + LogMsg("mDNSPlatformRetrieveTCPInfo: mDNSPlatformRetrieveTCPInfo failed %d", ret); + return ret; + } + + if (mti.IntfId != intf->InterfaceID) + { + LogInfo("mDNSPlatformRetrieveTCPInfo: InterfaceID mismatch mti %p, Interface %p", mti.IntfId, intf->InterfaceID); + return mStatus_BadParamErr; + } + + if (laddr.type == mDNSAddrType_IPv4) + newrdlength = mDNS_snprintf((char *)&txt.c[1], sizeof(txt.c) - 1, "t=%d h=%#a d=%#a l=%u r=%u s=%u a=%u w=%u", timeout, &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport), mti.seq, mti.ack, mti.window); + else + newrdlength = mDNS_snprintf((char *)&txt.c[1], sizeof(txt.c) - 1, "t=%d H=%#a D=%#a l=%u%u r=%u%u s=%u a=%u w=%u", timeout, &laddr, &raddr, lport.b[0], lport.b[1], rport.b[0], rport.b[1], rport.NotAnInteger, mti.seq, mti.ack, mti.window); + + // Did we insert a null byte at the end ? + if (newrdlength == (sizeof(txt.c) - 1)) + { + LogMsg("UpdateKeepaliveRData: could not allocate memory %s", ARDisplayString(m, rr)); + return mStatus_NoMemoryErr; + } + + // Include the length for the null byte at the end + txt.c[0] = newrdlength + 1; + // Account for the first length byte and the null byte at the end + newrdlength += 2; + + rdsize = newrdlength > sizeof(RDataBody) ? newrdlength : sizeof(RDataBody); + newrd = mDNSPlatformMemAllocate(sizeof(RData) - sizeof(RDataBody) + rdsize); + if (!newrd) { LogMsg("UpdateKeepaliveRData: ptr NULL"); return mStatus_NoMemoryErr; } + + newrd->MaxRDLength = (mDNSu16) rdsize; + mDNSPlatformMemCopy(&newrd->u, txt.c, newrdlength); + + rr->NewRData = newrd; + rr->newrdlength = newrdlength; + if (!ValidateRData(rr->resrec.rrtype, newrdlength, newrd)) + { + LogMsg("UpdateKeepaliveRData: ValidateRData failed %s", ARDisplayString(m, rr)); + return mStatus_BadParamErr; + } + + // We don't send goodbyes for non-shared records and hence updating here should be fine + CompleteRDataUpdate(m, rr); + + LogSPS("UpdateKeepaliveRData: successfully updated the record %s", ARDisplayString(m, rr)); + return mStatus_NoError; +} mDNSlocal void SendSPSRegistrationForOwner(mDNS *const m, NetworkInterfaceInfo *const intf, const mDNSOpaque16 id, const OwnerOptData *const owner) - { - const int optspace = DNSOpt_Header_Space + DNSOpt_LeaseData_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC); - const int sps = intf->NextSPSAttempt / 3; - AuthRecord *rr; - - if (!intf->SPSAddr[sps].type) - { - intf->NextSPSAttemptTime = m->timenow + mDNSPlatformOneSecond; - if (m->NextScheduledSPRetry - intf->NextSPSAttemptTime > 0) - m->NextScheduledSPRetry = intf->NextSPSAttemptTime; - LogSPS("SendSPSRegistration: %s SPS %d (%d) %##s not yet resolved", intf->ifname, intf->NextSPSAttempt, sps, intf->NetWakeResolve[sps].qname.c); - goto exit; - } - - // Mark our mDNS records (not unicast records) for transfer to SPS - if (mDNSOpaque16IsZero(id)) - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.RecordType > kDNSRecordTypeDeregistering) - if (rr->resrec.InterfaceID == intf->InterfaceID || (!rr->resrec.InterfaceID && (rr->ForceMCast || IsLocalDomain(rr->resrec.name)))) - if (mDNSPlatformMemSame(owner, &rr->WakeUp, sizeof(*owner))) - rr->SendRNow = mDNSInterfaceMark; // mark it now - - while (1) - { - mDNSu8 *p = m->omsg.data; - // To comply with RFC 2782, PutResourceRecord suppresses name compression for SRV records in unicast updates. - // For now we follow that same logic for SPS registrations too. - // If we decide to compress SRV records in SPS registrations in the future, we can achieve that by creating our - // initial DNSMessage with h.flags set to zero, and then update it to UpdateReqFlags right before sending the packet. - InitializeDNSMessage(&m->omsg.h, mDNSOpaque16IsZero(id) ? mDNS_NewMessageID(m) : id, UpdateReqFlags); - - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->SendRNow || (!mDNSOpaque16IsZero(id) && !AuthRecord_uDNS(rr) && mDNSSameOpaque16(rr->updateid, id) && m->timenow - (rr->LastAPTime + rr->ThisAPInterval) >= 0)) - if (mDNSPlatformMemSame(owner, &rr->WakeUp, sizeof(*owner))) - { - mDNSu8 *newptr; - const mDNSu8 *const limit = m->omsg.data + (m->omsg.h.mDNS_numUpdates ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData) - optspace; - if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) - rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the 'unique' bit so PutResourceRecord will set it - newptr = PutResourceRecordTTLWithLimit(&m->omsg, p, &m->omsg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl, limit); - rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear 'unique' bit back to normal state - if (!newptr) - LogSPS("SendSPSRegistration put %s FAILED %d/%d %s", intf->ifname, p - m->omsg.data, limit - m->omsg.data, ARDisplayString(m, rr)); - else - { - LogSPS("SendSPSRegistration put %s %s", intf->ifname, ARDisplayString(m, rr)); - rr->SendRNow = mDNSNULL; - rr->ThisAPInterval = mDNSPlatformOneSecond; - rr->LastAPTime = m->timenow; - rr->updateid = m->omsg.h.id; - if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0) - m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval); - p = newptr; - } - } - - if (!m->omsg.h.mDNS_numUpdates) break; - else - { - AuthRecord opt; - mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); - opt.resrec.rrclass = NormalMaxDNSMessageData; - opt.resrec.rdlength = sizeof(rdataOPT) * 2; // Two options in this OPT record - opt.resrec.rdestimate = sizeof(rdataOPT) * 2; - opt.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease; - opt.resrec.rdata->u.opt[0].optlen = DNSOpt_LeaseData_Space - 4; - opt.resrec.rdata->u.opt[0].u.updatelease = DEFAULT_UPDATE_LEASE; - if (!owner->HMAC.l[0]) // If no owner data, - SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[1]); // use our own interface information - else // otherwise, use the owner data we were given - { - opt.resrec.rdata->u.opt[1].u.owner = *owner; - opt.resrec.rdata->u.opt[1].opt = kDNSOpt_Owner; - opt.resrec.rdata->u.opt[1].optlen = DNSOpt_Owner_Space(&owner->HMAC, &owner->IMAC) - 4; - } - LogSPS("SendSPSRegistration put %s %s", intf->ifname, ARDisplayString(m, &opt)); - p = PutResourceRecordTTLWithLimit(&m->omsg, p, &m->omsg.h.numAdditionals, &opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData); - if (!p) - LogMsg("SendSPSRegistration: Failed to put OPT record (%d updates) %s", m->omsg.h.mDNS_numUpdates, ARDisplayString(m, &opt)); - else - { - mStatus err; - - LogSPS("SendSPSRegistration: Sending Update %s %d (%d) id %5d with %d records %d bytes to %#a:%d", intf->ifname, intf->NextSPSAttempt, sps, - mDNSVal16(m->omsg.h.id), m->omsg.h.mDNS_numUpdates, p - m->omsg.data, &intf->SPSAddr[sps], mDNSVal16(intf->SPSPort[sps])); - // if (intf->NextSPSAttempt < 5) m->omsg.h.flags = zeroID; // For simulating packet loss - err = mDNSSendDNSMessage(m, &m->omsg, p, intf->InterfaceID, mDNSNULL, &intf->SPSAddr[sps], intf->SPSPort[sps], mDNSNULL, mDNSNULL); - if (err) LogSPS("SendSPSRegistration: mDNSSendDNSMessage err %d", err); - if (err && intf->SPSAddr[sps].type == mDNSAddrType_IPv6 && intf->NetWakeResolve[sps].ThisQInterval == -1) - { - LogSPS("SendSPSRegistration %d %##s failed to send to IPv6 address; will try IPv4 instead", sps, intf->NetWakeResolve[sps].qname.c); - intf->NetWakeResolve[sps].qtype = kDNSType_A; - mDNS_StartQuery_internal(m, &intf->NetWakeResolve[sps]); - return; - } - } - } - } - - intf->NextSPSAttemptTime = m->timenow + mDNSPlatformOneSecond * 10; // If successful, update NextSPSAttemptTime +{ + const int optspace = DNSOpt_Header_Space + DNSOpt_LeaseData_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC); + const int sps = intf->NextSPSAttempt / 3; + AuthRecord *rr; + mDNSOpaque16 msgid; + mDNSu32 scopeid; + + scopeid = mDNSPlatformInterfaceIndexfromInterfaceID(m, intf->InterfaceID, mDNStrue); + if (!intf->SPSAddr[sps].type) + { + intf->NextSPSAttemptTime = m->timenow + mDNSPlatformOneSecond; + if (m->NextScheduledSPRetry - intf->NextSPSAttemptTime > 0) + m->NextScheduledSPRetry = intf->NextSPSAttemptTime; + LogSPS("SendSPSRegistration: %s SPS %d (%d) %##s not yet resolved", intf->ifname, intf->NextSPSAttempt, sps, intf->NetWakeResolve[sps].qname.c); + goto exit; + } + + // Mark our mDNS records (not unicast records) for transfer to SPS + if (mDNSOpaque16IsZero(id)) + { + // We may have to register this record over multiple interfaces and we don't want to + // overwrite the id. We send the registration over interface X with id "IDX" and before + // we get a response, we overwrite with id "IDY" for interface Y and we won't accept responses + // for "IDX". Hence, we want to use the same ID across all interfaces. + // + // In the case of sleep proxy server transfering its records when it goes to sleep, the owner + // option check below will set the same ID across the records from the same owner. Records + // with different owner option gets different ID. + msgid = mDNS_NewMessageID(m); + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.RecordType > kDNSRecordTypeDeregistering) + if (rr->resrec.InterfaceID == intf->InterfaceID || (!rr->resrec.InterfaceID && (rr->ForceMCast || IsLocalDomain(rr->resrec.name)))) + if (mDNSPlatformMemSame(owner, &rr->WakeUp, sizeof(*owner))) + { + rr->SendRNow = mDNSInterfaceMark; // mark it now + // When we are registering on the first interface, rr->updateid is zero in which case + // initialize with the new ID. For subsequent interfaces, we want to use the same ID. + // At the end, all the updates sent across all the interfaces with the same ID. + if (mDNSOpaque16IsZero(rr->updateid)) + rr->updateid = msgid; + else + msgid = rr->updateid; + } + } + else + msgid = id; + + while (1) + { + mDNSu8 *p = m->omsg.data; + // To comply with RFC 2782, PutResourceRecord suppresses name compression for SRV records in unicast updates. + // For now we follow that same logic for SPS registrations too. + // If we decide to compress SRV records in SPS registrations in the future, we can achieve that by creating our + // initial DNSMessage with h.flags set to zero, and then update it to UpdateReqFlags right before sending the packet. + InitializeDNSMessage(&m->omsg.h, msgid, UpdateReqFlags); + + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->SendRNow || mDNSUpdateOkToSend(m, rr, intf, scopeid)) + { + if (mDNSPlatformMemSame(owner, &rr->WakeUp, sizeof(*owner))) + { + mDNSu8 *newptr; + const mDNSu8 *const limit = m->omsg.data + (m->omsg.h.mDNS_numUpdates ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData) - optspace; + + // If we can't update the keepalive record, don't send it + if (mDNS_KeepaliveRecord(&rr->resrec) && (UpdateKeepaliveRData(m, rr, intf) != mStatus_NoError)) + { + if (scopeid < (sizeof(rr->updateIntID) * mDNSNBBY)) + { + bit_clr_opaque64(rr->updateIntID, scopeid); + } + continue; + } + + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the 'unique' bit so PutResourceRecord will set it + newptr = PutResourceRecordTTLWithLimit(&m->omsg, p, &m->omsg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl, limit); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear 'unique' bit back to normal state + if (!newptr) + LogSPS("SendSPSRegistration put %s FAILED %d/%d %s", intf->ifname, p - m->omsg.data, limit - m->omsg.data, ARDisplayString(m, rr)); + else + { + LogSPS("SendSPSRegistration put %s 0x%x 0x%x (updateid %d) %s", intf->ifname, rr->updateIntID.l[1], rr->updateIntID.l[0], mDNSVal16(m->omsg.h.id), ARDisplayString(m, rr)); + rr->SendRNow = mDNSNULL; + rr->ThisAPInterval = mDNSPlatformOneSecond; + rr->LastAPTime = m->timenow; + // should be initialized above + if (mDNSOpaque16IsZero(rr->updateid)) LogMsg("SendSPSRegistration: ERROR!! rr %s updateid is zero", ARDisplayString(m, rr)); + if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0) + m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval); + p = newptr; + } + } + } + + if (!m->omsg.h.mDNS_numUpdates) break; + else + { + AuthRecord opt; + mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + opt.resrec.rrclass = NormalMaxDNSMessageData; + opt.resrec.rdlength = sizeof(rdataOPT) * 2; // Two options in this OPT record + opt.resrec.rdestimate = sizeof(rdataOPT) * 2; + opt.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease; + opt.resrec.rdata->u.opt[0].optlen = DNSOpt_LeaseData_Space - 4; + opt.resrec.rdata->u.opt[0].u.updatelease = DEFAULT_UPDATE_LEASE; + if (!owner->HMAC.l[0]) // If no owner data, + SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[1]); // use our own interface information + else // otherwise, use the owner data we were given + { + opt.resrec.rdata->u.opt[1].u.owner = *owner; + opt.resrec.rdata->u.opt[1].opt = kDNSOpt_Owner; + opt.resrec.rdata->u.opt[1].optlen = DNSOpt_Owner_Space(&owner->HMAC, &owner->IMAC) - 4; + } + LogSPS("SendSPSRegistration put %s %s", intf->ifname, ARDisplayString(m, &opt)); + p = PutResourceRecordTTLWithLimit(&m->omsg, p, &m->omsg.h.numAdditionals, &opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData); + if (!p) + LogMsg("SendSPSRegistration: Failed to put OPT record (%d updates) %s", m->omsg.h.mDNS_numUpdates, ARDisplayString(m, &opt)); + else + { + mStatus err; + + LogSPS("SendSPSRegistration: Sending Update %s %d (%d) id %5d with %d records %d bytes to %#a:%d", intf->ifname, intf->NextSPSAttempt, sps, + mDNSVal16(m->omsg.h.id), m->omsg.h.mDNS_numUpdates, p - m->omsg.data, &intf->SPSAddr[sps], mDNSVal16(intf->SPSPort[sps])); + // if (intf->NextSPSAttempt < 5) m->omsg.h.flags = zeroID; // For simulating packet loss + err = mDNSSendDNSMessage(m, &m->omsg, p, intf->InterfaceID, mDNSNULL, &intf->SPSAddr[sps], intf->SPSPort[sps], mDNSNULL, mDNSNULL, mDNSfalse); + if (err) LogSPS("SendSPSRegistration: mDNSSendDNSMessage err %d", err); + if (err && intf->SPSAddr[sps].type == mDNSAddrType_IPv6 && intf->NetWakeResolve[sps].ThisQInterval == -1) + { + LogSPS("SendSPSRegistration %d %##s failed to send to IPv6 address; will try IPv4 instead", sps, intf->NetWakeResolve[sps].qname.c); + intf->NetWakeResolve[sps].qtype = kDNSType_A; + mDNS_StartQuery_internal(m, &intf->NetWakeResolve[sps]); + return; + } + } + } + } + + intf->NextSPSAttemptTime = m->timenow + mDNSPlatformOneSecond * 10; // If successful, update NextSPSAttemptTime exit: - if (mDNSOpaque16IsZero(id) && intf->NextSPSAttempt < 8) intf->NextSPSAttempt++; - } + if (mDNSOpaque16IsZero(id) && intf->NextSPSAttempt < 8) intf->NextSPSAttempt++; +} mDNSlocal mDNSBool RecordIsFirstOccurrenceOfOwner(mDNS *const m, const AuthRecord *const rr) - { - AuthRecord *ar; - for (ar = m->ResourceRecords; ar && ar != rr; ar=ar->next) - if (mDNSPlatformMemSame(&rr->WakeUp, &ar->WakeUp, sizeof(rr->WakeUp))) return mDNSfalse; - return mDNStrue; - } +{ + AuthRecord *ar; + for (ar = m->ResourceRecords; ar && ar != rr; ar=ar->next) + if (mDNSPlatformMemSame(&rr->WakeUp, &ar->WakeUp, sizeof(rr->WakeUp))) return mDNSfalse; + return mDNStrue; +} + +mDNSlocal void mDNSCoreStoreProxyRR(mDNS *const m, const mDNSInterfaceID InterfaceID, AuthRecord *const rr) +{ + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); + AuthRecord *newRR = mDNSPlatformMemAllocate(sizeof(AuthRecord)); + + if ((intf == mDNSNULL) || (newRR == mDNSNULL)) + { + return; + } + + mDNSPlatformMemZero(newRR, sizeof(AuthRecord)); + mDNS_SetupResourceRecord(newRR, mDNSNULL, InterfaceID, rr->resrec.rrtype, + rr->resrec.rroriginalttl, rr->resrec.RecordType, + rr->ARType, mDNSNULL, mDNSNULL); + + AssignDomainName(&newRR->namestorage, &rr->namestorage); + newRR->resrec.rdlength = DomainNameLength(rr->resrec.name); + newRR->resrec.rdata->u.name.c[0] = 0; + AssignDomainName(&newRR->resrec.rdata->u.name, rr->resrec.name); + newRR->resrec.namehash = DomainNameHashValue(newRR->resrec.name); + newRR->resrec.rrclass = rr->resrec.rrclass; + + if (intf->ip.type == mDNSAddrType_IPv4) + { + newRR->resrec.rdata->u.ipv4 = rr->resrec.rdata->u.ipv4; + } + else + { + newRR->resrec.rdata->u.ipv6 = rr->resrec.rdata->u.ipv6; + } + SetNewRData(&newRR->resrec, mDNSNULL, 0); + + // Insert the new node at the head of the list. + newRR->next = intf->SPSRRSet; + intf->SPSRRSet = newRR; +} + +// Some records are interface specific and some are not. The ones that are supposed to be registered +// on multiple interfaces need to be initialized with all the valid interfaces on which it will be sent. +// updateIntID bit field tells us on which interfaces we need to register this record. When we get an +// ack from the sleep proxy server, we clear the interface bit. This way, we know when a record completes +// registration on all the interfaces +mDNSlocal void SPSInitRecordsBeforeUpdate(mDNS *const m, mDNSOpaque64 updateIntID) +{ + AuthRecord *ar; + LogSPS("SPSInitRecordsBeforeUpdate: UpdateIntID 0x%x 0x%x", updateIntID.l[1], updateIntID.l[0]); + + // Before we store the A and AAAA records that we are going to register with the sleep proxy, + // make sure that the old sleep proxy records are removed. + mDNSCoreFreeProxyRR(m); + + // For records that are registered only on a specific interface, mark only that bit as it will + // never be registered on any other interface. For others, it should be sent on all interfaces. + for (ar = m->ResourceRecords; ar; ar=ar->next) + { + if (AuthRecord_uDNS(ar)) + { + continue; + } + ar->updateid = zeroID; + if (!ar->resrec.InterfaceID) + { + LogSPS("Setting scopeid (ALL) 0x%x 0x%x for %s", updateIntID.l[1], updateIntID.l[0], ARDisplayString(m, ar)); + ar->updateIntID = updateIntID; + } + else + { + // Filter records that belong to interfaces that we won't register the records on. UpdateIntID captures + // exactly this. + mDNSu32 scopeid = mDNSPlatformInterfaceIndexfromInterfaceID(m, ar->resrec.InterfaceID, mDNStrue); + if ((scopeid < (sizeof(updateIntID) * mDNSNBBY)) && bit_get_opaque64(updateIntID, scopeid)) + { + ar->updateIntID = zeroOpaque64; + bit_set_opaque64(ar->updateIntID, scopeid); + LogSPS("Setting scopeid(%d) 0x%x 0x%x for %s", scopeid, ar->updateIntID.l[1], ar->updateIntID.l[0], ARDisplayString(m, ar)); + } + else + { + LogSPS("SPSInitRecordsBeforeUpdate: scopeid %d beyond range or not valid for SPS registration", scopeid); + } + } + // Store the A and AAAA records that we registered with the sleep proxy. + // We will use this to prevent spurious name conflicts that may occur when we wake up + if (ar->resrec.rrtype == kDNSType_A || ar->resrec.rrtype == kDNSType_AAAA) + { + mDNSCoreStoreProxyRR(m, ar->resrec.InterfaceID, ar); + } + } +} mDNSlocal void SendSPSRegistration(mDNS *const m, NetworkInterfaceInfo *const intf, const mDNSOpaque16 id) - { - AuthRecord *ar; - OwnerOptData owner = zeroOwner; - - SendSPSRegistrationForOwner(m, intf, id, &owner); - - for (ar = m->ResourceRecords; ar; ar=ar->next) - { - if (!mDNSPlatformMemSame(&owner, &ar->WakeUp, sizeof(owner)) && RecordIsFirstOccurrenceOfOwner(m, ar)) - { - owner = ar->WakeUp; - SendSPSRegistrationForOwner(m, intf, id, &owner); - } - } - } +{ + AuthRecord *ar; + OwnerOptData owner = zeroOwner; + + SendSPSRegistrationForOwner(m, intf, id, &owner); + + for (ar = m->ResourceRecords; ar; ar=ar->next) + { + if (!mDNSPlatformMemSame(&owner, &ar->WakeUp, sizeof(owner)) && RecordIsFirstOccurrenceOfOwner(m, ar)) + { + owner = ar->WakeUp; + SendSPSRegistrationForOwner(m, intf, id, &owner); + } + } +} // RetrySPSRegistrations is called from SendResponses, with the lock held mDNSlocal void RetrySPSRegistrations(mDNS *const m) - { - AuthRecord *rr; - NetworkInterfaceInfo *intf; - - // First make sure none of our interfaces' NextSPSAttemptTimes are inadvertently set to m->timenow + mDNSPlatformOneSecond * 10 - for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) - if (intf->NextSPSAttempt && intf->NextSPSAttemptTime == m->timenow + mDNSPlatformOneSecond * 10) - intf->NextSPSAttemptTime++; - - // Retry any record registrations that are due - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (!AuthRecord_uDNS(rr) && !mDNSOpaque16IsZero(rr->updateid) && m->timenow - (rr->LastAPTime + rr->ThisAPInterval) >= 0) - for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) - if (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == intf->InterfaceID) - { - LogSPS("RetrySPSRegistrations: %s", ARDisplayString(m, rr)); - SendSPSRegistration(m, intf, rr->updateid); - } - - // For interfaces where we did an SPS registration attempt, increment intf->NextSPSAttempt - for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) - if (intf->NextSPSAttempt && intf->NextSPSAttemptTime == m->timenow + mDNSPlatformOneSecond * 10 && intf->NextSPSAttempt < 8) - intf->NextSPSAttempt++; - } +{ + AuthRecord *rr; + NetworkInterfaceInfo *intf; + + // First make sure none of our interfaces' NextSPSAttemptTimes are inadvertently set to m->timenow + mDNSPlatformOneSecond * 10 + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) + if (intf->NextSPSAttempt && intf->NextSPSAttemptTime == m->timenow + mDNSPlatformOneSecond * 10) + intf->NextSPSAttemptTime++; + + // Retry any record registrations that are due + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (!AuthRecord_uDNS(rr) && !mDNSOpaque16IsZero(rr->updateid) && m->timenow - (rr->LastAPTime + rr->ThisAPInterval) >= 0) + { + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) + { + // If we still have registrations pending on this interface, send it now + mDNSu32 scopeid = mDNSPlatformInterfaceIndexfromInterfaceID(m, intf->InterfaceID, mDNStrue); + if ((scopeid >= (sizeof(rr->updateIntID) * mDNSNBBY) || bit_get_opaque64(rr->updateIntID, scopeid)) && + (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == intf->InterfaceID)) + { + LogSPS("RetrySPSRegistrations: 0x%x 0x%x (updateid %d) %s", rr->updateIntID.l[1], rr->updateIntID.l[0], mDNSVal16(rr->updateid), ARDisplayString(m, rr)); + SendSPSRegistration(m, intf, rr->updateid); + } + } + } + + // For interfaces where we did an SPS registration attempt, increment intf->NextSPSAttempt + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) + if (intf->NextSPSAttempt && intf->NextSPSAttemptTime == m->timenow + mDNSPlatformOneSecond * 10 && intf->NextSPSAttempt < 8) + intf->NextSPSAttempt++; +} mDNSlocal void NetWakeResolve(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) - { - NetworkInterfaceInfo *intf = (NetworkInterfaceInfo *)question->QuestionContext; - int sps = (int)(question - intf->NetWakeResolve); - (void)m; // Unused - LogSPS("NetWakeResolve: SPS: %d Add: %d %s", sps, AddRecord, RRDisplayString(m, answer)); - - if (!AddRecord) return; // Don't care about REMOVE events - if (answer->rrtype != question->qtype) return; // Don't care about CNAMEs - - // if (answer->rrtype == kDNSType_AAAA && sps == 0) return; // To test failing to resolve sleep proxy's address - - if (answer->rrtype == kDNSType_SRV) - { - // 1. Got the SRV record; now look up the target host's IPv6 link-local address - mDNS_StopQuery(m, question); - intf->SPSPort[sps] = answer->rdata->u.srv.port; - AssignDomainName(&question->qname, &answer->rdata->u.srv.target); - question->qtype = kDNSType_AAAA; - mDNS_StartQuery(m, question); - } - else if (answer->rrtype == kDNSType_AAAA && answer->rdlength == sizeof(mDNSv6Addr) && mDNSv6AddressIsLinkLocal(&answer->rdata->u.ipv6)) - { - // 2. Got the target host's IPv6 link-local address; record address and initiate an SPS registration if appropriate - mDNS_StopQuery(m, question); - question->ThisQInterval = -1; - intf->SPSAddr[sps].type = mDNSAddrType_IPv6; - intf->SPSAddr[sps].ip.v6 = answer->rdata->u.ipv6; - mDNS_Lock(m); - if (sps == intf->NextSPSAttempt/3) SendSPSRegistration(m, intf, zeroID); // If we're ready for this result, use it now - mDNS_Unlock(m); - } - else if (answer->rrtype == kDNSType_AAAA && answer->rdlength == 0) - { - // 3. Got negative response -- target host apparently has IPv6 disabled -- so try looking up the target host's IPv4 address(es) instead - mDNS_StopQuery(m, question); - LogSPS("NetWakeResolve: SPS %d %##s has no IPv6 address, will try IPv4 instead", sps, question->qname.c); - question->qtype = kDNSType_A; - mDNS_StartQuery(m, question); - } - else if (answer->rrtype == kDNSType_A && answer->rdlength == sizeof(mDNSv4Addr)) - { - // 4. Got an IPv4 address for the target host; record address and initiate an SPS registration if appropriate - mDNS_StopQuery(m, question); - question->ThisQInterval = -1; - intf->SPSAddr[sps].type = mDNSAddrType_IPv4; - intf->SPSAddr[sps].ip.v4 = answer->rdata->u.ipv4; - mDNS_Lock(m); - if (sps == intf->NextSPSAttempt/3) SendSPSRegistration(m, intf, zeroID); // If we're ready for this result, use it now - mDNS_Unlock(m); - } - } +{ + NetworkInterfaceInfo *intf = (NetworkInterfaceInfo *)question->QuestionContext; + int sps = (int)(question - intf->NetWakeResolve); + (void)m; // Unused + LogSPS("NetWakeResolve: SPS: %d Add: %d %s", sps, AddRecord, RRDisplayString(m, answer)); + + if (!AddRecord) return; // Don't care about REMOVE events + if (answer->rrtype != question->qtype) return; // Don't care about CNAMEs + + // if (answer->rrtype == kDNSType_AAAA && sps == 0) return; // To test failing to resolve sleep proxy's address + + if (answer->rrtype == kDNSType_SRV) + { + // 1. Got the SRV record; now look up the target host's IPv6 link-local address + mDNS_StopQuery(m, question); + intf->SPSPort[sps] = answer->rdata->u.srv.port; + AssignDomainName(&question->qname, &answer->rdata->u.srv.target); + question->qtype = kDNSType_AAAA; + mDNS_StartQuery(m, question); + } + else if (answer->rrtype == kDNSType_AAAA && answer->rdlength == sizeof(mDNSv6Addr) && mDNSv6AddressIsLinkLocal(&answer->rdata->u.ipv6)) + { + // 2. Got the target host's IPv6 link-local address; record address and initiate an SPS registration if appropriate + mDNS_StopQuery(m, question); + question->ThisQInterval = -1; + intf->SPSAddr[sps].type = mDNSAddrType_IPv6; + intf->SPSAddr[sps].ip.v6 = answer->rdata->u.ipv6; + mDNS_Lock(m); + if (sps == intf->NextSPSAttempt/3) SendSPSRegistration(m, intf, zeroID); // If we're ready for this result, use it now + mDNS_Unlock(m); + } + else if (answer->rrtype == kDNSType_AAAA && answer->rdlength == 0) + { + // 3. Got negative response -- target host apparently has IPv6 disabled -- so try looking up the target host's IPv4 address(es) instead + mDNS_StopQuery(m, question); + LogSPS("NetWakeResolve: SPS %d %##s has no IPv6 address, will try IPv4 instead", sps, question->qname.c); + question->qtype = kDNSType_A; + mDNS_StartQuery(m, question); + } + else if (answer->rrtype == kDNSType_A && answer->rdlength == sizeof(mDNSv4Addr)) + { + // 4. Got an IPv4 address for the target host; record address and initiate an SPS registration if appropriate + mDNS_StopQuery(m, question); + question->ThisQInterval = -1; + intf->SPSAddr[sps].type = mDNSAddrType_IPv4; + intf->SPSAddr[sps].ip.v4 = answer->rdata->u.ipv4; + mDNS_Lock(m); + if (sps == intf->NextSPSAttempt/3) SendSPSRegistration(m, intf, zeroID); // If we're ready for this result, use it now + mDNS_Unlock(m); + } +} mDNSexport mDNSBool mDNSCoreHaveAdvertisedMulticastServices(mDNS *const m) - { - AuthRecord *rr; - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.rrtype == kDNSType_SRV && !AuthRecord_uDNS(rr) && !mDNSSameIPPort(rr->resrec.rdata->u.srv.port, DiscardPort)) - return mDNStrue; - return mDNSfalse; - } +{ + AuthRecord *rr; + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (mDNS_KeepaliveRecord(&rr->resrec) || (rr->resrec.rrtype == kDNSType_SRV && !AuthRecord_uDNS(rr) && !mDNSSameIPPort(rr->resrec.rdata->u.srv.port, DiscardPort))) + return mDNStrue; + return mDNSfalse; +} mDNSlocal void SendSleepGoodbyes(mDNS *const m) - { - AuthRecord *rr; - m->SleepState = SleepState_Sleeping; +{ + AuthRecord *rr; + m->SleepState = SleepState_Sleeping; #ifndef UNICAST_DISABLED - SleepRecordRegistrations(m); // If we have no SPS, need to deregister our uDNS records + SleepRecordRegistrations(m); // If we have no SPS, need to deregister our uDNS records #endif /* UNICAST_DISABLED */ - // 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->RequireGoodbye) - rr->ImmedAnswer = mDNSInterfaceMark; - SendResponses(m); - } + // Mark all the records we need to deregister and send them + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) + rr->ImmedAnswer = mDNSInterfaceMark; + SendResponses(m); +} // BeginSleepProcessing is called, with the lock held, from either mDNS_Execute or mDNSCoreMachineSleep mDNSlocal void BeginSleepProcessing(mDNS *const m) - { - mDNSBool SendGoodbyes = mDNStrue; - const CacheRecord *sps[3] = { mDNSNULL }; - - m->NextScheduledSPRetry = m->timenow; - - if (!m->SystemWakeOnLANEnabled) LogSPS("BeginSleepProcessing: m->SystemWakeOnLANEnabled is false"); - else if (!mDNSCoreHaveAdvertisedMulticastServices(m)) LogSPS("BeginSleepProcessing: No advertised services"); - else // If we have at least one advertised service - { - NetworkInterfaceInfo *intf; - for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) - { - if (!intf->NetWake) LogSPS("BeginSleepProcessing: %-6s not capable of magic packet wakeup", intf->ifname); +{ + mDNSBool SendGoodbyes = mDNStrue; + const CacheRecord *sps[3] = { mDNSNULL }; + mDNSOpaque64 updateIntID = zeroOpaque64; + + m->NextScheduledSPRetry = m->timenow; + + if (!m->SystemWakeOnLANEnabled) LogSPS("BeginSleepProcessing: m->SystemWakeOnLANEnabled is false"); + else if (!mDNSCoreHaveAdvertisedMulticastServices(m)) LogSPS("BeginSleepProcessing: No advertised services"); + else // If we have at least one advertised service + { + NetworkInterfaceInfo *intf; + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) + { + if (!intf->NetWake) LogSPS("BeginSleepProcessing: %-6s not capable of magic packet wakeup", intf->ifname); #if APPLE_OSX_mDNSResponder - else if (ActivateLocalProxy(m, intf->ifname) == mStatus_NoError) - { - SendGoodbyes = mDNSfalse; - LogSPS("BeginSleepProcessing: %-6s using local proxy", intf->ifname); - // This will leave m->SleepState set to SleepState_Transferring, - // which is okay because with no outstanding resolves, or updates in flight, - // mDNSCoreReadyForSleep() will conclude correctly that all the updates have already completed - } + else if (ActivateLocalProxy(m, intf->ifname) == mStatus_NoError) + { + SendGoodbyes = mDNSfalse; + LogSPS("BeginSleepProcessing: %-6s using local proxy", intf->ifname); + // This will leave m->SleepState set to SleepState_Transferring, + // which is okay because with no outstanding resolves, or updates in flight, + // mDNSCoreReadyForSleep() will conclude correctly that all the updates have already completed + } #endif // APPLE_OSX_mDNSResponder - else - { - FindSPSInCache(m, &intf->NetWakeBrowse, sps); - if (!sps[0]) LogSPS("BeginSleepProcessing: %-6s %#a No Sleep Proxy Server found (Next Browse Q in %d, interval %d)", - intf->ifname, &intf->ip, NextQSendTime(&intf->NetWakeBrowse) - m->timenow, intf->NetWakeBrowse.ThisQInterval); - else - { - int i; - SendGoodbyes = mDNSfalse; - intf->NextSPSAttempt = 0; - intf->NextSPSAttemptTime = m->timenow + mDNSPlatformOneSecond; - // Don't need to set m->NextScheduledSPRetry here because we already set "m->NextScheduledSPRetry = m->timenow" above - for (i=0; i<3; i++) - { + else + { + FindSPSInCache(m, &intf->NetWakeBrowse, sps); + if (!sps[0]) LogSPS("BeginSleepProcessing: %-6s %#a No Sleep Proxy Server found (Next Browse Q in %d, interval %d)", + intf->ifname, &intf->ip, NextQSendTime(&intf->NetWakeBrowse) - m->timenow, intf->NetWakeBrowse.ThisQInterval); + else + { + int i; + mDNSu32 scopeid; + SendGoodbyes = mDNSfalse; + intf->NextSPSAttempt = 0; + intf->NextSPSAttemptTime = m->timenow + mDNSPlatformOneSecond; + +#if APPLE_OSX_mDNSResponder + // Before we start the sleep processing, stop IPv6 advertisements + mDNSPlatformToggleInterfaceAdvt(m, mDNStrue); +#endif + scopeid = mDNSPlatformInterfaceIndexfromInterfaceID(m, intf->InterfaceID, mDNStrue); + // Now we know for sure that we have to wait for registration to complete on this interface. + if (scopeid < (sizeof(updateIntID) * mDNSNBBY)) + bit_set_opaque64(updateIntID, scopeid); + + // Don't need to set m->NextScheduledSPRetry here because we already set "m->NextScheduledSPRetry = m->timenow" above + for (i=0; i<3; i++) + { #if ForceAlerts - if (intf->SPSAddr[i].type) - { LogMsg("BeginSleepProcessing: %s %d intf->SPSAddr[i].type %d", intf->ifname, i, intf->SPSAddr[i].type); *(long*)0 = 0; } - if (intf->NetWakeResolve[i].ThisQInterval >= 0) - { LogMsg("BeginSleepProcessing: %s %d intf->NetWakeResolve[i].ThisQInterval %d", intf->ifname, i, intf->NetWakeResolve[i].ThisQInterval); *(long*)0 = 0; } + if (intf->SPSAddr[i].type) + { LogMsg("BeginSleepProcessing: %s %d intf->SPSAddr[i].type %d", intf->ifname, i, intf->SPSAddr[i].type); *(long*)0 = 0; } + if (intf->NetWakeResolve[i].ThisQInterval >= 0) + { LogMsg("BeginSleepProcessing: %s %d intf->NetWakeResolve[i].ThisQInterval %d", intf->ifname, i, intf->NetWakeResolve[i].ThisQInterval); *(long*)0 = 0; } #endif - intf->SPSAddr[i].type = mDNSAddrType_None; - if (intf->NetWakeResolve[i].ThisQInterval >= 0) mDNS_StopQuery(m, &intf->NetWakeResolve[i]); - intf->NetWakeResolve[i].ThisQInterval = -1; - if (sps[i]) - { - LogSPS("BeginSleepProcessing: %-6s Found Sleep Proxy Server %d TTL %d %s", intf->ifname, i, sps[i]->resrec.rroriginalttl, CRDisplayString(m, sps[i])); - mDNS_SetupQuestion(&intf->NetWakeResolve[i], intf->InterfaceID, &sps[i]->resrec.rdata->u.name, kDNSType_SRV, NetWakeResolve, intf); - intf->NetWakeResolve[i].ReturnIntermed = mDNStrue; - mDNS_StartQuery_internal(m, &intf->NetWakeResolve[i]); - } - } - } - } - } - } - - if (SendGoodbyes) // If we didn't find even one Sleep Proxy - { - LogSPS("BeginSleepProcessing: Not registering with Sleep Proxy Server"); - SendSleepGoodbyes(m); - } - } + intf->SPSAddr[i].type = mDNSAddrType_None; + if (intf->NetWakeResolve[i].ThisQInterval >= 0) mDNS_StopQuery(m, &intf->NetWakeResolve[i]); + intf->NetWakeResolve[i].ThisQInterval = -1; + if (sps[i]) + { + LogSPS("BeginSleepProcessing: %-6s Found Sleep Proxy Server %d TTL %d %s", intf->ifname, i, sps[i]->resrec.rroriginalttl, CRDisplayString(m, sps[i])); + mDNS_SetupQuestion(&intf->NetWakeResolve[i], intf->InterfaceID, &sps[i]->resrec.rdata->u.name, kDNSType_SRV, NetWakeResolve, intf); + intf->NetWakeResolve[i].ReturnIntermed = mDNStrue; + mDNS_StartQuery_internal(m, &intf->NetWakeResolve[i]); + } + } + } + } + } + } + + // If we have at least one interface on which we are registering with an external sleep proxy, + // initialize all the records appropriately. + if (!mDNSOpaque64IsZero(&updateIntID)) SPSInitRecordsBeforeUpdate(m, updateIntID); + + if (SendGoodbyes) // If we didn't find even one Sleep Proxy + { + LogSPS("BeginSleepProcessing: Not registering with Sleep Proxy Server"); + SendSleepGoodbyes(m); + } +} // Call mDNSCoreMachineSleep(m, mDNStrue) when the machine is about to go to sleep. // Call mDNSCoreMachineSleep(m, mDNSfalse) when the machine is has just woken up. // Normally, the platform support layer below mDNSCore should call this, not the client layer above. mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) - { - AuthRecord *rr; - - LogSPS("%s (old state %d) at %ld", sleep ? "Sleeping" : "Waking", m->SleepState, m->timenow); - - if (sleep && !m->SleepState) // Going to sleep - { - mDNS_Lock(m); - // If we're going to sleep, need to stop advertising that we're a Sleep Proxy Server - if (m->SPSSocket) - { - mDNSu8 oldstate = m->SPSState; - mDNS_DropLockBeforeCallback(); // mDNS_DeregisterService expects to be called without the lock held, so we emulate that here - m->SPSState = 2; - if (oldstate == 1) mDNS_DeregisterService(m, &m->SPSRecords); - mDNS_ReclaimLockAfterCallback(); - } - - m->SleepState = SleepState_Transferring; - if (m->SystemWakeOnLANEnabled && m->DelaySleep) - { - // If we just woke up moments ago, allow ten seconds for networking to stabilize before going back to sleep - LogSPS("mDNSCoreMachineSleep: Re-sleeping immediately after waking; will delay for %d ticks", m->DelaySleep - m->timenow); - m->SleepLimit = NonZeroTime(m->DelaySleep + mDNSPlatformOneSecond * 10); - } - else - { - m->DelaySleep = 0; - m->SleepLimit = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 10); - BeginSleepProcessing(m); - } +{ + AuthRecord *rr; + + LogSPS("%s (old state %d) at %ld", sleep ? "Sleeping" : "Waking", m->SleepState, m->timenow); + + if (sleep && !m->SleepState) // Going to sleep + { + mDNS_Lock(m); + // If we're going to sleep, need to stop advertising that we're a Sleep Proxy Server + if (m->SPSSocket) + { + mDNSu8 oldstate = m->SPSState; + mDNS_DropLockBeforeCallback(); // mDNS_DeregisterService expects to be called without the lock held, so we emulate that here + m->SPSState = 2; + if (oldstate == 1) mDNS_DeregisterService(m, &m->SPSRecords); + mDNS_ReclaimLockAfterCallback(); + } + + m->SleepState = SleepState_Transferring; + if (m->SystemWakeOnLANEnabled && m->DelaySleep) + { + // If we just woke up moments ago, allow ten seconds for networking to stabilize before going back to sleep + LogSPS("mDNSCoreMachineSleep: Re-sleeping immediately after waking; will delay for %d ticks", m->DelaySleep - m->timenow); + m->SleepLimit = NonZeroTime(m->DelaySleep + mDNSPlatformOneSecond * 10); + } + else + { + m->DelaySleep = 0; + m->SleepLimit = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 10); + BeginSleepProcessing(m); + } #ifndef UNICAST_DISABLED - SuspendLLQs(m); + SuspendLLQs(m); #endif - mDNS_Unlock(m); - // RemoveAutoTunnel6Record needs to be called outside the lock, as it grabs the lock also. #if APPLE_OSX_mDNSResponder - RemoveAutoTunnel6Record(m); + RemoveAutoTunnel6Record(m); #endif - LogSPS("mDNSCoreMachineSleep: m->SleepState %d (%s) seq %d", m->SleepState, - m->SleepState == SleepState_Transferring ? "Transferring" : - m->SleepState == SleepState_Sleeping ? "Sleeping" : "?", m->SleepSeqNum); - } - else if (!sleep) // Waking up - { - mDNSu32 slot; - CacheGroup *cg; - CacheRecord *cr; - NetworkInterfaceInfo *intf; - - mDNS_Lock(m); - // Reset SleepLimit back to 0 now that we're awake again. - m->SleepLimit = 0; - - // If we were previously sleeping, but now we're not, increment m->SleepSeqNum to indicate that we're entering a new period of wakefulness - if (m->SleepState != SleepState_Awake) - { - m->SleepState = SleepState_Awake; - m->SleepSeqNum++; - // If the machine wakes and then immediately tries to sleep again (e.g. a maintenance wake) - // then we enforce a minimum delay of 16 seconds before we begin sleep processing. - // This is to allow time for the Ethernet link to come up, DHCP to get an address, mDNS to issue queries, etc., - // before we make our determination of whether there's a Sleep Proxy out there we should register with. - m->DelaySleep = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 16); - } - - if (m->SPSState == 3) - { - m->SPSState = 0; - mDNSCoreBeSleepProxyServer_internal(m, m->SPSType, m->SPSPortability, m->SPSMarginalPower, m->SPSTotalPower); - } - - // In case we gave up waiting and went to sleep before we got an ack from the Sleep Proxy, - // on wake we go through our record list and clear updateid back to zero - for (rr = m->ResourceRecords; rr; rr=rr->next) rr->updateid = zeroID; - - // ... and the same for NextSPSAttempt - for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) intf->NextSPSAttempt = -1; - - // Restart unicast and multicast queries - mDNSCoreRestartQueries(m); - - // and reactivtate service registrations - m->NextSRVUpdate = NonZeroTime(m->timenow + mDNSPlatformOneSecond); - LogInfo("mDNSCoreMachineSleep waking: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow); - - // 2. Re-validate our cache records - FORALL_CACHERECORDS(slot, cg, cr) - mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForWake); - - // 3. Retrigger probing and announcing for all our authoritative records - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (AuthRecord_uDNS(rr)) - { - ActivateUnicastRegistration(m, rr); - } - else - { - if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique; - rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); - rr->AnnounceCount = InitialAnnounceCount; - rr->SendNSECNow = mDNSNULL; - InitializeLastAPTime(m, rr); - } - - // 4. Refresh NAT mappings - // We don't want to have to assume that all hardware can necessarily keep accurate - // track of passage of time while asleep, so on wake we refresh our NAT mappings - // We typically wake up with no interfaces active, so there's no need to rush to try to find our external address. - // When we get a network configuration change, mDNSMacOSXNetworkChanged calls uDNS_SetupDNSConfig, which calls - // mDNS_SetPrimaryInterfaceInfo, which then sets m->retryGetAddr to immediately request our external address from the NAT gateway. - m->retryIntervalGetAddr = NATMAP_INIT_RETRY; - m->retryGetAddr = m->timenow + mDNSPlatformOneSecond * 5; - LogInfo("mDNSCoreMachineSleep: retryGetAddr in %d %d", m->retryGetAddr - m->timenow, m->timenow); - RecreateNATMappings(m); - mDNS_Unlock(m); - } - } + LogSPS("mDNSCoreMachineSleep: m->SleepState %d (%s) seq %d", m->SleepState, + m->SleepState == SleepState_Transferring ? "Transferring" : + m->SleepState == SleepState_Sleeping ? "Sleeping" : "?", m->SleepSeqNum); + mDNS_Unlock(m); + } + else if (!sleep) // Waking up + { + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *cr; + NetworkInterfaceInfo *intf; + + mDNS_Lock(m); + // Reset SleepLimit back to 0 now that we're awake again. + m->SleepLimit = 0; + + // If we were previously sleeping, but now we're not, increment m->SleepSeqNum to indicate that we're entering a new period of wakefulness + if (m->SleepState != SleepState_Awake) + { + m->SleepState = SleepState_Awake; + m->SleepSeqNum++; + // If the machine wakes and then immediately tries to sleep again (e.g. a maintenance wake) + // then we enforce a minimum delay of 16 seconds before we begin sleep processing. + // This is to allow time for the Ethernet link to come up, DHCP to get an address, mDNS to issue queries, etc., + // before we make our determination of whether there's a Sleep Proxy out there we should register with. + m->DelaySleep = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 16); + } + + if (m->SPSState == 3) + { + m->SPSState = 0; + mDNSCoreBeSleepProxyServer_internal(m, m->SPSType, m->SPSPortability, m->SPSMarginalPower, m->SPSTotalPower, m->SPSFeatureFlags); + } + + // ... and the same for NextSPSAttempt + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) intf->NextSPSAttempt = -1; + + // Restart unicast and multicast queries + mDNSCoreRestartQueries(m); + + // and reactivtate service registrations + m->NextSRVUpdate = NonZeroTime(m->timenow + mDNSPlatformOneSecond); + LogInfo("mDNSCoreMachineSleep waking: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow); + + // 2. Re-validate our cache records + FORALL_CACHERECORDS(slot, cg, cr) + { + mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForWake); + } + + // 3. Retrigger probing and announcing for all our authoritative records + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (AuthRecord_uDNS(rr)) + { + ActivateUnicastRegistration(m, rr); + } + else + { + mDNSCoreRestartRegistration(m, rr, -1); + } + + // 4. Refresh NAT mappings + // We don't want to have to assume that all hardware can necessarily keep accurate + // track of passage of time while asleep, so on wake we refresh our NAT mappings + // We typically wake up with no interfaces active, so there's no need to rush to try to find our external address. + // When we get a network configuration change, mDNSMacOSXNetworkChanged calls uDNS_SetupDNSConfig, which calls + // mDNS_SetPrimaryInterfaceInfo, which then sets m->retryGetAddr to immediately request our external address from the NAT gateway. + m->retryIntervalGetAddr = NATMAP_INIT_RETRY; + m->retryGetAddr = m->timenow + mDNSPlatformOneSecond * 5; + LogInfo("mDNSCoreMachineSleep: retryGetAddr in %d %d", m->retryGetAddr - m->timenow, m->timenow); + RecreateNATMappings(m); + mDNS_Unlock(m); + } +} mDNSexport mDNSBool mDNSCoreReadyForSleep(mDNS *m, mDNSs32 now) - { - DNSQuestion *q; - AuthRecord *rr; - NetworkInterfaceInfo *intf; - - mDNS_Lock(m); - - if (m->DelaySleep) goto notready; - - // If we've not hit the sleep limit time, and it's not time for our next retry, we can skip these checks - if (m->SleepLimit - now > 0 && m->NextScheduledSPRetry - now > 0) goto notready; - - m->NextScheduledSPRetry = now + 0x40000000UL; - - // See if we might need to retransmit any lost Sleep Proxy Registrations - for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) - if (intf->NextSPSAttempt >= 0) - { - if (now - intf->NextSPSAttemptTime >= 0) - { - LogSPS("mDNSCoreReadyForSleep: retrying for %s SPS %d try %d", - intf->ifname, intf->NextSPSAttempt/3, intf->NextSPSAttempt); - SendSPSRegistration(m, intf, zeroID); - // Don't need to "goto notready" here, because if we do still have record registrations - // that have not been acknowledged yet, we'll catch that in the record list scan below. - } - else - if (m->NextScheduledSPRetry - intf->NextSPSAttemptTime > 0) - m->NextScheduledSPRetry = intf->NextSPSAttemptTime; - } - - // Scan list of interfaces, and see if we're still waiting for any sleep proxy resolves to complete - for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) - { - int sps = (intf->NextSPSAttempt == 0) ? 0 : (intf->NextSPSAttempt-1)/3; - if (intf->NetWakeResolve[sps].ThisQInterval >= 0) - { - LogSPS("mDNSCoreReadyForSleep: waiting for SPS Resolve %s %##s (%s)", - intf->ifname, intf->NetWakeResolve[sps].qname.c, DNSTypeName(intf->NetWakeResolve[sps].qtype)); - goto spsnotready; - } - } - - // Scan list of registered records - for (rr = m->ResourceRecords; rr; rr = rr->next) - if (!AuthRecord_uDNS(rr)) - if (!mDNSOpaque16IsZero(rr->updateid)) - { LogSPS("mDNSCoreReadyForSleep: waiting for SPS Update ID %d %s", mDNSVal16(rr->updateid), ARDisplayString(m,rr)); goto spsnotready; } - - // Scan list of private LLQs, and make sure they've all completed their handshake with the server - for (q = m->Questions; q; q = q->next) - if (!mDNSOpaque16IsZero(q->TargetQID) && q->LongLived && q->ReqLease == 0 && q->tcp) - { - LogSPS("mDNSCoreReadyForSleep: waiting for LLQ %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - goto notready; - } - - // Scan list of registered records - for (rr = m->ResourceRecords; rr; rr = rr->next) - if (AuthRecord_uDNS(rr)) - { - if (rr->state == regState_Refresh && rr->tcp) - { LogSPS("mDNSCoreReadyForSleep: waiting for Record Update ID %d %s", mDNSVal16(rr->updateid), ARDisplayString(m,rr)); goto notready; } - #if APPLE_OSX_mDNSResponder - if (!RecordReadyForSleep(m, rr)) { LogSPS("mDNSCoreReadyForSleep: waiting for %s", ARDisplayString(m, rr)); goto notready; } - #endif - } - - mDNS_Unlock(m); - return mDNStrue; +{ + DNSQuestion *q; + AuthRecord *rr; + NetworkInterfaceInfo *intf; + + mDNS_Lock(m); + + if (m->DelaySleep) goto notready; + + // If we've not hit the sleep limit time, and it's not time for our next retry, we can skip these checks + if (m->SleepLimit - now > 0 && m->NextScheduledSPRetry - now > 0) goto notready; + + m->NextScheduledSPRetry = now + 0x40000000UL; + + // See if we might need to retransmit any lost Sleep Proxy Registrations + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) + if (intf->NextSPSAttempt >= 0) + { + if (now - intf->NextSPSAttemptTime >= 0) + { + LogSPS("mDNSCoreReadyForSleep: retrying for %s SPS %d try %d", + intf->ifname, intf->NextSPSAttempt/3, intf->NextSPSAttempt); + SendSPSRegistration(m, intf, zeroID); + // Don't need to "goto notready" here, because if we do still have record registrations + // that have not been acknowledged yet, we'll catch that in the record list scan below. + } + else + if (m->NextScheduledSPRetry - intf->NextSPSAttemptTime > 0) + m->NextScheduledSPRetry = intf->NextSPSAttemptTime; + } + + // Scan list of interfaces, and see if we're still waiting for any sleep proxy resolves to complete + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) + { + int sps = (intf->NextSPSAttempt == 0) ? 0 : (intf->NextSPSAttempt-1)/3; + if (intf->NetWakeResolve[sps].ThisQInterval >= 0) + { + LogSPS("mDNSCoreReadyForSleep: waiting for SPS Resolve %s %##s (%s)", + intf->ifname, intf->NetWakeResolve[sps].qname.c, DNSTypeName(intf->NetWakeResolve[sps].qtype)); + goto spsnotready; + } + } + + // Scan list of registered records + for (rr = m->ResourceRecords; rr; rr = rr->next) + if (!AuthRecord_uDNS(rr)) + if (!mDNSOpaque64IsZero(&rr->updateIntID)) + { LogSPS("mDNSCoreReadyForSleep: waiting for SPS updateIntID 0x%x 0x%x (updateid %d) %s", rr->updateIntID.l[1], rr->updateIntID.l[0], mDNSVal16(rr->updateid), ARDisplayString(m,rr)); goto spsnotready; } + + // Scan list of private LLQs, and make sure they've all completed their handshake with the server + for (q = m->Questions; q; q = q->next) + if (!mDNSOpaque16IsZero(q->TargetQID) && q->LongLived && q->ReqLease == 0 && q->tcp) + { + LogSPS("mDNSCoreReadyForSleep: waiting for LLQ %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + goto notready; + } + + // Scan list of registered records + for (rr = m->ResourceRecords; rr; rr = rr->next) + if (AuthRecord_uDNS(rr)) + { + if (rr->state == regState_Refresh && rr->tcp) + { LogSPS("mDNSCoreReadyForSleep: waiting for Record updateIntID 0x%x 0x%x (updateid %d) %s", rr->updateIntID.l[1], rr->updateIntID.l[0], mDNSVal16(rr->updateid), ARDisplayString(m,rr)); goto notready; } + #if APPLE_OSX_mDNSResponder + if (!RecordReadyForSleep(m, rr)) { LogSPS("mDNSCoreReadyForSleep: waiting for %s", ARDisplayString(m, rr)); goto notready; } + #endif + } + + mDNS_Unlock(m); + return mDNStrue; spsnotready: - // If we failed to complete sleep proxy registration within ten seconds, we give up on that - // and allow up to ten seconds more to complete wide-area deregistration instead - if (now - m->SleepLimit >= 0) - { - LogMsg("Failed to register with SPS, now sending goodbyes"); - - for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) - if (intf->NetWakeBrowse.ThisQInterval >= 0) - { - LogSPS("ReadyForSleep mDNS_DeactivateNetWake %s %##s (%s)", - intf->ifname, intf->NetWakeResolve[0].qname.c, DNSTypeName(intf->NetWakeResolve[0].qtype)); - mDNS_DeactivateNetWake_internal(m, intf); - } - - for (rr = m->ResourceRecords; rr; rr = rr->next) - if (!AuthRecord_uDNS(rr)) - if (!mDNSOpaque16IsZero(rr->updateid)) - { - LogSPS("ReadyForSleep clearing updateid for %s", ARDisplayString(m, rr)); - rr->updateid = zeroID; - } - - // We'd really like to allow up to ten seconds more here, - // but if we don't respond to the sleep notification within 30 seconds - // we'll be put back to sleep forcibly without the chance to schedule the next maintenance wake. - // Right now we wait 16 sec after wake for all the interfaces to come up, then we wait up to 10 seconds - // more for SPS resolves and record registrations to complete, which puts us at 26 seconds. - // If we allow just one more second to send our goodbyes, that puts us at 27 seconds. - m->SleepLimit = now + mDNSPlatformOneSecond * 1; - - SendSleepGoodbyes(m); - } + // If we failed to complete sleep proxy registration within ten seconds, we give up on that + // and allow up to ten seconds more to complete wide-area deregistration instead + if (now - m->SleepLimit >= 0) + { + LogMsg("Failed to register with SPS, now sending goodbyes"); + + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) + if (intf->NetWakeBrowse.ThisQInterval >= 0) + { + LogSPS("ReadyForSleep mDNS_DeactivateNetWake %s %##s (%s)", + intf->ifname, intf->NetWakeResolve[0].qname.c, DNSTypeName(intf->NetWakeResolve[0].qtype)); + mDNS_DeactivateNetWake_internal(m, intf); + } + + for (rr = m->ResourceRecords; rr; rr = rr->next) + if (!AuthRecord_uDNS(rr)) + if (!mDNSOpaque64IsZero(&rr->updateIntID)) + { + LogSPS("ReadyForSleep clearing updateIntID 0x%x 0x%x (updateid %d) for %s", rr->updateIntID.l[1], rr->updateIntID.l[0], mDNSVal16(rr->updateid), ARDisplayString(m, rr)); + rr->updateIntID = zeroOpaque64; + } + + // We'd really like to allow up to ten seconds more here, + // but if we don't respond to the sleep notification within 30 seconds + // we'll be put back to sleep forcibly without the chance to schedule the next maintenance wake. + // Right now we wait 16 sec after wake for all the interfaces to come up, then we wait up to 10 seconds + // more for SPS resolves and record registrations to complete, which puts us at 26 seconds. + // If we allow just one more second to send our goodbyes, that puts us at 27 seconds. + m->SleepLimit = now + mDNSPlatformOneSecond * 1; + + SendSleepGoodbyes(m); + } notready: - mDNS_Unlock(m); - return mDNSfalse; - } + mDNS_Unlock(m); + return mDNSfalse; +} mDNSexport mDNSs32 mDNSCoreIntervalToNextWake(mDNS *const m, mDNSs32 now) - { - AuthRecord *ar; - - // Even when we have no wake-on-LAN-capable interfaces, or we failed to find a sleep proxy, or we have other - // failure scenarios, we still want to wake up in at most 120 minutes, to see if the network environment has changed. - // E.g. we might wake up and find no wireless network because the base station got rebooted just at that moment, - // and if that happens we don't want to just give up and go back to sleep and never try again. - mDNSs32 e = now + (120 * 60 * mDNSPlatformOneSecond); // Sleep for at most 120 minutes - - NATTraversalInfo *nat; - for (nat = m->NATTraversals; nat; nat=nat->next) - if (nat->Protocol && nat->ExpiryTime && nat->ExpiryTime - now > mDNSPlatformOneSecond*4) - { - mDNSs32 t = nat->ExpiryTime - (nat->ExpiryTime - now) / 10; // Wake up when 90% of the way to the expiry time - if (e - t > 0) e = t; - LogSPS("ComputeWakeTime: %p %s Int %5d Ext %5d Err %d Retry %5d Interval %5d Expire %5d Wake %5d", - nat, nat->Protocol == NATOp_MapTCP ? "TCP" : "UDP", - mDNSVal16(nat->IntPort), mDNSVal16(nat->ExternalPort), nat->Result, - nat->retryPortMap ? (nat->retryPortMap - now) / mDNSPlatformOneSecond : 0, - nat->retryInterval / mDNSPlatformOneSecond, - nat->ExpiryTime ? (nat->ExpiryTime - now) / mDNSPlatformOneSecond : 0, - (t - now) / mDNSPlatformOneSecond); - } - - // This loop checks both the time we need to renew wide-area registrations, - // and the time we need to renew Sleep Proxy registrations - for (ar = m->ResourceRecords; ar; ar = ar->next) - if (ar->expire && ar->expire - now > mDNSPlatformOneSecond*4) - { - mDNSs32 t = ar->expire - (ar->expire - now) / 10; // Wake up when 90% of the way to the expiry time - if (e - t > 0) e = t; - LogSPS("ComputeWakeTime: %p Int %7d Next %7d Expire %7d Wake %7d %s", - ar, ar->ThisAPInterval / mDNSPlatformOneSecond, - (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond, - ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0, - (t - now) / mDNSPlatformOneSecond, ARDisplayString(m, ar)); - } - - return(e - now); - } +{ + AuthRecord *ar; + + // Even when we have no wake-on-LAN-capable interfaces, or we failed to find a sleep proxy, or we have other + // failure scenarios, we still want to wake up in at most 120 minutes, to see if the network environment has changed. + // E.g. we might wake up and find no wireless network because the base station got rebooted just at that moment, + // and if that happens we don't want to just give up and go back to sleep and never try again. + mDNSs32 e = now + (120 * 60 * mDNSPlatformOneSecond); // Sleep for at most 120 minutes + + NATTraversalInfo *nat; + for (nat = m->NATTraversals; nat; nat=nat->next) + if (nat->Protocol && nat->ExpiryTime && nat->ExpiryTime - now > mDNSPlatformOneSecond*4) + { + mDNSs32 t = nat->ExpiryTime - (nat->ExpiryTime - now) / 10; // Wake up when 90% of the way to the expiry time + if (e - t > 0) e = t; + LogSPS("ComputeWakeTime: %p %s Int %5d Ext %5d Err %d Retry %5d Interval %5d Expire %5d Wake %5d", + nat, nat->Protocol == NATOp_MapTCP ? "TCP" : "UDP", + mDNSVal16(nat->IntPort), mDNSVal16(nat->ExternalPort), nat->Result, + nat->retryPortMap ? (nat->retryPortMap - now) / mDNSPlatformOneSecond : 0, + nat->retryInterval / mDNSPlatformOneSecond, + nat->ExpiryTime ? (nat->ExpiryTime - now) / mDNSPlatformOneSecond : 0, + (t - now) / mDNSPlatformOneSecond); + } + + // This loop checks both the time we need to renew wide-area registrations, + // and the time we need to renew Sleep Proxy registrations + for (ar = m->ResourceRecords; ar; ar = ar->next) + if (ar->expire && ar->expire - now > mDNSPlatformOneSecond*4) + { + mDNSs32 t = ar->expire - (ar->expire - now) / 10; // Wake up when 90% of the way to the expiry time + if (e - t > 0) e = t; + LogSPS("ComputeWakeTime: %p Int %7d Next %7d Expire %7d Wake %7d %s", + ar, ar->ThisAPInterval / mDNSPlatformOneSecond, + (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond, + ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0, + (t - now) / mDNSPlatformOneSecond, ARDisplayString(m, ar)); + } + + return(e - now); +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -5415,70 +5872,70 @@ mDNSexport mDNSs32 mDNSCoreIntervalToNextWake(mDNS *const m, mDNSs32 now) #define MustSendRecord(RR) ((RR)->NR_AnswerTo || (RR)->NR_AdditionalTo) mDNSlocal mDNSu8 *GenerateUnicastResponse(const DNSMessage *const query, const mDNSu8 *const end, - const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, DNSMessage *const response, AuthRecord *ResponseRecords) - { - mDNSu8 *responseptr = response->data; - const mDNSu8 *const limit = response->data + sizeof(response->data); - const mDNSu8 *ptr = query->data; - AuthRecord *rr; - mDNSu32 maxttl = 0x70000000; - int i; - - // Initialize the response fields so we can answer the questions - InitializeDNSMessage(&response->h, query->h.id, ResponseFlags); - - // *** - // *** 1. Write out the list of questions we are actually going to answer with this packet - // *** - if (LegacyQuery) - { - maxttl = kStaticCacheTTL; - for (i=0; ih.numQuestions; i++) // For each question... - { - DNSQuestion q; - ptr = getQuestion(query, ptr, end, InterfaceID, &q); // get the question... - if (!ptr) return(mDNSNULL); - - for (rr=ResponseRecords; rr; rr=rr->NextResponse) // and search our list of proposed answers - { - if (rr->NR_AnswerTo == ptr) // If we're going to generate a record answering this question - { // then put the question in the question section - responseptr = putQuestion(response, responseptr, limit, &q.qname, q.qtype, q.qclass); - if (!responseptr) { debugf("GenerateUnicastResponse: Ran out of space for questions!"); return(mDNSNULL); } - break; // break out of the ResponseRecords loop, and go on to the next question - } - } - } - - if (response->h.numQuestions == 0) { LogMsg("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL); } - } - - // *** - // *** 2. Write Answers - // *** - for (rr=ResponseRecords; rr; rr=rr->NextResponse) - if (rr->NR_AnswerTo) - { - mDNSu8 *p = PutResourceRecordTTL(response, responseptr, &response->h.numAnswers, &rr->resrec, - maxttl < rr->resrec.rroriginalttl ? maxttl : rr->resrec.rroriginalttl); - if (p) responseptr = p; - else { debugf("GenerateUnicastResponse: Ran out of space for answers!"); response->h.flags.b[0] |= kDNSFlag0_TC; } - } - - // *** - // *** 3. Write Additionals - // *** - for (rr=ResponseRecords; rr; rr=rr->NextResponse) - if (rr->NR_AdditionalTo && !rr->NR_AnswerTo) - { - mDNSu8 *p = PutResourceRecordTTL(response, responseptr, &response->h.numAdditionals, &rr->resrec, - maxttl < rr->resrec.rroriginalttl ? maxttl : rr->resrec.rroriginalttl); - if (p) responseptr = p; - else debugf("GenerateUnicastResponse: No more space for additionals"); - } - - return(responseptr); - } + const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, DNSMessage *const response, AuthRecord *ResponseRecords) +{ + mDNSu8 *responseptr = response->data; + const mDNSu8 *const limit = response->data + sizeof(response->data); + const mDNSu8 *ptr = query->data; + AuthRecord *rr; + mDNSu32 maxttl = 0x70000000; + int i; + + // Initialize the response fields so we can answer the questions + InitializeDNSMessage(&response->h, query->h.id, ResponseFlags); + + // *** + // *** 1. Write out the list of questions we are actually going to answer with this packet + // *** + if (LegacyQuery) + { + maxttl = kStaticCacheTTL; + for (i=0; ih.numQuestions; i++) // For each question... + { + DNSQuestion q; + ptr = getQuestion(query, ptr, end, InterfaceID, &q); // get the question... + if (!ptr) return(mDNSNULL); + + for (rr=ResponseRecords; rr; rr=rr->NextResponse) // and search our list of proposed answers + { + if (rr->NR_AnswerTo == ptr) // If we're going to generate a record answering this question + { // then put the question in the question section + responseptr = putQuestion(response, responseptr, limit, &q.qname, q.qtype, q.qclass); + if (!responseptr) { debugf("GenerateUnicastResponse: Ran out of space for questions!"); return(mDNSNULL); } + break; // break out of the ResponseRecords loop, and go on to the next question + } + } + } + + if (response->h.numQuestions == 0) { LogMsg("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL); } + } + + // *** + // *** 2. Write Answers + // *** + for (rr=ResponseRecords; rr; rr=rr->NextResponse) + if (rr->NR_AnswerTo) + { + mDNSu8 *p = PutResourceRecordTTL(response, responseptr, &response->h.numAnswers, &rr->resrec, + maxttl < rr->resrec.rroriginalttl ? maxttl : rr->resrec.rroriginalttl); + if (p) responseptr = p; + else { debugf("GenerateUnicastResponse: Ran out of space for answers!"); response->h.flags.b[0] |= kDNSFlag0_TC; } + } + + // *** + // *** 3. Write Additionals + // *** + for (rr=ResponseRecords; rr; rr=rr->NextResponse) + if (rr->NR_AdditionalTo && !rr->NR_AnswerTo) + { + mDNSu8 *p = PutResourceRecordTTL(response, responseptr, &response->h.numAdditionals, &rr->resrec, + maxttl < rr->resrec.rroriginalttl ? maxttl : rr->resrec.rroriginalttl); + if (p) responseptr = p; + else debugf("GenerateUnicastResponse: No more space for additionals"); + } + + return(responseptr); +} // AuthRecord *our is our Resource Record // CacheRecord *pkt is the Resource Record from the response packet we've witnessed on the network @@ -5486,25 +5943,25 @@ mDNSlocal mDNSu8 *GenerateUnicastResponse(const DNSMessage *const query, const m // Returns +1 if there was a conflict and we won // Returns -1 if there was a conflict and we lost and have to rename mDNSlocal int CompareRData(const AuthRecord *const our, const CacheRecord *const pkt) - { - mDNSu8 ourdata[256], *ourptr = ourdata, *ourend; - mDNSu8 pktdata[256], *pktptr = pktdata, *pktend; - if (!our) { LogMsg("CompareRData ERROR: our is NULL"); return(+1); } - if (!pkt) { LogMsg("CompareRData ERROR: pkt is NULL"); return(+1); } - - ourend = putRData(mDNSNULL, ourdata, ourdata + sizeof(ourdata), &our->resrec); - pktend = putRData(mDNSNULL, pktdata, pktdata + sizeof(pktdata), &pkt->resrec); - while (ourptr < ourend && pktptr < pktend && *ourptr == *pktptr) { ourptr++; pktptr++; } - if (ourptr >= ourend && pktptr >= pktend) return(0); // If data identical, not a conflict - - if (ourptr >= ourend) return(-1); // Our data ran out first; We lost - if (pktptr >= pktend) return(+1); // Packet data ran out first; We won - if (*pktptr > *ourptr) return(-1); // Our data is numerically lower; We lost - if (*pktptr < *ourptr) return(+1); // Packet data is numerically lower; We won - - LogMsg("CompareRData ERROR: Invalid state"); - return(-1); - } +{ + mDNSu8 ourdata[256], *ourptr = ourdata, *ourend; + mDNSu8 pktdata[256], *pktptr = pktdata, *pktend; + if (!our) { LogMsg("CompareRData ERROR: our is NULL"); return(+1); } + if (!pkt) { LogMsg("CompareRData ERROR: pkt is NULL"); return(+1); } + + ourend = putRData(mDNSNULL, ourdata, ourdata + sizeof(ourdata), &our->resrec); + pktend = putRData(mDNSNULL, pktdata, pktdata + sizeof(pktdata), &pkt->resrec); + while (ourptr < ourend && pktptr < pktend && *ourptr == *pktptr) { ourptr++; pktptr++; } + if (ourptr >= ourend && pktptr >= pktend) return(0); // If data identical, not a conflict + + if (ourptr >= ourend) return(-1); // Our data ran out first; We lost + if (pktptr >= pktend) return(+1); // Packet data ran out first; We won + if (*pktptr > *ourptr) return(-1); // Our data is numerically lower; We lost + if (*pktptr < *ourptr) return(+1); // Packet data is numerically lower; We won + + LogMsg("CompareRData ERROR: Invalid state"); + return(-1); +} // See if we have an authoritative record that's identical to this packet record, // whose canonical DependentOn record is the specified master record. @@ -5516,46 +5973,46 @@ mDNSlocal int CompareRData(const AuthRecord *const our, const CacheRecord *const // If the record has no DependentOn, then just return that record's pointer // Returns NULL if we don't have any local RRs that are identical to the one from the packet mDNSlocal mDNSBool MatchDependentOn(const mDNS *const m, const CacheRecord *const pktrr, const AuthRecord *const master) - { - const AuthRecord *r1; - for (r1 = m->ResourceRecords; r1; r1=r1->next) - { - if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec)) - { - const AuthRecord *r2 = r1; - while (r2->DependentOn) r2 = r2->DependentOn; - if (r2 == master) return(mDNStrue); - } - } - for (r1 = m->DuplicateRecords; r1; r1=r1->next) - { - if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec)) - { - const AuthRecord *r2 = r1; - while (r2->DependentOn) r2 = r2->DependentOn; - if (r2 == master) return(mDNStrue); - } - } - return(mDNSfalse); - } +{ + const AuthRecord *r1; + for (r1 = m->ResourceRecords; r1; r1=r1->next) + { + if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec)) + { + const AuthRecord *r2 = r1; + while (r2->DependentOn) r2 = r2->DependentOn; + if (r2 == master) return(mDNStrue); + } + } + for (r1 = m->DuplicateRecords; r1; r1=r1->next) + { + if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec)) + { + const AuthRecord *r2 = r1; + while (r2->DependentOn) r2 = r2->DependentOn; + if (r2 == master) return(mDNStrue); + } + } + return(mDNSfalse); +} // Find the canonical RRSet pointer for this RR received in a packet. // If we find any identical AuthRecord in our authoritative list, then follow its RRSet // pointers (if any) to make sure we return the canonical member of this name/type/class // Returns NULL if we don't have any local RRs that are identical to the one from the packet mDNSlocal const AuthRecord *FindRRSet(const mDNS *const m, const CacheRecord *const pktrr) - { - const AuthRecord *rr; - for (rr = m->ResourceRecords; rr; rr=rr->next) - { - if (IdenticalResourceRecord(&rr->resrec, &pktrr->resrec)) - { - while (rr->RRSet && rr != rr->RRSet) rr = rr->RRSet; - return(rr); - } - } - return(mDNSNULL); - } +{ + const AuthRecord *rr; + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if (IdenticalResourceRecord(&rr->resrec, &pktrr->resrec)) + { + while (rr->RRSet && rr != rr->RRSet) rr = rr->RRSet; + return(rr); + } + } + return(mDNSNULL); +} // PacketRRConflict is called when we've received an RR (pktrr) which has the same name // as one of our records (our) but different rdata. @@ -5567,898 +6024,1222 @@ mDNSlocal const AuthRecord *FindRRSet(const mDNS *const m, const CacheRecord *co // 3. If we have some *other* RR that exactly matches the one from the packet, and that record and our record // are members of the same RRSet, then this is not a conflict. mDNSlocal mDNSBool PacketRRConflict(const mDNS *const m, const AuthRecord *const our, const CacheRecord *const pktrr) - { - // If not supposed to be unique, not a conflict - if (!(our->resrec.RecordType & kDNSRecordTypeUniqueMask)) return(mDNSfalse); - - // If a dependent record, not a conflict - if (our->DependentOn || MatchDependentOn(m, pktrr, our)) return(mDNSfalse); - else - { - // If the pktrr matches a member of ourset, not a conflict - const AuthRecord *ourset = our->RRSet ? our->RRSet : our; - const AuthRecord *pktset = FindRRSet(m, pktrr); - if (pktset == ourset) return(mDNSfalse); - - // For records we're proxying, where we don't know the full - // relationship between the records, having any matching record - // in our AuthRecords list is sufficient evidence of non-conflict - if (our->WakeUp.HMAC.l[0] && pktset) return(mDNSfalse); - } - - // Okay, this is a conflict - return(mDNStrue); - } +{ + // If not supposed to be unique, not a conflict + if (!(our->resrec.RecordType & kDNSRecordTypeUniqueMask)) return(mDNSfalse); + + // If a dependent record, not a conflict + if (our->DependentOn || MatchDependentOn(m, pktrr, our)) return(mDNSfalse); + else + { + // If the pktrr matches a member of ourset, not a conflict + const AuthRecord *ourset = our->RRSet ? our->RRSet : our; + const AuthRecord *pktset = FindRRSet(m, pktrr); + if (pktset == ourset) return(mDNSfalse); + + // For records we're proxying, where we don't know the full + // relationship between the records, having any matching record + // in our AuthRecords list is sufficient evidence of non-conflict + if (our->WakeUp.HMAC.l[0] && pktset) return(mDNSfalse); + } + + // Okay, this is a conflict + return(mDNStrue); +} // Note: ResolveSimultaneousProbe calls mDNS_Deregister_internal which can call a user callback, which may change // the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end, - DNSQuestion *q, AuthRecord *our) - { - int i; - const mDNSu8 *ptr = LocateAuthorities(query, end); - mDNSBool FoundUpdate = mDNSfalse; - - for (i = 0; i < query->h.numAuthorities; i++) - { - ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, &m->rec); - if (!ptr) break; - if (m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && ResourceRecordAnswersQuestion(&m->rec.r.resrec, q)) - { - FoundUpdate = mDNStrue; - if (PacketRRConflict(m, our, &m->rec.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); - if (result) - { - const char *const msg = (result < 0) ? "lost:" : (result > 0) ? "won: " : "tie: "; - LogMsg("ResolveSimultaneousProbe: %p Pkt Record: %08lX %s", q->InterfaceID, m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r)); - LogMsg("ResolveSimultaneousProbe: %p Our Record %d %s %08lX %s", our->resrec.InterfaceID, our->ProbeCount, msg, our->resrec.rdatahash, ARDisplayString(m, our)); - } - // If we lost the tie-break for simultaneous probes, we don't immediately give up, because we might be seeing stale packets on the network. - // Instead we pause for one second, to give the other host (if real) a chance to establish its name, and then try probing again. - // If there really is another live host out there with the same name, it will answer our probes and we'll then rename. - if (result < 0) - { - m->SuppressProbes = NonZeroTime(m->timenow + mDNSPlatformOneSecond); - our->ProbeCount = DefaultProbeCountForTypeUnique; - our->AnnounceCount = InitialAnnounceCount; - InitializeLastAPTime(m, our); - goto exit; - } - } + DNSQuestion *q, AuthRecord *our) +{ + int i; + const mDNSu8 *ptr = LocateAuthorities(query, end); + mDNSBool FoundUpdate = mDNSfalse; + + for (i = 0; i < query->h.numAuthorities; i++) + { + ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, &m->rec); + if (!ptr) break; + if (m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && ResourceRecordAnswersQuestion(&m->rec.r.resrec, q)) + { + FoundUpdate = mDNStrue; + if (PacketRRConflict(m, our, &m->rec.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); + if (result) + { + const char *const msg = (result < 0) ? "lost:" : (result > 0) ? "won: " : "tie: "; + LogMsg("ResolveSimultaneousProbe: %p Pkt Record: %08lX %s", q->InterfaceID, m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r)); + LogMsg("ResolveSimultaneousProbe: %p Our Record %d %s %08lX %s", our->resrec.InterfaceID, our->ProbeCount, msg, our->resrec.rdatahash, ARDisplayString(m, our)); + } + // If we lost the tie-break for simultaneous probes, we don't immediately give up, because we might be seeing stale packets on the network. + // Instead we pause for one second, to give the other host (if real) a chance to establish its name, and then try probing again. + // If there really is another live host out there with the same name, it will answer our probes and we'll then rename. + if (result < 0) + { + m->SuppressProbes = NonZeroTime(m->timenow + mDNSPlatformOneSecond); + our->ProbeCount = DefaultProbeCountForTypeUnique; + our->AnnounceCount = InitialAnnounceCount; + InitializeLastAPTime(m, our); + goto exit; + } + } #if 0 - else - { - LogMsg("ResolveSimultaneousProbe: %p Pkt Record: %08lX %s", q->InterfaceID, m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r)); - LogMsg("ResolveSimultaneousProbe: %p Our Record %d ign: %08lX %s", our->resrec.InterfaceID, our->ProbeCount, our->resrec.rdatahash, ARDisplayString(m, our)); - } + else + { + LogMsg("ResolveSimultaneousProbe: %p Pkt Record: %08lX %s", q->InterfaceID, m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r)); + LogMsg("ResolveSimultaneousProbe: %p Our Record %d ign: %08lX %s", our->resrec.InterfaceID, our->ProbeCount, our->resrec.rdatahash, ARDisplayString(m, our)); + } #endif - } - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - } - if (!FoundUpdate) - LogInfo("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our->resrec.name->c, DNSTypeName(our->resrec.rrtype)); + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + if (!FoundUpdate) + LogInfo("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 - } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it +} mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, const ResourceRecord *const pktrr) - { - mDNSu32 slot = HashSlot(pktrr->name); - CacheGroup *cg = CacheGroupForRecord(m, slot, pktrr); - CacheRecord *rr; - mDNSBool match; - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) - { - match = !pktrr->InterfaceID ? pktrr->rDNSServer == rr->resrec.rDNSServer : pktrr->InterfaceID == rr->resrec.InterfaceID; - if (match && IdenticalSameNameRecord(pktrr, &rr->resrec)) break; - } - return(rr); - } +{ + mDNSu32 slot = HashSlot(pktrr->name); + CacheGroup *cg = CacheGroupForRecord(m, slot, pktrr); + CacheRecord *rr; + mDNSBool match; + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + { + if (!pktrr->InterfaceID) + { + mDNSu16 id1 = (pktrr->rDNSServer ? pktrr->rDNSServer->resGroupID : 0); + mDNSu16 id2 = (rr->resrec.rDNSServer ? rr->resrec.rDNSServer->resGroupID : 0); + match = (id1 == id2); + } + else match = (pktrr->InterfaceID == rr->resrec.InterfaceID); + + if (match && IdenticalSameNameRecord(pktrr, &rr->resrec)) break; + } + return(rr); +} // Called from mDNSCoreReceiveUpdate when we get a sleep proxy registration request, // to check our lists and discard any stale duplicates of this record we already have mDNSlocal void ClearIdenticalProxyRecords(mDNS *const m, const OwnerOptData *const owner, AuthRecord *const thelist) - { - if (m->CurrentRecord) - LogMsg("ClearIdenticalProxyRecords ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); - m->CurrentRecord = thelist; - while (m->CurrentRecord) - { - AuthRecord *const rr = m->CurrentRecord; - if (m->rec.r.resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&owner->HMAC, &rr->WakeUp.HMAC)) - if (IdenticalResourceRecord(&rr->resrec, &m->rec.r.resrec)) - { - LogSPS("ClearIdenticalProxyRecords: Removing %3d H-MAC %.6a I-MAC %.6a %d %d %s", - m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, owner->seq, ARDisplayString(m, rr)); - rr->WakeUp.HMAC = zeroEthAddr; // Clear HMAC so that mDNS_Deregister_internal doesn't waste packets trying to wake this host - rr->RequireGoodbye = mDNSfalse; // and we don't want to send goodbye for it - mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); - SetSPSProxyListChanged(m->rec.r.resrec.InterfaceID); - } - // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because - // new records could have been added to the end of the list as a result of that call. - if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now - m->CurrentRecord = rr->next; - } - } +{ + if (m->CurrentRecord) + LogMsg("ClearIdenticalProxyRecords ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = thelist; + while (m->CurrentRecord) + { + AuthRecord *const rr = m->CurrentRecord; + if (m->rec.r.resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&owner->HMAC, &rr->WakeUp.HMAC)) + // Normally, the RDATA of the keepalive record will be different each time and hence we always + // clean up the keepalive record. + if (mDNS_KeepaliveRecord(&rr->resrec) || IdenticalResourceRecord(&rr->resrec, &m->rec.r.resrec)) + { + LogSPS("ClearIdenticalProxyRecords: Removing %3d H-MAC %.6a I-MAC %.6a %d %d %s", + m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, owner->seq, ARDisplayString(m, rr)); + rr->WakeUp.HMAC = zeroEthAddr; // Clear HMAC so that mDNS_Deregister_internal doesn't waste packets trying to wake this host + rr->RequireGoodbye = mDNSfalse; // and we don't want to send goodbye for it + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); + SetSPSProxyListChanged(m->rec.r.resrec.InterfaceID); + } + // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because + // new records could have been added to the end of the list as a result of that call. + if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now + m->CurrentRecord = rr->next; + } +} // Called from ProcessQuery when we get an mDNS packet with an owner record in it mDNSlocal void ClearProxyRecords(mDNS *const m, const OwnerOptData *const owner, AuthRecord *const thelist) - { - if (m->CurrentRecord) - LogMsg("ClearProxyRecords ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); - m->CurrentRecord = thelist; - while (m->CurrentRecord) - { - AuthRecord *const rr = m->CurrentRecord; - if (m->rec.r.resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&owner->HMAC, &rr->WakeUp.HMAC)) - if (owner->seq != rr->WakeUp.seq || m->timenow - rr->TimeRcvd > mDNSPlatformOneSecond * 60) - { - if (rr->AddressProxy.type == mDNSAddrType_IPv6) - { - // We don't do this here because we know that the host is waking up at this point, so we don't send - // Unsolicited Neighbor Advertisements -- even Neighbor Advertisements agreeing with what the host should be - // saying itself -- because it can cause some IPv6 stacks to falsely conclude that there's an address conflict. - #if MDNS_USE_Unsolicited_Neighbor_Advertisements - LogSPS("NDP Announcement -- Releasing traffic for H-MAC %.6a I-MAC %.6a %s", - &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr)); - SendNDP(m, NDP_Adv, NDP_Override, rr, &rr->AddressProxy.ip.v6, &rr->WakeUp.IMAC, &AllHosts_v6, &AllHosts_v6_Eth); - #endif - } - LogSPS("ClearProxyRecords: Removing %3d AC %2d %02X H-MAC %.6a I-MAC %.6a %d %d %s", - m->ProxyRecords, rr->AnnounceCount, rr->resrec.RecordType, - &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, owner->seq, ARDisplayString(m, rr)); - if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) rr->resrec.RecordType = kDNSRecordTypeShared; - rr->WakeUp.HMAC = zeroEthAddr; // Clear HMAC so that mDNS_Deregister_internal doesn't waste packets trying to wake this host - rr->RequireGoodbye = mDNSfalse; // and we don't want to send goodbye for it, since real host is now back and functional - mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); - SetSPSProxyListChanged(m->rec.r.resrec.InterfaceID); - } - // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because - // new records could have been added to the end of the list as a result of that call. - if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now - m->CurrentRecord = rr->next; - } - } +{ + if (m->CurrentRecord) + LogMsg("ClearProxyRecords ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = thelist; + while (m->CurrentRecord) + { + AuthRecord *const rr = m->CurrentRecord; + if (m->rec.r.resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&owner->HMAC, &rr->WakeUp.HMAC)) + if (owner->seq != rr->WakeUp.seq || m->timenow - rr->TimeRcvd > mDNSPlatformOneSecond * 60) + { + if (rr->AddressProxy.type == mDNSAddrType_IPv6) + { + // We don't do this here because we know that the host is waking up at this point, so we don't send + // Unsolicited Neighbor Advertisements -- even Neighbor Advertisements agreeing with what the host should be + // saying itself -- because it can cause some IPv6 stacks to falsely conclude that there's an address conflict. + #if MDNS_USE_Unsolicited_Neighbor_Advertisements + LogSPS("NDP Announcement -- Releasing traffic for H-MAC %.6a I-MAC %.6a %s", + &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr)); + SendNDP(m, NDP_Adv, NDP_Override, rr, &rr->AddressProxy.ip.v6, &rr->WakeUp.IMAC, &AllHosts_v6, &AllHosts_v6_Eth); + #endif + } + LogSPS("ClearProxyRecords: Removing %3d AC %2d %02X H-MAC %.6a I-MAC %.6a %d %d %s", + m->ProxyRecords, rr->AnnounceCount, rr->resrec.RecordType, + &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, owner->seq, ARDisplayString(m, rr)); + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) rr->resrec.RecordType = kDNSRecordTypeShared; + rr->WakeUp.HMAC = zeroEthAddr; // Clear HMAC so that mDNS_Deregister_internal doesn't waste packets trying to wake this host + rr->RequireGoodbye = mDNSfalse; // and we don't want to send goodbye for it, since real host is now back and functional + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); + SetSPSProxyListChanged(m->rec.r.resrec.InterfaceID); + } + // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because + // new records could have been added to the end of the list as a result of that call. + if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now + m->CurrentRecord = rr->next; + } +} // 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, - mDNSBool QueryWasLocalUnicast, DNSMessage *const response) - { - mDNSBool FromLocalSubnet = srcaddr && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); - 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; - mDNSu8 *responseptr = mDNSNULL; - AuthRecord *rr; - int i; - - // *** - // *** 1. Look in Additional Section for an OPT record - // *** - ptr = LocateOptRR(query, end, DNSOpt_OwnerData_ID_Space); - if (ptr) - { - ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAdd, &m->rec); - if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_OPT) - { - const rdataOPT *opt; - const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength]; - // Find owner sub-option(s). We verify that the MAC is non-zero, otherwise we could inadvertently - // delete all our own AuthRecords (which are identified by having zero MAC tags on them). - for (opt = &m->rec.r.resrec.rdata->u.opt[0]; opt < e; opt++) - if (opt->opt == kDNSOpt_Owner && opt->u.owner.vers == 0 && opt->u.owner.HMAC.l[0]) - { - ClearProxyRecords(m, &opt->u.owner, m->DuplicateRecords); - ClearProxyRecords(m, &opt->u.owner, m->ResourceRecords); - } - } - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - } - - // *** - // *** 2. Parse Question Section and mark potential answers - // *** - ptr = query->data; - for (i=0; ih.numQuestions; i++) // For each question... - { - mDNSBool QuestionNeedsMulticastResponse; - int NumAnswersForThisQuestion = 0; - AuthRecord *NSECAnswer = mDNSNULL; - DNSQuestion pktq, *q; - ptr = getQuestion(query, ptr, end, InterfaceID, &pktq); // get the question... - if (!ptr) goto exit; - - // The only queries that *need* a multicast response are: - // * Queries sent via multicast - // * from port 5353 - // * that don't have the kDNSQClass_UnicastResponse bit set - // These queries need multicast responses because other clients will: - // * suppress their own identical questions when they see these questions, and - // * expire their cache records if they don't see the expected responses - // For other queries, we may still choose to send the occasional multicast response anyway, - // to keep our neighbours caches warm, and for ongoing conflict detection. - QuestionNeedsMulticastResponse = QueryWasMulticast && !LegacyQuery && !(pktq.qclass & kDNSQClass_UnicastResponse); - // Clear the UnicastResponse flag -- don't want to confuse the rest of the code that follows later - pktq.qclass &= ~kDNSQClass_UnicastResponse; - - // Note: We use the m->CurrentRecord mechanism here because calling ResolveSimultaneousProbe - // can result in user callbacks which may change the record list and/or question list. - // Also note: we just mark potential answer records here, without trying to build the - // "ResponseRecords" list, because we don't want to risk user callbacks deleting records - // from that list while we're in the middle of trying to build it. - if (m->CurrentRecord) - LogMsg("ProcessQuery ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); - m->CurrentRecord = m->ResourceRecords; - while (m->CurrentRecord) - { - rr = m->CurrentRecord; - m->CurrentRecord = rr->next; - if (AnyTypeRecordAnswersQuestion(&rr->resrec, &pktq) && (QueryWasMulticast || QueryWasLocalUnicast || rr->AllowRemoteQuery)) - { - if (RRTypeAnswersQuestionType(&rr->resrec, pktq.qtype)) - { - if (rr->resrec.RecordType == kDNSRecordTypeUnique) - ResolveSimultaneousProbe(m, query, end, &pktq, rr); - else if (ResourceRecordIsValidAnswer(rr)) - { - NumAnswersForThisQuestion++; - // Note: We should check here if this is a probe-type query, and if so, generate an immediate - // unicast answer back to the source, because timeliness in answering probes is important. - - // Notes: - // NR_AnswerTo pointing into query packet means "answer via immediate legacy unicast" (may *also* choose to multicast) - // NR_AnswerTo == (mDNSu8*)~1 means "answer via delayed unicast" (to modern querier; may promote to multicast instead) - // NR_AnswerTo == (mDNSu8*)~0 means "definitely answer via multicast" (can't downgrade to unicast later) - // If we're not multicasting this record because the kDNSQClass_UnicastResponse bit was set, - // but the multicast querier is not on a matching subnet (e.g. because of overlaid subnets on one link) - // then we'll multicast it anyway (if we unicast, the receiver will ignore it because it has an apparently non-local source) - if (QuestionNeedsMulticastResponse || (!FromLocalSubnet && QueryWasMulticast && !LegacyQuery)) - { - // We only mark this question for sending if it is at least one second since the last time we multicast it - // on this interface. If it is more than a second, or LastMCInterface is different, then we may multicast it. - // This is to guard against the case where someone blasts us with queries as fast as they can. - if (m->timenow - (rr->LastMCTime + mDNSPlatformOneSecond) >= 0 || - (rr->LastMCInterface != mDNSInterfaceMark && rr->LastMCInterface != InterfaceID)) - rr->NR_AnswerTo = (mDNSu8*)~0; - } - else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = LegacyQuery ? ptr : (mDNSu8*)~1; - } - } - else if ((rr->resrec.RecordType & kDNSRecordTypeActiveUniqueMask) && ResourceRecordIsValidAnswer(rr)) - { - // If we don't have any answers for this question, but we do own another record with the same name, - // then we'll want to mark it to generate an NSEC record on this interface - if (!NSECAnswer) NSECAnswer = rr; - } - } - } - - if (NumAnswersForThisQuestion == 0 && NSECAnswer) - { - NumAnswersForThisQuestion++; - NSECAnswer->SendNSECNow = InterfaceID; - m->NextScheduledResponse = m->timenow; - } - - // 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 + const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast, + mDNSBool QueryWasLocalUnicast, DNSMessage *const response) +{ + mDNSBool FromLocalSubnet = srcaddr && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); + 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; + mDNSu8 *responseptr = mDNSNULL; + AuthRecord *rr; + int i; + + // *** + // *** 1. Look in Additional Section for an OPT record + // *** + ptr = LocateOptRR(query, end, DNSOpt_OwnerData_ID_Space); + if (ptr) + { + ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAdd, &m->rec); + if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_OPT) + { + const rdataOPT *opt; + const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength]; + // Find owner sub-option(s). We verify that the MAC is non-zero, otherwise we could inadvertently + // delete all our own AuthRecords (which are identified by having zero MAC tags on them). + for (opt = &m->rec.r.resrec.rdata->u.opt[0]; opt < e; opt++) + if (opt->opt == kDNSOpt_Owner && opt->u.owner.vers == 0 && opt->u.owner.HMAC.l[0]) + { + ClearProxyRecords(m, &opt->u.owner, m->DuplicateRecords); + ClearProxyRecords(m, &opt->u.owner, m->ResourceRecords); + } + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + + // *** + // *** 2. Parse Question Section and mark potential answers + // *** + ptr = query->data; + for (i=0; ih.numQuestions; i++) // For each question... + { + mDNSBool QuestionNeedsMulticastResponse; + int NumAnswersForThisQuestion = 0; + AuthRecord *NSECAnswer = mDNSNULL; + DNSQuestion pktq, *q; + ptr = getQuestion(query, ptr, end, InterfaceID, &pktq); // get the question... + if (!ptr) goto exit; + + // The only queries that *need* a multicast response are: + // * Queries sent via multicast + // * from port 5353 + // * that don't have the kDNSQClass_UnicastResponse bit set + // These queries need multicast responses because other clients will: + // * suppress their own identical questions when they see these questions, and + // * expire their cache records if they don't see the expected responses + // For other queries, we may still choose to send the occasional multicast response anyway, + // to keep our neighbours caches warm, and for ongoing conflict detection. + QuestionNeedsMulticastResponse = QueryWasMulticast && !LegacyQuery && !(pktq.qclass & kDNSQClass_UnicastResponse); + // Clear the UnicastResponse flag -- don't want to confuse the rest of the code that follows later + pktq.qclass &= ~kDNSQClass_UnicastResponse; + + // Note: We use the m->CurrentRecord mechanism here because calling ResolveSimultaneousProbe + // can result in user callbacks which may change the record list and/or question list. + // Also note: we just mark potential answer records here, without trying to build the + // "ResponseRecords" list, because we don't want to risk user callbacks deleting records + // from that list while we're in the middle of trying to build it. + if (m->CurrentRecord) + LogMsg("ProcessQuery ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + if (AnyTypeRecordAnswersQuestion(&rr->resrec, &pktq) && (QueryWasMulticast || QueryWasLocalUnicast || rr->AllowRemoteQuery)) + { + if (RRTypeAnswersQuestionType(&rr->resrec, pktq.qtype)) + { + if (rr->resrec.RecordType == kDNSRecordTypeUnique) + ResolveSimultaneousProbe(m, query, end, &pktq, rr); + else if (ResourceRecordIsValidAnswer(rr)) + { + NumAnswersForThisQuestion++; + // Note: We should check here if this is a probe-type query, and if so, generate an immediate + // unicast answer back to the source, because timeliness in answering probes is important. + + // Notes: + // NR_AnswerTo pointing into query packet means "answer via immediate legacy unicast" (may *also* choose to multicast) + // NR_AnswerTo == (mDNSu8*)~1 means "answer via delayed unicast" (to modern querier; may promote to multicast instead) + // NR_AnswerTo == (mDNSu8*)~0 means "definitely answer via multicast" (can't downgrade to unicast later) + // If we're not multicasting this record because the kDNSQClass_UnicastResponse bit was set, + // but the multicast querier is not on a matching subnet (e.g. because of overlaid subnets on one link) + // then we'll multicast it anyway (if we unicast, the receiver will ignore it because it has an apparently non-local source) + if (QuestionNeedsMulticastResponse || (!FromLocalSubnet && QueryWasMulticast && !LegacyQuery)) + { + // We only mark this question for sending if it is at least one second since the last time we multicast it + // on this interface. If it is more than a second, or LastMCInterface is different, then we may multicast it. + // This is to guard against the case where someone blasts us with queries as fast as they can. + if (m->timenow - (rr->LastMCTime + mDNSPlatformOneSecond) >= 0 || + (rr->LastMCInterface != mDNSInterfaceMark && rr->LastMCInterface != InterfaceID)) + rr->NR_AnswerTo = (mDNSu8*)~0; + } + else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = LegacyQuery ? ptr : (mDNSu8*)~1; + } + } + else if ((rr->resrec.RecordType & kDNSRecordTypeActiveUniqueMask) && ResourceRecordIsValidAnswer(rr)) + { + // If we don't have any answers for this question, but we do own another record with the same name, + // then we'll want to mark it to generate an NSEC record on this interface + if (!NSECAnswer) NSECAnswer = rr; + } + } + } + + if (NumAnswersForThisQuestion == 0 && NSECAnswer) + { + NumAnswersForThisQuestion++; + NSECAnswer->SendNSECNow = InterfaceID; + m->NextScheduledResponse = m->timenow; + } + + // 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 #if ENABLE_MULTI_PACKET_QUERY_SNOOPING - if (QuestionNeedsMulticastResponse) + if (QuestionNeedsMulticastResponse) #else - // We only do the following accelerated cache expiration and duplicate question suppression processing - // for non-truncated 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. - // For truncated queries we don't do this because a response we're expecting might be suppressed by a subsequent - // known-answer packet, and when there's packet loss we can't safely assume we'll receive *all* known-answer packets. - if (QuestionNeedsMulticastResponse && !(query->h.flags.b[0] & kDNSFlag0_TC)) + // We only do the following accelerated cache expiration and duplicate question suppression processing + // for non-truncated 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. + // For truncated queries we don't do this because a response we're expecting might be suppressed by a subsequent + // known-answer packet, and when there's packet loss we can't safely assume we'll receive *all* known-answer packets. + if (QuestionNeedsMulticastResponse && !(query->h.flags.b[0] & kDNSFlag0_TC)) #endif - { - const mDNSu32 slot = HashSlot(&pktq.qname); - CacheGroup *cg = CacheGroupForName(m, slot, pktq.qnamehash, &pktq.qname); - CacheRecord *cr; + { + const mDNSu32 slot = HashSlot(&pktq.qname); + CacheGroup *cg = CacheGroupForName(m, slot, pktq.qnamehash, &pktq.qname); + CacheRecord *cr; - // 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 + // 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 #if ENABLE_MULTI_PACKET_QUERY_SNOOPING - if (!(query->h.flags.b[0] & kDNSFlag0_TC)) + if (!(query->h.flags.b[0] & kDNSFlag0_TC)) #endif - for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) - if (SameNameRecordAnswersQuestion(&cr->resrec, &pktq) && cr->resrec.rdlength <= SmallRecordLimit) - if (!cr->NextInKAList && eap != &cr->NextInKAList) - { - *eap = cr; - eap = &cr->NextInKAList; + for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) + if (SameNameRecordAnswersQuestion(&cr->resrec, &pktq) && cr->resrec.rdlength <= SmallRecordLimit) + if (!cr->NextInKAList && eap != &cr->NextInKAList) + { + *eap = cr; + eap = &cr->NextInKAList; #if ENABLE_MULTI_PACKET_QUERY_SNOOPING - if (cr->MPUnansweredQ == 0 || m->timenow - cr->MPLastUnansweredQT >= mDNSPlatformOneSecond) - { - // Although MPUnansweredQ is only really used for multi-packet query processing, - // we increment it for both single-packet and multi-packet queries, so that it stays in sync - // with the MPUnansweredKA value, which by necessity is incremented for both query types. - cr->MPUnansweredQ++; - cr->MPLastUnansweredQT = m->timenow; - cr->MPExpectingKA = mDNStrue; - } + if (cr->MPUnansweredQ == 0 || m->timenow - cr->MPLastUnansweredQT >= mDNSPlatformOneSecond) + { + // Although MPUnansweredQ is only really used for multi-packet query processing, + // we increment it for both single-packet and multi-packet queries, so that it stays in sync + // with the MPUnansweredKA value, which by necessity is incremented for both query types. + cr->MPUnansweredQ++; + cr->MPLastUnansweredQT = m->timenow; + cr->MPExpectingKA = mDNStrue; + } #endif - } - - // Check if this question is the same as any of mine. - // We only do this for non-truncated queries. Right now it would be too complicated to try - // to keep track of duplicate suppression state between multiple packets, especially when we - // can't guarantee to receive all of the Known Answer packets that go with a particular query. + } + + // Check if this question is the same as any of mine. + // We only do this for non-truncated queries. Right now it would be too complicated to try + // to keep track of duplicate suppression state between multiple packets, especially when we + // can't guarantee to receive all of the Known Answer packets that go with a particular query. #if ENABLE_MULTI_PACKET_QUERY_SNOOPING - if (!(query->h.flags.b[0] & kDNSFlag0_TC)) + if (!(query->h.flags.b[0] & kDNSFlag0_TC)) #endif - for (q = m->Questions; q; q=q->next) - if (!q->Target.type && ActiveQuestion(q) && m->timenow - q->LastQTxTime > mDNSPlatformOneSecond / 4) - if (!q->InterfaceID || q->InterfaceID == InterfaceID) - if (q->NextInDQList == mDNSNULL && dqp != &q->NextInDQList) - if (q->qtype == pktq.qtype && - q->qclass == pktq.qclass && - q->qnamehash == pktq.qnamehash && SameDomainName(&q->qname, &pktq.qname)) - { *dqp = q; dqp = &q->NextInDQList; } - } - } - - // *** - // *** 3. Now we can safely build the list of marked answers - // *** - for (rr = m->ResourceRecords; rr; rr=rr->next) // Now build our list of potential answers - if (rr->NR_AnswerTo) // If we marked the record... - AddRecordToResponseList(&nrp, rr, mDNSNULL); // ... add it to the list - - // *** - // *** 4. Add additional records - // *** - AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID); - - // *** - // *** 5. Parse Answer Section and cancel any records disallowed by Known-Answer list - // *** - for (i=0; ih.numAnswers; i++) // For each record in the query's answer section... - { - // Get the record... - CacheRecord *ourcacherr; - ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &m->rec); - if (!ptr) goto exit; - if (m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative) - { - // See if this Known-Answer suppresses any of our currently planned answers - for (rr=ResponseRecords; rr; rr=rr->NextResponse) - 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(&m->rec.r, rr)) - { - if (srcaddr->type == mDNSAddrType_IPv4) - { - if (mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = zerov4Addr; - } - else if (srcaddr->type == mDNSAddrType_IPv6) - { - if (mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = zerov6Addr; - } - if (mDNSIPv4AddressIsZero(rr->v4Requester) && mDNSIPv6AddressIsZero(rr->v6Requester)) - { - rr->ImmedAnswer = mDNSNULL; - rr->ImmedUnicast = mDNSfalse; - #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES - LogMsg("Suppressed after%4d: %s", m->timenow - rr->ImmedAnswerMarkTime, ARDisplayString(m, rr)); - #endif - } - } - } - - ourcacherr = FindIdenticalRecordInCache(m, &m->rec.r.resrec); - - #if ENABLE_MULTI_PACKET_QUERY_SNOOPING - // 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). - if (ourcacherr && ourcacherr->MPExpectingKA && m->timenow - ourcacherr->MPLastUnansweredQT < mDNSPlatformOneSecond) - { - ourcacherr->MPUnansweredKA++; - ourcacherr->MPExpectingKA = mDNSfalse; - } - #endif - - // Having built our ExpectedAnswers list from the questions in this packet, we then remove - // any records that are suppressed by the Known Answer list in this packet. - eap = &ExpectedAnswers; - while (*eap) - { - CacheRecord *cr = *eap; - if (cr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &cr->resrec)) - { *eap = cr->NextInKAList; cr->NextInKAList = mDNSNULL; } - else eap = &cr->NextInKAList; - } - - // See if this Known-Answer is a surprise to us. If so, we shouldn't suppress our own query. - if (!ourcacherr) - { - dqp = &DupQuestions; - while (*dqp) - { - DNSQuestion *q = *dqp; - if (ResourceRecordAnswersQuestion(&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 - } - - // *** - // *** 6. Cancel any additionals that were added because of now-deleted records - // *** - for (rr=ResponseRecords; rr; rr=rr->NextResponse) - if (rr->NR_AdditionalTo && !MustSendRecord(rr->NR_AdditionalTo)) - { rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; } - - // *** - // *** 7. Mark the send flags on the records we plan to send - // *** - for (rr=ResponseRecords; rr; rr=rr->NextResponse) - { - if (rr->NR_AnswerTo) - { - 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 this record was marked for modern (delayed) unicast response, then mark it as promoted to - // multicast response instead (don't want to end up ALSO setting SendUnicastResponse in the check below). - // If this record was marked for legacy unicast response, then we mustn't change the NR_AnswerTo value. - if (rr->NR_AnswerTo == (mDNSu8*)~1) rr->NR_AnswerTo = (mDNSu8*)~0; - } - - // 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 == (mDNSu8*)~1) SendUnicastResponse = mDNStrue; - else if (rr->NR_AnswerTo) SendLegacyResponse = mDNStrue; - - if (SendMulticastResponse || SendUnicastResponse) - { + for (q = m->Questions; q; q=q->next) + if (!q->Target.type && ActiveQuestion(q) && m->timenow - q->LastQTxTime > mDNSPlatformOneSecond / 4) + if (!q->InterfaceID || q->InterfaceID == InterfaceID) + if (q->NextInDQList == mDNSNULL && dqp != &q->NextInDQList) + if (q->qtype == pktq.qtype && + q->qclass == pktq.qclass && + q->qnamehash == pktq.qnamehash && SameDomainName(&q->qname, &pktq.qname)) + { *dqp = q; dqp = &q->NextInDQList; } + } + } + + // *** + // *** 3. Now we can safely build the list of marked answers + // *** + for (rr = m->ResourceRecords; rr; rr=rr->next) // Now build our list of potential answers + if (rr->NR_AnswerTo) // If we marked the record... + AddRecordToResponseList(&nrp, rr, mDNSNULL); // ... add it to the list + + // *** + // *** 4. Add additional records + // *** + AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID); + + // *** + // *** 5. Parse Answer Section and cancel any records disallowed by Known-Answer list + // *** + for (i=0; ih.numAnswers; i++) // For each record in the query's answer section... + { + // Get the record... + CacheRecord *ourcacherr; + ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &m->rec); + if (!ptr) goto exit; + if (m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative) + { + // See if this Known-Answer suppresses any of our currently planned answers + for (rr=ResponseRecords; rr; rr=rr->NextResponse) + 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(&m->rec.r, rr)) + { + if (srcaddr->type == mDNSAddrType_IPv4) + { + if (mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = zerov4Addr; + } + else if (srcaddr->type == mDNSAddrType_IPv6) + { + if (mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = zerov6Addr; + } + if (mDNSIPv4AddressIsZero(rr->v4Requester) && mDNSIPv6AddressIsZero(rr->v6Requester)) + { + rr->ImmedAnswer = mDNSNULL; + rr->ImmedUnicast = mDNSfalse; + #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES + LogMsg("Suppressed after%4d: %s", m->timenow - rr->ImmedAnswerMarkTime, ARDisplayString(m, rr)); + #endif + } + } + } + + ourcacherr = FindIdenticalRecordInCache(m, &m->rec.r.resrec); + + #if ENABLE_MULTI_PACKET_QUERY_SNOOPING + // 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). + if (ourcacherr && ourcacherr->MPExpectingKA && m->timenow - ourcacherr->MPLastUnansweredQT < mDNSPlatformOneSecond) + { + ourcacherr->MPUnansweredKA++; + ourcacherr->MPExpectingKA = mDNSfalse; + } + #endif + + // Having built our ExpectedAnswers list from the questions in this packet, we then remove + // any records that are suppressed by the Known Answer list in this packet. + eap = &ExpectedAnswers; + while (*eap) + { + CacheRecord *cr = *eap; + if (cr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &cr->resrec)) + { *eap = cr->NextInKAList; cr->NextInKAList = mDNSNULL; } + else eap = &cr->NextInKAList; + } + + // See if this Known-Answer is a surprise to us. If so, we shouldn't suppress our own query. + if (!ourcacherr) + { + dqp = &DupQuestions; + while (*dqp) + { + DNSQuestion *q = *dqp; + if (ResourceRecordAnswersQuestion(&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 + } + + // *** + // *** 6. Cancel any additionals that were added because of now-deleted records + // *** + for (rr=ResponseRecords; rr; rr=rr->NextResponse) + if (rr->NR_AdditionalTo && !MustSendRecord(rr->NR_AdditionalTo)) + { rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; } + + // *** + // *** 7. Mark the send flags on the records we plan to send + // *** + for (rr=ResponseRecords; rr; rr=rr->NextResponse) + { + if (rr->NR_AnswerTo) + { + 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 this record was marked for modern (delayed) unicast response, then mark it as promoted to + // multicast response instead (don't want to end up ALSO setting SendUnicastResponse in the check below). + // If this record was marked for legacy unicast response, then we mustn't change the NR_AnswerTo value. + if (rr->NR_AnswerTo == (mDNSu8*)~1) rr->NR_AnswerTo = (mDNSu8*)~0; + } + + // 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 == (mDNSu8*)~1) SendUnicastResponse = mDNStrue; + else if (rr->NR_AnswerTo) SendLegacyResponse = mDNStrue; + + if (SendMulticastResponse || SendUnicastResponse) + { #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES - rr->ImmedAnswerMarkTime = m->timenow; + rr->ImmedAnswerMarkTime = m->timenow; #endif - m->NextScheduledResponse = m->timenow; - // If we're already planning to send this on another interface, just send it on all interfaces - if (rr->ImmedAnswer && rr->ImmedAnswer != InterfaceID) - rr->ImmedAnswer = mDNSInterfaceMark; - 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; - else if (!mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = onesIPv4Addr; - } - else if (srcaddr->type == mDNSAddrType_IPv6) - { - if (mDNSIPv6AddressIsZero(rr->v6Requester)) rr->v6Requester = srcaddr->ip.v6; - else if (!mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = onesIPv6Addr; - } - } - } - // 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) - { - // Since additional records are an optimization anyway, we only ever send them on one interface at a time - // If two clients on different interfaces do queries that invoke the same optional additional answer, - // then the earlier client is out of luck - rr->ImmedAdditional = InterfaceID; - // No need to set m->NextScheduledResponse here - // We'll send these additional records when we send them, or not, as the case may be - } - } - - // *** - // *** 8. If we think other machines are likely to answer these questions, set our packet suppression timer - // *** - if (delayresponse && (!m->SuppressSending || (m->SuppressSending - m->timenow) < (delayresponse + 49) / 50)) - { + 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; + 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; + else if (!mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = onesIPv4Addr; + } + else if (srcaddr->type == mDNSAddrType_IPv6) + { + if (mDNSIPv6AddressIsZero(rr->v6Requester)) rr->v6Requester = srcaddr->ip.v6; + else if (!mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = onesIPv6Addr; + } + } + } + // 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) + { + // Since additional records are an optimization anyway, we only ever send them on one interface at a time + // If two clients on different interfaces do queries that invoke the same optional additional answer, + // then the earlier client is out of luck + rr->ImmedAdditional = InterfaceID; + // No need to set m->NextScheduledResponse here + // We'll send these additional records when we send them, or not, as the case may be + } + } + + // *** + // *** 8. If we think other machines are likely to answer these questions, set our packet suppression timer + // *** + if (delayresponse && (!m->SuppressSending || (m->SuppressSending - m->timenow) < (delayresponse + 49) / 50)) + { #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES - mDNSs32 oldss = m->SuppressSending; - if (oldss && delayresponse) - LogMsg("Current SuppressSending delay%5ld; require%5ld", m->SuppressSending - m->timenow, (delayresponse + 49) / 50); + mDNSs32 oldss = m->SuppressSending; + if (oldss && delayresponse) + LogMsg("Current SuppressSending delay%5ld; require%5ld", m->SuppressSending - m->timenow, (delayresponse + 49) / 50); #endif - // Pick a random delay: - // We start with the base delay chosen above (typically either 1 second or 20 seconds), - // and add a random value in the range 0-5 seconds (making 1-6 seconds or 20-25 seconds). - // This is an integer value, with resolution determined by the platform clock rate. - // We then divide that by 50 to get the delay value in ticks. We defer the division until last - // to get better results on platforms with coarse clock granularity (e.g. ten ticks per second). - // The +49 before dividing is to ensure we round up, not down, to ensure that even - // on platforms where the native clock rate is less than fifty ticks per second, - // we still guarantee that the final calculated delay is at least one platform tick. - // We want to make sure we don't ever allow the delay to be zero ticks, - // because if that happens we'll fail the Bonjour Conformance Test. - // Our final computed delay is 20-120ms for normal delayed replies, - // or 400-500ms in the case of multi-packet known-answer lists. - m->SuppressSending = m->timenow + (delayresponse + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*5) + 49) / 50; - if (m->SuppressSending == 0) m->SuppressSending = 1; + // Pick a random delay: + // We start with the base delay chosen above (typically either 1 second or 20 seconds), + // and add a random value in the range 0-5 seconds (making 1-6 seconds or 20-25 seconds). + // This is an integer value, with resolution determined by the platform clock rate. + // We then divide that by 50 to get the delay value in ticks. We defer the division until last + // to get better results on platforms with coarse clock granularity (e.g. ten ticks per second). + // The +49 before dividing is to ensure we round up, not down, to ensure that even + // on platforms where the native clock rate is less than fifty ticks per second, + // we still guarantee that the final calculated delay is at least one platform tick. + // We want to make sure we don't ever allow the delay to be zero ticks, + // because if that happens we'll fail the Bonjour Conformance Test. + // Our final computed delay is 20-120ms for normal delayed replies, + // or 400-500ms in the case of multi-packet known-answer lists. + m->SuppressSending = m->timenow + (delayresponse + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*5) + 49) / 50; + if (m->SuppressSending == 0) m->SuppressSending = 1; #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES - if (oldss && delayresponse) - LogMsg("Set SuppressSending to %5ld", m->SuppressSending - m->timenow); + if (oldss && delayresponse) + LogMsg("Set SuppressSending to %5ld", m->SuppressSending - m->timenow); #endif - } + } - // *** - // *** 9. If query is from a legacy client, or from a new client requesting a unicast reply, then generate a unicast response too - // *** - if (SendLegacyResponse) - responseptr = GenerateUnicastResponse(query, end, InterfaceID, LegacyQuery, response, ResponseRecords); + // *** + // *** 9. If query is from a legacy client, or from a new client requesting a unicast reply, then generate a unicast response too + // *** + 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 - - // *** - // *** 10. Finally, clear our link chains ready for use next time - // *** - while (ResponseRecords) - { - rr = ResponseRecords; - ResponseRecords = rr->NextResponse; - rr->NextResponse = mDNSNULL; - rr->NR_AnswerTo = mDNSNULL; - rr->NR_AdditionalTo = mDNSNULL; - } - - while (ExpectedAnswers) - { - CacheRecord *cr = ExpectedAnswers; - ExpectedAnswers = cr->NextInKAList; - cr->NextInKAList = mDNSNULL; - - // For non-truncated queries, we can definitively say that we should expect - // to be seeing a response for any records still left in the ExpectedAnswers list - if (!(query->h.flags.b[0] & kDNSFlag0_TC)) - if (cr->UnansweredQueries == 0 || m->timenow - cr->LastUnansweredTime >= mDNSPlatformOneSecond) - { - cr->UnansweredQueries++; - cr->LastUnansweredTime = m->timenow; + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + + // *** + // *** 10. Finally, clear our link chains ready for use next time + // *** + while (ResponseRecords) + { + rr = ResponseRecords; + ResponseRecords = rr->NextResponse; + rr->NextResponse = mDNSNULL; + rr->NR_AnswerTo = mDNSNULL; + rr->NR_AdditionalTo = mDNSNULL; + } + + while (ExpectedAnswers) + { + CacheRecord *cr = ExpectedAnswers; + ExpectedAnswers = cr->NextInKAList; + cr->NextInKAList = mDNSNULL; + + // For non-truncated queries, we can definitively say that we should expect + // to be seeing a response for any records still left in the ExpectedAnswers list + if (!(query->h.flags.b[0] & kDNSFlag0_TC)) + if (cr->UnansweredQueries == 0 || m->timenow - cr->LastUnansweredTime >= mDNSPlatformOneSecond) + { + cr->UnansweredQueries++; + cr->LastUnansweredTime = m->timenow; #if ENABLE_MULTI_PACKET_QUERY_SNOOPING - if (cr->UnansweredQueries > 1) - debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s", - cr->UnansweredQueries, cr->MPUnansweredQ, cr->MPUnansweredKA, CRDisplayString(m, cr)); + if (cr->UnansweredQueries > 1) + debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s", + cr->UnansweredQueries, cr->MPUnansweredQ, cr->MPUnansweredKA, CRDisplayString(m, cr)); #endif - SetNextCacheCheckTimeForRecord(m, cr); - } + SetNextCacheCheckTimeForRecord(m, cr); + } - // If we've seen multiple unanswered queries for this record, - // then mark it to expire in five seconds if we don't get a response by then. - if (cr->UnansweredQueries >= MaxUnansweredQueries) - { + // If we've seen multiple unanswered queries for this record, + // then mark it to expire in five seconds if we don't get a response by then. + if (cr->UnansweredQueries >= MaxUnansweredQueries) + { #if ENABLE_MULTI_PACKET_QUERY_SNOOPING - // Only show debugging message if this record was not about to expire anyway - if (RRExpireTime(cr) - m->timenow > 4 * mDNSPlatformOneSecond) - debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s", - cr->UnansweredQueries, cr->MPUnansweredQ, cr->MPUnansweredKA, CRDisplayString(m, cr)); + // Only show debugging message if this record was not about to expire anyway + if (RRExpireTime(cr) - m->timenow > 4 * mDNSPlatformOneSecond) + debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s", + cr->UnansweredQueries, cr->MPUnansweredQ, cr->MPUnansweredKA, CRDisplayString(m, cr)); #endif - mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); - } + mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); + } #if ENABLE_MULTI_PACKET_QUERY_SNOOPING - // Make a guess, based on the multi-packet query / known answer counts, whether we think we - // should have seen an answer for this. (We multiply MPQ by 4 and MPKA by 5, to allow for - // possible packet loss of up to 20% of the additional KA packets.) - else if (cr->MPUnansweredQ * 4 > cr->MPUnansweredKA * 5 + 8) - { - // We want to do this conservatively. - // If there are so many machines on the network that they have to use multi-packet known-answer lists, - // then we don't want them to all hit the network simultaneously with their final expiration queries. - // By setting the record to expire in four minutes, we achieve two things: - // (a) the 90-95% final expiration queries will be less bunched together - // (b) we allow some time for us to witness enough other failed queries that we don't have to do our own - mDNSu32 remain = (mDNSu32)(RRExpireTime(cr) - m->timenow) / 4; - if (remain > 240 * (mDNSu32)mDNSPlatformOneSecond) - remain = 240 * (mDNSu32)mDNSPlatformOneSecond; - - // Only show debugging message if this record was not about to expire anyway - if (RRExpireTime(cr) - m->timenow > 4 * mDNSPlatformOneSecond) - debugf("ProcessQuery: (MPQ) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s", - cr->UnansweredQueries, cr->MPUnansweredQ, cr->MPUnansweredKA, CRDisplayString(m, cr)); - - if (remain <= 60 * (mDNSu32)mDNSPlatformOneSecond) - cr->UnansweredQueries++; // Treat this as equivalent to one definite unanswered query - cr->MPUnansweredQ = 0; // Clear MPQ/MPKA statistics - cr->MPUnansweredKA = 0; - cr->MPExpectingKA = mDNSfalse; - - if (remain < kDefaultReconfirmTimeForNoAnswer) - remain = kDefaultReconfirmTimeForNoAnswer; - mDNS_Reconfirm_internal(m, cr, remain); - } + // Make a guess, based on the multi-packet query / known answer counts, whether we think we + // should have seen an answer for this. (We multiply MPQ by 4 and MPKA by 5, to allow for + // possible packet loss of up to 20% of the additional KA packets.) + else if (cr->MPUnansweredQ * 4 > cr->MPUnansweredKA * 5 + 8) + { + // We want to do this conservatively. + // If there are so many machines on the network that they have to use multi-packet known-answer lists, + // then we don't want them to all hit the network simultaneously with their final expiration queries. + // By setting the record to expire in four minutes, we achieve two things: + // (a) the 90-95% final expiration queries will be less bunched together + // (b) we allow some time for us to witness enough other failed queries that we don't have to do our own + mDNSu32 remain = (mDNSu32)(RRExpireTime(cr) - m->timenow) / 4; + if (remain > 240 * (mDNSu32)mDNSPlatformOneSecond) + remain = 240 * (mDNSu32)mDNSPlatformOneSecond; + + // Only show debugging message if this record was not about to expire anyway + if (RRExpireTime(cr) - m->timenow > 4 * mDNSPlatformOneSecond) + debugf("ProcessQuery: (MPQ) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s", + cr->UnansweredQueries, cr->MPUnansweredQ, cr->MPUnansweredKA, CRDisplayString(m, cr)); + + if (remain <= 60 * (mDNSu32)mDNSPlatformOneSecond) + cr->UnansweredQueries++; // Treat this as equivalent to one definite unanswered query + cr->MPUnansweredQ = 0; // Clear MPQ/MPKA statistics + cr->MPUnansweredKA = 0; + cr->MPExpectingKA = mDNSfalse; + + if (remain < kDefaultReconfirmTimeForNoAnswer) + remain = kDefaultReconfirmTimeForNoAnswer; + mDNS_Reconfirm_internal(m, cr, remain); + } #endif - } - - while (DupQuestions) - { - DNSQuestion *q = DupQuestions; - DupQuestions = q->NextInDQList; - q->NextInDQList = mDNSNULL; - i = RecordDupSuppressInfo(q->DupSuppress, m->timenow, InterfaceID, srcaddr->type); - debugf("ProcessQuery: Recorded DSI for %##s (%s) on %p/%s %d", q->qname.c, DNSTypeName(q->qtype), InterfaceID, - srcaddr->type == mDNSAddrType_IPv4 ? "v4" : "v6", i); - } - - return(responseptr); - } + } + + while (DupQuestions) + { + DNSQuestion *q = DupQuestions; + DupQuestions = q->NextInDQList; + q->NextInDQList = mDNSNULL; + i = RecordDupSuppressInfo(q->DupSuppress, m->timenow, InterfaceID, srcaddr->type); + debugf("ProcessQuery: Recorded DSI for %##s (%s) on %p/%s %d", q->qname.c, DNSTypeName(q->qtype), InterfaceID, + srcaddr->type == mDNSAddrType_IPv4 ? "v4" : "v6", i); + } + + return(responseptr); +} mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, - const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, - const mDNSInterfaceID InterfaceID) - { - mDNSu8 *responseend = mDNSNULL; - mDNSBool QueryWasLocalUnicast = srcaddr && dstaddr && - !mDNSAddrIsDNSMulticast(dstaddr) && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); - - if (!InterfaceID && dstaddr && mDNSAddrIsDNSMulticast(dstaddr)) - { - 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 %d bytes (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,", - msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", - msg->h.numAdditionals, msg->h.numAdditionals == 1 ? " " : "s", end - msg->data); - return; - } - - 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 %d bytes", - srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, - msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", - msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", - msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", - msg->h.numAdditionals, msg->h.numAdditionals == 1 ? " " : "s", end - msg->data); - - responseend = ProcessQuery(m, msg, end, srcaddr, InterfaceID, - !mDNSSameIPPort(srcport, MulticastDNSPort), 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", - 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, &m->omsg, responseend, InterfaceID, mDNSNULL, srcaddr, srcport, mDNSNULL, mDNSNULL); - } - } + const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, + const mDNSInterfaceID InterfaceID) +{ + mDNSu8 *responseend = mDNSNULL; + mDNSBool QueryWasLocalUnicast = srcaddr && dstaddr && + !mDNSAddrIsDNSMulticast(dstaddr) && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); + + if (!InterfaceID && dstaddr && mDNSAddrIsDNSMulticast(dstaddr)) + { + 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 %d bytes (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,", + msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", + msg->h.numAdditionals, msg->h.numAdditionals == 1 ? " " : "s", end - msg->data); + return; + } + + 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 %d bytes", + srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, + msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", + msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", + msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", + msg->h.numAdditionals, msg->h.numAdditionals == 1 ? " " : "s", end - msg->data); + + responseend = ProcessQuery(m, msg, end, srcaddr, InterfaceID, + !mDNSSameIPPort(srcport, MulticastDNSPort), 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", + 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, &m->omsg, responseend, InterfaceID, mDNSNULL, srcaddr, srcport, mDNSNULL, mDNSNULL, mDNSfalse); + } +} #if 0 mDNSlocal mDNSBool TrustedSource(const mDNS *const m, const mDNSAddr *const srcaddr) - { - DNSServer *s; - (void)m; // Unused - (void)srcaddr; // Unused - for (s = m->DNSServers; s; s = s->next) - if (mDNSSameAddress(srcaddr, &s->addr)) return(mDNStrue); - return(mDNSfalse); - } +{ + DNSServer *s; + (void)m; // Unused + (void)srcaddr; // Unused + for (s = m->DNSServers; s; s = s->next) + if (mDNSSameAddress(srcaddr, &s->addr)) return(mDNStrue); + return(mDNSfalse); +} #endif struct UDPSocket_struct - { - mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port - }; +{ + mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port +}; mDNSlocal DNSQuestion *ExpectingUnicastResponseForQuestion(const mDNS *const m, const mDNSIPPort port, const mDNSOpaque16 id, const DNSQuestion *const question, mDNSBool tcp) - { - DNSQuestion *q; - for (q = m->Questions; q; q=q->next) - { - if (!tcp && !q->LocalSocket) continue; - if (mDNSSameIPPort(tcp ? q->tcpSrcPort : q->LocalSocket->port, port) && - mDNSSameOpaque16(q->TargetQID, id) && - q->qtype == question->qtype && - q->qclass == question->qclass && - q->qnamehash == question->qnamehash && - SameDomainName(&q->qname, &question->qname)) - return(q); - } - return(mDNSNULL); - } +{ + DNSQuestion *q; + for (q = m->Questions; q; q=q->next) + { + if (!tcp && !q->LocalSocket) continue; + if (mDNSSameIPPort(tcp ? q->tcpSrcPort : q->LocalSocket->port, port) && + mDNSSameOpaque16(q->TargetQID, id) && + q->qtype == question->qtype && + q->qclass == question->qclass && + q->qnamehash == question->qnamehash && + SameDomainName(&q->qname, &question->qname)) + return(q); + } + return(mDNSNULL); +} // This function is called when we receive a unicast response. This could be the case of a unicast response from the // DNS server or a response to the QU query. Hence, the cache record's InterfaceId can be both NULL or non-NULL (QU case) mDNSlocal DNSQuestion *ExpectingUnicastResponseForRecord(mDNS *const m, - const mDNSAddr *const srcaddr, const mDNSBool SrcLocal, const mDNSIPPort port, const mDNSOpaque16 id, const CacheRecord *const rr, mDNSBool tcp) - { - DNSQuestion *q; - (void)id; - (void)srcaddr; - - for (q = m->Questions; q; q=q->next) - { - if (!q->DuplicateOf && ResourceRecordAnswersUnicastResponse(&rr->resrec, q)) - { - if (!mDNSOpaque16IsZero(q->TargetQID)) - { - debugf("ExpectingUnicastResponseForRecord msg->h.id %d q->TargetQID %d for %s", mDNSVal16(id), mDNSVal16(q->TargetQID), CRDisplayString(m, rr)); - - if (mDNSSameOpaque16(q->TargetQID, id)) - { - mDNSIPPort srcp; - if (!tcp) - { - srcp = q->LocalSocket ? q->LocalSocket->port : zeroIPPort; - } - else - { - srcp = q->tcpSrcPort; - } - if (mDNSSameIPPort(srcp, port)) return(q); - - // if (mDNSSameAddress(srcaddr, &q->Target)) return(mDNStrue); - // if (q->LongLived && mDNSSameAddress(srcaddr, &q->servAddr)) return(mDNStrue); Shouldn't need this now that we have LLQType checking - // if (TrustedSource(m, srcaddr)) return(mDNStrue); - LogInfo("WARNING: Ignoring suspect uDNS response for %##s (%s) [q->Target %#a:%d] from %#a:%d %s", - q->qname.c, DNSTypeName(q->qtype), &q->Target, mDNSVal16(srcp), srcaddr, mDNSVal16(port), CRDisplayString(m, rr)); - return(mDNSNULL); - } - } - else - { - if (SrcLocal && q->ExpectUnicastResp && (mDNSu32)(m->timenow - q->ExpectUnicastResp) < (mDNSu32)(mDNSPlatformOneSecond*2)) - return(q); - } - } - } - return(mDNSNULL); - } + const mDNSAddr *const srcaddr, const mDNSBool SrcLocal, const mDNSIPPort port, const mDNSOpaque16 id, const CacheRecord *const rr, mDNSBool tcp) +{ + DNSQuestion *q; + (void)id; + (void)srcaddr; + + for (q = m->Questions; q; q=q->next) + { + if (!q->DuplicateOf && ResourceRecordAnswersUnicastResponse(&rr->resrec, q)) + { + if (!mDNSOpaque16IsZero(q->TargetQID)) + { + debugf("ExpectingUnicastResponseForRecord msg->h.id %d q->TargetQID %d for %s", mDNSVal16(id), mDNSVal16(q->TargetQID), CRDisplayString(m, rr)); + + if (mDNSSameOpaque16(q->TargetQID, id)) + { + mDNSIPPort srcp; + if (!tcp) + { + srcp = q->LocalSocket ? q->LocalSocket->port : zeroIPPort; + } + else + { + srcp = q->tcpSrcPort; + } + if (mDNSSameIPPort(srcp, port)) return(q); + + // if (mDNSSameAddress(srcaddr, &q->Target)) return(mDNStrue); + // if (q->LongLived && mDNSSameAddress(srcaddr, &q->servAddr)) return(mDNStrue); Shouldn't need this now that we have LLQType checking + // if (TrustedSource(m, srcaddr)) return(mDNStrue); + LogInfo("WARNING: Ignoring suspect uDNS response for %##s (%s) [q->Target %#a:%d] from %#a:%d %s", + q->qname.c, DNSTypeName(q->qtype), &q->Target, mDNSVal16(srcp), srcaddr, mDNSVal16(port), CRDisplayString(m, rr)); + return(mDNSNULL); + } + } + else + { + if (SrcLocal && q->ExpectUnicastResp && (mDNSu32)(m->timenow - q->ExpectUnicastResp) < (mDNSu32)(mDNSPlatformOneSecond*2)) + return(q); + } + } + } + return(mDNSNULL); +} // Certain data types need more space for in-memory storage than their in-packet rdlength would imply // Currently this applies only to rdata types containing more than one domainname, // or types where the domainname is not the last item in the structure. -// In addition, NSEC currently requires less space for in-memory storage than its in-packet representation. mDNSlocal mDNSu16 GetRDLengthMem(const ResourceRecord *const rr) - { - switch (rr->rrtype) - { - case kDNSType_SOA: return sizeof(rdataSOA); - case kDNSType_RP: return sizeof(rdataRP); - case kDNSType_PX: return sizeof(rdataPX); - case kDNSType_NSEC:return sizeof(rdataNSEC); - default: return rr->rdlength; - } - } - -mDNSexport CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, CacheGroup *cg, mDNSs32 delay) - { - CacheRecord *rr = mDNSNULL; - mDNSu16 RDLength = GetRDLengthMem(&m->rec.r.resrec); - - if (!m->rec.r.resrec.InterfaceID) debugf("CreateNewCacheEntry %s", CRDisplayString(m, &m->rec.r)); - - //if (RDLength > InlineCacheRDSize) - // LogInfo("Rdata len %4d > InlineCacheRDSize %d %s", RDLength, InlineCacheRDSize, CRDisplayString(m, &m->rec.r)); - - if (!cg) cg = GetCacheGroup(m, slot, &m->rec.r.resrec); // If we don't have a CacheGroup for this name, make one now - if (cg) rr = GetCacheRecord(m, cg, RDLength); // Make a cache record, being careful not to recycle cg - if (!rr) NoCacheAnswer(m, &m->rec.r); - else - { - RData *saveptr = rr->resrec.rdata; // Save the rr->resrec.rdata pointer - *rr = m->rec.r; // Block copy the CacheRecord object - rr->resrec.rdata = saveptr; // Restore rr->resrec.rdata after the structure assignment - rr->resrec.name = cg->name; // And set rr->resrec.name to point into our CacheGroup header - rr->DelayDelivery = delay; - - // If this is an oversized record with external storage allocated, copy rdata to external storage - if (rr->resrec.rdata == (RData*)&rr->smallrdatastorage && RDLength > InlineCacheRDSize) - LogMsg("rr->resrec.rdata == &rr->rdatastorage but length > InlineCacheRDSize %##s", m->rec.r.resrec.name->c); - else if (rr->resrec.rdata != (RData*)&rr->smallrdatastorage && RDLength <= InlineCacheRDSize) - LogMsg("rr->resrec.rdata != &rr->rdatastorage but length <= InlineCacheRDSize %##s", m->rec.r.resrec.name->c); - if (RDLength > InlineCacheRDSize) - mDNSPlatformMemCopy(rr->resrec.rdata, m->rec.r.resrec.rdata, sizeofRDataHeader + RDLength); - - rr->next = mDNSNULL; // Clear 'next' pointer - *(cg->rrcache_tail) = rr; // Append this record to tail of cache slot list - cg->rrcache_tail = &(rr->next); // Advance tail pointer - - CacheRecordAdd(m, rr); // CacheRecordAdd calls SetNextCacheCheckTimeForRecord(m, rr); for us - } - return(rr); - } +{ + switch (rr->rrtype) + { + case kDNSType_SOA: return sizeof(rdataSOA); + case kDNSType_RP: return sizeof(rdataRP); + case kDNSType_PX: return sizeof(rdataPX); + default: return rr->rdlength; + } +} + +mDNSexport CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, CacheGroup *cg, mDNSs32 delay, mDNSBool Add, const mDNSAddr *sourceAddress) +{ + CacheRecord *rr = mDNSNULL; + mDNSu16 RDLength = GetRDLengthMem(&m->rec.r.resrec); + + if (!m->rec.r.resrec.InterfaceID) debugf("CreateNewCacheEntry %s", CRDisplayString(m, &m->rec.r)); + + //if (RDLength > InlineCacheRDSize) + // LogInfo("Rdata len %4d > InlineCacheRDSize %d %s", RDLength, InlineCacheRDSize, CRDisplayString(m, &m->rec.r)); + + if (!cg) cg = GetCacheGroup(m, slot, &m->rec.r.resrec); // If we don't have a CacheGroup for this name, make one now + if (cg) rr = GetCacheRecord(m, cg, RDLength); // Make a cache record, being careful not to recycle cg + if (!rr) NoCacheAnswer(m, &m->rec.r); + else + { + RData *saveptr = rr->resrec.rdata; // Save the rr->resrec.rdata pointer + *rr = m->rec.r; // Block copy the CacheRecord object + rr->resrec.rdata = saveptr; // Restore rr->resrec.rdata after the structure assignment + rr->resrec.name = cg->name; // And set rr->resrec.name to point into our CacheGroup header + rr->DelayDelivery = delay; + + // If this is an oversized record with external storage allocated, copy rdata to external storage + if (rr->resrec.rdata == (RData*)&rr->smallrdatastorage && RDLength > InlineCacheRDSize) + LogMsg("rr->resrec.rdata == &rr->rdatastorage but length > InlineCacheRDSize %##s", m->rec.r.resrec.name->c); + else if (rr->resrec.rdata != (RData*)&rr->smallrdatastorage && RDLength <= InlineCacheRDSize) + LogMsg("rr->resrec.rdata != &rr->rdatastorage but length <= InlineCacheRDSize %##s", m->rec.r.resrec.name->c); + if (RDLength > InlineCacheRDSize) + mDNSPlatformMemCopy(rr->resrec.rdata, m->rec.r.resrec.rdata, sizeofRDataHeader + RDLength); + + rr->next = mDNSNULL; // Clear 'next' pointer + rr->nsec = mDNSNULL; + + if (sourceAddress) + rr->sourceAddress = *sourceAddress; + + if (Add) + { + *(cg->rrcache_tail) = rr; // Append this record to tail of cache slot list + cg->rrcache_tail = &(rr->next); // Advance tail pointer + CacheRecordAdd(m, rr); // CacheRecordAdd calls SetNextCacheCheckTimeForRecord(m, rr); for us + } + else + { + // Can't use the "cg->name" if we are not adding to the cache as the + // CacheGroup may be released anytime if it is empty + domainname *name = mDNSPlatformMemAllocate(DomainNameLength(cg->name)); + if (name) + { + AssignDomainName(name, cg->name); + rr->resrec.name = name; + } + else + { + ReleaseCacheRecord(m, rr); + NoCacheAnswer(m, &m->rec.r); + rr = mDNSNULL; + } + } + } + return(rr); +} mDNSlocal void RefreshCacheRecord(mDNS *const m, CacheRecord *rr, mDNSu32 ttl) - { - rr->TimeRcvd = m->timenow; - rr->resrec.rroriginalttl = ttl; - rr->UnansweredQueries = 0; +{ + rr->TimeRcvd = m->timenow; + rr->resrec.rroriginalttl = ttl; + rr->UnansweredQueries = 0; #if ENABLE_MULTI_PACKET_QUERY_SNOOPING - rr->MPUnansweredQ = 0; - rr->MPUnansweredKA = 0; - rr->MPExpectingKA = mDNSfalse; + rr->MPUnansweredQ = 0; + rr->MPUnansweredKA = 0; + rr->MPExpectingKA = mDNSfalse; #endif - SetNextCacheCheckTimeForRecord(m, rr); - } + SetNextCacheCheckTimeForRecord(m, rr); +} mDNSexport void GrantCacheExtensions(mDNS *const m, DNSQuestion *q, mDNSu32 lease) - { - CacheRecord *rr; - const mDNSu32 slot = HashSlot(&q->qname); - CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) - if (rr->CRActiveQuestion == q) - { - //LogInfo("GrantCacheExtensions: new lease %d / %s", lease, CRDisplayString(m, rr)); - RefreshCacheRecord(m, rr, lease); - } - } - -mDNSlocal mDNSu32 GetEffectiveTTL(const uDNS_LLQType LLQType, mDNSu32 ttl) // TTL in seconds - { - if (LLQType == uDNS_LLQ_Entire) ttl = kLLQ_DefLease; - else if (LLQType == uDNS_LLQ_Events) - { - // If the TTL is -1 for uDNS LLQ event packet, that means "remove" - if (ttl == 0xFFFFFFFF) ttl = 0; - else ttl = kLLQ_DefLease; - } - else // else not LLQ (standard uDNS response) - { - // The TTL is already capped to a maximum value in GetLargeResourceRecord, but just to be extra safe we - // also do this check here to make sure we can't get overflow below when we add a quarter to the TTL - if (ttl > 0x60000000UL / mDNSPlatformOneSecond) ttl = 0x60000000UL / mDNSPlatformOneSecond; - - // Adjustment factor to avoid race condition: - // Suppose real record as TTL of 3600, and our local caching server has held it for 3500 seconds, so it returns an aged TTL of 100. - // If we do our normal refresh at 80% of the TTL, our local caching server will return 20 seconds, so we'll do another - // 80% refresh after 16 seconds, and then the server will return 4 seconds, and so on, in the fashion of Zeno's paradox. - // To avoid this, we extend the record's effective TTL to give it a little extra grace period. - // We adjust the 100 second TTL to 126. This means that when we do our 80% query at 101 seconds, - // the cached copy at our local caching server will already have expired, so the server will be forced - // to fetch a fresh copy from the authoritative server, and then return a fresh record with the full TTL of 3600 seconds. - ttl += ttl/4 + 2; - - // For mDNS, TTL zero means "delete this record" - // For uDNS, TTL zero means: this data is true at this moment, but don't cache it. - // For the sake of network efficiency, we impose a minimum effective TTL of 15 seconds. - // This means that we'll do our 80, 85, 90, 95% queries at 12.00, 12.75, 13.50, 14.25 seconds - // respectively, and then if we get no response, delete the record from the cache at 15 seconds. - // This gives the server up to three seconds to respond between when we send our 80% query at 12 seconds - // and when we delete the record at 15 seconds. Allowing cache lifetimes less than 15 seconds would - // (with the current code) result in the server having even less than three seconds to respond - // before we deleted the record and reported a "remove" event to any active questions. - // Furthermore, with the current code, if we were to allow a TTL of less than 2 seconds - // then things really break (e.g. we end up making a negative cache entry). - // In the future we may want to revisit this and consider properly supporting non-cached (TTL=0) uDNS answers. - if (ttl < 15) ttl = 15; - } - - return ttl; - } +{ + CacheRecord *rr; + const mDNSu32 slot = HashSlot(&q->qname); + CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + if (rr->CRActiveQuestion == q) + { + //LogInfo("GrantCacheExtensions: new lease %d / %s", lease, CRDisplayString(m, rr)); + RefreshCacheRecord(m, rr, lease); + } +} + +mDNSlocal mDNSu32 GetEffectiveTTL(const uDNS_LLQType LLQType, mDNSu32 ttl) // TTL in seconds +{ + if (LLQType == uDNS_LLQ_Entire) ttl = kLLQ_DefLease; + else if (LLQType == uDNS_LLQ_Events) + { + // If the TTL is -1 for uDNS LLQ event packet, that means "remove" + if (ttl == 0xFFFFFFFF) ttl = 0; + else ttl = kLLQ_DefLease; + } + else // else not LLQ (standard uDNS response) + { + // The TTL is already capped to a maximum value in GetLargeResourceRecord, but just to be extra safe we + // also do this check here to make sure we can't get overflow below when we add a quarter to the TTL + if (ttl > 0x60000000UL / mDNSPlatformOneSecond) ttl = 0x60000000UL / mDNSPlatformOneSecond; + + // Adjustment factor to avoid race condition: + // Suppose real record as TTL of 3600, and our local caching server has held it for 3500 seconds, so it returns an aged TTL of 100. + // If we do our normal refresh at 80% of the TTL, our local caching server will return 20 seconds, so we'll do another + // 80% refresh after 16 seconds, and then the server will return 4 seconds, and so on, in the fashion of Zeno's paradox. + // To avoid this, we extend the record's effective TTL to give it a little extra grace period. + // We adjust the 100 second TTL to 126. This means that when we do our 80% query at 101 seconds, + // the cached copy at our local caching server will already have expired, so the server will be forced + // to fetch a fresh copy from the authoritative server, and then return a fresh record with the full TTL of 3600 seconds. + ttl += ttl/4 + 2; + + // For mDNS, TTL zero means "delete this record" + // For uDNS, TTL zero means: this data is true at this moment, but don't cache it. + // For the sake of network efficiency, we impose a minimum effective TTL of 15 seconds. + // This means that we'll do our 80, 85, 90, 95% queries at 12.00, 12.75, 13.50, 14.25 seconds + // respectively, and then if we get no response, delete the record from the cache at 15 seconds. + // This gives the server up to three seconds to respond between when we send our 80% query at 12 seconds + // and when we delete the record at 15 seconds. Allowing cache lifetimes less than 15 seconds would + // (with the current code) result in the server having even less than three seconds to respond + // before we deleted the record and reported a "remove" event to any active questions. + // Furthermore, with the current code, if we were to allow a TTL of less than 2 seconds + // then things really break (e.g. we end up making a negative cache entry). + // In the future we may want to revisit this and consider properly supporting non-cached (TTL=0) uDNS answers. + if (ttl < 15) ttl = 15; + } + + return ttl; +} + +// When the response does not match the question directly, we still want to cache them sometimes. The current response is +// in m->rec. +mDNSlocal mDNSBool IsResponseAcceptable(mDNS *const m, const CacheRecord *crlist, DNSQuestion *q, mDNSBool *nseclist) +{ + CacheRecord *const newcr = &m->rec.r; + ResourceRecord *rr = &newcr->resrec; + const CacheRecord *cr; + + *nseclist = mDNSfalse; + for (cr = crlist; cr != (CacheRecord*)1; cr = cr->NextInCFList) + { + domainname *target = GetRRDomainNameTarget(&cr->resrec); + // When we issue a query for A record, the response might contain both a CNAME and A records. Only the CNAME would + // match the question and we already created a cache entry in the previous pass of this loop. Now when we process + // the A record, it does not match the question because the record name here is the CNAME. Hence we try to + // match with the previous records to make it an AcceptableResponse. We have to be careful about setting the + // DNSServer value that we got in the previous pass. This can happen for other record types like SRV also. + + if (target && cr->resrec.rdatahash == rr->namehash && SameDomainName(target, rr->name)) + { + LogInfo("IsResponseAcceptable: Found a matching entry for %##s in the CacheFlushRecords %s", rr->name->c, CRDisplayString(m, cr)); + return (mDNStrue); + } + } + + // Either the question requires validation or we are validating a response with DNSSEC in which case + // we need to accept the RRSIGs also so that we can validate the response. It is also possible that + // we receive NSECs for our query which does not match the qname and we need to cache in that case + // too. nseclist is set if they have to be cached as part of the negative cache record. + if (q && DNSSECQuestion(q)) + { + mDNSBool same = SameDomainName(&q->qname, rr->name); + if (same && (q->qtype == rr->rrtype || rr->rrtype == kDNSType_CNAME)) + { + LogInfo("IsResponseAcceptable: Accepting, same name and qtype %s, CR %s", DNSTypeName(q->qtype), + CRDisplayString(m, newcr)); + return mDNStrue; + } + // We cache RRSIGS if it covers the question type or NSEC. If it covers a NSEC, + // "nseclist" is set + if (rr->rrtype == kDNSType_RRSIG) + { + RDataBody2 *const rdb = (RDataBody2 *)newcr->smallrdatastorage.data; + rdataRRSig *rrsig = &rdb->rrsig; + mDNSu16 typeCovered = swap16(rrsig->typeCovered); + + // Note the ordering. If we are looking up the NSEC record, then the RRSIG's typeCovered + // would match the qtype and they are cached normally as they are not used to prove the + // non-existence of any name. In that case, it is like any other normal dnssec validation + // and hence nseclist should not be set. + + if (same && ((typeCovered == q->qtype) || (typeCovered == kDNSType_CNAME))) + { + LogInfo("IsResponseAcceptable: Accepting RRSIG %s matches question type %s", CRDisplayString(m, newcr), + DNSTypeName(q->qtype)); + return mDNStrue; + } + else if (typeCovered == kDNSType_NSEC) + { + LogInfo("IsResponseAcceptable: Accepting RRSIG %s matches NSEC type (nseclist = 1)", CRDisplayString(m, newcr)); + *nseclist = mDNStrue; + return mDNStrue; + } + else return mDNSfalse; + } + if (rr->rrtype == kDNSType_NSEC) + { + if (!UNICAST_NSEC(rr)) + { + LogMsg("IsResponseAcceptable: ERROR!! Not a unicast NSEC %s", CRDisplayString(m, newcr)); + return mDNSfalse; + } + LogInfo("IsResponseAcceptable: Accepting NSEC %s (nseclist = 1)", CRDisplayString(m, newcr)); + *nseclist = mDNStrue; + return mDNStrue; + } + } + return mDNSfalse; +} + +mDNSlocal void FreeNSECRecords(mDNS *const m, CacheRecord *NSECRecords) +{ + CacheRecord *rp, *next; + + for (rp = NSECRecords; rp; rp = next) + { + next = rp->next; + ReleaseCacheRecord(m, rp); + } +} + +mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, const mDNSAddr *dstaddr, + mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, uDNS_LLQType LLQType, mDNSu8 rcode, CacheRecord *NSECRecords) +{ + int i; + const mDNSu8 *ptr = response->data; + for (i = 0; i < response->h.numQuestions && ptr && ptr < end; i++) + { + DNSQuestion q; + DNSQuestion *qptr = mDNSNULL; + ptr = getQuestion(response, ptr, end, InterfaceID, &q); + if (ptr && (qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &q, !dstaddr))) + { + CacheRecord *rr, *neg = mDNSNULL; + mDNSu32 slot = HashSlot(&q.qname); + CacheGroup *cg = CacheGroupForName(m, slot, q.qnamehash, &q.qname); + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + if (SameNameRecordAnswersQuestion(&rr->resrec, qptr)) + { + // 1. If we got a fresh answer to this query, then don't need to generate a negative entry + if (RRExpireTime(rr) - m->timenow > 0) break; + // 2. If we already had a negative entry, keep track of it so we can resurrect it instead of creating a new one + if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) neg = rr; + } + // When we're doing parallel unicast and multicast queries for dot-local names (for supporting Microsoft + // Active Directory sites) we don't want to waste memory making negative cache entries for all the unicast answers. + // Otherwise we just fill up our cache with negative entries for just about every single multicast name we ever look up + // (since the Microsoft Active Directory server is going to assert that pretty much every single multicast name doesn't exist). + // This is not only a waste of memory, but there's also the problem of those negative entries confusing us later -- e.g. we + // suppress sending our mDNS query packet because we think we already have a valid (negative) answer to that query in our cache. + // The one exception is that we *DO* want to make a negative cache entry for "local. SOA", for the (common) case where we're + // *not* on a Microsoft Active Directory network, and there is no authoritative server for "local". Note that this is not + // in conflict with the mDNS spec, because that spec says, "Multicast DNS Zones have no SOA record," so it's okay to cache + // negative answers for "local. SOA" from a uDNS server, because the mDNS spec already says that such records do not exist :-) + // + // By suppressing negative responses, it might take longer to timeout a .local question as it might be expecting a + // response e.g., we deliver a positive "A" response and suppress negative "AAAA" response and the upper layer may + // be waiting longer to get the AAAA response before returning the "A" response to the application. To handle this + // case without creating the negative cache entries, we generate a negative response and let the layer above us + // do the appropriate thing. This negative response is also needed for appending new search domains. + if (!InterfaceID && q.qtype != kDNSType_SOA && IsLocalDomain(&q.qname)) + { + if (!rr) + { + LogInfo("mDNSCoreReceiveNoUnicastAnswers: Generate negative response for %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); + m->CurrentQuestion = qptr; + GenerateNegativeResponse(m); + m->CurrentQuestion = mDNSNULL; + } + else LogInfo("mDNSCoreReceiveNoUnicastAnswers: Skipping check to see if we need to generate a negative cache entry for %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); + } + else + { + if (!rr) + { + // We start off assuming a negative caching TTL of 60 seconds + // but then look to see if we can find an SOA authority record to tell us a better value we should be using + mDNSu32 negttl = 60; + int repeat = 0; + const domainname *name = &q.qname; + mDNSu32 hash = q.qnamehash; + + // Special case for our special Microsoft Active Directory "local SOA" check. + // Some cheap home gateways don't include an SOA record in the authority section when + // they send negative responses, so we don't know how long to cache the negative result. + // Because we don't want to keep hitting the root name servers with our query to find + // if we're on a network using Microsoft Active Directory using "local" as a private + // internal top-level domain, we make sure to cache the negative result for at least one day. + if (q.qtype == kDNSType_SOA && SameDomainName(&q.qname, &localdomain)) negttl = 60 * 60 * 24; + + // If we're going to make (or update) a negative entry, then look for the appropriate TTL from the SOA record + if (response->h.numAuthorities && (ptr = LocateAuthorities(response, end)) != mDNSNULL) + { + ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec); + if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_SOA) + { + const rdataSOA *const soa = (const rdataSOA *)m->rec.r.resrec.rdata->u.data; + mDNSu32 ttl_s = soa->min; + // We use the lesser of the SOA.MIN field and the SOA record's TTL, *except* + // for the SOA record for ".", where the record is reported as non-cacheable + // (TTL zero) for some reason, so in this case we just take the SOA record's TTL as-is + if (ttl_s > m->rec.r.resrec.rroriginalttl && m->rec.r.resrec.name->c[0]) + ttl_s = m->rec.r.resrec.rroriginalttl; + if (negttl < ttl_s) negttl = ttl_s; + + // Special check for SOA queries: If we queried for a.b.c.d.com, and got no answer, + // with an Authority Section SOA record for d.com, then this is a hint that the authority + // is d.com, and consequently SOA records b.c.d.com and c.d.com don't exist either. + // To do this we set the repeat count so the while loop below will make a series of negative cache entries for us + if (q.qtype == kDNSType_SOA) + { + int qcount = CountLabels(&q.qname); + int scount = CountLabels(m->rec.r.resrec.name); + if (qcount - 1 > scount) + if (SameDomainName(SkipLeadingLabels(&q.qname, qcount - scount), m->rec.r.resrec.name)) + repeat = qcount - 1 - scount; + } + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + + // If we already had a negative entry in the cache, then we double our existing negative TTL. This is to avoid + // the case where the record doesn't exist (e.g. particularly for things like our lb._dns-sd._udp. query), + // and the server returns no SOA record (or an SOA record with a small MIN TTL) so we assume a TTL + // of 60 seconds, and we end up polling the server every minute for a record that doesn't exist. + // With this fix in place, when this happens, we double the effective TTL each time (up to one hour), + // so that we back off our polling rate and don't keep hitting the server continually. + if (neg) + { + if (negttl < neg->resrec.rroriginalttl * 2) + negttl = neg->resrec.rroriginalttl * 2; + if (negttl > 3600) + negttl = 3600; + } + + negttl = GetEffectiveTTL(LLQType, negttl); // Add 25% grace period if necessary + + // If we already had a negative cache entry just update it, else make one or more new negative cache entries. + if (neg) + { + LogInfo("mDNSCoreReceiveNoUnicastAnswers: Renewing negative TTL from %d to %d %s", neg->resrec.rroriginalttl, negttl, CRDisplayString(m, neg)); + RefreshCacheRecord(m, neg, negttl); + // When we created the cache for the first time and answered the question, the question's + // interval was set to MaxQuestionInterval. If the cache is about to expire and we are resending + // the queries, the interval should still be at MaxQuestionInterval. If the query is being + // restarted (setting it to InitialQuestionInterval) for other reasons e.g., wakeup, + // we should reset its question interval here to MaxQuestionInterval. + ResetQuestionState(m, qptr); + // Update the NSEC records again. + // TBD: Need to purge and revalidate if the cached NSECS and the new set are not same. + if (NSECRecords) + { + if (!AddNSECSForCacheRecord(m, NSECRecords, neg, rcode)) + { + LogMsg("mDNSCoreReceiveNoUnicastAnswers: AddNSECSForCacheRecord failed to add NSEC for negcr %s during refresh", CRDisplayString(m, neg)); + FreeNSECRecords(m, NSECRecords); + } + NSECRecords = mDNSNULL; + } + } + else while (1) + { + debugf("mDNSCoreReceiveNoUnicastAnswers making negative cache entry TTL %d for %##s (%s)", negttl, name->c, DNSTypeName(q.qtype)); + MakeNegativeCacheRecord(m, &m->rec.r, name, hash, q.qtype, q.qclass, negttl, mDNSInterface_Any, qptr->qDNSServer); + if (NSECRecords && DNSSECQuestion(qptr)) + { + CacheRecord *negcr; + // Create the cache entry with delay and then add the NSEC records + // to it and add it immediately. + negcr = CreateNewCacheEntry(m, slot, cg, 1, mDNStrue, mDNSNULL); + if (!AddNSECSForCacheRecord(m, NSECRecords, negcr, rcode)) + { + LogMsg("mDNSCoreReceiveNoUnicastAnswers: AddNSECSForCacheRecord failed to add NSEC for negcr %s", CRDisplayString(m, negcr)); + FreeNSECRecords(m, NSECRecords); + } + else LogInfo("mDNSCoreReceiveResponse: AddNSECSForCacheRecord added neg NSEC for %s", CRDisplayString(m, negcr)); + NSECRecords = mDNSNULL; + negcr->DelayDelivery = 0; + CacheRecordDeferredAdd(m, negcr); + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + break; + } + else + { + CreateNewCacheEntry(m, slot, cg, 0, mDNStrue, mDNSNULL); // We never need any delivery delay for these generated negative cache records + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + if (!repeat) break; + repeat--; + name = (const domainname *)(name->c + 1 + name->c[0]); + hash = DomainNameHashValue(name); + slot = HashSlot(name); + cg = CacheGroupForName(m, slot, hash, name); + } + } + } + } + } + if (NSECRecords) { LogInfo("mDNSCoreReceiveNoUnicastAnswers: NSECRecords not used"); FreeNSECRecords(m, NSECRecords); } +} + +mDNSlocal mDNSBool mDNSCoreRegisteredProxyRecord(mDNS *const m, AuthRecord *rr) +{ + NetworkInterfaceInfo *intf = m->HostInterfaces; + AuthRecord *rrPtr = mDNSNULL; + + while (intf) + { + rrPtr = intf->SPSRRSet; + while (rrPtr) + { + if (SameResourceRecordSignature(rrPtr, rr)) + { + LogSPS("mDNSCoreRegisteredProxyRecord: Ignoring packet registered with sleep proxy : %s ", ARDisplayString(m, rr)); + return mDNStrue; + } + rrPtr = rrPtr->next; + } + intf = intf->next; + } + return mDNSfalse; +} // Note: mDNSCoreReceiveResponse calls mDNS_Deregister_internal which can call a user callback, which may change // the record list and/or question list. @@ -6467,717 +7248,666 @@ mDNSlocal mDNSu32 GetEffectiveTTL(const uDNS_LLQType LLQType, mDNSu32 ttl) // T // InterfaceID NULL tells us this was a unicast response // dstaddr NULL tells us we received this over an outgoing TCP connection we made 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) - { - int i; - mDNSBool ResponseMCast = dstaddr && mDNSAddrIsDNSMulticast(dstaddr); - mDNSBool ResponseSrcLocal = !srcaddr || mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); - DNSQuestion *llqMatch = mDNSNULL; - uDNS_LLQType LLQType = uDNS_recvLLQResponse(m, response, end, srcaddr, srcport, &llqMatch); - - // "(CacheRecord*)1" is a special (non-zero) end-of-list marker - // We use this non-zero marker so that records in our CacheFlushRecords list will always have NextInCFList - // set non-zero, and that tells GetCacheEntity() that they're not, at this moment, eligible for recycling. - CacheRecord *CacheFlushRecords = (CacheRecord*)1; - 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 firstauthority = response->h.numAnswers; - int firstadditional = firstauthority + response->h.numAuthorities; - int totalrecords = firstadditional + response->h.numAdditionals; - const mDNSu8 *ptr = response->data; - DNSServer *uDNSServer = mDNSNULL; - - debugf("Received Response from %#-15a addressed to %#-15a on %p with " - "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes LLQType %d", - 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", end - response->data, LLQType); - - // According to RFC 2181 - // When a DNS client receives a reply with TC - // set, it should ignore that response, and query again, using a - // mechanism, such as a TCP connection, that will permit larger replies. - // It feels wrong to be throwing away data after the network went to all the trouble of delivering it to us, but - // delivering some records of the RRSet first and then the remainder a couple of milliseconds later was causing - // failures in our Microsoft Active Directory client, which expects to get the entire set of answers at once. - // Can't bind to Active Directory - // In addition, if the client immediately canceled its query after getting the initial partial response, then we'll - // abort our TCP connection, and not complete the operation, and end up with an incomplete RRSet in our cache. - // Next time there's a query for this RRSet we'll see answers in our cache, and assume we have the whole RRSet already, - // and not even do the TCP query. - // Accordingly, if we get a uDNS reply with kDNSFlag0_TC set, we bail out and wait for the TCP response containing the entire RRSet. - if (!InterfaceID && (response->h.flags.b[0] & kDNSFlag0_TC)) return; - - if (LLQType == uDNS_LLQ_Ignore) return; - - // 1. We ignore questions (if any) in mDNS response packets - // 2. If this is an LLQ response, we handle it much the same - // 3. If we get a uDNS UDP response with the TC (truncated) bit set, then we can't treat this - // answer as being the authoritative complete RRSet, and respond by deleting all other - // matching cache records that don't appear in this packet. - // Otherwise, this is a authoritative uDNS answer, so arrange for any stale records to be purged - if (ResponseMCast || LLQType == uDNS_LLQ_Events || (response->h.flags.b[0] & kDNSFlag0_TC)) - ptr = LocateAnswers(response, end); - // Otherwise, for one-shot queries, any answers in our cache that are not also contained - // in this response packet are immediately deemed to be invalid. - else - { - mDNSu8 rcode = (mDNSu8)(response->h.flags.b[1] & kDNSFlag1_RC_Mask); - mDNSBool failure = !(rcode == kDNSFlag1_RC_NoErr || rcode == kDNSFlag1_RC_NXDomain || rcode == kDNSFlag1_RC_NotAuth); - mDNSBool returnEarly = mDNSfalse; - // We could possibly combine this with the similar loop at the end of this function -- - // instead of tagging cache records here and then rescuing them if we find them in the answer section, - // we could instead use the "m->PktNum" mechanism to tag each cache record with the packet number in - // which it was received (or refreshed), and then at the end if we find any cache records which - // answer questions in this packet's question section, but which aren't tagged with this packet's - // packet number, then we deduce they are old and delete them - for (i = 0; i < response->h.numQuestions && ptr && ptr < end; i++) - { - DNSQuestion q, *qptr = mDNSNULL; - ptr = getQuestion(response, ptr, end, InterfaceID, &q); - if (ptr && (qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &q, !dstaddr))) - { - if (!failure) - { - CacheRecord *rr; - const mDNSu32 slot = HashSlot(&q.qname); - CacheGroup *cg = CacheGroupForName(m, slot, q.qnamehash, &q.qname); - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) - if (SameNameRecordAnswersQuestion(&rr->resrec, qptr)) - { - debugf("uDNS marking %p %##s (%s) %p %s", q.InterfaceID, q.qname.c, DNSTypeName(q.qtype), - rr->resrec.InterfaceID, CRDisplayString(m, rr)); - // Don't want to disturb rroriginalttl here, because code below might need it for the exponential backoff doubling algorithm - rr->TimeRcvd = m->timenow - TicksTTL(rr) - 1; - rr->UnansweredQueries = MaxUnansweredQueries; - } - } - else - { - if (qptr) - { - LogInfo("mDNSCoreReceiveResponse: Server %p responded with code %d to query %##s (%s)", qptr->qDNSServer, rcode, q.qname.c, DNSTypeName(q.qtype)); - PenalizeDNSServer(m, qptr); - } - returnEarly = mDNStrue; - } - } - } - if (returnEarly) - { - LogInfo("Ignoring %2d Answer%s %2d Authorit%s %2d Additional%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"); - // not goto exit because we won't have any CacheFlushRecords and we do not want to - // generate negative cache entries (we want to query the next server) - return; - } - } - - for (i = 0; i < totalrecords && ptr && ptr < end; i++) - { - // All responses sent via LL multicast are acceptable for caching - // All responses received over our outbound TCP connections are acceptable for caching - mDNSBool AcceptableResponse = ResponseMCast || !dstaddr || LLQType; - // (Note that just because we are willing to cache something, that doesn't necessarily make it a trustworthy answer - // to any specific question -- any code reading records from the cache needs to make that determination for itself.) - - const mDNSu8 RecordType = - (i < firstauthority ) ? (mDNSu8)kDNSRecordTypePacketAns : - (i < firstadditional) ? (mDNSu8)kDNSRecordTypePacketAuth : (mDNSu8)kDNSRecordTypePacketAdd; - 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 - if (m->rec.r.resrec.RecordType == kDNSRecordTypePacketNegative) { m->rec.r.resrec.RecordType = 0; continue; } - - // Don't want to cache OPT or TSIG pseudo-RRs - if (m->rec.r.resrec.rrtype == kDNSType_TSIG) { m->rec.r.resrec.RecordType = 0; continue; } - if (m->rec.r.resrec.rrtype == kDNSType_OPT) - { - const rdataOPT *opt; - const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength]; - // Find owner sub-option(s). We verify that the MAC is non-zero, otherwise we could inadvertently - // delete all our own AuthRecords (which are identified by having zero MAC tags on them). - for (opt = &m->rec.r.resrec.rdata->u.opt[0]; opt < e; opt++) - if (opt->opt == kDNSOpt_Owner && opt->u.owner.vers == 0 && opt->u.owner.HMAC.l[0]) - { - ClearProxyRecords(m, &opt->u.owner, m->DuplicateRecords); - ClearProxyRecords(m, &opt->u.owner, m->ResourceRecords); - } - m->rec.r.resrec.RecordType = 0; - continue; - } - - // if a CNAME record points to itself, then don't add it to the cache - if ((m->rec.r.resrec.rrtype == kDNSType_CNAME) && SameDomainName(m->rec.r.resrec.name, &m->rec.r.resrec.rdata->u.name)) - { - LogInfo("mDNSCoreReceiveResponse: CNAME loop domain name %##s", m->rec.r.resrec.name->c); - m->rec.r.resrec.RecordType = 0; - continue; - } - - // When we receive uDNS LLQ responses, we assume a long cache lifetime -- - // In the case of active LLQs, we'll get remove events when the records actually do go away - // In the case of polling LLQs, we assume the record remains valid until the next poll - if (!mDNSOpaque16IsZero(response->h.id)) - m->rec.r.resrec.rroriginalttl = GetEffectiveTTL(LLQType, m->rec.r.resrec.rroriginalttl); - - // If response was not sent via LL multicast, - // then see if it answers a recent query of ours, which would also make it acceptable for caching. - if (!ResponseMCast) - { - if (LLQType) - { - // For Long Lived queries that are both sent over UDP and Private TCP, LLQType is set. - // Even though it is AcceptableResponse, we need a matching DNSServer pointer for the - // queries to get ADD/RMV events. To lookup the question, we can't use - // ExpectingUnicastResponseForRecord as the port numbers don't match. uDNS_recvLLQRespose - // has already matched the question using the 64 bit Id in the packet and we use that here. - - if (llqMatch != mDNSNULL) m->rec.r.resrec.rDNSServer = uDNSServer = llqMatch->qDNSServer; - } - else if (!AcceptableResponse || !dstaddr) - { - // For responses that come over TCP (Responses that can't fit within UDP) or TLS (Private queries - // that are not long lived e.g., AAAA lookup in a Private domain), it is indicated by !dstaddr. - // Even though it is AcceptableResponse, we still need a DNSServer pointer for the resource records that - // we create. - - DNSQuestion *q = ExpectingUnicastResponseForRecord(m, srcaddr, ResponseSrcLocal, dstport, response->h.id, &m->rec.r, !dstaddr); - - // Intialize the DNS server on the resource record which will now filter what questions we answer with - // this record. - // - // We could potentially lookup the DNS server based on the source address, but that may not work always - // and that's why ExpectingUnicastResponseForRecord does not try to verify whether the response came - // from the DNS server that queried. We follow the same logic here. If we can find a matching quetion based - // on the "id" and "source port", then this response answers the question and assume the response - // came from the same DNS server that we sent the query to. - - if (q != mDNSNULL) - { - AcceptableResponse = mDNStrue; - if (!InterfaceID) - { - debugf("mDNSCoreReceiveResponse: InterfaceID %p %##s (%s)", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); - m->rec.r.resrec.rDNSServer = uDNSServer = q->qDNSServer; - } - } - else - { - // If we can't find a matching question, we need to see whether we have seen records earlier that matched - // the question. The code below does that. So, make this record unacceptable for now - if (!InterfaceID) - { - debugf("mDNSCoreReceiveResponse: Can't find question for record name %##s", m->rec.r.resrec.name->c); - AcceptableResponse = mDNSfalse; - } - } - } - } - - // 1. Check that this packet resource record does not conflict with any of ours - if (mDNSOpaque16IsZero(response->h.id) && m->rec.r.resrec.rrtype != kDNSType_NSEC) - { - if (m->CurrentRecord) - LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); - m->CurrentRecord = m->ResourceRecords; - while (m->CurrentRecord) - { - AuthRecord *rr = m->CurrentRecord; - m->CurrentRecord = rr->next; - // We accept all multicast responses, and unicast responses resulting from queries we issued - // For other unicast responses, this code accepts them only for responses with an - // (apparently) local source address that pertain to a record of our own that's in probing state - if (!AcceptableResponse && !(ResponseSrcLocal && rr->resrec.RecordType == kDNSRecordTypeUnique)) continue; - - if (PacketRRMatchesSignature(&m->rec.r, rr)) // If interface, name, type (if shared record) and class match... - { - // ... check to see if type and rdata are identical - if (IdenticalSameNameRecord(&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 (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; rr->ImmedUnicast = mDNSfalse; } - } - else - { - if (rr->ImmedAnswer == mDNSNULL) { rr->ImmedAnswer = InterfaceID; m->NextScheduledResponse = m->timenow; } - else if (rr->ImmedAnswer != InterfaceID) { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; } - } - } - // 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)) - { - LogInfo("mDNSCoreReceiveResponse: Pkt Record: %08lX %s", m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r)); - LogInfo("mDNSCoreReceiveResponse: Our Record: %08lX %s", rr-> resrec.rdatahash, ARDisplayString(m, rr)); - - // 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 - if (rr->DependentOn) - { - while (rr->DependentOn) rr = rr->DependentOn; - LogInfo("mDNSCoreReceiveResponse: Dep Record: %08lX %s", rr-> resrec.rdatahash, ARDisplayString(m, rr)); - } - - // If we've just whacked this record's ProbeCount, don't need to do it again - if (rr->ProbeCount > DefaultProbeCountForTypeUnique) - LogInfo("mDNSCoreReceiveResponse: Already reset to Probing: %s", ARDisplayString(m, rr)); - else if (rr->ProbeCount == DefaultProbeCountForTypeUnique) - LogMsg("mDNSCoreReceiveResponse: Ignoring response received before we even began probing: %s", ARDisplayString(m, rr)); - else - { - LogMsg("mDNSCoreReceiveResponse: Received from %#a:%d %s", srcaddr, mDNSVal16(srcport), CRDisplayString(m, &m->rec.r)); - // If we'd previously verified this record, put it back to probing state and try again - if (rr->resrec.RecordType == kDNSRecordTypeVerified) - { - LogMsg("mDNSCoreReceiveResponse: Resetting to Probing: %s", ARDisplayString(m, rr)); - rr->resrec.RecordType = kDNSRecordTypeUnique; - // We set ProbeCount to one more than the usual value so we know we've already touched this record. - // This is because our single probe for "example-name.local" could yield a response with (say) two A records and - // three AAAA records in it, and we don't want to call RecordProbeFailure() five times and count that as five conflicts. - // This special value is recognised and reset to DefaultProbeCountForTypeUnique in SendQueries(). - rr->ProbeCount = DefaultProbeCountForTypeUnique + 1; - rr->AnnounceCount = InitialAnnounceCount; - 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) - { - LogMsg("mDNSCoreReceiveResponse: ProbeCount %d; will deregister %s", rr->ProbeCount, ARDisplayString(m, rr)); - 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, or there are two machines on the - // network using the same IP address.) This is simply a misconfiguration, and there's nothing we can do - // to fix it -- e.g. it's not our job to be trying to change the machine's IP address. We just discard our - // record to avoid continued conflicts (as we do for a conflict on our Unique records) and get on with life. - else if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique) - { - LogMsg("mDNSCoreReceiveResponse: Unexpected conflict discarding %s", ARDisplayString(m, rr)); - mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); - } - else - LogMsg("mDNSCoreReceiveResponse: Unexpected record type %X %s", rr->resrec.RecordType, ARDisplayString(m, rr)); - } - } - // 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; } - } - } - } - - if (!AcceptableResponse) - { - const CacheRecord *cr; - for (cr = CacheFlushRecords; cr != (CacheRecord*)1; cr = cr->NextInCFList) - { - domainname *target = GetRRDomainNameTarget(&cr->resrec); - // When we issue a query for A record, the response might contain both a CNAME and A records. Only the CNAME would - // match the question and we already created a cache entry in the previous pass of this loop. Now when we process - // the A record, it does not match the question because the record name here is the CNAME. Hence we try to - // match with the previous records to make it an AcceptableResponse. We have to be careful about setting the - // DNSServer value that we got in the previous pass. This can happen for other record types like SRV also. - - if (target && cr->resrec.rdatahash == m->rec.r.resrec.namehash && SameDomainName(target, m->rec.r.resrec.name)) - { - debugf("mDNSCoreReceiveResponse: Found a matching entry for %##s in the CacheFlushRecords", m->rec.r.resrec.name->c); - AcceptableResponse = mDNStrue; - m->rec.r.resrec.rDNSServer = uDNSServer; - break; - } - } - } - - // 2. See if we want to add this packet resource record to our cache - // We only try to cache answers if we have a cache to put them in - // Also, we ignore any apparent attempts at cache poisoning unicast to us that do not answer any outstanding active query - if (!AcceptableResponse) LogInfo("mDNSCoreReceiveResponse ignoring %s", CRDisplayString(m, &m->rec.r)); - if (m->rrcache_size && AcceptableResponse) - { - const mDNSu32 slot = HashSlot(m->rec.r.resrec.name); - CacheGroup *cg = CacheGroupForRecord(m, slot, &m->rec.r.resrec); - CacheRecord *rr; - - // 2a. Check if this packet resource record is already in our cache - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) - { - mDNSBool match = !InterfaceID ? m->rec.r.resrec.rDNSServer == rr->resrec.rDNSServer : rr->resrec.InterfaceID == InterfaceID; - // If we found this exact resource record, refresh its TTL - if (match && IdenticalSameNameRecord(&m->rec.r.resrec, &rr->resrec)) - { - if (m->rec.r.resrec.rdlength > InlineCacheRDSize) - verbosedebugf("Found record size %5d interface %p already in cache: %s", - m->rec.r.resrec.rdlength, InterfaceID, CRDisplayString(m, &m->rec.r)); - - 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 && LLQType != uDNS_LLQ_Events) - { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; } - - // If this packet record is marked unique, and our previous cached copy was not, then fix it - if (!(rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)) - { - DNSQuestion *q; - for (q = m->Questions; q; q=q->next) if (ResourceRecordAnswersQuestion(&rr->resrec, q)) q->UniqueAnswers++; - rr->resrec.RecordType = m->rec.r.resrec.RecordType; - } - } - - if (!SameRDataBody(&m->rec.r.resrec, &rr->resrec.rdata->u, SameDomainNameCS)) - { - // If the rdata of the packet record differs in name capitalization from the record in our cache - // then mDNSPlatformMemSame will detect this. In this case, throw the old record away, so that clients get - // a 'remove' event for the record with the old capitalization, and then an 'add' event for the new one. - // mDNS -F returns the same domain multiple times with different casing - rr->resrec.rroriginalttl = 0; - rr->TimeRcvd = m->timenow; - rr->UnansweredQueries = MaxUnansweredQueries; - SetNextCacheCheckTimeForRecord(m, rr); - LogInfo("Discarding due to domainname case change old: %s", CRDisplayString(m,rr)); - LogInfo("Discarding due to domainname case change new: %s", CRDisplayString(m,&m->rec.r)); - LogInfo("Discarding due to domainname case change in %d slot %3d in %d %d", - NextCacheCheckEvent(rr) - m->timenow, slot, m->rrcache_nextcheck[slot] - m->timenow, m->NextCacheCheck - m->timenow); - // DO NOT break out here -- we want to continue as if we never found it - } - else if (m->rec.r.resrec.rroriginalttl > 0) - { - DNSQuestion *q; - //if (rr->resrec.rroriginalttl == 0) LogMsg("uDNS rescuing %s", CRDisplayString(m, rr)); - RefreshCacheRecord(m, rr, m->rec.r.resrec.rroriginalttl); - - // We have to reset the question interval to MaxQuestionInterval so that we don't keep - // polling the network once we get a valid response back. For the first time when a new - // cache entry is created, AnswerCurrentQuestionWithResourceRecord does that. - // Subsequently, if we reissue questions from within the mDNSResponder e.g., DNS server - // configuration changed, without flushing the cache, we reset the question interval here. - // Currently, we do this for for both multicast and unicast questions as long as the record - // type is unique. For unicast, resource record is always unique and for multicast it is - // true for records like A etc. but not for PTR. - if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) - { - for (q = m->Questions; q; q=q->next) - { - if (!q->DuplicateOf && !q->LongLived && - ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) - { - ResetQuestionState(m, q); - debugf("mDNSCoreReceiveResponse: Set MaxQuestionInterval for %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); - break; // Why break here? Aren't there other questions we might want to look at?-- SC July 2010 - } - } - } - break; - } - else - { - // If the packet TTL is zero, that means we're deleting this record. - // To give other hosts on the network a chance to protest, we push the deletion - // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries. - // Otherwise, we'll do final queries for this record at 80% and 90% of its apparent - // lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth. - // If record's current expiry time is more than a second from now, we set it to expire in one second. - // If the record is already going to expire in less than one second anyway, we leave it alone -- - // we don't want to let the goodbye packet *extend* the record's lifetime in our cache. - debugf("DE for %s", CRDisplayString(m, rr)); - if (RRExpireTime(rr) - m->timenow > mDNSPlatformOneSecond) - { - rr->resrec.rroriginalttl = 1; - rr->TimeRcvd = m->timenow; - rr->UnansweredQueries = MaxUnansweredQueries; - SetNextCacheCheckTimeForRecord(m, rr); - } - break; - } - } - } - - // If packet resource record not in our cache, add it now - // (unless it is just a deletion of a record we never had, in which case we don't care) - if (!rr && m->rec.r.resrec.rroriginalttl > 0) - { - const mDNSBool AddToCFList = (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && (LLQType != uDNS_LLQ_Events); - const mDNSs32 delay = AddToCFList ? NonZeroTime(m->timenow + mDNSPlatformOneSecond) : - CheckForSoonToExpireRecords(m, m->rec.r.resrec.name, m->rec.r.resrec.namehash, slot); - // If unique, assume we may have to delay delivery of this 'add' event. - // Below, where we walk the CacheFlushRecords list, we either call CacheRecordDeferredAdd() - // to immediately to generate answer callbacks, or we call ScheduleNextCacheCheckTime() - // to schedule an mDNS_Execute task at the appropriate time. - rr = CreateNewCacheEntry(m, slot, cg, delay); - if (rr) - { - if (AddToCFList) { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; } - else if (rr->DelayDelivery) ScheduleNextCacheCheckTime(m, slot, rr->DelayDelivery); - } - } - } - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - } + const DNSMessage *const response, const mDNSu8 *end, + const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, + const mDNSInterfaceID InterfaceID) +{ + int i; + mDNSBool ResponseMCast = dstaddr && mDNSAddrIsDNSMulticast(dstaddr); + mDNSBool ResponseSrcLocal = !srcaddr || mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); + DNSQuestion *llqMatch = mDNSNULL; + DNSQuestion *unicastQuestion = mDNSNULL; + uDNS_LLQType LLQType = uDNS_recvLLQResponse(m, response, end, srcaddr, srcport, &llqMatch); + + // "(CacheRecord*)1" is a special (non-zero) end-of-list marker + // We use this non-zero marker so that records in our CacheFlushRecords list will always have NextInCFList + // set non-zero, and that tells GetCacheEntity() that they're not, at this moment, eligible for recycling. + CacheRecord *CacheFlushRecords = (CacheRecord*)1; + CacheRecord **cfp = &CacheFlushRecords; + CacheRecord *NSECRecords = mDNSNULL; + CacheRecord *NSECCachePtr = mDNSNULL; + CacheRecord **nsecp = &NSECRecords; + mDNSBool nseclist; + mDNSu8 rcode = '\0'; + + // 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 firstauthority = response->h.numAnswers; + int firstadditional = firstauthority + response->h.numAuthorities; + int totalrecords = firstadditional + response->h.numAdditionals; + const mDNSu8 *ptr = response->data; + DNSServer *uDNSServer = mDNSNULL; + + debugf("Received Response from %#-15a addressed to %#-15a on %p with " + "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes LLQType %d", + 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", end - response->data, LLQType); + + // According to RFC 2181 + // When a DNS client receives a reply with TC + // set, it should ignore that response, and query again, using a + // mechanism, such as a TCP connection, that will permit larger replies. + // It feels wrong to be throwing away data after the network went to all the trouble of delivering it to us, but + // delivering some records of the RRSet first and then the remainder a couple of milliseconds later was causing + // failures in our Microsoft Active Directory client, which expects to get the entire set of answers at once. + // Can't bind to Active Directory + // In addition, if the client immediately canceled its query after getting the initial partial response, then we'll + // abort our TCP connection, and not complete the operation, and end up with an incomplete RRSet in our cache. + // Next time there's a query for this RRSet we'll see answers in our cache, and assume we have the whole RRSet already, + // and not even do the TCP query. + // Accordingly, if we get a uDNS reply with kDNSFlag0_TC set, we bail out and wait for the TCP response containing the entire RRSet. + if (!InterfaceID && (response->h.flags.b[0] & kDNSFlag0_TC)) return; + + if (LLQType == uDNS_LLQ_Ignore) return; + + // 1. We ignore questions (if any) in mDNS response packets + // 2. If this is an LLQ response, we handle it much the same + // 3. If we get a uDNS UDP response with the TC (truncated) bit set, then we can't treat this + // answer as being the authoritative complete RRSet, and respond by deleting all other + // matching cache records that don't appear in this packet. + // Otherwise, this is a authoritative uDNS answer, so arrange for any stale records to be purged + if (ResponseMCast || LLQType == uDNS_LLQ_Events || (response->h.flags.b[0] & kDNSFlag0_TC)) + ptr = LocateAnswers(response, end); + // Otherwise, for one-shot queries, any answers in our cache that are not also contained + // in this response packet are immediately deemed to be invalid. + else + { + mDNSBool failure, returnEarly; + rcode = (mDNSu8)(response->h.flags.b[1] & kDNSFlag1_RC_Mask); + failure = !(rcode == kDNSFlag1_RC_NoErr || rcode == kDNSFlag1_RC_NXDomain || rcode == kDNSFlag1_RC_NotAuth); + returnEarly = mDNSfalse; + // We could possibly combine this with the similar loop at the end of this function -- + // instead of tagging cache records here and then rescuing them if we find them in the answer section, + // we could instead use the "m->PktNum" mechanism to tag each cache record with the packet number in + // which it was received (or refreshed), and then at the end if we find any cache records which + // answer questions in this packet's question section, but which aren't tagged with this packet's + // packet number, then we deduce they are old and delete them + for (i = 0; i < response->h.numQuestions && ptr && ptr < end; i++) + { + DNSQuestion q, *qptr = mDNSNULL; + ptr = getQuestion(response, ptr, end, InterfaceID, &q); + if (ptr && (qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &q, !dstaddr))) + { + if (!failure) + { + CacheRecord *rr; + // Remember the unicast question that we found, which we use to make caching + // decisions later on in this function + const mDNSu32 slot = HashSlot(&q.qname); + CacheGroup *cg = CacheGroupForName(m, slot, q.qnamehash, &q.qname); + if (!mDNSOpaque16IsZero(response->h.id)) unicastQuestion = qptr; + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + if (SameNameRecordAnswersQuestion(&rr->resrec, qptr)) + { + debugf("uDNS marking %p %##s (%s) %p %s", q.InterfaceID, q.qname.c, DNSTypeName(q.qtype), + rr->resrec.InterfaceID, CRDisplayString(m, rr)); + // Don't want to disturb rroriginalttl here, because code below might need it for the exponential backoff doubling algorithm + rr->TimeRcvd = m->timenow - TicksTTL(rr) - 1; + rr->UnansweredQueries = MaxUnansweredQueries; + } + } + else + { + if (qptr) + { + LogInfo("mDNSCoreReceiveResponse: Server %p responded with code %d to query %##s (%s)", qptr->qDNSServer, rcode, q.qname.c, DNSTypeName(q.qtype)); + PenalizeDNSServer(m, qptr); + } + returnEarly = mDNStrue; + } + } + } + if (returnEarly) + { + LogInfo("Ignoring %2d Answer%s %2d Authorit%s %2d Additional%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"); + // not goto exit because we won't have any CacheFlushRecords and we do not want to + // generate negative cache entries (we want to query the next server) + return; + } + } + + for (i = 0; i < totalrecords && ptr && ptr < end; i++) + { + // All responses sent via LL multicast are acceptable for caching + // All responses received over our outbound TCP connections are acceptable for caching + mDNSBool AcceptableResponse = ResponseMCast || !dstaddr || LLQType; + // (Note that just because we are willing to cache something, that doesn't necessarily make it a trustworthy answer + // to any specific question -- any code reading records from the cache needs to make that determination for itself.) + + const mDNSu8 RecordType = + (i < firstauthority ) ? (mDNSu8)kDNSRecordTypePacketAns : + (i < firstadditional) ? (mDNSu8)kDNSRecordTypePacketAuth : (mDNSu8)kDNSRecordTypePacketAdd; + 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 + if (m->rec.r.resrec.RecordType == kDNSRecordTypePacketNegative) { m->rec.r.resrec.RecordType = 0; continue; } + + // Don't want to cache OPT or TSIG pseudo-RRs + if (m->rec.r.resrec.rrtype == kDNSType_TSIG) { m->rec.r.resrec.RecordType = 0; continue; } + if (m->rec.r.resrec.rrtype == kDNSType_OPT) + { + const rdataOPT *opt; + const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength]; + // Find owner sub-option(s). We verify that the MAC is non-zero, otherwise we could inadvertently + // delete all our own AuthRecords (which are identified by having zero MAC tags on them). + for (opt = &m->rec.r.resrec.rdata->u.opt[0]; opt < e; opt++) + if (opt->opt == kDNSOpt_Owner && opt->u.owner.vers == 0 && opt->u.owner.HMAC.l[0]) + { + ClearProxyRecords(m, &opt->u.owner, m->DuplicateRecords); + ClearProxyRecords(m, &opt->u.owner, m->ResourceRecords); + } + m->rec.r.resrec.RecordType = 0; + continue; + } + + // if a CNAME record points to itself, then don't add it to the cache + if ((m->rec.r.resrec.rrtype == kDNSType_CNAME) && SameDomainName(m->rec.r.resrec.name, &m->rec.r.resrec.rdata->u.name)) + { + LogInfo("mDNSCoreReceiveResponse: CNAME loop domain name %##s", m->rec.r.resrec.name->c); + m->rec.r.resrec.RecordType = 0; + continue; + } + + // When we receive uDNS LLQ responses, we assume a long cache lifetime -- + // In the case of active LLQs, we'll get remove events when the records actually do go away + // In the case of polling LLQs, we assume the record remains valid until the next poll + if (!mDNSOpaque16IsZero(response->h.id)) + m->rec.r.resrec.rroriginalttl = GetEffectiveTTL(LLQType, m->rec.r.resrec.rroriginalttl); + + // If response was not sent via LL multicast, + // then see if it answers a recent query of ours, which would also make it acceptable for caching. + if (!ResponseMCast) + { + if (LLQType) + { + // For Long Lived queries that are both sent over UDP and Private TCP, LLQType is set. + // Even though it is AcceptableResponse, we need a matching DNSServer pointer for the + // queries to get ADD/RMV events. To lookup the question, we can't use + // ExpectingUnicastResponseForRecord as the port numbers don't match. uDNS_recvLLQRespose + // has already matched the question using the 64 bit Id in the packet and we use that here. + + if (llqMatch != mDNSNULL) m->rec.r.resrec.rDNSServer = uDNSServer = llqMatch->qDNSServer; + } + else if (!AcceptableResponse || !dstaddr) + { + // For responses that come over TCP (Responses that can't fit within UDP) or TLS (Private queries + // that are not long lived e.g., AAAA lookup in a Private domain), it is indicated by !dstaddr. + // Even though it is AcceptableResponse, we still need a DNSServer pointer for the resource records that + // we create. + + DNSQuestion *q = ExpectingUnicastResponseForRecord(m, srcaddr, ResponseSrcLocal, dstport, response->h.id, &m->rec.r, !dstaddr); + + // Intialize the DNS server on the resource record which will now filter what questions we answer with + // this record. + // + // We could potentially lookup the DNS server based on the source address, but that may not work always + // and that's why ExpectingUnicastResponseForRecord does not try to verify whether the response came + // from the DNS server that queried. We follow the same logic here. If we can find a matching quetion based + // on the "id" and "source port", then this response answers the question and assume the response + // came from the same DNS server that we sent the query to. + + if (q != mDNSNULL) + { + AcceptableResponse = mDNStrue; + if (!InterfaceID) + { + debugf("mDNSCoreReceiveResponse: InterfaceID %p %##s (%s)", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); + m->rec.r.resrec.rDNSServer = uDNSServer = q->qDNSServer; + } + } + else + { + // If we can't find a matching question, we need to see whether we have seen records earlier that matched + // the question. The code below does that. So, make this record unacceptable for now + if (!InterfaceID) + { + debugf("mDNSCoreReceiveResponse: Can't find question for record name %##s", m->rec.r.resrec.name->c); + AcceptableResponse = mDNSfalse; + } + } + } + } + + // 1. Check that this packet resource record does not conflict with any of ours + if (mDNSOpaque16IsZero(response->h.id) && m->rec.r.resrec.rrtype != kDNSType_NSEC) + { + if (m->CurrentRecord) + LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + AuthRecord *rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + // We accept all multicast responses, and unicast responses resulting from queries we issued + // For other unicast responses, this code accepts them only for responses with an + // (apparently) local source address that pertain to a record of our own that's in probing state + if (!AcceptableResponse && !(ResponseSrcLocal && rr->resrec.RecordType == kDNSRecordTypeUnique)) continue; + + if (PacketRRMatchesSignature(&m->rec.r, rr)) // If interface, name, type (if shared record) and class match... + { + // ... check to see if type and rdata are identical + if (IdenticalSameNameRecord(&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 (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; rr->ImmedUnicast = mDNSfalse; } + } + else + { + if (rr->ImmedAnswer == mDNSNULL) { rr->ImmedAnswer = InterfaceID; m->NextScheduledResponse = m->timenow; } + else if (rr->ImmedAnswer != InterfaceID) { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; } + } + } + // 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)) + { + LogInfo("mDNSCoreReceiveResponse: Pkt Record: %08lX %s", m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r)); + LogInfo("mDNSCoreReceiveResponse: Our Record: %08lX %s", rr->resrec.rdatahash, ARDisplayString(m, rr)); + + // 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 + if (rr->DependentOn) + { + while (rr->DependentOn) rr = rr->DependentOn; + LogInfo("mDNSCoreReceiveResponse: Dep Record: %08lX %s", rr->resrec.rdatahash, ARDisplayString(m, rr)); + } + + // If we've just whacked this record's ProbeCount, don't need to do it again + if (rr->ProbeCount > DefaultProbeCountForTypeUnique) + LogInfo("mDNSCoreReceiveResponse: Already reset to Probing: %s", ARDisplayString(m, rr)); + else if (rr->ProbeCount == DefaultProbeCountForTypeUnique) + LogMsg("mDNSCoreReceiveResponse: Ignoring response received before we even began probing: %s", ARDisplayString(m, rr)); + else + { + LogMsg("mDNSCoreReceiveResponse: Received from %#a:%d %s", srcaddr, mDNSVal16(srcport), CRDisplayString(m, &m->rec.r)); + // If we'd previously verified this record, put it back to probing state and try again + if (rr->resrec.RecordType == kDNSRecordTypeVerified) + { + LogMsg("mDNSCoreReceiveResponse: Resetting to Probing: %s", ARDisplayString(m, rr)); + rr->resrec.RecordType = kDNSRecordTypeUnique; + // We set ProbeCount to one more than the usual value so we know we've already touched this record. + // This is because our single probe for "example-name.local" could yield a response with (say) two A records and + // three AAAA records in it, and we don't want to call RecordProbeFailure() five times and count that as five conflicts. + // This special value is recognised and reset to DefaultProbeCountForTypeUnique in SendQueries(). + rr->ProbeCount = DefaultProbeCountForTypeUnique + 1; + rr->AnnounceCount = InitialAnnounceCount; + 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) + { + // Before we call deregister, check if this is a packet we registered with the sleep proxy. + if (!mDNSCoreRegisteredProxyRecord(m, rr)) + { + LogMsg("mDNSCoreReceiveResponse: ProbeCount %d; will deregister %s", rr->ProbeCount, ARDisplayString(m, rr)); + 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, or there are two machines on the + // network using the same IP address.) This is simply a misconfiguration, and there's nothing we can do + // to fix it -- e.g. it's not our job to be trying to change the machine's IP address. We just discard our + // record to avoid continued conflicts (as we do for a conflict on our Unique records) and get on with life. + else if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique) + { + LogMsg("mDNSCoreReceiveResponse: Unexpected conflict discarding %s", ARDisplayString(m, rr)); + mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); + } + else + LogMsg("mDNSCoreReceiveResponse: Unexpected record type %X %s", rr->resrec.RecordType, ARDisplayString(m, rr)); + } + } + // 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; } + } + } + } + + nseclist = mDNSfalse; + if (!AcceptableResponse) + { + AcceptableResponse = IsResponseAcceptable(m, CacheFlushRecords, unicastQuestion, &nseclist); + if (AcceptableResponse) m->rec.r.resrec.rDNSServer = uDNSServer; + } + + // 2. See if we want to add this packet resource record to our cache + // We only try to cache answers if we have a cache to put them in + // Also, we ignore any apparent attempts at cache poisoning unicast to us that do not answer any outstanding active query + if (!AcceptableResponse) LogInfo("mDNSCoreReceiveResponse ignoring %s", CRDisplayString(m, &m->rec.r)); + if (m->rrcache_size && AcceptableResponse) + { + const mDNSu32 slot = HashSlot(m->rec.r.resrec.name); + CacheGroup *cg = CacheGroupForRecord(m, slot, &m->rec.r.resrec); + CacheRecord *rr; + + // 2a. Check if this packet resource record is already in our cache + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + { + mDNSBool match; + // Resource record received via unicast, the resGroupID should match ? + if (!InterfaceID) + { + mDNSu16 id1 = (rr->resrec.rDNSServer ? rr->resrec.rDNSServer->resGroupID : 0); + mDNSu16 id2 = (m->rec.r.resrec.rDNSServer ? m->rec.r.resrec.rDNSServer->resGroupID : 0); + match = (id1 == id2); + } + else + match = (rr->resrec.InterfaceID == InterfaceID); + // If we found this exact resource record, refresh its TTL + if (match && IdenticalSameNameRecord(&m->rec.r.resrec, &rr->resrec)) + { + if (m->rec.r.resrec.rdlength > InlineCacheRDSize) + verbosedebugf("Found record size %5d interface %p already in cache: %s", + m->rec.r.resrec.rdlength, InterfaceID, CRDisplayString(m, &m->rec.r)); + + 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 && LLQType != uDNS_LLQ_Events) + { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; } + + // If this packet record is marked unique, and our previous cached copy was not, then fix it + if (!(rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)) + { + DNSQuestion *q; + for (q = m->Questions; q; q=q->next) if (ResourceRecordAnswersQuestion(&rr->resrec, q)) q->UniqueAnswers++; + rr->resrec.RecordType = m->rec.r.resrec.RecordType; + } + } + + if (!SameRDataBody(&m->rec.r.resrec, &rr->resrec.rdata->u, SameDomainNameCS)) + { + // If the rdata of the packet record differs in name capitalization from the record in our cache + // then mDNSPlatformMemSame will detect this. In this case, throw the old record away, so that clients get + // a 'remove' event for the record with the old capitalization, and then an 'add' event for the new one. + // mDNS -F returns the same domain multiple times with different casing + rr->resrec.rroriginalttl = 0; + rr->TimeRcvd = m->timenow; + rr->UnansweredQueries = MaxUnansweredQueries; + SetNextCacheCheckTimeForRecord(m, rr); + LogInfo("Discarding due to domainname case change old: %s", CRDisplayString(m,rr)); + LogInfo("Discarding due to domainname case change new: %s", CRDisplayString(m,&m->rec.r)); + LogInfo("Discarding due to domainname case change in %d slot %3d in %d %d", + NextCacheCheckEvent(rr) - m->timenow, slot, m->rrcache_nextcheck[slot] - m->timenow, m->NextCacheCheck - m->timenow); + // DO NOT break out here -- we want to continue as if we never found it + } + else if (m->rec.r.resrec.rroriginalttl > 0) + { + DNSQuestion *q; + //if (rr->resrec.rroriginalttl == 0) LogMsg("uDNS rescuing %s", CRDisplayString(m, rr)); + RefreshCacheRecord(m, rr, m->rec.r.resrec.rroriginalttl); + + // If we may have NSEC records returned with the answer (which we don't know yet as it + // has not been processed), we need to cache them along with the first cache + // record in the list that answers the question so that it can be used for validation + // later. + if (response->h.numAnswers && unicastQuestion && !NSECCachePtr) + { + LogInfo("mDNSCoreReceiveResponse: rescuing RR %s", CRDisplayString(m, rr)); + NSECCachePtr = rr; + } + // We have to reset the question interval to MaxQuestionInterval so that we don't keep + // polling the network once we get a valid response back. For the first time when a new + // cache entry is created, AnswerCurrentQuestionWithResourceRecord does that. + // Subsequently, if we reissue questions from within the mDNSResponder e.g., DNS server + // configuration changed, without flushing the cache, we reset the question interval here. + // Currently, we do this for for both multicast and unicast questions as long as the record + // type is unique. For unicast, resource record is always unique and for multicast it is + // true for records like A etc. but not for PTR. + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) + { + for (q = m->Questions; q; q=q->next) + { + if (!q->DuplicateOf && !q->LongLived && + ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) + { + ResetQuestionState(m, q); + debugf("mDNSCoreReceiveResponse: Set MaxQuestionInterval for %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + break; // Why break here? Aren't there other questions we might want to look at?-- SC July 2010 + } + } + } + break; + } + else + { + // If the packet TTL is zero, that means we're deleting this record. + // To give other hosts on the network a chance to protest, we push the deletion + // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries. + // Otherwise, we'll do final queries for this record at 80% and 90% of its apparent + // lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth. + // If record's current expiry time is more than a second from now, we set it to expire in one second. + // If the record is already going to expire in less than one second anyway, we leave it alone -- + // we don't want to let the goodbye packet *extend* the record's lifetime in our cache. + debugf("DE for %s", CRDisplayString(m, rr)); + if (RRExpireTime(rr) - m->timenow > mDNSPlatformOneSecond) + { + rr->resrec.rroriginalttl = 1; + rr->TimeRcvd = m->timenow; + rr->UnansweredQueries = MaxUnansweredQueries; + SetNextCacheCheckTimeForRecord(m, rr); + } + break; + } + } + } + + // If packet resource record not in our cache, add it now + // (unless it is just a deletion of a record we never had, in which case we don't care) + if (!rr && m->rec.r.resrec.rroriginalttl > 0) + { + const mDNSBool AddToCFList = (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && (LLQType != uDNS_LLQ_Events); + const mDNSs32 delay = AddToCFList ? NonZeroTime(m->timenow + mDNSPlatformOneSecond) : + CheckForSoonToExpireRecords(m, m->rec.r.resrec.name, m->rec.r.resrec.namehash, slot); + // If unique, assume we may have to delay delivery of this 'add' event. + // Below, where we walk the CacheFlushRecords list, we either call CacheRecordDeferredAdd() + // to immediately to generate answer callbacks, or we call ScheduleNextCacheCheckTime() + // to schedule an mDNS_Execute task at the appropriate time. + rr = CreateNewCacheEntry(m, slot, cg, delay, !nseclist, srcaddr); + if (rr) + { + // NSEC Records and its signatures are cached with the negative cache entry + // which we should be creating below. It is also needed in the wildcard + // expanded answer case and in that case it is cached along with the answer. + if (nseclist) { *nsecp = rr; nsecp = &rr->next; } + else if (AddToCFList) { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; } + else if (rr->DelayDelivery) ScheduleNextCacheCheckTime(m, slot, rr->DelayDelivery); + } + } + } + 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*)1) - { - CacheRecord *r1 = CacheFlushRecords, *r2; - const mDNSu32 slot = HashSlot(r1->resrec.name); - const CacheGroup *cg = CacheGroupForRecord(m, slot, &r1->resrec); - CacheFlushRecords = CacheFlushRecords->NextInCFList; - r1->NextInCFList = mDNSNULL; - - // Look for records in the cache with the same signature as this new one with the cache flush - // bit set, and either (a) if they're fresh, just make sure the whole RRSet has the same TTL - // (as required by DNS semantics) or (b) if they're old, mark them for deletion in one second. - // We make these TTL adjustments *only* for records that still have *more* than one second - // remaining to live. Otherwise, a record that we tagged for deletion half a second ago - // (and now has half a second remaining) could inadvertently get its life extended, by either - // (a) if we got an explicit goodbye packet half a second ago, the record would be considered - // "fresh" and would be incorrectly resurrected back to the same TTL as the rest of the RRSet, - // or (b) otherwise, the record would not be fully resurrected, but would be reset to expire - // in one second, thereby inadvertently delaying its actual expiration, instead of hastening it. - // If this were to happen repeatedly, the record's expiration could be deferred indefinitely. - // To avoid this, we need to ensure that the cache flushing operation will only act to - // *decrease* a record's remaining lifetime, never *increase* it. - for (r2 = cg ? cg->members : mDNSNULL; r2; r2=r2->next) - // For Unicast (null InterfaceID) the DNSservers should also match - if ((r1->resrec.InterfaceID == r2->resrec.InterfaceID) && - (r1->resrec.InterfaceID || (r1->resrec.rDNSServer == r2->resrec.rDNSServer)) && - r1->resrec.rrtype == r2->resrec.rrtype && - r1->resrec.rrclass == r2->resrec.rrclass) - { - // If record is recent, just ensure the whole RRSet has the same TTL (as required by DNS semantics) - // else, if record is old, mark it to be flushed - if (m->timenow - r2->TimeRcvd < mDNSPlatformOneSecond && RRExpireTime(r2) - m->timenow > mDNSPlatformOneSecond) - { - // If we find mismatched TTLs in an RRSet, correct them. - // We only do this for records with a TTL of 2 or higher. It's possible to have a - // goodbye announcement with the cache flush bit set (or a case-change on record rdata, - // which we treat as a goodbye followed by an addition) and in that case it would be - // inappropriate to synchronize all the other records to a TTL of 0 (or 1). - // We suppress the message for the specific case of correcting from 240 to 60 for type TXT, - // because certain early Bonjour devices are known to have this specific mismatch, and - // there's no point filling syslog with messages about something we already know about. - // We also don't log this for uDNS responses, since a caching name server is obliged - // to give us an aged TTL to correct for how long it has held the record, - // so our received TTLs are expected to vary in that case - if (r2->resrec.rroriginalttl != r1->resrec.rroriginalttl && r1->resrec.rroriginalttl > 1) - { - if (!(r2->resrec.rroriginalttl == 240 && r1->resrec.rroriginalttl == 60 && r2->resrec.rrtype == kDNSType_TXT) && - mDNSOpaque16IsZero(response->h.id)) - LogInfo("Correcting TTL from %4d to %4d for %s", - r2->resrec.rroriginalttl, r1->resrec.rroriginalttl, CRDisplayString(m, r2)); - r2->resrec.rroriginalttl = r1->resrec.rroriginalttl; - } - r2->TimeRcvd = m->timenow; - } - else // else, if record is old, mark it to be flushed - { - verbosedebugf("Cache flush new %p age %d expire in %d %s", r1, m->timenow - r1->TimeRcvd, RRExpireTime(r1) - m->timenow, CRDisplayString(m, r1)); - verbosedebugf("Cache flush old %p age %d expire in %d %s", r2, m->timenow - r2->TimeRcvd, RRExpireTime(r2) - m->timenow, CRDisplayString(m, r2)); - // We set stale records to expire in one second. - // This gives the owner a chance to rescue it if necessary. - // This is important in the case of multi-homing and bridged networks: - // Suppose host X is on Ethernet. X then connects to an AirPort base station, which happens to be - // bridged onto the same Ethernet. When X announces its AirPort IP address with the cache-flush bit - // set, the AirPort packet will be bridged onto the Ethernet, and all other hosts on the Ethernet - // will promptly delete their cached copies of the (still valid) Ethernet IP address record. - // By delaying the deletion by one second, we give X a change to notice that this bridging has - // happened, and re-announce its Ethernet IP address to rescue it from deletion from all our caches. - - // We set UnansweredQueries to MaxUnansweredQueries to avoid expensive and unnecessary - // final expiration queries for this record. - - // If a record is deleted twice, first with an explicit DE record, then a second time by virtue of the cache - // flush bit on the new record replacing it, then we allow the record to be deleted immediately, without the usual - // one-second grace period. This improves responsiveness for mDNS_Update(), as used for things like iChat status updates. - // Updating TXT records is too slow - // We check for "rroriginalttl == 1" because we want to include records tagged by the "packet TTL is zero" check above, - // which sets rroriginalttl to 1, but not records tagged by the rdata case-change check, which sets rroriginalttl to 0. - if (r2->TimeRcvd == m->timenow && r2->resrec.rroriginalttl == 1 && r2->UnansweredQueries == MaxUnansweredQueries) - { - LogInfo("Cache flush for DE record %s", CRDisplayString(m, r2)); - r2->resrec.rroriginalttl = 0; - } - else if (RRExpireTime(r2) - m->timenow > mDNSPlatformOneSecond) - { - // We only set a record to expire in one second if it currently has *more* than a second to live - // If it's already due to expire in a second or less, we just leave it alone - r2->resrec.rroriginalttl = 1; - r2->UnansweredQueries = MaxUnansweredQueries; - r2->TimeRcvd = m->timenow - 1; - // We use (m->timenow - 1) instead of m->timenow, because we use that to identify records - // that we marked for deletion via an explicit DE record - } - } - SetNextCacheCheckTimeForRecord(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 no longer delaying, deliver answer now, else schedule delivery for the appropriate time - if (!r1->DelayDelivery) CacheRecordDeferredAdd(m, r1); - else ScheduleNextCacheCheckTime(m, slot, r1->DelayDelivery); - } - } - - // See if we need to generate negative cache entries for unanswered unicast questions - ptr = response->data; - for (i = 0; i < response->h.numQuestions && ptr && ptr < end; i++) - { - DNSQuestion q; - DNSQuestion *qptr = mDNSNULL; - ptr = getQuestion(response, ptr, end, InterfaceID, &q); - if (ptr && (qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &q, !dstaddr))) - { - CacheRecord *rr, *neg = mDNSNULL; - mDNSu32 slot = HashSlot(&q.qname); - CacheGroup *cg = CacheGroupForName(m, slot, q.qnamehash, &q.qname); - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) - if (SameNameRecordAnswersQuestion(&rr->resrec, qptr)) - { - // 1. If we got a fresh answer to this query, then don't need to generate a negative entry - if (RRExpireTime(rr) - m->timenow > 0) break; - // 2. If we already had a negative entry, keep track of it so we can resurrect it instead of creating a new one - if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) neg = rr; - } - // When we're doing parallel unicast and multicast queries for dot-local names (for supporting Microsoft - // Active Directory sites) we don't want to waste memory making negative cache entries for all the unicast answers. - // Otherwise we just fill up our cache with negative entries for just about every single multicast name we ever look up - // (since the Microsoft Active Directory server is going to assert that pretty much every single multicast name doesn't exist). - // This is not only a waste of memory, but there's also the problem of those negative entries confusing us later -- e.g. we - // suppress sending our mDNS query packet because we think we already have a valid (negative) answer to that query in our cache. - // The one exception is that we *DO* want to make a negative cache entry for "local. SOA", for the (common) case where we're - // *not* on a Microsoft Active Directory network, and there is no authoritative server for "local". Note that this is not - // in conflict with the mDNS spec, because that spec says, "Multicast DNS Zones have no SOA record," so it's okay to cache - // negative answers for "local. SOA" from a uDNS server, because the mDNS spec already says that such records do not exist :-) - if (!InterfaceID && q.qtype != kDNSType_SOA && IsLocalDomain(&q.qname)) - { - // If we did not find a positive answer and we can append search domains to this question, - // generate a negative response (without creating a cache entry) to append search domains. - if (qptr->AppendSearchDomains && !rr) - { - LogInfo("mDNSCoreReceiveResponse: Generate negative response for %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); - m->CurrentQuestion = qptr; - GenerateNegativeResponse(m); - m->CurrentQuestion = mDNSNULL; - } - else LogInfo("mDNSCoreReceiveResponse: Skipping check to see if we need to generate a negative cache entry for %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); - } - else - { - if (!rr) - { - // We start off assuming a negative caching TTL of 60 seconds - // but then look to see if we can find an SOA authority record to tell us a better value we should be using - mDNSu32 negttl = 60; - int repeat = 0; - const domainname *name = &q.qname; - mDNSu32 hash = q.qnamehash; - - // Special case for our special Microsoft Active Directory "local SOA" check. - // Some cheap home gateways don't include an SOA record in the authority section when - // they send negative responses, so we don't know how long to cache the negative result. - // Because we don't want to keep hitting the root name servers with our query to find - // if we're on a network using Microsoft Active Directory using "local" as a private - // internal top-level domain, we make sure to cache the negative result for at least one day. - if (q.qtype == kDNSType_SOA && SameDomainName(&q.qname, &localdomain)) negttl = 60 * 60 * 24; - - // If we're going to make (or update) a negative entry, then look for the appropriate TTL from the SOA record - if (response->h.numAuthorities && (ptr = LocateAuthorities(response, end)) != mDNSNULL) - { - ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec); - if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_SOA) - { - const rdataSOA *const soa = (const rdataSOA *)m->rec.r.resrec.rdata->u.data; - mDNSu32 ttl_s = soa->min; - // We use the lesser of the SOA.MIN field and the SOA record's TTL, *except* - // for the SOA record for ".", where the record is reported as non-cacheable - // (TTL zero) for some reason, so in this case we just take the SOA record's TTL as-is - if (ttl_s > m->rec.r.resrec.rroriginalttl && m->rec.r.resrec.name->c[0]) - ttl_s = m->rec.r.resrec.rroriginalttl; - if (negttl < ttl_s) negttl = ttl_s; - - // Special check for SOA queries: If we queried for a.b.c.d.com, and got no answer, - // with an Authority Section SOA record for d.com, then this is a hint that the authority - // is d.com, and consequently SOA records b.c.d.com and c.d.com don't exist either. - // To do this we set the repeat count so the while loop below will make a series of negative cache entries for us - if (q.qtype == kDNSType_SOA) - { - int qcount = CountLabels(&q.qname); - int scount = CountLabels(m->rec.r.resrec.name); - if (qcount - 1 > scount) - if (SameDomainName(SkipLeadingLabels(&q.qname, qcount - scount), m->rec.r.resrec.name)) - repeat = qcount - 1 - scount; - } - } - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - } - - // If we already had a negative entry in the cache, then we double our existing negative TTL. This is to avoid - // the case where the record doesn't exist (e.g. particularly for things like our lb._dns-sd._udp. query), - // and the server returns no SOA record (or an SOA record with a small MIN TTL) so we assume a TTL - // of 60 seconds, and we end up polling the server every minute for a record that doesn't exist. - // With this fix in place, when this happens, we double the effective TTL each time (up to one hour), - // so that we back off our polling rate and don't keep hitting the server continually. - if (neg) - { - if (negttl < neg->resrec.rroriginalttl * 2) - negttl = neg->resrec.rroriginalttl * 2; - if (negttl > 3600) - negttl = 3600; - } - - negttl = GetEffectiveTTL(LLQType, negttl); // Add 25% grace period if necessary - - // If we already had a negative cache entry just update it, else make one or more new negative cache entries - if (neg) - { - debugf("mDNSCoreReceiveResponse: Renewing negative TTL from %d to %d %s", neg->resrec.rroriginalttl, negttl, CRDisplayString(m, neg)); - RefreshCacheRecord(m, neg, negttl); - // When we created the cache for the first time and answered the question, the question's - // interval was set to MaxQuestionInterval. If the cache is about to expire and we are resending - // the queries, the interval should still be at MaxQuestionInterval. If the query is being - // restarted (setting it to InitialQuestionInterval) for other reasons e.g., wakeup, - // we should reset its question interval here to MaxQuestionInterval. - ResetQuestionState(m, qptr); - } - else while (1) - { - debugf("mDNSCoreReceiveResponse making negative cache entry TTL %d for %##s (%s)", negttl, name->c, DNSTypeName(q.qtype)); - MakeNegativeCacheRecord(m, &m->rec.r, name, hash, q.qtype, q.qclass, negttl, mDNSInterface_Any, qptr->qDNSServer); - CreateNewCacheEntry(m, slot, cg, 0); // We never need any delivery delay for these generated negative cache records - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - if (!repeat) break; - repeat--; - name = (const domainname *)(name->c + 1 + name->c[0]); - hash = DomainNameHashValue(name); - slot = HashSlot(name); - cg = CacheGroupForName(m, slot, hash, name); - } - } - } - } - } - } + 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*)1) + { + CacheRecord *r1 = CacheFlushRecords, *r2; + const mDNSu32 slot = HashSlot(r1->resrec.name); + const CacheGroup *cg = CacheGroupForRecord(m, slot, &r1->resrec); + CacheFlushRecords = CacheFlushRecords->NextInCFList; + r1->NextInCFList = mDNSNULL; + + // Look for records in the cache with the same signature as this new one with the cache flush + // bit set, and either (a) if they're fresh, just make sure the whole RRSet has the same TTL + // (as required by DNS semantics) or (b) if they're old, mark them for deletion in one second. + // We make these TTL adjustments *only* for records that still have *more* than one second + // remaining to live. Otherwise, a record that we tagged for deletion half a second ago + // (and now has half a second remaining) could inadvertently get its life extended, by either + // (a) if we got an explicit goodbye packet half a second ago, the record would be considered + // "fresh" and would be incorrectly resurrected back to the same TTL as the rest of the RRSet, + // or (b) otherwise, the record would not be fully resurrected, but would be reset to expire + // in one second, thereby inadvertently delaying its actual expiration, instead of hastening it. + // If this were to happen repeatedly, the record's expiration could be deferred indefinitely. + // To avoid this, we need to ensure that the cache flushing operation will only act to + // *decrease* a record's remaining lifetime, never *increase* it. + for (r2 = cg ? cg->members : mDNSNULL; r2; r2=r2->next) + { + mDNSu16 id1; + mDNSu16 id2; + if (!r1->resrec.InterfaceID) + { + id1 = (r1->resrec.rDNSServer ? r1->resrec.rDNSServer->resGroupID : 0); + id2 = (r2->resrec.rDNSServer ? r2->resrec.rDNSServer->resGroupID : 0); + } + else + { + id1 = id2 = 0; + } + // When we receive new RRSIGs e.g., for DNSKEY record, we should not flush the old + // RRSIGS e.g., for TXT record. To do so, we need to look at the typeCovered field of + // the new RRSIG that we received. Process only if the typeCovered matches. + if ((r1->resrec.rrtype == r2->resrec.rrtype) && (r1->resrec.rrtype == kDNSType_RRSIG)) + { + rdataRRSig *rrsig1 = (rdataRRSig *)(((RDataBody2 *)(r1->resrec.rdata->u.data))->data); + rdataRRSig *rrsig2 = (rdataRRSig *)(((RDataBody2 *)(r2->resrec.rdata->u.data))->data); + if (swap16(rrsig1->typeCovered) != swap16(rrsig2->typeCovered)) + { + debugf("mDNSCoreReceiveResponse: Received RRSIG typeCovered %s, found %s, not processing", + DNSTypeName(swap16(rrsig1->typeCovered)), DNSTypeName(swap16(rrsig2->typeCovered))); + continue; + } + } + + // For Unicast (null InterfaceID) the resolver IDs should also match + if ((r1->resrec.InterfaceID == r2->resrec.InterfaceID) && + (r1->resrec.InterfaceID || (id1 == id2)) && + r1->resrec.rrtype == r2->resrec.rrtype && + r1->resrec.rrclass == r2->resrec.rrclass) + { + // If record is recent, just ensure the whole RRSet has the same TTL (as required by DNS semantics) + // else, if record is old, mark it to be flushed + if (m->timenow - r2->TimeRcvd < mDNSPlatformOneSecond && RRExpireTime(r2) - m->timenow > mDNSPlatformOneSecond) + { + // If we find mismatched TTLs in an RRSet, correct them. + // We only do this for records with a TTL of 2 or higher. It's possible to have a + // goodbye announcement with the cache flush bit set (or a case-change on record rdata, + // which we treat as a goodbye followed by an addition) and in that case it would be + // inappropriate to synchronize all the other records to a TTL of 0 (or 1). + // We suppress the message for the specific case of correcting from 240 to 60 for type TXT, + // because certain early Bonjour devices are known to have this specific mismatch, and + // there's no point filling syslog with messages about something we already know about. + // We also don't log this for uDNS responses, since a caching name server is obliged + // to give us an aged TTL to correct for how long it has held the record, + // so our received TTLs are expected to vary in that case + if (r2->resrec.rroriginalttl != r1->resrec.rroriginalttl && r1->resrec.rroriginalttl > 1) + { + if (!(r2->resrec.rroriginalttl == 240 && r1->resrec.rroriginalttl == 60 && r2->resrec.rrtype == kDNSType_TXT) && + mDNSOpaque16IsZero(response->h.id)) + LogInfo("Correcting TTL from %4d to %4d for %s", + r2->resrec.rroriginalttl, r1->resrec.rroriginalttl, CRDisplayString(m, r2)); + r2->resrec.rroriginalttl = r1->resrec.rroriginalttl; + } + r2->TimeRcvd = m->timenow; + } + else // else, if record is old, mark it to be flushed + { + verbosedebugf("Cache flush new %p age %d expire in %d %s", r1, m->timenow - r1->TimeRcvd, RRExpireTime(r1) - m->timenow, CRDisplayString(m, r1)); + verbosedebugf("Cache flush old %p age %d expire in %d %s", r2, m->timenow - r2->TimeRcvd, RRExpireTime(r2) - m->timenow, CRDisplayString(m, r2)); + // We set stale records to expire in one second. + // This gives the owner a chance to rescue it if necessary. + // This is important in the case of multi-homing and bridged networks: + // Suppose host X is on Ethernet. X then connects to an AirPort base station, which happens to be + // bridged onto the same Ethernet. When X announces its AirPort IP address with the cache-flush bit + // set, the AirPort packet will be bridged onto the Ethernet, and all other hosts on the Ethernet + // will promptly delete their cached copies of the (still valid) Ethernet IP address record. + // By delaying the deletion by one second, we give X a change to notice that this bridging has + // happened, and re-announce its Ethernet IP address to rescue it from deletion from all our caches. + + // We set UnansweredQueries to MaxUnansweredQueries to avoid expensive and unnecessary + // final expiration queries for this record. + + // If a record is deleted twice, first with an explicit DE record, then a second time by virtue of the cache + // flush bit on the new record replacing it, then we allow the record to be deleted immediately, without the usual + // one-second grace period. This improves responsiveness for mDNS_Update(), as used for things like iChat status updates. + // Updating TXT records is too slow + // We check for "rroriginalttl == 1" because we want to include records tagged by the "packet TTL is zero" check above, + // which sets rroriginalttl to 1, but not records tagged by the rdata case-change check, which sets rroriginalttl to 0. + if (r2->TimeRcvd == m->timenow && r2->resrec.rroriginalttl == 1 && r2->UnansweredQueries == MaxUnansweredQueries) + { + LogInfo("Cache flush for DE record %s", CRDisplayString(m, r2)); + r2->resrec.rroriginalttl = 0; + } + else if (RRExpireTime(r2) - m->timenow > mDNSPlatformOneSecond) + { + // We only set a record to expire in one second if it currently has *more* than a second to live + // If it's already due to expire in a second or less, we just leave it alone + r2->resrec.rroriginalttl = 1; + r2->UnansweredQueries = MaxUnansweredQueries; + r2->TimeRcvd = m->timenow - 1; + // We use (m->timenow - 1) instead of m->timenow, because we use that to identify records + // that we marked for deletion via an explicit DE record + } + } + SetNextCacheCheckTimeForRecord(m, r2); + } + } + + if (r1->DelayDelivery) // If we were planning to delay delivery of this record, see if we still need to + { + // If we had a unicast question for this response with at least one positive answer and we + // have NSECRecords, it is most likely a wildcard expanded answer. Cache the NSEC and its + // signatures along with the cache record which will be used for validation later. If + // we rescued a few records earlier in this function, then NSECCachePtr would be set. In that + // use that instead. + if (response->h.numAnswers && unicastQuestion && NSECRecords) + { + if (!NSECCachePtr) + { + LogInfo("mDNSCoreReceiveResponse: Updating NSECCachePtr to %s", CRDisplayString(m, r1)); + NSECCachePtr = r1; + } + // Note: We need to do this before we call CacheRecordDeferredAdd as this + // might start the verification process which needs these NSEC records + if (!AddNSECSForCacheRecord(m, NSECRecords, NSECCachePtr, rcode)) + { + LogMsg("mDNSCoreReceiveResponse: AddNSECSForCacheRecord failed to add NSEC for %s", CRDisplayString(m, NSECCachePtr)); + FreeNSECRecords(m, NSECRecords); + } + NSECRecords = mDNSNULL; + NSECCachePtr = mDNSNULL; + } + r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash, slot); + // If no longer delaying, deliver answer now, else schedule delivery for the appropriate time + if (!r1->DelayDelivery) CacheRecordDeferredAdd(m, r1); + else ScheduleNextCacheCheckTime(m, slot, r1->DelayDelivery); + } + } + + // If we have not consumed the NSEC records yet e.g., just refreshing the cache, + // update them now for future validations. + if (NSECRecords && NSECCachePtr) + { + LogInfo("mDNSCoreReceieveResponse: Updating NSEC records in %s", CRDisplayString(m, NSECCachePtr)); + if (!AddNSECSForCacheRecord(m, NSECRecords, NSECCachePtr, rcode)) + { + LogMsg("mDNSCoreReceiveResponse: AddNSECSForCacheRecord failed to add NSEC for %s", CRDisplayString(m, NSECCachePtr)); + FreeNSECRecords(m, NSECRecords); + } + NSECRecords = mDNSNULL; + NSECCachePtr = mDNSNULL; + } + + // See if we need to generate negative cache entries for unanswered unicast questions + mDNSCoreReceiveNoUnicastAnswers(m, response, end, dstaddr, dstport, InterfaceID, LLQType, rcode, NSECRecords); +} // ScheduleWakeup causes all proxy records with WakeUp.HMAC matching mDNSEthAddr 'e' to be deregistered, causing // multiple wakeup magic packets to be sent if appropriate, and all records to be ultimately freed after a few seconds. @@ -7186,393 +7916,744 @@ exit: // ScheduleWakeup must be called with the lock held (ScheduleWakeupForList uses mDNS_Deregister_internal) mDNSlocal void ScheduleWakeupForList(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *e, AuthRecord *const thelist) - { - // We don't need to use the m->CurrentRecord mechanism here because the target HMAC is nonzero, - // so all we're doing is marking the record to generate a few wakeup packets - AuthRecord *rr; - if (!e->l[0]) { LogMsg("ScheduleWakeupForList ERROR: Target HMAC is zero"); return; } - for (rr = thelist; rr; rr = rr->next) - if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && mDNSSameEthAddress(&rr->WakeUp.HMAC, e)) - { - LogInfo("ScheduleWakeupForList: Scheduling wakeup packets for %s", ARDisplayString(m, rr)); - mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); - } - } +{ + // We need to use the m->CurrentRecord mechanism here when dealing with DuplicateRecords list as + // mDNS_Deregister_internal deregisters duplicate records immediately as they are not used + // to send wakeups or goodbyes. See the comment in that function for more details. To keep it + // simple, we use the same mechanism for both lists. + if (!e->l[0]) + { + LogMsg("ScheduleWakeupForList ERROR: Target HMAC is zero"); + return; + } + m->CurrentRecord = thelist; + while (m->CurrentRecord) + { + AuthRecord *const rr = m->CurrentRecord; + if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && mDNSSameEthAddress(&rr->WakeUp.HMAC, e)) + { + LogInfo("ScheduleWakeupForList: Scheduling wakeup packets for %s", ARDisplayString(m, rr)); + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); + } + if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now + m->CurrentRecord = rr->next; + } +} mDNSlocal void ScheduleWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *e) - { - if (!e->l[0]) { LogMsg("ScheduleWakeup ERROR: Target HMAC is zero"); return; } - ScheduleWakeupForList(m, InterfaceID, e, m->DuplicateRecords); - ScheduleWakeupForList(m, InterfaceID, e, m->ResourceRecords); - } +{ + if (!e->l[0]) { LogMsg("ScheduleWakeup ERROR: Target HMAC is zero"); return; } + ScheduleWakeupForList(m, InterfaceID, e, m->DuplicateRecords); + ScheduleWakeupForList(m, InterfaceID, e, m->ResourceRecords); +} mDNSlocal void SPSRecordCallback(mDNS *const m, AuthRecord *const ar, mStatus result) - { - if (result && result != mStatus_MemFree) - LogInfo("SPS Callback %d %s", result, ARDisplayString(m, ar)); - - if (result == mStatus_NameConflict) - { - mDNS_Lock(m); - LogMsg("%-7s Conflicting mDNS -- waking %.6a %s", InterfaceNameForID(m, ar->resrec.InterfaceID), &ar->WakeUp.HMAC, ARDisplayString(m, ar)); - if (ar->WakeUp.HMAC.l[0]) - { - SendWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.IMAC, &ar->WakeUp.password); // Send one wakeup magic packet - ScheduleWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.HMAC); // Schedule all other records with the same owner to be woken - } - mDNS_Unlock(m); - } - - if (result == mStatus_NameConflict || result == mStatus_MemFree) - { - m->ProxyRecords--; - mDNSPlatformMemFree(ar); - mDNS_UpdateAllowSleep(m); - } - } +{ + if (result && result != mStatus_MemFree) + LogInfo("SPS Callback %d %s", result, ARDisplayString(m, ar)); + + if (result == mStatus_NameConflict) + { + mDNS_Lock(m); + LogMsg("%-7s Conflicting mDNS -- waking %.6a %s", InterfaceNameForID(m, ar->resrec.InterfaceID), &ar->WakeUp.HMAC, ARDisplayString(m, ar)); + if (ar->WakeUp.HMAC.l[0]) + { + SendWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.IMAC, &ar->WakeUp.password); // Send one wakeup magic packet + ScheduleWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.HMAC); // Schedule all other records with the same owner to be woken + } + mDNS_Unlock(m); + } + + if (result == mStatus_NameConflict || result == mStatus_MemFree) + { + m->ProxyRecords--; + mDNSPlatformMemFree(ar); + mDNS_UpdateAllowSleep(m); + } +} + +mDNSlocal mDNSu8 *GetValueForIPv6Addr(mDNSu8 *ptr, mDNSu8 *limit, mDNSv6Addr *v6) +{ + int hval; + int value; + int numBytes; + int digitsProcessed; + int zeroFillStart; + int numColons; + mDNSu8 v6addr[16]; + + // RFC 3513: Section 2.2 specifies IPv6 presentation format. The following parsing + // handles both (1) and (2) and does not handle embedded IPv4 addresses. + // + // First forms a address in "v6addr", then expands to fill the zeroes in and returns + // the result in "v6" + + numColons = numBytes = value = digitsProcessed = zeroFillStart = 0; + while (ptr < limit && *ptr != ' ') + { + hval = HexVal(*ptr); + if (hval != -1) + { + value <<= 4; + value |= hval; + digitsProcessed = 1; + } + else if (*ptr == ':') + { + if (!digitsProcessed) + { + // If we have already seen a "::", we should not see one more. Handle the special + // case of "::" + if (numColons) + { + // if we never filled any bytes and the next character is space (we have reached the end) + // we are done + if (!numBytes && (ptr + 1) < limit && *(ptr + 1) == ' ') + { + mDNSPlatformMemZero(v6->b, 16); + return ptr + 1; + } + LogMsg("GetValueForIPv6Addr: zeroFillStart non-zero %d", zeroFillStart); + return mDNSNULL; + } + + // We processed "::". We need to fill zeroes later. For now, mark the + // point where we will start filling zeroes from. + zeroFillStart = numBytes; + numColons++; + } + else if ((ptr + 1) < limit && *(ptr + 1) == ' ') + { + // We have a trailing ":" i.e., no more characters after ":" + LogMsg("GetValueForIPv6Addr: Trailing colon"); + return mDNSNULL; + } + else + { + // For a fully expanded IPv6 address, we fill the 14th and 15th byte outside of this while + // loop below as there is no ":" at the end. Hence, the last two bytes that can possibly + // filled here is 12 and 13. + if (numBytes > 13) { LogMsg("GetValueForIPv6Addr:1: numBytes is %d", numBytes); return mDNSNULL; } + + v6addr[numBytes++] = (mDNSu8) ((value >> 8) & 0xFF); + v6addr[numBytes++] = (mDNSu8) (value & 0xFF); + digitsProcessed = value = 0; + + // Make sure that we did not fill the 13th and 14th byte above + if (numBytes > 14) { LogMsg("GetValueForIPv6Addr:2: numBytes is %d", numBytes); return mDNSNULL; } + } + } + ptr++; + } + + // We should be processing the last set of bytes following the last ":" here + if (!digitsProcessed) + { + LogMsg("GetValueForIPv6Addr: no trailing bytes after colon, numBytes is %d", numBytes); + return mDNSNULL; + } + + if (numBytes > 14) { LogMsg("GetValueForIPv6Addr:3: numBytes is %d", numBytes); return mDNSNULL; } + v6addr[numBytes++] = (mDNSu8) ((value >> 8) & 0xFF); + v6addr[numBytes++] = (mDNSu8) (value & 0xFF); + + if (zeroFillStart) + { + int i, j, n; + for (i = 0; i < zeroFillStart; i++) + v6->b[i] = v6addr[i]; + for (j = i, n = 0; n < 16 - numBytes; j++, n++) + v6->b[j] = 0; + for (; j < 16; i++, j++) + v6->b[j] = v6addr[i]; + } + else if (numBytes == 16) + mDNSPlatformMemCopy(v6->b, v6addr, 16); + else + { + LogMsg("GetValueForIPv6addr: Not enough bytes for IPv6 address, numBytes is %d", numBytes); + return mDNSNULL; + } + return ptr; +} + +mDNSlocal mDNSu8 *GetValueForIPv4Addr(mDNSu8 *ptr, mDNSu8 *limit, mDNSv4Addr *v4) +{ + int i; + mDNSu32 val; + int dots = 0; + + val = 0; + for (i = 0; ptr < limit && *ptr != ' '; ptr++) + { + if (*ptr >= '0' && *ptr <= '9') + val = val * 10 + *ptr - '0'; + else if (*ptr == '.') + { + v4->b[dots++] = val; + val = 0; + } + else + { + // We have a zero at the end and if we reached that, then we are done. + if (*ptr == 0 && ptr == limit - 1 && dots == 3) + { + v4->b[dots] = val; + return ptr + 1; + } + else { LogMsg("GetValueForIPv4Addr: something wrong ptr(%p) %c, limit %p, dots %d", ptr, *ptr, limit, dots); return mDNSNULL; } + } + } + if (dots != 3) { LogMsg("GetValueForIPv4Addr: Address malformed dots %d", dots); return mDNSNULL; } + v4->b[dots] = val; + return ptr; +} + +mDNSlocal mDNSu8 *GetValueForKeepalive(mDNSu8 *ptr, mDNSu8 *limit, mDNSu32 *value) +{ + int i; + mDNSu32 val; + + val = 0; + for (i = 0; ptr < limit && *ptr != ' '; ptr++) + { + if (*ptr < '0' || *ptr > '9') + { + // We have a zero at the end and if we reached that, then we are done. + if (*ptr == 0 && ptr == limit - 1) + { + *value = val; + return ptr + 1; + } + else { LogMsg("GetValueForKeepalive: *ptr %d, ptr %p, limit %p, ptr +1 %d", *ptr, ptr, limit, *(ptr + 1)); return mDNSNULL; } + } + val = val * 10 + *ptr - '0'; + } + *value = val; + return ptr; +} + +mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSAddr *laddr, mDNSAddr *raddr, mDNSu32 *seq, + mDNSu32 *ack, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu16 *win) +{ + if (ar->resrec.rrtype != kDNSType_NULL) + return; + + if (mDNS_KeepaliveRecord(&ar->resrec)) + { + int len = ar->resrec.rdlength; + mDNSu8 *ptr = &ar->resrec.rdata->u.txt.c[1]; + mDNSu8 *limit = ptr + len - 1; // Exclude the first byte that is the length + mDNSu32 value; + + while (ptr < limit) + { + mDNSu8 param = *ptr; + mDNSu8 *p; + + ptr += 2; // Skip the letter and the "=" + if (param == 'h') + { + laddr->type = mDNSAddrType_IPv4; + ptr = GetValueForIPv4Addr(ptr, limit, &laddr->ip.v4); + } + else if (param == 'd') + { + raddr->type = mDNSAddrType_IPv4; + ptr = GetValueForIPv4Addr(ptr, limit, &raddr->ip.v4); + } + if (param == 'H') + { + laddr->type = mDNSAddrType_IPv6; + ptr = GetValueForIPv6Addr(ptr, limit, &laddr->ip.v6); + } + else if (param == 'D') + { + raddr->type = mDNSAddrType_IPv6; + ptr = GetValueForIPv6Addr(ptr, limit, &raddr->ip.v6); + } + else + { + ptr = GetValueForKeepalive(ptr, limit, &value); + } + if (!ptr) { LogMsg("mDNS_ExtractKeepaliveInfo: Cannot parse\n"); return; } + + p = (mDNSu8 *)&value; + // Extract everything in network order so that it is easy for sending a keepalive and also + // for matching incoming TCP packets + switch (param) + { + case 't': + *timeout = value; + //if (*timeout < 120) *timeout = 120; + break; + case 'h': + case 'H': + case 'd': + case 'D': + break; + case 'l': + lport->NotAnInteger = p[0] << 8 | p[1]; + break; + case 'r': + rport->NotAnInteger = p[0] << 8 | p[1]; + break; + case 's': + value = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; + *seq = value; + break; + case 'a': + value = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; + *ack = value; + break; + case 'w': + *win = p[0] << 8 | p[1]; + break; + default: + LogMsg("mDNS_ExtractKeepaliveInfo: unknown value\n"); + ptr = limit; + break; + } + ptr++; // skip the space + } + } +} + +// Matches the proxied auth records to the incoming TCP packet and returns the match and its sequence and ack in "rseq" and "rack" so that +// the clients need not retrieve this information from the auth record again. +mDNSlocal AuthRecord* mDNS_MatchKeepaliveInfo(mDNS *const m, const mDNSAddr const* pladdr, const mDNSAddr const* praddr, const mDNSIPPort plport, + const mDNSIPPort prport, mDNSu32 *rseq, mDNSu32 *rack) +{ + AuthRecord *ar; + mDNSAddr laddr, raddr; + mDNSIPPort lport, rport; + mDNSu32 timeout, seq, ack; + mDNSu16 win; + + for (ar = m->ResourceRecords; ar; ar=ar->next) + { + timeout = seq = ack = 0; + win = 0; + laddr = raddr = zeroAddr; + lport = rport = zeroIPPort; + + if (!ar->WakeUp.HMAC.l[0]) continue; + + mDNS_ExtractKeepaliveInfo(ar, &timeout, &laddr, &raddr, &seq, &ack, &lport, &rport, &win); + + // Did we parse correctly ? + if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || !seq || !ack || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport) || !win) + { + debugf("mDNS_MatchKeepaliveInfo: not a valid record %s for keepalive", ARDisplayString(m, ar)); + continue; + } + + debugf("mDNS_MatchKeepaliveInfo: laddr %#a pladdr %#a, raddr %#a praddr %#a, lport %d plport %d, rport %d prport %d", + &laddr, pladdr, &raddr, praddr, mDNSVal16(lport), mDNSVal16(plport), mDNSVal16(rport), mDNSVal16(prport)); + + // Does it match the incoming TCP packet ? + if (mDNSSameAddress(&laddr, pladdr) && mDNSSameAddress(&raddr, praddr) && mDNSSameIPPort(lport, plport) && mDNSSameIPPort(rport, prport)) + { + // returning in network order + *rseq = seq; + *rack = ack; + return ar; + } + } + return mDNSNULL; +} + +mDNSlocal void mDNS_SendKeepalives(mDNS *const m) +{ + AuthRecord *ar; + + for (ar = m->ResourceRecords; ar; ar=ar->next) + { + mDNSu32 timeout, seq, ack; + mDNSu16 win; + mDNSAddr laddr, raddr; + mDNSIPPort lport, rport; + + timeout = seq = ack = 0; + win = 0; + + laddr = raddr = zeroAddr; + lport = rport = zeroIPPort; + + if (!ar->WakeUp.HMAC.l[0]) continue; + + mDNS_ExtractKeepaliveInfo(ar, &timeout, &laddr, &raddr, &seq, &ack, &lport, &rport, &win); + + if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || !seq || !ack || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport) || !win) + { + debugf("mDNS_SendKeepalives: not a valid record %s for keepalive", ARDisplayString(m, ar)); + continue; + } + LogMsg("mDNS_SendKeepalives: laddr %#a raddr %#a lport %d rport %d", &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport)); + + // When we receive a proxy update, we set KATimeExpire to zero so that we always send a keepalive + // immediately (to detect any potential problems). After that we always set it to a non-zero value. + if (!ar->KATimeExpire || (m->timenow - ar->KATimeExpire >= 0)) + { + mDNSPlatformSendKeepalive(&laddr, &raddr, &lport, &rport, seq, ack, win); + ar->KATimeExpire = NonZeroTime(m->timenow + timeout * mDNSPlatformOneSecond); + } + if (m->NextScheduledKA - ar->KATimeExpire > 0) + m->NextScheduledKA = ar->KATimeExpire; + } +} mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m, - const DNSMessage *const msg, const mDNSu8 *end, - const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, - const mDNSInterfaceID InterfaceID) - { - int i; - AuthRecord opt; - mDNSu8 *p = m->omsg.data; - OwnerOptData owner = zeroOwner; // Need to zero this, so we'll know if this Update packet was missing its Owner option - mDNSu32 updatelease = 0; - const mDNSu8 *ptr; - - LogSPS("Received Update from %#-15a:%-5d to %#-15a:%-5d on 0x%p with " - "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes", - srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, - msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", - msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", - msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", - msg->h.numAdditionals, msg->h.numAdditionals == 1 ? " " : "s", end - msg->data); - - if (!InterfaceID || !m->SPSSocket || !mDNSSameIPPort(dstport, m->SPSSocket->port)) return; - - if (mDNS_PacketLoggingEnabled) - DumpPacket(m, mStatus_NoError, mDNSfalse, "UDP", srcaddr, srcport, dstaddr, dstport, msg, end); - - ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space + DNSOpt_OwnerData_ID_Space); - if (ptr) - { - ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec); - if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_OPT) - { - const rdataOPT *o; - const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength]; - for (o = &m->rec.r.resrec.rdata->u.opt[0]; o < e; o++) - { - if (o->opt == kDNSOpt_Lease) updatelease = o->u.updatelease; - else if (o->opt == kDNSOpt_Owner && o->u.owner.vers == 0) owner = o->u.owner; - } - } - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - } - - InitializeDNSMessage(&m->omsg.h, msg->h.id, UpdateRespFlags); - - if (!updatelease || !owner.HMAC.l[0]) - { - static int msgs = 0; - if (msgs < 100) - { - msgs++; - LogMsg("Refusing sleep proxy registration from %#a:%d:%s%s", srcaddr, mDNSVal16(srcport), - !updatelease ? " No lease" : "", !owner.HMAC.l[0] ? " No owner" : ""); - } - m->omsg.h.flags.b[1] |= kDNSFlag1_RC_FormErr; - } - else if (m->ProxyRecords + msg->h.mDNS_numUpdates > MAX_PROXY_RECORDS) - { - static int msgs = 0; - if (msgs < 100) - { - msgs++; - LogMsg("Refusing sleep proxy registration from %#a:%d: Too many records %d + %d = %d > %d", srcaddr, mDNSVal16(srcport), - m->ProxyRecords, msg->h.mDNS_numUpdates, m->ProxyRecords + msg->h.mDNS_numUpdates, MAX_PROXY_RECORDS); - } - m->omsg.h.flags.b[1] |= kDNSFlag1_RC_Refused; - } - else - { - LogSPS("Received Update for H-MAC %.6a I-MAC %.6a Password %.6a seq %d", &owner.HMAC, &owner.IMAC, &owner.password, owner.seq); - - if (updatelease > 24 * 60 * 60) - updatelease = 24 * 60 * 60; - - if (updatelease > 0x40000000UL / mDNSPlatformOneSecond) - updatelease = 0x40000000UL / mDNSPlatformOneSecond; - - ptr = LocateAuthorities(msg, end); - for (i = 0; i < msg->h.mDNS_numUpdates && ptr && ptr < end; i++) - { - ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec); - if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative) - { - mDNSu16 RDLengthMem = GetRDLengthMem(&m->rec.r.resrec); - AuthRecord *ar = mDNSPlatformMemAllocate(sizeof(AuthRecord) - sizeof(RDataBody) + RDLengthMem); - if (!ar) { m->omsg.h.flags.b[1] |= kDNSFlag1_RC_Refused; break; } - else - { - mDNSu8 RecordType = m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask ? kDNSRecordTypeUnique : kDNSRecordTypeShared; - m->rec.r.resrec.rrclass &= ~kDNSClass_UniqueRRSet; - ClearIdenticalProxyRecords(m, &owner, m->DuplicateRecords); // Make sure we don't have any old stale duplicates of this record - ClearIdenticalProxyRecords(m, &owner, m->ResourceRecords); - mDNS_SetupResourceRecord(ar, mDNSNULL, InterfaceID, m->rec.r.resrec.rrtype, m->rec.r.resrec.rroriginalttl, RecordType, AuthRecordAny, SPSRecordCallback, ar); - AssignDomainName(&ar->namestorage, m->rec.r.resrec.name); - ar->resrec.rdlength = GetRDLength(&m->rec.r.resrec, mDNSfalse); - ar->resrec.rdata->MaxRDLength = RDLengthMem; - mDNSPlatformMemCopy(ar->resrec.rdata->u.data, m->rec.r.resrec.rdata->u.data, RDLengthMem); - ar->ForceMCast = mDNStrue; - ar->WakeUp = owner; - if (m->rec.r.resrec.rrtype == kDNSType_PTR) - { - mDNSs32 t = ReverseMapDomainType(m->rec.r.resrec.name); - if (t == mDNSAddrType_IPv4) GetIPv4FromName(&ar->AddressProxy, m->rec.r.resrec.name); - else if (t == mDNSAddrType_IPv6) GetIPv6FromName(&ar->AddressProxy, m->rec.r.resrec.name); - debugf("mDNSCoreReceiveUpdate: PTR %d %d %#a %s", t, ar->AddressProxy.type, &ar->AddressProxy, ARDisplayString(m, ar)); - if (ar->AddressProxy.type) SetSPSProxyListChanged(InterfaceID); - } - ar->TimeRcvd = m->timenow; - ar->TimeExpire = m->timenow + updatelease * mDNSPlatformOneSecond; - if (m->NextScheduledSPS - ar->TimeExpire > 0) - m->NextScheduledSPS = ar->TimeExpire; - mDNS_Register_internal(m, ar); - // Unsolicited Neighbor Advertisements (RFC 2461 Section 7.2.6) give us fast address cache updating, - // but some older IPv6 clients get confused by them, so for now we don't send them. Without Unsolicited - // Neighbor Advertisements we have to rely on Neighbor Unreachability Detection instead, which is slower. - // Given this, we'll do our best to wake for existing IPv6 connections, but we don't want to encourage - // new ones for sleeping clients, so we'll we send deletions for our SPS clients' AAAA records. - if (m->KnownBugs & mDNS_KnownBug_LimitedIPv6) - if (ar->resrec.rrtype == kDNSType_AAAA) ar->resrec.rroriginalttl = 0; - m->ProxyRecords++; - mDNS_UpdateAllowSleep(m); - LogSPS("SPS Registered %4d %X %s", m->ProxyRecords, RecordType, ARDisplayString(m,ar)); - } - } - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - } - - if (m->omsg.h.flags.b[1] & kDNSFlag1_RC_Mask) - { - LogMsg("Refusing sleep proxy registration from %#a:%d: Out of memory", srcaddr, mDNSVal16(srcport)); - ClearProxyRecords(m, &owner, m->DuplicateRecords); - ClearProxyRecords(m, &owner, m->ResourceRecords); - } - else - { - mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); - opt.resrec.rrclass = NormalMaxDNSMessageData; - opt.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record - opt.resrec.rdestimate = sizeof(rdataOPT); - opt.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease; - opt.resrec.rdata->u.opt[0].u.updatelease = updatelease; - p = PutResourceRecordTTLWithLimit(&m->omsg, p, &m->omsg.h.numAdditionals, &opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData); - } - } - - if (p) mDNSSendDNSMessage(m, &m->omsg, p, InterfaceID, m->SPSSocket, srcaddr, srcport, mDNSNULL, mDNSNULL); - } + const DNSMessage *const msg, const mDNSu8 *end, + const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, + const mDNSInterfaceID InterfaceID) +{ + int i; + AuthRecord opt; + mDNSu8 *p = m->omsg.data; + OwnerOptData owner = zeroOwner; // Need to zero this, so we'll know if this Update packet was missing its Owner option + mDNSu32 updatelease = 0; + const mDNSu8 *ptr; + + LogSPS("Received Update from %#-15a:%-5d to %#-15a:%-5d on 0x%p with " + "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes", + srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, + msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", + msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", + msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", + msg->h.numAdditionals, msg->h.numAdditionals == 1 ? " " : "s", end - msg->data); + + if (!InterfaceID || !m->SPSSocket || !mDNSSameIPPort(dstport, m->SPSSocket->port)) return; + + if (mDNS_PacketLoggingEnabled) + DumpPacket(m, mStatus_NoError, mDNSfalse, "UDP", srcaddr, srcport, dstaddr, dstport, msg, end); + + ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space + DNSOpt_OwnerData_ID_Space); + if (ptr) + { + ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec); + if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_OPT) + { + const rdataOPT *o; + const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength]; + for (o = &m->rec.r.resrec.rdata->u.opt[0]; o < e; o++) + { + if (o->opt == kDNSOpt_Lease) updatelease = o->u.updatelease; + else if (o->opt == kDNSOpt_Owner && o->u.owner.vers == 0) owner = o->u.owner; + } + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + + InitializeDNSMessage(&m->omsg.h, msg->h.id, UpdateRespFlags); + + if (!updatelease || !owner.HMAC.l[0]) + { + static int msgs = 0; + if (msgs < 100) + { + msgs++; + LogMsg("Refusing sleep proxy registration from %#a:%d:%s%s", srcaddr, mDNSVal16(srcport), + !updatelease ? " No lease" : "", !owner.HMAC.l[0] ? " No owner" : ""); + } + m->omsg.h.flags.b[1] |= kDNSFlag1_RC_FormErr; + } + else if (m->ProxyRecords + msg->h.mDNS_numUpdates > MAX_PROXY_RECORDS) + { + static int msgs = 0; + if (msgs < 100) + { + msgs++; + LogMsg("Refusing sleep proxy registration from %#a:%d: Too many records %d + %d = %d > %d", srcaddr, mDNSVal16(srcport), + m->ProxyRecords, msg->h.mDNS_numUpdates, m->ProxyRecords + msg->h.mDNS_numUpdates, MAX_PROXY_RECORDS); + } + m->omsg.h.flags.b[1] |= kDNSFlag1_RC_Refused; + } + else + { + LogSPS("Received Update for H-MAC %.6a I-MAC %.6a Password %.6a seq %d", &owner.HMAC, &owner.IMAC, &owner.password, owner.seq); + + if (updatelease > 24 * 60 * 60) + updatelease = 24 * 60 * 60; + + if (updatelease > 0x40000000UL / mDNSPlatformOneSecond) + updatelease = 0x40000000UL / mDNSPlatformOneSecond; + + ptr = LocateAuthorities(msg, end); + for (i = 0; i < msg->h.mDNS_numUpdates && ptr && ptr < end; i++) + { + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec); + if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative) + { + mDNSu16 RDLengthMem = GetRDLengthMem(&m->rec.r.resrec); + AuthRecord *ar = mDNSPlatformMemAllocate(sizeof(AuthRecord) - sizeof(RDataBody) + RDLengthMem); + if (!ar) { m->omsg.h.flags.b[1] |= kDNSFlag1_RC_Refused; break; } + else + { + mDNSu8 RecordType = m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask ? kDNSRecordTypeUnique : kDNSRecordTypeShared; + m->rec.r.resrec.rrclass &= ~kDNSClass_UniqueRRSet; + ClearIdenticalProxyRecords(m, &owner, m->DuplicateRecords); // Make sure we don't have any old stale duplicates of this record + ClearIdenticalProxyRecords(m, &owner, m->ResourceRecords); + mDNS_SetupResourceRecord(ar, mDNSNULL, InterfaceID, m->rec.r.resrec.rrtype, m->rec.r.resrec.rroriginalttl, RecordType, AuthRecordAny, SPSRecordCallback, ar); + AssignDomainName(&ar->namestorage, m->rec.r.resrec.name); + ar->resrec.rdlength = GetRDLength(&m->rec.r.resrec, mDNSfalse); + ar->resrec.rdata->MaxRDLength = RDLengthMem; + mDNSPlatformMemCopy(ar->resrec.rdata->u.data, m->rec.r.resrec.rdata->u.data, RDLengthMem); + ar->ForceMCast = mDNStrue; + ar->WakeUp = owner; + if (m->rec.r.resrec.rrtype == kDNSType_PTR) + { + mDNSs32 t = ReverseMapDomainType(m->rec.r.resrec.name); + if (t == mDNSAddrType_IPv4) GetIPv4FromName(&ar->AddressProxy, m->rec.r.resrec.name); + else if (t == mDNSAddrType_IPv6) GetIPv6FromName(&ar->AddressProxy, m->rec.r.resrec.name); + debugf("mDNSCoreReceiveUpdate: PTR %d %d %#a %s", t, ar->AddressProxy.type, &ar->AddressProxy, ARDisplayString(m, ar)); + if (ar->AddressProxy.type) SetSPSProxyListChanged(InterfaceID); + } + ar->TimeRcvd = m->timenow; + ar->TimeExpire = m->timenow + updatelease * mDNSPlatformOneSecond; + if (m->NextScheduledSPS - ar->TimeExpire > 0) + m->NextScheduledSPS = ar->TimeExpire; + ar->KATimeExpire = 0; + mDNS_Register_internal(m, ar); + // Unsolicited Neighbor Advertisements (RFC 2461 Section 7.2.6) give us fast address cache updating, + // but some older IPv6 clients get confused by them, so for now we don't send them. Without Unsolicited + // Neighbor Advertisements we have to rely on Neighbor Unreachability Detection instead, which is slower. + // Given this, we'll do our best to wake for existing IPv6 connections, but we don't want to encourage + // new ones for sleeping clients, so we'll we send deletions for our SPS clients' AAAA records. + if (m->KnownBugs & mDNS_KnownBug_LimitedIPv6) + if (ar->resrec.rrtype == kDNSType_AAAA) ar->resrec.rroriginalttl = 0; + m->ProxyRecords++; + mDNS_UpdateAllowSleep(m); + LogSPS("SPS Registered %4d %X %s", m->ProxyRecords, RecordType, ARDisplayString(m,ar)); + } + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + + if (m->omsg.h.flags.b[1] & kDNSFlag1_RC_Mask) + { + LogMsg("Refusing sleep proxy registration from %#a:%d: Out of memory", srcaddr, mDNSVal16(srcport)); + ClearProxyRecords(m, &owner, m->DuplicateRecords); + ClearProxyRecords(m, &owner, m->ResourceRecords); + } + else + { + mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + opt.resrec.rrclass = NormalMaxDNSMessageData; + opt.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record + opt.resrec.rdestimate = sizeof(rdataOPT); + opt.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease; + opt.resrec.rdata->u.opt[0].u.updatelease = updatelease; + p = PutResourceRecordTTLWithLimit(&m->omsg, p, &m->omsg.h.numAdditionals, &opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData); + } + } + + if (p) mDNSSendDNSMessage(m, &m->omsg, p, InterfaceID, m->SPSSocket, srcaddr, srcport, mDNSNULL, mDNSNULL, mDNSfalse); + mDNS_SendKeepalives(m); +} mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSInterfaceID InterfaceID) - { - if (InterfaceID) - { - mDNSu32 updatelease = 60 * 60; // If SPS fails to indicate lease time, assume one hour - const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space); - if (ptr) - { - ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec); - if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_OPT) - { - const rdataOPT *o; - const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength]; - for (o = &m->rec.r.resrec.rdata->u.opt[0]; o < e; o++) - if (o->opt == kDNSOpt_Lease) - { - updatelease = o->u.updatelease; - LogSPS("Sleep Proxy granted lease time %4d seconds", updatelease); - } - } - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - } - - if (m->CurrentRecord) - LogMsg("mDNSCoreReceiveUpdateR ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); - m->CurrentRecord = m->ResourceRecords; - while (m->CurrentRecord) - { - AuthRecord *const rr = m->CurrentRecord; - if (rr->resrec.InterfaceID == InterfaceID || (!rr->resrec.InterfaceID && (rr->ForceMCast || IsLocalDomain(rr->resrec.name)))) - if (mDNSSameOpaque16(rr->updateid, msg->h.id)) - { - rr->updateid = zeroID; - rr->expire = NonZeroTime(m->timenow + updatelease * mDNSPlatformOneSecond); - LogSPS("Sleep Proxy %s record %5d %s", rr->WakeUp.HMAC.l[0] ? "transferred" : "registered", updatelease, ARDisplayString(m,rr)); - if (rr->WakeUp.HMAC.l[0]) - { - rr->WakeUp.HMAC = zeroEthAddr; // Clear HMAC so that mDNS_Deregister_internal doesn't waste packets trying to wake this host - rr->RequireGoodbye = mDNSfalse; // and we don't want to send goodbye for it - mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); - } - } - // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because - // new records could have been added to the end of the list as a result of that call. - if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now - m->CurrentRecord = rr->next; - } - } - // If we were waiting to go to sleep, then this SPS registration or wide-area record deletion - // may have been the thing we were waiting for, so schedule another check to see if we can sleep now. - if (m->SleepLimit) m->NextScheduledSPRetry = m->timenow; - } +{ + if (InterfaceID) + { + mDNSu32 updatelease = 60 * 60; // If SPS fails to indicate lease time, assume one hour + const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space); + if (ptr) + { + ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec); + if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_OPT) + { + const rdataOPT *o; + const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength]; + for (o = &m->rec.r.resrec.rdata->u.opt[0]; o < e; o++) + if (o->opt == kDNSOpt_Lease) + { + updatelease = o->u.updatelease; + LogSPS("Sleep Proxy granted lease time %4d seconds, updateid %d, InterfaceID %p", updatelease, mDNSVal16(msg->h.id), InterfaceID); + } + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + + if (m->CurrentRecord) + LogMsg("mDNSCoreReceiveUpdateR ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + AuthRecord *const rr = m->CurrentRecord; + if (rr->resrec.InterfaceID == InterfaceID || (!rr->resrec.InterfaceID && (rr->ForceMCast || IsLocalDomain(rr->resrec.name)))) + if (mDNSSameOpaque16(rr->updateid, msg->h.id)) + { + // We successfully completed this record's registration on this "InterfaceID". Clear that bit. + // Clear the updateid when we are done sending on all interfaces. + mDNSu32 scopeid = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNStrue); + if (scopeid < (sizeof(rr->updateIntID) * mDNSNBBY)) + bit_clr_opaque64(rr->updateIntID, scopeid); + if (mDNSOpaque64IsZero(&rr->updateIntID)) + rr->updateid = zeroID; + rr->expire = NonZeroTime(m->timenow + updatelease * mDNSPlatformOneSecond); + LogSPS("Sleep Proxy %s record %5d 0x%x 0x%x (%d) %s", rr->WakeUp.HMAC.l[0] ? "transferred" : "registered", updatelease, rr->updateIntID.l[1], rr->updateIntID.l[0], mDNSVal16(rr->updateid), ARDisplayString(m,rr)); + if (rr->WakeUp.HMAC.l[0]) + { + rr->WakeUp.HMAC = zeroEthAddr; // Clear HMAC so that mDNS_Deregister_internal doesn't waste packets trying to wake this host + rr->RequireGoodbye = mDNSfalse; // and we don't want to send goodbye for it + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); + } + } + // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because + // new records could have been added to the end of the list as a result of that call. + if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now + m->CurrentRecord = rr->next; + } + } + // If we were waiting to go to sleep, then this SPS registration or wide-area record deletion + // may have been the thing we were waiting for, so schedule another check to see if we can sleep now. + if (m->SleepLimit) m->NextScheduledSPRetry = m->timenow; +} mDNSexport void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr, - const domainname *const name, const mDNSu32 namehash, const mDNSu16 rrtype, const mDNSu16 rrclass, mDNSu32 ttl_seconds, mDNSInterfaceID InterfaceID, DNSServer *dnsserver) - { - if (cr == &m->rec.r && m->rec.r.resrec.RecordType) - { - LogMsg("MakeNegativeCacheRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r)); + const domainname *const name, const mDNSu32 namehash, const mDNSu16 rrtype, const mDNSu16 rrclass, mDNSu32 ttl_seconds, mDNSInterfaceID InterfaceID, DNSServer *dnsserver) +{ + if (cr == &m->rec.r && m->rec.r.resrec.RecordType) + { + LogMsg("MakeNegativeCacheRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r)); #if ForceAlerts - *(long*)0 = 0; + *(long*)0 = 0; #endif - } - - // Create empty resource record - cr->resrec.RecordType = kDNSRecordTypePacketNegative; - cr->resrec.InterfaceID = InterfaceID; - cr->resrec.rDNSServer = dnsserver; - cr->resrec.name = name; // Will be updated to point to cg->name when we call CreateNewCacheEntry - cr->resrec.rrtype = rrtype; - cr->resrec.rrclass = rrclass; - cr->resrec.rroriginalttl = ttl_seconds; - cr->resrec.rdlength = 0; - cr->resrec.rdestimate = 0; - cr->resrec.namehash = namehash; - cr->resrec.rdatahash = 0; - cr->resrec.rdata = (RData*)&cr->smallrdatastorage; - cr->resrec.rdata->MaxRDLength = 0; - - cr->NextInKAList = mDNSNULL; - cr->TimeRcvd = m->timenow; - cr->DelayDelivery = 0; - cr->NextRequiredQuery = m->timenow; - cr->LastUsed = m->timenow; - cr->CRActiveQuestion = mDNSNULL; - cr->UnansweredQueries = 0; - cr->LastUnansweredTime = 0; + } + + // Create empty resource record + cr->resrec.RecordType = kDNSRecordTypePacketNegative; + cr->resrec.InterfaceID = InterfaceID; + cr->resrec.rDNSServer = dnsserver; + cr->resrec.name = name; // Will be updated to point to cg->name when we call CreateNewCacheEntry + cr->resrec.rrtype = rrtype; + cr->resrec.rrclass = rrclass; + cr->resrec.rroriginalttl = ttl_seconds; + cr->resrec.rdlength = 0; + cr->resrec.rdestimate = 0; + cr->resrec.namehash = namehash; + cr->resrec.rdatahash = 0; + cr->resrec.rdata = (RData*)&cr->smallrdatastorage; + cr->resrec.rdata->MaxRDLength = 0; + + cr->NextInKAList = mDNSNULL; + cr->TimeRcvd = m->timenow; + cr->DelayDelivery = 0; + cr->NextRequiredQuery = m->timenow; + cr->LastUsed = m->timenow; + cr->CRActiveQuestion = mDNSNULL; + cr->UnansweredQueries = 0; + cr->LastUnansweredTime = 0; #if ENABLE_MULTI_PACKET_QUERY_SNOOPING - cr->MPUnansweredQ = 0; - cr->MPLastUnansweredQT = 0; - cr->MPUnansweredKA = 0; - cr->MPExpectingKA = mDNSfalse; + cr->MPUnansweredQ = 0; + cr->MPLastUnansweredQT = 0; + cr->MPUnansweredKA = 0; + cr->MPExpectingKA = mDNSfalse; #endif - cr->NextInCFList = mDNSNULL; - } + cr->NextInCFList = mDNSNULL; + cr->nsec = mDNSNULL; +} mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *const end, - const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, - const mDNSInterfaceID InterfaceID) - { - mDNSInterfaceID ifid = InterfaceID; - DNSMessage *msg = (DNSMessage *)pkt; - const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery; - const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; - const mDNSu8 UpdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_Update; - const mDNSu8 UpdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update; - mDNSu8 QR_OP; - mDNSu8 *ptr = mDNSNULL; - mDNSBool TLS = (dstaddr == (mDNSAddr *)1); // For debug logs: dstaddr = 0 means TCP; dstaddr = 1 means TLS - if (TLS) dstaddr = mDNSNULL; + const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, + const mDNSInterfaceID InterfaceID) +{ + mDNSInterfaceID ifid = InterfaceID; + DNSMessage *msg = (DNSMessage *)pkt; + const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery; + const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; + const mDNSu8 UpdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_Update; + const mDNSu8 UpdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update; + mDNSu8 QR_OP; + mDNSu8 *ptr = mDNSNULL; + mDNSBool TLS = (dstaddr == (mDNSAddr *)1); // For debug logs: dstaddr = 0 means TCP; dstaddr = 1 means TLS + if (TLS) dstaddr = mDNSNULL; #ifndef UNICAST_DISABLED - if (mDNSSameAddress(srcaddr, &m->Router)) - { + if (mDNSSameAddress(srcaddr, &m->Router)) + { #ifdef _LEGACY_NAT_TRAVERSAL_ - if (mDNSSameIPPort(srcport, SSDPPort) || (m->SSDPSocket && mDNSSameIPPort(dstport, m->SSDPSocket->port))) - { - mDNS_Lock(m); - LNT_ConfigureRouterInfo(m, InterfaceID, pkt, (mDNSu16)(end - (mDNSu8 *)pkt)); - mDNS_Unlock(m); - return; - } + if (mDNSSameIPPort(srcport, SSDPPort) || (m->SSDPSocket && mDNSSameIPPort(dstport, m->SSDPSocket->port))) + { + mDNS_Lock(m); + LNT_ConfigureRouterInfo(m, InterfaceID, pkt, (mDNSu16)(end - (mDNSu8 *)pkt)); + mDNS_Unlock(m); + return; + } #endif - if (mDNSSameIPPort(srcport, NATPMPPort)) - { - mDNS_Lock(m); - uDNS_ReceiveNATPMPPacket(m, InterfaceID, pkt, (mDNSu16)(end - (mDNSu8 *)pkt)); - mDNS_Unlock(m); - return; - } - } + if (mDNSSameIPPort(srcport, NATPMPPort)) + { + mDNS_Lock(m); + uDNS_ReceiveNATPMPPacket(m, InterfaceID, pkt, (mDNSu16)(end - (mDNSu8 *)pkt)); + mDNS_Unlock(m); + return; + } + } #ifdef _LEGACY_NAT_TRAVERSAL_ - else if (m->SSDPSocket && mDNSSameIPPort(dstport, m->SSDPSocket->port)) { debugf("Ignoring SSDP response from %#a:%d", srcaddr, mDNSVal16(srcport)); return; } + else if (m->SSDPSocket && mDNSSameIPPort(dstport, m->SSDPSocket->port)) { debugf("Ignoring SSDP response from %#a:%d", srcaddr, mDNSVal16(srcport)); return; } #endif #endif - if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader)) - { - LogMsg("DNS Message from %#a:%d to %#a:%d length %d too short", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt); - 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) - ptr = (mDNSu8 *)&msg->h.numQuestions; - msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); - msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); - msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); - msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]); - - if (!m) { LogMsg("mDNSCoreReceive ERROR m is NULL"); return; } - - // We use zero addresses and all-ones addresses at various places in the code to indicate special values like "no address" - // If we accept and try to process a packet with zero or all-ones source address, that could really mess things up - if (srcaddr && !mDNSAddressIsValid(srcaddr)) { debugf("mDNSCoreReceive ignoring packet from %#a", srcaddr); return; } - - mDNS_Lock(m); - m->PktNum++; + if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader)) + { + LogMsg("DNS Message from %#a:%d to %#a:%d length %d too short", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt); + 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) + ptr = (mDNSu8 *)&msg->h.numQuestions; + msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); + msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); + msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]); + + if (!m) { LogMsg("mDNSCoreReceive ERROR m is NULL"); return; } + + // We use zero addresses and all-ones addresses at various places in the code to indicate special values like "no address" + // If we accept and try to process a packet with zero or all-ones source address, that could really mess things up + if (srcaddr && !mDNSAddressIsValid(srcaddr)) { debugf("mDNSCoreReceive ignoring packet from %#a", srcaddr); return; } + + mDNS_Lock(m); + m->PktNum++; #ifndef UNICAST_DISABLED - if (!dstaddr || (!mDNSAddressIsAllDNSLinkGroup(dstaddr) && (QR_OP == StdR || QR_OP == UpdR))) - if (!mDNSOpaque16IsZero(msg->h.id)) // uDNS_ReceiveMsg only needs to get real uDNS responses, not "QU" mDNS responses - { - ifid = mDNSInterface_Any; - if (mDNS_PacketLoggingEnabled) - DumpPacket(m, mStatus_NoError, mDNSfalse, TLS ? "TLS" : !dstaddr ? "TCP" : "UDP", srcaddr, srcport, dstaddr, dstport, msg, end); - uDNS_ReceiveMsg(m, msg, end, srcaddr, srcport); - // Note: mDNSCore also needs to get access to received unicast responses - } + if (!dstaddr || (!mDNSAddressIsAllDNSLinkGroup(dstaddr) && (QR_OP == StdR || QR_OP == UpdR))) + if (!mDNSOpaque16IsZero(msg->h.id)) // uDNS_ReceiveMsg only needs to get real uDNS responses, not "QU" mDNS responses + { + ifid = mDNSInterface_Any; + if (mDNS_PacketLoggingEnabled) + DumpPacket(m, mStatus_NoError, mDNSfalse, TLS ? "TLS" : !dstaddr ? "TCP" : "UDP", srcaddr, srcport, dstaddr, dstport, msg, end); + uDNS_ReceiveMsg(m, msg, end, srcaddr, srcport); + // Note: mDNSCore also needs to get access to received unicast responses + } #endif - if (QR_OP == StdQ) mDNSCoreReceiveQuery (m, msg, end, srcaddr, srcport, dstaddr, dstport, ifid); - else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, srcport, dstaddr, dstport, ifid); - else if (QR_OP == UpdQ) mDNSCoreReceiveUpdate (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID); - else if (QR_OP == UpdR) mDNSCoreReceiveUpdateR (m, msg, end, InterfaceID); - else - { - LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d length %d on %p (ignored)", - msg->h.flags.b[0], msg->h.flags.b[1], srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt, InterfaceID); - if (mDNS_LoggingEnabled) - { - int i = 0; - while (ih.flags.b[0], msg->h.flags.b[1], srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt, InterfaceID); + if (mDNS_LoggingEnabled) + { + int i = 0; + while (iTarget.type == mDNSAddrType_None && (B)->Target.type == mDNSAddrType_None) || \ - (mDNSSameAddress(&(A)->Target, &(B)->Target) && mDNSSameIPPort((A)->TargetPort, (B)->TargetPort))) + (mDNSSameAddress(& (A)->Target, & (B)->Target) && mDNSSameIPPort((A)->TargetPort, (B)->TargetPort))) // Note: We explicitly disallow making a public query be a duplicate of a private one. This is to avoid the // circular deadlock where a client does a query for something like "dns-sd -Q _dns-query-tls._tcp.company.com SRV" @@ -7607,168 +8688,172 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co #define IsLLQ(Q) ((Q)->LongLived && !mDNSOpaque16IsZero((Q)->TargetQID)) mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuestion *const question) - { - DNSQuestion *q; - // Note: A question can only be marked as a duplicate of one that occurs *earlier* in the list. - // This prevents circular references, where two questions are each marked as a duplicate of the other. - // Accordingly, we break out of the loop when we get to 'question', because there's no point searching - // further in the list. - for (q = m->Questions; q && q != question; q=q->next) // Scan our list for another question - if (q->InterfaceID == question->InterfaceID && // with the same InterfaceID, - SameQTarget(q, question) && // and same unicast/multicast target settings - q->qtype == question->qtype && // type, - q->qclass == question->qclass && // class, - IsLLQ(q) == IsLLQ(question) && // and long-lived status matches - (!q->AuthInfo || question->AuthInfo) && // to avoid deadlock, don't make public query dup of a private one - (q->SuppressQuery == question->SuppressQuery) && // Questions that are suppressed/not suppressed - q->qnamehash == question->qnamehash && - SameDomainName(&q->qname, &question->qname)) // and name - return(q); - return(mDNSNULL); - } +{ + DNSQuestion *q; + // Note: A question can only be marked as a duplicate of one that occurs *earlier* in the list. + // This prevents circular references, where two questions are each marked as a duplicate of the other. + // Accordingly, we break out of the loop when we get to 'question', because there's no point searching + // further in the list. + for (q = m->Questions; q && q != question; q=q->next) // Scan our list for another question + if (q->InterfaceID == question->InterfaceID && // with the same InterfaceID, + SameQTarget(q, question) && // and same unicast/multicast target settings + q->qtype == question->qtype && // type, + q->qclass == question->qclass && // class, + IsLLQ(q) == IsLLQ(question) && // and long-lived status matches + (!q->AuthInfo || question->AuthInfo) && // to avoid deadlock, don't make public query dup of a private one + (q->SuppressQuery == question->SuppressQuery) && // Questions that are suppressed/not suppressed + (q->ValidationRequired == question->ValidationRequired) && // Questions that require DNSSEC validation + (q->ValidatingResponse == question->ValidatingResponse) && // Questions that are validating responses using DNSSEC + q->qnamehash == question->qnamehash && + SameDomainName(&q->qname, &question->qname)) // and name + return(q); + return(mDNSNULL); +} // This is called after a question is deleted, in case other identical questions were being suppressed as duplicates mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const question) - { - DNSQuestion *q; - DNSQuestion *first = mDNSNULL; - - // This is referring to some other question as duplicate. No other question can refer to this - // question as a duplicate. - if (question->DuplicateOf) - { - LogInfo("UpdateQuestionDuplicates: question %p %##s (%s) duplicate of %p %##s (%s)", - question, question->qname.c, DNSTypeName(question->qtype), - question->DuplicateOf, question->DuplicateOf->qname.c, DNSTypeName(question->DuplicateOf->qtype)); - return; - } - - 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->DuplicateOf = first; - if (!first) - { - first = q; - // If q used to be a duplicate, but now is not, - // then inherit the state from the question that's going away - q->LastQTime = question->LastQTime; - q->ThisQInterval = question->ThisQInterval; - q->ExpectUnicastResp = question->ExpectUnicastResp; - q->LastAnswerPktNum = question->LastAnswerPktNum; - q->RecentAnswerPkts = question->RecentAnswerPkts; - q->RequestUnicast = question->RequestUnicast; - q->LastQTxTime = question->LastQTxTime; - q->CNAMEReferrals = question->CNAMEReferrals; - q->nta = question->nta; - q->servAddr = question->servAddr; - q->servPort = question->servPort; - q->qDNSServer = question->qDNSServer; - q->validDNSServers = question->validDNSServers; - q->unansweredQueries = question->unansweredQueries; - q->noServerResponse = question->noServerResponse; - q->triedAllServersOnce = question->triedAllServersOnce; - - q->TargetQID = question->TargetQID; - q->LocalSocket = question->LocalSocket; - - q->state = question->state; - // q->tcp = question->tcp; - q->ReqLease = question->ReqLease; - q->expire = question->expire; - q->ntries = question->ntries; - q->id = question->id; - - question->LocalSocket = mDNSNULL; - question->nta = mDNSNULL; // If we've got a GetZoneData in progress, transfer it to the newly active question - // question->tcp = mDNSNULL; - - if (q->LocalSocket) - debugf("UpdateQuestionDuplicates transferred LocalSocket pointer for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - - if (q->nta) - { - LogInfo("UpdateQuestionDuplicates transferred nta pointer for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - q->nta->ZoneDataContext = q; - } - - // Need to work out how to safely transfer this state too -- appropriate context pointers need to be updated or the code will crash - if (question->tcp) LogInfo("UpdateQuestionDuplicates did not transfer tcp pointer"); - - if (question->state == LLQ_Established) - { - LogInfo("UpdateQuestionDuplicates transferred LLQ state for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - question->state = 0; // Must zero question->state, or mDNS_StopQuery_internal will clean up and cancel our LLQ from the server - } - - SetNextQueryTime(m,q); - } - } - } +{ + DNSQuestion *q; + DNSQuestion *first = mDNSNULL; + + // This is referring to some other question as duplicate. No other question can refer to this + // question as a duplicate. + if (question->DuplicateOf) + { + LogInfo("UpdateQuestionDuplicates: question %p %##s (%s) duplicate of %p %##s (%s)", + question, question->qname.c, DNSTypeName(question->qtype), + question->DuplicateOf, question->DuplicateOf->qname.c, DNSTypeName(question->DuplicateOf->qtype)); + return; + } + + 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->DuplicateOf = first; + if (!first) + { + first = q; + // If q used to be a duplicate, but now is not, + // then inherit the state from the question that's going away + q->LastQTime = question->LastQTime; + q->ThisQInterval = question->ThisQInterval; + q->ExpectUnicastResp = question->ExpectUnicastResp; + q->LastAnswerPktNum = question->LastAnswerPktNum; + q->RecentAnswerPkts = question->RecentAnswerPkts; + q->RequestUnicast = question->RequestUnicast; + q->LastQTxTime = question->LastQTxTime; + q->CNAMEReferrals = question->CNAMEReferrals; + q->nta = question->nta; + q->servAddr = question->servAddr; + q->servPort = question->servPort; + q->qDNSServer = question->qDNSServer; + q->validDNSServers = question->validDNSServers; + q->unansweredQueries = question->unansweredQueries; + q->noServerResponse = question->noServerResponse; + q->triedAllServersOnce = question->triedAllServersOnce; + + q->TargetQID = question->TargetQID; + q->LocalSocket = question->LocalSocket; + + q->state = question->state; + // q->tcp = question->tcp; + q->ReqLease = question->ReqLease; + q->expire = question->expire; + q->ntries = question->ntries; + q->id = question->id; + q->ValidationState = question->ValidationState; + q->ValidationStatus = question->ValidationStatus; + + question->LocalSocket = mDNSNULL; + question->nta = mDNSNULL; // If we've got a GetZoneData in progress, transfer it to the newly active question + // question->tcp = mDNSNULL; + + if (q->LocalSocket) + debugf("UpdateQuestionDuplicates transferred LocalSocket pointer for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + + if (q->nta) + { + LogInfo("UpdateQuestionDuplicates transferred nta pointer for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->nta->ZoneDataContext = q; + } + + // Need to work out how to safely transfer this state too -- appropriate context pointers need to be updated or the code will crash + if (question->tcp) LogInfo("UpdateQuestionDuplicates did not transfer tcp pointer"); + + if (question->state == LLQ_Established) + { + LogInfo("UpdateQuestionDuplicates transferred LLQ state for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + question->state = 0; // Must zero question->state, or mDNS_StopQuery_internal will clean up and cancel our LLQ from the server + } + + SetNextQueryTime(m,q); + } + } +} mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, mDNSu32 timeout) - { - McastResolver **p = &m->McastResolvers; - McastResolver *tmp = mDNSNULL; - - if (!d) d = (const domainname *)""; - - LogInfo("mDNS_AddMcastResolver: Adding %##s, InterfaceID %p, timeout %u", d->c, interface, timeout); - - if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("mDNS_AddMcastResolver: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); - - while (*p) // Check if we already have this {interface, domain} tuple registered - { - if ((*p)->interface == interface && SameDomainName(&(*p)->domain, d)) - { - if (!((*p)->flags & DNSServer_FlagDelete)) LogMsg("Note: Mcast Resolver domain %##s (%p) registered more than once", d->c, interface); - (*p)->flags &= ~DNSServer_FlagDelete; - tmp = *p; - *p = tmp->next; - tmp->next = mDNSNULL; - } - else - p=&(*p)->next; - } - - if (tmp) *p = tmp; // move to end of list, to ensure ordering from platform layer - else - { - // allocate, add to list - *p = mDNSPlatformMemAllocate(sizeof(**p)); - if (!*p) LogMsg("mDNS_AddMcastResolver: ERROR!! - malloc"); - else - { - (*p)->interface = interface; - (*p)->flags = DNSServer_FlagNew; - (*p)->timeout = timeout; - AssignDomainName(&(*p)->domain, d); - (*p)->next = mDNSNULL; - } - } - return(*p); - } +{ + McastResolver **p = &m->McastResolvers; + McastResolver *tmp = mDNSNULL; + + if (!d) d = (const domainname *)""; + + LogInfo("mDNS_AddMcastResolver: Adding %##s, InterfaceID %p, timeout %u", d->c, interface, timeout); + + if (m->mDNS_busy != m->mDNS_reentrancy+1) + LogMsg("mDNS_AddMcastResolver: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + + while (*p) // Check if we already have this {interface, domain} tuple registered + { + if ((*p)->interface == interface && SameDomainName(&(*p)->domain, d)) + { + if (!((*p)->flags & DNSServer_FlagDelete)) LogMsg("Note: Mcast Resolver domain %##s (%p) registered more than once", d->c, interface); + (*p)->flags &= ~DNSServer_FlagDelete; + tmp = *p; + *p = tmp->next; + tmp->next = mDNSNULL; + } + else + p=&(*p)->next; + } + + if (tmp) *p = tmp; // move to end of list, to ensure ordering from platform layer + else + { + // allocate, add to list + *p = mDNSPlatformMemAllocate(sizeof(**p)); + if (!*p) LogMsg("mDNS_AddMcastResolver: ERROR!! - malloc"); + else + { + (*p)->interface = interface; + (*p)->flags = DNSServer_FlagNew; + (*p)->timeout = timeout; + AssignDomainName(&(*p)->domain, d); + (*p)->next = mDNSNULL; + } + } + return(*p); +} mDNSinline mDNSs32 PenaltyTimeForServer(mDNS *m, DNSServer *server) - { - mDNSs32 ptime = 0; - if (server->penaltyTime != 0) - { - ptime = server->penaltyTime - m->timenow; - if (ptime < 0) - { - // This should always be a positive value between 0 and DNSSERVER_PENALTY_TIME - // If it does not get reset in ResetDNSServerPenalties for some reason, we do it - // here - LogMsg("PenaltyTimeForServer: PenaltyTime negative %d, (server penaltyTime %d, timenow %d) resetting the penalty", - ptime, server->penaltyTime, m->timenow); - server->penaltyTime = 0; - ptime = 0; - } - } - return ptime; - } +{ + mDNSs32 ptime = 0; + if (server->penaltyTime != 0) + { + ptime = server->penaltyTime - m->timenow; + if (ptime < 0) + { + // This should always be a positive value between 0 and DNSSERVER_PENALTY_TIME + // If it does not get reset in ResetDNSServerPenalties for some reason, we do it + // here + LogMsg("PenaltyTimeForServer: PenaltyTime negative %d, (server penaltyTime %d, timenow %d) resetting the penalty", + ptime, server->penaltyTime, m->timenow); + server->penaltyTime = 0; + ptime = 0; + } + } + return ptime; +} //Checks to see whether the newname is a better match for the name, given the best one we have //seen so far (given in bestcount). @@ -7776,1346 +8861,1421 @@ mDNSinline mDNSs32 PenaltyTimeForServer(mDNS *m, DNSServer *server) //Returns 0 if the newname is the same as the old match //Returns 1 if the newname is a better match mDNSlocal int BetterMatchForName(const domainname *name, int namecount, const domainname *newname, int newcount, - int bestcount) - { - // If the name contains fewer labels than the new server's domain or the new name - // contains fewer labels than the current best, then it can't possibly be a better match - if (namecount < newcount || newcount < bestcount) return -1; - - // If there is no match, return -1 and the caller will skip this newname for - // selection - // - // If we find a match and the number of labels is the same as bestcount, then - // we return 0 so that the caller can do additional logic to pick one of - // the best based on some other factors e.g., penaltyTime - // - // If we find a match and the number of labels is more than bestcount, then we - // return 1 so that the caller can pick this over the old one. - // - // Note: newcount can either be equal or greater than bestcount beause of the - // check above. - - if (SameDomainName(SkipLeadingLabels(name, namecount - newcount), newname)) - return bestcount == newcount ? 0 : 1; - else - return -1; - } + int bestcount) +{ + // If the name contains fewer labels than the new server's domain or the new name + // contains fewer labels than the current best, then it can't possibly be a better match + if (namecount < newcount || newcount < bestcount) return -1; + + // If there is no match, return -1 and the caller will skip this newname for + // selection + // + // If we find a match and the number of labels is the same as bestcount, then + // we return 0 so that the caller can do additional logic to pick one of + // the best based on some other factors e.g., penaltyTime + // + // If we find a match and the number of labels is more than bestcount, then we + // return 1 so that the caller can pick this over the old one. + // + // Note: newcount can either be equal or greater than bestcount beause of the + // check above. + + if (SameDomainName(SkipLeadingLabels(name, namecount - newcount), newname)) + return bestcount == newcount ? 0 : 1; + else + return -1; +} // Normally, we have McastResolvers for .local, in-addr.arpa and ip6.arpa. But there // can be queries that can forced to multicast (ForceMCast) even though they don't end in these // names. In that case, we give a default timeout of 5 seconds -#define DEFAULT_MCAST_TIMEOUT 5 +#define DEFAULT_MCAST_TIMEOUT 5 mDNSlocal mDNSu32 GetTimeoutForMcastQuestion(mDNS *m, DNSQuestion *question) - { - McastResolver *curmatch = mDNSNULL; - int bestmatchlen = -1, namecount = CountLabels(&question->qname); - McastResolver *curr; - int bettermatch, currcount; - for (curr = m->McastResolvers; curr; curr = curr->next) - { - currcount = CountLabels(&curr->domain); - bettermatch = BetterMatchForName(&question->qname, namecount, &curr->domain, currcount, bestmatchlen); - // Take the first best match. If there are multiple equally good matches (bettermatch = 0), we take - // the timeout value from the first one - if (bettermatch == 1) - { - curmatch = curr; - bestmatchlen = currcount; - } - } - LogInfo("GetTimeoutForMcastQuestion: question %##s curmatch %p, Timeout %d", question->qname.c, curmatch, - curmatch ? curmatch->timeout : DEFAULT_MCAST_TIMEOUT); - return ( curmatch ? curmatch->timeout : DEFAULT_MCAST_TIMEOUT); - } +{ + McastResolver *curmatch = mDNSNULL; + int bestmatchlen = -1, namecount = CountLabels(&question->qname); + McastResolver *curr; + int bettermatch, currcount; + for (curr = m->McastResolvers; curr; curr = curr->next) + { + currcount = CountLabels(&curr->domain); + bettermatch = BetterMatchForName(&question->qname, namecount, &curr->domain, currcount, bestmatchlen); + // Take the first best match. If there are multiple equally good matches (bettermatch = 0), we take + // the timeout value from the first one + if (bettermatch == 1) + { + curmatch = curr; + bestmatchlen = currcount; + } + } + LogInfo("GetTimeoutForMcastQuestion: question %##s curmatch %p, Timeout %d", question->qname.c, curmatch, + curmatch ? curmatch->timeout : DEFAULT_MCAST_TIMEOUT); + return ( curmatch ? curmatch->timeout : DEFAULT_MCAST_TIMEOUT); +} // Returns true if it is a Domain Enumeration Query mDNSexport mDNSBool DomainEnumQuery(const domainname *qname) - { - const mDNSu8 *mDNS_DEQLabels[] = { (const mDNSu8 *)"\001b", (const mDNSu8 *)"\002db", (const mDNSu8 *)"\002lb", - (const mDNSu8 *)"\001r", (const mDNSu8 *)"\002dr", (const mDNSu8 *)mDNSNULL, }; - const domainname *d = qname; - const mDNSu8 *label; - int i = 0; - - // We need at least 3 labels (DEQ prefix) + one more label to make a meaningful DE query - if (CountLabels(qname) < 4) { debugf("DomainEnumQuery: question %##s, not enough labels", qname->c); return mDNSfalse; } - - label = (const mDNSu8 *)d; - while (mDNS_DEQLabels[i] != (const mDNSu8 *)mDNSNULL) - { - if (SameDomainLabel(mDNS_DEQLabels[i], label)) {debugf("DomainEnumQuery: DEQ %##s, label1 match", qname->c); break;} - i++; - } - if (mDNS_DEQLabels[i] == (const mDNSu8 *)mDNSNULL) - { - debugf("DomainEnumQuery: Not a DEQ %##s, label1 mismatch", qname->c); - return mDNSfalse; - } - debugf("DomainEnumQuery: DEQ %##s, label1 match", qname->c); - - // CountLabels already verified the number of labels - d = (const domainname *)(d->c + 1 + d->c[0]); // Second Label - label = (const mDNSu8 *)d; - if (!SameDomainLabel(label, (const mDNSu8 *)"\007_dns-sd")) - { - debugf("DomainEnumQuery: Not a DEQ %##s, label2 mismatch", qname->c); - return(mDNSfalse); - } - debugf("DomainEnumQuery: DEQ %##s, label2 match", qname->c); - - d = (const domainname *)(d->c + 1 + d->c[0]); // Third Label - label = (const mDNSu8 *)d; - if (!SameDomainLabel(label, (const mDNSu8 *)"\004_udp")) - { - debugf("DomainEnumQuery: Not a DEQ %##s, label3 mismatch", qname->c); - return(mDNSfalse); - } - debugf("DomainEnumQuery: DEQ %##s, label3 match", qname->c); - - debugf("DomainEnumQuery: Question %##s is a Domain Enumeration query", qname->c); - - return mDNStrue; - } +{ + const mDNSu8 *mDNS_DEQLabels[] = { (const mDNSu8 *)"\001b", (const mDNSu8 *)"\002db", (const mDNSu8 *)"\002lb", + (const mDNSu8 *)"\001r", (const mDNSu8 *)"\002dr", (const mDNSu8 *)mDNSNULL, }; + const domainname *d = qname; + const mDNSu8 *label; + int i = 0; + + // We need at least 3 labels (DEQ prefix) + one more label to make a meaningful DE query + if (CountLabels(qname) < 4) { debugf("DomainEnumQuery: question %##s, not enough labels", qname->c); return mDNSfalse; } + + label = (const mDNSu8 *)d; + while (mDNS_DEQLabels[i] != (const mDNSu8 *)mDNSNULL) + { + if (SameDomainLabel(mDNS_DEQLabels[i], label)) {debugf("DomainEnumQuery: DEQ %##s, label1 match", qname->c); break;} + i++; + } + if (mDNS_DEQLabels[i] == (const mDNSu8 *)mDNSNULL) + { + debugf("DomainEnumQuery: Not a DEQ %##s, label1 mismatch", qname->c); + return mDNSfalse; + } + debugf("DomainEnumQuery: DEQ %##s, label1 match", qname->c); + + // CountLabels already verified the number of labels + d = (const domainname *)(d->c + 1 + d->c[0]); // Second Label + label = (const mDNSu8 *)d; + if (!SameDomainLabel(label, (const mDNSu8 *)"\007_dns-sd")) + { + debugf("DomainEnumQuery: Not a DEQ %##s, label2 mismatch", qname->c); + return(mDNSfalse); + } + debugf("DomainEnumQuery: DEQ %##s, label2 match", qname->c); + + d = (const domainname *)(d->c + 1 + d->c[0]); // Third Label + label = (const mDNSu8 *)d; + if (!SameDomainLabel(label, (const mDNSu8 *)"\004_udp")) + { + debugf("DomainEnumQuery: Not a DEQ %##s, label3 mismatch", qname->c); + return(mDNSfalse); + } + debugf("DomainEnumQuery: DEQ %##s, label3 match", qname->c); + + debugf("DomainEnumQuery: Question %##s is a Domain Enumeration query", qname->c); + + return mDNStrue; +} // Sets all the Valid DNS servers for a question mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question) - { - DNSServer *curmatch = mDNSNULL; - int bestmatchlen = -1, namecount = CountLabels(&question->qname); - DNSServer *curr; - int bettermatch, currcount; - int index = 0; - mDNSu32 timeout = 0; - mDNSBool DEQuery; - - question->validDNSServers = zeroOpaque64; - DEQuery = DomainEnumQuery(&question->qname); - for (curr = m->DNSServers; curr; curr = curr->next) - { - debugf("SetValidDNSServers: Parsing DNS server Address %#a (Domain %##s), Scope: %d", &curr->addr, curr->domain.c, curr->scoped); - // skip servers that will soon be deleted - if (curr->flags & DNSServer_FlagDelete) - { debugf("SetValidDNSServers: Delete set for index %d, DNS server %#a (Domain %##s), scoped %d", index, &curr->addr, curr->domain.c, curr->scoped); continue; } - - // This happens normally when you unplug the interface where we reset the interfaceID to mDNSInterface_Any for all - // the DNS servers whose scope match the interfaceID. Few seconds later, we also receive the updated DNS configuration. - // But any questions that has mDNSInterface_Any scope that are started/restarted before we receive the update - // (e.g., CheckSuppressUnusableQuestions is called when interfaces are deregistered with the core) should not - // match the scoped entries by mistake. - // - // Note: DNS configuration change will help pick the new dns servers but currently it does not affect the timeout - - if (curr->scoped && curr->interface == mDNSInterface_Any) - { debugf("SetValidDNSServers: Scoped DNS server %#a (Domain %##s) with Interface Any", &curr->addr, curr->domain.c); continue; } - - currcount = CountLabels(&curr->domain); - if ((!DEQuery || !curr->cellIntf) && - ((!curr->scoped && (!question->InterfaceID || (question->InterfaceID == mDNSInterface_Unicast))) || - (curr->interface == question->InterfaceID))) - { - bettermatch = BetterMatchForName(&question->qname, namecount, &curr->domain, currcount, bestmatchlen); - - // If we found a better match (bettermatch == 1) then clear all the bits - // corresponding to the old DNSServers that we have may set before and start fresh. - // If we find an equal match, then include that DNSServer also by setting the corresponding - // bit - if ((bettermatch == 1) || (bettermatch == 0)) - { - curmatch = curr; - bestmatchlen = currcount; - if (bettermatch) { debugf("SetValidDNSServers: Resetting all the bits"); question->validDNSServers = zeroOpaque64; timeout = 0; } - debugf("SetValidDNSServers: question %##s Setting the bit for DNS server Address %#a (Domain %##s), Scoped:%d index %d," - " Timeout %d, interface %p", question->qname.c, &curr->addr, curr->domain.c, curr->scoped, index, curr->timeout, - curr->interface); - timeout += curr->timeout; - if (DEQuery) debugf("DomainEnumQuery: Question %##s, DNSServer %#a, cell %d", question->qname.c, &curr->addr, curr->cellIntf); - bit_set_opaque64(question->validDNSServers, index); - } - } - index++; - } - question->noServerResponse = 0; - - debugf("SetValidDNSServers: ValidDNSServer bits 0x%x%x for question %p %##s (%s)", - question->validDNSServers.l[1], question->validDNSServers.l[0], question, question->qname.c, DNSTypeName(question->qtype)); - // If there are no matching resolvers, then use the default value to timeout - return (timeout ? timeout : DEFAULT_UDNS_TIMEOUT); - } +{ + DNSServer *curmatch = mDNSNULL; + int bestmatchlen = -1, namecount = CountLabels(&question->qname); + DNSServer *curr; + int bettermatch, currcount; + int index = 0; + mDNSu32 timeout = 0; + mDNSBool DEQuery; + + question->validDNSServers = zeroOpaque64; + DEQuery = DomainEnumQuery(&question->qname); + for (curr = m->DNSServers; curr; curr = curr->next) + { + debugf("SetValidDNSServers: Parsing DNS server Address %#a (Domain %##s), Scope: %d", &curr->addr, curr->domain.c, curr->scoped); + // skip servers that will soon be deleted + if (curr->flags & DNSServer_FlagDelete) + { debugf("SetValidDNSServers: Delete set for index %d, DNS server %#a (Domain %##s), scoped %d", index, &curr->addr, curr->domain.c, curr->scoped); continue; } + + // This happens normally when you unplug the interface where we reset the interfaceID to mDNSInterface_Any for all + // the DNS servers whose scope match the interfaceID. Few seconds later, we also receive the updated DNS configuration. + // But any questions that has mDNSInterface_Any scope that are started/restarted before we receive the update + // (e.g., CheckSuppressUnusableQuestions is called when interfaces are deregistered with the core) should not + // match the scoped entries by mistake. + // + // Note: DNS configuration change will help pick the new dns servers but currently it does not affect the timeout + + if (curr->scoped && curr->interface == mDNSInterface_Any) + { debugf("SetValidDNSServers: Scoped DNS server %#a (Domain %##s) with Interface Any", &curr->addr, curr->domain.c); continue; } + + currcount = CountLabels(&curr->domain); + if ((!DEQuery || !curr->cellIntf) && + ((!curr->scoped && (!question->InterfaceID || (question->InterfaceID == mDNSInterface_Unicast))) || + (curr->interface == question->InterfaceID))) + { + bettermatch = BetterMatchForName(&question->qname, namecount, &curr->domain, currcount, bestmatchlen); + + // If we found a better match (bettermatch == 1) then clear all the bits + // corresponding to the old DNSServers that we have may set before and start fresh. + // If we find an equal match, then include that DNSServer also by setting the corresponding + // bit + if ((bettermatch == 1) || (bettermatch == 0)) + { + curmatch = curr; + bestmatchlen = currcount; + if (bettermatch) { debugf("SetValidDNSServers: Resetting all the bits"); question->validDNSServers = zeroOpaque64; timeout = 0; } + debugf("SetValidDNSServers: question %##s Setting the bit for DNS server Address %#a (Domain %##s), Scoped:%d index %d," + " Timeout %d, interface %p", question->qname.c, &curr->addr, curr->domain.c, curr->scoped, index, curr->timeout, + curr->interface); + timeout += curr->timeout; + if (DEQuery) debugf("DomainEnumQuery: Question %##s, DNSServer %#a, cell %d", question->qname.c, &curr->addr, curr->cellIntf); + bit_set_opaque64(question->validDNSServers, index); + } + } + index++; + } + question->noServerResponse = 0; + + debugf("SetValidDNSServers: ValidDNSServer bits 0x%x%x for question %p %##s (%s)", + question->validDNSServers.l[1], question->validDNSServers.l[0], question, question->qname.c, DNSTypeName(question->qtype)); + // If there are no matching resolvers, then use the default value to timeout + return (question->ValidatingResponse ? DEFAULT_UDNSSEC_TIMEOUT : timeout ? timeout : DEFAULT_UDNS_TIMEOUT); +} // Get the Best server that matches a name. If you find penalized servers, look for the one // that will come out of the penalty box soon mDNSlocal DNSServer *GetBestServer(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID, mDNSOpaque64 validBits, int *selected, mDNSBool nameMatch) - { - DNSServer *curmatch = mDNSNULL; - int bestmatchlen = -1, namecount = name ? CountLabels(name) : 0; - DNSServer *curr; - mDNSs32 bestPenaltyTime, currPenaltyTime; - int bettermatch, currcount; - int index = 0; - int currindex = -1; - - debugf("GetBestServer: ValidDNSServer bits 0x%x%x", validBits.l[1], validBits.l[0]); - bestPenaltyTime = DNSSERVER_PENALTY_TIME + 1; - for (curr = m->DNSServers; curr; curr = curr->next) - { - // skip servers that will soon be deleted - if (curr->flags & DNSServer_FlagDelete) - { debugf("GetBestServer: Delete set for index %d, DNS server %#a (Domain %##s), scoped %d", index, &curr->addr, curr->domain.c, curr->scoped); continue; } - - // Check if this is a valid DNSServer - if (!bit_get_opaque64(validBits, index)) { debugf("GetBestServer: continuing for index %d", index); index++; continue; } - - currcount = CountLabels(&curr->domain); - currPenaltyTime = PenaltyTimeForServer(m, curr); - - debugf("GetBestServer: Address %#a (Domain %##s), PenaltyTime(abs) %d, PenaltyTime(rel) %d", - &curr->addr, curr->domain.c, curr->penaltyTime, currPenaltyTime); - - // If there are multiple best servers for a given question, we will pick the first one - // if none of them are penalized. If some of them are penalized in that list, we pick - // the least penalized one. BetterMatchForName walks through all best matches and - // "currPenaltyTime < bestPenaltyTime" check lets us either pick the first best server - // in the list when there are no penalized servers and least one among them - // when there are some penalized servers - // - // Notes on InterfaceID matching: - // - // 1) A DNSServer entry may have an InterfaceID but the scoped flag may not be set. This - // is the old way of specifying an InterfaceID option for DNSServer. We recoginize these - // entries by "scoped" being false. These are like any other unscoped entries except that - // if it is picked e.g., domain match, when the packet is sent out later, the packet will - // be sent out on that interface. Theese entries can be matched by either specifying a - // zero InterfaceID or non-zero InterfaceID on the question. Specifying an InterfaceID on - // the question will cause an extra check on matching the InterfaceID on the question - // against the DNSServer. - // - // 2) A DNSServer may also have both scoped set and InterfaceID non-NULL. This - // is the new way of specifying an InterfaceID option for DNSServer. These will be considered - // only when the question has non-zero interfaceID. - - if ((!curr->scoped && !InterfaceID) || (curr->interface == InterfaceID)) - { - - // If we know that all the names are already equally good matches, then skip calling BetterMatchForName. - // This happens when we initially walk all the DNS servers and set the validity bit on the question. - // Actually we just need PenaltyTime match, but for the sake of readability we just skip the expensive - // part and still do some redundant steps e.g., InterfaceID match - - if (nameMatch) bettermatch = BetterMatchForName(name, namecount, &curr->domain, currcount, bestmatchlen); - else bettermatch = 0; - - // If we found a better match (bettermatch == 1) then we don't need to - // compare penalty times. But if we found an equal match, then we compare - // the penalty times to pick a better match - - if ((bettermatch == 1) || ((bettermatch == 0) && currPenaltyTime < bestPenaltyTime)) - { currindex = index; curmatch = curr; bestmatchlen = currcount; bestPenaltyTime = currPenaltyTime; } - } - index++; - } - if (selected) *selected = currindex; - return curmatch; - } +{ + DNSServer *curmatch = mDNSNULL; + int bestmatchlen = -1, namecount = name ? CountLabels(name) : 0; + DNSServer *curr; + mDNSs32 bestPenaltyTime, currPenaltyTime; + int bettermatch, currcount; + int index = 0; + int currindex = -1; + + debugf("GetBestServer: ValidDNSServer bits 0x%x%x", validBits.l[1], validBits.l[0]); + bestPenaltyTime = DNSSERVER_PENALTY_TIME + 1; + for (curr = m->DNSServers; curr; curr = curr->next) + { + // skip servers that will soon be deleted + if (curr->flags & DNSServer_FlagDelete) + { debugf("GetBestServer: Delete set for index %d, DNS server %#a (Domain %##s), scoped %d", index, &curr->addr, curr->domain.c, curr->scoped); continue; } + + // Check if this is a valid DNSServer + if (!bit_get_opaque64(validBits, index)) { debugf("GetBestServer: continuing for index %d", index); index++; continue; } + + currcount = CountLabels(&curr->domain); + currPenaltyTime = PenaltyTimeForServer(m, curr); + + debugf("GetBestServer: Address %#a (Domain %##s), PenaltyTime(abs) %d, PenaltyTime(rel) %d", + &curr->addr, curr->domain.c, curr->penaltyTime, currPenaltyTime); + + // If there are multiple best servers for a given question, we will pick the first one + // if none of them are penalized. If some of them are penalized in that list, we pick + // the least penalized one. BetterMatchForName walks through all best matches and + // "currPenaltyTime < bestPenaltyTime" check lets us either pick the first best server + // in the list when there are no penalized servers and least one among them + // when there are some penalized servers + // + // Notes on InterfaceID matching: + // + // 1) A DNSServer entry may have an InterfaceID but the scoped flag may not be set. This + // is the old way of specifying an InterfaceID option for DNSServer. We recoginize these + // entries by "scoped" being false. These are like any other unscoped entries except that + // if it is picked e.g., domain match, when the packet is sent out later, the packet will + // be sent out on that interface. Theese entries can be matched by either specifying a + // zero InterfaceID or non-zero InterfaceID on the question. Specifying an InterfaceID on + // the question will cause an extra check on matching the InterfaceID on the question + // against the DNSServer. + // + // 2) A DNSServer may also have both scoped set and InterfaceID non-NULL. This + // is the new way of specifying an InterfaceID option for DNSServer. These will be considered + // only when the question has non-zero interfaceID. + + if ((!curr->scoped && !InterfaceID) || (curr->interface == InterfaceID)) + { + + // If we know that all the names are already equally good matches, then skip calling BetterMatchForName. + // This happens when we initially walk all the DNS servers and set the validity bit on the question. + // Actually we just need PenaltyTime match, but for the sake of readability we just skip the expensive + // part and still do some redundant steps e.g., InterfaceID match + + if (nameMatch) bettermatch = BetterMatchForName(name, namecount, &curr->domain, currcount, bestmatchlen); + else bettermatch = 0; + + // If we found a better match (bettermatch == 1) then we don't need to + // compare penalty times. But if we found an equal match, then we compare + // the penalty times to pick a better match + + if ((bettermatch == 1) || ((bettermatch == 0) && currPenaltyTime < bestPenaltyTime)) + { currindex = index; curmatch = curr; bestmatchlen = currcount; bestPenaltyTime = currPenaltyTime; } + } + index++; + } + if (selected) *selected = currindex; + return curmatch; +} // Look up a DNS Server, matching by name and InterfaceID mDNSexport DNSServer *GetServerForName(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID) - { - DNSServer *curmatch = mDNSNULL; - char *ifname = mDNSNULL; // for logging purposes only - mDNSOpaque64 allValid; +{ + DNSServer *curmatch = mDNSNULL; + char *ifname = mDNSNULL; // for logging purposes only + mDNSOpaque64 allValid; - if ((InterfaceID == mDNSInterface_Unicast) || (InterfaceID == mDNSInterface_LocalOnly)) - InterfaceID = mDNSNULL; + if ((InterfaceID == mDNSInterface_Unicast) || (InterfaceID == mDNSInterface_LocalOnly)) + InterfaceID = mDNSNULL; - if (InterfaceID) ifname = InterfaceNameForID(m, InterfaceID); + if (InterfaceID) ifname = InterfaceNameForID(m, InterfaceID); - // By passing in all ones, we make sure that every DNS server is considered - allValid.l[0] = allValid.l[1] = 0xFFFFFFFF; + // By passing in all ones, we make sure that every DNS server is considered + allValid.l[0] = allValid.l[1] = 0xFFFFFFFF; - curmatch = GetBestServer(m, name, InterfaceID, allValid, mDNSNULL, mDNStrue); + curmatch = GetBestServer(m, name, InterfaceID, allValid, mDNSNULL, mDNStrue); - if (curmatch != mDNSNULL) - LogInfo("GetServerForName: DNS server %#a:%d (Penalty Time Left %d) (Scope %s:%p) found for name %##s", &curmatch->addr, - mDNSVal16(curmatch->port), (curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0), ifname ? ifname : "None", - InterfaceID, name); - else - LogInfo("GetServerForName: no DNS server (Scope %s:%p) found for name %##s", ifname ? ifname : "None", InterfaceID, name); + if (curmatch != mDNSNULL) + LogInfo("GetServerForName: DNS server %#a:%d (Penalty Time Left %d) (Scope %s:%p) found for name %##s", &curmatch->addr, + mDNSVal16(curmatch->port), (curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0), ifname ? ifname : "None", + InterfaceID, name); + else + LogInfo("GetServerForName: no DNS server (Scope %s:%p) found for name %##s", ifname ? ifname : "None", InterfaceID, name); - return(curmatch); - } + return(curmatch); +} // Look up a DNS Server for a question within its valid DNSServer bits mDNSexport DNSServer *GetServerForQuestion(mDNS *m, DNSQuestion *question) - { - DNSServer *curmatch = mDNSNULL; - char *ifname = mDNSNULL; // for logging purposes only - mDNSInterfaceID InterfaceID = question->InterfaceID; - const domainname *name = &question->qname; - int currindex; +{ + DNSServer *curmatch = mDNSNULL; + char *ifname = mDNSNULL; // for logging purposes only + mDNSInterfaceID InterfaceID = question->InterfaceID; + const domainname *name = &question->qname; + int currindex; - if ((InterfaceID == mDNSInterface_Unicast) || (InterfaceID == mDNSInterface_LocalOnly)) - InterfaceID = mDNSNULL; + if ((InterfaceID == mDNSInterface_Unicast) || (InterfaceID == mDNSInterface_LocalOnly)) + InterfaceID = mDNSNULL; - if (InterfaceID) ifname = InterfaceNameForID(m, InterfaceID); + if (InterfaceID) ifname = InterfaceNameForID(m, InterfaceID); - if (!mDNSOpaque64IsZero(&question->validDNSServers)) - { - curmatch = GetBestServer(m, name, InterfaceID, question->validDNSServers, &currindex, mDNSfalse); - if (currindex != -1) bit_clr_opaque64(question->validDNSServers, currindex); - } + if (!mDNSOpaque64IsZero(&question->validDNSServers)) + { + curmatch = GetBestServer(m, name, InterfaceID, question->validDNSServers, &currindex, mDNSfalse); + if (currindex != -1) bit_clr_opaque64(question->validDNSServers, currindex); + } - if (curmatch != mDNSNULL) - LogInfo("GetServerForQuestion: %p DNS server %#a:%d (Penalty Time Left %d) (Scope %s:%p) found for name %##s (%s)", question, &curmatch->addr, - mDNSVal16(curmatch->port), (curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0), ifname ? ifname : "None", - InterfaceID, name, DNSTypeName(question->qtype)); - else - LogInfo("GetServerForQuestion: %p no DNS server (Scope %s:%p) found for name %##s (%s)", question, ifname ? ifname : "None", InterfaceID, name, DNSTypeName(question->qtype)); + if (curmatch != mDNSNULL) + LogInfo("GetServerForQuestion: %p DNS server %#a:%d (Penalty Time Left %d) (Scope %s:%p) found for name %##s (%s)", question, &curmatch->addr, + mDNSVal16(curmatch->port), (curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0), ifname ? ifname : "None", + InterfaceID, name, DNSTypeName(question->qtype)); + else + LogInfo("GetServerForQuestion: %p no DNS server (Scope %s:%p) found for name %##s (%s)", question, ifname ? ifname : "None", InterfaceID, name, DNSTypeName(question->qtype)); - return(curmatch); - } + return(curmatch); +} #define ValidQuestionTarget(Q) (((Q)->Target.type == mDNSAddrType_IPv4 || (Q)->Target.type == mDNSAddrType_IPv6) && \ - (mDNSSameIPPort((Q)->TargetPort, UnicastDNSPort) || mDNSSameIPPort((Q)->TargetPort, MulticastDNSPort))) + (mDNSSameIPPort((Q)->TargetPort, UnicastDNSPort) || mDNSSameIPPort((Q)->TargetPort, MulticastDNSPort))) // Called in normal client context (lock not held) mDNSlocal void LLQNATCallback(mDNS *m, NATTraversalInfo *n) - { - DNSQuestion *q; - (void)n; // Unused - mDNS_Lock(m); - LogInfo("LLQNATCallback external address:port %.4a:%u, NAT result %d", &n->ExternalAddress, mDNSVal16(n->ExternalPort), n->Result); - for (q = m->Questions; q; q=q->next) - if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID) && q->LongLived) - startLLQHandshake(m, q); // If ExternalPort is zero, will do StartLLQPolling instead +{ + DNSQuestion *q; + (void)n; // Unused + mDNS_Lock(m); + LogInfo("LLQNATCallback external address:port %.4a:%u, NAT result %d", &n->ExternalAddress, mDNSVal16(n->ExternalPort), n->Result); + for (q = m->Questions; q; q=q->next) + if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID) && q->LongLived) + startLLQHandshake(m, q); // If ExternalPort is zero, will do StartLLQPolling instead #if APPLE_OSX_mDNSResponder - UpdateAutoTunnelDomainStatuses(m); + UpdateAutoTunnelDomainStatuses(m); #endif - mDNS_Unlock(m); - } + mDNS_Unlock(m); +} + +mDNSlocal mDNSBool IsAutoTunnelAddress(mDNS *const m, const mDNSv6Addr a) +{ + DomainAuthInfo *ai = mDNSNULL; + + if (mDNSSameIPv6Address(a, m->AutoTunnelRelayAddr)) + return mDNStrue; + + for (ai = m->AuthInfoList; ai; ai = ai->next) + { + if (!ai->deltime && ai->AutoTunnel && mDNSSameIPv6Address(a, ai->AutoTunnelInnerAddress)) + { + return mDNStrue; + } + } + + return mDNSfalse; +} mDNSlocal mDNSBool ShouldSuppressQuery(mDNS *const m, domainname *qname, mDNSu16 qtype, mDNSInterfaceID InterfaceID) - { - NetworkInterfaceInfo *i; - mDNSs32 iptype; - DomainAuthInfo *AuthInfo; - - if (qtype == kDNSType_A) iptype = mDNSAddrType_IPv4; - else if (qtype == kDNSType_AAAA) iptype = mDNSAddrType_IPv6; - else { LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, not A/AAAA type", qname, DNSTypeName(qtype)); return mDNSfalse; } - - // We still want the ability to be able to listen to the local services and hence - // don't fail .local requests. We always have a loopback interface which we don't - // check here. - if (InterfaceID != mDNSInterface_Unicast && IsLocalDomain(qname)) { LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local question", qname, DNSTypeName(qtype)); return mDNSfalse; } - - // Skip Private domains as we have special addresses to get the hosts in the Private domain - AuthInfo = GetAuthInfoForName_internal(m, qname); - if (AuthInfo && !AuthInfo->deltime && AuthInfo->AutoTunnel) - { LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Private Domain", qname, DNSTypeName(qtype)); return mDNSfalse; } - - // Match on Type, Address and InterfaceID - // - // Check whether we are looking for a name that ends in .local, then presence of a link-local - // address on the interface is sufficient. - for (i = m->HostInterfaces; i; i = i->next) - { - if (i->ip.type != iptype) continue; - - if (!InterfaceID || (InterfaceID == mDNSInterface_LocalOnly) || (InterfaceID == mDNSInterface_P2P) || - (InterfaceID == mDNSInterface_Unicast) || (i->InterfaceID == InterfaceID)) - { - if (iptype == mDNSAddrType_IPv4 && !mDNSv4AddressIsLoopback(&i->ip.ip.v4) && !mDNSv4AddressIsLinkLocal(&i->ip.ip.v4)) - { - LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local Address %.4a found", qname, DNSTypeName(qtype), - &i->ip.ip.v4); - return mDNSfalse; - } - else if (iptype == mDNSAddrType_IPv6 && - !mDNSv6AddressIsLoopback(&i->ip.ip.v6) && - !mDNSv6AddressIsLinkLocal(&i->ip.ip.v6) && - !mDNSSameIPv6Address(i->ip.ip.v6, m->AutoTunnelHostAddr) && - !mDNSSameIPv6Address(i->ip.ip.v6, m->AutoTunnelRelayAddrOut)) - { - LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local Address %.16a found", qname, DNSTypeName(qtype), - &i->ip.ip.v6); - return mDNSfalse; - } - } - } - LogInfo("ShouldSuppressQuery: Query suppressed for %##s, qtype %s, because no matching interface found", qname, DNSTypeName(qtype)); - return mDNStrue; - } +{ + NetworkInterfaceInfo *i; + mDNSs32 iptype; + DomainAuthInfo *AuthInfo; + + if (qtype == kDNSType_A) iptype = mDNSAddrType_IPv4; + else if (qtype == kDNSType_AAAA) iptype = mDNSAddrType_IPv6; + else { LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, not A/AAAA type", qname, DNSTypeName(qtype)); return mDNSfalse; } + + // We still want the ability to be able to listen to the local services and hence + // don't fail .local requests. We always have a loopback interface which we don't + // check here. + if (InterfaceID != mDNSInterface_Unicast && IsLocalDomain(qname)) { LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local question", qname, DNSTypeName(qtype)); return mDNSfalse; } + + // Skip Private domains as we have special addresses to get the hosts in the Private domain + AuthInfo = GetAuthInfoForName_internal(m, qname); + if (AuthInfo && !AuthInfo->deltime && AuthInfo->AutoTunnel) + { LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Private Domain", qname, DNSTypeName(qtype)); return mDNSfalse; } + + // Match on Type, Address and InterfaceID + // + // Check whether we are looking for a name that ends in .local, then presence of a link-local + // address on the interface is sufficient. + for (i = m->HostInterfaces; i; i = i->next) + { + if (i->ip.type != iptype) continue; + + if (!InterfaceID || (InterfaceID == mDNSInterface_LocalOnly) || (InterfaceID == mDNSInterface_P2P) || + (InterfaceID == mDNSInterface_Unicast) || (i->InterfaceID == InterfaceID)) + { + if (iptype == mDNSAddrType_IPv4 && !mDNSv4AddressIsLoopback(&i->ip.ip.v4) && !mDNSv4AddressIsLinkLocal(&i->ip.ip.v4)) + { + LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local Address %.4a found", qname, DNSTypeName(qtype), + &i->ip.ip.v4); + if (m->SleepState == SleepState_Sleeping) + LogInfo("ShouldSuppressQuery: Would have returned true earlier"); + return mDNSfalse; + } + else if (iptype == mDNSAddrType_IPv6 && + !mDNSv6AddressIsLoopback(&i->ip.ip.v6) && + !mDNSv6AddressIsLinkLocal(&i->ip.ip.v6) && + !IsAutoTunnelAddress(m, i->ip.ip.v6)) + { + LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local Address %.16a found", qname, DNSTypeName(qtype), + &i->ip.ip.v6); + if (m->SleepState == SleepState_Sleeping) + LogInfo("ShouldSuppressQuery: Would have returned true earlier"); + return mDNSfalse; + } + } + } + LogInfo("ShouldSuppressQuery: Query suppressed for %##s, qtype %s, because no matching interface found", qname, DNSTypeName(qtype)); + return mDNStrue; +} mDNSlocal void CacheRecordRmvEventsForCurrentQuestion(mDNS *const m, DNSQuestion *q) - { - CacheRecord *rr; - mDNSu32 slot; - CacheGroup *cg; - - slot = HashSlot(&q->qname); - cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) - { - // Don't deliver RMV events for negative records - if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) - { - LogInfo("CacheRecordRmvEventsForCurrentQuestion: CacheRecord %s Suppressing RMV events for question %p %##s (%s), CRActiveQuestion %p, CurrentAnswers %d", - CRDisplayString(m, rr), q, q->qname.c, DNSTypeName(q->qtype), rr->CRActiveQuestion, q->CurrentAnswers); - continue; - } - - if (SameNameRecordAnswersQuestion(&rr->resrec, q)) - { - LogInfo("CacheRecordRmvEventsForCurrentQuestion: Calling AnswerCurrentQuestionWithResourceRecord (RMV) for question %##s using resource record %s LocalAnswers %d", - q->qname.c, CRDisplayString(m, rr), q->LOAddressAnswers); - - q->CurrentAnswers--; - if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--; - if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--; - - if (rr->CRActiveQuestion == q) - { - DNSQuestion *qptr; - // If this was the active question for this cache entry, it was the one that was - // responsible for keeping the cache entry fresh when the cache entry was reaching - // its expiry. We need to handover the responsibility to someone else. Otherwise, - // when the cache entry is about to expire, we won't find an active question - // (pointed by CRActiveQuestion) to refresh the cache. - for (qptr = m->Questions; qptr; qptr=qptr->next) - if (qptr != q && ActiveQuestion(qptr) && ResourceRecordAnswersQuestion(&rr->resrec, qptr)) - break; - - if (qptr) - LogInfo("CacheRecordRmvEventsForCurrentQuestion: Updating CRActiveQuestion to %p for cache record %s, " - "Original question CurrentAnswers %d, new question CurrentAnswers %d, SuppressUnusable %d, SuppressQuery %d", - qptr, CRDisplayString(m,rr), q->CurrentAnswers, qptr->CurrentAnswers, qptr->SuppressUnusable, qptr->SuppressQuery); - - rr->CRActiveQuestion = qptr; // Question used to be active; new value may or may not be null - if (!qptr) m->rrcache_active--; // If no longer active, decrement rrcache_active count - } - AnswerCurrentQuestionWithResourceRecord(m, rr, QC_rmv); - if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here - } - } - } +{ + CacheRecord *rr; + mDNSu32 slot; + CacheGroup *cg; + + slot = HashSlot(&q->qname); + cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + { + // Don't deliver RMV events for negative records + if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) + { + LogInfo("CacheRecordRmvEventsForCurrentQuestion: CacheRecord %s Suppressing RMV events for question %p %##s (%s), CRActiveQuestion %p, CurrentAnswers %d", + CRDisplayString(m, rr), q, q->qname.c, DNSTypeName(q->qtype), rr->CRActiveQuestion, q->CurrentAnswers); + continue; + } + + if (SameNameRecordAnswersQuestion(&rr->resrec, q)) + { + LogInfo("CacheRecordRmvEventsForCurrentQuestion: Calling AnswerCurrentQuestionWithResourceRecord (RMV) for question %##s using resource record %s LocalAnswers %d", + q->qname.c, CRDisplayString(m, rr), q->LOAddressAnswers); + + q->CurrentAnswers--; + if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--; + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--; + + if (rr->CRActiveQuestion == q) + { + DNSQuestion *qptr; + // If this was the active question for this cache entry, it was the one that was + // responsible for keeping the cache entry fresh when the cache entry was reaching + // its expiry. We need to handover the responsibility to someone else. Otherwise, + // when the cache entry is about to expire, we won't find an active question + // (pointed by CRActiveQuestion) to refresh the cache. + for (qptr = m->Questions; qptr; qptr=qptr->next) + if (qptr != q && ActiveQuestion(qptr) && ResourceRecordAnswersQuestion(&rr->resrec, qptr)) + break; + + if (qptr) + LogInfo("CacheRecordRmvEventsForCurrentQuestion: Updating CRActiveQuestion to %p for cache record %s, " + "Original question CurrentAnswers %d, new question CurrentAnswers %d, SuppressUnusable %d, SuppressQuery %d", + qptr, CRDisplayString(m,rr), q->CurrentAnswers, qptr->CurrentAnswers, qptr->SuppressUnusable, qptr->SuppressQuery); + + rr->CRActiveQuestion = qptr; // Question used to be active; new value may or may not be null + if (!qptr) m->rrcache_active--; // If no longer active, decrement rrcache_active count + } + AnswerCurrentQuestionWithResourceRecord(m, rr, QC_rmv); + if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here + } + } +} mDNSlocal mDNSBool IsQuestionNew(mDNS *const m, DNSQuestion *question) - { - DNSQuestion *q; - for (q = m->NewQuestions; q; q = q->next) - if (q == question) return mDNStrue; - return mDNSfalse; - } +{ + DNSQuestion *q; + for (q = m->NewQuestions; q; q = q->next) + if (q == question) return mDNStrue; + return mDNSfalse; +} mDNSlocal mDNSBool LocalRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q) - { - AuthRecord *rr; - mDNSu32 slot; - AuthGroup *ag; - - if (m->CurrentQuestion) - LogMsg("LocalRecordRmvEventsForQuestion: ERROR m->CurrentQuestion already set: %##s (%s)", - m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); - - if (IsQuestionNew(m, q)) - { - LogInfo("LocalRecordRmvEventsForQuestion: New Question %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - return mDNStrue; - } - m->CurrentQuestion = q; - slot = AuthHashSlot(&q->qname); - ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname); - if (ag) - { - for (rr = ag->members; rr; rr=rr->next) - // Filter the /etc/hosts records - LocalOnly, Unique, A/AAAA/CNAME - if (LORecordAnswersAddressType(rr) && LocalOnlyRecordAnswersQuestion(rr, q)) - { - LogInfo("LocalRecordRmvEventsForQuestion: Delivering possible Rmv events with record %s", - ARDisplayString(m, rr)); - if (q->CurrentAnswers <= 0 || q->LOAddressAnswers <= 0) - { - LogMsg("LocalRecordRmvEventsForQuestion: ERROR!! CurrentAnswers or LOAddressAnswers is zero %p %##s" - " (%s) CurrentAnswers %d, LOAddressAnswers %d", q, q->qname.c, DNSTypeName(q->qtype), - q->CurrentAnswers, q->LOAddressAnswers); - continue; - } - AnswerLocalQuestionWithLocalAuthRecord(m, rr, QC_rmv); // MUST NOT dereference q again - if (m->CurrentQuestion != q) { m->CurrentQuestion = mDNSNULL; return mDNSfalse; } - } - } - m->CurrentQuestion = mDNSNULL; - return mDNStrue; - } +{ + AuthRecord *rr; + mDNSu32 slot; + AuthGroup *ag; + + if (m->CurrentQuestion) + LogMsg("LocalRecordRmvEventsForQuestion: ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + + if (IsQuestionNew(m, q)) + { + LogInfo("LocalRecordRmvEventsForQuestion: New Question %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + return mDNStrue; + } + m->CurrentQuestion = q; + slot = AuthHashSlot(&q->qname); + ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname); + if (ag) + { + for (rr = ag->members; rr; rr=rr->next) + // Filter the /etc/hosts records - LocalOnly, Unique, A/AAAA/CNAME + if (LORecordAnswersAddressType(rr) && LocalOnlyRecordAnswersQuestion(rr, q)) + { + LogInfo("LocalRecordRmvEventsForQuestion: Delivering possible Rmv events with record %s", + ARDisplayString(m, rr)); + if (q->CurrentAnswers <= 0 || q->LOAddressAnswers <= 0) + { + LogMsg("LocalRecordRmvEventsForQuestion: ERROR!! CurrentAnswers or LOAddressAnswers is zero %p %##s" + " (%s) CurrentAnswers %d, LOAddressAnswers %d", q, q->qname.c, DNSTypeName(q->qtype), + q->CurrentAnswers, q->LOAddressAnswers); + continue; + } + AnswerLocalQuestionWithLocalAuthRecord(m, rr, QC_rmv); // MUST NOT dereference q again + if (m->CurrentQuestion != q) { m->CurrentQuestion = mDNSNULL; return mDNSfalse; } + } + } + m->CurrentQuestion = mDNSNULL; + return mDNStrue; +} // Returns false if the question got deleted while delivering the RMV events -// The caller should handle the case +// The caller should handle the case mDNSlocal mDNSBool CacheRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q) - { - if (m->CurrentQuestion) - LogMsg("CacheRecordRmvEventsForQuestion: ERROR m->CurrentQuestion already set: %##s (%s)", - m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); - - // If it is a new question, we have not delivered any ADD events yet. So, don't deliver RMV events. - // If this question was answered using local auth records, then you can't deliver RMVs using cache - if (!IsQuestionNew(m, q) && !q->LOAddressAnswers) - { - m->CurrentQuestion = q; - CacheRecordRmvEventsForCurrentQuestion(m, q); - if (m->CurrentQuestion != q) { m->CurrentQuestion = mDNSNULL; return mDNSfalse; } - m->CurrentQuestion = mDNSNULL; - } - else { LogInfo("CacheRecordRmvEventsForQuestion: Question %p %##s (%s) is a new question", q, q->qname.c, DNSTypeName(q->qtype)); } - return mDNStrue; - } +{ + if (m->CurrentQuestion) + LogMsg("CacheRecordRmvEventsForQuestion: ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + + // If it is a new question, we have not delivered any ADD events yet. So, don't deliver RMV events. + // If this question was answered using local auth records, then you can't deliver RMVs using cache + if (!IsQuestionNew(m, q) && !q->LOAddressAnswers) + { + m->CurrentQuestion = q; + CacheRecordRmvEventsForCurrentQuestion(m, q); + if (m->CurrentQuestion != q) { m->CurrentQuestion = mDNSNULL; return mDNSfalse; } + m->CurrentQuestion = mDNSNULL; + } + else { LogInfo("CacheRecordRmvEventsForQuestion: Question %p %##s (%s) is a new question", q, q->qname.c, DNSTypeName(q->qtype)); } + return mDNStrue; +} // The caller should hold the lock mDNSexport void CheckSuppressUnusableQuestions(mDNS *const m) - { - DNSQuestion *q; - DNSQuestion *restart = mDNSNULL; - - // We look through all questions including new questions. During network change events, - // we potentially restart questions here in this function that ends up as new questions, - // which may be suppressed at this instance. Before it is handled we get another network - // event that changes the status e.g., address becomes available. If we did not process - // new questions, we would never change its SuppressQuery status. - // - // CurrentQuestion is used by RmvEventsForQuestion below. While delivering RMV events, the - // application callback can potentially stop the current question (detected by CurrentQuestion) or - // *any* other question which could be the next one that we may process here. RestartQuestion - // points to the "next" question which will be automatically advanced in mDNS_StopQuery_internal - // if the "next" question is stopped while the CurrentQuestion is stopped - if (m->RestartQuestion) - LogMsg("CheckSuppressUnusableQuestions: ERROR!! m->RestartQuestion already set: %##s (%s)", - m->RestartQuestion->qname.c, DNSTypeName(m->RestartQuestion->qtype)); - m->RestartQuestion = m->Questions; - while (m->RestartQuestion) - { - q = m->RestartQuestion; - m->RestartQuestion = q->next; - if (!mDNSOpaque16IsZero(q->TargetQID) && q->SuppressUnusable) - { - mDNSBool old = q->SuppressQuery; - q->SuppressQuery = ShouldSuppressQuery(m, &q->qname, q->qtype, q->InterfaceID); - if (q->SuppressQuery != old) - { - // NOTE: CacheRecordRmvEventsForQuestion will not generate RMV events for queries that have non-zero - // LOddressAnswers. Hence it is important that we call CacheRecordRmvEventsForQuestion before - // LocalRecordRmvEventsForQuestion (which decrements LOAddressAnswers) - - if (q->SuppressQuery) - { - // Previously it was not suppressed, Generate RMV events for the ADDs that we might have delivered before - // followed by a negative cache response. Temporarily turn off suppression so that - // AnswerCurrentQuestionWithResourceRecord can answer the question - q->SuppressQuery = mDNSfalse; - if (!CacheRecordRmvEventsForQuestion(m, q)) { LogInfo("CheckSuppressUnusableQuestions: Question deleted while delivering RMV events"); continue; } - q->SuppressQuery = mDNStrue; - } - - // SuppressUnusable does not affect questions that are answered from the local records (/etc/hosts) - // and SuppressQuery status does not mean anything for these questions. As we are going to stop the - // question below, we need to deliver the RMV events so that the ADDs that will be delivered during - // the restart will not be a duplicate ADD - if (!LocalRecordRmvEventsForQuestion(m, q)) { LogInfo("CheckSuppressUnusableQuestions: Question deleted while delivering RMV events"); continue; } - - // There are two cases here. - // - // 1. Previously it was suppressed and now it is not suppressed, restart the question so - // that it will start as a new question. Note that we can't just call ActivateUnicastQuery - // because when we get the response, if we had entries in the cache already, it will not answer - // this question if the cache entry did not change. Hence, we need to restart - // the query so that it can be answered from the cache. - // - // 2. Previously it was not suppressed and now it is suppressed. We need to restart the questions - // so that we redo the duplicate checks in mDNS_StartQuery_internal. A SuppressUnusable question - // is a duplicate of non-SuppressUnusable question if it is not suppressed (SuppressQuery is false). - // A SuppressUnusable question is not a duplicate of non-SuppressUnusable question if it is suppressed - // (SuppressQuery is true). The reason for this is that when a question is suppressed, we want an - // immediate response and not want to be blocked behind a question that is querying DNS servers. When - // the question is not suppressed, we don't want two active questions sending packets on the wire. - // This affects both efficiency and also the current design where there is only one active question - // pointed to from a cache entry. - // - // We restart queries in a two step process by first calling stop and build a temporary list which we - // will restart at the end. The main reason for the two step process is to handle duplicate questions. - // If there are duplicate questions, calling stop inherits the values from another question on the list (which - // will soon become the real question) including q->ThisQInterval which might be zero if it was - // suppressed before. At the end when we have restarted all questions, none of them is active as each - // inherits from one another and we need to reactivate one of the questions here which is a little hacky. - // - // It is much cleaner and less error prone to build a list of questions and restart at the end. - - LogInfo("CheckSuppressUnusableQuestions: Stop question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); - mDNS_StopQuery_internal(m, q); - q->next = restart; - restart = q; - } - } - } - while (restart) - { - q = restart; - restart = restart->next; - q->next = mDNSNULL; - LogInfo("CheckSuppressUnusableQuestions: Start question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); - mDNS_StartQuery_internal(m, q); - } - } +{ + DNSQuestion *q; + DNSQuestion *restart = mDNSNULL; + + // We look through all questions including new questions. During network change events, + // we potentially restart questions here in this function that ends up as new questions, + // which may be suppressed at this instance. Before it is handled we get another network + // event that changes the status e.g., address becomes available. If we did not process + // new questions, we would never change its SuppressQuery status. + // + // CurrentQuestion is used by RmvEventsForQuestion below. While delivering RMV events, the + // application callback can potentially stop the current question (detected by CurrentQuestion) or + // *any* other question which could be the next one that we may process here. RestartQuestion + // points to the "next" question which will be automatically advanced in mDNS_StopQuery_internal + // if the "next" question is stopped while the CurrentQuestion is stopped + if (m->RestartQuestion) + LogMsg("CheckSuppressUnusableQuestions: ERROR!! m->RestartQuestion already set: %##s (%s)", + m->RestartQuestion->qname.c, DNSTypeName(m->RestartQuestion->qtype)); + m->RestartQuestion = m->Questions; + while (m->RestartQuestion) + { + q = m->RestartQuestion; + m->RestartQuestion = q->next; + if (!mDNSOpaque16IsZero(q->TargetQID) && q->SuppressUnusable) + { + mDNSBool old = q->SuppressQuery; + q->SuppressQuery = ShouldSuppressQuery(m, &q->qname, q->qtype, q->InterfaceID); + if (q->SuppressQuery != old) + { + // NOTE: CacheRecordRmvEventsForQuestion will not generate RMV events for queries that have non-zero + // LOddressAnswers. Hence it is important that we call CacheRecordRmvEventsForQuestion before + // LocalRecordRmvEventsForQuestion (which decrements LOAddressAnswers) + + if (q->SuppressQuery) + { + // Previously it was not suppressed, Generate RMV events for the ADDs that we might have delivered before + // followed by a negative cache response. Temporarily turn off suppression so that + // AnswerCurrentQuestionWithResourceRecord can answer the question + q->SuppressQuery = mDNSfalse; + if (!CacheRecordRmvEventsForQuestion(m, q)) { LogInfo("CheckSuppressUnusableQuestions: Question deleted while delivering RMV events"); continue; } + q->SuppressQuery = mDNStrue; + } + + // SuppressUnusable does not affect questions that are answered from the local records (/etc/hosts) + // and SuppressQuery status does not mean anything for these questions. As we are going to stop the + // question below, we need to deliver the RMV events so that the ADDs that will be delivered during + // the restart will not be a duplicate ADD + if (!LocalRecordRmvEventsForQuestion(m, q)) { LogInfo("CheckSuppressUnusableQuestions: Question deleted while delivering RMV events"); continue; } + + // There are two cases here. + // + // 1. Previously it was suppressed and now it is not suppressed, restart the question so + // that it will start as a new question. Note that we can't just call ActivateUnicastQuery + // because when we get the response, if we had entries in the cache already, it will not answer + // this question if the cache entry did not change. Hence, we need to restart + // the query so that it can be answered from the cache. + // + // 2. Previously it was not suppressed and now it is suppressed. We need to restart the questions + // so that we redo the duplicate checks in mDNS_StartQuery_internal. A SuppressUnusable question + // is a duplicate of non-SuppressUnusable question if it is not suppressed (SuppressQuery is false). + // A SuppressUnusable question is not a duplicate of non-SuppressUnusable question if it is suppressed + // (SuppressQuery is true). The reason for this is that when a question is suppressed, we want an + // immediate response and not want to be blocked behind a question that is querying DNS servers. When + // the question is not suppressed, we don't want two active questions sending packets on the wire. + // This affects both efficiency and also the current design where there is only one active question + // pointed to from a cache entry. + // + // We restart queries in a two step process by first calling stop and build a temporary list which we + // will restart at the end. The main reason for the two step process is to handle duplicate questions. + // If there are duplicate questions, calling stop inherits the values from another question on the list (which + // will soon become the real question) including q->ThisQInterval which might be zero if it was + // suppressed before. At the end when we have restarted all questions, none of them is active as each + // inherits from one another and we need to reactivate one of the questions here which is a little hacky. + // + // It is much cleaner and less error prone to build a list of questions and restart at the end. + + LogInfo("CheckSuppressUnusableQuestions: Stop question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + mDNS_StopQuery_internal(m, q); + q->next = restart; + restart = q; + } + } + } + while (restart) + { + q = restart; + restart = restart->next; + q->next = mDNSNULL; + LogInfo("CheckSuppressUnusableQuestions: Start question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + mDNS_StartQuery_internal(m, q); + } +} mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question) - { - if (question->Target.type && !ValidQuestionTarget(question)) - { - LogMsg("mDNS_StartQuery_internal: Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery? for question %##s)", - question->Target.type, mDNSVal16(question->TargetPort), question->qname.c); - question->Target.type = mDNSAddrType_None; - } +{ + if (question->Target.type && !ValidQuestionTarget(question)) + { + LogMsg("mDNS_StartQuery_internal: Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery? for question %##s)", + question->Target.type, mDNSVal16(question->TargetPort), question->qname.c); + question->Target.type = mDNSAddrType_None; + } - if (!question->Target.type) question->TargetPort = zeroIPPort; // If no question->Target specified clear TargetPort + if (!question->Target.type) question->TargetPort = zeroIPPort; // If no question->Target specified clear TargetPort - question->TargetQID = + question->TargetQID = #ifndef UNICAST_DISABLED - (question->Target.type || Question_uDNS(question)) ? mDNS_NewMessageID(m) : + (question->Target.type || Question_uDNS(question)) ? mDNS_NewMessageID(m) : #endif // UNICAST_DISABLED - zeroID; - - debugf("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 - { - int i; - DNSQuestion **q; - - if (!ValidateDomainName(&question->qname)) - { - LogMsg("Attempt to start query with invalid qname %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); - return(mStatus_Invalid); - } - - // Note: It important that new questions are appended at the *end* of the list, not prepended at the start - q = &m->Questions; - if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) q = &m->LocalOnlyQuestions; - while (*q && *q != question) q=&(*q)->next; - - if (*q) - { - LogMsg("Error! Tried to add a question %##s (%s) %p that's already in the active list", - question->qname.c, DNSTypeName(question->qtype), question); - return(mStatus_AlreadyRegistered); - } - - *q = question; - - // If this question is referencing a specific interface, verify it exists - if (question->InterfaceID && question->InterfaceID != mDNSInterface_LocalOnly && question->InterfaceID != mDNSInterface_Unicast && question->InterfaceID != mDNSInterface_P2P) - { - NetworkInterfaceInfo *intf = FirstInterfaceForID(m, question->InterfaceID); - if (!intf) - LogMsg("Note: InterfaceID %p for question %##s (%s) not currently found in active interface list", - question->InterfaceID, question->qname.c, DNSTypeName(question->qtype)); - } - - // 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 (InitialQuestionInterval). - // If AnswerNewQuestion() finds that we have *no* relevant answers currently in our cache, then it will accelerate - // that to go out immediately. - 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->LastQTime = m->timenow; - question->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question - question->ExpectUnicastResp = 0; - question->LastAnswerPktNum = m->PktNum; - question->RecentAnswerPkts = 0; - question->CurrentAnswers = 0; - question->LargeAnswers = 0; - question->UniqueAnswers = 0; - question->LOAddressAnswers = 0; - question->FlappingInterface1 = mDNSNULL; - question->FlappingInterface2 = mDNSNULL; - // Must do AuthInfo and SuppressQuery before calling FindDuplicateQuestion() - question->AuthInfo = GetAuthInfoForQuestion(m, question); - if (question->SuppressUnusable) - question->SuppressQuery = ShouldSuppressQuery(m, &question->qname, question->qtype, question->InterfaceID); - else - question->SuppressQuery = 0; - question->DuplicateOf = FindDuplicateQuestion(m, question); - question->NextInDQList = mDNSNULL; - question->SendQNow = mDNSNULL; - question->SendOnAll = mDNSfalse; - question->RequestUnicast = 0; - question->LastQTxTime = m->timenow; - question->CNAMEReferrals = 0; - - // We'll create our question->LocalSocket on demand, if needed. - // We won't need one for duplicate questions, or from questions answered immediately out of the cache. - // We also don't need one for LLQs because (when we're using NAT) we want them all to share a single - // NAT mapping for receiving inbound add/remove events. - question->LocalSocket = mDNSNULL; - question->deliverAddEvents = mDNSfalse; - question->qDNSServer = mDNSNULL; - question->unansweredQueries = 0; - question->nta = mDNSNULL; - question->servAddr = zeroAddr; - question->servPort = zeroIPPort; - question->tcp = mDNSNULL; - question->NoAnswer = NoAnswer_Normal; - - question->state = LLQ_InitialRequest; - question->ReqLease = 0; - question->expire = 0; - question->ntries = 0; - question->id = zeroOpaque64; - question->validDNSServers = zeroOpaque64; - question->triedAllServersOnce = 0; - question->noServerResponse = 0; - question->StopTime = 0; - if (question->WakeOnResolve) - { - question->WakeOnResolveCount = InitialWakeOnResolveCount; - mDNS_PurgeBeforeResolve(m, question); - } - else - question->WakeOnResolveCount = 0; - - if (question->DuplicateOf) question->AuthInfo = question->DuplicateOf->AuthInfo; - - for (i=0; iDupSuppress[i].InterfaceID = mDNSNULL; - - debugf("mDNS_StartQuery: Question %##s (%s) Interface %p Now %d Send in %d Answer in %d (%p) %s (%p)", - question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, m->timenow, - NextQSendTime(question) - m->timenow, - question->DelayAnswering ? question->DelayAnswering - m->timenow : 0, - question, question->DuplicateOf ? "duplicate of" : "not duplicate", question->DuplicateOf); - - if (question->DelayAnswering) - LogInfo("mDNS_StartQuery_internal: Delaying answering for %d ticks while cache stabilizes for %##s (%s)", - question->DelayAnswering - m->timenow, question->qname.c, DNSTypeName(question->qtype)); - - if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) - { - if (!m->NewLocalOnlyQuestions) m->NewLocalOnlyQuestions = question; - } - else - { - if (!m->NewQuestions) m->NewQuestions = question; - - // If the question's id is non-zero, then it's Wide Area - // MUST NOT do this Wide Area setup until near the end of - // mDNS_StartQuery_internal -- this code may itself issue queries (e.g. SOA, - // NS, etc.) and if we haven't finished setting up our own question and setting - // m->NewQuestions if necessary then we could end up recursively re-entering - // this routine with the question list data structures in an inconsistent state. - if (!mDNSOpaque16IsZero(question->TargetQID)) - { - // Duplicate questions should have the same DNSServers so that when we find - // a matching resource record, all of them get the answers. Calling GetServerForQuestion - // for the duplicate question may get a different DNS server from the original question - mDNSu32 timeout = SetValidDNSServers(m, question); - // We set the timeout whenever mDNS_StartQuery_internal is called. This means if we have - // a networking change/search domain change that calls this function again we keep - // reinitializing the timeout value which means it may never timeout. If this becomes - // a common case in the future, we can easily fix this by adding extra state that - // indicates that we have already set the StopTime. - if (question->TimeoutQuestion) - question->StopTime = NonZeroTime(m->timenow + timeout * mDNSPlatformOneSecond); - if (question->DuplicateOf) - { - question->validDNSServers = question->DuplicateOf->validDNSServers; - question->qDNSServer = question->DuplicateOf->qDNSServer; - LogInfo("mDNS_StartQuery_internal: Duplicate question %p (%p) %##s (%s), Timeout %d, DNS Server %#a:%d", - question, question->DuplicateOf, question->qname.c, DNSTypeName(question->qtype), timeout, - question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL, - mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort)); - } - else - { - question->qDNSServer = GetServerForQuestion(m, question); - LogInfo("mDNS_StartQuery_internal: question %p %##s (%s) Timeout %d, DNS Server %#a:%d", - question, question->qname.c, DNSTypeName(question->qtype), timeout, - question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL, - mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort)); - } - ActivateUnicastQuery(m, question, mDNSfalse); - - // If long-lived query, and we don't have our NAT mapping active, start it now - if (question->LongLived && !m->LLQNAT.clientContext) - { - m->LLQNAT.Protocol = NATOp_MapUDP; - m->LLQNAT.IntPort = m->UnicastPort4; - m->LLQNAT.RequestedPort = m->UnicastPort4; - m->LLQNAT.clientCallback = LLQNATCallback; - m->LLQNAT.clientContext = (void*)1; // Means LLQ NAT Traversal is active - mDNS_StartNATOperation_internal(m, &m->LLQNAT); - } - + zeroID; + + debugf("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 + { + int i; + DNSQuestion **q; + + if (!ValidateDomainName(&question->qname)) + { + LogMsg("Attempt to start query with invalid qname %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + return(mStatus_Invalid); + } + + // Note: It important that new questions are appended at the *end* of the list, not prepended at the start + q = &m->Questions; + if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) q = &m->LocalOnlyQuestions; + while (*q && *q != question) q=&(*q)->next; + + if (*q) + { + LogMsg("Error! Tried to add a question %##s (%s) %p that's already in the active list", + question->qname.c, DNSTypeName(question->qtype), question); + return(mStatus_AlreadyRegistered); + } + + *q = question; + + // If this question is referencing a specific interface, verify it exists + if (question->InterfaceID && question->InterfaceID != mDNSInterface_LocalOnly && question->InterfaceID != mDNSInterface_Unicast && question->InterfaceID != mDNSInterface_P2P) + { + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, question->InterfaceID); + if (!intf) + LogMsg("Note: InterfaceID %p for question %##s (%s) not currently found in active interface list", + question->InterfaceID, question->qname.c, DNSTypeName(question->qtype)); + } + + // 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 (InitialQuestionInterval). + // If AnswerNewQuestion() finds that we have *no* relevant answers currently in our cache, then it will accelerate + // that to go out immediately. + 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->LastQTime = m->timenow; + question->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question + question->ExpectUnicastResp = 0; + question->LastAnswerPktNum = m->PktNum; + question->RecentAnswerPkts = 0; + question->CurrentAnswers = 0; + question->LargeAnswers = 0; + question->UniqueAnswers = 0; + question->LOAddressAnswers = 0; + question->FlappingInterface1 = mDNSNULL; + question->FlappingInterface2 = mDNSNULL; + // Must do AuthInfo and SuppressQuery before calling FindDuplicateQuestion() + question->AuthInfo = GetAuthInfoForQuestion(m, question); + if (question->SuppressUnusable) + question->SuppressQuery = ShouldSuppressQuery(m, &question->qname, question->qtype, question->InterfaceID); + else + question->SuppressQuery = 0; + question->DuplicateOf = FindDuplicateQuestion(m, question); + question->NextInDQList = mDNSNULL; + question->SendQNow = mDNSNULL; + question->SendOnAll = mDNSfalse; + question->RequestUnicast = 0; + question->LastQTxTime = m->timenow; + question->CNAMEReferrals = 0; + + // We'll create our question->LocalSocket on demand, if needed. + // We won't need one for duplicate questions, or from questions answered immediately out of the cache. + // We also don't need one for LLQs because (when we're using NAT) we want them all to share a single + // NAT mapping for receiving inbound add/remove events. + question->LocalSocket = mDNSNULL; + question->qDNSServer = mDNSNULL; + question->unansweredQueries = 0; + question->nta = mDNSNULL; + question->servAddr = zeroAddr; + question->servPort = zeroIPPort; + question->tcp = mDNSNULL; + question->NoAnswer = NoAnswer_Normal; + + question->state = LLQ_InitialRequest; + question->ReqLease = 0; + question->expire = 0; + question->ntries = 0; + question->id = zeroOpaque64; + question->validDNSServers = zeroOpaque64; + question->triedAllServersOnce = 0; + question->noServerResponse = 0; + question->StopTime = 0; + if (question->WakeOnResolve) + { + question->WakeOnResolveCount = InitialWakeOnResolveCount; + mDNS_PurgeBeforeResolve(m, question); + } + else + question->WakeOnResolveCount = 0; + + question->ValidationState = (question->ValidationRequired ? DNSSECValRequired : DNSSECValNotRequired); + question->ValidationStatus = 0; + + + if (question->DuplicateOf) question->AuthInfo = question->DuplicateOf->AuthInfo; + + for (i=0; iDupSuppress[i].InterfaceID = mDNSNULL; + + debugf("mDNS_StartQuery: Question %##s (%s) Interface %p Now %d Send in %d Answer in %d (%p) %s (%p)", + question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, m->timenow, + NextQSendTime(question) - m->timenow, + question->DelayAnswering ? question->DelayAnswering - m->timenow : 0, + question, question->DuplicateOf ? "duplicate of" : "not duplicate", question->DuplicateOf); + + if (question->DelayAnswering) + LogInfo("mDNS_StartQuery_internal: Delaying answering for %d ticks while cache stabilizes for %##s (%s)", + question->DelayAnswering - m->timenow, question->qname.c, DNSTypeName(question->qtype)); + + if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) + { + if (!m->NewLocalOnlyQuestions) m->NewLocalOnlyQuestions = question; + } + else + { + if (!m->NewQuestions) m->NewQuestions = question; + + // If the question's id is non-zero, then it's Wide Area + // MUST NOT do this Wide Area setup until near the end of + // mDNS_StartQuery_internal -- this code may itself issue queries (e.g. SOA, + // NS, etc.) and if we haven't finished setting up our own question and setting + // m->NewQuestions if necessary then we could end up recursively re-entering + // this routine with the question list data structures in an inconsistent state. + if (!mDNSOpaque16IsZero(question->TargetQID)) + { + // Duplicate questions should have the same DNSServers so that when we find + // a matching resource record, all of them get the answers. Calling GetServerForQuestion + // for the duplicate question may get a different DNS server from the original question + mDNSu32 timeout = SetValidDNSServers(m, question); + // We set the timeout whenever mDNS_StartQuery_internal is called. This means if we have + // a networking change/search domain change that calls this function again we keep + // reinitializing the timeout value which means it may never timeout. If this becomes + // a common case in the future, we can easily fix this by adding extra state that + // indicates that we have already set the StopTime. + if (question->TimeoutQuestion) + question->StopTime = NonZeroTime(m->timenow + timeout * mDNSPlatformOneSecond); + if (question->DuplicateOf) + { + question->validDNSServers = question->DuplicateOf->validDNSServers; + question->qDNSServer = question->DuplicateOf->qDNSServer; + LogInfo("mDNS_StartQuery_internal: Duplicate question %p (%p) %##s (%s), Timeout %d, DNS Server %#a:%d", + question, question->DuplicateOf, question->qname.c, DNSTypeName(question->qtype), timeout, + question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL, + mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort)); + } + else + { + question->qDNSServer = GetServerForQuestion(m, question); + LogInfo("mDNS_StartQuery_internal: question %p %##s (%s) Timeout %d, DNS Server %#a:%d", + question, question->qname.c, DNSTypeName(question->qtype), timeout, + question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL, + mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort)); + } + // If we are talking to a server on the local host, unsupress the query. This happens if we have + // a DNS server running locally while we don't have any interfaces UP. + // + // TBD: Re-organise the code so that we can move this logic to ShouldSuppressQuery + if (question->SuppressQuery && question->qDNSServer && mDNSAddressIsLoopback(&question->qDNSServer->addr)) + { + LogInfo("mDNS_StartQuery_internal: question %p %##s (%s) unsuppressed due to local DNS Server %#a:%d", + question, question->qname.c, DNSTypeName(question->qtype), &question->qDNSServer->addr, + mDNSVal16(question->qDNSServer->port)); + question->SuppressQuery = 0; + } + ActivateUnicastQuery(m, question, mDNSfalse); + + // If there is a negative cache entry for this question and if it does + // not have cached nsecs, then we can't validate possibly. Hence, flush + // them so that we can reissue the question again with EDNS0/DO bit set. + if (!question->DuplicateOf && DNSSECQuestion(question)) + mDNS_CheckForCachedNSECS(m, question); + + // If long-lived query, and we don't have our NAT mapping active, start it now + if (question->LongLived && !m->LLQNAT.clientContext) + { + m->LLQNAT.Protocol = NATOp_MapUDP; + m->LLQNAT.IntPort = m->UnicastPort4; + m->LLQNAT.RequestedPort = m->UnicastPort4; + m->LLQNAT.clientCallback = LLQNATCallback; + m->LLQNAT.clientContext = (void*)1; // Means LLQ NAT Traversal is active + mDNS_StartNATOperation_internal(m, &m->LLQNAT); + } + #if APPLE_OSX_mDNSResponder - if (question->LongLived) - UpdateAutoTunnelDomainStatuses(m); + if (question->LongLived) + UpdateAutoTunnelDomainStatuses(m); #endif - - } - else - { - if (question->TimeoutQuestion) - question->StopTime = NonZeroTime(m->timenow + GetTimeoutForMcastQuestion(m, question) * mDNSPlatformOneSecond); - } - if (question->StopTime) SetNextQueryStopTime(m, question); - SetNextQueryTime(m,question); - } - - return(mStatus_NoError); - } - } + + } + else + { + if (question->TimeoutQuestion) + question->StopTime = NonZeroTime(m->timenow + GetTimeoutForMcastQuestion(m, question) * mDNSPlatformOneSecond); + } + if (question->StopTime) SetNextQueryStopTime(m, question); + SetNextQueryTime(m,question); + } + + return(mStatus_NoError); + } +} // CancelGetZoneData is an internal routine (i.e. must be called with the lock already held) mDNSexport void CancelGetZoneData(mDNS *const m, ZoneData *nta) - { - debugf("CancelGetZoneData %##s (%s)", nta->question.qname.c, DNSTypeName(nta->question.qtype)); - // This function may be called anytime to free the zone information.The question may or may not have stopped. - // If it was already stopped, mDNS_StopQuery_internal would have set q->ThisQInterval to -1 and should not - // call it again - if (nta->question.ThisQInterval != -1) - { - mDNS_StopQuery_internal(m, &nta->question); - if (nta->question.ThisQInterval != -1) - LogMsg("CancelGetZoneData: Question %##s (%s) ThisQInterval %d not -1", nta->question.qname.c, DNSTypeName(nta->question.qtype), nta->question.ThisQInterval); - } - mDNSPlatformMemFree(nta); - } +{ + debugf("CancelGetZoneData %##s (%s)", nta->question.qname.c, DNSTypeName(nta->question.qtype)); + // This function may be called anytime to free the zone information.The question may or may not have stopped. + // If it was already stopped, mDNS_StopQuery_internal would have set q->ThisQInterval to -1 and should not + // call it again + if (nta->question.ThisQInterval != -1) + { + mDNS_StopQuery_internal(m, &nta->question); + if (nta->question.ThisQInterval != -1) + LogMsg("CancelGetZoneData: Question %##s (%s) ThisQInterval %d not -1", nta->question.qname.c, DNSTypeName(nta->question.qtype), nta->question.ThisQInterval); + } + mDNSPlatformMemFree(nta); +} mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const question) - { - const mDNSu32 slot = HashSlot(&question->qname); - CacheGroup *cg = CacheGroupForName(m, slot, question->qnamehash, &question->qname); - CacheRecord *rr; - DNSQuestion **qp = &m->Questions; - - //LogInfo("mDNS_StopQuery_internal %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); - - if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) qp = &m->LocalOnlyQuestions; - while (*qp && *qp != question) qp=&(*qp)->next; - if (*qp) *qp = (*qp)->next; - else - { +{ + const mDNSu32 slot = HashSlot(&question->qname); + CacheGroup *cg = CacheGroupForName(m, slot, question->qnamehash, &question->qname); + CacheRecord *rr; + DNSQuestion **qp = &m->Questions; + + //LogInfo("mDNS_StopQuery_internal %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + + if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) qp = &m->LocalOnlyQuestions; + while (*qp && *qp != question) qp=&(*qp)->next; + if (*qp) *qp = (*qp)->next; + else + { #if !ForceAlerts - if (question->ThisQInterval >= 0) // Only log error message if the query was supposed to be active + if (question->ThisQInterval >= 0) // Only log error message if the query was supposed to be active #endif - LogMsg("mDNS_StopQuery_internal: Question %##s (%s) not found in active list", - question->qname.c, DNSTypeName(question->qtype)); + LogMsg("mDNS_StopQuery_internal: Question %##s (%s) not found in active list", + question->qname.c, DNSTypeName(question->qtype)); #if ForceAlerts - *(long*)0 = 0; + *(long*)0 = 0; #endif - return(mStatus_BadReferenceErr); - } - - // Take care to cut question from list *before* calling UpdateQuestionDuplicates - UpdateQuestionDuplicates(m, question); - // But don't trash ThisQInterval until afterwards. - question->ThisQInterval = -1; - - // If there are any cache records referencing this as their active question, then see if there is any - // other question that is also referencing them, else their CRActiveQuestion needs to get set to NULL. - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) - { - if (rr->CRActiveQuestion == question) - { - DNSQuestion *q; - // Checking for ActiveQuestion filters questions that are suppressed also - // as suppressed questions are not active - for (q = m->Questions; q; q=q->next) // Scan our list of questions - if (ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) - break; - if (q) - debugf("mDNS_StopQuery_internal: Updating CRActiveQuestion to %p for cache record %s, Original question CurrentAnswers %d, new question " - "CurrentAnswers %d, SuppressQuery %d", q, CRDisplayString(m,rr), question->CurrentAnswers, q->CurrentAnswers, q->SuppressQuery); - rr->CRActiveQuestion = q; // Question used to be active; new value may or may not be null - if (!q) m->rrcache_active--; // If no longer active, decrement rrcache_active count - } - } - - // If we just deleted the question that CacheRecordAdd() or CacheRecordRmv() is about to look at, - // bump its pointer forward one question. - if (m->CurrentQuestion == question) - { - debugf("mDNS_StopQuery_internal: Just deleted the currently active question: %##s (%s)", - question->qname.c, DNSTypeName(question->qtype)); - m->CurrentQuestion = question->next; - } - - if (m->NewQuestions == question) - { - debugf("mDNS_StopQuery_internal: Just deleted a new question that wasn't even answered yet: %##s (%s)", - question->qname.c, DNSTypeName(question->qtype)); - m->NewQuestions = question->next; - } - - if (m->NewLocalOnlyQuestions == question) m->NewLocalOnlyQuestions = question->next; - - if (m->RestartQuestion == question) - { - LogMsg("mDNS_StopQuery_internal: Just deleted the current restart question: %##s (%s)", - question->qname.c, DNSTypeName(question->qtype)); - m->RestartQuestion = question->next; - } - - // Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions - question->next = mDNSNULL; - - // LogMsg("mDNS_StopQuery_internal: Question %##s (%s) removed", question->qname.c, DNSTypeName(question->qtype)); - - // And finally, cancel any associated GetZoneData operation that's still running. - // Must not do this until last, because there's a good chance the GetZoneData question is the next in the list, - // so if we delete it earlier in this routine, we could find that our "question->next" pointer above is already - // invalid before we even use it. By making sure that we update m->CurrentQuestion and m->NewQuestions if necessary - // *first*, then they're all ready to be updated a second time if necessary when we cancel our GetZoneData query. - if (question->tcp) { DisposeTCPConn(question->tcp); question->tcp = mDNSNULL; } - if (question->LocalSocket) { mDNSPlatformUDPClose(question->LocalSocket); question->LocalSocket = mDNSNULL; } - if (!mDNSOpaque16IsZero(question->TargetQID) && question->LongLived) - { - // Scan our list to see if any more wide-area LLQs remain. If not, stop our NAT Traversal. - DNSQuestion *q; - for (q = m->Questions; q; q=q->next) - if (!mDNSOpaque16IsZero(q->TargetQID) && q->LongLived) break; - if (!q) - { - if (!m->LLQNAT.clientContext) // Should never happen, but just in case... - LogMsg("mDNS_StopQuery ERROR LLQNAT.clientContext NULL"); - else - { - LogInfo("Stopping LLQNAT"); - mDNS_StopNATOperation_internal(m, &m->LLQNAT); - m->LLQNAT.clientContext = mDNSNULL; // Means LLQ NAT Traversal not running - } - } - - // If necessary, tell server it can delete this LLQ state - if (question->state == LLQ_Established) - { - question->ReqLease = 0; - sendLLQRefresh(m, question); - // If we need need to make a TCP connection to cancel the LLQ, that's going to take a little while. - // We clear the tcp->question backpointer so that when the TCP connection completes, it doesn't - // crash trying to access our cancelled question, but we don't cancel the TCP operation itself -- - // we let that run out its natural course and complete asynchronously. - if (question->tcp) - { - question->tcp->question = mDNSNULL; - question->tcp = mDNSNULL; - } - } + return(mStatus_BadReferenceErr); + } + + // Take care to cut question from list *before* calling UpdateQuestionDuplicates + UpdateQuestionDuplicates(m, question); + // But don't trash ThisQInterval until afterwards. + question->ThisQInterval = -1; + + // If there are any cache records referencing this as their active question, then see if there is any + // other question that is also referencing them, else their CRActiveQuestion needs to get set to NULL. + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + { + if (rr->CRActiveQuestion == question) + { + DNSQuestion *q; + // Checking for ActiveQuestion filters questions that are suppressed also + // as suppressed questions are not active + for (q = m->Questions; q; q=q->next) // Scan our list of questions + if (ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) + break; + if (q) + debugf("mDNS_StopQuery_internal: Updating CRActiveQuestion to %p for cache record %s, Original question CurrentAnswers %d, new question " + "CurrentAnswers %d, SuppressQuery %d", q, CRDisplayString(m,rr), question->CurrentAnswers, q->CurrentAnswers, q->SuppressQuery); + rr->CRActiveQuestion = q; // Question used to be active; new value may or may not be null + if (!q) m->rrcache_active--; // If no longer active, decrement rrcache_active count + } + } + + // If we just deleted the question that CacheRecordAdd() or CacheRecordRmv() is about to look at, + // bump its pointer forward one question. + if (m->CurrentQuestion == question) + { + debugf("mDNS_StopQuery_internal: Just deleted the currently active question: %##s (%s)", + question->qname.c, DNSTypeName(question->qtype)); + m->CurrentQuestion = question->next; + } + + if (m->NewQuestions == question) + { + debugf("mDNS_StopQuery_internal: Just deleted a new question that wasn't even answered yet: %##s (%s)", + question->qname.c, DNSTypeName(question->qtype)); + m->NewQuestions = question->next; + } + + if (m->NewLocalOnlyQuestions == question) m->NewLocalOnlyQuestions = question->next; + + if (m->RestartQuestion == question) + { + LogMsg("mDNS_StopQuery_internal: Just deleted the current restart question: %##s (%s)", + question->qname.c, DNSTypeName(question->qtype)); + m->RestartQuestion = question->next; + } + + if (m->ValidationQuestion == question) + { + LogInfo("mDNS_StopQuery_internal: Just deleted the current Validation question: %##s (%s)", + question->qname.c, DNSTypeName(question->qtype)); + m->ValidationQuestion = question->next; + } + + // Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions + question->next = mDNSNULL; + + // LogMsg("mDNS_StopQuery_internal: Question %##s (%s) removed", question->qname.c, DNSTypeName(question->qtype)); + + // And finally, cancel any associated GetZoneData operation that's still running. + // Must not do this until last, because there's a good chance the GetZoneData question is the next in the list, + // so if we delete it earlier in this routine, we could find that our "question->next" pointer above is already + // invalid before we even use it. By making sure that we update m->CurrentQuestion and m->NewQuestions if necessary + // *first*, then they're all ready to be updated a second time if necessary when we cancel our GetZoneData query. + if (question->tcp) { DisposeTCPConn(question->tcp); question->tcp = mDNSNULL; } + if (question->LocalSocket) { mDNSPlatformUDPClose(question->LocalSocket); question->LocalSocket = mDNSNULL; } + if (!mDNSOpaque16IsZero(question->TargetQID) && question->LongLived) + { + // Scan our list to see if any more wide-area LLQs remain. If not, stop our NAT Traversal. + DNSQuestion *q; + for (q = m->Questions; q; q=q->next) + if (!mDNSOpaque16IsZero(q->TargetQID) && q->LongLived) break; + if (!q) + { + if (!m->LLQNAT.clientContext) // Should never happen, but just in case... + LogMsg("mDNS_StopQuery ERROR LLQNAT.clientContext NULL"); + else + { + LogInfo("Stopping LLQNAT"); + mDNS_StopNATOperation_internal(m, &m->LLQNAT); + m->LLQNAT.clientContext = mDNSNULL; // Means LLQ NAT Traversal not running + } + } + + // If necessary, tell server it can delete this LLQ state + if (question->state == LLQ_Established) + { + question->ReqLease = 0; + sendLLQRefresh(m, question); + // If we need need to make a TCP connection to cancel the LLQ, that's going to take a little while. + // We clear the tcp->question backpointer so that when the TCP connection completes, it doesn't + // crash trying to access our cancelled question, but we don't cancel the TCP operation itself -- + // we let that run out its natural course and complete asynchronously. + if (question->tcp) + { + question->tcp->question = mDNSNULL; + question->tcp = mDNSNULL; + } + } #if APPLE_OSX_mDNSResponder - UpdateAutoTunnelDomainStatuses(m); + UpdateAutoTunnelDomainStatuses(m); #endif - } - // wait until we send the refresh above which needs the nta - if (question->nta) { CancelGetZoneData(m, question->nta); question->nta = mDNSNULL; } + } + // wait until we send the refresh above which needs the nta + if (question->nta) { CancelGetZoneData(m, question->nta); question->nta = mDNSNULL; } - return(mStatus_NoError); - } + return(mStatus_NoError); +} mDNSexport mStatus mDNS_StartQuery(mDNS *const m, DNSQuestion *const question) - { - mStatus status; - mDNS_Lock(m); - status = mDNS_StartQuery_internal(m, question); - mDNS_Unlock(m); - return(status); - } +{ + mStatus status; + mDNS_Lock(m); + status = mDNS_StartQuery_internal(m, question); + mDNS_Unlock(m); + return(status); +} mDNSexport mStatus mDNS_StopQuery(mDNS *const m, DNSQuestion *const question) - { - mStatus status; - mDNS_Lock(m); - status = mDNS_StopQuery_internal(m, question); - mDNS_Unlock(m); - return(status); - } +{ + mStatus status; + mDNS_Lock(m); + status = mDNS_StopQuery_internal(m, question); + mDNS_Unlock(m); + return(status); +} // Note that mDNS_StopQueryWithRemoves() does not currently implement the full generality of the other APIs // Specifically, question callbacks invoked as a result of this call cannot themselves make API calls. // We invoke the callback without using mDNS_DropLockBeforeCallback/mDNS_ReclaimLockAfterCallback // specifically to catch and report if the client callback does try to make API calls mDNSexport mStatus mDNS_StopQueryWithRemoves(mDNS *const m, DNSQuestion *const question) - { - mStatus status; - DNSQuestion *qq; - mDNS_Lock(m); - - // Check if question is new -- don't want to give remove events for a question we haven't even answered yet - for (qq = m->NewQuestions; qq; qq=qq->next) if (qq == question) break; - - status = mDNS_StopQuery_internal(m, question); - if (status == mStatus_NoError && !qq) - { - const CacheRecord *rr; - const mDNSu32 slot = HashSlot(&question->qname); - CacheGroup *const cg = CacheGroupForName(m, slot, question->qnamehash, &question->qname); - LogInfo("Generating terminal removes for %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) - if (rr->resrec.RecordType != kDNSRecordTypePacketNegative && SameNameRecordAnswersQuestion(&rr->resrec, question)) - { - // Don't use mDNS_DropLockBeforeCallback() here, since we don't allow API calls - if (question->QuestionCallback) - question->QuestionCallback(m, question, &rr->resrec, mDNSfalse); - } - } - mDNS_Unlock(m); - return(status); - } +{ + mStatus status; + DNSQuestion *qq; + mDNS_Lock(m); + + // Check if question is new -- don't want to give remove events for a question we haven't even answered yet + for (qq = m->NewQuestions; qq; qq=qq->next) if (qq == question) break; + + status = mDNS_StopQuery_internal(m, question); + if (status == mStatus_NoError && !qq) + { + const CacheRecord *rr; + const mDNSu32 slot = HashSlot(&question->qname); + CacheGroup *const cg = CacheGroupForName(m, slot, question->qnamehash, &question->qname); + LogInfo("Generating terminal removes for %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + if (rr->resrec.RecordType != kDNSRecordTypePacketNegative && SameNameRecordAnswersQuestion(&rr->resrec, question)) + { + // Don't use mDNS_DropLockBeforeCallback() here, since we don't allow API calls + if (question->QuestionCallback) + question->QuestionCallback(m, question, &rr->resrec, mDNSfalse); + } + } + mDNS_Unlock(m); + return(status); +} mDNSexport mStatus mDNS_Reconfirm(mDNS *const m, CacheRecord *const cr) - { - mStatus status; - mDNS_Lock(m); - status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); - if (status == mStatus_NoError) ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, 0); - mDNS_Unlock(m); - return(status); - } +{ + mStatus status; + mDNS_Lock(m); + status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); + if (status == mStatus_NoError) ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, 0); + mDNS_Unlock(m); + return(status); +} mDNSexport mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr) - { - mStatus status = mStatus_BadReferenceErr; - CacheRecord *cr; - mDNS_Lock(m); - cr = FindIdenticalRecordInCache(m, rr); - debugf("mDNS_ReconfirmByValue: %p %s", cr, RRDisplayString(m, rr)); - if (cr) status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); - if (status == mStatus_NoError) ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, 0); - mDNS_Unlock(m); - return(status); - } +{ + mStatus status = mStatus_BadReferenceErr; + CacheRecord *cr; + mDNS_Lock(m); + cr = FindIdenticalRecordInCache(m, rr); + debugf("mDNS_ReconfirmByValue: %p %s", cr, RRDisplayString(m, rr)); + if (cr) status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); + if (status == mStatus_NoError) ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, 0); + mDNS_Unlock(m); + return(status); +} mDNSlocal mStatus mDNS_StartBrowse_internal(mDNS *const m, DNSQuestion *const question, - const domainname *const srv, const domainname *const domain, - 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 = mDNStrue; - question->ExpectUnique = mDNSfalse; - question->ForceMCast = ForceMCast; - question->ReturnIntermed = mDNSfalse; - question->SuppressUnusable = mDNSfalse; - question->SearchListIndex = 0; - question->AppendSearchDomains = 0; - question->RetryWithSearchDomains = mDNSfalse; - question->TimeoutQuestion = 0; - question->WakeOnResolve = 0; - question->qnameOrig = mDNSNULL; - question->QuestionCallback = Callback; - question->QuestionContext = Context; - if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr); - - return(mDNS_StartQuery_internal(m, question)); - } + const domainname *const srv, const domainname *const domain, + const mDNSInterfaceID InterfaceID, mDNSu32 flags, + mDNSBool ForceMCast, mDNSBool useBackgroundTrafficClass, + mDNSQuestionCallback *Callback, void *Context) +{ + question->InterfaceID = InterfaceID; + question->flags = flags; + question->Target = zeroAddr; + question->qtype = kDNSType_PTR; + question->qclass = kDNSClass_IN; + question->LongLived = mDNStrue; + question->ExpectUnique = mDNSfalse; + question->ForceMCast = ForceMCast; + question->ReturnIntermed = mDNSfalse; + question->SuppressUnusable = mDNSfalse; + question->SearchListIndex = 0; + question->AppendSearchDomains = 0; + question->RetryWithSearchDomains = mDNSfalse; + question->TimeoutQuestion = 0; + question->WakeOnResolve = 0; + question->UseBrackgroundTrafficClass = useBackgroundTrafficClass; + question->ValidationRequired = 0; + question->ValidatingResponse = 0; + question->qnameOrig = mDNSNULL; + question->QuestionCallback = Callback; + question->QuestionContext = Context; + if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr); + + return(mDNS_StartQuery_internal(m, question)); +} mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, - const domainname *const srv, const domainname *const domain, - const mDNSInterfaceID InterfaceID, mDNSBool ForceMCast, mDNSQuestionCallback *Callback, void *Context) - { - mStatus status; - mDNS_Lock(m); - status = mDNS_StartBrowse_internal(m, question, srv, domain, InterfaceID, ForceMCast, Callback, Context); - mDNS_Unlock(m); - return(status); - } + const domainname *const srv, const domainname *const domain, + const mDNSInterfaceID InterfaceID, mDNSu32 flags, + mDNSBool ForceMCast, mDNSBool useBackgroundTrafficClass, + mDNSQuestionCallback *Callback, void *Context) +{ + mStatus status; + mDNS_Lock(m); + status = mDNS_StartBrowse_internal(m, question, srv, domain, InterfaceID, flags, ForceMCast, useBackgroundTrafficClass, Callback, Context); + mDNS_Unlock(m); + return(status); +} 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); - } +{ + 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, QC_result AddRecord) - { - ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; - mDNSBool PortChanged = !mDNSSameIPPort(query->info->port, answer->rdata->u.srv.port); - if (!AddRecord) return; - if (answer->rrtype != kDNSType_SRV) return; - - query->info->port = answer->rdata->u.srv.port; - - // If this is our first answer, then set the GotSRV flag and start the address query - if (!query->GotSRV) - { - query->GotSRV = mDNStrue; - query->qAv4.InterfaceID = answer->InterfaceID; - AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target); - query->qAv6.InterfaceID = answer->InterfaceID; - AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target); - mDNS_StartQuery(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(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: - // 1. This is not our first SRV answer - // 2. The interface ID is different, but the target host and port are the same - // This implies that we're seeing the exact same SRV record on more than one interface, so we should - // make our address queries at least as broad as the original SRV query so that we catch all the answers. - query->qAv4.InterfaceID = query->qSRV.InterfaceID; // Will be mDNSInterface_Any, or a specific interface - query->qAv6.InterfaceID = query->qSRV.InterfaceID; - } - else - { - query->qAv4.InterfaceID = answer->InterfaceID; - AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target); - query->qAv6.InterfaceID = answer->InterfaceID; - AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target); - } - debugf("FoundServiceInfoSRV: Restarting address queries for %##s (%s)", query->qAv4.qname.c, DNSTypeName(query->qAv4.qtype)); - 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) - { - if (++query->Answers >= 100) - debugf("**** WARNING **** Have given %lu answers for %##s (SRV) %##s %u", - query->Answers, query->qSRV.qname.c, answer->rdata->u.srv.target.c, - mDNSVal16(answer->rdata->u.srv.port)); - query->ServiceInfoQueryCallback(m, query); - } - // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's - // callback function is allowed to do anything, including deleting this query and freeing its memory. - } +{ + ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; + mDNSBool PortChanged = !mDNSSameIPPort(query->info->port, answer->rdata->u.srv.port); + if (!AddRecord) return; + if (answer->rrtype != kDNSType_SRV) return; + + query->info->port = answer->rdata->u.srv.port; + + // If this is our first answer, then set the GotSRV flag and start the address query + if (!query->GotSRV) + { + query->GotSRV = mDNStrue; + query->qAv4.InterfaceID = answer->InterfaceID; + AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target); + query->qAv6.InterfaceID = answer->InterfaceID; + AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target); + mDNS_StartQuery(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(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: + // 1. This is not our first SRV answer + // 2. The interface ID is different, but the target host and port are the same + // This implies that we're seeing the exact same SRV record on more than one interface, so we should + // make our address queries at least as broad as the original SRV query so that we catch all the answers. + query->qAv4.InterfaceID = query->qSRV.InterfaceID; // Will be mDNSInterface_Any, or a specific interface + query->qAv6.InterfaceID = query->qSRV.InterfaceID; + } + else + { + query->qAv4.InterfaceID = answer->InterfaceID; + AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target); + query->qAv6.InterfaceID = answer->InterfaceID; + AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target); + } + debugf("FoundServiceInfoSRV: Restarting address queries for %##s (%s)", query->qAv4.qname.c, DNSTypeName(query->qAv4.qtype)); + 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) + { + if (++query->Answers >= 100) + debugf("**** WARNING **** Have given %lu answers for %##s (SRV) %##s %u", + query->Answers, query->qSRV.qname.c, answer->rdata->u.srv.target.c, + mDNSVal16(answer->rdata->u.srv.port)); + query->ServiceInfoQueryCallback(m, query); + } + // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's + // callback function is allowed to do anything, including deleting this query and freeing its memory. +} mDNSlocal void FoundServiceInfoTXT(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) - { - ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; - if (!AddRecord) return; - if (answer->rrtype != kDNSType_TXT) return; - if (answer->rdlength > sizeof(query->info->TXTinfo)) return; - - query->GotTXT = mDNStrue; - query->info->TXTlen = answer->rdlength; - query->info->TXTinfo[0] = 0; // In case answer->rdlength is zero - mDNSPlatformMemCopy(query->info->TXTinfo, answer->rdata->u.txt.c, answer->rdlength); - - verbosedebugf("FoundServiceInfoTXT: %##s GotADD=%d", query->info->name.c, query->GotADD); - - // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's - // callback function is allowed to do anything, including deleting this query and freeing its memory. - if (query->ServiceInfoQueryCallback && query->GotADD) - { - if (++query->Answers >= 100) - debugf("**** WARNING **** have given %lu answers for %##s (TXT) %#s...", - query->Answers, query->qSRV.qname.c, answer->rdata->u.txt.c); - query->ServiceInfoQueryCallback(m, query); - } - } +{ + ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; + if (!AddRecord) return; + if (answer->rrtype != kDNSType_TXT) return; + if (answer->rdlength > sizeof(query->info->TXTinfo)) return; + + query->GotTXT = mDNStrue; + query->info->TXTlen = answer->rdlength; + query->info->TXTinfo[0] = 0; // In case answer->rdlength is zero + mDNSPlatformMemCopy(query->info->TXTinfo, answer->rdata->u.txt.c, answer->rdlength); + + verbosedebugf("FoundServiceInfoTXT: %##s GotADD=%d", query->info->name.c, query->GotADD); + + // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's + // callback function is allowed to do anything, including deleting this query and freeing its memory. + if (query->ServiceInfoQueryCallback && query->GotADD) + { + if (++query->Answers >= 100) + debugf("**** WARNING **** have given %lu answers for %##s (TXT) %#s...", + query->Answers, query->qSRV.qname.c, answer->rdata->u.txt.c); + query->ServiceInfoQueryCallback(m, query); + } +} mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) - { - ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; - //LogInfo("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.ipv4; - } - else if (answer->rrtype == kDNSType_AAAA) - { - query->info->ip.type = mDNSAddrType_IPv6; - query->info->ip.ip.v6 = answer->rdata->u.ipv6; - } - else - { - debugf("FoundServiceInfo: answer %##s type %d (%s) unexpected", answer->name->c, answer->rrtype, DNSTypeName(answer->rrtype)); - return; - } - - query->GotADD = mDNStrue; - query->info->InterfaceID = answer->InterfaceID; - - 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. - if (query->ServiceInfoQueryCallback && query->GotTXT) - { - if (++query->Answers >= 100) - debugf(answer->rrtype == kDNSType_A ? - "**** WARNING **** have given %lu answers for %##s (A) %.4a" : - "**** WARNING **** have given %lu answers for %##s (AAAA) %.16a", - query->Answers, query->qSRV.qname.c, &answer->rdata->u.data); - query->ServiceInfoQueryCallback(m, query); - } - } +{ + ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; + //LogInfo("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.ipv4; + } + else if (answer->rrtype == kDNSType_AAAA) + { + query->info->ip.type = mDNSAddrType_IPv6; + query->info->ip.ip.v6 = answer->rdata->u.ipv6; + } + else + { + debugf("FoundServiceInfo: answer %##s type %d (%s) unexpected", answer->name->c, answer->rrtype, DNSTypeName(answer->rrtype)); + return; + } + + query->GotADD = mDNStrue; + query->info->InterfaceID = answer->InterfaceID; + + 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. + if (query->ServiceInfoQueryCallback && query->GotTXT) + { + if (++query->Answers >= 100) + debugf(answer->rrtype == kDNSType_A ? + "**** WARNING **** have given %lu answers for %##s (A) %.4a" : + "**** WARNING **** have given %lu answers for %##s (AAAA) %.16a", + query->Answers, query->qSRV.qname.c, &answer->rdata->u.data); + query->ServiceInfoQueryCallback(m, query); + } +} // On entry, the client must have set the name and InterfaceID fields of the ServiceInfo structure // If the query is not interface-specific, then InterfaceID may be zero // Each time the Callback is invoked, the remainder of the fields will have been filled in // In addition, InterfaceID will be updated to give the interface identifier corresponding to that response mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, - ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context) - { - mStatus status; - mDNS_Lock(m); - - query->qSRV.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question - query->qSRV.InterfaceID = info->InterfaceID; - query->qSRV.Target = zeroAddr; - AssignDomainName(&query->qSRV.qname, &info->name); - query->qSRV.qtype = kDNSType_SRV; - query->qSRV.qclass = kDNSClass_IN; - query->qSRV.LongLived = mDNSfalse; - query->qSRV.ExpectUnique = mDNStrue; - query->qSRV.ForceMCast = mDNSfalse; - query->qSRV.ReturnIntermed = mDNSfalse; - query->qSRV.SuppressUnusable = mDNSfalse; - query->qSRV.SearchListIndex = 0; - query->qSRV.AppendSearchDomains = 0; - query->qSRV.RetryWithSearchDomains = mDNSfalse; - query->qSRV.TimeoutQuestion = 0; - query->qSRV.WakeOnResolve = 0; - query->qSRV.qnameOrig = mDNSNULL; - query->qSRV.QuestionCallback = FoundServiceInfoSRV; - query->qSRV.QuestionContext = query; - - query->qTXT.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question - query->qTXT.InterfaceID = info->InterfaceID; - query->qTXT.Target = zeroAddr; - AssignDomainName(&query->qTXT.qname, &info->name); - query->qTXT.qtype = kDNSType_TXT; - query->qTXT.qclass = kDNSClass_IN; - query->qTXT.LongLived = mDNSfalse; - query->qTXT.ExpectUnique = mDNStrue; - query->qTXT.ForceMCast = mDNSfalse; - query->qTXT.ReturnIntermed = mDNSfalse; - query->qTXT.SuppressUnusable = mDNSfalse; - query->qTXT.SearchListIndex = 0; - query->qTXT.AppendSearchDomains = 0; - query->qTXT.RetryWithSearchDomains = mDNSfalse; - query->qTXT.TimeoutQuestion = 0; - query->qTXT.WakeOnResolve = 0; - query->qTXT.qnameOrig = mDNSNULL; - query->qTXT.QuestionCallback = FoundServiceInfoTXT; - query->qTXT.QuestionContext = query; - - query->qAv4.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question - query->qAv4.InterfaceID = info->InterfaceID; - query->qAv4.Target = zeroAddr; - query->qAv4.qname.c[0] = 0; - query->qAv4.qtype = kDNSType_A; - query->qAv4.qclass = kDNSClass_IN; - query->qAv4.LongLived = mDNSfalse; - query->qAv4.ExpectUnique = mDNStrue; - query->qAv4.ForceMCast = mDNSfalse; - query->qAv4.ReturnIntermed = mDNSfalse; - query->qAv4.SuppressUnusable = mDNSfalse; - query->qAv4.SearchListIndex = 0; - query->qAv4.AppendSearchDomains = 0; - query->qAv4.RetryWithSearchDomains = mDNSfalse; - query->qAv4.TimeoutQuestion = 0; - query->qAv4.WakeOnResolve = 0; - query->qAv4.qnameOrig = mDNSNULL; - query->qAv4.QuestionCallback = FoundServiceInfo; - query->qAv4.QuestionContext = query; - - query->qAv6.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question - query->qAv6.InterfaceID = info->InterfaceID; - query->qAv6.Target = zeroAddr; - query->qAv6.qname.c[0] = 0; - query->qAv6.qtype = kDNSType_AAAA; - query->qAv6.qclass = kDNSClass_IN; - query->qAv6.LongLived = mDNSfalse; - query->qAv6.ExpectUnique = mDNStrue; - query->qAv6.ForceMCast = mDNSfalse; - query->qAv6.ReturnIntermed = mDNSfalse; - query->qAv6.SuppressUnusable = mDNSfalse; - query->qAv6.SearchListIndex = 0; - query->qAv6.AppendSearchDomains = 0; - query->qAv6.RetryWithSearchDomains = mDNSfalse; - query->qAv6.TimeoutQuestion = 0; - query->qAv6.WakeOnResolve = 0; - query->qAv6.qnameOrig = mDNSNULL; - query->qAv6.QuestionCallback = FoundServiceInfo; - query->qAv6.QuestionContext = query; - - query->GotSRV = mDNSfalse; - query->GotTXT = mDNSfalse; - query->GotADD = mDNSfalse; - query->Answers = 0; - - query->info = info; - query->ServiceInfoQueryCallback = Callback; - query->ServiceInfoQueryContext = Context; + ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context) +{ + mStatus status; + mDNS_Lock(m); + + query->qSRV.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question + query->qSRV.InterfaceID = info->InterfaceID; + query->qSRV.flags = 0; + query->qSRV.Target = zeroAddr; + 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.ReturnIntermed = mDNSfalse; + query->qSRV.SuppressUnusable = mDNSfalse; + query->qSRV.SearchListIndex = 0; + query->qSRV.AppendSearchDomains = 0; + query->qSRV.RetryWithSearchDomains = mDNSfalse; + query->qSRV.TimeoutQuestion = 0; + query->qSRV.WakeOnResolve = 0; + query->qSRV.UseBrackgroundTrafficClass = mDNSfalse; + query->qSRV.ValidationRequired = 0; + query->qSRV.ValidatingResponse = 0; + query->qSRV.qnameOrig = mDNSNULL; + query->qSRV.QuestionCallback = FoundServiceInfoSRV; + query->qSRV.QuestionContext = query; + + query->qTXT.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question + query->qTXT.InterfaceID = info->InterfaceID; + query->qTXT.flags = 0; + query->qTXT.Target = zeroAddr; + 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.ReturnIntermed = mDNSfalse; + query->qTXT.SuppressUnusable = mDNSfalse; + query->qTXT.SearchListIndex = 0; + query->qTXT.AppendSearchDomains = 0; + query->qTXT.RetryWithSearchDomains = mDNSfalse; + query->qTXT.TimeoutQuestion = 0; + query->qTXT.WakeOnResolve = 0; + query->qTXT.UseBrackgroundTrafficClass = mDNSfalse; + query->qTXT.ValidationRequired = 0; + query->qTXT.ValidatingResponse = 0; + query->qTXT.qnameOrig = mDNSNULL; + query->qTXT.QuestionCallback = FoundServiceInfoTXT; + query->qTXT.QuestionContext = query; + + query->qAv4.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question + query->qAv4.InterfaceID = info->InterfaceID; + query->qAv4.flags = 0; + query->qAv4.Target = zeroAddr; + 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.ReturnIntermed = mDNSfalse; + query->qAv4.SuppressUnusable = mDNSfalse; + query->qAv4.SearchListIndex = 0; + query->qAv4.AppendSearchDomains = 0; + query->qAv4.RetryWithSearchDomains = mDNSfalse; + query->qAv4.TimeoutQuestion = 0; + query->qAv4.WakeOnResolve = 0; + query->qAv4.UseBrackgroundTrafficClass = mDNSfalse; + query->qAv4.ValidationRequired = 0; + query->qAv4.ValidatingResponse = 0; + query->qAv4.qnameOrig = mDNSNULL; + query->qAv4.QuestionCallback = FoundServiceInfo; + query->qAv4.QuestionContext = query; + + query->qAv6.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question + query->qAv6.InterfaceID = info->InterfaceID; + query->qAv6.flags = 0; + query->qAv6.Target = zeroAddr; + 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.ReturnIntermed = mDNSfalse; + query->qAv6.SuppressUnusable = mDNSfalse; + query->qAv6.SearchListIndex = 0; + query->qAv6.AppendSearchDomains = 0; + query->qAv6.RetryWithSearchDomains = mDNSfalse; + query->qAv6.TimeoutQuestion = 0; + query->qAv6.UseBrackgroundTrafficClass = mDNSfalse; + query->qAv6.ValidationRequired = 0; + query->qAv6.ValidatingResponse = 0; + query->qAv6.qnameOrig = mDNSNULL; + query->qAv6.QuestionCallback = FoundServiceInfo; + query->qAv6.QuestionContext = query; + + query->GotSRV = mDNSfalse; + query->GotTXT = mDNSfalse; + query->GotADD = mDNSfalse; + query->Answers = 0; + + query->info = info; + query->ServiceInfoQueryCallback = Callback; + query->ServiceInfoQueryContext = Context; // info->name = Must already be set up by client // info->interface = Must already be set up by client - info->ip = zeroAddr; - info->port = zeroIPPort; - info->TXTlen = 0; + info->ip = zeroAddr; + 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); + // 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); - mDNS_Unlock(m); - return(status); - } + mDNS_Unlock(m); + return(status); +} mDNSexport void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *q) - { - mDNS_Lock(m); - // We use mDNS_StopQuery_internal here because we're already holding the lock - if (q->qSRV.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qSRV); - if (q->qTXT.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qTXT); - if (q->qAv4.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qAv4); - if (q->qAv6.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qAv6); - mDNS_Unlock(m); - } +{ + mDNS_Lock(m); + // We use mDNS_StopQuery_internal here because we're already holding the lock + if (q->qSRV.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qSRV); + if (q->qTXT.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qTXT); + if (q->qAv4.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qAv4); + if (q->qAv6.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qAv6); + mDNS_Unlock(m); +} mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom, - const mDNSInterfaceID InterfaceID, 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 = mDNSfalse; - question->ReturnIntermed = mDNSfalse; - question->SuppressUnusable = mDNSfalse; - question->SearchListIndex = 0; - question->AppendSearchDomains = 0; - question->RetryWithSearchDomains = mDNSfalse; - question->TimeoutQuestion = 0; - question->WakeOnResolve = 0; - question->qnameOrig = mDNSNULL; - question->QuestionCallback = Callback; - question->QuestionContext = Context; - if (DomainType > mDNS_DomainTypeMax) return(mStatus_BadParamErr); - if (!MakeDomainNameFromDNSNameString(&question->qname, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr); - if (!dom) dom = &localdomain; - if (!AppendDomainName(&question->qname, dom)) return(mStatus_BadParamErr); - return(mDNS_StartQuery(m, question)); - } + const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context) +{ + question->InterfaceID = InterfaceID; + question->flags = 0; + question->Target = zeroAddr; + question->qtype = kDNSType_PTR; + question->qclass = kDNSClass_IN; + question->LongLived = mDNSfalse; + question->ExpectUnique = mDNSfalse; + question->ForceMCast = mDNSfalse; + question->ReturnIntermed = mDNSfalse; + question->SuppressUnusable = mDNSfalse; + question->SearchListIndex = 0; + question->AppendSearchDomains = 0; + question->RetryWithSearchDomains = mDNSfalse; + question->TimeoutQuestion = 0; + question->WakeOnResolve = 0; + question->UseBrackgroundTrafficClass = mDNSfalse; + question->ValidationRequired = 0; + question->ValidatingResponse = 0; + question->qnameOrig = mDNSNULL; + question->QuestionCallback = Callback; + question->QuestionContext = Context; + if (DomainType > mDNS_DomainTypeMax) return(mStatus_BadParamErr); + if (!MakeDomainNameFromDNSNameString(&question->qname, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr); + if (!dom) dom = &localdomain; + if (!AppendDomainName(&question->qname, dom)) return(mStatus_BadParamErr); + return(mDNS_StartQuery(m, question)); +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -9124,692 +10284,712 @@ mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, m #endif mDNSexport mStatus mDNS_Register(mDNS *const m, AuthRecord *const rr) - { - mStatus status; - mDNS_Lock(m); - status = mDNS_Register_internal(m, rr); - mDNS_Unlock(m); - return(status); - } +{ + mStatus status; + mDNS_Lock(m); + status = mDNS_Register_internal(m, rr); + mDNS_Unlock(m); + return(status); +} mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newttl, - const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback) - { - if (!ValidateRData(rr->resrec.rrtype, newrdlength, newrdata)) - { - 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 - if (newttl == 0) newttl = rr->resrec.rroriginalttl; - - // If we already have an update queued up which has not gone through yet, give the client a chance to free that memory - if (rr->NewRData) - { - RData *n = rr->NewRData; - rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... - if (rr->UpdateCallback) - rr->UpdateCallback(m, rr, n, rr->newrdlength); // ...and let the client free this memory, if necessary - } - - rr->NewRData = newrdata; - rr->newrdlength = newrdlength; - rr->UpdateCallback = Callback; + const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback) +{ + if (!ValidateRData(rr->resrec.rrtype, newrdlength, newrdata)) + { + 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 + if (newttl == 0) newttl = rr->resrec.rroriginalttl; + + // If we already have an update queued up which has not gone through yet, give the client a chance to free that memory + if (rr->NewRData) + { + RData *n = rr->NewRData; + rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... + if (rr->UpdateCallback) + rr->UpdateCallback(m, rr, n, rr->newrdlength); // ...and let the client free this memory, if necessary + } + + rr->NewRData = newrdata; + rr->newrdlength = newrdlength; + rr->UpdateCallback = Callback; #ifndef UNICAST_DISABLED - if (rr->ARType != AuthRecordLocalOnly && rr->ARType != AuthRecordP2P && !IsLocalDomain(rr->resrec.name)) - { - mStatus status = uDNS_UpdateRecord(m, rr); - // The caller frees the memory on error, don't retain stale pointers - if (status != mStatus_NoError) { rr->NewRData = mDNSNULL; rr->newrdlength = 0; } - mDNS_Unlock(m); - return(status); - } + if (rr->ARType != AuthRecordLocalOnly && rr->ARType != AuthRecordP2P && !IsLocalDomain(rr->resrec.name)) + { + mStatus status = uDNS_UpdateRecord(m, rr); + // The caller frees the memory on error, don't retain stale pointers + if (status != mStatus_NoError) { rr->NewRData = mDNSNULL; rr->newrdlength = 0; } + mDNS_Unlock(m); + return(status); + } #endif - if (RRLocalOnly(rr) || (rr->resrec.rroriginalttl == newttl && - rr->resrec.rdlength == newrdlength && mDNSPlatformMemSame(rr->resrec.rdata->u.data, newrdata->u.data, newrdlength))) - CompleteRDataUpdate(m, rr); - else - { - rr->AnnounceCount = InitialAnnounceCount; - 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 = NonZeroTime(m->timenow + kUpdateCreditRefreshInterval); - if (rr->AnnounceCount > rr->UpdateCredits + 1) rr->AnnounceCount = (mDNSu8)(rr->UpdateCredits + 1); - if (rr->UpdateCredits <= 5) - { - mDNSu32 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 + (mDNSs32)delay * mDNSPlatformOneSecond); - rr->ThisAPInterval *= 4; - 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; - } - - mDNS_Unlock(m); - return(mStatus_NoError); - } + if (RRLocalOnly(rr) || (rr->resrec.rroriginalttl == newttl && + rr->resrec.rdlength == newrdlength && mDNSPlatformMemSame(rr->resrec.rdata->u.data, newrdata->u.data, newrdlength))) + CompleteRDataUpdate(m, rr); + else + { + rr->AnnounceCount = InitialAnnounceCount; + 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 = NonZeroTime(m->timenow + kUpdateCreditRefreshInterval); + if (rr->AnnounceCount > rr->UpdateCredits + 1) rr->AnnounceCount = (mDNSu8)(rr->UpdateCredits + 1); + if (rr->UpdateCredits <= 5) + { + mDNSu32 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 + (mDNSs32)delay * mDNSPlatformOneSecond); + rr->ThisAPInterval *= 4; + 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; + } + + mDNS_Unlock(m); + return(mStatus_NoError); +} // Note: mDNS_Deregister calls mDNS_Deregister_internal which can call a user callback, which may change // the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. mDNSexport mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr) - { - mStatus status; - mDNS_Lock(m); - status = mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); - mDNS_Unlock(m); - return(status); - } +{ + mStatus status; + mDNS_Lock(m); + status = mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); + mDNS_Unlock(m); + return(status); +} // Circular reference: AdvertiseInterface references mDNS_HostNameCallback, which calls mDNS_SetFQDN, which call AdvertiseInterface mDNSlocal void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result); mDNSlocal NetworkInterfaceInfo *FindFirstAdvertisedInterface(mDNS *const m) - { - NetworkInterfaceInfo *intf; - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->Advertise) break; - return(intf); - } +{ + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->Advertise) break; + return(intf); +} mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) - { - char buffer[MAX_REVERSE_MAPPING_NAME]; - NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m); - if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary +{ + char buffer[MAX_REVERSE_MAPPING_NAME]; + NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m); + if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary - // Send dynamic update for non-linklocal IPv4 Addresses - mDNS_SetupResourceRecord(&set->RR_A, mDNSNULL, set->InterfaceID, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnique, AuthRecordAny, mDNS_HostNameCallback, set); - mDNS_SetupResourceRecord(&set->RR_PTR, mDNSNULL, set->InterfaceID, kDNSType_PTR, kHostNameTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); - mDNS_SetupResourceRecord(&set->RR_HINFO, mDNSNULL, set->InterfaceID, kDNSType_HINFO, kHostNameTTL, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + // Send dynamic update for non-linklocal IPv4 Addresses + mDNS_SetupResourceRecord(&set->RR_A, mDNSNULL, set->InterfaceID, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnique, AuthRecordAny, mDNS_HostNameCallback, set); + mDNS_SetupResourceRecord(&set->RR_PTR, mDNSNULL, set->InterfaceID, kDNSType_PTR, kHostNameTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + mDNS_SetupResourceRecord(&set->RR_HINFO, mDNSNULL, set->InterfaceID, kDNSType_HINFO, kHostNameTTL, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL); #if ANSWER_REMOTE_HOSTNAME_QUERIES - set->RR_A .AllowRemoteQuery = mDNStrue; - set->RR_PTR .AllowRemoteQuery = mDNStrue; - set->RR_HINFO.AllowRemoteQuery = mDNStrue; + 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.namestorage, &m->MulticastHostname); - if (set->ip.type == mDNSAddrType_IPv4) - { - set->RR_A.resrec.rrtype = kDNSType_A; - set->RR_A.resrec.rdata->u.ipv4 = set->ip.ip.v4; - // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code - mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", - set->ip.ip.v4.b[3], set->ip.ip.v4.b[2], set->ip.ip.v4.b[1], set->ip.ip.v4.b[0]); - } - else if (set->ip.type == mDNSAddrType_IPv6) - { - int i; - set->RR_A.resrec.rrtype = kDNSType_AAAA; - set->RR_A.resrec.rdata->u.ipv6 = set->ip.ip.v6; - for (i = 0; i < 16; i++) - { - static const char hexValues[] = "0123456789ABCDEF"; - buffer[i * 4 ] = hexValues[set->ip.ip.v6.b[15 - i] & 0x0F]; - buffer[i * 4 + 1] = '.'; - buffer[i * 4 + 2] = hexValues[set->ip.ip.v6.b[15 - i] >> 4]; - buffer[i * 4 + 3] = '.'; - } - mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa."); - } - - MakeDomainNameFromDNSNameString(&set->RR_PTR.namestorage, buffer); - set->RR_PTR.AutoTarget = Target_AutoHost; // 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 - - mDNS_Register_internal(m, &set->RR_A); - mDNS_Register_internal(m, &set->RR_PTR); - - if (!NO_HINFO && 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.namestorage, &m->MulticastHostname); - set->RR_HINFO.DependentOn = &set->RR_A; - mDNSPlatformMemCopy(p, &m->HIHardware, 1 + (mDNSu32)m->HIHardware.c[0]); - p += 1 + (int)p[0]; - mDNSPlatformMemCopy(p, &m->HISoftware, 1 + (mDNSu32)m->HISoftware.c[0]); - mDNS_Register_internal(m, &set->RR_HINFO); - } - else - { - debugf("Not creating HINFO record: platform support layer provided no information"); - set->RR_HINFO.resrec.RecordType = kDNSRecordTypeUnregistered; - } - } + // 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.namestorage, &m->MulticastHostname); + if (set->ip.type == mDNSAddrType_IPv4) + { + set->RR_A.resrec.rrtype = kDNSType_A; + set->RR_A.resrec.rdata->u.ipv4 = set->ip.ip.v4; + // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code + mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", + set->ip.ip.v4.b[3], set->ip.ip.v4.b[2], set->ip.ip.v4.b[1], set->ip.ip.v4.b[0]); + } + else if (set->ip.type == mDNSAddrType_IPv6) + { + int i; + set->RR_A.resrec.rrtype = kDNSType_AAAA; + set->RR_A.resrec.rdata->u.ipv6 = set->ip.ip.v6; + for (i = 0; i < 16; i++) + { + static const char hexValues[] = "0123456789ABCDEF"; + buffer[i * 4 ] = hexValues[set->ip.ip.v6.b[15 - i] & 0x0F]; + buffer[i * 4 + 1] = '.'; + buffer[i * 4 + 2] = hexValues[set->ip.ip.v6.b[15 - i] >> 4]; + buffer[i * 4 + 3] = '.'; + } + mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa."); + } + + MakeDomainNameFromDNSNameString(&set->RR_PTR.namestorage, buffer); + set->RR_PTR.AutoTarget = Target_AutoHost; // 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 + + mDNS_Register_internal(m, &set->RR_A); + mDNS_Register_internal(m, &set->RR_PTR); + + if (!NO_HINFO && 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.namestorage, &m->MulticastHostname); + set->RR_HINFO.DependentOn = &set->RR_A; + mDNSPlatformMemCopy(p, &m->HIHardware, 1 + (mDNSu32)m->HIHardware.c[0]); + p += 1 + (int)p[0]; + mDNSPlatformMemCopy(p, &m->HISoftware, 1 + (mDNSu32)m->HISoftware.c[0]); + mDNS_Register_internal(m, &set->RR_HINFO); + } + else + { + debugf("Not creating HINFO record: platform support layer provided no information"); + set->RR_HINFO.resrec.RecordType = kDNSRecordTypeUnregistered; + } +} mDNSlocal void DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) - { - NetworkInterfaceInfo *intf; - +{ + NetworkInterfaceInfo *intf; + // If we still have address records referring to this one, update them - NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m); - AuthRecord *A = primary ? &primary->RR_A : mDNSNULL; - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->RR_A.RRSet == &set->RR_A) - intf->RR_A.RRSet = A; - - // Unregister these records. - // When doing the mDNS_Exit processing, we first call DeadvertiseInterface for each interface, so by the time the platform - // support layer gets to call mDNS_DeregisterInterface, the address and PTR records have already been deregistered for it. - // Also, in the event of a name conflict, one or more of our records will have been forcibly deregistered. - // To avoid unnecessary and misleading warning messages, we check the RecordType before calling mDNS_Deregister_internal(). - if (set->RR_A. resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_A, mDNS_Dereg_normal); - if (set->RR_PTR. resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_PTR, mDNS_Dereg_normal); - if (set->RR_HINFO.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_HINFO, mDNS_Dereg_normal); - } + NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m); + AuthRecord *A = primary ? &primary->RR_A : mDNSNULL; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->RR_A.RRSet == &set->RR_A) + intf->RR_A.RRSet = A; + + // Unregister these records. + // When doing the mDNS_Exit processing, we first call DeadvertiseInterface for each interface, so by the time the platform + // support layer gets to call mDNS_DeregisterInterface, the address and PTR records have already been deregistered for it. + // Also, in the event of a name conflict, one or more of our records will have been forcibly deregistered. + // To avoid unnecessary and misleading warning messages, we check the RecordType before calling mDNS_Deregister_internal(). + if (set->RR_A.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_A, mDNS_Dereg_normal); + if (set->RR_PTR.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_PTR, mDNS_Dereg_normal); + if (set->RR_HINFO.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_HINFO, mDNS_Dereg_normal); +} mDNSexport void mDNS_SetFQDN(mDNS *const m) - { - domainname newmname; - NetworkInterfaceInfo *intf; - AuthRecord *rr; - newmname.c[0] = 0; - - 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; } - - mDNS_Lock(m); - - if (SameDomainNameCS(&m->MulticastHostname, &newmname)) debugf("mDNS_SetFQDN - hostname unchanged"); - else - { - AssignDomainName(&m->MulticastHostname, &newmname); - - // 1. Stop advertising our address records on all interfaces - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->Advertise) DeadvertiseInterface(m, intf); - - // 2. Start advertising our address records using the new name - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->Advertise) AdvertiseInterface(m, intf); - } - - // 3. Make sure that any AutoTarget SRV records (and the like) get updated - for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->AutoTarget) SetTargetToHostName(m, rr); - for (rr = m->DuplicateRecords; rr; rr=rr->next) if (rr->AutoTarget) SetTargetToHostName(m, rr); - - mDNS_Unlock(m); - } +{ + domainname newmname; + NetworkInterfaceInfo *intf; + AuthRecord *rr; + newmname.c[0] = 0; + + 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; } + + mDNS_Lock(m); + + if (SameDomainNameCS(&m->MulticastHostname, &newmname)) debugf("mDNS_SetFQDN - hostname unchanged"); + else + { + AssignDomainName(&m->MulticastHostname, &newmname); + + // 1. Stop advertising our address records on all interfaces + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->Advertise) DeadvertiseInterface(m, intf); + + // 2. Start advertising our address records using the new name + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->Advertise) AdvertiseInterface(m, intf); + } + + // 3. Make sure that any AutoTarget SRV records (and the like) get updated + for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->AutoTarget) SetTargetToHostName(m, rr); + for (rr = m->DuplicateRecords; rr; rr=rr->next) if (rr->AutoTarget) SetTargetToHostName(m, rr); + + mDNS_Unlock(m); +} mDNSlocal void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) - { - (void)rr; // Unused parameter - - #if MDNS_DEBUGMSGS - { - char *msg = "Unknown result"; - if (result == mStatus_NoError) msg = "Name registered"; - else if (result == mStatus_NameConflict) msg = "Name conflict"; - debugf("mDNS_HostNameCallback: %##s (%s) %s (%ld)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), msg, result); - } - #endif - - if (result == mStatus_NoError) - { - // Notify the client that the host name is successfully registered - if (m->MainCallback) - m->MainCallback(m, mStatus_NoError); - } - else if (result == mStatus_NameConflict) - { - domainlabel oldlabel = m->hostlabel; - - // 1. First give the client callback a chance to pick a new name - if (m->MainCallback) - m->MainCallback(m, mStatus_NameConflict); - - // 2. If the client callback didn't do it, add (or increment) an index ourselves - // This needs to be case-INSENSITIVE compare, because we need to know that the name has been changed so as to - // remedy the conflict, and a name that differs only in capitalization will just suffer the exact same conflict again. - if (SameDomainLabel(m->hostlabel.c, oldlabel.c)) - IncrementLabelSuffix(&m->hostlabel, mDNSfalse); - - // 3. Generate the FQDNs from the hostlabel, - // and make sure all SRV records, etc., are updated to reference our new hostname - mDNS_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); - } +{ + (void)rr; // Unused parameter + + #if MDNS_DEBUGMSGS + { + char *msg = "Unknown result"; + if (result == mStatus_NoError) msg = "Name registered"; + else if (result == mStatus_NameConflict) msg = "Name conflict"; + debugf("mDNS_HostNameCallback: %##s (%s) %s (%ld)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), msg, result); + } + #endif + + if (result == mStatus_NoError) + { + // Notify the client that the host name is successfully registered + if (m->MainCallback) + m->MainCallback(m, mStatus_NoError); + } + else if (result == mStatus_NameConflict) + { + domainlabel oldlabel = m->hostlabel; + + // 1. First give the client callback a chance to pick a new name + if (m->MainCallback) + m->MainCallback(m, mStatus_NameConflict); + + // 2. If the client callback didn't do it, add (or increment) an index ourselves + // This needs to be case-INSENSITIVE compare, because we need to know that the name has been changed so as to + // remedy the conflict, and a name that differs only in capitalization will just suffer the exact same conflict again. + if (SameDomainLabel(m->hostlabel.c, oldlabel.c)) + IncrementLabelSuffix(&m->hostlabel, mDNSfalse); + + // 3. Generate the FQDNs from the hostlabel, + // and make sure all SRV records, etc., are updated to reference our new hostname + mDNS_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); +} mDNSlocal void UpdateInterfaceProtocols(mDNS *const m, NetworkInterfaceInfo *active) - { - NetworkInterfaceInfo *intf; - active->IPv4Available = mDNSfalse; - active->IPv6Available = mDNSfalse; - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->InterfaceID == active->InterfaceID) - { - if (intf->ip.type == mDNSAddrType_IPv4 && intf->McastTxRx) active->IPv4Available = mDNStrue; - if (intf->ip.type == mDNSAddrType_IPv6 && intf->McastTxRx) active->IPv6Available = mDNStrue; - } - } +{ + NetworkInterfaceInfo *intf; + active->IPv4Available = mDNSfalse; + active->IPv6Available = mDNSfalse; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->InterfaceID == active->InterfaceID) + { + if (intf->ip.type == mDNSAddrType_IPv4 && intf->McastTxRx) active->IPv4Available = mDNStrue; + if (intf->ip.type == mDNSAddrType_IPv6 && intf->McastTxRx) active->IPv6Available = mDNStrue; + } +} mDNSlocal void RestartRecordGetZoneData(mDNS * const m) - { - AuthRecord *rr; - LogInfo("RestartRecordGetZoneData: ResourceRecords"); - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (AuthRecord_uDNS(rr) && rr->state != regState_NoTarget) - { - debugf("RestartRecordGetZoneData: StartGetZoneData for %##s", rr->resrec.name->c); - // Zero out the updateid so that if we have a pending response from the server, it won't - // be accepted as a valid response. If we accept the response, we might free the new "nta" - if (rr->nta) { rr->updateid = zeroID; CancelGetZoneData(m, rr->nta); } - rr->nta = StartGetZoneData(m, rr->resrec.name, ZoneServiceUpdate, RecordRegistrationGotZoneData, rr); - } - } +{ + AuthRecord *rr; + LogInfo("RestartRecordGetZoneData: ResourceRecords"); + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (AuthRecord_uDNS(rr) && rr->state != regState_NoTarget) + { + debugf("RestartRecordGetZoneData: StartGetZoneData for %##s", rr->resrec.name->c); + // Zero out the updateid so that if we have a pending response from the server, it won't + // be accepted as a valid response. If we accept the response, we might free the new "nta" + if (rr->nta) { rr->updateid = zeroID; CancelGetZoneData(m, rr->nta); } + rr->nta = StartGetZoneData(m, rr->resrec.name, ZoneServiceUpdate, RecordRegistrationGotZoneData, rr); + } +} mDNSlocal void InitializeNetWakeState(mDNS *const m, NetworkInterfaceInfo *set) - { - int i; - set->NetWakeBrowse.ThisQInterval = -1; - for (i=0; i<3; i++) - { - set->NetWakeResolve[i].ThisQInterval = -1; - set->SPSAddr[i].type = mDNSAddrType_None; - } - set->NextSPSAttempt = -1; - set->NextSPSAttemptTime = m->timenow; - } +{ + int i; + set->NetWakeBrowse.ThisQInterval = -1; + for (i=0; i<3; i++) + { + set->NetWakeResolve[i].ThisQInterval = -1; + set->SPSAddr[i].type = mDNSAddrType_None; + } + set->NextSPSAttempt = -1; + set->NextSPSAttemptTime = m->timenow; +} mDNSexport void mDNS_ActivateNetWake_internal(mDNS *const m, NetworkInterfaceInfo *set) - { - NetworkInterfaceInfo *p = m->HostInterfaces; - while (p && p != set) p=p->next; - if (!p) { LogMsg("mDNS_ActivateNetWake_internal: NetworkInterfaceInfo %p not found in active list", set); return; } - - if (set->InterfaceActive) - { - LogSPS("ActivateNetWake for %s (%#a)", set->ifname, &set->ip); - mDNS_StartBrowse_internal(m, &set->NetWakeBrowse, &SleepProxyServiceType, &localdomain, set->InterfaceID, mDNSfalse, m->SPSBrowseCallback, set); - } - } +{ + NetworkInterfaceInfo *p = m->HostInterfaces; + while (p && p != set) p=p->next; + if (!p) { LogMsg("mDNS_ActivateNetWake_internal: NetworkInterfaceInfo %p not found in active list", set); return; } + + if (set->InterfaceActive) + { + LogSPS("ActivateNetWake for %s (%#a)", set->ifname, &set->ip); + mDNS_StartBrowse_internal(m, &set->NetWakeBrowse, &SleepProxyServiceType, &localdomain, set->InterfaceID, 0, mDNSfalse, mDNSfalse, m->SPSBrowseCallback, set); + } +} mDNSexport void mDNS_DeactivateNetWake_internal(mDNS *const m, NetworkInterfaceInfo *set) - { - NetworkInterfaceInfo *p = m->HostInterfaces; - while (p && p != set) p=p->next; - if (!p) { LogMsg("mDNS_DeactivateNetWake_internal: NetworkInterfaceInfo %p not found in active list", set); return; } - - if (set->NetWakeBrowse.ThisQInterval >= 0) - { - int i; - LogSPS("DeactivateNetWake for %s (%#a)", set->ifname, &set->ip); - - // Stop our browse and resolve operations - mDNS_StopQuery_internal(m, &set->NetWakeBrowse); - for (i=0; i<3; i++) if (set->NetWakeResolve[i].ThisQInterval >= 0) mDNS_StopQuery_internal(m, &set->NetWakeResolve[i]); - - // Make special call to the browse callback to let it know it can to remove all records for this interface - if (m->SPSBrowseCallback) - { - mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback - m->SPSBrowseCallback(m, &set->NetWakeBrowse, mDNSNULL, mDNSfalse); - mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again - } - - // Reset our variables back to initial state, so we're ready for when NetWake is turned back on - // (includes resetting NetWakeBrowse.ThisQInterval back to -1) - InitializeNetWakeState(m, set); - } - } +{ + NetworkInterfaceInfo *p = m->HostInterfaces; + while (p && p != set) p=p->next; + if (!p) { LogMsg("mDNS_DeactivateNetWake_internal: NetworkInterfaceInfo %p not found in active list", set); return; } + + if (set->NetWakeBrowse.ThisQInterval >= 0) + { + int i; + LogSPS("DeactivateNetWake for %s (%#a)", set->ifname, &set->ip); + + // Stop our browse and resolve operations + mDNS_StopQuery_internal(m, &set->NetWakeBrowse); + for (i=0; i<3; i++) if (set->NetWakeResolve[i].ThisQInterval >= 0) mDNS_StopQuery_internal(m, &set->NetWakeResolve[i]); + + // Make special call to the browse callback to let it know it can to remove all records for this interface + if (m->SPSBrowseCallback) + { + mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback + m->SPSBrowseCallback(m, &set->NetWakeBrowse, mDNSNULL, mDNSfalse); + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + } + + // Reset our variables back to initial state, so we're ready for when NetWake is turned back on + // (includes resetting NetWakeBrowse.ThisQInterval back to -1) + InitializeNetWakeState(m, set); + } +} mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping) - { - AuthRecord *rr; - mDNSBool FirstOfType = mDNStrue; - NetworkInterfaceInfo **p = &m->HostInterfaces; - - if (!set->InterfaceID) - { LogMsg("mDNS_RegisterInterface: Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set->ip); return(mStatus_Invalid); } - - if (!mDNSAddressIsValidNonZero(&set->mask)) - { LogMsg("mDNS_RegisterInterface: 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 now, unless we find a duplicate already in the list - set->InterfaceActive = mDNStrue; - set->IPv4Available = (mDNSu8)(set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx); - set->IPv6Available = (mDNSu8)(set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx); - - InitializeNetWakeState(m, set); - - // Scan list to see if this InterfaceID is already represented - while (*p) - { - if (*p == set) - { - LogMsg("mDNS_RegisterInterface: Error! Tried to register a NetworkInterfaceInfo that's already in the list"); - mDNS_Unlock(m); - return(mStatus_AlreadyRegistered); - } - - if ((*p)->InterfaceID == set->InterfaceID) - { - // This InterfaceID already represented by a different interface in the list, so mark this instance inactive for now - set->InterfaceActive = mDNSfalse; - if (set->ip.type == (*p)->ip.type) FirstOfType = mDNSfalse; - if (set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx) (*p)->IPv4Available = mDNStrue; - if (set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx) (*p)->IPv6Available = mDNStrue; - } - - p=&(*p)->next; - } - - set->next = mDNSNULL; - *p = set; - - if (set->Advertise) - AdvertiseInterface(m, set); - - LogInfo("mDNS_RegisterInterface: InterfaceID %p %s (%#a) %s", set->InterfaceID, set->ifname, &set->ip, - set->InterfaceActive ? - "not represented in list; marking active and retriggering queries" : - "already represented in list; marking inactive for now"); - - if (set->NetWake) mDNS_ActivateNetWake_internal(m, set); - - // In early versions of OS X the IPv6 address remains on an interface even when the interface is turned off, - // giving the false impression that there's an active representative of this interface when there really isn't. - // Therefore, when registering an interface, we want to re-trigger our questions and re-probe our Resource Records, - // even if we believe that we previously had an active representative of this interface. - if (set->McastTxRx && (FirstOfType || set->InterfaceActive)) - { - DNSQuestion *q; - // Normally, after an interface comes up, we pause half a second before beginning probing. - // This is to guard against cases where there's rapid interface changes, where we could be confused by - // seeing packets we ourselves sent just moments ago (perhaps when this interface had a different address) - // which are then echoed back after a short delay by some Ethernet switches and some 802.11 base stations. - // We don't want to do a probe, and then see a stale echo of an announcement we ourselves sent, - // and think it's a conflicting answer to our probe. - // In the case of a flapping interface, we pause for five seconds, and reduce the announcement count to one packet. - const mDNSs32 probedelay = flapping ? mDNSPlatformOneSecond * 5 : mDNSPlatformOneSecond / 2; - const mDNSu8 numannounce = flapping ? (mDNSu8)1 : InitialAnnounceCount; - - // Use a small amount of randomness: - // In the case of a network administrator turning on an Ethernet hub so that all the - // connected machines establish link at exactly the same time, we don't want them all - // to go and hit the network with identical queries at exactly the same moment. - // We set a random delay of up to InitialQuestionInterval (1/3 second). - // We must *never* set m->SuppressSending to more than that (or set it repeatedly in a way - // that causes mDNSResponder to remain in a prolonged state of SuppressSending, because - // suppressing packet sending for more than about 1/3 second can cause protocol correctness - // to start to break down (e.g. we don't answer probes fast enough, and get name conflicts). - // See mDNS: m->SuppressSending set too enthusiastically - if (!m->SuppressSending) m->SuppressSending = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval); - - if (flapping) LogMsg("mDNS_RegisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip); - - LogInfo("mDNS_RegisterInterface: %s (%#a) probedelay %d", set->ifname, &set->ip, probedelay); - if (m->SuppressProbes == 0 || - m->SuppressProbes - NonZeroTime(m->timenow + probedelay) < 0) - m->SuppressProbes = NonZeroTime(m->timenow + probedelay); - - // Include OWNER option in packets for 60 seconds after connecting to the network. Setting - // it here also handles the wake up case as the network link comes UP after waking causing - // us to reconnect to the network. If we do this as part of the wake up code, it is possible - // that the network link comes UP after 60 seconds and we never set the OWNER option - m->AnnounceOwner = NonZeroTime(m->timenow + 60 * mDNSPlatformOneSecond); - LogInfo("mDNS_RegisterInterface: Setting AnnounceOwner"); - - for (q = m->Questions; q; q=q->next) // Scan our list of questions - if (mDNSOpaque16IsZero(q->TargetQID)) - if (!q->InterfaceID || q->InterfaceID == set->InterfaceID) // If non-specific Q, or Q on this specific interface, - { // then reactivate this question - // If flapping, delay between first and second queries is nine seconds instead of one second - mDNSBool dodelay = flapping && (q->FlappingInterface1 == set->InterfaceID || q->FlappingInterface2 == set->InterfaceID); - mDNSs32 initial = dodelay ? InitialQuestionInterval * QuestionIntervalStep2 : InitialQuestionInterval; - mDNSs32 qdelay = dodelay ? mDNSPlatformOneSecond * 5 : 0; - if (dodelay) LogInfo("No cache records expired for %##s (%s); okay to delay questions a little", q->qname.c, DNSTypeName(q->qtype)); - - if (!q->ThisQInterval || q->ThisQInterval > initial) - { - q->ThisQInterval = initial; - q->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it - } - q->LastQTime = m->timenow - q->ThisQInterval + qdelay; - q->RecentAnswerPkts = 0; - SetNextQueryTime(m,q); - } - - // For all our non-specific authoritative resource records (and any dormant records specific to this interface) - // we now need them to re-probe if necessary, and then re-announce. - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (!AuthRecord_uDNS(rr)) - 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 < numannounce) rr->AnnounceCount = numannounce; - rr->SendNSECNow = mDNSNULL; - InitializeLastAPTime(m, rr); - } - } - - RestartRecordGetZoneData(m); - - CheckSuppressUnusableQuestions(m); - - mDNS_UpdateAllowSleep(m); - - mDNS_Unlock(m); - return(mStatus_NoError); - } +{ + AuthRecord *rr; + mDNSBool FirstOfType = mDNStrue; + NetworkInterfaceInfo **p = &m->HostInterfaces; + + if (!set->InterfaceID) + { LogMsg("mDNS_RegisterInterface: Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set->ip); return(mStatus_Invalid); } + + if (!mDNSAddressIsValidNonZero(&set->mask)) + { LogMsg("mDNS_RegisterInterface: 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 now, unless we find a duplicate already in the list + set->InterfaceActive = mDNStrue; + set->IPv4Available = (mDNSu8)(set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx); + set->IPv6Available = (mDNSu8)(set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx); + + InitializeNetWakeState(m, set); + + // Scan list to see if this InterfaceID is already represented + while (*p) + { + if (*p == set) + { + LogMsg("mDNS_RegisterInterface: Error! Tried to register a NetworkInterfaceInfo that's already in the list"); + mDNS_Unlock(m); + return(mStatus_AlreadyRegistered); + } + + if ((*p)->InterfaceID == set->InterfaceID) + { + // This InterfaceID already represented by a different interface in the list, so mark this instance inactive for now + set->InterfaceActive = mDNSfalse; + if (set->ip.type == (*p)->ip.type) FirstOfType = mDNSfalse; + if (set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx) (*p)->IPv4Available = mDNStrue; + if (set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx) (*p)->IPv6Available = mDNStrue; + } + + p=&(*p)->next; + } + + set->next = mDNSNULL; + *p = set; + + if (set->Advertise) + AdvertiseInterface(m, set); + + LogInfo("mDNS_RegisterInterface: InterfaceID %p %s (%#a) %s", set->InterfaceID, set->ifname, &set->ip, + set->InterfaceActive ? + "not represented in list; marking active and retriggering queries" : + "already represented in list; marking inactive for now"); + + if (set->NetWake) mDNS_ActivateNetWake_internal(m, set); + + // In early versions of OS X the IPv6 address remains on an interface even when the interface is turned off, + // giving the false impression that there's an active representative of this interface when there really isn't. + // Therefore, when registering an interface, we want to re-trigger our questions and re-probe our Resource Records, + // even if we believe that we previously had an active representative of this interface. + if (set->McastTxRx && (FirstOfType || set->InterfaceActive)) + { + DNSQuestion *q; + // Normally, after an interface comes up, we pause half a second before beginning probing. + // This is to guard against cases where there's rapid interface changes, where we could be confused by + // seeing packets we ourselves sent just moments ago (perhaps when this interface had a different address) + // which are then echoed back after a short delay by some Ethernet switches and some 802.11 base stations. + // We don't want to do a probe, and then see a stale echo of an announcement we ourselves sent, + // and think it's a conflicting answer to our probe. + // In the case of a flapping interface, we pause for five seconds, and reduce the announcement count to one packet. + const mDNSs32 probedelay = flapping ? mDNSPlatformOneSecond * 5 : mDNSPlatformOneSecond / 2; + const mDNSu8 numannounce = flapping ? (mDNSu8)1 : InitialAnnounceCount; + + // Use a small amount of randomness: + // In the case of a network administrator turning on an Ethernet hub so that all the + // connected machines establish link at exactly the same time, we don't want them all + // to go and hit the network with identical queries at exactly the same moment. + // We set a random delay of up to InitialQuestionInterval (1/3 second). + // We must *never* set m->SuppressSending to more than that (or set it repeatedly in a way + // that causes mDNSResponder to remain in a prolonged state of SuppressSending, because + // suppressing packet sending for more than about 1/3 second can cause protocol correctness + // to start to break down (e.g. we don't answer probes fast enough, and get name conflicts). + // See mDNS: m->SuppressSending set too enthusiastically + if (!m->SuppressSending) m->SuppressSending = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval); + + if (flapping) LogMsg("mDNS_RegisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip); + + LogInfo("mDNS_RegisterInterface: %s (%#a) probedelay %d", set->ifname, &set->ip, probedelay); + if (m->SuppressProbes == 0 || + m->SuppressProbes - NonZeroTime(m->timenow + probedelay) < 0) + m->SuppressProbes = NonZeroTime(m->timenow + probedelay); + + // Include OWNER option in packets for 60 seconds after connecting to the network. Setting + // it here also handles the wake up case as the network link comes UP after waking causing + // us to reconnect to the network. If we do this as part of the wake up code, it is possible + // that the network link comes UP after 60 seconds and we never set the OWNER option + m->AnnounceOwner = NonZeroTime(m->timenow + 60 * mDNSPlatformOneSecond); + + // Clear the flag that ignores IPv6 neighbor advertisements after 2 seconds. + m->clearIgnoreNA = NonZeroTime(m->timenow + 2 * mDNSPlatformOneSecond); + + LogInfo("mDNS_RegisterInterface: Setting AnnounceOwner"); + + for (q = m->Questions; q; q=q->next) // Scan our list of questions + if (mDNSOpaque16IsZero(q->TargetQID)) + if (!q->InterfaceID || q->InterfaceID == set->InterfaceID) // If non-specific Q, or Q on this specific interface, + { // then reactivate this question + // If flapping, delay between first and second queries is nine seconds instead of one second + mDNSBool dodelay = flapping && (q->FlappingInterface1 == set->InterfaceID || q->FlappingInterface2 == set->InterfaceID); + mDNSs32 initial = dodelay ? InitialQuestionInterval * QuestionIntervalStep2 : InitialQuestionInterval; + mDNSs32 qdelay = dodelay ? mDNSPlatformOneSecond * 5 : 0; + if (dodelay) LogInfo("No cache records expired for %##s (%s); okay to delay questions a little", q->qname.c, DNSTypeName(q->qtype)); + + if (!q->ThisQInterval || q->ThisQInterval > initial) + { + q->ThisQInterval = initial; + q->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it + } + q->LastQTime = m->timenow - q->ThisQInterval + qdelay; + q->RecentAnswerPkts = 0; + SetNextQueryTime(m,q); + } + + // For all our non-specific authoritative resource records (and any dormant records specific to this interface) + // we now need them to re-probe if necessary, and then re-announce. + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == set->InterfaceID) + mDNSCoreRestartRegistration(m, rr, numannounce); + } + + RestartRecordGetZoneData(m); + + CheckSuppressUnusableQuestions(m); + + mDNS_UpdateAllowSleep(m); + + mDNS_Unlock(m); + return(mStatus_NoError); +} // Note: mDNS_DeregisterInterface calls mDNS_Deregister_internal which can call a user callback, which may change // the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping) - { - NetworkInterfaceInfo **p = &m->HostInterfaces; - mDNSBool revalidate = mDNSfalse; - - mDNS_Lock(m); - - // Find this record in our list - while (*p && *p != set) p=&(*p)->next; - if (!*p) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); mDNS_Unlock(m); return; } - - mDNS_DeactivateNetWake_internal(m, set); - - // Unlink this record from our list - *p = (*p)->next; - set->next = mDNSNULL; - - if (!set->InterfaceActive) - { - // If this interface not the active member of its set, update the v4/v6Available flags for the active member - NetworkInterfaceInfo *intf; - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->InterfaceActive && intf->InterfaceID == set->InterfaceID) - UpdateInterfaceProtocols(m, intf); - } - else - { - NetworkInterfaceInfo *intf = FirstInterfaceForID(m, set->InterfaceID); - if (intf) - { - LogInfo("mDNS_DeregisterInterface: Another representative of InterfaceID %p %s (%#a) exists;" - " making it active", set->InterfaceID, set->ifname, &set->ip); - if (intf->InterfaceActive) - LogMsg("mDNS_DeregisterInterface: ERROR intf->InterfaceActive already set for %s (%#a)", set->ifname, &set->ip); - intf->InterfaceActive = mDNStrue; - UpdateInterfaceProtocols(m, intf); - - if (intf->NetWake) mDNS_ActivateNetWake_internal(m, intf); - - // See if another representative *of the same type* exists. If not, we mave have gone from - // dual-stack to v6-only (or v4-only) so we need to reconfirm which records are still valid. - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->InterfaceID == set->InterfaceID && intf->ip.type == set->ip.type) - break; - if (!intf) revalidate = mDNStrue; - } - else - { - mDNSu32 slot; - CacheGroup *cg; - CacheRecord *rr; - DNSQuestion *q; - DNSServer *s; - - LogInfo("mDNS_DeregisterInterface: Last representative of InterfaceID %p %s (%#a) deregistered;" - " marking questions etc. dormant", set->InterfaceID, set->ifname, &set->ip); - - if (set->McastTxRx && flapping) - LogMsg("DeregisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip); - - // 1. Deactivate any questions specific to this interface, and tag appropriate questions - // so that mDNS_RegisterInterface() knows how swiftly it needs to reactivate them - for (q = m->Questions; q; q=q->next) - { - if (q->InterfaceID == set->InterfaceID) q->ThisQInterval = 0; - if (!q->InterfaceID || q->InterfaceID == set->InterfaceID) - { - q->FlappingInterface2 = q->FlappingInterface1; - q->FlappingInterface1 = set->InterfaceID; // Keep history of the last two interfaces to go away - } - } - - // 2. Flush any cache records received on this interface - revalidate = mDNSfalse; // Don't revalidate if we're flushing the records - FORALL_CACHERECORDS(slot, cg, rr) - if (rr->resrec.InterfaceID == set->InterfaceID) - { - // If this interface is deemed flapping, - // postpone deleting the cache records in case the interface comes back again - if (set->McastTxRx && flapping) - { - // For a flapping interface we want these record to go away after 30 seconds - mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForFlappingInterface); - // We set UnansweredQueries = MaxUnansweredQueries so we don't waste time doing any queries for them -- - // if the interface does come back, any relevant questions will be reactivated anyway - rr->UnansweredQueries = MaxUnansweredQueries; - } - else - mDNS_PurgeCacheResourceRecord(m, rr); - } - - // 3. Any DNS servers specific to this interface are now unusable - for (s = m->DNSServers; s; s = s->next) - if (s->interface == set->InterfaceID) - { - s->interface = mDNSInterface_Any; - s->teststate = DNSServer_Disabled; - } - } - } - - // If we were advertising on this interface, deregister those address and reverse-lookup records now - 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. - // Don't need to do this when shutting down, because *all* interfaces are about to go away - if (revalidate && !m->ShutdownTime) - { - mDNSu32 slot; - CacheGroup *cg; - CacheRecord *rr; - FORALL_CACHERECORDS(slot, cg, rr) - if (rr->resrec.InterfaceID == set->InterfaceID) - mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForFlappingInterface); - } - - CheckSuppressUnusableQuestions(m); - - mDNS_UpdateAllowSleep(m); - - mDNS_Unlock(m); - } +{ + NetworkInterfaceInfo **p = &m->HostInterfaces; + mDNSBool revalidate = mDNSfalse; + + mDNS_Lock(m); + + // Find this record in our list + while (*p && *p != set) p=&(*p)->next; + if (!*p) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); mDNS_Unlock(m); return; } + + mDNS_DeactivateNetWake_internal(m, set); + + // Unlink this record from our list + *p = (*p)->next; + set->next = mDNSNULL; + + if (!set->InterfaceActive) + { + // If this interface not the active member of its set, update the v4/v6Available flags for the active member + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->InterfaceActive && intf->InterfaceID == set->InterfaceID) + UpdateInterfaceProtocols(m, intf); + } + else + { + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, set->InterfaceID); + if (intf) + { + LogInfo("mDNS_DeregisterInterface: Another representative of InterfaceID %p %s (%#a) exists;" + " making it active", set->InterfaceID, set->ifname, &set->ip); + if (intf->InterfaceActive) + LogMsg("mDNS_DeregisterInterface: ERROR intf->InterfaceActive already set for %s (%#a)", set->ifname, &set->ip); + intf->InterfaceActive = mDNStrue; + UpdateInterfaceProtocols(m, intf); + + if (intf->NetWake) mDNS_ActivateNetWake_internal(m, intf); + + // See if another representative *of the same type* exists. If not, we mave have gone from + // dual-stack to v6-only (or v4-only) so we need to reconfirm which records are still valid. + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->InterfaceID == set->InterfaceID && intf->ip.type == set->ip.type) + break; + if (!intf) revalidate = mDNStrue; + } + else + { + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *rr; + DNSQuestion *q; + DNSServer *s; + + LogInfo("mDNS_DeregisterInterface: Last representative of InterfaceID %p %s (%#a) deregistered;" + " marking questions etc. dormant", set->InterfaceID, set->ifname, &set->ip); + + if (set->McastTxRx && flapping) + LogMsg("DeregisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip); + + // 1. Deactivate any questions specific to this interface, and tag appropriate questions + // so that mDNS_RegisterInterface() knows how swiftly it needs to reactivate them + for (q = m->Questions; q; q=q->next) + { + if (q->InterfaceID == set->InterfaceID) q->ThisQInterval = 0; + if (!q->InterfaceID || q->InterfaceID == set->InterfaceID) + { + q->FlappingInterface2 = q->FlappingInterface1; + q->FlappingInterface1 = set->InterfaceID; // Keep history of the last two interfaces to go away + } + } + + // 2. Flush any cache records received on this interface + revalidate = mDNSfalse; // Don't revalidate if we're flushing the records + FORALL_CACHERECORDS(slot, cg, rr) + { + if (rr->resrec.InterfaceID == set->InterfaceID) + { + // If this interface is deemed flapping, + // postpone deleting the cache records in case the interface comes back again + if (set->McastTxRx && flapping) + { + // For a flapping interface we want these record to go away after 30 seconds + mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForFlappingInterface); + // We set UnansweredQueries = MaxUnansweredQueries so we don't waste time doing any queries for them -- + // if the interface does come back, any relevant questions will be reactivated anyway + rr->UnansweredQueries = MaxUnansweredQueries; + } + else + { + mDNS_PurgeCacheResourceRecord(m, rr); + } + } + } + + // 3. Any DNS servers specific to this interface are now unusable + for (s = m->DNSServers; s; s = s->next) + if (s->interface == set->InterfaceID) + { + s->interface = mDNSInterface_Any; + s->teststate = DNSServer_Disabled; + } + } + } + + // If we were advertising on this interface, deregister those address and reverse-lookup records now + 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. + // Don't need to do this when shutting down, because *all* interfaces are about to go away + if (revalidate && !m->ShutdownTime) + { + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *rr; + FORALL_CACHERECORDS(slot, cg, rr) + if (rr->resrec.InterfaceID == set->InterfaceID) + mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForFlappingInterface); + } + + CheckSuppressUnusableQuestions(m); + + mDNS_UpdateAllowSleep(m); + + mDNS_Unlock(m); +} mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result) - { - ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext; - (void)m; // Unused parameter - - #if MDNS_DEBUGMSGS - { - char *msg = "Unknown result"; - if (result == mStatus_NoError) msg = "Name Registered"; - else if (result == mStatus_NameConflict) msg = "Name Conflict"; - else if (result == mStatus_MemFree) msg = "Memory Free"; - debugf("ServiceCallback: %##s (%s) %s (%d)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), msg, result); - } - #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 - mDNS_DeregisterService(m, sr); // Unlink the records from our list - return; - } - - if (result == mStatus_MemFree) - { - // If the SRV/TXT/PTR records, or the _services._dns-sd._udp record, or any of the subtype PTR records, - // are still in the process of deregistering, don't pass on the NameConflict/MemFree message until - // every record is finished cleaning up. - mDNSu32 i; - ExtraResourceRecord *e = sr->Extras; - - if (sr->RR_SRV.resrec.RecordType != kDNSRecordTypeUnregistered) return; - if (sr->RR_TXT.resrec.RecordType != kDNSRecordTypeUnregistered) return; - if (sr->RR_PTR.resrec.RecordType != kDNSRecordTypeUnregistered) return; - if (sr->RR_ADV.resrec.RecordType != kDNSRecordTypeUnregistered) return; - for (i=0; iNumSubTypes; i++) if (sr->SubTypes[i].resrec.RecordType != kDNSRecordTypeUnregistered) return; - - while (e) - { - if (e->r.resrec.RecordType != kDNSRecordTypeUnregistered) return; - e = e->next; - } - - // If this ServiceRecordSet was forcibly deregistered, and now its memory is ready for reuse, - // then we can now report the NameConflict to the client - if (sr->Conflict) result = mStatus_NameConflict; - - } - - LogInfo("ServiceCallback: All records %s for %##s", (result == mStatus_MemFree ? "Unregistered": "Registered"), sr->RR_PTR.resrec.name->c); - // CAUTION: MUST NOT do anything more with sr after calling sr->Callback(), because the client's callback - // function is allowed to do anything, including deregistering this service and freeing its memory. - if (sr->ServiceCallback) - sr->ServiceCallback(m, sr, result); - } +{ + ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext; + (void)m; // Unused parameter + + #if MDNS_DEBUGMSGS + { + char *msg = "Unknown result"; + if (result == mStatus_NoError) msg = "Name Registered"; + else if (result == mStatus_NameConflict) msg = "Name Conflict"; + else if (result == mStatus_MemFree) msg = "Memory Free"; + debugf("ServiceCallback: %##s (%s) %s (%d)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), msg, result); + } + #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 + mDNS_DeregisterService(m, sr); // Unlink the records from our list + return; + } + + if (result == mStatus_MemFree) + { + // If the SRV/TXT/PTR records, or the _services._dns-sd._udp record, or any of the subtype PTR records, + // are still in the process of deregistering, don't pass on the NameConflict/MemFree message until + // every record is finished cleaning up. + mDNSu32 i; + ExtraResourceRecord *e = sr->Extras; + + if (sr->RR_SRV.resrec.RecordType != kDNSRecordTypeUnregistered) return; + if (sr->RR_TXT.resrec.RecordType != kDNSRecordTypeUnregistered) return; + if (sr->RR_PTR.resrec.RecordType != kDNSRecordTypeUnregistered) return; + if (sr->RR_ADV.resrec.RecordType != kDNSRecordTypeUnregistered) return; + for (i=0; iNumSubTypes; i++) if (sr->SubTypes[i].resrec.RecordType != kDNSRecordTypeUnregistered) return; + + while (e) + { + if (e->r.resrec.RecordType != kDNSRecordTypeUnregistered) return; + e = e->next; + } + + // If this ServiceRecordSet was forcibly deregistered, and now its memory is ready for reuse, + // then we can now report the NameConflict to the client + if (sr->Conflict) result = mStatus_NameConflict; + + } + + LogInfo("ServiceCallback: All records %s for %##s", (result == mStatus_MemFree ? "Unregistered" : "Registered"), sr->RR_PTR.resrec.name->c); + // CAUTION: MUST NOT do anything more with sr after calling sr->Callback(), because the client's callback + // function is allowed to do anything, including deregistering this service and freeing its memory. + if (sr->ServiceCallback) + sr->ServiceCallback(m, sr, result); +} mDNSlocal void NSSCallback(mDNS *const m, AuthRecord *const rr, mStatus result) - { - ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext; - if (sr->ServiceCallback) - sr->ServiceCallback(m, sr, result); - } +{ + ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext; + if (sr->ServiceCallback) + sr->ServiceCallback(m, sr, result); +} + + +mDNSlocal AuthRecType setAuthRecType(mDNSInterfaceID InterfaceID, mDNSu32 flags) +{ + AuthRecType artype; + + if (InterfaceID == mDNSInterface_LocalOnly) + artype = AuthRecordLocalOnly; + else if (InterfaceID == mDNSInterface_P2P) + artype = AuthRecordP2P; + else if ((InterfaceID == mDNSInterface_Any) && (flags & coreFlagIncludeP2P)) + artype = AuthRecordAnyIncludeP2P; + else if ((InterfaceID == mDNSInterface_Any) && (flags & coreFlagIncludeAWDL)) + artype = AuthRecordAnyIncludeAWDL; + else + artype = AuthRecordAny; + + return artype; +} // Note: // Name is first label of domain name (any dots in the name are actual dots, not label separators) @@ -9821,308 +11001,294 @@ mDNSlocal void NSSCallback(mDNS *const m, AuthRecord *const rr, mStatus result) // then the default host name (m->MulticastHostname) is automatically used // If the optional target host parameter is set, then the storage it points to must remain valid for the lifetime of the service registration 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, - AuthRecord *SubTypes, mDNSu32 NumSubTypes, - mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context, mDNSu32 flags) - { - mStatus err; - mDNSu32 i; - mDNSu32 hostTTL; - AuthRecType artype; - mDNSu8 recordType = (flags & regFlagKnownUnique) ? kDNSRecordTypeKnownUnique : kDNSRecordTypeUnique; - - sr->ServiceCallback = Callback; - sr->ServiceContext = Context; - sr->Conflict = mDNSfalse; - - sr->Extras = mDNSNULL; - sr->NumSubTypes = NumSubTypes; - sr->SubTypes = SubTypes; - - if (InterfaceID == mDNSInterface_LocalOnly) - artype = AuthRecordLocalOnly; - else if (InterfaceID == mDNSInterface_P2P) - artype = AuthRecordP2P; - else if ((InterfaceID == mDNSInterface_Any) && (flags & regFlagIncludeP2P)) - artype = AuthRecordAnyIncludeP2P; - else - artype = AuthRecordAny; - - // Initialize the AuthRecord objects to sane values - // Need to initialize everything correctly *before* making the decision whether to do a RegisterNoSuchService and bail out - mDNS_SetupResourceRecord(&sr->RR_ADV, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeAdvisory, artype, ServiceCallback, sr); - mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, artype, ServiceCallback, sr); - - if (SameDomainName(type, (const domainname *) "\x4" "_ubd" "\x4" "_tcp")) - hostTTL = kHostNameSmallTTL; - else - hostTTL = kHostNameTTL; - - mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, hostTTL, recordType, artype, ServiceCallback, sr); - mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnique, artype, ServiceCallback, sr); - - // If port number is zero, that means the client is really trying to do a RegisterNoSuchService - if (mDNSIPPortIsZero(port)) - return(mDNS_RegisterNoSuchService(m, &sr->RR_SRV, name, type, domain, mDNSNULL, InterfaceID, NSSCallback, sr, (flags & regFlagIncludeP2P))); - - // If the client is registering an oversized TXT record, - // it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it - if (sr->RR_TXT.resrec.rdata->MaxRDLength < txtlen) - sr->RR_TXT.resrec.rdata->MaxRDLength = txtlen; - - // Set up the record names - // For now we only create an advisory record for the main type, not for subtypes - // We need to gain some operational experience before we decide if there's a need to create them for subtypes too - if (ConstructServiceName(&sr->RR_ADV.namestorage, (const domainlabel*)"\x09_services", (const domainname*)"\x07_dns-sd\x04_udp", domain) == mDNSNULL) - return(mStatus_BadParamErr); - if (ConstructServiceName(&sr->RR_PTR.namestorage, mDNSNULL, type, domain) == mDNSNULL) return(mStatus_BadParamErr); - if (ConstructServiceName(&sr->RR_SRV.namestorage, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr); - AssignDomainName(&sr->RR_TXT.namestorage, sr->RR_SRV.resrec.name); - - // 1. Set up the ADV record rdata to advertise our service type - AssignDomainName(&sr->RR_ADV.resrec.rdata->u.name, sr->RR_PTR.resrec.name); - - // 2. Set up the PTR record rdata to point to our service name - // We set up two additionals, so when a client asks for this PTR we automatically send the SRV and the TXT too - // Note: uDNS registration code assumes that Additional1 points to the SRV record - AssignDomainName(&sr->RR_PTR.resrec.rdata->u.name, sr->RR_SRV.resrec.name); - sr->RR_PTR.Additional1 = &sr->RR_SRV; - sr->RR_PTR.Additional2 = &sr->RR_TXT; - - // 2a. Set up any subtype PTRs to point to our service name - // If the client is using subtypes, it is the client's responsibility to have - // already set the first label of the record name to the subtype being registered - for (i=0; iSubTypes[i].resrec.name); - st.c[1+st.c[0]] = 0; // Only want the first label, not the whole FQDN (particularly for mDNS_RenameAndReregisterService()) - AppendDomainName(&st, type); - mDNS_SetupResourceRecord(&sr->SubTypes[i], mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, artype, ServiceCallback, sr); - if (ConstructServiceName(&sr->SubTypes[i].namestorage, mDNSNULL, &st, domain) == mDNSNULL) return(mStatus_BadParamErr); - AssignDomainName(&sr->SubTypes[i].resrec.rdata->u.name, &sr->RR_SRV.namestorage); - sr->SubTypes[i].Additional1 = &sr->RR_SRV; - sr->SubTypes[i].Additional2 = &sr->RR_TXT; - } - - // 3. Set up the SRV record rdata. - sr->RR_SRV.resrec.rdata->u.srv.priority = 0; - sr->RR_SRV.resrec.rdata->u.srv.weight = 0; - sr->RR_SRV.resrec.rdata->u.srv.port = port; - - // Setting AutoTarget tells DNS that the target of this SRV is to be automatically kept in sync with our host name - if (host && host->c[0]) AssignDomainName(&sr->RR_SRV.resrec.rdata->u.srv.target, host); - else { sr->RR_SRV.AutoTarget = Target_AutoHost; sr->RR_SRV.resrec.rdata->u.srv.target.c[0] = '\0'; } - - // 4. Set up the TXT record rdata, - // and set DependentOn because we're depending on the SRV record to find and resolve conflicts for us - // Note: uDNS registration code assumes that DependentOn points to the SRV record - if (txtinfo == mDNSNULL) sr->RR_TXT.resrec.rdlength = 0; - else if (txtinfo != sr->RR_TXT.resrec.rdata->u.txt.c) - { - sr->RR_TXT.resrec.rdlength = txtlen; - if (sr->RR_TXT.resrec.rdlength > sr->RR_TXT.resrec.rdata->MaxRDLength) return(mStatus_BadParamErr); - mDNSPlatformMemCopy(sr->RR_TXT.resrec.rdata->u.txt.c, txtinfo, txtlen); - } - sr->RR_TXT.DependentOn = &sr->RR_SRV; - - mDNS_Lock(m); - // It is important that we register SRV first. uDNS assumes that SRV is registered first so - // that if the SRV cannot find a target, rest of the records that belong to this service - // will not be activated. - err = mDNS_Register_internal(m, &sr->RR_SRV); - // If we can't register the SRV record due to errors, bail out. It has not been inserted in - // any list and hence no need to deregister. We could probably do similar checks for other - // records below and bail out. For now, this seems to be sufficient to address rdar://9304275 - if (err) - { - mDNS_Unlock(m); - return err; - } - if (!err) err = mDNS_Register_internal(m, &sr->RR_TXT); - // We register the RR_PTR last, because we want to be sure that in the event of a forced call to - // mDNS_StartExit, the RR_PTR will be the last one to be forcibly deregistered, since that is what triggers - // the mStatus_MemFree callback to ServiceCallback, which in turn passes on the mStatus_MemFree back to - // the client callback, which is then at liberty to free the ServiceRecordSet memory at will. We need to - // make sure we've deregistered all our records and done any other necessary cleanup before that happens. - if (!err) err = mDNS_Register_internal(m, &sr->RR_ADV); - for (i=0; iSubTypes[i]); - if (!err) err = mDNS_Register_internal(m, &sr->RR_PTR); - - mDNS_Unlock(m); - - if (err) mDNS_DeregisterService(m, sr); - return(err); - } + const domainlabel *const name, const domainname *const type, const domainname *const domain, + const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen, + AuthRecord *SubTypes, mDNSu32 NumSubTypes, + mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context, mDNSu32 flags) +{ + mStatus err; + mDNSu32 i; + mDNSu32 hostTTL; + AuthRecType artype; + mDNSu8 recordType = (flags & coreFlagKnownUnique) ? kDNSRecordTypeKnownUnique : kDNSRecordTypeUnique; + + sr->ServiceCallback = Callback; + sr->ServiceContext = Context; + sr->Conflict = mDNSfalse; + + sr->Extras = mDNSNULL; + sr->NumSubTypes = NumSubTypes; + sr->SubTypes = SubTypes; + + artype = setAuthRecType(InterfaceID, flags); + + // Initialize the AuthRecord objects to sane values + // Need to initialize everything correctly *before* making the decision whether to do a RegisterNoSuchService and bail out + mDNS_SetupResourceRecord(&sr->RR_ADV, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeAdvisory, artype, ServiceCallback, sr); + mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, artype, ServiceCallback, sr); + + if (SameDomainName(type, (const domainname *) "\x4" "_ubd" "\x4" "_tcp")) + hostTTL = kHostNameSmallTTL; + else + hostTTL = kHostNameTTL; + + mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, hostTTL, recordType, artype, ServiceCallback, sr); + mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnique, artype, ServiceCallback, sr); + + // If port number is zero, that means the client is really trying to do a RegisterNoSuchService + if (mDNSIPPortIsZero(port)) + return(mDNS_RegisterNoSuchService(m, &sr->RR_SRV, name, type, domain, mDNSNULL, InterfaceID, NSSCallback, sr, flags)); + + // If the client is registering an oversized TXT record, + // it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it + if (sr->RR_TXT.resrec.rdata->MaxRDLength < txtlen) + sr->RR_TXT.resrec.rdata->MaxRDLength = txtlen; + + // Set up the record names + // For now we only create an advisory record for the main type, not for subtypes + // We need to gain some operational experience before we decide if there's a need to create them for subtypes too + if (ConstructServiceName(&sr->RR_ADV.namestorage, (const domainlabel*)"\x09_services", (const domainname*)"\x07_dns-sd\x04_udp", domain) == mDNSNULL) + return(mStatus_BadParamErr); + if (ConstructServiceName(&sr->RR_PTR.namestorage, mDNSNULL, type, domain) == mDNSNULL) return(mStatus_BadParamErr); + if (ConstructServiceName(&sr->RR_SRV.namestorage, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr); + AssignDomainName(&sr->RR_TXT.namestorage, sr->RR_SRV.resrec.name); + + // 1. Set up the ADV record rdata to advertise our service type + AssignDomainName(&sr->RR_ADV.resrec.rdata->u.name, sr->RR_PTR.resrec.name); + + // 2. Set up the PTR record rdata to point to our service name + // We set up two additionals, so when a client asks for this PTR we automatically send the SRV and the TXT too + // Note: uDNS registration code assumes that Additional1 points to the SRV record + AssignDomainName(&sr->RR_PTR.resrec.rdata->u.name, sr->RR_SRV.resrec.name); + sr->RR_PTR.Additional1 = &sr->RR_SRV; + sr->RR_PTR.Additional2 = &sr->RR_TXT; + + // 2a. Set up any subtype PTRs to point to our service name + // If the client is using subtypes, it is the client's responsibility to have + // already set the first label of the record name to the subtype being registered + for (i=0; iSubTypes[i].resrec.name); + st.c[1+st.c[0]] = 0; // Only want the first label, not the whole FQDN (particularly for mDNS_RenameAndReregisterService()) + AppendDomainName(&st, type); + mDNS_SetupResourceRecord(&sr->SubTypes[i], mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, artype, ServiceCallback, sr); + if (ConstructServiceName(&sr->SubTypes[i].namestorage, mDNSNULL, &st, domain) == mDNSNULL) return(mStatus_BadParamErr); + AssignDomainName(&sr->SubTypes[i].resrec.rdata->u.name, &sr->RR_SRV.namestorage); + sr->SubTypes[i].Additional1 = &sr->RR_SRV; + sr->SubTypes[i].Additional2 = &sr->RR_TXT; + } + + // 3. Set up the SRV record rdata. + sr->RR_SRV.resrec.rdata->u.srv.priority = 0; + sr->RR_SRV.resrec.rdata->u.srv.weight = 0; + sr->RR_SRV.resrec.rdata->u.srv.port = port; + + // Setting AutoTarget tells DNS that the target of this SRV is to be automatically kept in sync with our host name + if (host && host->c[0]) AssignDomainName(&sr->RR_SRV.resrec.rdata->u.srv.target, host); + else { sr->RR_SRV.AutoTarget = Target_AutoHost; sr->RR_SRV.resrec.rdata->u.srv.target.c[0] = '\0'; } + + // 4. Set up the TXT record rdata, + // and set DependentOn because we're depending on the SRV record to find and resolve conflicts for us + // Note: uDNS registration code assumes that DependentOn points to the SRV record + if (txtinfo == mDNSNULL) sr->RR_TXT.resrec.rdlength = 0; + else if (txtinfo != sr->RR_TXT.resrec.rdata->u.txt.c) + { + sr->RR_TXT.resrec.rdlength = txtlen; + if (sr->RR_TXT.resrec.rdlength > sr->RR_TXT.resrec.rdata->MaxRDLength) return(mStatus_BadParamErr); + mDNSPlatformMemCopy(sr->RR_TXT.resrec.rdata->u.txt.c, txtinfo, txtlen); + } + sr->RR_TXT.DependentOn = &sr->RR_SRV; + + mDNS_Lock(m); + // It is important that we register SRV first. uDNS assumes that SRV is registered first so + // that if the SRV cannot find a target, rest of the records that belong to this service + // will not be activated. + err = mDNS_Register_internal(m, &sr->RR_SRV); + // If we can't register the SRV record due to errors, bail out. It has not been inserted in + // any list and hence no need to deregister. We could probably do similar checks for other + // records below and bail out. For now, this seems to be sufficient to address rdar://9304275 + if (err) + { + mDNS_Unlock(m); + return err; + } + if (!err) err = mDNS_Register_internal(m, &sr->RR_TXT); + // We register the RR_PTR last, because we want to be sure that in the event of a forced call to + // mDNS_StartExit, the RR_PTR will be the last one to be forcibly deregistered, since that is what triggers + // the mStatus_MemFree callback to ServiceCallback, which in turn passes on the mStatus_MemFree back to + // the client callback, which is then at liberty to free the ServiceRecordSet memory at will. We need to + // make sure we've deregistered all our records and done any other necessary cleanup before that happens. + if (!err) err = mDNS_Register_internal(m, &sr->RR_ADV); + for (i=0; iSubTypes[i]); + if (!err) err = mDNS_Register_internal(m, &sr->RR_PTR); + + mDNS_Unlock(m); + + if (err) mDNS_DeregisterService(m, sr); + return(err); +} mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, - ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl, mDNSu32 includeP2P) - { - ExtraResourceRecord **e; - mStatus status; - AuthRecType artype; - mDNSInterfaceID InterfaceID = sr->RR_PTR.resrec.InterfaceID; - - if (InterfaceID == mDNSInterface_LocalOnly) - artype = AuthRecordLocalOnly; - if (InterfaceID == mDNSInterface_P2P) - artype = AuthRecordP2P; - else if ((InterfaceID == mDNSInterface_Any) && includeP2P) - artype = AuthRecordAnyIncludeP2P; - else - artype = AuthRecordAny; - - extra->next = mDNSNULL; - mDNS_SetupResourceRecord(&extra->r, rdata, sr->RR_PTR.resrec.InterfaceID, - extra->r.resrec.rrtype, ttl, kDNSRecordTypeUnique, artype, ServiceCallback, sr); - AssignDomainName(&extra->r.namestorage, sr->RR_SRV.resrec.name); - - mDNS_Lock(m); - e = &sr->Extras; - while (*e) e = &(*e)->next; - - if (ttl == 0) ttl = kStandardTTL; - - extra->r.DependentOn = &sr->RR_SRV; - - debugf("mDNS_AddRecordToService adding record to %##s %s %d", - extra->r.resrec.name->c, DNSTypeName(extra->r.resrec.rrtype), extra->r.resrec.rdlength); - - status = mDNS_Register_internal(m, &extra->r); - if (status == mStatus_NoError) *e = extra; - - mDNS_Unlock(m); - return(status); - } + ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl, mDNSu32 flags) +{ + ExtraResourceRecord **e; + mStatus status; + AuthRecType artype; + mDNSInterfaceID InterfaceID = sr->RR_PTR.resrec.InterfaceID; + + artype = setAuthRecType(InterfaceID, flags); + + extra->next = mDNSNULL; + mDNS_SetupResourceRecord(&extra->r, rdata, sr->RR_PTR.resrec.InterfaceID, + extra->r.resrec.rrtype, ttl, kDNSRecordTypeUnique, artype, ServiceCallback, sr); + AssignDomainName(&extra->r.namestorage, sr->RR_SRV.resrec.name); + + mDNS_Lock(m); + e = &sr->Extras; + while (*e) e = &(*e)->next; + + if (ttl == 0) ttl = kStandardTTL; + + extra->r.DependentOn = &sr->RR_SRV; + + debugf("mDNS_AddRecordToService adding record to %##s %s %d", + extra->r.resrec.name->c, DNSTypeName(extra->r.resrec.rrtype), extra->r.resrec.rdlength); + + status = mDNS_Register_internal(m, &extra->r); + if (status == mStatus_NoError) *e = extra; + + mDNS_Unlock(m); + return(status); +} mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, - mDNSRecordCallback MemFreeCallback, void *Context) - { - ExtraResourceRecord **e; - mStatus status; - - mDNS_Lock(m); - e = &sr->Extras; - while (*e && *e != extra) e = &(*e)->next; - if (!*e) - { - debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra->r.resrec.name->c); - status = mStatus_BadReferenceErr; - } - else - { - debugf("mDNS_RemoveRecordFromService removing record from %##s", extra->r.resrec.name->c); - extra->r.RecordCallback = MemFreeCallback; - extra->r.RecordContext = Context; - *e = (*e)->next; - status = mDNS_Deregister_internal(m, &extra->r, mDNS_Dereg_normal); - } - mDNS_Unlock(m); - return(status); - } + mDNSRecordCallback MemFreeCallback, void *Context) +{ + ExtraResourceRecord **e; + mStatus status; + + mDNS_Lock(m); + e = &sr->Extras; + while (*e && *e != extra) e = &(*e)->next; + if (!*e) + { + debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra->r.resrec.name->c); + status = mStatus_BadReferenceErr; + } + else + { + debugf("mDNS_RemoveRecordFromService removing record from %##s", extra->r.resrec.name->c); + extra->r.RecordCallback = MemFreeCallback; + extra->r.RecordContext = Context; + *e = (*e)->next; + status = mDNS_Deregister_internal(m, &extra->r, mDNS_Dereg_normal); + } + mDNS_Unlock(m); + return(status); +} mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname) - { - // Note: Don't need to use mDNS_Lock(m) here, because this code is just using public routines - // mDNS_RegisterService() and mDNS_AddRecordToService(), which do the right locking internally. - domainlabel name1, name2; - domainname type, domain; - const domainname *host = sr->RR_SRV.AutoTarget ? mDNSNULL : &sr->RR_SRV.resrec.rdata->u.srv.target; - ExtraResourceRecord *extras = sr->Extras; - mStatus err; - - DeconstructServiceName(sr->RR_SRV.resrec.name, &name1, &type, &domain); - if (!newname) - { - name2 = name1; - IncrementLabelSuffix(&name2, mDNStrue); - newname = &name2; - } - - if (SameDomainName(&domain, &localdomain)) - debugf("%##s service renamed from \"%#s\" to \"%#s\"", type.c, name1.c, newname->c); - else debugf("%##s service (domain %##s) renamed from \"%#s\" to \"%#s\"",type.c, domain.c, name1.c, newname->c); - - err = mDNS_RegisterService(m, sr, newname, &type, &domain, - host, sr->RR_SRV.resrec.rdata->u.srv.port, sr->RR_TXT.resrec.rdata->u.txt.c, sr->RR_TXT.resrec.rdlength, - sr->SubTypes, sr->NumSubTypes, - sr->RR_PTR.resrec.InterfaceID, sr->ServiceCallback, sr->ServiceContext, 0); - - // mDNS_RegisterService() just reset sr->Extras to NULL. - // Fortunately we already grabbed ourselves a copy of this pointer (above), so we can now run - // through the old list of extra records, and re-add them to our freshly created service registration - while (!err && extras) - { - ExtraResourceRecord *e = extras; - extras = extras->next; - err = mDNS_AddRecordToService(m, sr, e, e->r.resrec.rdata, e->r.resrec.rroriginalttl, 0); - } - - return(err); - } +{ + // Note: Don't need to use mDNS_Lock(m) here, because this code is just using public routines + // mDNS_RegisterService() and mDNS_AddRecordToService(), which do the right locking internally. + domainlabel name1, name2; + domainname type, domain; + const domainname *host = sr->RR_SRV.AutoTarget ? mDNSNULL : &sr->RR_SRV.resrec.rdata->u.srv.target; + ExtraResourceRecord *extras = sr->Extras; + mStatus err; + + DeconstructServiceName(sr->RR_SRV.resrec.name, &name1, &type, &domain); + if (!newname) + { + name2 = name1; + IncrementLabelSuffix(&name2, mDNStrue); + newname = &name2; + } + + if (SameDomainName(&domain, &localdomain)) + debugf("%##s service renamed from \"%#s\" to \"%#s\"", type.c, name1.c, newname->c); + else debugf("%##s service (domain %##s) renamed from \"%#s\" to \"%#s\"",type.c, domain.c, name1.c, newname->c); + + err = mDNS_RegisterService(m, sr, newname, &type, &domain, + host, sr->RR_SRV.resrec.rdata->u.srv.port, sr->RR_TXT.resrec.rdata->u.txt.c, sr->RR_TXT.resrec.rdlength, + sr->SubTypes, sr->NumSubTypes, + sr->RR_PTR.resrec.InterfaceID, sr->ServiceCallback, sr->ServiceContext, 0); + + // mDNS_RegisterService() just reset sr->Extras to NULL. + // Fortunately we already grabbed ourselves a copy of this pointer (above), so we can now run + // through the old list of extra records, and re-add them to our freshly created service registration + while (!err && extras) + { + ExtraResourceRecord *e = extras; + extras = extras->next; + err = mDNS_AddRecordToService(m, sr, e, e->r.resrec.rdata, e->r.resrec.rroriginalttl, 0); + } + + return(err); +} // Note: mDNS_DeregisterService calls mDNS_Deregister_internal which can call a user callback, // which may change the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. mDNSexport mStatus mDNS_DeregisterService_drt(mDNS *const m, ServiceRecordSet *sr, mDNS_Dereg_type drt) - { - // If port number is zero, that means this was actually registered using mDNS_RegisterNoSuchService() - if (mDNSIPPortIsZero(sr->RR_SRV.resrec.rdata->u.srv.port)) return(mDNS_DeregisterNoSuchService(m, &sr->RR_SRV)); - - if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeUnregistered) - { - debugf("Service set for %##s already deregistered", sr->RR_SRV.resrec.name->c); - return(mStatus_BadReferenceErr); - } - else if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeDeregistering) - { - LogInfo("Service set for %##s already in the process of deregistering", sr->RR_SRV.resrec.name->c); - // Avoid race condition: - // If a service gets a conflict, then we set the Conflict flag to tell us to generate - // an mStatus_NameConflict message when we get the mStatus_MemFree for our PTR record. - // If the client happens to deregister the service in the middle of that process, then - // we clear the flag back to the normal state, so that we deliver a plain mStatus_MemFree - // instead of incorrectly promoting it to mStatus_NameConflict. - // This race condition is exposed particularly when the conformance test generates - // a whole batch of simultaneous conflicts across a range of services all advertised - // using the same system default name, and if we don't take this precaution then - // we end up incrementing m->nicelabel multiple times instead of just once. - // Bug when auto-renaming Computer Name after name collision - sr->Conflict = mDNSfalse; - return(mStatus_NoError); - } - else - { - mDNSu32 i; - mStatus status; - ExtraResourceRecord *e; - mDNS_Lock(m); - e = sr->Extras; - - // We use mDNS_Dereg_repeat because, in the event of a collision, some or all of the - // SRV, TXT, or Extra records could have already been automatically deregistered, and that's okay - mDNS_Deregister_internal(m, &sr->RR_SRV, mDNS_Dereg_repeat); - mDNS_Deregister_internal(m, &sr->RR_TXT, mDNS_Dereg_repeat); - - mDNS_Deregister_internal(m, &sr->RR_ADV, drt); - - // We deregister all of the extra records, but we leave the sr->Extras list intact - // in case the client wants to do a RenameAndReregister and reinstate the registration - while (e) - { - mDNS_Deregister_internal(m, &e->r, mDNS_Dereg_repeat); - e = e->next; - } - - for (i=0; iNumSubTypes; i++) - mDNS_Deregister_internal(m, &sr->SubTypes[i], drt); - - status = mDNS_Deregister_internal(m, &sr->RR_PTR, drt); - mDNS_Unlock(m); - return(status); - } - } +{ + // If port number is zero, that means this was actually registered using mDNS_RegisterNoSuchService() + if (mDNSIPPortIsZero(sr->RR_SRV.resrec.rdata->u.srv.port)) return(mDNS_DeregisterNoSuchService(m, &sr->RR_SRV)); + + if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeUnregistered) + { + debugf("Service set for %##s already deregistered", sr->RR_SRV.resrec.name->c); + return(mStatus_BadReferenceErr); + } + else if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeDeregistering) + { + LogInfo("Service set for %##s already in the process of deregistering", sr->RR_SRV.resrec.name->c); + // Avoid race condition: + // If a service gets a conflict, then we set the Conflict flag to tell us to generate + // an mStatus_NameConflict message when we get the mStatus_MemFree for our PTR record. + // If the client happens to deregister the service in the middle of that process, then + // we clear the flag back to the normal state, so that we deliver a plain mStatus_MemFree + // instead of incorrectly promoting it to mStatus_NameConflict. + // This race condition is exposed particularly when the conformance test generates + // a whole batch of simultaneous conflicts across a range of services all advertised + // using the same system default name, and if we don't take this precaution then + // we end up incrementing m->nicelabel multiple times instead of just once. + // Bug when auto-renaming Computer Name after name collision + sr->Conflict = mDNSfalse; + return(mStatus_NoError); + } + else + { + mDNSu32 i; + mStatus status; + ExtraResourceRecord *e; + mDNS_Lock(m); + e = sr->Extras; + + // We use mDNS_Dereg_repeat because, in the event of a collision, some or all of the + // SRV, TXT, or Extra records could have already been automatically deregistered, and that's okay + mDNS_Deregister_internal(m, &sr->RR_SRV, mDNS_Dereg_repeat); + mDNS_Deregister_internal(m, &sr->RR_TXT, mDNS_Dereg_repeat); + + mDNS_Deregister_internal(m, &sr->RR_ADV, drt); + + // We deregister all of the extra records, but we leave the sr->Extras list intact + // in case the client wants to do a RenameAndReregister and reinstate the registration + while (e) + { + mDNS_Deregister_internal(m, &e->r, mDNS_Dereg_repeat); + e = e->next; + } + + for (i=0; iNumSubTypes; i++) + mDNS_Deregister_internal(m, &sr->SubTypes[i], drt); + + status = mDNS_Deregister_internal(m, &sr->RR_PTR, drt); + mDNS_Unlock(m); + return(status); + } +} // Create a registration that asserts that no such service exists with this name. // This can be useful where there is a given function is available through several protocols. @@ -10131,77 +11297,70 @@ mDNSexport mStatus mDNS_DeregisterService_drt(mDNS *const m, ServiceRecordSet *s // "LPR" service called "Stuart's Printer". Without this precaution, another printer than offers only "LPR" printing // could inadvertently advertise its service under the same name "Stuart's Printer", which might be confusing for users. mDNSexport mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const rr, - const domainlabel *const name, const domainname *const type, const domainname *const domain, - const domainname *const host, - const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context, mDNSBool includeP2P) - { - AuthRecType artype; - - if (InterfaceID == mDNSInterface_LocalOnly) - artype = AuthRecordLocalOnly; - else if (InterfaceID == mDNSInterface_P2P) - artype = AuthRecordP2P; - else if ((InterfaceID == mDNSInterface_Any) && includeP2P) - artype = AuthRecordAnyIncludeP2P; - else - artype = AuthRecordAny; - - mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, artype, Callback, Context); - if (ConstructServiceName(&rr->namestorage, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr); - rr->resrec.rdata->u.srv.priority = 0; - rr->resrec.rdata->u.srv.weight = 0; - rr->resrec.rdata->u.srv.port = zeroIPPort; - if (host && host->c[0]) AssignDomainName(&rr->resrec.rdata->u.srv.target, host); - else rr->AutoTarget = Target_AutoHost; - return(mDNS_Register(m, rr)); - } + const domainlabel *const name, const domainname *const type, const domainname *const domain, + const domainname *const host, + const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context, mDNSu32 flags) +{ + AuthRecType artype; + + artype = setAuthRecType(InterfaceID, flags); + + mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, artype, Callback, Context); + if (ConstructServiceName(&rr->namestorage, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr); + rr->resrec.rdata->u.srv.priority = 0; + rr->resrec.rdata->u.srv.weight = 0; + rr->resrec.rdata->u.srv.port = zeroIPPort; + if (host && host->c[0]) AssignDomainName(&rr->resrec.rdata->u.srv.target, host); + else rr->AutoTarget = Target_AutoHost; + return(mDNS_Register(m, rr)); +} mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, - mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname) - { - AuthRecType artype; - - if (InterfaceID == mDNSInterface_LocalOnly) - artype = AuthRecordLocalOnly; - else if (InterfaceID == mDNSInterface_P2P) - artype = AuthRecordP2P; - else - artype = AuthRecordAny; - mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, artype, mDNSNULL, mDNSNULL); - if (!MakeDomainNameFromDNSNameString(&rr->namestorage, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr); - if (!MakeDomainNameFromDNSNameString(&rr->resrec.rdata->u.name, domname)) return(mStatus_BadParamErr); - return(mDNS_Register(m, rr)); - } - + mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname) +{ + AuthRecType artype; + + if (InterfaceID == mDNSInterface_LocalOnly) + artype = AuthRecordLocalOnly; + else if (InterfaceID == mDNSInterface_P2P) + artype = AuthRecordP2P; + else + artype = AuthRecordAny; + mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, artype, mDNSNULL, mDNSNULL); + if (!MakeDomainNameFromDNSNameString(&rr->namestorage, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr); + if (!MakeDomainNameFromDNSNameString(&rr->resrec.rdata->u.name, domname)) return(mStatus_BadParamErr); + return(mDNS_Register(m, rr)); +} + mDNSlocal mDNSBool mDNS_IdUsedInResourceRecordsList(mDNS * const m, mDNSOpaque16 id) - { - AuthRecord *r; - for (r = m->ResourceRecords; r; r=r->next) if (mDNSSameOpaque16(id, r->updateid)) return mDNStrue; - return mDNSfalse; - } - +{ + AuthRecord *r; + for (r = m->ResourceRecords; r; r=r->next) if (mDNSSameOpaque16(id, r->updateid)) return mDNStrue; + return mDNSfalse; +} + mDNSlocal mDNSBool mDNS_IdUsedInQuestionsList(mDNS * const m, mDNSOpaque16 id) - { - DNSQuestion *q; - for (q = m->Questions; q; q=q->next) if (mDNSSameOpaque16(id, q->TargetQID)) return mDNStrue; - return mDNSfalse; - } - +{ + DNSQuestion *q; + for (q = m->Questions; q; q=q->next) if (mDNSSameOpaque16(id, q->TargetQID)) return mDNStrue; + return mDNSfalse; +} + mDNSexport mDNSOpaque16 mDNS_NewMessageID(mDNS * const m) - { - mDNSOpaque16 id; - int i; +{ + mDNSOpaque16 id; + int i; - for (i=0; i<10; i++) - { - id = mDNSOpaque16fromIntVal(1 + (mDNSu16)mDNSRandom(0xFFFE)); - if (!mDNS_IdUsedInResourceRecordsList(m, id) && !mDNS_IdUsedInQuestionsList(m, id)) break; - } - - debugf("mDNS_NewMessageID: %5d", mDNSVal16(id)); + for (i=0; i<10; i++) + { + id = mDNSOpaque16fromIntVal(1 + (mDNSu16)mDNSRandom(0xFFFE)); + if (!mDNS_IdUsedInResourceRecordsList(m, id) && !mDNS_IdUsedInQuestionsList(m, id)) break; + } + + debugf("mDNS_NewMessageID: %5d", mDNSVal16(id)); - return id; - } + return id; +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -10210,431 +11369,485 @@ mDNSexport mDNSOpaque16 mDNS_NewMessageID(mDNS * const m) #endif mDNSlocal void RestartARPProbing(mDNS *const m, AuthRecord *const rr) - { - // If we see an ARP from a machine we think is sleeping, then either - // (i) the machine has woken, or - // (ii) it's just a stray old packet from before the machine slept - // To handle the second case, we reset ProbeCount, so we'll suppress our own answers for a while, to avoid - // generating ARP conflicts with a waking machine, and set rr->LastAPTime so we'll start probing again in 10 seconds. - // If the machine has just woken then we'll discard our records when we see the first new mDNS probe from that machine. - // If it was a stray old packet, then after 10 seconds we'll probe again and then start answering ARPs again. In this case we *do* - // need to send new ARP Announcements, because the owner's ARP broadcasts will have updated neighboring ARP caches, so we need to - // re-assert our (temporary) ownership of that IP address in order to receive subsequent packets addressed to that IPv4 address. - - rr->resrec.RecordType = kDNSRecordTypeUnique; - rr->ProbeCount = DefaultProbeCountForTypeUnique; - - // If we haven't started announcing yet (and we're not already in ten-second-delay mode) the machine is probably - // still going to sleep, so we just reset rr->ProbeCount so we'll continue probing until it stops responding. - // If we *have* started announcing, the machine is probably in the process of waking back up, so in that case - // we're more cautious and we wait ten seconds before probing it again. We do this because while waking from - // sleep, some network interfaces tend to lose or delay inbound packets, and without this delay, if the waking machine - // didn't answer our three probes within three seconds then we'd announce and cause it an unnecessary address conflict. - if (rr->AnnounceCount == InitialAnnounceCount && m->timenow - rr->LastAPTime >= 0) - InitializeLastAPTime(m, rr); - else - { - rr->AnnounceCount = InitialAnnounceCount; - rr->ThisAPInterval = mDNSPlatformOneSecond; - rr->LastAPTime = m->timenow + mDNSPlatformOneSecond * 9; // Send first packet at rr->LastAPTime + rr->ThisAPInterval, i.e. 10 seconds from now - SetNextAnnounceProbeTime(m, rr); - } - } +{ + // If we see an ARP from a machine we think is sleeping, then either + // (i) the machine has woken, or + // (ii) it's just a stray old packet from before the machine slept + // To handle the second case, we reset ProbeCount, so we'll suppress our own answers for a while, to avoid + // generating ARP conflicts with a waking machine, and set rr->LastAPTime so we'll start probing again in 10 seconds. + // If the machine has just woken then we'll discard our records when we see the first new mDNS probe from that machine. + // If it was a stray old packet, then after 10 seconds we'll probe again and then start answering ARPs again. In this case we *do* + // need to send new ARP Announcements, because the owner's ARP broadcasts will have updated neighboring ARP caches, so we need to + // re-assert our (temporary) ownership of that IP address in order to receive subsequent packets addressed to that IPv4 address. + + rr->resrec.RecordType = kDNSRecordTypeUnique; + rr->ProbeCount = DefaultProbeCountForTypeUnique; + + // If we haven't started announcing yet (and we're not already in ten-second-delay mode) the machine is probably + // still going to sleep, so we just reset rr->ProbeCount so we'll continue probing until it stops responding. + // If we *have* started announcing, the machine is probably in the process of waking back up, so in that case + // we're more cautious and we wait ten seconds before probing it again. We do this because while waking from + // sleep, some network interfaces tend to lose or delay inbound packets, and without this delay, if the waking machine + // didn't answer our three probes within three seconds then we'd announce and cause it an unnecessary address conflict. + if (rr->AnnounceCount == InitialAnnounceCount && m->timenow - rr->LastAPTime >= 0) + InitializeLastAPTime(m, rr); + else + { + rr->AnnounceCount = InitialAnnounceCount; + rr->ThisAPInterval = mDNSPlatformOneSecond; + rr->LastAPTime = m->timenow + mDNSPlatformOneSecond * 9; // Send first packet at rr->LastAPTime + rr->ThisAPInterval, i.e. 10 seconds from now + SetNextAnnounceProbeTime(m, rr); + } +} mDNSlocal void mDNSCoreReceiveRawARP(mDNS *const m, const ARP_EthIP *const arp, const mDNSInterfaceID InterfaceID) - { - static const mDNSOpaque16 ARP_op_request = { { 0, 1 } }; - AuthRecord *rr; - NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); - if (!intf) return; - - mDNS_Lock(m); - - // Pass 1: - // Process ARP Requests and Probes (but not Announcements), and generate an ARP Reply if necessary. - // We also process ARPs from our own kernel (and 'answer' them by injecting a local ARP table entry) - // We ignore ARP Announcements here -- Announcements are not questions, they're assertions, so we don't need to answer them. - // The times we might need to react to an ARP Announcement are: - // (i) as an indication that the host in question has not gone to sleep yet (so we should delay beginning to proxy for it) or - // (ii) if it's a conflicting Announcement from another host - // -- and we check for these in Pass 2 below. - if (mDNSSameOpaque16(arp->op, ARP_op_request) && !mDNSSameIPv4Address(arp->spa, arp->tpa)) - { - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && - rr->AddressProxy.type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->AddressProxy.ip.v4, arp->tpa)) - { - static const char msg1[] = "ARP Req from owner -- re-probing"; - static const char msg2[] = "Ignoring ARP Request from "; - static const char msg3[] = "Creating Local ARP Cache entry "; - static const char msg4[] = "Answering ARP Request from "; - const char *const msg = mDNSSameEthAddress(&arp->sha, &rr->WakeUp.IMAC) ? msg1 : - (rr->AnnounceCount == InitialAnnounceCount) ? msg2 : - mDNSSameEthAddress(&arp->sha, &intf->MAC) ? msg3 : msg4; - LogSPS("%-7s %s %.6a %.4a for %.4a -- H-MAC %.6a I-MAC %.6a %s", - intf->ifname, msg, &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); - if (msg == msg1) RestartARPProbing(m, rr); - else if (msg == msg3) mDNSPlatformSetLocalAddressCacheEntry(m, &rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID); - else if (msg == msg4) SendARP(m, 2, rr, &arp->tpa, &arp->sha, &arp->spa, &arp->sha); - } - } - - // Pass 2: - // For all types of ARP packet we check the Sender IP address to make sure it doesn't conflict with any AddressProxy record we're holding. - // (Strictly speaking we're only checking Announcement/Request/Reply packets, since ARP Probes have zero Sender IP address, - // so by definition (and by design) they can never conflict with any real (i.e. non-zero) IP address). - // We ignore ARPs we sent ourselves (Sender MAC address is our MAC address) because our own proxy ARPs do not constitute a conflict that we need to handle. - // If we see an apparently conflicting ARP, we check the sender hardware address: - // If the sender hardware address is the original owner this is benign, so we just suppress our own proxy answering for a while longer. - // If the sender hardware address is *not* the original owner, then this is a conflict, and we need to wake the sleeping machine to handle it. - if (mDNSSameEthAddress(&arp->sha, &intf->MAC)) - debugf("ARP from self for %.4a", &arp->tpa); - else - { - if (!mDNSSameIPv4Address(arp->spa, zerov4Addr)) - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && - rr->AddressProxy.type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->AddressProxy.ip.v4, arp->spa)) - { - RestartARPProbing(m, rr); - if (mDNSSameEthAddress(&arp->sha, &rr->WakeUp.IMAC)) - LogSPS("%-7s ARP %s from owner %.6a %.4a for %-15.4a -- re-starting probing for %s", intf->ifname, - mDNSSameIPv4Address(arp->spa, arp->tpa) ? "Announcement " : mDNSSameOpaque16(arp->op, ARP_op_request) ? "Request " : "Response ", - &arp->sha, &arp->spa, &arp->tpa, ARDisplayString(m, rr)); - else - { - LogMsg("%-7s Conflicting ARP from %.6a %.4a for %.4a -- waking H-MAC %.6a I-MAC %.6a %s", intf->ifname, - &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); - ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC); - } - } - } - - mDNS_Unlock(m); - } +{ + static const mDNSOpaque16 ARP_op_request = { { 0, 1 } }; + AuthRecord *rr; + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); + if (!intf) return; + + mDNS_Lock(m); + + // Pass 1: + // Process ARP Requests and Probes (but not Announcements), and generate an ARP Reply if necessary. + // We also process ARPs from our own kernel (and 'answer' them by injecting a local ARP table entry) + // We ignore ARP Announcements here -- Announcements are not questions, they're assertions, so we don't need to answer them. + // The times we might need to react to an ARP Announcement are: + // (i) as an indication that the host in question has not gone to sleep yet (so we should delay beginning to proxy for it) or + // (ii) if it's a conflicting Announcement from another host + // -- and we check for these in Pass 2 below. + if (mDNSSameOpaque16(arp->op, ARP_op_request) && !mDNSSameIPv4Address(arp->spa, arp->tpa)) + { + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && + rr->AddressProxy.type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->AddressProxy.ip.v4, arp->tpa)) + { + static const char msg1[] = "ARP Req from owner -- re-probing"; + static const char msg2[] = "Ignoring ARP Request from "; + static const char msg3[] = "Creating Local ARP Cache entry "; + static const char msg4[] = "Answering ARP Request from "; + const char *const msg = mDNSSameEthAddress(&arp->sha, &rr->WakeUp.IMAC) ? msg1 : + (rr->AnnounceCount == InitialAnnounceCount) ? msg2 : + mDNSSameEthAddress(&arp->sha, &intf->MAC) ? msg3 : msg4; + LogSPS("%-7s %s %.6a %.4a for %.4a -- H-MAC %.6a I-MAC %.6a %s", + intf->ifname, msg, &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + if (msg == msg1) RestartARPProbing(m, rr); + else if (msg == msg3) mDNSPlatformSetLocalAddressCacheEntry(m, &rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID); + else if (msg == msg4) SendARP(m, 2, rr, &arp->tpa, &arp->sha, &arp->spa, &arp->sha); + } + } + + // Pass 2: + // For all types of ARP packet we check the Sender IP address to make sure it doesn't conflict with any AddressProxy record we're holding. + // (Strictly speaking we're only checking Announcement/Request/Reply packets, since ARP Probes have zero Sender IP address, + // so by definition (and by design) they can never conflict with any real (i.e. non-zero) IP address). + // We ignore ARPs we sent ourselves (Sender MAC address is our MAC address) because our own proxy ARPs do not constitute a conflict that we need to handle. + // If we see an apparently conflicting ARP, we check the sender hardware address: + // If the sender hardware address is the original owner this is benign, so we just suppress our own proxy answering for a while longer. + // If the sender hardware address is *not* the original owner, then this is a conflict, and we need to wake the sleeping machine to handle it. + if (mDNSSameEthAddress(&arp->sha, &intf->MAC)) + debugf("ARP from self for %.4a", &arp->tpa); + else + { + if (!mDNSSameIPv4Address(arp->spa, zerov4Addr)) + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && + rr->AddressProxy.type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->AddressProxy.ip.v4, arp->spa)) + { + RestartARPProbing(m, rr); + if (mDNSSameEthAddress(&arp->sha, &rr->WakeUp.IMAC)) + LogSPS("%-7s ARP %s from owner %.6a %.4a for %-15.4a -- re-starting probing for %s", intf->ifname, + mDNSSameIPv4Address(arp->spa, arp->tpa) ? "Announcement " : mDNSSameOpaque16(arp->op, ARP_op_request) ? "Request " : "Response ", + &arp->sha, &arp->spa, &arp->tpa, ARDisplayString(m, rr)); + else + { + LogMsg("%-7s Conflicting ARP from %.6a %.4a for %.4a -- waking H-MAC %.6a I-MAC %.6a %s", intf->ifname, + &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC); + } + } + } + + mDNS_Unlock(m); +} /* -// Option 1 is Source Link Layer Address Option -// Option 2 is Target Link Layer Address Option -mDNSlocal const mDNSEthAddr *GetLinkLayerAddressOption(const IPv6NDP *const ndp, const mDNSu8 *const end, mDNSu8 op) - { - const mDNSu8 *options = (mDNSu8 *)(ndp+1); - while (options < end) - { - debugf("NDP Option %02X len %2d %d", options[0], options[1], end - options); - if (options[0] == op && options[1] == 1) return (const mDNSEthAddr*)(options+2); - options += options[1] * 8; - } - return mDNSNULL; - } -*/ + // Option 1 is Source Link Layer Address Option + // Option 2 is Target Link Layer Address Option + mDNSlocal const mDNSEthAddr *GetLinkLayerAddressOption(const IPv6NDP *const ndp, const mDNSu8 *const end, mDNSu8 op) + { + const mDNSu8 *options = (mDNSu8 *)(ndp+1); + while (options < end) + { + debugf("NDP Option %02X len %2d %d", options[0], options[1], end - options); + if (options[0] == op && options[1] == 1) return (const mDNSEthAddr*)(options+2); + options += options[1] * 8; + } + return mDNSNULL; + } + */ mDNSlocal void mDNSCoreReceiveRawND(mDNS *const m, const mDNSEthAddr *const sha, const mDNSv6Addr *spa, - const IPv6NDP *const ndp, const mDNSu8 *const end, const mDNSInterfaceID InterfaceID) - { - AuthRecord *rr; - NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); - if (!intf) return; - - mDNS_Lock(m); - - // Pass 1: Process Neighbor Solicitations, and generate a Neighbor Advertisement if necessary. - if (ndp->type == NDP_Sol) - { - //const mDNSEthAddr *const sha = GetLinkLayerAddressOption(ndp, end, NDP_SrcLL); - (void)end; - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && - rr->AddressProxy.type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->AddressProxy.ip.v6, ndp->target)) - { - static const char msg1[] = "NDP Req from owner -- re-probing"; - static const char msg2[] = "Ignoring NDP Request from "; - static const char msg3[] = "Creating Local NDP Cache entry "; - static const char msg4[] = "Answering NDP Request from "; - static const char msg5[] = "Answering NDP Probe from "; - const char *const msg = sha && mDNSSameEthAddress(sha, &rr->WakeUp.IMAC) ? msg1 : - (rr->AnnounceCount == InitialAnnounceCount) ? msg2 : - sha && mDNSSameEthAddress(sha, &intf->MAC) ? msg3 : - spa && mDNSIPv6AddressIsZero(*spa) ? msg4 : msg5; - LogSPS("%-7s %s %.6a %.16a for %.16a -- H-MAC %.6a I-MAC %.6a %s", - intf->ifname, msg, sha, spa, &ndp->target, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); - if (msg == msg1) RestartARPProbing(m, rr); - else if (msg == msg3) - { - if (!(m->KnownBugs & mDNS_KnownBug_LimitedIPv6)) - mDNSPlatformSetLocalAddressCacheEntry(m, &rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID); - } - else if (msg == msg4) SendNDP(m, NDP_Adv, NDP_Solicited, rr, &ndp->target, mDNSNULL, spa, sha ); - else if (msg == msg5) SendNDP(m, NDP_Adv, 0, rr, &ndp->target, mDNSNULL, &AllHosts_v6, &AllHosts_v6_Eth); - } - } - - // Pass 2: For all types of NDP packet we check the Sender IP address to make sure it doesn't conflict with any AddressProxy record we're holding. - if (mDNSSameEthAddress(sha, &intf->MAC)) - debugf("NDP from self for %.16a", &ndp->target); - else - { - // For Neighbor Advertisements we check the Target address field, not the actual IPv6 source address. - // When a machine has both link-local and routable IPv6 addresses, it may send NDP packets making assertions - // about its routable IPv6 address, using its link-local address as the source address for all NDP packets. - // Hence it is the NDP target address we care about, not the actual packet source address. - if (ndp->type == NDP_Adv) spa = &ndp->target; - if (!mDNSSameIPv6Address(*spa, zerov6Addr)) - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && - rr->AddressProxy.type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->AddressProxy.ip.v6, *spa)) - { - RestartARPProbing(m, rr); - if (mDNSSameEthAddress(sha, &rr->WakeUp.IMAC)) - LogSPS("%-7s NDP %s from owner %.6a %.16a for %.16a -- re-starting probing for %s", intf->ifname, - ndp->type == NDP_Sol ? "Solicitation " : "Advertisement", sha, spa, &ndp->target, ARDisplayString(m, rr)); - else - { - LogMsg("%-7s Conflicting NDP from %.6a %.16a for %.16a -- waking H-MAC %.6a I-MAC %.6a %s", intf->ifname, - sha, spa, &ndp->target, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); - ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC); - } - } - } - - mDNS_Unlock(m); - } + const IPv6NDP *const ndp, const mDNSu8 *const end, const mDNSInterfaceID InterfaceID) +{ + AuthRecord *rr; + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); + if (!intf) return; + + mDNS_Lock(m); + + // Pass 1: Process Neighbor Solicitations, and generate a Neighbor Advertisement if necessary. + if (ndp->type == NDP_Sol) + { + //const mDNSEthAddr *const sha = GetLinkLayerAddressOption(ndp, end, NDP_SrcLL); + (void)end; + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && + rr->AddressProxy.type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->AddressProxy.ip.v6, ndp->target)) + { + static const char msg1[] = "NDP Req from owner -- re-probing"; + static const char msg2[] = "Ignoring NDP Request from "; + static const char msg3[] = "Creating Local NDP Cache entry "; + static const char msg4[] = "Answering NDP Request from "; + static const char msg5[] = "Answering NDP Probe from "; + const char *const msg = sha && mDNSSameEthAddress(sha, &rr->WakeUp.IMAC) ? msg1 : + (rr->AnnounceCount == InitialAnnounceCount) ? msg2 : + sha && mDNSSameEthAddress(sha, &intf->MAC) ? msg3 : + spa && mDNSIPv6AddressIsZero(*spa) ? msg4 : msg5; + LogSPS("%-7s %s %.6a %.16a for %.16a -- H-MAC %.6a I-MAC %.6a %s", + intf->ifname, msg, sha, spa, &ndp->target, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + if (msg == msg1) RestartARPProbing(m, rr); + else if (msg == msg3) + { + if (!(m->KnownBugs & mDNS_KnownBug_LimitedIPv6)) + mDNSPlatformSetLocalAddressCacheEntry(m, &rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID); + } + else if (msg == msg4) SendNDP(m, NDP_Adv, NDP_Solicited, rr, &ndp->target, mDNSNULL, spa, sha ); + else if (msg == msg5) SendNDP(m, NDP_Adv, 0, rr, &ndp->target, mDNSNULL, &AllHosts_v6, &AllHosts_v6_Eth); + } + } + + // Pass 2: For all types of NDP packet we check the Sender IP address to make sure it doesn't conflict with any AddressProxy record we're holding. + if (mDNSSameEthAddress(sha, &intf->MAC)) + debugf("NDP from self for %.16a", &ndp->target); + else + { + // For Neighbor Advertisements we check the Target address field, not the actual IPv6 source address. + // When a machine has both link-local and routable IPv6 addresses, it may send NDP packets making assertions + // about its routable IPv6 address, using its link-local address as the source address for all NDP packets. + // Hence it is the NDP target address we care about, not the actual packet source address. + if (ndp->type == NDP_Adv) spa = &ndp->target; + if (!mDNSSameIPv6Address(*spa, zerov6Addr)) + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && + rr->AddressProxy.type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->AddressProxy.ip.v6, *spa)) + { + RestartARPProbing(m, rr); + if (mDNSSameEthAddress(sha, &rr->WakeUp.IMAC)) + LogSPS("%-7s NDP %s from owner %.6a %.16a for %.16a -- re-starting probing for %s", intf->ifname, + ndp->type == NDP_Sol ? "Solicitation " : "Advertisement", sha, spa, &ndp->target, ARDisplayString(m, rr)); + else + { + LogMsg("%-7s Conflicting NDP from %.6a %.16a for %.16a -- waking H-MAC %.6a I-MAC %.6a %s", intf->ifname, + sha, spa, &ndp->target, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC); + } + } + } + + mDNS_Unlock(m); +} mDNSlocal void mDNSCoreReceiveRawTransportPacket(mDNS *const m, const mDNSEthAddr *const sha, const mDNSAddr *const src, const mDNSAddr *const dst, const mDNSu8 protocol, - const mDNSu8 *const p, const TransportLayerPacket *const t, const mDNSu8 *const end, const mDNSInterfaceID InterfaceID, const mDNSu16 len) - { - const mDNSIPPort port = (protocol == 0x06) ? t->tcp.dst : (protocol == 0x11) ? t->udp.dst : zeroIPPort; - mDNSBool wake = mDNSfalse; - - switch (protocol) - { - #define XX wake ? "Received" : "Ignoring", end-p - case 0x01: LogSPS("Ignoring %d-byte ICMP from %#a to %#a", end-p, src, dst); - break; - - case 0x06: { - #define SSH_AsNumber 22 - static const mDNSIPPort SSH = { { SSH_AsNumber >> 8, SSH_AsNumber & 0xFF } }; - - // Plan to wake if - // (a) RST is not set, AND - // (b) packet is SYN, SYN+FIN, or plain data packet (no SYN or FIN). We won't wake for FIN alone. - wake = (!(t->tcp.flags & 4) && (t->tcp.flags & 3) != 1); - - // For now, to reduce spurious wakeups, we wake only for TCP SYN, - // except for ssh connections, where we'll wake for plain data packets too - if (!mDNSSameIPPort(port, SSH) && !(t->tcp.flags & 2)) wake = mDNSfalse; - - LogSPS("%s %d-byte TCP from %#a:%d to %#a:%d%s%s%s", XX, - src, mDNSVal16(t->tcp.src), dst, mDNSVal16(port), - (t->tcp.flags & 2) ? " SYN" : "", - (t->tcp.flags & 1) ? " FIN" : "", - (t->tcp.flags & 4) ? " RST" : ""); - } - break; - - case 0x11: { - #define ARD_AsNumber 3283 - static const mDNSIPPort ARD = { { ARD_AsNumber >> 8, ARD_AsNumber & 0xFF } }; - const mDNSu16 udplen = (mDNSu16)((mDNSu16)t->bytes[4] << 8 | t->bytes[5]); // Length *including* 8-byte UDP header - if (udplen >= sizeof(UDPHeader)) - { - const mDNSu16 datalen = udplen - sizeof(UDPHeader); - wake = mDNStrue; - - // For Back to My Mac UDP port 4500 (IPSEC) packets, we do some special handling - if (mDNSSameIPPort(port, IPSECPort)) - { - // Specifically ignore NAT keepalive packets - if (datalen == 1 && end >= &t->bytes[9] && t->bytes[8] == 0xFF) wake = mDNSfalse; - else - { - // Skip over the Non-ESP Marker if present - const mDNSBool NonESP = (end >= &t->bytes[12] && t->bytes[8] == 0 && t->bytes[9] == 0 && t->bytes[10] == 0 && t->bytes[11] == 0); - const IKEHeader *const ike = (IKEHeader *)(t + (NonESP ? 12 : 8)); - const mDNSu16 ikelen = datalen - (NonESP ? 4 : 0); - if (ikelen >= sizeof(IKEHeader) && end >= ((mDNSu8 *)ike) + sizeof(IKEHeader)) - if ((ike->Version & 0x10) == 0x10) - { - // ExchangeType == 5 means 'Informational' - // ExchangeType == 34 means 'IKE_SA_INIT' - if (ike->ExchangeType == 5 || ike->ExchangeType == 34) wake = mDNSfalse; - LogSPS("%s %d-byte IKE ExchangeType %d", XX, ike->ExchangeType); - } - } - } - - // For now, because we haven't yet worked out a clean elegant way to do this, we just special-case the - // Apple Remote Desktop port number -- we ignore all packets to UDP 3283 (the "Net Assistant" port), - // except for Apple Remote Desktop's explicit manual wakeup packet, which looks like this: - // UDP header (8 bytes) - // Payload: 13 88 00 6a 41 4e 41 20 (8 bytes) ffffffffffff (6 bytes) 16xMAC (96 bytes) = 110 bytes total - if (mDNSSameIPPort(port, ARD)) wake = (datalen >= 110 && end >= &t->bytes[10] && t->bytes[8] == 0x13 && t->bytes[9] == 0x88); - - LogSPS("%s %d-byte UDP from %#a:%d to %#a:%d", XX, src, mDNSVal16(t->udp.src), dst, mDNSVal16(port)); - } - } - break; - - case 0x3A: if (&t->bytes[len] <= end) - { - mDNSu16 checksum = IPv6CheckSum(&src->ip.v6, &dst->ip.v6, protocol, t->bytes, len); - if (!checksum) mDNSCoreReceiveRawND(m, sha, &src->ip.v6, &t->ndp, &t->bytes[len], InterfaceID); - else LogInfo("IPv6CheckSum bad %04X %02X%02X from %#a to %#a", checksum, t->bytes[2], t->bytes[3], src, dst); - } - break; - - default: LogSPS("Ignoring %d-byte IP packet unknown protocol %d from %#a to %#a", end-p, protocol, src, dst); - break; - } - - if (wake) - { - AuthRecord *rr, *r2; - - mDNS_Lock(m); - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.InterfaceID == InterfaceID && - rr->resrec.RecordType != kDNSRecordTypeDeregistering && - rr->AddressProxy.type && mDNSSameAddress(&rr->AddressProxy, dst)) - { - const mDNSu8 *const tp = (protocol == 6) ? (const mDNSu8 *)"\x4_tcp" : (const mDNSu8 *)"\x4_udp"; - for (r2 = m->ResourceRecords; r2; r2=r2->next) - if (r2->resrec.InterfaceID == InterfaceID && mDNSSameEthAddress(&r2->WakeUp.HMAC, &rr->WakeUp.HMAC) && - r2->resrec.RecordType != kDNSRecordTypeDeregistering && - r2->resrec.rrtype == kDNSType_SRV && mDNSSameIPPort(r2->resrec.rdata->u.srv.port, port) && - SameDomainLabel(ThirdLabel(r2->resrec.name)->c, tp)) - break; - if (!r2 && mDNSSameIPPort(port, IPSECPort)) r2 = rr; // So that we wake for BTMM IPSEC packets, even without a matching SRV record - if (r2) - { - LogMsg("Waking host at %s %#a H-MAC %.6a I-MAC %.6a for %s", - InterfaceNameForID(m, rr->resrec.InterfaceID), dst, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, r2)); - ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC); - } - else - LogSPS("Sleeping host at %s %#a %.6a has no service on %#s %d", - InterfaceNameForID(m, rr->resrec.InterfaceID), dst, &rr->WakeUp.HMAC, tp, mDNSVal16(port)); - } - mDNS_Unlock(m); - } - } + const mDNSu8 *const p, const TransportLayerPacket *const t, const mDNSu8 *const end, const mDNSInterfaceID InterfaceID, const mDNSu16 len) +{ + const mDNSIPPort port = (protocol == 0x06) ? t->tcp.dst : (protocol == 0x11) ? t->udp.dst : zeroIPPort; + mDNSBool wake = mDNSfalse; + mDNSBool kaWake = mDNSfalse; + + switch (protocol) + { + #define XX wake ? "Received" : "Ignoring", end-p + case 0x01: LogSPS("Ignoring %d-byte ICMP from %#a to %#a", end-p, src, dst); + break; + + case 0x06: { + AuthRecord *kr; + mDNSu32 seq, ack; + #define TH_FIN 0x01 + #define TH_SYN 0x02 + #define TH_RST 0x04 + + kr = mDNS_MatchKeepaliveInfo(m, dst, src, port, t->tcp.src, &seq, &ack); + if (kr) + { + LogSPS("mDNSCoreReceiveRawTransportPacket: Found a Keepalive record from %#a:%d to %#a:%d", src, mDNSVal16(t->tcp.src), dst, mDNSVal16(port)); + // Plan to wake if + // (a) RST or FIN is set (the keepalive that we sent could have caused a reset) + // (b) packet that contains new data and acks a sequence number higher than the one + // we have been sending in the keepalive + + wake = ((t->tcp.flags & TH_RST) || (t->tcp.flags & TH_FIN)) ; + if (!wake) + { + mDNSu8 *ptr; + mDNSu32 pseq, pack; + mDNSBool data = mDNSfalse; + mDNSu8 tcphlen; + + // Convert to host order + ptr = (mDNSu8 *)&seq; + seq = ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; + + ptr = (mDNSu8 *)&ack; + ack = ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; + + pseq = t->tcp.seq; + ptr = (mDNSu8 *)&pseq; + pseq = ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; + + pack = t->tcp.ack; + ptr = (mDNSu8 *)&pack; + pack = ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; + + // If the other side is acking one more than our sequence number (keepalive is one + // less than the last valid sequence sent) and it's sequence is more than what we + // acked before + //if (end - p - 34 - ((t->tcp.offset >> 4) * 4) > 0) data = mDNStrue; + tcphlen = ((t->tcp.offset >> 4) * 4); + if (end - ((mDNSu8 *)t + tcphlen) > 0) data = mDNStrue; + wake = ((int)(pack - seq) > 0) && ((int)(pseq - ack) >= 0) && data; + LogSPS("mDNSCoreReceiveRawTransportPacket: End %p, hlen %d, Datalen %d, pack %u, seq %u, pseq %u, ack %u, wake %d", + end, tcphlen, end - ((mDNSu8 *)t + tcphlen), pack, seq, pseq, ack, wake); + } + else { LogSPS("mDNSCoreReceiveRawTransportPacket: waking because of RST or FIN th_flags %d", t->tcp.flags); } + kaWake = wake; + } + else + { + + // Plan to wake if + // (a) RST is not set, AND + // (b) packet is SYN, SYN+FIN, or plain data packet (no SYN or FIN). We won't wake for FIN alone. + wake = (!(t->tcp.flags & TH_RST) && (t->tcp.flags & (TH_FIN|TH_SYN)) != TH_FIN); + + // For now, to reduce spurious wakeups, we wake only for TCP SYN, + // except for ssh connections, where we'll wake for plain data packets too + if (!mDNSSameIPPort(port, SSHPort) && !(t->tcp.flags & 2)) wake = mDNSfalse; + + LogSPS("%s %d-byte TCP from %#a:%d to %#a:%d%s%s%s", XX, + src, mDNSVal16(t->tcp.src), dst, mDNSVal16(port), + (t->tcp.flags & 2) ? " SYN" : "", + (t->tcp.flags & 1) ? " FIN" : "", + (t->tcp.flags & 4) ? " RST" : ""); + } + break; + } + + case 0x11: { + #define ARD_AsNumber 3283 + static const mDNSIPPort ARD = { { ARD_AsNumber >> 8, ARD_AsNumber & 0xFF } }; + const mDNSu16 udplen = (mDNSu16)((mDNSu16)t->bytes[4] << 8 | t->bytes[5]); // Length *including* 8-byte UDP header + if (udplen >= sizeof(UDPHeader)) + { + const mDNSu16 datalen = udplen - sizeof(UDPHeader); + wake = mDNStrue; + + // For Back to My Mac UDP port 4500 (IPSEC) packets, we do some special handling + if (mDNSSameIPPort(port, IPSECPort)) + { + // Specifically ignore NAT keepalive packets + if (datalen == 1 && end >= &t->bytes[9] && t->bytes[8] == 0xFF) wake = mDNSfalse; + else + { + // Skip over the Non-ESP Marker if present + const mDNSBool NonESP = (end >= &t->bytes[12] && t->bytes[8] == 0 && t->bytes[9] == 0 && t->bytes[10] == 0 && t->bytes[11] == 0); + const IKEHeader *const ike = (IKEHeader *)(t + (NonESP ? 12 : 8)); + const mDNSu16 ikelen = datalen - (NonESP ? 4 : 0); + if (ikelen >= sizeof(IKEHeader) && end >= ((mDNSu8 *)ike) + sizeof(IKEHeader)) + if ((ike->Version & 0x10) == 0x10) + { + // ExchangeType == 5 means 'Informational' + // ExchangeType == 34 means 'IKE_SA_INIT' + if (ike->ExchangeType == 5 || ike->ExchangeType == 34) wake = mDNSfalse; + LogSPS("%s %d-byte IKE ExchangeType %d", XX, ike->ExchangeType); + } + } + } + + // For now, because we haven't yet worked out a clean elegant way to do this, we just special-case the + // Apple Remote Desktop port number -- we ignore all packets to UDP 3283 (the "Net Assistant" port), + // except for Apple Remote Desktop's explicit manual wakeup packet, which looks like this: + // UDP header (8 bytes) + // Payload: 13 88 00 6a 41 4e 41 20 (8 bytes) ffffffffffff (6 bytes) 16xMAC (96 bytes) = 110 bytes total + if (mDNSSameIPPort(port, ARD)) wake = (datalen >= 110 && end >= &t->bytes[10] && t->bytes[8] == 0x13 && t->bytes[9] == 0x88); + + LogSPS("%s %d-byte UDP from %#a:%d to %#a:%d", XX, src, mDNSVal16(t->udp.src), dst, mDNSVal16(port)); + } + } + break; + + case 0x3A: if (&t->bytes[len] <= end) + { + mDNSu16 checksum = IPv6CheckSum(&src->ip.v6, &dst->ip.v6, protocol, t->bytes, len); + if (!checksum) mDNSCoreReceiveRawND(m, sha, &src->ip.v6, &t->ndp, &t->bytes[len], InterfaceID); + else LogInfo("IPv6CheckSum bad %04X %02X%02X from %#a to %#a", checksum, t->bytes[2], t->bytes[3], src, dst); + } + break; + + default: LogSPS("Ignoring %d-byte IP packet unknown protocol %d from %#a to %#a", end-p, protocol, src, dst); + break; + } + + if (wake) + { + AuthRecord *rr, *r2; + + mDNS_Lock(m); + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.InterfaceID == InterfaceID && + rr->resrec.RecordType != kDNSRecordTypeDeregistering && + rr->AddressProxy.type && mDNSSameAddress(&rr->AddressProxy, dst)) + { + const mDNSu8 *const tp = (protocol == 6) ? (const mDNSu8 *)"\x4_tcp" : (const mDNSu8 *)"\x4_udp"; + for (r2 = m->ResourceRecords; r2; r2=r2->next) + if (r2->resrec.InterfaceID == InterfaceID && mDNSSameEthAddress(&r2->WakeUp.HMAC, &rr->WakeUp.HMAC) && + r2->resrec.RecordType != kDNSRecordTypeDeregistering && + r2->resrec.rrtype == kDNSType_SRV && mDNSSameIPPort(r2->resrec.rdata->u.srv.port, port) && + SameDomainLabel(ThirdLabel(r2->resrec.name)->c, tp)) + break; + if (!r2 && mDNSSameIPPort(port, IPSECPort)) r2 = rr; // So that we wake for BTMM IPSEC packets, even without a matching SRV record + if (!r2 && kaWake) r2 = rr; // So that we wake for keepalive packets, even without a matching SRV record + if (r2) + { + LogMsg("Waking host at %s %#a H-MAC %.6a I-MAC %.6a for %s", + InterfaceNameForID(m, rr->resrec.InterfaceID), dst, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, r2)); + ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC); + } + else + LogSPS("Sleeping host at %s %#a %.6a has no service on %#s %d", + InterfaceNameForID(m, rr->resrec.InterfaceID), dst, &rr->WakeUp.HMAC, tp, mDNSVal16(port)); + } + mDNS_Unlock(m); + } +} mDNSexport void mDNSCoreReceiveRawPacket(mDNS *const m, const mDNSu8 *const p, const mDNSu8 *const end, const mDNSInterfaceID InterfaceID) - { - static const mDNSOpaque16 Ethertype_ARP = { { 0x08, 0x06 } }; // Ethertype 0x0806 = ARP - static const mDNSOpaque16 Ethertype_IPv4 = { { 0x08, 0x00 } }; // Ethertype 0x0800 = IPv4 - static const mDNSOpaque16 Ethertype_IPv6 = { { 0x86, 0xDD } }; // Ethertype 0x86DD = IPv6 - static const mDNSOpaque16 ARP_hrd_eth = { { 0x00, 0x01 } }; // Hardware address space (Ethernet = 1) - static const mDNSOpaque16 ARP_pro_ip = { { 0x08, 0x00 } }; // Protocol address space (IP = 0x0800) - - // Note: BPF guarantees that the NETWORK LAYER header will be word aligned, not the link-layer header. - // In other words, we can safely assume that pkt below (ARP, IPv4 or IPv6) is properly word aligned, - // but if pkt is 4-byte aligned, that necessarily means that eth CANNOT also be 4-byte aligned - // since it points to a an address 14 bytes before pkt. - const EthernetHeader *const eth = (const EthernetHeader *)p; - const NetworkLayerPacket *const pkt = (const NetworkLayerPacket *)(eth+1); - mDNSAddr src, dst; - #define RequiredCapLen(P) ((P)==0x01 ? 4 : (P)==0x06 ? 20 : (P)==0x11 ? 8 : (P)==0x3A ? 24 : 0) - - // Is ARP? Length must be at least 14 + 28 = 42 bytes - if (end >= p+42 && mDNSSameOpaque16(eth->ethertype, Ethertype_ARP) && mDNSSameOpaque16(pkt->arp.hrd, ARP_hrd_eth) && mDNSSameOpaque16(pkt->arp.pro, ARP_pro_ip)) - mDNSCoreReceiveRawARP(m, &pkt->arp, InterfaceID); - // Is IPv4 with zero fragmentation offset? Length must be at least 14 + 20 = 34 bytes - else if (end >= p+34 && mDNSSameOpaque16(eth->ethertype, Ethertype_IPv4) && (pkt->v4.flagsfrags.b[0] & 0x1F) == 0 && pkt->v4.flagsfrags.b[1] == 0) - { - const mDNSu8 *const trans = p + 14 + (pkt->v4.vlen & 0xF) * 4; - debugf("Got IPv4 %02X from %.4a to %.4a", pkt->v4.protocol, &pkt->v4.src, &pkt->v4.dst); - src.type = mDNSAddrType_IPv4; src.ip.v4 = pkt->v4.src; - dst.type = mDNSAddrType_IPv4; dst.ip.v4 = pkt->v4.dst; - if (end >= trans + RequiredCapLen(pkt->v4.protocol)) - mDNSCoreReceiveRawTransportPacket(m, ð->src, &src, &dst, pkt->v4.protocol, p, (TransportLayerPacket*)trans, end, InterfaceID, 0); - } - // Is IPv6? Length must be at least 14 + 28 = 42 bytes - else if (end >= p+54 && mDNSSameOpaque16(eth->ethertype, Ethertype_IPv6)) - { - const mDNSu8 *const trans = p + 54; - debugf("Got IPv6 %02X from %.16a to %.16a", pkt->v6.pro, &pkt->v6.src, &pkt->v6.dst); - src.type = mDNSAddrType_IPv6; src.ip.v6 = pkt->v6.src; - dst.type = mDNSAddrType_IPv6; dst.ip.v6 = pkt->v6.dst; - if (end >= trans + RequiredCapLen(pkt->v6.pro)) - mDNSCoreReceiveRawTransportPacket(m, ð->src, &src, &dst, pkt->v6.pro, p, (TransportLayerPacket*)trans, end, InterfaceID, - (mDNSu16)pkt->bytes[4] << 8 | pkt->bytes[5]); - } - } +{ + static const mDNSOpaque16 Ethertype_ARP = { { 0x08, 0x06 } }; // Ethertype 0x0806 = ARP + static const mDNSOpaque16 Ethertype_IPv4 = { { 0x08, 0x00 } }; // Ethertype 0x0800 = IPv4 + static const mDNSOpaque16 Ethertype_IPv6 = { { 0x86, 0xDD } }; // Ethertype 0x86DD = IPv6 + static const mDNSOpaque16 ARP_hrd_eth = { { 0x00, 0x01 } }; // Hardware address space (Ethernet = 1) + static const mDNSOpaque16 ARP_pro_ip = { { 0x08, 0x00 } }; // Protocol address space (IP = 0x0800) + + // Note: BPF guarantees that the NETWORK LAYER header will be word aligned, not the link-layer header. + // In other words, we can safely assume that pkt below (ARP, IPv4 or IPv6) is properly word aligned, + // but if pkt is 4-byte aligned, that necessarily means that eth CANNOT also be 4-byte aligned + // since it points to a an address 14 bytes before pkt. + const EthernetHeader *const eth = (const EthernetHeader *)p; + const NetworkLayerPacket *const pkt = (const NetworkLayerPacket *)(eth+1); + mDNSAddr src, dst; + #define RequiredCapLen(P) ((P)==0x01 ? 4 : (P)==0x06 ? 20 : (P)==0x11 ? 8 : (P)==0x3A ? 24 : 0) + + // Is ARP? Length must be at least 14 + 28 = 42 bytes + if (end >= p+42 && mDNSSameOpaque16(eth->ethertype, Ethertype_ARP) && mDNSSameOpaque16(pkt->arp.hrd, ARP_hrd_eth) && mDNSSameOpaque16(pkt->arp.pro, ARP_pro_ip)) + mDNSCoreReceiveRawARP(m, &pkt->arp, InterfaceID); + // Is IPv4 with zero fragmentation offset? Length must be at least 14 + 20 = 34 bytes + else if (end >= p+34 && mDNSSameOpaque16(eth->ethertype, Ethertype_IPv4) && (pkt->v4.flagsfrags.b[0] & 0x1F) == 0 && pkt->v4.flagsfrags.b[1] == 0) + { + const mDNSu8 *const trans = p + 14 + (pkt->v4.vlen & 0xF) * 4; + debugf("Got IPv4 %02X from %.4a to %.4a", pkt->v4.protocol, &pkt->v4.src, &pkt->v4.dst); + src.type = mDNSAddrType_IPv4; src.ip.v4 = pkt->v4.src; + dst.type = mDNSAddrType_IPv4; dst.ip.v4 = pkt->v4.dst; + if (end >= trans + RequiredCapLen(pkt->v4.protocol)) + mDNSCoreReceiveRawTransportPacket(m, ð->src, &src, &dst, pkt->v4.protocol, p, (TransportLayerPacket*)trans, end, InterfaceID, 0); + } + // Is IPv6? Length must be at least 14 + 28 = 42 bytes + else if (end >= p+54 && mDNSSameOpaque16(eth->ethertype, Ethertype_IPv6)) + { + const mDNSu8 *const trans = p + 54; + debugf("Got IPv6 %02X from %.16a to %.16a", pkt->v6.pro, &pkt->v6.src, &pkt->v6.dst); + src.type = mDNSAddrType_IPv6; src.ip.v6 = pkt->v6.src; + dst.type = mDNSAddrType_IPv6; dst.ip.v6 = pkt->v6.dst; + if (end >= trans + RequiredCapLen(pkt->v6.pro)) + mDNSCoreReceiveRawTransportPacket(m, ð->src, &src, &dst, pkt->v6.pro, p, (TransportLayerPacket*)trans, end, InterfaceID, + (mDNSu16)pkt->bytes[4] << 8 | pkt->bytes[5]); + } +} mDNSlocal void ConstructSleepProxyServerName(mDNS *const m, domainlabel *name) - { - name->c[0] = (mDNSu8)mDNS_snprintf((char*)name->c+1, 62, "%d-%d-%d-%d %#s", - m->SPSType, m->SPSPortability, m->SPSMarginalPower, m->SPSTotalPower, &m->nicelabel); - } +{ + name->c[0] = (mDNSu8)mDNS_snprintf((char*)name->c+1, 62, "%d-%d-%d-%d.%d %#s", + m->SPSType, m->SPSPortability, m->SPSMarginalPower, m->SPSTotalPower, m->SPSFeatureFlags, &m->nicelabel); +} mDNSlocal void SleepProxyServerCallback(mDNS *const m, ServiceRecordSet *const srs, mStatus result) - { - if (result == mStatus_NameConflict) - mDNS_RenameAndReregisterService(m, srs, mDNSNULL); - else if (result == mStatus_MemFree) - { - if (m->SleepState) - m->SPSState = 3; - else - { - m->SPSState = (mDNSu8)(m->SPSSocket != mDNSNULL); - if (m->SPSState) - { - domainlabel name; - ConstructSleepProxyServerName(m, &name); - mDNS_RegisterService(m, srs, - &name, &SleepProxyServiceType, &localdomain, - mDNSNULL, m->SPSSocket->port, // Host, port - (mDNSu8 *)"", 1, // TXT data, length - mDNSNULL, 0, // Subtypes (none) - mDNSInterface_Any, // Interface ID - SleepProxyServerCallback, mDNSNULL, 0); // Callback, context, flags - } - LogSPS("Sleep Proxy Server %#s %s", srs->RR_SRV.resrec.name->c, m->SPSState ? "started" : "stopped"); - } - } - } +{ + if (result == mStatus_NameConflict) + mDNS_RenameAndReregisterService(m, srs, mDNSNULL); + else if (result == mStatus_MemFree) + { + if (m->SleepState) + m->SPSState = 3; + else + { + m->SPSState = (mDNSu8)(m->SPSSocket != mDNSNULL); + if (m->SPSState) + { + domainlabel name; + ConstructSleepProxyServerName(m, &name); + mDNS_RegisterService(m, srs, + &name, &SleepProxyServiceType, &localdomain, + mDNSNULL, m->SPSSocket->port, // Host, port + (mDNSu8 *)"", 1, // TXT data, length + mDNSNULL, 0, // Subtypes (none) + mDNSInterface_Any, // Interface ID + SleepProxyServerCallback, mDNSNULL, 0); // Callback, context, flags + } + LogSPS("Sleep Proxy Server %#s %s", srs->RR_SRV.resrec.name->c, m->SPSState ? "started" : "stopped"); + } + } +} // Called with lock held -mDNSexport void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower) - { - // This routine uses mDNS_DeregisterService and calls SleepProxyServerCallback, so we execute in user callback context - mDNS_DropLockBeforeCallback(); - - // If turning off SPS, close our socket - // (Do this first, BEFORE calling mDNS_DeregisterService below) - if (!sps && m->SPSSocket) { mDNSPlatformUDPClose(m->SPSSocket); m->SPSSocket = mDNSNULL; } - - // If turning off, or changing type, deregister old name - if (m->SPSState == 1 && sps != m->SPSType) - { m->SPSState = 2; mDNS_DeregisterService_drt(m, &m->SPSRecords, sps ? mDNS_Dereg_rapid : mDNS_Dereg_normal); } - - // Record our new SPS parameters - m->SPSType = sps; - m->SPSPortability = port; - m->SPSMarginalPower = marginalpower; - m->SPSTotalPower = totpower; - - // If turning on, open socket and advertise service - if (sps) - { - if (!m->SPSSocket) - { - m->SPSSocket = mDNSPlatformUDPSocket(m, zeroIPPort); - if (!m->SPSSocket) { LogMsg("mDNSCoreBeSleepProxyServer: Failed to allocate SPSSocket"); goto fail; } - } - if (m->SPSState == 0) SleepProxyServerCallback(m, &m->SPSRecords, mStatus_MemFree); - } - else if (m->SPSState) - { - LogSPS("mDNSCoreBeSleepProxyServer turning off from state %d; will wake clients", m->SPSState); - m->NextScheduledSPS = m->timenow; - } +mDNSexport void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower, mDNSu8 features) +{ + // This routine uses mDNS_DeregisterService and calls SleepProxyServerCallback, so we execute in user callback context + mDNS_DropLockBeforeCallback(); + + // If turning off SPS, close our socket + // (Do this first, BEFORE calling mDNS_DeregisterService below) + if (!sps && m->SPSSocket) { mDNSPlatformUDPClose(m->SPSSocket); m->SPSSocket = mDNSNULL; } + + // If turning off, or changing type, deregister old name + if (m->SPSState == 1 && sps != m->SPSType) + { m->SPSState = 2; mDNS_DeregisterService_drt(m, &m->SPSRecords, sps ? mDNS_Dereg_rapid : mDNS_Dereg_normal); } + + // Record our new SPS parameters + m->SPSType = sps; + m->SPSPortability = port; + m->SPSMarginalPower = marginalpower; + m->SPSTotalPower = totpower; + m->SPSFeatureFlags = features; + // If turning on, open socket and advertise service + if (sps) + { + if (!m->SPSSocket) + { + m->SPSSocket = mDNSPlatformUDPSocket(m, zeroIPPort); + if (!m->SPSSocket) { LogMsg("mDNSCoreBeSleepProxyServer: Failed to allocate SPSSocket"); goto fail; } + } + if (m->SPSState == 0) SleepProxyServerCallback(m, &m->SPSRecords, mStatus_MemFree); + } + else if (m->SPSState) + { + LogSPS("mDNSCoreBeSleepProxyServer turning off from state %d; will wake clients", m->SPSState); + m->NextScheduledSPS = m->timenow; + } fail: - mDNS_ReclaimLockAfterCallback(); - } + mDNS_ReclaimLockAfterCallback(); +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -10643,879 +11856,794 @@ fail: #endif mDNSlocal void mDNS_GrowCache_internal(mDNS *const m, CacheEntity *storage, mDNSu32 numrecords) - { - if (storage && numrecords) - { - mDNSu32 i; - debugf("Adding cache storage for %d more records (%d bytes)", numrecords, numrecords*sizeof(CacheEntity)); - for (i=0; irrcache_free; - m->rrcache_free = storage; - m->rrcache_size += numrecords; - } - } +{ + if (storage && numrecords) + { + mDNSu32 i; + debugf("Adding cache storage for %d more records (%d bytes)", numrecords, numrecords*sizeof(CacheEntity)); + for (i=0; irrcache_free; + m->rrcache_free = storage; + m->rrcache_size += numrecords; + } +} mDNSexport void mDNS_GrowCache(mDNS *const m, CacheEntity *storage, mDNSu32 numrecords) - { - mDNS_Lock(m); - mDNS_GrowCache_internal(m, storage, numrecords); - mDNS_Unlock(m); - } +{ + mDNS_Lock(m); + mDNS_GrowCache_internal(m, storage, numrecords); + mDNS_Unlock(m); +} mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, - CacheEntity *rrcachestorage, mDNSu32 rrcachesize, - mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context) - { - mDNSu32 slot; - mDNSs32 timenow; - mStatus result; - - if (!rrcachestorage) rrcachesize = 0; - - m->p = p; - m->KnownBugs = 0; - m->CanReceiveUnicastOn5353 = mDNSfalse; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise - m->AdvertiseLocalAddresses = AdvertiseLocalAddresses; - m->DivertMulticastAdvertisements = mDNSfalse; - m->mDNSPlatformStatus = mStatus_Waiting; - m->UnicastPort4 = zeroIPPort; - m->UnicastPort6 = zeroIPPort; - m->PrimaryMAC = zeroEthAddr; - m->MainCallback = Callback; - m->MainContext = Context; - m->rec.r.resrec.RecordType = 0; - - // For debugging: To catch and report locking failures - m->mDNS_busy = 0; - m->mDNS_reentrancy = 0; - m->ShutdownTime = 0; - m->lock_rrcache = 0; - m->lock_Questions = 0; - m->lock_Records = 0; - - // Task Scheduling variables - result = mDNSPlatformTimeInit(); - if (result != mStatus_NoError) return(result); - m->timenow_adjust = (mDNSs32)mDNSRandom(0xFFFFFFFF); - timenow = mDNS_TimeNow_NoLock(m); - - m->timenow = 0; // MUST only be set within mDNS_Lock/mDNS_Unlock section - m->timenow_last = timenow; - m->NextScheduledEvent = timenow; - m->SuppressSending = timenow; - m->NextCacheCheck = timenow + 0x78000000; - m->NextScheduledQuery = timenow + 0x78000000; - m->NextScheduledProbe = timenow + 0x78000000; - m->NextScheduledResponse = timenow + 0x78000000; - m->NextScheduledNATOp = timenow + 0x78000000; - m->NextScheduledSPS = timenow + 0x78000000; - m->NextScheduledStopTime = timenow + 0x78000000; - m->RandomQueryDelay = 0; - m->RandomReconfirmDelay = 0; - m->PktNum = 0; - m->LocalRemoveEvents = mDNSfalse; - m->SleepState = SleepState_Awake; - m->SleepSeqNum = 0; - m->SystemWakeOnLANEnabled = mDNSfalse; - m->AnnounceOwner = NonZeroTime(timenow + 60 * mDNSPlatformOneSecond); - m->DelaySleep = 0; - m->SleepLimit = 0; - - // These fields only required for mDNS Searcher... - m->Questions = mDNSNULL; - m->NewQuestions = mDNSNULL; - m->CurrentQuestion = mDNSNULL; - m->LocalOnlyQuestions = mDNSNULL; - m->NewLocalOnlyQuestions = mDNSNULL; - m->RestartQuestion = mDNSNULL; - m->rrcache_size = 0; - m->rrcache_totalused = 0; - m->rrcache_active = 0; - m->rrcache_report = 10; - m->rrcache_free = mDNSNULL; - - for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) - { - m->rrcache_hash[slot] = mDNSNULL; - m->rrcache_nextcheck[slot] = timenow + 0x78000000;; - } - - mDNS_GrowCache_internal(m, rrcachestorage, rrcachesize); - m->rrauth.rrauth_free = mDNSNULL; - - for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) - m->rrauth.rrauth_hash[slot] = mDNSNULL; - - // Fields below only required for mDNS Responder... - m->hostlabel.c[0] = 0; - m->nicelabel.c[0] = 0; - m->MulticastHostname.c[0] = 0; - m->HIHardware.c[0] = 0; - m->HISoftware.c[0] = 0; - m->ResourceRecords = mDNSNULL; - m->DuplicateRecords = mDNSNULL; - m->NewLocalRecords = mDNSNULL; - m->NewLocalOnlyRecords = mDNSfalse; - m->CurrentRecord = mDNSNULL; - m->HostInterfaces = mDNSNULL; - m->ProbeFailTime = 0; - m->NumFailedProbes = 0; - m->SuppressProbes = 0; + CacheEntity *rrcachestorage, mDNSu32 rrcachesize, + mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context) +{ + mDNSu32 slot; + mDNSs32 timenow; + mStatus result; + + if (!rrcachestorage) rrcachesize = 0; + + m->p = p; + m->KnownBugs = 0; + m->CanReceiveUnicastOn5353 = mDNSfalse; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise + m->AdvertiseLocalAddresses = AdvertiseLocalAddresses; + m->DivertMulticastAdvertisements = mDNSfalse; + m->mDNSPlatformStatus = mStatus_Waiting; + m->UnicastPort4 = zeroIPPort; + m->UnicastPort6 = zeroIPPort; + m->PrimaryMAC = zeroEthAddr; + m->MainCallback = Callback; + m->MainContext = Context; + m->rec.r.resrec.RecordType = 0; + + // For debugging: To catch and report locking failures + m->mDNS_busy = 0; + m->mDNS_reentrancy = 0; + m->ShutdownTime = 0; + m->lock_rrcache = 0; + m->lock_Questions = 0; + m->lock_Records = 0; + + // Task Scheduling variables + result = mDNSPlatformTimeInit(); + if (result != mStatus_NoError) return(result); + m->timenow_adjust = (mDNSs32)mDNSRandom(0xFFFFFFFF); + timenow = mDNS_TimeNow_NoLock(m); + + m->timenow = 0; // MUST only be set within mDNS_Lock/mDNS_Unlock section + m->timenow_last = timenow; + m->NextScheduledEvent = timenow; + m->SuppressSending = timenow; + m->NextCacheCheck = timenow + 0x78000000; + m->NextScheduledQuery = timenow + 0x78000000; + m->NextScheduledProbe = timenow + 0x78000000; + m->NextScheduledResponse = timenow + 0x78000000; + m->NextScheduledNATOp = timenow + 0x78000000; + m->NextScheduledSPS = timenow + 0x78000000; + m->NextScheduledKA = timenow + 0x78000000; + m->NextScheduledStopTime = timenow + 0x78000000; + m->RandomQueryDelay = 0; + m->RandomReconfirmDelay = 0; + m->PktNum = 0; + m->LocalRemoveEvents = mDNSfalse; + m->SleepState = SleepState_Awake; + m->SleepSeqNum = 0; + m->SystemWakeOnLANEnabled = mDNSfalse; + m->AnnounceOwner = NonZeroTime(timenow + 60 * mDNSPlatformOneSecond); + m->clearIgnoreNA = NonZeroTime(timenow + 2 * mDNSPlatformOneSecond); + m->DelaySleep = 0; + m->SleepLimit = 0; + + // These fields only required for mDNS Searcher... + m->Questions = mDNSNULL; + m->NewQuestions = mDNSNULL; + m->CurrentQuestion = mDNSNULL; + m->LocalOnlyQuestions = mDNSNULL; + m->NewLocalOnlyQuestions = mDNSNULL; + m->RestartQuestion = mDNSNULL; + m->ValidationQuestion = mDNSNULL; + m->rrcache_size = 0; + m->rrcache_totalused = 0; + m->rrcache_active = 0; + m->rrcache_report = 10; + m->rrcache_free = mDNSNULL; + + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + { + m->rrcache_hash[slot] = mDNSNULL; + m->rrcache_nextcheck[slot] = timenow + 0x78000000;; + } + + mDNS_GrowCache_internal(m, rrcachestorage, rrcachesize); + m->rrauth.rrauth_free = mDNSNULL; + + for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) + m->rrauth.rrauth_hash[slot] = mDNSNULL; + + // Fields below only required for mDNS Responder... + m->hostlabel.c[0] = 0; + m->nicelabel.c[0] = 0; + m->MulticastHostname.c[0] = 0; + m->HIHardware.c[0] = 0; + m->HISoftware.c[0] = 0; + m->ResourceRecords = mDNSNULL; + m->DuplicateRecords = mDNSNULL; + m->NewLocalRecords = mDNSNULL; + m->NewLocalOnlyRecords = mDNSfalse; + m->CurrentRecord = mDNSNULL; + m->HostInterfaces = mDNSNULL; + m->ProbeFailTime = 0; + m->NumFailedProbes = 0; + m->SuppressProbes = 0; #ifndef UNICAST_DISABLED - m->NextuDNSEvent = timenow + 0x78000000; - m->NextSRVUpdate = timenow + 0x78000000; - - m->DNSServers = mDNSNULL; - - m->Router = zeroAddr; - m->AdvertisedV4 = zeroAddr; - m->AdvertisedV6 = zeroAddr; - - m->AuthInfoList = mDNSNULL; - - m->ReverseMap.ThisQInterval = -1; - m->StaticHostname.c[0] = 0; - m->FQDN.c[0] = 0; - m->Hostnames = mDNSNULL; - m->AutoTunnelHostAddr.b[0] = 0; - m->AutoTunnelHostAddrActive = mDNSfalse; - m->AutoTunnelLabel.c[0] = 0; - - m->StartWABQueries = mDNSfalse; - m->RegisterAutoTunnel6 = mDNStrue; - - // NAT traversal fields - m->NATTraversals = mDNSNULL; - m->CurrentNATTraversal = mDNSNULL; - m->retryIntervalGetAddr = 0; // delta between time sent and retry - m->retryGetAddr = timenow + 0x78000000; // absolute time when we retry - m->ExternalAddress = zerov4Addr; - - m->NATMcastRecvskt = mDNSNULL; - m->LastNATupseconds = 0; - m->LastNATReplyLocalTime = timenow; - m->LastNATMapResultCode = NATErr_None; - - m->UPnPInterfaceID = 0; - m->SSDPSocket = mDNSNULL; - m->SSDPWANPPPConnection = mDNSfalse; - m->UPnPRouterPort = zeroIPPort; - m->UPnPSOAPPort = zeroIPPort; - m->UPnPRouterURL = mDNSNULL; - m->UPnPWANPPPConnection = mDNSfalse; - m->UPnPSOAPURL = mDNSNULL; - m->UPnPRouterAddressString = mDNSNULL; - m->UPnPSOAPAddressString = mDNSNULL; - m->SPSType = 0; - m->SPSPortability = 0; - m->SPSMarginalPower = 0; - m->SPSTotalPower = 0; - m->SPSState = 0; - m->SPSProxyListChanged = mDNSNULL; - m->SPSSocket = mDNSNULL; - m->SPSBrowseCallback = mDNSNULL; - m->ProxyRecords = 0; + m->NextuDNSEvent = timenow + 0x78000000; + m->NextSRVUpdate = timenow + 0x78000000; + + m->DNSServers = mDNSNULL; + + m->Router = zeroAddr; + m->AdvertisedV4 = zeroAddr; + m->AdvertisedV6 = zeroAddr; + + m->AuthInfoList = mDNSNULL; + + m->ReverseMap.ThisQInterval = -1; + m->StaticHostname.c[0] = 0; + m->FQDN.c[0] = 0; + m->Hostnames = mDNSNULL; + m->AutoTunnelNAT.clientContext = mDNSNULL; + + m->StartWABQueries = mDNSfalse; + m->mDNSHandlePeerEvents = mDNSfalse; + + // NAT traversal fields + m->NATTraversals = mDNSNULL; + m->CurrentNATTraversal = mDNSNULL; + m->retryIntervalGetAddr = 0; // delta between time sent and retry + m->retryGetAddr = timenow + 0x78000000; // absolute time when we retry + m->ExternalAddress = zerov4Addr; + + m->NATMcastRecvskt = mDNSNULL; + m->LastNATupseconds = 0; + m->LastNATReplyLocalTime = timenow; + m->LastNATMapResultCode = NATErr_None; + + m->UPnPInterfaceID = 0; + m->SSDPSocket = mDNSNULL; + m->SSDPWANPPPConnection = mDNSfalse; + m->UPnPRouterPort = zeroIPPort; + m->UPnPSOAPPort = zeroIPPort; + m->UPnPRouterURL = mDNSNULL; + m->UPnPWANPPPConnection = mDNSfalse; + m->UPnPSOAPURL = mDNSNULL; + m->UPnPRouterAddressString = mDNSNULL; + m->UPnPSOAPAddressString = mDNSNULL; + m->SPSType = 0; + m->SPSPortability = 0; + m->SPSMarginalPower = 0; + m->SPSTotalPower = 0; + m->SPSFeatureFlags = 0; + m->SPSState = 0; + m->SPSProxyListChanged = mDNSNULL; + m->SPSSocket = mDNSNULL; + m->SPSBrowseCallback = mDNSNULL; + m->ProxyRecords = 0; #endif #if APPLE_OSX_mDNSResponder - m->TunnelClients = mDNSNULL; - -#if ! NO_WCF - CHECK_WCF_FUNCTION(WCFConnectionNew) - { - m->WCF = WCFConnectionNew(); - if (!m->WCF) { LogMsg("WCFConnectionNew failed"); return -1; } - } + m->TunnelClients = mDNSNULL; + +#if !NO_WCF + CHECK_WCF_FUNCTION(WCFConnectionNew) + { + m->WCF = WCFConnectionNew(); + if (!m->WCF) { LogMsg("WCFConnectionNew failed"); return -1; } + } #endif #endif - result = mDNSPlatformInit(m); + result = mDNSPlatformInit(m); #ifndef UNICAST_DISABLED - // It's better to do this *after* the platform layer has set up the - // interface list and security credentials - uDNS_SetupDNSConfig(m); // Get initial DNS configuration + // It's better to do this *after* the platform layer has set up the + // interface list and security credentials + uDNS_SetupDNSConfig(m); // Get initial DNS configuration #endif - return(result); - } + return(result); +} mDNSexport void mDNS_ConfigChanged(mDNS *const m) - { - if (m->SPSState == 1) - { - domainlabel name, newname; - domainname type, domain; - DeconstructServiceName(m->SPSRecords.RR_SRV.resrec.name, &name, &type, &domain); - ConstructSleepProxyServerName(m, &newname); - if (!SameDomainLabelCS(name.c, newname.c)) - { - LogSPS("Renaming SPS from “%#s” to “%#s”", name.c, newname.c); - // When SleepProxyServerCallback gets the mStatus_MemFree message, - // it will reregister the service under the new name - m->SPSState = 2; - mDNS_DeregisterService_drt(m, &m->SPSRecords, mDNS_Dereg_rapid); - } - } - - if (m->MainCallback) - m->MainCallback(m, mStatus_ConfigChanged); - } +{ + if (m->SPSState == 1) + { + domainlabel name, newname; + domainname type, domain; + DeconstructServiceName(m->SPSRecords.RR_SRV.resrec.name, &name, &type, &domain); + ConstructSleepProxyServerName(m, &newname); + if (!SameDomainLabelCS(name.c, newname.c)) + { + LogSPS("Renaming SPS from “%#s” to “%#s”", name.c, newname.c); + // When SleepProxyServerCallback gets the mStatus_MemFree message, + // it will reregister the service under the new name + m->SPSState = 2; + mDNS_DeregisterService_drt(m, &m->SPSRecords, mDNS_Dereg_rapid); + } + } + + if (m->MainCallback) + m->MainCallback(m, mStatus_ConfigChanged); +} mDNSlocal void DynDNSHostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) - { - (void)m; // unused - debugf("NameStatusCallback: result %d for registration of name %##s", result, rr->resrec.name->c); - mDNSPlatformDynDNSHostNameStatusChanged(rr->resrec.name, result); - } +{ + (void)m; // unused + debugf("NameStatusCallback: result %d for registration of name %##s", result, rr->resrec.name->c); + mDNSPlatformDynDNSHostNameStatusChanged(rr->resrec.name, result); +} mDNSlocal void PurgeOrReconfirmCacheRecord(mDNS *const m, CacheRecord *cr, const DNSServer * const ptr, mDNSBool lameduck) - { - mDNSBool purge = cr->resrec.RecordType == kDNSRecordTypePacketNegative || - cr->resrec.rrtype == kDNSType_A || - cr->resrec.rrtype == kDNSType_AAAA || - cr->resrec.rrtype == kDNSType_SRV; - - (void) lameduck; - (void) ptr; - debugf("PurgeOrReconfirmCacheRecord: %s cache record due to %s server %p %#a:%d (%##s): %s", - purge ? "purging" : "reconfirming", - lameduck ? "lame duck" : "new", - ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c, CRDisplayString(m, cr)); - - if (purge) - { - LogInfo("PurgeorReconfirmCacheRecord: Purging Resourcerecord %s, RecordType %x", CRDisplayString(m, cr), cr->resrec.RecordType); - mDNS_PurgeCacheResourceRecord(m, cr); - } - else - { - LogInfo("PurgeorReconfirmCacheRecord: Reconfirming Resourcerecord %s, RecordType %x", CRDisplayString(m, cr), cr->resrec.RecordType); - mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); - } - } +{ + mDNSBool purge = cr->resrec.RecordType == kDNSRecordTypePacketNegative || + cr->resrec.rrtype == kDNSType_A || + cr->resrec.rrtype == kDNSType_AAAA || + cr->resrec.rrtype == kDNSType_SRV; + + (void) lameduck; + (void) ptr; + debugf("PurgeOrReconfirmCacheRecord: %s cache record due to %s server %p %#a:%d (%##s): %s", + purge ? "purging" : "reconfirming", + lameduck ? "lame duck" : "new", + ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c, CRDisplayString(m, cr)); + + if (purge) + { + LogInfo("PurgeorReconfirmCacheRecord: Purging Resourcerecord %s, RecordType %x", CRDisplayString(m, cr), cr->resrec.RecordType); + mDNS_PurgeCacheResourceRecord(m, cr); + } + else + { + LogInfo("PurgeorReconfirmCacheRecord: Reconfirming Resourcerecord %s, RecordType %x", CRDisplayString(m, cr), cr->resrec.RecordType); + mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); + } +} mDNSlocal void mDNS_PurgeBeforeResolve(mDNS *const m, DNSQuestion *q) - { - const mDNSu32 slot = HashSlot(&q->qname); - CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); - CacheRecord *rp; - - for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next) - { - if (SameNameRecordAnswersQuestion(&rp->resrec, q)) - { - LogInfo("mDNS_PurgeBeforeResolve: Flushing %s", CRDisplayString(m, rp)); - mDNS_PurgeCacheResourceRecord(m, rp); - } - } - } - -mDNSlocal void CacheRecordResetDNSServer(mDNS *const m, DNSQuestion *q, DNSServer *new) - { - const mDNSu32 slot = HashSlot(&q->qname); - CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); - CacheRecord *rp; - mDNSBool found = mDNSfalse; - mDNSBool foundNew = mDNSfalse; - DNSServer *old = q->qDNSServer; - mDNSBool newQuestion = IsQuestionNew(m, q); - DNSQuestion *qptr; - - // This function is called when the DNSServer is updated to the new question. There may already be - // some cache entries matching the old DNSServer and/or new DNSServer. There are four cases. In the - // following table, "Yes" denotes that a cache entry was found for old/new DNSServer. - // - // old DNSServer new DNSServer - // - // Case 1 Yes Yes - // Case 2 No Yes - // Case 3 Yes No - // Case 4 No No - // - // Case 1: There are cache entries for both old and new DNSServer. We handle this case by simply - // expiring the old Cache entries, deliver a RMV event (if an ADD event was delivered before) - // followed by the ADD event of the cache entries corresponding to the new server. This - // case happens when we pick a DNSServer, issue a query and get a valid response and create - // cache entries after which it stops responding. Another query (non-duplicate) picks a different - // DNSServer and creates identical cache entries (perhaps through records in Additional records). - // Now if the first one expires and tries to pick the new DNSServer (the original DNSServer - // is not responding) we will find cache entries corresponding to both DNSServers. - // - // Case 2: There are no cache entries for the old DNSServer but there are some for the new DNSServer. - // This means we should deliver an ADD event. Normally ADD events are delivered by - // AnswerNewQuestion if it is a new question. So, we check to see if it is a new question - // and if so, leave it to AnswerNewQuestion to deliver it. Otherwise, we use - // AnswerQuestionsForDNSServerChanges to deliver the ADD event. This case happens when a - // question picks a DNS server for which AnswerNewQuestion could not deliver an answer even - // though there were potential cache entries but DNSServer did not match. Now when we - // pick a new DNSServer, those cache entries may answer this question. - // - // Case 3: There are the cache entries for the old DNSServer but none for the new. We just move - // the old cache entries to point to the new DNSServer and the caller is expected to - // do a purge or reconfirm to delete or validate the RDATA. We don't need to do anything - // special for delivering ADD events, as it should have been done/will be done by - // AnswerNewQuestion. This case happens when we picked a DNSServer, sent the query and - // got a response and the cache is expired now and we are reissuing the question but the - // original DNSServer does not respond. - // - // Case 4: There are no cache entries either for the old or for the new DNSServer. There is nothing - // much we can do here. - // - // Case 2 and 3 are the most common while case 4 is possible when no DNSServers are working. Case 1 - // is relatively less likely to happen in practice - - // Temporarily set the DNSServer to look for the matching records for the new DNSServer. - q->qDNSServer = new; - for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next) - { - if (SameNameRecordAnswersQuestion(&rp->resrec, q)) - { - LogInfo("CacheRecordResetDNSServer: Found cache record %##s for new DNSServer address: %#a", rp->resrec.name->c, - (rp->resrec.rDNSServer != mDNSNULL ? &rp->resrec.rDNSServer->addr : mDNSNULL)); - foundNew = mDNStrue; - break; - } - } - q->qDNSServer = old; - - for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next) - { - if (SameNameRecordAnswersQuestion(&rp->resrec, q)) - { - // Case1 - found = mDNStrue; - if (foundNew) - { - LogInfo("CacheRecordResetDNSServer: Flushing Resourcerecord %##s, before:%#a, after:%#a", rp->resrec.name->c, - (rp->resrec.rDNSServer != mDNSNULL ? &rp->resrec.rDNSServer->addr : mDNSNULL), - (new != mDNSNULL ? &new->addr : mDNSNULL)); - mDNS_PurgeCacheResourceRecord(m, rp); - if (newQuestion) - { - // "q" is not a duplicate question. If it is a newQuestion, then the CRActiveQuestion can't be - // possibly set as it is set only when we deliver the ADD event to the question. - if (rp->CRActiveQuestion != mDNSNULL) - { - LogMsg("CacheRecordResetDNSServer: ERROR!!: CRActiveQuestion %p set, current question %p, name %##s", rp->CRActiveQuestion, q, q->qname.c); - rp->CRActiveQuestion = mDNSNULL; - } - // if this is a new question, then we never delivered an ADD yet, so don't deliver the RMV. - continue; - } - } - LogInfo("CacheRecordResetDNSServer: resetting cache record %##s DNSServer address before:%#a," - " after:%#a, CRActiveQuestion %p", rp->resrec.name->c, (rp->resrec.rDNSServer != mDNSNULL ? - &rp->resrec.rDNSServer->addr : mDNSNULL), (new != mDNSNULL ? &new->addr : mDNSNULL), - rp->CRActiveQuestion); - // Though we set it to the new DNS server, the caller is *assumed* to do either a purge - // or reconfirm or send out questions to the "new" server to verify whether the cached - // RDATA is valid - rp->resrec.rDNSServer = new; - } - } - - // Case 1 and Case 2 - if ((found && foundNew) || (!found && foundNew)) - { - if (newQuestion) - LogInfo("CacheRecordResetDNSServer: deliverAddEvents not set for question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); - else if (QuerySuppressed(q)) - LogInfo("CacheRecordResetDNSServer: deliverAddEvents not set for suppressed question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); - else - { - LogInfo("CacheRecordResetDNSServer: deliverAddEvents set for %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); - q->deliverAddEvents = mDNStrue; - for (qptr = q->next; qptr; qptr = qptr->next) - if (qptr->DuplicateOf == q) qptr->deliverAddEvents = mDNStrue; - } - return; - } - - // Case 3 and Case 4 - return; - } +{ + const mDNSu32 slot = HashSlot(&q->qname); + CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + CacheRecord *rp; + + for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next) + { + if (SameNameRecordAnswersQuestion(&rp->resrec, q)) + { + LogInfo("mDNS_PurgeBeforeResolve: Flushing %s", CRDisplayString(m, rp)); + mDNS_PurgeCacheResourceRecord(m, rp); + } + } +} + +// If we need to validate the negative response, we need the NSECs to prove +// the non-existence. If we don't have the cached NSECs, purge them so that +// we can reissue the question with EDNS0/DO bit set. +mDNSlocal void mDNS_CheckForCachedNSECS(mDNS *const m, DNSQuestion *q) +{ + const mDNSu32 slot = HashSlot(&q->qname); + CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + CacheRecord *rp; + + for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next) + { + if (SameNameRecordAnswersQuestion(&rp->resrec, q) && + rp->resrec.RecordType == kDNSRecordTypePacketNegative && + !rp->nsec) + { + LogInfo("mDNS_CheckForCachedNSECS: Flushing %s", CRDisplayString(m, rp)); + mDNS_PurgeCacheResourceRecord(m, rp); + } + } +} + +// Check for a positive unicast response to the question but with qtype +mDNSexport mDNSBool mDNS_CheckForCacheRecord(mDNS *const m, DNSQuestion *q, mDNSu16 qtype) +{ + DNSQuestion question; + const mDNSu32 slot = HashSlot(&q->qname); + CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + CacheRecord *rp; + + // Create an identical question but with qtype + mDNS_SetupQuestion(&question, q->InterfaceID, &q->qname, qtype, mDNSNULL, mDNSNULL); + question.qDNSServer = q->qDNSServer; + + for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next) + { + if (!rp->resrec.InterfaceID && rp->resrec.RecordType != kDNSRecordTypePacketNegative && + SameNameRecordAnswersQuestion(&rp->resrec, &question)) + { + LogInfo("mDNS_CheckForCacheRecord: Found %s", CRDisplayString(m, rp)); + return mDNStrue; + } + } + return mDNSfalse; +} mDNSexport void DNSServerChangeForQuestion(mDNS *const m, DNSQuestion *q, DNSServer *new) - { - DNSQuestion *qptr; - - // 1. Whenever we change the DNS server, we change the message identifier also so that response - // from the old server is not accepted as a response from the new server but only messages - // from the new server are accepted as valid responses. We do it irrespective of whether "new" - // is NULL or not. It is possible that we send two queries, no responses, pick a new DNS server - // which is NULL and now the response comes back and will try to penalize the DNS server which - // is NULL. By setting the messageID here, we will not accept that as a valid response. - - q->TargetQID = mDNS_NewMessageID(m); - - // 2. Move the old cache records to point them at the new DNSServer so that we can deliver the ADD/RMV events - // appropriately. At any point in time, we want all the cache records point only to one DNSServer for a given - // question. "DNSServer" here is the DNSServer object and not the DNS server itself. It is possible to - // have the same DNS server address in two objects, one scoped and another not scoped. But, the cache is per - // DNSServer object. By maintaining the question and the cache entries point to the same DNSServer - // always, the cache maintenance and delivery of ADD/RMV events becomes simpler. - // - // CacheRecordResetDNSServer should be called only once for the non-duplicate question as once the cache - // entries are moved to point to the new DNSServer, we don't need to call it for the duplicate question - // and it is wrong to call for the duplicate question as it's decision to mark deliverAddevents will be - // incorrect. - - if (q->DuplicateOf) - LogMsg("DNSServerChangeForQuestion: ERROR: Called for duplicate question %##s", q->qname.c); - else - CacheRecordResetDNSServer(m, q, new); - - // 3. Make sure all the duplicate questions point to the same DNSServer so that delivery - // of events for all of them are consistent. Duplicates for a question are always inserted - // after in the list. - q->qDNSServer = new; - for (qptr = q->next ; qptr; qptr = qptr->next) - { - if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = new; } - } - } - +{ + DNSQuestion *qptr; + + (void) m; + + if (q->DuplicateOf) + LogMsg("DNSServerChangeForQuestion: ERROR: Called for duplicate question %##s", q->qname.c); + + // Make sure all the duplicate questions point to the same DNSServer so that delivery + // of events for all of them are consistent. Duplicates for a question are always inserted + // after in the list. + q->qDNSServer = new; + for (qptr = q->next ; qptr; qptr = qptr->next) + { + if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = new; } + } +} + mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) - { - mDNSu32 slot; - CacheGroup *cg; - CacheRecord *cr; - - mDNSAddr v4, v6, r; - domainname fqdn; - DNSServer *ptr, **p = &m->DNSServers; - const DNSServer *oldServers = m->DNSServers; - DNSQuestion *q; - McastResolver *mr, **mres = &m->McastResolvers; - - debugf("uDNS_SetupDNSConfig: entry"); - - // Let the platform layer get the current DNS information - // The m->StartWABQueries is set when we get the first domain enumeration query (no need to hit the network - // with domain enumeration queries until we actually need that information). Even if it is not set, we still - // need to setup the search domains so that we can append them to queries that need them. - - uDNS_SetupSearchDomains(m, m->StartWABQueries ? UDNS_START_WAB_QUERY : 0); - - mDNS_Lock(m); - - for (ptr = m->DNSServers; ptr; ptr = ptr->next) - { - ptr->penaltyTime = 0; - ptr->flags |= DNSServer_FlagDelete; - } - - // We handle the mcast resolvers here itself as mDNSPlatformSetDNSConfig looks at - // mcast resolvers. Today we get both mcast and ucast configuration using the same - // API - for (mr = m->McastResolvers; mr; mr = mr->next) - mr->flags |= McastResolver_FlagDelete; - - mDNSPlatformSetDNSConfig(m, mDNStrue, mDNSfalse, &fqdn, mDNSNULL, mDNSNULL); - - // For now, we just delete the mcast resolvers. We don't deal with cache or - // questions here. Neither question nor cache point to mcast resolvers. Questions - // do inherit the timeout values from mcast resolvers. But we don't bother - // affecting them as they never change. - while (*mres) - { - if (((*mres)->flags & DNSServer_FlagDelete) != 0) - { - mr = *mres; - *mres = (*mres)->next; - debugf("uDNS_SetupDNSConfig: Deleting mcast resolver %##s", mr, mr->domain.c); - mDNSPlatformMemFree(mr); - } - else - { - (*mres)->flags &= ~McastResolver_FlagNew; - mres = &(*mres)->next; - } - } - - // Mark the records to be flushed that match a new resolver. We need to do this before - // we walk the questions below where we change the DNSServer pointer of the cache - // record - FORALL_CACHERECORDS(slot, cg, cr) - { - if (cr->resrec.InterfaceID) continue; - - // We just mark them for purge or reconfirm. We can't affect the DNSServer pointer - // here as the code below that calls CacheRecordResetDNSServer relies on this - // - // The new DNSServer may be a scoped or non-scoped one. We use the active question's - // InterfaceID for looking up the right DNS server - ptr = GetServerForName(m, cr->resrec.name, cr->CRActiveQuestion ? cr->CRActiveQuestion->InterfaceID : mDNSNULL); - - // Purge or Reconfirm if this cache entry would use the new DNS server - if (ptr && (ptr != cr->resrec.rDNSServer)) - { - // As the DNSServers for this cache record is not the same anymore, we don't - // want any new questions to pick this old value - if (cr->CRActiveQuestion == mDNSNULL) - { - LogInfo("uDNS_SetupDNSConfig: Purging Resourcerecord %s", CRDisplayString(m, cr)); - mDNS_PurgeCacheResourceRecord(m, cr); - } - else - { - LogInfo("uDNS_SetupDNSConfig: Purging/Reconfirming Resourcerecord %s", CRDisplayString(m, cr)); - PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNSfalse); - } - } - } - // Update our qDNSServer pointers before we go and free the DNSServer object memory - for (q = m->Questions; q; q=q->next) - if (!mDNSOpaque16IsZero(q->TargetQID)) - { - DNSServer *s, *t; - DNSQuestion *qptr; - if (q->DuplicateOf) continue; - SetValidDNSServers(m, q); - q->triedAllServersOnce = 0; - s = GetServerForQuestion(m, q); - t = q->qDNSServer; - if (t != s) - { - // If DNS Server for this question has changed, reactivate it - debugf("uDNS_SetupDNSConfig: Updating DNS Server from %p %#a:%d (%##s) to %p %#a:%d (%##s) for %##s (%s)", - t, t ? &t->addr : mDNSNULL, mDNSVal16(t ? t->port : zeroIPPort), t ? t->domain.c : (mDNSu8*)"", - s, s ? &s->addr : mDNSNULL, mDNSVal16(s ? s->port : zeroIPPort), s ? s->domain.c : (mDNSu8*)"", - q->qname.c, DNSTypeName(q->qtype)); - - // After we reset the DNSServer pointer on the cache records here, three things could happen: - // - // 1) The query gets sent out and when the actual response comes back later it is possible - // that the response has the same RDATA, in which case we update our cache entry. - // If the response is different, then the entry will expire and a new entry gets added. - // For the latter case to generate a RMV followed by ADD events, we need to reset the DNS - // server here to match the question and the cache record. - // - // 2) We might have marked the cache entries for purge above and for us to be able to generate the RMV - // events for the questions, the DNSServer on the question should match the Cache Record - // - // 3) We might have marked the cache entries for reconfirm above, for which we send the query out which is - // the same as the first case above. - - DNSServerChangeForQuestion(m, q, s); - q->unansweredQueries = 0; - // We still need to pick a new DNSServer for the questions that have been - // suppressed, but it is wrong to activate the query as DNS server change - // could not possibly change the status of SuppressUnusable questions - if (!QuerySuppressed(q)) - { - debugf("uDNS_SetupDNSConfig: Activating query %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); - ActivateUnicastQuery(m, q, mDNStrue); - // ActivateUnicastQuery is called for duplicate questions also as it does something - // special for AutoTunnel questions - for (qptr = q->next ; qptr; qptr = qptr->next) - { - if (qptr->DuplicateOf == q) ActivateUnicastQuery(m, qptr, mDNStrue); - } - } - } - else - { - debugf("uDNS_SetupDNSConfig: Not Updating DNS server question %p %##s (%s) DNS server %#a:%d %p %d", - q, q->qname.c, DNSTypeName(q->qtype), t ? &t->addr : mDNSNULL, mDNSVal16(t ? t->port : zeroIPPort), q->DuplicateOf, q->SuppressUnusable); - for (qptr = q->next ; qptr; qptr = qptr->next) - if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; } - } - } - - while (*p) - { - if (((*p)->flags & DNSServer_FlagDelete) != 0) - { - // Scan our cache, looking for uDNS records that we would have queried this server for. - // We reconfirm any records that match, because in this world of split DNS, firewalls, etc. - // different DNS servers can give different answers to the same question. - ptr = *p; - FORALL_CACHERECORDS(slot, cg, cr) - { - if (cr->resrec.InterfaceID) continue; - if (cr->resrec.rDNSServer == ptr) - { - // If we don't have an active question for this cache record, neither Purge can - // generate RMV events nor Reconfirm can send queries out. Just set the DNSServer - // pointer on the record NULL so that we don't point to freed memory (We might dereference - // DNSServer pointers from resource record for logging purposes). - // - // If there is an active question, point to its DNSServer as long as it does not point to the - // freed one. We already went through the questions above and made them point at either the - // new server or NULL if there is no server and also affected the cache entries that match - // this question. Hence, whenever we hit a resource record with a DNSServer that is just - // about to be deleted, we should never have an active question. The code below just tries to - // be careful logging messages if we ever hit this case. - - if (cr->CRActiveQuestion) - { - DNSQuestion *qptr = cr->CRActiveQuestion; - if (qptr->qDNSServer == mDNSNULL) - LogMsg("uDNS_SetupDNSConfig: Cache Record %s match: Active question %##s (%s) with DNSServer Address NULL, Server to be deleted %#a", - CRDisplayString(m, cr), qptr->qname.c, DNSTypeName(qptr->qtype), &ptr->addr); - else - LogMsg("uDNS_SetupDNSConfig: Cache Record %s match: Active question %##s (%s) DNSServer Address %#a, Server to be deleted %#a", - CRDisplayString(m, cr), qptr->qname.c, DNSTypeName(qptr->qtype), &qptr->qDNSServer->addr, &ptr->addr); - - if (qptr->qDNSServer == ptr) - { - qptr->validDNSServers = zeroOpaque64; - qptr->qDNSServer = mDNSNULL; - cr->resrec.rDNSServer = mDNSNULL; - } - else - { - cr->resrec.rDNSServer = qptr->qDNSServer; - } - } - else - { - LogInfo("uDNS_SetupDNSConfig: Cache Record %##s has no Active question, Record's DNSServer Address %#a, Server to be deleted %#a", - cr->resrec.name, &cr->resrec.rDNSServer->addr, &ptr->addr); - cr->resrec.rDNSServer = mDNSNULL; - } - - PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNStrue); - } - } - *p = (*p)->next; - debugf("uDNS_SetupDNSConfig: Deleting server %p %#a:%d (%##s)", ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c); - mDNSPlatformMemFree(ptr); - NumUnicastDNSServers--; - } - else - { - (*p)->flags &= ~DNSServer_FlagNew; - p = &(*p)->next; - } - } - - // If we now have no DNS servers at all and we used to have some, then immediately purge all unicast cache records (including for LLQs). - // This is important for giving prompt remove events when the user disconnects the Ethernet cable or turns off wireless. - // Otherwise, stale data lingers for 5-10 seconds, which is not the user-experience people expect from Bonjour. - // Similarly, if we now have some DNS servers and we used to have none, we want to purge any fake negative results we may have generated. - if ((m->DNSServers != mDNSNULL) != (oldServers != mDNSNULL)) - { - int count = 0; - FORALL_CACHERECORDS(slot, cg, cr) if (!cr->resrec.InterfaceID) { mDNS_PurgeCacheResourceRecord(m, cr); count++; } - LogInfo("uDNS_SetupDNSConfig: %s available; purged %d unicast DNS records from cache", - m->DNSServers ? "DNS server became" : "No DNS servers", count); - - // Force anything that needs to get zone data to get that information again - RestartRecordGetZoneData(m); - } - - // Did our FQDN change? - if (!SameDomainName(&fqdn, &m->FQDN)) - { - if (m->FQDN.c[0]) mDNS_RemoveDynDNSHostName(m, &m->FQDN); - - AssignDomainName(&m->FQDN, &fqdn); - - if (m->FQDN.c[0]) - { - mDNSPlatformDynDNSHostNameStatusChanged(&m->FQDN, 1); - mDNS_AddDynDNSHostName(m, &m->FQDN, DynDNSHostNameCallback, mDNSNULL); - } - } - - mDNS_Unlock(m); - - // handle router and primary interface changes - v4 = v6 = r = zeroAddr; - v4.type = r.type = mDNSAddrType_IPv4; - - if (mDNSPlatformGetPrimaryInterface(m, &v4, &v6, &r) == mStatus_NoError && !mDNSv4AddressIsLinkLocal(&v4.ip.v4)) - { - mDNS_SetPrimaryInterfaceInfo(m, - !mDNSIPv4AddressIsZero(v4.ip.v4) ? &v4 : mDNSNULL, - !mDNSIPv6AddressIsZero(v6.ip.v6) ? &v6 : mDNSNULL, - !mDNSIPv4AddressIsZero(r .ip.v4) ? &r : mDNSNULL); - } - else - { - mDNS_SetPrimaryInterfaceInfo(m, mDNSNULL, mDNSNULL, mDNSNULL); - if (m->FQDN.c[0]) mDNSPlatformDynDNSHostNameStatusChanged(&m->FQDN, 1); // Set status to 1 to indicate temporary failure - } - - debugf("uDNS_SetupDNSConfig: number of unicast DNS servers %d", NumUnicastDNSServers); - return mStatus_NoError; - } +{ + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *cr; + + mDNSAddr v4, v6, r; + domainname fqdn; + DNSServer *ptr, **p = &m->DNSServers; + const DNSServer *oldServers = m->DNSServers; + DNSQuestion *q; + McastResolver *mr, **mres = &m->McastResolvers; + + debugf("uDNS_SetupDNSConfig: entry"); + + // Let the platform layer get the current DNS information + // The m->StartWABQueries is set when we get the first domain enumeration query (no need to hit the network + // with domain enumeration queries until we actually need that information). Even if it is not set, we still + // need to setup the search domains so that we can append them to queries that need them. + + uDNS_SetupSearchDomains(m, m->StartWABQueries ? UDNS_START_WAB_QUERY : 0); + + mDNS_Lock(m); + + for (ptr = m->DNSServers; ptr; ptr = ptr->next) + { + ptr->penaltyTime = 0; + ptr->flags |= DNSServer_FlagDelete; + } + + // We handle the mcast resolvers here itself as mDNSPlatformSetDNSConfig looks at + // mcast resolvers. Today we get both mcast and ucast configuration using the same + // API + for (mr = m->McastResolvers; mr; mr = mr->next) + mr->flags |= McastResolver_FlagDelete; + + mDNSPlatformSetDNSConfig(m, mDNStrue, mDNSfalse, &fqdn, mDNSNULL, mDNSNULL); + + // For now, we just delete the mcast resolvers. We don't deal with cache or + // questions here. Neither question nor cache point to mcast resolvers. Questions + // do inherit the timeout values from mcast resolvers. But we don't bother + // affecting them as they never change. + while (*mres) + { + if (((*mres)->flags & DNSServer_FlagDelete) != 0) + { + mr = *mres; + *mres = (*mres)->next; + debugf("uDNS_SetupDNSConfig: Deleting mcast resolver %##s", mr, mr->domain.c); + mDNSPlatformMemFree(mr); + } + else + { + (*mres)->flags &= ~McastResolver_FlagNew; + mres = &(*mres)->next; + } + } + + // Update our qDNSServer pointers before we go and free the DNSServer object memory + // + // All non-scoped resolvers share the same resGroupID. At no point in time a cache entry using DNSServer + // from scoped resolver will be used to answer non-scoped questions and vice versa, as scoped and non-scoped + // resolvers don't share the same resGroupID. A few examples to describe the interaction with how we pick + // DNSServers and flush the cache. + // + // - A non-scoped question picks DNSServer X, creates a cache entry with X. If a new resolver gets added later that + // is a better match, we pick the new DNSServer for the question and activate the unicast query. We may or may not + // flush the cache (See PurgeOrReconfirmCacheRecord). In either case, we don't change the cache record's DNSServer + // pointer immediately (qDNSServer and rDNSServer may be different but still share the same resGroupID). If we don't + // flush the cache immediately, the record's rDNSServer pointer will be updated (in mDNSCoreReceiveResponse) + // later when we get the response. If we purge the cache, we still deliver a RMV when it is purged even though + // we don't update the cache record's DNSServer pointer to match the question's DNSSever, as they both point to + // the same resGroupID. + // + // Note: If the new DNSServer comes back with a different response than what we have in the cache, we will deliver a RMV + // of the old followed by ADD of the new records. + // + // - A non-scoped question picks DNSServer X, creates a cache entry with X. If the resolver gets removed later, we will + // pick a new DNSServer for the question which may or may not be NULL and set the cache record's pointer to the same + // as in question's qDNSServer if the cache record is not flushed. If there is no active question, it will be set to NULL. + // + // - Two questions scoped and non-scoped for the same name will pick two different DNSServer and will end up creating separate + // cache records and as the resGroupID is different, you can't use the cache record from the scoped DNSServer to answer the + // non-scoped question and vice versa. + // + for (q = m->Questions; q; q=q->next) + if (!mDNSOpaque16IsZero(q->TargetQID)) + { + DNSServer *s, *t; + DNSQuestion *qptr; + if (q->DuplicateOf) continue; + SetValidDNSServers(m, q); + q->triedAllServersOnce = 0; + s = GetServerForQuestion(m, q); + t = q->qDNSServer; + if (t != s) + { + // If DNS Server for this question has changed, reactivate it + LogInfo("uDNS_SetupDNSConfig: Updating DNS Server from %#a:%d (%##s) to %#a:%d (%##s) for question %##s (%s) (scope:%p)", + t ? &t->addr : mDNSNULL, mDNSVal16(t ? t->port : zeroIPPort), t ? t->domain.c : (mDNSu8*)"", + s ? &s->addr : mDNSNULL, mDNSVal16(s ? s->port : zeroIPPort), s ? s->domain.c : (mDNSu8*)"", + q->qname.c, DNSTypeName(q->qtype), q->InterfaceID); + + DNSServerChangeForQuestion(m, q, s); + q->unansweredQueries = 0; + // We still need to pick a new DNSServer for the questions that have been + // suppressed, but it is wrong to activate the query as DNS server change + // could not possibly change the status of SuppressUnusable questions + if (!QuerySuppressed(q)) + { + debugf("uDNS_SetupDNSConfig: Activating query %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + ActivateUnicastQuery(m, q, mDNStrue); + // ActivateUnicastQuery is called for duplicate questions also as it does something + // special for AutoTunnel questions + for (qptr = q->next ; qptr; qptr = qptr->next) + { + if (qptr->DuplicateOf == q) ActivateUnicastQuery(m, qptr, mDNStrue); + } + } + } + else + { + debugf("uDNS_SetupDNSConfig: Not Updating DNS server question %p %##s (%s) DNS server %#a:%d %p %d", + q, q->qname.c, DNSTypeName(q->qtype), t ? &t->addr : mDNSNULL, mDNSVal16(t ? t->port : zeroIPPort), q->DuplicateOf, q->SuppressUnusable); + for (qptr = q->next ; qptr; qptr = qptr->next) + if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; } + } + } + + FORALL_CACHERECORDS(slot, cg, cr) + { + if (cr->resrec.InterfaceID) continue; + // We just mark them for purge or reconfirm. + // + // The new DNSServer may be a scoped or non-scoped one. We use the active question's + // InterfaceID for looking up the right DNS server + ptr = GetServerForName(m, cr->resrec.name, cr->CRActiveQuestion ? cr->CRActiveQuestion->InterfaceID : mDNSNULL); + + // Purge or Reconfirm if this cache entry would use the new DNS server + if (ptr && (ptr != cr->resrec.rDNSServer)) + { + // As the DNSServers for this cache record is not the same anymore, we don't + // want any new questions to pick this old value. If there is no active question, + // we can't possibly re-confirm, so purge in that case. + if (cr->CRActiveQuestion == mDNSNULL) + { + LogInfo("uDNS_SetupDNSConfig: Purging Resourcerecord %s, New DNS server %#a , Old DNS server %#a", CRDisplayString(m, cr), + &ptr->addr, (cr->resrec.rDNSServer != mDNSNULL ? &cr->resrec.rDNSServer->addr : mDNSNULL)); + mDNS_PurgeCacheResourceRecord(m, cr); + } + else + { + LogInfo("uDNS_SetupDNSConfig: Purging/Reconfirming Resourcerecord %s, New DNS server %#a, Old DNS server %#a", CRDisplayString(m, cr), + &ptr->addr, (cr->resrec.rDNSServer != mDNSNULL ? &cr->resrec.rDNSServer->addr : mDNSNULL)); + PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNSfalse); + } + } + } + + while (*p) + { + if (((*p)->flags & DNSServer_FlagDelete) != 0) + { + // Scan our cache, looking for uDNS records that we would have queried this server for. + // We reconfirm any records that match, because in this world of split DNS, firewalls, etc. + // different DNS servers can give different answers to the same question. + ptr = *p; + FORALL_CACHERECORDS(slot, cg, cr) + { + if (cr->resrec.InterfaceID) continue; + if (cr->resrec.rDNSServer == ptr) + { + // If we don't have an active question for this cache record, neither Purge can + // generate RMV events nor Reconfirm can send queries out. Just set the DNSServer + // pointer on the record NULL so that we don't point to freed memory (We might dereference + // DNSServer pointers from resource record for logging purposes). + // + // If there is an active question, point to its DNSServer as long as it does not point to the + // freed one. We already went through the questions above and made them point at either the + // new server or NULL if there is no server. + + if (cr->CRActiveQuestion) + { + DNSQuestion *qptr = cr->CRActiveQuestion; + + if (qptr->qDNSServer == ptr) + { + LogMsg("uDNS_SetupDNSConfig: ERROR!! Cache Record %s Active question %##s (%s) (scope:%p) poining to DNSServer Address %#a" + " to be freed", CRDisplayString(m, cr), qptr->qname.c, DNSTypeName(qptr->qtype), qptr->InterfaceID, &ptr->addr); + qptr->validDNSServers = zeroOpaque64; + qptr->qDNSServer = mDNSNULL; + cr->resrec.rDNSServer = mDNSNULL; + } + else + { + LogInfo("uDNS_SetupDNSConfig: Cache Record %s, Active question %##s (%s) (scope:%p), pointing to DNSServer %#a (to be deleted)," + " resetting to question's DNSServer Address %#a", CRDisplayString(m, cr), qptr->qname.c, DNSTypeName(qptr->qtype), + qptr->InterfaceID, &ptr->addr, (qptr->qDNSServer ? &qptr->qDNSServer->addr : mDNSNULL)); + cr->resrec.rDNSServer = qptr->qDNSServer; + } + } + else + { + LogInfo("uDNS_SetupDNSConfig: Cache Record %##s has no Active question, Record's DNSServer Address %#a, Server to be deleted %#a", + cr->resrec.name, &cr->resrec.rDNSServer->addr, &ptr->addr); + cr->resrec.rDNSServer = mDNSNULL; + } + + PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNStrue); + } + } + *p = (*p)->next; + debugf("uDNS_SetupDNSConfig: Deleting server %p %#a:%d (%##s)", ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c); + mDNSPlatformMemFree(ptr); + NumUnicastDNSServers--; + } + else + { + (*p)->flags &= ~DNSServer_FlagNew; + p = &(*p)->next; + } + } + + // If we now have no DNS servers at all and we used to have some, then immediately purge all unicast cache records (including for LLQs). + // This is important for giving prompt remove events when the user disconnects the Ethernet cable or turns off wireless. + // Otherwise, stale data lingers for 5-10 seconds, which is not the user-experience people expect from Bonjour. + // Similarly, if we now have some DNS servers and we used to have none, we want to purge any fake negative results we may have generated. + if ((m->DNSServers != mDNSNULL) != (oldServers != mDNSNULL)) + { + int count = 0; + FORALL_CACHERECORDS(slot, cg, cr) + { + if (!cr->resrec.InterfaceID) + { + mDNS_PurgeCacheResourceRecord(m, cr); + count++; + } + } + LogInfo("uDNS_SetupDNSConfig: %s available; purged %d unicast DNS records from cache", + m->DNSServers ? "DNS server became" : "No DNS servers", count); + + // Force anything that needs to get zone data to get that information again + RestartRecordGetZoneData(m); + } + + // Did our FQDN change? + if (!SameDomainName(&fqdn, &m->FQDN)) + { + if (m->FQDN.c[0]) mDNS_RemoveDynDNSHostName(m, &m->FQDN); + + AssignDomainName(&m->FQDN, &fqdn); + + if (m->FQDN.c[0]) + { + mDNSPlatformDynDNSHostNameStatusChanged(&m->FQDN, 1); + mDNS_AddDynDNSHostName(m, &m->FQDN, DynDNSHostNameCallback, mDNSNULL); + } + } + + mDNS_Unlock(m); + + // handle router and primary interface changes + v4 = v6 = r = zeroAddr; + v4.type = r.type = mDNSAddrType_IPv4; + + if (mDNSPlatformGetPrimaryInterface(m, &v4, &v6, &r) == mStatus_NoError && !mDNSv4AddressIsLinkLocal(&v4.ip.v4)) + { + mDNS_SetPrimaryInterfaceInfo(m, + !mDNSIPv4AddressIsZero(v4.ip.v4) ? &v4 : mDNSNULL, + !mDNSIPv6AddressIsZero(v6.ip.v6) ? &v6 : mDNSNULL, + !mDNSIPv4AddressIsZero(r.ip.v4) ? &r : mDNSNULL); + } + else + { + mDNS_SetPrimaryInterfaceInfo(m, mDNSNULL, mDNSNULL, mDNSNULL); + if (m->FQDN.c[0]) mDNSPlatformDynDNSHostNameStatusChanged(&m->FQDN, 1); // Set status to 1 to indicate temporary failure + } + + debugf("uDNS_SetupDNSConfig: number of unicast DNS servers %d", NumUnicastDNSServers); + return mStatus_NoError; +} mDNSexport void mDNSCoreInitComplete(mDNS *const m, mStatus result) - { - m->mDNSPlatformStatus = result; - if (m->MainCallback) - { - mDNS_Lock(m); - mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback - m->MainCallback(m, mStatus_NoError); - mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again - mDNS_Unlock(m); - } - } +{ + m->mDNSPlatformStatus = result; + if (m->MainCallback) + { + mDNS_Lock(m); + mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback + m->MainCallback(m, mStatus_NoError); + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + mDNS_Unlock(m); + } +} mDNSlocal void DeregLoop(mDNS *const m, AuthRecord *const start) - { - m->CurrentRecord = start; - while (m->CurrentRecord) - { - AuthRecord *rr = m->CurrentRecord; - LogInfo("DeregLoop: %s deregistration for %p %02X %s", - (rr->resrec.RecordType != kDNSRecordTypeDeregistering) ? "Initiating " : "Accelerating", - rr, rr->resrec.RecordType, ARDisplayString(m, rr)); - if (rr->resrec.RecordType != kDNSRecordTypeDeregistering) - mDNS_Deregister_internal(m, rr, mDNS_Dereg_rapid); - else if (rr->AnnounceCount > 1) - { - rr->AnnounceCount = 1; - rr->LastAPTime = m->timenow - rr->ThisAPInterval; - } - // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because - // new records could have been added to the end of the list as a result of that call. - if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now - m->CurrentRecord = rr->next; - } - } +{ + m->CurrentRecord = start; + while (m->CurrentRecord) + { + AuthRecord *rr = m->CurrentRecord; + LogInfo("DeregLoop: %s deregistration for %p %02X %s", + (rr->resrec.RecordType != kDNSRecordTypeDeregistering) ? "Initiating " : "Accelerating", + rr, rr->resrec.RecordType, ARDisplayString(m, rr)); + if (rr->resrec.RecordType != kDNSRecordTypeDeregistering) + mDNS_Deregister_internal(m, rr, mDNS_Dereg_rapid); + else if (rr->AnnounceCount > 1) + { + rr->AnnounceCount = 1; + rr->LastAPTime = m->timenow - rr->ThisAPInterval; + } + // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because + // new records could have been added to the end of the list as a result of that call. + if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now + m->CurrentRecord = rr->next; + } +} mDNSexport void mDNS_StartExit(mDNS *const m) - { - NetworkInterfaceInfo *intf; - AuthRecord *rr; +{ + NetworkInterfaceInfo *intf; + AuthRecord *rr; - mDNS_Lock(m); + mDNS_Lock(m); - LogInfo("mDNS_StartExit"); - m->ShutdownTime = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 5); + LogInfo("mDNS_StartExit"); + m->ShutdownTime = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 5); - mDNSCoreBeSleepProxyServer_internal(m, 0, 0, 0, 0); + mDNSCoreBeSleepProxyServer_internal(m, 0, 0, 0, 0, 0); #if APPLE_OSX_mDNSResponder -#if ! NO_WCF - CHECK_WCF_FUNCTION(WCFConnectionDealloc) - { - if (m->WCF) WCFConnectionDealloc((WCFConnection *)m->WCF); - } +#if !NO_WCF + CHECK_WCF_FUNCTION(WCFConnectionDealloc) + { + if (m->WCF) WCFConnectionDealloc((WCFConnection *)m->WCF); + } #endif #endif #ifndef UNICAST_DISABLED - { - SearchListElem *s; - SuspendLLQs(m); - // Don't need to do SleepRecordRegistrations() here - // because we deregister all records and services later in this routine - while (m->Hostnames) mDNS_RemoveDynDNSHostName(m, &m->Hostnames->fqdn); - - // For each member of our SearchList, deregister any records it may have created, and cut them from the list. - // Otherwise they'll be forcibly deregistered for us (without being cut them from the appropriate list) - // and we may crash because the list still contains dangling pointers. - for (s = SearchList; s; s = s->next) - while (s->AuthRecs) - { - ARListElem *dereg = s->AuthRecs; - s->AuthRecs = s->AuthRecs->next; - mDNS_Deregister_internal(m, &dereg->ar, mDNS_Dereg_normal); // Memory will be freed in the FreeARElemCallback - } - } + { + SearchListElem *s; + SuspendLLQs(m); + // Don't need to do SleepRecordRegistrations() here + // because we deregister all records and services later in this routine + while (m->Hostnames) mDNS_RemoveDynDNSHostName(m, &m->Hostnames->fqdn); + + // For each member of our SearchList, deregister any records it may have created, and cut them from the list. + // Otherwise they'll be forcibly deregistered for us (without being cut them from the appropriate list) + // and we may crash because the list still contains dangling pointers. + for (s = SearchList; s; s = s->next) + while (s->AuthRecs) + { + ARListElem *dereg = s->AuthRecs; + s->AuthRecs = s->AuthRecs->next; + mDNS_Deregister_internal(m, &dereg->ar, mDNS_Dereg_normal); // Memory will be freed in the FreeARElemCallback + } + } #endif - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->Advertise) - DeadvertiseInterface(m, intf); - - // Shut down all our active NAT Traversals - while (m->NATTraversals) - { - NATTraversalInfo *t = m->NATTraversals; - mDNS_StopNATOperation_internal(m, t); // This will cut 't' from the list, thereby advancing m->NATTraversals in the process - - // After stopping the NAT Traversal, we zero out the fields. - // This has particularly important implications for our AutoTunnel records -- - // when we deregister our AutoTunnel records below, we don't want their mStatus_MemFree - // handlers to just turn around and attempt to re-register those same records. - // Clearing t->ExternalPort/t->RequestedPort will cause the mStatus_MemFree callback handlers - // to not do this. - t->ExternalAddress = zerov4Addr; - t->ExternalPort = zeroIPPort; - t->RequestedPort = zeroIPPort; - t->Lifetime = 0; - t->Result = mStatus_NoError; - } - - // Make sure there are nothing but deregistering records remaining in the list - if (m->CurrentRecord) - LogMsg("mDNS_StartExit: ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); - - // We're in the process of shutting down, so queries, etc. are no longer available. - // Consequently, determining certain information, e.g. the uDNS update server's IP - // address, will not be possible. The records on the main list are more likely to - // already contain such information, so we deregister the duplicate records first. - LogInfo("mDNS_StartExit: Deregistering duplicate resource records"); - DeregLoop(m, m->DuplicateRecords); - LogInfo("mDNS_StartExit: Deregistering resource records"); - DeregLoop(m, m->ResourceRecords); - - // If we scheduled a response to send goodbye packets, we set NextScheduledResponse to now. Normally when deregistering records, - // we allow up to 100ms delay (to help improve record grouping) but when shutting down we don't want any such delay. - if (m->NextScheduledResponse - m->timenow < mDNSPlatformOneSecond) - { - m->NextScheduledResponse = m->timenow; - m->SuppressSending = 0; - } - - if (m->ResourceRecords) LogInfo("mDNS_StartExit: Sending final record deregistrations"); - else LogInfo("mDNS_StartExit: No deregistering records remain"); - - for (rr = m->DuplicateRecords; rr; rr = rr->next) - LogMsg("mDNS_StartExit: Should not still have Duplicate Records remaining: %02X %s", rr->resrec.RecordType, ARDisplayString(m, rr)); - - // If any deregistering records remain, send their deregistration announcements before we exit - if (m->mDNSPlatformStatus != mStatus_NoError) DiscardDeregistrations(m); - - mDNS_Unlock(m); - - LogInfo("mDNS_StartExit: done"); - } + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->Advertise) + DeadvertiseInterface(m, intf); + + // Shut down all our active NAT Traversals + while (m->NATTraversals) + { + NATTraversalInfo *t = m->NATTraversals; + mDNS_StopNATOperation_internal(m, t); // This will cut 't' from the list, thereby advancing m->NATTraversals in the process + + // After stopping the NAT Traversal, we zero out the fields. + // This has particularly important implications for our AutoTunnel records -- + // when we deregister our AutoTunnel records below, we don't want their mStatus_MemFree + // handlers to just turn around and attempt to re-register those same records. + // Clearing t->ExternalPort/t->RequestedPort will cause the mStatus_MemFree callback handlers + // to not do this. + t->ExternalAddress = zerov4Addr; + t->ExternalPort = zeroIPPort; + t->RequestedPort = zeroIPPort; + t->Lifetime = 0; + t->Result = mStatus_NoError; + } + + // Make sure there are nothing but deregistering records remaining in the list + if (m->CurrentRecord) + LogMsg("mDNS_StartExit: ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + + // We're in the process of shutting down, so queries, etc. are no longer available. + // Consequently, determining certain information, e.g. the uDNS update server's IP + // address, will not be possible. The records on the main list are more likely to + // already contain such information, so we deregister the duplicate records first. + LogInfo("mDNS_StartExit: Deregistering duplicate resource records"); + DeregLoop(m, m->DuplicateRecords); + LogInfo("mDNS_StartExit: Deregistering resource records"); + DeregLoop(m, m->ResourceRecords); + + // If we scheduled a response to send goodbye packets, we set NextScheduledResponse to now. Normally when deregistering records, + // we allow up to 100ms delay (to help improve record grouping) but when shutting down we don't want any such delay. + if (m->NextScheduledResponse - m->timenow < mDNSPlatformOneSecond) + { + m->NextScheduledResponse = m->timenow; + m->SuppressSending = 0; + } + + if (m->ResourceRecords) LogInfo("mDNS_StartExit: Sending final record deregistrations"); + else LogInfo("mDNS_StartExit: No deregistering records remain"); + + for (rr = m->DuplicateRecords; rr; rr = rr->next) + LogMsg("mDNS_StartExit: Should not still have Duplicate Records remaining: %02X %s", rr->resrec.RecordType, ARDisplayString(m, rr)); + + // If any deregistering records remain, send their deregistration announcements before we exit + if (m->mDNSPlatformStatus != mStatus_NoError) DiscardDeregistrations(m); + + mDNS_Unlock(m); + + LogInfo("mDNS_StartExit: done"); +} mDNSexport void mDNS_FinalExit(mDNS *const m) - { - mDNSu32 rrcache_active = 0; - mDNSu32 rrcache_totalused = 0; - mDNSu32 slot; - AuthRecord *rr; - - LogInfo("mDNS_FinalExit: mDNSPlatformClose"); - mDNSPlatformClose(m); - - rrcache_totalused = m->rrcache_totalused; - for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) - { - while (m->rrcache_hash[slot]) - { - CacheGroup *cg = m->rrcache_hash[slot]; - while (cg->members) - { - CacheRecord *cr = cg->members; - cg->members = cg->members->next; - if (cr->CRActiveQuestion) rrcache_active++; - ReleaseCacheRecord(m, cr); - } - cg->rrcache_tail = &cg->members; - ReleaseCacheGroup(m, &m->rrcache_hash[slot]); - } - } - debugf("mDNS_FinalExit: 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); - - for (rr = m->ResourceRecords; rr; rr = rr->next) - LogMsg("mDNS_FinalExit failed to send goodbye for: %p %02X %s", rr, rr->resrec.RecordType, ARDisplayString(m, rr)); - - LogInfo("mDNS_FinalExit: done"); - } +{ + mDNSu32 rrcache_active = 0; + mDNSu32 rrcache_totalused = 0; + mDNSu32 slot; + AuthRecord *rr; + + LogInfo("mDNS_FinalExit: mDNSPlatformClose"); + mDNSPlatformClose(m); + + rrcache_totalused = m->rrcache_totalused; + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + { + while (m->rrcache_hash[slot]) + { + CacheGroup *cg = m->rrcache_hash[slot]; + while (cg->members) + { + CacheRecord *cr = cg->members; + cg->members = cg->members->next; + if (cr->CRActiveQuestion) rrcache_active++; + ReleaseCacheRecord(m, cr); + } + cg->rrcache_tail = &cg->members; + ReleaseCacheGroup(m, &m->rrcache_hash[slot]); + } + } + debugf("mDNS_FinalExit: 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); + + for (rr = m->ResourceRecords; rr; rr = rr->next) + LogMsg("mDNS_FinalExit failed to send goodbye for: %p %02X %s", rr, rr->resrec.RecordType, ARDisplayString(m, rr)); + + LogInfo("mDNS_FinalExit: done"); +} diff --git a/mDNSCore/mDNSDebug.h b/mDNSCore/mDNSDebug.h index 07647b5..7b6c2c6 100755 --- a/mDNSCore/mDNSDebug.h +++ b/mDNSCore/mDNSDebug.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -37,13 +37,13 @@ #define MDNS_CHECK_PRINTF_STYLE_FUNCTIONS 0 typedef enum - { - MDNS_LOG_MSG, - MDNS_LOG_OPERATION, - MDNS_LOG_SPS, - MDNS_LOG_INFO, - MDNS_LOG_DEBUG, - } mDNSLogLevel_t; +{ + MDNS_LOG_MSG, + MDNS_LOG_OPERATION, + MDNS_LOG_SPS, + MDNS_LOG_INFO, + MDNS_LOG_DEBUG, +} mDNSLogLevel_t; // Set this symbol to 1 to answer remote queries for our Address, reverse mapping PTR, and HINFO records #define ANSWER_REMOTE_HOSTNAME_QUERIES 0 @@ -64,62 +64,62 @@ typedef enum #endif #ifdef __cplusplus - extern "C" { +extern "C" { #endif // Variable argument macro support. Use ANSI C99 __VA_ARGS__ where possible. Otherwise, use the next best thing. #if (defined(__GNUC__)) - #if ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 2))) - #define MDNS_C99_VA_ARGS 1 - #define MDNS_GNU_VA_ARGS 0 - #else - #define MDNS_C99_VA_ARGS 0 - #define MDNS_GNU_VA_ARGS 1 - #endif - #define MDNS_HAS_VA_ARG_MACROS 1 + #if ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 2))) + #define MDNS_C99_VA_ARGS 1 + #define MDNS_GNU_VA_ARGS 0 + #else + #define MDNS_C99_VA_ARGS 0 + #define MDNS_GNU_VA_ARGS 1 + #endif + #define MDNS_HAS_VA_ARG_MACROS 1 #elif (_MSC_VER >= 1400) // Visual Studio 2005 and later - #define MDNS_C99_VA_ARGS 1 - #define MDNS_GNU_VA_ARGS 0 - #define MDNS_HAS_VA_ARG_MACROS 1 + #define MDNS_C99_VA_ARGS 1 + #define MDNS_GNU_VA_ARGS 0 + #define MDNS_HAS_VA_ARG_MACROS 1 #elif (defined(__MWERKS__)) - #define MDNS_C99_VA_ARGS 1 - #define MDNS_GNU_VA_ARGS 0 - #define MDNS_HAS_VA_ARG_MACROS 1 + #define MDNS_C99_VA_ARGS 1 + #define MDNS_GNU_VA_ARGS 0 + #define MDNS_HAS_VA_ARG_MACROS 1 #else - #define MDNS_C99_VA_ARGS 0 - #define MDNS_GNU_VA_ARGS 0 - #define MDNS_HAS_VA_ARG_MACROS 0 + #define MDNS_C99_VA_ARGS 0 + #define MDNS_GNU_VA_ARGS 0 + #define MDNS_HAS_VA_ARG_MACROS 0 #endif #if (MDNS_HAS_VA_ARG_MACROS) - #if (MDNS_C99_VA_ARGS) - #define debug_noop( ... ) ((void)0) - #define LogMsg( ... ) LogMsgWithLevel(MDNS_LOG_MSG, __VA_ARGS__) - #define LogOperation( ... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_OPERATION, __VA_ARGS__); } while (0) - #define LogSPS( ... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_SPS, __VA_ARGS__); } while (0) - #define LogInfo( ... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_INFO, __VA_ARGS__); } while (0) - #elif (MDNS_GNU_VA_ARGS) - #define debug_noop( ARGS... ) ((void)0) - #define LogMsg( ARGS... ) LogMsgWithLevel(MDNS_LOG_MSG, ARGS) - #define LogOperation( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_OPERATION, ARGS); } while (0) - #define LogSPS( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_SPS, ARGS); } while (0) - #define LogInfo( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_INFO, ARGS); } while (0) - #else - #error Unknown variadic macros - #endif + #if (MDNS_C99_VA_ARGS) + #define debug_noop(... ) ((void)0) + #define LogMsg(... ) LogMsgWithLevel(MDNS_LOG_MSG, __VA_ARGS__) + #define LogOperation(... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_OPERATION, __VA_ARGS__);} while (0) + #define LogSPS(... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_SPS, __VA_ARGS__);} while (0) + #define LogInfo(... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_INFO, __VA_ARGS__);} while (0) + #elif (MDNS_GNU_VA_ARGS) + #define debug_noop( ARGS... ) ((void)0) + #define LogMsg( ARGS... ) LogMsgWithLevel(MDNS_LOG_MSG, ARGS) + #define LogOperation( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_OPERATION, ARGS);} while (0) + #define LogSPS( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_SPS, ARGS);} while (0) + #define LogInfo( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_INFO, ARGS);} while (0) + #else + #error Unknown variadic macros + #endif #else - // If your platform does not support variadic macros, you need to define the following variadic functions. - // See mDNSShared/mDNSDebug.c for sample implementation - #define debug_noop 1 ? (void)0 : (void) - #define LogMsg LogMsg_ - #define LogOperation (mDNS_LoggingEnabled == 0) ? ((void)0) : LogOperation_ - #define LogSPS (mDNS_LoggingEnabled == 0) ? ((void)0) : LogSPS_ - #define LogInfo (mDNS_LoggingEnabled == 0) ? ((void)0) : LogInfo_ - extern void LogMsg_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); - extern void LogOperation_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); - extern void LogSPS_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); - extern void LogInfo_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); +// If your platform does not support variadic macros, you need to define the following variadic functions. +// See mDNSShared/mDNSDebug.c for sample implementation + #define debug_noop 1 ? (void)0 : (void) + #define LogMsg LogMsg_ + #define LogOperation (mDNS_LoggingEnabled == 0) ? ((void)0) : LogOperation_ + #define LogSPS (mDNS_LoggingEnabled == 0) ? ((void)0) : LogSPS_ + #define LogInfo (mDNS_LoggingEnabled == 0) ? ((void)0) : LogInfo_ +extern void LogMsg_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); +extern void LogOperation_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); +extern void LogSPS_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); +extern void LogInfo_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); #endif #if MDNS_DEBUGMSGS @@ -136,9 +136,9 @@ extern void verbosedebugf_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1 #define verbosedebugf debug_noop #endif -extern int mDNS_LoggingEnabled; -extern int mDNS_PacketLoggingEnabled; -extern int mDNS_DebugMode; // If non-zero, LogMsg() writes to stderr instead of syslog +extern int mDNS_LoggingEnabled; +extern int mDNS_PacketLoggingEnabled; +extern int mDNS_DebugMode; // If non-zero, LogMsg() writes to stderr instead of syslog extern const char ProgramName[]; extern void LogMsgWithLevel(mDNSLogLevel_t logLevel, const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(2,3); @@ -158,7 +158,7 @@ extern void udns_validatelists(void *const v); #endif #ifdef __cplusplus - } +} #endif #endif diff --git a/mDNSCore/mDNSEmbeddedAPI.h b/mDNSCore/mDNSEmbeddedAPI.h index 7310dc5..f78e496 100755 --- a/mDNSCore/mDNSEmbeddedAPI.h +++ b/mDNSCore/mDNSEmbeddedAPI.h @@ -1,13 +1,13 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2012 Apple Computer, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -32,13 +32,13 @@ 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 @@ -64,7 +64,7 @@ #define va_arg(a, b) VA_ARG(a, b) #endif #else -#include // stdarg.h is required for for va_list support for the mDNS_vsnprintf declaration +#include // stdarg.h is required for for va_list support for the mDNS_vsnprintf declaration #endif #include "mDNSDebug.h" @@ -73,7 +73,7 @@ #endif #ifdef __cplusplus - extern "C" { +extern "C" { #endif // *************************************************************************** @@ -126,91 +126,91 @@ #pragma mark - DNS Resource Record class and type constants #endif -typedef enum // From RFC 1035 - { - kDNSClass_IN = 1, // Internet - kDNSClass_CS = 2, // CSNET - kDNSClass_CH = 3, // CHAOS - kDNSClass_HS = 4, // Hesiod - kDNSClass_NONE = 254, // Used in DNS UPDATE [RFC 2136] - - kDNSClass_Mask = 0x7FFF,// Multicast DNS uses the bottom 15 bits to identify the record class... - kDNSClass_UniqueRRSet = 0x8000,// ... and the top bit indicates that all other cached records are now invalid - - kDNSQClass_ANY = 255, // Not a DNS class, but a DNS query class, meaning "all classes" - kDNSQClass_UnicastResponse = 0x8000 // Top bit set in a question means "unicast response acceptable" - } DNS_ClassValues; - -typedef enum // From RFC 1035 - { - kDNSType_A = 1, // 1 Address - kDNSType_NS, // 2 Name Server - kDNSType_MD, // 3 Mail Destination - kDNSType_MF, // 4 Mail Forwarder - kDNSType_CNAME, // 5 Canonical Name - kDNSType_SOA, // 6 Start of Authority - kDNSType_MB, // 7 Mailbox - kDNSType_MG, // 8 Mail Group - kDNSType_MR, // 9 Mail Rename - kDNSType_NULL, // 10 NULL RR - kDNSType_WKS, // 11 Well-known-service - kDNSType_PTR, // 12 Domain name pointer - kDNSType_HINFO, // 13 Host information - kDNSType_MINFO, // 14 Mailbox information - kDNSType_MX, // 15 Mail Exchanger - kDNSType_TXT, // 16 Arbitrary text string - kDNSType_RP, // 17 Responsible person - kDNSType_AFSDB, // 18 AFS cell database - kDNSType_X25, // 19 X_25 calling address - kDNSType_ISDN, // 20 ISDN calling address - kDNSType_RT, // 21 Router - kDNSType_NSAP, // 22 NSAP address - kDNSType_NSAP_PTR, // 23 Reverse NSAP lookup (deprecated) - kDNSType_SIG, // 24 Security signature - kDNSType_KEY, // 25 Security key - kDNSType_PX, // 26 X.400 mail mapping - kDNSType_GPOS, // 27 Geographical position (withdrawn) - kDNSType_AAAA, // 28 IPv6 Address - kDNSType_LOC, // 29 Location Information - kDNSType_NXT, // 30 Next domain (security) - kDNSType_EID, // 31 Endpoint identifier - kDNSType_NIMLOC, // 32 Nimrod Locator - kDNSType_SRV, // 33 Service record - kDNSType_ATMA, // 34 ATM Address - kDNSType_NAPTR, // 35 Naming Authority PoinTeR - kDNSType_KX, // 36 Key Exchange - kDNSType_CERT, // 37 Certification record - kDNSType_A6, // 38 IPv6 Address (deprecated) - kDNSType_DNAME, // 39 Non-terminal DNAME (for IPv6) - kDNSType_SINK, // 40 Kitchen sink (experimental) - kDNSType_OPT, // 41 EDNS0 option (meta-RR) - kDNSType_APL, // 42 Address Prefix List - kDNSType_DS, // 43 Delegation Signer - kDNSType_SSHFP, // 44 SSH Key Fingerprint - kDNSType_IPSECKEY, // 45 IPSECKEY - kDNSType_RRSIG, // 46 RRSIG - kDNSType_NSEC, // 47 Denial of Existence - kDNSType_DNSKEY, // 48 DNSKEY - kDNSType_DHCID, // 49 DHCP Client Identifier - kDNSType_NSEC3, // 50 Hashed Authenticated Denial of Existence - kDNSType_NSEC3PARAM, // 51 Hashed Authenticated Denial of Existence - - kDNSType_HIP = 55, // 55 Host Identity Protocol - - kDNSType_SPF = 99, // 99 Sender Policy Framework for E-Mail - kDNSType_UINFO, // 100 IANA-Reserved - kDNSType_UID, // 101 IANA-Reserved - kDNSType_GID, // 102 IANA-Reserved - kDNSType_UNSPEC, // 103 IANA-Reserved - - kDNSType_TKEY = 249, // 249 Transaction key - kDNSType_TSIG, // 250 Transaction signature - kDNSType_IXFR, // 251 Incremental zone transfer - kDNSType_AXFR, // 252 Transfer zone of authority - kDNSType_MAILB, // 253 Transfer mailbox records - kDNSType_MAILA, // 254 Transfer mail agent records - kDNSQType_ANY // Not a DNS type, but a DNS query type, meaning "all types" - } DNS_TypeValues; +typedef enum // From RFC 1035 +{ + kDNSClass_IN = 1, // Internet + kDNSClass_CS = 2, // CSNET + kDNSClass_CH = 3, // CHAOS + kDNSClass_HS = 4, // Hesiod + kDNSClass_NONE = 254, // Used in DNS UPDATE [RFC 2136] + + kDNSClass_Mask = 0x7FFF, // Multicast DNS uses the bottom 15 bits to identify the record class... + kDNSClass_UniqueRRSet = 0x8000, // ... and the top bit indicates that all other cached records are now invalid + + kDNSQClass_ANY = 255, // Not a DNS class, but a DNS query class, meaning "all classes" + kDNSQClass_UnicastResponse = 0x8000 // Top bit set in a question means "unicast response acceptable" +} DNS_ClassValues; + +typedef enum // From RFC 1035 +{ + kDNSType_A = 1, // 1 Address + kDNSType_NS, // 2 Name Server + kDNSType_MD, // 3 Mail Destination + kDNSType_MF, // 4 Mail Forwarder + kDNSType_CNAME, // 5 Canonical Name + kDNSType_SOA, // 6 Start of Authority + kDNSType_MB, // 7 Mailbox + kDNSType_MG, // 8 Mail Group + kDNSType_MR, // 9 Mail Rename + kDNSType_NULL, // 10 NULL RR + kDNSType_WKS, // 11 Well-known-service + kDNSType_PTR, // 12 Domain name pointer + kDNSType_HINFO, // 13 Host information + kDNSType_MINFO, // 14 Mailbox information + kDNSType_MX, // 15 Mail Exchanger + kDNSType_TXT, // 16 Arbitrary text string + kDNSType_RP, // 17 Responsible person + kDNSType_AFSDB, // 18 AFS cell database + kDNSType_X25, // 19 X_25 calling address + kDNSType_ISDN, // 20 ISDN calling address + kDNSType_RT, // 21 Router + kDNSType_NSAP, // 22 NSAP address + kDNSType_NSAP_PTR, // 23 Reverse NSAP lookup (deprecated) + kDNSType_SIG, // 24 Security signature + kDNSType_KEY, // 25 Security key + kDNSType_PX, // 26 X.400 mail mapping + kDNSType_GPOS, // 27 Geographical position (withdrawn) + kDNSType_AAAA, // 28 IPv6 Address + kDNSType_LOC, // 29 Location Information + kDNSType_NXT, // 30 Next domain (security) + kDNSType_EID, // 31 Endpoint identifier + kDNSType_NIMLOC, // 32 Nimrod Locator + kDNSType_SRV, // 33 Service record + kDNSType_ATMA, // 34 ATM Address + kDNSType_NAPTR, // 35 Naming Authority PoinTeR + kDNSType_KX, // 36 Key Exchange + kDNSType_CERT, // 37 Certification record + kDNSType_A6, // 38 IPv6 Address (deprecated) + kDNSType_DNAME, // 39 Non-terminal DNAME (for IPv6) + kDNSType_SINK, // 40 Kitchen sink (experimental) + kDNSType_OPT, // 41 EDNS0 option (meta-RR) + kDNSType_APL, // 42 Address Prefix List + kDNSType_DS, // 43 Delegation Signer + kDNSType_SSHFP, // 44 SSH Key Fingerprint + kDNSType_IPSECKEY, // 45 IPSECKEY + kDNSType_RRSIG, // 46 RRSIG + kDNSType_NSEC, // 47 Denial of Existence + kDNSType_DNSKEY, // 48 DNSKEY + kDNSType_DHCID, // 49 DHCP Client Identifier + kDNSType_NSEC3, // 50 Hashed Authenticated Denial of Existence + kDNSType_NSEC3PARAM, // 51 Hashed Authenticated Denial of Existence + + kDNSType_HIP = 55, // 55 Host Identity Protocol + + kDNSType_SPF = 99, // 99 Sender Policy Framework for E-Mail + kDNSType_UINFO, // 100 IANA-Reserved + kDNSType_UID, // 101 IANA-Reserved + kDNSType_GID, // 102 IANA-Reserved + kDNSType_UNSPEC, // 103 IANA-Reserved + + kDNSType_TKEY = 249, // 249 Transaction key + kDNSType_TSIG, // 250 Transaction signature + kDNSType_IXFR, // 251 Incremental zone transfer + kDNSType_AXFR, // 252 Transfer zone of authority + kDNSType_MAILB, // 253 Transfer mailbox records + kDNSType_MAILA, // 254 Transfer mail agent records + kDNSQType_ANY // Not a DNS type, but a DNS query type, meaning "all types" +} DNS_TypeValues; // *************************************************************************** #if 0 @@ -220,9 +220,9 @@ typedef enum // From RFC 1035 // mDNS defines its own names for these common types to simplify portability across // multiple platforms that may each have their own (different) names for these types. -typedef int mDNSBool; -typedef signed char mDNSs8; -typedef unsigned char mDNSu8; +typedef int mDNSBool; +typedef signed char mDNSs8; +typedef unsigned char mDNSu8; typedef signed short mDNSs16; typedef unsigned short mDNSu16; @@ -238,11 +238,11 @@ typedef unsigned short mDNSu16; typedef signed int32 mDNSs32; typedef unsigned int32 mDNSu32; #elif defined(_LP64) || defined(__LP64__) -typedef signed int mDNSs32; -typedef unsigned int mDNSu32; +typedef signed int mDNSs32; +typedef unsigned int mDNSu32; #else -typedef signed long mDNSs32; -typedef unsigned long mDNSu32; +typedef signed long mDNSs32; +typedef unsigned long mDNSu32; //typedef signed int mDNSs32; //typedef unsigned int mDNSu32; #endif @@ -271,10 +271,10 @@ typedef union { mDNSu8 b[16]; mDNSu16 w[8]; mDNSu32 l[4]; } mDNSOpaque128; #pragma pack(pop) #endif -typedef mDNSOpaque16 mDNSIPPort; // An IP port is a two-byte opaque identifier (not an integer) -typedef mDNSOpaque32 mDNSv4Addr; // An IP address is a four-byte opaque identifier (not an integer) -typedef mDNSOpaque128 mDNSv6Addr; // An IPv6 address is a 16-byte opaque identifier (not an integer) -typedef mDNSOpaque48 mDNSEthAddr; // An Ethernet address is a six-byte opaque identifier (not an integer) +typedef mDNSOpaque16 mDNSIPPort; // An IP port is a two-byte opaque identifier (not an integer) +typedef mDNSOpaque32 mDNSv4Addr; // An IP address is a four-byte opaque identifier (not an integer) +typedef mDNSOpaque128 mDNSv6Addr; // An IPv6 address is a 16-byte opaque identifier (not an integer) +typedef mDNSOpaque48 mDNSEthAddr; // An Ethernet address is a six-byte opaque identifier (not an integer) // Bit operations for opaque 64 bit quantity. Uses the 32 bit quantity(l[2]) to set and clear bits #define mDNSNBBY 8 @@ -283,98 +283,98 @@ typedef mDNSOpaque48 mDNSEthAddr; // An Ethernet address is a six-byte opaque #define bit_get_opaque64(op64, index) (op64.l[((index))/(sizeof(mDNSu32) * mDNSNBBY)] & (1 << ((index) % (sizeof(mDNSu32) * mDNSNBBY)))) enum - { - mDNSAddrType_None = 0, - mDNSAddrType_IPv4 = 4, - mDNSAddrType_IPv6 = 6, - mDNSAddrType_Unknown = ~0 // Special marker value used in known answer list recording - }; +{ + mDNSAddrType_None = 0, + mDNSAddrType_IPv4 = 4, + mDNSAddrType_IPv6 = 6, + mDNSAddrType_Unknown = ~0 // Special marker value used in known answer list recording +}; enum - { - mDNSTransport_None = 0, - mDNSTransport_UDP = 1, - mDNSTransport_TCP = 2 - }; +{ + mDNSTransport_None = 0, + mDNSTransport_UDP = 1, + mDNSTransport_TCP = 2 +}; typedef struct - { - mDNSs32 type; - union { mDNSv6Addr v6; mDNSv4Addr v4; } ip; - } mDNSAddr; +{ + mDNSs32 type; + union { mDNSv6Addr v6; mDNSv4Addr v4; } ip; +} mDNSAddr; enum { mDNSfalse = 0, mDNStrue = 1 }; #define mDNSNULL 0L enum - { - mStatus_Waiting = 1, - mStatus_NoError = 0, - - // mDNS return values are in the range FFFE FF00 (-65792) to FFFE FFFF (-65537) - // The top end of the range (FFFE FFFF) is used for error codes; - // the bottom end of the range (FFFE FF00) is used for non-error values; - - // Error codes: - mStatus_UnknownErr = -65537, // First value: 0xFFFE FFFF - mStatus_NoSuchNameErr = -65538, - mStatus_NoMemoryErr = -65539, - mStatus_BadParamErr = -65540, - mStatus_BadReferenceErr = -65541, - mStatus_BadStateErr = -65542, - mStatus_BadFlagsErr = -65543, - mStatus_UnsupportedErr = -65544, - mStatus_NotInitializedErr = -65545, - mStatus_NoCache = -65546, - mStatus_AlreadyRegistered = -65547, - mStatus_NameConflict = -65548, - mStatus_Invalid = -65549, - mStatus_Firewall = -65550, - mStatus_Incompatible = -65551, - mStatus_BadInterfaceErr = -65552, - mStatus_Refused = -65553, - mStatus_NoSuchRecord = -65554, - mStatus_NoAuth = -65555, - mStatus_NoSuchKey = -65556, - mStatus_NATTraversal = -65557, - mStatus_DoubleNAT = -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, - mStatus_TransientErr = -65562, // transient failures, e.g. sending packets shortly after a network transition or wake from sleep - mStatus_ServiceNotRunning = -65563, // Background daemon not running - mStatus_NATPortMappingUnsupported = -65564, // NAT doesn't support NAT-PMP or UPnP - mStatus_NATPortMappingDisabled = -65565, // NAT supports NAT-PMP or UPnP but it's disabled by the administrator - mStatus_NoRouter = -65566, - mStatus_PollingMode = -65567, - mStatus_Timeout = -65568, - // -65568 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 // Last value: 0xFFFE FF00 - // mStatus_MemFree is the last legal mDNS error code, at the end of the range allocated for mDNS - }; +{ + mStatus_Waiting = 1, + mStatus_NoError = 0, + + // mDNS return values are in the range FFFE FF00 (-65792) to FFFE FFFF (-65537) + // The top end of the range (FFFE FFFF) is used for error codes; + // the bottom end of the range (FFFE FF00) is used for non-error values; + + // Error codes: + mStatus_UnknownErr = -65537, // First value: 0xFFFE FFFF + mStatus_NoSuchNameErr = -65538, + mStatus_NoMemoryErr = -65539, + mStatus_BadParamErr = -65540, + mStatus_BadReferenceErr = -65541, + mStatus_BadStateErr = -65542, + mStatus_BadFlagsErr = -65543, + mStatus_UnsupportedErr = -65544, + mStatus_NotInitializedErr = -65545, + mStatus_NoCache = -65546, + mStatus_AlreadyRegistered = -65547, + mStatus_NameConflict = -65548, + mStatus_Invalid = -65549, + mStatus_Firewall = -65550, + mStatus_Incompatible = -65551, + mStatus_BadInterfaceErr = -65552, + mStatus_Refused = -65553, + mStatus_NoSuchRecord = -65554, + mStatus_NoAuth = -65555, + mStatus_NoSuchKey = -65556, + mStatus_NATTraversal = -65557, + mStatus_DoubleNAT = -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, + mStatus_TransientErr = -65562, // transient failures, e.g. sending packets shortly after a network transition or wake from sleep + mStatus_ServiceNotRunning = -65563, // Background daemon not running + mStatus_NATPortMappingUnsupported = -65564, // NAT doesn't support NAT-PMP or UPnP + mStatus_NATPortMappingDisabled = -65565, // NAT supports NAT-PMP or UPnP but it's disabled by the administrator + mStatus_NoRouter = -65566, + mStatus_PollingMode = -65567, + mStatus_Timeout = -65568, + // -65568 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 // 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; // RFC 1034/1035 specify that a domain label consists of a length byte plus up to 63 characters #define MAX_DOMAIN_LABEL 63 -typedef struct { mDNSu8 c[ 64]; } domainlabel; // One label: length byte and up to 63 characters +typedef struct { mDNSu8 c[ 64]; } domainlabel; // One label: length byte and up to 63 characters // RFC 1034/1035/2181 specify that a domain name (length bytes and data bytes) may be up to 255 bytes long, // plus the terminating zero at the end makes 256 bytes total in the on-the-wire format. #define MAX_DOMAIN_NAME 256 -typedef struct { mDNSu8 c[256]; } domainname; // Up to 256 bytes of length-prefixed domainlabels +typedef struct { mDNSu8 c[256]; } domainname; // Up to 256 bytes of length-prefixed domainlabels -typedef struct { mDNSu8 c[256]; } UTF8str255; // Null-terminated C string +typedef struct { mDNSu8 c[256]; } UTF8str255; // Null-terminated C string // The longest legal textual form of a DNS name is 1009 bytes, including the C-string terminating NULL at the end. // Explanation: @@ -455,14 +455,14 @@ typedef struct UDPSocket_struct UDPSocket; #define mDNS_numUpdates numAuthorities typedef packedstruct - { - mDNSOpaque16 id; - mDNSOpaque16 flags; - mDNSu16 numQuestions; - mDNSu16 numAnswers; - mDNSu16 numAuthorities; - mDNSu16 numAdditionals; - } DNSMessageHeader; +{ + mDNSOpaque16 id; + mDNSOpaque16 flags; + mDNSu16 numQuestions; + mDNSu16 numAnswers; + mDNSu16 numAuthorities; + mDNSu16 numAdditionals; +} DNSMessageHeader; // We can send and receive packets up to 9000 bytes (Ethernet Jumbo Frame size, if that ever becomes widely used) // However, in the normal case we try to limit packets to 1500 bytes so that we don't get IP fragmentation on standard Ethernet @@ -470,27 +470,27 @@ typedef packedstruct #define AbsoluteMaxDNSMessageData 8940 #define NormalMaxDNSMessageData 1440 typedef packedstruct - { - DNSMessageHeader h; // Note: Size 12 bytes - mDNSu8 data[AbsoluteMaxDNSMessageData]; // 40 (IPv6) + 8 (UDP) + 12 (DNS header) + 8940 (data) = 9000 - } DNSMessage; +{ + DNSMessageHeader h; // Note: Size 12 bytes + mDNSu8 data[AbsoluteMaxDNSMessageData]; // 40 (IPv6) + 8 (UDP) + 12 (DNS header) + 8940 (data) = 9000 +} DNSMessage; typedef struct tcpInfo_t - { - mDNS *m; - TCPSocket *sock; - DNSMessage request; - int requestLen; - DNSQuestion *question; // For queries - AuthRecord *rr; // For record updates - mDNSAddr Addr; - mDNSIPPort Port; - mDNSIPPort SrcPort; - DNSMessage *reply; - mDNSu16 replylen; - unsigned long nread; - int numReplies; - } tcpInfo_t; +{ + mDNS *m; + TCPSocket *sock; + DNSMessage request; + int requestLen; + DNSQuestion *question; // For queries + AuthRecord *rr; // For record updates + mDNSAddr Addr; + mDNSIPPort Port; + mDNSIPPort SrcPort; + DNSMessage *reply; + mDNSu16 replylen; + unsigned long nread; + int numReplies; +} tcpInfo_t; // *************************************************************************** #if 0 @@ -499,95 +499,103 @@ typedef struct tcpInfo_t #endif typedef packedstruct - { - mDNSEthAddr dst; - mDNSEthAddr src; - mDNSOpaque16 ethertype; - } EthernetHeader; // 14 bytes +{ + mDNSEthAddr dst; + mDNSEthAddr src; + mDNSOpaque16 ethertype; +} EthernetHeader; // 14 bytes typedef packedstruct - { - mDNSOpaque16 hrd; - mDNSOpaque16 pro; - mDNSu8 hln; - mDNSu8 pln; - mDNSOpaque16 op; - mDNSEthAddr sha; - mDNSv4Addr spa; - mDNSEthAddr tha; - mDNSv4Addr tpa; - } ARP_EthIP; // 28 bytes +{ + mDNSOpaque16 hrd; + mDNSOpaque16 pro; + mDNSu8 hln; + mDNSu8 pln; + mDNSOpaque16 op; + mDNSEthAddr sha; + mDNSv4Addr spa; + mDNSEthAddr tha; + mDNSv4Addr tpa; +} ARP_EthIP; // 28 bytes typedef packedstruct - { - mDNSu8 vlen; - mDNSu8 tos; - mDNSu16 totlen; - mDNSOpaque16 id; - mDNSOpaque16 flagsfrags; - mDNSu8 ttl; - mDNSu8 protocol; // Payload type: 0x06 = TCP, 0x11 = UDP - mDNSu16 checksum; - mDNSv4Addr src; - mDNSv4Addr dst; - } IPv4Header; // 20 bytes +{ + mDNSu8 vlen; + mDNSu8 tos; + mDNSu16 totlen; + mDNSOpaque16 id; + mDNSOpaque16 flagsfrags; + mDNSu8 ttl; + mDNSu8 protocol; // Payload type: 0x06 = TCP, 0x11 = UDP + mDNSu16 checksum; + mDNSv4Addr src; + mDNSv4Addr dst; +} IPv4Header; // 20 bytes typedef packedstruct - { - mDNSu32 vcf; // Version, Traffic Class, Flow Label - mDNSu16 len; // Payload Length - mDNSu8 pro; // Type of next header: 0x06 = TCP, 0x11 = UDP, 0x3A = ICMPv6 - mDNSu8 ttl; // Hop Limit - mDNSv6Addr src; - mDNSv6Addr dst; - } IPv6Header; // 40 bytes +{ + mDNSu32 vcf; // Version, Traffic Class, Flow Label + mDNSu16 len; // Payload Length + mDNSu8 pro; // Type of next header: 0x06 = TCP, 0x11 = UDP, 0x3A = ICMPv6 + mDNSu8 ttl; // Hop Limit + mDNSv6Addr src; + mDNSv6Addr dst; +} IPv6Header; // 40 bytes typedef packedstruct - { - mDNSv6Addr src; - mDNSv6Addr dst; - mDNSOpaque32 len; - mDNSOpaque32 pro; - } IPv6PseudoHeader; // 40 bytes +{ + mDNSv6Addr src; + mDNSv6Addr dst; + mDNSOpaque32 len; + mDNSOpaque32 pro; +} IPv6PseudoHeader; // 40 bytes typedef union - { - mDNSu8 bytes[20]; - ARP_EthIP arp; - IPv4Header v4; - IPv6Header v6; - } NetworkLayerPacket; +{ + mDNSu8 bytes[20]; + ARP_EthIP arp; + IPv4Header v4; + IPv6Header v6; +} NetworkLayerPacket; typedef packedstruct - { - mDNSIPPort src; - mDNSIPPort dst; - mDNSu32 seq; - mDNSu32 ack; - mDNSu8 offset; - mDNSu8 flags; - mDNSu16 window; - mDNSu16 checksum; - mDNSu16 urgent; - } TCPHeader; // 20 bytes; IP protocol type 0x06 +{ + mDNSIPPort src; + mDNSIPPort dst; + mDNSu32 seq; + mDNSu32 ack; + mDNSu8 offset; + mDNSu8 flags; + mDNSu16 window; + mDNSu16 checksum; + mDNSu16 urgent; +} TCPHeader; // 20 bytes; IP protocol type 0x06 + +typedef struct +{ + mDNSInterfaceID IntfId; + mDNSu32 seq; + mDNSu32 ack; + mDNSu16 window; +} mDNSTCPInfo; typedef packedstruct - { - mDNSIPPort src; - mDNSIPPort dst; - mDNSu16 len; // Length including UDP header (i.e. minimum value is 8 bytes) - mDNSu16 checksum; - } UDPHeader; // 8 bytes; IP protocol type 0x11 +{ + mDNSIPPort src; + mDNSIPPort dst; + mDNSu16 len; // Length including UDP header (i.e. minimum value is 8 bytes) + mDNSu16 checksum; +} UDPHeader; // 8 bytes; IP protocol type 0x11 typedef packedstruct - { - mDNSu8 type; // 0x87 == Neighbor Solicitation, 0x88 == Neighbor Advertisement - mDNSu8 code; - mDNSu16 checksum; - mDNSu32 flags_res; // R/S/O flags and reserved bits - mDNSv6Addr target; - // Typically 8 bytes of options are also present - } IPv6NDP; // 24 bytes or more; IP protocol type 0x3A +{ + mDNSu8 type; // 0x87 == Neighbor Solicitation, 0x88 == Neighbor Advertisement + mDNSu8 code; + mDNSu16 checksum; + mDNSu32 flags_res; // R/S/O flags and reserved bits + mDNSv6Addr target; + // Typically 8 bytes of options are also present +} IPv6NDP; // 24 bytes or more; IP protocol type 0x3A #define NDP_Sol 0x87 #define NDP_Adv 0x88 @@ -600,24 +608,24 @@ typedef packedstruct #define NDP_TgtLL 2 typedef union - { - mDNSu8 bytes[20]; - TCPHeader tcp; - UDPHeader udp; - IPv6NDP ndp; - } TransportLayerPacket; +{ + mDNSu8 bytes[20]; + TCPHeader tcp; + UDPHeader udp; + IPv6NDP ndp; +} TransportLayerPacket; typedef packedstruct - { - mDNSOpaque64 InitiatorCookie; - mDNSOpaque64 ResponderCookie; - mDNSu8 NextPayload; - mDNSu8 Version; - mDNSu8 ExchangeType; - mDNSu8 Flags; - mDNSOpaque32 MessageID; - mDNSu32 Length; - } IKEHeader; // 28 bytes +{ + mDNSOpaque64 InitiatorCookie; + mDNSOpaque64 ResponderCookie; + mDNSu8 NextPayload; + mDNSu8 Version; + mDNSu8 ExchangeType; + mDNSu8 Flags; + mDNSOpaque32 MessageID; + mDNSu32 Length; +} IKEHeader; // 28 bytes // *************************************************************************** #if 0 @@ -679,34 +687,34 @@ typedef packedstruct // Bit 5 (value 0x20) is set for records received with the kDNSClass_UniqueRRSet enum - { - kDNSRecordTypeUnregistered = 0x00, // Not currently in any list - kDNSRecordTypeDeregistering = 0x01, // Shared record about to announce its departure and leave the list +{ + kDNSRecordTypeUnregistered = 0x00, // Not currently in any list + kDNSRecordTypeDeregistering = 0x01, // Shared record about to announce its departure and leave the list - kDNSRecordTypeUnique = 0x02, // Will become a kDNSRecordTypeVerified when probing is complete + kDNSRecordTypeUnique = 0x02, // Will become a kDNSRecordTypeVerified when probing is complete - kDNSRecordTypeAdvisory = 0x04, // Like Shared, but no goodbye packet - kDNSRecordTypeShared = 0x08, // Shared means record name does not have to be unique -- use random delay on responses + kDNSRecordTypeAdvisory = 0x04, // Like Shared, but no goodbye packet + kDNSRecordTypeShared = 0x08, // Shared means record name does not have to be unique -- use random delay on responses - kDNSRecordTypeVerified = 0x10, // Unique means mDNS should check that name is unique (and then send immediate responses) - kDNSRecordTypeKnownUnique = 0x20, // Known Unique means mDNS can assume name is unique without checking - // For Dynamic Update records, Known Unique means the record must already exist on the server. - kDNSRecordTypeUniqueMask = (kDNSRecordTypeUnique | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), - kDNSRecordTypeActiveSharedMask = (kDNSRecordTypeAdvisory | kDNSRecordTypeShared), - kDNSRecordTypeActiveUniqueMask = (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), - kDNSRecordTypeActiveMask = (kDNSRecordTypeActiveSharedMask | kDNSRecordTypeActiveUniqueMask), + kDNSRecordTypeVerified = 0x10, // Unique means mDNS should check that name is unique (and then send immediate responses) + kDNSRecordTypeKnownUnique = 0x20, // Known Unique means mDNS can assume name is unique without checking + // For Dynamic Update records, Known Unique means the record must already exist on the server. + kDNSRecordTypeUniqueMask = (kDNSRecordTypeUnique | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), + kDNSRecordTypeActiveSharedMask = (kDNSRecordTypeAdvisory | kDNSRecordTypeShared), + kDNSRecordTypeActiveUniqueMask = (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), + kDNSRecordTypeActiveMask = (kDNSRecordTypeActiveSharedMask | kDNSRecordTypeActiveUniqueMask), - 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 + 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 - kDNSRecordTypePacketNegative = 0xF0, // Pseudo-RR generated to cache non-existence results like NXDomain + kDNSRecordTypePacketNegative = 0xF0, // Pseudo-RR generated to cache non-existence results like NXDomain - kDNSRecordTypePacketUniqueMask = 0x10 // True for PacketAddUnique, PacketAnsUnique, PacketAuthUnique, kDNSRecordTypePacketNegative - }; + kDNSRecordTypePacketUniqueMask = 0x10 // True for PacketAddUnique, PacketAnsUnique, PacketAuthUnique, kDNSRecordTypePacketNegative +}; typedef packedstruct { mDNSu16 priority; mDNSu16 weight; mDNSIPPort port; domainname target; } rdataSRV; typedef packedstruct { mDNSu16 preference; domainname exchange; } rdataMX; @@ -714,15 +722,105 @@ typedef packedstruct { domainname mbox; domainname txt; typedef packedstruct { mDNSu16 preference; domainname map822; domainname mapx400; } rdataPX; 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; +{ + 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; + +// http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml +// Algorithm used for RRSIG, DS and DNS KEY +#define CRYPTO_RSA_SHA1 0x05 +#define CRYPTO_DSA_NSEC3_SHA1 0x06 +#define CRYPTO_RSA_NSEC3_SHA1 0x07 +#define CRYPTO_RSA_SHA256 0x08 +#define CRYPTO_RSA_SHA512 0x0A + +#define CRYPTO_ALG_MAX 0x0B + +// alg - same as in RRSIG, DNS KEY or DS +// RFC 4034 defines SHA1 +// RFC 4509 defines SHA256 +#define SHA1_DIGEST_TYPE 1 +#define SHA256_DIGEST_TYPE 2 +#define DIGEST_TYPE_MAX 3 + +// We need support for base64 and base32 encoding for displaying KEY, NSEC3 +// To make this platform agnostic, we define two types which the platform +// needs to support +#define ENC_BASE32 1 +#define ENC_BASE64 2 +#define ENC_ALG_MAX 3 + +#define DS_FIXED_SIZE 4 +typedef packedstruct +{ + mDNSu16 keyTag; + mDNSu8 alg; + mDNSu8 digestType; + mDNSu8 *digest; +} rdataDS; + +typedef struct TrustAnchor +{ + struct TrustAnchor *next; + int digestLen; + mDNSu32 validFrom; + mDNSu32 validUntil; + domainname zone; + rdataDS rds; +} TrustAnchor; + +//size of rdataRRSIG excluding signerName and signature (which are variable fields) +#define RRSIG_FIXED_SIZE 18 +typedef packedstruct +{ + mDNSu16 typeCovered; + mDNSu8 alg; + mDNSu8 labels; + mDNSu32 origTTL; + mDNSu32 sigExpireTime; + mDNSu32 sigInceptTime; + mDNSu16 keyTag; + mDNSu8 *signerName; + // mDNSu8 *signature +} rdataRRSig; + +// RFC 4034: For DNS Key RR +// flags - the valid value for DNSSEC is 256 (Zone signing key - ZSK) and 257 (Secure Entry Point) which also +// includes the ZSK bit +// +#define DNSKEY_ZONE_SIGN_KEY 0x100 +#define DNSKEY_SECURE_ENTRY_POINT 0x101 + +// proto - the only valid value for protocol is 3 (See RFC 4034) +#define DNSKEY_VALID_PROTO_VALUE 0x003 + +// alg - The only mandatory algorithm that we support is RSA/SHA-1 +// DNSSEC_RSA_SHA1_ALG + +#define DNSKEY_FIXED_SIZE 4 +typedef packedstruct +{ + mDNSu16 flags; + mDNSu8 proto; + mDNSu8 alg; + mDNSu8 *data; +} rdataDNSKey; + +// We define it here instead of dnssec.h so that these values can be used +// in files without bringing in all of dnssec.h unnecessarily. +typedef enum +{ + DNSSEC_Secure = 1, // Securely validated and has a chain up to the trust anchor + DNSSEC_Insecure, // Cannot build a chain up to the trust anchor + DNSSEC_Indeterminate, // Cannot fetch DNSSEC RRs + DNSSEC_Bogus // failed to validate signatures +} DNSSECStatus; // EDNS Option Code registrations are recorded in the "DNS EDNS0 Options" section of // @@ -733,31 +831,31 @@ typedef packedstruct #define kDNSOpt_Owner 4 typedef struct - { - mDNSu16 vers; - mDNSu16 llqOp; - mDNSu16 err; // Or UDP reply port, in setup request - // Note: In the in-memory form, there's typically a two-byte space here, so that the following 64-bit id is word-aligned - mDNSOpaque64 id; - mDNSu32 llqlease; - } LLQOptData; +{ + mDNSu16 vers; + mDNSu16 llqOp; + mDNSu16 err; // Or UDP reply port, in setup request + // Note: In the in-memory form, there's typically a two-byte space here, so that the following 64-bit id is word-aligned + mDNSOpaque64 id; + mDNSu32 llqlease; +} LLQOptData; typedef struct - { - mDNSu8 vers; // Version number of this Owner OPT record - mDNSs8 seq; // Sleep/wake epoch - mDNSEthAddr HMAC; // Host's primary identifier (e.g. MAC of on-board Ethernet) - mDNSEthAddr IMAC; // Interface's MAC address (if different to primary MAC) - mDNSOpaque48 password; // Optional password - } OwnerOptData; +{ + mDNSu8 vers; // Version number of this Owner OPT record + mDNSs8 seq; // Sleep/wake epoch + mDNSEthAddr HMAC; // Host's primary identifier (e.g. MAC of on-board Ethernet) + mDNSEthAddr IMAC; // Interface's MAC address (if different to primary MAC) + mDNSOpaque48 password; // Optional password +} OwnerOptData; // Note: rdataOPT format may be repeated an arbitrary number of times in a single resource record typedef packedstruct - { - mDNSu16 opt; - mDNSu16 optlen; - union { LLQOptData llq; mDNSu32 updatelease; OwnerOptData owner; } u; - } rdataOPT; +{ + mDNSu16 opt; + mDNSu16 optlen; + union { LLQOptData llq; mDNSu32 updatelease; OwnerOptData owner; } u; +} rdataOPT; // Space needed to put OPT records into a packet: // Header 11 bytes (name 1, type 2, class 2, TTL 4, length 2) @@ -773,28 +871,37 @@ typedef packedstruct #define DNSOpt_OwnerData_ID_Wake_PW4_Space (4 + 2 + 6 + 6 + 4) #define DNSOpt_OwnerData_ID_Wake_PW6_Space (4 + 2 + 6 + 6 + 6) -#define ValidOwnerLength(X) ( (X) == DNSOpt_OwnerData_ID_Space - 4 || \ - (X) == DNSOpt_OwnerData_ID_Wake_Space - 4 || \ - (X) == DNSOpt_OwnerData_ID_Wake_PW4_Space - 4 || \ - (X) == DNSOpt_OwnerData_ID_Wake_PW6_Space - 4 ) +#define ValidOwnerLength(X) ( (X) == DNSOpt_OwnerData_ID_Space - 4 || \ + (X) == DNSOpt_OwnerData_ID_Wake_Space - 4 || \ + (X) == DNSOpt_OwnerData_ID_Wake_PW4_Space - 4 || \ + (X) == DNSOpt_OwnerData_ID_Wake_PW6_Space - 4 ) #define DNSOpt_Owner_Space(A,B) (mDNSSameEthAddress((A),(B)) ? DNSOpt_OwnerData_ID_Space : DNSOpt_OwnerData_ID_Wake_Space) #define DNSOpt_Data_Space(O) ( \ - (O)->opt == kDNSOpt_LLQ ? DNSOpt_LLQData_Space : \ - (O)->opt == kDNSOpt_Lease ? DNSOpt_LeaseData_Space : \ - (O)->opt == kDNSOpt_Owner ? DNSOpt_Owner_Space(&(O)->u.owner.HMAC, &(O)->u.owner.IMAC) : 0x10000) + (O)->opt == kDNSOpt_LLQ ? DNSOpt_LLQData_Space : \ + (O)->opt == kDNSOpt_Lease ? DNSOpt_LeaseData_Space : \ + (O)->opt == kDNSOpt_Owner ? DNSOpt_Owner_Space(&(O)->u.owner.HMAC, &(O)->u.owner.IMAC) : 0x10000) -// A maximal NSEC record is: +// NSEC record is defined in RFC 4034. +// 16 bit RRTYPE space is split into 256 windows and each window has 256 bits (32 bytes). +// If we create a structure for NSEC, it's size would be: +// // 256 bytes domainname 'nextname' // + 256 * 34 = 8704 bytes of bitmap data // = 8960 bytes total -// For now we only support NSEC records encoding DNS types 0-255 and ignore the nextname (we always set it to be the same as the rrname), -// which gives us a fixed in-memory size of 32 bytes (256 bits) +// +// This would be a waste, as types about 256 are not very common. But it would be odd, if we receive +// a type above 256 (.US zone had TYPE65534 when this code was written) and not able to handle it. +// Hence, we handle any size by not fixing a strucure in place. The following is just a palceholder +// and never used anywhere. +// +#define NSEC_MCAST_WINDOW_SIZE 32 typedef struct - { - mDNSu8 bitmap[32]; - } rdataNSEC; +{ + //domainname *next; + //char bitmap[32]; +} rdataNSEC; // StandardAuthRDSize is 264 (256+8), which is large enough to hold a maximum-sized SRV record (6 + 256 bytes) // MaximumRDSize is 8K the absolute maximum we support (at least for now) @@ -815,78 +922,64 @@ typedef struct // have them both be the same size. Making one smaller without making the other smaller won't actually save any memory. #define InlineCacheRDSize 68 -// On 64-bit, the pointers in a CacheRecord are bigger, and that creates 8 bytes more space for the name in a CacheGroup -#if ENABLE_MULTI_PACKET_QUERY_SNOOPING - #if defined(_ILP64) || defined(__ILP64__) || defined(_LP64) || defined(__LP64__) || defined(_WIN64) - #define InlineCacheGroupNameSize 160 - #else - #define InlineCacheGroupNameSize 148 - #endif -#else - #if defined(_ILP64) || defined(__ILP64__) || defined(_LP64) || defined(__LP64__) || defined(_WIN64) - #define InlineCacheGroupNameSize 144 - #else - #define InlineCacheGroupNameSize 132 - #endif -#endif - // The RDataBody union defines the common rdata types that fit into our 264-byte limit typedef union - { - mDNSu8 data[StandardAuthRDSize]; - mDNSv4Addr ipv4; // For 'A' record - domainname name; // For PTR, NS, CNAME, DNAME - UTF8str255 txt; - rdataMX mx; - mDNSv6Addr ipv6; // For 'AAAA' record - rdataSRV srv; - rdataOPT opt[2]; // For EDNS0 OPT record; RDataBody may contain multiple variable-length rdataOPT objects packed together - rdataNSEC nsec; - } RDataBody; +{ + mDNSu8 data[StandardAuthRDSize]; + mDNSv4Addr ipv4; // For 'A' record + domainname name; // For PTR, NS, CNAME, DNAME + UTF8str255 txt; + rdataMX mx; + mDNSv6Addr ipv6; // For 'AAAA' record + rdataSRV srv; + rdataOPT opt[2]; // For EDNS0 OPT record; RDataBody may contain multiple variable-length rdataOPT objects packed together +} RDataBody; // The RDataBody2 union is the same as above, except it includes fields for the larger types like soa, rp, px typedef union - { - mDNSu8 data[StandardAuthRDSize]; - mDNSv4Addr ipv4; // For 'A' record - domainname name; // For PTR, NS, CNAME, DNAME - rdataSOA soa; // This is large; not included in the normal RDataBody definition - UTF8str255 txt; - rdataMX mx; - rdataRP rp; // This is large; not included in the normal RDataBody definition - rdataPX px; // This is large; not included in the normal RDataBody definition - mDNSv6Addr ipv6; // For 'AAAA' record - rdataSRV srv; - rdataOPT opt[2]; // For EDNS0 OPT record; RDataBody may contain multiple variable-length rdataOPT objects packed together - rdataNSEC nsec; - } RDataBody2; +{ + mDNSu8 data[StandardAuthRDSize]; + mDNSv4Addr ipv4; // For 'A' record + domainname name; // For PTR, NS, CNAME, DNAME + rdataSOA soa; // This is large; not included in the normal RDataBody definition + UTF8str255 txt; + rdataMX mx; + rdataRP rp; // This is large; not included in the normal RDataBody definition + rdataPX px; // This is large; not included in the normal RDataBody definition + mDNSv6Addr ipv6; // For 'AAAA' record + rdataSRV srv; + rdataOPT opt[2]; // For EDNS0 OPT record; RDataBody may contain multiple variable-length rdataOPT objects packed together + rdataDS ds; + rdataDNSKey key; + rdataRRSig rrsig; +} RDataBody2; typedef struct - { - mDNSu16 MaxRDLength; // Amount of storage allocated for rdata (usually sizeof(RDataBody)) - mDNSu16 padding; // So that RDataBody is aligned on 32-bit boundary - RDataBody u; - } RData; +{ + mDNSu16 MaxRDLength; // Amount of storage allocated for rdata (usually sizeof(RDataBody)) + mDNSu16 padding; // So that RDataBody is aligned on 32-bit boundary + RDataBody u; +} RData; // sizeofRDataHeader should be 4 bytes #define sizeofRDataHeader (sizeof(RData) - sizeof(RDataBody)) // RData_small is a smaller version of the RData object, used for inline data storage embedded in a CacheRecord_struct typedef struct - { - mDNSu16 MaxRDLength; // Storage allocated for data (may be greater than InlineCacheRDSize if additional storage follows this object) - mDNSu16 padding; // So that data is aligned on 32-bit boundary - mDNSu8 data[InlineCacheRDSize]; - } RData_small; +{ + mDNSu16 MaxRDLength; // Storage allocated for data (may be greater than InlineCacheRDSize if additional storage follows this object) + mDNSu16 padding; // So that data is aligned on 32-bit boundary + mDNSu8 data[InlineCacheRDSize]; +} RData_small; // Note: Within an mDNSRecordCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Exit(), mDNS_Execute() -typedef void mDNSRecordCallback(mDNS *const m, AuthRecord *const rr, mStatus result); +typedef void mDNSRecordCallback (mDNS *const m, AuthRecord *const rr, mStatus result); // Note: // Restrictions: An mDNSRecordUpdateCallback may not make any mDNS API calls. // The intent of this callback is to allow the client to free memory, if necessary. // The internal data structures of the mDNS code may not be in a state where mDNS API calls may be made safely. -typedef void mDNSRecordUpdateCallback(mDNS *const m, AuthRecord *const rr, RData *OldRData, mDNSu16 OldRDLen); +typedef void mDNSRecordUpdateCallback (mDNS *const m, AuthRecord *const rr, RData *OldRData, mDNSu16 OldRDLen); // *************************************************************************** #if 0 @@ -901,90 +994,90 @@ typedef void mDNSRecordUpdateCallback(mDNS *const m, AuthRecord *const rr, RData #define NATMAP_VERS 0 typedef enum - { - NATOp_AddrRequest = 0, - NATOp_MapUDP = 1, - NATOp_MapTCP = 2, - - NATOp_AddrResponse = 0x80 | 0, - NATOp_MapUDPResponse = 0x80 | 1, - NATOp_MapTCPResponse = 0x80 | 2, - } NATOp_t; +{ + NATOp_AddrRequest = 0, + NATOp_MapUDP = 1, + NATOp_MapTCP = 2, + + NATOp_AddrResponse = 0x80 | 0, + NATOp_MapUDPResponse = 0x80 | 1, + NATOp_MapTCPResponse = 0x80 | 2, +} NATOp_t; enum - { - NATErr_None = 0, - NATErr_Vers = 1, - NATErr_Refused = 2, - NATErr_NetFail = 3, - NATErr_Res = 4, - NATErr_Opcode = 5 - }; +{ + NATErr_None = 0, + NATErr_Vers = 1, + NATErr_Refused = 2, + NATErr_NetFail = 3, + NATErr_Res = 4, + NATErr_Opcode = 5 +}; typedef mDNSu16 NATErr_t; typedef packedstruct - { - mDNSu8 vers; - mDNSu8 opcode; - } NATAddrRequest; +{ + mDNSu8 vers; + mDNSu8 opcode; +} NATAddrRequest; typedef packedstruct - { - mDNSu8 vers; - mDNSu8 opcode; - mDNSu16 err; - mDNSu32 upseconds; // Time since last NAT engine reboot, in seconds - mDNSv4Addr ExtAddr; - } NATAddrReply; +{ + mDNSu8 vers; + mDNSu8 opcode; + mDNSu16 err; + mDNSu32 upseconds; // Time since last NAT engine reboot, in seconds + mDNSv4Addr ExtAddr; +} NATAddrReply; typedef packedstruct - { - mDNSu8 vers; - mDNSu8 opcode; - mDNSOpaque16 unused; - mDNSIPPort intport; - mDNSIPPort extport; - mDNSu32 NATReq_lease; - } NATPortMapRequest; +{ + mDNSu8 vers; + mDNSu8 opcode; + mDNSOpaque16 unused; + mDNSIPPort intport; + mDNSIPPort extport; + mDNSu32 NATReq_lease; +} NATPortMapRequest; typedef packedstruct - { - mDNSu8 vers; - mDNSu8 opcode; - mDNSu16 err; - mDNSu32 upseconds; // Time since last NAT engine reboot, in seconds - mDNSIPPort intport; - mDNSIPPort extport; - mDNSu32 NATRep_lease; - } NATPortMapReply; +{ + mDNSu8 vers; + mDNSu8 opcode; + mDNSu16 err; + mDNSu32 upseconds; // Time since last NAT engine reboot, in seconds + mDNSIPPort intport; + mDNSIPPort extport; + mDNSu32 NATRep_lease; +} NATPortMapReply; typedef enum - { - LNTDiscoveryOp = 1, - LNTExternalAddrOp = 2, - LNTPortMapOp = 3, - LNTPortMapDeleteOp = 4 - } LNTOp_t; +{ + LNTDiscoveryOp = 1, + LNTExternalAddrOp = 2, + LNTPortMapOp = 3, + LNTPortMapDeleteOp = 4 +} LNTOp_t; #define LNT_MAXBUFSIZE 8192 typedef struct tcpLNTInfo_struct tcpLNTInfo; struct tcpLNTInfo_struct - { - tcpLNTInfo *next; - mDNS *m; - NATTraversalInfo *parentNATInfo; // pointer back to the parent NATTraversalInfo - TCPSocket *sock; - LNTOp_t op; // operation performed using this connection - mDNSAddr Address; // router address - mDNSIPPort Port; // router port - mDNSu8 *Request; // xml request to router - int requestLen; - mDNSu8 *Reply; // xml reply from router - int replyLen; - unsigned long nread; // number of bytes read so far - int retries; // number of times we've tried to do this port mapping - }; +{ + tcpLNTInfo *next; + mDNS *m; + NATTraversalInfo *parentNATInfo; // pointer back to the parent NATTraversalInfo + TCPSocket *sock; + LNTOp_t op; // operation performed using this connection + mDNSAddr Address; // router address + mDNSIPPort Port; // router port + mDNSu8 *Request; // xml request to router + int requestLen; + mDNSu8 *Reply; // xml reply from router + int replyLen; + unsigned long nread; // number of bytes read so far + int retries; // number of times we've tried to do this port mapping +}; typedef void (*NATTraversalClientCallback)(mDNS *m, NATTraversalInfo *n); @@ -992,281 +1085,283 @@ typedef void (*NATTraversalClientCallback)(mDNS *m, NATTraversalInfo *n); // if m->timenow >= ExpiryTime then our mapping has expired, and we're trying to create one struct NATTraversalInfo_struct - { - // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. - NATTraversalInfo *next; +{ + // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. + NATTraversalInfo *next; - mDNSs32 ExpiryTime; // Time this mapping expires, or zero if no mapping - mDNSs32 retryInterval; // Current interval, between last packet we sent and the next one - mDNSs32 retryPortMap; // If Protocol is nonzero, time to send our next mapping packet - mStatus NewResult; // New error code; will be copied to Result just prior to invoking callback + mDNSs32 ExpiryTime; // Time this mapping expires, or zero if no mapping + mDNSs32 retryInterval; // Current interval, between last packet we sent and the next one + mDNSs32 retryPortMap; // If Protocol is nonzero, time to send our next mapping packet + mStatus NewResult; // New error code; will be copied to Result just prior to invoking callback #ifdef _LEGACY_NAT_TRAVERSAL_ - tcpLNTInfo tcpInfo; // Legacy NAT traversal (UPnP) TCP connection + tcpLNTInfo tcpInfo; // Legacy NAT traversal (UPnP) TCP connection #endif - // Result fields: When the callback is invoked these fields contain the answers the client is looking for - // When the callback is invoked ExternalPort is *usually* set to be the same the same as RequestedPort, except: - // (a) When we're behind a NAT gateway with port mapping disabled, ExternalPort is reported as zero to - // indicate that we don't currently have a working mapping (but RequestedPort retains the external port - // we'd like to get, the next time we meet an accomodating NAT gateway willing to give us one). - // (b) When we have a routable non-RFC1918 address, we don't *need* a port mapping, so ExternalPort - // is reported as the same as our InternalPort, since that is effectively our externally-visible port too. - // Again, RequestedPort retains the external port we'd like to get the next time we find ourself behind a NAT gateway. - // To improve stability of port mappings, RequestedPort is updated any time we get a successful - // mapping response from the NAT-PMP or UPnP gateway. For example, if we ask for port 80, and - // get assigned port 81, then thereafter we'll contine asking for port 81. - mDNSInterfaceID InterfaceID; - mDNSv4Addr ExternalAddress; // Initially set to onesIPv4Addr, until first callback - mDNSIPPort ExternalPort; - mDNSu32 Lifetime; - mStatus Result; - - // Client API fields: The client must set up these fields *before* making any NAT traversal API calls - mDNSu8 Protocol; // NATOp_MapUDP or NATOp_MapTCP, or zero if just requesting the external IP address - mDNSIPPort IntPort; // Client's internal port number (doesn't change) - mDNSIPPort RequestedPort; // Requested external port; may be updated with actual value assigned by gateway - mDNSu32 NATLease; // Requested lifetime in seconds (doesn't change) - NATTraversalClientCallback clientCallback; - void *clientContext; - }; + // Result fields: When the callback is invoked these fields contain the answers the client is looking for + // When the callback is invoked ExternalPort is *usually* set to be the same the same as RequestedPort, except: + // (a) When we're behind a NAT gateway with port mapping disabled, ExternalPort is reported as zero to + // indicate that we don't currently have a working mapping (but RequestedPort retains the external port + // we'd like to get, the next time we meet an accomodating NAT gateway willing to give us one). + // (b) When we have a routable non-RFC1918 address, we don't *need* a port mapping, so ExternalPort + // is reported as the same as our InternalPort, since that is effectively our externally-visible port too. + // Again, RequestedPort retains the external port we'd like to get the next time we find ourself behind a NAT gateway. + // To improve stability of port mappings, RequestedPort is updated any time we get a successful + // mapping response from the NAT-PMP or UPnP gateway. For example, if we ask for port 80, and + // get assigned port 81, then thereafter we'll contine asking for port 81. + mDNSInterfaceID InterfaceID; + mDNSv4Addr ExternalAddress; // Initially set to onesIPv4Addr, until first callback + mDNSIPPort ExternalPort; + mDNSu32 Lifetime; + mStatus Result; + + // Client API fields: The client must set up these fields *before* making any NAT traversal API calls + mDNSu8 Protocol; // NATOp_MapUDP or NATOp_MapTCP, or zero if just requesting the external IP address + mDNSIPPort IntPort; // Client's internal port number (doesn't change) + mDNSIPPort RequestedPort; // Requested external port; may be updated with actual value assigned by gateway + mDNSu32 NATLease; // Requested lifetime in seconds (doesn't change) + NATTraversalClientCallback clientCallback; + void *clientContext; +}; enum - { - DNSServer_Untested = 0, - DNSServer_Passed = 1, - DNSServer_Failed = 2, - DNSServer_Disabled = 3 - }; +{ + DNSServer_Untested = 0, + DNSServer_Passed = 1, + DNSServer_Failed = 2, + DNSServer_Disabled = 3 +}; enum - { - DNSServer_FlagDelete = 1, - DNSServer_FlagNew = 2 - }; +{ + DNSServer_FlagDelete = 1, + DNSServer_FlagNew = 2 +}; enum - { - McastResolver_FlagDelete = 1, - McastResolver_FlagNew = 2 - }; +{ + McastResolver_FlagDelete = 1, + McastResolver_FlagNew = 2 +}; typedef struct McastResolver - { - struct McastResolver *next; - mDNSInterfaceID interface; - mDNSu32 flags; // Set when we're planning to delete this from the list - domainname domain; - mDNSu32 timeout; // timeout value for questions - } McastResolver; +{ + struct McastResolver *next; + mDNSInterfaceID interface; + mDNSu32 flags; // Set when we're planning to delete this from the list + domainname domain; + mDNSu32 timeout; // timeout value for questions +} McastResolver; typedef struct DNSServer - { - struct DNSServer *next; - mDNSInterfaceID interface; // For specialized uses; we can have DNS servers reachable over specific interfaces - mDNSAddr addr; - mDNSIPPort port; - mDNSOpaque16 testid; - mDNSu32 flags; // Set when we're planning to delete this from the list - mDNSu32 teststate; // Have we sent bug-detection query to this server? - mDNSs32 lasttest; // Time we sent last bug-detection query to this server - domainname domain; // name->server matching for "split dns" - mDNSs32 penaltyTime; // amount of time this server is penalized - mDNSBool scoped; // interface should be matched against question only - // if scoped is set - mDNSu32 timeout; // timeout value for questions - mDNSBool cellIntf; // Resolver from Cellular Interface ? - } DNSServer; - -typedef struct // Size is 36 bytes when compiling for 32-bit; 48 when compiling for 64-bit - { - mDNSu8 RecordType; // See enum above - mDNSu16 rrtype; - mDNSu16 rrclass; - mDNSu32 rroriginalttl; // In seconds - mDNSu16 rdlength; // Size of the raw rdata, in bytes, in the on-the-wire format - // (In-memory storage may be larger, for structures containing 'holes', like SOA, - // or smaller, for NSEC where we don't bother storing the nextname field) - mDNSu16 rdestimate; // Upper bound on on-the-wire size of rdata after name compression - mDNSu32 namehash; // Name-based (i.e. case-insensitive) hash of name - mDNSu32 rdatahash; // For rdata containing domain name (e.g. PTR, SRV, CNAME etc.), case-insensitive name hash - // else, for all other rdata, 32-bit hash of the raw rdata - // Note: This requirement is important. Various routines like AddAdditionalsToResponseList(), - // ReconfirmAntecedents(), etc., use rdatahash as a pre-flight check to see - // whether it's worth doing a full SameDomainName() call. If the rdatahash - // is not a correct case-insensitive name hash, they'll get false negatives. - - // Grouping pointers together at the end of the structure improves the memory layout efficiency - mDNSInterfaceID InterfaceID; // Set if this RR is specific to one interface - // For records received off the wire, InterfaceID is *always* set to the receiving interface - // For our authoritative records, InterfaceID is usually zero, except for those few records - // that are interface-specific (e.g. address records, especially linklocal addresses) - const domainname *name; - RData *rdata; // Pointer to storage for this rdata - DNSServer *rDNSServer; // Unicast DNS server authoritative for this entry;null for multicast - } ResourceRecord; +{ + struct DNSServer *next; + mDNSInterfaceID interface; // For specialized uses; we can have DNS servers reachable over specific interfaces + mDNSAddr addr; + mDNSIPPort port; + mDNSOpaque16 testid; + mDNSu32 flags; // Set when we're planning to delete this from the list + mDNSu32 teststate; // Have we sent bug-detection query to this server? + mDNSs32 lasttest; // Time we sent last bug-detection query to this server + domainname domain; // name->server matching for "split dns" + mDNSs32 penaltyTime; // amount of time this server is penalized + mDNSBool scoped; // interface should be matched against question only + // if scoped is set + mDNSu32 timeout; // timeout value for questions + mDNSBool cellIntf; // Resolver from Cellular Interface ? + mDNSu16 resGroupID; // ID of the resolver group that contains this DNSServer +} DNSServer; + +typedef struct // Size is 36 bytes when compiling for 32-bit; 48 when compiling for 64-bit +{ + mDNSu8 RecordType; // See enum above + mDNSu16 rrtype; + mDNSu16 rrclass; + mDNSu32 rroriginalttl; // In seconds + mDNSu16 rdlength; // Size of the raw rdata, in bytes, in the on-the-wire format + // (In-memory storage may be larger, for structures containing 'holes', like SOA) + mDNSu16 rdestimate; // Upper bound on on-the-wire size of rdata after name compression + mDNSu32 namehash; // Name-based (i.e. case-insensitive) hash of name + mDNSu32 rdatahash; // For rdata containing domain name (e.g. PTR, SRV, CNAME etc.), case-insensitive name hash + // else, for all other rdata, 32-bit hash of the raw rdata + // Note: This requirement is important. Various routines like AddAdditionalsToResponseList(), + // ReconfirmAntecedents(), etc., use rdatahash as a pre-flight check to see + // whether it's worth doing a full SameDomainName() call. If the rdatahash + // is not a correct case-insensitive name hash, they'll get false negatives. + + // Grouping pointers together at the end of the structure improves the memory layout efficiency + mDNSInterfaceID InterfaceID; // Set if this RR is specific to one interface + // For records received off the wire, InterfaceID is *always* set to the receiving interface + // For our authoritative records, InterfaceID is usually zero, except for those few records + // that are interface-specific (e.g. address records, especially linklocal addresses) + const domainname *name; + RData *rdata; // Pointer to storage for this rdata + DNSServer *rDNSServer; // Unicast DNS server authoritative for this entry;null for multicast +} ResourceRecord; // Unless otherwise noted, states may apply to either independent record registrations or service registrations typedef enum - { - regState_Zero = 0, - regState_Pending = 1, // update sent, reply not received - regState_Registered = 2, // update sent, reply received - regState_DeregPending = 3, // dereg sent, reply not received - regState_Unregistered = 4, // not in any list - regState_Refresh = 5, // outstanding refresh (or target change) message - regState_NATMap = 6, // establishing NAT port mapping - regState_UpdatePending = 7, // update in flight as result of mDNS_Update call - regState_NoTarget = 8, // SRV Record registration pending registration of hostname - regState_NATError = 9 // unable to complete NAT traversal - } regState_t; +{ + regState_Zero = 0, + regState_Pending = 1, // update sent, reply not received + regState_Registered = 2, // update sent, reply received + regState_DeregPending = 3, // dereg sent, reply not received + regState_Unregistered = 4, // not in any list + regState_Refresh = 5, // outstanding refresh (or target change) message + regState_NATMap = 6, // establishing NAT port mapping + regState_UpdatePending = 7, // update in flight as result of mDNS_Update call + regState_NoTarget = 8, // SRV Record registration pending registration of hostname + regState_NATError = 9 // unable to complete NAT traversal +} regState_t; enum - { - Target_Manual = 0, - Target_AutoHost = 1, - Target_AutoHostAndNATMAP = 2 - }; +{ + Target_Manual = 0, + Target_AutoHost = 1, + Target_AutoHostAndNATMAP = 2 +}; typedef enum - { - mergeState_Zero = 0, - mergeState_DontMerge = 1 // Set on fatal error conditions to disable merging - } mergeState_t; - -struct AuthGroup_struct // Header object for a list of AuthRecords with the same name - { - AuthGroup *next; // Next AuthGroup object in this hash table bucket - mDNSu32 namehash; // Name-based (i.e. case insensitive) hash of name - AuthRecord *members; // List of CacheRecords with this same name - AuthRecord **rrauth_tail; // Tail end of that list - domainname *name; // Common name for all AuthRecords in this list - AuthRecord *NewLocalOnlyRecords; - // Size to here is 20 bytes when compiling 32-bit; 40 bytes when compiling 64-bit - mDNSu8 namestorage[InlineCacheGroupNameSize]; - }; +{ + mergeState_Zero = 0, + mergeState_DontMerge = 1 // Set on fatal error conditions to disable merging +} mergeState_t; + +#define AUTH_GROUP_NAME_SIZE 128 +struct AuthGroup_struct // Header object for a list of AuthRecords with the same name +{ + AuthGroup *next; // Next AuthGroup object in this hash table bucket + mDNSu32 namehash; // Name-based (i.e. case insensitive) hash of name + AuthRecord *members; // List of CacheRecords with this same name + AuthRecord **rrauth_tail; // Tail end of that list + domainname *name; // Common name for all AuthRecords in this list + AuthRecord *NewLocalOnlyRecords; + mDNSu8 namestorage[AUTH_GROUP_NAME_SIZE]; +}; #define AUTH_HASH_SLOTS 499 -#define FORALL_AUTHRECORDS(SLOT,AG,AR) \ - for ((SLOT) = 0; (SLOT) < AUTH_HASH_SLOTS; (SLOT)++) \ - for ((AG)=m->rrauth.rrauth_hash[(SLOT)]; (AG); (AG)=(AG)->next) \ - for ((AR) = (AG)->members; (AR); (AR)=(AR)->next) +#define FORALL_AUTHRECORDS(SLOT,AG,AR) \ + for ((SLOT) = 0; (SLOT) < AUTH_HASH_SLOTS; (SLOT)++) \ + for ((AG)=m->rrauth.rrauth_hash[(SLOT)]; (AG); (AG)=(AG)->next) \ + for ((AR) = (AG)->members; (AR); (AR)=(AR)->next) typedef union AuthEntity_union AuthEntity; union AuthEntity_union { AuthEntity *next; AuthGroup ag; }; typedef struct { - mDNSu32 rrauth_size; // Total number of available auth entries - mDNSu32 rrauth_totalused; // Number of auth entries currently occupied - mDNSu32 rrauth_report; - mDNSu8 rrauth_lock; // For debugging: Set at times when these lists may not be modified - AuthEntity *rrauth_free; - AuthGroup *rrauth_hash[AUTH_HASH_SLOTS]; + mDNSu32 rrauth_size; // Total number of available auth entries + mDNSu32 rrauth_totalused; // Number of auth entries currently occupied + mDNSu32 rrauth_report; + mDNSu8 rrauth_lock; // For debugging: Set at times when these lists may not be modified + AuthEntity *rrauth_free; + AuthGroup *rrauth_hash[AUTH_HASH_SLOTS]; }AuthHash; -// AuthRecordAny includes mDNSInterface_Any and interface specific auth records (anything -// other than P2P or LocalOnly) -typedef enum - { - AuthRecordAny, // registered for *Any, NOT including P2P interfaces - AuthRecordAnyIncludeP2P, // registered for *Any, including P2P interfaces - AuthRecordLocalOnly, - AuthRecordP2P // discovered over D2D/P2P framework - } AuthRecType; +// AuthRecordAny includes mDNSInterface_Any and interface specific auth records. +typedef enum +{ + AuthRecordAny, // registered for *Any, NOT including P2P interfaces + AuthRecordAnyIncludeP2P, // registered for *Any, including P2P interfaces + AuthRecordAnyIncludeAWDL, // registered for *Any, including AWDL interface + AuthRecordLocalOnly, + AuthRecordP2P // discovered over D2D/P2P framework +} AuthRecType; struct AuthRecord_struct - { - // For examples of how to set up this structure for use in mDNS_Register(), - // see mDNS_AdvertiseInterface() or mDNS_RegisterService(). - // Basically, resrec and persistent metadata need to be set up before calling mDNS_Register(). - // mDNS_SetupResourceRecord() is avaliable as a helper routine to set up most fields to sensible default values for you - - AuthRecord *next; // Next in list; first element of structure for efficiency reasons - // Field Group 1: Common ResourceRecord fields - ResourceRecord resrec; // 36 bytes when compiling for 32-bit; 48 when compiling for 64-bit - - // Field Group 2: Persistent metadata for Authoritative Records - AuthRecord *Additional1; // Recommended additional record to include in response (e.g. SRV for PTR record) - AuthRecord *Additional2; // Another additional (e.g. TXT for PTR record) - 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, and to free memory asynchronously on deregistration - void *RecordContext; // Context parameter for the callback function - mDNSu8 AutoTarget; // 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 - - OwnerOptData WakeUp; // WakeUp.HMAC.l[0] nonzero indicates that this is a Sleep Proxy record - mDNSAddr AddressProxy; // For reverse-mapping Sleep Proxy PTR records, address in question - mDNSs32 TimeRcvd; // In platform time units - mDNSs32 TimeExpire; // In platform time units - AuthRecType ARType; // LocalOnly, P2P or Normal ? - - // Field Group 3: 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 AnsweredLocalQ; // Set if this AuthRecord has been delivered to any local question (LocalOnly or mDNSInterface_Any) - mDNSu8 IncludeInProbe; // Set if this RR is being put into a probe right now - mDNSu8 ImmedUnicast; // Set if we may send our response directly via unicast to the requester - mDNSInterfaceID SendNSECNow; // Set if we need to generate associated NSEC data for this rrname - mDNSInterfaceID ImmedAnswer; // Someone on this interface issued a query we need to answer (all-ones for all interfaces) +{ + // For examples of how to set up this structure for use in mDNS_Register(), + // see mDNS_AdvertiseInterface() or mDNS_RegisterService(). + // Basically, resrec and persistent metadata need to be set up before calling mDNS_Register(). + // mDNS_SetupResourceRecord() is avaliable as a helper routine to set up most fields to sensible default values for you + + AuthRecord *next; // Next in list; first element of structure for efficiency reasons + // Field Group 1: Common ResourceRecord fields + ResourceRecord resrec; // 36 bytes when compiling for 32-bit; 48 when compiling for 64-bit + + // Field Group 2: Persistent metadata for Authoritative Records + AuthRecord *Additional1; // Recommended additional record to include in response (e.g. SRV for PTR record) + AuthRecord *Additional2; // Another additional (e.g. TXT for PTR record) + 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, and to free memory asynchronously on deregistration + void *RecordContext; // Context parameter for the callback function + mDNSu8 AutoTarget; // 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 + + OwnerOptData WakeUp; // WakeUp.HMAC.l[0] nonzero indicates that this is a Sleep Proxy record + mDNSAddr AddressProxy; // For reverse-mapping Sleep Proxy PTR records, address in question + mDNSs32 TimeRcvd; // In platform time units + mDNSs32 TimeExpire; // In platform time units + AuthRecType ARType; // LocalOnly, P2P or Normal ? + mDNSs32 KATimeExpire; // In platform time units: time to send keepalive packet for the proxy record + + // Field Group 3: 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 AnsweredLocalQ; // Set if this AuthRecord has been delivered to any local question (LocalOnly or mDNSInterface_Any) + mDNSu8 IncludeInProbe; // Set if this RR is being put into a probe right now + mDNSu8 ImmedUnicast; // Set if we may send our response directly via unicast to the requester + mDNSInterfaceID SendNSECNow; // Set if we need to generate associated NSEC data for this rrname + mDNSInterfaceID ImmedAnswer; // Someone on this interface issued a query we need to answer (all-ones for all interfaces) #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES - mDNSs32 ImmedAnswerMarkTime; + mDNSs32 ImmedAnswerMarkTime; #endif - mDNSInterfaceID ImmedAdditional; // Hint that we might want to also send this record, just to be helpful - mDNSInterfaceID SendRNow; // The interface this query is being sent on right now - mDNSv4Addr v4Requester; // Recent v4 query for this record, or all-ones if more than one recent query - mDNSv6Addr v6Requester; // Recent v6 query for this record, or all-ones if more than one recent query - AuthRecord *NextResponse; // Link to the next element in the chain of responses to generate - const mDNSu8 *NR_AnswerTo; // Set if this record was selected by virtue of being a direct answer to a question - AuthRecord *NR_AdditionalTo; // Set if this record was selected by virtue of being additional to another - mDNSs32 ThisAPInterval; // In platform time units: Current interval for announce/probe - mDNSs32 LastAPTime; // In platform time units: Last time we sent announcement/probe - mDNSs32 LastMCTime; // Last time we multicast this record (used to guard against packet-storm attacks) - mDNSInterfaceID LastMCInterface; // Interface this record was multicast on at the time LastMCTime was recorded - RData *NewRData; // Set if we are updating this record with new rdata - mDNSu16 newrdlength; // ... and the length of the new RData - mDNSRecordUpdateCallback *UpdateCallback; - mDNSu32 UpdateCredits; // Token-bucket rate limiting of excessive updates - mDNSs32 NextUpdateCredit; // Time next token is added to bucket - mDNSs32 UpdateBlocked; // Set if update delaying is in effect - - // Field Group 4: Transient uDNS state for Authoritative Records - regState_t state; // Maybe combine this with resrec.RecordType state? Right now it's ambiguous and confusing. - // e.g. rr->resrec.RecordType can be kDNSRecordTypeUnregistered, - // and rr->state can be regState_Unregistered - // What if we find one of those statements is true and the other false? What does that mean? - mDNSBool uselease; // dynamic update contains (should contain) lease option - mDNSs32 expire; // In platform time units: expiration of lease (-1 for static) - mDNSBool Private; // If zone is private, DNS updates may have to be encrypted to prevent eavesdropping - mDNSOpaque16 updateid; // Identifier to match update request and response -- also used when transferring records to Sleep Proxy - const domainname *zone; // the zone that is updated - ZoneData *nta; - struct tcpInfo_t *tcp; - NATTraversalInfo NATinfo; - mDNSBool SRVChanged; // temporarily deregistered service because its SRV target or port changed - mergeState_t mState; // Unicast Record Registrations merge state - mDNSu8 refreshCount; // Number of refreshes to the server - mStatus updateError; // Record update resulted in Error ? - - // uDNS_UpdateRecord support fields - // Do we really need all these in *addition* to NewRData and newrdlength above? - void *UpdateContext; // Context parameter for the update callback function - mDNSu16 OrigRDLen; // previously registered, being deleted - mDNSu16 InFlightRDLen; // currently being registered - mDNSu16 QueuedRDLen; // pending operation (re-transmitting if necessary) THEN register the queued update - RData *OrigRData; - RData *InFlightRData; - RData *QueuedRData; - - // Field Group 5: Large data objects go at the end - domainname namestorage; - RData rdatastorage; // Normally the storage is right here, except for oversized records - // rdatastorage MUST be the last thing in the structure -- when using oversized AuthRecords, extra bytes - // are appended after the end of the AuthRecord, logically augmenting the size of the rdatastorage - // DO NOT ADD ANY MORE FIELDS HERE - }; + mDNSInterfaceID ImmedAdditional; // Hint that we might want to also send this record, just to be helpful + mDNSInterfaceID SendRNow; // The interface this query is being sent on right now + mDNSv4Addr v4Requester; // Recent v4 query for this record, or all-ones if more than one recent query + mDNSv6Addr v6Requester; // Recent v6 query for this record, or all-ones if more than one recent query + AuthRecord *NextResponse; // Link to the next element in the chain of responses to generate + const mDNSu8 *NR_AnswerTo; // Set if this record was selected by virtue of being a direct answer to a question + AuthRecord *NR_AdditionalTo; // Set if this record was selected by virtue of being additional to another + mDNSs32 ThisAPInterval; // In platform time units: Current interval for announce/probe + mDNSs32 LastAPTime; // In platform time units: Last time we sent announcement/probe + mDNSs32 LastMCTime; // Last time we multicast this record (used to guard against packet-storm attacks) + mDNSInterfaceID LastMCInterface; // Interface this record was multicast on at the time LastMCTime was recorded + RData *NewRData; // Set if we are updating this record with new rdata + mDNSu16 newrdlength; // ... and the length of the new RData + mDNSRecordUpdateCallback *UpdateCallback; + mDNSu32 UpdateCredits; // Token-bucket rate limiting of excessive updates + mDNSs32 NextUpdateCredit; // Time next token is added to bucket + mDNSs32 UpdateBlocked; // Set if update delaying is in effect + + // Field Group 4: Transient uDNS state for Authoritative Records + regState_t state; // Maybe combine this with resrec.RecordType state? Right now it's ambiguous and confusing. + // e.g. rr->resrec.RecordType can be kDNSRecordTypeUnregistered, + // and rr->state can be regState_Unregistered + // What if we find one of those statements is true and the other false? What does that mean? + mDNSBool uselease; // dynamic update contains (should contain) lease option + mDNSs32 expire; // In platform time units: expiration of lease (-1 for static) + mDNSBool Private; // If zone is private, DNS updates may have to be encrypted to prevent eavesdropping + mDNSOpaque16 updateid; // Identifier to match update request and response -- also used when transferring records to Sleep Proxy + mDNSOpaque64 updateIntID; // Interface IDs (one bit per interface index)to which updates have been sent + const domainname *zone; // the zone that is updated + ZoneData *nta; + struct tcpInfo_t *tcp; + NATTraversalInfo NATinfo; + mDNSBool SRVChanged; // temporarily deregistered service because its SRV target or port changed + mergeState_t mState; // Unicast Record Registrations merge state + mDNSu8 refreshCount; // Number of refreshes to the server + mStatus updateError; // Record update resulted in Error ? + + // uDNS_UpdateRecord support fields + // Do we really need all these in *addition* to NewRData and newrdlength above? + void *UpdateContext; // Context parameter for the update callback function + mDNSu16 OrigRDLen; // previously registered, being deleted + mDNSu16 InFlightRDLen; // currently being registered + mDNSu16 QueuedRDLen; // pending operation (re-transmitting if necessary) THEN register the queued update + RData *OrigRData; + RData *InFlightRData; + RData *QueuedRData; + + // Field Group 5: Large data objects go at the end + domainname namestorage; + RData rdatastorage; // Normally the storage is right here, except for oversized records + // rdatastorage MUST be the last thing in the structure -- when using oversized AuthRecords, extra bytes + // are appended after the end of the AuthRecord, logically augmenting the size of the rdatastorage + // DO NOT ADD ANY MORE FIELDS HERE +}; // IsLocalDomain alone is not sufficient to determine that a record is mDNS or uDNS. By default domain names within // the "local" pseudo-TLD (and within the IPv4 and IPv6 link-local reverse mapping domains) are automatically treated @@ -1282,11 +1377,11 @@ struct AuthRecord_struct // Everywhere else in the code, the determination of whether a question is unicast is made by checking to see if TargetQID is nonzero. #define AuthRecord_uDNS(R) ((R)->resrec.InterfaceID == mDNSInterface_Any && !(R)->ForceMCast && !IsLocalDomain((R)->resrec.name)) #define Question_uDNS(Q) ((Q)->InterfaceID == mDNSInterface_Unicast || \ - ((Q)->InterfaceID != mDNSInterface_LocalOnly && (Q)->InterfaceID != mDNSInterface_P2P && !(Q)->ForceMCast && !IsLocalDomain(&(Q)->qname))) + ((Q)->InterfaceID != mDNSInterface_LocalOnly && (Q)->InterfaceID != mDNSInterface_P2P && !(Q)->ForceMCast && !IsLocalDomain(&(Q)->qname))) #define RRLocalOnly(rr) ((rr)->ARType == AuthRecordLocalOnly || (rr)->ARType == AuthRecordP2P) -#define RRAny(rr) ((rr)->ARType == AuthRecordAny || (rr)->ARType == AuthRecordAnyIncludeP2P) +#define RRAny(rr) ((rr)->ARType == AuthRecordAny || (rr)->ARType == AuthRecordAnyIncludeP2P || (rr)->ARType == AuthRecordAnyIncludeAWDL) // Question (A or AAAA) that is suppressed currently because IPv4 or IPv6 address // is not available locally for A or AAAA question respectively @@ -1301,47 +1396,61 @@ struct AuthRecord_struct // 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 element of structure, to accomodate oversized AuthRecords - } ARListElem; - -struct CacheGroup_struct // Header object for a list of CacheRecords with the same name - { - CacheGroup *next; // Next CacheGroup object in this hash table bucket - mDNSu32 namehash; // Name-based (i.e. case insensitive) hash of name - CacheRecord *members; // List of CacheRecords with this same name - CacheRecord **rrcache_tail; // Tail end of that list - domainname *name; // Common name for all CacheRecords in this list - // Size to here is 20 bytes when compiling 32-bit; 40 bytes when compiling 64-bit - mDNSu8 namestorage[InlineCacheGroupNameSize]; - }; - +{ + struct ARListElem *next; + AuthRecord ar; // Note: Must be last element of structure, to accomodate oversized AuthRecords +} ARListElem; struct CacheRecord_struct - { - CacheRecord *next; // Next in list; first element of structure for efficiency reasons - ResourceRecord resrec; // 36 bytes when compiling for 32-bit; 48 when compiling for 64-bit - - // 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 - DNSQuestion *CRActiveQuestion; // Points to an active question referencing this answer. Can never point to a NewQuestion. - 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 +{ + CacheRecord *next; // Next in list; first element of structure for efficiency reasons + ResourceRecord resrec; // 36 bytes when compiling for 32-bit; 48 when compiling for 64-bit + + // 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 + DNSQuestion *CRActiveQuestion; // Points to an active question referencing this answer. Can never point to a NewQuestion. + mDNSs32 LastUnansweredTime; // In platform time units; last time we incremented UnansweredQueries + mDNSu16 UnansweredQueries; // Number of times we've issued a query for this record without getting an answer + mDNSu16 rcode; // Error code needed for NSEC proofs #if ENABLE_MULTI_PACKET_QUERY_SNOOPING - mDNSu32 MPUnansweredQ; // Multi-packet query handling: Number of times we've seen a query for this record - mDNSs32 MPLastUnansweredQT; // Multi-packet query handling: Last time we incremented MPUnansweredQ - mDNSu32 MPUnansweredKA; // Multi-packet query handling: Number of times we've seen this record in a KA list - mDNSBool MPExpectingKA; // Multi-packet query handling: Set when we increment MPUnansweredQ; allows one KA + mDNSu32 MPUnansweredQ; // Multi-packet query handling: Number of times we've seen a query for this record + mDNSs32 MPLastUnansweredQT; // Multi-packet query handling: Last time we incremented MPUnansweredQ + mDNSu32 MPUnansweredKA; // Multi-packet query handling: Number of times we've seen this record in a KA list + mDNSBool MPExpectingKA; // Multi-packet query handling: Set when we increment MPUnansweredQ; allows one KA #endif - CacheRecord *NextInCFList; // Set if this is in the list of records we just received with the cache flush bit set - // Size to here is 76 bytes when compiling 32-bit; 104 bytes when compiling 64-bit - RData_small smallrdatastorage; // Storage for small records is right here (4 bytes header + 68 bytes data = 72 bytes) - }; + CacheRecord *NextInCFList; // Set if this is in the list of records we just received with the cache flush bit set + CacheRecord *nsec; // NSEC records needed for non-existence proofs + + mDNSAddr sourceAddress; // node from which we received this record + // Size to here is 76 bytes when compiling 32-bit; 104 bytes when compiling 64-bit + RData_small smallrdatastorage; // Storage for small records is right here (4 bytes header + 68 bytes data = 72 bytes) +}; + +// Should match the CacheGroup_struct members, except namestorage[]. Only used to calculate +// the size of the namestorage array in CacheGroup_struct so that +// sizeof(CacheGroup) == sizeof(CacheRecord) +struct CacheGroup_base +{ + CacheGroup *next; + mDNSu32 namehash; + CacheRecord *members; + CacheRecord **rrcache_tail; + domainname *name; +}; + +struct CacheGroup_struct // Header object for a list of CacheRecords with the same name +{ + CacheGroup *next; // Next CacheGroup object in this hash table bucket + mDNSu32 namehash; // Name-based (i.e. case insensitive) hash of name + CacheRecord *members; // List of CacheRecords with this same name + CacheRecord **rrcache_tail; // Tail end of that list + domainname *name; // Common name for all CacheRecords in this list + mDNSu8 namestorage[sizeof(CacheRecord) - sizeof(struct CacheGroup_base)]; // match sizeof(CacheRecord) +}; // Storage sufficient to hold either a CacheGroup header or a CacheRecord // -- for best efficiency (to avoid wasted unused storage) they should be the same size @@ -1349,36 +1458,36 @@ typedef union CacheEntity_union CacheEntity; union CacheEntity_union { CacheEntity *next; CacheGroup cg; CacheRecord cr; }; typedef struct - { - CacheRecord r; - mDNSu8 _extradata[MaximumRDSize-InlineCacheRDSize]; // Glue on the necessary number of extra bytes - domainname namestorage; // Needs to go *after* the extra rdata bytes - } LargeCacheRecord; +{ + CacheRecord r; + mDNSu8 _extradata[MaximumRDSize-InlineCacheRDSize]; // Glue on the necessary number of extra bytes + domainname namestorage; // Needs to go *after* the extra rdata bytes +} LargeCacheRecord; typedef struct HostnameInfo - { - struct HostnameInfo *next; - NATTraversalInfo natinfo; - domainname fqdn; - AuthRecord arv4; // registered IPv4 address record - AuthRecord arv6; // registered IPv6 address record - mDNSRecordCallback *StatusCallback; // callback to deliver success or error code to client layer - const void *StatusContext; // Client Context - } HostnameInfo; +{ + struct HostnameInfo *next; + NATTraversalInfo natinfo; + domainname fqdn; + AuthRecord arv4; // registered IPv4 address record + AuthRecord arv6; // registered IPv6 address record + mDNSRecordCallback *StatusCallback; // callback to deliver success or error code to client layer + const void *StatusContext; // Client Context +} HostnameInfo; 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 - // that this extra memory is available, which would result in any fields after the AuthRecord getting smashed - }; +{ + 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 + // that this extra memory is available, which would result in any fields after the AuthRecord getting smashed +}; // Note: Within an mDNSServiceCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Exit(), mDNS_Execute() -typedef void mDNSServiceCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result); +typedef void mDNSServiceCallback (mDNS *const m, ServiceRecordSet *const sr, mStatus result); // A ServiceRecordSet has no special meaning to the core code of the Multicast DNS protocol engine; // it is just a convenience structure to group together the records that make up a standard service @@ -1391,24 +1500,24 @@ typedef void mDNSServiceCallback(mDNS *const m, ServiceRecordSet *const sr, mSta // * the optional list of additional records attached to the service set (e.g. iChat pictures) struct ServiceRecordSet_struct - { - // These internal state fields are used internally by mDNSCore; the client layer needn't be concerned with them. - // No fields need to be set up by the client prior to calling mDNS_RegisterService(); - // all required data is passed as parameters to that function. - mDNSServiceCallback *ServiceCallback; - void *ServiceContext; - mDNSBool Conflict; // Set if this record set was forcibly deregistered because of a conflict - - ExtraResourceRecord *Extras; // Optional list of extra AuthRecords attached to this service registration - mDNSu32 NumSubTypes; - AuthRecord *SubTypes; - AuthRecord RR_ADV; // e.g. _services._dns-sd._udp.local. PTR _printer._tcp.local. - AuthRecord RR_PTR; // e.g. _printer._tcp.local. PTR Name._printer._tcp.local. - AuthRecord RR_SRV; // e.g. Name._printer._tcp.local. SRV 0 0 port target - AuthRecord RR_TXT; // e.g. Name._printer._tcp.local. TXT PrintQueueName - // Don't add any fields after AuthRecord RR_TXT. - // This is where the implicit extra space goes if we allocate a ServiceRecordSet containing an oversized RR_TXT record - }; +{ + // These internal state fields are used internally by mDNSCore; the client layer needn't be concerned with them. + // No fields need to be set up by the client prior to calling mDNS_RegisterService(); + // all required data is passed as parameters to that function. + mDNSServiceCallback *ServiceCallback; + void *ServiceContext; + mDNSBool Conflict; // Set if this record set was forcibly deregistered because of a conflict + + ExtraResourceRecord *Extras; // Optional list of extra AuthRecords attached to this service registration + mDNSu32 NumSubTypes; + AuthRecord *SubTypes; + AuthRecord RR_ADV; // e.g. _services._dns-sd._udp.local. PTR _printer._tcp.local. + AuthRecord RR_PTR; // e.g. _printer._tcp.local. PTR Name._printer._tcp.local. + AuthRecord RR_SRV; // e.g. Name._printer._tcp.local. SRV 0 0 port target + AuthRecord RR_TXT; // e.g. Name._printer._tcp.local. TXT PrintQueueName + // Don't add any fields after AuthRecord RR_TXT. + // This is where the implicit extra space goes if we allocate a ServiceRecordSet containing an oversized RR_TXT record +}; // *************************************************************************** #if 0 @@ -1423,19 +1532,19 @@ struct ServiceRecordSet_struct #define DupSuppressInfoSize 8 typedef struct - { - mDNSs32 Time; - mDNSInterfaceID InterfaceID; - mDNSs32 Type; // v4 or v6? - } DupSuppressInfo; +{ + mDNSs32 Time; + mDNSInterfaceID InterfaceID; + mDNSs32 Type; // v4 or v6? +} DupSuppressInfo; typedef enum - { - LLQ_InitialRequest = 1, - LLQ_SecondaryRequest = 2, - LLQ_Established = 3, - LLQ_Poll = 4 - } LLQ_State; +{ + LLQ_InitialRequest = 1, + LLQ_SecondaryRequest = 2, + LLQ_Established = 3, + LLQ_Poll = 4 +} LLQ_State; // LLQ constants #define kLLQ_Vers 1 @@ -1449,15 +1558,15 @@ typedef enum // LLQ Errror Codes enum - { - LLQErr_NoError = 0, - LLQErr_ServFull = 1, - LLQErr_Static = 2, - LLQErr_FormErr = 3, - LLQErr_NoSuchLLQ = 4, - LLQErr_BadVers = 5, - LLQErr_UnknownErr = 6 - }; +{ + LLQErr_NoError = 0, + LLQErr_ServFull = 1, + LLQErr_Static = 2, + LLQErr_FormErr = 3, + LLQErr_NoSuchLLQ = 4, + LLQErr_BadVers = 5, + LLQErr_UnknownErr = 6 +}; enum { NoAnswer_Normal = 0, NoAnswer_Suspended = 1, NoAnswer_Fail = 2 }; @@ -1466,216 +1575,234 @@ enum { NoAnswer_Normal = 0, NoAnswer_Suspended = 1, NoAnswer_Fail = 2 }; #define HMAC_OPAD 0x5c #define MD5_LEN 16 -#define AutoTunnelUnregistered(X) ( \ - (X)->AutoTunnelHostRecord.resrec.RecordType == kDNSRecordTypeUnregistered && \ - (X)->AutoTunnelDeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered && \ - (X)->AutoTunnelService. resrec.RecordType == kDNSRecordTypeUnregistered && \ - (X)->AutoTunnel6Record. resrec.RecordType == kDNSRecordTypeUnregistered ) +#define AutoTunnelUnregistered(X) ( \ + (X)->AutoTunnelHostRecord.resrec.RecordType == kDNSRecordTypeUnregistered && \ + (X)->AutoTunnelTarget.resrec.RecordType == kDNSRecordTypeUnregistered && \ + (X)->AutoTunnelDeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered && \ + (X)->AutoTunnelService.resrec.RecordType == kDNSRecordTypeUnregistered && \ + (X)->AutoTunnel6Record.resrec.RecordType == kDNSRecordTypeUnregistered ) // Internal data structure to maintain authentication information typedef struct DomainAuthInfo - { - struct DomainAuthInfo *next; - mDNSs32 deltime; // If we're planning to delete this DomainAuthInfo, the time we want it deleted - const char* AutoTunnel; // If NULL, this is not an AutoTunnel DAI. Otherwise, this is prepended to the IPSec identifier - AuthRecord AutoTunnelHostRecord; // User-visible hostname; used as SRV target for AutoTunnel services - AuthRecord AutoTunnelTarget; // Opaque hostname of tunnel endpoint; used as SRV target for AutoTunnelService record - AuthRecord AutoTunnelDeviceInfo; // Device info of tunnel endpoint - AuthRecord AutoTunnelService; // Service record (possibly NAT-Mapped) of IKE daemon implementing tunnel endpoint - AuthRecord AutoTunnel6Record; // AutoTunnel AAAA Record obtained from Connectivityd - NATTraversalInfo AutoTunnelNAT; - domainname domain; - domainname keyname; - domainname hostname; - mDNSIPPort port; - char b64keydata[32]; - mDNSu8 keydata_ipad[HMAC_LEN]; // padded key for inner hash rounds - mDNSu8 keydata_opad[HMAC_LEN]; // padded key for outer hash rounds - } DomainAuthInfo; +{ + struct DomainAuthInfo *next; + mDNSs32 deltime; // If we're planning to delete this DomainAuthInfo, the time we want it deleted + mDNSBool AutoTunnel; // Whether this is AutoTunnel + AuthRecord AutoTunnelHostRecord; // User-visible hostname; used as SRV target for AutoTunnel services + AuthRecord AutoTunnelTarget; // Opaque hostname of tunnel endpoint; used as SRV target for AutoTunnelService record + AuthRecord AutoTunnelDeviceInfo; // Device info of tunnel endpoint + AuthRecord AutoTunnelService; // Service record (possibly NAT-Mapped) of IKE daemon implementing tunnel endpoint + AuthRecord AutoTunnel6Record; // AutoTunnel AAAA Record obtained from awacsd + mDNSBool AutoTunnelServiceStarted; // Whether a service has been registered in this domain + mDNSv6Addr AutoTunnelInnerAddress; + domainname domain; + domainname keyname; + domainname hostname; + mDNSIPPort port; + char b64keydata[32]; + mDNSu8 keydata_ipad[HMAC_LEN]; // padded key for inner hash rounds + mDNSu8 keydata_opad[HMAC_LEN]; // padded key for outer hash rounds +} DomainAuthInfo; // Note: Within an mDNSQuestionCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Exit(), mDNS_Execute() -typedef enum { QC_rmv = 0, QC_add = 1, QC_addnocache = 2 } QC_result; -typedef void mDNSQuestionCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord); +// Note: Any value other than QC_rmv i.e., any non-zero value will result in kDNSServiceFlagsAdd to the application +// layer. These values are used within mDNSResponder and not sent across to the application. QC_addnocache is for +// delivering a response without adding to the cache. QC_forceresponse is superset of QC_addnocache where in +// addition to not entering in the cache, it also forces the negative response through. +typedef enum { QC_rmv = 0, QC_add, QC_addnocache, QC_forceresponse, QC_dnssec } QC_result; +typedef void mDNSQuestionCallback (mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord); #define NextQSendTime(Q) ((Q)->LastQTime + (Q)->ThisQInterval) #define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf) #define TimeToSendThisQuestion(Q,time) (ActiveQuestion(Q) && (time) - NextQSendTime(Q) >= 0) +// q->ValidationStatus is either DNSSECValNotRequired or DNSSECValRequired and then moves onto DNSSECValInProgress. +// When Validation is done, we mark all "DNSSECValInProgress" questions "DNSSECValDone". If we are answering +// questions from /etc/hosts, then we go straight to DNSSECValDone from the initial state. +typedef enum { DNSSECValNotRequired = 0, DNSSECValRequired, DNSSECValInProgress, DNSSECValDone } DNSSECValState; + +#define DNSSECQuestion(q) ((q)->ValidationRequired || (q)->ValidatingResponse) + 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 (should not still be in list) - mDNSs32 ExpectUnicastResp;// Set when we send a query with the kDNSQClass_UnicastResponse bit set - 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 - mDNSInterfaceID FlappingInterface1;// Set when an interface goes away, to flag if remove events are delivered for this Q - mDNSInterfaceID FlappingInterface2;// Set when an interface goes away, to flag if remove events are delivered for this Q - DomainAuthInfo *AuthInfo; // Non-NULL if query is currently being done using Private DNS - DNSQuestion *DuplicateOf; - DNSQuestion *NextInDQList; - DupSuppressInfo DupSuppress[DupSuppressInfoSize]; - mDNSInterfaceID SendQNow; // The interface this query is being sent on right now - mDNSBool SendOnAll; // Set if we're sending this question on all active interfaces - mDNSu32 RequestUnicast; // Non-zero if we want to send query with kDNSQClass_UnicastResponse bit set - mDNSs32 LastQTxTime; // Last time this Q was sent on one (but not necessarily all) interfaces - mDNSu32 CNAMEReferrals; // Count of how many CNAME redirections we've done - mDNSBool SuppressQuery; // This query should be suppressed and not sent on the wire - mDNSu8 LOAddressAnswers; // Number of answers from the local only auth records that are - // answering A, AAAA and CNAME (/etc/hosts) - mDNSu8 WakeOnResolveCount; // Number of wakes that should be sent on resolve - mDNSs32 StopTime; // Time this question should be stopped by giving them a negative answer - - // Wide Area fields. These are used internally by the uDNS core - UDPSocket *LocalSocket; - mDNSBool deliverAddEvents; // Change in DNSSserver requiring to deliver ADD events - DNSServer *qDNSServer; // Caching server for this query (in the absence of an SRV saying otherwise) - mDNSOpaque64 validDNSServers; // Valid DNSServers for this question - mDNSu16 noServerResponse; // At least one server did not respond. - mDNSu16 triedAllServersOnce; // Tried all DNS servers once - mDNSu8 unansweredQueries;// The number of unanswered queries to this server - - ZoneData *nta; // Used for getting zone data for private or LLQ query - mDNSAddr servAddr; // Address and port learned from _dns-llq, _dns-llq-tls or _dns-query-tls SRV query - mDNSIPPort servPort; - struct tcpInfo_t *tcp; - mDNSIPPort tcpSrcPort; // Local Port TCP packet received on;need this as tcp struct is disposed - // by tcpCallback before calling into mDNSCoreReceive - mDNSu8 NoAnswer; // Set if we want to suppress answers until tunnel setup has completed - - // LLQ-specific fields. These fields are only meaningful when LongLived flag is set - LLQ_State state; - mDNSu32 ReqLease; // seconds (relative) - mDNSs32 expire; // ticks (absolute) - mDNSs16 ntries; // for UDP: the number of packets sent for this LLQ state - // for TCP: there is some ambiguity in the use of this variable, but in general, it is - // the number of TCP/TLS connection attempts for this LLQ state, or - // the number of packets sent for this TCP/TLS connection - mDNSOpaque64 id; - - // 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 - mDNSAddr Target; // Non-zero if you want to direct queries to a specific unicast target address - mDNSIPPort TargetPort; // Must be set if Target is set - mDNSOpaque16 TargetQID; // Must be set if Target is set - domainname qname; - mDNSu16 qtype; - mDNSu16 qclass; - 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 - mDNSBool ReturnIntermed; // Set by client to request callbacks for intermediate CNAME/NXDOMAIN results - mDNSBool SuppressUnusable; // Set by client to suppress unusable queries to be sent on the wire - mDNSBool RetryWithSearchDomains; // Retry with search domains if there is no entry in the cache or AuthRecords - mDNSu8 TimeoutQuestion; // Timeout this question if there is no reply in configured time - mDNSu8 WakeOnResolve; // Send wakeup on resolve - mDNSs8 SearchListIndex; // Index into SearchList; Used by the client layer but not touched by core - mDNSs8 AppendSearchDomains; // Search domains can be appended for this query - mDNSs8 AppendLocalSearchDomains; // Search domains ending in .local can be appended for this query - domainname *qnameOrig; // Copy of the original question name if it is not fully qualified - mDNSQuestionCallback *QuestionCallback; - void *QuestionContext; - }; +{ + // 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 (should not still be in list) + mDNSs32 ExpectUnicastResp; // Set when we send a query with the kDNSQClass_UnicastResponse bit set + 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 + mDNSInterfaceID FlappingInterface1; // Set when an interface goes away, to flag if remove events are delivered for this Q + mDNSInterfaceID FlappingInterface2; // Set when an interface goes away, to flag if remove events are delivered for this Q + DomainAuthInfo *AuthInfo; // Non-NULL if query is currently being done using Private DNS + DNSQuestion *DuplicateOf; + DNSQuestion *NextInDQList; + DupSuppressInfo DupSuppress[DupSuppressInfoSize]; + mDNSInterfaceID SendQNow; // The interface this query is being sent on right now + mDNSBool SendOnAll; // Set if we're sending this question on all active interfaces + mDNSu32 RequestUnicast; // Non-zero if we want to send query with kDNSQClass_UnicastResponse bit set + mDNSs32 LastQTxTime; // Last time this Q was sent on one (but not necessarily all) interfaces + mDNSu32 CNAMEReferrals; // Count of how many CNAME redirections we've done + mDNSBool SuppressQuery; // This query should be suppressed and not sent on the wire + mDNSu8 LOAddressAnswers; // Number of answers from the local only auth records that are + // answering A, AAAA and CNAME (/etc/hosts) + mDNSu8 WakeOnResolveCount; // Number of wakes that should be sent on resolve + mDNSs32 StopTime; // Time this question should be stopped by giving them a negative answer + + // Wide Area fields. These are used internally by the uDNS core + UDPSocket *LocalSocket; + DNSServer *qDNSServer; // Caching server for this query (in the absence of an SRV saying otherwise) + mDNSOpaque64 validDNSServers; // Valid DNSServers for this question + mDNSu16 noServerResponse; // At least one server did not respond. + mDNSu16 triedAllServersOnce; // Tried all DNS servers once + mDNSu8 unansweredQueries; // The number of unanswered queries to this server + + ZoneData *nta; // Used for getting zone data for private or LLQ query + mDNSAddr servAddr; // Address and port learned from _dns-llq, _dns-llq-tls or _dns-query-tls SRV query + mDNSIPPort servPort; + struct tcpInfo_t *tcp; + mDNSIPPort tcpSrcPort; // Local Port TCP packet received on;need this as tcp struct is disposed + // by tcpCallback before calling into mDNSCoreReceive + mDNSu8 NoAnswer; // Set if we want to suppress answers until tunnel setup has completed + + // LLQ-specific fields. These fields are only meaningful when LongLived flag is set + LLQ_State state; + mDNSu32 ReqLease; // seconds (relative) + mDNSs32 expire; // ticks (absolute) + mDNSs16 ntries; // for UDP: the number of packets sent for this LLQ state + // for TCP: there is some ambiguity in the use of this variable, but in general, it is + // the number of TCP/TLS connection attempts for this LLQ state, or + // the number of packets sent for this TCP/TLS connection + DNSSECValState ValidationState; // Current state of the Validation process + DNSSECStatus ValidationStatus; // Validation status for "ValidationRequired" questions (dnssec) + mDNSu8 ValidatingResponse; // Question trying to validate a response (dnssec) on behalf of + // ValidationRequired question + mDNSOpaque64 id; + + // 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 + mDNSu32 flags; // flags from original DNSService*() API request. + mDNSAddr Target; // Non-zero if you want to direct queries to a specific unicast target address + mDNSIPPort TargetPort; // Must be set if Target is set + mDNSOpaque16 TargetQID; // Must be set if Target is set + domainname qname; + mDNSu16 qtype; + mDNSu16 qclass; + 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 + mDNSBool ReturnIntermed; // Set by client to request callbacks for intermediate CNAME/NXDOMAIN results + mDNSBool SuppressUnusable; // Set by client to suppress unusable queries to be sent on the wire + mDNSBool RetryWithSearchDomains; // Retry with search domains if there is no entry in the cache or AuthRecords + mDNSu8 TimeoutQuestion; // Timeout this question if there is no reply in configured time + mDNSu8 WakeOnResolve; // Send wakeup on resolve + mDNSu8 UseBrackgroundTrafficClass; // Use background traffic class for request + mDNSs8 SearchListIndex; // Index into SearchList; Used by the client layer but not touched by core + mDNSs8 AppendSearchDomains; // Search domains can be appended for this query + mDNSs8 AppendLocalSearchDomains; // Search domains ending in .local can be appended for this query + mDNSu8 ValidationRequired; // Requires DNSSEC validation. + domainname *qnameOrig; // Copy of the original question name if it is not fully qualified + mDNSQuestionCallback *QuestionCallback; + void *QuestionContext; +}; typedef struct - { - // Client API fields: The client must set up name and InterfaceID *before* calling mDNS_StartResolveService() - // When the callback is invoked, ip, port, TXTlen and TXTinfo will have been filled in with the results learned from the network. - domainname name; - mDNSInterfaceID InterfaceID; // ID of the interface the response was received on - mDNSAddr ip; // Remote (destination) IP address where this service can be accessed - mDNSIPPort port; // Port where this service can be accessed - mDNSu16 TXTlen; - mDNSu8 TXTinfo[2048]; // Additional demultiplexing information (e.g. LPR queue name) - } ServiceInfo; +{ + // Client API fields: The client must set up name and InterfaceID *before* calling mDNS_StartResolveService() + // When the callback is invoked, ip, port, TXTlen and TXTinfo will have been filled in with the results learned from the network. + domainname name; + mDNSInterfaceID InterfaceID; // ID of the interface the response was received on + mDNSAddr ip; // Remote (destination) IP address where this service can be accessed + mDNSIPPort port; // Port where this service can be accessed + mDNSu16 TXTlen; + mDNSu8 TXTinfo[2048]; // Additional demultiplexing information (e.g. LPR queue name) +} ServiceInfo; // Note: Within an mDNSServiceInfoQueryCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Exit(), mDNS_Execute() typedef struct ServiceInfoQuery_struct ServiceInfoQuery; -typedef void mDNSServiceInfoQueryCallback(mDNS *const m, ServiceInfoQuery *query); +typedef void mDNSServiceInfoQueryCallback (mDNS *const m, ServiceInfoQuery *query); struct ServiceInfoQuery_struct - { - // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. - // No fields need to be set up by the client prior to calling mDNS_StartResolveService(); - // all required data is passed as parameters to that function. - // The ServiceInfoQuery structure memory is working storage for mDNSCore to discover the requested information - // and place it in the ServiceInfo structure. After the client has called mDNS_StopResolveService(), it may - // dispose of the ServiceInfoQuery structure while retaining the results in the ServiceInfo structure. - DNSQuestion qSRV; - DNSQuestion qTXT; - DNSQuestion qAv4; - DNSQuestion qAv6; - mDNSu8 GotSRV; - mDNSu8 GotTXT; - mDNSu8 GotADD; - mDNSu32 Answers; - ServiceInfo *info; - mDNSServiceInfoQueryCallback *ServiceInfoQueryCallback; - void *ServiceInfoQueryContext; - }; +{ + // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. + // No fields need to be set up by the client prior to calling mDNS_StartResolveService(); + // all required data is passed as parameters to that function. + // The ServiceInfoQuery structure memory is working storage for mDNSCore to discover the requested information + // and place it in the ServiceInfo structure. After the client has called mDNS_StopResolveService(), it may + // dispose of the ServiceInfoQuery structure while retaining the results in the ServiceInfo structure. + DNSQuestion qSRV; + DNSQuestion qTXT; + DNSQuestion qAv4; + DNSQuestion qAv6; + mDNSu8 GotSRV; + mDNSu8 GotTXT; + mDNSu8 GotADD; + mDNSu32 Answers; + ServiceInfo *info; + mDNSServiceInfoQueryCallback *ServiceInfoQueryCallback; + void *ServiceInfoQueryContext; +}; typedef enum { ZoneServiceUpdate, ZoneServiceQuery, ZoneServiceLLQ } ZoneService; -typedef void ZoneDataCallback(mDNS *const m, mStatus err, const ZoneData *result); +typedef void ZoneDataCallback (mDNS *const m, mStatus err, const ZoneData *result); struct ZoneData_struct - { - domainname ChildName; // Name for which we're trying to find the responsible server - ZoneService ZoneService; // Which service we're seeking for this zone (update, query, or LLQ) - domainname *CurrentSOA; // Points to somewhere within ChildName - domainname ZoneName; // Discovered result: Left-hand-side of SOA record - mDNSu16 ZoneClass; // Discovered result: DNS Class from SOA record - domainname Host; // Discovered result: Target host from SRV record - mDNSIPPort Port; // Discovered result: Update port, query port, or LLQ port from SRV record - mDNSAddr Addr; // Discovered result: Address of Target host from SRV record - mDNSBool ZonePrivate; // Discovered result: Does zone require encrypted queries? - ZoneDataCallback *ZoneDataCallback; // Caller-specified function to be called upon completion - void *ZoneDataContext; - DNSQuestion question; // Storage for any active question - }; +{ + domainname ChildName; // Name for which we're trying to find the responsible server + ZoneService ZoneService; // Which service we're seeking for this zone (update, query, or LLQ) + domainname *CurrentSOA; // Points to somewhere within ChildName + domainname ZoneName; // Discovered result: Left-hand-side of SOA record + mDNSu16 ZoneClass; // Discovered result: DNS Class from SOA record + domainname Host; // Discovered result: Target host from SRV record + mDNSIPPort Port; // Discovered result: Update port, query port, or LLQ port from SRV record + mDNSAddr Addr; // Discovered result: Address of Target host from SRV record + mDNSBool ZonePrivate; // Discovered result: Does zone require encrypted queries? + ZoneDataCallback *ZoneDataCallback; // Caller-specified function to be called upon completion + void *ZoneDataContext; + DNSQuestion question; // Storage for any active question +}; extern ZoneData *StartGetZoneData(mDNS *const m, const domainname *const name, const ZoneService target, ZoneDataCallback callback, void *callbackInfo); extern void CancelGetZoneData(mDNS *const m, ZoneData *nta); extern mDNSBool IsGetZoneDataQuestion(DNSQuestion *q); typedef struct DNameListElem - { - struct DNameListElem *next; - mDNSu32 uid; - domainname name; - } DNameListElem; +{ + struct DNameListElem *next; + mDNSu32 uid; + domainname name; +} DNameListElem; #if APPLE_OSX_mDNSResponder // Different states that we go through locating the peer -#define TC_STATE_AAAA_PEER 0x000000001 /* Peer's BTMM IPv6 address */ -#define TC_STATE_AAAA_PEER_RELAY 0x000000002 /* Peer's IPv6 Relay address */ -#define TC_STATE_SRV_PEER 0x000000003 /* Peer's SRV Record corresponding to IPv4 address */ -#define TC_STATE_ADDR_PEER 0x000000004 /* Peer's IPv4 address */ +#define TC_STATE_AAAA_PEER 0x000000001 /* Peer's BTMM IPv6 address */ +#define TC_STATE_AAAA_PEER_RELAY 0x000000002 /* Peer's IPv6 Relay address */ +#define TC_STATE_SRV_PEER 0x000000003 /* Peer's SRV Record corresponding to IPv4 address */ +#define TC_STATE_ADDR_PEER 0x000000004 /* Peer's IPv4 address */ typedef struct ClientTunnel - { - struct ClientTunnel *next; - const char *prefix; - domainname dstname; - mDNSBool MarkedForDeletion; - mDNSv6Addr loc_inner; - mDNSv4Addr loc_outer; - mDNSv6Addr loc_outer6; - mDNSv6Addr rmt_inner; - mDNSv4Addr rmt_outer; - mDNSv6Addr rmt_outer6; - mDNSIPPort rmt_outer_port; - mDNSu16 tc_state; - DNSQuestion q; - } ClientTunnel; +{ + struct ClientTunnel *next; + domainname dstname; + mDNSBool MarkedForDeletion; + mDNSv6Addr loc_inner; + mDNSv4Addr loc_outer; + mDNSv6Addr loc_outer6; + mDNSv6Addr rmt_inner; + mDNSv4Addr rmt_outer; + mDNSv6Addr rmt_outer6; + mDNSIPPort rmt_outer_port; + mDNSu16 tc_state; + DNSQuestion q; +} ClientTunnel; #endif // *************************************************************************** @@ -1696,62 +1823,63 @@ typedef struct NetworkInterfaceInfo_struct NetworkInterfaceInfo; // active representative of the set; all others have the 'InterfaceActive' flag unset. struct NetworkInterfaceInfo_struct - { - // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. - NetworkInterfaceInfo *next; - - mDNSu8 InterfaceActive; // Set if interface is sending & receiving packets (see comment above) - mDNSu8 IPv4Available; // If InterfaceActive, set if v4 available on this InterfaceID - mDNSu8 IPv6Available; // If InterfaceActive, set if v6 available on this InterfaceID - - DNSQuestion NetWakeBrowse; - DNSQuestion NetWakeResolve[3]; // For fault-tolerance, we try up to three Sleep Proxies - mDNSAddr SPSAddr[3]; - mDNSIPPort SPSPort[3]; - mDNSs32 NextSPSAttempt; // -1 if we're not currently attempting to register with any Sleep Proxy - mDNSs32 NextSPSAttemptTime; - - // Standard AuthRecords that every Responder host should have (one per active IP address) - AuthRecord RR_A; // 'A' or 'AAAA' (address) record for our ".local" name - AuthRecord RR_PTR; // PTR (reverse lookup) record - AuthRecord RR_HINFO; - - // Client API fields: The client must set up these fields *before* calling mDNS_RegisterInterface() - mDNSInterfaceID InterfaceID; // Identifies physical interface; MUST NOT be 0, -1, or -2 - mDNSAddr ip; // The IPv4 or IPv6 address to advertise - mDNSAddr mask; - mDNSEthAddr MAC; - char ifname[64]; // Windows uses a GUID string for the interface name, which doesn't fit in 16 bytes - mDNSu8 Advertise; // False if you are only searching on this interface - mDNSu8 McastTxRx; // Send/Receive multicast on this { InterfaceID, address family } ? - mDNSu8 NetWake; // Set if Wake-On-Magic-Packet is enabled on this interface - mDNSu8 Loopback; // Set if this is the loopback interface - }; +{ + // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. + NetworkInterfaceInfo *next; + + mDNSu8 InterfaceActive; // Set if interface is sending & receiving packets (see comment above) + mDNSu8 IPv4Available; // If InterfaceActive, set if v4 available on this InterfaceID + mDNSu8 IPv6Available; // If InterfaceActive, set if v6 available on this InterfaceID + + DNSQuestion NetWakeBrowse; + DNSQuestion NetWakeResolve[3]; // For fault-tolerance, we try up to three Sleep Proxies + mDNSAddr SPSAddr[3]; + mDNSIPPort SPSPort[3]; + mDNSs32 NextSPSAttempt; // -1 if we're not currently attempting to register with any Sleep Proxy + mDNSs32 NextSPSAttemptTime; + + // Standard AuthRecords that every Responder host should have (one per active IP address) + AuthRecord RR_A; // 'A' or 'AAAA' (address) record for our ".local" name + AuthRecord RR_PTR; // PTR (reverse lookup) record + AuthRecord RR_HINFO; + + // Client API fields: The client must set up these fields *before* calling mDNS_RegisterInterface() + mDNSInterfaceID InterfaceID; // Identifies physical interface; MUST NOT be 0, -1, or -2 + mDNSAddr ip; // The IPv4 or IPv6 address to advertise + mDNSAddr mask; + mDNSEthAddr MAC; + char ifname[64]; // Windows uses a GUID string for the interface name, which doesn't fit in 16 bytes + mDNSu8 Advertise; // False if you are only searching on this interface + mDNSu8 McastTxRx; // Send/Receive multicast on this { InterfaceID, address family } ? + mDNSu8 NetWake; // Set if Wake-On-Magic-Packet is enabled on this interface + mDNSu8 Loopback; // Set if this is the loopback interface + AuthRecord *SPSRRSet; // To help the client keep track of the records registered with the sleep proxy +}; #define SLE_DELETE 0x00000001 #define SLE_WAB_QUERY_STARTED 0x00000002 typedef struct SearchListElem - { - struct SearchListElem *next; - domainname domain; - int flag; - mDNSInterfaceID InterfaceID; - DNSQuestion BrowseQ; - DNSQuestion DefBrowseQ; - DNSQuestion AutomaticBrowseQ; - DNSQuestion RegisterQ; - DNSQuestion DefRegisterQ; - int numCfAnswers; - ARListElem *AuthRecs; - } SearchListElem; +{ + struct SearchListElem *next; + domainname domain; + int flag; + mDNSInterfaceID InterfaceID; + DNSQuestion BrowseQ; + DNSQuestion DefBrowseQ; + DNSQuestion AutomaticBrowseQ; + DNSQuestion RegisterQ; + DNSQuestion DefRegisterQ; + int numCfAnswers; + ARListElem *AuthRecs; +} SearchListElem; // For domain enumeration and automatic browsing // This is the user's DNS search list. // In each of these domains we search for our special pointer records (lb._dns-sd._udp., etc.) // to discover recommended domains for domain enumeration (browse, default browse, registration, // default registration) and possibly one or more recommended automatic browsing domains. -extern SearchListElem *SearchList; // This really ought to be part of mDNS_struct -- SC +extern SearchListElem *SearchList; // This really ought to be part of mDNS_struct -- SC // *************************************************************************** #if 0 @@ -1759,206 +1887,206 @@ extern SearchListElem *SearchList; // This really ought to be part of mDNS_stru #pragma mark - Main mDNS object, used to hold all the mDNS state #endif -typedef void mDNSCallback(mDNS *const m, mStatus result); +typedef void mDNSCallback (mDNS *const m, mStatus result); #define CACHE_HASH_SLOTS 499 -enum // Bit flags -- i.e. values should be 1, 2, 4, 8, etc. - { - mDNS_KnownBug_LimitedIPv6 = 1, - mDNS_KnownBug_LossySyslog = 2 // - }; +enum // Bit flags -- i.e. values should be 1, 2, 4, 8, etc. +{ + mDNS_KnownBug_LimitedIPv6 = 1, + mDNS_KnownBug_LossySyslog = 2 // +}; enum - { - SleepState_Awake = 0, - SleepState_Transferring = 1, - SleepState_Sleeping = 2 - }; +{ + SleepState_Awake = 0, + SleepState_Transferring = 1, + SleepState_Sleeping = 2 +}; struct mDNS_struct - { - // Internal state fields. These hold the main internal state of mDNSCore; - // the client layer needn't be concerned with them. - // No fields need to be set up by the client prior to calling mDNS_Init(); - // all required data is passed as parameters to that function. - - mDNS_PlatformSupport *p; // Pointer to platform-specific data of indeterminite size - mDNSu32 KnownBugs; - mDNSBool CanReceiveUnicastOn5353; - mDNSBool AdvertiseLocalAddresses; - mDNSBool DivertMulticastAdvertisements; // from interfaces that do not advertise local addresses to local-only - mStatus mDNSPlatformStatus; - mDNSIPPort UnicastPort4; - mDNSIPPort UnicastPort6; - mDNSEthAddr PrimaryMAC; // Used as unique host ID - mDNSCallback *MainCallback; - void *MainContext; - - // For debugging: To catch and report locking failures - mDNSu32 mDNS_busy; // Incremented between mDNS_Lock/mDNS_Unlock section - mDNSu32 mDNS_reentrancy; // Incremented when calling a client callback - mDNSu8 lock_rrcache; // For debugging: Set at times when these lists may not be modified - mDNSu8 lock_Questions; - mDNSu8 lock_Records; +{ + // Internal state fields. These hold the main internal state of mDNSCore; + // the client layer needn't be concerned with them. + // No fields need to be set up by the client prior to calling mDNS_Init(); + // all required data is passed as parameters to that function. + + mDNS_PlatformSupport *p; // Pointer to platform-specific data of indeterminite size + mDNSu32 KnownBugs; + mDNSBool CanReceiveUnicastOn5353; + mDNSBool AdvertiseLocalAddresses; + mDNSBool DivertMulticastAdvertisements; // from interfaces that do not advertise local addresses to local-only + mStatus mDNSPlatformStatus; + mDNSIPPort UnicastPort4; + mDNSIPPort UnicastPort6; + mDNSEthAddr PrimaryMAC; // Used as unique host ID + mDNSCallback *MainCallback; + void *MainContext; + + // For debugging: To catch and report locking failures + mDNSu32 mDNS_busy; // Incremented between mDNS_Lock/mDNS_Unlock section + mDNSu32 mDNS_reentrancy; // Incremented when calling a client callback + mDNSu8 lock_rrcache; // For debugging: Set at times when these lists may not be modified + mDNSu8 lock_Questions; + mDNSu8 lock_Records; #ifndef MaxMsg - #define MaxMsg 160 + #define MaxMsg 512 #endif - char MsgBuffer[MaxMsg]; // Temp storage used while building error log messages - - // Task Scheduling variables - mDNSs32 timenow_adjust; // Correction applied if we ever discover time went backwards - mDNSs32 timenow; // The time that this particular activation of the mDNS code started - mDNSs32 timenow_last; // The time the last time we ran - mDNSs32 NextScheduledEvent; // Derived from values below - mDNSs32 ShutdownTime; // Set when we're shutting down; allows us to skip some unnecessary steps - mDNSs32 SuppressSending; // Don't send local-link mDNS packets during this time - mDNSs32 NextCacheCheck; // Next time to refresh cache record before it expires - mDNSs32 NextScheduledQuery; // Next time to send query in its exponential backoff sequence - mDNSs32 NextScheduledProbe; // Next time to probe for new authoritative record - mDNSs32 NextScheduledResponse; // Next time to send authoritative record(s) in responses - mDNSs32 NextScheduledNATOp; // Next time to send NAT-traversal packets - mDNSs32 NextScheduledSPS; // Next time to purge expiring Sleep Proxy records - mDNSs32 RandomQueryDelay; // For de-synchronization of query packets on the wire - mDNSu32 RandomReconfirmDelay; // For de-synchronization of reconfirmation queries on the wire - mDNSs32 PktNum; // Unique sequence number assigned to each received packet - mDNSu8 LocalRemoveEvents; // Set if we may need to deliver remove events for local-only questions and/or local-only records - mDNSu8 SleepState; // Set if we're sleeping - mDNSu8 SleepSeqNum; // "Epoch number" of our current period of wakefulness - mDNSu8 SystemWakeOnLANEnabled; // Set if we want to register with a Sleep Proxy before going to sleep - mDNSu8 SentSleepProxyRegistration;// Set if we registered (or tried to register) with a Sleep Proxy - mDNSu8 SystemSleepOnlyIfWakeOnLAN;// Set if we may only sleep if we managed to register with a Sleep Proxy - mDNSs32 AnnounceOwner; // After waking from sleep, include OWNER option in packets until this time - mDNSs32 DelaySleep; // To inhibit re-sleeping too quickly right after wake - mDNSs32 SleepLimit; // Time window to allow deregistrations, etc., - // during which underying platform layer should inhibit system sleep - mDNSs32 NextScheduledSPRetry; // Time next sleep proxy registration action is required. - // Only valid if SleepLimit is nonzero and DelaySleep is zero. - - mDNSs32 NextScheduledStopTime; // Next time to stop a question - - // These fields only required for mDNS Searcher... - DNSQuestion *Questions; // List of all registered questions, active and inactive - DNSQuestion *NewQuestions; // Fresh questions not yet answered from cache - DNSQuestion *CurrentQuestion; // Next question about to be examined in AnswerLocalQuestions() - DNSQuestion *LocalOnlyQuestions; // Questions with InterfaceID set to mDNSInterface_LocalOnly or mDNSInterface_P2P - DNSQuestion *NewLocalOnlyQuestions; // Fresh local-only or P2P questions not yet answered - DNSQuestion *RestartQuestion; // Questions that are being restarted (stop followed by start) - mDNSu32 rrcache_size; // Total number of available cache entries - mDNSu32 rrcache_totalused; // Number of cache entries currently occupied - mDNSu32 rrcache_active; // Number of cache entries currently occupied by records that answer active questions - mDNSu32 rrcache_report; - CacheEntity *rrcache_free; - CacheGroup *rrcache_hash[CACHE_HASH_SLOTS]; - mDNSs32 rrcache_nextcheck[CACHE_HASH_SLOTS]; - - AuthHash rrauth; - - // 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 MulticastHostname; // Fully Qualified "dot-local" Host Name, e.g. "Foo.local." - UTF8str255 HIHardware; - UTF8str255 HISoftware; - AuthRecord DeviceInfo; - AuthRecord *ResourceRecords; - AuthRecord *DuplicateRecords; // Records currently 'on hold' because they are duplicates of existing records - AuthRecord *NewLocalRecords; // Fresh AuthRecords (public) not yet delivered to our local-only questions - AuthRecord *CurrentRecord; // Next AuthRecord about to be examined - mDNSBool NewLocalOnlyRecords; // Fresh AuthRecords (local only) not yet delivered to our local questions - NetworkInterfaceInfo *HostInterfaces; - mDNSs32 ProbeFailTime; - mDNSu32 NumFailedProbes; - mDNSs32 SuppressProbes; - - // Unicast-specific data - mDNSs32 NextuDNSEvent; // uDNS next event - mDNSs32 NextSRVUpdate; // Time to perform delayed update - - DNSServer *DNSServers; // list of DNS servers - McastResolver *McastResolvers; // list of Mcast Resolvers - - mDNSAddr Router; - mDNSAddr AdvertisedV4; // IPv4 address pointed to by hostname - mDNSAddr AdvertisedV6; // IPv6 address pointed to by hostname - - DomainAuthInfo *AuthInfoList; // list of domains requiring authentication for updates - - DNSQuestion ReverseMap; // Reverse-map query to find static hostname for service target - DNSQuestion AutomaticBrowseDomainQ; - domainname StaticHostname; // Current answer to reverse-map query - domainname FQDN; - HostnameInfo *Hostnames; // List of registered hostnames + hostname metadata - mDNSv6Addr AutoTunnelHostAddr; // IPv6 address advertised for AutoTunnel services on this machine - mDNSBool AutoTunnelHostAddrActive; - // AutoTunnel Relay address has two distinct uses - // AutoTunnelRelayAddrIn: If non-zero, it means that this host can be reached (inbound connection) through the relay - // AutoTunnelRelayAddrOut: If non-zero, it means that this host can use the relay to reach (outbound connection) the - // other hosts through the relay - mDNSv6Addr AutoTunnelRelayAddrIn; - mDNSv6Addr AutoTunnelRelayAddrOut; - domainlabel AutoTunnelLabel; // Used to construct hostname for *IPv4* address of tunnel endpoints - - mDNSBool StartWABQueries; // Start WAB queries for the purpose of domain enumeration - mDNSBool RegisterAutoTunnel6; - - // NAT-Traversal fields - NATTraversalInfo LLQNAT; // Single shared NAT Traversal to receive inbound LLQ notifications - NATTraversalInfo *NATTraversals; - NATTraversalInfo *CurrentNATTraversal; - mDNSs32 retryIntervalGetAddr; // delta between time sent and retry - mDNSs32 retryGetAddr; // absolute time when we retry - mDNSv4Addr ExternalAddress; - - UDPSocket *NATMcastRecvskt; // For receiving NAT-PMP AddrReply multicasts from router on port 5350 - mDNSu32 LastNATupseconds; // NAT engine uptime in seconds, from most recent NAT packet - mDNSs32 LastNATReplyLocalTime; // Local time in ticks when most recent NAT packet was received - mDNSu16 LastNATMapResultCode; // Most recent error code for mappings - - tcpLNTInfo tcpAddrInfo; // legacy NAT traversal TCP connection info for external address - tcpLNTInfo tcpDeviceInfo; // legacy NAT traversal TCP connection info for device info - tcpLNTInfo *tcpInfoUnmapList; // list of pending unmap requests - mDNSInterfaceID UPnPInterfaceID; - UDPSocket *SSDPSocket; // For SSDP request/response - mDNSBool SSDPWANPPPConnection; // whether we should send the SSDP query for WANIPConnection or WANPPPConnection - mDNSIPPort UPnPRouterPort; // port we send discovery messages to - mDNSIPPort UPnPSOAPPort; // port we send SOAP messages to - mDNSu8 *UPnPRouterURL; // router's URL string - mDNSBool UPnPWANPPPConnection; // whether we're using WANIPConnection or WANPPPConnection - mDNSu8 *UPnPSOAPURL; // router's SOAP control URL string - mDNSu8 *UPnPRouterAddressString; // holds both the router's address and port - mDNSu8 *UPnPSOAPAddressString; // holds both address and port for SOAP messages - - // Sleep Proxy Server fields - mDNSu8 SPSType; // 0 = off, 10-99 encodes desirability metric - mDNSu8 SPSPortability; // 10-99 - mDNSu8 SPSMarginalPower; // 10-99 - mDNSu8 SPSTotalPower; // 10-99 - mDNSu8 SPSState; // 0 = off, 1 = running, 2 = shutting down, 3 = suspended during sleep - mDNSInterfaceID SPSProxyListChanged; - UDPSocket *SPSSocket; - ServiceRecordSet SPSRecords; - mDNSQuestionCallback *SPSBrowseCallback; // So the platform layer can do something useful with SPS browse results - int ProxyRecords; // Total number of records we're holding as proxy - #define MAX_PROXY_RECORDS 10000 /* DOS protection: 400 machines at 25 records each */ + char MsgBuffer[MaxMsg]; // Temp storage used while building error log messages + + // Task Scheduling variables + mDNSs32 timenow_adjust; // Correction applied if we ever discover time went backwards + mDNSs32 timenow; // The time that this particular activation of the mDNS code started + mDNSs32 timenow_last; // The time the last time we ran + mDNSs32 NextScheduledEvent; // Derived from values below + mDNSs32 ShutdownTime; // Set when we're shutting down; allows us to skip some unnecessary steps + mDNSs32 SuppressSending; // Don't send local-link mDNS packets during this time + mDNSs32 NextCacheCheck; // Next time to refresh cache record before it expires + mDNSs32 NextScheduledQuery; // Next time to send query in its exponential backoff sequence + mDNSs32 NextScheduledProbe; // Next time to probe for new authoritative record + mDNSs32 NextScheduledResponse; // Next time to send authoritative record(s) in responses + mDNSs32 NextScheduledNATOp; // Next time to send NAT-traversal packets + mDNSs32 NextScheduledSPS; // Next time to purge expiring Sleep Proxy records + mDNSs32 NextScheduledKA; // Next time to send Keepalive packets (SPS) + mDNSs32 RandomQueryDelay; // For de-synchronization of query packets on the wire + mDNSu32 RandomReconfirmDelay; // For de-synchronization of reconfirmation queries on the wire + mDNSs32 PktNum; // Unique sequence number assigned to each received packet + mDNSu8 LocalRemoveEvents; // Set if we may need to deliver remove events for local-only questions and/or local-only records + mDNSu8 SleepState; // Set if we're sleeping + mDNSu8 SleepSeqNum; // "Epoch number" of our current period of wakefulness + mDNSu8 SystemWakeOnLANEnabled; // Set if we want to register with a Sleep Proxy before going to sleep + mDNSu8 SentSleepProxyRegistration; // Set if we registered (or tried to register) with a Sleep Proxy + mDNSu8 SystemSleepOnlyIfWakeOnLAN; // Set if we may only sleep if we managed to register with a Sleep Proxy + mDNSs32 AnnounceOwner; // After waking from sleep, include OWNER option in packets until this time + mDNSs32 clearIgnoreNA; // After waking from sleep, clear the ignore neighbor advertisement after this time + mDNSs32 DelaySleep; // To inhibit re-sleeping too quickly right after wake + mDNSs32 SleepLimit; // Time window to allow deregistrations, etc., + // during which underying platform layer should inhibit system sleep + mDNSs32 NextScheduledSPRetry; // Time next sleep proxy registration action is required. + // Only valid if SleepLimit is nonzero and DelaySleep is zero. + + mDNSs32 NextScheduledStopTime; // Next time to stop a question + + // These fields only required for mDNS Searcher... + DNSQuestion *Questions; // List of all registered questions, active and inactive + DNSQuestion *NewQuestions; // Fresh questions not yet answered from cache + DNSQuestion *CurrentQuestion; // Next question about to be examined in AnswerLocalQuestions() + DNSQuestion *LocalOnlyQuestions; // Questions with InterfaceID set to mDNSInterface_LocalOnly or mDNSInterface_P2P + DNSQuestion *NewLocalOnlyQuestions; // Fresh local-only or P2P questions not yet answered + DNSQuestion *RestartQuestion; // Questions that are being restarted (stop followed by start) + DNSQuestion *ValidationQuestion; // Questions that are being validated (dnssec) + mDNSu32 rrcache_size; // Total number of available cache entries + mDNSu32 rrcache_totalused; // Number of cache entries currently occupied + mDNSu32 rrcache_active; // Number of cache entries currently occupied by records that answer active questions + mDNSu32 rrcache_report; + CacheEntity *rrcache_free; + CacheGroup *rrcache_hash[CACHE_HASH_SLOTS]; + mDNSs32 rrcache_nextcheck[CACHE_HASH_SLOTS]; + + AuthHash rrauth; + + // 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 MulticastHostname; // Fully Qualified "dot-local" Host Name, e.g. "Foo.local." + UTF8str255 HIHardware; + UTF8str255 HISoftware; + AuthRecord DeviceInfo; + AuthRecord *ResourceRecords; + AuthRecord *DuplicateRecords; // Records currently 'on hold' because they are duplicates of existing records + AuthRecord *NewLocalRecords; // Fresh AuthRecords (public) not yet delivered to our local-only questions + AuthRecord *CurrentRecord; // Next AuthRecord about to be examined + mDNSBool NewLocalOnlyRecords; // Fresh AuthRecords (local only) not yet delivered to our local questions + NetworkInterfaceInfo *HostInterfaces; + mDNSs32 ProbeFailTime; + mDNSu32 NumFailedProbes; + mDNSs32 SuppressProbes; + + // Unicast-specific data + mDNSs32 NextuDNSEvent; // uDNS next event + mDNSs32 NextSRVUpdate; // Time to perform delayed update + + DNSServer *DNSServers; // list of DNS servers + McastResolver *McastResolvers; // list of Mcast Resolvers + + mDNSAddr Router; + mDNSAddr AdvertisedV4; // IPv4 address pointed to by hostname + mDNSAddr AdvertisedV6; // IPv6 address pointed to by hostname + + DomainAuthInfo *AuthInfoList; // list of domains requiring authentication for updates + + DNSQuestion ReverseMap; // Reverse-map query to find static hostname for service target + DNSQuestion AutomaticBrowseDomainQ; + domainname StaticHostname; // Current answer to reverse-map query + domainname FQDN; + HostnameInfo *Hostnames; // List of registered hostnames + hostname metadata + NATTraversalInfo AutoTunnelNAT; // Shared between all AutoTunnel DomainAuthInfo structs + mDNSv6Addr AutoTunnelRelayAddr; + + mDNSBool StartWABQueries; // Start WAB queries for the purpose of domain enumeration + mDNSu8 SearchDomainsHash[MD5_LEN]; + + // NAT-Traversal fields + NATTraversalInfo LLQNAT; // Single shared NAT Traversal to receive inbound LLQ notifications + NATTraversalInfo *NATTraversals; + NATTraversalInfo *CurrentNATTraversal; + mDNSs32 retryIntervalGetAddr; // delta between time sent and retry + mDNSs32 retryGetAddr; // absolute time when we retry + mDNSv4Addr ExternalAddress; + + UDPSocket *NATMcastRecvskt; // For receiving NAT-PMP AddrReply multicasts from router on port 5350 + mDNSu32 LastNATupseconds; // NAT engine uptime in seconds, from most recent NAT packet + mDNSs32 LastNATReplyLocalTime; // Local time in ticks when most recent NAT packet was received + mDNSu16 LastNATMapResultCode; // Most recent error code for mappings + + tcpLNTInfo tcpAddrInfo; // legacy NAT traversal TCP connection info for external address + tcpLNTInfo tcpDeviceInfo; // legacy NAT traversal TCP connection info for device info + tcpLNTInfo *tcpInfoUnmapList; // list of pending unmap requests + mDNSInterfaceID UPnPInterfaceID; + UDPSocket *SSDPSocket; // For SSDP request/response + mDNSBool SSDPWANPPPConnection; // whether we should send the SSDP query for WANIPConnection or WANPPPConnection + mDNSIPPort UPnPRouterPort; // port we send discovery messages to + mDNSIPPort UPnPSOAPPort; // port we send SOAP messages to + mDNSu8 *UPnPRouterURL; // router's URL string + mDNSBool UPnPWANPPPConnection; // whether we're using WANIPConnection or WANPPPConnection + mDNSu8 *UPnPSOAPURL; // router's SOAP control URL string + mDNSu8 *UPnPRouterAddressString; // holds both the router's address and port + mDNSu8 *UPnPSOAPAddressString; // holds both address and port for SOAP messages + + // Sleep Proxy Server fields + mDNSu8 SPSType; // 0 = off, 10-99 encodes desirability metric + mDNSu8 SPSPortability; // 10-99 + mDNSu8 SPSMarginalPower; // 10-99 + mDNSu8 SPSTotalPower; // 10-99 + mDNSu8 SPSFeatureFlags; // Features supported. Currently 1 = TCP KeepAlive supported. + mDNSu8 SPSState; // 0 = off, 1 = running, 2 = shutting down, 3 = suspended during sleep + mDNSInterfaceID SPSProxyListChanged; + UDPSocket *SPSSocket; + ServiceRecordSet SPSRecords; + mDNSQuestionCallback *SPSBrowseCallback; // So the platform layer can do something useful with SPS browse results + int ProxyRecords; // Total number of records we're holding as proxy + #define MAX_PROXY_RECORDS 10000 /* DOS protection: 400 machines at 25 records each */ #if APPLE_OSX_mDNSResponder - ClientTunnel *TunnelClients; - uuid_t asl_uuid; // uuid for ASL logging - void *WCF; + ClientTunnel *TunnelClients; + uuid_t asl_uuid; // uuid for ASL logging + void *WCF; #endif + TrustAnchor *TrustAnchors; + int notifyToken; + mDNSBool mDNSHandlePeerEvents; // Handle AWDL Peer Events - // Fixed storage, to avoid creating large objects on the stack - // The imsg is declared as a union with a pointer type to enforce CPU-appropriate alignment - union { DNSMessage m; void *p; } imsg; // Incoming message received from wire - DNSMessage omsg; // Outgoing message we're building - LargeCacheRecord rec; // Resource Record extracted from received message - }; + // Fixed storage, to avoid creating large objects on the stack + // The imsg is declared as a union with a pointer type to enforce CPU-appropriate alignment + union { DNSMessage m; void *p; } imsg; // Incoming message received from wire + DNSMessage omsg; // Outgoing message we're building + LargeCacheRecord rec; // Resource Record extracted from received message +}; #define FORALL_CACHERECORDS(SLOT,CG,CR) \ - for ((SLOT) = 0; (SLOT) < CACHE_HASH_SLOTS; (SLOT)++) \ - for ((CG)=m->rrcache_hash[(SLOT)]; (CG); (CG)=(CG)->next) \ - for ((CR) = (CG)->members; (CR); (CR)=(CR)->next) + for ((SLOT) = 0; (SLOT) < CACHE_HASH_SLOTS; (SLOT)++) \ + for ((CG)=m->rrcache_hash[(SLOT)]; (CG); (CG)=(CG)->next) \ + for ((CR) = (CG)->members; (CR); (CR)=(CR)->next) // *************************************************************************** #if 0 @@ -1966,48 +2094,49 @@ struct mDNS_struct #pragma mark - Useful Static Constants #endif -extern const mDNSInterfaceID mDNSInterface_Any; // Zero -extern const mDNSInterfaceID mDNSInterface_LocalOnly; // Special value -extern const mDNSInterfaceID mDNSInterface_Unicast; // Special value -extern const mDNSInterfaceID mDNSInterfaceMark; // Special value -extern const mDNSInterfaceID mDNSInterface_P2P; // Special value - -extern const mDNSIPPort DiscardPort; -extern const mDNSIPPort SSHPort; -extern const mDNSIPPort UnicastDNSPort; -extern const mDNSIPPort SSDPPort; -extern const mDNSIPPort IPSECPort; -extern const mDNSIPPort NSIPCPort; -extern const mDNSIPPort NATPMPAnnouncementPort; -extern const mDNSIPPort NATPMPPort; -extern const mDNSIPPort DNSEXTPort; -extern const mDNSIPPort MulticastDNSPort; -extern const mDNSIPPort LoopbackIPCPort; -extern const mDNSIPPort PrivateDNSPort; - -extern const OwnerOptData zeroOwner; - -extern const mDNSIPPort zeroIPPort; -extern const mDNSv4Addr zerov4Addr; -extern const mDNSv6Addr zerov6Addr; -extern const mDNSEthAddr zeroEthAddr; -extern const mDNSv4Addr onesIPv4Addr; -extern const mDNSv6Addr onesIPv6Addr; -extern const mDNSEthAddr onesEthAddr; -extern const mDNSAddr zeroAddr; - -extern const mDNSv4Addr AllDNSAdminGroup; -extern const mDNSv4Addr AllHosts_v4; -extern const mDNSv6Addr AllHosts_v6; -extern const mDNSv6Addr NDP_prefix; -extern const mDNSEthAddr AllHosts_v6_Eth; -extern const mDNSAddr AllDNSLinkGroup_v4; -extern const mDNSAddr AllDNSLinkGroup_v6; +extern const mDNSInterfaceID mDNSInterface_Any; // Zero +extern const mDNSInterfaceID mDNSInterface_LocalOnly; // Special value +extern const mDNSInterfaceID mDNSInterface_Unicast; // Special value +extern const mDNSInterfaceID mDNSInterfaceMark; // Special value +extern const mDNSInterfaceID mDNSInterface_P2P; // Special value + +extern const mDNSIPPort DiscardPort; +extern const mDNSIPPort SSHPort; +extern const mDNSIPPort UnicastDNSPort; +extern const mDNSIPPort SSDPPort; +extern const mDNSIPPort IPSECPort; +extern const mDNSIPPort NSIPCPort; +extern const mDNSIPPort NATPMPAnnouncementPort; +extern const mDNSIPPort NATPMPPort; +extern const mDNSIPPort DNSEXTPort; +extern const mDNSIPPort MulticastDNSPort; +extern const mDNSIPPort LoopbackIPCPort; +extern const mDNSIPPort PrivateDNSPort; + +extern const OwnerOptData zeroOwner; + +extern const mDNSIPPort zeroIPPort; +extern const mDNSv4Addr zerov4Addr; +extern const mDNSv6Addr zerov6Addr; +extern const mDNSEthAddr zeroEthAddr; +extern const mDNSv4Addr onesIPv4Addr; +extern const mDNSv6Addr onesIPv6Addr; +extern const mDNSEthAddr onesEthAddr; +extern const mDNSAddr zeroAddr; + +extern const mDNSv4Addr AllDNSAdminGroup; +extern const mDNSv4Addr AllHosts_v4; +extern const mDNSv6Addr AllHosts_v6; +extern const mDNSv6Addr NDP_prefix; +extern const mDNSEthAddr AllHosts_v6_Eth; +extern const mDNSAddr AllDNSLinkGroup_v4; +extern const mDNSAddr AllDNSLinkGroup_v6; extern const mDNSOpaque16 zeroID; extern const mDNSOpaque16 onesID; extern const mDNSOpaque16 QueryFlags; extern const mDNSOpaque16 uQueryFlags; +extern const mDNSOpaque16 DNSSecQFlags; extern const mDNSOpaque16 ResponseFlags; extern const mDNSOpaque16 UpdateReqFlags; extern const mDNSOpaque16 UpdateRespFlags; @@ -2028,9 +2157,9 @@ extern mDNSu8 NumUnicastDNSServers; #endif #if (defined(_MSC_VER)) - #define mDNSinline static __inline + #define mDNSinline static __inline #elif ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 9))) - #define mDNSinline static inline + #define mDNSinline static inline #endif // If we're not doing inline functions, then this header needs to have the extern declarations @@ -2048,17 +2177,17 @@ extern mDNSOpaque16 mDNSOpaque16fromIntVal(mDNSu16 v); #ifdef mDNSinline -mDNSinline mDNSs32 NonZeroTime(mDNSs32 t) { if (t) return(t); else return(1); } +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 mDNSOpaque16 mDNSOpaque16fromIntVal(mDNSu16 v) - { - mDNSOpaque16 x; - x.b[0] = (mDNSu8)(v >> 8); - x.b[1] = (mDNSu8)(v & 0xFF); - return(x); - } +{ + mDNSOpaque16 x; + x.b[0] = (mDNSu8)(v >> 8); + x.b[1] = (mDNSu8)(v & 0xFF); + return(x); +} #endif @@ -2119,9 +2248,9 @@ mDNSinline mDNSOpaque16 mDNSOpaque16fromIntVal(mDNSu16 v) // code is not entered by an interrupt-time timer callback while in the middle of processing a client call. extern mStatus mDNS_Init (mDNS *const m, mDNS_PlatformSupport *const p, - CacheEntity *rrcachestorage, mDNSu32 rrcachesize, - mDNSBool AdvertiseLocalAddresses, - mDNSCallback *Callback, void *Context); + CacheEntity *rrcachestorage, mDNSu32 rrcachesize, + mDNSBool AdvertiseLocalAddresses, + mDNSCallback *Callback, void *Context); // See notes above on use of NoCache/ZeroCacheSize #define mDNS_Init_NoCache mDNSNULL #define mDNS_Init_ZeroCacheSize 0 @@ -2143,7 +2272,7 @@ extern mDNSs32 mDNS_Execute (mDNS *const m); extern mStatus mDNS_Register (mDNS *const m, AuthRecord *const rr); extern mStatus mDNS_Update (mDNS *const m, AuthRecord *const rr, mDNSu32 newttl, - const mDNSu16 newrdlength, 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); @@ -2168,7 +2297,7 @@ extern void mDNS_UpdateAllowSleep(mDNS *const m); #pragma mark - Platform support functions that are accessible to the client layer too #endif -extern mDNSs32 mDNSPlatformOneSecond; +extern mDNSs32 mDNSPlatformOneSecond; // *************************************************************************** #if 0 @@ -2205,58 +2334,63 @@ typedef enum { mDNS_Dereg_normal, mDNS_Dereg_rapid, mDNS_Dereg_conflict, mDNS_De // and the default domain in which to register in the case where the user has made no selection. extern void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID, - mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, AuthRecType artype, mDNSRecordCallback Callback, void *Context); + mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, AuthRecType artype, mDNSRecordCallback Callback, void *Context); -// mDNS_RegisterService() flags parameter bit definitions +// mDNS_RegisterService() flags parameter bit definitions. +// Note these are only defined to transfer the corresponding DNSServiceFlags settings into mDNSCore routines, +// since code in mDNSCore does not include the DNSServiceFlags definitions in dns_sd.h. enum - { - regFlagIncludeP2P = 0x1, // include P2P interfaces when using mDNSInterface_Any - regFlagKnownUnique = 0x2 // client guarantees that SRV and TXT record names are unique - }; +{ + coreFlagIncludeP2P = 0x1, // include P2P interfaces when using mDNSInterface_Any + coreFlagIncludeAWDL = 0x2, // include AWDL interface when using mDNSInterface_Any + coreFlagKnownUnique = 0x4 // client guarantees that SRV and TXT record names are unique +}; extern mStatus mDNS_RegisterService (mDNS *const m, ServiceRecordSet *sr, - const domainlabel *const name, const domainname *const type, const domainname *const domain, - const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen, - AuthRecord *SubTypes, mDNSu32 NumSubTypes, - mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context, mDNSu32 flags); -extern mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl, mDNSu32 includeP2P); + const domainlabel *const name, const domainname *const type, const domainname *const domain, + const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen, + AuthRecord *SubTypes, mDNSu32 NumSubTypes, + mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context, mDNSu32 flags); +extern mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl, mDNSu32 flags); 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_drt(mDNS *const m, ServiceRecordSet *sr, mDNS_Dereg_type drt); #define mDNS_DeregisterService(M,S) mDNS_DeregisterService_drt((M), (S), mDNS_Dereg_normal) extern mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const rr, - const domainlabel *const name, const domainname *const type, const domainname *const domain, - const domainname *const host, - const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context, mDNSBool includeP2P); + const domainlabel *const name, const domainname *const type, const domainname *const domain, + const domainname *const host, + const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context, mDNSu32 flags); #define mDNS_DeregisterNoSuchService mDNS_Deregister extern void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID InterfaceID, const domainname *const name, - const mDNSu16 qtype, mDNSQuestionCallback *const callback, void *const context); + const mDNSu16 qtype, mDNSQuestionCallback *const callback, void *const context); extern mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, - const domainname *const srv, const domainname *const domain, - const mDNSInterfaceID InterfaceID, mDNSBool ForceMCast, mDNSQuestionCallback *Callback, void *Context); + const domainname *const srv, const domainname *const domain, + const mDNSInterfaceID InterfaceID, mDNSu32 flags, + mDNSBool ForceMCast, mDNSBool useBackgroundTrafficClass, + mDNSQuestionCallback *Callback, void *Context); #define mDNS_StopBrowse mDNS_StopQuery extern mStatus mDNS_StartResolveService(mDNS *const m, ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context); extern void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *query); typedef enum - { - mDNS_DomainTypeBrowse = 0, - mDNS_DomainTypeBrowseDefault = 1, - mDNS_DomainTypeBrowseAutomatic = 2, - mDNS_DomainTypeRegistration = 3, - mDNS_DomainTypeRegistrationDefault = 4, +{ + mDNS_DomainTypeBrowse = 0, + mDNS_DomainTypeBrowseDefault = 1, + mDNS_DomainTypeBrowseAutomatic = 2, + mDNS_DomainTypeRegistration = 3, + mDNS_DomainTypeRegistrationDefault = 4, - mDNS_DomainTypeMax = 4 - } mDNS_DomainType; + mDNS_DomainTypeMax = 4 +} mDNS_DomainType; extern const char *const mDNS_DomainTypeNames[]; extern mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom, - const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context); + const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context); #define mDNS_StopGetDomains mDNS_StopQuery extern mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname); #define mDNS_StopAdvertiseDomains mDNS_Deregister @@ -2284,17 +2418,17 @@ extern mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question); // because that object is defined to be 256 bytes long, but not all domainname objects are truly the full size. // This macro uses mDNSPlatformMemCopy() to make sure it only touches the actual bytes that are valid. #define AssignDomainName(DST, SRC) do { mDNSu16 len__ = DomainNameLength((SRC)); \ - if (len__ <= MAX_DOMAIN_NAME) mDNSPlatformMemCopy((DST)->c, (SRC)->c, len__); else (DST)->c[0] = 0; } while(0) + if (len__ <= MAX_DOMAIN_NAME) mDNSPlatformMemCopy((DST)->c, (SRC)->c, len__);else (DST)->c[0] = 0;} while(0) // Comparison functions #define SameDomainLabelCS(A,B) ((A)[0] == (B)[0] && mDNSPlatformMemSame((A)+1, (B)+1, (A)[0])) extern mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b); extern mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2); extern mDNSBool SameDomainNameCS(const domainname *const d1, const domainname *const d2); -typedef mDNSBool DomainNameComparisonFn(const domainname *const d1, const domainname *const d2); +typedef mDNSBool DomainNameComparisonFn (const domainname *const d1, const domainname *const d2); extern mDNSBool IsLocalDomain(const domainname *d); // returns true for domains that by default should be looked up using link-local multicast -#define StripFirstLabel(X) ((const domainname *)&(X)->c[(X)->c[0] ? 1 + (X)->c[0] : 0]) +#define StripFirstLabel(X) ((const domainname *)& (X)->c[(X)->c[0] ? 1 + (X)->c[0] : 0]) #define FirstLabel(X) ((const domainlabel *)(X)) #define SecondLabel(X) ((const domainlabel *)StripFirstLabel(X)) @@ -2386,6 +2520,7 @@ extern mDNSBool mDNSv4AddrIsRFC1918(mDNSv4Addr *addr); // returns true for RFC1 #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 mDNSSameIPv6NetworkPart(A,B) ((A).l[0] == (B).l[0] && (A).l[1] == (B).l[1]) #define mDNSSameEthAddress(A,B) ((A)->w[0] == (B)->w[0] && (A)->w[1] == (B)->w[1] && (A)->w[2] == (B)->w[2]) #define mDNSIPPortIsZero(A) ((A).NotAnInteger == 0) @@ -2399,35 +2534,39 @@ extern mDNSBool mDNSv4AddrIsRFC1918(mDNSv4Addr *addr); // returns true for RFC1 #define mDNSIPv6AddressIsOnes(A) (((A).l[0] & (A).l[1] & (A).l[2] & (A).l[3]) == 0xFFFFFFFF) #define mDNSAddressIsAllDNSLinkGroup(X) ( \ - ((X)->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address((X)->ip.v4, AllDNSLinkGroup_v4.ip.v4)) || \ - ((X)->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address((X)->ip.v6, AllDNSLinkGroup_v6.ip.v6)) ) + ((X)->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address((X)->ip.v4, AllDNSLinkGroup_v4.ip.v4)) || \ + ((X)->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address((X)->ip.v6, AllDNSLinkGroup_v6.ip.v6)) ) #define mDNSAddressIsZero(X) ( \ - ((X)->type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsZero((X)->ip.v4)) || \ - ((X)->type == mDNSAddrType_IPv6 && mDNSIPv6AddressIsZero((X)->ip.v6)) ) + ((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)) ) + ((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)) ) + ((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) + ((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) #define mDNSv4AddressIsLinkLocal(X) ((X)->b[0] == 169 && (X)->b[1] == 254) #define mDNSv6AddressIsLinkLocal(X) ((X)->b[0] == 0xFE && ((X)->b[1] & 0xC0) == 0x80) #define mDNSAddressIsLinkLocal(X) ( \ - ((X)->type == mDNSAddrType_IPv4) ? mDNSv4AddressIsLinkLocal(&(X)->ip.v4) : \ - ((X)->type == mDNSAddrType_IPv6) ? mDNSv6AddressIsLinkLocal(&(X)->ip.v6) : mDNSfalse) + ((X)->type == mDNSAddrType_IPv4) ? mDNSv4AddressIsLinkLocal(&(X)->ip.v4) : \ + ((X)->type == mDNSAddrType_IPv6) ? mDNSv6AddressIsLinkLocal(&(X)->ip.v6) : mDNSfalse) #define mDNSv4AddressIsLoopback(X) ((X)->b[0] == 127 && (X)->b[1] == 0 && (X)->b[2] == 0 && (X)->b[3] == 1) #define mDNSv6AddressIsLoopback(X) ((((X)->l[0] | (X)->l[1] | (X)->l[2]) == 0) && ((X)->b[12] == 0 && (X)->b[13] == 0 && (X)->b[14] == 0 && (X)->b[15] == 1)) +#define mDNSAddressIsLoopback(X) ( \ + ((X)->type == mDNSAddrType_IPv4) ? mDNSv4AddressIsLoopback(&(X)->ip.v4) : \ + ((X)->type == mDNSAddrType_IPv6) ? mDNSv6AddressIsLoopback(&(X)->ip.v6) : mDNSfalse) + // *************************************************************************** #if 0 #pragma mark - @@ -2445,7 +2584,7 @@ extern mDNSBool mDNSv4AddrIsRFC1918(mDNSv4Addr *addr); // returns true for RFC1 // and the value is prepended to the IPSec identifier (used for key lookup) extern mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, - const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, const char *autoTunnelPrefix); + const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, mDNSBool autoTunnel); extern void RecreateNATMappings(mDNS *const m); @@ -2469,7 +2608,7 @@ extern void RecreateNATMappings(mDNS *const m); 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 *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router); -extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout, mDNSBool cellIntf); +extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID); extern void PenalizeDNSServer(mDNS *const m, DNSQuestion *q); extern void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID); @@ -2477,7 +2616,7 @@ extern McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, // We use ((void *)0) here instead of mDNSNULL to avoid compile warnings on gcc 4.2 #define mDNS_AddSearchDomain_CString(X, I) \ - do { domainname d__; if (((X) != (void*)0) && MakeDomainNameFromDNSNameString(&d__, (X)) && d__.c[0]) mDNS_AddSearchDomain(&d__, I); } while(0) + do { domainname d__; if (((X) != (void*)0) && MakeDomainNameFromDNSNameString(&d__, (X)) && d__.c[0]) mDNS_AddSearchDomain(&d__, I);} while(0) // Routines called by the core, exported by DNSDigest.c @@ -2495,10 +2634,10 @@ extern void DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, DomainAuthInfo (M)->h.numAnswers = (mDNSu16)((mDNSu8 *)&(M)->h.numAnswers )[0] << 8 | ((mDNSu8 *)&(M)->h.numAnswers )[1]; \ (M)->h.numAuthorities = (mDNSu16)((mDNSu8 *)&(M)->h.numAuthorities)[0] << 8 | ((mDNSu8 *)&(M)->h.numAuthorities)[1]; \ (M)->h.numAdditionals = (mDNSu16)((mDNSu8 *)&(M)->h.numAdditionals)[0] << 8 | ((mDNSu8 *)&(M)->h.numAdditionals)[1]; \ - } while (0) +} while (0) #define DNSDigest_SignMessageHostByteOrder(M,E,INFO) \ - do { SwapDNSHeaderBytes(M); DNSDigest_SignMessage((M), (E), (INFO), 0); SwapDNSHeaderBytes(M); } while (0) + do { SwapDNSHeaderBytes(M); DNSDigest_SignMessage((M), (E), (INFO), 0); SwapDNSHeaderBytes(M); } while (0) // verify a DNS message. The message must be complete, with all values in network byte order. end points to the // end of the record. tsig is a pointer to the resource record that contains the TSIG OPT record. info is @@ -2544,7 +2683,8 @@ extern mDNSBool DNSDigest_VerifyMessage(DNSMessage *msg, mDNSu8 *end, LargeCache extern mStatus mDNSPlatformInit (mDNS *const m); extern void mDNSPlatformClose (mDNS *const m); extern mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end, -mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, mDNSIPPort dstport); + mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, + mDNSIPPort dstport, mDNSBool useBackgroundTrafficClass); extern void mDNSPlatformLock (const mDNS *const m); extern void mDNSPlatformUnlock (const mDNS *const m); @@ -2553,9 +2693,11 @@ extern void mDNSPlatformStrCopy ( void *dst, const void *src); extern mDNSu32 mDNSPlatformStrLen ( const void *src); extern void mDNSPlatformMemCopy ( void *dst, const void *src, mDNSu32 len); extern mDNSBool mDNSPlatformMemSame (const void *dst, const void *src, mDNSu32 len); +extern int mDNSPlatformMemCmp (const void *dst, const void *src, mDNSu32 len); extern void mDNSPlatformMemZero ( void *dst, mDNSu32 len); +extern void mDNSPlatformQsort (void *base, int nel, int width, int (*compar)(const void *, const void *)); #if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING -#define mDNSPlatformMemAllocate(X) mallocL(#X, X) +#define mDNSPlatformMemAllocate(X) mallocL(# X, X) #else extern void * mDNSPlatformMemAllocate (mDNSu32 len); #endif @@ -2579,13 +2721,16 @@ extern mDNSs32 mDNSPlatformUTC (void); #define mDNS_TimeNow_NoLock(m) (mDNSPlatformRawTime() + (m)->timenow_adjust) #if MDNS_DEBUGMSGS -extern void mDNSPlatformWriteDebugMsg(const char *msg); +extern void mDNSPlatformWriteDebugMsg(const char *msg); #endif -extern void mDNSPlatformWriteLogMsg(const char *ident, const char *msg, mDNSLogLevel_t loglevel); +extern void mDNSPlatformWriteLogMsg(const char *ident, const char *msg, mDNSLogLevel_t loglevel); #if APPLE_OSX_mDNSResponder // Utility function for ASL logging mDNSexport void mDNSASLLog(uuid_t *uuid, const char *subdomain, const char *result, const char *signature, const char *fmt, ...); + +// Function to toggle IPv6 advertisements +mDNSexport void mDNSPlatformToggleInterfaceAdvt(mDNS *const m, mDNSBool stopAdvt); #endif // Platform support modules should provide the following functions to map between opaque interface IDs @@ -2612,17 +2757,17 @@ extern mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInte // event loop. CloseConnectin may be called at any time, including in a ConnectionCallback. typedef enum - { - kTCPSocketFlags_Zero = 0, - kTCPSocketFlags_UseTLS = (1 << 0) - } TCPSocketFlags; +{ + kTCPSocketFlags_Zero = 0, + kTCPSocketFlags_UseTLS = (1 << 0) +} TCPSocketFlags; typedef void (*TCPConnectionCallback)(TCPSocket *sock, void *context, mDNSBool ConnectionEstablished, mStatus err); -extern TCPSocket *mDNSPlatformTCPSocket(mDNS *const m, TCPSocketFlags flags, mDNSIPPort *port); // creates a TCP socket +extern TCPSocket *mDNSPlatformTCPSocket(mDNS *const m, TCPSocketFlags flags, mDNSIPPort *port, mDNSBool useBackgroundTrafficClass); // creates a TCP socket extern TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int sd); extern int mDNSPlatformTCPGetFD(TCPSocket *sock); extern mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, domainname *hostname, - mDNSInterfaceID InterfaceID, TCPConnectionCallback callback, void *context); + mDNSInterfaceID InterfaceID, TCPConnectionCallback callback, void *context); extern void mDNSPlatformTCPCloseConnection(TCPSocket *sock); extern long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool *closed); extern long mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len); @@ -2633,6 +2778,8 @@ extern void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterface extern void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID); extern void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID); extern void mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst); +extern void mDNSPlatformSendKeepalive(mDNSAddr *sadd, mDNSAddr *dadd, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu32 seq, mDNSu32 ack, mDNSu16 win); +extern mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, mDNSIPPort *lport, mDNSAddr *raddr, mDNSIPPort *rport, mDNSTCPInfo *mti); // mDNSPlatformTLSSetupCerts/mDNSPlatformTLSTearDownCerts used by dnsextd extern mStatus mDNSPlatformTLSSetupCerts(void); @@ -2647,7 +2794,14 @@ extern void mDNSPlatformDynDNSHostNameStatusChanged(const domainname *cons extern void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason); extern void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration); + +extern mDNSBool mDNSPlatformInterfaceIsD2D(mDNSInterfaceID InterfaceID); +extern mDNSBool mDNSPlatformInterfaceIsAWDL(const NetworkInterfaceInfo *intf); +extern mDNSBool mDNSPlatformValidRecordForQuestion(const ResourceRecord *const rr, const DNSQuestion *const q); extern mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf); +extern mDNSBool mDNSPlatformValidQuestionForInterface(DNSQuestion *q, const NetworkInterfaceInfo *intf); + +extern void mDNSPlatformFormatTime(unsigned long t, mDNSu8 *buf, int bufsize); #ifdef _LEGACY_NAT_TRAVERSAL_ // Support for legacy NAT traversal protocols, implemented by the platform layer and callable by the core. @@ -2701,13 +2855,15 @@ extern mStatus mDNS_RegisterInterface (mDNS *const m, NetworkInterfaceInfo *se extern void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping); extern void mDNSCoreInitComplete(mDNS *const m, mStatus result); extern void mDNSCoreReceive(mDNS *const m, void *const msg, const mDNSu8 *const end, - const mDNSAddr *const srcaddr, const mDNSIPPort srcport, - const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID); -extern void mDNSCoreRestartQueries(mDNS *const m); -typedef void (*FlushCache)(mDNS *const m); -typedef void (*CallbackBeforeStartQuery)(mDNS *const m, void *context); -extern void mDNSCoreRestartAddressQueries(mDNS *const m, mDNSBool SearchDomainsChanged, FlushCache flushCacheRecords, - CallbackBeforeStartQuery beforeQueryStart, void *context); + const mDNSAddr *const srcaddr, const mDNSIPPort srcport, + const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID); +extern void mDNSCoreRestartQueries(mDNS *const m); +extern void mDNSCoreRestartQuestion(mDNS *const m, DNSQuestion *q); +extern void mDNSCoreRestartRegistration(mDNS *const m, AuthRecord *rr, int announceCount); +typedef void (*FlushCache)(mDNS *const m); +typedef void (*CallbackBeforeStartQuery)(mDNS *const m, void *context); +extern void mDNSCoreRestartAddressQueries(mDNS *const m, mDNSBool SearchDomainsChanged, FlushCache flushCacheRecords, + CallbackBeforeStartQuery beforeQueryStart, void *context); extern mDNSBool mDNSCoreHaveAdvertisedMulticastServices(mDNS *const m); extern void mDNSCoreMachineSleep(mDNS *const m, mDNSBool wake); extern mDNSBool mDNSCoreReadyForSleep(mDNS *m, mDNSs32 now); @@ -2717,14 +2873,18 @@ extern void mDNSCoreReceiveRawPacket (mDNS *const m, const mDNSu8 *const p, extern mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip); -extern CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, CacheGroup *cg, mDNSs32 delay); +extern CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, CacheGroup *cg, mDNSs32 delay, mDNSBool Add, const mDNSAddr *sourceAddress); +extern CacheGroup *CacheGroupForName(const mDNS *const m, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name); +extern void ReleaseCacheRecord(mDNS *const m, CacheRecord *r); extern void ScheduleNextCacheCheckTime(mDNS *const m, const mDNSu32 slot, const mDNSs32 event); +extern void SetNextCacheCheckTimeForRecord(mDNS *const m, CacheRecord *const rr); extern void GrantCacheExtensions(mDNS *const m, DNSQuestion *q, mDNSu32 lease); extern void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr, - const domainname *const name, const mDNSu32 namehash, const mDNSu16 rrtype, const mDNSu16 rrclass, mDNSu32 ttl_seconds, - mDNSInterfaceID InterfaceID, DNSServer *dnsserver); + const domainname *const name, const mDNSu32 namehash, const mDNSu16 rrtype, const mDNSu16 rrclass, mDNSu32 ttl_seconds, + mDNSInterfaceID InterfaceID, DNSServer *dnsserver); extern void CompleteDeregistration(mDNS *const m, AuthRecord *rr); extern void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheRecord *const rr, const QC_result AddRecord); +extern void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, ResourceRecord *rr); extern char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID InterfaceID); extern void DNSServerChangeForQuestion(mDNS *const m, DNSQuestion *q, DNSServer *newServer); extern void ActivateUnicastRegistration(mDNS *const m, AuthRecord *const rr); @@ -2739,19 +2899,25 @@ extern AuthGroup *AuthGroupForName(AuthHash *r, const mDNSu32 slot, const mDNSu3 extern AuthGroup *AuthGroupForRecord(AuthHash *r, const mDNSu32 slot, const ResourceRecord *const rr); extern AuthGroup *InsertAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr); extern AuthGroup *RemoveAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr); +extern mDNSBool mDNS_CheckForCacheRecord(mDNS *const m, DNSQuestion *q, mDNSu16 qtype); // For now this AutoTunnel stuff is specific to Mac OS X. // In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer #if APPLE_OSX_mDNSResponder extern void AutoTunnelCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord); extern void AddNewClientTunnel(mDNS *const m, DNSQuestion *const q); -extern void SetupLocalAutoTunnelInterface_internal(mDNS *const m, mDNSBool servicesStarting); +extern void StartServerTunnel(mDNS *const m, DomainAuthInfo *const info); extern void UpdateAutoTunnelDomainStatuses(const mDNS *const m); -extern mStatus ActivateLocalProxy(mDNS *const m, char *ifname); extern void RemoveAutoTunnel6Record(mDNS *const m); extern mDNSBool RecordReadyForSleep(mDNS *const m, AuthRecord *rr); #endif +// For now this LocalSleepProxy stuff is specific to Mac OS X. +// In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer +#if APPLE_OSX_mDNSResponder +extern mStatus ActivateLocalProxy(mDNS *const m, char *ifname); +#endif + // *************************************************************************** #if 0 #pragma mark - @@ -2765,7 +2931,7 @@ extern mDNSBool RecordReadyForSleep(mDNS *const m, AuthRecord *rr); // Sleep Proxy Server(s) to use when multiple are available on the network. Each metric // is a two-digit decimal number in the range 10-99. Lower metrics are generally better. // -// AA-BB-CC-DD Name +// AA-BB-CC-DD.FF Name // // Metrics: // @@ -2773,6 +2939,7 @@ extern mDNSBool RecordReadyForSleep(mDNS *const m, AuthRecord *rr); // BB = Portability // CC = Marginal Power // DD = Total Power +// FF = Features Supported (Currently TCP Keepalive only) // // // ** Intent Metric ** @@ -2871,27 +3038,45 @@ extern mDNSBool RecordReadyForSleep(mDNS *const m, AuthRecord *rr); // 90 = 1 kW typedef enum - { - mDNSSleepProxyMetric_Dedicated = 20, - mDNSSleepProxyMetric_PrimaryHardware = 30, - mDNSSleepProxyMetric_PrimarySoftware = 40, - mDNSSleepProxyMetric_SecondaryHardware = 50, - mDNSSleepProxyMetric_SecondarySoftware = 60, - mDNSSleepProxyMetric_IncidentalHardware = 70, - mDNSSleepProxyMetric_IncidentalSoftware = 80 - } mDNSSleepProxyMetric; - -extern void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower); -#define mDNSCoreBeSleepProxyServer(M,S,P,MP,TP) \ - do { mDNS_Lock(m); mDNSCoreBeSleepProxyServer_internal((M),(S),(P),(MP),(TP)); mDNS_Unlock(m); } while(0) +{ + mDNSSleepProxyMetric_Dedicated = 20, + mDNSSleepProxyMetric_PrimaryHardware = 30, + mDNSSleepProxyMetric_PrimarySoftware = 40, + mDNSSleepProxyMetric_SecondaryHardware = 50, + mDNSSleepProxyMetric_SecondarySoftware = 60, + mDNSSleepProxyMetric_IncidentalHardware = 70, + mDNSSleepProxyMetric_IncidentalSoftware = 80 +} mDNSSleepProxyMetric; + +extern void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower, mDNSu8 features); +#define mDNSCoreBeSleepProxyServer(M,S,P,MP,TP,F) \ + do { mDNS_Lock(m); mDNSCoreBeSleepProxyServer_internal((M),(S),(P),(MP),(TP),(F)); mDNS_Unlock(m); } while(0) extern void FindSPSInCache(mDNS *const m, const DNSQuestion *const q, const CacheRecord *sps[3]); #define PrototypeSPSName(X) ((X)[0] >= 11 && (X)[3] == '-' && (X)[ 4] == '9' && (X)[ 5] == '9' && \ - (X)[6] == '-' && (X)[ 7] == '9' && (X)[ 8] == '9' && \ - (X)[9] == '-' && (X)[10] == '9' && (X)[11] == '9' ) + (X)[6] == '-' && (X)[ 7] == '9' && (X)[ 8] == '9' && \ + (X)[9] == '-' && (X)[10] == '9' && (X)[11] == '9' ) #define ValidSPSName(X) ((X)[0] >= 5 && mDNSIsDigit((X)[1]) && mDNSIsDigit((X)[2]) && mDNSIsDigit((X)[4]) && mDNSIsDigit((X)[5])) #define SPSMetric(X) (!ValidSPSName(X) || PrototypeSPSName(X) ? 1000000 : \ - ((X)[1]-'0') * 100000 + ((X)[2]-'0') * 10000 + ((X)[4]-'0') * 1000 + ((X)[5]-'0') * 100 + ((X)[7]-'0') * 10 + ((X)[8]-'0')) + ((X)[1]-'0') * 100000 + ((X)[2]-'0') * 10000 + ((X)[4]-'0') * 1000 + ((X)[5]-'0') * 100 + ((X)[7]-'0') * 10 + ((X)[8]-'0')) + +#define SPSFeatures(X) ((X)[0] >= 13 && (X)[12] =='.' ? ((X)[13]-'0') : 0 ) + +#define MD5_DIGEST_LENGTH 16 /* digest length in bytes */ +#define MD5_BLOCK_BYTES 64 /* block size in bytes */ +#define MD5_BLOCK_LONG (MD5_BLOCK_BYTES / sizeof(mDNSu32)) + +typedef struct MD5state_st +{ + mDNSu32 A,B,C,D; + mDNSu32 Nl,Nh; + mDNSu32 data[MD5_BLOCK_LONG]; + int num; +} MD5_CTX; + +extern int MD5_Init(MD5_CTX *c); +extern int MD5_Update(MD5_CTX *c, const void *data, unsigned long len); +extern int MD5_Final(unsigned char *md, MD5_CTX *c); // *************************************************************************** #if 0 @@ -2906,59 +3091,59 @@ extern void FindSPSInCache(mDNS *const m, const DNSQuestion *const q, const Cach // is false, the array size is negative, and the complier complains immediately. struct CompileTimeAssertionChecks_mDNS - { - // Check that the compiler generated our on-the-wire packet format structure definitions - // properly packed, without adding padding bytes to align fields on 32-bit or 64-bit boundaries. - char assert0[(sizeof(rdataSRV) == 262 ) ? 1 : -1]; - char assert1[(sizeof(DNSMessageHeader) == 12 ) ? 1 : -1]; - char assert2[(sizeof(DNSMessage) == 12+AbsoluteMaxDNSMessageData) ? 1 : -1]; - char assert3[(sizeof(mDNSs8) == 1 ) ? 1 : -1]; - char assert4[(sizeof(mDNSu8) == 1 ) ? 1 : -1]; - char assert5[(sizeof(mDNSs16) == 2 ) ? 1 : -1]; - char assert6[(sizeof(mDNSu16) == 2 ) ? 1 : -1]; - char assert7[(sizeof(mDNSs32) == 4 ) ? 1 : -1]; - char assert8[(sizeof(mDNSu32) == 4 ) ? 1 : -1]; - char assert9[(sizeof(mDNSOpaque16) == 2 ) ? 1 : -1]; - char assertA[(sizeof(mDNSOpaque32) == 4 ) ? 1 : -1]; - char assertB[(sizeof(mDNSOpaque128) == 16 ) ? 1 : -1]; - char assertC[(sizeof(CacheRecord ) == sizeof(CacheGroup) ) ? 1 : -1]; - char assertD[(sizeof(int) >= 4 ) ? 1 : -1]; - char assertE[(StandardAuthRDSize >= 256 ) ? 1 : -1]; - char assertF[(sizeof(EthernetHeader) == 14 ) ? 1 : -1]; - char assertG[(sizeof(ARP_EthIP ) == 28 ) ? 1 : -1]; - char assertH[(sizeof(IPv4Header ) == 20 ) ? 1 : -1]; - char assertI[(sizeof(IPv6Header ) == 40 ) ? 1 : -1]; - char assertJ[(sizeof(IPv6NDP ) == 24 ) ? 1 : -1]; - char assertK[(sizeof(UDPHeader ) == 8 ) ? 1 : -1]; - char assertL[(sizeof(IKEHeader ) == 28 ) ? 1 : -1]; - char assertM[(sizeof(TCPHeader ) == 20 ) ? 1 : -1]; - - // Check our structures are reasonable sizes. Including overly-large buffers, or embedding - // other overly-large structures instead of having a pointer to them, can inadvertently - // cause structure sizes (and therefore memory usage) to balloon unreasonably. - char sizecheck_RDataBody [(sizeof(RDataBody) == 264) ? 1 : -1]; - char sizecheck_ResourceRecord [(sizeof(ResourceRecord) <= 64) ? 1 : -1]; - char sizecheck_AuthRecord [(sizeof(AuthRecord) <= 1208) ? 1 : -1]; - char sizecheck_CacheRecord [(sizeof(CacheRecord) <= 184) ? 1 : -1]; - char sizecheck_CacheGroup [(sizeof(CacheGroup) <= 184) ? 1 : -1]; - char sizecheck_DNSQuestion [(sizeof(DNSQuestion) <= 786) ? 1 : -1]; - char sizecheck_ZoneData [(sizeof(ZoneData) <= 1624) ? 1 : -1]; - char sizecheck_NATTraversalInfo [(sizeof(NATTraversalInfo) <= 192) ? 1 : -1]; - char sizecheck_HostnameInfo [(sizeof(HostnameInfo) <= 3050) ? 1 : -1]; - char sizecheck_DNSServer [(sizeof(DNSServer) <= 328) ? 1 : -1]; - char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <= 6850) ? 1 : -1]; - char sizecheck_ServiceRecordSet [(sizeof(ServiceRecordSet) <= 5500) ? 1 : -1]; - char sizecheck_DomainAuthInfo [(sizeof(DomainAuthInfo) <= 7808) ? 1 : -1]; - char sizecheck_ServiceInfoQuery [(sizeof(ServiceInfoQuery) <= 3200) ? 1 : -1]; +{ + // Check that the compiler generated our on-the-wire packet format structure definitions + // properly packed, without adding padding bytes to align fields on 32-bit or 64-bit boundaries. + char assert0[(sizeof(rdataSRV) == 262 ) ? 1 : -1]; + char assert1[(sizeof(DNSMessageHeader) == 12 ) ? 1 : -1]; + char assert2[(sizeof(DNSMessage) == 12+AbsoluteMaxDNSMessageData) ? 1 : -1]; + char assert3[(sizeof(mDNSs8) == 1 ) ? 1 : -1]; + char assert4[(sizeof(mDNSu8) == 1 ) ? 1 : -1]; + char assert5[(sizeof(mDNSs16) == 2 ) ? 1 : -1]; + char assert6[(sizeof(mDNSu16) == 2 ) ? 1 : -1]; + char assert7[(sizeof(mDNSs32) == 4 ) ? 1 : -1]; + char assert8[(sizeof(mDNSu32) == 4 ) ? 1 : -1]; + char assert9[(sizeof(mDNSOpaque16) == 2 ) ? 1 : -1]; + char assertA[(sizeof(mDNSOpaque32) == 4 ) ? 1 : -1]; + char assertB[(sizeof(mDNSOpaque128) == 16 ) ? 1 : -1]; + char assertC[(sizeof(CacheRecord ) == sizeof(CacheGroup) ) ? 1 : -1]; + char assertD[(sizeof(int) >= 4 ) ? 1 : -1]; + char assertE[(StandardAuthRDSize >= 256 ) ? 1 : -1]; + char assertF[(sizeof(EthernetHeader) == 14 ) ? 1 : -1]; + char assertG[(sizeof(ARP_EthIP ) == 28 ) ? 1 : -1]; + char assertH[(sizeof(IPv4Header ) == 20 ) ? 1 : -1]; + char assertI[(sizeof(IPv6Header ) == 40 ) ? 1 : -1]; + char assertJ[(sizeof(IPv6NDP ) == 24 ) ? 1 : -1]; + char assertK[(sizeof(UDPHeader ) == 8 ) ? 1 : -1]; + char assertL[(sizeof(IKEHeader ) == 28 ) ? 1 : -1]; + char assertM[(sizeof(TCPHeader ) == 20 ) ? 1 : -1]; + + // Check our structures are reasonable sizes. Including overly-large buffers, or embedding + // other overly-large structures instead of having a pointer to them, can inadvertently + // cause structure sizes (and therefore memory usage) to balloon unreasonably. + char sizecheck_RDataBody [(sizeof(RDataBody) == 264) ? 1 : -1]; + char sizecheck_ResourceRecord [(sizeof(ResourceRecord) <= 64) ? 1 : -1]; + char sizecheck_AuthRecord [(sizeof(AuthRecord) <= 1208) ? 1 : -1]; + char sizecheck_CacheRecord [(sizeof(CacheRecord) <= 216) ? 1 : -1]; + char sizecheck_CacheGroup [(sizeof(CacheGroup) <= 216) ? 1 : -1]; + char sizecheck_DNSQuestion [(sizeof(DNSQuestion) <= 786) ? 1 : -1]; + char sizecheck_ZoneData [(sizeof(ZoneData) <= 1624) ? 1 : -1]; + char sizecheck_NATTraversalInfo [(sizeof(NATTraversalInfo) <= 192) ? 1 : -1]; + char sizecheck_HostnameInfo [(sizeof(HostnameInfo) <= 3050) ? 1 : -1]; + char sizecheck_DNSServer [(sizeof(DNSServer) <= 328) ? 1 : -1]; + char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <= 6850) ? 1 : -1]; + char sizecheck_ServiceRecordSet [(sizeof(ServiceRecordSet) <= 5500) ? 1 : -1]; + char sizecheck_DomainAuthInfo [(sizeof(DomainAuthInfo) <= 7888) ? 1 : -1]; + char sizecheck_ServiceInfoQuery [(sizeof(ServiceInfoQuery) <= 3200) ? 1 : -1]; #if APPLE_OSX_mDNSResponder - char sizecheck_ClientTunnel [(sizeof(ClientTunnel) <= 1148) ? 1 : -1]; + char sizecheck_ClientTunnel [(sizeof(ClientTunnel) <= 1148) ? 1 : -1]; #endif - }; +}; // *************************************************************************** #ifdef __cplusplus - } +} #endif #endif diff --git a/mDNSCore/nsec.c b/mDNSCore/nsec.c new file mode 100644 index 0000000..b230971 --- /dev/null +++ b/mDNSCore/nsec.c @@ -0,0 +1,1131 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// *************************************************************************** +// nsec.c: This file contains support functions to validate NSEC records for +// NODATA and NXDOMAIN error. +// *************************************************************************** + +#include "mDNSEmbeddedAPI.h" +#include "DNSCommon.h" +#include "nsec.h" + +// Implementation Notes +// +// NSEC records in DNSSEC are used for authenticated denial of existence i.e., if the response to a query +// results in NXDOMAIN or NODATA error, the response also contains NSEC records in the additional section +// to prove the non-existence of the original name. In most of the cases, NSEC records don't have any +// relationship to the original name queried i.e, if they are cached based on the name like other records, +// it can't be located to prove the non-existence of the original name. Hence, we create a negative cache +// record like we do for the NXDOMAIN/NODATA error and then cache the NSEC records as part of that. Sometimes, +// NSEC records are also used for wildcard expanded answer in which case they are cached with the cache record +// that is created for the original name. NSEC records are freed when the parent cache (the record that they +// are attached to is expired). +// +// NSEC records also can be queried like any other record and hence can exist independent of the negative +// cache record. It exists as part of negative cache record only when we get a NXDOMAIN/NODATA error with +// NSEC records. When a query results in NXDOMAIN/NODATA error and needs to be validated, the NSEC +// records (and its RRSIGS) are cached as part of the negative cache record. The NSEC records that +// exist separately from the negative cache record should not be used to answer ValidationRequired/ +// ValidatingResponse questions as it may not be sufficient to prove the non-existence of the name. +// The exception is when the NSEC record is looked up explicitly. See DNSSECRecordAnswersQuestion +// for more details. +// + +mDNSlocal CacheRecord *NSECParentForQuestion(mDNS *const m, DNSQuestion *q) +{ + CacheGroup *cg; + CacheRecord *cr; + mDNSu32 slot; + mDNSu32 namehash; + + slot = HashSlot(&q->qname); + namehash = DomainNameHashValue(&q->qname); + cg = CacheGroupForName(m, slot, namehash, &q->qname); + if (!cg) + { + LogDNSSEC("NSECParentForQuestion: Cannot find cg for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + return mDNSNULL; + } + for (cr = cg->members; cr; cr = cr->next) + if (SameNameRecordAnswersQuestion(&cr->resrec, q)) + return cr; + return mDNSNULL; +} + +// Note: This should just call the parent callback which will free the DNSSECVerifier. +mDNSlocal void VerifyNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status) +{ + if (!dv->parent) + { + LogMsg("VerifyNSECCCallback: ERROR!! no parent DV\n"); + FreeDNSSECVerifier(m, dv); + return; + } + if (dv->ac) + { + // Before we call the callback, we need to update the + // parent with our AuthChain information + AuthChainLink(dv->parent, dv->ac); + dv->ac = mDNSNULL; + dv->actail = &dv->ac; + } + dv->parent->DVCallback(m, dv->parent, status); + // The callback we called in the previous line should recursively + // free all the DNSSECVerifiers starting from dv->parent and above. + // So, set that to NULL and free the "dv" itself here. + dv->parent = mDNSNULL; + FreeDNSSECVerifier(m, dv); +} + +// If the caller provides a callback, it takes the responsibility of calling the original callback +// in "pdv" when it is done. +// +// INPUT: +// +// rr: The NSEC record that should be verified +// rv: The NSEC record can also be provided like this +// pdv: Parent DNSSECVerifier which will be called when the verification is done. +// callback: As part of the proof, we need multiple NSEC verifications before we call the "pdv" callback in +// which case a intermediate "callback" is provided which can be used to do multiple verifications. +// ncr: The cache record where the RRSIGS are cached +// +// NSEC records and signatures are cached along with the cache record so that we can expire them all together. We can't cache +// them based on the name hash like other records as in most cases the returned NSECs has a different name than we asked for +// (except for NODATA error where the name exists but type does not exist). +// +mDNSlocal void VerifyNSEC(mDNS *const m, ResourceRecord *rr, RRVerifier *rv, DNSSECVerifier *pdv, CacheRecord *ncr, + DNSSECVerifierCallback callback) +{ + DNSSECVerifier *dv = mDNSNULL; + CacheRecord **rp; + const domainname *name; + mDNSu16 rrtype; + + if (!rv && !rr) + { + LogDNSSEC("VerifyNSEC: Both rr and rv are NULL"); + goto error; + } + if (!pdv) + { + LogDNSSEC("VerifyNSEC: ERROR!! pdv is NULL"); + return; + } + // Remember the name and type for which we are verifying, so that when we are done processing all + // the verifications, we can trace it back. + // + // Note: Currently it is not used because when the verification completes as we just + // call the "pdv" callback which has its origName and origType. + if (rr) + { + name = rr->name; + rrtype = rr->rrtype; + } + else + { + name = &rv->name; + rrtype = rv->rrtype; + } + + dv = AllocateDNSSECVerifier(m, name, rrtype, pdv->q.InterfaceID, (callback ? callback : VerifyNSECCallback), mDNSNULL); + if (!dv) { LogMsg("VerifyNSEC: mDNSPlatformMemAlloc failed"); return; } + + dv->parent = pdv; + + if (AddRRSetToVerifier(dv, rr, rv, RRVS_rr) != mStatus_NoError) + { + LogMsg("VerifyNSEC: ERROR!! AddRRSetToVerifier failed to add NSEC"); + goto error; + } + + // Add the signatures after validating them + rp = &(ncr->nsec); + while (*rp) + { + if ((*rp)->resrec.rrtype == kDNSType_RRSIG) + { + ValidateRRSIG(dv, RRVS_rrsig, &(*rp)->resrec); + } + rp=&(*rp)->next; + } + + if (!dv->rrset || !dv->rrsig) + { + LogMsg("VerifyNSEC: ERROR!! AddRRSetToVerifier missing rrset %p, rrsig %p", dv->rrset, dv->rrsig); + goto error; + } + + // Next step is to fetch the keys + dv->next = RRVS_key; + + StartDNSSECVerification(m, dv); + return; +error: + pdv->DVCallback(m, pdv, DNSSEC_Indeterminate); + if (dv) + { + dv->parent = mDNSNULL; + FreeDNSSECVerifier(m, dv); + } + return; +} + +mDNSlocal void DeleteCachedNSECS(mDNS *const m, CacheRecord *cr) +{ + CacheRecord *rp, *next; + + if (cr->nsec) LogDNSSEC("DeleteCachedNSECS: Deleting NSEC Records\n"); + for (rp = cr->nsec; rp; rp = next) + { + next = rp->next; + ReleaseCacheRecord(m, rp); + } + cr->nsec = mDNSNULL; +} + +// Returns success if it adds the nsecs and the rrsigs to the cache record. Otherwise, it returns +// failure (mDNSfalse) +mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode) +{ + CacheRecord *cr, *next; + + if (rcode != kDNSFlag1_RC_NoErr && rcode != kDNSFlag1_RC_NXDomain) + { + LogMsg("AddNSECSForCacheRecord: Addings nsecs for rcode %d", rcode); + return mDNSfalse; + } + + // Sanity check the list to see if we have anything else other than + // NSECs and its RRSIGs + for (cr = crlist; cr; cr = cr->next) + { + next = cr->next; + if (cr->resrec.rrtype != kDNSType_NSEC && cr->resrec.rrtype != kDNSType_RRSIG) + { + LogMsg("AddNSECSForCacheRecord: ERROR!! Adding Wrong record %s", CRDisplayString(m, cr)); + return mDNSfalse; + } + if (cr->resrec.rrtype == kDNSType_RRSIG) + { + RDataBody2 *const rdb = (RDataBody2 *)cr->smallrdatastorage.data; + rdataRRSig *rrsig = &rdb->rrsig; + if (swap16(rrsig->typeCovered) != kDNSType_NSEC) + { + LogMsg("AddNSECSForCacheRecord:ERROR!! Adding RRSIG with Wrong type %s", CRDisplayString(m, cr)); + return mDNSfalse; + } + } + LogDNSSEC("AddNSECSForCacheRecord: Found a valid record %s", CRDisplayString(m, cr)); + } + DeleteCachedNSECS(m, negcr); + LogDNSSEC("AddNSECSForCacheRecord: Adding NSEC Records for %s", CRDisplayString(m, negcr)); + negcr->nsec = crlist; + negcr->rcode = rcode; + return mDNStrue; +} + +// Return the number of labels that matches starting from the right (excluding the +// root label) +mDNSlocal int CountLabelsMatch(const domainname *const d1, const domainname *const d2) +{ + int count, c1, c2; + int match, i, skip1, skip2; + + c1 = CountLabels(d1); + skip1 = c1 - 1; + c2 = CountLabels(d2); + skip2 = c2 - 1; + + // Root label always matches. And we don't include it here to + // match CountLabels + match = 0; + + // Compare as many labels as possible starting from the rightmost + count = c1 < c2 ? c1 : c2; + for (i = count; i > 0; i--) + { + const domainname *da, *db; + + da = SkipLeadingLabels(d1, skip1); + db = SkipLeadingLabels(d2, skip2); + if (!SameDomainName(da, db)) return match; + skip1--; + skip2--; + match++; + } + return match; +} + +// RFC 4034: +// +// Section 6.1: +// +// For the purposes of DNS security, owner names are ordered by treating +// individual labels as unsigned left-justified octet strings. The +// absence of a octet sorts before a zero value octet, and uppercase +// US-ASCII letters are treated as if they were lowercase US-ASCII +// letters. +// +// To compute the canonical ordering of a set of DNS names, start by +// sorting the names according to their most significant (rightmost) +// labels. For names in which the most significant label is identical, +// continue sorting according to their next most significant label, and +// so forth. +// +// Returns 0 if the names are same +// Returns -1 if d1 < d2 +// Returns 1 if d1 > d2 +// +// subdomain is set if there is at least one label match (starting from the end) +// and d1 has more labels than d2 e.g., a.b.com is a subdomain of b.com +// +mDNSlocal int DNSSECCanonicalOrder(const domainname *const d1, const domainname *const d2, int *subdomain) +{ + int count, c1, c2; + int i, skip1, skip2; + + c1 = CountLabels(d1); + skip1 = c1 - 1; + c2 = CountLabels(d2); + skip2 = c2 - 1; + + if (subdomain) *subdomain = 0; + + // Compare as many labels as possible starting from the rightmost + count = c1 < c2 ? c1 : c2; + for (i = count; i > 0; i--) + { + mDNSu8 *a, *b; + int j, len, lena, lenb; + + a = (mDNSu8 *)SkipLeadingLabels(d1, skip1); + b = (mDNSu8 *)SkipLeadingLabels(d2, skip2); + lena = *a; + lenb = *b; + // Compare label by label. Note that "z" > "yak" because z > y, but z < za + // (lena - lenb check below) because 'za' has two characters. Hence compare the + // letters first and then compare the length of the label at the end. + len = lena < lenb ? lena : lenb; + a++; b++; + for (j = 0; j < len; j++) + { + mDNSu8 ac = *a++; + mDNSu8 bc = *b++; + if (mDNSIsUpperCase(ac)) ac += 'a' - 'A'; + if (mDNSIsUpperCase(bc)) bc += 'a' - 'A'; + if (ac != bc) + { + verbosedebugf("DNSSECCanonicalOrder: returning ac %c, bc %c", ac, bc); + return ((ac < bc) ? -1 : 1); + } + } + if ((lena - lenb) != 0) + { + verbosedebugf("DNSSECCanonicalOrder: returning lena %d lenb %d", lena, lenb); + return ((lena < lenb) ? -1 : 1); + } + + // Continue with the next label + skip1--; + skip2--; + } + // We have compared label by label. Both of them are same if we are here. + // + // Two possibilities. + // + // 1) Both names have same number of labels. In that case, return zero. + // 2) The number of labels is not same. As zero label sorts before, names + // with more number of labels is greater. + + // a.b.com is a subdomain of b.com + if ((c1 > c2) && subdomain) + *subdomain = 1; + + verbosedebugf("DNSSECCanonicalOrder: returning c1 %d c2 %d\n", c1, c2); + if (c1 != c2) + return ((c1 < c2) ? -1 : 1); + else + return 0; +} + +// Empty Non-Terminal (ENT): if the qname is bigger than nsec owner's name and a +// subdomain of the nsec's nxt field, then the qname is a empty non-terminal. For +// example, if you are looking for (in RFC 4035 example zone) "y.w.example A" +// record, if it is a ENT, then it would return +// +// x.w.example. 3600 NSEC x.y.w.example. MX RRSIG NSEC +// +// This function is normally called before checking for wildcard matches. If you +// find this NSEC, there is no need to look for a wildcard record +// that could possibly answer the question. +mDNSexport mDNSBool NSECAnswersENT(const ResourceRecord *const rr, domainname *qname) +{ + const domainname *oname = rr->name; + const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; + const domainname *nxt = (const domainname *)&rdb->data; + int ret; + int subdomain; + + // Is the owner name smaller than qname? + ret = DNSSECCanonicalOrder(oname, qname, mDNSNULL); + if (ret < 0) + { + // Is the next domain field a subdomain of qname ? + ret = DNSSECCanonicalOrder(nxt, qname, &subdomain); + if (subdomain) + { + if (ret <= 0) + { + LogMsg("NSECAnswersENT: ERROR!! DNSSECCanonicalOrder subdomain set " + " qname %##s, NSEC %##s", qname->c, rr->name->c); + } + return mDNStrue; + } + } + return mDNSfalse; +} + +mDNSlocal const domainname *NSECClosestEncloser(ResourceRecord *rr, domainname *qname) +{ + const domainname *oname = rr->name; + const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; + const domainname *nxt = (const domainname *)&rdb->data; + int match1, match2; + + match1 = CountLabelsMatch(oname, qname); + match2 = CountLabelsMatch(nxt, qname); + // Return the closest i.e the one that matches more labels + if (match1 > match2) + return SkipLeadingLabels(oname, CountLabels(oname) - match1); + else + return SkipLeadingLabels(nxt, CountLabels(nxt) - match2); +} + +// Assumption: NSEC has been validated outside of this function +// +// Does the name exist given the name and NSEC rr ? +// +// Returns -1 if it is an inappropriate nsec +// Returns 1 if the name exists +// Returns 0 if the name does not exist +// +mDNSlocal int NSECNameExists(mDNS *const m, ResourceRecord *rr, domainname *name, mDNSu16 qtype) +{ + const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; + const domainname *nxt = (const domainname *)&rdb->data; + const domainname *oname = rr->name; // owner name + int ret1, subdomain1; + int ret2, subdomain2; + int ret3, subdomain3; + + ret1 = DNSSECCanonicalOrder(oname, name, &subdomain1); + if (ret1 > 0) + { + LogDNSSEC("NSECNameExists: owner name %##s is bigger than name %##s", oname->c, name->c); + return -1; + } + + // Section 4.1 of draft-ietf-dnsext-dnssec-bis-updates-14: + // + // Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume non- + // existence of any RRs below that zone cut, which include all RRs at + // that (original) owner name other than DS RRs, and all RRs below that + // owner name regardless of type. + // + // This also implies that we can't use the child side NSEC for DS question. + + if (!ret1) + { + mDNSBool soa = RRAssertsExistence(rr, kDNSType_SOA); + mDNSBool ns = RRAssertsExistence(rr, kDNSType_NS); + + // We are here because the owner name is the same as "name". Make sure the + // NSEC has the right NS and SOA bits set. + if (ns && !soa && qtype != kDNSType_DS) + { + LogDNSSEC("NSECNameExists: Parent side NSEC %s can't be used for question %##s (%s)", + RRDisplayString(m, rr), name->c, DNSTypeName(qtype)); + return -1; + } + else if (ns && soa && qtype == kDNSType_DS) + { + LogDNSSEC("NSECNameExists: Child side NSEC %s can't be used for question %##s (%s)", + RRDisplayString(m, rr), name->c, DNSTypeName(qtype)); + return -1; + } + LogDNSSEC("NSECNameExists: owner name %##s is same as name %##s", oname->c, name->c); + return 1; + } + + // If the name is a.b.com and NSEC's owner name is b.com i.e., a subdomain + // and nsec comes from the parent (NS is set and SOA is not set), then this + // NSEC can't be used for names below the owner name. + // + // Similarly if DNAME is set, we can't use it here. See RFC2672-bis-dname + // appendix. + if (subdomain1 && (RRAssertsExistence(rr, kDNSType_DNAME) || + (RRAssertsNonexistence(rr, kDNSType_SOA) && RRAssertsExistence(rr, kDNSType_NS)))) + { + LogDNSSEC("NSECNameExists: NSEC %s comes from the parent, can't use it here", + RRDisplayString(m, rr)); + return -1; + } + + // At this stage, we know that name is greater than the owner name and + // the nsec is not from the parent side. + // + // Compare with the next field in the nsec. + // + ret2 = DNSSECCanonicalOrder(name, nxt, &subdomain2); + + // Exact match with the nsec next name + if (!ret2) + { + LogDNSSEC("NSECNameExists: name %##s is same as nxt name %##s", name->c, nxt->c); + return 1; + } + + ret3 = DNSSECCanonicalOrder(oname, nxt, &subdomain3); + + if (!ret3) + { + // Pathological case of a single name in the domain. This means only the + // apex of the zone itself exists. Nothing below it. "subdomain2" indicates + // that name is a subdmain of "next" and hence below the zone. + if (subdomain2) + { + LogDNSSEC("NSECNameExists: owner name %##s subdomain of nxt name %##s", oname->c, nxt->c); + return 0; + } + else + { + LogDNSSEC("NSECNameExists: Single name in zone, owner name %##s is same as nxt name %##s", oname->c, nxt->c); + return -1; + } + } + + if (ret3 < 0) + { + // Regular NSEC in the zone. Make sure that the "name" lies within + // oname and next. oname < name and name < next + if (ret1 < 0 && ret2 < 0) + { + LogDNSSEC("NSECNameExists: Normal NSEC name %##s lies within owner %##s and nxt name %##s", + name->c, oname->c, nxt->c); + return 0; + } + else + { + LogDNSSEC("NSECNameExists: Normal NSEC name %##s does not lie within owner %##s and nxt name %##s", + name->c, oname->c, nxt->c); + return -1; + } + } + else + { + // Last NSEC in the zone. The "next" is pointing to the apex. All names + // should be a subdomain of that and the name should be bigger than + // oname + if (ret1 < 0 && subdomain2) + { + LogDNSSEC("NSECNameExists: Last NSEC name %##s lies within owner %##s and nxt name %##s", + name->c, oname->c, nxt->c); + return 0; + } + else + { + LogDNSSEC("NSECNameExists: Last NSEC name %##s does not lie within owner %##s and nxt name %##s", + name->c, oname->c, nxt->c); + return -1; + } + } + + LogDNSSEC("NSECNameExists: NSEC %s did not match any case", RRDisplayString(m, rr)); + return -1; +} + +// If the answer was result of a wildcard match, then this function proves +// that a proper wildcard was used to answer the question and that the +// original name does not exist +mDNSexport void WildcardAnswerProof(mDNS *const m, DNSSECVerifier *dv) +{ + CacheRecord *ncr; + CacheRecord **rp; + const domainname *ce; + DNSQuestion q; + + LogDNSSEC("WildcardAnswerProof: Question %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); + // + // RFC 4035: Section 3.1.3.3 + // + // 1) We used a wildcard because the qname does not exist, so verify + // that the qname does not exist + // + // 2) Is the wildcard the right one ? + // + // Unfortunately, this is not well explained in that section. Refer to + // RFC 5155 section 7.2.6. + + // Walk the list of nsecs we received and see if they prove that + // the name does not exist + + mDNSPlatformMemZero(&q, sizeof(DNSQuestion)); + q.ThisQInterval = -1; + InitializeQuestion(m, &q, dv->InterfaceID, &dv->origName, dv->origType, mDNSNULL, mDNSNULL); + + ncr = NSECParentForQuestion(m, &q); + if (!ncr) + { + LogMsg("NSECWildCardProof: Can't find NSEC Parent for %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); + goto error; + } + rp = &(ncr->nsec); + while (*rp) + { + if ((*rp)->resrec.rrtype == kDNSType_NSEC) + { + CacheRecord *cr = *rp; + if (!NSECNameExists(m, &cr->resrec, &dv->origName, dv->origType)) + break; + } + rp=&(*rp)->next; + } + if (!(*rp)) + { + LogMsg("NSECWildCardProof: ERROR!! No NSECs found for %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); + goto error; + } + ce = NSECClosestEncloser(&((*rp)->resrec), &dv->origName); + if (!ce) + { + LogMsg("NSECWildCardProof: ERROR!! Closest Encloser NULL for %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); + goto error; + } + if (!SameDomainName(ce, dv->wildcardName)) + { + LogMsg("NSECWildCardProof: ERROR!! Closest Encloser %##s does not match wildcard name %##s", q.qname.c, dv->wildcardName->c); + goto error; + } + + VerifyNSEC(m, &((*rp)->resrec), mDNSNULL, dv, ncr, mDNSNULL); + return; +error: + dv->DVCallback(m, dv, DNSSEC_Insecure); +} + +// We have a NSEC. Need to see if it proves that NODATA exists for the given name. Note that this +// function does not prove anything as proof may require more than one NSEC and this function +// processes only one NSEC at a time. +// +// Returns mDNSfalse if the NSEC does not prove the NODATA error +// Returns mDNStrue if the NSEC proves the NODATA error +// +mDNSlocal mDNSBool NSECNoDataError(mDNS *const m, ResourceRecord *rr, domainname *name, mDNSu16 qtype, domainname **wildcard) +{ + const domainname *oname = rr->name; // owner name + + if (wildcard) *wildcard = mDNSNULL; + // RFC 4035 + // + // section 3.1.3.1 : Name matches. Prove that the type does not exist and also CNAME is + // not set as in that case CNAME should have been returned ( CNAME part is mentioned in + // section 4.3 of dnssec-bis-updates.) Without the CNAME check, a positive response can + // be converted to a NODATA/NOERROR response. + // + // section 3.1.3.4 : No exact match for the name but there is a wildcard that could match + // the name but not the type. There are two NSECs in this case. One of them is a wildcard + // NSEC and another NSEC proving that the qname does not exist. We are called with one + // NSEC at a time. We return what we matched and the caller should decide whether all + // conditions are met for the proof. + if (SameDomainName(oname, name)) + { + mDNSBool soa = RRAssertsExistence(rr, kDNSType_SOA); + mDNSBool ns = RRAssertsExistence(rr, kDNSType_NS); + if (qtype != kDNSType_DS) + { + // For non-DS type questions, we don't want to use the parent side records to + // answer it + if (ns && !soa) + { + LogDNSSEC("NSECNoDataError: Parent side NSEC %s, can't use for child qname %##s (%s)", + RRDisplayString(m, rr), name->c, DNSTypeName(qtype)); + return mDNSfalse; + } + } + else + { + if (ns && soa) + { + LogDNSSEC("NSECNoDataError: Child side NSEC %s, can't use for parent qname %##s (%s)", + RRDisplayString(m, rr), name->c, DNSTypeName(qtype)); + return mDNSfalse; + } + } + if (RRAssertsExistence(rr, qtype) || RRAssertsExistence(rr, kDNSType_CNAME)) + { + LogMsg("NSECNoDataError: ERROR!! qtype %s exists in %s", DNSTypeName(qtype), RRDisplayString(m, rr)); + return mDNSfalse; + } + LogDNSSEC("NSECNoDataError: qype %s does not exist in %s", DNSTypeName(qtype), RRDisplayString(m, rr)); + return mDNStrue; + } + else + { + // Name does not exist. Before we check for a wildcard match, make sure that + // this is not an ENT. + // + if (NSECAnswersENT(rr, name)) + { + LogDNSSEC("NSECNoDataError: ERROR!! name %##s exists %s", name->c, RRDisplayString(m, rr)); + return mDNSfalse; + } + + // Wildcard check. If this is a wildcard NSEC, then check to see if we could + // have answered the question using this wildcard and it should not have the + // "qtype" passed in with its bitmap. + // + // See RFC 4592, on how wildcards are used to synthesize answers. Find the + // closest encloser and the qname should be a subdomain i.e if the wildcard + // is *.x.example, x.example is the closest encloser and the qname should be + // a subdomain e.g., y.x.example or z.y.x.example and so on. + if (oname->c[0] == 1 && oname->c[1] == '*') + { + int r, s; + const domainname *ce = SkipLeadingLabels(oname, 1); + + r = DNSSECCanonicalOrder(name, ce, &s); + if (s) + { + if (RRAssertsExistence(rr, qtype) || RRAssertsExistence(rr, kDNSType_CNAME)) + { + LogMsg("NSECNoDataError: ERROR!! qtype %s exists in wildcard %s", DNSTypeName(qtype), RRDisplayString(m, rr)); + return mDNSfalse; + } + // It is odd for a wildcard to match when we are looking up DS + // See RFC 4592 + if (qtype == kDNSType_DS) + { + LogMsg("NSECNoDataError: ERROR!! DS qtype exists in wildcard %s", RRDisplayString(m, rr)); + return mDNSfalse; + } + // Don't use the parent side record for this + if (RRAssertsNonexistence(rr, kDNSType_SOA) && + RRAssertsExistence(rr, kDNSType_NS)) + { + LogDNSSEC("NSECNoDataError: Parent side wildcard NSEC %s, can't use for child qname %##s (%s)", + RRDisplayString(m, rr), name->c, DNSTypeName(qtype)); + return mDNSfalse; + } + *wildcard = (domainname *)ce; + LogDNSSEC("NSECNoDataError: qtype %s does not exist in wildcard %s", DNSTypeName(qtype), RRDisplayString(m, rr)); + return mDNStrue; + } + } + return mDNSfalse; + } +} + +mDNSlocal void NoDataNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status) +{ + RRVerifier *rv; + DNSSECVerifier *pdv; + CacheRecord *ncr; + + LogDNSSEC("NoDataNSECCallback: called"); + if (!dv->parent) + { + LogMsg("NoDataNSECCCallback: no parent DV"); + FreeDNSSECVerifier(m, dv); + return; + } + + if (dv->ac) + { + // Before we free the "dv", we need to update the + // parent with our AuthChain information + AuthChainLink(dv->parent, dv->ac); + dv->ac = mDNSNULL; + dv->actail = &dv->ac; + } + + pdv = dv->parent; + if (status != DNSSEC_Secure) + { + goto error; + } + if (!(pdv->flags & NSEC_PROVES_NONAME_EXISTS)) + { + LogMsg("NoDataNSECCCallback: ERROR!! NSEC_PROVES_NONAME_EXISTS not set"); + goto error; + } + if (!(pdv->flags & WILDCARD_PROVES_NONAME_EXISTS)) + { + LogMsg("NoDataNSECCCallback: ERROR!! WILDCARD_PROVES_NONAME_EXISTS not set"); + goto error; + } + + // We don't care about the "dv" that was allocated in VerifyNSEC. + // Get the original verifier and verify the other NSEC like we did + // the first time. + dv->parent = mDNSNULL; + FreeDNSSECVerifier(m, dv); + + ncr = NSECParentForQuestion(m, &pdv->q); + if (!ncr) + { + LogMsg("NoDataNSECCallback: Can't find NSEC Parent for %##s (%s)", pdv->q.qname.c, DNSTypeName(pdv->q.qtype)); + goto error; + } + + rv = pdv->pendingNSEC; + pdv->pendingNSEC = mDNSNULL; + // Verify the pendingNSEC and we don't need to come back here. Let the regular + // NSECCallback call the original callback. + VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, mDNSNULL); + return; + +error: + dv->parent->DVCallback(m, dv->parent, status); + dv->parent = mDNSNULL; + FreeDNSSECVerifier(m, dv); +} + +mDNSlocal void NameErrorNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status) +{ + RRVerifier *rv; + DNSSECVerifier *pdv; + CacheRecord *ncr; + + LogDNSSEC("NameErrorNSECCallback: called"); + if (!dv->parent) + { + LogMsg("NameErrorNSECCCallback: no parent DV"); + FreeDNSSECVerifier(m, dv); + return; + } + + if (dv->ac) + { + // Before we free the "dv", we need to update the + // parent with our AuthChain information + AuthChainLink(dv->parent, dv->ac); + dv->ac = mDNSNULL; + dv->actail = &dv->ac; + } + + pdv = dv->parent; + if (status != DNSSEC_Secure) + { + goto error; + } + // We don't care about the "dv" that was allocated in VerifyNSEC. + // Get the original verifier and verify the other NSEC like we did + // the first time. + dv->parent = mDNSNULL; + FreeDNSSECVerifier(m, dv); + + ncr = NSECParentForQuestion(m, &pdv->q); + if (!ncr) + { + LogMsg("NameErrorNSECCallback: Can't find NSEC Parent for %##s (%s)", pdv->q.qname.c, DNSTypeName(pdv->q.qtype)); + goto error; + } + rv = pdv->pendingNSEC; + pdv->pendingNSEC = mDNSNULL; + // Verify the pendingNSEC and we don't need to come back here. Let the regular + // NSECCallback call the original callback. + VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, mDNSNULL); + return; + +error: + dv->parent->DVCallback(m, dv->parent, status); + dv->parent = mDNSNULL; + FreeDNSSECVerifier(m, dv); +} + +// We get a NODATA error with no records in answer section. This proves +// that qname does not exist. +mDNSlocal void NoDataProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr) +{ + CacheRecord **rp; + domainname *wildcard = mDNSNULL; + const domainname *ce = mDNSNULL; + ResourceRecord *nsec_wild = mDNSNULL; + ResourceRecord *nsec_noname = mDNSNULL; + + // NODATA Error could mean two things. The name exists with no type or there is a + // wildcard that matches the name but no type. This is done by NSECNoDataError. + // + // If it is the case of wildcard, there are two NSECs. One is the wildcard NSEC and + // the other NSEC to prove that there is no other closer match. + + wildcard = mDNSNULL; + rp = &(ncr->nsec); + while (*rp) + { + if ((*rp)->resrec.rrtype == kDNSType_NSEC) + { + CacheRecord *cr = *rp; + if (NSECNoDataError(m, &cr->resrec, &dv->q.qname, dv->q.qtype, &wildcard)) + { + if (wildcard) + { + dv->flags |= WILDCARD_PROVES_NONAME_EXISTS; + LogDNSSEC("NoDataProof: NSEC %s proves NODATA error for %##s (%s)", + RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype)); + } + else + { + dv->flags |= NSEC_PROVES_NOTYPE_EXISTS; + LogDNSSEC("NoDataProof: NSEC %s proves NOTYPE error for %##s (%s)", + RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype)); + } + nsec_wild = &cr->resrec; + } + if (!NSECNameExists(m, &cr->resrec, &dv->q.qname, dv->q.qtype)) + { + LogDNSSEC("NoDataProof: NSEC %s proves that name %##s (%s) does not exist", + RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype)); + // If we have a wildcard, then we should check to see if the closest + // encloser is the same as the wildcard. + ce = NSECClosestEncloser(&cr->resrec, &dv->q.qname); + dv->flags |= NSEC_PROVES_NONAME_EXISTS; + nsec_noname = &cr->resrec; + } + } + rp=&(*rp)->next; + } + // If the type exists, then we have to verify just that NSEC + if (!(dv->flags & NSEC_PROVES_NOTYPE_EXISTS)) + { + // If we have a wildcard, then we should have a "ce" which matches the wildcard + // If we don't have a wildcard, then we should have proven that the name does not + // exist which means we would have set the "ce". + if (wildcard && !ce) + { + LogMsg("NoDataProof: Cannot prove that the name %##s (%s) does not exist", dv->q.qname.c, DNSTypeName(dv->q.qtype)); + goto error; + } + if (wildcard && !SameDomainName(wildcard, ce)) + { + LogMsg("NoDataProof: wildcard %##s does not match closest encloser %##s", wildcard->c, ce->c); + goto error; + } + } + + if ((dv->flags & (WILDCARD_PROVES_NONAME_EXISTS|NSEC_PROVES_NONAME_EXISTS)) == + (WILDCARD_PROVES_NONAME_EXISTS|NSEC_PROVES_NONAME_EXISTS)) + { + mStatus status; + RRVerifier *r = AllocateRRVerifier(nsec_noname, &status); + if (!r) goto error; + // First verify wildcard NSEC and then when we are done, we + // will verify the noname nsec + dv->pendingNSEC = r; + VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, NoDataNSECCallback); + } + else if ((dv->flags & WILDCARD_PROVES_NONAME_EXISTS) || + (dv->flags & NSEC_PROVES_NOTYPE_EXISTS)) + { + VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, mDNSNULL); + } + else if (dv->flags & NSEC_PROVES_NONAME_EXISTS) + { + VerifyNSEC(m, nsec_noname, mDNSNULL, dv, ncr, mDNSNULL); + } + return; +error: + LogDNSSEC("NoDataProof: Error return"); + dv->DVCallback(m, dv, DNSSEC_Insecure); +} + +mDNSlocal mDNSBool NSECNoWildcard(mDNS *const m, ResourceRecord *rr, domainname *qname, mDNSu16 qtype) +{ + const domainname *ce; + domainname wild; + + // If the query name is c.x.w.example and if the name does not exist, we should get + // get a nsec back that looks something like this: + // + // w.example NSEC a.w.example + // + // First, we need to get the closest encloser which in this case is w.example. Wild + // card synthesis works by finding the closest encloser first and then look for + // a "*" label (assuming * label does not appear in the question). If it does not + // exists, it would return the NSEC at that name. And the wildcard name at the + // closest encloser "*.w.example" would be covered by such an NSEC. (Appending "*" + // makes it bigger than w.example and "* is smaller than "a" for the above NSEC) + // + ce = NSECClosestEncloser(rr, qname); + if (!ce) { LogMsg("NSECNoWildcard: No closest encloser for rr %s, qname %##s (%s)", qname->c, DNSTypeName(qtype)); return mDNSfalse; } + + wild.c[0] = 1; + wild.c[1] = '*'; + wild.c[2] = 0; + if (!AppendDomainName(&wild, ce)) + { + LogMsg("NSECNoWildcard: ERROR!! Can't append domainname closest encloser name %##s, qname %##s (%s)", ce->c, qname->c, DNSTypeName(qtype)); + return mDNSfalse; + } + if (NSECNameExists(m, rr, &wild, qtype) != 0) + { + LogDNSSEC("NSECNoWildcard: Wildcard name %##s exists or not valid qname %##s (%s)", wild.c, qname->c, DNSTypeName(qtype)); + return mDNSfalse; + } + LogDNSSEC("NSECNoWildcard: Wildcard name %##s does not exist for record %s, qname %##s (%s)", wild.c, + RRDisplayString(m, rr), qname->c, DNSTypeName(qtype)); + return mDNStrue; +} + +// We get a NXDOMAIN error with no records in answer section. This proves +// that qname does not exist. +mDNSlocal void NameErrorProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr) +{ + CacheRecord **rp; + ResourceRecord *nsec_wild = mDNSNULL; + ResourceRecord *nsec_noname = mDNSNULL; + mStatus status; + + // NXDOMAIN Error. We need to prove that the qname does not exist and there + // is no wildcard that can be used to answer the question. + + rp = &(ncr->nsec); + while (*rp) + { + if ((*rp)->resrec.rrtype == kDNSType_NSEC) + { + CacheRecord *cr = *rp; + if (!NSECNameExists(m, &cr->resrec, &dv->q.qname, dv->q.qtype)) + { + LogDNSSEC("NameErrorProof: NSEC %s proves name does not exist for %##s (%s)", + RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype)); + // If we have a wildcard, then we should check to see if the closest + // encloser is the same as the wildcard. + dv->flags |= NSEC_PROVES_NONAME_EXISTS; + nsec_noname = &cr->resrec; + } + if (NSECNoWildcard(m, &cr->resrec, &dv->q.qname, dv->q.qtype)) + { + dv->flags |= WILDCARD_PROVES_NONAME_EXISTS; + nsec_wild = &cr->resrec; + LogDNSSEC("NameErrorProof: NSEC %s proves wildcard cannot answer question for %##s (%s)", + RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype)); + } + } + rp=&(*rp)->next; + } + if (!nsec_noname || !nsec_wild) + { + LogMsg("NameErrorProof: Proof failed for %##s (%s) noname %p, wild %p", dv->q.qname.c, DNSTypeName(dv->q.qtype), nsec_noname, nsec_wild); + goto error; + } + + // First verify wildcard NSEC and then when we are done, we will verify the noname nsec. + // Sometimes a single NSEC can prove both that the "qname" does not exist and a wildcard + // could not have produced qname. These are a few examples where this can happen. + // + // 1. If the zone is example.com and you look up *.example.com and if there are no wildcards, + // you will get a NSEC back "example.com NSEC a.example.com". This proves that both the + // name does not exist and *.example.com also does not exist + // + // 2. If the zone is example.com and it has a record like this: + // + // example.com NSEC d.example.com + // + // any name you lookup in between like a.example.com,b.example.com etc. you will get a single + // NSEC back. In that case we just have to verify only once. + // + if (nsec_wild != nsec_noname) + { + RRVerifier *r = AllocateRRVerifier(nsec_noname, &status); + if (!r) goto error; + dv->pendingNSEC = r; + VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, NameErrorNSECCallback); + } + else + { + VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, mDNSNULL); + } + return; +error: + dv->DVCallback(m, dv, DNSSEC_Insecure); +} + +mDNSexport void ValidateWithNSECS(mDNS *const m, DNSSECVerifier *dv, CacheRecord *cr) +{ + LogDNSSEC("ValidateWithNSECS: called for %s", CRDisplayString(m, cr)); + // "parent" is set when we are validating a NSEC. In the process of validating that + // nsec, we encountered another NSEC. For example, we are looking up the A record for + // www.example.com, we got an NSEC at some stage. We come here to validate the NSEC + // the first time. While validating the NSEC we remember the original validation result + // in the parent. But while validating the NSEC, we got another NSEC back e.g., not + // a secure delegation i.e., we got an NSEC proving that DS does not exist. We prove + // that again. But if we receive more NSECs after this, we stop. + // + if (dv->parent) + { + if (dv->parent->parent) + { + LogMsg("ValidateWithNSECS: ERROR!! dv parent is set already"); + dv->DVCallback(m, dv, DNSSEC_Indeterminate); + return; + } + else + { + DNSSECVerifier *pdv = dv; + dv = AllocateDNSSECVerifier(m, &pdv->q.qname, pdv->q.qtype, pdv->q.InterfaceID, VerifyNSECCallback, mDNSNULL); + if (!dv) + { + LogMsg("VerifyNSEC: mDNSPlatformMemAlloc failed"); + pdv->DVCallback(m, pdv, DNSSEC_Indeterminate); + return; + } + LogDNSSEC("ValidateWithNSECS: Parent set, Verifying dv %p %##s (%s)", dv, pdv->q.qname.c, DNSTypeName(pdv->q.qtype)); + dv->parent = pdv; + } + } + if (cr->resrec.RecordType == kDNSRecordTypePacketNegative) + { + CacheRecord *neg = cr->nsec; + while (neg) + { + LogDNSSEC("ValidateWithNSECS: NSECCached Record %s", CRDisplayString(m, neg)); + neg = neg->next; + } + + if (cr->rcode == kDNSFlag1_RC_NoErr) + { + NoDataProof(m, dv, cr); + } + else if (cr->rcode == kDNSFlag1_RC_NXDomain) + { + NameErrorProof(m, dv, cr); + } + else + { + LogDNSSEC("ValidateWithNSECS: Rcode %d invalid", cr->rcode); + dv->DVCallback(m, dv, DNSSEC_Insecure); + } + } + else + { + LogMsg("ValidateWithNSECS: Not a valid cache record %s for NSEC proofs", CRDisplayString(m, cr)); + dv->DVCallback(m, dv, DNSSEC_Insecure); + return; + } +} diff --git a/mDNSCore/nsec.h b/mDNSCore/nsec.h new file mode 100644 index 0000000..b85e103 --- /dev/null +++ b/mDNSCore/nsec.h @@ -0,0 +1,28 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __NSEC_H +#define __NSEC_H + +#include "dnssec.h" + +extern mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode); +extern mDNSBool NSECAnswersENT(const ResourceRecord *const rr, domainname *name); +extern void WildcardAnswerProof(mDNS *const m, DNSSECVerifier *dv); +extern void ValidateWithNSECS(mDNS *const m, DNSSECVerifier *dv, CacheRecord *rr); +mDNSexport mDNSBool NSECAnswersDS(mDNS *const m, ResourceRecord *rr, DNSQuestion *q); + +#endif // __NSEC_H diff --git a/mDNSCore/uDNS.c b/mDNSCore/uDNS.c index 57cfc1a..040a1c9 100755 --- a/mDNSCore/uDNS.c +++ b/mDNSCore/uDNS.c @@ -1,13 +1,13 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2012 Apple Computer, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,13 +25,13 @@ #endif #include "uDNS.h" -#if(defined(_MSC_VER)) - // Disable "assignment within conditional expression". - // Other compilers understand the convention that if you place the assignment expression within an extra pair - // of parentheses, this signals to the compiler that you really intended an assignment and no warning is necessary. - // The Microsoft compiler doesn't understand this convention, so in the absense of any other way to signal - // to the compiler that the assignment is intentional, we have to just turn this warning off completely. - #pragma warning(disable:4706) +#if (defined(_MSC_VER)) +// Disable "assignment within conditional expression". +// Other compilers understand the convention that if you place the assignment expression within an extra pair +// of parentheses, this signals to the compiler that you really intended an assignment and no warning is necessary. +// The Microsoft compiler doesn't understand this convention, so in the absense of any other way to signal +// to the compiler that the assignment is intentional, we have to just turn this warning off completely. + #pragma warning(disable:4706) #endif // For domain enumeration and automatic browsing @@ -50,7 +50,12 @@ mDNSBool StrictUnicastOrdering = mDNSfalse; // the servers exactly once before giving up. If we could allocate memory in the core, then // arbitrary limitation of 64 DNSServers can be removed. mDNSu8 NumUnicastDNSServers = 0; -#define MAX_UNICAST_DNS_SERVERS 64 +#define MAX_UNICAST_DNS_SERVERS 64 + +#define SetNextuDNSEvent(m, rr) { \ + if ((m)->NextuDNSEvent - ((rr)->LastAPTime + (rr)->ThisAPInterval) >= 0) \ + (m)->NextuDNSEvent = ((rr)->LastAPTime + (rr)->ThisAPInterval); \ +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -59,224 +64,220 @@ mDNSu8 NumUnicastDNSServers = 0; // set retry timestamp for record with exponential backoff mDNSlocal void SetRecordRetry(mDNS *const m, AuthRecord *rr, mDNSu32 random) - { - rr->LastAPTime = m->timenow; - - if (rr->expire && rr->refreshCount < MAX_UPDATE_REFRESH_COUNT) - { - mDNSs32 remaining = rr->expire - m->timenow; - rr->refreshCount++; - if (remaining > MIN_UPDATE_REFRESH_TIME) - { - // Refresh at 70% + random (currently it is 0 to 10%) - rr->ThisAPInterval = 7 * (remaining/10) + (random ? random : mDNSRandom(remaining/10)); - // Don't update more often than 5 minutes - if (rr->ThisAPInterval < MIN_UPDATE_REFRESH_TIME) - rr->ThisAPInterval = MIN_UPDATE_REFRESH_TIME; - LogInfo("SetRecordRetry refresh in %d of %d for %s", - rr->ThisAPInterval/mDNSPlatformOneSecond, (rr->expire - m->timenow)/mDNSPlatformOneSecond, ARDisplayString(m, rr)); - } - else - { - rr->ThisAPInterval = MIN_UPDATE_REFRESH_TIME; - LogInfo("SetRecordRetry clamping to min refresh in %d of %d for %s", - rr->ThisAPInterval/mDNSPlatformOneSecond, (rr->expire - m->timenow)/mDNSPlatformOneSecond, ARDisplayString(m, rr)); - } - return; - } - - rr->expire = 0; - - rr->ThisAPInterval = rr->ThisAPInterval * QuestionIntervalStep; // Same Retry logic as Unicast Queries - if (rr->ThisAPInterval < INIT_RECORD_REG_INTERVAL) - rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; - if (rr->ThisAPInterval > MAX_RECORD_REG_INTERVAL) - rr->ThisAPInterval = MAX_RECORD_REG_INTERVAL; - - LogInfo("SetRecordRetry retry in %d ms for %s", rr->ThisAPInterval, ARDisplayString(m, rr)); - } +{ + rr->LastAPTime = m->timenow; + + if (rr->expire && rr->refreshCount < MAX_UPDATE_REFRESH_COUNT) + { + mDNSs32 remaining = rr->expire - m->timenow; + rr->refreshCount++; + if (remaining > MIN_UPDATE_REFRESH_TIME) + { + // Refresh at 70% + random (currently it is 0 to 10%) + rr->ThisAPInterval = 7 * (remaining/10) + (random ? random : mDNSRandom(remaining/10)); + // Don't update more often than 5 minutes + if (rr->ThisAPInterval < MIN_UPDATE_REFRESH_TIME) + rr->ThisAPInterval = MIN_UPDATE_REFRESH_TIME; + LogInfo("SetRecordRetry refresh in %d of %d for %s", + rr->ThisAPInterval/mDNSPlatformOneSecond, (rr->expire - m->timenow)/mDNSPlatformOneSecond, ARDisplayString(m, rr)); + } + else + { + rr->ThisAPInterval = MIN_UPDATE_REFRESH_TIME; + LogInfo("SetRecordRetry clamping to min refresh in %d of %d for %s", + rr->ThisAPInterval/mDNSPlatformOneSecond, (rr->expire - m->timenow)/mDNSPlatformOneSecond, ARDisplayString(m, rr)); + } + return; + } + + rr->expire = 0; + + rr->ThisAPInterval = rr->ThisAPInterval * QuestionIntervalStep; // Same Retry logic as Unicast Queries + if (rr->ThisAPInterval < INIT_RECORD_REG_INTERVAL) + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + if (rr->ThisAPInterval > MAX_RECORD_REG_INTERVAL) + rr->ThisAPInterval = MAX_RECORD_REG_INTERVAL; + + LogInfo("SetRecordRetry retry in %d ms for %s", rr->ThisAPInterval, ARDisplayString(m, rr)); +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - Name Server List Management #endif -mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout, mDNSBool cellIntf) - { - DNSServer **p = &m->DNSServers; - DNSServer *tmp = mDNSNULL; - - if ((NumUnicastDNSServers + 1) > MAX_UNICAST_DNS_SERVERS) - { - LogMsg("mDNS_AddDNSServer: DNS server limit of %d reached, not adding this server", MAX_UNICAST_DNS_SERVERS); - return mDNSNULL; - } - - if (!d) d = (const domainname *)""; - - LogInfo("mDNS_AddDNSServer: Adding %#a for %##s, InterfaceID %p, scoped %d", addr, d->c, interface, scoped); - if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("mDNS_AddDNSServer: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); - - while (*p) // Check if we already have this {interface,address,port,domain} tuple registered - { - if ((*p)->scoped == scoped && (*p)->interface == interface && (*p)->teststate != DNSServer_Disabled && - mDNSSameAddress(&(*p)->addr, addr) && mDNSSameIPPort((*p)->port, port) && SameDomainName(&(*p)->domain, d)) - { - if (!((*p)->flags & DNSServer_FlagDelete)) debugf("Note: DNS Server %#a:%d for domain %##s (%p) registered more than once", addr, mDNSVal16(port), d->c, interface); - (*p)->flags &= ~DNSServer_FlagDelete; - tmp = *p; - *p = tmp->next; - tmp->next = mDNSNULL; - } - else - p=&(*p)->next; - } - - if (tmp) *p = tmp; // move to end of list, to ensure ordering from platform layer - else - { - // allocate, add to list - *p = mDNSPlatformMemAllocate(sizeof(**p)); - if (!*p) LogMsg("Error: mDNS_AddDNSServer - malloc"); - else - { - NumUnicastDNSServers++; - (*p)->scoped = scoped; - (*p)->interface = interface; - (*p)->addr = *addr; - (*p)->port = port; - (*p)->flags = DNSServer_FlagNew; - (*p)->teststate = /* DNSServer_Untested */ DNSServer_Passed; - (*p)->lasttest = m->timenow - INIT_UCAST_POLL_INTERVAL; - (*p)->timeout = timeout; - (*p)->cellIntf = cellIntf; - AssignDomainName(&(*p)->domain, d); - (*p)->next = mDNSNULL; - } - } - (*p)->penaltyTime = 0; - return(*p); - } +mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID) +{ + DNSServer **p = &m->DNSServers; + DNSServer *tmp = mDNSNULL; + + if ((NumUnicastDNSServers + 1) > MAX_UNICAST_DNS_SERVERS) + { + LogMsg("mDNS_AddDNSServer: DNS server limit of %d reached, not adding this server", MAX_UNICAST_DNS_SERVERS); + return mDNSNULL; + } + + if (!d) d = (const domainname *)""; + + LogInfo("mDNS_AddDNSServer: Adding %#a for %##s, InterfaceID %p, scoped %d, resGroupID %d", addr, d->c, interface, scoped, resGroupID); + + if (m->mDNS_busy != m->mDNS_reentrancy+1) + LogMsg("mDNS_AddDNSServer: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + + while (*p) // Check if we already have this {interface,address,port,domain} tuple registered + { + if ((*p)->scoped == scoped && (*p)->interface == interface && (*p)->teststate != DNSServer_Disabled && + mDNSSameAddress(&(*p)->addr, addr) && mDNSSameIPPort((*p)->port, port) && SameDomainName(&(*p)->domain, d)) + { + if (!((*p)->flags & DNSServer_FlagDelete)) debugf("Note: DNS Server %#a:%d for domain %##s (%p) registered more than once", addr, mDNSVal16(port), d->c, interface); + (*p)->flags &= ~DNSServer_FlagDelete; + tmp = *p; + *p = tmp->next; + tmp->next = mDNSNULL; + } + else + p=&(*p)->next; + } + + if (tmp) *p = tmp; // move to end of list, to ensure ordering from platform layer + else + { + // allocate, add to list + *p = mDNSPlatformMemAllocate(sizeof(**p)); + if (!*p) LogMsg("Error: mDNS_AddDNSServer - malloc"); + else + { + NumUnicastDNSServers++; + (*p)->scoped = scoped; + (*p)->interface = interface; + (*p)->addr = *addr; + (*p)->port = port; + (*p)->flags = DNSServer_FlagNew; + (*p)->teststate = /* DNSServer_Untested */ DNSServer_Passed; + (*p)->lasttest = m->timenow - INIT_UCAST_POLL_INTERVAL; + (*p)->timeout = timeout; + (*p)->cellIntf = cellIntf; + AssignDomainName(&(*p)->domain, d); + (*p)->next = mDNSNULL; + } + } + (*p)->penaltyTime = 0; + // We always update the ID (not just when we allocate a new instance) because we could + // be adding a new non-scoped resolver with a new ID and we want all the non-scoped + // resolvers belong to the same group. + (*p)->resGroupID = resGroupID; + return(*p); +} // PenalizeDNSServer is called when the number of queries to the unicast // DNS server exceeds MAX_UCAST_UNANSWERED_QUERIES or when we receive an // error e.g., SERV_FAIL from DNS server. mDNSexport void PenalizeDNSServer(mDNS *const m, DNSQuestion *q) - { - DNSServer *new; - DNSServer *orig = q->qDNSServer; - - if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("PenalizeDNSServer: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); - - // This should never happen. Whenever we change DNS server, we change the ID on the question and hence - // we should never accept a response after we penalize a DNS server e.g., send two queries, no response, - // penalize DNS server and no new servers to pick for the question and hence qDNSServer is NULL. If we - // receive a response now, the DNS server can be NULL. But we won't because the ID already has been - // changed. - if (!q->qDNSServer) - { - LogMsg("PenalizeDNSServer: ERROR!! Null DNS server for %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), q->unansweredQueries); - goto end; - } - - LogInfo("PenalizeDNSServer: Penalizing DNS server %#a:%d question (%##s) for question %p %##s (%s) SuppressUnusable %d", - &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qDNSServer->domain.c, q, q->qname.c, DNSTypeName(q->qtype), - q->SuppressUnusable); - - // If strict ordering of unicast servers needs to be preserved, we just lookup - // the next best match server below - // - // If strict ordering is not required which is the default behavior, we penalize the server - // for DNSSERVER_PENALTY_TIME. We may also use additional logic e.g., don't penalize for PTR - // in the future. - - if (!StrictUnicastOrdering) - { - LogInfo("PenalizeDNSServer: Strict Unicast Ordering is FALSE"); - // We penalize the server so that new queries don't pick this server for DNSSERVER_PENALTY_TIME - // XXX Include other logic here to see if this server should really be penalized - // - if (q->qtype == kDNSType_PTR) - { - LogInfo("PenalizeDNSServer: Not Penalizing PTR question"); - } - else - { - LogInfo("PenalizeDNSServer: Penalizing question type %d", q->qtype); - q->qDNSServer->penaltyTime = NonZeroTime(m->timenow + DNSSERVER_PENALTY_TIME); - } - } - else - { - LogInfo("PenalizeDNSServer: Strict Unicast Ordering is TRUE"); - } +{ + DNSServer *new; + DNSServer *orig = q->qDNSServer; + + if (m->mDNS_busy != m->mDNS_reentrancy+1) + LogMsg("PenalizeDNSServer: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + + LogInfo("PenalizeDNSServer: Penalizing DNS server %#a question for question %p %##s (%s) SuppressUnusable %d", + (q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL), q, q->qname.c, DNSTypeName(q->qtype), q->SuppressUnusable); + + // After we reset the qDNSServer to NULL, we could get more SERV_FAILS that might end up + // peanlizing again. + if (!q->qDNSServer) goto end; + + // If strict ordering of unicast servers needs to be preserved, we just lookup + // the next best match server below + // + // If strict ordering is not required which is the default behavior, we penalize the server + // for DNSSERVER_PENALTY_TIME. We may also use additional logic e.g., don't penalize for PTR + // in the future. + + if (!StrictUnicastOrdering) + { + LogInfo("PenalizeDNSServer: Strict Unicast Ordering is FALSE"); + // We penalize the server so that new queries don't pick this server for DNSSERVER_PENALTY_TIME + // XXX Include other logic here to see if this server should really be penalized + // + if (q->qtype == kDNSType_PTR) + { + LogInfo("PenalizeDNSServer: Not Penalizing PTR question"); + } + else + { + LogInfo("PenalizeDNSServer: Penalizing question type %d", q->qtype); + q->qDNSServer->penaltyTime = NonZeroTime(m->timenow + DNSSERVER_PENALTY_TIME); + } + } + else + { + LogInfo("PenalizeDNSServer: Strict Unicast Ordering is TRUE"); + } end: - new = GetServerForQuestion(m, q); - - - if (new == orig) - { - if (new) - LogMsg("PenalizeDNSServer: ERROR!! GetServerForQuestion returned the same server %#a:%d", &new->addr, - mDNSVal16(new->port)); - else - LogMsg("PenalizeDNSServer: ERROR!! GetServerForQuestion returned the same server NULL"); - q->ThisQInterval = 0; // Inactivate this question so that we dont bombard the network - } - else - { - // The new DNSServer is set in DNSServerChangeForQuestion - DNSServerChangeForQuestion(m, q, new); - - if (new) - { - LogInfo("PenalizeDNSServer: Server for %##s (%s) changed to %#a:%d (%##s)", - q->qname.c, DNSTypeName(q->qtype), &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qDNSServer->domain.c); - // We want to try the next server immediately. As the question may already have backed off, reset - // the interval. We do this only the first time when we try all the DNS servers. Once we reached the end of - // list and retrying all the servers again e.g., at least one server failed to respond in the previous try, we - // use the normal backoff which is done in uDNS_CheckCurrentQuestion when we send the packet out. - if (!q->triedAllServersOnce) - { - q->ThisQInterval = InitialQuestionInterval; - q->LastQTime = m->timenow - q->ThisQInterval; - SetNextQueryTime(m, q); - } - } - else - { - // We don't have any more DNS servers for this question. If some server in the list did not return - // any response, we need to keep retrying till we get a response. uDNS_CheckCurrentQuestion handles - // this case. - // - // If all servers responded with a negative response, We need to do two things. First, generate a - // negative response so that applications get a reply. We also need to reinitialize the DNS servers - // so that when the cache expires, we can restart the query. - // - // Negative response may be generated in two ways. - // - // 1. AnswerQuestionForDNSServerChanges (called from DNSServerChangedForQuestion) might find some - // cache entries and answer this question. - // 2. uDNS_CheckCurrentQuestion will create a new cache entry and answer this question - // - // For (1), it might be okay to reinitialize the DNS servers here. But for (2), we can't do it here - // because uDNS_CheckCurrentQuestion will try resending the queries. Hence, to be consistent, we - // defer reintializing the DNS servers up until generating a negative cache response. - // - // Be careful not to touch the ThisQInterval here. For a normal question, when we answer the question - // in AnswerCurrentQuestionWithResourceRecord will set ThisQInterval to MaxQuestionInterval and hence - // the next query will not happen until cache expiry. If it is a long lived question, - // AnswerCurrentQuestionWithResourceRecord will not set it to MaxQuestionInterval. In that case, - // we want the normal backoff to work. - LogInfo("PenalizeDNSServer: Server for %p, %##s (%s) changed to NULL, Interval %d", q, q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval); - } - q->unansweredQueries = 0; - - } - } + new = GetServerForQuestion(m, q); + + if (new == orig) + { + if (new) + { + LogMsg("PenalizeDNSServer: ERROR!! GetServerForQuestion returned the same server %#a:%d", &new->addr, + mDNSVal16(new->port)); + q->ThisQInterval = 0; // Inactivate this question so that we dont bombard the network + } + else + { + // When we have no more DNS servers, we might end up calling PenalizeDNSServer multiple + // times when we receive SERVFAIL from delayed packets in the network e.g., DNS server + // is slow in responding and we have sent three queries. When we repeatedly call, it is + // okay to receive the same NULL DNS server. Next time we try to send the query, we will + // realize and re-initialize the DNS servers. + LogInfo("PenalizeDNSServer: GetServerForQuestion returned the same server NULL"); + } + } + else + { + // The new DNSServer is set in DNSServerChangeForQuestion + DNSServerChangeForQuestion(m, q, new); + + if (new) + { + LogInfo("PenalizeDNSServer: Server for %##s (%s) changed to %#a:%d (%##s)", + q->qname.c, DNSTypeName(q->qtype), &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qDNSServer->domain.c); + // We want to try the next server immediately. As the question may already have backed off, reset + // the interval. We do this only the first time when we try all the DNS servers. Once we reached the end of + // list and retrying all the servers again e.g., at least one server failed to respond in the previous try, we + // use the normal backoff which is done in uDNS_CheckCurrentQuestion when we send the packet out. + if (!q->triedAllServersOnce) + { + q->ThisQInterval = InitialQuestionInterval; + q->LastQTime = m->timenow - q->ThisQInterval; + SetNextQueryTime(m, q); + } + } + else + { + // We don't have any more DNS servers for this question. If some server in the list did not return + // any response, we need to keep retrying till we get a response. uDNS_CheckCurrentQuestion handles + // this case. + // + // If all servers responded with a negative response, We need to do two things. First, generate a + // negative response so that applications get a reply. We also need to reinitialize the DNS servers + // so that when the cache expires, we can restart the query. We defer this up until we generate + // a negative cache response in uDNS_CheckCurrentQuestion. + // + // Be careful not to touch the ThisQInterval here. For a normal question, when we answer the question + // in AnswerCurrentQuestionWithResourceRecord will set ThisQInterval to MaxQuestionInterval and hence + // the next query will not happen until cache expiry. If it is a long lived question, + // AnswerCurrentQuestionWithResourceRecord will not set it to MaxQuestionInterval. In that case, + // we want the normal backoff to work. + LogInfo("PenalizeDNSServer: Server for %p, %##s (%s) changed to NULL, Interval %d", q, q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval); + } + q->unansweredQueries = 0; + + } +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -284,132 +285,133 @@ end: #endif mDNSlocal DomainAuthInfo *GetAuthInfoForName_direct(mDNS *m, const domainname *const name) - { - const domainname *n = name; - while (n->c[0]) - { - DomainAuthInfo *ptr; - for (ptr = m->AuthInfoList; ptr; ptr = ptr->next) - if (SameDomainName(&ptr->domain, n)) - { - debugf("GetAuthInfoForName %##s Matched %##s Key name %##s", name->c, ptr->domain.c, ptr->keyname.c); - return(ptr); - } - n = (const domainname *)(n->c + 1 + n->c[0]); - } - //LogInfo("GetAuthInfoForName none found for %##s", name->c); - return mDNSNULL; - } +{ + const domainname *n = name; + while (n->c[0]) + { + DomainAuthInfo *ptr; + for (ptr = m->AuthInfoList; ptr; ptr = ptr->next) + if (SameDomainName(&ptr->domain, n)) + { + debugf("GetAuthInfoForName %##s Matched %##s Key name %##s", name->c, ptr->domain.c, ptr->keyname.c); + return(ptr); + } + n = (const domainname *)(n->c + 1 + n->c[0]); + } + //LogInfo("GetAuthInfoForName none found for %##s", name->c); + return mDNSNULL; +} // MUST be called with lock held mDNSexport DomainAuthInfo *GetAuthInfoForName_internal(mDNS *m, const domainname *const name) - { - DomainAuthInfo **p = &m->AuthInfoList; - - if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("GetAuthInfoForName_internal: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); - - // First purge any dead keys from the list - while (*p) - { - if ((*p)->deltime && m->timenow - (*p)->deltime >= 0 && AutoTunnelUnregistered(*p)) - { - DNSQuestion *q; - DomainAuthInfo *info = *p; - LogInfo("GetAuthInfoForName_internal deleting expired key %##s %##s", info->domain.c, info->keyname.c); - *p = info->next; // Cut DomainAuthInfo from list *before* scanning our question list updating AuthInfo pointers - for (q = m->Questions; q; q=q->next) - if (q->AuthInfo == info) - { - q->AuthInfo = GetAuthInfoForName_direct(m, &q->qname); - debugf("GetAuthInfoForName_internal updated q->AuthInfo from %##s to %##s for %##s (%s)", - info->domain.c, q->AuthInfo ? q->AuthInfo->domain.c : mDNSNULL, q->qname.c, DNSTypeName(q->qtype)); - } - - // Probably not essential, but just to be safe, zero out the secret key data - // so we don't leave it hanging around in memory - // (where it could potentially get exposed via some other bug) - mDNSPlatformMemZero(info, sizeof(*info)); - mDNSPlatformMemFree(info); - } - else - p = &(*p)->next; - } - - return(GetAuthInfoForName_direct(m, name)); - } +{ + DomainAuthInfo **p = &m->AuthInfoList; + + if (m->mDNS_busy != m->mDNS_reentrancy+1) + LogMsg("GetAuthInfoForName_internal: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + + // First purge any dead keys from the list + while (*p) + { + if ((*p)->deltime && m->timenow - (*p)->deltime >= 0 && AutoTunnelUnregistered(*p)) + { + DNSQuestion *q; + DomainAuthInfo *info = *p; + LogInfo("GetAuthInfoForName_internal deleting expired key %##s %##s", info->domain.c, info->keyname.c); + *p = info->next; // Cut DomainAuthInfo from list *before* scanning our question list updating AuthInfo pointers + for (q = m->Questions; q; q=q->next) + if (q->AuthInfo == info) + { + q->AuthInfo = GetAuthInfoForName_direct(m, &q->qname); + debugf("GetAuthInfoForName_internal updated q->AuthInfo from %##s to %##s for %##s (%s)", + info->domain.c, q->AuthInfo ? q->AuthInfo->domain.c : mDNSNULL, q->qname.c, DNSTypeName(q->qtype)); + } + + // Probably not essential, but just to be safe, zero out the secret key data + // so we don't leave it hanging around in memory + // (where it could potentially get exposed via some other bug) + mDNSPlatformMemZero(info, sizeof(*info)); + mDNSPlatformMemFree(info); + } + else + p = &(*p)->next; + } + + return(GetAuthInfoForName_direct(m, name)); +} mDNSexport DomainAuthInfo *GetAuthInfoForName(mDNS *m, const domainname *const name) - { - DomainAuthInfo *d; - mDNS_Lock(m); - d = GetAuthInfoForName_internal(m, name); - mDNS_Unlock(m); - return(d); - } +{ + DomainAuthInfo *d; + mDNS_Lock(m); + d = GetAuthInfoForName_internal(m, name); + mDNS_Unlock(m); + return(d); +} // MUST be called with the lock held mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, - const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, const char *autoTunnelPrefix) - { - DNSQuestion *q; - DomainAuthInfo **p = &m->AuthInfoList; - if (!info || !b64keydata) { LogMsg("mDNS_SetSecretForDomain: ERROR: info %p b64keydata %p", info, b64keydata); return(mStatus_BadParamErr); } - - LogInfo("mDNS_SetSecretForDomain: domain %##s key %##s%s%s", domain->c, keyname->c, autoTunnelPrefix ? " prefix " : "", autoTunnelPrefix ? autoTunnelPrefix : ""); - - info->AutoTunnel = autoTunnelPrefix; - AssignDomainName(&info->domain, domain); - AssignDomainName(&info->keyname, keyname); - if (hostname) - AssignDomainName(&info->hostname, hostname); - else - info->hostname.c[0] = 0; - if (port) - info->port = *port; - else - info->port = zeroIPPort; - mDNS_snprintf(info->b64keydata, sizeof(info->b64keydata), "%s", b64keydata); - - if (DNSDigest_ConstructHMACKeyfromBase64(info, b64keydata) < 0) - { - LogMsg("mDNS_SetSecretForDomain: ERROR: Could not convert shared secret from base64: domain %##s key %##s %s", domain->c, keyname->c, mDNS_LoggingEnabled ? b64keydata : ""); - return(mStatus_BadParamErr); - } - - // Don't clear deltime until after we've ascertained that b64keydata is valid - info->deltime = 0; - - while (*p && (*p) != info) p=&(*p)->next; - if (*p) {LogInfo("mDNS_SetSecretForDomain: Domain %##s Already in list", (*p)->domain.c); return(mStatus_AlreadyRegistered);} - - // Caution: Only zero AutoTunnelHostRecord.namestorage and AutoTunnelNAT.clientContext AFTER we've determined that this is a NEW DomainAuthInfo - // being added to the list. Otherwise we risk smashing our AutoTunnel host records and NATOperation that are already active and in use. - info->AutoTunnelHostRecord.resrec.RecordType = kDNSRecordTypeUnregistered; - info->AutoTunnelHostRecord.namestorage.c[0] = 0; - info->AutoTunnelTarget .resrec.RecordType = kDNSRecordTypeUnregistered; - info->AutoTunnelDeviceInfo.resrec.RecordType = kDNSRecordTypeUnregistered; - info->AutoTunnelService .resrec.RecordType = kDNSRecordTypeUnregistered; - info->AutoTunnel6Record .resrec.RecordType = kDNSRecordTypeUnregistered; - info->AutoTunnelNAT.clientContext = mDNSNULL; - info->next = mDNSNULL; - *p = info; - - // Check to see if adding this new DomainAuthInfo has changed the credentials for any of our questions - for (q = m->Questions; q; q=q->next) - { - DomainAuthInfo *newinfo = GetAuthInfoForQuestion(m, q); - if (q->AuthInfo != newinfo) - { - debugf("mDNS_SetSecretForDomain updating q->AuthInfo from %##s to %##s for %##s (%s)", - q->AuthInfo ? q->AuthInfo->domain.c : mDNSNULL, - newinfo ? newinfo ->domain.c : mDNSNULL, q->qname.c, DNSTypeName(q->qtype)); - q->AuthInfo = newinfo; - } - } - - return(mStatus_NoError); - } + const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, mDNSBool autoTunnel) +{ + DNSQuestion *q; + DomainAuthInfo **p = &m->AuthInfoList; + if (!info || !b64keydata) { LogMsg("mDNS_SetSecretForDomain: ERROR: info %p b64keydata %p", info, b64keydata); return(mStatus_BadParamErr); } + + LogInfo("mDNS_SetSecretForDomain: domain %##s key %##s%s", domain->c, keyname->c, autoTunnel ? " AutoTunnel" : ""); + + info->AutoTunnel = autoTunnel; + AssignDomainName(&info->domain, domain); + AssignDomainName(&info->keyname, keyname); + if (hostname) + AssignDomainName(&info->hostname, hostname); + else + info->hostname.c[0] = 0; + if (port) + info->port = *port; + else + info->port = zeroIPPort; + mDNS_snprintf(info->b64keydata, sizeof(info->b64keydata), "%s", b64keydata); + + if (DNSDigest_ConstructHMACKeyfromBase64(info, b64keydata) < 0) + { + LogMsg("mDNS_SetSecretForDomain: ERROR: Could not convert shared secret from base64: domain %##s key %##s %s", domain->c, keyname->c, mDNS_LoggingEnabled ? b64keydata : ""); + return(mStatus_BadParamErr); + } + + // Don't clear deltime until after we've ascertained that b64keydata is valid + info->deltime = 0; + + while (*p && (*p) != info) p=&(*p)->next; + if (*p) {LogInfo("mDNS_SetSecretForDomain: Domain %##s Already in list", (*p)->domain.c); return(mStatus_AlreadyRegistered);} + + // Caution: Only zero AutoTunnelHostRecord.namestorage AFTER we've determined that this is a NEW DomainAuthInfo + // being added to the list. Otherwise we risk smashing our AutoTunnel host records that are already active and in use. + info->AutoTunnelHostRecord.resrec.RecordType = kDNSRecordTypeUnregistered; + info->AutoTunnelHostRecord.namestorage.c[0] = 0; + info->AutoTunnelTarget.resrec.RecordType = kDNSRecordTypeUnregistered; + info->AutoTunnelDeviceInfo.resrec.RecordType = kDNSRecordTypeUnregistered; + info->AutoTunnelService.resrec.RecordType = kDNSRecordTypeUnregistered; + info->AutoTunnel6Record.resrec.RecordType = kDNSRecordTypeUnregistered; + info->AutoTunnelServiceStarted = mDNSfalse; + info->AutoTunnelInnerAddress = zerov6Addr; + info->next = mDNSNULL; + *p = info; + + // Check to see if adding this new DomainAuthInfo has changed the credentials for any of our questions + for (q = m->Questions; q; q=q->next) + { + DomainAuthInfo *newinfo = GetAuthInfoForQuestion(m, q); + if (q->AuthInfo != newinfo) + { + debugf("mDNS_SetSecretForDomain updating q->AuthInfo from %##s to %##s for %##s (%s)", + q->AuthInfo ? q->AuthInfo->domain.c : mDNSNULL, + newinfo ? newinfo->domain.c : mDNSNULL, q->qname.c, DNSTypeName(q->qtype)); + q->AuthInfo = newinfo; + } + } + + return(mStatus_NoError); +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -418,266 +420,266 @@ mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, #endif mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info) - { - mStatus err = mStatus_NoError; - - // send msg if we have a router and it is a private address - if (!mDNSIPv4AddressIsZero(m->Router.ip.v4) && mDNSv4AddrIsRFC1918(&m->Router.ip.v4)) - { - union { NATAddrRequest NATAddrReq; NATPortMapRequest NATPortReq; } u = { { NATMAP_VERS, NATOp_AddrRequest } } ; - const mDNSu8 *end = (mDNSu8 *)&u + sizeof(NATAddrRequest); - - if (info) // For NATOp_MapUDP and NATOp_MapTCP, fill in additional fields - { - mDNSu8 *p = (mDNSu8 *)&u.NATPortReq.NATReq_lease; - u.NATPortReq.opcode = info->Protocol; - u.NATPortReq.unused = zeroID; - u.NATPortReq.intport = info->IntPort; - u.NATPortReq.extport = info->RequestedPort; - p[0] = (mDNSu8)((info->NATLease >> 24) & 0xFF); - p[1] = (mDNSu8)((info->NATLease >> 16) & 0xFF); - p[2] = (mDNSu8)((info->NATLease >> 8) & 0xFF); - p[3] = (mDNSu8)( info->NATLease & 0xFF); - end = (mDNSu8 *)&u + sizeof(NATPortMapRequest); - } - - err = mDNSPlatformSendUDP(m, (mDNSu8 *)&u, end, 0, mDNSNULL, &m->Router, NATPMPPort); +{ + mStatus err = mStatus_NoError; + + // send msg if we have a router and it is a private address + if (!mDNSIPv4AddressIsZero(m->Router.ip.v4) && mDNSv4AddrIsRFC1918(&m->Router.ip.v4)) + { + union { NATAddrRequest NATAddrReq; NATPortMapRequest NATPortReq; } u = { { NATMAP_VERS, NATOp_AddrRequest } } ; + const mDNSu8 *end = (mDNSu8 *)&u + sizeof(NATAddrRequest); + + if (info) // For NATOp_MapUDP and NATOp_MapTCP, fill in additional fields + { + mDNSu8 *p = (mDNSu8 *)&u.NATPortReq.NATReq_lease; + u.NATPortReq.opcode = info->Protocol; + u.NATPortReq.unused = zeroID; + u.NATPortReq.intport = info->IntPort; + u.NATPortReq.extport = info->RequestedPort; + p[0] = (mDNSu8)((info->NATLease >> 24) & 0xFF); + p[1] = (mDNSu8)((info->NATLease >> 16) & 0xFF); + p[2] = (mDNSu8)((info->NATLease >> 8) & 0xFF); + p[3] = (mDNSu8)( info->NATLease & 0xFF); + end = (mDNSu8 *)&u + sizeof(NATPortMapRequest); + } + + err = mDNSPlatformSendUDP(m, (mDNSu8 *)&u, end, 0, mDNSNULL, &m->Router, NATPMPPort, mDNSfalse); #ifdef _LEGACY_NAT_TRAVERSAL_ - if (mDNSIPPortIsZero(m->UPnPRouterPort) || mDNSIPPortIsZero(m->UPnPSOAPPort)) LNT_SendDiscoveryMsg(m); - else if (info) err = LNT_MapPort(m, info); - else err = LNT_GetExternalAddress(m); + if (mDNSIPPortIsZero(m->UPnPRouterPort) || mDNSIPPortIsZero(m->UPnPSOAPPort)) LNT_SendDiscoveryMsg(m); + else if (info) err = LNT_MapPort(m, info); + else err = LNT_GetExternalAddress(m); #endif // _LEGACY_NAT_TRAVERSAL_ - } - return(err); - } + } + return(err); +} mDNSexport void RecreateNATMappings(mDNS *const m) - { - NATTraversalInfo *n; - for (n = m->NATTraversals; n; n=n->next) - { - n->ExpiryTime = 0; // Mark this mapping as expired - n->retryInterval = NATMAP_INIT_RETRY; - n->retryPortMap = m->timenow; +{ + NATTraversalInfo *n; + for (n = m->NATTraversals; n; n=n->next) + { + n->ExpiryTime = 0; // Mark this mapping as expired + n->retryInterval = NATMAP_INIT_RETRY; + n->retryPortMap = m->timenow; #ifdef _LEGACY_NAT_TRAVERSAL_ - if (n->tcpInfo.sock) { mDNSPlatformTCPCloseConnection(n->tcpInfo.sock); n->tcpInfo.sock = mDNSNULL; } + if (n->tcpInfo.sock) { mDNSPlatformTCPCloseConnection(n->tcpInfo.sock); n->tcpInfo.sock = mDNSNULL; } #endif // _LEGACY_NAT_TRAVERSAL_ - } + } - m->NextScheduledNATOp = m->timenow; // Need to send packets immediately - } + m->NextScheduledNATOp = m->timenow; // Need to send packets immediately +} mDNSexport void natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv4Addr ExtAddr) - { - static mDNSu16 last_err = 0; - - if (err) - { - if (err != last_err) LogMsg("Error getting external address %d", err); - ExtAddr = zerov4Addr; - } - else - { - LogInfo("Received external IP address %.4a from NAT", &ExtAddr); - if (mDNSv4AddrIsRFC1918(&ExtAddr)) - LogMsg("Double NAT (external NAT gateway address %.4a is also a private RFC 1918 address)", &ExtAddr); - if (mDNSIPv4AddressIsZero(ExtAddr)) - err = NATErr_NetFail; // fake error to handle routers that pathologically report success with the zero address - } - - if (!mDNSSameIPv4Address(m->ExternalAddress, ExtAddr)) - { - m->ExternalAddress = ExtAddr; - RecreateNATMappings(m); // Also sets NextScheduledNATOp for us - } - - if (!err) // Success, back-off to maximum interval - m->retryIntervalGetAddr = NATMAP_MAX_RETRY_INTERVAL; - else if (!last_err) // Failure after success, retry quickly (then back-off exponentially) - m->retryIntervalGetAddr = NATMAP_INIT_RETRY; - // else back-off normally in case of pathological failures - - m->retryGetAddr = m->timenow + m->retryIntervalGetAddr; - if (m->NextScheduledNATOp - m->retryIntervalGetAddr > 0) - m->NextScheduledNATOp = m->retryIntervalGetAddr; - - last_err = err; - } +{ + static mDNSu16 last_err = 0; + + if (err) + { + if (err != last_err) LogMsg("Error getting external address %d", err); + ExtAddr = zerov4Addr; + } + else + { + LogInfo("Received external IP address %.4a from NAT", &ExtAddr); + if (mDNSv4AddrIsRFC1918(&ExtAddr)) + LogMsg("Double NAT (external NAT gateway address %.4a is also a private RFC 1918 address)", &ExtAddr); + if (mDNSIPv4AddressIsZero(ExtAddr)) + err = NATErr_NetFail; // fake error to handle routers that pathologically report success with the zero address + } + + if (!mDNSSameIPv4Address(m->ExternalAddress, ExtAddr)) + { + m->ExternalAddress = ExtAddr; + RecreateNATMappings(m); // Also sets NextScheduledNATOp for us + } + + if (!err) // Success, back-off to maximum interval + m->retryIntervalGetAddr = NATMAP_MAX_RETRY_INTERVAL; + else if (!last_err) // Failure after success, retry quickly (then back-off exponentially) + m->retryIntervalGetAddr = NATMAP_INIT_RETRY; + // else back-off normally in case of pathological failures + + m->retryGetAddr = m->timenow + m->retryIntervalGetAddr; + if (m->NextScheduledNATOp - m->retryIntervalGetAddr > 0) + m->NextScheduledNATOp = m->retryIntervalGetAddr; + + last_err = err; +} // Both places that call NATSetNextRenewalTime() update m->NextScheduledNATOp correctly afterwards mDNSlocal void NATSetNextRenewalTime(mDNS *const m, NATTraversalInfo *n) - { - n->retryInterval = (n->ExpiryTime - m->timenow)/2; - if (n->retryInterval < NATMAP_MIN_RETRY_INTERVAL) // Min retry interval is 2 seconds - n->retryInterval = NATMAP_MIN_RETRY_INTERVAL; - n->retryPortMap = m->timenow + n->retryInterval; - } +{ + n->retryInterval = (n->ExpiryTime - m->timenow)/2; + if (n->retryInterval < NATMAP_MIN_RETRY_INTERVAL) // Min retry interval is 2 seconds + n->retryInterval = NATMAP_MIN_RETRY_INTERVAL; + n->retryPortMap = m->timenow + n->retryInterval; +} // Note: When called from handleLNTPortMappingResponse() only pkt->err, pkt->extport and pkt->NATRep_lease fields are filled in mDNSexport void natTraversalHandlePortMapReply(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSIPPort extport, mDNSu32 lease) - { - const char *prot = n->Protocol == NATOp_MapUDP ? "UDP" : n->Protocol == NATOp_MapTCP ? "TCP" : "?"; - (void)prot; - n->NewResult = err; - if (err || lease == 0 || mDNSIPPortIsZero(extport)) - { - LogInfo("natTraversalHandlePortMapReply: %p Response %s Port %5d External Port %5d lease %d error %d", - n, prot, mDNSVal16(n->IntPort), mDNSVal16(extport), lease, err); - n->retryInterval = NATMAP_MAX_RETRY_INTERVAL; - n->retryPortMap = m->timenow + NATMAP_MAX_RETRY_INTERVAL; - // No need to set m->NextScheduledNATOp here, since we're only ever extending the m->retryPortMap time - if (err == NATErr_Refused) n->NewResult = mStatus_NATPortMappingDisabled; - else if (err > NATErr_None && err <= NATErr_Opcode) n->NewResult = mStatus_NATPortMappingUnsupported; - } - else - { - if (lease > 999999999UL / mDNSPlatformOneSecond) - lease = 999999999UL / mDNSPlatformOneSecond; - n->ExpiryTime = NonZeroTime(m->timenow + lease * mDNSPlatformOneSecond); - - if (!mDNSSameIPPort(n->RequestedPort, extport)) - LogInfo("natTraversalHandlePortMapReply: %p Response %s Port %5d External Port %5d changed to %5d", - n, prot, mDNSVal16(n->IntPort), mDNSVal16(n->RequestedPort), mDNSVal16(extport)); - - n->InterfaceID = InterfaceID; - n->RequestedPort = extport; - - LogInfo("natTraversalHandlePortMapReply: %p Response %s Port %5d External Port %5d lease %d", - n, prot, mDNSVal16(n->IntPort), mDNSVal16(extport), lease); - - NATSetNextRenewalTime(m, n); // Got our port mapping; now set timer to renew it at halfway point - m->NextScheduledNATOp = m->timenow; // May need to invoke client callback immediately - } - } +{ + const char *prot = n->Protocol == NATOp_MapUDP ? "UDP" : n->Protocol == NATOp_MapTCP ? "TCP" : "?"; + (void)prot; + n->NewResult = err; + if (err || lease == 0 || mDNSIPPortIsZero(extport)) + { + LogInfo("natTraversalHandlePortMapReply: %p Response %s Port %5d External Port %5d lease %d error %d", + n, prot, mDNSVal16(n->IntPort), mDNSVal16(extport), lease, err); + n->retryInterval = NATMAP_MAX_RETRY_INTERVAL; + n->retryPortMap = m->timenow + NATMAP_MAX_RETRY_INTERVAL; + // No need to set m->NextScheduledNATOp here, since we're only ever extending the m->retryPortMap time + if (err == NATErr_Refused) n->NewResult = mStatus_NATPortMappingDisabled; + else if (err > NATErr_None && err <= NATErr_Opcode) n->NewResult = mStatus_NATPortMappingUnsupported; + } + else + { + if (lease > 999999999UL / mDNSPlatformOneSecond) + lease = 999999999UL / mDNSPlatformOneSecond; + n->ExpiryTime = NonZeroTime(m->timenow + lease * mDNSPlatformOneSecond); + + if (!mDNSSameIPPort(n->RequestedPort, extport)) + LogInfo("natTraversalHandlePortMapReply: %p Response %s Port %5d External Port %5d changed to %5d", + n, prot, mDNSVal16(n->IntPort), mDNSVal16(n->RequestedPort), mDNSVal16(extport)); + + n->InterfaceID = InterfaceID; + n->RequestedPort = extport; + + LogInfo("natTraversalHandlePortMapReply: %p Response %s Port %5d External Port %5d lease %d", + n, prot, mDNSVal16(n->IntPort), mDNSVal16(extport), lease); + + NATSetNextRenewalTime(m, n); // Got our port mapping; now set timer to renew it at halfway point + m->NextScheduledNATOp = m->timenow; // May need to invoke client callback immediately + } +} // Must be called with the mDNS_Lock held mDNSexport mStatus mDNS_StartNATOperation_internal(mDNS *const m, NATTraversalInfo *traversal) - { - NATTraversalInfo **n; - - LogInfo("mDNS_StartNATOperation_internal %p Protocol %d IntPort %d RequestedPort %d NATLease %d", traversal, - traversal->Protocol, mDNSVal16(traversal->IntPort), mDNSVal16(traversal->RequestedPort), traversal->NATLease); - - // Note: It important that new traversal requests are appended at the *end* of the list, not prepended at the start - for (n = &m->NATTraversals; *n; n=&(*n)->next) - { - if (traversal == *n) - { - LogMsg("Error! Tried to add a NAT traversal that's already in the active list: request %p Prot %d Int %d TTL %d", - traversal, traversal->Protocol, mDNSVal16(traversal->IntPort), traversal->NATLease); - #if ForceAlerts - *(long*)0 = 0; - #endif - return(mStatus_AlreadyRegistered); - } - if (traversal->Protocol && traversal->Protocol == (*n)->Protocol && mDNSSameIPPort(traversal->IntPort, (*n)->IntPort) && - !mDNSSameIPPort(traversal->IntPort, SSHPort)) - LogMsg("Warning: Created port mapping request %p Prot %d Int %d TTL %d " - "duplicates existing port mapping request %p Prot %d Int %d TTL %d", - traversal, traversal->Protocol, mDNSVal16(traversal->IntPort), traversal->NATLease, - *n, (*n) ->Protocol, mDNSVal16((*n) ->IntPort), (*n) ->NATLease); - } - - // Initialize necessary fields - traversal->next = mDNSNULL; - traversal->ExpiryTime = 0; - traversal->retryInterval = NATMAP_INIT_RETRY; - traversal->retryPortMap = m->timenow; - traversal->NewResult = mStatus_NoError; - traversal->ExternalAddress = onesIPv4Addr; - traversal->ExternalPort = zeroIPPort; - traversal->Lifetime = 0; - traversal->Result = mStatus_NoError; - - // set default lease if necessary - if (!traversal->NATLease) traversal->NATLease = NATMAP_DEFAULT_LEASE; +{ + NATTraversalInfo **n; + + LogInfo("mDNS_StartNATOperation_internal %p Protocol %d IntPort %d RequestedPort %d NATLease %d", traversal, + traversal->Protocol, mDNSVal16(traversal->IntPort), mDNSVal16(traversal->RequestedPort), traversal->NATLease); + + // Note: It important that new traversal requests are appended at the *end* of the list, not prepended at the start + for (n = &m->NATTraversals; *n; n=&(*n)->next) + { + if (traversal == *n) + { + LogMsg("Error! Tried to add a NAT traversal that's already in the active list: request %p Prot %d Int %d TTL %d", + traversal, traversal->Protocol, mDNSVal16(traversal->IntPort), traversal->NATLease); + #if ForceAlerts + *(long*)0 = 0; + #endif + return(mStatus_AlreadyRegistered); + } + if (traversal->Protocol && traversal->Protocol == (*n)->Protocol && mDNSSameIPPort(traversal->IntPort, (*n)->IntPort) && + !mDNSSameIPPort(traversal->IntPort, SSHPort)) + LogMsg("Warning: Created port mapping request %p Prot %d Int %d TTL %d " + "duplicates existing port mapping request %p Prot %d Int %d TTL %d", + traversal, traversal->Protocol, mDNSVal16(traversal->IntPort), traversal->NATLease, + *n, (*n)->Protocol, mDNSVal16((*n)->IntPort), (*n)->NATLease); + } + + // Initialize necessary fields + traversal->next = mDNSNULL; + traversal->ExpiryTime = 0; + traversal->retryInterval = NATMAP_INIT_RETRY; + traversal->retryPortMap = m->timenow; + traversal->NewResult = mStatus_NoError; + traversal->ExternalAddress = onesIPv4Addr; + traversal->ExternalPort = zeroIPPort; + traversal->Lifetime = 0; + traversal->Result = mStatus_NoError; + + // set default lease if necessary + if (!traversal->NATLease) traversal->NATLease = NATMAP_DEFAULT_LEASE; #ifdef _LEGACY_NAT_TRAVERSAL_ - mDNSPlatformMemZero(&traversal->tcpInfo, sizeof(traversal->tcpInfo)); + mDNSPlatformMemZero(&traversal->tcpInfo, sizeof(traversal->tcpInfo)); #endif // _LEGACY_NAT_TRAVERSAL_ - if (!m->NATTraversals) // If this is our first NAT request, kick off an address request too - { - m->retryGetAddr = m->timenow; - m->retryIntervalGetAddr = NATMAP_INIT_RETRY; - } + if (!m->NATTraversals) // If this is our first NAT request, kick off an address request too + { + m->retryGetAddr = m->timenow; + m->retryIntervalGetAddr = NATMAP_INIT_RETRY; + } - m->NextScheduledNATOp = m->timenow; // This will always trigger sending the packet ASAP, and generate client callback if necessary + m->NextScheduledNATOp = m->timenow; // This will always trigger sending the packet ASAP, and generate client callback if necessary - *n = traversal; // Append new NATTraversalInfo to the end of our list + *n = traversal; // Append new NATTraversalInfo to the end of our list - return(mStatus_NoError); - } + return(mStatus_NoError); +} // Must be called with the mDNS_Lock held mDNSexport mStatus mDNS_StopNATOperation_internal(mDNS *m, NATTraversalInfo *traversal) - { - mDNSBool unmap = mDNStrue; - NATTraversalInfo *p; - NATTraversalInfo **ptr = &m->NATTraversals; - - while (*ptr && *ptr != traversal) ptr=&(*ptr)->next; - if (*ptr) *ptr = (*ptr)->next; // If we found it, cut this NATTraversalInfo struct from our list - else - { - LogMsg("mDNS_StopNATOperation_internal: NATTraversalInfo %p not found in list", traversal); - return(mStatus_BadReferenceErr); - } - - LogInfo("mDNS_StopNATOperation_internal %p %d %d %d %d", traversal, - traversal->Protocol, mDNSVal16(traversal->IntPort), mDNSVal16(traversal->RequestedPort), traversal->NATLease); - - if (m->CurrentNATTraversal == traversal) - m->CurrentNATTraversal = m->CurrentNATTraversal->next; - - if (traversal->Protocol) - for (p = m->NATTraversals; p; p=p->next) - if (traversal->Protocol == p->Protocol && mDNSSameIPPort(traversal->IntPort, p->IntPort)) - { - if (!mDNSSameIPPort(traversal->IntPort, SSHPort)) - LogMsg("Warning: Removed port mapping request %p Prot %d Int %d TTL %d " - "duplicates existing port mapping request %p Prot %d Int %d TTL %d", - traversal, traversal->Protocol, mDNSVal16(traversal->IntPort), traversal->NATLease, - p, p ->Protocol, mDNSVal16(p ->IntPort), p ->NATLease); - unmap = mDNSfalse; - } - - if (traversal->ExpiryTime && unmap) - { - traversal->NATLease = 0; - traversal->retryInterval = 0; - uDNS_SendNATMsg(m, traversal); - } - - // Even if we DIDN'T make a successful UPnP mapping yet, we might still have a partially-open TCP connection we need to clean up - #ifdef _LEGACY_NAT_TRAVERSAL_ - { - mStatus err = LNT_UnmapPort(m, traversal); - if (err) LogMsg("Legacy NAT Traversal - unmap request failed with error %d", err); - } - #endif // _LEGACY_NAT_TRAVERSAL_ - - return(mStatus_NoError); - } +{ + mDNSBool unmap = mDNStrue; + NATTraversalInfo *p; + NATTraversalInfo **ptr = &m->NATTraversals; + + while (*ptr && *ptr != traversal) ptr=&(*ptr)->next; + if (*ptr) *ptr = (*ptr)->next; // If we found it, cut this NATTraversalInfo struct from our list + else + { + LogMsg("mDNS_StopNATOperation_internal: NATTraversalInfo %p not found in list", traversal); + return(mStatus_BadReferenceErr); + } + + LogInfo("mDNS_StopNATOperation_internal %p %d %d %d %d", traversal, + traversal->Protocol, mDNSVal16(traversal->IntPort), mDNSVal16(traversal->RequestedPort), traversal->NATLease); + + if (m->CurrentNATTraversal == traversal) + m->CurrentNATTraversal = m->CurrentNATTraversal->next; + + if (traversal->Protocol) + for (p = m->NATTraversals; p; p=p->next) + if (traversal->Protocol == p->Protocol && mDNSSameIPPort(traversal->IntPort, p->IntPort)) + { + if (!mDNSSameIPPort(traversal->IntPort, SSHPort)) + LogMsg("Warning: Removed port mapping request %p Prot %d Int %d TTL %d " + "duplicates existing port mapping request %p Prot %d Int %d TTL %d", + traversal, traversal->Protocol, mDNSVal16(traversal->IntPort), traversal->NATLease, + p, p->Protocol, mDNSVal16(p->IntPort), p->NATLease); + unmap = mDNSfalse; + } + + if (traversal->ExpiryTime && unmap) + { + traversal->NATLease = 0; + traversal->retryInterval = 0; + uDNS_SendNATMsg(m, traversal); + } + + // Even if we DIDN'T make a successful UPnP mapping yet, we might still have a partially-open TCP connection we need to clean up + #ifdef _LEGACY_NAT_TRAVERSAL_ + { + mStatus err = LNT_UnmapPort(m, traversal); + if (err) LogMsg("Legacy NAT Traversal - unmap request failed with error %d", err); + } + #endif // _LEGACY_NAT_TRAVERSAL_ + + return(mStatus_NoError); +} mDNSexport mStatus mDNS_StartNATOperation(mDNS *const m, NATTraversalInfo *traversal) - { - mStatus status; - mDNS_Lock(m); - status = mDNS_StartNATOperation_internal(m, traversal); - mDNS_Unlock(m); - return(status); - } +{ + mStatus status; + mDNS_Lock(m); + status = mDNS_StartNATOperation_internal(m, traversal); + mDNS_Unlock(m); + return(status); +} mDNSexport mStatus mDNS_StopNATOperation(mDNS *const m, NATTraversalInfo *traversal) - { - mStatus status; - mDNS_Lock(m); - status = mDNS_StopNATOperation_internal(m, traversal); - mDNS_Unlock(m); - return(status); - } +{ + mStatus status; + mDNS_Lock(m); + status = mDNS_StopNATOperation_internal(m, traversal); + mDNS_Unlock(m); + return(status); +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -687,46 +689,46 @@ mDNSexport mStatus mDNS_StopNATOperation(mDNS *const m, NATTraversalInfo *traver // Lock must be held -- otherwise m->timenow is undefined mDNSlocal void StartLLQPolling(mDNS *const m, DNSQuestion *q) - { - debugf("StartLLQPolling: %##s", q->qname.c); - q->state = LLQ_Poll; - q->ThisQInterval = INIT_UCAST_POLL_INTERVAL; - // We want to send our poll query ASAP, but the "+ 1" is because if we set the time to now, - // we risk causing spurious "SendQueries didn't send all its queries" log messages - q->LastQTime = m->timenow - q->ThisQInterval + 1; - SetNextQueryTime(m, q); +{ + debugf("StartLLQPolling: %##s", q->qname.c); + q->state = LLQ_Poll; + q->ThisQInterval = INIT_UCAST_POLL_INTERVAL; + // We want to send our poll query ASAP, but the "+ 1" is because if we set the time to now, + // we risk causing spurious "SendQueries didn't send all its queries" log messages + q->LastQTime = m->timenow - q->ThisQInterval + 1; + SetNextQueryTime(m, q); #if APPLE_OSX_mDNSResponder - UpdateAutoTunnelDomainStatuses(m); + UpdateAutoTunnelDomainStatuses(m); #endif - } +} mDNSlocal mDNSu8 *putLLQ(DNSMessage *const msg, mDNSu8 *ptr, const DNSQuestion *const question, const LLQOptData *const data) - { - AuthRecord rr; - ResourceRecord *opt = &rr.resrec; - rdataOPT *optRD; +{ + AuthRecord rr; + ResourceRecord *opt = &rr.resrec; + rdataOPT *optRD; - //!!!KRS when we implement multiple llqs per message, we'll need to memmove anything past the question section - ptr = putQuestion(msg, ptr, msg->data + AbsoluteMaxDNSMessageData, &question->qname, question->qtype, question->qclass); - if (!ptr) { LogMsg("ERROR: putLLQ - putQuestion"); return mDNSNULL; } + //!!!KRS when we implement multiple llqs per message, we'll need to memmove anything past the question section + ptr = putQuestion(msg, ptr, msg->data + AbsoluteMaxDNSMessageData, &question->qname, question->qtype, question->qclass); + if (!ptr) { LogMsg("ERROR: putLLQ - putQuestion"); return mDNSNULL; } - // locate OptRR if it exists, set pointer to end - // !!!KRS implement me + // locate OptRR if it exists, set pointer to end + // !!!KRS implement me - // format opt rr (fields not specified are zero-valued) - mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); - opt->rrclass = NormalMaxDNSMessageData; - opt->rdlength = sizeof(rdataOPT); // One option in this OPT record - opt->rdestimate = sizeof(rdataOPT); + // format opt rr (fields not specified are zero-valued) + mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + opt->rrclass = NormalMaxDNSMessageData; + opt->rdlength = sizeof(rdataOPT); // One option in this OPT record + opt->rdestimate = sizeof(rdataOPT); - optRD = &rr.resrec.rdata->u.opt[0]; - optRD->opt = kDNSOpt_LLQ; - optRD->u.llq = *data; - ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.numAdditionals, opt, 0); - if (!ptr) { LogMsg("ERROR: putLLQ - PutResourceRecordTTLJumbo"); return mDNSNULL; } + optRD = &rr.resrec.rdata->u.opt[0]; + optRD->opt = kDNSOpt_LLQ; + optRD->u.llq = *data; + ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.numAdditionals, opt, 0); + if (!ptr) { LogMsg("ERROR: putLLQ - PutResourceRecordTTLJumbo"); return mDNSNULL; } - return ptr; - } + return ptr; +} // Normally we'd just request event packets be sent directly to m->LLQNAT.ExternalPort, except... // with LLQs over TLS/TCP we're doing a weird thing where instead of requesting packets be sent to ExternalAddress:ExternalPort @@ -737,225 +739,225 @@ mDNSlocal mDNSu8 *putLLQ(DNSMessage *const msg, mDNSu8 *ptr, const DNSQuestion * // LLQ server to send events to us directly at port 5353 on that address, instead of at our mapped external NAT port. mDNSlocal mDNSu16 GetLLQEventPort(const mDNS *const m, const mDNSAddr *const dst) - { - mDNSAddr src; - mDNSPlatformSourceAddrForDest(&src, dst); - //LogMsg("GetLLQEventPort: src %#a for dst %#a (%d)", &src, dst, mDNSv4AddrIsRFC1918(&src.ip.v4) ? mDNSVal16(m->LLQNAT.ExternalPort) : 0); - return(mDNSv4AddrIsRFC1918(&src.ip.v4) ? mDNSVal16(m->LLQNAT.ExternalPort) : mDNSVal16(MulticastDNSPort)); - } +{ + mDNSAddr src; + mDNSPlatformSourceAddrForDest(&src, dst); + //LogMsg("GetLLQEventPort: src %#a for dst %#a (%d)", &src, dst, mDNSv4AddrIsRFC1918(&src.ip.v4) ? mDNSVal16(m->LLQNAT.ExternalPort) : 0); + return(mDNSv4AddrIsRFC1918(&src.ip.v4) ? mDNSVal16(m->LLQNAT.ExternalPort) : mDNSVal16(MulticastDNSPort)); +} // Normally called with llq set. // May be called with llq NULL, when retransmitting a lost Challenge Response mDNSlocal void sendChallengeResponse(mDNS *const m, DNSQuestion *const q, const LLQOptData *llq) - { - mDNSu8 *responsePtr = m->omsg.data; - LLQOptData llqBuf; - - if (q->tcp) { LogMsg("sendChallengeResponse: ERROR!!: question %##s (%s) tcp non-NULL", q->qname.c, DNSTypeName(q->qtype)); return; } - - if (PrivateQuery(q)) { LogMsg("sendChallengeResponse: ERROR!!: Private Query %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } - - if (q->ntries++ == kLLQ_MAX_TRIES) - { - LogMsg("sendChallengeResponse: %d failed attempts for LLQ %##s", kLLQ_MAX_TRIES, q->qname.c); - StartLLQPolling(m,q); - return; - } - - if (!llq) // Retransmission: need to make a new LLQOptData - { - llqBuf.vers = kLLQ_Vers; - llqBuf.llqOp = kLLQOp_Setup; - llqBuf.err = LLQErr_NoError; // Don't need to tell server UDP notification port when sending over UDP - llqBuf.id = q->id; - llqBuf.llqlease = q->ReqLease; - llq = &llqBuf; - } - - q->LastQTime = m->timenow; - q->ThisQInterval = q->tcp ? 0 : (kLLQ_INIT_RESEND * q->ntries * mDNSPlatformOneSecond); // If using TCP, don't need to retransmit - SetNextQueryTime(m, q); - - // To simulate loss of challenge response packet, uncomment line below - //if (q->ntries == 1) return; - - InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags); - responsePtr = putLLQ(&m->omsg, responsePtr, q, llq); - if (responsePtr) - { - mStatus err = mDNSSendDNSMessage(m, &m->omsg, responsePtr, mDNSInterface_Any, q->LocalSocket, &q->servAddr, q->servPort, mDNSNULL, mDNSNULL); - if (err) { LogMsg("sendChallengeResponse: mDNSSendDNSMessage%s failed: %d", q->tcp ? " (TCP)" : "", err); } - } - else StartLLQPolling(m,q); - } +{ + mDNSu8 *responsePtr = m->omsg.data; + LLQOptData llqBuf; + + if (q->tcp) { LogMsg("sendChallengeResponse: ERROR!!: question %##s (%s) tcp non-NULL", q->qname.c, DNSTypeName(q->qtype)); return; } + + if (PrivateQuery(q)) { LogMsg("sendChallengeResponse: ERROR!!: Private Query %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } + + if (q->ntries++ == kLLQ_MAX_TRIES) + { + LogMsg("sendChallengeResponse: %d failed attempts for LLQ %##s", kLLQ_MAX_TRIES, q->qname.c); + StartLLQPolling(m,q); + return; + } + + if (!llq) // Retransmission: need to make a new LLQOptData + { + llqBuf.vers = kLLQ_Vers; + llqBuf.llqOp = kLLQOp_Setup; + llqBuf.err = LLQErr_NoError; // Don't need to tell server UDP notification port when sending over UDP + llqBuf.id = q->id; + llqBuf.llqlease = q->ReqLease; + llq = &llqBuf; + } + + q->LastQTime = m->timenow; + q->ThisQInterval = q->tcp ? 0 : (kLLQ_INIT_RESEND * q->ntries * mDNSPlatformOneSecond); // If using TCP, don't need to retransmit + SetNextQueryTime(m, q); + + // To simulate loss of challenge response packet, uncomment line below + //if (q->ntries == 1) return; + + InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags); + responsePtr = putLLQ(&m->omsg, responsePtr, q, llq); + if (responsePtr) + { + mStatus err = mDNSSendDNSMessage(m, &m->omsg, responsePtr, mDNSInterface_Any, q->LocalSocket, &q->servAddr, q->servPort, mDNSNULL, mDNSNULL, mDNSfalse); + if (err) { LogMsg("sendChallengeResponse: mDNSSendDNSMessage%s failed: %d", q->tcp ? " (TCP)" : "", err); } + } + else StartLLQPolling(m,q); +} mDNSlocal void SetLLQTimer(mDNS *const m, DNSQuestion *const q, const LLQOptData *const llq) - { - mDNSs32 lease = (mDNSs32)llq->llqlease * mDNSPlatformOneSecond; - q->ReqLease = llq->llqlease; - q->LastQTime = m->timenow; - q->expire = m->timenow + lease; - q->ThisQInterval = lease/2 + mDNSRandom(lease/10); - debugf("SetLLQTimer setting %##s (%s) to %d %d", q->qname.c, DNSTypeName(q->qtype), lease/mDNSPlatformOneSecond, q->ThisQInterval/mDNSPlatformOneSecond); - SetNextQueryTime(m, q); - } +{ + mDNSs32 lease = (mDNSs32)llq->llqlease * mDNSPlatformOneSecond; + q->ReqLease = llq->llqlease; + q->LastQTime = m->timenow; + q->expire = m->timenow + lease; + q->ThisQInterval = lease/2 + mDNSRandom(lease/10); + debugf("SetLLQTimer setting %##s (%s) to %d %d", q->qname.c, DNSTypeName(q->qtype), lease/mDNSPlatformOneSecond, q->ThisQInterval/mDNSPlatformOneSecond); + SetNextQueryTime(m, q); +} mDNSlocal void recvSetupResponse(mDNS *const m, mDNSu8 rcode, DNSQuestion *const q, const LLQOptData *const llq) - { - if (rcode && rcode != kDNSFlag1_RC_NXDomain) - { LogMsg("ERROR: recvSetupResponse %##s (%s) - rcode && rcode != kDNSFlag1_RC_NXDomain", q->qname.c, DNSTypeName(q->qtype)); return; } - - if (llq->llqOp != kLLQOp_Setup) - { LogMsg("ERROR: recvSetupResponse %##s (%s) - bad op %d", q->qname.c, DNSTypeName(q->qtype), llq->llqOp); return; } - - if (llq->vers != kLLQ_Vers) - { LogMsg("ERROR: recvSetupResponse %##s (%s) - bad vers %d", q->qname.c, DNSTypeName(q->qtype), llq->vers); return; } - - if (q->state == LLQ_InitialRequest) - { - //LogInfo("Got LLQ_InitialRequest"); - - if (llq->err) { LogMsg("recvSetupResponse - received llq->err %d from server", llq->err); StartLLQPolling(m,q); return; } - - if (q->ReqLease != llq->llqlease) - debugf("recvSetupResponse: requested lease %lu, granted lease %lu", q->ReqLease, llq->llqlease); - - // cache expiration in case we go to sleep before finishing setup - q->ReqLease = llq->llqlease; - q->expire = m->timenow + ((mDNSs32)llq->llqlease * mDNSPlatformOneSecond); - - // update state - q->state = LLQ_SecondaryRequest; - q->id = llq->id; - q->ntries = 0; // first attempt to send response - sendChallengeResponse(m, q, llq); - } - else if (q->state == LLQ_SecondaryRequest) - { - //LogInfo("Got LLQ_SecondaryRequest"); - - // Fix this immediately if not sooner. Copy the id from the LLQOptData into our DNSQuestion struct. This is only - // an issue for private LLQs, because we skip parts 2 and 3 of the handshake. This is related to a bigger - // problem of the current implementation of TCP LLQ setup: we're not handling state transitions correctly - // if the server sends back SERVFULL or STATIC. - if (PrivateQuery(q)) - { - LogInfo("Private LLQ_SecondaryRequest; copying id %08X%08X", llq->id.l[0], llq->id.l[1]); - q->id = llq->id; - } - - if (llq->err) { LogMsg("ERROR: recvSetupResponse %##s (%s) code %d from server", q->qname.c, DNSTypeName(q->qtype), llq->err); StartLLQPolling(m,q); return; } - if (!mDNSSameOpaque64(&q->id, &llq->id)) - { LogMsg("recvSetupResponse - ID changed. discarding"); return; } // this can happen rarely (on packet loss + reordering) - q->state = LLQ_Established; - q->ntries = 0; - SetLLQTimer(m, q, llq); +{ + if (rcode && rcode != kDNSFlag1_RC_NXDomain) + { LogMsg("ERROR: recvSetupResponse %##s (%s) - rcode && rcode != kDNSFlag1_RC_NXDomain", q->qname.c, DNSTypeName(q->qtype)); return; } + + if (llq->llqOp != kLLQOp_Setup) + { LogMsg("ERROR: recvSetupResponse %##s (%s) - bad op %d", q->qname.c, DNSTypeName(q->qtype), llq->llqOp); return; } + + if (llq->vers != kLLQ_Vers) + { LogMsg("ERROR: recvSetupResponse %##s (%s) - bad vers %d", q->qname.c, DNSTypeName(q->qtype), llq->vers); return; } + + if (q->state == LLQ_InitialRequest) + { + //LogInfo("Got LLQ_InitialRequest"); + + if (llq->err) { LogMsg("recvSetupResponse - received llq->err %d from server", llq->err); StartLLQPolling(m,q); return; } + + if (q->ReqLease != llq->llqlease) + debugf("recvSetupResponse: requested lease %lu, granted lease %lu", q->ReqLease, llq->llqlease); + + // cache expiration in case we go to sleep before finishing setup + q->ReqLease = llq->llqlease; + q->expire = m->timenow + ((mDNSs32)llq->llqlease * mDNSPlatformOneSecond); + + // update state + q->state = LLQ_SecondaryRequest; + q->id = llq->id; + q->ntries = 0; // first attempt to send response + sendChallengeResponse(m, q, llq); + } + else if (q->state == LLQ_SecondaryRequest) + { + //LogInfo("Got LLQ_SecondaryRequest"); + + // Fix this immediately if not sooner. Copy the id from the LLQOptData into our DNSQuestion struct. This is only + // an issue for private LLQs, because we skip parts 2 and 3 of the handshake. This is related to a bigger + // problem of the current implementation of TCP LLQ setup: we're not handling state transitions correctly + // if the server sends back SERVFULL or STATIC. + if (PrivateQuery(q)) + { + LogInfo("Private LLQ_SecondaryRequest; copying id %08X%08X", llq->id.l[0], llq->id.l[1]); + q->id = llq->id; + } + + if (llq->err) { LogMsg("ERROR: recvSetupResponse %##s (%s) code %d from server", q->qname.c, DNSTypeName(q->qtype), llq->err); StartLLQPolling(m,q); return; } + if (!mDNSSameOpaque64(&q->id, &llq->id)) + { LogMsg("recvSetupResponse - ID changed. discarding"); return; } // this can happen rarely (on packet loss + reordering) + q->state = LLQ_Established; + q->ntries = 0; + SetLLQTimer(m, q, llq); #if APPLE_OSX_mDNSResponder - UpdateAutoTunnelDomainStatuses(m); + UpdateAutoTunnelDomainStatuses(m); #endif - } - } + } +} mDNSexport uDNS_LLQType uDNS_recvLLQResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, - const mDNSAddr *const srcaddr, const mDNSIPPort srcport, DNSQuestion **matchQuestion) - { - DNSQuestion pktQ, *q; - if (msg->h.numQuestions && getQuestion(msg, msg->data, end, 0, &pktQ)) - { - const rdataOPT *opt = GetLLQOptData(m, msg, end); - - for (q = m->Questions; q; q = q->next) - { - if (!mDNSOpaque16IsZero(q->TargetQID) && q->LongLived && q->qtype == pktQ.qtype && q->qnamehash == pktQ.qnamehash && SameDomainName(&q->qname, &pktQ.qname)) - { - debugf("uDNS_recvLLQResponse found %##s (%s) %d %#a %#a %X %X %X %X %d", - q->qname.c, DNSTypeName(q->qtype), q->state, srcaddr, &q->servAddr, - opt ? opt->u.llq.id.l[0] : 0, opt ? opt->u.llq.id.l[1] : 0, q->id.l[0], q->id.l[1], opt ? opt->u.llq.llqOp : 0); - if (q->state == LLQ_Poll) debugf("uDNS_LLQ_Events: q->state == LLQ_Poll msg->h.id %d q->TargetQID %d", mDNSVal16(msg->h.id), mDNSVal16(q->TargetQID)); - if (q->state == LLQ_Poll && mDNSSameOpaque16(msg->h.id, q->TargetQID)) - { - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - - // Don't reset the state to IntialRequest as we may write that to the dynamic store - // and PrefPane might wrongly think that we are "Starting" instead of "Polling". If - // we are in polling state because of NAT-PMP disabled or DoubleNAT, next LLQNATCallback - // would kick us back to LLQInitialRequest. So, resetting the state here may not be useful. - // - // If we have a good NAT (neither NAT-PMP disabled nor Double-NAT), then we should not be - // possibly in polling state. To be safe, we want to retry from the start in that case - // as there may not be another LLQNATCallback - // - // NOTE: We can be in polling state if we cannot resolve the SOA record i.e, servAddr is set to - // all ones. In that case, we would set it in LLQ_InitialRequest as it overrides the NAT-PMP or - // Double-NAT state. - if (!mDNSAddressIsOnes(&q->servAddr) && !mDNSIPPortIsZero(m->LLQNAT.ExternalPort) && - !m->LLQNAT.Result) - { - debugf("uDNS_recvLLQResponse got poll response; moving to LLQ_InitialRequest for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - q->state = LLQ_InitialRequest; - } - q->servPort = zeroIPPort; // Clear servPort so that startLLQHandshake will retry the GetZoneData processing - q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10); // Retry LLQ setup in approx 15 minutes - q->LastQTime = m->timenow; - SetNextQueryTime(m, q); - *matchQuestion = q; - return uDNS_LLQ_Entire; // uDNS_LLQ_Entire means flush stale records; assume a large effective TTL - } - // Note: In LLQ Event packets, the msg->h.id does not match our q->TargetQID, because in that case the msg->h.id nonce is selected by the server - else if (opt && q->state == LLQ_Established && opt->u.llq.llqOp == kLLQOp_Event && mDNSSameOpaque64(&opt->u.llq.id, &q->id)) - { - mDNSu8 *ackEnd; - //debugf("Sending LLQ ack for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - InitializeDNSMessage(&m->omsg.h, msg->h.id, ResponseFlags); - ackEnd = putLLQ(&m->omsg, m->omsg.data, q, &opt->u.llq); - if (ackEnd) mDNSSendDNSMessage(m, &m->omsg, ackEnd, mDNSInterface_Any, q->LocalSocket, srcaddr, srcport, mDNSNULL, mDNSNULL); - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - debugf("uDNS_LLQ_Events: q->state == LLQ_Established msg->h.id %d q->TargetQID %d", mDNSVal16(msg->h.id), mDNSVal16(q->TargetQID)); - *matchQuestion = q; - return uDNS_LLQ_Events; - } - if (opt && mDNSSameOpaque16(msg->h.id, q->TargetQID)) - { - if (q->state == LLQ_Established && opt->u.llq.llqOp == kLLQOp_Refresh && mDNSSameOpaque64(&opt->u.llq.id, &q->id) && msg->h.numAdditionals && !msg->h.numAnswers) - { - if (opt->u.llq.err != LLQErr_NoError) LogMsg("recvRefreshReply: received error %d from server", opt->u.llq.err); - else - { - //LogInfo("Received refresh confirmation ntries %d for %##s (%s)", q->ntries, q->qname.c, DNSTypeName(q->qtype)); - // If we're waiting to go to sleep, then this LLQ deletion may have been the thing - // we were waiting for, so schedule another check to see if we can sleep now. - if (opt->u.llq.llqlease == 0 && m->SleepLimit) m->NextScheduledSPRetry = m->timenow; - GrantCacheExtensions(m, q, opt->u.llq.llqlease); - SetLLQTimer(m, q, &opt->u.llq); - q->ntries = 0; - } - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - *matchQuestion = q; - return uDNS_LLQ_Ignore; - } - if (q->state < LLQ_Established && mDNSSameAddress(srcaddr, &q->servAddr)) - { - LLQ_State oldstate = q->state; - recvSetupResponse(m, msg->h.flags.b[1] & kDNSFlag1_RC_Mask, q, &opt->u.llq); - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - // We have a protocol anomaly here in the LLQ definition. - // Both the challenge packet from the server and the ack+answers packet have opt->u.llq.llqOp == kLLQOp_Setup. - // However, we need to treat them differently: - // The challenge packet has no answers in it, and tells us nothing about whether our cache entries - // are still valid, so this packet should not cause us to do anything that messes with our cache. - // The ack+answers packet gives us the whole truth, so we should handle it by updating our cache - // to match the answers in the packet, and only the answers in the packet. - *matchQuestion = q; - return (oldstate == LLQ_SecondaryRequest ? uDNS_LLQ_Entire : uDNS_LLQ_Ignore); - } - } - } - } - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - } - *matchQuestion = mDNSNULL; - return uDNS_LLQ_Not; - } + const mDNSAddr *const srcaddr, const mDNSIPPort srcport, DNSQuestion **matchQuestion) +{ + DNSQuestion pktQ, *q; + if (msg->h.numQuestions && getQuestion(msg, msg->data, end, 0, &pktQ)) + { + const rdataOPT *opt = GetLLQOptData(m, msg, end); + + for (q = m->Questions; q; q = q->next) + { + if (!mDNSOpaque16IsZero(q->TargetQID) && q->LongLived && q->qtype == pktQ.qtype && q->qnamehash == pktQ.qnamehash && SameDomainName(&q->qname, &pktQ.qname)) + { + debugf("uDNS_recvLLQResponse found %##s (%s) %d %#a %#a %X %X %X %X %d", + q->qname.c, DNSTypeName(q->qtype), q->state, srcaddr, &q->servAddr, + opt ? opt->u.llq.id.l[0] : 0, opt ? opt->u.llq.id.l[1] : 0, q->id.l[0], q->id.l[1], opt ? opt->u.llq.llqOp : 0); + if (q->state == LLQ_Poll) debugf("uDNS_LLQ_Events: q->state == LLQ_Poll msg->h.id %d q->TargetQID %d", mDNSVal16(msg->h.id), mDNSVal16(q->TargetQID)); + if (q->state == LLQ_Poll && mDNSSameOpaque16(msg->h.id, q->TargetQID)) + { + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + + // Don't reset the state to IntialRequest as we may write that to the dynamic store + // and PrefPane might wrongly think that we are "Starting" instead of "Polling". If + // we are in polling state because of NAT-PMP disabled or DoubleNAT, next LLQNATCallback + // would kick us back to LLQInitialRequest. So, resetting the state here may not be useful. + // + // If we have a good NAT (neither NAT-PMP disabled nor Double-NAT), then we should not be + // possibly in polling state. To be safe, we want to retry from the start in that case + // as there may not be another LLQNATCallback + // + // NOTE: We can be in polling state if we cannot resolve the SOA record i.e, servAddr is set to + // all ones. In that case, we would set it in LLQ_InitialRequest as it overrides the NAT-PMP or + // Double-NAT state. + if (!mDNSAddressIsOnes(&q->servAddr) && !mDNSIPPortIsZero(m->LLQNAT.ExternalPort) && + !m->LLQNAT.Result) + { + debugf("uDNS_recvLLQResponse got poll response; moving to LLQ_InitialRequest for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->state = LLQ_InitialRequest; + } + q->servPort = zeroIPPort; // Clear servPort so that startLLQHandshake will retry the GetZoneData processing + q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10); // Retry LLQ setup in approx 15 minutes + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + *matchQuestion = q; + return uDNS_LLQ_Entire; // uDNS_LLQ_Entire means flush stale records; assume a large effective TTL + } + // Note: In LLQ Event packets, the msg->h.id does not match our q->TargetQID, because in that case the msg->h.id nonce is selected by the server + else if (opt && q->state == LLQ_Established && opt->u.llq.llqOp == kLLQOp_Event && mDNSSameOpaque64(&opt->u.llq.id, &q->id)) + { + mDNSu8 *ackEnd; + //debugf("Sending LLQ ack for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + InitializeDNSMessage(&m->omsg.h, msg->h.id, ResponseFlags); + ackEnd = putLLQ(&m->omsg, m->omsg.data, q, &opt->u.llq); + if (ackEnd) mDNSSendDNSMessage(m, &m->omsg, ackEnd, mDNSInterface_Any, q->LocalSocket, srcaddr, srcport, mDNSNULL, mDNSNULL, mDNSfalse); + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + debugf("uDNS_LLQ_Events: q->state == LLQ_Established msg->h.id %d q->TargetQID %d", mDNSVal16(msg->h.id), mDNSVal16(q->TargetQID)); + *matchQuestion = q; + return uDNS_LLQ_Events; + } + if (opt && mDNSSameOpaque16(msg->h.id, q->TargetQID)) + { + if (q->state == LLQ_Established && opt->u.llq.llqOp == kLLQOp_Refresh && mDNSSameOpaque64(&opt->u.llq.id, &q->id) && msg->h.numAdditionals && !msg->h.numAnswers) + { + if (opt->u.llq.err != LLQErr_NoError) LogMsg("recvRefreshReply: received error %d from server", opt->u.llq.err); + else + { + //LogInfo("Received refresh confirmation ntries %d for %##s (%s)", q->ntries, q->qname.c, DNSTypeName(q->qtype)); + // If we're waiting to go to sleep, then this LLQ deletion may have been the thing + // we were waiting for, so schedule another check to see if we can sleep now. + if (opt->u.llq.llqlease == 0 && m->SleepLimit) m->NextScheduledSPRetry = m->timenow; + GrantCacheExtensions(m, q, opt->u.llq.llqlease); + SetLLQTimer(m, q, &opt->u.llq); + q->ntries = 0; + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + *matchQuestion = q; + return uDNS_LLQ_Ignore; + } + if (q->state < LLQ_Established && mDNSSameAddress(srcaddr, &q->servAddr)) + { + LLQ_State oldstate = q->state; + recvSetupResponse(m, msg->h.flags.b[1] & kDNSFlag1_RC_Mask, q, &opt->u.llq); + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + // We have a protocol anomaly here in the LLQ definition. + // Both the challenge packet from the server and the ack+answers packet have opt->u.llq.llqOp == kLLQOp_Setup. + // However, we need to treat them differently: + // The challenge packet has no answers in it, and tells us nothing about whether our cache entries + // are still valid, so this packet should not cause us to do anything that messes with our cache. + // The ack+answers packet gives us the whole truth, so we should handle it by updating our cache + // to match the answers in the packet, and only the answers in the packet. + *matchQuestion = q; + return (oldstate == LLQ_SecondaryRequest ? uDNS_LLQ_Entire : uDNS_LLQ_Ignore); + } + } + } + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + *matchQuestion = mDNSNULL; + return uDNS_LLQ_Not; +} // Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.) struct TCPSocket_struct { TCPSocketFlags flags; /* ... */ }; @@ -963,485 +965,485 @@ struct TCPSocket_struct { TCPSocketFlags flags; /* ... */ }; // tcpCallback is called to handle events (e.g. connection opening and data reception) on TCP connections for // Private DNS operations -- private queries, private LLQs, private record updates and private service updates mDNSlocal void tcpCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEstablished, mStatus err) - { - tcpInfo_t *tcpInfo = (tcpInfo_t *)context; - mDNSBool closed = mDNSfalse; - mDNS *m = tcpInfo->m; - DNSQuestion *const q = tcpInfo->question; - tcpInfo_t **backpointer = - q ? &q ->tcp : - tcpInfo->rr ? &tcpInfo->rr ->tcp : mDNSNULL; - if (backpointer && *backpointer != tcpInfo) - LogMsg("tcpCallback: %d backpointer %p incorrect tcpInfo %p question %p rr %p", - mDNSPlatformTCPGetFD(tcpInfo->sock), *backpointer, tcpInfo, q, tcpInfo->rr); - - if (err) goto exit; - - if (ConnectionEstablished) - { - mDNSu8 *end = ((mDNSu8*) &tcpInfo->request) + tcpInfo->requestLen; - DomainAuthInfo *AuthInfo; - - // Defensive coding for Crash in mDNSResponder at GetAuthInfoForName_internal + 366 - // Don't know yet what's causing this, but at least we can be cautious and try to avoid crashing if we find our pointers in an unexpected state - if (tcpInfo->rr && tcpInfo->rr->resrec.name != &tcpInfo->rr->namestorage) - LogMsg("tcpCallback: ERROR: tcpInfo->rr->resrec.name %p != &tcpInfo->rr->namestorage %p", - tcpInfo->rr->resrec.name, &tcpInfo->rr->namestorage); - if (tcpInfo->rr && tcpInfo->rr-> resrec.name != &tcpInfo->rr-> namestorage) return; - - AuthInfo = tcpInfo->rr ? GetAuthInfoForName(m, tcpInfo->rr->resrec.name) : mDNSNULL; - - // connection is established - send the message - if (q && q->LongLived && q->state == LLQ_Established) - { - // Lease renewal over TCP, resulting from opening a TCP connection in sendLLQRefresh - end = ((mDNSu8*) &tcpInfo->request) + tcpInfo->requestLen; - } - else if (q && q->LongLived && q->state != LLQ_Poll && !mDNSIPPortIsZero(m->LLQNAT.ExternalPort) && !mDNSIPPortIsZero(q->servPort)) - { - // Notes: - // If we have a NAT port mapping, ExternalPort is the external port - // If we have a routable address so we don't need a port mapping, ExternalPort is the same as our own internal port - // If we need a NAT port mapping but can't get one, then ExternalPort is zero - LLQOptData llqData; // set llq rdata - llqData.vers = kLLQ_Vers; - llqData.llqOp = kLLQOp_Setup; - llqData.err = GetLLQEventPort(m, &tcpInfo->Addr); // We're using TCP; tell server what UDP port to send notifications to - LogInfo("tcpCallback: eventPort %d", llqData.err); - llqData.id = zeroOpaque64; - llqData.llqlease = kLLQ_DefLease; - InitializeDNSMessage(&tcpInfo->request.h, q->TargetQID, uQueryFlags); - end = putLLQ(&tcpInfo->request, tcpInfo->request.data, q, &llqData); - if (!end) { LogMsg("ERROR: tcpCallback - putLLQ"); err = mStatus_UnknownErr; goto exit; } - AuthInfo = q->AuthInfo; // Need to add TSIG to this message - q->ntries = 0; // Reset ntries so that tcp/tls connection failures don't affect sendChallengeResponse failures - } - else if (q) - { - // LLQ Polling mode or non-LLQ uDNS over TCP - InitializeDNSMessage(&tcpInfo->request.h, q->TargetQID, uQueryFlags); - end = putQuestion(&tcpInfo->request, tcpInfo->request.data, tcpInfo->request.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); - AuthInfo = q->AuthInfo; // Need to add TSIG to this message - } - - err = mDNSSendDNSMessage(m, &tcpInfo->request, end, mDNSInterface_Any, mDNSNULL, &tcpInfo->Addr, tcpInfo->Port, sock, AuthInfo); - if (err) { debugf("ERROR: tcpCallback: mDNSSendDNSMessage - %d", err); err = mStatus_UnknownErr; goto exit; } - - // Record time we sent this question - if (q) - { - mDNS_Lock(m); - q->LastQTime = m->timenow; - if (q->ThisQInterval < (256 * mDNSPlatformOneSecond)) // Now we have a TCP connection open, make sure we wait at least 256 seconds before retrying - q->ThisQInterval = (256 * mDNSPlatformOneSecond); - SetNextQueryTime(m, q); - mDNS_Unlock(m); - } - } - else - { - long n; - if (tcpInfo->nread < 2) // First read the two-byte length preceeding the DNS message - { - mDNSu8 *lenptr = (mDNSu8 *)&tcpInfo->replylen; - n = mDNSPlatformReadTCP(sock, lenptr + tcpInfo->nread, 2 - tcpInfo->nread, &closed); - if (n < 0) - { - LogMsg("ERROR: tcpCallback - attempt to read message length failed (%d)", n); - err = mStatus_ConnFailed; - goto exit; - } - else if (closed) - { - // It's perfectly fine for this socket to close after the first reply. The server might - // be sending gratuitous replies using UDP and doesn't have a need to leave the TCP socket open. - // We'll only log this event if we've never received a reply before. - // BIND 9 appears to close an idle connection after 30 seconds. - if (tcpInfo->numReplies == 0) - { - LogMsg("ERROR: socket closed prematurely tcpInfo->nread = %d", tcpInfo->nread); - err = mStatus_ConnFailed; - goto exit; - } - else - { - // Note that we may not be doing the best thing if an error occurs after we've sent a second request - // over this tcp connection. That is, we only track whether we've received at least one response - // which may have been to a previous request sent over this tcp connection. - if (backpointer) *backpointer = mDNSNULL; // Clear client backpointer FIRST so we don't risk double-disposing our tcpInfo_t - DisposeTCPConn(tcpInfo); - return; - } - } - - tcpInfo->nread += n; - if (tcpInfo->nread < 2) goto exit; - - tcpInfo->replylen = (mDNSu16)((mDNSu16)lenptr[0] << 8 | lenptr[1]); - if (tcpInfo->replylen < sizeof(DNSMessageHeader)) - { LogMsg("ERROR: tcpCallback - length too short (%d bytes)", tcpInfo->replylen); err = mStatus_UnknownErr; goto exit; } - - tcpInfo->reply = mDNSPlatformMemAllocate(tcpInfo->replylen); - if (!tcpInfo->reply) { LogMsg("ERROR: tcpCallback - malloc failed"); err = mStatus_NoMemoryErr; goto exit; } - } - - n = mDNSPlatformReadTCP(sock, ((char *)tcpInfo->reply) + (tcpInfo->nread - 2), tcpInfo->replylen - (tcpInfo->nread - 2), &closed); - - if (n < 0) - { - LogMsg("ERROR: tcpCallback - read returned %d", n); - err = mStatus_ConnFailed; - goto exit; - } - else if (closed) - { - if (tcpInfo->numReplies == 0) - { - LogMsg("ERROR: socket closed prematurely tcpInfo->nread = %d", tcpInfo->nread); - err = mStatus_ConnFailed; - goto exit; - } - else - { - // Note that we may not be doing the best thing if an error occurs after we've sent a second request - // over this tcp connection. That is, we only track whether we've received at least one response - // which may have been to a previous request sent over this tcp connection. - if (backpointer) *backpointer = mDNSNULL; // Clear client backpointer FIRST so we don't risk double-disposing our tcpInfo_t - DisposeTCPConn(tcpInfo); - return; - } - } - - tcpInfo->nread += n; - - if ((tcpInfo->nread - 2) == tcpInfo->replylen) - { - mDNSBool tls; - DNSMessage *reply = tcpInfo->reply; - mDNSu8 *end = (mDNSu8 *)tcpInfo->reply + tcpInfo->replylen; - mDNSAddr Addr = tcpInfo->Addr; - mDNSIPPort Port = tcpInfo->Port; - mDNSIPPort srcPort = zeroIPPort; - tcpInfo->numReplies++; - tcpInfo->reply = mDNSNULL; // Detach reply buffer from tcpInfo_t, to make sure client callback can't cause it to be disposed - tcpInfo->nread = 0; - tcpInfo->replylen = 0; - - // If we're going to dispose this connection, do it FIRST, before calling client callback - // Note: Sleep code depends on us clearing *backpointer here -- it uses the clearing of rr->tcp - // as the signal that the DNS deregistration operation with the server has completed, and the machine may now sleep - // If we clear the tcp pointer in the question, mDNSCoreReceiveResponse cannot find a matching question. Hence - // we store the minimal information i.e., the source port of the connection in the question itself. - // Dereference sock before it is disposed in DisposeTCPConn below. - - if (sock->flags & kTCPSocketFlags_UseTLS) tls = mDNStrue; - else tls = mDNSfalse; - - if (q && q->tcp) {srcPort = q->tcp->SrcPort; q->tcpSrcPort = srcPort;} - - if (backpointer) - if (!q || !q->LongLived || m->SleepState) - { *backpointer = mDNSNULL; DisposeTCPConn(tcpInfo); } - - mDNSCoreReceive(m, reply, end, &Addr, Port, tls ? (mDNSAddr *)1 : mDNSNULL, srcPort, 0); - // USE CAUTION HERE: Invoking mDNSCoreReceive may have caused the environment to change, including canceling this operation itself - - mDNSPlatformMemFree(reply); - return; - } - } +{ + tcpInfo_t *tcpInfo = (tcpInfo_t *)context; + mDNSBool closed = mDNSfalse; + mDNS *m = tcpInfo->m; + DNSQuestion *const q = tcpInfo->question; + tcpInfo_t **backpointer = + q ? &q->tcp : + tcpInfo->rr ? &tcpInfo->rr->tcp : mDNSNULL; + if (backpointer && *backpointer != tcpInfo) + LogMsg("tcpCallback: %d backpointer %p incorrect tcpInfo %p question %p rr %p", + mDNSPlatformTCPGetFD(tcpInfo->sock), *backpointer, tcpInfo, q, tcpInfo->rr); + + if (err) goto exit; + + if (ConnectionEstablished) + { + mDNSu8 *end = ((mDNSu8*) &tcpInfo->request) + tcpInfo->requestLen; + DomainAuthInfo *AuthInfo; + + // Defensive coding for Crash in mDNSResponder at GetAuthInfoForName_internal + 366 + // Don't know yet what's causing this, but at least we can be cautious and try to avoid crashing if we find our pointers in an unexpected state + if (tcpInfo->rr && tcpInfo->rr->resrec.name != &tcpInfo->rr->namestorage) + LogMsg("tcpCallback: ERROR: tcpInfo->rr->resrec.name %p != &tcpInfo->rr->namestorage %p", + tcpInfo->rr->resrec.name, &tcpInfo->rr->namestorage); + if (tcpInfo->rr && tcpInfo->rr->resrec.name != &tcpInfo->rr->namestorage) return; + + AuthInfo = tcpInfo->rr ? GetAuthInfoForName(m, tcpInfo->rr->resrec.name) : mDNSNULL; + + // connection is established - send the message + if (q && q->LongLived && q->state == LLQ_Established) + { + // Lease renewal over TCP, resulting from opening a TCP connection in sendLLQRefresh + end = ((mDNSu8*) &tcpInfo->request) + tcpInfo->requestLen; + } + else if (q && q->LongLived && q->state != LLQ_Poll && !mDNSIPPortIsZero(m->LLQNAT.ExternalPort) && !mDNSIPPortIsZero(q->servPort)) + { + // Notes: + // If we have a NAT port mapping, ExternalPort is the external port + // If we have a routable address so we don't need a port mapping, ExternalPort is the same as our own internal port + // If we need a NAT port mapping but can't get one, then ExternalPort is zero + LLQOptData llqData; // set llq rdata + llqData.vers = kLLQ_Vers; + llqData.llqOp = kLLQOp_Setup; + llqData.err = GetLLQEventPort(m, &tcpInfo->Addr); // We're using TCP; tell server what UDP port to send notifications to + LogInfo("tcpCallback: eventPort %d", llqData.err); + llqData.id = zeroOpaque64; + llqData.llqlease = kLLQ_DefLease; + InitializeDNSMessage(&tcpInfo->request.h, q->TargetQID, uQueryFlags); + end = putLLQ(&tcpInfo->request, tcpInfo->request.data, q, &llqData); + if (!end) { LogMsg("ERROR: tcpCallback - putLLQ"); err = mStatus_UnknownErr; goto exit; } + AuthInfo = q->AuthInfo; // Need to add TSIG to this message + q->ntries = 0; // Reset ntries so that tcp/tls connection failures don't affect sendChallengeResponse failures + } + else if (q) + { + // LLQ Polling mode or non-LLQ uDNS over TCP + InitializeDNSMessage(&tcpInfo->request.h, q->TargetQID, (DNSSECQuestion(q) ? DNSSecQFlags : uQueryFlags)); + end = putQuestion(&tcpInfo->request, tcpInfo->request.data, tcpInfo->request.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); + if (DNSSECQuestion(q) && q->qDNSServer && !q->qDNSServer->cellIntf) + end = putDNSSECOption(&tcpInfo->request, end, tcpInfo->request.data + AbsoluteMaxDNSMessageData); + + AuthInfo = q->AuthInfo; // Need to add TSIG to this message + } + + err = mDNSSendDNSMessage(m, &tcpInfo->request, end, mDNSInterface_Any, mDNSNULL, &tcpInfo->Addr, tcpInfo->Port, sock, AuthInfo, mDNSfalse); + if (err) { debugf("ERROR: tcpCallback: mDNSSendDNSMessage - %d", err); err = mStatus_UnknownErr; goto exit; } + + // Record time we sent this question + if (q) + { + mDNS_Lock(m); + q->LastQTime = m->timenow; + if (q->ThisQInterval < (256 * mDNSPlatformOneSecond)) // Now we have a TCP connection open, make sure we wait at least 256 seconds before retrying + q->ThisQInterval = (256 * mDNSPlatformOneSecond); + SetNextQueryTime(m, q); + mDNS_Unlock(m); + } + } + else + { + long n; + if (tcpInfo->nread < 2) // First read the two-byte length preceeding the DNS message + { + mDNSu8 *lenptr = (mDNSu8 *)&tcpInfo->replylen; + n = mDNSPlatformReadTCP(sock, lenptr + tcpInfo->nread, 2 - tcpInfo->nread, &closed); + if (n < 0) + { + LogMsg("ERROR: tcpCallback - attempt to read message length failed (%d)", n); + err = mStatus_ConnFailed; + goto exit; + } + else if (closed) + { + // It's perfectly fine for this socket to close after the first reply. The server might + // be sending gratuitous replies using UDP and doesn't have a need to leave the TCP socket open. + // We'll only log this event if we've never received a reply before. + // BIND 9 appears to close an idle connection after 30 seconds. + if (tcpInfo->numReplies == 0) + { + LogMsg("ERROR: socket closed prematurely tcpInfo->nread = %d", tcpInfo->nread); + err = mStatus_ConnFailed; + goto exit; + } + else + { + // Note that we may not be doing the best thing if an error occurs after we've sent a second request + // over this tcp connection. That is, we only track whether we've received at least one response + // which may have been to a previous request sent over this tcp connection. + if (backpointer) *backpointer = mDNSNULL; // Clear client backpointer FIRST so we don't risk double-disposing our tcpInfo_t + DisposeTCPConn(tcpInfo); + return; + } + } + + tcpInfo->nread += n; + if (tcpInfo->nread < 2) goto exit; + + tcpInfo->replylen = (mDNSu16)((mDNSu16)lenptr[0] << 8 | lenptr[1]); + if (tcpInfo->replylen < sizeof(DNSMessageHeader)) + { LogMsg("ERROR: tcpCallback - length too short (%d bytes)", tcpInfo->replylen); err = mStatus_UnknownErr; goto exit; } + + tcpInfo->reply = mDNSPlatformMemAllocate(tcpInfo->replylen); + if (!tcpInfo->reply) { LogMsg("ERROR: tcpCallback - malloc failed"); err = mStatus_NoMemoryErr; goto exit; } + } + + n = mDNSPlatformReadTCP(sock, ((char *)tcpInfo->reply) + (tcpInfo->nread - 2), tcpInfo->replylen - (tcpInfo->nread - 2), &closed); + + if (n < 0) + { + LogMsg("ERROR: tcpCallback - read returned %d", n); + err = mStatus_ConnFailed; + goto exit; + } + else if (closed) + { + if (tcpInfo->numReplies == 0) + { + LogMsg("ERROR: socket closed prematurely tcpInfo->nread = %d", tcpInfo->nread); + err = mStatus_ConnFailed; + goto exit; + } + else + { + // Note that we may not be doing the best thing if an error occurs after we've sent a second request + // over this tcp connection. That is, we only track whether we've received at least one response + // which may have been to a previous request sent over this tcp connection. + if (backpointer) *backpointer = mDNSNULL; // Clear client backpointer FIRST so we don't risk double-disposing our tcpInfo_t + DisposeTCPConn(tcpInfo); + return; + } + } + + tcpInfo->nread += n; + + if ((tcpInfo->nread - 2) == tcpInfo->replylen) + { + mDNSBool tls; + DNSMessage *reply = tcpInfo->reply; + mDNSu8 *end = (mDNSu8 *)tcpInfo->reply + tcpInfo->replylen; + mDNSAddr Addr = tcpInfo->Addr; + mDNSIPPort Port = tcpInfo->Port; + mDNSIPPort srcPort = zeroIPPort; + tcpInfo->numReplies++; + tcpInfo->reply = mDNSNULL; // Detach reply buffer from tcpInfo_t, to make sure client callback can't cause it to be disposed + tcpInfo->nread = 0; + tcpInfo->replylen = 0; + + // If we're going to dispose this connection, do it FIRST, before calling client callback + // Note: Sleep code depends on us clearing *backpointer here -- it uses the clearing of rr->tcp + // as the signal that the DNS deregistration operation with the server has completed, and the machine may now sleep + // If we clear the tcp pointer in the question, mDNSCoreReceiveResponse cannot find a matching question. Hence + // we store the minimal information i.e., the source port of the connection in the question itself. + // Dereference sock before it is disposed in DisposeTCPConn below. + + if (sock->flags & kTCPSocketFlags_UseTLS) tls = mDNStrue; + else tls = mDNSfalse; + + if (q && q->tcp) {srcPort = q->tcp->SrcPort; q->tcpSrcPort = srcPort;} + + if (backpointer) + if (!q || !q->LongLived || m->SleepState) + { *backpointer = mDNSNULL; DisposeTCPConn(tcpInfo); } + + mDNSCoreReceive(m, reply, end, &Addr, Port, tls ? (mDNSAddr *)1 : mDNSNULL, srcPort, 0); + // USE CAUTION HERE: Invoking mDNSCoreReceive may have caused the environment to change, including canceling this operation itself + + mDNSPlatformMemFree(reply); + return; + } + } exit: - if (err) - { - // Clear client backpointer FIRST -- that way if one of the callbacks cancels its operation - // we won't end up double-disposing our tcpInfo_t - if (backpointer) *backpointer = mDNSNULL; - - mDNS_Lock(m); // Need to grab the lock to get m->timenow - - if (q) - { - if (q->ThisQInterval == 0) - { - // We get here when we fail to establish a new TCP/TLS connection that would have been used for a new LLQ request or an LLQ renewal. - // Note that ThisQInterval is also zero when sendChallengeResponse resends the LLQ request on an extant TCP/TLS connection. - q->LastQTime = m->timenow; - if (q->LongLived) - { - // We didn't get the chance to send our request packet before the TCP/TLS connection failed. - // We want to retry quickly, but want to back off exponentially in case the server is having issues. - // Since ThisQInterval was 0, we can't just multiply by QuestionIntervalStep, we must track the number - // of TCP/TLS connection failures using ntries. - mDNSu32 count = q->ntries + 1; // want to wait at least 1 second before retrying - - q->ThisQInterval = InitialQuestionInterval; - - for (;count;count--) - q->ThisQInterval *= QuestionIntervalStep; - - if (q->ThisQInterval > LLQ_POLL_INTERVAL) - q->ThisQInterval = LLQ_POLL_INTERVAL; - else - q->ntries++; - - LogMsg("tcpCallback: stream connection for LLQ %##s (%s) failed %d times, retrying in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ntries, q->ThisQInterval); - } - else - { - q->ThisQInterval = MAX_UCAST_POLL_INTERVAL; - LogMsg("tcpCallback: stream connection for %##s (%s) failed, retrying in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval); - } - SetNextQueryTime(m, q); - } - else if (NextQSendTime(q) - m->timenow > (q->LongLived ? LLQ_POLL_INTERVAL : MAX_UCAST_POLL_INTERVAL)) - { - // If we get an error and our next scheduled query for this question is more than the max interval from now, - // reset the next query to ensure we wait no longer the maximum interval from now before trying again. - q->LastQTime = m->timenow; - q->ThisQInterval = q->LongLived ? LLQ_POLL_INTERVAL : MAX_UCAST_POLL_INTERVAL; - SetNextQueryTime(m, q); - LogMsg("tcpCallback: stream connection for %##s (%s) failed, retrying in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval); - } - - // We're about to dispose of the TCP connection, so we must reset the state to retry over TCP/TLS - // because sendChallengeResponse will send the query via UDP if we don't have a tcp pointer. - // Resetting to LLQ_InitialRequest will cause uDNS_CheckCurrentQuestion to call startLLQHandshake, which - // will attempt to establish a new tcp connection. - if (q->LongLived && q->state == LLQ_SecondaryRequest) - q->state = LLQ_InitialRequest; - - // ConnFailed may happen if the server sends a TCP reset or TLS fails, in which case we want to retry establishing the LLQ - // quickly rather than switching to polling mode. This case is handled by the above code to set q->ThisQInterval just above. - // If the error isn't ConnFailed, then the LLQ is in bad shape, so we switch to polling mode. - if (err != mStatus_ConnFailed) - { - if (q->LongLived && q->state != LLQ_Poll) StartLLQPolling(m, q); - } - } - - mDNS_Unlock(m); - - DisposeTCPConn(tcpInfo); - } - } + if (err) + { + // Clear client backpointer FIRST -- that way if one of the callbacks cancels its operation + // we won't end up double-disposing our tcpInfo_t + if (backpointer) *backpointer = mDNSNULL; + + mDNS_Lock(m); // Need to grab the lock to get m->timenow + + if (q) + { + if (q->ThisQInterval == 0) + { + // We get here when we fail to establish a new TCP/TLS connection that would have been used for a new LLQ request or an LLQ renewal. + // Note that ThisQInterval is also zero when sendChallengeResponse resends the LLQ request on an extant TCP/TLS connection. + q->LastQTime = m->timenow; + if (q->LongLived) + { + // We didn't get the chance to send our request packet before the TCP/TLS connection failed. + // We want to retry quickly, but want to back off exponentially in case the server is having issues. + // Since ThisQInterval was 0, we can't just multiply by QuestionIntervalStep, we must track the number + // of TCP/TLS connection failures using ntries. + mDNSu32 count = q->ntries + 1; // want to wait at least 1 second before retrying + + q->ThisQInterval = InitialQuestionInterval; + + for (; count; count--) + q->ThisQInterval *= QuestionIntervalStep; + + if (q->ThisQInterval > LLQ_POLL_INTERVAL) + q->ThisQInterval = LLQ_POLL_INTERVAL; + else + q->ntries++; + + LogMsg("tcpCallback: stream connection for LLQ %##s (%s) failed %d times, retrying in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ntries, q->ThisQInterval); + } + else + { + q->ThisQInterval = MAX_UCAST_POLL_INTERVAL; + LogMsg("tcpCallback: stream connection for %##s (%s) failed, retrying in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval); + } + SetNextQueryTime(m, q); + } + else if (NextQSendTime(q) - m->timenow > (q->LongLived ? LLQ_POLL_INTERVAL : MAX_UCAST_POLL_INTERVAL)) + { + // If we get an error and our next scheduled query for this question is more than the max interval from now, + // reset the next query to ensure we wait no longer the maximum interval from now before trying again. + q->LastQTime = m->timenow; + q->ThisQInterval = q->LongLived ? LLQ_POLL_INTERVAL : MAX_UCAST_POLL_INTERVAL; + SetNextQueryTime(m, q); + LogMsg("tcpCallback: stream connection for %##s (%s) failed, retrying in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval); + } + + // We're about to dispose of the TCP connection, so we must reset the state to retry over TCP/TLS + // because sendChallengeResponse will send the query via UDP if we don't have a tcp pointer. + // Resetting to LLQ_InitialRequest will cause uDNS_CheckCurrentQuestion to call startLLQHandshake, which + // will attempt to establish a new tcp connection. + if (q->LongLived && q->state == LLQ_SecondaryRequest) + q->state = LLQ_InitialRequest; + + // ConnFailed may happen if the server sends a TCP reset or TLS fails, in which case we want to retry establishing the LLQ + // quickly rather than switching to polling mode. This case is handled by the above code to set q->ThisQInterval just above. + // If the error isn't ConnFailed, then the LLQ is in bad shape, so we switch to polling mode. + if (err != mStatus_ConnFailed) + { + if (q->LongLived && q->state != LLQ_Poll) StartLLQPolling(m, q); + } + } + + mDNS_Unlock(m); + + DisposeTCPConn(tcpInfo); + } +} mDNSlocal tcpInfo_t *MakeTCPConn(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, - TCPSocketFlags flags, const mDNSAddr *const Addr, const mDNSIPPort Port, domainname *hostname, - DNSQuestion *const question, AuthRecord *const rr) - { - mStatus err; - mDNSIPPort srcport = zeroIPPort; - tcpInfo_t *info; - - if ((flags & kTCPSocketFlags_UseTLS) && (!hostname || !hostname->c[0])) - { LogMsg("MakeTCPConn: TLS connection being setup with NULL hostname"); return mDNSNULL; } - - info = (tcpInfo_t *)mDNSPlatformMemAllocate(sizeof(tcpInfo_t)); - if (!info) { LogMsg("ERROR: MakeTCP - memallocate failed"); return(mDNSNULL); } - mDNSPlatformMemZero(info, sizeof(tcpInfo_t)); - - info->m = m; - info->sock = mDNSPlatformTCPSocket(m, flags, &srcport); - info->requestLen = 0; - info->question = question; - info->rr = rr; - info->Addr = *Addr; - info->Port = Port; - info->reply = mDNSNULL; - info->replylen = 0; - info->nread = 0; - info->numReplies = 0; - info->SrcPort = srcport; - - if (msg) - { - info->requestLen = (int) (end - ((mDNSu8*)msg)); - mDNSPlatformMemCopy(&info->request, msg, info->requestLen); - } - - if (!info->sock) { LogMsg("MakeTCPConn: unable to create TCP socket"); mDNSPlatformMemFree(info); return(mDNSNULL); } - err = mDNSPlatformTCPConnect(info->sock, Addr, Port, hostname, (question ? question->InterfaceID : mDNSNULL), tcpCallback, info); - - // Probably suboptimal here. - // Instead of returning mDNSNULL here on failure, we should probably invoke the callback with an error code. - // That way clients can put all the error handling and retry/recovery code in one place, - // instead of having to handle immediate errors in one place and async errors in another. - // Also: "err == mStatus_ConnEstablished" probably never happens. - - // Don't need to log "connection failed" in customer builds -- it happens quite often during sleep, wake, configuration changes, etc. - if (err == mStatus_ConnEstablished) { tcpCallback(info->sock, info, mDNStrue, mStatus_NoError); } - else if (err != mStatus_ConnPending ) { LogInfo("MakeTCPConn: connection failed"); DisposeTCPConn(info); return(mDNSNULL); } - return(info); - } + TCPSocketFlags flags, const mDNSAddr *const Addr, const mDNSIPPort Port, domainname *hostname, + DNSQuestion *const question, AuthRecord *const rr) +{ + mStatus err; + mDNSIPPort srcport = zeroIPPort; + tcpInfo_t *info; + mDNSBool useBackgroundTrafficClass; + + useBackgroundTrafficClass = question ? question->UseBrackgroundTrafficClass : mDNSfalse; + + if ((flags & kTCPSocketFlags_UseTLS) && (!hostname || !hostname->c[0])) + { LogMsg("MakeTCPConn: TLS connection being setup with NULL hostname"); return mDNSNULL; } + + info = (tcpInfo_t *)mDNSPlatformMemAllocate(sizeof(tcpInfo_t)); + if (!info) { LogMsg("ERROR: MakeTCP - memallocate failed"); return(mDNSNULL); } + mDNSPlatformMemZero(info, sizeof(tcpInfo_t)); + + info->m = m; + info->sock = mDNSPlatformTCPSocket(m, flags, &srcport, useBackgroundTrafficClass); + info->requestLen = 0; + info->question = question; + info->rr = rr; + info->Addr = *Addr; + info->Port = Port; + info->reply = mDNSNULL; + info->replylen = 0; + info->nread = 0; + info->numReplies = 0; + info->SrcPort = srcport; + + if (msg) + { + info->requestLen = (int) (end - ((mDNSu8*)msg)); + mDNSPlatformMemCopy(&info->request, msg, info->requestLen); + } + + if (!info->sock) { LogMsg("MakeTCPConn: unable to create TCP socket"); mDNSPlatformMemFree(info); return(mDNSNULL); } + err = mDNSPlatformTCPConnect(info->sock, Addr, Port, hostname, (question ? question->InterfaceID : mDNSNULL), tcpCallback, info); + + // Probably suboptimal here. + // Instead of returning mDNSNULL here on failure, we should probably invoke the callback with an error code. + // That way clients can put all the error handling and retry/recovery code in one place, + // instead of having to handle immediate errors in one place and async errors in another. + // Also: "err == mStatus_ConnEstablished" probably never happens. + + // Don't need to log "connection failed" in customer builds -- it happens quite often during sleep, wake, configuration changes, etc. + if (err == mStatus_ConnEstablished) { tcpCallback(info->sock, info, mDNStrue, mStatus_NoError); } + else if (err != mStatus_ConnPending ) { LogInfo("MakeTCPConn: connection failed"); DisposeTCPConn(info); return(mDNSNULL); } + return(info); +} mDNSexport void DisposeTCPConn(struct tcpInfo_t *tcp) - { - mDNSPlatformTCPCloseConnection(tcp->sock); - if (tcp->reply) mDNSPlatformMemFree(tcp->reply); - mDNSPlatformMemFree(tcp); - } +{ + mDNSPlatformTCPCloseConnection(tcp->sock); + if (tcp->reply) mDNSPlatformMemFree(tcp->reply); + mDNSPlatformMemFree(tcp); +} // Lock must be held mDNSexport void startLLQHandshake(mDNS *m, DNSQuestion *q) - { - if (mDNSIPv4AddressIsOnes(m->LLQNAT.ExternalAddress)) - { - LogInfo("startLLQHandshake: waiting for NAT status for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10); // Retry in approx 15 minutes - q->LastQTime = m->timenow; - SetNextQueryTime(m, q); - return; - } - - // Either we don't have NAT-PMP support (ExternalPort is zero) or behind a Double NAT that may or - // may not have NAT-PMP support (NATResult is non-zero) - if (mDNSIPPortIsZero(m->LLQNAT.ExternalPort) || m->LLQNAT.Result) - { - LogInfo("startLLQHandshake: Cannot receive inbound packets; will poll for %##s (%s) External Port %d, NAT Result %d", - q->qname.c, DNSTypeName(q->qtype), mDNSVal16(m->LLQNAT.ExternalPort), m->LLQNAT.Result); - StartLLQPolling(m, q); - return; - } - - if (mDNSIPPortIsZero(q->servPort)) - { - debugf("startLLQHandshake: StartGetZoneData for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10); // Retry in approx 15 minutes - q->LastQTime = m->timenow; - SetNextQueryTime(m, q); - q->servAddr = zeroAddr; - // We know q->servPort is zero because of check above - if (q->nta) CancelGetZoneData(m, q->nta); - q->nta = StartGetZoneData(m, &q->qname, ZoneServiceLLQ, LLQGotZoneData, q); - return; - } - - if (PrivateQuery(q)) - { - if (q->tcp) LogInfo("startLLQHandshake: Disposing existing TCP connection for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - if (q->tcp) { DisposeTCPConn(q->tcp); q->tcp = mDNSNULL; } - if (!q->nta) - { - // Normally we lookup the zone data and then call this function. And we never free the zone data - // for "PrivateQuery". But sometimes this can happen due to some race conditions. When we - // switch networks, we might end up "Polling" the network e.g., we are behind a Double NAT. - // When we poll, we free the zone information as we send the query to the server (See - // PrivateQueryGotZoneData). The NAT callback (LLQNATCallback) may happen soon after that. If we - // are still behind Double NAT, we would have returned early in this function. But we could - // have switched to a network with no NATs and we should get the zone data again. - LogInfo("startLLQHandshake: nta is NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - q->nta = StartGetZoneData(m, &q->qname, ZoneServiceLLQ, LLQGotZoneData, q); - return; - } - else if (!q->nta->Host.c[0]) - { - // This should not happen. If it happens, we print a log and MakeTCPConn will fail if it can't find a hostname - LogMsg("startLLQHandshake: ERROR!!: nta non NULL for %##s (%s) but HostName %d NULL, LongLived %d", q->qname.c, DNSTypeName(q->qtype), q->nta->Host.c[0], q->LongLived); - } - q->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &q->servAddr, q->servPort, &q->nta->Host, q, mDNSNULL); - if (!q->tcp) - q->ThisQInterval = mDNSPlatformOneSecond * 5; // If TCP failed (transient networking glitch) try again in five seconds - else - { - q->state = LLQ_SecondaryRequest; // Right now, for private DNS, we skip the four-way LLQ handshake - q->ReqLease = kLLQ_DefLease; - q->ThisQInterval = 0; - } - q->LastQTime = m->timenow; - SetNextQueryTime(m, q); - } - else - { - debugf("startLLQHandshake: m->AdvertisedV4 %#a%s Server %#a:%d%s %##s (%s)", - &m->AdvertisedV4, mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) ? " (RFC 1918)" : "", - &q->servAddr, mDNSVal16(q->servPort), mDNSAddrIsRFC1918(&q->servAddr) ? " (RFC 1918)" : "", - q->qname.c, DNSTypeName(q->qtype)); - - if (q->ntries++ >= kLLQ_MAX_TRIES) - { - LogMsg("startLLQHandshake: %d failed attempts for LLQ %##s Polling.", kLLQ_MAX_TRIES, q->qname.c); - StartLLQPolling(m, q); - } - else - { - mDNSu8 *end; - LLQOptData llqData; - - // set llq rdata - llqData.vers = kLLQ_Vers; - llqData.llqOp = kLLQOp_Setup; - llqData.err = LLQErr_NoError; // Don't need to tell server UDP notification port when sending over UDP - llqData.id = zeroOpaque64; - llqData.llqlease = kLLQ_DefLease; - - InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags); - end = putLLQ(&m->omsg, m->omsg.data, q, &llqData); - if (!end) { LogMsg("ERROR: startLLQHandshake - putLLQ"); StartLLQPolling(m,q); return; } - - mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, q->LocalSocket, &q->servAddr, q->servPort, mDNSNULL, mDNSNULL); - - // update question state - q->state = LLQ_InitialRequest; - q->ReqLease = kLLQ_DefLease; - q->ThisQInterval = (kLLQ_INIT_RESEND * mDNSPlatformOneSecond); - q->LastQTime = m->timenow; - SetNextQueryTime(m, q); - } - } - } +{ + if (mDNSIPv4AddressIsOnes(m->LLQNAT.ExternalAddress)) + { + LogInfo("startLLQHandshake: waiting for NAT status for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10); // Retry in approx 15 minutes + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + return; + } + + // Either we don't have NAT-PMP support (ExternalPort is zero) or behind a Double NAT that may or + // may not have NAT-PMP support (NATResult is non-zero) + if (mDNSIPPortIsZero(m->LLQNAT.ExternalPort) || m->LLQNAT.Result) + { + LogInfo("startLLQHandshake: Cannot receive inbound packets; will poll for %##s (%s) External Port %d, NAT Result %d", + q->qname.c, DNSTypeName(q->qtype), mDNSVal16(m->LLQNAT.ExternalPort), m->LLQNAT.Result); + StartLLQPolling(m, q); + return; + } + + if (mDNSIPPortIsZero(q->servPort)) + { + debugf("startLLQHandshake: StartGetZoneData for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10); // Retry in approx 15 minutes + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + q->servAddr = zeroAddr; + // We know q->servPort is zero because of check above + if (q->nta) CancelGetZoneData(m, q->nta); + q->nta = StartGetZoneData(m, &q->qname, ZoneServiceLLQ, LLQGotZoneData, q); + return; + } + + if (PrivateQuery(q)) + { + if (q->tcp) LogInfo("startLLQHandshake: Disposing existing TCP connection for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + if (q->tcp) { DisposeTCPConn(q->tcp); q->tcp = mDNSNULL; } + if (!q->nta) + { + // Normally we lookup the zone data and then call this function. And we never free the zone data + // for "PrivateQuery". But sometimes this can happen due to some race conditions. When we + // switch networks, we might end up "Polling" the network e.g., we are behind a Double NAT. + // When we poll, we free the zone information as we send the query to the server (See + // PrivateQueryGotZoneData). The NAT callback (LLQNATCallback) may happen soon after that. If we + // are still behind Double NAT, we would have returned early in this function. But we could + // have switched to a network with no NATs and we should get the zone data again. + LogInfo("startLLQHandshake: nta is NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->nta = StartGetZoneData(m, &q->qname, ZoneServiceLLQ, LLQGotZoneData, q); + return; + } + else if (!q->nta->Host.c[0]) + { + // This should not happen. If it happens, we print a log and MakeTCPConn will fail if it can't find a hostname + LogMsg("startLLQHandshake: ERROR!!: nta non NULL for %##s (%s) but HostName %d NULL, LongLived %d", q->qname.c, DNSTypeName(q->qtype), q->nta->Host.c[0], q->LongLived); + } + q->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &q->servAddr, q->servPort, &q->nta->Host, q, mDNSNULL); + if (!q->tcp) + q->ThisQInterval = mDNSPlatformOneSecond * 5; // If TCP failed (transient networking glitch) try again in five seconds + else + { + q->state = LLQ_SecondaryRequest; // Right now, for private DNS, we skip the four-way LLQ handshake + q->ReqLease = kLLQ_DefLease; + q->ThisQInterval = 0; + } + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + } + else + { + debugf("startLLQHandshake: m->AdvertisedV4 %#a%s Server %#a:%d%s %##s (%s)", + &m->AdvertisedV4, mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) ? " (RFC 1918)" : "", + &q->servAddr, mDNSVal16(q->servPort), mDNSAddrIsRFC1918(&q->servAddr) ? " (RFC 1918)" : "", + q->qname.c, DNSTypeName(q->qtype)); + + if (q->ntries++ >= kLLQ_MAX_TRIES) + { + LogMsg("startLLQHandshake: %d failed attempts for LLQ %##s Polling.", kLLQ_MAX_TRIES, q->qname.c); + StartLLQPolling(m, q); + } + else + { + mDNSu8 *end; + LLQOptData llqData; + + // set llq rdata + llqData.vers = kLLQ_Vers; + llqData.llqOp = kLLQOp_Setup; + llqData.err = LLQErr_NoError; // Don't need to tell server UDP notification port when sending over UDP + llqData.id = zeroOpaque64; + llqData.llqlease = kLLQ_DefLease; + + InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags); + end = putLLQ(&m->omsg, m->omsg.data, q, &llqData); + if (!end) { LogMsg("ERROR: startLLQHandshake - putLLQ"); StartLLQPolling(m,q); return; } + + mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, q->LocalSocket, &q->servAddr, q->servPort, mDNSNULL, mDNSNULL, mDNSfalse); + + // update question state + q->state = LLQ_InitialRequest; + q->ReqLease = kLLQ_DefLease; + q->ThisQInterval = (kLLQ_INIT_RESEND * mDNSPlatformOneSecond); + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + } + } +} // forward declaration so GetServiceTarget can do reverse lookup if needed mDNSlocal void GetStaticHostname(mDNS *m); mDNSexport const domainname *GetServiceTarget(mDNS *m, AuthRecord *const rr) - { - debugf("GetServiceTarget %##s", rr->resrec.name->c); +{ + debugf("GetServiceTarget %##s", rr->resrec.name->c); - if (!rr->AutoTarget) // If not automatically tracking this host's current name, just return the existing target - return(&rr->resrec.rdata->u.srv.target); - else - { + if (!rr->AutoTarget) // If not automatically tracking this host's current name, just return the existing target + return(&rr->resrec.rdata->u.srv.target); + else + { #if APPLE_OSX_mDNSResponder - DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name); - if (AuthInfo && AuthInfo->AutoTunnel) - { - // If this AutoTunnel is not yet active, start it now (which entails activating its NAT Traversal request, - // which will subsequently advertise the appropriate records when the NAT Traversal returns a result) - if (!AuthInfo->AutoTunnelNAT.clientContext && m->AutoTunnelHostAddr.b[0]) - { - LogInfo("GetServiceTarget: Calling SetupLocalAutoTunnelInterface_internal"); - SetupLocalAutoTunnelInterface_internal(m, mDNStrue); - } - if (AuthInfo->AutoTunnelHostRecord.namestorage.c[0] == 0) return(mDNSNULL); - debugf("GetServiceTarget: Returning %##s", AuthInfo->AutoTunnelHostRecord.namestorage.c); - return(&AuthInfo->AutoTunnelHostRecord.namestorage); - } - else + DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name); + if (AuthInfo && AuthInfo->AutoTunnel) + { + StartServerTunnel(m, AuthInfo); + if (AuthInfo->AutoTunnelHostRecord.namestorage.c[0] == 0) return(mDNSNULL); + debugf("GetServiceTarget: Returning %##s", AuthInfo->AutoTunnelHostRecord.namestorage.c); + return(&AuthInfo->AutoTunnelHostRecord.namestorage); + } + else #endif // APPLE_OSX_mDNSResponder - { - const int srvcount = CountLabels(rr->resrec.name); - HostnameInfo *besthi = mDNSNULL, *hi; - int best = 0; - for (hi = m->Hostnames; hi; hi = hi->next) - if (hi->arv4.state == regState_Registered || hi->arv4.state == regState_Refresh || - hi->arv6.state == regState_Registered || hi->arv6.state == regState_Refresh) - { - int x, hostcount = CountLabels(&hi->fqdn); - for (x = hostcount < srvcount ? hostcount : srvcount; x > 0 && x > best; x--) - if (SameDomainName(SkipLeadingLabels(rr->resrec.name, srvcount - x), SkipLeadingLabels(&hi->fqdn, hostcount - x))) - { best = x; besthi = hi; } - } - - if (besthi) return(&besthi->fqdn); - } - if (m->StaticHostname.c[0]) return(&m->StaticHostname); - else GetStaticHostname(m); // asynchronously do reverse lookup for primary IPv4 address - LogInfo("GetServiceTarget: Returning NULL for %s", ARDisplayString(m, rr)); - return(mDNSNULL); - } - } + { + const int srvcount = CountLabels(rr->resrec.name); + HostnameInfo *besthi = mDNSNULL, *hi; + int best = 0; + for (hi = m->Hostnames; hi; hi = hi->next) + if (hi->arv4.state == regState_Registered || hi->arv4.state == regState_Refresh || + hi->arv6.state == regState_Registered || hi->arv6.state == regState_Refresh) + { + int x, hostcount = CountLabels(&hi->fqdn); + for (x = hostcount < srvcount ? hostcount : srvcount; x > 0 && x > best; x--) + if (SameDomainName(SkipLeadingLabels(rr->resrec.name, srvcount - x), SkipLeadingLabels(&hi->fqdn, hostcount - x))) + { best = x; besthi = hi; } + } + + if (besthi) return(&besthi->fqdn); + } + if (m->StaticHostname.c[0]) return(&m->StaticHostname); + else GetStaticHostname(m); // asynchronously do reverse lookup for primary IPv4 address + LogInfo("GetServiceTarget: Returning NULL for %s", ARDisplayString(m, rr)); + return(mDNSNULL); + } +} mDNSlocal const domainname *PUBLIC_UPDATE_SERVICE_TYPE = (const domainname*)"\x0B_dns-update" "\x04_udp"; mDNSlocal const domainname *PUBLIC_LLQ_SERVICE_TYPE = (const domainname*)"\x08_dns-llq" "\x04_udp"; @@ -1450,10 +1452,10 @@ mDNSlocal const domainname *PRIVATE_UPDATE_SERVICE_TYPE = (const domainname*)"\x mDNSlocal const domainname *PRIVATE_QUERY_SERVICE_TYPE = (const domainname*)"\x0E_dns-query-tls" "\x04_tcp"; mDNSlocal const domainname *PRIVATE_LLQ_SERVICE_TYPE = (const domainname*)"\x0C_dns-llq-tls" "\x04_tcp"; -#define ZoneDataSRV(X) (\ - (X)->ZoneService == ZoneServiceUpdate ? ((X)->ZonePrivate ? PRIVATE_UPDATE_SERVICE_TYPE : PUBLIC_UPDATE_SERVICE_TYPE) : \ - (X)->ZoneService == ZoneServiceQuery ? ((X)->ZonePrivate ? PRIVATE_QUERY_SERVICE_TYPE : (const domainname*)"" ) : \ - (X)->ZoneService == ZoneServiceLLQ ? ((X)->ZonePrivate ? PRIVATE_LLQ_SERVICE_TYPE : PUBLIC_LLQ_SERVICE_TYPE ) : (const domainname*)"") +#define ZoneDataSRV(X) ( \ + (X)->ZoneService == ZoneServiceUpdate ? ((X)->ZonePrivate ? PRIVATE_UPDATE_SERVICE_TYPE : PUBLIC_UPDATE_SERVICE_TYPE) : \ + (X)->ZoneService == ZoneServiceQuery ? ((X)->ZonePrivate ? PRIVATE_QUERY_SERVICE_TYPE : (const domainname*)"" ) : \ + (X)->ZoneService == ZoneServiceLLQ ? ((X)->ZonePrivate ? PRIVATE_LLQ_SERVICE_TYPE : PUBLIC_LLQ_SERVICE_TYPE ) : (const domainname*)"") // Forward reference: GetZoneData_StartQuery references GetZoneData_QuestionCallback, and // GetZoneData_QuestionCallback calls GetZoneData_StartQuery @@ -1461,218 +1463,222 @@ mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qt // GetZoneData_QuestionCallback is called from normal client callback context (core API calls allowed) mDNSlocal void GetZoneData_QuestionCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) - { - ZoneData *zd = (ZoneData*)question->QuestionContext; - - debugf("GetZoneData_QuestionCallback: %s %s", AddRecord ? "Add" : "Rmv", RRDisplayString(m, answer)); - - if (!AddRecord) return; // Don't care about REMOVE events - if (AddRecord == QC_addnocache && answer->rdlength == 0) return; // Don't care about transient failure indications - if (answer->rrtype != question->qtype) return; // Don't care about CNAMEs - - if (answer->rrtype == kDNSType_SOA) - { - debugf("GetZoneData GOT SOA %s", RRDisplayString(m, answer)); - mDNS_StopQuery(m, question); - if (question->ThisQInterval != -1) - LogMsg("GetZoneData_QuestionCallback: Question %##s (%s) ThisQInterval %d not -1", question->qname.c, DNSTypeName(question->qtype), question->ThisQInterval); - if (answer->rdlength) - { - AssignDomainName(&zd->ZoneName, answer->name); - zd->ZoneClass = answer->rrclass; - AssignDomainName(&zd->question.qname, &zd->ZoneName); - GetZoneData_StartQuery(m, zd, kDNSType_SRV); - } - else if (zd->CurrentSOA->c[0]) - { - DomainAuthInfo *AuthInfo = GetAuthInfoForName(m, zd->CurrentSOA); - if (AuthInfo && AuthInfo->AutoTunnel) - { - // To keep the load on the server down, we don't chop down on - // SOA lookups for AutoTunnels - LogInfo("GetZoneData_QuestionCallback: not chopping labels for %##s", zd->CurrentSOA->c); - zd->ZoneDataCallback(m, mStatus_NoSuchNameErr, zd); - } - else - { - zd->CurrentSOA = (domainname *)(zd->CurrentSOA->c + zd->CurrentSOA->c[0]+1); - AssignDomainName(&zd->question.qname, zd->CurrentSOA); - GetZoneData_StartQuery(m, zd, kDNSType_SOA); - } - } - else - { - LogInfo("GetZoneData recursed to root label of %##s without finding SOA", zd->ChildName.c); - zd->ZoneDataCallback(m, mStatus_NoSuchNameErr, zd); - } - } - else if (answer->rrtype == kDNSType_SRV) - { - debugf("GetZoneData GOT SRV %s", RRDisplayString(m, answer)); - mDNS_StopQuery(m, question); - if (question->ThisQInterval != -1) - LogMsg("GetZoneData_QuestionCallback: Question %##s (%s) ThisQInterval %d not -1", question->qname.c, DNSTypeName(question->qtype), question->ThisQInterval); +{ + ZoneData *zd = (ZoneData*)question->QuestionContext; + + debugf("GetZoneData_QuestionCallback: %s %s", AddRecord ? "Add" : "Rmv", RRDisplayString(m, answer)); + + if (!AddRecord) return; // Don't care about REMOVE events + if (AddRecord == QC_addnocache && answer->rdlength == 0) return; // Don't care about transient failure indications + if (answer->rrtype != question->qtype) return; // Don't care about CNAMEs + + if (answer->rrtype == kDNSType_SOA) + { + debugf("GetZoneData GOT SOA %s", RRDisplayString(m, answer)); + mDNS_StopQuery(m, question); + if (question->ThisQInterval != -1) + LogMsg("GetZoneData_QuestionCallback: Question %##s (%s) ThisQInterval %d not -1", question->qname.c, DNSTypeName(question->qtype), question->ThisQInterval); + if (answer->rdlength) + { + AssignDomainName(&zd->ZoneName, answer->name); + zd->ZoneClass = answer->rrclass; + AssignDomainName(&zd->question.qname, &zd->ZoneName); + GetZoneData_StartQuery(m, zd, kDNSType_SRV); + } + else if (zd->CurrentSOA->c[0]) + { + DomainAuthInfo *AuthInfo = GetAuthInfoForName(m, zd->CurrentSOA); + if (AuthInfo && AuthInfo->AutoTunnel) + { + // To keep the load on the server down, we don't chop down on + // SOA lookups for AutoTunnels + LogInfo("GetZoneData_QuestionCallback: not chopping labels for %##s", zd->CurrentSOA->c); + zd->ZoneDataCallback(m, mStatus_NoSuchNameErr, zd); + } + else + { + zd->CurrentSOA = (domainname *)(zd->CurrentSOA->c + zd->CurrentSOA->c[0]+1); + AssignDomainName(&zd->question.qname, zd->CurrentSOA); + GetZoneData_StartQuery(m, zd, kDNSType_SOA); + } + } + else + { + LogInfo("GetZoneData recursed to root label of %##s without finding SOA", zd->ChildName.c); + zd->ZoneDataCallback(m, mStatus_NoSuchNameErr, zd); + } + } + else if (answer->rrtype == kDNSType_SRV) + { + debugf("GetZoneData GOT SRV %s", RRDisplayString(m, answer)); + mDNS_StopQuery(m, question); + if (question->ThisQInterval != -1) + LogMsg("GetZoneData_QuestionCallback: Question %##s (%s) ThisQInterval %d not -1", question->qname.c, DNSTypeName(question->qtype), question->ThisQInterval); // Right now we don't want to fail back to non-encrypted operations // If the AuthInfo has the AutoTunnel field set, then we want private or nothing // BTMM: Don't fallback to unencrypted operations when SRV lookup fails #if 0 - if (!answer->rdlength && zd->ZonePrivate && zd->ZoneService != ZoneServiceQuery) - { - zd->ZonePrivate = mDNSfalse; // Causes ZoneDataSRV() to yield a different SRV name when building the query - GetZoneData_StartQuery(m, zd, kDNSType_SRV); // Try again, non-private this time - } - else + if (!answer->rdlength && zd->ZonePrivate && zd->ZoneService != ZoneServiceQuery) + { + zd->ZonePrivate = mDNSfalse; // Causes ZoneDataSRV() to yield a different SRV name when building the query + GetZoneData_StartQuery(m, zd, kDNSType_SRV); // Try again, non-private this time + } + else #endif - { - if (answer->rdlength) - { - AssignDomainName(&zd->Host, &answer->rdata->u.srv.target); - zd->Port = answer->rdata->u.srv.port; - AssignDomainName(&zd->question.qname, &zd->Host); - GetZoneData_StartQuery(m, zd, kDNSType_A); - } - else - { - zd->ZonePrivate = mDNSfalse; - zd->Host.c[0] = 0; - zd->Port = zeroIPPort; - zd->Addr = zeroAddr; - zd->ZoneDataCallback(m, mStatus_NoError, zd); - } - } - } - else if (answer->rrtype == kDNSType_A) - { - debugf("GetZoneData GOT A %s", RRDisplayString(m, answer)); - mDNS_StopQuery(m, question); - if (question->ThisQInterval != -1) - LogMsg("GetZoneData_QuestionCallback: Question %##s (%s) ThisQInterval %d not -1", question->qname.c, DNSTypeName(question->qtype), question->ThisQInterval); - zd->Addr.type = mDNSAddrType_IPv4; - zd->Addr.ip.v4 = (answer->rdlength == 4) ? answer->rdata->u.ipv4 : zerov4Addr; - // In order to simulate firewalls blocking our outgoing TCP connections, returning immediate ICMP errors or TCP resets, - // the code below will make us try to connect to loopback, resulting in an immediate "port unreachable" failure. - // This helps us test to make sure we handle this case gracefully - // BTMM: mDNSResponder taking 100 percent CPU after upgrading to 10.5.1 + { + if (answer->rdlength) + { + AssignDomainName(&zd->Host, &answer->rdata->u.srv.target); + zd->Port = answer->rdata->u.srv.port; + AssignDomainName(&zd->question.qname, &zd->Host); + GetZoneData_StartQuery(m, zd, kDNSType_A); + } + else + { + zd->ZonePrivate = mDNSfalse; + zd->Host.c[0] = 0; + zd->Port = zeroIPPort; + zd->Addr = zeroAddr; + zd->ZoneDataCallback(m, mStatus_NoError, zd); + } + } + } + else if (answer->rrtype == kDNSType_A) + { + debugf("GetZoneData GOT A %s", RRDisplayString(m, answer)); + mDNS_StopQuery(m, question); + if (question->ThisQInterval != -1) + LogMsg("GetZoneData_QuestionCallback: Question %##s (%s) ThisQInterval %d not -1", question->qname.c, DNSTypeName(question->qtype), question->ThisQInterval); + zd->Addr.type = mDNSAddrType_IPv4; + zd->Addr.ip.v4 = (answer->rdlength == 4) ? answer->rdata->u.ipv4 : zerov4Addr; + // In order to simulate firewalls blocking our outgoing TCP connections, returning immediate ICMP errors or TCP resets, + // the code below will make us try to connect to loopback, resulting in an immediate "port unreachable" failure. + // This helps us test to make sure we handle this case gracefully + // BTMM: mDNSResponder taking 100 percent CPU after upgrading to 10.5.1 #if 0 - zd->Addr.ip.v4.b[0] = 127; - zd->Addr.ip.v4.b[1] = 0; - zd->Addr.ip.v4.b[2] = 0; - zd->Addr.ip.v4.b[3] = 1; + zd->Addr.ip.v4.b[0] = 127; + zd->Addr.ip.v4.b[1] = 0; + zd->Addr.ip.v4.b[2] = 0; + zd->Addr.ip.v4.b[3] = 1; #endif - // The caller needs to free the memory when done with zone data - zd->ZoneDataCallback(m, mStatus_NoError, zd); - } - } + // The caller needs to free the memory when done with zone data + zd->ZoneDataCallback(m, mStatus_NoError, zd); + } +} // GetZoneData_StartQuery is called from normal client context (lock not held, or client callback) mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qtype) - { - if (qtype == kDNSType_SRV) - { - AssignDomainName(&zd->question.qname, ZoneDataSRV(zd)); - AppendDomainName(&zd->question.qname, &zd->ZoneName); - debugf("lookupDNSPort %##s", zd->question.qname.c); - } - - // CancelGetZoneData can get called at any time. We should stop the question if it has not been - // stopped already. A value of -1 for ThisQInterval indicates that the question is not active - // yet. - zd->question.ThisQInterval = -1; - zd->question.InterfaceID = mDNSInterface_Any; - zd->question.Target = zeroAddr; - //zd->question.qname.c[0] = 0; // Already set - zd->question.qtype = qtype; - zd->question.qclass = kDNSClass_IN; - zd->question.LongLived = mDNSfalse; - zd->question.ExpectUnique = mDNStrue; - zd->question.ForceMCast = mDNSfalse; - zd->question.ReturnIntermed = mDNStrue; - zd->question.SuppressUnusable = mDNSfalse; - zd->question.SearchListIndex = 0; - zd->question.AppendSearchDomains = 0; - zd->question.RetryWithSearchDomains = mDNSfalse; - zd->question.TimeoutQuestion = 0; - zd->question.WakeOnResolve = 0; - zd->question.qnameOrig = mDNSNULL; - zd->question.QuestionCallback = GetZoneData_QuestionCallback; - zd->question.QuestionContext = zd; - - //LogMsg("GetZoneData_StartQuery %##s (%s) %p", zd->question.qname.c, DNSTypeName(zd->question.qtype), zd->question.Private); - return(mDNS_StartQuery(m, &zd->question)); - } +{ + if (qtype == kDNSType_SRV) + { + AssignDomainName(&zd->question.qname, ZoneDataSRV(zd)); + AppendDomainName(&zd->question.qname, &zd->ZoneName); + debugf("lookupDNSPort %##s", zd->question.qname.c); + } + + // CancelGetZoneData can get called at any time. We should stop the question if it has not been + // stopped already. A value of -1 for ThisQInterval indicates that the question is not active + // yet. + zd->question.ThisQInterval = -1; + zd->question.InterfaceID = mDNSInterface_Any; + zd->question.flags = 0; + zd->question.Target = zeroAddr; + //zd->question.qname.c[0] = 0; // Already set + zd->question.qtype = qtype; + zd->question.qclass = kDNSClass_IN; + zd->question.LongLived = mDNSfalse; + zd->question.ExpectUnique = mDNStrue; + zd->question.ForceMCast = mDNSfalse; + zd->question.ReturnIntermed = mDNStrue; + zd->question.SuppressUnusable = mDNSfalse; + zd->question.SearchListIndex = 0; + zd->question.AppendSearchDomains = 0; + zd->question.RetryWithSearchDomains = mDNSfalse; + zd->question.TimeoutQuestion = 0; + zd->question.WakeOnResolve = 0; + zd->question.UseBrackgroundTrafficClass = mDNSfalse; + zd->question.ValidationRequired = 0; + zd->question.ValidatingResponse = 0; + zd->question.qnameOrig = mDNSNULL; + zd->question.QuestionCallback = GetZoneData_QuestionCallback; + zd->question.QuestionContext = zd; + + //LogMsg("GetZoneData_StartQuery %##s (%s) %p", zd->question.qname.c, DNSTypeName(zd->question.qtype), zd->question.Private); + return(mDNS_StartQuery(m, &zd->question)); +} // StartGetZoneData is an internal routine (i.e. must be called with the lock already held) mDNSexport ZoneData *StartGetZoneData(mDNS *const m, const domainname *const name, const ZoneService target, ZoneDataCallback callback, void *ZoneDataContext) - { - DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, name); - int initialskip = (AuthInfo && AuthInfo->AutoTunnel) ? DomainNameLength(name) - DomainNameLength(&AuthInfo->domain) : 0; - ZoneData *zd = (ZoneData*)mDNSPlatformMemAllocate(sizeof(ZoneData)); - if (!zd) { LogMsg("ERROR: StartGetZoneData - mDNSPlatformMemAllocate failed"); return mDNSNULL; } - mDNSPlatformMemZero(zd, sizeof(ZoneData)); - AssignDomainName(&zd->ChildName, name); - zd->ZoneService = target; - zd->CurrentSOA = (domainname *)(&zd->ChildName.c[initialskip]); - zd->ZoneName.c[0] = 0; - zd->ZoneClass = 0; - zd->Host.c[0] = 0; - zd->Port = zeroIPPort; - zd->Addr = zeroAddr; - zd->ZonePrivate = AuthInfo && AuthInfo->AutoTunnel ? mDNStrue : mDNSfalse; - zd->ZoneDataCallback = callback; - zd->ZoneDataContext = ZoneDataContext; - - zd->question.QuestionContext = zd; - - mDNS_DropLockBeforeCallback(); // GetZoneData_StartQuery expects to be called from a normal callback, so we emulate that here - if (AuthInfo && AuthInfo->AutoTunnel && !mDNSIPPortIsZero(AuthInfo->port)) - { - LogInfo("StartGetZoneData: Bypassing SOA, SRV query for %##s", AuthInfo->domain.c); - // We bypass SOA and SRV queries if we know the hostname and port already from the configuration. - // Today this is only true for AutoTunnel. As we bypass, we need to infer a few things: - // - // 1. Zone name is the same as the AuthInfo domain - // 2. ZoneClass is kDNSClass_IN which should be a safe assumption - // - // If we want to make this bypass mechanism work for non-AutoTunnels also, (1) has to hold - // good. Otherwise, it has to be configured also. - - AssignDomainName(&zd->ZoneName, &AuthInfo->domain); - zd->ZoneClass = kDNSClass_IN; - AssignDomainName(&zd->Host, &AuthInfo->hostname); - zd->Port = AuthInfo->port; - AssignDomainName(&zd->question.qname, &zd->Host); - GetZoneData_StartQuery(m, zd, kDNSType_A); - } - else - { - if (AuthInfo && AuthInfo->AutoTunnel) LogInfo("StartGetZoneData: Not Bypassing SOA, SRV query for %##s", AuthInfo->domain.c); - AssignDomainName(&zd->question.qname, zd->CurrentSOA); - GetZoneData_StartQuery(m, zd, kDNSType_SOA); - } - mDNS_ReclaimLockAfterCallback(); - - return zd; - } +{ + DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, name); + int initialskip = (AuthInfo && AuthInfo->AutoTunnel) ? DomainNameLength(name) - DomainNameLength(&AuthInfo->domain) : 0; + ZoneData *zd = (ZoneData*)mDNSPlatformMemAllocate(sizeof(ZoneData)); + if (!zd) { LogMsg("ERROR: StartGetZoneData - mDNSPlatformMemAllocate failed"); return mDNSNULL; } + mDNSPlatformMemZero(zd, sizeof(ZoneData)); + AssignDomainName(&zd->ChildName, name); + zd->ZoneService = target; + zd->CurrentSOA = (domainname *)(&zd->ChildName.c[initialskip]); + zd->ZoneName.c[0] = 0; + zd->ZoneClass = 0; + zd->Host.c[0] = 0; + zd->Port = zeroIPPort; + zd->Addr = zeroAddr; + zd->ZonePrivate = AuthInfo && AuthInfo->AutoTunnel ? mDNStrue : mDNSfalse; + zd->ZoneDataCallback = callback; + zd->ZoneDataContext = ZoneDataContext; + + zd->question.QuestionContext = zd; + + mDNS_DropLockBeforeCallback(); // GetZoneData_StartQuery expects to be called from a normal callback, so we emulate that here + if (AuthInfo && AuthInfo->AutoTunnel && !mDNSIPPortIsZero(AuthInfo->port)) + { + LogInfo("StartGetZoneData: Bypassing SOA, SRV query for %##s", AuthInfo->domain.c); + // We bypass SOA and SRV queries if we know the hostname and port already from the configuration. + // Today this is only true for AutoTunnel. As we bypass, we need to infer a few things: + // + // 1. Zone name is the same as the AuthInfo domain + // 2. ZoneClass is kDNSClass_IN which should be a safe assumption + // + // If we want to make this bypass mechanism work for non-AutoTunnels also, (1) has to hold + // good. Otherwise, it has to be configured also. + + AssignDomainName(&zd->ZoneName, &AuthInfo->domain); + zd->ZoneClass = kDNSClass_IN; + AssignDomainName(&zd->Host, &AuthInfo->hostname); + zd->Port = AuthInfo->port; + AssignDomainName(&zd->question.qname, &zd->Host); + GetZoneData_StartQuery(m, zd, kDNSType_A); + } + else + { + if (AuthInfo && AuthInfo->AutoTunnel) LogInfo("StartGetZoneData: Not Bypassing SOA, SRV query for %##s", AuthInfo->domain.c); + AssignDomainName(&zd->question.qname, zd->CurrentSOA); + GetZoneData_StartQuery(m, zd, kDNSType_SOA); + } + mDNS_ReclaimLockAfterCallback(); + + return zd; +} // Returns if the question is a GetZoneData question. These questions are special in // that they are created internally while resolving a private query or LLQs. mDNSexport mDNSBool IsGetZoneDataQuestion(DNSQuestion *q) - { - if (q->QuestionCallback == GetZoneData_QuestionCallback) return(mDNStrue); - else return(mDNSfalse); - } +{ + if (q->QuestionCallback == GetZoneData_QuestionCallback) return(mDNStrue); + else return(mDNSfalse); +} // GetZoneData queries are a special case -- even if we have a key for them, we don't do them privately, // because that would result in an infinite loop (i.e. to do a private query we first need to get // the _dns-query-tls SRV record for the zone, and we can't do *that* privately because to do so // we'd need to already know the _dns-query-tls SRV record. // Also, as a general rule, we never do SOA queries privately -mDNSexport DomainAuthInfo *GetAuthInfoForQuestion(mDNS *m, const DNSQuestion *const q) // Must be called with lock held - { - if (q->QuestionCallback == GetZoneData_QuestionCallback) return(mDNSNULL); - if (q->qtype == kDNSType_SOA ) return(mDNSNULL); - return(GetAuthInfoForName_internal(m, &q->qname)); - } +mDNSexport DomainAuthInfo *GetAuthInfoForQuestion(mDNS *m, const DNSQuestion *const q) // Must be called with lock held +{ + if (q->QuestionCallback == GetZoneData_QuestionCallback) return(mDNSNULL); + if (q->qtype == kDNSType_SOA ) return(mDNSNULL); + return(GetAuthInfoForName_internal(m, &q->qname)); +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -1686,189 +1692,189 @@ mDNSlocal mDNSBool IsRecordMergeable(mDNS *const m, AuthRecord *rr, mDNSs32 time // When this function is called, service record is already deregistered. We just // have to deregister the PTR and TXT records. mDNSlocal void UpdateAllServiceRecords(mDNS *const m, AuthRecord *rr, mDNSBool reg) - { - AuthRecord *r, *srvRR; - - if (rr->resrec.rrtype != kDNSType_SRV) { LogMsg("UpdateAllServiceRecords:ERROR!! ResourceRecord not a service record %s", ARDisplayString(m, rr)); return; } - - if (reg && rr->state == regState_NoTarget) { LogMsg("UpdateAllServiceRecords:ERROR!! SRV record %s in noTarget state during registration", ARDisplayString(m, rr)); return; } - - LogInfo("UpdateAllServiceRecords: ResourceRecord %s", ARDisplayString(m, rr)); - - for (r = m->ResourceRecords; r; r=r->next) - { - if (!AuthRecord_uDNS(r)) continue; - srvRR = mDNSNULL; - if (r->resrec.rrtype == kDNSType_PTR) - srvRR = r->Additional1; - else if (r->resrec.rrtype == kDNSType_TXT) - srvRR = r->DependentOn; - if (srvRR && srvRR->resrec.rrtype != kDNSType_SRV) - LogMsg("UpdateAllServiceRecords: ERROR!! Resource record %s wrong, expecting SRV type", ARDisplayString(m, srvRR)); - if (srvRR == rr) - { - if (!reg) - { - LogInfo("UpdateAllServiceRecords: deregistering %s", ARDisplayString(m, r)); - r->SRVChanged = mDNStrue; - r->ThisAPInterval = INIT_RECORD_REG_INTERVAL; - r->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; - r->state = regState_DeregPending; - } - else - { - // Clearing SRVchanged is a safety measure. If our pevious dereg never - // came back and we had a target change, we are starting fresh - r->SRVChanged = mDNSfalse; - // if it is already registered or in the process of registering, then don't - // bother re-registering. This happens today for non-BTMM domains where the - // TXT and PTR get registered before SRV records because of the delay in - // getting the port mapping. There is no point in re-registering the TXT - // and PTR records. - if ((r->state == regState_Registered) || - (r->state == regState_Pending && r->nta && !mDNSIPv4AddressIsZero(r->nta->Addr.ip.v4))) - LogInfo("UpdateAllServiceRecords: not registering %s, state %d", ARDisplayString(m, r), r->state); - else - { - LogInfo("UpdateAllServiceRecords: registering %s, state %d", ARDisplayString(m, r), r->state); - ActivateUnicastRegistration(m, r); - } - } - } - } - } +{ + AuthRecord *r, *srvRR; + + if (rr->resrec.rrtype != kDNSType_SRV) { LogMsg("UpdateAllServiceRecords:ERROR!! ResourceRecord not a service record %s", ARDisplayString(m, rr)); return; } + + if (reg && rr->state == regState_NoTarget) { LogMsg("UpdateAllServiceRecords:ERROR!! SRV record %s in noTarget state during registration", ARDisplayString(m, rr)); return; } + + LogInfo("UpdateAllServiceRecords: ResourceRecord %s", ARDisplayString(m, rr)); + + for (r = m->ResourceRecords; r; r=r->next) + { + if (!AuthRecord_uDNS(r)) continue; + srvRR = mDNSNULL; + if (r->resrec.rrtype == kDNSType_PTR) + srvRR = r->Additional1; + else if (r->resrec.rrtype == kDNSType_TXT) + srvRR = r->DependentOn; + if (srvRR && srvRR->resrec.rrtype != kDNSType_SRV) + LogMsg("UpdateAllServiceRecords: ERROR!! Resource record %s wrong, expecting SRV type", ARDisplayString(m, srvRR)); + if (srvRR == rr) + { + if (!reg) + { + LogInfo("UpdateAllServiceRecords: deregistering %s", ARDisplayString(m, r)); + r->SRVChanged = mDNStrue; + r->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + r->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; + r->state = regState_DeregPending; + } + else + { + // Clearing SRVchanged is a safety measure. If our pevious dereg never + // came back and we had a target change, we are starting fresh + r->SRVChanged = mDNSfalse; + // if it is already registered or in the process of registering, then don't + // bother re-registering. This happens today for non-BTMM domains where the + // TXT and PTR get registered before SRV records because of the delay in + // getting the port mapping. There is no point in re-registering the TXT + // and PTR records. + if ((r->state == regState_Registered) || + (r->state == regState_Pending && r->nta && !mDNSIPv4AddressIsZero(r->nta->Addr.ip.v4))) + LogInfo("UpdateAllServiceRecords: not registering %s, state %d", ARDisplayString(m, r), r->state); + else + { + LogInfo("UpdateAllServiceRecords: registering %s, state %d", ARDisplayString(m, r), r->state); + ActivateUnicastRegistration(m, r); + } + } + } + } +} // Called in normal client context (lock not held) // Currently only supports SRV records for nat mapping mDNSlocal void CompleteRecordNatMap(mDNS *m, NATTraversalInfo *n) - { - const domainname *target; - domainname *srvt; - AuthRecord *rr = (AuthRecord *)n->clientContext; - debugf("SRVNatMap complete %.4a IntPort %u ExternalPort %u NATLease %u", &n->ExternalAddress, mDNSVal16(n->IntPort), mDNSVal16(n->ExternalPort), n->NATLease); - - if (!rr) { LogMsg("CompleteRecordNatMap called with unknown AuthRecord object"); return; } - if (!n->NATLease) { LogMsg("CompleteRecordNatMap No NATLease for %s", ARDisplayString(m, rr)); return; } - - if (rr->resrec.rrtype != kDNSType_SRV) {LogMsg("CompleteRecordNatMap: Not a service record %s", ARDisplayString(m, rr)); return; } - - if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) { LogInfo("CompleteRecordNatMap called for %s, Service deregistering", ARDisplayString(m, rr)); return; } - - if (rr->state == regState_DeregPending) { LogInfo("CompleteRecordNatMap called for %s, record in DeregPending", ARDisplayString(m, rr)); return; } - - // As we free the zone info after registering/deregistering with the server (See hndlRecordUpdateReply), - // we need to restart the get zone data and nat mapping request to get the latest mapping result as we can't handle it - // at this moment. Restart from the beginning. - if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4)) - { - LogInfo("CompleteRecordNatMap called for %s but no zone information!", ARDisplayString(m, rr)); - // We need to clear out the NATinfo state so that it will result in re-acquiring the mapping - // and hence this callback called again. - if (rr->NATinfo.clientContext) - { - mDNS_StopNATOperation_internal(m, &rr->NATinfo); - rr->NATinfo.clientContext = mDNSNULL; - } - rr->state = regState_Pending; - rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; - rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; - return; - } - - mDNS_Lock(m); - // Reevaluate the target always as Target could have changed while - // we were getting the port mapping (See UpdateOneSRVRecord) - target = GetServiceTarget(m, rr); - srvt = GetRRDomainNameTarget(&rr->resrec); - if (!target || target->c[0] == 0 || mDNSIPPortIsZero(n->ExternalPort)) - { - if (target && target->c[0]) - LogInfo("CompleteRecordNatMap - Target %##s for ResourceRecord %##s, ExternalPort %d", target->c, rr->resrec.name->c, mDNSVal16(n->ExternalPort)); - else - LogInfo("CompleteRecordNatMap - no target for %##s, ExternalPort %d", rr->resrec.name->c, mDNSVal16(n->ExternalPort)); - if (srvt) srvt->c[0] = 0; - rr->state = regState_NoTarget; - rr->resrec.rdlength = rr->resrec.rdestimate = 0; - mDNS_Unlock(m); - UpdateAllServiceRecords(m, rr, mDNSfalse); - return; - } - LogInfo("CompleteRecordNatMap - Target %##s for ResourceRecord %##s, ExternalPort %d", target->c, rr->resrec.name->c, mDNSVal16(n->ExternalPort)); - // This function might get called multiple times during a network transition event. Previosuly, we could - // have put the SRV record in NoTarget state above and deregistered all the other records. When this - // function gets called again with a non-zero ExternalPort, we need to set the target and register the - // other records again. - if (srvt && !SameDomainName(srvt, target)) - { - AssignDomainName(srvt, target); - SetNewRData(&rr->resrec, mDNSNULL, 0); // Update rdlength, rdestimate, rdatahash - } - - // SRVChanged is set when when the target of the SRV record changes (See UpdateOneSRVRecord). - // As a result of the target change, we might register just that SRV Record if it was - // previously registered and we have a new target OR deregister SRV (and the associated - // PTR/TXT records) if we don't have a target anymore. When we get a response from the server, - // SRVChanged state tells that we registered/deregistered because of a target change - // and hence handle accordingly e.g., if we deregistered, put the records in NoTarget state OR - // if we registered then put it in Registered state. - // - // Here, we are registering all the records again from the beginning. Treat this as first time - // registration rather than a temporary target change. - rr->SRVChanged = mDNSfalse; - - // We want IsRecordMergeable to check whether it is a record whose update can be - // sent with others. We set the time before we call IsRecordMergeable, so that - // it does not fail this record based on time. We are interested in other checks - // at this time - rr->state = regState_Pending; - rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; - rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; - if (IsRecordMergeable(m, rr, m->timenow + MERGE_DELAY_TIME)) - // Delay the record registration by MERGE_DELAY_TIME so that we can merge them - // into one update - rr->LastAPTime += MERGE_DELAY_TIME; - mDNS_Unlock(m); - // We call this always even though it may not be necessary always e.g., normal registration - // process where TXT and PTR gets registered followed by the SRV record after it gets - // the port mapping. In that case, UpdateAllServiceRecords handles the optimization. The - // update of TXT and PTR record is required if we entered noTargetState before as explained - // above. - UpdateAllServiceRecords(m, rr, mDNStrue); - } +{ + const domainname *target; + domainname *srvt; + AuthRecord *rr = (AuthRecord *)n->clientContext; + debugf("SRVNatMap complete %.4a IntPort %u ExternalPort %u NATLease %u", &n->ExternalAddress, mDNSVal16(n->IntPort), mDNSVal16(n->ExternalPort), n->NATLease); + + if (!rr) { LogMsg("CompleteRecordNatMap called with unknown AuthRecord object"); return; } + if (!n->NATLease) { LogMsg("CompleteRecordNatMap No NATLease for %s", ARDisplayString(m, rr)); return; } + + if (rr->resrec.rrtype != kDNSType_SRV) {LogMsg("CompleteRecordNatMap: Not a service record %s", ARDisplayString(m, rr)); return; } + + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) { LogInfo("CompleteRecordNatMap called for %s, Service deregistering", ARDisplayString(m, rr)); return; } + + if (rr->state == regState_DeregPending) { LogInfo("CompleteRecordNatMap called for %s, record in DeregPending", ARDisplayString(m, rr)); return; } + + // As we free the zone info after registering/deregistering with the server (See hndlRecordUpdateReply), + // we need to restart the get zone data and nat mapping request to get the latest mapping result as we can't handle it + // at this moment. Restart from the beginning. + if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4)) + { + LogInfo("CompleteRecordNatMap called for %s but no zone information!", ARDisplayString(m, rr)); + // We need to clear out the NATinfo state so that it will result in re-acquiring the mapping + // and hence this callback called again. + if (rr->NATinfo.clientContext) + { + mDNS_StopNATOperation_internal(m, &rr->NATinfo); + rr->NATinfo.clientContext = mDNSNULL; + } + rr->state = regState_Pending; + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; + return; + } + + mDNS_Lock(m); + // Reevaluate the target always as Target could have changed while + // we were getting the port mapping (See UpdateOneSRVRecord) + target = GetServiceTarget(m, rr); + srvt = GetRRDomainNameTarget(&rr->resrec); + if (!target || target->c[0] == 0 || mDNSIPPortIsZero(n->ExternalPort)) + { + if (target && target->c[0]) + LogInfo("CompleteRecordNatMap - Target %##s for ResourceRecord %##s, ExternalPort %d", target->c, rr->resrec.name->c, mDNSVal16(n->ExternalPort)); + else + LogInfo("CompleteRecordNatMap - no target for %##s, ExternalPort %d", rr->resrec.name->c, mDNSVal16(n->ExternalPort)); + if (srvt) srvt->c[0] = 0; + rr->state = regState_NoTarget; + rr->resrec.rdlength = rr->resrec.rdestimate = 0; + mDNS_Unlock(m); + UpdateAllServiceRecords(m, rr, mDNSfalse); + return; + } + LogInfo("CompleteRecordNatMap - Target %##s for ResourceRecord %##s, ExternalPort %d", target->c, rr->resrec.name->c, mDNSVal16(n->ExternalPort)); + // This function might get called multiple times during a network transition event. Previosuly, we could + // have put the SRV record in NoTarget state above and deregistered all the other records. When this + // function gets called again with a non-zero ExternalPort, we need to set the target and register the + // other records again. + if (srvt && !SameDomainName(srvt, target)) + { + AssignDomainName(srvt, target); + SetNewRData(&rr->resrec, mDNSNULL, 0); // Update rdlength, rdestimate, rdatahash + } + + // SRVChanged is set when when the target of the SRV record changes (See UpdateOneSRVRecord). + // As a result of the target change, we might register just that SRV Record if it was + // previously registered and we have a new target OR deregister SRV (and the associated + // PTR/TXT records) if we don't have a target anymore. When we get a response from the server, + // SRVChanged state tells that we registered/deregistered because of a target change + // and hence handle accordingly e.g., if we deregistered, put the records in NoTarget state OR + // if we registered then put it in Registered state. + // + // Here, we are registering all the records again from the beginning. Treat this as first time + // registration rather than a temporary target change. + rr->SRVChanged = mDNSfalse; + + // We want IsRecordMergeable to check whether it is a record whose update can be + // sent with others. We set the time before we call IsRecordMergeable, so that + // it does not fail this record based on time. We are interested in other checks + // at this time + rr->state = regState_Pending; + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; + if (IsRecordMergeable(m, rr, m->timenow + MERGE_DELAY_TIME)) + // Delay the record registration by MERGE_DELAY_TIME so that we can merge them + // into one update + rr->LastAPTime += MERGE_DELAY_TIME; + mDNS_Unlock(m); + // We call this always even though it may not be necessary always e.g., normal registration + // process where TXT and PTR gets registered followed by the SRV record after it gets + // the port mapping. In that case, UpdateAllServiceRecords handles the optimization. The + // update of TXT and PTR record is required if we entered noTargetState before as explained + // above. + UpdateAllServiceRecords(m, rr, mDNStrue); +} mDNSlocal void StartRecordNatMap(mDNS *m, AuthRecord *rr) - { - const mDNSu8 *p; - mDNSu8 protocol; - - if (rr->resrec.rrtype != kDNSType_SRV) - { - LogInfo("StartRecordNatMap: Resource Record %##s type %d, not supported", rr->resrec.name->c, rr->resrec.rrtype); - return; - } - p = rr->resrec.name->c; - //Assume ... - // Skip the first two labels to get to the transport protocol - if (p[0]) p += 1 + p[0]; - if (p[0]) p += 1 + p[0]; - if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_tcp")) protocol = NATOp_MapTCP; - else if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_udp")) protocol = NATOp_MapUDP; - else { LogMsg("StartRecordNatMap: could not determine transport protocol of service %##s", rr->resrec.name->c); return; } - - //LogMsg("StartRecordNatMap: clientContext %p IntPort %d srv.port %d %s", - // rr->NATinfo.clientContext, mDNSVal16(rr->NATinfo.IntPort), mDNSVal16(rr->resrec.rdata->u.srv.port), ARDisplayString(m, rr)); - if (rr->NATinfo.clientContext) mDNS_StopNATOperation_internal(m, &rr->NATinfo); - rr->NATinfo.Protocol = protocol; - - // Shouldn't be trying to set IntPort here -- - // BuildUpdateMessage overwrites srs->RR_SRV.resrec.rdata->u.srv.port with external (mapped) port number - rr->NATinfo.IntPort = rr->resrec.rdata->u.srv.port; - rr->NATinfo.RequestedPort = rr->resrec.rdata->u.srv.port; - rr->NATinfo.NATLease = 0; // Request default lease - rr->NATinfo.clientCallback = CompleteRecordNatMap; - rr->NATinfo.clientContext = rr; - mDNS_StartNATOperation_internal(m, &rr->NATinfo); - } +{ + const mDNSu8 *p; + mDNSu8 protocol; + + if (rr->resrec.rrtype != kDNSType_SRV) + { + LogInfo("StartRecordNatMap: Resource Record %##s type %d, not supported", rr->resrec.name->c, rr->resrec.rrtype); + return; + } + p = rr->resrec.name->c; + //Assume ... + // Skip the first two labels to get to the transport protocol + if (p[0]) p += 1 + p[0]; + if (p[0]) p += 1 + p[0]; + if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_tcp")) protocol = NATOp_MapTCP; + else if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_udp")) protocol = NATOp_MapUDP; + else { LogMsg("StartRecordNatMap: could not determine transport protocol of service %##s", rr->resrec.name->c); return; } + + //LogMsg("StartRecordNatMap: clientContext %p IntPort %d srv.port %d %s", + // rr->NATinfo.clientContext, mDNSVal16(rr->NATinfo.IntPort), mDNSVal16(rr->resrec.rdata->u.srv.port), ARDisplayString(m, rr)); + if (rr->NATinfo.clientContext) mDNS_StopNATOperation_internal(m, &rr->NATinfo); + rr->NATinfo.Protocol = protocol; + + // Shouldn't be trying to set IntPort here -- + // BuildUpdateMessage overwrites srs->RR_SRV.resrec.rdata->u.srv.port with external (mapped) port number + rr->NATinfo.IntPort = rr->resrec.rdata->u.srv.port; + rr->NATinfo.RequestedPort = rr->resrec.rdata->u.srv.port; + rr->NATinfo.NATLease = 0; // Request default lease + rr->NATinfo.clientCallback = CompleteRecordNatMap; + rr->NATinfo.clientContext = rr; + mDNS_StartNATOperation_internal(m, &rr->NATinfo); +} // Unlink an Auth Record from the m->ResourceRecords list. // When a resource record enters regState_NoTarget initially, mDNS_Register_internal @@ -1890,538 +1896,542 @@ mDNSlocal void StartRecordNatMap(mDNS *m, AuthRecord *rr) // mDNS_Deregister_internal path instead of just cutting the record from the list. mDNSlocal mStatus UnlinkResourceRecord(mDNS *const m, AuthRecord *const rr) - { - AuthRecord **list = &m->ResourceRecords; - while (*list && *list != rr) list = &(*list)->next; - if (*list) - { - *list = rr->next; - rr->next = mDNSNULL; - - // Temporary workaround to cancel any active NAT mapping operation - if (rr->NATinfo.clientContext) - { - mDNS_StopNATOperation_internal(m, &rr->NATinfo); - rr->NATinfo.clientContext = mDNSNULL; - if (rr->resrec.rrtype == kDNSType_SRV) rr->resrec.rdata->u.srv.port = rr->NATinfo.IntPort; - } - - return(mStatus_NoError); - } - LogMsg("UnlinkResourceRecord:ERROR!! - no such active record %##s", rr->resrec.name->c); - return(mStatus_NoSuchRecord); - } - -// We need to go through mDNS_Register again as we did not complete the +{ + AuthRecord **list = &m->ResourceRecords; + while (*list && *list != rr) list = &(*list)->next; + if (*list) + { + *list = rr->next; + rr->next = mDNSNULL; + + // Temporary workaround to cancel any active NAT mapping operation + if (rr->NATinfo.clientContext) + { + mDNS_StopNATOperation_internal(m, &rr->NATinfo); + rr->NATinfo.clientContext = mDNSNULL; + if (rr->resrec.rrtype == kDNSType_SRV) rr->resrec.rdata->u.srv.port = rr->NATinfo.IntPort; + } + + return(mStatus_NoError); + } + LogMsg("UnlinkResourceRecord:ERROR!! - no such active record %##s", rr->resrec.name->c); + return(mStatus_NoSuchRecord); +} + +// We need to go through mDNS_Register again as we did not complete the // full initialization last time e.g., duplicate checks. // After we register, we will be in regState_GetZoneData. mDNSlocal void RegisterAllServiceRecords(mDNS *const m, AuthRecord *rr) - { - LogInfo("RegisterAllServiceRecords: Service Record %##s", rr->resrec.name->c); - // First Register the service record, we do this differently from other records because - // when it entered NoTarget state, it did not go through complete initialization - rr->SRVChanged = mDNSfalse; - UnlinkResourceRecord(m, rr); - mDNS_Register_internal(m, rr); - // Register the other records - UpdateAllServiceRecords(m, rr, mDNStrue); - } +{ + LogInfo("RegisterAllServiceRecords: Service Record %##s", rr->resrec.name->c); + // First Register the service record, we do this differently from other records because + // when it entered NoTarget state, it did not go through complete initialization + rr->SRVChanged = mDNSfalse; + UnlinkResourceRecord(m, rr); + mDNS_Register_internal(m, rr); + // Register the other records + UpdateAllServiceRecords(m, rr, mDNStrue); +} // Called with lock held mDNSlocal void UpdateOneSRVRecord(mDNS *m, AuthRecord *rr) - { - // Target change if: - // We have a target and were previously waiting for one, or - // We had a target and no longer do, or - // The target has changed - - domainname *curtarget = &rr->resrec.rdata->u.srv.target; - const domainname *const nt = GetServiceTarget(m, rr); - const domainname *const newtarget = nt ? nt : (domainname*)""; - mDNSBool TargetChanged = (newtarget->c[0] && rr->state == regState_NoTarget) || !SameDomainName(curtarget, newtarget); - mDNSBool HaveZoneData = rr->nta && !mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4); - - // Nat state change if: - // We were behind a NAT, and now we are behind a new NAT, or - // We're not behind a NAT but our port was previously mapped to a different external port - // We were not behind a NAT and now we are - - mDNSIPPort port = rr->resrec.rdata->u.srv.port; - mDNSBool NowNeedNATMAP = (rr->AutoTarget == Target_AutoHostAndNATMAP && !mDNSIPPortIsZero(port) && mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && rr->nta && !mDNSAddrIsRFC1918(&rr->nta->Addr)); - mDNSBool WereBehindNAT = (rr->NATinfo.clientContext != mDNSNULL); - mDNSBool PortWasMapped = (rr->NATinfo.clientContext && !mDNSSameIPPort(rr->NATinfo.RequestedPort, port)); // I think this is always false -- SC Sept 07 - mDNSBool NATChanged = (!WereBehindNAT && NowNeedNATMAP) || (!NowNeedNATMAP && PortWasMapped); - - (void)HaveZoneData; //unused - - LogInfo("UpdateOneSRVRecord: Resource Record %s TargetChanged %d, NewTarget %##s", ARDisplayString(m, rr), TargetChanged, nt->c); - - debugf("UpdateOneSRVRecord: %##s newtarget %##s TargetChanged %d HaveZoneData %d port %d NowNeedNATMAP %d WereBehindNAT %d PortWasMapped %d NATChanged %d", - rr->resrec.name->c, newtarget, - TargetChanged, HaveZoneData, mDNSVal16(port), NowNeedNATMAP, WereBehindNAT, PortWasMapped, NATChanged); - - if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("UpdateOneSRVRecord: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); - - if (!TargetChanged && !NATChanged) return; - - // If we are deregistering the record, then ignore any NAT/Target change. - if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) - { - LogInfo("UpdateOneSRVRecord: Deregistering record, Ignoring TargetChanged %d, NATChanged %d for %##s, state %d", TargetChanged, NATChanged, - rr->resrec.name->c, rr->state); - return; - } - - if (newtarget) - LogInfo("UpdateOneSRVRecord: TargetChanged %d, NATChanged %d for %##s, state %d, newtarget %##s", TargetChanged, NATChanged, rr->resrec.name->c, rr->state, newtarget->c); - else - LogInfo("UpdateOneSRVRecord: TargetChanged %d, NATChanged %d for %##s, state %d, null newtarget", TargetChanged, NATChanged, rr->resrec.name->c, rr->state); - switch(rr->state) - { - case regState_NATMap: - // 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. This assumes that whenever we transition out - // of this state, we need to look at the target again. - return; - - case regState_UpdatePending: - // We are getting a Target change/NAT change while the SRV record is being updated ? - // let us not do anything for now. - return; - - case regState_NATError: - if (!NATChanged) return; - // if nat changed, register if we have a target (below) - - case regState_NoTarget: - if (!newtarget->c[0]) - { - LogInfo("UpdateOneSRVRecord: No target yet for Resource Record %s", ARDisplayString(m, rr)); - return; - } - RegisterAllServiceRecords(m , rr); - return; - case regState_DeregPending: - // We are in DeregPending either because the service was deregistered from above or we handled - // a NAT/Target change before and sent the deregistration below. There are a few race conditions - // possible - // - // 1. We are handling a second NAT/Target change while the first dereg is in progress. It is possible - // that first dereg never made it through because there was no network connectivity e.g., disconnecting - // from network triggers this function due to a target change and later connecting to the network - // retriggers this function but the deregistration never made it through yet. Just fall through. - // If there is a target register otherwise deregister. - // - // 2. While we sent the dereg during a previous NAT/Target change, uDNS_DeregisterRecord gets - // called as part of service deregistration. When the response comes back, we call - // CompleteDeregistration rather than handle NAT/Target change because the record is in - // kDNSRecordTypeDeregistering state. - // - // 3. If the upper layer deregisters the service, we check for kDNSRecordTypeDeregistering both - // here in this function to avoid handling NAT/Target change and in hndlRecordUpdateReply to call - // CompleteDeregistration instead of handling NAT/Target change. Hence, we are not concerned - // about that case here. - // - // We just handle case (1) by falling through - case regState_Pending: - case regState_Refresh: - case regState_Registered: - // target or nat changed. deregister service. upon completion, we'll look for a new target - rr->SRVChanged = mDNStrue; - rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; - rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; - if (newtarget->c[0]) - { - LogInfo("UpdateOneSRVRecord: SRV record changed for service %##s, registering with new target %##s", - rr->resrec.name->c, newtarget->c); - rr->state = regState_Pending; - } - else - { - LogInfo("UpdateOneSRVRecord: SRV record changed for service %##s de-registering", rr->resrec.name->c); - rr->state = regState_DeregPending; - UpdateAllServiceRecords(m, rr, mDNSfalse); - } - return; - case regState_Unregistered: - default: LogMsg("UpdateOneSRVRecord: Unknown state %d for %##s", rr->state, rr->resrec.name->c); - } - } +{ + // Target change if: + // We have a target and were previously waiting for one, or + // We had a target and no longer do, or + // The target has changed + + domainname *curtarget = &rr->resrec.rdata->u.srv.target; + const domainname *const nt = GetServiceTarget(m, rr); + const domainname *const newtarget = nt ? nt : (domainname*)""; + mDNSBool TargetChanged = (newtarget->c[0] && rr->state == regState_NoTarget) || !SameDomainName(curtarget, newtarget); + mDNSBool HaveZoneData = rr->nta && !mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4); + + // Nat state change if: + // We were behind a NAT, and now we are behind a new NAT, or + // We're not behind a NAT but our port was previously mapped to a different external port + // We were not behind a NAT and now we are + + mDNSIPPort port = rr->resrec.rdata->u.srv.port; + mDNSBool NowNeedNATMAP = (rr->AutoTarget == Target_AutoHostAndNATMAP && !mDNSIPPortIsZero(port) && mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && rr->nta && !mDNSAddrIsRFC1918(&rr->nta->Addr)); + mDNSBool WereBehindNAT = (rr->NATinfo.clientContext != mDNSNULL); + mDNSBool PortWasMapped = (rr->NATinfo.clientContext && !mDNSSameIPPort(rr->NATinfo.RequestedPort, port)); // I think this is always false -- SC Sept 07 + mDNSBool NATChanged = (!WereBehindNAT && NowNeedNATMAP) || (!NowNeedNATMAP && PortWasMapped); + + (void)HaveZoneData; //unused + + LogInfo("UpdateOneSRVRecord: Resource Record %s TargetChanged %d, NewTarget %##s", ARDisplayString(m, rr), TargetChanged, nt->c); + + debugf("UpdateOneSRVRecord: %##s newtarget %##s TargetChanged %d HaveZoneData %d port %d NowNeedNATMAP %d WereBehindNAT %d PortWasMapped %d NATChanged %d", + rr->resrec.name->c, newtarget, + TargetChanged, HaveZoneData, mDNSVal16(port), NowNeedNATMAP, WereBehindNAT, PortWasMapped, NATChanged); + + if (m->mDNS_busy != m->mDNS_reentrancy+1) + LogMsg("UpdateOneSRVRecord: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + + if (!TargetChanged && !NATChanged) return; + + // If we are deregistering the record, then ignore any NAT/Target change. + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) + { + LogInfo("UpdateOneSRVRecord: Deregistering record, Ignoring TargetChanged %d, NATChanged %d for %##s, state %d", TargetChanged, NATChanged, + rr->resrec.name->c, rr->state); + return; + } + + if (newtarget) + LogInfo("UpdateOneSRVRecord: TargetChanged %d, NATChanged %d for %##s, state %d, newtarget %##s", TargetChanged, NATChanged, rr->resrec.name->c, rr->state, newtarget->c); + else + LogInfo("UpdateOneSRVRecord: TargetChanged %d, NATChanged %d for %##s, state %d, null newtarget", TargetChanged, NATChanged, rr->resrec.name->c, rr->state); + switch(rr->state) + { + case regState_NATMap: + // 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. This assumes that whenever we transition out + // of this state, we need to look at the target again. + return; + + case regState_UpdatePending: + // We are getting a Target change/NAT change while the SRV record is being updated ? + // let us not do anything for now. + return; + + case regState_NATError: + if (!NATChanged) return; + // if nat changed, register if we have a target (below) + + case regState_NoTarget: + if (!newtarget->c[0]) + { + LogInfo("UpdateOneSRVRecord: No target yet for Resource Record %s", ARDisplayString(m, rr)); + return; + } + RegisterAllServiceRecords(m, rr); + return; + case regState_DeregPending: + // We are in DeregPending either because the service was deregistered from above or we handled + // a NAT/Target change before and sent the deregistration below. There are a few race conditions + // possible + // + // 1. We are handling a second NAT/Target change while the first dereg is in progress. It is possible + // that first dereg never made it through because there was no network connectivity e.g., disconnecting + // from network triggers this function due to a target change and later connecting to the network + // retriggers this function but the deregistration never made it through yet. Just fall through. + // If there is a target register otherwise deregister. + // + // 2. While we sent the dereg during a previous NAT/Target change, uDNS_DeregisterRecord gets + // called as part of service deregistration. When the response comes back, we call + // CompleteDeregistration rather than handle NAT/Target change because the record is in + // kDNSRecordTypeDeregistering state. + // + // 3. If the upper layer deregisters the service, we check for kDNSRecordTypeDeregistering both + // here in this function to avoid handling NAT/Target change and in hndlRecordUpdateReply to call + // CompleteDeregistration instead of handling NAT/Target change. Hence, we are not concerned + // about that case here. + // + // We just handle case (1) by falling through + case regState_Pending: + case regState_Refresh: + case regState_Registered: + // target or nat changed. deregister service. upon completion, we'll look for a new target + rr->SRVChanged = mDNStrue; + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; + if (newtarget->c[0]) + { + LogInfo("UpdateOneSRVRecord: SRV record changed for service %##s, registering with new target %##s", + rr->resrec.name->c, newtarget->c); + rr->state = regState_Pending; + } + else + { + LogInfo("UpdateOneSRVRecord: SRV record changed for service %##s de-registering", rr->resrec.name->c); + rr->state = regState_DeregPending; + UpdateAllServiceRecords(m, rr, mDNSfalse); + } + return; + case regState_Unregistered: + default: LogMsg("UpdateOneSRVRecord: Unknown state %d for %##s", rr->state, rr->resrec.name->c); + } +} mDNSexport void UpdateAllSRVRecords(mDNS *m) - { - m->NextSRVUpdate = 0; - LogInfo("UpdateAllSRVRecords %d", m->SleepState); - - if (m->CurrentRecord) - LogMsg("UpdateAllSRVRecords ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); - m->CurrentRecord = m->ResourceRecords; - while (m->CurrentRecord) - { - AuthRecord *rptr = m->CurrentRecord; - m->CurrentRecord = m->CurrentRecord->next; - if (AuthRecord_uDNS(rptr) && rptr->resrec.rrtype == kDNSType_SRV) - UpdateOneSRVRecord(m, rptr); - } - } +{ + m->NextSRVUpdate = 0; + LogInfo("UpdateAllSRVRecords %d", m->SleepState); + + if (m->CurrentRecord) + LogMsg("UpdateAllSRVRecords ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + AuthRecord *rptr = m->CurrentRecord; + m->CurrentRecord = m->CurrentRecord->next; + if (AuthRecord_uDNS(rptr) && rptr->resrec.rrtype == kDNSType_SRV) + UpdateOneSRVRecord(m, rptr); + } +} // Forward reference: AdvertiseHostname references HostnameCallback, and HostnameCallback calls AdvertiseHostname mDNSlocal void HostnameCallback(mDNS *const m, AuthRecord *const rr, mStatus result); // Called in normal client context (lock not held) mDNSlocal void hostnameGetPublicAddressCallback(mDNS *m, NATTraversalInfo *n) - { - HostnameInfo *h = (HostnameInfo *)n->clientContext; - - if (!h) { LogMsg("RegisterHostnameRecord: registration cancelled"); return; } - - if (!n->Result) - { - if (mDNSIPv4AddressIsZero(n->ExternalAddress) || mDNSv4AddrIsRFC1918(&n->ExternalAddress)) return; - - if (h->arv4.resrec.RecordType) - { - if (mDNSSameIPv4Address(h->arv4.resrec.rdata->u.ipv4, n->ExternalAddress)) return; // If address unchanged, do nothing - LogInfo("Updating hostname %p %##s IPv4 from %.4a to %.4a (NAT gateway's external address)",n, - h->arv4.resrec.name->c, &h->arv4.resrec.rdata->u.ipv4, &n->ExternalAddress); - mDNS_Deregister(m, &h->arv4); // mStatus_MemFree callback will re-register with new address - } - else - { - LogInfo("Advertising hostname %##s IPv4 %.4a (NAT gateway's external address)", h->arv4.resrec.name->c, &n->ExternalAddress); - h->arv4.resrec.RecordType = kDNSRecordTypeKnownUnique; - h->arv4.resrec.rdata->u.ipv4 = n->ExternalAddress; - mDNS_Register(m, &h->arv4); - } - } - } +{ + HostnameInfo *h = (HostnameInfo *)n->clientContext; + + if (!h) { LogMsg("RegisterHostnameRecord: registration cancelled"); return; } + + if (!n->Result) + { + if (mDNSIPv4AddressIsZero(n->ExternalAddress) || mDNSv4AddrIsRFC1918(&n->ExternalAddress)) return; + + if (h->arv4.resrec.RecordType) + { + if (mDNSSameIPv4Address(h->arv4.resrec.rdata->u.ipv4, n->ExternalAddress)) return; // If address unchanged, do nothing + LogInfo("Updating hostname %p %##s IPv4 from %.4a to %.4a (NAT gateway's external address)",n, + h->arv4.resrec.name->c, &h->arv4.resrec.rdata->u.ipv4, &n->ExternalAddress); + mDNS_Deregister(m, &h->arv4); // mStatus_MemFree callback will re-register with new address + } + else + { + LogInfo("Advertising hostname %##s IPv4 %.4a (NAT gateway's external address)", h->arv4.resrec.name->c, &n->ExternalAddress); + h->arv4.resrec.RecordType = kDNSRecordTypeKnownUnique; + h->arv4.resrec.rdata->u.ipv4 = n->ExternalAddress; + mDNS_Register(m, &h->arv4); + } + } +} // register record or begin NAT traversal mDNSlocal void AdvertiseHostname(mDNS *m, HostnameInfo *h) - { - if (!mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4) && h->arv4.resrec.RecordType == kDNSRecordTypeUnregistered) - { - mDNS_SetupResourceRecord(&h->arv4, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnregistered, AuthRecordAny, HostnameCallback, h); - AssignDomainName(&h->arv4.namestorage, &h->fqdn); - h->arv4.resrec.rdata->u.ipv4 = m->AdvertisedV4.ip.v4; - h->arv4.state = regState_Unregistered; - if (mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4)) - { - // If we already have a NAT query active, stop it and restart it to make sure we get another callback - if (h->natinfo.clientContext) mDNS_StopNATOperation_internal(m, &h->natinfo); - h->natinfo.Protocol = 0; - h->natinfo.IntPort = zeroIPPort; - h->natinfo.RequestedPort = zeroIPPort; - h->natinfo.NATLease = 0; - h->natinfo.clientCallback = hostnameGetPublicAddressCallback; - h->natinfo.clientContext = h; - mDNS_StartNATOperation_internal(m, &h->natinfo); - } - else - { - LogInfo("Advertising hostname %##s IPv4 %.4a", h->arv4.resrec.name->c, &m->AdvertisedV4.ip.v4); - h->arv4.resrec.RecordType = kDNSRecordTypeKnownUnique; - mDNS_Register_internal(m, &h->arv4); - } - } - - if (!mDNSIPv6AddressIsZero(m->AdvertisedV6.ip.v6) && h->arv6.resrec.RecordType == kDNSRecordTypeUnregistered) - { - mDNS_SetupResourceRecord(&h->arv6, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, HostnameCallback, h); - AssignDomainName(&h->arv6.namestorage, &h->fqdn); - h->arv6.resrec.rdata->u.ipv6 = m->AdvertisedV6.ip.v6; - h->arv6.state = regState_Unregistered; - LogInfo("Advertising hostname %##s IPv6 %.16a", h->arv6.resrec.name->c, &m->AdvertisedV6.ip.v6); - mDNS_Register_internal(m, &h->arv6); - } - } +{ + if (!mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4) && h->arv4.resrec.RecordType == kDNSRecordTypeUnregistered) + { + mDNS_SetupResourceRecord(&h->arv4, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnregistered, AuthRecordAny, HostnameCallback, h); + AssignDomainName(&h->arv4.namestorage, &h->fqdn); + h->arv4.resrec.rdata->u.ipv4 = m->AdvertisedV4.ip.v4; + h->arv4.state = regState_Unregistered; + if (mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4)) + { + // If we already have a NAT query active, stop it and restart it to make sure we get another callback + if (h->natinfo.clientContext) mDNS_StopNATOperation_internal(m, &h->natinfo); + h->natinfo.Protocol = 0; + h->natinfo.IntPort = zeroIPPort; + h->natinfo.RequestedPort = zeroIPPort; + h->natinfo.NATLease = 0; + h->natinfo.clientCallback = hostnameGetPublicAddressCallback; + h->natinfo.clientContext = h; + mDNS_StartNATOperation_internal(m, &h->natinfo); + } + else + { + LogInfo("Advertising hostname %##s IPv4 %.4a", h->arv4.resrec.name->c, &m->AdvertisedV4.ip.v4); + h->arv4.resrec.RecordType = kDNSRecordTypeKnownUnique; + mDNS_Register_internal(m, &h->arv4); + } + } + + if (!mDNSIPv6AddressIsZero(m->AdvertisedV6.ip.v6) && h->arv6.resrec.RecordType == kDNSRecordTypeUnregistered) + { + mDNS_SetupResourceRecord(&h->arv6, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, HostnameCallback, h); + AssignDomainName(&h->arv6.namestorage, &h->fqdn); + h->arv6.resrec.rdata->u.ipv6 = m->AdvertisedV6.ip.v6; + h->arv6.state = regState_Unregistered; + LogInfo("Advertising hostname %##s IPv6 %.16a", h->arv6.resrec.name->c, &m->AdvertisedV6.ip.v6); + mDNS_Register_internal(m, &h->arv6); + } +} mDNSlocal void HostnameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) - { - HostnameInfo *hi = (HostnameInfo *)rr->RecordContext; - - if (result == mStatus_MemFree) - { - if (hi) - { - // If we're still in the Hostnames list, update to new address - HostnameInfo *i; - LogInfo("HostnameCallback: Got mStatus_MemFree for %p %p %s", hi, rr, ARDisplayString(m, rr)); - for (i = m->Hostnames; i; i = i->next) - if (rr == &i->arv4 || rr == &i->arv6) - { mDNS_Lock(m); AdvertiseHostname(m, i); mDNS_Unlock(m); return; } - - // Else, we're not still in the Hostnames list, so free the memory - if (hi->arv4.resrec.RecordType == kDNSRecordTypeUnregistered && - hi->arv6.resrec.RecordType == kDNSRecordTypeUnregistered) - { - if (hi->natinfo.clientContext) mDNS_StopNATOperation_internal(m, &hi->natinfo); - hi->natinfo.clientContext = mDNSNULL; - mDNSPlatformMemFree(hi); // free hi when both v4 and v6 AuthRecs deallocated - } - } - return; - } - - if (result) - { - // don't unlink or free - we can retry when we get a new address/router - if (rr->resrec.rrtype == kDNSType_A) - LogMsg("HostnameCallback: Error %d for registration of %##s IP %.4a", result, rr->resrec.name->c, &rr->resrec.rdata->u.ipv4); - else - LogMsg("HostnameCallback: Error %d for registration of %##s IP %.16a", result, rr->resrec.name->c, &rr->resrec.rdata->u.ipv6); - if (!hi) { mDNSPlatformMemFree(rr); return; } - if (rr->state != regState_Unregistered) LogMsg("Error: HostnameCallback invoked with error code for record not in regState_Unregistered!"); - - if (hi->arv4.state == regState_Unregistered && - hi->arv6.state == regState_Unregistered) - { - // only deliver status if both v4 and v6 fail - rr->RecordContext = (void *)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 - mDNS_Lock(m); - m->NextSRVUpdate = NonZeroTime(m->timenow); - mDNS_Unlock(m); - - // Deliver success to client - if (!hi) { LogMsg("HostnameCallback invoked with orphaned address record"); return; } - if (rr->resrec.rrtype == kDNSType_A) - LogInfo("Registered hostname %##s IP %.4a", rr->resrec.name->c, &rr->resrec.rdata->u.ipv4); - else - LogInfo("Registered hostname %##s IP %.16a", rr->resrec.name->c, &rr->resrec.rdata->u.ipv6); - - rr->RecordContext = (void *)hi->StatusContext; - if (hi->StatusCallback) - hi->StatusCallback(m, rr, result); // client may NOT make API calls here - rr->RecordContext = (void *)hi; - } +{ + HostnameInfo *hi = (HostnameInfo *)rr->RecordContext; + + if (result == mStatus_MemFree) + { + if (hi) + { + // If we're still in the Hostnames list, update to new address + HostnameInfo *i; + LogInfo("HostnameCallback: Got mStatus_MemFree for %p %p %s", hi, rr, ARDisplayString(m, rr)); + for (i = m->Hostnames; i; i = i->next) + if (rr == &i->arv4 || rr == &i->arv6) + { mDNS_Lock(m); AdvertiseHostname(m, i); mDNS_Unlock(m); return; } + + // Else, we're not still in the Hostnames list, so free the memory + if (hi->arv4.resrec.RecordType == kDNSRecordTypeUnregistered && + hi->arv6.resrec.RecordType == kDNSRecordTypeUnregistered) + { + if (hi->natinfo.clientContext) mDNS_StopNATOperation_internal(m, &hi->natinfo); + hi->natinfo.clientContext = mDNSNULL; + mDNSPlatformMemFree(hi); // free hi when both v4 and v6 AuthRecs deallocated + } + } + return; + } + + if (result) + { + // don't unlink or free - we can retry when we get a new address/router + if (rr->resrec.rrtype == kDNSType_A) + LogMsg("HostnameCallback: Error %d for registration of %##s IP %.4a", result, rr->resrec.name->c, &rr->resrec.rdata->u.ipv4); + else + LogMsg("HostnameCallback: Error %d for registration of %##s IP %.16a", result, rr->resrec.name->c, &rr->resrec.rdata->u.ipv6); + if (!hi) { mDNSPlatformMemFree(rr); return; } + if (rr->state != regState_Unregistered) LogMsg("Error: HostnameCallback invoked with error code for record not in regState_Unregistered!"); + + if (hi->arv4.state == regState_Unregistered && + hi->arv6.state == regState_Unregistered) + { + // only deliver status if both v4 and v6 fail + rr->RecordContext = (void *)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 + mDNS_Lock(m); + m->NextSRVUpdate = NonZeroTime(m->timenow); + mDNS_Unlock(m); + + // Deliver success to client + if (!hi) { LogMsg("HostnameCallback invoked with orphaned address record"); return; } + if (rr->resrec.rrtype == kDNSType_A) + LogInfo("Registered hostname %##s IP %.4a", rr->resrec.name->c, &rr->resrec.rdata->u.ipv4); + else + LogInfo("Registered hostname %##s IP %.16a", rr->resrec.name->c, &rr->resrec.rdata->u.ipv6); + + rr->RecordContext = (void *)hi->StatusContext; + if (hi->StatusCallback) + hi->StatusCallback(m, rr, result); // client may NOT make API calls here + rr->RecordContext = (void *)hi; +} mDNSlocal void FoundStaticHostname(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) - { - const domainname *pktname = &answer->rdata->u.name; - domainname *storedname = &m->StaticHostname; - HostnameInfo *h = m->Hostnames; - - (void)question; - - if (answer->rdlength != 0) - LogInfo("FoundStaticHostname: question %##s -> answer %##s (%s)", question->qname.c, answer->rdata->u.name.c, AddRecord ? "ADD" : "RMV"); - else - LogInfo("FoundStaticHostname: question %##s -> answer NULL (%s)", question->qname.c, AddRecord ? "ADD" : "RMV"); - - if (AddRecord && answer->rdlength != 0 && !SameDomainName(pktname, storedname)) - { - AssignDomainName(storedname, pktname); - while (h) - { - if (h->arv4.state == regState_Pending || h->arv4.state == regState_NATMap || h->arv6.state == regState_Pending) - { - // if we're in the process of registering a dynamic hostname, delay SRV update so we don't have to reregister services if the dynamic name succeeds - m->NextSRVUpdate = NonZeroTime(m->timenow + 5 * mDNSPlatformOneSecond); - debugf("FoundStaticHostname: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow); - return; - } - h = h->next; - } - mDNS_Lock(m); - m->NextSRVUpdate = NonZeroTime(m->timenow); - mDNS_Unlock(m); - } - else if (!AddRecord && SameDomainName(pktname, storedname)) - { - mDNS_Lock(m); - storedname->c[0] = 0; - m->NextSRVUpdate = NonZeroTime(m->timenow); - mDNS_Unlock(m); - } - } +{ + const domainname *pktname = &answer->rdata->u.name; + domainname *storedname = &m->StaticHostname; + HostnameInfo *h = m->Hostnames; + + (void)question; + + if (answer->rdlength != 0) + LogInfo("FoundStaticHostname: question %##s -> answer %##s (%s)", question->qname.c, answer->rdata->u.name.c, AddRecord ? "ADD" : "RMV"); + else + LogInfo("FoundStaticHostname: question %##s -> answer NULL (%s)", question->qname.c, AddRecord ? "ADD" : "RMV"); + + if (AddRecord && answer->rdlength != 0 && !SameDomainName(pktname, storedname)) + { + AssignDomainName(storedname, pktname); + while (h) + { + if (h->arv4.state == regState_Pending || h->arv4.state == regState_NATMap || h->arv6.state == regState_Pending) + { + // if we're in the process of registering a dynamic hostname, delay SRV update so we don't have to reregister services if the dynamic name succeeds + m->NextSRVUpdate = NonZeroTime(m->timenow + 5 * mDNSPlatformOneSecond); + debugf("FoundStaticHostname: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow); + return; + } + h = h->next; + } + mDNS_Lock(m); + m->NextSRVUpdate = NonZeroTime(m->timenow); + mDNS_Unlock(m); + } + else if (!AddRecord && SameDomainName(pktname, storedname)) + { + mDNS_Lock(m); + storedname->c[0] = 0; + m->NextSRVUpdate = NonZeroTime(m->timenow); + mDNS_Unlock(m); + } +} // Called with lock held mDNSlocal void GetStaticHostname(mDNS *m) - { - char buf[MAX_REVERSE_MAPPING_NAME_V4]; - DNSQuestion *q = &m->ReverseMap; - mDNSu8 *ip = m->AdvertisedV4.ip.v4.b; - mStatus err; - - if (m->ReverseMap.ThisQInterval != -1) return; // already running - if (mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4)) return; - - mDNSPlatformMemZero(q, sizeof(*q)); - // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code - mDNS_snprintf(buf, sizeof(buf), "%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->ReturnIntermed = mDNStrue; - q->SuppressUnusable = mDNSfalse; - q->SearchListIndex = 0; - q->AppendSearchDomains = 0; - q->RetryWithSearchDomains = mDNSfalse; - q->TimeoutQuestion = 0; - q->WakeOnResolve = 0; - q->qnameOrig = mDNSNULL; - q->QuestionCallback = FoundStaticHostname; - q->QuestionContext = mDNSNULL; - - LogInfo("GetStaticHostname: %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - err = mDNS_StartQuery_internal(m, q); - if (err) LogMsg("Error: GetStaticHostname - StartQuery returned error %d", err); - } +{ + char buf[MAX_REVERSE_MAPPING_NAME_V4]; + DNSQuestion *q = &m->ReverseMap; + mDNSu8 *ip = m->AdvertisedV4.ip.v4.b; + mStatus err; + + if (m->ReverseMap.ThisQInterval != -1) return; // already running + if (mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4)) return; + + mDNSPlatformMemZero(q, sizeof(*q)); + // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code + mDNS_snprintf(buf, sizeof(buf), "%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->flags = 0; + q->Target = zeroAddr; + q->qtype = kDNSType_PTR; + q->qclass = kDNSClass_IN; + q->LongLived = mDNSfalse; + q->ExpectUnique = mDNSfalse; + q->ForceMCast = mDNSfalse; + q->ReturnIntermed = mDNStrue; + q->SuppressUnusable = mDNSfalse; + q->SearchListIndex = 0; + q->AppendSearchDomains = 0; + q->RetryWithSearchDomains = mDNSfalse; + q->TimeoutQuestion = 0; + q->WakeOnResolve = 0; + q->UseBrackgroundTrafficClass = mDNSfalse; + q->ValidationRequired = 0; + q->ValidatingResponse = 0; + q->qnameOrig = mDNSNULL; + q->QuestionCallback = FoundStaticHostname; + q->QuestionContext = mDNSNULL; + + LogInfo("GetStaticHostname: %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + err = mDNS_StartQuery_internal(m, q); + if (err) LogMsg("Error: GetStaticHostname - StartQuery returned error %d", err); +} mDNSexport void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext) - { - HostnameInfo **ptr = &m->Hostnames; +{ + HostnameInfo **ptr = &m->Hostnames; - LogInfo("mDNS_AddDynDNSHostName %##s", fqdn); + LogInfo("mDNS_AddDynDNSHostName %##s", fqdn); - while (*ptr && !SameDomainName(fqdn, &(*ptr)->fqdn)) ptr = &(*ptr)->next; - if (*ptr) { LogMsg("DynDNSHostName %##s already in list", fqdn->c); return; } + while (*ptr && !SameDomainName(fqdn, &(*ptr)->fqdn)) ptr = &(*ptr)->next; + if (*ptr) { LogMsg("DynDNSHostName %##s already in list", fqdn->c); return; } - // allocate and format new address record - *ptr = mDNSPlatformMemAllocate(sizeof(**ptr)); - if (!*ptr) { LogMsg("ERROR: mDNS_AddDynDNSHostName - malloc"); return; } + // allocate and format new address record + *ptr = mDNSPlatformMemAllocate(sizeof(**ptr)); + if (!*ptr) { LogMsg("ERROR: mDNS_AddDynDNSHostName - malloc"); return; } - mDNSPlatformMemZero(*ptr, sizeof(**ptr)); - AssignDomainName(&(*ptr)->fqdn, fqdn); - (*ptr)->arv4.state = regState_Unregistered; - (*ptr)->arv6.state = regState_Unregistered; - (*ptr)->StatusCallback = StatusCallback; - (*ptr)->StatusContext = StatusContext; + mDNSPlatformMemZero(*ptr, sizeof(**ptr)); + AssignDomainName(&(*ptr)->fqdn, fqdn); + (*ptr)->arv4.state = regState_Unregistered; + (*ptr)->arv6.state = regState_Unregistered; + (*ptr)->StatusCallback = StatusCallback; + (*ptr)->StatusContext = StatusContext; - AdvertiseHostname(m, *ptr); - } + AdvertiseHostname(m, *ptr); +} mDNSexport void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn) - { - HostnameInfo **ptr = &m->Hostnames; - - LogInfo("mDNS_RemoveDynDNSHostName %##s", fqdn); - - while (*ptr && !SameDomainName(fqdn, &(*ptr)->fqdn)) ptr = &(*ptr)->next; - if (!*ptr) LogMsg("mDNS_RemoveDynDNSHostName: no such domainname %##s", fqdn->c); - else - { - HostnameInfo *hi = *ptr; - // We do it this way because, if we have no active v6 record, the "mDNS_Deregister_internal(m, &hi->arv4);" - // below could free the memory, and we have to make sure we don't touch hi fields after that. - mDNSBool f4 = hi->arv4.resrec.RecordType != kDNSRecordTypeUnregistered && hi->arv4.state != regState_Unregistered; - mDNSBool f6 = hi->arv6.resrec.RecordType != kDNSRecordTypeUnregistered && hi->arv6.state != regState_Unregistered; - if (f4) LogInfo("mDNS_RemoveDynDNSHostName removing v4 %##s", fqdn); - if (f6) LogInfo("mDNS_RemoveDynDNSHostName removing v6 %##s", fqdn); - *ptr = (*ptr)->next; // unlink - if (f4) mDNS_Deregister_internal(m, &hi->arv4, mDNS_Dereg_normal); - if (f6) mDNS_Deregister_internal(m, &hi->arv6, mDNS_Dereg_normal); - // When both deregistrations complete we'll free the memory in the mStatus_MemFree callback - } - if (!m->mDNS_busy) LogMsg("mDNS_RemoveDynDNSHostName: ERROR: Lock not held"); - m->NextSRVUpdate = NonZeroTime(m->timenow); - } +{ + HostnameInfo **ptr = &m->Hostnames; + + LogInfo("mDNS_RemoveDynDNSHostName %##s", fqdn); + + while (*ptr && !SameDomainName(fqdn, &(*ptr)->fqdn)) ptr = &(*ptr)->next; + if (!*ptr) LogMsg("mDNS_RemoveDynDNSHostName: no such domainname %##s", fqdn->c); + else + { + HostnameInfo *hi = *ptr; + // We do it this way because, if we have no active v6 record, the "mDNS_Deregister_internal(m, &hi->arv4);" + // below could free the memory, and we have to make sure we don't touch hi fields after that. + mDNSBool f4 = hi->arv4.resrec.RecordType != kDNSRecordTypeUnregistered && hi->arv4.state != regState_Unregistered; + mDNSBool f6 = hi->arv6.resrec.RecordType != kDNSRecordTypeUnregistered && hi->arv6.state != regState_Unregistered; + if (f4) LogInfo("mDNS_RemoveDynDNSHostName removing v4 %##s", fqdn); + if (f6) LogInfo("mDNS_RemoveDynDNSHostName removing v6 %##s", fqdn); + *ptr = (*ptr)->next; // unlink + if (f4) mDNS_Deregister_internal(m, &hi->arv4, mDNS_Dereg_normal); + if (f6) mDNS_Deregister_internal(m, &hi->arv6, mDNS_Dereg_normal); + // When both deregistrations complete we'll free the memory in the mStatus_MemFree callback + } + if (!m->mDNS_busy) LogMsg("mDNS_RemoveDynDNSHostName: ERROR: Lock not held"); + m->NextSRVUpdate = NonZeroTime(m->timenow); +} // Currently called without holding the lock // Maybe we should change that? mDNSexport void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router) - { - mDNSBool v4Changed, v6Changed, RouterChanged; - - if (m->mDNS_busy != m->mDNS_reentrancy) - LogMsg("mDNS_SetPrimaryInterfaceInfo: mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); - - if (v4addr && v4addr->type != mDNSAddrType_IPv4) { LogMsg("mDNS_SetPrimaryInterfaceInfo v4 address - incorrect type. Discarding. %#a", v4addr); return; } - if (v6addr && v6addr->type != mDNSAddrType_IPv6) { LogMsg("mDNS_SetPrimaryInterfaceInfo v6 address - incorrect type. Discarding. %#a", v6addr); return; } - if (router && router->type != mDNSAddrType_IPv4) { LogMsg("mDNS_SetPrimaryInterfaceInfo passed non-v4 router. Discarding. %#a", router); return; } - - mDNS_Lock(m); - - v4Changed = !mDNSSameIPv4Address(m->AdvertisedV4.ip.v4, v4addr ? v4addr->ip.v4 : zerov4Addr); - v6Changed = !mDNSSameIPv6Address(m->AdvertisedV6.ip.v6, v6addr ? v6addr->ip.v6 : zerov6Addr); - RouterChanged = !mDNSSameIPv4Address(m->Router.ip.v4, router ? router->ip.v4 : zerov4Addr); - - if (v4addr && (v4Changed || RouterChanged)) - debugf("mDNS_SetPrimaryInterfaceInfo: address changed from %#a to %#a", &m->AdvertisedV4, v4addr); - - if (v4addr) m->AdvertisedV4 = *v4addr; else m->AdvertisedV4.ip.v4 = zerov4Addr; - if (v6addr) m->AdvertisedV6 = *v6addr; else m->AdvertisedV6.ip.v6 = zerov6Addr; - if (router) m->Router = *router; else m->Router .ip.v4 = zerov4Addr; - // setting router to zero indicates that nat mappings must be reestablished when router is reset - - if (v4Changed || RouterChanged || v6Changed) - { - HostnameInfo *i; - LogInfo("mDNS_SetPrimaryInterfaceInfo: %s%s%s%#a %#a %#a", - v4Changed ? "v4Changed " : "", - RouterChanged ? "RouterChanged " : "", - v6Changed ? "v6Changed " : "", v4addr, v6addr, router); - - for (i = m->Hostnames; i; i = i->next) - { - LogInfo("mDNS_SetPrimaryInterfaceInfo updating host name registrations for %##s", i->fqdn.c); - - if (i->arv4.resrec.RecordType > kDNSRecordTypeDeregistering && - !mDNSSameIPv4Address(i->arv4.resrec.rdata->u.ipv4, m->AdvertisedV4.ip.v4)) - { - LogInfo("mDNS_SetPrimaryInterfaceInfo deregistering %s", ARDisplayString(m, &i->arv4)); - mDNS_Deregister_internal(m, &i->arv4, mDNS_Dereg_normal); - } - - if (i->arv6.resrec.RecordType > kDNSRecordTypeDeregistering && - !mDNSSameIPv6Address(i->arv6.resrec.rdata->u.ipv6, m->AdvertisedV6.ip.v6)) - { - LogInfo("mDNS_SetPrimaryInterfaceInfo deregistering %s", ARDisplayString(m, &i->arv6)); - mDNS_Deregister_internal(m, &i->arv6, mDNS_Dereg_normal); - } - - // AdvertiseHostname will only register new address records. - // For records still in the process of deregistering it will ignore them, and let the mStatus_MemFree callback handle them. - AdvertiseHostname(m, i); - } - - if (v4Changed || RouterChanged) - { - // If we have a non-zero IPv4 address, we should try immediately to see if we have a NAT gateway - // If we have no IPv4 address, we don't want to be in quite such a hurry to report failures to our clients - // Sleeping server sometimes briefly disappears over Back to My Mac after it wakes up - m->ExternalAddress = zerov4Addr; - m->retryIntervalGetAddr = NATMAP_INIT_RETRY; - m->retryGetAddr = m->timenow + (v4addr ? 0 : mDNSPlatformOneSecond * 5); - m->NextScheduledNATOp = m->timenow; - m->LastNATMapResultCode = NATErr_None; +{ + mDNSBool v4Changed, v6Changed, RouterChanged; + + if (m->mDNS_busy != m->mDNS_reentrancy) + LogMsg("mDNS_SetPrimaryInterfaceInfo: mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + + if (v4addr && v4addr->type != mDNSAddrType_IPv4) { LogMsg("mDNS_SetPrimaryInterfaceInfo v4 address - incorrect type. Discarding. %#a", v4addr); return; } + if (v6addr && v6addr->type != mDNSAddrType_IPv6) { LogMsg("mDNS_SetPrimaryInterfaceInfo v6 address - incorrect type. Discarding. %#a", v6addr); return; } + if (router && router->type != mDNSAddrType_IPv4) { LogMsg("mDNS_SetPrimaryInterfaceInfo passed non-v4 router. Discarding. %#a", router); return; } + + mDNS_Lock(m); + + v4Changed = !mDNSSameIPv4Address(m->AdvertisedV4.ip.v4, v4addr ? v4addr->ip.v4 : zerov4Addr); + v6Changed = !mDNSSameIPv6Address(m->AdvertisedV6.ip.v6, v6addr ? v6addr->ip.v6 : zerov6Addr); + RouterChanged = !mDNSSameIPv4Address(m->Router.ip.v4, router ? router->ip.v4 : zerov4Addr); + + if (v4addr && (v4Changed || RouterChanged)) + debugf("mDNS_SetPrimaryInterfaceInfo: address changed from %#a to %#a", &m->AdvertisedV4, v4addr); + + if (v4addr) m->AdvertisedV4 = *v4addr;else m->AdvertisedV4.ip.v4 = zerov4Addr; + if (v6addr) m->AdvertisedV6 = *v6addr;else m->AdvertisedV6.ip.v6 = zerov6Addr; + if (router) m->Router = *router;else m->Router.ip.v4 = zerov4Addr; + // setting router to zero indicates that nat mappings must be reestablished when router is reset + + if (v4Changed || RouterChanged || v6Changed) + { + HostnameInfo *i; + LogInfo("mDNS_SetPrimaryInterfaceInfo: %s%s%s%#a %#a %#a", + v4Changed ? "v4Changed " : "", + RouterChanged ? "RouterChanged " : "", + v6Changed ? "v6Changed " : "", v4addr, v6addr, router); + + for (i = m->Hostnames; i; i = i->next) + { + LogInfo("mDNS_SetPrimaryInterfaceInfo updating host name registrations for %##s", i->fqdn.c); + + if (i->arv4.resrec.RecordType > kDNSRecordTypeDeregistering && + !mDNSSameIPv4Address(i->arv4.resrec.rdata->u.ipv4, m->AdvertisedV4.ip.v4)) + { + LogInfo("mDNS_SetPrimaryInterfaceInfo deregistering %s", ARDisplayString(m, &i->arv4)); + mDNS_Deregister_internal(m, &i->arv4, mDNS_Dereg_normal); + } + + if (i->arv6.resrec.RecordType > kDNSRecordTypeDeregistering && + !mDNSSameIPv6Address(i->arv6.resrec.rdata->u.ipv6, m->AdvertisedV6.ip.v6)) + { + LogInfo("mDNS_SetPrimaryInterfaceInfo deregistering %s", ARDisplayString(m, &i->arv6)); + mDNS_Deregister_internal(m, &i->arv6, mDNS_Dereg_normal); + } + + // AdvertiseHostname will only register new address records. + // For records still in the process of deregistering it will ignore them, and let the mStatus_MemFree callback handle them. + AdvertiseHostname(m, i); + } + + if (v4Changed || RouterChanged) + { + // If we have a non-zero IPv4 address, we should try immediately to see if we have a NAT gateway + // If we have no IPv4 address, we don't want to be in quite such a hurry to report failures to our clients + // Sleeping server sometimes briefly disappears over Back to My Mac after it wakes up + m->ExternalAddress = zerov4Addr; + m->retryIntervalGetAddr = NATMAP_INIT_RETRY; + m->retryGetAddr = m->timenow + (v4addr ? 0 : mDNSPlatformOneSecond * 5); + m->NextScheduledNATOp = m->timenow; + m->LastNATMapResultCode = NATErr_None; #ifdef _LEGACY_NAT_TRAVERSAL_ - LNT_ClearState(m); + LNT_ClearState(m); #endif // _LEGACY_NAT_TRAVERSAL_ - LogInfo("mDNS_SetPrimaryInterfaceInfo:%s%s: retryGetAddr in %d %d", - v4Changed ? " v4Changed" : "", - RouterChanged ? " RouterChanged" : "", - m->retryGetAddr - m->timenow, m->timenow); - } - - if (m->ReverseMap.ThisQInterval != -1) mDNS_StopQuery_internal(m, &m->ReverseMap); - m->StaticHostname.c[0] = 0; - - m->NextSRVUpdate = NonZeroTime(m->timenow); - + LogInfo("mDNS_SetPrimaryInterfaceInfo:%s%s: retryGetAddr in %d %d", + v4Changed ? " v4Changed" : "", + RouterChanged ? " RouterChanged" : "", + m->retryGetAddr - m->timenow, m->timenow); + } + + if (m->ReverseMap.ThisQInterval != -1) mDNS_StopQuery_internal(m, &m->ReverseMap); + m->StaticHostname.c[0] = 0; + + m->NextSRVUpdate = NonZeroTime(m->timenow); + #if APPLE_OSX_mDNSResponder - if (RouterChanged) uuid_generate(m->asl_uuid); - UpdateAutoTunnelDomainStatuses(m); + if (RouterChanged) uuid_generate(m->asl_uuid); + UpdateAutoTunnelDomainStatuses(m); #endif - } + } - mDNS_Unlock(m); - } + mDNS_Unlock(m); +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -2429,939 +2439,941 @@ mDNSexport void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, co #endif mDNSlocal mStatus ParseTSIGError(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const domainname *const displayname) - { - const mDNSu8 *ptr; - mStatus err = mStatus_NoError; - int i; - - ptr = LocateAdditionals(msg, end); - if (!ptr) goto finish; - - for (i = 0; i < msg->h.numAdditionals; i++) - { - ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec); - if (!ptr) goto finish; - if (m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_TSIG) - { - mDNSu32 macsize; - mDNSu8 *rd = m->rec.r.resrec.rdata->u.data; - mDNSu8 *rdend = rd + m->rec.r.resrec.rdlength; - int alglen = DomainNameLengthLimit(&m->rec.r.resrec.rdata->u.name, rdend); - if (alglen > MAX_DOMAIN_NAME) 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", displayname->c, err); err = mStatus_UnknownErr; } - goto finish; - } - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - } - - finish: - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - return err; - } +{ + const mDNSu8 *ptr; + mStatus err = mStatus_NoError; + int i; + + ptr = LocateAdditionals(msg, end); + if (!ptr) goto finish; + + for (i = 0; i < msg->h.numAdditionals; i++) + { + ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec); + if (!ptr) goto finish; + if (m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_TSIG) + { + mDNSu32 macsize; + mDNSu8 *rd = m->rec.r.resrec.rdata->u.data; + mDNSu8 *rdend = rd + m->rec.r.resrec.rdlength; + int alglen = DomainNameLengthLimit(&m->rec.r.resrec.rdata->u.name, rdend); + if (alglen > MAX_DOMAIN_NAME) 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", displayname->c, err); err = mStatus_UnknownErr; } + goto finish; + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + +finish: + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + return err; +} mDNSlocal mStatus checkUpdateResult(mDNS *const m, const domainname *const displayname, const mDNSu8 rcode, const DNSMessage *const msg, const mDNSu8 *const end) - { - (void)msg; // currently unused, needed for TSIG errors - if (!rcode) return mStatus_NoError; - else if (rcode == kDNSFlag1_RC_YXDomain) - { - debugf("name in use: %##s", displayname->c); - return mStatus_NameConflict; - } - else if (rcode == kDNSFlag1_RC_Refused) - { - LogMsg("Update %##s refused", displayname->c); - return mStatus_Refused; - } - else if (rcode == kDNSFlag1_RC_NXRRSet) - { - LogMsg("Reregister refused (NXRRSET): %##s", displayname->c); - return mStatus_NoSuchRecord; - } - else if (rcode == kDNSFlag1_RC_NotAuth) - { - // TSIG errors should come with FormErr 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_FormErr) - { - 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", displayname->c, rcode); - return mStatus_UnknownErr; - } - } +{ + (void)msg; // currently unused, needed for TSIG errors + if (!rcode) return mStatus_NoError; + else if (rcode == kDNSFlag1_RC_YXDomain) + { + debugf("name in use: %##s", displayname->c); + return mStatus_NameConflict; + } + else if (rcode == kDNSFlag1_RC_Refused) + { + LogMsg("Update %##s refused", displayname->c); + return mStatus_Refused; + } + else if (rcode == kDNSFlag1_RC_NXRRSet) + { + LogMsg("Reregister refused (NXRRSET): %##s", displayname->c); + return mStatus_NoSuchRecord; + } + else if (rcode == kDNSFlag1_RC_NotAuth) + { + // TSIG errors should come with FormErr 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_FormErr) + { + 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", displayname->c, rcode); + return mStatus_UnknownErr; + } +} // We add three Additional Records for unicast resource record registrations // which is a function of AuthInfo and AutoTunnel properties mDNSlocal mDNSu32 RRAdditionalSize(mDNS *const m, DomainAuthInfo *AuthInfo) - { - mDNSu32 leaseSize, hinfoSize, tsigSize; - mDNSu32 rr_base_size = 10; // type (2) class (2) TTL (4) rdlength (2) - - // OPT RR : Emptyname(.) + base size + rdataOPT - leaseSize = 1 + rr_base_size + sizeof(rdataOPT); - - // HINFO: Resource Record Name + base size + RDATA - // HINFO is added only for autotunnels - hinfoSize = 0; - if (AuthInfo && AuthInfo->AutoTunnel) - hinfoSize = (m->hostlabel.c[0] + 1) + DomainNameLength(&AuthInfo->domain) + - rr_base_size + (2 + m->HIHardware.c[0] + m->HISoftware.c[0]); - - //TSIG: Resource Record Name + base size + RDATA - // RDATA: - // Algorithm name: hmac-md5.sig-alg.reg.int (8+7+3+3 + 5 bytes for length = 26 bytes) - // Time: 6 bytes - // Fudge: 2 bytes - // Mac Size: 2 bytes - // Mac: 16 bytes - // ID: 2 bytes - // Error: 2 bytes - // Len: 2 bytes - // Total: 58 bytes - tsigSize = 0; - if (AuthInfo) tsigSize = DomainNameLength(&AuthInfo->keyname) + rr_base_size + 58; - - return (leaseSize + hinfoSize + tsigSize); - } +{ + mDNSu32 leaseSize, hinfoSize, tsigSize; + mDNSu32 rr_base_size = 10; // type (2) class (2) TTL (4) rdlength (2) + + // OPT RR : Emptyname(.) + base size + rdataOPT + leaseSize = 1 + rr_base_size + sizeof(rdataOPT); + + // HINFO: Resource Record Name + base size + RDATA + // HINFO is added only for autotunnels + hinfoSize = 0; + if (AuthInfo && AuthInfo->AutoTunnel) + hinfoSize = (m->hostlabel.c[0] + 1) + DomainNameLength(&AuthInfo->domain) + + rr_base_size + (2 + m->HIHardware.c[0] + m->HISoftware.c[0]); + + //TSIG: Resource Record Name + base size + RDATA + // RDATA: + // Algorithm name: hmac-md5.sig-alg.reg.int (8+7+3+3 + 5 bytes for length = 26 bytes) + // Time: 6 bytes + // Fudge: 2 bytes + // Mac Size: 2 bytes + // Mac: 16 bytes + // ID: 2 bytes + // Error: 2 bytes + // Len: 2 bytes + // Total: 58 bytes + tsigSize = 0; + if (AuthInfo) tsigSize = DomainNameLength(&AuthInfo->keyname) + rr_base_size + 58; + + return (leaseSize + hinfoSize + tsigSize); +} //Note: Make sure that RREstimatedSize is updated accordingly if anything that is done here //would modify rdlength/rdestimate mDNSlocal mDNSu8* BuildUpdateMessage(mDNS *const m, mDNSu8 *ptr, AuthRecord *rr, mDNSu8 *limit) - { - //If this record is deregistering, then just send the deletion record - if (rr->state == regState_DeregPending) - { - rr->expire = 0; // Indicate that we have no active registration any more - ptr = putDeletionRecordWithLimit(&m->omsg, ptr, &rr->resrec, limit); - if (!ptr) goto exit; - return ptr; - } - - // This is a common function to both sending an update in a group or individual - // records separately. Hence, we change the state here. - if (rr->state == regState_Registered) rr->state = regState_Refresh; - if (rr->state != regState_Refresh && rr->state != regState_UpdatePending) - rr->state = regState_Pending; - - // For Advisory records like e.g., _services._dns-sd, which is shared, don't send goodbyes as multiple - // host might be registering records and deregistering from one does not make sense - if (rr->resrec.RecordType != kDNSRecordTypeAdvisory) rr->RequireGoodbye = mDNStrue; - - if ((rr->resrec.rrtype == kDNSType_SRV) && (rr->AutoTarget == Target_AutoHostAndNATMAP) && - !mDNSIPPortIsZero(rr->NATinfo.ExternalPort)) - { - rr->resrec.rdata->u.srv.port = rr->NATinfo.ExternalPort; - } - - if (rr->state == regState_UpdatePending) - { - // delete old RData - SetNewRData(&rr->resrec, rr->OrigRData, rr->OrigRDLen); - if (!(ptr = putDeletionRecordWithLimit(&m->omsg, ptr, &rr->resrec, limit))) goto exit; // delete old rdata - - // add new RData - SetNewRData(&rr->resrec, rr->InFlightRData, rr->InFlightRDLen); - if (!(ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl, limit))) goto exit; - } - else - { - if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique || rr->resrec.RecordType == kDNSRecordTypeVerified) - { - // KnownUnique : Delete any previous value - // For Unicast registrations, we don't verify that it is unique, but set to verified and hence we want to - // delete any previous value - ptr = putDeleteRRSetWithLimit(&m->omsg, ptr, rr->resrec.name, rr->resrec.rrtype, limit); - if (!ptr) goto exit; - } - else if (rr->resrec.RecordType != kDNSRecordTypeShared) - { - // For now don't do this, until we have the logic for intelligent grouping of individual records into logical service record sets - //ptr = putPrereqNameNotInUse(rr->resrec.name, &m->omsg, ptr, end); - if (!ptr) goto exit; - } - - ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl, limit); - if (!ptr) goto exit; - } - - return ptr; +{ + //If this record is deregistering, then just send the deletion record + if (rr->state == regState_DeregPending) + { + rr->expire = 0; // Indicate that we have no active registration any more + ptr = putDeletionRecordWithLimit(&m->omsg, ptr, &rr->resrec, limit); + if (!ptr) goto exit; + return ptr; + } + + // This is a common function to both sending an update in a group or individual + // records separately. Hence, we change the state here. + if (rr->state == regState_Registered) rr->state = regState_Refresh; + if (rr->state != regState_Refresh && rr->state != regState_UpdatePending) + rr->state = regState_Pending; + + // For Advisory records like e.g., _services._dns-sd, which is shared, don't send goodbyes as multiple + // host might be registering records and deregistering from one does not make sense + if (rr->resrec.RecordType != kDNSRecordTypeAdvisory) rr->RequireGoodbye = mDNStrue; + + if ((rr->resrec.rrtype == kDNSType_SRV) && (rr->AutoTarget == Target_AutoHostAndNATMAP) && + !mDNSIPPortIsZero(rr->NATinfo.ExternalPort)) + { + rr->resrec.rdata->u.srv.port = rr->NATinfo.ExternalPort; + } + + if (rr->state == regState_UpdatePending) + { + // delete old RData + SetNewRData(&rr->resrec, rr->OrigRData, rr->OrigRDLen); + if (!(ptr = putDeletionRecordWithLimit(&m->omsg, ptr, &rr->resrec, limit))) goto exit; // delete old rdata + + // add new RData + SetNewRData(&rr->resrec, rr->InFlightRData, rr->InFlightRDLen); + if (!(ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl, limit))) goto exit; + } + else + { + if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique || rr->resrec.RecordType == kDNSRecordTypeVerified) + { + // KnownUnique : Delete any previous value + // For Unicast registrations, we don't verify that it is unique, but set to verified and hence we want to + // delete any previous value + ptr = putDeleteRRSetWithLimit(&m->omsg, ptr, rr->resrec.name, rr->resrec.rrtype, limit); + if (!ptr) goto exit; + } + else if (rr->resrec.RecordType != kDNSRecordTypeShared) + { + // For now don't do this, until we have the logic for intelligent grouping of individual records into logical service record sets + //ptr = putPrereqNameNotInUse(rr->resrec.name, &m->omsg, ptr, end); + if (!ptr) goto exit; + } + + ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl, limit); + if (!ptr) goto exit; + } + + return ptr; exit: - LogMsg("BuildUpdateMessage: Error formatting message for %s", ARDisplayString(m, rr)); - return mDNSNULL; - } + LogMsg("BuildUpdateMessage: Error formatting message for %s", ARDisplayString(m, rr)); + return mDNSNULL; +} // Called with lock held mDNSlocal void SendRecordRegistration(mDNS *const m, AuthRecord *rr) - { - mDNSu8 *ptr = m->omsg.data; - mStatus err = mStatus_UnknownErr; - mDNSu8 *limit; - DomainAuthInfo *AuthInfo; - - // For the ability to register large TXT records, we limit the single record registrations - // to AbsoluteMaxDNSMessageData - limit = ptr + AbsoluteMaxDNSMessageData; - - AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name); - limit -= RRAdditionalSize(m, AuthInfo); - - if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("SendRecordRegistration: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); - - if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4)) - { - // We never call this function when there is no zone information . Log a message if it ever happens. - LogMsg("SendRecordRegistration: No Zone information, should not happen %s", ARDisplayString(m, rr)); - return; - } - - rr->updateid = mDNS_NewMessageID(m); - InitializeDNSMessage(&m->omsg.h, rr->updateid, UpdateReqFlags); - - // set zone - ptr = putZone(&m->omsg, ptr, limit, rr->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); - if (!ptr) goto exit; - - if (!(ptr = BuildUpdateMessage(m, ptr, rr, limit))) goto exit; - - if (rr->uselease) - { - ptr = putUpdateLeaseWithLimit(&m->omsg, ptr, DEFAULT_UPDATE_LEASE, limit); - if (!ptr) goto exit; - } - if (rr->Private) - { - LogInfo("SendRecordRegistration TCP %p %s", rr->tcp, ARDisplayString(m, rr)); - if (rr->tcp) LogInfo("SendRecordRegistration: Disposing existing TCP connection for %s", ARDisplayString(m, rr)); - if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } - if (!rr->nta) { LogMsg("SendRecordRegistration:Private:ERROR!! nta is NULL for %s", ARDisplayString(m, rr)); return; } - rr->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &rr->nta->Addr, rr->nta->Port, &rr->nta->Host, mDNSNULL, rr); - } - else - { - LogInfo("SendRecordRegistration UDP %s", ARDisplayString(m, rr)); - if (!rr->nta) { LogMsg("SendRecordRegistration:ERROR!! nta is NULL for %s", ARDisplayString(m, rr)); return; } - err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, &rr->nta->Addr, rr->nta->Port, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name)); - if (err) debugf("ERROR: SendRecordRegistration - mDNSSendDNSMessage - %d", err); - } - - SetRecordRetry(m, rr, 0); - return; +{ + mDNSu8 *ptr = m->omsg.data; + mStatus err = mStatus_UnknownErr; + mDNSu8 *limit; + DomainAuthInfo *AuthInfo; + + // For the ability to register large TXT records, we limit the single record registrations + // to AbsoluteMaxDNSMessageData + limit = ptr + AbsoluteMaxDNSMessageData; + + AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name); + limit -= RRAdditionalSize(m, AuthInfo); + + if (m->mDNS_busy != m->mDNS_reentrancy+1) + LogMsg("SendRecordRegistration: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + + if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4)) + { + // We never call this function when there is no zone information . Log a message if it ever happens. + LogMsg("SendRecordRegistration: No Zone information, should not happen %s", ARDisplayString(m, rr)); + return; + } + + rr->updateid = mDNS_NewMessageID(m); + InitializeDNSMessage(&m->omsg.h, rr->updateid, UpdateReqFlags); + + // set zone + ptr = putZone(&m->omsg, ptr, limit, rr->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); + if (!ptr) goto exit; + + if (!(ptr = BuildUpdateMessage(m, ptr, rr, limit))) goto exit; + + if (rr->uselease) + { + ptr = putUpdateLeaseWithLimit(&m->omsg, ptr, DEFAULT_UPDATE_LEASE, limit); + if (!ptr) goto exit; + } + if (rr->Private) + { + LogInfo("SendRecordRegistration TCP %p %s", rr->tcp, ARDisplayString(m, rr)); + if (rr->tcp) LogInfo("SendRecordRegistration: Disposing existing TCP connection for %s", ARDisplayString(m, rr)); + if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } + if (!rr->nta) { LogMsg("SendRecordRegistration:Private:ERROR!! nta is NULL for %s", ARDisplayString(m, rr)); return; } + rr->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &rr->nta->Addr, rr->nta->Port, &rr->nta->Host, mDNSNULL, rr); + } + else + { + LogInfo("SendRecordRegistration UDP %s", ARDisplayString(m, rr)); + if (!rr->nta) { LogMsg("SendRecordRegistration:ERROR!! nta is NULL for %s", ARDisplayString(m, rr)); return; } + err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, &rr->nta->Addr, rr->nta->Port, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name), mDNSfalse); + if (err) debugf("ERROR: SendRecordRegistration - mDNSSendDNSMessage - %d", err); + } + + SetRecordRetry(m, rr, 0); + return; exit: - LogMsg("SendRecordRegistration: Error formatting message for %s, disabling further updates", ARDisplayString(m, rr)); - // Disable this record from future updates - rr->state = regState_NoTarget; - } + LogMsg("SendRecordRegistration: Error formatting message for %s, disabling further updates", ARDisplayString(m, rr)); + // Disable this record from future updates + rr->state = regState_NoTarget; +} // Is the given record "rr" eligible for merging ? mDNSlocal mDNSBool IsRecordMergeable(mDNS *const m, AuthRecord *rr, mDNSs32 time) - { - DomainAuthInfo *info; - (void) m; //unused - // A record is eligible for merge, if the following properties are met. - // - // 1. uDNS Resource Record - // 2. It is time to send them now - // 3. It is in proper state - // 4. Update zone has been resolved - // 5. if DomainAuthInfo exists for the zone, it should not be soon deleted - // 6. Zone information is present - // 7. Update server is not zero - // 8. It has a non-null zone - // 9. It uses a lease option - // 10. DontMerge is not set - // - // Following code is implemented as separate "if" statements instead of one "if" statement - // is for better debugging purposes e.g., we know exactly what failed if debugging turned on. - - if (!AuthRecord_uDNS(rr)) return mDNSfalse; - - if (rr->LastAPTime + rr->ThisAPInterval - time > 0) - { debugf("IsRecordMergeable: Time %d not reached for %s", rr->LastAPTime + rr->ThisAPInterval - m->timenow, ARDisplayString(m, rr)); return mDNSfalse; } - - if (!rr->zone) return mDNSfalse; - - info = GetAuthInfoForName_internal(m, rr->zone); - - if (info && info->deltime && m->timenow - info->deltime >= 0) {debugf("IsRecordMergeable: Domain %##s will be deleted soon", info->domain.c); return mDNSfalse;} - - if (rr->state != regState_DeregPending && rr->state != regState_Pending && rr->state != regState_Registered && rr->state != regState_Refresh && rr->state != regState_UpdatePending) - { debugf("IsRecordMergeable: state %d not right %s", rr->state, ARDisplayString(m, rr)); return mDNSfalse; } - - if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4)) return mDNSfalse; - - if (!rr->uselease) return mDNSfalse; - - if (rr->mState == mergeState_DontMerge) {debugf("IsRecordMergeable Dontmerge true %s", ARDisplayString(m, rr));return mDNSfalse;} - debugf("IsRecordMergeable: Returning true for %s", ARDisplayString(m, rr)); - return mDNStrue; - } +{ + DomainAuthInfo *info; + (void) m; //unused + // A record is eligible for merge, if the following properties are met. + // + // 1. uDNS Resource Record + // 2. It is time to send them now + // 3. It is in proper state + // 4. Update zone has been resolved + // 5. if DomainAuthInfo exists for the zone, it should not be soon deleted + // 6. Zone information is present + // 7. Update server is not zero + // 8. It has a non-null zone + // 9. It uses a lease option + // 10. DontMerge is not set + // + // Following code is implemented as separate "if" statements instead of one "if" statement + // is for better debugging purposes e.g., we know exactly what failed if debugging turned on. + + if (!AuthRecord_uDNS(rr)) return mDNSfalse; + + if (rr->LastAPTime + rr->ThisAPInterval - time > 0) + { debugf("IsRecordMergeable: Time %d not reached for %s", rr->LastAPTime + rr->ThisAPInterval - m->timenow, ARDisplayString(m, rr)); return mDNSfalse; } + + if (!rr->zone) return mDNSfalse; + + info = GetAuthInfoForName_internal(m, rr->zone); + + if (info && info->deltime && m->timenow - info->deltime >= 0) {debugf("IsRecordMergeable: Domain %##s will be deleted soon", info->domain.c); return mDNSfalse;} + + if (rr->state != regState_DeregPending && rr->state != regState_Pending && rr->state != regState_Registered && rr->state != regState_Refresh && rr->state != regState_UpdatePending) + { debugf("IsRecordMergeable: state %d not right %s", rr->state, ARDisplayString(m, rr)); return mDNSfalse; } + + if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4)) return mDNSfalse; + + if (!rr->uselease) return mDNSfalse; + + if (rr->mState == mergeState_DontMerge) {debugf("IsRecordMergeable Dontmerge true %s", ARDisplayString(m, rr)); return mDNSfalse;} + debugf("IsRecordMergeable: Returning true for %s", ARDisplayString(m, rr)); + return mDNStrue; +} // Is the resource record "rr" eligible to merge to with "currentRR" ? mDNSlocal mDNSBool AreRecordsMergeable(mDNS *const m, AuthRecord *currentRR, AuthRecord *rr, mDNSs32 time) - { - // A record is eligible to merge with another record as long it is eligible for merge in itself - // and it has the same zone information as the other record - if (!IsRecordMergeable(m, rr, time)) return mDNSfalse; +{ + // A record is eligible to merge with another record as long it is eligible for merge in itself + // and it has the same zone information as the other record + if (!IsRecordMergeable(m, rr, time)) return mDNSfalse; - if (!SameDomainName(currentRR->zone, rr->zone)) - { debugf("AreRecordMergeable zone mismatch current rr Zone %##s, rr zone %##s", currentRR->zone->c, rr->zone->c); return mDNSfalse; } + if (!SameDomainName(currentRR->zone, rr->zone)) + { debugf("AreRecordMergeable zone mismatch current rr Zone %##s, rr zone %##s", currentRR->zone->c, rr->zone->c); return mDNSfalse; } - if (!mDNSSameIPv4Address(currentRR->nta->Addr.ip.v4, rr->nta->Addr.ip.v4)) return mDNSfalse; + if (!mDNSSameIPv4Address(currentRR->nta->Addr.ip.v4, rr->nta->Addr.ip.v4)) return mDNSfalse; - if (!mDNSSameIPPort(currentRR->nta->Port, rr->nta->Port)) return mDNSfalse; + if (!mDNSSameIPPort(currentRR->nta->Port, rr->nta->Port)) return mDNSfalse; - debugf("AreRecordsMergeable: Returning true for %s", ARDisplayString(m, rr)); - return mDNStrue; - } + debugf("AreRecordsMergeable: Returning true for %s", ARDisplayString(m, rr)); + return mDNStrue; +} // If we can't build the message successfully because of problems in pre-computing // the space, we disable merging for all the current records mDNSlocal void RRMergeFailure(mDNS *const m) - { - AuthRecord *rr; - for (rr = m->ResourceRecords; rr; rr = rr->next) - { - rr->mState = mergeState_DontMerge; - rr->SendRNow = mDNSNULL; - // Restarting the registration is much simpler than saving and restoring - // the exact time - ActivateUnicastRegistration(m, rr); - } - } +{ + AuthRecord *rr; + for (rr = m->ResourceRecords; rr; rr = rr->next) + { + rr->mState = mergeState_DontMerge; + rr->SendRNow = mDNSNULL; + // Restarting the registration is much simpler than saving and restoring + // the exact time + ActivateUnicastRegistration(m, rr); + } +} mDNSlocal void SendGroupRRMessage(mDNS *const m, AuthRecord *anchorRR, mDNSu8 *ptr, DomainAuthInfo *info) - { - mDNSu8 *limit; - if (!anchorRR) {debugf("SendGroupRRMessage: Could not merge records"); return;} - - if (info && info->AutoTunnel) limit = m->omsg.data + AbsoluteMaxDNSMessageData; - else limit = m->omsg.data + NormalMaxDNSMessageData; - - // This has to go in the additional section and hence need to be done last - ptr = putUpdateLeaseWithLimit(&m->omsg, ptr, DEFAULT_UPDATE_LEASE, limit); - if (!ptr) - { - LogMsg("SendGroupRRMessage: ERROR: Could not put lease option, failing the group registration"); - // if we can't put the lease, we need to undo the merge - RRMergeFailure(m); - return; - } - if (anchorRR->Private) - { - if (anchorRR->tcp) debugf("SendGroupRRMessage: Disposing existing TCP connection for %s", ARDisplayString(m, anchorRR)); - if (anchorRR->tcp) { DisposeTCPConn(anchorRR->tcp); anchorRR->tcp = mDNSNULL; } - if (!anchorRR->nta) { LogMsg("SendGroupRRMessage:ERROR!! nta is NULL for %s", ARDisplayString(m, anchorRR)); return; } - anchorRR->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &anchorRR->nta->Addr, anchorRR->nta->Port, &anchorRR->nta->Host, mDNSNULL, anchorRR); - if (!anchorRR->tcp) LogInfo("SendGroupRRMessage: Cannot establish TCP connection for %s", ARDisplayString(m, anchorRR)); - else LogInfo("SendGroupRRMessage: Sent a group update ID: %d start %p, end %p, limit %p", mDNSVal16(m->omsg.h.id), m->omsg.data, ptr, limit); - } - else - { - mStatus err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, &anchorRR->nta->Addr, anchorRR->nta->Port, mDNSNULL, info); - if (err) LogInfo("SendGroupRRMessage: Cannot send UDP message for %s", ARDisplayString(m, anchorRR)); - else LogInfo("SendGroupRRMessage: Sent a group UDP update ID: %d start %p, end %p, limit %p", mDNSVal16(m->omsg.h.id), m->omsg.data, ptr, limit); - } - return; - } +{ + mDNSu8 *limit; + if (!anchorRR) {debugf("SendGroupRRMessage: Could not merge records"); return;} + + if (info && info->AutoTunnel) limit = m->omsg.data + AbsoluteMaxDNSMessageData; + else limit = m->omsg.data + NormalMaxDNSMessageData; + + // This has to go in the additional section and hence need to be done last + ptr = putUpdateLeaseWithLimit(&m->omsg, ptr, DEFAULT_UPDATE_LEASE, limit); + if (!ptr) + { + LogMsg("SendGroupRRMessage: ERROR: Could not put lease option, failing the group registration"); + // if we can't put the lease, we need to undo the merge + RRMergeFailure(m); + return; + } + if (anchorRR->Private) + { + if (anchorRR->tcp) debugf("SendGroupRRMessage: Disposing existing TCP connection for %s", ARDisplayString(m, anchorRR)); + if (anchorRR->tcp) { DisposeTCPConn(anchorRR->tcp); anchorRR->tcp = mDNSNULL; } + if (!anchorRR->nta) { LogMsg("SendGroupRRMessage:ERROR!! nta is NULL for %s", ARDisplayString(m, anchorRR)); return; } + anchorRR->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &anchorRR->nta->Addr, anchorRR->nta->Port, &anchorRR->nta->Host, mDNSNULL, anchorRR); + if (!anchorRR->tcp) LogInfo("SendGroupRRMessage: Cannot establish TCP connection for %s", ARDisplayString(m, anchorRR)); + else LogInfo("SendGroupRRMessage: Sent a group update ID: %d start %p, end %p, limit %p", mDNSVal16(m->omsg.h.id), m->omsg.data, ptr, limit); + } + else + { + mStatus err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, &anchorRR->nta->Addr, anchorRR->nta->Port, mDNSNULL, info, mDNSfalse); + if (err) LogInfo("SendGroupRRMessage: Cannot send UDP message for %s", ARDisplayString(m, anchorRR)); + else LogInfo("SendGroupRRMessage: Sent a group UDP update ID: %d start %p, end %p, limit %p", mDNSVal16(m->omsg.h.id), m->omsg.data, ptr, limit); + } + return; +} // As we always include the zone information and the resource records contain zone name // at the end, it will get compressed. Hence, we subtract zoneSize and add two bytes for // the compression pointer mDNSlocal mDNSu32 RREstimatedSize(AuthRecord *rr, int zoneSize) - { - int rdlength; - - // Note: Estimation of the record size has to mirror the logic in BuildUpdateMessage, otherwise estimation - // would be wrong. Currently BuildUpdateMessage calls SetNewRData in UpdatePending case. Hence, we need - // to account for that here. Otherwise, we might under estimate the size. - if (rr->state == regState_UpdatePending) - // old RData that will be deleted - // new RData that will be added - rdlength = rr->OrigRDLen + rr->InFlightRDLen; - else - rdlength = rr->resrec.rdestimate; - - if (rr->state == regState_DeregPending) - { - debugf("RREstimatedSize: ResourceRecord %##s (%s), DomainNameLength %d, zoneSize %d, rdestimate %d", - rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), DomainNameLength(rr->resrec.name), zoneSize, rdlength); - return DomainNameLength(rr->resrec.name) - zoneSize + 2 + 10 + rdlength; - } - - // For SRV, TXT, AAAA etc. that are Unique/Verified, we also send a Deletion Record - if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique || rr->resrec.RecordType == kDNSRecordTypeVerified) - { - // Deletion Record: Resource Record Name + Base size (10) + 0 - // Record: Resource Record Name (Compressed = 2) + Base size (10) + rdestimate - - debugf("RREstimatedSize: ResourceRecord %##s (%s), DomainNameLength %d, zoneSize %d, rdestimate %d", - rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), DomainNameLength(rr->resrec.name), zoneSize, rdlength); - return DomainNameLength(rr->resrec.name) - zoneSize + 2 + 10 + 2 + 10 + rdlength; - } - else - { - return DomainNameLength(rr->resrec.name) - zoneSize + 2 + 10 + rdlength; - } - } +{ + int rdlength; + + // Note: Estimation of the record size has to mirror the logic in BuildUpdateMessage, otherwise estimation + // would be wrong. Currently BuildUpdateMessage calls SetNewRData in UpdatePending case. Hence, we need + // to account for that here. Otherwise, we might under estimate the size. + if (rr->state == regState_UpdatePending) + // old RData that will be deleted + // new RData that will be added + rdlength = rr->OrigRDLen + rr->InFlightRDLen; + else + rdlength = rr->resrec.rdestimate; + + if (rr->state == regState_DeregPending) + { + debugf("RREstimatedSize: ResourceRecord %##s (%s), DomainNameLength %d, zoneSize %d, rdestimate %d", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), DomainNameLength(rr->resrec.name), zoneSize, rdlength); + return DomainNameLength(rr->resrec.name) - zoneSize + 2 + 10 + rdlength; + } + + // For SRV, TXT, AAAA etc. that are Unique/Verified, we also send a Deletion Record + if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique || rr->resrec.RecordType == kDNSRecordTypeVerified) + { + // Deletion Record: Resource Record Name + Base size (10) + 0 + // Record: Resource Record Name (Compressed = 2) + Base size (10) + rdestimate + + debugf("RREstimatedSize: ResourceRecord %##s (%s), DomainNameLength %d, zoneSize %d, rdestimate %d", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), DomainNameLength(rr->resrec.name), zoneSize, rdlength); + return DomainNameLength(rr->resrec.name) - zoneSize + 2 + 10 + 2 + 10 + rdlength; + } + else + { + return DomainNameLength(rr->resrec.name) - zoneSize + 2 + 10 + rdlength; + } +} mDNSlocal AuthRecord *MarkRRForSending(mDNS *const m) - { - AuthRecord *rr; - AuthRecord *firstRR = mDNSNULL; - - // Look for records that needs to be sent in the next two seconds (MERGE_DELAY_TIME is set to 1 second). - // The logic is as follows. - // - // 1. Record 1 finishes getting zone data and its registration gets delayed by 1 second - // 2. Record 2 comes 0.1 second later, finishes getting its zone data and its registration is also delayed by - // 1 second which is now scheduled at 1.1 second - // - // By looking for 1 second into the future (m->timenow + MERGE_DELAY_TIME below does that) we have merged both - // of the above records. Note that we can't look for records too much into the future as this will affect the - // retry logic. The first retry is scheduled at 3 seconds. Hence, we should always look smaller than that. - // Anything more than one second will affect the first retry to happen sooner. - // - // Note: As a side effect of looking one second into the future to facilitate merging, the retries happen - // one second sooner. - for (rr = m->ResourceRecords; rr; rr = rr->next) - { - if (!firstRR) - { - if (!IsRecordMergeable(m, rr, m->timenow + MERGE_DELAY_TIME)) continue; - firstRR = rr; - } - else if (!AreRecordsMergeable(m, firstRR, rr, m->timenow + MERGE_DELAY_TIME)) continue; - - if (rr->SendRNow) LogMsg("MarkRRForSending: Resourcerecord %s already marked for sending", ARDisplayString(m, rr)); - rr->SendRNow = mDNSInterfaceMark; - } - - // We parsed through all records and found something to send. The services/records might - // get registered at different times but we want the refreshes to be all merged and sent - // as one update. Hence, we accelerate some of the records so that they will sync up in - // the future. Look at the records excluding the ones that we have already sent in the - // previous pass. If it half way through its scheduled refresh/retransmit, merge them - // into this packet. - // - // Note that we only look at Registered/Refresh state to keep it simple. As we don't know - // whether the current update will fit into one or more packets, merging a resource record - // (which is in a different state) that has been scheduled for retransmit would trigger - // sending more packets. - if (firstRR) - { - int acc = 0; - for (rr = m->ResourceRecords; rr; rr = rr->next) - { - if ((rr->state != regState_Registered && rr->state != regState_Refresh) || - (rr->SendRNow == mDNSInterfaceMark) || - (!AreRecordsMergeable(m, firstRR, rr, m->timenow + rr->ThisAPInterval/2))) - continue; - rr->SendRNow = mDNSInterfaceMark; - acc++; - } - if (acc) LogInfo("MarkRRForSending: Accelereated %d records", acc); - } - return firstRR; - } +{ + AuthRecord *rr; + AuthRecord *firstRR = mDNSNULL; + + // Look for records that needs to be sent in the next two seconds (MERGE_DELAY_TIME is set to 1 second). + // The logic is as follows. + // + // 1. Record 1 finishes getting zone data and its registration gets delayed by 1 second + // 2. Record 2 comes 0.1 second later, finishes getting its zone data and its registration is also delayed by + // 1 second which is now scheduled at 1.1 second + // + // By looking for 1 second into the future (m->timenow + MERGE_DELAY_TIME below does that) we have merged both + // of the above records. Note that we can't look for records too much into the future as this will affect the + // retry logic. The first retry is scheduled at 3 seconds. Hence, we should always look smaller than that. + // Anything more than one second will affect the first retry to happen sooner. + // + // Note: As a side effect of looking one second into the future to facilitate merging, the retries happen + // one second sooner. + for (rr = m->ResourceRecords; rr; rr = rr->next) + { + if (!firstRR) + { + if (!IsRecordMergeable(m, rr, m->timenow + MERGE_DELAY_TIME)) continue; + firstRR = rr; + } + else if (!AreRecordsMergeable(m, firstRR, rr, m->timenow + MERGE_DELAY_TIME)) continue; + + if (rr->SendRNow) LogMsg("MarkRRForSending: Resourcerecord %s already marked for sending", ARDisplayString(m, rr)); + rr->SendRNow = mDNSInterfaceMark; + } + + // We parsed through all records and found something to send. The services/records might + // get registered at different times but we want the refreshes to be all merged and sent + // as one update. Hence, we accelerate some of the records so that they will sync up in + // the future. Look at the records excluding the ones that we have already sent in the + // previous pass. If it half way through its scheduled refresh/retransmit, merge them + // into this packet. + // + // Note that we only look at Registered/Refresh state to keep it simple. As we don't know + // whether the current update will fit into one or more packets, merging a resource record + // (which is in a different state) that has been scheduled for retransmit would trigger + // sending more packets. + if (firstRR) + { + int acc = 0; + for (rr = m->ResourceRecords; rr; rr = rr->next) + { + if ((rr->state != regState_Registered && rr->state != regState_Refresh) || + (rr->SendRNow == mDNSInterfaceMark) || + (!AreRecordsMergeable(m, firstRR, rr, m->timenow + rr->ThisAPInterval/2))) + continue; + rr->SendRNow = mDNSInterfaceMark; + acc++; + } + if (acc) LogInfo("MarkRRForSending: Accelereated %d records", acc); + } + return firstRR; +} mDNSlocal mDNSBool SendGroupUpdates(mDNS *const m) - { - mDNSOpaque16 msgid; - mDNSs32 spaceleft = 0; - mDNSs32 zoneSize, rrSize; - mDNSu8 *oldnext; // for debugging - mDNSu8 *next = m->omsg.data; - AuthRecord *rr; - AuthRecord *anchorRR = mDNSNULL; - int nrecords = 0; - AuthRecord *startRR = m->ResourceRecords; - mDNSu8 *limit = mDNSNULL; - DomainAuthInfo *AuthInfo = mDNSNULL; - mDNSBool sentallRecords = mDNStrue; - - - // We try to fit as many ResourceRecords as possible in AbsoluteNormal/MaxDNSMessageData. Before we start - // putting in resource records, we need to reserve space for a few things. Every group/packet should - // have the following. - // - // 1) Needs space for the Zone information (which needs to be at the beginning) - // 2) Additional section MUST have space for lease option, HINFO and TSIG option (which needs to - // to be at the end) - // - // In future we need to reserve space for the pre-requisites which also goes at the beginning. - // To accomodate pre-requisites in the future, first we walk the whole list marking records - // that can be sent in this packet and computing the space needed for these records. - // For TXT and SRV records, we delete the previous record if any by sending the same - // resource record with ANY RDATA and zero rdlen. Hence, we need to have space for both of them. - - while (startRR) - { - AuthInfo = mDNSNULL; - anchorRR = mDNSNULL; - nrecords = 0; - zoneSize = 0; - for (rr = startRR; rr; rr = rr->next) - { - if (rr->SendRNow != mDNSInterfaceMark) continue; - - rr->SendRNow = mDNSNULL; - - if (!anchorRR) - { - AuthInfo = GetAuthInfoForName_internal(m, rr->zone); - - // Though we allow single record registrations for UDP to be AbsoluteMaxDNSMessageData (See - // SendRecordRegistration) to handle large TXT records, to avoid fragmentation we limit UDP - // message to NormalMaxDNSMessageData - if (AuthInfo && AuthInfo->AutoTunnel) spaceleft = AbsoluteMaxDNSMessageData; - else spaceleft = NormalMaxDNSMessageData; - - next = m->omsg.data; - spaceleft -= RRAdditionalSize(m, AuthInfo); - if (spaceleft <= 0) - { - LogMsg("SendGroupUpdates: ERROR!!: spaceleft is zero at the beginning"); - RRMergeFailure(m); - return mDNSfalse; - } - limit = next + spaceleft; - - // Build the initial part of message before putting in the other records - msgid = mDNS_NewMessageID(m); - InitializeDNSMessage(&m->omsg.h, msgid, UpdateReqFlags); - - // We need zone information at the beginning of the packet. Length: ZNAME, ZTYPE(2), ZCLASS(2) - // zone has to be non-NULL for a record to be mergeable, hence it is safe to set/ examine zone - //without checking for NULL. - zoneSize = DomainNameLength(rr->zone) + 4; - spaceleft -= zoneSize; - if (spaceleft <= 0) - { - LogMsg("SendGroupUpdates: ERROR no space for zone information, disabling merge"); - RRMergeFailure(m); - return mDNSfalse; - } - next = putZone(&m->omsg, next, limit, rr->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); - if (!next) - { - LogMsg("SendGroupUpdates: ERROR! Cannot put zone, disabling merge"); - RRMergeFailure(m); - return mDNSfalse; - } - anchorRR = rr; - } - - rrSize = RREstimatedSize(rr, zoneSize - 4); - - if ((spaceleft - rrSize) < 0) - { - // If we can't fit even a single message, skip it, it will be sent separately - // in CheckRecordUpdates - if (!nrecords) - { - LogInfo("SendGroupUpdates: Skipping message %s, spaceleft %d, rrSize %d", ARDisplayString(m, rr), spaceleft, rrSize); - // Mark this as not sent so that the caller knows about it - rr->SendRNow = mDNSInterfaceMark; - // We need to remove the merge delay so that we can send it immediately - rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; - rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; - rr = rr->next; - anchorRR = mDNSNULL; - sentallRecords = mDNSfalse; - } - else - { - LogInfo("SendGroupUpdates:1: Parsed %d records and sending using %s, spaceleft %d, rrSize %d", nrecords, ARDisplayString(m, anchorRR), spaceleft, rrSize); - SendGroupRRMessage(m, anchorRR, next, AuthInfo); - } - break; // breaks out of for loop - } - spaceleft -= rrSize; - oldnext = next; - LogInfo("SendGroupUpdates: Building a message with resource record %s, next %p, state %d, ttl %d", ARDisplayString(m, rr), next, rr->state, rr->resrec.rroriginalttl); - if (!(next = BuildUpdateMessage(m, next, rr, limit))) - { - // We calculated the space and if we can't fit in, we had some bug in the calculation, - // disable merge completely. - LogMsg("SendGroupUpdates: ptr NULL while building message with %s", ARDisplayString(m, rr)); - RRMergeFailure(m); - return mDNSfalse; - } - // If our estimate was higher, adjust to the actual size - if ((next - oldnext) > rrSize) - LogMsg("SendGroupUpdates: ERROR!! Record size estimation is wrong for %s, Estimate %d, Actual %d, state %d", ARDisplayString(m, rr), rrSize, next - oldnext, rr->state); - else { spaceleft += rrSize; spaceleft -= (next - oldnext); } - - nrecords++; - // We could have sent an update earlier with this "rr" as anchorRR for which we never got a response. - // To preserve ordering, we blow away the previous connection before sending this. - if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL;} - rr->updateid = msgid; - - // By setting the retry time interval here, we will not be looking at these records - // again when we return to CheckGroupRecordUpdates. - SetRecordRetry(m, rr, 0); - } - // Either we have parsed all the records or stopped at "rr" above due to lack of space - startRR = rr; - } - - if (anchorRR) - { - LogInfo("SendGroupUpdates: Parsed %d records and sending using %s", nrecords, ARDisplayString(m, anchorRR)); - SendGroupRRMessage(m, anchorRR, next, AuthInfo); - } - return sentallRecords; - } +{ + mDNSOpaque16 msgid; + mDNSs32 spaceleft = 0; + mDNSs32 zoneSize, rrSize; + mDNSu8 *oldnext; // for debugging + mDNSu8 *next = m->omsg.data; + AuthRecord *rr; + AuthRecord *anchorRR = mDNSNULL; + int nrecords = 0; + AuthRecord *startRR = m->ResourceRecords; + mDNSu8 *limit = mDNSNULL; + DomainAuthInfo *AuthInfo = mDNSNULL; + mDNSBool sentallRecords = mDNStrue; + + + // We try to fit as many ResourceRecords as possible in AbsoluteNormal/MaxDNSMessageData. Before we start + // putting in resource records, we need to reserve space for a few things. Every group/packet should + // have the following. + // + // 1) Needs space for the Zone information (which needs to be at the beginning) + // 2) Additional section MUST have space for lease option, HINFO and TSIG option (which needs to + // to be at the end) + // + // In future we need to reserve space for the pre-requisites which also goes at the beginning. + // To accomodate pre-requisites in the future, first we walk the whole list marking records + // that can be sent in this packet and computing the space needed for these records. + // For TXT and SRV records, we delete the previous record if any by sending the same + // resource record with ANY RDATA and zero rdlen. Hence, we need to have space for both of them. + + while (startRR) + { + AuthInfo = mDNSNULL; + anchorRR = mDNSNULL; + nrecords = 0; + zoneSize = 0; + for (rr = startRR; rr; rr = rr->next) + { + if (rr->SendRNow != mDNSInterfaceMark) continue; + + rr->SendRNow = mDNSNULL; + + if (!anchorRR) + { + AuthInfo = GetAuthInfoForName_internal(m, rr->zone); + + // Though we allow single record registrations for UDP to be AbsoluteMaxDNSMessageData (See + // SendRecordRegistration) to handle large TXT records, to avoid fragmentation we limit UDP + // message to NormalMaxDNSMessageData + if (AuthInfo && AuthInfo->AutoTunnel) spaceleft = AbsoluteMaxDNSMessageData; + else spaceleft = NormalMaxDNSMessageData; + + next = m->omsg.data; + spaceleft -= RRAdditionalSize(m, AuthInfo); + if (spaceleft <= 0) + { + LogMsg("SendGroupUpdates: ERROR!!: spaceleft is zero at the beginning"); + RRMergeFailure(m); + return mDNSfalse; + } + limit = next + spaceleft; + + // Build the initial part of message before putting in the other records + msgid = mDNS_NewMessageID(m); + InitializeDNSMessage(&m->omsg.h, msgid, UpdateReqFlags); + + // We need zone information at the beginning of the packet. Length: ZNAME, ZTYPE(2), ZCLASS(2) + // zone has to be non-NULL for a record to be mergeable, hence it is safe to set/ examine zone + //without checking for NULL. + zoneSize = DomainNameLength(rr->zone) + 4; + spaceleft -= zoneSize; + if (spaceleft <= 0) + { + LogMsg("SendGroupUpdates: ERROR no space for zone information, disabling merge"); + RRMergeFailure(m); + return mDNSfalse; + } + next = putZone(&m->omsg, next, limit, rr->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); + if (!next) + { + LogMsg("SendGroupUpdates: ERROR! Cannot put zone, disabling merge"); + RRMergeFailure(m); + return mDNSfalse; + } + anchorRR = rr; + } + + rrSize = RREstimatedSize(rr, zoneSize - 4); + + if ((spaceleft - rrSize) < 0) + { + // If we can't fit even a single message, skip it, it will be sent separately + // in CheckRecordUpdates + if (!nrecords) + { + LogInfo("SendGroupUpdates: Skipping message %s, spaceleft %d, rrSize %d", ARDisplayString(m, rr), spaceleft, rrSize); + // Mark this as not sent so that the caller knows about it + rr->SendRNow = mDNSInterfaceMark; + // We need to remove the merge delay so that we can send it immediately + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; + rr = rr->next; + anchorRR = mDNSNULL; + sentallRecords = mDNSfalse; + } + else + { + LogInfo("SendGroupUpdates:1: Parsed %d records and sending using %s, spaceleft %d, rrSize %d", nrecords, ARDisplayString(m, anchorRR), spaceleft, rrSize); + SendGroupRRMessage(m, anchorRR, next, AuthInfo); + } + break; // breaks out of for loop + } + spaceleft -= rrSize; + oldnext = next; + LogInfo("SendGroupUpdates: Building a message with resource record %s, next %p, state %d, ttl %d", ARDisplayString(m, rr), next, rr->state, rr->resrec.rroriginalttl); + if (!(next = BuildUpdateMessage(m, next, rr, limit))) + { + // We calculated the space and if we can't fit in, we had some bug in the calculation, + // disable merge completely. + LogMsg("SendGroupUpdates: ptr NULL while building message with %s", ARDisplayString(m, rr)); + RRMergeFailure(m); + return mDNSfalse; + } + // If our estimate was higher, adjust to the actual size + if ((next - oldnext) > rrSize) + LogMsg("SendGroupUpdates: ERROR!! Record size estimation is wrong for %s, Estimate %d, Actual %d, state %d", ARDisplayString(m, rr), rrSize, next - oldnext, rr->state); + else { spaceleft += rrSize; spaceleft -= (next - oldnext); } + + nrecords++; + // We could have sent an update earlier with this "rr" as anchorRR for which we never got a response. + // To preserve ordering, we blow away the previous connection before sending this. + if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL;} + rr->updateid = msgid; + + // By setting the retry time interval here, we will not be looking at these records + // again when we return to CheckGroupRecordUpdates. + SetRecordRetry(m, rr, 0); + } + // Either we have parsed all the records or stopped at "rr" above due to lack of space + startRR = rr; + } + + if (anchorRR) + { + LogInfo("SendGroupUpdates: Parsed %d records and sending using %s", nrecords, ARDisplayString(m, anchorRR)); + SendGroupRRMessage(m, anchorRR, next, AuthInfo); + } + return sentallRecords; +} // Merge the record registrations and send them as a group only if they // have same DomainAuthInfo and hence the same key to put the TSIG mDNSlocal void CheckGroupRecordUpdates(mDNS *const m) - { - AuthRecord *rr, *nextRR; - // Keep sending as long as there is at least one record to be sent - while (MarkRRForSending(m)) - { - if (!SendGroupUpdates(m)) - { - // if everything that was marked was not sent, send them out individually - for (rr = m->ResourceRecords; rr; rr = nextRR) - { - // SendRecordRegistrtion might delete the rr from list, hence - // dereference nextRR before calling the function - nextRR = rr->next; - if (rr->SendRNow == mDNSInterfaceMark) - { - // Any records marked for sending should be eligible to be sent out - // immediately. Just being cautious - if (rr->LastAPTime + rr->ThisAPInterval - m->timenow > 0) - { LogMsg("CheckGroupRecordUpdates: ERROR!! Resourcerecord %s not ready", ARDisplayString(m, rr)); continue; } - rr->SendRNow = mDNSNULL; - SendRecordRegistration(m, rr); - } - } - } - } - - debugf("CheckGroupRecordUpdates: No work, returning"); - return; - } +{ + AuthRecord *rr, *nextRR; + // Keep sending as long as there is at least one record to be sent + while (MarkRRForSending(m)) + { + if (!SendGroupUpdates(m)) + { + // if everything that was marked was not sent, send them out individually + for (rr = m->ResourceRecords; rr; rr = nextRR) + { + // SendRecordRegistrtion might delete the rr from list, hence + // dereference nextRR before calling the function + nextRR = rr->next; + if (rr->SendRNow == mDNSInterfaceMark) + { + // Any records marked for sending should be eligible to be sent out + // immediately. Just being cautious + if (rr->LastAPTime + rr->ThisAPInterval - m->timenow > 0) + { LogMsg("CheckGroupRecordUpdates: ERROR!! Resourcerecord %s not ready", ARDisplayString(m, rr)); continue; } + rr->SendRNow = mDNSNULL; + SendRecordRegistration(m, rr); + } + } + } + } + + debugf("CheckGroupRecordUpdates: No work, returning"); + return; +} mDNSlocal void hndlSRVChanged(mDNS *const m, AuthRecord *rr) - { - // Reevaluate the target always as NAT/Target could have changed while - // we were registering/deeregistering - domainname *dt; - const domainname *target = GetServiceTarget(m, rr); - if (!target || target->c[0] == 0) - { - // we don't have a target, if we just derregistered, then we don't have to do anything - if (rr->state == regState_DeregPending) - { - LogInfo("hndlSRVChanged: SRVChanged, No Target, SRV Deregistered for %##s, state %d", rr->resrec.name->c, - rr->state); - rr->SRVChanged = mDNSfalse; - dt = GetRRDomainNameTarget(&rr->resrec); - if (dt) dt->c[0] = 0; - rr->state = regState_NoTarget; // Wait for the next target change - rr->resrec.rdlength = rr->resrec.rdestimate = 0; - return; - } - - // we don't have a target, if we just registered, we need to deregister - if (rr->state == regState_Pending) - { - LogInfo("hndlSRVChanged: SRVChanged, No Target, Deregistering again %##s, state %d", rr->resrec.name->c, rr->state); - rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; - rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; - rr->state = regState_DeregPending; - return; - } - LogInfo("hndlSRVChanged: Not in DeregPending or RegPending state %##s, state %d", rr->resrec.name->c, rr->state); - } - else - { - // If we were in registered state and SRV changed to NULL, we deregister and come back here - // if we have a target, we need to register again. - // - // if we just registered check to see if it is same. If it is different just re-register the - // SRV and its assoicated records - // - // UpdateOneSRVRecord takes care of re-registering all service records - if ((rr->state == regState_DeregPending) || - (rr->state == regState_Pending && !SameDomainName(target, &rr->resrec.rdata->u.srv.target))) - { - dt = GetRRDomainNameTarget(&rr->resrec); - if (dt) dt->c[0] = 0; - rr->state = regState_NoTarget; // NoTarget will allow us to pick up new target OR nat traversal state - rr->resrec.rdlength = rr->resrec.rdestimate = 0; - LogInfo("hndlSRVChanged: SRVChanged, Valid Target %##s, Registering all records for %##s, state %d", - target->c, rr->resrec.name->c, rr->state); - rr->SRVChanged = mDNSfalse; - UpdateOneSRVRecord(m, rr); - return; - } - // Target did not change while this record was registering. Hence, we go to - // Registered state - the state we started from. - if (rr->state == regState_Pending) rr->state = regState_Registered; - } - - rr->SRVChanged = mDNSfalse; - } +{ + // Reevaluate the target always as NAT/Target could have changed while + // we were registering/deeregistering + domainname *dt; + const domainname *target = GetServiceTarget(m, rr); + if (!target || target->c[0] == 0) + { + // we don't have a target, if we just derregistered, then we don't have to do anything + if (rr->state == regState_DeregPending) + { + LogInfo("hndlSRVChanged: SRVChanged, No Target, SRV Deregistered for %##s, state %d", rr->resrec.name->c, + rr->state); + rr->SRVChanged = mDNSfalse; + dt = GetRRDomainNameTarget(&rr->resrec); + if (dt) dt->c[0] = 0; + rr->state = regState_NoTarget; // Wait for the next target change + rr->resrec.rdlength = rr->resrec.rdestimate = 0; + return; + } + + // we don't have a target, if we just registered, we need to deregister + if (rr->state == regState_Pending) + { + LogInfo("hndlSRVChanged: SRVChanged, No Target, Deregistering again %##s, state %d", rr->resrec.name->c, rr->state); + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; + rr->state = regState_DeregPending; + return; + } + LogInfo("hndlSRVChanged: Not in DeregPending or RegPending state %##s, state %d", rr->resrec.name->c, rr->state); + } + else + { + // If we were in registered state and SRV changed to NULL, we deregister and come back here + // if we have a target, we need to register again. + // + // if we just registered check to see if it is same. If it is different just re-register the + // SRV and its assoicated records + // + // UpdateOneSRVRecord takes care of re-registering all service records + if ((rr->state == regState_DeregPending) || + (rr->state == regState_Pending && !SameDomainName(target, &rr->resrec.rdata->u.srv.target))) + { + dt = GetRRDomainNameTarget(&rr->resrec); + if (dt) dt->c[0] = 0; + rr->state = regState_NoTarget; // NoTarget will allow us to pick up new target OR nat traversal state + rr->resrec.rdlength = rr->resrec.rdestimate = 0; + LogInfo("hndlSRVChanged: SRVChanged, Valid Target %##s, Registering all records for %##s, state %d", + target->c, rr->resrec.name->c, rr->state); + rr->SRVChanged = mDNSfalse; + UpdateOneSRVRecord(m, rr); + return; + } + // Target did not change while this record was registering. Hence, we go to + // Registered state - the state we started from. + if (rr->state == regState_Pending) rr->state = regState_Registered; + } + + rr->SRVChanged = mDNSfalse; +} // Called with lock held mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err, mDNSu32 random) - { - mDNSBool InvokeCallback = mDNStrue; - mDNSIPPort UpdatePort = zeroIPPort; +{ + mDNSBool InvokeCallback = mDNStrue; + mDNSIPPort UpdatePort = zeroIPPort; - if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("hndlRecordUpdateReply: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + if (m->mDNS_busy != m->mDNS_reentrancy+1) + LogMsg("hndlRecordUpdateReply: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); - LogInfo("hndlRecordUpdateReply: err %d ID %d state %d %s(%p)", err, mDNSVal16(rr->updateid), rr->state, ARDisplayString(m, rr), rr); + LogInfo("hndlRecordUpdateReply: err %d ID %d state %d %s(%p)", err, mDNSVal16(rr->updateid), rr->state, ARDisplayString(m, rr), rr); - rr->updateError = err; + rr->updateError = err; #if APPLE_OSX_mDNSResponder - if (err == mStatus_BadSig) UpdateAutoTunnelDomainStatuses(m); + if (err == mStatus_BadSig || err == mStatus_BadKey) UpdateAutoTunnelDomainStatuses(m); #endif - SetRecordRetry(m, rr, random); - - rr->updateid = zeroID; // Make sure that this is not considered as part of a group anymore - // Later when need to send an update, we will get the zone data again. Thus we avoid - // using stale information. - // - // Note: By clearing out the zone info here, it also helps better merging of records - // in some cases. For example, when we get out regState_NoTarget state e.g., move out - // of Double NAT, we want all the records to be in one update. Some BTMM records like - // _autotunnel6 and host records are registered/deregistered when NAT state changes. - // As they are re-registered the zone information is cleared out. To merge with other - // records that might be possibly going out, clearing out the information here helps - // as all of them try to get the zone data. - if (rr->nta) - { - // We always expect the question to be stopped when we get a valid response from the server. - // If the zone info tries to change during this time, updateid would be different and hence - // this response should not have been accepted. - if (rr->nta->question.ThisQInterval != -1) - LogMsg("hndlRecordUpdateReply: ResourceRecord %s, zone info question %##s (%s) interval %d not -1", - ARDisplayString(m, rr), rr->nta->question.qname.c, DNSTypeName(rr->nta->question.qtype), rr->nta->question.ThisQInterval); - UpdatePort = rr->nta->Port; - CancelGetZoneData(m, rr->nta); - rr->nta = mDNSNULL; - } - - // If we are deregistering the record, then complete the deregistration. Ignore any NAT/SRV change - // that could have happened during that time. - if (rr->resrec.RecordType == kDNSRecordTypeDeregistering && rr->state == regState_DeregPending) - { - debugf("hndlRecordUpdateReply: 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 %d", - rr->resrec.name->c, rr->resrec.rrtype, err); - rr->state = regState_Unregistered; - CompleteDeregistration(m, rr); - return; - } - - // We are returning early without updating the state. When we come back from sleep we will re-register after - // re-initializing all the state as though it is a first registration. If the record can't be registered e.g., - // no target, it will be deregistered. Hence, the updating to the right state should not matter when going - // to sleep. - if (m->SleepState) - { - // Need to set it to NoTarget state so that RecordReadyForSleep knows that - // we are done - if (rr->resrec.rrtype == kDNSType_SRV && rr->state == regState_DeregPending) - rr->state = regState_NoTarget; - return; - } - - if (rr->state == regState_UpdatePending) - { - if (err) LogMsg("Update record failed for %##s (err %d)", rr->resrec.name->c, err); - rr->state = regState_Registered; - // deallocate old RData - if (rr->UpdateCallback) rr->UpdateCallback(m, rr, rr->OrigRData, rr->OrigRDLen); - SetNewRData(&rr->resrec, rr->InFlightRData, rr->InFlightRDLen); - rr->OrigRData = mDNSNULL; - rr->InFlightRData = mDNSNULL; - } - - if (rr->SRVChanged) - { - if (rr->resrec.rrtype == kDNSType_SRV) - hndlSRVChanged(m, rr); - else - { - LogInfo("hndlRecordUpdateReply: Deregistered %##s (%s), state %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->state); - rr->SRVChanged = mDNSfalse; - if (rr->state != regState_DeregPending) LogMsg("hndlRecordUpdateReply: ResourceRecord %s not in DeregPending state %d", ARDisplayString(m, rr), rr->state); - rr->state = regState_NoTarget; // Wait for the next target change - } - return; - } - - if (rr->state == regState_Pending || rr->state == regState_Refresh) - { - if (!err) - { - if (rr->state == regState_Refresh) InvokeCallback = mDNSfalse; - rr->state = regState_Registered; - } - else - { - // Retry without lease only for non-Private domains - LogMsg("hndlRecordUpdateReply: Registration of record %##s type %d failed with error %d", rr->resrec.name->c, rr->resrec.rrtype, err); - if (!rr->Private && rr->uselease && err == mStatus_UnknownErr && mDNSSameIPPort(UpdatePort, UnicastDNSPort)) - { - LogMsg("hndlRecordUpdateReply: Will retry update of record %##s without lease option", rr->resrec.name->c); - rr->uselease = mDNSfalse; - rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; - rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; - return; - } - // Communicate the error to the application in the callback below - } - } - - if (rr->QueuedRData && rr->state == regState_Registered) - { - rr->state = regState_UpdatePending; - rr->InFlightRData = rr->QueuedRData; - rr->InFlightRDLen = rr->QueuedRDLen; - rr->OrigRData = rr->resrec.rdata; - rr->OrigRDLen = rr->resrec.rdlength; - rr->QueuedRData = mDNSNULL; - rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; - rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; - return; - } - - // Don't invoke the callback on error as this may not be useful to the client. - // The client may potentially delete the resource record on error which we normally - // delete during deregistration - if (!err && InvokeCallback && rr->RecordCallback) - { - LogInfo("hndlRecordUpdateReply: Calling record callback on %##s", rr->resrec.name->c); - mDNS_DropLockBeforeCallback(); - rr->RecordCallback(m, rr, err); - mDNS_ReclaimLockAfterCallback(); - } - // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function - // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. - } + SetRecordRetry(m, rr, random); + + rr->updateid = zeroID; // Make sure that this is not considered as part of a group anymore + // Later when need to send an update, we will get the zone data again. Thus we avoid + // using stale information. + // + // Note: By clearing out the zone info here, it also helps better merging of records + // in some cases. For example, when we get out regState_NoTarget state e.g., move out + // of Double NAT, we want all the records to be in one update. Some BTMM records like + // _autotunnel6 and host records are registered/deregistered when NAT state changes. + // As they are re-registered the zone information is cleared out. To merge with other + // records that might be possibly going out, clearing out the information here helps + // as all of them try to get the zone data. + if (rr->nta) + { + // We always expect the question to be stopped when we get a valid response from the server. + // If the zone info tries to change during this time, updateid would be different and hence + // this response should not have been accepted. + if (rr->nta->question.ThisQInterval != -1) + LogMsg("hndlRecordUpdateReply: ResourceRecord %s, zone info question %##s (%s) interval %d not -1", + ARDisplayString(m, rr), rr->nta->question.qname.c, DNSTypeName(rr->nta->question.qtype), rr->nta->question.ThisQInterval); + UpdatePort = rr->nta->Port; + CancelGetZoneData(m, rr->nta); + rr->nta = mDNSNULL; + } + + // If we are deregistering the record, then complete the deregistration. Ignore any NAT/SRV change + // that could have happened during that time. + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering && rr->state == regState_DeregPending) + { + debugf("hndlRecordUpdateReply: 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 %d", + rr->resrec.name->c, rr->resrec.rrtype, err); + rr->state = regState_Unregistered; + CompleteDeregistration(m, rr); + return; + } + + // We are returning early without updating the state. When we come back from sleep we will re-register after + // re-initializing all the state as though it is a first registration. If the record can't be registered e.g., + // no target, it will be deregistered. Hence, the updating to the right state should not matter when going + // to sleep. + if (m->SleepState) + { + // Need to set it to NoTarget state so that RecordReadyForSleep knows that + // we are done + if (rr->resrec.rrtype == kDNSType_SRV && rr->state == regState_DeregPending) + rr->state = regState_NoTarget; + return; + } + + if (rr->state == regState_UpdatePending) + { + if (err) LogMsg("Update record failed for %##s (err %d)", rr->resrec.name->c, err); + rr->state = regState_Registered; + // deallocate old RData + if (rr->UpdateCallback) rr->UpdateCallback(m, rr, rr->OrigRData, rr->OrigRDLen); + SetNewRData(&rr->resrec, rr->InFlightRData, rr->InFlightRDLen); + rr->OrigRData = mDNSNULL; + rr->InFlightRData = mDNSNULL; + } + + if (rr->SRVChanged) + { + if (rr->resrec.rrtype == kDNSType_SRV) + hndlSRVChanged(m, rr); + else + { + LogInfo("hndlRecordUpdateReply: Deregistered %##s (%s), state %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->state); + rr->SRVChanged = mDNSfalse; + if (rr->state != regState_DeregPending) LogMsg("hndlRecordUpdateReply: ResourceRecord %s not in DeregPending state %d", ARDisplayString(m, rr), rr->state); + rr->state = regState_NoTarget; // Wait for the next target change + } + return; + } + + if (rr->state == regState_Pending || rr->state == regState_Refresh) + { + if (!err) + { + if (rr->state == regState_Refresh) InvokeCallback = mDNSfalse; + rr->state = regState_Registered; + } + else + { + // Retry without lease only for non-Private domains + LogMsg("hndlRecordUpdateReply: Registration of record %##s type %d failed with error %d", rr->resrec.name->c, rr->resrec.rrtype, err); + if (!rr->Private && rr->uselease && err == mStatus_UnknownErr && mDNSSameIPPort(UpdatePort, UnicastDNSPort)) + { + LogMsg("hndlRecordUpdateReply: Will retry update of record %##s without lease option", rr->resrec.name->c); + rr->uselease = mDNSfalse; + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; + SetNextuDNSEvent(m, rr); + return; + } + // Communicate the error to the application in the callback below + } + } + + if (rr->QueuedRData && rr->state == regState_Registered) + { + rr->state = regState_UpdatePending; + rr->InFlightRData = rr->QueuedRData; + rr->InFlightRDLen = rr->QueuedRDLen; + rr->OrigRData = rr->resrec.rdata; + rr->OrigRDLen = rr->resrec.rdlength; + rr->QueuedRData = mDNSNULL; + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; + SetNextuDNSEvent(m, rr); + return; + } + + // Don't invoke the callback on error as this may not be useful to the client. + // The client may potentially delete the resource record on error which we normally + // delete during deregistration + if (!err && InvokeCallback && rr->RecordCallback) + { + LogInfo("hndlRecordUpdateReply: Calling record callback on %##s", rr->resrec.name->c); + mDNS_DropLockBeforeCallback(); + rr->RecordCallback(m, rr, err); + mDNS_ReclaimLockAfterCallback(); + } + // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function + // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. +} mDNSexport void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len) - { - NATTraversalInfo *ptr; - NATAddrReply *AddrReply = (NATAddrReply *)pkt; - NATPortMapReply *PortMapReply = (NATPortMapReply *)pkt; - mDNSu32 nat_elapsed, our_elapsed; - - // Minimum packet is vers (1) opcode (1) err (2) upseconds (4) = 8 bytes - if (!AddrReply->err && len < 8) { LogMsg("NAT Traversal message too short (%d bytes)", len); return; } - if (AddrReply->vers != NATMAP_VERS) { LogMsg("Received NAT Traversal response with version %d (expected %d)", pkt[0], NATMAP_VERS); return; } - - // Read multi-byte numeric values (fields are identical in a NATPortMapReply) - AddrReply->err = (mDNSu16) ( (mDNSu16)pkt[2] << 8 | pkt[3]); - AddrReply->upseconds = (mDNSs32) ((mDNSs32)pkt[4] << 24 | (mDNSs32)pkt[5] << 16 | (mDNSs32)pkt[6] << 8 | pkt[7]); - - nat_elapsed = AddrReply->upseconds - m->LastNATupseconds; - our_elapsed = (m->timenow - m->LastNATReplyLocalTime) / mDNSPlatformOneSecond; - debugf("uDNS_ReceiveNATPMPPacket %X upseconds %u nat_elapsed %d our_elapsed %d", AddrReply->opcode, AddrReply->upseconds, nat_elapsed, our_elapsed); - - // We compute a conservative estimate of how much the NAT gateways's clock should have advanced - // 1. We subtract 12.5% from our own measured elapsed time, to allow for NAT gateways that have an inacurate clock that runs slowly - // 2. We add a two-second safety margin to allow for rounding errors: e.g. - // -- if NAT gateway sends a packet at t=2.000 seconds, then one at t=7.999, that's approximately 6 real seconds, - // but based on the values in the packet (2,7) the apparent difference according to the packet is only 5 seconds - // -- if we're slow handling packets and/or we have coarse clock granularity, - // we could receive the t=2 packet at our t=1.999 seconds, which we round down to 1 - // and the t=7.999 packet at our t=8.000 seconds, which we record as 8, - // giving an apparent local time difference of 7 seconds - // The two-second safety margin coves this possible calculation discrepancy - if (AddrReply->upseconds < m->LastNATupseconds || nat_elapsed + 2 < our_elapsed - our_elapsed/8) - { LogMsg("NAT gateway %#a rebooted", &m->Router); RecreateNATMappings(m); } - - m->LastNATupseconds = AddrReply->upseconds; - m->LastNATReplyLocalTime = m->timenow; +{ + NATTraversalInfo *ptr; + NATAddrReply *AddrReply = (NATAddrReply *)pkt; + NATPortMapReply *PortMapReply = (NATPortMapReply *)pkt; + mDNSu32 nat_elapsed, our_elapsed; + + // Minimum packet is vers (1) opcode (1) err (2) upseconds (4) = 8 bytes + if (!AddrReply->err && len < 8) { LogMsg("NAT Traversal message too short (%d bytes)", len); return; } + if (AddrReply->vers != NATMAP_VERS) { LogMsg("Received NAT Traversal response with version %d (expected %d)", pkt[0], NATMAP_VERS); return; } + + // Read multi-byte numeric values (fields are identical in a NATPortMapReply) + AddrReply->err = (mDNSu16) ( (mDNSu16)pkt[2] << 8 | pkt[3]); + AddrReply->upseconds = (mDNSs32) ((mDNSs32)pkt[4] << 24 | (mDNSs32)pkt[5] << 16 | (mDNSs32)pkt[6] << 8 | pkt[7]); + + nat_elapsed = AddrReply->upseconds - m->LastNATupseconds; + our_elapsed = (m->timenow - m->LastNATReplyLocalTime) / mDNSPlatformOneSecond; + debugf("uDNS_ReceiveNATPMPPacket %X upseconds %u nat_elapsed %d our_elapsed %d", AddrReply->opcode, AddrReply->upseconds, nat_elapsed, our_elapsed); + + // We compute a conservative estimate of how much the NAT gateways's clock should have advanced + // 1. We subtract 12.5% from our own measured elapsed time, to allow for NAT gateways that have an inacurate clock that runs slowly + // 2. We add a two-second safety margin to allow for rounding errors: e.g. + // -- if NAT gateway sends a packet at t=2.000 seconds, then one at t=7.999, that's approximately 6 real seconds, + // but based on the values in the packet (2,7) the apparent difference according to the packet is only 5 seconds + // -- if we're slow handling packets and/or we have coarse clock granularity, + // we could receive the t=2 packet at our t=1.999 seconds, which we round down to 1 + // and the t=7.999 packet at our t=8.000 seconds, which we record as 8, + // giving an apparent local time difference of 7 seconds + // The two-second safety margin coves this possible calculation discrepancy + if (AddrReply->upseconds < m->LastNATupseconds || nat_elapsed + 2 < our_elapsed - our_elapsed/8) + { LogMsg("NAT gateway %#a rebooted", &m->Router); RecreateNATMappings(m); } + + m->LastNATupseconds = AddrReply->upseconds; + m->LastNATReplyLocalTime = m->timenow; #ifdef _LEGACY_NAT_TRAVERSAL_ - LNT_ClearState(m); + LNT_ClearState(m); #endif // _LEGACY_NAT_TRAVERSAL_ - if (AddrReply->opcode == NATOp_AddrResponse) - { + if (AddrReply->opcode == NATOp_AddrResponse) + { #if APPLE_OSX_mDNSResponder - static char msgbuf[16]; - mDNS_snprintf(msgbuf, sizeof(msgbuf), "%d", AddrReply->err); - mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.natpmp.AddressRequest", AddrReply->err ? "failure" : "success", msgbuf, ""); + static char msgbuf[16]; + mDNS_snprintf(msgbuf, sizeof(msgbuf), "%d", AddrReply->err); + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.natpmp.AddressRequest", AddrReply->err ? "failure" : "success", msgbuf, ""); #endif - if (!AddrReply->err && len < sizeof(NATAddrReply)) { LogMsg("NAT Traversal AddrResponse message too short (%d bytes)", len); return; } - natTraversalHandleAddressReply(m, AddrReply->err, AddrReply->ExtAddr); - } - else if (AddrReply->opcode == NATOp_MapUDPResponse || AddrReply->opcode == NATOp_MapTCPResponse) - { - mDNSu8 Protocol = AddrReply->opcode & 0x7F; + if (!AddrReply->err && len < sizeof(NATAddrReply)) { LogMsg("NAT Traversal AddrResponse message too short (%d bytes)", len); return; } + natTraversalHandleAddressReply(m, AddrReply->err, AddrReply->ExtAddr); + } + else if (AddrReply->opcode == NATOp_MapUDPResponse || AddrReply->opcode == NATOp_MapTCPResponse) + { + mDNSu8 Protocol = AddrReply->opcode & 0x7F; #if APPLE_OSX_mDNSResponder - static char msgbuf[16]; - mDNS_snprintf(msgbuf, sizeof(msgbuf), "%s - %d", AddrReply->opcode == NATOp_MapUDPResponse ? "UDP" : "TCP", PortMapReply->err); - mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.natpmp.PortMapRequest", PortMapReply->err ? "failure" : "success", msgbuf, ""); + static char msgbuf[16]; + mDNS_snprintf(msgbuf, sizeof(msgbuf), "%s - %d", AddrReply->opcode == NATOp_MapUDPResponse ? "UDP" : "TCP", PortMapReply->err); + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.natpmp.PortMapRequest", PortMapReply->err ? "failure" : "success", msgbuf, ""); #endif - if (!PortMapReply->err) - { - if (len < sizeof(NATPortMapReply)) { LogMsg("NAT Traversal PortMapReply message too short (%d bytes)", len); return; } - PortMapReply->NATRep_lease = (mDNSu32) ((mDNSu32)pkt[12] << 24 | (mDNSu32)pkt[13] << 16 | (mDNSu32)pkt[14] << 8 | pkt[15]); - } - - // Since some NAT-PMP server implementations don't return the requested internal port in - // the reply, we can't associate this reply with a particular NATTraversalInfo structure. - // We globally keep track of the most recent error code for mappings. - m->LastNATMapResultCode = PortMapReply->err; - - for (ptr = m->NATTraversals; ptr; ptr=ptr->next) - if (ptr->Protocol == Protocol && mDNSSameIPPort(ptr->IntPort, PortMapReply->intport)) - natTraversalHandlePortMapReply(m, ptr, InterfaceID, PortMapReply->err, PortMapReply->extport, PortMapReply->NATRep_lease); - } - else { LogMsg("Received NAT Traversal response with version unknown opcode 0x%X", AddrReply->opcode); return; } - - // Don't need an SSDP socket if we get a NAT-PMP packet - if (m->SSDPSocket) { debugf("uDNS_ReceiveNATPMPPacket destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; } - } + if (!PortMapReply->err) + { + if (len < sizeof(NATPortMapReply)) { LogMsg("NAT Traversal PortMapReply message too short (%d bytes)", len); return; } + PortMapReply->NATRep_lease = (mDNSu32) ((mDNSu32)pkt[12] << 24 | (mDNSu32)pkt[13] << 16 | (mDNSu32)pkt[14] << 8 | pkt[15]); + } + + // Since some NAT-PMP server implementations don't return the requested internal port in + // the reply, we can't associate this reply with a particular NATTraversalInfo structure. + // We globally keep track of the most recent error code for mappings. + m->LastNATMapResultCode = PortMapReply->err; + + for (ptr = m->NATTraversals; ptr; ptr=ptr->next) + if (ptr->Protocol == Protocol && mDNSSameIPPort(ptr->IntPort, PortMapReply->intport)) + natTraversalHandlePortMapReply(m, ptr, InterfaceID, PortMapReply->err, PortMapReply->extport, PortMapReply->NATRep_lease); + } + else { LogMsg("Received NAT Traversal response with version unknown opcode 0x%X", AddrReply->opcode); return; } + + // Don't need an SSDP socket if we get a NAT-PMP packet + if (m->SSDPSocket) { debugf("uDNS_ReceiveNATPMPPacket destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; } +} // Shorten DNS-SD queries to avoid NAT bugs // Add check to avoid crashing NAT gateways that have buggy DNS relay code @@ -3406,166 +3418,166 @@ mDNSexport void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID Interfac // give themselves away by actually returning a result for this nonsense query. mDNSlocal const domainname *DNSRelayTestQuestion = (const domainname*) - "\x1" "1" "\x1" "0" "\x1" "0" "\x3" "127" "\xa" "dnsbugtest" - "\x1" "1" "\x1" "0" "\x1" "0" "\x3" "127" "\x7" "in-addr" "\x4" "arpa"; + "\x1" "1" "\x1" "0" "\x1" "0" "\x3" "127" "\xa" "dnsbugtest" + "\x1" "1" "\x1" "0" "\x1" "0" "\x3" "127" "\x7" "in-addr" "\x4" "arpa"; // See comments above for DNSRelayTestQuestion // If this is the kind of query that has the risk of crashing buggy DNS servers, we do a test question first mDNSlocal mDNSBool NoTestQuery(DNSQuestion *q) - { - int i; - mDNSu8 *p = q->qname.c; - if (q->AuthInfo) return(mDNStrue); // Don't need a test query for private queries sent directly to authoritative server over TLS/TCP - if (q->qtype != kDNSType_PTR) return(mDNStrue); // Don't need a test query for any non-PTR queries - for (i=0; i<4; i++) // If qname does not begin with num.num.num.num, can't skip the test query - { - if (p[0] < 1 || p[0] > 3) return(mDNSfalse); - if ( p[1] < '0' || p[1] > '9' ) return(mDNSfalse); - if (p[0] >= 2 && (p[2] < '0' || p[2] > '9')) return(mDNSfalse); - if (p[0] >= 3 && (p[3] < '0' || p[3] > '9')) return(mDNSfalse); - p += 1 + p[0]; - } - // If remainder of qname is ".in-addr.arpa.", this is a vanilla reverse-mapping query and - // we can safely do it without needing a test query first, otherwise we need the test query. - return(SameDomainName((domainname*)p, (const domainname*)"\x7" "in-addr" "\x4" "arpa")); - } +{ + int i; + mDNSu8 *p = q->qname.c; + if (q->AuthInfo) return(mDNStrue); // Don't need a test query for private queries sent directly to authoritative server over TLS/TCP + if (q->qtype != kDNSType_PTR) return(mDNStrue); // Don't need a test query for any non-PTR queries + for (i=0; i<4; i++) // If qname does not begin with num.num.num.num, can't skip the test query + { + if (p[0] < 1 || p[0] > 3) return(mDNSfalse); + if ( p[1] < '0' || p[1] > '9' ) return(mDNSfalse); + if (p[0] >= 2 && (p[2] < '0' || p[2] > '9')) return(mDNSfalse); + if (p[0] >= 3 && (p[3] < '0' || p[3] > '9')) return(mDNSfalse); + p += 1 + p[0]; + } + // If remainder of qname is ".in-addr.arpa.", this is a vanilla reverse-mapping query and + // we can safely do it without needing a test query first, otherwise we need the test query. + return(SameDomainName((domainname*)p, (const domainname*)"\x7" "in-addr" "\x4" "arpa")); +} // Returns mDNStrue if response was handled mDNSlocal mDNSBool uDNS_ReceiveTestQuestionResponse(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, - const mDNSAddr *const srcaddr, const mDNSIPPort srcport) - { - const mDNSu8 *ptr = msg->data; - DNSQuestion pktq; - DNSServer *s; - mDNSu32 result = 0; - - // 1. Find out if this is an answer to one of our test questions - if (msg->h.numQuestions != 1) return(mDNSfalse); - ptr = getQuestion(msg, ptr, end, mDNSInterface_Any, &pktq); - if (!ptr) return(mDNSfalse); - if (pktq.qtype != kDNSType_PTR || pktq.qclass != kDNSClass_IN) return(mDNSfalse); - if (!SameDomainName(&pktq.qname, DNSRelayTestQuestion)) return(mDNSfalse); - - // 2. If the DNS relay gave us a positive response, then it's got buggy firmware - // else, if the DNS relay gave us an error or no-answer response, it passed our test - if ((msg->h.flags.b[1] & kDNSFlag1_RC_Mask) == kDNSFlag1_RC_NoErr && msg->h.numAnswers > 0) - result = DNSServer_Failed; - else - result = DNSServer_Passed; - - // 3. Find occurrences of this server in our list, and mark them appropriately - for (s = m->DNSServers; s; s = s->next) - { - mDNSBool matchaddr = (s->teststate != result && mDNSSameAddress(srcaddr, &s->addr) && mDNSSameIPPort(srcport, s->port)); - mDNSBool matchid = (s->teststate == DNSServer_Untested && mDNSSameOpaque16(msg->h.id, s->testid)); - if (matchaddr || matchid) - { - DNSQuestion *q; - s->teststate = result; - if (result == DNSServer_Passed) - { - LogInfo("DNS Server %#a:%d (%#a:%d) %d passed%s", - &s->addr, mDNSVal16(s->port), srcaddr, mDNSVal16(srcport), mDNSVal16(s->testid), - matchaddr ? "" : " NOTE: Reply did not come from address to which query was sent"); - } - else - { - LogMsg("NOTE: Wide-Area Service Discovery disabled to avoid crashing defective DNS relay %#a:%d (%#a:%d) %d%s", - &s->addr, mDNSVal16(s->port), srcaddr, mDNSVal16(srcport), mDNSVal16(s->testid), - matchaddr ? "" : " NOTE: Reply did not come from address to which query was sent"); - } - - // If this server has just changed state from DNSServer_Untested to DNSServer_Passed, then retrigger any waiting questions. - // We use the NoTestQuery() test so that we only retrigger questions that were actually blocked waiting for this test to complete. - if (result == DNSServer_Passed) // Unblock any questions that were waiting for this result - for (q = m->Questions; q; q=q->next) - if (q->qDNSServer == s && !NoTestQuery(q)) - { - q->ThisQInterval = INIT_UCAST_POLL_INTERVAL / QuestionIntervalStep; - q->unansweredQueries = 0; - q->LastQTime = m->timenow - q->ThisQInterval; - m->NextScheduledQuery = m->timenow; - } - } - } - - return(mDNStrue); // Return mDNStrue to tell uDNS_ReceiveMsg it doesn't need to process this packet further - } + const mDNSAddr *const srcaddr, const mDNSIPPort srcport) +{ + const mDNSu8 *ptr = msg->data; + DNSQuestion pktq; + DNSServer *s; + mDNSu32 result = 0; + + // 1. Find out if this is an answer to one of our test questions + if (msg->h.numQuestions != 1) return(mDNSfalse); + ptr = getQuestion(msg, ptr, end, mDNSInterface_Any, &pktq); + if (!ptr) return(mDNSfalse); + if (pktq.qtype != kDNSType_PTR || pktq.qclass != kDNSClass_IN) return(mDNSfalse); + if (!SameDomainName(&pktq.qname, DNSRelayTestQuestion)) return(mDNSfalse); + + // 2. If the DNS relay gave us a positive response, then it's got buggy firmware + // else, if the DNS relay gave us an error or no-answer response, it passed our test + if ((msg->h.flags.b[1] & kDNSFlag1_RC_Mask) == kDNSFlag1_RC_NoErr && msg->h.numAnswers > 0) + result = DNSServer_Failed; + else + result = DNSServer_Passed; + + // 3. Find occurrences of this server in our list, and mark them appropriately + for (s = m->DNSServers; s; s = s->next) + { + mDNSBool matchaddr = (s->teststate != result && mDNSSameAddress(srcaddr, &s->addr) && mDNSSameIPPort(srcport, s->port)); + mDNSBool matchid = (s->teststate == DNSServer_Untested && mDNSSameOpaque16(msg->h.id, s->testid)); + if (matchaddr || matchid) + { + DNSQuestion *q; + s->teststate = result; + if (result == DNSServer_Passed) + { + LogInfo("DNS Server %#a:%d (%#a:%d) %d passed%s", + &s->addr, mDNSVal16(s->port), srcaddr, mDNSVal16(srcport), mDNSVal16(s->testid), + matchaddr ? "" : " NOTE: Reply did not come from address to which query was sent"); + } + else + { + LogMsg("NOTE: Wide-Area Service Discovery disabled to avoid crashing defective DNS relay %#a:%d (%#a:%d) %d%s", + &s->addr, mDNSVal16(s->port), srcaddr, mDNSVal16(srcport), mDNSVal16(s->testid), + matchaddr ? "" : " NOTE: Reply did not come from address to which query was sent"); + } + + // If this server has just changed state from DNSServer_Untested to DNSServer_Passed, then retrigger any waiting questions. + // We use the NoTestQuery() test so that we only retrigger questions that were actually blocked waiting for this test to complete. + if (result == DNSServer_Passed) // Unblock any questions that were waiting for this result + for (q = m->Questions; q; q=q->next) + if (q->qDNSServer == s && !NoTestQuery(q)) + { + q->ThisQInterval = INIT_UCAST_POLL_INTERVAL / QuestionIntervalStep; + q->unansweredQueries = 0; + q->LastQTime = m->timenow - q->ThisQInterval; + m->NextScheduledQuery = m->timenow; + } + } + } + + return(mDNStrue); // Return mDNStrue to tell uDNS_ReceiveMsg it doesn't need to process this packet further +} // Called from mDNSCoreReceive with the lock held mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport) - { - DNSQuestion *qptr; - mStatus err = mStatus_NoError; - - mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; - 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_Mask); - - (void)srcport; // Unused - - debugf("uDNS_ReceiveMsg from %#-15a with " - "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes", - srcaddr, - 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", end - msg->data); - - if (QR_OP == StdR) - { - //if (srcaddr && recvLLQResponse(m, msg, end, srcaddr, srcport)) return; - if (uDNS_ReceiveTestQuestionResponse(m, msg, end, srcaddr, srcport)) return; - for (qptr = m->Questions; qptr; qptr = qptr->next) - if (msg->h.flags.b[0] & kDNSFlag0_TC && mDNSSameOpaque16(qptr->TargetQID, msg->h.id) && m->timenow - qptr->LastQTime < RESPONSE_WINDOW) - { - if (!srcaddr) LogMsg("uDNS_ReceiveMsg: TCP DNS response had TC bit set: ignoring"); - else - { - // Don't reuse TCP connections. We might have failed over to a different DNS server - // while the first TCP connection is in progress. We need a new TCP connection to the - // new DNS server. So, always try to establish a new connection. - if (qptr->tcp) { DisposeTCPConn(qptr->tcp); qptr->tcp = mDNSNULL; } - qptr->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_Zero, srcaddr, srcport, mDNSNULL, qptr, mDNSNULL); - } - } - } - - if (QR_OP == UpdateR) - { - mDNSu32 lease = GetPktLease(m, msg, end); - mDNSs32 expire = m->timenow + (mDNSs32)lease * mDNSPlatformOneSecond; - mDNSu32 random = mDNSRandom((mDNSs32)lease * mDNSPlatformOneSecond/10); - - //rcode = kDNSFlag1_RC_ServFail; // Simulate server failure (rcode 2) - - // Walk through all the records that matches the messageID. There could be multiple - // records if we had sent them in a group - if (m->CurrentRecord) - LogMsg("uDNS_ReceiveMsg ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); - m->CurrentRecord = m->ResourceRecords; - while (m->CurrentRecord) - { - AuthRecord *rptr = m->CurrentRecord; - m->CurrentRecord = m->CurrentRecord->next; - if (AuthRecord_uDNS(rptr) && mDNSSameOpaque16(rptr->updateid, msg->h.id)) - { - err = checkUpdateResult(m, rptr->resrec.name, rcode, msg, end); - if (!err && rptr->uselease && lease) - if (rptr->expire - expire >= 0 || rptr->state != regState_UpdatePending) - { - rptr->expire = expire; - rptr->refreshCount = 0; - } - // We pass the random value to make sure that if we update multiple - // records, they all get the same random value - hndlRecordUpdateReply(m, rptr, err, random); - } - } - } - debugf("Received unexpected response: ID %d matches no active records", mDNSVal16(msg->h.id)); - } +{ + DNSQuestion *qptr; + mStatus err = mStatus_NoError; + + mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; + 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_Mask); + + (void)srcport; // Unused + + debugf("uDNS_ReceiveMsg from %#-15a with " + "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes", + srcaddr, + 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", end - msg->data); + + if (QR_OP == StdR) + { + //if (srcaddr && recvLLQResponse(m, msg, end, srcaddr, srcport)) return; + if (uDNS_ReceiveTestQuestionResponse(m, msg, end, srcaddr, srcport)) return; + for (qptr = m->Questions; qptr; qptr = qptr->next) + if (msg->h.flags.b[0] & kDNSFlag0_TC && mDNSSameOpaque16(qptr->TargetQID, msg->h.id) && m->timenow - qptr->LastQTime < RESPONSE_WINDOW) + { + if (!srcaddr) LogMsg("uDNS_ReceiveMsg: TCP DNS response had TC bit set: ignoring"); + else + { + // Don't reuse TCP connections. We might have failed over to a different DNS server + // while the first TCP connection is in progress. We need a new TCP connection to the + // new DNS server. So, always try to establish a new connection. + if (qptr->tcp) { DisposeTCPConn(qptr->tcp); qptr->tcp = mDNSNULL; } + qptr->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_Zero, srcaddr, srcport, mDNSNULL, qptr, mDNSNULL); + } + } + } + + if (QR_OP == UpdateR) + { + mDNSu32 lease = GetPktLease(m, msg, end); + mDNSs32 expire = m->timenow + (mDNSs32)lease * mDNSPlatformOneSecond; + mDNSu32 random = mDNSRandom((mDNSs32)lease * mDNSPlatformOneSecond/10); + + //rcode = kDNSFlag1_RC_ServFail; // Simulate server failure (rcode 2) + + // Walk through all the records that matches the messageID. There could be multiple + // records if we had sent them in a group + if (m->CurrentRecord) + LogMsg("uDNS_ReceiveMsg ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + AuthRecord *rptr = m->CurrentRecord; + m->CurrentRecord = m->CurrentRecord->next; + if (AuthRecord_uDNS(rptr) && mDNSSameOpaque16(rptr->updateid, msg->h.id)) + { + err = checkUpdateResult(m, rptr->resrec.name, rcode, msg, end); + if (!err && rptr->uselease && lease) + if (rptr->expire - expire >= 0 || rptr->state != regState_UpdatePending) + { + rptr->expire = expire; + rptr->refreshCount = 0; + } + // We pass the random value to make sure that if we update multiple + // records, they all get the same random value + hndlRecordUpdateReply(m, rptr, err, random); + } + } + } + debugf("Received unexpected response: ID %d matches no active records", mDNSVal16(msg->h.id)); +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -3573,171 +3585,171 @@ mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNS #endif mDNSexport void sendLLQRefresh(mDNS *m, DNSQuestion *q) - { - mDNSu8 *end; - LLQOptData llq; - mDNSu8 *limit = m->omsg.data + AbsoluteMaxDNSMessageData; - - if (q->ReqLease) - if ((q->state == LLQ_Established && q->ntries >= kLLQ_MAX_TRIES) || q->expire - m->timenow < 0) - { - LogMsg("Unable to refresh LLQ %##s (%s) - will retry in %d seconds", q->qname.c, DNSTypeName(q->qtype), LLQ_POLL_INTERVAL / mDNSPlatformOneSecond); - StartLLQPolling(m,q); - return; - } - - llq.vers = kLLQ_Vers; - llq.llqOp = kLLQOp_Refresh; - llq.err = q->tcp ? GetLLQEventPort(m, &q->servAddr) : LLQErr_NoError; // If using TCP tell server what UDP port to send notifications to - llq.id = q->id; - llq.llqlease = q->ReqLease; - - InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags); - end = putLLQ(&m->omsg, m->omsg.data, q, &llq); - if (!end) { LogMsg("sendLLQRefresh: putLLQ failed %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } - - // Note that we (conditionally) add HINFO and TSIG here, since the question might be going away, - // so we may not be able to reference it (most importantly it's AuthInfo) when we actually send the message - end = putHINFO(m, &m->omsg, end, q->AuthInfo, limit); - if (!end) { LogMsg("sendLLQRefresh: putHINFO failed %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } - - if (PrivateQuery(q)) - { - DNSDigest_SignMessageHostByteOrder(&m->omsg, &end, q->AuthInfo); - if (!end) { LogMsg("sendLLQRefresh: DNSDigest_SignMessage failed %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } - } - - if (PrivateQuery(q) && !q->tcp) - { - LogInfo("sendLLQRefresh setting up new TLS session %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - if (!q->nta) { LogMsg("sendLLQRefresh:ERROR!! q->nta is NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } - q->tcp = MakeTCPConn(m, &m->omsg, end, kTCPSocketFlags_UseTLS, &q->servAddr, q->servPort, &q->nta->Host, q, mDNSNULL); - } - else - { - mStatus err; - - // if AuthInfo and AuthInfo->AutoTunnel is set, we use the TCP socket but don't need to pass the AuthInfo as - // we already protected the message above. - LogInfo("sendLLQRefresh: using existing %s session %##s (%s)", PrivateQuery(q) ? "TLS" : "UDP", - q->qname.c, DNSTypeName(q->qtype)); - - err = mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, q->LocalSocket, &q->servAddr, q->servPort, q->tcp ? q->tcp->sock : mDNSNULL, mDNSNULL); - if (err) - { - LogMsg("sendLLQRefresh: mDNSSendDNSMessage%s failed: %d", q->tcp ? " (TCP)" : "", err); - if (q->tcp) { DisposeTCPConn(q->tcp); q->tcp = mDNSNULL; } - } - } - - q->ntries++; - - debugf("sendLLQRefresh ntries %d %##s (%s)", q->ntries, q->qname.c, DNSTypeName(q->qtype)); - - q->LastQTime = m->timenow; - SetNextQueryTime(m, q); - } +{ + mDNSu8 *end; + LLQOptData llq; + mDNSu8 *limit = m->omsg.data + AbsoluteMaxDNSMessageData; + + if (q->ReqLease) + if ((q->state == LLQ_Established && q->ntries >= kLLQ_MAX_TRIES) || q->expire - m->timenow < 0) + { + LogMsg("Unable to refresh LLQ %##s (%s) - will retry in %d seconds", q->qname.c, DNSTypeName(q->qtype), LLQ_POLL_INTERVAL / mDNSPlatformOneSecond); + StartLLQPolling(m,q); + return; + } + + llq.vers = kLLQ_Vers; + llq.llqOp = kLLQOp_Refresh; + llq.err = q->tcp ? GetLLQEventPort(m, &q->servAddr) : LLQErr_NoError; // If using TCP tell server what UDP port to send notifications to + llq.id = q->id; + llq.llqlease = q->ReqLease; + + InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags); + end = putLLQ(&m->omsg, m->omsg.data, q, &llq); + if (!end) { LogMsg("sendLLQRefresh: putLLQ failed %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } + + // Note that we (conditionally) add HINFO and TSIG here, since the question might be going away, + // so we may not be able to reference it (most importantly it's AuthInfo) when we actually send the message + end = putHINFO(m, &m->omsg, end, q->AuthInfo, limit); + if (!end) { LogMsg("sendLLQRefresh: putHINFO failed %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } + + if (PrivateQuery(q)) + { + DNSDigest_SignMessageHostByteOrder(&m->omsg, &end, q->AuthInfo); + if (!end) { LogMsg("sendLLQRefresh: DNSDigest_SignMessage failed %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } + } + + if (PrivateQuery(q) && !q->tcp) + { + LogInfo("sendLLQRefresh setting up new TLS session %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + if (!q->nta) { LogMsg("sendLLQRefresh:ERROR!! q->nta is NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } + q->tcp = MakeTCPConn(m, &m->omsg, end, kTCPSocketFlags_UseTLS, &q->servAddr, q->servPort, &q->nta->Host, q, mDNSNULL); + } + else + { + mStatus err; + + // if AuthInfo and AuthInfo->AutoTunnel is set, we use the TCP socket but don't need to pass the AuthInfo as + // we already protected the message above. + LogInfo("sendLLQRefresh: using existing %s session %##s (%s)", PrivateQuery(q) ? "TLS" : "UDP", + q->qname.c, DNSTypeName(q->qtype)); + + err = mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, q->LocalSocket, &q->servAddr, q->servPort, q->tcp ? q->tcp->sock : mDNSNULL, mDNSNULL, mDNSfalse); + if (err) + { + LogMsg("sendLLQRefresh: mDNSSendDNSMessage%s failed: %d", q->tcp ? " (TCP)" : "", err); + if (q->tcp) { DisposeTCPConn(q->tcp); q->tcp = mDNSNULL; } + } + } + + q->ntries++; + + debugf("sendLLQRefresh ntries %d %##s (%s)", q->ntries, q->qname.c, DNSTypeName(q->qtype)); + + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); +} mDNSexport void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo) - { - DNSQuestion *q = (DNSQuestion *)zoneInfo->ZoneDataContext; - - mDNS_Lock(m); - - // If we get here it means that the GetZoneData operation has completed. - // We hold on to the zone data if it is AutoTunnel as we use the hostname - // in zoneInfo during the TLS connection setup. - q->servAddr = zeroAddr; - q->servPort = zeroIPPort; - - if (!err && zoneInfo && !mDNSIPPortIsZero(zoneInfo->Port) && !mDNSAddressIsZero(&zoneInfo->Addr) && zoneInfo->Host.c[0]) - { - q->servAddr = zoneInfo->Addr; - q->servPort = zoneInfo->Port; - if (!PrivateQuery(q)) - { - // We don't need the zone data as we use it only for the Host information which we - // don't need if we are not going to use TLS connections. - if (q->nta) - { - if (q->nta != zoneInfo) LogMsg("LLQGotZoneData: nta (%p) != zoneInfo (%p) %##s (%s)", q->nta, zoneInfo, q->qname.c, DNSTypeName(q->qtype)); - CancelGetZoneData(m, q->nta); - q->nta = mDNSNULL; - } - } - q->ntries = 0; - debugf("LLQGotZoneData %#a:%d", &q->servAddr, mDNSVal16(q->servPort)); - startLLQHandshake(m, q); - } - else - { - if (q->nta) - { - if (q->nta != zoneInfo) LogMsg("LLQGotZoneData: nta (%p) != zoneInfo (%p) %##s (%s)", q->nta, zoneInfo, q->qname.c, DNSTypeName(q->qtype)); - CancelGetZoneData(m, q->nta); - q->nta = mDNSNULL; - } - StartLLQPolling(m,q); - if (err == mStatus_NoSuchNameErr) - { - // this actually failed, so mark it by setting address to all ones - q->servAddr.type = mDNSAddrType_IPv4; - q->servAddr.ip.v4 = onesIPv4Addr; - } - } - - mDNS_Unlock(m); - } +{ + DNSQuestion *q = (DNSQuestion *)zoneInfo->ZoneDataContext; + + mDNS_Lock(m); + + // If we get here it means that the GetZoneData operation has completed. + // We hold on to the zone data if it is AutoTunnel as we use the hostname + // in zoneInfo during the TLS connection setup. + q->servAddr = zeroAddr; + q->servPort = zeroIPPort; + + if (!err && zoneInfo && !mDNSIPPortIsZero(zoneInfo->Port) && !mDNSAddressIsZero(&zoneInfo->Addr) && zoneInfo->Host.c[0]) + { + q->servAddr = zoneInfo->Addr; + q->servPort = zoneInfo->Port; + if (!PrivateQuery(q)) + { + // We don't need the zone data as we use it only for the Host information which we + // don't need if we are not going to use TLS connections. + if (q->nta) + { + if (q->nta != zoneInfo) LogMsg("LLQGotZoneData: nta (%p) != zoneInfo (%p) %##s (%s)", q->nta, zoneInfo, q->qname.c, DNSTypeName(q->qtype)); + CancelGetZoneData(m, q->nta); + q->nta = mDNSNULL; + } + } + q->ntries = 0; + debugf("LLQGotZoneData %#a:%d", &q->servAddr, mDNSVal16(q->servPort)); + startLLQHandshake(m, q); + } + else + { + if (q->nta) + { + if (q->nta != zoneInfo) LogMsg("LLQGotZoneData: nta (%p) != zoneInfo (%p) %##s (%s)", q->nta, zoneInfo, q->qname.c, DNSTypeName(q->qtype)); + CancelGetZoneData(m, q->nta); + q->nta = mDNSNULL; + } + StartLLQPolling(m,q); + if (err == mStatus_NoSuchNameErr) + { + // this actually failed, so mark it by setting address to all ones + q->servAddr.type = mDNSAddrType_IPv4; + q->servAddr.ip.v4 = onesIPv4Addr; + } + } + + mDNS_Unlock(m); +} // Called in normal callback context (i.e. mDNS_busy and mDNS_reentrancy are both 1) mDNSlocal void PrivateQueryGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo) - { - DNSQuestion *q = (DNSQuestion *) zoneInfo->ZoneDataContext; - - LogInfo("PrivateQueryGotZoneData %##s (%s) err %d Zone %##s Private %d", q->qname.c, DNSTypeName(q->qtype), err, zoneInfo->ZoneName.c, zoneInfo->ZonePrivate); - - if (q->nta != zoneInfo) LogMsg("PrivateQueryGotZoneData:ERROR!!: nta (%p) != zoneInfo (%p) %##s (%s)", q->nta, zoneInfo, q->qname.c, DNSTypeName(q->qtype)); - - if (err || !zoneInfo || mDNSAddressIsZero(&zoneInfo->Addr) || mDNSIPPortIsZero(zoneInfo->Port) || !zoneInfo->Host.c[0]) - { - LogInfo("PrivateQueryGotZoneData: ERROR!! %##s (%s) invoked with error code %d %p %#a:%d", - q->qname.c, DNSTypeName(q->qtype), err, zoneInfo, - zoneInfo ? &zoneInfo->Addr : mDNSNULL, - zoneInfo ? mDNSVal16(zoneInfo->Port) : 0); - CancelGetZoneData(m, q->nta); - q->nta = mDNSNULL; - return; - } - - if (!zoneInfo->ZonePrivate) - { - debugf("Private port lookup failed -- retrying without TLS -- %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - q->AuthInfo = mDNSNULL; // Clear AuthInfo so we try again non-private - q->ThisQInterval = InitialQuestionInterval; - q->LastQTime = m->timenow - q->ThisQInterval; - CancelGetZoneData(m, q->nta); - q->nta = mDNSNULL; - mDNS_Lock(m); - SetNextQueryTime(m, q); - mDNS_Unlock(m); - return; - // Next call to uDNS_CheckCurrentQuestion() will do this as a non-private query - } - - if (!PrivateQuery(q)) - { - LogMsg("PrivateQueryGotZoneData: ERROR!! Not a private query %##s (%s) AuthInfo %p", q->qname.c, DNSTypeName(q->qtype), q->AuthInfo); - CancelGetZoneData(m, q->nta); - q->nta = mDNSNULL; - return; - } - - q->TargetQID = mDNS_NewMessageID(m); - if (q->tcp) { DisposeTCPConn(q->tcp); q->tcp = mDNSNULL; } - if (!q->nta) { LogMsg("PrivateQueryGotZoneData:ERROR!! nta is NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } - q->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &zoneInfo->Addr, zoneInfo->Port, &q->nta->Host, q, mDNSNULL); - if (q->nta) { CancelGetZoneData(m, q->nta); q->nta = mDNSNULL; } - } +{ + DNSQuestion *q = (DNSQuestion *) zoneInfo->ZoneDataContext; + + LogInfo("PrivateQueryGotZoneData %##s (%s) err %d Zone %##s Private %d", q->qname.c, DNSTypeName(q->qtype), err, zoneInfo->ZoneName.c, zoneInfo->ZonePrivate); + + if (q->nta != zoneInfo) LogMsg("PrivateQueryGotZoneData:ERROR!!: nta (%p) != zoneInfo (%p) %##s (%s)", q->nta, zoneInfo, q->qname.c, DNSTypeName(q->qtype)); + + if (err || !zoneInfo || mDNSAddressIsZero(&zoneInfo->Addr) || mDNSIPPortIsZero(zoneInfo->Port) || !zoneInfo->Host.c[0]) + { + LogInfo("PrivateQueryGotZoneData: ERROR!! %##s (%s) invoked with error code %d %p %#a:%d", + q->qname.c, DNSTypeName(q->qtype), err, zoneInfo, + zoneInfo ? &zoneInfo->Addr : mDNSNULL, + zoneInfo ? mDNSVal16(zoneInfo->Port) : 0); + CancelGetZoneData(m, q->nta); + q->nta = mDNSNULL; + return; + } + + if (!zoneInfo->ZonePrivate) + { + debugf("Private port lookup failed -- retrying without TLS -- %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->AuthInfo = mDNSNULL; // Clear AuthInfo so we try again non-private + q->ThisQInterval = InitialQuestionInterval; + q->LastQTime = m->timenow - q->ThisQInterval; + CancelGetZoneData(m, q->nta); + q->nta = mDNSNULL; + mDNS_Lock(m); + SetNextQueryTime(m, q); + mDNS_Unlock(m); + return; + // Next call to uDNS_CheckCurrentQuestion() will do this as a non-private query + } + + if (!PrivateQuery(q)) + { + LogMsg("PrivateQueryGotZoneData: ERROR!! Not a private query %##s (%s) AuthInfo %p", q->qname.c, DNSTypeName(q->qtype), q->AuthInfo); + CancelGetZoneData(m, q->nta); + q->nta = mDNSNULL; + return; + } + + q->TargetQID = mDNS_NewMessageID(m); + if (q->tcp) { DisposeTCPConn(q->tcp); q->tcp = mDNSNULL; } + if (!q->nta) { LogMsg("PrivateQueryGotZoneData:ERROR!! nta is NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } + q->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &zoneInfo->Addr, zoneInfo->Port, &q->nta->Host, q, mDNSNULL); + if (q->nta) { CancelGetZoneData(m, q->nta); q->nta = mDNSNULL; } +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -3746,363 +3758,402 @@ mDNSlocal void PrivateQueryGotZoneData(mDNS *const m, mStatus err, const ZoneDat // Called in normal callback context (i.e. mDNS_busy and mDNS_reentrancy are both 1) mDNSexport void RecordRegistrationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneData) - { - AuthRecord *newRR = (AuthRecord*)zoneData->ZoneDataContext; - AuthRecord *ptr; - int c1, c2; - - if (newRR->nta != zoneData) - LogMsg("RecordRegistrationGotZoneData: nta (%p) != zoneData (%p) %##s (%s)", newRR->nta, zoneData, newRR->resrec.name->c, DNSTypeName(newRR->resrec.rrtype)); - - if (m->mDNS_busy != m->mDNS_reentrancy) - LogMsg("RecordRegistrationGotZoneData: mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); - - // make sure record is still in list (!!!) - for (ptr = m->ResourceRecords; ptr; ptr = ptr->next) if (ptr == newRR) break; - if (!ptr) - { - LogMsg("RecordRegistrationGotZoneData - RR no longer in list. Discarding."); - CancelGetZoneData(m, newRR->nta); - newRR->nta = mDNSNULL; - return; - } - - // check error/result - if (err) - { - if (err != mStatus_NoSuchNameErr) LogMsg("RecordRegistrationGotZoneData: error %d", err); - CancelGetZoneData(m, newRR->nta); - newRR->nta = mDNSNULL; - return; - } - - if (!zoneData) { LogMsg("ERROR: RecordRegistrationGotZoneData invoked with NULL result and no error"); return; } - - if (newRR->resrec.rrclass != zoneData->ZoneClass) - { - LogMsg("ERROR: New resource record's class (%d) does not match zone class (%d)", newRR->resrec.rrclass, zoneData->ZoneClass); - CancelGetZoneData(m, newRR->nta); - newRR->nta = mDNSNULL; - return; - } - - // Don't try to do updates to the root name server. - // We might be tempted also to block updates to any single-label name server (e.g. com, edu, net, etc.) but some - // organizations use their own private pseudo-TLD, like ".home", etc, and we don't want to block that. - if (zoneData->ZoneName.c[0] == 0) - { - LogInfo("RecordRegistrationGotZoneData: No name server found claiming responsibility for \"%##s\"!", newRR->resrec.name->c); - CancelGetZoneData(m, newRR->nta); - newRR->nta = mDNSNULL; - return; - } - - // Store discovered zone data - c1 = CountLabels(newRR->resrec.name); - c2 = CountLabels(&zoneData->ZoneName); - if (c2 > c1) - { - LogMsg("RecordRegistrationGotZoneData: Zone \"%##s\" is longer than \"%##s\"", zoneData->ZoneName.c, newRR->resrec.name->c); - CancelGetZoneData(m, newRR->nta); - newRR->nta = mDNSNULL; - return; - } - newRR->zone = SkipLeadingLabels(newRR->resrec.name, c1-c2); - if (!SameDomainName(newRR->zone, &zoneData->ZoneName)) - { - LogMsg("RecordRegistrationGotZoneData: Zone \"%##s\" does not match \"%##s\" for \"%##s\"", newRR->zone->c, zoneData->ZoneName.c, newRR->resrec.name->c); - CancelGetZoneData(m, newRR->nta); - newRR->nta = mDNSNULL; - return; - } - - if (mDNSIPPortIsZero(zoneData->Port) || mDNSAddressIsZero(&zoneData->Addr) || !zoneData->Host.c[0]) - { - LogInfo("RecordRegistrationGotZoneData: No _dns-update._udp service found for \"%##s\"!", newRR->resrec.name->c); - CancelGetZoneData(m, newRR->nta); - newRR->nta = mDNSNULL; - return; - } - - newRR->Private = zoneData->ZonePrivate; - debugf("RecordRegistrationGotZoneData: Set zone information for %##s %##s to %#a:%d", - newRR->resrec.name->c, zoneData->ZoneName.c, &zoneData->Addr, mDNSVal16(zoneData->Port)); - - // If we are deregistering, uDNS_DeregisterRecord will do that as it has the zone data now. - if (newRR->state == regState_DeregPending) - { - mDNS_Lock(m); - uDNS_DeregisterRecord(m, newRR); - mDNS_Unlock(m); - return; - } - - if (newRR->resrec.rrtype == kDNSType_SRV) - { - const domainname *target; - // Reevaluate the target always as NAT/Target could have changed while - // we were fetching zone data. - mDNS_Lock(m); - target = GetServiceTarget(m, newRR); - mDNS_Unlock(m); - if (!target || target->c[0] == 0) - { - domainname *t = GetRRDomainNameTarget(&newRR->resrec); - LogInfo("RecordRegistrationGotZoneData - no target for %##s", newRR->resrec.name->c); - if (t) t->c[0] = 0; - newRR->resrec.rdlength = newRR->resrec.rdestimate = 0; - newRR->state = regState_NoTarget; - CancelGetZoneData(m, newRR->nta); - newRR->nta = mDNSNULL; - return; - } - } - // If we have non-zero service port (always?) - // and a private address, and update server is non-private - // and this service is AutoTarget - // then initiate a NAT mapping request. On completion it will do SendRecordRegistration() for us - if (newRR->resrec.rrtype == kDNSType_SRV && !mDNSIPPortIsZero(newRR->resrec.rdata->u.srv.port) && - mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && newRR->nta && !mDNSAddrIsRFC1918(&newRR->nta->Addr) && - newRR->AutoTarget == Target_AutoHostAndNATMAP) - { - DomainAuthInfo *AuthInfo; - AuthInfo = GetAuthInfoForName(m, newRR->resrec.name); - if (AuthInfo && AuthInfo->AutoTunnel) - { - domainname *t = GetRRDomainNameTarget(&newRR->resrec); - LogMsg("RecordRegistrationGotZoneData: ERROR!! AutoTunnel has Target_AutoHostAndNATMAP for %s", ARDisplayString(m, newRR)); - if (t) t->c[0] = 0; - newRR->resrec.rdlength = newRR->resrec.rdestimate = 0; - newRR->state = regState_NoTarget; - CancelGetZoneData(m, newRR->nta); - newRR->nta = mDNSNULL; - return; - } - // During network transitions, we are called multiple times in different states. Setup NAT - // state just once for this record. - if (!newRR->NATinfo.clientContext) - { - LogInfo("RecordRegistrationGotZoneData StartRecordNatMap %s", ARDisplayString(m, newRR)); - newRR->state = regState_NATMap; - StartRecordNatMap(m, newRR); - return; - } - else LogInfo("RecordRegistrationGotZoneData: StartRecordNatMap for %s, state %d, context %p", ARDisplayString(m, newRR), newRR->state, newRR->NATinfo.clientContext); - } - mDNS_Lock(m); - // We want IsRecordMergeable to check whether it is a record whose update can be - // sent with others. We set the time before we call IsRecordMergeable, so that - // it does not fail this record based on time. We are interested in other checks - // at this time. If a previous update resulted in error, then don't reset the - // interval. Preserve the back-off so that we don't keep retrying aggressively. - if (newRR->updateError == mStatus_NoError) - { - newRR->ThisAPInterval = INIT_RECORD_REG_INTERVAL; - newRR->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; - } - if (IsRecordMergeable(m, newRR, m->timenow + MERGE_DELAY_TIME)) - { - // Delay the record registration by MERGE_DELAY_TIME so that we can merge them - // into one update - LogInfo("RecordRegistrationGotZoneData: Delayed registration for %s", ARDisplayString(m, newRR)); - newRR->LastAPTime += MERGE_DELAY_TIME; - } - mDNS_Unlock(m); - } +{ + AuthRecord *newRR = (AuthRecord*)zoneData->ZoneDataContext; + AuthRecord *ptr; + int c1, c2; + + if (newRR->nta != zoneData) + LogMsg("RecordRegistrationGotZoneData: nta (%p) != zoneData (%p) %##s (%s)", newRR->nta, zoneData, newRR->resrec.name->c, DNSTypeName(newRR->resrec.rrtype)); + + if (m->mDNS_busy != m->mDNS_reentrancy) + LogMsg("RecordRegistrationGotZoneData: mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + + // make sure record is still in list (!!!) + for (ptr = m->ResourceRecords; ptr; ptr = ptr->next) if (ptr == newRR) break; + if (!ptr) + { + LogMsg("RecordRegistrationGotZoneData - RR no longer in list. Discarding."); + CancelGetZoneData(m, newRR->nta); + newRR->nta = mDNSNULL; + return; + } + + // check error/result + if (err) + { + if (err != mStatus_NoSuchNameErr) LogMsg("RecordRegistrationGotZoneData: error %d", err); + CancelGetZoneData(m, newRR->nta); + newRR->nta = mDNSNULL; + return; + } + + if (!zoneData) { LogMsg("ERROR: RecordRegistrationGotZoneData invoked with NULL result and no error"); return; } + + if (newRR->resrec.rrclass != zoneData->ZoneClass) + { + LogMsg("ERROR: New resource record's class (%d) does not match zone class (%d)", newRR->resrec.rrclass, zoneData->ZoneClass); + CancelGetZoneData(m, newRR->nta); + newRR->nta = mDNSNULL; + return; + } + + // Don't try to do updates to the root name server. + // We might be tempted also to block updates to any single-label name server (e.g. com, edu, net, etc.) but some + // organizations use their own private pseudo-TLD, like ".home", etc, and we don't want to block that. + if (zoneData->ZoneName.c[0] == 0) + { + LogInfo("RecordRegistrationGotZoneData: No name server found claiming responsibility for \"%##s\"!", newRR->resrec.name->c); + CancelGetZoneData(m, newRR->nta); + newRR->nta = mDNSNULL; + return; + } + + // Store discovered zone data + c1 = CountLabels(newRR->resrec.name); + c2 = CountLabels(&zoneData->ZoneName); + if (c2 > c1) + { + LogMsg("RecordRegistrationGotZoneData: Zone \"%##s\" is longer than \"%##s\"", zoneData->ZoneName.c, newRR->resrec.name->c); + CancelGetZoneData(m, newRR->nta); + newRR->nta = mDNSNULL; + return; + } + newRR->zone = SkipLeadingLabels(newRR->resrec.name, c1-c2); + if (!SameDomainName(newRR->zone, &zoneData->ZoneName)) + { + LogMsg("RecordRegistrationGotZoneData: Zone \"%##s\" does not match \"%##s\" for \"%##s\"", newRR->zone->c, zoneData->ZoneName.c, newRR->resrec.name->c); + CancelGetZoneData(m, newRR->nta); + newRR->nta = mDNSNULL; + return; + } + + if (mDNSIPPortIsZero(zoneData->Port) || mDNSAddressIsZero(&zoneData->Addr) || !zoneData->Host.c[0]) + { + LogInfo("RecordRegistrationGotZoneData: No _dns-update._udp service found for \"%##s\"!", newRR->resrec.name->c); + CancelGetZoneData(m, newRR->nta); + newRR->nta = mDNSNULL; + return; + } + + newRR->Private = zoneData->ZonePrivate; + debugf("RecordRegistrationGotZoneData: Set zone information for %##s %##s to %#a:%d", + newRR->resrec.name->c, zoneData->ZoneName.c, &zoneData->Addr, mDNSVal16(zoneData->Port)); + + // If we are deregistering, uDNS_DeregisterRecord will do that as it has the zone data now. + if (newRR->state == regState_DeregPending) + { + mDNS_Lock(m); + uDNS_DeregisterRecord(m, newRR); + mDNS_Unlock(m); + return; + } + + if (newRR->resrec.rrtype == kDNSType_SRV) + { + const domainname *target; + // Reevaluate the target always as NAT/Target could have changed while + // we were fetching zone data. + mDNS_Lock(m); + target = GetServiceTarget(m, newRR); + mDNS_Unlock(m); + if (!target || target->c[0] == 0) + { + domainname *t = GetRRDomainNameTarget(&newRR->resrec); + LogInfo("RecordRegistrationGotZoneData - no target for %##s", newRR->resrec.name->c); + if (t) t->c[0] = 0; + newRR->resrec.rdlength = newRR->resrec.rdestimate = 0; + newRR->state = regState_NoTarget; + CancelGetZoneData(m, newRR->nta); + newRR->nta = mDNSNULL; + return; + } + } + // If we have non-zero service port (always?) + // and a private address, and update server is non-private + // and this service is AutoTarget + // then initiate a NAT mapping request. On completion it will do SendRecordRegistration() for us + if (newRR->resrec.rrtype == kDNSType_SRV && !mDNSIPPortIsZero(newRR->resrec.rdata->u.srv.port) && + mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && newRR->nta && !mDNSAddrIsRFC1918(&newRR->nta->Addr) && + newRR->AutoTarget == Target_AutoHostAndNATMAP) + { + DomainAuthInfo *AuthInfo; + AuthInfo = GetAuthInfoForName(m, newRR->resrec.name); + if (AuthInfo && AuthInfo->AutoTunnel) + { + domainname *t = GetRRDomainNameTarget(&newRR->resrec); + LogMsg("RecordRegistrationGotZoneData: ERROR!! AutoTunnel has Target_AutoHostAndNATMAP for %s", ARDisplayString(m, newRR)); + if (t) t->c[0] = 0; + newRR->resrec.rdlength = newRR->resrec.rdestimate = 0; + newRR->state = regState_NoTarget; + CancelGetZoneData(m, newRR->nta); + newRR->nta = mDNSNULL; + return; + } + // During network transitions, we are called multiple times in different states. Setup NAT + // state just once for this record. + if (!newRR->NATinfo.clientContext) + { + LogInfo("RecordRegistrationGotZoneData StartRecordNatMap %s", ARDisplayString(m, newRR)); + newRR->state = regState_NATMap; + StartRecordNatMap(m, newRR); + return; + } + else LogInfo("RecordRegistrationGotZoneData: StartRecordNatMap for %s, state %d, context %p", ARDisplayString(m, newRR), newRR->state, newRR->NATinfo.clientContext); + } + mDNS_Lock(m); + // We want IsRecordMergeable to check whether it is a record whose update can be + // sent with others. We set the time before we call IsRecordMergeable, so that + // it does not fail this record based on time. We are interested in other checks + // at this time. If a previous update resulted in error, then don't reset the + // interval. Preserve the back-off so that we don't keep retrying aggressively. + if (newRR->updateError == mStatus_NoError) + { + newRR->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + newRR->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; + } + if (IsRecordMergeable(m, newRR, m->timenow + MERGE_DELAY_TIME)) + { + // Delay the record registration by MERGE_DELAY_TIME so that we can merge them + // into one update + LogInfo("RecordRegistrationGotZoneData: Delayed registration for %s", ARDisplayString(m, newRR)); + newRR->LastAPTime += MERGE_DELAY_TIME; + } + mDNS_Unlock(m); +} mDNSlocal void SendRecordDeregistration(mDNS *m, AuthRecord *rr) - { - mDNSu8 *ptr = m->omsg.data; - mDNSu8 *limit; - DomainAuthInfo *AuthInfo; - - if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("SendRecordDeRegistration: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); - - if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4)) - { - LogMsg("SendRecordDeRegistration: No zone info for Resource record %s RecordType %d", ARDisplayString(m, rr), rr->resrec.RecordType); - return; - } - - limit = ptr + AbsoluteMaxDNSMessageData; - AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name); - limit -= RRAdditionalSize(m, AuthInfo); - - rr->updateid = mDNS_NewMessageID(m); - InitializeDNSMessage(&m->omsg.h, rr->updateid, UpdateReqFlags); - - // set zone - ptr = putZone(&m->omsg, ptr, limit, rr->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); - if (!ptr) goto exit; - - ptr = BuildUpdateMessage(m, ptr, rr, limit); - - if (!ptr) goto exit; - - if (rr->Private) - { - LogInfo("SendRecordDeregistration TCP %p %s", rr->tcp, ARDisplayString(m, rr)); - if (rr->tcp) LogInfo("SendRecordDeregistration: Disposing existing TCP connection for %s", ARDisplayString(m, rr)); - if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } - if (!rr->nta) { LogMsg("SendRecordDeregistration:Private:ERROR!! nta is NULL for %s", ARDisplayString(m, rr)); return; } - rr->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &rr->nta->Addr, rr->nta->Port, &rr->nta->Host, mDNSNULL, rr); - } - else - { - mStatus err; - LogInfo("SendRecordDeregistration UDP %s", ARDisplayString(m, rr)); - if (!rr->nta) { LogMsg("SendRecordDeregistration:ERROR!! nta is NULL for %s", ARDisplayString(m, rr)); return; } - err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, &rr->nta->Addr, rr->nta->Port, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name)); - if (err) debugf("ERROR: SendRecordDeregistration - mDNSSendDNSMessage - %d", err); - //if (rr->state == regState_DeregPending) CompleteDeregistration(m, rr); // Don't touch rr after this - } - SetRecordRetry(m, rr, 0); - return; +{ + mDNSu8 *ptr = m->omsg.data; + mDNSu8 *limit; + DomainAuthInfo *AuthInfo; + + if (m->mDNS_busy != m->mDNS_reentrancy+1) + LogMsg("SendRecordDeRegistration: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + + if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4)) + { + LogMsg("SendRecordDeRegistration: No zone info for Resource record %s RecordType %d", ARDisplayString(m, rr), rr->resrec.RecordType); + return; + } + + limit = ptr + AbsoluteMaxDNSMessageData; + AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name); + limit -= RRAdditionalSize(m, AuthInfo); + + rr->updateid = mDNS_NewMessageID(m); + InitializeDNSMessage(&m->omsg.h, rr->updateid, UpdateReqFlags); + + // set zone + ptr = putZone(&m->omsg, ptr, limit, rr->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); + if (!ptr) goto exit; + + ptr = BuildUpdateMessage(m, ptr, rr, limit); + + if (!ptr) goto exit; + + if (rr->Private) + { + LogInfo("SendRecordDeregistration TCP %p %s", rr->tcp, ARDisplayString(m, rr)); + if (rr->tcp) LogInfo("SendRecordDeregistration: Disposing existing TCP connection for %s", ARDisplayString(m, rr)); + if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } + if (!rr->nta) { LogMsg("SendRecordDeregistration:Private:ERROR!! nta is NULL for %s", ARDisplayString(m, rr)); return; } + rr->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &rr->nta->Addr, rr->nta->Port, &rr->nta->Host, mDNSNULL, rr); + } + else + { + mStatus err; + LogInfo("SendRecordDeregistration UDP %s", ARDisplayString(m, rr)); + if (!rr->nta) { LogMsg("SendRecordDeregistration:ERROR!! nta is NULL for %s", ARDisplayString(m, rr)); return; } + err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, &rr->nta->Addr, rr->nta->Port, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name), mDNSfalse); + if (err) debugf("ERROR: SendRecordDeregistration - mDNSSendDNSMessage - %d", err); + //if (rr->state == regState_DeregPending) CompleteDeregistration(m, rr); // Don't touch rr after this + } + SetRecordRetry(m, rr, 0); + return; exit: - LogMsg("SendRecordDeregistration: Error formatting message for %s", ARDisplayString(m, rr)); - } + LogMsg("SendRecordDeregistration: Error formatting message for %s", ARDisplayString(m, rr)); +} mDNSexport mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr) - { - DomainAuthInfo *info; - - LogInfo("uDNS_DeregisterRecord: Resource Record %s, state %d", ARDisplayString(m, rr), rr->state); - - switch (rr->state) - { - case regState_Refresh: - case regState_Pending: - case regState_UpdatePending: - case regState_Registered: break; - case regState_DeregPending: break; - - case regState_NATError: - case regState_NATMap: - // A record could be in NoTarget to start with if the corresponding SRV record could not find a target. - // It is also possible to reenter the NoTarget state when we move to a network with a NAT that has - // no NAT-PMP/UPnP support. In that case before we entered NoTarget, we already deregistered with - // the server. - case regState_NoTarget: - case regState_Unregistered: - case regState_Zero: - default: - LogInfo("uDNS_DeregisterRecord: State %d for %##s type %s", rr->state, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); - // This function may be called during sleep when there are no sleep proxy servers - if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) CompleteDeregistration(m, rr); - return mStatus_NoError; - } - - // If a current group registration is pending, we can't send this deregisration till that registration - // has reached the server i.e., the ordering is important. Previously, if we did not send this - // registration in a group, then the previous connection will be torn down as part of sending the - // deregistration. If we send this in a group, we need to locate the resource record that was used - // to send this registration and terminate that connection. This means all the updates on that might - // be lost (assuming the response is not waiting for us at the socket) and the retry will send the - // update again sometime in the near future. - // - // NOTE: SSL handshake failures normally free the TCP connection immediately. Hence, you may not - // find the TCP below there. This case can happen only when tcp is trying to actively retransmit - // the request or SSL negotiation taking time i.e resource record is actively trying to get the - // message to the server. During that time a deregister has to happen. - - if (!mDNSOpaque16IsZero(rr->updateid)) - { - AuthRecord *anchorRR; - mDNSBool found = mDNSfalse; - for (anchorRR = m->ResourceRecords; anchorRR; anchorRR = anchorRR->next) - { - if (AuthRecord_uDNS(rr) && mDNSSameOpaque16(anchorRR->updateid, rr->updateid) && anchorRR->tcp) - { - LogInfo("uDNS_DeregisterRecord: Found Anchor RR %s terminated", ARDisplayString(m, anchorRR)); - if (found) - LogMsg("uDNS_DeregisterRecord: ERROR: Another anchorRR %s found", ARDisplayString(m, anchorRR)); - DisposeTCPConn(anchorRR->tcp); - anchorRR->tcp = mDNSNULL; - found = mDNStrue; - } - } - if (!found) LogInfo("uDNSDeregisterRecord: Cannot find the anchor Resource Record for %s, not an error", ARDisplayString(m, rr)); - } - - // Retry logic for deregistration should be no different from sending registration the first time. - // Currently ThisAPInterval most likely is set to the refresh interval - rr->state = regState_DeregPending; - rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; - rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; - info = GetAuthInfoForName_internal(m, rr->resrec.name); - if (IsRecordMergeable(m, rr, m->timenow + MERGE_DELAY_TIME)) - { - // Delay the record deregistration by MERGE_DELAY_TIME so that we can merge them - // into one update. If the domain is being deleted, delay by 2 * MERGE_DELAY_TIME - // so that we can merge all the AutoTunnel records and the service records in - // one update (they get deregistered a little apart) - if (info && info->deltime) rr->LastAPTime += (2 * MERGE_DELAY_TIME); - else rr->LastAPTime += MERGE_DELAY_TIME; - } - // IsRecordMergeable could have returned false for several reasons e.g., DontMerge is set or - // no zone information. Most likely it is the latter, CheckRecordUpdates will fetch the zone - // data when it encounters this record. - - if (m->NextuDNSEvent - (rr->LastAPTime + rr->ThisAPInterval) >= 0) - m->NextuDNSEvent = (rr->LastAPTime + rr->ThisAPInterval); - - return mStatus_NoError; - } +{ + DomainAuthInfo *info; + + LogInfo("uDNS_DeregisterRecord: Resource Record %s, state %d", ARDisplayString(m, rr), rr->state); + + switch (rr->state) + { + case regState_Refresh: + case regState_Pending: + case regState_UpdatePending: + case regState_Registered: break; + case regState_DeregPending: break; + + case regState_NATError: + case regState_NATMap: + // A record could be in NoTarget to start with if the corresponding SRV record could not find a target. + // It is also possible to reenter the NoTarget state when we move to a network with a NAT that has + // no NAT-PMP/UPnP support. In that case before we entered NoTarget, we already deregistered with + // the server. + case regState_NoTarget: + case regState_Unregistered: + case regState_Zero: + default: + LogInfo("uDNS_DeregisterRecord: State %d for %##s type %s", rr->state, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + // This function may be called during sleep when there are no sleep proxy servers + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) CompleteDeregistration(m, rr); + return mStatus_NoError; + } + + // if unsent rdata is queued, free it. + // + // The data may be queued in QueuedRData or InFlightRData. + // + // 1) If the record is in Registered state, we store it in InFlightRData and copy the same in "rdata" + // *just* before sending the update to the server. Till we get the response, InFlightRData and "rdata" + // in the resource record are same. We don't want to free in that case. It will be freed when "rdata" + // is freed. If they are not same, the update has not been sent and we should free it here. + // + // 2) If the record is in UpdatePending state, we queue the update in QueuedRData. When the previous update + // comes back from the server, we copy it from QueuedRData to InFlightRData and repeat (1). This implies + // that QueuedRData can never be same as "rdata" in the resource record. As long as we have something + // left in QueuedRData, we should free it here. + + if (rr->InFlightRData && rr->UpdateCallback) + { + if (rr->InFlightRData != rr->resrec.rdata) + { + LogInfo("uDNS_DeregisterRecord: Freeing InFlightRData for %s", ARDisplayString(m, rr)); + rr->UpdateCallback(m, rr, rr->InFlightRData, rr->InFlightRDLen); + rr->InFlightRData = mDNSNULL; + } + else + LogInfo("uDNS_DeregisterRecord: InFlightRData same as rdata for %s", ARDisplayString(m, rr)); + } + + if (rr->QueuedRData && rr->UpdateCallback) + { + if (rr->QueuedRData == rr->resrec.rdata) + LogMsg("uDNS_DeregisterRecord: ERROR!! QueuedRData same as rdata for %s", ARDisplayString(m, rr)); + else + { + LogInfo("uDNS_DeregisterRecord: Freeing QueuedRData for %s", ARDisplayString(m, rr)); + rr->UpdateCallback(m, rr, rr->QueuedRData, rr->QueuedRDLen); + rr->QueuedRData = mDNSNULL; + } + } + + // If a current group registration is pending, we can't send this deregisration till that registration + // has reached the server i.e., the ordering is important. Previously, if we did not send this + // registration in a group, then the previous connection will be torn down as part of sending the + // deregistration. If we send this in a group, we need to locate the resource record that was used + // to send this registration and terminate that connection. This means all the updates on that might + // be lost (assuming the response is not waiting for us at the socket) and the retry will send the + // update again sometime in the near future. + // + // NOTE: SSL handshake failures normally free the TCP connection immediately. Hence, you may not + // find the TCP below there. This case can happen only when tcp is trying to actively retransmit + // the request or SSL negotiation taking time i.e resource record is actively trying to get the + // message to the server. During that time a deregister has to happen. + + if (!mDNSOpaque16IsZero(rr->updateid)) + { + AuthRecord *anchorRR; + mDNSBool found = mDNSfalse; + for (anchorRR = m->ResourceRecords; anchorRR; anchorRR = anchorRR->next) + { + if (AuthRecord_uDNS(rr) && mDNSSameOpaque16(anchorRR->updateid, rr->updateid) && anchorRR->tcp) + { + LogInfo("uDNS_DeregisterRecord: Found Anchor RR %s terminated", ARDisplayString(m, anchorRR)); + if (found) + LogMsg("uDNS_DeregisterRecord: ERROR: Another anchorRR %s found", ARDisplayString(m, anchorRR)); + DisposeTCPConn(anchorRR->tcp); + anchorRR->tcp = mDNSNULL; + found = mDNStrue; + } + } + if (!found) LogInfo("uDNSDeregisterRecord: Cannot find the anchor Resource Record for %s, not an error", ARDisplayString(m, rr)); + } + + // Retry logic for deregistration should be no different from sending registration the first time. + // Currently ThisAPInterval most likely is set to the refresh interval + rr->state = regState_DeregPending; + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; + info = GetAuthInfoForName_internal(m, rr->resrec.name); + if (IsRecordMergeable(m, rr, m->timenow + MERGE_DELAY_TIME)) + { + // Delay the record deregistration by MERGE_DELAY_TIME so that we can merge them + // into one update. If the domain is being deleted, delay by 2 * MERGE_DELAY_TIME + // so that we can merge all the AutoTunnel records and the service records in + // one update (they get deregistered a little apart) + if (info && info->deltime) rr->LastAPTime += (2 * MERGE_DELAY_TIME); + else rr->LastAPTime += MERGE_DELAY_TIME; + } + // IsRecordMergeable could have returned false for several reasons e.g., DontMerge is set or + // no zone information. Most likely it is the latter, CheckRecordUpdates will fetch the zone + // data when it encounters this record. + + if (m->NextuDNSEvent - (rr->LastAPTime + rr->ThisAPInterval) >= 0) + m->NextuDNSEvent = (rr->LastAPTime + rr->ThisAPInterval); + + return mStatus_NoError; +} mDNSexport mStatus uDNS_UpdateRecord(mDNS *m, AuthRecord *rr) - { - LogInfo("uDNS_UpdateRecord: Resource Record %##s, state %d", rr->resrec.name->c, rr->state); - switch(rr->state) - { - case regState_DeregPending: - case regState_Unregistered: - // not actively registered - goto unreg_error; - - case regState_NATMap: - case regState_NoTarget: - // change rdata directly since it hasn't been sent yet - if (rr->UpdateCallback) rr->UpdateCallback(m, rr, rr->resrec.rdata, rr->resrec.rdlength); - SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); - rr->NewRData = mDNSNULL; - return mStatus_NoError; - - case regState_Pending: - case regState_Refresh: - case regState_UpdatePending: - // registration in-flight. queue rdata and return - if (rr->QueuedRData && rr->UpdateCallback) - // if unsent rdata is already queued, free it before we replace it - rr->UpdateCallback(m, rr, rr->QueuedRData, rr->QueuedRDLen); - rr->QueuedRData = rr->NewRData; - rr->QueuedRDLen = rr->newrdlength; - rr->NewRData = mDNSNULL; - return mStatus_NoError; - - case regState_Registered: - rr->OrigRData = rr->resrec.rdata; - rr->OrigRDLen = rr->resrec.rdlength; - rr->InFlightRData = rr->NewRData; - rr->InFlightRDLen = rr->newrdlength; - rr->NewRData = mDNSNULL; - rr->state = regState_UpdatePending; - rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; - rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; - return mStatus_NoError; - - case regState_NATError: - LogMsg("ERROR: uDNS_UpdateRecord called for record %##s with bad state regState_NATError", rr->resrec.name->c); - return mStatus_UnknownErr; // states for service records only - - default: LogMsg("uDNS_UpdateRecord: Unknown state %d for %##s", rr->state, rr->resrec.name->c); - } - - unreg_error: - LogMsg("uDNS_UpdateRecord: Requested update of record %##s type %d, in erroneous state %d", - rr->resrec.name->c, rr->resrec.rrtype, rr->state); - return mStatus_Invalid; - } +{ + LogInfo("uDNS_UpdateRecord: Resource Record %##s, state %d", rr->resrec.name->c, rr->state); + switch(rr->state) + { + case regState_DeregPending: + case regState_Unregistered: + // not actively registered + goto unreg_error; + + case regState_NATMap: + case regState_NoTarget: + // change rdata directly since it hasn't been sent yet + if (rr->UpdateCallback) rr->UpdateCallback(m, rr, rr->resrec.rdata, rr->resrec.rdlength); + SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); + rr->NewRData = mDNSNULL; + return mStatus_NoError; + + case regState_Pending: + case regState_Refresh: + case regState_UpdatePending: + // registration in-flight. queue rdata and return + if (rr->QueuedRData && rr->UpdateCallback) + // if unsent rdata is already queued, free it before we replace it + rr->UpdateCallback(m, rr, rr->QueuedRData, rr->QueuedRDLen); + rr->QueuedRData = rr->NewRData; + rr->QueuedRDLen = rr->newrdlength; + rr->NewRData = mDNSNULL; + return mStatus_NoError; + + case regState_Registered: + rr->OrigRData = rr->resrec.rdata; + rr->OrigRDLen = rr->resrec.rdlength; + rr->InFlightRData = rr->NewRData; + rr->InFlightRDLen = rr->newrdlength; + rr->NewRData = mDNSNULL; + rr->state = regState_UpdatePending; + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; + SetNextuDNSEvent(m, rr); + return mStatus_NoError; + + case regState_NATError: + LogMsg("ERROR: uDNS_UpdateRecord called for record %##s with bad state regState_NATError", rr->resrec.name->c); + return mStatus_UnknownErr; // states for service records only + + default: LogMsg("uDNS_UpdateRecord: Unknown state %d for %##s", rr->state, rr->resrec.name->c); + } + +unreg_error: + LogMsg("uDNS_UpdateRecord: Requested update of record %##s type %d, in erroneous state %d", + rr->resrec.name->c, rr->resrec.rrtype, rr->state); + return mStatus_Invalid; +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -4112,460 +4163,461 @@ mDNSexport mStatus uDNS_UpdateRecord(mDNS *m, AuthRecord *rr) // The question to be checked is not passed in as an explicit parameter; // instead it is implicit that the question to be checked is m->CurrentQuestion. mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) - { - DNSQuestion *q = m->CurrentQuestion; - if (m->timenow - NextQSendTime(q) < 0) return; - - if (q->LongLived) - { - switch (q->state) - { - case LLQ_InitialRequest: startLLQHandshake(m, q); break; - case LLQ_SecondaryRequest: - // For PrivateQueries, we need to start the handshake again as we don't do the Challenge/Response step - if (PrivateQuery(q)) - startLLQHandshake(m, q); - else - sendChallengeResponse(m, q, mDNSNULL); - break; - case LLQ_Established: sendLLQRefresh(m, q); break; - case LLQ_Poll: break; // Do nothing (handled below) - } - } - - // We repeat the check above (rather than just making this the "else" case) because startLLQHandshake can change q->state to LLQ_Poll - if (!(q->LongLived && q->state != LLQ_Poll)) - { - if (q->unansweredQueries >= MAX_UCAST_UNANSWERED_QUERIES) - { - DNSServer *orig = q->qDNSServer; - if (orig) - LogInfo("uDNS_CheckCurrentQuestion: Sent %d unanswered queries for %##s (%s) to %#a:%d (%##s)", - q->unansweredQueries, q->qname.c, DNSTypeName(q->qtype), &orig->addr, mDNSVal16(orig->port), orig->domain.c); - - PenalizeDNSServer(m, q); - q->noServerResponse = 1; - } - // There are two cases here. - // - // 1. We have only one DNS server for this question. It is not responding even after we sent MAX_UCAST_UNANSWERED_QUERIES. - // In that case, we need to keep retrying till we get a response. But we need to backoff as we retry. We set - // noServerResponse in the block above and below we do not touch the question interval. When we come here, we - // already waited for the response. We need to send another query right at this moment. We do that below by - // reinitializing dns servers and reissuing the query. - // - // 2. We have more than one DNS server. If at least one server did not respond, we would have set noServerResponse - // either now (the last server in the list) or before (non-last server in the list). In either case, if we have - // reached the end of DNS server list, we need to try again from the beginning. Ideally we should try just the - // servers that did not respond, but for simplicity we try all the servers. Once we reached the end of list, we - // set triedAllServersOnce so that we don't try all the servers aggressively. See PenalizeDNSServer. - if (!q->qDNSServer && q->noServerResponse) - { - DNSServer *new; - DNSQuestion *qptr; - q->triedAllServersOnce = 1; - // Re-initialize all DNS servers for this question. If we have a DNSServer, DNSServerChangeForQuestion will - // handle all the work including setting the new DNS server. - SetValidDNSServers(m, q); - new = GetServerForQuestion(m, q); - if (new) - { - LogInfo("uDNS_checkCurrentQuestion: Retrying question %p %##s (%s) DNS Server %#a:%d ThisQInterval %d", - q, q->qname.c, DNSTypeName(q->qtype), new ? &new->addr : mDNSNULL, mDNSVal16(new ? new->port : zeroIPPort), q->ThisQInterval); - DNSServerChangeForQuestion(m, q, new); - } - for (qptr = q->next ; qptr; qptr = qptr->next) - if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; } - } - if (q->qDNSServer && q->qDNSServer->teststate != DNSServer_Disabled) - { - mDNSu8 *end = m->omsg.data; - mStatus err = mStatus_NoError; - mDNSBool private = mDNSfalse; - - InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags); - - if (q->qDNSServer->teststate != DNSServer_Untested || NoTestQuery(q)) - { - end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); - private = PrivateQuery(q); - } - else if (m->timenow - q->qDNSServer->lasttest >= INIT_UCAST_POLL_INTERVAL) // Make sure at least three seconds has elapsed since last test query - { - LogInfo("Sending DNS test query to %#a:%d", &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port)); - q->ThisQInterval = INIT_UCAST_POLL_INTERVAL / QuestionIntervalStep; - q->qDNSServer->lasttest = m->timenow; - end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, DNSRelayTestQuestion, kDNSType_PTR, kDNSClass_IN); - q->qDNSServer->testid = m->omsg.h.id; - } - - if (end > m->omsg.data && (q->qDNSServer->teststate != DNSServer_Failed || NoTestQuery(q))) - { - //LogMsg("uDNS_CheckCurrentQuestion %p %d %p %##s (%s)", q, NextQSendTime(q) - m->timenow, private, q->qname.c, DNSTypeName(q->qtype)); - if (private) - { - if (q->nta) CancelGetZoneData(m, q->nta); - q->nta = StartGetZoneData(m, &q->qname, q->LongLived ? ZoneServiceLLQ : ZoneServiceQuery, PrivateQueryGotZoneData, q); - if (q->state == LLQ_Poll) q->ThisQInterval = (LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10)) / QuestionIntervalStep; - } - else - { - debugf("uDNS_CheckCurrentQuestion sending %p %##s (%s) %#a:%d UnansweredQueries %d", - q, q->qname.c, DNSTypeName(q->qtype), - q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort), q->unansweredQueries); - if (!q->LocalSocket) q->LocalSocket = mDNSPlatformUDPSocket(m, zeroIPPort); - if (!q->LocalSocket) err = mStatus_NoMemoryErr; // If failed to make socket (should be very rare), we'll try again next time - else err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface, q->LocalSocket, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL); - } - } - - if (err) debugf("ERROR: uDNS_idle - mDNSSendDNSMessage - %d", err); // surpress syslog messages if we have no network - else - { - q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep; // Only increase interval if send succeeded - q->unansweredQueries++; - if (q->ThisQInterval > MAX_UCAST_POLL_INTERVAL) - q->ThisQInterval = MAX_UCAST_POLL_INTERVAL; - if (private && q->state != LLQ_Poll) - { - // We don't want to retransmit too soon. Hence, we always schedule our first - // retransmisson at 3 seconds rather than one second - if (q->ThisQInterval < (3 * mDNSPlatformOneSecond)) - q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep; - if (q->ThisQInterval > LLQ_POLL_INTERVAL) - q->ThisQInterval = LLQ_POLL_INTERVAL; - LogInfo("uDNS_CheckCurrentQuestion: private non polling question for %##s (%s) will be retried in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval); - } - if (q->qDNSServer->cellIntf) - { - // We don't want to retransmit too soon. Schedule our first retransmisson at - // MIN_UCAST_RETRANS_TIMEOUT seconds. - if (q->ThisQInterval < MIN_UCAST_RETRANS_TIMEOUT) - q->ThisQInterval = MIN_UCAST_RETRANS_TIMEOUT; - } - debugf("uDNS_CheckCurrentQuestion: Increased ThisQInterval to %d for %##s (%s), cell %d", q->ThisQInterval, q->qname.c, DNSTypeName(q->qtype), q->qDNSServer->cellIntf); - } - q->LastQTime = m->timenow; - SetNextQueryTime(m, q); - } - else - { - // If we have no server for this query, or the only server is a disabled one, then we deliver - // a transient failure indication to the client. This is important for things like iPhone - // where we want to return timely feedback to the user when no network is available. - // After calling MakeNegativeCacheRecord() we store the resulting record in the - // cache so that it will be visible to other clients asking the same question. - // (When we have a group of identical questions, only the active representative of the group gets - // passed to uDNS_CheckCurrentQuestion -- we only want one set of query packets hitting the wire -- - // but we want *all* of the questions to get answer callbacks.) - - CacheRecord *rr; - const mDNSu32 slot = HashSlot(&q->qname); - CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); - if (cg) - for (rr = cg->members; rr; rr=rr->next) - if (SameNameRecordAnswersQuestion(&rr->resrec, q)) mDNS_PurgeCacheResourceRecord(m, rr); - - if (!q->qDNSServer) - { - if (!mDNSOpaque64IsZero(&q->validDNSServers)) - LogMsg("uDNS_CheckCurrentQuestion: ERROR!!: valid DNSServer bits not zero 0x%x, 0x%x for question %##s (%s)", - q->validDNSServers.l[1], q->validDNSServers.l[0], q->qname.c, DNSTypeName(q->qtype)); - // If we reached the end of list while picking DNS servers, then we don't want to deactivate the - // question. Try after 60 seconds. We find this by looking for valid DNSServers for this question, - // if we find any, then we must have tried them before we came here. This avoids maintaining - // another state variable to see if we had valid DNS servers for this question. - SetValidDNSServers(m, q); - if (mDNSOpaque64IsZero(&q->validDNSServers)) - { - LogInfo("uDNS_CheckCurrentQuestion: no DNS server for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - q->ThisQInterval = 0; - } - else - { - DNSQuestion *qptr; - // Pretend that we sent this question. As this is an ActiveQuestion, the NextScheduledQuery should - // be set properly. Also, we need to properly backoff in cases where we don't set the question to - // MaxQuestionInterval when we answer the question e.g., LongLived, we need to keep backing off - q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep; - q->LastQTime = m->timenow; - SetNextQueryTime(m, q); - // Pick a new DNS server now. Otherwise, when the cache is 80% of its expiry, we will try - // to send a query and come back to the same place here and log the above message. - q->qDNSServer = GetServerForQuestion(m, q); - for (qptr = q->next ; qptr; qptr = qptr->next) - if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; } - LogInfo("uDNS_checkCurrentQuestion: Tried all DNS servers, retry question %p SuppressUnusable %d %##s (%s) with DNS Server %#a:%d after 60 seconds, ThisQInterval %d", - q, q->SuppressUnusable, q->qname.c, DNSTypeName(q->qtype), - q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort), q->ThisQInterval); - } - } - else - { - q->ThisQInterval = 0; - LogMsg("uDNS_CheckCurrentQuestion DNS server %#a:%d for %##s is disabled", &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qname.c); - } - - // For some of the WAB queries that we generate form within the mDNSResponder, most of the home routers - // don't understand and return ServFail/NXDomain. In those cases, we don't want to try too often. We try - // every fifteen minutes in that case - MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, (DomainEnumQuery(&q->qname) ? 60 * 15 : 60), mDNSInterface_Any, q->qDNSServer); - q->unansweredQueries = 0; - // We're already using the m->CurrentQuestion pointer, so CacheRecordAdd can't use it to walk the question list. - // To solve this problem we set rr->DelayDelivery to a nonzero value (which happens to be 'now') so that we - // momentarily defer generating answer callbacks until mDNS_Execute time. - CreateNewCacheEntry(m, slot, cg, NonZeroTime(m->timenow)); - ScheduleNextCacheCheckTime(m, slot, NonZeroTime(m->timenow)); - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - // MUST NOT touch m->CurrentQuestion (or q) after this -- client callback could have deleted it - } - } - } +{ + DNSQuestion *q = m->CurrentQuestion; + if (m->timenow - NextQSendTime(q) < 0) return; + + if (q->LongLived) + { + switch (q->state) + { + case LLQ_InitialRequest: startLLQHandshake(m, q); break; + case LLQ_SecondaryRequest: + // For PrivateQueries, we need to start the handshake again as we don't do the Challenge/Response step + if (PrivateQuery(q)) + startLLQHandshake(m, q); + else + sendChallengeResponse(m, q, mDNSNULL); + break; + case LLQ_Established: sendLLQRefresh(m, q); break; + case LLQ_Poll: break; // Do nothing (handled below) + } + } + + // We repeat the check above (rather than just making this the "else" case) because startLLQHandshake can change q->state to LLQ_Poll + if (!(q->LongLived && q->state != LLQ_Poll)) + { + if (q->unansweredQueries >= MAX_UCAST_UNANSWERED_QUERIES) + { + DNSServer *orig = q->qDNSServer; + if (orig) + LogInfo("uDNS_CheckCurrentQuestion: Sent %d unanswered queries for %##s (%s) to %#a:%d (%##s)", + q->unansweredQueries, q->qname.c, DNSTypeName(q->qtype), &orig->addr, mDNSVal16(orig->port), orig->domain.c); + + PenalizeDNSServer(m, q); + q->noServerResponse = 1; + } + // There are two cases here. + // + // 1. We have only one DNS server for this question. It is not responding even after we sent MAX_UCAST_UNANSWERED_QUERIES. + // In that case, we need to keep retrying till we get a response. But we need to backoff as we retry. We set + // noServerResponse in the block above and below we do not touch the question interval. When we come here, we + // already waited for the response. We need to send another query right at this moment. We do that below by + // reinitializing dns servers and reissuing the query. + // + // 2. We have more than one DNS server. If at least one server did not respond, we would have set noServerResponse + // either now (the last server in the list) or before (non-last server in the list). In either case, if we have + // reached the end of DNS server list, we need to try again from the beginning. Ideally we should try just the + // servers that did not respond, but for simplicity we try all the servers. Once we reached the end of list, we + // set triedAllServersOnce so that we don't try all the servers aggressively. See PenalizeDNSServer. + if (!q->qDNSServer && q->noServerResponse) + { + DNSServer *new; + DNSQuestion *qptr; + q->triedAllServersOnce = 1; + // Re-initialize all DNS servers for this question. If we have a DNSServer, DNSServerChangeForQuestion will + // handle all the work including setting the new DNS server. + SetValidDNSServers(m, q); + new = GetServerForQuestion(m, q); + if (new) + { + LogInfo("uDNS_checkCurrentQuestion: Retrying question %p %##s (%s) DNS Server %#a:%d ThisQInterval %d", + q, q->qname.c, DNSTypeName(q->qtype), new ? &new->addr : mDNSNULL, mDNSVal16(new ? new->port : zeroIPPort), q->ThisQInterval); + DNSServerChangeForQuestion(m, q, new); + } + for (qptr = q->next ; qptr; qptr = qptr->next) + if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; } + } + if (q->qDNSServer && q->qDNSServer->teststate != DNSServer_Disabled) + { + mDNSu8 *end = m->omsg.data; + mStatus err = mStatus_NoError; + mDNSBool private = mDNSfalse; + + InitializeDNSMessage(&m->omsg.h, q->TargetQID, (DNSSECQuestion(q) ? DNSSecQFlags : uQueryFlags)); + + if (q->qDNSServer->teststate != DNSServer_Untested || NoTestQuery(q)) + { + end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); + if (DNSSECQuestion(q) && !q->qDNSServer->cellIntf) + end = putDNSSECOption(&m->omsg, end, m->omsg.data + AbsoluteMaxDNSMessageData); + private = PrivateQuery(q); + } + else if (m->timenow - q->qDNSServer->lasttest >= INIT_UCAST_POLL_INTERVAL) // Make sure at least three seconds has elapsed since last test query + { + LogInfo("Sending DNS test query to %#a:%d", &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port)); + q->ThisQInterval = INIT_UCAST_POLL_INTERVAL / QuestionIntervalStep; + q->qDNSServer->lasttest = m->timenow; + end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, DNSRelayTestQuestion, kDNSType_PTR, kDNSClass_IN); + q->qDNSServer->testid = m->omsg.h.id; + } + + if (end > m->omsg.data && (q->qDNSServer->teststate != DNSServer_Failed || NoTestQuery(q))) + { + //LogMsg("uDNS_CheckCurrentQuestion %p %d %p %##s (%s)", q, NextQSendTime(q) - m->timenow, private, q->qname.c, DNSTypeName(q->qtype)); + if (private) + { + if (q->nta) CancelGetZoneData(m, q->nta); + q->nta = StartGetZoneData(m, &q->qname, q->LongLived ? ZoneServiceLLQ : ZoneServiceQuery, PrivateQueryGotZoneData, q); + if (q->state == LLQ_Poll) q->ThisQInterval = (LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10)) / QuestionIntervalStep; + } + else + { + debugf("uDNS_CheckCurrentQuestion sending %p %##s (%s) %#a:%d UnansweredQueries %d", + q, q->qname.c, DNSTypeName(q->qtype), + q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort), q->unansweredQueries); + if (!q->LocalSocket) q->LocalSocket = mDNSPlatformUDPSocket(m, zeroIPPort); + if (!q->LocalSocket) err = mStatus_NoMemoryErr; // If failed to make socket (should be very rare), we'll try again next time + else err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface, q->LocalSocket, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL, q->UseBrackgroundTrafficClass); + } + } + + if (err != mStatus_TransientErr) // if it is not a transient error backoff and DO NOT flood queries unnecessarily + { + q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep; // Only increase interval if send succeeded + q->unansweredQueries++; + if (q->ThisQInterval > MAX_UCAST_POLL_INTERVAL) + q->ThisQInterval = MAX_UCAST_POLL_INTERVAL; + if (private && q->state != LLQ_Poll) + { + // We don't want to retransmit too soon. Hence, we always schedule our first + // retransmisson at 3 seconds rather than one second + if (q->ThisQInterval < (3 * mDNSPlatformOneSecond)) + q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep; + if (q->ThisQInterval > LLQ_POLL_INTERVAL) + q->ThisQInterval = LLQ_POLL_INTERVAL; + LogInfo("uDNS_CheckCurrentQuestion: private non polling question for %##s (%s) will be retried in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval); + } + if (q->qDNSServer->cellIntf) + { + // We don't want to retransmit too soon. Schedule our first retransmisson at + // MIN_UCAST_RETRANS_TIMEOUT seconds. + if (q->ThisQInterval < MIN_UCAST_RETRANS_TIMEOUT) + q->ThisQInterval = MIN_UCAST_RETRANS_TIMEOUT; + } + debugf("uDNS_CheckCurrentQuestion: Increased ThisQInterval to %d for %##s (%s), cell %d", q->ThisQInterval, q->qname.c, DNSTypeName(q->qtype), q->qDNSServer->cellIntf); + } + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + } + else + { + // If we have no server for this query, or the only server is a disabled one, then we deliver + // a transient failure indication to the client. This is important for things like iPhone + // where we want to return timely feedback to the user when no network is available. + // After calling MakeNegativeCacheRecord() we store the resulting record in the + // cache so that it will be visible to other clients asking the same question. + // (When we have a group of identical questions, only the active representative of the group gets + // passed to uDNS_CheckCurrentQuestion -- we only want one set of query packets hitting the wire -- + // but we want *all* of the questions to get answer callbacks.) + + CacheRecord *rr; + const mDNSu32 slot = HashSlot(&q->qname); + CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + if (cg) + for (rr = cg->members; rr; rr=rr->next) + if (SameNameRecordAnswersQuestion(&rr->resrec, q)) mDNS_PurgeCacheResourceRecord(m, rr); + + if (!q->qDNSServer) + { + if (!mDNSOpaque64IsZero(&q->validDNSServers)) + LogMsg("uDNS_CheckCurrentQuestion: ERROR!!: valid DNSServer bits not zero 0x%x, 0x%x for question %##s (%s)", + q->validDNSServers.l[1], q->validDNSServers.l[0], q->qname.c, DNSTypeName(q->qtype)); + // If we reached the end of list while picking DNS servers, then we don't want to deactivate the + // question. Try after 60 seconds. We find this by looking for valid DNSServers for this question, + // if we find any, then we must have tried them before we came here. This avoids maintaining + // another state variable to see if we had valid DNS servers for this question. + SetValidDNSServers(m, q); + if (mDNSOpaque64IsZero(&q->validDNSServers)) + { + LogInfo("uDNS_CheckCurrentQuestion: no DNS server for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->ThisQInterval = 0; + } + else + { + DNSQuestion *qptr; + // Pretend that we sent this question. As this is an ActiveQuestion, the NextScheduledQuery should + // be set properly. Also, we need to properly backoff in cases where we don't set the question to + // MaxQuestionInterval when we answer the question e.g., LongLived, we need to keep backing off + q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep; + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + // Pick a new DNS server now. Otherwise, when the cache is 80% of its expiry, we will try + // to send a query and come back to the same place here and log the above message. + q->qDNSServer = GetServerForQuestion(m, q); + for (qptr = q->next ; qptr; qptr = qptr->next) + if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; } + LogInfo("uDNS_checkCurrentQuestion: Tried all DNS servers, retry question %p SuppressUnusable %d %##s (%s) with DNS Server %#a:%d after 60 seconds, ThisQInterval %d", + q, q->SuppressUnusable, q->qname.c, DNSTypeName(q->qtype), + q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort), q->ThisQInterval); + } + } + else + { + q->ThisQInterval = 0; + LogMsg("uDNS_CheckCurrentQuestion DNS server %#a:%d for %##s is disabled", &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qname.c); + } + + // For some of the WAB queries that we generate form within the mDNSResponder, most of the home routers + // don't understand and return ServFail/NXDomain. In those cases, we don't want to try too often. We try + // every fifteen minutes in that case + MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, (DomainEnumQuery(&q->qname) ? 60 * 15 : 60), mDNSInterface_Any, q->qDNSServer); + q->unansweredQueries = 0; + // We're already using the m->CurrentQuestion pointer, so CacheRecordAdd can't use it to walk the question list. + // To solve this problem we set rr->DelayDelivery to a nonzero value (which happens to be 'now') so that we + // momentarily defer generating answer callbacks until mDNS_Execute time. + CreateNewCacheEntry(m, slot, cg, NonZeroTime(m->timenow), mDNStrue, mDNSNULL); + ScheduleNextCacheCheckTime(m, slot, NonZeroTime(m->timenow)); + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + // MUST NOT touch m->CurrentQuestion (or q) after this -- client callback could have deleted it + } + } +} mDNSexport void CheckNATMappings(mDNS *m) - { - mStatus err = mStatus_NoError; - mDNSBool rfc1918 = mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4); - mDNSBool HaveRoutable = !rfc1918 && !mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4); - m->NextScheduledNATOp = m->timenow + 0x3FFFFFFF; - - if (HaveRoutable) m->ExternalAddress = m->AdvertisedV4.ip.v4; - - if (m->NATTraversals && rfc1918) // Do we need to open NAT-PMP socket to receive multicast announcements from router? - { - if (m->NATMcastRecvskt == mDNSNULL) // If we are behind a NAT and the socket hasn't been opened yet, open it - { - // we need to log a message if we can't get our socket, but only the first time (after success) - static mDNSBool needLog = mDNStrue; - m->NATMcastRecvskt = mDNSPlatformUDPSocket(m, NATPMPAnnouncementPort); - if (!m->NATMcastRecvskt) - { - if (needLog) - { - LogMsg("CheckNATMappings: Failed to allocate port 5350 UDP multicast socket for NAT-PMP announcements"); - needLog = mDNSfalse; - } - } - else - needLog = mDNStrue; - } - } - else // else, we don't want to listen for announcements, so close them if they're open - { - if (m->NATMcastRecvskt) { mDNSPlatformUDPClose(m->NATMcastRecvskt); m->NATMcastRecvskt = mDNSNULL; } - if (m->SSDPSocket) { debugf("CheckNATMappings destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; } - } - - if (!m->NATTraversals) - m->retryGetAddr = m->timenow + 0x78000000; - else - { - if (m->timenow - m->retryGetAddr >= 0) - { - err = uDNS_SendNATMsg(m, mDNSNULL); // Will also do UPnP discovery for us, if necessary - if (!err) - { - if (m->retryIntervalGetAddr < NATMAP_INIT_RETRY) m->retryIntervalGetAddr = NATMAP_INIT_RETRY; - else if (m->retryIntervalGetAddr < NATMAP_MAX_RETRY_INTERVAL / 2) m->retryIntervalGetAddr *= 2; - else m->retryIntervalGetAddr = NATMAP_MAX_RETRY_INTERVAL; - } - LogInfo("CheckNATMappings retryGetAddr sent address request err %d interval %d", err, m->retryIntervalGetAddr); - - // Always update m->retryGetAddr, even if we fail to send the packet. Otherwise in cases where we can't send the packet - // (like when we have no active interfaces) we'll spin in an infinite loop repeatedly failing to send the packet - m->retryGetAddr = m->timenow + m->retryIntervalGetAddr; - } - // Even when we didn't send the GetAddr packet, still need to make sure NextScheduledNATOp is set correctly - if (m->NextScheduledNATOp - m->retryGetAddr > 0) - m->NextScheduledNATOp = m->retryGetAddr; - } - - if (m->CurrentNATTraversal) LogMsg("WARNING m->CurrentNATTraversal already in use"); - m->CurrentNATTraversal = m->NATTraversals; - - while (m->CurrentNATTraversal) - { - NATTraversalInfo *cur = m->CurrentNATTraversal; - m->CurrentNATTraversal = m->CurrentNATTraversal->next; - - if (HaveRoutable) // If not RFC 1918 address, our own address and port are effectively our external address and port - { - cur->ExpiryTime = 0; - cur->NewResult = mStatus_NoError; - } - else if (cur->Protocol) // Check if it's time to send port mapping packets - { - if (m->timenow - cur->retryPortMap >= 0) // Time to do something with this mapping - { - if (cur->ExpiryTime && cur->ExpiryTime - m->timenow < 0) // Mapping has expired - { - cur->ExpiryTime = 0; - cur->retryInterval = NATMAP_INIT_RETRY; - } - - //LogMsg("uDNS_SendNATMsg"); - err = uDNS_SendNATMsg(m, cur); - - if (cur->ExpiryTime) // If have active mapping then set next renewal time halfway to expiry - NATSetNextRenewalTime(m, cur); - else // else no mapping; use exponential backoff sequence - { - if (cur->retryInterval < NATMAP_INIT_RETRY ) cur->retryInterval = NATMAP_INIT_RETRY; - else if (cur->retryInterval < NATMAP_MAX_RETRY_INTERVAL / 2) cur->retryInterval *= 2; - else cur->retryInterval = NATMAP_MAX_RETRY_INTERVAL; - cur->retryPortMap = m->timenow + cur->retryInterval; - } - } - - if (m->NextScheduledNATOp - cur->retryPortMap > 0) - m->NextScheduledNATOp = cur->retryPortMap; - } - - // Notify the client if necessary. We invoke the callback if: - // (1) we have an ExternalAddress, or we've tried and failed a couple of times to discover it - // and (2) the client doesn't want a mapping, or the client won't need a mapping, or the client has a successful mapping, or we've tried and failed a couple of times - // and (3) we have new data to give the client that's changed since the last callback - // Time line is: Send, Wait 500ms, Send, Wait 1sec, Send, Wait 2sec, Send - // At this point we've sent three requests without an answer, we've just sent our fourth request, - // retryIntervalGetAddr is now 4 seconds, which is greater than NATMAP_INIT_RETRY * 8 (2 seconds), - // so we return an error result to the caller. - if (!mDNSIPv4AddressIsZero(m->ExternalAddress) || m->retryIntervalGetAddr > NATMAP_INIT_RETRY * 8) - { - const mStatus EffectiveResult = cur->NewResult ? cur->NewResult : mDNSv4AddrIsRFC1918(&m->ExternalAddress) ? mStatus_DoubleNAT : mStatus_NoError; - const mDNSIPPort ExternalPort = HaveRoutable ? cur->IntPort : - !mDNSIPv4AddressIsZero(m->ExternalAddress) && cur->ExpiryTime ? cur->RequestedPort : zeroIPPort; - if (!cur->Protocol || HaveRoutable || cur->ExpiryTime || cur->retryInterval > NATMAP_INIT_RETRY * 8) - if (!mDNSSameIPv4Address(cur->ExternalAddress, m->ExternalAddress) || - !mDNSSameIPPort (cur->ExternalPort, ExternalPort) || - cur->Result != EffectiveResult) - { - //LogMsg("NAT callback %d %d %d", cur->Protocol, cur->ExpiryTime, cur->retryInterval); - if (cur->Protocol && mDNSIPPortIsZero(ExternalPort) && !mDNSIPv4AddressIsZero(m->Router.ip.v4)) - { - if (!EffectiveResult) - LogInfo("CheckNATMapping: Failed to obtain NAT port mapping %p from router %#a external address %.4a internal port %5d interval %d error %d", - cur, &m->Router, &m->ExternalAddress, mDNSVal16(cur->IntPort), cur->retryInterval, EffectiveResult); - else - LogMsg("CheckNATMapping: Failed to obtain NAT port mapping %p from router %#a external address %.4a internal port %5d interval %d error %d", - cur, &m->Router, &m->ExternalAddress, mDNSVal16(cur->IntPort), cur->retryInterval, EffectiveResult); - } - - cur->ExternalAddress = m->ExternalAddress; - cur->ExternalPort = ExternalPort; - cur->Lifetime = cur->ExpiryTime && !mDNSIPPortIsZero(ExternalPort) ? - (cur->ExpiryTime - m->timenow + mDNSPlatformOneSecond/2) / mDNSPlatformOneSecond : 0; - cur->Result = EffectiveResult; - mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback - if (cur->clientCallback) - cur->clientCallback(m, cur); - mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again - // MUST NOT touch cur after invoking the callback - } - } - } - } +{ + mStatus err = mStatus_NoError; + mDNSBool rfc1918 = mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4); + mDNSBool HaveRoutable = !rfc1918 && !mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4); + m->NextScheduledNATOp = m->timenow + 0x3FFFFFFF; + + if (HaveRoutable) m->ExternalAddress = m->AdvertisedV4.ip.v4; + + if (m->NATTraversals && rfc1918) // Do we need to open NAT-PMP socket to receive multicast announcements from router? + { + if (m->NATMcastRecvskt == mDNSNULL) // If we are behind a NAT and the socket hasn't been opened yet, open it + { + // we need to log a message if we can't get our socket, but only the first time (after success) + static mDNSBool needLog = mDNStrue; + m->NATMcastRecvskt = mDNSPlatformUDPSocket(m, NATPMPAnnouncementPort); + if (!m->NATMcastRecvskt) + { + if (needLog) + { + LogMsg("CheckNATMappings: Failed to allocate port 5350 UDP multicast socket for NAT-PMP announcements"); + needLog = mDNSfalse; + } + } + else + needLog = mDNStrue; + } + } + else // else, we don't want to listen for announcements, so close them if they're open + { + if (m->NATMcastRecvskt) { mDNSPlatformUDPClose(m->NATMcastRecvskt); m->NATMcastRecvskt = mDNSNULL; } + if (m->SSDPSocket) { debugf("CheckNATMappings destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; } + } + + if (!m->NATTraversals) + m->retryGetAddr = m->timenow + 0x78000000; + else + { + if (m->timenow - m->retryGetAddr >= 0) + { + err = uDNS_SendNATMsg(m, mDNSNULL); // Will also do UPnP discovery for us, if necessary + if (!err) + { + if (m->retryIntervalGetAddr < NATMAP_INIT_RETRY) m->retryIntervalGetAddr = NATMAP_INIT_RETRY; + else if (m->retryIntervalGetAddr < NATMAP_MAX_RETRY_INTERVAL / 2) m->retryIntervalGetAddr *= 2; + else m->retryIntervalGetAddr = NATMAP_MAX_RETRY_INTERVAL; + } + LogInfo("CheckNATMappings retryGetAddr sent address request err %d interval %d", err, m->retryIntervalGetAddr); + + // Always update m->retryGetAddr, even if we fail to send the packet. Otherwise in cases where we can't send the packet + // (like when we have no active interfaces) we'll spin in an infinite loop repeatedly failing to send the packet + m->retryGetAddr = m->timenow + m->retryIntervalGetAddr; + } + // Even when we didn't send the GetAddr packet, still need to make sure NextScheduledNATOp is set correctly + if (m->NextScheduledNATOp - m->retryGetAddr > 0) + m->NextScheduledNATOp = m->retryGetAddr; + } + + if (m->CurrentNATTraversal) LogMsg("WARNING m->CurrentNATTraversal already in use"); + m->CurrentNATTraversal = m->NATTraversals; + + while (m->CurrentNATTraversal) + { + NATTraversalInfo *cur = m->CurrentNATTraversal; + m->CurrentNATTraversal = m->CurrentNATTraversal->next; + + if (HaveRoutable) // If not RFC 1918 address, our own address and port are effectively our external address and port + { + cur->ExpiryTime = 0; + cur->NewResult = mStatus_NoError; + } + else if (cur->Protocol) // Check if it's time to send port mapping packets + { + if (m->timenow - cur->retryPortMap >= 0) // Time to do something with this mapping + { + if (cur->ExpiryTime && cur->ExpiryTime - m->timenow < 0) // Mapping has expired + { + cur->ExpiryTime = 0; + cur->retryInterval = NATMAP_INIT_RETRY; + } + + //LogMsg("uDNS_SendNATMsg"); + err = uDNS_SendNATMsg(m, cur); + + if (cur->ExpiryTime) // If have active mapping then set next renewal time halfway to expiry + NATSetNextRenewalTime(m, cur); + else // else no mapping; use exponential backoff sequence + { + if (cur->retryInterval < NATMAP_INIT_RETRY ) cur->retryInterval = NATMAP_INIT_RETRY; + else if (cur->retryInterval < NATMAP_MAX_RETRY_INTERVAL / 2) cur->retryInterval *= 2; + else cur->retryInterval = NATMAP_MAX_RETRY_INTERVAL; + cur->retryPortMap = m->timenow + cur->retryInterval; + } + } + + if (m->NextScheduledNATOp - cur->retryPortMap > 0) + m->NextScheduledNATOp = cur->retryPortMap; + } + + // Notify the client if necessary. We invoke the callback if: + // (1) we have an ExternalAddress, or we've tried and failed a couple of times to discover it + // and (2) the client doesn't want a mapping, or the client won't need a mapping, or the client has a successful mapping, or we've tried and failed a couple of times + // and (3) we have new data to give the client that's changed since the last callback + // Time line is: Send, Wait 500ms, Send, Wait 1sec, Send, Wait 2sec, Send + // At this point we've sent three requests without an answer, we've just sent our fourth request, + // retryIntervalGetAddr is now 4 seconds, which is greater than NATMAP_INIT_RETRY * 8 (2 seconds), + // so we return an error result to the caller. + if (!mDNSIPv4AddressIsZero(m->ExternalAddress) || m->retryIntervalGetAddr > NATMAP_INIT_RETRY * 8) + { + const mStatus EffectiveResult = cur->NewResult ? cur->NewResult : mDNSv4AddrIsRFC1918(&m->ExternalAddress) ? mStatus_DoubleNAT : mStatus_NoError; + const mDNSIPPort ExternalPort = HaveRoutable ? cur->IntPort : + !mDNSIPv4AddressIsZero(m->ExternalAddress) && cur->ExpiryTime ? cur->RequestedPort : zeroIPPort; + if (!cur->Protocol || HaveRoutable || cur->ExpiryTime || cur->retryInterval > NATMAP_INIT_RETRY * 8) + if (!mDNSSameIPv4Address(cur->ExternalAddress, m->ExternalAddress) || + !mDNSSameIPPort (cur->ExternalPort, ExternalPort) || + cur->Result != EffectiveResult) + { + //LogMsg("NAT callback %d %d %d", cur->Protocol, cur->ExpiryTime, cur->retryInterval); + if (cur->Protocol && mDNSIPPortIsZero(ExternalPort) && !mDNSIPv4AddressIsZero(m->Router.ip.v4)) + { + if (!EffectiveResult) + LogInfo("CheckNATMapping: Failed to obtain NAT port mapping %p from router %#a external address %.4a internal port %5d interval %d error %d", + cur, &m->Router, &m->ExternalAddress, mDNSVal16(cur->IntPort), cur->retryInterval, EffectiveResult); + else + LogMsg("CheckNATMapping: Failed to obtain NAT port mapping %p from router %#a external address %.4a internal port %5d interval %d error %d", + cur, &m->Router, &m->ExternalAddress, mDNSVal16(cur->IntPort), cur->retryInterval, EffectiveResult); + } + + cur->ExternalAddress = m->ExternalAddress; + cur->ExternalPort = ExternalPort; + cur->Lifetime = cur->ExpiryTime && !mDNSIPPortIsZero(ExternalPort) ? + (cur->ExpiryTime - m->timenow + mDNSPlatformOneSecond/2) / mDNSPlatformOneSecond : 0; + cur->Result = EffectiveResult; + mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback + if (cur->clientCallback) + cur->clientCallback(m, cur); + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + // MUST NOT touch cur after invoking the callback + } + } + } +} mDNSlocal mDNSs32 CheckRecordUpdates(mDNS *m) - { - AuthRecord *rr; - mDNSs32 nextevent = m->timenow + 0x3FFFFFFF; - - CheckGroupRecordUpdates(m); - - for (rr = m->ResourceRecords; rr; rr = rr->next) - { - if (!AuthRecord_uDNS(rr)) continue; - if (rr->state == regState_NoTarget) {debugf("CheckRecordUpdates: Record %##s in NoTarget", rr->resrec.name->c); continue;} - // While we are waiting for the port mapping, we have nothing to do. The port mapping callback - // will take care of this - if (rr->state == regState_NATMap) {debugf("CheckRecordUpdates: Record %##s in NATMap", rr->resrec.name->c); continue;} - if (rr->state == regState_Pending || rr->state == regState_DeregPending || rr->state == regState_UpdatePending || - rr->state == regState_Refresh || rr->state == regState_Registered) - { - if (rr->LastAPTime + rr->ThisAPInterval - m->timenow <= 0) - { - if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } - if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4)) - { - // Zero out the updateid so that if we have a pending response from the server, it won't - // be accepted as a valid response. If we accept the response, we might free the new "nta" - if (rr->nta) { rr->updateid = zeroID; CancelGetZoneData(m, rr->nta); } - rr->nta = StartGetZoneData(m, rr->resrec.name, ZoneServiceUpdate, RecordRegistrationGotZoneData, rr); - - // We have just started the GetZoneData. We need to wait for it to finish. SetRecordRetry here - // schedules the update timer to fire in the future. - // - // There are three cases. - // - // 1) When the updates are sent the first time, the first retry is intended to be at three seconds - // in the future. But by calling SetRecordRetry here we set it to nine seconds. But it does not - // matter because when the answer comes back, RecordRegistrationGotZoneData resets the interval - // back to INIT_RECORD_REG_INTERVAL. This also gives enough time for the query. - // - // 2) In the case of update errors (updateError), this causes further backoff as - // RecordRegistrationGotZoneData does not reset the timer. This is intentional as in the case of - // errors, we don't want to update aggressively. - // - // 3) We might be refreshing the update. This is very similar to case (1). RecordRegistrationGotZoneData - // resets it back to INIT_RECORD_REG_INTERVAL. - // - SetRecordRetry(m, rr, 0); - } - else if (rr->state == regState_DeregPending) SendRecordDeregistration(m, rr); - else SendRecordRegistration(m, rr); - } - } - if (nextevent - (rr->LastAPTime + rr->ThisAPInterval) > 0) - nextevent = (rr->LastAPTime + rr->ThisAPInterval); - } - return nextevent; - } +{ + AuthRecord *rr; + mDNSs32 nextevent = m->timenow + 0x3FFFFFFF; + + CheckGroupRecordUpdates(m); + + for (rr = m->ResourceRecords; rr; rr = rr->next) + { + if (!AuthRecord_uDNS(rr)) continue; + if (rr->state == regState_NoTarget) {debugf("CheckRecordUpdates: Record %##s in NoTarget", rr->resrec.name->c); continue;} + // While we are waiting for the port mapping, we have nothing to do. The port mapping callback + // will take care of this + if (rr->state == regState_NATMap) {debugf("CheckRecordUpdates: Record %##s in NATMap", rr->resrec.name->c); continue;} + if (rr->state == regState_Pending || rr->state == regState_DeregPending || rr->state == regState_UpdatePending || + rr->state == regState_Refresh || rr->state == regState_Registered) + { + if (rr->LastAPTime + rr->ThisAPInterval - m->timenow <= 0) + { + if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } + if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4)) + { + // Zero out the updateid so that if we have a pending response from the server, it won't + // be accepted as a valid response. If we accept the response, we might free the new "nta" + if (rr->nta) { rr->updateid = zeroID; CancelGetZoneData(m, rr->nta); } + rr->nta = StartGetZoneData(m, rr->resrec.name, ZoneServiceUpdate, RecordRegistrationGotZoneData, rr); + + // We have just started the GetZoneData. We need to wait for it to finish. SetRecordRetry here + // schedules the update timer to fire in the future. + // + // There are three cases. + // + // 1) When the updates are sent the first time, the first retry is intended to be at three seconds + // in the future. But by calling SetRecordRetry here we set it to nine seconds. But it does not + // matter because when the answer comes back, RecordRegistrationGotZoneData resets the interval + // back to INIT_RECORD_REG_INTERVAL. This also gives enough time for the query. + // + // 2) In the case of update errors (updateError), this causes further backoff as + // RecordRegistrationGotZoneData does not reset the timer. This is intentional as in the case of + // errors, we don't want to update aggressively. + // + // 3) We might be refreshing the update. This is very similar to case (1). RecordRegistrationGotZoneData + // resets it back to INIT_RECORD_REG_INTERVAL. + // + SetRecordRetry(m, rr, 0); + } + else if (rr->state == regState_DeregPending) SendRecordDeregistration(m, rr); + else SendRecordRegistration(m, rr); + } + } + if (nextevent - (rr->LastAPTime + rr->ThisAPInterval) > 0) + nextevent = (rr->LastAPTime + rr->ThisAPInterval); + } + return nextevent; +} mDNSexport void uDNS_Tasks(mDNS *const m) - { - mDNSs32 nexte; - DNSServer *d; - - m->NextuDNSEvent = m->timenow + 0x3FFFFFFF; - - nexte = CheckRecordUpdates(m); - if (m->NextuDNSEvent - nexte > 0) - m->NextuDNSEvent = nexte; - - for (d = m->DNSServers; d; d=d->next) - if (d->penaltyTime) - { - if (m->timenow - d->penaltyTime >= 0) - { - LogInfo("DNS server %#a:%d out of penalty box", &d->addr, mDNSVal16(d->port)); - d->penaltyTime = 0; - } - else - if (m->NextuDNSEvent - d->penaltyTime > 0) - m->NextuDNSEvent = d->penaltyTime; - } - - if (m->CurrentQuestion) - LogMsg("uDNS_Tasks ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); - m->CurrentQuestion = m->Questions; - while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) - { - DNSQuestion *const q = m->CurrentQuestion; - if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID)) - { - uDNS_CheckCurrentQuestion(m); - if (q == m->CurrentQuestion) - if (m->NextuDNSEvent - NextQSendTime(q) > 0) - m->NextuDNSEvent = NextQSendTime(q); - } - // If m->CurrentQuestion wasn't modified out from under us, advance it now - // We can't do this at the start of the loop because uDNS_CheckCurrentQuestion() - // depends on having m->CurrentQuestion point to the right question - if (m->CurrentQuestion == q) - m->CurrentQuestion = q->next; - } - m->CurrentQuestion = mDNSNULL; - } +{ + mDNSs32 nexte; + DNSServer *d; + + m->NextuDNSEvent = m->timenow + 0x3FFFFFFF; + + nexte = CheckRecordUpdates(m); + if (m->NextuDNSEvent - nexte > 0) + m->NextuDNSEvent = nexte; + + for (d = m->DNSServers; d; d=d->next) + if (d->penaltyTime) + { + if (m->timenow - d->penaltyTime >= 0) + { + LogInfo("DNS server %#a:%d out of penalty box", &d->addr, mDNSVal16(d->port)); + d->penaltyTime = 0; + } + else + if (m->NextuDNSEvent - d->penaltyTime > 0) + m->NextuDNSEvent = d->penaltyTime; + } + + if (m->CurrentQuestion) + LogMsg("uDNS_Tasks ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) + { + DNSQuestion *const q = m->CurrentQuestion; + if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID)) + { + uDNS_CheckCurrentQuestion(m); + if (q == m->CurrentQuestion) + if (m->NextuDNSEvent - NextQSendTime(q) > 0) + m->NextuDNSEvent = NextQSendTime(q); + } + // If m->CurrentQuestion wasn't modified out from under us, advance it now + // We can't do this at the start of the loop because uDNS_CheckCurrentQuestion() + // depends on having m->CurrentQuestion point to the right question + if (m->CurrentQuestion == q) + m->CurrentQuestion = q->next; + } + m->CurrentQuestion = mDNSNULL; +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -4573,339 +4625,346 @@ mDNSexport void uDNS_Tasks(mDNS *const m) #endif mDNSexport void SleepRecordRegistrations(mDNS *m) - { - AuthRecord *rr; - for (rr = m->ResourceRecords; rr; rr=rr->next) - { - if (AuthRecord_uDNS(rr)) - { - // Zero out the updateid so that if we have a pending response from the server, it won't - // be accepted as a valid response. - if (rr->nta) { rr->updateid = zeroID; CancelGetZoneData(m, rr->nta); rr->nta = mDNSNULL; } - - if (rr->NATinfo.clientContext) - { - mDNS_StopNATOperation_internal(m, &rr->NATinfo); - rr->NATinfo.clientContext = mDNSNULL; - } - // We are waiting to update the resource record. The original data of the record is - // in OrigRData and the updated value is in InFlightRData. Free the old and the new - // one will be registered when we come back. - if (rr->state == regState_UpdatePending) - { - // act as if the update succeeded, since we're about to delete the name anyway - rr->state = regState_Registered; - // deallocate old RData - if (rr->UpdateCallback) rr->UpdateCallback(m, rr, rr->OrigRData, rr->OrigRDLen); - SetNewRData(&rr->resrec, rr->InFlightRData, rr->InFlightRDLen); - rr->OrigRData = mDNSNULL; - rr->InFlightRData = mDNSNULL; - } - - // If we have not begun the registration process i.e., never sent a registration packet, - // then uDNS_DeregisterRecord will not send a deregistration - uDNS_DeregisterRecord(m, rr); - - // When we wake, we call ActivateUnicastRegistration which starts at StartGetZoneData - } - } - } +{ + AuthRecord *rr; + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if (AuthRecord_uDNS(rr)) + { + // Zero out the updateid so that if we have a pending response from the server, it won't + // be accepted as a valid response. + if (rr->nta) { rr->updateid = zeroID; CancelGetZoneData(m, rr->nta); rr->nta = mDNSNULL; } + + if (rr->NATinfo.clientContext) + { + mDNS_StopNATOperation_internal(m, &rr->NATinfo); + rr->NATinfo.clientContext = mDNSNULL; + } + // We are waiting to update the resource record. The original data of the record is + // in OrigRData and the updated value is in InFlightRData. Free the old and the new + // one will be registered when we come back. + if (rr->state == regState_UpdatePending) + { + // act as if the update succeeded, since we're about to delete the name anyway + rr->state = regState_Registered; + // deallocate old RData + if (rr->UpdateCallback) rr->UpdateCallback(m, rr, rr->OrigRData, rr->OrigRDLen); + SetNewRData(&rr->resrec, rr->InFlightRData, rr->InFlightRDLen); + rr->OrigRData = mDNSNULL; + rr->InFlightRData = mDNSNULL; + } + + // If we have not begun the registration process i.e., never sent a registration packet, + // then uDNS_DeregisterRecord will not send a deregistration + uDNS_DeregisterRecord(m, rr); + + // When we wake, we call ActivateUnicastRegistration which starts at StartGetZoneData + } + } +} mDNSexport void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID) - { - SearchListElem **p; - SearchListElem *tmp = mDNSNULL; - - // Check to see if we already have this domain in our list - for (p = &SearchList; *p; p = &(*p)->next) - if (((*p)->InterfaceID == InterfaceID) && SameDomainName(&(*p)->domain, domain)) - { - // If domain is already in list, and marked for deletion, unmark the delete - // Be careful not to touch the other flags that may be present - LogInfo("mDNS_AddSearchDomain already in list %##s", domain->c); - if ((*p)->flag & SLE_DELETE) (*p)->flag &= ~SLE_DELETE; - tmp = *p; - *p = tmp->next; - tmp->next = mDNSNULL; - break; - } - - - // move to end of list so that we maintain the same order - while (*p) p = &(*p)->next; - - if (tmp) *p = tmp; - else - { - // if domain not in list, add to list, mark as add (1) - *p = mDNSPlatformMemAllocate(sizeof(SearchListElem)); - if (!*p) { LogMsg("ERROR: mDNS_AddSearchDomain - malloc"); return; } - mDNSPlatformMemZero(*p, sizeof(SearchListElem)); - AssignDomainName(&(*p)->domain, domain); - (*p)->next = mDNSNULL; - (*p)->InterfaceID = InterfaceID; - LogInfo("mDNS_AddSearchDomain created new %##s, InterfaceID %p", domain->c, InterfaceID); - } - } +{ + SearchListElem **p; + SearchListElem *tmp = mDNSNULL; + + // Check to see if we already have this domain in our list + for (p = &SearchList; *p; p = &(*p)->next) + if (((*p)->InterfaceID == InterfaceID) && SameDomainName(&(*p)->domain, domain)) + { + // If domain is already in list, and marked for deletion, unmark the delete + // Be careful not to touch the other flags that may be present + LogInfo("mDNS_AddSearchDomain already in list %##s", domain->c); + if ((*p)->flag & SLE_DELETE) (*p)->flag &= ~SLE_DELETE; + tmp = *p; + *p = tmp->next; + tmp->next = mDNSNULL; + break; + } + + + // move to end of list so that we maintain the same order + while (*p) p = &(*p)->next; + + if (tmp) *p = tmp; + else + { + // if domain not in list, add to list, mark as add (1) + *p = mDNSPlatformMemAllocate(sizeof(SearchListElem)); + if (!*p) { LogMsg("ERROR: mDNS_AddSearchDomain - malloc"); return; } + mDNSPlatformMemZero(*p, sizeof(SearchListElem)); + AssignDomainName(&(*p)->domain, domain); + (*p)->next = mDNSNULL; + (*p)->InterfaceID = InterfaceID; + LogInfo("mDNS_AddSearchDomain created new %##s, InterfaceID %p", domain->c, InterfaceID); + } +} mDNSlocal void FreeARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result) - { - (void)m; // unused - if (result == mStatus_MemFree) mDNSPlatformMemFree(rr->RecordContext); - } +{ + (void)m; // unused + if (result == mStatus_MemFree) mDNSPlatformMemFree(rr->RecordContext); +} mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) - { - SearchListElem *slElem = question->QuestionContext; - mStatus err; - const char *name; - - if (answer->rrtype != kDNSType_PTR) return; - if (answer->RecordType == kDNSRecordTypePacketNegative) return; - if (answer->InterfaceID == mDNSInterface_LocalOnly) return; - - if (question == &slElem->BrowseQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeBrowse]; - else if (question == &slElem->DefBrowseQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeBrowseDefault]; - else if (question == &slElem->AutomaticBrowseQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeBrowseAutomatic]; - else if (question == &slElem->RegisterQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeRegistration]; - else if (question == &slElem->DefRegisterQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeRegistrationDefault]; - else { LogMsg("FoundDomain - unknown question"); return; } - - LogInfo("FoundDomain: %p %s %s Q %##s A %s", answer->InterfaceID, AddRecord ? "Add" : "Rmv", name, question->qname.c, RRDisplayString(m, answer)); - - if (AddRecord) - { - ARListElem *arElem = mDNSPlatformMemAllocate(sizeof(ARListElem)); - if (!arElem) { LogMsg("ERROR: FoundDomain out of memory"); return; } - mDNS_SetupResourceRecord(&arElem->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, AuthRecordLocalOnly, FreeARElemCallback, arElem); - MakeDomainNameFromDNSNameString(&arElem->ar.namestorage, name); - AppendDNSNameString (&arElem->ar.namestorage, "local"); - AssignDomainName(&arElem->ar.resrec.rdata->u.name, &answer->rdata->u.name); - LogInfo("FoundDomain: Registering %s", ARDisplayString(m, &arElem->ar)); - err = mDNS_Register(m, &arElem->ar); - if (err) { LogMsg("ERROR: FoundDomain - mDNS_Register returned %d", err); mDNSPlatformMemFree(arElem); return; } - arElem->next = slElem->AuthRecs; - slElem->AuthRecs = arElem; - } - else - { - ARListElem **ptr = &slElem->AuthRecs; - while (*ptr) - { - if (SameDomainName(&(*ptr)->ar.resrec.rdata->u.name, &answer->rdata->u.name)) - { - ARListElem *dereg = *ptr; - *ptr = (*ptr)->next; - LogInfo("FoundDomain: Deregistering %s", ARDisplayString(m, &dereg->ar)); - err = mDNS_Deregister(m, &dereg->ar); - if (err) LogMsg("ERROR: FoundDomain - mDNS_Deregister returned %d", err); - // Memory will be freed in the FreeARElemCallback - } - else - ptr = &(*ptr)->next; - } - } - } +{ + SearchListElem *slElem = question->QuestionContext; + mStatus err; + const char *name; + + if (answer->rrtype != kDNSType_PTR) return; + if (answer->RecordType == kDNSRecordTypePacketNegative) return; + if (answer->InterfaceID == mDNSInterface_LocalOnly) return; + + if (question == &slElem->BrowseQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeBrowse]; + else if (question == &slElem->DefBrowseQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeBrowseDefault]; + else if (question == &slElem->AutomaticBrowseQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeBrowseAutomatic]; + else if (question == &slElem->RegisterQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeRegistration]; + else if (question == &slElem->DefRegisterQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeRegistrationDefault]; + else { LogMsg("FoundDomain - unknown question"); return; } + + LogInfo("FoundDomain: %p %s %s Q %##s A %s", answer->InterfaceID, AddRecord ? "Add" : "Rmv", name, question->qname.c, RRDisplayString(m, answer)); + + if (AddRecord) + { + ARListElem *arElem = mDNSPlatformMemAllocate(sizeof(ARListElem)); + if (!arElem) { LogMsg("ERROR: FoundDomain out of memory"); return; } + mDNS_SetupResourceRecord(&arElem->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, AuthRecordLocalOnly, FreeARElemCallback, arElem); + MakeDomainNameFromDNSNameString(&arElem->ar.namestorage, name); + AppendDNSNameString (&arElem->ar.namestorage, "local"); + AssignDomainName(&arElem->ar.resrec.rdata->u.name, &answer->rdata->u.name); + LogInfo("FoundDomain: Registering %s", ARDisplayString(m, &arElem->ar)); + err = mDNS_Register(m, &arElem->ar); + if (err) { LogMsg("ERROR: FoundDomain - mDNS_Register returned %d", err); mDNSPlatformMemFree(arElem); return; } + arElem->next = slElem->AuthRecs; + slElem->AuthRecs = arElem; + } + else + { + ARListElem **ptr = &slElem->AuthRecs; + while (*ptr) + { + if (SameDomainName(&(*ptr)->ar.resrec.rdata->u.name, &answer->rdata->u.name)) + { + ARListElem *dereg = *ptr; + *ptr = (*ptr)->next; + LogInfo("FoundDomain: Deregistering %s", ARDisplayString(m, &dereg->ar)); + err = mDNS_Deregister(m, &dereg->ar); + if (err) LogMsg("ERROR: FoundDomain - mDNS_Deregister returned %d", err); + // Memory will be freed in the FreeARElemCallback + } + else + ptr = &(*ptr)->next; + } + } +} #if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING mDNSexport void udns_validatelists(void *const v) - { - mDNS *const m = v; - - NATTraversalInfo *n; - for (n = m->NATTraversals; n; n=n->next) - if (n->next == (NATTraversalInfo *)~0 || n->clientCallback == (NATTraversalClientCallback)~0) - LogMemCorruption("m->NATTraversals: %p is garbage", n); - - DNSServer *d; - for (d = m->DNSServers; d; d=d->next) - if (d->next == (DNSServer *)~0 || d->teststate > DNSServer_Disabled) - LogMemCorruption("m->DNSServers: %p is garbage (%d)", d, d->teststate); - - DomainAuthInfo *info; - for (info = m->AuthInfoList; info; info = info->next) - if (info->next == (DomainAuthInfo *)~0 || info->AutoTunnel == (const char*)~0) - LogMemCorruption("m->AuthInfoList: %p is garbage (%X)", info, info->AutoTunnel); - - HostnameInfo *hi; - for (hi = m->Hostnames; hi; hi = hi->next) - if (hi->next == (HostnameInfo *)~0 || hi->StatusCallback == (mDNSRecordCallback*)~0) - LogMemCorruption("m->Hostnames: %p is garbage", n); - - SearchListElem *ptr; - for (ptr = SearchList; ptr; ptr = ptr->next) - if (ptr->next == (SearchListElem *)~0 || ptr->AuthRecs == (void*)~0) - LogMemCorruption("SearchList: %p is garbage (%X)", ptr, ptr->AuthRecs); - } +{ + mDNS *const m = v; + + NATTraversalInfo *n; + for (n = m->NATTraversals; n; n=n->next) + if (n->next == (NATTraversalInfo *)~0 || n->clientCallback == (NATTraversalClientCallback) ~0) + LogMemCorruption("m->NATTraversals: %p is garbage", n); + + DNSServer *d; + for (d = m->DNSServers; d; d=d->next) + if (d->next == (DNSServer *)~0 || d->teststate > DNSServer_Disabled) + LogMemCorruption("m->DNSServers: %p is garbage (%d)", d, d->teststate); + + DomainAuthInfo *info; + for (info = m->AuthInfoList; info; info = info->next) + if (info->next == (DomainAuthInfo *)~0 || info->AutoTunnel == (const char*)~0) + LogMemCorruption("m->AuthInfoList: %p is garbage (%X)", info, info->AutoTunnel); + + HostnameInfo *hi; + for (hi = m->Hostnames; hi; hi = hi->next) + if (hi->next == (HostnameInfo *)~0 || hi->StatusCallback == (mDNSRecordCallback*)~0) + LogMemCorruption("m->Hostnames: %p is garbage", n); + + SearchListElem *ptr; + for (ptr = SearchList; ptr; ptr = ptr->next) + if (ptr->next == (SearchListElem *)~0 || ptr->AuthRecs == (void*)~0) + LogMemCorruption("SearchList: %p is garbage (%X)", ptr, ptr->AuthRecs); +} #endif // This should probably move to the UDS daemon -- the concept of legacy clients and automatic registration / automatic browsing // is really a UDS API issue, not something intrinsic to uDNS mDNSexport mStatus uDNS_SetupSearchDomains(mDNS *const m, int action) - { - SearchListElem **p = &SearchList, *ptr; - mStatus err; - - // step 1: mark each element for removal - for (ptr = SearchList; ptr; ptr = ptr->next) ptr->flag |= SLE_DELETE; - - // Make sure we have the search domains from the platform layer so that if we start the WAB - // queries below, we have the latest information - mDNS_Lock(m); - mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNStrue, mDNSNULL, mDNSNULL, mDNSNULL); - mDNS_Unlock(m); - - if (action & UDNS_START_WAB_QUERY) - m->StartWABQueries = mDNStrue; - - // delete elems marked for removal, do queries for elems marked add - while (*p) - { - ptr = *p; - LogInfo("uDNS_SetupSearchDomains:action %d: Flags %d, AuthRecs %p, InterfaceID %p %##s", action, ptr->flag, ptr->AuthRecs, ptr->InterfaceID, ptr->domain.c); - if (ptr->flag & SLE_DELETE) - { - ARListElem *arList = ptr->AuthRecs; - ptr->AuthRecs = mDNSNULL; - *p = ptr->next; - - // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries - // We suppressed the domain enumeration for scoped search domains below. When we enable that - // enable this. - if ((ptr->flag & SLE_WAB_QUERY_STARTED) && - !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) - { - mDNS_StopGetDomains(m, &ptr->BrowseQ); - mDNS_StopGetDomains(m, &ptr->RegisterQ); - mDNS_StopGetDomains(m, &ptr->DefBrowseQ); - mDNS_StopGetDomains(m, &ptr->DefRegisterQ); - mDNS_StopGetDomains(m, &ptr->AutomaticBrowseQ); - } - - mDNSPlatformMemFree(ptr); - - // deregister records generated from answers to the query - while (arList) - { - ARListElem *dereg = arList; - arList = arList->next; - debugf("Deregistering PTR %##s -> %##s", dereg->ar.resrec.name->c, dereg->ar.resrec.rdata->u.name.c); - err = mDNS_Deregister(m, &dereg->ar); - if (err) LogMsg("uDNS_SetupSearchDomains:: ERROR!! mDNS_Deregister returned %d", err); - // Memory will be freed in the FreeARElemCallback - } - continue; - } - - if ((action & UDNS_START_WAB_QUERY) && !(ptr->flag & SLE_WAB_QUERY_STARTED)) - { - // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries. - // Also, suppress the domain enumeration for scoped search domains for now until there is a need. - if (!SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) - { - mStatus err1, err2, err3, err4, err5; - err1 = mDNS_GetDomains(m, &ptr->BrowseQ, mDNS_DomainTypeBrowse, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); - err2 = mDNS_GetDomains(m, &ptr->DefBrowseQ, mDNS_DomainTypeBrowseDefault, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); - err3 = mDNS_GetDomains(m, &ptr->RegisterQ, mDNS_DomainTypeRegistration, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); - err4 = mDNS_GetDomains(m, &ptr->DefRegisterQ, mDNS_DomainTypeRegistrationDefault, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); - err5 = mDNS_GetDomains(m, &ptr->AutomaticBrowseQ, mDNS_DomainTypeBrowseAutomatic, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); - if (err1 || err2 || err3 || err4 || err5) - LogMsg("uDNS_SetupSearchDomains: 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_DomainTypeBrowseAutomatic)\n", - ptr->domain.c, err1, err2, err3, err4, err5); - ptr->flag |= SLE_WAB_QUERY_STARTED; - } - } - - p = &ptr->next; - } - return mStatus_NoError; - } +{ + SearchListElem **p = &SearchList, *ptr; + mStatus err; + + // step 1: mark each element for removal + for (ptr = SearchList; ptr; ptr = ptr->next) ptr->flag |= SLE_DELETE; + + // Make sure we have the search domains from the platform layer so that if we start the WAB + // queries below, we have the latest information + mDNS_Lock(m); + mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNStrue, mDNSNULL, mDNSNULL, mDNSNULL); + mDNS_Unlock(m); + + if (action & UDNS_START_WAB_QUERY) + m->StartWABQueries = mDNStrue; + + // delete elems marked for removal, do queries for elems marked add + while (*p) + { + ptr = *p; + LogInfo("uDNS_SetupSearchDomains:action %d: Flags %d, AuthRecs %p, InterfaceID %p %##s", action, ptr->flag, ptr->AuthRecs, ptr->InterfaceID, ptr->domain.c); + if (ptr->flag & SLE_DELETE) + { + ARListElem *arList = ptr->AuthRecs; + ptr->AuthRecs = mDNSNULL; + *p = ptr->next; + + // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries + // We suppressed the domain enumeration for scoped search domains below. When we enable that + // enable this. + if ((ptr->flag & SLE_WAB_QUERY_STARTED) && + !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) + { + mDNS_StopGetDomains(m, &ptr->BrowseQ); + mDNS_StopGetDomains(m, &ptr->RegisterQ); + mDNS_StopGetDomains(m, &ptr->DefBrowseQ); + mDNS_StopGetDomains(m, &ptr->DefRegisterQ); + mDNS_StopGetDomains(m, &ptr->AutomaticBrowseQ); + } + + mDNSPlatformMemFree(ptr); + + // deregister records generated from answers to the query + while (arList) + { + ARListElem *dereg = arList; + arList = arList->next; + debugf("Deregistering PTR %##s -> %##s", dereg->ar.resrec.name->c, dereg->ar.resrec.rdata->u.name.c); + err = mDNS_Deregister(m, &dereg->ar); + if (err) LogMsg("uDNS_SetupSearchDomains:: ERROR!! mDNS_Deregister returned %d", err); + // Memory will be freed in the FreeARElemCallback + } + continue; + } + + if ((action & UDNS_START_WAB_QUERY) && !(ptr->flag & SLE_WAB_QUERY_STARTED)) + { + // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries. + // Also, suppress the domain enumeration for scoped search domains for now until there is a need. + if (!SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) + { + mStatus err1, err2, err3, err4, err5; + err1 = mDNS_GetDomains(m, &ptr->BrowseQ, mDNS_DomainTypeBrowse, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); + err2 = mDNS_GetDomains(m, &ptr->DefBrowseQ, mDNS_DomainTypeBrowseDefault, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); + err3 = mDNS_GetDomains(m, &ptr->RegisterQ, mDNS_DomainTypeRegistration, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); + err4 = mDNS_GetDomains(m, &ptr->DefRegisterQ, mDNS_DomainTypeRegistrationDefault, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); + err5 = mDNS_GetDomains(m, &ptr->AutomaticBrowseQ, mDNS_DomainTypeBrowseAutomatic, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); + if (err1 || err2 || err3 || err4 || err5) + LogMsg("uDNS_SetupSearchDomains: 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_DomainTypeBrowseAutomatic)\n", + ptr->domain.c, err1, err2, err3, err4, err5); + ptr->flag |= SLE_WAB_QUERY_STARTED; + } + } + + p = &ptr->next; + } + return mStatus_NoError; +} mDNSexport domainname *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal) - { - SearchListElem *p = SearchList; - int count = *searchIndex; - (void) m; // unused - - if (count < 0) { LogMsg("uDNS_GetNextSearchDomain: count %d less than zero", count); return mDNSNULL; } - - // skip the domains that we already looked at before - for (; count; count--) p = p->next; - - while (p) - { - int labels = CountLabels(&p->domain); - if (labels > 0) - { - const domainname *d = SkipLeadingLabels(&p->domain, labels - 1); - if (SameDomainLabel(d->c, (const mDNSu8 *)"\x4""arpa")) - { - LogInfo("uDNS_GetNextSearchDomain: skipping search domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID); - (*searchIndex)++; - p = p->next; - continue; - } - if (ignoreDotLocal && SameDomainLabel(d->c, (const mDNSu8 *)"\x5""local")) - { - LogInfo("uDNS_GetNextSearchDomain: skipping local domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID); - (*searchIndex)++; - p = p->next; - continue; - } - } - // Point to the next one in the list which we will look at next time. - (*searchIndex)++; - // When we are appending search domains in a ActiveDirectory domain, the question's InterfaceID - // set to mDNSInterface_Unicast. Match the unscoped entries in that case. - if (((InterfaceID == mDNSInterface_Unicast) && (p->InterfaceID == mDNSInterface_Any)) || - p->InterfaceID == InterfaceID) - { - LogInfo("uDNS_GetNextSearchDomain returning domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID); - return &p->domain; - } - LogInfo("uDNS_GetNextSearchDomain skipping domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID); - p = p->next; - } - return mDNSNULL; - } +{ + SearchListElem *p = SearchList; + int count = *searchIndex; + (void) m; // unused + + if (count < 0) { LogMsg("uDNS_GetNextSearchDomain: count %d less than zero", count); return mDNSNULL; } + + // Skip the domains that we already looked at before. Guard against "p" + // being NULL. When search domains change we may not set the SearchListIndex + // of the question to zero immediately e.g., domain enumeration query calls + // uDNS_SetupSearchDomain which reads in the new search domain but does not + // restart the questions immediately. Questions are restarted as part of + // network change and hence temporarily SearchListIndex may be out of range. + + for (; count && p; count--) + p = p->next; + + while (p) + { + int labels = CountLabels(&p->domain); + if (labels > 0) + { + const domainname *d = SkipLeadingLabels(&p->domain, labels - 1); + if (SameDomainLabel(d->c, (const mDNSu8 *)"\x4" "arpa")) + { + LogInfo("uDNS_GetNextSearchDomain: skipping search domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID); + (*searchIndex)++; + p = p->next; + continue; + } + if (ignoreDotLocal && SameDomainLabel(d->c, (const mDNSu8 *)"\x5" "local")) + { + LogInfo("uDNS_GetNextSearchDomain: skipping local domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID); + (*searchIndex)++; + p = p->next; + continue; + } + } + // Point to the next one in the list which we will look at next time. + (*searchIndex)++; + // When we are appending search domains in a ActiveDirectory domain, the question's InterfaceID + // set to mDNSInterface_Unicast. Match the unscoped entries in that case. + if (((InterfaceID == mDNSInterface_Unicast) && (p->InterfaceID == mDNSInterface_Any)) || + p->InterfaceID == InterfaceID) + { + LogInfo("uDNS_GetNextSearchDomain returning domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID); + return &p->domain; + } + LogInfo("uDNS_GetNextSearchDomain skipping domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID); + p = p->next; + } + return mDNSNULL; +} mDNSlocal void FlushAddressCacheRecords(mDNS *const m) - { - mDNSu32 slot; - CacheGroup *cg; - CacheRecord *cr; - FORALL_CACHERECORDS(slot, cg, cr) - { - if (cr->resrec.InterfaceID) continue; - - // If a resource record can answer A or AAAA, they need to be flushed so that we will - // never used to deliver an ADD or RMV - if (RRTypeAnswersQuestionType(&cr->resrec, kDNSType_A) || - RRTypeAnswersQuestionType(&cr->resrec, kDNSType_AAAA)) - { - LogInfo("FlushAddressCacheRecords: Purging Resourcerecord %s", CRDisplayString(m, cr)); - mDNS_PurgeCacheResourceRecord(m, cr); - } - } - } +{ + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *cr; + FORALL_CACHERECORDS(slot, cg, cr) + { + if (cr->resrec.InterfaceID) continue; + + // If a resource record can answer A or AAAA, they need to be flushed so that we will + // deliver an ADD or RMV + if (RRTypeAnswersQuestionType(&cr->resrec, kDNSType_A) || + RRTypeAnswersQuestionType(&cr->resrec, kDNSType_AAAA)) + { + LogInfo("FlushAddressCacheRecords: Purging Resourcerecord %s", CRDisplayString(m, cr)); + mDNS_PurgeCacheResourceRecord(m, cr); + } + } +} // Retry questions which has seach domains appended mDNSexport void RetrySearchDomainQuestions(mDNS *const m) - { - // Purge all the A/AAAA cache records and restart the queries. mDNSCoreRestartAddressQueries - // does this. When we restart the question, we first want to try the new search domains rather - // than use the entries that is already in the cache. When we appended search domains, we might - // have created cache entries which is no longer valid as there are new search domains now +{ + // Purge all the A/AAAA cache records and restart the queries. mDNSCoreRestartAddressQueries + // does this. When we restart the question, we first want to try the new search domains rather + // than use the entries that is already in the cache. When we appended search domains, we might + // have created cache entries which is no longer valid as there are new search domains now - LogInfo("RetrySearchDomainQuestions: Calling mDNSCoreRestartAddressQueries"); - mDNSCoreRestartAddressQueries(m, mDNStrue, FlushAddressCacheRecords, mDNSNULL, mDNSNULL); - } + LogInfo("RetrySearchDomainQuestions: Calling mDNSCoreRestartAddressQueries"); + mDNSCoreRestartAddressQueries(m, mDNStrue, FlushAddressCacheRecords, mDNSNULL, mDNSNULL); +} // Construction of Default Browse domain list (i.e. when clients pass NULL) is as follows: // 1) query for b._dns-sd._udp.local on LocalOnly interface @@ -4918,10 +4977,10 @@ mDNSexport void RetrySearchDomainQuestions(mDNS *const m) // (!!!KRS may add outgoing interface in addition) struct CompileTimeAssertionChecks_uDNS - { - // Check our structures are reasonable sizes. Including overly-large buffers, or embedding - // other overly-large structures instead of having a pointer to them, can inadvertently - // cause structure sizes (and therefore memory usage) to balloon unreasonably. - char sizecheck_tcpInfo_t [(sizeof(tcpInfo_t) <= 9056) ? 1 : -1]; - char sizecheck_SearchListElem[(sizeof(SearchListElem) <= 5000) ? 1 : -1]; - }; +{ + // Check our structures are reasonable sizes. Including overly-large buffers, or embedding + // other overly-large structures instead of having a pointer to them, can inadvertently + // cause structure sizes (and therefore memory usage) to balloon unreasonably. + char sizecheck_tcpInfo_t [(sizeof(tcpInfo_t) <= 9056) ? 1 : -1]; + char sizecheck_SearchListElem[(sizeof(SearchListElem) <= 5000) ? 1 : -1]; +}; diff --git a/mDNSCore/uDNS.h b/mDNSCore/uDNS.h index 8e46ffa..02bdd1c 100755 --- a/mDNSCore/uDNS.h +++ b/mDNSCore/uDNS.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -21,13 +21,13 @@ #include "mDNSEmbeddedAPI.h" #include "DNSCommon.h" -#ifdef __cplusplus - extern "C" { +#ifdef __cplusplus +extern "C" { #endif #define RESTART_GOODBYE_DELAY (6 * mDNSPlatformOneSecond) // delay after restarting LLQ before nuking previous known answers (avoids flutter if we restart before we have networking up) #define INIT_UCAST_POLL_INTERVAL (3 * mDNSPlatformOneSecond) // this interval is used after send failures on network transitions - // which typically heal quickly, so we start agressively and exponentially back off + // which typically heal quickly, so we start agressively and exponentially back off #define MAX_UCAST_POLL_INTERVAL (60 * 60 * mDNSPlatformOneSecond) //#define MAX_UCAST_POLL_INTERVAL (1 * 60 * mDNSPlatformOneSecond) #define LLQ_POLL_INTERVAL (15 * 60 * mDNSPlatformOneSecond) // Polling interval for zones w/ an advertised LLQ port (ie not static zones) if LLQ fails due to NAT, etc. @@ -50,17 +50,21 @@ // the record registration e.g., GetZoneData, we always back off by QuestionIntervalStep // so that the first retry does not happen until 3 seconds which should be enough for TCP/TLS to be done. #define INIT_RECORD_REG_INTERVAL (1 * mDNSPlatformOneSecond) -#define MAX_RECORD_REG_INTERVAL (15 * 60 * mDNSPlatformOneSecond) -#define MERGE_DELAY_TIME (1 * mDNSPlatformOneSecond) +#define MAX_RECORD_REG_INTERVAL (15 * 60 * mDNSPlatformOneSecond) +#define MERGE_DELAY_TIME (1 * mDNSPlatformOneSecond) -// If we are refreshing, we do it at least 5 times with a min update frequency of +// If we are refreshing, we do it at least 5 times with a min update frequency of // 5 minutes -#define MAX_UPDATE_REFRESH_COUNT 5 -#define MIN_UPDATE_REFRESH_TIME (5 * 60 * mDNSPlatformOneSecond) +#define MAX_UPDATE_REFRESH_COUNT 5 +#define MIN_UPDATE_REFRESH_TIME (5 * 60 * mDNSPlatformOneSecond) // For questions that use kDNSServiceFlagsTimeout and we don't have a matching resolver e.g., no dns servers, // then use the default value of 30 seconds -#define DEFAULT_UDNS_TIMEOUT 30 // in seconds +#define DEFAULT_UDNS_TIMEOUT 30 // in seconds + +// For questions that are validating responses (q->ValidatingResponse == 1), use 10 seconds +// which accomodates two DNS servers and two queries per DNS server. +#define DEFAULT_UDNSSEC_TIMEOUT 10 // in seconds // Entry points into unicast-specific routines @@ -79,7 +83,6 @@ extern void SleepRecordRegistrations(mDNS *m); extern mStatus uDNS_UpdateRecord(mDNS *m, AuthRecord *rr); extern void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q); -extern CacheGroup *CacheGroupForName(const mDNS *const m, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name); extern mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr); extern mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, mDNS_Dereg_type drt); extern mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question); @@ -93,7 +96,7 @@ extern void uDNS_CheckCurrentQuestion(mDNS *const m); // 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 srcaddr, const mDNSIPPort srcport); extern void uDNS_Tasks(mDNS *const m); extern void UpdateAllSRVRecords(mDNS *m); @@ -112,12 +115,12 @@ extern mStatus uDNS_SetupSearchDomains(mDNS *const m, int action); extern domainname *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal); typedef enum - { - uDNS_LLQ_Not = 0, // Normal uDNS answer: Flush any stale records from cache, and respect record TTL - uDNS_LLQ_Ignore, // LLQ initial challenge packet: ignore -- has no useful records for us - uDNS_LLQ_Entire, // LLQ initial set of answers: Flush any stale records from cache, but assume TTL is 2 x LLQ refresh interval - uDNS_LLQ_Events // LLQ event packet: don't flush cache; assume TTL is 2 x LLQ refresh interval - } uDNS_LLQType; +{ + uDNS_LLQ_Not = 0, // Normal uDNS answer: Flush any stale records from cache, and respect record TTL + uDNS_LLQ_Ignore, // LLQ initial challenge packet: ignore -- has no useful records for us + uDNS_LLQ_Entire, // LLQ initial set of answers: Flush any stale records from cache, but assume TTL is 2 x LLQ refresh interval + uDNS_LLQ_Events // LLQ event packet: don't flush cache; assume TTL is 2 x LLQ refresh interval +} uDNS_LLQType; extern uDNS_LLQType uDNS_recvLLQResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, DNSQuestion **matchQuestion); extern DomainAuthInfo *GetAuthInfoForName_internal(mDNS *m, const domainname *const name); @@ -125,12 +128,12 @@ extern DomainAuthInfo *GetAuthInfoForQuestion(mDNS *m, const DNSQuestion *const extern void DisposeTCPConn(struct tcpInfo_t *tcp); // NAT traversal -extern void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len); // Called for each received NAT-PMP packet -extern void natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv4Addr ExtAddr); -extern void natTraversalHandlePortMapReply(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSIPPort extport, mDNSu32 lease); +extern void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len); // Called for each received NAT-PMP packet +extern void natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv4Addr ExtAddr); +extern void natTraversalHandlePortMapReply(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSIPPort extport, mDNSu32 lease); -#ifdef __cplusplus - } +#ifdef __cplusplus +} #endif #endif // __UDNS_H_ diff --git a/mDNSMacOS9/Responder.c b/mDNSMacOS9/Responder.c index b412bac..78851a6 100644 --- a/mDNSMacOS9/Responder.c +++ b/mDNSMacOS9/Responder.c @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,169 +15,169 @@ * limitations under the License. */ -#include // For printf() -#include // For strcpy() +#include // For printf() +#include // For strcpy() -#include // For WaitNextEvent() +#include // For WaitNextEvent() #include #include -#include // For SIOUXHandleOneEvent() +#include // For SIOUXHandleOneEvent() #include "dns_sd.h" typedef union { UInt8 b[2]; UInt16 NotAnInteger; } mDNSOpaque16; static UInt16 mDNSVal16(mDNSOpaque16 x) { return((UInt16)(x.b[0]<<8 | x.b[1])); } static mDNSOpaque16 mDNSOpaque16fromIntVal(UInt16 v) - { mDNSOpaque16 x; x.b[0] = (UInt8)(v >> 8); x.b[1] = (UInt8)(v & 0xFF); return(x); } +{ mDNSOpaque16 x; x.b[0] = (UInt8)(v >> 8); x.b[1] = (UInt8)(v & 0xFF); return(x); } typedef struct RegisteredService_struct RegisteredService; struct RegisteredService_struct - { - RegisteredService *next; - DNSServiceRef sdRef; - Boolean gotresult; - DNSServiceErrorType errorCode; - char namestr[64]; - char typestr[kDNSServiceMaxDomainName]; - char domstr [kDNSServiceMaxDomainName]; - }; +{ + RegisteredService *next; + DNSServiceRef sdRef; + Boolean gotresult; + DNSServiceErrorType errorCode; + char namestr[64]; + char typestr[kDNSServiceMaxDomainName]; + char domstr [kDNSServiceMaxDomainName]; +}; static RegisteredService p1, p2, afp, http, njp; static RegisteredService *services = NULL, **nextservice = &services; static void RegCallback(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, - const char *name, const char *regtype, const char *domain, void *context) - { + const char *name, const char *regtype, const char *domain, void *context) +{ RegisteredService *rs = (RegisteredService *)context; - (void)sdRef; // Unused - (void)flags; // Unused + (void)sdRef; // Unused + (void)flags; // Unused rs->gotresult = true; rs->errorCode = errorCode; strcpy(rs->namestr, name); strcpy(rs->typestr, regtype); strcpy(rs->domstr, domain); - } +} static DNSServiceErrorType RegisterService(RegisteredService *rs, mDNSOpaque16 OpaquePort, - const char name[], const char type[], const char domain[], const char txtinfo[]) - { - DNSServiceErrorType err; - unsigned char txtbuffer[257]; - strncpy((char*)txtbuffer+1, txtinfo, 255); - txtbuffer[256] = 0; - txtbuffer[0] = (unsigned char)strlen((char*)txtbuffer); - rs->gotresult = 0; - rs->errorCode = kDNSServiceErr_NoError; - err = DNSServiceRegister(&rs->sdRef, /* kDNSServiceFlagsAutoRename*/ 0, 0, - name, type, domain, NULL, OpaquePort.NotAnInteger, (unsigned short)(1+txtbuffer[0]), txtbuffer, RegCallback, rs); - if (err) - printf("RegisterService(%s %s %s) failed %d\n", name, type, domain, err); - else - { *nextservice = rs; nextservice = &rs->next; } - return(err); - } + const char name[], const char type[], const char domain[], const char txtinfo[]) +{ + DNSServiceErrorType err; + unsigned char txtbuffer[257]; + strncpy((char*)txtbuffer+1, txtinfo, 255); + txtbuffer[256] = 0; + txtbuffer[0] = (unsigned char)strlen((char*)txtbuffer); + rs->gotresult = 0; + rs->errorCode = kDNSServiceErr_NoError; + err = DNSServiceRegister(&rs->sdRef, /* kDNSServiceFlagsAutoRename*/ 0, 0, + name, type, domain, NULL, OpaquePort.NotAnInteger, (unsigned short)(1+txtbuffer[0]), txtbuffer, RegCallback, rs); + if (err) + printf("RegisterService(%s %s %s) failed %d\n", name, type, domain, err); + else + { *nextservice = rs; nextservice = &rs->next; } + return(err); +} // RegisterFakeServiceForTesting() simulates the effect of services being registered on // dynamically-allocated port numbers. No real service exists on that port -- this is just for testing. static DNSServiceErrorType RegisterFakeServiceForTesting(RegisteredService *rs, - const char name[], const char type[], const char domain[], const char txtinfo[]) - { - static UInt16 NextPort = 0xF000; - return RegisterService(rs, mDNSOpaque16fromIntVal(NextPort++), name, type, domain, txtinfo); - } + const char name[], const char type[], const char domain[], const char txtinfo[]) +{ + static UInt16 NextPort = 0xF000; + return RegisterService(rs, mDNSOpaque16fromIntVal(NextPort++), name, type, domain, txtinfo); +} // CreateProxyRegistrationForRealService() checks to see if the given port is currently // in use, and if so, advertises the specified service as present on that port. // This is useful for advertising existing real services (Personal Web Sharing, Personal // File Sharing, etc.) that currently don't register with mDNS Service Discovery themselves. static DNSServiceErrorType CreateProxyRegistrationForRealService(RegisteredService *rs, - const char *servicetype, UInt16 PortAsNumber, const char txtinfo[]) - { - mDNSOpaque16 OpaquePort = mDNSOpaque16fromIntVal(PortAsNumber); - InetAddress ia; - TBind bindReq; - OSStatus err; - TEndpointInfo endpointinfo; - EndpointRef ep = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, &endpointinfo, &err); - if (!ep || err) { printf("OTOpenEndpoint (CreateProxyRegistrationForRealService) failed %d", err); return(err); } - - ia.fAddressType = AF_INET; - ia.fPort = OpaquePort.NotAnInteger; - ia.fHost = 0; - bindReq.addr.maxlen = sizeof(ia); - bindReq.addr.len = sizeof(ia); - bindReq.addr.buf = (UInt8*)&ia; - bindReq.qlen = 0; - err = OTBind(ep, &bindReq, NULL); - - if (err == kOTBadAddressErr) - err = RegisterService(rs, OpaquePort, "", servicetype, "local.", txtinfo); - else if (err) - printf("OTBind failed %d", err); - - OTCloseProvider(ep); - return(err); - } + const char *servicetype, UInt16 PortAsNumber, const char txtinfo[]) +{ + mDNSOpaque16 OpaquePort = mDNSOpaque16fromIntVal(PortAsNumber); + InetAddress ia; + TBind bindReq; + OSStatus err; + TEndpointInfo endpointinfo; + EndpointRef ep = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, &endpointinfo, &err); + if (!ep || err) { printf("OTOpenEndpoint (CreateProxyRegistrationForRealService) failed %d", err); return(err); } + + ia.fAddressType = AF_INET; + ia.fPort = OpaquePort.NotAnInteger; + ia.fHost = 0; + bindReq.addr.maxlen = sizeof(ia); + bindReq.addr.len = sizeof(ia); + bindReq.addr.buf = (UInt8*)&ia; + bindReq.qlen = 0; + err = OTBind(ep, &bindReq, NULL); + + if (err == kOTBadAddressErr) + err = RegisterService(rs, OpaquePort, "", servicetype, "local.", txtinfo); + else if (err) + printf("OTBind failed %d", err); + + OTCloseProvider(ep); + return(err); +} // YieldSomeTime() just cooperatively yields some time to other processes running on classic Mac OS static Boolean YieldSomeTime(UInt32 milliseconds) - { - extern Boolean SIOUXQuitting; - EventRecord e; - WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL); - SIOUXHandleOneEvent(&e); - return(SIOUXQuitting); - } +{ + extern Boolean SIOUXQuitting; + EventRecord e; + WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL); + SIOUXHandleOneEvent(&e); + return(SIOUXQuitting); +} int main() - { - OSStatus err; - RegisteredService *s; - - SIOUXSettings.asktosaveonclose = false; - SIOUXSettings.userwindowtitle = "\pMulticast DNS Responder"; +{ + OSStatus err; + RegisteredService *s; - printf("Multicast DNS Responder\n\n"); - printf("This software reports errors using MacsBug breaks,\n"); - printf("so if you don't have MacsBug installed your Mac may crash.\n\n"); - printf("******************************************************************************\n\n"); + SIOUXSettings.asktosaveonclose = false; + SIOUXSettings.userwindowtitle = "\pMulticast DNS Responder"; - err = InitOpenTransport(); - if (err) { printf("InitOpenTransport failed %d", err); return(err); } + printf("Multicast DNS Responder\n\n"); + printf("This software reports errors using MacsBug breaks,\n"); + printf("so if you don't have MacsBug installed your Mac may crash.\n\n"); + printf("******************************************************************************\n\n"); - printf("Advertising Services...\n"); + err = InitOpenTransport(); + if (err) { printf("InitOpenTransport failed %d", err); return(err); } + + printf("Advertising Services...\n"); #define SRSET 0 #if SRSET==0 - RegisterFakeServiceForTesting(&p1, "Web Server One", "_http._tcp.", "local.", "path=/index.html"); - RegisterFakeServiceForTesting(&p2, "Web Server Two", "_http._tcp.", "local.", "path=/path.html"); + RegisterFakeServiceForTesting(&p1, "Web Server One", "_http._tcp.", "local.", "path=/index.html"); + RegisterFakeServiceForTesting(&p2, "Web Server Two", "_http._tcp.", "local.", "path=/path.html"); #elif SRSET==1 - RegisterFakeServiceForTesting(&p1, "Epson Stylus 900N", "_printer._tcp.", "local.", "rn=lpq1"); - RegisterFakeServiceForTesting(&p2, "HP LaserJet", "_printer._tcp.", "local.", "rn=lpq2"); + RegisterFakeServiceForTesting(&p1, "Epson Stylus 900N", "_printer._tcp.", "local.", "rn=lpq1"); + RegisterFakeServiceForTesting(&p2, "HP LaserJet", "_printer._tcp.", "local.", "rn=lpq2"); #else - RegisterFakeServiceForTesting(&p1, "My Printer", "_printer._tcp.", "local.", "rn=lpq3"); - RegisterFakeServiceForTesting(&p2, "My Other Printer", "_printer._tcp.", "local.", "lrn=pq4"); + RegisterFakeServiceForTesting(&p1, "My Printer", "_printer._tcp.", "local.", "rn=lpq3"); + RegisterFakeServiceForTesting(&p2, "My Other Printer", "_printer._tcp.", "local.", "lrn=pq4"); #endif - // If AFP Server is running, register a record for it - CreateProxyRegistrationForRealService(&afp, "_afpovertcp._tcp.", 548, ""); - - // If Web Server is running, register a record for it - CreateProxyRegistrationForRealService(&http, "_http._tcp.", 80, "path=/index.html"); - - while (!YieldSomeTime(35)) - for (s = services; s; s = s->next) - if (s->gotresult) - { - printf("%s %s %s registered\n", s->namestr, s->typestr, s->domstr); - s->gotresult = false; - } - - for (s = services; s; s = s->next) - if (s->sdRef) DNSServiceRefDeallocate(s->sdRef); - - CloseOpenTransport(); - return(0); - } + // If AFP Server is running, register a record for it + CreateProxyRegistrationForRealService(&afp, "_afpovertcp._tcp.", 548, ""); + + // If Web Server is running, register a record for it + CreateProxyRegistrationForRealService(&http, "_http._tcp.", 80, "path=/index.html"); + + while (!YieldSomeTime(35)) + for (s = services; s; s = s->next) + if (s->gotresult) + { + printf("%s %s %s registered\n", s->namestr, s->typestr, s->domstr); + s->gotresult = false; + } + + for (s = services; s; s = s->next) + if (s->sdRef) DNSServiceRefDeallocate(s->sdRef); + + CloseOpenTransport(); + return(0); +} diff --git a/mDNSMacOS9/Searcher.c b/mDNSMacOS9/Searcher.c index 37875ce..a109683 100644 --- a/mDNSMacOS9/Searcher.c +++ b/mDNSMacOS9/Searcher.c @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,13 +15,13 @@ * limitations under the License. */ -#include // For printf() -#include // For strcpy() +#include // For printf() +#include // For strcpy() -#include // For WaitNextEvent() -#include // For SIOkUnresolvedCFragSymbolAddress +#include // For WaitNextEvent() +#include // For SIOkUnresolvedCFragSymbolAddress -#include // For SIOUXHandleOneEvent() +#include // For SIOUXHandleOneEvent() #include #include @@ -34,110 +34,110 @@ typedef union { UInt8 b[2]; UInt16 NotAnInteger; } mDNSOpaque16; static UInt16 mDNSVal16(mDNSOpaque16 x) { return((UInt16)(x.b[0]<<8 | x.b[1])); } static mDNSOpaque16 mDNSOpaque16fromIntVal(UInt16 v) - { mDNSOpaque16 x; x.b[0] = (UInt8)(v >> 8); x.b[1] = (UInt8)(v & 0xFF); return(x); } +{ mDNSOpaque16 x; x.b[0] = (UInt8)(v >> 8); x.b[1] = (UInt8)(v & 0xFF); return(x); } typedef struct - { - OTLIFO serviceinfolist; - Boolean headerPrinted; - Boolean lostRecords; - } SearcherServices; +{ + OTLIFO serviceinfolist; + Boolean headerPrinted; + Boolean lostRecords; +} SearcherServices; typedef struct - { - SearcherServices *services; - char name[kDNSServiceMaxDomainName]; - char type[kDNSServiceMaxDomainName]; - char domn[kDNSServiceMaxDomainName]; - char host[kDNSServiceMaxDomainName]; - char text[kDNSServiceMaxDomainName]; - InetHost address; - mDNSOpaque16 notAnIntPort; - DNSServiceRef sdRef; - Boolean add; - Boolean dom; - OTLink link; - } linkedServiceInfo; +{ + SearcherServices *services; + char name[kDNSServiceMaxDomainName]; + char type[kDNSServiceMaxDomainName]; + char domn[kDNSServiceMaxDomainName]; + char host[kDNSServiceMaxDomainName]; + char text[kDNSServiceMaxDomainName]; + InetHost address; + mDNSOpaque16 notAnIntPort; + DNSServiceRef sdRef; + Boolean add; + Boolean dom; + OTLink link; +} linkedServiceInfo; static SearcherServices services; // PrintServiceInfo prints the service information to standard out // A real application might want to do something else with the information static void PrintServiceInfo(SearcherServices *services) - { - OTLink *link = OTReverseList(OTLIFOStealList(&services->serviceinfolist)); - - while (link) - { - linkedServiceInfo *s = OTGetLinkObject(link, linkedServiceInfo, link); - - if (!services->headerPrinted) - { - printf("%-55s Type Domain Target Host IP Address Port Info\n", "Name"); - services->headerPrinted = true; - } - - if (s->dom) - { - if (s->add) printf("%-55s available for browsing\n", s->domn); - else printf("%-55s no longer available for browsing\n", s->domn); - } - else - { - char ip[16]; - unsigned char *p = (unsigned char *)&s->address; - sprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); - printf("%-55s %-16s %-14s ", s->name, s->type, s->domn); - if (s->add) printf("%-15s %-15s %5d %s\n", s->host, ip, mDNSVal16(s->notAnIntPort), s->text); - else printf("Removed\n"); - } - - link = link->fNext; - OTFreeMem(s); - } - } +{ + OTLink *link = OTReverseList(OTLIFOStealList(&services->serviceinfolist)); + + while (link) + { + linkedServiceInfo *s = OTGetLinkObject(link, linkedServiceInfo, link); + + if (!services->headerPrinted) + { + printf("%-55s Type Domain Target Host IP Address Port Info\n", "Name"); + services->headerPrinted = true; + } + + if (s->dom) + { + if (s->add) printf("%-55s available for browsing\n", s->domn); + else printf("%-55s no longer available for browsing\n", s->domn); + } + else + { + char ip[16]; + unsigned char *p = (unsigned char *)&s->address; + sprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + printf("%-55s %-16s %-14s ", s->name, s->type, s->domn); + if (s->add) printf("%-15s %-15s %5d %s\n", s->host, ip, mDNSVal16(s->notAnIntPort), s->text); + else printf("Removed\n"); + } + + link = link->fNext; + OTFreeMem(s); + } +} static void FoundInstanceAddress(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, - const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context) - { - linkedServiceInfo *info = (linkedServiceInfo *)context; - SearcherServices *services = info->services; - (void)sdRef; // Unused - (void)interfaceIndex; // Unused - (void)fullname; // Unused - (void)ttl; // Unused - if (errorCode == kDNSServiceErr_NoError) - if (flags & kDNSServiceFlagsAdd) - if (rrclass == ns_c_in && rrtype == ns_t_a && rdlen == sizeof(info->address)) - { - memcpy(&info->address, rdata, sizeof(info->address)); - DNSServiceRefDeallocate(info->sdRef); - OTLIFOEnqueue(&services->serviceinfolist, &info->link); - } - } + const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context) +{ + linkedServiceInfo *info = (linkedServiceInfo *)context; + SearcherServices *services = info->services; + (void)sdRef; // Unused + (void)interfaceIndex; // Unused + (void)fullname; // Unused + (void)ttl; // Unused + if (errorCode == kDNSServiceErr_NoError) + if (flags & kDNSServiceFlagsAdd) + if (rrclass == ns_c_in && rrtype == ns_t_a && rdlen == sizeof(info->address)) + { + memcpy(&info->address, rdata, sizeof(info->address)); + DNSServiceRefDeallocate(info->sdRef); + OTLIFOEnqueue(&services->serviceinfolist, &info->link); + } +} static void FoundInstanceInfo(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, - DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t notAnIntPort, - uint16_t txtLen, const unsigned char *txtRecord, void *context) - { - linkedServiceInfo *info = (linkedServiceInfo *)context; - SearcherServices *services = info->services; - (void)sdRef; // Unused - (void)flags; // Unused - (void)interfaceIndex; // Unused - (void)errorCode; // Unused - (void)fullname; // Unused - strcpy(info->host, hosttarget); - if (txtLen == 0) info->text[0] = 0; - else - { - strncpy(info->text, (char *)txtRecord+1, txtRecord[0]); - info->text[txtRecord[0]] = 0; - } - info->notAnIntPort.NotAnInteger = notAnIntPort; - DNSServiceRefDeallocate(info->sdRef); - DNSServiceQueryRecord(&info->sdRef, 0, 0, info->host, ns_t_a, ns_c_in, FoundInstanceAddress, info); - } + DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t notAnIntPort, + uint16_t txtLen, const unsigned char *txtRecord, void *context) +{ + linkedServiceInfo *info = (linkedServiceInfo *)context; + SearcherServices *services = info->services; + (void)sdRef; // Unused + (void)flags; // Unused + (void)interfaceIndex; // Unused + (void)errorCode; // Unused + (void)fullname; // Unused + strcpy(info->host, hosttarget); + if (txtLen == 0) info->text[0] = 0; + else + { + strncpy(info->text, (char *)txtRecord+1, txtRecord[0]); + info->text[txtRecord[0]] = 0; + } + info->notAnIntPort.NotAnInteger = notAnIntPort; + DNSServiceRefDeallocate(info->sdRef); + DNSServiceQueryRecord(&info->sdRef, 0, 0, info->host, ns_t_a, ns_c_in, FoundInstanceAddress, info); +} // When a new named instance of a service is found, FoundInstance() is called. // In this sample code we turn around and immediately to a DNSServiceResolve() to resolve that service name @@ -146,95 +146,95 @@ static void FoundInstanceInfo(DNSServiceRef sdRef, DNSServiceFlags flags, uint32 // in a real application. Defer resolving until the client has picked which instance from the // long list of services is the one they want to use, and then resolve only that one. static void FoundInstance(DNSServiceRef client, DNSServiceFlags flags, uint32_t interface, DNSServiceErrorType errorCode, - const char *replyName, const char *replyType, const char *replyDomain, void *context) - { + const char *replyName, const char *replyType, const char *replyDomain, void *context) +{ #pragma unused(client, interface, errorCode) - SearcherServices *services = (SearcherServices *)context; - linkedServiceInfo *info; - - if (!services) { DebugStr("\pFoundInstance: services is NULL"); return; } - - info = (linkedServiceInfo *)OTAllocMem(sizeof(linkedServiceInfo)); - if (!info) { services->lostRecords = true; return; } - - info->services = services; - strcpy(info->name, replyName); - strcpy(info->type, replyType); - strcpy(info->domn, replyDomain); - info->text[0] = 0; - info->add = (flags & kDNSServiceFlagsAdd) ? true : false; - info->dom = false; - - if (!info->add) // If TTL == 0 we're deleting a service, - OTLIFOEnqueue(&services->serviceinfolist, &info->link); - else // else we're adding a new service - DNSServiceResolve(&info->sdRef, 0, 0, info->name, info->type, info->domn, FoundInstanceInfo, info); - } + SearcherServices *services = (SearcherServices *)context; + linkedServiceInfo *info; + + if (!services) { DebugStr("\pFoundInstance: services is NULL"); return; } + + info = (linkedServiceInfo *)OTAllocMem(sizeof(linkedServiceInfo)); + if (!info) { services->lostRecords = true; return; } + + info->services = services; + strcpy(info->name, replyName); + strcpy(info->type, replyType); + strcpy(info->domn, replyDomain); + info->text[0] = 0; + info->add = (flags & kDNSServiceFlagsAdd) ? true : false; + info->dom = false; + + if (!info->add) // If TTL == 0 we're deleting a service, + OTLIFOEnqueue(&services->serviceinfolist, &info->link); + else // else we're adding a new service + DNSServiceResolve(&info->sdRef, 0, 0, info->name, info->type, info->domn, FoundInstanceInfo, info); +} // YieldSomeTime() just cooperatively yields some time to other processes running on classic Mac OS static Boolean YieldSomeTime(UInt32 milliseconds) - { - extern Boolean SIOUXQuitting; - EventRecord e; - WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL); - SIOUXHandleOneEvent(&e); - return(SIOUXQuitting); - } +{ + extern Boolean SIOUXQuitting; + EventRecord e; + WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL); + SIOUXHandleOneEvent(&e); + return(SIOUXQuitting); +} int main() - { - OSStatus err; - void *tempmem; - DNSServiceRef sdRef; - DNSServiceErrorType dse; - - SIOUXSettings.asktosaveonclose = false; - SIOUXSettings.userwindowtitle = "\pMulticast DNS Searcher"; - SIOUXSettings.rows = 40; - SIOUXSettings.columns = 160; - - printf("DNS-SD Search Client\n\n"); - printf("This software reports errors using MacsBug breaks,\n"); - printf("so if you don't have MacsBug installed your Mac may crash.\n\n"); - printf("******************************************************************************\n\n"); - - if (DNSServiceBrowse == (void*)kUnresolvedCFragSymbolAddress) - { - printf("Before you can use mDNS/DNS-SD clients, you need to place the \n"); - printf("\"Multicast DNS & DNS-SD\" Extension in the Extensions Folder and restart\n"); - return(-1); - } - - err = InitOpenTransport(); - if (err) { printf("InitOpenTransport failed %d", err); return(err); } - - // Make sure OT has a large enough memory pool for us to draw from at OTNotifier (interrupt) time - tempmem = OTAllocMem(0x10000); - if (tempmem) OTFreeMem(tempmem); - else printf("**** Warning: OTAllocMem couldn't pre-allocate 64K for us.\n"); - - services.serviceinfolist.fHead = NULL; - services.headerPrinted = false; - services.lostRecords = false; - - printf("Sending mDNS service lookup queries and waiting for responses...\n\n"); +{ + OSStatus err; + void *tempmem; + DNSServiceRef sdRef; + DNSServiceErrorType dse; + + SIOUXSettings.asktosaveonclose = false; + SIOUXSettings.userwindowtitle = "\pMulticast DNS Searcher"; + SIOUXSettings.rows = 40; + SIOUXSettings.columns = 160; + + printf("DNS-SD Search Client\n\n"); + printf("This software reports errors using MacsBug breaks,\n"); + printf("so if you don't have MacsBug installed your Mac may crash.\n\n"); + printf("******************************************************************************\n\n"); + + if (DNSServiceBrowse == (void*)kUnresolvedCFragSymbolAddress) + { + printf("Before you can use mDNS/DNS-SD clients, you need to place the \n"); + printf("\"Multicast DNS & DNS-SD\" Extension in the Extensions Folder and restart\n"); + return(-1); + } + + err = InitOpenTransport(); + if (err) { printf("InitOpenTransport failed %d", err); return(err); } + + // Make sure OT has a large enough memory pool for us to draw from at OTNotifier (interrupt) time + tempmem = OTAllocMem(0x10000); + if (tempmem) OTFreeMem(tempmem); + else printf("**** Warning: OTAllocMem couldn't pre-allocate 64K for us.\n"); + + services.serviceinfolist.fHead = NULL; + services.headerPrinted = false; + services.lostRecords = false; + + printf("Sending mDNS service lookup queries and waiting for responses...\n\n"); dse = DNSServiceBrowse(&sdRef, 0, 0, "_http._tcp", "", FoundInstance, &services); - if (dse == kDNSServiceErr_NoError) - { - while (!YieldSomeTime(35)) - { - if (services.serviceinfolist.fHead) - PrintServiceInfo(&services); - - if (services.lostRecords) - { - services.lostRecords = false; - printf("**** Warning: Out of memory: Records have been missed.\n"); - } - } - } - - DNSServiceRefDeallocate(sdRef); - CloseOpenTransport(); - return(0); - } + if (dse == kDNSServiceErr_NoError) + { + while (!YieldSomeTime(35)) + { + if (services.serviceinfolist.fHead) + PrintServiceInfo(&services); + + if (services.lostRecords) + { + services.lostRecords = false; + printf("**** Warning: Out of memory: Records have been missed.\n"); + } + } + } + + DNSServiceRefDeallocate(sdRef); + CloseOpenTransport(); + return(0); +} diff --git a/mDNSMacOS9/ShowInitIcon.c b/mDNSMacOS9/ShowInitIcon.c index 5844c5e..15d0221 100755 --- a/mDNSMacOS9/ShowInitIcon.c +++ b/mDNSMacOS9/ShowInitIcon.c @@ -33,10 +33,10 @@ // The MPW C compiler doesn't accept variables declared at an absolute address, so I use these macros instead. // Only one macro is defined per variable; there is no need to define a Set and a Get accessor like in . -#define LMVCheckSum (* (unsigned short*) 0x928) -#define LMVCoord (* ( short*) 0x92A) -#define LMHCoord (* ( short*) 0x92C) -#define LMHCheckSum (* (unsigned short*) 0x92E) +#define LMVCheckSum (*(unsigned short*) 0x928) +#define LMVCoord (*( short*) 0x92A) +#define LMHCoord (*( short*) 0x92C) +#define LMHCheckSum (*(unsigned short*) 0x92E) // --------------------------------------------------------------------------------------------------------------------- // Prototypes for the subroutines. The main routine comes first; this is necessary to make THINK C's "Custom Header" option work. @@ -50,41 +50,41 @@ static void DrawBWIcon (short iconID, Rect *iconRect); // Main routine. typedef struct { - QDGlobals qd; // Storage for the QuickDraw globals - long qdGlobalsPtr; // A5 points to this place; it will contain a pointer to qd + QDGlobals qd; // Storage for the QuickDraw globals + long qdGlobalsPtr; // A5 points to this place; it will contain a pointer to qd } QDStorage; pascal void ShowInitIcon (short iconFamilyID, Boolean advance) { - long oldA5; // Original value of register A5 - QDStorage qds; // Fake QD globals - CGrafPort colorPort; - GrafPort bwPort; - Rect destRect; - SysEnvRec environment; // Machine configuration. - - oldA5 = SetA5((long) &qds.qdGlobalsPtr); // Tell A5 to point to the end of the fake QD Globals - InitGraf(&qds.qd.thePort); // Initialize the fake QD Globals - - SysEnvirons(curSysEnvVers, &environment); // Find out what kind of machine this is - - ComputeIconRect(&destRect, &qds.qd.screenBits.bounds); // Compute where the icon should be drawn - - if (environment.systemVersion >= 0x0700 && environment.hasColorQD) { - OpenCPort(&colorPort); - PlotIconID(&destRect, atNone, ttNone, iconFamilyID); - CloseCPort(&colorPort); - } - else { - OpenPort(&bwPort); - DrawBWIcon(iconFamilyID, &destRect); - ClosePort(&bwPort); - } - - if (advance) - AdvanceIconPosition (&destRect); - - SetA5(oldA5); // Restore A5 to its previous value + long oldA5; // Original value of register A5 + QDStorage qds; // Fake QD globals + CGrafPort colorPort; + GrafPort bwPort; + Rect destRect; + SysEnvRec environment; // Machine configuration. + + oldA5 = SetA5((long) &qds.qdGlobalsPtr); // Tell A5 to point to the end of the fake QD Globals + InitGraf(&qds.qd.thePort); // Initialize the fake QD Globals + + SysEnvirons(curSysEnvVers, &environment); // Find out what kind of machine this is + + ComputeIconRect(&destRect, &qds.qd.screenBits.bounds); // Compute where the icon should be drawn + + if (environment.systemVersion >= 0x0700 && environment.hasColorQD) { + OpenCPort(&colorPort); + PlotIconID(&destRect, atNone, ttNone, iconFamilyID); + CloseCPort(&colorPort); + } + else { + OpenPort(&bwPort); + DrawBWIcon(iconFamilyID, &destRect); + ClosePort(&bwPort); + } + + if (advance) + AdvanceIconPosition (&destRect); + + SetA5(oldA5); // Restore A5 to its previous value } // --------------------------------------------------------------------------------------------------------------------- @@ -92,7 +92,7 @@ pascal void ShowInitIcon (short iconFamilyID, Boolean advance) static unsigned short CheckSum (short x) { - return (unsigned short)(((x << 1) | (x >> 15)) ^ 0x1021); + return (unsigned short)(((x << 1) | (x >> 15)) ^ 0x1021); } // --------------------------------------------------------------------------------------------------------------------- @@ -100,56 +100,56 @@ static unsigned short CheckSum (short x) static void ComputeIconRect (Rect* iconRect, Rect* screenBounds) { - if (CheckSum(LMHCoord) != LMHCheckSum) // If we are first, we need to initialize the shared data. - LMHCoord = 8; - if (CheckSum(LMVCoord) != LMVCheckSum) - LMVCoord = (short)(screenBounds->bottom - 40); - - if (LMHCoord + 34 > screenBounds->right) { // Check whether we must wrap - iconRect->left = 8; - iconRect->top = (short)(LMVCoord - 40); - } - else { - iconRect->left = LMHCoord; - iconRect->top = LMVCoord; - } - iconRect->right = (short)(iconRect->left + 32); - iconRect->bottom = (short)(iconRect->top + 32); + if (CheckSum(LMHCoord) != LMHCheckSum) // If we are first, we need to initialize the shared data. + LMHCoord = 8; + if (CheckSum(LMVCoord) != LMVCheckSum) + LMVCoord = (short)(screenBounds->bottom - 40); + + if (LMHCoord + 34 > screenBounds->right) { // Check whether we must wrap + iconRect->left = 8; + iconRect->top = (short)(LMVCoord - 40); + } + else { + iconRect->left = LMHCoord; + iconRect->top = LMVCoord; + } + iconRect->right = (short)(iconRect->left + 32); + iconRect->bottom = (short)(iconRect->top + 32); } // AdvanceIconPosition updates the shared global variables so that the next extension will draw its icon beside ours. static void AdvanceIconPosition (Rect* iconRect) { - LMHCoord = (short)(iconRect->left + 40); // Update the shared data - LMVCoord = iconRect->top; - LMHCheckSum = CheckSum(LMHCoord); - LMVCheckSum = CheckSum(LMVCoord); + LMHCoord = (short)(iconRect->left + 40); // Update the shared data + LMVCoord = iconRect->top; + LMHCheckSum = CheckSum(LMHCoord); + LMVCheckSum = CheckSum(LMVCoord); } // DrawBWIcon draws the 'ICN#' member of the icon family. It works under System 6. static void DrawBWIcon (short iconID, Rect *iconRect) { - Handle icon; - BitMap source, destination; - GrafPtr port; - - icon = Get1Resource('ICN#', iconID); - if (icon != NULL) { - HLock(icon); - // Prepare the source and destination bitmaps. - source.baseAddr = *icon + 128; // Mask address. - source.rowBytes = 4; - SetRect(&source.bounds, 0, 0, 32, 32); - GetPort(&port); - destination = port->portBits; - // Transfer the mask. - CopyBits(&source, &destination, &source.bounds, iconRect, srcBic, nil); - // Then the icon. - source.baseAddr = *icon; - CopyBits(&source, &destination, &source.bounds, iconRect, srcOr, nil); - } + Handle icon; + BitMap source, destination; + GrafPtr port; + + icon = Get1Resource('ICN#', iconID); + if (icon != NULL) { + HLock(icon); + // Prepare the source and destination bitmaps. + source.baseAddr = *icon + 128; // Mask address. + source.rowBytes = 4; + SetRect(&source.bounds, 0, 0, 32, 32); + GetPort(&port); + destination = port->portBits; + // Transfer the mask. + CopyBits(&source, &destination, &source.bounds, iconRect, srcBic, nil); + // Then the icon. + source.baseAddr = *icon; + CopyBits(&source, &destination, &source.bounds, iconRect, srcOr, nil); + } } // --------------------------------------------------------------------------------------------------------------------- diff --git a/mDNSMacOS9/SubTypeTester.c b/mDNSMacOS9/SubTypeTester.c index e7e76d5..4845dfb 100644 --- a/mDNSMacOS9/SubTypeTester.c +++ b/mDNSMacOS9/SubTypeTester.c @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,15 +15,15 @@ * limitations under the License. */ -#include // For printf() -#include // For strlen() etc. +#include // For printf() +#include // For strlen() etc. -#include // For WaitNextEvent() -#include // For SIOUXHandleOneEvent() +#include // For WaitNextEvent() +#include // For SIOUXHandleOneEvent() -#include "mDNSEmbeddedAPI.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 +#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. @@ -38,180 +38,180 @@ static Boolean availRec2Active; // 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); - } +{ + 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, 0); // Callback, context, flags - - ConvertDomainNameToCString(recordset->RR_SRV.resrec.name, buffer); - printf("Made Service Records for %s\n", buffer); - } + 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, 0); // Callback, context, flags + + 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); - } + 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]); +{ + 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; + 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."); + RegisterFakeServiceForTesting(m, &p1, "", "One", "_raop._tcp.", "local."); + RegisterFakeServiceForTesting(m, &p2, "", "Two", "_raop._tcp.", "local."); - return(kOTNoError); - } + 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; - } +{ + 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)); - } +{ + // 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); - } +{ + 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; +{ + mStatus err; + Boolean DoneSetup = false; + mDNSs32 nextAvail, nextBusy; - SIOUXSettings.asktosaveonclose = false; - SIOUXSettings.userwindowtitle = "\pMulticast DNS Responder"; + 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"); + 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 = 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); + 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)) - { + 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 + // 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); - } + 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/mDNSLibrary.c b/mDNSMacOS9/mDNSLibrary.c index 31d9423..6328395 100644 --- a/mDNSMacOS9/mDNSLibrary.c +++ b/mDNSMacOS9/mDNSLibrary.c @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,8 +17,8 @@ // Define the required CFM Shared Library entry and exit points #include -#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 +#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; static mDNS_PlatformSupport PlatformSupportStorage; @@ -27,37 +27,37 @@ static mDNS_PlatformSupport PlatformSupportStorage; static CacheEntity rrcachestorage[RR_CACHE_SIZE]; mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result) - { - if (result == mStatus_GrowCache) - { - // Allocate another chunk of cache storage - CacheEntity *storage = OTAllocMem(sizeof(CacheEntity) * RR_CACHE_SIZE); - if (storage) mDNS_GrowCache(m, storage, RR_CACHE_SIZE); - } - } +{ + if (result == mStatus_GrowCache) + { + // Allocate another chunk of cache storage + CacheEntity *storage = OTAllocMem(sizeof(CacheEntity) * RR_CACHE_SIZE); + if (storage) mDNS_GrowCache(m, storage, RR_CACHE_SIZE); + } +} extern pascal OSErr mDNS_CFMInit(const CFragInitBlock *theInitBlock); pascal OSErr mDNS_CFMInit(const CFragInitBlock *theInitBlock) - { - extern pascal OSErr __initialize(const CFragInitBlock *theInitBlock); - __initialize(theInitBlock); // MUST do this first! - { - mStatus err; - THz oldZone = GetZone(); - SetZone(SystemZone()); - LogMsg("mDNS/DNS-SD with Macsbug breaks -- do not ship this version to customers"); - err = mDNS_Init(&mDNSStorage, &PlatformSupportStorage, rrcachestorage, RR_CACHE_SIZE, - mDNS_Init_AdvertiseLocalAddresses, mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext); - SetZone(oldZone); - return((OSErr)err); - } - } - +{ + extern pascal OSErr __initialize(const CFragInitBlock *theInitBlock); + __initialize(theInitBlock); // MUST do this first! + { + mStatus err; + THz oldZone = GetZone(); + SetZone(SystemZone()); + LogMsg("mDNS/DNS-SD with Macsbug breaks -- do not ship this version to customers"); + err = mDNS_Init(&mDNSStorage, &PlatformSupportStorage, rrcachestorage, RR_CACHE_SIZE, + mDNS_Init_AdvertiseLocalAddresses, mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext); + SetZone(oldZone); + return((OSErr)err); + } +} + extern void mDNS_CFMTerm(void); void mDNS_CFMTerm(void) - { - extern pascal void __terminate(void); - LogMsg("mDNS_CFMTerm"); - mDNS_Close(&mDNSStorage); - __terminate(); - } +{ + extern pascal void __terminate(void); + LogMsg("mDNS_CFMTerm"); + mDNS_Close(&mDNSStorage); + __terminate(); +} diff --git a/mDNSMacOS9/mDNSLibraryLoader.c b/mDNSMacOS9/mDNSLibraryLoader.c index d4b3650..584ff9e 100644 --- a/mDNSMacOS9/mDNSLibraryLoader.c +++ b/mDNSMacOS9/mDNSLibraryLoader.c @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,47 +22,47 @@ extern pascal OSErr FragRegisterFileLibs(ConstFSSpecPtr fss, Boolean unregister); extern void main(void) - { - OSStatus err; - FCBPBRec fcbPB; - FSSpec fss; +{ + OSStatus err; + FCBPBRec fcbPB; + FSSpec fss; - // 1. Show our "icon march" icon - ShowInitIcon(128, true); + // 1. Show our "icon march" icon + ShowInitIcon(128, true); - // 2. Find our FSSpec - fss.name[0] = 0; - fcbPB.ioNamePtr = fss.name; - fcbPB.ioVRefNum = 0; - fcbPB.ioRefNum = (short)CurResFile(); - fcbPB.ioFCBIndx = 0; - err = PBGetFCBInfoSync(&fcbPB); + // 2. Find our FSSpec + fss.name[0] = 0; + fcbPB.ioNamePtr = fss.name; + fcbPB.ioVRefNum = 0; + fcbPB.ioRefNum = (short)CurResFile(); + fcbPB.ioFCBIndx = 0; + err = PBGetFCBInfoSync(&fcbPB); - // 3. Tell CFM that we're a CFM library container file - fss.vRefNum = fcbPB.ioFCBVRefNum; - fss.parID = fcbPB.ioFCBParID; - if (err == noErr) err = FragRegisterFileLibs(&fss, false); + // 3. Tell CFM that we're a CFM library container file + fss.vRefNum = fcbPB.ioFCBVRefNum; + fss.parID = fcbPB.ioFCBParID; + if (err == noErr) err = FragRegisterFileLibs(&fss, false); - // 4. Now that CFM knows we're a library container, tell it to go and get our library - if (err == noErr) - { - CFragConnectionID c; - Ptr m; - Str255 e; - THz oldZone = GetZone(); - SetZone(SystemZone()); - err = GetSharedLibrary("\pDarwin;mDNS", kPowerPCCFragArch, kLoadCFrag, &c, &m, e); - SetZone(oldZone); - } - } + // 4. Now that CFM knows we're a library container, tell it to go and get our library + if (err == noErr) + { + CFragConnectionID c; + Ptr m; + Str255 e; + THz oldZone = GetZone(); + SetZone(SystemZone()); + err = GetSharedLibrary("\pDarwin;mDNS", kPowerPCCFragArch, kLoadCFrag, &c, &m, e); + SetZone(oldZone); + } +} // There's no CFM stub library for the FragRegisterFileLibs() call, so we'll make our own #if __ide_target("FragRegisterFileLibsStub") #pragma export on pascal OSErr FragRegisterFileLibs(ConstFSSpecPtr fss, Boolean unregister) - { - (void)fss; // Unused - (void)unregister; // Unused - return(0); - } +{ + (void)fss; // Unused + (void)unregister; // Unused + return(0); +} #endif diff --git a/mDNSMacOS9/mDNSMacOS9.c b/mDNSMacOS9/mDNSMacOS9.c index 8ad0533..24b03f2 100644 --- a/mDNSMacOS9/mDNSMacOS9.c +++ b/mDNSMacOS9/mDNSMacOS9.c @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,21 +16,21 @@ */ #include -#include // For va_list support +#include // For va_list support -#include // For LMGetCurApName() -#include // For smSystemScript -#include // For ConvertFromPStringToUnicode() +#include // For LMGetCurApName() +#include // For smSystemScript +#include // For ConvertFromPStringToUnicode() -#include "mDNSEmbeddedAPI.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 +#include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform // *************************************************************************** // Constants static const TSetBooleanOption kReusePortOption = - { kOTBooleanOptionSize, INET_IP, IP_REUSEPORT, 0, true }; +{ kOTBooleanOptionSize, INET_IP, IP_REUSEPORT, 0, true }; // IP_RCVDSTADDR with TSetByteOption/kOTOneByteOptionSize works on OS 9, OS X Classic, and OS 9 Carbon, // but gives error #-3151 (kOTBadOptionErr) on OS X Carbon. @@ -38,18 +38,18 @@ static const TSetBooleanOption kReusePortOption = // no longer returns -3151 but it still doesn't actually work -- no destination addresses // are delivered by OTRcvUData. I think it's just a bug in OS X Carbon. static const TSetByteOption kRcvDestAddrOption = - { kOTOneByteOptionSize, INET_IP, IP_RCVDSTADDR, 0, true }; +{ kOTOneByteOptionSize, INET_IP, IP_RCVDSTADDR, 0, true }; //static const TSetBooleanOption kRcvDestAddrOption = // { kOTBooleanOptionSize, INET_IP, IP_RCVDSTADDR, 0, true }; static const TSetByteOption kSetUnicastTTLOption = - { kOTOneByteOptionSize, INET_IP, IP_TTL, 0, 255 }; +{ kOTOneByteOptionSize, INET_IP, IP_TTL, 0, 255 }; static const TSetByteOption kSetMulticastTTLOption = - { kOTOneByteOptionSize, INET_IP, IP_MULTICAST_TTL, 0, 255 }; +{ kOTOneByteOptionSize, INET_IP, IP_MULTICAST_TTL, 0, 255 }; static const TIPAddMulticastOption kAddLinkMulticastOption = - { sizeof(TIPAddMulticastOption), INET_IP, IP_ADD_MEMBERSHIP, 0, { 224, 0, 0,251 }, { 0,0,0,0 } }; +{ sizeof(TIPAddMulticastOption), INET_IP, IP_ADD_MEMBERSHIP, 0, { 224, 0, 0,251 }, { 0,0,0,0 } }; //static const TIPAddMulticastOption kAddAdminMulticastOption = // { sizeof(TIPAddMulticastOption), INET_IP, IP_ADD_MEMBERSHIP, 0, { 239,255,255,251 }, { 0,0,0,0 } }; @@ -68,29 +68,29 @@ static const TNetbuf zeroTNetbuf = { 0 }; // Functions mDNSlocal void SafeDebugStr(unsigned char *buffer) - { - int i; - // Don't want semicolons in MacsBug messages -- they signify commands to execute - for (i=1; i<= buffer[0]; i++) if (buffer[i] == ';') buffer[i] = '.'; - DebugStr(buffer); - } +{ + int i; + // Don't want semicolons in MacsBug messages -- they signify commands to execute + for (i=1; i<= buffer[0]; i++) if (buffer[i] == ';') buffer[i] = '.'; + DebugStr(buffer); +} #if MDNS_DEBUGMSGS mDNSexport void debugf_(const char *format, ...) - { - unsigned char buffer[256]; +{ + unsigned char buffer[256]; va_list ptr; - va_start(ptr,format); - buffer[0] = (unsigned char)mDNS_vsnprintf((char*)buffer+1, 255, format, ptr); - va_end(ptr); + va_start(ptr,format); + buffer[0] = (unsigned char)mDNS_vsnprintf((char*)buffer+1, 255, format, ptr); + va_end(ptr); #if MDNS_ONLYSYSTEMTASK - buffer[1+buffer[0]] = 0; - fprintf(stderr, "%s\n", buffer+1); - fflush(stderr); + buffer[1+buffer[0]] = 0; + fprintf(stderr, "%s\n", buffer+1); + fflush(stderr); #else - SafeDebugStr(buffer); + SafeDebugStr(buffer); #endif - } +} #endif #if MDNS_BUILDINGSHAREDLIBRARY >= 2 @@ -99,566 +99,566 @@ mDNSexport void debugf_(const char *format, ...) mDNSexport void LogMsg(const char *format, ...) { (void)format; } #else mDNSexport void LogMsg(const char *format, ...) - { - unsigned char buffer[256]; +{ + unsigned char buffer[256]; va_list ptr; - va_start(ptr,format); - buffer[0] = (unsigned char)mDNS_vsnprintf((char*)buffer+1, 255, format, ptr); - va_end(ptr); + va_start(ptr,format); + buffer[0] = (unsigned char)mDNS_vsnprintf((char*)buffer+1, 255, format, ptr); + va_end(ptr); #if MDNS_ONLYSYSTEMTASK - buffer[1+buffer[0]] = 0; - fprintf(stderr, "%s\n", buffer+1); - fflush(stderr); + buffer[1+buffer[0]] = 0; + fprintf(stderr, "%s\n", buffer+1); + fflush(stderr); #else - SafeDebugStr(buffer); + SafeDebugStr(buffer); #endif - } +} #endif 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 - #pragma unused(InterfaceID) - - InetAddress InetDest; - TUnitData senddata; - - if (dst->type != mDNSAddrType_IPv4) return(mStatus_NoError); - - InetDest.fAddressType = AF_INET; - InetDest.fPort = dstPort.NotAnInteger; - InetDest.fHost = dst->ip.v4.NotAnInteger; - - senddata.addr .maxlen = sizeof(InetDest); - senddata.addr .len = sizeof(InetDest); - senddata.addr .buf = (UInt8*)&InetDest; - senddata.opt = zeroTNetbuf; - senddata.udata.maxlen = (UInt32)((UInt8*)end - (UInt8*)msg); - senddata.udata.len = (UInt32)((UInt8*)end - (UInt8*)msg); - senddata.udata.buf = (UInt8*)msg; - - return(OTSndUData(m->p->ep, &senddata)); - } + mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstPort) +{ + // Note: If we did multi-homing, we'd have to use the InterfaceID parameter to specify from which interface to send this response + #pragma unused(InterfaceID) + + InetAddress InetDest; + TUnitData senddata; + + if (dst->type != mDNSAddrType_IPv4) return(mStatus_NoError); + + InetDest.fAddressType = AF_INET; + InetDest.fPort = dstPort.NotAnInteger; + InetDest.fHost = dst->ip.v4.NotAnInteger; + + senddata.addr.maxlen = sizeof(InetDest); + senddata.addr.len = sizeof(InetDest); + senddata.addr.buf = (UInt8*)&InetDest; + senddata.opt = zeroTNetbuf; + senddata.udata.maxlen = (UInt32)((UInt8*)end - (UInt8*)msg); + senddata.udata.len = (UInt32)((UInt8*)end - (UInt8*)msg); + senddata.udata.buf = (UInt8*)msg; + + return(OTSndUData(m->p->ep, &senddata)); +} mDNSlocal OSStatus readpacket(mDNS *m) - { - mDNSAddr senderaddr, destaddr; - mDNSInterfaceID interface; - mDNSIPPort senderport; - InetAddress sender; - char options[256]; - DNSMessage packet; - TUnitData recvdata; - OTFlags flags = 0; - OSStatus err; - - recvdata.addr .maxlen = sizeof(sender); - recvdata.addr .len = 0; - recvdata.addr .buf = (UInt8*)&sender; - recvdata.opt .maxlen = sizeof(options); - recvdata.opt .len = 0; - recvdata.opt .buf = (UInt8*)&options; - recvdata.udata.maxlen = sizeof(packet); - recvdata.udata.len = 0; - recvdata.udata.buf = (UInt8*)&packet; - - err = OTRcvUData(m->p->ep, &recvdata, &flags); - if (err && err != kOTNoDataErr) debugf("OTRcvUData error %d", err); - - if (err) return(err); - - senderaddr.type = mDNSAddrType_IPv4; - senderaddr.ip.v4.NotAnInteger = sender.fHost; - senderport.NotAnInteger = sender.fPort; - - destaddr.type = mDNSAddrType_IPv4; - destaddr.ip.v4 = 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_v4.ip.v4; - #endif - - if (recvdata.opt.len) - { - TOption *c = nil; - while (1) - { - err = OTNextOption(recvdata.opt.buf, recvdata.opt.len, &c); - if (err || !c) break; - if (c->level == INET_IP && c->name == IP_RCVDSTADDR && c->len - kOTOptionHeaderSize == sizeof(destaddr.ip.v4)) - mDNSPlatformMemCopy(&destaddr.ip.v4, c->value, sizeof(destaddr.ip.v4)); - } - } - - interface = m->HostInterfaces->InterfaceID; - - 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 mDNSCoreReceive(m, &packet, recvdata.udata.buf + recvdata.udata.len, &senderaddr, senderport, &destaddr, MulticastDNSPort, interface); - - return(err); - } +{ + mDNSAddr senderaddr, destaddr; + mDNSInterfaceID interface; + mDNSIPPort senderport; + InetAddress sender; + char options[256]; + DNSMessage packet; + TUnitData recvdata; + OTFlags flags = 0; + OSStatus err; + + recvdata.addr.maxlen = sizeof(sender); + recvdata.addr.len = 0; + recvdata.addr.buf = (UInt8*)&sender; + recvdata.opt.maxlen = sizeof(options); + recvdata.opt.len = 0; + recvdata.opt.buf = (UInt8*)&options; + recvdata.udata.maxlen = sizeof(packet); + recvdata.udata.len = 0; + recvdata.udata.buf = (UInt8*)&packet; + + err = OTRcvUData(m->p->ep, &recvdata, &flags); + if (err && err != kOTNoDataErr) debugf("OTRcvUData error %d", err); + + if (err) return(err); + + senderaddr.type = mDNSAddrType_IPv4; + senderaddr.ip.v4.NotAnInteger = sender.fHost; + senderport.NotAnInteger = sender.fPort; + + destaddr.type = mDNSAddrType_IPv4; + destaddr.ip.v4 = 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_v4.ip.v4; + #endif + + if (recvdata.opt.len) + { + TOption *c = nil; + while (1) + { + err = OTNextOption(recvdata.opt.buf, recvdata.opt.len, &c); + if (err || !c) break; + if (c->level == INET_IP && c->name == IP_RCVDSTADDR && c->len - kOTOptionHeaderSize == sizeof(destaddr.ip.v4)) + mDNSPlatformMemCopy(&destaddr.ip.v4, c->value, sizeof(destaddr.ip.v4)); + } + } + + interface = m->HostInterfaces->InterfaceID; + + 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 mDNSCoreReceive(m, &packet, recvdata.udata.buf + recvdata.udata.len, &senderaddr, senderport, &destaddr, MulticastDNSPort, interface); + + return(err); +} mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS * const m, TCPSocketFlags flags, mDNSIPPort * port) - { - (void)m; // Unused - (void)flags; // Unused - (void)port; // Unused - return NULL; - } +{ + (void)m; // Unused + (void)flags; // Unused + (void)port; // Unused + return NULL; +} mDNSexport TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int sd) - { - (void)flags; // Unused - (void)sd; // Unused - return NULL; - } +{ + (void)flags; // Unused + (void)sd; // Unused + return NULL; +} mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock) - { - (void)sock; // Unused - return -1; - } +{ + (void)sock; // Unused + return -1; +} mDNSexport mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr * dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID, - TCPConnectionCallback callback, void * context) - { - (void)sock; // Unused - (void)dst; // Unused - (void)dstport; // Unused - (void)InterfaceID; // Unused - (void)callback; // Unused - (void)context; // Unused - return(mStatus_UnsupportedErr); - } + TCPConnectionCallback callback, void * context) +{ + (void)sock; // Unused + (void)dst; // Unused + (void)dstport; // Unused + (void)InterfaceID; // Unused + (void)callback; // Unused + (void)context; // Unused + return(mStatus_UnsupportedErr); +} mDNSexport void mDNSPlatformTCPCloseConnection(TCPSocket *sd) - { - (void)sd; // Unused - } +{ + (void)sd; // Unused +} mDNSexport long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool *closed) - { - (void)sock; // Unused - (void)buf; // Unused - (void)buflen; // Unused - (void)closed; // Unused - return(0); - } +{ + (void)sock; // Unused + (void)buf; // Unused + (void)buflen; // Unused + (void)closed; // Unused + return(0); +} mDNSexport long mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len) - { - (void)sock; // Unused - (void)msg; // Unused - (void)len; // Unused - return(0); - } +{ + (void)sock; // Unused + (void)msg; // Unused + (void)len; // Unused + return(0); +} mDNSexport UDPSocket *mDNSPlatformUDPSocket(mDNS * const m, mDNSIPPort port) - { - (void)m; // Unused - (void)port; // Unused - return NULL; - } +{ + (void)m; // Unused + (void)port; // Unused + return NULL; +} mDNSexport void mDNSPlatformUDPClose(UDPSocket *sock) - { - (void)sock; // Unused - } +{ + (void)sock; // Unused +} mDNSlocal void mDNSOptionManagement(mDNS *const m) - { - OSStatus err; +{ + OSStatus err; - // Make sure the length in the TNetbuf agrees with the length in the TOptionHeader - m->p->optReq.opt.len = m->p->optBlock.h.len; - m->p->optReq.opt.maxlen = m->p->optBlock.h.len; - if (m->p->optReq.opt.maxlen < 4) - m->p->optReq.opt.maxlen = 4; + // Make sure the length in the TNetbuf agrees with the length in the TOptionHeader + m->p->optReq.opt.len = m->p->optBlock.h.len; + m->p->optReq.opt.maxlen = m->p->optBlock.h.len; + if (m->p->optReq.opt.maxlen < 4) + m->p->optReq.opt.maxlen = 4; - err = OTOptionManagement(m->p->ep, &m->p->optReq, NULL); - if (err) LogMsg("OTOptionManagement failed %d", err); - } + err = OTOptionManagement(m->p->ep, &m->p->optReq, NULL); + if (err) LogMsg("OTOptionManagement failed %d", err); +} mDNSlocal void mDNSinitComplete(mDNS *const m, mStatus result) - { - m->mDNSPlatformStatus = result; - mDNSCoreInitComplete(m, mStatus_NoError); - } +{ + m->mDNSPlatformStatus = result; + mDNSCoreInitComplete(m, mStatus_NoError); +} mDNSlocal pascal void mDNSNotifier(void *contextPtr, OTEventCode code, OTResult result, void *cookie) - { - mDNS *const m = (mDNS *const)contextPtr; - if (!m) debugf("mDNSNotifier FATAL ERROR! No context"); - switch (code) - { - case T_OPENCOMPLETE: - { - OSStatus err; - InetInterfaceInfo interfaceinfo; - if (result) { LogMsg("T_OPENCOMPLETE failed %d", result); mDNSinitComplete(m, result); return; } - //debugf("T_OPENCOMPLETE"); - m->p->ep = (EndpointRef)cookie; - //debugf("OTInetGetInterfaceInfo"); - // (In future may want to loop over all interfaces instead of just using kDefaultInetInterface) - err = OTInetGetInterfaceInfo(&interfaceinfo, kDefaultInetInterface); - if (err) { 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.mask.type = mDNSAddrType_IPv4; - m->p->interface.mask.ip.v4.NotAnInteger = interfaceinfo.fNetmask; - m->p->interface.ifname[0] = 0; - m->p->interface.Advertise = m->AdvertiseLocalAddresses; - m->p->interface.McastTxRx = mDNStrue; - } - - case T_OPTMGMTCOMPLETE: - case T_BINDCOMPLETE: - // IP_RCVDSTADDR is known to fail on OS X Carbon, so we don't want to abort for that error - // (see comment above at the definition of kRcvDestAddrOption) - #if OTCARBONAPPLICATION - if (result && m->p->mOTstate == mOT_RcvDestAddr) - LogMsg("Carbon IP_RCVDSTADDR option failed %d; continuing anyway", result); - else - #endif - if (result) { LogMsg("T_OPTMGMTCOMPLETE/T_BINDCOMPLETE %d failed %d", m->p->mOTstate, result); mDNSinitComplete(m, result); return; } - //LogMsg("T_OPTMGMTCOMPLETE/T_BINDCOMPLETE %d", m->p->mOTstate); - switch (++m->p->mOTstate) - { - case mOT_ReusePort: m->p->optBlock.b = kReusePortOption; mDNSOptionManagement(m); break; - case mOT_RcvDestAddr: m->p->optBlock.i = kRcvDestAddrOption; mDNSOptionManagement(m); break; - case mOT_SetUTTL: m->p->optBlock.i = kSetUnicastTTLOption; mDNSOptionManagement(m); break; - case mOT_SetMTTL: m->p->optBlock.i = kSetMulticastTTLOption; mDNSOptionManagement(m); break; - case mOT_LLScope: m->p->optBlock.m = kAddLinkMulticastOption; mDNSOptionManagement(m); break; +{ + mDNS *const m = (mDNS *const)contextPtr; + if (!m) debugf("mDNSNotifier FATAL ERROR! No context"); + switch (code) + { + case T_OPENCOMPLETE: + { + OSStatus err; + InetInterfaceInfo interfaceinfo; + if (result) { LogMsg("T_OPENCOMPLETE failed %d", result); mDNSinitComplete(m, result); return; } + //debugf("T_OPENCOMPLETE"); + m->p->ep = (EndpointRef)cookie; + //debugf("OTInetGetInterfaceInfo"); + // (In future may want to loop over all interfaces instead of just using kDefaultInetInterface) + err = OTInetGetInterfaceInfo(&interfaceinfo, kDefaultInetInterface); + if (err) { 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.mask.type = mDNSAddrType_IPv4; + m->p->interface.mask.ip.v4.NotAnInteger = interfaceinfo.fNetmask; + m->p->interface.ifname[0] = 0; + m->p->interface.Advertise = m->AdvertiseLocalAddresses; + m->p->interface.McastTxRx = mDNStrue; + } + + case T_OPTMGMTCOMPLETE: + case T_BINDCOMPLETE: + // IP_RCVDSTADDR is known to fail on OS X Carbon, so we don't want to abort for that error + // (see comment above at the definition of kRcvDestAddrOption) + #if OTCARBONAPPLICATION + if (result && m->p->mOTstate == mOT_RcvDestAddr) + LogMsg("Carbon IP_RCVDSTADDR option failed %d; continuing anyway", result); + else + #endif + if (result) { LogMsg("T_OPTMGMTCOMPLETE/T_BINDCOMPLETE %d failed %d", m->p->mOTstate, result); mDNSinitComplete(m, result); return; } + //LogMsg("T_OPTMGMTCOMPLETE/T_BINDCOMPLETE %d", m->p->mOTstate); + switch (++m->p->mOTstate) + { + case mOT_ReusePort: m->p->optBlock.b = kReusePortOption; mDNSOptionManagement(m); break; + case mOT_RcvDestAddr: m->p->optBlock.i = kRcvDestAddrOption; mDNSOptionManagement(m); break; + case mOT_SetUTTL: m->p->optBlock.i = kSetUnicastTTLOption; mDNSOptionManagement(m); break; + case mOT_SetMTTL: m->p->optBlock.i = kSetMulticastTTLOption; mDNSOptionManagement(m); break; + case mOT_LLScope: m->p->optBlock.m = kAddLinkMulticastOption; mDNSOptionManagement(m); break; // case mOT_AdminScope: m->p->optBlock.m = kAddAdminMulticastOption; mDNSOptionManagement(m); break; - case mOT_Bind: OTBind(m->p->ep, (TBind*)&mDNSbindReq, NULL); break; - case mOT_Ready: mDNSinitComplete(m, mStatus_NoError); - // Can't do mDNS_RegisterInterface until *after* mDNSinitComplete has set m->mDNSPlatformStatus to mStatus_NoError - mDNS_RegisterInterface(m, &m->p->interface, mDNSfalse); - break; - default: LogMsg("Unexpected m->p->mOTstate %d", m->p->mOTstate-1); - } - break; - - case T_DATA: - //debugf("T_DATA"); - while (readpacket(m) == kOTNoError) continue; // Read packets until we run out - break; - - case kOTProviderWillClose: LogMsg("kOTProviderWillClose"); break; - case kOTProviderIsClosed: // Machine is going to sleep, shutting down, or reconfiguring IP - LogMsg("kOTProviderIsClosed"); - if (m->p->mOTstate == mOT_Ready) - { - m->p->mOTstate = mOT_Closed; - mDNS_DeregisterInterface(m, &m->p->interface, mDNSfalse); - } - if (m->p->ep) { OTCloseProvider(m->p->ep); m->p->ep = NULL; } - break; // Do we need to do anything? - - default: debugf("mDNSNotifier: Unexpected OTEventCode %X", code); - break; - } - } + case mOT_Bind: OTBind(m->p->ep, (TBind*)&mDNSbindReq, NULL); break; + case mOT_Ready: mDNSinitComplete(m, mStatus_NoError); + // Can't do mDNS_RegisterInterface until *after* mDNSinitComplete has set m->mDNSPlatformStatus to mStatus_NoError + mDNS_RegisterInterface(m, &m->p->interface, mDNSfalse); + break; + default: LogMsg("Unexpected m->p->mOTstate %d", m->p->mOTstate-1); + } + break; + + case T_DATA: + //debugf("T_DATA"); + while (readpacket(m) == kOTNoError) continue; // Read packets until we run out + break; + + case kOTProviderWillClose: LogMsg("kOTProviderWillClose"); break; + case kOTProviderIsClosed: // Machine is going to sleep, shutting down, or reconfiguring IP + LogMsg("kOTProviderIsClosed"); + if (m->p->mOTstate == mOT_Ready) + { + m->p->mOTstate = mOT_Closed; + mDNS_DeregisterInterface(m, &m->p->interface, mDNSfalse); + } + if (m->p->ep) { OTCloseProvider(m->p->ep); m->p->ep = NULL; } + break; // Do we need to do anything? + + default: debugf("mDNSNotifier: Unexpected OTEventCode %X", code); + break; + } +} #if MDNS_ONLYSYSTEMTASK -static Boolean ONLYSYSTEMTASKevent; +static Boolean ONLYSYSTEMTASKevent; static void *ONLYSYSTEMTASKcontextPtr; static OTEventCode ONLYSYSTEMTASKcode; -static OTResult ONLYSYSTEMTASKresult; +static OTResult ONLYSYSTEMTASKresult; static void *ONLYSYSTEMTASKcookie; mDNSlocal pascal void CallmDNSNotifier(void *contextPtr, OTEventCode code, OTResult result, void *cookie) - { - ONLYSYSTEMTASKcontextPtr = contextPtr; - ONLYSYSTEMTASKcode = code; - ONLYSYSTEMTASKresult = result; - ONLYSYSTEMTASKcookie = cookie; - } +{ + ONLYSYSTEMTASKcontextPtr = contextPtr; + ONLYSYSTEMTASKcode = code; + ONLYSYSTEMTASKresult = result; + ONLYSYSTEMTASKcookie = cookie; +} #else mDNSlocal pascal void CallmDNSNotifier(void *contextPtr, OTEventCode code, OTResult result, void *cookie) - { - mDNS *const m = (mDNS *const)contextPtr; - if (!m) debugf("mDNSNotifier FATAL ERROR! No context"); - if (m->p->nesting) LogMsg("CallmDNSNotifier ERROR! OTEnterNotifier is supposed to suppress notifier callbacks"); - mDNSNotifier(contextPtr, code, result, cookie); - } +{ + mDNS *const m = (mDNS *const)contextPtr; + if (!m) debugf("mDNSNotifier FATAL ERROR! No context"); + if (m->p->nesting) LogMsg("CallmDNSNotifier ERROR! OTEnterNotifier is supposed to suppress notifier callbacks"); + mDNSNotifier(contextPtr, code, result, cookie); +} #endif static OTNotifyUPP CallmDNSNotifierUPP; mDNSlocal OSStatus mDNSOpenEndpoint(const mDNS *const m) - { - OSStatus err; - // m->optReq is pre-set to point to the shared m->optBlock - // m->optBlock is filled in by each OTOptionManagement call - m->p->optReq.opt.maxlen = sizeof(m->p->optBlock); - m->p->optReq.opt.len = sizeof(m->p->optBlock); - m->p->optReq.opt.buf = (UInt8*)&m->p->optBlock; - m->p->optReq.flags = T_NEGOTIATE; - - // Open an endpoint and start answering queries - //printf("Opening endpoint now...\n"); - m->p->ep = NULL; - m->p->mOTstate = mOT_Start; - err = OTAsyncOpenEndpoint(OTCreateConfiguration(kUDPName), 0, NULL, CallmDNSNotifierUPP, (void*)m); - if (err) { LogMsg("ERROR: OTAsyncOpenEndpoint(UDP) failed with error <%d>", err); return(err); } - return(kOTNoError); - } +{ + OSStatus err; + // m->optReq is pre-set to point to the shared m->optBlock + // m->optBlock is filled in by each OTOptionManagement call + m->p->optReq.opt.maxlen = sizeof(m->p->optBlock); + m->p->optReq.opt.len = sizeof(m->p->optBlock); + m->p->optReq.opt.buf = (UInt8*)&m->p->optBlock; + m->p->optReq.flags = T_NEGOTIATE; + + // Open an endpoint and start answering queries + //printf("Opening endpoint now...\n"); + m->p->ep = NULL; + m->p->mOTstate = mOT_Start; + err = OTAsyncOpenEndpoint(OTCreateConfiguration(kUDPName), 0, NULL, CallmDNSNotifierUPP, (void*)m); + if (err) { LogMsg("ERROR: OTAsyncOpenEndpoint(UDP) failed with error <%d>", err); return(err); } + return(kOTNoError); +} // Define these here because they're not in older versions of OpenTransport.h enum - { - xOTStackIsLoading = 0x27000001, /* Sent before Open Transport attempts to load the TCP/IP protocol stack.*/ - xOTStackWasLoaded = 0x27000002, /* Sent after the TCP/IP stack has been successfully loaded.*/ - xOTStackIsUnloading = 0x27000003 /* Sent before Open Transport unloads the TCP/IP stack.*/ - }; +{ + xOTStackIsLoading = 0x27000001, /* Sent before Open Transport attempts to load the TCP/IP protocol stack.*/ + xOTStackWasLoaded = 0x27000002, /* Sent after the TCP/IP stack has been successfully loaded.*/ + xOTStackIsUnloading = 0x27000003 /* Sent before Open Transport unloads the TCP/IP stack.*/ +}; static mDNS *ClientNotifierContext; mDNSlocal pascal void ClientNotifier(void *contextPtr, OTEventCode code, OTResult result, void *cookie) - { - mDNS *const m = ClientNotifierContext; - - #pragma unused(contextPtr) // Usually zero (except one in the 'xOTStackIsLoading' case) - #pragma unused(cookie) // Usually 'ipv4' (except for kOTPortNetworkChange) - #pragma unused(result) // Usually zero - - switch (code) - { - case xOTStackIsLoading: break; - case xOTStackWasLoaded: if (m->p->mOTstate == mOT_Closed) - { - LogMsg("kOTStackWasLoaded: Re-opening endpoint"); - if (m->p->ep) - LogMsg("kOTStackWasLoaded: ERROR: m->p->ep already set"); - m->mDNSPlatformStatus = mStatus_Waiting; - m->p->mOTstate = mOT_Reset; - #if !MDNS_ONLYSYSTEMTASK - mDNSOpenEndpoint(m); - #endif - } - else - LogMsg("kOTStackWasLoaded (no action)"); - break; - case xOTStackIsUnloading: break; - case kOTPortNetworkChange: break; - default: debugf("ClientNotifier unknown code %X, %X, %d", contextPtr, code, result); break; - } - } +{ + mDNS *const m = ClientNotifierContext; + + #pragma unused(contextPtr) // Usually zero (except one in the 'xOTStackIsLoading' case) + #pragma unused(cookie) // Usually 'ipv4' (except for kOTPortNetworkChange) + #pragma unused(result) // Usually zero + + switch (code) + { + case xOTStackIsLoading: break; + case xOTStackWasLoaded: if (m->p->mOTstate == mOT_Closed) + { + LogMsg("kOTStackWasLoaded: Re-opening endpoint"); + if (m->p->ep) + LogMsg("kOTStackWasLoaded: ERROR: m->p->ep already set"); + m->mDNSPlatformStatus = mStatus_Waiting; + m->p->mOTstate = mOT_Reset; + #if !MDNS_ONLYSYSTEMTASK + mDNSOpenEndpoint(m); + #endif + } + else + LogMsg("kOTStackWasLoaded (no action)"); + break; + case xOTStackIsUnloading: break; + case kOTPortNetworkChange: break; + default: debugf("ClientNotifier unknown code %X, %X, %d", contextPtr, code, result); break; + } +} #if TARGET_API_MAC_CARBON mDNSlocal void GetUserSpecifiedComputerName(domainlabel *const namelabel) - { - CFStringRef cfs = CSCopyMachineName(); - CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); - CFRelease(cfs); - } +{ + CFStringRef cfs = CSCopyMachineName(); + CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); + CFRelease(cfs); +} #else mDNSlocal OSStatus ConvertStringHandleToUTF8(const StringHandle machineName, UInt8 *const utf8, ByteCount maxlen) - { - OSStatus status; - TextEncoding utf8TextEncoding, SystemTextEncoding; - UnicodeMapping theMapping; - TextToUnicodeInfo textToUnicodeInfo; - ByteCount unicodelen = 0; - - if (maxlen > 255) maxlen = 255; // Can't put more than 255 in a Pascal String - - utf8TextEncoding = CreateTextEncoding(kTextEncodingUnicodeDefault, kTextEncodingDefaultVariant, kUnicodeUTF8Format); - UpgradeScriptInfoToTextEncoding(smSystemScript, kTextLanguageDontCare, kTextRegionDontCare, NULL, &SystemTextEncoding); - theMapping.unicodeEncoding = utf8TextEncoding; - theMapping.otherEncoding = SystemTextEncoding; - theMapping.mappingVersion = kUnicodeUseLatestMapping; - status = CreateTextToUnicodeInfo(&theMapping, &textToUnicodeInfo); - if (status == noErr) - { - status = ConvertFromPStringToUnicode(textToUnicodeInfo, *machineName, maxlen, &unicodelen, (UniCharArrayPtr)&(utf8[1])); - DisposeTextToUnicodeInfo(&textToUnicodeInfo); - } - utf8[0] = (UInt8)unicodelen; - return(status); - } +{ + OSStatus status; + TextEncoding utf8TextEncoding, SystemTextEncoding; + UnicodeMapping theMapping; + TextToUnicodeInfo textToUnicodeInfo; + ByteCount unicodelen = 0; + + if (maxlen > 255) maxlen = 255; // Can't put more than 255 in a Pascal String + + utf8TextEncoding = CreateTextEncoding(kTextEncodingUnicodeDefault, kTextEncodingDefaultVariant, kUnicodeUTF8Format); + UpgradeScriptInfoToTextEncoding(smSystemScript, kTextLanguageDontCare, kTextRegionDontCare, NULL, &SystemTextEncoding); + theMapping.unicodeEncoding = utf8TextEncoding; + theMapping.otherEncoding = SystemTextEncoding; + theMapping.mappingVersion = kUnicodeUseLatestMapping; + status = CreateTextToUnicodeInfo(&theMapping, &textToUnicodeInfo); + if (status == noErr) + { + status = ConvertFromPStringToUnicode(textToUnicodeInfo, *machineName, maxlen, &unicodelen, (UniCharArrayPtr)&(utf8[1])); + DisposeTextToUnicodeInfo(&textToUnicodeInfo); + } + utf8[0] = (UInt8)unicodelen; + return(status); +} mDNSlocal void GetUserSpecifiedComputerName(domainlabel *const namelabel) - { - StringHandle machineName = GetString(-16413); // Get machine name set in file sharing - if (machineName) - { - char machineNameState = HGetState((Handle)machineName); - HLock((Handle)machineName); - ConvertStringHandleToUTF8(machineName, namelabel->c, MAX_DOMAIN_LABEL); - HSetState((Handle)machineName, machineNameState); - } - } +{ + StringHandle machineName = GetString(-16413); // Get machine name set in file sharing + if (machineName) + { + char machineNameState = HGetState((Handle)machineName); + HLock((Handle)machineName); + ConvertStringHandleToUTF8(machineName, namelabel->c, MAX_DOMAIN_LABEL); + HSetState((Handle)machineName, machineNameState); + } +} #endif static pascal void mDNSTimerTask(void *arg) - { +{ #if MDNS_ONLYSYSTEMTASK #pragma unused(arg) - ONLYSYSTEMTASKevent = true; + ONLYSYSTEMTASKevent = true; #else - mDNS *const m = (mDNS *const)arg; - if (!m->p->ep) LogMsg("mDNSTimerTask NO endpoint"); - if (m->mDNS_busy) LogMsg("mDNS_busy"); - if (m->p->nesting) LogMsg("mDNSTimerTask ERROR! OTEnterNotifier is supposed to suppress timer callbacks too"); - - // If our timer fires at a time when we have no endpoint, ignore it -- - // once we reopen our endpoint and get our T_BINDCOMPLETE message we'll call - // mDNS_RegisterInterface(), which does a lock/unlock, which retriggers the timer. - // Likewise, if m->mDNS_busy or m->p->nesting, we'll catch this on the unlock - if (m->p->ep && m->mDNS_busy == 0 && m->p->nesting == 0) mDNS_Execute(m); + mDNS *const m = (mDNS *const)arg; + if (!m->p->ep) LogMsg("mDNSTimerTask NO endpoint"); + if (m->mDNS_busy) LogMsg("mDNS_busy"); + if (m->p->nesting) LogMsg("mDNSTimerTask ERROR! OTEnterNotifier is supposed to suppress timer callbacks too"); + + // If our timer fires at a time when we have no endpoint, ignore it -- + // once we reopen our endpoint and get our T_BINDCOMPLETE message we'll call + // mDNS_RegisterInterface(), which does a lock/unlock, which retriggers the timer. + // Likewise, if m->mDNS_busy or m->p->nesting, we'll catch this on the unlock + if (m->p->ep && m->mDNS_busy == 0 && m->p->nesting == 0) mDNS_Execute(m); #endif - } +} #if TEST_SLEEP long sleep, wake, mode; #endif -mDNSexport mStatus mDNSPlatformInit(mDNS *const m) - { - OSStatus err = InitOpenTransport(); +mDNSexport mStatus mDNSPlatformInit (mDNS *const m) +{ + OSStatus err = InitOpenTransport(); + + ClientNotifierContext = m; + // Note: OTRegisterAsClient returns kOTNotSupportedErr when running as Carbon code on OS X + // -- but that's okay, we don't need a ClientNotifier when running as Carbon code on OS X + OTRegisterAsClient(NULL, NewOTNotifyUPP(ClientNotifier)); - ClientNotifierContext = m; - // Note: OTRegisterAsClient returns kOTNotSupportedErr when running as Carbon code on OS X - // -- but that's okay, we don't need a ClientNotifier when running as Carbon code on OS X - OTRegisterAsClient(NULL, NewOTNotifyUPP(ClientNotifier)); - - m->p->OTTimerTask = OTCreateTimerTask(NewOTProcessUPP(mDNSTimerTask), m); - m->p->nesting = 0; + m->p->OTTimerTask = OTCreateTimerTask(NewOTProcessUPP(mDNSTimerTask), m); + m->p->nesting = 0; #if TEST_SLEEP - sleep = TickCount() + 600; - wake = TickCount() + 1200; - mode = 0; + sleep = TickCount() + 600; + wake = TickCount() + 1200; + mode = 0; #endif - // Set up the nice label - m->nicelabel.c[0] = 0; - GetUserSpecifiedComputerName(&m->nicelabel); + // Set up the nice label + m->nicelabel.c[0] = 0; + GetUserSpecifiedComputerName(&m->nicelabel); // m->nicelabel = *(domainlabel*)"\pStu"; // For conflict testing - if (m->nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->nicelabel, "Macintosh"); - - // Set up the RFC 1034-compliant label - m->hostlabel.c[0] = 0; - ConvertUTF8PstringToRFC1034HostLabel(m->nicelabel.c, &m->hostlabel); - if (m->hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->hostlabel, "Macintosh"); - - mDNS_SetFQDN(m); - - // When it's finished mDNSOpenEndpoint asynchronously calls mDNSinitComplete() and then mDNS_RegisterInterface() - CallmDNSNotifierUPP = NewOTNotifyUPP(CallmDNSNotifier); - err = mDNSOpenEndpoint(m); - if (err) - { - LogMsg("mDNSOpenEndpoint failed %d", err); - if (m->p->OTTimerTask) OTDestroyTimerTask(m->p->OTTimerTask); - OTUnregisterAsClient(); - CloseOpenTransport(); - } - return(err); - } + if (m->nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->nicelabel, "Macintosh"); + + // Set up the RFC 1034-compliant label + m->hostlabel.c[0] = 0; + ConvertUTF8PstringToRFC1034HostLabel(m->nicelabel.c, &m->hostlabel); + if (m->hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->hostlabel, "Macintosh"); + + mDNS_SetFQDN(m); + + // When it's finished mDNSOpenEndpoint asynchronously calls mDNSinitComplete() and then mDNS_RegisterInterface() + CallmDNSNotifierUPP = NewOTNotifyUPP(CallmDNSNotifier); + err = mDNSOpenEndpoint(m); + if (err) + { + LogMsg("mDNSOpenEndpoint failed %d", err); + if (m->p->OTTimerTask) OTDestroyTimerTask(m->p->OTTimerTask); + OTUnregisterAsClient(); + CloseOpenTransport(); + } + return(err); +} extern void mDNSPlatformClose (mDNS *const m) - { - if (m->p->mOTstate == mOT_Ready) - { - m->p->mOTstate = mOT_Closed; - mDNS_DeregisterInterface(m, &m->p->interface, mDNSfalse); - } - if (m->p->ep) { OTCloseProvider (m->p->ep); m->p->ep = NULL; } - if (m->p->OTTimerTask) { OTDestroyTimerTask(m->p->OTTimerTask); m->p->OTTimerTask = 0; } - - OTUnregisterAsClient(); - CloseOpenTransport(); - } +{ + if (m->p->mOTstate == mOT_Ready) + { + m->p->mOTstate = mOT_Closed; + mDNS_DeregisterInterface(m, &m->p->interface, mDNSfalse); + } + if (m->p->ep) { OTCloseProvider (m->p->ep); m->p->ep = NULL; } + if (m->p->OTTimerTask) { OTDestroyTimerTask(m->p->OTTimerTask); m->p->OTTimerTask = 0; } + + OTUnregisterAsClient(); + CloseOpenTransport(); +} #if MDNS_ONLYSYSTEMTASK extern void mDNSPlatformIdle(mDNS *const m); mDNSexport void mDNSPlatformIdle(mDNS *const m) - { - while (ONLYSYSTEMTASKcontextPtr) - { - void *contextPtr = ONLYSYSTEMTASKcontextPtr; - ONLYSYSTEMTASKcontextPtr = NULL; - mDNSNotifier(contextPtr, ONLYSYSTEMTASKcode, ONLYSYSTEMTASKresult, ONLYSYSTEMTASKcookie); - } - if (ONLYSYSTEMTASKevent) - { - ONLYSYSTEMTASKevent = false; - mDNS_Execute(m); - } - - if (m->p->mOTstate == mOT_Reset) - { - printf("\n"); - printf("******************************************************************************\n"); - printf("\n"); - printf("Reopening endpoint\n"); - mDNSOpenEndpoint(m); - m->ResourceRecords = NULL; - } +{ + while (ONLYSYSTEMTASKcontextPtr) + { + void *contextPtr = ONLYSYSTEMTASKcontextPtr; + ONLYSYSTEMTASKcontextPtr = NULL; + mDNSNotifier(contextPtr, ONLYSYSTEMTASKcode, ONLYSYSTEMTASKresult, ONLYSYSTEMTASKcookie); + } + if (ONLYSYSTEMTASKevent) + { + ONLYSYSTEMTASKevent = false; + mDNS_Execute(m); + } + + if (m->p->mOTstate == mOT_Reset) + { + printf("\n"); + printf("******************************************************************************\n"); + printf("\n"); + printf("Reopening endpoint\n"); + mDNSOpenEndpoint(m); + m->ResourceRecords = NULL; + } #if TEST_SLEEP - switch (mode) - { - case 0: if ((long)TickCount() - sleep >= 0) { mDNSCoreMachineSleep(m, 1); mode++; } - break; - case 1: if ((long)TickCount() - wake >= 0) { mDNSCoreMachineSleep(m, 0); mode++; } - break; - } + switch (mode) + { + case 0: if ((long)TickCount() - sleep >= 0) { mDNSCoreMachineSleep(m, 1); mode++; } + break; + case 1: if ((long)TickCount() - wake >= 0) { mDNSCoreMachineSleep(m, 0); mode++; } + break; + } #endif - } +} #endif mDNSexport void mDNSPlatformLock(const mDNS *const m) - { - if (!m) { DebugStr("\pmDNSPlatformLock m NULL!"); return; } - if (!m->p) { DebugStr("\pmDNSPlatformLock m->p NULL!"); return; } +{ + if (!m) { DebugStr("\pmDNSPlatformLock m NULL!"); return; } + if (!m->p) { DebugStr("\pmDNSPlatformLock m->p NULL!"); return; } - // If we try to call OTEnterNotifier and fail because we're already running at - // Notifier context, then make sure we don't do the matching OTLeaveNotifier() on exit. - // If we haven't even opened our endpoint yet, then just increment m->p->nesting for the same reason - if (m->p->mOTstate == mOT_Ready && !m->p->ep) DebugStr("\pmDNSPlatformLock: m->p->mOTstate == mOT_Ready && !m->p->ep"); - if (!m->p->ep || m->p->nesting || OTEnterNotifier(m->p->ep) == false) m->p->nesting++; - } + // If we try to call OTEnterNotifier and fail because we're already running at + // Notifier context, then make sure we don't do the matching OTLeaveNotifier() on exit. + // If we haven't even opened our endpoint yet, then just increment m->p->nesting for the same reason + if (m->p->mOTstate == mOT_Ready && !m->p->ep) DebugStr("\pmDNSPlatformLock: m->p->mOTstate == mOT_Ready && !m->p->ep"); + if (!m->p->ep || m->p->nesting || OTEnterNotifier(m->p->ep) == false) m->p->nesting++; +} mDNSlocal void ScheduleNextTimerCallback(const mDNS *const m) - { - if (m->mDNSPlatformStatus == mStatus_NoError) - { - SInt32 interval = m->NextScheduledEvent - mDNS_TimeNow_NoLock(m); - if (interval < 1) interval = 1; - else if (interval > 0x70000000 / 1000) interval = 0x70000000 / mDNSPlatformOneSecond; - else interval = (interval * 1000 + mDNSPlatformOneSecond-1)/ mDNSPlatformOneSecond; - OTScheduleTimerTask(m->p->OTTimerTask, (OTTimeout)interval); - } - } +{ + if (m->mDNSPlatformStatus == mStatus_NoError) + { + SInt32 interval = m->NextScheduledEvent - mDNS_TimeNow_NoLock(m); + if (interval < 1) interval = 1; + else if (interval > 0x70000000 / 1000) interval = 0x70000000 / mDNSPlatformOneSecond; + else interval = (interval * 1000 + mDNSPlatformOneSecond-1)/ mDNSPlatformOneSecond; + OTScheduleTimerTask(m->p->OTTimerTask, (OTTimeout)interval); + } +} mDNSexport void mDNSPlatformUnlock(const mDNS *const m) - { - if (!m) { DebugStr("\pmDNSPlatformUnlock m NULL!"); return; } - if (!m->p) { DebugStr("\pmDNSPlatformUnlock m->p NULL!"); return; } +{ + if (!m) { DebugStr("\pmDNSPlatformUnlock m NULL!"); return; } + if (!m->p) { DebugStr("\pmDNSPlatformUnlock m->p NULL!"); return; } - if (m->p->ep && m->mDNS_busy == 0) ScheduleNextTimerCallback(m); + if (m->p->ep && m->mDNS_busy == 0) ScheduleNextTimerCallback(m); - if (m->p->nesting) m->p->nesting--; - else OTLeaveNotifier(m->p->ep); - } + if (m->p->nesting) m->p->nesting--; + else OTLeaveNotifier(m->p->ep); +} mDNSexport void mDNSPlatformStrCopy( void *dst, const void *src) { OTStrCopy((char*)dst, (char*)src); } mDNSexport UInt32 mDNSPlatformStrLen ( const void *src) { return(OTStrLength((char*)src)); } @@ -670,18 +670,18 @@ mDNSexport void mDNSPlatformMemFree(void *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) - { - // Classic Mac OS since Midnight, 1st Jan 1904 - // Standard Unix counts from 1970 - // This value adjusts for the 66 years and 17 leap-days difference - mDNSu32 SecsSince1904; - MachineLocation ThisLocation; - #define TIME_ADJUST (((1970 - 1904) * 365 + 17) * 24 * 60 * 60) - #define ThisLocationGMTdelta ((ThisLocation.u.gmtDelta << 8) >> 8) - GetDateTime(&SecsSince1904); - ReadLocation(&ThisLocation); - return((mDNSs32)(SecsSince1904 - ThisLocationGMTdelta - TIME_ADJUST)); - } +mDNSexport SInt32 mDNSPlatformOneSecond = 60; + +mDNSexport mDNSs32 mDNSPlatformUTC(void) +{ + // Classic Mac OS since Midnight, 1st Jan 1904 + // Standard Unix counts from 1970 + // This value adjusts for the 66 years and 17 leap-days difference + mDNSu32 SecsSince1904; + MachineLocation ThisLocation; + #define TIME_ADJUST (((1970 - 1904) * 365 + 17) * 24 * 60 * 60) + #define ThisLocationGMTdelta ((ThisLocation.u.gmtDelta << 8) >> 8) + GetDateTime(&SecsSince1904); + ReadLocation(&ThisLocation); + return((mDNSs32)(SecsSince1904 - ThisLocationGMTdelta - TIME_ADJUST)); +} diff --git a/mDNSMacOS9/mDNSMacOS9.h b/mDNSMacOS9/mDNSMacOS9.h index eabd7e7..496c07f 100755 --- a/mDNSMacOS9/mDNSMacOS9.h +++ b/mDNSMacOS9/mDNSMacOS9.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -24,19 +24,19 @@ #include typedef enum - { - mOT_Closed = 0, // We got kOTProviderIsClosed message - mOT_Reset, // We got xOTStackWasLoaded message - mOT_Start, // We've called OTAsyncOpenEndpoint - mOT_ReusePort, // Have just done kReusePortOption - mOT_RcvDestAddr, // Have just done kRcvDestAddrOption - mOT_SetUTTL, // Have just done kSetUnicastTTLOption - mOT_SetMTTL, // Have just done kSetMulticastTTLOption - mOT_LLScope, // Have just done kAddLinkMulticastOption +{ + mOT_Closed = 0, // We got kOTProviderIsClosed message + mOT_Reset, // We got xOTStackWasLoaded message + mOT_Start, // We've called OTAsyncOpenEndpoint + mOT_ReusePort, // Have just done kReusePortOption + mOT_RcvDestAddr, // Have just done kRcvDestAddrOption + mOT_SetUTTL, // Have just done kSetUnicastTTLOption + mOT_SetMTTL, // Have just done kSetMulticastTTLOption + mOT_LLScope, // Have just done kAddLinkMulticastOption // mOT_AdminScope, // Have just done kAddAdminMulticastOption - mOT_Bind, // We've just called OTBind - mOT_Ready // Got T_BINDCOMPLETE; Interface is registered and active - } mOT_State; + mOT_Bind, // We've just called OTBind + mOT_Ready // Got T_BINDCOMPLETE; Interface is registered and active +} mOT_State; typedef struct { TOptionHeader h; mDNSv4Addr multicastGroupAddress; mDNSv4Addr InterfaceAddress; } TIPAddMulticastOption; typedef struct { TOptionHeader h; UInt8 val; } TSetByteOption; @@ -47,12 +47,12 @@ typedef struct { TOptionHeader h; UInt32 flag; } TSetBooleanOption; typedef union { TOptionHeader h; TIPAddMulticastOption m; TSetByteOption i; TSetBooleanOption b; } TOptionBlock; struct mDNS_PlatformSupport_struct - { - EndpointRef ep; - UInt32 mOTstate; // mOT_State enum - TOptionBlock optBlock; - TOptMgmt optReq; - long OTTimerTask; - UInt32 nesting; - NetworkInterfaceInfo interface; - }; +{ + EndpointRef ep; + UInt32 mOTstate; // mOT_State enum + TOptionBlock optBlock; + TOptMgmt optReq; + long OTTimerTask; + UInt32 nesting; + NetworkInterfaceInfo interface; +}; diff --git a/mDNSMacOS9/mDNSPrefix.h b/mDNSMacOS9/mDNSPrefix.h index fb8ac88..cf6cec8 100644 --- a/mDNSMacOS9/mDNSPrefix.h +++ b/mDNSMacOS9/mDNSPrefix.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/mDNSMacOSX/BonjourEvents.c b/mDNSMacOSX/BonjourEvents.c index b055a5f..f705a09 100644 --- a/mDNSMacOSX/BonjourEvents.c +++ b/mDNSMacOSX/BonjourEvents.c @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -28,78 +28,78 @@ #pragma mark - #pragma mark Types #pragma mark - -static const char* sPluginIdentifier = "com.apple.bonjour.events"; +static const char* sPluginIdentifier = "com.apple.bonjour.events"; // PLIST Keys -static const CFStringRef sServiceNameKey = CFSTR("ServiceName"); -static const CFStringRef sServiceTypeKey = CFSTR("ServiceType"); -static const CFStringRef sServiceDomainKey = CFSTR("ServiceDomain"); +static const CFStringRef sServiceNameKey = CFSTR("ServiceName"); +static const CFStringRef sServiceTypeKey = CFSTR("ServiceType"); +static const CFStringRef sServiceDomainKey = CFSTR("ServiceDomain"); -static const CFStringRef sOnServiceAddKey = CFSTR("OnServiceAdd"); -static const CFStringRef sOnServiceRemoveKey = CFSTR("OnServiceRemove"); +static const CFStringRef sOnServiceAddKey = CFSTR("OnServiceAdd"); +static const CFStringRef sOnServiceRemoveKey = CFSTR("OnServiceRemove"); -static const CFStringRef sLaunchdTokenKey = CFSTR("LaunchdToken"); -static const CFStringRef sLaunchdDictKey = CFSTR("LaunchdDict"); +static const CFStringRef sLaunchdTokenKey = CFSTR("LaunchdToken"); +static const CFStringRef sLaunchdDictKey = CFSTR("LaunchdDict"); /************************************************ - * Launch Event Dictionary (input from launchd) - * Passed To: ManageEventsCallback - *----------------------------------------------- - * Typing in this dictionary is not enforced - * above us. So this may not be true. Type check - * all input before using it. - *----------------------------------------------- - * sServiceNameKey - CFString (Optional) - * sServiceTypeKey - CFString - * sServiceDomainKey - CFString - * - * One or more of the following. - *----------------------------------- - * sOnServiceAddKey - CFBoolean - * sOnServiceRemoveKey - CFBoolean - * sWhileServiceExistsKey - CFBoolean - ************************************************/ +* Launch Event Dictionary (input from launchd) +* Passed To: ManageEventsCallback +*----------------------------------------------- +* Typing in this dictionary is not enforced +* above us. So this may not be true. Type check +* all input before using it. +*----------------------------------------------- +* sServiceNameKey - CFString (Optional) +* sServiceTypeKey - CFString +* sServiceDomainKey - CFString +* +* One or more of the following. +*----------------------------------- +* sOnServiceAddKey - CFBoolean +* sOnServiceRemoveKey - CFBoolean +* sWhileServiceExistsKey - CFBoolean +************************************************/ /************************************************ - * Browser Dictionary - *----------------------------------------------- - * sServiceDomainKey - CFString - * sServiceTypeKey - CFString - ************************************************/ +* Browser Dictionary +*----------------------------------------------- +* sServiceDomainKey - CFString +* sServiceTypeKey - CFString +************************************************/ /************************************************ - * Event Dictionary - *----------------------------------------------- - * sServiceNameKey - CFString (Optional) - * sLaunchdTokenKey - CFNumber - ************************************************/ +* Event Dictionary +*----------------------------------------------- +* sServiceNameKey - CFString (Optional) +* sLaunchdTokenKey - CFNumber +************************************************/ typedef struct { - UserEventAgentInterfaceStruct* _UserEventAgentInterface; - CFUUIDRef _factoryID; - UInt32 _refCount; - - void* _pluginContext; - - CFMutableDictionaryRef _tokenToBrowserMap; // Maps a token to a browser that can be used to scan the remaining dictionaries. - CFMutableDictionaryRef _browsers; // A Dictionary of Browser Dictionaries where the resposible browser is the key. - CFMutableDictionaryRef _onAddEvents; // A Dictionary of Event Dictionaries that describe events to trigger on a service appearing. - CFMutableDictionaryRef _onRemoveEvents; // A Dictionary of Event Dictionaries that describe events to trigger on a service disappearing. + UserEventAgentInterfaceStruct* _UserEventAgentInterface; + CFUUIDRef _factoryID; + UInt32 _refCount; + + void* _pluginContext; + + CFMutableDictionaryRef _tokenToBrowserMap; // Maps a token to a browser that can be used to scan the remaining dictionaries. + CFMutableDictionaryRef _browsers; // A Dictionary of Browser Dictionaries where the resposible browser is the key. + CFMutableDictionaryRef _onAddEvents; // A Dictionary of Event Dictionaries that describe events to trigger on a service appearing. + CFMutableDictionaryRef _onRemoveEvents; // A Dictionary of Event Dictionaries that describe events to trigger on a service disappearing. } BonjourUserEventsPlugin; typedef struct { - CFIndex refCount; - DNSServiceRef browserRef; + CFIndex refCount; + DNSServiceRef browserRef; } NetBrowserInfo; #pragma mark - #pragma mark Prototypes #pragma mark - // COM Stuff -static HRESULT QueryInterface(void *myInstance, REFIID iid, LPVOID *ppv); -static ULONG AddRef(void* instance); -static ULONG Release(void* instance); +static HRESULT QueryInterface(void *myInstance, REFIID iid, LPVOID *ppv); +static ULONG AddRef(void* instance); +static ULONG Release(void* instance); static BonjourUserEventsPlugin* Alloc(CFUUIDRef factoryID); static void Dealloc(BonjourUserEventsPlugin* plugin); @@ -109,15 +109,15 @@ void * UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID); // Plugin Management static void Install(void* instance); static void ManageEventsCallback( - UserEventAgentLaunchdAction action, - CFNumberRef token, - CFTypeRef eventMatchDict, - void * vContext); + UserEventAgentLaunchdAction action, + CFNumberRef token, + CFTypeRef eventMatchDict, + void * vContext); // Plugin Guts void AddEventToPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchdToken, CFDictionaryRef eventParameters); -void RemoveEventFromPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchToken); +void RemoveEventFromPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchToken); NetBrowserInfo* CreateBrowser(BonjourUserEventsPlugin* plugin, CFStringRef type, CFStringRef domain); NetBrowserInfo* BrowserForSDRef(BonjourUserEventsPlugin* plugin, DNSServiceRef sdRef); @@ -135,166 +135,166 @@ const char* CStringFromCFString(CFStringRef string); NetBrowserInfo* NetBrowserInfoCreate(CFStringRef serviceType, CFStringRef domain, void* context); const void* NetBrowserInfoRetain(CFAllocatorRef allocator, const void* info); void NetBrowserInfoRelease(CFAllocatorRef allocator, const void* info); -Boolean NetBrowserInfoEqual(const void *value1, const void *value2); -CFHashCode NetBrowserInfoHash(const void *value); -CFStringRef NetBrowserInfoCopyDescription(const void *value); - -static const CFDictionaryKeyCallBacks kNetBrowserInfoDictionaryKeyCallbacks = { - 0, - NetBrowserInfoRetain, - NetBrowserInfoRelease, - NetBrowserInfoCopyDescription, - NetBrowserInfoEqual, - NetBrowserInfoHash +Boolean NetBrowserInfoEqual(const void *value1, const void *value2); +CFHashCode NetBrowserInfoHash(const void *value); +CFStringRef NetBrowserInfoCopyDescription(const void *value); + +static const CFDictionaryKeyCallBacks kNetBrowserInfoDictionaryKeyCallbacks = { + 0, + NetBrowserInfoRetain, + NetBrowserInfoRelease, + NetBrowserInfoCopyDescription, + NetBrowserInfoEqual, + NetBrowserInfoHash }; -static const CFDictionaryValueCallBacks kNetBrowserInfoDictionaryValueCallbacks = { - 0, - NetBrowserInfoRetain, - NetBrowserInfoRelease, - NetBrowserInfoCopyDescription, - NetBrowserInfoEqual +static const CFDictionaryValueCallBacks kNetBrowserInfoDictionaryValueCallbacks = { + 0, + NetBrowserInfoRetain, + NetBrowserInfoRelease, + NetBrowserInfoCopyDescription, + NetBrowserInfoEqual }; // COM type definition goop. static UserEventAgentInterfaceStruct UserEventAgentInterfaceFtbl = { - NULL, // Required padding for COM - QueryInterface, // Query Interface - AddRef, // AddRef() - Release, // Release() - Install // Install -}; + NULL, // Required padding for COM + QueryInterface, // Query Interface + AddRef, // AddRef() + Release, // Release() + Install // Install +}; #pragma mark - #pragma mark COM Management #pragma mark - /***************************************************************************** - *****************************************************************************/ -static HRESULT QueryInterface(void *myInstance, REFIID iid, LPVOID *ppv) +*****************************************************************************/ +static HRESULT QueryInterface(void *myInstance, REFIID iid, LPVOID *ppv) { - CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes(NULL, iid); - - // Test the requested ID against the valid interfaces. - if(CFEqual(interfaceID, kUserEventAgentInterfaceID)) - { - ((BonjourUserEventsPlugin *) myInstance)->_UserEventAgentInterface->AddRef(myInstance); - *ppv = myInstance; - CFRelease(interfaceID); - return S_OK; - } - else if(CFEqual(interfaceID, IUnknownUUID)) - { - ((BonjourUserEventsPlugin *) myInstance)->_UserEventAgentInterface->AddRef(myInstance); - *ppv = myInstance; - CFRelease(interfaceID); - return S_OK; - } - else // Requested interface unknown, bail with error. - { - *ppv = NULL; - CFRelease(interfaceID); - return E_NOINTERFACE; - } + CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes(NULL, iid); + + // Test the requested ID against the valid interfaces. + if(CFEqual(interfaceID, kUserEventAgentInterfaceID)) + { + ((BonjourUserEventsPlugin *) myInstance)->_UserEventAgentInterface->AddRef(myInstance); + *ppv = myInstance; + CFRelease(interfaceID); + return S_OK; + } + else if(CFEqual(interfaceID, IUnknownUUID)) + { + ((BonjourUserEventsPlugin *) myInstance)->_UserEventAgentInterface->AddRef(myInstance); + *ppv = myInstance; + CFRelease(interfaceID); + return S_OK; + } + else // Requested interface unknown, bail with error. + { + *ppv = NULL; + CFRelease(interfaceID); + return E_NOINTERFACE; + } } /***************************************************************************** - *****************************************************************************/ +*****************************************************************************/ static ULONG AddRef(void* instance) { - BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)instance; - return ++plugin->_refCount; + BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)instance; + return ++plugin->_refCount; } /***************************************************************************** - *****************************************************************************/ +*****************************************************************************/ static ULONG Release(void* instance) { - BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)instance; - - if (plugin->_refCount != 0) - --plugin->_refCount; - - if (plugin->_refCount == 0) - { - Dealloc(instance); - return 0; - } - - return plugin->_refCount; -} + BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)instance; + + if (plugin->_refCount != 0) + --plugin->_refCount; + + if (plugin->_refCount == 0) + { + Dealloc(instance); + return 0; + } + + return plugin->_refCount; +} /***************************************************************************** - * Alloc - * - - * Functionas as both +[alloc] and -[init] for the plugin. Add any - * initalization of member variables here. - *****************************************************************************/ +* Alloc +* - +* Functionas as both +[alloc] and -[init] for the plugin. Add any +* initalization of member variables here. +*****************************************************************************/ static BonjourUserEventsPlugin* Alloc(CFUUIDRef factoryID) { - BonjourUserEventsPlugin* plugin = malloc(sizeof(BonjourUserEventsPlugin)); - - plugin->_UserEventAgentInterface = &UserEventAgentInterfaceFtbl; - plugin->_pluginContext = NULL; - - if (factoryID) - { - plugin->_factoryID = (CFUUIDRef)CFRetain(factoryID); - CFPlugInAddInstanceForFactory(factoryID); - } - - plugin->_refCount = 1; - plugin->_tokenToBrowserMap = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kNetBrowserInfoDictionaryValueCallbacks); - plugin->_browsers = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks); - plugin->_onAddEvents = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks); - plugin->_onRemoveEvents = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks); - - return plugin; + BonjourUserEventsPlugin* plugin = malloc(sizeof(BonjourUserEventsPlugin)); + + plugin->_UserEventAgentInterface = &UserEventAgentInterfaceFtbl; + plugin->_pluginContext = NULL; + + if (factoryID) + { + plugin->_factoryID = (CFUUIDRef)CFRetain(factoryID); + CFPlugInAddInstanceForFactory(factoryID); + } + + plugin->_refCount = 1; + plugin->_tokenToBrowserMap = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kNetBrowserInfoDictionaryValueCallbacks); + plugin->_browsers = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks); + plugin->_onAddEvents = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks); + plugin->_onRemoveEvents = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks); + + return plugin; } /***************************************************************************** - * Dealloc - * - - * Much like Obj-C dealloc this method is responsible for releasing any object - * this plugin is holding. Unlike ObjC, you call directly free() instead of - * [super dalloc]. - *****************************************************************************/ +* Dealloc +* - +* Much like Obj-C dealloc this method is responsible for releasing any object +* this plugin is holding. Unlike ObjC, you call directly free() instead of +* [super dalloc]. +*****************************************************************************/ static void Dealloc(BonjourUserEventsPlugin* plugin) { - CFUUIDRef factoryID = plugin->_factoryID; - - if (factoryID) - { - CFPlugInRemoveInstanceForFactory(factoryID); - CFRelease(factoryID); - } - - if (plugin->_tokenToBrowserMap) - CFRelease(plugin->_tokenToBrowserMap); - - if (plugin->_browsers) - CFRelease(plugin->_browsers); - - if (plugin->_onAddEvents) - CFRelease(plugin->_onAddEvents); - - if (plugin->_onRemoveEvents) - CFRelease(plugin->_onRemoveEvents); - - free(plugin); + CFUUIDRef factoryID = plugin->_factoryID; + + if (factoryID) + { + CFPlugInRemoveInstanceForFactory(factoryID); + CFRelease(factoryID); + } + + if (plugin->_tokenToBrowserMap) + CFRelease(plugin->_tokenToBrowserMap); + + if (plugin->_browsers) + CFRelease(plugin->_browsers); + + if (plugin->_onAddEvents) + CFRelease(plugin->_onAddEvents); + + if (plugin->_onRemoveEvents) + CFRelease(plugin->_onRemoveEvents); + + free(plugin); } /******************************************************************************* - *******************************************************************************/ +*******************************************************************************/ void * UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID) { - (void)allocator; + (void)allocator; BonjourUserEventsPlugin * result = NULL; - + if (typeID && CFEqual(typeID, kUserEventAgentTypeID)) { result = Alloc(kUserEventAgentFactoryID); } - + return (void *)result; } @@ -302,62 +302,61 @@ void * UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID) #pragma mark Plugin Management #pragma mark - /***************************************************************************** - * Install - * - - * This is invoked once when the plugin is loaded to do initial setup and - * allow us to register with launchd. If UserEventAgent crashes, the plugin - * will need to be reloaded, and hence this will get invoked again. - *****************************************************************************/ +* Install +* - +* This is invoked once when the plugin is loaded to do initial setup and +* allow us to register with launchd. If UserEventAgent crashes, the plugin +* will need to be reloaded, and hence this will get invoked again. +*****************************************************************************/ static void Install(void *instance) { - BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)instance; - - plugin->_pluginContext = UserEventAgentRegisterForLaunchEvents(sPluginIdentifier, &ManageEventsCallback, plugin); - - if (!plugin->_pluginContext) - { - fprintf(stderr, "%s:%s failed to register for launch events.\n", sPluginIdentifier, __FUNCTION__); - return; - } - + BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)instance; + + plugin->_pluginContext = UserEventAgentRegisterForLaunchEvents(sPluginIdentifier, &ManageEventsCallback, plugin); + + if (!plugin->_pluginContext) + { + fprintf(stderr, "%s:%s failed to register for launch events.\n", sPluginIdentifier, __FUNCTION__); + return; + } + } /***************************************************************************** - * ManageEventsCallback - * - - * This is invoked when launchd loads a event dictionary and needs to inform - * us what a daemon / agent is looking for. - *****************************************************************************/ +* ManageEventsCallback +* - +* This is invoked when launchd loads a event dictionary and needs to inform +* us what a daemon / agent is looking for. +*****************************************************************************/ static void ManageEventsCallback(UserEventAgentLaunchdAction action, CFNumberRef token, CFTypeRef eventMatchDict, void* vContext) { - - if (!eventMatchDict) - { - fprintf(stderr, "%s:%s empty dictionary\n", sPluginIdentifier, __FUNCTION__); - return; - } - if (CFGetTypeID(eventMatchDict) != CFDictionaryGetTypeID()) - { - fprintf(stderr, "%s:%s given non-dict for event dictionary, action %d\n", sPluginIdentifier, __FUNCTION__, action); - return; - } - - if (action == kUserEventAgentLaunchdAdd) - { - // Launchd wants us to add a launch event for this token and matching dictionary. - asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s calling AddEventToPlugin", sPluginIdentifier, __FUNCTION__); - AddEventToPlugin((BonjourUserEventsPlugin*)vContext, token, (CFDictionaryRef)eventMatchDict); - } - else if (action == kUserEventAgentLaunchdRemove) - { - // Launchd wants us to remove the event hook we setup for this token / matching dictionary. - asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s calling RemoveEventToPlugin", sPluginIdentifier, __FUNCTION__); - RemoveEventFromPlugin((BonjourUserEventsPlugin*)vContext, token); - } - else - { - asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s unknown callback event\n", sPluginIdentifier, __FUNCTION__); - } + if (action == kUserEventAgentLaunchdAdd) + { + if (!eventMatchDict) + { + fprintf(stderr, "%s:%s empty dictionary\n", sPluginIdentifier, __FUNCTION__); + return; + } + if (CFGetTypeID(eventMatchDict) != CFDictionaryGetTypeID()) + { + fprintf(stderr, "%s:%s given non-dict for event dictionary, action %d\n", sPluginIdentifier, __FUNCTION__, action); + return; + } + // Launchd wants us to add a launch event for this token and matching dictionary. + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s calling AddEventToPlugin", sPluginIdentifier, __FUNCTION__); + AddEventToPlugin((BonjourUserEventsPlugin*)vContext, token, (CFDictionaryRef)eventMatchDict); + } + else if (action == kUserEventAgentLaunchdRemove) + { + // Launchd wants us to remove the event hook we setup for this token / matching dictionary. + // Note: eventMatchDict can be NULL for Remove. + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s calling RemoveEventToPlugin", sPluginIdentifier, __FUNCTION__); + RemoveEventFromPlugin((BonjourUserEventsPlugin*)vContext, token); + } + else + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s unknown callback event\n", sPluginIdentifier, __FUNCTION__); + } } @@ -366,347 +365,347 @@ static void ManageEventsCallback(UserEventAgentLaunchdAction action, CFNumberRef #pragma mark - /***************************************************************************** - * AddEventToPlugin - * - - * This method is invoked when launchd wishes the plugin to setup a launch - * event matching the parameters in the dictionary. - *****************************************************************************/ +* AddEventToPlugin +* - +* This method is invoked when launchd wishes the plugin to setup a launch +* event matching the parameters in the dictionary. +*****************************************************************************/ void AddEventToPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchdToken, CFDictionaryRef eventParameters) { - CFStringRef domain = CFDictionaryGetValue(eventParameters, sServiceDomainKey); - CFStringRef type = CFDictionaryGetValue(eventParameters, sServiceTypeKey); - CFStringRef name = CFDictionaryGetValue(eventParameters, sServiceNameKey); - CFBooleanRef cfOnAdd = CFDictionaryGetValue(eventParameters, sOnServiceAddKey); - CFBooleanRef cfOnRemove = CFDictionaryGetValue(eventParameters, sOnServiceRemoveKey); - - Boolean onAdd = false; - Boolean onRemove = false; - - if (cfOnAdd && CFGetTypeID(cfOnAdd) == CFBooleanGetTypeID() && CFBooleanGetValue(cfOnAdd)) - onAdd = true; - - if (cfOnRemove && CFGetTypeID(cfOnRemove) == CFBooleanGetTypeID() && CFBooleanGetValue(cfOnRemove)) - onRemove = true; - - // A type is required. If none is specified, BAIL - if (!type || CFGetTypeID(type) != CFStringGetTypeID()) - { - fprintf(stderr, "%s:%s: a LaunchEvent is missing a service type.\n", sPluginIdentifier, __FUNCTION__); - return; - } - - // If we aren't suppose to launch on services appearing or disappearing, this service does nothing. Ignore. - if (!onAdd && !onRemove) - { - fprintf(stderr, "%s:%s a LaunchEvent is missing both onAdd and onRemove events\n", sPluginIdentifier, __FUNCTION__); - return; - } - - // If no domain is specified, assume local. - if (!domain) - { - domain = CFSTR("local"); - } - else if (CFGetTypeID(domain) != CFStringGetTypeID() ) // If the domain is not a string, fail - { - fprintf(stderr, "%s:%s a LaunchEvent has a domain that is not a string.\n", sPluginIdentifier, __FUNCTION__); - return; - } - - // If we have a name filter, but it's not a string. This event is broken, bail. - if (name && CFGetTypeID(name) != CFStringGetTypeID()) - { - fprintf(stderr, "%s:%s a LaunchEvent has a domain that is not a string.\n", sPluginIdentifier, __FUNCTION__); - return; - } - - // Get us a browser - NetBrowserInfo* browser = CreateBrowser(plugin, type, domain); - - if (!browser) - { - fprintf(stderr, "%s:%s cannot create browser\n", sPluginIdentifier, __FUNCTION__); - return; - } - - // Create Event Dictionary - CFMutableDictionaryRef eventDictionary = CFDictionaryCreateMutable(NULL, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - - // We store both the Token and the Dictionary. UserEventAgentSetLaunchEventState needs - // the token and UserEventAgentSetFireEvent needs both the token and the dictionary - CFDictionarySetValue(eventDictionary, sLaunchdTokenKey, launchdToken); - CFDictionarySetValue(eventDictionary, sLaunchdDictKey, eventParameters); - - if (name) - CFDictionarySetValue(eventDictionary, sServiceNameKey, name); - - // Add to the correct dictionary. - if (onAdd) - { - asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Adding browser to AddEvents", sPluginIdentifier, __FUNCTION__); - AddEventDictionary(eventDictionary, plugin->_onAddEvents, browser); - } - - if (onRemove) - { - asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Adding browser to RemoveEvents", sPluginIdentifier, __FUNCTION__); - AddEventDictionary(eventDictionary, plugin->_onRemoveEvents, browser); - } - - // Add Token Mapping - CFDictionarySetValue(plugin->_tokenToBrowserMap, launchdToken, browser); - - // Release Memory - CFRelease(eventDictionary); + CFStringRef domain = CFDictionaryGetValue(eventParameters, sServiceDomainKey); + CFStringRef type = CFDictionaryGetValue(eventParameters, sServiceTypeKey); + CFStringRef name = CFDictionaryGetValue(eventParameters, sServiceNameKey); + CFBooleanRef cfOnAdd = CFDictionaryGetValue(eventParameters, sOnServiceAddKey); + CFBooleanRef cfOnRemove = CFDictionaryGetValue(eventParameters, sOnServiceRemoveKey); + + Boolean onAdd = false; + Boolean onRemove = false; + + if (cfOnAdd && CFGetTypeID(cfOnAdd) == CFBooleanGetTypeID() && CFBooleanGetValue(cfOnAdd)) + onAdd = true; + + if (cfOnRemove && CFGetTypeID(cfOnRemove) == CFBooleanGetTypeID() && CFBooleanGetValue(cfOnRemove)) + onRemove = true; + + // A type is required. If none is specified, BAIL + if (!type || CFGetTypeID(type) != CFStringGetTypeID()) + { + fprintf(stderr, "%s:%s: a LaunchEvent is missing a service type.\n", sPluginIdentifier, __FUNCTION__); + return; + } + + // If we aren't suppose to launch on services appearing or disappearing, this service does nothing. Ignore. + if (!onAdd && !onRemove) + { + fprintf(stderr, "%s:%s a LaunchEvent is missing both onAdd and onRemove events\n", sPluginIdentifier, __FUNCTION__); + return; + } + + // If no domain is specified, assume local. + if (!domain) + { + domain = CFSTR("local"); + } + else if (CFGetTypeID(domain) != CFStringGetTypeID() ) // If the domain is not a string, fail + { + fprintf(stderr, "%s:%s a LaunchEvent has a domain that is not a string.\n", sPluginIdentifier, __FUNCTION__); + return; + } + + // If we have a name filter, but it's not a string. This event is broken, bail. + if (name && CFGetTypeID(name) != CFStringGetTypeID()) + { + fprintf(stderr, "%s:%s a LaunchEvent has a domain that is not a string.\n", sPluginIdentifier, __FUNCTION__); + return; + } + + // Get us a browser + NetBrowserInfo* browser = CreateBrowser(plugin, type, domain); + + if (!browser) + { + fprintf(stderr, "%s:%s cannot create browser\n", sPluginIdentifier, __FUNCTION__); + return; + } + + // Create Event Dictionary + CFMutableDictionaryRef eventDictionary = CFDictionaryCreateMutable(NULL, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + // We store both the Token and the Dictionary. UserEventAgentSetLaunchEventState needs + // the token and UserEventAgentSetFireEvent needs both the token and the dictionary + CFDictionarySetValue(eventDictionary, sLaunchdTokenKey, launchdToken); + CFDictionarySetValue(eventDictionary, sLaunchdDictKey, eventParameters); + + if (name) + CFDictionarySetValue(eventDictionary, sServiceNameKey, name); + + // Add to the correct dictionary. + if (onAdd) + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Adding browser to AddEvents", sPluginIdentifier, __FUNCTION__); + AddEventDictionary(eventDictionary, plugin->_onAddEvents, browser); + } + + if (onRemove) + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Adding browser to RemoveEvents", sPluginIdentifier, __FUNCTION__); + AddEventDictionary(eventDictionary, plugin->_onRemoveEvents, browser); + } + + // Add Token Mapping + CFDictionarySetValue(plugin->_tokenToBrowserMap, launchdToken, browser); + + // Release Memory + CFRelease(eventDictionary); } /***************************************************************************** - * RemoveEventFromPlugin - * - - * This method is invoked when launchd wishes the plugin to setup a launch - * event matching the parameters in the dictionary. - *****************************************************************************/ -void RemoveEventFromPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchdToken) +* RemoveEventFromPlugin +* - +* This method is invoked when launchd wishes the plugin to setup a launch +* event matching the parameters in the dictionary. +*****************************************************************************/ +void RemoveEventFromPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchdToken) { - NetBrowserInfo* browser = (NetBrowserInfo*)CFDictionaryGetValue(plugin->_tokenToBrowserMap, launchdToken); - Boolean othersUsingBrowser = false; - - if (!browser) - { - long long value = 0; - CFNumberGetValue(launchdToken, kCFNumberLongLongType, &value); - fprintf(stderr, "%s:%s Launchd asked us to remove a token we did not register! ==Token:%lld== \n", sPluginIdentifier, __FUNCTION__, value); - return; - } - - CFMutableArrayRef onAddEvents = (CFMutableArrayRef)CFDictionaryGetValue(plugin->_onAddEvents, browser); - CFMutableArrayRef onRemoveEvents = (CFMutableArrayRef)CFDictionaryGetValue(plugin->_onRemoveEvents, browser); - - if (onAddEvents) - { - asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Calling RemoveEventFromArray for OnAddEvents", sPluginIdentifier, __FUNCTION__); - RemoveEventFromArray(onAddEvents, launchdToken); - - // Is the array now empty, clean up - if (CFArrayGetCount(onAddEvents) == 0) - { - asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Removing the browser from AddEvents", sPluginIdentifier, __FUNCTION__); - CFDictionaryRemoveValue(plugin->_onAddEvents, browser); - } - } - - if (onRemoveEvents) - { - asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Calling RemoveEventFromArray for OnRemoveEvents", sPluginIdentifier, __FUNCTION__); - RemoveEventFromArray(onRemoveEvents, launchdToken); - - // Is the array now empty, clean up - if (CFArrayGetCount(onRemoveEvents) == 0) - { - asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Removing the browser from RemoveEvents", sPluginIdentifier, __FUNCTION__); - CFDictionaryRemoveValue(plugin->_onRemoveEvents, browser); - } - } - - // Remove ourselves from the token dictionary. - CFDictionaryRemoveValue(plugin->_tokenToBrowserMap, launchdToken); - - // Check to see if anyone else is using this browser. - CFIndex i; - CFIndex count = CFDictionaryGetCount(plugin->_tokenToBrowserMap); - NetBrowserInfo** browsers = malloc(count * sizeof(NetBrowserInfo*)); - - // Fetch the values of the token dictionary - CFDictionaryGetKeysAndValues(plugin->_tokenToBrowserMap, NULL, (const void**)browsers); - - for (i = 0; i < count; ++i) - { - if (NetBrowserInfoEqual(browsers[i], browser)) - { - othersUsingBrowser = true; - break; - } - } - - // If no one else is useing our browser, clean up! - if (!othersUsingBrowser) - { - asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Removing browser %p from _browsers", sPluginIdentifier, __FUNCTION__, browser); - CFDictionaryRemoveValue(plugin->_browsers, browser); // This triggers release and dealloc of the browser - } - else - { - asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Decrementing browsers %p count", sPluginIdentifier, __FUNCTION__, browser); - // Decrement my reference count (it was incremented when it was added to _browsers in CreateBrowser) - NetBrowserInfoRelease(NULL, browser); - } - - free(browsers); + NetBrowserInfo* browser = (NetBrowserInfo*)CFDictionaryGetValue(plugin->_tokenToBrowserMap, launchdToken); + Boolean othersUsingBrowser = false; + + if (!browser) + { + long long value = 0; + CFNumberGetValue(launchdToken, kCFNumberLongLongType, &value); + fprintf(stderr, "%s:%s Launchd asked us to remove a token we did not register! ==Token:%lld== \n", sPluginIdentifier, __FUNCTION__, value); + return; + } + + CFMutableArrayRef onAddEvents = (CFMutableArrayRef)CFDictionaryGetValue(plugin->_onAddEvents, browser); + CFMutableArrayRef onRemoveEvents = (CFMutableArrayRef)CFDictionaryGetValue(plugin->_onRemoveEvents, browser); + + if (onAddEvents) + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Calling RemoveEventFromArray for OnAddEvents", sPluginIdentifier, __FUNCTION__); + RemoveEventFromArray(onAddEvents, launchdToken); + + // Is the array now empty, clean up + if (CFArrayGetCount(onAddEvents) == 0) + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Removing the browser from AddEvents", sPluginIdentifier, __FUNCTION__); + CFDictionaryRemoveValue(plugin->_onAddEvents, browser); + } + } + + if (onRemoveEvents) + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Calling RemoveEventFromArray for OnRemoveEvents", sPluginIdentifier, __FUNCTION__); + RemoveEventFromArray(onRemoveEvents, launchdToken); + + // Is the array now empty, clean up + if (CFArrayGetCount(onRemoveEvents) == 0) + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Removing the browser from RemoveEvents", sPluginIdentifier, __FUNCTION__); + CFDictionaryRemoveValue(plugin->_onRemoveEvents, browser); + } + } + + // Remove ourselves from the token dictionary. + CFDictionaryRemoveValue(plugin->_tokenToBrowserMap, launchdToken); + + // Check to see if anyone else is using this browser. + CFIndex i; + CFIndex count = CFDictionaryGetCount(plugin->_tokenToBrowserMap); + NetBrowserInfo** browsers = malloc(count * sizeof(NetBrowserInfo*)); + + // Fetch the values of the token dictionary + CFDictionaryGetKeysAndValues(plugin->_tokenToBrowserMap, NULL, (const void**)browsers); + + for (i = 0; i < count; ++i) + { + if (NetBrowserInfoEqual(browsers[i], browser)) + { + othersUsingBrowser = true; + break; + } + } + + // If no one else is useing our browser, clean up! + if (!othersUsingBrowser) + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Removing browser %p from _browsers", sPluginIdentifier, __FUNCTION__, browser); + CFDictionaryRemoveValue(plugin->_browsers, browser); // This triggers release and dealloc of the browser + } + else + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Decrementing browsers %p count", sPluginIdentifier, __FUNCTION__, browser); + // Decrement my reference count (it was incremented when it was added to _browsers in CreateBrowser) + NetBrowserInfoRelease(NULL, browser); + } + + free(browsers); } /***************************************************************************** - * CreateBrowser - * - - * This method returns a NetBrowserInfo that is looking for a type of - * service in a domain. If no browser exists, it will create one and return it. - *****************************************************************************/ +* CreateBrowser +* - +* This method returns a NetBrowserInfo that is looking for a type of +* service in a domain. If no browser exists, it will create one and return it. +*****************************************************************************/ NetBrowserInfo* CreateBrowser(BonjourUserEventsPlugin* plugin, CFStringRef type, CFStringRef domain) { - CFIndex i; - CFIndex count = CFDictionaryGetCount(plugin->_browsers); - NetBrowserInfo* browser = NULL; - CFDictionaryRef* dicts = malloc(count * sizeof(CFDictionaryRef)); - NetBrowserInfo** browsers = malloc(count * sizeof(NetBrowserInfo*)); - - // Fetch the values of the browser dictionary - CFDictionaryGetKeysAndValues(plugin->_browsers, (const void**)browsers, (const void**)dicts); - - - // Loop thru the browsers list and see if we can find a matching one. - for (i = 0; i < count; ++i) - { - CFDictionaryRef browserDict = dicts[i]; - - CFStringRef browserType = CFDictionaryGetValue(browserDict, sServiceTypeKey); - CFStringRef browserDomain = CFDictionaryGetValue(browserDict, sServiceDomainKey); - - // If we have a matching browser, break - if ((CFStringCompare(browserType, type, kCFCompareCaseInsensitive) == kCFCompareEqualTo) && - (CFStringCompare(browserDomain, domain, kCFCompareCaseInsensitive) == kCFCompareEqualTo)) - { - asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: found a duplicate browser\n", sPluginIdentifier, __FUNCTION__); - browser = browsers[i]; - NetBrowserInfoRetain(NULL, browser); - break; - } - } - - // No match found, lets create one! - if (!browser) - { - - browser = NetBrowserInfoCreate(type, domain, plugin); - - if (!browser) - { - fprintf(stderr, "%s:%s failed to search for %s.%s", sPluginIdentifier, __FUNCTION__, CStringFromCFString(type) , CStringFromCFString(domain)); - free(dicts); - free(browsers); - return NULL; - } - - // Service browser created, lets add this to ourselves to the dictionary. - CFMutableDictionaryRef browserDict = CFDictionaryCreateMutable(NULL, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - - CFDictionarySetValue(browserDict, sServiceTypeKey, type); - CFDictionarySetValue(browserDict, sServiceDomainKey, domain); - - // Add the dictionary to the browsers dictionary. - CFDictionarySetValue(plugin->_browsers, browser, browserDict); - - NetBrowserInfoRelease(NULL, browser); - - // Release Memory - CFRelease(browserDict); - } - - free(dicts); - free(browsers); - - return browser; + CFIndex i; + CFIndex count = CFDictionaryGetCount(plugin->_browsers); + NetBrowserInfo* browser = NULL; + CFDictionaryRef* dicts = malloc(count * sizeof(CFDictionaryRef)); + NetBrowserInfo** browsers = malloc(count * sizeof(NetBrowserInfo*)); + + // Fetch the values of the browser dictionary + CFDictionaryGetKeysAndValues(plugin->_browsers, (const void**)browsers, (const void**)dicts); + + + // Loop thru the browsers list and see if we can find a matching one. + for (i = 0; i < count; ++i) + { + CFDictionaryRef browserDict = dicts[i]; + + CFStringRef browserType = CFDictionaryGetValue(browserDict, sServiceTypeKey); + CFStringRef browserDomain = CFDictionaryGetValue(browserDict, sServiceDomainKey); + + // If we have a matching browser, break + if ((CFStringCompare(browserType, type, kCFCompareCaseInsensitive) == kCFCompareEqualTo) && + (CFStringCompare(browserDomain, domain, kCFCompareCaseInsensitive) == kCFCompareEqualTo)) + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: found a duplicate browser\n", sPluginIdentifier, __FUNCTION__); + browser = browsers[i]; + NetBrowserInfoRetain(NULL, browser); + break; + } + } + + // No match found, lets create one! + if (!browser) + { + + browser = NetBrowserInfoCreate(type, domain, plugin); + + if (!browser) + { + fprintf(stderr, "%s:%s failed to search for %s.%s", sPluginIdentifier, __FUNCTION__, CStringFromCFString(type), CStringFromCFString(domain)); + free(dicts); + free(browsers); + return NULL; + } + + // Service browser created, lets add this to ourselves to the dictionary. + CFMutableDictionaryRef browserDict = CFDictionaryCreateMutable(NULL, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + CFDictionarySetValue(browserDict, sServiceTypeKey, type); + CFDictionarySetValue(browserDict, sServiceDomainKey, domain); + + // Add the dictionary to the browsers dictionary. + CFDictionarySetValue(plugin->_browsers, browser, browserDict); + + NetBrowserInfoRelease(NULL, browser); + + // Release Memory + CFRelease(browserDict); + } + + free(dicts); + free(browsers); + + return browser; } /***************************************************************************** - * BrowserForSDRef - * - - * This method returns a NetBrowserInfo that matches the calling SDRef passed - * in via the callback. - *****************************************************************************/ +* BrowserForSDRef +* - +* This method returns a NetBrowserInfo that matches the calling SDRef passed +* in via the callback. +*****************************************************************************/ NetBrowserInfo* BrowserForSDRef(BonjourUserEventsPlugin* plugin, DNSServiceRef sdRef) { - CFIndex i; - CFIndex count = CFDictionaryGetCount(plugin->_browsers); - NetBrowserInfo* browser = NULL; - NetBrowserInfo** browsers = malloc(count * sizeof(NetBrowserInfo*)); - - // Fetch the values of the browser dictionary - CFDictionaryGetKeysAndValues(plugin->_browsers, (const void**)browsers, NULL); - - // Loop thru the browsers list and see if we can find a matching one. - for (i = 0; i < count; ++i) - { - NetBrowserInfo* currentBrowser = browsers[i]; - - if (currentBrowser->browserRef == sdRef) - { - browser = currentBrowser; - break; - } - } - - - free(browsers); - - return browser; + CFIndex i; + CFIndex count = CFDictionaryGetCount(plugin->_browsers); + NetBrowserInfo* browser = NULL; + NetBrowserInfo** browsers = malloc(count * sizeof(NetBrowserInfo*)); + + // Fetch the values of the browser dictionary + CFDictionaryGetKeysAndValues(plugin->_browsers, (const void**)browsers, NULL); + + // Loop thru the browsers list and see if we can find a matching one. + for (i = 0; i < count; ++i) + { + NetBrowserInfo* currentBrowser = browsers[i]; + + if (currentBrowser->browserRef == sdRef) + { + browser = currentBrowser; + break; + } + } + + + free(browsers); + + return browser; } /***************************************************************************** - * AddEventDictionary - * - - * Adds a event to a browser's event dictionary - *****************************************************************************/ +* AddEventDictionary +* - +* Adds a event to a browser's event dictionary +*****************************************************************************/ void AddEventDictionary(CFDictionaryRef eventDict, CFMutableDictionaryRef allEventsDictionary, NetBrowserInfo* key) { - CFMutableArrayRef eventsForBrowser = (CFMutableArrayRef)CFDictionaryGetValue(allEventsDictionary, key); - - if (!eventsForBrowser) // We have no events for this browser yet, lets add him. - { - eventsForBrowser = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - CFDictionarySetValue(allEventsDictionary, key, eventsForBrowser); - asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s creating a new array", sPluginIdentifier, __FUNCTION__); - } - else - { - asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s Incrementing refcount", sPluginIdentifier, __FUNCTION__); - CFRetain(eventsForBrowser); - } - - CFArrayAppendValue(eventsForBrowser, eventDict); - CFRelease(eventsForBrowser); + CFMutableArrayRef eventsForBrowser = (CFMutableArrayRef)CFDictionaryGetValue(allEventsDictionary, key); + + if (!eventsForBrowser) // We have no events for this browser yet, lets add him. + { + eventsForBrowser = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + CFDictionarySetValue(allEventsDictionary, key, eventsForBrowser); + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s creating a new array", sPluginIdentifier, __FUNCTION__); + } + else + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s Incrementing refcount", sPluginIdentifier, __FUNCTION__); + CFRetain(eventsForBrowser); + } + + CFArrayAppendValue(eventsForBrowser, eventDict); + CFRelease(eventsForBrowser); } /***************************************************************************** - * RemoveEventFromArray - * - - * Searches a Array of Event Dictionaries to find one with a matching launchd - * token and remove it. - *****************************************************************************/ +* RemoveEventFromArray +* - +* Searches a Array of Event Dictionaries to find one with a matching launchd +* token and remove it. +*****************************************************************************/ void RemoveEventFromArray(CFMutableArrayRef array, CFNumberRef launchdToken) { - CFIndex i; - CFIndex count = CFArrayGetCount(array); - - // Loop thru looking for us. - for (i = 0; i < count; ) - { - CFDictionaryRef eventDict = CFArrayGetValueAtIndex(array, i); - CFNumberRef token = CFDictionaryGetValue(eventDict, sLaunchdTokenKey); - - if (CFEqual(token, launchdToken)) // This is the same event? - { - asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s found token", sPluginIdentifier, __FUNCTION__); - CFArrayRemoveValueAtIndex(array, i); // Remove the event, - break; // The token should only exist once, so it makes no sense to continue. - } - else - { - ++i; // If it's not us, advance. - } - } - if (i == count) asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s did not find token", sPluginIdentifier, __FUNCTION__); + CFIndex i; + CFIndex count = CFArrayGetCount(array); + + // Loop thru looking for us. + for (i = 0; i < count; ) + { + CFDictionaryRef eventDict = CFArrayGetValueAtIndex(array, i); + CFNumberRef token = CFDictionaryGetValue(eventDict, sLaunchdTokenKey); + + if (CFEqual(token, launchdToken)) // This is the same event? + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s found token", sPluginIdentifier, __FUNCTION__); + CFArrayRemoveValueAtIndex(array, i); // Remove the event, + break; // The token should only exist once, so it makes no sense to continue. + } + else + { + ++i; // If it's not us, advance. + } + } + if (i == count) asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s did not find token", sPluginIdentifier, __FUNCTION__); } #pragma mark - @@ -714,97 +713,97 @@ void RemoveEventFromArray(CFMutableArrayRef array, CFNumberRef launchdToken) #pragma mark - /***************************************************************************** - * ServiceBrowserCallback - * - - * This method is the heart of the plugin. It's the runloop callback annoucing - * the appearence and disappearance of network services. - *****************************************************************************/ - -void ServiceBrowserCallback (DNSServiceRef sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceErrorType errorCode, - const char* serviceName, - const char* regtype, - const char* replyDomain, - void* context ) +* ServiceBrowserCallback +* - +* This method is the heart of the plugin. It's the runloop callback annoucing +* the appearence and disappearance of network services. +*****************************************************************************/ + +void ServiceBrowserCallback (DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char* serviceName, + const char* regtype, + const char* replyDomain, + void* context ) { - (void)interfaceIndex; - (void)regtype; - (void)replyDomain; - BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)context; - NetBrowserInfo* browser = BrowserForSDRef(plugin, sdRef); - - if (!browser) // Missing browser? - { - fprintf(stderr, "%s:%s ServiceBrowserCallback: missing browser\n", sPluginIdentifier, __FUNCTION__); - return; - } - - if (errorCode != kDNSServiceErr_NoError) - { - fprintf(stderr, "%s:%s ServiceBrowserCallback: errcode set %d\n", sPluginIdentifier, __FUNCTION__, errorCode); - return; - } - - CFStringRef cfServiceName = CFStringCreateWithCString(NULL, serviceName, kCFStringEncodingUTF8); - - if (flags & kDNSServiceFlagsAdd) - { - asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s calling HandleTemporaryEventsForService Add\n", sPluginIdentifier, __FUNCTION__); - HandleTemporaryEventsForService(plugin, browser, cfServiceName, plugin->_onAddEvents); - } - else - { - asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s calling HandleTemporaryEventsForService Remove\n", sPluginIdentifier, __FUNCTION__); - HandleTemporaryEventsForService(plugin, browser, cfServiceName, plugin->_onRemoveEvents); - } - - CFRelease(cfServiceName); + (void)interfaceIndex; + (void)regtype; + (void)replyDomain; + BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)context; + NetBrowserInfo* browser = BrowserForSDRef(plugin, sdRef); + + if (!browser) // Missing browser? + { + fprintf(stderr, "%s:%s ServiceBrowserCallback: missing browser\n", sPluginIdentifier, __FUNCTION__); + return; + } + + if (errorCode != kDNSServiceErr_NoError) + { + fprintf(stderr, "%s:%s ServiceBrowserCallback: errcode set %d\n", sPluginIdentifier, __FUNCTION__, errorCode); + return; + } + + CFStringRef cfServiceName = CFStringCreateWithCString(NULL, serviceName, kCFStringEncodingUTF8); + + if (flags & kDNSServiceFlagsAdd) + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s calling HandleTemporaryEventsForService Add\n", sPluginIdentifier, __FUNCTION__); + HandleTemporaryEventsForService(plugin, browser, cfServiceName, plugin->_onAddEvents); + } + else + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s calling HandleTemporaryEventsForService Remove\n", sPluginIdentifier, __FUNCTION__); + HandleTemporaryEventsForService(plugin, browser, cfServiceName, plugin->_onRemoveEvents); + } + + CFRelease(cfServiceName); } /***************************************************************************** - * HandleTemporaryEventsForService - * - - * This method handles the firing of one shot events. Aka. Events that are - * signaled when a service appears / disappears. They have a temporarly - * signaled state. - *****************************************************************************/ +* HandleTemporaryEventsForService +* - +* This method handles the firing of one shot events. Aka. Events that are +* signaled when a service appears / disappears. They have a temporarly +* signaled state. +*****************************************************************************/ void HandleTemporaryEventsForService(BonjourUserEventsPlugin* plugin, NetBrowserInfo* browser, CFStringRef serviceName, CFMutableDictionaryRef eventsDictionary) { - CFArrayRef events = (CFArrayRef)CFDictionaryGetValue(eventsDictionary, browser); // Get events for the browser we passed in. - CFIndex i; - CFIndex count; - - if (!events) // Somehow we have a orphan browser... - return; - - count = CFArrayGetCount(events); - - // Go thru the events and run filters, notifity if they pass. - for (i = 0; i < count; ++i) - { - CFDictionaryRef eventDict = (CFDictionaryRef)CFArrayGetValueAtIndex(events, i); - CFStringRef eventServiceName = (CFStringRef)CFDictionaryGetValue(eventDict, sServiceNameKey); - CFNumberRef token = (CFNumberRef) CFDictionaryGetValue(eventDict, sLaunchdTokenKey); - CFDictionaryRef dict = (CFDictionaryRef) CFDictionaryGetValue(eventDict, sLaunchdDictKey); - - // Currently we only filter on service name, that makes this as simple as... - if (!eventServiceName || CFEqual(serviceName, eventServiceName)) - { - uint64_t tokenUint64; - // Signal Event: This is edge trigger. When the action has been taken, it will not - // be remembered anymore. - - asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s HandleTemporaryEventsForService signal\n", sPluginIdentifier, __FUNCTION__); - CFNumberGetValue(token, kCFNumberLongLongType, &tokenUint64); - - xpc_object_t jobRequest = _CFXPCCreateXPCObjectFromCFObject(dict); - - UserEventAgentFireEvent(plugin->_pluginContext, tokenUint64, jobRequest); - xpc_release(jobRequest); - } - } + CFArrayRef events = (CFArrayRef)CFDictionaryGetValue(eventsDictionary, browser); // Get events for the browser we passed in. + CFIndex i; + CFIndex count; + + if (!events) // Somehow we have a orphan browser... + return; + + count = CFArrayGetCount(events); + + // Go thru the events and run filters, notifity if they pass. + for (i = 0; i < count; ++i) + { + CFDictionaryRef eventDict = (CFDictionaryRef)CFArrayGetValueAtIndex(events, i); + CFStringRef eventServiceName = (CFStringRef)CFDictionaryGetValue(eventDict, sServiceNameKey); + CFNumberRef token = (CFNumberRef) CFDictionaryGetValue(eventDict, sLaunchdTokenKey); + CFDictionaryRef dict = (CFDictionaryRef) CFDictionaryGetValue(eventDict, sLaunchdDictKey); + + // Currently we only filter on service name, that makes this as simple as... + if (!eventServiceName || CFEqual(serviceName, eventServiceName)) + { + uint64_t tokenUint64; + // Signal Event: This is edge trigger. When the action has been taken, it will not + // be remembered anymore. + + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s HandleTemporaryEventsForService signal\n", sPluginIdentifier, __FUNCTION__); + CFNumberGetValue(token, kCFNumberLongLongType, &tokenUint64); + + xpc_object_t jobRequest = _CFXPCCreateXPCObjectFromCFObject(dict); + + UserEventAgentFireEvent(plugin->_pluginContext, tokenUint64, jobRequest); + xpc_release(jobRequest); + } + } } #pragma mark - @@ -812,178 +811,178 @@ void HandleTemporaryEventsForService(BonjourUserEventsPlugin* plugin, NetBrowser #pragma mark - /***************************************************************************** - * CStringFromCFString - * - - * Silly convenence function for dealing with non-critical CFSTR -> cStr - * conversions. - *****************************************************************************/ +* CStringFromCFString +* - +* Silly convenence function for dealing with non-critical CFSTR -> cStr +* conversions. +*****************************************************************************/ const char* CStringFromCFString(CFStringRef string) { - const char* defaultString = "??????"; - const char* cstring; - - if (!string) - return defaultString; - - cstring = CFStringGetCStringPtr(string, kCFStringEncodingUTF8); - - return (cstring) ? cstring : defaultString; - + const char* defaultString = "??????"; + const char* cstring; + + if (!string) + return defaultString; + + cstring = CFStringGetCStringPtr(string, kCFStringEncodingUTF8); + + return (cstring) ? cstring : defaultString; + } #pragma mark - #pragma mark NetBrowserInfo "Object" #pragma mark - /***************************************************************************** - * NetBrowserInfoCreate - * - - * The method creates a NetBrowserInfo Object and initalizes it. - *****************************************************************************/ +* NetBrowserInfoCreate +* - +* The method creates a NetBrowserInfo Object and initalizes it. +*****************************************************************************/ NetBrowserInfo* NetBrowserInfoCreate(CFStringRef serviceType, CFStringRef domain, void* context) { - NetBrowserInfo* outObj = NULL; - DNSServiceRef browserRef = NULL; - char* cServiceType = NULL; - char* cDomain = NULL; - Boolean success = true; - - CFIndex serviceSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(serviceType), kCFStringEncodingUTF8); - cServiceType = calloc(serviceSize, 1); - success = CFStringGetCString(serviceType, cServiceType, serviceSize, kCFStringEncodingUTF8); - - - if (domain) - { - CFIndex domainSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(domain), kCFStringEncodingUTF8); - cDomain = calloc(serviceSize, 1); - success = success && CFStringGetCString(domain, cDomain, domainSize, kCFStringEncodingUTF8); - } - - if (!success) - { - fprintf(stderr, "%s:%s LaunchEvent has badly encoded service type or domain.\n", sPluginIdentifier, __FUNCTION__); - free(cServiceType); - - if (cDomain) - free(cDomain); - - return NULL; - } - - DNSServiceErrorType err = DNSServiceBrowse(&browserRef, 0, 0, cServiceType, cDomain, ServiceBrowserCallback, context); - - if (err != kDNSServiceErr_NoError) - { - fprintf(stderr, "%s:%s Failed to create browser for %s, %s\n", sPluginIdentifier, __FUNCTION__, cServiceType, cDomain); - free(cServiceType); - - if (cDomain) - free(cDomain); - - return NULL; - } - - DNSServiceSetDispatchQueue(browserRef, dispatch_get_main_queue()); - - - outObj = malloc(sizeof(NetBrowserInfo)); - - outObj->refCount = 1; - outObj->browserRef = browserRef; - - asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: created new object %p", sPluginIdentifier, __FUNCTION__, outObj); - - free(cServiceType); - - if (cDomain) - free(cDomain); - - return outObj; + NetBrowserInfo* outObj = NULL; + DNSServiceRef browserRef = NULL; + char* cServiceType = NULL; + char* cDomain = NULL; + Boolean success = true; + + CFIndex serviceSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(serviceType), kCFStringEncodingUTF8); + cServiceType = calloc(serviceSize, 1); + success = CFStringGetCString(serviceType, cServiceType, serviceSize, kCFStringEncodingUTF8); + + + if (domain) + { + CFIndex domainSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(domain), kCFStringEncodingUTF8); + cDomain = calloc(serviceSize, 1); + success = success && CFStringGetCString(domain, cDomain, domainSize, kCFStringEncodingUTF8); + } + + if (!success) + { + fprintf(stderr, "%s:%s LaunchEvent has badly encoded service type or domain.\n", sPluginIdentifier, __FUNCTION__); + free(cServiceType); + + if (cDomain) + free(cDomain); + + return NULL; + } + + DNSServiceErrorType err = DNSServiceBrowse(&browserRef, 0, 0, cServiceType, cDomain, ServiceBrowserCallback, context); + + if (err != kDNSServiceErr_NoError) + { + fprintf(stderr, "%s:%s Failed to create browser for %s, %s\n", sPluginIdentifier, __FUNCTION__, cServiceType, cDomain); + free(cServiceType); + + if (cDomain) + free(cDomain); + + return NULL; + } + + DNSServiceSetDispatchQueue(browserRef, dispatch_get_main_queue()); + + + outObj = malloc(sizeof(NetBrowserInfo)); + + outObj->refCount = 1; + outObj->browserRef = browserRef; + + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: created new object %p", sPluginIdentifier, __FUNCTION__, outObj); + + free(cServiceType); + + if (cDomain) + free(cDomain); + + return outObj; } /***************************************************************************** - * NetBrowserInfoRetain - * - - * The method retains a NetBrowserInfo object. - *****************************************************************************/ +* NetBrowserInfoRetain +* - +* The method retains a NetBrowserInfo object. +*****************************************************************************/ const void* NetBrowserInfoRetain(CFAllocatorRef allocator, const void* info) { - (void)allocator; - NetBrowserInfo* obj = (NetBrowserInfo*)info; - - if (!obj) - return NULL; - - ++obj->refCount; - asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Incremented ref count on %p, count %d", sPluginIdentifier, __FUNCTION__, obj->browserRef, (int)obj->refCount); - - return obj; + (void)allocator; + NetBrowserInfo* obj = (NetBrowserInfo*)info; + + if (!obj) + return NULL; + + ++obj->refCount; + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Incremented ref count on %p, count %d", sPluginIdentifier, __FUNCTION__, obj->browserRef, (int)obj->refCount); + + return obj; } /***************************************************************************** - * NetBrowserInfoRelease - * - - * The method releases a NetBrowserInfo object. - *****************************************************************************/ +* NetBrowserInfoRelease +* - +* The method releases a NetBrowserInfo object. +*****************************************************************************/ void NetBrowserInfoRelease(CFAllocatorRef allocator, const void* info) { - (void)allocator; - NetBrowserInfo* obj = (NetBrowserInfo*)info; - - if (!obj) - return; - - if (obj->refCount == 1) - { - asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: DNSServiceRefDeallocate %p", sPluginIdentifier, __FUNCTION__, obj->browserRef); - DNSServiceRefDeallocate(obj->browserRef); - free(obj); - } - else - { - --obj->refCount; - asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Decremented ref count on %p, count %d", sPluginIdentifier, __FUNCTION__, obj->browserRef, (int)obj->refCount); - } + (void)allocator; + NetBrowserInfo* obj = (NetBrowserInfo*)info; + + if (!obj) + return; + + if (obj->refCount == 1) + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: DNSServiceRefDeallocate %p", sPluginIdentifier, __FUNCTION__, obj->browserRef); + DNSServiceRefDeallocate(obj->browserRef); + free(obj); + } + else + { + --obj->refCount; + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Decremented ref count on %p, count %d", sPluginIdentifier, __FUNCTION__, obj->browserRef, (int)obj->refCount); + } } /***************************************************************************** - * NetBrowserInfoEqual - * - - * The method is used to compare two NetBrowserInfo objects for equality. - *****************************************************************************/ -Boolean NetBrowserInfoEqual(const void *value1, const void *value2) +* NetBrowserInfoEqual +* - +* The method is used to compare two NetBrowserInfo objects for equality. +*****************************************************************************/ +Boolean NetBrowserInfoEqual(const void *value1, const void *value2) { - NetBrowserInfo* obj1 = (NetBrowserInfo*)value1; - NetBrowserInfo* obj2 = (NetBrowserInfo*)value2; - - if (obj1->browserRef == obj2->browserRef) - return true; - - return false; + NetBrowserInfo* obj1 = (NetBrowserInfo*)value1; + NetBrowserInfo* obj2 = (NetBrowserInfo*)value2; + + if (obj1->browserRef == obj2->browserRef) + return true; + + return false; } /***************************************************************************** - * NetBrowserInfoHash - * - - * The method is used to make a hash for the object. We can cheat and use the - * browser pointer. - *****************************************************************************/ -CFHashCode NetBrowserInfoHash(const void *value) +* NetBrowserInfoHash +* - +* The method is used to make a hash for the object. We can cheat and use the +* browser pointer. +*****************************************************************************/ +CFHashCode NetBrowserInfoHash(const void *value) { - return (CFHashCode)((NetBrowserInfo*)value)->browserRef; + return (CFHashCode)((NetBrowserInfo*)value)->browserRef; } /***************************************************************************** - * NetBrowserInfoCopyDescription - * - - * Make CF happy. - *****************************************************************************/ -CFStringRef NetBrowserInfoCopyDescription(const void *value) +* NetBrowserInfoCopyDescription +* - +* Make CF happy. +*****************************************************************************/ +CFStringRef NetBrowserInfoCopyDescription(const void *value) { - (void)value; - return CFStringCreateWithCString(NULL, "NetBrowserInfo: No useful description", kCFStringEncodingUTF8); + (void)value; + return CFStringCreateWithCString(NULL, "NetBrowserInfo: No useful description", kCFStringEncodingUTF8); } diff --git a/mDNSMacOSX/CryptoSupport.c b/mDNSMacOSX/CryptoSupport.c new file mode 100644 index 0000000..4223ca2 --- /dev/null +++ b/mDNSMacOSX/CryptoSupport.c @@ -0,0 +1,524 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// *************************************************************************** +// CryptoSupport.c +// Supporting routines for DNSSEC crypto +// *************************************************************************** + +#include "mDNSEmbeddedAPI.h" +#include // For Hash algorithms SHA1 etc. +#include // For Base32/Base64 encoding/decoding +#include // dispatch_data_create_with_transform +#include "CryptoAlg.h" +#include "CryptoSupport.h" +#include "dnssec.h" + +#if TARGET_OS_IPHONE +#include "SecRSAKey.h" // For RSA_SHA1 etc. verification +#endif + +typedef struct +{ +#if DISPATCH_API_VERSION >= 20111008 + dispatch_data_t encData; + dispatch_data_t encMap; + dispatch_data_t encNULL; +#endif +}encContext; + +mDNSlocal mStatus enc_create(AlgContext *ctx) +{ + encContext *ptr; + + switch (ctx->alg) + { + case ENC_BASE32: + case ENC_BASE64: + ptr = (encContext *)mDNSPlatformMemAllocate(sizeof(encContext)); + if (!ptr) return mStatus_NoMemoryErr; + break; + default: + LogMsg("enc_create: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } +#if DISPATCH_API_VERSION >= 20111008 + ptr->encData = NULL; + ptr->encMap = NULL; + // The encoded data is not NULL terminated. So, we concatenate a null byte later when we encode and map + // the real data. + ptr->encNULL = dispatch_data_create("", 1, dispatch_get_global_queue(0, 0), ^{}); + if (!ptr->encNULL) + { + mDNSPlatformMemFree(ptr); + return mStatus_NoMemoryErr; + } +#endif + ctx->context = ptr; + return mStatus_NoError; +} + +mDNSlocal mStatus enc_destroy(AlgContext *ctx) +{ + encContext *ptr = (encContext *)ctx->context; +#if DISPATCH_API_VERSION >= 20111008 + if (ptr->encData) dispatch_release(ptr->encData); + if (ptr->encMap) dispatch_release(ptr->encMap); + if (ptr->encNULL) dispatch_release(ptr->encNULL); +#endif + mDNSPlatformMemFree(ptr); + return mStatus_NoError; +} + +mDNSlocal mStatus enc_add(AlgContext *ctx, void *data, mDNSu32 len) +{ + switch (ctx->alg) + { + case ENC_BASE32: + case ENC_BASE64: + { +#if DISPATCH_API_VERSION >= 20111008 + encContext *ptr = (encContext *)ctx->context; + dispatch_data_t src_data = dispatch_data_create(data, len, dispatch_get_global_queue(0, 0), ^{}); + if (!src_data) + { + LogMsg("enc_add: dispatch_data_create src failed"); + return mStatus_BadParamErr; + } + dispatch_data_t dest_data = dispatch_data_create_with_transform(src_data, DISPATCH_DATA_FORMAT_TYPE_NONE, + (ctx->alg == ENC_BASE32 ? DISPATCH_DATA_FORMAT_TYPE_BASE32 : DISPATCH_DATA_FORMAT_TYPE_BASE64)); + dispatch_release(src_data); + if (!dest_data) + { + LogMsg("enc_add: dispatch_data_create dst failed"); + return mStatus_BadParamErr; + } + ptr->encData = dest_data; +#else + (void)data; + (void)len; +#endif + return mStatus_NoError; + } + default: + LogMsg("enc_add: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } +} + +mDNSlocal mDNSu8* enc_encode(AlgContext *ctx) +{ + const void *result = NULL; + + switch (ctx->alg) + { + case ENC_BASE32: + case ENC_BASE64: + { +#if DISPATCH_API_VERSION >= 20111008 + encContext *ptr = (encContext *)ctx->context; + size_t size; + dispatch_data_t dest_data = ptr->encData; + dispatch_data_t data = dispatch_data_create_concat(dest_data, ptr->encNULL); + + if (!data) + { + LogMsg("enc_encode: cannot concatenate"); + return NULL; + } + + dispatch_data_t map = dispatch_data_create_map(data, &result, &size); + if (!map) + { + LogMsg("enc_encode: cannot create map %d", ctx->alg); + return NULL; + } + dispatch_release(dest_data); + ptr->encData = data; + ptr->encMap = map; +#endif + return (mDNSu8 *)result; + } + default: + LogMsg("enc_encode: Unsupported algorithm %d", ctx->alg); + return mDNSNULL; + } +} + +mDNSlocal mStatus sha_create(AlgContext *ctx) +{ + mDNSu8 *ptr; + switch (ctx->alg) + { + case SHA1_DIGEST_TYPE: + ptr = mDNSPlatformMemAllocate(sizeof(CC_SHA1_CTX)); + if (!ptr) return mStatus_NoMemoryErr; + CC_SHA1_Init((CC_SHA1_CTX *)ptr); + break; + case SHA256_DIGEST_TYPE: + ptr = mDNSPlatformMemAllocate(sizeof(CC_SHA256_CTX)); + if (!ptr) return mStatus_NoMemoryErr; + CC_SHA256_Init((CC_SHA256_CTX *)ptr); + break; + default: + LogMsg("sha_create: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } + ctx->context = ptr; + return mStatus_NoError; +} + +mDNSlocal mStatus sha_destroy(AlgContext *ctx) +{ + mDNSPlatformMemFree(ctx->context); + return mStatus_NoError; +} + +mDNSlocal mDNSu32 sha_len(AlgContext *ctx) +{ + switch (ctx->alg) + { + case SHA1_DIGEST_TYPE: + return CC_SHA1_DIGEST_LENGTH; + case SHA256_DIGEST_TYPE: + return CC_SHA256_DIGEST_LENGTH; + default: + LogMsg("sha_len: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } +} + +mDNSlocal mStatus sha_add(AlgContext *ctx, void *data, mDNSu32 len) +{ + switch (ctx->alg) + { + case SHA1_DIGEST_TYPE: + CC_SHA1_Update((CC_SHA1_CTX *)ctx->context, data, len); + break; + case SHA256_DIGEST_TYPE: + CC_SHA256_Update((CC_SHA256_CTX *)ctx->context, data, len); + break; + default: + LogMsg("sha_add: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } + return mStatus_NoError; +} + +mDNSlocal mStatus sha_verify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *digestIn, mDNSu32 dlen) +{ + mDNSu8 digest[CC_SHA512_DIGEST_LENGTH]; + mDNSu32 digestLen; + + (void) key; //unused + (void)keylen; //unused + switch (ctx->alg) + { + case SHA1_DIGEST_TYPE: + digestLen = CC_SHA1_DIGEST_LENGTH; + CC_SHA1_Final(digest, (CC_SHA1_CTX *)ctx->context); + break; + case SHA256_DIGEST_TYPE: + digestLen = CC_SHA256_DIGEST_LENGTH; + CC_SHA256_Final(digest, (CC_SHA256_CTX *)ctx->context); + break; + default: + LogMsg("sha_verify: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } + if (dlen != digestLen) + { + LogMsg("sha_verify(Alg %d): digest len mismatch len %u, expected %u", ctx->alg, (unsigned int)dlen, (unsigned int)digestLen); + return mStatus_BadParamErr; + } + if (!memcmp(digest, digestIn, digestLen)) + return mStatus_NoError; + else + return mStatus_NoAuth; +} + +mDNSlocal mStatus rsa_sha_create(AlgContext *ctx) +{ + mDNSu8 *ptr; + switch (ctx->alg) + { + case CRYPTO_RSA_NSEC3_SHA1: + case CRYPTO_RSA_SHA1: + ptr = mDNSPlatformMemAllocate(sizeof(CC_SHA1_CTX)); + if (!ptr) return mStatus_NoMemoryErr; + CC_SHA1_Init((CC_SHA1_CTX *)ptr); + break; + case CRYPTO_RSA_SHA256: + ptr = mDNSPlatformMemAllocate(sizeof(CC_SHA256_CTX)); + if (!ptr) return mStatus_NoMemoryErr; + CC_SHA256_Init((CC_SHA256_CTX *)ptr); + break; + case CRYPTO_RSA_SHA512: + ptr = mDNSPlatformMemAllocate(sizeof(CC_SHA512_CTX)); + if (!ptr) return mStatus_NoMemoryErr; + CC_SHA512_Init((CC_SHA512_CTX *)ptr); + break; + default: + LogMsg("rsa_sha_create: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } + ctx->context = ptr; + return mStatus_NoError; +} + +mDNSlocal mStatus rsa_sha_destroy(AlgContext *ctx) +{ + mDNSPlatformMemFree(ctx->context); + return mStatus_NoError; +} + +mDNSlocal mDNSu32 rsa_sha_len(AlgContext *ctx) +{ + switch (ctx->alg) + { + case CRYPTO_RSA_NSEC3_SHA1: + case CRYPTO_RSA_SHA1: + return CC_SHA1_DIGEST_LENGTH; + case CRYPTO_RSA_SHA256: + return CC_SHA256_DIGEST_LENGTH; + case CRYPTO_RSA_SHA512: + return CC_SHA512_DIGEST_LENGTH; + default: + LogMsg("rsa_sha_len: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } +} + +mDNSlocal mStatus rsa_sha_add(AlgContext *ctx, void *data, mDNSu32 len) +{ + switch (ctx->alg) + { + case CRYPTO_RSA_NSEC3_SHA1: + case CRYPTO_RSA_SHA1: + CC_SHA1_Update((CC_SHA1_CTX *)ctx->context, data, len); + break; + case CRYPTO_RSA_SHA256: + CC_SHA256_Update((CC_SHA256_CTX *)ctx->context, data, len); + break; + case CRYPTO_RSA_SHA512: + CC_SHA512_Update((CC_SHA512_CTX *)ctx->context, data, len); + break; + default: + LogMsg("rsa_sha_add: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } + return mStatus_NoError; +} + +#if TARGET_OS_IPHONE +mDNSlocal SecKeyRef rfc3110_import(const mDNSu8 *data, const mDNSu32 len) +{ + static const int max_key_bytes = 4096 / 8; // max DNSSEC supported modulus is 4096 bits + static const int max_exp_bytes = 3; // DNSSEC supports 1 or 3 bytes for exponent + static const int asn1_cmd_bytes = 3; // since there is an ASN1 SEQ and two INTs + //static const int asn1_max_len_bytes = asn1_cmd_bytes * 3; // capped at 3 due to max payload size + static const int asn1_max_len_bytes = 3 * 3; // capped at 3 due to max payload size + unsigned char asn1[max_key_bytes + 1 + max_exp_bytes + asn1_cmd_bytes + asn1_max_len_bytes]; // +1 is for leading 0 for non negative asn1 number + const mDNSu8 *modulus; + unsigned int modulus_length; + unsigned int exp_length; + mDNSu32 index = 0; + mDNSu32 asn1_length = 0; + unsigned int i; + + // Validate Input + if (!data) + return NULL; + + // we have to have at least 1 byte for the length + if (len < 1) + return NULL; + + // Parse Modulus and Exponent + exp_length = data[0]; + + // we have to have at least len byte + size of exponent + if (len < 1+exp_length) + return NULL; + + // -1 is for the exp_length byte + modulus_length = len - 1 - exp_length; + + // rfc3110 limits modulus to 4096 bits + if (modulus_length > 512) + return NULL; + + if (modulus_length < 1) + return NULL; + + // add 1 to modulus length for pre-ceding 0 t make ASN1 value non-negative + ++modulus_length; + + // 1 is to skip exp_length byte + modulus = &data[1+exp_length]; + + // 2 bytes for commands since first doesn't count + // 2 bytes for min 1 byte length field + asn1_length = modulus_length + exp_length + 2 + 2; + + // account for modulus length causing INT length field to grow + if (modulus_length > 0xFF) + asn1_length += 2; + else if (modulus_length >= 128) + ++asn1_length; + + // Construct ASN1 formatted public key + // Write ASN1 SEQ byte + asn1[index++] = 0x30; + + // Write ASN1 length for SEQ + if (asn1_length < 128) + { + asn1[index++] = asn1_length & 0xFF; + } + else + { + asn1[index++] = (0x80 | ((asn1_length & 0xFF00) ? 2 : 1)); + if (asn1_length & 0xFF00) + asn1[index++] = (asn1_length & 0xFF00) >> 8; + asn1[index++] = asn1_length & 0xFF; + } + + // Write ASN1 INT for modulus + asn1[index++] = 0x02; + // Write ASN1 length for INT + if (modulus_length < 128) + { + asn1[index++] = asn1_length & 0xFF; + } + else + { + asn1[index++] = 0x80 | ((modulus_length & 0xFF00) ? 2 : 1); + if (modulus_length & 0xFF00) + asn1[index++] = (modulus_length & 0xFF00) >> 8; + asn1[index++] = modulus_length & 0xFF; + } + + // Write preceding 0 so our integer isn't negative + asn1[index++] = 0x00; + // Write actual modulus (-1 for preceding 0) + memcpy(&asn1[index], (void *)modulus, modulus_length-1); + index += modulus_length-1; + + // Write ASN1 INT for exponent + asn1[index++] = 0x02; + // Write ASN1 length for INT + asn1[index++] = exp_length & 0xFF; + // Write exponent bytes + for (i = 1; i <= exp_length; i++) + asn1[index++] = data[i]; + + // index contains bytes written, use it for length + return SecKeyCreateRSAPublicKey(NULL, asn1, index, kSecKeyEncodingPkcs1); +} + +mDNSlocal mStatus rsa_sha_verify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen) +{ + SecKeyRef keyref; + OSStatus result; + mDNSu8 digest[CC_SHA512_DIGEST_LENGTH]; + int digestlen; + + switch (ctx->alg) + { + case CRYPTO_RSA_NSEC3_SHA1: + case CRYPTO_RSA_SHA1: + digestlen = CC_SHA1_DIGEST_LENGTH; + CC_SHA1_Final(digest, (CC_SHA1_CTX *)ctx->context); + break; + case CRYPTO_RSA_SHA256: + digestlen = CC_SHA256_DIGEST_LENGTH; + CC_SHA256_Final(digest, (CC_SHA256_CTX *)ctx->context); + break; + case CRYPTO_RSA_SHA512: + digestlen = CC_SHA512_DIGEST_LENGTH; + CC_SHA512_Final(digest, (CC_SHA512_CTX *)ctx->context); + break; + default: + LogMsg("rsa_sha_verify: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } + + keyref = rfc3110_import(key, keylen); + if (!key) + { + LogMsg("rsa_sha_verify: Error decoding rfc3110 key data"); + return mStatus_NoMemoryErr; + } + // TBD: Use the right algorithm when the support becomes available + result = SecKeyRawVerify(keyref, kSecPaddingPKCS1SHA1, digest, digestlen, signature, siglen); + LogMsg("rsa_sha_verify: result: %s (%ld)", result == noErr ? "PASS" : "FAIL", result); + if (result == noErr) + return mStatus_NoError; + else + return mStatus_BadParamErr; +} +#else +mDNSlocal mStatus rsa_sha_verify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen) +{ + (void)ctx; + (void)key; + (void)keylen; + (void)signature; + (void)siglen; + return mStatus_NoError; +} +#endif + +AlgFuncs sha_funcs = {sha_create, sha_destroy, sha_len, sha_add, sha_verify, mDNSNULL}; +AlgFuncs rsa_sha_funcs = {rsa_sha_create, rsa_sha_destroy, rsa_sha_len, rsa_sha_add, rsa_sha_verify, mDNSNULL}; +AlgFuncs enc_funcs = {enc_create, enc_destroy, mDNSNULL, enc_add, mDNSNULL, enc_encode}; + +mDNSexport mStatus DNSSECCryptoInit(mDNS *const m) +{ + mStatus result; + + result = DigestAlgInit(SHA1_DIGEST_TYPE, &sha_funcs); + if (result != mStatus_NoError) + return result; + result = DigestAlgInit(SHA256_DIGEST_TYPE, &sha_funcs); + if (result != mStatus_NoError) + return result; + result = CryptoAlgInit(CRYPTO_RSA_SHA1, &rsa_sha_funcs); + if (result != mStatus_NoError) + return result; + result = CryptoAlgInit(CRYPTO_RSA_NSEC3_SHA1, &rsa_sha_funcs); + if (result != mStatus_NoError) + return result; + result = CryptoAlgInit(CRYPTO_RSA_SHA256, &rsa_sha_funcs); + if (result != mStatus_NoError) + return result; + result = CryptoAlgInit(CRYPTO_RSA_SHA512, &rsa_sha_funcs); + if (result != mStatus_NoError) + return result; + result = EncAlgInit(ENC_BASE32, &enc_funcs); + if (result != mStatus_NoError) + return result; + result = EncAlgInit(ENC_BASE64, &enc_funcs); + if (result != mStatus_NoError) + return result; + + m->TrustAnchors = mDNSNULL; + m->notifyToken = 0; + + return mStatus_NoError; +} diff --git a/mDNSMacOSX/safe_vproc.h b/mDNSMacOSX/CryptoSupport.h similarity index 72% rename from mDNSMacOSX/safe_vproc.h rename to mDNSMacOSX/CryptoSupport.h index 49ec219..9c0fd07 100644 --- a/mDNSMacOSX/safe_vproc.h +++ b/mDNSMacOSX/CryptoSupport.h @@ -1,24 +1,22 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2009 Apple Inc. All rights reserved. + * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ +#ifndef __CRYPTO_SUPPORT_H +#define __CRYPTO_SUPPORT_H -#ifndef __SAFE_VPROC_H -#define __SAFE_VPROC_H - -extern void safe_vproc_transaction_begin(void); -extern void safe_vproc_transaction_end(void); +extern mStatus DNSSECCryptoInit(mDNS *const m); -#endif /* __SAFE_VPROC_H */ +#endif // __CRYPTO_SUPPORT_H diff --git a/mDNSMacOSX/DNSServiceDiscovery.c b/mDNSMacOSX/DNSServiceDiscovery.c index 1dea7f9..e629732 100644 --- a/mDNSMacOSX/DNSServiceDiscovery.c +++ b/mDNSMacOSX/DNSServiceDiscovery.c @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -37,8 +37,8 @@ #include extern boolean_t DNSServiceDiscoveryReply_server( - mach_msg_header_t *InHeadP, - mach_msg_header_t *OutHeadP); + mach_msg_header_t *InHeadP, + mach_msg_header_t *OutHeadP); extern kern_return_t DNSServiceBrowserCreate_rpc @@ -111,28 +111,28 @@ kern_return_t DNSServiceRegistrationRemoveRecord_rpc ); struct a_requests { - struct a_requests *next; - mach_port_t client_port; + struct a_requests *next; + mach_port_t client_port; union { - DNSServiceBrowserReply browserCallback; - DNSServiceDomainEnumerationReply enumCallback; - DNSServiceRegistrationReply regCallback; - DNSServiceResolverReply resolveCallback; + DNSServiceBrowserReply browserCallback; + DNSServiceDomainEnumerationReply enumCallback; + DNSServiceRegistrationReply regCallback; + DNSServiceResolverReply resolveCallback; } callout; - void *context; + void *context; }; -static struct a_requests *a_requests = NULL; -static pthread_mutex_t a_requests_lock = PTHREAD_MUTEX_INITIALIZER; +static struct a_requests *a_requests = NULL; +static pthread_mutex_t a_requests_lock = PTHREAD_MUTEX_INITIALIZER; typedef struct _dns_service_discovery_t { - mach_port_t port; + mach_port_t port; } dns_service_discovery_t; static mach_port_t DNSServiceDiscoveryLookupServer(void) { - static mach_port_t sndPort = MACH_PORT_NULL; - kern_return_t result; + static mach_port_t sndPort = MACH_PORT_NULL; + kern_return_t result; if (sndPort != MACH_PORT_NULL) { return sndPort; @@ -152,7 +152,7 @@ static void _increaseQueueLengthOnPort(mach_port_t port) { mach_port_limits_t qlimits; kern_return_t result; - + qlimits.mpl_qlimit = 16; result = mach_port_set_attributes(mach_task_self(), port, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&qlimits, MACH_PORT_LIMITS_INFO_COUNT); @@ -167,8 +167,8 @@ dns_service_discovery_ref DNSServiceBrowserCreate (const char *regtype, const ch mach_port_t clientPort; kern_return_t result; dns_service_discovery_ref return_t; - struct a_requests *request; - + struct a_requests *request; + if (!serverPort) { return NULL; } @@ -206,7 +206,7 @@ dns_service_discovery_ref DNSServiceBrowserCreate (const char *regtype, const ch request->next = a_requests; a_requests = request; pthread_mutex_unlock(&a_requests_lock); - + return return_t; } @@ -218,7 +218,7 @@ dns_service_discovery_ref DNSServiceDomainEnumerationCreate (int registrationDom mach_port_t clientPort; kern_return_t result; dns_service_discovery_ref return_t; - struct a_requests *request; + struct a_requests *request; if (!serverPort) { return NULL; @@ -252,7 +252,7 @@ dns_service_discovery_ref DNSServiceDomainEnumerationCreate (int registrationDom free(request); return NULL; } - + pthread_mutex_lock(&a_requests_lock); request->next = a_requests; a_requests = request; @@ -265,22 +265,22 @@ dns_service_discovery_ref DNSServiceDomainEnumerationCreate (int registrationDom /* Service Registration */ dns_service_discovery_ref DNSServiceRegistrationCreate -(const char *name, const char *regtype, const char *domain, uint16_t port, const char *txtRecord, DNSServiceRegistrationReply callBack, void *context) + (const char *name, const char *regtype, const char *domain, uint16_t port, const char *txtRecord, DNSServiceRegistrationReply callBack, void *context) { mach_port_t serverPort = DNSServiceDiscoveryLookupServer(); mach_port_t clientPort; - kern_return_t result; + kern_return_t result; dns_service_discovery_ref return_t; - struct a_requests *request; + struct a_requests *request; IPPort IpPort; - char *portptr = (char *)&port; - + char *portptr = (char *)&port; + if (!serverPort) { return NULL; } if (!txtRecord) { - txtRecord = ""; + txtRecord = ""; } result = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &clientPort); @@ -304,13 +304,13 @@ dns_service_discovery_ref DNSServiceRegistrationCreate request->context = context; request->callout.regCallback = callBack; - // 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 - IpPort.bytes[0] = 0; - IpPort.bytes[1] = 0; - IpPort.bytes[2] = portptr[0]; - IpPort.bytes[3] = portptr[1]; + // 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 + IpPort.bytes[0] = 0; + IpPort.bytes[1] = 0; + IpPort.bytes[2] = portptr[0]; + IpPort.bytes[3] = portptr[1]; result = DNSServiceRegistrationCreate_rpc(serverPort, clientPort, (char *)name, (char *)regtype, (char *)domain, IpPort, (char *)txtRecord); @@ -334,9 +334,9 @@ dns_service_discovery_ref DNSServiceResolverResolve(const char *name, const char { mach_port_t serverPort = DNSServiceDiscoveryLookupServer(); mach_port_t clientPort; - kern_return_t result; + kern_return_t result; dns_service_discovery_ref return_t; - struct a_requests *request; + struct a_requests *request; if (!serverPort) { return NULL; @@ -395,7 +395,7 @@ DNSRecordReference DNSServiceRegistrationAddRecord(dns_service_discovery_ref ref if (result != KERN_SUCCESS) { printf("The result of the registration was not successful. Error %d, result %s\n", result, mach_error_string(result)); } - + return reference; } @@ -453,10 +453,10 @@ DNSServiceRegistrationReplyErrorType DNSServiceRegistrationRemoveRecord(dns_serv void DNSServiceDiscovery_handleReply(void *replyMsg) { - unsigned long result = 0xFFFFFFFF; - mach_msg_header_t * msgSendBufPtr; + unsigned long result = 0xFFFFFFFF; + mach_msg_header_t * msgSendBufPtr; mach_msg_header_t * receivedMessage; - unsigned msgSendBufLength; + unsigned msgSendBufLength; msgSendBufLength = internal_DNSServiceDiscoveryReply_subsystem.maxsize; msgSendBufPtr = (mach_msg_header_t *) malloc(msgSendBufLength); @@ -478,7 +478,7 @@ mach_port_t DNSServiceDiscoveryMachPort(dns_service_discovery_ref dnsServiceDisc void DNSServiceDiscoveryDeallocate(dns_service_discovery_ref dnsServiceDiscovery) { - struct a_requests *request0, *request; + struct a_requests *request0, *request; mach_port_t reply = dnsServiceDiscovery->port; if (dnsServiceDiscovery->port) { @@ -504,7 +504,7 @@ void DNSServiceDiscoveryDeallocate(dns_service_discovery_ref dnsServiceDiscovery pthread_mutex_unlock(&a_requests_lock); free(request); - + mach_port_destroy(mach_task_self(), dnsServiceDiscovery->port); free(dnsServiceDiscovery); @@ -522,7 +522,7 @@ kern_return_t internal_DNSServiceDomainEnumerationReply_rpc int flags ) { - struct a_requests *request; + struct a_requests *request; void *requestContext = NULL; DNSServiceDomainEnumerationReply callback = NULL; @@ -544,7 +544,7 @@ kern_return_t internal_DNSServiceDomainEnumerationReply_rpc if (request != NULL) { (callback)(resultType, replyDomain, flags, requestContext); } - + return KERN_SUCCESS; } @@ -559,7 +559,7 @@ kern_return_t internal_DNSServiceBrowserReply_rpc int flags ) { - struct a_requests *request; + struct a_requests *request; void *requestContext = NULL; DNSServiceBrowserReply callback = NULL; @@ -592,7 +592,7 @@ kern_return_t internal_DNSServiceRegistrationReply_rpc int resultType ) { - struct a_requests *request; + struct a_requests *request; void *requestContext = NULL; DNSServiceRegistrationReply callback = NULL; @@ -628,7 +628,7 @@ kern_return_t internal_DNSServiceResolverReply_rpc { struct sockaddr *interface_storage = NULL; struct sockaddr *address_storage = NULL; - struct a_requests *request; + struct a_requests *request; void *requestContext = NULL; DNSServiceResolverReply callback = NULL; @@ -669,6 +669,6 @@ kern_return_t internal_DNSServiceResolverReply_rpc if (address) { free(address_storage); } - + return KERN_SUCCESS; } diff --git a/mDNSMacOSX/DNSServiceDiscovery.h b/mDNSMacOSX/DNSServiceDiscovery.h index 202d158..ae73647 100644 --- a/mDNSMacOSX/DNSServiceDiscovery.h +++ b/mDNSMacOSX/DNSServiceDiscovery.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,28 +17,28 @@ /*! @header DNS Service Discovery (Deprecated Mach-based API) * - * @discussion This section describes the functions, callbacks, and data structures that + * @discussion This section describes the functions, callbacks, and data structures that * make up the DNS Service Discovery API. * - * The DNS Service Discovery API is part of Bonjour, Apple's implementation of + * The DNS Service Discovery API is part of Bonjour, Apple's implementation of * zero-configuration networking (ZEROCONF). * - * Bonjour allows you to register a network service, such as a - * printer or file server, so that it can be found by name or browsed - * for by service type and domain. Using Bonjour, applications can - * discover what services are available on the network, along with - * all necessary access information-such as name, IP address, and port + * Bonjour allows you to register a network service, such as a + * printer or file server, so that it can be found by name or browsed + * for by service type and domain. Using Bonjour, applications can + * discover what services are available on the network, along with + * all necessary access information-such as name, IP address, and port * number-for a given service. * - * In effect, Bonjour combines the functions of a local DNS server - * and AppleTalk. Bonjour allows applications to provide user-friendly printer - * and server browsing, among other things, over standard IP networks. - * This behavior is a result of combining protocols such as multicast and DNS + * In effect, Bonjour combines the functions of a local DNS server + * and AppleTalk. Bonjour allows applications to provide user-friendly printer + * and server browsing, among other things, over standard IP networks. + * This behavior is a result of combining protocols such as multicast and DNS * to add new functionality to the network (such as multicast DNS). * - * Bonjour gives applications easy access to services over local IP - * networks without requiring the service or the application to support - * an AppleTalk or a Netbeui stack, and without requiring a DNS server + * Bonjour gives applications easy access to services over local IP + * networks without requiring the service or the application to support + * an AppleTalk or a Netbeui stack, and without requiring a DNS server * for the local network. * * Note that this API was deprecated in Mac OS X 10.3, and replaced @@ -66,8 +66,8 @@ typedef struct _dns_service_discovery_t * dns_service_discovery_ref; /* possible reply flags values */ enum { - kDNSServiceDiscoveryNoFlags = 0, - kDNSServiceDiscoveryMoreRepliesImmediately = 1 << 0, + kDNSServiceDiscoveryNoFlags = 0, + kDNSServiceDiscoveryMoreRepliesImmediately = 1 << 0, }; @@ -76,8 +76,8 @@ typedef enum { kDNSServiceDiscoveryWaiting = 1, kDNSServiceDiscoveryNoError = 0, - // mDNS Error codes are in the range - // FFFE FF00 (-65792) to FFFE FFFF (-65537) + // mDNS Error codes are in the range + // FFFE FF00 (-65792) to FFFE FFFF (-65537) kDNSServiceDiscoveryUnknownErr = -65537, // 0xFFFE FFFF kDNSServiceDiscoveryNoSuchNameErr = -65538, kDNSServiceDiscoveryNoMemoryErr = -65539, @@ -98,24 +98,24 @@ typedef uint32_t DNSRecordReference; /*! -@function DNSServiceResolver_handleReply - @discussion This function should be called with the Mach message sent - to the port returned by the call to DNSServiceResolverResolve. - The reply message will be interpreted and will result in a - call to the specified callout function. - @param replyMsg The Mach message. + @function DNSServiceResolver_handleReply + @discussion This function should be called with the Mach message sent + to the port returned by the call to DNSServiceResolverResolve. + The reply message will be interpreted and will result in a + call to the specified callout function. + @param replyMsg The Mach message. */ void DNSServiceDiscovery_handleReply(void *replyMsg); /* Service Registration */ -typedef void (*DNSServiceRegistrationReply) ( - DNSServiceRegistrationReplyErrorType errorCode, - void *context -); +typedef void (*DNSServiceRegistrationReply)( + DNSServiceRegistrationReplyErrorType errorCode, + void *context + ); /*! -@function DNSServiceRegistrationCreate + @function DNSServiceRegistrationCreate @discussion Register a named service with DNS Service Discovery @param name The name of this service instance (e.g. "Steve's Printer") @param regtype The service type (e.g. "_printer._tcp." -- see @@ -126,16 +126,16 @@ typedef void (*DNSServiceRegistrationReply) ( @param callBack The DNSServiceRegistrationReply function to be called @param context A user specified context which will be passed to the callout function. @result A dns_registration_t -*/ + */ dns_service_discovery_ref DNSServiceRegistrationCreate ( - const char *name, - const char *regtype, - const char *domain, - uint16_t port, - const char *txtRecord, + const char *name, + const char *regtype, + const char *domain, + uint16_t port, + const char *txtRecord, DNSServiceRegistrationReply callBack, - void *context + void *context ) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3; /***************************************************************************/ @@ -143,9 +143,9 @@ dns_service_discovery_ref DNSServiceRegistrationCreate typedef enum { - DNSServiceDomainEnumerationReplyAddDomain, // Domain found - DNSServiceDomainEnumerationReplyAddDomainDefault, // Domain found (and should be selected by default) - DNSServiceDomainEnumerationReplyRemoveDomain, // Domain has been removed from network + DNSServiceDomainEnumerationReplyAddDomain, // Domain found + DNSServiceDomainEnumerationReplyAddDomainDefault, // Domain found (and should be selected by default) + DNSServiceDomainEnumerationReplyRemoveDomain, // Domain has been removed from network } DNSServiceDomainEnumerationReplyResultType; typedef enum @@ -154,12 +154,12 @@ typedef enum DNSServiceDiscoverReplyFlagsMoreComing, } DNSServiceDiscoveryReplyFlags; -typedef void (*DNSServiceDomainEnumerationReply) ( - DNSServiceDomainEnumerationReplyResultType resultType, // One of DNSServiceDomainEnumerationReplyResultType - const char *replyDomain, - DNSServiceDiscoveryReplyFlags flags, // DNS Service Discovery reply flags information - void *context -); +typedef void (*DNSServiceDomainEnumerationReply)( + DNSServiceDomainEnumerationReplyResultType resultType, // One of DNSServiceDomainEnumerationReplyResultType + const char *replyDomain, + DNSServiceDiscoveryReplyFlags flags, // DNS Service Discovery reply flags information + void *context + ); /*! @function DNSServiceDomainEnumerationCreate @@ -172,12 +172,12 @@ typedef void (*DNSServiceDomainEnumerationReply) ( @param callBack The function to be called when domains are found or removed @param context A user specified context which will be passed to the callout function. @result A dns_registration_t -*/ + */ dns_service_discovery_ref DNSServiceDomainEnumerationCreate ( - int registrationDomains, - DNSServiceDomainEnumerationReply callBack, - void *context + int registrationDomains, + DNSServiceDomainEnumerationReply callBack, + void *context ) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3; /***************************************************************************/ @@ -185,18 +185,18 @@ dns_service_discovery_ref DNSServiceDomainEnumerationCreate typedef enum { - DNSServiceBrowserReplyAddInstance, // Instance of service found - DNSServiceBrowserReplyRemoveInstance // Instance has been removed from network + DNSServiceBrowserReplyAddInstance, // Instance of service found + DNSServiceBrowserReplyRemoveInstance // Instance has been removed from network } DNSServiceBrowserReplyResultType; -typedef void (*DNSServiceBrowserReply) ( - DNSServiceBrowserReplyResultType resultType, // One of DNSServiceBrowserReplyResultType - const char *replyName, - const char *replyType, - const char *replyDomain, - DNSServiceDiscoveryReplyFlags flags, // DNS Service Discovery reply flags information - void *context -); +typedef void (*DNSServiceBrowserReply)( + DNSServiceBrowserReplyResultType resultType, // One of DNSServiceBrowserReplyResultType + const char *replyName, + const char *replyType, + const char *replyDomain, + DNSServiceDiscoveryReplyFlags flags, // DNS Service Discovery reply flags information + void *context + ); /*! @function DNSServiceBrowserCreate @@ -206,28 +206,28 @@ typedef void (*DNSServiceBrowserReply) ( @param callBack The function to be called when service instances are found or removed @param context A user specified context which will be passed to the callout function. @result A dns_registration_t -*/ + */ dns_service_discovery_ref DNSServiceBrowserCreate ( - const char *regtype, - const char *domain, - DNSServiceBrowserReply callBack, - void *context + const char *regtype, + const char *domain, + DNSServiceBrowserReply callBack, + void *context ) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3; /***************************************************************************/ /* Resolver requests */ -typedef void (*DNSServiceResolverReply) ( - struct sockaddr *interface, // Needed for scoped addresses like link-local - struct sockaddr *address, - const char *txtRecord, - DNSServiceDiscoveryReplyFlags flags, // DNS Service Discovery reply flags information - void *context -); +typedef void (*DNSServiceResolverReply)( + struct sockaddr *interface, // Needed for scoped addresses like link-local + struct sockaddr *address, + const char *txtRecord, + DNSServiceDiscoveryReplyFlags flags, // DNS Service Discovery reply flags information + void *context + ); /*! -@function DNSServiceResolverResolve + @function DNSServiceResolverResolve @discussion Resolved a named instance of a service to its address, port, and (optionally) other demultiplexing information contained in the TXT record. @param name The name of the service instance @@ -237,15 +237,15 @@ typedef void (*DNSServiceResolverReply) ( address has been resolved. @param context A user specified context which will be passed to the callout function. @result A dns_registration_t -*/ + */ dns_service_discovery_ref DNSServiceResolverResolve ( - const char *name, - const char *regtype, - const char *domain, + const char *name, + const char *regtype, + const char *domain, DNSServiceResolverReply callBack, - void *context + void *context ) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3; /***************************************************************************/ @@ -260,7 +260,7 @@ dns_service_discovery_ref DNSServiceResolverResolve function. A NULL value indicates that no address was specified or some other error occurred which prevented the resolution from being started. -*/ + */ mach_port_t DNSServiceDiscoveryMachPort(dns_service_discovery_ref dnsServiceDiscovery) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3; /*! @@ -268,7 +268,7 @@ mach_port_t DNSServiceDiscoveryMachPort(dns_service_discovery_ref dnsServiceDisc @discussion Deallocates the DNS Service Discovery type / closes the connection to the server @param dnsServiceDiscovery A dns_service_discovery_ref as returned from a creation or enumeration call @result void -*/ + */ void DNSServiceDiscoveryDeallocate(dns_service_discovery_ref dnsServiceDiscovery) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3; /***************************************************************************/ @@ -284,7 +284,7 @@ void DNSServiceDiscoveryDeallocate(dns_service_discovery_ref dnsServiceDiscovery @param rdata Opaque binary Resource Record data, up to 64 kB. @param ttl time to live for the added record. @result DNSRecordReference An opaque reference that can be passed to the update and remove record calls. If an error occurs, this value will be zero or negative -*/ + */ DNSRecordReference DNSServiceRegistrationAddRecord(dns_service_discovery_ref dnsServiceDiscovery, uint16_t rrtype, uint16_t rdlen, const char *rdata, uint32_t ttl) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3; /*! @@ -296,7 +296,7 @@ DNSRecordReference DNSServiceRegistrationAddRecord(dns_service_discovery_ref dns @param rdata Opaque binary Resource Record data, up to 64 kB. @param ttl time to live for the updated record. @result DNSServiceRegistrationReplyErrorType If an error occurs, this value will be non zero -*/ + */ DNSServiceRegistrationReplyErrorType DNSServiceRegistrationUpdateRecord(dns_service_discovery_ref ref, DNSRecordReference reference, uint16_t rdlen, const char *rdata, uint32_t ttl) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3; /*! @@ -305,10 +305,10 @@ DNSServiceRegistrationReplyErrorType DNSServiceRegistrationUpdateRecord(dns_serv @param dnsServiceDiscovery A dns_service_discovery_ref as returned from a DNSServiceRegistrationCreate call @param dnsRecordReference A dnsRecordReference as returned from a DNSServiceRegistrationAddRecord call @result DNSServiceRegistrationReplyErrorType If an error occurs, this value will be non zero -*/ + */ DNSServiceRegistrationReplyErrorType DNSServiceRegistrationRemoveRecord(dns_service_discovery_ref ref, DNSRecordReference reference) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3; __END_DECLS -#endif /* __DNS_SERVICE_DISCOVERY_H */ +#endif /* __DNS_SERVICE_DISCOVERY_H */ diff --git a/mDNSMacOSX/DNSServiceDiscoveryDefines.h b/mDNSMacOSX/DNSServiceDiscoveryDefines.h index 62b0296..cfad0a4 100644 --- a/mDNSMacOSX/DNSServiceDiscoveryDefines.h +++ b/mDNSMacOSX/DNSServiceDiscoveryDefines.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,10 +22,10 @@ #define DNS_SERVICE_DISCOVERY_SERVER "com.apple.mDNSResponder" -typedef char DNSCString[1024]; -typedef char sockaddr_t[128]; +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 */ +#endif /* __DNS_SERVICE_DISCOVERY_DEFINES_H */ diff --git a/mDNSMacOSX/LaunchDaemonInfo.helper.plist b/mDNSMacOSX/LaunchDaemonInfo.helper.plist index edf5d06..9a686d8 100644 --- a/mDNSMacOSX/LaunchDaemonInfo.helper.plist +++ b/mDNSMacOSX/LaunchDaemonInfo.helper.plist @@ -17,5 +17,7 @@ EnableTransactions + BeginTransactionAtShutdown + diff --git a/mDNSMacOSX/LaunchDaemonInfo.plist b/mDNSMacOSX/LaunchDaemonInfo.plist index c81682f..8966dc0 100644 --- a/mDNSMacOSX/LaunchDaemonInfo.plist +++ b/mDNSMacOSX/LaunchDaemonInfo.plist @@ -6,6 +6,8 @@ com.apple.mDNSResponder OnDemand + InitGroups + UserName _mdnsresponder GroupName @@ -34,5 +36,9 @@ EnableTransactions + BeginTransactionAtShutdown + + POSIXSpawnType + Interactive diff --git a/mDNSMacOSX/LegacyNATTraversal.c b/mDNSMacOSX/LegacyNATTraversal.c index 115719e..8e03d65 100644 --- a/mDNSMacOSX/LegacyNATTraversal.c +++ b/mDNSMacOSX/LegacyNATTraversal.c @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,47 +17,48 @@ #ifdef _LEGACY_NAT_TRAVERSAL_ -#include "stdlib.h" // For strtol() -#include "string.h" // For strlcpy(), For strncpy(), strncasecmp() +#include "stdlib.h" // For strtol() +#include "string.h" // For strlcpy(), For strncpy(), strncasecmp() +#include "assert.h" // For assert() #if defined( WIN32 ) -# include -# include -# define strcasecmp _stricmp -# define strncasecmp _strnicmp -# define mDNSASLLog( UUID, SUBDOMAIN, RESULT, SIGNATURE, FORMAT, ... ) ; +# include +# include +# define strcasecmp _stricmp +# define strncasecmp _strnicmp +# define mDNSASLLog( UUID, SUBDOMAIN, RESULT, SIGNATURE, FORMAT, ... ) ; static int inet_pton( int family, const char * addr, void * dst ) - { - struct sockaddr_storage ss; - int sslen = sizeof( ss ); - - ZeroMemory( &ss, sizeof( ss ) ); - ss.ss_family = (ADDRESS_FAMILY)family; - - if ( WSAStringToAddressA( (LPSTR)addr, family, NULL, ( struct sockaddr* ) &ss, &sslen ) == 0 ) - { - if ( family == AF_INET ) { memcpy( dst, &( ( struct sockaddr_in* ) &ss)->sin_addr, sizeof( IN_ADDR ) ); return 1; } - else if ( family == AF_INET6 ) { memcpy( dst, &( ( struct sockaddr_in6* ) &ss)->sin6_addr, sizeof( IN6_ADDR ) ); return 1; } - else return 0; - } - else return 0; - } +{ + struct sockaddr_storage ss; + int sslen = sizeof( ss ); + + ZeroMemory( &ss, sizeof( ss ) ); + ss.ss_family = (ADDRESS_FAMILY)family; + + if ( WSAStringToAddressA( (LPSTR)addr, family, NULL, ( struct sockaddr* ) &ss, &sslen ) == 0 ) + { + if ( family == AF_INET ) { memcpy( dst, &( ( struct sockaddr_in* ) &ss)->sin_addr, sizeof( IN_ADDR ) ); return 1; } + else if ( family == AF_INET6 ) { memcpy( dst, &( ( struct sockaddr_in6* ) &ss)->sin6_addr, sizeof( IN6_ADDR ) ); return 1; } + else return 0; + } + else return 0; +} #else -# include // For inet_pton() +# include // For inet_pton() #endif #include "mDNSEmbeddedAPI.h" -#include "uDNS.h" // For natTraversalHandleAddressReply() etc. +#include "uDNS.h" // For natTraversalHandleAddressReply() etc. // used to format SOAP port mapping arguments typedef struct Property_struct - { - char *name; - char *type; - char *value; - } Property; +{ + char *name; + char *type; + char *value; +} Property; // All of the text parsing in this file is intentionally transparent so that we know exactly // what's being done to the text, with an eye towards preventing security problems. @@ -79,828 +80,854 @@ mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n); // Note that this function assumes src is already NULL terminated mDNSlocal void AllocAndCopy(mDNSu8 **const dst, const mDNSu8 *const src) - { - if (src == mDNSNULL) return; - if ((*dst = mDNSPlatformMemAllocate((mDNSu32)strlen((char*)src) + 1)) == mDNSNULL) - { LogMsg("AllocAndCopy: can't allocate string"); return; } - strcpy((char*)*dst, (char*)src); - } +{ + if (src == mDNSNULL) return; + if ((*dst = mDNSPlatformMemAllocate((mDNSu32)strlen((char*)src) + 1)) == mDNSNULL) + { LogMsg("AllocAndCopy: can't allocate string"); return; } + strcpy((char*)*dst, (char*)src); +} // This function does a simple parse of an HTTP URL that may include a hostname, port, and path // If found in the URL, addressAndPort and path out params will point to newly allocated space (and will leak if they were previously pointing at allocated space) mDNSlocal mStatus ParseHttpUrl(const mDNSu8 *ptr, const mDNSu8 *const end, mDNSu8 **const addressAndPort, mDNSIPPort *const port, mDNSu8 **const path) - { - // if the data begins with "http://", we assume there is a hostname and possibly a port number - if (end - ptr >= 7 && strncasecmp((char*)ptr, "http://", 7) == 0) - { - int i; - const mDNSu8 *stop = end; - const mDNSu8 *addrPtr = mDNSNULL; - - ptr += 7; //skip over "http://" - if (ptr >= end) { LogInfo("ParseHttpUrl: past end of buffer parsing host:port"); return mStatus_BadParamErr; } - - // find the end of the host:port - addrPtr = ptr; - for (i = 0; addrPtr && addrPtr != end; i++, addrPtr++) if (*addrPtr == '/') break; - - // allocate the buffer (len i+1 so we have space to terminate the string) - if ((*addressAndPort = mDNSPlatformMemAllocate(i+1)) == mDNSNULL) - { LogMsg("ParseHttpUrl: can't allocate address string"); return mStatus_NoMemoryErr; } - strncpy((char*)*addressAndPort, (char*)ptr, i); - (*addressAndPort)[i] = '\0'; - - // find the port number in the string, by looking backwards for the ':' - stop = ptr; // can't go back farther than the original start - ptr = addrPtr; // move ptr to the path part - - for (addrPtr--; addrPtr>stop; addrPtr--) - { - if (*addrPtr == ':') - { - addrPtr++; // skip over ':' - *port = mDNSOpaque16fromIntVal((mDNSu16)strtol((char*)addrPtr, mDNSNULL, 10)); // store it properly converted - break; - } - } - } - - // ptr should now point to the first character we haven't yet processed - // everything that remains is the path - if (path && ptr < end) - { - if ((*path = mDNSPlatformMemAllocate((mDNSu32)(end - ptr) + 1)) == mDNSNULL) - { LogMsg("ParseHttpUrl: can't mDNSPlatformMemAllocate path"); return mStatus_NoMemoryErr; } - strncpy((char*)*path, (char*)ptr, end - ptr); - (*path)[end - ptr] = '\0'; - } - - return mStatus_NoError; - } +{ + // if the data begins with "http://", we assume there is a hostname and possibly a port number + if (end - ptr >= 7 && strncasecmp((char*)ptr, "http://", 7) == 0) + { + int i; + const mDNSu8 *stop = end; + const mDNSu8 *addrPtr = mDNSNULL; + + ptr += 7; //skip over "http://" + if (ptr >= end) { LogInfo("ParseHttpUrl: past end of buffer parsing host:port"); return mStatus_BadParamErr; } + + // find the end of the host:port + addrPtr = ptr; + for (i = 0; addrPtr && addrPtr != end; i++, addrPtr++) if (*addrPtr == '/') break; + + // allocate the buffer (len i+1 so we have space to terminate the string) + if ((*addressAndPort = mDNSPlatformMemAllocate(i+1)) == mDNSNULL) + { LogMsg("ParseHttpUrl: can't allocate address string"); return mStatus_NoMemoryErr; } + strncpy((char*)*addressAndPort, (char*)ptr, i); + (*addressAndPort)[i] = '\0'; + + // find the port number in the string, by looking backwards for the ':' + stop = ptr; // can't go back farther than the original start + ptr = addrPtr; // move ptr to the path part + + for (addrPtr--; addrPtr>stop; addrPtr--) + { + if (*addrPtr == ':') + { + addrPtr++; // skip over ':' + *port = mDNSOpaque16fromIntVal((mDNSu16)strtol((char*)addrPtr, mDNSNULL, 10)); // store it properly converted + break; + } + } + } + + // ptr should now point to the first character we haven't yet processed + // everything that remains is the path + if (path && ptr < end) + { + if ((*path = mDNSPlatformMemAllocate((mDNSu32)(end - ptr) + 1)) == mDNSNULL) + { LogMsg("ParseHttpUrl: can't mDNSPlatformMemAllocate path"); return mStatus_NoMemoryErr; } + strncpy((char*)*path, (char*)ptr, end - ptr); + (*path)[end - ptr] = '\0'; + } + + return mStatus_NoError; +} enum - { - HTTPCode_NeedMoreData = -1, // No code found in stream - HTTPCode_Other = -2, // Valid code other than those below found in stream - HTTPCode_Bad = -3, - HTTPCode_200 = 200, - HTTPCode_404 = 404, - HTTPCode_500 = 500, - }; - +{ + HTTPCode_NeedMoreData = -1, // No code found in stream + HTTPCode_Other = -2, // Valid code other than those below found in stream + HTTPCode_Bad = -3, + HTTPCode_200 = 200, + HTTPCode_404 = 404, + HTTPCode_500 = 500, +}; + mDNSlocal mDNSs16 ParseHTTPResponseCode(const mDNSu8 **const data, const mDNSu8 *const end) - { - const mDNSu8 *ptr = *data; - const mDNSu8 *code; - - if (end - ptr < 5) return HTTPCode_NeedMoreData; - if (strncasecmp((char*)ptr, "HTTP/", 5) != 0) return HTTPCode_Bad; - ptr += 5; - // should we care about the HTTP protocol version? - - // look for first space, which must come before first LF - while (ptr && ptr != end) - { - if (*ptr == '\n') return HTTPCode_Bad; - if (*ptr == ' ') break; - ptr++; - } - if (ptr == end) return HTTPCode_NeedMoreData; - ptr++; - - if (end - ptr < 3) return HTTPCode_NeedMoreData; - - code = ptr; - ptr += 3; - while (ptr && ptr != end) - { - if (*ptr == '\n') break; - ptr++; - } - if (ptr == end) return HTTPCode_NeedMoreData; - *data = ++ptr; - - if (memcmp((char*)code, "200", 3) == 0) return HTTPCode_200; - if (memcmp((char*)code, "404", 3) == 0) return HTTPCode_404; - if (memcmp((char*)code, "500", 3) == 0) return HTTPCode_500; - - LogInfo("ParseHTTPResponseCode found unexpected result code: %c%c%c", code[0], code[1], code[2]); - return HTTPCode_Other; - } +{ + const mDNSu8 *ptr = *data; + const mDNSu8 *code; + + if (end - ptr < 5) return HTTPCode_NeedMoreData; + if (strncasecmp((char*)ptr, "HTTP/", 5) != 0) return HTTPCode_Bad; + ptr += 5; + // should we care about the HTTP protocol version? + + // look for first space, which must come before first LF + while (ptr && ptr != end) + { + if (*ptr == '\n') return HTTPCode_Bad; + if (*ptr == ' ') break; + ptr++; + } + if (ptr == end) return HTTPCode_NeedMoreData; + ptr++; + + if (end - ptr < 3) return HTTPCode_NeedMoreData; + + code = ptr; + ptr += 3; + while (ptr && ptr != end) + { + if (*ptr == '\n') break; + ptr++; + } + if (ptr == end) return HTTPCode_NeedMoreData; + *data = ++ptr; + + if (memcmp((char*)code, "200", 3) == 0) return HTTPCode_200; + if (memcmp((char*)code, "404", 3) == 0) return HTTPCode_404; + if (memcmp((char*)code, "500", 3) == 0) return HTTPCode_500; + + LogInfo("ParseHTTPResponseCode found unexpected result code: %c%c%c", code[0], code[1], code[2]); + return HTTPCode_Other; +} // This function parses the xml body of the device description response from the router. Basically, we look to // make sure this is a response referencing a service we care about (WANIPConnection or WANPPPConnection), // look for the "controlURL" header immediately following, and copy the addressing and URL info we need mDNSlocal void handleLNTDeviceDescriptionResponse(tcpLNTInfo *tcpInfo) - { - mDNS *m = tcpInfo->m; - const mDNSu8 *ptr = tcpInfo->Reply; - const mDNSu8 *end = tcpInfo->Reply + tcpInfo->nread; - const mDNSu8 *stop; - mDNSs16 http_result; - - if (!mDNSIPPortIsZero(m->UPnPSOAPPort)) return; // already have the info we need - - http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr - if (http_result == HTTPCode_404) LNT_ClearState(m); - if (http_result != HTTPCode_200) - { - mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "noop", "HTTP Result", "HTTP code: %d", http_result); - return; - } - - // Always reset our flag to use WANIPConnection. We'll use WANPPPConnection if we find it and don't find WANIPConnection. - m->UPnPWANPPPConnection = mDNSfalse; - - // find either service we care about - while (ptr && ptr < end) - { - if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANIPConnection:1", 17) == 0)) break; - ptr++; - } - if (ptr == end) - { - ptr = tcpInfo->Reply; - while (ptr && ptr < end) - { - if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANPPPConnection:1", 18) == 0)) - { - m->UPnPWANPPPConnection = mDNStrue; - break; - } - ptr++; - } - } - if (ptr == mDNSNULL || ptr == end) { LogInfo("handleLNTDeviceDescriptionResponse: didn't find WANIPConnection:1 or WANPPPConnection:1 string"); return; } - - // find "controlURL", starting from where we left off - while (ptr && ptr < end) - { - if ((*ptr & 0xDF) == 'C' && (strncasecmp((char*)ptr, "controlURL", 10) == 0)) break; // find the first 'c'; is this controlURL? if not, keep looking - ptr++; - } - if (ptr == mDNSNULL || ptr == end) { LogInfo("handleLNTDeviceDescriptionResponse: didn't find controlURL string"); return; } - ptr += 11; // skip over "controlURL>" - if (ptr >= end) { LogInfo("handleLNTDeviceDescriptionResponse: past end of buffer and no body!"); return; } // check ptr again in case we skipped over the end of the buffer - - // find the end of the controlURL element - for (stop = ptr; stop < end; stop++) { if (*stop == '<') { end = stop; break; } } - - // fill in default port - m->UPnPSOAPPort = m->UPnPRouterPort; - - // free string pointers and set to NULL - if (m->UPnPSOAPAddressString != mDNSNULL) - { - mDNSPlatformMemFree(m->UPnPSOAPAddressString); - m->UPnPSOAPAddressString = mDNSNULL; - } - if (m->UPnPSOAPURL != mDNSNULL) - { - mDNSPlatformMemFree(m->UPnPSOAPURL); - m->UPnPSOAPURL = mDNSNULL; - } - - if (ParseHttpUrl(ptr, end, &m->UPnPSOAPAddressString, &m->UPnPSOAPPort, &m->UPnPSOAPURL) != mStatus_NoError) return; - // the SOAPURL should look something like "/uuid:0013-108c-4b3f0000f3dc" - - if (m->UPnPSOAPAddressString == mDNSNULL) - { - ptr = tcpInfo->Reply; - while (ptr && ptr < end) - { - if ((*ptr & 0xDF) == 'U' && (strncasecmp((char*)ptr, "URLBase", 7) == 0)) break; - ptr++; - } - - if (ptr < end) // found URLBase - { - LogInfo("handleLNTDeviceDescriptionResponse: found URLBase"); - ptr += 8; // skip over "URLBase>" - // find the end of the URLBase element - for (stop = ptr; stop < end; stop++) { if (*stop == '<') { end = stop; break; } } - if (ParseHttpUrl(ptr, end, &m->UPnPSOAPAddressString, &m->UPnPSOAPPort, mDNSNULL) != mStatus_NoError) - { - LogInfo("handleLNTDeviceDescriptionResponse: failed to parse URLBase"); - } - } - - // if all else fails, use the router address string - if (m->UPnPSOAPAddressString == mDNSNULL) AllocAndCopy(&m->UPnPSOAPAddressString, m->UPnPRouterAddressString); - } - if (m->UPnPSOAPAddressString == mDNSNULL) LogMsg("handleLNTDeviceDescriptionResponse: UPnPSOAPAddressString is NULL"); - else LogInfo("handleLNTDeviceDescriptionResponse: SOAP address string [%s]", m->UPnPSOAPAddressString); - - if (m->UPnPSOAPURL == mDNSNULL) AllocAndCopy(&m->UPnPSOAPURL, m->UPnPRouterURL); - if (m->UPnPSOAPURL == mDNSNULL) LogMsg("handleLNTDeviceDescriptionResponse: UPnPSOAPURL is NULL"); - else LogInfo("handleLNTDeviceDescriptionResponse: SOAP URL [%s]", m->UPnPSOAPURL); - } +{ + mDNS *m = tcpInfo->m; + const mDNSu8 *ptr = tcpInfo->Reply; + const mDNSu8 *end = tcpInfo->Reply + tcpInfo->nread; + const mDNSu8 *stop; + mDNSs16 http_result; + + if (!mDNSIPPortIsZero(m->UPnPSOAPPort)) return; // already have the info we need + + http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr + if (http_result == HTTPCode_404) LNT_ClearState(m); + if (http_result != HTTPCode_200) + { + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "noop", "HTTP Result", "HTTP code: %d", http_result); + return; + } + + // Always reset our flag to use WANIPConnection. We'll use WANPPPConnection if we find it and don't find WANIPConnection. + m->UPnPWANPPPConnection = mDNSfalse; + + // find either service we care about + while (ptr && ptr < end) + { + if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANIPConnection:1", 17) == 0)) break; + ptr++; + } + if (ptr == end) + { + ptr = tcpInfo->Reply; + while (ptr && ptr < end) + { + if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANPPPConnection:1", 18) == 0)) + { + m->UPnPWANPPPConnection = mDNStrue; + break; + } + ptr++; + } + } + if (ptr == mDNSNULL || ptr == end) { LogInfo("handleLNTDeviceDescriptionResponse: didn't find WANIPConnection:1 or WANPPPConnection:1 string"); return; } + + // find "controlURL", starting from where we left off + while (ptr && ptr < end) + { + if ((*ptr & 0xDF) == 'C' && (strncasecmp((char*)ptr, "controlURL", 10) == 0)) break; // find the first 'c'; is this controlURL? if not, keep looking + ptr++; + } + if (ptr == mDNSNULL || ptr == end) { LogInfo("handleLNTDeviceDescriptionResponse: didn't find controlURL string"); return; } + ptr += 11; // skip over "controlURL>" + if (ptr >= end) { LogInfo("handleLNTDeviceDescriptionResponse: past end of buffer and no body!"); return; } // check ptr again in case we skipped over the end of the buffer + + // find the end of the controlURL element + for (stop = ptr; stop < end; stop++) { if (*stop == '<') { end = stop; break; } } + + // fill in default port + m->UPnPSOAPPort = m->UPnPRouterPort; + + // free string pointers and set to NULL + if (m->UPnPSOAPAddressString != mDNSNULL) + { + mDNSPlatformMemFree(m->UPnPSOAPAddressString); + m->UPnPSOAPAddressString = mDNSNULL; + } + if (m->UPnPSOAPURL != mDNSNULL) + { + mDNSPlatformMemFree(m->UPnPSOAPURL); + m->UPnPSOAPURL = mDNSNULL; + } + + if (ParseHttpUrl(ptr, end, &m->UPnPSOAPAddressString, &m->UPnPSOAPPort, &m->UPnPSOAPURL) != mStatus_NoError) return; + // the SOAPURL should look something like "/uuid:0013-108c-4b3f0000f3dc" + + if (m->UPnPSOAPAddressString == mDNSNULL) + { + ptr = tcpInfo->Reply; + while (ptr && ptr < end) + { + if ((*ptr & 0xDF) == 'U' && (strncasecmp((char*)ptr, "URLBase", 7) == 0)) break; + ptr++; + } + + if (ptr < end) // found URLBase + { + LogInfo("handleLNTDeviceDescriptionResponse: found URLBase"); + ptr += 8; // skip over "URLBase>" + // find the end of the URLBase element + for (stop = ptr; stop < end; stop++) { if (*stop == '<') { end = stop; break; } } + if (ParseHttpUrl(ptr, end, &m->UPnPSOAPAddressString, &m->UPnPSOAPPort, mDNSNULL) != mStatus_NoError) + { + LogInfo("handleLNTDeviceDescriptionResponse: failed to parse URLBase"); + } + } + + // if all else fails, use the router address string + if (m->UPnPSOAPAddressString == mDNSNULL) AllocAndCopy(&m->UPnPSOAPAddressString, m->UPnPRouterAddressString); + } + if (m->UPnPSOAPAddressString == mDNSNULL) LogMsg("handleLNTDeviceDescriptionResponse: UPnPSOAPAddressString is NULL"); + else LogInfo("handleLNTDeviceDescriptionResponse: SOAP address string [%s]", m->UPnPSOAPAddressString); + + if (m->UPnPSOAPURL == mDNSNULL) AllocAndCopy(&m->UPnPSOAPURL, m->UPnPRouterURL); + if (m->UPnPSOAPURL == mDNSNULL) LogMsg("handleLNTDeviceDescriptionResponse: UPnPSOAPURL is NULL"); + else LogInfo("handleLNTDeviceDescriptionResponse: SOAP URL [%s]", m->UPnPSOAPURL); +} mDNSlocal void handleLNTGetExternalAddressResponse(tcpLNTInfo *tcpInfo) - { - mDNS *m = tcpInfo->m; - mDNSu16 err = NATErr_None; - mDNSv4Addr ExtAddr; - const mDNSu8 *ptr = tcpInfo->Reply; - const mDNSu8 *end = tcpInfo->Reply + tcpInfo->nread; - mDNSu8 *addrend; - static char tagname[20] = { 'N','e','w','E','x','t','e','r','n','a','l','I','P','A','d','d','r','e','s','s' }; - // Array NOT including a terminating nul +{ + mDNS *m = tcpInfo->m; + mDNSu16 err = NATErr_None; + mDNSv4Addr ExtAddr; + const mDNSu8 *ptr = tcpInfo->Reply; + const mDNSu8 *end = tcpInfo->Reply + tcpInfo->nread; + mDNSu8 *addrend; + static char tagname[20] = { 'N','e','w','E','x','t','e','r','n','a','l','I','P','A','d','d','r','e','s','s' }; + // Array NOT including a terminating nul // LogInfo("handleLNTGetExternalAddressResponse: %s", ptr); - mDNSs16 http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr - if (http_result == HTTPCode_404) LNT_ClearState(m); - if (http_result != HTTPCode_200) - { - mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", "noop", "HTTP Result", "HTTP code: %d", http_result); - return; - } - - while (ptr < end && strncasecmp((char*)ptr, tagname, sizeof(tagname))) ptr++; - ptr += sizeof(tagname); // Skip over "NewExternalIPAddress" - while (ptr < end && *ptr != '>') ptr++; - ptr += 1; // Skip over ">" - - // Find the end of the address and terminate the string so inet_pton() can convert it - // (Might be better to copy this to a local string here -- this is overwriting tcpInfo->Reply in-place - addrend = (mDNSu8*)ptr; - while (addrend < end && (mDNSIsDigit(*addrend) || *addrend == '.')) addrend++; - if (addrend >= end) return; - *addrend = 0; - - if (inet_pton(AF_INET, (char*)ptr, &ExtAddr) <= 0) - { - mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", "noop", "inet_pton", ""); - LogMsg("handleLNTGetExternalAddressResponse: Router returned bad address %s", ptr); - err = NATErr_NetFail; - ExtAddr = zerov4Addr; - } - if (!err) LogInfo("handleLNTGetExternalAddressResponse: External IP address is %.4a", &ExtAddr); - - natTraversalHandleAddressReply(m, err, ExtAddr); - } + mDNSs16 http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr + if (http_result == HTTPCode_404) LNT_ClearState(m); + if (http_result != HTTPCode_200) + { + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", "noop", "HTTP Result", "HTTP code: %d", http_result); + return; + } + + while (ptr < end && strncasecmp((char*)ptr, tagname, sizeof(tagname))) ptr++; + ptr += sizeof(tagname); // Skip over "NewExternalIPAddress" + while (ptr < end && *ptr != '>') ptr++; + ptr += 1; // Skip over ">" + + // Find the end of the address and terminate the string so inet_pton() can convert it + // (Might be better to copy this to a local string here -- this is overwriting tcpInfo->Reply in-place + addrend = (mDNSu8*)ptr; + while (addrend < end && (mDNSIsDigit(*addrend) || *addrend == '.')) addrend++; + if (addrend >= end) return; + *addrend = 0; + + if (inet_pton(AF_INET, (char*)ptr, &ExtAddr) <= 0) + { + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", "noop", "inet_pton", ""); + LogMsg("handleLNTGetExternalAddressResponse: Router returned bad address %s", ptr); + err = NATErr_NetFail; + ExtAddr = zerov4Addr; + } + if (!err) LogInfo("handleLNTGetExternalAddressResponse: External IP address is %.4a", &ExtAddr); + + natTraversalHandleAddressReply(m, err, ExtAddr); +} mDNSlocal void handleLNTPortMappingResponse(tcpLNTInfo *tcpInfo) - { - mDNS *m = tcpInfo->m; - mDNSIPPort extport = zeroIPPort; - const mDNSu8 *ptr = tcpInfo->Reply; - const mDNSu8 *const end = tcpInfo->Reply + tcpInfo->nread; - NATTraversalInfo *natInfo; - mDNSs16 http_result; - - for (natInfo = m->NATTraversals; natInfo; natInfo=natInfo->next) { if (natInfo == tcpInfo->parentNATInfo) break; } - - if (!natInfo) { LogInfo("handleLNTPortMappingResponse: can't find matching tcpInfo in NATTraversals!"); return; } - - http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr - if (http_result == HTTPCode_200) - { - LogInfo("handleLNTPortMappingResponse: got a valid response, sending reply to natTraversalHandlePortMapReply(internal %d external %d retries %d)", - mDNSVal16(natInfo->IntPort), RequestedPortNum(natInfo), tcpInfo->retries); - - // Make sure to compute extport *before* we zero tcpInfo->retries - extport = mDNSOpaque16fromIntVal(RequestedPortNum(natInfo)); - tcpInfo->retries = 0; - natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, mStatus_NoError, extport, NATMAP_DEFAULT_LEASE); - } - else if (http_result == HTTPCode_500) - { - while (ptr && ptr != end) - { - if (((*ptr & 0xDF) == 'C' && end - ptr >= 8 && strncasecmp((char*)ptr, "Conflict", 8) == 0) || - (*ptr == '>' && end - ptr >= 15 && strncasecmp((char*)ptr, ">718retries < 100) - { - tcpInfo->retries++; SendPortMapRequest(tcpInfo->m, natInfo); - mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "Conflict", "Retry %d", tcpInfo->retries); - } - else - { - LogMsg("handleLNTPortMappingResponse too many conflict retries %d %d", mDNSVal16(natInfo->IntPort), mDNSVal16(natInfo->RequestedPort)); - mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "Conflict - too many retries", "Retries: %d", tcpInfo->retries); - natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0); - } - return; - } - ptr++; - } - } - else if (http_result == HTTPCode_Bad) LogMsg("handleLNTPortMappingResponse got data that was not a valid HTTP response"); - else if (http_result == HTTPCode_Other) LogMsg("handleLNTPortMappingResponse got unexpected response code"); - else if (http_result == HTTPCode_404) LNT_ClearState(m); - if (http_result != HTTPCode_200 && http_result != HTTPCode_500) - mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "HTTP Result", "HTTP code: %d", http_result); - } +{ + mDNS *m = tcpInfo->m; + mDNSIPPort extport = zeroIPPort; + const mDNSu8 *ptr = tcpInfo->Reply; + const mDNSu8 *const end = tcpInfo->Reply + tcpInfo->nread; + NATTraversalInfo *natInfo; + mDNSs16 http_result; + + for (natInfo = m->NATTraversals; natInfo; natInfo=natInfo->next) { if (natInfo == tcpInfo->parentNATInfo) break;} + + if (!natInfo) { LogInfo("handleLNTPortMappingResponse: can't find matching tcpInfo in NATTraversals!"); return; } + + http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr + if (http_result == HTTPCode_200) + { + LogInfo("handleLNTPortMappingResponse: got a valid response, sending reply to natTraversalHandlePortMapReply(internal %d external %d retries %d)", + mDNSVal16(natInfo->IntPort), RequestedPortNum(natInfo), tcpInfo->retries); + + // Make sure to compute extport *before* we zero tcpInfo->retries + extport = mDNSOpaque16fromIntVal(RequestedPortNum(natInfo)); + tcpInfo->retries = 0; + natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, mStatus_NoError, extport, NATMAP_DEFAULT_LEASE); + } + else if (http_result == HTTPCode_500) + { + while (ptr && ptr != end) + { + if (((*ptr & 0xDF) == 'C' && end - ptr >= 8 && strncasecmp((char*)ptr, "Conflict", 8) == 0) || + (*ptr == '>' && end - ptr >= 15 && strncasecmp((char*)ptr, ">718retries < 100) + { + tcpInfo->retries++; SendPortMapRequest(tcpInfo->m, natInfo); + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "Conflict", "Retry %d", tcpInfo->retries); + } + else + { + LogMsg("handleLNTPortMappingResponse too many conflict retries %d %d", mDNSVal16(natInfo->IntPort), mDNSVal16(natInfo->RequestedPort)); + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "Conflict - too many retries", "Retries: %d", tcpInfo->retries); + natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0); + } + return; + } + ptr++; + } + } + else if (http_result == HTTPCode_Bad) LogMsg("handleLNTPortMappingResponse got data that was not a valid HTTP response"); + else if (http_result == HTTPCode_Other) LogMsg("handleLNTPortMappingResponse got unexpected response code"); + else if (http_result == HTTPCode_404) LNT_ClearState(m); + if (http_result != HTTPCode_200 && http_result != HTTPCode_500) + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "HTTP Result", "HTTP code: %d", http_result); +} mDNSlocal void DisposeInfoFromUnmapList(mDNS *m, tcpLNTInfo *tcpInfo) - { - tcpLNTInfo **ptr = &m->tcpInfoUnmapList; - while (*ptr && *ptr != tcpInfo) ptr = &(*ptr)->next; - if (*ptr) { *ptr = (*ptr)->next; mDNSPlatformMemFree(tcpInfo); } // If we found it, cut it from our list and free the memory - } +{ + tcpLNTInfo **ptr = &m->tcpInfoUnmapList; + while (*ptr && *ptr != tcpInfo) ptr = &(*ptr)->next; + if (*ptr) { *ptr = (*ptr)->next; mDNSPlatformMemFree(tcpInfo); } // If we found it, cut it from our list and free the memory +} mDNSlocal void tcpConnectionCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEstablished, mStatus err) - { - mStatus status = mStatus_NoError; - tcpLNTInfo *tcpInfo = (tcpLNTInfo *)context; - mDNSBool closed = mDNSfalse; - long n = 0; - long nsent = 0; - - if (tcpInfo == mDNSNULL) { LogInfo("tcpConnectionCallback: no tcpInfo context"); status = mStatus_Invalid; goto exit; } - - // The handlers below expect to be called with the lock held - mDNS_Lock(tcpInfo->m); - - if (err) { LogInfo("tcpConnectionCallback: received error"); goto exit; } - - if (ConnectionEstablished) // connection is established - send the message - { - LogInfo("tcpConnectionCallback: connection established, sending message"); - nsent = mDNSPlatformWriteTCP(sock, (char*)tcpInfo->Request, tcpInfo->requestLen); - if (nsent != (long)tcpInfo->requestLen) { LogMsg("tcpConnectionCallback: error writing"); status = mStatus_UnknownErr; goto exit; } - } - else - { - n = mDNSPlatformReadTCP(sock, (char*)tcpInfo->Reply + tcpInfo->nread, tcpInfo->replyLen - tcpInfo->nread, &closed); - LogInfo("tcpConnectionCallback: mDNSPlatformReadTCP read %d bytes", n); - - if (n < 0) { LogInfo("tcpConnectionCallback - read returned %d", n); status = mStatus_ConnFailed; goto exit; } - else if (closed) { LogInfo("tcpConnectionCallback: socket closed by remote end %d", tcpInfo->nread); status = mStatus_ConnFailed; goto exit; } - - tcpInfo->nread += n; - LogInfo("tcpConnectionCallback tcpInfo->nread %d", tcpInfo->nread); - if (tcpInfo->nread > LNT_MAXBUFSIZE) - { - LogInfo("result truncated..."); - tcpInfo->nread = LNT_MAXBUFSIZE; - } - - switch (tcpInfo->op) - { - case LNTDiscoveryOp: handleLNTDeviceDescriptionResponse (tcpInfo); break; - case LNTExternalAddrOp: handleLNTGetExternalAddressResponse(tcpInfo); break; - case LNTPortMapOp: handleLNTPortMappingResponse (tcpInfo); break; - case LNTPortMapDeleteOp: status = mStatus_ConfigChanged; break; - default: LogMsg("tcpConnectionCallback: bad tcp operation! %d", tcpInfo->op); status = mStatus_Invalid; break; - } - } +{ + mStatus status = mStatus_NoError; + tcpLNTInfo *tcpInfo = (tcpLNTInfo *)context; + mDNSBool closed = mDNSfalse; + long n = 0; + long nsent = 0; + static int LNTERRORcount = 0; + + if (tcpInfo == mDNSNULL) { LogInfo("tcpConnectionCallback: no tcpInfo context"); status = mStatus_Invalid; goto exit; } + + if (tcpInfo->sock != sock) + { + LogMsg("tcpConnectionCallback: WARNING- tcpInfo->sock(%p) != sock(%p) !!! Printing tcpInfo struct", tcpInfo->sock, sock); + LogMsg("tcpConnectionCallback: tcpInfo->Address:Port [%#a:%d] tcpInfo->op[%d] tcpInfo->retries[%d] tcpInfo->Request[%s] tcpInfo->Reply[%s]", + &tcpInfo->Address, mDNSVal16(tcpInfo->Port), tcpInfo->op, tcpInfo->retries, tcpInfo->Request, tcpInfo->Reply); + } + + // The handlers below expect to be called with the lock held + mDNS_Lock(tcpInfo->m); + + if (err) { LogInfo("tcpConnectionCallback: received error"); goto exit; } + + if (ConnectionEstablished) // connection is established - send the message + { + LogInfo("tcpConnectionCallback: connection established, sending message"); + nsent = mDNSPlatformWriteTCP(sock, (char*)tcpInfo->Request, tcpInfo->requestLen); + if (nsent != (long)tcpInfo->requestLen) { LogMsg("tcpConnectionCallback: error writing"); status = mStatus_UnknownErr; goto exit; } + } + else + { + n = mDNSPlatformReadTCP(sock, (char*)tcpInfo->Reply + tcpInfo->nread, tcpInfo->replyLen - tcpInfo->nread, &closed); + LogInfo("tcpConnectionCallback: mDNSPlatformReadTCP read %d bytes", n); + + if (n < 0) { LogInfo("tcpConnectionCallback - read returned %d", n); status = mStatus_ConnFailed; goto exit; } + else if (closed) { LogInfo("tcpConnectionCallback: socket closed by remote end %d", tcpInfo->nread); status = mStatus_ConnFailed; goto exit; } + + tcpInfo->nread += n; + LogInfo("tcpConnectionCallback tcpInfo->nread %d", tcpInfo->nread); + if (tcpInfo->nread > LNT_MAXBUFSIZE) + { + LogInfo("result truncated..."); + tcpInfo->nread = LNT_MAXBUFSIZE; + } + + switch (tcpInfo->op) + { + case LNTDiscoveryOp: handleLNTDeviceDescriptionResponse (tcpInfo); break; + case LNTExternalAddrOp: handleLNTGetExternalAddressResponse(tcpInfo); break; + case LNTPortMapOp: handleLNTPortMappingResponse (tcpInfo); break; + case LNTPortMapDeleteOp: status = mStatus_ConfigChanged; break; + default: LogMsg("tcpConnectionCallback: bad tcp operation! %d", tcpInfo->op); status = mStatus_Invalid; break; + } + } exit: - if (err || status) - { - mDNS *m = tcpInfo->m; - switch (tcpInfo->op) - { - case LNTDiscoveryOp: if (m->UPnPSOAPAddressString == mDNSNULL) - mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "failure", "SOAP Address", ""); - if (m->UPnPSOAPURL == mDNSNULL) - mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "failure", "SOAP path", ""); - if (m->UPnPSOAPAddressString && m->UPnPSOAPURL) - mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "success", "success", ""); - break; - case LNTExternalAddrOp: mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", - mDNSIPv4AddressIsZero(m->ExternalAddress) ? "failure" : "success", - mDNSIPv4AddressIsZero(m->ExternalAddress) ? "failure" : "success", ""); - break; - case LNTPortMapOp: if (tcpInfo->parentNATInfo) - mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", (tcpInfo->parentNATInfo->Result) ? "failure" : "success", - (tcpInfo->parentNATInfo->Result) ? "failure" : "success", "Result: %d", tcpInfo->parentNATInfo->Result); - break; - case LNTPortMapDeleteOp: break; - default: break; - } - - mDNSPlatformTCPCloseConnection(tcpInfo->sock); - tcpInfo->sock = mDNSNULL; - if (tcpInfo->Request) { mDNSPlatformMemFree(tcpInfo->Request); tcpInfo->Request = mDNSNULL; } - if (tcpInfo->Reply ) { mDNSPlatformMemFree(tcpInfo->Reply); tcpInfo->Reply = mDNSNULL; } - } - - if (tcpInfo) mDNS_Unlock(tcpInfo->m); - - if (status == mStatus_ConfigChanged) DisposeInfoFromUnmapList(tcpInfo->m, tcpInfo); - } + if (err || status) + { + mDNS *m = tcpInfo->m; + if ((++LNTERRORcount % 1000) == 0) + { + LogMsg("ERROR: tcpconnectioncallback -> got error status %d times", LNTERRORcount); + assert(LNTERRORcount < 1000); + // Recovery Mechanism to bail mDNSResponder out of trouble: It has been seen that we can get into + // this loop: [tcpKQSocketCallback()--> doTcpSocketCallback()-->tcpconnectionCallback()-->mDNSASLLog()], + // if mDNSPlatformTCPCloseConnection() does not close the TCPSocket. Instead of calling mDNSASLLog() + // repeatedly and logging the same error msg causing 100% CPU usage, we + // crash mDNSResponder using assert() and restart fresh. See advantages below: + // 1.Better User Experience + // 2.CrashLogs frequency can be monitored + // 3.StackTrace can be used for more info + } + + switch (tcpInfo->op) + { + case LNTDiscoveryOp: if (m->UPnPSOAPAddressString == mDNSNULL) + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "failure", "SOAP Address", ""); + if (m->UPnPSOAPURL == mDNSNULL) + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "failure", "SOAP path", ""); + if (m->UPnPSOAPAddressString && m->UPnPSOAPURL) + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "success", "success", ""); + break; + case LNTExternalAddrOp: mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", + mDNSIPv4AddressIsZero(m->ExternalAddress) ? "failure" : "success", + mDNSIPv4AddressIsZero(m->ExternalAddress) ? "failure" : "success", ""); + break; + case LNTPortMapOp: if (tcpInfo->parentNATInfo) + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", (tcpInfo->parentNATInfo->Result) ? "failure" : "success", + (tcpInfo->parentNATInfo->Result) ? "failure" : "success", "Result: %d", tcpInfo->parentNATInfo->Result); + break; + case LNTPortMapDeleteOp: break; + default: break; + } + + mDNSPlatformTCPCloseConnection(sock); + tcpInfo->sock = mDNSNULL; + if (tcpInfo->Request) { mDNSPlatformMemFree(tcpInfo->Request); tcpInfo->Request = mDNSNULL; } + if (tcpInfo->Reply ) { mDNSPlatformMemFree(tcpInfo->Reply); tcpInfo->Reply = mDNSNULL; } + } + else + { + LNTERRORcount = 0; // clear LNTERRORcount + } + + if (tcpInfo) mDNS_Unlock(tcpInfo->m); + + if (status == mStatus_ConfigChanged) DisposeInfoFromUnmapList(tcpInfo->m, tcpInfo); +} mDNSlocal mStatus MakeTCPConnection(mDNS *const m, tcpLNTInfo *info, const mDNSAddr *const Addr, const mDNSIPPort Port, LNTOp_t op) - { - mStatus err = mStatus_NoError; - mDNSIPPort srcport = zeroIPPort; - - if (mDNSIPv4AddressIsZero(Addr->ip.v4) || mDNSIPPortIsZero(Port)) - { LogMsg("LNT MakeTCPConnection: bad address/port %#a:%d", Addr, mDNSVal16(Port)); return(mStatus_Invalid); } - info->m = m; - info->Address = *Addr; - info->Port = Port; - info->op = op; - info->nread = 0; - info->replyLen = LNT_MAXBUFSIZE; - if (info->Reply != mDNSNULL) mDNSPlatformMemZero(info->Reply, LNT_MAXBUFSIZE); // reuse previously allocated buffer - else if ((info->Reply = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE)) == mDNSNULL) { LogInfo("can't allocate reply buffer"); return (mStatus_NoMemoryErr); } - - if (info->sock) { LogInfo("MakeTCPConnection: closing previous open connection"); mDNSPlatformTCPCloseConnection(info->sock); info->sock = mDNSNULL; } - info->sock = mDNSPlatformTCPSocket(m, kTCPSocketFlags_Zero, &srcport); - if (!info->sock) { LogMsg("LNT MakeTCPConnection: unable to create TCP socket"); mDNSPlatformMemFree(info->Reply); info->Reply = mDNSNULL; return(mStatus_NoMemoryErr); } - LogInfo("MakeTCPConnection: connecting to %#a:%d", &info->Address, mDNSVal16(info->Port)); - err = mDNSPlatformTCPConnect(info->sock, Addr, Port, mDNSNULL, 0, tcpConnectionCallback, info); - - if (err == mStatus_ConnPending) err = mStatus_NoError; - else if (err == mStatus_ConnEstablished) - { - mDNS_DropLockBeforeCallback(); - tcpConnectionCallback(info->sock, info, mDNStrue, mStatus_NoError); - mDNS_ReclaimLockAfterCallback(); - err = mStatus_NoError; - } - else - { - // Don't need to log this in customer builds -- it happens quite often during sleep, wake, configuration changes, etc. - LogInfo("LNT MakeTCPConnection: connection failed"); - mDNSPlatformTCPCloseConnection(info->sock); // Dispose the socket we created with mDNSPlatformTCPSocket() above - info->sock = mDNSNULL; - mDNSPlatformMemFree(info->Reply); - info->Reply = mDNSNULL; - } - return(err); - } +{ + mStatus err = mStatus_NoError; + mDNSIPPort srcport = zeroIPPort; + + if (mDNSIPv4AddressIsZero(Addr->ip.v4) || mDNSIPPortIsZero(Port)) + { LogMsg("LNT MakeTCPConnection: bad address/port %#a:%d", Addr, mDNSVal16(Port)); return(mStatus_Invalid); } + info->m = m; + info->Address = *Addr; + info->Port = Port; + info->op = op; + info->nread = 0; + info->replyLen = LNT_MAXBUFSIZE; + if (info->Reply != mDNSNULL) mDNSPlatformMemZero(info->Reply, LNT_MAXBUFSIZE); // reuse previously allocated buffer + else if ((info->Reply = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE)) == mDNSNULL) { LogInfo("can't allocate reply buffer"); return (mStatus_NoMemoryErr); } + + if (info->sock) { LogInfo("MakeTCPConnection: closing previous open connection"); mDNSPlatformTCPCloseConnection(info->sock); info->sock = mDNSNULL; } + info->sock = mDNSPlatformTCPSocket(m, kTCPSocketFlags_Zero, &srcport, mDNSfalse); + if (!info->sock) { LogMsg("LNT MakeTCPConnection: unable to create TCP socket"); mDNSPlatformMemFree(info->Reply); info->Reply = mDNSNULL; return(mStatus_NoMemoryErr); } + LogInfo("MakeTCPConnection: connecting to %#a:%d", &info->Address, mDNSVal16(info->Port)); + err = mDNSPlatformTCPConnect(info->sock, Addr, Port, mDNSNULL, 0, tcpConnectionCallback, info); + + if (err == mStatus_ConnPending) err = mStatus_NoError; + else if (err == mStatus_ConnEstablished) + { + mDNS_DropLockBeforeCallback(); + tcpConnectionCallback(info->sock, info, mDNStrue, mStatus_NoError); + mDNS_ReclaimLockAfterCallback(); + err = mStatus_NoError; + } + else + { + // Don't need to log this in customer builds -- it happens quite often during sleep, wake, configuration changes, etc. + LogInfo("LNT MakeTCPConnection: connection failed"); + mDNSPlatformTCPCloseConnection(info->sock); // Dispose the socket we created with mDNSPlatformTCPSocket() above + info->sock = mDNSNULL; + mDNSPlatformMemFree(info->Reply); + info->Reply = mDNSNULL; + } + return(err); +} mDNSlocal unsigned int AddSOAPArguments(char *const buf, const unsigned int maxlen, const int numArgs, const Property *const a) - { - static const char f1[] = "<%s>%s"; - static const char f2[] = "<%s xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"%s\">%s"; - int i, len = 0; - *buf = 0; - for (i = 0; i < numArgs; i++) - { - if (a[i].type) len += mDNS_snprintf(buf + len, maxlen - len, f2, a[i].name, a[i].type, a[i].value, a[i].name); - else len += mDNS_snprintf(buf + len, maxlen - len, f1, a[i].name, a[i].value, a[i].name); - } - return(len); - } +{ + static const char f1[] = "<%s>%s"; + static const char f2[] = "<%s xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"%s\">%s"; + int i, len = 0; + *buf = 0; + for (i = 0; i < numArgs; i++) + { + if (a[i].type) len += mDNS_snprintf(buf + len, maxlen - len, f2, a[i].name, a[i].type, a[i].value, a[i].name); + else len += mDNS_snprintf(buf + len, maxlen - len, f1, a[i].name, a[i].value, a[i].name); + } + return(len); +} mDNSlocal mStatus SendSOAPMsgControlAction(mDNS *m, tcpLNTInfo *info, const char *const Action, const int numArgs, const Property *const Arguments, const LNTOp_t op) - { - // SOAP message header format - - // - control URL - // - action (string) - // - router's host/port ("host:port") - // - content-length - static const char header[] = - "POST %s HTTP/1.1\r\n" - "Content-Type: text/xml; charset=\"utf-8\"\r\n" - "SOAPAction: \"urn:schemas-upnp-org:service:WAN%sConnection:1#%s\"\r\n" - "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows 9x)\r\n" - "Host: %s\r\n" - "Content-Length: %d\r\n" - "Connection: close\r\n" - "Pragma: no-cache\r\n" - "\r\n" - "%s\r\n"; - - static const char body1[] = - "\r\n" - "" - "" - ""; - - static const char body2[] = - "" - "" - "\r\n"; - - mStatus err; - char *body = (char*)&m->omsg; // Typically requires 1110-1122 bytes; m->omsg is 8952 bytes, which is plenty - int bodyLen; - - if (mDNSIPPortIsZero(m->UPnPSOAPPort) || m->UPnPSOAPURL == mDNSNULL || m->UPnPSOAPAddressString == mDNSNULL) // if no SOAP URL or address exists get out here - { LogInfo("SendSOAPMsgControlAction: no SOAP port, URL or address string"); return mStatus_Invalid; } - - // Create body - bodyLen = mDNS_snprintf (body, sizeof(m->omsg), body1, Action, m->UPnPWANPPPConnection ? "PPP" : "IP"); - bodyLen += AddSOAPArguments(body + bodyLen, sizeof(m->omsg) - bodyLen, numArgs, Arguments); - bodyLen += mDNS_snprintf (body + bodyLen, sizeof(m->omsg) - bodyLen, body2, Action); - - // Create info->Request; the header needs to contain the bodyLen in the "Content-Length" field - if (!info->Request) info->Request = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE); - if (!info->Request) { LogMsg("SendSOAPMsgControlAction: Can't allocate info->Request"); return mStatus_NoMemoryErr; } - info->requestLen = mDNS_snprintf((char*)info->Request, LNT_MAXBUFSIZE, header, m->UPnPSOAPURL, m->UPnPWANPPPConnection ? "PPP" : "IP", Action, m->UPnPSOAPAddressString, bodyLen, body); - - err = MakeTCPConnection(m, info, &m->Router, m->UPnPSOAPPort, op); - if (err) { mDNSPlatformMemFree(info->Request); info->Request = mDNSNULL; } - return err; - } +{ + // SOAP message header format - + // - control URL + // - action (string) + // - router's host/port ("host:port") + // - content-length + static const char header[] = + "POST %s HTTP/1.1\r\n" + "Content-Type: text/xml; charset=\"utf-8\"\r\n" + "SOAPAction: \"urn:schemas-upnp-org:service:WAN%sConnection:1#%s\"\r\n" + "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows 9x)\r\n" + "Host: %s\r\n" + "Content-Length: %d\r\n" + "Connection: close\r\n" + "Pragma: no-cache\r\n" + "\r\n" + "%s\r\n"; + + static const char body1[] = + "\r\n" + "" + "" + ""; + + static const char body2[] = + "" + "" + "\r\n"; + + mStatus err; + char *body = (char*)&m->omsg; // Typically requires 1110-1122 bytes; m->omsg is 8952 bytes, which is plenty + int bodyLen; + + if (mDNSIPPortIsZero(m->UPnPSOAPPort) || m->UPnPSOAPURL == mDNSNULL || m->UPnPSOAPAddressString == mDNSNULL) // if no SOAP URL or address exists get out here + { LogInfo("SendSOAPMsgControlAction: no SOAP port, URL or address string"); return mStatus_Invalid; } + + // Create body + bodyLen = mDNS_snprintf (body, sizeof(m->omsg), body1, Action, m->UPnPWANPPPConnection ? "PPP" : "IP"); + bodyLen += AddSOAPArguments(body + bodyLen, sizeof(m->omsg) - bodyLen, numArgs, Arguments); + bodyLen += mDNS_snprintf (body + bodyLen, sizeof(m->omsg) - bodyLen, body2, Action); + + // Create info->Request; the header needs to contain the bodyLen in the "Content-Length" field + if (!info->Request) info->Request = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE); + if (!info->Request) { LogMsg("SendSOAPMsgControlAction: Can't allocate info->Request"); return mStatus_NoMemoryErr; } + info->requestLen = mDNS_snprintf((char*)info->Request, LNT_MAXBUFSIZE, header, m->UPnPSOAPURL, m->UPnPWANPPPConnection ? "PPP" : "IP", Action, m->UPnPSOAPAddressString, bodyLen, body); + + err = MakeTCPConnection(m, info, &m->Router, m->UPnPSOAPPort, op); + if (err) { mDNSPlatformMemFree(info->Request); info->Request = mDNSNULL; } + return err; +} // Build port mapping request with new port (up to max) and send it mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n) - { - char externalPort[6]; - char internalPort[6]; - char localIPAddrString[30]; - char publicPortString[40]; - Property propArgs[8]; - mDNSu16 ReqPortNum = RequestedPortNum(n); - NATTraversalInfo *n2 = m->NATTraversals; - - // Scan our m->NATTraversals list to make sure the external port we're requesting is locally unique. - // UPnP gateways will report conflicts if different devices request the same external port, but if two - // clients on the same device request the same external port the second one just stomps over the first. - // One way this can happen is like this: - // 1. Client A binds local port 80 - // 2. Client A requests external port 80 -> internal port 80 - // 3. UPnP NAT gateway refuses external port 80 (some other client already has it) - // 4. Client A tries again, and successfully gets external port 80 -> internal port 81 - // 5. Client B on same machine tries to bind local port 80, and fails - // 6. Client B tries again, and successfully binds local port 81 - // 7. Client B now requests external port 81 -> internal port 81 - // 8. UPnP NAT gateway allows this, stomping over Client A's existing mapping - - while (n2) - { - if (n2 == n || RequestedPortNum(n2) != ReqPortNum) n2=n2->next; - else - { - if (n->tcpInfo.retries < 100) - { - n->tcpInfo.retries++; - ReqPortNum = RequestedPortNum(n); // Pick a new port number - n2 = m->NATTraversals; // And re-scan the list looking for conflicts - } - else - { - natTraversalHandlePortMapReply(m, n, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0); - return mStatus_NoError; - } - } - } - - // create strings to use in the message - mDNS_snprintf(externalPort, sizeof(externalPort), "%u", ReqPortNum); - mDNS_snprintf(internalPort, sizeof(internalPort), "%u", mDNSVal16(n->IntPort)); - mDNS_snprintf(publicPortString, sizeof(publicPortString), "iC%u", ReqPortNum); - mDNS_snprintf(localIPAddrString, sizeof(localIPAddrString), "%u.%u.%u.%u", - m->AdvertisedV4.ip.v4.b[0], m->AdvertisedV4.ip.v4.b[1], m->AdvertisedV4.ip.v4.b[2], m->AdvertisedV4.ip.v4.b[3]); - - // build the message - mDNSPlatformMemZero(propArgs, sizeof(propArgs)); - propArgs[0].name = "NewRemoteHost"; - propArgs[0].type = "string"; - propArgs[0].value = ""; - propArgs[1].name = "NewExternalPort"; - propArgs[1].type = "ui2"; - propArgs[1].value = externalPort; - propArgs[2].name = "NewProtocol"; - propArgs[2].type = "string"; - propArgs[2].value = (n->Protocol == NATOp_MapUDP) ? "UDP" : "TCP"; - propArgs[3].name = "NewInternalPort"; - propArgs[3].type = "ui2"; - propArgs[3].value = internalPort; - propArgs[4].name = "NewInternalClient"; - propArgs[4].type = "string"; - propArgs[4].value = localIPAddrString; - propArgs[5].name = "NewEnabled"; - propArgs[5].type = "boolean"; - propArgs[5].value = "1"; - propArgs[6].name = "NewPortMappingDescription"; - propArgs[6].type = "string"; - propArgs[6].value = publicPortString; - propArgs[7].name = "NewLeaseDuration"; - propArgs[7].type = "ui4"; - propArgs[7].value = "0"; - - LogInfo("SendPortMapRequest: internal %u external %u", mDNSVal16(n->IntPort), ReqPortNum); - return SendSOAPMsgControlAction(m, &n->tcpInfo, "AddPortMapping", 8, propArgs, LNTPortMapOp); - } +{ + char externalPort[6]; + char internalPort[6]; + char localIPAddrString[30]; + char publicPortString[40]; + Property propArgs[8]; + mDNSu16 ReqPortNum = RequestedPortNum(n); + NATTraversalInfo *n2 = m->NATTraversals; + + // Scan our m->NATTraversals list to make sure the external port we're requesting is locally unique. + // UPnP gateways will report conflicts if different devices request the same external port, but if two + // clients on the same device request the same external port the second one just stomps over the first. + // One way this can happen is like this: + // 1. Client A binds local port 80 + // 2. Client A requests external port 80 -> internal port 80 + // 3. UPnP NAT gateway refuses external port 80 (some other client already has it) + // 4. Client A tries again, and successfully gets external port 80 -> internal port 81 + // 5. Client B on same machine tries to bind local port 80, and fails + // 6. Client B tries again, and successfully binds local port 81 + // 7. Client B now requests external port 81 -> internal port 81 + // 8. UPnP NAT gateway allows this, stomping over Client A's existing mapping + + while (n2) + { + if (n2 == n || RequestedPortNum(n2) != ReqPortNum) n2=n2->next; + else + { + if (n->tcpInfo.retries < 100) + { + n->tcpInfo.retries++; + ReqPortNum = RequestedPortNum(n); // Pick a new port number + n2 = m->NATTraversals; // And re-scan the list looking for conflicts + } + else + { + natTraversalHandlePortMapReply(m, n, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0); + return mStatus_NoError; + } + } + } + + // create strings to use in the message + mDNS_snprintf(externalPort, sizeof(externalPort), "%u", ReqPortNum); + mDNS_snprintf(internalPort, sizeof(internalPort), "%u", mDNSVal16(n->IntPort)); + mDNS_snprintf(publicPortString, sizeof(publicPortString), "iC%u", ReqPortNum); + mDNS_snprintf(localIPAddrString, sizeof(localIPAddrString), "%u.%u.%u.%u", + m->AdvertisedV4.ip.v4.b[0], m->AdvertisedV4.ip.v4.b[1], m->AdvertisedV4.ip.v4.b[2], m->AdvertisedV4.ip.v4.b[3]); + + // build the message + mDNSPlatformMemZero(propArgs, sizeof(propArgs)); + propArgs[0].name = "NewRemoteHost"; + propArgs[0].type = "string"; + propArgs[0].value = ""; + propArgs[1].name = "NewExternalPort"; + propArgs[1].type = "ui2"; + propArgs[1].value = externalPort; + propArgs[2].name = "NewProtocol"; + propArgs[2].type = "string"; + propArgs[2].value = (n->Protocol == NATOp_MapUDP) ? "UDP" : "TCP"; + propArgs[3].name = "NewInternalPort"; + propArgs[3].type = "ui2"; + propArgs[3].value = internalPort; + propArgs[4].name = "NewInternalClient"; + propArgs[4].type = "string"; + propArgs[4].value = localIPAddrString; + propArgs[5].name = "NewEnabled"; + propArgs[5].type = "boolean"; + propArgs[5].value = "1"; + propArgs[6].name = "NewPortMappingDescription"; + propArgs[6].type = "string"; + propArgs[6].value = publicPortString; + propArgs[7].name = "NewLeaseDuration"; + propArgs[7].type = "ui4"; + propArgs[7].value = "0"; + + LogInfo("SendPortMapRequest: internal %u external %u", mDNSVal16(n->IntPort), ReqPortNum); + return SendSOAPMsgControlAction(m, &n->tcpInfo, "AddPortMapping", 8, propArgs, LNTPortMapOp); +} mDNSexport mStatus LNT_MapPort(mDNS *m, NATTraversalInfo *const n) - { - LogInfo("LNT_MapPort"); - if (n->tcpInfo.sock) return(mStatus_NoError); // If we already have a connection up don't make another request for the same thing - n->tcpInfo.parentNATInfo = n; - n->tcpInfo.retries = 0; - return SendPortMapRequest(m, n); - } +{ + LogInfo("LNT_MapPort"); + if (n->tcpInfo.sock) return(mStatus_NoError); // If we already have a connection up don't make another request for the same thing + n->tcpInfo.parentNATInfo = n; + n->tcpInfo.retries = 0; + return SendPortMapRequest(m, n); +} mDNSexport mStatus LNT_UnmapPort(mDNS *m, NATTraversalInfo *const n) - { - char externalPort[10]; - Property propArgs[3]; - tcpLNTInfo *info; - tcpLNTInfo **infoPtr = &m->tcpInfoUnmapList; - mStatus err; - - // If no NAT gateway to talk to, no need to do all this work for nothing - if (mDNSIPPortIsZero(m->UPnPSOAPPort) || !m->UPnPSOAPURL || !m->UPnPSOAPAddressString) return mStatus_NoError; - - mDNS_snprintf(externalPort, sizeof(externalPort), "%u", mDNSVal16(mDNSIPPortIsZero(n->RequestedPort) ? n->IntPort : n->RequestedPort)); - - mDNSPlatformMemZero(propArgs, sizeof(propArgs)); - propArgs[0].name = "NewRemoteHost"; - propArgs[0].type = "string"; - propArgs[0].value = ""; - propArgs[1].name = "NewExternalPort"; - propArgs[1].type = "ui2"; - propArgs[1].value = externalPort; - propArgs[2].name = "NewProtocol"; - propArgs[2].type = "string"; - propArgs[2].value = (n->Protocol == NATOp_MapUDP) ? "UDP" : "TCP"; - - n->tcpInfo.parentNATInfo = n; - - // clean up previous port mapping requests and allocations - if (n->tcpInfo.sock) LogInfo("LNT_UnmapPort: closing previous open connection"); - if (n->tcpInfo.sock ) { mDNSPlatformTCPCloseConnection(n->tcpInfo.sock); n->tcpInfo.sock = mDNSNULL; } - if (n->tcpInfo.Request) { mDNSPlatformMemFree(n->tcpInfo.Request); n->tcpInfo.Request = mDNSNULL; } - if (n->tcpInfo.Reply ) { mDNSPlatformMemFree(n->tcpInfo.Reply); n->tcpInfo.Reply = mDNSNULL; } - - // make a copy of the tcpInfo that we can clean up later (the one passed in will be destroyed by the client as soon as this returns) - if ((info = mDNSPlatformMemAllocate(sizeof(tcpLNTInfo))) == mDNSNULL) - { LogInfo("LNT_UnmapPort: can't allocate tcpInfo"); return(mStatus_NoMemoryErr); } - *info = n->tcpInfo; - - while (*infoPtr) infoPtr = &(*infoPtr)->next; // find the end of the list - *infoPtr = info; // append - - err = SendSOAPMsgControlAction(m, info, "DeletePortMapping", 3, propArgs, LNTPortMapDeleteOp); - if (err) DisposeInfoFromUnmapList(m, info); - return err; - } +{ + char externalPort[10]; + Property propArgs[3]; + tcpLNTInfo *info; + tcpLNTInfo **infoPtr = &m->tcpInfoUnmapList; + mStatus err; + + // If no NAT gateway to talk to, no need to do all this work for nothing + if (mDNSIPPortIsZero(m->UPnPSOAPPort) || !m->UPnPSOAPURL || !m->UPnPSOAPAddressString) return mStatus_NoError; + + mDNS_snprintf(externalPort, sizeof(externalPort), "%u", mDNSVal16(mDNSIPPortIsZero(n->RequestedPort) ? n->IntPort : n->RequestedPort)); + + mDNSPlatformMemZero(propArgs, sizeof(propArgs)); + propArgs[0].name = "NewRemoteHost"; + propArgs[0].type = "string"; + propArgs[0].value = ""; + propArgs[1].name = "NewExternalPort"; + propArgs[1].type = "ui2"; + propArgs[1].value = externalPort; + propArgs[2].name = "NewProtocol"; + propArgs[2].type = "string"; + propArgs[2].value = (n->Protocol == NATOp_MapUDP) ? "UDP" : "TCP"; + + n->tcpInfo.parentNATInfo = n; + + // clean up previous port mapping requests and allocations + if (n->tcpInfo.sock) LogInfo("LNT_UnmapPort: closing previous open connection"); + if (n->tcpInfo.sock ) { mDNSPlatformTCPCloseConnection(n->tcpInfo.sock); n->tcpInfo.sock = mDNSNULL; } + if (n->tcpInfo.Request) { mDNSPlatformMemFree(n->tcpInfo.Request); n->tcpInfo.Request = mDNSNULL; } + if (n->tcpInfo.Reply ) { mDNSPlatformMemFree(n->tcpInfo.Reply); n->tcpInfo.Reply = mDNSNULL; } + + // make a copy of the tcpInfo that we can clean up later (the one passed in will be destroyed by the client as soon as this returns) + if ((info = mDNSPlatformMemAllocate(sizeof(tcpLNTInfo))) == mDNSNULL) + { LogInfo("LNT_UnmapPort: can't allocate tcpInfo"); return(mStatus_NoMemoryErr); } + *info = n->tcpInfo; + + while (*infoPtr) infoPtr = &(*infoPtr)->next; // find the end of the list + *infoPtr = info; // append + + err = SendSOAPMsgControlAction(m, info, "DeletePortMapping", 3, propArgs, LNTPortMapDeleteOp); + if (err) DisposeInfoFromUnmapList(m, info); + return err; +} mDNSexport mStatus LNT_GetExternalAddress(mDNS *m) - { - return SendSOAPMsgControlAction(m, &m->tcpAddrInfo, "GetExternalIPAddress", 0, mDNSNULL, LNTExternalAddrOp); - } +{ + return SendSOAPMsgControlAction(m, &m->tcpAddrInfo, "GetExternalIPAddress", 0, mDNSNULL, LNTExternalAddrOp); +} mDNSlocal mStatus GetDeviceDescription(mDNS *m, tcpLNTInfo *info) - { - // Device description format - - // - device description URL - // - 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" - "\r\n"; - - if (!mDNSIPPortIsZero(m->UPnPSOAPPort)) return mStatus_NoError; // already have the info we need - - if (m->UPnPRouterURL == mDNSNULL || m->UPnPRouterAddressString == mDNSNULL) { LogInfo("GetDeviceDescription: no router URL or address string!"); return (mStatus_Invalid); } - - // build message - if (info->Request != mDNSNULL) mDNSPlatformMemZero(info->Request, LNT_MAXBUFSIZE); // reuse previously allocated buffer - else if ((info->Request = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE)) == mDNSNULL) { LogInfo("can't allocate send buffer for discovery"); return (mStatus_NoMemoryErr); } - info->requestLen = mDNS_snprintf((char*)info->Request, LNT_MAXBUFSIZE, szSSDPMsgDescribeDeviceFMT, m->UPnPRouterURL, m->UPnPRouterAddressString); - LogInfo("Describe Device: [%s]", info->Request); - return MakeTCPConnection(m, info, &m->Router, m->UPnPRouterPort, LNTDiscoveryOp); - } +{ + // Device description format - + // - device description URL + // - 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" + "\r\n"; + + if (!mDNSIPPortIsZero(m->UPnPSOAPPort)) return mStatus_NoError; // already have the info we need + + if (m->UPnPRouterURL == mDNSNULL || m->UPnPRouterAddressString == mDNSNULL) { LogInfo("GetDeviceDescription: no router URL or address string!"); return (mStatus_Invalid); } + + // build message + if (info->Request != mDNSNULL) mDNSPlatformMemZero(info->Request, LNT_MAXBUFSIZE); // reuse previously allocated buffer + else if ((info->Request = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE)) == mDNSNULL) { LogInfo("can't allocate send buffer for discovery"); return (mStatus_NoMemoryErr); } + info->requestLen = mDNS_snprintf((char*)info->Request, LNT_MAXBUFSIZE, szSSDPMsgDescribeDeviceFMT, m->UPnPRouterURL, m->UPnPRouterAddressString); + LogInfo("Describe Device: [%s]", info->Request); + return MakeTCPConnection(m, info, &m->Router, m->UPnPRouterPort, LNTDiscoveryOp); +} // This function parses the response to our SSDP discovery message. Basically, we look to make sure this is a response // referencing a service we care about (WANIPConnection or WANPPPConnection), then look for the "Location:" header and copy the addressing and // URL info we need. mDNSexport void LNT_ConfigureRouterInfo(mDNS *m, const mDNSInterfaceID InterfaceID, const mDNSu8 *const data, const mDNSu16 len) - { - const mDNSu8 *ptr = data; - const mDNSu8 *end = data + len; - const mDNSu8 *stop = ptr; - - if (!mDNSIPPortIsZero(m->UPnPRouterPort)) return; // already have the info we need - - // The formatting of the HTTP header is not always the same when it comes to the placement of - // the service and location strings, so we just look for each of them from the beginning for every response - - // figure out if this is a message from a service we care about - while (ptr && ptr != end) - { - if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANIPConnection:1", 17) == 0)) break; - ptr++; - } - if (ptr == end) - { - ptr = data; - while (ptr && ptr != end) - { - if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANPPPConnection:1", 18) == 0)) break; - ptr++; - } - } - if (ptr == mDNSNULL || ptr == end) return; // not a message we care about - - // find "Location:", starting from the beginning - ptr = data; - while (ptr && ptr != end) - { - if ((*ptr & 0xDF) == 'L' && (strncasecmp((char*)ptr, "Location:", 9) == 0)) break; // find the first 'L'; is this Location? if not, keep looking - ptr++; - } - if (ptr == mDNSNULL || ptr == end) - { - mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Location", ""); - return; // not a message we care about - } - ptr += 9; //Skip over 'Location:' - while (*ptr == ' ' && ptr < end) ptr++; // skip over spaces - if (ptr >= end) return; - - // find the end of the line - for (stop = ptr; stop != end; stop++) { if (*stop == '\r') { end = stop; break; } } - - // fill in default port - m->UPnPRouterPort = mDNSOpaque16fromIntVal(80); - - // free string pointers and set to NULL - if (m->UPnPRouterAddressString != mDNSNULL) - { - mDNSPlatformMemFree(m->UPnPRouterAddressString); - m->UPnPRouterAddressString = mDNSNULL; - } - if (m->UPnPRouterURL != mDNSNULL) - { - mDNSPlatformMemFree(m->UPnPRouterURL); - m->UPnPRouterURL = mDNSNULL; - } - - // the Router URL should look something like "/dyndev/uuid:0013-108c-4b3f0000f3dc" - if (ParseHttpUrl(ptr, end, &m->UPnPRouterAddressString, &m->UPnPRouterPort, &m->UPnPRouterURL) != mStatus_NoError) - { - mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Parse URL", ""); - return; - } - - m->UPnPInterfaceID = InterfaceID; - - if (m->UPnPRouterAddressString == mDNSNULL) - { - mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Router address", ""); - LogMsg("LNT_ConfigureRouterInfo: UPnPRouterAddressString is NULL"); - } - else LogInfo("LNT_ConfigureRouterInfo: Router address string [%s]", m->UPnPRouterAddressString); - - if (m->UPnPRouterURL == mDNSNULL) - { - mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Router path", ""); - LogMsg("LNT_ConfigureRouterInfo: UPnPRouterURL is NULL"); - } - else LogInfo("LNT_ConfigureRouterInfo: Router URL [%s]", m->UPnPRouterURL); - - LogInfo("LNT_ConfigureRouterInfo: Router port %d", mDNSVal16(m->UPnPRouterPort)); - LogInfo("LNT_ConfigureRouterInfo: Router interface %d", m->UPnPInterfaceID); - - // Don't need the SSDP socket anymore - if (m->SSDPSocket) { debugf("LNT_ConfigureRouterInfo destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; } - - mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "success", "success", ""); - // now send message to get the device description - GetDeviceDescription(m, &m->tcpDeviceInfo); - } +{ + const mDNSu8 *ptr = data; + const mDNSu8 *end = data + len; + const mDNSu8 *stop = ptr; + + if (!mDNSIPPortIsZero(m->UPnPRouterPort)) return; // already have the info we need + + // The formatting of the HTTP header is not always the same when it comes to the placement of + // the service and location strings, so we just look for each of them from the beginning for every response + + // figure out if this is a message from a service we care about + while (ptr && ptr != end) + { + if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANIPConnection:1", 17) == 0)) break; + ptr++; + } + if (ptr == end) + { + ptr = data; + while (ptr && ptr != end) + { + if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANPPPConnection:1", 18) == 0)) break; + ptr++; + } + } + if (ptr == mDNSNULL || ptr == end) return; // not a message we care about + + // find "Location:", starting from the beginning + ptr = data; + while (ptr && ptr != end) + { + if ((*ptr & 0xDF) == 'L' && (strncasecmp((char*)ptr, "Location:", 9) == 0)) break; // find the first 'L'; is this Location? if not, keep looking + ptr++; + } + if (ptr == mDNSNULL || ptr == end) + { + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Location", ""); + return; // not a message we care about + } + ptr += 9; //Skip over 'Location:' + while (*ptr == ' ' && ptr < end) ptr++; // skip over spaces + if (ptr >= end) return; + + // find the end of the line + for (stop = ptr; stop != end; stop++) { if (*stop == '\r') { end = stop; break; } } + + // fill in default port + m->UPnPRouterPort = mDNSOpaque16fromIntVal(80); + + // free string pointers and set to NULL + if (m->UPnPRouterAddressString != mDNSNULL) + { + mDNSPlatformMemFree(m->UPnPRouterAddressString); + m->UPnPRouterAddressString = mDNSNULL; + } + if (m->UPnPRouterURL != mDNSNULL) + { + mDNSPlatformMemFree(m->UPnPRouterURL); + m->UPnPRouterURL = mDNSNULL; + } + + // the Router URL should look something like "/dyndev/uuid:0013-108c-4b3f0000f3dc" + if (ParseHttpUrl(ptr, end, &m->UPnPRouterAddressString, &m->UPnPRouterPort, &m->UPnPRouterURL) != mStatus_NoError) + { + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Parse URL", ""); + return; + } + + m->UPnPInterfaceID = InterfaceID; + + if (m->UPnPRouterAddressString == mDNSNULL) + { + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Router address", ""); + LogMsg("LNT_ConfigureRouterInfo: UPnPRouterAddressString is NULL"); + } + else LogInfo("LNT_ConfigureRouterInfo: Router address string [%s]", m->UPnPRouterAddressString); + + if (m->UPnPRouterURL == mDNSNULL) + { + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Router path", ""); + LogMsg("LNT_ConfigureRouterInfo: UPnPRouterURL is NULL"); + } + else LogInfo("LNT_ConfigureRouterInfo: Router URL [%s]", m->UPnPRouterURL); + + LogInfo("LNT_ConfigureRouterInfo: Router port %d", mDNSVal16(m->UPnPRouterPort)); + LogInfo("LNT_ConfigureRouterInfo: Router interface %d", m->UPnPInterfaceID); + + // Don't need the SSDP socket anymore + if (m->SSDPSocket) { debugf("LNT_ConfigureRouterInfo destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; } + + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "success", "success", ""); + // now send message to get the device description + GetDeviceDescription(m, &m->tcpDeviceInfo); +} mDNSexport void LNT_SendDiscoveryMsg(mDNS *m) - { - static const char msg[] = - "M-SEARCH * HTTP/1.1\r\n" - "Host:239.255.255.250:1900\r\n" - "ST:urn:schemas-upnp-org:service:WAN%sConnection:1\r\n" - "Man:\"ssdp:discover\"\r\n" - "MX:3\r\n\r\n"; - static const mDNSAddr multicastDest = { mDNSAddrType_IPv4, { { { 239, 255, 255, 250 } } } }; - - mDNSu8 *buf = (mDNSu8*)&m->omsg; //m->omsg is 8952 bytes, which is plenty - unsigned int bufLen; - - if (!mDNSIPPortIsZero(m->UPnPRouterPort)) - { - if (m->SSDPSocket) { debugf("LNT_SendDiscoveryMsg destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; } - if (mDNSIPPortIsZero(m->UPnPSOAPPort) && !m->tcpDeviceInfo.sock) GetDeviceDescription(m, &m->tcpDeviceInfo); - return; - } - - // Always query for WANIPConnection in the first SSDP packet - if (m->retryIntervalGetAddr <= NATMAP_INIT_RETRY) m->SSDPWANPPPConnection = mDNSfalse; - - // Create message - bufLen = mDNS_snprintf((char*)buf, sizeof(m->omsg), msg, m->SSDPWANPPPConnection ? "PPP" : "IP"); - - debugf("LNT_SendDiscoveryMsg Router %.4a Current External Address %.4a", &m->Router.ip.v4, &m->ExternalAddress); - - if (!mDNSIPv4AddressIsZero(m->Router.ip.v4)) - { - if (!m->SSDPSocket) { m->SSDPSocket = mDNSPlatformUDPSocket(m, zeroIPPort); debugf("LNT_SendDiscoveryMsg created SSDPSocket %p", &m->SSDPSocket); } - mDNSPlatformSendUDP(m, buf, buf + bufLen, 0, m->SSDPSocket, &m->Router, SSDPPort); - mDNSPlatformSendUDP(m, buf, buf + bufLen, 0, m->SSDPSocket, &multicastDest, SSDPPort); - } - - m->SSDPWANPPPConnection = !m->SSDPWANPPPConnection; - } +{ + static const char msg[] = + "M-SEARCH * HTTP/1.1\r\n" + "Host:239.255.255.250:1900\r\n" + "ST:urn:schemas-upnp-org:service:WAN%sConnection:1\r\n" + "Man:\"ssdp:discover\"\r\n" + "MX:3\r\n\r\n"; + static const mDNSAddr multicastDest = { mDNSAddrType_IPv4, { { { 239, 255, 255, 250 } } } }; + + mDNSu8 *buf = (mDNSu8*)&m->omsg; //m->omsg is 8952 bytes, which is plenty + unsigned int bufLen; + + if (!mDNSIPPortIsZero(m->UPnPRouterPort)) + { + if (m->SSDPSocket) { debugf("LNT_SendDiscoveryMsg destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; } + if (mDNSIPPortIsZero(m->UPnPSOAPPort) && !m->tcpDeviceInfo.sock) GetDeviceDescription(m, &m->tcpDeviceInfo); + return; + } + + // Always query for WANIPConnection in the first SSDP packet + if (m->retryIntervalGetAddr <= NATMAP_INIT_RETRY) m->SSDPWANPPPConnection = mDNSfalse; + + // Create message + bufLen = mDNS_snprintf((char*)buf, sizeof(m->omsg), msg, m->SSDPWANPPPConnection ? "PPP" : "IP"); + + debugf("LNT_SendDiscoveryMsg Router %.4a Current External Address %.4a", &m->Router.ip.v4, &m->ExternalAddress); + + if (!mDNSIPv4AddressIsZero(m->Router.ip.v4)) + { + if (!m->SSDPSocket) { m->SSDPSocket = mDNSPlatformUDPSocket(m, zeroIPPort); debugf("LNT_SendDiscoveryMsg created SSDPSocket %p", &m->SSDPSocket); } + mDNSPlatformSendUDP(m, buf, buf + bufLen, 0, m->SSDPSocket, &m->Router, SSDPPort, mDNSfalse); + mDNSPlatformSendUDP(m, buf, buf + bufLen, 0, m->SSDPSocket, &multicastDest, SSDPPort, mDNSfalse); + } + + m->SSDPWANPPPConnection = !m->SSDPWANPPPConnection; +} mDNSexport void LNT_ClearState(mDNS *const m) - { - if (m->tcpAddrInfo.sock) { mDNSPlatformTCPCloseConnection(m->tcpAddrInfo.sock); m->tcpAddrInfo.sock = mDNSNULL; } - if (m->tcpDeviceInfo.sock) { mDNSPlatformTCPCloseConnection(m->tcpDeviceInfo.sock); m->tcpDeviceInfo.sock = mDNSNULL; } - m->UPnPSOAPPort = m->UPnPRouterPort = zeroIPPort; // Reset UPnP ports - } +{ + if (m->tcpAddrInfo.sock) { mDNSPlatformTCPCloseConnection(m->tcpAddrInfo.sock); m->tcpAddrInfo.sock = mDNSNULL; } + if (m->tcpDeviceInfo.sock) { mDNSPlatformTCPCloseConnection(m->tcpDeviceInfo.sock); m->tcpDeviceInfo.sock = mDNSNULL; } + m->UPnPSOAPPort = m->UPnPRouterPort = zeroIPPort; // Reset UPnP ports +} #endif /* _LEGACY_NAT_TRAVERSAL_ */ diff --git a/mDNSMacOSX/P2PPacketFilter.c b/mDNSMacOSX/P2PPacketFilter.c index c61c7b1..1ab8a02 100644 --- a/mDNSMacOSX/P2PPacketFilter.c +++ b/mDNSMacOSX/P2PPacketFilter.c @@ -25,9 +25,9 @@ #include #include "P2PPacketFilter.h" -#define AIRDROP_ANCHOR_PATH "com.apple/200.AirDrop" -#define MDNS_ANCHOR_NAME "Bonjour" -#define MDNS_ANCHOR_PATH AIRDROP_ANCHOR_PATH "/" MDNS_ANCHOR_NAME +#define AIRDROP_ANCHOR_PATH "com.apple/200.AirDrop" +#define MDNS_ANCHOR_NAME "Bonjour" +#define MDNS_ANCHOR_PATH AIRDROP_ANCHOR_PATH "/" MDNS_ANCHOR_NAME #define PF_DEV_PATH "/dev/pf" #define BONJOUR_PORT 5353 @@ -36,7 +36,7 @@ static int openPFDevice( int * outFD ) { int err; int fd = open( PF_DEV_PATH, O_RDWR ); - + if( fd >= 0 ) { err = 0; @@ -46,25 +46,25 @@ static int openPFDevice( int * outFD ) { err = errno; } - + return err; } static int getTicket( int devFD, u_int32_t * outTicket, char * anchorPath ) { struct pfioc_trans_e trans_e; - + trans_e.rs_num = PF_RULESET_FILTER; strlcpy( trans_e.anchor, anchorPath, sizeof( trans_e.anchor ) ); - + struct pfioc_trans trans; - + trans.size = 1; trans.esize = sizeof( trans_e ); trans.array = &trans_e; - + int result, ioctlError; - + ioctlError = ioctl( devFD, DIOCXBEGIN, &trans ); if( ioctlError ) { @@ -75,41 +75,41 @@ static int getTicket( int devFD, u_int32_t * outTicket, char * anchorPath ) result = 0; *outTicket = trans_e.ticket; } - + return result; } static int commitChange( int devFD, u_int32_t ticket, char * anchorPath ) { struct pfioc_trans_e trans_e; - + trans_e.rs_num = PF_RULESET_FILTER; strlcpy( trans_e.anchor, anchorPath, sizeof( trans_e.anchor ) ); trans_e.ticket = ticket; - + struct pfioc_trans trans; - + trans.size = 1; trans.esize = sizeof( trans_e ); trans.array = &trans_e; - + int result, ioctlError; - + ioctlError = ioctl( devFD, DIOCXCOMMIT, &trans ); if( ioctlError ) result = errno; else result = 0; - + return result; } static int getPoolTicket( int devFD, u_int32_t * outPoolTicket ) { struct pfioc_pooladdr pp; - + int result, ioctlError; - + ioctlError = ioctl( devFD, DIOCBEGINADDRS, &pp ); if( ioctlError ) { @@ -120,27 +120,27 @@ static int getPoolTicket( int devFD, u_int32_t * outPoolTicket ) result = 0; *outPoolTicket = pp.ticket; } - + return result; } static int addRule( int devFD, struct pfioc_rule * pr ) { int result, ioctlResult; - + ioctlResult = ioctl( devFD, DIOCADDRULE, pr ); if( ioctlResult ) result = errno; else result = 0; - + return result; } static void initRuleHeader( struct pfioc_rule * pr, - u_int32_t ticket, - u_int32_t poolTicket, - char * anchorPath ) + u_int32_t ticket, + u_int32_t poolTicket, + char * anchorPath ) { pr->action = PF_CHANGE_NONE; pr->ticket = ticket; @@ -148,24 +148,24 @@ static void initRuleHeader( struct pfioc_rule * pr, strlcpy( pr->anchor, anchorPath, sizeof( pr->anchor ) ); } -// allow inbound traffice on the Bonjour port (5353) +// allow inbound traffice on the Bonjour port (5353) static void initBonjourRule( struct pfioc_rule * pr, - const char * interfaceName, - u_int32_t ticket, - u_int32_t poolTicket, - char * anchorPath ) + const char * interfaceName, + u_int32_t ticket, + u_int32_t poolTicket, + char * anchorPath ) { memset( pr, 0, sizeof( *pr ) ); - + // Header initRuleHeader( pr, ticket, poolTicket, anchorPath ); - + // Rule pr->rule.dst.xport.range.port[0] = htons( BONJOUR_PORT ); pr->rule.dst.xport.range.op = PF_OP_EQ; - + strlcpy( pr->rule.ifname, interfaceName, sizeof( pr->rule.ifname ) ); - + pr->rule.action = PF_PASS; pr->rule.direction = PF_IN; pr->rule.keep_state = 1; @@ -176,19 +176,19 @@ static void initBonjourRule( struct pfioc_rule * pr, // allow outbound TCP connections and return traffic for those connections static void initOutboundTCPRule( struct pfioc_rule * pr, - const char * interfaceName, - u_int32_t ticket, - u_int32_t poolTicket, - char * anchorPath ) + const char * interfaceName, + u_int32_t ticket, + u_int32_t poolTicket, + char * anchorPath ) { memset( pr, 0, sizeof( *pr ) ); - + // Header initRuleHeader( pr, ticket, poolTicket, anchorPath ); - + // Rule strlcpy( pr->rule.ifname, interfaceName, sizeof( pr->rule.ifname ) ); - + pr->rule.action = PF_PASS; pr->rule.direction = PF_OUT; pr->rule.keep_state = 1; @@ -197,25 +197,25 @@ static void initOutboundTCPRule( struct pfioc_rule * pr, // allow inbound traffic on the specified port and protocol static void initPortRule( struct pfioc_rule * pr, - const char * interfaceName, - u_int32_t ticket, - u_int32_t poolTicket, - char * anchorPath, - u_int16_t port, - u_int16_t protocol ) + const char * interfaceName, + u_int32_t ticket, + u_int32_t poolTicket, + char * anchorPath, + u_int16_t port, + u_int16_t protocol ) { memset( pr, 0, sizeof( *pr ) ); - + // Header initRuleHeader( pr, ticket, poolTicket, anchorPath ); - + // Rule - // mDNSResponder passes the port in Network Byte Order, so htons(port) is not required + // mDNSResponder passes the port in Network Byte Order, so htons(port) is not required pr->rule.dst.xport.range.port[0] = port; pr->rule.dst.xport.range.op = PF_OP_EQ; - + strlcpy( pr->rule.ifname, interfaceName, sizeof( pr->rule.ifname ) ); - + pr->rule.action = PF_PASS; pr->rule.direction = PF_IN; pr->rule.keep_state = 1; @@ -224,74 +224,75 @@ static void initPortRule( struct pfioc_rule * pr, pr->rule.extfilter = PF_EXTFILTER_APD; } -// allow inbound traffice on the Bonjour port (5353) and the specified port and protocol -int P2PPacketFilterAddBonjourRuleSet(const char * interfaceName, u_int16_t port, u_int16_t protocol ) +// allow inbound traffic on the Bonjour port (5353) and the specified port and protocol sets +int P2PPacketFilterAddBonjourRuleSet(const char * interfaceName, u_int32_t count, pfArray_t portArray, pfArray_t protocolArray ) { int result; - u_int32_t ticket, poolTicket; + u_int32_t i, ticket, poolTicket; int devFD = -1; - char * anchorPath = MDNS_ANCHOR_PATH; - + char * anchorPath = MDNS_ANCHOR_PATH; + result = openPFDevice( &devFD ); require( result == 0, exit ); - + result = getTicket( devFD, &ticket, anchorPath ); require( result == 0, exit ); - + result = getPoolTicket( devFD, &poolTicket ); require( result == 0, exit ); - + struct pfioc_rule pr; - - // allow inbound Bonjour traffice to port 5353 + + // allow inbound Bonjour traffice to port 5353 initBonjourRule( &pr, interfaceName, ticket, poolTicket, anchorPath); - - result = addRule( devFD, &pr ); - require( result == 0, exit ); - - // open inbound port for service - initPortRule( &pr, interfaceName, ticket, poolTicket, anchorPath, port, protocol ); - + result = addRule( devFD, &pr ); require( result == 0, exit ); - + + // open inbound port for each service + for (i = 0; i < count; i++) { + initPortRule( &pr, interfaceName, ticket, poolTicket, anchorPath, portArray[i], protocolArray[i] ); + result = addRule( devFD, &pr ); + require( result == 0, exit ); + } + // allow outbound TCP connections and return traffic for those connections initOutboundTCPRule( &pr, interfaceName, ticket, poolTicket, anchorPath); - + result = addRule( devFD, &pr ); require( result == 0, exit ); - + result = commitChange( devFD, ticket, anchorPath ); require( result == 0, exit ); - + exit: - + if( devFD >= 0 ) close( devFD ); - + return result; } int P2PPacketFilterClearBonjourRules() { - int result; - int pfDev = -1; - u_int32_t ticket; - char * anchorPath = MDNS_ANCHOR_PATH; - - result = openPFDevice( &pfDev ); - require( result == 0, exit ); - - result = getTicket( pfDev, &ticket, anchorPath ); - require( result == 0, exit ); - - result = commitChange( pfDev, ticket, anchorPath ); - + int result; + int pfDev = -1; + u_int32_t ticket; + char * anchorPath = MDNS_ANCHOR_PATH; + + result = openPFDevice( &pfDev ); + require( result == 0, exit ); + + result = getTicket( pfDev, &ticket, anchorPath ); + require( result == 0, exit ); + + result = commitChange( pfDev, ticket, anchorPath ); + exit: - - if( pfDev >= 0 ) - close( pfDev ); - return result; + if( pfDev >= 0 ) + close( pfDev ); + + return result; } diff --git a/mDNSMacOSX/P2PPacketFilter.h b/mDNSMacOSX/P2PPacketFilter.h index 81bd3df..9c19657 100644 --- a/mDNSMacOSX/P2PPacketFilter.h +++ b/mDNSMacOSX/P2PPacketFilter.h @@ -18,12 +18,14 @@ #ifndef _P2P_PACKET_FILTER_H_ #define _P2P_PACKET_FILTER_H_ +#include "helpermsg-types.h" + enum { - PF_SET_RULES, - PF_CLEAR_RULES + PF_SET_RULES, + PF_CLEAR_RULES }; -int P2PPacketFilterAddBonjourRuleSet(const char * interfaceName, u_int16_t port, u_int16_t protocol ); +int P2PPacketFilterAddBonjourRuleSet(const char * interfaceName, u_int32_t count, pfArray_t portArray, pfArray_t protocolArray ); int P2PPacketFilterClearBonjourRules(void); #endif /* _P2P_PACKET_FILTER_H_ */ diff --git a/mDNSMacOSX/PreferencePane/ConfigurationAuthority.c b/mDNSMacOSX/PreferencePane/ConfigurationAuthority.c index d5b2d1a..a2ab546 100644 --- a/mDNSMacOSX/PreferencePane/ConfigurationAuthority.c +++ b/mDNSMacOSX/PreferencePane/ConfigurationAuthority.c @@ -48,133 +48,133 @@ #include -static AuthorizationRef gAuthRef = 0; +static AuthorizationRef gAuthRef = 0; -static AuthorizationItem gAuthorizations[] = { { UPDATE_SC_RIGHT, 0, NULL, 0 }, - { EDIT_SYS_KEYCHAIN_RIGHT, 0, NULL, 0 }}; -static AuthorizationRights gAuthSet = { sizeof gAuthorizations / sizeof gAuthorizations[0], gAuthorizations }; +static AuthorizationItem gAuthorizations[] = { { UPDATE_SC_RIGHT, 0, NULL, 0 }, + { EDIT_SYS_KEYCHAIN_RIGHT, 0, NULL, 0 }}; +static AuthorizationRights gAuthSet = { sizeof gAuthorizations / sizeof gAuthorizations[0], gAuthorizations }; -static CFDictionaryRef CreateRightsDict( CFStringRef prompt) +static CFDictionaryRef CreateRightsDict( CFStringRef prompt) /* Create a CFDictionary decribing an auth right. See /etc/authorization for examples. */ /* Specifies that the right requires admin authentication, which persists for 5 minutes. */ { - CFMutableDictionaryRef dict = NULL, tmpDict; - CFMutableArrayRef mechanisms; - CFNumberRef timeout; - int val; - - tmpDict = CFDictionaryCreateMutable( (CFAllocatorRef) NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - require( tmpDict != NULL, MakeDictFailed); - - CFDictionaryAddValue(tmpDict, CFSTR("class"), CFSTR("user")); - CFDictionaryAddValue(tmpDict, CFSTR("comment"), prompt); - CFDictionaryAddValue(tmpDict, CFSTR("group"), CFSTR("admin")); - - mechanisms = CFArrayCreateMutable((CFAllocatorRef) NULL, 1, &kCFTypeArrayCallBacks); - require( mechanisms != NULL, MakeArrayFailed); - CFArrayAppendValue( mechanisms, CFSTR("builtin:authenticate")); - CFDictionaryAddValue( tmpDict, CFSTR("mechanisms"), mechanisms); - - val = 300; // seconds - timeout = CFNumberCreate((CFAllocatorRef) NULL, kCFNumberIntType, &val); - require( timeout != NULL, MakeIntFailed); - CFDictionaryAddValue( tmpDict, CFSTR("timeout"), timeout); - CFDictionaryAddValue( tmpDict, CFSTR("shared"), kCFBooleanTrue); - - dict = tmpDict; - tmpDict = NULL; - - CFRelease( timeout); + CFMutableDictionaryRef dict = NULL, tmpDict; + CFMutableArrayRef mechanisms; + CFNumberRef timeout; + int val; + + tmpDict = CFDictionaryCreateMutable( (CFAllocatorRef) NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + require( tmpDict != NULL, MakeDictFailed); + + CFDictionaryAddValue(tmpDict, CFSTR("class"), CFSTR("user")); + CFDictionaryAddValue(tmpDict, CFSTR("comment"), prompt); + CFDictionaryAddValue(tmpDict, CFSTR("group"), CFSTR("admin")); + + mechanisms = CFArrayCreateMutable((CFAllocatorRef) NULL, 1, &kCFTypeArrayCallBacks); + require( mechanisms != NULL, MakeArrayFailed); + CFArrayAppendValue( mechanisms, CFSTR("builtin:authenticate")); + CFDictionaryAddValue( tmpDict, CFSTR("mechanisms"), mechanisms); + + val = 300; // seconds + timeout = CFNumberCreate((CFAllocatorRef) NULL, kCFNumberIntType, &val); + require( timeout != NULL, MakeIntFailed); + CFDictionaryAddValue( tmpDict, CFSTR("timeout"), timeout); + CFDictionaryAddValue( tmpDict, CFSTR("shared"), kCFBooleanTrue); + + dict = tmpDict; + tmpDict = NULL; + + CFRelease( timeout); MakeIntFailed: - CFRelease( mechanisms); + CFRelease( mechanisms); MakeArrayFailed: - if ( tmpDict) - CFRelease( tmpDict); + if ( tmpDict) + CFRelease( tmpDict); MakeDictFailed: - return dict; + return dict; } OSStatus InitConfigAuthority(void) /* Initialize the authorization record-keeping */ { - OSStatus err; - CFDictionaryRef dict; - CFStringRef rightInfo; - - err = AuthorizationCreate((AuthorizationRights*) NULL, (AuthorizationEnvironment*) NULL, - (AuthorizationFlags) 0, &gAuthRef); - require_noerr( err, NewAuthFailed); - - err = AuthorizationRightGet( UPDATE_SC_RIGHT, (CFDictionaryRef*) NULL); - if (err == errAuthorizationDenied) - { - rightInfo = CFCopyLocalizedString(CFSTR("Authentication required to set Dynamic DNS preferences."), - CFSTR("Describes operation that requires user authorization")); - require_action( rightInfo != NULL, GetStrFailed, err=coreFoundationUnknownErr;); - dict = CreateRightsDict(rightInfo); - require_action( dict != NULL, GetStrFailed, err=coreFoundationUnknownErr;); - - err = AuthorizationRightSet(gAuthRef, UPDATE_SC_RIGHT, dict, (CFStringRef) NULL, - (CFBundleRef) NULL, (CFStringRef) NULL); - CFRelease(rightInfo); - CFRelease(dict); - } - require_noerr( err, AuthSetFailed); - - err = AuthorizationRightGet( EDIT_SYS_KEYCHAIN_RIGHT, (CFDictionaryRef*) NULL); - if (err == errAuthorizationDenied) - { - rightInfo = CFCopyLocalizedString( CFSTR("Authentication required to edit System Keychain."), - CFSTR("Describes operation that requires user authorization")); - require_action( rightInfo != NULL, GetStrFailed, err=coreFoundationUnknownErr;); - dict = CreateRightsDict( rightInfo); - require_action( dict != NULL, GetStrFailed, err=coreFoundationUnknownErr;); - - err = AuthorizationRightSet(gAuthRef, EDIT_SYS_KEYCHAIN_RIGHT, dict, (CFStringRef) NULL, - (CFBundleRef) NULL, (CFStringRef) NULL); - CFRelease( rightInfo); - CFRelease( dict); - } - require_noerr( err, AuthSetFailed); + OSStatus err; + CFDictionaryRef dict; + CFStringRef rightInfo; + + err = AuthorizationCreate((AuthorizationRights*) NULL, (AuthorizationEnvironment*) NULL, + (AuthorizationFlags) 0, &gAuthRef); + require_noerr( err, NewAuthFailed); + + err = AuthorizationRightGet( UPDATE_SC_RIGHT, (CFDictionaryRef*) NULL); + if (err == errAuthorizationDenied) + { + rightInfo = CFCopyLocalizedString(CFSTR("Authentication required to set Dynamic DNS preferences."), + CFSTR("Describes operation that requires user authorization")); + require_action( rightInfo != NULL, GetStrFailed, err=coreFoundationUnknownErr;); + dict = CreateRightsDict(rightInfo); + require_action( dict != NULL, GetStrFailed, err=coreFoundationUnknownErr;); + + err = AuthorizationRightSet(gAuthRef, UPDATE_SC_RIGHT, dict, (CFStringRef) NULL, + (CFBundleRef) NULL, (CFStringRef) NULL); + CFRelease(rightInfo); + CFRelease(dict); + } + require_noerr( err, AuthSetFailed); + + err = AuthorizationRightGet( EDIT_SYS_KEYCHAIN_RIGHT, (CFDictionaryRef*) NULL); + if (err == errAuthorizationDenied) + { + rightInfo = CFCopyLocalizedString( CFSTR("Authentication required to edit System Keychain."), + CFSTR("Describes operation that requires user authorization")); + require_action( rightInfo != NULL, GetStrFailed, err=coreFoundationUnknownErr;); + dict = CreateRightsDict( rightInfo); + require_action( dict != NULL, GetStrFailed, err=coreFoundationUnknownErr;); + + err = AuthorizationRightSet(gAuthRef, EDIT_SYS_KEYCHAIN_RIGHT, dict, (CFStringRef) NULL, + (CFBundleRef) NULL, (CFStringRef) NULL); + CFRelease( rightInfo); + CFRelease( dict); + } + require_noerr( err, AuthSetFailed); AuthSetFailed: GetStrFailed: NewAuthFailed: - return err; + return err; } -OSStatus AttemptAcquireAuthority( Boolean allowUI) +OSStatus AttemptAcquireAuthority( Boolean allowUI) /* Try to get permission for privileged ops, either implicitly or by asking the user for */ /* authority to perform operations (if necessary) */ { - AuthorizationFlags allowFlag = allowUI ? kAuthorizationFlagInteractionAllowed : 0; - OSStatus err; - - err = AuthorizationCopyRights( gAuthRef, &gAuthSet, (AuthorizationEnvironment*) NULL, - kAuthorizationFlagExtendRights | kAuthorizationFlagPreAuthorize | - allowFlag, - (AuthorizationRights**) NULL); - return err; + AuthorizationFlags allowFlag = allowUI ? kAuthorizationFlagInteractionAllowed : 0; + OSStatus err; + + err = AuthorizationCopyRights( gAuthRef, &gAuthSet, (AuthorizationEnvironment*) NULL, + kAuthorizationFlagExtendRights | kAuthorizationFlagPreAuthorize | + allowFlag, + (AuthorizationRights**) NULL); + return err; } OSStatus ReleaseAuthority(void) /* Discard authority to perform operations */ { - (void) AuthorizationFree( gAuthRef, kAuthorizationFlagDefaults); - gAuthRef = 0; - return AuthorizationCreate( (AuthorizationRights*) NULL, (AuthorizationEnvironment*) NULL, - (AuthorizationFlags) 0, &gAuthRef); + (void) AuthorizationFree( gAuthRef, kAuthorizationFlagDefaults); + gAuthRef = 0; + return AuthorizationCreate( (AuthorizationRights*) NULL, (AuthorizationEnvironment*) NULL, + (AuthorizationFlags) 0, &gAuthRef); } -Boolean CurrentlyAuthorized(void) +Boolean CurrentlyAuthorized(void) { - OSStatus err = AttemptAcquireAuthority(true); - return err == noErr; + OSStatus err = AttemptAcquireAuthority(true); + return err == noErr; } OSStatus ExternalizeAuthority(AuthorizationExternalForm *pAuth) /* Package up current authorizations for transfer to another process */ { - return AuthorizationMakeExternalForm(gAuthRef, pAuth); + return AuthorizationMakeExternalForm(gAuthRef, pAuth); } diff --git a/mDNSMacOSX/PreferencePane/ConfigurationAuthority.h b/mDNSMacOSX/PreferencePane/ConfigurationAuthority.h index 3b033bb..49da93d 100644 --- a/mDNSMacOSX/PreferencePane/ConfigurationAuthority.h +++ b/mDNSMacOSX/PreferencePane/ConfigurationAuthority.h @@ -46,7 +46,7 @@ #include OSStatus InitConfigAuthority(void); -Boolean CurrentlyAuthorized(void); +Boolean CurrentlyAuthorized(void); OSStatus AttemptAcquireAuthority(Boolean allowUI); OSStatus ReleaseAuthority(void); OSStatus ExternalizeAuthority(AuthorizationExternalForm *pAuth); diff --git a/mDNSMacOSX/PreferencePane/ConfigurationRights.h b/mDNSMacOSX/PreferencePane/ConfigurationRights.h index a16cbbb..44379c6 100644 --- a/mDNSMacOSX/PreferencePane/ConfigurationRights.h +++ b/mDNSMacOSX/PreferencePane/ConfigurationRights.h @@ -45,5 +45,5 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define UPDATE_SC_RIGHT "system.preferences" -#define EDIT_SYS_KEYCHAIN_RIGHT "system.preferences" +#define UPDATE_SC_RIGHT "system.preferences" +#define EDIT_SYS_KEYCHAIN_RIGHT "system.preferences" diff --git a/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.h b/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.h index 212e711..f058659 100644 --- a/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.h +++ b/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.h @@ -49,13 +49,13 @@ #import typedef struct MyDNSServiceState { - DNSServiceRef service; - CFRunLoopSourceRef source; - CFSocketRef socket; + DNSServiceRef service; + CFRunLoopSourceRef source; + CFSocketRef socket; } MyDNSServiceState; -@interface DNSServiceDiscoveryPref : NSPreferencePane +@interface DNSServiceDiscoveryPref : NSPreferencePane { IBOutlet NSTextField *hostName; IBOutlet NSTextField *sharedSecretName; @@ -68,103 +68,103 @@ typedef struct MyDNSServiceState { IBOutlet NSButton *applyButton; IBOutlet NSButton *revertButton; IBOutlet NSWindow *sharedSecretWindow; - IBOutlet NSWindow *addBrowseDomainWindow; + IBOutlet NSWindow *addBrowseDomainWindow; IBOutlet NSButton *addBrowseDomainButton; - IBOutlet NSButton *removeBrowseDomainButton; + IBOutlet NSButton *removeBrowseDomainButton; IBOutlet NSButton *browseOKButton; IBOutlet NSButton *browseCancelButton; - IBOutlet NSButton *secretOKButton; + IBOutlet NSButton *secretOKButton; IBOutlet NSButton *secretCancelButton; IBOutlet NSImageView *statusImageView; IBOutlet NSTabView *tabView; - IBOutlet NSTableView *browseDomainList; - IBOutlet SFAuthorizationView *comboAuthButton; + IBOutlet NSTableView *browseDomainList; + IBOutlet SFAuthorizationView *comboAuthButton; NSWindow *mainWindow; NSString *currentHostName; NSString *currentRegDomain; - NSArray *currentBrowseDomainsArray; - NSMutableArray *browseDomainsArray; - NSMutableArray *defaultBrowseDomainsArray; + NSArray *currentBrowseDomainsArray; + NSMutableArray *browseDomainsArray; + NSMutableArray *defaultBrowseDomainsArray; NSString *defaultRegDomain; NSString *hostNameSharedSecretName; NSString *hostNameSharedSecretValue; NSString *regSharedSecretName; NSString *regSharedSecretValue; - BOOL currentWideAreaState; - BOOL prefsNeedUpdating; - BOOL toolInstalled; - BOOL browseDomainListEnabled; - BOOL justStartedEditing; - NSImage *successImage; - NSImage *inprogressImage; - NSImage *failureImage; - + BOOL currentWideAreaState; + BOOL prefsNeedUpdating; + BOOL toolInstalled; + BOOL browseDomainListEnabled; + BOOL justStartedEditing; + NSImage *successImage; + NSImage *inprogressImage; + NSImage *failureImage; + MyDNSServiceState regQuery; MyDNSServiceState browseQuery; NSMutableArray *browseDataSource; NSMutableArray *registrationDataSource; } -- (IBAction)applyClicked:(id)sender; -- (IBAction)enableBrowseDomainClicked:(id)sender; -- (IBAction)addBrowseDomainClicked:(id)sender; -- (IBAction)removeBrowseDomainClicked:(id)sender; -- (IBAction)revertClicked:(id)sender; -- (IBAction)changeButtonPressed:(id)sender; -- (IBAction)closeMyCustomSheet:(id)sender; -- (IBAction)comboAction:(id)sender; -- (IBAction)wideAreaCheckBoxChanged:(id)sender; - - -- (NSMutableArray *)browseDataSource; -- (NSMutableArray *)registrationDataSource; -- (NSComboBox *)browseDomainsComboBox; -- (NSComboBox *)regDomainsComboBox; -- (NSString *)currentRegDomain; -- (NSMutableArray *)defaultBrowseDomainsArray; -- (NSArray *)currentBrowseDomainsArray; -- (NSString *)currentHostName; -- (NSString *)defaultRegDomain; -- (void)setDefaultRegDomain:(NSString *)domain; - - - -- (void)enableApplyButton; -- (void)disableApplyButton; -- (void)applyCurrentState; -- (void)setBrowseDomainsComboBox; -- (void)setupInitialValues; -- (void)startDomainBrowsing; -- (void)toggleWideAreaBonjour:(BOOL)state; -- (void)updateApplyButtonState; -- (void)enableControls; -- (void)disableControls; -- (void)validateTextFields; -- (void)readPreferences; -- (void)savePreferences; -- (void)restorePreferences; -- (void)watchForPreferenceChanges; -- (void)updateStatusImageView; - - -- (NSString *)sharedSecretKeyName:(NSString * )domain; -- (NSString *)domainForHostName:(NSString *)hostNameString; -- (int)statusForHostName:(NSString * )domain; -- (NSData *)dataForDomainArray:(NSArray *)domainArray; -- (NSData *)dataForDomain:(NSString *)domainName isEnabled:(BOOL)enabled; -- (NSData *)dataForSharedSecret:(NSString *)secret domain:(NSString *)domainName key:(NSString *)keyName; -- (BOOL)domainAlreadyInList:(NSString *)domainString; -- (NSString *)trimCharactersFromDomain:(NSString *)domain; +-(IBAction)applyClicked : (id)sender; +-(IBAction)enableBrowseDomainClicked : (id)sender; +-(IBAction)addBrowseDomainClicked : (id)sender; +-(IBAction)removeBrowseDomainClicked : (id)sender; +-(IBAction)revertClicked : (id)sender; +-(IBAction)changeButtonPressed : (id)sender; +-(IBAction)closeMyCustomSheet : (id)sender; +-(IBAction)comboAction : (id)sender; +-(IBAction)wideAreaCheckBoxChanged : (id)sender; + + +-(NSMutableArray *)browseDataSource; +-(NSMutableArray *)registrationDataSource; +-(NSComboBox *)browseDomainsComboBox; +-(NSComboBox *)regDomainsComboBox; +-(NSString *)currentRegDomain; +-(NSMutableArray *)defaultBrowseDomainsArray; +-(NSArray *)currentBrowseDomainsArray; +-(NSString *)currentHostName; +-(NSString *)defaultRegDomain; +-(void)setDefaultRegDomain : (NSString *)domain; + + + +-(void)enableApplyButton; +-(void)disableApplyButton; +-(void)applyCurrentState; +-(void)setBrowseDomainsComboBox; +-(void)setupInitialValues; +-(void)startDomainBrowsing; +-(void)toggleWideAreaBonjour : (BOOL)state; +-(void)updateApplyButtonState; +-(void)enableControls; +-(void)disableControls; +-(void)validateTextFields; +-(void)readPreferences; +-(void)savePreferences; +-(void)restorePreferences; +-(void)watchForPreferenceChanges; +-(void)updateStatusImageView; + + +-(NSString *)sharedSecretKeyName : (NSString * )domain; +-(NSString *)domainForHostName : (NSString *)hostNameString; +-(int)statusForHostName : (NSString * )domain; +-(NSData *)dataForDomainArray : (NSArray *)domainArray; +-(NSData *)dataForDomain : (NSString *)domainName isEnabled : (BOOL)enabled; +-(NSData *)dataForSharedSecret : (NSString *)secret domain : (NSString *)domainName key : (NSString *)keyName; +-(BOOL)domainAlreadyInList : (NSString *)domainString; +-(NSString *)trimCharactersFromDomain : (NSString *)domain; // Delegate methods -- (void)authorizationViewDidAuthorize:(SFAuthorizationView *)view; -- (void)authorizationViewDidDeauthorize:(SFAuthorizationView *)view; -- (void)mainViewDidLoad; -- (int)numberOfItemsInComboBox:(NSComboBox *)aComboBox; -- (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(int)index; -- (void)controlTextDidChange:(NSNotification *) notification; +-(void)authorizationViewDidAuthorize : (SFAuthorizationView *)view; +-(void)authorizationViewDidDeauthorize : (SFAuthorizationView *)view; +-(void)mainViewDidLoad; +-(int)numberOfItemsInComboBox : (NSComboBox *)aComboBox; +-(id)comboBox : (NSComboBox *)aComboBox objectValueForItemAtIndex : (int)index; +-(void)controlTextDidChange : (NSNotification *) notification; @end diff --git a/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.m b/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.m index 6e5db80..063bc72 100644 --- a/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.m +++ b/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.m @@ -54,15 +54,14 @@ @implementation DNSServiceDiscoveryPref -static NSComparisonResult +static NSInteger MyArrayCompareFunction(id val1, id val2, void *context) { (void)context; // Unused return CFStringCompare((CFStringRef)val1, (CFStringRef)val2, kCFCompareCaseInsensitive); } - -static NSComparisonResult +static NSInteger MyDomainArrayCompareFunction(id val1, id val2, void *context) { (void)context; // Unused diff --git a/mDNSMacOSX/PreferencePane/PrivilegedOperations.c b/mDNSMacOSX/PreferencePane/PrivilegedOperations.c index 49d35b9..4c0ffa0 100644 --- a/mDNSMacOSX/PreferencePane/PrivilegedOperations.c +++ b/mDNSMacOSX/PreferencePane/PrivilegedOperations.c @@ -53,178 +53,178 @@ #include #include -Boolean gToolApproved = false; +Boolean gToolApproved = false; -static pid_t execTool(const char *args[]) +static pid_t execTool(const char *args[]) // fork/exec and return new pid { - pid_t child; - - child = vfork(); - if (child == 0) - { - execv(args[0], (char *const *)args); -printf("exec of %s failed; errno = %d\n", args[0], errno); - _exit(-1); // exec failed - } - else - return child; + pid_t child; + + child = vfork(); + if (child == 0) + { + execv(args[0], (char *const *)args); + printf("exec of %s failed; errno = %d\n", args[0], errno); + _exit(-1); // exec failed + } + else + return child; } OSStatus EnsureToolInstalled(void) // Make sure that the tool is installed in the right place, with the right privs, and the right version. { - CFURLRef bundleURL; - pid_t toolPID; - int status; - OSStatus err = noErr; - const char *args[] = { kToolPath, "0", "V", NULL }; - char toolSourcePath[PATH_MAX] = {}; - char toolInstallerPath[PATH_MAX] = {}; - - if (gToolApproved) - return noErr; - - // Check version of installed tool - toolPID = execTool(args); - if (toolPID > 0) - { - waitpid(toolPID, &status, 0); - if (WIFEXITED(status) && WEXITSTATUS(status) == PRIV_OP_TOOL_VERS) - return noErr; - } - - // Locate our in-bundle copy of privop tool - bundleURL = CFBundleCopyBundleURL(CFBundleGetBundleWithIdentifier(CFSTR("com.apple.preference.bonjour")) ); - if (bundleURL != NULL) - { - CFURLGetFileSystemRepresentation(bundleURL, false, (UInt8*) toolSourcePath, sizeof toolSourcePath); - if (strlcat(toolSourcePath, "/Contents/Resources/" kToolName, sizeof toolSourcePath ) >= sizeof toolSourcePath ) return(-1); - CFURLGetFileSystemRepresentation(bundleURL, false, (UInt8*) toolInstallerPath, sizeof toolInstallerPath); - if (strlcat(toolInstallerPath, "/Contents/Resources/" kToolInstaller, sizeof toolInstallerPath) >= sizeof toolInstallerPath) return(-1); - } - else - return coreFoundationUnknownErr; - - // Obtain authorization and run in-bundle copy as root to install it - { - AuthorizationItem aewpRight = { kAuthorizationRightExecute, strlen(toolInstallerPath), toolInstallerPath, 0 }; - AuthorizationItemSet rights = { 1, &aewpRight }; - AuthorizationRef authRef; - - err = AuthorizationCreate(&rights, (AuthorizationEnvironment*) NULL, - kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights | - kAuthorizationFlagPreAuthorize, &authRef); - if (err == noErr) - { - char *installerargs[] = { toolSourcePath, NULL }; - err = AuthorizationExecuteWithPrivileges(authRef, toolInstallerPath, 0, installerargs, (FILE**) NULL); - if (err == noErr) { - int pid = wait(&status); - if (pid > 0 && WIFEXITED(status)) { - err = WEXITSTATUS(status); - if (err == noErr) { - gToolApproved = true; - } - } else { - err = -1; - } - } - (void) AuthorizationFree(authRef, kAuthorizationFlagDefaults); - } - } - - return err; + CFURLRef bundleURL; + pid_t toolPID; + int status; + OSStatus err = noErr; + const char *args[] = { kToolPath, "0", "V", NULL }; + char toolSourcePath[PATH_MAX] = {}; + char toolInstallerPath[PATH_MAX] = {}; + + if (gToolApproved) + return noErr; + + // Check version of installed tool + toolPID = execTool(args); + if (toolPID > 0) + { + waitpid(toolPID, &status, 0); + if (WIFEXITED(status) && WEXITSTATUS(status) == PRIV_OP_TOOL_VERS) + return noErr; + } + + // Locate our in-bundle copy of privop tool + bundleURL = CFBundleCopyBundleURL(CFBundleGetBundleWithIdentifier(CFSTR("com.apple.preference.bonjour")) ); + if (bundleURL != NULL) + { + CFURLGetFileSystemRepresentation(bundleURL, false, (UInt8*) toolSourcePath, sizeof toolSourcePath); + if (strlcat(toolSourcePath, "/Contents/Resources/" kToolName, sizeof toolSourcePath ) >= sizeof toolSourcePath ) return(-1); + CFURLGetFileSystemRepresentation(bundleURL, false, (UInt8*) toolInstallerPath, sizeof toolInstallerPath); + if (strlcat(toolInstallerPath, "/Contents/Resources/" kToolInstaller, sizeof toolInstallerPath) >= sizeof toolInstallerPath) return(-1); + } + else + return coreFoundationUnknownErr; + + // Obtain authorization and run in-bundle copy as root to install it + { + AuthorizationItem aewpRight = { kAuthorizationRightExecute, strlen(toolInstallerPath), toolInstallerPath, 0 }; + AuthorizationItemSet rights = { 1, &aewpRight }; + AuthorizationRef authRef; + + err = AuthorizationCreate(&rights, (AuthorizationEnvironment*) NULL, + kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights | + kAuthorizationFlagPreAuthorize, &authRef); + if (err == noErr) + { + char *installerargs[] = { toolSourcePath, NULL }; + err = AuthorizationExecuteWithPrivileges(authRef, toolInstallerPath, 0, installerargs, (FILE**) NULL); + if (err == noErr) { + int pid = wait(&status); + if (pid > 0 && WIFEXITED(status)) { + err = WEXITSTATUS(status); + if (err == noErr) { + gToolApproved = true; + } + } else { + err = -1; + } + } + (void) AuthorizationFree(authRef, kAuthorizationFlagDefaults); + } + } + + return err; } -static OSStatus ExecWithCmdAndParam(const char *subCmd, CFDataRef paramData) +static OSStatus ExecWithCmdAndParam(const char *subCmd, CFDataRef paramData) // Execute our privop tool with the supplied subCmd and parameter { - OSStatus err = noErr; - int commFD, dataLen; - u_int32_t len; - pid_t child; - char fileNum[16]; - UInt8 *buff; - const char *args[] = { kToolPath, NULL, "A", NULL, NULL }; - AuthorizationExternalForm authExt; - - err = ExternalizeAuthority(&authExt); - require_noerr(err, AuthFailed); - - dataLen = CFDataGetLength(paramData); - buff = (UInt8*) malloc(dataLen * sizeof(UInt8)); - require_action(buff != NULL, AllocBuffFailed, err=memFullErr;); - { - CFRange all = { 0, dataLen }; - CFDataGetBytes(paramData, all, buff); - } - - commFD = fileno(tmpfile()); - sprintf(fileNum, "%d", commFD); - args[1] = fileNum; - args[3] = subCmd; - - // write authority to pipe - len = 0; // tag, unused - write(commFD, &len, sizeof len); - len = sizeof authExt; // len - write(commFD, &len, sizeof len); - write(commFD, &authExt, len); - - // write parameter to pipe - len = 0; // tag, unused - write(commFD, &len, sizeof len); - len = dataLen; // len - write(commFD, &len, sizeof len); - write(commFD, buff, len); - - child = execTool(args); - if (child > 0) { - int status; - waitpid(child, &status, 0); - if (WIFEXITED(status)) - err = WEXITSTATUS(status); - //fprintf(stderr, "child exited; status = %d (%ld)\n", status, err); - } - - close(commFD); - - free(buff); + OSStatus err = noErr; + int commFD, dataLen; + u_int32_t len; + pid_t child; + char fileNum[16]; + UInt8 *buff; + const char *args[] = { kToolPath, NULL, "A", NULL, NULL }; + AuthorizationExternalForm authExt; + + err = ExternalizeAuthority(&authExt); + require_noerr(err, AuthFailed); + + dataLen = CFDataGetLength(paramData); + buff = (UInt8*) malloc(dataLen * sizeof(UInt8)); + require_action(buff != NULL, AllocBuffFailed, err=memFullErr;); + { + CFRange all = { 0, dataLen }; + CFDataGetBytes(paramData, all, buff); + } + + commFD = fileno(tmpfile()); + sprintf(fileNum, "%d", commFD); + args[1] = fileNum; + args[3] = subCmd; + + // write authority to pipe + len = 0; // tag, unused + write(commFD, &len, sizeof len); + len = sizeof authExt; // len + write(commFD, &len, sizeof len); + write(commFD, &authExt, len); + + // write parameter to pipe + len = 0; // tag, unused + write(commFD, &len, sizeof len); + len = dataLen; // len + write(commFD, &len, sizeof len); + write(commFD, buff, len); + + child = execTool(args); + if (child > 0) { + int status; + waitpid(child, &status, 0); + if (WIFEXITED(status)) + err = WEXITSTATUS(status); + //fprintf(stderr, "child exited; status = %d (%ld)\n", status, err); + } + + close(commFD); + + free(buff); AllocBuffFailed: AuthFailed: - return err; + return err; } OSStatus WriteBrowseDomain(CFDataRef domainArrayData) { - if (!CurrentlyAuthorized()) - return authFailErr; - return ExecWithCmdAndParam("Wb", domainArrayData); + if (!CurrentlyAuthorized()) + return authFailErr; + return ExecWithCmdAndParam("Wb", domainArrayData); } OSStatus WriteRegistrationDomain(CFDataRef domainArrayData) { - if (!CurrentlyAuthorized()) - return authFailErr; - return ExecWithCmdAndParam("Wd", domainArrayData); + if (!CurrentlyAuthorized()) + return authFailErr; + return ExecWithCmdAndParam("Wd", domainArrayData); } OSStatus WriteHostname(CFDataRef domainArrayData) { - if (!CurrentlyAuthorized()) - return authFailErr; - return ExecWithCmdAndParam("Wh", domainArrayData); + if (!CurrentlyAuthorized()) + return authFailErr; + return ExecWithCmdAndParam("Wh", domainArrayData); } OSStatus SetKeyForDomain(CFDataRef secretData) { - if (!CurrentlyAuthorized()) - return authFailErr; - return ExecWithCmdAndParam("Wk", secretData); + if (!CurrentlyAuthorized()) + return authFailErr; + return ExecWithCmdAndParam("Wk", secretData); } diff --git a/mDNSMacOSX/PreferencePane/PrivilegedOperations.h b/mDNSMacOSX/PreferencePane/PrivilegedOperations.h index 4f9514a..91a60da 100644 --- a/mDNSMacOSX/PreferencePane/PrivilegedOperations.h +++ b/mDNSMacOSX/PreferencePane/PrivilegedOperations.h @@ -44,22 +44,22 @@ #include #include -#define PRIV_OP_TOOL_VERS 4 +#define PRIV_OP_TOOL_VERS 4 -#define kToolName "ddnswriteconfig" -#define kToolPath "/Library/Application Support/Bonjour/" kToolName -#define kToolInstaller "installtool" +#define kToolName "ddnswriteconfig" +#define kToolPath "/Library/Application Support/Bonjour/" kToolName +#define kToolInstaller "installtool" -#define SC_DYNDNS_SETUP_KEY CFSTR("Setup:/Network/DynamicDNS") -#define SC_DYNDNS_STATE_KEY CFSTR("State:/Network/DynamicDNS") -#define SC_DYNDNS_REGDOMAINS_KEY CFSTR("RegistrationDomains") -#define SC_DYNDNS_BROWSEDOMAINS_KEY CFSTR("BrowseDomains") -#define SC_DYNDNS_HOSTNAMES_KEY CFSTR("HostNames") -#define SC_DYNDNS_DOMAIN_KEY CFSTR("Domain") -#define SC_DYNDNS_KEYNAME_KEY CFSTR("KeyName") -#define SC_DYNDNS_SECRET_KEY CFSTR("Secret") -#define SC_DYNDNS_ENABLED_KEY CFSTR("Enabled") -#define SC_DYNDNS_STATUS_KEY CFSTR("Status") +#define SC_DYNDNS_SETUP_KEY CFSTR("Setup:/Network/DynamicDNS") +#define SC_DYNDNS_STATE_KEY CFSTR("State:/Network/DynamicDNS") +#define SC_DYNDNS_REGDOMAINS_KEY CFSTR("RegistrationDomains") +#define SC_DYNDNS_BROWSEDOMAINS_KEY CFSTR("BrowseDomains") +#define SC_DYNDNS_HOSTNAMES_KEY CFSTR("HostNames") +#define SC_DYNDNS_DOMAIN_KEY CFSTR("Domain") +#define SC_DYNDNS_KEYNAME_KEY CFSTR("KeyName") +#define SC_DYNDNS_SECRET_KEY CFSTR("Secret") +#define SC_DYNDNS_ENABLED_KEY CFSTR("Enabled") +#define SC_DYNDNS_STATUS_KEY CFSTR("Status") #define DYNDNS_KEYCHAIN_DESCRIPTION "Dynamic DNS Key" diff --git a/mDNSMacOSX/README.privsep b/mDNSMacOSX/README.privsep index d687d07..30de198 100644 --- a/mDNSMacOSX/README.privsep +++ b/mDNSMacOSX/README.privsep @@ -20,8 +20,7 @@ needed and handles requests from mDNSResponder. * There are currently six remote procedure calls handled by mDNSResponderHelper: mDNSDynamicStoreSetConfig, mDNSPreferencesSetName, mDNSKeychainGetSecrets, - mDNSAutoTunnelInterfaceUpDown, mDNSConfigureServer, and - mDNSAutoTunnelSetKeys + mDNSConfigureServer, and mDNSAutoTunnelSetKeys * mDNSDynamicStoreSetConfig allows mDNSResponder to set the MulticastDNS, PrivateDNS, or DynamicDNS configurations. @@ -43,5 +42,5 @@ needed and handles requests from mDNSResponder. references whenever a NULL SecKeychainRef is used. Wherever a SecKeychainRef is needed, NULL is now specified. -* mDNSAutoTunnelInterfaceUpDown, mDNSConfigureServer, and - mDNSAutoTunnelSetKeys do various setup and teardown for BTMM. +* mDNSConfigureServer, and mDNSAutoTunnelSetKeys do various setup and + teardown for BTMM. diff --git a/mDNSMacOSX/SamplemDNSClient.c b/mDNSMacOSX/SamplemDNSClient.c deleted file mode 100644 index e75ce59..0000000 --- a/mDNSMacOSX/SamplemDNSClient.c +++ /dev/null @@ -1,441 +0,0 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Formatting notes: - * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion - * on C indentation can be found on the web, such as , - * but for the sake of brevity here I will say just this: Curly braces are not syntactially - * part of an "if" statement; they are the beginning and ending markers of a compound statement; - * therefore common sense dictates that if they are part of a compound statement then they - * should be indented to the same level as everything else in that compound statement. - * Indenting curly braces at the same level as the "if" implies that curly braces are - * part of the "if", which is false. (This is as misleading as people who write "char* x,y;" - * thinking that variables x and y are both of type "char*" -- and anyone who doesn't - * understand why variable y is not of type "char*" just proves the point that poor code - * layout leads people to unfortunate misunderstandings about how the C language really works.) - */ - -#include -#include -#include -#include -#include - -// 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 -#undef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3 -#define AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3 - -#include - -//************************************************************************************************************* -// Globals - -typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16; - -static char operation; -static dns_service_discovery_ref client = NULL; -static int num_printed; -static char addtest = 0; -static DNSRecordReference record; -static char myhinfo9[11] = "\003Mac\006OS 9.2"; -static char myhinfoX[ 9] = "\003Mac\004OS X"; -static char updatetest[3] = "\002AA"; -static char bigNULL[4096]; - -//************************************************************************************************************* -// Supporting Utility Functions -// -// This code takes care of: -// 1. Extracting the mach_port_t from the dns_service_discovery_ref -// 2. Making a CFMachPortRef from it -// 3. Making a CFRunLoopSourceRef from that -// 4. Adding that source to the current RunLoop -// 5. and passing the resulting messages back to DNSServiceDiscovery_handleReply() for processing -// -// Code that's not based around a CFRunLoop will need its own mechanism to receive Mach messages -// from the mDNSResponder daemon and pass them to the DNSServiceDiscovery_handleReply() routine. -// (There is no way to automate this, because it varies depending on the application's existing -// event handling model.) - -static void MyHandleMachMessage(CFMachPortRef port, void *msg, CFIndex size, void *info) - { - (void)port; // Unused - (void)size; // Unused - (void)info; // Unused - DNSServiceDiscovery_handleReply(msg); - } - -static int AddDNSServiceClientToRunLoop(dns_service_discovery_ref c) - { - mach_port_t port = DNSServiceDiscoveryMachPort(c); - if (!port) - return(-1); - else - { - CFMachPortContext context = { 0, 0, NULL, NULL, NULL }; - Boolean shouldFreeInfo; - CFMachPortRef cfMachPort = CFMachPortCreateWithPort(kCFAllocatorDefault, port, MyHandleMachMessage, &context, &shouldFreeInfo); - CFRunLoopSourceRef rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0); - CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); - CFRelease(rls); - return(0); - } - } - -//************************************************************************************************************* -// Sample callback functions for each of the operation types - -static void printtimestamp(void) - { - struct timeval tv; - struct tm tm; - gettimeofday(&tv, NULL); - localtime_r((time_t*)&tv.tv_sec, &tm); - printf("%2d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec/1000); - } - -#define DomainMsg(X) ((X) == DNSServiceDomainEnumerationReplyAddDomain ? "Added" : \ - (X) == DNSServiceDomainEnumerationReplyAddDomainDefault ? "(Default)" : \ - (X) == DNSServiceDomainEnumerationReplyRemoveDomain ? "Removed" : "Unknown") - -static void regdom_reply(DNSServiceDomainEnumerationReplyResultType resultType, const char *replyDomain, - DNSServiceDiscoveryReplyFlags flags, void *context) - { - (void)context; // Unused - printtimestamp(); - printf("Recommended Registration Domain %s %s", replyDomain, DomainMsg(resultType)); - if (flags) printf(" Flags: %X", flags); - printf("\n"); - } - -static void browsedom_reply(DNSServiceDomainEnumerationReplyResultType resultType, const char *replyDomain, - DNSServiceDiscoveryReplyFlags flags, void *context) - { - (void)context; // Unused - printtimestamp(); - printf("Recommended Browsing Domain %s %s", replyDomain, DomainMsg(resultType)); - if (flags) printf(" Flags: %X", flags); - printf("\n"); - } - -static void browse_reply(DNSServiceBrowserReplyResultType resultType, - const char *replyName, const char *replyType, const char *replyDomain, DNSServiceDiscoveryReplyFlags flags, void *context) - { - char *op = (resultType == DNSServiceBrowserReplyAddInstance) ? "Add" : "Rmv"; - (void)context; // Unused - if (num_printed++ == 0) printf("Timestamp A/R Flags %-24s %-24s %s\n", "Domain", "Service Type", "Instance Name"); - printtimestamp(); - printf("%s%6X %-24s %-24s %s\n", op, flags, replyDomain, replyType, replyName); - } - -static void resolve_reply(struct sockaddr *interface, struct sockaddr *address, const char *txtRecord, DNSServiceDiscoveryReplyFlags flags, void *context) - { - (void)interface; // Unused - (void)context; // Unused - if (address->sa_family != AF_INET && address->sa_family != AF_INET6) - printf("Unknown address family %d\n", address->sa_family); - else - { - const char *src = txtRecord; - printtimestamp(); - - if (address->sa_family == AF_INET) - { - struct sockaddr_in *ip = (struct sockaddr_in *)address; - union { uint32_t l; u_char b[4]; } addr = { ip->sin_addr.s_addr }; - union { uint16_t s; u_char b[2]; } port = { ip->sin_port }; - uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1]; - char ipstring[16]; - sprintf(ipstring, "%d.%d.%d.%d", addr.b[0], addr.b[1], addr.b[2], addr.b[3]); - printf("Service can be reached at %-15s:%u", ipstring, PortAsNumber); - } - else if (address->sa_family == AF_INET6) - { - struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)address; - u_int8_t *b = ip6->sin6_addr.__u6_addr.__u6_addr8; - union { uint16_t s; u_char b[2]; } port = { ip6->sin6_port }; - uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1]; - char ipstring[40]; - char ifname[IF_NAMESIZE + 1] = ""; - sprintf(ipstring, "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X", - b[0x0], b[0x1], b[0x2], b[0x3], b[0x4], b[0x5], b[0x6], b[0x7], - b[0x8], b[0x9], b[0xA], b[0xB], b[0xC], b[0xD], b[0xE], b[0xF]); - if (ip6->sin6_scope_id) { ifname[0] = '%'; if_indextoname(ip6->sin6_scope_id, &ifname[1]); } - printf("%s%s:%u", ipstring, ifname, PortAsNumber); - } - if (flags) printf(" Flags: %X", flags); - if (*src) - { - char txtInfo[64]; // Display at most first 64 characters of TXT record - char *dst = txtInfo; - const char *const lim = &txtInfo[sizeof(txtInfo)]; - while (*src && dst < lim-1) - { - if (*src == '\\') *dst++ = '\\'; // '\' displays as "\\" - if (*src >= ' ') *dst++ = *src++; // Display normal characters as-is - else - { - *dst++ = '\\'; // Display a backslash - if (*src == 1) *dst++ = ' '; // String boundary displayed as "\ " - else // Other chararacters displayed as "\0xHH" - { - static const char hexchars[16] = "0123456789ABCDEF"; - *dst++ = '0'; - *dst++ = 'x'; - *dst++ = hexchars[*src >> 4]; - *dst++ = hexchars[*src & 0xF]; - } - src++; - } - } - *dst++ = 0; - printf(" TXT %s", txtInfo); - } - printf("\n"); - } - } - -static void myCFRunLoopTimerCallBack(CFRunLoopTimerRef timer, void *info) - { - (void)timer; // Parameter not used - (void)info; // Parameter not used - - switch (operation) - { - case 'A': - { - switch (addtest) - { - case 0: printf("Adding Test HINFO record\n"); - record = DNSServiceRegistrationAddRecord(client, ns_t_hinfo, sizeof(myhinfo9), &myhinfo9[0], 120); - addtest = 1; - break; - case 1: printf("Updating Test HINFO record\n"); - DNSServiceRegistrationUpdateRecord(client, record, sizeof(myhinfoX), &myhinfoX[0], 120); - addtest = 2; - break; - case 2: printf("Removing Test HINFO record\n"); - DNSServiceRegistrationRemoveRecord(client, record); - addtest = 0; - break; - } - } - break; - - case 'U': - { - if (updatetest[1] != 'Z') updatetest[1]++; - else updatetest[1] = 'A'; - updatetest[0] = 3 - updatetest[0]; - updatetest[2] = updatetest[1]; - printf("Updating Test TXT record to %c\n", updatetest[1]); - DNSServiceRegistrationUpdateRecord(client, 0, 1+updatetest[0], &updatetest[0], 120); - } - break; - - case 'N': - { - printf("Adding big NULL record\n"); - DNSServiceRegistrationAddRecord(client, ns_t_null, sizeof(bigNULL), &bigNULL[0], 120); - CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode); - } - break; - } - } - -static void reg_reply(DNSServiceRegistrationReplyErrorType errorCode, void *context) - { - (void)context; // Unused - printf("Got a reply from the server: "); - switch (errorCode) - { - case kDNSServiceDiscoveryNoError: printf("Name now registered and active\n"); break; - case kDNSServiceDiscoveryNameConflict: printf("Name in use, please choose another\n"); exit(-1); - default: printf("Error %d\n", errorCode); return; - } - - if (operation == 'A' || operation == 'U' || operation == 'N') - { - CFRunLoopTimerContext myCFRunLoopTimerContext = { 0, 0, NULL, NULL, NULL }; - CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault, - CFAbsoluteTimeGetCurrent() + 5.0, 5.0, 0, 1, // Next fire time, periodic interval, flags, and order - myCFRunLoopTimerCallBack, &myCFRunLoopTimerContext); - CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode); - } - } - -//************************************************************************************************************* -// The main test function - -int main(int argc, char **argv) - { - const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]; - char *d; - setlinebuf(stdout); // Want to see lines as they appear, not block buffered - - if (argc < 2) goto Fail; // Minimum command line is the command name and one argument - operation = getopt(argc, (char * const *)argv, "EFBLRAUNTMI"); - if (operation == -1) goto Fail; - - switch (operation) - { - case 'E': printf("Looking for recommended registration domains:\n"); - client = DNSServiceDomainEnumerationCreate(1, regdom_reply, nil); - break; - - case 'F': printf("Looking for recommended browsing domains:\n"); - client = DNSServiceDomainEnumerationCreate(0, browsedom_reply, nil); - break; - - case 'B': if (argc < optind+1) goto Fail; - d = (argc < optind+2) ? "" : argv[optind+1]; // Missing domain argument is the same as empty string i.e. use system default(s) - if (d[0] == '.' && d[1] == 0) d[0] = 0; // We allow '.' on the command line as a synonym for empty string - printf("Browsing for %s%s\n", argv[optind+0], d); - client = DNSServiceBrowserCreate(argv[optind+0], d, browse_reply, nil); - break; - - case 'L': if (argc < optind+2) goto Fail; - d = (argc < optind+3) ? "" : argv[optind+2]; - if (d[0] == '.' && d[1] == 0) d = "local"; // We allow '.' on the command line as a synonym for "local" - printf("Lookup %s.%s%s\n", argv[optind+0], argv[optind+1], d); - client = DNSServiceResolverResolve(argv[optind+0], argv[optind+1], d, resolve_reply, nil); - break; - - case 'R': if (argc < optind+4) goto Fail; - { - char *nam = argv[optind+0]; - char *typ = argv[optind+1]; - char *dom = argv[optind+2]; - uint16_t PortAsNumber = atoi(argv[optind+3]); - Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } }; - char txt[2048]; - char *ptr = txt; - int i; - - if (nam[0] == '.' && nam[1] == 0) nam[0] = 0; // We allow '.' on the command line as a synonym for empty string - if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string - - // Copy all the TXT strings into one C string separated by ASCII-1 delimiters - for (i = optind+4; i < argc; i++) - { - int len = strlen(argv[i]); - if (len > 255 || ptr + len + 1 >= txt + sizeof(txt)) break; - strcpy(ptr, argv[i]); - ptr += len; - *ptr++ = 1; - } - if (ptr > txt) ptr--; - *ptr = 0; - - printf("Registering Service %s.%s%s port %s %s\n", nam, typ, dom, argv[optind+3], txt); - client = DNSServiceRegistrationCreate(nam, typ, dom, registerPort.NotAnInteger, txt, reg_reply, nil); - break; - } - - case 'A': - case 'U': - case 'N': { - Opaque16 registerPort = { { 0x12, 0x34 } }; - static const char TXT[] = "First String\001Second String\001Third String"; - printf("Registering Service Test._testupdate._tcp.local.\n"); - client = DNSServiceRegistrationCreate("Test", "_testupdate._tcp.", "", registerPort.NotAnInteger, TXT, reg_reply, nil); - break; - } - - case 'T': { - Opaque16 registerPort = { { 0x23, 0x45 } }; - char TXT[1000]; - unsigned int i; - for (i=0; i> 5); - TXT[i] = 0; - printf("Registering Service Test._testlargetxt._tcp.local.\n"); - client = DNSServiceRegistrationCreate("Test", "_testlargetxt._tcp.", "", registerPort.NotAnInteger, TXT, reg_reply, nil); - break; - } - - case 'M': { - pid_t pid = getpid(); - Opaque16 registerPort = { { pid >> 8, pid & 0xFF } }; - static const char TXT1[] = "First String\001Second String\001Third String"; - static const char TXT2[] = "\x0D" "Fourth String" "\x0C" "Fifth String" "\x0C" "Sixth String"; - printf("Registering Service Test._testdualtxt._tcp.local.\n"); - client = DNSServiceRegistrationCreate("", "_testdualtxt._tcp.", "", registerPort.NotAnInteger, TXT1, reg_reply, nil); - // use "sizeof(TXT2)-1" because we don't wan't the C compiler's null byte on the end of the string - record = DNSServiceRegistrationAddRecord(client, ns_t_txt, sizeof(TXT2)-1, TXT2, 120); - break; - } - - case 'I': { - pid_t pid = getpid(); - Opaque16 registerPort = { { pid >> 8, pid & 0xFF } }; - static const char TXT[] = "\x09" "Test Data"; - printf("Registering Service Test._testtxt._tcp.local.\n"); - client = DNSServiceRegistrationCreate("", "_testtxt._tcp.", "", registerPort.NotAnInteger, "", reg_reply, nil); - if (client) DNSServiceRegistrationUpdateRecord(client, 0, 1+TXT[0], &TXT[0], 120); - break; - } - - default: goto Exit; - } - - if (!client) { fprintf(stderr, "DNSService call failed\n"); return (-1); } - if (AddDNSServiceClientToRunLoop(client) != 0) { fprintf(stderr, "AddDNSServiceClientToRunLoop failed\n"); return (-1); } - printf("Talking to DNS SD Daemon at Mach port %d\n", DNSServiceDiscoveryMachPort(client)); - CFRunLoopRun(); - - // Be sure to deallocate the dns_service_discovery_ref when you're finished - // Note: What other cleanup has to be done here? - // We should probably invalidate, remove and release our CFRunLoopSourceRef? - DNSServiceDiscoveryDeallocate(client); - -Exit: - return 0; - -Fail: - fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", 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; - } - -// Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion -// e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4" -// To expand "version" to its value before making the string, use STRINGIFY(version) instead -#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s -#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) - -// NOT static -- otherwise the compiler may optimize it out -// The "@(#) " pattern is a special prefix the "what" command looks for -const char VersionString_SCCS[] = "@(#) mDNS " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; - -#if _BUILDING_XCODE_PROJECT_ -// If the process crashes, then this string will be magically included in the automatically-generated crash log -const char *__crashreporter_info__ = VersionString_SCCS + 5; -asm(".desc ___crashreporter_info__, 0x10"); -#endif diff --git a/mDNSMacOSX/daemon.c b/mDNSMacOSX/daemon.c index 767ad5e..151053f 100644 --- a/mDNSMacOSX/daemon.c +++ b/mDNSMacOSX/daemon.c @@ -5,27 +5,15 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * - * Formatting notes: - * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion - * on C indentation can be found on the web, such as , - * but for the sake of brevity here I will say just this: Curly braces are not syntactially - * part of an "if" statement; they are the beginning and ending markers of a compound statement; - * therefore common sense dictates that if they are part of a compound statement then they - * should be indented to the same level as everything else in that compound statement. - * Indenting curly braces at the same level as the "if" implies that curly braces are - * part of the "if", which is false. (This is as misleading as people who write "char* x,y;" - * thinking that variables x and y are both of type "char*" -- and anyone who doesn't - * understand why variable y is not of type "char*" just proves the point that poor code - * layout leads people to unfortunate misunderstandings about how the C language really works.) */ // We set VERSION_MIN_REQUIRED to 10.4 to avoid "bootstrap_register is deprecated" warnings from bootstrap.h @@ -41,11 +29,11 @@ #include #include #include +#include #include #include #include #include -#include #include #if TARGET_OS_EMBEDDED @@ -59,13 +47,12 @@ #include "uDNS.h" #include "DNSCommon.h" -#include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform +#include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform -#include "uds_daemon.h" // Interface to the server side implementation of dns_sd.h +#include "uds_daemon.h" // Interface to the server side implementation of dns_sd.h #include #include "helper.h" -#include "safe_vproc.h" //************************************************************************************************************* #if COMPILER_LIKES_PRAGMA_MARK @@ -104,8 +91,8 @@ static mDNSu32 launchd_fds_count = 0; // even extra-slow clients a fair chance before we cut them off. #define MDNS_MM_TIMEOUT 250 -static int restarting_via_mach_init = 0; // Used on Jaguar/Panther when daemon is started via mach_init mechanism -static int started_via_launchdaemon = 0; // Indicates we're running on Tiger or later, where daemon is managed by launchd +static int restarting_via_mach_init = 0; // Used on Jaguar/Panther when daemon is started via mach_init mechanism +static int started_via_launchdaemon = 0; // Indicates we're running on Tiger or later, where daemon is managed by launchd static mDNSBool advertise = mDNS_Init_AdvertiseLocalAddresses; // By default, advertise addresses (& other records) via multicast extern mDNSBool StrictUnicastOrdering; @@ -119,73 +106,73 @@ extern mDNSBool AlwaysAppendSearchDomains; typedef struct DNSServiceDomainEnumeration_struct DNSServiceDomainEnumeration; struct DNSServiceDomainEnumeration_struct - { - DNSServiceDomainEnumeration *next; - mach_port_t ClientMachPort; - DNSQuestion dom; // Question asking for domains - DNSQuestion def; // Question asking for default domain - }; +{ + DNSServiceDomainEnumeration *next; + mach_port_t ClientMachPort; + DNSQuestion dom; // Question asking for domains + DNSQuestion def; // Question asking for default domain +}; typedef struct DNSServiceBrowserResult_struct DNSServiceBrowserResult; struct DNSServiceBrowserResult_struct - { - DNSServiceBrowserResult *next; - int resultType; - domainname result; - }; +{ + DNSServiceBrowserResult *next; + int resultType; + domainname result; +}; typedef struct DNSServiceBrowser_struct DNSServiceBrowser; typedef struct DNSServiceBrowserQuestion - { - struct DNSServiceBrowserQuestion *next; - DNSQuestion q; +{ + struct DNSServiceBrowserQuestion *next; + DNSQuestion q; domainname domain; - } DNSServiceBrowserQuestion; +} DNSServiceBrowserQuestion; struct DNSServiceBrowser_struct - { - DNSServiceBrowser *next; - mach_port_t ClientMachPort; - DNSServiceBrowserQuestion *qlist; - DNSServiceBrowserResult *results; - mDNSs32 lastsuccess; +{ + DNSServiceBrowser *next; + mach_port_t ClientMachPort; + 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; struct DNSServiceResolver_struct - { - DNSServiceResolver *next; - mach_port_t ClientMachPort; - ServiceInfoQuery q; - ServiceInfo i; - mDNSs32 ReportTime; - }; +{ + DNSServiceResolver *next; + mach_port_t ClientMachPort; + ServiceInfoQuery q; + ServiceInfo i; + mDNSs32 ReportTime; +}; // 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 - { +{ struct ServiceInstance *next; - mach_port_t ClientMachPort; - mDNSBool autoname; // Set if this name is tied to the Computer Name - mDNSBool renameonmemfree; // Set if we just got a name conflict and now need to automatically pick a new name + mach_port_t ClientMachPort; + mDNSBool autoname; // Set if this name is tied to the Computer Name + mDNSBool renameonmemfree; // 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; + // 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; + mach_port_t ClientMachPort; mDNSBool DefaultDomain; mDNSBool autoname; size_t rdsize; @@ -198,7 +185,7 @@ typedef struct DNSServiceRegistration size_t txt_len; uint32_t NextRef; ServiceInstance *regs; - } DNSServiceRegistration; +} DNSServiceRegistration; static DNSServiceDomainEnumeration *DNSServiceDomainEnumerationList = NULL; static DNSServiceBrowser *DNSServiceBrowserList = NULL; @@ -207,11 +194,11 @@ static DNSServiceRegistration *DNSServiceRegistrationList = NULL; // We keep a list of client-supplied event sources in KQSocketEventSource records typedef struct KQSocketEventSource - { - struct KQSocketEventSource *next; - int fd; - KQueueEntry kqs; - } KQSocketEventSource; +{ + struct KQSocketEventSource *next; + int fd; + KQueueEntry kqs; +} KQSocketEventSource; static KQSocketEventSource *gEventSources; @@ -226,145 +213,145 @@ static KQSocketEventSource *gEventSources; char _malloc_options[] = "AXZ"; mDNSexport void LogMemCorruption(const char *format, ...) - { - char buffer[512]; - va_list ptr; - va_start(ptr,format); - buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; - va_end(ptr); - LogMsg("!!!! %s !!!!", buffer); - NotifyOfElusiveBug("Memory Corruption", buffer); +{ + char buffer[512]; + va_list ptr; + va_start(ptr,format); + buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; + va_end(ptr); + LogMsg("!!!! %s !!!!", buffer); + NotifyOfElusiveBug("Memory Corruption", buffer); #if ForceAlerts - *(long*)0 = 0; // Trick to crash and get a stack trace right here, if that's what we want + *(long*)0 = 0; // Trick to crash and get a stack trace right here, if that's what we want #endif - } +} mDNSlocal void validatelists(mDNS *const m) - { - // Check local lists - KQSocketEventSource *k; - for (k = gEventSources; k; k=k->next) - if (k->next == (KQSocketEventSource *)~0 || k->fd < 0) - LogMemCorruption("gEventSources: %p is garbage (%d)", k, k->fd); - - // Check Mach client lists - DNSServiceDomainEnumeration *e; - for (e = DNSServiceDomainEnumerationList; e; e=e->next) - if (e->next == (DNSServiceDomainEnumeration *)~0 || e->ClientMachPort == 0 || e->ClientMachPort == (mach_port_t)~0) - LogMemCorruption("DNSServiceDomainEnumerationList: %p is garbage (%X)", e, e->ClientMachPort); - - DNSServiceBrowser *b; - for (b = DNSServiceBrowserList; b; b=b->next) - if (b->next == (DNSServiceBrowser *)~0 || b->ClientMachPort == 0 || b->ClientMachPort == (mach_port_t)~0) - LogMemCorruption("DNSServiceBrowserList: %p is garbage (%X)", b, b->ClientMachPort); - - DNSServiceResolver *l; - for (l = DNSServiceResolverList; l; l=l->next) - if (l->next == (DNSServiceResolver *)~0 || l->ClientMachPort == 0 || l->ClientMachPort == (mach_port_t)~0) - LogMemCorruption("DNSServiceResolverList: %p is garbage (%X)", l, l->ClientMachPort); - - DNSServiceRegistration *r; - for (r = DNSServiceRegistrationList; r; r=r->next) - if (r->next == (DNSServiceRegistration *)~0 || r->ClientMachPort == 0 || r->ClientMachPort == (mach_port_t)~0) - LogMemCorruption("DNSServiceRegistrationList: %p is garbage (%X)", r, r->ClientMachPort); - - // Check Unix Domain Socket client lists (uds_daemon.c) - uds_validatelists(); - - // Check core mDNS lists - AuthRecord *rr; - for (rr = m->ResourceRecords; rr; rr=rr->next) - { - if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) - LogMemCorruption("ResourceRecords list: %p is garbage (%X)", rr, rr->resrec.RecordType); - if (rr->resrec.name != &rr->namestorage) - LogMemCorruption("ResourceRecords list: %p name %p does not point to namestorage %p %##s", - rr, rr->resrec.name->c, rr->namestorage.c, rr->namestorage.c); - } - - for (rr = m->DuplicateRecords; rr; rr=rr->next) - if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) - LogMemCorruption("DuplicateRecords list: %p is garbage (%X)", rr, rr->resrec.RecordType); - - rr = m->NewLocalRecords; - if (rr) - if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) - LogMemCorruption("NewLocalRecords: %p is garbage (%X)", rr, rr->resrec.RecordType); - - rr = m->CurrentRecord; - if (rr) - if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) - LogMemCorruption("CurrentRecord: %p is garbage (%X)", rr, rr->resrec.RecordType); - - DNSQuestion *q; - for (q = m->Questions; q; q=q->next) - if (q->next == (DNSQuestion*)~0 || q->ThisQInterval == (mDNSs32)~0) - LogMemCorruption("Questions list: %p is garbage (%lX %p)", q, q->ThisQInterval, q->next); - - CacheGroup *cg; - CacheRecord *cr; - mDNSu32 slot; - FORALL_CACHERECORDS(slot, cg, cr) - { - if (cr->resrec.RecordType == 0 || cr->resrec.RecordType == 0xFF) - LogMemCorruption("Cache slot %lu: %p is garbage (%X)", slot, cr, cr->resrec.RecordType); - if (cr->CRActiveQuestion) - { - for (q = m->Questions; q; q=q->next) if (q == cr->CRActiveQuestion) break; - if (!q) LogMemCorruption("Cache slot %lu: CRActiveQuestion %p not in m->Questions list %s", slot, cr->CRActiveQuestion, CRDisplayString(m, cr)); - } - } - - // Check core uDNS lists - udns_validatelists(m); - - // Check platform-layer lists - NetworkInterfaceInfoOSX *i; - for (i = m->p->InterfaceList; i; i = i->next) - if (i->next == (NetworkInterfaceInfoOSX *)~0 || !i->m || i->m == (mDNS *)~0) - LogMemCorruption("m->p->InterfaceList: %p is garbage (%p)", i, i->ifinfo.ifname); - - ClientTunnel *t; - for (t = m->TunnelClients; t; t=t->next) - if (t->next == (ClientTunnel *)~0 || t->dstname.c[0] > 63) - LogMemCorruption("m->TunnelClients: %p is garbage (%d)", t, t->dstname.c[0]); - } +{ + // Check local lists + KQSocketEventSource *k; + for (k = gEventSources; k; k=k->next) + if (k->next == (KQSocketEventSource *)~0 || k->fd < 0) + LogMemCorruption("gEventSources: %p is garbage (%d)", k, k->fd); + + // Check Mach client lists + DNSServiceDomainEnumeration *e; + for (e = DNSServiceDomainEnumerationList; e; e=e->next) + if (e->next == (DNSServiceDomainEnumeration *)~0 || e->ClientMachPort == 0 || e->ClientMachPort == (mach_port_t) ~0) + LogMemCorruption("DNSServiceDomainEnumerationList: %p is garbage (%X)", e, e->ClientMachPort); + + DNSServiceBrowser *b; + for (b = DNSServiceBrowserList; b; b=b->next) + if (b->next == (DNSServiceBrowser *)~0 || b->ClientMachPort == 0 || b->ClientMachPort == (mach_port_t) ~0) + LogMemCorruption("DNSServiceBrowserList: %p is garbage (%X)", b, b->ClientMachPort); + + DNSServiceResolver *l; + for (l = DNSServiceResolverList; l; l=l->next) + if (l->next == (DNSServiceResolver *)~0 || l->ClientMachPort == 0 || l->ClientMachPort == (mach_port_t) ~0) + LogMemCorruption("DNSServiceResolverList: %p is garbage (%X)", l, l->ClientMachPort); + + DNSServiceRegistration *r; + for (r = DNSServiceRegistrationList; r; r=r->next) + if (r->next == (DNSServiceRegistration *)~0 || r->ClientMachPort == 0 || r->ClientMachPort == (mach_port_t) ~0) + LogMemCorruption("DNSServiceRegistrationList: %p is garbage (%X)", r, r->ClientMachPort); + + // Check Unix Domain Socket client lists (uds_daemon.c) + uds_validatelists(); + + // Check core mDNS lists + AuthRecord *rr; + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) + LogMemCorruption("ResourceRecords list: %p is garbage (%X)", rr, rr->resrec.RecordType); + if (rr->resrec.name != &rr->namestorage) + LogMemCorruption("ResourceRecords list: %p name %p does not point to namestorage %p %##s", + rr, rr->resrec.name->c, rr->namestorage.c, rr->namestorage.c); + } + + for (rr = m->DuplicateRecords; rr; rr=rr->next) + if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) + LogMemCorruption("DuplicateRecords list: %p is garbage (%X)", rr, rr->resrec.RecordType); + + rr = m->NewLocalRecords; + if (rr) + if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) + LogMemCorruption("NewLocalRecords: %p is garbage (%X)", rr, rr->resrec.RecordType); + + rr = m->CurrentRecord; + if (rr) + if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) + LogMemCorruption("CurrentRecord: %p is garbage (%X)", rr, rr->resrec.RecordType); + + DNSQuestion *q; + for (q = m->Questions; q; q=q->next) + if (q->next == (DNSQuestion*)~0 || q->ThisQInterval == (mDNSs32) ~0) + LogMemCorruption("Questions list: %p is garbage (%lX %p)", q, q->ThisQInterval, q->next); + + CacheGroup *cg; + CacheRecord *cr; + mDNSu32 slot; + FORALL_CACHERECORDS(slot, cg, cr) + { + if (cr->resrec.RecordType == 0 || cr->resrec.RecordType == 0xFF) + LogMemCorruption("Cache slot %lu: %p is garbage (%X)", slot, cr, cr->resrec.RecordType); + if (cr->CRActiveQuestion) + { + for (q = m->Questions; q; q=q->next) if (q == cr->CRActiveQuestion) break; + if (!q) LogMemCorruption("Cache slot %lu: CRActiveQuestion %p not in m->Questions list %s", slot, cr->CRActiveQuestion, CRDisplayString(m, cr)); + } + } + + // Check core uDNS lists + udns_validatelists(m); + + // Check platform-layer lists + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) + if (i->next == (NetworkInterfaceInfoOSX *)~0 || !i->m || i->m == (mDNS *)~0) + LogMemCorruption("m->p->InterfaceList: %p is garbage (%p)", i, i->ifinfo.ifname); + + ClientTunnel *t; + for (t = m->TunnelClients; t; t=t->next) + if (t->next == (ClientTunnel *)~0 || t->dstname.c[0] > 63) + LogMemCorruption("m->TunnelClients: %p is garbage (%d)", t, t->dstname.c[0]); +} mDNSexport void *mallocL(char *msg, unsigned int size) - { - // Allocate space for two words of sanity checking data before the requested block - mDNSu32 *mem = malloc(sizeof(mDNSu32) * 2 + size); - if (!mem) - { LogMsg("malloc( %s : %d ) failed", msg, size); return(NULL); } - else - { - if (size > 24000) LogMsg("malloc( %s : %lu ) = %p suspiciously large", msg, size, &mem[2]); - else if (MACOSX_MDNS_MALLOC_DEBUGGING >= 2) LogMsg("malloc( %s : %lu ) = %p", msg, size, &mem[2]); - mem[0] = 0xDEAD1234; - mem[1] = size; - //mDNSPlatformMemZero(&mem[2], size); - memset(&mem[2], 0xFF, size); - validatelists(&mDNSStorage); - return(&mem[2]); - } - } +{ + // Allocate space for two words of sanity checking data before the requested block + mDNSu32 *mem = malloc(sizeof(mDNSu32) * 2 + size); + if (!mem) + { LogMsg("malloc( %s : %d ) failed", msg, size); return(NULL); } + else + { + if (size > 24000) LogMsg("malloc( %s : %lu ) = %p suspiciously large", msg, size, &mem[2]); + else if (MACOSX_MDNS_MALLOC_DEBUGGING >= 2) LogMsg("malloc( %s : %lu ) = %p", msg, size, &mem[2]); + mem[0] = 0xDEAD1234; + mem[1] = size; + //mDNSPlatformMemZero(&mem[2], size); + memset(&mem[2], 0xFF, size); + validatelists(&mDNSStorage); + return(&mem[2]); + } +} mDNSexport void freeL(char *msg, void *x) - { - if (!x) - LogMsg("free( %s @ NULL )!", msg); - else - { - mDNSu32 *mem = ((mDNSu32 *)x) - 2; - if (mem[0] != 0xDEAD1234) { LogMsg("free( %s @ %p ) !!!! NOT ALLOCATED !!!!", msg, &mem[2]); return; } - if (mem[1] > 24000) LogMsg("free( %s : %ld @ %p) suspiciously large", msg, mem[1], &mem[2]); - else if (MACOSX_MDNS_MALLOC_DEBUGGING >= 2) LogMsg("free( %s : %ld @ %p)", msg, mem[1], &mem[2]); - //mDNSPlatformMemZero(mem, sizeof(mDNSu32) * 2 + mem[1]); - memset(mem, 0xFF, sizeof(mDNSu32) * 2 + mem[1]); - validatelists(&mDNSStorage); - free(mem); - } - } +{ + if (!x) + LogMsg("free( %s @ NULL )!", msg); + else + { + mDNSu32 *mem = ((mDNSu32 *)x) - 2; + if (mem[0] != 0xDEAD1234) { LogMsg("free( %s @ %p ) !!!! NOT ALLOCATED !!!!", msg, &mem[2]); return; } + if (mem[1] > 24000) LogMsg("free( %s : %ld @ %p) suspiciously large", msg, mem[1], &mem[2]); + else if (MACOSX_MDNS_MALLOC_DEBUGGING >= 2) LogMsg("free( %s : %ld @ %p)", msg, mem[1], &mem[2]); + //mDNSPlatformMemZero(mem, sizeof(mDNSu32) * 2 + mem[1]); + memset(mem, 0xFF, sizeof(mDNSu32) * 2 + mem[1]); + validatelists(&mDNSStorage); + free(mem); + } +} #endif @@ -379,1193 +366,1193 @@ mDNSexport void freeL(char *msg, void *x) // This gets called after ALL constituent records of the Service Record Set have been deregistered mDNSlocal void FreeServiceInstance(ServiceInstance *x) - { - ServiceRecordSet *s = &x->srs; - ExtraResourceRecord *e = x->srs.Extras, *tmp; - - while (e) - { - 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); - freeL("ServiceInstance", x); - } +{ + ServiceRecordSet *s = &x->srs; + ExtraResourceRecord *e = x->srs.Extras, *tmp; + + while (e) + { + 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); + freeL("ServiceInstance", x); +} // AbortClient finds whatever client is identified by the given Mach port, // stops whatever operation that client was doing, and frees its memory. // In the case of a service registration, the actual freeing may be deferred // until we get the mStatus_MemFree message, if necessary mDNSlocal void AbortClient(mach_port_t ClientMachPort, void *m) - { - DNSServiceDomainEnumeration **e = &DNSServiceDomainEnumerationList; - DNSServiceBrowser **b = &DNSServiceBrowserList; - DNSServiceResolver **l = &DNSServiceResolverList; - DNSServiceRegistration **r = &DNSServiceRegistrationList; - - while (*e && (*e)->ClientMachPort != ClientMachPort) e = &(*e)->next; - if (*e) - { - DNSServiceDomainEnumeration *x = *e; - *e = (*e)->next; - if (m && m != x) - LogMsg("%5d: DNSServiceDomainEnumeration(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->dom.qname.c, m, x); - else LogOperation("%5d: DNSServiceDomainEnumeration(%##s) STOP", ClientMachPort, x->dom.qname.c); - mDNS_StopGetDomains(&mDNSStorage, &x->dom); - mDNS_StopGetDomains(&mDNSStorage, &x->def); - freeL("DNSServiceDomainEnumeration", x); - return; - } - - while (*b && (*b)->ClientMachPort != ClientMachPort) b = &(*b)->next; - if (*b) - { - DNSServiceBrowser *x = *b; - DNSServiceBrowserQuestion *freePtr, *qptr = x->qlist; - *b = (*b)->next; - while (qptr) - { - if (m && m != x) - LogMsg("%5d: DNSServiceBrowse(%##s) STOP; WARNING m %p != x %p", ClientMachPort, qptr->q.qname.c, m, x); - else LogOperation("%5d: DNSServiceBrowse(%##s) STOP", ClientMachPort, qptr->q.qname.c); - mDNS_StopBrowse(&mDNSStorage, &qptr->q); - freePtr = qptr; - qptr = qptr->next; - freeL("DNSServiceBrowserQuestion", freePtr); - } - while (x->results) - { - DNSServiceBrowserResult *t = x->results; - x->results = x->results->next; - freeL("DNSServiceBrowserResult", t); - } - freeL("DNSServiceBrowser", x); - return; - } - - while (*l && (*l)->ClientMachPort != ClientMachPort) l = &(*l)->next; - if (*l) - { - DNSServiceResolver *x = *l; - *l = (*l)->next; - if (m && m != x) - LogMsg("%5d: DNSServiceResolve(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->i.name.c, m, x); - else LogOperation("%5d: DNSServiceResolve(%##s) STOP", ClientMachPort, x->i.name.c); - mDNS_StopResolveService(&mDNSStorage, &x->q); - freeL("DNSServiceResolver", x); - return; - } - - while (*r && (*r)->ClientMachPort != ClientMachPort) r = &(*r)->next; - if (*r) - { - ServiceInstance *si = NULL; - DNSServiceRegistration *x = *r; - *r = (*r)->next; - - si = x->regs; - while (si) - { - ServiceInstance *instance = si; - si = si->next; - instance->renameonmemfree = 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 - } - x->regs = NULL; - freeL("DNSServiceRegistration", x); - return; - } - - LogMsg("%5d: died or deallocated, but no record of client can be found!", ClientMachPort); - } +{ + DNSServiceDomainEnumeration **e = &DNSServiceDomainEnumerationList; + DNSServiceBrowser **b = &DNSServiceBrowserList; + DNSServiceResolver **l = &DNSServiceResolverList; + DNSServiceRegistration **r = &DNSServiceRegistrationList; + + while (*e && (*e)->ClientMachPort != ClientMachPort) e = &(*e)->next; + if (*e) + { + DNSServiceDomainEnumeration *x = *e; + *e = (*e)->next; + if (m && m != x) + LogMsg("%5d: DNSServiceDomainEnumeration(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->dom.qname.c, m, x); + else LogOperation("%5d: DNSServiceDomainEnumeration(%##s) STOP", ClientMachPort, x->dom.qname.c); + mDNS_StopGetDomains(&mDNSStorage, &x->dom); + mDNS_StopGetDomains(&mDNSStorage, &x->def); + freeL("DNSServiceDomainEnumeration", x); + return; + } + + while (*b && (*b)->ClientMachPort != ClientMachPort) b = &(*b)->next; + if (*b) + { + DNSServiceBrowser *x = *b; + DNSServiceBrowserQuestion *freePtr, *qptr = x->qlist; + *b = (*b)->next; + while (qptr) + { + if (m && m != x) + LogMsg("%5d: DNSServiceBrowse(%##s) STOP; WARNING m %p != x %p", ClientMachPort, qptr->q.qname.c, m, x); + else LogOperation("%5d: DNSServiceBrowse(%##s) STOP", ClientMachPort, qptr->q.qname.c); + mDNS_StopBrowse(&mDNSStorage, &qptr->q); + freePtr = qptr; + qptr = qptr->next; + freeL("DNSServiceBrowserQuestion", freePtr); + } + while (x->results) + { + DNSServiceBrowserResult *t = x->results; + x->results = x->results->next; + freeL("DNSServiceBrowserResult", t); + } + freeL("DNSServiceBrowser", x); + return; + } + + while (*l && (*l)->ClientMachPort != ClientMachPort) l = &(*l)->next; + if (*l) + { + DNSServiceResolver *x = *l; + *l = (*l)->next; + if (m && m != x) + LogMsg("%5d: DNSServiceResolve(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->i.name.c, m, x); + else LogOperation("%5d: DNSServiceResolve(%##s) STOP", ClientMachPort, x->i.name.c); + mDNS_StopResolveService(&mDNSStorage, &x->q); + freeL("DNSServiceResolver", x); + return; + } + + while (*r && (*r)->ClientMachPort != ClientMachPort) r = &(*r)->next; + if (*r) + { + ServiceInstance *si = NULL; + DNSServiceRegistration *x = *r; + *r = (*r)->next; + + si = x->regs; + while (si) + { + ServiceInstance *instance = si; + si = si->next; + instance->renameonmemfree = 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 + } + x->regs = NULL; + freeL("DNSServiceRegistration", x); + return; + } + + LogMsg("%5d: died or deallocated, but no record of client can be found!", ClientMachPort); +} #define AbortBlockedClient(C,MSG,M) AbortClientWithLogMessage((C), "stopped accepting Mach messages", " (" MSG ")", (M)) mDNSlocal void AbortClientWithLogMessage(mach_port_t c, char *reason, char *msg, void *m) - { - DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList; - DNSServiceBrowser *b = DNSServiceBrowserList; - DNSServiceResolver *l = DNSServiceResolverList; - DNSServiceRegistration *r = DNSServiceRegistrationList; - DNSServiceBrowserQuestion *qptr; - - while (e && e->ClientMachPort != c) e = e->next; - while (b && b->ClientMachPort != c) b = b->next; - while (l && l->ClientMachPort != c) l = l->next; - while (r && r->ClientMachPort != c) r = r->next; - - if (e) LogMsg("%5d: DomainEnumeration(%##s) %s%s", c, e->dom.qname.c, reason, msg); - else if (b) - { - for (qptr = b->qlist; qptr; qptr = qptr->next) - LogMsg("%5d: Browser(%##s) %s%s", c, qptr->q.qname.c, reason, msg); - } - else if (l) LogMsg("%5d: Resolver(%##s) %s%s", c, l->i.name.c, reason, msg); - else if (r) - { - 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); - - AbortClient(c, m); - } +{ + DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList; + DNSServiceBrowser *b = DNSServiceBrowserList; + DNSServiceResolver *l = DNSServiceResolverList; + DNSServiceRegistration *r = DNSServiceRegistrationList; + DNSServiceBrowserQuestion *qptr; + + while (e && e->ClientMachPort != c) e = e->next; + while (b && b->ClientMachPort != c) b = b->next; + while (l && l->ClientMachPort != c) l = l->next; + while (r && r->ClientMachPort != c) r = r->next; + + if (e) LogMsg("%5d: DomainEnumeration(%##s) %s%s", c, e->dom.qname.c, reason, msg); + else if (b) + { + for (qptr = b->qlist; qptr; qptr = qptr->next) + LogMsg("%5d: Browser(%##s) %s%s", c, qptr->q.qname.c, reason, msg); + } + else if (l) LogMsg("%5d: Resolver(%##s) %s%s", c, l->i.name.c, reason, msg); + else if (r) + { + 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); + + AbortClient(c, m); +} mDNSlocal mDNSBool CheckForExistingClient(mach_port_t c) - { - DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList; - DNSServiceBrowser *b = DNSServiceBrowserList; - DNSServiceResolver *l = DNSServiceResolverList; - DNSServiceRegistration *r = DNSServiceRegistrationList; - DNSServiceBrowserQuestion *qptr; - - while (e && e->ClientMachPort != c) e = e->next; - while (b && b->ClientMachPort != c) b = b->next; - while (l && l->ClientMachPort != c) l = l->next; - while (r && r->ClientMachPort != c) r = r->next; - if (e) LogMsg("%5d: DomainEnumeration(%##s) already exists!", c, e->dom.qname.c); - if (b) - { - for (qptr = b->qlist; qptr; qptr = qptr->next) - LogMsg("%5d: Browser(%##s) already exists!", c, qptr->q.qname.c); - } - if (l) LogMsg("%5d: Resolver(%##s) already exists!", c, l->i.name.c); - if (r) LogMsg("%5d: Registration(%##s) already exists!", c, r->regs ? r->regs->srs.RR_SRV.resrec.name->c : NULL); - return(e || b || l || r); - } +{ + DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList; + DNSServiceBrowser *b = DNSServiceBrowserList; + DNSServiceResolver *l = DNSServiceResolverList; + DNSServiceRegistration *r = DNSServiceRegistrationList; + DNSServiceBrowserQuestion *qptr; + + while (e && e->ClientMachPort != c) e = e->next; + while (b && b->ClientMachPort != c) b = b->next; + while (l && l->ClientMachPort != c) l = l->next; + while (r && r->ClientMachPort != c) r = r->next; + if (e) LogMsg("%5d: DomainEnumeration(%##s) already exists!", c, e->dom.qname.c); + if (b) + { + for (qptr = b->qlist; qptr; qptr = qptr->next) + LogMsg("%5d: Browser(%##s) already exists!", c, qptr->q.qname.c); + } + if (l) LogMsg("%5d: Resolver(%##s) already exists!", c, l->i.name.c); + if (r) LogMsg("%5d: Registration(%##s) already exists!", c, r->regs ? r->regs->srs.RR_SRV.resrec.name->c : NULL); + return(e || b || l || r); +} #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM mDNSlocal void ClientDeathCallback(CFMachPortRef unusedport, void *voidmsg, CFIndex size, void *info) - { - KQueueLock(&mDNSStorage); - mach_msg_header_t *msg = (mach_msg_header_t *)voidmsg; - (void)unusedport; // Unused - (void)size; // Unused - (void)info; // Unused - if (msg->msgh_id == MACH_NOTIFY_DEAD_NAME) - { - const mach_dead_name_notification_t *const deathMessage = (mach_dead_name_notification_t *)msg; - AbortClient(deathMessage->not_port, NULL); - - /* Deallocate the send right that came in the dead name notification */ - mach_port_destroy(mach_task_self(), deathMessage->not_port); - } - KQueueUnlock(&mDNSStorage, "Mach AbortClient"); - } +{ + KQueueLock(&mDNSStorage); + mach_msg_header_t *msg = (mach_msg_header_t *)voidmsg; + (void)unusedport; // Unused + (void)size; // Unused + (void)info; // Unused + if (msg->msgh_id == MACH_NOTIFY_DEAD_NAME) + { + const mach_dead_name_notification_t *const deathMessage = (mach_dead_name_notification_t *)msg; + AbortClient(deathMessage->not_port, NULL); + + /* Deallocate the send right that came in the dead name notification */ + mach_port_destroy(mach_task_self(), deathMessage->not_port); + } + KQueueUnlock(&mDNSStorage, "Mach AbortClient"); +} #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM mDNSlocal void EnableDeathNotificationForClient(mach_port_t ClientMachPort, void *m) - { +{ #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM - dispatch_source_t mach_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, ClientMachPort, 0, dispatch_get_main_queue()); - if (mach_source == mDNSNULL) - { - AbortClientWithLogMessage(ClientMachPort, "died/deallocated before we could enable death notification", "", m); - return; - } - dispatch_source_set_event_handler(mach_source, ^{ - mach_port_destroy(mach_task_self(), ClientMachPort); - }); - dispatch_resume(mach_source); + dispatch_source_t mach_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, ClientMachPort, 0, dispatch_get_main_queue()); + if (mach_source == mDNSNULL) + { + AbortClientWithLogMessage(ClientMachPort, "died/deallocated before we could enable death notification", "", m); + return; + } + dispatch_source_set_event_handler(mach_source, ^{ + mach_port_destroy(mach_task_self(), ClientMachPort); + }); + dispatch_resume(mach_source); #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM - mach_port_t prev; - kern_return_t r = mach_port_request_notification(mach_task_self(), ClientMachPort, MACH_NOTIFY_DEAD_NAME, 0, - client_death_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev); - // If the port already died while we were thinking about it, then abort the operation right away - if (r != KERN_SUCCESS) - AbortClientWithLogMessage(ClientMachPort, "died/deallocated before we could enable death notification", "", m); + mach_port_t prev; + kern_return_t r = mach_port_request_notification(mach_task_self(), ClientMachPort, MACH_NOTIFY_DEAD_NAME, 0, + client_death_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev); + // If the port already died while we were thinking about it, then abort the operation right away + if (r != KERN_SUCCESS) + AbortClientWithLogMessage(ClientMachPort, "died/deallocated before we could enable death notification", "", m); #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM - } +} //************************************************************************************************************* // Domain Enumeration mDNSlocal void DomainEnumFound(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) - { - kern_return_t status; - char buffer[MAX_ESCAPED_DOMAIN_NAME]; - DNSServiceDomainEnumerationReplyResultType rt; - DNSServiceDomainEnumeration *x = (DNSServiceDomainEnumeration *)question->QuestionContext; - (void)m; // Unused - - debugf("DomainEnumFound: %##s PTR %##s", answer->name->c, answer->rdata->u.name.c); - if (answer->rrtype != kDNSType_PTR) return; - if (!x) { debugf("DomainEnumFound: DNSServiceDomainEnumeration is NULL"); return; } - - if (AddRecord) - { - if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyAddDomain; - else rt = DNSServiceDomainEnumerationReplyAddDomainDefault; - } - else - { - if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyRemoveDomain; - else return; - } - - LogOperation("%5d: DNSServiceDomainEnumeration(%##s) %##s %s", - x->ClientMachPort, x->dom.qname.c, answer->rdata->u.name.c, - !AddRecord ? "RemoveDomain" : - question == &x->dom ? "AddDomain" : "AddDomainDefault"); - - ConvertDomainNameToCString(&answer->rdata->u.name, buffer); - status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, buffer, 0, MDNS_MM_TIMEOUT); - if (status == MACH_SEND_TIMED_OUT) - AbortBlockedClient(x->ClientMachPort, "enumeration", x); - } +{ + kern_return_t status; + char buffer[MAX_ESCAPED_DOMAIN_NAME]; + DNSServiceDomainEnumerationReplyResultType rt; + DNSServiceDomainEnumeration *x = (DNSServiceDomainEnumeration *)question->QuestionContext; + (void)m; // Unused + + debugf("DomainEnumFound: %##s PTR %##s", answer->name->c, answer->rdata->u.name.c); + if (answer->rrtype != kDNSType_PTR) return; + if (!x) { debugf("DomainEnumFound: DNSServiceDomainEnumeration is NULL"); return; } + + if (AddRecord) + { + if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyAddDomain; + else rt = DNSServiceDomainEnumerationReplyAddDomainDefault; + } + else + { + if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyRemoveDomain; + else return; + } + + LogOperation("%5d: DNSServiceDomainEnumeration(%##s) %##s %s", + x->ClientMachPort, x->dom.qname.c, answer->rdata->u.name.c, + !AddRecord ? "RemoveDomain" : + question == &x->dom ? "AddDomain" : "AddDomainDefault"); + + ConvertDomainNameToCString(&answer->rdata->u.name, buffer); + status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, buffer, 0, MDNS_MM_TIMEOUT); + if (status == MACH_SEND_TIMED_OUT) + AbortBlockedClient(x->ClientMachPort, "enumeration", x); +} mDNSexport kern_return_t provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver, mach_port_t client, - int regDom) - { - // Check client parameter - (void)unusedserver; // Unused - mStatus err = mStatus_NoError; - const char *errormsg = "Unknown"; - if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } - if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; } - - mDNS_DomainType dt1 = regDom ? mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse; - mDNS_DomainType dt2 = regDom ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault; - - // Allocate memory, and handle failure - DNSServiceDomainEnumeration *x = mallocL("DNSServiceDomainEnumeration", sizeof(*x)); - if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } - - // Set up object, and link into list - x->ClientMachPort = client; - x->next = DNSServiceDomainEnumerationList; - DNSServiceDomainEnumerationList = x; - - verbosedebugf("%5d: Enumerate %s Domains", client, regDom ? "Registration" : "Browsing"); - - // Do the operation - err = mDNS_GetDomains(&mDNSStorage, &x->dom, dt1, NULL, mDNSInterface_LocalOnly, DomainEnumFound, x); - if (!err) err = mDNS_GetDomains(&mDNSStorage, &x->def, dt2, NULL, mDNSInterface_LocalOnly, DomainEnumFound, x); - if (err) { AbortClient(client, x); errormsg = "mDNS_GetDomains"; goto fail; } - - // Succeeded: Wrap up and return - LogOperation("%5d: DNSServiceDomainEnumeration(%##s) START", client, x->dom.qname.c); - EnableDeathNotificationForClient(client, x); - return(mStatus_NoError); + int regDom) +{ + // Check client parameter + (void)unusedserver; // Unused + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } + if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; } + + mDNS_DomainType dt1 = regDom ? mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse; + mDNS_DomainType dt2 = regDom ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault; + + // Allocate memory, and handle failure + DNSServiceDomainEnumeration *x = mallocL("DNSServiceDomainEnumeration", sizeof(*x)); + if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + + // Set up object, and link into list + x->ClientMachPort = client; + x->next = DNSServiceDomainEnumerationList; + DNSServiceDomainEnumerationList = x; + + verbosedebugf("%5d: Enumerate %s Domains", client, regDom ? "Registration" : "Browsing"); + + // Do the operation + err = mDNS_GetDomains(&mDNSStorage, &x->dom, dt1, NULL, mDNSInterface_LocalOnly, DomainEnumFound, x); + if (!err) err = mDNS_GetDomains(&mDNSStorage, &x->def, dt2, NULL, mDNSInterface_LocalOnly, DomainEnumFound, x); + if (err) { AbortClient(client, x); errormsg = "mDNS_GetDomains"; goto fail; } + + // Succeeded: Wrap up and return + LogOperation("%5d: DNSServiceDomainEnumeration(%##s) START", client, x->dom.qname.c); + EnableDeathNotificationForClient(client, x); + return(mStatus_NoError); fail: - LogMsg("%5d: DNSServiceDomainEnumeration(%d) failed: %s (%d)", client, regDom, errormsg, err); - return(err); - } + LogMsg("%5d: DNSServiceDomainEnumeration(%d) failed: %s (%d)", client, regDom, errormsg, err); + return(err); +} //************************************************************************************************************* // Browse for services mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) - { - (void)m; // Unused - - if (answer->rrtype != kDNSType_PTR) - { LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer->rrtype); return; } - - domainlabel name; - domainname type, domain; - if (!DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain)) - { - LogMsg("FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer", - answer->name->c, answer->rdata->u.name.c); - return; - } - - DNSServiceBrowserResult *x = mallocL("DNSServiceBrowserResult", sizeof(*x)); - if (!x) { LogMsg("FoundInstance: Failed to allocate memory for result %##s", answer->rdata->u.name.c); return; } - - verbosedebugf("FoundInstance: %s %##s", AddRecord ? "Add" : "Rmv", answer->rdata->u.name.c); - AssignDomainName(&x->result, &answer->rdata->u.name); - if (AddRecord) - x->resultType = DNSServiceBrowserReplyAddInstance; - else x->resultType = DNSServiceBrowserReplyRemoveInstance; - x->next = NULL; - - DNSServiceBrowser *browser = (DNSServiceBrowser *)question->QuestionContext; - DNSServiceBrowserResult **p = &browser->results; - while (*p) p = &(*p)->next; - *p = x; - - LogOperation("%5d: DNSServiceBrowse(%##s, %s) RESULT %s %s", - browser->ClientMachPort, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv", RRDisplayString(m, answer)); - } +{ + (void)m; // Unused + + if (answer->rrtype != kDNSType_PTR) + { LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer->rrtype); return; } + + domainlabel name; + domainname type, domain; + if (!DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain)) + { + LogMsg("FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer", + answer->name->c, answer->rdata->u.name.c); + return; + } + + DNSServiceBrowserResult *x = mallocL("DNSServiceBrowserResult", sizeof(*x)); + if (!x) { LogMsg("FoundInstance: Failed to allocate memory for result %##s", answer->rdata->u.name.c); return; } + + verbosedebugf("FoundInstance: %s %##s", AddRecord ? "Add" : "Rmv", answer->rdata->u.name.c); + AssignDomainName(&x->result, &answer->rdata->u.name); + if (AddRecord) + x->resultType = DNSServiceBrowserReplyAddInstance; + else x->resultType = DNSServiceBrowserReplyRemoveInstance; + x->next = NULL; + + DNSServiceBrowser *browser = (DNSServiceBrowser *)question->QuestionContext; + DNSServiceBrowserResult **p = &browser->results; + while (*p) p = &(*p)->next; + *p = x; + + LogOperation("%5d: DNSServiceBrowse(%##s, %s) RESULT %s %s", + browser->ClientMachPort, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv", RRDisplayString(m, answer)); +} 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; - 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) - browser->qlist = question; - else - { - LogMsg("Error: AddDomainToBrowser: mDNS_StartBrowse %d", err); - freeL("DNSServiceBrowserQuestion", question); - } - return err; - } +{ + 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; + LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", browser->ClientMachPort, browser->type.c, d->c); + err = mDNS_StartBrowse(&mDNSStorage, &question->q, &browser->type, d, mDNSInterface_Any, 0, mDNSfalse, mDNSfalse, FoundInstance, browser); + if (!err) + browser->qlist = question; + else + { + LogMsg("Error: AddDomainToBrowser: mDNS_StartBrowse %d", err); + freeL("DNSServiceBrowserQuestion", question); + } + return err; +} mDNSexport void machserver_automatic_browse_domain_changed(const domainname *d, mDNSBool add) - { - DNSServiceBrowser *ptr; - 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 - { - DNSServiceBrowserQuestion **q = &ptr->qlist; - while (*q) - { - if (SameDomainName(&(*q)->domain, d)) - { - DNSServiceBrowserQuestion *rem = *q; - *q = (*q)->next; - mDNS_StopQueryWithRemoves(&mDNSStorage, &rem->q); - freeL("DNSServiceBrowserQuestion", rem); - return; - } - q = &(*q)->next; - } - LogMsg("Requested removal of default domain %##s not in client %5d's list", d->c, ptr->ClientMachPort); - } - } - } - } +{ + DNSServiceBrowser *ptr; + 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 + { + DNSServiceBrowserQuestion **q = &ptr->qlist; + while (*q) + { + if (SameDomainName(&(*q)->domain, d)) + { + DNSServiceBrowserQuestion *rem = *q; + *q = (*q)->next; + mDNS_StopQueryWithRemoves(&mDNSStorage, &rem->q); + freeL("DNSServiceBrowserQuestion", rem); + return; + } + q = &(*q)->next; + } + LogMsg("Requested removal of default domain %##s not in client %5d's list", d->c, ptr->ClientMachPort); + } + } + } +} mDNSexport kern_return_t provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver, mach_port_t client, - DNSCString regtype, DNSCString domain) - { - // Check client parameter - (void)unusedserver; // Unused - mStatus err = mStatus_NoError; - const char *errormsg = "Unknown"; - - if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } - if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; } - - // Check other parameters - domainname t, d; - 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->next = DNSServiceBrowserList; - DNSServiceBrowserList = x; - - if (domain[0]) - { - // Start browser for an explicit domain - 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 - { - DNameListElem *sdPtr; - // Start browser on all domains - x->DefaultDomain = mDNStrue; - if (!AutoBrowseDomains) { AbortClient(client, x); errormsg = "GetSearchDomainList"; goto fail; } - for (sdPtr = AutoBrowseDomains; sdPtr; sdPtr = sdPtr->next) - { - 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); - return(mStatus_NoError); - - badparam: - err = mStatus_BadParamErr; + DNSCString regtype, DNSCString domain) +{ + // Check client parameter + (void)unusedserver; // Unused + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + + if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } + if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; } + + // Check other parameters + domainname t, d; + 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->next = DNSServiceBrowserList; + DNSServiceBrowserList = x; + + if (domain[0]) + { + // Start browser for an explicit domain + 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 + { + DNameListElem *sdPtr; + // Start browser on all domains + x->DefaultDomain = mDNStrue; + if (!AutoBrowseDomains) { AbortClient(client, x); errormsg = "GetSearchDomainList"; goto fail; } + for (sdPtr = AutoBrowseDomains; sdPtr; sdPtr = sdPtr->next) + { + 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); + return(mStatus_NoError); + +badparam: + err = mStatus_BadParamErr; fail: - LogMsg("%5d: DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%d)", client, regtype, domain, errormsg, err); - return(err); - } + LogMsg("%5d: DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%d)", client, regtype, domain, errormsg, err); + return(err); +} //************************************************************************************************************* // Resolve Service Info - + mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query) - { - kern_return_t status; - DNSServiceResolver *x = (DNSServiceResolver *)query->ServiceInfoQueryContext; - NetworkInterfaceInfoOSX *ifx = IfindexToInterfaceInfoOSX(m, query->info->InterfaceID); - if (query->info->InterfaceID == mDNSInterface_LocalOnly || query->info->InterfaceID == mDNSInterface_P2P) ifx = mDNSNULL; - struct sockaddr_storage interface; - struct sockaddr_storage address; - char cstring[1024]; - int i, pstrlen = query->info->TXTinfo[0]; - (void)m; // Unused - - //debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name); - - if (query->info->TXTlen > sizeof(cstring)) return; - - mDNSPlatformMemZero(&interface, sizeof(interface)); - mDNSPlatformMemZero(&address, sizeof(address)); - - if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv4) - { - struct sockaddr_in *s = (struct sockaddr_in*)&interface; - s->sin_len = sizeof(*s); - s->sin_family = AF_INET; - s->sin_port = 0; - s->sin_addr.s_addr = ifx->ifinfo.ip.ip.v4.NotAnInteger; - } - else if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv6) - { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&interface; - sin6->sin6_len = sizeof(*sin6); - sin6->sin6_family = AF_INET6; - sin6->sin6_flowinfo = 0; - sin6->sin6_port = 0; - sin6->sin6_addr = *(struct in6_addr*)&ifx->ifinfo.ip.ip.v6; - sin6->sin6_scope_id = ifx->scope_id; - } - - if (query->info->ip.type == mDNSAddrType_IPv4) - { - struct sockaddr_in *s = (struct sockaddr_in*)&address; - s->sin_len = sizeof(*s); - s->sin_family = AF_INET; - s->sin_port = query->info->port.NotAnInteger; - s->sin_addr.s_addr = query->info->ip.ip.v4.NotAnInteger; - } - else - { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&address; - sin6->sin6_len = sizeof(*sin6); - sin6->sin6_family = AF_INET6; - sin6->sin6_port = query->info->port.NotAnInteger; - sin6->sin6_flowinfo = 0; - sin6->sin6_addr = *(struct in6_addr*)&query->info->ip.ip.v6; - sin6->sin6_scope_id = ifx ? ifx->scope_id : 0; - } - - // The OS X DNSServiceResolverResolve() API is defined using a C-string, - // but the mDNS_StartResolveService() call actually returns a packed block of P-strings. - // Hence we have to convert the P-string(s) to a C-string before returning the result to the client. - // ASCII-1 characters are used in the C-string as boundary markers, - // to indicate the boundaries between the original constituent P-strings. - for (i=1; iinfo->TXTlen; i++) - { - if (--pstrlen >= 0) - cstring[i-1] = query->info->TXTinfo[i]; - else - { - cstring[i-1] = 1; - pstrlen = query->info->TXTinfo[i]; - } - } - cstring[i-1] = 0; // Put the terminating NULL on the end - - LogOperation("%5d: DNSServiceResolver(%##s) -> %#a:%u", x->ClientMachPort, - x->i.name.c, &query->info->ip, mDNSVal16(query->info->port)); - status = DNSServiceResolverReply_rpc(x->ClientMachPort, - (char*)&interface, (char*)&address, cstring, 0, MDNS_MM_TIMEOUT); - if (status == MACH_SEND_TIMED_OUT) - AbortBlockedClient(x->ClientMachPort, "resolve", x); - } +{ + kern_return_t status; + DNSServiceResolver *x = (DNSServiceResolver *)query->ServiceInfoQueryContext; + NetworkInterfaceInfoOSX *ifx = IfindexToInterfaceInfoOSX(m, query->info->InterfaceID); + if (query->info->InterfaceID == mDNSInterface_LocalOnly || query->info->InterfaceID == mDNSInterface_P2P) ifx = mDNSNULL; + struct sockaddr_storage interface; + struct sockaddr_storage address; + char cstring[1024]; + int i, pstrlen = query->info->TXTinfo[0]; + (void)m; // Unused + + //debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name); + + if (query->info->TXTlen > sizeof(cstring)) return; + + mDNSPlatformMemZero(&interface, sizeof(interface)); + mDNSPlatformMemZero(&address, sizeof(address)); + + if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv4) + { + struct sockaddr_in *s = (struct sockaddr_in*)&interface; + s->sin_len = sizeof(*s); + s->sin_family = AF_INET; + s->sin_port = 0; + s->sin_addr.s_addr = ifx->ifinfo.ip.ip.v4.NotAnInteger; + } + else if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv6) + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&interface; + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_flowinfo = 0; + sin6->sin6_port = 0; + sin6->sin6_addr = *(struct in6_addr*)&ifx->ifinfo.ip.ip.v6; + sin6->sin6_scope_id = ifx->scope_id; + } + + if (query->info->ip.type == mDNSAddrType_IPv4) + { + struct sockaddr_in *s = (struct sockaddr_in*)&address; + s->sin_len = sizeof(*s); + s->sin_family = AF_INET; + s->sin_port = query->info->port.NotAnInteger; + s->sin_addr.s_addr = query->info->ip.ip.v4.NotAnInteger; + } + else + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&address; + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = query->info->port.NotAnInteger; + sin6->sin6_flowinfo = 0; + sin6->sin6_addr = *(struct in6_addr*)&query->info->ip.ip.v6; + sin6->sin6_scope_id = ifx ? ifx->scope_id : 0; + } + + // The OS X DNSServiceResolverResolve() API is defined using a C-string, + // but the mDNS_StartResolveService() call actually returns a packed block of P-strings. + // Hence we have to convert the P-string(s) to a C-string before returning the result to the client. + // ASCII-1 characters are used in the C-string as boundary markers, + // to indicate the boundaries between the original constituent P-strings. + for (i=1; iinfo->TXTlen; i++) + { + if (--pstrlen >= 0) + cstring[i-1] = query->info->TXTinfo[i]; + else + { + cstring[i-1] = 1; + pstrlen = query->info->TXTinfo[i]; + } + } + cstring[i-1] = 0; // Put the terminating NULL on the end + + LogOperation("%5d: DNSServiceResolver(%##s) -> %#a:%u", x->ClientMachPort, + x->i.name.c, &query->info->ip, mDNSVal16(query->info->port)); + status = DNSServiceResolverReply_rpc(x->ClientMachPort, + (char*)&interface, (char*)&address, cstring, 0, MDNS_MM_TIMEOUT); + if (status == MACH_SEND_TIMED_OUT) + AbortBlockedClient(x->ClientMachPort, "resolve", x); +} mDNSexport kern_return_t provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver, mach_port_t client, - DNSCString name, DNSCString regtype, DNSCString domain) - { - // Check client parameter - (void)unusedserver; // Unused - mStatus err = mStatus_NoError; - const char *errormsg = "Unknown"; - if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } - if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; } - - // Check other parameters - domainlabel n; - domainname t, d, srv; - if (!name[0] || !MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; } - if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; } - if (!domain[0] || !MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Bad Domain"; goto badparam; } - if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; } - - // Allocate memory, and handle failure - DNSServiceResolver *x = mallocL("DNSServiceResolver", sizeof(*x)); - if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } - - // Set up object, and link into list - x->ClientMachPort = client; - x->i.InterfaceID = mDNSInterface_Any; - x->i.name = srv; - x->ReportTime = NonZeroTime(mDNS_TimeNow(&mDNSStorage) + 130 * mDNSPlatformOneSecond); - x->next = DNSServiceResolverList; - DNSServiceResolverList = x; - - // Do the operation - LogOperation("%5d: DNSServiceResolve(%##s) START", client, x->i.name.c); - err = mDNS_StartResolveService(&mDNSStorage, &x->q, &x->i, FoundInstanceInfo, x); - if (err) { AbortClient(client, x); errormsg = "mDNS_StartResolveService"; goto fail; } - - // Succeeded: Wrap up and return - EnableDeathNotificationForClient(client, x); - return(mStatus_NoError); + DNSCString name, DNSCString regtype, DNSCString domain) +{ + // Check client parameter + (void)unusedserver; // Unused + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } + if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; } + + // Check other parameters + domainlabel n; + domainname t, d, srv; + if (!name[0] || !MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; } + if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; } + if (!domain[0] || !MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Bad Domain"; goto badparam; } + if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; } + + // Allocate memory, and handle failure + DNSServiceResolver *x = mallocL("DNSServiceResolver", sizeof(*x)); + if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + + // Set up object, and link into list + x->ClientMachPort = client; + x->i.InterfaceID = mDNSInterface_Any; + x->i.name = srv; + x->ReportTime = NonZeroTime(mDNS_TimeNow(&mDNSStorage) + 130 * mDNSPlatformOneSecond); + x->next = DNSServiceResolverList; + DNSServiceResolverList = x; + + // Do the operation + LogOperation("%5d: DNSServiceResolve(%##s) START", client, x->i.name.c); + err = mDNS_StartResolveService(&mDNSStorage, &x->q, &x->i, FoundInstanceInfo, x); + if (err) { AbortClient(client, x); errormsg = "mDNS_StartResolveService"; goto fail; } + + // Succeeded: Wrap up and return + EnableDeathNotificationForClient(client, x); + return(mStatus_NoError); badparam: - err = mStatus_BadParamErr; + err = mStatus_BadParamErr; fail: - LogMsg("%5d: DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%d)", client, name, regtype, domain, errormsg, err); - return(err); - } + LogMsg("%5d: DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%d)", client, name, regtype, domain, errormsg, err); + return(err); +} //************************************************************************************************************* // Registration mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay) - { - m->p->NotifyUser = NonZeroTime(m->timenow + delay); - } +{ + m->p->NotifyUser = NonZeroTime(m->timenow + delay); +} mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const srs, mStatus result) - { - ServiceInstance *si = (ServiceInstance*)srs->ServiceContext; - - if (result == mStatus_NoError) - { - kern_return_t status; - 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(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", 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 (si->autoname && CountPeerRegistrations(m, srs) == 0) - { - // On conflict for an autoname service, rename and reregister *all* autoname services - IncrementLabelSuffix(&m->nicelabel, mDNStrue); - mDNS_ConfigChanged(m); - } - else if (si->autoname) - { +{ + ServiceInstance *si = (ServiceInstance*)srs->ServiceContext; + + if (result == mStatus_NoError) + { + kern_return_t status; + 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(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", 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 (si->autoname && CountPeerRegistrations(m, srs) == 0) + { + // On conflict for an autoname service, rename and reregister *all* autoname services + IncrementLabelSuffix(&m->nicelabel, mDNStrue); + mDNS_ConfigChanged(m); + } + 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(si->ClientMachPort, result, MDNS_MM_TIMEOUT); - if (status == MACH_SEND_TIMED_OUT) - AbortBlockedClient(si->ClientMachPort, "registration conflict", NULL); - } - } - - else if (result == mStatus_MemFree) - { - if (si->renameonmemfree) // We intentionally terminated registration so we could re-register with new name - { - debugf("RegCallback renaming %#s to %#s", si->name.c, m->nicelabel.c); - si->renameonmemfree = mDNSfalse; - si->name = m->nicelabel; - mDNS_RenameAndReregisterService(m, srs, &si->name); - } - else - { - // SANITY CHECK: make sure service instance is no longer in any ServiceRegistration's list - DNSServiceRegistration *r; - for (r = DNSServiceRegistrationList; r; r = r->next) - { - ServiceInstance **sp = &r->regs; - while (*sp) - { - if (*sp == si) { LogMsg("RegCallback: %##s Still in list; removing", srs->RR_SRV.resrec.name->c); *sp = (*sp)->next; break; } - sp = &(*sp)->next; - } - } - // END SANITY CHECK - FreeServiceInstance(si); - } - } - - else if (result != mStatus_NATTraversal) - LogMsg("%5d: DNSServiceRegistration(%##s, %u) Unknown Result %d", si->ClientMachPort, srs->RR_SRV.resrec.name->c, SRS_PORT(srs), result); - } + } + 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(si->ClientMachPort, result, MDNS_MM_TIMEOUT); + if (status == MACH_SEND_TIMED_OUT) + AbortBlockedClient(si->ClientMachPort, "registration conflict", NULL); + } + } + + else if (result == mStatus_MemFree) + { + if (si->renameonmemfree) // We intentionally terminated registration so we could re-register with new name + { + debugf("RegCallback renaming %#s to %#s", si->name.c, m->nicelabel.c); + si->renameonmemfree = mDNSfalse; + si->name = m->nicelabel; + mDNS_RenameAndReregisterService(m, srs, &si->name); + } + else + { + // SANITY CHECK: make sure service instance is no longer in any ServiceRegistration's list + DNSServiceRegistration *r; + for (r = DNSServiceRegistrationList; r; r = r->next) + { + ServiceInstance **sp = &r->regs; + while (*sp) + { + if (*sp == si) { LogMsg("RegCallback: %##s Still in list; removing", srs->RR_SRV.resrec.name->c); *sp = (*sp)->next; break; } + sp = &(*sp)->next; + } + } + // END SANITY CHECK + FreeServiceInstance(si); + } + } + + else if (result != mStatus_NATTraversal) + LogMsg("%5d: DNSServiceRegistration(%##s, %u) Unknown Result %d", 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->renameonmemfree = 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, 0); - if (!err) - { - si->next = x->regs; - x->regs = si; - } - else - { - LogMsg("Error %d for registration of service in domain %##s", err, domain->c); - freeL("ServiceInstance", si); - } - return err; - } +{ + 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->renameonmemfree = 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, 0); + if (!err) + { + si->next = x->regs; + x->regs = si; + } + else + { + LogMsg("Error %d for registration of service in domain %##s", err, domain->c); + freeL("ServiceInstance", si); + } + return err; +} mDNSexport void machserver_automatic_registration_domain_changed(const domainname *d, mDNSBool add) - { - DNSServiceRegistration *reg; - - for (reg = DNSServiceRegistrationList; reg; reg = reg->next) - { - if (reg->DefaultDomain) - { - if (add) - AddServiceInstance(reg, d); - else - { - ServiceInstance **si = ®->regs; - while (*si) - { - if (SameDomainName(&(*si)->domain, d)) - { - ServiceInstance *s = *si; - *si = (*si)->next; - if (mDNS_DeregisterService(&mDNSStorage, &s->srs)) FreeServiceInstance(s); // only free memory synchronously on error - break; - } - si = &(*si)->next; - } - if (!si) debugf("Requested removal of default domain %##s not in client %5d's list", d, reg->ClientMachPort); // normal if registration failed - } - } - } - } +{ + DNSServiceRegistration *reg; + + for (reg = DNSServiceRegistrationList; reg; reg = reg->next) + { + if (reg->DefaultDomain) + { + if (add) + AddServiceInstance(reg, d); + else + { + ServiceInstance **si = ®->regs; + while (*si) + { + if (SameDomainName(&(*si)->domain, d)) + { + ServiceInstance *s = *si; + *si = (*si)->next; + if (mDNS_DeregisterService(&mDNSStorage, &s->srs)) FreeServiceInstance(s); // only free memory synchronously on error + break; + } + si = &(*si)->next; + } + if (!si) debugf("Requested removal of default domain %##s not in client %5d's list", d, reg->ClientMachPort); // normal if registration failed + } + } + } +} 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) - { - (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; } + DNSCString name, DNSCString regtype, DNSCString domain, IPPort IpPort, DNSCString txtRecord) +{ + (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 - 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; - domainname t, d; - domainname srv; - if (!name[0]) n = mDNSStorage.nicelabel; - else if (!MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; } - if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; } - if (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) { errormsg = "Bad Domain"; goto badparam; } - if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; } - - unsigned char txtinfo[1024] = ""; - unsigned int data_len = 0; - unsigned int size = sizeof(RDataBody); - unsigned char *pstring = &txtinfo[data_len]; - char *ptr = txtRecord; - - // The OS X DNSServiceRegistrationCreate() API is defined using a C-string, - // but the mDNS_RegisterService() call actually requires a packed block of P-strings. - // Hence we have to convert the C-string to a P-string. - // ASCII-1 characters are allowed in the C-string as boundary markers, - // so that a single C-string can be used to represent one or more P-strings. - while (*ptr) - { - if (++data_len >= sizeof(txtinfo)) { errormsg = "TXT record too long"; goto badtxt; } - if (*ptr == 1) // If this is our boundary marker, start a new P-string - { - pstring = &txtinfo[data_len]; - pstring[0] = 0; - ptr++; - } - else - { - if (pstring[0] == 255) { errormsg = "TXT record invalid (component longer than 255)"; goto badtxt; } - pstring[++pstring[0]] = *ptr++; - } - } - - data_len++; - if (size < data_len) - size = data_len; - - // 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 (!mDNSIPPortIsZero(port)) - { - 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)); - } - - // Allocate memory, and handle failure - DNSServiceRegistration *x = mallocL("DNSServiceRegistration", sizeof(*x)); - if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } - mDNSPlatformMemZero(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; - - LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\", %u) START", - x->ClientMachPort, name, regtype, domain, mDNSVal16(port)); - - err = AddServiceInstance(x, &d); - if (err) { AbortClient(client, x); errormsg = "mDNS_RegisterService"; goto fail; } // bail if .local (or explicit domain) fails - - if (x->DefaultDomain) - { - DNameListElem *p; - for (p = AutoRegistrationDomains; p; p = p->next) - AddServiceInstance(x, &p->name); - } - - // Succeeded: Wrap up and return - EnableDeathNotificationForClient(client, x); - return(mStatus_NoError); + 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; + domainname t, d; + domainname srv; + if (!name[0]) n = mDNSStorage.nicelabel; + else if (!MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; } + if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; } + if (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) { errormsg = "Bad Domain"; goto badparam; } + if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; } + + unsigned char txtinfo[1024] = ""; + unsigned int data_len = 0; + unsigned int size = sizeof(RDataBody); + unsigned char *pstring = &txtinfo[data_len]; + char *ptr = txtRecord; + + // The OS X DNSServiceRegistrationCreate() API is defined using a C-string, + // but the mDNS_RegisterService() call actually requires a packed block of P-strings. + // Hence we have to convert the C-string to a P-string. + // ASCII-1 characters are allowed in the C-string as boundary markers, + // so that a single C-string can be used to represent one or more P-strings. + while (*ptr) + { + if (++data_len >= sizeof(txtinfo)) { errormsg = "TXT record too long"; goto badtxt; } + if (*ptr == 1) // If this is our boundary marker, start a new P-string + { + pstring = &txtinfo[data_len]; + pstring[0] = 0; + ptr++; + } + else + { + if (pstring[0] == 255) { errormsg = "TXT record invalid (component longer than 255)"; goto badtxt; } + pstring[++pstring[0]] = *ptr++; + } + } + + data_len++; + if (size < data_len) + size = data_len; + + // 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 (!mDNSIPPortIsZero(port)) + { + 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)); + } + + // Allocate memory, and handle failure + DNSServiceRegistration *x = mallocL("DNSServiceRegistration", sizeof(*x)); + if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + mDNSPlatformMemZero(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; + + LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\", %u) START", + x->ClientMachPort, name, regtype, domain, mDNSVal16(port)); + + err = AddServiceInstance(x, &d); + if (err) { AbortClient(client, x); errormsg = "mDNS_RegisterService"; goto fail; } // bail if .local (or explicit domain) fails + + if (x->DefaultDomain) + { + DNameListElem *p; + for (p = AutoRegistrationDomains; p; p = p->next) + AddServiceInstance(x, &p->name); + } + + // Succeeded: Wrap up and return + EnableDeathNotificationForClient(client, x); + return(mStatus_NoError); badtxt: - LogMsg("%5d: TXT record: %.100s...", client, txtRecord); + LogMsg("%5d: TXT record: %.100s...", client, txtRecord); badparam: - err = mStatus_BadParamErr; + err = mStatus_BadParamErr; fail: - LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%d)", - client, name, regtype, domain, mDNSVal16(port), errormsg, err); - return(err); - } + LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%d)", + client, name, regtype, domain, mDNSVal16(port), errormsg, err); + return(err); +} mDNSlocal void mDNSPreferencesSetNames(mDNS *const m, int key, domainlabel *old, domainlabel *new) - { - domainlabel *prevold, *prevnew; - switch (key) - { - case kmDNSComputerName: - case kmDNSLocalHostName: - if (key == kmDNSComputerName) - { - prevold = &m->p->prevoldnicelabel; - prevnew = &m->p->prevnewnicelabel; - } - else - { - prevold = &m->p->prevoldhostlabel; - prevnew = &m->p->prevnewhostlabel; - } - // There are a few cases where we need to invoke the helper. - // - // 1. If the "old" label and "new" label are not same, it means there is a conflict. We need - // to invoke the helper so that it pops up a dialogue to inform the user about the - // conflict - // - // 2. If the "old" label and "new" label are same, it means the user has set the host/nice label - // through the preferences pane. We may have to inform the helper as it may have popped up - // a dialogue previously (due to a conflict) and it needs to suppress it now. We can avoid invoking - // the helper in this case if the previous values (old and new) that we told helper last time - // are same. If the previous old and new values are same, helper does not care. - // - // Note: "new" can be NULL when we have repeated conflicts and we are asking helper to give up. "old" - // is not called with NULL today, but this makes it future proof. - if (!old || !new || !SameDomainLabelCS(old->c, new->c) || - !SameDomainLabelCS(old->c, prevold->c) || - !SameDomainLabelCS(new->c, prevnew->c)) - { - if (old) - *prevold = *old; - else - prevold->c[0] = 0; - if (new) - *prevnew = *new; - else - prevnew->c[0] = 0; - mDNSPreferencesSetName(key, old, new); - } - else - { - LogInfo("mDNSPreferencesSetNames not invoking helper %s %#s, %s %#s, old %#s, new %#s", - (key == kmDNSComputerName ? "prevoldnicelabel" : "prevoldhostlabel"), prevold->c, - (key == kmDNSComputerName ? "prevnewnicelabel" : "prevnewhostlabel"), prevnew->c, - old->c, new->c); - } - break; - default: - LogMsg("mDNSPreferencesSetNames: unrecognized key: %d", key); - return; - } - } +{ + domainlabel *prevold, *prevnew; + switch (key) + { + case kmDNSComputerName: + case kmDNSLocalHostName: + if (key == kmDNSComputerName) + { + prevold = &m->p->prevoldnicelabel; + prevnew = &m->p->prevnewnicelabel; + } + else + { + prevold = &m->p->prevoldhostlabel; + prevnew = &m->p->prevnewhostlabel; + } + // There are a few cases where we need to invoke the helper. + // + // 1. If the "old" label and "new" label are not same, it means there is a conflict. We need + // to invoke the helper so that it pops up a dialogue to inform the user about the + // conflict + // + // 2. If the "old" label and "new" label are same, it means the user has set the host/nice label + // through the preferences pane. We may have to inform the helper as it may have popped up + // a dialogue previously (due to a conflict) and it needs to suppress it now. We can avoid invoking + // the helper in this case if the previous values (old and new) that we told helper last time + // are same. If the previous old and new values are same, helper does not care. + // + // Note: "new" can be NULL when we have repeated conflicts and we are asking helper to give up. "old" + // is not called with NULL today, but this makes it future proof. + if (!old || !new || !SameDomainLabelCS(old->c, new->c) || + !SameDomainLabelCS(old->c, prevold->c) || + !SameDomainLabelCS(new->c, prevnew->c)) + { + if (old) + *prevold = *old; + else + prevold->c[0] = 0; + if (new) + *prevnew = *new; + else + prevnew->c[0] = 0; + mDNSPreferencesSetName(key, old, new); + } + else + { + LogInfo("mDNSPreferencesSetNames not invoking helper %s %#s, %s %#s, old %#s, new %#s", + (key == kmDNSComputerName ? "prevoldnicelabel" : "prevoldhostlabel"), prevold->c, + (key == kmDNSComputerName ? "prevnewnicelabel" : "prevnewhostlabel"), prevnew->c, + old->c, new->c); + } + break; + default: + LogMsg("mDNSPreferencesSetNames: unrecognized key: %d", key); + return; + } +} mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result) - { - (void)m; // Unused - if (result == mStatus_NoError) - { - if (!SameDomainLabelCS(m->p->userhostlabel.c, m->hostlabel.c)) - LogInfo("Local Hostname changed from \"%#s.local\" to \"%#s.local\"", m->p->userhostlabel.c, m->hostlabel.c); - // One second pause in case we get a Computer Name update too -- don't want to alert the user twice - RecordUpdatedNiceLabel(m, mDNSPlatformOneSecond); - } - else if (result == mStatus_NameConflict) - { - LogInfo("Local Hostname conflict for \"%#s.local\"", m->hostlabel.c); - if (!m->p->HostNameConflict) m->p->HostNameConflict = NonZeroTime(m->timenow); - else if (m->timenow - m->p->HostNameConflict > 60 * mDNSPlatformOneSecond) - { - // Tell the helper we've given up - mDNSPreferencesSetNames(m, kmDNSLocalHostName, &m->p->userhostlabel, NULL); - } - } - else if (result == mStatus_GrowCache) - { - // Allocate another chunk of cache storage - CacheEntity *storage = mallocL("mStatus_GrowCache", sizeof(CacheEntity) * RR_CACHE_SIZE); - //LogInfo("GrowCache %d * %d = %d", sizeof(CacheEntity), RR_CACHE_SIZE, sizeof(CacheEntity) * RR_CACHE_SIZE); - if (storage) mDNS_GrowCache(m, storage, RR_CACHE_SIZE); - } - else if (result == mStatus_ConfigChanged) - { - // Tell the helper we've seen a change in the labels. It will dismiss the name conflict alert if needed. - mDNSPreferencesSetNames(m, kmDNSComputerName, &m->p->usernicelabel, &m->nicelabel); - mDNSPreferencesSetNames(m, kmDNSLocalHostName, &m->p->userhostlabel, &m->hostlabel); - - // First we check our list of old Mach-based registered services, to see if any need to be updated to a new name - DNSServiceRegistration *r; - for (r = DNSServiceRegistrationList; r; r=r->next) - if (r->autoname) - { - ServiceInstance *si; - for (si = r->regs; si; si = si->next) - { - if (!SameDomainLabelCS(si->name.c, m->nicelabel.c)) - { - debugf("NetworkChanged renaming %##s to %#s", si->srs.RR_SRV.resrec.name->c, m->nicelabel.c); - si->renameonmemfree = mDNStrue; - if (mDNS_DeregisterService_drt(m, &si->srs, mDNS_Dereg_rapid)) - RegCallback(m, &si->srs, mStatus_MemFree); // If service deregistered already, we can re-register immediately - } - } - } - - // Then we call into the UDS daemon code, to let it do the same - udsserver_handle_configchange(m); - } - } +{ + (void)m; // Unused + if (result == mStatus_NoError) + { + if (!SameDomainLabelCS(m->p->userhostlabel.c, m->hostlabel.c)) + LogInfo("Local Hostname changed from \"%#s.local\" to \"%#s.local\"", m->p->userhostlabel.c, m->hostlabel.c); + // One second pause in case we get a Computer Name update too -- don't want to alert the user twice + RecordUpdatedNiceLabel(m, mDNSPlatformOneSecond); + } + else if (result == mStatus_NameConflict) + { + LogInfo("Local Hostname conflict for \"%#s.local\"", m->hostlabel.c); + if (!m->p->HostNameConflict) m->p->HostNameConflict = NonZeroTime(m->timenow); + else if (m->timenow - m->p->HostNameConflict > 60 * mDNSPlatformOneSecond) + { + // Tell the helper we've given up + mDNSPreferencesSetNames(m, kmDNSLocalHostName, &m->p->userhostlabel, NULL); + } + } + else if (result == mStatus_GrowCache) + { + // Allocate another chunk of cache storage + CacheEntity *storage = mallocL("mStatus_GrowCache", sizeof(CacheEntity) * RR_CACHE_SIZE); + //LogInfo("GrowCache %d * %d = %d", sizeof(CacheEntity), RR_CACHE_SIZE, sizeof(CacheEntity) * RR_CACHE_SIZE); + if (storage) mDNS_GrowCache(m, storage, RR_CACHE_SIZE); + } + else if (result == mStatus_ConfigChanged) + { + // Tell the helper we've seen a change in the labels. It will dismiss the name conflict alert if needed. + mDNSPreferencesSetNames(m, kmDNSComputerName, &m->p->usernicelabel, &m->nicelabel); + mDNSPreferencesSetNames(m, kmDNSLocalHostName, &m->p->userhostlabel, &m->hostlabel); + + // First we check our list of old Mach-based registered services, to see if any need to be updated to a new name + DNSServiceRegistration *r; + for (r = DNSServiceRegistrationList; r; r=r->next) + if (r->autoname) + { + ServiceInstance *si; + for (si = r->regs; si; si = si->next) + { + if (!SameDomainLabelCS(si->name.c, m->nicelabel.c)) + { + debugf("NetworkChanged renaming %##s to %#s", si->srs.RR_SRV.resrec.name->c, m->nicelabel.c); + si->renameonmemfree = mDNStrue; + if (mDNS_DeregisterService_drt(m, &si->srs, mDNS_Dereg_rapid)) + RegCallback(m, &si->srs, mStatus_MemFree); // If service deregistered already, we can re-register immediately + } + } + } + + // Then we call into the UDS daemon code, to let it do the same + udsserver_handle_configchange(m); + } +} //************************************************************************************************************* // Add / Update / Remove records from existing Registration mDNSexport kern_return_t provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t unusedserver, mach_port_t client, - int type, const char *data, mach_msg_type_number_t data_len, uint32_t ttl, natural_t *reference) - { - // Check client parameter - uint32_t id; - mStatus err = mStatus_NoError; - const char *errormsg = "Unknown"; - DNSServiceRegistration *x = DNSServiceRegistrationList; - if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } - 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, 0); - - if (err) - { - freeL("Extra Resource Record", extra); - errormsg = "mDNS_AddRecordToService"; - goto fail; - } - - extra->ClientID = id; - } - - return mStatus_NoError; + int type, const char *data, mach_msg_type_number_t data_len, uint32_t ttl, natural_t *reference) +{ + // Check client parameter + uint32_t id; + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + DNSServiceRegistration *x = DNSServiceRegistrationList; + if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } + 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, 0); + + if (err) + { + freeL("Extra Resource Record", extra); + errormsg = "mDNS_AddRecordToService"; + goto fail; + } + + extra->ClientID = id; + } + + return mStatus_NoError; fail: - LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%d)", client, x ? x->name.c : (mDNSu8*)"\x8""«NULL»", type, data_len, errormsg, err); - return mStatus_UnknownErr; - } + LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%d)", client, x ? x->name.c : (mDNSu8*)"\x8" "«NULL»", type, data_len, errormsg, err); + return mStatus_UnknownErr; +} mDNSlocal void UpdateCallback(mDNS *const m, AuthRecord *const rr, RData *OldRData, mDNSu16 OldRDLen) - { - (void)m; // Unused - (void)OldRDLen; // Unused - if (OldRData != &rr->rdatastorage) - freeL("Old RData", OldRData); - } +{ + (void)m; // Unused + (void)OldRDLen; // Unused + if (OldRData != &rr->rdatastorage) + freeL("Old RData", OldRData); +} mDNSlocal mStatus UpdateRecord(ServiceRecordSet *srs, mach_port_t client, AuthRecord *rr, const char *data, mach_msg_type_number_t data_len, uint32_t ttl) - { +{ // Check client parameter - mStatus err = mStatus_NoError; - const char *errormsg = "Unknown"; - const domainname *name = (const domainname *)""; + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + const domainname *name = (const domainname *)""; - name = srs->RR_SRV.resrec.name; + name = srs->RR_SRV.resrec.name; - unsigned int size = sizeof(RDataBody); + unsigned int size = sizeof(RDataBody); if (size < data_len) - size = data_len; - - // Allocate memory, and handle failure - RData *newrdata = mallocL("RData", sizeof(*newrdata) - sizeof(RDataBody) + size); - if (!newrdata) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } - - // Fill in new length, and data - newrdata->MaxRDLength = size; - memcpy(&newrdata->u, data, data_len); - - // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct, - // since RFC 1035 specifies a TXT record as "One or more s", not "Zero or more s". - // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here. - if (rr->resrec.rrtype == kDNSType_TXT && data_len == 0) { data_len = 1; newrdata->u.txt.c[0] = 0; } - - // Do the operation - LogOperation("%5d: DNSServiceRegistrationUpdateRecord(%##s, new length %d)", - client, srs->RR_SRV.resrec.name->c, data_len); - - err = mDNS_Update(&mDNSStorage, rr, ttl, data_len, newrdata, UpdateCallback); - if (err) - { - errormsg = "mDNS_Update"; - freeL("RData", newrdata); - return err; - } - return(mStatus_NoError); + size = data_len; + + // Allocate memory, and handle failure + RData *newrdata = mallocL("RData", sizeof(*newrdata) - sizeof(RDataBody) + size); + if (!newrdata) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + + // Fill in new length, and data + newrdata->MaxRDLength = size; + memcpy(&newrdata->u, data, data_len); + + // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct, + // since RFC 1035 specifies a TXT record as "One or more s", not "Zero or more s". + // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here. + if (rr->resrec.rrtype == kDNSType_TXT && data_len == 0) { data_len = 1; newrdata->u.txt.c[0] = 0; } + + // Do the operation + LogOperation("%5d: DNSServiceRegistrationUpdateRecord(%##s, new length %d)", + client, srs->RR_SRV.resrec.name->c, data_len); + + err = mDNS_Update(&mDNSStorage, rr, ttl, data_len, newrdata, UpdateCallback); + if (err) + { + errormsg = "mDNS_Update"; + freeL("RData", newrdata); + return err; + } + return(mStatus_NoError); fail: - LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %d) failed: %s (%d)", client, name->c, data_len, errormsg, err); - return(err); - } + LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %d) failed: %s (%d)", 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) - { + natural_t reference, const char *data, mach_msg_type_number_t data_len, uint32_t ttl) +{ // Check client parameter - mStatus err = mStatus_NoError; - const char *errormsg = "Unknown"; - const domainname *name = (const domainname *)""; - ServiceInstance *si; + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + const domainname *name = (const domainname *)""; + 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; - 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; } - - for (si = x->regs; si; si = si->next) - { - AuthRecord *r = NULL; - - // 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; + DNSServiceRegistration *x = DNSServiceRegistrationList; + while (x && x->ClientMachPort != client) x = x->next; + if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; } + + // Check other parameters + if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; } + + for (si = x->regs; si; si = si->next) + { + AuthRecord *r = NULL; + + // 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 (%d)", client, name->c, reference, data_len, errormsg, err); - return(err); - } + LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, %d) failed: %s (%d)", client, name->c, reference, data_len, errormsg, err); + return(err); +} mDNSlocal mStatus RemoveRecord(ServiceRecordSet *srs, ExtraResourceRecord *extra, mach_port_t client) - { - const domainname *const name = srs->RR_SRV.resrec.name; - mStatus err = mStatus_NoError; +{ + const domainname *const name = srs->RR_SRV.resrec.name; + mStatus err = mStatus_NoError; + + // Do the operation + LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s)", client, srs->RR_SRV.resrec.name->c); - // Do the operation - LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s)", client, srs->RR_SRV.resrec.name->c); + err = mDNS_RemoveRecordFromService(&mDNSStorage, srs, extra, FreeExtraRR, extra); + if (err) LogMsg("%5d: DNSServiceRegistrationRemoveRecord (%##s) failed: %d", client, name->c, err); - err = mDNS_RemoveRecordFromService(&mDNSStorage, srs, extra, FreeExtraRR, extra); - if (err) LogMsg("%5d: DNSServiceRegistrationRemoveRecord (%##s) failed: %d", client, name->c, err); - - return err; - } + return err; +} mDNSexport kern_return_t provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver, mach_port_t client, - natural_t reference) - { - // Check client parameter - (void)unusedserver; // Unused - mStatus err = mStatus_NoError; - const char *errormsg = "Unknown"; - 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; } - - for (si = x->regs; si; si = si->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; } - } - - return mStatus_NoError; + natural_t reference) +{ + // Check client parameter + (void)unusedserver; // Unused + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } + DNSServiceRegistration *x = DNSServiceRegistrationList; + ServiceInstance *si; + + while (x && x->ClientMachPort != client) x = x->next; + if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; } + + for (si = x->regs; si; si = si->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; } + } + + return mStatus_NoError; fail: - LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%X) failed: %s (%d)", client, reference, errormsg, err); - return(err); - } + LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%X) failed: %s (%d)", client, reference, errormsg, err); + return(err); +} //************************************************************************************************************* #if COMPILER_LIKES_PRAGMA_MARK @@ -1574,27 +1561,27 @@ fail: #endif mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) - { - mig_reply_error_t *request = msg; - mig_reply_error_t *reply; - mach_msg_return_t mr; - int options; - (void)port; // Unused - (void)size; // Unused - (void)info; // Unused - - KQueueLock(&mDNSStorage); - - /* allocate a reply buffer */ - reply = CFAllocatorAllocate(NULL, provide_DNSServiceDiscoveryRequest_subsystem.maxsize, 0); - - /* call the MiG server routine */ - (void) DNSServiceDiscoveryRequest_server(&request->Head, &reply->Head); - - if (!(reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (reply->RetCode != KERN_SUCCESS)) - { +{ + mig_reply_error_t *request = msg; + mig_reply_error_t *reply; + mach_msg_return_t mr; + int options; + (void)port; // Unused + (void)size; // Unused + (void)info; // Unused + + KQueueLock(&mDNSStorage); + + /* allocate a reply buffer */ + reply = CFAllocatorAllocate(NULL, provide_DNSServiceDiscoveryRequest_subsystem.maxsize, 0); + + /* call the MiG server routine */ + (void) DNSServiceDiscoveryRequest_server(&request->Head, &reply->Head); + + if (!(reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (reply->RetCode != KERN_SUCCESS)) + { if (reply->RetCode == MIG_NO_REPLY) - { + { /* * This return code is a little tricky -- it appears that the * demux routine found an error of some sort, but since that @@ -1603,7 +1590,7 @@ mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, vo */ CFAllocatorDeallocate(NULL, reply); goto done; - } + } /* * destroy any out-of-line data in the request buffer but don't destroy @@ -1611,16 +1598,16 @@ mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, vo */ request->Head.msgh_remote_port = MACH_PORT_NULL; mach_msg_destroy(&request->Head); - } + } if (reply->Head.msgh_remote_port == MACH_PORT_NULL) - { + { /* no reply port, so destroy the reply */ if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) mach_msg_destroy(&reply->Head); CFAllocatorDeallocate(NULL, reply); goto done; - } + } /* * send reply. @@ -1638,362 +1625,353 @@ mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, vo if (MACH_MSGH_BITS_REMOTE(reply->Head.msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE) options |= MACH_SEND_TIMEOUT; - mr = mach_msg(&reply->Head, /* msg */ - options, /* option */ - reply->Head.msgh_size, /* send_size */ - 0, /* rcv_size */ - MACH_PORT_NULL, /* rcv_name */ - MACH_MSG_TIMEOUT_NONE, /* timeout */ - MACH_PORT_NULL); /* notify */ + mr = mach_msg(&reply->Head, /* msg */ + options, /* option */ + reply->Head.msgh_size, /* send_size */ + 0, /* rcv_size */ + MACH_PORT_NULL, /* rcv_name */ + MACH_MSG_TIMEOUT_NONE, /* timeout */ + MACH_PORT_NULL); /* notify */ /* Has a message error occurred? */ switch (mr) - { - case MACH_SEND_INVALID_DEST: - case MACH_SEND_TIMED_OUT: - /* the reply can't be delivered, so destroy it */ - mach_msg_destroy(&reply->Head); - break; - - default : - /* Includes success case. */ - break; - } + { + case MACH_SEND_INVALID_DEST: + case MACH_SEND_TIMED_OUT: + /* the reply can't be delivered, so destroy it */ + mach_msg_destroy(&reply->Head); + break; + + default: + /* Includes success case. */ + break; + } CFAllocatorDeallocate(NULL, reply); - + done: - KQueueUnlock(&mDNSStorage, "Mach client event"); - } + KQueueUnlock(&mDNSStorage, "Mach client event"); +} mDNSlocal kern_return_t registerBootstrapService() - { - kern_return_t status; - mach_port_t service_send_port, service_rcv_port; - - debugf("Registering Bootstrap Service"); - - /* - * See if our service name is already registered and if we have privilege to check in. - */ - status = bootstrap_check_in(bootstrap_port, (char*)kmDNSBootstrapName, &service_rcv_port); - if (status == KERN_SUCCESS) - { - /* - * If so, we must be a followup instance of an already defined server. In that case, - * the bootstrap port we inherited from our parent is the server's privilege port, so set - * that in case we have to unregister later (which requires the privilege port). - */ - server_priv_port = bootstrap_port; - restarting_via_mach_init = TRUE; - } - else if (status == BOOTSTRAP_UNKNOWN_SERVICE) - { - status = bootstrap_create_server(bootstrap_port, "/usr/sbin/mDNSResponder", getuid(), - FALSE /* relaunch immediately, not on demand */, &server_priv_port); - if (status != KERN_SUCCESS) return status; - - status = bootstrap_create_service(server_priv_port, (char*)kmDNSBootstrapName, &service_send_port); - if (status != KERN_SUCCESS) - { - mach_port_deallocate(mach_task_self(), server_priv_port); - return status; - } - - status = bootstrap_check_in(server_priv_port, (char*)kmDNSBootstrapName, &service_rcv_port); - if (status != KERN_SUCCESS) - { - mach_port_deallocate(mach_task_self(), server_priv_port); - mach_port_deallocate(mach_task_self(), service_send_port); - return status; - } - assert(service_send_port == service_rcv_port); - } - - /* - * We have no intention of responding to requests on the service port. We are not otherwise - * a Mach port-based service. We are just using this mechanism for relaunch facilities. - * So, we can dispose of all the rights we have for the service port. We don't destroy the - * send right for the server's privileged bootstrap port - in case we have to unregister later. - */ - mach_port_destroy(mach_task_self(), service_rcv_port); - return status; - } +{ + kern_return_t status; + mach_port_t service_rcv_port; + + LogMsg("Registering Bootstrap Service"); + + /* + * See if our service name is already registered and if we have privilege to check in. + */ + status = bootstrap_check_in(bootstrap_port, (char*)kmDNSBootstrapName, &service_rcv_port); + if (status == KERN_SUCCESS) + { + /* + * If so, we must be a followup instance of an already defined server. In that case, + * the bootstrap port we inherited from our parent is the server's privilege port, so set + * that in case we have to unregister later (which requires the privilege port). + */ + server_priv_port = bootstrap_port; + restarting_via_mach_init = TRUE; + } + else + { + LogMsg("registerBootstrapService: ERROR!! Registering Bootstrap Service failed %d", status); + return status; + } + + /* + * We have no intention of responding to requests on the service port. We are not otherwise + * a Mach port-based service. We are just using this mechanism for relaunch facilities. + * So, we can dispose of all the rights we have for the service port. We don't destroy the + * send right for the server's privileged bootstrap port - in case we have to unregister later. + */ + mach_port_destroy(mach_task_self(), service_rcv_port); + return status; +} mDNSlocal kern_return_t destroyBootstrapService() - { - debugf("Destroying Bootstrap Service"); - return bootstrap_register(server_priv_port, (char*)kmDNSBootstrapName, MACH_PORT_NULL); - } +{ + debugf("Destroying Bootstrap Service"); + return bootstrap_register(server_priv_port, (char*)kmDNSBootstrapName, MACH_PORT_NULL); +} mDNSlocal void ExitCallback(int sig) - { - (void)sig; // Unused - LogMsg("%s stopping", mDNSResponderVersionString); - - debugf("ExitCallback"); - if (!mDNS_DebugMode && !started_via_launchdaemon) - destroyBootstrapService(); - - debugf("ExitCallback: Aborting MIG clients"); - while (DNSServiceDomainEnumerationList) - AbortClient(DNSServiceDomainEnumerationList->ClientMachPort, DNSServiceDomainEnumerationList); - while (DNSServiceBrowserList) - AbortClient(DNSServiceBrowserList ->ClientMachPort, DNSServiceBrowserList); - while (DNSServiceResolverList) - AbortClient(DNSServiceResolverList ->ClientMachPort, DNSServiceResolverList); - while (DNSServiceRegistrationList) - AbortClient(DNSServiceRegistrationList ->ClientMachPort, DNSServiceRegistrationList); - - if (udsserver_exit() < 0) LogMsg("ExitCallback: udsserver_exit failed"); - - debugf("ExitCallback: mDNS_StartExit"); - mDNS_StartExit(&mDNSStorage); - } +{ + (void)sig; // Unused + LogMsg("%s stopping", mDNSResponderVersionString); + + debugf("ExitCallback"); + if (!mDNS_DebugMode && !started_via_launchdaemon) + destroyBootstrapService(); + + debugf("ExitCallback: Aborting MIG clients"); + while (DNSServiceDomainEnumerationList) + AbortClient(DNSServiceDomainEnumerationList->ClientMachPort, DNSServiceDomainEnumerationList); + while (DNSServiceBrowserList) + AbortClient(DNSServiceBrowserList->ClientMachPort, DNSServiceBrowserList); + while (DNSServiceResolverList) + AbortClient(DNSServiceResolverList->ClientMachPort, DNSServiceResolverList); + while (DNSServiceRegistrationList) + AbortClient(DNSServiceRegistrationList->ClientMachPort, DNSServiceRegistrationList); + + if (udsserver_exit() < 0) LogMsg("ExitCallback: udsserver_exit failed"); + + debugf("ExitCallback: mDNS_StartExit"); + mDNS_StartExit(&mDNSStorage); +} #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit mDNSlocal void HandleSIG(int sig) - { - kern_return_t status; - mach_msg_header_t header; - - // WARNING: can't call syslog or fprintf from signal handler - header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); - header.msgh_remote_port = signal_port; - header.msgh_local_port = MACH_PORT_NULL; - header.msgh_size = sizeof(header); - header.msgh_id = sig; - - status = mach_msg(&header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, header.msgh_size, - 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); - - if (status != MACH_MSG_SUCCESS) - { - if (status == MACH_SEND_TIMED_OUT) mach_msg_destroy(&header); - if (sig == SIGTERM || sig == SIGINT) exit(-1); - } - } +{ + kern_return_t status; + mach_msg_header_t header; + + // WARNING: can't call syslog or fprintf from signal handler + header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); + header.msgh_remote_port = signal_port; + header.msgh_local_port = MACH_PORT_NULL; + header.msgh_size = sizeof(header); + header.msgh_id = sig; + + status = mach_msg(&header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, header.msgh_size, + 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); + + if (status != MACH_MSG_SUCCESS) + { + if (status == MACH_SEND_TIMED_OUT) mach_msg_destroy(&header); + if (sig == SIGTERM || sig == SIGINT) exit(-1); + } +} #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM mDNSlocal void INFOCallback(void) - { - mDNSs32 utc = mDNSPlatformUTC(); - DNSServiceDomainEnumeration *e; - DNSServiceBrowser *b; - DNSServiceResolver *l; - DNSServiceRegistration *r; - NetworkInterfaceInfoOSX *i; - DNSServer *s; - McastResolver *mr; - - LogMsg("---- BEGIN STATE LOG ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers); - - udsserver_info(&mDNSStorage); - - LogMsgNoIdent("--------- Mach Clients ---------"); - if (!DNSServiceDomainEnumerationList && !DNSServiceBrowserList && !DNSServiceResolverList && !DNSServiceRegistrationList) - LogMsgNoIdent(""); - else - { - for (e = DNSServiceDomainEnumerationList; e; e=e->next) - 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: Mach ServiceBrowse %##s", b->ClientMachPort, qptr->q.qname.c); - } - - for (l = DNSServiceResolverList; l; l=l->next) - LogMsgNoIdent("%5d: Mach ServiceResolve %##s", l->ClientMachPort, l->i.name.c); - - for (r = DNSServiceRegistrationList; r; r=r->next) - { - 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)); - } - } - - LogMsgNoIdent("----- KQSocketEventSources -----"); - if (!gEventSources) LogMsgNoIdent(""); - else - { - KQSocketEventSource *k; - for (k = gEventSources; k; k=k->next) - { - LogMsgNoIdent("%3d %s", k->fd, k->kqs.KQtask); - usleep((mDNSStorage.KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); - } - } - - LogMsgNoIdent("------ Network Interfaces ------"); - if (!mDNSStorage.p->InterfaceList) LogMsgNoIdent(""); - else - { - for (i = mDNSStorage.p->InterfaceList; i; i = i->next) - { - // Allow six characters for interface name, for names like "vmnet8" - if (!i->Exists) - LogMsgNoIdent("%p %2ld, Registered %p, %s %-6s(%lu) %.6a %.6a %#-14a dormant for %d seconds", - i, i->ifinfo.InterfaceID, i->Registered, - i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifinfo.ifname, i->scope_id, &i->ifinfo.MAC, &i->BSSID, - &i->ifinfo.ip, utc - i->LastSeen); - else - { - const CacheRecord *sps[3]; - FindSPSInCache(&mDNSStorage, &i->ifinfo.NetWakeBrowse, sps); - LogMsgNoIdent("%p %2ld, Registered %p, %s %-6s(%lu) %.6a %.6a %s %s %-15.4a %s %s %s %s %#a", - i, i->ifinfo.InterfaceID, i->Registered, - i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifinfo.ifname, i->scope_id, &i->ifinfo.MAC, &i->BSSID, - i->ifinfo.InterfaceActive ? "Active" : " ", - i->ifinfo.IPv4Available ? "v4" : " ", - i->ifinfo.IPv4Available ? (mDNSv4Addr*)&i->ifa_v4addr : &zerov4Addr, - i->ifinfo.IPv6Available ? "v6" : " ", - i->ifinfo.Advertise ? "⊙" : " ", - i->ifinfo.McastTxRx ? "⇆" : " ", - !(i->ifinfo.InterfaceActive && i->ifinfo.NetWake) ? " " : !sps[0] ? "☼" : "☀", - &i->ifinfo.ip); - - if (sps[0]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps[0]->resrec.rdata->u.name.c), sps[0]->resrec.rdata->u.name.c); - if (sps[1]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps[1]->resrec.rdata->u.name.c), sps[1]->resrec.rdata->u.name.c); - if (sps[2]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps[2]->resrec.rdata->u.name.c), sps[2]->resrec.rdata->u.name.c); - } - } - } - - LogMsgNoIdent("--------- DNS Servers ----------"); - if (!mDNSStorage.DNSServers) LogMsgNoIdent(""); - else - { - for (s = mDNSStorage.DNSServers; s; s = s->next) - { - NetworkInterfaceInfoOSX *ifx = IfindexToInterfaceInfoOSX(&mDNSStorage, s->interface); - LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %d %s %d %s", - s->domain.c, ifx ? ifx->ifinfo.ifname : "", ifx ? " " : "", &s->addr, mDNSVal16(s->port), - s->penaltyTime ? s->penaltyTime - mDNS_TimeNow(&mDNSStorage) : 0, s->scoped ? "Scoped" : "", - s->timeout, - s->teststate == DNSServer_Untested ? "(Untested)" : - s->teststate == DNSServer_Passed ? "" : - s->teststate == DNSServer_Failed ? "(Failed)" : - s->teststate == DNSServer_Disabled ? "(Disabled)" : "(Unknown state)"); - } - } - - LogMsgNoIdent("--------- Mcast Resolvers ----------"); - if (!mDNSStorage.McastResolvers) LogMsgNoIdent(""); - else - { - for (mr = mDNSStorage.McastResolvers; mr; mr = mr->next) - LogMsgNoIdent("Mcast Resolver %##s timeout %u", mr->domain.c, mr->timeout); - } - - mDNSs32 now = mDNS_TimeNow(&mDNSStorage); - LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32)now, now); - - LogMsg("---- END STATE LOG ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers); - } +{ + mDNSs32 utc = mDNSPlatformUTC(); + DNSServiceDomainEnumeration *e; + DNSServiceBrowser *b; + DNSServiceResolver *l; + DNSServiceRegistration *r; + NetworkInterfaceInfoOSX *i; + DNSServer *s; + McastResolver *mr; + + LogMsg("---- BEGIN STATE LOG ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers); + + udsserver_info(&mDNSStorage); + + LogMsgNoIdent("--------- Mach Clients ---------"); + if (!DNSServiceDomainEnumerationList && !DNSServiceBrowserList && !DNSServiceResolverList && !DNSServiceRegistrationList) + LogMsgNoIdent(""); + else + { + for (e = DNSServiceDomainEnumerationList; e; e=e->next) + 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: Mach ServiceBrowse %##s", b->ClientMachPort, qptr->q.qname.c); + } + + for (l = DNSServiceResolverList; l; l=l->next) + LogMsgNoIdent("%5d: Mach ServiceResolve %##s", l->ClientMachPort, l->i.name.c); + + for (r = DNSServiceRegistrationList; r; r=r->next) + { + 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)); + } + } + + LogMsgNoIdent("----- KQSocketEventSources -----"); + if (!gEventSources) LogMsgNoIdent(""); + else + { + KQSocketEventSource *k; + for (k = gEventSources; k; k=k->next) + { + LogMsgNoIdent("%3d %s", k->fd, k->kqs.KQtask); + usleep((mDNSStorage.KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); + } + } + + LogMsgNoIdent("------ Network Interfaces ------"); + if (!mDNSStorage.p->InterfaceList) LogMsgNoIdent(""); + else + { + for (i = mDNSStorage.p->InterfaceList; i; i = i->next) + { + // Allow six characters for interface name, for names like "vmnet8" + if (!i->Exists) + LogMsgNoIdent("%p %2ld, Registered %p, %s %-6s(%lu) %.6a %.6a %#-14a dormant for %d seconds", + i, i->ifinfo.InterfaceID, i->Registered, + i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifinfo.ifname, i->scope_id, &i->ifinfo.MAC, &i->BSSID, + &i->ifinfo.ip, utc - i->LastSeen); + else + { + const CacheRecord *sps[3]; + FindSPSInCache(&mDNSStorage, &i->ifinfo.NetWakeBrowse, sps); + LogMsgNoIdent("%p %2ld, Registered %p, %s %-6s(%lu) %.6a %.6a %s %s %-15.4a %s %s %s %s %#a", + i, i->ifinfo.InterfaceID, i->Registered, + i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifinfo.ifname, i->scope_id, &i->ifinfo.MAC, &i->BSSID, + i->ifinfo.InterfaceActive ? "Active" : " ", + i->ifinfo.IPv4Available ? "v4" : " ", + i->ifinfo.IPv4Available ? (mDNSv4Addr*)&i->ifa_v4addr : &zerov4Addr, + i->ifinfo.IPv6Available ? "v6" : " ", + i->ifinfo.Advertise ? "⊙" : " ", + i->ifinfo.McastTxRx ? "⇆" : " ", + !(i->ifinfo.InterfaceActive && i->ifinfo.NetWake) ? " " : !sps[0] ? "☼" : "☀", + &i->ifinfo.ip); + + if (sps[0]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps[0]->resrec.rdata->u.name.c), sps[0]->resrec.rdata->u.name.c); + if (sps[1]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps[1]->resrec.rdata->u.name.c), sps[1]->resrec.rdata->u.name.c); + if (sps[2]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps[2]->resrec.rdata->u.name.c), sps[2]->resrec.rdata->u.name.c); + } + } + } + + LogMsgNoIdent("--------- DNS Servers ----------"); + if (!mDNSStorage.DNSServers) LogMsgNoIdent(""); + else + { + for (s = mDNSStorage.DNSServers; s; s = s->next) + { + NetworkInterfaceInfoOSX *ifx = IfindexToInterfaceInfoOSX(&mDNSStorage, s->interface); + LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %d %s %d %d %s", + s->domain.c, ifx ? ifx->ifinfo.ifname : "", ifx ? " " : "", &s->addr, mDNSVal16(s->port), + s->penaltyTime ? s->penaltyTime - mDNS_TimeNow(&mDNSStorage) : 0, s->scoped ? "Scoped" : "", + s->timeout, s->resGroupID, + s->teststate == DNSServer_Untested ? "(Untested)" : + s->teststate == DNSServer_Passed ? "" : + s->teststate == DNSServer_Failed ? "(Failed)" : + s->teststate == DNSServer_Disabled ? "(Disabled)" : "(Unknown state)"); + } + } + + LogMsgNoIdent("--------- Mcast Resolvers ----------"); + if (!mDNSStorage.McastResolvers) LogMsgNoIdent(""); + else + { + for (mr = mDNSStorage.McastResolvers; mr; mr = mr->next) + LogMsgNoIdent("Mcast Resolver %##s timeout %u", mr->domain.c, mr->timeout); + } + + mDNSs32 now = mDNS_TimeNow(&mDNSStorage); + LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32)now, now); + + LogMsg("---- END STATE LOG ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers); +} #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) - { - (void)port; // Unused - (void)size; // Unused - (void)info; // Unused - mach_msg_header_t *msg_header = (mach_msg_header_t *)msg; - mDNS *const m = &mDNSStorage; - - // We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding - KQueueLock(m); - switch(msg_header->msgh_id) - { - case SIGHUP: { - mDNSu32 slot; - CacheGroup *cg; - CacheRecord *rr; - LogMsg("SIGHUP: Purge cache"); - mDNS_Lock(m); - FORALL_CACHERECORDS(slot, cg, rr) mDNS_PurgeCacheResourceRecord(m, rr); - // Restart unicast and multicast queries - mDNSCoreRestartQueries(m); - mDNS_Unlock(m); - } break; - case SIGINT: - case SIGTERM: ExitCallback(msg_header->msgh_id); break; - case SIGINFO: INFOCallback(); break; - case SIGUSR1: mDNS_LoggingEnabled = mDNS_LoggingEnabled ? 0 : 1; - LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled ? "Enabled" : "Disabled"); - WatchDogReportingThreshold = mDNS_LoggingEnabled ? 50 : 250; - break; - case SIGUSR2: mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1; - LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled"); - break; - default: LogMsg("SignalCallback: Unknown signal %d", msg_header->msgh_id); break; - } - KQueueUnlock(m, "Unix Signal"); - } +{ + (void)port; // Unused + (void)size; // Unused + (void)info; // Unused + mach_msg_header_t *msg_header = (mach_msg_header_t *)msg; + mDNS *const m = &mDNSStorage; + + // We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding + KQueueLock(m); + switch(msg_header->msgh_id) + { + case SIGHUP: { + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *rr; + LogMsg("SIGHUP: Purge cache"); + mDNS_Lock(m); + FORALL_CACHERECORDS(slot, cg, rr) + { + mDNS_PurgeCacheResourceRecord(m, rr); + } + // Restart unicast and multicast queries + mDNSCoreRestartQueries(m); + mDNS_Unlock(m); + } break; + case SIGINT: + case SIGTERM: ExitCallback(msg_header->msgh_id); break; + case SIGINFO: INFOCallback(); break; + case SIGUSR1: mDNS_LoggingEnabled = mDNS_LoggingEnabled ? 0 : 1; + LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled ? "Enabled" : "Disabled"); + WatchDogReportingThreshold = mDNS_LoggingEnabled ? 50 : 250; + break; + case SIGUSR2: mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1; + LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled"); + break; + case SIGIO: + m->mDNSHandlePeerEvents = m->mDNSHandlePeerEvents ? mDNSfalse : mDNStrue; + LogMsg("SIGIO: Handle AWDL Peer Events %s", m->mDNSHandlePeerEvents ? "Enabled" : "Disabled"); + break; + + default: LogMsg("SignalCallback: Unknown signal %d", msg_header->msgh_id); break; + } + KQueueUnlock(m, "Unix Signal"); +} // On 10.2 the MachServerName is DNSServiceDiscoveryServer // On 10.3 and later, the MachServerName is com.apple.mDNSResponder mDNSlocal kern_return_t mDNSDaemonInitialize(void) - { - mStatus err; - CFMachPortRef s_port; - CFRunLoopSourceRef s_rls; - CFRunLoopSourceRef d_rls; - - // If launchd already created our Mach port for us, then use that, else we create a new one of our own - if (m_port != MACH_PORT_NULL) - s_port = CFMachPortCreateWithPort(NULL, m_port, DNSserverCallback, NULL, NULL); - else - { - s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL); - m_port = CFMachPortGetPort(s_port); - kern_return_t status = bootstrap_register(bootstrap_port, "com.apple.mDNSResponder", m_port); - - if (status) - { - if (status == 1103) - LogMsg("bootstrap_register() failed: A copy of the daemon is apparently already running"); - else - LogMsg("bootstrap_register() failed: %d %X %s", status, status, mach_error_string(status)); - return(status); - } - } - - CFMachPortRef d_port = CFMachPortCreate(NULL, ClientDeathCallback, NULL, NULL); - - err = mDNS_Init(&mDNSStorage, &PlatformStorage, - rrcachestorage, RR_CACHE_SIZE, - advertise, - mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext); - - if (err) { LogMsg("Daemon start: mDNS_Init failed %d", err); return(err); } - - client_death_port = CFMachPortGetPort(d_port); - - s_rls = CFMachPortCreateRunLoopSource(NULL, s_port, 0); - CFRunLoopAddSource(PlatformStorage.CFRunLoop, s_rls, kCFRunLoopDefaultMode); - CFRelease(s_rls); - - d_rls = CFMachPortCreateRunLoopSource(NULL, d_port, 0); - CFRunLoopAddSource(PlatformStorage.CFRunLoop, d_rls, kCFRunLoopDefaultMode); - CFRelease(d_rls); - - CFMachPortRef i_port = CFMachPortCreate(NULL, SignalCallback, NULL, NULL); - CFRunLoopSourceRef i_rls = CFMachPortCreateRunLoopSource(NULL, i_port, 0); - signal_port = CFMachPortGetPort(i_port); - CFRunLoopAddSource(PlatformStorage.CFRunLoop, i_rls, kCFRunLoopDefaultMode); - CFRelease(i_rls); - - if (mDNS_DebugMode) printf("Service registered with Mach Port %d\n", m_port); - return(err); - } +{ + mStatus err; + CFMachPortRef s_port; + CFRunLoopSourceRef s_rls; + CFRunLoopSourceRef d_rls; + + // If launchd already created our Mach port for us, then use that, else we create a new one of our own + if (m_port != MACH_PORT_NULL) + s_port = CFMachPortCreateWithPort(NULL, m_port, DNSserverCallback, NULL, NULL); + else + { + s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL); + m_port = CFMachPortGetPort(s_port); + kern_return_t status = bootstrap_register(bootstrap_port, "com.apple.mDNSResponder", m_port); + + if (status) + { + if (status == 1103) + LogMsg("bootstrap_register() failed: A copy of the daemon is apparently already running"); + else + LogMsg("bootstrap_register() failed: %d %X %s", status, status, mach_error_string(status)); + return(status); + } + } + + CFMachPortRef d_port = CFMachPortCreate(NULL, ClientDeathCallback, NULL, NULL); + + err = mDNS_Init(&mDNSStorage, &PlatformStorage, + rrcachestorage, RR_CACHE_SIZE, + advertise, + mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext); + + if (err) { LogMsg("Daemon start: mDNS_Init failed %d", err); return(err); } + + client_death_port = CFMachPortGetPort(d_port); + + s_rls = CFMachPortCreateRunLoopSource(NULL, s_port, 0); + CFRunLoopAddSource(PlatformStorage.CFRunLoop, s_rls, kCFRunLoopDefaultMode); + CFRelease(s_rls); + + d_rls = CFMachPortCreateRunLoopSource(NULL, d_port, 0); + CFRunLoopAddSource(PlatformStorage.CFRunLoop, d_rls, kCFRunLoopDefaultMode); + CFRelease(d_rls); + + CFMachPortRef i_port = CFMachPortCreate(NULL, SignalCallback, NULL, NULL); + CFRunLoopSourceRef i_rls = CFMachPortCreateRunLoopSource(NULL, i_port, 0); + signal_port = CFMachPortGetPort(i_port); + CFRunLoopAddSource(PlatformStorage.CFRunLoop, i_rls, kCFRunLoopDefaultMode); + CFRelease(i_rls); + + if (mDNS_DebugMode) printf("Service registered with Mach Port %d\n", m_port); + return(err); +} #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM @@ -2004,257 +1982,260 @@ mDNSlocal kern_return_t mDNSDaemonInitialize(void) // single routines, with the few differing parts bracketed with "#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM" mDNSlocal void SignalDispatch(dispatch_source_t source) - { - int sig = (int)dispatch_source_get_handle(source); - mDNS *const m = &mDNSStorage; - KQueueLock(m); - switch(sig) - { - case SIGHUP: { - mDNSu32 slot; - CacheGroup *cg; - CacheRecord *rr; - LogMsg("SIGHUP: Purge cache"); - mDNS_Lock(m); - FORALL_CACHERECORDS(slot, cg, rr) mDNS_PurgeCacheResourceRecord(m, rr); - // Restart unicast and multicast queries - mDNSCoreRestartQueries(m); - mDNS_Unlock(m); - } break; - case SIGINT: - case SIGTERM: ExitCallback(sig); break; - case SIGINFO: INFOCallback(); break; - case SIGUSR1: mDNS_LoggingEnabled = mDNS_LoggingEnabled ? 0 : 1; - LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled ? "Enabled" : "Disabled"); - WatchDogReportingThreshold = mDNS_LoggingEnabled ? 50 : 250; - break; - case SIGUSR2: mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1; - LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled"); - break; - default: LogMsg("SignalCallback: Unknown signal %d", sig); break; - } - KQueueUnlock(m, "Unix Signal"); - } +{ + int sig = (int)dispatch_source_get_handle(source); + mDNS *const m = &mDNSStorage; + KQueueLock(m); + switch(sig) + { + case SIGHUP: { + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *rr; + LogMsg("SIGHUP: Purge cache"); + mDNS_Lock(m); + FORALL_CACHERECORDS(slot, cg, rr) + { + mDNS_PurgeCacheResourceRecord(m, rr); + } + // Restart unicast and multicast queries + mDNSCoreRestartQueries(m); + mDNS_Unlock(m); + } break; + case SIGINT: + case SIGTERM: ExitCallback(sig); break; + case SIGINFO: INFOCallback(); break; + case SIGUSR1: mDNS_LoggingEnabled = mDNS_LoggingEnabled ? 0 : 1; + LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled ? "Enabled" : "Disabled"); + WatchDogReportingThreshold = mDNS_LoggingEnabled ? 50 : 250; + break; + case SIGUSR2: mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1; + LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled"); + break; + default: LogMsg("SignalCallback: Unknown signal %d", sig); break; + } + KQueueUnlock(m, "Unix Signal"); +} mDNSlocal void mDNSSetupSignal(dispatch_queue_t queue, int sig) - { - signal(sig, SIG_IGN); - dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, sig, 0, queue); - - if (source) - { - dispatch_source_set_event_handler(source, ^{SignalDispatch(source);}); - // Start processing signals - dispatch_resume(source); - } - else - { - LogMsg("mDNSSetupSignal: Cannot setup signal %d", sig); - } - } +{ + signal(sig, SIG_IGN); + dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, sig, 0, queue); + + if (source) + { + dispatch_source_set_event_handler(source, ^{SignalDispatch(source);}); + // Start processing signals + dispatch_resume(source); + } + else + { + LogMsg("mDNSSetupSignal: Cannot setup signal %d", sig); + } +} // On 10.2 the MachServerName is DNSServiceDiscoveryServer // On 10.3 and later, the MachServerName is com.apple.mDNSResponder mDNSlocal kern_return_t mDNSDaemonInitialize(void) - { - mStatus err; - CFMachPortRef s_port; - dispatch_source_t mach_source; - dispatch_queue_t queue = dispatch_get_main_queue(); - - // If launchd already created our Mach port for us, then use that, else we create a new one of our own - if (m_port != MACH_PORT_NULL) - s_port = CFMachPortCreateWithPort(NULL, m_port, DNSserverCallback, NULL, NULL); - else - { - s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL); - m_port = CFMachPortGetPort(s_port); - kern_return_t status = bootstrap_register(bootstrap_port, "com.apple.mDNSResponder", m_port); - - if (status) - { - if (status == 1103) - LogMsg("bootstrap_register() failed: A copy of the daemon is apparently already running"); - else - LogMsg("bootstrap_register() failed: %d %X %s", status, status, mach_error_string(status)); - return(status); - } - } - - err = mDNS_Init(&mDNSStorage, &PlatformStorage, - rrcachestorage, RR_CACHE_SIZE, - advertise, - mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext); - - if (err) { LogMsg("Daemon start: mDNS_Init failed %d", err); return(err); } - - mach_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, m_port, 0, queue); - if (mach_source == mDNSNULL){LogMsg("mDNSDaemonInitialize: Error creating source for m_port"); return -1;} - dispatch_source_set_event_handler(mach_source, ^{ - dispatch_mig_server(mach_source, sizeof(union __RequestUnion__DNSServiceDiscoveryReply_subsystem), - DNSServiceDiscoveryRequest_server); - }); - dispatch_resume(mach_source); - - mDNSSetupSignal(queue, SIGHUP); - mDNSSetupSignal(queue, SIGINT); - mDNSSetupSignal(queue, SIGTERM); - mDNSSetupSignal(queue, SIGINFO); - mDNSSetupSignal(queue, SIGUSR1); - mDNSSetupSignal(queue, SIGUSR2); - mDNSSetupSignal(queue, SIGHUP); - - // Create a custom handler for doing the housekeeping work. This is either triggered - // by the timer or an event source - PlatformStorage.custom = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, queue); - if (PlatformStorage.custom == mDNSNULL){LogMsg("mDNSDaemonInitialize: Error creating custom source"); return -1;} - dispatch_source_set_event_handler(PlatformStorage.custom, ^{PrepareForIdle(&mDNSStorage);}); - dispatch_resume(PlatformStorage.custom); - - // Create a timer source to trigger housekeeping work. The houskeeping work itself - // is done in the custom handler that we set below. - - PlatformStorage.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); - if (PlatformStorage.timer == mDNSNULL){LogMsg("mDNSDaemonInitialize: Error creating timer source"); return -1;} - - // As the API does not support one shot timers, we pass zero for the interval. In the custom handler, we - // always reset the time to the new time computed. In effect, we ignore the interval - dispatch_source_set_timer(PlatformStorage.timer, DISPATCH_TIME_NOW, 1000ull * 1000000000, 0); - dispatch_source_set_event_handler(PlatformStorage.timer, ^{ - dispatch_source_merge_data(PlatformStorage.custom, 1); - }); - dispatch_resume(PlatformStorage.timer); - - LogMsg("DaemonIntialize done successfully"); - - if (mDNS_DebugMode) printf("Service registered with Mach Port %d\n", m_port); - return(err); - } +{ + mStatus err; + CFMachPortRef s_port; + dispatch_source_t mach_source; + dispatch_queue_t queue = dispatch_get_main_queue(); + + // If launchd already created our Mach port for us, then use that, else we create a new one of our own + if (m_port != MACH_PORT_NULL) + s_port = CFMachPortCreateWithPort(NULL, m_port, DNSserverCallback, NULL, NULL); + else + { + s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL); + m_port = CFMachPortGetPort(s_port); + kern_return_t status = bootstrap_register(bootstrap_port, "com.apple.mDNSResponder", m_port); + + if (status) + { + if (status == 1103) + LogMsg("bootstrap_register() failed: A copy of the daemon is apparently already running"); + else + LogMsg("bootstrap_register() failed: %d %X %s", status, status, mach_error_string(status)); + return(status); + } + } + + err = mDNS_Init(&mDNSStorage, &PlatformStorage, + rrcachestorage, RR_CACHE_SIZE, + advertise, + mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext); + + if (err) { LogMsg("Daemon start: mDNS_Init failed %d", err); return(err); } + + mach_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, m_port, 0, queue); + if (mach_source == mDNSNULL) {LogMsg("mDNSDaemonInitialize: Error creating source for m_port"); return -1;} + dispatch_source_set_event_handler(mach_source, ^{ + dispatch_mig_server(mach_source, sizeof(union __RequestUnion__DNSServiceDiscoveryReply_subsystem), + DNSServiceDiscoveryRequest_server); + }); + dispatch_resume(mach_source); + + mDNSSetupSignal(queue, SIGHUP); + mDNSSetupSignal(queue, SIGINT); + mDNSSetupSignal(queue, SIGTERM); + mDNSSetupSignal(queue, SIGINFO); + mDNSSetupSignal(queue, SIGUSR1); + mDNSSetupSignal(queue, SIGUSR2); + mDNSSetupSignal(queue, SIGIO); + + // Create a custom handler for doing the housekeeping work. This is either triggered + // by the timer or an event source + PlatformStorage.custom = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, queue); + if (PlatformStorage.custom == mDNSNULL) {LogMsg("mDNSDaemonInitialize: Error creating custom source"); return -1;} + dispatch_source_set_event_handler(PlatformStorage.custom, ^{PrepareForIdle(&mDNSStorage);}); + dispatch_resume(PlatformStorage.custom); + + // Create a timer source to trigger housekeeping work. The houskeeping work itself + // is done in the custom handler that we set below. + + PlatformStorage.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); + if (PlatformStorage.timer == mDNSNULL) {LogMsg("mDNSDaemonInitialize: Error creating timer source"); return -1;} + + // As the API does not support one shot timers, we pass zero for the interval. In the custom handler, we + // always reset the time to the new time computed. In effect, we ignore the interval + dispatch_source_set_timer(PlatformStorage.timer, DISPATCH_TIME_NOW, 1000ull * 1000000000, 0); + dispatch_source_set_event_handler(PlatformStorage.timer, ^{ + dispatch_source_merge_data(PlatformStorage.custom, 1); + }); + dispatch_resume(PlatformStorage.timer); + + LogMsg("DaemonIntialize done successfully"); + + if (mDNS_DebugMode) printf("Service registered with Mach Port %d\n", m_port); + return(err); +} #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM mDNSlocal mDNSs32 mDNSDaemonIdle(mDNS *const m) - { - mDNSs32 now = mDNS_TimeNow(m); - - // 1. If we need to set domain secrets, do so before handling the network change - // Detailed reason: - // BTMM domains listed in DynStore Setup:/Network/BackToMyMac are added to the registration domains list, - // and we need to setup the associated AutoTunnel DomainAuthInfo entries before that happens. - if (m->p->KeyChainTimer && now - m->p->KeyChainTimer >= 0) - { - m->p->KeyChainTimer = 0; - mDNS_Lock(m); - SetDomainSecrets(m); - mDNS_Unlock(m); - } - - // 2. If we have network change events to handle, do them before calling mDNS_Execute() - // Detailed reason: - // mDNSMacOSXNetworkChanged() currently closes and re-opens its sockets. If there are received packets waiting, they are lost. - // mDNS_Execute() generates packets, including multicasts that are looped back to ourself. - // If we call mDNS_Execute() first, and generate packets, and then call mDNSMacOSXNetworkChanged() immediately afterwards - // we then systematically lose our own looped-back packets. - if (m->p->NetworkChanged && now - m->p->NetworkChanged >= 0) mDNSMacOSXNetworkChanged(m); - - if (m->p->RequestReSleep && now - m->p->RequestReSleep >= 0) { m->p->RequestReSleep = 0; mDNSPowerRequest(0, 0); } - - // 3. Call mDNS_Execute() to let mDNSCore do what it needs to do - mDNSs32 nextevent = mDNS_Execute(m); - - if (m->p->NetworkChanged) - if (nextevent - m->p->NetworkChanged > 0) - nextevent = m->p->NetworkChanged; - - if (m->p->KeyChainTimer) - if (nextevent - m->p->KeyChainTimer > 0) - nextevent = m->p->KeyChainTimer; - - if (m->p->RequestReSleep) - if (nextevent - m->p->RequestReSleep > 0) - nextevent = m->p->RequestReSleep; - - // 4. Deliver any waiting browse messages to clients - DNSServiceBrowser *b = DNSServiceBrowserList; - - while (b) - { - // Note: Need to advance b to the next element BEFORE we call DeliverInstance(), because in the - // event that the client Mach queue overflows, DeliverInstance() will call AbortBlockedClient() - // and that will cause the DNSServiceBrowser object's memory to be freed before it returns - DNSServiceBrowser *x = b; - b = b->next; - if (x->results) // Try to deliver the list of results - { - while (x->results) - { - DNSServiceBrowserResult *const r = x->results; - domainlabel name; - domainname type, domain; - DeconstructServiceName(&r->result, &name, &type, &domain); // Don't need to check result; already validated in FoundInstance() - char cname[MAX_DOMAIN_LABEL+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL. - char ctype[MAX_ESCAPED_DOMAIN_NAME]; - char cdom [MAX_ESCAPED_DOMAIN_NAME]; - ConvertDomainLabelToCString_unescaped(&name, cname); - ConvertDomainNameToCString(&type, ctype); - ConvertDomainNameToCString(&domain, cdom); - DNSServiceDiscoveryReplyFlags flags = (r->next) ? DNSServiceDiscoverReplyFlagsMoreComing : 0; - kern_return_t status = DNSServiceBrowserReply_rpc(x->ClientMachPort, r->resultType, cname, ctype, cdom, flags, 1); - // If we failed to send the mach message, try again in one second - if (status == MACH_SEND_TIMED_OUT) - { - if (nextevent - now > mDNSPlatformOneSecond) - nextevent = now + mDNSPlatformOneSecond; - break; - } - else - { - x->lastsuccess = now; - x->results = x->results->next; - freeL("DNSServiceBrowserResult", r); - } - } - // If this client hasn't read a single message in the last 60 seconds, abort it - if (now - x->lastsuccess >= 60 * mDNSPlatformOneSecond) - AbortBlockedClient(x->ClientMachPort, "browse", x); - } - } - - DNSServiceResolver *l; - for (l = DNSServiceResolverList; l; l=l->next) - if (l->ReportTime && now - l->ReportTime >= 0) - { - l->ReportTime = 0; - 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 (!SameDomainLabelCS(m->p->usernicelabel.c, m->nicelabel.c)) - { - LogMsg("Name Conflict: Updated Computer Name from \"%#s\" to \"%#s\"", m->p->usernicelabel.c, m->nicelabel.c); - mDNSPreferencesSetNames(m, kmDNSComputerName, &m->p->usernicelabel, &m->nicelabel); - m->p->usernicelabel = m->nicelabel; - } - if (!SameDomainLabelCS(m->p->userhostlabel.c, m->hostlabel.c)) - { - LogMsg("Name Conflict: Updated Local Hostname from \"%#s.local\" to \"%#s.local\"", m->p->userhostlabel.c, m->hostlabel.c); - mDNSPreferencesSetNames(m, kmDNSLocalHostName, &m->p->userhostlabel, &m->hostlabel); - m->p->HostNameConflict = 0; // Clear our indicator, now name change has been successful - m->p->userhostlabel = m->hostlabel; - } - m->p->NotifyUser = 0; - } - else - if (nextevent - m->p->NotifyUser > 0) - nextevent = m->p->NotifyUser; - } - - return(nextevent); - } +{ + mDNSs32 now = mDNS_TimeNow(m); + + // 1. If we need to set domain secrets, do so before handling the network change + // Detailed reason: + // BTMM domains listed in DynStore Setup:/Network/BackToMyMac are added to the registration domains list, + // and we need to setup the associated AutoTunnel DomainAuthInfo entries before that happens. + if (m->p->KeyChainTimer && now - m->p->KeyChainTimer >= 0) + { + m->p->KeyChainTimer = 0; + mDNS_Lock(m); + SetDomainSecrets(m); + mDNS_Unlock(m); + } + + // 2. If we have network change events to handle, do them before calling mDNS_Execute() + // Detailed reason: + // mDNSMacOSXNetworkChanged() currently closes and re-opens its sockets. If there are received packets waiting, they are lost. + // mDNS_Execute() generates packets, including multicasts that are looped back to ourself. + // If we call mDNS_Execute() first, and generate packets, and then call mDNSMacOSXNetworkChanged() immediately afterwards + // we then systematically lose our own looped-back packets. + if (m->p->NetworkChanged && now - m->p->NetworkChanged >= 0) mDNSMacOSXNetworkChanged(m); + + if (m->p->RequestReSleep && now - m->p->RequestReSleep >= 0) { m->p->RequestReSleep = 0; mDNSPowerRequest(0, 0); } + + // 3. Call mDNS_Execute() to let mDNSCore do what it needs to do + mDNSs32 nextevent = mDNS_Execute(m); + + if (m->p->NetworkChanged) + if (nextevent - m->p->NetworkChanged > 0) + nextevent = m->p->NetworkChanged; + + if (m->p->KeyChainTimer) + if (nextevent - m->p->KeyChainTimer > 0) + nextevent = m->p->KeyChainTimer; + + if (m->p->RequestReSleep) + if (nextevent - m->p->RequestReSleep > 0) + nextevent = m->p->RequestReSleep; + + // 4. Deliver any waiting browse messages to clients + DNSServiceBrowser *b = DNSServiceBrowserList; + + while (b) + { + // Note: Need to advance b to the next element BEFORE we call DeliverInstance(), because in the + // event that the client Mach queue overflows, DeliverInstance() will call AbortBlockedClient() + // and that will cause the DNSServiceBrowser object's memory to be freed before it returns + DNSServiceBrowser *x = b; + b = b->next; + if (x->results) // Try to deliver the list of results + { + while (x->results) + { + DNSServiceBrowserResult *const r = x->results; + domainlabel name; + domainname type, domain; + DeconstructServiceName(&r->result, &name, &type, &domain); // Don't need to check result; already validated in FoundInstance() + char cname[MAX_DOMAIN_LABEL+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL. + char ctype[MAX_ESCAPED_DOMAIN_NAME]; + char cdom [MAX_ESCAPED_DOMAIN_NAME]; + ConvertDomainLabelToCString_unescaped(&name, cname); + ConvertDomainNameToCString(&type, ctype); + ConvertDomainNameToCString(&domain, cdom); + DNSServiceDiscoveryReplyFlags flags = (r->next) ? DNSServiceDiscoverReplyFlagsMoreComing : 0; + kern_return_t status = DNSServiceBrowserReply_rpc(x->ClientMachPort, r->resultType, cname, ctype, cdom, flags, 1); + // If we failed to send the mach message, try again in one second + if (status == MACH_SEND_TIMED_OUT) + { + if (nextevent - now > mDNSPlatformOneSecond) + nextevent = now + mDNSPlatformOneSecond; + break; + } + else + { + x->lastsuccess = now; + x->results = x->results->next; + freeL("DNSServiceBrowserResult", r); + } + } + // If this client hasn't read a single message in the last 60 seconds, abort it + if (now - x->lastsuccess >= 60 * mDNSPlatformOneSecond) + AbortBlockedClient(x->ClientMachPort, "browse", x); + } + } + + DNSServiceResolver *l; + for (l = DNSServiceResolverList; l; l=l->next) + if (l->ReportTime && now - l->ReportTime >= 0) + { + l->ReportTime = 0; + 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 (!SameDomainLabelCS(m->p->usernicelabel.c, m->nicelabel.c)) + { + LogMsg("Name Conflict: Updated Computer Name from \"%#s\" to \"%#s\"", m->p->usernicelabel.c, m->nicelabel.c); + mDNSPreferencesSetNames(m, kmDNSComputerName, &m->p->usernicelabel, &m->nicelabel); + m->p->usernicelabel = m->nicelabel; + } + if (!SameDomainLabelCS(m->p->userhostlabel.c, m->hostlabel.c)) + { + LogMsg("Name Conflict: Updated Local Hostname from \"%#s.local\" to \"%#s.local\"", m->p->userhostlabel.c, m->hostlabel.c); + mDNSPreferencesSetNames(m, kmDNSLocalHostName, &m->p->userhostlabel, &m->hostlabel); + m->p->HostNameConflict = 0; // Clear our indicator, now name change has been successful + m->p->userhostlabel = m->hostlabel; + } + m->p->NotifyUser = 0; + } + else + if (nextevent - m->p->NotifyUser > 0) + nextevent = m->p->NotifyUser; + } + + return(nextevent); +} // Right now we consider *ALL* of our DHCP leases // It might make sense to be a bit more selective and only consider the leases on interfaces @@ -2262,73 +2243,70 @@ mDNSlocal mDNSs32 mDNSDaemonIdle(mDNS *const m) // (b) where we have found (and successfully registered with) a Sleep Proxy // If we can't be woken for traffic on a given interface, then why keep waking to renew its lease? mDNSlocal mDNSu32 DHCPWakeTime(void) - { - mDNSu32 e = 24 * 3600; // Maximum maintenance wake interval is 24 hours - const CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); - if (!now) LogMsg("DHCPWakeTime: CFAbsoluteTimeGetCurrent failed"); - else - { - const SCPreferencesRef prefs = SCPreferencesCreate(NULL, CFSTR("mDNSResponder:DHCPWakeTime"), NULL); - if (!prefs) LogMsg("DHCPWakeTime: SCPreferencesCreate failed"); - else - { - const SCNetworkSetRef currentset = SCNetworkSetCopyCurrent(prefs); - if (!currentset) LogMsg("DHCPWakeTime: SCNetworkSetCopyCurrent failed"); - else - { - const CFArrayRef services = SCNetworkSetCopyServices(currentset); - if (!services) LogMsg("DHCPWakeTime: SCNetworkSetCopyServices failed"); - else - { - int i; - for (i = 0; i < CFArrayGetCount(services); i++) - { - const SCNetworkServiceRef service = CFArrayGetValueAtIndex(services, i); - if (!service) LogMsg("DHCPWakeTime: CFArrayGetValueAtIndex %d failed", i); - else - { - const CFStringRef serviceid = SCNetworkServiceGetServiceID(service); - if (!serviceid) LogMsg("DHCPWakeTime: SCNetworkServiceGetServiceID %d failed", i); - else - { - // Note: It's normal for this call to return NULL, for interfaces not using DHCP - const CFDictionaryRef dhcp = SCDynamicStoreCopyDHCPInfo(NULL, serviceid); - if (dhcp) - { - const CFDateRef start = DHCPInfoGetLeaseStartTime(dhcp); - const CFDataRef lease = DHCPInfoGetOptionData(dhcp, 51); // Option 51 = IP Address Lease Time - if (!start || !lease || CFDataGetLength(lease) < 4) - LogMsg("DHCPWakeTime: SCDynamicStoreCopyDHCPInfo index %d failed " - "CFDateRef start %p CFDataRef lease %p CFDataGetLength(lease) %d", - i, start, lease, lease ? CFDataGetLength(lease) : 0); - else - { - const UInt8 *d = CFDataGetBytePtr(lease); - if (!d) LogMsg("DHCPWakeTime: CFDataGetBytePtr %d failed", i); - else - { - const mDNSu32 elapsed = now - CFDateGetAbsoluteTime(start); - const mDNSu32 lifetime = (mDNSs32) ((mDNSs32)d[0] << 24 | (mDNSs32)d[1] << 16 | (mDNSs32)d[2] << 8 | d[3]); - const mDNSu32 remaining = lifetime - elapsed; - const mDNSu32 wake = remaining > 60 ? remaining - remaining/10 : 54; // Wake at 90% of the lease time - LogSPS("DHCP Address Lease Elapsed %6u Lifetime %6u Remaining %6u Wake %6u", elapsed, lifetime, remaining, wake); - if (e > wake) e = wake; - } - } - CFRelease(dhcp); - } - } - } - } - CFRelease(services); - } - CFRelease(currentset); - } - CFRelease(prefs); - } - } - return(e); - } +{ + mDNSu32 e = 24 * 3600; // Maximum maintenance wake interval is 24 hours + const CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); + if (!now) LogMsg("DHCPWakeTime: CFAbsoluteTimeGetCurrent failed"); + else + { + int ic, j; + + const void *pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetDHCP); + if (!pattern) + { + LogMsg("DHCPWakeTime: SCDynamicStoreKeyCreateNetworkServiceEntity failed\n"); + return e; + } + CFArrayRef dhcpinfo = CFArrayCreate(NULL, (const void **)&pattern, 1, &kCFTypeArrayCallBacks); + CFRelease(pattern); + if (dhcpinfo) + { + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("DHCP-LEASES"), NULL, NULL); + if (store) + { + CFDictionaryRef dict = SCDynamicStoreCopyMultiple(store, NULL, dhcpinfo); + if (dict) + { + ic = CFDictionaryGetCount(dict); + const void *vals[ic]; + CFDictionaryGetKeysAndValues(dict, NULL, vals); + + for (j = 0; j < ic; j++) + { + const CFDictionaryRef dhcp = (CFDictionaryRef)vals[j]; + if (dhcp) + { + const CFDateRef start = DHCPInfoGetLeaseStartTime(dhcp); + const CFDataRef lease = DHCPInfoGetOptionData(dhcp, 51); // Option 51 = IP Address Lease Time + if (!start || !lease || CFDataGetLength(lease) < 4) + LogMsg("DHCPWakeTime: SCDynamicStoreCopyDHCPInfo index %d failed " + "CFDateRef start %p CFDataRef lease %p CFDataGetLength(lease) %d", + j, start, lease, lease ? CFDataGetLength(lease) : 0); + else + { + const UInt8 *d = CFDataGetBytePtr(lease); + if (!d) LogMsg("DHCPWakeTime: CFDataGetBytePtr %d failed", j); + else + { + const mDNSu32 elapsed = now - CFDateGetAbsoluteTime(start); + const mDNSu32 lifetime = (mDNSs32) ((mDNSs32)d[0] << 24 | (mDNSs32)d[1] << 16 | (mDNSs32)d[2] << 8 | d[3]); + const mDNSu32 remaining = lifetime - elapsed; + const mDNSu32 wake = remaining > 60 ? remaining - remaining/10 : 54; // Wake at 90% of the lease time + LogSPS("DHCP Address Lease Elapsed %6u Lifetime %6u Remaining %6u Wake %6u", elapsed, lifetime, remaining, wake); + if (e > wake) e = wake; + } + } + } + } + CFRelease(dict); + } + CFRelease(store); + } + CFRelease(dhcpinfo); + } + } + return(e); +} // We deliberately schedule our wakeup for halfway between when we'd *like* it and when we *need* it. // For example, if our DHCP lease expires in two hours, we'll typically renew it at the halfway point, after one hour. @@ -2339,693 +2317,682 @@ mDNSlocal mDNSu32 DHCPWakeTime(void) // allowing us an adequate safety margin to renew our lease before we lose it. mDNSlocal mDNSBool AllowSleepNow(mDNS *const m, mDNSs32 now) - { - mDNSBool ready = mDNSCoreReadyForSleep(m, now); - if (m->SleepState && !ready && now - m->SleepLimit < 0) return(mDNSfalse); - - m->p->WakeAtUTC = 0; - int result = kIOReturnSuccess; - CFDictionaryRef opts = NULL; - - // If the sleep request was cancelled, and we're no longer planning to sleep, don't need to - // do the stuff below, but we *DO* still need to acknowledge the sleep message we received. - if (!m->SleepState) - LogMsg("AllowSleepNow: Sleep request was canceled with %d ticks remaining", m->SleepLimit - now); - else - { - if (!m->SystemWakeOnLANEnabled || !mDNSCoreHaveAdvertisedMulticastServices(m)) - LogSPS("AllowSleepNow: Not scheduling wakeup: SystemWakeOnLAN %s enabled; %s advertised services", - m->SystemWakeOnLANEnabled ? "is" : "not", - mDNSCoreHaveAdvertisedMulticastServices(m) ? "have" : "no"); - else - { - mDNSs32 dhcp = DHCPWakeTime(); - LogSPS("ComputeWakeTime: DHCP Wake %d", dhcp); - mDNSs32 interval = mDNSCoreIntervalToNextWake(m, now) / mDNSPlatformOneSecond; - if (interval > dhcp) interval = dhcp; - - // If we're not ready to sleep (failed to register with Sleep Proxy, maybe because of - // transient network problem) then schedule a wakeup in one hour to try again. Otherwise, - // a single SPS failure could result in a remote machine falling permanently asleep, requiring - // someone to go to the machine in person to wake it up again, which would be unacceptable. - if (!ready && interval > 3600) interval = 3600; - - //interval = 48; // For testing - +{ + mDNSBool ready = mDNSCoreReadyForSleep(m, now); + if (m->SleepState && !ready && now - m->SleepLimit < 0) return(mDNSfalse); + + m->p->WakeAtUTC = 0; + int result = kIOReturnSuccess; + CFDictionaryRef opts = NULL; + + // If the sleep request was cancelled, and we're no longer planning to sleep, don't need to + // do the stuff below, but we *DO* still need to acknowledge the sleep message we received. + if (!m->SleepState) + LogMsg("AllowSleepNow: Sleep request was canceled with %d ticks remaining", m->SleepLimit - now); + else + { + if (!m->SystemWakeOnLANEnabled || !mDNSCoreHaveAdvertisedMulticastServices(m)) + LogSPS("AllowSleepNow: Not scheduling wakeup: SystemWakeOnLAN %s enabled; %s advertised services", + m->SystemWakeOnLANEnabled ? "is" : "not", + mDNSCoreHaveAdvertisedMulticastServices(m) ? "have" : "no"); + else + { + mDNSs32 dhcp = DHCPWakeTime(); + LogSPS("ComputeWakeTime: DHCP Wake %d", dhcp); + mDNSs32 interval = mDNSCoreIntervalToNextWake(m, now) / mDNSPlatformOneSecond; + if (interval > dhcp) interval = dhcp; + + // If we're not ready to sleep (failed to register with Sleep Proxy, maybe because of + // transient network problem) then schedule a wakeup in one hour to try again. Otherwise, + // a single SPS failure could result in a remote machine falling permanently asleep, requiring + // someone to go to the machine in person to wake it up again, which would be unacceptable. + if (!ready && interval > 3600) interval = 3600; + + //interval = 48; // For testing + #ifdef kIOPMAcknowledgmentOptionSystemCapabilityRequirements - if (m->p->IOPMConnection) // If lightweight-wake capability is available, use that - { - const CFDateRef WakeDate = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent() + interval); - if (!WakeDate) LogMsg("ScheduleNextWake: CFDateCreate failed"); - else - { - const mDNSs32 reqs = kIOPMSystemPowerStateCapabilityNetwork; - const CFNumberRef Requirements = CFNumberCreate(NULL, kCFNumberSInt32Type, &reqs); - if (!Requirements) LogMsg("ScheduleNextWake: CFNumberCreate failed"); - else - { - const void *OptionKeys[2] = { CFSTR("WakeDate"), CFSTR("Requirements") }; - const void *OptionVals[2] = { WakeDate, Requirements }; - opts = CFDictionaryCreate(NULL, (void*)OptionKeys, (void*)OptionVals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - if (!opts) LogMsg("ScheduleNextWake: CFDictionaryCreate failed"); - CFRelease(Requirements); - } - CFRelease(WakeDate); - } - LogSPS("AllowSleepNow: Will request lightweight wakeup in %d seconds", interval); - } - else // else schedule the wakeup using the old API instead to + if (m->p->IOPMConnection) // If lightweight-wake capability is available, use that + { + const CFDateRef WakeDate = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent() + interval); + if (!WakeDate) LogMsg("ScheduleNextWake: CFDateCreate failed"); + else + { + const mDNSs32 reqs = kIOPMSystemPowerStateCapabilityNetwork; + const CFNumberRef Requirements = CFNumberCreate(NULL, kCFNumberSInt32Type, &reqs); + if (!Requirements) LogMsg("ScheduleNextWake: CFNumberCreate failed"); + else + { + const void *OptionKeys[2] = { CFSTR("WakeDate"), CFSTR("Requirements") }; + const void *OptionVals[2] = { WakeDate, Requirements }; + opts = CFDictionaryCreate(NULL, (void*)OptionKeys, (void*)OptionVals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!opts) LogMsg("ScheduleNextWake: CFDictionaryCreate failed"); + CFRelease(Requirements); + } + CFRelease(WakeDate); + } + LogSPS("AllowSleepNow: Will request lightweight wakeup in %d seconds", interval); + } + else // else schedule the wakeup using the old API instead to #endif - { - // If we wake within +/- 30 seconds of our requested time we'll assume the system woke for us, - // so we should put it back to sleep. To avoid frustrating the user, we always request at least - // 60 seconds sleep, so if they immediately re-wake the system within seconds of it going to sleep, - // we then shouldn't hit our 30-second window, and we won't attempt to re-sleep the machine. - if (interval < 60) interval = 60; - - result = mDNSPowerRequest(1, interval); - - if (result == kIOReturnNotReady) - { - int r; - LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful; retrying with longer intervals", interval); - // IOPMSchedulePowerEvent fails with kIOReturnNotReady (-536870184/0xe00002d8) if the - // requested wake time is "too soon", but there's no API to find out what constitutes - // "too soon" on any given OS/hardware combination, so if we get kIOReturnNotReady - // we just have to iterate with successively longer intervals until it doesn't fail. - // We preserve the value of "result" because if our original power request was deemed "too soon" - // for the machine to get to sleep and wake back up again, we attempt to cancel the sleep request, - // since the implication is that the system won't manage to be awake again at the time we need it. - do - { - interval += (interval < 20) ? 1 : ((interval+3) / 4); - r = mDNSPowerRequest(1, interval); - } - while (r == kIOReturnNotReady); - if (r) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval, r, r); - else LogSPS("AllowSleepNow: Requested later wakeup in %d seconds; will also attempt IOCancelPowerChange", interval); - } - else - { - if (result) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval, result, result); - else LogSPS("AllowSleepNow: Requested wakeup in %d seconds", interval); - } - m->p->WakeAtUTC = mDNSPlatformUTC() + interval; - } - } - - m->SleepState = SleepState_Sleeping; - // We used to clear our interface list to empty state here before going to sleep. - // The applications that try to connect to an external server during maintenance wakes, saw - // DNS resolution errors as we don't have any interfaces (most queries use SuppressUnusable - // flag). Thus, we don't remove our interfaces anymore on sleep. - } - - LogSPS("AllowSleepNow: %s(%lX) %s at %ld (%d ticks remaining)", + { + // If we wake within +/- 30 seconds of our requested time we'll assume the system woke for us, + // so we should put it back to sleep. To avoid frustrating the user, we always request at least + // 60 seconds sleep, so if they immediately re-wake the system within seconds of it going to sleep, + // we then shouldn't hit our 30-second window, and we won't attempt to re-sleep the machine. + if (interval < 60) interval = 60; + + result = mDNSPowerRequest(1, interval); + + if (result == kIOReturnNotReady) + { + int r; + LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful; retrying with longer intervals", interval); + // IOPMSchedulePowerEvent fails with kIOReturnNotReady (-536870184/0xe00002d8) if the + // requested wake time is "too soon", but there's no API to find out what constitutes + // "too soon" on any given OS/hardware combination, so if we get kIOReturnNotReady + // we just have to iterate with successively longer intervals until it doesn't fail. + // We preserve the value of "result" because if our original power request was deemed "too soon" + // for the machine to get to sleep and wake back up again, we attempt to cancel the sleep request, + // since the implication is that the system won't manage to be awake again at the time we need it. + do + { + interval += (interval < 20) ? 1 : ((interval+3) / 4); + r = mDNSPowerRequest(1, interval); + } + while (r == kIOReturnNotReady); + if (r) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval, r, r); + else LogSPS("AllowSleepNow: Requested later wakeup in %d seconds; will also attempt IOCancelPowerChange", interval); + } + else + { + if (result) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval, result, result); + else LogSPS("AllowSleepNow: Requested wakeup in %d seconds", interval); + } + m->p->WakeAtUTC = mDNSPlatformUTC() + interval; + } + } + + m->SleepState = SleepState_Sleeping; + // We used to clear our interface list to empty state here before going to sleep. + // The applications that try to connect to an external server during maintenance wakes, saw + // DNS resolution errors as we don't have any interfaces (most queries use SuppressUnusable + // flag). Thus, we don't remove our interfaces anymore on sleep. + } + + LogSPS("AllowSleepNow: %s(%lX) %s at %ld (%d ticks remaining)", #if !TARGET_OS_EMBEDDED && defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements) - (m->p->IOPMConnection) ? "IOPMConnectionAcknowledgeEventWithOptions" : + (m->p->IOPMConnection) ? "IOPMConnectionAcknowledgeEventWithOptions" : #endif - (result == kIOReturnSuccess) ? "IOAllowPowerChange" : "IOCancelPowerChange", - m->p->SleepCookie, ready ? "ready for sleep" : "giving up", now, m->SleepLimit - now); + (result == kIOReturnSuccess) ? "IOAllowPowerChange" : "IOCancelPowerChange", + m->p->SleepCookie, ready ? "ready for sleep" : "giving up", now, m->SleepLimit - now); - m->SleepLimit = 0; // Don't clear m->SleepLimit until after we've logged it above + m->SleepLimit = 0; // Don't clear m->SleepLimit until after we've logged it above #if !TARGET_OS_EMBEDDED && defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements) - if (m->p->IOPMConnection) IOPMConnectionAcknowledgeEventWithOptions(m->p->IOPMConnection, m->p->SleepCookie, opts); - else + if (m->p->IOPMConnection) IOPMConnectionAcknowledgeEventWithOptions(m->p->IOPMConnection, m->p->SleepCookie, opts); + else #endif - if (result == kIOReturnSuccess) IOAllowPowerChange (m->p->PowerConnection, m->p->SleepCookie); - else IOCancelPowerChange(m->p->PowerConnection, m->p->SleepCookie); + if (result == kIOReturnSuccess) IOAllowPowerChange (m->p->PowerConnection, m->p->SleepCookie); + else IOCancelPowerChange(m->p->PowerConnection, m->p->SleepCookie); - if (opts) CFRelease(opts); - return(mDNStrue); - } + if (opts) CFRelease(opts); + return(mDNStrue); +} #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM mDNSexport void TriggerEventCompletion() - { - debugf("TriggerEventCompletion: Merge data"); - dispatch_source_merge_data(PlatformStorage.custom, 1); - } +{ + debugf("TriggerEventCompletion: Merge data"); + dispatch_source_merge_data(PlatformStorage.custom, 1); +} mDNSlocal void PrepareForIdle(void *m_param) - { - mDNS *m = m_param; - int64_t time_offset; - dispatch_time_t dtime; - - const int multiplier = 1000000000 / mDNSPlatformOneSecond; - - // This is the main work loop: - // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time - // (2) Then we make sure we've delivered all waiting browse messages to our clients - // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner - - debugf("PrepareForIdle: called"); - // Run mDNS_Execute to find out the time we next need to wake up - mDNSs32 start = mDNSPlatformRawTime(); - mDNSs32 nextTimerEvent = udsserver_idle(mDNSDaemonIdle(m)); - mDNSs32 end = mDNSPlatformRawTime(); - if (end - start >= WatchDogReportingThreshold) - LogInfo("CustomSourceHandler:WARNING: Idle task took %dms to complete", end - start); - - mDNSs32 now = mDNS_TimeNow(m); - - if (m->ShutdownTime) - { - if (mDNSStorage.ResourceRecords) - { - LogInfo("Cannot exit yet; Resource Record still exists: %s", ARDisplayString(m, mDNSStorage.ResourceRecords)); - if (mDNS_LoggingEnabled) usleep(10000); // Sleep 10ms so that we don't flood syslog with too many messages - } - if (mDNS_ExitNow(m, now)) - { - if (!mDNSStorage.ResourceRecords) - safe_vproc_transaction_end(); - LogInfo("IdleLoop: mDNS_FinalExit"); - mDNS_FinalExit(&mDNSStorage); - usleep(1000); // Little 1ms pause before exiting, so we don't lose our final syslog messages - exit(0); - } - if (nextTimerEvent - m->ShutdownTime >= 0) - nextTimerEvent = m->ShutdownTime; - } - - if (m->SleepLimit) - if (!AllowSleepNow(m, now)) - if (nextTimerEvent - m->SleepLimit >= 0) - nextTimerEvent = m->SleepLimit; - - // Convert absolute wakeup time to a relative time from now - mDNSs32 ticks = nextTimerEvent - now; - if (ticks < 1) ticks = 1; - - static mDNSs32 RepeatedBusy = 0; // Debugging sanity check, to guard against CPU spins - if (ticks > 1) - RepeatedBusy = 0; - else - { - ticks = 1; - if (++RepeatedBusy >= mDNSPlatformOneSecond) { ShowTaskSchedulingError(&mDNSStorage); RepeatedBusy = 0; } - } - - time_offset = ((mDNSu32)ticks / mDNSPlatformOneSecond) * 1000000000 + (ticks % mDNSPlatformOneSecond) * multiplier; - dtime = dispatch_time(DISPATCH_TIME_NOW, time_offset); - dispatch_source_set_timer(PlatformStorage.timer, dtime, 1000ull*1000000000, 0); - debugf("PrepareForIdle: scheduling timer with ticks %d", ticks); - return; - } +{ + mDNS *m = m_param; + int64_t time_offset; + dispatch_time_t dtime; + + const int multiplier = 1000000000 / mDNSPlatformOneSecond; + + // This is the main work loop: + // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time + // (2) Then we make sure we've delivered all waiting browse messages to our clients + // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner + + debugf("PrepareForIdle: called"); + // Run mDNS_Execute to find out the time we next need to wake up + mDNSs32 start = mDNSPlatformRawTime(); + mDNSs32 nextTimerEvent = udsserver_idle(mDNSDaemonIdle(m)); + mDNSs32 end = mDNSPlatformRawTime(); + if (end - start >= WatchDogReportingThreshold) + LogInfo("CustomSourceHandler:WARNING: Idle task took %dms to complete", end - start); + + mDNSs32 now = mDNS_TimeNow(m); + + if (m->ShutdownTime) + { + if (mDNSStorage.ResourceRecords) + { + LogInfo("Cannot exit yet; Resource Record still exists: %s", ARDisplayString(m, mDNSStorage.ResourceRecords)); + if (mDNS_LoggingEnabled) usleep(10000); // Sleep 10ms so that we don't flood syslog with too many messages + } + if (mDNS_ExitNow(m, now)) + { + LogInfo("IdleLoop: mDNS_FinalExit"); + mDNS_FinalExit(&mDNSStorage); + usleep(1000); // Little 1ms pause before exiting, so we don't lose our final syslog messages + exit(0); + } + if (nextTimerEvent - m->ShutdownTime >= 0) + nextTimerEvent = m->ShutdownTime; + } + + if (m->SleepLimit) + if (!AllowSleepNow(m, now)) + if (nextTimerEvent - m->SleepLimit >= 0) + nextTimerEvent = m->SleepLimit; + + // Convert absolute wakeup time to a relative time from now + mDNSs32 ticks = nextTimerEvent - now; + if (ticks < 1) ticks = 1; + + static mDNSs32 RepeatedBusy = 0; // Debugging sanity check, to guard against CPU spins + if (ticks > 1) + RepeatedBusy = 0; + else + { + ticks = 1; + if (++RepeatedBusy >= mDNSPlatformOneSecond) { ShowTaskSchedulingError(&mDNSStorage); RepeatedBusy = 0; } + } + + time_offset = ((mDNSu32)ticks / mDNSPlatformOneSecond) * 1000000000 + (ticks % mDNSPlatformOneSecond) * multiplier; + dtime = dispatch_time(DISPATCH_TIME_NOW, time_offset); + dispatch_source_set_timer(PlatformStorage.timer, dtime, 1000ull*1000000000, 0); + debugf("PrepareForIdle: scheduling timer with ticks %d", ticks); + return; +} #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM mDNSlocal void KQWokenFlushBytes(int fd, __unused short filter, __unused void *context) - { - // Read all of the bytes so we won't wake again. - char buffer[100]; - while (recv(fd, buffer, sizeof(buffer), MSG_DONTWAIT) > 0) continue; - } +{ + // Read all of the bytes so we won't wake again. + char buffer[100]; + while (recv(fd, buffer, sizeof(buffer), MSG_DONTWAIT) > 0) continue; +} mDNSlocal void * KQueueLoop(void *m_param) - { - mDNS *m = m_param; - int numevents = 0; +{ + mDNS *m = m_param; + int numevents = 0; #if USE_SELECT_WITH_KQUEUEFD - fd_set readfds; - FD_ZERO(&readfds); - const int multiplier = 1000000 / mDNSPlatformOneSecond; + fd_set readfds; + FD_ZERO(&readfds); + const int multiplier = 1000000 / mDNSPlatformOneSecond; #else - const int multiplier = 1000000000 / mDNSPlatformOneSecond; + const int multiplier = 1000000000 / mDNSPlatformOneSecond; #endif - - pthread_mutex_lock(&PlatformStorage.BigMutex); - LogInfo("Starting time value 0x%08lX (%ld)", (mDNSu32)mDNSStorage.timenow_last, mDNSStorage.timenow_last); - - // This is the main work loop: - // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time - // (2) Then we make sure we've delivered all waiting browse messages to our clients - // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner - // (4) On wakeup we first process *all* events - // (5) then when no more events remain, we go back to (1) to finish off any deferred work and do it all again - for ( ; ; ) - { - #define kEventsToReadAtOnce 1 - struct kevent new_events[kEventsToReadAtOnce]; - - // Run mDNS_Execute to find out the time we next need to wake up - mDNSs32 start = mDNSPlatformRawTime(); - mDNSs32 nextTimerEvent = udsserver_idle(mDNSDaemonIdle(m)); - mDNSs32 end = mDNSPlatformRawTime(); - if (end - start >= WatchDogReportingThreshold) - LogInfo("WARNING: Idle task took %dms to complete", end - start); - - mDNSs32 now = mDNS_TimeNow(m); - - if (m->ShutdownTime) - { - if (mDNSStorage.ResourceRecords) - { - AuthRecord *rr; - for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next) - { - LogInfo("Cannot exit yet; Resource Record still exists: %s", ARDisplayString(m, rr)); - if (mDNS_LoggingEnabled) usleep(10000); // Sleep 10ms so that we don't flood syslog with too many messages - } - } - if (mDNS_ExitNow(m, now)) - { - if (!mDNSStorage.ResourceRecords) - safe_vproc_transaction_end(); - LogInfo("mDNS_FinalExit"); - mDNS_FinalExit(&mDNSStorage); - usleep(1000); // Little 1ms pause before exiting, so we don't lose our final syslog messages - exit(0); - } - if (nextTimerEvent - m->ShutdownTime >= 0) - nextTimerEvent = m->ShutdownTime; - } - - if (m->SleepLimit) - if (!AllowSleepNow(m, now)) - if (nextTimerEvent - m->SleepLimit >= 0) - nextTimerEvent = m->SleepLimit; - - // Convert absolute wakeup time to a relative time from now - mDNSs32 ticks = nextTimerEvent - now; - if (ticks < 1) ticks = 1; - - static mDNSs32 RepeatedBusy = 0; // Debugging sanity check, to guard against CPU spins - if (ticks > 1) - RepeatedBusy = 0; - else - { - ticks = 1; - if (++RepeatedBusy >= mDNSPlatformOneSecond) { ShowTaskSchedulingError(&mDNSStorage); RepeatedBusy = 0; } - } - - verbosedebugf("KQueueLoop: Handled %d events; now sleeping for %d ticks", numevents, ticks); - numevents = 0; - - // Release the lock, and sleep until: - // 1. Something interesting happens like a packet arriving, or - // 2. The other thread writes a byte to WakeKQueueLoopFD to poke us and make us wake up, or - // 3. The timeout expires - pthread_mutex_unlock(&PlatformStorage.BigMutex); + + pthread_mutex_lock(&PlatformStorage.BigMutex); + LogInfo("Starting time value 0x%08lX (%ld)", (mDNSu32)mDNSStorage.timenow_last, mDNSStorage.timenow_last); + + // This is the main work loop: + // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time + // (2) Then we make sure we've delivered all waiting browse messages to our clients + // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner + // (4) On wakeup we first process *all* events + // (5) then when no more events remain, we go back to (1) to finish off any deferred work and do it all again + for ( ; ; ) + { + #define kEventsToReadAtOnce 1 + struct kevent new_events[kEventsToReadAtOnce]; + + // Run mDNS_Execute to find out the time we next need to wake up + mDNSs32 start = mDNSPlatformRawTime(); + mDNSs32 nextTimerEvent = udsserver_idle(mDNSDaemonIdle(m)); + mDNSs32 end = mDNSPlatformRawTime(); + if (end - start >= WatchDogReportingThreshold) + LogInfo("WARNING: Idle task took %dms to complete", end - start); + + mDNSs32 now = mDNS_TimeNow(m); + + if (m->ShutdownTime) + { + if (mDNSStorage.ResourceRecords) + { + AuthRecord *rr; + for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next) + { + LogInfo("Cannot exit yet; Resource Record still exists: %s", ARDisplayString(m, rr)); + if (mDNS_LoggingEnabled) usleep(10000); // Sleep 10ms so that we don't flood syslog with too many messages + } + } + if (mDNS_ExitNow(m, now)) + { + LogInfo("mDNS_FinalExit"); + mDNS_FinalExit(&mDNSStorage); + usleep(1000); // Little 1ms pause before exiting, so we don't lose our final syslog messages + exit(0); + } + if (nextTimerEvent - m->ShutdownTime >= 0) + nextTimerEvent = m->ShutdownTime; + } + + if (m->SleepLimit) + if (!AllowSleepNow(m, now)) + if (nextTimerEvent - m->SleepLimit >= 0) + nextTimerEvent = m->SleepLimit; + + // Convert absolute wakeup time to a relative time from now + mDNSs32 ticks = nextTimerEvent - now; + if (ticks < 1) ticks = 1; + + static mDNSs32 RepeatedBusy = 0; // Debugging sanity check, to guard against CPU spins + if (ticks > 1) + RepeatedBusy = 0; + else + { + ticks = 1; + if (++RepeatedBusy >= mDNSPlatformOneSecond) { ShowTaskSchedulingError(&mDNSStorage); RepeatedBusy = 0; } + } + + verbosedebugf("KQueueLoop: Handled %d events; now sleeping for %d ticks", numevents, ticks); + numevents = 0; + + // Release the lock, and sleep until: + // 1. Something interesting happens like a packet arriving, or + // 2. The other thread writes a byte to WakeKQueueLoopFD to poke us and make us wake up, or + // 3. The timeout expires + pthread_mutex_unlock(&PlatformStorage.BigMutex); #if USE_SELECT_WITH_KQUEUEFD - struct timeval timeout; - timeout.tv_sec = ticks / mDNSPlatformOneSecond; - timeout.tv_usec = (ticks % mDNSPlatformOneSecond) * multiplier; - FD_SET(KQueueFD, &readfds); - if (select(KQueueFD+1, &readfds, NULL, NULL, &timeout) < 0) - { LogMsg("select(%d) failed errno %d (%s)", KQueueFD, errno, strerror(errno)); sleep(1); } + struct timeval timeout; + timeout.tv_sec = ticks / mDNSPlatformOneSecond; + timeout.tv_usec = (ticks % mDNSPlatformOneSecond) * multiplier; + FD_SET(KQueueFD, &readfds); + if (select(KQueueFD+1, &readfds, NULL, NULL, &timeout) < 0) + { LogMsg("select(%d) failed errno %d (%s)", KQueueFD, errno, strerror(errno)); sleep(1); } #else - struct timespec timeout; - timeout.tv_sec = ticks / mDNSPlatformOneSecond; - timeout.tv_nsec = (ticks % mDNSPlatformOneSecond) * multiplier; - // In my opinion, you ought to be able to call kevent() with nevents set to zero, - // and have it work similarly to the way it does with nevents non-zero -- - // i.e. it waits until either an event happens or the timeout expires, and then wakes up. - // In fact, what happens if you do this is that it just returns immediately. So, we have - // to pass nevents set to one, and then we just ignore the event it gives back to us. -- SC - if (kevent(KQueueFD, NULL, 0, new_events, 1, &timeout) < 0) - { LogMsg("kevent(%d) failed errno %d (%s)", KQueueFD, errno, strerror(errno)); sleep(1); } + struct timespec timeout; + timeout.tv_sec = ticks / mDNSPlatformOneSecond; + timeout.tv_nsec = (ticks % mDNSPlatformOneSecond) * multiplier; + // In my opinion, you ought to be able to call kevent() with nevents set to zero, + // and have it work similarly to the way it does with nevents non-zero -- + // i.e. it waits until either an event happens or the timeout expires, and then wakes up. + // In fact, what happens if you do this is that it just returns immediately. So, we have + // to pass nevents set to one, and then we just ignore the event it gives back to us. -- SC + if (kevent(KQueueFD, NULL, 0, new_events, 1, &timeout) < 0) + { LogMsg("kevent(%d) failed errno %d (%s)", KQueueFD, errno, strerror(errno)); sleep(1); } #endif - pthread_mutex_lock(&PlatformStorage.BigMutex); - // We have to ignore the event we may have been told about above, because that - // was done without holding the lock, and between the time we woke up and the - // time we reclaimed the lock the other thread could have done something that - // makes the event no longer valid. Now we have the lock, we call kevent again - // and this time we can safely process the events it tells us about. - - static const struct timespec zero_timeout = { 0, 0 }; - int events_found; - while ((events_found = kevent(KQueueFD, NULL, 0, new_events, kEventsToReadAtOnce, &zero_timeout)) != 0) - { - if (events_found > kEventsToReadAtOnce || (events_found < 0 && errno != EINTR)) - { - // Not sure what to do here, our kqueue has failed us - this isn't ideal - LogMsg("ERROR: KQueueLoop - kevent failed errno %d (%s)", errno, strerror(errno)); - exit(errno); - } - - numevents += events_found; - - int i; - for (i = 0; i < events_found; i++) - { - const KQueueEntry *const kqentry = new_events[i].udata; - mDNSs32 stime = mDNSPlatformRawTime(); - const char *const KQtask = kqentry->KQtask; // Grab a copy in case KQcallback deletes the task - kqentry->KQcallback(new_events[i].ident, new_events[i].filter, kqentry->KQcontext); - mDNSs32 etime = mDNSPlatformRawTime(); - if (etime - stime >= WatchDogReportingThreshold) - LogInfo("WARNING: %s took %dms to complete", KQtask, etime - stime); - } - } - } - - return NULL; - } + pthread_mutex_lock(&PlatformStorage.BigMutex); + // We have to ignore the event we may have been told about above, because that + // was done without holding the lock, and between the time we woke up and the + // time we reclaimed the lock the other thread could have done something that + // makes the event no longer valid. Now we have the lock, we call kevent again + // and this time we can safely process the events it tells us about. + + static const struct timespec zero_timeout = { 0, 0 }; + int events_found; + while ((events_found = kevent(KQueueFD, NULL, 0, new_events, kEventsToReadAtOnce, &zero_timeout)) != 0) + { + if (events_found > kEventsToReadAtOnce || (events_found < 0 && errno != EINTR)) + { + // Not sure what to do here, our kqueue has failed us - this isn't ideal + LogMsg("ERROR: KQueueLoop - kevent failed errno %d (%s)", errno, strerror(errno)); + exit(errno); + } + + numevents += events_found; + + int i; + for (i = 0; i < events_found; i++) + { + const KQueueEntry *const kqentry = new_events[i].udata; + mDNSs32 stime = mDNSPlatformRawTime(); + const char *const KQtask = kqentry->KQtask; // Grab a copy in case KQcallback deletes the task + kqentry->KQcallback(new_events[i].ident, new_events[i].filter, kqentry->KQcontext); + mDNSs32 etime = mDNSPlatformRawTime(); + if (etime - stime >= WatchDogReportingThreshold) + LogInfo("WARNING: %s took %dms to complete", KQtask, etime - stime); + } + } + } + + return NULL; +} #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM mDNSlocal void LaunchdCheckin(void) - { - launch_data_t msg = launch_data_new_string(LAUNCH_KEY_CHECKIN); - launch_data_t resp = launch_msg(msg); - launch_data_free(msg); - if (!resp) { LogMsg("launch_msg returned NULL"); return; } - - if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) - { - int err = launch_data_get_errno(resp); - // When running on Tiger with "ServiceIPC = false", we get "err == EACCES" to tell us there's no launchdata to fetch - if (err != EACCES) LogMsg("launch_msg returned %d", err); - else LogInfo("Launchd provided no launchdata; will open Mach port and Unix Domain Socket explicitly...", err); - } - else - { - launch_data_t skts = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_SOCKETS); - if (!skts) LogMsg("launch_data_dict_lookup LAUNCH_JOBKEY_SOCKETS returned NULL"); - else - { - launch_data_t skt = launch_data_dict_lookup(skts, "Listeners"); - if (!skt) LogMsg("launch_data_dict_lookup Listeners returned NULL"); - else - { - launchd_fds_count = launch_data_array_get_count(skt); - if (launchd_fds_count == 0) LogMsg("launch_data_array_get_count(skt) returned 0"); - else - { - launchd_fds = mallocL("LaunchdCheckin", sizeof(dnssd_sock_t) * launchd_fds_count); - if (!launchd_fds) LogMsg("LaunchdCheckin: malloc failed"); - else - { - size_t i; - for(i = 0; i < launchd_fds_count; i++) - { - launch_data_t s = launch_data_array_get_index(skt, i); - if (!s) - { - launchd_fds[i] = dnssd_InvalidSocket; - LogMsg("launch_data_array_get_index(skt, %d) returned NULL", i); - } - else - { - launchd_fds[i] = launch_data_get_fd(s); - LogInfo("Launchd Unix Domain Socket [%d]: %d", i, launchd_fds[i]); - } - } - } - // In some early versions of 10.4.x, the permissions on the UDS were not set correctly, so we fix them here - chmod(MDNS_UDS_SERVERPATH, S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | S_IROTH|S_IWOTH); - } - } - } - - launch_data_t ports = launch_data_dict_lookup(resp, "MachServices"); - if (!ports) LogMsg("launch_data_dict_lookup MachServices returned NULL"); - else - { - launch_data_t p = launch_data_dict_lookup(ports, "com.apple.mDNSResponder"); - if (!p) LogInfo("launch_data_dict_lookup(ports, \"com.apple.mDNSResponder\") returned NULL"); - else - { - m_port = launch_data_get_fd(p); - LogInfo("Launchd Mach Port: %d", m_port); - if (m_port == ~0U) m_port = MACH_PORT_NULL; - } - } - } - launch_data_free(resp); - } +{ + launch_data_t msg = launch_data_new_string(LAUNCH_KEY_CHECKIN); + launch_data_t resp = launch_msg(msg); + launch_data_free(msg); + if (!resp) { LogMsg("launch_msg returned NULL"); return; } + + if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) + { + int err = launch_data_get_errno(resp); + // When running on Tiger with "ServiceIPC = false", we get "err == EACCES" to tell us there's no launchdata to fetch + if (err != EACCES) LogMsg("launch_msg returned %d", err); + else LogInfo("Launchd provided no launchdata; will open Mach port and Unix Domain Socket explicitly...", err); + } + else + { + launch_data_t skts = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_SOCKETS); + if (!skts) LogMsg("launch_data_dict_lookup LAUNCH_JOBKEY_SOCKETS returned NULL"); + else + { + launch_data_t skt = launch_data_dict_lookup(skts, "Listeners"); + if (!skt) LogMsg("launch_data_dict_lookup Listeners returned NULL"); + else + { + launchd_fds_count = launch_data_array_get_count(skt); + if (launchd_fds_count == 0) LogMsg("launch_data_array_get_count(skt) returned 0"); + else + { + launchd_fds = mallocL("LaunchdCheckin", sizeof(dnssd_sock_t) * launchd_fds_count); + if (!launchd_fds) LogMsg("LaunchdCheckin: malloc failed"); + else + { + size_t i; + for(i = 0; i < launchd_fds_count; i++) + { + launch_data_t s = launch_data_array_get_index(skt, i); + if (!s) + { + launchd_fds[i] = dnssd_InvalidSocket; + LogMsg("launch_data_array_get_index(skt, %d) returned NULL", i); + } + else + { + launchd_fds[i] = launch_data_get_fd(s); + LogInfo("Launchd Unix Domain Socket [%d]: %d", i, launchd_fds[i]); + } + } + } + // In some early versions of 10.4.x, the permissions on the UDS were not set correctly, so we fix them here + chmod(MDNS_UDS_SERVERPATH, S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | S_IROTH|S_IWOTH); + } + } + } + + launch_data_t ports = launch_data_dict_lookup(resp, "MachServices"); + if (!ports) LogMsg("launch_data_dict_lookup MachServices returned NULL"); + else + { + launch_data_t p = launch_data_dict_lookup(ports, "com.apple.mDNSResponder"); + if (!p) LogInfo("launch_data_dict_lookup(ports, \"com.apple.mDNSResponder\") returned NULL"); + else + { + m_port = launch_data_get_fd(p); + LogInfo("Launchd Mach Port: %d", m_port); + if (m_port == ~0U) m_port = MACH_PORT_NULL; + } + } + } + launch_data_free(resp); +} mDNSlocal void DropPrivileges(void) - { - static const char login[] = "_mdnsresponder"; - struct passwd *pwd = getpwnam(login); - if (NULL == pwd) - LogMsg("Could not find account name \"%s\". Running as root.", login); - else - { - uid_t uid = pwd->pw_uid; - gid_t gid = pwd->pw_gid; - - LogMsg("Started as root. Switching to userid \"%s\".", login); - - if (unlink(MDNS_UDS_SERVERPATH) < 0 && errno != ENOENT) LogMsg("DropPrivileges: Could not unlink \"%s\": (%d) %s", MDNS_UDS_SERVERPATH, errno, strerror(errno)); - else - { - static char path[] = "/var/run/mdns/mDNSResponder"; - char *p = strrchr(path, '/'); - *p = '\0'; - if (mkdir(path, 0755) < 0 && errno != EEXIST) LogMsg("DropPrivileges: Could not create directory \"%s\": (%d) %s", path, errno, strerror(errno)); - else if (chown(path, uid, gid) < 0) LogMsg("DropPrivileges: Could not chown directory \"%s\": (%d) %s", path, errno, strerror(errno)); - else - { - *p = '/'; - if (unlink(path) < 0 && errno != ENOENT) LogMsg("DropPrivileges: Could not unlink \"%s\": (%d) %s", path, errno, strerror(errno)); - else if (symlink(path, MDNS_UDS_SERVERPATH) < 0) LogMsg("DropPrivileges: Could not symlink \"%s\" -> \"%s\": (%d) %s", MDNS_UDS_SERVERPATH, path, errno, strerror(errno)); - else LogInfo("DropPrivileges: Created subdirectory and symlink"); - } - } - - if (0 != initgroups(login, gid)) LogMsg("initgroups(\"%s\", %lu) failed. Continuing.", login, (unsigned long)gid); - if (0 != setgid(gid)) LogMsg("setgid(%lu) failed. Continuing with group %lu privileges.", (unsigned long)getegid()); - if (0 != setuid(uid)) LogMsg("setuid(%lu) failed. Continuing as root after all.", (unsigned long)uid); - } - } +{ + static const char login[] = "_mdnsresponder"; + struct passwd *pwd = getpwnam(login); + if (NULL == pwd) + LogMsg("Could not find account name \"%s\". Running as root.", login); + else + { + uid_t uid = pwd->pw_uid; + gid_t gid = pwd->pw_gid; + + LogMsg("Started as root. Switching to userid \"%s\".", login); + + if (unlink(MDNS_UDS_SERVERPATH) < 0 && errno != ENOENT) LogMsg("DropPrivileges: Could not unlink \"%s\": (%d) %s", MDNS_UDS_SERVERPATH, errno, strerror(errno)); + else + { + static char path[] = "/var/run/mdns/mDNSResponder"; + char *p = strrchr(path, '/'); + *p = '\0'; + if (mkdir(path, 0755) < 0 && errno != EEXIST) LogMsg("DropPrivileges: Could not create directory \"%s\": (%d) %s", path, errno, strerror(errno)); + else if (chown(path, uid, gid) < 0) LogMsg("DropPrivileges: Could not chown directory \"%s\": (%d) %s", path, errno, strerror(errno)); + else + { + *p = '/'; + if (unlink(path) < 0 && errno != ENOENT) LogMsg("DropPrivileges: Could not unlink \"%s\": (%d) %s", path, errno, strerror(errno)); + else if (symlink(path, MDNS_UDS_SERVERPATH) < 0) LogMsg("DropPrivileges: Could not symlink \"%s\" -> \"%s\": (%d) %s", MDNS_UDS_SERVERPATH, path, errno, strerror(errno)); + else LogInfo("DropPrivileges: Created subdirectory and symlink"); + } + } + + if (0 != initgroups(login, gid)) LogMsg("initgroups(\"%s\", %lu) failed. Continuing.", login, (unsigned long)gid); + if (0 != setgid(gid)) LogMsg("setgid(%lu) failed. Continuing with group %lu privileges.", (unsigned long)getegid()); + if (0 != setuid(uid)) LogMsg("setuid(%lu) failed. Continuing as root after all.", (unsigned long)uid); + } +} extern int sandbox_init(const char *profile, uint64_t flags, char **errorbuf) __attribute__((weak_import)); mDNSexport int main(int argc, char **argv) - { - int i; - kern_return_t status; +{ + int i; + kern_return_t status; + + mDNSMacOSXSystemBuildNumber(NULL); + LogMsg("%s starting %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers); - mDNSMacOSXSystemBuildNumber(NULL); - LogMsg("%s starting %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers); - #if 0 - LogMsg("CacheRecord %d", sizeof(CacheRecord)); - LogMsg("CacheGroup %d", sizeof(CacheGroup)); - LogMsg("ResourceRecord %d", sizeof(ResourceRecord)); - LogMsg("RData_small %d", sizeof(RData_small)); - - LogMsg("sizeof(CacheEntity) %d", sizeof(CacheEntity)); - LogMsg("RR_CACHE_SIZE %d", RR_CACHE_SIZE); - LogMsg("block usage %d", sizeof(CacheEntity) * RR_CACHE_SIZE); - LogMsg("block wastage %d", 16*1024 - sizeof(CacheEntity) * RR_CACHE_SIZE); + LogMsg("CacheRecord %d", sizeof(CacheRecord)); + LogMsg("CacheGroup %d", sizeof(CacheGroup)); + LogMsg("ResourceRecord %d", sizeof(ResourceRecord)); + LogMsg("RData_small %d", sizeof(RData_small)); + + LogMsg("sizeof(CacheEntity) %d", sizeof(CacheEntity)); + LogMsg("RR_CACHE_SIZE %d", RR_CACHE_SIZE); + LogMsg("block usage %d", sizeof(CacheEntity) * RR_CACHE_SIZE); + LogMsg("block wastage %d", 16*1024 - sizeof(CacheEntity) * RR_CACHE_SIZE); #endif - safe_vproc_transaction_begin(); - - if (0 == geteuid()) DropPrivileges(); - - for (i=1; ifd != fd) p = &(*p)->next; - if (*p) { LogMsg("udsSupportAddFDToEventLoop: ERROR fd %d already has EventLoop source entry", fd); return mStatus_AlreadyRegistered; } - - KQSocketEventSource *newSource = (KQSocketEventSource*) mallocL("KQSocketEventSource", sizeof *newSource); - if (!newSource) return mStatus_NoMemoryErr; - - newSource->next = mDNSNULL; - newSource->fd = fd; - newSource->kqs.KQcallback = callback; - newSource->kqs.KQcontext = context; - newSource->kqs.KQtask = "UDS client"; +{ + KQSocketEventSource **p = &gEventSources; + (void) platform_data; + while (*p && (*p)->fd != fd) p = &(*p)->next; + if (*p) { LogMsg("udsSupportAddFDToEventLoop: ERROR fd %d already has EventLoop source entry", fd); return mStatus_AlreadyRegistered; } + + KQSocketEventSource *newSource = (KQSocketEventSource*) mallocL("KQSocketEventSource", sizeof *newSource); + if (!newSource) return mStatus_NoMemoryErr; + + newSource->next = mDNSNULL; + newSource->fd = fd; + newSource->kqs.KQcallback = callback; + newSource->kqs.KQcontext = context; + newSource->kqs.KQtask = "UDS client"; #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM - newSource->kqs.readSource = mDNSNULL; - newSource->kqs.writeSource = mDNSNULL; - newSource->kqs.fdClosed = mDNSfalse; + newSource->kqs.readSource = mDNSNULL; + newSource->kqs.writeSource = mDNSNULL; + newSource->kqs.fdClosed = mDNSfalse; #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM - if (KQueueSet(fd, EV_ADD, EVFILT_READ, &newSource->kqs) == 0) - { - *p = newSource; - return mStatus_NoError; - } + if (KQueueSet(fd, EV_ADD, EVFILT_READ, &newSource->kqs) == 0) + { + *p = newSource; + return mStatus_NoError; + } - LogMsg("KQueueSet failed for fd %d errno %d (%s)", fd, errno, strerror(errno)); - freeL("KQSocketEventSource", newSource); - return mStatus_BadParamErr; - } + LogMsg("KQueueSet failed for fd %d errno %d (%s)", fd, errno, strerror(errno)); + freeL("KQSocketEventSource", newSource); + return mStatus_BadParamErr; +} int udsSupportReadFD(dnssd_sock_t fd, char *buf, int len, int flags, void *platform_data) - { - (void) platform_data; - return recv(fd, buf, len, flags); - } - -mDNSexport mStatus udsSupportRemoveFDFromEventLoop(int fd, void *platform_data) // Note: This also CLOSES the file descriptor - { - KQSocketEventSource **p = &gEventSources; - (void) platform_data; - while (*p && (*p)->fd != fd) p = &(*p)->next; - if (*p) - { - KQSocketEventSource *s = *p; - *p = (*p)->next; - // We don't have to explicitly do a kqueue EV_DELETE here because closing the fd - // causes the kernel to automatically remove any associated kevents - mDNSPlatformCloseFD(&s->kqs, s->fd); - freeL("KQSocketEventSource", s); - return mStatus_NoError; - } - LogMsg("udsSupportRemoveFDFromEventLoop: ERROR fd %d not found in EventLoop source list", fd); - return mStatus_NoSuchNameErr; - } +{ + (void) platform_data; + return recv(fd, buf, len, flags); +} + +mDNSexport mStatus udsSupportRemoveFDFromEventLoop(int fd, void *platform_data) // Note: This also CLOSES the file descriptor +{ + KQSocketEventSource **p = &gEventSources; + (void) platform_data; + while (*p && (*p)->fd != fd) p = &(*p)->next; + if (*p) + { + KQSocketEventSource *s = *p; + *p = (*p)->next; + // We don't have to explicitly do a kqueue EV_DELETE here because closing the fd + // causes the kernel to automatically remove any associated kevents + mDNSPlatformCloseFD(&s->kqs, s->fd); + freeL("KQSocketEventSource", s); + return mStatus_NoError; + } + LogMsg("udsSupportRemoveFDFromEventLoop: ERROR fd %d not found in EventLoop source list", fd); + return mStatus_NoSuchNameErr; +} #if _BUILDING_XCODE_PROJECT_ // 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"); +asm (".desc ___crashreporter_info__, 0x10"); #endif // For convenience when using the "strings" command, this is the last thing in the file diff --git a/mDNSMacOSX/helper-error.h b/mDNSMacOSX/helper-error.h index 93307c1..f90d5ce 100644 --- a/mDNSMacOSX/helper-error.h +++ b/mDNSMacOSX/helper-error.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/mDNSMacOSX/helper-main.c b/mDNSMacOSX/helper-main.c index 72e9fbc..d3610a4 100644 --- a/mDNSMacOSX/helper-main.c +++ b/mDNSMacOSX/helper-main.c @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -42,7 +42,7 @@ #include "helper-server.h" #include "helpermsg.h" #include "helpermsgServer.h" -#include "safe_vproc.h" +#include #if TARGET_OS_EMBEDDED #include @@ -58,20 +58,10 @@ #endif union max_msg_size - { - union __RequestUnion__proxy_helper_subsystem req; - union __ReplyUnion__proxy_helper_subsystem rep; - }; - -#ifdef VPROC_HAS_TRANSACTIONS -typedef struct __transaction_s - { - struct __transaction_s* next; - vproc_transaction_t vt; - } transaction_t; - -static transaction_t* transactions = NULL; -#endif +{ + union __RequestUnion__proxy_helper_subsystem req; + union __ReplyUnion__proxy_helper_subsystem rep; +}; static const mach_msg_size_t MAX_MSG_SIZE = sizeof(union max_msg_size) + MAX_TRAILER_SIZE; static aslclient logclient = NULL; @@ -87,256 +77,260 @@ CFRunLoopTimerRef gTimer = NULL; mach_port_t gPort = MACH_PORT_NULL; static void helplogv(int level, const char *fmt, va_list ap) - { - if (NULL == logclient) { vfprintf(stderr, fmt, ap); fflush(stderr); } - else asl_vlog(logclient, NULL, level, fmt, ap); - } +{ + if (NULL == logclient) { vfprintf(stderr, fmt, ap); fflush(stderr); } + else asl_vlog(logclient, NULL, level, fmt, ap); +} void helplog(int level, const char *fmt, ...) - { - va_list ap; - va_start(ap, fmt); - helplogv(level, fmt, ap); - va_end(ap); - } - +{ + va_list ap; + va_start(ap, fmt); + helplogv(level, fmt, ap); + va_end(ap); +} + // for safe_vproc void LogMsgWithLevel(mDNSLogLevel_t logLevel, const char *fmt, ...) - { - (void)logLevel; - va_list ap; - va_start(ap, fmt); - // safe_vproc only calls LogMsg, so assume logLevel maps to ASL_LEVEL_ERR - helplog(ASL_LEVEL_ERR, fmt, ap); - va_end(ap); - } +{ + (void)logLevel; + va_list ap; + va_start(ap, fmt); + // safe_vproc only calls LogMsg, so assume logLevel maps to ASL_LEVEL_ERR + helplog(ASL_LEVEL_ERR, fmt, ap); + va_end(ap); +} static void handle_sigterm(int sig) - { - // debug("entry sig=%d", sig); Can't use syslog from within a signal handler - assert(sig == SIGTERM); - (void)proxy_mDNSExit(gPort); - } +{ + // debug("entry sig=%d", sig); Can't use syslog from within a signal handler + assert(sig == SIGTERM); + (void)proxy_mDNSExit(gPort); +} static void initialize_logging(void) - { - logclient = asl_open(NULL, kmDNSHelperServiceName, (opt_debug ? ASL_OPT_STDERR : 0)); - if (NULL == logclient) { fprintf(stderr, "Could not initialize ASL logging.\n"); fflush(stderr); return; } - if (opt_debug) asl_set_filter(logclient, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); - } +{ + logclient = asl_open(NULL, kmDNSHelperServiceName, (opt_debug ? ASL_OPT_STDERR : 0)); + if (NULL == logclient) { fprintf(stderr, "Could not initialize ASL logging.\n"); fflush(stderr); return; } + if (opt_debug) asl_set_filter(logclient, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); +} static void initialize_id(void) - { - static char login[] = "_mdnsresponder"; - struct passwd hardcode; - struct passwd *pwd = &hardcode; // getpwnam(login); - hardcode.pw_uid = 65; - hardcode.pw_gid = 65; - - if (!pwd) { helplog(ASL_LEVEL_ERR, "Could not find account name `%s'. I will only help root.", login); return; } - mDNSResponderUID = pwd->pw_uid; - mDNSResponderGID = pwd->pw_gid; - } +{ + static char login[] = "_mdnsresponder"; + struct passwd hardcode; + struct passwd *pwd = &hardcode; // getpwnam(login); + hardcode.pw_uid = 65; + hardcode.pw_gid = 65; + + if (!pwd) { helplog(ASL_LEVEL_ERR, "Could not find account name `%s'. I will only help root.", login); return; } + mDNSResponderUID = pwd->pw_uid; + mDNSResponderGID = pwd->pw_gid; +} static void diediedie(CFRunLoopTimerRef timer, void *context) - { - debug("entry %p %p %d", timer, context, maxidle); - assert(gTimer == timer); - if (maxidle) - (void)proxy_mDNSExit(gPort); - } +{ + debug("entry %p %p %d", timer, context, maxidle); + assert(gTimer == timer); + if (maxidle) + (void)proxy_mDNSExit(gPort); +} void pause_idle_timer(void) - { - debug("entry"); - assert(gTimer); - assert(gRunLoop); - CFRunLoopRemoveTimer(gRunLoop, gTimer, kCFRunLoopDefaultMode); - } +{ + debug("entry"); + assert(gTimer); + assert(gRunLoop); + CFRunLoopRemoveTimer(gRunLoop, gTimer, kCFRunLoopDefaultMode); +} void unpause_idle_timer(void) - { - debug("entry"); - assert(gRunLoop); - assert(gTimer); - CFRunLoopAddTimer(gRunLoop, gTimer, kCFRunLoopDefaultMode); - } +{ + debug("entry"); + assert(gRunLoop); + assert(gTimer); + CFRunLoopAddTimer(gRunLoop, gTimer, kCFRunLoopDefaultMode); +} void update_idle_timer(void) - { - debug("entry"); - assert(gTimer); - CFRunLoopTimerSetNextFireDate(gTimer, CFAbsoluteTimeGetCurrent() + actualidle); - } +{ + debug("entry"); + assert(gTimer); + CFRunLoopTimerSetNextFireDate(gTimer, CFAbsoluteTimeGetCurrent() + actualidle); +} static void *idletimer(void *context) - { - debug("entry context=%p", context); - gRunLoop = CFRunLoopGetCurrent(); - - unpause_idle_timer(); - - for (;;) - { - debug("Running CFRunLoop"); - CFRunLoopRun(); - sleep(1); - } - - return NULL; - } +{ + debug("entry context=%p", context); + gRunLoop = CFRunLoopGetCurrent(); + + unpause_idle_timer(); + + for (;;) + { + debug("Running CFRunLoop"); + CFRunLoopRun(); + sleep(1); + } + + return NULL; +} static int initialize_timer() - { - gTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + actualidle, actualidle, 0, 0, diediedie, NULL); - int err = 0; +{ + gTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + actualidle, actualidle, 0, 0, diediedie, NULL); + int err = 0; - debug("entry"); - if (0 != (err = pthread_create(&idletimer_thread, NULL, idletimer, NULL))) - helplog(ASL_LEVEL_ERR, "Could not start idletimer thread: %s", strerror(err)); + debug("entry"); + if (0 != (err = pthread_create(&idletimer_thread, NULL, idletimer, NULL))) + helplog(ASL_LEVEL_ERR, "Could not start idletimer thread: %s", strerror(err)); - return err; - } + return err; +} static mach_port_t checkin(char *service_name) - { - kern_return_t kr = KERN_SUCCESS; - mach_port_t port = MACH_PORT_NULL; - launch_data_t msg = NULL, reply = NULL, datum = NULL; - - if (NULL == (msg = launch_data_new_string(LAUNCH_KEY_CHECKIN))) - { helplog(ASL_LEVEL_ERR, "Could not create checkin message for launchd."); goto fin; } - if (NULL == (reply = launch_msg(msg))) - { helplog(ASL_LEVEL_ERR, "Could not message launchd."); goto fin; } - if (LAUNCH_DATA_ERRNO == launch_data_get_type(reply)) - { - if (launch_data_get_errno(reply) == EACCES) { launch_data_free(msg); launch_data_free(reply); return(MACH_PORT_NULL); } - helplog(ASL_LEVEL_ERR, "Launchd checkin failed: %s.", strerror(launch_data_get_errno(reply))); goto fin; - } - if (NULL == (datum = launch_data_dict_lookup(reply, LAUNCH_JOBKEY_MACHSERVICES)) || LAUNCH_DATA_DICTIONARY != launch_data_get_type(datum)) - { helplog(ASL_LEVEL_ERR, "Launchd reply does not contain %s dictionary.", LAUNCH_JOBKEY_MACHSERVICES); goto fin; } - if (NULL == (datum = launch_data_dict_lookup(datum, service_name)) || LAUNCH_DATA_MACHPORT != launch_data_get_type(datum)) - { helplog(ASL_LEVEL_ERR, "Launchd reply does not contain %s Mach port.", service_name); goto fin; } - if (MACH_PORT_NULL == (port = launch_data_get_machport(datum))) - { helplog(ASL_LEVEL_ERR, "Launchd gave me a null Mach port."); goto fin; } - if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND))) - { helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %d %X %s", kr, kr, mach_error_string(kr)); goto fin; } +{ + kern_return_t kr = KERN_SUCCESS; + mach_port_t port = MACH_PORT_NULL; + launch_data_t msg = NULL, reply = NULL, datum = NULL; + + if (NULL == (msg = launch_data_new_string(LAUNCH_KEY_CHECKIN))) + { helplog(ASL_LEVEL_ERR, "Could not create checkin message for launchd."); goto fin; } + if (NULL == (reply = launch_msg(msg))) + { helplog(ASL_LEVEL_ERR, "Could not message launchd."); goto fin; } + if (LAUNCH_DATA_ERRNO == launch_data_get_type(reply)) + { + if (launch_data_get_errno(reply) == EACCES) { launch_data_free(msg); launch_data_free(reply); return(MACH_PORT_NULL); } + helplog(ASL_LEVEL_ERR, "Launchd checkin failed: %s.", strerror(launch_data_get_errno(reply))); goto fin; + } + if (NULL == (datum = launch_data_dict_lookup(reply, LAUNCH_JOBKEY_MACHSERVICES)) || LAUNCH_DATA_DICTIONARY != launch_data_get_type(datum)) + { helplog(ASL_LEVEL_ERR, "Launchd reply does not contain %s dictionary.", LAUNCH_JOBKEY_MACHSERVICES); goto fin; } + if (NULL == (datum = launch_data_dict_lookup(datum, service_name)) || LAUNCH_DATA_MACHPORT != launch_data_get_type(datum)) + { helplog(ASL_LEVEL_ERR, "Launchd reply does not contain %s Mach port.", service_name); goto fin; } + if (MACH_PORT_NULL == (port = launch_data_get_machport(datum))) + { helplog(ASL_LEVEL_ERR, "Launchd gave me a null Mach port."); goto fin; } + if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND))) + { helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %d %X %s", kr, kr, mach_error_string(kr)); goto fin; } fin: - if (NULL != msg) launch_data_free(msg); - if (NULL != reply) launch_data_free(reply); - if (MACH_PORT_NULL == port) exit(EXIT_FAILURE); - return port; - } + if (NULL != msg) launch_data_free(msg); + if (NULL != reply) launch_data_free(reply); + if (MACH_PORT_NULL == port) exit(EXIT_FAILURE); + return port; +} static mach_port_t register_service(const char *service_name) - { - mach_port_t port = MACH_PORT_NULL; - kern_return_t kr; - - if (KERN_SUCCESS == (kr = bootstrap_check_in(bootstrap_port, (char *)service_name, &port))) - { - if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND))) - helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %d %X %s", kr, kr, mach_error_string(kr)); - else - return port; - } - if (KERN_SUCCESS != (kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port))) - { helplog(ASL_LEVEL_ERR, "mach_port_allocate: %d %X %s", kr, kr, mach_error_string(kr)); goto error; } - if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND))) - { helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %d %X %s", kr, kr, mach_error_string(kr)); goto error; } - - // XXX bootstrap_register does not modify its second argument, but the prototype does not include const. - if (KERN_SUCCESS != (kr = bootstrap_register(bootstrap_port, (char *)service_name, port))) - { helplog(ASL_LEVEL_ERR, "bootstrap_register failed: %s %d %X %s", service_name, kr, kr, mach_error_string(kr)); goto error; } - - return port; +{ + mach_port_t port = MACH_PORT_NULL; + kern_return_t kr; + + if (KERN_SUCCESS == (kr = bootstrap_check_in(bootstrap_port, (char *)service_name, &port))) + { + if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND))) + helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %d %X %s", kr, kr, mach_error_string(kr)); + else + return port; + } + if (KERN_SUCCESS != (kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port))) + { helplog(ASL_LEVEL_ERR, "mach_port_allocate: %d %X %s", kr, kr, mach_error_string(kr)); goto error; } + if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND))) + { helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %d %X %s", kr, kr, mach_error_string(kr)); goto error; } + + // XXX bootstrap_register does not modify its second argument, but the prototype does not include const. + if (KERN_SUCCESS != (kr = bootstrap_register(bootstrap_port, (char *)service_name, port))) + { helplog(ASL_LEVEL_ERR, "bootstrap_register failed: %s %d %X %s", service_name, kr, kr, mach_error_string(kr)); goto error; } + + return port; error: - if (MACH_PORT_NULL != port) mach_port_deallocate(mach_task_self(), port); - return MACH_PORT_NULL; - } + if (MACH_PORT_NULL != port) mach_port_deallocate(mach_task_self(), port); + return MACH_PORT_NULL; +} int main(int ac, char *av[]) - { - char *p = NULL; - kern_return_t kr = KERN_FAILURE; - long n; - int ch; - mach_msg_header_t hdr; - - while ((ch = getopt(ac, av, "dt:")) != -1) - switch (ch) - { - case 'd': opt_debug = 1; break; - case 't': - n = strtol(optarg, &p, 0); - if ('\0' == optarg[0] || '\0' != *p || n > LONG_MAX || n < 0) - { fprintf(stderr, "Invalid idle timeout: %s\n", optarg); exit(EXIT_FAILURE); } - maxidle = n; - break; - case '?': - default: - fprintf(stderr, "Usage: mDNSResponderHelper [-d] [-t maxidle]\n"); - exit(EXIT_FAILURE); - } - ac -= optind; - av += optind; - - initialize_logging(); - helplog(ASL_LEVEL_INFO, "Starting"); - initialize_id(); +{ + char *p = NULL; + kern_return_t kr = KERN_FAILURE; + long n; + int ch; + mach_msg_header_t hdr; + + while ((ch = getopt(ac, av, "dt:")) != -1) + switch (ch) + { + case 'd': opt_debug = 1; break; + case 't': + n = strtol(optarg, &p, 0); + if ('\0' == optarg[0] || '\0' != *p || n > LONG_MAX || n < 0) + { fprintf(stderr, "Invalid idle timeout: %s\n", optarg); exit(EXIT_FAILURE); } + maxidle = n; + break; + case '?': + default: + fprintf(stderr, "Usage: mDNSResponderHelper [-d] [-t maxidle]\n"); + exit(EXIT_FAILURE); + } + ac -= optind; + av += optind; + + initialize_logging(); + helplog(ASL_LEVEL_INFO, "Starting"); + initialize_id(); #ifndef NO_SECURITYFRAMEWORK - // We should normally be running as a system daemon. However, that might not be the case in some scenarios (e.g. debugging). - // Explicitly ensure that our Keychain operations utilize the system domain. - if (opt_debug) SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem); + // We should normally be running as a system daemon. However, that might not be the case in some scenarios (e.g. debugging). + // Explicitly ensure that our Keychain operations utilize the system domain. + if (opt_debug) SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem); #endif - gPort = checkin(kmDNSHelperServiceName); - if (!gPort) - { - helplog(ASL_LEVEL_ERR, "Launchd provided no launchdata; will open Mach port explicitly"); - gPort = register_service(kmDNSHelperServiceName); - } - - if (maxidle) actualidle = maxidle; - - signal(SIGTERM, handle_sigterm); - - if (initialize_timer()) exit(EXIT_FAILURE); - for (n=0; n<100000; n++) if (!gRunLoop) usleep(100); - if (!gRunLoop) - { - helplog(ASL_LEVEL_ERR, "gRunLoop not set after waiting"); - exit(EXIT_FAILURE); - } - - for(;;) - { - hdr.msgh_bits = 0; - hdr.msgh_local_port = gPort; - hdr.msgh_remote_port = MACH_PORT_NULL; - hdr.msgh_size = sizeof(hdr); - hdr.msgh_id = 0; - kr = mach_msg(&hdr, MACH_RCV_LARGE | MACH_RCV_MSG, 0, hdr.msgh_size, gPort, 0, 0); - if (MACH_RCV_TOO_LARGE != kr) - helplog(ASL_LEVEL_ERR, "main MACH_RCV_MSG error: %d %X %s", kr, kr, mach_error_string(kr)); - - safe_vproc_transaction_begin(); - - kr = mach_msg_server_once(helper_server, MAX_MSG_SIZE, gPort, - MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)); - if (KERN_SUCCESS != kr) - { helplog(ASL_LEVEL_ERR, "mach_msg_server: %d %X %s", kr, kr, mach_error_string(kr)); exit(EXIT_FAILURE); } - - safe_vproc_transaction_end(); - } - exit(EXIT_SUCCESS); - } + gPort = checkin(kmDNSHelperServiceName); + if (!gPort) + { + helplog(ASL_LEVEL_ERR, "Launchd provided no launchdata; will open Mach port explicitly"); + gPort = register_service(kmDNSHelperServiceName); + } + + if (maxidle) actualidle = maxidle; + + signal(SIGTERM, handle_sigterm); + + // We use BeginTransactionAtShutdown in the plist that ensures that we will + // receive a SIGTERM during shutdown rather than a SIGKILL. But launchd (due to some + // limitation) currently requires us to still start and end the transaction for + // its proper initialization. + vproc_transaction_t vt = vproc_transaction_begin(NULL); + if (vt) vproc_transaction_end(NULL, vt); + + if (initialize_timer()) exit(EXIT_FAILURE); + for (n=0; n<100000; n++) if (!gRunLoop) usleep(100); + if (!gRunLoop) + { + helplog(ASL_LEVEL_ERR, "gRunLoop not set after waiting"); + exit(EXIT_FAILURE); + } + + for(;;) + { + hdr.msgh_bits = 0; + hdr.msgh_local_port = gPort; + hdr.msgh_remote_port = MACH_PORT_NULL; + hdr.msgh_size = sizeof(hdr); + hdr.msgh_id = 0; + kr = mach_msg(&hdr, MACH_RCV_LARGE | MACH_RCV_MSG, 0, hdr.msgh_size, gPort, 0, 0); + if (MACH_RCV_TOO_LARGE != kr) + helplog(ASL_LEVEL_ERR, "main MACH_RCV_MSG error: %d %X %s", kr, kr, mach_error_string(kr)); + + kr = mach_msg_server_once(helper_server, MAX_MSG_SIZE, gPort, + MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)); + if (KERN_SUCCESS != kr) + { helplog(ASL_LEVEL_ERR, "mach_msg_server: %d %X %s", kr, kr, mach_error_string(kr)); exit(EXIT_FAILURE); } + + } + exit(EXIT_SUCCESS); +} // 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) // For convenience when using the "strings" command, this is the last thing in the file @@ -346,5 +340,5 @@ const char VersionString_SCCS[] = "@(#) mDNSResponderHelper " STRINGIFY(mDNSResp #if _BUILDING_XCODE_PROJECT_ // If the process crashes, then this string will be magically included in the automatically-generated crash log const char *__crashreporter_info__ = VersionString_SCCS + 5; -asm(".desc ___crashreporter_info__, 0x10"); +asm (".desc ___crashreporter_info__, 0x10"); #endif diff --git a/mDNSMacOSX/helper-server.h b/mDNSMacOSX/helper-server.h index 66bdb37..1c391a0 100644 --- a/mDNSMacOSX/helper-server.h +++ b/mDNSMacOSX/helper-server.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/mDNSMacOSX/helper-stubs.c b/mDNSMacOSX/helper-stubs.c index 8cb7d58..9b1afaf 100644 --- a/mDNSMacOSX/helper-stubs.c +++ b/mDNSMacOSX/helper-stubs.c @@ -1,12 +1,12 @@ /* - * Copyright (c) 2007 Apple Inc. All rights reserved. + * Copyright (c) 2007-2012 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -26,270 +26,272 @@ #define ERROR(x, y) y, static const char *errorstring[] = - { - #include "helper-error.h" - NULL - }; +{ + #include "helper-error.h" + NULL +}; #undef ERROR static mach_port_t getHelperPort(int retry) - { - static mach_port_t port = MACH_PORT_NULL; - if (retry) port = MACH_PORT_NULL; - if (port == MACH_PORT_NULL && BOOTSTRAP_SUCCESS != bootstrap_look_up(bootstrap_port, kmDNSHelperServiceName, &port)) - LogMsg("%s: cannot contact helper", __func__); - return port; - } +{ + static mach_port_t port = MACH_PORT_NULL; + if (retry) port = MACH_PORT_NULL; + if (port == MACH_PORT_NULL && BOOTSTRAP_SUCCESS != bootstrap_look_up(bootstrap_port, kmDNSHelperServiceName, &port)) + LogMsg("%s: cannot contact helper", __func__); + return port; +} const char *mDNSHelperError(int err) - { - static const char *p = ""; - if (mDNSHelperErrorBase < err && mDNSHelperErrorEnd > err) - p = errorstring[err - mDNSHelperErrorBase - 1]; - return p; - } +{ + static const char *p = ""; + if (mDNSHelperErrorBase < err && mDNSHelperErrorEnd > err) + p = errorstring[err - mDNSHelperErrorBase - 1]; + return p; +} /* Ugly but handy. */ // We don't bother reporting kIOReturnNotReady because that error code occurs in "normal" operation // and doesn't indicate anything unexpected that needs to be investigated #define MACHRETRYLOOP_BEGIN(kr, retry, err, fin) \ - for (;;) \ - { -#define MACHRETRYLOOP_END(kr, retry, err, fin) \ - if (KERN_SUCCESS == (kr)) break; \ - else if (MACH_SEND_INVALID_DEST == (kr) && 0 == (retry)++) continue; \ - else \ - { \ - (err) = kmDNSHelperCommunicationFailed; \ - LogMsg("%s: Mach communication failed: %d %X %s", __func__, kr, kr, mach_error_string(kr)); \ - goto fin; \ - } \ - } \ - if (0 != (err) && kIOReturnNotReady != (err)) \ - { LogMsg("%s: %d 0x%X (%s)", __func__, (err), (err), mDNSHelperError(err)); goto fin; } + for (;;) \ + { +#define MACHRETRYLOOP_END(kr, retry, err, fin) \ + if (KERN_SUCCESS == (kr)) break; \ + else if (MACH_SEND_INVALID_DEST == (kr) && 0 == (retry)++) continue; \ + else \ + { \ + (err) = kmDNSHelperCommunicationFailed; \ + LogMsg("%s: Mach communication failed: %d %X %s", __func__, kr, kr, mach_error_string(kr)); \ + goto fin; \ + } \ + } \ + if (0 != (err) && kIOReturnNotReady != (err)) \ + { LogMsg("%s: %d 0x%X (%s)", __func__, (err), (err), mDNSHelperError(err)); goto fin; } void mDNSPreferencesSetName(int key, domainlabel *old, domainlabel *new) - { - kern_return_t kr = KERN_FAILURE; - int retry = 0; - int err = 0; - char oldname[MAX_DOMAIN_LABEL+1] = {0}; - char newname[MAX_DOMAIN_LABEL+1] = {0}; - ConvertDomainLabelToCString_unescaped(old, oldname); - if (new) ConvertDomainLabelToCString_unescaped(new, newname); +{ + kern_return_t kr = KERN_FAILURE; + int retry = 0; + int err = 0; + char oldname[MAX_DOMAIN_LABEL+1] = {0}; + char newname[MAX_DOMAIN_LABEL+1] = {0}; + ConvertDomainLabelToCString_unescaped(old, oldname); + if (new) ConvertDomainLabelToCString_unescaped(new, newname); - MACHRETRYLOOP_BEGIN(kr, retry, err, fin); - kr = proxy_mDNSPreferencesSetName(getHelperPort(retry), key, oldname, newname); - MACHRETRYLOOP_END(kr, retry, err, fin); + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSPreferencesSetName(getHelperPort(retry), key, oldname, newname); + MACHRETRYLOOP_END(kr, retry, err, fin); fin: - (void)err; - } + (void)err; +} void mDNSDynamicStoreSetConfig(int key, const char *subkey, CFPropertyListRef value) - { - CFWriteStreamRef stream = NULL; - CFDataRef bytes = NULL; - kern_return_t kr = KERN_FAILURE; - int retry = 0; - int err = 0; +{ + CFWriteStreamRef stream = NULL; + CFDataRef bytes = NULL; + kern_return_t kr = KERN_FAILURE; + int retry = 0; + int err = 0; - if (NULL == (stream = CFWriteStreamCreateWithAllocatedBuffers(NULL, NULL))) - { - err = kmDNSHelperCreationFailed; - LogMsg("%s: CFWriteStreamCreateWithAllocatedBuffers failed", __func__); - goto fin; - } - CFWriteStreamOpen(stream); - if (0 == CFPropertyListWriteToStream(value, stream, kCFPropertyListBinaryFormat_v1_0, NULL)) - { - err = kmDNSHelperPListWriteFailed; - LogMsg("%s: CFPropertyListWriteToStream failed", __func__); - goto fin; - } - if (NULL == (bytes = CFWriteStreamCopyProperty(stream, kCFStreamPropertyDataWritten))) - { - err = kmDNSHelperCreationFailed; - LogMsg("%s: CFWriteStreamCopyProperty failed", __func__); - goto fin; - } - CFWriteStreamClose(stream); - CFRelease(stream); - stream = NULL; - MACHRETRYLOOP_BEGIN(kr, retry, err, fin); - kr = proxy_mDNSDynamicStoreSetConfig(getHelperPort(retry), key, subkey ? subkey : "", (vm_offset_t)CFDataGetBytePtr(bytes), CFDataGetLength(bytes)); - MACHRETRYLOOP_END(kr, retry, err, fin); + if (NULL == (stream = CFWriteStreamCreateWithAllocatedBuffers(NULL, NULL))) + { + err = kmDNSHelperCreationFailed; + LogMsg("%s: CFWriteStreamCreateWithAllocatedBuffers failed", __func__); + goto fin; + } + CFWriteStreamOpen(stream); + if (0 == CFPropertyListWriteToStream(value, stream, kCFPropertyListBinaryFormat_v1_0, NULL)) + { + err = kmDNSHelperPListWriteFailed; + LogMsg("%s: CFPropertyListWriteToStream failed", __func__); + goto fin; + } + if (NULL == (bytes = CFWriteStreamCopyProperty(stream, kCFStreamPropertyDataWritten))) + { + err = kmDNSHelperCreationFailed; + LogMsg("%s: CFWriteStreamCopyProperty failed", __func__); + goto fin; + } + CFWriteStreamClose(stream); + CFRelease(stream); + stream = NULL; + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSDynamicStoreSetConfig(getHelperPort(retry), key, subkey ? subkey : "", (vm_offset_t)CFDataGetBytePtr(bytes), CFDataGetLength(bytes)); + MACHRETRYLOOP_END(kr, retry, err, fin); fin: - if (NULL != stream) { CFWriteStreamClose(stream); CFRelease(stream); } - if (NULL != bytes) CFRelease(bytes); - (void)err; - } + if (NULL != stream) { CFWriteStreamClose(stream); CFRelease(stream); } + if (NULL != bytes) CFRelease(bytes); + (void)err; +} void mDNSRequestBPF(void) - { - kern_return_t kr = KERN_FAILURE; - int retry = 0, err = 0; - MACHRETRYLOOP_BEGIN(kr, retry, err, fin); - kr = proxy_mDNSRequestBPF(getHelperPort(retry)); - MACHRETRYLOOP_END(kr, retry, err, fin); +{ + kern_return_t kr = KERN_FAILURE; + int retry = 0, err = 0; + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSRequestBPF(getHelperPort(retry)); + MACHRETRYLOOP_END(kr, retry, err, fin); fin: - (void)err; - } + (void)err; +} int mDNSPowerRequest(int key, int interval) - { - kern_return_t kr = KERN_FAILURE; - int retry = 0, err = 0; - MACHRETRYLOOP_BEGIN(kr, retry, err, fin); - kr = proxy_mDNSPowerRequest(getHelperPort(retry), key, interval, &err); - MACHRETRYLOOP_END(kr, retry, err, fin); +{ + kern_return_t kr = KERN_FAILURE; + int retry = 0, err = 0; + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSPowerRequest(getHelperPort(retry), key, interval, &err); + MACHRETRYLOOP_END(kr, retry, err, fin); fin: - return err; - } + return err; +} int mDNSSetLocalAddressCacheEntry(int ifindex, int family, const v6addr_t ip, const ethaddr_t eth) - { - kern_return_t kr = KERN_FAILURE; - int retry = 0, err = 0; - MACHRETRYLOOP_BEGIN(kr, retry, err, fin); - kr = proxy_mDNSSetLocalAddressCacheEntry(getHelperPort(retry), ifindex, family, (uint8_t*)ip, (uint8_t*)eth, &err); - MACHRETRYLOOP_END(kr, retry, err, fin); +{ + kern_return_t kr = KERN_FAILURE; + int retry = 0, err = 0; + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSSetLocalAddressCacheEntry(getHelperPort(retry), ifindex, family, (uint8_t*)ip, (uint8_t*)eth, &err); + MACHRETRYLOOP_END(kr, retry, err, fin); fin: - return err; - } + return err; +} -void mDNSNotify(const char *title, const char *msg) // Both strings are UTF-8 text - { - kern_return_t kr = KERN_FAILURE; - int retry = 0, err = 0; - MACHRETRYLOOP_BEGIN(kr, retry, err, fin); - kr = proxy_mDNSNotify(getHelperPort(retry), title, msg); - MACHRETRYLOOP_END(kr, retry, err, fin); +void mDNSNotify(const char *title, const char *msg) // Both strings are UTF-8 text +{ + kern_return_t kr = KERN_FAILURE; + int retry = 0, err = 0; + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSNotify(getHelperPort(retry), title, msg); + MACHRETRYLOOP_END(kr, retry, err, fin); fin: - (void)err; - } + (void)err; +} int mDNSKeychainGetSecrets(CFArrayRef *result) - { - CFPropertyListRef plist = NULL; - CFDataRef bytes = NULL; - kern_return_t kr = KERN_FAILURE; - unsigned int numsecrets = 0; - vm_offset_t secrets = 0; - mach_msg_type_number_t secretsCnt = 0; - int retry = 0, err = 0; - - MACHRETRYLOOP_BEGIN(kr, retry, err, fin); - kr = proxy_mDNSKeychainGetSecrets(getHelperPort(retry), &numsecrets, &secrets, &secretsCnt, &err); - MACHRETRYLOOP_END(kr, retry, err, fin); +{ + CFPropertyListRef plist = NULL; + CFDataRef bytes = NULL; + kern_return_t kr = KERN_FAILURE; + unsigned int numsecrets = 0; + vm_offset_t secrets = 0; + mach_msg_type_number_t secretsCnt = 0; + int retry = 0, err = 0; - if (NULL == (bytes = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (void*)secrets, secretsCnt, kCFAllocatorNull))) - { - err = kmDNSHelperCreationFailed; - LogMsg("%s: CFDataCreateWithBytesNoCopy failed", __func__); - goto fin; - } - if (NULL == (plist = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, bytes, kCFPropertyListImmutable, NULL))) - { - err = kmDNSHelperInvalidPList; - LogMsg("%s: CFPropertyListCreateFromXMLData failed", __func__); - goto fin; - } - if (CFArrayGetTypeID() != CFGetTypeID(plist)) - { - err = kmDNSHelperTypeError; - LogMsg("%s: Unexpected result type", __func__); - CFRelease(plist); - plist = NULL; - goto fin; - } - *result = (CFArrayRef)plist; + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSKeychainGetSecrets(getHelperPort(retry), &numsecrets, &secrets, &secretsCnt, &err); + MACHRETRYLOOP_END(kr, retry, err, fin); -fin: - if (bytes) CFRelease(bytes); - if (secrets) vm_deallocate(mach_task_self(), secrets, secretsCnt); - return err; - } + if (NULL == (bytes = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (void*)secrets, secretsCnt, kCFAllocatorNull))) + { + err = kmDNSHelperCreationFailed; + LogMsg("%s: CFDataCreateWithBytesNoCopy failed", __func__); + goto fin; + } + if (NULL == (plist = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, bytes, kCFPropertyListImmutable, NULL))) + { + err = kmDNSHelperInvalidPList; + LogMsg("%s: CFPropertyListCreateFromXMLData failed", __func__); + goto fin; + } + if (CFArrayGetTypeID() != CFGetTypeID(plist)) + { + err = kmDNSHelperTypeError; + LogMsg("%s: Unexpected result type", __func__); + CFRelease(plist); + plist = NULL; + goto fin; + } + *result = (CFArrayRef)plist; -void mDNSAutoTunnelInterfaceUpDown(int updown, v6addr_t address) - { - kern_return_t kr = KERN_SUCCESS; - int retry = 0, err = 0; - MACHRETRYLOOP_BEGIN(kr, retry, err, fin); - kr = proxy_mDNSAutoTunnelInterfaceUpDown(getHelperPort(retry), updown, address); - MACHRETRYLOOP_END(kr, retry, err, fin); fin: - (void)err; - } - -extern const char dnsprefix[]; + if (bytes) CFRelease(bytes); + if (secrets) vm_deallocate(mach_task_self(), secrets, secretsCnt); + return err; +} void mDNSConfigureServer(int updown, const char *const prefix, const domainname *const fqdn) - { - kern_return_t kr = KERN_SUCCESS; - int retry = 0, err = 0; - char fqdnStr[MAX_ESCAPED_DOMAIN_NAME + 10] = { 0 }; // Assume the prefix is no larger than 10 chars - if (fqdn) - { - mDNSPlatformStrCopy(fqdnStr, prefix); - if (ConvertDomainNameToCString(fqdn, fqdnStr + mDNSPlatformStrLen(prefix)) && prefix == dnsprefix) - { - // remove the trailing dot, as that is not used in the keychain entry racoon will lookup - mDNSu32 fqdnEnd = mDNSPlatformStrLen(fqdnStr); - if (fqdnEnd) fqdnStr[fqdnEnd - 1] = 0; - } - } - MACHRETRYLOOP_BEGIN(kr, retry, err, fin); - kr = proxy_mDNSConfigureServer(getHelperPort(retry), updown, fqdnStr); - MACHRETRYLOOP_END(kr, retry, err, fin); +{ + kern_return_t kr = KERN_SUCCESS; + int retry = 0, err = 0; + char fqdnStr[MAX_ESCAPED_DOMAIN_NAME + 10] = { 0 }; // Assume the prefix is no larger than 10 chars + if (fqdn) + { + mDNSPlatformStrCopy(fqdnStr, prefix); + ConvertDomainNameToCString(fqdn, fqdnStr + mDNSPlatformStrLen(prefix)); + } + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSConfigureServer(getHelperPort(retry), updown, fqdnStr); + MACHRETRYLOOP_END(kr, retry, err, fin); fin: - (void)err; - } + (void)err; +} int mDNSAutoTunnelSetKeys(int replacedelete, v6addr_t local_inner, - v6addr_t local_outer, short local_port, v6addr_t remote_inner, - v6addr_t remote_outer, short remote_port, const char* const prefix, const domainname *const fqdn) - { - kern_return_t kr = KERN_SUCCESS; - int retry = 0, err = 0; - char fqdnStr[MAX_ESCAPED_DOMAIN_NAME + 10] = { 0 }; // Assume the prefix is no larger than 10 chars - if (fqdn) - { - mDNSPlatformStrCopy(fqdnStr, prefix); - if (ConvertDomainNameToCString(fqdn, fqdnStr + mDNSPlatformStrLen(prefix)) && prefix == dnsprefix) - { - // remove the trailing dot, as that is not used in the keychain entry racoon will lookup - mDNSu32 fqdnEnd = mDNSPlatformStrLen(fqdnStr); - if (fqdnEnd) fqdnStr[fqdnEnd - 1] = 0; - } - } - MACHRETRYLOOP_BEGIN(kr, retry, err, fin); - kr = proxy_mDNSAutoTunnelSetKeys(getHelperPort(retry), replacedelete, local_inner, local_outer, local_port, remote_inner, remote_outer, remote_port, fqdnStr, &err); - MACHRETRYLOOP_END(kr, retry, err, fin); + v6addr_t local_outer, short local_port, v6addr_t remote_inner, + v6addr_t remote_outer, short remote_port, const char* const prefix, const domainname *const fqdn) +{ + kern_return_t kr = KERN_SUCCESS; + int retry = 0, err = 0; + char fqdnStr[MAX_ESCAPED_DOMAIN_NAME + 10] = { 0 }; // Assume the prefix is no larger than 10 chars + if (fqdn) + { + mDNSPlatformStrCopy(fqdnStr, prefix); + ConvertDomainNameToCString(fqdn, fqdnStr + mDNSPlatformStrLen(prefix)); + } + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSAutoTunnelSetKeys(getHelperPort(retry), replacedelete, local_inner, local_outer, local_port, remote_inner, remote_outer, remote_port, fqdnStr, &err); + MACHRETRYLOOP_END(kr, retry, err, fin); fin: - return err; - } + return err; +} void mDNSSendWakeupPacket(unsigned ifid, char *eth_addr, char *ip_addr, int iteration) - { - kern_return_t kr = KERN_SUCCESS; - int retry = 0, err = 0; - MACHRETRYLOOP_BEGIN(kr, retry, err, fin); - kr = proxy_mDNSSendWakeupPacket(getHelperPort(retry), ifid, eth_addr, ip_addr, iteration); - MACHRETRYLOOP_END(kr, retry, err, fin); +{ + kern_return_t kr = KERN_SUCCESS; + int retry = 0, err = 0; + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSSendWakeupPacket(getHelperPort(retry), ifid, eth_addr, ip_addr, iteration); + MACHRETRYLOOP_END(kr, retry, err, fin); +fin: + (void) err; +} + +void mDNSPacketFilterControl(uint32_t command, char * ifname, uint32_t count, pfArray_t portArray, pfArray_t protocolArray) +{ + kern_return_t kr = KERN_SUCCESS; + int retry = 0, err = 0; + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSPacketFilterControl(getHelperPort(retry), command, ifname, count, portArray, protocolArray); + MACHRETRYLOOP_END(kr, retry, err, fin); +fin: + (void) err; +} + +int mDNSSendKeepalive(v6addr_t sadd, v6addr_t dadd, uint16_t lport, uint16_t rport, unsigned seq, unsigned ack, uint16_t win) +{ + kern_return_t kr = KERN_FAILURE; + int retry = 0, err = 0; + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSSendKeepalive(getHelperPort(retry), sadd, dadd, lport, rport, seq, ack, win); + MACHRETRYLOOP_END(kr, retry, err, fin); fin: - (void) err; - } + return err; +} + + +int mDNSInterfaceAdvtIoctl(const char *ifname, int op) +{ + kern_return_t kr = KERN_FAILURE; + int retry = 0, err = 0; + + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSInterfaceAdvtIoctl(getHelperPort(retry), ifname, op); + MACHRETRYLOOP_END(kr, retry, err, fin); -void mDNSPacketFilterControl(uint32_t command, char * ifname, uint16_t servicePort, uint16_t protocol) - { - kern_return_t kr = KERN_SUCCESS; - int retry = 0, err = 0; - MACHRETRYLOOP_BEGIN(kr, retry, err, fin); - kr = proxy_mDNSPacketFilterControl(getHelperPort(retry), command, ifname, servicePort, protocol); - MACHRETRYLOOP_END(kr, retry, err, fin); fin: - (void) err; - } + return err; +} diff --git a/mDNSMacOSX/helper.c b/mDNSMacOSX/helper.c index 73ceb3e..c1f2497 100644 --- a/mDNSMacOSX/helper.c +++ b/mDNSMacOSX/helper.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2007 Apple Inc. All rights reserved. + * Copyright (c) 2007-2012 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -59,6 +59,10 @@ #include "ipsec_options.h" #include "P2PPacketFilter.h" +#include +#include +#include + #ifndef RTF_IFSCOPE #define RTF_IFSCOPE 0x1000000 #endif @@ -77,355 +81,397 @@ typedef struct sadb_x_policy *ipsec_policy_t; +unsigned short InetChecksum(unsigned short *ptr,int nbytes); +unsigned long in_cksum(unsigned short *ptr,int nbytes); +void TCPCheckSum(int af, struct tcphdr *t, int tcplen, v6addr_t sadd6, v6addr_t dadd6); + uid_t mDNSResponderUID; gid_t mDNSResponderGID; static const char kTunnelAddressInterface[] = "lo0"; void debug_(const char *func, const char *fmt, ...) - { - char buf[2048]; - va_list ap; +{ + char buf[2048]; + va_list ap; - va_start(ap, fmt); - vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - helplog(ASL_LEVEL_DEBUG, "%s: %s", func, buf); - } + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + helplog(ASL_LEVEL_DEBUG, "%s: %s", func, buf); +} static int authorized(audit_token_t *token) - { - int ok = 0; - pid_t pid = (pid_t)-1; - uid_t euid = (uid_t)-1; - - audit_token_to_au32(*token, NULL, &euid, NULL, NULL, NULL, &pid, NULL, - NULL); - ok = (euid == mDNSResponderUID || euid == 0); - if (!ok) - helplog(ASL_LEVEL_NOTICE, - "Unauthorized access by euid=%lu pid=%lu", - (unsigned long)euid, (unsigned long)pid); - return ok; - } +{ + int ok = 0; + pid_t pid = (pid_t)-1; + uid_t euid = (uid_t)-1; + + audit_token_to_au32(*token, NULL, &euid, NULL, NULL, NULL, &pid, NULL, + NULL); + ok = (euid == mDNSResponderUID || euid == 0); + if (!ok) + helplog(ASL_LEVEL_NOTICE, + "Unauthorized access by euid=%lu pid=%lu", + (unsigned long)euid, (unsigned long)pid); + return ok; +} kern_return_t do_mDNSExit(__unused mach_port_t port, audit_token_t token) - { - debug("entry"); - if (!authorized(&token)) - goto fin; - helplog(ASL_LEVEL_INFO, "exit"); - exit(0); +{ + debug("entry"); + if (!authorized(&token)) + goto fin; + helplog(ASL_LEVEL_INFO, "exit"); + exit(0); fin: - debug("fin"); - return KERN_SUCCESS; - } + debug("fin"); + return KERN_SUCCESS; +} kern_return_t do_mDNSRequestBPF(__unused mach_port_t port, audit_token_t token) - { - if (!authorized(&token)) return KERN_SUCCESS; - DNSServiceRef ref; - DNSServiceErrorType err = ConnectToServer(&ref, 0, send_bpf, NULL, NULL, NULL); - if (err) { helplog(ASL_LEVEL_ERR, "do_mDNSRequestBPF: ConnectToServer %d", err); return err; } - - char *ptr; - size_t len = sizeof(DNSServiceFlags); - ipc_msg_hdr *hdr = create_hdr(send_bpf, &len, &ptr, 0, ref); - if (!hdr) { DNSServiceRefDeallocate(ref); return kDNSServiceErr_NoMemory; } - put_flags(0, &ptr); - deliver_request(hdr, ref); // Will free hdr for us - DNSServiceRefDeallocate(ref); - update_idle_timer(); - return KERN_SUCCESS; - } +{ + if (!authorized(&token)) return KERN_SUCCESS; + DNSServiceRef ref; + DNSServiceErrorType err = ConnectToServer(&ref, 0, send_bpf, NULL, NULL, NULL); + if (err) { helplog(ASL_LEVEL_ERR, "do_mDNSRequestBPF: ConnectToServer %d", err); return err; } + + char *ptr; + size_t len = sizeof(DNSServiceFlags); + ipc_msg_hdr *hdr = create_hdr(send_bpf, &len, &ptr, 0, ref); + if (!hdr) { DNSServiceRefDeallocate(ref); return kDNSServiceErr_NoMemory; } + put_flags(0, &ptr); + deliver_request(hdr, ref); // Will free hdr for us + DNSServiceRefDeallocate(ref); + update_idle_timer(); + return KERN_SUCCESS; +} + +kern_return_t do_mDNSInterfaceAdvtIoctl(__unused mach_port_t port, const char *ifname, int op, audit_token_t token) +{ + struct in6_ndireq nd; + mDNSu32 newflags; + int sock; + + if (!authorized(&token)) + { + return KERN_SUCCESS; + } + + if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) + { + helplog(ASL_LEVEL_ERR, "%s: Socket call failed - error (%d) %s", __func__, errno, strerror(errno)); + return errno; + } + memset(&nd, 0, sizeof(nd)); + strlcpy(nd.ifname, ifname, sizeof(nd.ifname)); + + if (ioctl(sock, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) + { + helplog(ASL_LEVEL_ERR, "%s: ioctl call SIOCGIFINFO_IN6 failed - error (%d) %s", __func__, errno, strerror(errno)); + close(sock); + return errno; + } + newflags = nd.ndi.flags; + newflags = (op == 0) ? (newflags | ND6_IFF_IGNORE_NA) : (newflags & ~(ND6_IFF_IGNORE_NA)); + + if (ioctl(sock, SIOCSIFINFO_FLAGS, (caddr_t)&nd) < 0) + { + helplog(ASL_LEVEL_ERR, "%s: ioctl call SIOCSIFINFO_IN6 failed - error (%d) %s", __func__, errno, strerror(errno)); + close(sock); + return errno; + } + close(sock); + return KERN_SUCCESS; +} kern_return_t do_mDNSPowerRequest(__unused mach_port_t port, int key, int interval, int *err, audit_token_t token) - { - *err = -1; - if (!authorized(&token)) { *err = kmDNSHelperNotAuthorized; goto fin; } - - CFArrayRef events = IOPMCopyScheduledPowerEvents(); - if (events) - { - int i; - CFIndex count = CFArrayGetCount(events); - for (i=0; i 0) - { - CFDateRef w = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent() + interval); - if (w) - { - IOReturn r = IOPMSchedulePowerEvent(w, CFSTR("mDNSResponderHelper"), key ? CFSTR(kIOPMAutoWake) : CFSTR(kIOPMAutoSleep)); - if (r) { usleep(100000); helplog(ASL_LEVEL_ERR, "IOPMSchedulePowerEvent(%d) %d %x", interval, r, r); } - *err = r; - CFRelease(w); - } - } +{ + *err = -1; + if (!authorized(&token)) { *err = kmDNSHelperNotAuthorized; goto fin; } + + CFArrayRef events = IOPMCopyScheduledPowerEvents(); + if (events) + { + int i; + CFIndex count = CFArrayGetCount(events); + for (i=0; i 0) + { + CFDateRef w = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent() + interval); + if (w) + { + IOReturn r = IOPMSchedulePowerEvent(w, CFSTR("mDNSResponderHelper"), key ? CFSTR(kIOPMAutoWake) : CFSTR(kIOPMAutoSleep)); + if (r) { usleep(100000); helplog(ASL_LEVEL_ERR, "IOPMSchedulePowerEvent(%d) %d %x", interval, r, r); } + *err = r; + CFRelease(w); + } + } fin: - update_idle_timer(); - return KERN_SUCCESS; - } + update_idle_timer(); + return KERN_SUCCESS; +} kern_return_t do_mDNSSetLocalAddressCacheEntry(__unused mach_port_t port, int ifindex, int family, v6addr_t ip, ethaddr_t eth, int *err, audit_token_t token) - { - #define IPv6FMTSTRING "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X" - #define IPv6FMTARGS ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[8], ip[9], ip[10], ip[11], ip[12], ip[13], ip[14], ip[15] - #if 0 - if (family == 4) - helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry %d IPv%d %d.%d.%d.%d %02X:%02X:%02X:%02X:%02X:%02X", - ifindex, family, ip[0], ip[1], ip[2], ip[3], eth[0], eth[1], eth[2], eth[3], eth[4], eth[5]); - else - helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry %d IPv%d " IPv6FMTSTRING " %02X:%02X:%02X:%02X:%02X:%02X", - ifindex, family, IPv6FMTARGS, eth[0], eth[1], eth[2], eth[3], eth[4], eth[5]); - #endif - - *err = -1; - if (!authorized(&token)) { *err = kmDNSHelperNotAuthorized; goto fin; } - - static int s = -1, seq = 0; - if (s < 0) - { - s = socket(PF_ROUTE, SOCK_RAW, 0); - if (s < 0) helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry: socket(PF_ROUTE, SOCK_RAW, 0) failed %d (%s)", errno, strerror(errno)); - } - - if (s >= 0) - { - struct timeval tv; - gettimeofday(&tv, 0); - if (family == 4) - { - struct { struct rt_msghdr hdr; struct sockaddr_inarp dst; struct sockaddr_dl sdl; } rtmsg; - memset(&rtmsg, 0, sizeof(rtmsg)); - - rtmsg.hdr.rtm_msglen = sizeof(rtmsg); - rtmsg.hdr.rtm_version = RTM_VERSION; - rtmsg.hdr.rtm_type = RTM_ADD; - rtmsg.hdr.rtm_index = ifindex; - rtmsg.hdr.rtm_flags = RTF_HOST | RTF_STATIC | RTF_IFSCOPE; - rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY; - rtmsg.hdr.rtm_pid = 0; - rtmsg.hdr.rtm_seq = seq++; - rtmsg.hdr.rtm_errno = 0; - rtmsg.hdr.rtm_use = 0; - rtmsg.hdr.rtm_inits = RTV_EXPIRE; - rtmsg.hdr.rtm_rmx.rmx_expire = tv.tv_sec + 30; - - rtmsg.dst.sin_len = sizeof(rtmsg.dst); - rtmsg.dst.sin_family = AF_INET; - rtmsg.dst.sin_port = 0; - rtmsg.dst.sin_addr.s_addr = *(in_addr_t*)ip; - rtmsg.dst.sin_srcaddr.s_addr = 0; - rtmsg.dst.sin_tos = 0; - rtmsg.dst.sin_other = 0; - - rtmsg.sdl.sdl_len = sizeof(rtmsg.sdl); - rtmsg.sdl.sdl_family = AF_LINK; - rtmsg.sdl.sdl_index = ifindex; - rtmsg.sdl.sdl_type = IFT_ETHER; - rtmsg.sdl.sdl_nlen = 0; - rtmsg.sdl.sdl_alen = ETHER_ADDR_LEN; - rtmsg.sdl.sdl_slen = 0; - - // Target MAC address goes in rtmsg.sdl.sdl_data[0..5]; (See LLADDR() in /usr/include/net/if_dl.h) - memcpy(rtmsg.sdl.sdl_data, eth, sizeof(ethaddr_t)); - - int len = write(s, (char *)&rtmsg, sizeof(rtmsg)); - if (len < 0) - helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry: write(%d) interface %d address %d.%d.%d.%d seq %d result %d errno %d (%s)", - sizeof(rtmsg), ifindex, ip[0], ip[1], ip[2], ip[3], rtmsg.hdr.rtm_seq, len, errno, strerror(errno)); - len = read(s, (char *)&rtmsg, sizeof(rtmsg)); - if (len < 0 || rtmsg.hdr.rtm_errno) - helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry: read (%d) interface %d address %d.%d.%d.%d seq %d result %d errno %d (%s) %d", - sizeof(rtmsg), ifindex, ip[0], ip[1], ip[2], ip[3], rtmsg.hdr.rtm_seq, len, errno, strerror(errno), rtmsg.hdr.rtm_errno); - - *err = 0; - } - else - { - struct { struct rt_msghdr hdr; struct sockaddr_in6 dst; struct sockaddr_dl sdl; } rtmsg; - memset(&rtmsg, 0, sizeof(rtmsg)); - - rtmsg.hdr.rtm_msglen = sizeof(rtmsg); - rtmsg.hdr.rtm_version = RTM_VERSION; - rtmsg.hdr.rtm_type = RTM_ADD; - rtmsg.hdr.rtm_index = ifindex; - rtmsg.hdr.rtm_flags = RTF_HOST | RTF_STATIC | RTF_IFSCOPE; - rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY; - rtmsg.hdr.rtm_pid = 0; - rtmsg.hdr.rtm_seq = seq++; - rtmsg.hdr.rtm_errno = 0; - rtmsg.hdr.rtm_use = 0; - rtmsg.hdr.rtm_inits = RTV_EXPIRE; - rtmsg.hdr.rtm_rmx.rmx_expire = tv.tv_sec + 30; - - rtmsg.dst.sin6_len = sizeof(rtmsg.dst); - rtmsg.dst.sin6_family = AF_INET6; - rtmsg.dst.sin6_port = 0; - rtmsg.dst.sin6_flowinfo = 0; - rtmsg.dst.sin6_addr = *(struct in6_addr*)ip; - rtmsg.dst.sin6_scope_id = ifindex; - - rtmsg.sdl.sdl_len = sizeof(rtmsg.sdl); - rtmsg.sdl.sdl_family = AF_LINK; - rtmsg.sdl.sdl_index = ifindex; - rtmsg.sdl.sdl_type = IFT_ETHER; - rtmsg.sdl.sdl_nlen = 0; - rtmsg.sdl.sdl_alen = ETHER_ADDR_LEN; - rtmsg.sdl.sdl_slen = 0; - - // Target MAC address goes in rtmsg.sdl.sdl_data[0..5]; (See LLADDR() in /usr/include/net/if_dl.h) - memcpy(rtmsg.sdl.sdl_data, eth, sizeof(ethaddr_t)); - - int len = write(s, (char *)&rtmsg, sizeof(rtmsg)); - if (len < 0) - helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry: write(%d) interface %d address " IPv6FMTSTRING " seq %d result %d errno %d (%s)", - sizeof(rtmsg), ifindex, IPv6FMTARGS, rtmsg.hdr.rtm_seq, len, errno, strerror(errno)); - len = read(s, (char *)&rtmsg, sizeof(rtmsg)); - if (len < 0 || rtmsg.hdr.rtm_errno) - helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry: read (%d) interface %d address " IPv6FMTSTRING " seq %d result %d errno %d (%s) %d", - sizeof(rtmsg), ifindex, IPv6FMTARGS, rtmsg.hdr.rtm_seq, len, errno, strerror(errno), rtmsg.hdr.rtm_errno); - - *err = 0; - } - - } +{ + #define IPv6FMTSTRING "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X" + #define IPv6FMTARGS ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[8], ip[9], ip[10], ip[11], ip[12], ip[13], ip[14], ip[15] + #if 0 + if (family == 4) + helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry %d IPv%d %d.%d.%d.%d %02X:%02X:%02X:%02X:%02X:%02X", + ifindex, family, ip[0], ip[1], ip[2], ip[3], eth[0], eth[1], eth[2], eth[3], eth[4], eth[5]); + else + helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry %d IPv%d " IPv6FMTSTRING " %02X:%02X:%02X:%02X:%02X:%02X", + ifindex, family, IPv6FMTARGS, eth[0], eth[1], eth[2], eth[3], eth[4], eth[5]); + #endif + + *err = -1; + if (!authorized(&token)) { *err = kmDNSHelperNotAuthorized; goto fin; } + + static int s = -1, seq = 0; + if (s < 0) + { + s = socket(PF_ROUTE, SOCK_RAW, 0); + if (s < 0) helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry: socket(PF_ROUTE, SOCK_RAW, 0) failed %d (%s)", errno, strerror(errno)); + } + + if (s >= 0) + { + struct timeval tv; + gettimeofday(&tv, 0); + if (family == 4) + { + struct { struct rt_msghdr hdr; struct sockaddr_inarp dst; struct sockaddr_dl sdl; } rtmsg; + memset(&rtmsg, 0, sizeof(rtmsg)); + + rtmsg.hdr.rtm_msglen = sizeof(rtmsg); + rtmsg.hdr.rtm_version = RTM_VERSION; + rtmsg.hdr.rtm_type = RTM_ADD; + rtmsg.hdr.rtm_index = ifindex; + rtmsg.hdr.rtm_flags = RTF_HOST | RTF_STATIC | RTF_IFSCOPE; + rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY; + rtmsg.hdr.rtm_pid = 0; + rtmsg.hdr.rtm_seq = seq++; + rtmsg.hdr.rtm_errno = 0; + rtmsg.hdr.rtm_use = 0; + rtmsg.hdr.rtm_inits = RTV_EXPIRE; + rtmsg.hdr.rtm_rmx.rmx_expire = tv.tv_sec + 30; + + rtmsg.dst.sin_len = sizeof(rtmsg.dst); + rtmsg.dst.sin_family = AF_INET; + rtmsg.dst.sin_port = 0; + rtmsg.dst.sin_addr.s_addr = *(in_addr_t*)ip; + rtmsg.dst.sin_srcaddr.s_addr = 0; + rtmsg.dst.sin_tos = 0; + rtmsg.dst.sin_other = 0; + + rtmsg.sdl.sdl_len = sizeof(rtmsg.sdl); + rtmsg.sdl.sdl_family = AF_LINK; + rtmsg.sdl.sdl_index = ifindex; + rtmsg.sdl.sdl_type = IFT_ETHER; + rtmsg.sdl.sdl_nlen = 0; + rtmsg.sdl.sdl_alen = ETHER_ADDR_LEN; + rtmsg.sdl.sdl_slen = 0; + + // Target MAC address goes in rtmsg.sdl.sdl_data[0..5]; (See LLADDR() in /usr/include/net/if_dl.h) + memcpy(rtmsg.sdl.sdl_data, eth, sizeof(ethaddr_t)); + + int len = write(s, (char *)&rtmsg, sizeof(rtmsg)); + if (len < 0) + helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry: write(%d) interface %d address %d.%d.%d.%d seq %d result %d errno %d (%s)", + sizeof(rtmsg), ifindex, ip[0], ip[1], ip[2], ip[3], rtmsg.hdr.rtm_seq, len, errno, strerror(errno)); + len = read(s, (char *)&rtmsg, sizeof(rtmsg)); + if (len < 0 || rtmsg.hdr.rtm_errno) + helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry: read (%d) interface %d address %d.%d.%d.%d seq %d result %d errno %d (%s) %d", + sizeof(rtmsg), ifindex, ip[0], ip[1], ip[2], ip[3], rtmsg.hdr.rtm_seq, len, errno, strerror(errno), rtmsg.hdr.rtm_errno); + + *err = 0; + } + else + { + struct { struct rt_msghdr hdr; struct sockaddr_in6 dst; struct sockaddr_dl sdl; } rtmsg; + memset(&rtmsg, 0, sizeof(rtmsg)); + + rtmsg.hdr.rtm_msglen = sizeof(rtmsg); + rtmsg.hdr.rtm_version = RTM_VERSION; + rtmsg.hdr.rtm_type = RTM_ADD; + rtmsg.hdr.rtm_index = ifindex; + rtmsg.hdr.rtm_flags = RTF_HOST | RTF_STATIC | RTF_IFSCOPE; + rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY; + rtmsg.hdr.rtm_pid = 0; + rtmsg.hdr.rtm_seq = seq++; + rtmsg.hdr.rtm_errno = 0; + rtmsg.hdr.rtm_use = 0; + rtmsg.hdr.rtm_inits = RTV_EXPIRE; + rtmsg.hdr.rtm_rmx.rmx_expire = tv.tv_sec + 30; + + rtmsg.dst.sin6_len = sizeof(rtmsg.dst); + rtmsg.dst.sin6_family = AF_INET6; + rtmsg.dst.sin6_port = 0; + rtmsg.dst.sin6_flowinfo = 0; + rtmsg.dst.sin6_addr = *(struct in6_addr*)ip; + rtmsg.dst.sin6_scope_id = ifindex; + + rtmsg.sdl.sdl_len = sizeof(rtmsg.sdl); + rtmsg.sdl.sdl_family = AF_LINK; + rtmsg.sdl.sdl_index = ifindex; + rtmsg.sdl.sdl_type = IFT_ETHER; + rtmsg.sdl.sdl_nlen = 0; + rtmsg.sdl.sdl_alen = ETHER_ADDR_LEN; + rtmsg.sdl.sdl_slen = 0; + + // Target MAC address goes in rtmsg.sdl.sdl_data[0..5]; (See LLADDR() in /usr/include/net/if_dl.h) + memcpy(rtmsg.sdl.sdl_data, eth, sizeof(ethaddr_t)); + + int len = write(s, (char *)&rtmsg, sizeof(rtmsg)); + if (len < 0) + helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry: write(%d) interface %d address " IPv6FMTSTRING " seq %d result %d errno %d (%s)", + sizeof(rtmsg), ifindex, IPv6FMTARGS, rtmsg.hdr.rtm_seq, len, errno, strerror(errno)); + len = read(s, (char *)&rtmsg, sizeof(rtmsg)); + if (len < 0 || rtmsg.hdr.rtm_errno) + helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry: read (%d) interface %d address " IPv6FMTSTRING " seq %d result %d errno %d (%s) %d", + sizeof(rtmsg), ifindex, IPv6FMTARGS, rtmsg.hdr.rtm_seq, len, errno, strerror(errno), rtmsg.hdr.rtm_errno); + + *err = 0; + } + + } fin: - update_idle_timer(); - return KERN_SUCCESS; - } + update_idle_timer(); + return KERN_SUCCESS; +} kern_return_t do_mDNSNotify(__unused mach_port_t port, const char *title, const char *msg, audit_token_t token) - { - if (!authorized(&token)) return KERN_SUCCESS; +{ + if (!authorized(&token)) return KERN_SUCCESS; #ifndef NO_CFUSERNOTIFICATION - static const char footer[] = "(Note: This message only appears on machines with 17.x.x.x IP addresses — i.e. at Apple — not on customer machines.)"; - CFStringRef alertHeader = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8); - CFStringRef alertBody = CFStringCreateWithCString(NULL, msg, kCFStringEncodingUTF8); - CFStringRef alertFooter = CFStringCreateWithCString(NULL, footer, kCFStringEncodingUTF8); - CFStringRef alertMessage = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@\r\r%@"), alertBody, alertFooter); - CFRelease(alertBody); - CFRelease(alertFooter); - int err = CFUserNotificationDisplayNotice(0.0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL, alertHeader, alertMessage, NULL); - if (err) helplog(ASL_LEVEL_ERR, "CFUserNotificationDisplayNotice returned %d", err); - CFRelease(alertHeader); - CFRelease(alertMessage); + static const char footer[] = "(Note: This message only appears on machines with 17.x.x.x IP addresses — i.e. at Apple — not on customer machines.)"; + CFStringRef alertHeader = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8); + CFStringRef alertBody = CFStringCreateWithCString(NULL, msg, kCFStringEncodingUTF8); + CFStringRef alertFooter = CFStringCreateWithCString(NULL, footer, kCFStringEncodingUTF8); + CFStringRef alertMessage = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@\r\r%@"), alertBody, alertFooter); + CFRelease(alertBody); + CFRelease(alertFooter); + int err = CFUserNotificationDisplayNotice(0.0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL, alertHeader, alertMessage, NULL); + if (err) helplog(ASL_LEVEL_ERR, "CFUserNotificationDisplayNotice returned %d", err); + CFRelease(alertHeader); + CFRelease(alertMessage); #else - (void)title; - (void)msg; + (void)title; + (void)msg; #endif /* NO_CFUSERNOTIFICATION */ - update_idle_timer(); - return KERN_SUCCESS; - } + update_idle_timer(); + return KERN_SUCCESS; +} kern_return_t do_mDNSDynamicStoreSetConfig(__unused mach_port_t port, int key, - const char* subkey, vm_offset_t value, mach_msg_type_number_t valueCnt, - audit_token_t token) - { - CFStringRef sckey = NULL; - Boolean release_sckey = FALSE; - CFDataRef bytes = NULL; - CFPropertyListRef plist = NULL; - SCDynamicStoreRef store = NULL; - - debug("entry"); - if (!authorized(&token)) goto fin; - - switch ((enum mDNSDynamicStoreSetConfigKey)key) - { - case kmDNSMulticastConfig: - sckey = CFSTR("State:/Network/" kDNSServiceCompMulticastDNS); - break; - case kmDNSDynamicConfig: - sckey = CFSTR("State:/Network/DynamicDNS"); - break; - case kmDNSPrivateConfig: - sckey = CFSTR("State:/Network/" kDNSServiceCompPrivateDNS); - break; - case kmDNSBackToMyMacConfig: - sckey = CFSTR("State:/Network/BackToMyMac"); - break; - case kmDNSSleepProxyServersState: - { - CFMutableStringRef tmp = CFStringCreateMutable(kCFAllocatorDefault, 0); - CFStringAppend(tmp, CFSTR("State:/Network/Interface/")); - CFStringAppendCString(tmp, subkey, kCFStringEncodingUTF8); - CFStringAppend(tmp, CFSTR("/SleepProxyServers")); - sckey = CFStringCreateCopy(kCFAllocatorDefault, tmp); - release_sckey = TRUE; - CFRelease(tmp); - break; - } - default: - debug("unrecognized key %d", key); - goto fin; - } - if (NULL == (bytes = CFDataCreateWithBytesNoCopy(NULL, (void *)value, - valueCnt, kCFAllocatorNull))) - { - debug("CFDataCreateWithBytesNoCopy of value failed"); - goto fin; - } - if (NULL == (plist = CFPropertyListCreateFromXMLData(NULL, bytes, - kCFPropertyListImmutable, NULL))) - { - debug("CFPropertyListCreateFromXMLData of bytes failed"); - goto fin; - } - CFRelease(bytes); - bytes = NULL; - if (NULL == (store = SCDynamicStoreCreate(NULL, - CFSTR(kmDNSHelperServiceName), NULL, NULL))) - { - debug("SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); - goto fin; - } - SCDynamicStoreSetValue(store, sckey, plist); - debug("succeeded"); + const char* subkey, vm_offset_t value, mach_msg_type_number_t valueCnt, + audit_token_t token) +{ + CFStringRef sckey = NULL; + Boolean release_sckey = FALSE; + CFDataRef bytes = NULL; + CFPropertyListRef plist = NULL; + SCDynamicStoreRef store = NULL; + + debug("entry"); + if (!authorized(&token)) goto fin; + + switch ((enum mDNSDynamicStoreSetConfigKey)key) + { + case kmDNSMulticastConfig: + sckey = CFSTR("State:/Network/" kDNSServiceCompMulticastDNS); + break; + case kmDNSDynamicConfig: + sckey = CFSTR("State:/Network/DynamicDNS"); + break; + case kmDNSPrivateConfig: + sckey = CFSTR("State:/Network/" kDNSServiceCompPrivateDNS); + break; + case kmDNSBackToMyMacConfig: + sckey = CFSTR("State:/Network/BackToMyMac"); + break; + case kmDNSSleepProxyServersState: + { + CFMutableStringRef tmp = CFStringCreateMutable(kCFAllocatorDefault, 0); + CFStringAppend(tmp, CFSTR("State:/Network/Interface/")); + CFStringAppendCString(tmp, subkey, kCFStringEncodingUTF8); + CFStringAppend(tmp, CFSTR("/SleepProxyServers")); + sckey = CFStringCreateCopy(kCFAllocatorDefault, tmp); + release_sckey = TRUE; + CFRelease(tmp); + break; + } + default: + debug("unrecognized key %d", key); + goto fin; + } + if (NULL == (bytes = CFDataCreateWithBytesNoCopy(NULL, (void *)value, + valueCnt, kCFAllocatorNull))) + { + debug("CFDataCreateWithBytesNoCopy of value failed"); + goto fin; + } + if (NULL == (plist = CFPropertyListCreateFromXMLData(NULL, bytes, + kCFPropertyListImmutable, NULL))) + { + debug("CFPropertyListCreateFromXMLData of bytes failed"); + goto fin; + } + CFRelease(bytes); + bytes = NULL; + if (NULL == (store = SCDynamicStoreCreate(NULL, + CFSTR(kmDNSHelperServiceName), NULL, NULL))) + { + debug("SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); + goto fin; + } + SCDynamicStoreSetValue(store, sckey, plist); + debug("succeeded"); fin: - if (NULL != bytes) - CFRelease(bytes); - if (NULL != plist) - CFRelease(plist); - if (NULL != store) - CFRelease(store); - if (release_sckey && sckey) - CFRelease(sckey); - vm_deallocate(mach_task_self(), value, valueCnt); - update_idle_timer(); - return KERN_SUCCESS; - } + if (NULL != bytes) + CFRelease(bytes); + if (NULL != plist) + CFRelease(plist); + if (NULL != store) + CFRelease(store); + if (release_sckey && sckey) + CFRelease(sckey); + vm_deallocate(mach_task_self(), value, valueCnt); + update_idle_timer(); + return KERN_SUCCESS; +} char usercompname[MAX_DOMAIN_LABEL+1] = {0}; // the last computer name the user saw char userhostname[MAX_DOMAIN_LABEL+1] = {0}; // the last local host name the user saw @@ -443,329 +489,329 @@ static CFStringRef CFS_LocalHostNameMsg = NULL; static CFStringRef CFS_Problem = NULL; static CFUserNotificationRef gNotification = NULL; -static CFRunLoopSourceRef gNotificationRLS = NULL; +static CFRunLoopSourceRef gNotificationRLS = NULL; static void NotificationCallBackDismissed(CFUserNotificationRef userNotification, CFOptionFlags responseFlags) - { - debug("entry"); - (void)responseFlags; // Unused - if (userNotification != gNotification) helplog(ASL_LEVEL_ERR, "NotificationCallBackDismissed: Wrong CFUserNotificationRef"); - if (gNotificationRLS) - { - // Caution: don't use CFRunLoopGetCurrent() here, because the currently executing thread may not be our "CFRunLoopRun" thread. - // We need to explicitly specify the desired CFRunLoop from which we want to remove this event source. - CFRunLoopRemoveSource(gRunLoop, gNotificationRLS, kCFRunLoopDefaultMode); - CFRelease(gNotificationRLS); - gNotificationRLS = NULL; - CFRelease(gNotification); - gNotification = NULL; - } - // By dismissing the alert, the user has conceptually acknowleged the rename. - // (e.g. the machine's name is now officially "computer-2.local", not "computer.local".) - // If we get *another* conflict, the new alert should refer to the 'old' name - // as now being "computer-2.local", not "computer.local" - usercompname[0] = 0; - userhostname[0] = 0; - lastcompname[0] = 0; - lasthostname[0] = 0; - update_idle_timer(); - unpause_idle_timer(); - } +{ + debug("entry"); + (void)responseFlags; // Unused + if (userNotification != gNotification) helplog(ASL_LEVEL_ERR, "NotificationCallBackDismissed: Wrong CFUserNotificationRef"); + if (gNotificationRLS) + { + // Caution: don't use CFRunLoopGetCurrent() here, because the currently executing thread may not be our "CFRunLoopRun" thread. + // We need to explicitly specify the desired CFRunLoop from which we want to remove this event source. + CFRunLoopRemoveSource(gRunLoop, gNotificationRLS, kCFRunLoopDefaultMode); + CFRelease(gNotificationRLS); + gNotificationRLS = NULL; + CFRelease(gNotification); + gNotification = NULL; + } + // By dismissing the alert, the user has conceptually acknowleged the rename. + // (e.g. the machine's name is now officially "computer-2.local", not "computer.local".) + // If we get *another* conflict, the new alert should refer to the 'old' name + // as now being "computer-2.local", not "computer.local" + usercompname[0] = 0; + userhostname[0] = 0; + lastcompname[0] = 0; + lasthostname[0] = 0; + update_idle_timer(); + unpause_idle_timer(); +} static void ShowNameConflictNotification(CFMutableArrayRef header, CFStringRef subtext) - { - CFMutableDictionaryRef dictionary = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - if (!dictionary) return; - - debug("entry"); - - CFDictionarySetValue(dictionary, kCFUserNotificationAlertHeaderKey, header); - CFDictionarySetValue(dictionary, kCFUserNotificationAlertMessageKey, subtext); - - CFURLRef urlRef = CFURLCreateWithFileSystemPath(NULL, CFSTR("/System/Library/CoreServices/mDNSResponder.bundle"), kCFURLPOSIXPathStyle, true); - if (urlRef) { CFDictionarySetValue(dictionary, kCFUserNotificationLocalizationURLKey, urlRef); CFRelease(urlRef); } - - if (gNotification) // If notification already on-screen, update it in place - CFUserNotificationUpdate(gNotification, 0, kCFUserNotificationCautionAlertLevel, dictionary); - else // else, we need to create it - { - SInt32 error; - gNotification = CFUserNotificationCreate(NULL, 0, kCFUserNotificationCautionAlertLevel, &error, dictionary); - if (!gNotification || error) { helplog(ASL_LEVEL_ERR, "ShowNameConflictNotification: CFUserNotificationRef: Error %d", error); return; } - gNotificationRLS = CFUserNotificationCreateRunLoopSource(NULL, gNotification, NotificationCallBackDismissed, 0); - if (!gNotificationRLS) { helplog(ASL_LEVEL_ERR,"ShowNameConflictNotification: RLS"); CFRelease(gNotification); gNotification = NULL; return; } - // Caution: don't use CFRunLoopGetCurrent() here, because the currently executing thread may not be our "CFRunLoopRun" thread. - // We need to explicitly specify the desired CFRunLoop to which we want to add this event source. - CFRunLoopAddSource(gRunLoop, gNotificationRLS, kCFRunLoopDefaultMode); - debug("gRunLoop=%p gNotification=%p gNotificationRLS=%p", gRunLoop, gNotification, gNotificationRLS); - pause_idle_timer(); - } - - CFRelease(dictionary); - } +{ + CFMutableDictionaryRef dictionary = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!dictionary) return; + + debug("entry"); + + CFDictionarySetValue(dictionary, kCFUserNotificationAlertHeaderKey, header); + CFDictionarySetValue(dictionary, kCFUserNotificationAlertMessageKey, subtext); + + CFURLRef urlRef = CFURLCreateWithFileSystemPath(NULL, CFSTR("/System/Library/CoreServices/mDNSResponder.bundle"), kCFURLPOSIXPathStyle, true); + if (urlRef) { CFDictionarySetValue(dictionary, kCFUserNotificationLocalizationURLKey, urlRef); CFRelease(urlRef); } + + if (gNotification) // If notification already on-screen, update it in place + CFUserNotificationUpdate(gNotification, 0, kCFUserNotificationCautionAlertLevel, dictionary); + else // else, we need to create it + { + SInt32 error; + gNotification = CFUserNotificationCreate(NULL, 0, kCFUserNotificationCautionAlertLevel, &error, dictionary); + if (!gNotification || error) { helplog(ASL_LEVEL_ERR, "ShowNameConflictNotification: CFUserNotificationRef: Error %d", error); return; } + gNotificationRLS = CFUserNotificationCreateRunLoopSource(NULL, gNotification, NotificationCallBackDismissed, 0); + if (!gNotificationRLS) { helplog(ASL_LEVEL_ERR,"ShowNameConflictNotification: RLS"); CFRelease(gNotification); gNotification = NULL; return; } + // Caution: don't use CFRunLoopGetCurrent() here, because the currently executing thread may not be our "CFRunLoopRun" thread. + // We need to explicitly specify the desired CFRunLoop to which we want to add this event source. + CFRunLoopAddSource(gRunLoop, gNotificationRLS, kCFRunLoopDefaultMode); + debug("gRunLoop=%p gNotification=%p gNotificationRLS=%p", gRunLoop, gNotification, gNotificationRLS); + pause_idle_timer(); + } + + CFRelease(dictionary); +} static CFMutableArrayRef GetHeader(const char* oldname, const char* newname, const CFStringRef msg, const char* suffix) - { - CFMutableArrayRef alertHeader = NULL; - - const CFStringRef cfoldname = CFStringCreateWithCString(NULL, oldname, kCFStringEncodingUTF8); - // NULL newname means we've given up trying to construct a name that doesn't conflict - const CFStringRef cfnewname = newname ? CFStringCreateWithCString(NULL, newname, kCFStringEncodingUTF8) : NULL; - // We tag a zero-width non-breaking space at the end of the literal text to guarantee that, no matter what - // arbitrary computer name the user may choose, this exact text (with zero-width non-breaking space added) - // can never be one that occurs in the Localizable.strings translation file. - if (!cfoldname) - helplog(ASL_LEVEL_ERR,"Could not construct CFStrings for old=%s", newname); - else if (newname && !cfnewname) - helplog(ASL_LEVEL_ERR,"Could not construct CFStrings for new=%s", newname); - else - { - const CFStringRef s1 = CFStringCreateWithFormat(NULL, NULL, CFS_Format, cfoldname, suffix); - const CFStringRef s2 = cfnewname ? CFStringCreateWithFormat(NULL, NULL, CFS_Format, cfnewname, suffix) : NULL; - - alertHeader = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - - if (!s1) - helplog(ASL_LEVEL_ERR, "Could not construct secondary CFString for old=%s", oldname); - else if (cfnewname && !s2) - helplog(ASL_LEVEL_ERR, "Could not construct secondary CFString for new=%s", newname); - else if (!alertHeader) - helplog(ASL_LEVEL_ERR, "Could not construct CFArray for notification"); - else - { - // Make sure someone is logged in. We don't want this popping up over the login window - uid_t uid; - gid_t gid; - CFStringRef userName = SCDynamicStoreCopyConsoleUser(NULL, &uid, &gid); - if (userName) - { - CFRelease(userName); - CFArrayAppendValue(alertHeader, msg); // Opening phrase of message, provided by caller - CFArrayAppendValue(alertHeader, CFS_OQ); CFArrayAppendValue(alertHeader, s1); CFArrayAppendValue(alertHeader, CFS_CQ); - CFArrayAppendValue(alertHeader, CFSTR(" is already in use on this network. ")); - if (s2) - { - CFArrayAppendValue(alertHeader, CFSTR("The name has been changed to ")); - CFArrayAppendValue(alertHeader, CFS_OQ); CFArrayAppendValue(alertHeader, s2); CFArrayAppendValue(alertHeader, CFS_CQ); - CFArrayAppendValue(alertHeader, CFSTR(".")); - } - else - CFArrayAppendValue(alertHeader, CFSTR("All attempts to find an available name by adding a number to the name were also unsuccessful.")); - } - } - if (s1) CFRelease(s1); - if (s2) CFRelease(s2); - } - if (cfoldname) CFRelease(cfoldname); - if (cfnewname) CFRelease(cfnewname); - - return alertHeader; - } +{ + CFMutableArrayRef alertHeader = NULL; + + const CFStringRef cfoldname = CFStringCreateWithCString(NULL, oldname, kCFStringEncodingUTF8); + // NULL newname means we've given up trying to construct a name that doesn't conflict + const CFStringRef cfnewname = newname ? CFStringCreateWithCString(NULL, newname, kCFStringEncodingUTF8) : NULL; + // We tag a zero-width non-breaking space at the end of the literal text to guarantee that, no matter what + // arbitrary computer name the user may choose, this exact text (with zero-width non-breaking space added) + // can never be one that occurs in the Localizable.strings translation file. + if (!cfoldname) + helplog(ASL_LEVEL_ERR,"Could not construct CFStrings for old=%s", newname); + else if (newname && !cfnewname) + helplog(ASL_LEVEL_ERR,"Could not construct CFStrings for new=%s", newname); + else + { + const CFStringRef s1 = CFStringCreateWithFormat(NULL, NULL, CFS_Format, cfoldname, suffix); + const CFStringRef s2 = cfnewname ? CFStringCreateWithFormat(NULL, NULL, CFS_Format, cfnewname, suffix) : NULL; + + alertHeader = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + + if (!s1) + helplog(ASL_LEVEL_ERR, "Could not construct secondary CFString for old=%s", oldname); + else if (cfnewname && !s2) + helplog(ASL_LEVEL_ERR, "Could not construct secondary CFString for new=%s", newname); + else if (!alertHeader) + helplog(ASL_LEVEL_ERR, "Could not construct CFArray for notification"); + else + { + // Make sure someone is logged in. We don't want this popping up over the login window + uid_t uid; + gid_t gid; + CFStringRef userName = SCDynamicStoreCopyConsoleUser(NULL, &uid, &gid); + if (userName) + { + CFRelease(userName); + CFArrayAppendValue(alertHeader, msg); // Opening phrase of message, provided by caller + CFArrayAppendValue(alertHeader, CFS_OQ); CFArrayAppendValue(alertHeader, s1); CFArrayAppendValue(alertHeader, CFS_CQ); + CFArrayAppendValue(alertHeader, CFSTR(" is already in use on this network. ")); + if (s2) + { + CFArrayAppendValue(alertHeader, CFSTR("The name has been changed to ")); + CFArrayAppendValue(alertHeader, CFS_OQ); CFArrayAppendValue(alertHeader, s2); CFArrayAppendValue(alertHeader, CFS_CQ); + CFArrayAppendValue(alertHeader, CFSTR(".")); + } + else + CFArrayAppendValue(alertHeader, CFSTR("All attempts to find an available name by adding a number to the name were also unsuccessful.")); + } + } + if (s1) CFRelease(s1); + if (s2) CFRelease(s2); + } + if (cfoldname) CFRelease(cfoldname); + if (cfnewname) CFRelease(cfnewname); + + return alertHeader; +} #endif /* ndef NO_CFUSERNOTIFICATION */ static void update_notification(void) - { +{ #ifndef NO_CFUSERNOTIFICATION - debug("entry ucn=%s, uhn=%s, lcn=%s, lhn=%s", usercompname, userhostname, lastcompname, lasthostname); - if (!CFS_OQ) - { - // Note: the "\xEF\xBB\xBF" byte sequence in the CFS_Format string is the UTF-8 encoding of the zero-width non-breaking space character. - // By appending this invisible character on the end of literal names, we ensure the these strings cannot inadvertently match any string - // in the localization file -- since we know for sure that none of our strings in the localization file contain the ZWNBS character. - // - // For languages that are written right to left, when we mix English (host names could be in english with brackets etc. and the - // rest in Arabic) we need unicode markups for proper formatting. The Unicode sequence 202C (UTF8 E2 80 AC), 200E (UTF8 E2 80 8E) and - // 202B (UTF8 E2 80 AB) helps with the formatting. See for more details. - CFS_OQ = CFStringCreateWithCString(NULL, "“\xE2\x80\xAB", kCFStringEncodingUTF8); - CFS_CQ = CFStringCreateWithCString(NULL, "\xE2\x80\xAC”", kCFStringEncodingUTF8); - CFS_Format = CFStringCreateWithCString(NULL, "%@%s\xEF\xBB\xBF\xE2\x80\x8E", kCFStringEncodingUTF8); - CFS_ComputerName = CFStringCreateWithCString(NULL, "The name of your computer ", kCFStringEncodingUTF8); - CFS_ComputerNameMsg = CFStringCreateWithCString(NULL, "To change the name of your computer, " - "open System Preferences and click Sharing, then type the name in the Computer Name field.", kCFStringEncodingUTF8); - CFS_LocalHostName = CFStringCreateWithCString(NULL, "This computer’s local hostname ", kCFStringEncodingUTF8); - CFS_LocalHostNameMsg = CFStringCreateWithCString(NULL, "To change the local hostname, " - "open System Preferences and click Sharing, then click “Edit” and type the name in the Local Hostname field.", kCFStringEncodingUTF8); - CFS_Problem = CFStringCreateWithCString(NULL, "This may indicate a problem with the local network. " - "Please inform your network administrator.", kCFStringEncodingUTF8); - } - - if (!usercompname[0] && !userhostname[0]) - { - if (gNotificationRLS) - { - debug("canceling notification %p", gNotification); - CFUserNotificationCancel(gNotification); - unpause_idle_timer(); - } - } - else - { - CFMutableArrayRef header = NULL; - CFStringRef* subtext = NULL; - if (userhostname[0] && !lasthostname[0]) // we've given up trying to construct a name that doesn't conflict - { - header = GetHeader(userhostname, NULL, CFS_LocalHostName, ".local"); - subtext = &CFS_Problem; - } - else if (usercompname[0]) - { - header = GetHeader(usercompname, lastcompname, CFS_ComputerName, ""); - subtext = &CFS_ComputerNameMsg; - } - else - { - header = GetHeader(userhostname, lasthostname, CFS_LocalHostName, ".local"); - subtext = &CFS_LocalHostNameMsg; - } - ShowNameConflictNotification(header, *subtext); - CFRelease(header); - } + debug("entry ucn=%s, uhn=%s, lcn=%s, lhn=%s", usercompname, userhostname, lastcompname, lasthostname); + if (!CFS_OQ) + { + // Note: the "\xEF\xBB\xBF" byte sequence in the CFS_Format string is the UTF-8 encoding of the zero-width non-breaking space character. + // By appending this invisible character on the end of literal names, we ensure the these strings cannot inadvertently match any string + // in the localization file -- since we know for sure that none of our strings in the localization file contain the ZWNBS character. + // + // For languages that are written right to left, when we mix English (host names could be in english with brackets etc. and the + // rest in Arabic) we need unicode markups for proper formatting. The Unicode sequence 202C (UTF8 E2 80 AC), 200E (UTF8 E2 80 8E) and + // 202B (UTF8 E2 80 AB) helps with the formatting. See for more details. + CFS_OQ = CFStringCreateWithCString(NULL, "“\xE2\x80\xAB", kCFStringEncodingUTF8); + CFS_CQ = CFStringCreateWithCString(NULL, "\xE2\x80\xAC”", kCFStringEncodingUTF8); + CFS_Format = CFStringCreateWithCString(NULL, "%@%s\xEF\xBB\xBF\xE2\x80\x8E", kCFStringEncodingUTF8); + CFS_ComputerName = CFStringCreateWithCString(NULL, "The name of your computer ", kCFStringEncodingUTF8); + CFS_ComputerNameMsg = CFStringCreateWithCString(NULL, "To change the name of your computer, " + "open System Preferences and click Sharing, then type the name in the Computer Name field.", kCFStringEncodingUTF8); + CFS_LocalHostName = CFStringCreateWithCString(NULL, "This computer’s local hostname ", kCFStringEncodingUTF8); + CFS_LocalHostNameMsg = CFStringCreateWithCString(NULL, "To change the local hostname, " + "open System Preferences and click Sharing, then click “Edit” and type the name in the Local Hostname field.", kCFStringEncodingUTF8); + CFS_Problem = CFStringCreateWithCString(NULL, "This may indicate a problem with the local network. " + "Please inform your network administrator.", kCFStringEncodingUTF8); + } + + if (!usercompname[0] && !userhostname[0]) + { + if (gNotificationRLS) + { + debug("canceling notification %p", gNotification); + CFUserNotificationCancel(gNotification); + unpause_idle_timer(); + } + } + else + { + CFMutableArrayRef header = NULL; + CFStringRef* subtext = NULL; + if (userhostname[0] && !lasthostname[0]) // we've given up trying to construct a name that doesn't conflict + { + header = GetHeader(userhostname, NULL, CFS_LocalHostName, ".local"); + subtext = &CFS_Problem; + } + else if (usercompname[0]) + { + header = GetHeader(usercompname, lastcompname, CFS_ComputerName, ""); + subtext = &CFS_ComputerNameMsg; + } + else + { + header = GetHeader(userhostname, lasthostname, CFS_LocalHostName, ".local"); + subtext = &CFS_LocalHostNameMsg; + } + ShowNameConflictNotification(header, *subtext); + CFRelease(header); + } #endif - } +} kern_return_t do_mDNSPreferencesSetName(__unused mach_port_t port, int key, const char* old, const char* new, audit_token_t token) - { - SCPreferencesRef session = NULL; - Boolean ok = FALSE; - Boolean locked = FALSE; - CFStringRef cfstr = NULL; - char* user = NULL; - char* last = NULL; - Boolean needUpdate = FALSE; - - debug("entry %s old=%s new=%s", key==kmDNSComputerName ? "ComputerName" : (key==kmDNSLocalHostName ? "LocalHostName" : "UNKNOWN"), old, new); - if (!authorized(&token)) goto fin; - - switch ((enum mDNSPreferencesSetNameKey)key) - { - case kmDNSComputerName: - user = usercompname; - last = lastcompname; - break; - case kmDNSLocalHostName: - user = userhostname; - last = lasthostname; - break; - default: - debug("unrecognized key: %d", key); - goto fin; - } - - if (!last) - { - helplog(ASL_LEVEL_ERR, "%s: no last ptr", __func__); - goto fin; - } - - if (!user) - { - helplog(ASL_LEVEL_ERR, "%s: no user ptr", __func__); - goto fin; - } - - if (0 == strncmp(old, new, MAX_DOMAIN_LABEL+1)) - { - // old and new are same means the config changed i.e, the user has set something in the preferences pane. - // This means the conflict has been resolved. We need to dismiss the dialogue. - if (last[0] && 0 != strncmp(last, new, MAX_DOMAIN_LABEL+1)) - { - last[0] = 0; - user[0] = 0; - needUpdate = TRUE; - } - goto fin; - } - else - { - // old and new are not same, this means there is a conflict. For the first conflict, we show - // the old value and the new value. For all subsequent conflicts, while the dialogue is still - // up, we do a real time update of the "new" value in the dialogue. That's why we update just - // "last" here and not "user". - if (strncmp(last, new, MAX_DOMAIN_LABEL+1)) - { - strncpy(last, new, MAX_DOMAIN_LABEL); - needUpdate = TRUE; - } - } - - // If we are not showing the dialogue, we need to remember the first "old" value so that - // we maintain the same through the lifetime of the dialogue. Subsequence conflicts don't - // update the "old" value. - if (!user[0]) - { - strncpy(user, old, MAX_DOMAIN_LABEL); - needUpdate = TRUE; - } - - if (!new[0]) // we've given up trying to construct a name that doesn't conflict - goto fin; - - cfstr = CFStringCreateWithCString(NULL, new, kCFStringEncodingUTF8); - - session = SCPreferencesCreate(NULL, CFSTR(kmDNSHelperServiceName), NULL); - - if (cfstr == NULL || session == NULL) - { - debug("SCPreferencesCreate failed"); - goto fin; - } - if (!SCPreferencesLock(session, 0)) - { - debug("lock failed"); - goto fin; - } - locked = TRUE; - - switch ((enum mDNSPreferencesSetNameKey)key) - { - case kmDNSComputerName: - { - // We want to write the new Computer Name to System Preferences, without disturbing the user-selected - // system-wide default character set used for things like AppleTalk NBP and NETBIOS service advertising. - // Note that this encoding is not used for the computer name, but since both are set by the same call, - // we need to take care to set the name without changing the character set. - CFStringEncoding encoding = kCFStringEncodingUTF8; - CFStringRef unused = SCDynamicStoreCopyComputerName(NULL, &encoding); - if (unused) { CFRelease(unused); unused = NULL; } - else encoding = kCFStringEncodingUTF8; - - ok = SCPreferencesSetComputerName(session, cfstr, encoding); - } - break; - case kmDNSLocalHostName: - ok = SCPreferencesSetLocalHostName(session, cfstr); - break; - default: - break; - } - - if (!ok || !SCPreferencesCommitChanges(session) || - !SCPreferencesApplyChanges(session)) - { - debug("SCPreferences update failed"); - goto fin; - } - debug("succeeded"); +{ + SCPreferencesRef session = NULL; + Boolean ok = FALSE; + Boolean locked = FALSE; + CFStringRef cfstr = NULL; + char* user = NULL; + char* last = NULL; + Boolean needUpdate = FALSE; + + debug("entry %s old=%s new=%s", key==kmDNSComputerName ? "ComputerName" : (key==kmDNSLocalHostName ? "LocalHostName" : "UNKNOWN"), old, new); + if (!authorized(&token)) goto fin; + + switch ((enum mDNSPreferencesSetNameKey)key) + { + case kmDNSComputerName: + user = usercompname; + last = lastcompname; + break; + case kmDNSLocalHostName: + user = userhostname; + last = lasthostname; + break; + default: + debug("unrecognized key: %d", key); + goto fin; + } + + if (!last) + { + helplog(ASL_LEVEL_ERR, "%s: no last ptr", __func__); + goto fin; + } + + if (!user) + { + helplog(ASL_LEVEL_ERR, "%s: no user ptr", __func__); + goto fin; + } + + if (0 == strncmp(old, new, MAX_DOMAIN_LABEL+1)) + { + // old and new are same means the config changed i.e, the user has set something in the preferences pane. + // This means the conflict has been resolved. We need to dismiss the dialogue. + if (last[0] && 0 != strncmp(last, new, MAX_DOMAIN_LABEL+1)) + { + last[0] = 0; + user[0] = 0; + needUpdate = TRUE; + } + goto fin; + } + else + { + // old and new are not same, this means there is a conflict. For the first conflict, we show + // the old value and the new value. For all subsequent conflicts, while the dialogue is still + // up, we do a real time update of the "new" value in the dialogue. That's why we update just + // "last" here and not "user". + if (strncmp(last, new, MAX_DOMAIN_LABEL+1)) + { + strncpy(last, new, MAX_DOMAIN_LABEL); + needUpdate = TRUE; + } + } + + // If we are not showing the dialogue, we need to remember the first "old" value so that + // we maintain the same through the lifetime of the dialogue. Subsequence conflicts don't + // update the "old" value. + if (!user[0]) + { + strncpy(user, old, MAX_DOMAIN_LABEL); + needUpdate = TRUE; + } + + if (!new[0]) // we've given up trying to construct a name that doesn't conflict + goto fin; + + cfstr = CFStringCreateWithCString(NULL, new, kCFStringEncodingUTF8); + + session = SCPreferencesCreate(NULL, CFSTR(kmDNSHelperServiceName), NULL); + + if (cfstr == NULL || session == NULL) + { + debug("SCPreferencesCreate failed"); + goto fin; + } + if (!SCPreferencesLock(session, 0)) + { + debug("lock failed"); + goto fin; + } + locked = TRUE; + + switch ((enum mDNSPreferencesSetNameKey)key) + { + case kmDNSComputerName: + { + // We want to write the new Computer Name to System Preferences, without disturbing the user-selected + // system-wide default character set used for things like AppleTalk NBP and NETBIOS service advertising. + // Note that this encoding is not used for the computer name, but since both are set by the same call, + // we need to take care to set the name without changing the character set. + CFStringEncoding encoding = kCFStringEncodingUTF8; + CFStringRef unused = SCDynamicStoreCopyComputerName(NULL, &encoding); + if (unused) { CFRelease(unused); unused = NULL; } + else encoding = kCFStringEncodingUTF8; + + ok = SCPreferencesSetComputerName(session, cfstr, encoding); + } + break; + case kmDNSLocalHostName: + ok = SCPreferencesSetLocalHostName(session, cfstr); + break; + default: + break; + } + + if (!ok || !SCPreferencesCommitChanges(session) || + !SCPreferencesApplyChanges(session)) + { + debug("SCPreferences update failed"); + goto fin; + } + debug("succeeded"); fin: - if (NULL != cfstr) - CFRelease(cfstr); - if (NULL != session) - { - if (locked) - SCPreferencesUnlock(session); - CFRelease(session); - } - update_idle_timer(); - if (needUpdate) update_notification(); - return KERN_SUCCESS; - } + if (NULL != cfstr) + CFRelease(cfstr); + if (NULL != session) + { + if (locked) + SCPreferencesUnlock(session); + CFRelease(session); + } + update_idle_timer(); + if (needUpdate) update_notification(); + return KERN_SUCCESS; +} enum DNSKeyFormat - { - formatNotDNSKey, formatDdnsTypeItem, formatDnsPrefixedServiceItem, formatBtmmPrefixedServiceItem - }; +{ + formatNotDNSKey, formatDdnsTypeItem, formatDnsPrefixedServiceItem, formatBtmmPrefixedServiceItem +}; // On Mac OS X on Intel, the four-character string seems to be stored backwards, at least sometimes. // I suspect some overenthusiastic inexperienced engineer said, "On Intel everything's backwards, @@ -780,437 +826,309 @@ static const char btmmprefix[] = "btmmdns:"; #ifndef NO_SECURITYFRAMEWORK static enum DNSKeyFormat getDNSKeyFormat(SecKeychainItemRef item, SecKeychainAttributeList **attributesp) - { - static UInt32 tags[4] = - { - kSecTypeItemAttr, kSecServiceItemAttr, kSecAccountItemAttr, kSecLabelItemAttr - }; - static SecKeychainAttributeInfo attributeInfo = - { - sizeof(tags)/sizeof(tags[0]), tags, NULL - }; - SecKeychainAttributeList *attributes = NULL; - enum DNSKeyFormat format; - Boolean malformed = FALSE; - OSStatus status = noErr; - int i = 0; - - *attributesp = NULL; - if (noErr != (status = SecKeychainItemCopyAttributesAndData(item, - &attributeInfo, NULL, &attributes, NULL, NULL))) - { - debug("SecKeychainItemCopyAttributesAndData %d - skipping", - status); - goto skip; - } - if (attributeInfo.count != attributes->count) - malformed = TRUE; - for (i = 0; !malformed && i < (int)attributeInfo.count; ++i) - if (attributeInfo.tag[i] != attributes->attr[i].tag) - malformed = TRUE; - if (malformed) - { - debug( - "malformed result from SecKeychainItemCopyAttributesAndData - skipping"); - goto skip; - } - - debug("entry (\"%.*s\", \"%.*s\", \"%.*s\")", - (int)attributes->attr[0].length, attributes->attr[0].data, - (int)attributes->attr[1].length, attributes->attr[1].data, - (int)attributes->attr[2].length, attributes->attr[2].data); - if (attributes->attr[1].length >= MAX_ESCAPED_DOMAIN_NAME + - sizeof(dnsprefix)-1) - { - debug("kSecServiceItemAttr too long (%u) - skipping", - (unsigned int)attributes->attr[1].length); - goto skip; - } - if (attributes->attr[2].length >= MAX_ESCAPED_DOMAIN_NAME) - { - debug("kSecAccountItemAttr too long (%u) - skipping", - (unsigned int)attributes->attr[2].length); - goto skip; - } - if (attributes->attr[1].length >= sizeof(dnsprefix)-1 && - 0 == strncasecmp(attributes->attr[1].data, dnsprefix, - sizeof(dnsprefix)-1)) - format = formatDnsPrefixedServiceItem; - else if (attributes->attr[1].length >= sizeof(btmmprefix)-1 && - 0 == strncasecmp(attributes->attr[1].data, btmmprefix, sizeof(btmmprefix)-1)) - format = formatBtmmPrefixedServiceItem; - else if (attributes->attr[0].length == sizeof(ddns)-1 && - 0 == strncasecmp(attributes->attr[0].data, ddns, sizeof(ddns)-1)) - format = formatDdnsTypeItem; - else if (attributes->attr[0].length == sizeof(ddnsrev)-1 && - 0 == strncasecmp(attributes->attr[0].data, ddnsrev, sizeof(ddnsrev)-1)) - format = formatDdnsTypeItem; - else - { - debug("uninterested in this entry"); - goto skip; - } - *attributesp = attributes; - debug("accepting this entry"); - return format; +{ + static UInt32 tags[4] = + { + kSecTypeItemAttr, kSecServiceItemAttr, kSecAccountItemAttr, kSecLabelItemAttr + }; + static SecKeychainAttributeInfo attributeInfo = + { + sizeof(tags)/sizeof(tags[0]), tags, NULL + }; + SecKeychainAttributeList *attributes = NULL; + enum DNSKeyFormat format; + Boolean malformed = FALSE; + OSStatus status = noErr; + int i = 0; + + *attributesp = NULL; + if (noErr != (status = SecKeychainItemCopyAttributesAndData(item, + &attributeInfo, NULL, &attributes, NULL, NULL))) + { + debug("SecKeychainItemCopyAttributesAndData %d - skipping", + status); + goto skip; + } + if (attributeInfo.count != attributes->count) + malformed = TRUE; + for (i = 0; !malformed && i < (int)attributeInfo.count; ++i) + if (attributeInfo.tag[i] != attributes->attr[i].tag) + malformed = TRUE; + if (malformed) + { + debug( + "malformed result from SecKeychainItemCopyAttributesAndData - skipping"); + goto skip; + } + + debug("entry (\"%.*s\", \"%.*s\", \"%.*s\")", + (int)attributes->attr[0].length, attributes->attr[0].data, + (int)attributes->attr[1].length, attributes->attr[1].data, + (int)attributes->attr[2].length, attributes->attr[2].data); + if (attributes->attr[1].length >= MAX_ESCAPED_DOMAIN_NAME + + sizeof(dnsprefix)-1) + { + debug("kSecServiceItemAttr too long (%u) - skipping", + (unsigned int)attributes->attr[1].length); + goto skip; + } + if (attributes->attr[2].length >= MAX_ESCAPED_DOMAIN_NAME) + { + debug("kSecAccountItemAttr too long (%u) - skipping", + (unsigned int)attributes->attr[2].length); + goto skip; + } + if (attributes->attr[1].length >= sizeof(dnsprefix)-1 && + 0 == strncasecmp(attributes->attr[1].data, dnsprefix, + sizeof(dnsprefix)-1)) + format = formatDnsPrefixedServiceItem; + else if (attributes->attr[1].length >= sizeof(btmmprefix)-1 && + 0 == strncasecmp(attributes->attr[1].data, btmmprefix, sizeof(btmmprefix)-1)) + format = formatBtmmPrefixedServiceItem; + else if (attributes->attr[0].length == sizeof(ddns)-1 && + 0 == strncasecmp(attributes->attr[0].data, ddns, sizeof(ddns)-1)) + format = formatDdnsTypeItem; + else if (attributes->attr[0].length == sizeof(ddnsrev)-1 && + 0 == strncasecmp(attributes->attr[0].data, ddnsrev, sizeof(ddnsrev)-1)) + format = formatDdnsTypeItem; + else + { + debug("uninterested in this entry"); + goto skip; + } + *attributesp = attributes; + debug("accepting this entry"); + return format; skip: - SecKeychainItemFreeAttributesAndData(attributes, NULL); - return formatNotDNSKey; - } + SecKeychainItemFreeAttributesAndData(attributes, NULL); + return formatNotDNSKey; +} // Insert the attributes as defined by mDNSKeyChainAttributes static CFPropertyListRef getKeychainItemInfo(SecKeychainItemRef item, - SecKeychainAttributeList *attributes, enum DNSKeyFormat format) - { - CFMutableArrayRef entry = NULL; - CFDataRef data = NULL; - OSStatus status = noErr; - UInt32 keylen = 0; - void *keyp = 0; - - if (NULL == (entry = CFArrayCreateMutable(NULL, 0, - &kCFTypeArrayCallBacks))) - { - debug("CFArrayCreateMutable failed"); - goto error; - } - - // Insert the Account attribute (kmDNSKcWhere) - switch ((enum DNSKeyFormat)format) - { - case formatDdnsTypeItem: - data = CFDataCreate(kCFAllocatorDefault, - attributes->attr[1].data, attributes->attr[1].length); - break; - case formatDnsPrefixedServiceItem: - case formatBtmmPrefixedServiceItem: - data = CFDataCreate(kCFAllocatorDefault, - attributes->attr[1].data, attributes->attr[1].length); - break; - default: - assert("unknown DNSKeyFormat value"); - break; - } - if (NULL == data) - { - debug("CFDataCreate for attr[1] failed"); - goto error; - } - CFArrayAppendValue(entry, data); - CFRelease(data); - - // Insert the Where attribute (kmDNSKcAccount) - if (NULL == (data = CFDataCreate(kCFAllocatorDefault, - attributes->attr[2].data, attributes->attr[2].length))) - { - debug("CFDataCreate for attr[2] failed"); - goto error; - } - CFArrayAppendValue(entry, data); - CFRelease(data); - - // Insert the Key attribute (kmDNSKcKey) - if (noErr != (status = SecKeychainItemCopyAttributesAndData(item, NULL, - NULL, NULL, &keylen, &keyp))) - { - debug("could not retrieve key for \"%.*s\": %d", - (int)attributes->attr[1].length, attributes->attr[1].data, - status); - goto error; - } - data = CFDataCreate(kCFAllocatorDefault, keyp, keylen); - SecKeychainItemFreeAttributesAndData(NULL, keyp); - if (NULL == data) - { - debug("CFDataCreate for keyp failed"); - goto error; - } - CFArrayAppendValue(entry, data); - CFRelease(data); - - // Insert the Name attribute (kmDNSKcName) - if (NULL == (data = CFDataCreate(kCFAllocatorDefault, - attributes->attr[3].data, attributes->attr[3].length))) - { - debug("CFDataCreate for attr[3] failed"); - goto error; - } - CFArrayAppendValue(entry, data); - CFRelease(data); - return entry; + SecKeychainAttributeList *attributes, enum DNSKeyFormat format) +{ + CFMutableArrayRef entry = NULL; + CFDataRef data = NULL; + OSStatus status = noErr; + UInt32 keylen = 0; + void *keyp = 0; + + if (NULL == (entry = CFArrayCreateMutable(NULL, 0, + &kCFTypeArrayCallBacks))) + { + debug("CFArrayCreateMutable failed"); + goto error; + } + + // Insert the Account attribute (kmDNSKcWhere) + switch ((enum DNSKeyFormat)format) + { + case formatDdnsTypeItem: + data = CFDataCreate(kCFAllocatorDefault, + attributes->attr[1].data, attributes->attr[1].length); + break; + case formatDnsPrefixedServiceItem: + case formatBtmmPrefixedServiceItem: + data = CFDataCreate(kCFAllocatorDefault, + attributes->attr[1].data, attributes->attr[1].length); + break; + default: + assert("unknown DNSKeyFormat value"); + break; + } + if (NULL == data) + { + debug("CFDataCreate for attr[1] failed"); + goto error; + } + CFArrayAppendValue(entry, data); + CFRelease(data); + + // Insert the Where attribute (kmDNSKcAccount) + if (NULL == (data = CFDataCreate(kCFAllocatorDefault, + attributes->attr[2].data, attributes->attr[2].length))) + { + debug("CFDataCreate for attr[2] failed"); + goto error; + } + CFArrayAppendValue(entry, data); + CFRelease(data); + + // Insert the Key attribute (kmDNSKcKey) + if (noErr != (status = SecKeychainItemCopyAttributesAndData(item, NULL, + NULL, NULL, &keylen, &keyp))) + { + debug("could not retrieve key for \"%.*s\": %d", + (int)attributes->attr[1].length, attributes->attr[1].data, + status); + goto error; + } + data = CFDataCreate(kCFAllocatorDefault, keyp, keylen); + SecKeychainItemFreeAttributesAndData(NULL, keyp); + if (NULL == data) + { + debug("CFDataCreate for keyp failed"); + goto error; + } + CFArrayAppendValue(entry, data); + CFRelease(data); + + // Insert the Name attribute (kmDNSKcName) + if (NULL == (data = CFDataCreate(kCFAllocatorDefault, + attributes->attr[3].data, attributes->attr[3].length))) + { + debug("CFDataCreate for attr[3] failed"); + goto error; + } + CFArrayAppendValue(entry, data); + CFRelease(data); + return entry; error: - if (NULL != entry) - CFRelease(entry); - return NULL; - } + if (NULL != entry) + CFRelease(entry); + return NULL; +} #endif kern_return_t do_mDNSKeychainGetSecrets(__unused mach_port_t port, __unused unsigned int *numsecrets, - __unused vm_offset_t *secrets, __unused mach_msg_type_number_t *secretsCnt, __unused int *err, - __unused audit_token_t token) - { + __unused vm_offset_t *secrets, __unused mach_msg_type_number_t *secretsCnt, __unused int *err, + __unused audit_token_t token) +{ #ifndef NO_SECURITYFRAMEWORK - CFWriteStreamRef stream = NULL; - CFDataRef result = NULL; - CFPropertyListRef entry = NULL; - CFMutableArrayRef keys = NULL; - SecKeychainRef skc = NULL; - SecKeychainItemRef item = NULL; - SecKeychainSearchRef search = NULL; - SecKeychainAttributeList *attributes = NULL; - enum DNSKeyFormat format; - OSStatus status = 0; - - debug("entry"); - *err = 0; - *numsecrets = 0; - *secrets = (vm_offset_t)NULL; - if (!authorized(&token)) - { - *err = kmDNSHelperNotAuthorized; - goto fin; - } - if (NULL == (keys = CFArrayCreateMutable(NULL, 0, - &kCFTypeArrayCallBacks))) - { - debug("CFArrayCreateMutable failed"); - *err = kmDNSHelperCreationFailed; - goto fin; - } - if (noErr != (status = SecKeychainCopyDefault(&skc))) - { - *err = kmDNSHelperKeychainCopyDefaultFailed; - goto fin; - } - if (noErr != (status = SecKeychainSearchCreateFromAttributes(skc, kSecGenericPasswordItemClass, NULL, &search))) - { - *err = kmDNSHelperKeychainSearchCreationFailed; - goto fin; - } - for (status = SecKeychainSearchCopyNext(search, &item); - noErr == status; - status = SecKeychainSearchCopyNext(search, &item)) - { - if (formatNotDNSKey != (format = getDNSKeyFormat(item, - &attributes)) && - NULL != (entry = getKeychainItemInfo(item, attributes, - format))) - { - CFArrayAppendValue(keys, entry); - CFRelease(entry); - } - SecKeychainItemFreeAttributesAndData(attributes, NULL); - CFRelease(item); - } - if (errSecItemNotFound != status) - helplog(ASL_LEVEL_ERR, "%s: SecKeychainSearchCopyNext failed: %d", - __func__, status); - if (NULL == (stream = - CFWriteStreamCreateWithAllocatedBuffers(kCFAllocatorDefault, - kCFAllocatorDefault))) - { - *err = kmDNSHelperCreationFailed; - debug("CFWriteStreamCreateWithAllocatedBuffers failed"); - goto fin; - } - CFWriteStreamOpen(stream); - if (0 == CFPropertyListWriteToStream(keys, stream, - kCFPropertyListBinaryFormat_v1_0, NULL)) - { - *err = kmDNSHelperPListWriteFailed; - debug("CFPropertyListWriteToStream failed"); - goto fin; - } - result = CFWriteStreamCopyProperty(stream, - kCFStreamPropertyDataWritten); - if (KERN_SUCCESS != vm_allocate(mach_task_self(), secrets, - CFDataGetLength(result), VM_FLAGS_ANYWHERE)) - { - *err = kmDNSHelperCreationFailed; - debug("vm_allocate failed"); - goto fin; - } - CFDataGetBytes(result, CFRangeMake(0, CFDataGetLength(result)), - (void *)*secrets); - *secretsCnt = CFDataGetLength(result); - *numsecrets = CFArrayGetCount(keys); - debug("succeeded"); + CFWriteStreamRef stream = NULL; + CFDataRef result = NULL; + CFPropertyListRef entry = NULL; + CFMutableArrayRef keys = NULL; + SecKeychainRef skc = NULL; + SecKeychainItemRef item = NULL; + SecKeychainSearchRef search = NULL; + SecKeychainAttributeList *attributes = NULL; + enum DNSKeyFormat format; + OSStatus status = 0; + + debug("entry"); + *err = 0; + *numsecrets = 0; + *secrets = (vm_offset_t)NULL; + if (!authorized(&token)) + { + *err = kmDNSHelperNotAuthorized; + goto fin; + } + if (NULL == (keys = CFArrayCreateMutable(NULL, 0, + &kCFTypeArrayCallBacks))) + { + debug("CFArrayCreateMutable failed"); + *err = kmDNSHelperCreationFailed; + goto fin; + } + if (noErr != (status = SecKeychainCopyDefault(&skc))) + { + *err = kmDNSHelperKeychainCopyDefaultFailed; + goto fin; + } + if (noErr != (status = SecKeychainSearchCreateFromAttributes(skc, kSecGenericPasswordItemClass, NULL, &search))) + { + *err = kmDNSHelperKeychainSearchCreationFailed; + goto fin; + } + for (status = SecKeychainSearchCopyNext(search, &item); + noErr == status; + status = SecKeychainSearchCopyNext(search, &item)) + { + if (formatNotDNSKey != (format = getDNSKeyFormat(item, + &attributes)) && + NULL != (entry = getKeychainItemInfo(item, attributes, + format))) + { + CFArrayAppendValue(keys, entry); + CFRelease(entry); + } + SecKeychainItemFreeAttributesAndData(attributes, NULL); + CFRelease(item); + } + if (errSecItemNotFound != status) + helplog(ASL_LEVEL_ERR, "%s: SecKeychainSearchCopyNext failed: %d", + __func__, status); + if (NULL == (stream = + CFWriteStreamCreateWithAllocatedBuffers(kCFAllocatorDefault, + kCFAllocatorDefault))) + { + *err = kmDNSHelperCreationFailed; + debug("CFWriteStreamCreateWithAllocatedBuffers failed"); + goto fin; + } + CFWriteStreamOpen(stream); + if (0 == CFPropertyListWriteToStream(keys, stream, + kCFPropertyListBinaryFormat_v1_0, NULL)) + { + *err = kmDNSHelperPListWriteFailed; + debug("CFPropertyListWriteToStream failed"); + goto fin; + } + result = CFWriteStreamCopyProperty(stream, + kCFStreamPropertyDataWritten); + if (KERN_SUCCESS != vm_allocate(mach_task_self(), secrets, + CFDataGetLength(result), VM_FLAGS_ANYWHERE)) + { + *err = kmDNSHelperCreationFailed; + debug("vm_allocate failed"); + goto fin; + } + CFDataGetBytes(result, CFRangeMake(0, CFDataGetLength(result)), + (void *)*secrets); + *secretsCnt = CFDataGetLength(result); + *numsecrets = CFArrayGetCount(keys); + debug("succeeded"); fin: - debug("returning %u secrets", *numsecrets); - if (NULL != stream) - { - CFWriteStreamClose(stream); - CFRelease(stream); - } - if (NULL != result) - CFRelease(result); - if (NULL != keys) - CFRelease(keys); - if (NULL != search) - CFRelease(search); - if (NULL != skc) - CFRelease(skc); - update_idle_timer(); - return KERN_SUCCESS; + debug("returning %u secrets", *numsecrets); + if (NULL != stream) + { + CFWriteStreamClose(stream); + CFRelease(stream); + } + if (NULL != result) + CFRelease(result); + if (NULL != keys) + CFRelease(keys); + if (NULL != search) + CFRelease(search); + if (NULL != skc) + CFRelease(skc); + update_idle_timer(); + return KERN_SUCCESS; #else - return KERN_FAILURE; + return KERN_FAILURE; #endif - } +} #ifndef MDNS_NO_IPSEC typedef enum _mDNSTunnelPolicyWhich - { - kmDNSTunnelPolicySetup, - kmDNSTunnelPolicyTeardown, - kmDNSTunnelPolicyGenerate - } mDNSTunnelPolicyWhich; +{ + kmDNSTunnelPolicySetup, + kmDNSTunnelPolicyTeardown, + kmDNSTunnelPolicyGenerate +} mDNSTunnelPolicyWhich; // For kmDNSTunnelPolicySetup, you can setup IPv6-in-IPv6 tunnel or IPv6-in-IPv4 tunnel // kmDNSNoTunnel is used for other Policy types typedef enum _mDNSTunnelType - { - kmDNSNoTunnel, - kmDNSIPv6IPv4Tunnel, - kmDNSIPv6IPv6Tunnel - } mDNSTunnelType; +{ + kmDNSNoTunnel, + kmDNSIPv6IPv4Tunnel, + kmDNSIPv6IPv6Tunnel +} mDNSTunnelType; static const uint8_t kWholeV6Mask = 128; -static const uint8_t kZeroV6Mask = 0; - -static int -doTunnelPolicy(mDNSTunnelPolicyWhich which, mDNSTunnelType type, - v6addr_t loc_inner, uint8_t loc_bits, - v4addr_t loc_outer, uint16_t loc_port, - v6addr_t rmt_inner, uint8_t rmt_bits, - v4addr_t rmt_outer, uint16_t rmt_port, - v6addr_t loc_outer6, v6addr_t rmt_outer6); - -static int -aliasTunnelAddress(v6addr_t address) - { - struct in6_aliasreq ifra_in6; - int err = 0; - int s = -1; - - if (0 > (s = socket(AF_INET6, SOCK_DGRAM, 0))) - { - helplog(ASL_LEVEL_ERR, "socket(AF_INET6, ...) failed: %s", - strerror(errno)); - err = kmDNSHelperDatagramSocketCreationFailed; - goto fin; - } - memset(&ifra_in6, 0, sizeof(ifra_in6)); - strlcpy(ifra_in6.ifra_name, kTunnelAddressInterface, - sizeof(ifra_in6.ifra_name)); - ifra_in6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; - ifra_in6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; - - ifra_in6.ifra_addr.sin6_family = AF_INET6; - ifra_in6.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); - memcpy(&(ifra_in6.ifra_addr.sin6_addr), address, - sizeof(ifra_in6.ifra_addr.sin6_addr)); - - ifra_in6.ifra_prefixmask.sin6_family = AF_INET6; - ifra_in6.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); - memset(&(ifra_in6.ifra_prefixmask.sin6_addr), 0xFF, - sizeof(ifra_in6.ifra_prefixmask.sin6_addr)); - - if (0 > ioctl(s, SIOCAIFADDR_IN6, &ifra_in6)) - { - helplog(ASL_LEVEL_ERR, - "ioctl(..., SIOCAIFADDR_IN6, ...) failed: %s", - strerror(errno)); - err = kmDNSHelperInterfaceCreationFailed; - goto fin; - } - - v6addr_t zero = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; - err = doTunnelPolicy(kmDNSTunnelPolicyGenerate, kmDNSNoTunnel, - address, kWholeV6Mask, NULL, 0, - zero, kZeroV6Mask, NULL, 0, NULL, NULL); -fin: - if (0 <= s) - close(s); - return err; - } - -static int -unaliasTunnelAddress(v6addr_t address) - { - struct in6_ifreq ifr; - int err = 0; - int s = -1; - - if (0 > (s = socket(AF_INET6, SOCK_DGRAM, 0))) - { - helplog(ASL_LEVEL_ERR, "socket(AF_INET6, ...) failed: %s", - strerror(errno)); - err = kmDNSHelperDatagramSocketCreationFailed; - goto fin; - } - memset(&ifr, 0, sizeof(ifr)); - strlcpy(ifr.ifr_name, kTunnelAddressInterface, sizeof(ifr.ifr_name)); - ifr.ifr_ifru.ifru_addr.sin6_family = AF_INET6; - ifr.ifr_ifru.ifru_addr.sin6_len = sizeof(struct sockaddr_in6); - memcpy(&(ifr.ifr_ifru.ifru_addr.sin6_addr), address, - sizeof(ifr.ifr_ifru.ifru_addr.sin6_addr)); - - if (0 > ioctl(s, SIOCDIFADDR_IN6, &ifr)) - { - helplog(ASL_LEVEL_ERR, - "ioctl(..., SIOCDIFADDR_IN6, ...) failed: %s", - strerror(errno)); - err = kmDNSHelperInterfaceDeletionFailed; - goto fin; - } - - v6addr_t zero = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - err = doTunnelPolicy(kmDNSTunnelPolicyTeardown, kmDNSNoTunnel, - address, kWholeV6Mask, NULL, 0, - zero, kZeroV6Mask, NULL, 0, NULL, NULL); - -fin: - if (0 <= s) - close(s); - return err; - } #endif /* ifndef MDNS_NO_IPSEC */ -int -do_mDNSAutoTunnelInterfaceUpDown(__unused mach_port_t port, int updown, - v6addr_t address, audit_token_t token) - { -#ifndef MDNS_NO_IPSEC - debug("entry"); - if (!authorized(&token)) goto fin; - - switch ((enum mDNSUpDown)updown) - { - case kmDNSUp: - aliasTunnelAddress(address); - break; - case kmDNSDown: - unaliasTunnelAddress(address); - break; - default: - goto fin; - } - debug("succeeded"); - -fin: -#else - (void)port; (void)updown; (void)address; (void)token; -#endif - update_idle_timer(); - return KERN_SUCCESS; - } - #ifndef MDNS_NO_IPSEC static const char g_racoon_config_dir[] = "/var/run/racoon/"; @@ -1225,560 +1143,560 @@ CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey; // Major version 9 is 10.5.x (Leopard) // Major version 10 is 10.6.x (SnowLeopard) static int MacOSXSystemBuildNumber(char* letter_out, int* minor_out) - { - int major = 0, minor = 0; - char letter = 0, buildver[256]=""; - CFDictionaryRef vers = _CFCopySystemVersionDictionary(); - if (vers) - { - CFStringRef cfbuildver = CFDictionaryGetValue(vers, _kCFSystemVersionBuildVersionKey); - if (cfbuildver) CFStringGetCString(cfbuildver, buildver, sizeof(buildver), kCFStringEncodingUTF8); - sscanf(buildver, "%d%c%d", &major, &letter, &minor); - CFRelease(vers); - } - else - helplog(ASL_LEVEL_NOTICE, "_CFCopySystemVersionDictionary failed"); - - if (!major) { major=10; letter = 'A'; minor = 190; helplog(ASL_LEVEL_NOTICE, "Note: No Major Build Version number found; assuming 10A190"); } - if (letter_out) *letter_out = letter; - if (minor_out) *minor_out = minor; - return(major); - } - +{ + int major = 0, minor = 0; + char letter = 0, buildver[256]=""; + CFDictionaryRef vers = _CFCopySystemVersionDictionary(); + if (vers) + { + CFStringRef cfbuildver = CFDictionaryGetValue(vers, _kCFSystemVersionBuildVersionKey); + if (cfbuildver) CFStringGetCString(cfbuildver, buildver, sizeof(buildver), kCFStringEncodingUTF8); + sscanf(buildver, "%d%c%d", &major, &letter, &minor); + CFRelease(vers); + } + else + helplog(ASL_LEVEL_NOTICE, "_CFCopySystemVersionDictionary failed"); + + if (!major) { major=10; letter = 'A'; minor = 190; helplog(ASL_LEVEL_NOTICE, "Note: No Major Build Version number found; assuming 10A190"); } + if (letter_out) *letter_out = letter; + if (minor_out) *minor_out = minor; + return(major); +} + static int UseOldRacoon() - { - static int g_oldRacoon = -1; - - if (g_oldRacoon == -1) - { - char letter = 0; - int minor = 0; - g_oldRacoon = (MacOSXSystemBuildNumber(&letter, &minor) < 10); - debug("%s", g_oldRacoon?"old":"new"); - } - - return g_oldRacoon; - } - +{ + static int g_oldRacoon = -1; + + if (g_oldRacoon == -1) + { + char letter = 0; + int minor = 0; + g_oldRacoon = (MacOSXSystemBuildNumber(&letter, &minor) < 10); + debug("%s", g_oldRacoon ? "old" : "new"); + } + + return g_oldRacoon; +} + static int RacoonSignal() - { - return UseOldRacoon() ? SIGHUP : SIGUSR1; - } - +{ + return UseOldRacoon() ? SIGHUP : SIGUSR1; +} + static const char* GetRacoonConfigDir() - { - return UseOldRacoon() ? g_racoon_config_dir_old : g_racoon_config_dir; - } - +{ + return UseOldRacoon() ? g_racoon_config_dir_old : g_racoon_config_dir; +} + static const char* GetOldRacoonConfigDir() - { - return UseOldRacoon() ? NULL : g_racoon_config_dir_old; - } - +{ + return UseOldRacoon() ? NULL : g_racoon_config_dir_old; +} + static const char racoon_config_file[] = "anonymous.conf"; static const char racoon_config_file_orig[] = "anonymous.conf.orig"; static const char configHeader[] = "# BackToMyMac\n"; static int IsFamiliarRacoonConfiguration(const char* racoon_config_path) - { - int fd = open(racoon_config_path, O_RDONLY); - debug("entry %s", racoon_config_path); - if (0 > fd) - { - helplog(ASL_LEVEL_NOTICE, "open \"%s\" failed: %s", racoon_config_path, strerror(errno)); - return 0; - } - else - { - char header[sizeof(configHeader)] = {0}; - ssize_t bytesRead = read(fd, header, sizeof(header)-1); - close(fd); - if (bytesRead != sizeof(header)-1) return 0; - return (0 == memcmp(header, configHeader, sizeof(header)-1)); - } - } +{ + int fd = open(racoon_config_path, O_RDONLY); + debug("entry %s", racoon_config_path); + if (0 > fd) + { + helplog(ASL_LEVEL_NOTICE, "open \"%s\" failed: %s", racoon_config_path, strerror(errno)); + return 0; + } + else + { + char header[sizeof(configHeader)] = {0}; + ssize_t bytesRead = read(fd, header, sizeof(header)-1); + close(fd); + if (bytesRead != sizeof(header)-1) return 0; + return (0 == memcmp(header, configHeader, sizeof(header)-1)); + } +} static void revertAnonymousRacoonConfiguration(const char* dir) - { - if (!dir) return; - - debug("entry %s", dir); - - char racoon_config_path[64]; - strlcpy(racoon_config_path, dir, sizeof(racoon_config_path)); - strlcat(racoon_config_path, racoon_config_file, sizeof(racoon_config_path)); - - struct stat s; - int ret = stat(racoon_config_path, &s); - debug("stat(%s): %d errno=%d", racoon_config_path, ret, errno); - if (ret == 0) - { - if (IsFamiliarRacoonConfiguration(racoon_config_path)) - { - helplog(ASL_LEVEL_INFO, "\"%s\" looks familiar, unlinking", racoon_config_path); - unlink(racoon_config_path); - } - else - { - helplog(ASL_LEVEL_NOTICE, "\"%s\" does not look familiar, leaving in place", racoon_config_path); - return; - } - } - else if (errno != ENOENT) - { - helplog(ASL_LEVEL_NOTICE, "stat failed for \"%s\", leaving in place: %s", racoon_config_path, strerror(errno)); - return; - } - - char racoon_config_path_orig[64]; - strlcpy(racoon_config_path_orig, dir, sizeof(racoon_config_path_orig)); - strlcat(racoon_config_path_orig, racoon_config_file_orig, sizeof(racoon_config_path_orig)); - - ret = stat(racoon_config_path_orig, &s); - debug("stat(%s): %d errno=%d", racoon_config_path_orig, ret, errno); - if (ret == 0) - { - if (0 > rename(racoon_config_path_orig, racoon_config_path)) - helplog(ASL_LEVEL_NOTICE, "rename \"%s\" \"%s\" failed: %s", racoon_config_path_orig, racoon_config_path, strerror(errno)); - else - debug("reverted \"%s\" to \"%s\"", racoon_config_path_orig, racoon_config_path); - } - else if (errno != ENOENT) - { - helplog(ASL_LEVEL_NOTICE, "stat failed for \"%s\", leaving in place: %s", racoon_config_path_orig, strerror(errno)); - return; - } - } +{ + if (!dir) return; + + debug("entry %s", dir); + + char racoon_config_path[64]; + strlcpy(racoon_config_path, dir, sizeof(racoon_config_path)); + strlcat(racoon_config_path, racoon_config_file, sizeof(racoon_config_path)); + + struct stat s; + int ret = stat(racoon_config_path, &s); + debug("stat(%s): %d errno=%d", racoon_config_path, ret, errno); + if (ret == 0) + { + if (IsFamiliarRacoonConfiguration(racoon_config_path)) + { + helplog(ASL_LEVEL_INFO, "\"%s\" looks familiar, unlinking", racoon_config_path); + unlink(racoon_config_path); + } + else + { + helplog(ASL_LEVEL_NOTICE, "\"%s\" does not look familiar, leaving in place", racoon_config_path); + return; + } + } + else if (errno != ENOENT) + { + helplog(ASL_LEVEL_NOTICE, "stat failed for \"%s\", leaving in place: %s", racoon_config_path, strerror(errno)); + return; + } + + char racoon_config_path_orig[64]; + strlcpy(racoon_config_path_orig, dir, sizeof(racoon_config_path_orig)); + strlcat(racoon_config_path_orig, racoon_config_file_orig, sizeof(racoon_config_path_orig)); + + ret = stat(racoon_config_path_orig, &s); + debug("stat(%s): %d errno=%d", racoon_config_path_orig, ret, errno); + if (ret == 0) + { + if (0 > rename(racoon_config_path_orig, racoon_config_path)) + helplog(ASL_LEVEL_NOTICE, "rename \"%s\" \"%s\" failed: %s", racoon_config_path_orig, racoon_config_path, strerror(errno)); + else + debug("reverted \"%s\" to \"%s\"", racoon_config_path_orig, racoon_config_path); + } + else if (errno != ENOENT) + { + helplog(ASL_LEVEL_NOTICE, "stat failed for \"%s\", leaving in place: %s", racoon_config_path_orig, strerror(errno)); + return; + } +} static void moveAsideAnonymousRacoonConfiguration(const char* dir) - { - if (!dir) return; - - debug("entry %s", dir); - - char racoon_config_path[64]; - strlcpy(racoon_config_path, dir, sizeof(racoon_config_path)); - strlcat(racoon_config_path, racoon_config_file, sizeof(racoon_config_path)); - - struct stat s; - int ret = stat(racoon_config_path, &s); - if (ret == 0) - { - if (IsFamiliarRacoonConfiguration(racoon_config_path)) - { - helplog(ASL_LEVEL_INFO, "\"%s\" looks familiar, unlinking", racoon_config_path); - unlink(racoon_config_path); - } - else - { - char racoon_config_path_orig[64]; - strlcpy(racoon_config_path_orig, dir, sizeof(racoon_config_path_orig)); - strlcat(racoon_config_path_orig, racoon_config_file_orig, sizeof(racoon_config_path_orig)); - if (0 > rename(racoon_config_path, racoon_config_path_orig)) // If we didn't write it, move it to the side so it can be reverted later - helplog(ASL_LEVEL_NOTICE, "rename \"%s\" to \"%s\" failed: %s", racoon_config_path, racoon_config_path_orig, strerror(errno)); - else - debug("successfully renamed \"%s\" to \"%s\"", racoon_config_path, racoon_config_path_orig); - } - } - else if (errno != ENOENT) - { - helplog(ASL_LEVEL_NOTICE, "stat failed for \"%s\", leaving in place: %s", racoon_config_path, strerror(errno)); - return; - } - } +{ + if (!dir) return; + + debug("entry %s", dir); + + char racoon_config_path[64]; + strlcpy(racoon_config_path, dir, sizeof(racoon_config_path)); + strlcat(racoon_config_path, racoon_config_file, sizeof(racoon_config_path)); + + struct stat s; + int ret = stat(racoon_config_path, &s); + if (ret == 0) + { + if (IsFamiliarRacoonConfiguration(racoon_config_path)) + { + helplog(ASL_LEVEL_INFO, "\"%s\" looks familiar, unlinking", racoon_config_path); + unlink(racoon_config_path); + } + else + { + char racoon_config_path_orig[64]; + strlcpy(racoon_config_path_orig, dir, sizeof(racoon_config_path_orig)); + strlcat(racoon_config_path_orig, racoon_config_file_orig, sizeof(racoon_config_path_orig)); + if (0 > rename(racoon_config_path, racoon_config_path_orig)) // If we didn't write it, move it to the side so it can be reverted later + helplog(ASL_LEVEL_NOTICE, "rename \"%s\" to \"%s\" failed: %s", racoon_config_path, racoon_config_path_orig, strerror(errno)); + else + debug("successfully renamed \"%s\" to \"%s\"", racoon_config_path, racoon_config_path_orig); + } + } + else if (errno != ENOENT) + { + helplog(ASL_LEVEL_NOTICE, "stat failed for \"%s\", leaving in place: %s", racoon_config_path, strerror(errno)); + return; + } +} static int ensureExistenceOfRacoonConfigDir(const char* const racoon_config_dir) - { - struct stat s; - int ret = stat(racoon_config_dir, &s); - if (ret != 0) - { - if (errno != ENOENT) - { - helplog(ASL_LEVEL_ERR, "stat of \"%s\" failed (%d): %s", - racoon_config_dir, ret, strerror(errno)); - return -1; - } - else - { - ret = mkdir(racoon_config_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); - if (ret != 0) - { - helplog(ASL_LEVEL_ERR, "mkdir \"%s\" failed: %s", - racoon_config_dir, strerror(errno)); - return -1; - } - else - helplog(ASL_LEVEL_INFO, "created directory \"%s\"", racoon_config_dir); - } - } - else if (!(s.st_mode & S_IFDIR)) - { - helplog(ASL_LEVEL_ERR, "\"%s\" is not a directory!", - racoon_config_dir); - return -1; - } - - return 0; - } +{ + struct stat s; + int ret = stat(racoon_config_dir, &s); + if (ret != 0) + { + if (errno != ENOENT) + { + helplog(ASL_LEVEL_ERR, "stat of \"%s\" failed (%d): %s", + racoon_config_dir, ret, strerror(errno)); + return -1; + } + else + { + ret = mkdir(racoon_config_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + if (ret != 0) + { + helplog(ASL_LEVEL_ERR, "mkdir \"%s\" failed: %s", + racoon_config_dir, strerror(errno)); + return -1; + } + else + helplog(ASL_LEVEL_INFO, "created directory \"%s\"", racoon_config_dir); + } + } + else if (!(s.st_mode & S_IFDIR)) + { + helplog(ASL_LEVEL_ERR, "\"%s\" is not a directory!", + racoon_config_dir); + return -1; + } + + return 0; +} static int createAnonymousRacoonConfiguration(const char *fqdn) - { - static const char config1[] = - "remote anonymous {\n" - " exchange_mode aggressive;\n" - " doi ipsec_doi;\n" - " situation identity_only;\n" - " verify_identifier off;\n" - " generate_policy on;\n" - " shared_secret keychain_by_id \""; - static const char config2[] = - "\";\n" - " nonce_size 16;\n" - " lifetime time 15 min;\n" - " initial_contact on;\n" - " support_proxy on;\n" - " nat_traversal force;\n" - " proposal_check claim;\n" - " proposal {\n" - " encryption_algorithm aes;\n" - " hash_algorithm sha1;\n" - " authentication_method pre_shared_key;\n" - " dh_group 2;\n" - " lifetime time 15 min;\n" - " }\n" - "}\n\n" - "sainfo anonymous { \n" - " pfs_group 2;\n" - " lifetime time 10 min;\n" - " encryption_algorithm aes;\n" - " authentication_algorithm hmac_sha1;\n" - " compression_algorithm deflate;\n" - "}\n"; - char tmp_config_path[64]; - char racoon_config_path[64]; - const char* const racoon_config_dir = GetRacoonConfigDir(); - const char* const racoon_config_dir_old = GetOldRacoonConfigDir(); - int fd = -1; - - debug("entry"); - - if (0 > ensureExistenceOfRacoonConfigDir(racoon_config_dir)) - return -1; - - strlcpy(tmp_config_path, racoon_config_dir, sizeof(tmp_config_path)); - strlcat(tmp_config_path, "tmp.XXXXXX", sizeof(tmp_config_path)); - - fd = mkstemp(tmp_config_path); - - if (0 > fd) - { - helplog(ASL_LEVEL_ERR, "mkstemp \"%s\" failed: %s", - tmp_config_path, strerror(errno)); - return -1; - } - write(fd, configHeader, sizeof(configHeader)-1); - write(fd, config1, sizeof(config1)-1); - write(fd, fqdn, strlen(fqdn)); - write(fd, config2, sizeof(config2)-1); - close(fd); - - strlcpy(racoon_config_path, racoon_config_dir, sizeof(racoon_config_path)); - strlcat(racoon_config_path, racoon_config_file, sizeof(racoon_config_path)); - - moveAsideAnonymousRacoonConfiguration(racoon_config_dir_old); - moveAsideAnonymousRacoonConfiguration(racoon_config_dir); - - if (0 > rename(tmp_config_path, racoon_config_path)) - { - unlink(tmp_config_path); - helplog(ASL_LEVEL_ERR, "rename \"%s\" \"%s\" failed: %s", - tmp_config_path, racoon_config_path, strerror(errno)); - revertAnonymousRacoonConfiguration(racoon_config_dir_old); - revertAnonymousRacoonConfiguration(racoon_config_dir); - return -1; - } - - debug("successfully renamed \"%s\" \"%s\"", tmp_config_path, racoon_config_path); - return 0; - } +{ + static const char config1[] = + "remote anonymous {\n" + " exchange_mode aggressive;\n" + " doi ipsec_doi;\n" + " situation identity_only;\n" + " verify_identifier off;\n" + " generate_policy on;\n" + " shared_secret keychain_by_id \""; + static const char config2[] = + "\";\n" + " nonce_size 16;\n" + " lifetime time 15 min;\n" + " initial_contact on;\n" + " support_proxy on;\n" + " nat_traversal force;\n" + " proposal_check claim;\n" + " proposal {\n" + " encryption_algorithm aes;\n" + " hash_algorithm sha1;\n" + " authentication_method pre_shared_key;\n" + " dh_group 2;\n" + " lifetime time 15 min;\n" + " }\n" + "}\n\n" + "sainfo anonymous { \n" + " pfs_group 2;\n" + " lifetime time 10 min;\n" + " encryption_algorithm aes;\n" + " authentication_algorithm hmac_sha1;\n" + " compression_algorithm deflate;\n" + "}\n"; + char tmp_config_path[64]; + char racoon_config_path[64]; + const char* const racoon_config_dir = GetRacoonConfigDir(); + const char* const racoon_config_dir_old = GetOldRacoonConfigDir(); + int fd = -1; + + debug("entry"); + + if (0 > ensureExistenceOfRacoonConfigDir(racoon_config_dir)) + return -1; + + strlcpy(tmp_config_path, racoon_config_dir, sizeof(tmp_config_path)); + strlcat(tmp_config_path, "tmp.XXXXXX", sizeof(tmp_config_path)); + + fd = mkstemp(tmp_config_path); + + if (0 > fd) + { + helplog(ASL_LEVEL_ERR, "mkstemp \"%s\" failed: %s", + tmp_config_path, strerror(errno)); + return -1; + } + write(fd, configHeader, sizeof(configHeader)-1); + write(fd, config1, sizeof(config1)-1); + write(fd, fqdn, strlen(fqdn)); + write(fd, config2, sizeof(config2)-1); + close(fd); + + strlcpy(racoon_config_path, racoon_config_dir, sizeof(racoon_config_path)); + strlcat(racoon_config_path, racoon_config_file, sizeof(racoon_config_path)); + + moveAsideAnonymousRacoonConfiguration(racoon_config_dir_old); + moveAsideAnonymousRacoonConfiguration(racoon_config_dir); + + if (0 > rename(tmp_config_path, racoon_config_path)) + { + unlink(tmp_config_path); + helplog(ASL_LEVEL_ERR, "rename \"%s\" \"%s\" failed: %s", + tmp_config_path, racoon_config_path, strerror(errno)); + revertAnonymousRacoonConfiguration(racoon_config_dir_old); + revertAnonymousRacoonConfiguration(racoon_config_dir); + return -1; + } + + debug("successfully renamed \"%s\" \"%s\"", tmp_config_path, racoon_config_path); + return 0; +} static int notifyRacoon(void) - { - debug("entry"); - static const char racoon_pid_path[] = "/var/run/racoon.pid"; - char buf[] = "18446744073709551615"; /* largest 64-bit integer */ - char *p = NULL; - ssize_t n = 0; - unsigned long m = 0; - int fd = open(racoon_pid_path, O_RDONLY); - - if (0 > fd) - { - debug("open \"%s\" failed, and that's OK: %s", racoon_pid_path, - strerror(errno)); - return kmDNSHelperRacoonNotificationFailed; - } - n = read(fd, buf, sizeof(buf)-1); - close(fd); - if (1 > n) - { - debug("read of \"%s\" failed: %s", racoon_pid_path, - n == 0 ? "empty file" : strerror(errno)); - return kmDNSHelperRacoonNotificationFailed; - } - buf[n] = '\0'; - m = strtoul(buf, &p, 10); - if (*p != '\0' && !isspace(*p)) - { - debug("invalid PID \"%s\" (around '%c')", buf, *p); - return kmDNSHelperRacoonNotificationFailed; - } - if (2 > m) - { - debug("refusing to kill PID %lu", m); - return kmDNSHelperRacoonNotificationFailed; - } - if (0 != kill(m, RacoonSignal())) - { - debug("Could not signal racoon (%lu): %s", m, strerror(errno)); - return kmDNSHelperRacoonNotificationFailed; - } - debug("Sent racoon (%lu) signal %d", m, RacoonSignal()); - return 0; - } +{ + debug("entry"); + static const char racoon_pid_path[] = "/var/run/racoon.pid"; + char buf[] = "18446744073709551615"; /* largest 64-bit integer */ + char *p = NULL; + ssize_t n = 0; + unsigned long m = 0; + int fd = open(racoon_pid_path, O_RDONLY); + + if (0 > fd) + { + debug("open \"%s\" failed, and that's OK: %s", racoon_pid_path, + strerror(errno)); + return kmDNSHelperRacoonNotificationFailed; + } + n = read(fd, buf, sizeof(buf)-1); + close(fd); + if (1 > n) + { + debug("read of \"%s\" failed: %s", racoon_pid_path, + n == 0 ? "empty file" : strerror(errno)); + return kmDNSHelperRacoonNotificationFailed; + } + buf[n] = '\0'; + m = strtoul(buf, &p, 10); + if (*p != '\0' && !isspace(*p)) + { + debug("invalid PID \"%s\" (around '%c')", buf, *p); + return kmDNSHelperRacoonNotificationFailed; + } + if (2 > m) + { + debug("refusing to kill PID %lu", m); + return kmDNSHelperRacoonNotificationFailed; + } + if (0 != kill(m, RacoonSignal())) + { + debug("Could not signal racoon (%lu): %s", m, strerror(errno)); + return kmDNSHelperRacoonNotificationFailed; + } + debug("Sent racoon (%lu) signal %d", m, RacoonSignal()); + return 0; +} static void closefds(int from) - { - int fd = 0; - struct dirent entry, *entryp = NULL; - DIR *dirp = opendir("/dev/fd"); - - if (dirp == NULL) - { - /* fall back to the erroneous getdtablesize method */ - for (fd = from; fd < getdtablesize(); ++fd) - close(fd); - return; - } - while (0 == readdir_r(dirp, &entry, &entryp) && NULL != entryp) - { - fd = atoi(entryp->d_name); - if (fd >= from && fd != dirfd(dirp)) - close(fd); - } - closedir(dirp); - } +{ + int fd = 0; + struct dirent entry, *entryp = NULL; + DIR *dirp = opendir("/dev/fd"); + + if (dirp == NULL) + { + /* fall back to the erroneous getdtablesize method */ + for (fd = from; fd < getdtablesize(); ++fd) + close(fd); + return; + } + while (0 == readdir_r(dirp, &entry, &entryp) && NULL != entryp) + { + fd = atoi(entryp->d_name); + if (fd >= from && fd != dirfd(dirp)) + close(fd); + } + closedir(dirp); +} static int startRacoonOld(void) - { - debug("entry"); - char * const racoon_args[] = { "/usr/sbin/racoon", "-e", NULL }; - ssize_t n = 0; - pid_t pid = 0; - int status = 0; - - if (0 == (pid = fork())) - { - closefds(0); - execve(racoon_args[0], racoon_args, NULL); - helplog(ASL_LEVEL_ERR, "execve of \"%s\" failed: %s", - racoon_args[0], strerror(errno)); - exit(2); - } - helplog(ASL_LEVEL_NOTICE, "racoon (pid=%lu) started", - (unsigned long)pid); - n = waitpid(pid, &status, 0); - if (-1 == n) - { - helplog(ASL_LEVEL_ERR, "Unexpected waitpid failure: %s", - strerror(errno)); - return kmDNSHelperRacoonStartFailed; - } - else if (pid != n) - { - helplog(ASL_LEVEL_ERR, "Unexpected waitpid return value %d", - (int)n); - return kmDNSHelperRacoonStartFailed; - } - else if (WIFSIGNALED(status)) - { - helplog(ASL_LEVEL_ERR, - "racoon (pid=%lu) terminated due to signal %d", - (unsigned long)pid, WTERMSIG(status)); - return kmDNSHelperRacoonStartFailed; - } - else if (WIFSTOPPED(status)) - { - helplog(ASL_LEVEL_ERR, - "racoon (pid=%lu) has stopped due to signal %d", - (unsigned long)pid, WSTOPSIG(status)); - return kmDNSHelperRacoonStartFailed; - } - else if (0 != WEXITSTATUS(status)) - { - helplog(ASL_LEVEL_ERR, - "racoon (pid=%lu) exited with status %d", - (unsigned long)pid, WEXITSTATUS(status)); - return kmDNSHelperRacoonStartFailed; - } - debug("racoon (pid=%lu) daemonized normally", (unsigned long)pid); - return 0; - } +{ + debug("entry"); + char * const racoon_args[] = { "/usr/sbin/racoon", "-e", NULL }; + ssize_t n = 0; + pid_t pid = 0; + int status = 0; + + if (0 == (pid = fork())) + { + closefds(0); + execve(racoon_args[0], racoon_args, NULL); + helplog(ASL_LEVEL_ERR, "execve of \"%s\" failed: %s", + racoon_args[0], strerror(errno)); + exit(2); + } + helplog(ASL_LEVEL_NOTICE, "racoon (pid=%lu) started", + (unsigned long)pid); + n = waitpid(pid, &status, 0); + if (-1 == n) + { + helplog(ASL_LEVEL_ERR, "Unexpected waitpid failure: %s", + strerror(errno)); + return kmDNSHelperRacoonStartFailed; + } + else if (pid != n) + { + helplog(ASL_LEVEL_ERR, "Unexpected waitpid return value %d", + (int)n); + return kmDNSHelperRacoonStartFailed; + } + else if (WIFSIGNALED(status)) + { + helplog(ASL_LEVEL_ERR, + "racoon (pid=%lu) terminated due to signal %d", + (unsigned long)pid, WTERMSIG(status)); + return kmDNSHelperRacoonStartFailed; + } + else if (WIFSTOPPED(status)) + { + helplog(ASL_LEVEL_ERR, + "racoon (pid=%lu) has stopped due to signal %d", + (unsigned long)pid, WSTOPSIG(status)); + return kmDNSHelperRacoonStartFailed; + } + else if (0 != WEXITSTATUS(status)) + { + helplog(ASL_LEVEL_ERR, + "racoon (pid=%lu) exited with status %d", + (unsigned long)pid, WEXITSTATUS(status)); + return kmDNSHelperRacoonStartFailed; + } + debug("racoon (pid=%lu) daemonized normally", (unsigned long)pid); + return 0; +} // constant and structure for the racoon control socket #define VPNCTL_CMD_PING 0x0004 typedef struct vpnctl_hdr_struct - { - u_int16_t msg_type; - u_int16_t flags; - u_int32_t cookie; - u_int32_t reserved; - u_int16_t result; - u_int16_t len; - } vpnctl_hdr; +{ + u_int16_t msg_type; + u_int16_t flags; + u_int32_t cookie; + u_int32_t reserved; + u_int16_t result; + u_int16_t len; +} vpnctl_hdr; static int startRacoon(void) - { - debug("entry"); - int fd = socket(PF_UNIX, SOCK_STREAM, 0); - if (0 > fd) - { - helplog(ASL_LEVEL_ERR, "Could not create endpoint for racoon control socket: %d %s", - errno, strerror(errno)); - return kmDNSHelperRacoonStartFailed; - } - - struct sockaddr_un saddr; - memset(&saddr, 0, sizeof(saddr)); - saddr.sun_family = AF_UNIX; - saddr.sun_len = sizeof(saddr); - static const char racoon_control_sock_path[] = "/var/run/vpncontrol.sock"; - strcpy(saddr.sun_path, racoon_control_sock_path); - int result = connect(fd, (struct sockaddr*) &saddr, saddr.sun_len); - if (0 > result) - { - helplog(ASL_LEVEL_ERR, "Could not connect racoon control socket %s: %d %s", - racoon_control_sock_path, errno, strerror(errno)); - return kmDNSHelperRacoonStartFailed; - } - - u_int32_t btmm_cookie = 0x4d4d5442; - vpnctl_hdr h = { VPNCTL_CMD_PING, 0, btmm_cookie, 0, 0, 0 }; - size_t bytes = 0; - ssize_t ret = 0; - - while (bytes < sizeof(vpnctl_hdr)) - { - ret = write(fd, ((unsigned char*)&h)+bytes, sizeof(vpnctl_hdr) - bytes); - if (ret == -1) - { - helplog(ASL_LEVEL_ERR, "Could not write to racoon control socket: %d %s", - errno, strerror(errno)); - return kmDNSHelperRacoonStartFailed; - } - bytes += ret; - } - - int nfds = fd + 1; - fd_set fds; - int counter = 0; - struct timeval tv; - bytes = 0; - h.cookie = 0; - - for (counter = 0; counter < 100; counter++) - { - FD_ZERO(&fds); - FD_SET(fd, &fds); - tv = (struct timeval){ 0, 10000 }; // 10 milliseconds * 100 iterations = 1 second max wait time - - result = select(nfds, &fds, (fd_set*)NULL, (fd_set*)NULL, &tv); - if (result > 0) - { - if (FD_ISSET(fd, &fds)) - { - ret = read(fd, ((unsigned char*)&h)+bytes, sizeof(vpnctl_hdr) - bytes); - - if (ret == -1) - { - helplog(ASL_LEVEL_ERR, "Could not read from racoon control socket: %d %s", - strerror(errno)); - break; - } - bytes += ret; - if (bytes >= sizeof(vpnctl_hdr)) break; - } - else - { - debug("select returned but fd_isset not on expected fd\n"); - } - } - else if (result < 0) - { - debug("select returned %d errno %d %s\n", result, errno, strerror(errno)); - if (errno != EINTR) break; - } - } - - close(fd); - - if (bytes < sizeof(vpnctl_hdr) || h.cookie != btmm_cookie) return kmDNSHelperRacoonStartFailed; - - debug("racoon started"); - return 0; - } +{ + debug("entry"); + int fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (0 > fd) + { + helplog(ASL_LEVEL_ERR, "Could not create endpoint for racoon control socket: %d %s", + errno, strerror(errno)); + return kmDNSHelperRacoonStartFailed; + } + + struct sockaddr_un saddr; + memset(&saddr, 0, sizeof(saddr)); + saddr.sun_family = AF_UNIX; + saddr.sun_len = sizeof(saddr); + static const char racoon_control_sock_path[] = "/var/run/vpncontrol.sock"; + strcpy(saddr.sun_path, racoon_control_sock_path); + int result = connect(fd, (struct sockaddr*) &saddr, saddr.sun_len); + if (0 > result) + { + helplog(ASL_LEVEL_ERR, "Could not connect racoon control socket %s: %d %s", + racoon_control_sock_path, errno, strerror(errno)); + return kmDNSHelperRacoonStartFailed; + } + + u_int32_t btmm_cookie = 0x4d4d5442; + vpnctl_hdr h = { VPNCTL_CMD_PING, 0, btmm_cookie, 0, 0, 0 }; + size_t bytes = 0; + ssize_t ret = 0; + + while (bytes < sizeof(vpnctl_hdr)) + { + ret = write(fd, ((unsigned char*)&h)+bytes, sizeof(vpnctl_hdr) - bytes); + if (ret == -1) + { + helplog(ASL_LEVEL_ERR, "Could not write to racoon control socket: %d %s", + errno, strerror(errno)); + return kmDNSHelperRacoonStartFailed; + } + bytes += ret; + } + + int nfds = fd + 1; + fd_set fds; + int counter = 0; + struct timeval tv; + bytes = 0; + h.cookie = 0; + + for (counter = 0; counter < 100; counter++) + { + FD_ZERO(&fds); + FD_SET(fd, &fds); + tv = (struct timeval){ 0, 10000 }; // 10 milliseconds * 100 iterations = 1 second max wait time + + result = select(nfds, &fds, (fd_set*)NULL, (fd_set*)NULL, &tv); + if (result > 0) + { + if (FD_ISSET(fd, &fds)) + { + ret = read(fd, ((unsigned char*)&h)+bytes, sizeof(vpnctl_hdr) - bytes); + + if (ret == -1) + { + helplog(ASL_LEVEL_ERR, "Could not read from racoon control socket: %d %s", + strerror(errno)); + break; + } + bytes += ret; + if (bytes >= sizeof(vpnctl_hdr)) break; + } + else + { + debug("select returned but fd_isset not on expected fd\n"); + } + } + else if (result < 0) + { + debug("select returned %d errno %d %s\n", result, errno, strerror(errno)); + if (errno != EINTR) break; + } + } + + close(fd); + + if (bytes < sizeof(vpnctl_hdr) || h.cookie != btmm_cookie) return kmDNSHelperRacoonStartFailed; + + debug("racoon started"); + return 0; +} static int kickRacoon(void) - { - if ( 0 == notifyRacoon() ) - return 0; - return UseOldRacoon() ? startRacoonOld() : startRacoon(); - } +{ + if ( 0 == notifyRacoon() ) + return 0; + return UseOldRacoon() ? startRacoonOld() : startRacoon(); +} #endif /* ndef MDNS_NO_IPSEC */ int do_mDNSConfigureServer(__unused mach_port_t port, int updown, const char *fqdn, audit_token_t token) - { +{ #ifndef MDNS_NO_IPSEC - debug("entry"); - if (!authorized(&token)) goto fin; - - switch ((enum mDNSUpDown)updown) - { - case kmDNSUp: - if (0 != createAnonymousRacoonConfiguration(fqdn)) goto fin; - break; - case kmDNSDown: - revertAnonymousRacoonConfiguration(GetOldRacoonConfigDir()); - revertAnonymousRacoonConfiguration(GetRacoonConfigDir()); - break; - default: - goto fin; - } - - if (0 != kickRacoon()) - goto fin; - debug("succeeded"); + debug("entry"); + if (!authorized(&token)) goto fin; + + switch ((enum mDNSUpDown)updown) + { + case kmDNSUp: + if (0 != createAnonymousRacoonConfiguration(fqdn)) goto fin; + break; + case kmDNSDown: + revertAnonymousRacoonConfiguration(GetOldRacoonConfigDir()); + revertAnonymousRacoonConfiguration(GetRacoonConfigDir()); + break; + default: + goto fin; + } + + if (0 != kickRacoon()) + goto fin; + debug("succeeded"); fin: #else - (void)port; (void)updown; (void)fqdn; (void)token; + (void)port; (void)updown; (void)fqdn; (void)token; #endif - update_idle_timer(); - return KERN_SUCCESS; - } + update_idle_timer(); + return KERN_SUCCESS; +} #ifndef MDNS_NO_IPSEC @@ -1786,651 +1704,651 @@ static unsigned int routeSeq = 1; static int setupTunnelRoute(v6addr_t local, v6addr_t remote) - { - struct - { - struct rt_msghdr hdr; - struct sockaddr_in6 dst; - struct sockaddr_in6 gtwy; - } msg; - int err = 0; - int s = -1; - - if (0 > (s = socket(PF_ROUTE, SOCK_RAW, AF_INET))) - { - helplog(ASL_LEVEL_ERR, "socket(PF_ROUTE, ...) failed: %s", - strerror(errno)); - err = kmDNSHelperRoutingSocketCreationFailed; - goto fin; - } - memset(&msg, 0, sizeof(msg)); - msg.hdr.rtm_msglen = sizeof(msg); - msg.hdr.rtm_type = RTM_ADD; - /* The following flags are set by `route add -inet6 -host ...` */ - msg.hdr.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_HOST | RTF_STATIC; - msg.hdr.rtm_version = RTM_VERSION; - msg.hdr.rtm_seq = routeSeq++; - msg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY; - msg.hdr.rtm_inits = RTV_MTU; - msg.hdr.rtm_rmx.rmx_mtu = 1280; - - msg.dst.sin6_len = sizeof(msg.dst); - msg.dst.sin6_family = AF_INET6; - memcpy(&msg.dst.sin6_addr, remote, sizeof(msg.dst.sin6_addr)); - - msg.gtwy.sin6_len = sizeof(msg.gtwy); - msg.gtwy.sin6_family = AF_INET6; - memcpy(&msg.gtwy.sin6_addr, local, sizeof(msg.gtwy.sin6_addr)); - - /* send message, ignore error when route already exists */ - if (0 > write(s, &msg, msg.hdr.rtm_msglen)) - { - int errno_ = errno; - - debug("write to routing socket failed: %s", strerror(errno_)); - if (EEXIST != errno_) - { - err = kmDNSHelperRouteAdditionFailed; - goto fin; - } - } +{ + struct + { + struct rt_msghdr hdr; + struct sockaddr_in6 dst; + struct sockaddr_in6 gtwy; + } msg; + int err = 0; + int s = -1; + + if (0 > (s = socket(PF_ROUTE, SOCK_RAW, AF_INET))) + { + helplog(ASL_LEVEL_ERR, "socket(PF_ROUTE, ...) failed: %s", + strerror(errno)); + err = kmDNSHelperRoutingSocketCreationFailed; + goto fin; + } + memset(&msg, 0, sizeof(msg)); + msg.hdr.rtm_msglen = sizeof(msg); + msg.hdr.rtm_type = RTM_ADD; + /* The following flags are set by `route add -inet6 -host ...` */ + msg.hdr.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_HOST | RTF_STATIC; + msg.hdr.rtm_version = RTM_VERSION; + msg.hdr.rtm_seq = routeSeq++; + msg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY; + msg.hdr.rtm_inits = RTV_MTU; + msg.hdr.rtm_rmx.rmx_mtu = 1280; + + msg.dst.sin6_len = sizeof(msg.dst); + msg.dst.sin6_family = AF_INET6; + memcpy(&msg.dst.sin6_addr, remote, sizeof(msg.dst.sin6_addr)); + + msg.gtwy.sin6_len = sizeof(msg.gtwy); + msg.gtwy.sin6_family = AF_INET6; + memcpy(&msg.gtwy.sin6_addr, local, sizeof(msg.gtwy.sin6_addr)); + + /* send message, ignore error when route already exists */ + if (0 > write(s, &msg, msg.hdr.rtm_msglen)) + { + int errno_ = errno; + + debug("write to routing socket failed: %s", strerror(errno_)); + if (EEXIST != errno_) + { + err = kmDNSHelperRouteAdditionFailed; + goto fin; + } + } fin: - if (0 <= s) - close(s); - return err; - } + if (0 <= s) + close(s); + return err; +} static int teardownTunnelRoute(v6addr_t remote) - { - struct - { - struct rt_msghdr hdr; - struct sockaddr_in6 dst; - } msg; - int err = 0; - int s = -1; - - if (0 > (s = socket(PF_ROUTE, SOCK_RAW, AF_INET))) - { - helplog(ASL_LEVEL_ERR, "socket(PF_ROUTE, ...) failed: %s", - strerror(errno)); - err = kmDNSHelperRoutingSocketCreationFailed; - goto fin; - } - memset(&msg, 0, sizeof(msg)); - - msg.hdr.rtm_msglen = sizeof(msg); - msg.hdr.rtm_type = RTM_DELETE; - msg.hdr.rtm_version = RTM_VERSION; - msg.hdr.rtm_seq = routeSeq++; - msg.hdr.rtm_addrs = RTA_DST; - - msg.dst.sin6_len = sizeof(msg.dst); - msg.dst.sin6_family = AF_INET6; - memcpy(&msg.dst.sin6_addr, remote, sizeof(msg.dst.sin6_addr)); - if (0 > write(s, &msg, msg.hdr.rtm_msglen)) - { - int errno_ = errno; - - debug("write to routing socket failed: %s", strerror(errno_)); - if (ESRCH != errno_) - { - err = kmDNSHelperRouteDeletionFailed; - goto fin; - } - } +{ + struct + { + struct rt_msghdr hdr; + struct sockaddr_in6 dst; + } msg; + int err = 0; + int s = -1; + + if (0 > (s = socket(PF_ROUTE, SOCK_RAW, AF_INET))) + { + helplog(ASL_LEVEL_ERR, "socket(PF_ROUTE, ...) failed: %s", + strerror(errno)); + err = kmDNSHelperRoutingSocketCreationFailed; + goto fin; + } + memset(&msg, 0, sizeof(msg)); + + msg.hdr.rtm_msglen = sizeof(msg); + msg.hdr.rtm_type = RTM_DELETE; + msg.hdr.rtm_version = RTM_VERSION; + msg.hdr.rtm_seq = routeSeq++; + msg.hdr.rtm_addrs = RTA_DST; + + msg.dst.sin6_len = sizeof(msg.dst); + msg.dst.sin6_family = AF_INET6; + memcpy(&msg.dst.sin6_addr, remote, sizeof(msg.dst.sin6_addr)); + if (0 > write(s, &msg, msg.hdr.rtm_msglen)) + { + int errno_ = errno; + + debug("write to routing socket failed: %s", strerror(errno_)); + if (ESRCH != errno_) + { + err = kmDNSHelperRouteDeletionFailed; + goto fin; + } + } fin: - if (0 <= s) - close(s); - return err; - } + if (0 <= s) + close(s); + return err; +} static int v4addr_to_string(v4addr_t addr, char *buf, size_t buflen) - { - if (NULL == inet_ntop(AF_INET, addr, buf, buflen)) - { - helplog(ASL_LEVEL_ERR, "inet_ntop failed: %s", - strerror(errno)); - return kmDNSHelperInvalidNetworkAddress; - } - else - return 0; - } +{ + if (NULL == inet_ntop(AF_INET, addr, buf, buflen)) + { + helplog(ASL_LEVEL_ERR, "inet_ntop failed: %s", + strerror(errno)); + return kmDNSHelperInvalidNetworkAddress; + } + else + return 0; +} static int v6addr_to_string(v6addr_t addr, char *buf, size_t buflen) - { - if (NULL == inet_ntop(AF_INET6, addr, buf, buflen)) - { - helplog(ASL_LEVEL_ERR, "inet_ntop failed: %s", - strerror(errno)); - return kmDNSHelperInvalidNetworkAddress; - } - else - return 0; - } +{ + if (NULL == inet_ntop(AF_INET6, addr, buf, buflen)) + { + helplog(ASL_LEVEL_ERR, "inet_ntop failed: %s", + strerror(errno)); + return kmDNSHelperInvalidNetworkAddress; + } + else + return 0; +} /* Caller owns object returned in `policy' */ static int generateTunnelPolicy(mDNSTunnelPolicyWhich which, mDNSTunnelType type, int in, - v4addr_t src, uint16_t src_port, - v4addr_t dst, uint16_t dst_port, - v6addr_t src6, v6addr_t dst6, - ipsec_policy_t *policy, size_t *len) - { - char srcs[INET_ADDRSTRLEN], dsts[INET_ADDRSTRLEN]; - char srcs6[INET6_ADDRSTRLEN], dsts6[INET6_ADDRSTRLEN]; - char buf[512]; - char *inOut = in ? "in" : "out"; - ssize_t n = 0; - int err = 0; - - *policy = NULL; - *len = 0; - - switch (which) - { - case kmDNSTunnelPolicySetup: - if (type == kmDNSIPv6IPv4Tunnel) - { - if (0 != (err = v4addr_to_string(src, srcs, sizeof(srcs)))) - goto fin; - if (0 != (err = v4addr_to_string(dst, dsts, sizeof(dsts)))) - goto fin; - n = snprintf(buf, sizeof(buf), - "%s ipsec esp/tunnel/%s[%u]-%s[%u]/require", - inOut, srcs, src_port, dsts, dst_port); - } - else if (type == kmDNSIPv6IPv6Tunnel) - { - if (0 != (err = v6addr_to_string(src6, srcs6, sizeof(srcs6)))) - goto fin; - if (0 != (err = v6addr_to_string(dst6, dsts6, sizeof(dsts6)))) - goto fin; - n = snprintf(buf, sizeof(buf), - "%s ipsec esp/tunnel/%s-%s/require", - inOut, srcs6, dsts6); - } - break; - case kmDNSTunnelPolicyTeardown: - n = strlcpy(buf, inOut, sizeof(buf)); - break; - case kmDNSTunnelPolicyGenerate: - n = snprintf(buf, sizeof(buf), "%s generate", inOut); - break; - default: - err = kmDNSHelperIPsecPolicyCreationFailed; - goto fin; - } - - if (n >= (int)sizeof(buf)) - { - err = kmDNSHelperResultTooLarge; - goto fin; - } - - debug("policy=\"%s\"", buf); - if (NULL == (*policy = (ipsec_policy_t)ipsec_set_policy(buf, n))) - { - helplog(ASL_LEVEL_ERR, - "Could not create IPsec policy from \"%s\"", buf); - err = kmDNSHelperIPsecPolicyCreationFailed; - goto fin; - } - *len = ((ipsec_policy_t)(*policy))->sadb_x_policy_len * 8; + v4addr_t src, uint16_t src_port, + v4addr_t dst, uint16_t dst_port, + v6addr_t src6, v6addr_t dst6, + ipsec_policy_t *policy, size_t *len) +{ + char srcs[INET_ADDRSTRLEN], dsts[INET_ADDRSTRLEN]; + char srcs6[INET6_ADDRSTRLEN], dsts6[INET6_ADDRSTRLEN]; + char buf[512]; + char *inOut = in ? "in" : "out"; + ssize_t n = 0; + int err = 0; + + *policy = NULL; + *len = 0; + + switch (which) + { + case kmDNSTunnelPolicySetup: + if (type == kmDNSIPv6IPv4Tunnel) + { + if (0 != (err = v4addr_to_string(src, srcs, sizeof(srcs)))) + goto fin; + if (0 != (err = v4addr_to_string(dst, dsts, sizeof(dsts)))) + goto fin; + n = snprintf(buf, sizeof(buf), + "%s ipsec esp/tunnel/%s[%u]-%s[%u]/require", + inOut, srcs, src_port, dsts, dst_port); + } + else if (type == kmDNSIPv6IPv6Tunnel) + { + if (0 != (err = v6addr_to_string(src6, srcs6, sizeof(srcs6)))) + goto fin; + if (0 != (err = v6addr_to_string(dst6, dsts6, sizeof(dsts6)))) + goto fin; + n = snprintf(buf, sizeof(buf), + "%s ipsec esp/tunnel/%s-%s/require", + inOut, srcs6, dsts6); + } + break; + case kmDNSTunnelPolicyTeardown: + n = strlcpy(buf, inOut, sizeof(buf)); + break; + case kmDNSTunnelPolicyGenerate: + n = snprintf(buf, sizeof(buf), "%s generate", inOut); + break; + default: + err = kmDNSHelperIPsecPolicyCreationFailed; + goto fin; + } + + if (n >= (int)sizeof(buf)) + { + err = kmDNSHelperResultTooLarge; + goto fin; + } + + debug("policy=\"%s\"", buf); + if (NULL == (*policy = (ipsec_policy_t)ipsec_set_policy(buf, n))) + { + helplog(ASL_LEVEL_ERR, + "Could not create IPsec policy from \"%s\"", buf); + err = kmDNSHelperIPsecPolicyCreationFailed; + goto fin; + } + *len = ((ipsec_policy_t)(*policy))->sadb_x_policy_len * 8; fin: - return err; - } + return err; +} static int sendPolicy(int s, int setup, - struct sockaddr *src, uint8_t src_bits, - struct sockaddr *dst, uint8_t dst_bits, - ipsec_policy_t policy, size_t len) - { - static unsigned int policySeq = 0; - int err = 0; - - debug("entry, setup=%d", setup); - if (setup) - err = pfkey_send_spdadd(s, src, src_bits, dst, dst_bits, -1, - (char *)policy, len, policySeq++); - else - err = pfkey_send_spddelete(s, src, src_bits, dst, dst_bits, -1, - (char *)policy, len, policySeq++); - if (0 > err) - { - helplog(ASL_LEVEL_ERR, "Could not set IPsec policy: %s", - ipsec_strerror()); - err = kmDNSHelperIPsecPolicySetFailed; - goto fin; - } - else - err = 0; - debug("succeeded"); + struct sockaddr *src, uint8_t src_bits, + struct sockaddr *dst, uint8_t dst_bits, + ipsec_policy_t policy, size_t len) +{ + static unsigned int policySeq = 0; + int err = 0; + + debug("entry, setup=%d", setup); + if (setup) + err = pfkey_send_spdadd(s, src, src_bits, dst, dst_bits, -1, + (char *)policy, len, policySeq++); + else + err = pfkey_send_spddelete(s, src, src_bits, dst, dst_bits, -1, + (char *)policy, len, policySeq++); + if (0 > err) + { + helplog(ASL_LEVEL_ERR, "Could not set IPsec policy: %s", + ipsec_strerror()); + err = kmDNSHelperIPsecPolicySetFailed; + goto fin; + } + else + err = 0; + debug("succeeded"); fin: - return err; - } + return err; +} static int removeSA(int s, struct sockaddr *src, struct sockaddr *dst) - { - int err = 0; - - debug("entry"); - err = pfkey_send_delete_all(s, SADB_SATYPE_ESP, IPSEC_MODE_ANY, src, dst); - if (0 > err) - { - helplog(ASL_LEVEL_ERR, "Could not remove IPsec SA: %s", ipsec_strerror()); - err = kmDNSHelperIPsecRemoveSAFailed; - goto fin; - } - err = pfkey_send_delete_all(s, SADB_SATYPE_ESP, IPSEC_MODE_ANY, dst, src); - if (0 > err) - { - helplog(ASL_LEVEL_ERR, "Could not remove IPsec SA: %s", ipsec_strerror()); - err = kmDNSHelperIPsecRemoveSAFailed; - goto fin; - } - else - err = 0; - - debug("succeeded"); +{ + int err = 0; + + debug("entry"); + err = pfkey_send_delete_all(s, SADB_SATYPE_ESP, IPSEC_MODE_ANY, src, dst); + if (0 > err) + { + helplog(ASL_LEVEL_ERR, "Could not remove IPsec SA: %s", ipsec_strerror()); + err = kmDNSHelperIPsecRemoveSAFailed; + goto fin; + } + err = pfkey_send_delete_all(s, SADB_SATYPE_ESP, IPSEC_MODE_ANY, dst, src); + if (0 > err) + { + helplog(ASL_LEVEL_ERR, "Could not remove IPsec SA: %s", ipsec_strerror()); + err = kmDNSHelperIPsecRemoveSAFailed; + goto fin; + } + else + err = 0; + + debug("succeeded"); fin: - return err; - } + return err; +} static int doTunnelPolicy(mDNSTunnelPolicyWhich which, mDNSTunnelType type, - v6addr_t loc_inner, uint8_t loc_bits, - v4addr_t loc_outer, uint16_t loc_port, - v6addr_t rmt_inner, uint8_t rmt_bits, - v4addr_t rmt_outer, uint16_t rmt_port, - v6addr_t loc_outer6, v6addr_t rmt_outer6) - { - struct sockaddr_in6 sin6_loc; - struct sockaddr_in6 sin6_rmt; - ipsec_policy_t policy = NULL; - size_t len = 0; - int s = -1; - int err = 0; - - debug("entry"); - if (0 > (s = pfkey_open())) - { - helplog(ASL_LEVEL_ERR, - "Could not create IPsec policy socket: %s", - ipsec_strerror()); - err = kmDNSHelperIPsecPolicySocketCreationFailed; - goto fin; - } - - memset(&sin6_loc, 0, sizeof(sin6_loc)); - sin6_loc.sin6_len = sizeof(sin6_loc); - sin6_loc.sin6_family = AF_INET6; - sin6_loc.sin6_port = htons(0); - memcpy(&sin6_loc.sin6_addr, loc_inner, sizeof(sin6_loc.sin6_addr)); - - memset(&sin6_rmt, 0, sizeof(sin6_rmt)); - sin6_rmt.sin6_len = sizeof(sin6_rmt); - sin6_rmt.sin6_family = AF_INET6; - sin6_rmt.sin6_port = htons(0); - memcpy(&sin6_rmt.sin6_addr, rmt_inner, sizeof(sin6_rmt.sin6_addr)); - - int setup = which != kmDNSTunnelPolicyTeardown; - - if (0 != (err = generateTunnelPolicy(which, type, 1, - rmt_outer, rmt_port, - loc_outer, loc_port, - rmt_outer6, loc_outer6, - &policy, &len))) - goto fin; - if (0 != (err = sendPolicy(s, setup, - (struct sockaddr *)&sin6_rmt, rmt_bits, - (struct sockaddr *)&sin6_loc, loc_bits, - policy, len))) - goto fin; - if (NULL != policy) - { - free(policy); - policy = NULL; - } - if (0 != (err = generateTunnelPolicy(which, type, 0, - loc_outer, loc_port, - rmt_outer, rmt_port, - loc_outer6, rmt_outer6, - &policy, &len))) - goto fin; - if (0 != (err = sendPolicy(s, setup, - (struct sockaddr *)&sin6_loc, loc_bits, - (struct sockaddr *)&sin6_rmt, rmt_bits, - policy, len))) - goto fin; - - if (which == kmDNSTunnelPolicyTeardown) - { - if (rmt_port) // Outer tunnel is IPv4 - { - if (loc_outer && rmt_outer) - { - struct sockaddr_in sin_loc; - struct sockaddr_in sin_rmt; - memset(&sin_loc, 0, sizeof(sin_loc)); - sin_loc.sin_len = sizeof(sin_loc); - sin_loc.sin_family = AF_INET; - memcpy(&sin_loc.sin_addr, loc_outer, sizeof(sin_loc.sin_addr)); - - memset(&sin_rmt, 0, sizeof(sin_rmt)); - sin_rmt.sin_len = sizeof(sin_rmt); - sin_rmt.sin_family = AF_INET; - memcpy(&sin_rmt.sin_addr, rmt_outer, sizeof(sin_rmt.sin_addr)); - if (0 != (err = removeSA(s, (struct sockaddr *)&sin_loc, (struct sockaddr *)&sin_rmt))) - goto fin; - } - } - else - { - if (loc_outer6 && rmt_outer6) - { - struct sockaddr_in6 sin6_lo; - struct sockaddr_in6 sin6_rm; - - memset(&sin6_lo, 0, sizeof(sin6_lo)); - sin6_lo.sin6_len = sizeof(sin6_lo); - sin6_lo.sin6_family = AF_INET6; - memcpy(&sin6_lo.sin6_addr, loc_outer6, sizeof(sin6_lo.sin6_addr)); - - memset(&sin6_rm, 0, sizeof(sin6_rm)); - sin6_rm.sin6_len = sizeof(sin6_rm); - sin6_rm.sin6_family = AF_INET6; - memcpy(&sin6_rm.sin6_addr, rmt_outer6, sizeof(sin6_rm.sin6_addr)); - if (0 != (err = removeSA(s, (struct sockaddr *)&sin6_lo, (struct sockaddr *)&sin6_rm))) - goto fin; - } - } - } - - - debug("succeeded"); + v6addr_t loc_inner, uint8_t loc_bits, + v4addr_t loc_outer, uint16_t loc_port, + v6addr_t rmt_inner, uint8_t rmt_bits, + v4addr_t rmt_outer, uint16_t rmt_port, + v6addr_t loc_outer6, v6addr_t rmt_outer6) +{ + struct sockaddr_in6 sin6_loc; + struct sockaddr_in6 sin6_rmt; + ipsec_policy_t policy = NULL; + size_t len = 0; + int s = -1; + int err = 0; + + debug("entry"); + if (0 > (s = pfkey_open())) + { + helplog(ASL_LEVEL_ERR, + "Could not create IPsec policy socket: %s", + ipsec_strerror()); + err = kmDNSHelperIPsecPolicySocketCreationFailed; + goto fin; + } + + memset(&sin6_loc, 0, sizeof(sin6_loc)); + sin6_loc.sin6_len = sizeof(sin6_loc); + sin6_loc.sin6_family = AF_INET6; + sin6_loc.sin6_port = htons(0); + memcpy(&sin6_loc.sin6_addr, loc_inner, sizeof(sin6_loc.sin6_addr)); + + memset(&sin6_rmt, 0, sizeof(sin6_rmt)); + sin6_rmt.sin6_len = sizeof(sin6_rmt); + sin6_rmt.sin6_family = AF_INET6; + sin6_rmt.sin6_port = htons(0); + memcpy(&sin6_rmt.sin6_addr, rmt_inner, sizeof(sin6_rmt.sin6_addr)); + + int setup = which != kmDNSTunnelPolicyTeardown; + + if (0 != (err = generateTunnelPolicy(which, type, 1, + rmt_outer, rmt_port, + loc_outer, loc_port, + rmt_outer6, loc_outer6, + &policy, &len))) + goto fin; + if (0 != (err = sendPolicy(s, setup, + (struct sockaddr *)&sin6_rmt, rmt_bits, + (struct sockaddr *)&sin6_loc, loc_bits, + policy, len))) + goto fin; + if (NULL != policy) + { + free(policy); + policy = NULL; + } + if (0 != (err = generateTunnelPolicy(which, type, 0, + loc_outer, loc_port, + rmt_outer, rmt_port, + loc_outer6, rmt_outer6, + &policy, &len))) + goto fin; + if (0 != (err = sendPolicy(s, setup, + (struct sockaddr *)&sin6_loc, loc_bits, + (struct sockaddr *)&sin6_rmt, rmt_bits, + policy, len))) + goto fin; + + if (which == kmDNSTunnelPolicyTeardown) + { + if (rmt_port) // Outer tunnel is IPv4 + { + if (loc_outer && rmt_outer) + { + struct sockaddr_in sin_loc; + struct sockaddr_in sin_rmt; + memset(&sin_loc, 0, sizeof(sin_loc)); + sin_loc.sin_len = sizeof(sin_loc); + sin_loc.sin_family = AF_INET; + memcpy(&sin_loc.sin_addr, loc_outer, sizeof(sin_loc.sin_addr)); + + memset(&sin_rmt, 0, sizeof(sin_rmt)); + sin_rmt.sin_len = sizeof(sin_rmt); + sin_rmt.sin_family = AF_INET; + memcpy(&sin_rmt.sin_addr, rmt_outer, sizeof(sin_rmt.sin_addr)); + if (0 != (err = removeSA(s, (struct sockaddr *)&sin_loc, (struct sockaddr *)&sin_rmt))) + goto fin; + } + } + else + { + if (loc_outer6 && rmt_outer6) + { + struct sockaddr_in6 sin6_lo; + struct sockaddr_in6 sin6_rm; + + memset(&sin6_lo, 0, sizeof(sin6_lo)); + sin6_lo.sin6_len = sizeof(sin6_lo); + sin6_lo.sin6_family = AF_INET6; + memcpy(&sin6_lo.sin6_addr, loc_outer6, sizeof(sin6_lo.sin6_addr)); + + memset(&sin6_rm, 0, sizeof(sin6_rm)); + sin6_rm.sin6_len = sizeof(sin6_rm); + sin6_rm.sin6_family = AF_INET6; + memcpy(&sin6_rm.sin6_addr, rmt_outer6, sizeof(sin6_rm.sin6_addr)); + if (0 != (err = removeSA(s, (struct sockaddr *)&sin6_lo, (struct sockaddr *)&sin6_rm))) + goto fin; + } + } + } + + + debug("succeeded"); fin: - if (s >= 0) - pfkey_close(s); - if (NULL != policy) - free(policy); - return err; - } + if (s >= 0) + pfkey_close(s); + if (NULL != policy) + free(policy); + return err; +} #endif /* ndef MDNS_NO_IPSEC */ int do_mDNSAutoTunnelSetKeys(__unused mach_port_t port, int replacedelete, - v6addr_t loc_inner, v6addr_t loc_outer6, uint16_t loc_port, - v6addr_t rmt_inner, v6addr_t rmt_outer6, uint16_t rmt_port, - const char *id, int *err, audit_token_t token) - { + v6addr_t loc_inner, v6addr_t loc_outer6, uint16_t loc_port, + v6addr_t rmt_inner, v6addr_t rmt_outer6, uint16_t rmt_port, + const char *id, int *err, audit_token_t token) +{ #ifndef MDNS_NO_IPSEC - static const char config[] = - "%s" - "remote %s [%u] {\n" - " disconnect_on_idle idle_timeout 600 idle_direction idle_outbound;\n" - " exchange_mode aggressive;\n" - " doi ipsec_doi;\n" - " situation identity_only;\n" - " verify_identifier off;\n" - " generate_policy on;\n" - " my_identifier user_fqdn \"%s\";\n" - " shared_secret keychain \"%s\";\n" - " nonce_size 16;\n" - " lifetime time 15 min;\n" - " initial_contact on;\n" - " support_proxy on;\n" - " nat_traversal force;\n" - " proposal_check claim;\n" - " proposal {\n" - " encryption_algorithm aes;\n" - " hash_algorithm sha1;\n" - " authentication_method pre_shared_key;\n" - " dh_group 2;\n" - " lifetime time 15 min;\n" - " }\n" - "}\n\n" - "sainfo address %s any address %s any {\n" - " pfs_group 2;\n" - " lifetime time 10 min;\n" - " encryption_algorithm aes;\n" - " authentication_algorithm hmac_sha1;\n" - " compression_algorithm deflate;\n" - "}\n\n" - "sainfo address %s any address %s any {\n" - " pfs_group 2;\n" - " lifetime time 10 min;\n" - " encryption_algorithm aes;\n" - " authentication_algorithm hmac_sha1;\n" - " compression_algorithm deflate;\n" - "}\n"; - char path[PATH_MAX] = ""; - char li[INET6_ADDRSTRLEN], lo[INET_ADDRSTRLEN], lo6[INET6_ADDRSTRLEN], - ri[INET6_ADDRSTRLEN], ro[INET_ADDRSTRLEN], ro6[INET6_ADDRSTRLEN]; - FILE *fp = NULL; - int fd = -1; - char tmp_path[PATH_MAX] = ""; - v4addr_t loc_outer, rmt_outer; - - debug("entry"); - *err = 0; - if (!authorized(&token)) - { - *err = kmDNSHelperNotAuthorized; - goto fin; - } - switch ((enum mDNSAutoTunnelSetKeysReplaceDelete)replacedelete) - { - case kmDNSAutoTunnelSetKeysReplace: - case kmDNSAutoTunnelSetKeysDelete: - break; - default: - *err = kmDNSHelperInvalidTunnelSetKeysOperation; - goto fin; - } - - if (0 != (*err = v6addr_to_string(loc_inner, li, sizeof(li)))) - goto fin; - if (0 != (*err = v6addr_to_string(rmt_inner, ri, sizeof(ri)))) - goto fin; - - debug("loc_inner=%s rmt_inner=%s", li, ri); - if (!rmt_port) - { - loc_outer[0] = loc_outer[1] = loc_outer[2] = loc_outer[3] = 0; - rmt_outer[0] = rmt_outer[1] = rmt_outer[2] = rmt_outer[3] = 0; - - if (0 != (*err = v6addr_to_string(loc_outer6, lo6, sizeof(lo6)))) - goto fin; - if (0 != (*err = v6addr_to_string(rmt_outer6, ro6, sizeof(ro6)))) - goto fin; - debug("IPv6 outer tunnel: loc_outer6=%s rmt_outer6=%s", lo6, ro6); - if ((int)sizeof(path) <= snprintf(path, sizeof(path), - "%s%s.conf", GetRacoonConfigDir(), ro6)) - { - *err = kmDNSHelperResultTooLarge; - goto fin; - } - } - else - { - loc_outer[0] = loc_outer6[0]; - loc_outer[1] = loc_outer6[1]; - loc_outer[2] = loc_outer6[2]; - loc_outer[3] = loc_outer6[3]; - - rmt_outer[0] = rmt_outer6[0]; - rmt_outer[1] = rmt_outer6[1]; - rmt_outer[2] = rmt_outer6[2]; - rmt_outer[3] = rmt_outer6[3]; - - if (0 != (*err = v4addr_to_string(loc_outer, lo, sizeof(lo)))) - goto fin; - if (0 != (*err = v4addr_to_string(rmt_outer, ro, sizeof(ro)))) - goto fin; - debug("IPv4 outer tunnel: loc_outer=%s loc_port=%u rmt_outer=%s rmt_port=%u", - lo, loc_port, ro, rmt_port); - - if ((int)sizeof(path) <= snprintf(path, sizeof(path), - "%s%s.%u.conf", GetRacoonConfigDir(), ro, - rmt_port)) - { - *err = kmDNSHelperResultTooLarge; - goto fin; - } - } - - - - if (kmDNSAutoTunnelSetKeysReplace == replacedelete) - { - if (0 > ensureExistenceOfRacoonConfigDir(GetRacoonConfigDir())) - { - *err = kmDNSHelperRacoonConfigCreationFailed; - goto fin; - } - if ((int)sizeof(tmp_path) <= - snprintf(tmp_path, sizeof(tmp_path), "%s.XXXXXX", path)) - { - *err = kmDNSHelperResultTooLarge; - goto fin; - } - if (0 > (fd = mkstemp(tmp_path))) - { - helplog(ASL_LEVEL_ERR, "mkstemp \"%s\" failed: %s", - tmp_path, strerror(errno)); - *err = kmDNSHelperRacoonConfigCreationFailed; - goto fin; - } - if (NULL == (fp = fdopen(fd, "w"))) - { - helplog(ASL_LEVEL_ERR, "fdopen: %s", - strerror(errno)); - *err = kmDNSHelperRacoonConfigCreationFailed; - goto fin; - } - fd = -1; - fprintf(fp, config, configHeader, (!rmt_port ? ro6 : ro), rmt_port, id, id, ri, li, li, ri); - fclose(fp); - fp = NULL; - if (0 > rename(tmp_path, path)) - { - helplog(ASL_LEVEL_ERR, - "rename \"%s\" \"%s\" failed: %s", - tmp_path, path, strerror(errno)); - *err = kmDNSHelperRacoonConfigCreationFailed; - goto fin; - } - } - else - { - if (0 != unlink(path)) - debug("unlink \"%s\" failed: %s", path, - strerror(errno)); - } - - if (0 != (*err = doTunnelPolicy(kmDNSTunnelPolicyTeardown, kmDNSNoTunnel, - loc_inner, kWholeV6Mask, loc_outer, loc_port, - rmt_inner, kWholeV6Mask, rmt_outer, rmt_port, loc_outer6, rmt_outer6))) - goto fin; - if (kmDNSAutoTunnelSetKeysReplace == replacedelete && - 0 != (*err = doTunnelPolicy(kmDNSTunnelPolicySetup, (!rmt_port ? kmDNSIPv6IPv6Tunnel : kmDNSIPv6IPv4Tunnel), - loc_inner, kWholeV6Mask, loc_outer, loc_port, - rmt_inner, kWholeV6Mask, rmt_outer, rmt_port, loc_outer6, rmt_outer6))) - goto fin; - - if (0 != (*err = teardownTunnelRoute(rmt_inner))) - goto fin; - if (kmDNSAutoTunnelSetKeysReplace == replacedelete && - 0 != (*err = setupTunnelRoute(loc_inner, rmt_inner))) - goto fin; - - if (kmDNSAutoTunnelSetKeysReplace == replacedelete && - 0 != (*err = kickRacoon())) - goto fin; - - debug("succeeded"); + static const char config[] = + "%s" + "remote %s [%u] {\n" + " disconnect_on_idle idle_timeout 600 idle_direction idle_outbound;\n" + " exchange_mode aggressive;\n" + " doi ipsec_doi;\n" + " situation identity_only;\n" + " verify_identifier off;\n" + " generate_policy on;\n" + " my_identifier user_fqdn \"%s\";\n" + " shared_secret keychain \"%s\";\n" + " nonce_size 16;\n" + " lifetime time 15 min;\n" + " initial_contact on;\n" + " support_proxy on;\n" + " nat_traversal force;\n" + " proposal_check claim;\n" + " proposal {\n" + " encryption_algorithm aes;\n" + " hash_algorithm sha1;\n" + " authentication_method pre_shared_key;\n" + " dh_group 2;\n" + " lifetime time 15 min;\n" + " }\n" + "}\n\n" + "sainfo address %s any address %s any {\n" + " pfs_group 2;\n" + " lifetime time 10 min;\n" + " encryption_algorithm aes;\n" + " authentication_algorithm hmac_sha1;\n" + " compression_algorithm deflate;\n" + "}\n\n" + "sainfo address %s any address %s any {\n" + " pfs_group 2;\n" + " lifetime time 10 min;\n" + " encryption_algorithm aes;\n" + " authentication_algorithm hmac_sha1;\n" + " compression_algorithm deflate;\n" + "}\n"; + char path[PATH_MAX] = ""; + char li[INET6_ADDRSTRLEN], lo[INET_ADDRSTRLEN], lo6[INET6_ADDRSTRLEN], + ri[INET6_ADDRSTRLEN], ro[INET_ADDRSTRLEN], ro6[INET6_ADDRSTRLEN]; + FILE *fp = NULL; + int fd = -1; + char tmp_path[PATH_MAX] = ""; + v4addr_t loc_outer, rmt_outer; + + debug("entry"); + *err = 0; + if (!authorized(&token)) + { + *err = kmDNSHelperNotAuthorized; + goto fin; + } + switch ((enum mDNSAutoTunnelSetKeysReplaceDelete)replacedelete) + { + case kmDNSAutoTunnelSetKeysReplace: + case kmDNSAutoTunnelSetKeysDelete: + break; + default: + *err = kmDNSHelperInvalidTunnelSetKeysOperation; + goto fin; + } + + if (0 != (*err = v6addr_to_string(loc_inner, li, sizeof(li)))) + goto fin; + if (0 != (*err = v6addr_to_string(rmt_inner, ri, sizeof(ri)))) + goto fin; + + debug("loc_inner=%s rmt_inner=%s", li, ri); + if (!rmt_port) + { + loc_outer[0] = loc_outer[1] = loc_outer[2] = loc_outer[3] = 0; + rmt_outer[0] = rmt_outer[1] = rmt_outer[2] = rmt_outer[3] = 0; + + if (0 != (*err = v6addr_to_string(loc_outer6, lo6, sizeof(lo6)))) + goto fin; + if (0 != (*err = v6addr_to_string(rmt_outer6, ro6, sizeof(ro6)))) + goto fin; + debug("IPv6 outer tunnel: loc_outer6=%s rmt_outer6=%s", lo6, ro6); + if ((int)sizeof(path) <= snprintf(path, sizeof(path), + "%s%s.conf", GetRacoonConfigDir(), ro6)) + { + *err = kmDNSHelperResultTooLarge; + goto fin; + } + } + else + { + loc_outer[0] = loc_outer6[0]; + loc_outer[1] = loc_outer6[1]; + loc_outer[2] = loc_outer6[2]; + loc_outer[3] = loc_outer6[3]; + + rmt_outer[0] = rmt_outer6[0]; + rmt_outer[1] = rmt_outer6[1]; + rmt_outer[2] = rmt_outer6[2]; + rmt_outer[3] = rmt_outer6[3]; + + if (0 != (*err = v4addr_to_string(loc_outer, lo, sizeof(lo)))) + goto fin; + if (0 != (*err = v4addr_to_string(rmt_outer, ro, sizeof(ro)))) + goto fin; + debug("IPv4 outer tunnel: loc_outer=%s loc_port=%u rmt_outer=%s rmt_port=%u", + lo, loc_port, ro, rmt_port); + + if ((int)sizeof(path) <= snprintf(path, sizeof(path), + "%s%s.%u.conf", GetRacoonConfigDir(), ro, + rmt_port)) + { + *err = kmDNSHelperResultTooLarge; + goto fin; + } + } + + + + if (kmDNSAutoTunnelSetKeysReplace == replacedelete) + { + if (0 > ensureExistenceOfRacoonConfigDir(GetRacoonConfigDir())) + { + *err = kmDNSHelperRacoonConfigCreationFailed; + goto fin; + } + if ((int)sizeof(tmp_path) <= + snprintf(tmp_path, sizeof(tmp_path), "%s.XXXXXX", path)) + { + *err = kmDNSHelperResultTooLarge; + goto fin; + } + if (0 > (fd = mkstemp(tmp_path))) + { + helplog(ASL_LEVEL_ERR, "mkstemp \"%s\" failed: %s", + tmp_path, strerror(errno)); + *err = kmDNSHelperRacoonConfigCreationFailed; + goto fin; + } + if (NULL == (fp = fdopen(fd, "w"))) + { + helplog(ASL_LEVEL_ERR, "fdopen: %s", + strerror(errno)); + *err = kmDNSHelperRacoonConfigCreationFailed; + goto fin; + } + fd = -1; + fprintf(fp, config, configHeader, (!rmt_port ? ro6 : ro), rmt_port, id, id, ri, li, li, ri); + fclose(fp); + fp = NULL; + if (0 > rename(tmp_path, path)) + { + helplog(ASL_LEVEL_ERR, + "rename \"%s\" \"%s\" failed: %s", + tmp_path, path, strerror(errno)); + *err = kmDNSHelperRacoonConfigCreationFailed; + goto fin; + } + } + else + { + if (0 != unlink(path)) + debug("unlink \"%s\" failed: %s", path, + strerror(errno)); + } + + if (0 != (*err = doTunnelPolicy(kmDNSTunnelPolicyTeardown, kmDNSNoTunnel, + loc_inner, kWholeV6Mask, loc_outer, loc_port, + rmt_inner, kWholeV6Mask, rmt_outer, rmt_port, loc_outer6, rmt_outer6))) + goto fin; + if (kmDNSAutoTunnelSetKeysReplace == replacedelete && + 0 != (*err = doTunnelPolicy(kmDNSTunnelPolicySetup, (!rmt_port ? kmDNSIPv6IPv6Tunnel : kmDNSIPv6IPv4Tunnel), + loc_inner, kWholeV6Mask, loc_outer, loc_port, + rmt_inner, kWholeV6Mask, rmt_outer, rmt_port, loc_outer6, rmt_outer6))) + goto fin; + + if (0 != (*err = teardownTunnelRoute(rmt_inner))) + goto fin; + if (kmDNSAutoTunnelSetKeysReplace == replacedelete && + 0 != (*err = setupTunnelRoute(loc_inner, rmt_inner))) + goto fin; + + if (kmDNSAutoTunnelSetKeysReplace == replacedelete && + 0 != (*err = kickRacoon())) + goto fin; + + debug("succeeded"); fin: - if (NULL != fp) - fclose(fp); - if (0 <= fd) - close(fd); - unlink(tmp_path); + if (NULL != fp) + fclose(fp); + if (0 <= fd) + close(fd); + unlink(tmp_path); #else - (void)replacedelete; (void)loc_inner; (void)loc_outer6; (void)loc_port; (void)rmt_inner; - (void)rmt_outer6; (void)rmt_port; (void)id; (void)token; - - *err = kmDNSHelperIPsecDisabled; + (void)replacedelete; (void)loc_inner; (void)loc_outer6; (void)loc_port; (void)rmt_inner; + (void)rmt_outer6; (void)rmt_port; (void)id; (void)token; + + *err = kmDNSHelperIPsecDisabled; #endif /* MDNS_NO_IPSEC */ - update_idle_timer(); - return KERN_SUCCESS; - } + update_idle_timer(); + return KERN_SUCCESS; +} kern_return_t do_mDNSSendWakeupPacket(__unused mach_port_t port, unsigned ifid, const char *eth_addr, const char *ip_addr, int iteration, audit_token_t token) - { - int bpf_fd, i, j; - struct ifreq ifr; - char ifname[IFNAMSIZ]; - char packet[512]; - char *ptr = packet; - char bpf_device[12]; +{ + int bpf_fd, i, j; + struct ifreq ifr; + char ifname[IFNAMSIZ]; + char packet[512]; + char *ptr = packet; + char bpf_device[12]; struct ether_addr *ea; - (void) ip_addr; // unused - (void) iteration; // unused - (void) token; // unused + (void) ip_addr; // unused + (void) iteration; // unused + (void) token; // unused - if (if_indextoname(ifid, ifname) == NULL) - { - helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket invalid interface index %u", ifid); - return errno; - } + if (if_indextoname(ifid, ifname) == NULL) + { + helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket invalid interface index %u", ifid); + return errno; + } ea = ether_aton(eth_addr); - if (ea == NULL) - { - helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket invalid ethernet address %s", eth_addr); - return errno; - } - - for (i = 0; i < 100; i++) - { + if (ea == NULL) + { + helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket invalid ethernet address %s", eth_addr); + return errno; + } + + for (i = 0; i < 100; i++) + { snprintf(bpf_device, sizeof(bpf_device), "/dev/bpf%d", i); - bpf_fd = open(bpf_device, O_RDWR, 0); - if (bpf_fd == -1) - continue; - else break; - } - - if (bpf_fd == -1) - { - helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket cannot find a bpf device"); - return ENXIO; - } - - memset(&ifr, 0, sizeof(ifr)); - strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); - - if (ioctl(bpf_fd, BIOCSETIF, (char *)&ifr) < 0) - { - helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket BIOCSETIF failed %s", strerror(errno)); - return errno; - } - - // 0x00 Destination address + bpf_fd = open(bpf_device, O_RDWR, 0); + if (bpf_fd == -1) + continue; + else break; + } + + if (bpf_fd == -1) + { + helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket cannot find a bpf device"); + return ENXIO; + } + + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + + if (ioctl(bpf_fd, BIOCSETIF, (char *)&ifr) < 0) + { + helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket BIOCSETIF failed %s", strerror(errno)); + return errno; + } + + // 0x00 Destination address for (i=0; i<6; i++) *ptr++ = ea->octet[i]; // 0x06 Source address (Note: Since we don't currently set the BIOCSHDRCMPLT option, BPF will fill in the real interface address for us) @@ -2449,62 +2367,319 @@ do_mDNSSendWakeupPacket(__unused mach_port_t port, unsigned ifid, const char *et // 0x74 Password for (i=0; i<6; i++) *ptr++ = 0; - if (write(bpf_fd, packet, ptr - packet) < 0) - { - helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket write failed %s", strerror(errno)); - return errno; - } - helplog(ASL_LEVEL_INFO, "do_mDNSSendWakeupPacket sent unicast eth_addr %s, ip_addr %s", eth_addr, ip_addr); - // Send a broadcast one to handle ethernet switches that don't flood forward packets with - // unknown mac addresses. - for (i=0; i<6; i++) packet[i] = 0xFF; - if (write(bpf_fd, packet, ptr - packet) < 0) - { - helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket write failed %s", strerror(errno)); - return errno; - } - helplog(ASL_LEVEL_INFO, "do_mDNSSendWakeupPacket sent broadcast eth_addr %s, ip_addr %s", eth_addr, ip_addr); - close(bpf_fd); - return KERN_SUCCESS; - } + if (write(bpf_fd, packet, ptr - packet) < 0) + { + helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket write failed %s", strerror(errno)); + return errno; + } + helplog(ASL_LEVEL_INFO, "do_mDNSSendWakeupPacket sent unicast eth_addr %s, ip_addr %s", eth_addr, ip_addr); + // Send a broadcast one to handle ethernet switches that don't flood forward packets with + // unknown mac addresses. + for (i=0; i<6; i++) packet[i] = 0xFF; + if (write(bpf_fd, packet, ptr - packet) < 0) + { + helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket write failed %s", strerror(errno)); + return errno; + } + helplog(ASL_LEVEL_INFO, "do_mDNSSendWakeupPacket sent broadcast eth_addr %s, ip_addr %s", eth_addr, ip_addr); + close(bpf_fd); + return KERN_SUCCESS; +} // Open the specified port for protocol in the P2P firewall. kern_return_t -do_mDNSPacketFilterControl(__unused mach_port_t port, uint32_t command, const char * ifname, uint16_t servicePort, uint16_t protocol, audit_token_t token) - { - (void) token; // unused - int error; - kern_return_t result = KERN_SUCCESS; - - helplog(ASL_LEVEL_INFO, "do_mDNSPacketFilterControl: command %d ifname %s, servicePort 0x%x, protocol %d", - command, ifname, servicePort, protocol); - - switch (command) - { - case PF_SET_RULES: - error = P2PPacketFilterAddBonjourRuleSet(ifname, servicePort, protocol); - if (error) - { - helplog(ASL_LEVEL_ERR, "P2PPacketFilterAddBonjourRuleSet failed %s", strerror(error)); - result = KERN_FAILURE; - } - break; - - case PF_CLEAR_RULES: - error = P2PPacketFilterClearBonjourRules(); - if (error) - { - helplog(ASL_LEVEL_ERR, "P2PPacketFilterClearBonjourRules failed %s", strerror(error)); - result = KERN_FAILURE; - } - break; - - default: - helplog(ASL_LEVEL_ERR, "do_mDNSPacketFilterControl: invalid command %d", command); - result = KERN_INVALID_ARGUMENT; - break; - } - - return result; - } - +do_mDNSPacketFilterControl(__unused mach_port_t port, uint32_t command, const char * ifname, uint32_t count, pfArray_t portArray, pfArray_t protocolArray, audit_token_t token) +{ + (void) token; // unused + int error; + kern_return_t result = KERN_SUCCESS; + + helplog(ASL_LEVEL_INFO, "do_mDNSPacketFilterControl: command %d ifname %s, count %d", + command, ifname, count); + + switch (command) + { + case PF_SET_RULES: + error = P2PPacketFilterAddBonjourRuleSet(ifname, count, portArray, protocolArray); + if (error) + { + helplog(ASL_LEVEL_ERR, "P2PPacketFilterAddBonjourRuleSet failed %s", strerror(error)); + result = KERN_FAILURE; + } + break; + + case PF_CLEAR_RULES: + error = P2PPacketFilterClearBonjourRules(); + if (error) + { + helplog(ASL_LEVEL_ERR, "P2PPacketFilterClearBonjourRules failed %s", strerror(error)); + result = KERN_FAILURE; + } + break; + + default: + helplog(ASL_LEVEL_ERR, "do_mDNSPacketFilterControl: invalid command %d", command); + result = KERN_INVALID_ARGUMENT; + break; + } + + return result; +} + +unsigned long +in_cksum(unsigned short *ptr,int nbytes) +{ + unsigned long sum; + u_short oddbyte; + + /* + * Our algorithm is simple, using a 32-bit accumulator (sum), + * we add sequential 16-bit words to it, and at the end, fold back + * all the carry bits from the top 16 bits into the lower 16 bits. + */ + sum = 0; + while (nbytes > 1) { + sum += *ptr++; + nbytes -= 2; + } + + /* mop up an odd byte, if necessary */ + if (nbytes == 1) { + /* make sure top half is zero */ + oddbyte = 0; + + /* one byte only */ + *((u_char *)&oddbyte) = *(u_char *)ptr; + sum += oddbyte; + } + /* Add back carry outs from top 16 bits to low 16 bits. */ + sum = (sum >> 16) + (sum & 0xffff); + + /* add carry */ + sum += (sum >> 16); + + return sum; +} + +unsigned short +InetChecksum(unsigned short *ptr,int nbytes) +{ + unsigned long sum; + + sum = in_cksum(ptr, nbytes); + return (unsigned short)~sum; +} + +void TCPCheckSum(int af, struct tcphdr *t, int tcplen, v6addr_t sadd6, v6addr_t dadd6) +{ + unsigned long sum = 0; + unsigned short *ptr; + + /* TCP header checksum */ + sum = in_cksum((unsigned short *)t, tcplen); + + if (af == AF_INET) + { + /* Pseudo header */ + ptr = (unsigned short *)sadd6; + sum += *ptr++; + sum += *ptr++; + ptr = (unsigned short *)dadd6; + sum += *ptr++; + sum += *ptr++; + } + else if (af == AF_INET6) + { + /* Pseudo header */ + ptr = (unsigned short *)sadd6; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + ptr = (unsigned short *)dadd6; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + } + + sum += htons(tcplen); + sum += htons(IPPROTO_TCP); + + while (sum >> 16) + sum = (sum >> 16) + (sum & 0xFFFF); + + t->th_sum = ~sum; + +} + +kern_return_t do_mDNSSendKeepalive(__unused mach_port_t port, v6addr_t sadd6, v6addr_t dadd6, uint16_t lport, uint16_t rport, unsigned seq, unsigned ack, uint16_t win, audit_token_t token) +{ + struct packet4 { + struct ip ip; + struct tcphdr tcp; + } packet4; + struct packet6 { + struct tcphdr tcp; + } packet6; + int sock, on; + struct tcphdr *t; + int af; + struct sockaddr_storage ss_to; + struct sockaddr_in *sin_to = (struct sockaddr_in *)&ss_to; + struct sockaddr_in6 *sin6_to = (struct sockaddr_in6 *)&ss_to; + void *packet; + ssize_t packetlen; + char ctlbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + struct msghdr msghdr; + struct iovec iov; + ssize_t len; + + if (!authorized(&token)) + { + helplog(ASL_LEVEL_ERR, "mDNSSendKeepalive: Not authorized"); + return kmDNSHelperNotAuthorized; + } + + helplog(ASL_LEVEL_ERR, "mDNSSendKeepalive: called"); + + // all the incoming arguments are in network order + if ((*(unsigned *)(sadd6 +4) == 0) && (*(unsigned *)(sadd6 + 8) == 0) && (*(unsigned *)(sadd6 + 12) == 0)) + { + af = AF_INET; + memset(&packet4, 0, sizeof (packet4)); + + /* Fill in all the IP header information - should be in host order*/ + packet4.ip.ip_v = 4; /* 4-bit Version */ + packet4.ip.ip_hl = 5; /* 4-bit Header Length */ + packet4.ip.ip_tos = 0; /* 8-bit Type of service */ + packet4.ip.ip_len = 40; /* 16-bit Total length */ + packet4.ip.ip_id = 9864; /* 16-bit ID field */ + packet4.ip.ip_off = 0; /* 13-bit Fragment offset */ + packet4.ip.ip_ttl = 63; /* 8-bit Time To Live */ + packet4.ip.ip_p = IPPROTO_TCP; /* 8-bit Protocol */ + packet4.ip.ip_sum = 0; /* 16-bit Header checksum (below) */ + memcpy(&packet4.ip.ip_src.s_addr, sadd6, 4); + memcpy(&packet4.ip.ip_dst.s_addr, dadd6, 4); + + /* IP header checksum */ + packet4.ip.ip_sum = InetChecksum((unsigned short *)&packet4.ip, 20); + t = &packet4.tcp; + packet = &packet4; + packetlen = 40; // sum of IPv4 header len(20) and TCP header len(20) + } + else + { + af = AF_INET6; + memset(&packet6, 0, sizeof (packet6)); + t = &packet6.tcp; + packet = &packet6; + // We don't send IPv6 header, hence just the TCP header len (20) + packetlen = 20; + } + + /* Fill in all the TCP header information */ + t->th_sport = lport; /* 16-bit Source port number */ + t->th_dport = rport; /* 16-bit Destination port */ + t->th_seq = seq; /* 32-bit Sequence Number */ + t->th_ack = ack; /* 32-bit Acknowledgement Number */ + t->th_off = 5; /* Data offset */ + t->th_flags = TH_ACK; + t->th_win = win; + t->th_sum = 0; /* 16-bit checksum (below) */ + t->th_urp = 0; /* 16-bit urgent offset */ + + TCPCheckSum(af, t, 20, sadd6, dadd6); + + /* Open up a RAW socket */ + if ((sock = socket(af, SOCK_RAW, IPPROTO_TCP)) < 0) + { + helplog(ASL_LEVEL_ERR, "mDNSSendKeepalive: socket %s", strerror(errno)); + return errno; + } + + + if (af == AF_INET) + { + on = 1; + if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &on, sizeof (on))) + { + close(sock); + helplog(ASL_LEVEL_ERR, "mDNSSendKeepalive: setsockopt %s", strerror(errno)); + return errno; + } + + memset(sin_to, 0, sizeof(struct sockaddr_in)); + sin_to->sin_len = sizeof(struct sockaddr_in); + sin_to->sin_family = AF_INET; + memcpy(&sin_to->sin_addr, sadd6, sizeof(struct in_addr)); + sin_to->sin_port = rport; + + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; + + } + else + { + struct cmsghdr *ctl; + + memset(sin6_to, 0, sizeof(struct sockaddr_in6)); + sin6_to->sin6_len = sizeof(struct sockaddr_in6); + sin6_to->sin6_family = AF_INET6; + memcpy(&sin6_to->sin6_addr, dadd6, sizeof(struct in6_addr)); + + sin6_to->sin6_port = rport; + sin6_to->sin6_flowinfo = 0; + + + msghdr.msg_control = ctlbuf; + msghdr.msg_controllen = sizeof(ctlbuf); + ctl = CMSG_FIRSTHDR(&msghdr); + ctl->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + ctl->cmsg_level = IPPROTO_IPV6; + ctl->cmsg_type = IPV6_PKTINFO; + struct in6_pktinfo *pktinfo = (struct in6_pktinfo *) CMSG_DATA(ctl); + memcpy(&pktinfo->ipi6_addr, sadd6, sizeof(struct in6_addr)); + pktinfo->ipi6_ifindex = 0; + } + + msghdr.msg_name = (struct sockaddr *)&ss_to; + msghdr.msg_namelen = ss_to.ss_len; + iov.iov_base = packet; + iov.iov_len = packetlen; + msghdr.msg_iov = &iov; + msghdr.msg_iovlen = 1; + msghdr.msg_flags = 0; +again: + len = sendmsg(sock, &msghdr, 0); + if (len == -1) + { + if (errno == EINTR) + goto again; + } + + if (len != packetlen) + { + helplog(ASL_LEVEL_ERR, "mDNSSendKeepalive: sendmsg failed %s", strerror(errno)); + } + else + { + char source[INET6_ADDRSTRLEN], dest[INET6_ADDRSTRLEN]; + + inet_ntop(af, (void *)sadd6, source, sizeof(source)); + inet_ntop(af, (void *)dadd6, dest, sizeof(dest)); + + helplog(ASL_LEVEL_ERR, "mDNSSendKeepalive: Success Source %s:%d, Dest %s:%d, %u, %u, %u", source, ntohs(lport), dest, ntohs(rport), ntohl(seq), ntohl(ack), ntohs(win)); + + } + close(sock); + return KERN_SUCCESS; +} diff --git a/mDNSMacOSX/helper.h b/mDNSMacOSX/helper.h index dadf5d9..c7dabc5 100644 --- a/mDNSMacOSX/helper.h +++ b/mDNSMacOSX/helper.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2007 Apple Inc. All rights reserved. + * Copyright (c) 2007-2012 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,50 +21,50 @@ #define kmDNSHelperServiceName "com.apple.mDNSResponderHelper" enum mDNSDynamicStoreSetConfigKey - { - kmDNSMulticastConfig = 1, - kmDNSDynamicConfig, - kmDNSPrivateConfig, - kmDNSBackToMyMacConfig, - kmDNSSleepProxyServersState - }; +{ + kmDNSMulticastConfig = 1, + kmDNSDynamicConfig, + kmDNSPrivateConfig, + kmDNSBackToMyMacConfig, + kmDNSSleepProxyServersState +}; enum mDNSPreferencesSetNameKey - { - kmDNSComputerName = 1, - kmDNSLocalHostName - }; +{ + kmDNSComputerName = 1, + kmDNSLocalHostName +}; enum mDNSUpDown - { - kmDNSUp = 1, - kmDNSDown - }; +{ + kmDNSUp = 1, + kmDNSDown +}; enum mDNSAutoTunnelSetKeysReplaceDelete - { - kmDNSAutoTunnelSetKeysReplace = 1, - kmDNSAutoTunnelSetKeysDelete - }; +{ + kmDNSAutoTunnelSetKeysReplace = 1, + kmDNSAutoTunnelSetKeysDelete +}; // helper parses the system keychain and returns the information to mDNSResponder. // It returns four attributes. Attributes are defined after how they show up in // keychain access utility (the actual attribute name to retrieve these are different). enum mDNSKeyChainAttributes - { - kmDNSKcWhere, // Where - kmDNSKcAccount, // Account - kmDNSKcKey, // Key - kmDNSKcName // Name - }; +{ + kmDNSKcWhere, // Where + kmDNSKcAccount, // Account + kmDNSKcKey, // Key + kmDNSKcName // Name +}; #define ERROR(x, y) x, enum mDNSHelperErrors - { - mDNSHelperErrorBase = 2300, - #include "helper-error.h" - mDNSHelperErrorEnd - }; +{ + mDNSHelperErrorBase = 2300, + #include "helper-error.h" + mDNSHelperErrorEnd +}; #undef ERROR #include "mDNSEmbeddedAPI.h" @@ -75,16 +75,17 @@ extern const char *mDNSHelperError(int errornum); extern void mDNSRequestBPF(void); extern int mDNSPowerRequest(int key, int interval); extern int mDNSSetLocalAddressCacheEntry(int ifindex, int family, const v6addr_t ip, const ethaddr_t eth); -extern void mDNSNotify(const char *title, const char *msg); // Both strings are UTF-8 text +extern void mDNSNotify(const char *title, const char *msg); // Both strings are UTF-8 text extern void mDNSDynamicStoreSetConfig(int key, const char *subkey, CFPropertyListRef value); extern void mDNSPreferencesSetName(int key, domainlabel *old, domainlabel *new); extern int mDNSKeychainGetSecrets(CFArrayRef *secrets); -extern void mDNSAutoTunnelInterfaceUpDown(int updown, v6addr_t addr); extern void mDNSConfigureServer(int updown, const char *const prefix, const domainname *const fqdn); extern int mDNSAutoTunnelSetKeys(int replacedelete, v6addr_t local_inner, - v6addr_t local_outer, short local_port, v6addr_t remote_inner, - v6addr_t remote_outer, short remote_port, const char *const prefix, const domainname *const fqdn); + v6addr_t local_outer, short local_port, v6addr_t remote_inner, + v6addr_t remote_outer, short remote_port, const char *const prefix, const domainname *const fqdn); extern void mDNSSendWakeupPacket(unsigned ifid, char *eth_addr, char *ip_addr, int iteration); -extern void mDNSPacketFilterControl(uint32_t command, char * ifname, uint16_t servicePort, uint16_t protocol); +extern void mDNSPacketFilterControl(uint32_t command, char * ifname, uint32_t count, pfArray_t portArray, pfArray_t protocolArray); +extern int mDNSSendKeepalive(v6addr_t sadd, v6addr_t dadd, uint16_t lport, uint16_t rport, unsigned seq, unsigned ack, uint16_t win); +extern int mDNSInterfaceAdvtIoctl(const char *ifname, int op); #endif /* H_HELPER_H */ diff --git a/mDNSMacOSX/helpermsg-types.h b/mDNSMacOSX/helpermsg-types.h index 8e36635..ca5b140 100644 --- a/mDNSMacOSX/helpermsg-types.h +++ b/mDNSMacOSX/helpermsg-types.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -24,4 +24,7 @@ typedef uint8_t ethaddr_t[ 6]; typedef uint8_t v6addr_t [16]; typedef const char *string_t; +#define PFPortArraySize 16 +typedef uint16_t pfArray_t [PFPortArraySize]; + #endif /* H_HELPERMSG_TYPES_H */ diff --git a/mDNSMacOSX/helpermsg.defs b/mDNSMacOSX/helpermsg.defs index 5026078..02c8af4 100644 --- a/mDNSMacOSX/helpermsg.defs +++ b/mDNSMacOSX/helpermsg.defs @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2007 Apple Inc. All rights reserved. + * Copyright (c) 2007-2012 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,9 @@ type ethaddr_t = array [ 6] of uint8_t; type v6addr_t = array [16] of uint8_t; type string_t = c_string[*:1024]; +// Mig doesn't generate the output file if I use the constant PFPortArraySize below +type pfArray_t = array [16] of uint16_t; + subsystem helper 1833193043; serverprefix do_; userprefix proxy_; @@ -75,12 +78,6 @@ routine mDNSKeychainGetSecrets( port : mach_port_t; out err : int; ServerAuditToken token : audit_token_t); -simpleroutine mDNSAutoTunnelInterfaceUpDown( - port : mach_port_t; - updown : int; - address : v6addr_t; - ServerAuditToken token : audit_token_t); - simpleroutine mDNSConfigureServer( port : mach_port_t; updown : int; @@ -111,6 +108,23 @@ simpleroutine mDNSPacketFilterControl( port : mach_port_t; command : uint32_t; ifname : string_t; - servicePort : uint16_t; - protocol : uint16_t; + arraySize : uint32_t; + portArray : pfArray_t; + protocolArray : pfArray_t; ServerAuditToken token : audit_token_t); + + +simpleroutine mDNSSendKeepalive( port : mach_port_t; + sadd : v6addr_t; + dadd : v6addr_t; + lport : uint16_t; + rport : uint16_t; + seq : unsigned; + ack : unsigned; + win : uint16_t; + ServerAuditToken token : audit_token_t); + +simpleroutine mDNSInterfaceAdvtIoctl( port : mach_port_t; + ifname : string_t; + op : int; + ServerAuditToken token : audit_token_t); diff --git a/mDNSMacOSX/ipsec_strerror.h b/mDNSMacOSX/ipsec_strerror.h index 4104f85..ecacf3b 100644 --- a/mDNSMacOSX/ipsec_strerror.h +++ b/mDNSMacOSX/ipsec_strerror.h @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -48,31 +48,31 @@ extern int __ipsec_errcode; extern void __ipsec_set_strerror __P((const char *)); -#define EIPSEC_NO_ERROR 0 /*success*/ -#define EIPSEC_NOT_SUPPORTED 1 /*not supported*/ -#define EIPSEC_INVAL_ARGUMENT 2 /*invalid argument*/ -#define EIPSEC_INVAL_SADBMSG 3 /*invalid sadb message*/ -#define EIPSEC_INVAL_VERSION 4 /*invalid version*/ -#define EIPSEC_INVAL_POLICY 5 /*invalid security policy*/ -#define EIPSEC_INVAL_ADDRESS 6 /*invalid address specification*/ -#define EIPSEC_INVAL_PROTO 7 /*invalid ipsec protocol*/ -#define EIPSEC_INVAL_MODE 8 /*Invalid ipsec mode*/ -#define EIPSEC_INVAL_LEVEL 9 /*invalid ipsec level*/ -#define EIPSEC_INVAL_SATYPE 10 /*invalid SA type*/ -#define EIPSEC_INVAL_MSGTYPE 11 /*invalid message type*/ -#define EIPSEC_INVAL_EXTTYPE 12 /*invalid extension type*/ -#define EIPSEC_INVAL_ALGS 13 /*Invalid algorithm type*/ -#define EIPSEC_INVAL_KEYLEN 14 /*invalid key length*/ -#define EIPSEC_INVAL_FAMILY 15 /*invalid address family*/ -#define EIPSEC_INVAL_PREFIXLEN 16 /*SPI range violation*/ -#define EIPSEC_INVAL_DIR 17 /*Invalid direciton*/ -#define EIPSEC_INVAL_SPI 18 /*invalid prefixlen*/ -#define EIPSEC_NO_PROTO 19 /*no protocol specified*/ -#define EIPSEC_NO_ALGS 20 /*No algorithm specified*/ -#define EIPSEC_NO_BUFS 21 /*no buffers available*/ -#define EIPSEC_DO_GET_SUPP_LIST 22 /*must get supported algorithm first*/ -#define EIPSEC_PROTO_MISMATCH 23 /*protocol mismatch*/ -#define EIPSEC_FAMILY_MISMATCH 24 /*family mismatch*/ -#define EIPSEC_FEW_ARGUMENTS 25 /*Too few arguments*/ -#define EIPSEC_SYSTEM_ERROR 26 /*system error*/ -#define EIPSEC_MAX 27 /*unknown error*/ +#define EIPSEC_NO_ERROR 0 /*success*/ +#define EIPSEC_NOT_SUPPORTED 1 /*not supported*/ +#define EIPSEC_INVAL_ARGUMENT 2 /*invalid argument*/ +#define EIPSEC_INVAL_SADBMSG 3 /*invalid sadb message*/ +#define EIPSEC_INVAL_VERSION 4 /*invalid version*/ +#define EIPSEC_INVAL_POLICY 5 /*invalid security policy*/ +#define EIPSEC_INVAL_ADDRESS 6 /*invalid address specification*/ +#define EIPSEC_INVAL_PROTO 7 /*invalid ipsec protocol*/ +#define EIPSEC_INVAL_MODE 8 /*Invalid ipsec mode*/ +#define EIPSEC_INVAL_LEVEL 9 /*invalid ipsec level*/ +#define EIPSEC_INVAL_SATYPE 10 /*invalid SA type*/ +#define EIPSEC_INVAL_MSGTYPE 11 /*invalid message type*/ +#define EIPSEC_INVAL_EXTTYPE 12 /*invalid extension type*/ +#define EIPSEC_INVAL_ALGS 13 /*Invalid algorithm type*/ +#define EIPSEC_INVAL_KEYLEN 14 /*invalid key length*/ +#define EIPSEC_INVAL_FAMILY 15 /*invalid address family*/ +#define EIPSEC_INVAL_PREFIXLEN 16 /*SPI range violation*/ +#define EIPSEC_INVAL_DIR 17 /*Invalid direciton*/ +#define EIPSEC_INVAL_SPI 18 /*invalid prefixlen*/ +#define EIPSEC_NO_PROTO 19 /*no protocol specified*/ +#define EIPSEC_NO_ALGS 20 /*No algorithm specified*/ +#define EIPSEC_NO_BUFS 21 /*no buffers available*/ +#define EIPSEC_DO_GET_SUPP_LIST 22 /*must get supported algorithm first*/ +#define EIPSEC_PROTO_MISMATCH 23 /*protocol mismatch*/ +#define EIPSEC_FAMILY_MISMATCH 24 /*family mismatch*/ +#define EIPSEC_FEW_ARGUMENTS 25 /*Too few arguments*/ +#define EIPSEC_SYSTEM_ERROR 26 /*system error*/ +#define EIPSEC_MAX 27 /*unknown error*/ diff --git a/mDNSMacOSX/libpfkey.h b/mDNSMacOSX/libpfkey.h index 077414b..98d192d 100644 --- a/mDNSMacOSX/libpfkey.h +++ b/mDNSMacOSX/libpfkey.h @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -57,21 +57,21 @@ int ipsec_get_keylen __P((u_int, u_int, struct sadb_alg *)); u_int pfkey_set_softrate __P((u_int, u_int)); u_int pfkey_get_softrate __P((u_int)); int pfkey_send_getspi __P((int, u_int, u_int, struct sockaddr *, - struct sockaddr *, u_int32_t, u_int32_t, u_int32_t, u_int32_t)); + struct sockaddr *, u_int32_t, u_int32_t, u_int32_t, u_int32_t)); int pfkey_send_update __P((int, u_int, u_int, struct sockaddr *, - struct sockaddr *, u_int32_t, u_int32_t, u_int, - caddr_t, u_int, u_int, u_int, u_int, u_int, u_int32_t, u_int64_t, - u_int64_t, u_int64_t, u_int32_t)); + struct sockaddr *, u_int32_t, u_int32_t, u_int, + caddr_t, u_int, u_int, u_int, u_int, u_int, u_int32_t, u_int64_t, + u_int64_t, u_int64_t, u_int32_t)); int pfkey_send_add __P((int, u_int, u_int, struct sockaddr *, - struct sockaddr *, u_int32_t, u_int32_t, u_int, - caddr_t, u_int, u_int, u_int, u_int, u_int, u_int32_t, u_int64_t, - u_int64_t, u_int64_t, u_int32_t)); + struct sockaddr *, u_int32_t, u_int32_t, u_int, + caddr_t, u_int, u_int, u_int, u_int, u_int, u_int32_t, u_int64_t, + u_int64_t, u_int64_t, u_int32_t)); int pfkey_send_delete __P((int, u_int, u_int, - struct sockaddr *, struct sockaddr *, u_int32_t)); + struct sockaddr *, struct sockaddr *, u_int32_t)); int pfkey_send_delete_all __P((int, u_int, u_int, - struct sockaddr *, struct sockaddr *)); + struct sockaddr *, struct sockaddr *)); int pfkey_send_get __P((int, u_int, u_int, - struct sockaddr *, struct sockaddr *, u_int32_t)); + struct sockaddr *, struct sockaddr *, u_int32_t)); int pfkey_send_register __P((int, u_int)); int pfkey_recv_register __P((int)); int pfkey_set_supported __P((struct sadb_msg *, int)); @@ -79,21 +79,21 @@ int pfkey_send_flush __P((int, u_int)); int pfkey_send_dump __P((int, u_int)); int pfkey_send_promisc_toggle __P((int, int)); int pfkey_send_spdadd __P((int, struct sockaddr *, u_int, - struct sockaddr *, u_int, u_int, caddr_t, int, u_int32_t)); + struct sockaddr *, u_int, u_int, caddr_t, int, u_int32_t)); int pfkey_send_spdadd2 __P((int, struct sockaddr *, u_int, - struct sockaddr *, u_int, u_int, u_int64_t, u_int64_t, - caddr_t, int, u_int32_t)); + struct sockaddr *, u_int, u_int, u_int64_t, u_int64_t, + caddr_t, int, u_int32_t)); int pfkey_send_spdupdate __P((int, struct sockaddr *, u_int, - struct sockaddr *, u_int, u_int, caddr_t, int, u_int32_t)); + struct sockaddr *, u_int, u_int, caddr_t, int, u_int32_t)); int pfkey_send_spdupdate2 __P((int, struct sockaddr *, u_int, - struct sockaddr *, u_int, u_int, u_int64_t, u_int64_t, - caddr_t, int, u_int32_t)); + struct sockaddr *, u_int, u_int, u_int64_t, u_int64_t, + caddr_t, int, u_int32_t)); int pfkey_send_spddelete __P((int, struct sockaddr *, u_int, - struct sockaddr *, u_int, u_int, caddr_t, int, u_int32_t)); + struct sockaddr *, u_int, u_int, caddr_t, int, u_int32_t)); int pfkey_send_spddelete2 __P((int, u_int32_t)); int pfkey_send_spdget __P((int, u_int32_t)); int pfkey_send_spdsetidx __P((int, struct sockaddr *, u_int, - struct sockaddr *, u_int, u_int, caddr_t, int, u_int32_t)); + struct sockaddr *, u_int, u_int, caddr_t, int, u_int32_t)); int pfkey_send_spdflush __P((int)); int pfkey_send_spddump __P((int)); diff --git a/mDNSMacOSX/mDNSMacOSX.c b/mDNSMacOSX/mDNSMacOSX.c index fa4a833..d33e100 100644 --- a/mDNSMacOSX/mDNSMacOSX.c +++ b/mDNSMacOSX/mDNSMacOSX.c @@ -1,13 +1,13 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2011 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2012 Apple Computer, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -41,21 +41,23 @@ #define USE_V6_ONLY_WHEN_NO_ROUTABLE_V4 0 -#include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above +#include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above #include "DNSCommon.h" #include "uDNS.h" -#include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform -#include "dns_sd.h" // For mDNSInterface_LocalOnly etc. +#include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform +#include "dns_sd.h" // For mDNSInterface_LocalOnly etc. #include "PlatformCommon.h" #include "uds_daemon.h" +#include "CryptoSupport.h" +#include "MobileInternetSharing_priv.h" #include #include // For va_list support #include // For arc4random #include -#include // For IFT_ETHER +#include // For IFT_ETHER #include -#include // For BIOCSETIF etc. +#include // For BIOCSETIF etc. #include #include #include @@ -66,7 +68,8 @@ #include // platform support for UTC time #include // for inet_aton #include -#include // for getaddrinfo +#include // for getaddrinfo +#include // for SIOCGIFEFLAGS #include // For IP_RECVTTL #ifndef IP_RECVTTL @@ -78,6 +81,8 @@ #include // For IN6_IFF_NOTREADY etc. #include // For ND6_INFINITE_LIFETIME etc. +#include + #if TARGET_OS_EMBEDDED #define NO_SECURITYFRAMEWORK 1 #define NO_CFUSERNOTIFICATION 1 @@ -91,25 +96,11 @@ #include #include "dnsinfo.h" -// Code contributed by Dave Heller: -// Define RUN_ON_PUMA_WITHOUT_IFADDRS to compile code that will -// work on Mac OS X 10.1, which does not have the getifaddrs call. -#define RUN_ON_PUMA_WITHOUT_IFADDRS 0 -#if RUN_ON_PUMA_WITHOUT_IFADDRS -#include "mDNSMacOSXPuma.c" -#else #include -#endif #include #include -#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM -// This is currently defined in IOKit/PrivateHeaders/IOKitLibPrivate.h. Till it becomes an Public -// API, we will have our own declaration -void IONotificationPortSetDispatchQueue(IONotificationPortRef notify, dispatch_queue_t queue); -#endif - #if USE_IOPMCOPYACTIVEPMPREFERENCES #include #include @@ -123,13 +114,20 @@ void IONotificationPortSetDispatchQueue(IONotificationPortRef notify, dispatch_q #include +#if DNSINFO_VERSION >= 20110420 +#include +#else #include +#endif // DNSINFO_VERSION >= 20110420 + +// Include definition of opaque_presence_indication for KEV_DL_NODE_PRESENCE handling logic. +#include #if APPLE_OSX_mDNSResponder #include #include -#if ! NO_D2D +#if !NO_D2D D2DStatus D2DInitialize(CFRunLoopRef runLoop, D2DServiceCallback serviceCallback, void* userData) __attribute__((weak_import)); D2DStatus D2DTerminate() __attribute__((weak_import)); D2DStatus D2DStartAdvertisingPair(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize) __attribute__((weak_import)); @@ -141,8 +139,6 @@ void D2DStopResolvingPair(const Byte *key, const size_t keySize, const Byte *val D2DStatus D2DRetain(D2DServiceInstance instanceHandle, D2DTransportType transportType) __attribute__((weak_import)); D2DStatus D2DRelease(D2DServiceInstance instanceHandle, D2DTransportType transportType) __attribute__((weak_import)); -#define CHECK_D2D_FUNCTION(X) if (X) - #endif // ! NO_D2D #else @@ -152,6 +148,9 @@ D2DStatus D2DRelease(D2DServiceInstance instanceHandle, D2DTransportType transpo #define kInterfaceSpecificOption "interface=" +// cache the InterfaceID of the AWDL interface +static mDNSInterfaceID AWDLInterfaceID; + // *************************************************************************** // Globals @@ -165,10 +164,8 @@ D2DStatus D2DRelease(D2DServiceInstance instanceHandle, D2DTransportType transpo // We currently do not offer sleep proxy service on laptops, or on machines that are set to go to sleep. mDNSexport int OfferSleepProxyService = 0; mDNSexport int DisableSleepProxyClient = 0; -mDNSexport int UseInternalSleepProxy = 1; // Set to non-zero to use internal (in-NIC) Sleep Proxy +mDNSexport int UseInternalSleepProxy = 1; // Set to non-zero to use internal (in-NIC) Sleep Proxy -// We disable inbound relay connection if this value is set to true (typically via command-line switch). -mDNSBool DisableInboundRelayConnection = mDNSfalse; mDNSexport int OSXVers, iOSVers; mDNSexport int KQueueFD; @@ -186,10 +183,11 @@ static CFStringRef NetworkChangedKey_DynamicDNS = CFSTR("Setup:/Network/Dy static CFStringRef NetworkChangedKey_BackToMyMac = CFSTR("Setup:/Network/BackToMyMac"); static CFStringRef NetworkChangedKey_BTMMConnectivity = CFSTR("State:/Network/Connectivity"); static CFStringRef NetworkChangedKey_PowerSettings = CFSTR("State:/IOKit/PowerManagement/CurrentSettings"); +static CFStringRef NetworkChangedKey_InternetSharing = CFSTR("com.apple.InternetSharing"); -static char HINFO_HWstring_buffer[32]; +static char HINFO_HWstring_buffer[32]; static char *HINFO_HWstring = "Device"; -static int HINFO_HWstring_prefixlen = 6; +static int HINFO_HWstring_prefixlen = 6; mDNSexport int WatchDogReportingThreshold = 250; @@ -201,9 +199,10 @@ dispatch_queue_t SSLqueue; static mDNSu8 SPMetricPortability = 99; static mDNSu8 SPMetricMarginalPower = 99; static mDNSu8 SPMetricTotalPower = 99; +static mDNSu8 SPMetricFeatures = 1; /* The current version supports TCP Keep Alive Feature */ mDNSexport domainname ActiveDirectoryPrimaryDomain; -mDNSexport int ActiveDirectoryPrimaryDomainLabelCount; -mDNSexport mDNSAddr ActiveDirectoryPrimaryDomainServer; +mDNSexport int ActiveDirectoryPrimaryDomainLabelCount; +mDNSexport mDNSAddr ActiveDirectoryPrimaryDomainServer; #endif // APPLE_OSX_mDNSResponder // Used by AutoTunnel @@ -216,7 +215,7 @@ const char dnsprefix[] = "dns:"; #pragma mark - D2D Support #endif -#if ! NO_D2D +#if !NO_D2D // Name compression items for fake packet version number 1 static const mDNSu8 compression_packet_v1 = 0x01; @@ -231,553 +230,726 @@ mDNSlocal void PrintHex(mDNSu8 *data, mDNSu16 len); static ARListElem *D2DRecords = NULL; // List of locally-generated PTR records to records found via D2D typedef struct D2DBrowseListElem - { - struct D2DBrowseListElem *next; - domainname name; - mDNSu16 type; - unsigned int refCount; - } D2DBrowseListElem; +{ + struct D2DBrowseListElem *next; + domainname name; + mDNSu16 type; + unsigned int refCount; +} D2DBrowseListElem; D2DBrowseListElem* D2DBrowseList = NULL; mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val) - { - ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF); - ptr[1] = (mDNSu8)((val ) & 0xFF); - return ptr + sizeof(mDNSu16); - } +{ + ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF); + ptr[1] = (mDNSu8)((val ) & 0xFF); + return ptr + sizeof(mDNSu16); +} 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); - } +{ + 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 void DomainnameToLower(const domainname * const in, domainname * const out) - { - const mDNSu8 * const start = (const mDNSu8 * const)in; - mDNSu8 *ptr = (mDNSu8*)start; - while(*ptr) - { - mDNSu8 c = *ptr; - out->c[ptr-start] = *ptr; - ptr++; - for (;c;c--,ptr++) out->c[ptr-start] = mDNSIsUpperCase(*ptr) ? (*ptr - 'A' + 'a') : *ptr; - } - out->c[ptr-start] = *ptr; - } +{ + const mDNSu8 * const start = (const mDNSu8 * const)in; + mDNSu8 *ptr = (mDNSu8*)start; + while(*ptr) + { + mDNSu8 c = *ptr; + out->c[ptr-start] = *ptr; + ptr++; + for (; c; c--,ptr++) out->c[ptr-start] = mDNSIsUpperCase(*ptr) ? (*ptr - 'A' + 'a') : *ptr; + } + out->c[ptr-start] = *ptr; +} mDNSlocal mStatus DNSNameCompressionParseBytes(mDNS *const m, const mDNSu8 *const lhs, const mDNSu16 lhs_len, const mDNSu8 *const rhs, const mDNSu16 rhs_len, AuthRecord *rr) - { - if (mDNS_LoggingEnabled) - { - LogInfo("%s", __func__); - LogInfo(" Static Bytes: "); - PrintHex((mDNSu8*)&compression_base_msg, compression_lhs - (mDNSu8*)&compression_base_msg); - } - - mDNSu8 *ptr = compression_lhs; // pointer to the end of our fake packet - - // Check to make sure we're not going to go past the end of the DNSMessage data - // 7 = 2 for CLASS (-1 for our version) + 4 for TTL + 2 for RDLENGTH - if (ptr + lhs_len - 7 + rhs_len >= compression_limit) return mStatus_NoMemoryErr; - - // Copy the LHS onto our fake wire packet - mDNSPlatformMemCopy(ptr, lhs, lhs_len); - ptr += lhs_len - 1; - - // Check the 'fake packet' version number, to ensure that we know how to decompress this data - if (*ptr != compression_packet_v1) return mStatus_Incompatible; - - // two bytes of CLASS - ptr = putVal16(ptr, kDNSClass_IN | kDNSClass_UniqueRRSet); - - // four bytes of TTL - ptr = putVal32(ptr, 120); - - // Copy the RHS length into the RDLENGTH of our fake wire packet - ptr = putVal16(ptr, rhs_len); - - // Copy the RHS onto our fake wire packet - mDNSPlatformMemCopy(ptr, rhs, rhs_len); - ptr += rhs_len; - - if (mDNS_LoggingEnabled) - { - LogInfo(" Our Bytes %d: ", __LINE__); - PrintHex(compression_lhs, ptr - compression_lhs); - } - - ptr = (mDNSu8 *) GetLargeResourceRecord(m, &compression_base_msg, compression_lhs, ptr, mDNSInterface_Any, kDNSRecordTypePacketAns, &m->rec); - if (!ptr || m->rec.r.resrec.RecordType == kDNSRecordTypePacketNegative) - { LogMsg("DNSNameCompressionParseBytes: failed to get large RR"); m->rec.r.resrec.RecordType = 0; return mStatus_UnknownErr; } - else LogInfo("DNSNameCompressionParseBytes: got rr: %s", CRDisplayString(m, &m->rec.r)); - - mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSInterface_P2P, m->rec.r.resrec.rrtype, 7200, kDNSRecordTypeShared, AuthRecordP2P, FreeD2DARElemCallback, NULL); - AssignDomainName(&rr->namestorage, &m->rec.namestorage); - rr->resrec.rdlength = m->rec.r.resrec.rdlength; - rr->resrec.rdata->MaxRDLength = m->rec.r.resrec.rdlength; - mDNSPlatformMemCopy(rr->resrec.rdata->u.data, m->rec.r.resrec.rdata->u.data, m->rec.r.resrec.rdlength); - rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); - SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us - - m->rec.r.resrec.RecordType = 0; // Mark m->rec as no longer in use - - return mStatus_NoError; - } +{ + if (mDNS_LoggingEnabled) + { + LogInfo("%s", __func__); + LogInfo(" Static Bytes: (%d bytes)", compression_lhs - (mDNSu8*)&compression_base_msg); + PrintHex((mDNSu8*)&compression_base_msg, compression_lhs - (mDNSu8*)&compression_base_msg); + } + + mDNSu8 *ptr = compression_lhs; // pointer to the end of our fake packet + + // Check to make sure we're not going to go past the end of the DNSMessage data + // 7 = 2 for CLASS (-1 for our version) + 4 for TTL + 2 for RDLENGTH + if (ptr + lhs_len - 7 + rhs_len >= compression_limit) return mStatus_NoMemoryErr; + + // Copy the LHS onto our fake wire packet + mDNSPlatformMemCopy(ptr, lhs, lhs_len); + ptr += lhs_len - 1; + + // Check the 'fake packet' version number, to ensure that we know how to decompress this data + if (*ptr != compression_packet_v1) return mStatus_Incompatible; + + // two bytes of CLASS + ptr = putVal16(ptr, kDNSClass_IN | kDNSClass_UniqueRRSet); + + // four bytes of TTL + ptr = putVal32(ptr, 120); + + // Copy the RHS length into the RDLENGTH of our fake wire packet + ptr = putVal16(ptr, rhs_len); + + // Copy the RHS onto our fake wire packet + mDNSPlatformMemCopy(ptr, rhs, rhs_len); + ptr += rhs_len; + + if (mDNS_LoggingEnabled) + { + LogInfo(" Our Bytes (%d bytes): ", ptr - compression_lhs); + PrintHex(compression_lhs, ptr - compression_lhs); + } + + ptr = (mDNSu8 *) GetLargeResourceRecord(m, &compression_base_msg, compression_lhs, ptr, mDNSInterface_Any, kDNSRecordTypePacketAns, &m->rec); + if (!ptr || m->rec.r.resrec.RecordType == kDNSRecordTypePacketNegative) + { LogMsg("DNSNameCompressionParseBytes: failed to get large RR"); m->rec.r.resrec.RecordType = 0; return mStatus_UnknownErr; } + else LogInfo("DNSNameCompressionParseBytes: got rr: %s", CRDisplayString(m, &m->rec.r)); + + mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSInterface_P2P, m->rec.r.resrec.rrtype, 7200, kDNSRecordTypeShared, AuthRecordP2P, FreeD2DARElemCallback, NULL); + AssignDomainName(&rr->namestorage, &m->rec.namestorage); + rr->resrec.rdlength = m->rec.r.resrec.rdlength; + rr->resrec.rdata->MaxRDLength = m->rec.r.resrec.rdlength; + mDNSPlatformMemCopy(rr->resrec.rdata->u.data, m->rec.r.resrec.rdata->u.data, m->rec.r.resrec.rdlength); + rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); + SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us + + m->rec.r.resrec.RecordType = 0; // Mark m->rec as no longer in use + + return mStatus_NoError; +} mDNSlocal mDNSu8 * DNSNameCompressionBuildLHS(const domainname const *typeDomain, DNS_TypeValues qtype) - { - mDNSu8 *ptr = putDomainNameAsLabels(&compression_base_msg, compression_lhs, compression_limit, typeDomain); - if (!ptr) return ptr; - *ptr = (qtype >> 8) & 0xff; - ptr += 1; - *ptr = qtype & 0xff; - ptr += 1; - *ptr = compression_packet_v1; - return ptr + 1; - } +{ + mDNSu8 *ptr = putDomainNameAsLabels(&compression_base_msg, compression_lhs, compression_limit, typeDomain); + if (!ptr) return ptr; + *ptr = (qtype >> 8) & 0xff; + ptr += 1; + *ptr = qtype & 0xff; + ptr += 1; + *ptr = compression_packet_v1; + return ptr + 1; +} mDNSlocal mDNSu8 * DNSNameCompressionBuildRHS(mDNSu8 *start, const ResourceRecord *const resourceRecord) - { - return putRData(&compression_base_msg, start, compression_limit, resourceRecord); - } +{ + return putRData(&compression_base_msg, start, compression_limit, resourceRecord); +} + +#define PRINT_DEBUG_BYTES_LIMIT 64 // set limit on number of record bytes printed for debugging mDNSlocal void PrintHex(mDNSu8 *data, mDNSu16 len) - { - mDNSu8 *end = data + len; - char buffer[49] = {0}; - char *bufend = buffer + sizeof(buffer); - while(data PRINT_DEBUG_BYTES_LIMIT) + { + LogInfo(" (limiting debug output to %d bytes)", PRINT_DEBUG_BYTES_LIMIT); + len = PRINT_DEBUG_BYTES_LIMIT; + } + end = data + len; + + while(data < end) + { + char *ptr = buffer; + for(; data < end && ptr < bufend-1; ptr+=3,data++) + mDNS_snprintf(ptr, bufend - ptr, "%02X ", *data); + LogInfo(" %s", buffer); + } +} mDNSlocal void PrintHelper(const char *const tag, mDNSu8 *lhs, mDNSu16 lhs_len, mDNSu8 *rhs, mDNSu16 rhs_len) - { - if (!mDNS_LoggingEnabled) return; - - LogInfo("%s:", tag); - LogInfo(" LHS: "); - PrintHex(lhs, lhs_len); - - if (!rhs) return; - - LogInfo(" RHS: "); - PrintHex(rhs, rhs_len); - } +{ + if (!mDNS_LoggingEnabled) return; + + LogInfo("%s:", tag); + LogInfo(" LHS: (%d bytes)", lhs_len); + PrintHex(lhs, lhs_len); + + if (!rhs) return; + + LogInfo(" RHS: (%d bytes)", rhs_len); + PrintHex(rhs, rhs_len); +} mDNSlocal void FreeD2DARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result) - { - (void)m; // unused - if (result == mStatus_MemFree) - { - ARListElem **ptr = &D2DRecords; - ARListElem *tmp; - while (*ptr && &(*ptr)->ar != rr) ptr = &(*ptr)->next; - if (!*ptr) { LogMsg("FreeD2DARElemCallback: Could not find in D2DRecords: %s", ARDisplayString(m, rr)); return; } - LogInfo("FreeD2DARElemCallback: Found in D2DRecords: %s", ARDisplayString(m, rr)); - tmp = *ptr; - *ptr = (*ptr)->next; - // Just because we stoppped browsing, doesn't mean we should tear down the PAN connection. - mDNSPlatformMemFree(tmp); - } - } - -mDNSlocal void xD2DClearCache(mDNS *const m, const domainname *regType) - { - ARListElem *ptr = D2DRecords; - for ( ; ptr ; ptr = ptr->next) - { - if (SameDomainName(&ptr->ar.namestorage, regType)) - { - char buffer[MAX_ESCAPED_DOMAIN_NAME]; - mDNS_Deregister(m, &ptr->ar); - ConvertDomainNameToCString(regType, buffer); - LogInfo("xD2DClearCache: Clearing cache record and deregistering %s", buffer); - } - } - } +{ + (void)m; // unused + if (result == mStatus_MemFree) + { + ARListElem **ptr = &D2DRecords; + ARListElem *tmp; + while (*ptr && &(*ptr)->ar != rr) ptr = &(*ptr)->next; + if (!*ptr) { LogMsg("FreeD2DARElemCallback: Could not find in D2DRecords: %s", ARDisplayString(m, rr)); return; } + LogInfo("FreeD2DARElemCallback: Found in D2DRecords: %s", ARDisplayString(m, rr)); + tmp = *ptr; + *ptr = (*ptr)->next; + // Just because we stoppped browsing, doesn't mean we should tear down the PAN connection. + mDNSPlatformMemFree(tmp); + } +} + +mDNSlocal void xD2DClearCache(const domainname *regType) +{ + ARListElem *ptr = D2DRecords; + for ( ; ptr ; ptr = ptr->next) + { + if (SameDomainName(&ptr->ar.namestorage, regType)) + { + char buffer[MAX_ESCAPED_DOMAIN_NAME]; + mDNS_Deregister(&mDNSStorage, &ptr->ar); + ConvertDomainNameToCString(regType, buffer); + LogInfo("xD2DClearCache: Clearing cache record and deregistering %s", buffer); + } + } +} mDNSlocal D2DBrowseListElem ** D2DFindInBrowseList(const domainname *const name, mDNSu16 type) - { - D2DBrowseListElem **ptr = &D2DBrowseList; +{ + D2DBrowseListElem **ptr = &D2DBrowseList; + + for ( ; *ptr; ptr = &(*ptr)->next) + if ((*ptr)->type == type && SameDomainName(&(*ptr)->name, name)) + break; - for ( ; *ptr; ptr = &(*ptr)->next) - if ((*ptr)->type == type && SameDomainName(&(*ptr)->name, name)) - break; - - return ptr; - } + return ptr; +} mDNSlocal unsigned int D2DBrowseListRefCount(const domainname *const name, mDNSu16 type) - { - D2DBrowseListElem **ptr = D2DFindInBrowseList(name, type); - return *ptr ? (*ptr)->refCount : 0; - } +{ + D2DBrowseListElem **ptr = D2DFindInBrowseList(name, type); + return *ptr ? (*ptr)->refCount : 0; +} mDNSlocal void D2DBrowseListRetain(const domainname *const name, mDNSu16 type) - { - D2DBrowseListElem **ptr = D2DFindInBrowseList(name, type); - - if (!*ptr) - { - *ptr = mDNSPlatformMemAllocate(sizeof(**ptr)); - mDNSPlatformMemZero(*ptr, sizeof(**ptr)); - (*ptr)->type = type; - AssignDomainName(&(*ptr)->name, name); - } - (*ptr)->refCount += 1; - - LogInfo("D2DBrowseListRetain: %##s %s refcount now %u", (*ptr)->name.c, DNSTypeName((*ptr)->type), (*ptr)->refCount); - } +{ + D2DBrowseListElem **ptr = D2DFindInBrowseList(name, type); + + if (!*ptr) + { + *ptr = mDNSPlatformMemAllocate(sizeof(**ptr)); + mDNSPlatformMemZero(*ptr, sizeof(**ptr)); + (*ptr)->type = type; + AssignDomainName(&(*ptr)->name, name); + } + (*ptr)->refCount += 1; + + LogInfo("D2DBrowseListRetain: %##s %s refcount now %u", (*ptr)->name.c, DNSTypeName((*ptr)->type), (*ptr)->refCount); +} mDNSlocal void D2DBrowseListRelease(const domainname *const name, mDNSu16 type) - { - D2DBrowseListElem **ptr = D2DFindInBrowseList(name, type); - - if (!*ptr) { LogMsg("D2DBrowseListRelease: Didn't find %##s %s in list", name->c, DNSTypeName(type)); return; } - - (*ptr)->refCount -= 1; - - LogInfo("D2DBrowseListRelease: %##s %s refcount now %u", (*ptr)->name.c, DNSTypeName((*ptr)->type), (*ptr)->refCount); - - if (!(*ptr)->refCount) - { - D2DBrowseListElem *tmp = *ptr; - *ptr = (*ptr)->next; - mDNSPlatformMemFree(tmp); - } - } +{ + D2DBrowseListElem **ptr = D2DFindInBrowseList(name, type); + + if (!*ptr) { LogMsg("D2DBrowseListRelease: Didn't find %##s %s in list", name->c, DNSTypeName(type)); return; } + + (*ptr)->refCount -= 1; + + LogInfo("D2DBrowseListRelease: %##s %s refcount now %u", (*ptr)->name.c, DNSTypeName((*ptr)->type), (*ptr)->refCount); + + if (!(*ptr)->refCount) + { + D2DBrowseListElem *tmp = *ptr; + *ptr = (*ptr)->next; + mDNSPlatformMemFree(tmp); + } +} mDNSlocal mStatus xD2DParse(mDNS *const m, const mDNSu8 * const lhs, const mDNSu16 lhs_len, const mDNSu8 * const rhs, const mDNSu16 rhs_len, AuthRecord *rr) - { - if (*(lhs + (lhs_len - 1)) == compression_packet_v1) - return DNSNameCompressionParseBytes(m, lhs, lhs_len, rhs, rhs_len, rr); - else - return mStatus_Incompatible; - } +{ + if (*(lhs + (lhs_len - 1)) == compression_packet_v1) + return DNSNameCompressionParseBytes(m, lhs, lhs_len, rhs, rhs_len, rr); + else + return mStatus_Incompatible; +} mDNSlocal void xD2DAddToCache(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize) - { - (void)transportType; // We don't care about this, yet. - (void)instanceHandle; // We don't care about this, yet. - - if (result == kD2DSuccess) - { - if ( key == NULL || value == NULL || keySize == 0 || valueSize == 0) { LogMsg("xD2DAddToCache: NULL Byte * passed in or length == 0"); return; } - - mStatus err; - ARListElem *ptr = mDNSPlatformMemAllocate(sizeof(ARListElem) + (valueSize < sizeof(RData) ? 0 : valueSize - sizeof(RData))); - - if (ptr == NULL) { LogMsg("xD2DAddToCache: memory allocation failure"); return; } - - err = xD2DParse(m, (const mDNSu8 * const)key, (const mDNSu16)keySize, (const mDNSu8 * const)value, (const mDNSu16)valueSize, &ptr->ar); - if (err) - { - LogMsg("xD2DAddToCache: xD2DParse returned error: %d", err); - PrintHelper(__func__, (mDNSu8 *)key, (mDNSu16)keySize, (mDNSu8 *)value, (mDNSu16)valueSize); - mDNSPlatformMemFree(ptr); - return; - } - err = mDNS_Register(m, &ptr->ar); - if (err) - { - LogMsg("xD2DAddToCache: mDNS_Register returned error %d for %s", err, ARDisplayString(m, &ptr->ar)); - mDNSPlatformMemFree(ptr); - return; - } - - LogInfo("xD2DAddToCache: mDNS_Register succeeded for %s", ARDisplayString(m, &ptr->ar)); - ptr->next = D2DRecords; - D2DRecords = ptr; - } - else - LogMsg("xD2DAddToCache: Unexpected result %d", result); - } +{ + (void)transportType; // We don't care about this, yet. + (void)instanceHandle; // We don't care about this, yet. + + if (result == kD2DSuccess) + { + if ( key == NULL || value == NULL || keySize == 0 || valueSize == 0) { LogMsg("xD2DAddToCache: NULL Byte * passed in or length == 0"); return; } + + mStatus err; + ARListElem *ptr = mDNSPlatformMemAllocate(sizeof(ARListElem) + (valueSize < sizeof(RData) ? 0 : valueSize - sizeof(RData))); + + if (ptr == NULL) { LogMsg("xD2DAddToCache: memory allocation failure"); return; } + + err = xD2DParse(m, (const mDNSu8 * const)key, (const mDNSu16)keySize, (const mDNSu8 * const)value, (const mDNSu16)valueSize, &ptr->ar); + if (err) + { + LogMsg("xD2DAddToCache: xD2DParse returned error: %d", err); + PrintHelper(__func__, (mDNSu8 *)key, (mDNSu16)keySize, (mDNSu8 *)value, (mDNSu16)valueSize); + mDNSPlatformMemFree(ptr); + return; + } + err = mDNS_Register(m, &ptr->ar); + if (err) + { + LogMsg("xD2DAddToCache: mDNS_Register returned error %d for %s", err, ARDisplayString(m, &ptr->ar)); + mDNSPlatformMemFree(ptr); + return; + } + + LogInfo("xD2DAddToCache: mDNS_Register succeeded for %s", ARDisplayString(m, &ptr->ar)); + ptr->next = D2DRecords; + D2DRecords = ptr; + } + else + LogMsg("xD2DAddToCache: Unexpected result %d", result); +} mDNSlocal ARListElem * xD2DFindInList(mDNS *const m, const Byte *const key, const size_t keySize, const Byte *const value, const size_t valueSize) - { - ARListElem *ptr = D2DRecords; - ARListElem *arptr; - - if ( key == NULL || value == NULL || keySize == 0 || valueSize == 0) { LogMsg("xD2DFindInList: NULL Byte * passed in or length == 0"); return NULL; } - - arptr = mDNSPlatformMemAllocate(sizeof(ARListElem) + (valueSize < sizeof(RData) ? 0 : valueSize - sizeof(RData))); - if (arptr == NULL) { LogMsg("xD2DFindInList: memory allocation failure"); return NULL; } - - if (xD2DParse(m, (const mDNSu8 *const)key, (const mDNSu16)keySize, (const mDNSu8 *const)value, (const mDNSu16)valueSize, &arptr->ar) != mStatus_NoError) - { - LogMsg("xD2DFindInList: xD2DParse failed for key: %p (%u) value: %p (%u)", key, keySize, value, valueSize); - mDNSPlatformMemFree(arptr); - return NULL; - } - - while (ptr) - { - if (IdenticalResourceRecord(&arptr->ar.resrec, &ptr->ar.resrec)) break; - ptr = ptr->next; - } - - if (!ptr) LogMsg("xD2DFindInList: Could not find in D2DRecords: %s", ARDisplayString(m, &arptr->ar)); - mDNSPlatformMemFree(arptr); - return ptr; - } +{ + ARListElem *ptr = D2DRecords; + ARListElem *arptr; + + if ( key == NULL || value == NULL || keySize == 0 || valueSize == 0) { LogMsg("xD2DFindInList: NULL Byte * passed in or length == 0"); return NULL; } + + arptr = mDNSPlatformMemAllocate(sizeof(ARListElem) + (valueSize < sizeof(RData) ? 0 : valueSize - sizeof(RData))); + if (arptr == NULL) { LogMsg("xD2DFindInList: memory allocation failure"); return NULL; } + + if (xD2DParse(m, (const mDNSu8 *const)key, (const mDNSu16)keySize, (const mDNSu8 *const)value, (const mDNSu16)valueSize, &arptr->ar) != mStatus_NoError) + { + LogMsg("xD2DFindInList: xD2DParse failed for key: %p (%u) value: %p (%u)", key, keySize, value, valueSize); + mDNSPlatformMemFree(arptr); + return NULL; + } + + while (ptr) + { + if (IdenticalResourceRecord(&arptr->ar.resrec, &ptr->ar.resrec)) break; + ptr = ptr->next; + } + + if (!ptr) LogMsg("xD2DFindInList: Could not find in D2DRecords: %s", ARDisplayString(m, &arptr->ar)); + mDNSPlatformMemFree(arptr); + return ptr; +} mDNSlocal void xD2DRemoveFromCache(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize) - { - (void)transportType; // We don't care about this, yet. - (void)instanceHandle; // We don't care about this, yet. - - if (result == kD2DSuccess) - { - ARListElem *ptr = xD2DFindInList(m, key, keySize, value, valueSize); - if (ptr) - { - LogInfo("xD2DRemoveFromCache: Remove from cache: %s", ARDisplayString(m, &ptr->ar)); - mDNS_Deregister(m, &ptr->ar); - } - } - else - LogMsg("xD2DRemoveFromCache: Unexpected result %d", result); - } +{ + (void)transportType; // We don't care about this, yet. + (void)instanceHandle; // We don't care about this, yet. + + if (result == kD2DSuccess) + { + ARListElem *ptr = xD2DFindInList(m, key, keySize, value, valueSize); + if (ptr) + { + LogInfo("xD2DRemoveFromCache: Remove from cache: %s", ARDisplayString(m, &ptr->ar)); + mDNS_Deregister(m, &ptr->ar); + } + } + else + LogMsg("xD2DRemoveFromCache: Unexpected result %d", result); +} mDNSlocal void xD2DServiceResolved(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize) - { - (void)m; - (void)key; - (void)keySize; - (void)value; - (void)valueSize; - - if (result == kD2DSuccess) - { - LogInfo("xD2DServiceResolved: Starting up PAN connection for %p", instanceHandle); - CHECK_D2D_FUNCTION(D2DRetain) D2DRetain(instanceHandle, transportType); - } - else LogMsg("xD2DServiceResolved: Unexpected result %d", result); - } +{ + (void)m; + (void)key; + (void)keySize; + (void)value; + (void)valueSize; + + if (result == kD2DSuccess) + { + LogInfo("xD2DServiceResolved: Starting up PAN connection for %p", instanceHandle); + if (D2DRetain) D2DRetain(instanceHandle, transportType); + } + else LogMsg("xD2DServiceResolved: Unexpected result %d", result); +} mDNSlocal void xD2DRetainHappened(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize) - { - (void)m; - (void)instanceHandle; - (void)transportType; - (void)key; - (void)keySize; - (void)value; - (void)valueSize; - - if (result == kD2DSuccess) LogInfo("xD2DRetainHappened: Opening up PAN connection for %p", instanceHandle); - else LogMsg("xD2DRetainHappened: Unexpected result %d", result); - } +{ + (void)m; + (void)instanceHandle; + (void)transportType; + (void)key; + (void)keySize; + (void)value; + (void)valueSize; + + if (result == kD2DSuccess) LogInfo("xD2DRetainHappened: Opening up PAN connection for %p", instanceHandle); + else LogMsg("xD2DRetainHappened: Unexpected result %d", result); +} mDNSlocal void xD2DReleaseHappened(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize) - { - (void)m; - (void)instanceHandle; - (void)transportType; - (void)key; - (void)keySize; - (void)value; - (void)valueSize; - - if (result == kD2DSuccess) LogInfo("xD2DReleaseHappened: Closing PAN connection for %p", instanceHandle); - else LogMsg("xD2DReleaseHappened: Unexpected result %d", result); - } +{ + (void)m; + (void)instanceHandle; + (void)transportType; + (void)key; + (void)keySize; + (void)value; + (void)valueSize; + + if (result == kD2DSuccess) LogInfo("xD2DReleaseHappened: Closing PAN connection for %p", instanceHandle); + else LogMsg("xD2DReleaseHappened: Unexpected result %d", result); +} mDNSlocal void xD2DServiceCallback(D2DServiceEvent event, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize, void *userData) - { - mDNS *m = (mDNS *) userData; - const char *eventString = "unknown"; - - KQueueLock(m); - - if (keySize > 0xFFFF) LogMsg("xD2DServiceCallback: keySize too large: %u", keySize); - if (valueSize > 0xFFFF) LogMsg("xD2DServiceCallback: valueSize too large: %u", valueSize); - - switch (event) - { - case D2DServiceFound: - eventString = "D2DServiceFound"; - break; - case D2DServiceLost: - eventString = "D2DServiceLost"; - break; - case D2DServiceResolved: - eventString = "D2DServiceResolved"; - break; - case D2DServiceRetained: - eventString = "D2DServiceRetained"; - break; - case D2DServiceReleased: - eventString = "D2DServiceReleased"; - break; - default: - break; - } - - LogInfo("xD2DServiceCallback: event=%s result=%d instanceHandle=%p transportType=%d LHS=%p (%u) RHS=%p (%u) userData=%p", eventString, result, instanceHandle, transportType, key, keySize, value, valueSize, userData); - PrintHelper(__func__, (mDNSu8 *)key, (mDNSu16)keySize, (mDNSu8 *)value, (mDNSu16)valueSize); - - switch (event) - { - case D2DServiceFound: - xD2DAddToCache(m, result, instanceHandle, transportType, key, keySize, value, valueSize); - break; - case D2DServiceLost: - xD2DRemoveFromCache(m, result, instanceHandle, transportType, key, keySize, value, valueSize); - break; - case D2DServiceResolved: - xD2DServiceResolved(m, result, instanceHandle, transportType, key, keySize, value, valueSize); - break; - case D2DServiceRetained: - xD2DRetainHappened(m, result, instanceHandle, transportType, key, keySize, value, valueSize); - break; - case D2DServiceReleased: - xD2DReleaseHappened(m, result, instanceHandle, transportType, key, keySize, value, valueSize); - break; - default: - break; - } - - // Need to tickle the main kqueue loop to potentially handle records we removed or added. - KQueueUnlock(m, "xD2DServiceCallback"); - } - -mDNSexport void external_start_browsing_for_service(mDNS *const m, const domainname *const typeDomain, DNS_TypeValues qtype) - { - (void)m; - domainname lower; - - if (qtype == kDNSServiceType_A || qtype == kDNSServiceType_AAAA) - { - LogInfo("external_start_browsing_for_service: ignoring address record"); - return; - } - - DomainnameToLower(typeDomain, &lower); - - if (!D2DBrowseListRefCount(&lower, qtype)) - { - LogInfo("external_start_browsing_for_service: Starting browse for: %##s %s", lower.c, DNSTypeName(qtype)); - mDNSu8 *end = DNSNameCompressionBuildLHS(&lower, qtype); - PrintHelper(__func__, compression_lhs, end - compression_lhs, mDNSNULL, 0); - CHECK_D2D_FUNCTION(D2DStartBrowsingForKey) D2DStartBrowsingForKey(compression_lhs, end - compression_lhs); - } - D2DBrowseListRetain(&lower, qtype); - } - -mDNSexport void external_stop_browsing_for_service(mDNS *const m, const domainname *const typeDomain, DNS_TypeValues qtype) - { - domainname lower; - - if (qtype == kDNSServiceType_A || qtype == kDNSServiceType_AAAA) - { - LogInfo("external_stop_browsing_for_service: ignoring address record"); - return; - } - - DomainnameToLower(typeDomain, &lower); - - D2DBrowseListRelease(&lower, qtype); - if (!D2DBrowseListRefCount(&lower, qtype)) - { - LogInfo("external_stop_browsing_for_service: Stopping browse for: %##s %s", lower.c, DNSTypeName(qtype)); - mDNSu8 *end = DNSNameCompressionBuildLHS(&lower, qtype); - PrintHelper(__func__, compression_lhs, end - compression_lhs, mDNSNULL, 0); - CHECK_D2D_FUNCTION(D2DStopBrowsingForKey) D2DStopBrowsingForKey(compression_lhs, end - compression_lhs); - xD2DClearCache(m, &lower); - } - } - -mDNSexport void external_start_advertising_service(const ResourceRecord *const resourceRecord) - { - domainname lower; - mDNSu8 *rhs = NULL; - mDNSu8 *end = NULL; - DomainnameToLower(resourceRecord->name, &lower); - - LogInfo("external_start_advertising_service: %s", RRDisplayString(&mDNSStorage, resourceRecord)); - // For SRV records, update packet filter if p2p interface already exists, otherwise, - // if will be updated when we get the KEV_DL_IF_ATTACHED event for the interface. - // Bonjour filter rules are removed when p2p interface KEV_DL_IF_DETACHED event is received. - if (resourceRecord->rrtype == kDNSType_SRV) - mDNSInitPacketFilter(); - - if (resourceRecord->rrtype == kDNSServiceType_A || resourceRecord->rrtype == kDNSServiceType_AAAA) - { - LogInfo("external_start_advertising_service: ignoring address record"); - return; - } - rhs = DNSNameCompressionBuildLHS(&lower, resourceRecord->rrtype); - end = DNSNameCompressionBuildRHS(rhs, resourceRecord); - PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs); - CHECK_D2D_FUNCTION(D2DStartAdvertisingPair) D2DStartAdvertisingPair(compression_lhs, rhs - compression_lhs, rhs, end - rhs); - } - -mDNSexport void external_stop_advertising_service(const ResourceRecord *const resourceRecord) - { - domainname lower; - mDNSu8 *rhs = NULL; - mDNSu8 *end = NULL; - DomainnameToLower(resourceRecord->name, &lower); - - LogInfo("external_stop_advertising_service: %s", RRDisplayString(&mDNSStorage, resourceRecord)); - if (resourceRecord->rrtype == kDNSServiceType_A || resourceRecord->rrtype == kDNSServiceType_AAAA) - { - LogInfo("external_stop_advertising_service: ignoring address record"); - return; - } - rhs = DNSNameCompressionBuildLHS(&lower, resourceRecord->rrtype); - end = DNSNameCompressionBuildRHS(rhs, resourceRecord); - PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs); - CHECK_D2D_FUNCTION(D2DStopAdvertisingPair) D2DStopAdvertisingPair(compression_lhs, rhs - compression_lhs, rhs, end - rhs); - } - -mDNSexport void external_start_resolving_service(const domainname *const fqdn) - { - domainname lower; - mDNSu8 *rhs = NULL; - mDNSu8 *end = NULL; - DomainnameToLower(SkipLeadingLabels(fqdn, 1), &lower); - - LogInfo("external_start_resolving_service: %##s", fqdn->c); - rhs = DNSNameCompressionBuildLHS(&lower, kDNSType_PTR); - end = putDomainNameAsLabels(&compression_base_msg, rhs, compression_limit, fqdn); - PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs); - CHECK_D2D_FUNCTION(D2DStartResolvingPair) D2DStartResolvingPair(compression_lhs, rhs - compression_lhs, rhs, end - rhs); - } - -mDNSexport void external_stop_resolving_service(const domainname *const fqdn) - { - domainname lower; - mDNSu8 *rhs = NULL; - mDNSu8 *end = NULL; - DomainnameToLower(SkipLeadingLabels(fqdn, 1), &lower); - - LogInfo("external_stop_resolving_service: %##s", fqdn->c); - rhs = DNSNameCompressionBuildLHS(&lower, kDNSType_PTR); - end = putDomainNameAsLabels(&compression_base_msg, rhs, compression_limit, fqdn); - PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs); - CHECK_D2D_FUNCTION(D2DStopResolvingPair) D2DStopResolvingPair(compression_lhs, rhs - compression_lhs, rhs, end - rhs); - } +{ + mDNS *m = (mDNS *) userData; + const char *eventString = "unknown"; + + KQueueLock(m); + + if (keySize > 0xFFFF) LogMsg("xD2DServiceCallback: keySize too large: %u", keySize); + if (valueSize > 0xFFFF) LogMsg("xD2DServiceCallback: valueSize too large: %u", valueSize); + + switch (event) + { + case D2DServiceFound: + eventString = "D2DServiceFound"; + break; + case D2DServiceLost: + eventString = "D2DServiceLost"; + break; + case D2DServiceResolved: + eventString = "D2DServiceResolved"; + break; + case D2DServiceRetained: + eventString = "D2DServiceRetained"; + break; + case D2DServiceReleased: + eventString = "D2DServiceReleased"; + break; + default: + break; + } + + LogInfo("xD2DServiceCallback: event=%s result=%d instanceHandle=%p transportType=%d LHS=%p (%u) RHS=%p (%u) userData=%p", eventString, result, instanceHandle, transportType, key, keySize, value, valueSize, userData); + PrintHelper(__func__, (mDNSu8 *)key, (mDNSu16)keySize, (mDNSu8 *)value, (mDNSu16)valueSize); + + switch (event) + { + case D2DServiceFound: + xD2DAddToCache(m, result, instanceHandle, transportType, key, keySize, value, valueSize); + break; + case D2DServiceLost: + xD2DRemoveFromCache(m, result, instanceHandle, transportType, key, keySize, value, valueSize); + break; + case D2DServiceResolved: + xD2DServiceResolved(m, result, instanceHandle, transportType, key, keySize, value, valueSize); + break; + case D2DServiceRetained: + xD2DRetainHappened(m, result, instanceHandle, transportType, key, keySize, value, valueSize); + break; + case D2DServiceReleased: + xD2DReleaseHappened(m, result, instanceHandle, transportType, key, keySize, value, valueSize); + break; + default: + break; + } + + // Need to tickle the main kqueue loop to potentially handle records we removed or added. + KQueueUnlock(m, "xD2DServiceCallback"); +} + +// Map interface index and flags to a specific D2D transport type or D2DTransportMax if all plugins +// should be called. +// When D2DTransportMax is returned, if a specific transport should not be called, *excludedTransportType +// will be set to the excluded transport value, otherwise, it will be set to D2DTransportMax. +// If the return value is not D2DTransportMax, excludedTransportType is undefined. + +mDNSlocal D2DTransportType xD2DInterfaceToTransportType(mDNSInterfaceID InterfaceID, DNSServiceFlags flags, D2DTransportType * excludedTransportType) +{ + NetworkInterfaceInfoOSX *info; + + // Default exludes the D2DAWDLTransport when D2DTransportMax is returned. + *excludedTransportType = D2DAWDLTransport; + + // Call all D2D plugins when both kDNSServiceFlagsIncludeP2P and kDNSServiceFlagsIncludeAWDL are set. + if ((flags & kDNSServiceFlagsIncludeP2P) && (flags & kDNSServiceFlagsIncludeAWDL)) + { + LogInfo("xD2DInterfaceToTransportType: returning D2DTransportMax (including AWDL) since both kDNSServiceFlagsIncludeP2P and kDNSServiceFlagsIncludeAWDL are set"); + *excludedTransportType = D2DTransportMax; + return D2DTransportMax; + } + // Call all D2D plugins (exlcluding AWDL) when only kDNSServiceFlagsIncludeP2P is set. + else if (flags & kDNSServiceFlagsIncludeP2P) + { + LogInfo("xD2DInterfaceToTransportType: returning D2DTransportMax (excluding AWDL) since only kDNSServiceFlagsIncludeP2P is set"); + return D2DTransportMax; + } + // Call AWDL D2D plugin when only kDNSServiceFlagsIncludeAWDL is set. + else if (flags & kDNSServiceFlagsIncludeAWDL) + { + LogInfo("xD2DInterfaceToTransportType: returning D2DAWDLTransport since only kDNSServiceFlagsIncludeAWDL is set"); + return D2DAWDLTransport; + } + + if (InterfaceID == mDNSInterface_P2P) + { + LogInfo("xD2DInterfaceToTransportType: returning D2DTransportMax (excluding AWDL) for interface index mDNSInterface_P2P"); + return D2DTransportMax; + } + + // Compare to cached AWDL interface ID. + if (AWDLInterfaceID && (InterfaceID == AWDLInterfaceID)) + { + LogInfo("xD2DInterfaceToTransportType: returning D2DAWDLTransport for interface index %d", InterfaceID); + return D2DAWDLTransport; + } + + info = IfindexToInterfaceInfoOSX(&mDNSStorage, InterfaceID); + if (info == NULL) + { + LogInfo("xD2DInterfaceToTransportType: Invalid interface index %d", InterfaceID); + return D2DTransportMax; + } + + // Recognize AirDrop specific p2p* interface based on interface name. + if (strncmp(info->ifinfo.ifname, "p2p", 3) == 0) + { + LogInfo("xD2DInterfaceToTransportType: returning D2DWifiPeerToPeerTransport for interface index %d", InterfaceID); + return D2DWifiPeerToPeerTransport; + } + + // Currently there is no way to identify Bluetooth interface by name, + // since they use "en*" based name strings. + + LogInfo("xD2DInterfaceToTransportType: returning default D2DTransportMax for interface index %d", InterfaceID); + return D2DTransportMax; +} + +mDNSexport void external_start_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const typeDomain, DNS_TypeValues qtype, DNSServiceFlags flags) +{ + domainname lower; + + if (qtype == kDNSServiceType_A || qtype == kDNSServiceType_AAAA) + { + LogInfo("external_start_browsing_for_service: ignoring address record"); + return; + } + + DomainnameToLower(typeDomain, &lower); + + if (!D2DBrowseListRefCount(&lower, qtype)) + { + D2DTransportType transportType, excludedTransport; + + LogInfo("external_start_browsing_for_service: Starting browse for: %##s %s", lower.c, DNSTypeName(qtype)); + mDNSu8 *end = DNSNameCompressionBuildLHS(&lower, qtype); + PrintHelper(__func__, compression_lhs, end - compression_lhs, mDNSNULL, 0); + + transportType = xD2DInterfaceToTransportType(InterfaceID, flags, & excludedTransport); + if (transportType == D2DTransportMax) + { + D2DTransportType i; + for (i = 0; i < D2DTransportMax; i++) + { + if (i == excludedTransport) continue; + if (D2DStartBrowsingForKeyOnTransport) D2DStartBrowsingForKeyOnTransport(compression_lhs, end - compression_lhs, i); + } + } + else + { + if (D2DStartBrowsingForKeyOnTransport) D2DStartBrowsingForKeyOnTransport(compression_lhs, end - compression_lhs, transportType); + } + } + D2DBrowseListRetain(&lower, qtype); +} + +mDNSexport void external_stop_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const typeDomain, DNS_TypeValues qtype, DNSServiceFlags flags) +{ + domainname lower; + + if (qtype == kDNSServiceType_A || qtype == kDNSServiceType_AAAA) + { + LogInfo("external_stop_browsing_for_service: ignoring address record"); + return; + } + + DomainnameToLower(typeDomain, &lower); + + D2DBrowseListRelease(&lower, qtype); + if (!D2DBrowseListRefCount(&lower, qtype)) + { + D2DTransportType transportType, excludedTransport; + + LogInfo("external_stop_browsing_for_service: Stopping browse for: %##s %s", lower.c, DNSTypeName(qtype)); + mDNSu8 *end = DNSNameCompressionBuildLHS(&lower, qtype); + PrintHelper(__func__, compression_lhs, end - compression_lhs, mDNSNULL, 0); + + transportType = xD2DInterfaceToTransportType(InterfaceID, flags, & excludedTransport); + if (transportType == D2DTransportMax) + { + D2DTransportType i; + for (i = 0; i < D2DTransportMax; i++) + { + if (i == excludedTransport) continue; + if (D2DStopBrowsingForKeyOnTransport) D2DStopBrowsingForKeyOnTransport(compression_lhs, end - compression_lhs, i); + } + } + else + { + if (D2DStopBrowsingForKeyOnTransport) D2DStopBrowsingForKeyOnTransport(compression_lhs, end - compression_lhs, transportType); + } + + xD2DClearCache(&lower); + } +} + +mDNSexport void external_start_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags) +{ + domainname lower; + mDNSu8 *rhs = NULL; + mDNSu8 *end = NULL; + D2DTransportType transportType, excludedTransport; + DomainnameToLower(resourceRecord->name, &lower); + + LogInfo("external_start_advertising_service: %s", RRDisplayString(&mDNSStorage, resourceRecord)); + // For SRV records, update packet filter if p2p interface already exists, otherwise, + // if will be updated when we get the KEV_DL_IF_ATTACHED event for the interface. + if (resourceRecord->rrtype == kDNSType_SRV) + mDNSUpdatePacketFilter(NULL); + + if (resourceRecord->rrtype == kDNSServiceType_A || resourceRecord->rrtype == kDNSServiceType_AAAA) + { + LogInfo("external_start_advertising_service: ignoring address record"); + return; + } + rhs = DNSNameCompressionBuildLHS(&lower, resourceRecord->rrtype); + end = DNSNameCompressionBuildRHS(rhs, resourceRecord); + PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs); + + transportType = xD2DInterfaceToTransportType(resourceRecord->InterfaceID, flags, & excludedTransport); + if (transportType == D2DTransportMax) + { + D2DTransportType i; + for (i = 0; i < D2DTransportMax; i++) + { + if (i == excludedTransport) continue; + if (D2DStartAdvertisingPairOnTransport) D2DStartAdvertisingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, i); + } + } + else + { + if (D2DStartAdvertisingPairOnTransport) D2DStartAdvertisingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, transportType); + } +} + +mDNSexport void external_stop_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags) +{ + domainname lower; + mDNSu8 *rhs = NULL; + mDNSu8 *end = NULL; + D2DTransportType transportType, excludedTransport; + DomainnameToLower(resourceRecord->name, &lower); + + LogInfo("external_stop_advertising_service: %s", RRDisplayString(&mDNSStorage, resourceRecord)); + + // For SRV records, update packet filter to to remove this port from list + if (resourceRecord->rrtype == kDNSType_SRV) + mDNSUpdatePacketFilter(resourceRecord); + + if (resourceRecord->rrtype == kDNSServiceType_A || resourceRecord->rrtype == kDNSServiceType_AAAA) + { + LogInfo("external_stop_advertising_service: ignoring address record"); + return; + } + rhs = DNSNameCompressionBuildLHS(&lower, resourceRecord->rrtype); + end = DNSNameCompressionBuildRHS(rhs, resourceRecord); + PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs); + + transportType = xD2DInterfaceToTransportType(resourceRecord->InterfaceID, flags, & excludedTransport); + if (transportType == D2DTransportMax) + { + D2DTransportType i; + for (i = 0; i < D2DTransportMax; i++) + { + if (i == excludedTransport) continue; + if (D2DStopAdvertisingPairOnTransport) D2DStopAdvertisingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, i); + } + } + else + { + if (D2DStopAdvertisingPairOnTransport) D2DStopAdvertisingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, transportType); + } +} + +mDNSexport void external_start_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags) +{ + domainname lower; + mDNSu8 *rhs = NULL; + mDNSu8 *end = NULL; + D2DTransportType transportType, excludedTransport; + DomainnameToLower(SkipLeadingLabels(fqdn, 1), &lower); + + LogInfo("external_start_resolving_service: %##s", fqdn->c); + rhs = DNSNameCompressionBuildLHS(&lower, kDNSType_PTR); + end = putDomainNameAsLabels(&compression_base_msg, rhs, compression_limit, fqdn); + PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs); + + transportType = xD2DInterfaceToTransportType(InterfaceID, flags, & excludedTransport); + if (transportType == D2DTransportMax) + { + D2DTransportType i; + for (i = 0; i < D2DTransportMax; i++) + { + if (i == excludedTransport) continue; + if (D2DStartResolvingPairOnTransport) D2DStartResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, i); + } + } + else + { + if (D2DStartResolvingPairOnTransport) D2DStartResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, transportType); + } +} + +mDNSexport void external_stop_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags) +{ + domainname lower; + mDNSu8 *rhs = NULL; + mDNSu8 *end = NULL; + D2DTransportType transportType, excludedTransport; + DomainnameToLower(SkipLeadingLabels(fqdn, 1), &lower); + + LogInfo("external_stop_resolving_service: %##s", fqdn->c); + rhs = DNSNameCompressionBuildLHS(&lower, kDNSType_PTR); + end = putDomainNameAsLabels(&compression_base_msg, rhs, compression_limit, fqdn); + PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs); + + transportType = xD2DInterfaceToTransportType(InterfaceID, flags, & excludedTransport); + if (transportType == D2DTransportMax) + { + D2DTransportType i; + for (i = 0; i < D2DTransportMax; i++) + { + if (i == excludedTransport) continue; + if (D2DStopResolvingPairOnTransport) D2DStopResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, i); + } + } + else + { + if (D2DStopResolvingPairOnTransport) D2DStopResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, transportType); + } +} #elif APPLE_OSX_mDNSResponder -mDNSexport void external_start_browsing_for_service(mDNS *const m, const domainname *const type, DNS_TypeValues qtype) { (void)m; (void)type; (void)qtype; } -mDNSexport void external_stop_browsing_for_service(mDNS *const m, const domainname *const type, DNS_TypeValues qtype) { (void)m; (void)type; (void)qtype; } -mDNSexport void external_start_advertising_service(const ResourceRecord *const resourceRecord) { (void)resourceRecord; } -mDNSexport void external_stop_advertising_service(const ResourceRecord *const resourceRecord) { (void)resourceRecord; } -mDNSexport void external_start_resolving_service(const domainname *const fqdn) { (void)fqdn; } -mDNSexport void external_stop_resolving_service(const domainname *const fqdn) { (void)fqdn; } +mDNSexport void external_start_browsing_for_service(mDNS *const m, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags) { (void)m; (void)type; (void)qtype; (void)flags;} +mDNSexport void external_stop_browsing_for_service(mDNS *const m, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags) { (void)m; (void)type; (void)qtype; (void)flags;} +mDNSexport void external_start_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags) { (void)resourceRecord; (void)flags;} +mDNSexport void external_stop_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags) { (void)resourceRecord; (void)flags;} +mDNSexport void external_start_resolving_service(const domainname *const fqdn, DNSServiceFlags flags) { (void)fqdn; (void)flags;} +mDNSexport void external_stop_resolving_service(const domainname *const fqdn, DNSServiceFlags flags) { (void)fqdn; (void)flags;} #endif // ! NO_D2D @@ -797,165 +969,164 @@ mDNSexport void external_stop_resolving_service(const domainname *const fqdn) { // other end, and that device (e.g. a modem bank) is probably not answering Multicast DNS queries anyway. #define MulticastInterface(i) (((i)->ifa_flags & IFF_MULTICAST) && !((i)->ifa_flags & IFF_POINTOPOINT)) -mDNSexport void NotifyOfElusiveBug(const char *title, const char *msg) // Both strings are UTF-8 text - { - 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; - - // Unless ForceAlerts is defined, we only show these bug report alerts on machines that have a 17.x.x.x address - #if !ForceAlerts - { - // Determine if we're at Apple (17.*.*.*) - NetworkInterfaceInfoOSX *i; - 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 - } - #endif - - LogMsg("%s", title); - LogMsg("%s", msg); - // Display a notification to the user - notifyCount++; +mDNSexport void NotifyOfElusiveBug(const char *title, const char *msg) // Both strings are UTF-8 text +{ + 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; + + // Unless ForceAlerts is defined, we only show these bug report alerts on machines that have a 17.x.x.x address + #if !ForceAlerts + { + // Determine if we're at Apple (17.*.*.*) + NetworkInterfaceInfoOSX *i; + 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 + } + #endif + + LogMsg("%s", title); + LogMsg("%s", msg); + // Display a notification to the user + notifyCount++; #ifndef NO_CFUSERNOTIFICATION - mDNSNotify(title, msg); + mDNSNotify(title, msg); #endif /* NO_CFUSERNOTIFICATION */ - } +} mDNSlocal struct ifaddrs *myGetIfAddrs(int refresh) - { - static struct ifaddrs *ifa = NULL; - - if (refresh && ifa) - { - freeifaddrs(ifa); - ifa = NULL; - } +{ + static struct ifaddrs *ifa = NULL; - if (ifa == NULL) getifaddrs(&ifa); + if (refresh && ifa) + { + freeifaddrs(ifa); + ifa = NULL; + } - return ifa; - } + if (ifa == NULL) getifaddrs(&ifa); + return ifa; +} // To match *either* a v4 or v6 instance of this interface name, pass AF_UNSPEC for type 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->ifinfo.ifname, ifname) && - ((type == AF_UNSPEC ) || - (type == AF_INET && i->ifinfo.ip.type == mDNSAddrType_IPv4) || - (type == AF_INET6 && i->ifinfo.ip.type == mDNSAddrType_IPv6))) return(i); - return(NULL); - } +{ + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) + if (i->Exists && !strcmp(i->ifinfo.ifname, ifname) && + ((type == AF_UNSPEC ) || + (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 ifindex, char *name) - { - struct ifaddrs *ifa; - for (ifa = myGetIfAddrs(0); ifa; ifa = ifa->ifa_next) - if (ifa->ifa_addr->sa_family == AF_LINK) - if (((struct sockaddr_dl*)ifa->ifa_addr)->sdl_index == ifindex) - { strlcpy(name, ifa->ifa_name, IF_NAMESIZE); return 0; } - return -1; - } +{ + struct ifaddrs *ifa; + for (ifa = myGetIfAddrs(0); ifa; ifa = ifa->ifa_next) + if (ifa->ifa_addr->sa_family == AF_LINK) + if (((struct sockaddr_dl*)ifa->ifa_addr)->sdl_index == ifindex) + { strlcpy(name, ifa->ifa_name, IF_NAMESIZE); return 0; } + return -1; +} mDNSexport NetworkInterfaceInfoOSX *IfindexToInterfaceInfoOSX(const mDNS *const m, mDNSInterfaceID ifindex) { - mDNSu32 scope_id = (mDNSu32)(uintptr_t)ifindex; - NetworkInterfaceInfoOSX *i; + mDNSu32 scope_id = (mDNSu32)(uintptr_t)ifindex; + NetworkInterfaceInfoOSX *i; - // Don't get tricked by inactive interfaces - for (i = m->p->InterfaceList; i; i = i->next) - if (i->Registered && i->scope_id == scope_id) return(i); + // Don't get tricked by inactive interfaces + for (i = m->p->InterfaceList; i; i = i->next) + if (i->Registered && i->scope_id == scope_id) return(i); - return mDNSNULL; + return mDNSNULL; } mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const m, mDNSu32 ifindex) - { - if (ifindex == kDNSServiceInterfaceIndexLocalOnly) return(mDNSInterface_LocalOnly); - if (ifindex == kDNSServiceInterfaceIndexP2P ) return(mDNSInterface_P2P); - if (ifindex == kDNSServiceInterfaceIndexAny ) return(mDNSNULL); - - NetworkInterfaceInfoOSX* ifi = IfindexToInterfaceInfoOSX(m, (mDNSInterfaceID)(uintptr_t)ifindex); - if (!ifi) - { - // Not found. Make sure our interface list is up to date, then try again. - LogInfo("mDNSPlatformInterfaceIDfromInterfaceIndex: InterfaceID for interface index %d not found; Updating interface list", ifindex); - mDNSMacOSXNetworkChanged(m); - ifi = IfindexToInterfaceInfoOSX(m, (mDNSInterfaceID)(uintptr_t)ifindex); - } - - if (!ifi) return(mDNSNULL); - - return(ifi->ifinfo.InterfaceID); - } +{ + if (ifindex == kDNSServiceInterfaceIndexLocalOnly) return(mDNSInterface_LocalOnly); + if (ifindex == kDNSServiceInterfaceIndexP2P ) return(mDNSInterface_P2P); + if (ifindex == kDNSServiceInterfaceIndexAny ) return(mDNSNULL); + + NetworkInterfaceInfoOSX* ifi = IfindexToInterfaceInfoOSX(m, (mDNSInterfaceID)(uintptr_t)ifindex); + if (!ifi) + { + // Not found. Make sure our interface list is up to date, then try again. + LogInfo("mDNSPlatformInterfaceIDfromInterfaceIndex: InterfaceID for interface index %d not found; Updating interface list", ifindex); + mDNSMacOSXNetworkChanged(m); + ifi = IfindexToInterfaceInfoOSX(m, (mDNSInterfaceID)(uintptr_t)ifindex); + } + + if (!ifi) return(mDNSNULL); + + return(ifi->ifinfo.InterfaceID); +} mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id, mDNSBool suppressNetworkChange) - { - NetworkInterfaceInfoOSX *i; - if (id == mDNSInterface_LocalOnly) return(kDNSServiceInterfaceIndexLocalOnly); - if (id == mDNSInterface_P2P ) return(kDNSServiceInterfaceIndexP2P); - if (id == mDNSInterface_Any ) return(0); +{ + NetworkInterfaceInfoOSX *i; + if (id == mDNSInterface_LocalOnly) return(kDNSServiceInterfaceIndexLocalOnly); + if (id == mDNSInterface_P2P ) return(kDNSServiceInterfaceIndexP2P); + if (id == mDNSInterface_Any ) return(0); - mDNSu32 scope_id = (mDNSu32)(uintptr_t)id; + mDNSu32 scope_id = (mDNSu32)(uintptr_t)id; - // Don't use i->Registered here, because we DO want to find inactive interfaces, which have no Registered set - for (i = m->p->InterfaceList; i; i = i->next) - if (i->scope_id == scope_id) return(i->scope_id); + // Don't use i->Registered here, because we DO want to find inactive interfaces, which have no Registered set + for (i = m->p->InterfaceList; i; i = i->next) + if (i->scope_id == scope_id) return(i->scope_id); - // If we are supposed to suppress network change, return "id" back - if (suppressNetworkChange) return scope_id; + // If we are supposed to suppress network change, return "id" back + if (suppressNetworkChange) return scope_id; - // Not found. Make sure our interface list is up to date, then try again. - LogInfo("Interface index for InterfaceID %p not found; Updating interface list", id); - mDNSMacOSXNetworkChanged(m); - for (i = m->p->InterfaceList; i; i = i->next) - if (i->scope_id == scope_id) return(i->scope_id); + // Not found. Make sure our interface list is up to date, then try again. + LogInfo("Interface index for InterfaceID %p not found; Updating interface list", id); + mDNSMacOSXNetworkChanged(m); + for (i = m->p->InterfaceList; i; i = i->next) + if (i->scope_id == scope_id) return(i->scope_id); - return(0); - } + return(0); +} #if APPLE_OSX_mDNSResponder mDNSexport void mDNSASLLog(uuid_t *uuid, const char *subdomain, const char *result, const char *signature, const char *fmt, ...) - { - if (OSXVers < OSXVers_10_6_SnowLeopard) return; // Only do ASL on Mac OS X 10.6 and later (not on iOS) - - static char buffer[512]; - aslmsg asl_msg = asl_new(ASL_TYPE_MSG); - - if (!asl_msg) { LogMsg("mDNSASLLog: asl_new failed"); return; } - if (uuid) - { - char uuidStr[37]; - uuid_unparse(*uuid, uuidStr); - asl_set (asl_msg, "com.apple.message.uuid", uuidStr); - } - - static char domainBase[] = "com.apple.mDNSResponder.%s"; - mDNS_snprintf (buffer, sizeof(buffer), domainBase, subdomain); - asl_set (asl_msg, "com.apple.message.domain", buffer); - - if (result) asl_set(asl_msg, "com.apple.message.result", result); - if (signature) asl_set(asl_msg, "com.apple.message.signature", signature); - - va_list ptr; - va_start(ptr,fmt); - mDNS_vsnprintf(buffer, sizeof(buffer), fmt, ptr); - va_end(ptr); - - int old_filter = asl_set_filter(NULL,ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); - asl_log(NULL, asl_msg, ASL_LEVEL_DEBUG, "%s", buffer); - asl_set_filter(NULL, old_filter); - asl_free(asl_msg); - } +{ + if (OSXVers < OSXVers_10_6_SnowLeopard) return; // Only do ASL on Mac OS X 10.6 and later (not on iOS) + + static char buffer[512]; + aslmsg asl_msg = asl_new(ASL_TYPE_MSG); + + if (!asl_msg) { LogMsg("mDNSASLLog: asl_new failed"); return; } + if (uuid) + { + char uuidStr[37]; + uuid_unparse(*uuid, uuidStr); + asl_set (asl_msg, "com.apple.message.uuid", uuidStr); + } + + static char domainBase[] = "com.apple.mDNSResponder.%s"; + mDNS_snprintf (buffer, sizeof(buffer), domainBase, subdomain); + asl_set (asl_msg, "com.apple.message.domain", buffer); + + if (result) asl_set(asl_msg, "com.apple.message.result", result); + if (signature) asl_set(asl_msg, "com.apple.message.signature", signature); + + va_list ptr; + va_start(ptr,fmt); + mDNS_vsnprintf(buffer, sizeof(buffer), fmt, ptr); + va_end(ptr); + + int old_filter = asl_set_filter(NULL,ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); + asl_log(NULL, asl_msg, ASL_LEVEL_DEBUG, "%s", buffer); + asl_set_filter(NULL, old_filter); + asl_free(asl_msg); +} #endif // APPLE_OSX_mDNSResponder #if COMPILER_LIKES_PRAGMA_MARK @@ -964,792 +1135,828 @@ mDNSexport void mDNSASLLog(uuid_t *uuid, const char *subdomain, const char *resu #endif mDNSlocal mDNSBool AddrRequiresPPPConnection(const struct sockaddr *addr) - { - mDNSBool result = mDNSfalse; - SCNetworkConnectionFlags flags; - SCNetworkReachabilityRef ReachRef = NULL; +{ + mDNSBool result = mDNSfalse; + SCNetworkConnectionFlags flags; +#if DNSINFO_VERSION >= 20110420 + CFDataRef remote_addr; + CFMutableDictionaryRef options; +#endif // DNSINFO_VERSION >= 20110420 + SCNetworkReachabilityRef ReachRef = NULL; + +#if DNSINFO_VERSION >= 20110420 + options = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + remote_addr = CFDataCreate(NULL, (const UInt8 *)addr, addr->sa_len); + CFDictionarySetValue(options, kSCNetworkReachabilityOptionRemoteAddress, remote_addr); + CFDictionarySetValue(options, kSCNetworkReachabilityOptionServerBypass, kCFBooleanTrue); + ReachRef = SCNetworkReachabilityCreateWithOptions(kCFAllocatorDefault, options); + CFRelease(options); + CFRelease(remote_addr); + if (!ReachRef) { LogMsg("ERROR: RequiresConnection - SCNetworkReachabilityCreateWithOptions"); goto end; } +#else + ReachRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, addr); + if (!ReachRef) { LogMsg("ERROR: RequiresConnection - SCNetworkReachabilityCreateWithAddress"); goto end; } +#endif // DNSINFO_VERSION >= 20110420 + if (!SCNetworkReachabilityGetFlags(ReachRef, &flags)) { LogMsg("ERROR: AddrRequiresPPPConnection - SCNetworkReachabilityGetFlags"); goto end; } + result = flags & kSCNetworkFlagsConnectionRequired; + +end: + if (ReachRef) CFRelease(ReachRef); + return result; +} + +// Set traffic class for socket +mDNSlocal void setTrafficClass(int socketfd, mDNSBool useBackgroundTrafficClass) +{ + int traffic_class; - ReachRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, addr); - if (!ReachRef) { LogMsg("ERROR: RequiresConnection - SCNetworkReachabilityCreateWithAddress"); goto end; } - if (!SCNetworkReachabilityGetFlags(ReachRef, &flags)) { LogMsg("ERROR: AddrRequiresPPPConnection - SCNetworkReachabilityGetFlags"); goto end; } - result = flags & kSCNetworkFlagsConnectionRequired; + if (useBackgroundTrafficClass) + traffic_class = SO_TC_BK_SYS; + else + traffic_class = SO_TC_CTL; - end: - if (ReachRef) CFRelease(ReachRef); - return result; - } + (void) setsockopt(socketfd, SOL_SOCKET, SO_TRAFFIC_CLASS, (void *)&traffic_class, sizeof(traffic_class)); +} // 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" // OR send via our primary v4 unicast socket // UPDATE: The UDPSocket *src parameter now allows the caller to specify the source socket mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end, - mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, mDNSIPPort dstPort) - { - NetworkInterfaceInfoOSX *info = mDNSNULL; - struct sockaddr_storage to; - int s = -1, err; - mStatus result = mStatus_NoError; - - if (InterfaceID) - { - info = IfindexToInterfaceInfoOSX(m, InterfaceID); - if (info == NULL) - { - LogMsg("mDNSPlatformSendUDP: Invalid interface index %p", InterfaceID); - return mStatus_BadParamErr; - } - } - - char *ifa_name = InterfaceID ? info->ifinfo.ifname : "unicast"; - - if (dst->type == mDNSAddrType_IPv4) - { - struct sockaddr_in *sin_to = (struct sockaddr_in*)&to; - sin_to->sin_len = sizeof(*sin_to); - sin_to->sin_family = AF_INET; - sin_to->sin_port = dstPort.NotAnInteger; - sin_to->sin_addr.s_addr = dst->ip.v4.NotAnInteger; - s = (src ? src->ss : m->p->permanentsockets).sktv4; - - if (info) // Specify outgoing interface - { - if (!mDNSAddrIsDNSMulticast(dst)) - { - #ifdef IP_BOUND_IF - if (info->scope_id == 0) - LogInfo("IP_BOUND_IF socket option not set -- info %p (%s) scope_id is zero", info, ifa_name); - else - setsockopt(s, IPPROTO_IP, IP_BOUND_IF, &info->scope_id, sizeof(info->scope_id)); - #else - { - static int displayed = 0; - if (displayed < 1000) - { - displayed++; - LogInfo("IP_BOUND_IF socket option not defined -- cannot specify interface for unicast packets"); - } - } - #endif - } - else - #ifdef IP_MULTICAST_IFINDEX - { - err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IFINDEX, &info->scope_id, sizeof(info->scope_id)); - // We get an error when we compile on a machine that supports this option and run the binary on - // a different machine that does not support it - if (err < 0) - { - if (errno != ENOPROTOOPT) LogInfo("mDNSPlatformSendUDP: setsockopt: IP_MUTLTICAST_IFINDEX returned %d", errno); - err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &info->ifa_v4addr, sizeof(info->ifa_v4addr)); - if (err < 0 && !m->p->NetworkChanged) - LogMsg("setsockopt - IP_MULTICAST_IF error %.4a %d errno %d (%s)", &info->ifa_v4addr, err, errno, strerror(errno)); - } - } - #else - { - err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &info->ifa_v4addr, sizeof(info->ifa_v4addr)); - if (err < 0 && !m->p->NetworkChanged) - LogMsg("setsockopt - IP_MULTICAST_IF error %.4a %d errno %d (%s)", &info->ifa_v4addr, err, errno, strerror(errno)); - - } - #endif - } - } + mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, + mDNSIPPort dstPort, mDNSBool useBackgroundTrafficClass) +{ + NetworkInterfaceInfoOSX *info = mDNSNULL; + struct sockaddr_storage to; + int s = -1, err; + mStatus result = mStatus_NoError; + + if (InterfaceID) + { + info = IfindexToInterfaceInfoOSX(m, InterfaceID); + if (info == NULL) + { + // We may not have registered interfaces with the "core" as we may not have + // seen any interface notifications yet. This typically happens during wakeup + // where we might try to send DNS requests (non-SuppressUnusable questions internal + // to mDNSResponder) before we receive network notifications. + LogInfo("mDNSPlatformSendUDP: Invalid interface index %p", InterfaceID); + return mStatus_BadParamErr; + } + } + + char *ifa_name = InterfaceID ? info->ifinfo.ifname : "unicast"; + + if (dst->type == mDNSAddrType_IPv4) + { + struct sockaddr_in *sin_to = (struct sockaddr_in*)&to; + sin_to->sin_len = sizeof(*sin_to); + sin_to->sin_family = AF_INET; + sin_to->sin_port = dstPort.NotAnInteger; + sin_to->sin_addr.s_addr = dst->ip.v4.NotAnInteger; + s = (src ? src->ss : m->p->permanentsockets).sktv4; + + if (info) // Specify outgoing interface + { + if (!mDNSAddrIsDNSMulticast(dst)) + { + #ifdef IP_BOUND_IF + if (info->scope_id == 0) + LogInfo("IP_BOUND_IF socket option not set -- info %p (%s) scope_id is zero", info, ifa_name); + else + setsockopt(s, IPPROTO_IP, IP_BOUND_IF, &info->scope_id, sizeof(info->scope_id)); + #else + { + static int displayed = 0; + if (displayed < 1000) + { + displayed++; + LogInfo("IP_BOUND_IF socket option not defined -- cannot specify interface for unicast packets"); + } + } + #endif + } + else + #ifdef IP_MULTICAST_IFINDEX + { + err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IFINDEX, &info->scope_id, sizeof(info->scope_id)); + // We get an error when we compile on a machine that supports this option and run the binary on + // a different machine that does not support it + if (err < 0) + { + if (errno != ENOPROTOOPT) LogInfo("mDNSPlatformSendUDP: setsockopt: IP_MUTLTICAST_IFINDEX returned %d", errno); + err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &info->ifa_v4addr, sizeof(info->ifa_v4addr)); + if (err < 0 && !m->p->NetworkChanged) + LogMsg("setsockopt - IP_MULTICAST_IF error %.4a %d errno %d (%s)", &info->ifa_v4addr, err, errno, strerror(errno)); + } + } + #else + { + err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &info->ifa_v4addr, sizeof(info->ifa_v4addr)); + if (err < 0 && !m->p->NetworkChanged) + LogMsg("setsockopt - IP_MULTICAST_IF error %.4a %d errno %d (%s)", &info->ifa_v4addr, err, errno, strerror(errno)); + + } + #endif + } + } #ifndef NO_IPV6 - else if (dst->type == mDNSAddrType_IPv6) - { - struct sockaddr_in6 *sin6_to = (struct sockaddr_in6*)&to; - sin6_to->sin6_len = sizeof(*sin6_to); - sin6_to->sin6_family = AF_INET6; - sin6_to->sin6_port = dstPort.NotAnInteger; - sin6_to->sin6_flowinfo = 0; - sin6_to->sin6_addr = *(struct in6_addr*)&dst->ip.v6; - sin6_to->sin6_scope_id = info ? info->scope_id : 0; - s = (src ? src->ss : m->p->permanentsockets).sktv6; - if (info && mDNSAddrIsDNSMulticast(dst)) // Specify outgoing interface - { - err = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &info->scope_id, sizeof(info->scope_id)); - if (err < 0) - { - char name[IFNAMSIZ]; - if (if_indextoname(info->scope_id, name) != NULL) - LogMsg("setsockopt - IPV6_MULTICAST_IF error %d errno %d (%s)", err, errno, strerror(errno)); - else - LogInfo("setsockopt - IPV6_MUTLICAST_IF scopeid %d, not a valid interface", info->scope_id); - } - } - } + else if (dst->type == mDNSAddrType_IPv6) + { + struct sockaddr_in6 *sin6_to = (struct sockaddr_in6*)&to; + sin6_to->sin6_len = sizeof(*sin6_to); + sin6_to->sin6_family = AF_INET6; + sin6_to->sin6_port = dstPort.NotAnInteger; + sin6_to->sin6_flowinfo = 0; + sin6_to->sin6_addr = *(struct in6_addr*)&dst->ip.v6; + sin6_to->sin6_scope_id = info ? info->scope_id : 0; + s = (src ? src->ss : m->p->permanentsockets).sktv6; + if (info && mDNSAddrIsDNSMulticast(dst)) // Specify outgoing interface + { + err = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &info->scope_id, sizeof(info->scope_id)); + if (err < 0) + { + char name[IFNAMSIZ]; + if (if_indextoname(info->scope_id, name) != NULL) + LogMsg("setsockopt - IPV6_MULTICAST_IF error %d errno %d (%s)", err, errno, strerror(errno)); + else + LogInfo("setsockopt - IPV6_MUTLICAST_IF scopeid %d, not a valid interface", info->scope_id); + } + } + } #endif - else - { - LogMsg("mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!"); + else + { + LogMsg("mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!"); #if ForceAlerts - *(long*)0 = 0; + *(long*)0 = 0; #endif - return mStatus_BadParamErr; - } - - if (s >= 0) - 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 %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 - // If we don't have the corresponding type of socket available, then return mStatus_Invalid - if (s < 0) return(mStatus_Invalid); - - err = sendto(s, msg, (UInt8*)end - (UInt8*)msg, 0, (struct sockaddr *)&to, to.ss_len); - if (err < 0) - { - static int MessageCount = 0; - // Don't report EHOSTDOWN (i.e. ARP failure), ENETDOWN, or no route to host for unicast destinations - if (!mDNSAddressIsAllDNSLinkGroup(dst)) - if (errno == EHOSTDOWN || errno == ENETDOWN || errno == EHOSTUNREACH || errno == ENETUNREACH) return(mStatus_TransientErr); - // 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)(mDNSPlatformRawTime()) < (mDNSu32)(mDNSPlatformOneSecond * 180)) return(mStatus_TransientErr); - // Don't report EADDRNOTAVAIL ("Can't assign requested address") if we're in the middle of a network configuration change - if (errno == EADDRNOTAVAIL && m->p->NetworkChanged) return(mStatus_TransientErr); - if (MessageCount < 1000) - { - MessageCount++; - if (errno == EHOSTUNREACH || errno == EADDRNOTAVAIL || errno == ENETDOWN) - LogInfo("mDNSPlatformSendUDP sendto(%d) failed to send packet on InterfaceID %p %5s/%d to %#a:%d skt %d error %d errno %d (%s) %lu", - s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow)); - else - LogMsg("mDNSPlatformSendUDP sendto(%d) failed to send packet on InterfaceID %p %5s/%d to %#a:%d skt %d error %d errno %d (%s) %lu", - s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow)); - } - result = mStatus_UnknownErr; - } + return mStatus_BadParamErr; + } + + if (s >= 0) + 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 %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 + // If we don't have the corresponding type of socket available, then return mStatus_Invalid + if (s < 0) return(mStatus_Invalid); + + // switch to background traffic class for this message if requested + if (useBackgroundTrafficClass) + setTrafficClass(s, useBackgroundTrafficClass); + + err = sendto(s, msg, (UInt8*)end - (UInt8*)msg, 0, (struct sockaddr *)&to, to.ss_len); + + // set traffic class back to default value + if (useBackgroundTrafficClass) + setTrafficClass(s, mDNSfalse); + + if (err < 0) + { + static int MessageCount = 0; + // Don't report EHOSTDOWN (i.e. ARP failure), ENETDOWN, or no route to host for unicast destinations + if (!mDNSAddressIsAllDNSLinkGroup(dst)) + if (errno == EHOSTDOWN || errno == ENETDOWN || errno == EHOSTUNREACH || errno == ENETUNREACH) return(mStatus_TransientErr); + // 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)(mDNSPlatformRawTime()) < (mDNSu32)(mDNSPlatformOneSecond * 180)) return(mStatus_TransientErr); + // Don't report EADDRNOTAVAIL ("Can't assign requested address") if we're in the middle of a network configuration change + if (errno == EADDRNOTAVAIL && m->p->NetworkChanged) return(mStatus_TransientErr); + if (errno == EHOSTUNREACH || errno == EADDRNOTAVAIL || errno == ENETDOWN) + LogInfo("mDNSPlatformSendUDP sendto(%d) failed to send packet on InterfaceID %p %5s/%d to %#a:%d skt %d error %d errno %d (%s) %lu", + s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow)); + else + { + MessageCount += 100; + if (MessageCount < 1000) // Cap and ensure NO spamming of LogMsgs + LogMsg("mDNSPlatformSendUDP: sendto(%d) failed to send packet on InterfaceID %p %5s/%d to %#a:%d skt %d error %d errno %d (%s) %lu MessageCount is %d", + s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow), MessageCount); + else // If logging is enabled, remove the cap and log aggressively + LogInfo("mDNSPlatformSendUDP: sendto(%d) failed to send packet on InterfaceID %p %5s/%d to %#a:%d skt %d error %d errno %d (%s) %lu MessageCount is %d", + s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow), MessageCount); + } + + result = mStatus_UnknownErr; + } #ifdef IP_BOUND_IF - if (dst->type == mDNSAddrType_IPv4 && info && !mDNSAddrIsDNSMulticast(dst)) - { - static const mDNSu32 ifindex = 0; - setsockopt(s, IPPROTO_IP, IP_BOUND_IF, &ifindex, sizeof(ifindex)); - } + if (dst->type == mDNSAddrType_IPv4 && info && !mDNSAddrIsDNSMulticast(dst)) + { + static const mDNSu32 ifindex = 0; + setsockopt(s, IPPROTO_IP, IP_BOUND_IF, &ifindex, sizeof(ifindex)); + } #endif - return(result); - } + return(result); +} mDNSlocal ssize_t myrecvfrom(const int s, void *const buffer, const size_t max, - struct sockaddr *const from, size_t *const fromlen, mDNSAddr *dstaddr, char ifname[IF_NAMESIZE], mDNSu8 *ttl) - { - static unsigned int numLogMessages = 0; - struct iovec databuffers = { (char *)buffer, max }; - struct msghdr msg; - ssize_t n; - struct cmsghdr *cmPtr; - char ancillary[1024]; - - *ttl = 255; // If kernel fails to provide TTL data (e.g. Jaguar doesn't) then assume the TTL was 255 as it should be - - // Set up the message - msg.msg_name = (caddr_t)from; - msg.msg_namelen = *fromlen; - msg.msg_iov = &databuffers; - msg.msg_iovlen = 1; - msg.msg_control = (caddr_t)&ancillary; - msg.msg_controllen = sizeof(ancillary); - msg.msg_flags = 0; - - // Receive the data - n = recvmsg(s, &msg, 0); - if (n<0) - { - if (errno != EWOULDBLOCK && numLogMessages++ < 100) LogMsg("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("mDNSMacOSX.c: recvmsg(%d) returned %d msg.msg_controllen %d < sizeof(struct cmsghdr) %lu", - s, n, msg.msg_controllen, sizeof(struct cmsghdr)); - return(-1); - } - if (msg.msg_flags & MSG_CTRUNC) - { - if (numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) msg.msg_flags & MSG_CTRUNC", s); - return(-1); - } - - *fromlen = msg.msg_namelen; - - // Parse each option out of the ancillary data. - for (cmPtr = CMSG_FIRSTHDR(&msg); cmPtr; cmPtr = CMSG_NXTHDR(&msg, cmPtr)) - { - // debugf("myrecvfrom cmsg_level %d cmsg_type %d", cmPtr->cmsg_level, cmPtr->cmsg_type); - if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVDSTADDR) - { - dstaddr->type = mDNSAddrType_IPv4; - dstaddr->ip.v4 = *(mDNSv4Addr*)CMSG_DATA(cmPtr); - //LogMsg("mDNSMacOSX.c: recvmsg IP_RECVDSTADDR %.4a", &dstaddr->ip.v4); - } - if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVIF) - { - struct sockaddr_dl *sdl = (struct sockaddr_dl *)CMSG_DATA(cmPtr); - if (sdl->sdl_nlen < IF_NAMESIZE) - { - mDNSPlatformMemCopy(ifname, sdl->sdl_data, sdl->sdl_nlen); - ifname[sdl->sdl_nlen] = 0; - // debugf("IP_RECVIF sdl_index %d, sdl_data %s len %d", sdl->sdl_index, ifname, sdl->sdl_nlen); - } - } - if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVTTL) - *ttl = *(u_char*)CMSG_DATA(cmPtr); - if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_PKTINFO) - { - struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmPtr); - dstaddr->type = mDNSAddrType_IPv6; - dstaddr->ip.v6 = *(mDNSv6Addr*)&ip6_info->ipi6_addr; - myIfIndexToName(ip6_info->ipi6_ifindex, ifname); - } - if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_HOPLIMIT) - *ttl = *(int*)CMSG_DATA(cmPtr); - } - - return(n); - } + struct sockaddr *const from, size_t *const fromlen, mDNSAddr *dstaddr, char ifname[IF_NAMESIZE], mDNSu8 *ttl) +{ + static unsigned int numLogMessages = 0; + struct iovec databuffers = { (char *)buffer, max }; + struct msghdr msg; + ssize_t n; + struct cmsghdr *cmPtr; + char ancillary[1024]; + + *ttl = 255; // If kernel fails to provide TTL data (e.g. Jaguar doesn't) then assume the TTL was 255 as it should be + + // Set up the message + msg.msg_name = (caddr_t)from; + msg.msg_namelen = *fromlen; + msg.msg_iov = &databuffers; + msg.msg_iovlen = 1; + msg.msg_control = (caddr_t)&ancillary; + msg.msg_controllen = sizeof(ancillary); + msg.msg_flags = 0; + + // Receive the data + n = recvmsg(s, &msg, 0); + if (n<0) + { + if (errno != EWOULDBLOCK && numLogMessages++ < 100) LogMsg("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("mDNSMacOSX.c: recvmsg(%d) returned %d msg.msg_controllen %d < sizeof(struct cmsghdr) %lu", + s, n, msg.msg_controllen, sizeof(struct cmsghdr)); + return(-1); + } + if (msg.msg_flags & MSG_CTRUNC) + { + if (numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) msg.msg_flags & MSG_CTRUNC", s); + return(-1); + } + + *fromlen = msg.msg_namelen; + + // Parse each option out of the ancillary data. + for (cmPtr = CMSG_FIRSTHDR(&msg); cmPtr; cmPtr = CMSG_NXTHDR(&msg, cmPtr)) + { + // debugf("myrecvfrom cmsg_level %d cmsg_type %d", cmPtr->cmsg_level, cmPtr->cmsg_type); + if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVDSTADDR) + { + dstaddr->type = mDNSAddrType_IPv4; + dstaddr->ip.v4 = *(mDNSv4Addr*)CMSG_DATA(cmPtr); + //LogMsg("mDNSMacOSX.c: recvmsg IP_RECVDSTADDR %.4a", &dstaddr->ip.v4); + } + if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVIF) + { + struct sockaddr_dl *sdl = (struct sockaddr_dl *)CMSG_DATA(cmPtr); + if (sdl->sdl_nlen < IF_NAMESIZE) + { + mDNSPlatformMemCopy(ifname, sdl->sdl_data, sdl->sdl_nlen); + ifname[sdl->sdl_nlen] = 0; + // debugf("IP_RECVIF sdl_index %d, sdl_data %s len %d", sdl->sdl_index, ifname, sdl->sdl_nlen); + } + } + if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVTTL) + *ttl = *(u_char*)CMSG_DATA(cmPtr); + if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_PKTINFO) + { + struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmPtr); + dstaddr->type = mDNSAddrType_IPv6; + dstaddr->ip.v6 = *(mDNSv6Addr*)&ip6_info->ipi6_addr; + myIfIndexToName(ip6_info->ipi6_ifindex, ifname); + } + if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_HOPLIMIT) + *ttl = *(int*)CMSG_DATA(cmPtr); + } + + return(n); +} mDNSlocal void myKQSocketCallBack(int s1, short filter, void *context) - { - KQSocketSet *const ss = (KQSocketSet *)context; - mDNS *const m = ss->m; - int err = 0, count = 0, closed = 0; +{ + KQSocketSet *const ss = (KQSocketSet *)context; + mDNS *const m = ss->m; + int err = 0, count = 0, closed = 0; - if (filter != EVFILT_READ) - LogMsg("myKQSocketCallBack: Why is filter %d not EVFILT_READ (%d)?", filter, EVFILT_READ); + if (filter != EVFILT_READ) + LogMsg("myKQSocketCallBack: Why is filter %d not EVFILT_READ (%d)?", filter, EVFILT_READ); - if (s1 != ss->sktv4 + if (s1 != ss->sktv4 #ifndef NO_IPV6 - && s1 != ss->sktv6 + && s1 != ss->sktv6 #endif - ) - { - LogMsg("myKQSocketCallBack: native socket %d", s1); - LogMsg("myKQSocketCallBack: sktv4 %d", ss->sktv4); + ) + { + LogMsg("myKQSocketCallBack: native socket %d", s1); + LogMsg("myKQSocketCallBack: sktv4 %d", ss->sktv4); #ifndef NO_IPV6 - LogMsg("myKQSocketCallBack: sktv6 %d", ss->sktv6); + LogMsg("myKQSocketCallBack: sktv6 %d", ss->sktv6); #endif - } - - while (!closed) - { - mDNSAddr senderAddr, destAddr; - mDNSIPPort senderPort; - struct sockaddr_storage from; - size_t fromlen = sizeof(from); - char packetifname[IF_NAMESIZE] = ""; - mDNSu8 ttl; - err = myrecvfrom(s1, &m->imsg, sizeof(m->imsg), (struct sockaddr *)&from, &fromlen, &destAddr, packetifname, &ttl); - if (err < 0) break; - - count++; - if (from.ss_family == AF_INET) - { - struct sockaddr_in *s = (struct sockaddr_in*)&from; - senderAddr.type = mDNSAddrType_IPv4; - senderAddr.ip.v4.NotAnInteger = s->sin_addr.s_addr; - senderPort.NotAnInteger = s->sin_port; - //LogInfo("myKQSocketCallBack received IPv4 packet from %#-15a to %#-15a on skt %d %s", &senderAddr, &destAddr, s1, packetifname); - } - else if (from.ss_family == AF_INET6) - { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&from; - senderAddr.type = mDNSAddrType_IPv6; - senderAddr.ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr; - senderPort.NotAnInteger = sin6->sin6_port; - //LogInfo("myKQSocketCallBack received IPv6 packet from %#-15a to %#-15a on skt %d %s", &senderAddr, &destAddr, s1, packetifname); - } - else - { - LogMsg("myKQSocketCallBack from is unknown address family %d", from.ss_family); - return; - } - - // Note: When handling multiple packets in a batch, MUST reset InterfaceID before handling each packet - mDNSInterfaceID InterfaceID = mDNSNULL; - //NetworkInterfaceInfo *intf = m->HostInterfaces; - //while (intf && strcmp(intf->ifname, packetifname)) intf = intf->next; - - NetworkInterfaceInfoOSX *intf = m->p->InterfaceList; - while (intf && strcmp(intf->ifinfo.ifname, packetifname)) intf = intf->next; - - // When going to sleep we deregister all our interfaces, but if the machine - // takes a few seconds to sleep we may continue to receive multicasts - // during that time, which would confuse mDNSCoreReceive, because as far - // as it's concerned, we should have no active interfaces any more. - // Hence we ignore multicasts for which we can find no matching InterfaceID. - if (intf) InterfaceID = intf->ifinfo.InterfaceID; - else if (mDNSAddrIsDNSMulticast(&destAddr)) continue; + } + + while (!closed) + { + mDNSAddr senderAddr, destAddr; + mDNSIPPort senderPort; + struct sockaddr_storage from; + size_t fromlen = sizeof(from); + char packetifname[IF_NAMESIZE] = ""; + mDNSu8 ttl; + err = myrecvfrom(s1, &m->imsg, sizeof(m->imsg), (struct sockaddr *)&from, &fromlen, &destAddr, packetifname, &ttl); + if (err < 0) break; + + count++; + if (from.ss_family == AF_INET) + { + struct sockaddr_in *s = (struct sockaddr_in*)&from; + senderAddr.type = mDNSAddrType_IPv4; + senderAddr.ip.v4.NotAnInteger = s->sin_addr.s_addr; + senderPort.NotAnInteger = s->sin_port; + //LogInfo("myKQSocketCallBack received IPv4 packet from %#-15a to %#-15a on skt %d %s", &senderAddr, &destAddr, s1, packetifname); + } + else if (from.ss_family == AF_INET6) + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&from; + senderAddr.type = mDNSAddrType_IPv6; + senderAddr.ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr; + senderPort.NotAnInteger = sin6->sin6_port; + //LogInfo("myKQSocketCallBack received IPv6 packet from %#-15a to %#-15a on skt %d %s", &senderAddr, &destAddr, s1, packetifname); + } + else + { + LogMsg("myKQSocketCallBack from is unknown address family %d", from.ss_family); + return; + } + + // Note: When handling multiple packets in a batch, MUST reset InterfaceID before handling each packet + mDNSInterfaceID InterfaceID = mDNSNULL; + //NetworkInterfaceInfo *intf = m->HostInterfaces; + //while (intf && strcmp(intf->ifname, packetifname)) intf = intf->next; + + NetworkInterfaceInfoOSX *intf = m->p->InterfaceList; + while (intf && strcmp(intf->ifinfo.ifname, packetifname)) intf = intf->next; + + // When going to sleep we deregister all our interfaces, but if the machine + // takes a few seconds to sleep we may continue to receive multicasts + // during that time, which would confuse mDNSCoreReceive, because as far + // as it's concerned, we should have no active interfaces any more. + // Hence we ignore multicasts for which we can find no matching InterfaceID. + if (intf) InterfaceID = intf->ifinfo.InterfaceID; + else if (mDNSAddrIsDNSMulticast(&destAddr)) continue; // LogMsg("myKQSocketCallBack got packet from %#a to %#a on interface %#a/%s", // &senderAddr, &destAddr, &ss->info->ifinfo.ip, ss->info->ifinfo.ifname); - // mDNSCoreReceive may close the socket we're reading from. We must break out of our - // loop when that happens, or we may try to read from an invalid FD. We do this by - // setting the closeFlag pointer in the socketset, so CloseSocketSet can inform us - // if it closes the socketset. - ss->closeFlag = &closed; - - mDNSCoreReceive(m, &m->imsg, (unsigned char*)&m->imsg + err, &senderAddr, senderPort, &destAddr, ss->port, InterfaceID); - - // if we didn't close, we can safely dereference the socketset, and should to - // reset the closeFlag, since it points to something on the stack - if (!closed) ss->closeFlag = mDNSNULL; - } - - if (err < 0 && (errno != EWOULDBLOCK || count == 0)) - { - // Something is busted here. - // kqueue says there is a packet, but myrecvfrom says there is not. - // Try calling select() to get another opinion. - // Find out about other socket parameter that can help understand why select() says the socket is ready for read - // All of this is racy, as data may have arrived after the call to select() - static unsigned int numLogMessages = 0; - int save_errno = errno; - int so_error = -1; - int so_nread = -1; - int fionread = -1; - socklen_t solen = sizeof(int); - fd_set readfds; - struct timeval timeout; - int selectresult; - FD_ZERO(&readfds); - FD_SET(s1, &readfds); - timeout.tv_sec = 0; - timeout.tv_usec = 0; - selectresult = select(s1+1, &readfds, NULL, NULL, &timeout); - if (getsockopt(s1, SOL_SOCKET, SO_ERROR, &so_error, &solen) == -1) - LogMsg("myKQSocketCallBack getsockopt(SO_ERROR) error %d", errno); - if (getsockopt(s1, SOL_SOCKET, SO_NREAD, &so_nread, &solen) == -1) - LogMsg("myKQSocketCallBack getsockopt(SO_NREAD) error %d", errno); - if (ioctl(s1, FIONREAD, &fionread) == -1) - LogMsg("myKQSocketCallBack ioctl(FIONREAD) error %d", errno); - if (numLogMessages++ < 100) - LogMsg("myKQSocketCallBack 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)", - "Congratulations, you've reproduced an elusive bug.\r" - "Please contact the current assignee of .\r" - "Alternatively, you can send email to radar-3387020@group.apple.com. (Note number is different.)\r" - "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 - } - } + // mDNSCoreReceive may close the socket we're reading from. We must break out of our + // loop when that happens, or we may try to read from an invalid FD. We do this by + // setting the closeFlag pointer in the socketset, so CloseSocketSet can inform us + // if it closes the socketset. + ss->closeFlag = &closed; + + mDNSCoreReceive(m, &m->imsg, (unsigned char*)&m->imsg + err, &senderAddr, senderPort, &destAddr, ss->port, InterfaceID); + + // if we didn't close, we can safely dereference the socketset, and should to + // reset the closeFlag, since it points to something on the stack + if (!closed) ss->closeFlag = mDNSNULL; + } + + if (err < 0 && (errno != EWOULDBLOCK || count == 0)) + { + // Something is busted here. + // kqueue says there is a packet, but myrecvfrom says there is not. + // Try calling select() to get another opinion. + // Find out about other socket parameter that can help understand why select() says the socket is ready for read + // All of this is racy, as data may have arrived after the call to select() + static unsigned int numLogMessages = 0; + int save_errno = errno; + int so_error = -1; + int so_nread = -1; + int fionread = -1; + socklen_t solen = sizeof(int); + fd_set readfds; + struct timeval timeout; + int selectresult; + FD_ZERO(&readfds); + FD_SET(s1, &readfds); + timeout.tv_sec = 0; + timeout.tv_usec = 0; + selectresult = select(s1+1, &readfds, NULL, NULL, &timeout); + if (getsockopt(s1, SOL_SOCKET, SO_ERROR, &so_error, &solen) == -1) + LogMsg("myKQSocketCallBack getsockopt(SO_ERROR) error %d", errno); + if (getsockopt(s1, SOL_SOCKET, SO_NREAD, &so_nread, &solen) == -1) + LogMsg("myKQSocketCallBack getsockopt(SO_NREAD) error %d", errno); + if (ioctl(s1, FIONREAD, &fionread) == -1) + LogMsg("myKQSocketCallBack ioctl(FIONREAD) error %d", errno); + if (numLogMessages++ < 100) + LogMsg("myKQSocketCallBack 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)", + "Congratulations, you've reproduced an elusive bug.\r" + "Please contact the current assignee of .\r" + "Alternatively, you can send email to radar-3387020@group.apple.com. (Note number is different.)\r" + "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 typedef enum - { - handshake_required, - handshake_in_progress, - handshake_completed, - handshake_to_be_closed - } handshakeStatus; - +{ + handshake_required, + handshake_in_progress, + handshake_completed, + handshake_to_be_closed +} handshakeStatus; + struct TCPSocket_struct - { - TCPSocketFlags flags; // MUST BE FIRST FIELD -- mDNSCore expects every TCPSocket_struct to begin with TCPSocketFlags flags - TCPConnectionCallback callback; - int fd; - KQueueEntry *kqEntry; - KQSocketSet ss; +{ + TCPSocketFlags flags; // MUST BE FIRST FIELD -- mDNSCore expects every TCPSocket_struct to begin with TCPSocketFlags flags + TCPConnectionCallback callback; + int fd; + KQueueEntry *kqEntry; + KQSocketSet ss; #ifndef NO_SECURITYFRAMEWORK - SSLContextRef tlsContext; - pthread_t handshake_thread; + SSLContextRef tlsContext; + pthread_t handshake_thread; #endif /* NO_SECURITYFRAMEWORK */ - domainname hostname; - void *context; - mDNSBool setup; - mDNSBool connected; - handshakeStatus handshake; - mDNS *m; // So we can call KQueueLock from the SSLHandshake thread - mStatus err; - }; + domainname hostname; + void *context; + mDNSBool setup; + mDNSBool connected; + handshakeStatus handshake; + mDNS *m; // So we can call KQueueLock from the SSLHandshake thread + mStatus err; +}; mDNSlocal void doTcpSocketCallback(TCPSocket *sock) - { - mDNSBool c = !sock->connected; - sock->connected = mDNStrue; - sock->callback(sock, sock->context, c, sock->err); - // Note: the callback may call CloseConnection here, which frees the context structure! - } +{ + mDNSBool c = !sock->connected; + sock->connected = mDNStrue; + sock->callback(sock, sock->context, c, sock->err); + // Note: the callback may call CloseConnection here, which frees the context structure! +} #ifndef NO_SECURITYFRAMEWORK mDNSlocal OSStatus tlsWriteSock(SSLConnectionRef connection, const void *data, size_t *dataLength) - { - int ret = send(((TCPSocket *)connection)->fd, data, *dataLength, 0); - if (ret >= 0 && (size_t)ret < *dataLength) { *dataLength = ret; return(errSSLWouldBlock); } - if (ret >= 0) { *dataLength = ret; return(noErr); } - *dataLength = 0; - if (errno == EAGAIN ) return(errSSLWouldBlock); - if (errno == ENOENT ) return(errSSLClosedGraceful); - if (errno == EPIPE || errno == ECONNRESET) return(errSSLClosedAbort); - LogMsg("ERROR: tlsWriteSock: %d error %d (%s)\n", ((TCPSocket *)connection)->fd, errno, strerror(errno)); - return(errSSLClosedAbort); - } +{ + int ret = send(((TCPSocket *)connection)->fd, data, *dataLength, 0); + if (ret >= 0 && (size_t)ret < *dataLength) { *dataLength = ret; return(errSSLWouldBlock); } + if (ret >= 0) { *dataLength = ret; return(noErr); } + *dataLength = 0; + if (errno == EAGAIN ) return(errSSLWouldBlock); + if (errno == ENOENT ) return(errSSLClosedGraceful); + if (errno == EPIPE || errno == ECONNRESET) return(errSSLClosedAbort); + LogMsg("ERROR: tlsWriteSock: %d error %d (%s)\n", ((TCPSocket *)connection)->fd, errno, strerror(errno)); + return(errSSLClosedAbort); +} mDNSlocal OSStatus tlsReadSock(SSLConnectionRef connection, void *data, size_t *dataLength) - { - int ret = recv(((TCPSocket *)connection)->fd, data, *dataLength, 0); - if (ret > 0 && (size_t)ret < *dataLength) { *dataLength = ret; return(errSSLWouldBlock); } - if (ret > 0) { *dataLength = ret; return(noErr); } - *dataLength = 0; - if (ret == 0 || errno == ENOENT ) return(errSSLClosedGraceful); - if ( errno == EAGAIN ) return(errSSLWouldBlock); - if ( errno == ECONNRESET) return(errSSLClosedAbort); - LogMsg("ERROR: tlsSockRead: error %d (%s)\n", errno, strerror(errno)); - return(errSSLClosedAbort); - } +{ + int ret = recv(((TCPSocket *)connection)->fd, data, *dataLength, 0); + if (ret > 0 && (size_t)ret < *dataLength) { *dataLength = ret; return(errSSLWouldBlock); } + if (ret > 0) { *dataLength = ret; return(noErr); } + *dataLength = 0; + if (ret == 0 || errno == ENOENT ) return(errSSLClosedGraceful); + if ( errno == EAGAIN ) return(errSSLWouldBlock); + if ( errno == ECONNRESET) return(errSSLClosedAbort); + LogMsg("ERROR: tlsSockRead: error %d (%s)\n", errno, strerror(errno)); + return(errSSLClosedAbort); +} mDNSlocal OSStatus tlsSetupSock(TCPSocket *sock, mDNSBool server) - { - char domname_cstr[MAX_ESCAPED_DOMAIN_NAME]; +{ + char domname_cstr[MAX_ESCAPED_DOMAIN_NAME]; - mStatus err = SSLNewContext(server, &sock->tlsContext); - if (err) { LogMsg("ERROR: tlsSetupSock: SSLNewContext failed with error code: %d", err); return(err); } + mStatus err = SSLNewContext(server, &sock->tlsContext); + if (err) { LogMsg("ERROR: tlsSetupSock: SSLNewContext failed with error code: %d", err); return(err); } - err = SSLSetIOFuncs(sock->tlsContext, tlsReadSock, tlsWriteSock); - if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetIOFuncs failed with error code: %d", err); return(err); } + err = SSLSetIOFuncs(sock->tlsContext, tlsReadSock, tlsWriteSock); + if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetIOFuncs failed with error code: %d", err); return(err); } - err = SSLSetConnection(sock->tlsContext, (SSLConnectionRef) sock); - if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetConnection failed with error code: %d", err); return(err); } + err = SSLSetConnection(sock->tlsContext, (SSLConnectionRef) sock); + if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetConnection failed with error code: %d", err); return(err); } - // Instead of listing all the acceptable ciphers, we just disable the bad ciphers. It does not disable - // all the bad ciphers like RC4_MD5, but it assumes that the servers don't offer them. - err = SSLSetAllowAnonymousCiphers(sock->tlsContext, 0); - if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetAllowAnonymousCiphers failed with error code: %d", err); return(err); } + // Instead of listing all the acceptable ciphers, we just disable the bad ciphers. It does not disable + // all the bad ciphers like RC4_MD5, but it assumes that the servers don't offer them. + err = SSLSetAllowAnonymousCiphers(sock->tlsContext, 0); + if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetAllowAnonymousCiphers failed with error code: %d", err); return(err); } - // We already checked for NULL in hostname and this should never happen. Hence, returning -1 - // (error not in OSStatus space) is okay. - if (!sock->hostname.c[0]) {LogMsg("ERROR: tlsSetupSock: hostname NULL"); return -1; } + // We already checked for NULL in hostname and this should never happen. Hence, returning -1 + // (error not in OSStatus space) is okay. + if (!sock->hostname.c[0]) {LogMsg("ERROR: tlsSetupSock: hostname NULL"); return -1; } - ConvertDomainNameToCString(&sock->hostname, domname_cstr); - err = SSLSetPeerDomainName(sock->tlsContext, domname_cstr, strlen(domname_cstr)); - if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetPeerDomainname: %s failed with error code: %d", domname_cstr, err); return(err); } + ConvertDomainNameToCString(&sock->hostname, domname_cstr); + err = SSLSetPeerDomainName(sock->tlsContext, domname_cstr, strlen(domname_cstr)); + if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetPeerDomainname: %s failed with error code: %d", domname_cstr, err); return(err); } - return(err); - } + return(err); +} #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM mDNSlocal void doSSLHandshake(void *ctx) - { - TCPSocket *sock = (TCPSocket*)ctx; - mStatus err = SSLHandshake(sock->tlsContext); - - //Can't have multiple threads in mDNS core. When MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM is - //defined, KQueueLock is a noop. Hence we need to serialize here - // - //NOTE: We just can't serialize doTcpSocketCallback alone on the main queue. - //We need the rest of the logic also. Otherwise, we can enable the READ - //events below, dispatch a doTcpSocketCallback on the main queue. Assume it is - //ConnFailed which means we are going to free the tcpInfo. While it - //is waiting to be dispatched, another read event can come into tcpKQSocketCallback - //and potentially call doTCPCallback with error which can close the fd and free the - //tcpInfo. Later when the thread gets dispatched it will crash because the tcpInfo - //is already freed. - - dispatch_async(dispatch_get_main_queue(), ^{ - - LogInfo("doSSLHandshake %p: got lock", sock); // Log *after* we get the lock - - if (sock->handshake == handshake_to_be_closed) - { - LogInfo("SSLHandshake completed after close"); - mDNSPlatformTCPCloseConnection(sock); - } - else - { - if (sock->fd != -1) KQueueSet(sock->fd, EV_ADD, EVFILT_READ, sock->kqEntry); - else LogMsg("doSSLHandshake: sock->fd is -1"); - - if (err == errSSLWouldBlock) - sock->handshake = handshake_required; - else - { - if (err) - { - LogMsg("SSLHandshake failed: %d%s", err, err == errSSLPeerInternalError ? " (server busy)" : ""); - SSLDisposeContext(sock->tlsContext); - sock->tlsContext = NULL; - } - - sock->err = err ? mStatus_ConnFailed : 0; - sock->handshake = handshake_completed; - - LogInfo("doSSLHandshake: %p calling doTcpSocketCallback fd %d", sock, sock->fd); - doTcpSocketCallback(sock); - } - } - - LogInfo("SSLHandshake %p: dropping lock for fd %d", sock, sock->fd); - return; - }); - } +{ + TCPSocket *sock = (TCPSocket*)ctx; + mStatus err = SSLHandshake(sock->tlsContext); + + //Can't have multiple threads in mDNS core. When MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM is + //defined, KQueueLock is a noop. Hence we need to serialize here + // + //NOTE: We just can't serialize doTcpSocketCallback alone on the main queue. + //We need the rest of the logic also. Otherwise, we can enable the READ + //events below, dispatch a doTcpSocketCallback on the main queue. Assume it is + //ConnFailed which means we are going to free the tcpInfo. While it + //is waiting to be dispatched, another read event can come into tcpKQSocketCallback + //and potentially call doTCPCallback with error which can close the fd and free the + //tcpInfo. Later when the thread gets dispatched it will crash because the tcpInfo + //is already freed. + + dispatch_async(dispatch_get_main_queue(), ^{ + + LogInfo("doSSLHandshake %p: got lock", sock); // Log *after* we get the lock + + if (sock->handshake == handshake_to_be_closed) + { + LogInfo("SSLHandshake completed after close"); + mDNSPlatformTCPCloseConnection(sock); + } + else + { + if (sock->fd != -1) KQueueSet(sock->fd, EV_ADD, EVFILT_READ, sock->kqEntry); + else LogMsg("doSSLHandshake: sock->fd is -1"); + + if (err == errSSLWouldBlock) + sock->handshake = handshake_required; + else + { + if (err) + { + LogMsg("SSLHandshake failed: %d%s", err, err == errSSLPeerInternalError ? " (server busy)" : ""); + SSLDisposeContext(sock->tlsContext); + sock->tlsContext = NULL; + } + + sock->err = err ? mStatus_ConnFailed : 0; + sock->handshake = handshake_completed; + + LogInfo("doSSLHandshake: %p calling doTcpSocketCallback fd %d", sock, sock->fd); + doTcpSocketCallback(sock); + } + } + + LogInfo("SSLHandshake %p: dropping lock for fd %d", sock, sock->fd); + return; + }); +} #else mDNSlocal void *doSSLHandshake(void *ctx) - { - // Warning: Touching sock without the kqueue lock! - // We're protected because sock->handshake == handshake_in_progress - TCPSocket *sock = (TCPSocket*)ctx; - mDNS * const m = sock->m; // Get m now, as we may free sock if marked to be closed while we're waiting on SSLHandshake - mStatus err = SSLHandshake(sock->tlsContext); - - KQueueLock(m); - debugf("doSSLHandshake %p: got lock", sock); // Log *after* we get the lock - - if (sock->handshake == handshake_to_be_closed) - { - LogInfo("SSLHandshake completed after close"); - mDNSPlatformTCPCloseConnection(sock); - } - else - { - if (sock->fd != -1) KQueueSet(sock->fd, EV_ADD, EVFILT_READ, sock->kqEntry); - else LogMsg("doSSLHandshake: sock->fd is -1"); - - if (err == errSSLWouldBlock) - sock->handshake = handshake_required; - else - { - if (err) - { - LogMsg("SSLHandshake failed: %d%s", err, err == errSSLPeerInternalError ? " (server busy)" : ""); - SSLDisposeContext(sock->tlsContext); - sock->tlsContext = NULL; - } - - sock->err = err ? mStatus_ConnFailed : 0; - sock->handshake = handshake_completed; - - debugf("doSSLHandshake: %p calling doTcpSocketCallback fd %d", sock, sock->fd); - doTcpSocketCallback(sock); - } - } - - debugf("SSLHandshake %p: dropping lock for fd %d", sock, sock->fd); - KQueueUnlock(m, "doSSLHandshake"); - return NULL; - } +{ + // Warning: Touching sock without the kqueue lock! + // We're protected because sock->handshake == handshake_in_progress + TCPSocket *sock = (TCPSocket*)ctx; + mDNS * const m = sock->m; // Get m now, as we may free sock if marked to be closed while we're waiting on SSLHandshake + mStatus err = SSLHandshake(sock->tlsContext); + + KQueueLock(m); + debugf("doSSLHandshake %p: got lock", sock); // Log *after* we get the lock + + if (sock->handshake == handshake_to_be_closed) + { + LogInfo("SSLHandshake completed after close"); + mDNSPlatformTCPCloseConnection(sock); + } + else + { + if (sock->fd != -1) KQueueSet(sock->fd, EV_ADD, EVFILT_READ, sock->kqEntry); + else LogMsg("doSSLHandshake: sock->fd is -1"); + + if (err == errSSLWouldBlock) + sock->handshake = handshake_required; + else + { + if (err) + { + LogMsg("SSLHandshake failed: %d%s", err, err == errSSLPeerInternalError ? " (server busy)" : ""); + SSLDisposeContext(sock->tlsContext); + sock->tlsContext = NULL; + } + + sock->err = err ? mStatus_ConnFailed : 0; + sock->handshake = handshake_completed; + + debugf("doSSLHandshake: %p calling doTcpSocketCallback fd %d", sock, sock->fd); + doTcpSocketCallback(sock); + } + } + + debugf("SSLHandshake %p: dropping lock for fd %d", sock, sock->fd); + KQueueUnlock(m, "doSSLHandshake"); + return NULL; +} #endif mDNSlocal mStatus spawnSSLHandshake(TCPSocket* sock) - { - debugf("spawnSSLHandshake %p: entry", sock); - mStatus err; +{ + debugf("spawnSSLHandshake %p: entry", sock); + mStatus err; - if (sock->handshake != handshake_required) LogMsg("spawnSSLHandshake: handshake status not required: %d", sock->handshake); - sock->handshake = handshake_in_progress; - KQueueSet(sock->fd, EV_DELETE, EVFILT_READ, sock->kqEntry); + if (sock->handshake != handshake_required) LogMsg("spawnSSLHandshake: handshake status not required: %d", sock->handshake); + sock->handshake = handshake_in_progress; + KQueueSet(sock->fd, EV_DELETE, EVFILT_READ, sock->kqEntry); #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM - // Dispatch it on a separate serial queue to avoid deadlocks with threads running on main queue - dispatch_async(SSLqueue, ^{doSSLHandshake(sock);}); - err = 0; + // Dispatch it on a separate serial queue to avoid deadlocks with threads running on main queue + dispatch_async(SSLqueue, ^{doSSLHandshake(sock);}); + err = 0; #else - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - err = pthread_create(&sock->handshake_thread, &attr, doSSLHandshake, sock); - pthread_attr_destroy(&attr); - if (err) - { - LogMsg("Could not start SSLHandshake thread: (%d) %s", err, strerror(err)); - sock->handshake = handshake_completed; - sock->err = err; - KQueueSet(sock->fd, EV_ADD, EVFILT_READ, sock->kqEntry); - } + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + err = pthread_create(&sock->handshake_thread, &attr, doSSLHandshake, sock); + pthread_attr_destroy(&attr); + if (err) + { + LogMsg("Could not start SSLHandshake thread: (%d) %s", err, strerror(err)); + sock->handshake = handshake_completed; + sock->err = err; + KQueueSet(sock->fd, EV_ADD, EVFILT_READ, sock->kqEntry); + } #endif - debugf("spawnSSLHandshake %p: done for %d", sock, sock->fd); - return err; - } - -mDNSlocal mDNSBool IsTunnelModeDomain(const domainname *d) - { - static const domainname *mmc = (const domainname*) "\x7" "members" "\x3" "mac" "\x3" "com"; - const domainname *d1 = mDNSNULL; // TLD - const domainname *d2 = mDNSNULL; // SLD - const domainname *d3 = mDNSNULL; - while (d->c[0]) { d3 = d2; d2 = d1; d1 = d; d = (const domainname*)(d->c + 1 + d->c[0]); } - return(d3 && SameDomainName(d3, mmc)); - } + debugf("spawnSSLHandshake %p: done for %d", sock, sock->fd); + return err; +} #endif /* NO_SECURITYFRAMEWORK */ mDNSlocal void tcpKQSocketCallback(__unused int fd, short filter, void *context) - { - TCPSocket *sock = context; - sock->err = mStatus_NoError; +{ + TCPSocket *sock = context; + sock->err = mStatus_NoError; - //if (filter == EVFILT_READ ) LogMsg("myKQSocketCallBack: tcpKQSocketCallback %d is EVFILT_READ", filter); - //if (filter == EVFILT_WRITE) LogMsg("myKQSocketCallBack: tcpKQSocketCallback %d is EVFILT_WRITE", filter); - // EV_ONESHOT doesn't seem to work, so we add the filter with EV_ADD, and explicitly delete it here with EV_DELETE - if (filter == EVFILT_WRITE) KQueueSet(sock->fd, EV_DELETE, EVFILT_WRITE, sock->kqEntry); + //if (filter == EVFILT_READ ) LogMsg("myKQSocketCallBack: tcpKQSocketCallback %d is EVFILT_READ", filter); + //if (filter == EVFILT_WRITE) LogMsg("myKQSocketCallBack: tcpKQSocketCallback %d is EVFILT_WRITE", filter); + // EV_ONESHOT doesn't seem to work, so we add the filter with EV_ADD, and explicitly delete it here with EV_DELETE + if (filter == EVFILT_WRITE) KQueueSet(sock->fd, EV_DELETE, EVFILT_WRITE, sock->kqEntry); - if (sock->flags & kTCPSocketFlags_UseTLS) - { + if (sock->flags & kTCPSocketFlags_UseTLS) + { #ifndef NO_SECURITYFRAMEWORK - if (!sock->setup) { sock->setup = mDNStrue; tlsSetupSock(sock, mDNSfalse); } - - if (sock->handshake == handshake_required) { if (spawnSSLHandshake(sock) == 0) return; } - else if (sock->handshake == handshake_in_progress || sock->handshake == handshake_to_be_closed) return; - else if (sock->handshake != handshake_completed) - { - if (!sock->err) sock->err = mStatus_UnknownErr; - LogMsg("tcpKQSocketCallback called with unexpected SSLHandshake status: %d", sock->handshake); - } + if (!sock->setup) { sock->setup = mDNStrue; tlsSetupSock(sock, mDNSfalse); } + + if (sock->handshake == handshake_required) { if (spawnSSLHandshake(sock) == 0) return;} + else if (sock->handshake == handshake_in_progress || sock->handshake == handshake_to_be_closed) return; + else if (sock->handshake != handshake_completed) + { + if (!sock->err) sock->err = mStatus_UnknownErr; + LogMsg("tcpKQSocketCallback called with unexpected SSLHandshake status: %d", sock->handshake); + } #else - sock->err = mStatus_UnsupportedErr; + sock->err = mStatus_UnsupportedErr; #endif /* NO_SECURITYFRAMEWORK */ - } - - doTcpSocketCallback(sock); - } + } + + doTcpSocketCallback(sock); +} #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM mDNSexport int KQueueSet(int fd, u_short flags, short filter, KQueueEntry *const entryRef) - { - dispatch_queue_t queue = dispatch_get_main_queue(); - dispatch_source_t source; - if (flags == EV_DELETE) - { - if (filter == EVFILT_READ) - { - dispatch_source_cancel(entryRef->readSource); - dispatch_release(entryRef->readSource); - entryRef->readSource = mDNSNULL; - debugf("KQueueSet: source cancel for read %p, %p", entryRef->readSource, entryRef->writeSource); - } - else if (filter == EVFILT_WRITE) - { - dispatch_source_cancel(entryRef->writeSource); - dispatch_release(entryRef->writeSource); - entryRef->writeSource = mDNSNULL; - debugf("KQueueSet: source cancel for write %p, %p", entryRef->readSource, entryRef->writeSource); - } - else - LogMsg("KQueueSet: ERROR: Wrong filter value %d for EV_DELETE", filter); - return 0; - } - if (flags != EV_ADD) LogMsg("KQueueSet: Invalid flags %d", flags); - - if (filter == EVFILT_READ) - { - source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, queue); - } - else if (filter == EVFILT_WRITE) - { - source = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, fd, 0, queue); - } - else - { - LogMsg("KQueueSet: ERROR: Wrong filter value %d for EV_ADD", filter); - return -1; - } - if (!source) return -1; - dispatch_source_set_event_handler(source, ^{ - - mDNSs32 stime = mDNSPlatformRawTime(); - entryRef->KQcallback(fd, filter, entryRef->KQcontext); - mDNSs32 etime = mDNSPlatformRawTime(); - if (etime - stime >= WatchDogReportingThreshold) - LogInfo("KQEntryCallback Block: WARNING: took %dms to complete", etime - stime); - - // Trigger the event delivery to the application. Even though we trigger the - // event completion after handling every event source, these all will hopefully - // get merged - TriggerEventCompletion(); - - }); +{ + dispatch_queue_t queue = dispatch_get_main_queue(); + dispatch_source_t source; + if (flags == EV_DELETE) + { + if (filter == EVFILT_READ) + { + dispatch_source_cancel(entryRef->readSource); + dispatch_release(entryRef->readSource); + entryRef->readSource = mDNSNULL; + debugf("KQueueSet: source cancel for read %p, %p", entryRef->readSource, entryRef->writeSource); + } + else if (filter == EVFILT_WRITE) + { + dispatch_source_cancel(entryRef->writeSource); + dispatch_release(entryRef->writeSource); + entryRef->writeSource = mDNSNULL; + debugf("KQueueSet: source cancel for write %p, %p", entryRef->readSource, entryRef->writeSource); + } + else + LogMsg("KQueueSet: ERROR: Wrong filter value %d for EV_DELETE", filter); + return 0; + } + if (flags != EV_ADD) LogMsg("KQueueSet: Invalid flags %d", flags); + + if (filter == EVFILT_READ) + { + source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, queue); + } + else if (filter == EVFILT_WRITE) + { + source = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, fd, 0, queue); + } + else + { + LogMsg("KQueueSet: ERROR: Wrong filter value %d for EV_ADD", filter); + return -1; + } + if (!source) return -1; + dispatch_source_set_event_handler(source, ^{ + + mDNSs32 stime = mDNSPlatformRawTime(); + entryRef->KQcallback(fd, filter, entryRef->KQcontext); + mDNSs32 etime = mDNSPlatformRawTime(); + if (etime - stime >= WatchDogReportingThreshold) + LogInfo("KQEntryCallback Block: WARNING: took %dms to complete", etime - stime); + + // Trigger the event delivery to the application. Even though we trigger the + // event completion after handling every event source, these all will hopefully + // get merged + TriggerEventCompletion(); + + }); dispatch_source_set_cancel_handler(source, ^{ - if (entryRef->fdClosed) - { - //LogMsg("CancelHandler: closing fd %d", fd); - close(fd); - } - }); - dispatch_resume(source); - if (filter == EVFILT_READ) - entryRef->readSource = source; - else - entryRef->writeSource = source; - - return 0; - } + if (entryRef->fdClosed) + { + //LogMsg("CancelHandler: closing fd %d", fd); + close(fd); + } + }); + dispatch_resume(source); + if (filter == EVFILT_READ) + entryRef->readSource = source; + else + entryRef->writeSource = source; + + return 0; +} mDNSexport void KQueueLock(mDNS *const m) - { - (void)m; //unused - } +{ + (void)m; //unused +} mDNSexport void KQueueUnlock(mDNS *const m, const char const *task) - { - (void)m; //unused - (void)task; //unused - } +{ + (void)m; //unused + (void)task; //unused +} #else mDNSexport int KQueueSet(int fd, u_short flags, short filter, const KQueueEntry *const entryRef) - { - struct kevent new_event; - EV_SET(&new_event, fd, filter, flags, 0, 0, (void*)entryRef); - return (kevent(KQueueFD, &new_event, 1, NULL, 0, NULL) < 0) ? errno : 0; - } +{ + struct kevent new_event; + EV_SET(&new_event, fd, filter, flags, 0, 0, (void*)entryRef); + return (kevent(KQueueFD, &new_event, 1, NULL, 0, NULL) < 0) ? errno : 0; +} mDNSexport void KQueueLock(mDNS *const m) - { - pthread_mutex_lock(&m->p->BigMutex); - m->p->BigMutexStartTime = mDNSPlatformRawTime(); - } +{ + pthread_mutex_lock(&m->p->BigMutex); + m->p->BigMutexStartTime = mDNSPlatformRawTime(); +} mDNSexport void KQueueUnlock(mDNS *const m, const char const *task) - { - mDNSs32 end = mDNSPlatformRawTime(); - (void)task; - if (end - m->p->BigMutexStartTime >= WatchDogReportingThreshold) - LogInfo("WARNING: %s took %dms to complete", task, end - m->p->BigMutexStartTime); - - pthread_mutex_unlock(&m->p->BigMutex); - - char wake = 1; - if (send(m->p->WakeKQueueLoopFD, &wake, sizeof(wake), 0) == -1) - LogMsg("ERROR: KQueueWake: send failed with error code: %d (%s)", errno, strerror(errno)); - } +{ + mDNSs32 end = mDNSPlatformRawTime(); + (void)task; + if (end - m->p->BigMutexStartTime >= WatchDogReportingThreshold) + LogInfo("WARNING: %s took %dms to complete", task, end - m->p->BigMutexStartTime); + + pthread_mutex_unlock(&m->p->BigMutex); + + char wake = 1; + if (send(m->p->WakeKQueueLoopFD, &wake, sizeof(wake), 0) == -1) + LogMsg("ERROR: KQueueWake: send failed with error code: %d (%s)", errno, strerror(errno)); +} #endif mDNSexport void mDNSPlatformCloseFD(KQueueEntry *kq, int fd) - { +{ #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM - (void)fd; //unused - if (kq->readSource) - { - dispatch_source_cancel(kq->readSource); - kq->readSource = mDNSNULL; - } - if (kq->writeSource) - { - dispatch_source_cancel(kq->writeSource); - kq->writeSource = mDNSNULL; - } - // Close happens in the cancellation handler - debugf("mDNSPlatformCloseFD: resetting sources for %d", fd); - kq->fdClosed = mDNStrue; + (void) fd; //unused + if (kq->readSource) + { + dispatch_source_cancel(kq->readSource); + kq->readSource = mDNSNULL; + } + if (kq->writeSource) + { + dispatch_source_cancel(kq->writeSource); + kq->writeSource = mDNSNULL; + } + // Close happens in the cancellation handler + debugf("mDNSPlatformCloseFD: resetting sources for %d", fd); + kq->fdClosed = mDNStrue; #else - (void)kq; //unused - close(fd); + (void)kq; //unused + close(fd); #endif - } +} -mDNSlocal mStatus SetupTCPSocket(TCPSocket *sock, u_short sa_family, mDNSIPPort *port) - { - KQSocketSet *cp = &sock->ss; +mDNSlocal mStatus SetupTCPSocket(TCPSocket *sock, u_short sa_family, mDNSIPPort *port, mDNSBool useBackgroundTrafficClass) +{ + KQSocketSet *cp = &sock->ss; #ifndef NO_IPV6 int *s = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6; KQueueEntry *k = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6; @@ -1757,111 +1964,116 @@ mDNSlocal mStatus SetupTCPSocket(TCPSocket *sock, u_short sa_family, mDNSIPPort int *s = &cp->sktv4; KQueueEntry *k = &cp->kqsv4; #endif - const int on = 1; // "on" for setsockopt - mStatus err; + const int on = 1; // "on" for setsockopt + mStatus err; int skt = socket(sa_family, SOCK_STREAM, IPPROTO_TCP); - if (skt < 3) { if (errno != EAFNOSUPPORT) LogMsg("SetupTCPSocket: socket error %d errno %d (%s)", skt, errno, strerror(errno)); return(skt); } - if (sa_family == AF_INET) - { - // Bind it - struct sockaddr_in addr; - mDNSPlatformMemZero(&addr, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = port->NotAnInteger; - err = bind(skt, (struct sockaddr*) &addr, sizeof(addr)); - if (err < 0) { LogMsg("ERROR: bind %s", strerror(errno)); return err; } - - // Receive interface identifiers - err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)); - if (err < 0) { LogMsg("setsockopt IP_RECVIF - %s", strerror(errno)); return err; } - - mDNSPlatformMemZero(&addr, sizeof(addr)); - socklen_t len = sizeof(addr); - err = getsockname(skt, (struct sockaddr*) &addr, &len); - if (err < 0) { LogMsg("getsockname - %s", strerror(errno)); return err; } - - port->NotAnInteger = addr.sin_port; - } - else - { - // Bind it - struct sockaddr_in6 addr6; - mDNSPlatformMemZero(&addr6, sizeof(addr6)); - addr6.sin6_family = AF_INET6; - addr6.sin6_port = port->NotAnInteger; - err = bind(skt, (struct sockaddr*) &addr6, sizeof(addr6)); - if (err < 0) { LogMsg("ERROR: bind6 %s", strerror(errno)); return err; } - - // We want to receive destination addresses and receive interface identifiers + if (skt < 3) { if (errno != EAFNOSUPPORT) LogMsg("SetupTCPSocket: socket error %d errno %d (%s)", skt, errno, strerror(errno));return(skt); } + + // for TCP sockets, the traffic class is set once and not changed + setTrafficClass(skt, useBackgroundTrafficClass); + + if (sa_family == AF_INET) + { + // Bind it + struct sockaddr_in addr; + mDNSPlatformMemZero(&addr, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = port->NotAnInteger; + err = bind(skt, (struct sockaddr*) &addr, sizeof(addr)); + if (err < 0) { LogMsg("ERROR: bind %s", strerror(errno)); return err; } + + // Receive interface identifiers + err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)); + if (err < 0) { LogMsg("setsockopt IP_RECVIF - %s", strerror(errno)); return err; } + + mDNSPlatformMemZero(&addr, sizeof(addr)); + socklen_t len = sizeof(addr); + err = getsockname(skt, (struct sockaddr*) &addr, &len); + if (err < 0) { LogMsg("getsockname - %s", strerror(errno)); return err; } + + port->NotAnInteger = addr.sin_port; + } + else + { + // Bind it + struct sockaddr_in6 addr6; + mDNSPlatformMemZero(&addr6, sizeof(addr6)); + addr6.sin6_family = AF_INET6; + addr6.sin6_port = port->NotAnInteger; + err = bind(skt, (struct sockaddr*) &addr6, sizeof(addr6)); + if (err < 0) { LogMsg("ERROR: bind6 %s", strerror(errno)); return err; } + + // We want to receive destination addresses and receive interface identifiers err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); - if (err < 0) { LogMsg("ERROR: setsockopt IPV6_RECVPKTINFO %s", strerror(errno)); return err; } - - mDNSPlatformMemZero(&addr6, sizeof(addr6)); - socklen_t len = sizeof(addr6); - err = getsockname(skt, (struct sockaddr *) &addr6, &len); - if (err < 0) { LogMsg("getsockname6 - %s", strerror(errno)); return err; } - - port->NotAnInteger = addr6.sin6_port; - - } - *s = skt; - k->KQcallback = tcpKQSocketCallback; - k->KQcontext = sock; - k->KQtask = "mDNSPlatformTCPSocket"; + if (err < 0) { LogMsg("ERROR: setsockopt IPV6_RECVPKTINFO %s", strerror(errno)); return err; } + + mDNSPlatformMemZero(&addr6, sizeof(addr6)); + socklen_t len = sizeof(addr6); + err = getsockname(skt, (struct sockaddr *) &addr6, &len); + if (err < 0) { LogMsg("getsockname6 - %s", strerror(errno)); return err; } + + port->NotAnInteger = addr6.sin6_port; + + } + *s = skt; + k->KQcallback = tcpKQSocketCallback; + k->KQcontext = sock; + k->KQtask = "mDNSPlatformTCPSocket"; #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM - k->readSource = mDNSNULL; - k->writeSource = mDNSNULL; - k->fdClosed = mDNSfalse; + k->readSource = mDNSNULL; + k->writeSource = mDNSNULL; + k->fdClosed = mDNSfalse; #endif - return mStatus_NoError; - } + return mStatus_NoError; +} -mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS *const m, TCPSocketFlags flags, mDNSIPPort *port) - { - mStatus err; - (void) m; +mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS *const m, TCPSocketFlags flags, mDNSIPPort *port, mDNSBool useBackgroundTrafficClass) +{ + mStatus err; + (void) m; - TCPSocket *sock = mallocL("TCPSocket/mDNSPlatformTCPSocket", sizeof(TCPSocket)); - if (!sock) { LogMsg("mDNSPlatformTCPSocket: memory allocation failure"); return(mDNSNULL); } + TCPSocket *sock = mallocL("TCPSocket/mDNSPlatformTCPSocket", sizeof(TCPSocket)); + if (!sock) { LogMsg("mDNSPlatformTCPSocket: memory allocation failure"); return(mDNSNULL); } - mDNSPlatformMemZero(sock, sizeof(TCPSocket)); + mDNSPlatformMemZero(sock, sizeof(TCPSocket)); - sock->ss.m = m; - sock->ss.sktv4 = -1; + sock->ss.m = m; + sock->ss.sktv4 = -1; #ifndef NO_IPV6 - sock->ss.sktv6 = -1; + sock->ss.sktv6 = -1; #endif - err = SetupTCPSocket(sock, AF_INET, port); + err = SetupTCPSocket(sock, AF_INET, port, useBackgroundTrafficClass); #ifndef NO_IPV6 - if (!err) - { - err = SetupTCPSocket(sock, AF_INET6, port); - if (err) { mDNSPlatformCloseFD(&sock->ss.kqsv4, sock->ss.sktv4); sock->ss.sktv4 = -1; } - } + if (!err) + { + err = SetupTCPSocket(sock, AF_INET6, port, useBackgroundTrafficClass); + if (err) { mDNSPlatformCloseFD(&sock->ss.kqsv4, sock->ss.sktv4); sock->ss.sktv4 = -1; } + } #endif - if (err) - { - LogMsg("mDNSPlatformTCPSocket: socket error %d errno %d (%s)", sock->fd, errno, strerror(errno)); - freeL("TCPSocket/mDNSPlatformTCPSocket", sock); - return(mDNSNULL); - } - - sock->callback = mDNSNULL; - sock->flags = flags; - sock->context = mDNSNULL; - sock->setup = mDNSfalse; - sock->connected = mDNSfalse; - sock->handshake = handshake_required; - sock->m = m; - sock->err = mStatus_NoError; - - return sock; - } + if (err) + { + LogMsg("mDNSPlatformTCPSocket: socket error %d errno %d (%s)", sock->fd, errno, strerror(errno)); + freeL("TCPSocket/mDNSPlatformTCPSocket", sock); + return(mDNSNULL); + } + // sock->fd is used as the default fd if the caller does not call mDNSPlatformTCPConnect + sock->fd = sock->ss.sktv4; + sock->callback = mDNSNULL; + sock->flags = flags; + sock->context = mDNSNULL; + sock->setup = mDNSfalse; + sock->connected = mDNSfalse; + sock->handshake = handshake_required; + sock->m = m; + sock->err = mStatus_NoError; + + return sock; +} mDNSexport mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, domainname *hostname, mDNSInterfaceID InterfaceID, TCPConnectionCallback callback, void *context) - { - KQSocketSet *cp = &sock->ss; +{ + KQSocketSet *cp = &sock->ss; #ifndef NO_IPV6 int *s = (dst->type == mDNSAddrType_IPv4) ? &cp->sktv4 : &cp->sktv6; KQueueEntry *k = (dst->type == mDNSAddrType_IPv4) ? &cp->kqsv4 : &cp->kqsv6; @@ -1869,472 +2081,499 @@ mDNSexport mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, int *s = &cp->sktv4; KQueueEntry *k = &cp->kqsv4; #endif - mStatus err = mStatus_NoError; - struct sockaddr_storage ss; - - sock->callback = callback; - sock->context = context; - sock->setup = mDNSfalse; - sock->connected = mDNSfalse; - sock->handshake = handshake_required; - sock->err = mStatus_NoError; - - if (hostname) { debugf("mDNSPlatformTCPConnect: hostname %##s", hostname->c); AssignDomainName(&sock->hostname, hostname); } - - if (dst->type == mDNSAddrType_IPv4) - { - struct sockaddr_in *saddr = (struct sockaddr_in *)&ss; - mDNSPlatformMemZero(saddr, sizeof(*saddr)); - saddr->sin_family = AF_INET; - saddr->sin_port = dstport.NotAnInteger; - saddr->sin_len = sizeof(*saddr); - saddr->sin_addr.s_addr = dst->ip.v4.NotAnInteger; - } - else - { - struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)&ss; - mDNSPlatformMemZero(saddr6, sizeof(*saddr6)); - saddr6->sin6_family = AF_INET6; - saddr6->sin6_port = dstport.NotAnInteger; - saddr6->sin6_len = sizeof(*saddr6); - saddr6->sin6_addr = *(struct in6_addr *)&dst->ip.v6; - } - - // Watch for connect complete (write is ready) - // EV_ONESHOT doesn't seem to work, so we add the filter with EV_ADD, and explicitly delete it in tcpKQSocketCallback using EV_DELETE - if (KQueueSet(*s, EV_ADD /* | EV_ONESHOT */, EVFILT_WRITE, k)) - { - LogMsg("ERROR: mDNSPlatformTCPConnect - KQueueSet failed"); - return errno; - } - - // Watch for incoming data - if (KQueueSet(*s, EV_ADD, EVFILT_READ, k)) - { - LogMsg("ERROR: mDNSPlatformTCPConnect - KQueueSet failed"); - return errno; - } - - if (fcntl(*s, F_SETFL, fcntl(*s, F_GETFL, 0) | O_NONBLOCK) < 0) // set non-blocking - { - LogMsg("ERROR: setsockopt O_NONBLOCK - %s", strerror(errno)); - return mStatus_UnknownErr; - } - - // We bind to the interface and all subsequent packets including the SYN will be sent out - // on this interface - // - // Note: If we are in Active Directory domain, we may try TCP (if the response can't fit in - // UDP). mDNSInterface_Unicast indicates this case and not a valid interface. - if (InterfaceID && InterfaceID != mDNSInterface_Unicast) - { - NetworkInterfaceInfoOSX *info = IfindexToInterfaceInfoOSX(&mDNSStorage, InterfaceID); - if (dst->type == mDNSAddrType_IPv4) - { - #ifdef IP_BOUND_IF - if (info) setsockopt(*s, IPPROTO_IP, IP_BOUND_IF, &info->scope_id, sizeof(info->scope_id)); - else { LogMsg("mDNSPlatformTCPConnect: Invalid interface index %p", InterfaceID); return mStatus_BadParamErr; } - #else - (void)InterfaceID; // Unused - (void)info; // Unused - #endif - } - else - { - #ifdef IPV6_BOUND_IF - if (info) setsockopt(*s, IPPROTO_IPV6, IPV6_BOUND_IF, &info->scope_id, sizeof(info->scope_id)); - else { LogMsg("mDNSPlatformTCPConnect: Invalid interface index %p", InterfaceID); return mStatus_BadParamErr; } - #else - (void)InterfaceID; // Unused - (void)info; // Unused - #endif - } - } - - // mDNSPlatformReadTCP/WriteTCP (unlike the UDP counterpart) does not provide the destination address - // from which we can infer the destination address family. Hence we need to remember that here. - // Instead of remembering the address family, we remember the right fd. - sock->fd = *s; - sock->kqEntry = k; - // initiate connection wth peer - if (connect(*s, (struct sockaddr *)&ss, ss.ss_len) < 0) - { - if (errno == EINPROGRESS) return mStatus_ConnPending; - if (errno == EHOSTUNREACH || errno == EADDRNOTAVAIL || errno == ENETDOWN) - LogInfo("ERROR: mDNSPlatformTCPConnect - connect failed: socket %d: Error %d (%s)", sock->fd, errno, strerror(errno)); - else - LogMsg("ERROR: mDNSPlatformTCPConnect - connect failed: socket %d: Error %d (%s) length %d", sock->fd, errno, strerror(errno), ss.ss_len); - return mStatus_ConnFailed; - } - - LogMsg("NOTE: mDNSPlatformTCPConnect completed synchronously"); - // kQueue should notify us, but this LogMsg is to help track down if it doesn't - return err; - } + mStatus err = mStatus_NoError; + struct sockaddr_storage ss; + + sock->callback = callback; + sock->context = context; + sock->setup = mDNSfalse; + sock->connected = mDNSfalse; + sock->handshake = handshake_required; + sock->err = mStatus_NoError; + + if (hostname) { debugf("mDNSPlatformTCPConnect: hostname %##s", hostname->c); AssignDomainName(&sock->hostname, hostname); } + + if (dst->type == mDNSAddrType_IPv4) + { + struct sockaddr_in *saddr = (struct sockaddr_in *)&ss; + mDNSPlatformMemZero(saddr, sizeof(*saddr)); + saddr->sin_family = AF_INET; + saddr->sin_port = dstport.NotAnInteger; + saddr->sin_len = sizeof(*saddr); + saddr->sin_addr.s_addr = dst->ip.v4.NotAnInteger; + } + else + { + struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)&ss; + mDNSPlatformMemZero(saddr6, sizeof(*saddr6)); + saddr6->sin6_family = AF_INET6; + saddr6->sin6_port = dstport.NotAnInteger; + saddr6->sin6_len = sizeof(*saddr6); + saddr6->sin6_addr = *(struct in6_addr *)&dst->ip.v6; + } + + // Watch for connect complete (write is ready) + // EV_ONESHOT doesn't seem to work, so we add the filter with EV_ADD, and explicitly delete it in tcpKQSocketCallback using EV_DELETE + if (KQueueSet(*s, EV_ADD /* | EV_ONESHOT */, EVFILT_WRITE, k)) + { + LogMsg("ERROR: mDNSPlatformTCPConnect - KQueueSet failed"); + return errno; + } + + // Watch for incoming data + if (KQueueSet(*s, EV_ADD, EVFILT_READ, k)) + { + LogMsg("ERROR: mDNSPlatformTCPConnect - KQueueSet failed"); + return errno; + } + + if (fcntl(*s, F_SETFL, fcntl(*s, F_GETFL, 0) | O_NONBLOCK) < 0) // set non-blocking + { + LogMsg("ERROR: setsockopt O_NONBLOCK - %s", strerror(errno)); + return mStatus_UnknownErr; + } + + // We bind to the interface and all subsequent packets including the SYN will be sent out + // on this interface + // + // Note: If we are in Active Directory domain, we may try TCP (if the response can't fit in + // UDP). mDNSInterface_Unicast indicates this case and not a valid interface. + if (InterfaceID && InterfaceID != mDNSInterface_Unicast) + { + NetworkInterfaceInfoOSX *info = IfindexToInterfaceInfoOSX(&mDNSStorage, InterfaceID); + if (dst->type == mDNSAddrType_IPv4) + { + #ifdef IP_BOUND_IF + if (info) setsockopt(*s, IPPROTO_IP, IP_BOUND_IF, &info->scope_id, sizeof(info->scope_id)); + else { LogMsg("mDNSPlatformTCPConnect: Invalid interface index %p", InterfaceID); return mStatus_BadParamErr; } + #else + (void)InterfaceID; // Unused + (void)info; // Unused + #endif + } + else + { + #ifdef IPV6_BOUND_IF + if (info) setsockopt(*s, IPPROTO_IPV6, IPV6_BOUND_IF, &info->scope_id, sizeof(info->scope_id)); + else { LogMsg("mDNSPlatformTCPConnect: Invalid interface index %p", InterfaceID); return mStatus_BadParamErr; } + #else + (void)InterfaceID; // Unused + (void)info; // Unused + #endif + } + } + + // mDNSPlatformReadTCP/WriteTCP (unlike the UDP counterpart) does not provide the destination address + // from which we can infer the destination address family. Hence we need to remember that here. + // Instead of remembering the address family, we remember the right fd. + sock->fd = *s; + sock->kqEntry = k; + // initiate connection wth peer + if (connect(*s, (struct sockaddr *)&ss, ss.ss_len) < 0) + { + if (errno == EINPROGRESS) return mStatus_ConnPending; + if (errno == EHOSTUNREACH || errno == EADDRNOTAVAIL || errno == ENETDOWN) + LogInfo("ERROR: mDNSPlatformTCPConnect - connect failed: socket %d: Error %d (%s)", sock->fd, errno, strerror(errno)); + else + LogMsg("ERROR: mDNSPlatformTCPConnect - connect failed: socket %d: Error %d (%s) length %d", sock->fd, errno, strerror(errno), ss.ss_len); + return mStatus_ConnFailed; + } + + LogMsg("NOTE: mDNSPlatformTCPConnect completed synchronously"); + // kQueue should notify us, but this LogMsg is to help track down if it doesn't + return err; +} // Why doesn't mDNSPlatformTCPAccept actually call accept() ? mDNSexport TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int fd) - { - mStatus err = mStatus_NoError; +{ + mStatus err = mStatus_NoError; - TCPSocket *sock = mallocL("TCPSocket/mDNSPlatformTCPAccept", sizeof(TCPSocket)); - if (!sock) return(mDNSNULL); + TCPSocket *sock = mallocL("TCPSocket/mDNSPlatformTCPAccept", sizeof(TCPSocket)); + if (!sock) return(mDNSNULL); - mDNSPlatformMemZero(sock, sizeof(*sock)); - sock->fd = fd; - sock->flags = flags; + mDNSPlatformMemZero(sock, sizeof(*sock)); + sock->fd = fd; + sock->flags = flags; - if (flags & kTCPSocketFlags_UseTLS) - { + if (flags & kTCPSocketFlags_UseTLS) + { #ifndef NO_SECURITYFRAMEWORK - if (!ServerCerts) { LogMsg("ERROR: mDNSPlatformTCPAccept: unable to find TLS certificates"); err = mStatus_UnknownErr; goto exit; } + if (!ServerCerts) { LogMsg("ERROR: mDNSPlatformTCPAccept: unable to find TLS certificates"); err = mStatus_UnknownErr; goto exit; } - err = tlsSetupSock(sock, mDNStrue); - if (err) { LogMsg("ERROR: mDNSPlatformTCPAccept: tlsSetupSock failed with error code: %d", err); goto exit; } + err = tlsSetupSock(sock, mDNStrue); + if (err) { LogMsg("ERROR: mDNSPlatformTCPAccept: tlsSetupSock failed with error code: %d", err); goto exit; } - err = SSLSetCertificate(sock->tlsContext, ServerCerts); - if (err) { LogMsg("ERROR: mDNSPlatformTCPAccept: SSLSetCertificate failed with error code: %d", err); goto exit; } + err = SSLSetCertificate(sock->tlsContext, ServerCerts); + if (err) { LogMsg("ERROR: mDNSPlatformTCPAccept: SSLSetCertificate failed with error code: %d", err); goto exit; } #else - err = mStatus_UnsupportedErr; + err = mStatus_UnsupportedErr; #endif /* NO_SECURITYFRAMEWORK */ - } + } #ifndef NO_SECURITYFRAMEWORK exit: #endif - if (err) { freeL("TCPSocket/mDNSPlatformTCPAccept", sock); return(mDNSNULL); } - return(sock); - } + if (err) { freeL("TCPSocket/mDNSPlatformTCPAccept", sock); return(mDNSNULL); } + return(sock); +} mDNSlocal void CloseSocketSet(KQSocketSet *ss) - { - if (ss->sktv4 != -1) - { - mDNSPlatformCloseFD(&ss->kqsv4, ss->sktv4); - ss->sktv4 = -1; - } +{ + if (ss->sktv4 != -1) + { + mDNSPlatformCloseFD(&ss->kqsv4, ss->sktv4); + ss->sktv4 = -1; + } #ifndef NO_IPV6 - if (ss->sktv6 != -1) - { - mDNSPlatformCloseFD(&ss->kqsv6, ss->sktv6); - ss->sktv6 = -1; - } + if (ss->sktv6 != -1) + { + mDNSPlatformCloseFD(&ss->kqsv6, ss->sktv6); + ss->sktv6 = -1; + } #endif - if (ss->closeFlag) *ss->closeFlag = 1; - } + if (ss->closeFlag) *ss->closeFlag = 1; +} mDNSexport void mDNSPlatformTCPCloseConnection(TCPSocket *sock) - { - if (sock) - { +{ + if (sock) + { #ifndef NO_SECURITYFRAMEWORK - if (sock->tlsContext) - { - if (sock->handshake == handshake_in_progress) // SSLHandshake thread using this sock (esp. tlsContext) - { - LogInfo("mDNSPlatformTCPCloseConnection: called while handshake in progress"); - // When we come back from SSLHandshake, we will notice that a close was here and - // call this function again which will do the cleanup then. - sock->handshake = handshake_to_be_closed; - return; - } - - SSLClose(sock->tlsContext); - SSLDisposeContext(sock->tlsContext); - sock->tlsContext = NULL; - } + if (sock->tlsContext) + { + if (sock->handshake == handshake_in_progress) // SSLHandshake thread using this sock (esp. tlsContext) + { + LogInfo("mDNSPlatformTCPCloseConnection: called while handshake in progress"); + // When we come back from SSLHandshake, we will notice that a close was here and + // call this function again which will do the cleanup then. + sock->handshake = handshake_to_be_closed; + return; + } + + SSLClose(sock->tlsContext); + SSLDisposeContext(sock->tlsContext); + sock->tlsContext = NULL; + } #endif /* NO_SECURITYFRAMEWORK */ - if (sock->ss.sktv4 != -1) shutdown(sock->ss.sktv4, 2); + if (sock->ss.sktv4 != -1) shutdown(sock->ss.sktv4, 2); #ifndef NO_IPV6 - if (sock->ss.sktv6 != -1) shutdown(sock->ss.sktv6, 2); + if (sock->ss.sktv6 != -1) shutdown(sock->ss.sktv6, 2); #endif - CloseSocketSet(&sock->ss); - sock->fd = -1; + CloseSocketSet(&sock->ss); + sock->fd = -1; - freeL("TCPSocket/mDNSPlatformTCPCloseConnection", sock); - } - } + freeL("TCPSocket/mDNSPlatformTCPCloseConnection", sock); + } +} mDNSexport long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool *closed) - { - size_t nread = 0; - *closed = mDNSfalse; +{ + size_t nread = 0; + *closed = mDNSfalse; - if (sock->flags & kTCPSocketFlags_UseTLS) - { + if (sock->flags & kTCPSocketFlags_UseTLS) + { #ifndef NO_SECURITYFRAMEWORK - if (sock->handshake == handshake_required) { LogMsg("mDNSPlatformReadTCP called while handshake required"); return 0; } - else if (sock->handshake == handshake_in_progress) return 0; - else if (sock->handshake != handshake_completed) LogMsg("mDNSPlatformReadTCP called with unexpected SSLHandshake status: %d", sock->handshake); - - //LogMsg("Starting SSLRead %d %X", sock->fd, fcntl(sock->fd, F_GETFL, 0)); - mStatus err = SSLRead(sock->tlsContext, buf, buflen, &nread); - //LogMsg("SSLRead returned %d (%d) nread %d buflen %d", err, errSSLWouldBlock, nread, buflen); - if (err == errSSLClosedGraceful) { nread = 0; *closed = mDNStrue; } - else if (err && err != errSSLWouldBlock) - { LogMsg("ERROR: mDNSPlatformReadTCP - SSLRead: %d", err); nread = -1; *closed = mDNStrue; } + if (sock->handshake == handshake_required) { LogMsg("mDNSPlatformReadTCP called while handshake required"); return 0; } + else if (sock->handshake == handshake_in_progress) return 0; + else if (sock->handshake != handshake_completed) LogMsg("mDNSPlatformReadTCP called with unexpected SSLHandshake status: %d", sock->handshake); + + //LogMsg("Starting SSLRead %d %X", sock->fd, fcntl(sock->fd, F_GETFL, 0)); + mStatus err = SSLRead(sock->tlsContext, buf, buflen, &nread); + //LogMsg("SSLRead returned %d (%d) nread %d buflen %d", err, errSSLWouldBlock, nread, buflen); + if (err == errSSLClosedGraceful) { nread = 0; *closed = mDNStrue; } + else if (err && err != errSSLWouldBlock) + { LogMsg("ERROR: mDNSPlatformReadTCP - SSLRead: %d", err); nread = -1; *closed = mDNStrue; } #else - nread = -1; - *closed = mDNStrue; + nread = -1; + *closed = mDNStrue; #endif /* NO_SECURITYFRAMEWORK */ - } - else - { - static int CLOSEDcount = 0; - static int EAGAINcount = 0; - nread = recv(sock->fd, buf, buflen, 0); - - if (nread > 0) { CLOSEDcount = 0; EAGAINcount = 0; } // On success, clear our error counters - else if (nread == 0) - { - *closed = mDNStrue; - if ((++CLOSEDcount % 1000) == 0) { LogMsg("ERROR: mDNSPlatformReadTCP - recv %d got CLOSED %d times", sock->fd, CLOSEDcount); sleep(1); } - } - // else nread is negative -- see what kind of error we got - else if (errno == ECONNRESET) { nread = 0; *closed = mDNStrue; } - else if (errno != EAGAIN) { LogMsg("ERROR: mDNSPlatformReadTCP - recv: %d (%s)", errno, strerror(errno)); nread = -1; } - else // errno is EAGAIN (EWOULDBLOCK) -- no data available - { - nread = 0; - if ((++EAGAINcount % 1000) == 0) { LogMsg("ERROR: mDNSPlatformReadTCP - recv %d got EAGAIN %d times", sock->fd, EAGAINcount); sleep(1); } - } - } - - return nread; - } + } + else + { + static int CLOSEDcount = 0; + static int EAGAINcount = 0; + nread = recv(sock->fd, buf, buflen, 0); + + if (nread > 0) + { + CLOSEDcount = 0; + EAGAINcount = 0; + } // On success, clear our error counters + else if (nread == 0) + { + *closed = mDNStrue; + if ((++CLOSEDcount % 1000) == 0) + { + LogMsg("ERROR: mDNSPlatformReadTCP - recv %d got CLOSED %d times", sock->fd, CLOSEDcount); + assert(CLOSEDcount < 1000); + // Recovery Mechanism to bail mDNSResponder out of trouble: Instead of logging the same error msg multiple times, + // crash mDNSResponder using assert() and restart fresh. See advantages below: + // 1.Better User Experience + // 2.CrashLogs frequency can be monitored + // 3.StackTrace can be used for more info + } + } + // else nread is negative -- see what kind of error we got + else if (errno == ECONNRESET) { nread = 0; *closed = mDNStrue; } + else if (errno != EAGAIN) { LogMsg("ERROR: mDNSPlatformReadTCP - recv: %d (%s)", errno, strerror(errno)); nread = -1; } + else // errno is EAGAIN (EWOULDBLOCK) -- no data available + { + nread = 0; + if ((++EAGAINcount % 1000) == 0) { LogMsg("ERROR: mDNSPlatformReadTCP - recv %d got EAGAIN %d times", sock->fd, EAGAINcount); sleep(1); } + } + } + + return nread; +} mDNSexport long mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len) - { - int nsent; +{ + int nsent; - if (sock->flags & kTCPSocketFlags_UseTLS) - { + if (sock->flags & kTCPSocketFlags_UseTLS) + { #ifndef NO_SECURITYFRAMEWORK - size_t processed; - if (sock->handshake == handshake_required) { LogMsg("mDNSPlatformWriteTCP called while handshake required"); return 0; } - if (sock->handshake == handshake_in_progress) return 0; - else if (sock->handshake != handshake_completed) LogMsg("mDNSPlatformWriteTCP called with unexpected SSLHandshake status: %d", sock->handshake); - - mStatus err = SSLWrite(sock->tlsContext, msg, len, &processed); - - if (!err) nsent = (int) processed; - else if (err == errSSLWouldBlock) nsent = 0; - else { LogMsg("ERROR: mDNSPlatformWriteTCP - SSLWrite returned %d", err); nsent = -1; } + size_t processed; + if (sock->handshake == handshake_required) { LogMsg("mDNSPlatformWriteTCP called while handshake required"); return 0; } + if (sock->handshake == handshake_in_progress) return 0; + else if (sock->handshake != handshake_completed) LogMsg("mDNSPlatformWriteTCP called with unexpected SSLHandshake status: %d", sock->handshake); + + mStatus err = SSLWrite(sock->tlsContext, msg, len, &processed); + + if (!err) nsent = (int) processed; + else if (err == errSSLWouldBlock) nsent = 0; + else { LogMsg("ERROR: mDNSPlatformWriteTCP - SSLWrite returned %d", err); nsent = -1; } #else - nsent = -1; + nsent = -1; #endif /* NO_SECURITYFRAMEWORK */ - } - else - { - nsent = send(sock->fd, msg, len, 0); - if (nsent < 0) - { - if (errno == EAGAIN) nsent = 0; - else { LogMsg("ERROR: mDNSPlatformWriteTCP - send %s", strerror(errno)); nsent = -1; } - } - } - - return nsent; - } + } + else + { + nsent = send(sock->fd, msg, len, 0); + if (nsent < 0) + { + if (errno == EAGAIN) nsent = 0; + else { LogMsg("ERROR: mDNSPlatformWriteTCP - send %s", strerror(errno)); nsent = -1; } + } + } + + return nsent; +} mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock) - { - return sock->fd; - } +{ + return sock->fd; +} // 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(KQSocketSet *cp, const mDNSIPPort port, u_short sa_family, mDNSIPPort *const outport) - { +{ #ifndef NO_IPV6 - int *s = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6; - KQueueEntry *k = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6; + int *s = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6; + KQueueEntry *k = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6; #else - int *s = &cp->sktv4; - KQueueEntry *k = &cp->kqsv4; + int *s = &cp->sktv4; + KQueueEntry *k = &cp->kqsv4; #endif - const int on = 1; - const int twofivefive = 255; - mStatus err = mStatus_NoError; - char *errstr = mDNSNULL; + const int on = 1; + const int twofivefive = 255; + mStatus err = mStatus_NoError; + char *errstr = mDNSNULL; #ifdef NO_IPV6 - if (sa_family != AF_INET) return -1; + if (sa_family != AF_INET) return -1; #endif - - cp->closeFlag = mDNSNULL; - - int skt = socket(sa_family, SOCK_DGRAM, IPPROTO_UDP); - if (skt < 3) { if (errno != EAFNOSUPPORT) 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 (mDNSSameIPPort(port, MulticastDNSPort) || mDNSSameIPPort(port, NATPMPAnnouncementPort)) err = setsockopt(skt, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); - if (err < 0) { errstr = "setsockopt - SO_REUSEPORT"; goto fail; } - - if (sa_family == AF_INET) - { - // We want to receive destination addresses - err = setsockopt(skt, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); - if (err < 0) { errstr = "setsockopt - IP_RECVDSTADDR"; goto fail; } - - // We want to receive interface identifiers - err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)); - if (err < 0) { errstr = "setsockopt - IP_RECVIF"; goto fail; } - - // We want to receive packet TTL value so we can check it - err = setsockopt(skt, IPPROTO_IP, IP_RECVTTL, &on, sizeof(on)); - // We ignore errors here -- we already know Jaguar doesn't support this, but we can get by without it - - // Send unicast packets with TTL 255 - err = setsockopt(skt, IPPROTO_IP, IP_TTL, &twofivefive, sizeof(twofivefive)); - if (err < 0) { errstr = "setsockopt - IP_TTL"; goto fail; } - - // And multicast packets with TTL 255 too - err = setsockopt(skt, IPPROTO_IP, IP_MULTICAST_TTL, &twofivefive, sizeof(twofivefive)); - if (err < 0) { errstr = "setsockopt - IP_MULTICAST_TTL"; goto fail; } - - // And start listening for packets - struct sockaddr_in listening_sockaddr; - listening_sockaddr.sin_family = AF_INET; - listening_sockaddr.sin_port = port.NotAnInteger; // Pass in opaque ID without any byte swapping - listening_sockaddr.sin_addr.s_addr = mDNSSameIPPort(port, NATPMPAnnouncementPort) ? AllHosts_v4.NotAnInteger : 0; - err = bind(skt, (struct sockaddr *) &listening_sockaddr, sizeof(listening_sockaddr)); - if (err) { errstr = "bind"; goto fail; } - if (outport) outport->NotAnInteger = listening_sockaddr.sin_port; - } + + cp->closeFlag = mDNSNULL; + + int skt = socket(sa_family, SOCK_DGRAM, IPPROTO_UDP); + if (skt < 3) { if (errno != EAFNOSUPPORT) LogMsg("SetupSocket: socket error %d errno %d (%s)", skt, errno, strerror(errno));return(skt); } + + // set default traffic class + setTrafficClass(skt, mDNSfalse); + +#ifdef SO_RECV_ANYIF + // Enable inbound packets on IFEF_AWDL interface. + // Only done for multicast sockets, since we don't expect unicast socket operations + // on the IFEF_AWDL interface. Operation is a no-op for other interface types. + if (mDNSSameIPPort(port, MulticastDNSPort)) + { + err = setsockopt(skt, SOL_SOCKET, SO_RECV_ANYIF, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - SO_RECV_ANYIF"; goto fail; } + } +#endif // SO_RECV_ANYIF + + // ... with a shared UDP port, if it's for multicast receiving + if (mDNSSameIPPort(port, MulticastDNSPort) || mDNSSameIPPort(port, NATPMPAnnouncementPort)) err = setsockopt(skt, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - SO_REUSEPORT"; goto fail; } + + if (sa_family == AF_INET) + { + // We want to receive destination addresses + err = setsockopt(skt, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - IP_RECVDSTADDR"; goto fail; } + + // We want to receive interface identifiers + err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - IP_RECVIF"; goto fail; } + + // We want to receive packet TTL value so we can check it + err = setsockopt(skt, IPPROTO_IP, IP_RECVTTL, &on, sizeof(on)); + // We ignore errors here -- we already know Jaguar doesn't support this, but we can get by without it + + // Send unicast packets with TTL 255 + err = setsockopt(skt, IPPROTO_IP, IP_TTL, &twofivefive, sizeof(twofivefive)); + if (err < 0) { errstr = "setsockopt - IP_TTL"; goto fail; } + + // And multicast packets with TTL 255 too + err = setsockopt(skt, IPPROTO_IP, IP_MULTICAST_TTL, &twofivefive, sizeof(twofivefive)); + if (err < 0) { errstr = "setsockopt - IP_MULTICAST_TTL"; goto fail; } + + // And start listening for packets + struct sockaddr_in listening_sockaddr; + listening_sockaddr.sin_family = AF_INET; + listening_sockaddr.sin_port = port.NotAnInteger; // Pass in opaque ID without any byte swapping + listening_sockaddr.sin_addr.s_addr = mDNSSameIPPort(port, NATPMPAnnouncementPort) ? AllHosts_v4.NotAnInteger : 0; + err = bind(skt, (struct sockaddr *) &listening_sockaddr, sizeof(listening_sockaddr)); + if (err) { errstr = "bind"; goto fail; } + if (outport) outport->NotAnInteger = listening_sockaddr.sin_port; + } #ifndef NO_IPV6 - else if (sa_family == AF_INET6) - { - // NAT-PMP Announcements make no sense on IPv6, so bail early w/o error - if (mDNSSameIPPort(port, NATPMPAnnouncementPort)) { if (outport) *outport = zeroIPPort; return mStatus_NoError; } - - // We want to receive destination addresses and receive interface identifiers - err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); - if (err < 0) { errstr = "setsockopt - IPV6_RECVPKTINFO"; goto fail; } - - // We want to receive packet hop count value so we can check it - err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)); - if (err < 0) { errstr = "setsockopt - IPV6_RECVHOPLIMIT"; goto fail; } - - // We want to receive only IPv6 packets. Without this option we get IPv4 packets too, - // with mapped addresses of the form 0:0:0:0:0:FFFF:xxxx:xxxx, where xxxx:xxxx is the IPv4 address - err = setsockopt(skt, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); - if (err < 0) { errstr = "setsockopt - IPV6_V6ONLY"; goto fail; } - - // Send unicast packets with TTL 255 - err = setsockopt(skt, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &twofivefive, sizeof(twofivefive)); - if (err < 0) { errstr = "setsockopt - IPV6_UNICAST_HOPS"; goto fail; } - - // And multicast packets with TTL 255 too - err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &twofivefive, sizeof(twofivefive)); - if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_HOPS"; goto fail; } - - // Want to receive our own packets - err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(on)); - if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_LOOP"; goto fail; } - - // And start listening for packets - struct sockaddr_in6 listening_sockaddr6; - mDNSPlatformMemZero(&listening_sockaddr6, sizeof(listening_sockaddr6)); - listening_sockaddr6.sin6_len = sizeof(listening_sockaddr6); - listening_sockaddr6.sin6_family = AF_INET6; - listening_sockaddr6.sin6_port = port.NotAnInteger; // Pass in opaque ID without any byte swapping - listening_sockaddr6.sin6_flowinfo = 0; - listening_sockaddr6.sin6_addr = in6addr_any; // Want to receive multicasts AND unicasts on this socket - listening_sockaddr6.sin6_scope_id = 0; - err = bind(skt, (struct sockaddr *) &listening_sockaddr6, sizeof(listening_sockaddr6)); - if (err) { errstr = "bind"; goto fail; } - if (outport) outport->NotAnInteger = listening_sockaddr6.sin6_port; - } + else if (sa_family == AF_INET6) + { + // NAT-PMP Announcements make no sense on IPv6, so bail early w/o error + if (mDNSSameIPPort(port, NATPMPAnnouncementPort)) { if (outport) *outport = zeroIPPort;return mStatus_NoError; } + + // We want to receive destination addresses and receive interface identifiers + err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - IPV6_RECVPKTINFO"; goto fail; } + + // We want to receive packet hop count value so we can check it + err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - IPV6_RECVHOPLIMIT"; goto fail; } + + // We want to receive only IPv6 packets. Without this option we get IPv4 packets too, + // with mapped addresses of the form 0:0:0:0:0:FFFF:xxxx:xxxx, where xxxx:xxxx is the IPv4 address + err = setsockopt(skt, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - IPV6_V6ONLY"; goto fail; } + + // Send unicast packets with TTL 255 + err = setsockopt(skt, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &twofivefive, sizeof(twofivefive)); + if (err < 0) { errstr = "setsockopt - IPV6_UNICAST_HOPS"; goto fail; } + + // And multicast packets with TTL 255 too + err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &twofivefive, sizeof(twofivefive)); + if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_HOPS"; goto fail; } + + // Want to receive our own packets + err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_LOOP"; goto fail; } + + // And start listening for packets + struct sockaddr_in6 listening_sockaddr6; + mDNSPlatformMemZero(&listening_sockaddr6, sizeof(listening_sockaddr6)); + listening_sockaddr6.sin6_len = sizeof(listening_sockaddr6); + listening_sockaddr6.sin6_family = AF_INET6; + listening_sockaddr6.sin6_port = port.NotAnInteger; // Pass in opaque ID without any byte swapping + listening_sockaddr6.sin6_flowinfo = 0; + listening_sockaddr6.sin6_addr = in6addr_any; // Want to receive multicasts AND unicasts on this socket + listening_sockaddr6.sin6_scope_id = 0; + err = bind(skt, (struct sockaddr *) &listening_sockaddr6, sizeof(listening_sockaddr6)); + if (err) { errstr = "bind"; goto fail; } + if (outport) outport->NotAnInteger = listening_sockaddr6.sin6_port; + } #endif - fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK); // set non-blocking - fcntl(skt, F_SETFD, 1); // set close-on-exec - *s = skt; - k->KQcallback = myKQSocketCallBack; - k->KQcontext = cp; - k->KQtask = "UDP packet reception"; + fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK); // set non-blocking + fcntl(skt, F_SETFD, 1); // set close-on-exec + *s = skt; + k->KQcallback = myKQSocketCallBack; + k->KQcontext = cp; + k->KQtask = "UDP packet reception"; #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM - k->readSource = mDNSNULL; - k->writeSource = mDNSNULL; - k->fdClosed = mDNSfalse; + k->readSource = mDNSNULL; + k->writeSource = mDNSNULL; + k->fdClosed = mDNSfalse; #endif - KQueueSet(*s, EV_ADD, EVFILT_READ, k); - - return(err); - - fail: - // For "bind" failures, only write log messages for our shared mDNS port, or for binding to zero - if (strcmp(errstr, "bind") || mDNSSameIPPort(port, MulticastDNSPort) || mDNSIPPortIsZero(port)) - LogMsg("%s skt %d port %d error %d errno %d (%s)", errstr, skt, mDNSVal16(port), err, errno, strerror(errno)); - - // If we got a "bind" failure of EADDRINUSE, inform the caller as it might need to try another random port - if (!strcmp(errstr, "bind") && errno == EADDRINUSE) - { - err = EADDRINUSE; - if (mDNSSameIPPort(port, MulticastDNSPort)) - NotifyOfElusiveBug("Setsockopt SO_REUSEPORT failed", - "Congratulations, you've reproduced an elusive bug.\r" - "Please contact the current assignee of .\r" - "Alternatively, you can send email to radar-3387020@group.apple.com. (Note number is different.)\r" - "If possible, please leave your machine undisturbed so that someone can come to investigate the problem."); - } - - mDNSPlatformCloseFD(k, skt); - return(err); - } + KQueueSet(*s, EV_ADD, EVFILT_READ, k); + + return(err); + +fail: + // For "bind" failures, only write log messages for our shared mDNS port, or for binding to zero + if (strcmp(errstr, "bind") || mDNSSameIPPort(port, MulticastDNSPort) || mDNSIPPortIsZero(port)) + LogMsg("%s skt %d port %d error %d errno %d (%s)", errstr, skt, mDNSVal16(port), err, errno, strerror(errno)); + + // If we got a "bind" failure of EADDRINUSE, inform the caller as it might need to try another random port + if (!strcmp(errstr, "bind") && errno == EADDRINUSE) + { + err = EADDRINUSE; + if (mDNSSameIPPort(port, MulticastDNSPort)) + NotifyOfElusiveBug("Setsockopt SO_REUSEPORT failed", + "Congratulations, you've reproduced an elusive bug.\r" + "Please contact the current assignee of .\r" + "Alternatively, you can send email to radar-3387020@group.apple.com. (Note number is different.)\r" + "If possible, please leave your machine undisturbed so that someone can come to investigate the problem."); + } + + mDNSPlatformCloseFD(k, skt); + return(err); +} mDNSexport UDPSocket *mDNSPlatformUDPSocket(mDNS *const m, const mDNSIPPort requestedport) - { - mStatus err; - mDNSIPPort port = requestedport; - mDNSBool randomizePort = mDNSIPPortIsZero(requestedport); - int i = 10000; // Try at most 10000 times to get a unique random port - UDPSocket *p = mallocL("UDPSocket", sizeof(UDPSocket)); - if (!p) { LogMsg("mDNSPlatformUDPSocket: memory exhausted"); return(mDNSNULL); } - mDNSPlatformMemZero(p, sizeof(UDPSocket)); - p->ss.port = zeroIPPort; - p->ss.m = m; - p->ss.sktv4 = -1; +{ + mStatus err; + mDNSIPPort port = requestedport; + mDNSBool randomizePort = mDNSIPPortIsZero(requestedport); + int i = 10000; // Try at most 10000 times to get a unique random port + UDPSocket *p = mallocL("UDPSocket", sizeof(UDPSocket)); + if (!p) { LogMsg("mDNSPlatformUDPSocket: memory exhausted"); return(mDNSNULL); } + mDNSPlatformMemZero(p, sizeof(UDPSocket)); + p->ss.port = zeroIPPort; + p->ss.m = m; + p->ss.sktv4 = -1; #ifndef NO_IPV6 - p->ss.sktv6 = -1; + p->ss.sktv6 = -1; #endif - do - { - // The kernel doesn't do cryptographically strong random port allocation, so we do it ourselves here - if (randomizePort) port = mDNSOpaque16fromIntVal(0xC000 + mDNSRandom(0x3FFF)); - err = SetupSocket(&p->ss, port, AF_INET, &p->ss.port); + do + { + // The kernel doesn't do cryptographically strong random port allocation, so we do it ourselves here + if (randomizePort) port = mDNSOpaque16fromIntVal(0xC000 + mDNSRandom(0x3FFF)); + err = SetupSocket(&p->ss, port, AF_INET, &p->ss.port); #ifndef NO_IPV6 - if (!err) - { - err = SetupSocket(&p->ss, port, AF_INET6, &p->ss.port); - if (err) { mDNSPlatformCloseFD(&p->ss.kqsv4, p->ss.sktv4); p->ss.sktv4 = -1; } - } + if (!err) + { + err = SetupSocket(&p->ss, port, AF_INET6, &p->ss.port); + if (err) { mDNSPlatformCloseFD(&p->ss.kqsv4, p->ss.sktv4); p->ss.sktv4 = -1; } + } #endif - i--; - } while (err == EADDRINUSE && randomizePort && i); - - if (err) - { - // In customer builds we don't want to log failures with port 5351, because this is a known issue - // of failing to bind to this port when Internet Sharing has already bound to it - // We also don't want to log about port 5350, due to a known bug when some other - // process is bound to it. - if (mDNSSameIPPort(requestedport, NATPMPPort) || mDNSSameIPPort(requestedport, NATPMPAnnouncementPort)) - LogInfo("mDNSPlatformUDPSocket: SetupSocket %d failed error %d errno %d (%s)", mDNSVal16(requestedport), err, errno, strerror(errno)); - else LogMsg("mDNSPlatformUDPSocket: SetupSocket %d failed error %d errno %d (%s)", mDNSVal16(requestedport), err, errno, strerror(errno)); - freeL("UDPSocket", p); - return(mDNSNULL); - } - return(p); - } + i--; + } while (err == EADDRINUSE && randomizePort && i); + + if (err) + { + // In customer builds we don't want to log failures with port 5351, because this is a known issue + // of failing to bind to this port when Internet Sharing has already bound to it + // We also don't want to log about port 5350, due to a known bug when some other + // process is bound to it. + if (mDNSSameIPPort(requestedport, NATPMPPort) || mDNSSameIPPort(requestedport, NATPMPAnnouncementPort)) + LogInfo("mDNSPlatformUDPSocket: SetupSocket %d failed error %d errno %d (%s)", mDNSVal16(requestedport), err, errno, strerror(errno)); + else LogMsg("mDNSPlatformUDPSocket: SetupSocket %d failed error %d errno %d (%s)", mDNSVal16(requestedport), err, errno, strerror(errno)); + freeL("UDPSocket", p); + return(mDNSNULL); + } + return(p); +} mDNSexport void mDNSPlatformUDPClose(UDPSocket *sock) - { - CloseSocketSet(&sock->ss); - freeL("UDPSocket", sock); - } +{ + CloseSocketSet(&sock->ss); + freeL("UDPSocket", sock); +} #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -2344,372 +2583,398 @@ mDNSexport void mDNSPlatformUDPClose(UDPSocket *sock) #if APPLE_OSX_mDNSResponder mDNSexport void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID) - { - if (!InterfaceID) { LogMsg("mDNSPlatformSendRawPacket: No InterfaceID specified"); return; } - NetworkInterfaceInfoOSX *info; - - info = IfindexToInterfaceInfoOSX(&mDNSStorage, InterfaceID); - if (info == NULL) - { - LogMsg("mDNSPlatformSendUDP: Invalid interface index %p", InterfaceID); - return; - } - if (info->BPF_fd < 0) - LogMsg("mDNSPlatformSendRawPacket: %s BPF_fd %d not ready", info->ifinfo.ifname, info->BPF_fd); - else - { - //LogMsg("mDNSPlatformSendRawPacket %d bytes on %s", end - (mDNSu8 *)msg, info->ifinfo.ifname); - if (write(info->BPF_fd, msg, end - (mDNSu8 *)msg) < 0) - LogMsg("mDNSPlatformSendRawPacket: BPF write(%d) failed %d (%s)", info->BPF_fd, errno, strerror(errno)); - } - } +{ + if (!InterfaceID) { LogMsg("mDNSPlatformSendRawPacket: No InterfaceID specified"); return; } + NetworkInterfaceInfoOSX *info; + + info = IfindexToInterfaceInfoOSX(&mDNSStorage, InterfaceID); + if (info == NULL) + { + LogMsg("mDNSPlatformSendUDP: Invalid interface index %p", InterfaceID); + return; + } + if (info->BPF_fd < 0) + LogMsg("mDNSPlatformSendRawPacket: %s BPF_fd %d not ready", info->ifinfo.ifname, info->BPF_fd); + else + { + //LogMsg("mDNSPlatformSendRawPacket %d bytes on %s", end - (mDNSu8 *)msg, info->ifinfo.ifname); + if (write(info->BPF_fd, msg, end - (mDNSu8 *)msg) < 0) + LogMsg("mDNSPlatformSendRawPacket: BPF write(%d) failed %d (%s)", info->BPF_fd, errno, strerror(errno)); + } +} mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID) - { - if (!InterfaceID) { LogMsg("mDNSPlatformSetLocalAddressCacheEntry: No InterfaceID specified"); return; } - NetworkInterfaceInfoOSX *info; - info = IfindexToInterfaceInfoOSX(m, InterfaceID); - if (info == NULL) { LogMsg("mDNSPlatformSetLocalAddressCacheEntry: Invalid interface index %p", InterfaceID); return; } - // Manually inject an entry into our local ARP cache. - // (We can't do this by sending an ARP broadcast, because the kernel only pays attention to incoming ARP packets, not outgoing.) - if (!mDNS_AddressIsLocalSubnet(m, InterfaceID, tpa)) - LogSPS("Don't need address cache entry for %s %#a %.6a", info->ifinfo.ifname, tpa, tha); - else - { - int result = mDNSSetLocalAddressCacheEntry(info->scope_id, tpa->type, tpa->ip.v6.b, tha->b); - if (result) LogMsg("Set local address cache entry for %s %#a %.6a failed: %d", info->ifinfo.ifname, tpa, tha, result); - else LogSPS("Set local address cache entry for %s %#a %.6a", info->ifinfo.ifname, tpa, tha); - } - } +{ + if (!InterfaceID) { LogMsg("mDNSPlatformSetLocalAddressCacheEntry: No InterfaceID specified"); return; } + NetworkInterfaceInfoOSX *info; + info = IfindexToInterfaceInfoOSX(m, InterfaceID); + if (info == NULL) { LogMsg("mDNSPlatformSetLocalAddressCacheEntry: Invalid interface index %p", InterfaceID); return; } + // Manually inject an entry into our local ARP cache. + // (We can't do this by sending an ARP broadcast, because the kernel only pays attention to incoming ARP packets, not outgoing.) + if (!mDNS_AddressIsLocalSubnet(m, InterfaceID, tpa)) + LogSPS("Don't need address cache entry for %s %#a %.6a", info->ifinfo.ifname, tpa, tha); + else + { + int result = mDNSSetLocalAddressCacheEntry(info->scope_id, tpa->type, tpa->ip.v6.b, tha->b); + if (result) LogMsg("Set local address cache entry for %s %#a %.6a failed: %d", info->ifinfo.ifname, tpa, tha, result); + else LogSPS("Set local address cache entry for %s %#a %.6a", info->ifinfo.ifname, tpa, tha); + } +} mDNSlocal void CloseBPF(NetworkInterfaceInfoOSX *const i) - { - LogSPS("%s closing BPF fd %d", i->ifinfo.ifname, i->BPF_fd); +{ + LogSPS("%s closing BPF fd %d", i->ifinfo.ifname, i->BPF_fd); #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM - // close will happen in the cancel handler - dispatch_source_cancel(i->BPF_source); + // close will happen in the cancel handler + dispatch_source_cancel(i->BPF_source); #else - // 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 on its other thread, before it can safely close them. - CFRunLoopRemoveSource(i->m->p->CFRunLoop, i->BPF_rls, kCFRunLoopDefaultMode); - CFRelease(i->BPF_rls); - CFSocketInvalidate(i->BPF_cfs); - CFRelease(i->BPF_cfs); + // 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 on its other thread, before it can safely close them. + CFRunLoopRemoveSource(i->m->p->CFRunLoop, i->BPF_rls, kCFRunLoopDefaultMode); + CFRelease(i->BPF_rls); + CFSocketInvalidate(i->BPF_cfs); + CFRelease(i->BPF_cfs); #endif - i->BPF_fd = -1; - if (i->BPF_mcfd >= 0) { close(i->BPF_mcfd); i->BPF_mcfd = -1; } - } + i->BPF_fd = -1; + if (i->BPF_mcfd >= 0) { close(i->BPF_mcfd); i->BPF_mcfd = -1; } +} mDNSlocal void bpf_callback_common(NetworkInterfaceInfoOSX *info) - { - KQueueLock(info->m); - - // Now we've got the lock, make sure the kqueue thread didn't close the fd out from under us (will not be a problem once the OS X - // kernel has a mechanism for dispatching all events to a single thread, but for now we have to guard against this race condition). - if (info->BPF_fd < 0) goto exit; - - ssize_t n = read(info->BPF_fd, &info->m->imsg, info->BPF_len); - const mDNSu8 *ptr = (const mDNSu8 *)&info->m->imsg; - const mDNSu8 *end = (const mDNSu8 *)&info->m->imsg + n; - debugf("%3d: bpf_callback got %d bytes on %s", info->BPF_fd, n, info->ifinfo.ifname); - - if (n<0) - { - LogMsg("Closing %s BPF fd %d due to error %d (%s)", info->ifinfo.ifname, info->BPF_fd, errno, strerror(errno)); - CloseBPF(info); - goto exit; - } - - while (ptr < end) - { - const struct bpf_hdr *const bh = (const struct bpf_hdr *)ptr; - debugf("%3d: bpf_callback ptr %p bh_hdrlen %d data %p bh_caplen %4d bh_datalen %4d next %p remaining %4d", - info->BPF_fd, ptr, bh->bh_hdrlen, ptr + bh->bh_hdrlen, bh->bh_caplen, bh->bh_datalen, - ptr + BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen), end - (ptr + BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen))); - // Note that BPF guarantees that the NETWORK LAYER header will be word aligned, not the link-layer header. - // Given that An Ethernet header is 14 bytes, this means that if the network layer header (e.g. IP header, - // ARP message, etc.) is 4-byte aligned, then necessarily the Ethernet header will be NOT be 4-byte aligned. - mDNSCoreReceiveRawPacket(info->m, ptr + bh->bh_hdrlen, ptr + bh->bh_hdrlen + bh->bh_caplen, info->ifinfo.InterfaceID); - ptr += BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen); - } +{ + KQueueLock(info->m); + + // Now we've got the lock, make sure the kqueue thread didn't close the fd out from under us (will not be a problem once the OS X + // kernel has a mechanism for dispatching all events to a single thread, but for now we have to guard against this race condition). + if (info->BPF_fd < 0) goto exit; + + ssize_t n = read(info->BPF_fd, &info->m->imsg, info->BPF_len); + const mDNSu8 *ptr = (const mDNSu8 *)&info->m->imsg; + const mDNSu8 *end = (const mDNSu8 *)&info->m->imsg + n; + debugf("%3d: bpf_callback got %d bytes on %s", info->BPF_fd, n, info->ifinfo.ifname); + + if (n<0) + { + /* + * sometimes there can be a race condition btw when the bpf socket + * gets data and the callback get scheduled and when we call BIOCSETF (which + * clears the socket). this can cause the read to hang for a really long time + * and effectively prevent us from responding to requests for long periods of time. + * to prevent this make the socket non blocking and just bail if we dont get anything + */ + if (errno == EAGAIN) + { + LogMsg("bpf_callback got EAGAIN bailing"); + goto exit; + } + LogMsg("Closing %s BPF fd %d due to error %d (%s)", info->ifinfo.ifname, info->BPF_fd, errno, strerror(errno)); + CloseBPF(info); + goto exit; + } + + while (ptr < end) + { + const struct bpf_hdr *const bh = (const struct bpf_hdr *)ptr; + debugf("%3d: bpf_callback ptr %p bh_hdrlen %d data %p bh_caplen %4d bh_datalen %4d next %p remaining %4d", + info->BPF_fd, ptr, bh->bh_hdrlen, ptr + bh->bh_hdrlen, bh->bh_caplen, bh->bh_datalen, + ptr + BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen), end - (ptr + BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen))); + // Note that BPF guarantees that the NETWORK LAYER header will be word aligned, not the link-layer header. + // Given that An Ethernet header is 14 bytes, this means that if the network layer header (e.g. IP header, + // ARP message, etc.) is 4-byte aligned, then necessarily the Ethernet header will be NOT be 4-byte aligned. + mDNSCoreReceiveRawPacket(info->m, ptr + bh->bh_hdrlen, ptr + bh->bh_hdrlen + bh->bh_caplen, info->ifinfo.InterfaceID); + ptr += BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen); + } exit: - KQueueUnlock(info->m, "bpf_callback"); - } + KQueueUnlock(info->m, "bpf_callback"); +} #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM mDNSlocal void bpf_callback_dispatch(NetworkInterfaceInfoOSX *const info) - { - bpf_callback_common(info); - } +{ + bpf_callback_common(info); +} #else mDNSlocal void bpf_callback(const CFSocketRef cfs, const CFSocketCallBackType CallBackType, const CFDataRef address, const void *const data, void *const context) - { - (void)cfs; - (void)CallBackType; - (void)address; - (void)data; - bpf_callback_common((NetworkInterfaceInfoOSX *)context); - } +{ + (void)cfs; + (void)CallBackType; + (void)address; + (void)data; + bpf_callback_common((NetworkInterfaceInfoOSX *)context); +} #endif +mDNSexport void mDNSPlatformSendKeepalive(mDNSAddr *sadd, mDNSAddr *dadd, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu32 seq, mDNSu32 ack, mDNSu16 win) +{ + LogMsg("mDNSPlatformSendKeepalive called\n"); + mDNSSendKeepalive(sadd->ip.v6.b, dadd->ip.v6.b, lport->NotAnInteger, rport->NotAnInteger, seq, ack, win); +} + #define BPF_SetOffset(from, cond, to) (from)->cond = (to) - 1 - (from) mDNSlocal int CountProxyTargets(mDNS *const m, NetworkInterfaceInfoOSX *x, int *p4, int *p6) - { - int numv4 = 0, numv6 = 0; - AuthRecord *rr; - - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.InterfaceID == x->ifinfo.InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv4) - { - if (p4) LogSPS("CountProxyTargets: fd %d %-7s IP%2d %.4a", x->BPF_fd, x->ifinfo.ifname, numv4, &rr->AddressProxy.ip.v4); - numv4++; - } - - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.InterfaceID == x->ifinfo.InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv6) - { - if (p6) LogSPS("CountProxyTargets: fd %d %-7s IP%2d %.16a", x->BPF_fd, x->ifinfo.ifname, numv6, &rr->AddressProxy.ip.v6); - numv6++; - } - - if (p4) *p4 = numv4; - if (p6) *p6 = numv6; - return(numv4 + numv6); - } +{ + int numv4 = 0, numv6 = 0; + AuthRecord *rr; + + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.InterfaceID == x->ifinfo.InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv4) + { + if (p4) LogSPS("CountProxyTargets: fd %d %-7s IP%2d %.4a", x->BPF_fd, x->ifinfo.ifname, numv4, &rr->AddressProxy.ip.v4); + numv4++; + } + + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.InterfaceID == x->ifinfo.InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv6) + { + if (p6) LogSPS("CountProxyTargets: fd %d %-7s IP%2d %.16a", x->BPF_fd, x->ifinfo.ifname, numv6, &rr->AddressProxy.ip.v6); + numv6++; + } + + if (p4) *p4 = numv4; + if (p6) *p6 = numv6; + return(numv4 + numv6); +} mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID) - { - NetworkInterfaceInfoOSX *x; - - // Note: We can't use IfIndexToInterfaceInfoOSX because that looks for Registered also. - for (x = m->p->InterfaceList; x; x = x->next) if (x->ifinfo.InterfaceID == InterfaceID) break; - - if (!x) { LogMsg("mDNSPlatformUpdateProxyList: ERROR InterfaceID %p not found", InterfaceID); return; } - - #define MAX_BPF_ADDRS 250 - int numv4 = 0, numv6 = 0; - - if (CountProxyTargets(m, x, &numv4, &numv6) > MAX_BPF_ADDRS) - { - LogMsg("mDNSPlatformUpdateProxyList: ERROR Too many address proxy records v4 %d v6 %d", numv4, numv6); - if (numv4 > MAX_BPF_ADDRS) numv4 = MAX_BPF_ADDRS; - numv6 = MAX_BPF_ADDRS - numv4; - } - - LogSPS("mDNSPlatformUpdateProxyList: fd %d %-7s MAC %.6a %d v4 %d v6", x->BPF_fd, x->ifinfo.ifname, &x->ifinfo.MAC, numv4, numv6); - - // Caution: This is a static structure, so we need to be careful that any modifications we make to it - // are done in such a way that they work correctly when mDNSPlatformUpdateProxyList is called multiple times - static struct bpf_insn filter[17 + MAX_BPF_ADDRS] = - { - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12), // 0 Read Ethertype (bytes 12,13) - - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0806, 0, 1), // 1 If Ethertype == ARP goto next, else 3 - BPF_STMT(BPF_RET + BPF_K, 42), // 2 Return 42-byte ARP - - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0800, 4, 0), // 3 If Ethertype == IPv4 goto 8 (IPv4 address list check) else next - - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x86DD, 0, 9), // 4 If Ethertype == IPv6 goto next, else exit - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20), // 5 Read Protocol and Hop Limit (bytes 20,21) - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x3AFF, 0, 9), // 6 If (Prot,TTL) == (3A,FF) goto next, else IPv6 address list check - BPF_STMT(BPF_RET + BPF_K, 86), // 7 Return 86-byte ND - - // Is IPv4 packet; check if it's addressed to any IPv4 address we're proxying for - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 30), // 8 Read IPv4 Dst (bytes 30,31,32,33) - }; - - struct bpf_insn *pc = &filter[9]; - struct bpf_insn *chk6 = pc + numv4 + 1; // numv4 address checks, plus a "return 0" - struct bpf_insn *fail = chk6 + 1 + numv6; // Get v6 Dst LSW, plus numv6 address checks - struct bpf_insn *ret4 = fail + 1; - struct bpf_insn *ret6 = ret4 + 4; - - static const struct bpf_insn rf = BPF_STMT(BPF_RET + BPF_K, 0); // No match: Return nothing - - static const struct bpf_insn g6 = BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 50); // Read IPv6 Dst LSW (bytes 50,51,52,53) - - static const struct bpf_insn r4a = BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14); // Get IP Header length (normally 20) - static const struct bpf_insn r4b = BPF_STMT(BPF_LD + BPF_IMM, 54); // A = 54 (14-byte Ethernet plus 20-byte TCP + 20 bytes spare) - static const struct bpf_insn r4c = BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0); // A += IP Header length - static const struct bpf_insn r4d = BPF_STMT(BPF_RET + BPF_A, 0); // Success: Return Ethernet + IP + TCP + 20 bytes spare (normally 74) - - static const struct bpf_insn r6a = BPF_STMT(BPF_RET + BPF_K, 94); // Success: Return Eth + IPv6 + TCP + 20 bytes spare - - BPF_SetOffset(&filter[4], jf, fail); // If Ethertype not ARP, IPv4, or IPv6, fail - BPF_SetOffset(&filter[6], jf, chk6); // If IPv6 but not ICMPv6, go to IPv6 address list check - - // BPF Byte-Order Note - // The BPF API designers apparently thought that programmers would not be smart enough to use htons - // and htonl correctly to convert numeric values to network byte order on little-endian machines, - // so instead they chose to make the API implicitly byte-swap *ALL* values, even literal byte strings - // that shouldn't be byte-swapped, like ASCII text, Ethernet addresses, IP addresses, etc. - // As a result, if we put Ethernet addresses and IP addresses in the right byte order, the BPF API - // will byte-swap and make them backwards, and then our filter won't work. So, we have to arrange - // that on little-endian machines we deliberately put addresses in memory with the bytes backwards, - // so that when the BPF API goes through and swaps them all, they end up back as they should be. - // In summary, if we byte-swap all the non-numeric fields that shouldn't be swapped, and we *don't* - // swap any of the numeric values that *should* be byte-swapped, then the filter will work correctly. - - // IPSEC capture size notes: - // 8 bytes UDP header - // 4 bytes Non-ESP Marker - // 28 bytes IKE Header - // -- - // 40 Total. Capturing TCP Header + 20 gets us enough bytes to receive the IKE Header in a UDP-encapsulated IKE packet. - - AuthRecord *rr; - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.InterfaceID == InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv4) - { - mDNSv4Addr a = rr->AddressProxy.ip.v4; - pc->code = BPF_JMP + BPF_JEQ + BPF_K; - BPF_SetOffset(pc, jt, ret4); - pc->jf = 0; - pc->k = (bpf_u_int32)a.b[0] << 24 | (bpf_u_int32)a.b[1] << 16 | (bpf_u_int32)a.b[2] << 8 | (bpf_u_int32)a.b[3]; - pc++; - } - *pc++ = rf; - - if (pc != chk6) LogMsg("mDNSPlatformUpdateProxyList: pc %p != chk6 %p", pc, chk6); - *pc++ = g6; // chk6 points here - - // First cancel any previous ND group memberships we had, then create a fresh socket - if (x->BPF_mcfd >= 0) close(x->BPF_mcfd); - x->BPF_mcfd = socket(AF_INET6, SOCK_DGRAM, 0); - - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.InterfaceID == InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv6) - { - const mDNSv6Addr *const a = &rr->AddressProxy.ip.v6; - pc->code = BPF_JMP + BPF_JEQ + BPF_K; - BPF_SetOffset(pc, jt, ret6); - pc->jf = 0; - pc->k = (bpf_u_int32)a->b[0x0C] << 24 | (bpf_u_int32)a->b[0x0D] << 16 | (bpf_u_int32)a->b[0x0E] << 8 | (bpf_u_int32)a->b[0x0F]; - pc++; - - struct ipv6_mreq i6mr; - i6mr.ipv6mr_interface = x->scope_id; - i6mr.ipv6mr_multiaddr = *(const struct in6_addr*)&NDP_prefix; - i6mr.ipv6mr_multiaddr.s6_addr[0xD] = a->b[0xD]; - i6mr.ipv6mr_multiaddr.s6_addr[0xE] = a->b[0xE]; - i6mr.ipv6mr_multiaddr.s6_addr[0xF] = a->b[0xF]; - - // Do precautionary IPV6_LEAVE_GROUP first, necessary to clear stale kernel state - mStatus err = setsockopt(x->BPF_mcfd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &i6mr, sizeof(i6mr)); - if (err < 0 && (errno != EADDRNOTAVAIL)) - LogMsg("mDNSPlatformUpdateProxyList: IPV6_LEAVE_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); - - err = setsockopt(x->BPF_mcfd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &i6mr, sizeof(i6mr)); - if (err < 0 && (errno != EADDRINUSE)) // Joining same group twice can give "Address already in use" error -- no need to report that - LogMsg("mDNSPlatformUpdateProxyList: IPV6_JOIN_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); - - LogSPS("Joined IPv6 ND multicast group %.16a for %.16a", &i6mr.ipv6mr_multiaddr, a); - } - - if (pc != fail) LogMsg("mDNSPlatformUpdateProxyList: pc %p != fail %p", pc, fail); - *pc++ = rf; // fail points here - - if (pc != ret4) LogMsg("mDNSPlatformUpdateProxyList: pc %p != ret4 %p", pc, ret4); - *pc++ = r4a; // ret4 points here - *pc++ = r4b; - *pc++ = r4c; - *pc++ = r4d; - - if (pc != ret6) LogMsg("mDNSPlatformUpdateProxyList: pc %p != ret6 %p", pc, ret6); - *pc++ = r6a; // ret6 points here - - struct bpf_program prog = { pc - filter, filter }; +{ + NetworkInterfaceInfoOSX *x; + + // Note: We can't use IfIndexToInterfaceInfoOSX because that looks for Registered also. + for (x = m->p->InterfaceList; x; x = x->next) if (x->ifinfo.InterfaceID == InterfaceID) break; + + if (!x) { LogMsg("mDNSPlatformUpdateProxyList: ERROR InterfaceID %p not found", InterfaceID); return; } + + #define MAX_BPF_ADDRS 250 + int numv4 = 0, numv6 = 0; + + if (CountProxyTargets(m, x, &numv4, &numv6) > MAX_BPF_ADDRS) + { + LogMsg("mDNSPlatformUpdateProxyList: ERROR Too many address proxy records v4 %d v6 %d", numv4, numv6); + if (numv4 > MAX_BPF_ADDRS) numv4 = MAX_BPF_ADDRS; + numv6 = MAX_BPF_ADDRS - numv4; + } + + LogSPS("mDNSPlatformUpdateProxyList: fd %d %-7s MAC %.6a %d v4 %d v6", x->BPF_fd, x->ifinfo.ifname, &x->ifinfo.MAC, numv4, numv6); + + // Caution: This is a static structure, so we need to be careful that any modifications we make to it + // are done in such a way that they work correctly when mDNSPlatformUpdateProxyList is called multiple times + static struct bpf_insn filter[17 + MAX_BPF_ADDRS] = + { + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12), // 0 Read Ethertype (bytes 12,13) + + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0806, 0, 1), // 1 If Ethertype == ARP goto next, else 3 + BPF_STMT(BPF_RET + BPF_K, 42), // 2 Return 42-byte ARP + + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0800, 4, 0), // 3 If Ethertype == IPv4 goto 8 (IPv4 address list check) else next + + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x86DD, 0, 9), // 4 If Ethertype == IPv6 goto next, else exit + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20), // 5 Read Protocol and Hop Limit (bytes 20,21) + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x3AFF, 0, 9), // 6 If (Prot,TTL) == (3A,FF) goto next, else IPv6 address list check + BPF_STMT(BPF_RET + BPF_K, 86), // 7 Return 86-byte ND + + // Is IPv4 packet; check if it's addressed to any IPv4 address we're proxying for + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 30), // 8 Read IPv4 Dst (bytes 30,31,32,33) + }; + + struct bpf_insn *pc = &filter[9]; + struct bpf_insn *chk6 = pc + numv4 + 1; // numv4 address checks, plus a "return 0" + struct bpf_insn *fail = chk6 + 1 + numv6; // Get v6 Dst LSW, plus numv6 address checks + struct bpf_insn *ret4 = fail + 1; + struct bpf_insn *ret6 = ret4 + 4; + + static const struct bpf_insn rf = BPF_STMT(BPF_RET + BPF_K, 0); // No match: Return nothing + + static const struct bpf_insn g6 = BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 50); // Read IPv6 Dst LSW (bytes 50,51,52,53) + + static const struct bpf_insn r4a = BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14); // Get IP Header length (normally 20) + static const struct bpf_insn r4b = BPF_STMT(BPF_LD + BPF_IMM, 54); // A = 54 (14-byte Ethernet plus 20-byte TCP + 20 bytes spare) + static const struct bpf_insn r4c = BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0); // A += IP Header length + static const struct bpf_insn r4d = BPF_STMT(BPF_RET + BPF_A, 0); // Success: Return Ethernet + IP + TCP + 20 bytes spare (normally 74) + + static const struct bpf_insn r6a = BPF_STMT(BPF_RET + BPF_K, 94); // Success: Return Eth + IPv6 + TCP + 20 bytes spare + + BPF_SetOffset(&filter[4], jf, fail); // If Ethertype not ARP, IPv4, or IPv6, fail + BPF_SetOffset(&filter[6], jf, chk6); // If IPv6 but not ICMPv6, go to IPv6 address list check + + // BPF Byte-Order Note + // The BPF API designers apparently thought that programmers would not be smart enough to use htons + // and htonl correctly to convert numeric values to network byte order on little-endian machines, + // so instead they chose to make the API implicitly byte-swap *ALL* values, even literal byte strings + // that shouldn't be byte-swapped, like ASCII text, Ethernet addresses, IP addresses, etc. + // As a result, if we put Ethernet addresses and IP addresses in the right byte order, the BPF API + // will byte-swap and make them backwards, and then our filter won't work. So, we have to arrange + // that on little-endian machines we deliberately put addresses in memory with the bytes backwards, + // so that when the BPF API goes through and swaps them all, they end up back as they should be. + // In summary, if we byte-swap all the non-numeric fields that shouldn't be swapped, and we *don't* + // swap any of the numeric values that *should* be byte-swapped, then the filter will work correctly. + + // IPSEC capture size notes: + // 8 bytes UDP header + // 4 bytes Non-ESP Marker + // 28 bytes IKE Header + // -- + // 40 Total. Capturing TCP Header + 20 gets us enough bytes to receive the IKE Header in a UDP-encapsulated IKE packet. + + AuthRecord *rr; + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.InterfaceID == InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv4) + { + mDNSv4Addr a = rr->AddressProxy.ip.v4; + pc->code = BPF_JMP + BPF_JEQ + BPF_K; + BPF_SetOffset(pc, jt, ret4); + pc->jf = 0; + pc->k = (bpf_u_int32)a.b[0] << 24 | (bpf_u_int32)a.b[1] << 16 | (bpf_u_int32)a.b[2] << 8 | (bpf_u_int32)a.b[3]; + pc++; + } + *pc++ = rf; + + if (pc != chk6) LogMsg("mDNSPlatformUpdateProxyList: pc %p != chk6 %p", pc, chk6); + *pc++ = g6; // chk6 points here + + // First cancel any previous ND group memberships we had, then create a fresh socket + if (x->BPF_mcfd >= 0) close(x->BPF_mcfd); + x->BPF_mcfd = socket(AF_INET6, SOCK_DGRAM, 0); + + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.InterfaceID == InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv6) + { + const mDNSv6Addr *const a = &rr->AddressProxy.ip.v6; + pc->code = BPF_JMP + BPF_JEQ + BPF_K; + BPF_SetOffset(pc, jt, ret6); + pc->jf = 0; + pc->k = (bpf_u_int32)a->b[0x0C] << 24 | (bpf_u_int32)a->b[0x0D] << 16 | (bpf_u_int32)a->b[0x0E] << 8 | (bpf_u_int32)a->b[0x0F]; + pc++; + + struct ipv6_mreq i6mr; + i6mr.ipv6mr_interface = x->scope_id; + i6mr.ipv6mr_multiaddr = *(const struct in6_addr*)&NDP_prefix; + i6mr.ipv6mr_multiaddr.s6_addr[0xD] = a->b[0xD]; + i6mr.ipv6mr_multiaddr.s6_addr[0xE] = a->b[0xE]; + i6mr.ipv6mr_multiaddr.s6_addr[0xF] = a->b[0xF]; + + // Do precautionary IPV6_LEAVE_GROUP first, necessary to clear stale kernel state + mStatus err = setsockopt(x->BPF_mcfd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &i6mr, sizeof(i6mr)); + if (err < 0 && (errno != EADDRNOTAVAIL)) + LogMsg("mDNSPlatformUpdateProxyList: IPV6_LEAVE_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); + + err = setsockopt(x->BPF_mcfd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &i6mr, sizeof(i6mr)); + if (err < 0 && (errno != EADDRINUSE)) // Joining same group twice can give "Address already in use" error -- no need to report that + LogMsg("mDNSPlatformUpdateProxyList: IPV6_JOIN_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); + + LogSPS("Joined IPv6 ND multicast group %.16a for %.16a", &i6mr.ipv6mr_multiaddr, a); + } + + if (pc != fail) LogMsg("mDNSPlatformUpdateProxyList: pc %p != fail %p", pc, fail); + *pc++ = rf; // fail points here + + if (pc != ret4) LogMsg("mDNSPlatformUpdateProxyList: pc %p != ret4 %p", pc, ret4); + *pc++ = r4a; // ret4 points here + *pc++ = r4b; + *pc++ = r4c; + *pc++ = r4d; + + if (pc != ret6) LogMsg("mDNSPlatformUpdateProxyList: pc %p != ret6 %p", pc, ret6); + *pc++ = r6a; // ret6 points here + + struct bpf_program prog = { pc - filter, filter }; #if 0 - // For debugging BPF filter program - unsigned int q; - for (q=0; qtimenow == 0) LogMsg("mDNSPlatformUpdateProxyList: m->timenow == 0"); - // Schedule check to see if we can close this BPF_fd now - if (!m->p->NetworkChanged) m->p->NetworkChanged = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2); - // prog.bf_len = 0; This seems to panic the kernel - if (x->BPF_fd < 0) return; // If we've already closed our BPF_fd, no need to generate an error message below - } - - if (ioctl(x->BPF_fd, BIOCSETF, &prog) < 0) LogMsg("mDNSPlatformUpdateProxyList: BIOCSETF(%d) failed %d (%s)", prog.bf_len, errno, strerror(errno)); - else LogSPS("mDNSPlatformUpdateProxyList: BIOCSETF(%d) successful", prog.bf_len); - } + if (!numv4 && !numv6) + { + LogSPS("mDNSPlatformUpdateProxyList: No need for filter"); + if (m->timenow == 0) LogMsg("mDNSPlatformUpdateProxyList: m->timenow == 0"); + // Schedule check to see if we can close this BPF_fd now + if (!m->p->NetworkChanged) m->p->NetworkChanged = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2); + // prog.bf_len = 0; This seems to panic the kernel + if (x->BPF_fd < 0) return; // If we've already closed our BPF_fd, no need to generate an error message below + } + + if (ioctl(x->BPF_fd, BIOCSETF, &prog) < 0) LogMsg("mDNSPlatformUpdateProxyList: BIOCSETF(%d) failed %d (%s)", prog.bf_len, errno, strerror(errno)); + else LogSPS("mDNSPlatformUpdateProxyList: BIOCSETF(%d) successful", prog.bf_len); +} mDNSexport void mDNSPlatformReceiveBPF_fd(mDNS *const m, int fd) - { - mDNS_Lock(m); - - NetworkInterfaceInfoOSX *i; - for (i = m->p->InterfaceList; i; i = i->next) if (i->BPF_fd == -2) break; - if (!i) { LogSPS("mDNSPlatformReceiveBPF_fd: No Interfaces awaiting BPF fd %d; closing", fd); close(fd); } - else - { - LogSPS("%s using BPF fd %d", i->ifinfo.ifname, fd); - - struct bpf_version v; - if (ioctl(fd, BIOCVERSION, &v) < 0) - LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCVERSION failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); - else if (BPF_MAJOR_VERSION != v.bv_major || BPF_MINOR_VERSION != v.bv_minor) - LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCVERSION header %d.%d kernel %d.%d", - fd, i->ifinfo.ifname, BPF_MAJOR_VERSION, BPF_MINOR_VERSION, v.bv_major, v.bv_minor); - - if (ioctl(fd, BIOCGBLEN, &i->BPF_len) < 0) - LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCGBLEN failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); - - if (i->BPF_len > sizeof(m->imsg)) - { - i->BPF_len = sizeof(m->imsg); - if (ioctl(fd, BIOCSBLEN, &i->BPF_len) < 0) - LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSBLEN failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); - else - LogSPS("mDNSPlatformReceiveBPF_fd: %d %s BIOCSBLEN %d", fd, i->ifinfo.ifname, i->BPF_len); - } - - static const u_int opt_one = 1; - if (ioctl(fd, BIOCIMMEDIATE, &opt_one) < 0) - LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCIMMEDIATE failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); - - //if (ioctl(fd, BIOCPROMISC, &opt_one) < 0) - // LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCPROMISC failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); - - //if (ioctl(fd, BIOCSHDRCMPLT, &opt_one) < 0) - // LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSHDRCMPLT failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); - - struct ifreq ifr; - mDNSPlatformMemZero(&ifr, sizeof(ifr)); - strlcpy(ifr.ifr_name, i->ifinfo.ifname, sizeof(ifr.ifr_name)); - if (ioctl(fd, BIOCSETIF, &ifr) < 0) - { LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSETIF failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); i->BPF_fd = -3; } - else - { +{ + mDNS_Lock(m); + + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) if (i->BPF_fd == -2) break; + if (!i) { LogSPS("mDNSPlatformReceiveBPF_fd: No Interfaces awaiting BPF fd %d; closing", fd); close(fd); } + else + { + LogSPS("%s using BPF fd %d", i->ifinfo.ifname, fd); + + struct bpf_version v; + if (ioctl(fd, BIOCVERSION, &v) < 0) + LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCVERSION failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); + else if (BPF_MAJOR_VERSION != v.bv_major || BPF_MINOR_VERSION != v.bv_minor) + LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCVERSION header %d.%d kernel %d.%d", + fd, i->ifinfo.ifname, BPF_MAJOR_VERSION, BPF_MINOR_VERSION, v.bv_major, v.bv_minor); + + if (ioctl(fd, BIOCGBLEN, &i->BPF_len) < 0) + LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCGBLEN failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); + + if (i->BPF_len > sizeof(m->imsg)) + { + i->BPF_len = sizeof(m->imsg); + if (ioctl(fd, BIOCSBLEN, &i->BPF_len) < 0) + LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSBLEN failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); + else + LogSPS("mDNSPlatformReceiveBPF_fd: %d %s BIOCSBLEN %d", fd, i->ifinfo.ifname, i->BPF_len); + } + + static const u_int opt_one = 1; + if (ioctl(fd, BIOCIMMEDIATE, &opt_one) < 0) + LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCIMMEDIATE failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); + + //if (ioctl(fd, BIOCPROMISC, &opt_one) < 0) + // LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCPROMISC failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); + + //if (ioctl(fd, BIOCSHDRCMPLT, &opt_one) < 0) + // LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSHDRCMPLT failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); + + /* + * make socket non blocking see comments in bpf_callback_common for more info + */ + if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) < 0) // set non-blocking + { + LogMsg("mDNSPlatformReceiveBPF_fd: %d %s O_NONBLOCK failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); + } + + struct ifreq ifr; + mDNSPlatformMemZero(&ifr, sizeof(ifr)); + strlcpy(ifr.ifr_name, i->ifinfo.ifname, sizeof(ifr.ifr_name)); + if (ioctl(fd, BIOCSETIF, &ifr) < 0) + { LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSETIF failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); i->BPF_fd = -3; } + else + { #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM - i->BPF_fd = fd; - i->BPF_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, dispatch_get_main_queue()); - if (!i->BPF_source) {LogMsg("mDNSPlatformReceiveBPF_fd: dispatch source create failed");return;} - dispatch_source_set_event_handler(i->BPF_source, ^{bpf_callback_dispatch(i);}); - dispatch_source_set_cancel_handler(i->BPF_source, ^{close(fd);}); - dispatch_resume(i->BPF_source); + i->BPF_fd = fd; + i->BPF_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, dispatch_get_main_queue()); + if (!i->BPF_source) {LogMsg("mDNSPlatformReceiveBPF_fd: dispatch source create failed"); return;} + dispatch_source_set_event_handler(i->BPF_source, ^{bpf_callback_dispatch(i);}); + dispatch_source_set_cancel_handler(i->BPF_source, ^{close(fd);}); + dispatch_resume(i->BPF_source); #else - CFSocketContext myCFSocketContext = { 0, i, NULL, NULL, NULL }; - i->BPF_fd = fd; - i->BPF_cfs = CFSocketCreateWithNative(kCFAllocatorDefault, fd, kCFSocketReadCallBack, bpf_callback, &myCFSocketContext); - i->BPF_rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, i->BPF_cfs, 0); - CFRunLoopAddSource(i->m->p->CFRunLoop, i->BPF_rls, kCFRunLoopDefaultMode); + CFSocketContext myCFSocketContext = { 0, i, NULL, NULL, NULL }; + i->BPF_fd = fd; + i->BPF_cfs = CFSocketCreateWithNative(kCFAllocatorDefault, fd, kCFSocketReadCallBack, bpf_callback, &myCFSocketContext); + i->BPF_rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, i->BPF_cfs, 0); + CFRunLoopAddSource(i->m->p->CFRunLoop, i->BPF_rls, kCFRunLoopDefaultMode); #endif - mDNSPlatformUpdateProxyList(m, i->ifinfo.InterfaceID); - } - } + mDNSPlatformUpdateProxyList(m, i->ifinfo.InterfaceID); + } + } - mDNS_Unlock(m); - } + mDNS_Unlock(m); +} #endif // APPLE_OSX_mDNSResponder @@ -2720,211 +2985,211 @@ mDNSexport void mDNSPlatformReceiveBPF_fd(mDNS *const m, int fd) #ifndef NO_SECURITYFRAMEWORK mDNSlocal CFArrayRef GetCertChain(SecIdentityRef identity) - { - CFMutableArrayRef certChain = NULL; - if (!identity) { LogMsg("getCertChain: identity is NULL"); return(NULL); } - SecCertificateRef cert; - OSStatus err = SecIdentityCopyCertificate(identity, &cert); - if (err || !cert) LogMsg("getCertChain: SecIdentityCopyCertificate() returned %d", (int) err); - else - { - SecPolicySearchRef searchRef; - err = SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_X509_BASIC, NULL, &searchRef); - if (err || !searchRef) LogMsg("getCertChain: SecPolicySearchCreate() returned %d", (int) err); - else - { - SecPolicyRef policy; - err = SecPolicySearchCopyNext(searchRef, &policy); - if (err || !policy) LogMsg("getCertChain: SecPolicySearchCopyNext() returned %d", (int) err); - else - { - CFArrayRef wrappedCert = CFArrayCreate(NULL, (const void**) &cert, 1, &kCFTypeArrayCallBacks); - if (!wrappedCert) LogMsg("getCertChain: wrappedCert is NULL"); - else - { - SecTrustRef trust; - err = SecTrustCreateWithCertificates(wrappedCert, policy, &trust); - if (err || !trust) LogMsg("getCertChain: SecTrustCreateWithCertificates() returned %d", (int) err); - else - { - err = SecTrustEvaluate(trust, NULL); - if (err) LogMsg("getCertChain: SecTrustEvaluate() returned %d", (int) err); - else - { - CFArrayRef rawCertChain; - CSSM_TP_APPLE_EVIDENCE_INFO *statusChain = NULL; - err = SecTrustGetResult(trust, NULL, &rawCertChain, &statusChain); - if (err || !rawCertChain || !statusChain) LogMsg("getCertChain: SecTrustGetResult() returned %d", (int) err); - else - { - certChain = CFArrayCreateMutableCopy(NULL, 0, rawCertChain); - if (!certChain) LogMsg("getCertChain: certChain is NULL"); - else - { - // Replace the SecCertificateRef at certChain[0] with a SecIdentityRef per documentation for SSLSetCertificate: - // - CFArraySetValueAtIndex(certChain, 0, identity); - // Remove root from cert chain, but keep any and all intermediate certificates that have been signed by the root certificate - if (CFArrayGetCount(certChain) > 1) CFArrayRemoveValueAtIndex(certChain, CFArrayGetCount(certChain) - 1); - } - CFRelease(rawCertChain); - // Do not free statusChain: - // says: - // certChain: Call the CFRelease function to release this object when you are finished with it. - // statusChain: Do not attempt to free this pointer; it remains valid until the trust management object is released... - } - } - CFRelease(trust); - } - CFRelease(wrappedCert); - } - CFRelease(policy); - } - CFRelease(searchRef); - } - CFRelease(cert); - } - return certChain; - } +{ + CFMutableArrayRef certChain = NULL; + if (!identity) { LogMsg("getCertChain: identity is NULL"); return(NULL); } + SecCertificateRef cert; + OSStatus err = SecIdentityCopyCertificate(identity, &cert); + if (err || !cert) LogMsg("getCertChain: SecIdentityCopyCertificate() returned %d", (int) err); + else + { + SecPolicySearchRef searchRef; + err = SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_X509_BASIC, NULL, &searchRef); + if (err || !searchRef) LogMsg("getCertChain: SecPolicySearchCreate() returned %d", (int) err); + else + { + SecPolicyRef policy; + err = SecPolicySearchCopyNext(searchRef, &policy); + if (err || !policy) LogMsg("getCertChain: SecPolicySearchCopyNext() returned %d", (int) err); + else + { + CFArrayRef wrappedCert = CFArrayCreate(NULL, (const void**) &cert, 1, &kCFTypeArrayCallBacks); + if (!wrappedCert) LogMsg("getCertChain: wrappedCert is NULL"); + else + { + SecTrustRef trust; + err = SecTrustCreateWithCertificates(wrappedCert, policy, &trust); + if (err || !trust) LogMsg("getCertChain: SecTrustCreateWithCertificates() returned %d", (int) err); + else + { + err = SecTrustEvaluate(trust, NULL); + if (err) LogMsg("getCertChain: SecTrustEvaluate() returned %d", (int) err); + else + { + CFArrayRef rawCertChain; + CSSM_TP_APPLE_EVIDENCE_INFO *statusChain = NULL; + err = SecTrustGetResult(trust, NULL, &rawCertChain, &statusChain); + if (err || !rawCertChain || !statusChain) LogMsg("getCertChain: SecTrustGetResult() returned %d", (int) err); + else + { + certChain = CFArrayCreateMutableCopy(NULL, 0, rawCertChain); + if (!certChain) LogMsg("getCertChain: certChain is NULL"); + else + { + // Replace the SecCertificateRef at certChain[0] with a SecIdentityRef per documentation for SSLSetCertificate: + // + CFArraySetValueAtIndex(certChain, 0, identity); + // Remove root from cert chain, but keep any and all intermediate certificates that have been signed by the root certificate + if (CFArrayGetCount(certChain) > 1) CFArrayRemoveValueAtIndex(certChain, CFArrayGetCount(certChain) - 1); + } + CFRelease(rawCertChain); + // Do not free statusChain: + // says: + // certChain: Call the CFRelease function to release this object when you are finished with it. + // statusChain: Do not attempt to free this pointer; it remains valid until the trust management object is released... + } + } + CFRelease(trust); + } + CFRelease(wrappedCert); + } + CFRelease(policy); + } + CFRelease(searchRef); + } + CFRelease(cert); + } + return certChain; +} #endif /* NO_SECURITYFRAMEWORK */ mDNSexport mStatus mDNSPlatformTLSSetupCerts(void) - { +{ #ifdef NO_SECURITYFRAMEWORK - return mStatus_UnsupportedErr; + return mStatus_UnsupportedErr; #else - SecIdentityRef identity = nil; - SecIdentitySearchRef srchRef = nil; - OSStatus err; + SecIdentityRef identity = nil; + SecIdentitySearchRef srchRef = nil; + OSStatus err; - // search for "any" identity matching specified key use - // In this app, we expect there to be exactly one - err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_DECRYPT, &srchRef); - if (err) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: SecIdentitySearchCreate returned %d", (int) err); return err; } + // search for "any" identity matching specified key use + // In this app, we expect there to be exactly one + err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_DECRYPT, &srchRef); + if (err) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: SecIdentitySearchCreate returned %d", (int) err); return err; } - err = SecIdentitySearchCopyNext(srchRef, &identity); - if (err) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: SecIdentitySearchCopyNext returned %d", (int) err); return err; } + err = SecIdentitySearchCopyNext(srchRef, &identity); + if (err) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: SecIdentitySearchCopyNext returned %d", (int) err); return err; } - if (CFGetTypeID(identity) != SecIdentityGetTypeID()) - { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: SecIdentitySearchCopyNext CFTypeID failure"); return mStatus_UnknownErr; } + if (CFGetTypeID(identity) != SecIdentityGetTypeID()) + { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: SecIdentitySearchCopyNext CFTypeID failure"); return mStatus_UnknownErr; } - // Found one. Call getCertChain to create the correct certificate chain. - ServerCerts = GetCertChain(identity); - if (ServerCerts == nil) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: getCertChain error"); return mStatus_UnknownErr; } + // Found one. Call getCertChain to create the correct certificate chain. + ServerCerts = GetCertChain(identity); + if (ServerCerts == nil) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: getCertChain error"); return mStatus_UnknownErr; } - return mStatus_NoError; + return mStatus_NoError; #endif /* NO_SECURITYFRAMEWORK */ - } +} -mDNSexport void mDNSPlatformTLSTearDownCerts(void) - { +mDNSexport void mDNSPlatformTLSTearDownCerts(void) +{ #ifndef NO_SECURITYFRAMEWORK - if (ServerCerts) { CFRelease(ServerCerts); ServerCerts = NULL; } + if (ServerCerts) { CFRelease(ServerCerts); ServerCerts = NULL; } #endif /* NO_SECURITYFRAMEWORK */ - } +} // This gets the text of the field currently labelled "Computer Name" in the Sharing Prefs Control Panel mDNSlocal void GetUserSpecifiedFriendlyComputerName(domainlabel *const namelabel) - { - CFStringEncoding encoding = kCFStringEncodingUTF8; - CFStringRef cfs = SCDynamicStoreCopyComputerName(NULL, &encoding); - if (cfs) - { - CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); - CFRelease(cfs); - } - } +{ + CFStringEncoding encoding = kCFStringEncodingUTF8; + CFStringRef cfs = SCDynamicStoreCopyComputerName(NULL, &encoding); + if (cfs) + { + CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); + CFRelease(cfs); + } +} // This gets the text of the field currently labelled "Local Hostname" in the Sharing Prefs Control Panel mDNSlocal void GetUserSpecifiedLocalHostName(domainlabel *const namelabel) - { - CFStringRef cfs = SCDynamicStoreCopyLocalHostName(NULL); - if (cfs) - { - CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); - CFRelease(cfs); - } - } +{ + CFStringRef cfs = SCDynamicStoreCopyLocalHostName(NULL); + if (cfs) + { + CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); + CFRelease(cfs); + } +} mDNSexport mDNSBool DictionaryIsEnabled(CFDictionaryRef dict) - { - mDNSs32 val; - CFNumberRef state = (CFNumberRef)CFDictionaryGetValue(dict, CFSTR("Enabled")); - if (!state) return mDNSfalse; - if (!CFNumberGetValue(state, kCFNumberSInt32Type, &val)) - { LogMsg("ERROR: DictionaryIsEnabled - CFNumberGetValue"); return mDNSfalse; } - return val ? mDNStrue : mDNSfalse; - } +{ + mDNSs32 val; + CFNumberRef state = (CFNumberRef)CFDictionaryGetValue(dict, CFSTR("Enabled")); + if (!state) return mDNSfalse; + if (!CFNumberGetValue(state, kCFNumberSInt32Type, &val)) + { LogMsg("ERROR: DictionaryIsEnabled - CFNumberGetValue"); return mDNSfalse; } + return val ? mDNStrue : mDNSfalse; +} 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(mStatus_NoError); - } - - if (sa->sa_family == AF_INET6) - { - struct sockaddr_in6 *ifa_addr = (struct sockaddr_in6 *)sa; - // Inside the BSD kernel they use a hack where they stuff the sin6->sin6_scope_id - // value into the second word of the IPv6 link-local address, so they can just - // pass around IPv6 address structures instead of full sockaddr_in6 structures. - // Those hacked IPv6 addresses aren't supposed to escape the kernel in that form, but they do. - // To work around this we always whack the second word of any IPv6 link-local address back to zero. - if (IN6_IS_ADDR_LINKLOCAL(&ifa_addr->sin6_addr)) ifa_addr->sin6_addr.__u6_addr.__u6_addr16[1] = 0; - ip->type = mDNSAddrType_IPv6; - ip->ip.v6 = *(mDNSv6Addr*)&ifa_addr->sin6_addr; - return(mStatus_NoError); - } - - LogMsg("SetupAddr invalid sa_family %d", sa->sa_family); - return(mStatus_Invalid); - } +{ + 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(mStatus_NoError); + } + + if (sa->sa_family == AF_INET6) + { + struct sockaddr_in6 *ifa_addr = (struct sockaddr_in6 *)sa; + // Inside the BSD kernel they use a hack where they stuff the sin6->sin6_scope_id + // value into the second word of the IPv6 link-local address, so they can just + // pass around IPv6 address structures instead of full sockaddr_in6 structures. + // Those hacked IPv6 addresses aren't supposed to escape the kernel in that form, but they do. + // To work around this we always whack the second word of any IPv6 link-local address back to zero. + if (IN6_IS_ADDR_LINKLOCAL(&ifa_addr->sin6_addr)) ifa_addr->sin6_addr.__u6_addr.__u6_addr16[1] = 0; + ip->type = mDNSAddrType_IPv6; + ip->ip.v6 = *(mDNSv6Addr*)&ifa_addr->sin6_addr; + return(mStatus_NoError); + } + + 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("GetBSSID: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); - else - { - 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); - } +{ + mDNSEthAddr eth = zeroEthAddr; + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetBSSID"), NULL, NULL); + if (!store) + LogMsg("GetBSSID: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); + else + { + 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 int GetMAC(mDNSEthAddr *eth, u_short ifindex) - { - struct ifaddrs *ifa; - for (ifa = myGetIfAddrs(0); ifa; ifa = ifa->ifa_next) - if (ifa->ifa_addr->sa_family == AF_LINK) - { - const struct sockaddr_dl *const sdl = (const struct sockaddr_dl *)ifa->ifa_addr; - if (sdl->sdl_index == ifindex) - { mDNSPlatformMemCopy(eth->b, sdl->sdl_data + sdl->sdl_nlen, 6); return 0; } - } - *eth = zeroEthAddr; - return -1; - } +{ + struct ifaddrs *ifa; + for (ifa = myGetIfAddrs(0); ifa; ifa = ifa->ifa_next) + if (ifa->ifa_addr->sa_family == AF_LINK) + { + const struct sockaddr_dl *const sdl = (const struct sockaddr_dl *)ifa->ifa_addr; + if (sdl->sdl_index == ifindex) + { mDNSPlatformMemCopy(eth->b, sdl->sdl_data + sdl->sdl_nlen, 6); return 0; } + } + *eth = zeroEthAddr; + return -1; +} #ifndef SIOCGIFWAKEFLAGS #define SIOCGIFWAKEFLAGS _IOWR('i', 136, struct ifreq) /* get interface wake property flags */ @@ -2939,140 +3204,173 @@ mDNSlocal int GetMAC(mDNSEthAddr *eth, u_short ifindex) #endif mDNSlocal mDNSBool NetWakeInterface(NetworkInterfaceInfoOSX *i) - { - if (!MulticastInterface(i) ) return(mDNSfalse); // We only use Sleep Proxy Service on multicast-capable interfaces - if (i->ifa_flags & IFF_LOOPBACK) return(mDNSfalse); // except loopback +{ + if (!MulticastInterface(i) ) return(mDNSfalse); // We only use Sleep Proxy Service on multicast-capable interfaces + if (i->ifa_flags & IFF_LOOPBACK) return(mDNSfalse); // except loopback + + int s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { LogMsg("NetWakeInterface socket failed %s error %d errno %d (%s)", i->ifinfo.ifname, s, errno, strerror(errno)); return(mDNSfalse); } - int s = socket(AF_INET, SOCK_DGRAM, 0); - if (s < 0) { LogMsg("NetWakeInterface socket failed %s error %d errno %d (%s)", i->ifinfo.ifname, s, errno, strerror(errno)); return(mDNSfalse); } + struct ifreq ifr; + strlcpy(ifr.ifr_name, i->ifinfo.ifname, sizeof(ifr.ifr_name)); + if (ioctl(s, SIOCGIFWAKEFLAGS, &ifr) < 0) + { + // For some strange reason, in /usr/include/sys/errno.h, EOPNOTSUPP is defined to be + // 102 when compiling kernel code, and 45 when compiling user-level code. Since this + // error code is being returned from the kernel, we need to use the kernel version. + #define KERNEL_EOPNOTSUPP 102 + if (errno != KERNEL_EOPNOTSUPP) // "Operation not supported on socket", the expected result on Leopard and earlier + LogMsg("NetWakeInterface SIOCGIFWAKEFLAGS %s errno %d (%s)", i->ifinfo.ifname, errno, strerror(errno)); + // If on Leopard or earlier, we get EOPNOTSUPP, so in that case + // we enable WOL if this interface is not AirPort and "Wake for Network access" is turned on. + ifr.ifr_wake_flags = (errno == KERNEL_EOPNOTSUPP && !(i)->BSSID.l[0] && i->m->SystemWakeOnLANEnabled) ? IF_WAKE_ON_MAGIC_PACKET : 0; + } + + close(s); + + // ifr.ifr_wake_flags = IF_WAKE_ON_MAGIC_PACKET; // For testing with MacBook Air, using a USB dongle that doesn't actually support Wake-On-LAN + + LogSPS("%-6s %#-14a %s WOMP", i->ifinfo.ifname, &i->ifinfo.ip, (ifr.ifr_wake_flags & IF_WAKE_ON_MAGIC_PACKET) ? "supports" : "no"); - struct ifreq ifr; - strlcpy(ifr.ifr_name, i->ifinfo.ifname, sizeof(ifr.ifr_name)); - if (ioctl(s, SIOCGIFWAKEFLAGS, &ifr) < 0) - { - // For some strange reason, in /usr/include/sys/errno.h, EOPNOTSUPP is defined to be - // 102 when compiling kernel code, and 45 when compiling user-level code. Since this - // error code is being returned from the kernel, we need to use the kernel version. - #define KERNEL_EOPNOTSUPP 102 - if (errno != KERNEL_EOPNOTSUPP) // "Operation not supported on socket", the expected result on Leopard and earlier - LogMsg("NetWakeInterface SIOCGIFWAKEFLAGS %s errno %d (%s)", i->ifinfo.ifname, errno, strerror(errno)); - // If on Leopard or earlier, we get EOPNOTSUPP, so in that case - // we enable WOL if this interface is not AirPort and "Wake for Network access" is turned on. - ifr.ifr_wake_flags = (errno == KERNEL_EOPNOTSUPP && !(i)->BSSID.l[0] && i->m->SystemWakeOnLANEnabled) ? IF_WAKE_ON_MAGIC_PACKET : 0; - } + return((ifr.ifr_wake_flags & IF_WAKE_ON_MAGIC_PACKET) != 0); +} + +mDNSlocal u_int64_t getExtendedFlags(char * ifa_name) +{ + int sockFD; + struct ifreq ifr; - close(s); + sockFD = socket(AF_INET, SOCK_DGRAM, 0); + if (sockFD < 0) + { + LogMsg("getExtendedFlags: socket() call failed, errno = %d (%s)", errno, strerror(errno)); + return 0; + } - // ifr.ifr_wake_flags = IF_WAKE_ON_MAGIC_PACKET; // For testing with MacBook Air, using a USB dongle that doesn't actually support Wake-On-LAN + ifr.ifr_addr.sa_family = AF_INET; + strlcpy(ifr.ifr_name, ifa_name, sizeof(ifr.ifr_name)); - LogSPS("%-6s %#-14a %s WOMP", i->ifinfo.ifname, &i->ifinfo.ip, (ifr.ifr_wake_flags & IF_WAKE_ON_MAGIC_PACKET) ? "supports" : "no"); + if (ioctl(sockFD, SIOCGIFEFLAGS, (caddr_t)&ifr) == -1) + { + LogMsg("getExtendedFlags: SIOCGIFEFLAGS failed, errno = %d (%s)", errno, strerror(errno)); + ifr.ifr_eflags = 0; + } + LogInfo("getExtendedFlags: %s ifr_eflags = 0x%x", ifa_name, ifr.ifr_eflags); - return((ifr.ifr_wake_flags & IF_WAKE_ON_MAGIC_PACKET) != 0); - } + close(sockFD); + return ifr.ifr_eflags; +} // Returns pointer to newly created NetworkInterfaceInfoOSX object, or // pointer to already-existing NetworkInterfaceInfoOSX object found in list, or // may return NULL if out of memory (unlikely) or parameters are invalid for some reason // (e.g. sa_family not AF_INET or AF_INET6) mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifaddrs *ifa, mDNSs32 utc) - { - 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) && - mDNSSameEthAddress(&bssid, &(*p)->BSSID)) - { - debugf("AddInterfaceToList: Found existing interface %lu %.6a with address %#a at %p, ifname before %s, after %s", scope_id, &bssid, &ip, *p, (*p)->ifinfo.ifname, ifa->ifa_name); - // The name should be updated to the new name so that we don't report a wrong name in our SIGINFO output. - // When interfaces are created with same MAC address, kernel resurrects the old interface. - // Even though the interface index is the same (which should be sufficient), when we receive a UDP packet - // we get the corresponding name for the interface index on which the packet was received and check against - // the InterfaceList for a matching name. So, keep the name in sync - strlcpy((*p)->ifinfo.ifname, ifa->ifa_name, sizeof((*p)->ifinfo.ifname)); - (*p)->Exists = mDNStrue; - // If interface was not in getifaddrs list last time we looked, but it is now, update 'AppearanceTime' for this record - if ((*p)->LastSeen != utc) (*p)->AppearanceTime = utc; - - // If Wake-on-LAN capability of this interface has changed (e.g. because power cable on laptop has been disconnected) - // we may need to start or stop or sleep proxy browse operation - const mDNSBool NetWake = NetWakeInterface(*p); - if ((*p)->ifinfo.NetWake != NetWake) - { - (*p)->ifinfo.NetWake = NetWake; - // If this interface is already registered with mDNSCore, then we need to start or stop its NetWake browse on-the-fly. - // If this interface is not already registered (i.e. it's a dormant interface we had in our list - // from when we previously saw it) then we mustn't do that, because mDNSCore doesn't know about it yet. - // In this case, the mDNS_RegisterInterface() call will take care of starting the NetWake browse if necessary. - if ((*p)->Registered) - { - mDNS_Lock(m); - if (NetWake) mDNS_ActivateNetWake_internal (m, &(*p)->ifinfo); - else mDNS_DeactivateNetWake_internal(m, &(*p)->ifinfo); - mDNS_Unlock(m); - } - } - - return(*p); - } - - 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); - mDNSPlatformMemZero(i, sizeof(NetworkInterfaceInfoOSX)); - i->ifinfo.InterfaceID = (mDNSInterfaceID)(uintptr_t)scope_id; - i->ifinfo.ip = ip; - i->ifinfo.mask = mask; - strlcpy(i->ifinfo.ifname, ifa->ifa_name, sizeof(i->ifinfo.ifname)); - i->ifinfo.ifname[sizeof(i->ifinfo.ifname)-1] = 0; - // We can be configured to disable multicast advertisement, but we want to to support - // local-only services, which need a loopback address record. - i->ifinfo.Advertise = m->DivertMulticastAdvertisements ? ((ifa->ifa_flags & IFF_LOOPBACK) ? mDNStrue : mDNSfalse) : m->AdvertiseLocalAddresses; - i->ifinfo.McastTxRx = mDNSfalse; // For now; will be set up later at the end of UpdateInterfaceList - i->ifinfo.Loopback = ((ifa->ifa_flags & IFF_LOOPBACK) != 0) ? mDNStrue : mDNSfalse; - - i->next = mDNSNULL; - i->m = m; - i->Exists = mDNStrue; - i->Flashing = mDNSfalse; - i->Occulting = mDNSfalse; - i->AppearanceTime = utc; // Brand new interface; AppearanceTime is now - i->LastSeen = utc; - i->ifa_flags = ifa->ifa_flags; - i->scope_id = scope_id; - i->BSSID = bssid; - i->sa_family = ifa->ifa_addr->sa_family; - i->BPF_fd = -1; - i->BPF_mcfd = -1; - i->BPF_len = 0; - i->Registered = mDNSNULL; - - // Do this AFTER i->BSSID has been set up - i->ifinfo.NetWake = NetWakeInterface(i); - GetMAC(&i->ifinfo.MAC, scope_id); - if (i->ifinfo.NetWake && !i->ifinfo.MAC.l[0]) - LogMsg("AddInterfaceToList: Bad MAC address %.6a for %d %s %#a", &i->ifinfo.MAC, scope_id, i->ifinfo.ifname, &ip); - - *p = i; - return(i); - } +{ + mDNSu32 scope_id = if_nametoindex(ifa->ifa_name); + mDNSEthAddr bssid = GetBSSID(ifa->ifa_name); + u_int64_t eflags = getExtendedFlags(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) && + mDNSSameEthAddress(&bssid, &(*p)->BSSID)) + { + debugf("AddInterfaceToList: Found existing interface %lu %.6a with address %#a at %p, ifname before %s, after %s", scope_id, &bssid, &ip, *p, (*p)->ifinfo.ifname, ifa->ifa_name); + // The name should be updated to the new name so that we don't report a wrong name in our SIGINFO output. + // When interfaces are created with same MAC address, kernel resurrects the old interface. + // Even though the interface index is the same (which should be sufficient), when we receive a UDP packet + // we get the corresponding name for the interface index on which the packet was received and check against + // the InterfaceList for a matching name. So, keep the name in sync + strlcpy((*p)->ifinfo.ifname, ifa->ifa_name, sizeof((*p)->ifinfo.ifname)); + (*p)->Exists = mDNStrue; + // If interface was not in getifaddrs list last time we looked, but it is now, update 'AppearanceTime' for this record + if ((*p)->LastSeen != utc) (*p)->AppearanceTime = utc; + + // If Wake-on-LAN capability of this interface has changed (e.g. because power cable on laptop has been disconnected) + // we may need to start or stop or sleep proxy browse operation + const mDNSBool NetWake = NetWakeInterface(*p); + if ((*p)->ifinfo.NetWake != NetWake) + { + (*p)->ifinfo.NetWake = NetWake; + // If this interface is already registered with mDNSCore, then we need to start or stop its NetWake browse on-the-fly. + // If this interface is not already registered (i.e. it's a dormant interface we had in our list + // from when we previously saw it) then we mustn't do that, because mDNSCore doesn't know about it yet. + // In this case, the mDNS_RegisterInterface() call will take care of starting the NetWake browse if necessary. + if ((*p)->Registered) + { + mDNS_Lock(m); + if (NetWake) mDNS_ActivateNetWake_internal (m, &(*p)->ifinfo); + else mDNS_DeactivateNetWake_internal(m, &(*p)->ifinfo); + mDNS_Unlock(m); + } + } + + return(*p); + } + + 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); + mDNSPlatformMemZero(i, sizeof(NetworkInterfaceInfoOSX)); + i->ifinfo.InterfaceID = (mDNSInterfaceID)(uintptr_t)scope_id; + i->ifinfo.ip = ip; + i->ifinfo.mask = mask; + strlcpy(i->ifinfo.ifname, ifa->ifa_name, sizeof(i->ifinfo.ifname)); + i->ifinfo.ifname[sizeof(i->ifinfo.ifname)-1] = 0; + // We can be configured to disable multicast advertisement, but we want to to support + // local-only services, which need a loopback address record. + i->ifinfo.Advertise = m->DivertMulticastAdvertisements ? ((ifa->ifa_flags & IFF_LOOPBACK) ? mDNStrue : mDNSfalse) : m->AdvertiseLocalAddresses; + i->ifinfo.McastTxRx = mDNSfalse; // For now; will be set up later at the end of UpdateInterfaceList + i->ifinfo.Loopback = ((ifa->ifa_flags & IFF_LOOPBACK) != 0) ? mDNStrue : mDNSfalse; + + i->next = mDNSNULL; + i->m = m; + i->Exists = mDNStrue; + i->Flashing = mDNSfalse; + i->Occulting = mDNSfalse; + i->D2DInterface = (eflags & IFEF_LOCALNET_PRIVATE) ? mDNStrue: mDNSfalse; + if (eflags & IFEF_AWDL) + { + AWDLInterfaceID = i->ifinfo.InterfaceID; + LogInfo("AddInterfaceToList: AWDLInterfaceID = %d", (int) AWDLInterfaceID); + } + i->AppearanceTime = utc; // Brand new interface; AppearanceTime is now + i->LastSeen = utc; + i->ifa_flags = ifa->ifa_flags; + i->scope_id = scope_id; + i->BSSID = bssid; + i->sa_family = ifa->ifa_addr->sa_family; + i->BPF_fd = -1; + i->BPF_mcfd = -1; + i->BPF_len = 0; + i->Registered = mDNSNULL; + + // Do this AFTER i->BSSID has been set up + i->ifinfo.NetWake = NetWakeInterface(i); + GetMAC(&i->ifinfo.MAC, scope_id); + if (i->ifinfo.NetWake && !i->ifinfo.MAC.l[0]) + LogMsg("AddInterfaceToList: Bad MAC address %.6a for %d %s %#a", &i->ifinfo.MAC, scope_id, i->ifinfo.ifname, &ip); + + *p = i; + return(i); +} #if USE_V6_ONLY_WHEN_NO_ROUTABLE_V4 mDNSlocal NetworkInterfaceInfoOSX *FindRoutableIPv4(mDNS *const m, mDNSu32 scope_id) - { - NetworkInterfaceInfoOSX *i; - for (i = m->p->InterfaceList; i; i = i->next) - if (i->Exists && i->scope_id == scope_id && i->ifinfo.ip.type == mDNSAddrType_IPv4) - if (!mDNSv4AddressIsLinkLocal(&i->ifinfo.ip.ip.v4)) - return(i); - return(mDNSNULL); - } +{ + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) + if (i->Exists && i->scope_id == scope_id && i->ifinfo.ip.type == mDNSAddrType_IPv4) + if (!mDNSv4AddressIsLinkLocal(&i->ifinfo.ip.ip.v4)) + return(i); + return(mDNSNULL); +} #endif #if APPLE_OSX_mDNSResponder @@ -3090,1316 +3388,1088 @@ static DomainAuthInfo* AnonymousRacoonConfig = mDNSNULL; static CFMutableDictionaryRef domainStatusDict = NULL; -// MUST be called with lock held -mDNSlocal void RemoveAutoTunnelDomainStatus(const mDNS *const m, const DomainAuthInfo *const info) - { - char buffer[1024]; - mDNSu32 buflen; - CFStringRef domain; - - LogInfo("RemoveAutoTunnelDomainStatus: %##s", info->domain.c); - - if (!domainStatusDict) { LogMsg("RemoveAutoTunnelDomainStatus: No domainStatusDict"); return; } - - buflen = mDNS_snprintf(buffer, sizeof(buffer), "%##s", info->domain.c); - if (info->AutoTunnel == dnsprefix) buffer[buflen-1] = 0; // Strip the trailing dot for Classic - domain = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); - if (!domain) { LogMsg("RemoveAutoTunnelDomainStatus: Could not create CFString domain"); return; } - - if (CFDictionaryContainsKey(domainStatusDict, domain)) - { - CFDictionaryRemoveValue(domainStatusDict, domain); - if (!m->ShutdownTime) mDNSDynamicStoreSetConfig(kmDNSBackToMyMacConfig, mDNSNULL, domainStatusDict); - } - CFRelease(domain); - } - mDNSlocal mStatus CheckQuestionForStatus(const DNSQuestion *const q) - { - if (q->LongLived) - { - if (q->servAddr.type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsOnes(q->servAddr.ip.v4)) - return mStatus_NoSuchRecord; - else if (q->state == LLQ_Poll) - return mStatus_PollingMode; - else if (q->state != LLQ_Established && !q->DuplicateOf) - return mStatus_TransientErr; - } - - return mStatus_NoError; +{ + if (q->LongLived) + { + if (q->servAddr.type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsOnes(q->servAddr.ip.v4)) + return mStatus_NoSuchRecord; + else if (q->state == LLQ_Poll) + return mStatus_PollingMode; + else if (q->state != LLQ_Established && !q->DuplicateOf) + return mStatus_TransientErr; + } + + return mStatus_NoError; } mDNSlocal mStatus UpdateLLQStatus(const mDNS *const m, char *buffer, int bufsz, const DomainAuthInfo *const info) - { - mStatus status = mStatus_NoError; - DNSQuestion* q, *worst_q = mDNSNULL; - for (q = m->Questions; q; q=q->next) - if (q->AuthInfo == info) - { - mStatus newStatus = CheckQuestionForStatus(q); - if (newStatus == mStatus_NoSuchRecord) { status = newStatus; worst_q = q; break; } - else if (newStatus == mStatus_PollingMode) { status = newStatus; worst_q = q; } - else if (newStatus == mStatus_TransientErr && status == mStatus_NoError) { status = newStatus; worst_q = q; } - } - - if (status == mStatus_NoError) mDNS_snprintf(buffer, bufsz, "Success"); - else if (status == mStatus_NoSuchRecord) mDNS_snprintf(buffer, bufsz, "GetZoneData %s: %##s", worst_q->nta ? "not yet complete" : "failed", worst_q->qname.c); - else if (status == mStatus_PollingMode) mDNS_snprintf(buffer, bufsz, "Query polling %##s", worst_q->qname.c); - else if (status == mStatus_TransientErr) mDNS_snprintf(buffer, bufsz, "Query not yet established %##s", worst_q->qname.c); - return status; - } +{ + mStatus status = mStatus_NoError; + DNSQuestion* q, *worst_q = mDNSNULL; + for (q = m->Questions; q; q=q->next) + if (q->AuthInfo == info) + { + mStatus newStatus = CheckQuestionForStatus(q); + if (newStatus == mStatus_NoSuchRecord) { status = newStatus; worst_q = q; break; } + else if (newStatus == mStatus_PollingMode) { status = newStatus; worst_q = q; } + else if (newStatus == mStatus_TransientErr && status == mStatus_NoError) { status = newStatus; worst_q = q; } + } + + if (status == mStatus_NoError) mDNS_snprintf(buffer, bufsz, "Success"); + else if (status == mStatus_NoSuchRecord) mDNS_snprintf(buffer, bufsz, "GetZoneData %s: %##s", worst_q->nta ? "not yet complete" : "failed", worst_q->qname.c); + else if (status == mStatus_PollingMode) mDNS_snprintf(buffer, bufsz, "Query polling %##s", worst_q->qname.c); + else if (status == mStatus_TransientErr) mDNS_snprintf(buffer, bufsz, "Query not yet established %##s", worst_q->qname.c); + return status; +} mDNSlocal mStatus UpdateRRStatus(const mDNS *const m, char *buffer, int bufsz, const DomainAuthInfo *const info) - { - AuthRecord *r; - - if (info->deltime) return mStatus_NoError; - for (r = m->ResourceRecords; r; r = r->next) - { - // This function is called from UpdateAutoTunnelDomainStatus which in turn may be called from - // a callback e.g., CheckNATMappings. GetAuthInfoFor_internal does not like that (reentrancy being 1), - // hence we inline the code here. We just need the lock to walk the list of AuthInfos which the caller - // has already checked - const domainname *n = r->resrec.name; - while (n->c[0]) - { - DomainAuthInfo *ptr; - for (ptr = m->AuthInfoList; ptr; ptr = ptr->next) - if (SameDomainName(&ptr->domain, n)) - { - if (ptr == info && r->updateError == mStatus_BadSig) - { - mDNS_snprintf(buffer, bufsz, "Resource record update failed for %##s", r->resrec.name); - return r->updateError; - } - } - n = (const domainname *)(n->c + 1 + n->c[0]); - } - } - return mStatus_NoError; - } +{ + AuthRecord *r; + + if (info->deltime) return mStatus_NoError; + for (r = m->ResourceRecords; r; r = r->next) + { + // This function is called from UpdateAutoTunnelDomainStatus which in turn may be called from + // a callback e.g., CheckNATMappings. GetAuthInfoFor_internal does not like that (reentrancy being 1), + // hence we inline the code here. We just need the lock to walk the list of AuthInfos which the caller + // has already checked + const domainname *n = r->resrec.name; + while (n->c[0]) + { + DomainAuthInfo *ptr; + for (ptr = m->AuthInfoList; ptr; ptr = ptr->next) + if (SameDomainName(&ptr->domain, n)) + { + if (ptr == info && (r->updateError == mStatus_BadSig || r->updateError == mStatus_BadKey)) + { + mDNS_snprintf(buffer, bufsz, "Resource record update failed for %##s", r->resrec.name); + return r->updateError; + } + } + n = (const domainname *)(n->c + 1 + n->c[0]); + } + } + return mStatus_NoError; +} #endif // ndef NO_SECURITYFRAMEWORK // MUST be called with lock held mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAuthInfo *const info) - { +{ #ifdef NO_SECURITYFRAMEWORK - (void)m; - (void)info; + (void) m; + (void)info; #else - const NATTraversalInfo *const llq = m->LLQNAT.clientContext ? &m->LLQNAT : mDNSNULL; - const NATTraversalInfo *const tun = info->AutoTunnelNAT.clientContext ? &info->AutoTunnelNAT : mDNSNULL; - char buffer[1024]; - mDNSu32 buflen = 0; - CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFStringRef domain = NULL; - CFStringRef tmp = NULL; - CFNumberRef num = NULL; - mStatus status = mStatus_NoError; - mStatus llqStatus = mStatus_NoError; - char llqBuffer[1024]; - - if (!m->mDNS_busy) LogMsg("UpdateAutoTunnelDomainStatus: ERROR!! Lock not held"); - if (!domainStatusDict) - { - domainStatusDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - if (!domainStatusDict) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFDictionary domainStatusDict"); return; } - } - - if (!dict) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFDictionary dict"); return; } - - buflen = mDNS_snprintf(buffer, sizeof(buffer), "%##s", info->domain.c); - if (info->AutoTunnel == dnsprefix) buffer[buflen-1] = 0; // Strip the trailing dot for Classic - domain = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); - if (!domain) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString domain"); return; } - - mDNS_snprintf(buffer, sizeof(buffer), "%#a", &m->Router); - tmp = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); - if (!tmp) - LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString RouterAddress"); - else - { - CFDictionarySetValue(dict, CFSTR("RouterAddress"), tmp); - CFRelease(tmp); - } - - mDNS_snprintf(buffer, sizeof(buffer), "%.4a", &m->ExternalAddress); - tmp = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); - if (!tmp) - LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString ExternalAddress"); - else - { - CFDictionarySetValue(dict, CFSTR("ExternalAddress"), tmp); - CFRelease(tmp); - } - - if (llq) - { - mDNSu32 port = mDNSVal16(llq->ExternalPort); - - num = CFNumberCreate(NULL, kCFNumberSInt32Type, &port); - if (!num) - LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber LLQExternalPort"); - else - { - CFDictionarySetValue(dict, CFSTR("LLQExternalPort"), num); - CFRelease(num); - } - - if (llq->Result) - { - num = CFNumberCreate(NULL, kCFNumberSInt32Type, &llq->Result); - if (!num) - LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber LLQNPMStatus"); - else - { - CFDictionarySetValue(dict, CFSTR("LLQNPMStatus"), num); - CFRelease(num); - } - } - } - - if (tun) - { - mDNSu32 port = mDNSVal16(tun->ExternalPort); - - num = CFNumberCreate(NULL, kCFNumberSInt32Type, &port); - if (!num) - LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber AutoTunnelExternalPort"); - else - { - CFDictionarySetValue(dict, CFSTR("AutoTunnelExternalPort"), num); - CFRelease(num); - } - - if (tun->Result) - { - num = CFNumberCreate(NULL, kCFNumberSInt32Type, &tun->Result); - if (!num) - LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber AutoTunnelNPMStatus"); - else - { - CFDictionarySetValue(dict, CFSTR("AutoTunnelNPMStatus"), num); - CFRelease(num); - } - } - } - if (tun || llq) - { - mDNSu32 code = m->LastNATMapResultCode; - - num = CFNumberCreate(NULL, kCFNumberSInt32Type, &code); - if (!num) - LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber LastNATMapResultCode"); - else - { - CFDictionarySetValue(dict, CFSTR("LastNATMapResultCode"), num); - CFRelease(num); - } - } - - mDNS_snprintf(buffer, sizeof(buffer), "Success"); - llqStatus = UpdateLLQStatus(m, llqBuffer, sizeof(llqBuffer), info); - status = UpdateRRStatus(m, buffer, sizeof(buffer), info); - - // If we have a bad signature error updating a RR, it overrides any error as it needs to be - // reported so that it can be fixed automatically (or the user needs to be notified) - if (status != mStatus_NoError) - { - LogInfo("UpdateAutoTunnelDomainStatus: RR Status %d, %s", status, buffer); - } - else if (m->Router.type == mDNSAddrType_None) - { - status = mStatus_NoRouter; - mDNS_snprintf(buffer, sizeof(buffer), "No network connection - none"); - } - else if (m->Router.type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsZero(m->Router.ip.v4)) - { - status = mStatus_NoRouter; - mDNS_snprintf(buffer, sizeof(buffer), "No network connection - v4 zero"); - } - else if (mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddrOut)) - { - status = info->AutoTunnel == btmmprefix ? mStatus_ServiceNotRunning : mStatus_PollingMode; - mDNS_snprintf(buffer, sizeof(buffer), "No relay connection"); - } - else if (!llq && !tun) - { - status = mStatus_NotInitializedErr; - mDNS_snprintf(buffer, sizeof(buffer), "Neither LLQ nor AutoTunnel NAT port mapping is currently active"); - } - else if (llqStatus == mStatus_NoSuchRecord) - { - status = llqStatus; - mDNS_snprintf(buffer, sizeof(buffer), llqBuffer); - } - else if (info->AutoTunnel == btmmprefix && ((llq && llq->Result == mStatus_DoubleNAT) || (tun && tun->Result == mStatus_DoubleNAT))) - { - status = mStatus_DoubleNAT; - mDNS_snprintf(buffer, sizeof(buffer), "Double NAT: Router is reporting an external address"); - } - else if (info->AutoTunnel == btmmprefix && ((llq && llq->Result == mStatus_NATPortMappingDisabled) || (tun && tun->Result == mStatus_NATPortMappingDisabled) || - (m->LastNATMapResultCode == NATErr_Refused && ((llq && !llq->Result && mDNSIPPortIsZero(llq->ExternalPort)) || (tun && !tun->Result && mDNSIPPortIsZero(tun->ExternalPort)))))) - { - status = mStatus_NATPortMappingDisabled; - mDNS_snprintf(buffer, sizeof(buffer), "NAT-PMP is disabled on the router"); - } - else if (info->AutoTunnel == btmmprefix && ((llq && llq->Result) || (tun && tun->Result))) - { - status = mStatus_NATTraversal; - mDNS_snprintf(buffer, sizeof(buffer), "Error obtaining NAT port mapping from router"); - } - else if (info->AutoTunnel == btmmprefix && ((llq && mDNSIPPortIsZero(llq->ExternalPort)) || (tun && mDNSIPPortIsZero(tun->ExternalPort)))) - { - status = mStatus_NATTraversal; - mDNS_snprintf(buffer, sizeof(buffer), "Unable to obtain NAT port mapping from router"); - } - else if (info->AutoTunnel == btmmprefix || llqStatus != mStatus_PollingMode) - { - status = llqStatus; - mDNS_snprintf(buffer, sizeof(buffer), llqBuffer); - LogInfo("UpdateAutoTunnelDomainStatus: LLQ Status %d, %s", status, buffer); - } - else - { - mDNS_snprintf(buffer, sizeof(buffer), "Polling success"); - } - - num = CFNumberCreate(NULL, kCFNumberSInt32Type, &status); - if (!num) - LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber StatusCode"); - else - { - CFDictionarySetValue(dict, CFSTR("StatusCode"), num); - CFRelease(num); - } - - tmp = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); - if (!tmp) - LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString StatusMessage"); - else - { - CFDictionarySetValue(dict, CFSTR("StatusMessage"), tmp); - CFRelease(tmp); - } - - if (!CFDictionaryContainsKey(domainStatusDict, domain) || - !CFEqual(dict, (CFMutableDictionaryRef)CFDictionaryGetValue(domainStatusDict, domain))) - { - CFDictionarySetValue(domainStatusDict, domain, dict); - if (!m->ShutdownTime) - { - static char statusBuf[16]; - mDNS_snprintf(statusBuf, sizeof(statusBuf), "%d", (int)status); - mDNSASLLog((uuid_t *)&m->asl_uuid, "autotunnel.domainstatus", status ? "failure" : "success", statusBuf, ""); - mDNSDynamicStoreSetConfig(kmDNSBackToMyMacConfig, mDNSNULL, domainStatusDict); - } - } - - CFRelease(domain); - CFRelease(dict); - - debugf("UpdateAutoTunnelDomainStatus: %s", buffer); + const NATTraversalInfo *const llq = m->LLQNAT.clientContext ? &m->LLQNAT : mDNSNULL; + const NATTraversalInfo *const tun = m->AutoTunnelNAT.clientContext ? &m->AutoTunnelNAT : mDNSNULL; + char buffer[1024]; + mDNSu32 buflen = 0; + CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFStringRef domain = NULL; + CFStringRef tmp = NULL; + CFNumberRef num = NULL; + mStatus status = mStatus_NoError; + mStatus llqStatus = mStatus_NoError; + char llqBuffer[1024]; + + mDNS_CheckLock(m); + + if (!domainStatusDict) + { + domainStatusDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!domainStatusDict) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFDictionary domainStatusDict"); return; } + } + + if (!dict) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFDictionary dict"); return; } + + buflen = mDNS_snprintf(buffer, sizeof(buffer), "%##s", info->domain.c); + domain = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); + if (!domain) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString domain"); return; } + + if (info->deltime) + { + if (CFDictionaryContainsKey(domainStatusDict, domain)) + { + CFDictionaryRemoveValue(domainStatusDict, domain); + if (!m->ShutdownTime) mDNSDynamicStoreSetConfig(kmDNSBackToMyMacConfig, mDNSNULL, domainStatusDict); + } + CFRelease(domain); + CFRelease(dict); + + return; + } + + mDNS_snprintf(buffer, sizeof(buffer), "%#a", &m->Router); + tmp = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); + if (!tmp) + LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString RouterAddress"); + else + { + CFDictionarySetValue(dict, CFSTR("RouterAddress"), tmp); + CFRelease(tmp); + } + + mDNS_snprintf(buffer, sizeof(buffer), "%.4a", &m->ExternalAddress); + tmp = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); + if (!tmp) + LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString ExternalAddress"); + else + { + CFDictionarySetValue(dict, CFSTR("ExternalAddress"), tmp); + CFRelease(tmp); + } + + if (llq) + { + mDNSu32 port = mDNSVal16(llq->ExternalPort); + + num = CFNumberCreate(NULL, kCFNumberSInt32Type, &port); + if (!num) + LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber LLQExternalPort"); + else + { + CFDictionarySetValue(dict, CFSTR("LLQExternalPort"), num); + CFRelease(num); + } + + if (llq->Result) + { + num = CFNumberCreate(NULL, kCFNumberSInt32Type, &llq->Result); + if (!num) + LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber LLQNPMStatus"); + else + { + CFDictionarySetValue(dict, CFSTR("LLQNPMStatus"), num); + CFRelease(num); + } + } + } + + if (tun) + { + mDNSu32 port = mDNSVal16(tun->ExternalPort); + + num = CFNumberCreate(NULL, kCFNumberSInt32Type, &port); + if (!num) + LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber AutoTunnelExternalPort"); + else + { + CFDictionarySetValue(dict, CFSTR("AutoTunnelExternalPort"), num); + CFRelease(num); + } + + if (tun->Result) + { + num = CFNumberCreate(NULL, kCFNumberSInt32Type, &tun->Result); + if (!num) + LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber AutoTunnelNPMStatus"); + else + { + CFDictionarySetValue(dict, CFSTR("AutoTunnelNPMStatus"), num); + CFRelease(num); + } + } + } + if (tun || llq) + { + mDNSu32 code = m->LastNATMapResultCode; + + num = CFNumberCreate(NULL, kCFNumberSInt32Type, &code); + if (!num) + LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber LastNATMapResultCode"); + else + { + CFDictionarySetValue(dict, CFSTR("LastNATMapResultCode"), num); + CFRelease(num); + } + } + + mDNS_snprintf(buffer, sizeof(buffer), "Success"); + llqStatus = UpdateLLQStatus(m, llqBuffer, sizeof(llqBuffer), info); + status = UpdateRRStatus(m, buffer, sizeof(buffer), info); + + // If we have a bad signature error updating a RR, it overrides any error as it needs to be + // reported so that it can be fixed automatically (or the user needs to be notified) + if (status != mStatus_NoError) + { + LogInfo("UpdateAutoTunnelDomainStatus: RR Status %d, %s", status, buffer); + } + else if (m->Router.type == mDNSAddrType_None) + { + status = mStatus_NoRouter; + mDNS_snprintf(buffer, sizeof(buffer), "No network connection - none"); + } + else if (m->Router.type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsZero(m->Router.ip.v4)) + { + status = mStatus_NoRouter; + mDNS_snprintf(buffer, sizeof(buffer), "No network connection - v4 zero"); + } + else if (mDNSIPv6AddressIsZero(info->AutoTunnelInnerAddress)) + { + status = mStatus_ServiceNotRunning; + mDNS_snprintf(buffer, sizeof(buffer), "No inner address"); + } + else if (!llq && !tun) + { + status = mStatus_NotInitializedErr; + mDNS_snprintf(buffer, sizeof(buffer), "Neither LLQ nor AutoTunnel NAT port mapping is currently active"); + } + else if (llqStatus == mStatus_NoSuchRecord) + { + status = llqStatus; + mDNS_snprintf(buffer, sizeof(buffer), llqBuffer); + } + else if ((llq && llq->Result == mStatus_DoubleNAT) || (tun && tun->Result == mStatus_DoubleNAT)) + { + status = mStatus_DoubleNAT; + mDNS_snprintf(buffer, sizeof(buffer), "Double NAT: Router is reporting an external address"); + } + else if ((llq && llq->Result == mStatus_NATPortMappingDisabled) || + (tun && tun->Result == mStatus_NATPortMappingDisabled) || + (m->LastNATMapResultCode == NATErr_Refused && ((llq && !llq->Result && mDNSIPPortIsZero(llq->ExternalPort)) || (tun && !tun->Result && mDNSIPPortIsZero(tun->ExternalPort))))) + { + status = mStatus_NATPortMappingDisabled; + mDNS_snprintf(buffer, sizeof(buffer), "NAT-PMP is disabled on the router"); + } + else if ((llq && llq->Result) || (tun && tun->Result)) + { + status = mStatus_NATTraversal; + mDNS_snprintf(buffer, sizeof(buffer), "Error obtaining NAT port mapping from router"); + } + else if ((llq && mDNSIPPortIsZero(llq->ExternalPort)) || (tun && mDNSIPPortIsZero(tun->ExternalPort))) + { + status = mStatus_NATTraversal; + mDNS_snprintf(buffer, sizeof(buffer), "Unable to obtain NAT port mapping from router"); + } + else + { + status = llqStatus; + mDNS_snprintf(buffer, sizeof(buffer), llqBuffer); + LogInfo("UpdateAutoTunnelDomainStatus: LLQ Status %d, %s", status, buffer); + } + + num = CFNumberCreate(NULL, kCFNumberSInt32Type, &status); + if (!num) + LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber StatusCode"); + else + { + CFDictionarySetValue(dict, CFSTR("StatusCode"), num); + CFRelease(num); + } + + tmp = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); + if (!tmp) + LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString StatusMessage"); + else + { + CFDictionarySetValue(dict, CFSTR("StatusMessage"), tmp); + CFRelease(tmp); + } + + if (!CFDictionaryContainsKey(domainStatusDict, domain) || + !CFEqual(dict, (CFMutableDictionaryRef)CFDictionaryGetValue(domainStatusDict, domain))) + { + CFDictionarySetValue(domainStatusDict, domain, dict); + if (!m->ShutdownTime) + { + static char statusBuf[16]; + mDNS_snprintf(statusBuf, sizeof(statusBuf), "%d", (int)status); + mDNSASLLog((uuid_t *)&m->asl_uuid, "autotunnel.domainstatus", status ? "failure" : "success", statusBuf, ""); + mDNSDynamicStoreSetConfig(kmDNSBackToMyMacConfig, mDNSNULL, domainStatusDict); + } + } + + CFRelease(domain); + CFRelease(dict); + + debugf("UpdateAutoTunnelDomainStatus: %s", buffer); #endif // def NO_SECURITYFRAMEWORK - } +} // MUST be called with lock held mDNSexport void UpdateAutoTunnelDomainStatuses(const mDNS *const m) - { +{ #ifdef NO_SECURITYFRAMEWORK - (void)m; + (void) m; #else - if (!m->mDNS_busy) LogMsg("UpdateAutoTunnelDomainStatuses: ERROR!! Lock not held"); - DomainAuthInfo* info; - for (info = m->AuthInfoList; info; info = info->next) - if (info->AutoTunnel && !info->deltime) - UpdateAutoTunnelDomainStatus(m, info); + mDNS_CheckLock(m); + DomainAuthInfo* info; + for (info = m->AuthInfoList; info; info = info->next) + if (info->AutoTunnel && !info->deltime) + UpdateAutoTunnelDomainStatus(m, info); #endif // def NO_SECURITYFRAMEWORK - } - -// MUST be called with lock held -mDNSlocal mDNSBool TunnelServers(mDNS *const m) - { - AuthRecord *r; - for (r = m->ResourceRecords; r; r = r->next) - if (r->resrec.rrtype == kDNSType_SRV) - { - DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, r->resrec.name); - if (AuthInfo && AuthInfo->AutoTunnel && !AuthInfo->deltime) return(mDNStrue); - } - - return(mDNSfalse); - } - -// MUST be called with lock held -mDNSlocal mDNSBool TunnelClients(mDNS *const m) - { - ClientTunnel *p; - for (p = m->TunnelClients; p; p = p->next) - if (p->q.ThisQInterval < 0) - return(mDNStrue); - return(mDNSfalse); - } - -mDNSlocal void UpdateAnonymousRacoonConfig(mDNS *m) // Determine whether we need racoon to accept incoming connections - { - DomainAuthInfo *info; - - for (info = m->AuthInfoList; info; info = info->next) - if (info->AutoTunnel && !info->deltime && (!mDNSIPPortIsZero(info->AutoTunnelNAT.ExternalPort) || !mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddrIn))) - break; - - if (info != AnonymousRacoonConfig) - { - AnonymousRacoonConfig = info; - // Create or revert configuration file, and start (or SIGHUP) Racoon - (void)mDNSConfigureServer(AnonymousRacoonConfig ? kmDNSUp : kmDNSDown, AnonymousRacoonConfig ? AnonymousRacoonConfig->AutoTunnel : mDNSNULL, AnonymousRacoonConfig ? &AnonymousRacoonConfig->domain : mDNSNULL); - } - } - -// Caller should hold the lock. We don't call mDNS_Register (which acquires the lock) in this function because -// sometimes the caller may already be holding the lock e.g., RegisterAutoTunnel6Record and sometimes -// not e.g., RegisterAutoTunnelServiceRecord -mDNSlocal void RegisterAutoTunnelHostRecord(mDNS *m, DomainAuthInfo *info) - { - mStatus err; - mDNSBool NATProblem; - - if (!m->mDNS_busy) LogMsg("RegisterAutoTunnelHostRecord: ERROR!! Lock not held"); - - // We use AutoTunnelNAT.clientContext to infer that SetupLocalAutoTunnelInterface_internal has been - // called at least once with some Services/Records in the domain and hence it is safe to register - // records when this function is called. - if (!info->AutoTunnelNAT.clientContext) { LogInfo("RegisterAutoTunnelHostRecord: No services registered, not registering the record\n"); return; } - - // Are we behind a NAT with no NAT-PMP support or behind a Double NAT ? Double NATs may have - // NAT-PMP support but it still does not provide inbound connectivity. If there is no NAT-PMP - // support, ExternalPort is zero. If we are behind a Double NAT, then the NATResult is non-zero. - - NATProblem = mDNSIPPortIsZero(info->AutoTunnelNAT.ExternalPort) || info->AutoTunnelNAT.Result; - - if (mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddrIn)) - { - // If we don't have a relay address, check to see if we are behind a Double NAT or NAT with no NAT-PMP - // support. - if (NATProblem) - { - LogInfo("RegisterAutoTunnelHostRecord %##s, not registering the Host Record, Neither AutoTunnel6 nor NAT is available, ClientContext %p, ExternalPort %d, NAT Result %d", info->domain.c, info->AutoTunnelNAT.clientContext, mDNSVal16(info->AutoTunnelNAT.ExternalPort), info->AutoTunnelNAT.Result); - return; - } - } - else - { - // Relay address may be non-zero but we might be going to sleep as the utun interface is not removed - // when going to sleep. If we are awake, we don't care about the NATProblem as the relay connnection - // is up. If we are going to sleep, we should not register the host record if we have a NAT problem. - if (m->SleepState != SleepState_Awake && NATProblem) - { - LogInfo("RegisterAutoTunnelHostRecord %##s, not registering the Host Record, Not in awake state(%d), and some NAT Problem, ClientContext %p, ExternalPort %d, NAT Result %d", info->domain.c, m->SleepState, info->AutoTunnelNAT.clientContext, mDNSVal16(info->AutoTunnelNAT.ExternalPort), info->AutoTunnelNAT.Result); - return; - } - } - - // Note: - // - // We use zero Requested port to infer that we should not be calling Register anymore as it might - // be shutdown or the DomainAuthInfo is going away. - // - // We can use a different set of state variables to track the above as the records registered in - // this function is not dependent on NAT traversal info. For the sake of simplicity, we just - // reuse the NAT variables. - - // Set up our address record for the internal tunnel address - // (User-visible user-friendly host name, used as target in AutoTunnel SRV records) - if (!mDNSIPPortIsZero(info->AutoTunnelNAT.RequestedPort) && info->AutoTunnelHostRecord.resrec.RecordType == kDNSRecordTypeUnregistered) - { - info->AutoTunnelHostRecord.namestorage.c[0] = 0; - AppendDomainLabel(&info->AutoTunnelHostRecord.namestorage, &m->hostlabel); - AppendDomainName (&info->AutoTunnelHostRecord.namestorage, &info->domain); - info->AutoTunnelHostRecord.resrec.rdata->u.ipv6 = m->AutoTunnelHostAddr; - info->AutoTunnelHostRecord.resrec.RecordType = kDNSRecordTypeKnownUnique; - - err = mDNS_Register_internal(m, &info->AutoTunnelHostRecord); - - if (err) LogMsg("RegisterAutoTunnelHostRecord error %d registering AutoTunnelHostRecord %##s", err, info->AutoTunnelHostRecord.namestorage.c); - else - { - // Make sure we trigger the registration of all SRV records in regState_NoTarget again - m->NextSRVUpdate = NonZeroTime(m->timenow); - LogInfo("RegisterAutoTunnelHostRecord registering AutoTunnelHostRecord %##s", info->AutoTunnelHostRecord.namestorage.c); - } - } - else LogInfo("RegisterAutoTunnelHostRecord: Not registering Context %p Port %d Type %d", info->AutoTunnelNAT.clientContext, mDNSVal16(info->AutoTunnelNAT.RequestedPort), info->AutoTunnelHostRecord.resrec.RecordType); - } +} -mDNSlocal void DeregisterAutoTunnelHostRecord(mDNS *m, DomainAuthInfo *info) - { - LogInfo("DeregisterAutoTunnelHostRecord %##s", info->domain.c); - - // Don't deregister if we have the AutoTunnel6 or AutoTunnelService records are registered. - // They indicate that BTMM is working - if (info->AutoTunnel6Record.resrec.RecordType > kDNSRecordTypeDeregistering || - info->AutoTunnelService.resrec.RecordType > kDNSRecordTypeDeregistering) - { - LogInfo("DeregisterAutoTunnelHostRecord %##s, not deregistering the Host Record AutoTunnel6 RecordType:%d AutoTunnel RecordType: %d", info->domain.c, - info->AutoTunnel6Record.resrec.RecordType, info->AutoTunnelService.resrec.RecordType); - return; - } - - if (info->AutoTunnelHostRecord.resrec.RecordType > kDNSRecordTypeDeregistering) - { - mStatus err = mDNS_Deregister(m, &info->AutoTunnelHostRecord); - if (err) - { - info->AutoTunnelHostRecord.resrec.RecordType = kDNSRecordTypeUnregistered; - LogMsg("DeregisterAutoTunnelHostRecord error %d deregistering AutoTunnelHostRecord %##s", err, info->AutoTunnelHostRecord.namestorage.c); - } - else LogInfo("DeregisterAutoTunnelHostRecord: Deregistered AutoTunnel Host Record"); - } - else LogInfo("DeregisterAutoTunnelHostRecord: Not deregistering Host Record state:%d", info->AutoTunnelHostRecord.resrec.RecordType); - } - -mDNSlocal void RegisterAutoTunnelServiceRecords(mDNS *m, DomainAuthInfo *info) - { - mStatus err; - - //if (m->mDNS_busy) LogMsg("RegisterAutoTunnelServiceRecords: ERROR!! Lock already held"); - - if (info->AutoTunnelNAT.clientContext && !info->AutoTunnelNAT.Result && !mDNSIPPortIsZero(info->AutoTunnelNAT.ExternalPort) && info->AutoTunnelTarget.resrec.RecordType == kDNSRecordTypeUnregistered) - { - LogInfo("RegisterAutoTunnelServiceRecords %##s (%#s)", info->domain.c, m->hostlabel.c); - - // 1. Set up our address record for the external tunnel address - // (Constructed name, not generally user-visible, used as target in IKE tunnel's SRV record) - info->AutoTunnelTarget.namestorage.c[0] = 0; - AppendDomainLabel(&info->AutoTunnelTarget.namestorage, &m->AutoTunnelLabel); - AppendDomainName (&info->AutoTunnelTarget.namestorage, &info->domain); - info->AutoTunnelTarget.resrec.rdata->u.ipv4 = info->AutoTunnelNAT.ExternalAddress; - info->AutoTunnelTarget.resrec.RecordType = kDNSRecordTypeKnownUnique; - - err = mDNS_Register(m, &info->AutoTunnelTarget); - if (err) LogMsg("RegisterAutoTunnelServiceRecords error %d registering AutoTunnelTarget %##s", err, info->AutoTunnelTarget.namestorage.c); - else LogInfo("RegisterAutoTunnelServiceRecords registering AutoTunnelTarget %##s", info->AutoTunnelTarget.namestorage.c); - - } - - if (info->AutoTunnelNAT.clientContext && !info->AutoTunnelNAT.Result && !mDNSIPPortIsZero(info->AutoTunnelNAT.ExternalPort) && info->AutoTunnelService.resrec.RecordType == kDNSRecordTypeUnregistered) - { - // 2. Set up IKE tunnel's SRV record: "AutoTunnelHostRecord SRV 0 0 port AutoTunnelTarget" - AssignDomainName (&info->AutoTunnelService.namestorage, (const domainname*) "\x0B" "_autotunnel" "\x04" "_udp"); - AppendDomainLabel(&info->AutoTunnelService.namestorage, &m->hostlabel); - AppendDomainName (&info->AutoTunnelService.namestorage, &info->domain); - info->AutoTunnelService.resrec.rdata->u.srv.priority = 0; - info->AutoTunnelService.resrec.rdata->u.srv.weight = 0; - info->AutoTunnelService.resrec.rdata->u.srv.port = info->AutoTunnelNAT.ExternalPort; - AssignDomainName(&info->AutoTunnelService.resrec.rdata->u.srv.target, &info->AutoTunnelTarget.namestorage); - info->AutoTunnelService.resrec.RecordType = kDNSRecordTypeKnownUnique; - err = mDNS_Register(m, &info->AutoTunnelService); - if (err) LogMsg("RegisterAutoTunnelServiceRecords error %d registering AutoTunnelService %##s", err, info->AutoTunnelService.namestorage.c); - else LogInfo("RegisterAutoTunnelServiceRecords registering AutoTunnelService %##s", info->AutoTunnelService.namestorage.c); - - LogInfo("AutoTunnel server listening for connections on %##s[%.4a]:%d:%##s[%.16a]", - info->AutoTunnelTarget.namestorage.c, &m->AdvertisedV4.ip.v4, mDNSVal16(info->AutoTunnelNAT.IntPort), - info->AutoTunnelHostRecord.namestorage.c, &m->AutoTunnelHostAddr); - } - mDNS_Lock(m); - RegisterAutoTunnelHostRecord(m, info); - mDNS_Unlock(m); - } +mDNSlocal void UpdateAnonymousRacoonConfig(mDNS *m) // Determine whether we need racoon to accept incoming connections +{ + DomainAuthInfo *info; -mDNSlocal void DeregisterAutoTunnelServiceRecords(mDNS *m, DomainAuthInfo *info) - { - LogInfo("DeregisterAutoTunnelServiceRecords %##s", info->domain.c); - if (info->AutoTunnelTarget.resrec.RecordType > kDNSRecordTypeDeregistering) - { - mStatus err = mDNS_Deregister(m, &info->AutoTunnelTarget); - if (err) - { - info->AutoTunnelTarget.resrec.RecordType = kDNSRecordTypeUnregistered; - LogMsg("DeregisterAutoTunnelServiceRecords error %d deregistering AutoTunnelTarget %##s", err, info->AutoTunnelTarget.namestorage.c); - } - else LogInfo("DeregisterAutoTunnelServiceRecords: Deregistered AutoTunnel Target Record"); - - } - else LogInfo("DeregisterAutoTunnelServiceRecords: Not deregistering Target record state:%d", info->AutoTunnelService.resrec.RecordType); - - if (info->AutoTunnelService.resrec.RecordType > kDNSRecordTypeDeregistering) - { - mStatus err = mDNS_Deregister(m, &info->AutoTunnelService); - if (err) - { - info->AutoTunnelService.resrec.RecordType = kDNSRecordTypeUnregistered; - LogMsg("DeregisterAutoTunnelServiceRecords error %d deregistering AutoTunnelService %##s", err, info->AutoTunnelService.namestorage.c); - } - else LogInfo("DeregisterAutoTunnelServiceRecords: Deregistered AutoTunnel Service Record"); - - } - else LogInfo("DeregisterAutoTunnelServiceRecords: Not deregistering service records state:%d", info->AutoTunnelService.resrec.RecordType); - - DeregisterAutoTunnelHostRecord(m, info); - } - -// Caller should hold the lock. We don't call mDNS_Register (which acquires the lock) in this function because -// sometimes the caller may already be holding the lock e.g., SetupLocalAutoTunnelInterface_internal and sometimes -// not e.g., AutoTunnelHostNameChanged -mDNSlocal void RegisterAutoTunnelDevInfoRecord(mDNS *m, DomainAuthInfo *info) - { - mStatus err; - - if (!m->mDNS_busy) LogMsg("RegisterAutoTunnelDevInfoRecord: Lock not held"); - // Note: - // a. We use AutoTunnelNAT.clientContext to infer that SetupLocalAutoTunnelInterface_internal has been - // called at least once with some Services/Records in the domain and hence it is safe to register - // records when this function is called. - // - // b. We use zero Requested port to infer that we should not be calling Register anymore as it might - // be shutdown or the DomainAuthInfo is going away. - // - // We can use a different set of state variables to track the above as the records registered in - // this function is not dependent on NAT traversal info. For the sake of simplicity, we just - // reuse the NAT variables. - - // Set up device info record - if (info->AutoTunnelNAT.clientContext && !mDNSIPPortIsZero(info->AutoTunnelNAT.RequestedPort) && info->AutoTunnelDeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered) - { - ConstructServiceName(&info->AutoTunnelDeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &info->domain); - mDNSu8 len = m->HIHardware.c[0] < 255 - 6 ? m->HIHardware.c[0] : 255 - 6; - mDNSPlatformMemCopy(info->AutoTunnelDeviceInfo.resrec.rdata->u.data + 1, "model=", 6); - mDNSPlatformMemCopy(info->AutoTunnelDeviceInfo.resrec.rdata->u.data + 7, m->HIHardware.c + 1, len); - info->AutoTunnelDeviceInfo.resrec.rdata->u.data[0] = 6 + len; // "model=" plus the device string - info->AutoTunnelDeviceInfo.resrec.rdlength = 7 + len; // One extra for the length byte at the start of the string - info->AutoTunnelDeviceInfo.resrec.RecordType = kDNSRecordTypeKnownUnique; - - err = mDNS_Register_internal(m, &info->AutoTunnelDeviceInfo); - if (err) LogMsg("RegisterAutoTunnelDevInfoRecord error %d registering AutoTunnelDeviceInfo %##s", err, info->AutoTunnelDeviceInfo.namestorage.c); - else LogInfo("RegisterAutoTunnelDevInfoRecord registering AutoTunnelDeviceInfo %##s", info->AutoTunnelDeviceInfo.namestorage.c); - } - } + for (info = m->AuthInfoList; info; info = info->next) + if (info->AutoTunnel && !info->deltime && (!mDNSIPPortIsZero(m->AutoTunnelNAT.ExternalPort) || !mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddr))) + break; -#ifndef NO_SECURITYFRAMEWORK -mDNSlocal void DeregisterAutoTunnelDevInfoRecord(mDNS *m, DomainAuthInfo *info) - { - LogInfo("DeregisterAutoTunnelDevInfoRecord %##s", info->domain.c); - - if (info->AutoTunnelDeviceInfo.resrec.RecordType > kDNSRecordTypeDeregistering) - { - mStatus err = mDNS_Deregister(m, &info->AutoTunnelDeviceInfo); - if (err) - { - info->AutoTunnelDeviceInfo.resrec.RecordType = kDNSRecordTypeUnregistered; - LogMsg("DeregisterAutoTunnelDevInfoRecord error %d deregistering AutoTunnelDeviceInfo %##s", err, info->AutoTunnelDeviceInfo.namestorage.c); - } - else LogInfo("DeregisterAutoTunnelDevInfoRecord: Deregistered AutoTunnel Device Info"); - } - else LogInfo("DeregisterAutoTunnelDevInfoRecord: Not deregistering DeviceInfo Record state:%d", info->AutoTunnelDeviceInfo.resrec.RecordType); - } -#endif + if (info != AnonymousRacoonConfig) + { + AnonymousRacoonConfig = info; + // Create or revert configuration file, and start (or SIGHUP) Racoon + (void)mDNSConfigureServer(AnonymousRacoonConfig ? kmDNSUp : kmDNSDown, AnonymousRacoonConfig ? btmmprefix : mDNSNULL, AnonymousRacoonConfig ? &AnonymousRacoonConfig->domain : mDNSNULL); + } +} +mDNSlocal void AutoTunnelRecordCallback(mDNS *const m, AuthRecord *const rr, mStatus result); -// Caller should hold the lock. We don't call mDNS_Register (which acquires the lock) in this function because -// sometimes the caller may already be holding the lock e.g., SetupLocalAutoTunnelInterface_internal and sometimes -// not e.g., AutoTunnelHostNameChanged -mDNSlocal void RegisterAutoTunnel6Record(mDNS *m, DomainAuthInfo *info) - { - mStatus err; - - if (!m->mDNS_busy) LogMsg("RegisterAutoTunnel6Record: ERROR!! Lock not held"); - - // We deregister the AutoTunnel6Record during sleep and come back here (AutoTunnelRecordCallback) to - // register the address if needed. During that time, we might get a network change event which finds - // that the utun interface exists and tries to register the AutoTunnel6Record which should be stopped. - // Also the RelayAddress is reinitialized during that process which in turn causes the AutoTunnelRecordCallback - // to re-register again. To stop these, we check for the SleepState and register only if we are awake. - if (m->SleepState != SleepState_Awake) - { - LogInfo("RegisterAutoTunnel6Record: Not in awake state, SleepState %d", m->SleepState); - return; - } - - // if disabled administratively, don't register - if (!m->RegisterAutoTunnel6 || DisableInboundRelayConnection) - { - LogInfo("RegisterAutoTunnel6Record: registration Disabled RegisterAutoTunnel6 %d, DisableInbound %d", - m->RegisterAutoTunnel6, DisableInboundRelayConnection); - return; - } - // - // If we have a valid Relay address, we need to register it now. When we got a valid address, we may not - // have registered it because it was waiting for at least one service to become active in the BTMM domain. - // During network change event, we might be called multiple times while the "Connectivity" key did not - // change, so check to see if the value has changed. This can also be zero when we are deregistering and - // getting called from the AutoTunnelRecordCallback - - if (mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddrIn)) - { - LogInfo("RegisterAutoTunnel6Record: Relay address is zero, not registering"); - return; - } - - if ((info->AutoTunnel6Record.resrec.RecordType > kDNSRecordTypeDeregistering) && - (mDNSSameIPv6Address(info->AutoTunnel6Record.resrec.rdata->u.ipv6, m->AutoTunnelRelayAddrIn))) - { - LogInfo("RegisterAutoTunnel6Record: Relay address %.16a same, not registering", &m->AutoTunnelRelayAddrIn); - return; - } - - // Note: - // a. We use AutoTunnelNAT.clientContext to infer that SetupLocalAutoTunnelInterface_internal has been - // called at least once with some Services/Records in the domain and hence it is safe to register - // records when this function is called. - // - // b. We use zero Requested port to infer that we should not be calling Register anymore as it might - // be shutdown or the DomainAuthInfo is going away. - // - // We can use a different set of state variables to track the above as the records registered in - // this function is not dependent on NAT traversal info. For the sake of simplicity, we just - // reuse the NAT variables. - - if (info->AutoTunnelNAT.clientContext && !mDNSIPPortIsZero(info->AutoTunnelNAT.RequestedPort) && - info->AutoTunnel6Record.resrec.RecordType == kDNSRecordTypeUnregistered) - { - AssignDomainName (&info->AutoTunnel6Record.namestorage, (const domainname*) "\x0C" "_autotunnel6"); - AppendDomainLabel(&info->AutoTunnel6Record.namestorage, &m->hostlabel); - AppendDomainName (&info->AutoTunnel6Record.namestorage, &info->domain); - info->AutoTunnel6Record.resrec.rdata->u.ipv6 = m->AutoTunnelRelayAddrIn; - info->AutoTunnel6Record.resrec.RecordType = kDNSRecordTypeKnownUnique; - - err = mDNS_Register_internal(m, &info->AutoTunnel6Record); - if (err) LogMsg("RegisterAutoTunnel6Record error %d registering AutoTunnel6 Record %##s", err, info->AutoTunnel6Record.namestorage.c); - else LogInfo("RegisterAutoTunnel6Record registering AutoTunnel6 Record %##s", info->AutoTunnel6Record.namestorage.c); - - LogInfo("AutoTunnel6 server listening for connections on %##s[%.16a] :%##s[%.16a]", - info->AutoTunnel6Record.namestorage.c, &m->AutoTunnelRelayAddrIn, - info->AutoTunnelHostRecord.namestorage.c, &m->AutoTunnelHostAddr); - - } else {LogInfo("RegisterAutoTunnel6Record: client context %p, RequestedPort %d, Address %.16a, record type %d", info->AutoTunnelNAT.clientContext, info->AutoTunnelNAT.RequestedPort, &m->AutoTunnelRelayAddrIn, info->AutoTunnel6Record.resrec.RecordType);} - - RegisterAutoTunnelHostRecord(m, info); - // When the AutoTunnel6 record comes up, we need to kick racoon and update the status. - // If we had a port mapping, we would have done it in RegisterAutoTunnelServiceRecords. - // If we don't have a port mapping, we need to do it here. - UpdateAnonymousRacoonConfig(m); // Determine whether we need racoon to accept incoming connections - UpdateAutoTunnelDomainStatus(m, info); - } +// Caller must hold the lock +mDNSlocal mDNSBool DeregisterAutoTunnelRecord(mDNS *m, DomainAuthInfo *info, AuthRecord* record) +{ + mDNS_CheckLock(m); -mDNSlocal void DeregisterAutoTunnel6Record(mDNS *m, DomainAuthInfo *info) - { - LogInfo("DeregisterAutoTunnel6Record %##s", info->domain.c); - - if (info->AutoTunnel6Record.resrec.RecordType > kDNSRecordTypeDeregistering) - { - mStatus err = mDNS_Deregister(m, &info->AutoTunnel6Record); - if (err) - { - info->AutoTunnel6Record.resrec.RecordType = kDNSRecordTypeUnregistered; - info->AutoTunnel6Record.resrec.rdata->u.ipv6 = zerov6Addr; - LogMsg("DeregisterAutoTunnel6Record error %d deregistering AutoTunnel6Record %##s", err, info->AutoTunnel6Record.namestorage.c); - } - else LogInfo("DeregisterAutoTunnel6Record: Deregistered AutoTunnel6 Record"); - } - else LogInfo("DeregisterAutoTunnel6Record: Not deregistering AuoTunnel6 record state:%d", info->AutoTunnel6Record.resrec.RecordType); - - DeregisterAutoTunnelHostRecord(m, info); - // UpdateAutoTunnelDomainStatus is careful enough not to turn it on if we don't have - // a external port mapping. Otherwise, it will be turned off. - mDNS_Lock(m); - UpdateAutoTunnelDomainStatus(m, info); - mDNS_Unlock(m); - } + LogInfo("DeregisterAutoTunnelRecord %##s %##s", &info->domain.c, record->namestorage.c); -mDNSlocal void AutoTunnelRecordCallback(mDNS *const m, AuthRecord *const rr, mStatus result) - { - DomainAuthInfo *info = (DomainAuthInfo *)rr->RecordContext; - if (result == mStatus_MemFree) - { - LogInfo("AutoTunnelRecordCallback MemFree %s", ARDisplayString(m, rr)); - // Reset the host record namestorage to force high-level PTR/SRV/TXT to deregister - if (rr == &info->AutoTunnelHostRecord) - { - rr->namestorage.c[0] = 0; - m->NextSRVUpdate = NonZeroTime(m->timenow); - LogInfo("AutoTunnelRecordCallback: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow); - } - if (m->ShutdownTime) {LogInfo("AutoTunnelRecordCallback: Shutdown, returning");return;} - if (rr == &info->AutoTunnelHostRecord) - { - LogInfo("AutoTunnelRecordCallback: calling RegisterAutoTunnelHostRecord"); - RegisterAutoTunnelHostRecord(m,info); - } - else if (rr == &info->AutoTunnelDeviceInfo) - { - LogInfo("AutoTunnelRecordCallback: Calling RegisterAutoTunnelDevInfoRecord"); - RegisterAutoTunnelDevInfoRecord(m,info); - } - else if (rr == &info->AutoTunnelService || rr == &info->AutoTunnelTarget) - { - LogInfo("AutoTunnelRecordCallback: Calling RegisterAutoTunnelServiceRecords"); - RegisterAutoTunnelServiceRecords(m,info); - } - else if (rr == &info->AutoTunnel6Record) - { - LogInfo("AutoTunnelRecordCallback: Calling RegisterAutoTunnel6Record"); - info->AutoTunnel6Record.resrec.rdata->u.ipv6 = zerov6Addr; - RegisterAutoTunnel6Record(m,info); - } - } - } + if (record->resrec.RecordType > kDNSRecordTypeDeregistering) + { + mStatus err = mDNS_Deregister_internal(m, record, mDNS_Dereg_normal); + if (err) + { + record->resrec.RecordType = kDNSRecordTypeUnregistered; + LogMsg("DeregisterAutoTunnelRecord error %d deregistering %##s %##s", err, info->domain.c, record->namestorage.c); + return mDNSfalse; + } + else LogInfo("DeregisterAutoTunnelRecord: Deregistered"); + } + else LogInfo("DeregisterAutoTunnelRecord: Not deregistering, state:%d", record->resrec.RecordType); + + return mDNStrue; +} -#ifndef NO_SECURITYFRAMEWORK -mDNSlocal void AutoTunnelDeleteAuthInfoState(mDNS *m, DomainAuthInfo *info) - { - LogInfo("AutoTunnelDeleteAuthInfoState: Cleaning up state releated to Domain AuthInfo %##s", info->domain.c); - - m->NextSRVUpdate = NonZeroTime(m->timenow); - DeregisterAutoTunnelDevInfoRecord(m, info); - DeregisterAutoTunnelServiceRecords(m, info); - DeregisterAutoTunnel6Record(m, info); - UpdateAnonymousRacoonConfig(m); // Determine whether we need racoon to accept incoming connections - UpdateAutoTunnelDomainStatus(m, info); - } -#endif // ndef NO_SECURITYFRAMEWORK +// Caller must hold the lock +mDNSlocal void DeregisterAutoTunnelHostRecord(mDNS *m, DomainAuthInfo *info) +{ + if (!DeregisterAutoTunnelRecord(m, info, &info->AutoTunnelHostRecord)) + { + info->AutoTunnelHostRecord.namestorage.c[0] = 0; + m->NextSRVUpdate = NonZeroTime(m->timenow); + } +} -mDNSlocal void AutoTunnelNATCallback(mDNS *m, NATTraversalInfo *n) - { - DomainAuthInfo *info = (DomainAuthInfo *)n->clientContext; - LogInfo("AutoTunnelNATCallback Result %d %.4a Internal %d External %d %#s.%##s", - n->Result, &n->ExternalAddress, mDNSVal16(n->IntPort), mDNSVal16(n->ExternalPort), m->hostlabel.c, info->domain.c); - - m->NextSRVUpdate = NonZeroTime(m->timenow); - LogInfo("AutoTunnelNATCallback: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow); - - DeregisterAutoTunnelServiceRecords(m, info); - RegisterAutoTunnelServiceRecords(m, info); - - UpdateAnonymousRacoonConfig(m); // Determine whether we need racoon to accept incoming connections - - UpdateAutoTunnelDomainStatus(m, (DomainAuthInfo *)n->clientContext); - } - -mDNSlocal void AbortDeregistration(mDNS *const m, AuthRecord *rr) - { - if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) - { - LogInfo("Aborting deregistration of %s", ARDisplayString(m, rr)); - CompleteDeregistration(m, rr); - } - else if (rr->resrec.RecordType != kDNSRecordTypeUnregistered) - LogMsg("AbortDeregistration ERROR RecordType %02X for %s", ARDisplayString(m, rr)); - } +// Caller must hold the lock +mDNSlocal void UpdateAutoTunnelHostRecord(mDNS *m, DomainAuthInfo *info) +{ + mStatus err; + mDNSBool NATProblem = mDNSIPPortIsZero(m->AutoTunnelNAT.ExternalPort) || m->AutoTunnelNAT.Result; -mDNSlocal void AutoTunnelHostNameChanged(mDNS *m, DomainAuthInfo *info) - { - LogInfo("AutoTunnelHostNameChanged %#s.%##s", m->hostlabel.c, info->domain.c); + mDNS_CheckLock(m); + + if (!info->AutoTunnelServiceStarted || info->deltime || m->ShutdownTime || mDNSIPv6AddressIsZero(info->AutoTunnelInnerAddress) || (m->SleepState != SleepState_Awake && NATProblem)) + { + LogInfo("UpdateAutoTunnelHostRecord: Dereg %##s : AutoTunnelServiceStarted(%d) deltime(%d) address(%.16a) sleepstate(%d)", + info->domain.c, info->AutoTunnelServiceStarted, info->deltime, &info->AutoTunnelInnerAddress, m->SleepState); + DeregisterAutoTunnelHostRecord(m, info); + } + else if (info->AutoTunnelHostRecord.resrec.RecordType == kDNSRecordTypeUnregistered) + { + mDNS_SetupResourceRecord(&info->AutoTunnelHostRecord, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, + kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info); + info->AutoTunnelHostRecord.namestorage.c[0] = 0; + AppendDomainLabel(&info->AutoTunnelHostRecord.namestorage, &m->hostlabel); + AppendDomainName (&info->AutoTunnelHostRecord.namestorage, &info->domain); + info->AutoTunnelHostRecord.resrec.rdata->u.ipv6 = info->AutoTunnelInnerAddress; + info->AutoTunnelHostRecord.resrec.RecordType = kDNSRecordTypeKnownUnique; + + err = mDNS_Register_internal(m, &info->AutoTunnelHostRecord); + if (err) LogMsg("UpdateAutoTunnelHostRecord error %d registering %##s", err, info->AutoTunnelHostRecord.namestorage.c); + else + { + // Make sure we trigger the registration of all SRV records in regState_NoTarget again + m->NextSRVUpdate = NonZeroTime(m->timenow); + LogInfo("UpdateAutoTunnelHostRecord registering %##s", info->AutoTunnelHostRecord.namestorage.c); + } + } + else LogInfo("UpdateAutoTunnelHostRecord: Type %d", info->AutoTunnelHostRecord.resrec.RecordType); +} + +// Caller must hold the lock +mDNSlocal void DeregisterAutoTunnelServiceRecords(mDNS *m, DomainAuthInfo *info) +{ + LogInfo("DeregisterAutoTunnelServiceRecords %##s", info->domain.c); + + DeregisterAutoTunnelRecord(m, info, &info->AutoTunnelTarget); + DeregisterAutoTunnelRecord(m, info, &info->AutoTunnelService); + UpdateAutoTunnelHostRecord(m, info); +} + +// Caller must hold the lock +mDNSlocal void UpdateAutoTunnelServiceRecords(mDNS *m, DomainAuthInfo *info) +{ + mDNS_CheckLock(m); + + if (!info->AutoTunnelServiceStarted || info->deltime || m->ShutdownTime || mDNSIPPortIsZero(m->AutoTunnelNAT.ExternalPort) || m->AutoTunnelNAT.Result) + { + LogInfo("UpdateAutoTunnelServiceRecords: Dereg %##s : AutoTunnelServiceStarted(%d) deltime(%d) ExtPort(%d) NATResult(%d)", info->domain.c, info->AutoTunnelServiceStarted, info->deltime, mDNSVal16(m->AutoTunnelNAT.ExternalPort), m->AutoTunnelNAT.Result); + DeregisterAutoTunnelServiceRecords(m, info); + } + else + { + if (info->AutoTunnelTarget.resrec.RecordType == kDNSRecordTypeUnregistered) + { + // 1. Set up our address record for the external tunnel address + // (Constructed name, not generally user-visible, used as target in IKE tunnel's SRV record) + mDNS_SetupResourceRecord(&info->AutoTunnelTarget, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL, + kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info); + AssignDomainName (&info->AutoTunnelTarget.namestorage, (const domainname*) "\x0B" "_autotunnel"); + AppendDomainLabel(&info->AutoTunnelTarget.namestorage, &m->hostlabel); + AppendDomainName (&info->AutoTunnelTarget.namestorage, &info->domain); + info->AutoTunnelTarget.resrec.rdata->u.ipv4 = m->AutoTunnelNAT.ExternalAddress; + info->AutoTunnelTarget.resrec.RecordType = kDNSRecordTypeKnownUnique; + + mStatus err = mDNS_Register_internal(m, &info->AutoTunnelTarget); + if (err) LogMsg("UpdateAutoTunnelServiceRecords error %d registering %##s", err, info->AutoTunnelTarget.namestorage.c); + else LogInfo("UpdateAutoTunnelServiceRecords registering %##s", info->AutoTunnelTarget.namestorage.c); + } + else LogInfo("UpdateAutoTunnelServiceRecords: NOOP Target state(%d)", info->AutoTunnelTarget.resrec.RecordType); + + if (info->AutoTunnelService.resrec.RecordType == kDNSRecordTypeUnregistered) + { + // 2. Set up IKE tunnel's SRV record: _autotunnel._udp.AutoTunnelHost SRV 0 0 port AutoTunnelTarget + mDNS_SetupResourceRecord(&info->AutoTunnelService, mDNSNULL, mDNSInterface_Any, kDNSType_SRV, kHostNameTTL, + kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info); + AssignDomainName (&info->AutoTunnelService.namestorage, (const domainname*) "\x0B" "_autotunnel" "\x04" "_udp"); + AppendDomainLabel(&info->AutoTunnelService.namestorage, &m->hostlabel); + AppendDomainName (&info->AutoTunnelService.namestorage, &info->domain); + info->AutoTunnelService.resrec.rdata->u.srv.priority = 0; + info->AutoTunnelService.resrec.rdata->u.srv.weight = 0; + info->AutoTunnelService.resrec.rdata->u.srv.port = m->AutoTunnelNAT.ExternalPort; + AssignDomainName(&info->AutoTunnelService.resrec.rdata->u.srv.target, &info->AutoTunnelTarget.namestorage); + info->AutoTunnelService.resrec.RecordType = kDNSRecordTypeKnownUnique; + + mStatus err = mDNS_Register_internal(m, &info->AutoTunnelService); + if (err) LogMsg("UpdateAutoTunnelServiceRecords error %d registering %##s", err, info->AutoTunnelService.namestorage.c); + else LogInfo("UpdateAutoTunnelServiceRecords registering %##s", info->AutoTunnelService.namestorage.c); + } + else LogInfo("UpdateAutoTunnelServiceRecords: NOOP Service state(%d)", info->AutoTunnelService.resrec.RecordType); + + UpdateAutoTunnelHostRecord(m, info); + + LogInfo("AutoTunnel server listening for connections on %##s[%.4a]:%d:%##s[%.16a]", + info->AutoTunnelTarget.namestorage.c, &m->AdvertisedV4.ip.v4, mDNSVal16(m->AutoTunnelNAT.IntPort), + info->AutoTunnelHostRecord.namestorage.c, &info->AutoTunnelInnerAddress); + + } +} + +// Caller must hold the lock +mDNSlocal void DeregisterAutoTunnelDeviceInfoRecord(mDNS *m, DomainAuthInfo *info) +{ + DeregisterAutoTunnelRecord(m, info, &info->AutoTunnelDeviceInfo); +} + +// Caller must hold the lock +mDNSlocal void UpdateAutoTunnelDeviceInfoRecord(mDNS *m, DomainAuthInfo *info) +{ + mDNS_CheckLock(m); + + if (!info->AutoTunnelServiceStarted || info->deltime || m->ShutdownTime) + DeregisterAutoTunnelDeviceInfoRecord(m, info); + else if (info->AutoTunnelDeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered) + { + mDNS_SetupResourceRecord(&info->AutoTunnelDeviceInfo, mDNSNULL, mDNSInterface_Any, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info); + ConstructServiceName(&info->AutoTunnelDeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &info->domain); + mDNSu8 len = m->HIHardware.c[0] < 255 - 6 ? m->HIHardware.c[0] : 255 - 6; + mDNSPlatformMemCopy(info->AutoTunnelDeviceInfo.resrec.rdata->u.data + 1, "model=", 6); + mDNSPlatformMemCopy(info->AutoTunnelDeviceInfo.resrec.rdata->u.data + 7, m->HIHardware.c + 1, len); + info->AutoTunnelDeviceInfo.resrec.rdata->u.data[0] = 6 + len; // "model=" plus the device string + info->AutoTunnelDeviceInfo.resrec.rdlength = 7 + len; // One extra for the length byte at the start of the string + info->AutoTunnelDeviceInfo.resrec.RecordType = kDNSRecordTypeKnownUnique; + + mStatus err = mDNS_Register_internal(m, &info->AutoTunnelDeviceInfo); + if (err) LogMsg("UpdateAutoTunnelDeviceInfoRecord error %d registering %##s", err, info->AutoTunnelDeviceInfo.namestorage.c); + else LogInfo("UpdateAutoTunnelDeviceInfoRecord registering %##s", info->AutoTunnelDeviceInfo.namestorage.c); + } + else + LogInfo("UpdateAutoTunnelDeviceInfoRecord: not in Unregistered state: %d",info->AutoTunnelDeviceInfo.resrec.RecordType); +} + +// Caller must hold the lock +mDNSlocal void DeregisterAutoTunnel6Record(mDNS *m, DomainAuthInfo *info) +{ + LogInfo("DeregisterAutoTunnel6Record %##s", info->domain.c); + + DeregisterAutoTunnelRecord(m, info, &info->AutoTunnel6Record); + UpdateAutoTunnelHostRecord(m, info); + UpdateAutoTunnelDomainStatus(m, info); +} + +// Caller must hold the lock +mDNSlocal void UpdateAutoTunnel6Record(mDNS *m, DomainAuthInfo *info) +{ + mDNS_CheckLock(m); + + if (!info->AutoTunnelServiceStarted || info->deltime || m->ShutdownTime || mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddr) || m->SleepState != SleepState_Awake) + DeregisterAutoTunnel6Record(m, info); + else if (info->AutoTunnel6Record.resrec.RecordType == kDNSRecordTypeUnregistered) + { + mDNS_SetupResourceRecord(&info->AutoTunnel6Record, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, + kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info); + AssignDomainName (&info->AutoTunnel6Record.namestorage, (const domainname*) "\x0C" "_autotunnel6"); + AppendDomainLabel(&info->AutoTunnel6Record.namestorage, &m->hostlabel); + AppendDomainName (&info->AutoTunnel6Record.namestorage, &info->domain); + info->AutoTunnel6Record.resrec.rdata->u.ipv6 = m->AutoTunnelRelayAddr; + info->AutoTunnel6Record.resrec.RecordType = kDNSRecordTypeKnownUnique; + + mStatus err = mDNS_Register_internal(m, &info->AutoTunnel6Record); + if (err) LogMsg("UpdateAutoTunnel6Record error %d registering %##s", err, info->AutoTunnel6Record.namestorage.c); + else LogInfo("UpdateAutoTunnel6Record registering %##s", info->AutoTunnel6Record.namestorage.c); + + UpdateAutoTunnelHostRecord(m, info); + + LogInfo("AutoTunnel6 server listening for connections on %##s[%.16a] :%##s[%.16a]", + info->AutoTunnel6Record.namestorage.c, &m->AutoTunnelRelayAddr, + info->AutoTunnelHostRecord.namestorage.c, &info->AutoTunnelInnerAddress); + + } + else LogInfo("UpdateAutoTunnel6Record NOOP state(%d)",info->AutoTunnel6Record.resrec.RecordType); +} + +mDNSlocal void AutoTunnelRecordCallback(mDNS *const m, AuthRecord *const rr, mStatus result) +{ + DomainAuthInfo *info = (DomainAuthInfo *)rr->RecordContext; + if (result == mStatus_MemFree) + { + LogInfo("AutoTunnelRecordCallback MemFree %s", ARDisplayString(m, rr)); + + mDNS_Lock(m); + + // Reset the host record namestorage to force high-level PTR/SRV/TXT to deregister + if (rr == &info->AutoTunnelHostRecord) + { + rr->namestorage.c[0] = 0; + m->NextSRVUpdate = NonZeroTime(m->timenow); + LogInfo("AutoTunnelRecordCallback: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow); + } + if (m->ShutdownTime) + { + LogInfo("AutoTunnelRecordCallback: Shutdown, returning"); + mDNS_Unlock(m); + return; + } + if (rr == &info->AutoTunnelHostRecord) + { + LogInfo("AutoTunnelRecordCallback: calling UpdateAutoTunnelHostRecord"); + UpdateAutoTunnelHostRecord(m,info); + } + else if (rr == &info->AutoTunnelDeviceInfo) + { + LogInfo("AutoTunnelRecordCallback: Calling UpdateAutoTunnelDeviceInfoRecord"); + UpdateAutoTunnelDeviceInfoRecord(m,info); + } + else if (rr == &info->AutoTunnelService || rr == &info->AutoTunnelTarget) + { + LogInfo("AutoTunnelRecordCallback: Calling UpdateAutoTunnelServiceRecords"); + UpdateAutoTunnelServiceRecords(m,info); + } + else if (rr == &info->AutoTunnel6Record) + { + LogInfo("AutoTunnelRecordCallback: Calling UpdateAutoTunnel6Record"); + UpdateAutoTunnel6Record(m,info); + } + + mDNS_Unlock(m); + } +} + +mDNSlocal void AutoTunnelNATCallback(mDNS *m, NATTraversalInfo *n) +{ + DomainAuthInfo *info; + + LogInfo("AutoTunnelNATCallback Result %d %.4a Internal %d External %d", + n->Result, &n->ExternalAddress, mDNSVal16(n->IntPort), mDNSVal16(n->ExternalPort)); + + mDNS_Lock(m); + + m->NextSRVUpdate = NonZeroTime(m->timenow); + LogInfo("AutoTunnelNATCallback: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow); + + for (info = m->AuthInfoList; info; info = info->next) + if (info->AutoTunnel) + UpdateAutoTunnelServiceRecords(m, info); + + UpdateAnonymousRacoonConfig(m); // Determine whether we need racoon to accept incoming connections + + UpdateAutoTunnelDomainStatuses(m); + + mDNS_Unlock(m); +} + +mDNSlocal void AutoTunnelHostNameChanged(mDNS *m, DomainAuthInfo *info) +{ + LogInfo("AutoTunnelHostNameChanged %#s.%##s", m->hostlabel.c, info->domain.c); + + mDNS_Lock(m); + // We forcibly deregister the records that are based on the hostname. + // When deregistration of each completes, the MemFree callback will make the + // appropriate Update* call to use the new name to reregister. + DeregisterAutoTunnelHostRecord(m, info); + DeregisterAutoTunnelDeviceInfoRecord(m, info); + DeregisterAutoTunnelServiceRecords(m, info); + DeregisterAutoTunnel6Record(m, info); + m->NextSRVUpdate = NonZeroTime(m->timenow); + mDNS_Unlock(m); +} -#ifndef NO_SECURITYFRAMEWORK - DeregisterAutoTunnelDevInfoRecord(m, info); -#endif - DeregisterAutoTunnelServiceRecords(m, info); - DeregisterAutoTunnel6Record(m, info); - RegisterAutoTunnelServiceRecords(m, info); - - mDNS_Lock(m); - RegisterAutoTunnelDevInfoRecord(m, info); - RegisterAutoTunnel6Record(m, info); - m->NextSRVUpdate = NonZeroTime(m->timenow); - mDNS_Unlock(m); - } - -mDNSlocal void SetupLocalAutoTunnel6Records(mDNS *const m, DomainAuthInfo *info) - { - AbortDeregistration(m, &info->AutoTunnelDeviceInfo); - AbortDeregistration(m, &info->AutoTunnel6Record); - - // When the BTMM is turned on/off too quickly, following things happen. - // - // 1. Turning off BTMM triggers deregistration of the DevInfo/AutoTunnel6 etc. records - // 2. While (1) is in progress, the BTMM is turned on - // - // At step (2), mDNS_SetSecretForDomain clears info->deltime indicating that the domain is valid - // while we have not processed the turning off BTMM completely. Hence, we end up calling this - // function to re-register the records. AbortDeregistration above aborts the Deregistration as the - // records are still in Deregistering state and in AutoTunnelRecordCallback we end up registering - // again. So, we have to be careful below to not call mDNS_SetupResourceRecord again which will - // reset the state to Unregistered and registering again will lead to error as it is registered - // and already in the list. Hence, register below only if needed. - - if (info->AutoTunnelDeviceInfo.resrec.RecordType != kDNSRecordTypeUnregistered || - info->AutoTunnel6Record.resrec.RecordType != kDNSRecordTypeUnregistered) - { - LogInfo("SetupLocalAutoTunnel6Records: AutoTunnel Records not in Unregistered state: Device: %d, AutoTunnel6:%d", - info->AutoTunnelDeviceInfo.resrec.RecordType, info->AutoTunnel6Record.resrec.RecordType); - } - - if (info->AutoTunnelDeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered) - { - mDNS_SetupResourceRecord(&info->AutoTunnelDeviceInfo, mDNSNULL, mDNSInterface_Any, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info); - RegisterAutoTunnelDevInfoRecord(m, info); - } - if (info->AutoTunnel6Record.resrec.RecordType == kDNSRecordTypeUnregistered) - { - mDNS_SetupResourceRecord(&info->AutoTunnel6Record, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info); - RegisterAutoTunnel6Record(m, info); - } - - UpdateAnonymousRacoonConfig(m); // Determine whether we need racoon to accept incoming connections - - UpdateAutoTunnelDomainStatus(m, info); - m->NextSRVUpdate = NonZeroTime(m->timenow); - } - -// Before SetupLocalAutoTunnelInterface_internal is called, -// m->AutoTunnelHostAddr.b[0] must be non-zero, and there must be at least one TunnelClient or TunnelServer // Must be called with the lock held -mDNSexport void SetupLocalAutoTunnelInterface_internal(mDNS *const m, mDNSBool servicesStarting) - { - // 1. Configure the local IPv6 ULA BTMM address - if (!m->AutoTunnelHostAddrActive) - { - m->AutoTunnelHostAddrActive = mDNStrue; - LogInfo("SetupLocalAutoTunnelInterface_internal: Setting up AutoTunnel address %.16a", &m->AutoTunnelHostAddr); - (void)mDNSAutoTunnelInterfaceUpDown(kmDNSUp, m->AutoTunnelHostAddr.b); - } - - // 2. If we have at least one server (pending) listening, publish our records - // The services may not be in the list when it is first trying to resolve the target of the SRV record. - // servicesStarting is an indication of that. Use that instead of looking up in the list of Services/Records. - if (servicesStarting || TunnelServers(m)) - { - DomainAuthInfo *info; - for (info = m->AuthInfoList; info; info = info->next) - { - if (info->AutoTunnel && !info->deltime && !info->AutoTunnelNAT.clientContext) - { - // If we just resurrected a DomainAuthInfo that is still deregistering, we need to abort the - // deregistration process before re-using the AuthRecord memory - // - // Note: We don't need the same caution as in SetupLocalAutoTunnel6Records (see the comments there) - // as AutoTunnelRecordCallback (called as a result of AbortDeregistration) will not end up registering - // the records because clientContext is still NULL - - AbortDeregistration(m, &info->AutoTunnelTarget); - AbortDeregistration(m, &info->AutoTunnelService); - AbortDeregistration(m, &info->AutoTunnelHostRecord); - - mDNS_SetupResourceRecord(&info->AutoTunnelTarget, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL, - kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info); - mDNS_SetupResourceRecord(&info->AutoTunnelService, mDNSNULL, mDNSInterface_Any, kDNSType_SRV, kHostNameTTL, - kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info); - mDNS_SetupResourceRecord(&info->AutoTunnelHostRecord, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, - kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info); - - // Try to get a NAT port mapping for the AutoTunnelService - info->AutoTunnelNAT.clientCallback = AutoTunnelNATCallback; - info->AutoTunnelNAT.clientContext = info; - info->AutoTunnelNAT.Protocol = NATOp_MapUDP; - info->AutoTunnelNAT.IntPort = IPSECPort; - info->AutoTunnelNAT.RequestedPort = IPSECPort; - info->AutoTunnelNAT.NATLease = 0; - mStatus err = mDNS_StartNATOperation_internal(m, &info->AutoTunnelNAT); - if (err) LogMsg("SetupLocalAutoTunnelInterface_internal: error %d starting NAT mapping", err); - - // Register the records that can be done without communicating with the NAT - // - // Note: This should be done after we setup the AutoTunnelNAT information - // as some of the fields in that structure are used to infer other information - // e.g., is it okay to register now ? - - SetupLocalAutoTunnel6Records(m, info); - - } - } - } - } +mDNSexport void StartServerTunnel(mDNS *const m, DomainAuthInfo *const info) +{ + if (info->deltime) return; + + if (info->AutoTunnelServiceStarted) + { + // On wake from sleep, this function will be called when determining SRV targets, + // and needs to re-register the host record for the target to be set correctly + UpdateAutoTunnelHostRecord(m, info); + return; + } + + info->AutoTunnelServiceStarted = mDNStrue; + + // Now that we have a service in this domain, we need to try to register the + // AutoTunnel records, because the relay connection & NAT-T may have already been + // started for another domain. If the relay connection is not up or the NAT-T has not + // yet succeeded, the Update* functions are smart enough to not register the records. + // Note: This should be done after we set AutoTunnelServiceStarted, as that variable is used to + // decide whether to register the AutoTunnel records in the calls below. + UpdateAutoTunnelServiceRecords(m, info); + UpdateAutoTunnel6Record(m, info); + UpdateAutoTunnelDeviceInfoRecord(m, info); + UpdateAutoTunnelHostRecord(m, info); + + // If the global AutoTunnel NAT-T is not yet started, start it. + if (!m->AutoTunnelNAT.clientContext) + { + m->AutoTunnelNAT.clientCallback = AutoTunnelNATCallback; + m->AutoTunnelNAT.clientContext = (void*)1; // Means AutoTunnelNAT Traversal is active; + m->AutoTunnelNAT.Protocol = NATOp_MapUDP; + m->AutoTunnelNAT.IntPort = IPSECPort; + m->AutoTunnelNAT.RequestedPort = IPSECPort; + m->AutoTunnelNAT.NATLease = 0; + mStatus err = mDNS_StartNATOperation_internal(m, &m->AutoTunnelNAT); + if (err) LogMsg("StartServerTunnel: error %d starting NAT mapping", err); + } +} mDNSlocal mStatus AutoTunnelSetKeys(ClientTunnel *tun, mDNSBool AddNew) - { - mDNSv6Addr loc_outer6; - mDNSv6Addr rmt_outer6; - - // When we are tunneling over IPv6 Relay address, the port number is zero - if (mDNSIPPortIsZero(tun->rmt_outer_port)) - { - loc_outer6 = tun->loc_outer6; - rmt_outer6 = tun->rmt_outer6; - } - else - { - loc_outer6 = zerov6Addr; - loc_outer6.b[0] = tun->loc_outer.b[0]; - loc_outer6.b[1] = tun->loc_outer.b[1]; - loc_outer6.b[2] = tun->loc_outer.b[2]; - loc_outer6.b[3] = tun->loc_outer.b[3]; - - rmt_outer6 = zerov6Addr; - rmt_outer6.b[0] = tun->rmt_outer.b[0]; - rmt_outer6.b[1] = tun->rmt_outer.b[1]; - rmt_outer6.b[2] = tun->rmt_outer.b[2]; - rmt_outer6.b[3] = tun->rmt_outer.b[3]; - } - - return(mDNSAutoTunnelSetKeys(AddNew ? kmDNSAutoTunnelSetKeysReplace : kmDNSAutoTunnelSetKeysDelete, tun->loc_inner.b, loc_outer6.b, kRacoonPort, tun->rmt_inner.b, rmt_outer6.b, mDNSVal16(tun->rmt_outer_port), tun->prefix, SkipLeadingLabels(&tun->dstname, 1))); - } +{ + mDNSv6Addr loc_outer6; + mDNSv6Addr rmt_outer6; + + // When we are tunneling over IPv6 Relay address, the port number is zero + if (mDNSIPPortIsZero(tun->rmt_outer_port)) + { + loc_outer6 = tun->loc_outer6; + rmt_outer6 = tun->rmt_outer6; + } + else + { + loc_outer6 = zerov6Addr; + loc_outer6.b[0] = tun->loc_outer.b[0]; + loc_outer6.b[1] = tun->loc_outer.b[1]; + loc_outer6.b[2] = tun->loc_outer.b[2]; + loc_outer6.b[3] = tun->loc_outer.b[3]; + + rmt_outer6 = zerov6Addr; + rmt_outer6.b[0] = tun->rmt_outer.b[0]; + rmt_outer6.b[1] = tun->rmt_outer.b[1]; + rmt_outer6.b[2] = tun->rmt_outer.b[2]; + rmt_outer6.b[3] = tun->rmt_outer.b[3]; + } + + return(mDNSAutoTunnelSetKeys(AddNew ? kmDNSAutoTunnelSetKeysReplace : kmDNSAutoTunnelSetKeysDelete, tun->loc_inner.b, loc_outer6.b, kRacoonPort, tun->rmt_inner.b, rmt_outer6.b, mDNSVal16(tun->rmt_outer_port), btmmprefix, SkipLeadingLabels(&tun->dstname, 1))); +} // If the EUI-64 part of the IPv6 ULA matches, then that means the two addresses point to the same machine #define mDNSSameClientTunnel(A,B) ((A)->l[2] == (B)->l[2] && (A)->l[3] == (B)->l[3]) mDNSlocal void ReissueBlockedQuestionWithType(mDNS *const m, domainname *d, mDNSBool success, mDNSu16 qtype) - { - DNSQuestion *q = m->Questions; - while (q) - { - if (q->NoAnswer == NoAnswer_Suspended && q->qtype == qtype && q->AuthInfo && q->AuthInfo->AutoTunnel && SameDomainName(&q->qname, d)) - { - LogInfo("Restart %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - mDNSQuestionCallback *tmp = q->QuestionCallback; - q->QuestionCallback = AutoTunnelCallback; // Set QuestionCallback to suppress another call back to AddNewClientTunnel - mDNS_StopQuery(m, q); - mDNS_StartQuery(m, q); - q->QuestionCallback = tmp; // Restore QuestionCallback back to the real value - if (!success) q->NoAnswer = NoAnswer_Fail; - // When we call mDNS_StopQuery, it's possible for other subordinate questions like the GetZoneData query to be cancelled too. - // In general we have to assume that the question list might have changed in arbitrary ways. - // This code is itself called from a question callback, so the m->CurrentQuestion mechanism is - // already in use. The safest solution is just to go back to the start of the list and start again. - // In principle this sounds like an n^2 algorithm, but in practice we almost always activate - // just one suspended question, so it's really a 2n algorithm. - q = m->Questions; - } - else - q = q->next; - } - } +{ + DNSQuestion *q = m->Questions; + while (q) + { + if (q->NoAnswer == NoAnswer_Suspended && q->qtype == qtype && q->AuthInfo && q->AuthInfo->AutoTunnel && SameDomainName(&q->qname, d)) + { + LogInfo("Restart %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + mDNSQuestionCallback *tmp = q->QuestionCallback; + q->QuestionCallback = AutoTunnelCallback; // Set QuestionCallback to suppress another call back to AddNewClientTunnel + mDNS_StopQuery(m, q); + mDNS_StartQuery(m, q); + q->QuestionCallback = tmp; // Restore QuestionCallback back to the real value + if (!success) q->NoAnswer = NoAnswer_Fail; + // When we call mDNS_StopQuery, it's possible for other subordinate questions like the GetZoneData query to be cancelled too. + // In general we have to assume that the question list might have changed in arbitrary ways. + // This code is itself called from a question callback, so the m->CurrentQuestion mechanism is + // already in use. The safest solution is just to go back to the start of the list and start again. + // In principle this sounds like an n^2 algorithm, but in practice we almost always activate + // just one suspended question, so it's really a 2n algorithm. + q = m->Questions; + } + else + q = q->next; + } +} mDNSlocal void ReissueBlockedQuestions(mDNS *const m, domainname *d, mDNSBool success) - { - // 1. We deliberately restart AAAA queries before A queries, because in the common case where a BTTM host has - // a v6 address but no v4 address, we prefer the caller to get the positive AAAA response before the A NXDOMAIN. - // 2. In the case of AAAA queries, if our tunnel setup failed, then we return a deliberate failure indication to the caller -- - // even if the name does have a valid AAAA record, we don't want clients trying to connect to it without a properly encrypted tunnel. - // 3. For A queries we never fabricate failures -- if a BTTM service is really using raw IPv4, then it doesn't need the IPv6 tunnel. - ReissueBlockedQuestionWithType(m, d, success, kDNSType_AAAA); - ReissueBlockedQuestionWithType(m, d, mDNStrue, kDNSType_A); - } +{ + // 1. We deliberately restart AAAA queries before A queries, because in the common case where a BTTM host has + // a v6 address but no v4 address, we prefer the caller to get the positive AAAA response before the A NXDOMAIN. + // 2. In the case of AAAA queries, if our tunnel setup failed, then we return a deliberate failure indication to the caller -- + // even if the name does have a valid AAAA record, we don't want clients trying to connect to it without a properly encrypted tunnel. + // 3. For A queries we never fabricate failures -- if a BTTM service is really using raw IPv4, then it doesn't need the IPv6 tunnel. + ReissueBlockedQuestionWithType(m, d, success, kDNSType_AAAA); + ReissueBlockedQuestionWithType(m, d, mDNStrue, kDNSType_A); +} mDNSlocal void UnlinkAndReissueBlockedQuestions(mDNS *const m, ClientTunnel *tun, mDNSBool success) - { - ClientTunnel **p = &m->TunnelClients; - while (*p != tun && *p) p = &(*p)->next; - if (*p) *p = tun->next; - ReissueBlockedQuestions(m, &tun->dstname, success); - LogInfo("UnlinkAndReissueBlockedQuestions: Disposing ClientTunnel %p", tun); - freeL("ClientTunnel", tun); - } +{ + ClientTunnel **p = &m->TunnelClients; + while (*p != tun && *p) p = &(*p)->next; + if (*p) *p = tun->next; + ReissueBlockedQuestions(m, &tun->dstname, success); + LogInfo("UnlinkAndReissueBlockedQuestions: Disposing ClientTunnel %p", tun); + freeL("ClientTunnel", tun); +} mDNSlocal mDNSBool TunnelClientDeleteMatching(mDNS *const m, ClientTunnel *tun, mDNSBool v6Tunnel) - { - ClientTunnel **p; - mDNSBool needSetKeys = mDNStrue; - - p = &tun->next; - while (*p) - { - // Is this a tunnel to the same host that we are trying to setup now? - if (!mDNSSameClientTunnel(&(*p)->rmt_inner, &tun->rmt_inner)) p = &(*p)->next; - else - { - ClientTunnel *old = *p; - if (v6Tunnel) - { - if (!mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue; } - LogInfo("TunnelClientDeleteMatching: Found existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); - if (old->q.ThisQInterval >= 0) - { - LogInfo("TunnelClientDeleteMatching: Stopping query on IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); - mDNS_StopQuery(m, &old->q); - } - else if (!mDNSSameIPv6Address((*p)->rmt_inner, tun->rmt_inner) || - !mDNSSameIPv6Address(old->loc_inner, tun->loc_inner) || - !mDNSSameIPv6Address(old->loc_outer6, tun->loc_outer6) || - !mDNSSameIPv6Address(old->rmt_outer6, tun->rmt_outer6)) - { - // Delete the old tunnel if the current tunnel to the same host does not have the same ULA or - // the other parameters of the tunnel are different - LogInfo("TunnelClientDeleteMatching: Deleting existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); - AutoTunnelSetKeys(old, mDNSfalse); - } - else - { - // Reusing the existing tunnel means that we reuse the IPsec SAs and the policies. We delete the old - // as "tun" and "old" are identical - LogInfo("TunnelClientDeleteMatching: Reusing the existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c, - &old->rmt_inner); - needSetKeys = mDNSfalse; - } - } - else - { - if (mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue; } - LogInfo("TunnelClientDeleteMatching: Found existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); - if (old->q.ThisQInterval >= 0) - { - LogInfo("TunnelClientDeleteMatching: Stopping query on IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); - mDNS_StopQuery(m, &old->q); - } - else if (!mDNSSameIPv6Address((*p)->rmt_inner, tun->rmt_inner) || - !mDNSSameIPv6Address(old->loc_inner, tun->loc_inner) || - !mDNSSameIPv4Address(old->loc_outer, tun->loc_outer) || - !mDNSSameIPv4Address(old->rmt_outer, tun->rmt_outer) || - !mDNSSameIPPort(old->rmt_outer_port, tun->rmt_outer_port)) - { - // Delete the old tunnel if the current tunnel to the same host does not have the same ULA or - // the other parameters of the tunnel are different - LogInfo("TunnelClientDeleteMatching: Deleting existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); - AutoTunnelSetKeys(old, mDNSfalse); - } - else - { - // Reusing the existing tunnel means that we reuse the IPsec SAs and the policies. We delete the old - // as "tun" and "old" are identical - LogInfo("TunnelClientDeleteMatching: Reusing the existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c, - &old->rmt_inner); - needSetKeys = mDNSfalse; - } - } - - *p = old->next; - LogInfo("TunnelClientDeleteMatching: Disposing ClientTunnel %p", old); - freeL("ClientTunnel", old); - } - } - return needSetKeys; - } +{ + ClientTunnel **p; + mDNSBool needSetKeys = mDNStrue; + + p = &tun->next; + while (*p) + { + // Is this a tunnel to the same host that we are trying to setup now? + if (!mDNSSameClientTunnel(&(*p)->rmt_inner, &tun->rmt_inner)) p = &(*p)->next; + else + { + ClientTunnel *old = *p; + if (v6Tunnel) + { + if (!mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue; } + LogInfo("TunnelClientDeleteMatching: Found existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); + if (old->q.ThisQInterval >= 0) + { + LogInfo("TunnelClientDeleteMatching: Stopping query on IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); + mDNS_StopQuery(m, &old->q); + } + else if (!mDNSSameIPv6Address((*p)->rmt_inner, tun->rmt_inner) || + !mDNSSameIPv6Address(old->loc_inner, tun->loc_inner) || + !mDNSSameIPv6Address(old->loc_outer6, tun->loc_outer6) || + !mDNSSameIPv6Address(old->rmt_outer6, tun->rmt_outer6)) + { + // Delete the old tunnel if the current tunnel to the same host does not have the same ULA or + // the other parameters of the tunnel are different + LogInfo("TunnelClientDeleteMatching: Deleting existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); + AutoTunnelSetKeys(old, mDNSfalse); + } + else + { + // Reusing the existing tunnel means that we reuse the IPsec SAs and the policies. We delete the old + // as "tun" and "old" are identical + LogInfo("TunnelClientDeleteMatching: Reusing the existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c, + &old->rmt_inner); + needSetKeys = mDNSfalse; + } + } + else + { + if (mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue; } + LogInfo("TunnelClientDeleteMatching: Found existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); + if (old->q.ThisQInterval >= 0) + { + LogInfo("TunnelClientDeleteMatching: Stopping query on IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); + mDNS_StopQuery(m, &old->q); + } + else if (!mDNSSameIPv6Address((*p)->rmt_inner, tun->rmt_inner) || + !mDNSSameIPv6Address(old->loc_inner, tun->loc_inner) || + !mDNSSameIPv4Address(old->loc_outer, tun->loc_outer) || + !mDNSSameIPv4Address(old->rmt_outer, tun->rmt_outer) || + !mDNSSameIPPort(old->rmt_outer_port, tun->rmt_outer_port)) + { + // Delete the old tunnel if the current tunnel to the same host does not have the same ULA or + // the other parameters of the tunnel are different + LogInfo("TunnelClientDeleteMatching: Deleting existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); + AutoTunnelSetKeys(old, mDNSfalse); + } + else + { + // Reusing the existing tunnel means that we reuse the IPsec SAs and the policies. We delete the old + // as "tun" and "old" are identical + LogInfo("TunnelClientDeleteMatching: Reusing the existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c, + &old->rmt_inner); + needSetKeys = mDNSfalse; + } + } + + *p = old->next; + LogInfo("TunnelClientDeleteMatching: Disposing ClientTunnel %p", old); + freeL("ClientTunnel", old); + } + } + return needSetKeys; +} // v6Tunnel indicates whether to delete a tunnel whose outer header is IPv6. If false, outer IPv4 // tunnel will be deleted mDNSlocal void TunnelClientDeleteAny(mDNS *const m, ClientTunnel *tun, mDNSBool v6Tunnel) - { - ClientTunnel **p; - - p = &tun->next; - while (*p) - { - // If there is more than one client tunnel to the same host, delete all of them. - // We do this by just checking against the EUI64 rather than the full address - if (!mDNSSameClientTunnel(&(*p)->rmt_inner, &tun->rmt_inner)) p = &(*p)->next; - else - { - ClientTunnel *old = *p; - if (v6Tunnel) - { - if (!mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue;} - LogInfo("TunnelClientDeleteAny: Found existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); - } - else - { - if (mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue;} - LogInfo("TunnelClientDeleteAny: Found existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); - } - if (old->q.ThisQInterval >= 0) - { - LogInfo("TunnelClientDeleteAny: Stopping query on AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); - mDNS_StopQuery(m, &old->q); - } - else - { - LogInfo("TunnelClientDeleteAny: Deleting existing AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); - AutoTunnelSetKeys(old, mDNSfalse); - } - *p = old->next; - LogInfo("TunnelClientDeleteAny: Disposing ClientTunnel %p", old); - freeL("ClientTunnel", old); - } - } - } +{ + ClientTunnel **p; + + p = &tun->next; + while (*p) + { + // If there is more than one client tunnel to the same host, delete all of them. + // We do this by just checking against the EUI64 rather than the full address + if (!mDNSSameClientTunnel(&(*p)->rmt_inner, &tun->rmt_inner)) p = &(*p)->next; + else + { + ClientTunnel *old = *p; + if (v6Tunnel) + { + if (!mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue;} + LogInfo("TunnelClientDeleteAny: Found existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); + } + else + { + if (mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue;} + LogInfo("TunnelClientDeleteAny: Found existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); + } + if (old->q.ThisQInterval >= 0) + { + LogInfo("TunnelClientDeleteAny: Stopping query on AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); + mDNS_StopQuery(m, &old->q); + } + else + { + LogInfo("TunnelClientDeleteAny: Deleting existing AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); + AutoTunnelSetKeys(old, mDNSfalse); + } + *p = old->next; + LogInfo("TunnelClientDeleteAny: Disposing ClientTunnel %p", old); + freeL("ClientTunnel", old); + } + } +} mDNSlocal void TunnelClientFinish(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer) - { - mDNSBool needSetKeys = mDNStrue; - ClientTunnel *tun = (ClientTunnel *)question->QuestionContext; - mDNSBool v6Tunnel = mDNSfalse; - - // If the port is zero, then we have a relay address of the peer - if (mDNSIPPortIsZero(tun->rmt_outer_port)) - v6Tunnel = mDNStrue; - - if (v6Tunnel) - { - LogInfo("TunnelClientFinish: Relay address %.16a", &answer->rdata->u.ipv6); - tun->rmt_outer6 = answer->rdata->u.ipv6; - tun->loc_outer6 = m->AutoTunnelRelayAddrOut; - } - else - { - LogInfo("TunnelClientFinish: SRV target address %.4a", &answer->rdata->u.ipv4); - tun->rmt_outer = answer->rdata->u.ipv4; - mDNSAddr tmpDst = { mDNSAddrType_IPv4, {{{0}}} }; - tmpDst.ip.v4 = tun->rmt_outer; - mDNSAddr tmpSrc = zeroAddr; - mDNSPlatformSourceAddrForDest(&tmpSrc, &tmpDst); - if (tmpSrc.type == mDNSAddrType_IPv4) tun->loc_outer = tmpSrc.ip.v4; - else tun->loc_outer = m->AdvertisedV4.ip.v4; - } - - question->ThisQInterval = -1; // So we know this tunnel setup has completed - tun->loc_inner = m->AutoTunnelHostAddr; - - // If we found a v6Relay address for our peer, delete all the v4Tunnels for our peer and - // look for existing tunnels to see whether they have the same information for our peer. - // If not, delete them and need to create a new tunnel. If they are same, just use the - // same tunnel. Do the similar thing if we found a v4Tunnel end point for our peer. - TunnelClientDeleteAny(m, tun, !v6Tunnel); - needSetKeys = TunnelClientDeleteMatching(m, tun, v6Tunnel); - - if (needSetKeys) LogInfo("TunnelClientFinish: New %s AutoTunnel for %##s %.16a", (v6Tunnel ? "IPv6" : "IPv4"), tun->dstname.c, &tun->rmt_inner); - else LogInfo("TunnelClientFinish: Reusing exiting %s AutoTunnel for %##s %.16a", (v6Tunnel ? "IPv6" : "IPv4"), tun->dstname.c, &tun->rmt_inner); - - if (m->AutoTunnelHostAddr.b[0]) { mDNS_Lock(m); SetupLocalAutoTunnelInterface_internal(m, mDNSfalse); mDNS_Unlock(m); }; - - mStatus result = needSetKeys ? AutoTunnelSetKeys(tun, mDNStrue) : mStatus_NoError; - static char msgbuf[32]; - mDNS_snprintf(msgbuf, sizeof(msgbuf), "Tunnel setup - %d", result); - mDNSASLLog((uuid_t *)&m->asl_uuid, "autotunnel.config", result ? "failure" : "success", msgbuf, ""); - // Kick off any questions that were held pending this tunnel setup - ReissueBlockedQuestions(m, &tun->dstname, (result == mStatus_NoError) ? mDNStrue : mDNSfalse); - } +{ + mDNSBool needSetKeys = mDNStrue; + ClientTunnel *tun = (ClientTunnel *)question->QuestionContext; + mDNSBool v6Tunnel = mDNSfalse; + DomainAuthInfo *info; + + // If the port is zero, then we have a relay address of the peer + if (mDNSIPPortIsZero(tun->rmt_outer_port)) + v6Tunnel = mDNStrue; + + if (v6Tunnel) + { + LogInfo("TunnelClientFinish: Relay address %.16a", &answer->rdata->u.ipv6); + tun->rmt_outer6 = answer->rdata->u.ipv6; + tun->loc_outer6 = m->AutoTunnelRelayAddr; + } + else + { + LogInfo("TunnelClientFinish: SRV target address %.4a", &answer->rdata->u.ipv4); + tun->rmt_outer = answer->rdata->u.ipv4; + mDNSAddr tmpDst = { mDNSAddrType_IPv4, {{{0}}} }; + tmpDst.ip.v4 = tun->rmt_outer; + mDNSAddr tmpSrc = zeroAddr; + mDNSPlatformSourceAddrForDest(&tmpSrc, &tmpDst); + if (tmpSrc.type == mDNSAddrType_IPv4) tun->loc_outer = tmpSrc.ip.v4; + else tun->loc_outer = m->AdvertisedV4.ip.v4; + } + + question->ThisQInterval = -1; // So we know this tunnel setup has completed + + info = GetAuthInfoForName(m, &tun->dstname); + if (!info) + { + LogMsg("TunnelClientFinish: Could not get AuthInfo for %##s", tun->dstname.c); + ReissueBlockedQuestions(m, &tun->dstname, mDNSfalse); + return; + } + + tun->loc_inner = info->AutoTunnelInnerAddress; + + // If we found a v6Relay address for our peer, delete all the v4Tunnels for our peer and + // look for existing tunnels to see whether they have the same information for our peer. + // If not, delete them and need to create a new tunnel. If they are same, just use the + // same tunnel. Do the similar thing if we found a v4Tunnel end point for our peer. + TunnelClientDeleteAny(m, tun, !v6Tunnel); + needSetKeys = TunnelClientDeleteMatching(m, tun, v6Tunnel); + + if (needSetKeys) LogInfo("TunnelClientFinish: New %s AutoTunnel for %##s %.16a", (v6Tunnel ? "IPv6" : "IPv4"), tun->dstname.c, &tun->rmt_inner); + else LogInfo("TunnelClientFinish: Reusing exiting %s AutoTunnel for %##s %.16a", (v6Tunnel ? "IPv6" : "IPv4"), tun->dstname.c, &tun->rmt_inner); + + mStatus result = needSetKeys ? AutoTunnelSetKeys(tun, mDNStrue) : mStatus_NoError; + static char msgbuf[32]; + mDNS_snprintf(msgbuf, sizeof(msgbuf), "Tunnel setup - %d", result); + mDNSASLLog((uuid_t *)&m->asl_uuid, "autotunnel.config", result ? "failure" : "success", msgbuf, ""); + // Kick off any questions that were held pending this tunnel setup + ReissueBlockedQuestions(m, &tun->dstname, (result == mStatus_NoError) ? mDNStrue : mDNSfalse); +} mDNSexport void AutoTunnelCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) - { - ClientTunnel *tun = (ClientTunnel *)question->QuestionContext; - - LogInfo("AutoTunnelCallback tun %p AddRecord %d rdlength %d qtype %d", tun, AddRecord, answer->rdlength, question->qtype); - - if (!AddRecord) return; - mDNS_StopQuery(m, question); - - // If we are looking up the AAAA record for _autotunnel6, don't consider it as failure. - // The code below will look for _autotunnel._udp SRV record followed by A record - if (tun->tc_state != TC_STATE_AAAA_PEER_RELAY && !answer->rdlength) - { - LogInfo("AutoTunnelCallback NXDOMAIN %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); - static char msgbuf[16]; - mDNS_snprintf(msgbuf, sizeof(msgbuf), "%s lookup", DNSTypeName(question->qtype)); - mDNSASLLog((uuid_t *)&m->asl_uuid, "autotunnel.config", "failure", msgbuf, ""); - UnlinkAndReissueBlockedQuestions(m, tun, mDNSfalse); - return; - } - - switch (tun->tc_state) - { - case TC_STATE_AAAA_PEER: - if (question->qtype != kDNSType_AAAA) - { - LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_AAAA_PEER", question->qtype); - } - if (mDNSSameIPv6Address(answer->rdata->u.ipv6, m->AutoTunnelHostAddr)) - { - LogInfo("AutoTunnelCallback: suppressing tunnel to self %.16a", &answer->rdata->u.ipv6); - UnlinkAndReissueBlockedQuestions(m, tun, mDNStrue); - return; - } - tun->rmt_inner = answer->rdata->u.ipv6; - LogInfo("AutoTunnelCallback:TC_STATE_AAAA_PEER: dst host %.16a", &tun->rmt_inner); - if (!mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddrOut)) - { - LogInfo("AutoTunnelCallback: Looking up _autotunnel6 AAAA"); - tun->tc_state = TC_STATE_AAAA_PEER_RELAY; - question->qtype = kDNSType_AAAA; - AssignDomainName(&question->qname, (const domainname*) "\x0C" "_autotunnel6"); - } - else - { - LogInfo("AutoTunnelCallback: Looking up _autotunnel._udp SRV"); - tun->tc_state = TC_STATE_SRV_PEER; - question->qtype = kDNSType_SRV; - AssignDomainName(&question->qname, (const domainname*) "\x0B" "_autotunnel" "\x04" "_udp"); - } - AppendDomainName(&question->qname, &tun->dstname); - mDNS_StartQuery(m, &tun->q); - return; - case TC_STATE_AAAA_PEER_RELAY: - if (question->qtype != kDNSType_AAAA) - { - LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_AAAA_PEER_RELAY", question->qtype); - } - // If it failed, look for the SRV record. - if (!answer->rdlength) - { - LogInfo("AutoTunnelCallback: Looking up _autotunnel6 AAAA failed, trying SRV"); - tun->tc_state = TC_STATE_SRV_PEER; - AssignDomainName(&question->qname, (const domainname*) "\x0B" "_autotunnel" "\x04" "_udp"); - AppendDomainName(&question->qname, &tun->dstname); - question->qtype = kDNSType_SRV; - mDNS_StartQuery(m, &tun->q); - return; - } - TunnelClientFinish(m, question, answer); - return; - case TC_STATE_SRV_PEER: - if (question->qtype != kDNSType_SRV) - { - LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_SRV_PEER", question->qtype); - } - LogInfo("AutoTunnelCallback: SRV target name %##s", answer->rdata->u.srv.target.c); - tun->tc_state = TC_STATE_ADDR_PEER; - AssignDomainName(&tun->q.qname, &answer->rdata->u.srv.target); - tun->rmt_outer_port = answer->rdata->u.srv.port; - question->qtype = kDNSType_A; - mDNS_StartQuery(m, &tun->q); - return; - case TC_STATE_ADDR_PEER: - if (question->qtype != kDNSType_A) - { - LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_ADDR_PEER", question->qtype); - } - TunnelClientFinish(m, question, answer); - return; - default: - LogMsg("AutoTunnelCallback: Unknown question %p", question); - } - } +{ + ClientTunnel *tun = (ClientTunnel *)question->QuestionContext; + DomainAuthInfo *info; + + LogInfo("AutoTunnelCallback tun %p AddRecord %d rdlength %d qtype %d", tun, AddRecord, answer->rdlength, question->qtype); + + if (!AddRecord) return; + mDNS_StopQuery(m, question); + + // If we are looking up the AAAA record for _autotunnel6, don't consider it as failure. + // The code below will look for _autotunnel._udp SRV record followed by A record + if (tun->tc_state != TC_STATE_AAAA_PEER_RELAY && !answer->rdlength) + { + LogInfo("AutoTunnelCallback NXDOMAIN %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + static char msgbuf[16]; + mDNS_snprintf(msgbuf, sizeof(msgbuf), "%s lookup", DNSTypeName(question->qtype)); + mDNSASLLog((uuid_t *)&m->asl_uuid, "autotunnel.config", "failure", msgbuf, ""); + UnlinkAndReissueBlockedQuestions(m, tun, mDNSfalse); + return; + } + + switch (tun->tc_state) + { + case TC_STATE_AAAA_PEER: + if (question->qtype != kDNSType_AAAA) + { + LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_AAAA_PEER", question->qtype); + } + info = GetAuthInfoForName(m, &tun->dstname); + if (!info) + { + LogMsg("AutoTunnelCallback: Could not get AuthInfo for %##s", tun->dstname.c); + UnlinkAndReissueBlockedQuestions(m, tun, mDNStrue); + return; + } + if (mDNSSameIPv6Address(answer->rdata->u.ipv6, info->AutoTunnelInnerAddress)) + { + LogInfo("AutoTunnelCallback: suppressing tunnel to self %.16a", &answer->rdata->u.ipv6); + UnlinkAndReissueBlockedQuestions(m, tun, mDNStrue); + return; + } + if (info && mDNSSameIPv6NetworkPart(answer->rdata->u.ipv6, info->AutoTunnelInnerAddress)) + { + LogInfo("AutoTunnelCallback: suppressing tunnel to peer %.16a", &answer->rdata->u.ipv6); + UnlinkAndReissueBlockedQuestions(m, tun, mDNStrue); + return; + } + tun->rmt_inner = answer->rdata->u.ipv6; + LogInfo("AutoTunnelCallback:TC_STATE_AAAA_PEER: dst host %.16a", &tun->rmt_inner); + if (!mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddr)) + { + LogInfo("AutoTunnelCallback: Looking up _autotunnel6 AAAA"); + tun->tc_state = TC_STATE_AAAA_PEER_RELAY; + question->qtype = kDNSType_AAAA; + AssignDomainName(&question->qname, (const domainname*) "\x0C" "_autotunnel6"); + } + else + { + LogInfo("AutoTunnelCallback: Looking up _autotunnel._udp SRV"); + tun->tc_state = TC_STATE_SRV_PEER; + question->qtype = kDNSType_SRV; + AssignDomainName(&question->qname, (const domainname*) "\x0B" "_autotunnel" "\x04" "_udp"); + } + AppendDomainName(&question->qname, &tun->dstname); + mDNS_StartQuery(m, &tun->q); + return; + case TC_STATE_AAAA_PEER_RELAY: + if (question->qtype != kDNSType_AAAA) + { + LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_AAAA_PEER_RELAY", question->qtype); + } + // If it failed, look for the SRV record. + if (!answer->rdlength) + { + LogInfo("AutoTunnelCallback: Looking up _autotunnel6 AAAA failed, trying SRV"); + tun->tc_state = TC_STATE_SRV_PEER; + AssignDomainName(&question->qname, (const domainname*) "\x0B" "_autotunnel" "\x04" "_udp"); + AppendDomainName(&question->qname, &tun->dstname); + question->qtype = kDNSType_SRV; + mDNS_StartQuery(m, &tun->q); + return; + } + TunnelClientFinish(m, question, answer); + return; + case TC_STATE_SRV_PEER: + if (question->qtype != kDNSType_SRV) + { + LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_SRV_PEER", question->qtype); + } + LogInfo("AutoTunnelCallback: SRV target name %##s", answer->rdata->u.srv.target.c); + tun->tc_state = TC_STATE_ADDR_PEER; + AssignDomainName(&tun->q.qname, &answer->rdata->u.srv.target); + tun->rmt_outer_port = answer->rdata->u.srv.port; + question->qtype = kDNSType_A; + mDNS_StartQuery(m, &tun->q); + return; + case TC_STATE_ADDR_PEER: + if (question->qtype != kDNSType_A) + { + LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_ADDR_PEER", question->qtype); + } + TunnelClientFinish(m, question, answer); + return; + default: + LogMsg("AutoTunnelCallback: Unknown question %p", question); + } +} // Must be called with the lock held mDNSexport void AddNewClientTunnel(mDNS *const m, DNSQuestion *const q) - { - ClientTunnel *p = mallocL("ClientTunnel", sizeof(ClientTunnel)); - if (!p) return; - p->prefix = q->AuthInfo->AutoTunnel; - AssignDomainName(&p->dstname, &q->qname); - p->MarkedForDeletion = mDNSfalse; - p->loc_inner = zerov6Addr; - p->loc_outer = zerov4Addr; - p->loc_outer6 = zerov6Addr; - p->rmt_inner = zerov6Addr; - p->rmt_outer = zerov4Addr; - p->rmt_outer6 = zerov6Addr; - p->rmt_outer_port = zeroIPPort; - p->tc_state = TC_STATE_AAAA_PEER; - p->next = m->TunnelClients; - m->TunnelClients = p; // We intentionally build list in reverse order - - p->q.InterfaceID = mDNSInterface_Any; - p->q.Target = zeroAddr; - AssignDomainName(&p->q.qname, &q->qname); - p->q.qtype = kDNSType_AAAA; - p->q.qclass = kDNSClass_IN; - p->q.LongLived = mDNSfalse; - p->q.ExpectUnique = mDNStrue; - p->q.ForceMCast = mDNSfalse; - p->q.ReturnIntermed = mDNStrue; - p->q.SuppressUnusable = mDNSfalse; - p->q.SearchListIndex = 0; - p->q.AppendSearchDomains = 0; - p->q.RetryWithSearchDomains = mDNSfalse; - p->q.TimeoutQuestion = 0; - p->q.WakeOnResolve = 0; - p->q.qnameOrig = mDNSNULL; - p->q.QuestionCallback = AutoTunnelCallback; - p->q.QuestionContext = p; - - LogInfo("AddNewClientTunnel start tun %p %##s (%s)%s", p, &q->qname.c, DNSTypeName(q->qtype), q->LongLived ? " LongLived" : ""); - mDNS_StartQuery_internal(m, &p->q); - } +{ + ClientTunnel *p = mallocL("ClientTunnel", sizeof(ClientTunnel)); + if (!p) return; + AssignDomainName(&p->dstname, &q->qname); + p->MarkedForDeletion = mDNSfalse; + p->loc_inner = zerov6Addr; + p->loc_outer = zerov4Addr; + p->loc_outer6 = zerov6Addr; + p->rmt_inner = zerov6Addr; + p->rmt_outer = zerov4Addr; + p->rmt_outer6 = zerov6Addr; + p->rmt_outer_port = zeroIPPort; + p->tc_state = TC_STATE_AAAA_PEER; + p->next = m->TunnelClients; + m->TunnelClients = p; // We intentionally build list in reverse order + + p->q.InterfaceID = mDNSInterface_Any; + p->q.flags = 0; + p->q.Target = zeroAddr; + AssignDomainName(&p->q.qname, &q->qname); + p->q.qtype = kDNSType_AAAA; + p->q.qclass = kDNSClass_IN; + p->q.LongLived = mDNSfalse; + p->q.ExpectUnique = mDNStrue; + p->q.ForceMCast = mDNSfalse; + p->q.ReturnIntermed = mDNStrue; + p->q.SuppressUnusable = mDNSfalse; + p->q.SearchListIndex = 0; + p->q.AppendSearchDomains = 0; + p->q.RetryWithSearchDomains = mDNSfalse; + p->q.TimeoutQuestion = 0; + p->q.WakeOnResolve = 0; + p->q.UseBrackgroundTrafficClass = mDNSfalse; + p->q.ValidationRequired = 0; + p->q.ValidatingResponse = 0; + p->q.qnameOrig = mDNSNULL; + p->q.QuestionCallback = AutoTunnelCallback; + p->q.QuestionContext = p; + + LogInfo("AddNewClientTunnel start tun %p %##s (%s)%s", p, &q->qname.c, DNSTypeName(q->qtype), q->LongLived ? " LongLived" : ""); + mDNS_StartQuery_internal(m, &p->q); +} #endif // APPLE_OSX_mDNSResponder @@ -4409,483 +4479,503 @@ mDNSexport void AddNewClientTunnel(mDNS *const m, DNSQuestion *const q) #endif mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc) - { - mDNSBool foundav4 = mDNSfalse; - mDNSBool foundav6 = mDNSfalse; - struct ifaddrs *ifa = myGetIfAddrs(1); - struct ifaddrs *v4Loopback = NULL; - struct ifaddrs *v6Loopback = NULL; - char defaultname[64]; +{ + mDNSBool foundav4 = mDNSfalse; + mDNSBool foundav6 = mDNSfalse; + struct ifaddrs *ifa = myGetIfAddrs(1); + struct ifaddrs *v4Loopback = NULL; + struct ifaddrs *v6Loopback = NULL; + char defaultname[64]; #ifndef NO_IPV6 - int InfoSocket = socket(AF_INET6, SOCK_DGRAM, 0); - if (InfoSocket < 3 && errno != EAFNOSUPPORT) LogMsg("UpdateInterfaceList: InfoSocket error %d errno %d (%s)", InfoSocket, errno, strerror(errno)); + int InfoSocket = socket(AF_INET6, SOCK_DGRAM, 0); + if (InfoSocket < 3 && errno != EAFNOSUPPORT) LogMsg("UpdateInterfaceList: InfoSocket error %d errno %d (%s)", InfoSocket, errno, strerror(errno)); #endif - // During wakeup, we may get a network change notification e.g., new addresses, before we get - // a wake notification. This means that we have not set AnnounceOwner. Registering interfaces with - // core would cause us to probe again which will conflict with the sleep proxy server, if we had - // registered with it when going to sleep. Hence, need to delay until we get the wake notification - - if (m->SleepState == SleepState_Sleeping) ifa = NULL; - - while (ifa) - { + while (ifa) + { #if LIST_ALL_INTERFACES - if (ifa->ifa_addr->sa_family == AF_APPLETALK) - LogMsg("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) - LogMsg("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) - LogMsg("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)) - LogMsg("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)) - LogMsg("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) - LogMsg("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) - LogMsg("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); + if (ifa->ifa_addr->sa_family == AF_APPLETALK) + LogMsg("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) + LogMsg("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) + LogMsg("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)) + LogMsg("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)) + LogMsg("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) + LogMsg("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) + LogMsg("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_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(m->PrimaryMAC) && mDNSSameEthAddress(&m->PrimaryMAC, &zeroEthAddr)) - mDNSPlatformMemCopy(m->PrimaryMAC.b, sdl->sdl_data + sdl->sdl_nlen, 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) - { - 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); - } - // Apparently it's normal for the sa_family of an ifa_netmask to sometimes be zero, so we don't complain about that - // getifaddrs is returning invalid netmask family for fw0 and vmnet - else if (ifa->ifa_netmask->sa_family != ifa->ifa_addr->sa_family && ifa->ifa_netmask->sa_family != 0) - { - 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); - } - // Currently we use a few internal ones like mDNSInterfaceID_LocalOnly etc. that are negative values (0, -1, -2). - else if ((int)if_nametoindex(ifa->ifa_name) <= 0) - { - LogMsg("UpdateInterfaceList: if_nametoindex returned zero/negative value for %5s(%d)", ifa->ifa_name, if_nametoindex(ifa->ifa_name)); - } - else - { - // Make sure ifa_netmask->sa_family is set correctly - // getifaddrs is returning invalid netmask family for fw0 and vmnet - ifa->ifa_netmask->sa_family = ifa->ifa_addr->sa_family; - int ifru_flags6 = 0; + 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(m->PrimaryMAC) && mDNSSameEthAddress(&m->PrimaryMAC, &zeroEthAddr)) + mDNSPlatformMemCopy(m->PrimaryMAC.b, sdl->sdl_data + sdl->sdl_nlen, 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) + { + 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); + } + // Apparently it's normal for the sa_family of an ifa_netmask to sometimes be zero, so we don't complain about that + // getifaddrs is returning invalid netmask family for fw0 and vmnet + else if (ifa->ifa_netmask->sa_family != ifa->ifa_addr->sa_family && ifa->ifa_netmask->sa_family != 0) + { + 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); + } + // Currently we use a few internal ones like mDNSInterfaceID_LocalOnly etc. that are negative values (0, -1, -2). + else if ((int)if_nametoindex(ifa->ifa_name) <= 0) + { + LogMsg("UpdateInterfaceList: if_nametoindex returned zero/negative value for %5s(%d)", ifa->ifa_name, if_nametoindex(ifa->ifa_name)); + } + else + { + // Make sure ifa_netmask->sa_family is set correctly + // getifaddrs is returning invalid netmask family for fw0 and vmnet + ifa->ifa_netmask->sa_family = ifa->ifa_addr->sa_family; + int ifru_flags6 = 0; #ifndef NO_IPV6 - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; - if (ifa->ifa_addr->sa_family == AF_INET6 && InfoSocket >= 0) - { - struct in6_ifreq ifr6; - mDNSPlatformMemZero((char *)&ifr6, sizeof(ifr6)); - strlcpy(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); - } + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; + if (ifa->ifa_addr->sa_family == AF_INET6 && InfoSocket >= 0) + { + struct in6_ifreq ifr6; + mDNSPlatformMemZero((char *)&ifr6, sizeof(ifr6)); + strlcpy(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); + } #endif - 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; + 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; #ifndef NO_IPV6 - else if (sin6->sin6_addr.s6_addr[0] != 0xFD) v6Loopback = ifa; + else if (sin6->sin6_addr.s6_addr[0] != 0xFD) v6Loopback = ifa; #endif - } - else - { - NetworkInterfaceInfoOSX *i = AddInterfaceToList(m, ifa, utc); - if (i && MulticastInterface(i) && i->ifinfo.Advertise) - { - if (ifa->ifa_addr->sa_family == AF_INET) foundav4 = mDNStrue; - else foundav6 = mDNStrue; - } - } - } - } - } - ifa = ifa->ifa_next; - } - - // For efficiency, we don't register a loopback interface when other interfaces of that family are available and advertising - if (!foundav4 && v4Loopback) AddInterfaceToList(m, v4Loopback, utc); - if (!foundav6 && v6Loopback) AddInterfaceToList(m, v6Loopback, utc); - - // Now the list is complete, set the McastTxRx setting for each interface. - NetworkInterfaceInfoOSX *i; - for (i = m->p->InterfaceList; i; i = i->next) - if (i->Exists) - { - mDNSBool txrx = MulticastInterface(i); + } + else + { + NetworkInterfaceInfoOSX *i = AddInterfaceToList(m, ifa, utc); + if (i && MulticastInterface(i) && i->ifinfo.Advertise) + { + if (ifa->ifa_addr->sa_family == AF_INET) foundav4 = mDNStrue; + else foundav6 = mDNStrue; + } + } + } + } + } + ifa = ifa->ifa_next; + } + + // For efficiency, we don't register a loopback interface when other interfaces of that family are available and advertising + if (!foundav4 && v4Loopback) AddInterfaceToList(m, v4Loopback, utc); + if (!foundav6 && v6Loopback) AddInterfaceToList(m, v6Loopback, utc); + + // Now the list is complete, set the McastTxRx setting for each interface. + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) + if (i->Exists) + { + mDNSBool txrx = MulticastInterface(i); #if USE_V6_ONLY_WHEN_NO_ROUTABLE_V4 - txrx = txrx && ((i->ifinfo.ip.type == mDNSAddrType_IPv4) || !FindRoutableIPv4(m, i->scope_id)); + txrx = txrx && ((i->ifinfo.ip.type == mDNSAddrType_IPv4) || !FindRoutableIPv4(m, i->scope_id)); #endif - if (i->ifinfo.McastTxRx != txrx) - { - i->ifinfo.McastTxRx = txrx; - i->Exists = 2; // State change; need to deregister and reregister this interface - } - } + if (i->ifinfo.McastTxRx != txrx) + { + i->ifinfo.McastTxRx = txrx; + i->Exists = 2; // State change; need to deregister and reregister this interface + } + } #ifndef NO_IPV6 - if (InfoSocket >= 0) close(InfoSocket); + if (InfoSocket >= 0) close(InfoSocket); #endif - // If we haven't set up AutoTunnelHostAddr yet, do it now - if (!mDNSSameEthAddress(&m->PrimaryMAC, &zeroEthAddr) && m->AutoTunnelHostAddr.b[0] == 0) - { - m->AutoTunnelHostAddr.b[0x0] = 0xFD; // Required prefix for "locally assigned" ULA (See RFC 4193) - m->AutoTunnelHostAddr.b[0x1] = mDNSRandom(255); - m->AutoTunnelHostAddr.b[0x2] = mDNSRandom(255); - m->AutoTunnelHostAddr.b[0x3] = mDNSRandom(255); - m->AutoTunnelHostAddr.b[0x4] = mDNSRandom(255); - m->AutoTunnelHostAddr.b[0x5] = mDNSRandom(255); - m->AutoTunnelHostAddr.b[0x6] = mDNSRandom(255); - m->AutoTunnelHostAddr.b[0x7] = mDNSRandom(255); - m->AutoTunnelHostAddr.b[0x8] = m->PrimaryMAC.b[0] ^ 0x02; // See RFC 3513, Appendix A for explanation - m->AutoTunnelHostAddr.b[0x9] = m->PrimaryMAC.b[1]; - m->AutoTunnelHostAddr.b[0xA] = m->PrimaryMAC.b[2]; - m->AutoTunnelHostAddr.b[0xB] = 0xFF; - m->AutoTunnelHostAddr.b[0xC] = 0xFE; - m->AutoTunnelHostAddr.b[0xD] = m->PrimaryMAC.b[3]; - m->AutoTunnelHostAddr.b[0xE] = m->PrimaryMAC.b[4]; - m->AutoTunnelHostAddr.b[0xF] = m->PrimaryMAC.b[5]; - m->AutoTunnelLabel.c[0] = mDNS_snprintf((char*)m->AutoTunnelLabel.c+1, 254, "AutoTunnel-%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X", - m->AutoTunnelHostAddr.b[0x8], m->AutoTunnelHostAddr.b[0x9], m->AutoTunnelHostAddr.b[0xA], m->AutoTunnelHostAddr.b[0xB], - m->AutoTunnelHostAddr.b[0xC], m->AutoTunnelHostAddr.b[0xD], m->AutoTunnelHostAddr.b[0xE], m->AutoTunnelHostAddr.b[0xF]); - LogInfo("m->AutoTunnelLabel %#s", m->AutoTunnelLabel.c); - } - - mDNS_snprintf(defaultname, sizeof(defaultname), "%.*s-%02X%02X%02X%02X%02X%02X", HINFO_HWstring_prefixlen, HINFO_HWstring, - m->PrimaryMAC.b[0], m->PrimaryMAC.b[1], m->PrimaryMAC.b[2], m->PrimaryMAC.b[3], m->PrimaryMAC.b[4], m->PrimaryMAC.b[5]); - - // Set up the nice label - domainlabel nicelabel; - nicelabel.c[0] = 0; - GetUserSpecifiedFriendlyComputerName(&nicelabel); - if (nicelabel.c[0] == 0) - { - debugf("Couldn’t read user-specified Computer Name; using default “%s” instead", defaultname); - MakeDomainLabelFromLiteralString(&nicelabel, defaultname); - } - - // Set up the RFC 1034-compliant label - domainlabel hostlabel; - hostlabel.c[0] = 0; - GetUserSpecifiedLocalHostName(&hostlabel); - if (hostlabel.c[0] == 0) - { - debugf("Couldn’t read user-specified Local Hostname; using default “%s.local” instead", defaultname); - MakeDomainLabelFromLiteralString(&hostlabel, defaultname); - } - - mDNSBool namechange = mDNSfalse; - - // We use a case-sensitive comparison here because even though changing the capitalization - // of the name alone is not significant to DNS, it's still a change from the user's point of view - if (SameDomainLabelCS(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 - { - if (m->p->usernicelabel.c[0]) // Don't show message first time through, when we first read name from prefs on boot - LogMsg("User updated Computer Name from “%#s” to “%#s”", m->p->usernicelabel.c, nicelabel.c); - m->p->usernicelabel = m->nicelabel = nicelabel; - namechange = mDNStrue; - } - - if (SameDomainLabelCS(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 - { - if (m->p->userhostlabel.c[0]) // Don't show message first time through, when we first read name from prefs on boot - LogMsg("User updated Local Hostname from “%#s” to “%#s”", m->p->userhostlabel.c, hostlabel.c); - m->p->userhostlabel = m->hostlabel = hostlabel; - mDNS_SetFQDN(m); - namechange = mDNStrue; - } + mDNS_snprintf(defaultname, sizeof(defaultname), "%.*s-%02X%02X%02X%02X%02X%02X", HINFO_HWstring_prefixlen, HINFO_HWstring, + m->PrimaryMAC.b[0], m->PrimaryMAC.b[1], m->PrimaryMAC.b[2], m->PrimaryMAC.b[3], m->PrimaryMAC.b[4], m->PrimaryMAC.b[5]); + + // Set up the nice label + domainlabel nicelabel; + nicelabel.c[0] = 0; + GetUserSpecifiedFriendlyComputerName(&nicelabel); + if (nicelabel.c[0] == 0) + { + debugf("Couldn’t read user-specified Computer Name; using default “%s” instead", defaultname); + MakeDomainLabelFromLiteralString(&nicelabel, defaultname); + } + + // Set up the RFC 1034-compliant label + domainlabel hostlabel; + hostlabel.c[0] = 0; + GetUserSpecifiedLocalHostName(&hostlabel); + if (hostlabel.c[0] == 0) + { + debugf("Couldn’t read user-specified Local Hostname; using default “%s.local” instead", defaultname); + MakeDomainLabelFromLiteralString(&hostlabel, defaultname); + } + + mDNSBool namechange = mDNSfalse; + + // We use a case-sensitive comparison here because even though changing the capitalization + // of the name alone is not significant to DNS, it's still a change from the user's point of view + if (SameDomainLabelCS(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 + { + if (m->p->usernicelabel.c[0]) // Don't show message first time through, when we first read name from prefs on boot + LogMsg("User updated Computer Name from “%#s” to “%#s”", m->p->usernicelabel.c, nicelabel.c); + m->p->usernicelabel = m->nicelabel = nicelabel; + namechange = mDNStrue; + } + + if (SameDomainLabelCS(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 + { + if (m->p->userhostlabel.c[0]) // Don't show message first time through, when we first read name from prefs on boot + LogMsg("User updated Local Hostname from “%#s” to “%#s”", m->p->userhostlabel.c, hostlabel.c); + m->p->userhostlabel = m->hostlabel = hostlabel; + mDNS_SetFQDN(m); + namechange = mDNStrue; + } #if APPLE_OSX_mDNSResponder - if (namechange) // If either name has changed, we need to tickle our AutoTunnel state machine to update its registered records - { - DomainAuthInfo *info; - for (info = m->AuthInfoList; info; info = info->next) - if (info->AutoTunnel) AutoTunnelHostNameChanged(m, info); - } + if (namechange) // If either name has changed, we need to tickle our AutoTunnel state machine to update its registered records + { + DomainAuthInfo *info; + for (info = m->AuthInfoList; info; info = info->next) + if (info->AutoTunnel) AutoTunnelHostNameChanged(m, info); + } #endif // APPLE_OSX_mDNSResponder - return(mStatus_NoError); - } + return(mStatus_NoError); +} // Returns number of leading one-bits in mask: 0-32 for IPv4, 0-128 for IPv6 // Returns -1 if all the one-bits are not contiguous mDNSlocal int CountMaskBits(mDNSAddr *mask) - { - int i = 0, bits = 0; - int bytes = mask->type == mDNSAddrType_IPv4 ? 4 : mask->type == mDNSAddrType_IPv6 ? 16 : 0; - while (i < bytes) - { - mDNSu8 b = mask->ip.v6.b[i++]; - while (b & 0x80) { bits++; b <<= 1; } - if (b) return(-1); - } - while (i < bytes) if (mask->ip.v6.b[i++]) return(-1); - return(bits); - } +{ + int i = 0, bits = 0; + int bytes = mask->type == mDNSAddrType_IPv4 ? 4 : mask->type == mDNSAddrType_IPv6 ? 16 : 0; + while (i < bytes) + { + mDNSu8 b = mask->ip.v6.b[i++]; + while (b & 0x80) { bits++; b <<= 1; } + if (b) return(-1); + } + while (i < bytes) if (mask->ip.v6.b[i++]) return(-1); + return(bits); +} // returns count of non-link local V4 addresses registered mDNSlocal int SetupActiveInterfaces(mDNS *const m, mDNSs32 utc) - { - NetworkInterfaceInfoOSX *i; - int count = 0; - for (i = m->p->InterfaceList; i; i = i->next) - if (i->Exists) - { - NetworkInterfaceInfo *const n = &i->ifinfo; - NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifinfo.ifname, AAAA_OVER_V4 ? AF_UNSPEC : i->sa_family); - if (!primary) LogMsg("SetupActiveInterfaces ERROR! SearchForInterfaceByName didn't find %s", i->ifinfo.ifname); - - if (i->Registered && i->Registered != primary) // Sanity check - { - LogMsg("SetupActiveInterfaces ERROR! n->Registered %p != primary %p", i->Registered, primary); - i->Registered = mDNSNULL; - } - - if (!i->Registered) - { - // Note: If i->Registered is set, that means we've called mDNS_RegisterInterface() for this interface, - // so we need to make sure we call mDNS_DeregisterInterface() before disposing it. - // If i->Registered is NOT set, then we haven't registered it and we should not try to deregister it - // - - i->Registered = primary; - - // If i->LastSeen == utc, then this is a brand-new interface, just created, or an interface that never went away. - // If i->LastSeen != utc, then this is an old interface, previously seen, that went away for (utc - i->LastSeen) seconds. - // If the interface is an old one that went away and came back in less than a minute, then we're in a flapping scenario. - i->Occulting = !(i->ifa_flags & IFF_LOOPBACK) && (utc - i->LastSeen > 0 && utc - i->LastSeen < 60); - - // Temporary fix to handle P2P flapping. P2P reuses the scope-id, mac address and the IP address - // everytime it creates a new interface. We think it is a duplicate and hence consider it - // as flashing and occulting, that is, flapping. If an interface is marked as flapping, - // mDNS_RegisterInterface() changes the probe delay from 1/2 second to 5 seconds and - // logs a warning message to system.log noting frequent interface transitions. - if (strncmp(i->ifinfo.ifname, "p2p", 3) == 0) - { - LogInfo("SetupActiveInterfaces: P2P %s interface registering %s %s", i->ifinfo.ifname, - i->Flashing ? " (Flashing)" : "", - i->Occulting ? " (Occulting)" : ""); - mDNS_RegisterInterface(m, n, 0); - } - else - { - mDNS_RegisterInterface(m, n, i->Flashing && i->Occulting); - } - - if (!mDNSAddressIsLinkLocal(&n->ip)) count++; - LogInfo("SetupActiveInterfaces: Registered %5s(%lu) %.6a InterfaceID %p(%p), primary %p, %#a/%d%s%s%s", - i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, i, primary, &n->ip, CountMaskBits(&n->mask), - i->Flashing ? " (Flashing)" : "", - i->Occulting ? " (Occulting)" : "", - n->InterfaceActive ? " (Primary)" : ""); - - if (!n->McastTxRx) - debugf("SetupActiveInterfaces: No Tx/Rx on %5s(%lu) %.6a InterfaceID %p %#a", i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, &n->ip); - else - { - if (i->sa_family == AF_INET) - { - struct ip_mreq imr; - primary->ifa_v4addr.s_addr = n->ip.ip.v4.NotAnInteger; - imr.imr_multiaddr.s_addr = AllDNSLinkGroup_v4.ip.v4.NotAnInteger; - imr.imr_interface = primary->ifa_v4addr; - - // If this is our *first* IPv4 instance for this interface name, we need to do a IP_DROP_MEMBERSHIP first, - // before trying to join the group, to clear out stale kernel state which may be lingering. - // In particular, this happens with removable network interfaces like USB Ethernet adapters -- the kernel has stale state - // from the last time the USB Ethernet adapter was connected, and part of the kernel thinks we've already joined the group - // on that interface (so we get EADDRINUSE when we try to join again) but a different part of the kernel thinks we haven't - // joined the group (so we receive no multicasts). Doing an IP_DROP_MEMBERSHIP before joining seems to flush the stale state. - // Also, trying to make the code leave the group when the adapter is removed doesn't work either, - // because by the time we get the configuration change notification, the interface is already gone, - // so attempts to unsubscribe fail with EADDRNOTAVAIL (errno 49 "Can't assign requested address"). - // IP_ADD_MEMBERSHIP fails for previously-connected removable interfaces - if (SearchForInterfaceByName(m, i->ifinfo.ifname, AF_INET) == i) - { - LogInfo("SetupActiveInterfaces: %5s(%lu) Doing precautionary IP_DROP_MEMBERSHIP for %.4a on %.4a", i->ifinfo.ifname, i->scope_id, &imr.imr_multiaddr, &imr.imr_interface); - mStatus err = setsockopt(m->p->permanentsockets.sktv4, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(imr)); - if (err < 0 && (errno != EADDRNOTAVAIL)) - LogMsg("setsockopt - IP_DROP_MEMBERSHIP error %d errno %d (%s)", err, errno, strerror(errno)); - } - - LogInfo("SetupActiveInterfaces: %5s(%lu) joining IPv4 mcast group %.4a on %.4a", i->ifinfo.ifname, i->scope_id, &imr.imr_multiaddr, &imr.imr_interface); - mStatus err = setsockopt(m->p->permanentsockets.sktv4, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)); - // Joining same group twice can give "Address already in use" error -- no need to report that - if (err < 0 && (errno != EADDRINUSE)) - LogMsg("setsockopt - IP_ADD_MEMBERSHIP error %d errno %d (%s) group %.4a on %.4a", err, errno, strerror(errno), &imr.imr_multiaddr, &imr.imr_interface); - } +{ + NetworkInterfaceInfoOSX *i; + int count = 0; + for (i = m->p->InterfaceList; i; i = i->next) + if (i->Exists) + { + NetworkInterfaceInfo *const n = &i->ifinfo; + NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifinfo.ifname, AAAA_OVER_V4 ? AF_UNSPEC : i->sa_family); + if (!primary) LogMsg("SetupActiveInterfaces ERROR! SearchForInterfaceByName didn't find %s", i->ifinfo.ifname); + + if (i->Registered && i->Registered != primary) // Sanity check + { + LogMsg("SetupActiveInterfaces ERROR! n->Registered %p != primary %p", i->Registered, primary); + i->Registered = mDNSNULL; + } + + if (!i->Registered) + { + // Note: If i->Registered is set, that means we've called mDNS_RegisterInterface() for this interface, + // so we need to make sure we call mDNS_DeregisterInterface() before disposing it. + // If i->Registered is NOT set, then we haven't registered it and we should not try to deregister it + // + + i->Registered = primary; + + // If i->LastSeen == utc, then this is a brand-new interface, just created, or an interface that never went away. + // If i->LastSeen != utc, then this is an old interface, previously seen, that went away for (utc - i->LastSeen) seconds. + // If the interface is an old one that went away and came back in less than a minute, then we're in a flapping scenario. + i->Occulting = !(i->ifa_flags & IFF_LOOPBACK) && (utc - i->LastSeen > 0 && utc - i->LastSeen < 60); + + // Temporary fix to handle P2P flapping. P2P reuses the scope-id, mac address and the IP address + // everytime it creates a new interface. We think it is a duplicate and hence consider it + // as flashing and occulting, that is, flapping. If an interface is marked as flapping, + // mDNS_RegisterInterface() changes the probe delay from 1/2 second to 5 seconds and + // logs a warning message to system.log noting frequent interface transitions. + if (strncmp(i->ifinfo.ifname, "p2p", 3) == 0) + { + LogInfo("SetupActiveInterfaces: P2P %s interface registering %s %s", i->ifinfo.ifname, + i->Flashing ? " (Flashing)" : "", + i->Occulting ? " (Occulting)" : ""); + mDNS_RegisterInterface(m, n, 0); + } + else + { + mDNS_RegisterInterface(m, n, i->Flashing && i->Occulting); + } + + if (!mDNSAddressIsLinkLocal(&n->ip)) count++; + LogInfo("SetupActiveInterfaces: Registered %5s(%lu) %.6a InterfaceID %p(%p), primary %p, %#a/%d%s%s%s", + i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, i, primary, &n->ip, CountMaskBits(&n->mask), + i->Flashing ? " (Flashing)" : "", + i->Occulting ? " (Occulting)" : "", + n->InterfaceActive ? " (Primary)" : ""); + + if (!n->McastTxRx) + debugf("SetupActiveInterfaces: No Tx/Rx on %5s(%lu) %.6a InterfaceID %p %#a", i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, &n->ip); + else + { + if (i->sa_family == AF_INET) + { + struct ip_mreq imr; + primary->ifa_v4addr.s_addr = n->ip.ip.v4.NotAnInteger; + imr.imr_multiaddr.s_addr = AllDNSLinkGroup_v4.ip.v4.NotAnInteger; + imr.imr_interface = primary->ifa_v4addr; + + // If this is our *first* IPv4 instance for this interface name, we need to do a IP_DROP_MEMBERSHIP first, + // before trying to join the group, to clear out stale kernel state which may be lingering. + // In particular, this happens with removable network interfaces like USB Ethernet adapters -- the kernel has stale state + // from the last time the USB Ethernet adapter was connected, and part of the kernel thinks we've already joined the group + // on that interface (so we get EADDRINUSE when we try to join again) but a different part of the kernel thinks we haven't + // joined the group (so we receive no multicasts). Doing an IP_DROP_MEMBERSHIP before joining seems to flush the stale state. + // Also, trying to make the code leave the group when the adapter is removed doesn't work either, + // because by the time we get the configuration change notification, the interface is already gone, + // so attempts to unsubscribe fail with EADDRNOTAVAIL (errno 49 "Can't assign requested address"). + // IP_ADD_MEMBERSHIP fails for previously-connected removable interfaces + if (SearchForInterfaceByName(m, i->ifinfo.ifname, AF_INET) == i) + { + LogInfo("SetupActiveInterfaces: %5s(%lu) Doing precautionary IP_DROP_MEMBERSHIP for %.4a on %.4a", i->ifinfo.ifname, i->scope_id, &imr.imr_multiaddr, &imr.imr_interface); + mStatus err = setsockopt(m->p->permanentsockets.sktv4, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(imr)); + if (err < 0 && (errno != EADDRNOTAVAIL)) + LogMsg("setsockopt - IP_DROP_MEMBERSHIP error %d errno %d (%s)", err, errno, strerror(errno)); + } + + LogInfo("SetupActiveInterfaces: %5s(%lu) joining IPv4 mcast group %.4a on %.4a", i->ifinfo.ifname, i->scope_id, &imr.imr_multiaddr, &imr.imr_interface); + mStatus err = setsockopt(m->p->permanentsockets.sktv4, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)); + // Joining same group twice can give "Address already in use" error -- no need to report that + if (err < 0 && (errno != EADDRINUSE)) + LogMsg("setsockopt - IP_ADD_MEMBERSHIP error %d errno %d (%s) group %.4a on %.4a", err, errno, strerror(errno), &imr.imr_multiaddr, &imr.imr_interface); + } #ifndef NO_IPV6 - if (i->sa_family == AF_INET6) - { - struct ipv6_mreq i6mr; - i6mr.ipv6mr_interface = primary->scope_id; - i6mr.ipv6mr_multiaddr = *(struct in6_addr*)&AllDNSLinkGroup_v6.ip.v6; - - if (SearchForInterfaceByName(m, i->ifinfo.ifname, AF_INET6) == i) - { - LogInfo("SetupActiveInterfaces: %5s(%lu) Doing precautionary IPV6_LEAVE_GROUP for %.16a on %u", i->ifinfo.ifname, i->scope_id, &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); - mStatus err = setsockopt(m->p->permanentsockets.sktv6, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &i6mr, sizeof(i6mr)); - if (err < 0 && (errno != EADDRNOTAVAIL)) - LogMsg("setsockopt - IPV6_LEAVE_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); - } - - LogInfo("SetupActiveInterfaces: %5s(%lu) joining IPv6 mcast group %.16a on %u", i->ifinfo.ifname, i->scope_id, &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); - mStatus err = setsockopt(m->p->permanentsockets.sktv6, IPPROTO_IPV6, IPV6_JOIN_GROUP, &i6mr, sizeof(i6mr)); - // Joining same group twice can give "Address already in use" error -- no need to report that - if (err < 0 && (errno != EADDRINUSE)) - LogMsg("setsockopt - IPV6_JOIN_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); - } + if (i->sa_family == AF_INET6) + { + struct ipv6_mreq i6mr; + i6mr.ipv6mr_interface = primary->scope_id; + i6mr.ipv6mr_multiaddr = *(struct in6_addr*)&AllDNSLinkGroup_v6.ip.v6; + + if (SearchForInterfaceByName(m, i->ifinfo.ifname, AF_INET6) == i) + { + LogInfo("SetupActiveInterfaces: %5s(%lu) Doing precautionary IPV6_LEAVE_GROUP for %.16a on %u", i->ifinfo.ifname, i->scope_id, &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); + mStatus err = setsockopt(m->p->permanentsockets.sktv6, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &i6mr, sizeof(i6mr)); + if (err < 0 && (errno != EADDRNOTAVAIL)) + LogMsg("setsockopt - IPV6_LEAVE_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); + } + + LogInfo("SetupActiveInterfaces: %5s(%lu) joining IPv6 mcast group %.16a on %u", i->ifinfo.ifname, i->scope_id, &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); + mStatus err = setsockopt(m->p->permanentsockets.sktv6, IPPROTO_IPV6, IPV6_JOIN_GROUP, &i6mr, sizeof(i6mr)); + // Joining same group twice can give "Address already in use" error -- no need to report that + if (err < 0 && (errno != EADDRINUSE)) + LogMsg("setsockopt - IPV6_JOIN_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); + } #endif - } - } - } + } + } + } - return count; - } + return count; +} mDNSlocal void MarkAllInterfacesInactive(mDNS *const m, mDNSs32 utc) - { - NetworkInterfaceInfoOSX *i; - for (i = m->p->InterfaceList; i; i = i->next) - { - if (i->Exists) i->LastSeen = utc; - i->Exists = mDNSfalse; - } - } +{ + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) + { + if (i->Exists) i->LastSeen = utc; + i->Exists = mDNSfalse; + } +} // returns count of non-link local V4 addresses deregistered mDNSlocal int ClearInactiveInterfaces(mDNS *const m, mDNSs32 utc) - { - // First pass: - // If an interface is going away, then deregister this from the mDNSCore. - // We also have to deregister it if the primary interface that it's using for its InterfaceID is going away. - // We have to do this because mDNSCore will use that InterfaceID when sending packets, and if the memory - // it refers to has gone away we'll crash. - NetworkInterfaceInfoOSX *i; - int count = 0; - for (i = m->p->InterfaceList; i; i = i->next) - { - // If this interface is no longer active, or its InterfaceID is changing, deregister it - NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifinfo.ifname, AAAA_OVER_V4 ? AF_UNSPEC : i->sa_family); - if (i->Registered) - if (i->Exists == 0 || i->Exists == 2 || i->Registered != primary) - { - i->Flashing = !(i->ifa_flags & IFF_LOOPBACK) && (utc - i->AppearanceTime < 60); - LogInfo("ClearInactiveInterfaces: Deregistering %5s(%lu) %.6a InterfaceID %p(%p), primary %p, %#a/%d%s%s%s", - i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, i, primary, - &i->ifinfo.ip, CountMaskBits(&i->ifinfo.mask), - i->Flashing ? " (Flashing)" : "", - i->Occulting ? " (Occulting)" : "", - i->ifinfo.InterfaceActive ? " (Primary)" : ""); - - // Temporary fix to handle P2P flapping. P2P reuses the scope-id, mac address and the IP address - // everytime it creates a new interface. We think it is a duplicate and hence consider it - // as flashing and occulting. The "core" does not flush the cache for this case. This leads to - // stale data returned to the application even after the interface is removed. The application - // then starts to send data but the new interface is not yet created. - if (strncmp(i->ifinfo.ifname, "p2p", 3) == 0) - { - LogInfo("ClearInactiveInterfaces: P2P %s interface deregistering %s %s", i->ifinfo.ifname, - i->Flashing ? " (Flashing)" : "", - i->Occulting ? " (Occulting)" : ""); - mDNS_DeregisterInterface(m, &i->ifinfo, 0); - } - else - { - mDNS_DeregisterInterface(m, &i->ifinfo, i->Flashing && i->Occulting); - } - if (!mDNSAddressIsLinkLocal(&i->ifinfo.ip)) count++; - i->Registered = mDNSNULL; - // Note: If i->Registered is set, that means we've called mDNS_RegisterInterface() for this interface, - // so we need to make sure we call mDNS_DeregisterInterface() before disposing it. - // If i->Registered is NOT set, then it's not registered and we should not call mDNS_DeregisterInterface() on it. - - // Caution: If we ever decide to add code here to leave the multicast group, we need to make sure that this - // is the LAST representative of this physical interface, or we'll unsubscribe from the group prematurely. - } - } - - // Second pass: - // Now that everything that's going to deregister has done so, we can clean up and free the memory - NetworkInterfaceInfoOSX **p = &m->p->InterfaceList; - while (*p) - { - i = *p; - // If no longer active, delete interface from list and free memory - if (!i->Exists) - { - if (i->LastSeen == utc) i->LastSeen = utc - 1; - mDNSBool delete = (NumCacheRecordsForInterfaceID(m, i->ifinfo.InterfaceID) == 0) && (utc - i->LastSeen >= 60); - LogInfo("ClearInactiveInterfaces: %-13s %5s(%lu) %.6a InterfaceID %p(%p) %#a/%d Age %d%s", delete ? "Deleting" : "Holding", - i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, i, - &i->ifinfo.ip, CountMaskBits(&i->ifinfo.mask), utc - i->LastSeen, - i->ifinfo.InterfaceActive ? " (Primary)" : ""); +{ + // First pass: + // If an interface is going away, then deregister this from the mDNSCore. + // We also have to deregister it if the primary interface that it's using for its InterfaceID is going away. + // We have to do this because mDNSCore will use that InterfaceID when sending packets, and if the memory + // it refers to has gone away we'll crash. + NetworkInterfaceInfoOSX *i; + int count = 0; + for (i = m->p->InterfaceList; i; i = i->next) + { + // If this interface is no longer active, or its InterfaceID is changing, deregister it + NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifinfo.ifname, AAAA_OVER_V4 ? AF_UNSPEC : i->sa_family); + if (i->Registered) + if (i->Exists == 0 || i->Exists == 2 || i->Registered != primary) + { + i->Flashing = !(i->ifa_flags & IFF_LOOPBACK) && (utc - i->AppearanceTime < 60); + LogInfo("ClearInactiveInterfaces: Deregistering %5s(%lu) %.6a InterfaceID %p(%p), primary %p, %#a/%d%s%s%s", + i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, i, primary, + &i->ifinfo.ip, CountMaskBits(&i->ifinfo.mask), + i->Flashing ? " (Flashing)" : "", + i->Occulting ? " (Occulting)" : "", + i->ifinfo.InterfaceActive ? " (Primary)" : ""); + + // Temporary fix to handle P2P flapping. P2P reuses the scope-id, mac address and the IP address + // everytime it creates a new interface. We think it is a duplicate and hence consider it + // as flashing and occulting. The "core" does not flush the cache for this case. This leads to + // stale data returned to the application even after the interface is removed. The application + // then starts to send data but the new interface is not yet created. + if (strncmp(i->ifinfo.ifname, "p2p", 3) == 0) + { + LogInfo("ClearInactiveInterfaces: P2P %s interface deregistering %s %s", i->ifinfo.ifname, + i->Flashing ? " (Flashing)" : "", + i->Occulting ? " (Occulting)" : ""); + mDNS_DeregisterInterface(m, &i->ifinfo, 0); + } + else + { + mDNS_DeregisterInterface(m, &i->ifinfo, i->Flashing && i->Occulting); + } + if (!mDNSAddressIsLinkLocal(&i->ifinfo.ip)) count++; + i->Registered = mDNSNULL; + // Note: If i->Registered is set, that means we've called mDNS_RegisterInterface() for this interface, + // so we need to make sure we call mDNS_DeregisterInterface() before disposing it. + // If i->Registered is NOT set, then it's not registered and we should not call mDNS_DeregisterInterface() on it. + + // Caution: If we ever decide to add code here to leave the multicast group, we need to make sure that this + // is the LAST representative of this physical interface, or we'll unsubscribe from the group prematurely. + } + } + + // Second pass: + // Now that everything that's going to deregister has done so, we can clean up and free the memory + NetworkInterfaceInfoOSX **p = &m->p->InterfaceList; + while (*p) + { + i = *p; + // If no longer active, delete interface from list and free memory + if (!i->Exists) + { + if (i->LastSeen == utc) i->LastSeen = utc - 1; + mDNSBool delete = (NumCacheRecordsForInterfaceID(m, i->ifinfo.InterfaceID) == 0) && (utc - i->LastSeen >= 60); + LogInfo("ClearInactiveInterfaces: %-13s %5s(%lu) %.6a InterfaceID %p(%p) %#a/%d Age %d%s", delete ? "Deleting" : "Holding", + i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, i, + &i->ifinfo.ip, CountMaskBits(&i->ifinfo.mask), utc - i->LastSeen, + i->ifinfo.InterfaceActive ? " (Primary)" : ""); #if APPLE_OSX_mDNSResponder - if (i->BPF_fd >= 0) CloseBPF(i); + if (i->BPF_fd >= 0) CloseBPF(i); #endif // APPLE_OSX_mDNSResponder - if (delete) - { - *p = i->next; - freeL("NetworkInterfaceInfoOSX", i); - continue; // After deleting this object, don't want to do the "p = &i->next;" thing at the end of the loop - } - } - p = &i->next; - } - return count; - } + if (delete) + { + *p = i->next; + freeL("NetworkInterfaceInfoOSX", i); + continue; // After deleting this object, don't want to do the "p = &i->next;" thing at the end of the loop + } + } + p = &i->next; + } + return count; +} mDNSlocal void AppendDNameListElem(DNameListElem ***List, mDNSu32 uid, domainname *name) - { - DNameListElem *dnle = (DNameListElem*) mallocL("DNameListElem/AppendDNameListElem", sizeof(DNameListElem)); - if (!dnle) LogMsg("ERROR: AppendDNameListElem: memory exhausted"); - else - { - dnle->next = mDNSNULL; - dnle->uid = uid; - AssignDomainName(&dnle->name, name); - **List = dnle; - *List = &dnle->next; - } - } +{ + DNameListElem *dnle = (DNameListElem*) mallocL("DNameListElem/AppendDNameListElem", sizeof(DNameListElem)); + if (!dnle) LogMsg("ERROR: AppendDNameListElem: memory exhausted"); + else + { + dnle->next = mDNSNULL; + dnle->uid = uid; + AssignDomainName(&dnle->name, name); + **List = dnle; + *List = &dnle->next; + } +} mDNSlocal int compare_dns_configs(const void *aa, const void *bb) - { - dns_resolver_t *a = *(dns_resolver_t**)aa; - dns_resolver_t *b = *(dns_resolver_t**)bb; - - return (a->search_order < b->search_order) ? -1 : (a->search_order == b->search_order) ? 0 : 1; - } +{ + dns_resolver_t *a = *(dns_resolver_t**)aa; + dns_resolver_t *b = *(dns_resolver_t**)bb; + + return (a->search_order < b->search_order) ? -1 : (a->search_order == b->search_order) ? 0 : 1; +} + +mDNSlocal void UpdateSearchDomainHash(mDNS *const m, MD5_CTX *sdc, char *domain, mDNSInterfaceID InterfaceID) +{ + char *buf = "."; + mDNSu32 scopeid = 0; + char ifid_buf[16]; + + if (domain) + buf = domain; + // + // Hash the search domain name followed by the InterfaceID. + // As we have scoped search domains, we also included InterfaceID. If either of them change, + // we will detect it. Even if the order of them change, we will detect it. + // + // Note: We have to handle a few of these tricky cases. + // + // 1) Current: com, apple.com Changing to: comapple.com + // 2) Current: a.com,b.com Changing to a.comb.com + // 3) Current: a.com,b.com (ifid 8), Changing to a.com8b.com (ifid 8) + // 4) Current: a.com (ifid 12), Changing to a.com1 (ifid: 2) + // + // There are more variants of the above. The key thing is if we include the null in each case + // at the end of name and the InterfaceID, it will prevent a new name (which can't include + // NULL as part of the name) to be mistakenly thought of as a old name. + + scopeid = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNStrue); + // mDNS_snprintf always null terminates + if (mDNS_snprintf(ifid_buf, sizeof(ifid_buf), "%u", scopeid) >= sizeof(ifid_buf)) + LogMsg("UpdateSearchDomainHash: mDNS_snprintf failed for scopeid %u", scopeid); + + LogInfo("UpdateSearchDomainHash: buf %s, ifid_buf %s", buf, ifid_buf); + MD5_Update(sdc, buf, strlen(buf) + 1); + MD5_Update(sdc, ifid_buf, strlen(ifid_buf) + 1); +} + +mDNSlocal void FinalizeSearchDomainHash(mDNS *const m, MD5_CTX *sdc) +{ + mDNSu8 md5_hash[MD5_LEN]; + + MD5_Final(md5_hash, sdc); + + if (memcmp(md5_hash, m->SearchDomainsHash, MD5_LEN)) + { + // If the hash is different, either the search domains have changed or + // the ordering between them has changed. Restart the questions that + // would be affected by this. + LogInfo("FinalizeSearchDomains: The hash is different"); + memcpy(m->SearchDomainsHash, md5_hash, MD5_LEN); + RetrySearchDomainQuestions(m); + } + else { LogInfo("FinalizeSearchDomains: The hash is same"); } +} // ConfigResolvers is called twice - once to parse the "scoped_resolver" list and second time to parse the "resolver" list. // "scoped_resolver" has entries that should only be used for "scoped_questions" (for questions that specify an interface index @@ -4896,1100 +4986,1128 @@ mDNSlocal int compare_dns_configs(const void *aa, const void *bb) // Before "scoped_resolver" was introduced, the entries in "resolver" list can contain options like "interface=en0" which // was meant to scope the query (non-scoped queries) to a specific interface. We still support this option. On top of that, // we also support a new way of specifying the interface index as described above. -mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSBool scope, mDNSBool setsearch, mDNSBool setservers) - { - int i, j; - domainname d; +mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSBool scope, mDNSBool setsearch, mDNSBool setservers, MD5_CTX *sdc, mDNSu16 resGroupID) +{ + int i, j; + domainname d; #if DNSINFO_VERSION >= 20091104 - dns_resolver_t **resolver = scope ? config->scoped_resolver : config->resolver; - int nresolvers = scope ? config->n_scoped_resolver : config->n_resolver; + dns_resolver_t **resolver = scope ? config->scoped_resolver : config->resolver; + int nresolvers = scope ? config->n_scoped_resolver : config->n_resolver; #else - (void) scope; // unused - dns_resolver_t **resolver = config->resolver; - int nresolvers = config->n_resolver; + (void) scope; // unused + dns_resolver_t **resolver = config->resolver; + int nresolvers = config->n_resolver; #endif - if (setsearch && !scope && nresolvers) - { - // Due to the vagaries of Apple's SystemConfiguration and dnsinfo.h APIs, if there are no search domains - // listed, then you're supposed to interpret the "domain" field as also being the search domain, but if - // there *are* search domains listed, then you're supposed to ignore the "domain" field completely and - // instead use the search domain list as the sole authority for what domains to search and in what order - // (and the domain from the "domain" field will also appear somewhere in that list). - // Also, all search domains get added to the search list for resolver[0], so the domains and/or - // search lists for other resolvers in the list need to be ignored. - // - // Note: Starting DNSINFO_VERSION 20091104, search list is present only in the first resolver (resolver 0). - // i.e., n_search for the first resolver is always non-zero. We don't guard it with #ifs for better readability - - if (resolver[0]->n_search == 0) - { - LogInfo("ConfigResolvers: (%s) configuring zeroth domain as search list %s", scope ? "Scoped" : "Non-scoped", resolver[0]->domain); - mDNS_AddSearchDomain_CString(resolver[0]->domain, mDNSNULL); - } - else - { - for (i = 0; i < resolver[0]->n_search; i++) - { - LogInfo("ConfigResolvers: (%s) configuring search list %s", scope ? "Scoped" : "Non-scoped", resolver[0]->search[i]); - mDNS_AddSearchDomain_CString(resolver[0]->search[i], mDNSNULL); - } - } - } - - // scoped search domains are set below. If neither scoped nor setting servers, we have nothing to do - if (!scope && !setservers) return; - - // For the "default" resolver ("resolver #1") the "domain" value is bogus and we need to ignore it. - // e.g. the default resolver's "domain" value might say "apple.com", which indicates that this resolver - // is only for names that fall under "apple.com", but that's not correct. Actually the default resolver is - // for all names not covered by a more specific resolver (i.e. its domain should be ".", the root domain). - // - // Note: Starting DNSINFO_VERSION 20091104, domain value of this first resolver (resolver 0) is always NULL. - // We don't guard it with #ifs for better readability - // - if ((nresolvers != 0) && resolver[0]->domain) - resolver[0]->domain[0] = 0; // don't stop pointing at the memory, just change the first byte - - qsort(resolver, nresolvers, sizeof(dns_resolver_t*), compare_dns_configs); - - for (i = 0; i < nresolvers; i++) - { - int n; - dns_resolver_t *r = resolver[i]; - mDNSInterfaceID interface = mDNSInterface_Any; - int disabled = 0; - - LogInfo("ConfigResolvers: %s resolver[%d] domain %s n_nameserver %d", scope ? "Scoped" : "", i, r->domain, r->n_nameserver); - - // On Tiger, dnsinfo entries for mDNS domains have port 5353, the mDNS port. Ignore them. - // Note: Unlike the BSD Sockets APIs (where TCP and UDP port numbers are universally in network byte order) - // in Apple's "dnsinfo.h" API the port number is declared to be a "uint16_t in host byte order" - // We also don't need to do any more work if there are no nameserver addresses - if (r->port == 5353 || r->n_nameserver == 0) - { - char *opt = r->options; - if (opt && !strncmp(opt, "mdns", strlen(opt))) - { - if (!MakeDomainNameFromDNSNameString(&d, r->domain)) - { LogMsg("ConfigResolvers: config->resolver[%d] bad domain %s", i, r->domain); continue; } - mDNS_AddMcastResolver(m, &d, interface, r->timeout); - } - continue; - } - - - if (!r->domain || !*r->domain) d.c[0] = 0; - else if (!MakeDomainNameFromDNSNameString(&d, r->domain)) - { LogMsg("ConfigResolvers: config->resolver[%d] bad domain %s", i, r->domain); continue; } - - // DNS server option parsing - if (r->options != NULL) - { - char *nextOption = r->options; - char *currentOption = NULL; - while ((currentOption = strsep(&nextOption, " ")) != NULL && currentOption[0] != 0) - { - // The option may be in the form of interface=xxx where xxx is an interface name. - if (strncmp(currentOption, kInterfaceSpecificOption, sizeof(kInterfaceSpecificOption) - 1) == 0) - { - NetworkInterfaceInfoOSX *ni; - char ifname[IF_NAMESIZE+1]; - mDNSu32 ifindex = 0; - // If something goes wrong finding the interface, create the server entry anyhow but mark it as disabled. - // This allows us to block these special queries from going out on the wire. - strlcpy(ifname, currentOption + sizeof(kInterfaceSpecificOption)-1, sizeof(ifname)); - ifindex = if_nametoindex(ifname); - if (ifindex == 0) { disabled = 1; LogMsg("ConfigResolvers: RegisterSplitDNS interface specific - interface %s not found", ifname); continue; } - LogInfo("ConfigResolvers: interface specific entry: %s on %s (%d)", r->domain, ifname, ifindex); - // Find the interface. Can't use mDNSPlatformInterfaceIDFromInterfaceIndex - // because that will call mDNSMacOSXNetworkChanged if the interface doesn't exist - for (ni = m->p->InterfaceList; ni; ni = ni->next) - if (ni->ifinfo.InterfaceID && ni->scope_id == ifindex) break; - if (ni != NULL) interface = ni->ifinfo.InterfaceID; - if (interface == mDNSNULL) - { - disabled = 1; - LogMsg("ConfigResolvers: RegisterSplitDNS interface specific - index %d (%s) not found", ifindex, ifname); - continue; - } - } - } - } - - // flags and if_index are defined only from this DNSINFO_VERSION onwards. - // Parse the interface index if we have not already parsed one above. + if (setsearch && !scope && nresolvers) + { + // Due to the vagaries of Apple's SystemConfiguration and dnsinfo.h APIs, if there are no search domains + // listed, then you're supposed to interpret the "domain" field as also being the search domain, but if + // there *are* search domains listed, then you're supposed to ignore the "domain" field completely and + // instead use the search domain list as the sole authority for what domains to search and in what order + // (and the domain from the "domain" field will also appear somewhere in that list). + // Also, all search domains get added to the search list for resolver[0], so the domains and/or + // search lists for other resolvers in the list need to be ignored. + // + // Note: Starting DNSINFO_VERSION 20091104, search list is present only in the first resolver (resolver 0). + // i.e., n_search for the first resolver is always non-zero. We don't guard it with #ifs for better readability + + if (resolver[0]->n_search == 0) + { + LogInfo("ConfigResolvers: (%s) configuring zeroth domain as search list %s", scope ? "Scoped" : "Non-scoped", resolver[0]->domain); + UpdateSearchDomainHash(m, sdc, resolver[0]->domain, NULL); + mDNS_AddSearchDomain_CString(resolver[0]->domain, mDNSNULL); + } + else + { + for (i = 0; i < resolver[0]->n_search; i++) + { + LogInfo("ConfigResolvers: (%s) configuring search list %s", scope ? "Scoped" : "Non-scoped", resolver[0]->search[i]); + UpdateSearchDomainHash(m, sdc, resolver[0]->search[i], NULL); + mDNS_AddSearchDomain_CString(resolver[0]->search[i], mDNSNULL); + } + } + } + + // scoped search domains are set below. If neither scoped nor setting servers, we have nothing to do + if (!scope && !setservers) return; + + // For the "default" resolver ("resolver #1") the "domain" value is bogus and we need to ignore it. + // e.g. the default resolver's "domain" value might say "apple.com", which indicates that this resolver + // is only for names that fall under "apple.com", but that's not correct. Actually the default resolver is + // for all names not covered by a more specific resolver (i.e. its domain should be ".", the root domain). + // + // Note: Starting DNSINFO_VERSION 20091104, domain value of this first resolver (resolver 0) is always NULL. + // We don't guard it with #ifs for better readability + // + if ((nresolvers != 0) && resolver[0]->domain) + resolver[0]->domain[0] = 0; // don't stop pointing at the memory, just change the first byte + + qsort(resolver, nresolvers, sizeof(dns_resolver_t*), compare_dns_configs); + + for (i = 0; i < nresolvers; i++) + { + int n; + dns_resolver_t *r = resolver[i]; + mDNSInterfaceID interface = mDNSInterface_Any; + int disabled = 0; + + LogInfo("ConfigResolvers: %s resolver[%d] domain %s n_nameserver %d", scope ? "Scoped" : "", i, r->domain, r->n_nameserver); + + // On Tiger, dnsinfo entries for mDNS domains have port 5353, the mDNS port. Ignore them. + // Note: Unlike the BSD Sockets APIs (where TCP and UDP port numbers are universally in network byte order) + // in Apple's "dnsinfo.h" API the port number is declared to be a "uint16_t in host byte order" + // We also don't need to do any more work if there are no nameserver addresses + if (r->port == 5353 || r->n_nameserver == 0) + { + char *opt = r->options; + if (opt && !strncmp(opt, "mdns", strlen(opt))) + { + if (!MakeDomainNameFromDNSNameString(&d, r->domain)) + { LogMsg("ConfigResolvers: config->resolver[%d] bad domain %s", i, r->domain); continue; } + mDNS_AddMcastResolver(m, &d, interface, r->timeout); + } + continue; + } + + + if (!r->domain || !*r->domain) d.c[0] = 0; + else if (!MakeDomainNameFromDNSNameString(&d, r->domain)) + { LogMsg("ConfigResolvers: config->resolver[%d] bad domain %s", i, r->domain); continue; } + + // DNS server option parsing + if (r->options != NULL) + { + char *nextOption = r->options; + char *currentOption = NULL; + while ((currentOption = strsep(&nextOption, " ")) != NULL && currentOption[0] != 0) + { + // The option may be in the form of interface=xxx where xxx is an interface name. + if (strncmp(currentOption, kInterfaceSpecificOption, sizeof(kInterfaceSpecificOption) - 1) == 0) + { + NetworkInterfaceInfoOSX *ni; + char ifname[IF_NAMESIZE+1]; + mDNSu32 ifindex = 0; + // If something goes wrong finding the interface, create the server entry anyhow but mark it as disabled. + // This allows us to block these special queries from going out on the wire. + strlcpy(ifname, currentOption + sizeof(kInterfaceSpecificOption)-1, sizeof(ifname)); + ifindex = if_nametoindex(ifname); + if (ifindex == 0) { disabled = 1; LogMsg("ConfigResolvers: RegisterSplitDNS interface specific - interface %s not found", ifname); continue; } + LogInfo("ConfigResolvers: interface specific entry: %s on %s (%d)", r->domain, ifname, ifindex); + // Find the interface. Can't use mDNSPlatformInterfaceIDFromInterfaceIndex + // because that will call mDNSMacOSXNetworkChanged if the interface doesn't exist + for (ni = m->p->InterfaceList; ni; ni = ni->next) + if (ni->ifinfo.InterfaceID && ni->scope_id == ifindex) break; + if (ni != NULL) interface = ni->ifinfo.InterfaceID; + if (interface == mDNSNULL) + { + disabled = 1; + LogMsg("ConfigResolvers: RegisterSplitDNS interface specific - index %d (%s) not found", ifindex, ifname); + continue; + } + } + } + } + + // flags and if_index are defined only from this DNSINFO_VERSION onwards. + // Parse the interface index if we have not already parsed one above. #if DNSINFO_VERSION >= 20091104 - if ((interface == mDNSInterface_Any) && (r->if_index != 0)) - { - NetworkInterfaceInfoOSX *ni; - interface = mDNSNULL; - for (ni = m->p->InterfaceList; ni; ni = ni->next) - if (ni->ifinfo.InterfaceID && ni->scope_id == r->if_index) break; - if (ni != NULL) interface = ni->ifinfo.InterfaceID; - if (interface == mDNSNULL) - { - disabled = 1; - LogMsg("ConfigResolvers: interface specific index %d not found", r->if_index); - continue; - } - } + if ((interface == mDNSInterface_Any) && (r->if_index != 0)) + { + NetworkInterfaceInfoOSX *ni; + interface = mDNSNULL; + for (ni = m->p->InterfaceList; ni; ni = ni->next) + if (ni->ifinfo.InterfaceID && ni->scope_id == r->if_index) break; + if (ni != NULL) interface = ni->ifinfo.InterfaceID; + if (interface == mDNSNULL) + { + disabled = 1; + LogMsg("ConfigResolvers: interface specific index %d not found", r->if_index); + continue; + } + } #endif - if (setsearch) - { - // For non-scoped resolvers unlike scoped resolvers, only zeroth resolver has search lists if any. For scoped - // resolvers, we need to parse all the entries. - if (scope) - { - for (j = 0; j < resolver[i]->n_search; j++) - { - LogInfo("ConfigResolvers: (%s) configuring search list %s, Interface %p", scope ? "Scoped" : "Non-scoped", resolver[i]->search[j], interface); - mDNS_AddSearchDomain_CString(resolver[i]->search[j], interface); - } - // Parse other scoped resolvers for search lists - if (!setservers) continue; - } - } - - for (n = 0; n < r->n_nameserver; n++) - if (r->nameserver[n]->sa_family == AF_INET || r->nameserver[n]->sa_family == AF_INET6) - { - mDNSAddr saddr; - // mDNSAddr saddr = { mDNSAddrType_IPv4, { { { 192, 168, 1, 1 } } } }; // for testing - if (SetupAddr(&saddr, r->nameserver[n])) LogMsg("RegisterSplitDNS: bad IP address"); - else - { - mDNSBool cellIntf = mDNSfalse; - mDNSBool scopedDNS = mDNSfalse; - DNSServer *s; + if (setsearch) + { + // For non-scoped resolvers unlike scoped resolvers, only zeroth resolver has search lists if any. For scoped + // resolvers, we need to parse all the entries. + if (scope) + { + for (j = 0; j < resolver[i]->n_search; j++) + { + LogInfo("ConfigResolvers: (%s) configuring search list %s, Interface %p", scope ? "Scoped" : "Non-scoped", resolver[i]->search[j], interface); + UpdateSearchDomainHash(m, sdc, resolver[i]->search[j], interface); + mDNS_AddSearchDomain_CString(resolver[i]->search[j], interface); + } + // Parse other scoped resolvers for search lists + if (!setservers) continue; + } + } + + // Each scoped resolver gets its own ID (i.e., they are in their own group) so that responses from the + // scoped resolver are not used by other non-scoped or scoped resolvers. + if (scope) resGroupID++; + + for (n = 0; n < r->n_nameserver; n++) + if (r->nameserver[n]->sa_family == AF_INET || r->nameserver[n]->sa_family == AF_INET6) + { + mDNSAddr saddr; + // mDNSAddr saddr = { mDNSAddrType_IPv4, { { { 192, 168, 1, 1 } } } }; // for testing + if (SetupAddr(&saddr, r->nameserver[n])) LogMsg("RegisterSplitDNS: bad IP address"); + else + { + mDNSBool cellIntf = mDNSfalse; + mDNSBool scopedDNS = mDNSfalse; + DNSServer *s; #if DNSINFO_VERSION >= 20091104 - // By setting scoped, this DNSServer can only be picked if the right interfaceID - // is given in the question. - if (scope && (r->flags & DNS_RESOLVER_FLAGS_SCOPED) && (interface == mDNSNULL)) - LogMsg("ConfigResolvers: ERROR: scoped is set but if_index %d is invalid for DNSServer %#a:%d", - r->if_index, &saddr, mDNSVal16(r->port ? mDNSOpaque16fromIntVal(r->port) : UnicastDNSPort)); - else - scopedDNS = (scope && (r->flags & DNS_RESOLVER_FLAGS_SCOPED)) ? mDNStrue : mDNSfalse; + // By setting scoped, this DNSServer can only be picked if the right interfaceID + // is given in the question. + if (scope && (r->flags & DNS_RESOLVER_FLAGS_SCOPED) && (interface == mDNSNULL)) + LogMsg("ConfigResolvers: ERROR: scoped is set but if_index %d is invalid for DNSServer %#a:%d", + r->if_index, &saddr, mDNSVal16(r->port ? mDNSOpaque16fromIntVal(r->port) : UnicastDNSPort)); + else + scopedDNS = (scope && (r->flags & DNS_RESOLVER_FLAGS_SCOPED)) ? mDNStrue : mDNSfalse; #endif -#if DNSINFO_VERSION >= 20110420 - cellIntf = r->reach_flags & kSCNetworkReachabilityFlagsIsWWAN; +#if (DNSINFO_VERSION >= 20110420) && TARGET_OS_IPHONE + cellIntf = r->reach_flags & kSCNetworkReachabilityFlagsIsWWAN; #endif - // The timeout value is for all the DNS servers in a given resolver, hence we pass - // the timeout value only for the first DNSServer. If we don't have a value in the - // resolver, then use the core's default value - // - // Note: this assumes that when the core picks a list of DNSServers for a question, - // it takes the sum of all the timeout values for all DNS servers. By doing this, it - // tries all the DNS servers in a specified timeout - s = mDNS_AddDNSServer(m, &d, interface, &saddr, r->port ? mDNSOpaque16fromIntVal(r->port) : UnicastDNSPort, scopedDNS, - (n == 0 ? (r->timeout ? r->timeout : DEFAULT_UDNS_TIMEOUT) : 0), cellIntf); - if (s) - { - if (disabled) s->teststate = DNSServer_Disabled; - LogInfo("ConfigResolvers: DNS server %#a:%d for domain %##s from slot %d, %d", - &s->addr, mDNSVal16(s->port), d.c, i, n); - } - } - } - } - } + // The timeout value is for all the DNS servers in a given resolver, hence we pass + // the timeout value only for the first DNSServer. If we don't have a value in the + // resolver, then use the core's default value + // + // Note: this assumes that when the core picks a list of DNSServers for a question, + // it takes the sum of all the timeout values for all DNS servers. By doing this, it + // tries all the DNS servers in a specified timeout + s = mDNS_AddDNSServer(m, &d, interface, &saddr, r->port ? mDNSOpaque16fromIntVal(r->port) : UnicastDNSPort, scopedDNS, + (n == 0 ? (r->timeout ? r->timeout : DEFAULT_UDNS_TIMEOUT) : 0), cellIntf, resGroupID); + if (s) + { + if (disabled) s->teststate = DNSServer_Disabled; + LogInfo("ConfigResolvers: DNS server %#a:%d for domain %##s from slot %d, %d", + &s->addr, mDNSVal16(s->port), d.c, i, n); + } + } + } + } +} mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **RegDomains, DNameListElem **BrowseDomains) - { - int i; - char buf[MAX_ESCAPED_DOMAIN_NAME]; // Max legal C-string name, including terminating NUL - domainname d; - - // Need to set these here because we need to do this even if SCDynamicStoreCreate() or SCDynamicStoreCopyValue() below don't succeed - if (fqdn) fqdn->c[0] = 0; - if (RegDomains ) *RegDomains = NULL; - if (BrowseDomains) *BrowseDomains = NULL; - - LogInfo("mDNSPlatformSetDNSConfig:%s%s%s%s%s", - setservers ? " setservers" : "", - setsearch ? " setsearch" : "", - fqdn ? " fqdn" : "", - RegDomains ? " RegDomains" : "", - BrowseDomains ? " BrowseDomains" : ""); - - // Add the inferred address-based configuration discovery domains - // (should really be in core code I think, not platform-specific) - if (setsearch) - { - struct ifaddrs *ifa = mDNSNULL; - struct sockaddr_in saddr; - mDNSPlatformMemZero(&saddr, sizeof(saddr)); - saddr.sin_len = sizeof(saddr); - saddr.sin_family = AF_INET; - saddr.sin_port = 0; - saddr.sin_addr.s_addr = *(in_addr_t *)&m->Router.ip.v4; - - // Don't add any reverse-IP search domains if doing the WAB bootstrap queries would cause dial-on-demand connection initiation - if (!AddrRequiresPPPConnection((struct sockaddr *)&saddr)) ifa = myGetIfAddrs(1); - - while (ifa) - { - mDNSAddr a, n; - if (ifa->ifa_addr->sa_family == AF_INET && - ifa->ifa_netmask && - !(ifa->ifa_flags & IFF_LOOPBACK) && - !SetupAddr(&a, ifa->ifa_addr) && - !mDNSv4AddressIsLinkLocal(&a.ip.v4) ) - { - // Apparently it's normal for the sa_family of an ifa_netmask to sometimes be incorrect, so we explicitly fix it here before calling SetupAddr - // getifaddrs is returning invalid netmask family for fw0 and vmnet - ifa->ifa_netmask->sa_family = ifa->ifa_addr->sa_family; // Make sure ifa_netmask->sa_family is set correctly - SetupAddr(&n, ifa->ifa_netmask); - // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code - mDNS_snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa.", a.ip.v4.b[3] & n.ip.v4.b[3], - a.ip.v4.b[2] & n.ip.v4.b[2], - a.ip.v4.b[1] & n.ip.v4.b[1], - a.ip.v4.b[0] & n.ip.v4.b[0]); - mDNS_AddSearchDomain_CString(buf, mDNSNULL); - } - ifa = ifa->ifa_next; - } - } +{ + int i; + char buf[MAX_ESCAPED_DOMAIN_NAME]; // Max legal C-string name, including terminating NUL + domainname d; + MD5_CTX sdc; // search domain context + static mDNSu16 resolverGroupID = 0; + + // Need to set these here because we need to do this even if SCDynamicStoreCreate() or SCDynamicStoreCopyValue() below don't succeed + if (fqdn) fqdn->c[0] = 0; + if (RegDomains ) *RegDomains = NULL; + if (BrowseDomains) *BrowseDomains = NULL; + + LogInfo("mDNSPlatformSetDNSConfig:%s%s%s%s%s", + setservers ? " setservers" : "", + setsearch ? " setsearch" : "", + fqdn ? " fqdn" : "", + RegDomains ? " RegDomains" : "", + BrowseDomains ? " BrowseDomains" : ""); + + if (setsearch) MD5_Init(&sdc); + + // Add the inferred address-based configuration discovery domains + // (should really be in core code I think, not platform-specific) + if (setsearch) + { + struct ifaddrs *ifa = mDNSNULL; + struct sockaddr_in saddr; + mDNSPlatformMemZero(&saddr, sizeof(saddr)); + saddr.sin_len = sizeof(saddr); + saddr.sin_family = AF_INET; + saddr.sin_port = 0; + saddr.sin_addr.s_addr = *(in_addr_t *)&m->Router.ip.v4; + + // Don't add any reverse-IP search domains if doing the WAB bootstrap queries would cause dial-on-demand connection initiation + if (!AddrRequiresPPPConnection((struct sockaddr *)&saddr)) ifa = myGetIfAddrs(1); + + while (ifa) + { + mDNSAddr a, n; + if (ifa->ifa_addr->sa_family == AF_INET && + ifa->ifa_netmask && + !(ifa->ifa_flags & IFF_LOOPBACK) && + !SetupAddr(&a, ifa->ifa_addr) && + !mDNSv4AddressIsLinkLocal(&a.ip.v4) ) + { + // Apparently it's normal for the sa_family of an ifa_netmask to sometimes be incorrect, so we explicitly fix it here before calling SetupAddr + // getifaddrs is returning invalid netmask family for fw0 and vmnet + ifa->ifa_netmask->sa_family = ifa->ifa_addr->sa_family; // Make sure ifa_netmask->sa_family is set correctly + SetupAddr(&n, ifa->ifa_netmask); + // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code + mDNS_snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa.", a.ip.v4.b[3] & n.ip.v4.b[3], + a.ip.v4.b[2] & n.ip.v4.b[2], + a.ip.v4.b[1] & n.ip.v4.b[1], + a.ip.v4.b[0] & n.ip.v4.b[0]); + UpdateSearchDomainHash(m, &sdc, buf, NULL); + mDNS_AddSearchDomain_CString(buf, mDNSNULL); + } + ifa = ifa->ifa_next; + } + } #ifndef MDNS_NO_DNSINFO - if (setservers || setsearch) - { - 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 - // 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)) - LogMsg("mDNSPlatformSetDNSConfig: Error: dns_configuration_copy returned NULL"); - } - else - { - LogInfo("mDNSPlatformSetDNSConfig: config->n_resolver = %d", config->n_resolver); + if (setservers || setsearch) + { + 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 + // 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)) + LogMsg("mDNSPlatformSetDNSConfig: Error: dns_configuration_copy returned NULL"); + } + else + { + LogInfo("mDNSPlatformSetDNSConfig: config->n_resolver = %d", config->n_resolver); #if APPLE_OSX_mDNSResponder - // Record the so-called "primary" domain, which we use as a hint to tell if the user is on a network set up - // by someone using Microsoft Active Directory using "local" as a private internal top-level domain - if (config->n_resolver && config->resolver[0]->domain && config->resolver[0]->n_nameserver && config->resolver[0]->nameserver[0]) - MakeDomainNameFromDNSNameString(&ActiveDirectoryPrimaryDomain, config->resolver[0]->domain); - else ActiveDirectoryPrimaryDomain.c[0] = 0; - //MakeDomainNameFromDNSNameString(&ActiveDirectoryPrimaryDomain, "test.local"); - ActiveDirectoryPrimaryDomainLabelCount = CountLabels(&ActiveDirectoryPrimaryDomain); - if (config->n_resolver && config->resolver[0]->n_nameserver && SameDomainName(SkipLeadingLabels(&ActiveDirectoryPrimaryDomain, ActiveDirectoryPrimaryDomainLabelCount - 1), &localdomain)) - SetupAddr(&ActiveDirectoryPrimaryDomainServer, config->resolver[0]->nameserver[0]); - else - { - AssignDomainName(&ActiveDirectoryPrimaryDomain, (const domainname *)""); - ActiveDirectoryPrimaryDomainLabelCount = 0; - ActiveDirectoryPrimaryDomainServer = zeroAddr; - } + // Record the so-called "primary" domain, which we use as a hint to tell if the user is on a network set up + // by someone using Microsoft Active Directory using "local" as a private internal top-level domain + if (config->n_resolver && config->resolver[0]->domain && config->resolver[0]->n_nameserver && config->resolver[0]->nameserver[0]) + MakeDomainNameFromDNSNameString(&ActiveDirectoryPrimaryDomain, config->resolver[0]->domain); + else ActiveDirectoryPrimaryDomain.c[0] = 0; + //MakeDomainNameFromDNSNameString(&ActiveDirectoryPrimaryDomain, "test.local"); + ActiveDirectoryPrimaryDomainLabelCount = CountLabels(&ActiveDirectoryPrimaryDomain); + if (config->n_resolver && config->resolver[0]->n_nameserver && SameDomainName(SkipLeadingLabels(&ActiveDirectoryPrimaryDomain, ActiveDirectoryPrimaryDomainLabelCount - 1), &localdomain)) + SetupAddr(&ActiveDirectoryPrimaryDomainServer, config->resolver[0]->nameserver[0]); + else + { + AssignDomainName(&ActiveDirectoryPrimaryDomain, (const domainname *)""); + ActiveDirectoryPrimaryDomainLabelCount = 0; + ActiveDirectoryPrimaryDomainServer = zeroAddr; + } #endif - ConfigResolvers(m, config, mDNSfalse, setsearch, setservers); + // With scoped DNS, we don't want to answer a non-scoped question using a scoped cache entry + // and vice-versa. As we compare resolverGroupID for matching cache entry with question, we need + // to make sure that they don't match. We ensure this by always bumping up resolverGroupID between + // the two calls to ConfigResolvers DNSServers for scoped and non-scoped can never have the + // same resolverGroupID. + // + // All non-scoped resolvers use the same resolverGroupID i.e, we treat them all equally. + ConfigResolvers(m, config, mDNSfalse, setsearch, setservers, &sdc, ++resolverGroupID); + resolverGroupID += config->n_resolver; #if DNSINFO_VERSION >= 20091104 - ConfigResolvers(m, config, mDNStrue, setsearch, setservers); + ConfigResolvers(m, config, mDNStrue, setsearch, setservers, &sdc, resolverGroupID); #endif - dns_configuration_free(config); - if (setsearch) RetrySearchDomainQuestions(m); - setservers = mDNSfalse; // Done these now -- no need to fetch the same data from SCDynamicStore - setsearch = mDNSfalse; - } - } + // Acking provides a hint that we processed this current configuration and + // we will use that from now on, assuming we don't get another one immediately + // after we return from here. + _dns_configuration_ack(config, "com.apple.mDNSResponder"); + dns_configuration_free(config); + if (setsearch) FinalizeSearchDomainHash(m, &sdc); + setservers = mDNSfalse; // Done these now -- no need to fetch the same data from SCDynamicStore + setsearch = mDNSfalse; + } + } #endif // MDNS_NO_DNSINFO - SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:mDNSPlatformSetDNSConfig"), NULL, NULL); - if (!store) - LogMsg("mDNSPlatformSetDNSConfig: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); - else - { - CFDictionaryRef ddnsdict = SCDynamicStoreCopyValue(store, NetworkChangedKey_DynamicDNS); - if (ddnsdict) - { - if (fqdn) - { - CFArrayRef fqdnArray = CFDictionaryGetValue(ddnsdict, CFSTR("HostNames")); - if (fqdnArray && CFArrayGetCount(fqdnArray) > 0) - { - // for now, we only look at the first array element. if we ever support multiple configurations, we will walk the list - CFDictionaryRef fqdnDict = CFArrayGetValueAtIndex(fqdnArray, 0); - if (fqdnDict && DictionaryIsEnabled(fqdnDict)) - { - CFStringRef name = CFDictionaryGetValue(fqdnDict, CFSTR("Domain")); - 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 (RegDomains) - { - CFArrayRef regArray = CFDictionaryGetValue(ddnsdict, CFSTR("RegistrationDomains")); - if (regArray && CFArrayGetCount(regArray) > 0) - { - CFDictionaryRef regDict = CFArrayGetValueAtIndex(regArray, 0); - if (regDict && DictionaryIsEnabled(regDict)) - { - CFStringRef name = CFDictionaryGetValue(regDict, CFSTR("Domain")); - if (name) - { - if (!CFStringGetCString(name, buf, sizeof(buf), kCFStringEncodingUTF8) || - !MakeDomainNameFromDNSNameString(&d, buf) || !d.c[0]) - LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS registration domain: %s", buf[0] ? buf : "(unknown)"); - else - { - debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS registration domain: %s", buf); - AppendDNameListElem(&RegDomains, 0, &d); - } - } - } - } - } - - if (BrowseDomains) - { - CFArrayRef browseArray = CFDictionaryGetValue(ddnsdict, CFSTR("BrowseDomains")); - if (browseArray) - { - for (i = 0; i < CFArrayGetCount(browseArray); i++) - { - CFDictionaryRef browseDict = CFArrayGetValueAtIndex(browseArray, i); - if (browseDict && DictionaryIsEnabled(browseDict)) - { - CFStringRef name = CFDictionaryGetValue(browseDict, CFSTR("Domain")); - if (name) - { - if (!CFStringGetCString(name, buf, sizeof(buf), kCFStringEncodingUTF8) || - !MakeDomainNameFromDNSNameString(&d, buf) || !d.c[0]) - LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS browsing domain: %s", buf[0] ? buf : "(unknown)"); - else - { - debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS browsing domain: %s", buf); - AppendDNameListElem(&BrowseDomains, 0, &d); - } - } - } - } - } - } - CFRelease(ddnsdict); - } - - if (RegDomains) - { - CFDictionaryRef btmm = SCDynamicStoreCopyValue(store, NetworkChangedKey_BackToMyMac); - if (btmm) - { - CFIndex size = CFDictionaryGetCount(btmm); - const void *key[size]; - const void *val[size]; - CFDictionaryGetKeysAndValues(btmm, key, val); - for (i = 0; i < size; i++) - { - LogInfo("BackToMyMac %d", i); - if (!CFStringGetCString(key[i], buf, sizeof(buf), kCFStringEncodingUTF8)) - LogMsg("Can't read BackToMyMac %d key %s", i, buf); - else - { - mDNSu32 uid = atoi(buf); - if (!CFStringGetCString(val[i], buf, sizeof(buf), kCFStringEncodingUTF8)) - LogMsg("Can't read BackToMyMac %d val %s", i, buf); - else if (MakeDomainNameFromDNSNameString(&d, buf) && d.c[0]) - { - LogInfo("BackToMyMac %d %d %##s", i, uid, d.c); - AppendDNameListElem(&RegDomains, uid, &d); - } - } - } - CFRelease(btmm); - } - } - - if (setservers || setsearch) - { - CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_DNS); - if (dict) - { - if (setservers) - { - CFArrayRef values = CFDictionaryGetValue(dict, kSCPropNetDNSServerAddresses); - if (values) - { - LogInfo("DNS Server Address values: %d", (int)CFArrayGetCount(values)); - for (i = 0; i < CFArrayGetCount(values); i++) - { - CFStringRef s = CFArrayGetValueAtIndex(values, i); - mDNSAddr addr = { mDNSAddrType_IPv4, { { { 0 } } } }; - if (s && CFStringGetCString(s, buf, 256, kCFStringEncodingUTF8) && - inet_aton(buf, (struct in_addr *) &addr.ip.v4)) - { - LogInfo("Adding DNS server from dict: %s", buf); - mDNS_AddDNSServer(m, mDNSNULL, mDNSInterface_Any, &addr, UnicastDNSPort, mDNSfalse, 0, mDNSfalse); - } - } - } - else LogInfo("No DNS Server Address values"); - } - if (setsearch) - { - // Add the manual and/or DHCP-dicovered search domains - CFArrayRef searchDomains = CFDictionaryGetValue(dict, kSCPropNetDNSSearchDomains); - if (searchDomains) - { - for (i = 0; i < CFArrayGetCount(searchDomains); i++) - { - CFStringRef s = CFArrayGetValueAtIndex(searchDomains, i); - if (s && CFStringGetCString(s, buf, sizeof(buf), kCFStringEncodingUTF8)) - mDNS_AddSearchDomain_CString(buf, mDNSNULL); - } - } - else // No kSCPropNetDNSSearchDomains, so use kSCPropNetDNSDomainName - { - // Due to the vagaries of Apple's SystemConfiguration and dnsinfo.h APIs, if there are no search domains - // listed, then you're supposed to interpret the "domain" field as also being the search domain, but if - // there *are* search domains listed, then you're supposed to ignore the "domain" field completely and - // instead use the search domain list as the sole authority for what domains to search and in what order - // (and the domain from the "domain" field will also appear somewhere in that list). - CFStringRef string = CFDictionaryGetValue(dict, kSCPropNetDNSDomainName); - if (string && CFStringGetCString(string, buf, sizeof(buf), kCFStringEncodingUTF8)) - mDNS_AddSearchDomain_CString(buf, mDNSNULL); - } - RetrySearchDomainQuestions(m); - } - CFRelease(dict); - } - } - CFRelease(store); - } - } + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:mDNSPlatformSetDNSConfig"), NULL, NULL); + if (!store) + LogMsg("mDNSPlatformSetDNSConfig: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); + else + { + CFDictionaryRef ddnsdict = SCDynamicStoreCopyValue(store, NetworkChangedKey_DynamicDNS); + if (ddnsdict) + { + if (fqdn) + { + CFArrayRef fqdnArray = CFDictionaryGetValue(ddnsdict, CFSTR("HostNames")); + if (fqdnArray && CFArrayGetCount(fqdnArray) > 0) + { + // for now, we only look at the first array element. if we ever support multiple configurations, we will walk the list + CFDictionaryRef fqdnDict = CFArrayGetValueAtIndex(fqdnArray, 0); + if (fqdnDict && DictionaryIsEnabled(fqdnDict)) + { + CFStringRef name = CFDictionaryGetValue(fqdnDict, CFSTR("Domain")); + 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 (RegDomains) + { + CFArrayRef regArray = CFDictionaryGetValue(ddnsdict, CFSTR("RegistrationDomains")); + if (regArray && CFArrayGetCount(regArray) > 0) + { + CFDictionaryRef regDict = CFArrayGetValueAtIndex(regArray, 0); + if (regDict && DictionaryIsEnabled(regDict)) + { + CFStringRef name = CFDictionaryGetValue(regDict, CFSTR("Domain")); + if (name) + { + if (!CFStringGetCString(name, buf, sizeof(buf), kCFStringEncodingUTF8) || + !MakeDomainNameFromDNSNameString(&d, buf) || !d.c[0]) + LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS registration domain: %s", buf[0] ? buf : "(unknown)"); + else + { + debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS registration domain: %s", buf); + AppendDNameListElem(&RegDomains, 0, &d); + } + } + } + } + } + + if (BrowseDomains) + { + CFArrayRef browseArray = CFDictionaryGetValue(ddnsdict, CFSTR("BrowseDomains")); + if (browseArray) + { + for (i = 0; i < CFArrayGetCount(browseArray); i++) + { + CFDictionaryRef browseDict = CFArrayGetValueAtIndex(browseArray, i); + if (browseDict && DictionaryIsEnabled(browseDict)) + { + CFStringRef name = CFDictionaryGetValue(browseDict, CFSTR("Domain")); + if (name) + { + if (!CFStringGetCString(name, buf, sizeof(buf), kCFStringEncodingUTF8) || + !MakeDomainNameFromDNSNameString(&d, buf) || !d.c[0]) + LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS browsing domain: %s", buf[0] ? buf : "(unknown)"); + else + { + debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS browsing domain: %s", buf); + AppendDNameListElem(&BrowseDomains, 0, &d); + } + } + } + } + } + } + CFRelease(ddnsdict); + } + + if (RegDomains) + { + CFDictionaryRef btmm = SCDynamicStoreCopyValue(store, NetworkChangedKey_BackToMyMac); + if (btmm) + { + CFIndex size = CFDictionaryGetCount(btmm); + const void *key[size]; + const void *val[size]; + CFDictionaryGetKeysAndValues(btmm, key, val); + for (i = 0; i < size; i++) + { + LogInfo("BackToMyMac %d", i); + if (!CFStringGetCString(key[i], buf, sizeof(buf), kCFStringEncodingUTF8)) + LogMsg("Can't read BackToMyMac %d key %s", i, buf); + else + { + mDNSu32 uid = atoi(buf); + if (!CFStringGetCString(val[i], buf, sizeof(buf), kCFStringEncodingUTF8)) + LogMsg("Can't read BackToMyMac %d val %s", i, buf); + else if (MakeDomainNameFromDNSNameString(&d, buf) && d.c[0]) + { + LogInfo("BackToMyMac %d %d %##s", i, uid, d.c); + AppendDNameListElem(&RegDomains, uid, &d); + } + } + } + CFRelease(btmm); + } + } + + if (setservers || setsearch) + { + CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_DNS); + if (dict) + { + if (setservers) + { + CFArrayRef values = CFDictionaryGetValue(dict, kSCPropNetDNSServerAddresses); + if (values) + { + LogInfo("DNS Server Address values: %d", (int)CFArrayGetCount(values)); + for (i = 0; i < CFArrayGetCount(values); i++) + { + CFStringRef s = CFArrayGetValueAtIndex(values, i); + mDNSAddr addr = { mDNSAddrType_IPv4, { { { 0 } } } }; + if (s && CFStringGetCString(s, buf, 256, kCFStringEncodingUTF8) && + inet_aton(buf, (struct in_addr *) &addr.ip.v4)) + { + LogInfo("Adding DNS server from dict: %s", buf); + mDNS_AddDNSServer(m, mDNSNULL, mDNSInterface_Any, &addr, UnicastDNSPort, mDNSfalse, 0, mDNSfalse, 0); + } + } + } + else LogInfo("No DNS Server Address values"); + } + if (setsearch) + { + // Add the manual and/or DHCP-dicovered search domains + CFArrayRef searchDomains = CFDictionaryGetValue(dict, kSCPropNetDNSSearchDomains); + if (searchDomains) + { + for (i = 0; i < CFArrayGetCount(searchDomains); i++) + { + CFStringRef s = CFArrayGetValueAtIndex(searchDomains, i); + if (s && CFStringGetCString(s, buf, sizeof(buf), kCFStringEncodingUTF8)) + { + UpdateSearchDomainHash(m, &sdc, buf, NULL); + mDNS_AddSearchDomain_CString(buf, mDNSNULL); + } + } + } + else // No kSCPropNetDNSSearchDomains, so use kSCPropNetDNSDomainName + { + // Due to the vagaries of Apple's SystemConfiguration and dnsinfo.h APIs, if there are no search domains + // listed, then you're supposed to interpret the "domain" field as also being the search domain, but if + // there *are* search domains listed, then you're supposed to ignore the "domain" field completely and + // instead use the search domain list as the sole authority for what domains to search and in what order + // (and the domain from the "domain" field will also appear somewhere in that list). + CFStringRef string = CFDictionaryGetValue(dict, kSCPropNetDNSDomainName); + if (string && CFStringGetCString(string, buf, sizeof(buf), kCFStringEncodingUTF8)) + { + UpdateSearchDomainHash(m, &sdc, buf, NULL); + mDNS_AddSearchDomain_CString(buf, mDNSNULL); + } + } + FinalizeSearchDomainHash(m, &sdc); + } + CFRelease(dict); + } + } + CFRelease(store); + } +} mDNSexport mStatus mDNSPlatformGetPrimaryInterface(mDNS *const m, mDNSAddr *v4, mDNSAddr *v6, mDNSAddr *r) - { - char buf[256]; - (void)m; // Unused - - SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:mDNSPlatformGetPrimaryInterface"), NULL, NULL); - if (!store) - LogMsg("mDNSPlatformGetPrimaryInterface: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); - else - { - CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_IPv4); - if (dict) - { - r->type = mDNSAddrType_IPv4; - r->ip.v4 = zerov4Addr; - CFStringRef string = CFDictionaryGetValue(dict, kSCPropNetIPv4Router); - if (string) - { - if (!CFStringGetCString(string, buf, 256, kCFStringEncodingUTF8)) - LogMsg("Could not convert router to CString"); - else - { - struct sockaddr_in saddr; - saddr.sin_len = sizeof(saddr); - saddr.sin_family = AF_INET; - saddr.sin_port = 0; - inet_aton(buf, &saddr.sin_addr); - - *(in_addr_t *)&r->ip.v4 = saddr.sin_addr.s_addr; - } - } - - string = CFDictionaryGetValue(dict, kSCDynamicStorePropNetPrimaryInterface); - if (string) - { - mDNSBool HavePrimaryGlobalv6 = mDNSfalse; // does the primary interface have a global v6 address? - struct ifaddrs *ifa = myGetIfAddrs(1); - - *v4 = *v6 = zeroAddr; - - if (!CFStringGetCString(string, buf, 256, kCFStringEncodingUTF8)) { LogMsg("Could not convert router to CString"); goto exit; } - - // find primary interface in list - while (ifa && (mDNSIPv4AddressIsZero(v4->ip.v4) || mDNSv4AddressIsLinkLocal(&v4->ip.v4) || !HavePrimaryGlobalv6)) - { - mDNSAddr tmp6 = zeroAddr; - if (!strcmp(buf, ifa->ifa_name)) - { - if (ifa->ifa_addr->sa_family == AF_INET) - { - if (mDNSIPv4AddressIsZero(v4->ip.v4) || mDNSv4AddressIsLinkLocal(&v4->ip.v4)) SetupAddr(v4, ifa->ifa_addr); - } - else if (ifa->ifa_addr->sa_family == AF_INET6) - { - SetupAddr(&tmp6, ifa->ifa_addr); - if (tmp6.ip.v6.b[0] >> 5 == 1) // global prefix: 001 - { HavePrimaryGlobalv6 = mDNStrue; *v6 = tmp6; } - } - } - else - { - // We'll take a V6 address from the non-primary interface if the primary interface doesn't have a global V6 address - if (!HavePrimaryGlobalv6 && ifa->ifa_addr->sa_family == AF_INET6 && !v6->ip.v6.b[0]) - { - SetupAddr(&tmp6, ifa->ifa_addr); - if (tmp6.ip.v6.b[0] >> 5 == 1) *v6 = tmp6; - } - } - ifa = ifa->ifa_next; - } - - // Note that while we advertise v6, we still require v4 (possibly NAT'd, but not link-local) because we must use - // V4 to communicate w/ our DNS server - } - - exit: - CFRelease(dict); - } - CFRelease(store); - } - return mStatus_NoError; - } +{ + char buf[256]; + (void)m; // Unused + + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:mDNSPlatformGetPrimaryInterface"), NULL, NULL); + if (!store) + LogMsg("mDNSPlatformGetPrimaryInterface: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); + else + { + CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_IPv4); + if (dict) + { + r->type = mDNSAddrType_IPv4; + r->ip.v4 = zerov4Addr; + CFStringRef string = CFDictionaryGetValue(dict, kSCPropNetIPv4Router); + if (string) + { + if (!CFStringGetCString(string, buf, 256, kCFStringEncodingUTF8)) + LogMsg("Could not convert router to CString"); + else + { + struct sockaddr_in saddr; + saddr.sin_len = sizeof(saddr); + saddr.sin_family = AF_INET; + saddr.sin_port = 0; + inet_aton(buf, &saddr.sin_addr); + + *(in_addr_t *)&r->ip.v4 = saddr.sin_addr.s_addr; + } + } + + string = CFDictionaryGetValue(dict, kSCDynamicStorePropNetPrimaryInterface); + if (string) + { + mDNSBool HavePrimaryGlobalv6 = mDNSfalse; // does the primary interface have a global v6 address? + struct ifaddrs *ifa = myGetIfAddrs(1); + + *v4 = *v6 = zeroAddr; + + if (!CFStringGetCString(string, buf, 256, kCFStringEncodingUTF8)) { LogMsg("Could not convert router to CString"); goto exit; } + + // find primary interface in list + while (ifa && (mDNSIPv4AddressIsZero(v4->ip.v4) || mDNSv4AddressIsLinkLocal(&v4->ip.v4) || !HavePrimaryGlobalv6)) + { + mDNSAddr tmp6 = zeroAddr; + if (!strcmp(buf, ifa->ifa_name)) + { + if (ifa->ifa_addr->sa_family == AF_INET) + { + if (mDNSIPv4AddressIsZero(v4->ip.v4) || mDNSv4AddressIsLinkLocal(&v4->ip.v4)) SetupAddr(v4, ifa->ifa_addr); + } + else if (ifa->ifa_addr->sa_family == AF_INET6) + { + SetupAddr(&tmp6, ifa->ifa_addr); + if (tmp6.ip.v6.b[0] >> 5 == 1) // global prefix: 001 + { HavePrimaryGlobalv6 = mDNStrue; *v6 = tmp6; } + } + } + else + { + // We'll take a V6 address from the non-primary interface if the primary interface doesn't have a global V6 address + if (!HavePrimaryGlobalv6 && ifa->ifa_addr->sa_family == AF_INET6 && !v6->ip.v6.b[0]) + { + SetupAddr(&tmp6, ifa->ifa_addr); + if (tmp6.ip.v6.b[0] >> 5 == 1) *v6 = tmp6; + } + } + ifa = ifa->ifa_next; + } + + // Note that while we advertise v6, we still require v4 (possibly NAT'd, but not link-local) because we must use + // V4 to communicate w/ our DNS server + } + +exit: + CFRelease(dict); + } + CFRelease(store); + } + return mStatus_NoError; +} mDNSexport void mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status) - { - LogInfo("mDNSPlatformDynDNSHostNameStatusChanged %d %##s", status, dname->c); - char uname[MAX_ESCAPED_DOMAIN_NAME]; // Max legal C-string name, including terminating NUL - ConvertDomainNameToCString(dname, uname); - - char *p = uname; - while (*p) - { - *p = tolower(*p); - if (!(*(p+1)) && *p == '.') *p = 0; // if last character, strip trailing dot - p++; - } - - // We need to make a CFDictionary called "State:/Network/DynamicDNS" containing (at present) a single entity. - // That single entity is a CFDictionary with name "HostNames". - // The "HostNames" CFDictionary contains a set of name/value pairs, where the each name is the FQDN - // in question, and the corresponding value is a CFDictionary giving the state for that FQDN. - // (At present we only support a single FQDN, so this dictionary holds just a single name/value pair.) - // The CFDictionary for each FQDN holds (at present) a single name/value pair, - // where the name is "Status" and the value is a CFNumber giving an errror code (with zero meaning success). - - const CFStringRef StateKeys [1] = { CFSTR("HostNames") }; - const CFStringRef HostKeys [1] = { CFStringCreateWithCString(NULL, uname, kCFStringEncodingUTF8) }; - const CFStringRef StatusKeys[1] = { CFSTR("Status") }; - if (!HostKeys[0]) LogMsg("SetDDNSNameStatus: CFStringCreateWithCString(%s) failed", uname); - else - { - const CFNumberRef StatusVals[1] = { CFNumberCreate(NULL, kCFNumberSInt32Type, &status) }; - if (!StatusVals[0]) LogMsg("SetDDNSNameStatus: CFNumberCreate(%d) failed", status); - else - { - const CFDictionaryRef HostVals[1] = { CFDictionaryCreate(NULL, (void*)StatusKeys, (void*)StatusVals, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) }; - if (HostVals[0]) - { - const CFDictionaryRef StateVals[1] = { CFDictionaryCreate(NULL, (void*)HostKeys, (void*)HostVals, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) }; - if (StateVals[0]) - { - CFDictionaryRef StateDict = CFDictionaryCreate(NULL, (void*)StateKeys, (void*)StateVals, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - if (StateDict) - { - mDNSDynamicStoreSetConfig(kmDNSDynamicConfig, mDNSNULL, StateDict); - CFRelease(StateDict); - } - CFRelease(StateVals[0]); - } - CFRelease(HostVals[0]); - } - CFRelease(StatusVals[0]); - } - CFRelease(HostKeys[0]); - } - } +{ + LogInfo("mDNSPlatformDynDNSHostNameStatusChanged %d %##s", status, dname->c); + char uname[MAX_ESCAPED_DOMAIN_NAME]; // Max legal C-string name, including terminating NUL + ConvertDomainNameToCString(dname, uname); + + char *p = uname; + while (*p) + { + *p = tolower(*p); + if (!(*(p+1)) && *p == '.') *p = 0; // if last character, strip trailing dot + p++; + } + + // We need to make a CFDictionary called "State:/Network/DynamicDNS" containing (at present) a single entity. + // That single entity is a CFDictionary with name "HostNames". + // The "HostNames" CFDictionary contains a set of name/value pairs, where the each name is the FQDN + // in question, and the corresponding value is a CFDictionary giving the state for that FQDN. + // (At present we only support a single FQDN, so this dictionary holds just a single name/value pair.) + // The CFDictionary for each FQDN holds (at present) a single name/value pair, + // where the name is "Status" and the value is a CFNumber giving an errror code (with zero meaning success). + + const CFStringRef StateKeys [1] = { CFSTR("HostNames") }; + const CFStringRef HostKeys [1] = { CFStringCreateWithCString(NULL, uname, kCFStringEncodingUTF8) }; + const CFStringRef StatusKeys[1] = { CFSTR("Status") }; + if (!HostKeys[0]) LogMsg("SetDDNSNameStatus: CFStringCreateWithCString(%s) failed", uname); + else + { + const CFNumberRef StatusVals[1] = { CFNumberCreate(NULL, kCFNumberSInt32Type, &status) }; + if (!StatusVals[0]) LogMsg("SetDDNSNameStatus: CFNumberCreate(%d) failed", status); + else + { + const CFDictionaryRef HostVals[1] = { CFDictionaryCreate(NULL, (void*)StatusKeys, (void*)StatusVals, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) }; + if (HostVals[0]) + { + const CFDictionaryRef StateVals[1] = { CFDictionaryCreate(NULL, (void*)HostKeys, (void*)HostVals, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) }; + if (StateVals[0]) + { + CFDictionaryRef StateDict = CFDictionaryCreate(NULL, (void*)StateKeys, (void*)StateVals, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (StateDict) + { + mDNSDynamicStoreSetConfig(kmDNSDynamicConfig, mDNSNULL, StateDict); + CFRelease(StateDict); + } + CFRelease(StateVals[0]); + } + CFRelease(HostVals[0]); + } + CFRelease(StatusVals[0]); + } + CFRelease(HostKeys[0]); + } +} #if APPLE_OSX_mDNSResponder -#if ! NO_AWACS +#if !NO_AWACS // checks whether a domain is present in Setup:/Network/BackToMyMac. Just because there is a key in the // keychain for a domain, it does not become a valid BTMM domain. If things get inconsistent, this will // help catch it mDNSlocal mDNSBool IsBTMMDomain(domainname *d) - { - SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:IsBTMMDomain"), NULL, NULL); - if (!store) - { - LogMsg("IsBTMMDomain: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); - return mDNSfalse; - } - CFDictionaryRef btmm = SCDynamicStoreCopyValue(store, NetworkChangedKey_BackToMyMac); - if (btmm) - { - CFIndex size = CFDictionaryGetCount(btmm); - char buf[MAX_ESCAPED_DOMAIN_NAME]; // Max legal C-string name, including terminating NUL - const void *key[size]; - const void *val[size]; - domainname dom; - int i; - CFDictionaryGetKeysAndValues(btmm, key, val); - for (i = 0; i < size; i++) - { - LogInfo("BackToMyMac %d", i); - if (!CFStringGetCString(key[i], buf, sizeof(buf), kCFStringEncodingUTF8)) - LogMsg("IsBTMMDomain: ERROR!! Can't read BackToMyMac %d key %s", i, buf); - else - { - mDNSu32 uid = atoi(buf); - if (!CFStringGetCString(val[i], buf, sizeof(buf), kCFStringEncodingUTF8)) - LogMsg("IsBTMMDomain: Can't read BackToMyMac %d val %s", i, buf); - else if (MakeDomainNameFromDNSNameString(&dom, buf) && dom.c[0]) - { - if (SameDomainName(&dom, d)) - { - LogInfo("IsBTMMDomain: Domain %##s is a btmm domain, uid %u", d->c, uid); - CFRelease(btmm); - CFRelease(store); - return mDNStrue; - } - } - } - } - CFRelease(btmm); - } - CFRelease(store); - LogInfo("IsBTMMDomain: Domain %##s not a btmm domain", d->c); - return mDNSfalse; - } +{ + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:IsBTMMDomain"), NULL, NULL); + if (!store) + { + LogMsg("IsBTMMDomain: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); + return mDNSfalse; + } + CFDictionaryRef btmm = SCDynamicStoreCopyValue(store, NetworkChangedKey_BackToMyMac); + if (btmm) + { + CFIndex size = CFDictionaryGetCount(btmm); + char buf[MAX_ESCAPED_DOMAIN_NAME]; // Max legal C-string name, including terminating NUL + const void *key[size]; + const void *val[size]; + domainname dom; + int i; + CFDictionaryGetKeysAndValues(btmm, key, val); + for (i = 0; i < size; i++) + { + LogInfo("BackToMyMac %d", i); + if (!CFStringGetCString(key[i], buf, sizeof(buf), kCFStringEncodingUTF8)) + LogMsg("IsBTMMDomain: ERROR!! Can't read BackToMyMac %d key %s", i, buf); + else + { + mDNSu32 uid = atoi(buf); + if (!CFStringGetCString(val[i], buf, sizeof(buf), kCFStringEncodingUTF8)) + LogMsg("IsBTMMDomain: Can't read BackToMyMac %d val %s", i, buf); + else if (MakeDomainNameFromDNSNameString(&dom, buf) && dom.c[0]) + { + if (SameDomainName(&dom, d)) + { + LogInfo("IsBTMMDomain: Domain %##s is a btmm domain, uid %u", d->c, uid); + CFRelease(btmm); + CFRelease(store); + return mDNStrue; + } + } + } + } + CFRelease(btmm); + } + CFRelease(store); + LogInfo("IsBTMMDomain: Domain %##s not a btmm domain", d->c); + return mDNSfalse; +} // Appends data to the buffer mDNSlocal int AddOneItem(char *buf, int bufsz, char *data, int *currlen) - { - int len; - - len = strlcpy(buf + *currlen, data, bufsz - *currlen); - if (len >= (bufsz - *currlen)) - { - // if we have exceeded the space in buf, it has already been NULL terminated - // and we have nothing more to do. Set currlen to the last byte so that the caller - // knows to do the right thing - LogMsg("AddOneItem: Exceeded the max buffer size currlen %d, len %d", *currlen, len); - *currlen = bufsz - 1; - return -1; - } - else { (*currlen) += len; } - - buf[*currlen] = ','; - if (*currlen >= bufsz) - { - LogMsg("AddOneItem: ERROR!! How can currlen be %d", *currlen); - *currlen = bufsz - 1; - buf[*currlen] = 0; - return -1; - } - // if we have filled up the buffer exactly, then there is no more work to do - if (*currlen == bufsz - 1) { buf[*currlen] = 0; return -1; } - (*currlen)++; - return *currlen; - } +{ + int len; + + len = strlcpy(buf + *currlen, data, bufsz - *currlen); + if (len >= (bufsz - *currlen)) + { + // if we have exceeded the space in buf, it has already been NULL terminated + // and we have nothing more to do. Set currlen to the last byte so that the caller + // knows to do the right thing + LogMsg("AddOneItem: Exceeded the max buffer size currlen %d, len %d", *currlen, len); + *currlen = bufsz - 1; + return -1; + } + else { (*currlen) += len; } + + buf[*currlen] = ','; + if (*currlen >= bufsz) + { + LogMsg("AddOneItem: ERROR!! How can currlen be %d", *currlen); + *currlen = bufsz - 1; + buf[*currlen] = 0; + return -1; + } + // if we have filled up the buffer exactly, then there is no more work to do + if (*currlen == bufsz - 1) { buf[*currlen] = 0; return -1; } + (*currlen)++; + return *currlen; +} // If we have at least one BTMM domain, then trigger the connection to the relay. If we have no // BTMM domains, then bring down the connection to the relay. mDNSlocal void UpdateBTMMRelayConnection(mDNS *const m) - { - DomainAuthInfo *BTMMDomain = mDNSNULL; - DomainAuthInfo *FoundInList; - static mDNSBool AWACSDConnected = mDNSfalse; - char AllUsers[1024]; // maximum size of mach message - char AllPass[1024]; // maximum size of mach message - char username[MAX_DOMAIN_LABEL + 1]; - int currulen = 0; - int currplen = 0; - - // if a domain is being deleted, we want to send a disconnect. If we send a disconnect now, - // we may not be able to send the dns queries over the relay connection which may be needed - // for sending the deregistrations. Hence, we need to delay sending the disconnect. But we - // need to make sure that we send the disconnect before attempting the next connect as the - // awacs connections are redirected based on usernames. - // - // For now we send a disconnect immediately. When we start sending dns queries over the relay - // connection, we will need to fix this. - - for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next) - if (!FoundInList->deltime && FoundInList->AutoTunnel && IsBTMMDomain(&FoundInList->domain)) - { - // We need the passwd from the first domain. - BTMMDomain = FoundInList; - ConvertDomainLabelToCString_unescaped((domainlabel *)BTMMDomain->domain.c, username); - LogInfo("UpdateBTMMRelayConnection: user %s for domain %##s", username, BTMMDomain->domain.c); - if (AddOneItem(AllUsers, sizeof(AllUsers), username, &currulen) == -1) break; - if (AddOneItem(AllPass, sizeof(AllPass), BTMMDomain->b64keydata, &currplen) == -1) break; - } - - if (BTMMDomain) - { - // In the normal case (where we neither exceed the buffer size nor write bytes that - // fit exactly into the buffer), currulen/currplen should be a different size than - // (AllUsers - 1) / (AllPass - 1). In that case, we need to override the "," with a NULL byte. - - if (currulen != (int)(sizeof(AllUsers) - 1)) AllUsers[currulen - 1] = 0; - if (currplen != (int)(sizeof(AllPass) - 1)) AllPass[currplen - 1] = 0; - - LogInfo("UpdateBTMMRelayConnection: AWS_Connect for user %s", AllUsers); - AWACS_Connect(AllUsers, AllPass, "hello.connectivity.me.com"); - AWACSDConnected = mDNStrue; - } - else - { - // Disconnect only if we connected previously - if (AWACSDConnected) - { - LogInfo("UpdateBTMMRelayConnection: AWS_Disconnect"); - AWACS_Disconnect(); - AWACSDConnected = mDNSfalse; - } - else LogInfo("UpdateBTMMRelayConnection: Not calling AWS_Disconnect"); - } - } +{ + DomainAuthInfo *BTMMDomain = mDNSNULL; + DomainAuthInfo *FoundInList; + static mDNSBool AWACSDConnected = mDNSfalse; + char AllUsers[1024]; // maximum size of mach message + char AllPass[1024]; // maximum size of mach message + char username[MAX_DOMAIN_LABEL + 1]; + int currulen = 0; + int currplen = 0; + + // if a domain is being deleted, we want to send a disconnect. If we send a disconnect now, + // we may not be able to send the dns queries over the relay connection which may be needed + // for sending the deregistrations. Hence, we need to delay sending the disconnect. But we + // need to make sure that we send the disconnect before attempting the next connect as the + // awacs connections are redirected based on usernames. + // + // For now we send a disconnect immediately. When we start sending dns queries over the relay + // connection, we will need to fix this. + + for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next) + if (!FoundInList->deltime && FoundInList->AutoTunnel && IsBTMMDomain(&FoundInList->domain)) + { + // We need the passwd from the first domain. + BTMMDomain = FoundInList; + ConvertDomainLabelToCString_unescaped((domainlabel *)BTMMDomain->domain.c, username); + LogInfo("UpdateBTMMRelayConnection: user %s for domain %##s", username, BTMMDomain->domain.c); + if (AddOneItem(AllUsers, sizeof(AllUsers), username, &currulen) == -1) break; + if (AddOneItem(AllPass, sizeof(AllPass), BTMMDomain->b64keydata, &currplen) == -1) break; + } + + if (BTMMDomain) + { + // In the normal case (where we neither exceed the buffer size nor write bytes that + // fit exactly into the buffer), currulen/currplen should be a different size than + // (AllUsers - 1) / (AllPass - 1). In that case, we need to override the "," with a NULL byte. + + if (currulen != (int)(sizeof(AllUsers) - 1)) AllUsers[currulen - 1] = 0; + if (currplen != (int)(sizeof(AllPass) - 1)) AllPass[currplen - 1] = 0; + + LogInfo("UpdateBTMMRelayConnection: AWS_Connect for user %s", AllUsers); + AWACS_Connect(AllUsers, AllPass, "hello.connectivity.me.com"); + AWACSDConnected = mDNStrue; + } + else + { + // Disconnect only if we connected previously + if (AWACSDConnected) + { + LogInfo("UpdateBTMMRelayConnection: AWS_Disconnect"); + AWACS_Disconnect(); + AWACSDConnected = mDNSfalse; + } + else LogInfo("UpdateBTMMRelayConnection: Not calling AWS_Disconnect"); + } +} #else mDNSlocal void UpdateBTMMRelayConnection(mDNS *const m) - { - (void) m; // Unused - LogInfo("UpdateBTMMRelayConnection: AWACS connection not started, no AWACS library"); - } +{ + (void) m; // Unused + LogInfo("UpdateBTMMRelayConnection: AWACS connection not started, no AWACS library"); +} #endif // ! NO_AWACS + +mDNSlocal void ProcessConndConfigChanges(mDNS *const m); + #endif // APPLE_OSX_mDNSResponder -// MUST be called holding the lock -- this routine calls SetupLocalAutoTunnelInterface_internal() +// MUST be called holding the lock mDNSexport void SetDomainSecrets(mDNS *m) - { +{ #ifdef NO_SECURITYFRAMEWORK - (void)m; - LogMsg("Note: SetDomainSecrets: no keychain support"); + (void) m; + LogMsg("Note: SetDomainSecrets: no keychain support"); #else - mDNSBool haveAutoTunnels = mDNSfalse; + mDNSBool haveAutoTunnels = mDNSfalse; - LogInfo("SetDomainSecrets"); + LogInfo("SetDomainSecrets"); - // Rather than immediately deleting all keys now, we mark them for deletion in ten seconds. - // In the case where the user simultaneously removes their DDNS host name and the key - // for it, this gives mDNSResponder ten seconds to gracefully delete the name from the - // server before it loses access to the necessary key. Otherwise, we'd leave orphaned - // address records behind that we no longer have permission to delete. - DomainAuthInfo *ptr; - for (ptr = m->AuthInfoList; ptr; ptr = ptr->next) - ptr->deltime = NonZeroTime(m->timenow + mDNSPlatformOneSecond*10); + // Rather than immediately deleting all keys now, we mark them for deletion in ten seconds. + // In the case where the user simultaneously removes their DDNS host name and the key + // for it, this gives mDNSResponder ten seconds to gracefully delete the name from the + // server before it loses access to the necessary key. Otherwise, we'd leave orphaned + // address records behind that we no longer have permission to delete. + DomainAuthInfo *ptr; + for (ptr = m->AuthInfoList; ptr; ptr = ptr->next) + ptr->deltime = NonZeroTime(m->timenow + mDNSPlatformOneSecond*10); #if APPLE_OSX_mDNSResponder - { - // Mark all TunnelClients for deletion - ClientTunnel *client; - for (client = m->TunnelClients; client; client = client->next) - { - LogInfo("SetDomainSecrets: tunnel to %##s marked for deletion", client->dstname.c); - client->MarkedForDeletion = mDNStrue; - } - } + { + // Mark all TunnelClients for deletion + ClientTunnel *client; + for (client = m->TunnelClients; client; client = client->next) + { + LogInfo("SetDomainSecrets: tunnel to %##s marked for deletion", client->dstname.c); + client->MarkedForDeletion = mDNStrue; + } + } #endif // APPLE_OSX_mDNSResponder - // String Array used to write list of private domains to Dynamic Store - CFMutableArrayRef sa = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - if (!sa) { LogMsg("SetDomainSecrets: CFArrayCreateMutable failed"); return; } - CFIndex i; - CFDataRef data = NULL; - const int itemsPerEntry = 4; // domain name, key name, key value, Name value - CFArrayRef secrets = NULL; - int err = mDNSKeychainGetSecrets(&secrets); - if (err || !secrets) - LogMsg("SetDomainSecrets: mDNSKeychainGetSecrets failed error %d CFArrayRef %p", err, secrets); - else - { - CFIndex ArrayCount = CFArrayGetCount(secrets); - // Iterate through the secrets - for (i = 0; i < ArrayCount; ++i) - { - mDNSBool AutoTunnel; - int j, offset; - CFArrayRef entry = CFArrayGetValueAtIndex(secrets, i); - if (CFArrayGetTypeID() != CFGetTypeID(entry) || itemsPerEntry != CFArrayGetCount(entry)) - { LogMsg("SetDomainSecrets: malformed entry %d, itemsPerEntry %d", i, itemsPerEntry); continue; } - for (j = 0; j < CFArrayGetCount(entry); ++j) - if (CFDataGetTypeID() != CFGetTypeID(CFArrayGetValueAtIndex(entry, j))) - { LogMsg("SetDomainSecrets: malformed entry item %d", j); continue; } - - // The names have already been vetted by the helper, but checking them again here helps humans and automated tools verify correctness - - // Max legal domainname as C-string, including space for btmmprefix and terminating NUL - // Get DNS domain this key is for (kmDNSKcWhere) - char stringbuf[MAX_ESCAPED_DOMAIN_NAME + sizeof(btmmprefix)]; - data = CFArrayGetValueAtIndex(entry, kmDNSKcWhere); - if (CFDataGetLength(data) >= (int)sizeof(stringbuf)) - { LogMsg("SetDomainSecrets: Bad kSecServiceItemAttr length %d", CFDataGetLength(data)); continue; } - CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)stringbuf); - stringbuf[CFDataGetLength(data)] = '\0'; - - AutoTunnel = mDNSfalse; - offset = 0; - if (!strncmp(stringbuf, dnsprefix, strlen(dnsprefix))) - offset = strlen(dnsprefix); - else if (!strncmp(stringbuf, btmmprefix, strlen(btmmprefix))) - { - AutoTunnel = mDNStrue; - offset = strlen(btmmprefix); - } - domainname domain; - if (!MakeDomainNameFromDNSNameString(&domain, stringbuf + offset)) { LogMsg("SetDomainSecrets: bad key domain %s", stringbuf); continue; } - - // Get key name (kmDNSKcAccount) - data = CFArrayGetValueAtIndex(entry, kmDNSKcAccount); - if (CFDataGetLength(data) >= (int)sizeof(stringbuf)) - { LogMsg("SetDomainSecrets: Bad kSecAccountItemAttr length %d", CFDataGetLength(data)); continue; } - CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8 *)stringbuf); - stringbuf[CFDataGetLength(data)] = '\0'; - - domainname keyname; - if (!MakeDomainNameFromDNSNameString(&keyname, stringbuf)) { LogMsg("SetDomainSecrets: bad key name %s", stringbuf); continue; } - - // Get key data (kmDNSKcKey) - data = CFArrayGetValueAtIndex(entry, kmDNSKcKey); - if (CFDataGetLength(data) >= (int)sizeof(stringbuf)) - { LogMsg("SetDomainSecrets: Shared secret too long: %d", CFDataGetLength(data)); continue; } - CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)stringbuf); - stringbuf[CFDataGetLength(data)] = '\0'; // mDNS_SetSecretForDomain requires NULL-terminated C string for key - - // Get the Name of the keychain entry (kmDNSKcName) host or host:port - // The hostname also has the port number and ":". It should take a maximum of 6 bytes. - char hostbuf[MAX_ESCAPED_DOMAIN_NAME + 6]; // Max legal domainname as C-string, including terminating NUL - data = CFArrayGetValueAtIndex(entry, kmDNSKcName); - if (CFDataGetLength(data) >= (int)sizeof(hostbuf)) - { LogMsg("SetDomainSecrets: Shared secret too long: %d", CFDataGetLength(data)); continue; } - CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8 *)hostbuf); - hostbuf[CFDataGetLength(data)] = '\0'; - - domainname hostname; - mDNSIPPort port; - char *hptr; - hptr = strchr(hostbuf, ':'); - - port.NotAnInteger = 0; - if (hptr) - { - mDNSu8 *p; - mDNSu16 val = 0; - - *hptr++ = '\0'; - while(hptr && *hptr != 0) - { - if (*hptr < '0' || *hptr > '9') - { LogMsg("SetDomainSecrets: Malformed Port number %d, val %d", *hptr, val); val = 0; break;} - val = val * 10 + *hptr - '0'; - hptr++; - } - if (!val) continue; - p = (mDNSu8 *)&val; - port.NotAnInteger = p[0] << 8 | p[1]; - } - // The hostbuf is of the format dsid@hostname:port. We don't care about the dsid. - hptr = strchr(hostbuf, '@'); - if (hptr) - hptr++; - else - hptr = hostbuf; - if (!MakeDomainNameFromDNSNameString(&hostname, hptr)) { LogMsg("SetDomainSecrets: bad host name %s", hptr); continue; } - - DomainAuthInfo *FoundInList; - for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next) - if (SameDomainName(&FoundInList->domain, &domain)) break; + // String Array used to write list of private domains to Dynamic Store + CFMutableArrayRef sa = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (!sa) { LogMsg("SetDomainSecrets: CFArrayCreateMutable failed"); return; } + CFIndex i; + CFDataRef data = NULL; + const int itemsPerEntry = 4; // domain name, key name, key value, Name value + CFArrayRef secrets = NULL; + int err = mDNSKeychainGetSecrets(&secrets); + if (err || !secrets) + LogMsg("SetDomainSecrets: mDNSKeychainGetSecrets failed error %d CFArrayRef %p", err, secrets); + else + { + CFIndex ArrayCount = CFArrayGetCount(secrets); + // Iterate through the secrets + for (i = 0; i < ArrayCount; ++i) + { + mDNSBool AutoTunnel; + int j, offset; + CFArrayRef entry = CFArrayGetValueAtIndex(secrets, i); + if (CFArrayGetTypeID() != CFGetTypeID(entry) || itemsPerEntry != CFArrayGetCount(entry)) + { LogMsg("SetDomainSecrets: malformed entry %d, itemsPerEntry %d", i, itemsPerEntry); continue; } + for (j = 0; j < CFArrayGetCount(entry); ++j) + if (CFDataGetTypeID() != CFGetTypeID(CFArrayGetValueAtIndex(entry, j))) + { LogMsg("SetDomainSecrets: malformed entry item %d", j); continue; } + + // The names have already been vetted by the helper, but checking them again here helps humans and automated tools verify correctness + + // Max legal domainname as C-string, including space for btmmprefix and terminating NUL + // Get DNS domain this key is for (kmDNSKcWhere) + char stringbuf[MAX_ESCAPED_DOMAIN_NAME + sizeof(btmmprefix)]; + data = CFArrayGetValueAtIndex(entry, kmDNSKcWhere); + if (CFDataGetLength(data) >= (int)sizeof(stringbuf)) + { LogMsg("SetDomainSecrets: Bad kSecServiceItemAttr length %d", CFDataGetLength(data)); continue; } + CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)stringbuf); + stringbuf[CFDataGetLength(data)] = '\0'; + + AutoTunnel = mDNSfalse; + offset = 0; + if (!strncmp(stringbuf, dnsprefix, strlen(dnsprefix))) + offset = strlen(dnsprefix); + else if (!strncmp(stringbuf, btmmprefix, strlen(btmmprefix))) + { + AutoTunnel = mDNStrue; + offset = strlen(btmmprefix); + } + domainname domain; + if (!MakeDomainNameFromDNSNameString(&domain, stringbuf + offset)) { LogMsg("SetDomainSecrets: bad key domain %s", stringbuf); continue; } + + // Get key name (kmDNSKcAccount) + data = CFArrayGetValueAtIndex(entry, kmDNSKcAccount); + if (CFDataGetLength(data) >= (int)sizeof(stringbuf)) + { LogMsg("SetDomainSecrets: Bad kSecAccountItemAttr length %d", CFDataGetLength(data)); continue; } + CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8 *)stringbuf); + stringbuf[CFDataGetLength(data)] = '\0'; + + domainname keyname; + if (!MakeDomainNameFromDNSNameString(&keyname, stringbuf)) { LogMsg("SetDomainSecrets: bad key name %s", stringbuf); continue; } + + // Get key data (kmDNSKcKey) + data = CFArrayGetValueAtIndex(entry, kmDNSKcKey); + if (CFDataGetLength(data) >= (int)sizeof(stringbuf)) + { + LogMsg("SetDomainSecrets: Shared secret too long: %d", CFDataGetLength(data)); + continue; + } + CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)stringbuf); + stringbuf[CFDataGetLength(data)] = '\0'; // mDNS_SetSecretForDomain requires NULL-terminated C string for key + + // Get the Name of the keychain entry (kmDNSKcName) host or host:port + // The hostname also has the port number and ":". It should take a maximum of 6 bytes. + char hostbuf[MAX_ESCAPED_DOMAIN_NAME + 6]; // Max legal domainname as C-string, including terminating NUL + data = CFArrayGetValueAtIndex(entry, kmDNSKcName); + if (CFDataGetLength(data) >= (int)sizeof(hostbuf)) + { + LogMsg("SetDomainSecrets: host:port data too long: %d", CFDataGetLength(data)); + continue; + } + CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8 *)hostbuf); + hostbuf[CFDataGetLength(data)] = '\0'; + + domainname hostname; + mDNSIPPort port; + char *hptr; + hptr = strchr(hostbuf, ':'); + + port.NotAnInteger = 0; + if (hptr) + { + mDNSu8 *p; + mDNSu16 val = 0; + + *hptr++ = '\0'; + while(hptr && *hptr != 0) + { + if (*hptr < '0' || *hptr > '9') + { LogMsg("SetDomainSecrets: Malformed Port number %d, val %d", *hptr, val); val = 0; break;} + val = val * 10 + *hptr - '0'; + hptr++; + } + if (!val) continue; + p = (mDNSu8 *)&val; + port.NotAnInteger = p[0] << 8 | p[1]; + } + // The hostbuf is of the format dsid@hostname:port. We don't care about the dsid. + hptr = strchr(hostbuf, '@'); + if (hptr) + hptr++; + else + hptr = hostbuf; + if (!MakeDomainNameFromDNSNameString(&hostname, hptr)) { LogMsg("SetDomainSecrets: bad host name %s", hptr); continue; } + + DomainAuthInfo *FoundInList; + for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next) + if (SameDomainName(&FoundInList->domain, &domain)) break; #if APPLE_OSX_mDNSResponder - if (FoundInList) - { - // If any client tunnel destination is in this domain, set deletion flag to false - ClientTunnel *client; - for (client = m->TunnelClients; client; client = client->next) - if (FoundInList == GetAuthInfoForName_internal(m, &client->dstname)) - { - LogInfo("SetDomainSecrets: tunnel to %##s no longer marked for deletion", client->dstname.c); - client->MarkedForDeletion = mDNSfalse; - } - } + if (FoundInList) + { + // If any client tunnel destination is in this domain, set deletion flag to false + ClientTunnel *client; + for (client = m->TunnelClients; client; client = client->next) + if (FoundInList == GetAuthInfoForName_internal(m, &client->dstname)) + { + LogInfo("SetDomainSecrets: tunnel to %##s no longer marked for deletion", client->dstname.c); + client->MarkedForDeletion = mDNSfalse; + } + } #endif // APPLE_OSX_mDNSResponder - // Uncomment the line below to view the keys as they're read out of the system keychain - // DO NOT SHIP CODE THIS WAY OR YOU'LL LEAK SECRET DATA INTO A PUBLICLY READABLE FILE! - //LogInfo("SetDomainSecrets: domain %##s keyname %##s key %s hostname %##s port %d", &domain.c, &keyname.c, stringbuf, hostname.c, (port.b[0] << 8 | port.b[1])); - LogInfo("SetDomainSecrets: domain %##s keyname %##s hostname %##s port %d", &domain.c, &keyname.c, hostname.c, (port.b[0] << 8 | port.b[1])); - - // If didn't find desired domain in the list, make a new entry - ptr = FoundInList; - if (FoundInList && FoundInList->AutoTunnel && haveAutoTunnels == mDNSfalse) haveAutoTunnels = mDNStrue; - if (!FoundInList) - { - ptr = (DomainAuthInfo*)mallocL("DomainAuthInfo", sizeof(*ptr)); - if (!ptr) { LogMsg("SetDomainSecrets: No memory"); continue; } - } - - //LogInfo("SetDomainSecrets: %d of %d %##s", i, ArrayCount, &domain); - - // It is an AutoTunnel if the keychains tells us so (with btmm prefix) or if it is a TunnelModeDomain - if (mDNS_SetSecretForDomain(m, ptr, &domain, &keyname, stringbuf, &hostname, &port, (AutoTunnel ? btmmprefix : (IsTunnelModeDomain(&domain) ? dnsprefix : NULL))) == mStatus_BadParamErr) - { - if (!FoundInList) mDNSPlatformMemFree(ptr); // If we made a new DomainAuthInfo here, and it turned out bad, dispose it immediately - continue; - } + // Uncomment the line below to view the keys as they're read out of the system keychain + // DO NOT SHIP CODE THIS WAY OR YOU'LL LEAK SECRET DATA INTO A PUBLICLY READABLE FILE! + //LogInfo("SetDomainSecrets: domain %##s keyname %##s key %s hostname %##s port %d", &domain.c, &keyname.c, stringbuf, hostname.c, (port.b[0] << 8 | port.b[1])); + LogInfo("SetDomainSecrets: domain %##s keyname %##s hostname %##s port %d", &domain.c, &keyname.c, hostname.c, (port.b[0] << 8 | port.b[1])); + + // If didn't find desired domain in the list, make a new entry + ptr = FoundInList; + if (FoundInList && FoundInList->AutoTunnel && haveAutoTunnels == mDNSfalse) haveAutoTunnels = mDNStrue; + if (!FoundInList) + { + ptr = (DomainAuthInfo*)mallocL("DomainAuthInfo", sizeof(*ptr)); + if (!ptr) { LogMsg("SetDomainSecrets: No memory"); continue; } + } + + //LogInfo("SetDomainSecrets: %d of %d %##s", i, ArrayCount, &domain); + + // It is an AutoTunnel if the keychains tells us so (with btmm prefix) or if it is a TunnelModeDomain + if (mDNS_SetSecretForDomain(m, ptr, &domain, &keyname, stringbuf, &hostname, &port, AutoTunnel) == mStatus_BadParamErr) + { + if (!FoundInList) mDNSPlatformMemFree(ptr); // If we made a new DomainAuthInfo here, and it turned out bad, dispose it immediately + continue; + } + + ConvertDomainNameToCString(&domain, stringbuf); + CFStringRef cfs = CFStringCreateWithCString(NULL, stringbuf, kCFStringEncodingUTF8); + if (cfs) { CFArrayAppendValue(sa, cfs); CFRelease(cfs); } + } + CFRelease(secrets); + } + mDNSDynamicStoreSetConfig(kmDNSPrivateConfig, mDNSNULL, sa); + CFRelease(sa); #if APPLE_OSX_mDNSResponder - if (ptr->AutoTunnel) UpdateAutoTunnelDomainStatus(m, ptr); -#endif // APPLE_OSX_mDNSResponder - - ConvertDomainNameToCString(&domain, stringbuf); - CFStringRef cfs = CFStringCreateWithCString(NULL, stringbuf, kCFStringEncodingUTF8); - if (cfs) { CFArrayAppendValue(sa, cfs); CFRelease(cfs); } - } - CFRelease(secrets); - } - mDNSDynamicStoreSetConfig(kmDNSPrivateConfig, mDNSNULL, sa); - CFRelease(sa); - -#if APPLE_OSX_mDNSResponder - { - // clean up ClientTunnels - ClientTunnel **pp = &m->TunnelClients; - while (*pp) - { - if ((*pp)->MarkedForDeletion) - { - ClientTunnel *cur = *pp; - LogInfo("SetDomainSecrets: removing client %p %##s from list", cur, cur->dstname.c); - if (cur->q.ThisQInterval >= 0) mDNS_StopQuery(m, &cur->q); - AutoTunnelSetKeys(cur, mDNSfalse); - *pp = cur->next; - freeL("ClientTunnel", cur); - } - else - pp = &(*pp)->next; - } - - DomainAuthInfo *info = m->AuthInfoList; - while (info) - { - if (info->AutoTunnel && info->deltime) - { - if (info->AutoTunnelNAT.clientContext) - { - // stop the NAT operation - mDNS_StopNATOperation_internal(m, &info->AutoTunnelNAT); - if (info->AutoTunnelNAT.clientCallback) - { - // Reset port and cleanup the state - info->AutoTunnelNAT.ExternalAddress = m->ExternalAddress; - info->AutoTunnelNAT.ExternalPort = zeroIPPort; - info->AutoTunnelNAT.RequestedPort = zeroIPPort; - info->AutoTunnelNAT.Lifetime = 0; - info->AutoTunnelNAT.Result = mStatus_NoError; - mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback - AutoTunnelDeleteAuthInfoState(m, info); - mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again - } - info->AutoTunnelNAT.clientContext = mDNSNULL; - } - RemoveAutoTunnelDomainStatus(m, info); - } - info = info->next; - } - - if (!haveAutoTunnels && !m->TunnelClients && m->AutoTunnelHostAddrActive) - { - // remove interface if no autotunnel servers and no more client tunnels - LogInfo("SetDomainSecrets: Bringing tunnel interface DOWN"); - m->AutoTunnelHostAddrActive = mDNSfalse; - (void)mDNSAutoTunnelInterfaceUpDown(kmDNSDown, m->AutoTunnelHostAddr.b); - mDNSPlatformMemZero(m->AutoTunnelHostAddr.b, sizeof(m->AutoTunnelHostAddr.b)); - } - - if (m->AutoTunnelHostAddr.b[0]) - if (TunnelClients(m) || TunnelServers(m)) - SetupLocalAutoTunnelInterface_internal(m, mDNSfalse); - - UpdateAnonymousRacoonConfig(m); // Determine whether we need racoon to accept incoming connections - UpdateBTMMRelayConnection(m); - } + { + // clean up ClientTunnels + ClientTunnel **pp = &m->TunnelClients; + while (*pp) + { + if ((*pp)->MarkedForDeletion) + { + ClientTunnel *cur = *pp; + LogInfo("SetDomainSecrets: removing client %p %##s from list", cur, cur->dstname.c); + if (cur->q.ThisQInterval >= 0) mDNS_StopQuery(m, &cur->q); + AutoTunnelSetKeys(cur, mDNSfalse); + *pp = cur->next; + freeL("ClientTunnel", cur); + } + else + pp = &(*pp)->next; + } + + mDNSBool needAutoTunnelNAT = mDNSfalse; + DomainAuthInfo *info; + for (info = m->AuthInfoList; info; info = info->next) + { + if (info->AutoTunnel) + { + UpdateAutoTunnelDeviceInfoRecord(m, info); + UpdateAutoTunnelHostRecord(m, info); + UpdateAutoTunnelServiceRecords(m, info); + UpdateAutoTunnel6Record(m, info); + if (info->deltime) + { + if (info->AutoTunnelServiceStarted) info->AutoTunnelServiceStarted = mDNSfalse; + } + else if (info->AutoTunnelServiceStarted) + needAutoTunnelNAT = true; + + UpdateAutoTunnelDomainStatus(m, info); + } + } + + // If the AutoTunnel NAT-T is no longer needed (& is currently running), stop it + if (!needAutoTunnelNAT && m->AutoTunnelNAT.clientContext) + { + // stop the NAT operation, reset port, cleanup state + mDNS_StopNATOperation_internal(m, &m->AutoTunnelNAT); + m->AutoTunnelNAT.ExternalAddress = m->ExternalAddress; + m->AutoTunnelNAT.ExternalPort = zeroIPPort; + m->AutoTunnelNAT.RequestedPort = zeroIPPort; + m->AutoTunnelNAT.Lifetime = 0; + m->AutoTunnelNAT.Result = mStatus_NoError; + m->AutoTunnelNAT.clientContext = mDNSNULL; + } + + UpdateAnonymousRacoonConfig(m); // Determine whether we need racoon to accept incoming connections + ProcessConndConfigChanges(m); // Update AutoTunnelInnerAddress values and default ipsec policies as necessary + } #endif // APPLE_OSX_mDNSResponder - CheckSuppressUnusableQuestions(m); + CheckSuppressUnusableQuestions(m); #endif /* NO_SECURITYFRAMEWORK */ - } +} mDNSlocal void SetLocalDomains(void) - { - CFMutableArrayRef sa = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - if (!sa) { LogMsg("SetLocalDomains: CFArrayCreateMutable failed"); return; } - - CFArrayAppendValue(sa, CFSTR("local")); - CFArrayAppendValue(sa, CFSTR("254.169.in-addr.arpa")); - CFArrayAppendValue(sa, CFSTR("8.e.f.ip6.arpa")); - CFArrayAppendValue(sa, CFSTR("9.e.f.ip6.arpa")); - CFArrayAppendValue(sa, CFSTR("a.e.f.ip6.arpa")); - CFArrayAppendValue(sa, CFSTR("b.e.f.ip6.arpa")); - - mDNSDynamicStoreSetConfig(kmDNSMulticastConfig, mDNSNULL, sa); - CFRelease(sa); - } +{ + CFMutableArrayRef sa = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (!sa) { LogMsg("SetLocalDomains: CFArrayCreateMutable failed"); return; } + + CFArrayAppendValue(sa, CFSTR("local")); + CFArrayAppendValue(sa, CFSTR("254.169.in-addr.arpa")); + CFArrayAppendValue(sa, CFSTR("8.e.f.ip6.arpa")); + CFArrayAppendValue(sa, CFSTR("9.e.f.ip6.arpa")); + CFArrayAppendValue(sa, CFSTR("a.e.f.ip6.arpa")); + CFArrayAppendValue(sa, CFSTR("b.e.f.ip6.arpa")); + + mDNSDynamicStoreSetConfig(kmDNSMulticastConfig, mDNSNULL, sa); + CFRelease(sa); +} mDNSlocal void GetCurrentPMSetting(const CFStringRef name, mDNSs32 *val) - { +{ #if USE_IOPMCOPYACTIVEPMPREFERENCES - CFTypeRef blob = NULL; - CFStringRef str = NULL; - CFDictionaryRef odict = NULL; - CFDictionaryRef idict = NULL; - CFNumberRef number = NULL; - - blob = IOPSCopyPowerSourcesInfo(); - if (!blob) { LogMsg("GetCurrentPMSetting: IOPSCopyPowerSourcesInfo failed!"); goto end; } - - odict = IOPMCopyActivePMPreferences(); - if (!odict) { LogMsg("GetCurrentPMSetting: IOPMCopyActivePMPreferences failed!"); goto end; } - - str = IOPSGetProvidingPowerSourceType(blob); - if (!str) { LogMsg("GetCurrentPMSetting: IOPSGetProvidingPowerSourceType failed!"); goto end; } - - idict = CFDictionaryGetValue(odict, str); - if (!idict) - { - char buf[256]; - if (!CFStringGetCString(str, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; - LogMsg("GetCurrentPMSetting: CFDictionaryGetValue (%s) failed!", buf); - goto end; - } - - number = CFDictionaryGetValue(idict, name); - if (!number || CFGetTypeID(number) != CFNumberGetTypeID() || !CFNumberGetValue(number, kCFNumberSInt32Type, val)) - *val = 0; + CFTypeRef blob = NULL; + CFStringRef str = NULL; + CFDictionaryRef odict = NULL; + CFDictionaryRef idict = NULL; + CFNumberRef number = NULL; + + blob = IOPSCopyPowerSourcesInfo(); + if (!blob) { LogMsg("GetCurrentPMSetting: IOPSCopyPowerSourcesInfo failed!"); goto end; } + + odict = IOPMCopyActivePMPreferences(); + if (!odict) { LogMsg("GetCurrentPMSetting: IOPMCopyActivePMPreferences failed!"); goto end; } + + str = IOPSGetProvidingPowerSourceType(blob); + if (!str) { LogMsg("GetCurrentPMSetting: IOPSGetProvidingPowerSourceType failed!"); goto end; } + + idict = CFDictionaryGetValue(odict, str); + if (!idict) + { + char buf[256]; + if (!CFStringGetCString(str, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; + LogMsg("GetCurrentPMSetting: CFDictionaryGetValue (%s) failed!", buf); + goto end; + } + + number = CFDictionaryGetValue(idict, name); + if (!number || CFGetTypeID(number) != CFNumberGetTypeID() || !CFNumberGetValue(number, kCFNumberSInt32Type, val)) + *val = 0; end: - if (blob) CFRelease(blob); - if (odict) CFRelease(odict); + if (blob) CFRelease(blob); + if (odict) CFRelease(odict); #else - SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetCurrentPMSetting"), NULL, NULL); - if (!store) LogMsg("GetCurrentPMSetting: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); - else - { - CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_PowerSettings); - if (!dict) LogSPS("GetCurrentPMSetting: Could not get IOPM CurrentSettings dict"); - else - { - CFNumberRef number = CFDictionaryGetValue(dict, name); - if (!number || CFGetTypeID(number) != CFNumberGetTypeID() || !CFNumberGetValue(number, kCFNumberSInt32Type, val)) - *val = 0; - CFRelease(dict); - } - CFRelease(store); - } - + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetCurrentPMSetting"), NULL, NULL); + if (!store) LogMsg("GetCurrentPMSetting: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); + else + { + CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_PowerSettings); + if (!dict) LogSPS("GetCurrentPMSetting: Could not get IOPM CurrentSettings dict"); + else + { + CFNumberRef number = CFDictionaryGetValue(dict, name); + if (!number || CFGetTypeID(number) != CFNumberGetTypeID() || !CFNumberGetValue(number, kCFNumberSInt32Type, val)) + *val = 0; + CFRelease(dict); + } + CFRelease(store); + } + #endif - } +} #if APPLE_OSX_mDNSResponder @@ -5997,221 +6115,222 @@ static CFMutableDictionaryRef spsStatusDict = NULL; static const CFStringRef kMetricRef = CFSTR("Metric"); mDNSlocal void SPSStatusPutNumber(CFMutableDictionaryRef dict, const mDNSu8* const ptr, CFStringRef key) - { - mDNSu8 tmp = (ptr[0] - '0') * 10 + ptr[1] - '0'; - CFNumberRef num = CFNumberCreate(NULL, kCFNumberSInt8Type, &tmp); - if (!num) - LogMsg("SPSStatusPutNumber: Could not create CFNumber"); - else - { - CFDictionarySetValue(dict, key, num); - CFRelease(num); - } - } +{ + mDNSu8 tmp = (ptr[0] - '0') * 10 + ptr[1] - '0'; + CFNumberRef num = CFNumberCreate(NULL, kCFNumberSInt8Type, &tmp); + if (!num) + LogMsg("SPSStatusPutNumber: Could not create CFNumber"); + else + { + CFDictionarySetValue(dict, key, num); + CFRelease(num); + } +} mDNSlocal CFMutableDictionaryRef SPSCreateDict(const mDNSu8* const ptr) - { - CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - if (!dict) { LogMsg("SPSCreateDict: Could not create CFDictionary dict"); return dict; } - - char buffer[1024]; - buffer[mDNS_snprintf(buffer, sizeof(buffer), "%##s", ptr) - 1] = 0; - CFStringRef spsname = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); - if (!spsname) { LogMsg("SPSCreateDict: Could not create CFString spsname full"); CFRelease(dict); return NULL; } - CFDictionarySetValue(dict, CFSTR("FullName"), spsname); - CFRelease(spsname); - - if (ptr[0] >= 2) SPSStatusPutNumber(dict, ptr + 1, CFSTR("Type")); - if (ptr[0] >= 5) SPSStatusPutNumber(dict, ptr + 4, CFSTR("Portability")); - if (ptr[0] >= 8) SPSStatusPutNumber(dict, ptr + 7, CFSTR("MarginalPower")); - if (ptr[0] >= 11) SPSStatusPutNumber(dict, ptr +10, CFSTR("TotalPower")); - - mDNSu32 tmp = SPSMetric(ptr); - CFNumberRef num = CFNumberCreate(NULL, kCFNumberSInt32Type, &tmp); - if (!num) - LogMsg("SPSCreateDict: Could not create CFNumber"); - else - { - CFDictionarySetValue(dict, kMetricRef, num); - CFRelease(num); - } - - if (ptr[0] >= 12) - { - memcpy(buffer, ptr + 13, ptr[0] - 12); - buffer[ptr[0] - 12] = 0; - spsname = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); - if (!spsname) { LogMsg("SPSCreateDict: Could not create CFString spsname"); CFRelease(dict); return NULL; } - else - { - CFDictionarySetValue(dict, CFSTR("PrettyName"), spsname); - CFRelease(spsname); - } - } - - return dict; - } - +{ + CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!dict) { LogMsg("SPSCreateDict: Could not create CFDictionary dict"); return dict; } + + char buffer[1024]; + buffer[mDNS_snprintf(buffer, sizeof(buffer), "%##s", ptr) - 1] = 0; + CFStringRef spsname = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); + if (!spsname) { LogMsg("SPSCreateDict: Could not create CFString spsname full"); CFRelease(dict); return NULL; } + CFDictionarySetValue(dict, CFSTR("FullName"), spsname); + CFRelease(spsname); + + if (ptr[0] >= 2) SPSStatusPutNumber(dict, ptr + 1, CFSTR("Type")); + if (ptr[0] >= 5) SPSStatusPutNumber(dict, ptr + 4, CFSTR("Portability")); + if (ptr[0] >= 8) SPSStatusPutNumber(dict, ptr + 7, CFSTR("MarginalPower")); + if (ptr[0] >= 11) SPSStatusPutNumber(dict, ptr +10, CFSTR("TotalPower")); + + mDNSu32 tmp = SPSMetric(ptr); + CFNumberRef num = CFNumberCreate(NULL, kCFNumberSInt32Type, &tmp); + if (!num) + LogMsg("SPSCreateDict: Could not create CFNumber"); + else + { + CFDictionarySetValue(dict, kMetricRef, num); + CFRelease(num); + } + + if (ptr[0] >= 12) + { + memcpy(buffer, ptr + 13, ptr[0] - 12); + buffer[ptr[0] - 12] = 0; + spsname = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); + if (!spsname) { LogMsg("SPSCreateDict: Could not create CFString spsname"); CFRelease(dict); return NULL; } + else + { + CFDictionarySetValue(dict, CFSTR("PrettyName"), spsname); + CFRelease(spsname); + } + } + + return dict; +} + mDNSlocal CFComparisonResult CompareSPSEntries(const void *val1, const void *val2, void *context) - { - (void)context; - return CFNumberCompare((CFNumberRef)CFDictionaryGetValue((CFDictionaryRef)val1, kMetricRef), - (CFNumberRef)CFDictionaryGetValue((CFDictionaryRef)val2, kMetricRef), - NULL); - } +{ + (void)context; + return CFNumberCompare((CFNumberRef)CFDictionaryGetValue((CFDictionaryRef)val1, kMetricRef), + (CFNumberRef)CFDictionaryGetValue((CFDictionaryRef)val2, kMetricRef), + NULL); +} mDNSlocal void UpdateSPSStatus(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) - { - NetworkInterfaceInfo* info = (NetworkInterfaceInfo*)question->QuestionContext; - debugf("UpdateSPSStatus: %s %##s %s %s", info->ifname, question->qname.c, AddRecord ? "Add" : "Rmv", answer ? RRDisplayString(m, answer) : ""); - - mDNS_Lock(m); - mDNS_UpdateAllowSleep(m); - mDNS_Unlock(m); - - if (answer && SPSMetric(answer->rdata->u.name.c) > 999999) return; // Ignore instances with invalid names - - if (!spsStatusDict) - { - spsStatusDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - if (!spsStatusDict) { LogMsg("UpdateSPSStatus: Could not create CFDictionary spsStatusDict"); return; } - } - - CFStringRef ifname = CFStringCreateWithCString(NULL, info->ifname, kCFStringEncodingUTF8); - if (!ifname) { LogMsg("UpdateSPSStatus: Could not create CFString ifname"); return; } - - CFMutableArrayRef array = NULL; - - if (!CFDictionaryGetValueIfPresent(spsStatusDict, ifname, (const void**) &array)) - { - array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - if (!array) { LogMsg("UpdateSPSStatus: Could not create CFMutableArray"); CFRelease(ifname); return; } - CFDictionarySetValue(spsStatusDict, ifname, array); - CFRelease(array); // let go of our reference, now that the dict has one - } - else - if (!array) { LogMsg("UpdateSPSStatus: Could not get CFMutableArray for %s", info->ifname); CFRelease(ifname); return; } - - if (!answer) // special call that means the question has been stopped (because the interface is going away) - CFArrayRemoveAllValues(array); - else - { - CFMutableDictionaryRef dict = SPSCreateDict(answer->rdata->u.name.c); - if (!dict) { CFRelease(ifname); return; } - - if (AddRecord) - { - if (!CFArrayContainsValue(array, CFRangeMake(0, CFArrayGetCount(array)), dict)) - { - int i=0; - for (i=0; iifname, answer->rdata->u.name.c); - } - else - { - CFIndex i = CFArrayGetFirstIndexOfValue(array, CFRangeMake(0, CFArrayGetCount(array)), dict); - if (i != -1) CFArrayRemoveValueAtIndex(array, i); - else LogMsg("UpdateSPSStatus: %s array does not contain %##s", info->ifname, answer->rdata->u.name.c); - } - - CFRelease(dict); - } - - if (!m->ShutdownTime) mDNSDynamicStoreSetConfig(kmDNSSleepProxyServersState, info->ifname, array); - - CFRelease(ifname); - } +{ + NetworkInterfaceInfo* info = (NetworkInterfaceInfo*)question->QuestionContext; + debugf("UpdateSPSStatus: %s %##s %s %s", info->ifname, question->qname.c, AddRecord ? "Add" : "Rmv", answer ? RRDisplayString(m, answer) : ""); -mDNSlocal mDNSs32 GetSystemSleepTimerSetting(void) - { - mDNSs32 val = -1; - SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetSystemSleepTimerSetting"), NULL, NULL); - if (!store) - LogMsg("GetSystemSleepTimerSetting: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); - else - { - CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_PowerSettings); - if (dict) - { - CFNumberRef number = CFDictionaryGetValue(dict, CFSTR("System Sleep Timer")); - if (number) CFNumberGetValue(number, kCFNumberSInt32Type, &val); - CFRelease(dict); - } - CFRelease(store); - } - return val; - } + mDNS_Lock(m); + mDNS_UpdateAllowSleep(m); + mDNS_Unlock(m); -mDNSlocal void SetSPS(mDNS *const m) - { - SCPreferencesSynchronize(m->p->SCPrefs); - CFDictionaryRef dict = SCPreferencesGetValue(m->p->SCPrefs, CFSTR("NAT")); - mDNSBool natenabled = (dict && (CFGetTypeID(dict) == CFDictionaryGetTypeID()) && DictionaryIsEnabled(dict)); - mDNSu8 sps = natenabled ? mDNSSleepProxyMetric_PrimarySoftware : - (OfferSleepProxyService && GetSystemSleepTimerSetting() == 0) ? mDNSSleepProxyMetric_IncidentalSoftware : 0; - - // For devices that are not running NAT, but are set to never sleep, we may choose to act - // as a Sleep Proxy, but only for non-portable Macs (Portability > 35 means nominal weight < 3kg) - //if (sps > mDNSSleepProxyMetric_PrimarySoftware && SPMetricPortability > 35) sps = 0; - - // If we decide to let laptops act as Sleep Proxy, we should do it only when running on AC power, not on battery - - // For devices that are unable to sleep at all to save power, or save 1W or less by sleeping, - // it makes sense for them to offer low-priority Sleep Proxy service on the network. - // We rate such a device as metric 70 ("Incidentally Available Hardware") - if (SPMetricMarginalPower <= 60 && !sps) sps = mDNSSleepProxyMetric_IncidentalHardware; - - // If the launchd plist specifies an explicit value for the Intent Metric, then use that instead of the - // computed value (currently 40 "Primary Network Infrastructure Software" or 80 "Incidentally Available Software") - if (sps && OfferSleepProxyService && OfferSleepProxyService < 100) sps = OfferSleepProxyService; - - mDNSCoreBeSleepProxyServer(m, sps, SPMetricPortability, SPMetricMarginalPower, SPMetricTotalPower); - } - -mDNSlocal void InternetSharingChanged(SCPreferencesRef prefs, SCPreferencesNotification notificationType, void *context) - { - (void)prefs; // Parameter not used - (void)notificationType; // Parameter not used - mDNS *const m = (mDNS *const)context; - KQueueLock(m); - mDNS_Lock(m); - - // Tell platform layer to open or close its BPF fds - if (!m->p->NetworkChanged || - m->p->NetworkChanged - NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2) < 0) - { - m->p->NetworkChanged = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2); - LogInfo("InternetSharingChanged: Set NetworkChanged to %d (%d)", m->p->NetworkChanged - m->timenow, m->p->NetworkChanged); - } - - mDNS_Unlock(m); - KQueueUnlock(m, "InternetSharingChanged"); - } - -mDNSlocal mStatus WatchForInternetSharingChanges(mDNS *const m) - { - SCPreferencesRef SCPrefs = SCPreferencesCreate(NULL, CFSTR("mDNSResponder:WatchForInternetSharingChanges"), CFSTR("com.apple.nat.plist")); - if (!SCPrefs) { LogMsg("SCPreferencesCreate failed: %s", SCErrorString(SCError())); return(mStatus_NoMemoryErr); } - - SCPreferencesContext context = { 0, m, NULL, NULL, NULL }; - if (!SCPreferencesSetCallback(SCPrefs, InternetSharingChanged, &context)) - { LogMsg("SCPreferencesSetCallback failed: %s", SCErrorString(SCError())); CFRelease(SCPrefs); return(mStatus_NoMemoryErr); } + if (answer && SPSMetric(answer->rdata->u.name.c) > 999999) return; // Ignore instances with invalid names -#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM - if (!SCPreferencesSetDispatchQueue( SCPrefs, dispatch_get_main_queue())) - { LogMsg("SCPreferencesSetDispatchQueue failed: %s", SCErrorString(SCError())); return(mStatus_NoMemoryErr); } + if (!spsStatusDict) + { + spsStatusDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!spsStatusDict) { LogMsg("UpdateSPSStatus: Could not create CFDictionary spsStatusDict"); return; } + } + + CFStringRef ifname = CFStringCreateWithCString(NULL, info->ifname, kCFStringEncodingUTF8); + if (!ifname) { LogMsg("UpdateSPSStatus: Could not create CFString ifname"); return; } + + CFMutableArrayRef array = NULL; + + if (!CFDictionaryGetValueIfPresent(spsStatusDict, ifname, (const void**) &array)) + { + array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (!array) { LogMsg("UpdateSPSStatus: Could not create CFMutableArray"); CFRelease(ifname); return; } + CFDictionarySetValue(spsStatusDict, ifname, array); + CFRelease(array); // let go of our reference, now that the dict has one + } + else + if (!array) { LogMsg("UpdateSPSStatus: Could not get CFMutableArray for %s", info->ifname); CFRelease(ifname); return; } + + if (!answer) // special call that means the question has been stopped (because the interface is going away) + CFArrayRemoveAllValues(array); + else + { + CFMutableDictionaryRef dict = SPSCreateDict(answer->rdata->u.name.c); + if (!dict) { CFRelease(ifname); return; } + + if (AddRecord) + { + if (!CFArrayContainsValue(array, CFRangeMake(0, CFArrayGetCount(array)), dict)) + { + int i=0; + for (i=0; iifname, answer->rdata->u.name.c); + } + else + { + CFIndex i = CFArrayGetFirstIndexOfValue(array, CFRangeMake(0, CFArrayGetCount(array)), dict); + if (i != -1) CFArrayRemoveValueAtIndex(array, i); + else LogMsg("UpdateSPSStatus: %s array does not contain %##s", info->ifname, answer->rdata->u.name.c); + } + + CFRelease(dict); + } + + if (!m->ShutdownTime) mDNSDynamicStoreSetConfig(kmDNSSleepProxyServersState, info->ifname, array); + + CFRelease(ifname); +} + +mDNSlocal mDNSs32 GetSystemSleepTimerSetting(void) +{ + mDNSs32 val = -1; + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetSystemSleepTimerSetting"), NULL, NULL); + if (!store) + LogMsg("GetSystemSleepTimerSetting: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); + else + { + CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_PowerSettings); + if (dict) + { + CFNumberRef number = CFDictionaryGetValue(dict, CFSTR("System Sleep Timer")); + if (number) CFNumberGetValue(number, kCFNumberSInt32Type, &val); + CFRelease(dict); + } + CFRelease(store); + } + return val; +} + +#if !TARGET_OS_IPHONE +mDNSlocal mDNSBool GetInternetSharingStatus(void) +{ + mDNSBool ret = mDNSfalse; + + // Currently, we do not have a mechanism to advertise sleep proxy services only on the network on which Internet sharing + // is enabled. As a result, we start acting as a very unreliable sleep proxy on other networks when Internet sharing is + // turned on. + // We should look at ways to advertise only on the relevant network. Till this is done, we should turn off this feature. + LogInfo("GetInternetSharingStatus: Disabled"); + return mDNSfalse; + + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:mDNSPlatformInternetSharing"), NULL, NULL); + if (!store) + LogMsg("GetInternetSharingStatus: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); + else + { + CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_InternetSharing); + if (dict) + { + CFNumberRef state = (CFNumberRef)CFDictionaryGetValue(dict, CFSTR("State")); + if (state) + { + mDNSu32 val; + if (CFNumberGetValue(state, kCFNumberSInt32Type, &val)) + ret = (val == MIS_SVC_STATE_ON) ? mDNStrue : mDNSfalse; + else + LogMsg("GetInternetSharingStatus: CFNumberGetValue error"); + } + CFRelease(dict); + } + CFRelease(store); + } + LogInfo("GetInternetSharingStatus: Internet Sharing state %s", (!ret ? "OFF" : "ON")); + return ret; +} #else - if (!SCPreferencesScheduleWithRunLoop(SCPrefs, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) - { LogMsg("SCPreferencesScheduleWithRunLoop failed: %s", SCErrorString(SCError())); CFRelease(SCPrefs); return(mStatus_NoMemoryErr); } +// We don't want to support this yet in embedded +mDNSlocal mDNSBool GetInternetSharingStatus(void) +{ + LogInfo("GetInternetSharingStatus: Disabled on embedded"); + return mDNSfalse; +} #endif - m->p->SCPrefs = SCPrefs; - return(mStatus_NoError); - } +mDNSlocal void SetSPS(mDNS *const m) +{ + mDNSu8 sps = GetInternetSharingStatus() ? mDNSSleepProxyMetric_PrimarySoftware : + (OfferSleepProxyService && GetSystemSleepTimerSetting() == 0) ? mDNSSleepProxyMetric_IncidentalSoftware : 0; + + // For devices that are not running NAT, but are set to never sleep, we may choose to act + // as a Sleep Proxy, but only for non-portable Macs (Portability > 35 means nominal weight < 3kg) + //if (sps > mDNSSleepProxyMetric_PrimarySoftware && SPMetricPortability > 35) sps = 0; + + // If we decide to let laptops act as Sleep Proxy, we should do it only when running on AC power, not on battery + + // For devices that are unable to sleep at all to save power, or save 1W or less by sleeping, + // it makes sense for them to offer low-priority Sleep Proxy service on the network. + // We rate such a device as metric 70 ("Incidentally Available Hardware") + if (SPMetricMarginalPower <= 60 && !sps) sps = mDNSSleepProxyMetric_IncidentalHardware; + + // If the launchd plist specifies an explicit value for the Intent Metric, then use that instead of the + // computed value (currently 40 "Primary Network Infrastructure Software" or 80 "Incidentally Available Software") + if (sps && OfferSleepProxyService && OfferSleepProxyService < 100) sps = OfferSleepProxyService; + + mDNSCoreBeSleepProxyServer(m, sps, SPMetricPortability, SPMetricMarginalPower, SPMetricTotalPower, SPMetricFeatures); +} // The definitions below should eventually come from some externally-supplied header file. // However, since these definitions can't really be changed without breaking binary compatibility, @@ -6222,1371 +6341,1564 @@ mDNSlocal mStatus WatchForInternetSharingChanges(mDNS *const m) #define mDNS_USER_CLIENT_CREATE_TYPE 'mDNS' enum - { // commands from the daemon to the driver - cmd_mDNSOffloadRR = 21, // give the mdns update buffer to the driver - }; +{ // commands from the daemon to the driver + cmd_mDNSOffloadRR = 21, // give the mdns update buffer to the driver +}; typedef union { void *ptr; mDNSOpaque64 sixtyfourbits; } FatPtr; typedef struct - { // cmd_mDNSOffloadRR structure - uint32_t command; // set to OffloadRR - uint32_t rrBufferSize; // number of bytes of RR records - uint32_t numUDPPorts; // number of SRV UDP ports - uint32_t numTCPPorts; // number of SRV TCP ports - uint32_t numRRRecords; // number of RR records - uint32_t compression; // rrRecords - compression is base for compressed strings - FatPtr rrRecords; // address of array of pointers to the rr records - FatPtr udpPorts; // address of udp port list (SRV) - FatPtr tcpPorts; // address of tcp port list (SRV) - } mDNSOffloadCmd; +{ // cmd_mDNSOffloadRR structure + uint32_t command; // set to OffloadRR + uint32_t rrBufferSize; // number of bytes of RR records + uint32_t numUDPPorts; // number of SRV UDP ports + uint32_t numTCPPorts; // number of SRV TCP ports + uint32_t numRRRecords; // number of RR records + uint32_t compression; // rrRecords - compression is base for compressed strings + FatPtr rrRecords; // address of array of pointers to the rr records + FatPtr udpPorts; // address of udp port list (SRV) + FatPtr tcpPorts; // address of tcp port list (SRV) +} mDNSOffloadCmd; #include #include mDNSlocal mDNSu16 GetPortArray(mDNS *const m, int trans, mDNSIPPort *portarray) - { - const domainlabel *const tp = (trans == mDNSTransport_UDP) ? (const domainlabel *)"\x4_udp" : (const domainlabel *)"\x4_tcp"; - int count = 0; - AuthRecord *rr; - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.rrtype == kDNSType_SRV && SameDomainLabel(ThirdLabel(rr->resrec.name)->c, tp->c)) - { - if (portarray) portarray[count] = rr->resrec.rdata->u.srv.port; - count++; - } - - // If Back to My Mac is on, also wake for packets to the IPSEC UDP port (4500) - if (trans == mDNSTransport_UDP && TunnelServers(m)) - { - LogSPS("GetPortArray Back to My Mac at %d", count); - if (portarray) portarray[count] = IPSECPort; - count++; - } - return(count); - } +{ + const domainlabel *const tp = (trans == mDNSTransport_UDP) ? (const domainlabel *)"\x4_udp" : (const domainlabel *)"\x4_tcp"; + int count = 0; + AuthRecord *rr; + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.rrtype == kDNSType_SRV && SameDomainLabel(ThirdLabel(rr->resrec.name)->c, tp->c)) + { + if (portarray) portarray[count] = rr->resrec.rdata->u.srv.port; + count++; + } + + // If Back to My Mac is on, also wake for packets to the IPSEC UDP port (4500) + if (trans == mDNSTransport_UDP && m->AutoTunnelNAT.clientContext) + { + LogSPS("GetPortArray Back to My Mac at %d", count); + if (portarray) portarray[count] = IPSECPort; + count++; + } + return(count); +} #define TfrRecordToNIC(RR) \ - ((!(RR)->resrec.InterfaceID && ((RR)->ForceMCast || IsLocalDomain((RR)->resrec.name)))) + ((!(RR)->resrec.InterfaceID && ((RR)->ForceMCast || IsLocalDomain((RR)->resrec.name)))) mDNSlocal mDNSu32 CountProxyRecords(mDNS *const m, uint32_t *const numbytes) - { - *numbytes = 0; - int count = 0; - AuthRecord *rr; - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.RecordType > kDNSRecordTypeDeregistering) - if (TfrRecordToNIC(rr)) - { - *numbytes += DomainNameLength(rr->resrec.name) + 10 + rr->resrec.rdestimate; - LogSPS("CountProxyRecords: %3d size %5d total %5d %s", - count, DomainNameLength(rr->resrec.name) + 10 + rr->resrec.rdestimate, *numbytes, ARDisplayString(m,rr)); - count++; - } - return(count); - } +{ + *numbytes = 0; + int count = 0; + AuthRecord *rr; + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.RecordType > kDNSRecordTypeDeregistering) + if (TfrRecordToNIC(rr)) + { + *numbytes += DomainNameLength(rr->resrec.name) + 10 + rr->resrec.rdestimate; + LogSPS("CountProxyRecords: %3d size %5d total %5d %s", + count, DomainNameLength(rr->resrec.name) + 10 + rr->resrec.rdestimate, *numbytes, ARDisplayString(m,rr)); + count++; + } + return(count); +} mDNSlocal void GetProxyRecords(mDNS *const m, DNSMessage *const msg, uint32_t *const numbytes, FatPtr *const records) - { - mDNSu8 *p = msg->data; - const mDNSu8 *const limit = p + *numbytes; - InitializeDNSMessage(&msg->h, zeroID, zeroID); - - int count = 0; - AuthRecord *rr; - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.RecordType > kDNSRecordTypeDeregistering) - if (TfrRecordToNIC(rr)) - { - records[count].sixtyfourbits = zeroOpaque64; - records[count].ptr = p; - if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) - rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the 'unique' bit so PutResourceRecord will set it - p = PutResourceRecordTTLWithLimit(msg, p, &msg->h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl, limit); - rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear 'unique' bit back to normal state - LogSPS("GetProxyRecords: %3d start %p end %p size %5d total %5d %s", - count, records[count].ptr, p, p - (mDNSu8 *)records[count].ptr, p - msg->data, ARDisplayString(m,rr)); - count++; - } - *numbytes = p - msg->data; - } +{ + mDNSu8 *p = msg->data; + const mDNSu8 *const limit = p + *numbytes; + InitializeDNSMessage(&msg->h, zeroID, zeroID); + + int count = 0; + AuthRecord *rr; + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.RecordType > kDNSRecordTypeDeregistering) + if (TfrRecordToNIC(rr)) + { + records[count].sixtyfourbits = zeroOpaque64; + records[count].ptr = p; + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the 'unique' bit so PutResourceRecord will set it + p = PutResourceRecordTTLWithLimit(msg, p, &msg->h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl, limit); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear 'unique' bit back to normal state + LogSPS("GetProxyRecords: %3d start %p end %p size %5d total %5d %s", + count, records[count].ptr, p, p - (mDNSu8 *)records[count].ptr, p - msg->data, ARDisplayString(m,rr)); + count++; + } + *numbytes = p - msg->data; +} // If compiling with old headers and libraries (pre 10.5) that don't include IOConnectCallStructMethod // then we declare a dummy version here so that the code at least compiles #ifndef AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER static kern_return_t IOConnectCallStructMethod( - mach_port_t connection, // In - uint32_t selector, // In - const void *inputStruct, // In - size_t inputStructCnt, // In - void *outputStruct, // Out - size_t *outputStructCnt) // In/Out - { - (void)connection; - (void)selector; - (void)inputStruct; - (void)inputStructCnt; - (void)outputStruct; - (void)outputStructCnt; - LogMsg("Compiled without IOConnectCallStructMethod"); - return(KERN_FAILURE); - } + mach_port_t connection, // In + uint32_t selector, // In + const void *inputStruct, // In + size_t inputStructCnt, // In + void *outputStruct, // Out + size_t *outputStructCnt) // In/Out +{ + (void)connection; + (void)selector; + (void)inputStruct; + (void)inputStructCnt; + (void)outputStruct; + (void)outputStructCnt; + LogMsg("Compiled without IOConnectCallStructMethod"); + return(KERN_FAILURE); +} #endif -mDNSexport mStatus ActivateLocalProxy(mDNS *const m, char *ifname) // Called with the lock held - { - mStatus result = mStatus_UnknownErr; - io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOBSDNameMatching(kIOMasterPortDefault, 0, ifname)); - if (!service) { LogMsg("ActivateLocalProxy: No service for interface %s", ifname); return(mStatus_UnknownErr); } - - io_name_t n1, n2; - IOObjectGetClass(service, n1); - io_object_t parent; - kern_return_t kr = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent); - if (kr != KERN_SUCCESS) LogMsg("ActivateLocalProxy: IORegistryEntryGetParentEntry for %s/%s failed %d", ifname, n1, kr); - else - { - IOObjectGetClass(parent, n2); - LogSPS("ActivateLocalProxy: Interface %s service %s parent %s", ifname, n1, n2); - const CFTypeRef ref = IORegistryEntryCreateCFProperty(parent, CFSTR(mDNS_IOREG_KEY), kCFAllocatorDefault, mDNSNULL); - if (!ref) LogSPS("ActivateLocalProxy: No mDNS_IOREG_KEY for interface %s/%s/%s", ifname, n1, n2); - else - { - if (CFGetTypeID(ref) != CFStringGetTypeID() || !CFEqual(ref, CFSTR(mDNS_IOREG_VALUE))) - LogMsg("ActivateLocalProxy: mDNS_IOREG_KEY for interface %s/%s/%s value %s != %s", - ifname, n1, n2, CFStringGetCStringPtr(ref, mDNSNULL), mDNS_IOREG_VALUE); - else if (!UseInternalSleepProxy) - LogSPS("ActivateLocalProxy: Not using internal (NIC) sleep proxy for interface %s", ifname); - else - { - io_connect_t conObj; - kr = IOServiceOpen(parent, mach_task_self(), mDNS_USER_CLIENT_CREATE_TYPE, &conObj); - if (kr != KERN_SUCCESS) LogMsg("ActivateLocalProxy: IOServiceOpen for %s/%s/%s failed %d", ifname, n1, n2, kr); - else - { - mDNSOffloadCmd cmd; - mDNSPlatformMemZero(&cmd, sizeof(cmd)); // When compiling 32-bit, make sure top 32 bits of 64-bit pointers get initialized to zero - cmd.command = cmd_mDNSOffloadRR; - cmd.numUDPPorts = GetPortArray(m, mDNSTransport_UDP, mDNSNULL); - cmd.numTCPPorts = GetPortArray(m, mDNSTransport_TCP, mDNSNULL); - cmd.numRRRecords = CountProxyRecords(m, &cmd.rrBufferSize); - cmd.compression = sizeof(DNSMessageHeader); - - DNSMessage *msg = (DNSMessage *)mallocL("mDNSOffloadCmd msg", sizeof(DNSMessageHeader) + cmd.rrBufferSize); - cmd.rrRecords.ptr = mallocL("mDNSOffloadCmd rrRecords", cmd.numRRRecords * sizeof(FatPtr)); - cmd.udpPorts .ptr = mallocL("mDNSOffloadCmd udpPorts", cmd.numUDPPorts * sizeof(mDNSIPPort)); - cmd.tcpPorts .ptr = mallocL("mDNSOffloadCmd tcpPorts", cmd.numTCPPorts * sizeof(mDNSIPPort)); - - LogSPS("ActivateLocalProxy: msg %p %d RR %p %d, UDP %p %d, TCP %p %d", - msg, cmd.rrBufferSize, - cmd.rrRecords.ptr, cmd.numRRRecords, - cmd.udpPorts .ptr, cmd.numUDPPorts, - cmd.tcpPorts .ptr, cmd.numTCPPorts); - - if (!msg || !cmd.rrRecords.ptr || !cmd.udpPorts.ptr || !cmd.tcpPorts.ptr) - LogMsg("ActivateLocalProxy: Failed to allocate memory: msg %p %d RR %p %d, UDP %p %d, TCP %p %d", - msg, cmd.rrBufferSize, - cmd.rrRecords.ptr, cmd.numRRRecords, - cmd.udpPorts .ptr, cmd.numUDPPorts, - cmd.tcpPorts .ptr, cmd.numTCPPorts); - else - { - GetProxyRecords(m, msg, &cmd.rrBufferSize, cmd.rrRecords.ptr); - GetPortArray(m, mDNSTransport_UDP, cmd.udpPorts.ptr); - GetPortArray(m, mDNSTransport_TCP, cmd.tcpPorts.ptr); - char outputData[2]; - size_t outputDataSize = sizeof(outputData); - kr = IOConnectCallStructMethod(conObj, 0, &cmd, sizeof(cmd), outputData, &outputDataSize); - LogSPS("ActivateLocalProxy: IOConnectCallStructMethod for %s/%s/%s %d", ifname, n1, n2, kr); - if (kr == KERN_SUCCESS) result = mStatus_NoError; - } - - if (cmd.tcpPorts. ptr) freeL("mDNSOffloadCmd udpPorts", cmd.tcpPorts .ptr); - if (cmd.udpPorts. ptr) freeL("mDNSOffloadCmd tcpPorts", cmd.udpPorts .ptr); - if (cmd.rrRecords.ptr) freeL("mDNSOffloadCmd rrRecords", cmd.rrRecords.ptr); - if (msg) freeL("mDNSOffloadCmd msg", msg); - IOServiceClose(conObj); - } - } - CFRelease(ref); - } - IOObjectRelease(parent); - } - IOObjectRelease(service); - return result; - } +mDNSexport mStatus ActivateLocalProxy(mDNS *const m, char *ifname) // Called with the lock held +{ + mStatus result = mStatus_UnknownErr; + io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOBSDNameMatching(kIOMasterPortDefault, 0, ifname)); + if (!service) { LogMsg("ActivateLocalProxy: No service for interface %s", ifname); return(mStatus_UnknownErr); } + + io_name_t n1, n2; + IOObjectGetClass(service, n1); + io_object_t parent; + kern_return_t kr = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent); + if (kr != KERN_SUCCESS) LogMsg("ActivateLocalProxy: IORegistryEntryGetParentEntry for %s/%s failed %d", ifname, n1, kr); + else + { + IOObjectGetClass(parent, n2); + LogSPS("ActivateLocalProxy: Interface %s service %s parent %s", ifname, n1, n2); + const CFTypeRef ref = IORegistryEntryCreateCFProperty(parent, CFSTR(mDNS_IOREG_KEY), kCFAllocatorDefault, mDNSNULL); + if (!ref) LogSPS("ActivateLocalProxy: No mDNS_IOREG_KEY for interface %s/%s/%s", ifname, n1, n2); + else + { + if (CFGetTypeID(ref) != CFStringGetTypeID() || !CFEqual(ref, CFSTR(mDNS_IOREG_VALUE))) + LogMsg("ActivateLocalProxy: mDNS_IOREG_KEY for interface %s/%s/%s value %s != %s", + ifname, n1, n2, CFStringGetCStringPtr(ref, mDNSNULL), mDNS_IOREG_VALUE); + else if (!UseInternalSleepProxy) + LogSPS("ActivateLocalProxy: Not using internal (NIC) sleep proxy for interface %s", ifname); + else + { + io_connect_t conObj; + kr = IOServiceOpen(parent, mach_task_self(), mDNS_USER_CLIENT_CREATE_TYPE, &conObj); + if (kr != KERN_SUCCESS) LogMsg("ActivateLocalProxy: IOServiceOpen for %s/%s/%s failed %d", ifname, n1, n2, kr); + else + { + mDNSOffloadCmd cmd; + mDNSPlatformMemZero(&cmd, sizeof(cmd)); // When compiling 32-bit, make sure top 32 bits of 64-bit pointers get initialized to zero + cmd.command = cmd_mDNSOffloadRR; + cmd.numUDPPorts = GetPortArray(m, mDNSTransport_UDP, mDNSNULL); + cmd.numTCPPorts = GetPortArray(m, mDNSTransport_TCP, mDNSNULL); + cmd.numRRRecords = CountProxyRecords(m, &cmd.rrBufferSize); + cmd.compression = sizeof(DNSMessageHeader); + + DNSMessage *msg = (DNSMessage *)mallocL("mDNSOffloadCmd msg", sizeof(DNSMessageHeader) + cmd.rrBufferSize); + cmd.rrRecords.ptr = mallocL("mDNSOffloadCmd rrRecords", cmd.numRRRecords * sizeof(FatPtr)); + cmd.udpPorts.ptr = mallocL("mDNSOffloadCmd udpPorts", cmd.numUDPPorts * sizeof(mDNSIPPort)); + cmd.tcpPorts.ptr = mallocL("mDNSOffloadCmd tcpPorts", cmd.numTCPPorts * sizeof(mDNSIPPort)); + + LogSPS("ActivateLocalProxy: msg %p %d RR %p %d, UDP %p %d, TCP %p %d", + msg, cmd.rrBufferSize, + cmd.rrRecords.ptr, cmd.numRRRecords, + cmd.udpPorts.ptr, cmd.numUDPPorts, + cmd.tcpPorts.ptr, cmd.numTCPPorts); + + if (!msg || !cmd.rrRecords.ptr || !cmd.udpPorts.ptr || !cmd.tcpPorts.ptr) + LogMsg("ActivateLocalProxy: Failed to allocate memory: msg %p %d RR %p %d, UDP %p %d, TCP %p %d", + msg, cmd.rrBufferSize, + cmd.rrRecords.ptr, cmd.numRRRecords, + cmd.udpPorts.ptr, cmd.numUDPPorts, + cmd.tcpPorts.ptr, cmd.numTCPPorts); + else + { + GetProxyRecords(m, msg, &cmd.rrBufferSize, cmd.rrRecords.ptr); + GetPortArray(m, mDNSTransport_UDP, cmd.udpPorts.ptr); + GetPortArray(m, mDNSTransport_TCP, cmd.tcpPorts.ptr); + char outputData[2]; + size_t outputDataSize = sizeof(outputData); + kr = IOConnectCallStructMethod(conObj, 0, &cmd, sizeof(cmd), outputData, &outputDataSize); + LogSPS("ActivateLocalProxy: IOConnectCallStructMethod for %s/%s/%s %d", ifname, n1, n2, kr); + if (kr == KERN_SUCCESS) result = mStatus_NoError; + } + + if (cmd.tcpPorts.ptr) freeL("mDNSOffloadCmd udpPorts", cmd.tcpPorts.ptr); + if (cmd.udpPorts.ptr) freeL("mDNSOffloadCmd tcpPorts", cmd.udpPorts.ptr); + if (cmd.rrRecords.ptr) freeL("mDNSOffloadCmd rrRecords", cmd.rrRecords.ptr); + if (msg) freeL("mDNSOffloadCmd msg", msg); + IOServiceClose(conObj); + } + } + CFRelease(ref); + } + IOObjectRelease(parent); + } + IOObjectRelease(service); + return result; +} #endif // APPLE_OSX_mDNSResponder static io_service_t g_rootdomain = MACH_PORT_NULL; mDNSlocal mDNSBool SystemWakeForNetworkAccess(void) - { - mDNSs32 val = 0; - CFBooleanRef clamshellStop = NULL; - mDNSBool retnow = mDNSfalse; - - if (DisableSleepProxyClient) { LogSPS("SystemWakeForNetworkAccess: Sleep Proxy Client disabled by command-line option"); return mDNSfalse; } - - GetCurrentPMSetting(CFSTR("Wake On LAN"), &val); - LogSPS("SystemWakeForNetworkAccess: Wake On LAN: %d", val); - if (!val) return mDNSfalse; - - if (!g_rootdomain) g_rootdomain = IORegistryEntryFromPath(MACH_PORT_NULL, kIOPowerPlane ":/IOPowerConnection/IOPMrootDomain"); - if (!g_rootdomain) { LogMsg("SystemWakeForNetworkAccess: IORegistryEntryFromPath failed; assuming no clamshell so can WOMP"); return mDNStrue; } - - clamshellStop = (CFBooleanRef)IORegistryEntryCreateCFProperty(g_rootdomain, CFSTR(kAppleClamshellStateKey), kCFAllocatorDefault, 0); - if (!clamshellStop) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellStateKey does not exist; assuming no clamshell so can WOMP"); return mDNStrue; } - retnow = clamshellStop == kCFBooleanFalse; - CFRelease(clamshellStop); - if (retnow) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellStateKey is false; clamshell is open so can WOMP"); return mDNStrue; } - - clamshellStop = (CFBooleanRef)IORegistryEntryCreateCFProperty(g_rootdomain, CFSTR(kAppleClamshellCausesSleepKey), kCFAllocatorDefault, 0); - if (!clamshellStop) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellCausesSleepKey does not exist; assuming no clamshell so can WOMP"); return mDNStrue; } - retnow = (clamshellStop == kCFBooleanFalse); - CFRelease(clamshellStop); - if (retnow) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellCausesSleepKey is false; clamshell is closed but can WOMP"); return mDNStrue; } - - LogSPS("SystemWakeForNetworkAccess: clamshell is closed and can't WOMP"); - return mDNSfalse; - } +{ + mDNSs32 val = 0; + CFBooleanRef clamshellStop = NULL; + mDNSBool retnow = mDNSfalse; + + if (DisableSleepProxyClient) { LogSPS("SystemWakeForNetworkAccess: Sleep Proxy Client disabled by command-line option"); return mDNSfalse; } + + GetCurrentPMSetting(CFSTR("Wake On LAN"), &val); + LogSPS("SystemWakeForNetworkAccess: Wake On LAN: %d", val); + if (!val) return mDNSfalse; + + if (!g_rootdomain) g_rootdomain = IORegistryEntryFromPath(MACH_PORT_NULL, kIOPowerPlane ":/IOPowerConnection/IOPMrootDomain"); + if (!g_rootdomain) { LogMsg("SystemWakeForNetworkAccess: IORegistryEntryFromPath failed; assuming no clamshell so can WOMP"); return mDNStrue; } + + clamshellStop = (CFBooleanRef)IORegistryEntryCreateCFProperty(g_rootdomain, CFSTR(kAppleClamshellStateKey), kCFAllocatorDefault, 0); + if (!clamshellStop) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellStateKey does not exist; assuming no clamshell so can WOMP"); return mDNStrue; } + retnow = clamshellStop == kCFBooleanFalse; + CFRelease(clamshellStop); + if (retnow) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellStateKey is false; clamshell is open so can WOMP"); return mDNStrue; } + + clamshellStop = (CFBooleanRef)IORegistryEntryCreateCFProperty(g_rootdomain, CFSTR(kAppleClamshellCausesSleepKey), kCFAllocatorDefault, 0); + if (!clamshellStop) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellCausesSleepKey does not exist; assuming no clamshell so can WOMP"); return mDNStrue; } + retnow = (clamshellStop == kCFBooleanFalse); + CFRelease(clamshellStop); + if (retnow) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellCausesSleepKey is false; clamshell is closed but can WOMP"); return mDNStrue; } + + LogSPS("SystemWakeForNetworkAccess: clamshell is closed and can't WOMP"); + return mDNSfalse; +} mDNSlocal mDNSBool SystemSleepOnlyIfWakeOnLAN(void) - { - mDNSs32 val = 0; - GetCurrentPMSetting(CFSTR("PrioritizeNetworkReachabilityOverSleep"), &val); - return val != 0 ? mDNStrue : mDNSfalse; - } +{ + mDNSs32 val = 0; + GetCurrentPMSetting(CFSTR("PrioritizeNetworkReachabilityOverSleep"), &val); + return val != 0 ? mDNStrue : mDNSfalse; +} #if APPLE_OSX_mDNSResponder -// If the _autotunnel6 record is still there in the list, we are waiting for the ack from -// the server. +// When sleeping, we always ensure that the _autotunnel6 record (if connected to RR relay) +// gets deregistered, so that older peers are forced to connect over direct UDP instead of +// the RR relay. +// +// When sleeping w/o a successful AutoTunnel NAT Mapping, we ensure that all our BTMM +// service records are deregistered, so they do not appear in peers' Finder sidebars. +// We do this by checking for the (non-autotunnel) SRV records, as the PTR and TXT records +// depend on their associated SRV record and therefore will be deregistered together in a +// single update with the SRV record. // -// If we are behind a double-NAT or NAT with no NAT-PMP support, we should make sure that all our -// BTMM records are deregistered so that it does not appear on the Finder sidebar of our peers -// when we go to sleep. First _autotunnel6 and the host record gets deregistered, then SRV -// (UpdateAllSrvRecords) and then PTR and TXT +// Also, the per-zone _kerberos TXT record is always there, including while sleeping, so +// its presence shouldn't delay sleep. // -// Note: We wait up to a maximum of 10 seconds before we ack the sleep. So, returning "false" -// here does not necessarily mean that it will be honored. +// Note that the order of record deregistration is: first _autotunnel6 (if connected to RR +// relay) and host records get deregistered, then SRV (UpdateAllSrvRecords), PTR and TXT. +// +// Also note that returning false here will not delay sleep past the maximum of 10 seconds. mDNSexport mDNSBool RecordReadyForSleep(mDNS *const m, AuthRecord *rr) - { - if (!AuthRecord_uDNS(rr)) return mDNStrue; - - if (SameDomainLabel(rr->namestorage.c, (const mDNSu8 *)"\x0c_autotunnel6")) - { - LogInfo("RecordReadyForSleep: %s not ready for sleep", ARDisplayString(m, rr)); - return mDNSfalse; - } - // Just check for the SRV record alone as the PTR and TXT records are dependent on SRV - // and will get deregistered together in a single update. We also don't check for TXT - // records as _kerberos TXT record is always there even when there are no services - // and we don't want to delay the sleep in that case. - if (mDNSIPPortIsZero(m->LLQNAT.ExternalPort) || m->LLQNAT.Result) - { - if ((rr->resrec.rrtype == kDNSType_SRV) && rr->state != regState_NoTarget && rr->zone) - { - DomainAuthInfo *info = GetAuthInfoForName_internal(m, rr->zone); - if (info && info->AutoTunnel) - { - LogInfo("RecordReadyForSleep: %s not ready for sleep", ARDisplayString(m, rr)); - return mDNSfalse; - } - } - } - return mDNStrue; - } - -// Note: BTMMDict needs to be retained by the caller if needed -mDNSlocal CFDictionaryRef ParseBackToMyMacKey(CFDictionaryRef connd) - { - CFDictionaryRef BTMMDict = CFDictionaryGetValue(connd, CFSTR("BackToMyMac")); - if (!BTMMDict) - { - LogInfo("ParseBackToMyMacKey: CFDictionaryGetValue No value for BackToMyMac"); - return NULL; - } - - // Non-dictionary is treated as non-existent dictionary - if (CFGetTypeID(BTMMDict) != CFDictionaryGetTypeID()) - {LogMsg("ERROR: ParseBackToMyMacKey: CFDictionaryGetValue BackToMyMac not a dictionary"); CFRelease(BTMMDict); return NULL;} - - return BTMMDict; - } - -mDNSlocal void ParseBTMMInterfaceKey(CFDictionaryRef BTMMDict, char *buf, int buflen) - { - CFTypeRef string; - mDNSBool ifExists; - - ifExists = CFDictionaryGetValueIfPresent(BTMMDict, CFSTR("Interface"), &string); - if (ifExists) - { - if (!CFStringGetCString(string, buf, buflen, kCFStringEncodingUTF8)) - { - LogMsg("ERROR: ParseBTMMInterfaceKey: Could not convert Interface to CString"); - if (buflen) buf[0] = 0; - return; - } - else - debugf("ParseBTMMInterfaceKey: Interface Key exists %s", buf); - } - else - { - if (buflen) buf[0] = 0; - debugf("ParseBTMMInterfaceKey: Interface Key does not exist"); - } - } +{ + if (!AuthRecord_uDNS(rr)) return mDNStrue; + + if ((rr->resrec.rrtype == kDNSType_AAAA) && SameDomainLabel(rr->namestorage.c, (const mDNSu8 *)"\x0c_autotunnel6")) + { + LogInfo("RecordReadyForSleep: %s not ready for sleep", ARDisplayString(m, rr)); + return mDNSfalse; + } + + if ((mDNSIPPortIsZero(m->AutoTunnelNAT.ExternalPort) || m->AutoTunnelNAT.Result)) + { + if (rr->resrec.rrtype == kDNSType_SRV && rr->state != regState_NoTarget && rr->zone + && !SameDomainLabel(rr->namestorage.c, (const mDNSu8 *)"\x0b_autotunnel")) + { + DomainAuthInfo *info = GetAuthInfoForName_internal(m, rr->zone); + if (info && info->AutoTunnel) + { + LogInfo("RecordReadyForSleep: %s not ready for sleep", ARDisplayString(m, rr)); + return mDNSfalse; + } + } + } + + return mDNStrue; +} +// Caller must hold the lock mDNSexport void RemoveAutoTunnel6Record(mDNS *const m) - { - DomainAuthInfo *info; - char buf[IFNAMSIZ]; - - // Did we parse a non-empty dictionary before ? - if (!m->p->ConndBTMMDict || (CFDictionaryGetCount(m->p->ConndBTMMDict) == 0)) - { - LogInfo("RemoveAutoTunnel6Record: Never registered any records before, not deregistering %p", m->p->ConndBTMMDict); - return; - } - - // Did we have a non-NULL Interface name before ? - ParseBTMMInterfaceKey(m->p->ConndBTMMDict, buf, sizeof(buf)); - if (!strlen(buf)) - { - LogInfo("RemoveAutoTunnel6Record: Interface name already NULL, not deregistering"); - return; - } - - // Set the address to zero before calling DeregisterAutoTunnel6Record. If we call - // Deregister too quickly before the previous Register completed (just scheduled - // to be sent out) and when DeregisterAutoTunnel6Record calls mDNS_Register_internal, - // it invokes the AutoTunnelRecordCallback immediately and AutoTunnelRelayAddrIn should - // be zero so that we don't register again. - m->AutoTunnelRelayAddrIn = zerov6Addr; - if (!m->AuthInfoList) LogInfo("RemoveAutoTunnel6Record: No Domain AuthInfo"); - for (info = m->AuthInfoList; info; info = info->next) - { - if (!info->AutoTunnel) { LogInfo("RemoveAutoTunnel6Record: Domain %##s not an AutoTunnel", info->domain.c); continue;} - - if (info->deltime) {LogInfo("RemoveAutoTunnel6Record: Domain %##s about to be deleted", info->domain.c); continue;} - - LogInfo("RemoveAutoTunnel6Record: Deregistering records for domain %##s", info->domain.c); - DeregisterAutoTunnel6Record(m, info); - } - CFRelease(m->p->ConndBTMMDict); - m->p->ConndBTMMDict = NULL; - } - -// Returns zero on success -mDNSlocal int GetIPv6AddressForIfname(char *ifname, mDNSv6Addr *ipv6Addr) - { - struct ifaddrs *ifa; - struct ifaddrs *ifaddrs; - mDNSAddr addr; - - if (if_nametoindex(ifname) == 0) {LogInfo("GetIPv6AddressForIfname: Invalid name %s", ifname); return (-1);} - - if (getifaddrs(&ifaddrs) < 0) {LogInfo("GetIPv6AddressForIfname: getifaddrs failed"); return (-1);} - - /* - * Find the ifaddr entry corresponding to the interface name, - * and return the first matching non-linklocal IPv6 address. - */ - for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) - { - if (strncmp(ifa->ifa_name, ifname, IFNAMSIZ) != 0) - continue; - if (ifa->ifa_flags & IFF_UP && ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6) - { - struct sockaddr_in6 *ifa_addr = (struct sockaddr_in6 *)ifa->ifa_addr; - if (IN6_IS_ADDR_LINKLOCAL(&ifa_addr->sin6_addr)) - continue; - if (SetupAddr(&addr, ifa->ifa_addr) != mStatus_NoError) - { - LogInfo("GetIPv6AddressForIfname: SetupAddr error, continuing to the next address"); - continue; - } - else - { - *ipv6Addr = *(mDNSv6Addr *)&addr.ip.v6; - LogInfo("GetIPv6AddressForIfname: Returning IPv6 address %.16a", ipv6Addr); - freeifaddrs(ifaddrs); - return 0; - } - } - } - LogInfo("GetIPv6AddressForIfname: No Valid IPv6 address"); - freeifaddrs(ifaddrs); - return (-1); - } - -mDNSlocal void AddAutoTunnel6Record(mDNS *const m, char *ifname, CFDictionaryRef BTMMDict) - { - mDNSv6Addr v6addr; - DomainAuthInfo *info; - - if (GetIPv6AddressForIfname(ifname, &v6addr) != 0) - { - LogInfo("AddAutoTunnel6Record: No Valid IPv6 addresses found for %s", ifname); - // If the interface does not exist but the dictionary has the value, we treat - // this case as though the dictionary does not have the value - RemoveAutoTunnel6Record(m); - // If awacsd crashes or exits for some reason, restart the relay connection - UpdateBTMMRelayConnection(m); - return; - } - - m->AutoTunnelRelayAddrOut = v6addr; - - // if disabled administratively, don't bother to register. RegisterAutoTunnel6Record makes these same - // checks, but we do it here not just as an optimization but mainly to keep AutoTunnelRelayAddrIn zero - // as a non-zero AutoTunnelRelayAddrIn indicates that we have registered _autotunnel6 record and hence - // other hosts can connect to this host through the relay - if (!m->RegisterAutoTunnel6 || DisableInboundRelayConnection) - { - LogInfo("RegisterAutoTunnel6Record: registration Disabled RegisterAutoTunnel6 %d, DisableInbound %d", - m->RegisterAutoTunnel6, DisableInboundRelayConnection); - return; - } - m->AutoTunnelRelayAddrIn = v6addr; - - if (!m->AuthInfoList) LogInfo("AddAutoTunnel6Record: No Domain AuthInfo"); - for (info = m->AuthInfoList; info; info = info->next) - { - // clientContext for a domain tells us that we are listening for at least one Service/Record - // in a domain and SetLocalAutoTunnelInterface_internal was called - if (!info->AutoTunnel) { LogInfo("AddAutoTunnel6Record: Domain %##s not an AutoTunnel", info->domain.c); continue;} - - if (!info->AutoTunnelNAT.clientContext) {LogInfo("AddAutoTunnel6Record: Domain %##s has no services", info->domain.c); continue;} - - if (info->deltime) {LogInfo("AddAutoTunnel6Record: Domain %##s about to be deleted", info->domain.c); continue;} - - LogInfo("AddAutoTunnel6Record: Registering records for domain %##s", info->domain.c); - mDNS_Lock(m); - RegisterAutoTunnel6Record(m, info); - mDNS_Unlock(m); - } - if (m->p->ConndBTMMDict) CFRelease(m->p->ConndBTMMDict); - m->p->ConndBTMMDict = CFRetain(BTMMDict); - } - -mDNSlocal void ParseBackToMyMac(mDNS *const m, CFDictionaryRef connd) - { - CFDictionaryRef BTMMDict; - char buf[IFNAMSIZ]; - - BTMMDict = ParseBackToMyMacKey(connd); - if (!BTMMDict) - { - LogInfo("ParseBackToMyMac: CFDictionaryGetValue No value for BackToMyMac, Removing autotunnel6"); - RemoveAutoTunnel6Record(m); - // Note: AutoTunnelRelayAddrIn is zeroed out in RemoveAutoTunnel6Record as it is called - // from other places. - m->AutoTunnelRelayAddrOut = zerov6Addr; - mDNS_Lock(m); - UpdateAutoTunnelDomainStatuses(m); - mDNS_Unlock(m); - return; - } - - ParseBTMMInterfaceKey(BTMMDict, buf, sizeof(buf)); - if (!strlen(buf)) - { - LogInfo("ParseBackToMyMac: NULL value for Interface, Removing autotunnel6"); - RemoveAutoTunnel6Record(m); - m->AutoTunnelRelayAddrOut = zerov6Addr; - // We don't have a utun interface, start the relay connection if possible - UpdateBTMMRelayConnection(m); - } - else - { - LogInfo("ParseBackToMyMac: non-NULL value for Interface, Adding autotunnel6"); - AddAutoTunnel6Record(m, buf, BTMMDict); - } - - mDNS_Lock(m); - UpdateAutoTunnelDomainStatuses(m); - mDNS_Unlock(m); - } - -mDNSlocal void SetupConndConfigChanges(mDNS *const m) - { - CFDictionaryRef connd; - SCDynamicStoreRef store; - - store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:SetupConndConfigChanges"), NULL, NULL); - if (!store) {LogMsg("SetupConndConfigChanges: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); return;} - - connd = SCDynamicStoreCopyValue(store, NetworkChangedKey_BTMMConnectivity); - if (!connd) - {LogInfo("SetupConndConfigChanges: SCDynamicStoreCopyValue failed: %s", SCErrorString(SCError())); CFRelease(store); return;} - else - { - ParseBackToMyMac(m, connd); - } - CFRelease(connd); - CFRelease(store); - } +{ + DomainAuthInfo *info; + // Set the address to zero before calling UpdateAutoTunnel6Record, so that it will + // deregister the record, and the MemFree callback won't re-register. + m->AutoTunnelRelayAddr = zerov6Addr; + for (info = m->AuthInfoList; info; info = info->next) + if (info->AutoTunnel) + UpdateAutoTunnel6Record(m, info); +} + +mDNSlocal mDNSBool IPv6AddressIsOnInterface(mDNSv6Addr ipv6Addr, char *ifname) +{ + struct ifaddrs *ifa; + struct ifaddrs *ifaddrs; + mDNSAddr addr; + + if (if_nametoindex(ifname) == 0) {LogInfo("IPv6AddressIsOnInterface: Invalid name %s", ifname); return mDNSfalse;} + + if (getifaddrs(&ifaddrs) < 0) {LogInfo("IPv6AddressIsOnInterface: getifaddrs failed"); return mDNSfalse;} + + for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) + { + if (strncmp(ifa->ifa_name, ifname, IFNAMSIZ) != 0) + continue; + if ((ifa->ifa_flags & IFF_UP) == 0 || !ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET6) + continue; + if (SetupAddr(&addr, ifa->ifa_addr) != mStatus_NoError) + { + LogInfo("IPv6AddressIsOnInterface: SetupAddr error, continuing to the next address"); + continue; + } + if (mDNSSameIPv6Address(ipv6Addr, *(mDNSv6Addr*)&addr.ip.v6)) + { + LogInfo("IPv6AddressIsOnInterface: found %.16a", &ipv6Addr); + break; + } + } + freeifaddrs(ifaddrs); + return ifa != NULL; +} + +mDNSlocal mDNSv6Addr IPv6AddressFromString(char* buf) +{ + mDNSv6Addr retVal; + struct addrinfo hints; + struct addrinfo *res0; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_flags = AI_NUMERICHOST; + + int err = getaddrinfo(buf, NULL, &hints, &res0); + if (err) + return zerov6Addr; + + retVal = *(mDNSv6Addr*)&((struct sockaddr_in6*)res0->ai_addr)->sin6_addr; + + freeaddrinfo(res0); + + return retVal; +} + +mDNSlocal CFDictionaryRef CopyConnectivityBackToMyMacDict() +{ + SCDynamicStoreRef store = NULL; + CFDictionaryRef connd = NULL; + CFDictionaryRef BTMMDict = NULL; + + store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:CopyConnectivityBackToMyMacDict"), NULL, NULL); + if (!store) + { + LogMsg("CopyConnectivityBackToMyMacDict: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); + goto end; + } + + connd = SCDynamicStoreCopyValue(store, NetworkChangedKey_BTMMConnectivity); + if (!connd) + { + LogInfo("CopyConnectivityBackToMyMacDict: SCDynamicStoreCopyValue failed: %s", SCErrorString(SCError())); + goto end; + } + + BTMMDict = CFDictionaryGetValue(connd, CFSTR("BackToMyMac")); + if (!BTMMDict) + { + LogInfo("CopyConnectivityBackToMyMacDict: CFDictionaryGetValue: No value for BackToMyMac"); + goto end; + } + + // Non-dictionary is treated as non-existent dictionary + if (CFGetTypeID(BTMMDict) != CFDictionaryGetTypeID()) + { + BTMMDict = NULL; + LogMsg("CopyConnectivityBackToMyMacDict: BackToMyMac not a dictionary"); + goto end; + } + + CFRetain(BTMMDict); + +end: + if (connd) CFRelease(connd); + if (store) CFRelease(store); + + return BTMMDict; +} + +#define MAX_IPV6_TEXTUAL 40 + +mDNSlocal mDNSv6Addr ParseBackToMyMacAddr(CFDictionaryRef BTMMDict, CFStringRef ifKey, CFStringRef addrKey) +{ + mDNSv6Addr retVal = zerov6Addr; + CFTypeRef string = NULL; + char ifname[IFNAMSIZ]; + char address[MAX_IPV6_TEXTUAL]; + + if (!BTMMDict) + return zerov6Addr; + + if (!CFDictionaryGetValueIfPresent(BTMMDict, ifKey, &string)) + { + LogInfo("ParseBackToMyMacAddr: interface key does not exist"); + return zerov6Addr; + } + + if (!CFStringGetCString(string, ifname, IFNAMSIZ, kCFStringEncodingUTF8)) + { + LogMsg("ParseBackToMyMacAddr: Could not convert interface to CString"); + return zerov6Addr; + } + + if (!CFDictionaryGetValueIfPresent(BTMMDict, addrKey, &string)) + { + LogMsg("ParseBackToMyMacAddr: address key does not exist, but interface key does"); + return zerov6Addr; + } + + if (!CFStringGetCString(string, address, sizeof(address), kCFStringEncodingUTF8)) + { + LogMsg("ParseBackToMyMacAddr: Could not convert address to CString"); + return zerov6Addr; + } + + retVal = IPv6AddressFromString(address); + LogInfo("ParseBackToMyMacAddr: %s (%s) %.16a", ifname, address, &retVal); + + if (mDNSIPv6AddressIsZero(retVal)) + return zerov6Addr; + + if (!IPv6AddressIsOnInterface(retVal, ifname)) + { + LogMsg("ParseBackToMyMacAddr: %.16a is not on %s", &retVal, ifname); + return zerov6Addr; + } + + return retVal; +} + +mDNSlocal CFDictionaryRef GetBackToMyMacZones(CFDictionaryRef BTMMDict) +{ + CFTypeRef zones = NULL; + + if (!BTMMDict) + return NULL; + + if (!CFDictionaryGetValueIfPresent(BTMMDict, CFSTR("Zones"), &zones)) + { + LogInfo("CopyBTMMZones: Zones key does not exist"); + return NULL; + } + + return zones; +} + +mDNSlocal mDNSv6Addr ParseBackToMyMacZone(CFDictionaryRef zones, DomainAuthInfo* info) +{ + mDNSv6Addr addr = zerov6Addr; + char buffer[MAX_ESCAPED_DOMAIN_NAME]; + CFStringRef domain = NULL; + CFTypeRef theZone = NULL; + + if (!zones) + return addr; + + ConvertDomainNameToCString(&info->domain, buffer); + domain = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); + if (!domain) + return addr; + + if (CFDictionaryGetValueIfPresent(zones, domain, &theZone)) + addr = ParseBackToMyMacAddr(theZone, CFSTR("Interface"), CFSTR("Address")); + + CFRelease(domain); + + return addr; +} + +mDNSlocal void SetupBackToMyMacInnerAddresses(mDNS *const m, CFDictionaryRef BTMMDict) +{ + DomainAuthInfo* info; + CFDictionaryRef zones = GetBackToMyMacZones(BTMMDict); + mDNSv6Addr newAddr; + + for (info = m->AuthInfoList; info; info = info->next) + { + if (!info->AutoTunnel) + continue; + + newAddr = ParseBackToMyMacZone(zones, info); + + if (mDNSSameIPv6Address(newAddr, info->AutoTunnelInnerAddress)) + continue; + + info->AutoTunnelInnerAddress = newAddr; + DeregisterAutoTunnelHostRecord(m, info); + UpdateAutoTunnelHostRecord(m, info); + UpdateAutoTunnelDomainStatus(m, info); + } +} + +// MUST be called holding the lock +mDNSlocal void ProcessConndConfigChanges(mDNS *const m) +{ + CFDictionaryRef dict = CopyConnectivityBackToMyMacDict(); + if (!dict) + LogInfo("ProcessConndConfigChanges: No BTMM dictionary"); + mDNSv6Addr relayAddr = ParseBackToMyMacAddr(dict, CFSTR("RelayInterface"), CFSTR("RelayAddress")); + + LogInfo("ProcessConndConfigChanges: relay %.16a", &relayAddr); + + SetupBackToMyMacInnerAddresses(m, dict); + + if (dict) CFRelease(dict); + + if (!mDNSSameIPv6Address(relayAddr, m->AutoTunnelRelayAddr)) + { + m->AutoTunnelRelayAddr = relayAddr; + + DomainAuthInfo* info; + for (info = m->AuthInfoList; info; info = info->next) + if (info->AutoTunnel) + { + DeregisterAutoTunnel6Record(m, info); + UpdateAutoTunnel6Record(m, info); + UpdateAutoTunnelDomainStatus(m, info); + } + + // Determine whether we need racoon to accept incoming connections + UpdateAnonymousRacoonConfig(m); + } + + // If awacsd crashes or exits for some reason, restart it + UpdateBTMMRelayConnection(m); +} #endif /* APPLE_OSX_mDNSResponder */ - + mDNSexport void mDNSMacOSXNetworkChanged(mDNS *const m) - { - LogInfo("*** Network Configuration Change *** (%d)%s", - m->p->NetworkChanged ? mDNS_TimeNow(m) - m->p->NetworkChanged : 0, - m->p->NetworkChanged ? "" : " (no scheduled configuration change)"); - m->p->NetworkChanged = 0; // If we received a network change event and deferred processing, we're now dealing with it - mDNSs32 utc = mDNSPlatformUTC(); - m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess(); - m->SystemSleepOnlyIfWakeOnLAN = SystemSleepOnlyIfWakeOnLAN(); - MarkAllInterfacesInactive(m, utc); - UpdateInterfaceList(m, utc); - ClearInactiveInterfaces(m, utc); - SetupActiveInterfaces(m, utc); +{ + LogInfo("*** Network Configuration Change *** (%d)%s", + m->p->NetworkChanged ? mDNS_TimeNow(m) - m->p->NetworkChanged : 0, + m->p->NetworkChanged ? "" : " (no scheduled configuration change)"); + m->p->NetworkChanged = 0; // If we received a network change event and deferred processing, we're now dealing with it + mDNSs32 utc = mDNSPlatformUTC(); + m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess(); + m->SystemSleepOnlyIfWakeOnLAN = SystemSleepOnlyIfWakeOnLAN(); + MarkAllInterfacesInactive(m, utc); + UpdateInterfaceList(m, utc); + ClearInactiveInterfaces(m, utc); + SetupActiveInterfaces(m, utc); #if APPLE_OSX_mDNSResponder - - SetupConndConfigChanges(m); - - if (m->AutoTunnelHostAddr.b[0]) - { - mDNS_Lock(m); - if (TunnelClients(m) || TunnelServers(m)) - SetupLocalAutoTunnelInterface_internal(m, mDNSfalse); - mDNS_Unlock(m); - } - - // Scan to find client tunnels whose questions have completed, - // but whose local inner/outer addresses have changed since the tunnel was set up - ClientTunnel *p; - for (p = m->TunnelClients; p; p = p->next) - if (p->q.ThisQInterval < 0) - { - if (!mDNSIPPortIsZero(p->rmt_outer_port)) - { - mDNSAddr tmpSrc = zeroAddr; - mDNSAddr tmpDst = { mDNSAddrType_IPv4, {{{0}}} }; - tmpDst.ip.v4 = p->rmt_outer; - mDNSPlatformSourceAddrForDest(&tmpSrc, &tmpDst); - if (!mDNSSameIPv6Address(p->loc_inner, m->AutoTunnelHostAddr) || - !mDNSSameIPv4Address(p->loc_outer, tmpSrc.ip.v4)) - { - AutoTunnelSetKeys(p, mDNSfalse); - p->loc_inner = m->AutoTunnelHostAddr; - p->loc_outer = tmpSrc.ip.v4; - AutoTunnelSetKeys(p, mDNStrue); - } - } - else - { - if (!mDNSSameIPv6Address(p->loc_inner, m->AutoTunnelHostAddr) || - !mDNSSameIPv6Address(p->loc_outer6, m->AutoTunnelRelayAddrOut)) - { - AutoTunnelSetKeys(p, mDNSfalse); - p->loc_inner = m->AutoTunnelHostAddr; - p->loc_outer6 = m->AutoTunnelRelayAddrOut; - AutoTunnelSetKeys(p, mDNStrue); - } - } - } - - - SetSPS(m); - - NetworkInterfaceInfoOSX *i; - for (i = m->p->InterfaceList; i; i = i->next) - { - if (!m->SPSSocket) // Not being Sleep Proxy Server; close any open BPF fds - { - if (i->BPF_fd >= 0 && CountProxyTargets(m, i, mDNSNULL, mDNSNULL) == 0) CloseBPF(i); - } - else // else, we're Sleep Proxy Server; open BPF fds - { - if (i->Exists && i->Registered == i && i->ifinfo.McastTxRx && !(i->ifa_flags & IFF_LOOPBACK) && i->BPF_fd == -1) - { LogSPS("%s requesting BPF", i->ifinfo.ifname); i->BPF_fd = -2; mDNSRequestBPF(); } - } - } + mDNS_Lock(m); + ProcessConndConfigChanges(m); + mDNS_Unlock(m); + + // Scan to find client tunnels whose questions have completed, + // but whose local inner/outer addresses have changed since the tunnel was set up + ClientTunnel *p; + for (p = m->TunnelClients; p; p = p->next) + if (p->q.ThisQInterval < 0) + { + DomainAuthInfo* info = GetAuthInfoForName(m, &p->dstname); + if (!info) + { + LogMsg("mDNSMacOSXNetworkChanged: Could not get AuthInfo for %##s, removing tunnel keys", p->dstname.c); + AutoTunnelSetKeys(p, mDNSfalse); + } + else + { + mDNSv6Addr inner = info->AutoTunnelInnerAddress; + + if (!mDNSIPPortIsZero(p->rmt_outer_port)) + { + mDNSAddr tmpSrc = zeroAddr; + mDNSAddr tmpDst = { mDNSAddrType_IPv4, {{{0}}} }; + tmpDst.ip.v4 = p->rmt_outer; + mDNSPlatformSourceAddrForDest(&tmpSrc, &tmpDst); + if (!mDNSSameIPv6Address(p->loc_inner, inner) || + !mDNSSameIPv4Address(p->loc_outer, tmpSrc.ip.v4)) + { + AutoTunnelSetKeys(p, mDNSfalse); + p->loc_inner = inner; + p->loc_outer = tmpSrc.ip.v4; + AutoTunnelSetKeys(p, mDNStrue); + } + } + else + { + if (!mDNSSameIPv6Address(p->loc_inner, inner) || + !mDNSSameIPv6Address(p->loc_outer6, m->AutoTunnelRelayAddr)) + { + AutoTunnelSetKeys(p, mDNSfalse); + p->loc_inner = inner; + p->loc_outer6 = m->AutoTunnelRelayAddr; + AutoTunnelSetKeys(p, mDNStrue); + } + } + } + } + + SetSPS(m); + + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) + { + if (!m->SPSSocket) // Not being Sleep Proxy Server; close any open BPF fds + { + if (i->BPF_fd >= 0 && CountProxyTargets(m, i, mDNSNULL, mDNSNULL) == 0) CloseBPF(i); + } + else // else, we're Sleep Proxy Server; open BPF fds + { + if (i->Exists && i->Registered == i && i->ifinfo.McastTxRx && !(i->ifa_flags & IFF_LOOPBACK) && i->BPF_fd == -1) + { LogSPS("%s requesting BPF", i->ifinfo.ifname); i->BPF_fd = -2; mDNSRequestBPF(); } + } + } #endif // APPLE_OSX_mDNSResponder - uDNS_SetupDNSConfig(m); - mDNS_ConfigChanged(m); - } + uDNS_SetupDNSConfig(m); + mDNS_ConfigChanged(m); +} // Called with KQueueLock & mDNS lock mDNSlocal void SetNetworkChanged(mDNS *const m, mDNSs32 delay) - { - if (!m->p->NetworkChanged || m->p->NetworkChanged - NonZeroTime(m->timenow + delay) < 0) - { - m->p->NetworkChanged = NonZeroTime(m->timenow + delay); - LogInfo("SetNetworkChanged: setting network changed to %d (%d)", delay, m->p->NetworkChanged); - } - } +{ + if (!m->p->NetworkChanged || m->p->NetworkChanged - NonZeroTime(m->timenow + delay) < 0) + { + m->p->NetworkChanged = NonZeroTime(m->timenow + delay); + LogInfo("SetNetworkChanged: scheduling in %d msec", delay); + } +} // Called with KQueueLock & mDNS lock mDNSlocal void SetKeyChainTimer(mDNS *const m, mDNSs32 delay) - { - // If it's not set or it needs to happen sooner than when it's currently set - if (!m->p->KeyChainTimer || m->p->KeyChainTimer - NonZeroTime(m->timenow + delay) > 0) - { - m->p->KeyChainTimer = NonZeroTime(m->timenow + delay); - LogInfo("SetKeyChainTimer: %d", delay); - } - } +{ + // If it's not set or it needs to happen sooner than when it's currently set + if (!m->p->KeyChainTimer || m->p->KeyChainTimer - NonZeroTime(m->timenow + delay) > 0) + { + m->p->KeyChainTimer = NonZeroTime(m->timenow + delay); + LogInfo("SetKeyChainTimer: %d", delay); + } +} // Copy the fourth slash-delimited element from either: // State:/Network/Interface//IPv4 // or // Setup:/Network/Service//Interface mDNSlocal CFStringRef CopyNameFromKey(CFStringRef key) - { - CFArrayRef a; - CFStringRef name = NULL; +{ + CFArrayRef a; + CFStringRef name = NULL; + + a = CFStringCreateArrayBySeparatingStrings(NULL, key, CFSTR("/")); + if (a && CFArrayGetCount(a) == 5) name = CFRetain(CFArrayGetValueAtIndex(a, 3)); + if (a != NULL) CFRelease(a); + + return name; +} + +// Whether a key from a network change notification corresponds to +// an IP service that is explicitly configured for IPv4 Link Local +mDNSlocal mDNSBool ChangedKeysHaveIPv4LL(CFArrayRef inkeys) +{ + SCDynamicStoreRef store = NULL; + CFDictionaryRef dict = NULL; + CFMutableArrayRef a; + const void **keys = NULL, **vals = NULL; + CFStringRef pattern = NULL; + int i, ic, j, jc; + mDNSBool found = mDNSfalse; + + jc = CFArrayGetCount(inkeys); + if (!jc) goto done; + + store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:ChangedKeysHaveIPv4LL"), NULL, NULL); + if (store == NULL) goto done; + + a = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (a == NULL) goto done; + + // Setup:/Network/Service/[^/]+/Interface + pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, kSCCompAnyRegex, kSCEntNetInterface); + if (pattern == NULL) goto done; + CFArrayAppendValue(a, pattern); + CFRelease(pattern); + + // Setup:/Network/Service/[^/]+/IPv4 + pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, kSCCompAnyRegex, kSCEntNetIPv4); + if (pattern == NULL) goto done; + CFArrayAppendValue(a, pattern); + CFRelease(pattern); + + dict = SCDynamicStoreCopyMultiple(store, NULL, a); + CFRelease(a); + + if (!dict) + { + LogMsg("ChangedKeysHaveIPv4LL: Empty dictionary"); + goto done; + } + + ic = CFDictionaryGetCount(dict); + vals = mDNSPlatformMemAllocate(sizeof (void *) * ic); + keys = mDNSPlatformMemAllocate(sizeof (void *) * ic); + CFDictionaryGetKeysAndValues(dict, keys, vals); + + for (j = 0; j < jc && !found; j++) + { + CFStringRef key = CFArrayGetValueAtIndex(inkeys, j); + CFStringRef ifname = NULL; + + char buf[256]; + + // It would be nice to use a regex here + if (!CFStringHasPrefix(key, CFSTR("State:/Network/Interface/")) || !CFStringHasSuffix(key, kSCEntNetIPv4)) continue; + + if ((ifname = CopyNameFromKey(key)) == NULL) continue; + if (mDNS_LoggingEnabled) + { + if (!CFStringGetCString(ifname, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; + LogInfo("ChangedKeysHaveIPv4LL: potential ifname %s", buf); + } + + for (i = 0; i < ic; i++) + { + CFDictionaryRef ipv4dict; + CFStringRef name; + CFStringRef serviceid; + CFStringRef configmethod; + + if (!CFStringHasSuffix(keys[i], kSCEntNetInterface)) continue; + + if (CFDictionaryGetTypeID() != CFGetTypeID(vals[i])) continue; + + if ((name = CFDictionaryGetValue(vals[i], kSCPropNetInterfaceDeviceName)) == NULL) continue; + + if (!CFEqual(ifname, name)) continue; + + if ((serviceid = CopyNameFromKey(keys[i])) == NULL) continue; + if (mDNS_LoggingEnabled) + { + if (!CFStringGetCString(serviceid, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; + LogInfo("ChangedKeysHaveIPv4LL: found serviceid %s", buf); + } + + pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, serviceid, kSCEntNetIPv4); + CFRelease(serviceid); + if (pattern == NULL) continue; + + ipv4dict = CFDictionaryGetValue(dict, pattern); + CFRelease(pattern); + if (!ipv4dict || CFDictionaryGetTypeID() != CFGetTypeID(ipv4dict)) continue; + + configmethod = CFDictionaryGetValue(ipv4dict, kSCPropNetIPv4ConfigMethod); + if (!configmethod) continue; + + if (mDNS_LoggingEnabled) + { + if (!CFStringGetCString(configmethod, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; + LogInfo("ChangedKeysHaveIPv4LL: configmethod %s", buf); + } + + if (CFEqual(configmethod, kSCValNetIPv4ConfigMethodLinkLocal)) { found = mDNStrue; break; } + } + + CFRelease(ifname); + } + +done: + if (vals != NULL) mDNSPlatformMemFree(vals); + if (keys != NULL) mDNSPlatformMemFree(keys); + if (dict != NULL) CFRelease(dict); + if (store != NULL) CFRelease(store); + + return found; +} - a = CFStringCreateArrayBySeparatingStrings(NULL, key, CFSTR("/")); - if (a && CFArrayGetCount(a) == 5) name = CFRetain(CFArrayGetValueAtIndex(a, 3)); - if (a != NULL) CFRelease(a); +mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context) +{ + (void)store; // Parameter not used + mDNSBool changeNow = mDNSfalse; + mDNS *const m = (mDNS *const)context; + KQueueLock(m); + mDNS_Lock(m); + + mDNSs32 delay = mDNSPlatformOneSecond * 2; // Start off assuming a two-second delay + + int c = CFArrayGetCount(changedKeys); // Count changes + CFRange range = { 0, c }; + int c1 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_Hostnames ) != 0); + int c2 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_Computername) != 0); + int c3 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DynamicDNS ) != 0); + int c4 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DNS ) != 0); + if (c && c - c1 - c2 - c3 - c4 == 0) delay = mDNSPlatformOneSecond/10; // If these were the only changes, shorten delay + + { + int i; + for (i=0; i1 ? "s" : "", + c1 ? "(Local Hostname) " : "", + c2 ? "(Computer Name) " : "", + c3 ? "(DynamicDNS) " : "", + c4 ? "(DNS) " : "", + delay); + } + + mDNSBool btmmChanged = CFArrayContainsValue(changedKeys, range, NetworkChangedKey_BackToMyMac); + if (btmmChanged) delay = 0; + + SetNetworkChanged(m, delay); + + // Other software might pick up these changes to register or browse in WAB or BTMM domains, + // so in order for secure updates to be made to the server, make sure to read the keychain and + // setup the DomainAuthInfo before handing the network change. + // If we don't, then we will first try to register services in the clear, then later setup the + // DomainAuthInfo, which is incorrect. + if (c3 || btmmChanged) + SetKeyChainTimer(m, delay); + + mDNS_Unlock(m); + + // If DNS settings changed, immediately force a reconfig (esp. cache flush) + // Similarly, if an interface changed that is explicitly IPv4 link local, immediately force a reconfig + if (c4 || ChangedKeysHaveIPv4LL(changedKeys) || changeNow) mDNSMacOSXNetworkChanged(m); + + KQueueUnlock(m, "NetworkChanged"); +} + +#if APPLE_OSX_mDNSResponder +mDNSlocal void RefreshSPSStatus(const void *key, const void *value, void *context) +{ + (void)context; + char buf[IFNAMSIZ]; + + CFStringRef ifnameStr = (CFStringRef)key; + CFArrayRef array = (CFArrayRef)value; + if (!CFStringGetCString(ifnameStr, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; + + LogInfo("RefreshSPSStatus: Updating SPS state for key %s, array count %d", buf, CFArrayGetCount(array)); + mDNSDynamicStoreSetConfig(kmDNSSleepProxyServersState, buf, value); +} +#endif + +mDNSlocal void DynamicStoreReconnected(SCDynamicStoreRef store, void *info) +{ + mDNS *const m = (mDNS *const)info; + (void)store; + + LogInfo("DynamicStoreReconnected: Reconnected"); + + // State:/Network/MulticastDNS + SetLocalDomains(); + + // State:/Network/DynamicDNS + if (m->FQDN.c[0]) + mDNSPlatformDynDNSHostNameStatusChanged(&m->FQDN, 1); + + // Note: PrivateDNS and BackToMyMac are automatically populated when configd is restarted + // as we receive network change notifications and thus not necessary. But we leave it here + // so that if things are done differently in the future, this code still works. + + // State:/Network/PrivateDNS + CFMutableArrayRef sa = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (!sa) + LogMsg("DynamicStoreReconnected:PrivateDNS: CFArrayCreateMutable failed"); + else + { + DomainAuthInfo *FoundInList; + char stringbuf[MAX_ESCAPED_DOMAIN_NAME]; // Max legal domainname as C-string, including terminating NUL + for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next) + { + ConvertDomainNameToCString(&FoundInList->domain, stringbuf); + CFStringRef cfs = CFStringCreateWithCString(NULL, stringbuf, kCFStringEncodingUTF8); + if (cfs) { CFArrayAppendValue(sa, cfs); CFRelease(cfs); } + } + mDNSDynamicStoreSetConfig(kmDNSPrivateConfig, mDNSNULL, sa); + CFRelease(sa); + } + + // State:/Network/BackToMyMac +#if APPLE_OSX_mDNSResponder + mDNS_Lock(m); + UpdateAutoTunnelDomainStatuses(m); + mDNS_Unlock(m); + + // State:/Network/Interface/en0/SleepProxyServers + if (spsStatusDict) CFDictionaryApplyFunction(spsStatusDict, RefreshSPSStatus, NULL); +#endif +} + +mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m) +{ + mStatus err = -1; + SCDynamicStoreContext context = { 0, m, NULL, NULL, NULL }; + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:WatchForNetworkChanges"), NetworkChanged, &context); + CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + CFStringRef pattern1 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4); + CFStringRef pattern2 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6); + CFMutableArrayRef patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + + if (!store) { LogMsg("SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); goto error; } + if (!keys || !pattern1 || !pattern2 || !patterns) goto error; + + CFArrayAppendValue(keys, NetworkChangedKey_IPv4); + CFArrayAppendValue(keys, NetworkChangedKey_IPv6); + CFArrayAppendValue(keys, NetworkChangedKey_Hostnames); + CFArrayAppendValue(keys, NetworkChangedKey_Computername); + CFArrayAppendValue(keys, NetworkChangedKey_DNS); + CFArrayAppendValue(keys, NetworkChangedKey_DynamicDNS); + CFArrayAppendValue(keys, NetworkChangedKey_BackToMyMac); + CFArrayAppendValue(keys, NetworkChangedKey_PowerSettings); // should remove as part of + CFArrayAppendValue(keys, NetworkChangedKey_BTMMConnectivity); + CFArrayAppendValue(keys, NetworkChangedKey_InternetSharing); + CFArrayAppendValue(patterns, pattern1); + CFArrayAppendValue(patterns, pattern2); + CFArrayAppendValue(patterns, CFSTR("State:/Network/Interface/[^/]+/AirPort")); + if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) + { LogMsg("SCDynamicStoreSetNotificationKeys failed: %s", SCErrorString(SCError())); goto error; } + +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + if (!SCDynamicStoreSetDispatchQueue(store, dispatch_get_main_queue())) + { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s", SCErrorString(SCError())); goto error; } +#else + m->p->StoreRLS = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); + if (!m->p->StoreRLS) { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s", SCErrorString(SCError())); goto error; } + CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode); +#endif + SCDynamicStoreSetDisconnectCallBack(store, DynamicStoreReconnected); + m->p->Store = store; + err = 0; + goto exit; + +error: + if (store) CFRelease(store); + +exit: + if (patterns) CFRelease(patterns); + if (pattern2) CFRelease(pattern2); + if (pattern1) CFRelease(pattern1); + if (keys) CFRelease(keys); + + return(err); +} + +#if 0 // +mDNSlocal void PMChanged(void *context) +{ + mDNS *const m = (mDNS *const)context; + + KQueueLock(m); + mDNS_Lock(m); + + LogSPS("PMChanged"); + + SetNetworkChanged(m, mDNSPlatformOneSecond * 2); + + mDNS_Unlock(m); + KQueueUnlock(m, "PMChanged"); +} + +mDNSlocal mStatus WatchForPMChanges(mDNS *const m) +{ + m->p->PMRLS = IOPMPrefsNotificationCreateRunLoopSource(PMChanged, m); + if (!m->p->PMRLS) { LogMsg("IOPMPrefsNotificationCreateRunLoopSource failed!"); return mStatus_UnknownErr; } + + CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->PMRLS, kCFRunLoopDefaultMode); + + return mStatus_NoError; +} +#endif - return name; - } +#if !TARGET_OS_EMBEDDED // don't setup packet filter rules on embedded -// Whether a key from a network change notification corresponds to -// an IP service that is explicitly configured for IPv4 Link Local -mDNSlocal mDNSBool ChangedKeysHaveIPv4LL(CFArrayRef inkeys) - { - SCDynamicStoreRef store = NULL; - CFDictionaryRef dict = NULL; - CFMutableArrayRef a; - const void **keys = NULL, **vals = NULL; - CFStringRef pattern = NULL; - int i, ic, j, jc; - mDNSBool found = mDNSfalse; - - jc = CFArrayGetCount(inkeys); - if (!jc) goto done; - - store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:ChangedKeysHaveIPv4LL"), NULL, NULL); - if (store == NULL) goto done; - - a = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - if (a == NULL) goto done; - - // Setup:/Network/Service/[^/]+/Interface - pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, kSCCompAnyRegex, kSCEntNetInterface); - if (pattern == NULL) goto done; - CFArrayAppendValue(a, pattern); - CFRelease(pattern); - - // Setup:/Network/Service/[^/]+/IPv4 - pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, kSCCompAnyRegex, kSCEntNetIPv4); - if (pattern == NULL) goto done; - CFArrayAppendValue(a, pattern); - CFRelease(pattern); - - dict = SCDynamicStoreCopyMultiple(store, NULL, a); - CFRelease(a); - - if (!dict) - { - LogMsg("ChangedKeysHaveIPv4LL: Empty dictionary"); - goto done; - } - - ic = CFDictionaryGetCount(dict); - vals = mDNSPlatformMemAllocate(sizeof (void *) * ic); - keys = mDNSPlatformMemAllocate(sizeof (void *) * ic); - CFDictionaryGetKeysAndValues(dict, keys, vals); - - for (j = 0; j < jc && !found; j++) - { - CFStringRef key = CFArrayGetValueAtIndex(inkeys, j); - CFStringRef ifname = NULL; - - char buf[256]; - - // It would be nice to use a regex here - if (!CFStringHasPrefix(key, CFSTR("State:/Network/Interface/")) || !CFStringHasSuffix(key, kSCEntNetIPv4)) continue; - - if ((ifname = CopyNameFromKey(key)) == NULL) continue; - if (mDNS_LoggingEnabled) - { - if (!CFStringGetCString(ifname, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; - LogInfo("ChangedKeysHaveIPv4LL: potential ifname %s", buf); - } - - for (i = 0; i < ic; i++) - { - CFDictionaryRef ipv4dict; - CFStringRef name; - CFStringRef serviceid; - CFStringRef configmethod; - - if (!CFStringHasSuffix(keys[i], kSCEntNetInterface)) continue; - - if (CFDictionaryGetTypeID() != CFGetTypeID(vals[i])) continue; - - if ((name = CFDictionaryGetValue(vals[i], kSCPropNetInterfaceDeviceName)) == NULL) continue; - - if (!CFEqual(ifname, name)) continue; - - if ((serviceid = CopyNameFromKey(keys[i])) == NULL) continue; - if (mDNS_LoggingEnabled) - { - if (!CFStringGetCString(serviceid, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; - LogInfo("ChangedKeysHaveIPv4LL: found serviceid %s", buf); - } - - pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, serviceid, kSCEntNetIPv4); - CFRelease(serviceid); - if (pattern == NULL) continue; - - ipv4dict = CFDictionaryGetValue(dict, pattern); - CFRelease(pattern); - if (!ipv4dict || CFDictionaryGetTypeID() != CFGetTypeID(ipv4dict)) continue; - - configmethod = CFDictionaryGetValue(ipv4dict, kSCPropNetIPv4ConfigMethod); - if (!configmethod) continue; - - if (mDNS_LoggingEnabled) - { - if (!CFStringGetCString(configmethod, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; - LogInfo("ChangedKeysHaveIPv4LL: configmethod %s", buf); - } - - if (CFEqual(configmethod, kSCValNetIPv4ConfigMethodLinkLocal)) { found = mDNStrue; break; } - } - - CFRelease(ifname); - } - - done: - if (vals != NULL) mDNSPlatformMemFree(vals); - if (keys != NULL) mDNSPlatformMemFree(keys); - if (dict != NULL) CFRelease(dict); - if (store != NULL) CFRelease(store); - - return found; - } +mDNSlocal void mDNSSetPacketFilterRules(mDNS *const m, char * ifname, const ResourceRecord *const excludeRecord) +{ + AuthRecord *rr; + pfArray_t portArray; + pfArray_t protocolArray; + uint32_t count = 0; -mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context) - { - (void)store; // Parameter not used - mDNSBool changeNow = mDNSfalse; - mDNS *const m = (mDNS *const)context; - KQueueLock(m); - mDNS_Lock(m); - - mDNSs32 delay = mDNSPlatformOneSecond * 2; // Start off assuming a two-second delay - - int c = CFArrayGetCount(changedKeys); // Count changes - CFRange range = { 0, c }; - int c1 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_Hostnames ) != 0); - int c2 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_Computername) != 0); - int c3 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DynamicDNS ) != 0); - int c4 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DNS ) != 0); - if (c && c - c1 - c2 - c3 - c4 == 0) delay = mDNSPlatformOneSecond/10; // If these were the only changes, shorten delay - - { - int i; - for (i=0; i1?"s":"", - c1 ? "(Local Hostname) " : "", - c2 ? "(Computer Name) " : "", - c3 ? "(DynamicDNS) " : "", - c4 ? "(DNS) " : "", - delay); - } - - mDNSBool btmmChanged = CFArrayContainsValue(changedKeys, range, NetworkChangedKey_BackToMyMac); - if (btmmChanged) delay = 0; - - SetNetworkChanged(m, delay); - - // Other software might pick up these changes to register or browse in WAB or BTMM domains, - // so in order for secure updates to be made to the server, make sure to read the keychain and - // setup the DomainAuthInfo before handing the network change. - // If we don't, then we will first try to register services in the clear, then later setup the - // DomainAuthInfo, which is incorrect. - if (c3 || btmmChanged) - SetKeyChainTimer(m, delay); - - mDNS_Unlock(m); - - // If DNS settings changed, immediately force a reconfig (esp. cache flush) - // Similarly, if an interface changed that is explicitly IPv4 link local, immediately force a reconfig - if (c4 || ChangedKeysHaveIPv4LL(changedKeys) || changeNow) mDNSMacOSXNetworkChanged(m); - - KQueueUnlock(m, "NetworkChanged"); - } + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if ((rr->resrec.rrtype == kDNSServiceType_SRV) && (rr->ARType == AuthRecordAnyIncludeP2P)) + { + const mDNSu8 *p; + + if (count >= PFPortArraySize) + { + LogMsg("mDNSSetPacketFilterRules: %d service limit, skipping %s", PFPortArraySize, ARDisplayString(m, rr)); + continue; + } + + if (excludeRecord && IdenticalResourceRecord(&rr->resrec, excludeRecord)) + { + LogInfo("mDNSSetPacketFilterRules: record being removed, skipping %s", ARDisplayString(m, rr)); + continue; + } + + LogInfo("mDNSSetPacketFilterRules: found %s", ARDisplayString(m, rr)); + + portArray[count] = rr->resrec.rdata->u.srv.port.NotAnInteger; + + // Assume ... + p = rr->resrec.name->c; + + // Skip to App Protocol + if (p[0]) p += 1 + p[0]; + + // Skip to Transport Protocol + if (p[0]) p += 1 + p[0]; + + if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_tcp")) protocolArray[count] = IPPROTO_TCP; + else if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_udp")) protocolArray[count] = IPPROTO_UDP; + else + { + LogMsg("mDNSSetPacketFilterRules: could not determine transport protocol of service"); + LogMsg("mDNSSetPacketFilterRules: %s", ARDisplayString(m, rr)); + return; + } + count++; + } + } + mDNSPacketFilterControl(PF_SET_RULES, ifname, count, portArray, protocolArray); +} -#if APPLE_OSX_mDNSResponder -mDNSlocal void RefreshSPSStatus(const void *key, const void *value, void *context) - { - (void)context; - char buf[IFNAMSIZ]; +// If the p2p interface already exists, update the Bonjour packet filter rules for it. +mDNSexport void mDNSUpdatePacketFilter(const ResourceRecord *const excludeRecord) +{ + mDNS *const m = &mDNSStorage; - CFStringRef ifnameStr = (CFStringRef)key; - CFArrayRef array = (CFArrayRef)value; - if (!CFStringGetCString(ifnameStr, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; + NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces); + while (intf) + { + if (strncmp(intf->ifname, "p2p", 3) == 0) + { + LogInfo("mDNSInitPacketFilter: Setting rules for ifname %s", intf->ifname); + mDNSSetPacketFilterRules(m, intf->ifname, excludeRecord); + break; + } + intf = GetFirstActiveInterface(intf->next); + } +} - LogInfo("RefreshSPSStatus: Updating SPS state for key %s, array count %d", buf, CFArrayGetCount(array)); - mDNSDynamicStoreSetConfig(kmDNSSleepProxyServersState, buf, value); - } -#endif +#else // !TARGET_OS_EMBEDDED -mDNSlocal void DynamicStoreReconnected(SCDynamicStoreRef store, void *info) - { - mDNS *const m = (mDNS *const)info; - (void)store; - - LogInfo("DynamicStoreReconnected: Reconnected"); - - // State:/Network/MulticastDNS - SetLocalDomains(); - - // State:/Network/DynamicDNS - if (m->FQDN.c[0]) - mDNSPlatformDynDNSHostNameStatusChanged(&m->FQDN, 1); - - // Note: PrivateDNS and BackToMyMac are automatically populated when configd is restarted - // as we receive network change notifications and thus not necessary. But we leave it here - // so that if things are done differently in the future, this code still works. - - // State:/Network/PrivateDNS - CFMutableArrayRef sa = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - if (!sa) - LogMsg("DynamicStoreReconnected:PrivateDNS: CFArrayCreateMutable failed"); - else - { - DomainAuthInfo *FoundInList; - char stringbuf[MAX_ESCAPED_DOMAIN_NAME]; // Max legal domainname as C-string, including terminating NUL - for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next) - { - ConvertDomainNameToCString(&FoundInList->domain, stringbuf); - CFStringRef cfs = CFStringCreateWithCString(NULL, stringbuf, kCFStringEncodingUTF8); - if (cfs) { CFArrayAppendValue(sa, cfs); CFRelease(cfs); } - } - mDNSDynamicStoreSetConfig(kmDNSPrivateConfig, mDNSNULL, sa); - CFRelease(sa); - } - - // State:/Network/BackToMyMac -#if APPLE_OSX_mDNSResponder - mDNS_Lock(m); - UpdateAutoTunnelDomainStatuses(m); - mDNS_Unlock(m); +// Currently no packet filter setup required on embedded platforms. +mDNSexport void mDNSUpdatePacketFilter(const ResourceRecord *const excludeRecord) +{ + (void) excludeRecord; // unused +} - // State:/Network/Interface/en0/SleepProxyServers - if (spsStatusDict) CFDictionaryApplyFunction(spsStatusDict, RefreshSPSStatus, NULL); -#endif - } +#endif // !TARGET_OS_EMBEDDED -mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m) - { - mStatus err = -1; - SCDynamicStoreContext context = { 0, m, NULL, NULL, NULL }; - SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:WatchForNetworkChanges"), NetworkChanged, &context); - CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - CFStringRef pattern1 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4); - CFStringRef pattern2 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6); - CFMutableArrayRef patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - - if (!store) { LogMsg("SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); goto error; } - if (!keys || !pattern1 || !pattern2 || !patterns) goto error; - - CFArrayAppendValue(keys, NetworkChangedKey_IPv4); - CFArrayAppendValue(keys, NetworkChangedKey_IPv6); - CFArrayAppendValue(keys, NetworkChangedKey_Hostnames); - CFArrayAppendValue(keys, NetworkChangedKey_Computername); - CFArrayAppendValue(keys, NetworkChangedKey_DNS); - CFArrayAppendValue(keys, NetworkChangedKey_DynamicDNS); - CFArrayAppendValue(keys, NetworkChangedKey_BackToMyMac); - CFArrayAppendValue(keys, NetworkChangedKey_PowerSettings); // should remove as part of - CFArrayAppendValue(keys, NetworkChangedKey_BTMMConnectivity); - CFArrayAppendValue(patterns, pattern1); - CFArrayAppendValue(patterns, pattern2); - CFArrayAppendValue(patterns, CFSTR("State:/Network/Interface/[^/]+/AirPort")); - if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) - { LogMsg("SCDynamicStoreSetNotificationKeys failed: %s", SCErrorString(SCError())); goto error; } +// Handle AWDL KEV_DL_MASTER_ELECTED event by restarting queries and advertisements +// marked to include the AWDL interface. +mDNSlocal void newMasterElected(mDNS *const m, struct net_event_data * ptr) +{ + char ifname[IFNAMSIZ]; + mDNSu32 interfaceIndex; + DNSQuestion *q; + AuthRecord *rr; + NetworkInterfaceInfoOSX *infoOSX; + mDNSInterfaceID InterfaceID; -#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM - if (!SCDynamicStoreSetDispatchQueue(store, dispatch_get_main_queue())) - { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s", SCErrorString(SCError())); goto error; } -#else - m->p->StoreRLS = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); - if (!m->p->StoreRLS) { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s", SCErrorString(SCError())); goto error; } - CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode); -#endif - SCDynamicStoreSetDisconnectCallBack(store, DynamicStoreReconnected); - m->p->Store = store; - err = 0; - goto exit; + snprintf(ifname, IFNAMSIZ, "%s%d", ptr->if_name, ptr->if_unit); + interfaceIndex = if_nametoindex(ifname); - error: - if (store) CFRelease(store); + if (!interfaceIndex) + { + LogMsg("newMasterElected: if_nametoindex(%s) failed", ifname); + return; + } - exit: - if (patterns) CFRelease(patterns); - if (pattern2) CFRelease(pattern2); - if (pattern1) CFRelease(pattern1); - if (keys) CFRelease(keys); + LogInfo("newMasterElected: ifname = %s, interfaceIndex = %d", ifname, interfaceIndex); + infoOSX = IfindexToInterfaceInfoOSX(m, (mDNSInterfaceID)(uintptr_t)interfaceIndex); - return(err); - } + // Can get an KEV_DL_MASTER_ELECTED event prior to the interface existing + // when it is first brought up. + if (!infoOSX) + { + LogInfo("newMasterElected: interface not yet active"); + return; + } + InterfaceID = infoOSX->ifinfo.InterfaceID; -#if 0 // -mDNSlocal void PMChanged(void *context) - { - mDNS *const m = (mDNS *const)context; - - KQueueLock(m); - mDNS_Lock(m); - - LogSPS("PMChanged"); - - SetNetworkChanged(m, mDNSPlatformOneSecond * 2); - - mDNS_Unlock(m); - KQueueUnlock(m, "PMChanged"); - } + for (q = m->Questions; q; q=q->next) + { + if ((!q->InterfaceID && (q->flags & kDNSServiceFlagsIncludeAWDL)) + || q->InterfaceID == InterfaceID) + { + LogInfo("newMasterElected: restarting %s query for %##s", DNSTypeName(q->qtype), q->qname.c); + mDNSCoreRestartQuestion(m, q); + } + } + + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if ((!rr->resrec.InterfaceID && (rr->ARType == AuthRecordAnyIncludeAWDL)) + || rr->resrec.InterfaceID == InterfaceID) + { + LogInfo("newMasterElected: restarting %s announcements for %##s", DNSTypeName(rr->resrec.rrtype), rr->namestorage.c); + mDNSCoreRestartRegistration(m, rr, -1); + } + } +} -mDNSlocal mStatus WatchForPMChanges(mDNS *const m) - { - m->p->PMRLS = IOPMPrefsNotificationCreateRunLoopSource(PMChanged, m); - if (!m->p->PMRLS) { LogMsg("IOPMPrefsNotificationCreateRunLoopSource failed!"); return mStatus_UnknownErr; } +// An ssth array of all zeroes indicates the peer has no services registered. +mDNSlocal mDNSBool allZeroSSTH(struct opaque_presence_indication *op) +{ + int i; + int *intp = (int *) op->ssth; - CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->PMRLS, kCFRunLoopDefaultMode); + for (i = 0; i < (int)(MAX_SSTH_SIZE / sizeof(int)); i++, intp++) + { + if (*intp) + return mDNSfalse; + } + return mDNStrue; +} - return mStatus_NoError; - } -#endif +// Mark records from this peer for deletion from the cache. +mDNSlocal void removeCachedPeerRecords(mDNS *const m, mDNSu32 ifindex, mDNSAddr *ap) +{ + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *cr; + mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(m, ifindex); -#ifndef KEV_DL_WAKEFLAGS_CHANGED -#define KEV_DL_WAKEFLAGS_CHANGED 17 -#endif + if (!InterfaceID) + { + LogInfo("removeCachedPeerRecords: Invalid ifindex: %d", ifindex); + return; + } -#if !TARGET_OS_EMBEDDED // don't setup packet filter rules on embedded - -mDNSlocal void mDNSSetPacketFilterRules(mDNS *const m, char * ifname) - { - AuthRecord *rr; - int found = 0; - - for (rr = m->ResourceRecords; rr; rr=rr->next) - { - if ((rr->resrec.rrtype == kDNSServiceType_SRV) && (rr->ARType == AuthRecordAnyIncludeP2P)) - { - uint16_t port = rr->resrec.rdata->u.srv.port.NotAnInteger; - uint16_t protocol; - const mDNSu8 *p; - - LogInfo("mDNSSetPacketFilterRules: found %s", ARDisplayString(m, rr)); - - // Note presence of more than one p2p service in list - // since code is currently opening only one port in the packet filter. - found++; - if (found > 1) - { - LogMsg("mDNSSetPacketFilterRules: found service #%d %s", found, ARDisplayString(m, rr)); - } - - // Assume ... - p = rr->resrec.name->c; - - // Skip to App Protocol - if (p[0]) p += 1 + p[0]; - // Skip to Transport Protocol - if (p[0]) p += 1 + p[0]; - - if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_tcp")) protocol = IPPROTO_TCP; - else if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_udp")) protocol = IPPROTO_UDP; - else - { - LogMsg("mDNSSetPacketFilterRules: could not determine transport protocol of service"); - LogMsg("mDNSSetPacketFilterRules: %s", ARDisplayString(m, rr)); - return; - } - - LogInfo("mDNSSetPacketFilterRules: ifname %s, port(in NBO)0x%X, protocol %d", - ifname, port, protocol); - mDNSPacketFilterControl(PF_SET_RULES, ifname, port, protocol); - } - } - } -#endif // !TARGET_OS_EMBEDDED + FORALL_CACHERECORDS(slot, cg, cr) + { + if ((InterfaceID == cr->resrec.InterfaceID) && mDNSSameAddress(ap, & cr->sourceAddress)) + { + LogInfo("removeCachedPeerRecords: %s %##s marking for deletion", + DNSTypeName(cr->resrec.rrtype), cr->resrec.name->c); + mDNS_PurgeCacheResourceRecord(m, cr); + } + } +} +// Handle KEV_DL_NODE_PRESENCE event. +mDNSlocal void nodePresence(mDNS *const m, struct kev_dl_node_presence * p) +{ + char buf[INET6_ADDRSTRLEN]; + struct opaque_presence_indication *op = (struct opaque_presence_indication *) p->node_service_info; + + if (inet_ntop(AF_INET6, & p->sin6_node_address.sin6_addr, buf, sizeof(buf))) + LogInfo("nodePresence: IPv6 address: %s, SUI %d", buf, op->SUI); + else + LogInfo("nodePresence: inet_ntop() error"); + + // AWDL will generate a KEV_DL_NODE_PRESENCE event with SSTH field of + // all zeroes when a node is present and has no services registered. + // We should have already flushed the cache when the previous + // KEV_DL_NODE_ABSENCE was received or upon receipt of the goodbye packets + // when the service was deregistered. Thus, the following + // cache flush logic is disabled by mDNSHandlePeerEvents being false + // until we determine that a cache flush is required on receipt of this event. + if (m->mDNSHandlePeerEvents && allZeroSSTH(op)) + { + mDNSAddr peerAddr; -#if APPLE_OSX_mDNSResponder + peerAddr.type = mDNSAddrType_IPv6; + peerAddr.ip.v6 = *(mDNSv6Addr*)&p->sin6_node_address.sin6_addr; -#if !TARGET_OS_EMBEDDED -// If the p2p interface already exists, set the Bonjour packet filter rules for it. -mDNSexport void mDNSInitPacketFilter(void) - { - mDNS *const m = & mDNSStorage; + LogInfo("nodePresence: ssth is all zeroes, delete cached records from this peer"); + removeCachedPeerRecords(m, p->sdl_node_address.sdl_index, & peerAddr); + } +} - NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces); - while (intf) - { - if (strncmp(intf->ifname, "p2p", 3) == 0) - { - LogInfo("mDNSInitPacketFilter: Setting rules for ifname %s", intf->ifname); - mDNSSetPacketFilterRules(m, intf->ifname); - break; - } - intf = GetFirstActiveInterface(intf->next); - } - } +// Handle KEV_DL_NODE_ABSENCE event. +mDNSlocal void nodeAbsence(mDNS *const m, struct kev_dl_node_absence * p) +{ + mDNSAddr peerAddr; + char buf[INET6_ADDRSTRLEN]; -#else // !TARGET_OS_EMBEDDED + if (inet_ntop(AF_INET6, & p->sin6_node_address.sin6_addr, buf, sizeof(buf))) + LogInfo("nodeAbsence: IPv6 address: %s", buf); + else + LogInfo("nodeAbsence: inet_ntop() error"); - // Currently no packet filter setup required embedded on platforms. -mDNSexport void mDNSInitPacketFilter() - { - } + peerAddr.type = mDNSAddrType_IPv6; + peerAddr.ip.v6 = *(mDNSv6Addr*)&p->sin6_node_address.sin6_addr; -#endif // !TARGET_OS_EMBEDDED -#endif // APPLE_OSX_mDNSResponder + LogInfo("nodeAbsence: delete cached records from this peer"); + removeCachedPeerRecords(m, p->sdl_node_address.sdl_index, & peerAddr); +} mDNSlocal void SysEventCallBack(int s1, short __unused filter, void *context) - { - mDNS *const m = (mDNS *const)context; - - mDNS_Lock(m); - - struct { struct kern_event_msg k; char extra[256]; } msg; - int bytes = recv(s1, &msg, sizeof(msg), 0); - if (bytes < 0) - LogMsg("SysEventCallBack: recv error %d errno %d (%s)", bytes, errno, strerror(errno)); - else - { - LogInfo("SysEventCallBack got %d bytes size %d %X %s %X %s %X %s id %d code %d %s", - bytes, msg.k.total_size, - msg.k.vendor_code , msg.k.vendor_code == KEV_VENDOR_APPLE ? "KEV_VENDOR_APPLE" : "?", - msg.k.kev_class , msg.k.kev_class == KEV_NETWORK_CLASS ? "KEV_NETWORK_CLASS" : "?", - msg.k.kev_subclass, msg.k.kev_subclass == KEV_DL_SUBCLASS ? "KEV_DL_SUBCLASS" : "?", - msg.k.id, msg.k.event_code, - msg.k.event_code == KEV_DL_SIFFLAGS ? "KEV_DL_SIFFLAGS" : - msg.k.event_code == KEV_DL_SIFMETRICS ? "KEV_DL_SIFMETRICS" : - msg.k.event_code == KEV_DL_SIFMTU ? "KEV_DL_SIFMTU" : - msg.k.event_code == KEV_DL_SIFPHYS ? "KEV_DL_SIFPHYS" : - msg.k.event_code == KEV_DL_SIFMEDIA ? "KEV_DL_SIFMEDIA" : - msg.k.event_code == KEV_DL_SIFGENERIC ? "KEV_DL_SIFGENERIC" : - msg.k.event_code == KEV_DL_ADDMULTI ? "KEV_DL_ADDMULTI" : - msg.k.event_code == KEV_DL_DELMULTI ? "KEV_DL_DELMULTI" : - msg.k.event_code == KEV_DL_IF_ATTACHED ? "KEV_DL_IF_ATTACHED" : - msg.k.event_code == KEV_DL_IF_DETACHING ? "KEV_DL_IF_DETACHING" : - msg.k.event_code == KEV_DL_IF_DETACHED ? "KEV_DL_IF_DETACHED" : - msg.k.event_code == KEV_DL_LINK_OFF ? "KEV_DL_LINK_OFF" : - msg.k.event_code == KEV_DL_LINK_ON ? "KEV_DL_LINK_ON" : - msg.k.event_code == KEV_DL_PROTO_ATTACHED ? "KEV_DL_PROTO_ATTACHED" : - msg.k.event_code == KEV_DL_PROTO_DETACHED ? "KEV_DL_PROTO_DETACHED" : - msg.k.event_code == KEV_DL_LINK_ADDRESS_CHANGED ? "KEV_DL_LINK_ADDRESS_CHANGED" : - msg.k.event_code == KEV_DL_WAKEFLAGS_CHANGED ? "KEV_DL_WAKEFLAGS_CHANGED" : "?"); - - // We receive network change notifications both through configd and through SYSPROTO_EVENT socket. - // Configd may not generate network change events for manually configured interfaces (i.e., non-DHCP) - // always during sleep/wakeup due to some race conditions (See radar:8666757). At the same time, if - // "Wake on Network Access" is not turned on, the notification will not have KEV_DL_WAKEFLAGS_CHANGED. - // Hence, during wake up, if we see a KEV_DL_LINK_ON (i.e., link is UP), we trigger a network change. - - if (msg.k.event_code == KEV_DL_WAKEFLAGS_CHANGED || msg.k.event_code == KEV_DL_LINK_ON) - SetNetworkChanged(m, mDNSPlatformOneSecond * 2); - -#if !TARGET_OS_EMBEDDED // don't setup packet filter rules on embedded - - // For p2p interfaces, need to open the advertised service port in the firewall. - if (msg.k.event_code == KEV_DL_IF_ATTACHED) - { - struct net_event_data * p; - p = (struct net_event_data *) & msg.k.event_data; - - if (strncmp(p->if_name, "p2p", 3) == 0) - { - char ifname[IFNAMSIZ]; - snprintf(ifname, IFNAMSIZ, "%s%d", p->if_name, p->if_unit); - - LogInfo("SysEventCallBack: KEV_DL_IF_ATTACHED if_family = %d, if_unit = %d, if_name = %s", p->if_family, p->if_unit, p->if_name); - - mDNSSetPacketFilterRules(m, ifname); - } - } - - // For p2p interfaces, need to clear the firewall rules on interface detach - if (msg.k.event_code == KEV_DL_IF_DETACHED) - { - struct net_event_data * p; - p = (struct net_event_data *) & msg.k.event_data; - - if (strncmp(p->if_name, "p2p", 3) == 0) - { - char ifname[IFNAMSIZ]; - snprintf(ifname, IFNAMSIZ, "%s%d", p->if_name, p->if_unit); - - LogInfo("SysEventCallBack: KEV_DL_IF_DETACHED if_family = %d, if_unit = %d, if_name = %s", p->if_family, p->if_unit, p->if_name); - - mDNSPacketFilterControl(PF_CLEAR_RULES, ifname, 0, 0); - } - } +{ + mDNS *const m = (mDNS *const)context; + + mDNS_Lock(m); + + struct { struct kern_event_msg k; char extra[256]; } msg; + int bytes = recv(s1, &msg, sizeof(msg), 0); + if (bytes < 0) + LogMsg("SysEventCallBack: recv error %d errno %d (%s)", bytes, errno, strerror(errno)); + else + { + LogInfo("SysEventCallBack got %d bytes size %d %X %s %X %s %X %s id %d code %d %s", + bytes, msg.k.total_size, + msg.k.vendor_code, msg.k.vendor_code == KEV_VENDOR_APPLE ? "KEV_VENDOR_APPLE" : "?", + msg.k.kev_class, msg.k.kev_class == KEV_NETWORK_CLASS ? "KEV_NETWORK_CLASS" : "?", + msg.k.kev_subclass, msg.k.kev_subclass == KEV_DL_SUBCLASS ? "KEV_DL_SUBCLASS" : "?", + msg.k.id, msg.k.event_code, + msg.k.event_code == KEV_DL_SIFFLAGS ? "KEV_DL_SIFFLAGS" : + msg.k.event_code == KEV_DL_SIFMETRICS ? "KEV_DL_SIFMETRICS" : + msg.k.event_code == KEV_DL_SIFMTU ? "KEV_DL_SIFMTU" : + msg.k.event_code == KEV_DL_SIFPHYS ? "KEV_DL_SIFPHYS" : + msg.k.event_code == KEV_DL_SIFMEDIA ? "KEV_DL_SIFMEDIA" : + msg.k.event_code == KEV_DL_SIFGENERIC ? "KEV_DL_SIFGENERIC" : + msg.k.event_code == KEV_DL_ADDMULTI ? "KEV_DL_ADDMULTI" : + msg.k.event_code == KEV_DL_DELMULTI ? "KEV_DL_DELMULTI" : + msg.k.event_code == KEV_DL_IF_ATTACHED ? "KEV_DL_IF_ATTACHED" : + msg.k.event_code == KEV_DL_IF_DETACHING ? "KEV_DL_IF_DETACHING" : + msg.k.event_code == KEV_DL_IF_DETACHED ? "KEV_DL_IF_DETACHED" : + msg.k.event_code == KEV_DL_LINK_OFF ? "KEV_DL_LINK_OFF" : + msg.k.event_code == KEV_DL_LINK_ON ? "KEV_DL_LINK_ON" : + msg.k.event_code == KEV_DL_PROTO_ATTACHED ? "KEV_DL_PROTO_ATTACHED" : + msg.k.event_code == KEV_DL_PROTO_DETACHED ? "KEV_DL_PROTO_DETACHED" : + msg.k.event_code == KEV_DL_LINK_ADDRESS_CHANGED ? "KEV_DL_LINK_ADDRESS_CHANGED" : + msg.k.event_code == KEV_DL_WAKEFLAGS_CHANGED ? "KEV_DL_WAKEFLAGS_CHANGED" : + msg.k.event_code == KEV_DL_IF_IDLE_ROUTE_REFCNT ? "KEV_DL_IF_IDLE_ROUTE_REFCNT" : + msg.k.event_code == KEV_DL_IFCAP_CHANGED ? "KEV_DL_IFCAP_CHANGED" : + msg.k.event_code == KEV_DL_LINK_QUALITY_METRIC_CHANGED ? "KEV_DL_LINK_QUALITY_METRIC_CHANGED" : + msg.k.event_code == KEV_DL_NODE_PRESENCE ? "KEV_DL_NODE_PRESENCE" : + msg.k.event_code == KEV_DL_NODE_ABSENCE ? "KEV_DL_NODE_ABSENCE" : + msg.k.event_code == KEV_DL_MASTER_ELECTED ? "KEV_DL_MASTER_ELECTED" : + "?"); + + if (msg.k.event_code == KEV_DL_NODE_PRESENCE) + nodePresence(m, (struct kev_dl_node_presence *) &msg.k.event_data); + + if (msg.k.event_code == KEV_DL_NODE_ABSENCE) + nodeAbsence(m, (struct kev_dl_node_absence *) &msg.k.event_data); + + if (msg.k.event_code == KEV_DL_MASTER_ELECTED) + newMasterElected(m, (struct net_event_data *) &msg.k.event_data); + + // We receive network change notifications both through configd and through SYSPROTO_EVENT socket. + // Configd may not generate network change events for manually configured interfaces (i.e., non-DHCP) + // always during sleep/wakeup due to some race conditions (See radar:8666757). At the same time, if + // "Wake on Network Access" is not turned on, the notification will not have KEV_DL_WAKEFLAGS_CHANGED. + // Hence, during wake up, if we see a KEV_DL_LINK_ON (i.e., link is UP), we trigger a network change. + + if (msg.k.event_code == KEV_DL_WAKEFLAGS_CHANGED || msg.k.event_code == KEV_DL_LINK_ON) + SetNetworkChanged(m, mDNSPlatformOneSecond * 2); + +#if !TARGET_OS_EMBEDDED // don't setup packet filter rules on embedded + + // For p2p interfaces, need to open the advertised service port in the firewall. + if (msg.k.event_code == KEV_DL_IF_ATTACHED) + { + struct net_event_data * p; + p = (struct net_event_data *) &msg.k.event_data; + + if (strncmp(p->if_name, "p2p", 3) == 0) + { + char ifname[IFNAMSIZ]; + snprintf(ifname, IFNAMSIZ, "%s%d", p->if_name, p->if_unit); + + LogInfo("SysEventCallBack: KEV_DL_IF_ATTACHED if_family = %d, if_unit = %d, if_name = %s", p->if_family, p->if_unit, p->if_name); + + mDNSSetPacketFilterRules(m, ifname, NULL); + } + } + + // For p2p interfaces, need to clear the firewall rules on interface detach + if (msg.k.event_code == KEV_DL_IF_DETACHED) + { + struct net_event_data * p; + p = (struct net_event_data *) &msg.k.event_data; + + if (strncmp(p->if_name, "p2p", 3) == 0) + { + pfArray_t portArray, protocolArray; // not initialized since count is 0 for PF_CLEAR_RULES + char ifname[IFNAMSIZ]; + snprintf(ifname, IFNAMSIZ, "%s%d", p->if_name, p->if_unit); + + LogInfo("SysEventCallBack: KEV_DL_IF_DETACHED if_family = %d, if_unit = %d, if_name = %s", p->if_family, p->if_unit, p->if_name); + + mDNSPacketFilterControl(PF_CLEAR_RULES, ifname, 0, portArray, protocolArray); + } + } #endif // !TARGET_OS_EMBEDDED - } + } - mDNS_Unlock(m); - } + mDNS_Unlock(m); +} mDNSlocal mStatus WatchForSysEvents(mDNS *const m) - { - m->p->SysEventNotifier = socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT); - if (m->p->SysEventNotifier < 0) - { LogMsg("WatchForSysEvents: socket failed error %d errno %d (%s)", m->p->SysEventNotifier, errno, strerror(errno)); return(mStatus_NoMemoryErr); } - - struct kev_request kev_req = { KEV_VENDOR_APPLE, KEV_NETWORK_CLASS, KEV_DL_SUBCLASS }; - int err = ioctl(m->p->SysEventNotifier, SIOCSKEVFILT, &kev_req); - if (err < 0) - { - LogMsg("WatchForSysEvents: SIOCSKEVFILT failed error %d errno %d (%s)", err, errno, strerror(errno)); - close(m->p->SysEventNotifier); - m->p->SysEventNotifier = -1; - return(mStatus_UnknownErr); - } - - m->p->SysEventKQueue.KQcallback = SysEventCallBack; - m->p->SysEventKQueue.KQcontext = m; - m->p->SysEventKQueue.KQtask = "System Event Notifier"; - KQueueSet(m->p->SysEventNotifier, EV_ADD, EVFILT_READ, &m->p->SysEventKQueue); - - return(mStatus_NoError); - } +{ + m->p->SysEventNotifier = socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT); + if (m->p->SysEventNotifier < 0) + { LogMsg("WatchForSysEvents: socket failed error %d errno %d (%s)", m->p->SysEventNotifier, errno, strerror(errno)); return(mStatus_NoMemoryErr); } + + struct kev_request kev_req = { KEV_VENDOR_APPLE, KEV_NETWORK_CLASS, KEV_DL_SUBCLASS }; + int err = ioctl(m->p->SysEventNotifier, SIOCSKEVFILT, &kev_req); + if (err < 0) + { + LogMsg("WatchForSysEvents: SIOCSKEVFILT failed error %d errno %d (%s)", err, errno, strerror(errno)); + close(m->p->SysEventNotifier); + m->p->SysEventNotifier = -1; + return(mStatus_UnknownErr); + } + + m->p->SysEventKQueue.KQcallback = SysEventCallBack; + m->p->SysEventKQueue.KQcontext = m; + m->p->SysEventKQueue.KQtask = "System Event Notifier"; + KQueueSet(m->p->SysEventNotifier, EV_ADD, EVFILT_READ, &m->p->SysEventKQueue); + + return(mStatus_NoError); +} #ifndef NO_SECURITYFRAMEWORK mDNSlocal OSStatus KeychainChanged(SecKeychainEvent keychainEvent, SecKeychainCallbackInfo *info, void *context) - { - LogInfo("*** Keychain Changed ***"); - mDNS *const m = (mDNS *const)context; - SecKeychainRef skc; - OSStatus err = SecKeychainCopyDefault(&skc); - if (!err) - { - if (info->keychain == skc) - { - // For delete events, attempt to verify what item was deleted fail because the item is already gone, so we just assume they may be relevant - mDNSBool relevant = (keychainEvent == kSecDeleteEvent); - if (!relevant) - { - UInt32 tags[3] = { kSecTypeItemAttr, kSecServiceItemAttr, kSecAccountItemAttr }; - SecKeychainAttributeInfo attrInfo = { 3, tags, NULL }; // Count, array of tags, array of formats - SecKeychainAttributeList *a = NULL; - err = SecKeychainItemCopyAttributesAndData(info->item, &attrInfo, NULL, &a, NULL, NULL); - if (!err) - { - relevant = ((a->attr[0].length == 4 && (!strncasecmp(a->attr[0].data, "ddns", 4) || !strncasecmp(a->attr[0].data, "sndd", 4))) || - (a->attr[1].length >= mDNSPlatformStrLen(dnsprefix) && (!strncasecmp(a->attr[1].data, dnsprefix, mDNSPlatformStrLen(dnsprefix)))) || - (a->attr[1].length >= mDNSPlatformStrLen(btmmprefix) && (!strncasecmp(a->attr[1].data, btmmprefix, mDNSPlatformStrLen(btmmprefix))))); - SecKeychainItemFreeAttributesAndData(a, NULL); - } - } - if (relevant) - { - LogInfo("*** Keychain Changed *** KeychainEvent=%d %s", - keychainEvent, - keychainEvent == kSecAddEvent ? "kSecAddEvent" : - keychainEvent == kSecDeleteEvent ? "kSecDeleteEvent" : - keychainEvent == kSecUpdateEvent ? "kSecUpdateEvent" : ""); - // We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding - KQueueLock(m); - mDNS_Lock(m); - - // To not read the keychain twice: when BTMM is enabled, changes happen to the keychain - // then the BTMM DynStore dictionary, so delay reading the keychain for a second. - // NetworkChanged() will reset the keychain timer to fire immediately when the DynStore changes. - // - // In the "fixup" case where the BTMM DNS servers aren't accepting the key mDNSResponder has, - // the DynStore dictionary won't change (because the BTMM zone won't change). In that case, - // a one second delay is ok, as we'll still converge to correctness, and there's no race - // condition between the RegistrationDomain and the DomainAuthInfo. - // - // Lastly, non-BTMM WAB cases can use the keychain but not the DynStore, so we need to set - // the timer here, as it will not get set by NetworkChanged(). - SetKeyChainTimer(m, mDNSPlatformOneSecond); - - mDNS_Unlock(m); - KQueueUnlock(m, "KeychainChanged"); - } - } - CFRelease(skc); - } - - return 0; - } +{ + LogInfo("*** Keychain Changed ***"); + mDNS *const m = (mDNS *const)context; + SecKeychainRef skc; + OSStatus err = SecKeychainCopyDefault(&skc); + if (!err) + { + if (info->keychain == skc) + { + // For delete events, attempt to verify what item was deleted fail because the item is already gone, so we just assume they may be relevant + mDNSBool relevant = (keychainEvent == kSecDeleteEvent); + if (!relevant) + { + UInt32 tags[3] = { kSecTypeItemAttr, kSecServiceItemAttr, kSecAccountItemAttr }; + SecKeychainAttributeInfo attrInfo = { 3, tags, NULL }; // Count, array of tags, array of formats + SecKeychainAttributeList *a = NULL; + err = SecKeychainItemCopyAttributesAndData(info->item, &attrInfo, NULL, &a, NULL, NULL); + if (!err) + { + relevant = ((a->attr[0].length == 4 && (!strncasecmp(a->attr[0].data, "ddns", 4) || !strncasecmp(a->attr[0].data, "sndd", 4))) || + (a->attr[1].length >= mDNSPlatformStrLen(dnsprefix) && (!strncasecmp(a->attr[1].data, dnsprefix, mDNSPlatformStrLen(dnsprefix)))) || + (a->attr[1].length >= mDNSPlatformStrLen(btmmprefix) && (!strncasecmp(a->attr[1].data, btmmprefix, mDNSPlatformStrLen(btmmprefix))))); + SecKeychainItemFreeAttributesAndData(a, NULL); + } + } + if (relevant) + { + LogInfo("*** Keychain Changed *** KeychainEvent=%d %s", + keychainEvent, + keychainEvent == kSecAddEvent ? "kSecAddEvent" : + keychainEvent == kSecDeleteEvent ? "kSecDeleteEvent" : + keychainEvent == kSecUpdateEvent ? "kSecUpdateEvent" : ""); + // We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding + KQueueLock(m); + mDNS_Lock(m); + + // To not read the keychain twice: when BTMM is enabled, changes happen to the keychain + // then the BTMM DynStore dictionary, so delay reading the keychain for a second. + // NetworkChanged() will reset the keychain timer to fire immediately when the DynStore changes. + // + // In the "fixup" case where the BTMM DNS servers aren't accepting the key mDNSResponder has, + // the DynStore dictionary won't change (because the BTMM zone won't change). In that case, + // a one second delay is ok, as we'll still converge to correctness, and there's no race + // condition between the RegistrationDomain and the DomainAuthInfo. + // + // Lastly, non-BTMM WAB cases can use the keychain but not the DynStore, so we need to set + // the timer here, as it will not get set by NetworkChanged(). + SetKeyChainTimer(m, mDNSPlatformOneSecond); + + mDNS_Unlock(m); + KQueueUnlock(m, "KeychainChanged"); + } + } + CFRelease(skc); + } + + return 0; +} #endif mDNSlocal void PowerOn(mDNS *const m) - { - mDNSCoreMachineSleep(m, false); // Will set m->SleepState = SleepState_Awake; - if (m->p->WakeAtUTC) - { - long utc = mDNSPlatformUTC(); - mDNSPowerRequest(-1,-1); // Need to explicitly clear any previous power requests -- they're not cleared automatically on wake - if (m->p->WakeAtUTC - utc > 30) LogSPS("PowerChanged PowerOn %d seconds early, assuming not maintenance wake", m->p->WakeAtUTC - utc); - else if (utc - m->p->WakeAtUTC > 30) LogSPS("PowerChanged PowerOn %d seconds late, assuming not maintenance wake", utc - m->p->WakeAtUTC); - else if (!strncasecmp(HINFO_HWstring, "K66AP", 5)) LogSPS("PowerChanged PowerOn %d seconds late, device is K66AP so not re-sleeping", utc - m->p->WakeAtUTC); - else - { - LogSPS("PowerChanged: Waking for network maintenance operations %d seconds early; re-sleeping in 20 seconds", m->p->WakeAtUTC - utc); - m->p->RequestReSleep = mDNS_TimeNow(m) + 20 * mDNSPlatformOneSecond; - } - } - } +{ + mDNSCoreMachineSleep(m, false); // Will set m->SleepState = SleepState_Awake; + if (m->p->WakeAtUTC) + { + long utc = mDNSPlatformUTC(); + mDNSPowerRequest(-1,-1); // Need to explicitly clear any previous power requests -- they're not cleared automatically on wake + if (m->p->WakeAtUTC - utc > 30) LogSPS("PowerChanged PowerOn %d seconds early, assuming not maintenance wake", m->p->WakeAtUTC - utc); + else if (utc - m->p->WakeAtUTC > 30) LogSPS("PowerChanged PowerOn %d seconds late, assuming not maintenance wake", utc - m->p->WakeAtUTC); + else if (!strncasecmp(HINFO_HWstring, "K66AP", 5)) LogSPS("PowerChanged PowerOn %d seconds late, device is K66AP so not re-sleeping", utc - m->p->WakeAtUTC); + else if (!strncasecmp(HINFO_HWstring, "J33AP", 5)) LogSPS("PowerChanged PowerOn %d seconds late, device is J33AP so not re-sleeping", utc - m->p->WakeAtUTC); + else + { + LogSPS("PowerChanged: Waking for network maintenance operations %d seconds early; re-sleeping in 20 seconds", m->p->WakeAtUTC - utc); + m->p->RequestReSleep = mDNS_TimeNow(m) + 20 * mDNSPlatformOneSecond; + } + } +} mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messageType, void *messageArgument) - { - mDNS *const m = (mDNS *const)refcon; - KQueueLock(m); - (void)service; // Parameter not used - debugf("PowerChanged %X %lX", messageType, messageArgument); - - // Make sure our m->SystemWakeOnLANEnabled value correctly reflects the current system setting - m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess(); - - switch(messageType) - { - case kIOMessageCanSystemPowerOff: LogSPS("PowerChanged kIOMessageCanSystemPowerOff (no action)"); break; // E0000240 - case kIOMessageSystemWillPowerOff: LogSPS("PowerChanged kIOMessageSystemWillPowerOff"); // E0000250 - mDNSCoreMachineSleep(m, true); - if (m->SleepState == SleepState_Sleeping) mDNSMacOSXNetworkChanged(m); - break; - case kIOMessageSystemWillNotPowerOff: LogSPS("PowerChanged kIOMessageSystemWillNotPowerOff (no action)"); break; // E0000260 - case kIOMessageCanSystemSleep: LogSPS("PowerChanged kIOMessageCanSystemSleep (no action)"); break; // E0000270 - case kIOMessageSystemWillSleep: LogSPS("PowerChanged kIOMessageSystemWillSleep"); // E0000280 - mDNSCoreMachineSleep(m, true); - break; - case kIOMessageSystemWillNotSleep: LogSPS("PowerChanged kIOMessageSystemWillNotSleep (no action)"); break; // E0000290 - case kIOMessageSystemHasPoweredOn: LogSPS("PowerChanged kIOMessageSystemHasPoweredOn"); // E0000300 - // If still sleeping (didn't get 'WillPowerOn' message for some reason?) wake now - if (m->SleepState) - { - LogMsg("PowerChanged kIOMessageSystemHasPoweredOn: ERROR m->SleepState %d", m->SleepState); - PowerOn(m); - } - // Just to be safe, schedule a mDNSMacOSXNetworkChanged(), in case we never received - // the System Configuration Framework "network changed" event that we expect - // to receive some time shortly after the kIOMessageSystemWillPowerOn message - mDNS_Lock(m); - if (!m->p->NetworkChanged || - m->p->NetworkChanged - NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2) < 0) - m->p->NetworkChanged = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2); - mDNS_Unlock(m); - - break; - case kIOMessageSystemWillRestart: LogSPS("PowerChanged kIOMessageSystemWillRestart (no action)"); break; // E0000310 - case kIOMessageSystemWillPowerOn: LogSPS("PowerChanged kIOMessageSystemWillPowerOn"); // E0000320 - - // On Leopard and earlier, on wake from sleep, instead of reporting link state down, Apple - // Ethernet drivers report "hardware incapable of detecting link state", which the kernel - // interprets as "should assume we have networking", which results in the first 4-5 seconds - // of packets vanishing into a black hole. To work around this, on wake from sleep, - // we block for five seconds to let Ethernet come up, and then resume normal operation. - if (OSXVers && OSXVers < OSXVers_10_6_SnowLeopard) - { - sleep(5); - LogMsg("Running on Mac OS X version 10.%d earlier than 10.6; " - "PowerChanged did sleep(5) to wait for Ethernet hardware", OSXVers - OSXVers_Base); - } - - // Make sure our interface list is cleared to the empty state, then tell mDNSCore to wake - if (m->SleepState != SleepState_Sleeping) - { - LogMsg("kIOMessageSystemWillPowerOn: ERROR m->SleepState %d", m->SleepState); - m->SleepState = SleepState_Sleeping; - mDNSMacOSXNetworkChanged(m); - } - PowerOn(m); - break; - default: LogSPS("PowerChanged unknown message %X", messageType); break; - } - - if (messageType == kIOMessageSystemWillSleep) m->p->SleepCookie = (long)messageArgument; - else IOAllowPowerChange(m->p->PowerConnection, (long)messageArgument); - - KQueueUnlock(m, "PowerChanged Sleep/Wake"); - } +{ + mDNS *const m = (mDNS *const)refcon; + KQueueLock(m); + (void)service; // Parameter not used + debugf("PowerChanged %X %lX", messageType, messageArgument); + + // Make sure our m->SystemWakeOnLANEnabled value correctly reflects the current system setting + m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess(); + + switch(messageType) + { + case kIOMessageCanSystemPowerOff: LogSPS("PowerChanged kIOMessageCanSystemPowerOff (no action)"); break; // E0000240 + case kIOMessageSystemWillPowerOff: LogSPS("PowerChanged kIOMessageSystemWillPowerOff"); // E0000250 + mDNSCoreMachineSleep(m, true); + if (m->SleepState == SleepState_Sleeping) mDNSMacOSXNetworkChanged(m); + break; + case kIOMessageSystemWillNotPowerOff: LogSPS("PowerChanged kIOMessageSystemWillNotPowerOff (no action)"); break; // E0000260 + case kIOMessageCanSystemSleep: LogSPS("PowerChanged kIOMessageCanSystemSleep (no action)"); break; // E0000270 + case kIOMessageSystemWillSleep: LogSPS("PowerChanged kIOMessageSystemWillSleep"); // E0000280 + mDNSCoreMachineSleep(m, true); + break; + case kIOMessageSystemWillNotSleep: LogSPS("PowerChanged kIOMessageSystemWillNotSleep (no action)"); break; // E0000290 + case kIOMessageSystemHasPoweredOn: LogSPS("PowerChanged kIOMessageSystemHasPoweredOn"); // E0000300 + // If still sleeping (didn't get 'WillPowerOn' message for some reason?) wake now + if (m->SleepState) + { + LogMsg("PowerChanged kIOMessageSystemHasPoweredOn: ERROR m->SleepState %d", m->SleepState); + PowerOn(m); + } + // Just to be safe, schedule a mDNSMacOSXNetworkChanged(), in case we never received + // the System Configuration Framework "network changed" event that we expect + // to receive some time shortly after the kIOMessageSystemWillPowerOn message + mDNS_Lock(m); + if (!m->p->NetworkChanged || + m->p->NetworkChanged - NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2) < 0) + m->p->NetworkChanged = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2); + mDNS_Unlock(m); + + break; + case kIOMessageSystemWillRestart: LogSPS("PowerChanged kIOMessageSystemWillRestart (no action)"); break; // E0000310 + case kIOMessageSystemWillPowerOn: LogSPS("PowerChanged kIOMessageSystemWillPowerOn"); // E0000320 + + // On Leopard and earlier, on wake from sleep, instead of reporting link state down, Apple + // Ethernet drivers report "hardware incapable of detecting link state", which the kernel + // interprets as "should assume we have networking", which results in the first 4-5 seconds + // of packets vanishing into a black hole. To work around this, on wake from sleep, + // we block for five seconds to let Ethernet come up, and then resume normal operation. + if (OSXVers && OSXVers < OSXVers_10_6_SnowLeopard) + { + sleep(5); + LogMsg("Running on Mac OS X version 10.%d earlier than 10.6; " + "PowerChanged did sleep(5) to wait for Ethernet hardware", OSXVers - OSXVers_Base); + } + + // Make sure our interface list is cleared to the empty state, then tell mDNSCore to wake + if (m->SleepState != SleepState_Sleeping) + { + LogMsg("kIOMessageSystemWillPowerOn: ERROR m->SleepState %d", m->SleepState); + m->SleepState = SleepState_Sleeping; + mDNSMacOSXNetworkChanged(m); + } + PowerOn(m); + break; + default: LogSPS("PowerChanged unknown message %X", messageType); break; + } + + if (messageType == kIOMessageSystemWillSleep) m->p->SleepCookie = (long)messageArgument; + else IOAllowPowerChange(m->p->PowerConnection, (long)messageArgument); + + KQueueUnlock(m, "PowerChanged Sleep/Wake"); +} // iPhone OS doesn't currently have SnowLeopard's IO Power Management // but it does define kIOPMAcknowledgmentOptionSystemCapabilityRequirements #if defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements) && !TARGET_OS_EMBEDDED mDNSlocal void SnowLeopardPowerChanged(void *refcon, IOPMConnection connection, IOPMConnectionMessageToken token, IOPMSystemPowerStateCapabilities eventDescriptor) - { - mDNS *const m = (mDNS *const)refcon; - KQueueLock(m); - LogSPS("SnowLeopardPowerChanged %X %X %X%s%s%s%s%s", - connection, token, eventDescriptor, - eventDescriptor & kIOPMSystemPowerStateCapabilityCPU ? " CPU" : "", - eventDescriptor & kIOPMSystemPowerStateCapabilityVideo ? " Video" : "", - eventDescriptor & kIOPMSystemPowerStateCapabilityAudio ? " Audio" : "", - eventDescriptor & kIOPMSystemPowerStateCapabilityNetwork ? " Network" : "", - eventDescriptor & kIOPMSystemPowerStateCapabilityDisk ? " Disk" : ""); - - // Make sure our m->SystemWakeOnLANEnabled value correctly reflects the current system setting - m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess(); - - if (eventDescriptor & kIOPMSystemPowerStateCapabilityCPU) - { - // We might be in Sleeping or Transferring state. When we go from "wakeup" to "sleep" state, we don't - // go directly to sleep state, but transfer in to the sleep state during which SleepState is set to - // SleepState_Transferring. During that time, we might get another wakeup before we transition to Sleeping - // state. In that case, we need to acknowledge the previous "sleep" before we acknowledge the wakeup. - if (m->SleepLimit) - { - LogSPS("SnowLeopardPowerChanged: Waking up, Acking old Sleep, SleepLimit %d SleepState %d", m->SleepLimit, m->SleepState); - IOPMConnectionAcknowledgeEvent(connection, m->p->SleepCookie); - m->SleepLimit = 0; - } - LogSPS("SnowLeopardPowerChanged: Waking up, Acking Wakeup, SleepLimit %d SleepState %d", m->SleepLimit, m->SleepState); - // CPU Waking. Note: Can get this message repeatedly, as other subsystems power up or down. - if (m->SleepState != SleepState_Awake) PowerOn(m); - IOPMConnectionAcknowledgeEvent(connection, token); - } - else - { - // CPU sleeping. Should not get this repeatedly -- once we're told that the CPU is halting - // we should hear nothing more until we're told that the CPU has started executing again. - if (m->SleepState) LogMsg("SnowLeopardPowerChanged: Sleep Error %X m->SleepState %d", eventDescriptor, m->SleepState); - //sleep(5); - //mDNSMacOSXNetworkChanged(m); - mDNSCoreMachineSleep(m, true); - //if (m->SleepState == SleepState_Sleeping) mDNSMacOSXNetworkChanged(m); - m->p->SleepCookie = token; - } - - KQueueUnlock(m, "SnowLeopardPowerChanged Sleep/Wake"); - } +{ + mDNS *const m = (mDNS *const)refcon; + KQueueLock(m); + LogSPS("SnowLeopardPowerChanged %X %X %X%s%s%s%s%s", + connection, token, eventDescriptor, + eventDescriptor & kIOPMSystemPowerStateCapabilityCPU ? " CPU" : "", + eventDescriptor & kIOPMSystemPowerStateCapabilityVideo ? " Video" : "", + eventDescriptor & kIOPMSystemPowerStateCapabilityAudio ? " Audio" : "", + eventDescriptor & kIOPMSystemPowerStateCapabilityNetwork ? " Network" : "", + eventDescriptor & kIOPMSystemPowerStateCapabilityDisk ? " Disk" : ""); + + // Make sure our m->SystemWakeOnLANEnabled value correctly reflects the current system setting + m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess(); + + if (eventDescriptor & kIOPMSystemPowerStateCapabilityCPU) + { + // We might be in Sleeping or Transferring state. When we go from "wakeup" to "sleep" state, we don't + // go directly to sleep state, but transfer in to the sleep state during which SleepState is set to + // SleepState_Transferring. During that time, we might get another wakeup before we transition to Sleeping + // state. In that case, we need to acknowledge the previous "sleep" before we acknowledge the wakeup. + if (m->SleepLimit) + { + LogSPS("SnowLeopardPowerChanged: Waking up, Acking old Sleep, SleepLimit %d SleepState %d", m->SleepLimit, m->SleepState); + IOPMConnectionAcknowledgeEvent(connection, m->p->SleepCookie); + m->SleepLimit = 0; + } + LogSPS("SnowLeopardPowerChanged: Waking up, Acking Wakeup, SleepLimit %d SleepState %d", m->SleepLimit, m->SleepState); + // If the network notifications have already come before we got the wakeup, we ignored them and + // in case we get no more, we need to trigger one. + mDNS_Lock(m); + SetNetworkChanged(m, 2 * mDNSPlatformOneSecond); + mDNS_Unlock(m); + // CPU Waking. Note: Can get this message repeatedly, as other subsystems power up or down. + if (m->SleepState != SleepState_Awake) PowerOn(m); + IOPMConnectionAcknowledgeEvent(connection, token); + } + else + { + // CPU sleeping. Should not get this repeatedly -- once we're told that the CPU is halting + // we should hear nothing more until we're told that the CPU has started executing again. + if (m->SleepState) LogMsg("SnowLeopardPowerChanged: Sleep Error %X m->SleepState %d", eventDescriptor, m->SleepState); + //sleep(5); + //mDNSMacOSXNetworkChanged(m); + mDNSCoreMachineSleep(m, true); + //if (m->SleepState == SleepState_Sleeping) mDNSMacOSXNetworkChanged(m); + m->p->SleepCookie = token; + } + + KQueueUnlock(m, "SnowLeopardPowerChanged Sleep/Wake"); +} #endif #if COMPILER_LIKES_PRAGMA_MARK @@ -7600,7 +7912,7 @@ mDNSlocal void SnowLeopardPowerChanged(void *refcon, IOPMConnection connection, // 23000 entries with about 4000 duplicates), we can't use a linked list to store these entries. So, we parse // them into a hash table. The implementation need to be able to do the following things efficiently // -// 1. Detect duplicates e.g., two entries with "1.2.3.4 foo" +// 1. Detect duplicates e.g., two entries with "1.2.3.4 foo" // 2. Detect whether /etc/hosts has changed and what has changed since the last read from the disk // 3. Ability to support multiple addresses per name e.g., "1.2.3.4 foo, 2.3.4.5 foo". To support this, we // need to be able set the RRSet of a resource record to the first one in the list and also update when @@ -7612,749 +7924,757 @@ mDNSlocal void SnowLeopardPowerChanged(void *refcon, IOPMConnection connection, // "hash" function to solve this, the others are hard to solve. Hence, we share the hash (AuthHash) implementation // of the core layer which does all of the above very efficiently +#define ETCHOSTS_BUFSIZE 1024 // Buffer size to parse a single line in /etc/hosts + mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result) - { +{ (void)m; // unused - (void)rr; - (void)result; - if (result == mStatus_MemFree) - { - LogInfo("FreeEtcHosts: %s", ARDisplayString(m, rr)); - freeL("etchosts", rr); - } - } + (void)rr; + (void)result; + if (result == mStatus_MemFree) + { + LogInfo("FreeEtcHosts: %s", ARDisplayString(m, rr)); + freeL("etchosts", rr); + } +} // Returns true on success and false on failure mDNSlocal mDNSBool mDNSMacOSXCreateEtcHostsEntry(mDNS *const m, const domainname *domain, const struct sockaddr *sa, const domainname *cname, char *ifname, AuthHash *auth) - { - AuthRecord *rr; - mDNSu32 slot; - mDNSu32 namehash; - AuthGroup *ag; - mDNSInterfaceID InterfaceID = mDNSInterface_LocalOnly; - mDNSu16 rrtype; - - if (!domain) - { - LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! name NULL"); - return mDNSfalse; - } - if (!sa && !cname) - { - LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! sa and cname both NULL"); - return mDNSfalse; - } - - if (sa && sa->sa_family != AF_INET && sa->sa_family != AF_INET6) - { - LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! sa with bad family %d", sa->sa_family); - return mDNSfalse; - } - - - if (ifname) - { - mDNSu32 ifindex = if_nametoindex(ifname); - if (!ifindex) - { - LogMsg("mDNSMacOSXCreateEtcHostsEntry: hosts entry %##s with invalid ifname %s", domain->c, ifname); - return mDNSfalse; - } - InterfaceID = (mDNSInterfaceID)(uintptr_t)ifindex; - } - - if (sa) - rrtype = (sa->sa_family == AF_INET ? kDNSType_A : kDNSType_AAAA); - else - rrtype = kDNSType_CNAME; - - // Check for duplicates. See whether we parsed an entry before like this ? - slot = AuthHashSlot(domain); - namehash = DomainNameHashValue(domain); - ag = AuthGroupForName(auth, slot, namehash, domain); - if (ag) - { - rr = ag->members; - while (rr) - { - if (rr->resrec.rrtype == rrtype) - { - if (rrtype == kDNSType_A) - { - mDNSv4Addr ip; - ip.NotAnInteger = ((struct sockaddr_in*)sa)->sin_addr.s_addr; - if (mDNSSameIPv4Address(rr->resrec.rdata->u.ipv4, ip)) - { - LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same IPv4 address for name %##s", domain->c); - return mDNSfalse; - } - } - else if (rrtype == kDNSType_AAAA) - { - mDNSv6Addr ip6; - ip6.l[0] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[0]; - ip6.l[1] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[1]; - ip6.l[2] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[2]; - ip6.l[3] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[3]; - if (mDNSSameIPv6Address(rr->resrec.rdata->u.ipv6, ip6)) - { - LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same IPv6 address for name %##s", domain->c); - return mDNSfalse; - } - } - else if (rrtype == kDNSType_CNAME) - { - if (SameDomainName(&rr->resrec.rdata->u.name, cname)) - { - LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same cname %##s for name %##s", cname->c, domain->c); - return mDNSfalse; - } - } - } - rr = rr->next; - } - } - rr= mallocL("etchosts", sizeof(*rr)); - if (rr == NULL) return mDNSfalse; - mDNSPlatformMemZero(rr, sizeof(*rr)); - mDNS_SetupResourceRecord(rr, NULL, InterfaceID, rrtype, 1, kDNSRecordTypeKnownUnique, AuthRecordLocalOnly, FreeEtcHosts, NULL); - AssignDomainName(&rr->namestorage, domain); - - if (sa) - { - rr->resrec.rdlength = sa->sa_family == AF_INET ? sizeof(mDNSv4Addr) : sizeof(mDNSv6Addr); - if (sa->sa_family == AF_INET) - rr->resrec.rdata->u.ipv4.NotAnInteger = ((struct sockaddr_in*)sa)->sin_addr.s_addr; - else - { - rr->resrec.rdata->u.ipv6.l[0] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[0]; - rr->resrec.rdata->u.ipv6.l[1] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[1]; - rr->resrec.rdata->u.ipv6.l[2] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[2]; - rr->resrec.rdata->u.ipv6.l[3] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[3]; - } - } - else - { - rr->resrec.rdlength = DomainNameLength(cname); - rr->resrec.rdata->u.name.c[0] = 0; - AssignDomainName(&rr->resrec.rdata->u.name, cname); - } - rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); - SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us - LogInfo("mDNSMacOSXCreateEtcHostsEntry: Adding resource record %s", ARDisplayString(m, rr)); - InsertAuthRecord(m, auth, rr); - return mDNStrue; - } +{ + AuthRecord *rr; + mDNSu32 slot; + mDNSu32 namehash; + AuthGroup *ag; + mDNSInterfaceID InterfaceID = mDNSInterface_LocalOnly; + mDNSu16 rrtype; + + if (!domain) + { + LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! name NULL"); + return mDNSfalse; + } + if (!sa && !cname) + { + LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! sa and cname both NULL"); + return mDNSfalse; + } + + if (sa && sa->sa_family != AF_INET && sa->sa_family != AF_INET6) + { + LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! sa with bad family %d", sa->sa_family); + return mDNSfalse; + } + + + if (ifname) + { + mDNSu32 ifindex = if_nametoindex(ifname); + if (!ifindex) + { + LogMsg("mDNSMacOSXCreateEtcHostsEntry: hosts entry %##s with invalid ifname %s", domain->c, ifname); + return mDNSfalse; + } + InterfaceID = (mDNSInterfaceID)(uintptr_t)ifindex; + } + + if (sa) + rrtype = (sa->sa_family == AF_INET ? kDNSType_A : kDNSType_AAAA); + else + rrtype = kDNSType_CNAME; + + // Check for duplicates. See whether we parsed an entry before like this ? + slot = AuthHashSlot(domain); + namehash = DomainNameHashValue(domain); + ag = AuthGroupForName(auth, slot, namehash, domain); + if (ag) + { + rr = ag->members; + while (rr) + { + if (rr->resrec.rrtype == rrtype) + { + if (rrtype == kDNSType_A) + { + mDNSv4Addr ip; + ip.NotAnInteger = ((struct sockaddr_in*)sa)->sin_addr.s_addr; + if (mDNSSameIPv4Address(rr->resrec.rdata->u.ipv4, ip)) + { + LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same IPv4 address for name %##s", domain->c); + return mDNSfalse; + } + } + else if (rrtype == kDNSType_AAAA) + { + mDNSv6Addr ip6; + ip6.l[0] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[0]; + ip6.l[1] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[1]; + ip6.l[2] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[2]; + ip6.l[3] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[3]; + if (mDNSSameIPv6Address(rr->resrec.rdata->u.ipv6, ip6)) + { + LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same IPv6 address for name %##s", domain->c); + return mDNSfalse; + } + } + else if (rrtype == kDNSType_CNAME) + { + if (SameDomainName(&rr->resrec.rdata->u.name, cname)) + { + LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same cname %##s for name %##s", cname->c, domain->c); + return mDNSfalse; + } + } + } + rr = rr->next; + } + } + rr= mallocL("etchosts", sizeof(*rr)); + if (rr == NULL) return mDNSfalse; + mDNSPlatformMemZero(rr, sizeof(*rr)); + mDNS_SetupResourceRecord(rr, NULL, InterfaceID, rrtype, 1, kDNSRecordTypeKnownUnique, AuthRecordLocalOnly, FreeEtcHosts, NULL); + AssignDomainName(&rr->namestorage, domain); + + if (sa) + { + rr->resrec.rdlength = sa->sa_family == AF_INET ? sizeof(mDNSv4Addr) : sizeof(mDNSv6Addr); + if (sa->sa_family == AF_INET) + rr->resrec.rdata->u.ipv4.NotAnInteger = ((struct sockaddr_in*)sa)->sin_addr.s_addr; + else + { + rr->resrec.rdata->u.ipv6.l[0] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[0]; + rr->resrec.rdata->u.ipv6.l[1] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[1]; + rr->resrec.rdata->u.ipv6.l[2] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[2]; + rr->resrec.rdata->u.ipv6.l[3] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[3]; + } + } + else + { + rr->resrec.rdlength = DomainNameLength(cname); + rr->resrec.rdata->u.name.c[0] = 0; + AssignDomainName(&rr->resrec.rdata->u.name, cname); + } + rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); + SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us + LogInfo("mDNSMacOSXCreateEtcHostsEntry: Adding resource record %s", ARDisplayString(m, rr)); + InsertAuthRecord(m, auth, rr); + return mDNStrue; +} mDNSlocal int EtcHostsParseOneName(int start, int length, char *buffer, char **name) - { - int i; - - *name = NULL; - for (i = start; i < length; i++) - { - if (buffer[i] != ' ' && buffer[i] != ',' && buffer[i] != '\t') - { - *name = &buffer[i]; - - // Found the start of a name, find the end and null terminate - for (i++; i < length; i++) - { - if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t') - { - buffer[i] = 0; - break; - } - } - return i; - } - } - return -1; - } +{ + int i; + + *name = NULL; + for (i = start; i < length; i++) + { + if (buffer[i] == '#') + return -1; + if (buffer[i] != ' ' && buffer[i] != ',' && buffer[i] != '\t') + { + *name = &buffer[i]; + + // Found the start of a name, find the end and null terminate + for (i++; i < length; i++) + { + if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t') + { + buffer[i] = 0; + break; + } + } + return i; + } + } + return -1; +} mDNSlocal void mDNSMacOSXParseEtcHostsLine(mDNS *const m, char *buffer, ssize_t length, AuthHash *auth) - { - int i; - int ifStart = 0; - char *ifname = NULL; - domainname name1d; - domainname name2d; - char *name1; - char *name2; - int aliasIndex; - - // Find the end of the address string - for (i = 0; i < length; i++) - { - if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t' || buffer[i] == '%') - { - if (buffer[i] == '%') - ifStart = i + 1; - buffer[i] = 0; - break; - } - } - - // Convert the address string to an address - struct addrinfo hints; - bzero(&hints, sizeof(hints)); - hints.ai_flags = AI_NUMERICHOST; - struct addrinfo *gairesults = NULL; - if (getaddrinfo(buffer, NULL, &hints, &gairesults) != 0) - { - LogInfo("mDNSMacOSXParseEtcHostsLine: getaddrinfo returning null"); - return; - } - - if (ifStart) - { - // Parse the interface - ifname = &buffer[ifStart]; - for (i = ifStart + 1; i < length; i++) - { - if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t') - { - buffer[i] = 0; - break; - } - } - } - - i = EtcHostsParseOneName(i + 1, length, buffer, &name1); - if (i == length) - { - // Common case (no aliases) : The entry is of the form "1.2.3.4 somehost" with no trailing white spaces/tabs etc. - if (!MakeDomainNameFromDNSNameString(&name1d, name1)) - { - LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name1); - freeaddrinfo(gairesults); - return; - } - mDNSMacOSXCreateEtcHostsEntry(m, &name1d, gairesults->ai_addr, mDNSNULL, ifname, auth); - } - else if (i != -1) - { - domainname first; - // We might have some extra white spaces at the end for the common case of "1.2.3.4 somehost". - // When we parse again below, EtchHostsParseOneName would return -1 and we will end up - // doing the right thing. - if (!MakeDomainNameFromDNSNameString(&first, name1)) - { - LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name1); - freeaddrinfo(gairesults); - return; - } - // If the /etc/hosts has an entry like this - // - // 1.2.3.4 sun star bright - // - // star and bright are aliases (gethostbyname h_alias should point to these) and sun is the canonical - // name (getaddrinfo ai_cannonname and gethostbyname h_name points to "sun") - // - // To achieve this, we need to add the entry like this: - // - // star CNAME bright - // bright CNAME sun - // sun A 1.2.3.4 - // - // We store the first name we parsed in "first". Then we parse additional names adding CNAME records - // till we reach the end. When we reach the end, we wrap around and add one final CNAME with the last - // entry and the first entry. Finally, we add the Address (A/AAAA) record. - aliasIndex = 0; - while (i <= length) - { - // Parse a name. If there are no names, we need to know whether we - // parsed CNAMEs before or not. If we parsed CNAMEs before, then we - // add a CNAME with the last name and the first name. Otherwise, this - // is same as the common case above where the line has just one name - // but with trailing white spaces. - i = EtcHostsParseOneName(i + 1, length, buffer, &name2); - if (name2) - { - if (!MakeDomainNameFromDNSNameString(&name2d, name2)) - { - LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name2); - freeaddrinfo(gairesults); - return; - } - aliasIndex++; - } - else if (!aliasIndex) - { - // We have never parsed any aliases. This case happens if there - // is just one name and some extra white spaces at the end. - LogInfo("mDNSMacOSXParseEtcHostsLine: White space at the end of %##s", first.c); - break; - } - else - { - // We have parsed at least one alias before and we reached the end of the line. - // Setup a CNAME for the last name with "first" name as its RDATA - name2d.c[0] = 0; - AssignDomainName(&name2d, &first); - } - - // Don't add a CNAME for the first alias we parse (see the example above). - // As we parse more, we might discover that there are no more aliases, in - // which case we would have set "name2d" to "first" above. We need to add - // the CNAME in that case. - - if (aliasIndex > 1 || SameDomainName(&name2d, &first)) - { - // Ignore if it points to itself - if (!SameDomainName(&name1d, &name2d)) - { - if (!mDNSMacOSXCreateEtcHostsEntry(m, &name1d, mDNSNULL, &name2d, ifname, auth)) - { - freeaddrinfo(gairesults); - return; - } - } - else - LogMsg("mDNSMacOSXParseEtcHostsLine: Ignoring entry with same names name1 %##s, name2 %##s", name1d.c, name2d.c); - } - - // If we have already wrapped around, we just need to add the A/AAAA record alone - // which is done below - if (SameDomainName(&name2d, &first)) break; - - // Remember the current name so that we can set the CNAME record if we parse one - // more name - name1d.c[0] = 0; - AssignDomainName(&name1d, &name2d); - } - // Added all the CNAMEs if any, add the "A/AAAA" record - mDNSMacOSXCreateEtcHostsEntry(m, &first, gairesults->ai_addr, mDNSNULL, ifname, auth); - } - freeaddrinfo(gairesults); - } +{ + int i; + int ifStart = 0; + char *ifname = NULL; + domainname name1d; + domainname name2d; + char *name1; + char *name2; + int aliasIndex; + + //Ignore leading whitespaces and tabs + while (*buffer == ' ' || *buffer == '\t') + { + buffer++; + length--; + } + + // Find the end of the address string + for (i = 0; i < length; i++) + { + if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t' || buffer[i] == '%') + { + if (buffer[i] == '%') + ifStart = i + 1; + buffer[i] = 0; + break; + } + } + + // Convert the address string to an address + struct addrinfo hints; + bzero(&hints, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + struct addrinfo *gairesults = NULL; + if (getaddrinfo(buffer, NULL, &hints, &gairesults) != 0) + { + LogInfo("mDNSMacOSXParseEtcHostsLine: getaddrinfo returning null"); + return; + } + + if (ifStart) + { + // Parse the interface + ifname = &buffer[ifStart]; + for (i = ifStart + 1; i < length; i++) + { + if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t') + { + buffer[i] = 0; + break; + } + } + } + + i = EtcHostsParseOneName(i + 1, length, buffer, &name1); + if (i == length) + { + // Common case (no aliases) : The entry is of the form "1.2.3.4 somehost" with no trailing white spaces/tabs etc. + if (!MakeDomainNameFromDNSNameString(&name1d, name1)) + { + LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name1); + freeaddrinfo(gairesults); + return; + } + mDNSMacOSXCreateEtcHostsEntry(m, &name1d, gairesults->ai_addr, mDNSNULL, ifname, auth); + } + else if (i != -1) + { + domainname first; + // We might have some extra white spaces at the end for the common case of "1.2.3.4 somehost". + // When we parse again below, EtchHostsParseOneName would return -1 and we will end up + // doing the right thing. + if (!MakeDomainNameFromDNSNameString(&first, name1)) + { + LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name1); + freeaddrinfo(gairesults); + return; + } + // If the /etc/hosts has an entry like this + // + // 1.2.3.4 sun star bright + // + // star and bright are aliases (gethostbyname h_alias should point to these) and sun is the canonical + // name (getaddrinfo ai_cannonname and gethostbyname h_name points to "sun") + // + // To achieve this, we need to add the entry like this: + // + // star CNAME bright + // bright CNAME sun + // sun A 1.2.3.4 + // + // We store the first name we parsed in "first". Then we parse additional names adding CNAME records + // till we reach the end. When we reach the end, we wrap around and add one final CNAME with the last + // entry and the first entry. Finally, we add the Address (A/AAAA) record. + aliasIndex = 0; + while (i <= length) + { + // Parse a name. If there are no names, we need to know whether we + // parsed CNAMEs before or not. If we parsed CNAMEs before, then we + // add a CNAME with the last name and the first name. Otherwise, this + // is same as the common case above where the line has just one name + // but with trailing white spaces. + i = EtcHostsParseOneName(i + 1, length, buffer, &name2); + if (name2) + { + if (!MakeDomainNameFromDNSNameString(&name2d, name2)) + { + LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name2); + freeaddrinfo(gairesults); + return; + } + aliasIndex++; + } + else if (!aliasIndex) + { + // We have never parsed any aliases. This case happens if there + // is just one name and some extra white spaces at the end. + LogInfo("mDNSMacOSXParseEtcHostsLine: White space at the end of %##s", first.c); + break; + } + else + { + // We have parsed at least one alias before and we reached the end of the line. + // Setup a CNAME for the last name with "first" name as its RDATA + name2d.c[0] = 0; + AssignDomainName(&name2d, &first); + } + + // Don't add a CNAME for the first alias we parse (see the example above). + // As we parse more, we might discover that there are no more aliases, in + // which case we would have set "name2d" to "first" above. We need to add + // the CNAME in that case. + + if (aliasIndex > 1 || SameDomainName(&name2d, &first)) + { + // Ignore if it points to itself + if (!SameDomainName(&name1d, &name2d)) + { + if (!mDNSMacOSXCreateEtcHostsEntry(m, &name1d, mDNSNULL, &name2d, ifname, auth)) + { + freeaddrinfo(gairesults); + return; + } + } + else + LogMsg("mDNSMacOSXParseEtcHostsLine: Ignoring entry with same names name1 %##s, name2 %##s", name1d.c, name2d.c); + } + + // If we have already wrapped around, we just need to add the A/AAAA record alone + // which is done below + if (SameDomainName(&name2d, &first)) break; + + // Remember the current name so that we can set the CNAME record if we parse one + // more name + name1d.c[0] = 0; + AssignDomainName(&name1d, &name2d); + } + // Added all the CNAMEs if any, add the "A/AAAA" record + mDNSMacOSXCreateEtcHostsEntry(m, &first, gairesults->ai_addr, mDNSNULL, ifname, auth); + } + freeaddrinfo(gairesults); +} mDNSlocal void mDNSMacOSXParseEtcHosts(mDNS *const m, int fd, AuthHash *auth) - { - if (fd == -1) { LogInfo("mDNSMacOSXParseEtcHosts: fd is -1"); return; } - - LogInfo("mDNSMacOSXParseEtcHosts: Parsing etc hosts"); - - // Read through the file - char readBuf[1024]; // support a maximum line of 1024 bytes including newline - off_t offset = 0; - int comment = 0; - ssize_t i = 0; - ssize_t length; - ssize_t linestart; - while ((length = pread(fd, &readBuf[i], sizeof(readBuf) - i, offset) + i) >= i) - { - if (length == i) - { - // end of file - if (length != 0) mDNSMacOSXParseEtcHostsLine(m, readBuf, length, auth); - break; - } - - // increment our offset by the number of bytes we read - offset += length - i; - - // loop through the buffer looking for end of lines - for (linestart = 0; i < length; i++) - { - if (readBuf[i] == '#' || readBuf[i] == '\r' || readBuf[i] == '\n') - { - int parseline = i - linestart > 0 && comment == 0; - comment = readBuf[i] == '#'; - if (parseline) - { - readBuf[i] = 0; - mDNSMacOSXParseEtcHostsLine(m, &readBuf[linestart], i - linestart, auth); - } - linestart = i + 1; - } - } - - // prepare to read the next chunk of the file - if (linestart == 0) - { - // single line was larger than our buffer, we're going to ignore it - comment = 1; // treat remainder of line as comment - i = 0; - } - else - { - i = length - linestart; - if (i) memmove(readBuf, &readBuf[linestart], i); - } - } - } +{ + mDNSBool good; + char buf[ETCHOSTS_BUFSIZE]; + ssize_t len; + FILE *fp; + + if (fd == -1) { LogInfo("mDNSMacOSXParseEtcHosts: fd is -1"); return; } + + fp = fopen("/etc/hosts", "r"); + if (!fp) { LogInfo("mDNSMacOSXParseEtcHosts: fp is NULL"); return; } + + while (1) + { + good = (fgets(buf, ETCHOSTS_BUFSIZE, fp) != NULL); + if (!good) break; + + // skip comment and empty lines + if (buf[0] == '#' || buf[0] == '\r' || buf[0] == '\n') + continue; + + len = strlen(buf); + if (!len) break; // sanity check + //Check for end of line code(mostly only \n but pre-OS X Macs could have only \r) + if (buf[len - 1] == '\r' || buf[len - 1] == '\n') + { + buf[len - 1] = '\0'; + len = len - 1; + } + // fgets always null terminates and hence even if we have no + // newline at the end, it is null terminated. The callee + // (mDNSMacOSXParseEtcHostsLine) expects the length to be such that + // buf[length] is zero and hence we decrement len to reflect that. + if (len) + { + //Additional check when end of line code is 2 chars ie\r\n(DOS, other old OSes) + //here we need to check for just \r but taking extra caution. + if (buf[len - 1] == '\r' || buf[len - 1] == '\n') + { + buf[len - 1] = '\0'; + len = len - 1; + } + } + if (!len) //Sanity Check: len should never be zero + { + LogMsg("mDNSMacOSXParseEtcHosts: Length is zero!"); + continue; + } + mDNSMacOSXParseEtcHostsLine(m, buf, len, auth); + } + fclose(fp); +} mDNSlocal void mDNSMacOSXUpdateEtcHosts(mDNS *const m); mDNSlocal int mDNSMacOSXGetEtcHostsFD(mDNS *const m) - { +{ #ifdef __DISPATCH_GROUP__ - // Can't do this stuff to be notified of changes in /etc/hosts if we don't have libdispatch - static dispatch_queue_t etcq = 0; - static dispatch_source_t etcsrc = 0; - static dispatch_source_t hostssrc = 0; - - // First time through? just schedule ourselves on the main queue and we'll do the work later - if (!etcq) - { - etcq = dispatch_get_main_queue(); - if (etcq) - { - // Do this work on the queue, not here - solves potential synchronization issues - dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);}); - } - return -1; - } - - if (hostssrc) return dispatch_source_get_handle(hostssrc); + // Can't do this stuff to be notified of changes in /etc/hosts if we don't have libdispatch + static dispatch_queue_t etcq = 0; + static dispatch_source_t etcsrc = 0; + static dispatch_source_t hostssrc = 0; + + // First time through? just schedule ourselves on the main queue and we'll do the work later + if (!etcq) + { + etcq = dispatch_get_main_queue(); + if (etcq) + { + // Do this work on the queue, not here - solves potential synchronization issues + dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);}); + } + return -1; + } + + if (hostssrc) return dispatch_source_get_handle(hostssrc); #endif - int fd = open("/etc/hosts", O_RDONLY); - + int fd = open("/etc/hosts", O_RDONLY); + #ifdef __DISPATCH_GROUP__ - // Can't do this stuff to be notified of changes in /etc/hosts if we don't have libdispatch - if (fd == -1) - { - // If the open failed and we're already watching /etc, we're done - if (etcsrc) { LogInfo("mDNSMacOSXGetEtcHostsFD: Returning etcfd because no etchosts"); return fd; } - - // we aren't watching /etc, we should be - fd = open("/etc", O_RDONLY); - if (fd == -1) { LogInfo("mDNSMacOSXGetEtcHostsFD: etc does not exist"); return -1; } - etcsrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_RENAME, etcq); - if (etcsrc == NULL) - { - close(fd); - return -1; - } - dispatch_source_set_event_handler(etcsrc, - ^{ - u_int32_t flags = dispatch_source_get_data(etcsrc); - LogMsg("mDNSMacOSXGetEtcHostsFD: /etc changed 0x%x", flags); - if ((flags & (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME)) != 0) - { - dispatch_source_cancel(etcsrc); - dispatch_release(etcsrc); - etcsrc = NULL; - dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);}); - return; - } - if ((flags & DISPATCH_VNODE_WRITE) != 0 && hostssrc == NULL) - { - mDNSMacOSXUpdateEtcHosts(m); - } - }); - dispatch_source_set_cancel_handler(etcsrc, ^{close(fd);}); - dispatch_resume(etcsrc); - - // Try and open /etc/hosts once more now that we're watching /etc, in case we missed the creation - fd = open("/etc/hosts", O_RDONLY | O_EVTONLY); - if (fd == -1) { LogMsg("mDNSMacOSXGetEtcHostsFD etc hosts does not exist, watching etc"); return -1; } - } - - // create a dispatch source to watch for changes to hosts file - hostssrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, - (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_RENAME | - DISPATCH_VNODE_ATTRIB | DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_LINK | DISPATCH_VNODE_REVOKE), etcq); - if (hostssrc == NULL) - { - close(fd); - return -1; - } - dispatch_source_set_event_handler(hostssrc, - ^{ - u_int32_t flags = dispatch_source_get_data(hostssrc); - LogInfo("mDNSMacOSXGetEtcHostsFD: /etc/hosts changed 0x%x", flags); - if ((flags & (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME)) != 0) - { - dispatch_source_cancel(hostssrc); - dispatch_release(hostssrc); - hostssrc = NULL; - // Bug in LibDispatch: wait a second before scheduling the block. If we schedule - // the block immediately, we try to open the file and the file may not exist and may - // fail to get a notification in the future. When the file does not exist and - // we start to monitor the directory, on "dispatch_resume" of that source, there - // is no guarantee that the file creation will be notified always because when - // the dispatch_resume returns, the kevent manager may not have registered the - // kevent yet but the file may have been created - usleep(1000000); - dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);}); - return; - } - if ((flags & DISPATCH_VNODE_WRITE) != 0) - { - mDNSMacOSXUpdateEtcHosts(m); - } - }); - dispatch_source_set_cancel_handler(hostssrc, ^{LogInfo("mDNSMacOSXGetEtcHostsFD: Closing etchosts fd %d", fd); close(fd);}); - dispatch_resume(hostssrc); - - // Cleanup /etc source, no need to watch it if we already have /etc/hosts - if (etcsrc) - { - dispatch_source_cancel(etcsrc); - dispatch_release(etcsrc); - etcsrc = NULL; - } - - LogInfo("mDNSMacOSXGetEtcHostsFD: /etc/hosts being monitored, and not etc"); - return hostssrc ? (int)dispatch_source_get_handle(hostssrc) : -1; + // Can't do this stuff to be notified of changes in /etc/hosts if we don't have libdispatch + if (fd == -1) + { + // If the open failed and we're already watching /etc, we're done + if (etcsrc) { LogInfo("mDNSMacOSXGetEtcHostsFD: Returning etcfd because no etchosts"); return fd; } + + // we aren't watching /etc, we should be + fd = open("/etc", O_RDONLY); + if (fd == -1) { LogInfo("mDNSMacOSXGetEtcHostsFD: etc does not exist"); return -1; } + etcsrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_RENAME, etcq); + if (etcsrc == NULL) + { + close(fd); + return -1; + } + dispatch_source_set_event_handler(etcsrc, + ^{ + u_int32_t flags = dispatch_source_get_data(etcsrc); + LogMsg("mDNSMacOSXGetEtcHostsFD: /etc changed 0x%x", flags); + if ((flags & (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME)) != 0) + { + dispatch_source_cancel(etcsrc); + dispatch_release(etcsrc); + etcsrc = NULL; + dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);}); + return; + } + if ((flags & DISPATCH_VNODE_WRITE) != 0 && hostssrc == NULL) + { + mDNSMacOSXUpdateEtcHosts(m); + } + }); + dispatch_source_set_cancel_handler(etcsrc, ^{close(fd);}); + dispatch_resume(etcsrc); + + // Try and open /etc/hosts once more now that we're watching /etc, in case we missed the creation + fd = open("/etc/hosts", O_RDONLY | O_EVTONLY); + if (fd == -1) { LogMsg("mDNSMacOSXGetEtcHostsFD etc hosts does not exist, watching etc"); return -1; } + } + + // create a dispatch source to watch for changes to hosts file + hostssrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, + (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_RENAME | + DISPATCH_VNODE_ATTRIB | DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_LINK | DISPATCH_VNODE_REVOKE), etcq); + if (hostssrc == NULL) + { + close(fd); + return -1; + } + dispatch_source_set_event_handler(hostssrc, + ^{ + u_int32_t flags = dispatch_source_get_data(hostssrc); + LogInfo("mDNSMacOSXGetEtcHostsFD: /etc/hosts changed 0x%x", flags); + if ((flags & (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME)) != 0) + { + dispatch_source_cancel(hostssrc); + dispatch_release(hostssrc); + hostssrc = NULL; + // Bug in LibDispatch: wait a second before scheduling the block. If we schedule + // the block immediately, we try to open the file and the file may not exist and may + // fail to get a notification in the future. When the file does not exist and + // we start to monitor the directory, on "dispatch_resume" of that source, there + // is no guarantee that the file creation will be notified always because when + // the dispatch_resume returns, the kevent manager may not have registered the + // kevent yet but the file may have been created + usleep(1000000); + dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);}); + return; + } + if ((flags & DISPATCH_VNODE_WRITE) != 0) + { + mDNSMacOSXUpdateEtcHosts(m); + } + }); + dispatch_source_set_cancel_handler(hostssrc, ^{LogInfo("mDNSMacOSXGetEtcHostsFD: Closing etchosts fd %d", fd); close(fd);}); + dispatch_resume(hostssrc); + + // Cleanup /etc source, no need to watch it if we already have /etc/hosts + if (etcsrc) + { + dispatch_source_cancel(etcsrc); + dispatch_release(etcsrc); + etcsrc = NULL; + } + + LogInfo("mDNSMacOSXGetEtcHostsFD: /etc/hosts being monitored, and not etc"); + return hostssrc ? (int)dispatch_source_get_handle(hostssrc) : -1; #else - (void)m; - return fd; + (void)m; + return fd; #endif - } +} // When /etc/hosts is modified, flush all the cache records as there may be local // authoritative answers now mDNSlocal void FlushAllCacheRecords(mDNS *const m) - { - CacheRecord *cr; - mDNSu32 slot; - CacheGroup *cg; - - FORALL_CACHERECORDS(slot, cg, cr) - { - // Skip multicast. - if (cr->resrec.InterfaceID) continue; - - // If a resource record can answer A or AAAA, they need to be flushed so that we will - // never used to deliver an ADD or RMV - if (RRTypeAnswersQuestionType(&cr->resrec, kDNSType_A) || - RRTypeAnswersQuestionType(&cr->resrec, kDNSType_AAAA)) - { - LogInfo("FlushAllCacheRecords: Purging Resourcerecord %s", CRDisplayString(m, cr)); - mDNS_PurgeCacheResourceRecord(m, cr); - } - } - } +{ + CacheRecord *cr; + mDNSu32 slot; + CacheGroup *cg; + + FORALL_CACHERECORDS(slot, cg, cr) + { + // Skip multicast. + if (cr->resrec.InterfaceID) continue; + + // If a resource record can answer A or AAAA, they need to be flushed so that we will + // never used to deliver an ADD or RMV + if (RRTypeAnswersQuestionType(&cr->resrec, kDNSType_A) || + RRTypeAnswersQuestionType(&cr->resrec, kDNSType_AAAA)) + { + LogInfo("FlushAllCacheRecords: Purging Resourcerecord %s", CRDisplayString(m, cr)); + mDNS_PurgeCacheResourceRecord(m, cr); + } + } +} // Add new entries to the core. If justCheck is set, this function does not add, just returns true mDNSlocal mDNSBool EtcHostsAddNewEntries(mDNS *const m, AuthHash *newhosts, mDNSBool justCheck) - { - AuthGroup *ag; - mDNSu32 slot; - AuthRecord *rr, *primary, *rrnext; - for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) - for (ag = newhosts->rrauth_hash[slot]; ag; ag = ag->next) - { - primary = NULL; - for (rr = ag->members; rr; rr = rrnext) - { - rrnext = rr->next; - AuthGroup *ag1; - AuthRecord *rr1; - mDNSBool found = mDNSfalse; - ag1 = AuthGroupForRecord(&m->rrauth, slot, &rr->resrec); - if (ag1 && ag1->members) - { - if (!primary) primary = ag1->members; - rr1 = ag1->members; - while (rr1) - { - // We are not using InterfaceID in checking for duplicates. This means, - // if there are two addresses for a given name e.g., fe80::1%en0 and - // fe80::1%en1, we only add the first one. It is not clear whether - // this is a common case. To fix this, we also need to modify - // mDNS_Register_internal in how it handles duplicates. If it becomes a - // common case, we will fix it then. - if (IdenticalResourceRecord(&rr1->resrec, &rr->resrec)) - { - LogInfo("EtcHostsAddNewEntries: Skipping, not adding %s", ARDisplayString(m, rr1)); - found = mDNStrue; - break; - } - rr1 = rr1->next; - } - } - if (!found) - { - if (justCheck) - { - LogInfo("EtcHostsAddNewEntries: Entry %s not registered with core yet", ARDisplayString(m, rr)); - return mDNStrue; - } - RemoveAuthRecord(m, newhosts, rr); - // if there is no primary, point to self - rr->RRSet = (primary ? primary : rr); - rr->next = NULL; - LogInfo("EtcHostsAddNewEntries: Adding %s", ARDisplayString(m, rr)); - if (mDNS_Register_internal(m, rr) != mStatus_NoError) - LogMsg("EtcHostsAddNewEntries: mDNS_Register failed for %s", ARDisplayString(m, rr)); - } - } - } - return mDNSfalse; - } +{ + AuthGroup *ag; + mDNSu32 slot; + AuthRecord *rr, *primary, *rrnext; + for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) + for (ag = newhosts->rrauth_hash[slot]; ag; ag = ag->next) + { + primary = NULL; + for (rr = ag->members; rr; rr = rrnext) + { + rrnext = rr->next; + AuthGroup *ag1; + AuthRecord *rr1; + mDNSBool found = mDNSfalse; + ag1 = AuthGroupForRecord(&m->rrauth, slot, &rr->resrec); + if (ag1 && ag1->members) + { + if (!primary) primary = ag1->members; + rr1 = ag1->members; + while (rr1) + { + // We are not using InterfaceID in checking for duplicates. This means, + // if there are two addresses for a given name e.g., fe80::1%en0 and + // fe80::1%en1, we only add the first one. It is not clear whether + // this is a common case. To fix this, we also need to modify + // mDNS_Register_internal in how it handles duplicates. If it becomes a + // common case, we will fix it then. + if (IdenticalResourceRecord(&rr1->resrec, &rr->resrec)) + { + LogInfo("EtcHostsAddNewEntries: Skipping, not adding %s", ARDisplayString(m, rr1)); + found = mDNStrue; + break; + } + rr1 = rr1->next; + } + } + if (!found) + { + if (justCheck) + { + LogInfo("EtcHostsAddNewEntries: Entry %s not registered with core yet", ARDisplayString(m, rr)); + return mDNStrue; + } + RemoveAuthRecord(m, newhosts, rr); + // if there is no primary, point to self + rr->RRSet = (primary ? primary : rr); + rr->next = NULL; + LogInfo("EtcHostsAddNewEntries: Adding %s", ARDisplayString(m, rr)); + if (mDNS_Register_internal(m, rr) != mStatus_NoError) + LogMsg("EtcHostsAddNewEntries: mDNS_Register failed for %s", ARDisplayString(m, rr)); + } + } + } + return mDNSfalse; +} // Delete entries from the core that are no longer needed. If justCheck is set, this function // does not delete, just returns true mDNSlocal mDNSBool EtcHostsDeleteOldEntries(mDNS *const m, AuthHash *newhosts, mDNSBool justCheck) - { - AuthGroup *ag; - mDNSu32 slot; - AuthRecord *rr, *primary, *rrnext; - for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) - for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next) - for (rr = ag->members; rr; rr = rrnext) - { - mDNSBool found = mDNSfalse; - AuthGroup *ag1; - AuthRecord *rr1; - rrnext = rr->next; - if (rr->RecordCallback != FreeEtcHosts) continue; - ag1 = AuthGroupForRecord(newhosts, slot, &rr->resrec); - if (ag1) - { - primary = rr1 = ag1->members; - while (rr1) - { - if (IdenticalResourceRecord(&rr1->resrec, &rr->resrec)) - { - LogInfo("EtcHostsDeleteOldEntries: Old record %s found in new, skipping", ARDisplayString(m, rr)); - found = mDNStrue; - break; - } - rr1 = rr1->next; - } - } - // there is no corresponding record in newhosts for the same name. This means - // we should delete this from the core. - if (!found) - { - if (justCheck) - { - LogInfo("EtcHostsDeleteOldEntries: Record %s not found in new, deleting", ARDisplayString(m, rr)); - return mDNStrue; - } - // if primary is going away, make sure that the rest of the records - // point to the new primary - if (rr == ag->members) - { - AuthRecord *new_primary = rr->next; - AuthRecord *r = new_primary; - while (r) - { - if (r->RRSet == rr) - { - LogInfo("EtcHostsDeleteOldEntries: Updating Resource Record %s to primary", ARDisplayString(m, r)); - r->RRSet = new_primary; - } - else LogMsg("EtcHostsDeleteOldEntries: ERROR!! Resource Record %s not pointing to primary %##s", ARDisplayString(m, r), r->resrec.name); - r = r->next; - } - } - LogInfo("EtcHostsDeleteOldEntries: Deleting %s", ARDisplayString(m, rr)); - mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); - } - } - return mDNSfalse; - } +{ + AuthGroup *ag; + mDNSu32 slot; + AuthRecord *rr, *primary, *rrnext; + for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) + for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next) + for (rr = ag->members; rr; rr = rrnext) + { + mDNSBool found = mDNSfalse; + AuthGroup *ag1; + AuthRecord *rr1; + rrnext = rr->next; + if (rr->RecordCallback != FreeEtcHosts) continue; + ag1 = AuthGroupForRecord(newhosts, slot, &rr->resrec); + if (ag1) + { + primary = rr1 = ag1->members; + while (rr1) + { + if (IdenticalResourceRecord(&rr1->resrec, &rr->resrec)) + { + LogInfo("EtcHostsDeleteOldEntries: Old record %s found in new, skipping", ARDisplayString(m, rr)); + found = mDNStrue; + break; + } + rr1 = rr1->next; + } + } + // there is no corresponding record in newhosts for the same name. This means + // we should delete this from the core. + if (!found) + { + if (justCheck) + { + LogInfo("EtcHostsDeleteOldEntries: Record %s not found in new, deleting", ARDisplayString(m, rr)); + return mDNStrue; + } + // if primary is going away, make sure that the rest of the records + // point to the new primary + if (rr == ag->members) + { + AuthRecord *new_primary = rr->next; + AuthRecord *r = new_primary; + while (r) + { + if (r->RRSet == rr) + { + LogInfo("EtcHostsDeleteOldEntries: Updating Resource Record %s to primary", ARDisplayString(m, r)); + r->RRSet = new_primary; + } + else LogMsg("EtcHostsDeleteOldEntries: ERROR!! Resource Record %s not pointing to primary %##s", ARDisplayString(m, r), r->resrec.name); + r = r->next; + } + } + LogInfo("EtcHostsDeleteOldEntries: Deleting %s", ARDisplayString(m, rr)); + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); + } + } + return mDNSfalse; +} mDNSlocal void UpdateEtcHosts(mDNS *const m, void *context) - { - AuthHash *newhosts = (AuthHash *)context; +{ + AuthHash *newhosts = (AuthHash *)context; - if (!m->mDNS_busy) LogMsg("UpdateEtcHosts: ERROR!! Lock not held"); + if (!m->mDNS_busy) LogMsg("UpdateEtcHosts: ERROR!! Lock not held"); - //Delete old entries from the core if they are not present in the newhosts - EtcHostsDeleteOldEntries(m, newhosts, mDNSfalse); - // Add the new entries to the core if not already present in the core - EtcHostsAddNewEntries(m, newhosts, mDNSfalse); - } + //Delete old entries from the core if they are not present in the newhosts + EtcHostsDeleteOldEntries(m, newhosts, mDNSfalse); + // Add the new entries to the core if not already present in the core + EtcHostsAddNewEntries(m, newhosts, mDNSfalse); +} mDNSlocal void FreeNewHosts(AuthHash *newhosts) - { - mDNSu32 slot; - AuthGroup *ag, *agnext; - AuthRecord *rr, *rrnext; - - for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) - for (ag = newhosts->rrauth_hash[slot]; ag; ag = agnext) - { - agnext = ag->next; - for (rr = ag->members; rr; rr = rrnext) - { - rrnext = rr->next; - freeL("etchosts", rr); - } - freeL("AuthGroups", ag); - } - } +{ + mDNSu32 slot; + AuthGroup *ag, *agnext; + AuthRecord *rr, *rrnext; + + for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) + for (ag = newhosts->rrauth_hash[slot]; ag; ag = agnext) + { + agnext = ag->next; + for (rr = ag->members; rr; rr = rrnext) + { + rrnext = rr->next; + freeL("etchosts", rr); + } + freeL("AuthGroups", ag); + } +} mDNSlocal void mDNSMacOSXUpdateEtcHosts(mDNS *const m) - { - AuthHash newhosts; - - // As we will be modifying the core, we can only have one thread running at - // any point in time. - KQueueLock(m); - - mDNSPlatformMemZero(&newhosts, sizeof(AuthHash)); - - // Get the file desecriptor (will trigger us to start watching for changes) - int fd = mDNSMacOSXGetEtcHostsFD(m); - if (fd != -1) - { - LogInfo("mDNSMacOSXUpdateEtcHosts: Parsing /etc/hosts fd %d", fd); - mDNSMacOSXParseEtcHosts(m, fd, &newhosts); - } - else LogInfo("mDNSMacOSXUpdateEtcHosts: /etc/hosts is not present"); - - // Optimization: Detect whether /etc/hosts changed or not. - // - // 1. Check to see if there are any new entries. We do this by seeing whether any entries in - // newhosts is already registered with core. If we find at least one entry that is not - // registered with core, then it means we have work to do. - // - // 2. Next, we check to see if any of the entries that are registered with core is not present - // in newhosts. If we find at least one entry that is not present, it means we have work to - // do. - // - // Note: We may not have to hold the lock right here as KQueueLock is held which prevents any - // other thread from running. But mDNS_Lock is needed here as we will be traversing the core - // data structure in EtcHostsDeleteOldEntries/NewEntries which might expect the lock to be held - // in the future and this code does not have to change. - mDNS_Lock(m); - // Add the new entries to the core if not already present in the core - if (!EtcHostsAddNewEntries(m, &newhosts, mDNStrue)) - { - // No new entries to add, check to see if we need to delete any old entries from the - // core if they are not present in the newhosts - if (!EtcHostsDeleteOldEntries(m, &newhosts, mDNStrue)) - { - LogInfo("mDNSMacOSXUpdateEtcHosts: No work"); - mDNS_Unlock(m); - KQueueUnlock(m, "/etc/hosts changed"); - FreeNewHosts(&newhosts); - return; - } - } - - // This will flush the cache, stop and start the query so that the queries - // can look at the /etc/hosts again - // - // Notes: - // - // We can't delete and free the records here. We wait for the mDNSCoreRestartAddressQueries to - // deliver RMV events. It has to be done in a deferred way because we can't deliver RMV - // events for local records *before* the RMV events for cache records. mDNSCoreRestartAddressQueries - // delivers these events in the right order and then calls us back to delete them. - // - // Similarly, we do a deferred Registration of the record because mDNSCoreRestartAddressQueries - // is a common function that looks at all local auth records and delivers a RMV including - // the records that we might add here. If we deliver a ADD here, it will get a RMV and then when - // the query is restarted, it will get another ADD. To avoid this (ADD-RMV-ADD), we defer registering - // the record until the RMVs are delivered in mDNSCoreRestartAddressQueries after which UpdateEtcHosts - // is called back where we do the Registration of the record. This results in RMV followed by ADD which +{ + AuthHash newhosts; + + // As we will be modifying the core, we can only have one thread running at + // any point in time. + KQueueLock(m); + + mDNSPlatformMemZero(&newhosts, sizeof(AuthHash)); + + // Get the file desecriptor (will trigger us to start watching for changes) + int fd = mDNSMacOSXGetEtcHostsFD(m); + if (fd != -1) + { + LogInfo("mDNSMacOSXUpdateEtcHosts: Parsing /etc/hosts fd %d", fd); + mDNSMacOSXParseEtcHosts(m, fd, &newhosts); + } + else LogInfo("mDNSMacOSXUpdateEtcHosts: /etc/hosts is not present"); + + // Optimization: Detect whether /etc/hosts changed or not. + // + // 1. Check to see if there are any new entries. We do this by seeing whether any entries in + // newhosts is already registered with core. If we find at least one entry that is not + // registered with core, then it means we have work to do. + // + // 2. Next, we check to see if any of the entries that are registered with core is not present + // in newhosts. If we find at least one entry that is not present, it means we have work to + // do. + // + // Note: We may not have to hold the lock right here as KQueueLock is held which prevents any + // other thread from running. But mDNS_Lock is needed here as we will be traversing the core + // data structure in EtcHostsDeleteOldEntries/NewEntries which might expect the lock to be held + // in the future and this code does not have to change. + mDNS_Lock(m); + // Add the new entries to the core if not already present in the core + if (!EtcHostsAddNewEntries(m, &newhosts, mDNStrue)) + { + // No new entries to add, check to see if we need to delete any old entries from the + // core if they are not present in the newhosts + if (!EtcHostsDeleteOldEntries(m, &newhosts, mDNStrue)) + { + LogInfo("mDNSMacOSXUpdateEtcHosts: No work"); + mDNS_Unlock(m); + KQueueUnlock(m, "/etc/hosts changed"); + FreeNewHosts(&newhosts); + return; + } + } + + // This will flush the cache, stop and start the query so that the queries + // can look at the /etc/hosts again + // + // Notes: + // + // We can't delete and free the records here. We wait for the mDNSCoreRestartAddressQueries to + // deliver RMV events. It has to be done in a deferred way because we can't deliver RMV + // events for local records *before* the RMV events for cache records. mDNSCoreRestartAddressQueries + // delivers these events in the right order and then calls us back to delete them. + // + // Similarly, we do a deferred Registration of the record because mDNSCoreRestartAddressQueries + // is a common function that looks at all local auth records and delivers a RMV including + // the records that we might add here. If we deliver a ADD here, it will get a RMV and then when + // the query is restarted, it will get another ADD. To avoid this (ADD-RMV-ADD), we defer registering + // the record until the RMVs are delivered in mDNSCoreRestartAddressQueries after which UpdateEtcHosts + // is called back where we do the Registration of the record. This results in RMV followed by ADD which // looks normal. - mDNSCoreRestartAddressQueries(m, mDNSfalse, FlushAllCacheRecords, UpdateEtcHosts, &newhosts); - mDNS_Unlock(m); + mDNSCoreRestartAddressQueries(m, mDNSfalse, FlushAllCacheRecords, UpdateEtcHosts, &newhosts); + mDNS_Unlock(m); - KQueueUnlock(m, "/etc/hosts changed"); - FreeNewHosts(&newhosts); - } + KQueueUnlock(m, "/etc/hosts changed"); + FreeNewHosts(&newhosts); +} #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -8372,73 +8692,73 @@ CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey; // Major version 9 is 10.5.x (Leopard) // Major version 10 is 10.6.x (SnowLeopard) mDNSexport void mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring) - { - int major = 0, minor = 0; - char letter = 0, prodname[256]="", prodvers[256]="", buildver[256]=""; - CFDictionaryRef vers = _CFCopySystemVersionDictionary(); - if (vers) - { - CFStringRef cfprodname = CFDictionaryGetValue(vers, _kCFSystemVersionProductNameKey); - CFStringRef cfprodvers = CFDictionaryGetValue(vers, _kCFSystemVersionProductVersionKey); - CFStringRef cfbuildver = CFDictionaryGetValue(vers, _kCFSystemVersionBuildVersionKey); - if (cfprodname) CFStringGetCString(cfprodname, prodname, sizeof(prodname), kCFStringEncodingUTF8); - if (cfprodvers) CFStringGetCString(cfprodvers, prodvers, sizeof(prodvers), kCFStringEncodingUTF8); - if (cfbuildver && CFStringGetCString(cfbuildver, buildver, sizeof(buildver), kCFStringEncodingUTF8)) - sscanf(buildver, "%d%c%d", &major, &letter, &minor); - CFRelease(vers); - } - if (!major) { major=8; LogMsg("Note: No Major Build Version number found; assuming 8"); } - if (HINFO_SWstring) mDNS_snprintf(HINFO_SWstring, 256, "%s %s (%s), %s", prodname, prodvers, buildver, STRINGIFY(mDNSResponderVersion)); - //LogMsg("%s %s (%s), %d %c %d", prodname, prodvers, buildver, major, letter, minor); - - // If product name is "Mac OS X" (or similar) we set OSXVers, else we set iOSVers; - if ((prodname[0] & 0xDF) == 'M') OSXVers = major; else iOSVers = major; - } +{ + int major = 0, minor = 0; + char letter = 0, prodname[256]="", prodvers[256]="", buildver[256]=""; + CFDictionaryRef vers = _CFCopySystemVersionDictionary(); + if (vers) + { + CFStringRef cfprodname = CFDictionaryGetValue(vers, _kCFSystemVersionProductNameKey); + CFStringRef cfprodvers = CFDictionaryGetValue(vers, _kCFSystemVersionProductVersionKey); + CFStringRef cfbuildver = CFDictionaryGetValue(vers, _kCFSystemVersionBuildVersionKey); + if (cfprodname) CFStringGetCString(cfprodname, prodname, sizeof(prodname), kCFStringEncodingUTF8); + if (cfprodvers) CFStringGetCString(cfprodvers, prodvers, sizeof(prodvers), kCFStringEncodingUTF8); + if (cfbuildver && CFStringGetCString(cfbuildver, buildver, sizeof(buildver), kCFStringEncodingUTF8)) + sscanf(buildver, "%d%c%d", &major, &letter, &minor); + CFRelease(vers); + } + if (!major) { major=8; LogMsg("Note: No Major Build Version number found; assuming 8"); } + if (HINFO_SWstring) mDNS_snprintf(HINFO_SWstring, 256, "%s %s (%s), %s", prodname, prodvers, buildver, STRINGIFY(mDNSResponderVersion)); + //LogMsg("%s %s (%s), %d %c %d", prodname, prodvers, buildver, major, letter, minor); + + // If product name is "Mac OS X" (or similar) we set OSXVers, else we set iOSVers; + if ((prodname[0] & 0xDF) == 'M') OSXVers = major;else iOSVers = major; +} // 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_CanReceiveUnicast(void) - { - int err = -1; - int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (s < 3) - LogMsg("mDNSPlatformInit_CanReceiveUnicast: socket error %d errno %d (%s)", s, errno, strerror(errno)); - else - { - 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) LogMsg("No unicast UDP responses"); - else debugf("Unicast UDP responses okay"); - return(err == 0); - } +{ + int err = -1; + int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s < 3) + LogMsg("mDNSPlatformInit_CanReceiveUnicast: socket error %d errno %d (%s)", s, errno, strerror(errno)); + else + { + 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) LogMsg("No unicast UDP responses"); + else debugf("Unicast UDP responses okay"); + return(err == 0); +} mDNSlocal void CreatePTRRecord(mDNS *const m, const domainname *domain) - { - AuthRecord *rr; - const domainname *pname = (domainname *)"\x9""localhost"; +{ + AuthRecord *rr; + const domainname *pname = (domainname *)"\x9" "localhost"; - rr= mallocL("localhosts", sizeof(*rr)); - if (rr == NULL) return; - mDNSPlatformMemZero(rr, sizeof(*rr)); + rr= mallocL("localhosts", sizeof(*rr)); + if (rr == NULL) return; + mDNSPlatformMemZero(rr, sizeof(*rr)); - mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, kHostNameTTL, kDNSRecordTypeKnownUnique, AuthRecordLocalOnly, mDNSNULL, mDNSNULL); - AssignDomainName(&rr->namestorage, domain); + mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, kHostNameTTL, kDNSRecordTypeKnownUnique, AuthRecordLocalOnly, mDNSNULL, mDNSNULL); + AssignDomainName(&rr->namestorage, domain); - rr->resrec.rdlength = DomainNameLength(pname); - rr->resrec.rdata->u.name.c[0] = 0; - AssignDomainName(&rr->resrec.rdata->u.name, pname); + rr->resrec.rdlength = DomainNameLength(pname); + rr->resrec.rdata->u.name.c[0] = 0; + AssignDomainName(&rr->resrec.rdata->u.name, pname); - rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); - SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us - mDNS_Register(m, rr); - } + rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); + SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us + mDNS_Register(m, rr); +} // Setup PTR records for 127.0.0.1 and ::1. This helps answering them locally rather than relying // on the external DNS server to answer this. Sometimes, the DNS servers don't respond in a timely @@ -8448,38 +8768,38 @@ mDNSlocal void CreatePTRRecord(mDNS *const m, const domainname *domain) // Note: We could have set this up while parsing the entries in /etc/hosts. But this is kept separate // intentionally to avoid adding to the complexity of code handling /etc/hosts. mDNSlocal void SetupLocalHostRecords(mDNS *const m) - { - char buffer[MAX_REVERSE_MAPPING_NAME]; - domainname name; - int i; - struct in6_addr addr; - mDNSu8 *ptr = addr.__u6_addr.__u6_addr8; - - if (inet_pton(AF_INET, "127.0.0.1", &addr) == 1) - { - mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", - ptr[3], ptr[2], ptr[1], ptr[0]); - MakeDomainNameFromDNSNameString(&name, buffer); - CreatePTRRecord(m, &name); - } - else LogMsg("SetupLocalHostRecords: ERROR!! inet_pton AF_INET failed"); - - if (inet_pton(AF_INET6, "::1", &addr) == 1) - { - for (i = 0; i < 16; i++) - { - static const char hexValues[] = "0123456789ABCDEF"; - buffer[i * 4 ] = hexValues[ptr[15 - i] & 0x0F]; - buffer[i * 4 + 1] = '.'; - buffer[i * 4 + 2] = hexValues[ptr[15 - i] >> 4]; - buffer[i * 4 + 3] = '.'; - } - mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa."); - MakeDomainNameFromDNSNameString(&name, buffer); - CreatePTRRecord(m, &name); - } - else LogMsg("SetupLocalHostRecords: ERROR!! inet_pton AF_INET6 failed"); - } +{ + char buffer[MAX_REVERSE_MAPPING_NAME]; + domainname name; + int i; + struct in6_addr addr; + mDNSu8 *ptr = addr.__u6_addr.__u6_addr8; + + if (inet_pton(AF_INET, "127.0.0.1", &addr) == 1) + { + mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", + ptr[3], ptr[2], ptr[1], ptr[0]); + MakeDomainNameFromDNSNameString(&name, buffer); + CreatePTRRecord(m, &name); + } + else LogMsg("SetupLocalHostRecords: ERROR!! inet_pton AF_INET failed"); + + if (inet_pton(AF_INET6, "::1", &addr) == 1) + { + for (i = 0; i < 16; i++) + { + static const char hexValues[] = "0123456789ABCDEF"; + buffer[i * 4 ] = hexValues[ptr[15 - i] & 0x0F]; + buffer[i * 4 + 1] = '.'; + buffer[i * 4 + 2] = hexValues[ptr[15 - i] >> 4]; + buffer[i * 4 + 3] = '.'; + } + mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa."); + MakeDomainNameFromDNSNameString(&name, buffer); + CreatePTRRecord(m, &name); + } + else LogMsg("SetupLocalHostRecords: ERROR!! inet_pton AF_INET6 failed"); +} // Construction of Default Browse domain list (i.e. when clients pass NULL) is as follows: // 1) query for b._dns-sd._udp.local on LocalOnly interface @@ -8492,343 +8812,336 @@ mDNSlocal void SetupLocalHostRecords(mDNS *const m) // (!!!KRS may add outgoing interface in addition) mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) - { - mStatus err; - m->p->CFRunLoop = CFRunLoopGetCurrent(); - - char HINFO_SWstring[256] = ""; - mDNSMacOSXSystemBuildNumber(HINFO_SWstring); - - // In 10.4, mDNSResponder is launched very early in the boot process, while other subsystems are still in the process of starting up. - // If we can't read the user's preferences, then we sleep a bit and try again, for up to five seconds before we give up. - int i; - for (i=0; i<100; i++) - { - domainlabel testlabel; - testlabel.c[0] = 0; - GetUserSpecifiedLocalHostName(&testlabel); - if (testlabel.c[0]) break; - usleep(50000); - } - - m->hostlabel.c[0] = 0; - - int get_model[2] = { CTL_HW, HW_MODEL }; - size_t len_model = sizeof(HINFO_HWstring_buffer); - - // Normal Apple model names are of the form "iPhone2,1", and - // internal code names are strings containing no commas, e.g. "N88AP". - // We used to ignore internal code names, but Apple now uses these internal code names - // even in released shipping products, so we no longer ignore strings containing no commas. +{ + mStatus err; + m->p->CFRunLoop = CFRunLoopGetCurrent(); + + char HINFO_SWstring[256] = ""; + mDNSMacOSXSystemBuildNumber(HINFO_SWstring); + + // In 10.4, mDNSResponder is launched very early in the boot process, while other subsystems are still in the process of starting up. + // If we can't read the user's preferences, then we sleep a bit and try again, for up to five seconds before we give up. + int i; + for (i=0; i<100; i++) + { + domainlabel testlabel; + testlabel.c[0] = 0; + GetUserSpecifiedLocalHostName(&testlabel); + if (testlabel.c[0]) break; + usleep(50000); + } + + m->hostlabel.c[0] = 0; + + int get_model[2] = { CTL_HW, HW_MODEL }; + size_t len_model = sizeof(HINFO_HWstring_buffer); + + // Normal Apple model names are of the form "iPhone2,1", and + // internal code names are strings containing no commas, e.g. "N88AP". + // We used to ignore internal code names, but Apple now uses these internal code names + // even in released shipping products, so we no longer ignore strings containing no commas. // if (sysctl(get_model, 2, HINFO_HWstring_buffer, &len_model, NULL, 0) == 0 && strchr(HINFO_HWstring_buffer, ',')) - if (sysctl(get_model, 2, HINFO_HWstring_buffer, &len_model, NULL, 0) == 0) - HINFO_HWstring = HINFO_HWstring_buffer; - - // For names of the form "iPhone2,1" we use "iPhone" as the prefix for automatic name generation. - // For names of the form "N88AP" containg no comma, we use the entire string. - HINFO_HWstring_prefixlen = strchr(HINFO_HWstring_buffer, ',') ? strcspn(HINFO_HWstring, "0123456789") : strlen(HINFO_HWstring); - - if (OSXVers && OSXVers <= OSXVers_10_6_SnowLeopard) m->KnownBugs |= mDNS_KnownBug_LimitedIPv6; - if (OSXVers && OSXVers >= OSXVers_10_6_SnowLeopard) m->KnownBugs |= mDNS_KnownBug_LossySyslog; - if (mDNSPlatformInit_CanReceiveUnicast()) m->CanReceiveUnicastOn5353 = mDNStrue; - - mDNSu32 hlen = mDNSPlatformStrLen(HINFO_HWstring); - mDNSu32 slen = mDNSPlatformStrLen(HINFO_SWstring); - if (hlen + slen < 254) - { - m->HIHardware.c[0] = hlen; - m->HISoftware.c[0] = slen; - mDNSPlatformMemCopy(&m->HIHardware.c[1], HINFO_HWstring, hlen); - mDNSPlatformMemCopy(&m->HISoftware.c[1], HINFO_SWstring, slen); - } - - m->p->permanentsockets.port = MulticastDNSPort; - m->p->permanentsockets.m = m; - m->p->permanentsockets.sktv4 = -1; - m->p->permanentsockets.kqsv4.KQcallback = myKQSocketCallBack; - m->p->permanentsockets.kqsv4.KQcontext = &m->p->permanentsockets; - m->p->permanentsockets.kqsv4.KQtask = "UDP packet reception"; + if (sysctl(get_model, 2, HINFO_HWstring_buffer, &len_model, NULL, 0) == 0) + HINFO_HWstring = HINFO_HWstring_buffer; + + // For names of the form "iPhone2,1" we use "iPhone" as the prefix for automatic name generation. + // For names of the form "N88AP" containg no comma, we use the entire string. + HINFO_HWstring_prefixlen = strchr(HINFO_HWstring_buffer, ',') ? strcspn(HINFO_HWstring, "0123456789") : strlen(HINFO_HWstring); + + if (OSXVers && OSXVers <= OSXVers_10_6_SnowLeopard) m->KnownBugs |= mDNS_KnownBug_LimitedIPv6; + if (OSXVers && OSXVers >= OSXVers_10_6_SnowLeopard) m->KnownBugs |= mDNS_KnownBug_LossySyslog; + if (mDNSPlatformInit_CanReceiveUnicast()) m->CanReceiveUnicastOn5353 = mDNStrue; + + mDNSu32 hlen = mDNSPlatformStrLen(HINFO_HWstring); + mDNSu32 slen = mDNSPlatformStrLen(HINFO_SWstring); + if (hlen + slen < 254) + { + m->HIHardware.c[0] = hlen; + m->HISoftware.c[0] = slen; + mDNSPlatformMemCopy(&m->HIHardware.c[1], HINFO_HWstring, hlen); + mDNSPlatformMemCopy(&m->HISoftware.c[1], HINFO_SWstring, slen); + } + + m->p->permanentsockets.port = MulticastDNSPort; + m->p->permanentsockets.m = m; + m->p->permanentsockets.sktv4 = -1; + m->p->permanentsockets.kqsv4.KQcallback = myKQSocketCallBack; + m->p->permanentsockets.kqsv4.KQcontext = &m->p->permanentsockets; + m->p->permanentsockets.kqsv4.KQtask = "UDP packet reception"; #ifndef NO_IPV6 - m->p->permanentsockets.sktv6 = -1; - m->p->permanentsockets.kqsv6.KQcallback = myKQSocketCallBack; - m->p->permanentsockets.kqsv6.KQcontext = &m->p->permanentsockets; - m->p->permanentsockets.kqsv6.KQtask = "UDP packet reception"; + m->p->permanentsockets.sktv6 = -1; + m->p->permanentsockets.kqsv6.KQcallback = myKQSocketCallBack; + m->p->permanentsockets.kqsv6.KQcontext = &m->p->permanentsockets; + m->p->permanentsockets.kqsv6.KQtask = "UDP packet reception"; #endif - err = SetupSocket(&m->p->permanentsockets, MulticastDNSPort, AF_INET, mDNSNULL); + err = SetupSocket(&m->p->permanentsockets, MulticastDNSPort, AF_INET, mDNSNULL); #ifndef NO_IPV6 - err = SetupSocket(&m->p->permanentsockets, MulticastDNSPort, AF_INET6, mDNSNULL); + err = SetupSocket(&m->p->permanentsockets, MulticastDNSPort, AF_INET6, mDNSNULL); #endif - struct sockaddr_in s4; - socklen_t n4 = sizeof(s4); - if (getsockname(m->p->permanentsockets.sktv4, (struct sockaddr *)&s4, &n4) < 0) LogMsg("getsockname v4 error %d (%s)", errno, strerror(errno)); - else m->UnicastPort4.NotAnInteger = s4.sin_port; + struct sockaddr_in s4; + socklen_t n4 = sizeof(s4); + if (getsockname(m->p->permanentsockets.sktv4, (struct sockaddr *)&s4, &n4) < 0) LogMsg("getsockname v4 error %d (%s)", errno, strerror(errno)); + else m->UnicastPort4.NotAnInteger = s4.sin_port; #ifndef NO_IPV6 - if (m->p->permanentsockets.sktv6 >= 0) - { - struct sockaddr_in6 s6; - socklen_t n6 = sizeof(s6); - if (getsockname(m->p->permanentsockets.sktv6, (struct sockaddr *)&s6, &n6) < 0) LogMsg("getsockname v6 error %d (%s)", errno, strerror(errno)); - else m->UnicastPort6.NotAnInteger = s6.sin6_port; - } + if (m->p->permanentsockets.sktv6 >= 0) + { + struct sockaddr_in6 s6; + socklen_t n6 = sizeof(s6); + if (getsockname(m->p->permanentsockets.sktv6, (struct sockaddr *)&s6, &n6) < 0) LogMsg("getsockname v6 error %d (%s)", errno, strerror(errno)); + else m->UnicastPort6.NotAnInteger = s6.sin6_port; + } #endif - m->p->InterfaceList = mDNSNULL; - m->p->userhostlabel.c[0] = 0; - m->p->usernicelabel.c[0] = 0; - m->p->prevoldnicelabel.c[0] = 0; - m->p->prevnewnicelabel.c[0] = 0; - m->p->prevoldhostlabel.c[0] = 0; - m->p->prevnewhostlabel.c[0] = 0; - m->p->NotifyUser = 0; - m->p->KeyChainTimer = 0; - m->p->WakeAtUTC = 0; - m->p->RequestReSleep = 0; - + m->p->InterfaceList = mDNSNULL; + m->p->userhostlabel.c[0] = 0; + m->p->usernicelabel.c[0] = 0; + m->p->prevoldnicelabel.c[0] = 0; + m->p->prevnewnicelabel.c[0] = 0; + m->p->prevoldhostlabel.c[0] = 0; + m->p->prevnewhostlabel.c[0] = 0; + m->p->NotifyUser = 0; + m->p->KeyChainTimer = 0; + m->p->WakeAtUTC = 0; + m->p->RequestReSleep = 0; #if APPLE_OSX_mDNSResponder - uuid_generate(m->asl_uuid); + uuid_generate(m->asl_uuid); #endif - m->AutoTunnelHostAddr.b[0] = 0; // Zero out AutoTunnelHostAddr so UpdateInterfaceList() know it has to set it up - m->AutoTunnelRelayAddrIn = zerov6Addr; - m->AutoTunnelRelayAddrOut = zerov6Addr; + m->AutoTunnelRelayAddr = zerov6Addr; - NetworkChangedKey_IPv4 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4); - NetworkChangedKey_IPv6 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6); - NetworkChangedKey_Hostnames = SCDynamicStoreKeyCreateHostNames(NULL); - NetworkChangedKey_Computername = SCDynamicStoreKeyCreateComputerName(NULL); - NetworkChangedKey_DNS = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetDNS); - if (!NetworkChangedKey_IPv4 || !NetworkChangedKey_IPv6 || !NetworkChangedKey_Hostnames || !NetworkChangedKey_Computername || !NetworkChangedKey_DNS) - { LogMsg("SCDynamicStore string setup failed"); return(mStatus_NoMemoryErr); } + NetworkChangedKey_IPv4 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4); + NetworkChangedKey_IPv6 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6); + NetworkChangedKey_Hostnames = SCDynamicStoreKeyCreateHostNames(NULL); + NetworkChangedKey_Computername = SCDynamicStoreKeyCreateComputerName(NULL); + NetworkChangedKey_DNS = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetDNS); + if (!NetworkChangedKey_IPv4 || !NetworkChangedKey_IPv6 || !NetworkChangedKey_Hostnames || !NetworkChangedKey_Computername || !NetworkChangedKey_DNS) + { LogMsg("SCDynamicStore string setup failed"); return(mStatus_NoMemoryErr); } - err = WatchForNetworkChanges(m); - if (err) { LogMsg("mDNSPlatformInit_setup: WatchForNetworkChanges failed %d", err); return(err); } + err = WatchForNetworkChanges(m); + if (err) { LogMsg("mDNSPlatformInit_setup: WatchForNetworkChanges failed %d", err); return(err); } #if 0 // - err = WatchForPMChanges(m); - if (err) { LogMsg("mDNSPlatformInit_setup: WatchForPMChanges failed %d", err); return(err); } + err = WatchForPMChanges(m); + if (err) { LogMsg("mDNSPlatformInit_setup: WatchForPMChanges failed %d", err); return(err); } #endif - err = WatchForSysEvents(m); - if (err) { LogMsg("mDNSPlatformInit_setup: WatchForSysEvents failed %d", err); return(err); } + err = WatchForSysEvents(m); + if (err) { LogMsg("mDNSPlatformInit_setup: WatchForSysEvents failed %d", err); return(err); } - mDNSs32 utc = mDNSPlatformUTC(); - m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess(); - UpdateInterfaceList(m, utc); - SetupActiveInterfaces(m, utc); + mDNSs32 utc = mDNSPlatformUTC(); + m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess(); + UpdateInterfaceList(m, utc); + SetupActiveInterfaces(m, utc); - // Explicitly ensure that our Keychain operations utilize the system domain. + // Explicitly ensure that our Keychain operations utilize the system domain. #ifndef NO_SECURITYFRAMEWORK - SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem); + SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem); #endif - mDNS_Lock(m); - SetDomainSecrets(m); - SetLocalDomains(); - mDNS_Unlock(m); + mDNS_Lock(m); + SetDomainSecrets(m); + SetLocalDomains(); + mDNS_Unlock(m); #ifndef NO_SECURITYFRAMEWORK - err = SecKeychainAddCallback(KeychainChanged, kSecAddEventMask|kSecDeleteEventMask|kSecUpdateEventMask, m); - if (err) { LogMsg("mDNSPlatformInit_setup: SecKeychainAddCallback failed %d", err); return(err); } + err = SecKeychainAddCallback(KeychainChanged, kSecAddEventMask|kSecDeleteEventMask|kSecUpdateEventMask, m); + if (err) { LogMsg("mDNSPlatformInit_setup: SecKeychainAddCallback failed %d", err); return(err); } #endif #if !defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements) || TARGET_OS_EMBEDDED - LogMsg("Note: Compiled without SnowLeopard Fine-Grained Power Management support"); + LogMsg("Note: Compiled without SnowLeopard Fine-Grained Power Management support"); +#else + IOPMConnection c; + IOReturn iopmerr = IOPMConnectionCreate(CFSTR("mDNSResponder"), kIOPMSystemPowerStateCapabilityCPU, &c); + if (iopmerr) LogMsg("IOPMConnectionCreate failed %d", iopmerr); + else + { + iopmerr = IOPMConnectionSetNotification(c, m, SnowLeopardPowerChanged); + if (iopmerr) LogMsg("IOPMConnectionSetNotification failed %d", iopmerr); + else + { +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + IOPMConnectionSetDispatchQueue(c, dispatch_get_main_queue()); + LogInfo("IOPMConnectionSetDispatchQueue is now running"); #else - IOPMConnection c; - IOReturn iopmerr = IOPMConnectionCreate(CFSTR("mDNSResponder"), kIOPMSystemPowerStateCapabilityCPU, &c); - if (iopmerr) LogMsg("IOPMConnectionCreate failed %d", iopmerr); - else - { - iopmerr = IOPMConnectionSetNotification(c, m, SnowLeopardPowerChanged); - if (iopmerr) LogMsg("IOPMConnectionSetNotification failed %d", iopmerr); - else - { - iopmerr = IOPMConnectionScheduleWithRunLoop(c, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); - if (iopmerr) LogMsg("IOPMConnectionScheduleWithRunLoop failed %d", iopmerr); - } - } - m->p->IOPMConnection = iopmerr ? mDNSNULL : c; - if (iopmerr) // If IOPMConnectionCreate unavailable or failed, proceed with old-style power notification code below + iopmerr = IOPMConnectionScheduleWithRunLoop(c, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + if (iopmerr) LogMsg("IOPMConnectionScheduleWithRunLoop failed %d", iopmerr); + LogInfo("IOPMConnectionScheduleWithRunLoop is now running"); +#endif /* MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM */ + } + } + m->p->IOPMConnection = iopmerr ? mDNSNULL : c; + if (iopmerr) // If IOPMConnectionCreate unavailable or failed, proceed with old-style power notification code below #endif // kIOPMAcknowledgmentOptionSystemCapabilityRequirements - { - m->p->PowerConnection = IORegisterForSystemPower(m, &m->p->PowerPortRef, PowerChanged, &m->p->PowerNotifier); - if (!m->p->PowerConnection) { LogMsg("mDNSPlatformInit_setup: IORegisterForSystemPower failed"); return(-1); } - else - { + { + m->p->PowerConnection = IORegisterForSystemPower(m, &m->p->PowerPortRef, PowerChanged, &m->p->PowerNotifier); + if (!m->p->PowerConnection) { LogMsg("mDNSPlatformInit_setup: IORegisterForSystemPower failed"); return(-1); } + else + { #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM - IONotificationPortSetDispatchQueue(m->p->PowerPortRef, dispatch_get_main_queue()); + IONotificationPortSetDispatchQueue(m->p->PowerPortRef, dispatch_get_main_queue()); #else - CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(m->p->PowerPortRef), kCFRunLoopDefaultMode); + CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(m->p->PowerPortRef), kCFRunLoopDefaultMode); #endif /* MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM */ - } - } + } + } #if APPLE_OSX_mDNSResponder - // Note: We use SPMetricPortability > 35 to indicate a laptop of some kind - // SPMetricPortability <= 35 means nominally a non-portable machine (i.e. Mac mini or better) - // Apple TVs, AirPort base stations, and Time Capsules do not actually weigh 3kg, but we assign them - // higher 'nominal' masses to indicate they should be treated as being relatively less portable than a laptop - if (!strncasecmp(HINFO_HWstring, "Xserve", 6)) { SPMetricPortability = 25 /* 30kg */; SPMetricMarginalPower = 84 /* 250W */; SPMetricTotalPower = 85 /* 300W */; } - else if (!strncasecmp(HINFO_HWstring, "RackMac", 7)) { SPMetricPortability = 25 /* 30kg */; SPMetricMarginalPower = 84 /* 250W */; SPMetricTotalPower = 85 /* 300W */; } - else if (!strncasecmp(HINFO_HWstring, "MacPro", 6)) { SPMetricPortability = 27 /* 20kg */; SPMetricMarginalPower = 84 /* 250W */; SPMetricTotalPower = 85 /* 300W */; } - else if (!strncasecmp(HINFO_HWstring, "PowerMac", 8)) { SPMetricPortability = 27 /* 20kg */; SPMetricMarginalPower = 82 /* 160W */; SPMetricTotalPower = 83 /* 200W */; } - else if (!strncasecmp(HINFO_HWstring, "iMac", 4)) { SPMetricPortability = 30 /* 10kg */; SPMetricMarginalPower = 77 /* 50W */; SPMetricTotalPower = 78 /* 60W */; } - else if (!strncasecmp(HINFO_HWstring, "Macmini", 7)) { SPMetricPortability = 33 /* 5kg */; SPMetricMarginalPower = 73 /* 20W */; SPMetricTotalPower = 74 /* 25W */; } - else if (!strncasecmp(HINFO_HWstring, "TimeCapsule", 11)) { SPMetricPortability = 34 /* 4kg */; SPMetricMarginalPower = 10 /* ~0W */; SPMetricTotalPower = 70 /* 13W */; } - else if (!strncasecmp(HINFO_HWstring, "AirPort", 7)) { SPMetricPortability = 35 /* 3kg */; SPMetricMarginalPower = 10 /* ~0W */; SPMetricTotalPower = 70 /* 12W */; } - else if (!strncasecmp(HINFO_HWstring, "AppleTV", 7)) { SPMetricPortability = 35 /* 3kg */; SPMetricMarginalPower = 10 /* ~0W */; SPMetricTotalPower = 73 /* 20W */; } - else if (!strncasecmp(HINFO_HWstring, "K66AP", 5)) { SPMetricPortability = 35 /* 3kg */; SPMetricMarginalPower = 60 /* 1W */; SPMetricTotalPower = 63 /* 2W */; } - else if (!strncasecmp(HINFO_HWstring, "MacBook", 7)) { SPMetricPortability = 37 /* 2kg */; SPMetricMarginalPower = 71 /* 13W */; SPMetricTotalPower = 72 /* 15W */; } - else if (!strncasecmp(HINFO_HWstring, "PowerBook", 9)) { SPMetricPortability = 37 /* 2kg */; SPMetricMarginalPower = 71 /* 13W */; SPMetricTotalPower = 72 /* 15W */; } - LogSPS("HW_MODEL: %.*s (%s) Portability %d Marginal Power %d Total Power %d", - HINFO_HWstring_prefixlen, HINFO_HWstring, HINFO_HWstring, SPMetricPortability, SPMetricMarginalPower, SPMetricTotalPower); - - err = WatchForInternetSharingChanges(m); - if (err) { LogMsg("WatchForInternetSharingChanges failed %d", err); return(err); } - - m->p->ConndBTMMDict = mDNSNULL; + // Note: We use SPMetricPortability > 35 to indicate a laptop of some kind + // SPMetricPortability <= 35 means nominally a non-portable machine (i.e. Mac mini or better) + // Apple TVs, AirPort base stations, and Time Capsules do not actually weigh 3kg, but we assign them + // higher 'nominal' masses to indicate they should be treated as being relatively less portable than a laptop + if (!strncasecmp(HINFO_HWstring, "Xserve", 6)) { SPMetricPortability = 25 /* 30kg */; SPMetricMarginalPower = 84 /* 250W */; SPMetricTotalPower = 85 /* 300W */; } + else if (!strncasecmp(HINFO_HWstring, "RackMac", 7)) { SPMetricPortability = 25 /* 30kg */; SPMetricMarginalPower = 84 /* 250W */; SPMetricTotalPower = 85 /* 300W */; } + else if (!strncasecmp(HINFO_HWstring, "MacPro", 6)) { SPMetricPortability = 27 /* 20kg */; SPMetricMarginalPower = 84 /* 250W */; SPMetricTotalPower = 85 /* 300W */; } + else if (!strncasecmp(HINFO_HWstring, "PowerMac", 8)) { SPMetricPortability = 27 /* 20kg */; SPMetricMarginalPower = 82 /* 160W */; SPMetricTotalPower = 83 /* 200W */; } + else if (!strncasecmp(HINFO_HWstring, "iMac", 4)) { SPMetricPortability = 30 /* 10kg */; SPMetricMarginalPower = 77 /* 50W */; SPMetricTotalPower = 78 /* 60W */; } + else if (!strncasecmp(HINFO_HWstring, "Macmini", 7)) { SPMetricPortability = 33 /* 5kg */; SPMetricMarginalPower = 73 /* 20W */; SPMetricTotalPower = 74 /* 25W */; } + else if (!strncasecmp(HINFO_HWstring, "TimeCapsule", 11)) { SPMetricPortability = 34 /* 4kg */; SPMetricMarginalPower = 10 /* ~0W */; SPMetricTotalPower = 70 /* 13W */; } + else if (!strncasecmp(HINFO_HWstring, "AirPort", 7)) { SPMetricPortability = 35 /* 3kg */; SPMetricMarginalPower = 10 /* ~0W */; SPMetricTotalPower = 70 /* 12W */; } + else if (!strncasecmp(HINFO_HWstring, "AppleTV", 7)) { SPMetricPortability = 35 /* 3kg */; SPMetricMarginalPower = 10 /* ~0W */; SPMetricTotalPower = 73 /* 20W */; } + else if (!strncasecmp(HINFO_HWstring, "K66AP", 5)) { SPMetricPortability = 35 /* 3kg */; SPMetricMarginalPower = 60 /* 1W */; SPMetricTotalPower = 63 /* 2W */; } + else if (!strncasecmp(HINFO_HWstring, "J33AP", 5)) { SPMetricPortability = 35 /* 3kg */; SPMetricMarginalPower = 60 /* 1W */; SPMetricTotalPower = 63 /* 2W */; } + else if (!strncasecmp(HINFO_HWstring, "MacBook", 7)) { SPMetricPortability = 37 /* 2kg */; SPMetricMarginalPower = 71 /* 13W */; SPMetricTotalPower = 72 /* 15W */; } + else if (!strncasecmp(HINFO_HWstring, "PowerBook", 9)) { SPMetricPortability = 37 /* 2kg */; SPMetricMarginalPower = 71 /* 13W */; SPMetricTotalPower = 72 /* 15W */; } + LogSPS("HW_MODEL: %.*s (%s) Portability %d Marginal Power %d Total Power %d Features %d", + HINFO_HWstring_prefixlen, HINFO_HWstring, HINFO_HWstring, SPMetricPortability, SPMetricMarginalPower, SPMetricTotalPower, SPMetricFeatures); #endif // APPLE_OSX_mDNSResponder #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM - // Currently this is not defined. SSL code will eventually fix this. If it becomes - // critical, we will define this to workaround the bug in SSL. + // Currently this is not defined. SSL code will eventually fix this. If it becomes + // critical, we will define this to workaround the bug in SSL. #ifdef __SSL_NEEDS_SERIALIZATION__ - SSLqueue = dispatch_queue_create("com.apple.mDNSResponder.SSLQueue", NULL); + SSLqueue = dispatch_queue_create("com.apple.mDNSResponder.SSLQueue", NULL); #else - SSLqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + SSLqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); #endif - if (SSLqueue == mDNSNULL) LogMsg("dispatch_queue_create: SSL queue NULL"); + if (SSLqueue == mDNSNULL) LogMsg("dispatch_queue_create: SSL queue NULL"); #endif - mDNSMacOSXUpdateEtcHosts(m); - SetupLocalHostRecords(m); - return(mStatus_NoError); - } + mDNSMacOSXUpdateEtcHosts(m); + SetupLocalHostRecords(m); + m->TrustAnchors = mDNSNULL; + return(mStatus_NoError); +} mDNSexport mStatus mDNSPlatformInit(mDNS *const m) - { +{ #if MDNS_NO_DNSINFO - LogMsg("Note: Compiled without Apple-specific Split-DNS support"); + LogMsg("Note: Compiled without Apple-specific Split-DNS support"); #endif - // Adding interfaces will use this flag, so set it now. - m->DivertMulticastAdvertisements = !m->AdvertiseLocalAddresses; - + // Adding interfaces will use this flag, so set it now. + m->DivertMulticastAdvertisements = !m->AdvertiseLocalAddresses; + #if APPLE_OSX_mDNSResponder - m->SPSBrowseCallback = UpdateSPSStatus; + m->SPSBrowseCallback = UpdateSPSStatus; #endif // APPLE_OSX_mDNSResponder - mStatus result = mDNSPlatformInit_setup(m); - - // We don't do asynchronous initialization on OS X, so by the time we get here the setup will already - // have succeeded or failed -- so if it succeeded, we should just call mDNSCoreInitComplete() immediately - if (result == mStatus_NoError) - { - mDNSCoreInitComplete(m, mStatus_NoError); - -#if ! NO_D2D - // We only initialize if mDNSCore successfully initialized. - CHECK_D2D_FUNCTION(D2DInitialize) - { - D2DStatus ds = D2DInitialize(m->p->CFRunLoop, xD2DServiceCallback, m) ; - if (ds != kD2DSuccess) - LogMsg("D2DInitialiize failed: %d", ds); - else - LogMsg("D2DInitialize succeeded"); - } + mStatus result = mDNSPlatformInit_setup(m); + + // We don't do asynchronous initialization on OS X, so by the time we get here the setup will already + // have succeeded or failed -- so if it succeeded, we should just call mDNSCoreInitComplete() immediately + if (result == mStatus_NoError) + { + mDNSCoreInitComplete(m, mStatus_NoError); + +#if !NO_D2D + // We only initialize if mDNSCore successfully initialized. + if (D2DInitialize) + { + D2DStatus ds = D2DInitialize(m->p->CFRunLoop, xD2DServiceCallback, m) ; + if (ds != kD2DSuccess) + LogMsg("D2DInitialiize failed: %d", ds); + else + LogMsg("D2DInitialize succeeded"); + } #endif // ! NO_D2D - - } - return(result); - } + + } + result = DNSSECCryptoInit(m); + return(result); +} mDNSexport void mDNSPlatformClose(mDNS *const m) - { - if (m->p->PowerConnection) - { +{ + if (m->p->PowerConnection) + { #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM - IONotificationPortSetDispatchQueue(m->p->PowerPortRef, NULL); + IONotificationPortSetDispatchQueue(m->p->PowerPortRef, NULL); #else - CFRunLoopRemoveSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(m->p->PowerPortRef), kCFRunLoopDefaultMode); + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(m->p->PowerPortRef), kCFRunLoopDefaultMode); #endif - // According to , a single call - // to IORegisterForSystemPower creates *three* objects that need to be disposed individually: - IODeregisterForSystemPower(&m->p->PowerNotifier); - IOServiceClose ( m->p->PowerConnection); - IONotificationPortDestroy ( m->p->PowerPortRef); - m->p->PowerConnection = 0; - } - - if (m->p->Store) - { + // According to , a single call + // to IORegisterForSystemPower creates *three* objects that need to be disposed individually: + IODeregisterForSystemPower(&m->p->PowerNotifier); + IOServiceClose ( m->p->PowerConnection); + IONotificationPortDestroy ( m->p->PowerPortRef); + m->p->PowerConnection = 0; + } + + if (m->p->Store) + { #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM - if (!SCDynamicStoreSetDispatchQueue(m->p->Store, NULL)) - LogMsg("mDNSPlatformClose: SCDynamicStoreSetDispatchQueue failed"); + if (!SCDynamicStoreSetDispatchQueue(m->p->Store, NULL)) + LogMsg("mDNSPlatformClose: SCDynamicStoreSetDispatchQueue failed"); #else - CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode); - CFRunLoopSourceInvalidate(m->p->StoreRLS); - CFRelease(m->p->StoreRLS); - m->p->StoreRLS = NULL; + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode); + CFRunLoopSourceInvalidate(m->p->StoreRLS); + CFRelease(m->p->StoreRLS); + m->p->StoreRLS = NULL; #endif - CFRelease(m->p->Store); - m->p->Store = NULL; - } - - if (m->p->PMRLS) - { - CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->PMRLS, kCFRunLoopDefaultMode); - CFRunLoopSourceInvalidate(m->p->PMRLS); - CFRelease(m->p->PMRLS); - m->p->PMRLS = NULL; - } - - if (m->p->SysEventNotifier >= 0) { close(m->p->SysEventNotifier); m->p->SysEventNotifier = -1; } - -#if ! NO_D2D - CHECK_D2D_FUNCTION(D2DTerminate) - { - D2DStatus ds = D2DTerminate(); - if (ds != kD2DSuccess) - LogMsg("D2DTerminate failed: %d", ds); - else - LogMsg("D2DTerminate succeeded"); - } + CFRelease(m->p->Store); + m->p->Store = NULL; + } + + if (m->p->PMRLS) + { + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->PMRLS, kCFRunLoopDefaultMode); + CFRunLoopSourceInvalidate(m->p->PMRLS); + CFRelease(m->p->PMRLS); + m->p->PMRLS = NULL; + } + + if (m->p->SysEventNotifier >= 0) { close(m->p->SysEventNotifier); m->p->SysEventNotifier = -1; } + +#if !NO_D2D + if (D2DTerminate) + { + D2DStatus ds = D2DTerminate(); + if (ds != kD2DSuccess) + LogMsg("D2DTerminate failed: %d", ds); + else + LogMsg("D2DTerminate succeeded"); + } #endif // ! NO_D2D - mDNSs32 utc = mDNSPlatformUTC(); - MarkAllInterfacesInactive(m, utc); - ClearInactiveInterfaces(m, utc); - CloseSocketSet(&m->p->permanentsockets); + mDNSs32 utc = mDNSPlatformUTC(); + MarkAllInterfacesInactive(m, utc); + ClearInactiveInterfaces(m, utc); + CloseSocketSet(&m->p->permanentsockets); #if APPLE_OSX_mDNSResponder - // clean up tunnels - while (m->TunnelClients) - { - ClientTunnel *cur = m->TunnelClients; - LogInfo("mDNSPlatformClose: removing client tunnel %p %##s from list", cur, cur->dstname.c); - if (cur->q.ThisQInterval >= 0) mDNS_StopQuery(m, &cur->q); - AutoTunnelSetKeys(cur, mDNSfalse); - m->TunnelClients = cur->next; - freeL("ClientTunnel", cur); - } - - if (AnonymousRacoonConfig) - { - AnonymousRacoonConfig = mDNSNULL; - LogInfo("mDNSPlatformClose: Deconfiguring autotunnel"); - (void)mDNSConfigureServer(kmDNSDown, mDNSNULL, mDNSNULL); - } - - if (m->AutoTunnelHostAddrActive && m->AutoTunnelHostAddr.b[0]) - { - m->AutoTunnelHostAddrActive = mDNSfalse; - LogInfo("mDNSPlatformClose: Removing AutoTunnel address %.16a", &m->AutoTunnelHostAddr); - (void)mDNSAutoTunnelInterfaceUpDown(kmDNSDown, m->AutoTunnelHostAddr.b); - } - if (m->p->ConndBTMMDict) CFRelease(m->p->ConndBTMMDict); + // clean up tunnels + while (m->TunnelClients) + { + ClientTunnel *cur = m->TunnelClients; + LogInfo("mDNSPlatformClose: removing client tunnel %p %##s from list", cur, cur->dstname.c); + if (cur->q.ThisQInterval >= 0) mDNS_StopQuery(m, &cur->q); + AutoTunnelSetKeys(cur, mDNSfalse); + m->TunnelClients = cur->next; + freeL("ClientTunnel", cur); + } + + if (AnonymousRacoonConfig) + { + AnonymousRacoonConfig = mDNSNULL; + LogInfo("mDNSPlatformClose: Deconfiguring autotunnel"); + (void)mDNSConfigureServer(kmDNSDown, mDNSNULL, mDNSNULL); + } #endif // APPLE_OSX_mDNSResponder - } +} #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -8836,68 +9149,68 @@ mDNSexport void mDNSPlatformClose(mDNS *const m) #endif mDNSexport mDNSu32 mDNSPlatformRandomNumber(void) - { - return(arc4random()); - } +{ + return(arc4random()); +} mDNSexport mDNSs32 mDNSPlatformOneSecond = 1000; mDNSexport mDNSu32 mDNSPlatformClockDivisor = 0; mDNSexport mStatus mDNSPlatformTimeInit(void) - { - // Notes: Typical values for mach_timebase_info: - // tbi.numer = 1000 million - // tbi.denom = 33 million - // These are set such that (mach_absolute_time() * numer/denom) gives us nanoseconds; - // numer / denom = nanoseconds per hardware clock tick (e.g. 30); - // denom / numer = hardware clock ticks per nanosecond (e.g. 0.033) - // (denom*1000000) / numer = hardware clock ticks per millisecond (e.g. 33333) - // So: mach_absolute_time() / ((denom*1000000)/numer) = milliseconds - // - // Arithmetic notes: - // tbi.denom is at least 1, and not more than 2^32-1. - // Therefore (tbi.denom * 1000000) is at least one million, but cannot overflow a uint64_t. - // tbi.denom is at least 1, and not more than 2^32-1. - // Therefore clockdivisor should end up being a number roughly in the range 10^3 - 10^9. - // If clockdivisor is less than 10^3 then that means that the native clock frequency is less than 1MHz, - // which is unlikely on any current or future Macintosh. - // If clockdivisor is greater than 10^9 then that means the native clock frequency is greater than 1000GHz. - // When we ship Macs with clock frequencies above 1000GHz, we may have to update this code. - struct mach_timebase_info tbi; - kern_return_t result = mach_timebase_info(&tbi); - if (result == KERN_SUCCESS) mDNSPlatformClockDivisor = ((uint64_t)tbi.denom * 1000000) / tbi.numer; - return(result); - } +{ + // Notes: Typical values for mach_timebase_info: + // tbi.numer = 1000 million + // tbi.denom = 33 million + // These are set such that (mach_absolute_time() * numer/denom) gives us nanoseconds; + // numer / denom = nanoseconds per hardware clock tick (e.g. 30); + // denom / numer = hardware clock ticks per nanosecond (e.g. 0.033) + // (denom*1000000) / numer = hardware clock ticks per millisecond (e.g. 33333) + // So: mach_absolute_time() / ((denom*1000000)/numer) = milliseconds + // + // Arithmetic notes: + // tbi.denom is at least 1, and not more than 2^32-1. + // Therefore (tbi.denom * 1000000) is at least one million, but cannot overflow a uint64_t. + // tbi.denom is at least 1, and not more than 2^32-1. + // Therefore clockdivisor should end up being a number roughly in the range 10^3 - 10^9. + // If clockdivisor is less than 10^3 then that means that the native clock frequency is less than 1MHz, + // which is unlikely on any current or future Macintosh. + // If clockdivisor is greater than 10^9 then that means the native clock frequency is greater than 1000GHz. + // When we ship Macs with clock frequencies above 1000GHz, we may have to update this code. + struct mach_timebase_info tbi; + kern_return_t result = mach_timebase_info(&tbi); + if (result == KERN_SUCCESS) mDNSPlatformClockDivisor = ((uint64_t)tbi.denom * 1000000) / tbi.numer; + return(result); +} mDNSexport mDNSs32 mDNSPlatformRawTime(void) - { - if (mDNSPlatformClockDivisor == 0) { LogMsg("mDNSPlatformRawTime called before mDNSPlatformTimeInit"); return(0); } - - static uint64_t last_mach_absolute_time = 0; - //static uint64_t last_mach_absolute_time = 0x8000000000000000LL; // Use this value for testing the alert display - 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; - // Note: This bug happens all the time on 10.3 - NotifyOfElusiveBug("mach_absolute_time went backwards!", - "This error occurs from time to time, often on newly released hardware, " - "and usually the exact cause is different in each instance.\r\r" - "Please file a new Radar bug report with the title “mach_absolute_time went backwards” " - "and assign it to Radar Component “Kernel” Version “X”."); - } - last_mach_absolute_time = this_mach_absolute_time; - - return((mDNSs32)(this_mach_absolute_time / mDNSPlatformClockDivisor)); - } +{ + if (mDNSPlatformClockDivisor == 0) { LogMsg("mDNSPlatformRawTime called before mDNSPlatformTimeInit"); return(0); } + + static uint64_t last_mach_absolute_time = 0; + //static uint64_t last_mach_absolute_time = 0x8000000000000000LL; // Use this value for testing the alert display + 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; + // Note: This bug happens all the time on 10.3 + NotifyOfElusiveBug("mach_absolute_time went backwards!", + "This error occurs from time to time, often on newly released hardware, " + "and usually the exact cause is different in each instance.\r\r" + "Please file a new Radar bug report with the title “mach_absolute_time went backwards” " + "and assign it to Radar Component “Kernel” Version “X”."); + } + last_mach_absolute_time = this_mach_absolute_time; + + return((mDNSs32)(this_mach_absolute_time / mDNSPlatformClockDivisor)); +} mDNSexport mDNSs32 mDNSPlatformUTC(void) - { - return time(NULL); - } +{ + return time(NULL); +} // Locking is a no-op here, because we're single-threaded with a CFRunLoop, so we can never interrupt ourselves mDNSexport void mDNSPlatformLock (const mDNS *const m) { (void)m; } @@ -8906,53 +9219,237 @@ mDNSexport void mDNSPlatformStrCopy( void *dst, const void *src) mDNSexport mDNSu32 mDNSPlatformStrLen ( const void *src) { return(strlen((char*)src)); } mDNSexport void mDNSPlatformMemCopy( void *dst, const void *src, mDNSu32 len) { memcpy(dst, src, len); } mDNSexport mDNSBool mDNSPlatformMemSame(const void *dst, const void *src, mDNSu32 len) { return(memcmp(dst, src, len) == 0); } +mDNSexport int mDNSPlatformMemCmp(const void *dst, const void *src, mDNSu32 len) { return(memcmp(dst, src, len)); } mDNSexport void mDNSPlatformMemZero( void *dst, mDNSu32 len) { memset(dst, 0, len); } +mDNSexport void mDNSPlatformQsort ( void *base, int nel, int width, int (*compar)(const void *, const void *)) +{ + return (qsort(base, nel, width, compar)); +} #if !(APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING) mDNSexport void * mDNSPlatformMemAllocate(mDNSu32 len) { return(mallocL("mDNSPlatformMemAllocate", len)); } #endif mDNSexport void mDNSPlatformMemFree (void *mem) { freeL("mDNSPlatformMemFree", mem); } mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason) - { - if (allowSleep && m->p->IOPMAssertion) - { - LogInfo("%s Destroying NoIdleSleep power assertion", __FUNCTION__); - IOPMAssertionRelease(m->p->IOPMAssertion); - m->p->IOPMAssertion = 0; - } - else if (!allowSleep && m->p->IOPMAssertion == 0) - { +{ + if (allowSleep && m->p->IOPMAssertion) + { + LogInfo("%s Destroying NoIdleSleep power assertion", __FUNCTION__); + IOPMAssertionRelease(m->p->IOPMAssertion); + m->p->IOPMAssertion = 0; + } + else if (!allowSleep && m->p->IOPMAssertion == 0) + { #ifdef kIOPMAssertionTypeNoIdleSleep - CFStringRef assertionName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s.%d %s"), getprogname(), getpid(), reason ? reason : ""); - IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, assertionName ? assertionName : CFSTR("mDNSResponder"), &m->p->IOPMAssertion); - if (assertionName) CFRelease(assertionName); - LogInfo("%s Creating NoIdleSleep power assertion", __FUNCTION__); + CFStringRef assertionName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s.%d %s"), getprogname(), getpid(), reason ? reason : ""); + IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, assertionName ? assertionName : CFSTR("mDNSResponder"), &m->p->IOPMAssertion); + if (assertionName) CFRelease(assertionName); + LogInfo("%s Creating NoIdleSleep power assertion", __FUNCTION__); #endif - } - } + } +} mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration) - { - mDNSu32 ifindex; - - // Sanity check - ifindex = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNStrue); - if (ifindex <= 0) - { - LogMsg("mDNSPlatformSendWakeupPacket: ERROR!! Invalid InterfaceID %u", ifindex); - return; - } - mDNSSendWakeupPacket(ifindex, EthAddr, IPAddr, iteration); - } - -// Called for rr->InterfaceID == mDNSInterface_Any. -// If current interface is P2P, verify that record is marked to IncludeP2P. +{ + mDNSu32 ifindex; + + // Sanity check + ifindex = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNStrue); + if (ifindex <= 0) + { + LogMsg("mDNSPlatformSendWakeupPacket: ERROR!! Invalid InterfaceID %u", ifindex); + return; + } + mDNSSendWakeupPacket(ifindex, EthAddr, IPAddr, iteration); +} + +mDNSexport mDNSBool mDNSPlatformInterfaceIsD2D(mDNSInterfaceID InterfaceID) +{ + NetworkInterfaceInfoOSX *info; + + if (InterfaceID == mDNSInterface_P2P) + return mDNStrue; + + if ( (InterfaceID == mDNSInterface_Any) + || (InterfaceID == mDNSInterfaceMark) + || (InterfaceID == mDNSInterface_LocalOnly) + || (InterfaceID == mDNSInterface_Unicast)) + return mDNSfalse; + + info = IfindexToInterfaceInfoOSX(&mDNSStorage, InterfaceID); + if (info == NULL) + { + // this log message can print when operations are stopped on an interface that has gone away + LogInfo("mDNSPlatformInterfaceIsD2D: Invalid interface index %d", InterfaceID); + return mDNSfalse; + } + + return (mDNSBool) info->D2DInterface; +} + +// Filter records send over P2P (D2D) type interfaces +// Note that the terms P2P and D2D are used synonymously in the current code and comments. mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf) - { - mDNSBool p2pInterface = (strncmp(intf->ifname, "p2p", 3) == 0); - - if (!p2pInterface || (rr->ARType == AuthRecordAnyIncludeP2P)) - return mDNStrue; - else - return mDNSfalse; - } +{ + // For an explicit match to a valid interface ID, return true. + if (rr->resrec.InterfaceID == intf->InterfaceID) + return mDNStrue; + + // Only filtering records for D2D type interfaces, return true for all other interface types. + if (!mDNSPlatformInterfaceIsD2D(intf->InterfaceID)) + return mDNStrue; + + // If it's an AWDL interface the record must be explicitly marked to include AWDL. + if (intf->InterfaceID == AWDLInterfaceID) + { + if (rr->ARType == AuthRecordAnyIncludeAWDL) + return mDNStrue; + else + return mDNSfalse; + } + + // Sent record if it is explicitly marked to include all other P2P type interfaces. + if (rr->ARType == AuthRecordAnyIncludeP2P) + return mDNStrue; + + // Don't send the record over this interface. + return mDNSfalse; +} + +// Filter questions send over P2P (D2D) type interfaces. +mDNSexport mDNSBool mDNSPlatformValidQuestionForInterface(DNSQuestion *q, const NetworkInterfaceInfo *intf) +{ + // For an explicit match to a valid interface ID, return true. + if (q->InterfaceID == intf->InterfaceID) + return mDNStrue; + + // Only filtering questions for D2D type interfaces + if (!mDNSPlatformInterfaceIsD2D(intf->InterfaceID)) + return mDNStrue; + + // If it's an AWDL interface the question must be explicitly marked to include AWDL. + if (intf->InterfaceID == AWDLInterfaceID) + { + if (q->flags & kDNSServiceFlagsIncludeAWDL) + return mDNStrue; + else + return mDNSfalse; + } + + // Sent question if it is explicitly marked to include all other P2P type interfaces. + if (q->flags & kDNSServiceFlagsIncludeP2P) + return mDNStrue; + + // Don't send the question over this interface. + return mDNSfalse; +} + +// Returns true unless record was received over the AWDL interface and +// the question was not specific to the AWDL interface or did not specify kDNSServiceInterfaceIndexAny +// with the kDNSServiceFlagsIncludeAWDL flag set. +mDNSexport mDNSBool mDNSPlatformValidRecordForQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) +{ + // Don't do AWDL specific filtering if AWDL support is not enabled. + if (!mDNSStorage.mDNSHandlePeerEvents) + return mDNStrue; + + if (!rr->InterfaceID || (rr->InterfaceID == q->InterfaceID)) + return mDNStrue; + + if ((rr->InterfaceID == AWDLInterfaceID) && !(q->flags & kDNSServiceFlagsIncludeAWDL)) + { + LogInfo("mDNSPlatformValidRecordForQuestion: Record recieved over AWDL not returned for %s %##s query", + DNSTypeName(q->qtype), q->qname.c); + return mDNSfalse; + } + + return mDNStrue; +} + +// formating time to RFC 4034 format +mDNSexport void mDNSPlatformFormatTime(unsigned long te, mDNSu8 *buf, int bufsize) +{ + struct tm tmTime; + time_t t = (time_t)te; + // Time since epoch : strftime takes "tm". Convert seconds to "tm" using + // gmtime_r first and then use strftime + gmtime_r(&t, &tmTime); + strftime((char *)buf, bufsize, "%Y%m%d%H%M%S", &tmTime); +} + +mDNSexport mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, mDNSIPPort *lport, mDNSAddr *raddr, mDNSIPPort *rport, mDNSTCPInfo *mti) +{ + struct tcp_info ti; + struct info_tuple itpl; + int mib[4]; + unsigned int miblen; + size_t len; + size_t sz; + + itpl.itpl_proto = IPPROTO_TCP; + + if (laddr->type == mDNSAddrType_IPv4) + { + mDNSPlatformMemCopy(&itpl.itpl_local_sin.sin_addr, &laddr->ip.v4, sizeof(struct in_addr)); + mDNSPlatformMemCopy(&itpl.itpl_remote_sin.sin_addr, &raddr->ip.v4, sizeof(struct in_addr)); + itpl.itpl_local_sin.sin_port = lport->NotAnInteger; + itpl.itpl_remote_sin.sin_port = rport->NotAnInteger; + itpl.itpl_remote_sin.sin_family = AF_INET; + } + else + { + mDNSPlatformMemCopy(&itpl.itpl_local_sin6.sin6_addr, &laddr->ip.v6, sizeof(struct in6_addr)); + mDNSPlatformMemCopy(&itpl.itpl_remote_sin6.sin6_addr, &raddr->ip.v6, sizeof(struct in6_addr)); + itpl.itpl_local_sin6.sin6_port = lport->NotAnInteger; + itpl.itpl_remote_sin6.sin6_port = rport->NotAnInteger; + itpl.itpl_remote_sin6.sin6_family = AF_INET6; + } + + + sz = sizeof(mib)/sizeof(mib[0]); + if (sysctlnametomib("net.inet.tcp.info", mib, &sz) == -1) + { + LogMsg("mDNSPlatformRetrieveTCPInfo: sysctlnametomib failed %d, %s", errno, strerror(errno)); + return mStatus_UnknownErr; + } + + miblen = (unsigned int)sz; + + len = sizeof(struct tcp_info); + if (sysctl(mib, miblen, &ti, &len, &itpl, sizeof(struct info_tuple)) == -1) + { + LogMsg("mDNSPlatformRetrieveTCPInfo: sysctl failed %d, %s", errno, strerror(errno)); + return mStatus_UnknownErr; + } + mti->seq = ti.tcpi_snd_nxt - 1; + mti->ack = ti.tcpi_rcv_nxt; + mti->window = ti.tcpi_rcv_space >> ti.tcpi_rcv_wscale; + mti->IntfId = mDNSPlatformInterfaceIDfromInterfaceIndex(m, ti.tcpi_last_outif); + if (mti->IntfId == mDNSInterface_LocalOnly || mti->IntfId == mDNSInterface_P2P || mti->IntfId == mDNSInterface_Any) + { + LogMsg("mDNSPlatformRetrieveTCPInfo: Bad InterfaceId %p", mti->IntfId); + return mStatus_BadParamErr; + } + + return mStatus_NoError; +} + +#if APPLE_OSX_mDNSResponder +mDNSexport void mDNSPlatformToggleInterfaceAdvt(mDNS *const m, mDNSBool stopAdvt) +{ + NetworkInterfaceInfoOSX *intf; + + LogInfo("%s called to %s advertisements", __func__, stopAdvt? "stop" : "start"); + for (intf = m->p->InterfaceList; intf; intf = intf->next) + { + // For now, the ioctl only supports suppression of IPv6 advertisements + if (intf->ifinfo.InterfaceActive && intf->ifinfo.McastTxRx) + { + if (mDNSInterfaceAdvtIoctl(intf->ifinfo.ifname, (stopAdvt? 0 : 1)) < 0) + { + LogMsg("%s: mDNSInterfaceAdvtIoctl failed"); + } + } + } +} +#endif diff --git a/mDNSMacOSX/mDNSMacOSX.h b/mDNSMacOSX/mDNSMacOSX.h index c914415..7c30207 100644 --- a/mDNSMacOSX/mDNSMacOSX.h +++ b/mDNSMacOSX/mDNSMacOSX.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,7 +19,7 @@ #define __mDNSOSX_h #ifdef __cplusplus - extern "C" { +extern "C" { #endif #include @@ -39,120 +39,118 @@ typedef struct NetworkInterfaceInfoOSX_struct NetworkInterfaceInfoOSX; typedef void (*KQueueEventCallback)(int fd, short filter, void *context); typedef struct - { - KQueueEventCallback KQcallback; - void *KQcontext; - const char const *KQtask; // For debugging messages +{ + KQueueEventCallback KQcallback; + void *KQcontext; + const char const *KQtask; // For debugging messages #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM - dispatch_source_t readSource; - dispatch_source_t writeSource; - mDNSBool fdClosed; + dispatch_source_t readSource; + dispatch_source_t writeSource; + mDNSBool fdClosed; #endif - } KQueueEntry; +} KQueueEntry; typedef struct - { - mDNSIPPort port; // MUST BE FIRST FIELD -- UDPSocket_struct begins with a KQSocketSet, - // and mDNSCore requires every UDPSocket_struct to begin with a mDNSIPPort port - mDNS *m; - int sktv4; - KQueueEntry kqsv4; +{ + mDNSIPPort port; // MUST BE FIRST FIELD -- UDPSocket_struct begins with a KQSocketSet, + // and mDNSCore requires every UDPSocket_struct to begin with a mDNSIPPort port + mDNS *m; + int sktv4; + KQueueEntry kqsv4; #ifndef NO_IPV6 - int sktv6; - KQueueEntry kqsv6; + int sktv6; + KQueueEntry kqsv6; #endif - int *closeFlag; - } KQSocketSet; + int *closeFlag; +} KQSocketSet; struct UDPSocket_struct - { - KQSocketSet ss; // First field of KQSocketSet has to be mDNSIPPort -- mDNSCore requires every UDPSocket_struct to begin with mDNSIPPort port - }; +{ + KQSocketSet ss; // First field of KQSocketSet has to be mDNSIPPort -- mDNSCore requires every UDPSocket_struct to begin with mDNSIPPort port +}; struct NetworkInterfaceInfoOSX_struct - { - NetworkInterfaceInfo ifinfo; // MUST be the first element in this structure - NetworkInterfaceInfoOSX *next; - mDNS *m; - mDNSu8 Exists; // 1 = currently exists in getifaddrs list; 0 = doesn't - // 2 = exists, but McastTxRx state changed - mDNSu8 Flashing; // Set if interface appeared for less than 60 seconds and then vanished - mDNSu8 Occulting; // Set if interface vanished for less than 60 seconds and then came back - mDNSs32 AppearanceTime; // Time this interface appeared most recently in getifaddrs list - // i.e. the first time an interface is seen, AppearanceTime is set. - // If an interface goes away temporarily and then comes back then - // AppearanceTime is updated to the time of the most recent appearance. - mDNSs32 LastSeen; // If Exists==0, last time this interface appeared in getifaddrs list - unsigned int ifa_flags; - struct in_addr ifa_v4addr; - mDNSu32 scope_id; // interface index / IPv6 scope ID - mDNSEthAddr BSSID; // BSSID of 802.11 base station, if applicable - u_short sa_family; - int BPF_fd; // -1 uninitialized; -2 requested BPF; -3 failed - int BPF_mcfd; // Socket for our IPv6 ND group membership - u_int BPF_len; +{ + NetworkInterfaceInfo ifinfo; // MUST be the first element in this structure + NetworkInterfaceInfoOSX *next; + mDNS *m; + mDNSu8 Exists; // 1 = currently exists in getifaddrs list; 0 = doesn't + // 2 = exists, but McastTxRx state changed + mDNSu8 Flashing; // Set if interface appeared for less than 60 seconds and then vanished + mDNSu8 Occulting; // Set if interface vanished for less than 60 seconds and then came back + mDNSu8 D2DInterface; // IFEF_LOCALNET_PRIVATE flag indicates we should call + // D2D plugin for operations over this interface + + mDNSs32 AppearanceTime; // Time this interface appeared most recently in getifaddrs list + // i.e. the first time an interface is seen, AppearanceTime is set. + // If an interface goes away temporarily and then comes back then + // AppearanceTime is updated to the time of the most recent appearance. + mDNSs32 LastSeen; // If Exists==0, last time this interface appeared in getifaddrs list + unsigned int ifa_flags; + struct in_addr ifa_v4addr; + mDNSu32 scope_id; // interface index / IPv6 scope ID + mDNSEthAddr BSSID; // BSSID of 802.11 base station, if applicable + u_short sa_family; + int BPF_fd; // -1 uninitialized; -2 requested BPF; -3 failed + int BPF_mcfd; // Socket for our IPv6 ND group membership + u_int BPF_len; #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM - dispatch_source_t BPF_source; + dispatch_source_t BPF_source; #else - CFSocketRef BPF_cfs; - CFRunLoopSourceRef BPF_rls; + CFSocketRef BPF_cfs; + CFRunLoopSourceRef BPF_rls; #endif - NetworkInterfaceInfoOSX *Registered; // non-NULL means registered with mDNS Core - }; + NetworkInterfaceInfoOSX *Registered; // non-NULL means registered with mDNS Core +}; struct mDNS_PlatformSupport_struct - { - NetworkInterfaceInfoOSX *InterfaceList; - KQSocketSet permanentsockets; - domainlabel userhostlabel; // The hostlabel as it was set in System Preferences the last time we looked - domainlabel usernicelabel; // The nicelabel as it was set in System Preferences the last time we looked - // Following four variables are used for optimization where the helper is not - // invoked when not needed. It records the state of what we told helper the - // last time we invoked mDNSPreferencesSetName - domainlabel prevoldhostlabel; // Previous m->p->userhostlabel - domainlabel prevnewhostlabel; // Previous m->hostlabel - domainlabel prevoldnicelabel; // Previous m->p->usernicelabel - domainlabel prevnewnicelabel; // Previous m->nicelabel - mDNSs32 NotifyUser; - mDNSs32 HostNameConflict; // Time we experienced conflict on our link-local host name - mDNSs32 NetworkChanged; - mDNSs32 KeyChainTimer; - - CFRunLoopRef CFRunLoop; - SCDynamicStoreRef Store; - CFRunLoopSourceRef StoreRLS; - CFRunLoopSourceRef PMRLS; - int SysEventNotifier; - KQueueEntry SysEventKQueue; - IONotificationPortRef PowerPortRef; - io_connect_t PowerConnection; - io_object_t PowerNotifier; +{ + NetworkInterfaceInfoOSX *InterfaceList; + KQSocketSet permanentsockets; + domainlabel userhostlabel; // The hostlabel as it was set in System Preferences the last time we looked + domainlabel usernicelabel; // The nicelabel as it was set in System Preferences the last time we looked + // Following four variables are used for optimization where the helper is not + // invoked when not needed. It records the state of what we told helper the + // last time we invoked mDNSPreferencesSetName + domainlabel prevoldhostlabel; // Previous m->p->userhostlabel + domainlabel prevnewhostlabel; // Previous m->hostlabel + domainlabel prevoldnicelabel; // Previous m->p->usernicelabel + domainlabel prevnewnicelabel; // Previous m->nicelabel + mDNSs32 NotifyUser; + mDNSs32 HostNameConflict; // Time we experienced conflict on our link-local host name + mDNSs32 NetworkChanged; + mDNSs32 KeyChainTimer; + + CFRunLoopRef CFRunLoop; + SCDynamicStoreRef Store; + CFRunLoopSourceRef StoreRLS; + CFRunLoopSourceRef PMRLS; + int SysEventNotifier; + KQueueEntry SysEventKQueue; + IONotificationPortRef PowerPortRef; + io_connect_t PowerConnection; + io_object_t PowerNotifier; #ifdef kIOPMAcknowledgmentOptionSystemCapabilityRequirements - IOPMConnection IOPMConnection; + IOPMConnection IOPMConnection; #endif - IOPMAssertionID IOPMAssertion; - SCPreferencesRef SCPrefs; - long SleepCookie; // Cookie we need to pass to IOAllowPowerChange() - long WakeAtUTC; - mDNSs32 RequestReSleep; + IOPMAssertionID IOPMAssertion; + long SleepCookie; // Cookie we need to pass to IOAllowPowerChange() + long WakeAtUTC; + mDNSs32 RequestReSleep; #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM - dispatch_source_t timer; - dispatch_source_t custom; + dispatch_source_t timer; + dispatch_source_t custom; #else - pthread_mutex_t BigMutex; + pthread_mutex_t BigMutex; #endif - mDNSs32 BigMutexStartTime; - int WakeKQueueLoopFD; -#if APPLE_OSX_mDNSResponder - CFDictionaryRef ConndBTMMDict; // Dictionary of tunnel name/value pairs -#endif - }; + mDNSs32 BigMutexStartTime; + int WakeKQueueLoopFD; +}; extern int OfferSleepProxyService; extern int DisableSleepProxyClient; extern int UseInternalSleepProxy; extern int OSXVers, iOSVers; -extern mDNSBool DisableInboundRelayConnection; #define OSXVers_Base 4 #define OSXVers_10_0_Cheetah 4 #define OSXVers_10_1_Puma 5 @@ -164,11 +162,12 @@ extern mDNSBool DisableInboundRelayConnection; extern int KQueueFD; -extern void NotifyOfElusiveBug(const char *title, const char *msg); // Both strings are UTF-8 text +extern void NotifyOfElusiveBug(const char *title, const char *msg); // Both strings are UTF-8 text extern void SetDomainSecrets(mDNS *m); extern void mDNSMacOSXNetworkChanged(mDNS *const m); extern void mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring); extern NetworkInterfaceInfoOSX *IfindexToInterfaceInfoOSX(const mDNS *const m, mDNSInterfaceID ifindex); +extern void mDNSUpdatePacketFilter(const ResourceRecord *const excludeRecord); #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM extern int KQueueSet(int fd, u_short flags, short filter, KQueueEntry *const entryRef); @@ -185,6 +184,8 @@ extern void mDNSPlatformCloseFD(KQueueEntry *kq, int fd); extern mDNSBool DictionaryIsEnabled(CFDictionaryRef dict); +extern void mDNSPlatformToggleInterfaceAdvt(mDNS *const m, mDNSBool stopAdvt); + // If any event takes more than WatchDogReportingThreshold milliseconds to be processed, we log a warning message // General event categories are: // o Mach client request initiated / terminated @@ -204,16 +205,16 @@ extern mDNSBool DictionaryIsEnabled(CFDictionaryRef dict); extern int WatchDogReportingThreshold; struct CompileTimeAssertionChecks_mDNSMacOSX - { - // Check our structures are reasonable sizes. Including overly-large buffers, or embedding - // other overly-large structures instead of having a pointer to them, can inadvertently - // cause structure sizes (and therefore memory usage) to balloon unreasonably. - char sizecheck_NetworkInterfaceInfoOSX[(sizeof(NetworkInterfaceInfoOSX) <= 7000) ? 1 : -1]; - char sizecheck_mDNS_PlatformSupport [(sizeof(mDNS_PlatformSupport) <= 768) ? 1 : -1]; - }; +{ + // Check our structures are reasonable sizes. Including overly-large buffers, or embedding + // other overly-large structures instead of having a pointer to them, can inadvertently + // cause structure sizes (and therefore memory usage) to balloon unreasonably. + char sizecheck_NetworkInterfaceInfoOSX[(sizeof(NetworkInterfaceInfoOSX) <= 7000) ? 1 : -1]; + char sizecheck_mDNS_PlatformSupport [(sizeof(mDNS_PlatformSupport) <= 768) ? 1 : -1]; +}; #ifdef __cplusplus - } +} #endif #endif diff --git a/mDNSMacOSX/mDNSResponder-entitlements.plist b/mDNSMacOSX/mDNSResponder-entitlements.plist new file mode 100644 index 0000000..6c42a2e --- /dev/null +++ b/mDNSMacOSX/mDNSResponder-entitlements.plist @@ -0,0 +1,8 @@ + + + + + com.apple.wifi.manager-access + + + diff --git a/mDNSMacOSX/mDNSResponder.order b/mDNSMacOSX/mDNSResponder.order index bb130ff..55cfd6f 100644 --- a/mDNSMacOSX/mDNSResponder.order +++ b/mDNSMacOSX/mDNSResponder.order @@ -6,7 +6,6 @@ _main _LogMsgWithLevel _mDNS_vsnprintf _mDNSPlatformWriteLogMsg -_safe_vproc_transaction_begin _KQueueSet _mDNSMacOSXSystemBuildNumber _mDNSDaemonInitialize @@ -149,8 +148,6 @@ _mDNS_RegisterService _ServiceCallback _GetServiceTarget _SetupLocalAutoTunnelInterface_internal -_mDNSAutoTunnelInterfaceUpDown -_proxy_mDNSAutoTunnelInterfaceUpDown _NetworkChanged _KQueueLock _AbortDeregistration diff --git a/mDNSMacOSX/mDNSResponder.plist b/mDNSMacOSX/mDNSResponder.plist new file mode 100644 index 0000000..6150bc3 --- /dev/null +++ b/mDNSMacOSX/mDNSResponder.plist @@ -0,0 +1,31 @@ + + + + + + OpenSourceProject + libipsec + OpenSourceVersion + Original version number unavailable, possibly RELENG_4_9_0_RELEASE + OpenSourceWebsiteURL + http://www.freebsd.org + OpenSourceSCM + svn export http://svn.freebsd.org/base/release/4.9.0/lib/libipsec + OpenSourceImportDate + 2007-07-31 + OpenSourceModifications + + Removed all files except ipsec_strerror.h libpfkey.h pfkey.c + Added Apple Computer copyright and Apache license + Removed unused include netkey/key_var.h + Fixed compiler warnings such as "unused function parameter" and "signed/unsigned comparison" + Added code to conditionally compile only on OSX + Whitespace changes + + OpenSourceLicense + bsd + OpenSourceLicenseFile + mDNSResponder.txt + + + diff --git a/mDNSMacOSX/mDNSResponder.sb b/mDNSMacOSX/mDNSResponder.sb index fc46bac..7fa2643 100644 --- a/mDNSMacOSX/mDNSResponder.sb +++ b/mDNSMacOSX/mDNSResponder.sb @@ -1,6 +1,6 @@ ; -*- Mode: Scheme; tab-width: 4 -*- ; -; Copyright (c) 2007 Apple Inc. All rights reserved. +; Copyright (c) 2012 Apple 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: @@ -27,126 +27,105 @@ ; ;############################################################################ -(version 1) ; WARNING: The sandbox rule capabilities and syntax used in this file are currently an ; Apple SPI (System Private Interface) and are subject to change at any time without notice. -; Apple may in future announce an official public supported sandbox API, but until then Developers -; are cautioned not to build products that use or depend on the sandbox facilities illustrated here. - -; Use "debug all" to log all operations examined by seatbelt, whether allowed or not. -; Use "debug deny" to log only operations that are denied by seatbelt -; to discover what specific attempted operation is causing an exception. -;(debug all) -(debug deny) - -; To help debugging, "with send-signal SIGFPE" will trigger a fake floating-point exception, -; which will crash the process and show the call stack leading to the offending operation. -; For the shipping version "deny" is probably better because it vetoes the operation -; without killing the process. +(version 1) +; When mDNSResponder is denied access, we want to avoid symoblification of mDNSResponder +; to get the stack trace as that can get into deadlock. no-callout will prevent +; symbolification. +(deny default (with no-callout)) -(deny default) -;(deny default (with send-signal SIGFPE)) +(import "system.sb") -; Special exception: "send-signal" command does not apply to the mach-* operations, -; so for those we have to use a plain unadorned "deny" instead -; (which means we may not get any notification of unintentional mach-* denials) -(deny mach-lookup) -(deny mach-priv-host-port) +; Baseline +(allow file-read-metadata ipc-posix-shm) ; Mach communications ; These are needed for things like getpwnam, hostname changes, & keychain -(allow mach-lookup (global-name - "com.apple.bsd.dirhelper" - "com.apple.distributed_notifications.2" - "com.apple.ocspd" - "com.apple.PowerManagement.control" - "com.apple.mDNSResponderHelper" - "com.apple.SecurityServer" - "com.apple.SystemConfiguration.configd" - "com.apple.SystemConfiguration.SCNetworkReachability" - "com.apple.system.DirectoryService.libinfo_v1" - "com.apple.system.DirectoryService.membership_v1" - "com.apple.system.notification_center" - "com.apple.system.logger" - "com.apple.webcontentfilter.dns" - "com.apple.server.bluetooth" - "com.apple.awacs" - "com.apple.blued")) - -; Rules to allow the operations mDNSResponder needs start here - -(allow signal (target self)) -(allow network*) ; Allow networking, including Unix Domain Sockets +(allow mach-lookup + (global-name "com.apple.bsd.dirhelper") + (global-name "com.apple.distributed_notifications.2") + (global-name "com.apple.ocspd") + (global-name "com.apple.PowerManagement.control") + (global-name "com.apple.mDNSResponderHelper") + (global-name "com.apple.SecurityServer") + (global-name "com.apple.SystemConfiguration.configd") + (global-name "com.apple.SystemConfiguration.SCNetworkReachability") + (global-name "com.apple.system.notification_center") + (global-name "com.apple.system.logger") + (global-name "com.apple.webcontentfilter.dns") + (global-name "com.apple.server.bluetooth") + (global-name "com.apple.awacs") + (global-name "com.apple.networkd") + (global-name "com.apple.securityd") + (global-name "com.apple.wifi.manager") + (global-name "com.apple.blued")) + +; Networking, including Unix Domain Sockets +(allow network*) + +; Raw sockets (if (defined? 'system-socket) - (allow system-socket)) ; To create raw sockets -(allow sysctl-read) ; To get hardware model information -(allow sysctl-write) ; Needed for CFSocket -(allow file-read-metadata) ; Needed for dyld to work -(allow ipc-posix-shm) ; Needed for POSIX shared memory - -(allow file-read-data (regex #"^/dev/random$")) -(allow file-read-data file-write-data (regex #"^/dev/console$")) ; Needed for syslog early in the boot process -(allow file-read-data (regex #"^/dev/autofs_nowait$")) ; Used by CF to circumvent automount triggers -(allow file-read-data (regex #"^/private/etc/hosts$")) ; /etc/hosts support -(allow file-read-data (regex #"^/private/etc$")) ; /etc/hosts support - -; Allow us to read and write our socket -(allow file-read* file-write* (regex #"^/private/var/run/mDNSResponder$")) - -; Allow us to read system version, settings, and other miscellaneous necessary file system accesses -(allow file-read-data (regex #"^/dev/urandom$")) -(allow file-read-data (regex #"^/usr/sbin(/mDNSResponder)?$")) ; Needed for CFCopyVersionDictionary() -(allow file-read-data (regex #"^/usr/share/icu/.*$")) -(allow file-read-data (regex #"^/usr/share/zoneinfo/.*$")) -(allow file-read-data (regex #"^/Library/Preferences/SystemConfiguration/preferences\.plist$")) -(allow file-read-data (regex #"^/Library/Preferences/SystemConfiguration/com\.apple\.nat\.plist$")) -(allow file-read-data (regex #"^/Library/Preferences/(ByHost/)?\.GlobalPreferences.*\.plist$")) -(allow file-read-data (regex #"^/Library/Preferences/com\.apple\.security.*\.plist$")) -(allow file-read-data (regex #"^/Library/Preferences/com\.apple\.crypto\.plist$")) -(allow file-read-data (regex #"^/Library/Security/Trust Settings/Admin\.plist$")) -(allow file-read-data (regex #"^/System/Library/CoreServices/SystemVersion.*$")) -(allow file-read-data (regex #"^/System/Library/Preferences/com\.apple\.security.*\.plist$")) -(allow file-read-data (regex #"^/System/Library/Preferences/com\.apple\.crypto\.plist$")) -(allow file-read-data (regex #"^/System/Library/SystemConfiguration/PowerManagement\.bundle(/|$)")) -(allow file-read-data (regex #"^/Library/Preferences/SystemConfiguration/com\.apple\.PowerManagement\.plist$")) -(allow file-read-data (regex #"^/private/var/preferences/SystemConfiguration/preferences\.plist$")) - -; Allow access to System Keychain -(allow file-read-data (regex #"^/System/Library/Security$")) -(allow file-read-data (regex #"^/System/Library/Keychains/.*$")) + (allow system-socket)) + +; Hardware model information +(allow sysctl-read) + +; Syslog early in the boot process +(allow file-read-data file-write-data (literal "/dev/console")) + +(allow file-read-data + ; /etc/hosts support + (literal "/private/etc/hosts") + (literal "/private/etc")) + +; Our socket +(allow file-read* file-write* (literal "/private/var/run/mDNSResponder")) + +; System version, settings, and other miscellaneous necessary file system accesses +(allow file-read-data + ; Needed for CFCopyVersionDictionary() + (literal "/usr/sbin") + (literal "/usr/sbin/mDNSResponder") + + (literal "/Library/Preferences/SystemConfiguration/preferences.plist") + (literal "/Library/Preferences/SystemConfiguration/com.apple.nat.plist") + (regex #"^/Library/Preferences/(ByHost/)?\.GlobalPreferences\.") + (literal "/Library/Preferences/com.apple.crypto.plist") + (literal "/Library/Security/Trust Settings/Admin.plist") + (regex #"^/Library/Preferences/com\.apple\.security\.") + (literal "/Library/Preferences/SystemConfiguration/com.apple.PowerManagement.plist") + (literal "/private/var/preferences/SystemConfiguration/preferences.plist")) + ; We just need access to System.keychain. But we don't want errors logged if other keychains are ; accessed under /Library/Keychains. Other keychains may be accessed as part of setting up an SSL -; connection. Instead of adding access to it here(to things which we don't need), we disable any +; connection. Instead of adding access to it here (to things which we don't need), we disable any ; logging that might happen during the access -(deny file-read-data (regex #"^/Library/Keychains/") (with no-log)) -(allow file-read-data (regex #"^/Library/Keychains/System\.keychain$")) +(deny file-read-data (regex #"^/Library/Keychains/") (with no-log)) +(allow file-read-data (literal "/Library/Keychains/System.keychain")) + ; Our Module Directory Services cache -(allow file-read-data (regex #"^/private/var/tmp/mds/")) -(allow file-read* file-write* (regex #"^/private/var/tmp/mds/[0-9]+(/|$)")) -(allow file-read-data (regex #"^/private/var/db/mds/")) -(allow file-read* file-write* (regex #"^/private/var/db/mds/[0-9]+(/|$)")) -(allow file-read* file-write* (regex #"^/private/var/folders/[^/]+/[^/]+/C/mds(/|$)")) -(allow file-read* file-write* (regex #"^/private/var/folders/[^/]+/[^/]+/-Caches-/mds(/|$)")) ; Required on 10.5 and 10.6 +(allow file-read-data + (subpath "/private/var/tmp/mds") + (subpath "/private/var/db/mds")) + +(allow file-read* file-write* + (regex #"^/private/var/tmp/mds/[0-9]+(/|$)") + (regex #"^/private/var/db/mds/[0-9]+(/|$)") + (regex #"^/private/var/folders/[^/]+/[^/]+/C/mds(/|$)") + + ; Required on 10.5 and 10.6 + (regex #"^/private/var/folders/[^/]+/[^/]+/-Caches-/mds(/|$)")) + ; CRL Cache for SSL/TLS connections -(allow file-read-data (regex #"^/private/var/db/crls/crlcache\.db$")) +(allow file-read-data (literal "/private/var/db/crls/crlcache.db")) ; For mDNS sleep proxy offload and IOPMConnectionCreate (if (defined? 'iokit-open) (begin - (allow iokit-open (iokit-user-client-class "NVEthernetUserClientMDNS")) - (allow iokit-open (iokit-user-client-class "mDNSOffloadUserClient")) - (allow iokit-open (iokit-user-client-class "RootDomainUserClient")))) - -; For D2D -(allow file-read-data (regex #"^/System/Library/PrivateFrameworks/DeviceToDeviceManager.framework(/|$)")) -(allow file-read-data (regex #"^/System/Library/PrivateFrameworks/MobileBluetooth.framework(/|$)")) -(allow file-read-data (regex #"^/System/Library/Frameworks/CoreFoundation.framework(/|$)")) -(allow file-read-data (regex #"^/System/Library/Frameworks/SystemConfiguration.framework(/|$)")) -(allow file-read-data (regex #"^/System/Library/Frameworks/IOKit.framework(/|$)")) -(allow file-read-data (regex #"^/System/Library/Frameworks/Security.framework(/|$)")) -(allow file-read-data file-write-data file-ioctl (regex #"^/dev/dtracehelper$")) - -; For WebFilterDNS framework -(allow file-read-data (regex #"^/System/Library/PrivateFrameworks/WebFilterDNS.framework(/|$)")) + (allow iokit-open + (iokit-user-client-class "NVEthernetUserClientMDNS") + (iokit-user-client-class "mDNSOffloadUserClient") + (iokit-user-client-class "RootDomainUserClient")))) diff --git a/mDNSMacOSX/mDNSResponder.txt b/mDNSMacOSX/mDNSResponder.txt new file mode 100644 index 0000000..f798e56 --- /dev/null +++ b/mDNSMacOSX/mDNSResponder.txt @@ -0,0 +1,55 @@ +1) + * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project 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 THE PROJECT AND 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 THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + +2) + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project 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 THE PROJECT AND 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 THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. diff --git a/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj b/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj index ba9cbb3..95f6f83 100644 --- a/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj +++ b/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj @@ -17,8 +17,6 @@ D284BF2C0ADD815A0027CCDF /* PBXTargetDependency */, D284BF2E0ADD81600027CCDF /* PBXTargetDependency */, D284BF300ADD81630027CCDF /* PBXTargetDependency */, - D284BF260ADD814F0027CCDF /* PBXTargetDependency */, - D284BF2A0ADD81530027CCDF /* PBXTargetDependency */, ); name = "Build More"; productName = "Build All"; @@ -93,6 +91,14 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 2124FA2C1471E98C0021D7BB /* nsec.h in Headers */ = {isa = PBXBuildFile; fileRef = 2124FA2B1471E98C0021D7BB /* nsec.h */; }; + 2124FA2D1471E98C0021D7BB /* nsec.h in Headers */ = {isa = PBXBuildFile; fileRef = 2124FA2B1471E98C0021D7BB /* nsec.h */; }; + 2124FA301471E9B50021D7BB /* dnssec.h in Headers */ = {isa = PBXBuildFile; fileRef = 2124FA2F1471E9B50021D7BB /* dnssec.h */; }; + 2124FA311471E9B50021D7BB /* dnssec.h in Headers */ = {isa = PBXBuildFile; fileRef = 2124FA2F1471E9B50021D7BB /* dnssec.h */; }; + 2124FA331471E9DE0021D7BB /* nsec.c in Sources */ = {isa = PBXBuildFile; fileRef = 2124FA321471E9DE0021D7BB /* nsec.c */; }; + 2124FA341471E9DE0021D7BB /* nsec.c in Sources */ = {isa = PBXBuildFile; fileRef = 2124FA321471E9DE0021D7BB /* nsec.c */; }; + 213BDC6D147319F400000896 /* dnssec.c in Sources */ = {isa = PBXBuildFile; fileRef = 213BDC6C147319F400000896 /* dnssec.c */; }; + 213BDC6E147319F400000896 /* dnssec.c in Sources */ = {isa = PBXBuildFile; fileRef = 213BDC6C147319F400000896 /* dnssec.c */; }; 213FB23C12028C4A002B3A08 /* BonjourEvents.c in Sources */ = {isa = PBXBuildFile; fileRef = 213FB22C12028B53002B3A08 /* BonjourEvents.c */; }; 213FB23D12028C5A002B3A08 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */; }; 215FFAEE124000F900470DE1 /* dnssd_ipc.c in Sources */ = {isa = PBXBuildFile; fileRef = F5E11B5A04A28126019798ED /* dnssd_ipc.c */; }; @@ -113,6 +119,18 @@ 215FFAFD1240013400470DE1 /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */; }; 215FFAFE1240013400470DE1 /* DNSServiceDiscoveryRequest.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */; }; 215FFAFF1240013400470DE1 /* DNSServiceDiscoveryReply.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */; settings = {ATTRIBUTES = (Server, ); }; }; + 219D5542149ED645004464AE /* libxml2.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 219D5541149ED645004464AE /* libxml2.2.dylib */; }; + 219D5543149ED645004464AE /* libxml2.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 219D5541149ED645004464AE /* libxml2.2.dylib */; }; + 21A57F4C145B2AE100939099 /* CryptoAlg.c in Sources */ = {isa = PBXBuildFile; fileRef = 21A57F4A145B2AE100939099 /* CryptoAlg.c */; }; + 21A57F4D145B2AE100939099 /* CryptoAlg.c in Sources */ = {isa = PBXBuildFile; fileRef = 21A57F4A145B2AE100939099 /* CryptoAlg.c */; }; + 21A57F4E145B2AE100939099 /* CryptoAlg.h in Headers */ = {isa = PBXBuildFile; fileRef = 21A57F4B145B2AE100939099 /* CryptoAlg.h */; }; + 21A57F4F145B2AE100939099 /* CryptoAlg.h in Headers */ = {isa = PBXBuildFile; fileRef = 21A57F4B145B2AE100939099 /* CryptoAlg.h */; }; + 21A57F53145B2B1400939099 /* CryptoSupport.c in Sources */ = {isa = PBXBuildFile; fileRef = 21A57F51145B2B1400939099 /* CryptoSupport.c */; }; + 21A57F54145B2B1400939099 /* CryptoSupport.c in Sources */ = {isa = PBXBuildFile; fileRef = 21A57F51145B2B1400939099 /* CryptoSupport.c */; }; + 21A57F55145B2B1400939099 /* CryptoSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 21A57F52145B2B1400939099 /* CryptoSupport.h */; }; + 21A57F56145B2B1400939099 /* CryptoSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 21A57F52145B2B1400939099 /* CryptoSupport.h */; }; + 21DCD05C1461B23700702FC8 /* CryptoAlg.c in Sources */ = {isa = PBXBuildFile; fileRef = 21A57F4A145B2AE100939099 /* CryptoAlg.c */; }; + 21DCD05D1461B23700702FC8 /* CryptoAlg.h in Sources */ = {isa = PBXBuildFile; fileRef = 21A57F4B145B2AE100939099 /* CryptoAlg.h */; }; 2E0405F50C3195F700F13B59 /* helper.c in Sources */ = {isa = PBXBuildFile; fileRef = 2E0405F40C3195F700F13B59 /* helper.c */; }; 2E0405F60C31961100F13B59 /* helpermsg.defs in Sources */ = {isa = PBXBuildFile; fileRef = 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */; settings = {ATTRIBUTES = (Server, Client, ); COMPILER_FLAGS = "-Wno-error"; }; }; 2E0406150C3197CB00F13B59 /* libbsm.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2E0406140C3197CB00F13B59 /* libbsm.dylib */; }; @@ -144,13 +162,9 @@ 2EDC5E730C39EA640092701B /* helper-server.h in Headers */ = {isa = PBXBuildFile; fileRef = 2EDC5E720C39EA640092701B /* helper-server.h */; }; 2EDC5E740C39EA640092701B /* helper-server.h in Headers */ = {isa = PBXBuildFile; fileRef = 2EDC5E720C39EA640092701B /* helper-server.h */; }; 2EDC5E750C39EA640092701B /* helper-server.h in Headers */ = {isa = PBXBuildFile; fileRef = 2EDC5E720C39EA640092701B /* helper-server.h */; }; + 4A7B9E8014FDA25000B84CC1 /* mDNSResponder.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4A7B9E7E14FDA1BB00B84CC1 /* mDNSResponder.plist */; }; + 4A7B9E8214FDA26C00B84CC1 /* mDNSResponder.txt in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4A7B9E7C14FDA19F00B84CC1 /* mDNSResponder.txt */; }; 4AAE0C9A0C68EA81003882A5 /* mDNSResponderHelper.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4AAE0C7A0C68E97F003882A5 /* mDNSResponderHelper.8 */; }; - 4AE9B04B0F39448B0080B59D /* safe_vproc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4AE9B0480F39448B0080B59D /* safe_vproc.c */; }; - 4AE9B04C0F39448B0080B59D /* safe_vproc.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AE9B0490F39448B0080B59D /* safe_vproc.h */; }; - 4AE9B0920F3A52A10080B59D /* safe_vproc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4AE9B0480F39448B0080B59D /* safe_vproc.c */; }; - 4AE9B0930F3A52A20080B59D /* safe_vproc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4AE9B0480F39448B0080B59D /* safe_vproc.c */; }; - 4AE9B0940F3A52AE0080B59D /* safe_vproc.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AE9B0490F39448B0080B59D /* safe_vproc.h */; }; - 4AE9B0950F3A52AE0080B59D /* safe_vproc.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AE9B0490F39448B0080B59D /* safe_vproc.h */; }; 4BD2B63A134FE09F002B96D5 /* P2PPacketFilter.c in Sources */ = {isa = PBXBuildFile; fileRef = 4BD2B638134FE09F002B96D5 /* P2PPacketFilter.c */; }; 4BD2B63B134FE09F002B96D5 /* P2PPacketFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BD2B639134FE09F002B96D5 /* P2PPacketFilter.h */; }; D284BE530ADD80740027CCDF /* DNSServiceDiscoveryDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 6575FBFF022EAFBA00000109 /* DNSServiceDiscoveryDefines.h */; }; @@ -195,9 +209,6 @@ D284BE8E0ADD80800027CCDF /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65713D46025A293200000109 /* SystemConfiguration.framework */; }; D284BE8F0ADD80800027CCDF /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 00CA213D02786FC30CCA2C71 /* IOKit.framework */; }; D284BE900ADD80800027CCDF /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F869685066EE02400D2A2DC /* Security.framework */; }; - D284BE9A0ADD808B0027CCDF /* SamplemDNSClient.c in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC20022EB7AA00000109 /* SamplemDNSClient.c */; }; - D284BE9C0ADD808B0027CCDF /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */; }; - D284BE9F0ADD808B0027CCDF /* mDNS.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FF0E0B5D065ADC7600FE4D9C /* mDNS.1 */; }; D284BEA80ADD80920027CCDF /* dns-sd.c in Sources */ = {isa = PBXBuildFile; fileRef = FF1C919F07021E3F001048AB /* dns-sd.c */; }; D284BEAC0ADD80920027CCDF /* dns-sd.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FF1C919D07021D77001048AB /* dns-sd.1 */; }; D284BEB70ADD809A0027CCDF /* JNISupport.c in Sources */ = {isa = PBXBuildFile; fileRef = DB2CC44B0662DD1100335AB3 /* JNISupport.c */; }; @@ -274,10 +285,10 @@ fileType = sourcecode.yacc; isEditable = 1; outputFiles = ( - "$(DERIVED_FILE_DIR)/dnsextd_parser.h", - "$(DERIVED_FILE_DIR)/dnsextd_parser.c", + "$(DERIVED_FILE_DIR)/$(INPUT_FILE_BASE).h", + "$(DERIVED_FILE_DIR)/$(INPUT_FILE_BASE).c", ); - script = "/usr/bin/bison -o ${DERIVED_FILE_DIR}/${INPUT_FILE_BASE}.c -d ${INPUT_FILE_PATH}"; + script = "echo NOOP yacc ${INPUT_FILE_PATH}"; }; D284BFB80ADD8E510027CCDF /* PBXBuildRule */ = { isa = PBXBuildRule; @@ -285,7 +296,7 @@ fileType = sourcecode.lex; isEditable = 1; outputFiles = ( - "$(DERIVED_FILE_DIR)/dnsextd_lexer.c", + "$(DERIVED_FILE_DIR)/$(INPUT_FILE_BASE).c", ); script = "/usr/bin/flex -i -o${DERIVED_FILE_DIR}/${INPUT_FILE_BASE}.c ${INPUT_FILE_PATH}"; }; @@ -383,20 +394,6 @@ remoteGlobalIDString = 4AE471670EAFF81900A6C5AD; remoteInfo = dns_sd.jar; }; - D284BF250ADD814F0027CCDF /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D284BE970ADD808B0027CCDF; - remoteInfo = "mDNS command-line tool"; - }; - D284BF290ADD81530027CCDF /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D284BEB20ADD809A0027CCDF; - remoteInfo = libjdns_sd.jnilib; - }; D284BF2B0ADD815A0027CCDF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; @@ -456,33 +453,43 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - 4AAE0C5A0C68E6EC003882A5 /* CopyFiles */ = { + 4A7B9E7F14FDA21B00B84CC1 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = /usr/share/man/man8; + dstPath = /usr/local/OpenSourceVersions; dstSubfolderSpec = 0; files = ( - 4AAE0C9A0C68EA81003882A5 /* mDNSResponderHelper.8 in CopyFiles */, + 4A7B9E8014FDA25000B84CC1 /* mDNSResponder.plist in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 1; }; - D284BE6A0ADD80740027CCDF /* CopyFiles */ = { + 4A7B9E8114FDA25500B84CC1 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/local/OpenSourceLicenses; + dstSubfolderSpec = 0; + files = ( + 4A7B9E8214FDA26C00B84CC1 /* mDNSResponder.txt in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 4AAE0C5A0C68E6EC003882A5 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; dstPath = /usr/share/man/man8; dstSubfolderSpec = 0; files = ( - D284BE6B0ADD80740027CCDF /* mDNSResponder.8 in CopyFiles */, + 4AAE0C9A0C68EA81003882A5 /* mDNSResponderHelper.8 in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 1; }; - D284BE9E0ADD808B0027CCDF /* CopyFiles */ = { + D284BE6A0ADD80740027CCDF /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = /usr/share/man/man1; + dstPath = /usr/share/man/man8; dstSubfolderSpec = 0; files = ( - D284BE9F0ADD808B0027CCDF /* mDNS.1 in CopyFiles */, + D284BE6B0ADD80740027CCDF /* mDNSResponder.8 in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 1; }; @@ -522,12 +529,21 @@ 000753D303367C1C0CCA2C71 /* mDNSMacOSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mDNSMacOSX.h; sourceTree = ""; }; 00CA213D02786FC30CCA2C71 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = ""; }; 09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = ""; }; + 2124FA2B1471E98C0021D7BB /* nsec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nsec.h; path = ../mDNSCore/nsec.h; sourceTree = ""; }; + 2124FA2F1471E9B50021D7BB /* dnssec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dnssec.h; path = ../mDNSCore/dnssec.h; sourceTree = ""; }; + 2124FA321471E9DE0021D7BB /* nsec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nsec.c; path = ../mDNSCore/nsec.c; sourceTree = ""; }; + 213BDC6C147319F400000896 /* dnssec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dnssec.c; path = ../mDNSCore/dnssec.c; sourceTree = ""; }; 213FB21812028A7A002B3A08 /* BonjourEvents.plugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BonjourEvents.plugin; sourceTree = BUILT_PRODUCTS_DIR; }; 213FB22C12028B53002B3A08 /* BonjourEvents.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = BonjourEvents.c; sourceTree = ""; }; 213FB22D12028B53002B3A08 /* BonjourEvents-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "BonjourEvents-Info.plist"; sourceTree = ""; }; 2141DD1D123FFCDB0086D23E /* libdns_sd.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdns_sd.a; sourceTree = BUILT_PRODUCTS_DIR; }; 2141DD24123FFD0F0086D23E /* libdns_sd_debug.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdns_sd_debug.a; sourceTree = BUILT_PRODUCTS_DIR; }; 2141DD2A123FFD2C0086D23E /* libdns_sd_profile.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdns_sd_profile.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 219D5541149ED645004464AE /* libxml2.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.2.dylib; path = SDKs/MacOSX10.8.sdk/usr/lib/libxml2.2.dylib; sourceTree = DEVELOPER_DIR; }; + 21A57F4A145B2AE100939099 /* CryptoAlg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = CryptoAlg.c; path = ../mDNSCore/CryptoAlg.c; sourceTree = ""; }; + 21A57F4B145B2AE100939099 /* CryptoAlg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CryptoAlg.h; path = ../mDNSCore/CryptoAlg.h; sourceTree = ""; }; + 21A57F51145B2B1400939099 /* CryptoSupport.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CryptoSupport.c; sourceTree = ""; }; + 21A57F52145B2B1400939099 /* CryptoSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptoSupport.h; sourceTree = ""; }; 21F432971134AA6800581B69 /* WebFilterDNS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebFilterDNS.framework; path = /System/Library/PrivateFrameworks/WebFilterDNS.framework; sourceTree = ""; }; 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.mig; path = helpermsg.defs; sourceTree = ""; }; 2E0405F00C31955500F13B59 /* mDNSResponderHelper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mDNSResponderHelper; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -542,13 +558,13 @@ 2EDC5E720C39EA640092701B /* helper-server.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = "helper-server.h"; sourceTree = ""; }; 4A2E69DD0F5475A3004A87B0 /* uds_daemon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = uds_daemon.h; path = ../mDNSShared/uds_daemon.h; sourceTree = SOURCE_ROOT; }; 4A3600DF0F34F8CD00453EFB /* DeviceToDeviceManager.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DeviceToDeviceManager.framework; path = /System/Library/PrivateFrameworks/DeviceToDeviceManager.framework; sourceTree = ""; }; + 4A7B9E7C14FDA19F00B84CC1 /* mDNSResponder.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = mDNSResponder.txt; sourceTree = ""; }; + 4A7B9E7E14FDA1BB00B84CC1 /* mDNSResponder.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = mDNSResponder.plist; sourceTree = ""; }; 4A8202510C56C36500DDFD48 /* ipsec_strerror.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ipsec_strerror.h; sourceTree = ""; }; 4A8202520C56C36500DDFD48 /* libpfkey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libpfkey.h; sourceTree = ""; }; 4A8202530C56C36600DDFD48 /* pfkey.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pfkey.c; sourceTree = ""; }; 4AAE0C7A0C68E97F003882A5 /* mDNSResponderHelper.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = mDNSResponderHelper.8; sourceTree = SOURCE_ROOT; }; 4ADB5F230F6AB9F400B95BF3 /* helper-entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "helper-entitlements.plist"; sourceTree = ""; }; - 4AE9B0480F39448B0080B59D /* safe_vproc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = safe_vproc.c; sourceTree = ""; }; - 4AE9B0490F39448B0080B59D /* safe_vproc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = safe_vproc.h; sourceTree = ""; }; 4BD2B638134FE09F002B96D5 /* P2PPacketFilter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = P2PPacketFilter.c; sourceTree = ""; }; 4BD2B639134FE09F002B96D5 /* P2PPacketFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = P2PPacketFilter.h; sourceTree = ""; }; 654BE64F02B63B93000001D1 /* mDNSEmbeddedAPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mDNSEmbeddedAPI.h; path = ../mDNSCore/mDNSEmbeddedAPI.h; sourceTree = ""; }; @@ -568,7 +584,6 @@ 7FC8F9D406D14E66007E879D /* LegacyNATTraversal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = LegacyNATTraversal.c; sourceTree = SOURCE_ROOT; }; D284BE730ADD80740027CCDF /* mDNSResponder */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mDNSResponder; sourceTree = BUILT_PRODUCTS_DIR; }; D284BE950ADD80800027CCDF /* mDNSResponder.debug */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mDNSResponder.debug; sourceTree = BUILT_PRODUCTS_DIR; }; - D284BEA30ADD808B0027CCDF /* mDNS */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mDNS; sourceTree = BUILT_PRODUCTS_DIR; }; D284BEB00ADD80920027CCDF /* dns-sd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "dns-sd"; sourceTree = BUILT_PRODUCTS_DIR; }; D284BEBE0ADD809A0027CCDF /* libjdns_sd.jnilib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libjdns_sd.jnilib; sourceTree = BUILT_PRODUCTS_DIR; }; D284BED90ADD80A20027CCDF /* dnsextd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dnsextd; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -691,6 +706,7 @@ D284BE660ADD80740027CCDF /* SystemConfiguration.framework in Frameworks */, D284BE670ADD80740027CCDF /* IOKit.framework in Frameworks */, D284BE680ADD80740027CCDF /* Security.framework in Frameworks */, + 219D5542149ED645004464AE /* libxml2.2.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -702,14 +718,7 @@ D284BE8E0ADD80800027CCDF /* SystemConfiguration.framework in Frameworks */, D284BE8F0ADD80800027CCDF /* IOKit.framework in Frameworks */, D284BE900ADD80800027CCDF /* Security.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D284BE9B0ADD808B0027CCDF /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - D284BE9C0ADD808B0027CCDF /* CoreFoundation.framework in Frameworks */, + 219D5543149ED645004464AE /* libxml2.2.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -805,10 +814,16 @@ 08FB7795FE84155DC02AAC07 /* mDNS Server Sources */ = { isa = PBXGroup; children = ( + 213BDC6C147319F400000896 /* dnssec.c */, + 2124FA321471E9DE0021D7BB /* nsec.c */, + 2124FA2F1471E9B50021D7BB /* dnssec.h */, + 2124FA2B1471E98C0021D7BB /* nsec.h */, + 21A57F51145B2B1400939099 /* CryptoSupport.c */, + 21A57F52145B2B1400939099 /* CryptoSupport.h */, + 21A57F4A145B2AE100939099 /* CryptoAlg.c */, + 21A57F4B145B2AE100939099 /* CryptoAlg.h */, 4ADB5F230F6AB9F400B95BF3 /* helper-entitlements.plist */, 4A2E69DD0F5475A3004A87B0 /* uds_daemon.h */, - 4AE9B0480F39448B0080B59D /* safe_vproc.c */, - 4AE9B0490F39448B0080B59D /* safe_vproc.h */, 4AAE0C7A0C68E97F003882A5 /* mDNSResponderHelper.8 */, 2ECC11A50C4FEC3800CB1885 /* helpermsg-types.h */, 2E35528F0C3A95C100CA1CB7 /* helper-error.h */, @@ -840,6 +855,8 @@ FFF4F63A06CFE4DD00459EFD /* dnsextd.8 */, FFFF8F800C3307AC00722979 /* dnsextd.conf */, FF85880B0BD599F40080D89F /* mDNSResponder.sb */, + 4A7B9E7C14FDA19F00B84CC1 /* mDNSResponder.txt */, + 4A7B9E7E14FDA1BB00B84CC1 /* mDNSResponder.plist */, 7F18A9F60587CEF6001880B3 /* DNSCommon.c */, 7F18A9F70587CEF6001880B3 /* uDNS.c */, FF25794606C9A8BF00376F7B /* dnsextd.c */, @@ -859,6 +876,7 @@ 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */ = { isa = PBXGroup; children = ( + 219D5541149ED645004464AE /* libxml2.2.dylib */, 4A3600DF0F34F8CD00453EFB /* DeviceToDeviceManager.framework */, 2E8165F60C59835F00485EB2 /* libipsec.dylib */, 65713D46025A293200000109 /* SystemConfiguration.framework */, @@ -881,7 +899,6 @@ D284C04D0ADD95D30027CCDF /* Info-PreferencePane.plist */, D284BE730ADD80740027CCDF /* mDNSResponder */, D284BE950ADD80800027CCDF /* mDNSResponder.debug */, - D284BEA30ADD808B0027CCDF /* mDNS */, D284BEB00ADD80920027CCDF /* dns-sd */, D284BEBE0ADD809A0027CCDF /* libjdns_sd.jnilib */, D284BED90ADD80A20027CCDF /* dnsextd */, @@ -1020,7 +1037,6 @@ 2ECC11A80C4FEC3800CB1885 /* helpermsg-types.h in Headers */, 2E8165E80C5980E300485EB2 /* libpfkey.h in Headers */, 2E8165EA0C5980F700485EB2 /* ipsec_strerror.h in Headers */, - 4AE9B04C0F39448B0080B59D /* safe_vproc.h in Headers */, 4BD2B63B134FE09F002B96D5 /* P2PPacketFilter.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1035,7 +1051,10 @@ 2EDC5E750C39EA640092701B /* helper-server.h in Headers */, 2E3552900C3A95C100CA1CB7 /* helper-error.h in Headers */, 2ECC11A60C4FEC3800CB1885 /* helpermsg-types.h in Headers */, - 4AE9B0950F3A52AE0080B59D /* safe_vproc.h in Headers */, + 21A57F4E145B2AE100939099 /* CryptoAlg.h in Headers */, + 21A57F55145B2B1400939099 /* CryptoSupport.h in Headers */, + 2124FA2C1471E98C0021D7BB /* nsec.h in Headers */, + 2124FA301471E9B50021D7BB /* dnssec.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1052,14 +1071,10 @@ 2EDC5E740C39EA640092701B /* helper-server.h in Headers */, 2E3552910C3A95C100CA1CB7 /* helper-error.h in Headers */, 2ECC11A70C4FEC3800CB1885 /* helpermsg-types.h in Headers */, - 4AE9B0940F3A52AE0080B59D /* safe_vproc.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D284BE980ADD808B0027CCDF /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( + 21A57F4F145B2AE100939099 /* CryptoAlg.h in Headers */, + 21A57F56145B2B1400939099 /* CryptoSupport.h in Headers */, + 2124FA2D1471E98C0021D7BB /* nsec.h in Headers */, + 2124FA311471E9B50021D7BB /* dnssec.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1240,6 +1255,8 @@ D284BE640ADD80740027CCDF /* Frameworks */, D284BE690ADD80740027CCDF /* Rez */, D284BE6A0ADD80740027CCDF /* CopyFiles */, + 4A7B9E7F14FDA21B00B84CC1 /* CopyFiles */, + 4A7B9E8114FDA25500B84CC1 /* CopyFiles */, D284BE6C0ADD80740027CCDF /* ShellScript */, ); buildRules = ( @@ -1271,26 +1288,6 @@ productReference = D284BE950ADD80800027CCDF /* mDNSResponder.debug */; productType = "com.apple.product-type.tool"; }; - D284BE970ADD808B0027CCDF /* mDNS tool */ = { - isa = PBXNativeTarget; - buildConfigurationList = D284BEA00ADD808B0027CCDF /* Build configuration list for PBXNativeTarget "mDNS tool" */; - buildPhases = ( - D284BE980ADD808B0027CCDF /* Headers */, - D284BE990ADD808B0027CCDF /* Sources */, - D284BE9B0ADD808B0027CCDF /* Frameworks */, - D284BE9D0ADD808B0027CCDF /* Rez */, - D284BE9E0ADD808B0027CCDF /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "mDNS tool"; - productInstallPath = /usr/bin; - productName = "mDNS command-line tool"; - productReference = D284BEA30ADD808B0027CCDF /* mDNS */; - productType = "com.apple.product-type.tool"; - }; D284BEA50ADD80920027CCDF /* dns-sd tool */ = { isa = PBXNativeTarget; buildConfigurationList = D284BEAD0ADD80920027CCDF /* Build configuration list for PBXNativeTarget "dns-sd tool" */; @@ -1337,9 +1334,9 @@ buildConfigurationList = D284BED60ADD80A20027CCDF /* Build configuration list for PBXNativeTarget "dnsextd" */; buildPhases = ( D284BEC20ADD80A20027CCDF /* Headers */, + 4A4EE3A413CB8E82005C624B /* Build yacc file into derived source files */, D284BEC40ADD80A20027CCDF /* Sources */, D284BECE0ADD80A20027CCDF /* Frameworks */, - D284BED30ADD80A20027CCDF /* Rez */, D284BED40ADD80A20027CCDF /* CopyFiles */, FFFF8F770C32F0FD00722979 /* CopyFiles */, FF37FAAD0BC581780044A5CF /* ShellScript */, @@ -1473,7 +1470,6 @@ D284BE500ADD80740027CCDF /* mDNSResponder */, D284BE750ADD80800027CCDF /* mDNSResponder debug */, 2E0405EF0C31955500F13B59 /* mDNSResponderHelper */, - D284BE970ADD808B0027CCDF /* mDNS tool */, D284BEA50ADD80920027CCDF /* dns-sd tool */, 4AE471670EAFF81900A6C5AD /* dns_sd.jar */, D284BEB20ADD809A0027CCDF /* libjdns_sd.jnilib */, @@ -1540,13 +1536,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - D284BE9D0ADD808B0027CCDF /* Rez */ = { - isa = PBXRezBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; D284BEAA0ADD80920027CCDF /* Rez */ = { isa = PBXRezBuildPhase; buildActionMask = 2147483647; @@ -1561,13 +1550,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - D284BED30ADD80A20027CCDF /* Rez */ = { - isa = PBXRezBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; D284BEE40ADD80A70027CCDF /* Rez */ = { isa = PBXRezBuildPhase; buildActionMask = 2147483647; @@ -1637,6 +1619,23 @@ shellPath = /bin/sh; shellScript = "#if we are building for simulator, change the installation path\nif [ \"${RC_ProjectName%_Sim}\" != \"${RC_ProjectName}\" ] ; then\n\tDSTROOT=${DSTROOT}${SDKROOT}\nfi\nmkdir -p \"$DSTROOT/usr/include/DNSServiceDiscovery\"\ncp $SRCROOT/DNSServiceDiscovery.h $DSTROOT/usr/include/DNSServiceDiscovery\nsed 's/\\(^#define _DNS_SD_LIBDISPATCH \\)0$/\\1 1/' \"$SRCROOT/../mDNSShared/dns_sd.h\" > \"$DSTROOT/usr/include/dns_sd.h\""; }; + 4A4EE3A413CB8E82005C624B /* Build yacc file into derived source files */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/../mDNSShared/dnsextd_parser.y", + ); + name = "Build yacc file into derived source files"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/dnsextd_parser.c", + "$(DERIVED_FILE_DIR)/dnsextd_parser.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/usr/bin/bison -o ${SCRIPT_OUTPUT_FILE_0} -d ${SCRIPT_INPUT_FILE_0}"; + }; D284BE510ADD80740027CCDF /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1744,7 +1743,6 @@ 2E0405F60C31961100F13B59 /* helpermsg.defs in Sources */, 2E96A51D0C39BDAC0087C4D2 /* helper-main.c in Sources */, 2E8165E90C5980EE00485EB2 /* pfkey.c in Sources */, - 4AE9B04B0F39448B0080B59D /* safe_vproc.c in Sources */, 4BD2B63A134FE09F002B96D5 /* P2PPacketFilter.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1768,7 +1766,10 @@ D284BE630ADD80740027CCDF /* daemon.c in Sources */, 2E04061F0C3198B700F13B59 /* helpermsg.defs in Sources */, 2E96A5320C39C1A50087C4D2 /* helper-stubs.c in Sources */, - 4AE9B0930F3A52A20080B59D /* safe_vproc.c in Sources */, + 21A57F4C145B2AE100939099 /* CryptoAlg.c in Sources */, + 21A57F53145B2B1400939099 /* CryptoSupport.c in Sources */, + 2124FA331471E9DE0021D7BB /* nsec.c in Sources */, + 213BDC6D147319F400000896 /* dnssec.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1791,15 +1792,10 @@ D284BE8B0ADD80800027CCDF /* daemon.c in Sources */, 2E0406200C3198B700F13B59 /* helpermsg.defs in Sources */, 2E96A5300C39C1A50087C4D2 /* helper-stubs.c in Sources */, - 4AE9B0920F3A52A10080B59D /* safe_vproc.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D284BE990ADD808B0027CCDF /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D284BE9A0ADD808B0027CCDF /* SamplemDNSClient.c in Sources */, + 21A57F4D145B2AE100939099 /* CryptoAlg.c in Sources */, + 21A57F54145B2B1400939099 /* CryptoSupport.c in Sources */, + 2124FA341471E9DE0021D7BB /* nsec.c in Sources */, + 213BDC6E147319F400000896 /* dnssec.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1824,6 +1820,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 21DCD05C1461B23700702FC8 /* CryptoAlg.c in Sources */, + 21DCD05D1461B23700702FC8 /* CryptoAlg.h in Sources */, D284BEC50ADD80A20027CCDF /* DNSCommon.c in Sources */, D284BEC60ADD80A20027CCDF /* DNSDigest.c in Sources */, D284BEC70ADD80A20027CCDF /* dnsextd.c in Sources */, @@ -1964,16 +1962,6 @@ target = 4AE471670EAFF81900A6C5AD /* dns_sd.jar */; targetProxy = 4AE471690EAFF83800A6C5AD /* PBXContainerItemProxy */; }; - D284BF260ADD814F0027CCDF /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D284BE970ADD808B0027CCDF /* mDNS tool */; - targetProxy = D284BF250ADD814F0027CCDF /* PBXContainerItemProxy */; - }; - D284BF2A0ADD81530027CCDF /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D284BEB20ADD809A0027CCDF /* libjdns_sd.jnilib */; - targetProxy = D284BF290ADD81530027CCDF /* PBXContainerItemProxy */; - }; D284BF2C0ADD815A0027CCDF /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = D284BEBF0ADD80A20027CCDF /* dnsextd */; @@ -2148,6 +2136,7 @@ isa = XCBuildConfiguration; buildSettings = { "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "helper-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)"; CONFIGURATION_TEMP_DIR = "$(PROJECT_TEMP_DIR)"; COPY_PHASE_STRIP = NO; @@ -2170,12 +2159,13 @@ "-lipsec", ); "OTHER_LDFLAGS[sdk=iphoneos*] [arch=*]" = "-lipsec -Wl,-pie"; - "OTHER_LDFLAGS[sdk=macosx10.7][arch=*]" = ( + "OTHER_LDFLAGS[sdk=macosx*][arch=*]" = ( "-lipsec", "-Wl,-pie", ); PREBINDING = NO; PRODUCT_NAME = mDNSResponderHelper; + PROVISIONING_PROFILE = ""; }; name = Development; }; @@ -2241,6 +2231,8 @@ D284BE6E0ADD80740027CCDF /* Development */ = { isa = XCBuildConfiguration; buildSettings = { + "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "mDNSResponder-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; CONFIGURATION_BUILD_DIR = "${BUILD_DIR}"; CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; FRAMEWORK_SEARCH_PATHS = ( @@ -2250,10 +2242,12 @@ GCC_DYNAMIC_NO_PIC = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; HEADER_SEARCH_PATHS = ( + "$(SDKROOT)/$(SYSTEM_LIBRARY_DIR)/Frameworks/Security.framework/PrivateHeaders", ../mDNSShared, "${SYSTEM_LIBRARY_DIR}/Frameworks/System.framework/PrivateHeaders", "${APPLE_INTERNAL_DEVELOPER_DIR}/Headers", "${CONFIGURATION_TEMP_DIR}", + "$(SDKROOT)/usr/include/libxml2", ); INSTALL_PATH = /usr/sbin; LIBRARY_SEARCH_PATHS = "\"${CONFIGURATION_TEMP_DIR}\""; @@ -2273,8 +2267,7 @@ "-weak_framework", DeviceToDeviceManager, ); - "OTHER_LDFLAGS[sdk=macosx10.6][arch=*]" = "-lAWACS"; - "OTHER_LDFLAGS[sdk=macosx10.7][arch=*]" = ( + "OTHER_LDFLAGS[sdk=macosx*][arch=*]" = ( "-Wl,-pie", "-lAWACS", "-weak_framework", @@ -2282,8 +2275,10 @@ "-weak_framework", DeviceToDeviceManager, ); + "OTHER_LDFLAGS[sdk=macosx10.6][arch=*]" = "-lAWACS"; OTHER_REZFLAGS = ""; PRODUCT_NAME = mDNSResponder; + PROVISIONING_PROFILE = ""; REZ_EXECUTABLE = YES; }; name = Development; @@ -2305,10 +2300,12 @@ "MDNS_DEBUGMSGS=1", ); HEADER_SEARCH_PATHS = ( + "$(SDKROOT)/$(SYSTEM_LIBRARY_DIR)/Frameworks/Security.framework/PrivateHeaders", ../mDNSShared, "${SYSTEM_LIBRARY_DIR}/Frameworks/System.framework/PrivateHeaders", "${APPLE_INTERNAL_DEVELOPER_DIR}/Headers", "${CONFIGURATION_TEMP_DIR}", + "$(SDKROOT)/usr/include/libxml2", ); LIBRARY_SEARCH_PATHS = "\"${CONFIGURATION_TEMP_DIR}\""; MACOSX_DEPLOYMENT_TARGET = 10.5; @@ -2322,8 +2319,7 @@ "-weak_framework", DeviceToDeviceManager, ); - "OTHER_LDFLAGS[sdk=macosx10.6][arch=*]" = "-lAWACS"; - "OTHER_LDFLAGS[sdk=macosx10.7][arch=*]" = ( + "OTHER_LDFLAGS[sdk=macosx*][arch=*]" = ( "-Wl,-pie", "-lAWACS", "-weak_framework", @@ -2331,6 +2327,7 @@ "-weak_framework", DeviceToDeviceManager, ); + "OTHER_LDFLAGS[sdk=macosx10.6][arch=*]" = "-lAWACS"; OTHER_REZFLAGS = ""; PRODUCT_NAME = mDNSResponder.debug; REZ_EXECUTABLE = YES; @@ -2344,23 +2341,6 @@ }; name = Development; }; - D284BEA10ADD808B0027CCDF /* Development */ = { - isa = XCBuildConfiguration; - buildSettings = { - CONFIGURATION_BUILD_DIR = "${BUILD_DIR}"; - CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; - GCC_DYNAMIC_NO_PIC = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - INSTALL_PATH = /usr/bin; - OTHER_CFLAGS = "-no-cpp-precomp"; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = mDNS; - REZ_EXECUTABLE = YES; - SECTORDER_FLAGS = ""; - }; - name = Development; - }; D284BEAE0ADD80920027CCDF /* Development */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2417,13 +2397,12 @@ GCC_DYNAMIC_NO_PIC = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; HEADER_SEARCH_PATHS = ( + "${SYSTEM_LIBRARY_DIR}/Frameworks/System.framework/PrivateHeaders", "${APPLE_INTERNAL_DEVELOPER_DIR}/Headers", "${CONFIGURATION_TEMP_DIR}", + /System/Library/Frameworks/System.Framework/PrivateHeaders, ); INSTALL_PATH = /usr/sbin; - LEX = /usr/bin/flex; - LEXFLAGS = ""; - LEX_CASE_INSENSITIVE_SCANNER = YES; LIBRARY_SEARCH_PATHS = "\"${CONFIGURATION_TEMP_DIR}\""; MACOSX_DEPLOYMENT_TARGET = 10.5; OTHER_CFLAGS = ( @@ -2431,12 +2410,9 @@ "-UAPPLE_OSX_mDNSResponder", ); OTHER_LDFLAGS = ""; - "OTHER_LDFLAGS[sdk=macosx10.7][arch=*]" = "-Wl,-pie"; - OTHER_REZFLAGS = ""; + "OTHER_LDFLAGS[sdk=macosx*][arch=*]" = "-Wl,-pie"; PRODUCT_NAME = dnsextd; - REZ_EXECUTABLE = YES; SECTORDER_FLAGS = ""; - YACC = "/usr/bin/bison -y"; }; name = Development; }; @@ -2692,14 +2668,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Development; }; - D284BEA00ADD808B0027CCDF /* Build configuration list for PBXNativeTarget "mDNS tool" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D284BEA10ADD808B0027CCDF /* Development */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; - }; D284BEAD0ADD80920027CCDF /* Build configuration list for PBXNativeTarget "dns-sd tool" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/mDNSMacOSX/pfkey.c b/mDNSMacOSX/pfkey.c index 9d75414..839c8d7 100644 --- a/mDNSMacOSX/pfkey.c +++ b/mDNSMacOSX/pfkey.c @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -77,26 +77,26 @@ static int findsupportedmap __P((int)); static int setsupportedmap __P((struct sadb_supported *)); static struct sadb_alg *findsupportedalg __P((u_int, u_int)); static int pfkey_send_x1 __P((int, u_int, u_int, u_int, struct sockaddr *, - struct sockaddr *, u_int32_t, u_int32_t, u_int, caddr_t, - u_int, u_int, u_int, u_int, u_int, u_int32_t, u_int32_t, - u_int32_t, u_int32_t, u_int32_t)); + struct sockaddr *, u_int32_t, u_int32_t, u_int, caddr_t, + u_int, u_int, u_int, u_int, u_int, u_int32_t, u_int32_t, + u_int32_t, u_int32_t, u_int32_t)); static int pfkey_send_x2 __P((int, u_int, u_int, u_int, - struct sockaddr *, struct sockaddr *, u_int32_t)); + struct sockaddr *, struct sockaddr *, u_int32_t)); static int pfkey_send_x3 __P((int, u_int, u_int)); static int pfkey_send_x4 __P((int, u_int, struct sockaddr *, u_int, - struct sockaddr *, u_int, u_int, u_int64_t, u_int64_t, - char *, int, u_int32_t)); + struct sockaddr *, u_int, u_int, u_int64_t, u_int64_t, + char *, int, u_int32_t)); static int pfkey_send_x5 __P((int, u_int, u_int32_t)); static caddr_t pfkey_setsadbmsg __P((caddr_t, caddr_t, u_int, u_int, - u_int, u_int32_t, pid_t)); + u_int, u_int32_t, pid_t)); static caddr_t pfkey_setsadbsa __P((caddr_t, caddr_t, u_int32_t, u_int, - u_int, u_int, u_int32_t)); + u_int, u_int, u_int32_t)); static caddr_t pfkey_setsadbaddr __P((caddr_t, caddr_t, u_int, - struct sockaddr *, u_int, u_int)); + struct sockaddr *, u_int, u_int)); static caddr_t pfkey_setsadbkey __P((caddr_t, caddr_t, u_int, caddr_t, u_int)); static caddr_t pfkey_setsadblifetime __P((caddr_t, caddr_t, u_int, u_int32_t, - u_int32_t, u_int32_t, u_int32_t)); + u_int32_t, u_int32_t, u_int32_t)); static caddr_t pfkey_setsadbxsa2 __P((caddr_t, caddr_t, u_int32_t, u_int32_t)); /* @@ -105,90 +105,90 @@ static caddr_t pfkey_setsadbxsa2 __P((caddr_t, caddr_t, u_int32_t, u_int32_t)); static struct sadb_supported *ipsec_supported[] = { NULL, NULL, NULL, }; static int supported_map[] = { - SADB_SATYPE_AH, - SADB_SATYPE_ESP, - SADB_X_SATYPE_IPCOMP, + SADB_SATYPE_AH, + SADB_SATYPE_ESP, + SADB_X_SATYPE_IPCOMP, }; static int findsupportedmap(satype) - int satype; +int satype; { - int i; + int i; - for (i = 0; (unsigned int)i < sizeof(supported_map)/sizeof(supported_map[0]); i++) - if (supported_map[i] == satype) - return i; - return -1; + for (i = 0; (unsigned int)i < sizeof(supported_map)/sizeof(supported_map[0]); i++) + if (supported_map[i] == satype) + return i; + return -1; } static struct sadb_alg * findsupportedalg(satype, alg_id) - u_int satype, alg_id; +u_int satype, alg_id; { - int algno; - int tlen; - caddr_t p; - - /* validity check */ - algno = findsupportedmap(satype); - if (algno == -1) { - __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; - return NULL; - } - if (ipsec_supported[algno] == NULL) { - __ipsec_errcode = EIPSEC_DO_GET_SUPP_LIST; - return NULL; - } - - tlen = ipsec_supported[algno]->sadb_supported_len - - sizeof(struct sadb_supported); - p = (caddr_t)(ipsec_supported[algno] + 1); - while (tlen > 0) { - if ((unsigned int)tlen < sizeof(struct sadb_alg)) { - /* invalid format */ - break; - } - if (((struct sadb_alg *)p)->sadb_alg_id == alg_id) - return (struct sadb_alg *)p; - - tlen -= sizeof(struct sadb_alg); - p += sizeof(struct sadb_alg); - } - - __ipsec_errcode = EIPSEC_NOT_SUPPORTED; - return NULL; + int algno; + int tlen; + caddr_t p; + + /* validity check */ + algno = findsupportedmap(satype); + if (algno == -1) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return NULL; + } + if (ipsec_supported[algno] == NULL) { + __ipsec_errcode = EIPSEC_DO_GET_SUPP_LIST; + return NULL; + } + + tlen = ipsec_supported[algno]->sadb_supported_len + - sizeof(struct sadb_supported); + p = (caddr_t)(ipsec_supported[algno] + 1); + while (tlen > 0) { + if ((unsigned int)tlen < sizeof(struct sadb_alg)) { + /* invalid format */ + break; + } + if (((struct sadb_alg *)p)->sadb_alg_id == alg_id) + return (struct sadb_alg *)p; + + tlen -= sizeof(struct sadb_alg); + p += sizeof(struct sadb_alg); + } + + __ipsec_errcode = EIPSEC_NOT_SUPPORTED; + return NULL; } static int setsupportedmap(sup) - struct sadb_supported *sup; +struct sadb_supported *sup; { - struct sadb_supported **ipsup; - - switch (sup->sadb_supported_exttype) { - case SADB_EXT_SUPPORTED_AUTH: - ipsup = &ipsec_supported[findsupportedmap(SADB_SATYPE_AH)]; - break; - case SADB_EXT_SUPPORTED_ENCRYPT: - ipsup = &ipsec_supported[findsupportedmap(SADB_SATYPE_ESP)]; - break; - default: - __ipsec_errcode = EIPSEC_INVAL_SATYPE; - return -1; - } - - if (*ipsup) - free(*ipsup); - - *ipsup = malloc(sup->sadb_supported_len); - if (!*ipsup) { - __ipsec_set_strerror(strerror(errno)); - return -1; - } - memcpy(*ipsup, sup, sup->sadb_supported_len); - - return 0; + struct sadb_supported **ipsup; + + switch (sup->sadb_supported_exttype) { + case SADB_EXT_SUPPORTED_AUTH: + ipsup = &ipsec_supported[findsupportedmap(SADB_SATYPE_AH)]; + break; + case SADB_EXT_SUPPORTED_ENCRYPT: + ipsup = &ipsec_supported[findsupportedmap(SADB_SATYPE_ESP)]; + break; + default: + __ipsec_errcode = EIPSEC_INVAL_SATYPE; + return -1; + } + + if (*ipsup) + free(*ipsup); + + *ipsup = malloc(sup->sadb_supported_len); + if (!*ipsup) { + __ipsec_set_strerror(strerror(errno)); + return -1; + } + memcpy(*ipsup, sup, sup->sadb_supported_len); + + return 0; } /* @@ -202,26 +202,26 @@ setsupportedmap(sup) */ int ipsec_check_keylen(supported, alg_id, keylen) - u_int supported; - u_int alg_id; - u_int keylen; +u_int supported; +u_int alg_id; +u_int keylen; { - int satype; - - /* validity check */ - switch (supported) { - case SADB_EXT_SUPPORTED_AUTH: - satype = SADB_SATYPE_AH; - break; - case SADB_EXT_SUPPORTED_ENCRYPT: - satype = SADB_SATYPE_ESP; - break; - default: - __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; - return -1; - } - - return ipsec_check_keylen2(satype, alg_id, keylen); + int satype; + + /* validity check */ + switch (supported) { + case SADB_EXT_SUPPORTED_AUTH: + satype = SADB_SATYPE_AH; + break; + case SADB_EXT_SUPPORTED_ENCRYPT: + satype = SADB_SATYPE_ESP; + break; + default: + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + + return ipsec_check_keylen2(satype, alg_id, keylen); } /* @@ -234,23 +234,23 @@ ipsec_check_keylen(supported, alg_id, keylen) */ int ipsec_check_keylen2(satype, alg_id, keylen) - u_int satype; - u_int alg_id; - u_int keylen; +u_int satype; +u_int alg_id; +u_int keylen; { - struct sadb_alg *alg; + struct sadb_alg *alg; - alg = findsupportedalg(satype, alg_id); - if (!alg) - return -1; + alg = findsupportedalg(satype, alg_id); + if (!alg) + return -1; - if (keylen < alg->sadb_alg_minbits || keylen > alg->sadb_alg_maxbits) { - __ipsec_errcode = EIPSEC_INVAL_KEYLEN; - return -1; - } + if (keylen < alg->sadb_alg_minbits || keylen > alg->sadb_alg_maxbits) { + __ipsec_errcode = EIPSEC_INVAL_KEYLEN; + return -1; + } - __ipsec_errcode = EIPSEC_NO_ERROR; - return 0; + __ipsec_errcode = EIPSEC_NO_ERROR; + return 0; } /* @@ -263,38 +263,38 @@ ipsec_check_keylen2(satype, alg_id, keylen) */ int ipsec_get_keylen(supported, alg_id, alg0) - u_int supported, alg_id; - struct sadb_alg *alg0; +u_int supported, alg_id; +struct sadb_alg *alg0; { - struct sadb_alg *alg; - u_int satype; - - /* validity check */ - if (!alg0) { - __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; - return -1; - } - - switch (supported) { - case SADB_EXT_SUPPORTED_AUTH: - satype = SADB_SATYPE_AH; - break; - case SADB_EXT_SUPPORTED_ENCRYPT: - satype = SADB_SATYPE_ESP; - break; - default: - __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; - return -1; - } - - alg = findsupportedalg(satype, alg_id); - if (!alg) - return -1; - - memcpy(alg0, alg, sizeof(*alg0)); - - __ipsec_errcode = EIPSEC_NO_ERROR; - return 0; + struct sadb_alg *alg; + u_int satype; + + /* validity check */ + if (!alg0) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + + switch (supported) { + case SADB_EXT_SUPPORTED_AUTH: + satype = SADB_SATYPE_AH; + break; + case SADB_EXT_SUPPORTED_ENCRYPT: + satype = SADB_SATYPE_ESP; + break; + default: + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + + alg = findsupportedalg(satype, alg_id); + if (!alg) + return -1; + + memcpy(alg0, alg, sizeof(*alg0)); + + __ipsec_errcode = EIPSEC_NO_ERROR; + return 0; } /* @@ -308,30 +308,30 @@ static u_int soft_lifetime_usetime_rate = PFKEY_SOFT_LIFETIME_RATE; u_int pfkey_set_softrate(type, rate) - u_int type, rate; +u_int type, rate; { - __ipsec_errcode = EIPSEC_NO_ERROR; - - if (rate > 100 || rate == 0) - rate = 100; - - switch (type) { - case SADB_X_LIFETIME_ALLOCATIONS: - soft_lifetime_allocations_rate = rate; - return 0; - case SADB_X_LIFETIME_BYTES: - soft_lifetime_bytes_rate = rate; - return 0; - case SADB_X_LIFETIME_ADDTIME: - soft_lifetime_addtime_rate = rate; - return 0; - case SADB_X_LIFETIME_USETIME: - soft_lifetime_usetime_rate = rate; - return 0; - } - - __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; - return 1; + __ipsec_errcode = EIPSEC_NO_ERROR; + + if (rate > 100 || rate == 0) + rate = 100; + + switch (type) { + case SADB_X_LIFETIME_ALLOCATIONS: + soft_lifetime_allocations_rate = rate; + return 0; + case SADB_X_LIFETIME_BYTES: + soft_lifetime_bytes_rate = rate; + return 0; + case SADB_X_LIFETIME_ADDTIME: + soft_lifetime_addtime_rate = rate; + return 0; + case SADB_X_LIFETIME_USETIME: + soft_lifetime_usetime_rate = rate; + return 0; + } + + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return 1; } /* @@ -340,20 +340,20 @@ pfkey_set_softrate(type, rate) */ u_int pfkey_get_softrate(type) - u_int type; +u_int type; { - switch (type) { - case SADB_X_LIFETIME_ALLOCATIONS: - return soft_lifetime_allocations_rate; - case SADB_X_LIFETIME_BYTES: - return soft_lifetime_bytes_rate; - case SADB_X_LIFETIME_ADDTIME: - return soft_lifetime_addtime_rate; - case SADB_X_LIFETIME_USETIME: - return soft_lifetime_usetime_rate; - } - - return ~0; + switch (type) { + case SADB_X_LIFETIME_ALLOCATIONS: + return soft_lifetime_allocations_rate; + case SADB_X_LIFETIME_BYTES: + return soft_lifetime_bytes_rate; + case SADB_X_LIFETIME_ADDTIME: + return soft_lifetime_addtime_rate; + case SADB_X_LIFETIME_USETIME: + return soft_lifetime_usetime_rate; + } + + return ~0; } /* @@ -364,124 +364,124 @@ pfkey_get_softrate(type) */ int pfkey_send_getspi(so, satype, mode, src, dst, min, max, reqid, seq) - int so; - u_int satype, mode; - struct sockaddr *src, *dst; - u_int32_t min, max, reqid, seq; +int so; +u_int satype, mode; +struct sockaddr *src, *dst; +u_int32_t min, max, reqid, seq; { - struct sadb_msg *newmsg; - caddr_t ep; - int len; - int need_spirange = 0; - caddr_t p; - int plen; - - /* validity check */ - if (src == NULL || dst == NULL) { - __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; - return -1; - } - if (src->sa_family != dst->sa_family) { - __ipsec_errcode = EIPSEC_FAMILY_MISMATCH; - return -1; - } - if (min > max || (min > 0 && min <= 255)) { - __ipsec_errcode = EIPSEC_INVAL_SPI; - return -1; - } - switch (src->sa_family) { - case AF_INET: - plen = sizeof(struct in_addr) << 3; - break; - case AF_INET6: - plen = sizeof(struct in6_addr) << 3; - break; - default: - __ipsec_errcode = EIPSEC_INVAL_FAMILY; - return -1; - } - - /* create new sadb_msg to send. */ - len = sizeof(struct sadb_msg) - + sizeof(struct sadb_x_sa2) - + sizeof(struct sadb_address) - + PFKEY_ALIGN8(src->sa_len) - + sizeof(struct sadb_address) - + PFKEY_ALIGN8(dst->sa_len); - - if (min > (u_int32_t)255 && max < (u_int32_t)~0) { - need_spirange++; - len += sizeof(struct sadb_spirange); - } - - if ((newmsg = CALLOC(len, struct sadb_msg *)) == NULL) { - __ipsec_set_strerror(strerror(errno)); - return -1; - } - ep = ((caddr_t)newmsg) + len; - - p = pfkey_setsadbmsg((caddr_t)newmsg, ep, SADB_GETSPI, - len, satype, seq, getpid()); - if (!p) { - free(newmsg); - return -1; - } - - p = pfkey_setsadbxsa2(p, ep, mode, reqid); - if (!p) { - free(newmsg); - return -1; - } - - /* set sadb_address for source */ - p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_SRC, src, plen, - IPSEC_ULPROTO_ANY); - if (!p) { - free(newmsg); - return -1; - } - - /* set sadb_address for destination */ - p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_DST, dst, plen, - IPSEC_ULPROTO_ANY); - if (!p) { - free(newmsg); - return -1; - } - - /* proccessing spi range */ - if (need_spirange) { - struct sadb_spirange spirange; - - if (p + sizeof(spirange) > ep) { - free(newmsg); - return -1; - } - - memset(&spirange, 0, sizeof(spirange)); - spirange.sadb_spirange_len = PFKEY_UNIT64(sizeof(spirange)); - spirange.sadb_spirange_exttype = SADB_EXT_SPIRANGE; - spirange.sadb_spirange_min = min; - spirange.sadb_spirange_max = max; - - memcpy(p, &spirange, sizeof(spirange)); - - p += sizeof(spirange); - } - if (p != ep) { - free(newmsg); - return -1; - } - - /* send message */ - len = pfkey_send(so, newmsg, len); - free(newmsg); - - if (len < 0) - return -1; - - __ipsec_errcode = EIPSEC_NO_ERROR; - return len; + struct sadb_msg *newmsg; + caddr_t ep; + int len; + int need_spirange = 0; + caddr_t p; + int plen; + + /* validity check */ + if (src == NULL || dst == NULL) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + if (src->sa_family != dst->sa_family) { + __ipsec_errcode = EIPSEC_FAMILY_MISMATCH; + return -1; + } + if (min > max || (min > 0 && min <= 255)) { + __ipsec_errcode = EIPSEC_INVAL_SPI; + return -1; + } + switch (src->sa_family) { + case AF_INET: + plen = sizeof(struct in_addr) << 3; + break; + case AF_INET6: + plen = sizeof(struct in6_addr) << 3; + break; + default: + __ipsec_errcode = EIPSEC_INVAL_FAMILY; + return -1; + } + + /* create new sadb_msg to send. */ + len = sizeof(struct sadb_msg) + + sizeof(struct sadb_x_sa2) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(src->sa_len) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(dst->sa_len); + + if (min > (u_int32_t)255 && max < (u_int32_t) ~0) { + need_spirange++; + len += sizeof(struct sadb_spirange); + } + + if ((newmsg = CALLOC(len, struct sadb_msg *)) == NULL) { + __ipsec_set_strerror(strerror(errno)); + return -1; + } + ep = ((caddr_t)newmsg) + len; + + p = pfkey_setsadbmsg((caddr_t)newmsg, ep, SADB_GETSPI, + len, satype, seq, getpid()); + if (!p) { + free(newmsg); + return -1; + } + + p = pfkey_setsadbxsa2(p, ep, mode, reqid); + if (!p) { + free(newmsg); + return -1; + } + + /* set sadb_address for source */ + p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_SRC, src, plen, + IPSEC_ULPROTO_ANY); + if (!p) { + free(newmsg); + return -1; + } + + /* set sadb_address for destination */ + p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_DST, dst, plen, + IPSEC_ULPROTO_ANY); + if (!p) { + free(newmsg); + return -1; + } + + /* proccessing spi range */ + if (need_spirange) { + struct sadb_spirange spirange; + + if (p + sizeof(spirange) > ep) { + free(newmsg); + return -1; + } + + memset(&spirange, 0, sizeof(spirange)); + spirange.sadb_spirange_len = PFKEY_UNIT64(sizeof(spirange)); + spirange.sadb_spirange_exttype = SADB_EXT_SPIRANGE; + spirange.sadb_spirange_min = min; + spirange.sadb_spirange_max = max; + + memcpy(p, &spirange, sizeof(spirange)); + + p += sizeof(spirange); + } + if (p != ep) { + free(newmsg); + return -1; + } + + /* send message */ + len = pfkey_send(so, newmsg, len); + free(newmsg); + + if (len < 0) + return -1; + + __ipsec_errcode = EIPSEC_NO_ERROR; + return len; } /* @@ -493,26 +493,26 @@ pfkey_send_getspi(so, satype, mode, src, dst, min, max, reqid, seq) */ int pfkey_send_update(so, satype, mode, src, dst, spi, reqid, wsize, - keymat, e_type, e_keylen, a_type, a_keylen, flags, - l_alloc, l_bytes, l_addtime, l_usetime, seq) - int so; - u_int satype, mode, wsize; - struct sockaddr *src, *dst; - u_int32_t spi, reqid; - caddr_t keymat; - u_int e_type, e_keylen, a_type, a_keylen, flags; - u_int32_t l_alloc; - u_int64_t l_bytes, l_addtime, l_usetime; - u_int32_t seq; + keymat, e_type, e_keylen, a_type, a_keylen, flags, + l_alloc, l_bytes, l_addtime, l_usetime, seq) +int so; +u_int satype, mode, wsize; +struct sockaddr *src, *dst; +u_int32_t spi, reqid; +caddr_t keymat; +u_int e_type, e_keylen, a_type, a_keylen, flags; +u_int32_t l_alloc; +u_int64_t l_bytes, l_addtime, l_usetime; +u_int32_t seq; { - int len; - if ((len = pfkey_send_x1(so, SADB_UPDATE, satype, mode, src, dst, spi, - reqid, wsize, - keymat, e_type, e_keylen, a_type, a_keylen, flags, - l_alloc, l_bytes, l_addtime, l_usetime, seq)) < 0) - return -1; - - return len; + int len; + if ((len = pfkey_send_x1(so, SADB_UPDATE, satype, mode, src, dst, spi, + reqid, wsize, + keymat, e_type, e_keylen, a_type, a_keylen, flags, + l_alloc, l_bytes, l_addtime, l_usetime, seq)) < 0) + return -1; + + return len; } /* @@ -524,26 +524,26 @@ pfkey_send_update(so, satype, mode, src, dst, spi, reqid, wsize, */ int pfkey_send_add(so, satype, mode, src, dst, spi, reqid, wsize, - keymat, e_type, e_keylen, a_type, a_keylen, flags, - l_alloc, l_bytes, l_addtime, l_usetime, seq) - int so; - u_int satype, mode, wsize; - struct sockaddr *src, *dst; - u_int32_t spi, reqid; - caddr_t keymat; - u_int e_type, e_keylen, a_type, a_keylen, flags; - u_int32_t l_alloc; - u_int64_t l_bytes, l_addtime, l_usetime; - u_int32_t seq; + keymat, e_type, e_keylen, a_type, a_keylen, flags, + l_alloc, l_bytes, l_addtime, l_usetime, seq) +int so; +u_int satype, mode, wsize; +struct sockaddr *src, *dst; +u_int32_t spi, reqid; +caddr_t keymat; +u_int e_type, e_keylen, a_type, a_keylen, flags; +u_int32_t l_alloc; +u_int64_t l_bytes, l_addtime, l_usetime; +u_int32_t seq; { - int len; - if ((len = pfkey_send_x1(so, SADB_ADD, satype, mode, src, dst, spi, - reqid, wsize, - keymat, e_type, e_keylen, a_type, a_keylen, flags, - l_alloc, l_bytes, l_addtime, l_usetime, seq)) < 0) - return -1; - - return len; + int len; + if ((len = pfkey_send_x1(so, SADB_ADD, satype, mode, src, dst, spi, + reqid, wsize, + keymat, e_type, e_keylen, a_type, a_keylen, flags, + l_alloc, l_bytes, l_addtime, l_usetime, seq)) < 0) + return -1; + + return len; } /* @@ -554,16 +554,16 @@ pfkey_send_add(so, satype, mode, src, dst, spi, reqid, wsize, */ int pfkey_send_delete(so, satype, mode, src, dst, spi) - int so; - u_int satype, mode; - struct sockaddr *src, *dst; - u_int32_t spi; +int so; +u_int satype, mode; +struct sockaddr *src, *dst; +u_int32_t spi; { - int len; - if ((len = pfkey_send_x2(so, SADB_DELETE, satype, mode, src, dst, spi)) < 0) - return -1; + int len; + if ((len = pfkey_send_x2(so, SADB_DELETE, satype, mode, src, dst, spi)) < 0) + return -1; - return len; + return len; } /* @@ -577,80 +577,80 @@ pfkey_send_delete(so, satype, mode, src, dst, spi) */ int pfkey_send_delete_all(so, satype, mode, src, dst) - int so; - u_int satype, mode; - struct sockaddr *src, *dst; +int so; +u_int satype, mode; +struct sockaddr *src, *dst; { - struct sadb_msg *newmsg; - int len; - caddr_t p; - int plen; - caddr_t ep; - - (void)mode; - - /* validity check */ - if (src == NULL || dst == NULL) { - __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; - return -1; - } - if (src->sa_family != dst->sa_family) { - __ipsec_errcode = EIPSEC_FAMILY_MISMATCH; - return -1; - } - switch (src->sa_family) { - case AF_INET: - plen = sizeof(struct in_addr) << 3; - break; - case AF_INET6: - plen = sizeof(struct in6_addr) << 3; - break; - default: - __ipsec_errcode = EIPSEC_INVAL_FAMILY; - return -1; - } - - /* create new sadb_msg to reply. */ - len = sizeof(struct sadb_msg) - + sizeof(struct sadb_address) - + PFKEY_ALIGN8(src->sa_len) - + sizeof(struct sadb_address) - + PFKEY_ALIGN8(dst->sa_len); - - if ((newmsg = CALLOC(len, struct sadb_msg *)) == NULL) { - __ipsec_set_strerror(strerror(errno)); - return -1; - } - ep = ((caddr_t)newmsg) + len; - - p = pfkey_setsadbmsg((caddr_t)newmsg, ep, SADB_DELETE, len, satype, 0, - getpid()); - if (!p) { - free(newmsg); - return -1; - } - p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_SRC, src, plen, - IPSEC_ULPROTO_ANY); - if (!p) { - free(newmsg); - return -1; - } - p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_DST, dst, plen, - IPSEC_ULPROTO_ANY); - if (!p || p != ep) { - free(newmsg); - return -1; - } - - /* send message */ - len = pfkey_send(so, newmsg, len); - free(newmsg); - - if (len < 0) - return -1; - - __ipsec_errcode = EIPSEC_NO_ERROR; - return len; + struct sadb_msg *newmsg; + int len; + caddr_t p; + int plen; + caddr_t ep; + + (void)mode; + + /* validity check */ + if (src == NULL || dst == NULL) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + if (src->sa_family != dst->sa_family) { + __ipsec_errcode = EIPSEC_FAMILY_MISMATCH; + return -1; + } + switch (src->sa_family) { + case AF_INET: + plen = sizeof(struct in_addr) << 3; + break; + case AF_INET6: + plen = sizeof(struct in6_addr) << 3; + break; + default: + __ipsec_errcode = EIPSEC_INVAL_FAMILY; + return -1; + } + + /* create new sadb_msg to reply. */ + len = sizeof(struct sadb_msg) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(src->sa_len) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(dst->sa_len); + + if ((newmsg = CALLOC(len, struct sadb_msg *)) == NULL) { + __ipsec_set_strerror(strerror(errno)); + return -1; + } + ep = ((caddr_t)newmsg) + len; + + p = pfkey_setsadbmsg((caddr_t)newmsg, ep, SADB_DELETE, len, satype, 0, + getpid()); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_SRC, src, plen, + IPSEC_ULPROTO_ANY); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_DST, dst, plen, + IPSEC_ULPROTO_ANY); + if (!p || p != ep) { + free(newmsg); + return -1; + } + + /* send message */ + len = pfkey_send(so, newmsg, len); + free(newmsg); + + if (len < 0) + return -1; + + __ipsec_errcode = EIPSEC_NO_ERROR; + return len; } /* @@ -661,16 +661,16 @@ pfkey_send_delete_all(so, satype, mode, src, dst) */ int pfkey_send_get(so, satype, mode, src, dst, spi) - int so; - u_int satype, mode; - struct sockaddr *src, *dst; - u_int32_t spi; +int so; +u_int satype, mode; +struct sockaddr *src, *dst; +u_int32_t spi; { - int len; - if ((len = pfkey_send_x2(so, SADB_GET, satype, mode, src, dst, spi)) < 0) - return -1; + int len; + if ((len = pfkey_send_x2(so, SADB_GET, satype, mode, src, dst, spi)) < 0) + return -1; - return len; + return len; } /* @@ -681,37 +681,37 @@ pfkey_send_get(so, satype, mode, src, dst, spi) */ int pfkey_send_register(so, satype) - int so; - u_int satype; +int so; +u_int satype; { - int len, algno; - - if (satype == PF_UNSPEC) { - for (algno = 0; - (unsigned int)algno < sizeof(supported_map)/sizeof(supported_map[0]); - algno++) { - if (ipsec_supported[algno]) { - free(ipsec_supported[algno]); - ipsec_supported[algno] = NULL; - } - } - } else { - algno = findsupportedmap(satype); - if (algno == -1) { - __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; - return -1; - } - - if (ipsec_supported[algno]) { - free(ipsec_supported[algno]); - ipsec_supported[algno] = NULL; - } - } - - if ((len = pfkey_send_x3(so, SADB_REGISTER, satype)) < 0) - return -1; - - return len; + int len, algno; + + if (satype == PF_UNSPEC) { + for (algno = 0; + (unsigned int)algno < sizeof(supported_map)/sizeof(supported_map[0]); + algno++) { + if (ipsec_supported[algno]) { + free(ipsec_supported[algno]); + ipsec_supported[algno] = NULL; + } + } + } else { + algno = findsupportedmap(satype); + if (algno == -1) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + + if (ipsec_supported[algno]) { + free(ipsec_supported[algno]); + ipsec_supported[algno] = NULL; + } + } + + if ((len = pfkey_send_x3(so, SADB_REGISTER, satype)) < 0) + return -1; + + return len; } /* @@ -723,29 +723,29 @@ pfkey_send_register(so, satype) */ int pfkey_recv_register(so) - int so; +int so; { - pid_t pid = getpid(); - struct sadb_msg *newmsg; - int error = -1; + pid_t pid = getpid(); + struct sadb_msg *newmsg; + int error = -1; - /* receive message */ - do { - if ((newmsg = pfkey_recv(so)) == NULL) - return -1; - } while (newmsg->sadb_msg_type != SADB_REGISTER - || (pid_t)newmsg->sadb_msg_pid != pid); + /* receive message */ + do { + if ((newmsg = pfkey_recv(so)) == NULL) + return -1; + } while (newmsg->sadb_msg_type != SADB_REGISTER + || (pid_t)newmsg->sadb_msg_pid != pid); - /* check and fix */ - newmsg->sadb_msg_len = PFKEY_UNUNIT64(newmsg->sadb_msg_len); + /* check and fix */ + newmsg->sadb_msg_len = PFKEY_UNUNIT64(newmsg->sadb_msg_len); - error = pfkey_set_supported(newmsg, newmsg->sadb_msg_len); - free(newmsg); + error = pfkey_set_supported(newmsg, newmsg->sadb_msg_len); + free(newmsg); - if (error == 0) - __ipsec_errcode = EIPSEC_NO_ERROR; + if (error == 0) + __ipsec_errcode = EIPSEC_NO_ERROR; - return error; + return error; } /* @@ -760,60 +760,60 @@ pfkey_recv_register(so) */ int pfkey_set_supported(msg, tlen) - struct sadb_msg *msg; - int tlen; +struct sadb_msg *msg; +int tlen; { - struct sadb_supported *sup; - caddr_t p; - caddr_t ep; - - /* validity */ - if (msg->sadb_msg_len != tlen) { - __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; - return -1; - } - - p = (caddr_t)msg; - ep = p + tlen; - - p += sizeof(struct sadb_msg); - - while (p < ep) { - sup = (struct sadb_supported *)p; - if (ep < p + sizeof(*sup) || - (size_t)PFKEY_EXTLEN(sup) < sizeof(*sup) || - ep < p + sup->sadb_supported_len) { - /* invalid format */ - break; - } - - switch (sup->sadb_supported_exttype) { - case SADB_EXT_SUPPORTED_AUTH: - case SADB_EXT_SUPPORTED_ENCRYPT: - break; - default: - __ipsec_errcode = EIPSEC_INVAL_SATYPE; - return -1; - } - - /* fixed length */ - sup->sadb_supported_len = PFKEY_EXTLEN(sup); - - /* set supported map */ - if (setsupportedmap(sup) != 0) - return -1; - - p += sup->sadb_supported_len; - } - - if (p != ep) { - __ipsec_errcode = EIPSEC_INVAL_SATYPE; - return -1; - } - - __ipsec_errcode = EIPSEC_NO_ERROR; - - return 0; + struct sadb_supported *sup; + caddr_t p; + caddr_t ep; + + /* validity */ + if (msg->sadb_msg_len != tlen) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + + p = (caddr_t)msg; + ep = p + tlen; + + p += sizeof(struct sadb_msg); + + while (p < ep) { + sup = (struct sadb_supported *)p; + if (ep < p + sizeof(*sup) || + (size_t)PFKEY_EXTLEN(sup) < sizeof(*sup) || + ep < p + sup->sadb_supported_len) { + /* invalid format */ + break; + } + + switch (sup->sadb_supported_exttype) { + case SADB_EXT_SUPPORTED_AUTH: + case SADB_EXT_SUPPORTED_ENCRYPT: + break; + default: + __ipsec_errcode = EIPSEC_INVAL_SATYPE; + return -1; + } + + /* fixed length */ + sup->sadb_supported_len = PFKEY_EXTLEN(sup); + + /* set supported map */ + if (setsupportedmap(sup) != 0) + return -1; + + p += sup->sadb_supported_len; + } + + if (p != ep) { + __ipsec_errcode = EIPSEC_INVAL_SATYPE; + return -1; + } + + __ipsec_errcode = EIPSEC_NO_ERROR; + + return 0; } /* @@ -824,15 +824,15 @@ pfkey_set_supported(msg, tlen) */ int pfkey_send_flush(so, satype) - int so; - u_int satype; +int so; +u_int satype; { - int len; + int len; - if ((len = pfkey_send_x3(so, SADB_FLUSH, satype)) < 0) - return -1; + if ((len = pfkey_send_x3(so, SADB_FLUSH, satype)) < 0) + return -1; - return len; + return len; } /* @@ -843,15 +843,15 @@ pfkey_send_flush(so, satype) */ int pfkey_send_dump(so, satype) - int so; - u_int satype; +int so; +u_int satype; { - int len; + int len; - if ((len = pfkey_send_x3(so, SADB_DUMP, satype)) < 0) - return -1; + if ((len = pfkey_send_x3(so, SADB_DUMP, satype)) < 0) + return -1; - return len; + return len; } /* @@ -868,15 +868,15 @@ pfkey_send_dump(so, satype) */ int pfkey_send_promisc_toggle(so, flag) - int so; - int flag; +int so; +int flag; { - int len; + int len; - if ((len = pfkey_send_x3(so, SADB_X_PROMISC, (flag ? 1 : 0))) < 0) - return -1; + if ((len = pfkey_send_x3(so, SADB_X_PROMISC, (flag ? 1 : 0))) < 0) + return -1; - return len; + return len; } /* @@ -887,22 +887,22 @@ pfkey_send_promisc_toggle(so, flag) */ int pfkey_send_spdadd(so, src, prefs, dst, prefd, proto, policy, policylen, seq) - int so; - struct sockaddr *src, *dst; - u_int prefs, prefd, proto; - caddr_t policy; - int policylen; - u_int32_t seq; +int so; +struct sockaddr *src, *dst; +u_int prefs, prefd, proto; +caddr_t policy; +int policylen; +u_int32_t seq; { - int len; + int len; - if ((len = pfkey_send_x4(so, SADB_X_SPDADD, - src, prefs, dst, prefd, proto, - 0, 0, - policy, policylen, seq)) < 0) - return -1; + if ((len = pfkey_send_x4(so, SADB_X_SPDADD, + src, prefs, dst, prefd, proto, + 0, 0, + policy, policylen, seq)) < 0) + return -1; - return len; + return len; } /* @@ -913,24 +913,24 @@ pfkey_send_spdadd(so, src, prefs, dst, prefd, proto, policy, policylen, seq) */ int pfkey_send_spdadd2(so, src, prefs, dst, prefd, proto, ltime, vtime, - policy, policylen, seq) - int so; - struct sockaddr *src, *dst; - u_int prefs, prefd, proto; - u_int64_t ltime, vtime; - caddr_t policy; - int policylen; - u_int32_t seq; + policy, policylen, seq) +int so; +struct sockaddr *src, *dst; +u_int prefs, prefd, proto; +u_int64_t ltime, vtime; +caddr_t policy; +int policylen; +u_int32_t seq; { - int len; + int len; - if ((len = pfkey_send_x4(so, SADB_X_SPDADD, - src, prefs, dst, prefd, proto, - ltime, vtime, - policy, policylen, seq)) < 0) - return -1; + if ((len = pfkey_send_x4(so, SADB_X_SPDADD, + src, prefs, dst, prefd, proto, + ltime, vtime, + policy, policylen, seq)) < 0) + return -1; - return len; + return len; } /* @@ -941,22 +941,22 @@ pfkey_send_spdadd2(so, src, prefs, dst, prefd, proto, ltime, vtime, */ int pfkey_send_spdupdate(so, src, prefs, dst, prefd, proto, policy, policylen, seq) - int so; - struct sockaddr *src, *dst; - u_int prefs, prefd, proto; - caddr_t policy; - int policylen; - u_int32_t seq; +int so; +struct sockaddr *src, *dst; +u_int prefs, prefd, proto; +caddr_t policy; +int policylen; +u_int32_t seq; { - int len; + int len; - if ((len = pfkey_send_x4(so, SADB_X_SPDUPDATE, - src, prefs, dst, prefd, proto, - 0, 0, - policy, policylen, seq)) < 0) - return -1; + if ((len = pfkey_send_x4(so, SADB_X_SPDUPDATE, + src, prefs, dst, prefd, proto, + 0, 0, + policy, policylen, seq)) < 0) + return -1; - return len; + return len; } /* @@ -967,24 +967,24 @@ pfkey_send_spdupdate(so, src, prefs, dst, prefd, proto, policy, policylen, seq) */ int pfkey_send_spdupdate2(so, src, prefs, dst, prefd, proto, ltime, vtime, - policy, policylen, seq) - int so; - struct sockaddr *src, *dst; - u_int prefs, prefd, proto; - u_int64_t ltime, vtime; - caddr_t policy; - int policylen; - u_int32_t seq; + policy, policylen, seq) +int so; +struct sockaddr *src, *dst; +u_int prefs, prefd, proto; +u_int64_t ltime, vtime; +caddr_t policy; +int policylen; +u_int32_t seq; { - int len; + int len; - if ((len = pfkey_send_x4(so, SADB_X_SPDUPDATE, - src, prefs, dst, prefd, proto, - ltime, vtime, - policy, policylen, seq)) < 0) - return -1; + if ((len = pfkey_send_x4(so, SADB_X_SPDUPDATE, + src, prefs, dst, prefd, proto, + ltime, vtime, + policy, policylen, seq)) < 0) + return -1; - return len; + return len; } /* @@ -995,27 +995,27 @@ pfkey_send_spdupdate2(so, src, prefs, dst, prefd, proto, ltime, vtime, */ int pfkey_send_spddelete(so, src, prefs, dst, prefd, proto, policy, policylen, seq) - int so; - struct sockaddr *src, *dst; - u_int prefs, prefd, proto; - caddr_t policy; - int policylen; - u_int32_t seq; +int so; +struct sockaddr *src, *dst; +u_int prefs, prefd, proto; +caddr_t policy; +int policylen; +u_int32_t seq; { - int len; + int len; - if (policylen != sizeof(struct sadb_x_policy)) { - __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; - return -1; - } + if (policylen != sizeof(struct sadb_x_policy)) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } - if ((len = pfkey_send_x4(so, SADB_X_SPDDELETE, - src, prefs, dst, prefd, proto, - 0, 0, - policy, policylen, seq)) < 0) - return -1; + if ((len = pfkey_send_x4(so, SADB_X_SPDDELETE, + src, prefs, dst, prefd, proto, + 0, 0, + policy, policylen, seq)) < 0) + return -1; - return len; + return len; } /* @@ -1026,15 +1026,15 @@ pfkey_send_spddelete(so, src, prefs, dst, prefd, proto, policy, policylen, seq) */ int pfkey_send_spddelete2(so, spid) - int so; - u_int32_t spid; +int so; +u_int32_t spid; { - int len; + int len; - if ((len = pfkey_send_x5(so, SADB_X_SPDDELETE2, spid)) < 0) - return -1; + if ((len = pfkey_send_x5(so, SADB_X_SPDDELETE2, spid)) < 0) + return -1; - return len; + return len; } /* @@ -1045,15 +1045,15 @@ pfkey_send_spddelete2(so, spid) */ int pfkey_send_spdget(so, spid) - int so; - u_int32_t spid; +int so; +u_int32_t spid; { - int len; + int len; - if ((len = pfkey_send_x5(so, SADB_X_SPDGET, spid)) < 0) - return -1; + if ((len = pfkey_send_x5(so, SADB_X_SPDGET, spid)) < 0) + return -1; - return len; + return len; } /* @@ -1064,27 +1064,27 @@ pfkey_send_spdget(so, spid) */ int pfkey_send_spdsetidx(so, src, prefs, dst, prefd, proto, policy, policylen, seq) - int so; - struct sockaddr *src, *dst; - u_int prefs, prefd, proto; - caddr_t policy; - int policylen; - u_int32_t seq; +int so; +struct sockaddr *src, *dst; +u_int prefs, prefd, proto; +caddr_t policy; +int policylen; +u_int32_t seq; { - int len; + int len; - if (policylen != sizeof(struct sadb_x_policy)) { - __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; - return -1; - } + if (policylen != sizeof(struct sadb_x_policy)) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } - if ((len = pfkey_send_x4(so, SADB_X_SPDSETIDX, - src, prefs, dst, prefd, proto, - 0, 0, - policy, policylen, seq)) < 0) - return -1; + if ((len = pfkey_send_x4(so, SADB_X_SPDSETIDX, + src, prefs, dst, prefd, proto, + 0, 0, + policy, policylen, seq)) < 0) + return -1; - return len; + return len; } /* @@ -1095,14 +1095,14 @@ pfkey_send_spdsetidx(so, src, prefs, dst, prefd, proto, policy, policylen, seq) */ int pfkey_send_spdflush(so) - int so; +int so; { - int len; + int len; - if ((len = pfkey_send_x3(so, SADB_X_SPDFLUSH, SADB_SATYPE_UNSPEC)) < 0) - return -1; + if ((len = pfkey_send_x3(so, SADB_X_SPDFLUSH, SADB_SATYPE_UNSPEC)) < 0) + return -1; - return len; + return len; } /* @@ -1113,266 +1113,266 @@ pfkey_send_spdflush(so) */ int pfkey_send_spddump(so) - int so; +int so; { - int len; + int len; - if ((len = pfkey_send_x3(so, SADB_X_SPDDUMP, SADB_SATYPE_UNSPEC)) < 0) - return -1; + if ((len = pfkey_send_x3(so, SADB_X_SPDDUMP, SADB_SATYPE_UNSPEC)) < 0) + return -1; - return len; + return len; } /* sending SADB_ADD or SADB_UPDATE message to the kernel */ static int pfkey_send_x1(so, type, satype, mode, src, dst, spi, reqid, wsize, - keymat, e_type, e_keylen, a_type, a_keylen, flags, - l_alloc, l_bytes, l_addtime, l_usetime, seq) - int so; - u_int type, satype, mode; - struct sockaddr *src, *dst; - u_int32_t spi, reqid; - u_int wsize; - caddr_t keymat; - u_int e_type, e_keylen, a_type, a_keylen, flags; - u_int32_t l_alloc, l_bytes, l_addtime, l_usetime, seq; + keymat, e_type, e_keylen, a_type, a_keylen, flags, + l_alloc, l_bytes, l_addtime, l_usetime, seq) +int so; +u_int type, satype, mode; +struct sockaddr *src, *dst; +u_int32_t spi, reqid; +u_int wsize; +caddr_t keymat; +u_int e_type, e_keylen, a_type, a_keylen, flags; +u_int32_t l_alloc, l_bytes, l_addtime, l_usetime, seq; { - struct sadb_msg *newmsg; - int len; - caddr_t p; - int plen; - caddr_t ep; - - /* validity check */ - if (src == NULL || dst == NULL) { - __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; - return -1; - } - if (src->sa_family != dst->sa_family) { - __ipsec_errcode = EIPSEC_FAMILY_MISMATCH; - return -1; - } - switch (src->sa_family) { - case AF_INET: - plen = sizeof(struct in_addr) << 3; - break; - case AF_INET6: - plen = sizeof(struct in6_addr) << 3; - break; - default: - __ipsec_errcode = EIPSEC_INVAL_FAMILY; - return -1; - } - - switch (satype) { - case SADB_SATYPE_ESP: - if (e_type == SADB_EALG_NONE) { - __ipsec_errcode = EIPSEC_NO_ALGS; - return -1; - } - break; - case SADB_SATYPE_AH: - if (e_type != SADB_EALG_NONE) { - __ipsec_errcode = EIPSEC_INVAL_ALGS; - return -1; - } - if (a_type == SADB_AALG_NONE) { - __ipsec_errcode = EIPSEC_NO_ALGS; - return -1; - } - break; - case SADB_X_SATYPE_IPCOMP: - if (e_type == SADB_X_CALG_NONE) { - __ipsec_errcode = EIPSEC_INVAL_ALGS; - return -1; - } - if (a_type != SADB_AALG_NONE) { - __ipsec_errcode = EIPSEC_NO_ALGS; - return -1; - } - break; - default: - __ipsec_errcode = EIPSEC_INVAL_SATYPE; - return -1; - } - - /* create new sadb_msg to reply. */ - len = sizeof(struct sadb_msg) - + sizeof(struct sadb_sa) - + sizeof(struct sadb_x_sa2) - + sizeof(struct sadb_address) - + PFKEY_ALIGN8(src->sa_len) - + sizeof(struct sadb_address) - + PFKEY_ALIGN8(dst->sa_len) - + sizeof(struct sadb_lifetime) - + sizeof(struct sadb_lifetime); - - if (e_type != SADB_EALG_NONE) - len += (sizeof(struct sadb_key) + PFKEY_ALIGN8(e_keylen)); - if (a_type != SADB_AALG_NONE) - len += (sizeof(struct sadb_key) + PFKEY_ALIGN8(a_keylen)); - - if ((newmsg = CALLOC(len, struct sadb_msg *)) == NULL) { - __ipsec_set_strerror(strerror(errno)); - return -1; - } - ep = ((caddr_t)newmsg) + len; - - p = pfkey_setsadbmsg((caddr_t)newmsg, ep, type, len, - satype, seq, getpid()); - if (!p) { - free(newmsg); - return -1; - } - p = pfkey_setsadbsa(p, ep, spi, wsize, a_type, e_type, flags); - if (!p) { - free(newmsg); - return -1; - } - p = pfkey_setsadbxsa2(p, ep, mode, reqid); - if (!p) { - free(newmsg); - return -1; - } - p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_SRC, src, plen, - IPSEC_ULPROTO_ANY); - if (!p) { - free(newmsg); - return -1; - } - p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_DST, dst, plen, - IPSEC_ULPROTO_ANY); - if (!p) { - free(newmsg); - return -1; - } - - if (e_type != SADB_EALG_NONE) { - p = pfkey_setsadbkey(p, ep, SADB_EXT_KEY_ENCRYPT, - keymat, e_keylen); - if (!p) { - free(newmsg); - return -1; - } - } - if (a_type != SADB_AALG_NONE) { - p = pfkey_setsadbkey(p, ep, SADB_EXT_KEY_AUTH, - keymat + e_keylen, a_keylen); - if (!p) { - free(newmsg); - return -1; - } - } - - /* set sadb_lifetime for destination */ - p = pfkey_setsadblifetime(p, ep, SADB_EXT_LIFETIME_HARD, - l_alloc, l_bytes, l_addtime, l_usetime); - if (!p) { - free(newmsg); - return -1; - } - p = pfkey_setsadblifetime(p, ep, SADB_EXT_LIFETIME_SOFT, - l_alloc, l_bytes, l_addtime, l_usetime); - if (!p || p != ep) { - free(newmsg); - return -1; - } - - /* send message */ - len = pfkey_send(so, newmsg, len); - free(newmsg); - - if (len < 0) - return -1; - - __ipsec_errcode = EIPSEC_NO_ERROR; - return len; + struct sadb_msg *newmsg; + int len; + caddr_t p; + int plen; + caddr_t ep; + + /* validity check */ + if (src == NULL || dst == NULL) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + if (src->sa_family != dst->sa_family) { + __ipsec_errcode = EIPSEC_FAMILY_MISMATCH; + return -1; + } + switch (src->sa_family) { + case AF_INET: + plen = sizeof(struct in_addr) << 3; + break; + case AF_INET6: + plen = sizeof(struct in6_addr) << 3; + break; + default: + __ipsec_errcode = EIPSEC_INVAL_FAMILY; + return -1; + } + + switch (satype) { + case SADB_SATYPE_ESP: + if (e_type == SADB_EALG_NONE) { + __ipsec_errcode = EIPSEC_NO_ALGS; + return -1; + } + break; + case SADB_SATYPE_AH: + if (e_type != SADB_EALG_NONE) { + __ipsec_errcode = EIPSEC_INVAL_ALGS; + return -1; + } + if (a_type == SADB_AALG_NONE) { + __ipsec_errcode = EIPSEC_NO_ALGS; + return -1; + } + break; + case SADB_X_SATYPE_IPCOMP: + if (e_type == SADB_X_CALG_NONE) { + __ipsec_errcode = EIPSEC_INVAL_ALGS; + return -1; + } + if (a_type != SADB_AALG_NONE) { + __ipsec_errcode = EIPSEC_NO_ALGS; + return -1; + } + break; + default: + __ipsec_errcode = EIPSEC_INVAL_SATYPE; + return -1; + } + + /* create new sadb_msg to reply. */ + len = sizeof(struct sadb_msg) + + sizeof(struct sadb_sa) + + sizeof(struct sadb_x_sa2) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(src->sa_len) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(dst->sa_len) + + sizeof(struct sadb_lifetime) + + sizeof(struct sadb_lifetime); + + if (e_type != SADB_EALG_NONE) + len += (sizeof(struct sadb_key) + PFKEY_ALIGN8(e_keylen)); + if (a_type != SADB_AALG_NONE) + len += (sizeof(struct sadb_key) + PFKEY_ALIGN8(a_keylen)); + + if ((newmsg = CALLOC(len, struct sadb_msg *)) == NULL) { + __ipsec_set_strerror(strerror(errno)); + return -1; + } + ep = ((caddr_t)newmsg) + len; + + p = pfkey_setsadbmsg((caddr_t)newmsg, ep, type, len, + satype, seq, getpid()); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadbsa(p, ep, spi, wsize, a_type, e_type, flags); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadbxsa2(p, ep, mode, reqid); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_SRC, src, plen, + IPSEC_ULPROTO_ANY); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_DST, dst, plen, + IPSEC_ULPROTO_ANY); + if (!p) { + free(newmsg); + return -1; + } + + if (e_type != SADB_EALG_NONE) { + p = pfkey_setsadbkey(p, ep, SADB_EXT_KEY_ENCRYPT, + keymat, e_keylen); + if (!p) { + free(newmsg); + return -1; + } + } + if (a_type != SADB_AALG_NONE) { + p = pfkey_setsadbkey(p, ep, SADB_EXT_KEY_AUTH, + keymat + e_keylen, a_keylen); + if (!p) { + free(newmsg); + return -1; + } + } + + /* set sadb_lifetime for destination */ + p = pfkey_setsadblifetime(p, ep, SADB_EXT_LIFETIME_HARD, + l_alloc, l_bytes, l_addtime, l_usetime); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadblifetime(p, ep, SADB_EXT_LIFETIME_SOFT, + l_alloc, l_bytes, l_addtime, l_usetime); + if (!p || p != ep) { + free(newmsg); + return -1; + } + + /* send message */ + len = pfkey_send(so, newmsg, len); + free(newmsg); + + if (len < 0) + return -1; + + __ipsec_errcode = EIPSEC_NO_ERROR; + return len; } /* sending SADB_DELETE or SADB_GET message to the kernel */ static int pfkey_send_x2(so, type, satype, mode, src, dst, spi) - int so; - u_int type, satype, mode; - struct sockaddr *src, *dst; - u_int32_t spi; +int so; +u_int type, satype, mode; +struct sockaddr *src, *dst; +u_int32_t spi; { - struct sadb_msg *newmsg; - int len; - caddr_t p; - int plen; - caddr_t ep; - - (void)mode; - - /* validity check */ - if (src == NULL || dst == NULL) { - __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; - return -1; - } - if (src->sa_family != dst->sa_family) { - __ipsec_errcode = EIPSEC_FAMILY_MISMATCH; - return -1; - } - switch (src->sa_family) { - case AF_INET: - plen = sizeof(struct in_addr) << 3; - break; - case AF_INET6: - plen = sizeof(struct in6_addr) << 3; - break; - default: - __ipsec_errcode = EIPSEC_INVAL_FAMILY; - return -1; - } - - /* create new sadb_msg to reply. */ - len = sizeof(struct sadb_msg) - + sizeof(struct sadb_sa) - + sizeof(struct sadb_address) - + PFKEY_ALIGN8(src->sa_len) - + sizeof(struct sadb_address) - + PFKEY_ALIGN8(dst->sa_len); - - if ((newmsg = CALLOC(len, struct sadb_msg *)) == NULL) { - __ipsec_set_strerror(strerror(errno)); - return -1; - } - ep = ((caddr_t)newmsg) + len; - - p = pfkey_setsadbmsg((caddr_t)newmsg, ep, type, len, satype, 0, - getpid()); - if (!p) { - free(newmsg); - return -1; - } - p = pfkey_setsadbsa(p, ep, spi, 0, 0, 0, 0); - if (!p) { - free(newmsg); - return -1; - } - p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_SRC, src, plen, - IPSEC_ULPROTO_ANY); - if (!p) { - free(newmsg); - return -1; - } - p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_DST, dst, plen, - IPSEC_ULPROTO_ANY); - if (!p || p != ep) { - free(newmsg); - return -1; - } - - /* send message */ - len = pfkey_send(so, newmsg, len); - free(newmsg); - - if (len < 0) - return -1; - - __ipsec_errcode = EIPSEC_NO_ERROR; - return len; + struct sadb_msg *newmsg; + int len; + caddr_t p; + int plen; + caddr_t ep; + + (void)mode; + + /* validity check */ + if (src == NULL || dst == NULL) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + if (src->sa_family != dst->sa_family) { + __ipsec_errcode = EIPSEC_FAMILY_MISMATCH; + return -1; + } + switch (src->sa_family) { + case AF_INET: + plen = sizeof(struct in_addr) << 3; + break; + case AF_INET6: + plen = sizeof(struct in6_addr) << 3; + break; + default: + __ipsec_errcode = EIPSEC_INVAL_FAMILY; + return -1; + } + + /* create new sadb_msg to reply. */ + len = sizeof(struct sadb_msg) + + sizeof(struct sadb_sa) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(src->sa_len) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(dst->sa_len); + + if ((newmsg = CALLOC(len, struct sadb_msg *)) == NULL) { + __ipsec_set_strerror(strerror(errno)); + return -1; + } + ep = ((caddr_t)newmsg) + len; + + p = pfkey_setsadbmsg((caddr_t)newmsg, ep, type, len, satype, 0, + getpid()); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadbsa(p, ep, spi, 0, 0, 0, 0); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_SRC, src, plen, + IPSEC_ULPROTO_ANY); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_DST, dst, plen, + IPSEC_ULPROTO_ANY); + if (!p || p != ep) { + free(newmsg); + return -1; + } + + /* send message */ + len = pfkey_send(so, newmsg, len); + free(newmsg); + + if (len < 0) + return -1; + + __ipsec_errcode = EIPSEC_NO_ERROR; + return len; } /* @@ -1381,205 +1381,205 @@ pfkey_send_x2(so, type, satype, mode, src, dst, spi) */ static int pfkey_send_x3(so, type, satype) - int so; - u_int type, satype; +int so; +u_int type, satype; { - struct sadb_msg *newmsg; - int len; - caddr_t p; - caddr_t ep; - - /* validity check */ - switch (type) { - case SADB_X_PROMISC: - if (satype != 0 && satype != 1) { - __ipsec_errcode = EIPSEC_INVAL_SATYPE; - return -1; - } - break; - default: - switch (satype) { - case SADB_SATYPE_UNSPEC: - case SADB_SATYPE_AH: - case SADB_SATYPE_ESP: - case SADB_X_SATYPE_IPCOMP: - break; - default: - __ipsec_errcode = EIPSEC_INVAL_SATYPE; - return -1; - } - } - - /* create new sadb_msg to send. */ - len = sizeof(struct sadb_msg); - - if ((newmsg = CALLOC(len, struct sadb_msg *)) == NULL) { - __ipsec_set_strerror(strerror(errno)); - return -1; - } - ep = ((caddr_t)newmsg) + len; - - p = pfkey_setsadbmsg((caddr_t)newmsg, ep, type, len, satype, 0, - getpid()); - if (!p || p != ep) { - free(newmsg); - return -1; - } - - /* send message */ - len = pfkey_send(so, newmsg, len); - free(newmsg); - - if (len < 0) - return -1; - - __ipsec_errcode = EIPSEC_NO_ERROR; - return len; + struct sadb_msg *newmsg; + int len; + caddr_t p; + caddr_t ep; + + /* validity check */ + switch (type) { + case SADB_X_PROMISC: + if (satype != 0 && satype != 1) { + __ipsec_errcode = EIPSEC_INVAL_SATYPE; + return -1; + } + break; + default: + switch (satype) { + case SADB_SATYPE_UNSPEC: + case SADB_SATYPE_AH: + case SADB_SATYPE_ESP: + case SADB_X_SATYPE_IPCOMP: + break; + default: + __ipsec_errcode = EIPSEC_INVAL_SATYPE; + return -1; + } + } + + /* create new sadb_msg to send. */ + len = sizeof(struct sadb_msg); + + if ((newmsg = CALLOC(len, struct sadb_msg *)) == NULL) { + __ipsec_set_strerror(strerror(errno)); + return -1; + } + ep = ((caddr_t)newmsg) + len; + + p = pfkey_setsadbmsg((caddr_t)newmsg, ep, type, len, satype, 0, + getpid()); + if (!p || p != ep) { + free(newmsg); + return -1; + } + + /* send message */ + len = pfkey_send(so, newmsg, len); + free(newmsg); + + if (len < 0) + return -1; + + __ipsec_errcode = EIPSEC_NO_ERROR; + return len; } /* sending SADB_X_SPDADD message to the kernel */ static int pfkey_send_x4(so, type, src, prefs, dst, prefd, proto, - ltime, vtime, policy, policylen, seq) - int so; - struct sockaddr *src, *dst; - u_int type, prefs, prefd, proto; - u_int64_t ltime, vtime; - char *policy; - int policylen; - u_int32_t seq; + ltime, vtime, policy, policylen, seq) +int so; +struct sockaddr *src, *dst; +u_int type, prefs, prefd, proto; +u_int64_t ltime, vtime; +char *policy; +int policylen; +u_int32_t seq; { - struct sadb_msg *newmsg; - int len; - caddr_t p; - int plen; - caddr_t ep; - - /* validity check */ - if (src == NULL || dst == NULL) { - __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; - return -1; - } - if (src->sa_family != dst->sa_family) { - __ipsec_errcode = EIPSEC_FAMILY_MISMATCH; - return -1; - } - - switch (src->sa_family) { - case AF_INET: - plen = sizeof(struct in_addr) << 3; - break; - case AF_INET6: - plen = sizeof(struct in6_addr) << 3; - break; - default: - __ipsec_errcode = EIPSEC_INVAL_FAMILY; - return -1; - } - if (prefs > (u_int)plen || prefd > (u_int)plen) { - __ipsec_errcode = EIPSEC_INVAL_PREFIXLEN; - return -1; - } - - /* create new sadb_msg to reply. */ - len = sizeof(struct sadb_msg) - + sizeof(struct sadb_address) - + PFKEY_ALIGN8(src->sa_len) - + sizeof(struct sadb_address) - + PFKEY_ALIGN8(src->sa_len) - + sizeof(struct sadb_lifetime) - + policylen; - - if ((newmsg = CALLOC(len, struct sadb_msg *)) == NULL) { - __ipsec_set_strerror(strerror(errno)); - return -1; - } - ep = ((caddr_t)newmsg) + len; - - p = pfkey_setsadbmsg((caddr_t)newmsg, ep, type, len, - SADB_SATYPE_UNSPEC, seq, getpid()); - if (!p) { - free(newmsg); - return -1; - } - p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_SRC, src, prefs, proto); - if (!p) { - free(newmsg); - return -1; - } - p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_DST, dst, prefd, proto); - if (!p) { - free(newmsg); - return -1; - } - p = pfkey_setsadblifetime(p, ep, SADB_EXT_LIFETIME_HARD, - 0, 0, ltime, vtime); - if (!p || p + policylen != ep) { - free(newmsg); - return -1; - } - memcpy(p, policy, policylen); - - /* send message */ - len = pfkey_send(so, newmsg, len); - free(newmsg); - - if (len < 0) - return -1; - - __ipsec_errcode = EIPSEC_NO_ERROR; - return len; + struct sadb_msg *newmsg; + int len; + caddr_t p; + int plen; + caddr_t ep; + + /* validity check */ + if (src == NULL || dst == NULL) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + if (src->sa_family != dst->sa_family) { + __ipsec_errcode = EIPSEC_FAMILY_MISMATCH; + return -1; + } + + switch (src->sa_family) { + case AF_INET: + plen = sizeof(struct in_addr) << 3; + break; + case AF_INET6: + plen = sizeof(struct in6_addr) << 3; + break; + default: + __ipsec_errcode = EIPSEC_INVAL_FAMILY; + return -1; + } + if (prefs > (u_int)plen || prefd > (u_int)plen) { + __ipsec_errcode = EIPSEC_INVAL_PREFIXLEN; + return -1; + } + + /* create new sadb_msg to reply. */ + len = sizeof(struct sadb_msg) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(src->sa_len) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(src->sa_len) + + sizeof(struct sadb_lifetime) + + policylen; + + if ((newmsg = CALLOC(len, struct sadb_msg *)) == NULL) { + __ipsec_set_strerror(strerror(errno)); + return -1; + } + ep = ((caddr_t)newmsg) + len; + + p = pfkey_setsadbmsg((caddr_t)newmsg, ep, type, len, + SADB_SATYPE_UNSPEC, seq, getpid()); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_SRC, src, prefs, proto); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_DST, dst, prefd, proto); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadblifetime(p, ep, SADB_EXT_LIFETIME_HARD, + 0, 0, ltime, vtime); + if (!p || p + policylen != ep) { + free(newmsg); + return -1; + } + memcpy(p, policy, policylen); + + /* send message */ + len = pfkey_send(so, newmsg, len); + free(newmsg); + + if (len < 0) + return -1; + + __ipsec_errcode = EIPSEC_NO_ERROR; + return len; } /* sending SADB_X_SPDGET or SADB_X_SPDDELETE message to the kernel */ static int pfkey_send_x5(so, type, spid) - int so; - u_int type; - u_int32_t spid; +int so; +u_int type; +u_int32_t spid; { - struct sadb_msg *newmsg; - struct sadb_x_policy xpl; - int len; - caddr_t p; - caddr_t ep; - - /* create new sadb_msg to reply. */ - len = sizeof(struct sadb_msg) - + sizeof(xpl); - - if ((newmsg = CALLOC(len, struct sadb_msg *)) == NULL) { - __ipsec_set_strerror(strerror(errno)); - return -1; - } - ep = ((caddr_t)newmsg) + len; - - p = pfkey_setsadbmsg((caddr_t)newmsg, ep, type, len, - SADB_SATYPE_UNSPEC, 0, getpid()); - if (!p) { - free(newmsg); - return -1; - } - - if (p + sizeof(xpl) != ep) { - free(newmsg); - return -1; - } - memset(&xpl, 0, sizeof(xpl)); - xpl.sadb_x_policy_len = PFKEY_UNUNIT64(sizeof(xpl)); - xpl.sadb_x_policy_exttype = SADB_X_EXT_POLICY; - xpl.sadb_x_policy_id = spid; - memcpy(p, &xpl, sizeof(xpl)); - - /* send message */ - len = pfkey_send(so, newmsg, len); - free(newmsg); - - if (len < 0) - return -1; - - __ipsec_errcode = EIPSEC_NO_ERROR; - return len; + struct sadb_msg *newmsg; + struct sadb_x_policy xpl; + int len; + caddr_t p; + caddr_t ep; + + /* create new sadb_msg to reply. */ + len = sizeof(struct sadb_msg) + + sizeof(xpl); + + if ((newmsg = CALLOC(len, struct sadb_msg *)) == NULL) { + __ipsec_set_strerror(strerror(errno)); + return -1; + } + ep = ((caddr_t)newmsg) + len; + + p = pfkey_setsadbmsg((caddr_t)newmsg, ep, type, len, + SADB_SATYPE_UNSPEC, 0, getpid()); + if (!p) { + free(newmsg); + return -1; + } + + if (p + sizeof(xpl) != ep) { + free(newmsg); + return -1; + } + memset(&xpl, 0, sizeof(xpl)); + xpl.sadb_x_policy_len = PFKEY_UNUNIT64(sizeof(xpl)); + xpl.sadb_x_policy_exttype = SADB_X_EXT_POLICY; + xpl.sadb_x_policy_id = spid; + memcpy(p, &xpl, sizeof(xpl)); + + /* send message */ + len = pfkey_send(so, newmsg, len); + free(newmsg); + + if (len < 0) + return -1; + + __ipsec_errcode = EIPSEC_NO_ERROR; + return len; } /* @@ -1591,23 +1591,23 @@ pfkey_send_x5(so, type, spid) int pfkey_open() { - int so; - const int bufsiz = 128 * 1024; /*is 128K enough?*/ - - if ((so = socket(PF_KEY, SOCK_RAW, PF_KEY_V2)) < 0) { - __ipsec_set_strerror(strerror(errno)); - return -1; - } - - /* - * This is a temporary workaround for KAME PR 154. - * Don't really care even if it fails. - */ - (void)setsockopt(so, SOL_SOCKET, SO_SNDBUF, &bufsiz, sizeof(bufsiz)); - (void)setsockopt(so, SOL_SOCKET, SO_RCVBUF, &bufsiz, sizeof(bufsiz)); - - __ipsec_errcode = EIPSEC_NO_ERROR; - return so; + int so; + const int bufsiz = 128 * 1024; /*is 128K enough?*/ + + if ((so = socket(PF_KEY, SOCK_RAW, PF_KEY_V2)) < 0) { + __ipsec_set_strerror(strerror(errno)); + return -1; + } + + /* + * This is a temporary workaround for KAME PR 154. + * Don't really care even if it fails. + */ + (void)setsockopt(so, SOL_SOCKET, SO_SNDBUF, &bufsiz, sizeof(bufsiz)); + (void)setsockopt(so, SOL_SOCKET, SO_RCVBUF, &bufsiz, sizeof(bufsiz)); + + __ipsec_errcode = EIPSEC_NO_ERROR; + return so; } /* @@ -1618,12 +1618,12 @@ pfkey_open() */ void pfkey_close(so) - int so; +int so; { - (void)close(so); + (void)close(so); - __ipsec_errcode = EIPSEC_NO_ERROR; - return; + __ipsec_errcode = EIPSEC_NO_ERROR; + return; } /* @@ -1637,54 +1637,54 @@ pfkey_close(so) */ struct sadb_msg * pfkey_recv(so) - int so; +int so; { - struct sadb_msg buf, *newmsg; - int len, reallen; - - while ((len = recv(so, (caddr_t)&buf, sizeof(buf), MSG_PEEK)) < 0) { - if (errno == EINTR) - continue; - __ipsec_set_strerror(strerror(errno)); - return NULL; - } - - if ((size_t)len < sizeof(buf)) { - recv(so, (caddr_t)&buf, sizeof(buf), 0); - __ipsec_errcode = EIPSEC_MAX; - return NULL; - } - - /* read real message */ - reallen = PFKEY_UNUNIT64(buf.sadb_msg_len); - if ((newmsg = CALLOC(reallen, struct sadb_msg *)) == 0) { - __ipsec_set_strerror(strerror(errno)); - return NULL; - } - - while ((len = recv(so, (caddr_t)newmsg, reallen, 0)) < 0) { - if (errno == EINTR) - continue; - __ipsec_set_strerror(strerror(errno)); - free(newmsg); - return NULL; - } - - if (len != reallen) { - __ipsec_errcode = EIPSEC_SYSTEM_ERROR; - free(newmsg); - return NULL; - } - - /* don't trust what the kernel says, validate! */ - if (PFKEY_UNUNIT64(newmsg->sadb_msg_len) != len) { - __ipsec_errcode = EIPSEC_SYSTEM_ERROR; - free(newmsg); - return NULL; - } - - __ipsec_errcode = EIPSEC_NO_ERROR; - return newmsg; + struct sadb_msg buf, *newmsg; + int len, reallen; + + while ((len = recv(so, (caddr_t)&buf, sizeof(buf), MSG_PEEK)) < 0) { + if (errno == EINTR) + continue; + __ipsec_set_strerror(strerror(errno)); + return NULL; + } + + if ((size_t)len < sizeof(buf)) { + recv(so, (caddr_t)&buf, sizeof(buf), 0); + __ipsec_errcode = EIPSEC_MAX; + return NULL; + } + + /* read real message */ + reallen = PFKEY_UNUNIT64(buf.sadb_msg_len); + if ((newmsg = CALLOC(reallen, struct sadb_msg *)) == 0) { + __ipsec_set_strerror(strerror(errno)); + return NULL; + } + + while ((len = recv(so, (caddr_t)newmsg, reallen, 0)) < 0) { + if (errno == EINTR) + continue; + __ipsec_set_strerror(strerror(errno)); + free(newmsg); + return NULL; + } + + if (len != reallen) { + __ipsec_errcode = EIPSEC_SYSTEM_ERROR; + free(newmsg); + return NULL; + } + + /* don't trust what the kernel says, validate! */ + if (PFKEY_UNUNIT64(newmsg->sadb_msg_len) != len) { + __ipsec_errcode = EIPSEC_SYSTEM_ERROR; + free(newmsg); + return NULL; + } + + __ipsec_errcode = EIPSEC_NO_ERROR; + return newmsg; } /* @@ -1695,17 +1695,17 @@ pfkey_recv(so) */ int pfkey_send(so, msg, len) - int so; - struct sadb_msg *msg; - int len; +int so; +struct sadb_msg *msg; +int len; { - if ((len = send(so, (caddr_t)msg, len, 0)) < 0) { - __ipsec_set_strerror(strerror(errno)); - return -1; - } + if ((len = send(so, (caddr_t)msg, len, 0)) < 0) { + __ipsec_set_strerror(strerror(errno)); + return -1; + } - __ipsec_errcode = EIPSEC_NO_ERROR; - return len; + __ipsec_errcode = EIPSEC_NO_ERROR; + return len; } /* @@ -1724,87 +1724,87 @@ pfkey_send(so, msg, len) */ int pfkey_align(msg, mhp) - struct sadb_msg *msg; - caddr_t *mhp; +struct sadb_msg *msg; +caddr_t *mhp; { - struct sadb_ext *ext; - int i; - caddr_t p; - caddr_t ep; /* XXX should be passed from upper layer */ - - /* validity check */ - if (msg == NULL || mhp == NULL) { - __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; - return -1; - } - - /* initialize */ - for (i = 0; i < SADB_EXT_MAX + 1; i++) - mhp[i] = NULL; - - mhp[0] = (caddr_t)msg; - - /* initialize */ - p = (caddr_t) msg; - ep = p + PFKEY_UNUNIT64(msg->sadb_msg_len); - - /* skip base header */ - p += sizeof(struct sadb_msg); - - while (p < ep) { - ext = (struct sadb_ext *)p; - if (ep < p + sizeof(*ext) || (size_t)PFKEY_EXTLEN(ext) < sizeof(*ext) || - ep < p + PFKEY_EXTLEN(ext)) { - /* invalid format */ - break; - } - - /* duplicate check */ - /* XXX Are there duplication either KEY_AUTH or KEY_ENCRYPT ?*/ - if (mhp[ext->sadb_ext_type] != NULL) { - __ipsec_errcode = EIPSEC_INVAL_EXTTYPE; - return -1; - } - - /* set pointer */ - switch (ext->sadb_ext_type) { - case SADB_EXT_SA: - case SADB_EXT_LIFETIME_CURRENT: - case SADB_EXT_LIFETIME_HARD: - case SADB_EXT_LIFETIME_SOFT: - case SADB_EXT_ADDRESS_SRC: - case SADB_EXT_ADDRESS_DST: - case SADB_EXT_ADDRESS_PROXY: - case SADB_EXT_KEY_AUTH: - /* XXX should to be check weak keys. */ - case SADB_EXT_KEY_ENCRYPT: - /* XXX should to be check weak keys. */ - case SADB_EXT_IDENTITY_SRC: - case SADB_EXT_IDENTITY_DST: - case SADB_EXT_SENSITIVITY: - case SADB_EXT_PROPOSAL: - case SADB_EXT_SUPPORTED_AUTH: - case SADB_EXT_SUPPORTED_ENCRYPT: - case SADB_EXT_SPIRANGE: - case SADB_X_EXT_POLICY: - case SADB_X_EXT_SA2: - mhp[ext->sadb_ext_type] = (caddr_t)ext; - break; - default: - __ipsec_errcode = EIPSEC_INVAL_EXTTYPE; - return -1; - } - - p += PFKEY_EXTLEN(ext); - } - - if (p != ep) { - __ipsec_errcode = EIPSEC_INVAL_SADBMSG; - return -1; - } - - __ipsec_errcode = EIPSEC_NO_ERROR; - return 0; + struct sadb_ext *ext; + int i; + caddr_t p; + caddr_t ep; /* XXX should be passed from upper layer */ + + /* validity check */ + if (msg == NULL || mhp == NULL) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + + /* initialize */ + for (i = 0; i < SADB_EXT_MAX + 1; i++) + mhp[i] = NULL; + + mhp[0] = (caddr_t)msg; + + /* initialize */ + p = (caddr_t) msg; + ep = p + PFKEY_UNUNIT64(msg->sadb_msg_len); + + /* skip base header */ + p += sizeof(struct sadb_msg); + + while (p < ep) { + ext = (struct sadb_ext *)p; + if (ep < p + sizeof(*ext) || (size_t)PFKEY_EXTLEN(ext) < sizeof(*ext) || + ep < p + PFKEY_EXTLEN(ext)) { + /* invalid format */ + break; + } + + /* duplicate check */ + /* XXX Are there duplication either KEY_AUTH or KEY_ENCRYPT ?*/ + if (mhp[ext->sadb_ext_type] != NULL) { + __ipsec_errcode = EIPSEC_INVAL_EXTTYPE; + return -1; + } + + /* set pointer */ + switch (ext->sadb_ext_type) { + case SADB_EXT_SA: + case SADB_EXT_LIFETIME_CURRENT: + case SADB_EXT_LIFETIME_HARD: + case SADB_EXT_LIFETIME_SOFT: + case SADB_EXT_ADDRESS_SRC: + case SADB_EXT_ADDRESS_DST: + case SADB_EXT_ADDRESS_PROXY: + case SADB_EXT_KEY_AUTH: + /* XXX should to be check weak keys. */ + case SADB_EXT_KEY_ENCRYPT: + /* XXX should to be check weak keys. */ + case SADB_EXT_IDENTITY_SRC: + case SADB_EXT_IDENTITY_DST: + case SADB_EXT_SENSITIVITY: + case SADB_EXT_PROPOSAL: + case SADB_EXT_SUPPORTED_AUTH: + case SADB_EXT_SUPPORTED_ENCRYPT: + case SADB_EXT_SPIRANGE: + case SADB_X_EXT_POLICY: + case SADB_X_EXT_SA2: + mhp[ext->sadb_ext_type] = (caddr_t)ext; + break; + default: + __ipsec_errcode = EIPSEC_INVAL_EXTTYPE; + return -1; + } + + p += PFKEY_EXTLEN(ext); + } + + if (p != ep) { + __ipsec_errcode = EIPSEC_INVAL_SADBMSG; + return -1; + } + + __ipsec_errcode = EIPSEC_NO_ERROR; + return 0; } /* @@ -1820,109 +1820,109 @@ pfkey_align(msg, mhp) */ int pfkey_check(mhp) - caddr_t *mhp; +caddr_t *mhp; { - struct sadb_msg *msg; - - /* validity check */ - if (mhp == NULL || mhp[0] == NULL) { - __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; - return -1; - } - - msg = (struct sadb_msg *)mhp[0]; - - /* check version */ - if (msg->sadb_msg_version != PF_KEY_V2) { - __ipsec_errcode = EIPSEC_INVAL_VERSION; - return -1; - } - - /* check type */ - if (msg->sadb_msg_type > SADB_MAX) { - __ipsec_errcode = EIPSEC_INVAL_MSGTYPE; - return -1; - } - - /* check SA type */ - switch (msg->sadb_msg_satype) { - case SADB_SATYPE_UNSPEC: - switch (msg->sadb_msg_type) { - case SADB_GETSPI: - case SADB_UPDATE: - case SADB_ADD: - case SADB_DELETE: - case SADB_GET: - case SADB_ACQUIRE: - case SADB_EXPIRE: - __ipsec_errcode = EIPSEC_INVAL_SATYPE; - return -1; - } - break; - case SADB_SATYPE_ESP: - case SADB_SATYPE_AH: - case SADB_X_SATYPE_IPCOMP: - switch (msg->sadb_msg_type) { - case SADB_X_SPDADD: - case SADB_X_SPDDELETE: - case SADB_X_SPDGET: - case SADB_X_SPDDUMP: - case SADB_X_SPDFLUSH: - __ipsec_errcode = EIPSEC_INVAL_SATYPE; - return -1; - } - break; - case SADB_SATYPE_RSVP: - case SADB_SATYPE_OSPFV2: - case SADB_SATYPE_RIPV2: - case SADB_SATYPE_MIP: - __ipsec_errcode = EIPSEC_NOT_SUPPORTED; - return -1; - case 1: /* XXX: What does it do ? */ - if (msg->sadb_msg_type == SADB_X_PROMISC) - break; - /*FALLTHROUGH*/ - default: - __ipsec_errcode = EIPSEC_INVAL_SATYPE; - return -1; - } - - /* check field of upper layer protocol and address family */ - if (mhp[SADB_EXT_ADDRESS_SRC] != NULL - && mhp[SADB_EXT_ADDRESS_DST] != NULL) { - struct sadb_address *src0, *dst0; - - src0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_SRC]); - dst0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_DST]); - - if (src0->sadb_address_proto != dst0->sadb_address_proto) { - __ipsec_errcode = EIPSEC_PROTO_MISMATCH; - return -1; - } - - if (PFKEY_ADDR_SADDR(src0)->sa_family - != PFKEY_ADDR_SADDR(dst0)->sa_family) { - __ipsec_errcode = EIPSEC_FAMILY_MISMATCH; - return -1; - } - - switch (PFKEY_ADDR_SADDR(src0)->sa_family) { - case AF_INET: - case AF_INET6: - break; - default: - __ipsec_errcode = EIPSEC_INVAL_FAMILY; - return -1; - } - - /* - * prefixlen == 0 is valid because there must be the case - * all addresses are matched. - */ - } - - __ipsec_errcode = EIPSEC_NO_ERROR; - return 0; + struct sadb_msg *msg; + + /* validity check */ + if (mhp == NULL || mhp[0] == NULL) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + + msg = (struct sadb_msg *)mhp[0]; + + /* check version */ + if (msg->sadb_msg_version != PF_KEY_V2) { + __ipsec_errcode = EIPSEC_INVAL_VERSION; + return -1; + } + + /* check type */ + if (msg->sadb_msg_type > SADB_MAX) { + __ipsec_errcode = EIPSEC_INVAL_MSGTYPE; + return -1; + } + + /* check SA type */ + switch (msg->sadb_msg_satype) { + case SADB_SATYPE_UNSPEC: + switch (msg->sadb_msg_type) { + case SADB_GETSPI: + case SADB_UPDATE: + case SADB_ADD: + case SADB_DELETE: + case SADB_GET: + case SADB_ACQUIRE: + case SADB_EXPIRE: + __ipsec_errcode = EIPSEC_INVAL_SATYPE; + return -1; + } + break; + case SADB_SATYPE_ESP: + case SADB_SATYPE_AH: + case SADB_X_SATYPE_IPCOMP: + switch (msg->sadb_msg_type) { + case SADB_X_SPDADD: + case SADB_X_SPDDELETE: + case SADB_X_SPDGET: + case SADB_X_SPDDUMP: + case SADB_X_SPDFLUSH: + __ipsec_errcode = EIPSEC_INVAL_SATYPE; + return -1; + } + break; + case SADB_SATYPE_RSVP: + case SADB_SATYPE_OSPFV2: + case SADB_SATYPE_RIPV2: + case SADB_SATYPE_MIP: + __ipsec_errcode = EIPSEC_NOT_SUPPORTED; + return -1; + case 1: /* XXX: What does it do ? */ + if (msg->sadb_msg_type == SADB_X_PROMISC) + break; + /*FALLTHROUGH*/ + default: + __ipsec_errcode = EIPSEC_INVAL_SATYPE; + return -1; + } + + /* check field of upper layer protocol and address family */ + if (mhp[SADB_EXT_ADDRESS_SRC] != NULL + && mhp[SADB_EXT_ADDRESS_DST] != NULL) { + struct sadb_address *src0, *dst0; + + src0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_SRC]); + dst0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_DST]); + + if (src0->sadb_address_proto != dst0->sadb_address_proto) { + __ipsec_errcode = EIPSEC_PROTO_MISMATCH; + return -1; + } + + if (PFKEY_ADDR_SADDR(src0)->sa_family + != PFKEY_ADDR_SADDR(dst0)->sa_family) { + __ipsec_errcode = EIPSEC_FAMILY_MISMATCH; + return -1; + } + + switch (PFKEY_ADDR_SADDR(src0)->sa_family) { + case AF_INET: + case AF_INET6: + break; + default: + __ipsec_errcode = EIPSEC_INVAL_FAMILY; + return -1; + } + + /* + * prefixlen == 0 is valid because there must be the case + * all addresses are matched. + */ + } + + __ipsec_errcode = EIPSEC_NO_ERROR; + return 0; } /* @@ -1931,33 +1931,33 @@ pfkey_check(mhp) */ static caddr_t pfkey_setsadbmsg(buf, lim, type, tlen, satype, seq, pid) - caddr_t buf; - caddr_t lim; - u_int type, satype; - u_int tlen; - u_int32_t seq; - pid_t pid; +caddr_t buf; +caddr_t lim; +u_int type, satype; +u_int tlen; +u_int32_t seq; +pid_t pid; { - struct sadb_msg *p; - u_int len; - - p = (struct sadb_msg *)buf; - len = sizeof(struct sadb_msg); - - if (buf + len > lim) - return NULL; - - memset(p, 0, len); - p->sadb_msg_version = PF_KEY_V2; - p->sadb_msg_type = type; - p->sadb_msg_errno = 0; - p->sadb_msg_satype = satype; - p->sadb_msg_len = PFKEY_UNIT64(tlen); - p->sadb_msg_reserved = 0; - p->sadb_msg_seq = seq; - p->sadb_msg_pid = (u_int32_t)pid; - - return(buf + len); + struct sadb_msg *p; + u_int len; + + p = (struct sadb_msg *)buf; + len = sizeof(struct sadb_msg); + + if (buf + len > lim) + return NULL; + + memset(p, 0, len); + p->sadb_msg_version = PF_KEY_V2; + p->sadb_msg_type = type; + p->sadb_msg_errno = 0; + p->sadb_msg_satype = satype; + p->sadb_msg_len = PFKEY_UNIT64(tlen); + p->sadb_msg_reserved = 0; + p->sadb_msg_seq = seq; + p->sadb_msg_pid = (u_int32_t)pid; + + return(buf + len); } /* @@ -1966,31 +1966,31 @@ pfkey_setsadbmsg(buf, lim, type, tlen, satype, seq, pid) */ static caddr_t pfkey_setsadbsa(buf, lim, spi, wsize, auth, enc, flags) - caddr_t buf; - caddr_t lim; - u_int32_t spi, flags; - u_int wsize, auth, enc; +caddr_t buf; +caddr_t lim; +u_int32_t spi, flags; +u_int wsize, auth, enc; { - struct sadb_sa *p; - u_int len; - - p = (struct sadb_sa *)buf; - len = sizeof(struct sadb_sa); - - if (buf + len > lim) - return NULL; - - memset(p, 0, len); - p->sadb_sa_len = PFKEY_UNIT64(len); - p->sadb_sa_exttype = SADB_EXT_SA; - p->sadb_sa_spi = spi; - p->sadb_sa_replay = wsize; - p->sadb_sa_state = SADB_SASTATE_LARVAL; - p->sadb_sa_auth = auth; - p->sadb_sa_encrypt = enc; - p->sadb_sa_flags = flags; - - return(buf + len); + struct sadb_sa *p; + u_int len; + + p = (struct sadb_sa *)buf; + len = sizeof(struct sadb_sa); + + if (buf + len > lim) + return NULL; + + memset(p, 0, len); + p->sadb_sa_len = PFKEY_UNIT64(len); + p->sadb_sa_exttype = SADB_EXT_SA; + p->sadb_sa_spi = spi; + p->sadb_sa_replay = wsize; + p->sadb_sa_state = SADB_SASTATE_LARVAL; + p->sadb_sa_auth = auth; + p->sadb_sa_encrypt = enc; + p->sadb_sa_flags = flags; + + return(buf + len); } /* @@ -2000,32 +2000,32 @@ pfkey_setsadbsa(buf, lim, spi, wsize, auth, enc, flags) */ static caddr_t pfkey_setsadbaddr(buf, lim, exttype, saddr, prefixlen, ul_proto) - caddr_t buf; - caddr_t lim; - u_int exttype; - struct sockaddr *saddr; - u_int prefixlen; - u_int ul_proto; +caddr_t buf; +caddr_t lim; +u_int exttype; +struct sockaddr *saddr; +u_int prefixlen; +u_int ul_proto; { - struct sadb_address *p; - u_int len; + struct sadb_address *p; + u_int len; - p = (struct sadb_address *)buf; - len = sizeof(struct sadb_address) + PFKEY_ALIGN8(saddr->sa_len); + p = (struct sadb_address *)buf; + len = sizeof(struct sadb_address) + PFKEY_ALIGN8(saddr->sa_len); - if (buf + len > lim) - return NULL; + if (buf + len > lim) + return NULL; - memset(p, 0, len); - p->sadb_address_len = PFKEY_UNIT64(len); - p->sadb_address_exttype = exttype & 0xffff; - p->sadb_address_proto = ul_proto & 0xff; - p->sadb_address_prefixlen = prefixlen; - p->sadb_address_reserved = 0; + memset(p, 0, len); + p->sadb_address_len = PFKEY_UNIT64(len); + p->sadb_address_exttype = exttype & 0xffff; + p->sadb_address_proto = ul_proto & 0xff; + p->sadb_address_prefixlen = prefixlen; + p->sadb_address_reserved = 0; - memcpy(p + 1, saddr, saddr->sa_len); + memcpy(p + 1, saddr, saddr->sa_len); - return(buf + len); + return(buf + len); } /* @@ -2034,29 +2034,29 @@ pfkey_setsadbaddr(buf, lim, exttype, saddr, prefixlen, ul_proto) */ static caddr_t pfkey_setsadbkey(buf, lim, type, key, keylen) - caddr_t buf; - caddr_t lim; - caddr_t key; - u_int type, keylen; +caddr_t buf; +caddr_t lim; +caddr_t key; +u_int type, keylen; { - struct sadb_key *p; - u_int len; + struct sadb_key *p; + u_int len; - p = (struct sadb_key *)buf; - len = sizeof(struct sadb_key) + PFKEY_ALIGN8(keylen); + p = (struct sadb_key *)buf; + len = sizeof(struct sadb_key) + PFKEY_ALIGN8(keylen); - if (buf + len > lim) - return NULL; + if (buf + len > lim) + return NULL; - memset(p, 0, len); - p->sadb_key_len = PFKEY_UNIT64(len); - p->sadb_key_exttype = type; - p->sadb_key_bits = keylen << 3; - p->sadb_key_reserved = 0; + memset(p, 0, len); + p->sadb_key_len = PFKEY_UNIT64(len); + p->sadb_key_exttype = type; + p->sadb_key_bits = keylen << 3; + p->sadb_key_reserved = 0; - memcpy(p + 1, key, keylen); + memcpy(p + 1, key, keylen); - return buf + len; + return buf + len; } /* @@ -2065,44 +2065,44 @@ pfkey_setsadbkey(buf, lim, type, key, keylen) */ static caddr_t pfkey_setsadblifetime(buf, lim, type, l_alloc, l_bytes, l_addtime, l_usetime) - caddr_t buf; - caddr_t lim; - u_int type; - u_int32_t l_alloc, l_bytes, l_addtime, l_usetime; +caddr_t buf; +caddr_t lim; +u_int type; +u_int32_t l_alloc, l_bytes, l_addtime, l_usetime; { - struct sadb_lifetime *p; - u_int len; - - p = (struct sadb_lifetime *)buf; - len = sizeof(struct sadb_lifetime); - - if (buf + len > lim) - return NULL; - - memset(p, 0, len); - p->sadb_lifetime_len = PFKEY_UNIT64(len); - p->sadb_lifetime_exttype = type; - - switch (type) { - case SADB_EXT_LIFETIME_SOFT: - p->sadb_lifetime_allocations - = (l_alloc * soft_lifetime_allocations_rate) /100; - p->sadb_lifetime_bytes - = (l_bytes * soft_lifetime_bytes_rate) /100; - p->sadb_lifetime_addtime - = (l_addtime * soft_lifetime_addtime_rate) /100; - p->sadb_lifetime_usetime - = (l_usetime * soft_lifetime_usetime_rate) /100; - break; - case SADB_EXT_LIFETIME_HARD: - p->sadb_lifetime_allocations = l_alloc; - p->sadb_lifetime_bytes = l_bytes; - p->sadb_lifetime_addtime = l_addtime; - p->sadb_lifetime_usetime = l_usetime; - break; - } - - return buf + len; + struct sadb_lifetime *p; + u_int len; + + p = (struct sadb_lifetime *)buf; + len = sizeof(struct sadb_lifetime); + + if (buf + len > lim) + return NULL; + + memset(p, 0, len); + p->sadb_lifetime_len = PFKEY_UNIT64(len); + p->sadb_lifetime_exttype = type; + + switch (type) { + case SADB_EXT_LIFETIME_SOFT: + p->sadb_lifetime_allocations + = (l_alloc * soft_lifetime_allocations_rate) /100; + p->sadb_lifetime_bytes + = (l_bytes * soft_lifetime_bytes_rate) /100; + p->sadb_lifetime_addtime + = (l_addtime * soft_lifetime_addtime_rate) /100; + p->sadb_lifetime_usetime + = (l_usetime * soft_lifetime_usetime_rate) /100; + break; + case SADB_EXT_LIFETIME_HARD: + p->sadb_lifetime_allocations = l_alloc; + p->sadb_lifetime_bytes = l_bytes; + p->sadb_lifetime_addtime = l_addtime; + p->sadb_lifetime_usetime = l_usetime; + break; + } + + return buf + len; } /* @@ -2111,28 +2111,28 @@ pfkey_setsadblifetime(buf, lim, type, l_alloc, l_bytes, l_addtime, l_usetime) */ static caddr_t pfkey_setsadbxsa2(buf, lim, mode0, reqid) - caddr_t buf; - caddr_t lim; - u_int32_t mode0; - u_int32_t reqid; +caddr_t buf; +caddr_t lim; +u_int32_t mode0; +u_int32_t reqid; { - struct sadb_x_sa2 *p; - u_int8_t mode = mode0 & 0xff; - u_int len; + struct sadb_x_sa2 *p; + u_int8_t mode = mode0 & 0xff; + u_int len; - p = (struct sadb_x_sa2 *)buf; - len = sizeof(struct sadb_x_sa2); + p = (struct sadb_x_sa2 *)buf; + len = sizeof(struct sadb_x_sa2); - if (buf + len > lim) - return NULL; + if (buf + len > lim) + return NULL; - memset(p, 0, len); - p->sadb_x_sa2_len = PFKEY_UNIT64(len); - p->sadb_x_sa2_exttype = SADB_X_EXT_SA2; - p->sadb_x_sa2_mode = mode; - p->sadb_x_sa2_reqid = reqid; + memset(p, 0, len); + p->sadb_x_sa2_len = PFKEY_UNIT64(len); + p->sadb_x_sa2_exttype = SADB_X_EXT_SA2; + p->sadb_x_sa2_mode = mode; + p->sadb_x_sa2_reqid = reqid; - return(buf + len); + return(buf + len); } #endif /* ndef MDNS_NO_IPSEC */ diff --git a/mDNSMacOSX/safe_vproc.c b/mDNSMacOSX/safe_vproc.c deleted file mode 100644 index a6b737e..0000000 --- a/mDNSMacOSX/safe_vproc.c +++ /dev/null @@ -1,84 +0,0 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2009 Apple Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include "safe_vproc.h" -#include "mDNSDebug.h" -#include - -#if defined(VPROC_HAS_TRANSACTIONS) && !TARGET_OS_EMBEDDED - -static vproc_transaction_t transaction = NULL; - -void safe_vproc_transaction_begin(void) - { - if (vproc_transaction_begin) - { - if (transaction) { LogMsg("safe_vproc_transaction_begin: Already have a transaction"); } - else transaction = vproc_transaction_begin(NULL); - } - } - -void safe_vproc_transaction_end(void) - { - if (vproc_transaction_end) - { - if (transaction) { vproc_transaction_end(NULL, transaction); transaction = NULL; } - else LogMsg("safe_vproc_transaction_end: No current transaction"); - } - } - -#else - -#if ! TARGET_OS_EMBEDDED -#include -#include - -CF_EXPORT CFDictionaryRef _CFCopySystemVersionDictionary(void); -CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey; -#define OSXVers_10_6_SnowLeopard 10 - -void safe_vproc_transaction_begin(void) - { - static int majorversion = 0; - if (!majorversion) - { - CFDictionaryRef vers = _CFCopySystemVersionDictionary(); - if (vers) - { - char buildver[256]; - CFStringRef cfbuildver = CFDictionaryGetValue(vers, _kCFSystemVersionBuildVersionKey); - if (cfbuildver && CFStringGetCString(cfbuildver, buildver, sizeof(buildver), kCFStringEncodingUTF8)) - sscanf(buildver, "%d", &majorversion); - CFRelease(vers); - } - if (!majorversion || majorversion >= OSXVers_10_6_SnowLeopard) - LogMsg("Compiled without vproc_transaction support"); - } - } - -#else - -void safe_vproc_transaction_begin(void) { } - -#endif - -void safe_vproc_transaction_end(void) { } - -#endif diff --git a/mDNSPosix/Client.c b/mDNSPosix/Client.c index c21b1ee..1b43c07 100755 --- a/mDNSPosix/Client.c +++ b/mDNSPosix/Client.c @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,7 +22,7 @@ #include #include -#include "mDNSEmbeddedAPI.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" @@ -37,20 +37,20 @@ mDNSexport const char ProgramName[] = "mDNSClientPosix"; static const char *gProgramName = ProgramName; static void BrowseCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) - // A callback from the core mDNS code that indicates that we've received a - // response to our query. Note that this code runs on the main thread - // (in fact, there is only one thread!), so we can safely printf the results. +// A callback from the core mDNS code that indicates that we've received a +// response to our query. Note that this code runs on the main thread +// (in fact, there is only one thread!), so we can safely printf the results. { domainlabel name; - domainname type; - domainname domain; - char nameC [MAX_DOMAIN_LABEL+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL. - char typeC [MAX_ESCAPED_DOMAIN_NAME]; - char domainC[MAX_ESCAPED_DOMAIN_NAME]; + domainname type; + domainname domain; + char nameC [MAX_DOMAIN_LABEL+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL. + char typeC [MAX_ESCAPED_DOMAIN_NAME]; + char domainC[MAX_ESCAPED_DOMAIN_NAME]; const char *state; - (void)m; // Unused - (void)question; // Unused + (void)m; // Unused + (void)question; // Unused assert(answer->rrtype == kDNSType_PTR); @@ -70,25 +70,25 @@ static void BrowseCallback(mDNS *const m, DNSQuestion *question, const ResourceR } static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType, mDNSBool printExplanation) - // Checks that serviceType is a reasonable service type - // label and, if it isn't and printExplanation is true, prints - // an explanation of why not. +// Checks that serviceType is a reasonable service type +// label and, if it isn't and printExplanation is true, prints +// an explanation of why not. { mDNSBool result; - + result = mDNStrue; if (result && strlen(serviceType) > 63) { if (printExplanation) { - fprintf(stderr, - "%s: Service type specified by -t is too long (must be 63 characters or less)\n", + fprintf(stderr, + "%s: Service type specified by -t is too long (must be 63 characters or less)\n", gProgramName); } result = mDNSfalse; } if (result && serviceType[0] == 0) { if (printExplanation) { - fprintf(stderr, - "%s: Service type specified by -t can't be empty\n", + fprintf(stderr, + "%s: Service type specified by -t can't be empty\n", gProgramName); } result = mDNSfalse; @@ -101,7 +101,7 @@ static const char kDefaultDomain[] = "local."; static void PrintUsage() { - fprintf(stderr, + fprintf(stderr, "Usage: %s [-v level] [-t type] [-d domain]\n", gProgramName); fprintf(stderr, " -v verbose mode, level is a number from 0 to 2\n"); @@ -116,55 +116,55 @@ static const char *gServiceType = kDefaultServiceType; static const char *gServiceDomain = kDefaultDomain; static void ParseArguments(int argc, char **argv) - // Parses our command line arguments into the global variables - // listed above. +// Parses our command line arguments into the global variables +// listed above. { int ch; - + // Set gProgramName to the last path component of argv[0] - + gProgramName = strrchr(argv[0], '/'); if (gProgramName == NULL) { gProgramName = argv[0]; } else { gProgramName += 1; } - + // Parse command line options using getopt. - + do { ch = getopt(argc, argv, "v:t:d:"); if (ch != -1) { switch (ch) { - case 'v': - gMDNSPlatformPosixVerboseLevel = atoi(optarg); - if (gMDNSPlatformPosixVerboseLevel < 0 || gMDNSPlatformPosixVerboseLevel > 2) { - fprintf(stderr, - "%s: Verbose mode must be in the range 0..2\n", - gProgramName); - exit(1); - } - break; - case 't': - gServiceType = optarg; - if ( ! CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) { - exit(1); - } - break; - case 'd': - gServiceDomain = optarg; - break; - case '?': - default: - PrintUsage(); + case 'v': + gMDNSPlatformPosixVerboseLevel = atoi(optarg); + if (gMDNSPlatformPosixVerboseLevel < 0 || gMDNSPlatformPosixVerboseLevel > 2) { + fprintf(stderr, + "%s: Verbose mode must be in the range 0..2\n", + gProgramName); exit(1); - break; + } + break; + case 't': + gServiceType = optarg; + if ( !CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) { + exit(1); + } + break; + case 'd': + gServiceDomain = optarg; + break; + case '?': + default: + PrintUsage(); + exit(1); + break; } } } while (ch != -1); // Check for any left over command line arguments. - + if (optind != argc) { fprintf(stderr, "%s: Unexpected argument '%s'\n", gProgramName, argv[optind]); exit(1); @@ -172,44 +172,44 @@ static void ParseArguments(int argc, char **argv) } int main(int argc, char **argv) - // The program's main entry point. The program does a trivial - // mDNS query, looking for all AFP servers in the local domain. +// The program's main entry point. The program does a trivial +// mDNS query, looking for all AFP servers in the local domain. { - int result; - mStatus status; + int result; + mStatus status; DNSQuestion question; - domainname type; - domainname domain; + domainname type; + domainname domain; // Parse our command line arguments. This won't come back if there's an error. ParseArguments(argc, argv); // Initialise the mDNS core. - status = mDNS_Init(&mDNSStorage, &PlatformStorage, - gRRCache, RR_CACHE_SIZE, - mDNS_Init_DontAdvertiseLocalAddresses, - mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); + status = mDNS_Init(&mDNSStorage, &PlatformStorage, + gRRCache, RR_CACHE_SIZE, + mDNS_Init_DontAdvertiseLocalAddresses, + mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); if (status == mStatus_NoError) { - + // Construct and start the query. - + MakeDomainNameFromDNSNameString(&type, gServiceType); MakeDomainNameFromDNSNameString(&domain, gServiceDomain); - 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 + status = mDNS_StartBrowse(&mDNSStorage, &question, &type, &domain, mDNSInterface_Any, 0, mDNSfalse, mDNSfalse, BrowseCallback, NULL); + + // Run the platform main event loop until the user types ^C. + // The BrowseCallback routine is responsible for printing // any results that we find. - + if (status == mStatus_NoError) { fprintf(stderr, "Hit ^C when you're bored waiting for responses.\n"); - ExampleClientEventLoop(&mDNSStorage); + ExampleClientEventLoop(&mDNSStorage); mDNS_StopQuery(&mDNSStorage, &question); - mDNS_Close(&mDNSStorage); + mDNS_Close(&mDNSStorage); } } - + if (status == mStatus_NoError) { result = 0; } else { diff --git a/mDNSPosix/ExampleClientApp.c b/mDNSPosix/ExampleClientApp.c index e05b541..f23248b 100644 --- a/mDNSPosix/ExampleClientApp.c +++ b/mDNSPosix/ExampleClientApp.c @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,15 +15,15 @@ * limitations under the License. */ -#include // For printf() -#include // For exit() etc. -#include // For strlen() etc. -#include // For select() -#include // For errno, EINTR -#include // For INADDR_NONE -#include // For inet_addr() -#include // For gethostbyname() -#include // For SIGINT, etc. +#include // For printf() +#include // For exit() etc. +#include // For strlen() etc. +#include // For select() +#include // For errno, EINTR +#include // For INADDR_NONE +#include // For inet_addr() +#include // For gethostbyname() +#include // For SIGINT, etc. #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 @@ -34,58 +34,58 @@ static volatile mDNSBool StopNow; mDNSlocal void HandleSIG(int signal) - { - (void)signal; // Unused - debugf("%s",""); - debugf("HandleSIG"); - StopNow = mDNStrue; - } +{ + (void)signal; // Unused + debugf("%s",""); + debugf("HandleSIG"); + StopNow = mDNStrue; +} mDNSexport void ExampleClientEventLoop(mDNS *const m) - { - signal(SIGINT, HandleSIG); // SIGINT is what you get for a Ctrl-C - signal(SIGTERM, HandleSIG); +{ + signal(SIGINT, HandleSIG); // SIGINT is what you get for a Ctrl-C + signal(SIGTERM, HandleSIG); + + while (!StopNow) + { + int nfds = 0; + fd_set readfds; + struct timeval timeout; + int result; + + // 1. Set up the fd_set as usual here. + // This example client has no file descriptors of its own, + // but a real application would call FD_SET to add them to the set here + FD_ZERO(&readfds); + + // 2. Set up the timeout. + // This example client has no other work it needs to be doing, + // so we set an effectively infinite timeout + timeout.tv_sec = 0x3FFFFFFF; + timeout.tv_usec = 0; + + // 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout + mDNSPosixGetFDSet(m, &nfds, &readfds, &timeout); + + // 4. Call select as normal + verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec); + result = select(nfds, &readfds, NULL, NULL, &timeout); + + if (result < 0) + { + verbosedebugf("select() returned %d errno %d", result, errno); + if (errno != EINTR) StopNow = mDNStrue; + } + else + { + // 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work + mDNSPosixProcessFDSet(m, &readfds); - while (!StopNow) - { - int nfds = 0; - fd_set readfds; - struct timeval timeout; - int result; - - // 1. Set up the fd_set as usual here. - // This example client has no file descriptors of its own, - // but a real application would call FD_SET to add them to the set here - FD_ZERO(&readfds); - - // 2. Set up the timeout. - // This example client has no other work it needs to be doing, - // so we set an effectively infinite timeout - timeout.tv_sec = 0x3FFFFFFF; - timeout.tv_usec = 0; - - // 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout - mDNSPosixGetFDSet(m, &nfds, &readfds, &timeout); - - // 4. Call select as normal - verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec); - result = select(nfds, &readfds, NULL, NULL, &timeout); - - if (result < 0) - { - verbosedebugf("select() returned %d errno %d", result, errno); - if (errno != EINTR) StopNow = mDNStrue; - } - else - { - // 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work - mDNSPosixProcessFDSet(m, &readfds); - - // 6. This example client has no other work it needs to be doing, - // but a real client would do its work here - // ... (do work) ... - } - } + // 6. This example client has no other work it needs to be doing, + // but a real client would do its work here + // ... (do work) ... + } + } - debugf("Exiting"); - } + debugf("Exiting"); +} diff --git a/mDNSPosix/ExampleClientApp.h b/mDNSPosix/ExampleClientApp.h index ab643af..53f7f17 100644 --- a/mDNSPosix/ExampleClientApp.h +++ b/mDNSPosix/ExampleClientApp.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/mDNSPosix/Identify.c b/mDNSPosix/Identify.c index 2ca9fc6..745ff21 100644 --- a/mDNSPosix/Identify.c +++ b/mDNSPosix/Identify.c @@ -5,27 +5,15 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * - * Formatting notes: - * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion - * on C indentation can be found on the web, such as , - * but for the sake of brevity here I will say just this: Curly braces are not syntactially - * part of an "if" statement; they are the beginning and ending markers of a compound statement; - * therefore common sense dictates that if they are part of a compound statement then they - * should be indented to the same level as everything else in that compound statement. - * Indenting curly braces at the same level as the "if" implies that curly braces are - * part of the "if", which is false. (This is as misleading as people who write "char* x,y;" - * thinking that variables x and y are both of type "char*" -- and anyone who doesn't - * understand why variable y is not of type "char*" just proves the point that poor code - * layout leads people to unfortunate misunderstandings about how the C language really works.) */ //************************************************************************************************************* @@ -46,12 +34,12 @@ #include #include #include -#include // For n_long, required by below -#include // For IPTOS_LOWDELAY etc. +#include // For n_long, required by below +#include // For IPTOS_LOWDELAY etc. #include #include -#include "mDNSEmbeddedAPI.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" @@ -64,7 +52,7 @@ static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals static CacheEntity gRRCache[RR_CACHE_SIZE]; mDNSexport const char ProgramName[] = "mDNSIdentify"; -static volatile int StopNow; // 0 means running, 1 means stop because we got an answer, 2 means stop because of Ctrl-C +static volatile int StopNow; // 0 means running, 1 means stop because we got an answer, 2 means stop because of Ctrl-C static volatile int NumAnswers, NumAddr, NumAAAA, NumHINFO; static char hostname[MAX_ESCAPED_DOMAIN_NAME], hardware[256], software[256]; static mDNSAddr lastsrc, hostaddr, target; @@ -76,298 +64,310 @@ static mDNSOpaque16 lastid, id; // Special version of printf that knows how to print IP addresses, DNS-format name strings, etc. mDNSlocal mDNSu32 mprintf(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); mDNSlocal mDNSu32 mprintf(const char *format, ...) - { - mDNSu32 length; - unsigned char buffer[512]; - va_list ptr; - va_start(ptr,format); - length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr); - va_end(ptr); - printf("%s", buffer); - return(length); - } +{ + mDNSu32 length; + unsigned char buffer[512]; + va_list ptr; + va_start(ptr,format); + length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr); + va_end(ptr); + printf("%s", buffer); + return(length); +} //************************************************************************************************************* // Main code mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, - const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport, - const mDNSInterfaceID InterfaceID) - { - (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 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); - } + const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport, + 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 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); +} mDNSlocal void NameCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) - { - (void)m; // Unused - (void)question; // Unused - (void)AddRecord;// Unused - if (!id.NotAnInteger) id = lastid; - if (answer->rrtype == kDNSType_PTR || answer->rrtype == kDNSType_CNAME) - { - ConvertDomainNameToCString(&answer->rdata->u.name, hostname); - StopNow = 1; - mprintf("%##s %s %##s\n", answer->name->c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c); - } - } +{ + (void)m; // Unused + (void)question; // Unused + (void)AddRecord; // Unused + if (!id.NotAnInteger) id = lastid; + if (answer->rrtype == kDNSType_PTR || answer->rrtype == kDNSType_CNAME) + { + ConvertDomainNameToCString(&answer->rdata->u.name, hostname); + StopNow = 1; + mprintf("%##s %s %##s\n", answer->name->c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c); + } +} mDNSlocal void InfoCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) - { - (void)m; // Unused - (void)question; // Unused - (void)AddRecord;// Unused - if (answer->rrtype == kDNSType_A) - { - if (!id.NotAnInteger) id = lastid; - NumAnswers++; - NumAddr++; - mprintf("%##s %s %.4a\n", answer->name->c, DNSTypeName(answer->rrtype), &answer->rdata->u.ipv4); - hostaddr.type = mDNSAddrType_IPv4; // Prefer v4 target to v6 target, for now - hostaddr.ip.v4 = answer->rdata->u.ipv4; - } - else if (answer->rrtype == kDNSType_AAAA) - { - if (!id.NotAnInteger) id = lastid; - NumAnswers++; - NumAAAA++; - mprintf("%##s %s %.16a\n", answer->name->c, DNSTypeName(answer->rrtype), &answer->rdata->u.ipv6); - if (!hostaddr.type) // Prefer v4 target to v6 target, for now - { - hostaddr.type = mDNSAddrType_IPv6; - hostaddr.ip.v6 = answer->rdata->u.ipv6; - } - } - else if (answer->rrtype == kDNSType_HINFO) - { - mDNSu8 *p = answer->rdata->u.data; - strncpy(hardware, (char*)(p+1), p[0]); - hardware[p[0]] = 0; - p += 1 + p[0]; - strncpy(software, (char*)(p+1), p[0]); - software[p[0]] = 0; - NumAnswers++; - NumHINFO++; - } - - // If we've got everything we're looking for, don't need to wait any more - if (/*NumHINFO && */ (NumAddr || NumAAAA)) StopNow = 1; - } +{ + (void)m; // Unused + (void)question; // Unused + (void)AddRecord; // Unused + if (answer->rrtype == kDNSType_A) + { + if (!id.NotAnInteger) id = lastid; + NumAnswers++; + NumAddr++; + mprintf("%##s %s %.4a\n", answer->name->c, DNSTypeName(answer->rrtype), &answer->rdata->u.ipv4); + hostaddr.type = mDNSAddrType_IPv4; // Prefer v4 target to v6 target, for now + hostaddr.ip.v4 = answer->rdata->u.ipv4; + } + else if (answer->rrtype == kDNSType_AAAA) + { + if (!id.NotAnInteger) id = lastid; + NumAnswers++; + NumAAAA++; + mprintf("%##s %s %.16a\n", answer->name->c, DNSTypeName(answer->rrtype), &answer->rdata->u.ipv6); + if (!hostaddr.type) // Prefer v4 target to v6 target, for now + { + hostaddr.type = mDNSAddrType_IPv6; + hostaddr.ip.v6 = answer->rdata->u.ipv6; + } + } + else if (answer->rrtype == kDNSType_HINFO) + { + mDNSu8 *p = answer->rdata->u.data; + strncpy(hardware, (char*)(p+1), p[0]); + hardware[p[0]] = 0; + p += 1 + p[0]; + strncpy(software, (char*)(p+1), p[0]); + software[p[0]] = 0; + NumAnswers++; + NumHINFO++; + } + + // If we've got everything we're looking for, don't need to wait any more + if (/*NumHINFO && */ (NumAddr || NumAAAA)) StopNow = 1; +} mDNSlocal void ServicesCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) - { - (void)m; // Unused - (void)question; // Unused - (void)AddRecord;// Unused - // Right now the mDNSCore targeted-query code is incomplete -- - // it issues targeted queries, but accepts answers from anywhere - // For now, we'll just filter responses here so we don't get confused by responses from someone else - if (answer->rrtype == kDNSType_PTR && mDNSSameAddress(&lastsrc, &target)) - { - NumAnswers++; - mprintf("%##s %s %##s\n", answer->name->c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c); - } - } +{ + (void)m; // Unused + (void)question; // Unused + (void)AddRecord; // Unused + // Right now the mDNSCore targeted-query code is incomplete -- + // it issues targeted queries, but accepts answers from anywhere + // For now, we'll just filter responses here so we don't get confused by responses from someone else + if (answer->rrtype == kDNSType_PTR && mDNSSameAddress(&lastsrc, &target)) + { + NumAnswers++; + mprintf("%##s %s %##s\n", answer->name->c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c); + } +} mDNSlocal void WaitForAnswer(mDNS *const m, int seconds) - { - struct timeval end; - gettimeofday(&end, NULL); - end.tv_sec += seconds; - StopNow = 0; - NumAnswers = 0; - while (!StopNow) - { - int nfds = 0; - fd_set readfds; - struct timeval now, remain = end; - int result; - - FD_ZERO(&readfds); - gettimeofday(&now, NULL); - if (remain.tv_usec < now.tv_usec) { remain.tv_usec += 1000000; remain.tv_sec--; } - if (remain.tv_sec < now.tv_sec) - { - if (!NumAnswers) printf("No response after %d seconds\n", seconds); - return; - } - remain.tv_usec -= now.tv_usec; - remain.tv_sec -= now.tv_sec; - mDNSPosixGetFDSet(m, &nfds, &readfds, &remain); - result = select(nfds, &readfds, NULL, NULL, &remain); - if (result >= 0) mDNSPosixProcessFDSet(m, &readfds); - else if (errno != EINTR) StopNow = 2; - } - } +{ + struct timeval end; + gettimeofday(&end, NULL); + end.tv_sec += seconds; + StopNow = 0; + NumAnswers = 0; + while (!StopNow) + { + int nfds = 0; + fd_set readfds; + struct timeval now, remain = end; + int result; + + FD_ZERO(&readfds); + gettimeofday(&now, NULL); + if (remain.tv_usec < now.tv_usec) { remain.tv_usec += 1000000; remain.tv_sec--; } + if (remain.tv_sec < now.tv_sec) + { + if (!NumAnswers) printf("No response after %d seconds\n", seconds); + return; + } + remain.tv_usec -= now.tv_usec; + remain.tv_sec -= now.tv_sec; + mDNSPosixGetFDSet(m, &nfds, &readfds, &remain); + result = select(nfds, &readfds, NULL, NULL, &remain); + if (result >= 0) mDNSPosixProcessFDSet(m, &readfds); + else if (errno != EINTR) StopNow = 2; + } +} mDNSlocal mStatus StartQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback) - { - lastsrc = zeroAddr; - if (qname) MakeDomainNameFromDNSNameString(&q->qname, qname); - q->InterfaceID = mDNSInterface_Any; - q->Target = target ? *target : zeroAddr; - q->TargetPort = MulticastDNSPort; - q->TargetQID = zeroID; - q->qtype = qtype; - q->qclass = kDNSClass_IN; - q->LongLived = mDNSfalse; - q->ExpectUnique = mDNSfalse; // Don't want to stop after the first response packet - q->ForceMCast = mDNStrue; // Query via multicast, even for apparently uDNS names like 1.1.1.17.in-addr.arpa. - q->ReturnIntermed = mDNStrue; - q->SuppressUnusable = mDNSfalse; - q->SearchListIndex = 0; - q->AppendSearchDomains = 0; - q->RetryWithSearchDomains = mDNSfalse; - q->TimeoutQuestion = 0; - q->WakeOnResolve = 0; - q->qnameOrig = mDNSNULL; - q->QuestionCallback = callback; - q->QuestionContext = NULL; - - //mprintf("%##s %s ?\n", q->qname.c, DNSTypeName(qtype)); - return(mDNS_StartQuery(&mDNSStorage, q)); - } +{ + lastsrc = zeroAddr; + if (qname) MakeDomainNameFromDNSNameString(&q->qname, qname); + q->InterfaceID = mDNSInterface_Any; + q->flags = 0; + q->Target = target ? *target : zeroAddr; + q->TargetPort = MulticastDNSPort; + q->TargetQID = zeroID; + q->qtype = qtype; + q->qclass = kDNSClass_IN; + q->LongLived = mDNSfalse; + q->ExpectUnique = mDNSfalse; // Don't want to stop after the first response packet + q->ForceMCast = mDNStrue; // Query via multicast, even for apparently uDNS names like 1.1.1.17.in-addr.arpa. + q->ReturnIntermed = mDNStrue; + q->SuppressUnusable = mDNSfalse; + q->SearchListIndex = 0; + q->AppendSearchDomains = 0; + q->RetryWithSearchDomains = mDNSfalse; + q->TimeoutQuestion = 0; + q->ValidationRequired = 0; + q->ValidatingResponse = 0; + q->WakeOnResolve = 0; + q->UseBrackgroundTrafficClass = mDNSfalse; + q->qnameOrig = mDNSNULL; + q->QuestionCallback = callback; + q->QuestionContext = NULL; + + //mprintf("%##s %s ?\n", q->qname.c, DNSTypeName(qtype)); + return(mDNS_StartQuery(&mDNSStorage, q)); +} mDNSlocal void DoOneQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback) - { - mStatus status = StartQuery(q, qname, qtype, target, callback); - if (status != mStatus_NoError) - StopNow = 2; - else - { - WaitForAnswer(&mDNSStorage, 4); - mDNS_StopQuery(&mDNSStorage, q); - } - } +{ + mStatus status = StartQuery(q, qname, qtype, target, callback); + if (status != mStatus_NoError) + StopNow = 2; + else + { + WaitForAnswer(&mDNSStorage, 4); + mDNS_StopQuery(&mDNSStorage, q); + } +} mDNSlocal int DoQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback) - { - DoOneQuery(q, qname, qtype, target, callback); - if (StopNow == 0 && NumAnswers == 0 && target && target->type) - { - mprintf("%##s %s Trying multicast\n", q->qname.c, DNSTypeName(q->qtype)); - DoOneQuery(q, qname, qtype, NULL, callback); - } - if (StopNow == 0 && NumAnswers == 0) - mprintf("%##s %s *** No Answer ***\n", q->qname.c, DNSTypeName(q->qtype)); - return(StopNow); - } +{ + DoOneQuery(q, qname, qtype, target, callback); + if (StopNow == 0 && NumAnswers == 0 && target && target->type) + { + mprintf("%##s %s Trying multicast\n", q->qname.c, DNSTypeName(q->qtype)); + DoOneQuery(q, qname, qtype, NULL, callback); + } + if (StopNow == 0 && NumAnswers == 0) + mprintf("%##s %s *** No Answer ***\n", q->qname.c, DNSTypeName(q->qtype)); + return(StopNow); +} mDNSlocal void HandleSIG(int signal) - { - (void)signal; // Unused - debugf("%s",""); - debugf("HandleSIG"); - StopNow = 2; - } +{ + (void)signal; // Unused + 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; +{ + 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; + struct in6_addr s6; #endif - char buffer[256]; - DNSQuestion q; - - if (argc < 2) goto usage; - - // Since this is a special command-line tool, we want LogMsg() errors to go to stderr, not syslog - mDNS_DebugMode = mDNStrue; - + char buffer[256]; + DNSQuestion q; + + if (argc < 2) goto usage; + + // Since this is a special command-line tool, we want LogMsg() errors to go to stderr, not syslog + mDNS_DebugMode = mDNStrue; + // Initialise the mDNS core. - status = mDNS_Init(&mDNSStorage, &PlatformStorage, - gRRCache, RR_CACHE_SIZE, - mDNS_Init_DontAdvertiseLocalAddresses, - mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); - if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %d\n", (int)status); return(status); } - - signal(SIGINT, HandleSIG); // SIGINT is what you get for a Ctrl-C - signal(SIGTERM, HandleSIG); - - while (this_arg < argc) - { - char *arg = argv[this_arg++]; - if (this_arg > 2) printf("\n"); - - lastid = id = zeroID; - hostaddr = target = zeroAddr; - hostname[0] = hardware[0] = software[0] = 0; - NumAddr = NumAAAA = NumHINFO = 0; - - if (inet_pton(AF_INET, arg, &s4) == 1) - { - mDNSu8 *p = (mDNSu8 *)&s4; - // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code - mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", p[3], p[2], p[1], p[0]); - printf("%s\n", buffer); - target.type = mDNSAddrType_IPv4; - target.ip.v4.NotAnInteger = s4.s_addr; - DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback); - if (StopNow == 2) break; - } + status = mDNS_Init(&mDNSStorage, &PlatformStorage, + gRRCache, RR_CACHE_SIZE, + mDNS_Init_DontAdvertiseLocalAddresses, + mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); + if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %d\n", (int)status); return(status); } + + signal(SIGINT, HandleSIG); // SIGINT is what you get for a Ctrl-C + signal(SIGTERM, HandleSIG); + + while (this_arg < argc) + { + char *arg = argv[this_arg++]; + if (this_arg > 2) printf("\n"); + + lastid = id = zeroID; + hostaddr = target = zeroAddr; + hostname[0] = hardware[0] = software[0] = 0; + NumAddr = NumAAAA = NumHINFO = 0; + + if (inet_pton(AF_INET, arg, &s4) == 1) + { + mDNSu8 *p = (mDNSu8 *)&s4; + // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code + mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", p[3], p[2], p[1], p[0]); + printf("%s\n", buffer); + target.type = mDNSAddrType_IPv4; + target.ip.v4.NotAnInteger = s4.s_addr; + DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback); + if (StopNow == 2) break; + } #if HAVE_IPV6 - else if (inet_pton(AF_INET6, arg, &s6) == 1) - { - int i; - mDNSu8 *p = (mDNSu8 *)&s6; - for (i = 0; i < 16; i++) - { - static const char hexValues[] = "0123456789ABCDEF"; - buffer[i * 4 ] = hexValues[p[15-i] & 0x0F]; - buffer[i * 4 + 1] = '.'; - buffer[i * 4 + 2] = hexValues[p[15-i] >> 4]; - buffer[i * 4 + 3] = '.'; - } - mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa."); - target.type = mDNSAddrType_IPv6; - mDNSPlatformMemCopy(&target.ip.v6, &s6, sizeof(target.ip.v6)); - DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback); - if (StopNow == 2) break; - } + else if (inet_pton(AF_INET6, arg, &s6) == 1) + { + int i; + mDNSu8 *p = (mDNSu8 *)&s6; + for (i = 0; i < 16; i++) + { + static const char hexValues[] = "0123456789ABCDEF"; + buffer[i * 4 ] = hexValues[p[15-i] & 0x0F]; + buffer[i * 4 + 1] = '.'; + buffer[i * 4 + 2] = hexValues[p[15-i] >> 4]; + buffer[i * 4 + 3] = '.'; + } + mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa."); + target.type = mDNSAddrType_IPv6; + mDNSPlatformMemCopy(&target.ip.v6, &s6, sizeof(target.ip.v6)); + DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback); + if (StopNow == 2) break; + } #endif - else - strcpy(hostname, arg); - - // Now we have the host name; get its A, AAAA, and HINFO - if (hostname[0]) DoQuery(&q, hostname, kDNSQType_ANY, &target, InfoCallback); - if (StopNow == 2) break; - - if (hardware[0] || software[0]) - { - printf("HINFO Hardware: %s\n", hardware); - printf("HINFO Software: %s\n", software); - } - else if (NumAnswers) printf("%s has no HINFO record\n", hostname); - else printf("Incorrect dot-local hostname, address, or no mDNSResponder running on that machine\n"); - - if (NumAnswers) - { - // Because of the way we use lastsrc in ServicesCallback, we need to clear the cache to make sure we're getting fresh answers - mDNS *const m = &mDNSStorage; - mDNSu32 slot; - CacheGroup *cg; - CacheRecord *rr; - FORALL_CACHERECORDS(slot, cg, rr) mDNS_PurgeCacheResourceRecord(m, rr); - if (target.type == 0) target = hostaddr; // Make sure the services query is targeted - DoQuery(&q, "_services._dns-sd._udp.local.", kDNSType_PTR, &target, ServicesCallback); - if (StopNow == 2) break; - } - } - - mDNS_Close(&mDNSStorage); - return(0); + else { + if (strlen(arg) >= sizeof(hostname)) { + fprintf(stderr, "hostname must be < %d characters\n", (int)sizeof(hostname)); + goto usage; + } + strcpy(hostname, arg); + } + + // Now we have the host name; get its A, AAAA, and HINFO + if (hostname[0]) DoQuery(&q, hostname, kDNSQType_ANY, &target, InfoCallback); + if (StopNow == 2) break; + + if (hardware[0] || software[0]) + { + printf("HINFO Hardware: %s\n", hardware); + printf("HINFO Software: %s\n", software); + } + else if (NumAnswers) printf("%s has no HINFO record\n", hostname); + else printf("Incorrect dot-local hostname, address, or no mDNSResponder running on that machine\n"); + + if (NumAnswers) + { + // Because of the way we use lastsrc in ServicesCallback, we need to clear the cache to make sure we're getting fresh answers + mDNS *const m = &mDNSStorage; + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *rr; + FORALL_CACHERECORDS(slot, cg, rr) + { + mDNS_PurgeCacheResourceRecord(m, rr); + } + if (target.type == 0) target = hostaddr; // Make sure the services query is targeted + DoQuery(&q, "_services._dns-sd._udp.local.", kDNSType_PTR, &target, ServicesCallback); + if (StopNow == 2) break; + } + } + + mDNS_Close(&mDNSStorage); + return(0); usage: - fprintf(stderr, "%s or or ...\n", progname); - return(-1); - } + fprintf(stderr, "Usage: %s or or ...\n", progname); + return(-1); +} diff --git a/mDNSPosix/Makefile b/mDNSPosix/Makefile index 2e8ff68..b9ec10b 100755 --- a/mDNSPosix/Makefile +++ b/mDNSPosix/Makefile @@ -99,13 +99,18 @@ STRIP = strip endif else -ifeq ($(os),linux) -CFLAGS_OS = -DNOT_HAVE_SA_LEN -DUSES_NETLINK -DHAVE_LINUX -DTARGET_OS_LINUX -fno-strict-aliasing +# any target that contains the string "linux" +ifeq ($(findstring linux,$(os)),linux) +CFLAGS_OS = -D_GNU_SOURCE -DHAVE_IPV6 -DNOT_HAVE_SA_LEN -DUSES_NETLINK -DHAVE_LINUX -DTARGET_OS_LINUX -fno-strict-aliasing LD = gcc -shared FLEXFLAGS_OS = -l JAVACFLAGS_OS += -I$(JDK)/include/linux + +# uClibc does not support Name Service Switch +ifneq ($(os),linux-uclibc) OPTIONALTARG = nss_mdns OPTINSTALL = InstalledNSS +endif else ifeq ($(os),netbsd) @@ -115,11 +120,9 @@ else ifeq ($(os),freebsd) # If not already defined, set LOCALBASE to /usr/local -# FreeBSD requires the startup script to end in ".sh" LOCALBASE?=/usr/local INSTBASE=$(LOCALBASE) -STARTUPSCRIPTNAME=mdns.sh -CFLAGS_OS = +CFLAGS_OS = -DHAVE_IPV6 # FreeBSD 4 requires threaded code to be compiled and linked using the "-pthread" option, # and requires that the "-lpthread" link option NOT be used # This appies only to FreeBSD -- "man cc" on FreeBSD says: @@ -152,7 +155,7 @@ JAVACFLAGS_OS = -dynamiclib -I/System/Library/Frameworks/JavaVM.framework/Header else $(error ERROR: Must specify target OS on command-line, e.g. "make os=x [target]".\ -Supported operating systems include: x, linux, netbsd, freebsd, openbsd, solaris) +Supported operating systems include: x, linux, linux-uclibc, netbsd, freebsd, openbsd, solaris) endif endif endif @@ -231,11 +234,13 @@ 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)/PlatformCommon.c.o + $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/dnssd_ipc.c.o $(OBJDIR)/GenLinkedList.c.o $(OBJDIR)/PlatformCommon.c.o \ + $(OBJDIR)/CryptoAlg.c.o # dnsextd target build dnsextd DNSEXTDOBJ = $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/GenLinkedList.c.o $(OBJDIR)/DNSDigest.c.o \ - $(OBJDIR)/DNSCommon.c.o $(OBJDIR)/PlatformCommon.c.o $(OBJDIR)/dnsextd_parser.y.o $(OBJDIR)/dnsextd_lexer.l.o + $(OBJDIR)/DNSCommon.c.o $(OBJDIR)/PlatformCommon.c.o $(OBJDIR)/dnsextd_parser.y.o $(OBJDIR)/dnsextd_lexer.l.o \ + $(OBJDIR)/CryptoAlg.c.o Daemon: setup $(BUILDDIR)/mdnsd @echo "Responder daemon done" @@ -440,7 +445,8 @@ JavaDoc: Java setup # The following targets build embedded example programs SPECIALOBJ = $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/GenLinkedList.c.o \ - $(OBJDIR)/DNSDigest.c.o $(OBJDIR)/uDNS.c.o $(OBJDIR)/DNSCommon.c.o $(OBJDIR)/PlatformCommon.c.o + $(OBJDIR)/DNSDigest.c.o $(OBJDIR)/uDNS.c.o $(OBJDIR)/DNSCommon.c.o $(OBJDIR)/PlatformCommon.c.o \ + $(OBJDIR)/CryptoAlg.c.o COMMONOBJ = $(SPECIALOBJ) $(OBJDIR)/mDNS.c.o APPOBJ = $(COMMONOBJ) $(OBJDIR)/ExampleClientApp.c.o diff --git a/mDNSPosix/NetMonitor.c b/mDNSPosix/NetMonitor.c index de7c988..7b23fc3 100644 --- a/mDNSPosix/NetMonitor.c +++ b/mDNSPosix/NetMonitor.c @@ -5,27 +5,15 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * - * Formatting notes: - * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion - * on C indentation can be found on the web, such as , - * but for the sake of brevity here I will say just this: Curly braces are not syntactially - * part of an "if" statement; they are the beginning and ending markers of a compound statement; - * therefore common sense dictates that if they are part of a compound statement then they - * should be indented to the same level as everything else in that compound statement. - * Indenting curly braces at the same level as the "if" implies that curly braces are - * part of the "if", which is false. (This is as misleading as people who write "char* x,y;" - * thinking that variables x and y are both of type "char*" -- and anyone who doesn't - * understand why variable y is not of type "char*" just proves the point that poor code - * layout leads people to unfortunate misunderstandings about how the C language really works.) */ //************************************************************************************************************* @@ -40,32 +28,32 @@ //************************************************************************************************************* // Headers -#include // For printf() -#include // For malloc() -#include // For strrchr(), strcmp() -#include // For "struct tm" etc. -#include // For SIGINT, SIGTERM +#include // For printf() +#include // For malloc() +#include // For strrchr(), strcmp() +#include // For "struct tm" etc. +#include // For SIGINT, SIGTERM #if defined(WIN32) -// Both mDNS.c and mDNSWin32.h declare UDPSocket_struct type resulting in a compile-time error, so +// Both mDNS.c and mDNSWin32.h declare UDPSocket_struct type resulting in a compile-time error, so // trick the compiler when including mDNSWin32.h -# define UDPSocket_struct _UDPSocket_struct -# include -# include -# include -# include -# define IFNAMSIZ 256 +# define UDPSocket_struct _UDPSocket_struct +# include +# include +# include +# include +# define IFNAMSIZ 256 static HANDLE gStopEvent = INVALID_HANDLE_VALUE; static mDNSBool gRunning; static void CALLBACK StopNotification( HANDLE event, void *context ) { gRunning = mDNSfalse; } static BOOL WINAPI ConsoleControlHandler( DWORD inControlEvent ) { SetEvent( gStopEvent ); return TRUE; } void setlinebuf( FILE * fp ) {} #else -# include // For gethostbyname() -# include // For AF_INET, AF_INET6, etc. -# include // For IF_NAMESIZE -# include // For INADDR_NONE -# include // For inet_addr() -# include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform +# include // For gethostbyname() +# include // For AF_INET, AF_INET6, etc. +# include // For IF_NAMESIZE +# include // For INADDR_NONE +# include // For inet_addr() +# include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform #endif #include "ExampleClientApp.h" @@ -73,47 +61,47 @@ void setlinebuf( FILE * fp ) {} // Types and structures enum - { - // Primitive operations - OP_probe = 0, - OP_goodbye = 1, - - // These are meta-categories; - // Query and Answer operations are actually subdivided into two classes: - // Browse query/answer and - // Resolve query/answer - OP_query = 2, - OP_answer = 3, - - // The "Browse" variants of query/answer - OP_browsegroup = 2, - OP_browseq = 2, - OP_browsea = 3, - - // The "Resolve" variants of query/answer - OP_resolvegroup = 4, - OP_resolveq = 4, - OP_resolvea = 5, - - OP_NumTypes = 6 - }; +{ + // Primitive operations + OP_probe = 0, + OP_goodbye = 1, + + // These are meta-categories; + // Query and Answer operations are actually subdivided into two classes: + // Browse query/answer and + // Resolve query/answer + OP_query = 2, + OP_answer = 3, + + // The "Browse" variants of query/answer + OP_browsegroup = 2, + OP_browseq = 2, + OP_browsea = 3, + + // The "Resolve" variants of query/answer + OP_resolvegroup = 4, + OP_resolveq = 4, + OP_resolvea = 5, + + OP_NumTypes = 6 +}; typedef struct ActivityStat_struct ActivityStat; struct ActivityStat_struct - { - ActivityStat *next; - domainname srvtype; - int printed; - int totalops; - int stat[OP_NumTypes]; - }; +{ + ActivityStat *next; + domainname srvtype; + int printed; + int totalops; + int stat[OP_NumTypes]; +}; typedef struct FilterList_struct FilterList; struct FilterList_struct - { - FilterList *next; - mDNSAddr FilterAddr; - }; +{ + FilterList *next; + mDNSAddr FilterAddr; +}; //************************************************************************************************************* // Constants @@ -124,8 +112,8 @@ struct FilterList_struct //************************************************************************************************************* // Globals -mDNS mDNSStorage; // mDNS core uses this to store its globals -static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals +mDNS mDNSStorage; // mDNS core uses this to store its globals +static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals mDNSexport const char ProgramName[] = "mDNSNetMonitor"; struct timeval tv_start, tv_end, tv_interval; @@ -133,7 +121,7 @@ static int FilterInterface = 0; static FilterList *Filters; #define ExactlyOneFilter (Filters && !Filters->next) -static int NumPktQ, NumPktL, NumPktR, NumPktB; // Query/Legacy/Response/Bad +static int NumPktQ, NumPktL, NumPktR, NumPktB; // Query/Legacy/Response/Bad static int NumProbes, NumGoodbyes, NumQuestions, NumLegacy, NumAnswers, NumAdditionals; static ActivityStat *stats; @@ -146,16 +134,16 @@ static ActivityStat *stats; // Special version of printf that knows how to print IP addresses, DNS-format name strings, etc. mDNSlocal mDNSu32 mprintf(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); mDNSlocal mDNSu32 mprintf(const char *format, ...) - { - mDNSu32 length; - unsigned char buffer[512]; - va_list ptr; - va_start(ptr,format); - length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr); - va_end(ptr); - printf("%s", buffer); - return(length); - } +{ + mDNSu32 length; + unsigned char buffer[512]; + va_list ptr; + va_start(ptr,format); + length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr); + va_end(ptr); + printf("%s", buffer); + return(length); +} //************************************************************************************************************* // Host Address List @@ -163,851 +151,852 @@ mDNSlocal mDNSu32 mprintf(const char *format, ...) // Would benefit from a hash typedef enum - { - HostPkt_Q = 0, // Query - HostPkt_L = 1, // Legacy Query - HostPkt_R = 2, // Response - HostPkt_B = 3, // Bad - HostPkt_NumTypes = 4 - } HostPkt_Type; +{ + HostPkt_Q = 0, // Query + HostPkt_L = 1, // Legacy Query + HostPkt_R = 2, // Response + HostPkt_B = 3, // Bad + HostPkt_NumTypes = 4 +} HostPkt_Type; typedef struct - { - mDNSAddr addr; - unsigned long pkts[HostPkt_NumTypes]; - unsigned long totalops; - unsigned long stat[OP_NumTypes]; - domainname hostname; - domainname revname; - UTF8str255 HIHardware; - UTF8str255 HISoftware; - mDNSu32 NumQueries; - mDNSs32 LastQuery; - } HostEntry; +{ + mDNSAddr addr; + unsigned long pkts[HostPkt_NumTypes]; + unsigned long totalops; + unsigned long stat[OP_NumTypes]; + domainname hostname; + domainname revname; + UTF8str255 HIHardware; + UTF8str255 HISoftware; + mDNSu32 NumQueries; + mDNSs32 LastQuery; +} HostEntry; #define HostEntryTotalPackets(H) ((H)->pkts[HostPkt_Q] + (H)->pkts[HostPkt_L] + (H)->pkts[HostPkt_R] + (H)->pkts[HostPkt_B]) typedef struct - { - long num; - long max; - HostEntry *hosts; - } HostList; +{ + long num; + long max; + HostEntry *hosts; +} HostList; static HostList IPv4HostList = { 0, 0, 0 }; static HostList IPv6HostList = { 0, 0, 0 }; mDNSlocal HostEntry *FindHost(const mDNSAddr *addr, HostList *list) - { - long i; - - for (i = 0; i < list->num; i++) - { - HostEntry *entry = list->hosts + i; - if (mDNSSameAddress(addr, &entry->addr)) - return entry; - } - - return NULL; - } - +{ + long i; + + for (i = 0; i < list->num; i++) + { + HostEntry *entry = list->hosts + i; + if (mDNSSameAddress(addr, &entry->addr)) + return entry; + } + + return NULL; +} + mDNSlocal HostEntry *AddHost(const mDNSAddr *addr, HostList *list) - { - int i; - HostEntry *entry; - if (list->num >= list->max) - { - long newMax = list->max + 64; - HostEntry *newHosts = realloc(list->hosts, newMax * sizeof(HostEntry)); - if (newHosts == NULL) - return NULL; - list->max = newMax; - list->hosts = newHosts; - } - - entry = list->hosts + list->num++; - - entry->addr = *addr; - for (i=0; ipkts[i] = 0; - entry->totalops = 0; - for (i=0; istat[i] = 0; - entry->hostname.c[0] = 0; - entry->revname.c[0] = 0; - entry->HIHardware.c[0] = 0; - entry->HISoftware.c[0] = 0; - entry->NumQueries = 0; - - if (entry->addr.type == mDNSAddrType_IPv4) - { - mDNSv4Addr ip = entry->addr.ip.v4; - char buffer[32]; - // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code - mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", ip.b[3], ip.b[2], ip.b[1], ip.b[0]); - MakeDomainNameFromDNSNameString(&entry->revname, buffer); - } - - return(entry); - } +{ + int i; + HostEntry *entry; + if (list->num >= list->max) + { + long newMax = list->max + 64; + HostEntry *newHosts = realloc(list->hosts, newMax * sizeof(HostEntry)); + if (newHosts == NULL) + return NULL; + list->max = newMax; + list->hosts = newHosts; + } + + entry = list->hosts + list->num++; + + entry->addr = *addr; + for (i=0; ipkts[i] = 0; + entry->totalops = 0; + for (i=0; istat[i] = 0; + entry->hostname.c[0] = 0; + entry->revname.c[0] = 0; + entry->HIHardware.c[0] = 0; + entry->HISoftware.c[0] = 0; + entry->NumQueries = 0; + + if (entry->addr.type == mDNSAddrType_IPv4) + { + mDNSv4Addr ip = entry->addr.ip.v4; + char buffer[32]; + // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code + mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", ip.b[3], ip.b[2], ip.b[1], ip.b[0]); + MakeDomainNameFromDNSNameString(&entry->revname, buffer); + } + + return(entry); +} mDNSlocal HostEntry *GotPacketFromHost(const mDNSAddr *addr, HostPkt_Type t, mDNSOpaque16 id) - { - if (ExactlyOneFilter) return(NULL); - else - { - HostList *list = (addr->type == mDNSAddrType_IPv4) ? &IPv4HostList : &IPv6HostList; - HostEntry *entry = FindHost(addr, list); - if (!entry) entry = AddHost(addr, list); - if (!entry) return(NULL); - // Don't count our own interrogation packets - if (id.NotAnInteger != 0xFFFF) entry->pkts[t]++; - return(entry); - } - } +{ + if (ExactlyOneFilter) return(NULL); + else + { + HostList *list = (addr->type == mDNSAddrType_IPv4) ? &IPv4HostList : &IPv6HostList; + HostEntry *entry = FindHost(addr, list); + if (!entry) entry = AddHost(addr, list); + if (!entry) return(NULL); + // Don't count our own interrogation packets + if (id.NotAnInteger != 0xFFFF) entry->pkts[t]++; + return(entry); + } +} mDNSlocal void RecordHostInfo(HostEntry *entry, const ResourceRecord *const pktrr) - { - if (!entry->hostname.c[0]) - { - if (pktrr->rrtype == kDNSType_A || pktrr->rrtype == kDNSType_AAAA) - { - // Should really check that the rdata in the address record matches the source address of this packet - entry->NumQueries = 0; - AssignDomainName(&entry->hostname, pktrr->name); - } - - if (pktrr->rrtype == kDNSType_PTR) - if (SameDomainName(&entry->revname, pktrr->name)) - { - entry->NumQueries = 0; - AssignDomainName(&entry->hostname, &pktrr->rdata->u.name); - } - } - else if (pktrr->rrtype == kDNSType_HINFO) - { - RDataBody *rd = &pktrr->rdata->u; - mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength; - mDNSu8 *hw = rd->txt.c; - mDNSu8 *sw = hw + 1 + (mDNSu32)hw[0]; - if (sw + 1 + sw[0] <= rdend) - { - AssignDomainName(&entry->hostname, pktrr->name); - mDNSPlatformMemCopy(entry->HIHardware.c, hw, 1 + (mDNSu32)hw[0]); - mDNSPlatformMemCopy(entry->HISoftware.c, sw, 1 + (mDNSu32)sw[0]); - } - } - } +{ + if (!entry->hostname.c[0]) + { + if (pktrr->rrtype == kDNSType_A || pktrr->rrtype == kDNSType_AAAA) + { + // Should really check that the rdata in the address record matches the source address of this packet + entry->NumQueries = 0; + AssignDomainName(&entry->hostname, pktrr->name); + } + + if (pktrr->rrtype == kDNSType_PTR) + if (SameDomainName(&entry->revname, pktrr->name)) + { + entry->NumQueries = 0; + AssignDomainName(&entry->hostname, &pktrr->rdata->u.name); + } + } + else if (pktrr->rrtype == kDNSType_HINFO) + { + RDataBody *rd = &pktrr->rdata->u; + mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength; + mDNSu8 *hw = rd->txt.c; + mDNSu8 *sw = hw + 1 + (mDNSu32)hw[0]; + if (sw + 1 + sw[0] <= rdend) + { + AssignDomainName(&entry->hostname, pktrr->name); + mDNSPlatformMemCopy(entry->HIHardware.c, hw, 1 + (mDNSu32)hw[0]); + mDNSPlatformMemCopy(entry->HISoftware.c, sw, 1 + (mDNSu32)sw[0]); + } + } +} mDNSlocal void SendUnicastQuery(mDNS *const m, HostEntry *entry, domainname *name, mDNSu16 rrtype, mDNSInterfaceID InterfaceID) - { - const mDNSOpaque16 id = { { 0xFF, 0xFF } }; - DNSMessage query; - mDNSu8 *qptr = query.data; - const mDNSu8 *const limit = query.data + sizeof(query.data); - const mDNSAddr *target = &entry->addr; - InitializeDNSMessage(&query.h, id, QueryFlags); - qptr = putQuestion(&query, qptr, limit, name, rrtype, kDNSClass_IN); - entry->LastQuery = m->timenow; - entry->NumQueries++; - - // Note: When there are multiple mDNSResponder agents running on a single machine - // (e.g. Apple mDNSResponder plus a SliMP3 server with embedded mDNSResponder) - // it is possible that unicast queries may not go to the primary system responder. - // We try the first query using unicast, but if that doesn't work we try again via multicast. - if (entry->NumQueries > 2) - { - target = &AllDNSLinkGroup_v4; - } - else - { - //mprintf("%#a Q\n", target); - InterfaceID = mDNSInterface_Any; // Send query from our unicast reply socket - } - - mDNSSendDNSMessage(&mDNSStorage, &query, qptr, InterfaceID, mDNSNULL, target, MulticastDNSPort, mDNSNULL, mDNSNULL); - } +{ + const mDNSOpaque16 id = { { 0xFF, 0xFF } }; + DNSMessage query; + mDNSu8 *qptr = query.data; + const mDNSu8 *const limit = query.data + sizeof(query.data); + const mDNSAddr *target = &entry->addr; + InitializeDNSMessage(&query.h, id, QueryFlags); + qptr = putQuestion(&query, qptr, limit, name, rrtype, kDNSClass_IN); + entry->LastQuery = m->timenow; + entry->NumQueries++; + + // Note: When there are multiple mDNSResponder agents running on a single machine + // (e.g. Apple mDNSResponder plus a SliMP3 server with embedded mDNSResponder) + // it is possible that unicast queries may not go to the primary system responder. + // We try the first query using unicast, but if that doesn't work we try again via multicast. + if (entry->NumQueries > 2) + { + target = &AllDNSLinkGroup_v4; + } + else + { + //mprintf("%#a Q\n", target); + InterfaceID = mDNSInterface_Any; // Send query from our unicast reply socket + } + + mDNSSendDNSMessage(&mDNSStorage, &query, qptr, InterfaceID, mDNSNULL, target, MulticastDNSPort, mDNSNULL, mDNSNULL, mDNSfalse); +} mDNSlocal void AnalyseHost(mDNS *const m, HostEntry *entry, const mDNSInterfaceID InterfaceID) - { - // If we've done four queries without answer, give up - if (entry->NumQueries >= 4) return; - - // If we've done a query in the last second, give the host a chance to reply before trying again - if (entry->NumQueries && m->timenow - entry->LastQuery < mDNSPlatformOneSecond) return; - - // If we don't know the host name, try to find that first - if (!entry->hostname.c[0]) - { - if (entry->revname.c[0]) - { - SendUnicastQuery(m, entry, &entry->revname, kDNSType_PTR, InterfaceID); - //mprintf("%##s PTR %d\n", entry->revname.c, entry->NumQueries); - } - } - // If we have the host name but no HINFO, now ask for that - else if (!entry->HIHardware.c[0]) - { - SendUnicastQuery(m, entry, &entry->hostname, kDNSType_HINFO, InterfaceID); - //mprintf("%##s HINFO %d\n", entry->hostname.c, entry->NumQueries); - } - } +{ + // If we've done four queries without answer, give up + if (entry->NumQueries >= 4) return; + + // If we've done a query in the last second, give the host a chance to reply before trying again + if (entry->NumQueries && m->timenow - entry->LastQuery < mDNSPlatformOneSecond) return; + + // If we don't know the host name, try to find that first + if (!entry->hostname.c[0]) + { + if (entry->revname.c[0]) + { + SendUnicastQuery(m, entry, &entry->revname, kDNSType_PTR, InterfaceID); + //mprintf("%##s PTR %d\n", entry->revname.c, entry->NumQueries); + } + } + // If we have the host name but no HINFO, now ask for that + else if (!entry->HIHardware.c[0]) + { + SendUnicastQuery(m, entry, &entry->hostname, kDNSType_HINFO, InterfaceID); + //mprintf("%##s HINFO %d\n", entry->hostname.c, entry->NumQueries); + } +} mDNSlocal int CompareHosts(const void *p1, const void *p2) - { - return (int)(HostEntryTotalPackets((HostEntry *)p2) - HostEntryTotalPackets((HostEntry *)p1)); - } +{ + return (int)(HostEntryTotalPackets((HostEntry *)p2) - HostEntryTotalPackets((HostEntry *)p1)); +} mDNSlocal void ShowSortedHostList(HostList *list, int max) - { - HostEntry *e, *end = &list->hosts[(max < list->num) ? max : list->num]; - qsort(list->hosts, list->num, sizeof(HostEntry), CompareHosts); - if (list->num) mprintf("\n%-25s%s%s\n", "Source Address", OPBanner, " Pkts Query LegacyQ Response"); - for (e = &list->hosts[0]; e < end; e++) - { - int len = mprintf("%#-25a", &e->addr); - if (len > 25) mprintf("\n%25s", ""); - mprintf("%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]); - mprintf(" %8lu %8lu %8lu %8lu", - HostEntryTotalPackets(e), e->pkts[HostPkt_Q], e->pkts[HostPkt_L], e->pkts[HostPkt_R]); - if (e->pkts[HostPkt_B]) mprintf("Bad: %8lu", e->pkts[HostPkt_B]); - mprintf("\n"); - if (!e->HISoftware.c[0] && e->NumQueries > 2) - mDNSPlatformMemCopy(&e->HISoftware, "\x27*** Unknown (Jaguar, Windows, etc.) ***", 0x28); - if (e->hostname.c[0] || e->HIHardware.c[0] || e->HISoftware.c[0]) - mprintf("%##-45s %#-14s %#s\n", e->hostname.c, e->HIHardware.c, e->HISoftware.c); - } - } +{ + HostEntry *e, *end = &list->hosts[(max < list->num) ? max : list->num]; + qsort(list->hosts, list->num, sizeof(HostEntry), CompareHosts); + if (list->num) mprintf("\n%-25s%s%s\n", "Source Address", OPBanner, " Pkts Query LegacyQ Response"); + for (e = &list->hosts[0]; e < end; e++) + { + int len = mprintf("%#-25a", &e->addr); + if (len > 25) mprintf("\n%25s", ""); + mprintf("%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]); + mprintf(" %8lu %8lu %8lu %8lu", + HostEntryTotalPackets(e), e->pkts[HostPkt_Q], e->pkts[HostPkt_L], e->pkts[HostPkt_R]); + if (e->pkts[HostPkt_B]) mprintf("Bad: %8lu", e->pkts[HostPkt_B]); + mprintf("\n"); + if (!e->HISoftware.c[0] && e->NumQueries > 2) + mDNSPlatformMemCopy(&e->HISoftware, "\x27*** Unknown (Jaguar, Windows, etc.) ***", 0x28); + if (e->hostname.c[0] || e->HIHardware.c[0] || e->HISoftware.c[0]) + mprintf("%##-45s %#-14s %#s\n", e->hostname.c, e->HIHardware.c, e->HISoftware.c); + } +} //************************************************************************************************************* // Receive and process packets mDNSexport mDNSBool ExtractServiceType(const domainname *const fqdn, domainname *const srvtype) - { - int i, len; - const mDNSu8 *src = fqdn->c; - mDNSu8 *dst = srvtype->c; - - len = *src; - if (len == 0 || len >= 0x40) return(mDNSfalse); - if (src[1] != '_') src += 1 + len; - - len = *src; - if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse); - for (i=0; i<=len; i++) *dst++ = *src++; - - len = *src; - if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse); - for (i=0; i<=len; i++) *dst++ = *src++; - - *dst++ = 0; // Put the null root label on the end of the service type - - return(mDNStrue); - } +{ + int i, len; + const mDNSu8 *src = fqdn->c; + mDNSu8 *dst = srvtype->c; + + len = *src; + if (len == 0 || len >= 0x40) return(mDNSfalse); + if (src[1] != '_') src += 1 + len; + + len = *src; + if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse); + for (i=0; i<=len; i++) *dst++ = *src++; + + len = *src; + if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse); + for (i=0; i<=len; i++) *dst++ = *src++; + + *dst++ = 0; // Put the null root label on the end of the service type + + return(mDNStrue); +} mDNSlocal void recordstat(HostEntry *entry, const domainname *fqdn, int op, mDNSu16 rrtype) - { - ActivityStat **s = &stats; - domainname srvtype; - - if (op != OP_probe) - { - if (rrtype == kDNSType_SRV || rrtype == kDNSType_TXT) op = op - OP_browsegroup + OP_resolvegroup; - else if (rrtype != kDNSType_PTR) return; - } - - if (!ExtractServiceType(fqdn, &srvtype)) return; - - while (*s && !SameDomainName(&(*s)->srvtype, &srvtype)) s = &(*s)->next; - if (!*s) - { - int i; - *s = malloc(sizeof(ActivityStat)); - if (!*s) exit(-1); - (*s)->next = NULL; - (*s)->srvtype = srvtype; - (*s)->printed = 0; - (*s)->totalops = 0; - for (i=0; istat[i] = 0; - } - - (*s)->totalops++; - (*s)->stat[op]++; - if (entry) - { - entry->totalops++; - entry->stat[op]++; - } - } +{ + ActivityStat **s = &stats; + domainname srvtype; + + if (op != OP_probe) + { + if (rrtype == kDNSType_SRV || rrtype == kDNSType_TXT) op = op - OP_browsegroup + OP_resolvegroup; + else if (rrtype != kDNSType_PTR) return; + } + + if (!ExtractServiceType(fqdn, &srvtype)) return; + + while (*s && !SameDomainName(&(*s)->srvtype, &srvtype)) s = &(*s)->next; + if (!*s) + { + int i; + *s = malloc(sizeof(ActivityStat)); + if (!*s) exit(-1); + (*s)->next = NULL; + (*s)->srvtype = srvtype; + (*s)->printed = 0; + (*s)->totalops = 0; + for (i=0; istat[i] = 0; + } + + (*s)->totalops++; + (*s)->stat[op]++; + if (entry) + { + entry->totalops++; + entry->stat[op]++; + } +} mDNSlocal void printstats(int max) - { - int i; - if (!stats) return; - for (i=0; inext) - if (!s->printed && max < s->totalops) - { m = s; max = s->totalops; } - if (!m) return; - m->printed = mDNStrue; - if (i==0) mprintf("%-25s%s\n", "Service Type", OPBanner); - mprintf("%##-25s%8d %8d %8d %8d %8d %8d %8d\n", m->srvtype.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]); - } - } +{ + int i; + if (!stats) return; + for (i=0; inext) + if (!s->printed && max < s->totalops) + { m = s; max = s->totalops; } + if (!m) return; + m->printed = mDNStrue; + if (i==0) mprintf("%-25s%s\n", "Service Type", OPBanner); + mprintf("%##-25s%8d %8d %8d %8d %8d %8d %8d\n", m->srvtype.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]); + } +} mDNSlocal const mDNSu8 *FindUpdate(mDNS *const m, const DNSMessage *const query, const mDNSu8 *ptr, const mDNSu8 *const end, - DNSQuestion *q, LargeCacheRecord *pkt) - { - int i; - for (i = 0; i < query->h.numAuthorities; i++) - { - const mDNSu8 *p2 = ptr; - ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, pkt); - if (!ptr) break; - if (m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && ResourceRecordAnswersQuestion(&pkt->r.resrec, q)) return(p2); - } - return(mDNSNULL); - } + DNSQuestion *q, LargeCacheRecord *pkt) +{ + int i; + for (i = 0; i < query->h.numAuthorities; i++) + { + const mDNSu8 *p2 = ptr; + ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, pkt); + if (!ptr) break; + if (m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && ResourceRecordAnswersQuestion(&pkt->r.resrec, q)) return(p2); + } + return(mDNSNULL); +} mDNSlocal void DisplayPacketHeader(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID) - { - const char *const ptype = (msg->h.flags.b[0] & kDNSFlag0_QR_Response) ? "-R- " : - (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) ? "-Q- " : "-LQ-"; - - struct timeval tv; - struct tm tm; - const mDNSu32 index = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNSfalse); - char if_name[IFNAMSIZ]; // Older Linux distributions don't define IF_NAMESIZE - if_indextoname(index, if_name); - gettimeofday(&tv, NULL); - localtime_r((time_t*)&tv.tv_sec, &tm); - mprintf("\n%d:%02d:%02d.%06d Interface %d/%s\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec, index, if_name); - - mprintf("%#-16a %s Q:%3d Ans:%3d Auth:%3d Add:%3d Size:%5d bytes", - srcaddr, ptype, msg->h.numQuestions, msg->h.numAnswers, msg->h.numAuthorities, msg->h.numAdditionals, end - (mDNSu8 *)msg); - - if (msg->h.id.NotAnInteger) mprintf(" ID:%u", mDNSVal16(msg->h.id)); - - if (!mDNSAddrIsDNSMulticast(dstaddr)) mprintf(" To: %#a", dstaddr); - - if (msg->h.flags.b[0] & kDNSFlag0_TC) - { - if (msg->h.flags.b[0] & kDNSFlag0_QR_Response) mprintf(" Truncated"); - else mprintf(" Truncated (KA list continues in next packet)"); - } - mprintf("\n"); - } +{ + const char *const ptype = (msg->h.flags.b[0] & kDNSFlag0_QR_Response) ? "-R- " : + (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) ? "-Q- " : "-LQ-"; + + struct timeval tv; + struct tm tm; + const mDNSu32 index = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNSfalse); + char if_name[IFNAMSIZ]; // Older Linux distributions don't define IF_NAMESIZE + if_indextoname(index, if_name); + gettimeofday(&tv, NULL); + localtime_r((time_t*)&tv.tv_sec, &tm); + mprintf("\n%d:%02d:%02d.%06d Interface %d/%s\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec, index, if_name); + + mprintf("%#-16a %s Q:%3d Ans:%3d Auth:%3d Add:%3d Size:%5d bytes", + srcaddr, ptype, msg->h.numQuestions, msg->h.numAnswers, msg->h.numAuthorities, msg->h.numAdditionals, end - (mDNSu8 *)msg); + + if (msg->h.id.NotAnInteger) mprintf(" ID:%u", mDNSVal16(msg->h.id)); + + if (!mDNSAddrIsDNSMulticast(dstaddr)) mprintf(" To: %#a", dstaddr); + + if (msg->h.flags.b[0] & kDNSFlag0_TC) + { + if (msg->h.flags.b[0] & kDNSFlag0_QR_Response) mprintf(" Truncated"); + else mprintf(" Truncated (KA list continues in next packet)"); + } + mprintf("\n"); +} mDNSlocal void DisplayResourceRecord(const mDNSAddr *const srcaddr, const char *const op, const ResourceRecord *const pktrr) - { - static const char hexchars[16] = "0123456789ABCDEF"; - #define MaxWidth 132 - char buffer[MaxWidth+8]; - char *p = buffer; - - RDataBody *rd = &pktrr->rdata->u; - mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength; - int n = mprintf("%#-16a %-5s %-5s%5lu %##s -> ", srcaddr, op, DNSTypeName(pktrr->rrtype), pktrr->rroriginalttl, pktrr->name->c); - - if (pktrr->RecordType == kDNSRecordTypePacketNegative) { mprintf("**** ERROR: FAILED TO READ RDATA ****\n"); return; } - - // The kDNSType_OPT case below just calls GetRRDisplayString_rdb - // Perhaps more (or all?) of the cases should do that? - switch(pktrr->rrtype) - { - 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; - while (t < rdend && t[0] && p < buffer+MaxWidth) - { - int i; - for (i=1; i<=t[0] && p < buffer+MaxWidth; i++) - { - if (t[i] == '\\') *p++ = '\\'; - if (t[i] >= ' ') *p++ = t[i]; - else - { - *p++ = '\\'; - *p++ = '0'; - *p++ = 'x'; - *p++ = hexchars[t[i] >> 4]; - *p++ = hexchars[t[i] & 0xF]; - } - } - t += 1+t[0]; - if (t < rdend && t[0]) { *p++ = '\\'; *p++ = ' '; } - } - *p++ = 0; - n += mprintf("%.*s", MaxWidth - n, buffer); - } break; - case kDNSType_AAAA: n += mprintf("%.16a", &rd->ipv6); break; - case kDNSType_SRV: n += mprintf("%##s:%d", rd->srv.target.c, mDNSVal16(rd->srv.port)); break; - case kDNSType_OPT: { - char b[MaxMsg]; - // Quick hack: we don't want the prefix that GetRRDisplayString_rdb puts at the start of its - // string, because it duplicates the name and rrtype we already display, so we compute the - // length of that prefix and strip that many bytes off the beginning of the string we display. - mDNSu32 striplen = mDNS_snprintf(b, MaxMsg-1, "%4d %##s %s ", pktrr->rdlength, pktrr->name->c, DNSTypeName(pktrr->rrtype)); - GetRRDisplayString_rdb(pktrr, &pktrr->rdata->u, b); - n += mprintf("%.*s", MaxWidth - n, b + striplen); - } break; - case kDNSType_NSEC: { - int i; - for (i=0; i<255; i++) - if (rd->nsec.bitmap[i>>3] & (128 >> (i&7))) - n += mprintf("%s ", DNSTypeName(i)); - } break; - default: { - mDNSu8 *s = rd->data; - while (s < rdend && p < buffer+MaxWidth) - { - if (*s == '\\') *p++ = '\\'; - if (*s >= ' ') *p++ = *s; - else - { - *p++ = '\\'; - *p++ = '0'; - *p++ = 'x'; - *p++ = hexchars[*s >> 4]; - *p++ = hexchars[*s & 0xF]; - } - s++; - } - *p++ = 0; - n += mprintf("%.*s", MaxWidth - n, buffer); - } break; - } - - mprintf("\n"); - } +{ + static const char hexchars[16] = "0123456789ABCDEF"; + #define MaxWidth 132 + char buffer[MaxWidth+8]; + char *p = buffer; + + RDataBody *rd = &pktrr->rdata->u; + mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength; + int n = mprintf("%#-16a %-5s %-5s%5lu %##s -> ", srcaddr, op, DNSTypeName(pktrr->rrtype), pktrr->rroriginalttl, pktrr->name->c); + + if (pktrr->RecordType == kDNSRecordTypePacketNegative) { mprintf("**** ERROR: FAILED TO READ RDATA ****\n"); return; } + + // The kDNSType_OPT case below just calls GetRRDisplayString_rdb + // Perhaps more (or all?) of the cases should do that? + switch(pktrr->rrtype) + { + 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; + while (t < rdend && t[0] && p < buffer+MaxWidth) + { + int i; + for (i=1; i<=t[0] && p < buffer+MaxWidth; i++) + { + if (t[i] == '\\') *p++ = '\\'; + if (t[i] >= ' ') *p++ = t[i]; + else + { + *p++ = '\\'; + *p++ = '0'; + *p++ = 'x'; + *p++ = hexchars[t[i] >> 4]; + *p++ = hexchars[t[i] & 0xF]; + } + } + t += 1+t[0]; + if (t < rdend && t[0]) { *p++ = '\\'; *p++ = ' '; } + } + *p++ = 0; + n += mprintf("%.*s", MaxWidth - n, buffer); + } break; + case kDNSType_AAAA: n += mprintf("%.16a", &rd->ipv6); break; + case kDNSType_SRV: n += mprintf("%##s:%d", rd->srv.target.c, mDNSVal16(rd->srv.port)); break; + case kDNSType_OPT: { + char b[MaxMsg]; + // Quick hack: we don't want the prefix that GetRRDisplayString_rdb puts at the start of its + // string, because it duplicates the name and rrtype we already display, so we compute the + // length of that prefix and strip that many bytes off the beginning of the string we display. + mDNSu32 striplen = mDNS_snprintf(b, MaxMsg-1, "%4d %##s %s ", pktrr->rdlength, pktrr->name->c, DNSTypeName(pktrr->rrtype)); + GetRRDisplayString_rdb(pktrr, &pktrr->rdata->u, b); + n += mprintf("%.*s", MaxWidth - n, b + striplen); + } break; + case kDNSType_NSEC: { + char b[MaxMsg]; + // See the quick hack above + mDNSu32 striplen = mDNS_snprintf(b, MaxMsg-1, "%4d %##s %s ", pktrr->rdlength, pktrr->name->c, DNSTypeName(pktrr->rrtype)); + GetRRDisplayString_rdb(pktrr, &pktrr->rdata->u, b); + n += mprintf("%s", b+striplen); + } break; + default: { + mDNSu8 *s = rd->data; + while (s < rdend && p < buffer+MaxWidth) + { + if (*s == '\\') *p++ = '\\'; + if (*s >= ' ') *p++ = *s; + else + { + *p++ = '\\'; + *p++ = '0'; + *p++ = 'x'; + *p++ = hexchars[*s >> 4]; + *p++ = hexchars[*s & 0xF]; + } + s++; + } + *p++ = 0; + n += mprintf("%.*s", MaxWidth - n, buffer); + } break; + } + + mprintf("\n"); +} mDNSlocal void HexDump(const mDNSu8 *ptr, const mDNSu8 *const end) - { - while (ptr < end) - { - int i; - for (i=0; i<16; i++) - if (&ptr[i] < end) mprintf("%02X ", ptr[i]); - else mprintf(" "); - for (i=0; i<16; i++) - if (&ptr[i] < end) mprintf("%c", ptr[i] <= ' ' || ptr[i] >= 126 ? '.' : ptr[i]); - ptr += 16; - mprintf("\n"); - } - } +{ + while (ptr < end) + { + int i; + for (i=0; i<16; i++) + if (&ptr[i] < end) mprintf("%02X ", ptr[i]); + else mprintf(" "); + for (i=0; i<16; i++) + if (&ptr[i] < end) mprintf("%c", ptr[i] <= ' ' || ptr[i] >= 126 ? '.' : ptr[i]); + ptr += 16; + mprintf("\n"); + } +} mDNSlocal void DisplayError(const mDNSAddr *srcaddr, const mDNSu8 *ptr, const mDNSu8 *const end, char *msg) - { - mprintf("%#-16a **** ERROR: FAILED TO READ %s **** \n", srcaddr, msg); - HexDump(ptr, end); - } +{ + mprintf("%#-16a **** ERROR: FAILED TO READ %s **** \n", srcaddr, msg); + HexDump(ptr, end); +} mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, - const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID) - { - int i; - const mDNSu8 *ptr = msg->data; - const mDNSu8 *auth = LocateAuthorities(msg, end); - mDNSBool MQ = (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger); - HostEntry *entry = GotPacketFromHost(srcaddr, MQ ? HostPkt_Q : HostPkt_L, msg->h.id); - LargeCacheRecord pkt; - - DisplayPacketHeader(m, msg, end, srcaddr, srcport, dstaddr, InterfaceID); - if (msg->h.id.NotAnInteger != 0xFFFF) - { - if (MQ) NumPktQ++; else NumPktL++; - } - - for (i=0; ih.numQuestions; i++) - { - DNSQuestion q; - mDNSu8 *p2 = (mDNSu8 *)getQuestion(msg, ptr, end, InterfaceID, &q); - mDNSu16 ucbit = q.qclass & kDNSQClass_UnicastResponse; - q.qclass &= ~kDNSQClass_UnicastResponse; - if (!p2) { DisplayError(srcaddr, ptr, end, "QUESTION"); return; } - ptr = p2; - p2 = (mDNSu8 *)FindUpdate(m, msg, auth, end, &q, &pkt); - if (p2) - { - NumProbes++; - DisplayResourceRecord(srcaddr, ucbit ? "(PU)" : "(PM)", &pkt.r.resrec); - recordstat(entry, &q.qname, OP_probe, q.qtype); - p2 = (mDNSu8 *)skipDomainName(msg, p2, end); - // Having displayed this update record, clear type and class so we don't display the same one again. - p2[0] = p2[1] = p2[2] = p2[3] = 0; - } - else - { - const char *ptype = ucbit ? "(QU)" : "(QM)"; - if (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) NumQuestions++; - else { NumLegacy++; ptype = "(LQ)"; } - mprintf("%#-16a %-5s %-5s %##s\n", srcaddr, ptype, DNSTypeName(q.qtype), q.qname.c); - if (msg->h.id.NotAnInteger != 0xFFFF) recordstat(entry, &q.qname, OP_query, q.qtype); - } - } - - for (i=0; ih.numAnswers; i++) - { - const mDNSu8 *ep = ptr; - ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt); - if (!ptr) { DisplayError(srcaddr, ep, end, "KNOWN ANSWER"); return; } - DisplayResourceRecord(srcaddr, "(KA)", &pkt.r.resrec); - - // In the case of queries with long multi-packet KA lists, we count each subsequent KA packet - // the same as a single query, to more accurately reflect the burden on the network - // (A query with a six-packet KA list is *at least* six times the burden on the network as a single-packet query.) - if (msg->h.numQuestions == 0 && i == 0) - recordstat(entry, pkt.r.resrec.name, OP_query, pkt.r.resrec.rrtype); - } - - for (i=0; ih.numAuthorities; i++) - { - const mDNSu8 *ep = ptr; - ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &pkt); - if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; } - // After we display an Update record with its matching question (above) we zero out its type and class - // If any remain that haven't been zero'd out, display them here - if (pkt.r.resrec.rrtype || pkt.r.resrec.rrclass) DisplayResourceRecord(srcaddr, "(AU)", &pkt.r.resrec); - } - - for (i=0; ih.numAdditionals; i++) - { - const mDNSu8 *ep = ptr; - ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAdd, &pkt); - if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; } - DisplayResourceRecord(srcaddr, pkt.r.resrec.rrtype == kDNSType_OPT ? "(OP)" : "(AD)", &pkt.r.resrec); - } - - if (entry) AnalyseHost(m, entry, InterfaceID); - } + const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID) +{ + int i; + const mDNSu8 *ptr = msg->data; + const mDNSu8 *auth = LocateAuthorities(msg, end); + mDNSBool MQ = (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger); + HostEntry *entry = GotPacketFromHost(srcaddr, MQ ? HostPkt_Q : HostPkt_L, msg->h.id); + LargeCacheRecord pkt; + + DisplayPacketHeader(m, msg, end, srcaddr, srcport, dstaddr, InterfaceID); + if (msg->h.id.NotAnInteger != 0xFFFF) + { + if (MQ) NumPktQ++;else NumPktL++; + } + + for (i=0; ih.numQuestions; i++) + { + DNSQuestion q; + mDNSu8 *p2 = (mDNSu8 *)getQuestion(msg, ptr, end, InterfaceID, &q); + mDNSu16 ucbit = q.qclass & kDNSQClass_UnicastResponse; + q.qclass &= ~kDNSQClass_UnicastResponse; + if (!p2) { DisplayError(srcaddr, ptr, end, "QUESTION"); return; } + ptr = p2; + p2 = (mDNSu8 *)FindUpdate(m, msg, auth, end, &q, &pkt); + if (p2) + { + NumProbes++; + DisplayResourceRecord(srcaddr, ucbit ? "(PU)" : "(PM)", &pkt.r.resrec); + recordstat(entry, &q.qname, OP_probe, q.qtype); + p2 = (mDNSu8 *)skipDomainName(msg, p2, end); + // Having displayed this update record, clear type and class so we don't display the same one again. + p2[0] = p2[1] = p2[2] = p2[3] = 0; + } + else + { + const char *ptype = ucbit ? "(QU)" : "(QM)"; + if (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) NumQuestions++; + else { NumLegacy++; ptype = "(LQ)"; } + mprintf("%#-16a %-5s %-5s %##s\n", srcaddr, ptype, DNSTypeName(q.qtype), q.qname.c); + if (msg->h.id.NotAnInteger != 0xFFFF) recordstat(entry, &q.qname, OP_query, q.qtype); + } + } + + for (i=0; ih.numAnswers; i++) + { + const mDNSu8 *ep = ptr; + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt); + if (!ptr) { DisplayError(srcaddr, ep, end, "KNOWN ANSWER"); return; } + DisplayResourceRecord(srcaddr, "(KA)", &pkt.r.resrec); + + // In the case of queries with long multi-packet KA lists, we count each subsequent KA packet + // the same as a single query, to more accurately reflect the burden on the network + // (A query with a six-packet KA list is *at least* six times the burden on the network as a single-packet query.) + if (msg->h.numQuestions == 0 && i == 0) + recordstat(entry, pkt.r.resrec.name, OP_query, pkt.r.resrec.rrtype); + } + + for (i=0; ih.numAuthorities; i++) + { + const mDNSu8 *ep = ptr; + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &pkt); + if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; } + // After we display an Update record with its matching question (above) we zero out its type and class + // If any remain that haven't been zero'd out, display them here + if (pkt.r.resrec.rrtype || pkt.r.resrec.rrclass) DisplayResourceRecord(srcaddr, "(AU)", &pkt.r.resrec); + } + + for (i=0; ih.numAdditionals; i++) + { + const mDNSu8 *ep = ptr; + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAdd, &pkt); + if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; } + DisplayResourceRecord(srcaddr, pkt.r.resrec.rrtype == kDNSType_OPT ? "(OP)" : "(AD)", &pkt.r.resrec); + } + + if (entry) AnalyseHost(m, entry, InterfaceID); +} mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, - const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID) - { - int i; - const mDNSu8 *ptr = msg->data; - HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R, msg->h.id); - LargeCacheRecord pkt; - - DisplayPacketHeader(m, msg, end, srcaddr, srcport, dstaddr, InterfaceID); - if (msg->h.id.NotAnInteger != 0xFFFF) NumPktR++; - - for (i=0; ih.numQuestions; i++) - { - DNSQuestion q; - const mDNSu8 *ep = ptr; - ptr = getQuestion(msg, ptr, end, InterfaceID, &q); - if (!ptr) { DisplayError(srcaddr, ep, end, "QUESTION"); return; } - if (mDNSAddrIsDNSMulticast(dstaddr)) - mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE Q IN mDNS RESPONSE **** %-5s %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c); - else - mprintf("%#-16a (Q) %-5s %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c); - } - - for (i=0; ih.numAnswers; i++) - { - const mDNSu8 *ep = ptr; - ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt); - if (!ptr) { DisplayError(srcaddr, ep, end, "ANSWER"); return; } - if (pkt.r.resrec.rroriginalttl) - { - NumAnswers++; - DisplayResourceRecord(srcaddr, (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AN)" : "(AN+)", &pkt.r.resrec); - if (msg->h.id.NotAnInteger != 0xFFFF) recordstat(entry, pkt.r.resrec.name, OP_answer, pkt.r.resrec.rrtype); - if (entry) RecordHostInfo(entry, &pkt.r.resrec); - } - else - { - NumGoodbyes++; - DisplayResourceRecord(srcaddr, "(DE)", &pkt.r.resrec); - recordstat(entry, pkt.r.resrec.name, OP_goodbye, pkt.r.resrec.rrtype); - } - } - - for (i=0; ih.numAuthorities; i++) - { - const mDNSu8 *ep = ptr; - ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 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); - } - - for (i=0; ih.numAdditionals; i++) - { - const mDNSu8 *ep = ptr; - ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAdd, &pkt); - if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; } - NumAdditionals++; - DisplayResourceRecord(srcaddr, - pkt.r.resrec.rrtype == kDNSType_OPT ? "(OP)" : (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AD)" : "(AD+)", - &pkt.r.resrec); - if (entry) RecordHostInfo(entry, &pkt.r.resrec); - } - - if (entry) AnalyseHost(m, entry, InterfaceID); - } + 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(m, msg, end, srcaddr, srcport, dstaddr, InterfaceID); + if (msg->h.id.NotAnInteger != 0xFFFF) NumPktR++; + + for (i=0; ih.numQuestions; i++) + { + DNSQuestion q; + const mDNSu8 *ep = ptr; + ptr = getQuestion(msg, ptr, end, InterfaceID, &q); + if (!ptr) { DisplayError(srcaddr, ep, end, "QUESTION"); return; } + if (mDNSAddrIsDNSMulticast(dstaddr)) + mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE Q IN mDNS RESPONSE **** %-5s %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c); + else + mprintf("%#-16a (Q) %-5s %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c); + } + + for (i=0; ih.numAnswers; i++) + { + const mDNSu8 *ep = ptr; + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt); + if (!ptr) { DisplayError(srcaddr, ep, end, "ANSWER"); return; } + if (pkt.r.resrec.rroriginalttl) + { + NumAnswers++; + DisplayResourceRecord(srcaddr, (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AN)" : "(AN+)", &pkt.r.resrec); + if (msg->h.id.NotAnInteger != 0xFFFF) recordstat(entry, pkt.r.resrec.name, OP_answer, pkt.r.resrec.rrtype); + if (entry) RecordHostInfo(entry, &pkt.r.resrec); + } + else + { + NumGoodbyes++; + DisplayResourceRecord(srcaddr, "(DE)", &pkt.r.resrec); + recordstat(entry, pkt.r.resrec.name, OP_goodbye, pkt.r.resrec.rrtype); + } + } + + for (i=0; ih.numAuthorities; i++) + { + const mDNSu8 *ep = ptr; + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 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); + } + + for (i=0; ih.numAdditionals; i++) + { + const mDNSu8 *ep = ptr; + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAdd, &pkt); + if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; } + NumAdditionals++; + DisplayResourceRecord(srcaddr, + pkt.r.resrec.rrtype == kDNSType_OPT ? "(OP)" : (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AD)" : "(AD+)", + &pkt.r.resrec); + if (entry) RecordHostInfo(entry, &pkt.r.resrec); + } + + if (entry) AnalyseHost(m, entry, InterfaceID); +} mDNSlocal void ProcessUnicastResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID) - { - int i; - const mDNSu8 *ptr = LocateAnswers(msg, end); - HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R, msg->h.id); - //mprintf("%#a R\n", srcaddr); - - for (i=0; ih.numAnswers + msg->h.numAuthorities + msg->h.numAdditionals; i++) - { - LargeCacheRecord pkt; - ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt); - if (ptr && pkt.r.resrec.rroriginalttl && entry) RecordHostInfo(entry, &pkt.r.resrec); - } - } +{ + int i; + const mDNSu8 *ptr = LocateAnswers(msg, end); + HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R, msg->h.id); + //mprintf("%#a R\n", srcaddr); + + for (i=0; ih.numAnswers + msg->h.numAuthorities + msg->h.numAdditionals; i++) + { + LargeCacheRecord pkt; + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt); + if (ptr && pkt.r.resrec.rroriginalttl && entry) RecordHostInfo(entry, &pkt.r.resrec); + } +} mDNSlocal mDNSBool AddressMatchesFilterList(const mDNSAddr *srcaddr) - { - FilterList *f; - if (!Filters) return(srcaddr->type == mDNSAddrType_IPv4); - for (f=Filters; f; f=f->next) if (mDNSSameAddress(srcaddr, &f->FilterAddr)) return(mDNStrue); - return(mDNSfalse); - } +{ + FilterList *f; + if (!Filters) return(srcaddr->type == mDNSAddrType_IPv4); + for (f=Filters; f; f=f->next) if (mDNSSameAddress(srcaddr, &f->FilterAddr)) return(mDNStrue); + return(mDNSfalse); +} mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, - const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, const mDNSInterfaceID InterfaceID) - { - const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery; - const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; - const mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); - mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions; - int goodinterface = (FilterInterface == 0); - - (void)dstaddr; // Unused - (void)dstport; // Unused - - // Read the integer parts which are in IETF byte-order (MSB first, LSB second) - msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); - msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); - msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); - msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]); - - // For now we're only interested in monitoring IPv4 traffic. - // All IPv6 packets should just be duplicates of the v4 packets. - if (!goodinterface) goodinterface = (FilterInterface == (int)mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNSfalse)); - if (goodinterface && AddressMatchesFilterList(srcaddr)) - { - mDNS_Lock(m); - if (!mDNSAddrIsDNSMulticast(dstaddr)) - { - if (QR_OP == StdQ) mprintf("Unicast query from %#a\n", srcaddr); - else if (QR_OP == StdR) ProcessUnicastResponse(m, msg, end, srcaddr, InterfaceID); - } - else - { - 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]); - GotPacketFromHost(srcaddr, HostPkt_B, msg->h.id); - NumPktB++; - } - } - mDNS_Unlock(m); - } - } + 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; + const mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); + mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions; + int goodinterface = (FilterInterface == 0); + + (void)dstaddr; // Unused + (void)dstport; // Unused + + // Read the integer parts which are in IETF byte-order (MSB first, LSB second) + msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); + msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); + msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]); + + // For now we're only interested in monitoring IPv4 traffic. + // All IPv6 packets should just be duplicates of the v4 packets. + if (!goodinterface) goodinterface = (FilterInterface == (int)mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNSfalse)); + if (goodinterface && AddressMatchesFilterList(srcaddr)) + { + mDNS_Lock(m); + if (!mDNSAddrIsDNSMulticast(dstaddr)) + { + if (QR_OP == StdQ) mprintf("Unicast query from %#a\n", srcaddr); + else if (QR_OP == StdR) ProcessUnicastResponse(m, msg, end, srcaddr, InterfaceID); + } + else + { + 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]); + GotPacketFromHost(srcaddr, HostPkt_B, msg->h.id); + NumPktB++; + } + } + mDNS_Unlock(m); + } +} mDNSlocal mStatus mDNSNetMonitor(void) - { - struct tm tm; - int h, m, s, mul, div, TotPkt; +{ + struct tm tm; + int h, m, s, mul, div, TotPkt; #if !defined(WIN32) - sigset_t signals; + sigset_t signals; #endif - - mStatus status = mDNS_Init(&mDNSStorage, &PlatformStorage, - mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, - mDNS_Init_DontAdvertiseLocalAddresses, - mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); - if (status) return(status); - gettimeofday(&tv_start, NULL); + mStatus status = mDNS_Init(&mDNSStorage, &PlatformStorage, + mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, + mDNS_Init_DontAdvertiseLocalAddresses, + mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); + if (status) return(status); + + gettimeofday(&tv_start, NULL); #if defined( WIN32 ) - status = SetupInterfaceList(&mDNSStorage); - if (status) return(status); - gStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if (gStopEvent == INVALID_HANDLE_VALUE) return mStatus_UnknownErr; - mDNSPollRegisterEvent( gStopEvent, StopNotification, NULL ); - if (!SetConsoleCtrlHandler(ConsoleControlHandler, TRUE)) return mStatus_UnknownErr; - gRunning = mDNStrue; while (gRunning) mDNSPoll( INFINITE ); - if (!SetConsoleCtrlHandler(ConsoleControlHandler, FALSE)) return mStatus_UnknownErr; - CloseHandle(gStopEvent); + status = SetupInterfaceList(&mDNSStorage); + if (status) return(status); + gStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if (gStopEvent == INVALID_HANDLE_VALUE) return mStatus_UnknownErr; + mDNSPollRegisterEvent( gStopEvent, StopNotification, NULL ); + if (!SetConsoleCtrlHandler(ConsoleControlHandler, TRUE)) return mStatus_UnknownErr; + gRunning = mDNStrue; while (gRunning) mDNSPoll( INFINITE ); + if (!SetConsoleCtrlHandler(ConsoleControlHandler, FALSE)) return mStatus_UnknownErr; + CloseHandle(gStopEvent); #else - mDNSPosixListenForSignalInEventLoop(SIGINT); - mDNSPosixListenForSignalInEventLoop(SIGTERM); - - do - { - struct timeval timeout = { 0x3FFFFFFF, 0 }; // wait until SIGINT or SIGTERM - mDNSBool gotSomething; - mDNSPosixRunEventLoopOnce(&mDNSStorage, &timeout, &signals, &gotSomething); - } - while ( !( sigismember( &signals, SIGINT) || sigismember( &signals, SIGTERM))); + mDNSPosixListenForSignalInEventLoop(SIGINT); + mDNSPosixListenForSignalInEventLoop(SIGTERM); + + do + { + struct timeval timeout = { 0x3FFFFFFF, 0 }; // wait until SIGINT or SIGTERM + mDNSBool gotSomething; + mDNSPosixRunEventLoopOnce(&mDNSStorage, &timeout, &signals, &gotSomething); + } + while ( !( sigismember( &signals, SIGINT) || sigismember( &signals, SIGTERM))); #endif - - // Now display final summary - TotPkt = NumPktQ + NumPktL + NumPktR; - gettimeofday(&tv_end, NULL); - tv_interval = tv_end; - if (tv_start.tv_usec > tv_interval.tv_usec) - { tv_interval.tv_usec += 1000000; tv_interval.tv_sec--; } - tv_interval.tv_sec -= tv_start.tv_sec; - tv_interval.tv_usec -= tv_start.tv_usec; - h = (tv_interval.tv_sec / 3600); - m = (tv_interval.tv_sec % 3600) / 60; - s = (tv_interval.tv_sec % 60); - if (tv_interval.tv_sec > 10) - { - mul = 60; - div = tv_interval.tv_sec; - } - else - { - mul = 60000; - div = tv_interval.tv_sec * 1000 + tv_interval.tv_usec / 1000; - if (div == 0) div=1; - } - - mprintf("\n\n"); - localtime_r((time_t*)&tv_start.tv_sec, &tm); - mprintf("Started %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_start.tv_usec); - localtime_r((time_t*)&tv_end.tv_sec, &tm); - mprintf("End %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_end.tv_usec); - mprintf("Captured for %3d:%02d:%02d.%06d\n", h, m, s, tv_interval.tv_usec); - if (!Filters) - { - mprintf("Unique source addresses seen on network:"); - if (IPv4HostList.num) mprintf(" %ld (IPv4)", IPv4HostList.num); - if (IPv6HostList.num) mprintf(" %ld (IPv6)", IPv6HostList.num); - if (!IPv4HostList.num && !IPv6HostList.num) mprintf(" None"); - mprintf("\n"); - } - mprintf("\n"); - mprintf("Modern Query Packets: %7d (avg%5d/min)\n", NumPktQ, NumPktQ * mul / div); - mprintf("Legacy Query Packets: %7d (avg%5d/min)\n", NumPktL, NumPktL * mul / div); - mprintf("Multicast Response Packets: %7d (avg%5d/min)\n", NumPktR, NumPktR * mul / div); - mprintf("Total Multicast Packets: %7d (avg%5d/min)\n", TotPkt, TotPkt * mul / div); - mprintf("\n"); - mprintf("Total New Service Probes: %7d (avg%5d/min)\n", NumProbes, NumProbes * mul / div); - mprintf("Total Goodbye Announcements: %7d (avg%5d/min)\n", NumGoodbyes, NumGoodbyes * mul / div); - mprintf("Total Query Questions: %7d (avg%5d/min)\n", NumQuestions, NumQuestions * mul / div); - mprintf("Total Queries from Legacy Clients:%7d (avg%5d/min)\n", NumLegacy, NumLegacy * mul / div); - mprintf("Total Answers/Announcements: %7d (avg%5d/min)\n", NumAnswers, NumAnswers * mul / div); - mprintf("Total Additional Records: %7d (avg%5d/min)\n", NumAdditionals, NumAdditionals * mul / div); - mprintf("\n"); - printstats(kReportTopServices); - - if (!ExactlyOneFilter) - { - ShowSortedHostList(&IPv4HostList, kReportTopHosts); - ShowSortedHostList(&IPv6HostList, kReportTopHosts); - } - - mDNS_Close(&mDNSStorage); - return(0); - } + + // Now display final summary + TotPkt = NumPktQ + NumPktL + NumPktR; + gettimeofday(&tv_end, NULL); + tv_interval = tv_end; + if (tv_start.tv_usec > tv_interval.tv_usec) + { tv_interval.tv_usec += 1000000; tv_interval.tv_sec--; } + tv_interval.tv_sec -= tv_start.tv_sec; + tv_interval.tv_usec -= tv_start.tv_usec; + h = (tv_interval.tv_sec / 3600); + m = (tv_interval.tv_sec % 3600) / 60; + s = (tv_interval.tv_sec % 60); + if (tv_interval.tv_sec > 10) + { + mul = 60; + div = tv_interval.tv_sec; + } + else + { + mul = 60000; + div = tv_interval.tv_sec * 1000 + tv_interval.tv_usec / 1000; + if (div == 0) div=1; + } + + mprintf("\n\n"); + localtime_r((time_t*)&tv_start.tv_sec, &tm); + mprintf("Started %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_start.tv_usec); + localtime_r((time_t*)&tv_end.tv_sec, &tm); + mprintf("End %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_end.tv_usec); + mprintf("Captured for %3d:%02d:%02d.%06d\n", h, m, s, tv_interval.tv_usec); + if (!Filters) + { + mprintf("Unique source addresses seen on network:"); + if (IPv4HostList.num) mprintf(" %ld (IPv4)", IPv4HostList.num); + if (IPv6HostList.num) mprintf(" %ld (IPv6)", IPv6HostList.num); + if (!IPv4HostList.num && !IPv6HostList.num) mprintf(" None"); + mprintf("\n"); + } + mprintf("\n"); + mprintf("Modern Query Packets: %7d (avg%5d/min)\n", NumPktQ, NumPktQ * mul / div); + mprintf("Legacy Query Packets: %7d (avg%5d/min)\n", NumPktL, NumPktL * mul / div); + mprintf("Multicast Response Packets: %7d (avg%5d/min)\n", NumPktR, NumPktR * mul / div); + mprintf("Total Multicast Packets: %7d (avg%5d/min)\n", TotPkt, TotPkt * mul / div); + mprintf("\n"); + mprintf("Total New Service Probes: %7d (avg%5d/min)\n", NumProbes, NumProbes * mul / div); + mprintf("Total Goodbye Announcements: %7d (avg%5d/min)\n", NumGoodbyes, NumGoodbyes * mul / div); + mprintf("Total Query Questions: %7d (avg%5d/min)\n", NumQuestions, NumQuestions * mul / div); + mprintf("Total Queries from Legacy Clients:%7d (avg%5d/min)\n", NumLegacy, NumLegacy * mul / div); + mprintf("Total Answers/Announcements: %7d (avg%5d/min)\n", NumAnswers, NumAnswers * mul / div); + mprintf("Total Additional Records: %7d (avg%5d/min)\n", NumAdditionals, NumAdditionals * mul / div); + mprintf("\n"); + printstats(kReportTopServices); + + if (!ExactlyOneFilter) + { + ShowSortedHostList(&IPv4HostList, kReportTopHosts); + ShowSortedHostList(&IPv6HostList, kReportTopHosts); + } + + mDNS_Close(&mDNSStorage); + return(0); +} mDNSexport int main(int argc, char **argv) - { - const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]; - int i; - mStatus status; +{ + const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]; + int i; + mStatus status; #if defined(WIN32) - HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); + HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); #endif - setlinebuf(stdout); // Want to see lines as they appear, not block buffered - - for (i=1; ih_addr; - else goto usage; - } - - f = malloc(sizeof(*f)); - f->FilterAddr = a; - f->next = Filters; - Filters = f; - } - } - - status = mDNSNetMonitor(); - if (status) { fprintf(stderr, "%s: mDNSNetMonitor failed %d\n", progname, (int)status); return(status); } - return(0); + setlinebuf(stdout); // Want to see lines as they appear, not block buffered + + for (i=1; ih_addr; + else goto usage; + } + + f = malloc(sizeof(*f)); + f->FilterAddr = a; + f->next = Filters; + Filters = f; + } + } + + status = mDNSNetMonitor(); + if (status) { fprintf(stderr, "%s: mDNSNetMonitor failed %d\n", progname, (int)status); return(status); } + return(0); usage: - fprintf(stderr, "\nmDNS traffic monitor\n"); - fprintf(stderr, "Usage: %s [-i index] [host]\n", progname); - fprintf(stderr, "Optional [-i index] parameter displays only packets from that interface index\n"); - fprintf(stderr, "Optional [host] parameter displays only packets from that host\n"); - - fprintf(stderr, "\nPer-packet header output:\n"); - fprintf(stderr, "-Q- Multicast Query from mDNS client that accepts multicast responses\n"); - fprintf(stderr, "-R- Multicast Response packet containing answers/announcements\n"); - fprintf(stderr, "-LQ- Multicast Query from legacy client that does *not* listen for multicast responses\n"); - fprintf(stderr, "Q/Ans/Auth/Add Number of questions, answers, authority records and additional records in packet\n"); - - fprintf(stderr, "\nPer-record display:\n"); - fprintf(stderr, "(PM) Probe Question (new service starting), requesting multicast response\n"); - fprintf(stderr, "(PU) Probe Question (new service starting), requesting unicast response\n"); - fprintf(stderr, "(DE) Deletion/Goodbye (service going away)\n"); - fprintf(stderr, "(LQ) Legacy Query Question\n"); - fprintf(stderr, "(QM) Query Question, requesting multicast response\n"); - fprintf(stderr, "(QU) Query Question, requesting unicast response\n"); - fprintf(stderr, "(KA) Known Answer (information querier already knows)\n"); - fprintf(stderr, "(AN) Unique Answer to question (or periodic announcment) (entire RR Set)\n"); - fprintf(stderr, "(AN+) Answer to question (or periodic announcment) (add to existing RR Set members)\n"); - fprintf(stderr, "(AD) Unique Additional Record Set (entire RR Set)\n"); - fprintf(stderr, "(AD+) Additional records (add to existing RR Set members)\n"); - - fprintf(stderr, "\nFinal summary, sorted by service type:\n"); - fprintf(stderr, "Probe Probes for this service type starting up\n"); - fprintf(stderr, "Goodbye Goodbye (deletion) packets for this service type shutting down\n"); - fprintf(stderr, "BrowseQ Browse questions from clients browsing to find a list of instances of this service\n"); - fprintf(stderr, "BrowseA Browse answers/announcments advertising instances of this service\n"); - fprintf(stderr, "ResolveQ Resolve questions from clients actively connecting to an instance of this service\n"); - fprintf(stderr, "ResolveA Resolve answers/announcments giving connection information for an instance of this service\n"); - fprintf(stderr, "\n"); - return(-1); - } + fprintf(stderr, "\nmDNS traffic monitor\n"); + fprintf(stderr, "Usage: %s [-i index] [host]\n", progname); + fprintf(stderr, "Optional [-i index] parameter displays only packets from that interface index\n"); + fprintf(stderr, "Optional [host] parameter displays only packets from that host\n"); + + fprintf(stderr, "\nPer-packet header output:\n"); + fprintf(stderr, "-Q- Multicast Query from mDNS client that accepts multicast responses\n"); + fprintf(stderr, "-R- Multicast Response packet containing answers/announcements\n"); + fprintf(stderr, "-LQ- Multicast Query from legacy client that does *not* listen for multicast responses\n"); + fprintf(stderr, "Q/Ans/Auth/Add Number of questions, answers, authority records and additional records in packet\n"); + + fprintf(stderr, "\nPer-record display:\n"); + fprintf(stderr, "(PM) Probe Question (new service starting), requesting multicast response\n"); + fprintf(stderr, "(PU) Probe Question (new service starting), requesting unicast response\n"); + fprintf(stderr, "(DE) Deletion/Goodbye (service going away)\n"); + fprintf(stderr, "(LQ) Legacy Query Question\n"); + fprintf(stderr, "(QM) Query Question, requesting multicast response\n"); + fprintf(stderr, "(QU) Query Question, requesting unicast response\n"); + fprintf(stderr, "(KA) Known Answer (information querier already knows)\n"); + fprintf(stderr, "(AN) Unique Answer to question (or periodic announcment) (entire RR Set)\n"); + fprintf(stderr, "(AN+) Answer to question (or periodic announcment) (add to existing RR Set members)\n"); + fprintf(stderr, "(AD) Unique Additional Record Set (entire RR Set)\n"); + fprintf(stderr, "(AD+) Additional records (add to existing RR Set members)\n"); + + fprintf(stderr, "\nFinal summary, sorted by service type:\n"); + fprintf(stderr, "Probe Probes for this service type starting up\n"); + fprintf(stderr, "Goodbye Goodbye (deletion) packets for this service type shutting down\n"); + fprintf(stderr, "BrowseQ Browse questions from clients browsing to find a list of instances of this service\n"); + fprintf(stderr, "BrowseA Browse answers/announcments advertising instances of this service\n"); + fprintf(stderr, "ResolveQ Resolve questions from clients actively connecting to an instance of this service\n"); + fprintf(stderr, "ResolveA Resolve answers/announcments giving connection information for an instance of this service\n"); + fprintf(stderr, "\n"); + return(-1); +} diff --git a/mDNSPosix/PosixDaemon.c b/mDNSPosix/PosixDaemon.c index 76681eb..88b3292 100644 --- a/mDNSPosix/PosixDaemon.c +++ b/mDNSPosix/PosixDaemon.c @@ -5,18 +5,18 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - File: daemon.c + File: daemon.c - Contains: main & associated Application layer for mDNSResponder on Linux. + Contains: main & associated Application layer for mDNSResponder on Linux. */ @@ -45,7 +45,7 @@ extern int daemon(int, int); #include "mDNSEmbeddedAPI.h" #include "mDNSPosix.h" -#include "mDNSUNP.h" // For daemon() +#include "mDNSUNP.h" // For daemon() #include "uds_daemon.h" #include "PlatformCommon.h" @@ -58,194 +58,194 @@ static CacheEntity gRRCache[RR_CACHE_SIZE]; static mDNS_PlatformSupport PlatformStorage; mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result) - { - (void)m; // Unused - if (result == mStatus_NoError) - { - // On successful registration of dot-local mDNS host name, daemon may want to check if - // any name conflict and automatic renaming took place, and if so, record the newly negotiated - // name in persistent storage for next time. It should also inform the user of the name change. - // On Mac OS X we store the current dot-local mDNS host name in the SCPreferences store, - // and notify the user with a CFUserNotification. - } - else if (result == mStatus_ConfigChanged) - { - udsserver_handle_configchange(m); - } - else if (result == mStatus_GrowCache) - { - // Allocate another chunk of cache storage - CacheEntity *storage = malloc(sizeof(CacheEntity) * RR_CACHE_SIZE); - if (storage) mDNS_GrowCache(m, storage, RR_CACHE_SIZE); - } - } +{ + (void)m; // Unused + if (result == mStatus_NoError) + { + // On successful registration of dot-local mDNS host name, daemon may want to check if + // any name conflict and automatic renaming took place, and if so, record the newly negotiated + // name in persistent storage for next time. It should also inform the user of the name change. + // On Mac OS X we store the current dot-local mDNS host name in the SCPreferences store, + // and notify the user with a CFUserNotification. + } + else if (result == mStatus_ConfigChanged) + { + udsserver_handle_configchange(m); + } + else if (result == mStatus_GrowCache) + { + // Allocate another chunk of cache storage + CacheEntity *storage = malloc(sizeof(CacheEntity) * RR_CACHE_SIZE); + if (storage) mDNS_GrowCache(m, storage, RR_CACHE_SIZE); + } +} // %%% Reconfigure() probably belongs in the platform support layer (mDNSPosix.c), not the daemon cde // -- all client layers running on top of mDNSPosix.c need to handle network configuration changes, // not only the Unix Domain Socket Daemon static void Reconfigure(mDNS *m) - { - mDNSAddr DynDNSIP; - const mDNSAddr dummy = { mDNSAddrType_IPv4, { { { 1, 1, 1, 1 } } } };; - mDNS_SetPrimaryInterfaceInfo(m, NULL, NULL, NULL); - if (ParseDNSServers(m, uDNS_SERVERS_FILE) < 0) - LogMsg("Unable to parse DNS server list. Unicast DNS-SD unavailable"); - ReadDDNSSettingsFromConfFile(m, CONFIG_FILE, &DynDNSHostname, &DynDNSZone, NULL); - mDNSPlatformSourceAddrForDest(&DynDNSIP, &dummy); - if (DynDNSHostname.c[0]) mDNS_AddDynDNSHostName(m, &DynDNSHostname, NULL, NULL); - if (DynDNSIP.type) mDNS_SetPrimaryInterfaceInfo(m, &DynDNSIP, NULL, NULL); - mDNS_ConfigChanged(m); - } +{ + mDNSAddr DynDNSIP; + const mDNSAddr dummy = { mDNSAddrType_IPv4, { { { 1, 1, 1, 1 } } } };; + mDNS_SetPrimaryInterfaceInfo(m, NULL, NULL, NULL); + if (ParseDNSServers(m, uDNS_SERVERS_FILE) < 0) + LogMsg("Unable to parse DNS server list. Unicast DNS-SD unavailable"); + ReadDDNSSettingsFromConfFile(m, CONFIG_FILE, &DynDNSHostname, &DynDNSZone, NULL); + mDNSPlatformSourceAddrForDest(&DynDNSIP, &dummy); + if (DynDNSHostname.c[0]) mDNS_AddDynDNSHostName(m, &DynDNSHostname, NULL, NULL); + if (DynDNSIP.type) mDNS_SetPrimaryInterfaceInfo(m, &DynDNSIP, NULL, NULL); + mDNS_ConfigChanged(m); +} // Do appropriate things at startup with command line arguments. Calls exit() if unhappy. mDNSlocal void ParseCmdLinArgs(int argc, char **argv) - { - if (argc > 1) - { - if (0 == strcmp(argv[1], "-debug")) mDNS_DebugMode = mDNStrue; - else printf("Usage: %s [-debug]\n", argv[0]); - } - - if (!mDNS_DebugMode) - { - int result = daemon(0, 0); - if (result != 0) { LogMsg("Could not run as daemon - exiting"); exit(result); } +{ + if (argc > 1) + { + if (0 == strcmp(argv[1], "-debug")) mDNS_DebugMode = mDNStrue; + else printf("Usage: %s [-debug]\n", argv[0]); + } + + if (!mDNS_DebugMode) + { + int result = daemon(0, 0); + if (result != 0) { LogMsg("Could not run as daemon - exiting"); exit(result); } #if __APPLE__ - LogMsg("The POSIX mdnsd should only be used on OS X for testing - exiting"); - exit(-1); + LogMsg("The POSIX mdnsd should only be used on OS X for testing - exiting"); + exit(-1); #endif - } - } + } +} mDNSlocal void DumpStateLog(mDNS *const m) // Dump a little log of what we've been up to. - { - LogMsg("---- BEGIN STATE LOG ----"); - udsserver_info(m); - LogMsg("---- END STATE LOG ----"); - } +{ + LogMsg("---- BEGIN STATE LOG ----"); + udsserver_info(m); + LogMsg("---- END STATE LOG ----"); +} mDNSlocal mStatus MainLoop(mDNS *m) // Loop until we quit. - { - sigset_t signals; - mDNSBool gotData = mDNSfalse; - - mDNSPosixListenForSignalInEventLoop(SIGINT); - mDNSPosixListenForSignalInEventLoop(SIGTERM); - mDNSPosixListenForSignalInEventLoop(SIGUSR1); - mDNSPosixListenForSignalInEventLoop(SIGPIPE); - mDNSPosixListenForSignalInEventLoop(SIGHUP) ; - - for (; ;) - { - // Work out how long we expect to sleep before the next scheduled task - struct timeval timeout; - mDNSs32 ticks; - - // Only idle if we didn't find any data the last time around - if (!gotData) - { - mDNSs32 nextTimerEvent = mDNS_Execute(m); - nextTimerEvent = udsserver_idle(nextTimerEvent); - ticks = nextTimerEvent - 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); - - 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; - } +{ + sigset_t signals; + mDNSBool gotData = mDNSfalse; + + mDNSPosixListenForSignalInEventLoop(SIGINT); + mDNSPosixListenForSignalInEventLoop(SIGTERM); + mDNSPosixListenForSignalInEventLoop(SIGUSR1); + mDNSPosixListenForSignalInEventLoop(SIGPIPE); + mDNSPosixListenForSignalInEventLoop(SIGHUP) ; + + for (; ;) + { + // Work out how long we expect to sleep before the next scheduled task + struct timeval timeout; + mDNSs32 ticks; + + // Only idle if we didn't find any data the last time around + if (!gotData) + { + mDNSs32 nextTimerEvent = mDNS_Execute(m); + nextTimerEvent = udsserver_idle(nextTimerEvent); + ticks = nextTimerEvent - 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); + + 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; +} int main(int argc, char **argv) - { - mStatus err; +{ + mStatus err; + + ParseCmdLinArgs(argc, argv); - ParseCmdLinArgs(argc, argv); + LogMsg("%s starting", mDNSResponderVersionString); - LogMsg("%s starting", mDNSResponderVersionString); + err = mDNS_Init(&mDNSStorage, &PlatformStorage, gRRCache, RR_CACHE_SIZE, mDNS_Init_AdvertiseLocalAddresses, + mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext); - err = mDNS_Init(&mDNSStorage, &PlatformStorage, gRRCache, RR_CACHE_SIZE, mDNS_Init_AdvertiseLocalAddresses, - mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext); + if (mStatus_NoError == err) + err = udsserver_init(mDNSNULL, 0); - if (mStatus_NoError == err) - err = udsserver_init(mDNSNULL, 0); - - Reconfigure(&mDNSStorage); + Reconfigure(&mDNSStorage); - // 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"); - } + // 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(&mDNSStorage); - - LogMsg("%s stopping", mDNSResponderVersionString); + if (mStatus_NoError == err) + err = MainLoop(&mDNSStorage); - mDNS_Close(&mDNSStorage); + LogMsg("%s stopping", mDNSResponderVersionString); + + mDNS_Close(&mDNSStorage); + + if (udsserver_exit() < 0) + LogMsg("ExitCallback: udsserver_exit failed"); - if (udsserver_exit() < 0) - LogMsg("ExitCallback: udsserver_exit failed"); - #if MDNS_DEBUGMSGS > 0 - printf("mDNSResponder exiting normally with %ld\n", err); + printf("mDNSResponder exiting normally with %ld\n", err); #endif - - return err; - } + + return err; +} // uds_daemon support //////////////////////////////////////////////////////////// mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context, void **platform_data) /* Support routine for uds_daemon.c */ - { - // Depends on the fact that udsEventCallback == mDNSPosixEventCallback - (void) platform_data; - return mDNSPosixAddFDToEventLoop(fd, callback, context); - } +{ + // Depends on the fact that udsEventCallback == mDNSPosixEventCallback + (void) platform_data; + return mDNSPosixAddFDToEventLoop(fd, callback, context); +} int udsSupportReadFD(dnssd_sock_t fd, char *buf, int len, int flags, void *platform_data) - { - (void) platform_data; - return recv(fd, buf, len, flags); - } - -mStatus udsSupportRemoveFDFromEventLoop(int fd, void *platform_data) // Note: This also CLOSES the file descriptor - { - mStatus err = mDNSPosixRemoveFDFromEventLoop(fd); - (void) platform_data; - close(fd); - return err; - } +{ + (void) platform_data; + return recv(fd, buf, len, flags); +} + +mStatus udsSupportRemoveFDFromEventLoop(int fd, void *platform_data) // Note: This also CLOSES the file descriptor +{ + mStatus err = mDNSPosixRemoveFDFromEventLoop(fd); + (void) platform_data; + close(fd); + return err; +} mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay) - { - (void)m; - (void)delay; - // No-op, for now - } +{ + (void)m; + (void)delay; + // No-op, for now +} #if _BUILDING_XCODE_PROJECT_ // If the process crashes, then this string will be magically included in the automatically-generated crash log const char *__crashreporter_info__ = mDNSResponderVersionString_SCCS + 5; -asm(".desc ___crashreporter_info__, 0x10"); +asm (".desc ___crashreporter_info__, 0x10"); #endif // For convenience when using the "strings" command, this is the last thing in the file diff --git a/mDNSPosix/ProxyResponder.c b/mDNSPosix/ProxyResponder.c index ebbbaa7..3982161 100644 --- a/mDNSPosix/ProxyResponder.c +++ b/mDNSPosix/ProxyResponder.c @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,23 +15,23 @@ * limitations under the License. */ -#include // For printf() -#include // For exit() etc. -#include // For strlen() etc. -#include // For select() -#include // For SIGINT, SIGTERM -#include // For errno, EINTR -#include // For INADDR_NONE -#include // For inet_addr() -#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 // For printf() +#include // For exit() etc. +#include // For strlen() etc. +#include // For select() +#include // For SIGINT, SIGTERM +#include // For errno, EINTR +#include // For INADDR_NONE +#include // For inet_addr() +#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 +#ifndef INADDR_NONE +#define INADDR_NONE (mDNSu32)0xffffffff #endif //************************************************************************************************************* @@ -44,53 +44,53 @@ mDNSexport const char ProgramName[] = "mDNSProxyResponderPosix"; // Proxy Host Registration typedef struct - { - mDNSv4Addr ip; - domainlabel hostlabel; // Conforms to standard DNS letter-digit-hyphen host name rules - AuthRecord RR_A; // 'A' (address) record for our ".local" name - AuthRecord RR_PTR; // PTR (reverse lookup) record - } ProxyHost; +{ + mDNSv4Addr ip; + domainlabel hostlabel; // Conforms to standard DNS letter-digit-hyphen host name rules + AuthRecord RR_A; // 'A' (address) record for our ".local" name + AuthRecord RR_PTR; // PTR (reverse lookup) record +} ProxyHost; mDNSlocal void HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) - { - ProxyHost *f = (ProxyHost*)rr->RecordContext; - if (result == mStatus_NoError) - debugf("Host name successfully registered: %##s", rr->resrec.name->c); - else - { - debugf("Host name conflict for %##s", rr->resrec.name->c); - mDNS_Deregister(m, &f->RR_A); - mDNS_Deregister(m, &f->RR_PTR); - exit(-1); - } - } +{ + ProxyHost *f = (ProxyHost*)rr->RecordContext; + if (result == mStatus_NoError) + debugf("Host name successfully registered: %##s", rr->resrec.name->c); + else + { + debugf("Host name conflict for %##s", rr->resrec.name->c); + mDNS_Deregister(m, &f->RR_A); + mDNS_Deregister(m, &f->RR_PTR); + exit(-1); + } +} mDNSlocal mStatus mDNS_RegisterProxyHost(mDNS *m, ProxyHost *p) - { - char buffer[32]; - - mDNS_SetupResourceRecord(&p->RR_A, mDNSNULL, mDNSInterface_Any, kDNSType_A, 60, kDNSRecordTypeUnique, AuthRecordAny, HostNameCallback, p); - mDNS_SetupResourceRecord(&p->RR_PTR, mDNSNULL, mDNSInterface_Any, kDNSType_PTR, 60, kDNSRecordTypeKnownUnique, AuthRecordAny, HostNameCallback, p); +{ + char buffer[32]; + + mDNS_SetupResourceRecord(&p->RR_A, mDNSNULL, mDNSInterface_Any, kDNSType_A, 60, kDNSRecordTypeUnique, AuthRecordAny, HostNameCallback, p); + mDNS_SetupResourceRecord(&p->RR_PTR, mDNSNULL, mDNSInterface_Any, kDNSType_PTR, 60, kDNSRecordTypeKnownUnique, AuthRecordAny, HostNameCallback, p); + + p->RR_A.namestorage.c[0] = 0; + AppendDomainLabel(&p->RR_A.namestorage, &p->hostlabel); + AppendLiteralLabelString(&p->RR_A.namestorage, "local"); - p->RR_A.namestorage.c[0] = 0; - AppendDomainLabel(&p->RR_A.namestorage, &p->hostlabel); - AppendLiteralLabelString(&p->RR_A.namestorage, "local"); + // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code + 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.namestorage, buffer); + p->RR_PTR.ForceMCast = mDNStrue; // This PTR points to our dot-local name, so don't ever try to write it into a uDNS server - // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code - 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.namestorage, buffer); - p->RR_PTR.ForceMCast = mDNStrue; // This PTR points to our dot-local name, so don't ever try to write it into a uDNS server + p->RR_A.resrec.rdata->u.ipv4 = p->ip; + AssignDomainName(&p->RR_PTR.resrec.rdata->u.name, p->RR_A.resrec.name); - p->RR_A. resrec.rdata->u.ipv4 = p->ip; - AssignDomainName(&p->RR_PTR.resrec.rdata->u.name, p->RR_A.resrec.name); + mDNS_Register(m, &p->RR_A); + mDNS_Register(m, &p->RR_PTR); - mDNS_Register(m, &p->RR_A); - mDNS_Register(m, &p->RR_PTR); + debugf("Made Proxy Host Records for %##s", p->RR_A.resrec.name->c); - debugf("Made Proxy Host Records for %##s", p->RR_A.resrec.name->c); - - return(mStatus_NoError); - } + return(mStatus_NoError); +} //************************************************************************************************************* // Service Registration @@ -100,69 +100,69 @@ mDNSlocal mStatus mDNS_RegisterProxyHost(mDNS *m, ProxyHost *p) // For a device with a user interface, and a screen, and a keyboard, the appropriate // response may be to prompt the user and ask them to choose a new name for the service. mDNSlocal void ServiceCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result) - { - switch (result) - { - case mStatus_NoError: debugf("Callback: %##s Name Registered", sr->RR_SRV.resrec.name->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) - { - char buffer[MAX_ESCAPED_DOMAIN_NAME]; - ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer); - printf("Service %s now registered and active\n", buffer); - } - - if (result == mStatus_NameConflict) - { - char buffer1[MAX_ESCAPED_DOMAIN_NAME], buffer2[MAX_ESCAPED_DOMAIN_NAME]; - ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer1); - mDNS_RenameAndReregisterService(m, sr, mDNSNULL); - ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer2); - printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2); - } - } +{ + 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 %ld", sr->RR_SRV.resrec.name->c, result); break; + } + + if (result == mStatus_NoError) + { + char buffer[MAX_ESCAPED_DOMAIN_NAME]; + ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer); + printf("Service %s now registered and active\n", buffer); + } + + if (result == mStatus_NameConflict) + { + char buffer1[MAX_ESCAPED_DOMAIN_NAME], buffer2[MAX_ESCAPED_DOMAIN_NAME]; + ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer1); + mDNS_RenameAndReregisterService(m, sr, mDNSNULL); + ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer2); + printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2); + } +} // RegisterService() is a simple wrapper function which takes C string // parameters, converts them to domainname parameters, and calls mDNS_RegisterService() mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset, - const char name[], const char type[], const char domain[], - const domainname *host, mDNSu16 PortAsNumber, int argc, char **argv) - { - domainlabel n; - domainname t, d; - unsigned char txtbuffer[1024], *bptr = txtbuffer; - char buffer[MAX_ESCAPED_DOMAIN_NAME]; - - MakeDomainLabelFromLiteralString(&n, name); - MakeDomainNameFromDNSNameString(&t, type); - MakeDomainNameFromDNSNameString(&d, domain); - while (argc) - { - int len = strlen(argv[0]); - if (len > 255 || bptr + 1 + len >= txtbuffer + sizeof(txtbuffer)) break; - printf("STR: %s\n", argv[0]); - bptr[0] = len; - strcpy((char*)(bptr+1), argv[0]); - bptr += 1 + len; - argc--; - argv++; - } - - mDNS_RegisterService(m, recordset, - &n, &t, &d, // Name, type, domain - host, mDNSOpaque16fromIntVal(PortAsNumber), - txtbuffer, bptr-txtbuffer, // TXT data, length - mDNSNULL, 0, // Subtypes - mDNSInterface_Any, // Interface ID - ServiceCallback, mDNSNULL, 0); // Callback, context, flags - - ConvertDomainNameToCString(recordset->RR_SRV.resrec.name, buffer); - printf("Made Service Records for %s\n", buffer); - } + const char name[], const char type[], const char domain[], + const domainname *host, mDNSu16 PortAsNumber, int argc, char **argv) +{ + domainlabel n; + domainname t, d; + unsigned char txtbuffer[1024], *bptr = txtbuffer; + char buffer[MAX_ESCAPED_DOMAIN_NAME]; + + MakeDomainLabelFromLiteralString(&n, name); + MakeDomainNameFromDNSNameString(&t, type); + MakeDomainNameFromDNSNameString(&d, domain); + while (argc) + { + int len = strlen(argv[0]); + if (len > 255 || bptr + 1 + len >= txtbuffer + sizeof(txtbuffer)) break; + printf("STR: %s\n", argv[0]); + bptr[0] = len; + strcpy((char*)(bptr+1), argv[0]); + bptr += 1 + len; + argc--; + argv++; + } + + mDNS_RegisterService(m, recordset, + &n, &t, &d, // Name, type, domain + host, mDNSOpaque16fromIntVal(PortAsNumber), + txtbuffer, bptr-txtbuffer, // TXT data, length + mDNSNULL, 0, // Subtypes + mDNSInterface_Any, // Interface ID + ServiceCallback, mDNSNULL, 0); // Callback, context, flags + + ConvertDomainNameToCString(recordset->RR_SRV.resrec.name, buffer); + printf("Made Service Records for %s\n", buffer); +} //************************************************************************************************************* // Service non-existence assertion @@ -173,128 +173,128 @@ mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset, // since it would be confusing to users to have two equivalent services with the same name. mDNSlocal void NoSuchServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result) - { - const domainname *proxyhostname = (const domainname *)rr->RecordContext; - switch (result) - { - 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) - { - char buffer[MAX_ESCAPED_DOMAIN_NAME]; - ConvertDomainNameToCString(rr->resrec.name, buffer); - printf("Non-existence assertion %s now registered and active\n", buffer); - } - - if (result == mStatus_NameConflict) - { - domainlabel n; - domainname t, d; - char buffer1[MAX_ESCAPED_DOMAIN_NAME], buffer2[MAX_ESCAPED_DOMAIN_NAME]; - ConvertDomainNameToCString(rr->resrec.name, buffer1); - DeconstructServiceName(rr->resrec.name, &n, &t, &d); - IncrementLabelSuffix(&n, mDNStrue); - mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, mDNSNULL, mDNSfalse); - ConvertDomainNameToCString(rr->resrec.name, buffer2); - printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2); - } - } +{ + const domainname *proxyhostname = (const domainname *)rr->RecordContext; + switch (result) + { + 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) + { + char buffer[MAX_ESCAPED_DOMAIN_NAME]; + ConvertDomainNameToCString(rr->resrec.name, buffer); + printf("Non-existence assertion %s now registered and active\n", buffer); + } + + if (result == mStatus_NameConflict) + { + domainlabel n; + domainname t, d; + char buffer1[MAX_ESCAPED_DOMAIN_NAME], buffer2[MAX_ESCAPED_DOMAIN_NAME]; + ConvertDomainNameToCString(rr->resrec.name, buffer1); + DeconstructServiceName(rr->resrec.name, &n, &t, &d); + IncrementLabelSuffix(&n, mDNStrue); + mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, mDNSNULL, 0); + ConvertDomainNameToCString(rr->resrec.name, buffer2); + printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2); + } +} mDNSlocal void RegisterNoSuchService(mDNS *m, AuthRecord *const rr, domainname *proxyhostname, - const char name[], const char type[], const char domain[]) - { - domainlabel n; - domainname t, d; - char buffer[MAX_ESCAPED_DOMAIN_NAME]; - MakeDomainLabelFromLiteralString(&n, name); - MakeDomainNameFromDNSNameString(&t, type); - MakeDomainNameFromDNSNameString(&d, domain); - mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, proxyhostname, mDNSfalse); - ConvertDomainNameToCString(rr->resrec.name, buffer); - printf("Made Non-existence Record for %s\n", buffer); - } + const char name[], const char type[], const char domain[]) +{ + domainlabel n; + domainname t, d; + char buffer[MAX_ESCAPED_DOMAIN_NAME]; + MakeDomainLabelFromLiteralString(&n, name); + MakeDomainNameFromDNSNameString(&t, type); + MakeDomainNameFromDNSNameString(&d, domain); + mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, proxyhostname, 0); + ConvertDomainNameToCString(rr->resrec.name, buffer); + printf("Made Non-existence Record for %s\n", buffer); +} //************************************************************************************************************* // Main mDNSexport int main(int argc, char **argv) - { - mStatus status; - sigset_t signals; - - if (argc < 3) goto usage; - - status = mDNS_Init(&mDNSStorage, &PlatformStorage, - mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, - mDNS_Init_DontAdvertiseLocalAddresses, - mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); - if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %d\n", (int)status); return(status); } - - mDNSPosixListenForSignalInEventLoop(SIGINT); - mDNSPosixListenForSignalInEventLoop(SIGTERM); - - if (!strcmp(argv[1], "-")) - { - domainname proxyhostname; - AuthRecord proxyrecord; - if (argc < 5) goto usage; - proxyhostname.c[0] = 0; - AppendLiteralLabelString(&proxyhostname, argv[2]); - AppendLiteralLabelString(&proxyhostname, "local"); - RegisterNoSuchService(&mDNSStorage, &proxyrecord, &proxyhostname, argv[3], argv[4], "local."); - } - else - { - ProxyHost proxyhost; - ServiceRecordSet proxyservice; - - proxyhost.ip.NotAnInteger = inet_addr(argv[1]); - if (proxyhost.ip.NotAnInteger == INADDR_NONE) // INADDR_NONE is 0xFFFFFFFF - { - struct hostent *h = gethostbyname(argv[1]); - if (h) proxyhost.ip.NotAnInteger = *(long*)h->h_addr; - } - if (proxyhost.ip.NotAnInteger == INADDR_NONE) // INADDR_NONE is 0xFFFFFFFF - { - fprintf(stderr, "%s is not valid host address\n", argv[1]); - return(-1); - } - - MakeDomainLabelFromLiteralString(&proxyhost.hostlabel, argv[2]); - - mDNS_RegisterProxyHost(&mDNSStorage, &proxyhost); - - if (argc >=6) - RegisterService(&mDNSStorage, &proxyservice, argv[3], argv[4], "local.", - proxyhost.RR_A.resrec.name, atoi(argv[5]), argc-6, &argv[6]); - } - - do - { - struct timeval timeout = { 0x3FFFFFFF, 0 }; // wait until SIGINT or SIGTERM - mDNSBool gotSomething; - mDNSPosixRunEventLoopOnce(&mDNSStorage, &timeout, &signals, &gotSomething); - } - while ( !( sigismember( &signals, SIGINT) || sigismember( &signals, SIGTERM))); - - mDNS_Close(&mDNSStorage); - - return(0); +{ + mStatus status; + sigset_t signals; + + if (argc < 3) goto usage; + + status = mDNS_Init(&mDNSStorage, &PlatformStorage, + mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, + mDNS_Init_DontAdvertiseLocalAddresses, + mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); + if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %d\n", (int)status); return(status); } + + mDNSPosixListenForSignalInEventLoop(SIGINT); + mDNSPosixListenForSignalInEventLoop(SIGTERM); + + if (!strcmp(argv[1], "-")) + { + domainname proxyhostname; + AuthRecord proxyrecord; + if (argc < 5) goto usage; + proxyhostname.c[0] = 0; + AppendLiteralLabelString(&proxyhostname, argv[2]); + AppendLiteralLabelString(&proxyhostname, "local"); + RegisterNoSuchService(&mDNSStorage, &proxyrecord, &proxyhostname, argv[3], argv[4], "local."); + } + else + { + ProxyHost proxyhost; + ServiceRecordSet proxyservice; + + proxyhost.ip.NotAnInteger = inet_addr(argv[1]); + if (proxyhost.ip.NotAnInteger == INADDR_NONE) // INADDR_NONE is 0xFFFFFFFF + { + struct hostent *h = gethostbyname(argv[1]); + if (h) proxyhost.ip.NotAnInteger = *(long*)h->h_addr; + } + if (proxyhost.ip.NotAnInteger == INADDR_NONE) // INADDR_NONE is 0xFFFFFFFF + { + fprintf(stderr, "%s is not valid host address\n", argv[1]); + return(-1); + } + + MakeDomainLabelFromLiteralString(&proxyhost.hostlabel, argv[2]); + + mDNS_RegisterProxyHost(&mDNSStorage, &proxyhost); + + if (argc >=6) + RegisterService(&mDNSStorage, &proxyservice, argv[3], argv[4], "local.", + proxyhost.RR_A.resrec.name, atoi(argv[5]), argc-6, &argv[6]); + } + + do + { + struct timeval timeout = { 0x3FFFFFFF, 0 }; // wait until SIGINT or SIGTERM + mDNSBool gotSomething; + mDNSPosixRunEventLoopOnce(&mDNSStorage, &timeout, &signals, &gotSomething); + } + while ( !( sigismember( &signals, SIGINT) || sigismember( &signals, SIGTERM))); + + mDNS_Close(&mDNSStorage); + + return(0); usage: - fprintf(stderr, "%s ip hostlabel [srvname srvtype port txt [txt ...]]\n", argv[0]); - fprintf(stderr, "ip Real IP address (or valid host name) of the host where the service actually resides\n"); - fprintf(stderr, "hostlabel First label of the dot-local host name to create for this host, e.g. \"foo\" for \"foo.local.\"\n"); - fprintf(stderr, "srvname Descriptive name of service, e.g. \"Stuart's Ink Jet Printer\"\n"); - fprintf(stderr, "srvtype IANA service type, e.g. \"_ipp._tcp\" or \"_ssh._tcp\", etc.\n"); - fprintf(stderr, "port Port number where the service resides (1-65535)\n"); - fprintf(stderr, "txt Additional name/value pairs specified in service definition, e.g. \"pdl=application/postscript\"\n"); - fprintf(stderr, "e.g. %s 169.254.12.34 thehost (just create a dot-local host name)\n", argv[0]); - fprintf(stderr, "or %s 169.254.12.34 thehost \"My Printer\" _printer._tcp. 515 rp=lpt1 pdl=application/postscript\n", argv[0]); - fprintf(stderr, "or %s - thehost \"My Printer\" _printer._tcp. (assertion of non-existence)\n", argv[0]); - return(-1); - } + fprintf(stderr, "%s ip hostlabel [srvname srvtype port txt [txt ...]]\n", argv[0]); + fprintf(stderr, "ip Real IP address (or valid host name) of the host where the service actually resides\n"); + fprintf(stderr, "hostlabel First label of the dot-local host name to create for this host, e.g. \"foo\" for \"foo.local.\"\n"); + fprintf(stderr, "srvname Descriptive name of service, e.g. \"Stuart's Ink Jet Printer\"\n"); + fprintf(stderr, "srvtype IANA service type, e.g. \"_ipp._tcp\" or \"_ssh._tcp\", etc.\n"); + fprintf(stderr, "port Port number where the service resides (1-65535)\n"); + fprintf(stderr, "txt Additional name/value pairs specified in service definition, e.g. \"pdl=application/postscript\"\n"); + fprintf(stderr, "e.g. %s 169.254.12.34 thehost (just create a dot-local host name)\n", argv[0]); + fprintf(stderr, "or %s 169.254.12.34 thehost \"My Printer\" _printer._tcp. 515 rp=lpt1 pdl=application/postscript\n", argv[0]); + fprintf(stderr, "or %s - thehost \"My Printer\" _printer._tcp. (assertion of non-existence)\n", argv[0]); + return(-1); +} diff --git a/mDNSPosix/ReadMe.txt b/mDNSPosix/ReadMe.txt index d4bff85..c2f5641 100755 --- a/mDNSPosix/ReadMe.txt +++ b/mDNSPosix/ReadMe.txt @@ -297,6 +297,16 @@ Networking, Communications, Hardware 6 Aug 2002 +Impact: A local network user may cause a denial of the Bonjour service +Description: An error handling issue exists in the Bonjour Namespace +Provider. A local network user may send a maliciously crafted multicast +DNS packet leading to an unexpected termination of the Bonjour service. +This update addresses the issue by performing additional validation of +multicast DNS packets. This issue does not affect systems running Mac OS +X or Windows. +CVE-ID +CVE-2011-0220 : JaeSeung Song of the Department of Computing at Imperial +College London To Do List ---------- diff --git a/mDNSPosix/Responder.c b/mDNSPosix/Responder.c index 21109b8..3996b7b 100755 --- a/mDNSPosix/Responder.c +++ b/mDNSPosix/Responder.c @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -24,11 +24,11 @@ #endif #include -#include // For printf() -#include // For exit() etc. -#include // For strlen() etc. -#include // For select() -#include // For errno, EINTR +#include // For printf() +#include // For exit() etc. +#include // For strlen() etc. +#include // For select() +#include // For errno, EINTR #include #include @@ -37,9 +37,9 @@ extern int daemon(int, int); #endif -#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 "mDNSUNP.h" // For daemon() +#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 "mDNSUNP.h" // For daemon() #if COMPILER_LIKES_PRAGMA_MARK #pragma mark ***** Globals @@ -68,38 +68,38 @@ static volatile mDNSBool gStopNow; // o SIGQUIT causes a somewhat orderly shutdown (direct but dangerous) // o SIGKILL kills us dead (easy to implement :-) // -// There are fatal race conditions in our signal handling, but there's not much -// we can do about them while remaining within the Posix space. Specifically, -// if a signal arrives after we test the globals its sets but before we call -// select, the signal will be dropped. The user will have to send the signal -// again. Unfortunately, Posix does not have a "sigselect" to atomically +// There are fatal race conditions in our signal handling, but there's not much +// we can do about them while remaining within the Posix space. Specifically, +// if a signal arrives after we test the globals its sets but before we call +// select, the signal will be dropped. The user will have to send the signal +// again. Unfortunately, Posix does not have a "sigselect" to atomically // modify the signal mask and start a select. static void HandleSigUsr1(int sigraised) - // If we get a SIGUSR1 we toggle the state of the - // verbose mode. +// If we get a SIGUSR1 we toggle the state of the +// verbose mode. { assert(sigraised == SIGUSR1); gReceivedSigUsr1 = mDNStrue; } static void HandleSigHup(int sigraised) - // A handler for SIGHUP that causes us to break out of the - // main event loop when the user kill 1's us. This has the - // effect of triggered the main loop to deregister the - // current services and re-read the preferences. +// A handler for SIGHUP that causes us to break out of the +// main event loop when the user kill 1's us. This has the +// effect of triggered the main loop to deregister the +// current services and re-read the preferences. { assert(sigraised == SIGHUP); - gReceivedSigHup = mDNStrue; + gReceivedSigHup = mDNStrue; } static void HandleSigInt(int sigraised) - // A handler for SIGINT that causes us to break out of the - // main event loop when the user types ^C. This has the - // effect of quitting the program. +// A handler for SIGINT that causes us to break out of the +// main event loop when the user types ^C. This has the +// effect of quitting the program. { assert(sigraised == SIGINT); - + if (gMDNSPlatformPosixVerboseLevel > 0) { fprintf(stderr, "\nSIGINT\n"); } @@ -107,11 +107,11 @@ static void HandleSigInt(int sigraised) } static void HandleSigQuit(int sigraised) - // If we get a SIGQUIT the user is desperate and we - // just call mDNS_Close directly. This is definitely - // not safe (because it could reenter mDNS), but - // we presume that the user has already tried the safe - // alternatives. +// If we get a SIGQUIT the user is desperate and we +// just call mDNS_Close directly. This is definitely +// not safe (because it could reenter mDNS), but +// we presume that the user has already tried the safe +// alternatives. { assert(sigraised == SIGQUIT); @@ -127,15 +127,15 @@ static void HandleSigQuit(int sigraised) #endif static mDNSBool CheckThatRichTextNameIsUsable(const char *richTextName, mDNSBool printExplanation) - // Checks that richTextName is reasonable - // label and, if it isn't and printExplanation is true, prints - // an explanation of why not. +// Checks that richTextName is reasonable +// label and, if it isn't and printExplanation is true, prints +// an explanation of why not. { mDNSBool result = mDNStrue; if (result && strlen(richTextName) > 63) { if (printExplanation) { - fprintf(stderr, - "%s: Service name is too long (must be 63 characters or less)\n", + fprintf(stderr, + "%s: Service name is too long (must be 63 characters or less)\n", gProgramName); } result = mDNSfalse; @@ -150,25 +150,25 @@ static mDNSBool CheckThatRichTextNameIsUsable(const char *richTextName, mDNSBool } static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType, mDNSBool printExplanation) - // Checks that serviceType is a reasonable service type - // label and, if it isn't and printExplanation is true, prints - // an explanation of why not. +// Checks that serviceType is a reasonable service type +// label and, if it isn't and printExplanation is true, prints +// an explanation of why not. { mDNSBool result; - + result = mDNStrue; if (result && strlen(serviceType) > 63) { if (printExplanation) { - fprintf(stderr, - "%s: Service type is too long (must be 63 characters or less)\n", + fprintf(stderr, + "%s: Service type is too long (must be 63 characters or less)\n", gProgramName); } result = mDNSfalse; } if (result && serviceType[0] == 0) { if (printExplanation) { - fprintf(stderr, - "%s: Service type can't be empty\n", + fprintf(stderr, + "%s: Service type can't be empty\n", gProgramName); } result = mDNSfalse; @@ -177,17 +177,17 @@ static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType, mDNSBool p } static mDNSBool CheckThatPortNumberIsUsable(long portNumber, mDNSBool printExplanation) - // Checks that portNumber is a reasonable port number - // and, if it isn't and printExplanation is true, prints - // an explanation of why not. +// Checks that portNumber is a reasonable port number +// and, if it isn't and printExplanation is true, prints +// an explanation of why not. { mDNSBool result; - + result = mDNStrue; if (result && (portNumber <= 0 || portNumber > 65535)) { if (printExplanation) { - fprintf(stderr, - "%s: Port number specified by -p must be in range 1..65535\n", + fprintf(stderr, + "%s: Port number specified by -p must be in range 1..65535\n", gProgramName); } result = mDNSfalse; @@ -208,8 +208,8 @@ enum { static void PrintUsage() { - fprintf(stderr, - "Usage: %s [-v level ] [-r] [-n name] [-t type] [-d domain] [-p port] [-f file] [-b] [-P pidfile] [-x name=val ...]\n", + fprintf(stderr, + "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"); @@ -231,111 +231,111 @@ static void PrintUsage() fprintf(stderr, " all subsequent arguments after -x are treated as name=val pairs.\n"); } -static mDNSBool gAvoidPort53 = mDNStrue; +static mDNSBool gAvoidPort53 = mDNStrue; static const char *gServiceName = ""; static const char *gServiceType = kDefaultServiceType; static const char *gServiceDomain = kDefaultServiceDomain; -static mDNSu8 gServiceText[sizeof(RDataBody)]; -static mDNSu16 gServiceTextLen = 0; -static int gPortNumber = kDefaultPortNumber; +static mDNSu8 gServiceText[sizeof(RDataBody)]; +static mDNSu16 gServiceTextLen = 0; +static int gPortNumber = kDefaultPortNumber; static const char *gServiceFile = ""; -static mDNSBool gDaemon = mDNSfalse; +static mDNSBool gDaemon = mDNSfalse; static const char *gPIDFile = kDefaultPIDFile; static void ParseArguments(int argc, char **argv) - // Parses our command line arguments into the global variables - // listed above. +// Parses our command line arguments into the global variables +// listed above. { int ch; - + // Set gProgramName to the last path component of argv[0] - + gProgramName = strrchr(argv[0], '/'); if (gProgramName == NULL) { gProgramName = argv[0]; } else { gProgramName += 1; } - + // Parse command line options using getopt. - + do { ch = getopt(argc, argv, "v:rn:t:d:p:f:dP:bx"); if (ch != -1) { switch (ch) { - case 'v': - gMDNSPlatformPosixVerboseLevel = atoi(optarg); - if (gMDNSPlatformPosixVerboseLevel < 0 || gMDNSPlatformPosixVerboseLevel > 2) { - fprintf(stderr, - "%s: Verbose mode must be in the range 0..2\n", - gProgramName); - exit(1); - } - break; - case 'r': - gAvoidPort53 = mDNSfalse; - break; - case 'n': - gServiceName = optarg; - if ( ! CheckThatRichTextNameIsUsable(gServiceName, mDNStrue) ) { - exit(1); - } - break; - case 't': - gServiceType = optarg; - if ( ! CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) { - exit(1); - } - break; - case 'd': - gServiceDomain = optarg; - break; - case 'p': - gPortNumber = atol(optarg); - if ( ! CheckThatPortNumberIsUsable(gPortNumber, mDNStrue) ) { - exit(1); - } - break; - case 'f': - gServiceFile = optarg; - break; - case 'b': - gDaemon = mDNStrue; - break; - case 'P': - gPIDFile = optarg; - break; - case 'x': - while (optind < argc) - { - gServiceText[gServiceTextLen] = strlen(argv[optind]); - mDNSPlatformMemCopy(gServiceText+gServiceTextLen+1, argv[optind], gServiceText[gServiceTextLen]); - gServiceTextLen += 1 + gServiceText[gServiceTextLen]; - optind++; - } - ch = -1; - break; - case '?': - default: - PrintUsage(); + case 'v': + gMDNSPlatformPosixVerboseLevel = atoi(optarg); + if (gMDNSPlatformPosixVerboseLevel < 0 || gMDNSPlatformPosixVerboseLevel > 2) { + fprintf(stderr, + "%s: Verbose mode must be in the range 0..2\n", + gProgramName); exit(1); - break; + } + break; + case 'r': + gAvoidPort53 = mDNSfalse; + break; + case 'n': + gServiceName = optarg; + if ( !CheckThatRichTextNameIsUsable(gServiceName, mDNStrue) ) { + exit(1); + } + break; + case 't': + gServiceType = optarg; + if ( !CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) { + exit(1); + } + break; + case 'd': + gServiceDomain = optarg; + break; + case 'p': + gPortNumber = atol(optarg); + if ( !CheckThatPortNumberIsUsable(gPortNumber, mDNStrue) ) { + exit(1); + } + break; + case 'f': + gServiceFile = optarg; + break; + case 'b': + gDaemon = mDNStrue; + break; + case 'P': + gPIDFile = optarg; + break; + case 'x': + while (optind < argc) + { + gServiceText[gServiceTextLen] = strlen(argv[optind]); + mDNSPlatformMemCopy(gServiceText+gServiceTextLen+1, argv[optind], gServiceText[gServiceTextLen]); + gServiceTextLen += 1 + gServiceText[gServiceTextLen]; + optind++; + } + ch = -1; + break; + case '?': + default: + PrintUsage(); + exit(1); + break; } } } while (ch != -1); // Check for any left over command line arguments. - + if (optind != argc) { - PrintUsage(); + PrintUsage(); fprintf(stderr, "%s: Unexpected argument '%s'\n", gProgramName, argv[optind]); exit(1); } - + // Check for inconsistency between the arguments. - + if ( (gServiceName[0] == 0) && (gServiceFile[0] == 0) ) { - PrintUsage(); + PrintUsage(); fprintf(stderr, "%s: You must specify a service name to register (-n) or a service file (-f).\n", gProgramName); exit(1); } @@ -356,77 +356,77 @@ struct PosixService { static PosixService *gServiceList = NULL; static void RegistrationCallback(mDNS *const m, ServiceRecordSet *const thisRegistration, mStatus status) - // mDNS core calls this routine to tell us about the status of - // our registration. The appropriate action to take depends - // entirely on the value of status. +// mDNS core calls this routine to tell us about the status of +// our registration. The appropriate action to take depends +// entirely on the value of status. { switch (status) { - case mStatus_NoError: - debugf("Callback: %##s Name Registered", thisRegistration->RR_SRV.resrec.name->c); - // Do nothing; our name was successfully registered. We may - // get more call backs in the future. - break; + case mStatus_NoError: + debugf("Callback: %##s Name Registered", thisRegistration->RR_SRV.resrec.name->c); + // Do nothing; our name was successfully registered. We may + // get more call backs in the future. + break; - case mStatus_NameConflict: - debugf("Callback: %##s Name Conflict", thisRegistration->RR_SRV.resrec.name->c); - - // In the event of a conflict, this sample RegistrationCallback - // just calls mDNS_RenameAndReregisterService to automatically - // pick a new unique name for the service. For a device such as a - // printer, this may be appropriate. For a device with a user - // interface, and a screen, and a keyboard, the appropriate response - // may be to prompt the user and ask them to choose a new name for - // the service. - // - // Also, what do we do if mDNS_RenameAndReregisterService returns an - // error. Right now I have no place to send that error to. - - status = mDNS_RenameAndReregisterService(m, thisRegistration, mDNSNULL); - assert(status == mStatus_NoError); - break; + case mStatus_NameConflict: + debugf("Callback: %##s Name Conflict", thisRegistration->RR_SRV.resrec.name->c); + + // In the event of a conflict, this sample RegistrationCallback + // just calls mDNS_RenameAndReregisterService to automatically + // pick a new unique name for the service. For a device such as a + // printer, this may be appropriate. For a device with a user + // interface, and a screen, and a keyboard, the appropriate response + // may be to prompt the user and ask them to choose a new name for + // the service. + // + // Also, what do we do if mDNS_RenameAndReregisterService returns an + // error. Right now I have no place to send that error to. + + status = mDNS_RenameAndReregisterService(m, thisRegistration, mDNSNULL); + assert(status == mStatus_NoError); + break; + + case mStatus_MemFree: + debugf("Callback: %##s Memory Free", thisRegistration->RR_SRV.resrec.name->c); + + // When debugging is enabled, make sure that thisRegistration + // is not on our gServiceList. - case mStatus_MemFree: - debugf("Callback: %##s Memory Free", thisRegistration->RR_SRV.resrec.name->c); - - // When debugging is enabled, make sure that thisRegistration - // is not on our gServiceList. - #if !defined(NDEBUG) - { - PosixService *cursor; - - cursor = gServiceList; - while (cursor != NULL) { - assert(&cursor->coreServ != thisRegistration); - cursor = cursor->next; - } - } + { + PosixService *cursor; + + cursor = gServiceList; + while (cursor != NULL) { + assert(&cursor->coreServ != thisRegistration); + cursor = cursor->next; + } + } #endif - free(thisRegistration); - break; + free(thisRegistration); + break; - default: - debugf("Callback: %##s Unknown Status %ld", thisRegistration->RR_SRV.resrec.name->c, status); - break; + default: + debugf("Callback: %##s Unknown Status %ld", thisRegistration->RR_SRV.resrec.name->c, status); + break; } } static int gServiceID = 0; -static mStatus RegisterOneService(const char * richTextName, - const char * serviceType, - const char * serviceDomain, - const mDNSu8 text[], - mDNSu16 textLen, - long portNumber) +static mStatus RegisterOneService(const char * richTextName, + const char * serviceType, + const char * serviceDomain, + const mDNSu8 text[], + mDNSu16 textLen, + long portNumber) { - mStatus status; + mStatus status; PosixService * thisServ; - domainlabel name; - domainname type; - domainname domain; - + domainlabel name; + domainname type; + domainname domain; + status = mStatus_NoError; thisServ = (PosixService *) malloc(sizeof(*thisServ)); if (thisServ == NULL) { @@ -437,12 +437,12 @@ static mStatus RegisterOneService(const char * richTextName, MakeDomainNameFromDNSNameString(&type, serviceType); MakeDomainNameFromDNSNameString(&domain, serviceDomain); status = mDNS_RegisterService(&mDNSStorage, &thisServ->coreServ, - &name, &type, &domain, // Name, type, domain - NULL, mDNSOpaque16fromIntVal(portNumber), - text, textLen, // TXT data, length - NULL, 0, // Subtypes - mDNSInterface_Any, // Interface ID - RegistrationCallback, thisServ, 0); // Callback, context, flags + &name, &type, &domain, // Name, type, domain + NULL, mDNSOpaque16fromIntVal(portNumber), + text, textLen, // TXT data, length + NULL, 0, // Subtypes + mDNSInterface_Any, // Interface ID + RegistrationCallback, thisServ, 0); // Callback, context, flags } if (status == mStatus_NoError) { thisServ->serviceID = gServiceID; @@ -452,12 +452,13 @@ static mStatus RegisterOneService(const char * richTextName, gServiceList = thisServ; if (gMDNSPlatformPosixVerboseLevel > 0) { - fprintf(stderr, - "%s: Registered service %d, name '%s', type '%s', port %ld\n", - gProgramName, - thisServ->serviceID, + fprintf(stderr, + "%s: Registered service %d, name \"%s\", type \"%s\", domain \"%s\", port %ld\n", + gProgramName, + thisServ->serviceID, richTextName, serviceType, + serviceDomain, portNumber); } } else { @@ -468,120 +469,155 @@ static mStatus RegisterOneService(const char * richTextName, return status; } -static mDNSBool ReadALine(char *buf, size_t bufSize, FILE *fp) -// Read a line, skipping over any blank lines or lines starting with '#' +static mDNSBool ReadALine(char *buf, size_t bufSize, FILE *fp, mDNSBool skipBlankLines) { - mDNSBool good, skip; - do { - good = (fgets(buf, bufSize, fp) != NULL); - skip = (good && (buf[0] == '#')); - } while (good && skip); - if (good) - { - int len = strlen( buf); - if ( buf[len - 1] == '\r' || buf[len - 1] == '\n') - buf[len - 1] = '\0'; - } - return good; + size_t len; + mDNSBool readNextLine; + + do { + readNextLine = mDNSfalse; + + if (fgets(buf, bufSize, fp) == NULL) + return mDNSfalse; // encountered EOF or an error condition + + // These first characters indicate a blank line. + if (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\r' || buf[0] == '\n') { + if (!skipBlankLines) + return mDNSfalse; + readNextLine = mDNStrue; + } + // always skip comment lines + if (buf[0] == '#') + readNextLine = mDNStrue; + + } while (readNextLine); + + len = strlen( buf); + if ( buf[len - 1] == '\r' || buf[len - 1] == '\n') + buf[len - 1] = '\0'; + + return mDNStrue; } static mStatus RegisterServicesInFile(const char *filePath) { - mStatus status = mStatus_NoError; + mStatus status = mStatus_NoError; FILE * fp = fopen(filePath, "r"); - int junk; - + if (fp == NULL) { - status = mStatus_UnknownErr; + return mStatus_UnknownErr; } - if (status == mStatus_NoError) { - mDNSBool good = mDNStrue; - do { - int ch; - char name[256]; - char type[256]; - const char *dom = kDefaultServiceDomain; - char rawText[1024]; - mDNSu8 text[sizeof(RDataBody)]; - unsigned int textLen = 0; - char port[256]; - - // Skip over any blank lines. - do ch = fgetc(fp); while ( ch == '\n' || ch == '\r' ); - if (ch != EOF) good = (ungetc(ch, fp) == ch); - - // Read three lines, check them for validity, and register the service. - 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 = CheckThatRichTextNameIsUsable(name, mDNSfalse) - && CheckThatServiceTypeIsUsable(type, mDNSfalse) - && CheckThatPortNumberIsUsable(atol(port), mDNSfalse); - } - if (good) { - while (1) { - int len; - if (!ReadALine(rawText, sizeof(rawText), fp)) break; - len = strlen(rawText); - if (len <= 255) - { - unsigned int newlen = textLen + 1 + len; - if (len == 0 || newlen >= sizeof(text)) break; - text[textLen] = len; - mDNSPlatformMemCopy(text + textLen + 1, rawText, len); - textLen = newlen; - } - else - fprintf(stderr, "%s: TXT attribute too long for name = %s, type = %s, port = %s\n", - gProgramName, name, type, port); - } - } - 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, filePath); + + if (gMDNSPlatformPosixVerboseLevel > 1) + fprintf(stderr, "Parsing %s for services\n", filePath); + + do { + char nameBuf[256]; + char * name = nameBuf; + char type[256]; + const char *dom = kDefaultServiceDomain; + char rawText[1024]; + mDNSu8 text[sizeof(RDataBody)]; + unsigned int textLen = 0; + char port[256]; + char *p; + + // Read the service name, type, port, and optional text record fields. + // Skip blank lines while looking for the next service name. + if (!ReadALine(name, sizeof(nameBuf), fp, mDNStrue)) + break; + + // Special case that allows service name to begin with a '#' + // character by escaping it with a '\' to distiguish it from + // a comment line. Remove the leading '\' here before + // registering the service. + if (name[0] == '\\' && name[1] == '#') + name++; + + if (gMDNSPlatformPosixVerboseLevel > 1) + fprintf(stderr, "Service name: \"%s\"\n", name); + + // Don't skip blank lines in calls to ReadAline() after finding the + // service name since the next blank line indicates the end + // of this service record. + if (!ReadALine(type, sizeof(type), fp, mDNSfalse)) + break; + + // see if a domain name is specified + p = type; + while (*p && *p != ' ' && *p != '\t') p++; + if (*p) { + *p = 0; // NULL terminate the . string + // skip any leading whitespace before domain name + p++; + while (*p && (*p == ' ' || *p == '\t')) p++; + if (*p) + dom = p; } + if (gMDNSPlatformPosixVerboseLevel > 1) { + fprintf(stderr, "Service type: \"%s\"\n", type); + fprintf(stderr, "Service domain: \"%s\"\n", dom); + } + + if (!ReadALine(port, sizeof(port), fp, mDNSfalse)) + break; + if (gMDNSPlatformPosixVerboseLevel > 1) + fprintf(stderr, "Service port: %s\n", port); + + if ( !CheckThatRichTextNameIsUsable(name, mDNStrue) + || !CheckThatServiceTypeIsUsable(type, mDNStrue) + || !CheckThatPortNumberIsUsable(atol(port), mDNStrue)) + break; + + // read the TXT record fields + while (1) { + int len; + if (!ReadALine(rawText, sizeof(rawText), fp, mDNSfalse)) break; + if (gMDNSPlatformPosixVerboseLevel > 1) + fprintf(stderr, "Text string: \"%s\"\n", rawText); + len = strlen(rawText); + if (len <= 255) + { + unsigned int newlen = textLen + 1 + len; + if (len == 0 || newlen >= sizeof(text)) break; + text[textLen] = len; + mDNSPlatformMemCopy(text + textLen + 1, rawText, len); + textLen = newlen; + } + else + fprintf(stderr, "%s: TXT attribute too long for name = %s, type = %s, port = %s\n", + gProgramName, name, type, port); + } + + status = RegisterOneService(name, type, dom, text, textLen, atol(port)); + if (status != mStatus_NoError) { + // print error, but try to read and register other services in the file + fprintf(stderr, "%s: Failed to register service, name \"%s\", type \"%s\", domain \"%s\", port %s\n", + gProgramName, name, type, dom, port); + } + + } while (!feof(fp)); + + if (!feof(fp)) { + fprintf(stderr, "%s: Error reading service file %s\n", gProgramName, filePath); + status = mStatus_UnknownErr; } - - if (fp != NULL) { - junk = fclose(fp); - assert(junk == 0); - } - + + assert(0 == fclose(fp)); + return status; } static mStatus RegisterOurServices(void) { mStatus status; - + status = mStatus_NoError; if (gServiceName[0] != 0) { - status = RegisterOneService(gServiceName, - gServiceType, - gServiceDomain, - gServiceText, gServiceTextLen, + status = RegisterOneService(gServiceName, + gServiceType, + gServiceDomain, + gServiceText, gServiceTextLen, gPortNumber); } if (status == mStatus_NoError && gServiceFile[0] != 0) { @@ -594,19 +630,19 @@ static void DeregisterOurServices(void) { PosixService *thisServ; int thisServID; - + while (gServiceList != NULL) { thisServ = gServiceList; gServiceList = thisServ->next; thisServID = thisServ->serviceID; - + mDNS_DeregisterService(&mDNSStorage, &thisServ->coreServ); if (gMDNSPlatformPosixVerboseLevel > 0) { - fprintf(stderr, + fprintf(stderr, "%s: Deregistered service %d\n", - gProgramName, + gProgramName, thisServ->serviceID); } } @@ -619,27 +655,27 @@ static void DeregisterOurServices(void) int main(int argc, char **argv) { mStatus status; - int result; + int result; // Parse our command line arguments. This won't come back if there's an error. - + ParseArguments(argc, argv); // If we're told to run as a daemon, then do that straight away. - // Note that we don't treat the inability to create our PID - // file as an error. Also note that we assign getpid to a long + // Note that we don't treat the inability to create our PID + // file as an error. Also note that we assign getpid to a long // because printf has no format specified for pid_t. - + if (gDaemon) { - int result; + int result; if (gMDNSPlatformPosixVerboseLevel > 0) { fprintf(stderr, "%s: Starting in daemon mode\n", gProgramName); } result = daemon(0,0); if (result == 0) { FILE *fp; - int junk; - + int junk; + fp = fopen(gPIDFile, "w"); if (fp != NULL) { fprintf(fp, "%ld\n", (long) getpid()); @@ -657,87 +693,87 @@ int main(int argc, char **argv) } status = mDNS_Init(&mDNSStorage, &PlatformStorage, - mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, - mDNS_Init_AdvertiseLocalAddresses, - mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); + mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, + mDNS_Init_AdvertiseLocalAddresses, + mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); if (status != mStatus_NoError) return(2); - status = RegisterOurServices(); + status = RegisterOurServices(); if (status != mStatus_NoError) return(2); - + signal(SIGHUP, HandleSigHup); // SIGHUP has to be sent by kill -HUP signal(SIGINT, HandleSigInt); // SIGINT is what you get for a Ctrl-C signal(SIGQUIT, HandleSigQuit); // SIGQUIT is what you get for a Ctrl-\ (indeed) signal(SIGUSR1, HandleSigUsr1); // SIGUSR1 has to be sent by kill -USR1 - while (!gStopNow) - { - int nfds = 0; - fd_set readfds; - struct timeval timeout; - int result; - - // 1. Set up the fd_set as usual here. - // This example client has no file descriptors of its own, - // but a real application would call FD_SET to add them to the set here - FD_ZERO(&readfds); - - // 2. Set up the timeout. - // This example client has no other work it needs to be doing, - // so we set an effectively infinite timeout - timeout.tv_sec = 0x3FFFFFFF; - timeout.tv_usec = 0; - - // 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout - mDNSPosixGetFDSet(&mDNSStorage, &nfds, &readfds, &timeout); - - // 4. Call select as normal - verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec); - result = select(nfds, &readfds, NULL, NULL, &timeout); - - if (result < 0) - { - verbosedebugf("select() returned %d errno %d", result, errno); - if (errno != EINTR) gStopNow = mDNStrue; - else - { - if (gReceivedSigUsr1) - { - gReceivedSigUsr1 = mDNSfalse; - gMDNSPlatformPosixVerboseLevel += 1; - if (gMDNSPlatformPosixVerboseLevel > 2) - gMDNSPlatformPosixVerboseLevel = 0; - if ( gMDNSPlatformPosixVerboseLevel > 0 ) - fprintf(stderr, "\nVerbose level %d\n", gMDNSPlatformPosixVerboseLevel); - } - if (gReceivedSigHup) - { - if (gMDNSPlatformPosixVerboseLevel > 0) - fprintf(stderr, "\nSIGHUP\n"); - gReceivedSigHup = mDNSfalse; - DeregisterOurServices(); - status = mDNSPlatformPosixRefreshInterfaceList(&mDNSStorage); - if (status != mStatus_NoError) break; - status = RegisterOurServices(); - if (status != mStatus_NoError) break; - } - } - } - else - { - // 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work - mDNSPosixProcessFDSet(&mDNSStorage, &readfds); - - // 6. This example client has no other work it needs to be doing, - // but a real client would do its work here - // ... (do work) ... - } - } - - debugf("Exiting"); - - DeregisterOurServices(); - mDNS_Close(&mDNSStorage); + while (!gStopNow) + { + int nfds = 0; + fd_set readfds; + struct timeval timeout; + int result; + + // 1. Set up the fd_set as usual here. + // This example client has no file descriptors of its own, + // but a real application would call FD_SET to add them to the set here + FD_ZERO(&readfds); + + // 2. Set up the timeout. + // This example client has no other work it needs to be doing, + // so we set an effectively infinite timeout + timeout.tv_sec = 0x3FFFFFFF; + timeout.tv_usec = 0; + + // 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout + mDNSPosixGetFDSet(&mDNSStorage, &nfds, &readfds, &timeout); + + // 4. Call select as normal + verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec); + result = select(nfds, &readfds, NULL, NULL, &timeout); + + if (result < 0) + { + verbosedebugf("select() returned %d errno %d", result, errno); + if (errno != EINTR) gStopNow = mDNStrue; + else + { + if (gReceivedSigUsr1) + { + gReceivedSigUsr1 = mDNSfalse; + gMDNSPlatformPosixVerboseLevel += 1; + if (gMDNSPlatformPosixVerboseLevel > 2) + gMDNSPlatformPosixVerboseLevel = 0; + if ( gMDNSPlatformPosixVerboseLevel > 0 ) + fprintf(stderr, "\nVerbose level %d\n", gMDNSPlatformPosixVerboseLevel); + } + if (gReceivedSigHup) + { + if (gMDNSPlatformPosixVerboseLevel > 0) + fprintf(stderr, "\nSIGHUP\n"); + gReceivedSigHup = mDNSfalse; + DeregisterOurServices(); + status = mDNSPlatformPosixRefreshInterfaceList(&mDNSStorage); + if (status != mStatus_NoError) break; + status = RegisterOurServices(); + if (status != mStatus_NoError) break; + } + } + } + else + { + // 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work + mDNSPosixProcessFDSet(&mDNSStorage, &readfds); + + // 6. This example client has no other work it needs to be doing, + // but a real client would do its work here + // ... (do work) ... + } + } + + debugf("Exiting"); + + DeregisterOurServices(); + mDNS_Close(&mDNSStorage); if (status == mStatus_NoError) { result = 0; @@ -747,6 +783,6 @@ int main(int argc, char **argv) if ( (result != 0) || (gMDNSPlatformPosixVerboseLevel > 0) ) { fprintf(stderr, "%s: Finished with status %d, result %d\n", gProgramName, (int)status, result); } - + return result; } diff --git a/mDNSPosix/Services.txt b/mDNSPosix/Services.txt index f5870bb..f8d6978 100755 --- a/mDNSPosix/Services.txt +++ b/mDNSPosix/Services.txt @@ -1,15 +1,36 @@ -Tweedlebug +# +# Example services file parsed by mDNSResponderPosix. +# +# Lines beginning with '#' are comments/ignored. +# Blank lines indicate the end of a service record specification. +# The first character of the service name can be a '#' if you escape it with +# backslash to distinguish if from a comment line. +# ie, "\#serviceName" will be registered as "#serviceName". +# Note that any line beginning with white space is considered a blank line. +# +# The record format is: +# +# +# . +# +# +# +# +# +# Examples shown below. + +serviceName1 _afpovertcp._tcp. 548 name=val1 -Tweedlebug2 +serviceName2 _afpovertcp._tcp. local. 548 name=val2 name2=anotherattribute -Tweedlebug3 +serviceName3 _afpovertcp._tcp. 548 name=val3 diff --git a/mDNSPosix/mDNSPosix.c b/mDNSPosix/mDNSPosix.c index 8ff2978..d2869d4 100755 --- a/mDNSPosix/mDNSPosix.c +++ b/mDNSPosix/mDNSPosix.c @@ -5,33 +5,23 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * - * Formatting notes: - * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion - * on C indentation can be found on the web, such as , - * but for the sake of brevity here I will say just this: Curly braces are not syntactially - * part of an "if" statement; they are the beginning and ending markers of a compound statement; - * therefore common sense dictates that if they are part of a compound statement then they - * should be indented to the same level as everything else in that compound statement. - * Indenting curly braces at the same level as the "if" implies that curly braces are - * part of the "if", which is false. (This is as misleading as people who write "char* x,y;" - * thinking that variables x and y are both of type "char*" -- and anyone who doesn't - * understand why variable y is not of type "char*" just proves the point that poor code - * layout leads people to unfortunate misunderstandings about how the C language really works.) */ #include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above #include "DNSCommon.h" -#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform +#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform #include "dns_sd.h" +#include "dnssec.h" +#include "nsec.h" #include #include @@ -66,30 +56,30 @@ // *************************************************************************** // Structures -// 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 PosixEventSource - { - mDNSPosixEventCallback Callback; - void *Context; - int fd; - struct PosixEventSource *Next; - }; -typedef struct PosixEventSource PosixEventSource; +{ + mDNSPosixEventCallback Callback; + void *Context; + int fd; + struct PosixEventSource *Next; +}; +typedef struct PosixEventSource PosixEventSource; // Context record for interface change callback struct IfChangeRec - { - int NotifySD; - mDNS *mDNS; - }; -typedef struct IfChangeRec IfChangeRec; +{ + int NotifySD; + mDNS *mDNS; +}; +typedef struct IfChangeRec IfChangeRec; // Note that static data is initialized to zero in (modern) C. -static fd_set gEventFDs; -static int gMaxFD; // largest fd in gEventFDs -static GenLinkedList gEventSources; // linked list of PosixEventSource's -static sigset_t gEventSignalSet; // Signals which event loop listens for -static sigset_t gEventSignals; // Signals which were received while inside loop +static fd_set gEventFDs; +static int gMaxFD; // largest fd in gEventFDs +static GenLinkedList gEventSources; // linked list of PosixEventSource's +static sigset_t gEventSignalSet; // Signals which event loop listens for +static sigset_t gEventSignals; // Signals which were received while inside loop // *************************************************************************** // Globals (for debugging) @@ -106,39 +96,39 @@ int gMDNSPlatformPosixVerboseLevel = 0; #define PosixErrorToStatus(errNum) ((errNum) == 0 ? mStatus_NoError : mStatus_UnknownErr) mDNSlocal void SockAddrTomDNSAddr(const struct sockaddr *const sa, mDNSAddr *ipAddr, mDNSIPPort *ipPort) - { - switch (sa->sa_family) - { - case AF_INET: - { - struct sockaddr_in *sin = (struct sockaddr_in*)sa; - ipAddr->type = mDNSAddrType_IPv4; - ipAddr->ip.v4.NotAnInteger = sin->sin_addr.s_addr; - if (ipPort) ipPort->NotAnInteger = sin->sin_port; - break; - } +{ + switch (sa->sa_family) + { + case AF_INET: + { + struct sockaddr_in *sin = (struct sockaddr_in*)sa; + ipAddr->type = mDNSAddrType_IPv4; + ipAddr->ip.v4.NotAnInteger = sin->sin_addr.s_addr; + if (ipPort) ipPort->NotAnInteger = sin->sin_port; + break; + } #if HAVE_IPV6 - case AF_INET6: - { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)sa; + case AF_INET6: + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)sa; #ifndef NOT_HAVE_SA_LEN - assert(sin6->sin6_len == sizeof(*sin6)); + assert(sin6->sin6_len == sizeof(*sin6)); #endif - ipAddr->type = mDNSAddrType_IPv6; - ipAddr->ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr; - if (ipPort) ipPort->NotAnInteger = sin6->sin6_port; - break; - } + ipAddr->type = mDNSAddrType_IPv6; + ipAddr->ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr; + if (ipPort) ipPort->NotAnInteger = sin6->sin6_port; + break; + } #endif - default: - verbosedebugf("SockAddrTomDNSAddr: Uknown address family %d\n", sa->sa_family); - ipAddr->type = mDNSAddrType_None; - if (ipPort) ipPort->NotAnInteger = 0; - break; - } - } + default: + verbosedebugf("SockAddrTomDNSAddr: Uknown address family %d\n", sa->sa_family); + ipAddr->type = mDNSAddrType_None; + if (ipPort) ipPort->NotAnInteger = 0; + break; + } +} #if COMPILER_LIKES_PRAGMA_MARK #pragma mark ***** Send and Receive @@ -146,281 +136,284 @@ mDNSlocal void SockAddrTomDNSAddr(const struct sockaddr *const sa, mDNSAddr *ipA // mDNS core calls this routine when it needs to send a packet. mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end, - mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, mDNSIPPort dstPort) - { - int err = 0; - struct sockaddr_storage to; - PosixNetworkInterface * thisIntf = (PosixNetworkInterface *)(InterfaceID); - int sendingsocket = -1; - - (void)src; // Will need to use this parameter once we implement mDNSPlatformUDPSocket/mDNSPlatformUDPClose - - assert(m != NULL); - assert(msg != NULL); - assert(end != NULL); - assert((((char *) end) - ((char *) msg)) > 0); - - if (dstPort.NotAnInteger == 0) - { - LogMsg("mDNSPlatformSendUDP: Invalid argument -dstPort is set to 0"); - return PosixErrorToStatus(EINVAL); - } - if (dst->type == mDNSAddrType_IPv4) - { - struct sockaddr_in *sin = (struct sockaddr_in*)&to; + mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, + mDNSIPPort dstPort, mDNSBool useBackgroundTrafficClass) +{ + int err = 0; + struct sockaddr_storage to; + PosixNetworkInterface * thisIntf = (PosixNetworkInterface *)(InterfaceID); + int sendingsocket = -1; + + (void)src; // Will need to use this parameter once we implement mDNSPlatformUDPSocket/mDNSPlatformUDPClose + (void) useBackgroundTrafficClass; + + assert(m != NULL); + assert(msg != NULL); + assert(end != NULL); + assert((((char *) end) - ((char *) msg)) > 0); + + if (dstPort.NotAnInteger == 0) + { + LogMsg("mDNSPlatformSendUDP: Invalid argument -dstPort is set to 0"); + return PosixErrorToStatus(EINVAL); + } + if (dst->type == mDNSAddrType_IPv4) + { + struct sockaddr_in *sin = (struct sockaddr_in*)&to; #ifndef NOT_HAVE_SA_LEN - sin->sin_len = sizeof(*sin); + sin->sin_len = sizeof(*sin); #endif - sin->sin_family = AF_INET; - sin->sin_port = dstPort.NotAnInteger; - sin->sin_addr.s_addr = dst->ip.v4.NotAnInteger; - sendingsocket = thisIntf ? thisIntf->multicastSocket4 : m->p->unicastSocket4; - } + sin->sin_family = AF_INET; + sin->sin_port = dstPort.NotAnInteger; + sin->sin_addr.s_addr = dst->ip.v4.NotAnInteger; + sendingsocket = thisIntf ? thisIntf->multicastSocket4 : m->p->unicastSocket4; + } #if HAVE_IPV6 - else if (dst->type == mDNSAddrType_IPv6) - { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&to; - mDNSPlatformMemZero(sin6, sizeof(*sin6)); + else if (dst->type == mDNSAddrType_IPv6) + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&to; + mDNSPlatformMemZero(sin6, sizeof(*sin6)); #ifndef NOT_HAVE_SA_LEN - sin6->sin6_len = sizeof(*sin6); + sin6->sin6_len = sizeof(*sin6); #endif - sin6->sin6_family = AF_INET6; - sin6->sin6_port = dstPort.NotAnInteger; - sin6->sin6_addr = *(struct in6_addr*)&dst->ip.v6; - sendingsocket = thisIntf ? thisIntf->multicastSocket6 : m->p->unicastSocket6; - } + sin6->sin6_family = AF_INET6; + sin6->sin6_port = dstPort.NotAnInteger; + sin6->sin6_addr = *(struct in6_addr*)&dst->ip.v6; + sendingsocket = thisIntf ? thisIntf->multicastSocket6 : m->p->unicastSocket6; + } #endif - if (sendingsocket >= 0) - err = sendto(sendingsocket, msg, (char*)end - (char*)msg, 0, (struct sockaddr *)&to, GET_SA_LEN(to)); + if (sendingsocket >= 0) + err = sendto(sendingsocket, msg, (char*)end - (char*)msg, 0, (struct sockaddr *)&to, GET_SA_LEN(to)); - if (err > 0) err = 0; - else if (err < 0) - { - static int MessageCount = 0; + if (err > 0) err = 0; + else if (err < 0) + { + static int MessageCount = 0; // Don't report EHOSTDOWN (i.e. ARP failure), ENETDOWN, or no route to host for unicast destinations - if (!mDNSAddressIsAllDNSLinkGroup(dst)) - if (errno == EHOSTDOWN || errno == ENETDOWN || errno == EHOSTUNREACH || errno == ENETUNREACH) return(mStatus_TransientErr); - - if (MessageCount < 1000) - { - MessageCount++; - if (thisIntf) - LogMsg("mDNSPlatformSendUDP got error %d (%s) sending packet to %#a on interface %#a/%s/%d", - errno, strerror(errno), dst, &thisIntf->coreIntf.ip, thisIntf->intfName, thisIntf->index); - else - LogMsg("mDNSPlatformSendUDP got error %d (%s) sending packet to %#a", errno, strerror(errno), dst); - } - } - - return PosixErrorToStatus(err); - } + if (!mDNSAddressIsAllDNSLinkGroup(dst)) + if (errno == EHOSTDOWN || errno == ENETDOWN || errno == EHOSTUNREACH || errno == ENETUNREACH) return(mStatus_TransientErr); + + if (MessageCount < 1000) + { + MessageCount++; + if (thisIntf) + LogMsg("mDNSPlatformSendUDP got error %d (%s) sending packet to %#a on interface %#a/%s/%d", + errno, strerror(errno), dst, &thisIntf->coreIntf.ip, thisIntf->intfName, thisIntf->index); + else + LogMsg("mDNSPlatformSendUDP got error %d (%s) sending packet to %#a", errno, strerror(errno), dst); + } + } + + return PosixErrorToStatus(err); +} // This routine is called when the main loop detects that data is available on a socket. mDNSlocal void SocketDataReady(mDNS *const m, PosixNetworkInterface *intf, int skt) - { - mDNSAddr senderAddr, destAddr; - mDNSIPPort senderPort; - ssize_t packetLen; - DNSMessage packet; - struct my_in_pktinfo packetInfo; - struct sockaddr_storage from; - socklen_t fromLen; - int flags; - mDNSu8 ttl; - mDNSBool reject; - const mDNSInterfaceID InterfaceID = intf ? intf->coreIntf.InterfaceID : NULL; - - assert(m != NULL); - assert(skt >= 0); - - fromLen = sizeof(from); - flags = 0; - packetLen = recvfrom_flags(skt, &packet, sizeof(packet), &flags, (struct sockaddr *) &from, &fromLen, &packetInfo, &ttl); - - if (packetLen >= 0) - { - SockAddrTomDNSAddr((struct sockaddr*)&from, &senderAddr, &senderPort); - SockAddrTomDNSAddr((struct sockaddr*)&packetInfo.ipi_addr, &destAddr, NULL); - - // If we have broken IP_RECVDSTADDR functionality (so far - // I've only seen this on OpenBSD) then apply a hack to - // convince mDNS Core that this isn't a spoof packet. - // Basically what we do is check to see whether the - // packet arrived as a multicast and, if so, set its - // destAddr to the mDNS address. - // - // I must admit that I could just be doing something - // wrong on OpenBSD and hence triggering this problem - // but I'm at a loss as to how. - // - // If this platform doesn't have IP_PKTINFO or IP_RECVDSTADDR, then we have - // no way to tell the destination address or interface this packet arrived on, - // so all we can do is just assume it's a multicast - - #if HAVE_BROKEN_RECVDSTADDR || (!defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR)) - if ((destAddr.NotAnInteger == 0) && (flags & MSG_MCAST)) - { - destAddr.type = senderAddr.type; - if (senderAddr.type == mDNSAddrType_IPv4) destAddr.ip.v4 = AllDNSLinkGroup_v4.ip.v4; - else if (senderAddr.type == mDNSAddrType_IPv6) destAddr.ip.v6 = AllDNSLinkGroup_v6.ip.v6; - } - #endif - - // We only accept the packet if the interface on which it came - // in matches the interface associated with this socket. - // We do this match by name or by index, depending on which - // information is available. recvfrom_flags sets the name - // to "" if the name isn't available, or the index to -1 - // if the index is available. This accomodates the various - // different capabilities of our target platforms. - - reject = mDNSfalse; - if (!intf) - { - // Ignore multicasts accidentally delivered to our unicast receiving socket - if (mDNSAddrIsDNSMulticast(&destAddr)) packetLen = -1; - } - else - { - if (packetInfo.ipi_ifname[0] != 0) reject = (strcmp(packetInfo.ipi_ifname, intf->intfName) != 0); - else if (packetInfo.ipi_ifindex != -1) reject = (packetInfo.ipi_ifindex != intf->index); - - if (reject) - { - verbosedebugf("SocketDataReady ignored a packet from %#a to %#a on interface %s/%d expecting %#a/%s/%d/%d", - &senderAddr, &destAddr, packetInfo.ipi_ifname, packetInfo.ipi_ifindex, - &intf->coreIntf.ip, intf->intfName, intf->index, skt); - packetLen = -1; - num_pkts_rejected++; - if (num_pkts_rejected > (num_pkts_accepted + 1) * (num_registered_interfaces + 1) * 2) - { - fprintf(stderr, - "*** WARNING: Received %d packets; Accepted %d packets; Rejected %d packets because of interface mismatch\n", - num_pkts_accepted + num_pkts_rejected, num_pkts_accepted, num_pkts_rejected); - num_pkts_accepted = 0; - num_pkts_rejected = 0; - } - } - else - { - verbosedebugf("SocketDataReady got a packet from %#a to %#a on interface %#a/%s/%d/%d", - &senderAddr, &destAddr, &intf->coreIntf.ip, intf->intfName, intf->index, skt); - num_pkts_accepted++; - } - } - } - - if (packetLen >= 0) - mDNSCoreReceive(m, &packet, (mDNSu8 *)&packet + packetLen, - &senderAddr, senderPort, &destAddr, MulticastDNSPort, InterfaceID); - } - -mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS * const m, TCPSocketFlags flags, mDNSIPPort * port) - { - (void)m; // Unused - (void)flags; // Unused - (void)port; // Unused - return NULL; - } +{ + mDNSAddr senderAddr, destAddr; + mDNSIPPort senderPort; + ssize_t packetLen; + DNSMessage packet; + struct my_in_pktinfo packetInfo; + struct sockaddr_storage from; + socklen_t fromLen; + int flags; + mDNSu8 ttl; + mDNSBool reject; + const mDNSInterfaceID InterfaceID = intf ? intf->coreIntf.InterfaceID : NULL; + + assert(m != NULL); + assert(skt >= 0); + + fromLen = sizeof(from); + flags = 0; + packetLen = recvfrom_flags(skt, &packet, sizeof(packet), &flags, (struct sockaddr *) &from, &fromLen, &packetInfo, &ttl); + + if (packetLen >= 0) + { + SockAddrTomDNSAddr((struct sockaddr*)&from, &senderAddr, &senderPort); + SockAddrTomDNSAddr((struct sockaddr*)&packetInfo.ipi_addr, &destAddr, NULL); + + // If we have broken IP_RECVDSTADDR functionality (so far + // I've only seen this on OpenBSD) then apply a hack to + // convince mDNS Core that this isn't a spoof packet. + // Basically what we do is check to see whether the + // packet arrived as a multicast and, if so, set its + // destAddr to the mDNS address. + // + // I must admit that I could just be doing something + // wrong on OpenBSD and hence triggering this problem + // but I'm at a loss as to how. + // + // If this platform doesn't have IP_PKTINFO or IP_RECVDSTADDR, then we have + // no way to tell the destination address or interface this packet arrived on, + // so all we can do is just assume it's a multicast + + #if HAVE_BROKEN_RECVDSTADDR || (!defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR)) + if ((destAddr.NotAnInteger == 0) && (flags & MSG_MCAST)) + { + destAddr.type = senderAddr.type; + if (senderAddr.type == mDNSAddrType_IPv4) destAddr.ip.v4 = AllDNSLinkGroup_v4.ip.v4; + else if (senderAddr.type == mDNSAddrType_IPv6) destAddr.ip.v6 = AllDNSLinkGroup_v6.ip.v6; + } + #endif + + // We only accept the packet if the interface on which it came + // in matches the interface associated with this socket. + // We do this match by name or by index, depending on which + // information is available. recvfrom_flags sets the name + // to "" if the name isn't available, or the index to -1 + // if the index is available. This accomodates the various + // different capabilities of our target platforms. + + reject = mDNSfalse; + if (!intf) + { + // Ignore multicasts accidentally delivered to our unicast receiving socket + if (mDNSAddrIsDNSMulticast(&destAddr)) packetLen = -1; + } + else + { + if (packetInfo.ipi_ifname[0] != 0) reject = (strcmp(packetInfo.ipi_ifname, intf->intfName) != 0); + else if (packetInfo.ipi_ifindex != -1) reject = (packetInfo.ipi_ifindex != intf->index); + + if (reject) + { + verbosedebugf("SocketDataReady ignored a packet from %#a to %#a on interface %s/%d expecting %#a/%s/%d/%d", + &senderAddr, &destAddr, packetInfo.ipi_ifname, packetInfo.ipi_ifindex, + &intf->coreIntf.ip, intf->intfName, intf->index, skt); + packetLen = -1; + num_pkts_rejected++; + if (num_pkts_rejected > (num_pkts_accepted + 1) * (num_registered_interfaces + 1) * 2) + { + fprintf(stderr, + "*** WARNING: Received %d packets; Accepted %d packets; Rejected %d packets because of interface mismatch\n", + num_pkts_accepted + num_pkts_rejected, num_pkts_accepted, num_pkts_rejected); + num_pkts_accepted = 0; + num_pkts_rejected = 0; + } + } + else + { + verbosedebugf("SocketDataReady got a packet from %#a to %#a on interface %#a/%s/%d/%d", + &senderAddr, &destAddr, &intf->coreIntf.ip, intf->intfName, intf->index, skt); + num_pkts_accepted++; + } + } + } + + if (packetLen >= 0) + mDNSCoreReceive(m, &packet, (mDNSu8 *)&packet + packetLen, + &senderAddr, senderPort, &destAddr, MulticastDNSPort, InterfaceID); +} + +mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS * const m, TCPSocketFlags flags, mDNSIPPort * port, mDNSBool useBackgroundTrafficClass) +{ + (void)m; // Unused + (void)flags; // Unused + (void)port; // Unused + (void)useBackgroundTrafficClass; // Unused + return NULL; +} mDNSexport TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int sd) - { - (void)flags; // Unused - (void)sd; // Unused - return NULL; - } +{ + (void)flags; // Unused + (void)sd; // Unused + return NULL; +} mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock) - { - (void)sock; // Unused - return -1; - } +{ + (void)sock; // Unused + return -1; +} mDNSexport mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, domainname *hostname, mDNSInterfaceID InterfaceID, - TCPConnectionCallback callback, void *context) - { - (void)sock; // Unused - (void)dst; // Unused - (void)dstport; // Unused - (void)hostname; // Unused - (void)InterfaceID; // Unused - (void)callback; // Unused - (void)context; // Unused - return(mStatus_UnsupportedErr); - } + TCPConnectionCallback callback, void *context) +{ + (void)sock; // Unused + (void)dst; // Unused + (void)dstport; // Unused + (void)hostname; // Unused + (void)InterfaceID; // Unused + (void)callback; // Unused + (void)context; // Unused + return(mStatus_UnsupportedErr); +} mDNSexport void mDNSPlatformTCPCloseConnection(TCPSocket *sock) - { - (void)sock; // Unused - } +{ + (void)sock; // Unused +} mDNSexport long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool * closed) - { - (void)sock; // Unused - (void)buf; // Unused - (void)buflen; // Unused - (void)closed; // Unused - return 0; - } +{ + (void)sock; // Unused + (void)buf; // Unused + (void)buflen; // Unused + (void)closed; // Unused + return 0; +} mDNSexport long mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len) - { - (void)sock; // Unused - (void)msg; // Unused - (void)len; // Unused - return 0; - } +{ + (void)sock; // Unused + (void)msg; // Unused + (void)len; // Unused + return 0; +} mDNSexport UDPSocket *mDNSPlatformUDPSocket(mDNS * const m, mDNSIPPort port) - { - (void)m; // Unused - (void)port; // Unused - return NULL; - } +{ + (void)m; // Unused + (void)port; // Unused + return NULL; +} mDNSexport void mDNSPlatformUDPClose(UDPSocket *sock) - { - (void)sock; // Unused - } - +{ + (void)sock; // Unused +} + mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID) - { - (void)m; // Unused - (void)InterfaceID; // Unused - } +{ + (void)m; // Unused + (void)InterfaceID; // Unused +} mDNSexport void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID) - { - (void)msg; // Unused - (void)end; // Unused - (void)InterfaceID; // Unused - } - +{ + (void)msg; // Unused + (void)end; // Unused + (void)InterfaceID; // Unused +} + mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID) - { - (void)m; // Unused - (void)tpa; // Unused - (void)tha; // Unused - (void)InterfaceID; // Unused - } +{ + (void)m; // Unused + (void)tpa; // Unused + (void)tha; // Unused + (void)InterfaceID; // Unused +} mDNSexport mStatus mDNSPlatformTLSSetupCerts(void) - { - return(mStatus_UnsupportedErr); - } - +{ + return(mStatus_UnsupportedErr); +} + mDNSexport void mDNSPlatformTLSTearDownCerts(void) - { - } +{ +} mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason) - { - (void) m; - (void) allowSleep; - (void) reason; - } +{ + (void) m; + (void) allowSleep; + (void) reason; +} #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -428,11 +421,11 @@ mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, co #endif mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result) - { +{ (void)m; // unused - (void)rr; - (void)result; - } + (void)rr; + (void)result; +} #if COMPILER_LIKES_PRAGMA_MARK @@ -440,30 +433,30 @@ mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result #endif mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **RegDomains, DNameListElem **BrowseDomains) - { - (void) m; - (void) setservers; - (void) fqdn; - (void) setsearch; - (void) RegDomains; - (void) BrowseDomains; - } +{ + (void) m; + (void) setservers; + (void) fqdn; + (void) setsearch; + (void) RegDomains; + (void) BrowseDomains; +} mDNSexport mStatus mDNSPlatformGetPrimaryInterface(mDNS * const m, mDNSAddr * v4, mDNSAddr * v6, mDNSAddr * router) - { - (void) m; - (void) v4; - (void) v6; - (void) router; +{ + (void) m; + (void) v4; + (void) v6; + (void) router; - return mStatus_UnsupportedErr; - } + return mStatus_UnsupportedErr; +} mDNSexport void mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status) - { - (void) dname; - (void) status; - } +{ + (void) dname; + (void) status; +} #if COMPILER_LIKES_PRAGMA_MARK #pragma mark ***** Init and Term @@ -471,497 +464,498 @@ mDNSexport void mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const // This gets the current hostname, truncating it at the first dot if necessary mDNSlocal void GetUserSpecifiedRFC1034ComputerName(domainlabel *const namelabel) - { - int len = 0; - gethostname((char *)(&namelabel->c[1]), MAX_DOMAIN_LABEL); - while (len < MAX_DOMAIN_LABEL && namelabel->c[len+1] && namelabel->c[len+1] != '.') len++; - namelabel->c[0] = len; - } +{ + int len = 0; + gethostname((char *)(&namelabel->c[1]), MAX_DOMAIN_LABEL); + while (len < MAX_DOMAIN_LABEL && namelabel->c[len+1] && namelabel->c[len+1] != '.') len++; + namelabel->c[0] = len; +} // On OS X this gets the text of the field labelled "Computer Name" in the Sharing Prefs Control Panel // Other platforms can either get the information from the appropriate place, // or they can alternatively just require all registering services to provide an explicit name mDNSlocal void GetUserSpecifiedFriendlyComputerName(domainlabel *const namelabel) - { - // On Unix we have no better name than the host name, so we just use that. - GetUserSpecifiedRFC1034ComputerName(namelabel); - } +{ + // On Unix we have no better name than the host name, so we just use that. + GetUserSpecifiedRFC1034ComputerName(namelabel); +} mDNSexport int ParseDNSServers(mDNS *m, const char *filePath) - { - 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 (strncasecmp(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, NULL, mDNSInterface_Any, &DNSAddr, UnicastDNSPort, mDNSfalse, 0, mDNSfalse); - numOfServers++; - } - } - return (numOfServers > 0) ? 0 : -1; - } +{ + char line[256]; + char nameserver[16]; + char keyword[11]; + 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 (strncasecmp(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, NULL, mDNSInterface_Any, &DNSAddr, UnicastDNSPort, mDNSfalse, 0, mDNSfalse, 0); + numOfServers++; + } + } + return (numOfServers > 0) ? 0 : -1; +} // Searches the interface list looking for the named interface. // Returns a pointer to if it found, or NULL otherwise. mDNSlocal PosixNetworkInterface *SearchForInterfaceByName(mDNS *const m, const char *intfName) - { - PosixNetworkInterface *intf; +{ + PosixNetworkInterface *intf; - assert(m != NULL); - assert(intfName != NULL); + assert(m != NULL); + assert(intfName != NULL); - intf = (PosixNetworkInterface*)(m->HostInterfaces); - while ((intf != NULL) && (strcmp(intf->intfName, intfName) != 0)) - intf = (PosixNetworkInterface *)(intf->coreIntf.next); + intf = (PosixNetworkInterface*)(m->HostInterfaces); + while ((intf != NULL) && (strcmp(intf->intfName, intfName) != 0)) + intf = (PosixNetworkInterface *)(intf->coreIntf.next); - return intf; - } + return intf; +} mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const m, mDNSu32 index) - { - PosixNetworkInterface *intf; +{ + PosixNetworkInterface *intf; - assert(m != NULL); + assert(m != NULL); - if (index == kDNSServiceInterfaceIndexLocalOnly) return(mDNSInterface_LocalOnly); - if (index == kDNSServiceInterfaceIndexP2P ) return(mDNSInterface_P2P); - if (index == kDNSServiceInterfaceIndexAny ) return(mDNSInterface_Any); + if (index == kDNSServiceInterfaceIndexLocalOnly) return(mDNSInterface_LocalOnly); + if (index == kDNSServiceInterfaceIndexP2P ) return(mDNSInterface_P2P); + if (index == kDNSServiceInterfaceIndexAny ) return(mDNSInterface_Any); - intf = (PosixNetworkInterface*)(m->HostInterfaces); - while ((intf != NULL) && (mDNSu32) intf->index != index) - intf = (PosixNetworkInterface *)(intf->coreIntf.next); + intf = (PosixNetworkInterface*)(m->HostInterfaces); + while ((intf != NULL) && (mDNSu32) intf->index != index) + intf = (PosixNetworkInterface *)(intf->coreIntf.next); + + return (mDNSInterfaceID) intf; +} - return (mDNSInterfaceID) intf; - } - mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id, mDNSBool suppressNetworkChange) - { - PosixNetworkInterface *intf; - (void) suppressNetworkChange; // Unused +{ + PosixNetworkInterface *intf; + (void) suppressNetworkChange; // Unused - assert(m != NULL); + assert(m != NULL); - if (id == mDNSInterface_LocalOnly) return(kDNSServiceInterfaceIndexLocalOnly); - if (id == mDNSInterface_P2P ) return(kDNSServiceInterfaceIndexP2P); - if (id == mDNSInterface_Any ) return(kDNSServiceInterfaceIndexAny); + if (id == mDNSInterface_LocalOnly) return(kDNSServiceInterfaceIndexLocalOnly); + if (id == mDNSInterface_P2P ) return(kDNSServiceInterfaceIndexP2P); + if (id == mDNSInterface_Any ) return(kDNSServiceInterfaceIndexAny); - intf = (PosixNetworkInterface*)(m->HostInterfaces); - while ((intf != NULL) && (mDNSInterfaceID) intf != id) - intf = (PosixNetworkInterface *)(intf->coreIntf.next); + intf = (PosixNetworkInterface*)(m->HostInterfaces); + while ((intf != NULL) && (mDNSInterfaceID) intf != id) + intf = (PosixNetworkInterface *)(intf->coreIntf.next); - return intf ? intf->index : 0; - } + return intf ? intf->index : 0; +} // Frees the specified PosixNetworkInterface structure. The underlying // interface must have already been deregistered with the mDNS core. mDNSlocal void FreePosixNetworkInterface(PosixNetworkInterface *intf) - { - assert(intf != NULL); - if (intf->intfName != NULL) free((void *)intf->intfName); - if (intf->multicastSocket4 != -1) assert(close(intf->multicastSocket4) == 0); +{ + assert(intf != NULL); + if (intf->intfName != NULL) free((void *)intf->intfName); + if (intf->multicastSocket4 != -1) assert(close(intf->multicastSocket4) == 0); #if HAVE_IPV6 - if (intf->multicastSocket6 != -1) assert(close(intf->multicastSocket6) == 0); + if (intf->multicastSocket6 != -1) assert(close(intf->multicastSocket6) == 0); #endif - free(intf); - } + free(intf); +} // Grab the first interface, deregister it, free it, and repeat until done. mDNSlocal void ClearInterfaceList(mDNS *const m) - { - assert(m != NULL); - - while (m->HostInterfaces) - { - PosixNetworkInterface *intf = (PosixNetworkInterface*)(m->HostInterfaces); - mDNS_DeregisterInterface(m, &intf->coreIntf, mDNSfalse); - if (gMDNSPlatformPosixVerboseLevel > 0) fprintf(stderr, "Deregistered interface %s\n", intf->intfName); - FreePosixNetworkInterface(intf); - } - num_registered_interfaces = 0; - num_pkts_accepted = 0; - num_pkts_rejected = 0; - } +{ + assert(m != NULL); + + while (m->HostInterfaces) + { + PosixNetworkInterface *intf = (PosixNetworkInterface*)(m->HostInterfaces); + mDNS_DeregisterInterface(m, &intf->coreIntf, mDNSfalse); + if (gMDNSPlatformPosixVerboseLevel > 0) fprintf(stderr, "Deregistered interface %s\n", intf->intfName); + FreePosixNetworkInterface(intf); + } + num_registered_interfaces = 0; + num_pkts_accepted = 0; + num_pkts_rejected = 0; +} // Sets up a send/receive socket. // If mDNSIPPort port is non-zero, then it's a multicast socket on the specified interface // If mDNSIPPort port is zero, then it's a randomly assigned port number, used for sending unicast queries mDNSlocal int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interfaceIndex, int *sktPtr) - { - int err = 0; - static const int kOn = 1; - static const int kIntTwoFiveFive = 255; - static const unsigned char kByteTwoFiveFive = 255; - const mDNSBool JoinMulticastGroup = (port.NotAnInteger != 0); - - (void) interfaceIndex; // This parameter unused on plaforms that don't have IPv6 - assert(intfAddr != NULL); - assert(sktPtr != NULL); - assert(*sktPtr == -1); - - // Open the socket... - if (intfAddr->sa_family == AF_INET) *sktPtr = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); +{ + int err = 0; + static const int kOn = 1; + static const int kIntTwoFiveFive = 255; + static const unsigned char kByteTwoFiveFive = 255; + const mDNSBool JoinMulticastGroup = (port.NotAnInteger != 0); + + (void) interfaceIndex; // This parameter unused on plaforms that don't have IPv6 + assert(intfAddr != NULL); + assert(sktPtr != NULL); + assert(*sktPtr == -1); + + // Open the socket... + if (intfAddr->sa_family == AF_INET) *sktPtr = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); #if HAVE_IPV6 - else if (intfAddr->sa_family == AF_INET6) *sktPtr = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); + else if (intfAddr->sa_family == AF_INET6) *sktPtr = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); #endif - else return EINVAL; - - if (*sktPtr < 0) { err = errno; perror((intfAddr->sa_family == AF_INET) ? "socket AF_INET" : "socket AF_INET6"); } - - // ... with a shared UDP port, if it's for multicast receiving - if (err == 0 && port.NotAnInteger) - { - #if defined(SO_REUSEPORT) - err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEPORT, &kOn, sizeof(kOn)); - #elif defined(SO_REUSEADDR) - err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); - #else - #error This platform has no way to avoid address busy errors on multicast. - #endif - if (err < 0) { err = errno; perror("setsockopt - SO_REUSExxxx"); } - } - - // We want to receive destination addresses and interface identifiers. - if (intfAddr->sa_family == AF_INET) - { - struct ip_mreq imr; - struct sockaddr_in bindAddr; - if (err == 0) - { - #if defined(IP_PKTINFO) // Linux - err = setsockopt(*sktPtr, IPPROTO_IP, IP_PKTINFO, &kOn, sizeof(kOn)); - if (err < 0) { err = errno; perror("setsockopt - IP_PKTINFO"); } - #elif defined(IP_RECVDSTADDR) || defined(IP_RECVIF) // BSD and Solaris - #if defined(IP_RECVDSTADDR) - err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVDSTADDR, &kOn, sizeof(kOn)); - if (err < 0) { err = errno; perror("setsockopt - IP_RECVDSTADDR"); } - #endif - #if defined(IP_RECVIF) - if (err == 0) - { - err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVIF, &kOn, sizeof(kOn)); - if (err < 0) { err = errno; perror("setsockopt - IP_RECVIF"); } - } - #endif - #else - #warning This platform has no way to get the destination interface information -- will only work for single-homed hosts - #endif - } - #if defined(IP_RECVTTL) // Linux - if (err == 0) - { - setsockopt(*sktPtr, IPPROTO_IP, IP_RECVTTL, &kOn, sizeof(kOn)); - // We no longer depend on being able to get the received TTL, so don't worry if the option fails - } - #endif - - // Add multicast group membership on this interface - if (err == 0 && JoinMulticastGroup) - { - imr.imr_multiaddr.s_addr = AllDNSLinkGroup_v4.ip.v4.NotAnInteger; - imr.imr_interface = ((struct sockaddr_in*)intfAddr)->sin_addr; - err = setsockopt(*sktPtr, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)); - if (err < 0) { err = errno; perror("setsockopt - IP_ADD_MEMBERSHIP"); } - } - - // Specify outgoing interface too - if (err == 0 && JoinMulticastGroup) - { - err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_IF, &((struct sockaddr_in*)intfAddr)->sin_addr, sizeof(struct in_addr)); - if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_IF"); } - } - - // Per the mDNS spec, send unicast packets with TTL 255 - if (err == 0) - { - err = setsockopt(*sktPtr, IPPROTO_IP, IP_TTL, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); - if (err < 0) { err = errno; perror("setsockopt - IP_TTL"); } - } - - // and multicast packets with TTL 255 too - // There's some debate as to whether IP_MULTICAST_TTL is an int or a byte so we just try both. - if (err == 0) - { - err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_TTL, &kByteTwoFiveFive, sizeof(kByteTwoFiveFive)); - if (err < 0 && errno == EINVAL) - err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_TTL, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); - if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_TTL"); } - } - - // And start listening for packets - if (err == 0) - { - bindAddr.sin_family = AF_INET; - bindAddr.sin_port = port.NotAnInteger; - bindAddr.sin_addr.s_addr = INADDR_ANY; // Want to receive multicasts AND unicasts on this socket - err = bind(*sktPtr, (struct sockaddr *) &bindAddr, sizeof(bindAddr)); - if (err < 0) { err = errno; perror("bind"); fflush(stderr); } - } - } // endif (intfAddr->sa_family == AF_INET) + else return EINVAL; + + if (*sktPtr < 0) { err = errno; perror((intfAddr->sa_family == AF_INET) ? "socket AF_INET" : "socket AF_INET6"); } + + // ... with a shared UDP port, if it's for multicast receiving + if (err == 0 && port.NotAnInteger) + { + #if defined(SO_REUSEPORT) + err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEPORT, &kOn, sizeof(kOn)); + #elif defined(SO_REUSEADDR) + err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); + #else + #error This platform has no way to avoid address busy errors on multicast. + #endif + if (err < 0) { err = errno; perror("setsockopt - SO_REUSExxxx"); } + } + + // We want to receive destination addresses and interface identifiers. + if (intfAddr->sa_family == AF_INET) + { + struct ip_mreq imr; + struct sockaddr_in bindAddr; + if (err == 0) + { + #if defined(IP_PKTINFO) // Linux + err = setsockopt(*sktPtr, IPPROTO_IP, IP_PKTINFO, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IP_PKTINFO"); } + #elif defined(IP_RECVDSTADDR) || defined(IP_RECVIF) // BSD and Solaris + #if defined(IP_RECVDSTADDR) + err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVDSTADDR, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IP_RECVDSTADDR"); } + #endif + #if defined(IP_RECVIF) + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVIF, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IP_RECVIF"); } + } + #endif + #else + #warning This platform has no way to get the destination interface information -- will only work for single-homed hosts + #endif + } + #if defined(IP_RECVTTL) // Linux + if (err == 0) + { + setsockopt(*sktPtr, IPPROTO_IP, IP_RECVTTL, &kOn, sizeof(kOn)); + // We no longer depend on being able to get the received TTL, so don't worry if the option fails + } + #endif + + // Add multicast group membership on this interface + if (err == 0 && JoinMulticastGroup) + { + imr.imr_multiaddr.s_addr = AllDNSLinkGroup_v4.ip.v4.NotAnInteger; + imr.imr_interface = ((struct sockaddr_in*)intfAddr)->sin_addr; + err = setsockopt(*sktPtr, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)); + if (err < 0) { err = errno; perror("setsockopt - IP_ADD_MEMBERSHIP"); } + } + + // Specify outgoing interface too + if (err == 0 && JoinMulticastGroup) + { + err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_IF, &((struct sockaddr_in*)intfAddr)->sin_addr, sizeof(struct in_addr)); + if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_IF"); } + } + + // Per the mDNS spec, send unicast packets with TTL 255 + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IP, IP_TTL, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); + if (err < 0) { err = errno; perror("setsockopt - IP_TTL"); } + } + + // and multicast packets with TTL 255 too + // There's some debate as to whether IP_MULTICAST_TTL is an int or a byte so we just try both. + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_TTL, &kByteTwoFiveFive, sizeof(kByteTwoFiveFive)); + if (err < 0 && errno == EINVAL) + err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_TTL, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); + if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_TTL"); } + } + + // And start listening for packets + if (err == 0) + { + bindAddr.sin_family = AF_INET; + bindAddr.sin_port = port.NotAnInteger; + bindAddr.sin_addr.s_addr = INADDR_ANY; // Want to receive multicasts AND unicasts on this socket + err = bind(*sktPtr, (struct sockaddr *) &bindAddr, sizeof(bindAddr)); + if (err < 0) { err = errno; perror("bind"); fflush(stderr); } + } + } // endif (intfAddr->sa_family == AF_INET) #if HAVE_IPV6 - else if (intfAddr->sa_family == AF_INET6) - { - struct ipv6_mreq imr6; - struct sockaddr_in6 bindAddr6; - #if defined(IPV6_PKTINFO) - if (err == 0) - { - err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_PKTINFO, &kOn, sizeof(kOn)); - if (err < 0) { err = errno; perror("setsockopt - IPV6_PKTINFO"); } - } - #else - #warning This platform has no way to get the destination interface information for IPv6 -- will only work for single-homed hosts - #endif - #if defined(IPV6_HOPLIMIT) - if (err == 0) - { - err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_HOPLIMIT, &kOn, sizeof(kOn)); - if (err < 0) { err = errno; perror("setsockopt - IPV6_HOPLIMIT"); } - } - #endif - - // Add multicast group membership on this interface - if (err == 0 && JoinMulticastGroup) - { - imr6.ipv6mr_multiaddr = *(const struct in6_addr*)&AllDNSLinkGroup_v6.ip.v6; - imr6.ipv6mr_interface = interfaceIndex; - //LogMsg("Joining %.16a on %d", &imr6.ipv6mr_multiaddr, imr6.ipv6mr_interface); - err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_JOIN_GROUP, &imr6, sizeof(imr6)); - if (err < 0) - { - err = errno; - verbosedebugf("IPV6_JOIN_GROUP %.16a on %d failed.\n", &imr6.ipv6mr_multiaddr, imr6.ipv6mr_interface); - perror("setsockopt - IPV6_JOIN_GROUP"); - } - } - - // Specify outgoing interface too - if (err == 0 && JoinMulticastGroup) - { - u_int multicast_if = interfaceIndex; - err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_if, sizeof(multicast_if)); - if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_IF"); } - } - - // We want to receive only IPv6 packets on this socket. - // Without this option, we may get IPv4 addresses as mapped addresses. - if (err == 0) - { - err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_V6ONLY, &kOn, sizeof(kOn)); - if (err < 0) { err = errno; perror("setsockopt - IPV6_V6ONLY"); } - } - - // Per the mDNS spec, send unicast packets with TTL 255 - if (err == 0) - { - err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); - if (err < 0) { err = errno; perror("setsockopt - IPV6_UNICAST_HOPS"); } - } - - // and multicast packets with TTL 255 too - // There's some debate as to whether IPV6_MULTICAST_HOPS is an int or a byte so we just try both. - if (err == 0) - { - err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kByteTwoFiveFive, sizeof(kByteTwoFiveFive)); - if (err < 0 && errno == EINVAL) - err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); - if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_HOPS"); } - } - - // And start listening for packets - if (err == 0) - { - mDNSPlatformMemZero(&bindAddr6, sizeof(bindAddr6)); + else if (intfAddr->sa_family == AF_INET6) + { + struct ipv6_mreq imr6; + struct sockaddr_in6 bindAddr6; + #if defined(IPV6_PKTINFO) + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_2292_PKTINFO, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_PKTINFO"); } + } + #else + #warning This platform has no way to get the destination interface information for IPv6 -- will only work for single-homed hosts + #endif + #if defined(IPV6_HOPLIMIT) + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_2292_HOPLIMIT, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_HOPLIMIT"); } + } + #endif + + // Add multicast group membership on this interface + if (err == 0 && JoinMulticastGroup) + { + imr6.ipv6mr_multiaddr = *(const struct in6_addr*)&AllDNSLinkGroup_v6.ip.v6; + imr6.ipv6mr_interface = interfaceIndex; + //LogMsg("Joining %.16a on %d", &imr6.ipv6mr_multiaddr, imr6.ipv6mr_interface); + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_JOIN_GROUP, &imr6, sizeof(imr6)); + if (err < 0) + { + err = errno; + verbosedebugf("IPV6_JOIN_GROUP %.16a on %d failed.\n", &imr6.ipv6mr_multiaddr, imr6.ipv6mr_interface); + perror("setsockopt - IPV6_JOIN_GROUP"); + } + } + + // Specify outgoing interface too + if (err == 0 && JoinMulticastGroup) + { + u_int multicast_if = interfaceIndex; + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_if, sizeof(multicast_if)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_IF"); } + } + + // We want to receive only IPv6 packets on this socket. + // Without this option, we may get IPv4 addresses as mapped addresses. + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_V6ONLY, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_V6ONLY"); } + } + + // Per the mDNS spec, send unicast packets with TTL 255 + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_UNICAST_HOPS"); } + } + + // and multicast packets with TTL 255 too + // There's some debate as to whether IPV6_MULTICAST_HOPS is an int or a byte so we just try both. + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kByteTwoFiveFive, sizeof(kByteTwoFiveFive)); + if (err < 0 && errno == EINVAL) + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_HOPS"); } + } + + // And start listening for packets + if (err == 0) + { + mDNSPlatformMemZero(&bindAddr6, sizeof(bindAddr6)); #ifndef NOT_HAVE_SA_LEN - bindAddr6.sin6_len = sizeof(bindAddr6); + bindAddr6.sin6_len = sizeof(bindAddr6); #endif - bindAddr6.sin6_family = AF_INET6; - bindAddr6.sin6_port = port.NotAnInteger; - bindAddr6.sin6_flowinfo = 0; - bindAddr6.sin6_addr = in6addr_any; // Want to receive multicasts AND unicasts on this socket - bindAddr6.sin6_scope_id = 0; - err = bind(*sktPtr, (struct sockaddr *) &bindAddr6, sizeof(bindAddr6)); - if (err < 0) { err = errno; perror("bind"); fflush(stderr); } - } - } // endif (intfAddr->sa_family == AF_INET6) + bindAddr6.sin6_family = AF_INET6; + bindAddr6.sin6_port = port.NotAnInteger; + bindAddr6.sin6_flowinfo = 0; + bindAddr6.sin6_addr = in6addr_any; // Want to receive multicasts AND unicasts on this socket + bindAddr6.sin6_scope_id = 0; + err = bind(*sktPtr, (struct sockaddr *) &bindAddr6, sizeof(bindAddr6)); + if (err < 0) { err = errno; perror("bind"); fflush(stderr); } + } + } // endif (intfAddr->sa_family == AF_INET6) #endif - // Set the socket to non-blocking. - if (err == 0) - { - err = fcntl(*sktPtr, F_GETFL, 0); - if (err < 0) err = errno; - else - { - err = fcntl(*sktPtr, F_SETFL, err | O_NONBLOCK); - if (err < 0) err = errno; - } - } - - // Clean up - if (err != 0 && *sktPtr != -1) { assert(close(*sktPtr) == 0); *sktPtr = -1; } - assert((err == 0) == (*sktPtr != -1)); - return err; - } + // Set the socket to non-blocking. + if (err == 0) + { + err = fcntl(*sktPtr, F_GETFL, 0); + if (err < 0) err = errno; + else + { + err = fcntl(*sktPtr, F_SETFL, err | O_NONBLOCK); + if (err < 0) err = errno; + } + } + + // Clean up + if (err != 0 && *sktPtr != -1) { assert(close(*sktPtr) == 0); *sktPtr = -1; } + assert((err == 0) == (*sktPtr != -1)); + return err; +} // Creates a PosixNetworkInterface for the interface whose IP address is // intfAddr and whose name is intfName and registers it with mDNS core. mDNSlocal int SetupOneInterface(mDNS *const m, struct sockaddr *intfAddr, struct sockaddr *intfMask, const char *intfName, int intfIndex) - { - int err = 0; - PosixNetworkInterface *intf; - PosixNetworkInterface *alias = NULL; - - assert(m != NULL); - assert(intfAddr != NULL); - assert(intfName != NULL); - assert(intfMask != NULL); - - // Allocate the interface structure itself. - intf = (PosixNetworkInterface*)malloc(sizeof(*intf)); - if (intf == NULL) { assert(0); err = ENOMEM; } - - // And make a copy of the intfName. - if (err == 0) - { - intf->intfName = strdup(intfName); - if (intf->intfName == NULL) { assert(0); err = ENOMEM; } - } - - if (err == 0) - { - // Set up the fields required by the mDNS core. - SockAddrTomDNSAddr(intfAddr, &intf->coreIntf.ip, NULL); - SockAddrTomDNSAddr(intfMask, &intf->coreIntf.mask, NULL); - //LogMsg("SetupOneInterface: %#a %#a", &intf->coreIntf.ip, &intf->coreIntf.mask); - strncpy(intf->coreIntf.ifname, intfName, sizeof(intf->coreIntf.ifname)); - intf->coreIntf.ifname[sizeof(intf->coreIntf.ifname)-1] = 0; - intf->coreIntf.Advertise = m->AdvertiseLocalAddresses; - intf->coreIntf.McastTxRx = mDNStrue; - - // Set up the extra fields in PosixNetworkInterface. - assert(intf->intfName != NULL); // intf->intfName already set up above - intf->index = intfIndex; - intf->multicastSocket4 = -1; +{ + int err = 0; + PosixNetworkInterface *intf; + PosixNetworkInterface *alias = NULL; + + assert(m != NULL); + assert(intfAddr != NULL); + assert(intfName != NULL); + assert(intfMask != NULL); + + // Allocate the interface structure itself. + intf = (PosixNetworkInterface*)malloc(sizeof(*intf)); + if (intf == NULL) { assert(0); err = ENOMEM; } + + // And make a copy of the intfName. + if (err == 0) + { + intf->intfName = strdup(intfName); + if (intf->intfName == NULL) { assert(0); err = ENOMEM; } + } + + if (err == 0) + { + // Set up the fields required by the mDNS core. + SockAddrTomDNSAddr(intfAddr, &intf->coreIntf.ip, NULL); + SockAddrTomDNSAddr(intfMask, &intf->coreIntf.mask, NULL); + + //LogMsg("SetupOneInterface: %#a %#a", &intf->coreIntf.ip, &intf->coreIntf.mask); + strncpy(intf->coreIntf.ifname, intfName, sizeof(intf->coreIntf.ifname)); + intf->coreIntf.ifname[sizeof(intf->coreIntf.ifname)-1] = 0; + intf->coreIntf.Advertise = m->AdvertiseLocalAddresses; + intf->coreIntf.McastTxRx = mDNStrue; + + // Set up the extra fields in PosixNetworkInterface. + assert(intf->intfName != NULL); // intf->intfName already set up above + intf->index = intfIndex; + intf->multicastSocket4 = -1; #if HAVE_IPV6 - intf->multicastSocket6 = -1; + intf->multicastSocket6 = -1; #endif - alias = SearchForInterfaceByName(m, intf->intfName); - if (alias == NULL) alias = intf; - intf->coreIntf.InterfaceID = (mDNSInterfaceID)alias; - - if (alias != intf) - debugf("SetupOneInterface: %s %#a is an alias of %#a", intfName, &intf->coreIntf.ip, &alias->coreIntf.ip); - } - - // Set up the multicast socket - if (err == 0) - { - if (alias->multicastSocket4 == -1 && intfAddr->sa_family == AF_INET) - err = SetupSocket(intfAddr, MulticastDNSPort, intf->index, &alias->multicastSocket4); + alias = SearchForInterfaceByName(m, intf->intfName); + if (alias == NULL) alias = intf; + intf->coreIntf.InterfaceID = (mDNSInterfaceID)alias; + + if (alias != intf) + debugf("SetupOneInterface: %s %#a is an alias of %#a", intfName, &intf->coreIntf.ip, &alias->coreIntf.ip); + } + + // Set up the multicast socket + if (err == 0) + { + if (alias->multicastSocket4 == -1 && intfAddr->sa_family == AF_INET) + err = SetupSocket(intfAddr, MulticastDNSPort, intf->index, &alias->multicastSocket4); #if HAVE_IPV6 - else if (alias->multicastSocket6 == -1 && intfAddr->sa_family == AF_INET6) - err = SetupSocket(intfAddr, MulticastDNSPort, intf->index, &alias->multicastSocket6); + else if (alias->multicastSocket6 == -1 && intfAddr->sa_family == AF_INET6) + err = SetupSocket(intfAddr, MulticastDNSPort, intf->index, &alias->multicastSocket6); #endif - } - - // The interface is all ready to go, let's register it with the mDNS core. - if (err == 0) - err = mDNS_RegisterInterface(m, &intf->coreIntf, mDNSfalse); - - // Clean up. - if (err == 0) - { - num_registered_interfaces++; - debugf("SetupOneInterface: %s %#a Registered", intf->intfName, &intf->coreIntf.ip); - if (gMDNSPlatformPosixVerboseLevel > 0) - fprintf(stderr, "Registered interface %s\n", intf->intfName); - } - else - { - // Use intfName instead of intf->intfName in the next line to avoid dereferencing NULL. - debugf("SetupOneInterface: %s %#a failed to register %d", intfName, &intf->coreIntf.ip, err); - if (intf) { FreePosixNetworkInterface(intf); intf = NULL; } - } - - assert((err == 0) == (intf != NULL)); - - return err; - } + } + + // The interface is all ready to go, let's register it with the mDNS core. + if (err == 0) + err = mDNS_RegisterInterface(m, &intf->coreIntf, mDNSfalse); + + // Clean up. + if (err == 0) + { + num_registered_interfaces++; + debugf("SetupOneInterface: %s %#a Registered", intf->intfName, &intf->coreIntf.ip); + if (gMDNSPlatformPosixVerboseLevel > 0) + fprintf(stderr, "Registered interface %s\n", intf->intfName); + } + else + { + // Use intfName instead of intf->intfName in the next line to avoid dereferencing NULL. + debugf("SetupOneInterface: %s %#a failed to register %d", intfName, &intf->coreIntf.ip, err); + if (intf) { FreePosixNetworkInterface(intf); intf = NULL; } + } + + assert((err == 0) == (intf != NULL)); + + return err; +} // Call get_ifi_info() to obtain a list of active interfaces and call SetupOneInterface() on each one. mDNSlocal int SetupInterfaceList(mDNS *const m) - { - mDNSBool foundav4 = mDNSfalse; - int err = 0; - struct ifi_info *intfList = get_ifi_info(AF_INET, mDNStrue); - struct ifi_info *firstLoopback = NULL; +{ + mDNSBool foundav4 = mDNSfalse; + int err = 0; + struct ifi_info *intfList = get_ifi_info(AF_INET, mDNStrue); + struct ifi_info *firstLoopback = NULL; - assert(m != NULL); - debugf("SetupInterfaceList"); + assert(m != NULL); + debugf("SetupInterfaceList"); - if (intfList == NULL) err = ENOENT; + if (intfList == NULL) err = ENOENT; #if HAVE_IPV6 - if (err == 0) /* Link the IPv6 list to the end of the IPv4 list */ - { - struct ifi_info **p = &intfList; - while (*p) p = &(*p)->ifi_next; - *p = get_ifi_info(AF_INET6, mDNStrue); - } + if (err == 0) /* Link the IPv6 list to the end of the IPv4 list */ + { + struct ifi_info **p = &intfList; + while (*p) p = &(*p)->ifi_next; + *p = get_ifi_info(AF_INET6, mDNStrue); + } #endif - if (err == 0) - { - struct ifi_info *i = intfList; - while (i) - { - if ( ((i->ifi_addr->sa_family == AF_INET) + if (err == 0) + { + struct ifi_info *i = intfList; + while (i) + { + if ( ((i->ifi_addr->sa_family == AF_INET) #if HAVE_IPV6 - || (i->ifi_addr->sa_family == AF_INET6) + || (i->ifi_addr->sa_family == AF_INET6) #endif - ) && (i->ifi_flags & IFF_UP) && !(i->ifi_flags & IFF_POINTOPOINT)) - { - if (i->ifi_flags & IFF_LOOPBACK) - { - if (firstLoopback == NULL) - firstLoopback = i; - } - else - { - if (SetupOneInterface(m, i->ifi_addr, i->ifi_netmask, i->ifi_name, i->ifi_index) == 0) - if (i->ifi_addr->sa_family == AF_INET) - foundav4 = mDNStrue; - } - } - i = i->ifi_next; - } - - // If we found no normal interfaces but we did find a loopback interface, register the - // loopback interface. This allows self-discovery if no interfaces are configured. - // Temporary workaround: Multicast loopback on IPv6 interfaces appears not to work. - // In the interim, we skip loopback interface only if we found at least one v4 interface to use - // if ((m->HostInterfaces == NULL) && (firstLoopback != NULL)) - if (!foundav4 && firstLoopback) - (void) SetupOneInterface(m, firstLoopback->ifi_addr, firstLoopback->ifi_netmask, firstLoopback->ifi_name, firstLoopback->ifi_index); - } - - // Clean up. - if (intfList != NULL) free_ifi_info(intfList); - return err; - } + ) && (i->ifi_flags & IFF_UP) && !(i->ifi_flags & IFF_POINTOPOINT)) + { + if (i->ifi_flags & IFF_LOOPBACK) + { + if (firstLoopback == NULL) + firstLoopback = i; + } + else + { + if (SetupOneInterface(m, i->ifi_addr, i->ifi_netmask, i->ifi_name, i->ifi_index) == 0) + if (i->ifi_addr->sa_family == AF_INET) + foundav4 = mDNStrue; + } + } + i = i->ifi_next; + } + + // If we found no normal interfaces but we did find a loopback interface, register the + // loopback interface. This allows self-discovery if no interfaces are configured. + // Temporary workaround: Multicast loopback on IPv6 interfaces appears not to work. + // In the interim, we skip loopback interface only if we found at least one v4 interface to use + // if ((m->HostInterfaces == NULL) && (firstLoopback != NULL)) + if (!foundav4 && firstLoopback) + (void) SetupOneInterface(m, firstLoopback->ifi_addr, firstLoopback->ifi_netmask, firstLoopback->ifi_name, firstLoopback->ifi_index); + } + + // Clean up. + if (intfList != NULL) free_ifi_info(intfList); + return err; +} #if USES_NETLINK @@ -969,322 +963,322 @@ mDNSlocal int SetupInterfaceList(mDNS *const m) // Open a socket that will receive interface change notifications mDNSlocal mStatus OpenIfNotifySocket(int *pFD) - { - mStatus err = mStatus_NoError; - struct sockaddr_nl snl; - int sock; - int ret; - - sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (sock < 0) - return errno; - - // Configure read to be non-blocking because inbound msg size is not known in advance - (void) fcntl(sock, F_SETFL, O_NONBLOCK); - - /* Subscribe the socket to Link & IP addr notifications. */ - mDNSPlatformMemZero(&snl, sizeof snl); - snl.nl_family = AF_NETLINK; - snl.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR; - ret = bind(sock, (struct sockaddr *) &snl, sizeof snl); - if (0 == ret) - *pFD = sock; - else - err = errno; - - return err; - } +{ + mStatus err = mStatus_NoError; + struct sockaddr_nl snl; + int sock; + int ret; + + sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock < 0) + return errno; + + // Configure read to be non-blocking because inbound msg size is not known in advance + (void) fcntl(sock, F_SETFL, O_NONBLOCK); + + /* Subscribe the socket to Link & IP addr notifications. */ + mDNSPlatformMemZero(&snl, sizeof snl); + snl.nl_family = AF_NETLINK; + snl.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR; + ret = bind(sock, (struct sockaddr *) &snl, sizeof snl); + if (0 == ret) + *pFD = sock; + else + err = errno; + + return err; +} #if MDNS_DEBUGMSGS -mDNSlocal void PrintNetLinkMsg(const struct nlmsghdr *pNLMsg) - { - const char *kNLMsgTypes[] = { "", "NLMSG_NOOP", "NLMSG_ERROR", "NLMSG_DONE", "NLMSG_OVERRUN" }; - const char *kNLRtMsgTypes[] = { "RTM_NEWLINK", "RTM_DELLINK", "RTM_GETLINK", "RTM_NEWADDR", "RTM_DELADDR", "RTM_GETADDR" }; - - printf("nlmsghdr len=%d, type=%s, flags=0x%x\n", pNLMsg->nlmsg_len, - pNLMsg->nlmsg_type < RTM_BASE ? kNLMsgTypes[pNLMsg->nlmsg_type] : kNLRtMsgTypes[pNLMsg->nlmsg_type - RTM_BASE], - pNLMsg->nlmsg_flags); - - if (RTM_NEWLINK <= pNLMsg->nlmsg_type && pNLMsg->nlmsg_type <= RTM_GETLINK) - { - struct ifinfomsg *pIfInfo = (struct ifinfomsg*) NLMSG_DATA(pNLMsg); - printf("ifinfomsg family=%d, type=%d, index=%d, flags=0x%x, change=0x%x\n", pIfInfo->ifi_family, - pIfInfo->ifi_type, pIfInfo->ifi_index, pIfInfo->ifi_flags, pIfInfo->ifi_change); - - } - else if (RTM_NEWADDR <= pNLMsg->nlmsg_type && pNLMsg->nlmsg_type <= RTM_GETADDR) - { - struct ifaddrmsg *pIfAddr = (struct ifaddrmsg*) NLMSG_DATA(pNLMsg); - printf("ifaddrmsg family=%d, index=%d, flags=0x%x\n", pIfAddr->ifa_family, - pIfAddr->ifa_index, pIfAddr->ifa_flags); - } - printf("\n"); - } +mDNSlocal void PrintNetLinkMsg(const struct nlmsghdr *pNLMsg) +{ + const char *kNLMsgTypes[] = { "", "NLMSG_NOOP", "NLMSG_ERROR", "NLMSG_DONE", "NLMSG_OVERRUN" }; + const char *kNLRtMsgTypes[] = { "RTM_NEWLINK", "RTM_DELLINK", "RTM_GETLINK", "RTM_NEWADDR", "RTM_DELADDR", "RTM_GETADDR" }; + + printf("nlmsghdr len=%d, type=%s, flags=0x%x\n", pNLMsg->nlmsg_len, + pNLMsg->nlmsg_type < RTM_BASE ? kNLMsgTypes[pNLMsg->nlmsg_type] : kNLRtMsgTypes[pNLMsg->nlmsg_type - RTM_BASE], + pNLMsg->nlmsg_flags); + + if (RTM_NEWLINK <= pNLMsg->nlmsg_type && pNLMsg->nlmsg_type <= RTM_GETLINK) + { + struct ifinfomsg *pIfInfo = (struct ifinfomsg*) NLMSG_DATA(pNLMsg); + printf("ifinfomsg family=%d, type=%d, index=%d, flags=0x%x, change=0x%x\n", pIfInfo->ifi_family, + pIfInfo->ifi_type, pIfInfo->ifi_index, pIfInfo->ifi_flags, pIfInfo->ifi_change); + + } + else if (RTM_NEWADDR <= pNLMsg->nlmsg_type && pNLMsg->nlmsg_type <= RTM_GETADDR) + { + struct ifaddrmsg *pIfAddr = (struct ifaddrmsg*) NLMSG_DATA(pNLMsg); + printf("ifaddrmsg family=%d, index=%d, flags=0x%x\n", pIfAddr->ifa_family, + pIfAddr->ifa_index, pIfAddr->ifa_flags); + } + printf("\n"); +} #endif -mDNSlocal mDNSu32 ProcessRoutingNotification(int sd) +mDNSlocal mDNSu32 ProcessRoutingNotification(int sd) // Read through the messages on sd and if any indicate that any interface records should // be torn down and rebuilt, return affected indices as a bitmask. Otherwise return 0. - { - ssize_t readCount; - char buff[4096]; - struct nlmsghdr *pNLMsg = (struct nlmsghdr*) buff; - mDNSu32 result = 0; - - // The structure here is more complex than it really ought to be because, - // unfortunately, there's no good way to size a buffer in advance large - // enough to hold all pending data and so avoid message fragmentation. - // (Note that FIONREAD is not supported on AF_NETLINK.) - - readCount = read(sd, buff, sizeof buff); - while (1) - { - // Make sure we've got an entire nlmsghdr in the buffer, and payload, too. - // If not, discard already-processed messages in buffer and read more data. - if (((char*) &pNLMsg[1] > (buff + readCount)) || // i.e. *pNLMsg extends off end of buffer - ((char*) pNLMsg + pNLMsg->nlmsg_len > (buff + readCount))) - { - if (buff < (char*) pNLMsg) // we have space to shuffle - { - // discard processed data - readCount -= ((char*) pNLMsg - buff); - memmove(buff, pNLMsg, readCount); - pNLMsg = (struct nlmsghdr*) buff; - - // read more data - readCount += read(sd, buff + readCount, sizeof buff - readCount); - continue; // spin around and revalidate with new readCount - } - else - break; // Otherwise message does not fit in buffer - } +{ + ssize_t readCount; + char buff[4096]; + struct nlmsghdr *pNLMsg = (struct nlmsghdr*) buff; + mDNSu32 result = 0; + + // The structure here is more complex than it really ought to be because, + // unfortunately, there's no good way to size a buffer in advance large + // enough to hold all pending data and so avoid message fragmentation. + // (Note that FIONREAD is not supported on AF_NETLINK.) + + readCount = read(sd, buff, sizeof buff); + while (1) + { + // Make sure we've got an entire nlmsghdr in the buffer, and payload, too. + // If not, discard already-processed messages in buffer and read more data. + if (((char*) &pNLMsg[1] > (buff + readCount)) || // i.e. *pNLMsg extends off end of buffer + ((char*) pNLMsg + pNLMsg->nlmsg_len > (buff + readCount))) + { + if (buff < (char*) pNLMsg) // we have space to shuffle + { + // discard processed data + readCount -= ((char*) pNLMsg - buff); + memmove(buff, pNLMsg, readCount); + pNLMsg = (struct nlmsghdr*) buff; + + // read more data + readCount += read(sd, buff + readCount, sizeof buff - readCount); + continue; // spin around and revalidate with new readCount + } + else + break; // Otherwise message does not fit in buffer + } #if MDNS_DEBUGMSGS - PrintNetLinkMsg(pNLMsg); + PrintNetLinkMsg(pNLMsg); #endif - // Process the NetLink message - if (pNLMsg->nlmsg_type == RTM_GETLINK || pNLMsg->nlmsg_type == RTM_NEWLINK) - result |= 1 << ((struct ifinfomsg*) NLMSG_DATA(pNLMsg))->ifi_index; - else if (pNLMsg->nlmsg_type == RTM_DELADDR || pNLMsg->nlmsg_type == RTM_NEWADDR) - result |= 1 << ((struct ifaddrmsg*) NLMSG_DATA(pNLMsg))->ifa_index; - - // Advance pNLMsg to the next message in the buffer - if ((pNLMsg->nlmsg_flags & NLM_F_MULTI) != 0 && pNLMsg->nlmsg_type != NLMSG_DONE) - { - ssize_t len = readCount - ((char*)pNLMsg - buff); - pNLMsg = NLMSG_NEXT(pNLMsg, len); - } - else - break; // all done! - } - - return result; - } + // Process the NetLink message + if (pNLMsg->nlmsg_type == RTM_GETLINK || pNLMsg->nlmsg_type == RTM_NEWLINK) + result |= 1 << ((struct ifinfomsg*) NLMSG_DATA(pNLMsg))->ifi_index; + else if (pNLMsg->nlmsg_type == RTM_DELADDR || pNLMsg->nlmsg_type == RTM_NEWADDR) + result |= 1 << ((struct ifaddrmsg*) NLMSG_DATA(pNLMsg))->ifa_index; + + // Advance pNLMsg to the next message in the buffer + if ((pNLMsg->nlmsg_flags & NLM_F_MULTI) != 0 && pNLMsg->nlmsg_type != NLMSG_DONE) + { + ssize_t len = readCount - ((char*)pNLMsg - buff); + pNLMsg = NLMSG_NEXT(pNLMsg, len); + } + else + break; // all done! + } + + return result; +} #else // USES_NETLINK // Open a socket that will receive interface change notifications mDNSlocal mStatus OpenIfNotifySocket(int *pFD) - { - *pFD = socket(AF_ROUTE, SOCK_RAW, 0); +{ + *pFD = socket(AF_ROUTE, SOCK_RAW, 0); - if (*pFD < 0) - return mStatus_UnknownErr; + if (*pFD < 0) + return mStatus_UnknownErr; - // Configure read to be non-blocking because inbound msg size is not known in advance - (void) fcntl(*pFD, F_SETFL, O_NONBLOCK); + // Configure read to be non-blocking because inbound msg size is not known in advance + (void) fcntl(*pFD, F_SETFL, O_NONBLOCK); - return mStatus_NoError; - } + return mStatus_NoError; +} #if MDNS_DEBUGMSGS -mDNSlocal void PrintRoutingSocketMsg(const struct ifa_msghdr *pRSMsg) - { - const char *kRSMsgTypes[] = { "", "RTM_ADD", "RTM_DELETE", "RTM_CHANGE", "RTM_GET", "RTM_LOSING", - "RTM_REDIRECT", "RTM_MISS", "RTM_LOCK", "RTM_OLDADD", "RTM_OLDDEL", "RTM_RESOLVE", - "RTM_NEWADDR", "RTM_DELADDR", "RTM_IFINFO", "RTM_NEWMADDR", "RTM_DELMADDR" }; +mDNSlocal void PrintRoutingSocketMsg(const struct ifa_msghdr *pRSMsg) +{ + const char *kRSMsgTypes[] = { "", "RTM_ADD", "RTM_DELETE", "RTM_CHANGE", "RTM_GET", "RTM_LOSING", + "RTM_REDIRECT", "RTM_MISS", "RTM_LOCK", "RTM_OLDADD", "RTM_OLDDEL", "RTM_RESOLVE", + "RTM_NEWADDR", "RTM_DELADDR", "RTM_IFINFO", "RTM_NEWMADDR", "RTM_DELMADDR" }; - int index = pRSMsg->ifam_type == RTM_IFINFO ? ((struct if_msghdr*) pRSMsg)->ifm_index : pRSMsg->ifam_index; + int index = pRSMsg->ifam_type == RTM_IFINFO ? ((struct if_msghdr*) pRSMsg)->ifm_index : pRSMsg->ifam_index; - printf("ifa_msghdr len=%d, type=%s, index=%d\n", pRSMsg->ifam_msglen, kRSMsgTypes[pRSMsg->ifam_type], index); - } + printf("ifa_msghdr len=%d, type=%s, index=%d\n", pRSMsg->ifam_msglen, kRSMsgTypes[pRSMsg->ifam_type], index); +} #endif -mDNSlocal mDNSu32 ProcessRoutingNotification(int sd) +mDNSlocal mDNSu32 ProcessRoutingNotification(int sd) // Read through the messages on sd and if any indicate that any interface records should // be torn down and rebuilt, return affected indices as a bitmask. Otherwise return 0. - { - ssize_t readCount; - char buff[4096]; - struct ifa_msghdr *pRSMsg = (struct ifa_msghdr*) buff; - mDNSu32 result = 0; +{ + ssize_t readCount; + char buff[4096]; + struct ifa_msghdr *pRSMsg = (struct ifa_msghdr*) buff; + mDNSu32 result = 0; - readCount = read(sd, buff, sizeof buff); - if (readCount < (ssize_t) sizeof(struct ifa_msghdr)) - return mStatus_UnsupportedErr; // cannot decipher message + readCount = read(sd, buff, sizeof buff); + if (readCount < (ssize_t) sizeof(struct ifa_msghdr)) + return mStatus_UnsupportedErr; // cannot decipher message #if MDNS_DEBUGMSGS - PrintRoutingSocketMsg(pRSMsg); + PrintRoutingSocketMsg(pRSMsg); #endif - // Process the message - if (pRSMsg->ifam_type == RTM_NEWADDR || pRSMsg->ifam_type == RTM_DELADDR || - pRSMsg->ifam_type == RTM_IFINFO) - { - if (pRSMsg->ifam_type == RTM_IFINFO) - result |= 1 << ((struct if_msghdr*) pRSMsg)->ifm_index; - else - result |= 1 << pRSMsg->ifam_index; - } + // Process the message + if (pRSMsg->ifam_type == RTM_NEWADDR || pRSMsg->ifam_type == RTM_DELADDR || + pRSMsg->ifam_type == RTM_IFINFO) + { + if (pRSMsg->ifam_type == RTM_IFINFO) + result |= 1 << ((struct if_msghdr*) pRSMsg)->ifm_index; + else + result |= 1 << pRSMsg->ifam_index; + } - return result; - } + return result; +} #endif // USES_NETLINK // Called when data appears on interface change notification socket mDNSlocal void InterfaceChangeCallback(int fd, short filter, void *context) - { - IfChangeRec *pChgRec = (IfChangeRec*) context; - fd_set readFDs; - mDNSu32 changedInterfaces = 0; - struct timeval zeroTimeout = { 0, 0 }; - - (void)fd; // Unused - (void)filter; // Unused - - FD_ZERO(&readFDs); - FD_SET(pChgRec->NotifySD, &readFDs); - - do - { - changedInterfaces |= ProcessRoutingNotification(pChgRec->NotifySD); - } - while (0 < select(pChgRec->NotifySD + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &zeroTimeout)); - - // Currently we rebuild the entire interface list whenever any interface change is - // detected. If this ever proves to be a performance issue in a multi-homed - // configuration, more care should be paid to changedInterfaces. - if (changedInterfaces) - mDNSPlatformPosixRefreshInterfaceList(pChgRec->mDNS); - } +{ + IfChangeRec *pChgRec = (IfChangeRec*) context; + fd_set readFDs; + mDNSu32 changedInterfaces = 0; + struct timeval zeroTimeout = { 0, 0 }; + + (void)fd; // Unused + (void)filter; // Unused + + FD_ZERO(&readFDs); + FD_SET(pChgRec->NotifySD, &readFDs); + + do + { + changedInterfaces |= ProcessRoutingNotification(pChgRec->NotifySD); + } + while (0 < select(pChgRec->NotifySD + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &zeroTimeout)); + + // Currently we rebuild the entire interface list whenever any interface change is + // detected. If this ever proves to be a performance issue in a multi-homed + // configuration, more care should be paid to changedInterfaces. + if (changedInterfaces) + mDNSPlatformPosixRefreshInterfaceList(pChgRec->mDNS); +} // Register with either a Routing Socket or RtNetLink to listen for interface changes. mDNSlocal mStatus WatchForInterfaceChange(mDNS *const m) - { - mStatus err; - IfChangeRec *pChgRec; +{ + mStatus err; + IfChangeRec *pChgRec; - pChgRec = (IfChangeRec*) mDNSPlatformMemAllocate(sizeof *pChgRec); - if (pChgRec == NULL) - return mStatus_NoMemoryErr; + pChgRec = (IfChangeRec*) mDNSPlatformMemAllocate(sizeof *pChgRec); + if (pChgRec == NULL) + return mStatus_NoMemoryErr; - pChgRec->mDNS = m; - err = OpenIfNotifySocket(&pChgRec->NotifySD); - if (err == 0) - err = mDNSPosixAddFDToEventLoop(pChgRec->NotifySD, InterfaceChangeCallback, pChgRec); + pChgRec->mDNS = m; + err = OpenIfNotifySocket(&pChgRec->NotifySD); + if (err == 0) + err = mDNSPosixAddFDToEventLoop(pChgRec->NotifySD, InterfaceChangeCallback, pChgRec); - return err; - } + return err; +} // Test to see if we're the first client running on UDP port 5353, by trying to bind to 5353 without using SO_REUSEPORT. // If we fail, someone else got here first. That's not a big problem; we can share the port for multicast responses -- // we just need to be aware that we shouldn't expect to successfully receive unicast UDP responses. mDNSlocal mDNSBool mDNSPlatformInit_CanReceiveUnicast(void) - { - int err; - int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - struct sockaddr_in s5353; - s5353.sin_family = AF_INET; - s5353.sin_port = MulticastDNSPort.NotAnInteger; - s5353.sin_addr.s_addr = 0; - err = bind(s, (struct sockaddr *)&s5353, sizeof(s5353)); - close(s); - if (err) debugf("No unicast UDP responses"); - else debugf("Unicast UDP responses okay"); - return(err == 0); - } +{ + int err; + int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + struct sockaddr_in s5353; + s5353.sin_family = AF_INET; + s5353.sin_port = MulticastDNSPort.NotAnInteger; + s5353.sin_addr.s_addr = 0; + err = bind(s, (struct sockaddr *)&s5353, sizeof(s5353)); + close(s); + if (err) debugf("No unicast UDP responses"); + else debugf("Unicast UDP responses okay"); + return(err == 0); +} // mDNS core calls this routine to initialise the platform-specific data. mDNSexport mStatus mDNSPlatformInit(mDNS *const m) - { - int err = 0; - struct sockaddr sa; - assert(m != NULL); +{ + int err = 0; + struct sockaddr sa; + assert(m != NULL); - if (mDNSPlatformInit_CanReceiveUnicast()) m->CanReceiveUnicastOn5353 = mDNStrue; + if (mDNSPlatformInit_CanReceiveUnicast()) m->CanReceiveUnicastOn5353 = mDNStrue; - // Tell mDNS core the names of this machine. + // Tell mDNS core the names of this machine. - // Set up the nice label - m->nicelabel.c[0] = 0; - GetUserSpecifiedFriendlyComputerName(&m->nicelabel); - if (m->nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->nicelabel, "Computer"); + // Set up the nice label + m->nicelabel.c[0] = 0; + GetUserSpecifiedFriendlyComputerName(&m->nicelabel); + if (m->nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->nicelabel, "Computer"); - // Set up the RFC 1034-compliant label - m->hostlabel.c[0] = 0; - GetUserSpecifiedRFC1034ComputerName(&m->hostlabel); - if (m->hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->hostlabel, "Computer"); + // Set up the RFC 1034-compliant label + m->hostlabel.c[0] = 0; + GetUserSpecifiedRFC1034ComputerName(&m->hostlabel); + if (m->hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->hostlabel, "Computer"); - mDNS_SetFQDN(m); + mDNS_SetFQDN(m); - sa.sa_family = AF_INET; - m->p->unicastSocket4 = -1; - if (err == mStatus_NoError) err = SetupSocket(&sa, zeroIPPort, 0, &m->p->unicastSocket4); + sa.sa_family = AF_INET; + m->p->unicastSocket4 = -1; + if (err == mStatus_NoError) err = SetupSocket(&sa, zeroIPPort, 0, &m->p->unicastSocket4); #if HAVE_IPV6 - sa.sa_family = AF_INET6; - m->p->unicastSocket6 = -1; - if (err == mStatus_NoError) err = SetupSocket(&sa, zeroIPPort, 0, &m->p->unicastSocket6); + sa.sa_family = AF_INET6; + m->p->unicastSocket6 = -1; + if (err == mStatus_NoError) err = SetupSocket(&sa, zeroIPPort, 0, &m->p->unicastSocket6); #endif - // Tell mDNS core about the network interfaces on this machine. - if (err == mStatus_NoError) err = SetupInterfaceList(m); - - // Tell mDNS core about DNS Servers - mDNS_Lock(m); - if (err == mStatus_NoError) ParseDNSServers(m, uDNS_SERVERS_FILE); - mDNS_Unlock(m); - - if (err == mStatus_NoError) - { - err = WatchForInterfaceChange(m); - // Failure to observe interface changes is non-fatal. - if (err != mStatus_NoError) - { - fprintf(stderr, "mDNS(%d) WARNING: Unable to detect interface changes (%d).\n", getpid(), err); - err = mStatus_NoError; - } - } - - // We don't do asynchronous initialization on the Posix platform, so by the time - // we get here the setup will already have succeeded or failed. If it succeeded, - // we should just call mDNSCoreInitComplete() immediately. - if (err == mStatus_NoError) - mDNSCoreInitComplete(m, mStatus_NoError); - - return PosixErrorToStatus(err); - } + // Tell mDNS core about the network interfaces on this machine. + if (err == mStatus_NoError) err = SetupInterfaceList(m); + + // Tell mDNS core about DNS Servers + mDNS_Lock(m); + if (err == mStatus_NoError) ParseDNSServers(m, uDNS_SERVERS_FILE); + mDNS_Unlock(m); + + if (err == mStatus_NoError) + { + err = WatchForInterfaceChange(m); + // Failure to observe interface changes is non-fatal. + if (err != mStatus_NoError) + { + fprintf(stderr, "mDNS(%d) WARNING: Unable to detect interface changes (%d).\n", getpid(), err); + err = mStatus_NoError; + } + } + + // We don't do asynchronous initialization on the Posix platform, so by the time + // we get here the setup will already have succeeded or failed. If it succeeded, + // we should just call mDNSCoreInitComplete() immediately. + if (err == mStatus_NoError) + mDNSCoreInitComplete(m, mStatus_NoError); + + return PosixErrorToStatus(err); +} // mDNS core calls this routine to clean up the platform-specific data. // In our case all we need to do is to tear down every network interface. mDNSexport void mDNSPlatformClose(mDNS *const m) - { - assert(m != NULL); - ClearInterfaceList(m); - if (m->p->unicastSocket4 != -1) assert(close(m->p->unicastSocket4) == 0); +{ + assert(m != NULL); + ClearInterfaceList(m); + if (m->p->unicastSocket4 != -1) assert(close(m->p->unicastSocket4) == 0); #if HAVE_IPV6 - if (m->p->unicastSocket6 != -1) assert(close(m->p->unicastSocket6) == 0); + if (m->p->unicastSocket6 != -1) assert(close(m->p->unicastSocket6) == 0); #endif - } +} mDNSexport mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m) - { - int err; - ClearInterfaceList(m); - err = SetupInterfaceList(m); - return PosixErrorToStatus(err); - } +{ + int err; + ClearInterfaceList(m); + err = SetupInterfaceList(m); + return PosixErrorToStatus(err); +} #if COMPILER_LIKES_PRAGMA_MARK #pragma mark ***** Locking @@ -1296,16 +1290,16 @@ mDNSexport mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m) // mDNS core calls this routine when it wants to prevent // the platform from reentering mDNS core code. mDNSexport void mDNSPlatformLock (const mDNS *const m) - { - (void) m; // Unused - } +{ + (void) m; // Unused +} // mDNS core calls this routine when it release the lock taken by // mDNSPlatformLock and allow the platform to reenter mDNS core code. mDNSexport void mDNSPlatformUnlock (const mDNS *const m) - { - (void) m; // Unused - } +{ + (void) m; // Unused +} #if COMPILER_LIKES_PRAGMA_MARK #pragma mark ***** Strings @@ -1314,307 +1308,374 @@ mDNSexport void mDNSPlatformUnlock (const mDNS *const m) // mDNS core calls this routine to copy C strings. // On the Posix platform this maps directly to the ANSI C strcpy. mDNSexport void mDNSPlatformStrCopy(void *dst, const void *src) - { - strcpy((char *)dst, (char *)src); - } +{ + strcpy((char *)dst, (char *)src); +} // mDNS core calls this routine to get the length of a C string. // On the Posix platform this maps directly to the ANSI C strlen. mDNSexport mDNSu32 mDNSPlatformStrLen (const void *src) - { - return strlen((char*)src); - } +{ + return strlen((char*)src); +} // mDNS core calls this routine to copy memory. // On the Posix platform this maps directly to the ANSI C memcpy. mDNSexport void mDNSPlatformMemCopy(void *dst, const void *src, mDNSu32 len) - { - memcpy(dst, src, len); - } +{ + memcpy(dst, src, len); +} // mDNS core calls this routine to test whether blocks of memory are byte-for-byte // identical. On the Posix platform this is a simple wrapper around ANSI C memcmp. mDNSexport mDNSBool mDNSPlatformMemSame(const void *dst, const void *src, mDNSu32 len) - { - return memcmp(dst, src, len) == 0; - } +{ + return memcmp(dst, src, len) == 0; +} + +// If the caller wants to know the exact return of memcmp, then use this instead +// of mDNSPlatformMemSame +mDNSexport int mDNSPlatformMemCmp(const void *dst, const void *src, mDNSu32 len) +{ + return (memcmp(dst, src, len)); +} + +mDNSexport void mDNSPlatformQsort(void *base, int nel, int width, int (*compar)(const void *, const void *)) +{ + return (qsort(base, nel, width, compar)); +} + +// DNSSEC stub functions +mDNSexport void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *q) +{ + (void)m; + (void)dv; + (void)q; +} + +mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode) +{ + (void)m; + (void)crlist; + (void)negcr; + (void)rcode; + return mDNSfalse; +} // mDNS core calls this routine to clear blocks of memory. // On the Posix platform this is a simple wrapper around ANSI C memset. mDNSexport void mDNSPlatformMemZero(void *dst, mDNSu32 len) - { - memset(dst, 0, len); - } +{ + memset(dst, 0, len); +} mDNSexport void * mDNSPlatformMemAllocate(mDNSu32 len) { return(malloc(len)); } mDNSexport void mDNSPlatformMemFree (void *mem) { free(mem); } mDNSexport mDNSu32 mDNSPlatformRandomSeed(void) - { - struct timeval tv; - gettimeofday(&tv, NULL); - return(tv.tv_usec); - } +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return(tv.tv_usec); +} -mDNSexport mDNSs32 mDNSPlatformOneSecond = 1024; +mDNSexport mDNSs32 mDNSPlatformOneSecond = 1024; mDNSexport mStatus mDNSPlatformTimeInit(void) - { - // No special setup is required on Posix -- we just use gettimeofday(); - // This is not really safe, because gettimeofday can go backwards if the user manually changes the date or time - // We should find a better way to do this - return(mStatus_NoError); - } +{ + // No special setup is required on Posix -- we just use gettimeofday(); + // This is not really safe, because gettimeofday can go backwards if the user manually changes the date or time + // We should find a better way to do this + return(mStatus_NoError); +} mDNSexport mDNSs32 mDNSPlatformRawTime() - { - struct timeval tv; - gettimeofday(&tv, NULL); - // tv.tv_sec is seconds since 1st January 1970 (GMT, with no adjustment for daylight savings time) - // tv.tv_usec is microseconds since the start of this second (i.e. values 0 to 999999) - // We use the lower 22 bits of tv.tv_sec for the top 22 bits of our result - // and we multiply tv.tv_usec by 16 / 15625 to get a value in the range 0-1023 to go in the bottom 10 bits. - // This gives us a proper modular (cyclic) counter that has a resolution of roughly 1ms (actually 1/1024 second) - // and correctly cycles every 2^22 seconds (4194304 seconds = approx 48 days). - return((tv.tv_sec << 10) | (tv.tv_usec * 16 / 15625)); - } +{ + struct timeval tv; + gettimeofday(&tv, NULL); + // tv.tv_sec is seconds since 1st January 1970 (GMT, with no adjustment for daylight savings time) + // tv.tv_usec is microseconds since the start of this second (i.e. values 0 to 999999) + // We use the lower 22 bits of tv.tv_sec for the top 22 bits of our result + // and we multiply tv.tv_usec by 16 / 15625 to get a value in the range 0-1023 to go in the bottom 10 bits. + // This gives us a proper modular (cyclic) counter that has a resolution of roughly 1ms (actually 1/1024 second) + // and correctly cycles every 2^22 seconds (4194304 seconds = approx 48 days). + return((tv.tv_sec << 10) | (tv.tv_usec * 16 / 15625)); +} mDNSexport mDNSs32 mDNSPlatformUTC(void) - { - return time(NULL); - } +{ + return time(NULL); +} mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration) - { - (void) m; - (void) InterfaceID; - (void) EthAddr; - (void) IPAddr; - (void) iteration; - } +{ + (void) m; + (void) InterfaceID; + (void) EthAddr; + (void) IPAddr; + (void) iteration; +} mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf) - { - (void) rr; - (void) intf; - - return 1; - } +{ + (void) rr; + (void) intf; + + return 1; +} + +mDNSexport mDNSBool mDNSPlatformValidQuestionForInterface(DNSQuestion *q, const NetworkInterfaceInfo *intf) +{ + (void) q; + (void) intf; + + return 1; +} + +// Used for debugging purposes. For now, just set the buffer to zero +mDNSexport void mDNSPlatformFormatTime(unsigned long te, mDNSu8 *buf, int bufsize) +{ + (void) te; + if (bufsize) buf[0] = 0; +} + +mDNSexport void mDNSPlatformSendKeepalive(mDNSAddr *sadd, mDNSAddr *dadd, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu32 seq, mDNSu32 ack, mDNSu16 win) +{ + (void) sadd; // Unused + (void) dadd; // Unused + (void) lport; // Unused + (void) rport; // Unused + (void) seq; // Unused + (void) ack; // Unused + (void) win; // Unused +} + +mDNSexport mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, mDNSIPPort *lport, mDNSAddr *raddr, mDNSIPPort *rport, mDNSTCPInfo *mti) +{ + (void) m; // Unused + (void) laddr; // Unused + (void) raddr; // Unused + (void) lport; // Unused + (void) rport; // Unused + (void) mti; // Unused + + return mStatus_NoError; +} mDNSlocal void mDNSPosixAddToFDSet(int *nfds, fd_set *readfds, int s) - { - if (*nfds < s + 1) *nfds = s + 1; - FD_SET(s, readfds); - } +{ + if (*nfds < s + 1) *nfds = s + 1; + FD_SET(s, readfds); +} mDNSexport void mDNSPosixGetFDSet(mDNS *m, int *nfds, fd_set *readfds, struct timeval *timeout) - { - mDNSs32 ticks; - struct timeval interval; +{ + mDNSs32 ticks; + struct timeval interval; - // 1. Call mDNS_Execute() to let mDNSCore do what it needs to do - mDNSs32 nextevent = mDNS_Execute(m); + // 1. Call mDNS_Execute() to let mDNSCore do what it needs to do + mDNSs32 nextevent = mDNS_Execute(m); - // 2. Build our list of active file descriptors - PosixNetworkInterface *info = (PosixNetworkInterface *)(m->HostInterfaces); - if (m->p->unicastSocket4 != -1) mDNSPosixAddToFDSet(nfds, readfds, m->p->unicastSocket4); + // 2. Build our list of active file descriptors + PosixNetworkInterface *info = (PosixNetworkInterface *)(m->HostInterfaces); + if (m->p->unicastSocket4 != -1) mDNSPosixAddToFDSet(nfds, readfds, m->p->unicastSocket4); #if HAVE_IPV6 - if (m->p->unicastSocket6 != -1) mDNSPosixAddToFDSet(nfds, readfds, m->p->unicastSocket6); + if (m->p->unicastSocket6 != -1) mDNSPosixAddToFDSet(nfds, readfds, m->p->unicastSocket6); #endif - while (info) - { - if (info->multicastSocket4 != -1) mDNSPosixAddToFDSet(nfds, readfds, info->multicastSocket4); + while (info) + { + if (info->multicastSocket4 != -1) mDNSPosixAddToFDSet(nfds, readfds, info->multicastSocket4); #if HAVE_IPV6 - if (info->multicastSocket6 != -1) mDNSPosixAddToFDSet(nfds, readfds, info->multicastSocket6); + if (info->multicastSocket6 != -1) mDNSPosixAddToFDSet(nfds, readfds, info->multicastSocket6); #endif - info = (PosixNetworkInterface *)(info->coreIntf.next); - } + info = (PosixNetworkInterface *)(info->coreIntf.next); + } - // 3. Calculate the time remaining to the next scheduled event (in struct timeval format) - ticks = nextevent - mDNS_TimeNow(m); - if (ticks < 1) ticks = 1; - interval.tv_sec = ticks >> 10; // The high 22 bits are seconds - interval.tv_usec = ((ticks & 0x3FF) * 15625) / 16; // The low 10 bits are 1024ths + // 3. Calculate the time remaining to the next scheduled event (in struct timeval format) + ticks = nextevent - mDNS_TimeNow(m); + if (ticks < 1) ticks = 1; + interval.tv_sec = ticks >> 10; // The high 22 bits are seconds + interval.tv_usec = ((ticks & 0x3FF) * 15625) / 16; // The low 10 bits are 1024ths - // 4. If client's proposed timeout is more than what we want, then reduce it - if (timeout->tv_sec > interval.tv_sec || - (timeout->tv_sec == interval.tv_sec && timeout->tv_usec > interval.tv_usec)) - *timeout = interval; - } + // 4. If client's proposed timeout is more than what we want, then reduce it + if (timeout->tv_sec > interval.tv_sec || + (timeout->tv_sec == interval.tv_sec && timeout->tv_usec > interval.tv_usec)) + *timeout = interval; +} mDNSexport void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds) - { - PosixNetworkInterface *info; - assert(m != NULL); - assert(readfds != NULL); - info = (PosixNetworkInterface *)(m->HostInterfaces); - - if (m->p->unicastSocket4 != -1 && FD_ISSET(m->p->unicastSocket4, readfds)) - { - FD_CLR(m->p->unicastSocket4, readfds); - SocketDataReady(m, NULL, m->p->unicastSocket4); - } +{ + PosixNetworkInterface *info; + assert(m != NULL); + assert(readfds != NULL); + info = (PosixNetworkInterface *)(m->HostInterfaces); + + if (m->p->unicastSocket4 != -1 && FD_ISSET(m->p->unicastSocket4, readfds)) + { + FD_CLR(m->p->unicastSocket4, readfds); + SocketDataReady(m, NULL, m->p->unicastSocket4); + } #if HAVE_IPV6 - if (m->p->unicastSocket6 != -1 && FD_ISSET(m->p->unicastSocket6, readfds)) - { - FD_CLR(m->p->unicastSocket6, readfds); - SocketDataReady(m, NULL, m->p->unicastSocket6); - } + if (m->p->unicastSocket6 != -1 && FD_ISSET(m->p->unicastSocket6, readfds)) + { + FD_CLR(m->p->unicastSocket6, readfds); + SocketDataReady(m, NULL, m->p->unicastSocket6); + } #endif - while (info) - { - if (info->multicastSocket4 != -1 && FD_ISSET(info->multicastSocket4, readfds)) - { - FD_CLR(info->multicastSocket4, readfds); - SocketDataReady(m, info, info->multicastSocket4); - } + while (info) + { + if (info->multicastSocket4 != -1 && FD_ISSET(info->multicastSocket4, readfds)) + { + FD_CLR(info->multicastSocket4, readfds); + SocketDataReady(m, info, info->multicastSocket4); + } #if HAVE_IPV6 - if (info->multicastSocket6 != -1 && FD_ISSET(info->multicastSocket6, readfds)) - { - FD_CLR(info->multicastSocket6, readfds); - SocketDataReady(m, info, info->multicastSocket6); - } + if (info->multicastSocket6 != -1 && FD_ISSET(info->multicastSocket6, readfds)) + { + FD_CLR(info->multicastSocket6, readfds); + SocketDataReady(m, info, info->multicastSocket6); + } #endif - info = (PosixNetworkInterface *)(info->coreIntf.next); - } - } + info = (PosixNetworkInterface *)(info->coreIntf.next); + } +} // update gMaxFD -mDNSlocal void DetermineMaxEventFD(void) - { - PosixEventSource *iSource; - - gMaxFD = 0; - for (iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) - if (gMaxFD < iSource->fd) - gMaxFD = iSource->fd; - } +mDNSlocal void DetermineMaxEventFD(void) +{ + PosixEventSource *iSource; + + gMaxFD = 0; + for (iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) + if (gMaxFD < iSource->fd) + gMaxFD = iSource->fd; +} // Add a file descriptor to the set that mDNSPosixRunEventLoopOnce() listens to. mStatus mDNSPosixAddFDToEventLoop(int fd, mDNSPosixEventCallback callback, void *context) - { - PosixEventSource *newSource; - - if (gEventSources.LinkOffset == 0) - InitLinkedList(&gEventSources, offsetof(PosixEventSource, Next)); +{ + PosixEventSource *newSource; - if (fd >= (int) FD_SETSIZE || fd < 0) - return mStatus_UnsupportedErr; - if (callback == NULL) - return mStatus_BadParamErr; + if (gEventSources.LinkOffset == 0) + InitLinkedList(&gEventSources, offsetof(PosixEventSource, Next)); - newSource = (PosixEventSource*) malloc(sizeof *newSource); - if (NULL == newSource) - return mStatus_NoMemoryErr; + if (fd >= (int) FD_SETSIZE || fd < 0) + return mStatus_UnsupportedErr; + if (callback == NULL) + return mStatus_BadParamErr; - newSource->Callback = callback; - newSource->Context = context; - newSource->fd = fd; + newSource = (PosixEventSource*) malloc(sizeof *newSource); + if (NULL == newSource) + return mStatus_NoMemoryErr; - AddToTail(&gEventSources, newSource); - FD_SET(fd, &gEventFDs); + newSource->Callback = callback; + newSource->Context = context; + newSource->fd = fd; - DetermineMaxEventFD(); + AddToTail(&gEventSources, newSource); + FD_SET(fd, &gEventFDs); - return mStatus_NoError; - } + DetermineMaxEventFD(); + + return mStatus_NoError; +} // Remove a file descriptor from the set that mDNSPosixRunEventLoopOnce() listens to. mStatus mDNSPosixRemoveFDFromEventLoop(int fd) - { - PosixEventSource *iSource; - - for (iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) - { - if (fd == iSource->fd) - { - FD_CLR(fd, &gEventFDs); - RemoveFromList(&gEventSources, iSource); - free(iSource); - DetermineMaxEventFD(); - return mStatus_NoError; - } - } - return mStatus_NoSuchNameErr; - } +{ + PosixEventSource *iSource; + + for (iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) + { + if (fd == iSource->fd) + { + FD_CLR(fd, &gEventFDs); + RemoveFromList(&gEventSources, iSource); + free(iSource); + DetermineMaxEventFD(); + return mStatus_NoError; + } + } + return mStatus_NoSuchNameErr; +} // Simply note the received signal in gEventSignals. -mDNSlocal void NoteSignal(int signum) - { - sigaddset(&gEventSignals, signum); - } +mDNSlocal void NoteSignal(int signum) +{ + sigaddset(&gEventSignals, signum); +} // Tell the event package to listen for signal and report it in mDNSPosixRunEventLoopOnce(). mStatus mDNSPosixListenForSignalInEventLoop(int signum) - { - struct sigaction action; - mStatus err; +{ + struct sigaction action; + mStatus err; + + mDNSPlatformMemZero(&action, sizeof action); // more portable than member-wise assignment + action.sa_handler = NoteSignal; + err = sigaction(signum, &action, (struct sigaction*) NULL); - mDNSPlatformMemZero(&action, sizeof action); // more portable than member-wise assignment - action.sa_handler = NoteSignal; - err = sigaction(signum, &action, (struct sigaction*) NULL); - - sigaddset(&gEventSignalSet, signum); + sigaddset(&gEventSignalSet, signum); - return err; - } + return err; +} // Tell the event package to stop listening for signal in mDNSPosixRunEventLoopOnce(). mStatus mDNSPosixIgnoreSignalInEventLoop(int signum) - { - struct sigaction action; - mStatus err; +{ + struct sigaction action; + mStatus err; - mDNSPlatformMemZero(&action, sizeof action); // more portable than member-wise assignment - action.sa_handler = SIG_DFL; - err = sigaction(signum, &action, (struct sigaction*) NULL); - - sigdelset(&gEventSignalSet, signum); + mDNSPlatformMemZero(&action, sizeof action); // more portable than member-wise assignment + action.sa_handler = SIG_DFL; + err = sigaction(signum, &action, (struct sigaction*) NULL); - return err; - } + sigdelset(&gEventSignalSet, signum); + + return err; +} // Do a single pass through the attendent event sources and dispatch any found to their callbacks. // Return as soon as internal timeout expires, or a signal we're listening for is received. -mStatus mDNSPosixRunEventLoopOnce(mDNS *m, const struct timeval *pTimeout, - sigset_t *pSignalsReceived, mDNSBool *pDataDispatched) - { - fd_set listenFDs = gEventFDs; - int fdMax = 0, numReady; - struct timeval timeout = *pTimeout; - - // Include the sockets that are listening to the wire in our select() set - mDNSPosixGetFDSet(m, &fdMax, &listenFDs, &timeout); // timeout may get modified - if (fdMax < gMaxFD) - fdMax = gMaxFD; - - numReady = select(fdMax + 1, &listenFDs, (fd_set*) NULL, (fd_set*) NULL, &timeout); - - // If any data appeared, invoke its callback - if (numReady > 0) - { - PosixEventSource *iSource; - - (void) mDNSPosixProcessFDSet(m, &listenFDs); // call this first to process wire data for clients - - for (iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) - { - if (FD_ISSET(iSource->fd, &listenFDs)) - { - iSource->Callback(iSource->fd, 0, iSource->Context); - break; // in case callback removed elements from gEventSources - } - } - *pDataDispatched = mDNStrue; - } - else - *pDataDispatched = mDNSfalse; - - (void) sigprocmask(SIG_BLOCK, &gEventSignalSet, (sigset_t*) NULL); - *pSignalsReceived = gEventSignals; - sigemptyset(&gEventSignals); - (void) sigprocmask(SIG_UNBLOCK, &gEventSignalSet, (sigset_t*) NULL); - - return mStatus_NoError; - } +mStatus mDNSPosixRunEventLoopOnce(mDNS *m, const struct timeval *pTimeout, + sigset_t *pSignalsReceived, mDNSBool *pDataDispatched) +{ + fd_set listenFDs = gEventFDs; + int fdMax = 0, numReady; + struct timeval timeout = *pTimeout; + + // Include the sockets that are listening to the wire in our select() set + mDNSPosixGetFDSet(m, &fdMax, &listenFDs, &timeout); // timeout may get modified + if (fdMax < gMaxFD) + fdMax = gMaxFD; + + numReady = select(fdMax + 1, &listenFDs, (fd_set*) NULL, (fd_set*) NULL, &timeout); + + // If any data appeared, invoke its callback + if (numReady > 0) + { + PosixEventSource *iSource; + + (void) mDNSPosixProcessFDSet(m, &listenFDs); // call this first to process wire data for clients + + for (iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) + { + if (FD_ISSET(iSource->fd, &listenFDs)) + { + iSource->Callback(iSource->fd, 0, iSource->Context); + break; // in case callback removed elements from gEventSources + } + } + *pDataDispatched = mDNStrue; + } + else + *pDataDispatched = mDNSfalse; + + (void) sigprocmask(SIG_BLOCK, &gEventSignalSet, (sigset_t*) NULL); + *pSignalsReceived = gEventSignals; + sigemptyset(&gEventSignals); + (void) sigprocmask(SIG_UNBLOCK, &gEventSignalSet, (sigset_t*) NULL); + + return mStatus_NoError; +} diff --git a/mDNSPosix/mDNSPosix.h b/mDNSPosix/mDNSPosix.h index dab4b11..f9fcea7 100755 --- a/mDNSPosix/mDNSPosix.h +++ b/mDNSPosix/mDNSPosix.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,7 +22,7 @@ #include #ifdef __cplusplus - extern "C" { +extern "C" { #endif // PosixNetworkInterface is a record extension of the core NetworkInterfaceInfo @@ -34,32 +34,32 @@ typedef struct PosixNetworkInterface PosixNetworkInterface; struct PosixNetworkInterface - { - NetworkInterfaceInfo coreIntf; - const char * intfName; - PosixNetworkInterface * aliasIntf; - int index; - int multicastSocket4; +{ + NetworkInterfaceInfo coreIntf; + const char * intfName; + PosixNetworkInterface * aliasIntf; + int index; + int multicastSocket4; #if HAVE_IPV6 - int multicastSocket6; + int multicastSocket6; #endif - }; +}; // This is a global because debugf_() needs to be able to check its value extern int gMDNSPlatformPosixVerboseLevel; struct mDNS_PlatformSupport_struct - { - int unicastSocket4; +{ + int unicastSocket4; #if HAVE_IPV6 - int unicastSocket6; + int unicastSocket6; #endif - }; +}; #define uDNS_SERVERS_FILE "/etc/resolv.conf" extern int ParseDNSServers(mDNS *m, const char *filePath); extern mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m); - // See comment in implementation. +// See comment in implementation. // Call mDNSPosixGetFDSet before calling select(), to update the parameters // as may be necessary to meet the needs of the mDNSCore code. @@ -70,7 +70,7 @@ extern mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m); extern void mDNSPosixGetFDSet(mDNS *m, int *nfds, fd_set *readfds, struct timeval *timeout); extern void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds); -typedef void (*mDNSPosixEventCallback)(int fd, short filter, void *context); +typedef void (*mDNSPosixEventCallback)(int fd, short filter, void *context); extern mStatus mDNSPosixAddFDToEventLoop( int fd, mDNSPosixEventCallback callback, void *context); extern mStatus mDNSPosixRemoveFDFromEventLoop( int fd); @@ -79,7 +79,7 @@ extern mStatus mDNSPosixIgnoreSignalInEventLoop( int signum); extern mStatus mDNSPosixRunEventLoopOnce( mDNS *m, const struct timeval *pTimeout, sigset_t *pSignalsReceived, mDNSBool *pDataDispatched); #ifdef __cplusplus - } +} #endif #endif diff --git a/mDNSPosix/mDNSUNP.c b/mDNSPosix/mDNSUNP.c index e8fc649..b392fc7 100755 --- a/mDNSPosix/mDNSUNP.c +++ b/mDNSPosix/mDNSUNP.c @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -31,24 +31,24 @@ macro, usually defined in or someplace like that, to make sure the CMSG_NXTHDR macro is well-formed. On such platforms, the symbol NEED_ALIGN_MACRO should be set to the name of the header to include to get the ALIGN(P) macro. -*/ + */ #ifdef NEED_ALIGN_MACRO #include NEED_ALIGN_MACRO #endif -/* Solaris defined SIOCGIFCONF etc in but - other platforms don't even have that include file. So, - if we haven't yet got a definition, let's try to find +/* Solaris defined SIOCGIFCONF etc in but + other platforms don't even have that include file. So, + if we haven't yet got a definition, let's try to find . -*/ + */ #ifndef SIOCGIFCONF #include #endif -/* sockaddr_dl is only referenced if we're using IP_RECVIF, +/* sockaddr_dl is only referenced if we're using IP_RECVIF, so only include the header in that case. -*/ + */ #ifdef IP_RECVIF #include @@ -66,150 +66,177 @@ /* Converts a prefix length to IPv6 network mask */ void plen_to_mask(int plen, char *addr) { - int i; - int colons=7; /* Number of colons in IPv6 address */ - int bits_in_block=16; /* Bits per IPv6 block */ - for(i=0;i<=colons;i++) { - int block, ones=0xffff, ones_in_block; - if (plen>bits_in_block) ones_in_block=bits_in_block; - else ones_in_block=plen; - block = ones & (ones << (bits_in_block-ones_in_block)); - i==0 ? sprintf(addr, "%x", block) : sprintf(addr, "%s:%x", addr, block); - plen -= ones_in_block; - } - } + int i; + int colons=7; /* Number of colons in IPv6 address */ + int bits_in_block=16; /* Bits per IPv6 block */ + for(i=0; i<=colons; i++) { + int block, ones=0xffff, ones_in_block; + if (plen>bits_in_block) ones_in_block=bits_in_block; + else ones_in_block=plen; + block = ones & (ones << (bits_in_block-ones_in_block)); + i==0 ? sprintf(addr, "%x", block) : sprintf(addr, "%s:%x", addr, block); + plen -= ones_in_block; + } +} /* Gets IPv6 interface information from the /proc filesystem in linux*/ struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases) - { - struct ifi_info *ifi, *ifihead, **ifipnext; - FILE *fp; - char addr[8][5]; - int flags, myflags, index, plen, scope; - char ifname[9], lastname[IFNAMSIZ]; - char addr6[32+7+1]; /* don't forget the seven ':' */ - struct addrinfo hints, *res0; - struct sockaddr_in6 *sin6; - struct in6_addr *addrptr; - int err; - - res0=NULL; - ifihead = NULL; - ifipnext = &ifihead; - lastname[0] = 0; - - if ((fp = fopen(PROC_IFINET6_PATH, "r")) != NULL) { - while (fscanf(fp, - "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %8s\n", - addr[0],addr[1],addr[2],addr[3], - addr[4],addr[5],addr[6],addr[7], - &index, &plen, &scope, &flags, ifname) != EOF) { - - myflags = 0; - if (strncmp(lastname, ifname, IFNAMSIZ) == 0) { - if (doaliases == 0) - continue; /* already processed this interface */ - myflags = IFI_ALIAS; - } - memcpy(lastname, ifname, IFNAMSIZ); - ifi = (struct ifi_info*)calloc(1, sizeof(struct ifi_info)); - if (ifi == NULL) { - goto gotError; - } - - *ifipnext = ifi; /* prev points to this new one */ - ifipnext = &ifi->ifi_next; /* pointer to next one goes here */ - - sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s", - addr[0],addr[1],addr[2],addr[3], - addr[4],addr[5],addr[6],addr[7]); - - /* Add address of the interface */ - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET6; - hints.ai_flags = AI_NUMERICHOST; - err = getaddrinfo(addr6, NULL, &hints, &res0); - if (err) { - goto gotError; - } - ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in6)); - if (ifi->ifi_addr == NULL) { - goto gotError; - } - memcpy(ifi->ifi_addr, res0->ai_addr, sizeof(struct sockaddr_in6)); - - /* Add netmask of the interface */ - char ipv6addr[INET6_ADDRSTRLEN]; - plen_to_mask(plen, ipv6addr); - ifi->ifi_netmask = calloc(1, sizeof(struct sockaddr_in6)); - if (ifi->ifi_addr == NULL) { - goto gotError; - } - sin6=calloc(1, sizeof(struct sockaddr_in6)); - addrptr=calloc(1, sizeof(struct in6_addr)); - inet_pton(family, ipv6addr, addrptr); - sin6->sin6_family=family; - sin6->sin6_addr=*addrptr; - sin6->sin6_scope_id=scope; - memcpy(ifi->ifi_netmask, sin6, sizeof(struct sockaddr_in6)); - free(sin6); - - - /* Add interface name */ - memcpy(ifi->ifi_name, ifname, IFI_NAME); - - /* Add interface index */ - ifi->ifi_index = index; - - /* If interface is in /proc then it is up*/ - ifi->ifi_flags = IFF_UP; - - freeaddrinfo(res0); - res0=NULL; - } - } - goto done; - - gotError: - if (ifihead != NULL) { - free_ifi_info(ifihead); - ifihead = NULL; - } - if (res0 != NULL) { - freeaddrinfo(res0); - res0=NULL; - } - done: - return(ifihead); /* pointer to first structure in linked list */ - } +{ + struct ifi_info *ifi, *ifihead, **ifipnext, *ifipold, **ifiptr; + FILE *fp; + char addr[8][5]; + int flags, myflags, index, plen, scope; + char ifname[9], lastname[IFNAMSIZ]; + char addr6[32+7+1]; /* don't forget the seven ':' */ + struct addrinfo hints, *res0; + struct sockaddr_in6 *sin6; + struct in6_addr *addrptr; + int err; + int sockfd = -1; + struct ifreq ifr; + + res0=NULL; + ifihead = NULL; + ifipnext = &ifihead; + lastname[0] = 0; + + if ((fp = fopen(PROC_IFINET6_PATH, "r")) != NULL) { + sockfd = socket(AF_INET6, SOCK_DGRAM, 0); + if (sockfd < 0) { + goto gotError; + } + while (fscanf(fp, + "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %8s\n", + addr[0],addr[1],addr[2],addr[3], + addr[4],addr[5],addr[6],addr[7], + &index, &plen, &scope, &flags, ifname) != EOF) { + + myflags = 0; + if (strncmp(lastname, ifname, IFNAMSIZ) == 0) { + if (doaliases == 0) + continue; /* already processed this interface */ + myflags = IFI_ALIAS; + } + memcpy(lastname, ifname, IFNAMSIZ); + ifi = (struct ifi_info*)calloc(1, sizeof(struct ifi_info)); + if (ifi == NULL) { + goto gotError; + } + + ifipold = *ifipnext; /* need this later */ + ifiptr = ifipnext; + *ifipnext = ifi; /* prev points to this new one */ + ifipnext = &ifi->ifi_next; /* pointer to next one goes here */ + + sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s", + addr[0],addr[1],addr[2],addr[3], + addr[4],addr[5],addr[6],addr[7]); + + /* Add address of the interface */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_flags = AI_NUMERICHOST; + err = getaddrinfo(addr6, NULL, &hints, &res0); + if (err) { + goto gotError; + } + ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in6)); + if (ifi->ifi_addr == NULL) { + goto gotError; + } + memcpy(ifi->ifi_addr, res0->ai_addr, sizeof(struct sockaddr_in6)); + + /* Add netmask of the interface */ + char ipv6addr[INET6_ADDRSTRLEN]; + plen_to_mask(plen, ipv6addr); + ifi->ifi_netmask = calloc(1, sizeof(struct sockaddr_in6)); + if (ifi->ifi_addr == NULL) { + goto gotError; + } + sin6=calloc(1, sizeof(struct sockaddr_in6)); + addrptr=calloc(1, sizeof(struct in6_addr)); + inet_pton(family, ipv6addr, addrptr); + sin6->sin6_family=family; + sin6->sin6_addr=*addrptr; + sin6->sin6_scope_id=scope; + memcpy(ifi->ifi_netmask, sin6, sizeof(struct sockaddr_in6)); + free(sin6); + + + /* Add interface name */ + memcpy(ifi->ifi_name, ifname, IFI_NAME); + + /* Add interface index */ + ifi->ifi_index = index; + + /* Add interface flags*/ + memcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) { + if (errno == EADDRNOTAVAIL) { + /* + * If the main interface is configured with no IP address but + * an alias interface exists with an IP address, you get + * EADDRNOTAVAIL for the main interface + */ + free(ifi->ifi_addr); + free(ifi); + ifipnext = ifiptr; + *ifipnext = ifipold; + continue; + } else { + goto gotError; + } + } + ifi->ifi_flags = ifr.ifr_flags; + freeaddrinfo(res0); + res0=NULL; + } + } + goto done; + +gotError: + if (ifihead != NULL) { + free_ifi_info(ifihead); + ifihead = NULL; + } + if (res0 != NULL) { + freeaddrinfo(res0); + res0=NULL; + } +done: + if (sockfd != -1) { + assert(close(sockfd) == 0); + } + return(ifihead); /* pointer to first structure in linked list */ +} #endif // defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX struct ifi_info *get_ifi_info(int family, int doaliases) { - int junk; - struct ifi_info *ifi, *ifihead, **ifipnext; - int sockfd, sockf6, len, lastlen, flags, myflags; + int junk; + struct ifi_info *ifi, *ifihead, **ifipnext, *ifipold, **ifiptr; + int sockfd, sockf6, len, lastlen, flags, myflags; #ifdef NOT_HAVE_IF_NAMETOINDEX - int index = 200; + int index = 200; #endif char *ptr, *buf, lastname[IFNAMSIZ], *cptr; - struct ifconf ifc; + struct ifconf ifc; struct ifreq *ifr, ifrcopy; struct sockaddr_in *sinptr; - + #if defined(AF_INET6) && HAVE_IPV6 struct sockaddr_in6 *sinptr6; #endif #if defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX - if (family == AF_INET6) return get_ifi_info_linuxv6(family, doaliases); + if (family == AF_INET6) return get_ifi_info_linuxv6(family, doaliases); #endif - sockfd = -1; + sockfd = -1; sockf6 = -1; buf = NULL; ifihead = NULL; - + sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { goto gotError; @@ -252,7 +279,7 @@ struct ifi_info *get_ifi_info(int family, int doaliases) ptr += sizeof(ifr->ifr_name) + GET_SA_LEN(ifr->ifr_addr); // fprintf(stderr, "intf %p name=%s AF=%d\n", index, ifr->ifr_name, ifr->ifr_addr.sa_family); - + if (ifr->ifr_addr.sa_family != family) continue; /* ignore if not desired address family */ @@ -270,7 +297,7 @@ struct ifi_info *get_ifi_info(int family, int doaliases) if (ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy) < 0) { goto gotError; } - + flags = ifrcopy.ifr_flags; if ((flags & IFF_UP) == 0) continue; /* ignore if interface not up */ @@ -279,8 +306,10 @@ struct ifi_info *get_ifi_info(int family, int doaliases) if (ifi == NULL) { goto gotError; } - *ifipnext = ifi; /* prev points to this new one */ - ifipnext = &ifi->ifi_next; /* pointer to next one goes here */ + ifipold = *ifipnext; /* need this later */ + ifiptr = ifipnext; + *ifipnext = ifi; /* prev points to this new one */ + ifipnext = &ifi->ifi_next; /* pointer to next one goes here */ ifi->ifi_flags = flags; /* IFF_xxx values */ ifi->ifi_myflags = myflags; /* IFI_xxx values */ @@ -289,11 +318,11 @@ struct ifi_info *get_ifi_info(int family, int doaliases) #else ifrcopy = *ifr; #ifdef SIOCGIFINDEX - if ( 0 >= ioctl(sockfd, SIOCGIFINDEX, &ifrcopy)) + if ( 0 >= ioctl(sockfd, SIOCGIFINDEX, &ifrcopy)) ifi->ifi_index = ifrcopy.ifr_index; else #endif - ifi->ifi_index = index++; /* SIOCGIFINDEX is broken on Solaris 2.5ish, so fake it */ + ifi->ifi_index = index++; /* SIOCGIFINDEX is broken on Solaris 2.5ish, so fake it */ #endif memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME); ifi->ifi_name[IFI_NAME-1] = '\0'; @@ -310,16 +339,32 @@ struct ifi_info *get_ifi_info(int family, int doaliases) memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in)); #ifdef SIOCGIFNETMASK - if (ioctl(sockfd, SIOCGIFNETMASK, &ifrcopy) < 0) goto gotError; - ifi->ifi_netmask = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in)); - if (ifi->ifi_netmask == NULL) goto gotError; - sinptr = (struct sockaddr_in *) &ifrcopy.ifr_addr; - /* The BSD ioctls (including Mac OS X) stick some weird values in for sin_len and sin_family */ + if (ioctl(sockfd, SIOCGIFNETMASK, &ifrcopy) < 0) { + if (errno == EADDRNOTAVAIL) { + /* + * If the main interface is configured with no IP address but + * an alias interface exists with an IP address, you get + * EADDRNOTAVAIL for the main interface + */ + free(ifi->ifi_addr); + free(ifi); + ifipnext = ifiptr; + *ifipnext = ifipold; + continue; + } else { + goto gotError; + } + } + + ifi->ifi_netmask = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in)); + if (ifi->ifi_netmask == NULL) goto gotError; + sinptr = (struct sockaddr_in *) &ifrcopy.ifr_addr; + /* The BSD ioctls (including Mac OS X) stick some weird values in for sin_len and sin_family */ #ifndef NOT_HAVE_SA_LEN - sinptr->sin_len = sizeof(struct sockaddr_in); + sinptr->sin_len = sizeof(struct sockaddr_in); #endif - sinptr->sin_family = AF_INET; - memcpy(ifi->ifi_netmask, sinptr, sizeof(struct sockaddr_in)); + sinptr->sin_family = AF_INET; + memcpy(ifi->ifi_netmask, sinptr, sizeof(struct sockaddr_in)); #endif #ifdef SIOCGIFBRDADDR @@ -328,11 +373,11 @@ struct ifi_info *get_ifi_info(int family, int doaliases) goto gotError; } sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr; - /* The BSD ioctls (including Mac OS X) stick some weird values in for sin_len and sin_family */ + /* The BSD ioctls (including Mac OS X) stick some weird values in for sin_len and sin_family */ #ifndef NOT_HAVE_SA_LEN - sinptr->sin_len = sizeof( struct sockaddr_in ); + sinptr->sin_len = sizeof( struct sockaddr_in ); #endif - sinptr->sin_family = AF_INET; + sinptr->sin_family = AF_INET; ifi->ifi_brdaddr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in)); if (ifi->ifi_brdaddr == NULL) { goto gotError; @@ -349,9 +394,9 @@ struct ifi_info *get_ifi_info(int family, int doaliases) sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr; /* The BSD ioctls (including Mac OS X) stick some weird values in for sin_len and sin_family */ #ifndef NOT_HAVE_SA_LEN - sinptr->sin_len = sizeof( struct sockaddr_in ); + sinptr->sin_len = sizeof( struct sockaddr_in ); #endif - sinptr->sin_family = AF_INET; + sinptr->sin_family = AF_INET; ifi->ifi_dstaddr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in)); if (ifi->ifi_dstaddr == NULL) { goto gotError; @@ -370,27 +415,42 @@ struct ifi_info *get_ifi_info(int family, int doaliases) if (ifi->ifi_addr == NULL) { goto gotError; } - + /* Some platforms (*BSD) inject the prefix in IPv6LL addresses */ /* We need to strip that out */ if (IN6_IS_ADDR_LINKLOCAL(&sinptr6->sin6_addr)) - sinptr6->sin6_addr.s6_addr[2] = sinptr6->sin6_addr.s6_addr[3] = 0; + sinptr6->sin6_addr.s6_addr[2] = sinptr6->sin6_addr.s6_addr[3] = 0; memcpy(ifi->ifi_addr, sinptr6, sizeof(struct sockaddr_in6)); #ifdef SIOCGIFNETMASK_IN6 - { - struct in6_ifreq ifr6; - if (sockf6 == -1) - sockf6 = socket(AF_INET6, SOCK_DGRAM, 0); - memset(&ifr6, 0, sizeof(ifr6)); - memcpy(&ifr6.ifr_name, &ifr->ifr_name, sizeof(ifr6.ifr_name )); - memcpy(&ifr6.ifr_ifru.ifru_addr, &ifr->ifr_addr, sizeof(ifr6.ifr_ifru.ifru_addr)); - if (ioctl(sockf6, SIOCGIFNETMASK_IN6, &ifr6) < 0) goto gotError; - ifi->ifi_netmask = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in6)); - if (ifi->ifi_netmask == NULL) goto gotError; - sinptr6 = (struct sockaddr_in6 *) &ifr6.ifr_ifru.ifru_addr; - memcpy(ifi->ifi_netmask, sinptr6, sizeof(struct sockaddr_in6)); - } + { + struct in6_ifreq ifr6; + if (sockf6 == -1) + sockf6 = socket(AF_INET6, SOCK_DGRAM, 0); + memset(&ifr6, 0, sizeof(ifr6)); + memcpy(&ifr6.ifr_name, &ifr->ifr_name, sizeof(ifr6.ifr_name )); + memcpy(&ifr6.ifr_ifru.ifru_addr, &ifr->ifr_addr, sizeof(ifr6.ifr_ifru.ifru_addr)); + if (ioctl(sockf6, SIOCGIFNETMASK_IN6, &ifr6) < 0) { + if (errno == EADDRNOTAVAIL) { + /* + * If the main interface is configured with no IP address but + * an alias interface exists with an IP address, you get + * EADDRNOTAVAIL for the main interface + */ + free(ifi->ifi_addr); + free(ifi); + ifipnext = ifiptr; + *ifipnext = ifipold; + continue; + } else { + goto gotError; + } + } + ifi->ifi_netmask = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in6)); + if (ifi->ifi_netmask == NULL) goto gotError; + sinptr6 = (struct sockaddr_in6 *) &ifr6.ifr_ifru.ifru_addr; + memcpy(ifi->ifi_netmask, sinptr6, sizeof(struct sockaddr_in6)); + } #endif } break; @@ -401,7 +461,7 @@ struct ifi_info *get_ifi_info(int family, int doaliases) } } goto done; - + gotError: if (ifihead != NULL) { free_ifi_info(ifihead); @@ -445,22 +505,22 @@ free_ifi_info(struct ifi_info *ifihead) } /* end free_ifi_info */ -ssize_t +ssize_t recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp, struct sockaddr *sa, socklen_t *salenptr, struct my_in_pktinfo *pktp, u_char *ttl) { - struct msghdr msg; - struct iovec iov[1]; - ssize_t n; + struct msghdr msg; + struct iovec iov[1]; + ssize_t n; #ifdef CMSG_FIRSTHDR struct cmsghdr *cmptr; union { - struct cmsghdr cm; - char control[1024]; + struct cmsghdr cm; + char control[1024]; } control_un; - *ttl = 255; // If kernel fails to provide TTL data then assume the TTL was 255 as it should be + *ttl = 255; // If kernel fails to provide TTL data then assume the TTL was 255 as it should be msg.msg_control = control_un.control; msg.msg_controllen = sizeof(control_un.control); @@ -482,12 +542,12 @@ recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp, *salenptr = msg.msg_namelen; /* pass back results */ if (pktp) { /* 0.0.0.0, i/f = -1 */ - /* We set the interface to -1 so that the caller can - tell whether we returned a meaningful value or - just some default. Previously this code just - set the value to 0, but I'm concerned that 0 + /* We set the interface to -1 so that the caller can + tell whether we returned a meaningful value or + just some default. Previously this code just + set the value to 0, but I'm concerned that 0 might be a valid interface value. - */ + */ memset(pktp, 0, sizeof(struct my_in_pktinfo)); pktp->ipi_ifindex = -1; } @@ -495,7 +555,7 @@ recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp, /* include recvfrom_flags2 */ #ifndef CMSG_FIRSTHDR - #warning CMSG_FIRSTHDR not defined. Will not be able to determine destination address, received interface, etc. + #warning CMSG_FIRSTHDR not defined. Will not be able to determine destination address, received interface, etc. *flagsp = 0; /* pass back results */ return(n); #else @@ -510,18 +570,18 @@ recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp, #ifdef IP_PKTINFO #if in_pktinfo_definition_is_missing -struct in_pktinfo -{ - int ipi_ifindex; - struct in_addr ipi_spec_dst; - struct in_addr ipi_addr; -}; + struct in_pktinfo + { + int ipi_ifindex; + struct in_addr ipi_spec_dst; + struct in_addr ipi_addr; + }; #endif - if (cmptr->cmsg_level == IPPROTO_IP && + if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO) { struct in_pktinfo *tmp; struct sockaddr_in *sin = (struct sockaddr_in*)&pktp->ipi_addr; - + tmp = (struct in_pktinfo *) CMSG_DATA(cmptr); sin->sin_family = AF_INET; sin->sin_addr = tmp->ipi_addr; @@ -535,7 +595,7 @@ struct in_pktinfo if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) { struct sockaddr_in *sin = (struct sockaddr_in*)&pktp->ipi_addr; - + sin->sin_family = AF_INET; sin->sin_addr = *(struct in_addr*)CMSG_DATA(cmptr); sin->sin_port = 0; @@ -553,10 +613,10 @@ struct in_pktinfo #endif pktp->ipi_ifindex = sdl->sdl_index; #ifdef HAVE_BROKEN_RECVIF_NAME - if (sdl->sdl_index == 0) { - pktp->ipi_ifindex = *(uint_t*)sdl; - } -#endif + if (sdl->sdl_index == 0) { + pktp->ipi_ifindex = *(uint_t*)sdl; + } +#endif assert(pktp->ipi_ifname[IFI_NAME - 1] == 0); // null terminated because of memset above continue; @@ -566,22 +626,22 @@ struct in_pktinfo #ifdef IP_RECVTTL if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVTTL) { - *ttl = *(u_char*)CMSG_DATA(cmptr); + *ttl = *(u_char*)CMSG_DATA(cmptr); continue; } else if (cmptr->cmsg_level == IPPROTO_IP && - cmptr->cmsg_type == IP_TTL) { // some implementations seem to send IP_TTL instead of IP_RECVTTL - *ttl = *(int*)CMSG_DATA(cmptr); + cmptr->cmsg_type == IP_TTL) { // some implementations seem to send IP_TTL instead of IP_RECVTTL + *ttl = *(int*)CMSG_DATA(cmptr); continue; } #endif #if defined(IPV6_PKTINFO) && HAVE_IPV6 - if (cmptr->cmsg_level == IPPROTO_IPV6 && - cmptr->cmsg_type == IPV6_PKTINFO) { + if (cmptr->cmsg_level == IPPROTO_IPV6 && + cmptr->cmsg_type == IPV6_2292_PKTINFO) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&pktp->ipi_addr; - struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmptr); - + struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmptr); + sin6->sin6_family = AF_INET6; #ifndef NOT_HAVE_SA_LEN sin6->sin6_len = sizeof(*sin6); @@ -590,15 +650,15 @@ struct in_pktinfo sin6->sin6_flowinfo = 0; sin6->sin6_scope_id = 0; sin6->sin6_port = 0; - pktp->ipi_ifindex = ip6_info->ipi6_ifindex; + pktp->ipi_ifindex = ip6_info->ipi6_ifindex; continue; } #endif #if defined(IPV6_HOPLIMIT) && HAVE_IPV6 - if (cmptr->cmsg_level == IPPROTO_IPV6 && - cmptr->cmsg_type == IPV6_HOPLIMIT) { - *ttl = *(int*)CMSG_DATA(cmptr); + if (cmptr->cmsg_level == IPPROTO_IPV6 && + cmptr->cmsg_type == IPV6_2292_HOPLIMIT) { + *ttl = *(int*)CMSG_DATA(cmptr); continue; } #endif @@ -619,41 +679,41 @@ struct in_pktinfo #include int daemon(int nochdir, int noclose) +{ + switch (fork()) { - switch (fork()) - { - case -1: return (-1); // Fork failed - case 0: break; // Child -- continue - default: _exit(0); // Parent -- exit - } - - if (setsid() == -1) return(-1); - - signal(SIGHUP, SIG_IGN); - - switch (fork()) // Fork again, primarily for reasons of Unix trivia - { - case -1: return (-1); // Fork failed - case 0: break; // Child -- continue - default: _exit(0); // Parent -- exit - } - - if (!nochdir) (void)chdir("/"); - umask(0); - - if (!noclose) - { - int fd = open("/dev/null", O_RDWR, 0); - if (fd != -1) - { - // Avoid unnecessarily duplicating a file descriptor to itself - if (fd != STDIN_FILENO) (void)dup2(fd, STDIN_FILENO); - if (fd != STDOUT_FILENO) (void)dup2(fd, STDOUT_FILENO); - if (fd != STDERR_FILENO) (void)dup2(fd, STDERR_FILENO); - if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) - (void)close (fd); - } - } - return (0); + case -1: return (-1); // Fork failed + case 0: break; // Child -- continue + default: _exit(0); // Parent -- exit } + + if (setsid() == -1) return(-1); + + signal(SIGHUP, SIG_IGN); + + switch (fork()) // Fork again, primarily for reasons of Unix trivia + { + case -1: return (-1); // Fork failed + case 0: break; // Child -- continue + default: _exit(0); // Parent -- exit + } + + if (!nochdir) (void)chdir("/"); + umask(0); + + if (!noclose) + { + int fd = open("/dev/null", O_RDWR, 0); + if (fd != -1) + { + // Avoid unnecessarily duplicating a file descriptor to itself + if (fd != STDIN_FILENO) (void)dup2(fd, STDIN_FILENO); + if (fd != STDOUT_FILENO) (void)dup2(fd, STDOUT_FILENO); + if (fd != STDERR_FILENO) (void)dup2(fd, STDERR_FILENO); + if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) + (void)close (fd); + } + } + return (0); +} #endif /* NOT_HAVE_DAEMON */ diff --git a/mDNSPosix/mDNSUNP.h b/mDNSPosix/mDNSUNP.h index 59b5501..cc81b7d 100755 --- a/mDNSPosix/mDNSUNP.h +++ b/mDNSPosix/mDNSUNP.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,14 +25,23 @@ #ifdef HAVE_LINUX #include +#define IPV6_2292_PKTINFO IPV6_2292PKTINFO +#define IPV6_2292_HOPLIMIT IPV6_2292HOPLIMIT +#else +// The following are the supported non-linux posix OSes - +// netbsd, freebsd and openbsd. +#if HAVE_IPV6 +#define IPV6_2292_PKTINFO 19 +#define IPV6_2292_HOPLIMIT 20 +#endif #endif #ifdef __cplusplus - extern "C" { +extern "C" { #endif #ifdef NOT_HAVE_SOCKLEN_T - typedef unsigned int socklen_t; +typedef unsigned int socklen_t; #endif #if !defined(_SS_MAXSIZE) @@ -40,7 +49,7 @@ #define sockaddr_storage sockaddr_in6 #else #define sockaddr_storage sockaddr -#endif // HAVE_IPV6 +#endif // HAVE_IPV6 #endif // !defined(_SS_MAXSIZE) #ifndef NOT_HAVE_SA_LEN @@ -60,8 +69,8 @@ struct my_in_pktinfo { struct sockaddr_storage ipi_addr; - int ipi_ifindex; /* received interface index */ - char ipi_ifname[IFI_NAME]; /* received interface name */ + int ipi_ifindex; /* received interface index */ + char ipi_ifname[IFI_NAME]; /* received interface name */ }; /* From the text (Stevens, section 20.2): */ @@ -71,33 +80,33 @@ struct my_in_pktinfo { /* 2. the destination addres of the received datagram (from the IP_RECVDSTADDR socket option, and */ /* 3. the index of the interface on which the datagram was received (the IP_RECVIF socket option).' */ extern ssize_t recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp, - struct sockaddr *sa, socklen_t *salenptr, struct my_in_pktinfo *pktp, u_char *ttl); + struct sockaddr *sa, socklen_t *salenptr, struct my_in_pktinfo *pktp, u_char *ttl); struct ifi_info { - char ifi_name[IFI_NAME]; /* interface name, null terminated */ - u_char ifi_haddr[IFI_HADDR]; /* hardware address */ - u_short ifi_hlen; /* #bytes in hardware address: 0, 6, 8 */ - short ifi_flags; /* IFF_xxx constants from */ - short ifi_myflags; /* our own IFI_xxx flags */ - int ifi_index; /* interface index */ - struct sockaddr *ifi_addr; /* primary address */ - struct sockaddr *ifi_netmask; - struct sockaddr *ifi_brdaddr;/* broadcast address */ - struct sockaddr *ifi_dstaddr;/* destination address */ - struct ifi_info *ifi_next; /* next of these structures */ + char ifi_name[IFI_NAME]; /* interface name, null terminated */ + u_char ifi_haddr[IFI_HADDR]; /* hardware address */ + u_short ifi_hlen; /* #bytes in hardware address: 0, 6, 8 */ + short ifi_flags; /* IFF_xxx constants from */ + short ifi_myflags; /* our own IFI_xxx flags */ + int ifi_index; /* interface index */ + struct sockaddr *ifi_addr; /* primary address */ + struct sockaddr *ifi_netmask; + struct sockaddr *ifi_brdaddr; /* broadcast address */ + struct sockaddr *ifi_dstaddr; /* destination address */ + struct ifi_info *ifi_next; /* next of these structures */ }; #if defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX #define PROC_IFINET6_PATH "/proc/net/if_inet6" extern struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases); #endif - + #if defined(AF_INET6) && HAVE_IPV6 #define INET6_ADDRSTRLEN 46 /*Maximum length of IPv6 address */ #endif - - + + #define IFI_ALIAS 1 /* ifi_addr is an alias */ /* From the text (Stevens, section 16.6): */ @@ -115,7 +124,7 @@ extern int daemon(int nochdir, int noclose); #endif #ifdef __cplusplus - } +} #endif #endif diff --git a/mDNSPosix/nss_mdns.c b/mDNSPosix/nss_mdns.c index 5af2f8e..afadb3c 100755 --- a/mDNSPosix/nss_mdns.c +++ b/mDNSPosix/nss_mdns.c @@ -1,72 +1,72 @@ /* -NICTA Public Software Licence -Version 1.0 - -Copyright © 2004 National ICT Australia Ltd - -All rights reserved. - -By this licence, National ICT Australia Ltd (NICTA) grants permission, -free of charge, to any person who obtains a copy of this software -and any associated documentation files ("the Software") to use and -deal with the Software in source code and binary forms without -restriction, with or without modification, and to permit persons -to whom the Software is furnished to do so, provided that the -following conditions are met: - -- Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimers. -- Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimers in - the documentation and/or other materials provided with the - distribution. -- The name of NICTA may not be used to endorse or promote products - derived from this Software without specific prior written permission. - -EXCEPT AS EXPRESSLY STATED IN THIS LICENCE AND TO THE FULL EXTENT -PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED "AS-IS" AND -NICTA MAKES NO REPRESENTATIONS, WARRANTIES OR CONDITIONS OF ANY -KIND, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY -REPRESENTATIONS, WARRANTIES OR CONDITIONS REGARDING THE CONTENTS -OR ACCURACY OF THE SOFTWARE, OR OF TITLE, MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, THE ABSENCE OF LATENT -OR OTHER DEFECTS, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR -NOT DISCOVERABLE. - -TO THE FULL EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL -NICTA BE LIABLE ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, -NEGLIGENCE) FOR ANY LOSS OR DAMAGE WHATSOEVER, INCLUDING (WITHOUT -LIMITATION) LOSS OF PRODUCTION OR OPERATION TIME, LOSS, DAMAGE OR -CORRUPTION OF DATA OR RECORDS; OR LOSS OF ANTICIPATED SAVINGS, -OPPORTUNITY, REVENUE, PROFIT OR GOODWILL, OR OTHER ECONOMIC LOSS; -OR ANY SPECIAL, INCIDENTAL, INDIRECT, CONSEQUENTIAL, PUNITIVE OR -EXEMPLARY DAMAGES ARISING OUT OF OR IN CONNECTION WITH THIS LICENCE, -THE SOFTWARE OR THE USE OF THE SOFTWARE, EVEN IF NICTA HAS BEEN -ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -If applicable legislation implies warranties or conditions, or -imposes obligations or liability on NICTA in respect of the Software -that cannot be wholly or partly excluded, restricted or modified, -NICTA's liability is limited, to the full extent permitted by the -applicable legislation, at its option, to: - -a. in the case of goods, any one or more of the following: - i. the replacement of the goods or the supply of equivalent goods; - ii. the repair of the goods; - iii. the payment of the cost of replacing the goods or of acquiring + NICTA Public Software Licence + Version 1.0 + + Copyright © 2004 National ICT Australia Ltd + + All rights reserved. + + By this licence, National ICT Australia Ltd (NICTA) grants permission, + free of charge, to any person who obtains a copy of this software + and any associated documentation files ("the Software") to use and + deal with the Software in source code and binary forms without + restriction, with or without modification, and to permit persons + to whom the Software is furnished to do so, provided that the + following conditions are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimers. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimers in + the documentation and/or other materials provided with the + distribution. + - The name of NICTA may not be used to endorse or promote products + derived from this Software without specific prior written permission. + + EXCEPT AS EXPRESSLY STATED IN THIS LICENCE AND TO THE FULL EXTENT + PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED "AS-IS" AND + NICTA MAKES NO REPRESENTATIONS, WARRANTIES OR CONDITIONS OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY + REPRESENTATIONS, WARRANTIES OR CONDITIONS REGARDING THE CONTENTS + OR ACCURACY OF THE SOFTWARE, OR OF TITLE, MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, THE ABSENCE OF LATENT + OR OTHER DEFECTS, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR + NOT DISCOVERABLE. + + TO THE FULL EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL + NICTA BE LIABLE ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) FOR ANY LOSS OR DAMAGE WHATSOEVER, INCLUDING (WITHOUT + LIMITATION) LOSS OF PRODUCTION OR OPERATION TIME, LOSS, DAMAGE OR + CORRUPTION OF DATA OR RECORDS; OR LOSS OF ANTICIPATED SAVINGS, + OPPORTUNITY, REVENUE, PROFIT OR GOODWILL, OR OTHER ECONOMIC LOSS; + OR ANY SPECIAL, INCIDENTAL, INDIRECT, CONSEQUENTIAL, PUNITIVE OR + EXEMPLARY DAMAGES ARISING OUT OF OR IN CONNECTION WITH THIS LICENCE, + THE SOFTWARE OR THE USE OF THE SOFTWARE, EVEN IF NICTA HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + If applicable legislation implies warranties or conditions, or + imposes obligations or liability on NICTA in respect of the Software + that cannot be wholly or partly excluded, restricted or modified, + NICTA's liability is limited, to the full extent permitted by the + applicable legislation, at its option, to: + + a. in the case of goods, any one or more of the following: + i. the replacement of the goods or the supply of equivalent goods; + ii. the repair of the goods; + iii. the payment of the cost of replacing the goods or of acquiring equivalent goods; - iv. the payment of the cost of having the goods repaired; or -b. in the case of services: - i. the supplying of the services again; or - ii. the payment of the cost of having the services supplied + iv. the payment of the cost of having the goods repaired; or + b. in the case of services: + i. the supplying of the services again; or + ii. the payment of the cost of having the services supplied again. */ /* - NSSwitch Implementation of mDNS interface. - - Andrew White (Andrew.White@nicta.com.au) - May 2004 + NSSwitch Implementation of mDNS interface. + + Andrew White (Andrew.White@nicta.com.au) + May 2004 */ #include @@ -94,284 +94,284 @@ b. in the case of services: // Public functions /* - Count the number of dots in a name string. + Count the number of dots in a name string. */ int count_dots (const char * name); /* - Test whether a domain name is local. + Test whether a domain name is local. - Returns - 1 if name ends with ".local" or ".local." - 0 otherwise + Returns + 1 if name ends with ".local" or ".local." + 0 otherwise */ int islocal (const char * name); /* - Format an address structure as a string appropriate for DNS reverse (PTR) - lookup, based on address type. - - Parameters - prefixlen - Prefix length, in bits. When formatting, this will be rounded up - to the nearest appropriate size. If -1, assume maximum. - buf - Output buffer. Must be long enough to hold largest possible - output. - Returns - Pointer to (first character of) output buffer, - or NULL on error. + Format an address structure as a string appropriate for DNS reverse (PTR) + lookup, based on address type. + + Parameters + prefixlen + Prefix length, in bits. When formatting, this will be rounded up + to the nearest appropriate size. If -1, assume maximum. + buf + Output buffer. Must be long enough to hold largest possible + output. + Returns + Pointer to (first character of) output buffer, + or NULL on error. */ char * format_reverse_addr (int af, const void * addr, int prefixlen, char * buf); /* - Format an address structure as a string appropriate for DNS reverse (PTR) - lookup for AF_INET. Output is in .in-addr.arpa domain. - - Parameters - prefixlen - Prefix length, in bits. When formatting, this will be rounded up - to the nearest byte (8). If -1, assume 32. - buf - Output buffer. Must be long enough to hold largest possible - output. For AF_INET, this is 29 characters (including null). - Returns - Pointer to (first character of) output buffer, - or NULL on error. + Format an address structure as a string appropriate for DNS reverse (PTR) + lookup for AF_INET. Output is in .in-addr.arpa domain. + + Parameters + prefixlen + Prefix length, in bits. When formatting, this will be rounded up + to the nearest byte (8). If -1, assume 32. + buf + Output buffer. Must be long enough to hold largest possible + output. For AF_INET, this is 29 characters (including null). + Returns + Pointer to (first character of) output buffer, + or NULL on error. */ char * format_reverse_addr_in ( - const struct in_addr * addr, - int prefixlen, - char * buf -); + const struct in_addr * addr, + int prefixlen, + char * buf + ); #define DNS_PTR_AF_INET_SIZE 29 /* - Format an address structure as a string appropriate for DNS reverse (PTR) - lookup for AF_INET6. Output is in .ip6.arpa domain. - - Parameters - prefixlen - Prefix length, in bits. When formatting, this will be rounded up - to the nearest nibble (4). If -1, assume 128. - buf - Output buffer. Must be long enough to hold largest possible - output. For AF_INET6, this is 72 characters (including null). - Returns - Pointer to (first character of) output buffer, - or NULL on error. + Format an address structure as a string appropriate for DNS reverse (PTR) + lookup for AF_INET6. Output is in .ip6.arpa domain. + + Parameters + prefixlen + Prefix length, in bits. When formatting, this will be rounded up + to the nearest nibble (4). If -1, assume 128. + buf + Output buffer. Must be long enough to hold largest possible + output. For AF_INET6, this is 72 characters (including null). + Returns + Pointer to (first character of) output buffer, + or NULL on error. */ char * format_reverse_addr_in6 ( - const struct in6_addr * addr, - int prefixlen, - char * buf -); + const struct in6_addr * addr, + int prefixlen, + char * buf + ); #define DNS_PTR_AF_INET6_SIZE 72 /* - Compare whether the given dns name has the given domain suffix. - A single leading '.' on the name or leading or trailing '.' on the - domain is ignored for the purposes of the comparison. - Multiple leading or trailing '.'s are an error. Other DNS syntax - errors are not checked for. The comparison is case insensitive. - - Returns - 1 on success (match) - 0 on failure (no match) - < 0 on error + Compare whether the given dns name has the given domain suffix. + A single leading '.' on the name or leading or trailing '.' on the + domain is ignored for the purposes of the comparison. + Multiple leading or trailing '.'s are an error. Other DNS syntax + errors are not checked for. The comparison is case insensitive. + + Returns + 1 on success (match) + 0 on failure (no match) + < 0 on error */ int cmp_dns_suffix (const char * name, const char * domain); enum { - CMP_DNS_SUFFIX_SUCCESS = 1, - CMP_DNS_SUFFIX_FAILURE = 0, - CMP_DNS_SUFFIX_BAD_NAME = 1, - CMP_DNS_SUFFIX_BAD_DOMAIN = -2 + CMP_DNS_SUFFIX_SUCCESS = 1, + CMP_DNS_SUFFIX_FAILURE = 0, + CMP_DNS_SUFFIX_BAD_NAME = 1, + CMP_DNS_SUFFIX_BAD_DOMAIN = -2 }; typedef int ns_type_t; typedef int ns_class_t; /* - Convert a DNS resource record (RR) code to an address family (AF) code. - - Parameters - rrtype - resource record type (from nameser.h) - - Returns - Appropriate AF code (from socket.h), or AF_UNSPEC if an appropriate - mapping couldn't be determined + Convert a DNS resource record (RR) code to an address family (AF) code. + + Parameters + rrtype + resource record type (from nameser.h) + + Returns + Appropriate AF code (from socket.h), or AF_UNSPEC if an appropriate + mapping couldn't be determined */ int rr_to_af (ns_type_t rrtype); /* - Convert an address family (AF) code to a DNS resource record (RR) code. - - Parameters - int - address family code (from socket.h) - Returns - Appropriate RR code (from nameser.h), or ns_t_invalid if an appropriate - mapping couldn't be determined + Convert an address family (AF) code to a DNS resource record (RR) code. + + Parameters + int + address family code (from socket.h) + Returns + Appropriate RR code (from nameser.h), or ns_t_invalid if an appropriate + mapping couldn't be determined */ ns_type_t af_to_rr (int af); /* - Convert a string to an address family (case insensitive). + Convert a string to an address family (case insensitive). - Returns - Matching AF code, or AF_UNSPEC if no match found. + Returns + Matching AF code, or AF_UNSPEC if no match found. */ int str_to_af (const char * str); /* - Convert a string to an ns_class_t (case insensitive). + Convert a string to an ns_class_t (case insensitive). - Returns - Matching ns_class_t, or ns_c_invalid if no match found. + Returns + Matching ns_class_t, or ns_c_invalid if no match found. */ ns_class_t str_to_ns_class (const char * str); /* - Convert a string to an ns_type_t (case insensitive). + Convert a string to an ns_type_t (case insensitive). - Returns - Matching ns_type_t, or ns_t_invalid if no match found. + Returns + Matching ns_type_t, or ns_t_invalid if no match found. */ ns_type_t str_to_ns_type (const char * str); /* - Convert an address family code to a string. + Convert an address family code to a string. - Returns - String representation of AF, - or NULL if address family unrecognised or invalid. + Returns + String representation of AF, + or NULL if address family unrecognised or invalid. */ const char * af_to_str (int in); /* - Convert an ns_class_t code to a string. + Convert an ns_class_t code to a string. - Returns - String representation of ns_class_t, - or NULL if ns_class_t unrecognised or invalid. + Returns + String representation of ns_class_t, + or NULL if ns_class_t unrecognised or invalid. */ const char * ns_class_to_str (ns_class_t in); /* - Convert an ns_type_t code to a string. + Convert an ns_type_t code to a string. - Returns - String representation of ns_type_t, - or NULL if ns_type_t unrecognised or invalid. + Returns + String representation of ns_type_t, + or NULL if ns_type_t unrecognised or invalid. */ const char * ns_type_to_str (ns_type_t in); /* - Convert DNS rdata in label format (RFC1034, RFC1035) to a name. - - On error, partial data is written to name (as much as was successfully - processed) and an error code is returned. Errors include a name too - long for the buffer and a pointer in the label (which cannot be - resolved). - - Parameters - rdata - Rdata formatted as series of labels. - rdlen - Length of rdata buffer. - name - Buffer to store fully qualified result in. - By RFC1034 section 3.1, a 255 character buffer (256 characters - including null) is long enough for any legal name. - name_len - Number of characters available in name buffer, not including - trailing null. - - Returns - Length of name buffer (not including trailing null). - < 0 on error. - A return of 0 implies the empty domain. + Convert DNS rdata in label format (RFC1034, RFC1035) to a name. + + On error, partial data is written to name (as much as was successfully + processed) and an error code is returned. Errors include a name too + long for the buffer and a pointer in the label (which cannot be + resolved). + + Parameters + rdata + Rdata formatted as series of labels. + rdlen + Length of rdata buffer. + name + Buffer to store fully qualified result in. + By RFC1034 section 3.1, a 255 character buffer (256 characters + including null) is long enough for any legal name. + name_len + Number of characters available in name buffer, not including + trailing null. + + Returns + Length of name buffer (not including trailing null). + < 0 on error. + A return of 0 implies the empty domain. */ -int -dns_rdata_to_name (const char * rdata, int rdlen, char * name, int name_len); +static int +dns_rdata_to_name (const unsigned char * rdata, int rdlen, char * name, unsigned int name_len); enum { - DNS_RDATA_TO_NAME_BAD_FORMAT = -1, - // Format is broken. Usually because we ran out of data - // (according to rdata) before the labels said we should. - DNS_RDATA_TO_NAME_TOO_LONG = -2, - // The converted rdata is longer than the name buffer. - DNS_RDATA_TO_NAME_PTR = -3, - // The rdata contains a pointer. + DNS_RDATA_TO_NAME_BAD_FORMAT = -1, + // Format is broken. Usually because we ran out of data + // (according to rdata) before the labels said we should. + DNS_RDATA_TO_NAME_TOO_LONG = -2, + // The converted rdata is longer than the name buffer. + DNS_RDATA_TO_NAME_PTR = -3, + // The rdata contains a pointer. }; #define DNS_LABEL_MAXLEN 63 - // Maximum length of a single DNS label +// Maximum length of a single DNS label #define DNS_NAME_MAXLEN 256 - // Maximum length of a DNS name +// Maximum length of a DNS name //---------- // Public types typedef int errcode_t; - // Used for 0 = success, non-zero = error code functions +// Used for 0 = success, non-zero = error code functions //---------- // Public functions /* - Test whether a domain name is in a domain covered by nss_mdns. - The name is assumed to be fully qualified (trailing dot optional); - unqualified names will be processed but may return unusual results - if the unqualified prefix happens to match a domain suffix. - - Returns - 1 success - 0 failure - -1 error, check errno + Test whether a domain name is in a domain covered by nss_mdns. + The name is assumed to be fully qualified (trailing dot optional); + unqualified names will be processed but may return unusual results + if the unqualified prefix happens to match a domain suffix. + + Returns + 1 success + 0 failure + -1 error, check errno */ int config_is_mdns_suffix (const char * name); /* - Loads all relevant data from configuration file. Other code should - rarely need to call this function, since all other public configuration - functions do so implicitly. Once loaded, configuration info doesn't - change. - - Returns - 0 configuration ready - non-zero configuration error code + Loads all relevant data from configuration file. Other code should + rarely need to call this function, since all other public configuration + functions do so implicitly. Once loaded, configuration info doesn't + change. + + Returns + 0 configuration ready + non-zero configuration error code */ errcode_t init_config (); @@ -380,187 +380,187 @@ init_config (); #define DATABASE "hosts" #include - // For nss_status +// For nss_status #include - // For hostent +// For hostent #include - // For size_t +// For size_t typedef enum nss_status nss_status; typedef struct hostent hostent; /* -gethostbyname implementation - - name: - name to look up - result_buf: - resulting entry - buf: - auxillary buffer - buflen: - length of auxillary buffer - errnop: - pointer to errno - h_errnop: - pointer to h_errno + gethostbyname implementation + + name: + name to look up + result_buf: + resulting entry + buf: + auxillary buffer + buflen: + length of auxillary buffer + errnop: + pointer to errno + h_errnop: + pointer to h_errno */ nss_status _nss_mdns_gethostbyname_r ( - const char *name, - hostent * result_buf, - char *buf, - size_t buflen, - int *errnop, - int *h_errnop -); + const char *name, + hostent * result_buf, + char *buf, + size_t buflen, + int *errnop, + int *h_errnop + ); /* -gethostbyname2 implementation - - name: - name to look up - af: - address family - result_buf: - resulting entry - buf: - auxillary buffer - buflen: - length of auxillary buffer - errnop: - pointer to errno - h_errnop: - pointer to h_errno + gethostbyname2 implementation + + name: + name to look up + af: + address family + result_buf: + resulting entry + buf: + auxillary buffer + buflen: + length of auxillary buffer + errnop: + pointer to errno + h_errnop: + pointer to h_errno */ nss_status _nss_mdns_gethostbyname2_r ( - const char *name, - int af, - hostent * result_buf, - char *buf, - size_t buflen, - int *errnop, - int *h_errnop -); + const char *name, + int af, + hostent * result_buf, + char *buf, + size_t buflen, + int *errnop, + int *h_errnop + ); /* -gethostbyaddr implementation - - addr: - address structure to look up - len: - length of address structure - af: - address family - result_buf: - resulting entry - buf: - auxillary buffer - buflen: - length of auxillary buffer - errnop: - pointer to errno - h_errnop: - pointer to h_errno + gethostbyaddr implementation + + addr: + address structure to look up + len: + length of address structure + af: + address family + result_buf: + resulting entry + buf: + auxillary buffer + buflen: + length of auxillary buffer + errnop: + pointer to errno + h_errnop: + pointer to h_errno */ nss_status _nss_mdns_gethostbyaddr_r ( - const void *addr, - socklen_t len, - int af, - hostent * result_buf, - char *buf, - size_t buflen, - int *errnop, - int *h_errnop -); + const void *addr, + socklen_t len, + int af, + hostent * result_buf, + char *buf, + size_t buflen, + int *errnop, + int *h_errnop + ); //---------- // Types and Constants const int MDNS_VERBOSE = 0; - // This enables verbose syslog messages - // If zero, only "imporant" messages will appear in syslog +// This enables verbose syslog messages +// If zero, only "imporant" messages will appear in syslog #define k_hostname_maxlen 256 - // As per RFC1034 and RFC1035 +// As per RFC1034 and RFC1035 #define k_aliases_max 15 #define k_addrs_max 15 typedef struct buf_header { - char hostname [k_hostname_maxlen + 1]; - char * aliases [k_aliases_max + 1]; - char * addrs [k_addrs_max + 1]; + char hostname [k_hostname_maxlen + 1]; + char * aliases [k_aliases_max + 1]; + char * addrs [k_addrs_max + 1]; } buf_header_t; typedef struct result_map { - int done; - nss_status status; - hostent * hostent; - buf_header_t * header; - int aliases_count; - int addrs_count; - char * buffer; - int addr_idx; - // Index for addresses - grow from low end - // Index points to first empty space - int alias_idx; - // Index for aliases - grow from high end - // Index points to lowest entry - int r_errno; - int r_h_errno; + int done; + nss_status status; + hostent * hostent; + buf_header_t * header; + int aliases_count; + int addrs_count; + char * buffer; + int addr_idx; + // Index for addresses - grow from low end + // Index points to first empty space + int alias_idx; + // Index for aliases - grow from high end + // Index points to lowest entry + int r_errno; + int r_h_errno; } result_map_t; static const struct timeval - k_select_time = { 0, 500000 }; - // 0 seconds, 500 milliseconds +k_select_time = { 0, 500000 }; +// 0 seconds, 500 milliseconds //---------- // Local prototypes static nss_status mdns_gethostbyname2 ( - const char *name, - int af, - hostent * result_buf, - char *buf, - size_t buflen, - int *errnop, - int *h_errnop -); + const char *name, + int af, + hostent * result_buf, + char *buf, + size_t buflen, + int *errnop, + int *h_errnop + ); /* - Lookup name using mDNS server + Lookup name using mDNS server */ static nss_status mdns_lookup_name ( - const char * fullname, - int af, - result_map_t * result -); + const char * fullname, + int af, + result_map_t * result + ); /* - Lookup address using mDNS server + Lookup address using mDNS server */ static nss_status mdns_lookup_addr ( - const void * addr, - socklen_t len, - int af, - const char * addr_str, - result_map_t * result -); + const void * addr, + socklen_t len, + int af, + const char * addr_str, + result_map_t * result + ); /* - Handle incoming MDNS events + Handle incoming MDNS events */ static nss_status handle_events (DNSServiceRef sdref, result_map_t * result, const char * str); @@ -571,17 +571,17 @@ handle_events (DNSServiceRef sdref, result_map_t * result, const char * str); typedef void mdns_lookup_callback_t ( - DNSServiceRef sdref, - DNSServiceFlags flags, - uint32_t interface_index, - DNSServiceErrorType error_code, - const char *fullname, - uint16_t rrtype, - uint16_t rrclass, - uint16_t rdlen, - const void *rdata, - uint32_t ttl, - void *context + DNSServiceRef sdref, + DNSServiceFlags flags, + uint32_t interface_index, + DNSServiceErrorType error_code, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata, + uint32_t ttl, + void *context ); mdns_lookup_callback_t mdns_lookup_callback; @@ -589,19 +589,19 @@ mdns_lookup_callback_t mdns_lookup_callback; static int init_result ( - result_map_t * result, - hostent * result_buf, - char * buf, - size_t buflen -); + result_map_t * result, + hostent * result_buf, + char * buf, + size_t buflen + ); static int callback_body_ptr ( - const char * fullname, - result_map_t * result, - int rdlen, - const void * rdata -); + const char * fullname, + result_map_t * result, + int rdlen, + const void * rdata + ); static void * add_address_to_buffer (result_map_t * result, const void * data, int len); @@ -620,18 +620,18 @@ contains_alias (result_map_t * result, const char * data); static const char * is_applicable_name ( - result_map_t * result, - const char * name, - char * lookup_name -); + result_map_t * result, + const char * name, + char * lookup_name + ); static const char * is_applicable_addr ( - result_map_t * result, - const void * addr, - int af, - char * addr_str -); + result_map_t * result, + const void * addr, + int af, + char * addr_str + ); // Error code functions @@ -657,121 +657,121 @@ static nss_status set_err_success (result_map_t * result); nss_status _nss_mdns_gethostbyname_r ( - const char *name, - hostent * result_buf, - char *buf, - size_t buflen, - int *errnop, - int *h_errnop -) + const char *name, + hostent * result_buf, + char *buf, + size_t buflen, + int *errnop, + int *h_errnop + ) { - if (MDNS_VERBOSE) - syslog (LOG_DEBUG, - "mdns: Called nss_mdns_gethostbyname with %s", - name - ); - - return - mdns_gethostbyname2 ( - name, AF_INET, result_buf, buf, buflen, errnop, h_errnop - ); + if (MDNS_VERBOSE) + syslog (LOG_DEBUG, + "mdns: Called nss_mdns_gethostbyname with %s", + name + ); + + return + mdns_gethostbyname2 ( + name, AF_INET, result_buf, buf, buflen, errnop, h_errnop + ); } nss_status _nss_mdns_gethostbyname2_r ( - const char *name, - int af, - hostent * result_buf, - char *buf, - size_t buflen, - int *errnop, - int *h_errnop -) + const char *name, + int af, + hostent * result_buf, + char *buf, + size_t buflen, + int *errnop, + int *h_errnop + ) { - if (MDNS_VERBOSE) - syslog (LOG_DEBUG, - "mdns: Called nss_mdns_gethostbyname2 with %s", - name - ); - - return - mdns_gethostbyname2 ( - name, af, result_buf, buf, buflen, errnop, h_errnop - ); + if (MDNS_VERBOSE) + syslog (LOG_DEBUG, + "mdns: Called nss_mdns_gethostbyname2 with %s", + name + ); + + return + mdns_gethostbyname2 ( + name, af, result_buf, buf, buflen, errnop, h_errnop + ); } nss_status _nss_mdns_gethostbyaddr_r ( - const void *addr, - socklen_t len, - int af, - hostent * result_buf, - char *buf, - size_t buflen, - int *errnop, - int *h_errnop -) + const void *addr, + socklen_t len, + int af, + hostent * result_buf, + char *buf, + size_t buflen, + int *errnop, + int *h_errnop + ) { - char addr_str [NI_MAXHOST + 1]; - result_map_t result; - int err_status; - - if (inet_ntop (af, addr, addr_str, NI_MAXHOST) == NULL) - { - const char * family = af_to_str (af); - if (family == NULL) - { - family = "Unknown"; - } - - syslog (LOG_WARNING, - "mdns: Couldn't covert address, family %d (%s) in nss_mdns_gethostbyaddr: %s", - af, - family, - strerror (errno) - ); - - // This address family never applicable to us, so return NOT_FOUND - - *errnop = ENOENT; - *h_errnop = HOST_NOT_FOUND; - return NSS_STATUS_NOTFOUND; - } - if (MDNS_VERBOSE) - { - syslog (LOG_DEBUG, - "mdns: Called nss_mdns_gethostbyaddr with %s", - addr_str - ); - } - - // Initialise result - err_status = init_result (&result, result_buf, buf, buflen); - if (err_status) - { - *errnop = err_status; - *h_errnop = NETDB_INTERNAL; - return NSS_STATUS_TRYAGAIN; - } - - if (is_applicable_addr (&result, addr, af, addr_str)) - { - nss_status rv; - - rv = mdns_lookup_addr (addr, len, af, addr_str, &result); - if (rv == NSS_STATUS_SUCCESS) - { - return rv; - } - } - - // Return current error status (defaults to NOT_FOUND) - - *errnop = result.r_errno; - *h_errnop = result.r_h_errno; - return result.status; + char addr_str [NI_MAXHOST + 1]; + result_map_t result; + int err_status; + + if (inet_ntop (af, addr, addr_str, NI_MAXHOST) == NULL) + { + const char * family = af_to_str (af); + if (family == NULL) + { + family = "Unknown"; + } + + syslog (LOG_WARNING, + "mdns: Couldn't covert address, family %d (%s) in nss_mdns_gethostbyaddr: %s", + af, + family, + strerror (errno) + ); + + // This address family never applicable to us, so return NOT_FOUND + + *errnop = ENOENT; + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } + if (MDNS_VERBOSE) + { + syslog (LOG_DEBUG, + "mdns: Called nss_mdns_gethostbyaddr with %s", + addr_str + ); + } + + // Initialise result + err_status = init_result (&result, result_buf, buf, buflen); + if (err_status) + { + *errnop = err_status; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_TRYAGAIN; + } + + if (is_applicable_addr (&result, addr, af, addr_str)) + { + nss_status rv; + + rv = mdns_lookup_addr (addr, len, af, addr_str, &result); + if (rv == NSS_STATUS_SUCCESS) + { + return rv; + } + } + + // Return current error status (defaults to NOT_FOUND) + + *errnop = result.r_errno; + *h_errnop = result.r_h_errno; + return result.status; } @@ -780,905 +780,905 @@ _nss_mdns_gethostbyaddr_r ( nss_status mdns_gethostbyname2 ( - const char *name, - int af, - hostent * result_buf, - char *buf, - size_t buflen, - int *errnop, - int *h_errnop -) + const char *name, + int af, + hostent * result_buf, + char *buf, + size_t buflen, + int *errnop, + int *h_errnop + ) { - char lookup_name [k_hostname_maxlen + 1]; - result_map_t result; - int err_status; - - // Initialise result - err_status = init_result (&result, result_buf, buf, buflen); - if (err_status) - { - *errnop = err_status; - *h_errnop = NETDB_INTERNAL; - return NSS_STATUS_TRYAGAIN; - } - - if (is_applicable_name (&result, name, lookup_name)) - { - // Try using mdns - nss_status rv; - - if (MDNS_VERBOSE) - syslog (LOG_DEBUG, - "mdns: Local name: %s", - name - ); - - rv = mdns_lookup_name (name, af, &result); - if (rv == NSS_STATUS_SUCCESS) - { - return rv; - } - } - - // Return current error status (defaults to NOT_FOUND) - - *errnop = result.r_errno; - *h_errnop = result.r_h_errno; - return result.status; + char lookup_name [k_hostname_maxlen + 1]; + result_map_t result; + int err_status; + + // Initialise result + err_status = init_result (&result, result_buf, buf, buflen); + if (err_status) + { + *errnop = err_status; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_TRYAGAIN; + } + + if (is_applicable_name (&result, name, lookup_name)) + { + // Try using mdns + nss_status rv; + + if (MDNS_VERBOSE) + syslog (LOG_DEBUG, + "mdns: Local name: %s", + name + ); + + rv = mdns_lookup_name (name, af, &result); + if (rv == NSS_STATUS_SUCCESS) + { + return rv; + } + } + + // Return current error status (defaults to NOT_FOUND) + + *errnop = result.r_errno; + *h_errnop = result.r_h_errno; + return result.status; } /* - Lookup a fully qualified hostname using the default record type - for the specified address family. - - Parameters - fullname - Fully qualified hostname. If not fully qualified the code will - still 'work', but the lookup is unlikely to succeed. - af - Either AF_INET or AF_INET6. Other families are not supported. - result - Initialised 'result' data structure. + Lookup a fully qualified hostname using the default record type + for the specified address family. + + Parameters + fullname + Fully qualified hostname. If not fully qualified the code will + still 'work', but the lookup is unlikely to succeed. + af + Either AF_INET or AF_INET6. Other families are not supported. + result + Initialised 'result' data structure. */ static nss_status mdns_lookup_name ( - const char * fullname, - int af, - result_map_t * result -) + const char * fullname, + int af, + result_map_t * result + ) { - // Lookup using mDNS. - DNSServiceErrorType errcode; - DNSServiceRef sdref; - ns_type_t rrtype; - nss_status status; - - if (MDNS_VERBOSE) - syslog (LOG_DEBUG, - "mdns: Attempting lookup of %s", - fullname - ); - - switch (af) - { - case AF_INET: - rrtype = kDNSServiceType_A; - result->hostent->h_length = 4; - // Length of an A record - break; - - case AF_INET6: - rrtype = kDNSServiceType_AAAA; - result->hostent->h_length = 16; - // Length of an AAAA record - break; - - default: - syslog (LOG_WARNING, - "mdns: Unsupported address family %d", - af - ); - return set_err_bad_hostname (result); - } - result->hostent->h_addrtype = af; - - errcode = - DNSServiceQueryRecord ( - &sdref, - kDNSServiceFlagsForceMulticast, // force multicast query - kDNSServiceInterfaceIndexAny, // all interfaces - fullname, // full name to query for - rrtype, // resource record type - kDNSServiceClass_IN, // internet class records - mdns_lookup_callback, // callback - result // Context - result buffer - ); - - if (errcode) - { - syslog (LOG_WARNING, - "mdns: Failed to initialise lookup, error %d", - errcode - ); - return set_err_mdns_failed (result); - } - - status = handle_events (sdref, result, fullname); - DNSServiceRefDeallocate (sdref); - return status; + // Lookup using mDNS. + DNSServiceErrorType errcode; + DNSServiceRef sdref; + ns_type_t rrtype; + nss_status status; + + if (MDNS_VERBOSE) + syslog (LOG_DEBUG, + "mdns: Attempting lookup of %s", + fullname + ); + + switch (af) + { + case AF_INET: + rrtype = kDNSServiceType_A; + result->hostent->h_length = 4; + // Length of an A record + break; + + case AF_INET6: + rrtype = kDNSServiceType_AAAA; + result->hostent->h_length = 16; + // Length of an AAAA record + break; + + default: + syslog (LOG_WARNING, + "mdns: Unsupported address family %d", + af + ); + return set_err_bad_hostname (result); + } + result->hostent->h_addrtype = af; + + errcode = + DNSServiceQueryRecord ( + &sdref, + kDNSServiceFlagsForceMulticast, // force multicast query + kDNSServiceInterfaceIndexAny, // all interfaces + fullname, // full name to query for + rrtype, // resource record type + kDNSServiceClass_IN, // internet class records + mdns_lookup_callback, // callback + result // Context - result buffer + ); + + if (errcode) + { + syslog (LOG_WARNING, + "mdns: Failed to initialise lookup, error %d", + errcode + ); + return set_err_mdns_failed (result); + } + + status = handle_events (sdref, result, fullname); + DNSServiceRefDeallocate (sdref); + return status; } /* - Reverse (PTR) lookup for the specified address. - - Parameters - addr - Either a struct in_addr or a struct in6_addr - addr_len - size of the address - af - Either AF_INET or AF_INET6. Other families are not supported. - Must match addr - addr_str - Address in format suitable for PTR lookup. - AF_INET: a.b.c.d -> d.c.b.a.in-addr.arpa - AF_INET6: reverse nibble format, x.x.x...x.ip6.arpa - result - Initialised 'result' data structure. + Reverse (PTR) lookup for the specified address. + + Parameters + addr + Either a struct in_addr or a struct in6_addr + addr_len + size of the address + af + Either AF_INET or AF_INET6. Other families are not supported. + Must match addr + addr_str + Address in format suitable for PTR lookup. + AF_INET: a.b.c.d -> d.c.b.a.in-addr.arpa + AF_INET6: reverse nibble format, x.x.x...x.ip6.arpa + result + Initialised 'result' data structure. */ static nss_status mdns_lookup_addr ( - const void * addr, - socklen_t addr_len, - int af, - const char * addr_str, - result_map_t * result -) + const void * addr, + socklen_t addr_len, + int af, + const char * addr_str, + result_map_t * result + ) { - DNSServiceErrorType errcode; - DNSServiceRef sdref; - nss_status status; - - if (MDNS_VERBOSE) - syslog (LOG_DEBUG, - "mdns: Attempting lookup of %s", - addr_str - ); - - result->hostent->h_addrtype = af; - result->hostent->h_length = addr_len; - - // Query address becomes "address" in result. - if (! add_address_to_buffer (result, addr, addr_len)) - { - return result->status; - } - - result->hostent->h_name [0] = 0; - - errcode = - DNSServiceQueryRecord ( - &sdref, - kDNSServiceFlagsForceMulticast, // force multicast query - kDNSServiceInterfaceIndexAny, // all interfaces - addr_str, // address string to query for - kDNSServiceType_PTR, // pointer RRs - kDNSServiceClass_IN, // internet class records - mdns_lookup_callback, // callback - result // Context - result buffer - ); - - if (errcode) - { - syslog (LOG_WARNING, - "mdns: Failed to initialise mdns lookup, error %d", - errcode - ); - return set_err_mdns_failed (result); - } - - status = handle_events (sdref, result, addr_str); - DNSServiceRefDeallocate (sdref); - return status; + DNSServiceErrorType errcode; + DNSServiceRef sdref; + nss_status status; + + if (MDNS_VERBOSE) + syslog (LOG_DEBUG, + "mdns: Attempting lookup of %s", + addr_str + ); + + result->hostent->h_addrtype = af; + result->hostent->h_length = addr_len; + + // Query address becomes "address" in result. + if (!add_address_to_buffer (result, addr, addr_len)) + { + return result->status; + } + + result->hostent->h_name [0] = 0; + + errcode = + DNSServiceQueryRecord ( + &sdref, + kDNSServiceFlagsForceMulticast, // force multicast query + kDNSServiceInterfaceIndexAny, // all interfaces + addr_str, // address string to query for + kDNSServiceType_PTR, // pointer RRs + kDNSServiceClass_IN, // internet class records + mdns_lookup_callback, // callback + result // Context - result buffer + ); + + if (errcode) + { + syslog (LOG_WARNING, + "mdns: Failed to initialise mdns lookup, error %d", + errcode + ); + return set_err_mdns_failed (result); + } + + status = handle_events (sdref, result, addr_str); + DNSServiceRefDeallocate (sdref); + return status; } /* - Wait on result of callback, and process it when it arrives. - - Parameters - sdref - dns-sd reference - result - Initialised 'result' data structure. - str - lookup string, used for status/error reporting. + Wait on result of callback, and process it when it arrives. + + Parameters + sdref + dns-sd reference + result + Initialised 'result' data structure. + str + lookup string, used for status/error reporting. */ static nss_status handle_events (DNSServiceRef sdref, result_map_t * result, const char * str) { - int dns_sd_fd = DNSServiceRefSockFD(sdref); - int nfds = dns_sd_fd + 1; - fd_set readfds; - struct timeval tv; - int select_result; - - while (! result->done) - { - FD_ZERO(&readfds); - FD_SET(dns_sd_fd, &readfds); - - tv = k_select_time; - - select_result = - select (nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv); - if (select_result > 0) - { - if (FD_ISSET(dns_sd_fd, &readfds)) - { - if (MDNS_VERBOSE) - syslog (LOG_DEBUG, - "mdns: Reply received for %s", - str - ); - DNSServiceProcessResult(sdref); - } - else - { - syslog (LOG_WARNING, - "mdns: Unexpected return from select on lookup of %s", - str - ); - } - } - else - { - // Terminate loop due to timer expiry - if (MDNS_VERBOSE) - syslog (LOG_DEBUG, - "mdns: %s not found - timer expired", - str - ); - set_err_notfound (result); - break; - } - } - - return result->status; + int dns_sd_fd = DNSServiceRefSockFD(sdref); + int nfds = dns_sd_fd + 1; + fd_set readfds; + struct timeval tv; + int select_result; + + while (!result->done) + { + FD_ZERO(&readfds); + FD_SET(dns_sd_fd, &readfds); + + tv = k_select_time; + + select_result = + select (nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv); + if (select_result > 0) + { + if (FD_ISSET(dns_sd_fd, &readfds)) + { + if (MDNS_VERBOSE) + syslog (LOG_DEBUG, + "mdns: Reply received for %s", + str + ); + DNSServiceProcessResult(sdref); + } + else + { + syslog (LOG_WARNING, + "mdns: Unexpected return from select on lookup of %s", + str + ); + } + } + else + { + // Terminate loop due to timer expiry + if (MDNS_VERBOSE) + syslog (LOG_DEBUG, + "mdns: %s not found - timer expired", + str + ); + set_err_notfound (result); + break; + } + } + + return result->status; } /* - Examine incoming data and add to relevant fields in result structure. - This routine is called from DNSServiceProcessResult where appropriate. + Examine incoming data and add to relevant fields in result structure. + This routine is called from DNSServiceProcessResult where appropriate. */ void mdns_lookup_callback ( - DNSServiceRef sdref, - DNSServiceFlags flags, - uint32_t interface_index, - DNSServiceErrorType error_code, - const char *fullname, - uint16_t rrtype, - uint16_t rrclass, - uint16_t rdlen, - const void *rdata, - uint32_t ttl, - void *context + DNSServiceRef sdref, + DNSServiceFlags flags, + uint32_t interface_index, + DNSServiceErrorType error_code, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata, + uint32_t ttl, + void *context ) { - // A single record is received - - result_map_t * result = (result_map_t *) context; - - (void)sdref; // Unused - (void)interface_index; // Unused - (void)ttl; // Unused - - if (! (flags & kDNSServiceFlagsMoreComing) ) - { - result->done = 1; - } - - if (error_code == kDNSServiceErr_NoError) - { - ns_type_t expected_rr_type = - af_to_rr (result->hostent->h_addrtype); - - // Idiot check class - if (rrclass != C_IN) - { - syslog (LOG_WARNING, - "mdns: Received bad RR class: expected %d (%s)," - " got %d (%s), RR type %d (%s)", - C_IN, - ns_class_to_str (C_IN), - rrclass, - ns_class_to_str (rrclass), - rrtype, - ns_type_to_str (rrtype) - ); - return; - } - - // If a PTR - if (rrtype == kDNSServiceType_PTR) - { - if (callback_body_ptr (fullname, result, rdlen, rdata) < 0) - return; - } - else if (rrtype == expected_rr_type) - { - if (! - add_hostname_or_alias ( - result, - fullname, - strlen (fullname) - ) - ) - { - result->done = 1; - return; - // Abort on error - } - - if (! add_address_to_buffer (result, rdata, rdlen) ) - { - result->done = 1; - return; - // Abort on error - } - } - else - { - syslog (LOG_WARNING, - "mdns: Received bad RR type: expected %d (%s)," - " got %d (%s)", - expected_rr_type, - ns_type_to_str (expected_rr_type), - rrtype, - ns_type_to_str (rrtype) - ); - return; - } - - if (result->status != NSS_STATUS_SUCCESS) - set_err_success (result); - } - else - { - // For now, dump message to syslog and continue - syslog (LOG_WARNING, - "mdns: callback returned error %d", - error_code - ); - } + // A single record is received + + result_map_t * result = (result_map_t *) context; + + (void)sdref; // Unused + (void)interface_index; // Unused + (void)ttl; // Unused + + if (!(flags & kDNSServiceFlagsMoreComing) ) + { + result->done = 1; + } + + if (error_code == kDNSServiceErr_NoError) + { + ns_type_t expected_rr_type = + af_to_rr (result->hostent->h_addrtype); + + // Idiot check class + if (rrclass != C_IN) + { + syslog (LOG_WARNING, + "mdns: Received bad RR class: expected %d (%s)," + " got %d (%s), RR type %d (%s)", + C_IN, + ns_class_to_str (C_IN), + rrclass, + ns_class_to_str (rrclass), + rrtype, + ns_type_to_str (rrtype) + ); + return; + } + + // If a PTR + if (rrtype == kDNSServiceType_PTR) + { + if (callback_body_ptr (fullname, result, rdlen, rdata) < 0) + return; + } + else if (rrtype == expected_rr_type) + { + if (! + add_hostname_or_alias ( + result, + fullname, + strlen (fullname) + ) + ) + { + result->done = 1; + return; + // Abort on error + } + + if (!add_address_to_buffer (result, rdata, rdlen) ) + { + result->done = 1; + return; + // Abort on error + } + } + else + { + syslog (LOG_WARNING, + "mdns: Received bad RR type: expected %d (%s)," + " got %d (%s)", + expected_rr_type, + ns_type_to_str (expected_rr_type), + rrtype, + ns_type_to_str (rrtype) + ); + return; + } + + if (result->status != NSS_STATUS_SUCCESS) + set_err_success (result); + } + else + { + // For now, dump message to syslog and continue + syslog (LOG_WARNING, + "mdns: callback returned error %d", + error_code + ); + } } static int callback_body_ptr ( - const char * fullname, - result_map_t * result, - int rdlen, - const void * rdata -) + const char * fullname, + result_map_t * result, + int rdlen, + const void * rdata + ) { - char result_name [k_hostname_maxlen + 1]; - int rv; - - // Fullname should be .in-addr.arpa or equivalent, which we're - // not interested in. Ignore it. - - rv = dns_rdata_to_name (rdata, rdlen, result_name, k_hostname_maxlen); - if (rv < 0) - { - const char * errmsg; - - switch (rv) - { - case DNS_RDATA_TO_NAME_BAD_FORMAT: - errmsg = "mdns: PTR '%s' result badly formatted ('%s...')"; - break; - - case DNS_RDATA_TO_NAME_TOO_LONG: - errmsg = "mdns: PTR '%s' result too long ('%s...')"; - break; - - case DNS_RDATA_TO_NAME_PTR: - errmsg = "mdns: PTR '%s' result contained pointer ('%s...')"; - break; - - default: - errmsg = "mdns: PTR '%s' result conversion failed ('%s...')"; - } - - syslog (LOG_WARNING, - errmsg, - fullname, - result_name - ); - - return -1; - } - - if (MDNS_VERBOSE) - { - syslog (LOG_DEBUG, - "mdns: PTR '%s' resolved to '%s'", - fullname, - result_name - ); - } - - // Data should be a hostname - if (! - add_hostname_or_alias ( - result, - result_name, - rv - ) - ) - { - result->done = 1; - return -1; - } - - return 0; + char result_name [k_hostname_maxlen + 1]; + int rv; + + // Fullname should be .in-addr.arpa or equivalent, which we're + // not interested in. Ignore it. + + rv = dns_rdata_to_name (rdata, rdlen, result_name, k_hostname_maxlen); + if (rv < 0) + { + const char * errmsg; + + switch (rv) + { + case DNS_RDATA_TO_NAME_BAD_FORMAT: + errmsg = "mdns: PTR '%s' result badly formatted ('%s...')"; + break; + + case DNS_RDATA_TO_NAME_TOO_LONG: + errmsg = "mdns: PTR '%s' result too long ('%s...')"; + break; + + case DNS_RDATA_TO_NAME_PTR: + errmsg = "mdns: PTR '%s' result contained pointer ('%s...')"; + break; + + default: + errmsg = "mdns: PTR '%s' result conversion failed ('%s...')"; + } + + syslog (LOG_WARNING, + errmsg, + fullname, + result_name + ); + + return -1; + } + + if (MDNS_VERBOSE) + { + syslog (LOG_DEBUG, + "mdns: PTR '%s' resolved to '%s'", + fullname, + result_name + ); + } + + // Data should be a hostname + if (! + add_hostname_or_alias ( + result, + result_name, + rv + ) + ) + { + result->done = 1; + return -1; + } + + return 0; } /* - Add an address to the buffer. - - Parameter - result - Result structure to write to - data - Incoming address data buffer - Must be 'int' aligned - len - Length of data buffer (in bytes) - Must match data alignment - - Result - Pointer to start of newly written data, - or NULL on error. - If address already exists in buffer, returns pointer to that instead. + Add an address to the buffer. + + Parameter + result + Result structure to write to + data + Incoming address data buffer + Must be 'int' aligned + len + Length of data buffer (in bytes) + Must match data alignment + + Result + Pointer to start of newly written data, + or NULL on error. + If address already exists in buffer, returns pointer to that instead. */ static void * add_address_to_buffer (result_map_t * result, const void * data, int len) { - int new_addr; - void * start; - void * temp; - - if ((temp = contains_address (result, data, len))) - { - return temp; - } - - if (result->addrs_count >= k_addrs_max) - { - // Not enough addr slots - set_err_internal_resource_full (result); - syslog (LOG_ERR, - "mdns: Internal address buffer full; increase size" - ); - return NULL; - } - - // Idiot check - if (len != result->hostent->h_length) - { - syslog (LOG_WARNING, - "mdns: Unexpected rdata length for address. Expected %d, got %d", - result->hostent->h_length, - len - ); - // XXX And continue for now. - } - - new_addr = result->addr_idx + len; - - if (new_addr > result->alias_idx) - { - // Not enough room - set_err_buf_too_small (result); - if (MDNS_VERBOSE) - syslog (LOG_DEBUG, - "mdns: Ran out of buffer when adding address %d", - result->addrs_count + 1 - ); - return NULL; - } - - start = result->buffer + result->addr_idx; - memcpy (start, data, len); - result->addr_idx = new_addr; - result->header->addrs [result->addrs_count] = start; - result->addrs_count ++; - result->header->addrs [result->addrs_count] = NULL; - - return start; + int new_addr; + void * start; + void * temp; + + if ((temp = contains_address (result, data, len))) + { + return temp; + } + + if (result->addrs_count >= k_addrs_max) + { + // Not enough addr slots + set_err_internal_resource_full (result); + syslog (LOG_ERR, + "mdns: Internal address buffer full; increase size" + ); + return NULL; + } + + // Idiot check + if (len != result->hostent->h_length) + { + syslog (LOG_WARNING, + "mdns: Unexpected rdata length for address. Expected %d, got %d", + result->hostent->h_length, + len + ); + // XXX And continue for now. + } + + new_addr = result->addr_idx + len; + + if (new_addr > result->alias_idx) + { + // Not enough room + set_err_buf_too_small (result); + if (MDNS_VERBOSE) + syslog (LOG_DEBUG, + "mdns: Ran out of buffer when adding address %d", + result->addrs_count + 1 + ); + return NULL; + } + + start = result->buffer + result->addr_idx; + memcpy (start, data, len); + result->addr_idx = new_addr; + result->header->addrs [result->addrs_count] = start; + result->addrs_count++; + result->header->addrs [result->addrs_count] = NULL; + + return start; } static void * contains_address (result_map_t * result, const void * data, int len) { - int i; - - // Idiot check - if (len != result->hostent->h_length) - { - syslog (LOG_WARNING, - "mdns: Unexpected rdata length for address. Expected %d, got %d", - result->hostent->h_length, - len - ); - // XXX And continue for now. - } - - for (i = 0; result->header->addrs [i]; i++) - { - if (memcmp (result->header->addrs [i], data, len) == 0) - { - return result->header->addrs [i]; - } - } - - return NULL; + int i; + + // Idiot check + if (len != result->hostent->h_length) + { + syslog (LOG_WARNING, + "mdns: Unexpected rdata length for address. Expected %d, got %d", + result->hostent->h_length, + len + ); + // XXX And continue for now. + } + + for (i = 0; result->header->addrs [i]; i++) + { + if (memcmp (result->header->addrs [i], data, len) == 0) + { + return result->header->addrs [i]; + } + } + + return NULL; } /* - Add an alias to the buffer. - - Parameter - result - Result structure to write to - data - Incoming alias (null terminated) - len - Length of data buffer (in bytes), including trailing null - - Result - Pointer to start of newly written data, - or NULL on error - If alias already exists in buffer, returns pointer to that instead. + Add an alias to the buffer. + + Parameter + result + Result structure to write to + data + Incoming alias (null terminated) + len + Length of data buffer (in bytes), including trailing null + + Result + Pointer to start of newly written data, + or NULL on error + If alias already exists in buffer, returns pointer to that instead. */ static char * add_alias_to_buffer (result_map_t * result, const char * data, int len) { - int new_alias; - char * start; - char * temp; - - if ((temp = contains_alias (result, data))) - { - return temp; - } - - if (result->aliases_count >= k_aliases_max) - { - // Not enough alias slots - set_err_internal_resource_full (result); - syslog (LOG_ERR, - "mdns: Internal alias buffer full; increase size" - ); - return NULL; - } - - new_alias = result->alias_idx - len; - - if (new_alias < result->addr_idx) - { - // Not enough room - set_err_buf_too_small (result); - if (MDNS_VERBOSE) - syslog (LOG_DEBUG, - "mdns: Ran out of buffer when adding alias %d", - result->aliases_count + 1 - ); - return NULL; - } - - start = result->buffer + new_alias; - memcpy (start, data, len); - result->alias_idx = new_alias; - result->header->aliases [result->aliases_count] = start; - result->aliases_count ++; - result->header->aliases [result->aliases_count] = NULL; - - return start; + int new_alias; + char * start; + char * temp; + + if ((temp = contains_alias (result, data))) + { + return temp; + } + + if (result->aliases_count >= k_aliases_max) + { + // Not enough alias slots + set_err_internal_resource_full (result); + syslog (LOG_ERR, + "mdns: Internal alias buffer full; increase size" + ); + return NULL; + } + + new_alias = result->alias_idx - len; + + if (new_alias < result->addr_idx) + { + // Not enough room + set_err_buf_too_small (result); + if (MDNS_VERBOSE) + syslog (LOG_DEBUG, + "mdns: Ran out of buffer when adding alias %d", + result->aliases_count + 1 + ); + return NULL; + } + + start = result->buffer + new_alias; + memcpy (start, data, len); + result->alias_idx = new_alias; + result->header->aliases [result->aliases_count] = start; + result->aliases_count++; + result->header->aliases [result->aliases_count] = NULL; + + return start; } static char * contains_alias (result_map_t * result, const char * alias) { - int i; - - for (i = 0; result->header->aliases [i]; i++) - { - if (strcmp (result->header->aliases [i], alias) == 0) - { - return result->header->aliases [i]; - } - } - - return NULL; + int i; + + for (i = 0; result->header->aliases [i]; i++) + { + if (strcmp (result->header->aliases [i], alias) == 0) + { + return result->header->aliases [i]; + } + } + + return NULL; } /* - Add fully qualified hostname to result. - - Parameter - result - Result structure to write to - fullname - Fully qualified hostname - - Result - Pointer to start of hostname buffer, - or NULL on error (usually hostname too long) + Add fully qualified hostname to result. + + Parameter + result + Result structure to write to + fullname + Fully qualified hostname + + Result + Pointer to start of hostname buffer, + or NULL on error (usually hostname too long) */ static char * add_hostname_len (result_map_t * result, const char * fullname, int len) { - if (len >= k_hostname_maxlen) - { - set_err_bad_hostname (result); - syslog (LOG_WARNING, - "mdns: Hostname too long '%.*s': len %d, max %d", - len, - fullname, - len, - k_hostname_maxlen - ); - return NULL; - } - - result->hostent->h_name = - strcpy (result->header->hostname, fullname); - - return result->header->hostname; + if (len >= k_hostname_maxlen) + { + set_err_bad_hostname (result); + syslog (LOG_WARNING, + "mdns: Hostname too long '%.*s': len %d, max %d", + len, + fullname, + len, + k_hostname_maxlen + ); + return NULL; + } + + result->hostent->h_name = + strcpy (result->header->hostname, fullname); + + return result->header->hostname; } /* - Add fully qualified name as hostname or alias. - - If hostname is not fully qualified this is not an error, but the data - returned may be not what the application wanted. - - Parameter - result - Result structure to write to - data - Incoming alias (null terminated) - len - Length of data buffer (in bytes), including trailing null - - Result - Pointer to start of newly written data, - or NULL on error - If alias or hostname already exists, returns pointer to that instead. + Add fully qualified name as hostname or alias. + + If hostname is not fully qualified this is not an error, but the data + returned may be not what the application wanted. + + Parameter + result + Result structure to write to + data + Incoming alias (null terminated) + len + Length of data buffer (in bytes), including trailing null + + Result + Pointer to start of newly written data, + or NULL on error + If alias or hostname already exists, returns pointer to that instead. */ static char * add_hostname_or_alias (result_map_t * result, const char * data, int len) { - char * hostname = result->hostent->h_name; - - if (*hostname) - { - if (strcmp (hostname, data) == 0) - { - return hostname; - } - else - { - return add_alias_to_buffer (result, data, len); - } - } - else - { - return add_hostname_len (result, data, len); - } + char * hostname = result->hostent->h_name; + + if (*hostname) + { + if (strcmp (hostname, data) == 0) + { + return hostname; + } + else + { + return add_alias_to_buffer (result, data, len); + } + } + else + { + return add_hostname_len (result, data, len); + } } static int init_result ( - result_map_t * result, - hostent * result_buf, - char * buf, - size_t buflen -) + result_map_t * result, + hostent * result_buf, + char * buf, + size_t buflen + ) { - if (buflen < sizeof (buf_header_t)) - { - return ERANGE; - } - - result->hostent = result_buf; - result->header = (buf_header_t *) buf; - result->header->hostname[0] = 0; - result->aliases_count = 0; - result->header->aliases[0] = NULL; - result->addrs_count = 0; - result->header->addrs[0] = NULL; - result->buffer = buf + sizeof (buf_header_t); - result->addr_idx = 0; - result->alias_idx = buflen - sizeof (buf_header_t); - result->done = 0; - set_err_notfound (result); - - // Point hostent to the right buffers - result->hostent->h_name = result->header->hostname; - result->hostent->h_aliases = result->header->aliases; - result->hostent->h_addr_list = result->header->addrs; - - return 0; + if (buflen < sizeof (buf_header_t)) + { + return ERANGE; + } + + result->hostent = result_buf; + result->header = (buf_header_t *) buf; + result->header->hostname[0] = 0; + result->aliases_count = 0; + result->header->aliases[0] = NULL; + result->addrs_count = 0; + result->header->addrs[0] = NULL; + result->buffer = buf + sizeof (buf_header_t); + result->addr_idx = 0; + result->alias_idx = buflen - sizeof (buf_header_t); + result->done = 0; + set_err_notfound (result); + + // Point hostent to the right buffers + result->hostent->h_name = result->header->hostname; + result->hostent->h_aliases = result->header->aliases; + result->hostent->h_addr_list = result->header->addrs; + + return 0; } /* - Set the status in the result. - - Parameters - result - Result structure to update - status - New nss_status value - err - New errno value - herr - New h_errno value - - Returns - New status value + Set the status in the result. + + Parameters + result + Result structure to update + status + New nss_status value + err + New errno value + herr + New h_errno value + + Returns + New status value */ static nss_status set_err (result_map_t * result, nss_status status, int err, int herr) { - result->status = status; - result->r_errno = err; - result->r_h_errno = herr; - - return status; + result->status = status; + result->r_errno = err; + result->r_h_errno = herr; + + return status; } static nss_status set_err_notfound (result_map_t * result) { - return set_err (result, NSS_STATUS_NOTFOUND, ENOENT, HOST_NOT_FOUND); + return set_err (result, NSS_STATUS_NOTFOUND, ENOENT, HOST_NOT_FOUND); } static nss_status set_err_bad_hostname (result_map_t * result) { - return set_err (result, NSS_STATUS_TRYAGAIN, ENOENT, NO_RECOVERY); + return set_err (result, NSS_STATUS_TRYAGAIN, ENOENT, NO_RECOVERY); } static nss_status set_err_buf_too_small (result_map_t * result) { - return set_err (result, NSS_STATUS_TRYAGAIN, ERANGE, NETDB_INTERNAL); + return set_err (result, NSS_STATUS_TRYAGAIN, ERANGE, NETDB_INTERNAL); } static nss_status set_err_internal_resource_full (result_map_t * result) { - return set_err (result, NSS_STATUS_RETURN, ERANGE, NO_RECOVERY); + return set_err (result, NSS_STATUS_RETURN, ERANGE, NO_RECOVERY); } static nss_status set_err_system (result_map_t * result) { - return set_err (result, NSS_STATUS_UNAVAIL, errno, NETDB_INTERNAL); + return set_err (result, NSS_STATUS_UNAVAIL, errno, NETDB_INTERNAL); } static nss_status set_err_mdns_failed (result_map_t * result) { - return set_err (result, NSS_STATUS_TRYAGAIN, EAGAIN, TRY_AGAIN); + return set_err (result, NSS_STATUS_TRYAGAIN, EAGAIN, TRY_AGAIN); } static nss_status set_err_success (result_map_t * result) { - result->status = NSS_STATUS_SUCCESS; - return result->status; + result->status = NSS_STATUS_SUCCESS; + return result->status; } /* - Test whether name is applicable for mdns to process, and if so copy into - lookup_name buffer (if non-NULL). - - Returns - Pointer to name to lookup up, if applicable, or NULL otherwise. + Test whether name is applicable for mdns to process, and if so copy into + lookup_name buffer (if non-NULL). + + Returns + Pointer to name to lookup up, if applicable, or NULL otherwise. */ static const char * is_applicable_name ( - result_map_t * result, - const char * name, - char * lookup_name -) + result_map_t * result, + const char * name, + char * lookup_name + ) { - int match = config_is_mdns_suffix (name); - if (match > 0) - { - if (lookup_name) - { - strncpy (lookup_name, name, k_hostname_maxlen + 1); - return lookup_name; - } - else - { - return name; - } - } - else - { - if (match < 0) - { - set_err_system (result); - } - return NULL; - } + int match = config_is_mdns_suffix (name); + if (match > 0) + { + if (lookup_name) + { + strncpy (lookup_name, name, k_hostname_maxlen + 1); + return lookup_name; + } + else + { + return name; + } + } + else + { + if (match < 0) + { + set_err_system (result); + } + return NULL; + } } /* - Test whether address is applicable for mdns to process, and if so copy into - addr_str buffer as an address suitable for ptr lookup. - - Returns - Pointer to name to lookup up, if applicable, or NULL otherwise. + Test whether address is applicable for mdns to process, and if so copy into + addr_str buffer as an address suitable for ptr lookup. + + Returns + Pointer to name to lookup up, if applicable, or NULL otherwise. */ static const char * is_applicable_addr ( - result_map_t * result, - const void * addr, - int af, - char * addr_str -) + result_map_t * result, + const void * addr, + int af, + char * addr_str + ) { - int match; - - if (! format_reverse_addr (af, addr, -1, addr_str)) - { - if (MDNS_VERBOSE) - syslog (LOG_DEBUG, - "mdns: Failed to create reverse address" - ); - return NULL; - } - - if (MDNS_VERBOSE) - syslog (LOG_DEBUG, - "mdns: Reverse address: %s", - addr_str - ); - - match = config_is_mdns_suffix (addr_str); - if (match > 0) - { - return addr_str; - } - else - { - if (match < 0) - { - set_err_system (result); - } - return NULL; - } + int match; + + if (!format_reverse_addr (af, addr, -1, addr_str)) + { + if (MDNS_VERBOSE) + syslog (LOG_DEBUG, + "mdns: Failed to create reverse address" + ); + return NULL; + } + + if (MDNS_VERBOSE) + syslog (LOG_DEBUG, + "mdns: Reverse address: %s", + addr_str + ); + + match = config_is_mdns_suffix (addr_str); + if (match > 0) + { + return addr_str; + } + else + { + if (match < 0) + { + set_err_system (result); + } + return NULL; + } } //---------- @@ -1692,46 +1692,46 @@ const char k_comment_char = '#'; const char * k_keyword_domain = "domain"; const char * k_default_domains [] = - { - "local", - "254.169.in-addr.arpa", - "8.e.f.ip6.int", - "8.e.f.ip6.arpa", - "9.e.f.ip6.int", - "9.e.f.ip6.arpa", - "a.e.f.ip6.int", - "a.e.f.ip6.arpa", - "b.e.f.ip6.int", - "b.e.f.ip6.arpa", - NULL - // Always null terminated - }; +{ + "local", + "254.169.in-addr.arpa", + "8.e.f.ip6.int", + "8.e.f.ip6.arpa", + "9.e.f.ip6.int", + "9.e.f.ip6.arpa", + "a.e.f.ip6.int", + "a.e.f.ip6.arpa", + "b.e.f.ip6.int", + "b.e.f.ip6.arpa", + NULL + // Always null terminated +}; // Linked list of domains typedef struct domain_entry { - char * domain; - struct domain_entry * next; + char * domain; + struct domain_entry * next; } domain_entry_t; // Config typedef struct { - domain_entry_t * domains; + domain_entry_t * domains; } config_t; const config_t k_empty_config = - { - NULL - }; +{ + NULL +}; // Context - tracks position in config file, used for error reporting typedef struct { - const char * filename; - int linenum; + const char * filename; + int linenum; } config_file_context_t; @@ -1743,10 +1743,10 @@ load_config (config_t * conf); static errcode_t process_config_line ( - config_t * conf, - char * line, - config_file_context_t * context -); + config_t * conf, + char * line, + config_file_context_t * context + ); static char * get_next_word (char * input, char **next); @@ -1768,13 +1768,13 @@ contains_domain_suffix (const config_t * conf, const char * addr); // Global variables static config_t * g_config = NULL; - // Configuration info +// Configuration info pthread_mutex_t g_config_mutex = #ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP - PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; + PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; #else - PTHREAD_MUTEX_INITIALIZER; + PTHREAD_MUTEX_INITIALIZER; #endif @@ -1783,98 +1783,98 @@ pthread_mutex_t g_config_mutex = /* - Initialise the configuration from the config file. - - Returns - 0 success - non-zero error code on failure + Initialise the configuration from the config file. + + Returns + 0 success + non-zero error code on failure */ errcode_t init_config () { - if (g_config) - { - /* - Safe to test outside mutex. - If non-zero, initialisation is complete and g_config can be - safely used read-only. If zero, then we do proper mutex - testing before initialisation. - */ - return 0; - } - else - { - int errcode = -1; - int presult; - config_t * temp_config; - - // Acquire mutex - presult = pthread_mutex_lock (&g_config_mutex); - if (presult) - { - syslog (LOG_ERR, - "mdns: Fatal mutex lock error in nss_mdns:init_config, %s:%d: %d: %s", - __FILE__, __LINE__, presult, strerror (presult) - ); - return presult; - } - - // Test again now we have mutex, in case initialisation occurred while - // we were waiting - if (! g_config) - { - temp_config = (config_t *) malloc (sizeof (config_t)); - if (temp_config) - { - // Note: This code will leak memory if initialisation fails - // repeatedly. This should only happen in the case of a memory - // error, so I'm not sure if it's a meaningful problem. - AW - *temp_config = k_empty_config; - errcode = load_config (temp_config); - - if (! errcode) - { - g_config = temp_config; - } - } - else - { - syslog (LOG_ERR, - "mdns: Can't allocate memory in nss_mdns:init_config, %s:%d", - __FILE__, __LINE__ - ); - errcode = errno; - } - } - - presult = pthread_mutex_unlock (&g_config_mutex); - if (presult) - { - syslog (LOG_ERR, - "mdns: Fatal mutex unlock error in nss_mdns:init_config, %s:%d: %d: %s", - __FILE__, __LINE__, presult, strerror (presult) - ); - errcode = presult; - } - - return errcode; - } + if (g_config) + { + /* + Safe to test outside mutex. + If non-zero, initialisation is complete and g_config can be + safely used read-only. If zero, then we do proper mutex + testing before initialisation. + */ + return 0; + } + else + { + int errcode = -1; + int presult; + config_t * temp_config; + + // Acquire mutex + presult = pthread_mutex_lock (&g_config_mutex); + if (presult) + { + syslog (LOG_ERR, + "mdns: Fatal mutex lock error in nss_mdns:init_config, %s:%d: %d: %s", + __FILE__, __LINE__, presult, strerror (presult) + ); + return presult; + } + + // Test again now we have mutex, in case initialisation occurred while + // we were waiting + if (!g_config) + { + temp_config = (config_t *) malloc (sizeof (config_t)); + if (temp_config) + { + // Note: This code will leak memory if initialisation fails + // repeatedly. This should only happen in the case of a memory + // error, so I'm not sure if it's a meaningful problem. - AW + *temp_config = k_empty_config; + errcode = load_config (temp_config); + + if (!errcode) + { + g_config = temp_config; + } + } + else + { + syslog (LOG_ERR, + "mdns: Can't allocate memory in nss_mdns:init_config, %s:%d", + __FILE__, __LINE__ + ); + errcode = errno; + } + } + + presult = pthread_mutex_unlock (&g_config_mutex); + if (presult) + { + syslog (LOG_ERR, + "mdns: Fatal mutex unlock error in nss_mdns:init_config, %s:%d: %d: %s", + __FILE__, __LINE__, presult, strerror (presult) + ); + errcode = presult; + } + + return errcode; + } } int config_is_mdns_suffix (const char * name) { - int errcode = init_config (); - if (! errcode) - { - return contains_domain_suffix (g_config, name); - } - else - { - errno = errcode; - return -1; - } + int errcode = init_config (); + if (!errcode) + { + return contains_domain_suffix (g_config, name); + } + else + { + errno = errcode; + return -1; + } } @@ -1884,263 +1884,263 @@ config_is_mdns_suffix (const char * name) static errcode_t load_config (config_t * conf) { - FILE * cf; - char line [CONF_LINE_SIZE]; - config_file_context_t context; - - context.filename = k_conf_file; - context.linenum = 0; - - - cf = fopen (context.filename, "r"); - if (! cf) - { - syslog (LOG_INFO, - "mdns: Couldn't open nss_mdns configuration file %s, using default.", - context.filename - ); - return default_config (conf); - } - - while (fgets (line, CONF_LINE_SIZE, cf)) - { - int errcode; - context.linenum++; - errcode = process_config_line (conf, line, &context); - if (errcode) - { - // Critical error, give up - fclose(cf); - return errcode; - } - } - - fclose (cf); - - return 0; + FILE * cf; + char line [CONF_LINE_SIZE]; + config_file_context_t context; + + context.filename = k_conf_file; + context.linenum = 0; + + + cf = fopen (context.filename, "r"); + if (!cf) + { + syslog (LOG_INFO, + "mdns: Couldn't open nss_mdns configuration file %s, using default.", + context.filename + ); + return default_config (conf); + } + + while (fgets (line, CONF_LINE_SIZE, cf)) + { + int errcode; + context.linenum++; + errcode = process_config_line (conf, line, &context); + if (errcode) + { + // Critical error, give up + fclose(cf); + return errcode; + } + } + + fclose (cf); + + return 0; } /* - Parse a line of the configuration file. - For each keyword recognised, perform appropriate handling. - If the keyword is not recognised, print a message to syslog - and continue. - - Returns - 0 success, or recoverable config file error - non-zero serious system error, processing aborted + Parse a line of the configuration file. + For each keyword recognised, perform appropriate handling. + If the keyword is not recognised, print a message to syslog + and continue. + + Returns + 0 success, or recoverable config file error + non-zero serious system error, processing aborted */ static errcode_t process_config_line ( - config_t * conf, - char * line, - config_file_context_t * context -) + config_t * conf, + char * line, + config_file_context_t * context + ) { - char * curr = line; - char * word; - - word = get_next_word (curr, &curr); - if (! word || word [0] == k_comment_char) - { - // Nothing interesting on this line - return 0; - } - - if (strcmp (word, k_keyword_domain) == 0) - { - word = get_next_word (curr, &curr); - if (word) - { - int errcode = add_domain (conf, word); - if (errcode) - { - // something badly wrong, bail - return errcode; - } - - if (get_next_word (curr, NULL)) - { - syslog (LOG_WARNING, - "%s, line %d: ignored extra text found after domain", - context->filename, - context->linenum - ); - } - } - else - { - syslog (LOG_WARNING, - "%s, line %d: no domain specified", - context->filename, - context->linenum - ); - } - } - else - { - syslog (LOG_WARNING, - "%s, line %d: unknown keyword %s - skipping", - context->filename, - context->linenum, - word - ); - } - - return 0; + char * curr = line; + char * word; + + word = get_next_word (curr, &curr); + if (!word || word [0] == k_comment_char) + { + // Nothing interesting on this line + return 0; + } + + if (strcmp (word, k_keyword_domain) == 0) + { + word = get_next_word (curr, &curr); + if (word) + { + int errcode = add_domain (conf, word); + if (errcode) + { + // something badly wrong, bail + return errcode; + } + + if (get_next_word (curr, NULL)) + { + syslog (LOG_WARNING, + "%s, line %d: ignored extra text found after domain", + context->filename, + context->linenum + ); + } + } + else + { + syslog (LOG_WARNING, + "%s, line %d: no domain specified", + context->filename, + context->linenum + ); + } + } + else + { + syslog (LOG_WARNING, + "%s, line %d: unknown keyword %s - skipping", + context->filename, + context->linenum, + word + ); + } + + return 0; } /* - Get next word (whitespace separated) from input string. - A null character is written into the first whitespace character following - the word. - - Parameters - input - Input string. This string is modified by get_next_word. - next - If non-NULL and the result is non-NULL, a pointer to the - character following the end of the word (after the null) - is written to 'next'. - If no word is found, the original value is unchanged. - If the word extended to the end of the string, 'next' points - to the trailling NULL. - It is safe to pass 'str' as 'input' and '&str' as 'next'. - Returns - Pointer to the first non-whitespace character (and thus word) found. - if no word is found, returns NULL. + Get next word (whitespace separated) from input string. + A null character is written into the first whitespace character following + the word. + + Parameters + input + Input string. This string is modified by get_next_word. + next + If non-NULL and the result is non-NULL, a pointer to the + character following the end of the word (after the null) + is written to 'next'. + If no word is found, the original value is unchanged. + If the word extended to the end of the string, 'next' points + to the trailling NULL. + It is safe to pass 'str' as 'input' and '&str' as 'next'. + Returns + Pointer to the first non-whitespace character (and thus word) found. + if no word is found, returns NULL. */ static char * get_next_word (char * input, char **next) { - char * curr = input; - char * result; - - while (isspace (*curr)) - { - curr ++; - } - - if (*curr == 0) - { - return NULL; - } - - result = curr; - while (*curr && ! isspace (*curr)) - { - curr++; - } - if (*curr) - { - *curr = 0; - if (next) - { - *next = curr+1; - } - } - else - { - if (next) - { - *next = curr; - } - } - - return result; + char * curr = input; + char * result; + + while (isspace (*curr)) + { + curr++; + } + + if (*curr == 0) + { + return NULL; + } + + result = curr; + while (*curr && !isspace (*curr)) + { + curr++; + } + if (*curr) + { + *curr = 0; + if (next) + { + *next = curr+1; + } + } + else + { + if (next) + { + *next = curr; + } + } + + return result; } static errcode_t default_config (config_t * conf) { - int i; - for (i = 0; k_default_domains [i]; i++) - { - int errcode = - add_domain (conf, k_default_domains [i]); - if (errcode) - { - // Something has gone (badly) wrong - let's bail - return errcode; - } - } - - return 0; + int i; + for (i = 0; k_default_domains [i]; i++) + { + int errcode = + add_domain (conf, k_default_domains [i]); + if (errcode) + { + // Something has gone (badly) wrong - let's bail + return errcode; + } + } + + return 0; } static errcode_t add_domain (config_t * conf, const char * domain) { - if (! contains_domain (conf, domain)) - { - domain_entry_t * d = - (domain_entry_t *) malloc (sizeof (domain_entry_t)); - if (! d) - { - syslog (LOG_ERR, - "mdns: Can't allocate memory in nss_mdns:init_config, %s:%d", - __FILE__, __LINE__ - ); - return ENOMEM; - } - - d->domain = strdup (domain); - if (! d->domain) - { - syslog (LOG_ERR, - "mdns: Can't allocate memory in nss_mdns:init_config, %s:%d", - __FILE__, __LINE__ - ); - free (d); - return ENOMEM; - } - d->next = conf->domains; - conf->domains = d; - } - - return 0; + if (!contains_domain (conf, domain)) + { + domain_entry_t * d = + (domain_entry_t *) malloc (sizeof (domain_entry_t)); + if (!d) + { + syslog (LOG_ERR, + "mdns: Can't allocate memory in nss_mdns:init_config, %s:%d", + __FILE__, __LINE__ + ); + return ENOMEM; + } + + d->domain = strdup (domain); + if (!d->domain) + { + syslog (LOG_ERR, + "mdns: Can't allocate memory in nss_mdns:init_config, %s:%d", + __FILE__, __LINE__ + ); + free (d); + return ENOMEM; + } + d->next = conf->domains; + conf->domains = d; + } + + return 0; } static int contains_domain (const config_t * conf, const char * domain) { - const domain_entry_t * curr = conf->domains; - - while (curr != NULL) - { - if (strcasecmp (curr->domain, domain) == 0) - { - return 1; - } - - curr = curr->next; - } - - return 0; + const domain_entry_t * curr = conf->domains; + + while (curr != NULL) + { + if (strcasecmp (curr->domain, domain) == 0) + { + return 1; + } + + curr = curr->next; + } + + return 0; } static int contains_domain_suffix (const config_t * conf, const char * addr) { - const domain_entry_t * curr = conf->domains; - - while (curr != NULL) - { - if (cmp_dns_suffix (addr, curr->domain) > 0) - { - return 1; - } - - curr = curr->next; - } - - return 0; + const domain_entry_t * curr = conf->domains; + + while (curr != NULL) + { + if (cmp_dns_suffix (addr, curr->domain) > 0) + { + return 1; + } + + curr = curr->next; + } + + return 0; } //---------- @@ -2149,82 +2149,82 @@ contains_domain_suffix (const config_t * conf, const char * addr) static const char * k_local_suffix = "local"; static const char k_dns_separator = '.'; -static const int k_label_maxlen = DNS_LABEL_MAXLEN; - // Label entries longer than this are actually pointers. +static const unsigned int k_label_maxlen = DNS_LABEL_MAXLEN; +// Label entries longer than this are actually pointers. typedef struct { - int value; - const char * name; - const char * comment; + int value; + const char * name; + const char * comment; } table_entry_t; static const table_entry_t k_table_af [] = - { - { AF_UNSPEC, NULL, NULL }, - { AF_LOCAL, "LOCAL", NULL }, - { AF_UNIX, "UNIX", NULL }, - { AF_INET, "INET", NULL }, - { AF_INET6, "INET6", NULL } - }; +{ + { AF_UNSPEC, NULL, NULL }, + { AF_LOCAL, "LOCAL", NULL }, + { AF_UNIX, "UNIX", NULL }, + { AF_INET, "INET", NULL }, + { AF_INET6, "INET6", NULL } +}; static const int k_table_af_size = - sizeof (k_table_af) / sizeof (* k_table_af); + sizeof (k_table_af) / sizeof (*k_table_af); static const char * k_table_ns_class [] = - { - NULL, - "IN" - }; +{ + NULL, + "IN" +}; static const int k_table_ns_class_size = - sizeof (k_table_ns_class) / sizeof (* k_table_ns_class); + sizeof (k_table_ns_class) / sizeof (*k_table_ns_class); static const char * k_table_ns_type [] = - { - NULL, - "A", - "NS", - "MD", - "MF", - "CNAME", - "SOA", - "MB", - "MG", - "MR", - "NULL", - "WKS", - "PTR", - "HINFO", - "MINFO", - "MX", - "TXT", - "RP", - "AFSDB", - "X25", - "ISDN", - "RT", - "NSAP", - NULL, - "SIG", - "KEY", - "PX", - "GPOS", - "AAAA", - "LOC", - "NXT", - "EID", - "NIMLOC", - "SRV", - "ATMA", - "NAPTR", - "KX", - "CERT", - "A6", - "DNAME", - "SINK", - "OPT" - }; +{ + NULL, + "A", + "NS", + "MD", + "MF", + "CNAME", + "SOA", + "MB", + "MG", + "MR", + "NULL", + "WKS", + "PTR", + "HINFO", + "MINFO", + "MX", + "TXT", + "RP", + "AFSDB", + "X25", + "ISDN", + "RT", + "NSAP", + NULL, + "SIG", + "KEY", + "PX", + "GPOS", + "AAAA", + "LOC", + "NXT", + "EID", + "NIMLOC", + "SRV", + "ATMA", + "NAPTR", + "KX", + "CERT", + "A6", + "DNAME", + "SINK", + "OPT" +}; static const int k_table_ns_type_size = - sizeof (k_table_ns_type) / sizeof (* k_table_ns_type); + sizeof (k_table_ns_type) / sizeof (*k_table_ns_type); //---------- @@ -2250,373 +2250,373 @@ table_index_value (const table_entry_t table [], int size, int n); int count_dots (const char * name) { - int count = 0; - int i; - for (i = 0; name[i]; i++) - { - if (name [i] == k_dns_separator) - count++; - } - - return count; + int count = 0; + int i; + for (i = 0; name[i]; i++) + { + if (name [i] == k_dns_separator) + count++; + } + + return count; } int islocal (const char * name) { - return cmp_dns_suffix (name, k_local_suffix) > 0; + return cmp_dns_suffix (name, k_local_suffix) > 0; } int rr_to_af (ns_type_t rrtype) { - switch (rrtype) - { - case kDNSServiceType_A: - return AF_INET; - - case kDNSServiceType_AAAA: - return AF_INET6; - - default: - return AF_UNSPEC; - } + switch (rrtype) + { + case kDNSServiceType_A: + return AF_INET; + + case kDNSServiceType_AAAA: + return AF_INET6; + + default: + return AF_UNSPEC; + } } ns_type_t af_to_rr (int af) { - switch (af) - { - case AF_INET: - return kDNSServiceType_A; - - case AF_INET6: - return kDNSServiceType_AAAA; - - default: - //return ns_t_invalid; - return 0; - } + switch (af) + { + case AF_INET: + return kDNSServiceType_A; + + case AF_INET6: + return kDNSServiceType_AAAA; + + default: + //return ns_t_invalid; + return 0; + } } int str_to_af (const char * str) { - int result = - table_index_name (k_table_af, k_table_af_size, str); - if (result < 0) - result = 0; + int result = + table_index_name (k_table_af, k_table_af_size, str); + if (result < 0) + result = 0; - return k_table_af [result].value; + return k_table_af [result].value; } ns_class_t str_to_ns_class (const char * str) { - return (ns_class_t) - simple_table_index (k_table_ns_class, k_table_ns_class_size, str); + return (ns_class_t) + simple_table_index (k_table_ns_class, k_table_ns_class_size, str); } ns_type_t str_to_ns_type (const char * str) { - return (ns_type_t) - simple_table_index (k_table_ns_type, k_table_ns_type_size, str); + return (ns_type_t) + simple_table_index (k_table_ns_type, k_table_ns_type_size, str); } const char * af_to_str (int in) { - int result = - table_index_value (k_table_af, k_table_af_size, in); - if (result < 0) - result = 0; + int result = + table_index_value (k_table_af, k_table_af_size, in); + if (result < 0) + result = 0; - return k_table_af [result].name; + return k_table_af [result].name; } const char * ns_class_to_str (ns_class_t in) { - if (in < k_table_ns_class_size) - return k_table_ns_class [in]; - else - return NULL; + if (in < k_table_ns_class_size) + return k_table_ns_class [in]; + else + return NULL; } const char * ns_type_to_str (ns_type_t in) { - if (in < k_table_ns_type_size) - return k_table_ns_type [in]; - else - return NULL; + if (in < k_table_ns_type_size) + return k_table_ns_type [in]; + else + return NULL; } char * format_reverse_addr_in ( - const struct in_addr * addr, - int prefixlen, - char * buf -) + const struct in_addr * addr, + int prefixlen, + char * buf + ) { - char * curr = buf; - int i; - - const uint8_t * in_addr_a = (uint8_t *) addr; - - if (prefixlen > 32) - return NULL; - if (prefixlen < 0) - prefixlen = 32; - - i = (prefixlen + 7) / 8; - // divide prefixlen into bytes, rounding up - - while (i > 0) - { - i--; - curr += sprintf (curr, "%d.", in_addr_a [i]); - } - sprintf (curr, "in-addr.arpa"); - - return buf; + char * curr = buf; + int i; + + const uint8_t * in_addr_a = (uint8_t *) addr; + + if (prefixlen > 32) + return NULL; + if (prefixlen < 0) + prefixlen = 32; + + i = (prefixlen + 7) / 8; + // divide prefixlen into bytes, rounding up + + while (i > 0) + { + i--; + curr += sprintf (curr, "%d.", in_addr_a [i]); + } + sprintf (curr, "in-addr.arpa"); + + return buf; } char * format_reverse_addr_in6 ( - const struct in6_addr * addr, - int prefixlen, - char * buf -) + const struct in6_addr * addr, + int prefixlen, + char * buf + ) { - char * curr = buf; - int i; - - const uint8_t * in_addr_a = (uint8_t *) addr; - - if (prefixlen > 128) - return NULL; - if (prefixlen < 0) - prefixlen = 128; - - i = (prefixlen + 3) / 4; - // divide prefixlen into nibbles, rounding up - - // Special handling for first - if (i % 2) - { - curr += sprintf (curr, "%d.", (in_addr_a [i/2] >> 4) & 0x0F); - } - i >>= 1; - // Convert i to bytes (divide by 2) - - while (i > 0) - { - uint8_t val; - - i--; - val = in_addr_a [i]; - curr += sprintf (curr, "%x.%x.", val & 0x0F, (val >> 4) & 0x0F); - } - sprintf (curr, "ip6.arpa"); - - return buf; + char * curr = buf; + int i; + + const uint8_t * in_addr_a = (uint8_t *) addr; + + if (prefixlen > 128) + return NULL; + if (prefixlen < 0) + prefixlen = 128; + + i = (prefixlen + 3) / 4; + // divide prefixlen into nibbles, rounding up + + // Special handling for first + if (i % 2) + { + curr += sprintf (curr, "%d.", (in_addr_a [i/2] >> 4) & 0x0F); + } + i >>= 1; + // Convert i to bytes (divide by 2) + + while (i > 0) + { + uint8_t val; + + i--; + val = in_addr_a [i]; + curr += sprintf (curr, "%x.%x.", val & 0x0F, (val >> 4) & 0x0F); + } + sprintf (curr, "ip6.arpa"); + + return buf; } char * format_reverse_addr ( - int af, - const void * addr, - int prefixlen, - char * buf -) + int af, + const void * addr, + int prefixlen, + char * buf + ) { - switch (af) - { - case AF_INET: - return - format_reverse_addr_in ( - (struct in_addr *) addr, prefixlen, buf - ); - break; - - case AF_INET6: - return - format_reverse_addr_in6 ( - (struct in6_addr *) addr, prefixlen, buf - ); - break; - - default: - return NULL; - } + switch (af) + { + case AF_INET: + return + format_reverse_addr_in ( + (struct in_addr *) addr, prefixlen, buf + ); + break; + + case AF_INET6: + return + format_reverse_addr_in6 ( + (struct in6_addr *) addr, prefixlen, buf + ); + break; + + default: + return NULL; + } } int cmp_dns_suffix (const char * name, const char * domain) { - const char * nametail; - const char * domaintail; - - // Idiot checks - if (*name == 0 || *name == k_dns_separator) - { - // Name can't be empty or start with separator - return CMP_DNS_SUFFIX_BAD_NAME; - } - - if (*domain == 0) - { - return CMP_DNS_SUFFIX_SUCCESS; - // trivially true - } - - if (*domain == k_dns_separator) - { - // drop leading separator from domain - domain++; - if (*domain == k_dns_separator) - { - return CMP_DNS_SUFFIX_BAD_DOMAIN; - } - } - - // Find ends of strings - for (nametail = name; *nametail; nametail++) - ; - for (domaintail = domain; *domaintail; domaintail++) - ; - - // Shuffle back to last real character, and drop any trailing '.' - // while we're at it. - nametail --; - if (*nametail == k_dns_separator) - { - nametail --; - if (*nametail == k_dns_separator) - { - return CMP_DNS_SUFFIX_BAD_NAME; - } - } - domaintail --; - if (*domaintail == k_dns_separator) - { - domaintail --; - if (*domaintail == k_dns_separator) - { - return CMP_DNS_SUFFIX_BAD_DOMAIN; - } - } - - // Compare. - while ( - nametail >= name - && domaintail >= domain - && tolower(*nametail) == tolower(*domaintail)) - { - nametail--; - domaintail--; - } - - /* A successful finish will be one of the following: - (leading and trailing . ignored) - - name : domain2.domain1 - domain: domain2.domain1 - ^ - - name : domain3.domain2.domain1 - domain: domain2.domain1 - ^ - */ - if ( - domaintail < domain - && (nametail < name || *nametail == k_dns_separator) - ) - { - return CMP_DNS_SUFFIX_SUCCESS; - } - else - { - return CMP_DNS_SUFFIX_FAILURE; - } + const char * nametail; + const char * domaintail; + + // Idiot checks + if (*name == 0 || *name == k_dns_separator) + { + // Name can't be empty or start with separator + return CMP_DNS_SUFFIX_BAD_NAME; + } + + if (*domain == 0) + { + return CMP_DNS_SUFFIX_SUCCESS; + // trivially true + } + + if (*domain == k_dns_separator) + { + // drop leading separator from domain + domain++; + if (*domain == k_dns_separator) + { + return CMP_DNS_SUFFIX_BAD_DOMAIN; + } + } + + // Find ends of strings + for (nametail = name; *nametail; nametail++) + ; + for (domaintail = domain; *domaintail; domaintail++) + ; + + // Shuffle back to last real character, and drop any trailing '.' + // while we're at it. + nametail--; + if (*nametail == k_dns_separator) + { + nametail--; + if (*nametail == k_dns_separator) + { + return CMP_DNS_SUFFIX_BAD_NAME; + } + } + domaintail--; + if (*domaintail == k_dns_separator) + { + domaintail--; + if (*domaintail == k_dns_separator) + { + return CMP_DNS_SUFFIX_BAD_DOMAIN; + } + } + + // Compare. + while ( + nametail >= name + && domaintail >= domain + && tolower(*nametail) == tolower(*domaintail)) + { + nametail--; + domaintail--; + } + + /* A successful finish will be one of the following: + (leading and trailing . ignored) + + name : domain2.domain1 + domain: domain2.domain1 + ^ + + name : domain3.domain2.domain1 + domain: domain2.domain1 + ^ + */ + if ( + domaintail < domain + && (nametail < name || *nametail == k_dns_separator) + ) + { + return CMP_DNS_SUFFIX_SUCCESS; + } + else + { + return CMP_DNS_SUFFIX_FAILURE; + } } -int -dns_rdata_to_name (const char * rdata, int rdlen, char * name, int name_len) -{ - int i = 0; - // Index into 'name' - const char * rdata_curr = rdata; - - if (rdlen == 0) return DNS_RDATA_TO_NAME_BAD_FORMAT; - - /* - In RDATA, a DNS name is stored as a series of labels. - Each label consists of a length octet (max value 63) - followed by the data for that label. - The series is terminated with a length 0 octet. - A length octet beginning with bits 11 is a pointer to - somewhere else in the payload, but we don't support these - since we don't have access to the entire payload. - - See RFC1034 section 3.1 and RFC1035 section 3.1. - */ - while (1) - { - int term_len = *rdata_curr; - rdata_curr++; - - if (term_len == 0) - { - break; - // 0 length record terminates label - } - else if (term_len > k_label_maxlen) - { - name [i] = 0; - return DNS_RDATA_TO_NAME_PTR; - } - else if (rdata_curr + term_len > rdata + rdlen) - { - name [i] = 0; - return DNS_RDATA_TO_NAME_BAD_FORMAT; - } - - if (name_len < i + term_len + 1) - // +1 is separator - { - name [i] = 0; - return DNS_RDATA_TO_NAME_TOO_LONG; - } - - memcpy (name + i, rdata_curr, term_len); - - i += term_len; - rdata_curr += term_len; - - name [i] = k_dns_separator; - i++; - } - - name [i] = 0; - return i; +static int +dns_rdata_to_name (const unsigned char * rdata, int rdlen, char * name, unsigned int name_len) +{ + int i = 0; + // Index into 'name' + const unsigned char * rdata_curr = rdata; + + if (rdlen == 0) return DNS_RDATA_TO_NAME_BAD_FORMAT; + + /* + In RDATA, a DNS name is stored as a series of labels. + Each label consists of a length octet (max value 63) + followed by the data for that label. + The series is terminated with a length 0 octet. + A length octet beginning with bits 11 is a pointer to + somewhere else in the payload, but we don't support these + since we don't have access to the entire payload. + + See RFC1034 section 3.1 and RFC1035 section 3.1. + */ + while (1) + { + unsigned int term_len = *rdata_curr; + rdata_curr++; + + if (term_len == 0) + { + break; + // 0 length record terminates label + } + else if (term_len > k_label_maxlen) + { + name [i] = 0; + return DNS_RDATA_TO_NAME_PTR; + } + else if (rdata_curr + term_len > rdata + rdlen) + { + name [i] = 0; + return DNS_RDATA_TO_NAME_BAD_FORMAT; + } + + if (name_len < i + term_len + 1) + // +1 is separator + { + name [i] = 0; + return DNS_RDATA_TO_NAME_TOO_LONG; + } + + memcpy (name + i, rdata_curr, term_len); + + i += term_len; + rdata_curr += term_len; + + name [i] = k_dns_separator; + i++; + } + + name [i] = 0; + return i; } @@ -2624,100 +2624,100 @@ dns_rdata_to_name (const char * rdata, int rdlen, char * name, int name_len) // Local functions /* - Find the index of an string entry in a table. A case insenitive match - is performed. If no entry is found, 0 is returned. - - Parameters - table - Lookup table - Table entries may be NULL. NULL entries will never match. - size - number of entries in table - str - lookup string - - Result - index of first matching entry, or 0 if no matches + Find the index of an string entry in a table. A case insenitive match + is performed. If no entry is found, 0 is returned. + + Parameters + table + Lookup table + Table entries may be NULL. NULL entries will never match. + size + number of entries in table + str + lookup string + + Result + index of first matching entry, or 0 if no matches */ static int simple_table_index (const char * table [], int size, const char * str) { - int i; - for (i = 0; i < size; i++) - { - if ( - table [i] - && (strcasecmp (table [i], str) == 0) - ) - { - return i; - } - } - - return 0; + int i; + for (i = 0; i < size; i++) + { + if ( + table [i] + && (strcasecmp (table [i], str) == 0) + ) + { + return i; + } + } + + return 0; } /* - Find the index of a name in a table. - - Parameters - table - array of table_entry_t records. The name field is compared - (ignoring case) to the input string. - size - number of entries in table - str - lookup string - - Result - index of first matching entry, or -1 if no matches + Find the index of a name in a table. + + Parameters + table + array of table_entry_t records. The name field is compared + (ignoring case) to the input string. + size + number of entries in table + str + lookup string + + Result + index of first matching entry, or -1 if no matches */ static int table_index_name (const table_entry_t table [], int size, const char * str) { - int i; - for (i = 0; i < size; i++) - { - if ( - table [i].name - && (strcasecmp (table [i].name, str) == 0) - ) - { - return i; - } - } - - return -1; + int i; + for (i = 0; i < size; i++) + { + if ( + table [i].name + && (strcasecmp (table [i].name, str) == 0) + ) + { + return i; + } + } + + return -1; } /* - Find the index of a value a table. - - Parameters - table - array of table_entry_t records. The value field is compared to - the input value - size - number of entries in table - n - lookup value - - Result - index of first matching entry, or -1 if no matches + Find the index of a value a table. + + Parameters + table + array of table_entry_t records. The value field is compared to + the input value + size + number of entries in table + n + lookup value + + Result + index of first matching entry, or -1 if no matches */ static int table_index_value (const table_entry_t table [], int size, int n) { - int i; - for (i = 0; i < size; i++) - { - if (table [i].value == n) - { - return i; - } - } - - return -1; + int i; + for (i = 0; i < size; i++) + { + if (table [i].value == n) + { + return i; + } + } + + return -1; } diff --git a/mDNSShared/CommonServices.h b/mDNSShared/CommonServices.h index 1261f1d..342479b 100644 --- a/mDNSShared/CommonServices.h +++ b/mDNSShared/CommonServices.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,15 +17,15 @@ //--------------------------------------------------------------------------------------------------------------------------- /*! @header CommonServices - - Common Services for Mac OS X, Linux, Palm, VxWorks, Windows, and Windows CE. -*/ -#ifndef __COMMON_SERVICES__ -#define __COMMON_SERVICES__ + Common Services for Mac OS X, Linux, Palm, VxWorks, Windows, and Windows CE. + */ -#ifdef __cplusplus - extern "C" { +#ifndef __COMMON_SERVICES__ +#define __COMMON_SERVICES__ + +#ifdef __cplusplus +extern "C" { #endif #if 0 @@ -38,87 +38,97 @@ // Macintosh -#if( !defined( TARGET_OS_MAC ) ) - #if( ( macintosh || __MACH__ ) && !KERNEL ) - // ConditionalMacros.h in CoreServices will define this TARGET_* flag. - #else - #define TARGET_OS_MAC 0 - #endif +#if ( !defined( TARGET_OS_MAC ) ) + #if ( ( macintosh || __MACH__ ) && !KERNEL ) +// ConditionalMacros.h in CoreServices will define this TARGET_* flag. + #else + #define TARGET_OS_MAC 0 + #endif +#endif + +#if ( !defined( TARGET_API_MAC_OSX_KERNEL ) ) + #if ( __MACH__ && KERNEL ) + #define TARGET_API_MAC_OSX_KERNEL 1 + #else + #define TARGET_API_MAC_OSX_KERNEL 0 + #endif #endif -#if( !defined( TARGET_API_MAC_OSX_KERNEL ) ) - #if( __MACH__ && KERNEL ) - #define TARGET_API_MAC_OSX_KERNEL 1 - #else - #define TARGET_API_MAC_OSX_KERNEL 0 - #endif +// FreeBSD + +#if ( !defined( TARGET_OS_FREEBSD ) ) + #if ( defined( __FreeBSD__ ) ) + #define TARGET_OS_FREEBSD 1 + #else + #define TARGET_OS_FREEBSD 0 + #endif #endif // Linux -#if( !defined( TARGET_OS_LINUX ) ) - #if( defined( __linux__ ) ) - #define TARGET_OS_LINUX 1 - #else - #define TARGET_OS_LINUX 0 - #endif +#if ( !defined( TARGET_OS_LINUX ) ) + #if ( defined( __linux__ ) ) + #define TARGET_OS_LINUX 1 + #else + #define TARGET_OS_LINUX 0 + #endif #endif // Solaris -#if( !defined( TARGET_OS_SOLARIS ) ) - #if( defined(solaris) || (defined(__SVR4) && defined(sun)) ) - #define TARGET_OS_SOLARIS 1 - #else - #define TARGET_OS_SOLARIS 0 - #endif +#if ( !defined( TARGET_OS_SOLARIS ) ) + #if ( defined(solaris) || (defined(__SVR4) && defined(sun)) ) + #define TARGET_OS_SOLARIS 1 + #else + #define TARGET_OS_SOLARIS 0 + #endif #endif // Palm -#if( !defined( TARGET_OS_PALM ) ) - #if( defined( __PALMOS_TRAPS__ ) || defined( __PALMOS_ARMLET__ ) ) - #define TARGET_OS_PALM 1 - #else - #define TARGET_OS_PALM 0 - #endif +#if ( !defined( TARGET_OS_PALM ) ) + #if ( defined( __PALMOS_TRAPS__ ) || defined( __PALMOS_ARMLET__ ) ) + #define TARGET_OS_PALM 1 + #else + #define TARGET_OS_PALM 0 + #endif #endif // VxWorks -#if( !defined( TARGET_OS_VXWORKS ) ) - - // No predefined macro for VxWorks so just assume VxWorks if nothing else is set. - - #if( !macintosh && !__MACH__ && !defined( __linux__ ) && !defined ( __SVR4 ) && !defined ( __sun ) && !defined( __PALMOS_TRAPS__ ) && !defined( __PALMOS_ARMLET__ ) && !defined( _WIN32 ) ) - #define TARGET_OS_VXWORKS 1 - #else - #define TARGET_OS_VXWORKS 0 - #endif +#if ( !defined( TARGET_OS_VXWORKS ) ) + +// No predefined macro for VxWorks so just assume VxWorks if nothing else is set. + + #if ( !macintosh && !__MACH__ && !defined( __FreeBSD__ ) && !defined( __linux__ ) && !defined ( __SVR4 ) && !defined ( __sun ) && !defined( __PALMOS_TRAPS__ ) && !defined( __PALMOS_ARMLET__ ) && !defined( _WIN32 ) ) + #define TARGET_OS_VXWORKS 1 + #else + #define TARGET_OS_VXWORKS 0 + #endif #endif // Windows -#if( !defined( TARGET_OS_WIN32 ) ) - #if( macintosh || __MACH__ ) - // ConditionalMacros.h in CoreServices will define this TARGET_* flag. - #else - #if( defined( _WIN32 ) ) - #define TARGET_OS_WIN32 1 - #else - #define TARGET_OS_WIN32 0 - #endif - #endif +#if ( !defined( TARGET_OS_WIN32 ) ) + #if ( macintosh || __MACH__ ) +// ConditionalMacros.h in CoreServices will define this TARGET_* flag. + #else + #if ( defined( _WIN32 ) ) + #define TARGET_OS_WIN32 1 + #else + #define TARGET_OS_WIN32 0 + #endif + #endif #endif // Windows CE -#if( !defined( TARGET_OS_WINDOWS_CE ) ) - #if( defined( _WIN32_WCE ) ) - #define TARGET_OS_WINDOWS_CE 1 - #else - #define TARGET_OS_WINDOWS_CE 0 - #endif +#if ( !defined( TARGET_OS_WINDOWS_CE ) ) + #if ( defined( _WIN32_WCE ) ) + #define TARGET_OS_WINDOWS_CE 1 + #else + #define TARGET_OS_WINDOWS_CE 0 + #endif #endif #if 0 @@ -129,142 +139,151 @@ // Includes //=========================================================================================================================== -#if( !KERNEL ) - #if defined(WIN32) && !defined(_WSPIAPI_COUNTOF) - #define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0])) - #endif - #include -#endif - -#if( ( macintosh || __MACH__ ) && !KERNEL ) - - #if( defined( __MWERKS__ ) ) - #if( __option( c9x ) ) - #include - #endif - #else - #include - #endif - - #include - - #if( __MACH__ ) - - // Mac OS X - - #include - #include - #include - #include - #include - #include - #include - #include - - #else - - // Classic Mac OS - - #include - #include - - #endif - -#elif( KERNEL ) - - // Mac OS X Kernel - - #include - - #include - #include - -#elif( TARGET_OS_LINUX ) - - // Linux - - #include - #include - -#elif( TARGET_OS_SOLARIS ) - - // Solaris - - #include - - #include - #include - - #if ( defined( BYTE_ORDER ) && defined( LITTLE_ENDIAN ) && ( BYTE_ORDER == LITTLE_ENDIAN ) ) - #define TARGET_RT_LITTLE_ENDIAN 1 - #endif - #if ( defined( BYTE_ORDER ) && defined( BIG_ENDIAN ) && ( BYTE_ORDER == BIG_ENDIAN ) ) - #define TARGET_RT_BIG_ENDIAN 1 - #endif - -#elif( TARGET_OS_PALM ) - - // Palm (no special includes yet). - -#elif( TARGET_OS_VXWORKS ) - - // VxWorks - - #include "vxWorks.h" - -#elif( TARGET_OS_WIN32 ) - - // Windows - - #if( !defined( WIN32_WINDOWS ) ) - #define WIN32_WINDOWS 0x0401 - #endif - - #if( !defined( _WIN32_WINDOWS ) ) - #define _WIN32_WINDOWS 0x0401 - #endif - - #if( !defined( WIN32_LEAN_AND_MEAN ) ) - #define WIN32_LEAN_AND_MEAN // Needed to avoid redefinitions by Windows interfaces. - #endif - - #if( defined( __MWERKS__ ) ) - - #if( __option( c9x ) ) - #include - #endif - - #include - - #elif( defined( _MSC_VER ) ) - - #pragma warning( disable:4127 ) // Disable "conditional expression is constant" warning for debug macros. - #pragma warning( disable:4706 ) // Disable "assignment within conditional expression" for Microsoft headers. - - #endif - - #include - #include - #include - - #if( defined( _MSC_VER ) ) - #pragma warning( default:4706 ) - #endif - +#if ( !KERNEL ) + #if defined(WIN32) && !defined(_WSPIAPI_COUNTOF) + #define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0])) + #endif + #include +#endif + +#if ( ( macintosh || __MACH__ ) && !KERNEL ) + + #if ( defined( __MWERKS__ ) ) + #if ( __option( c9x ) ) + #include + #endif + #else + #include + #endif + + #include + + #if ( __MACH__ ) + +// Mac OS X + + #include + #include + #include + #include + #include + #include + #include + #include + + #else + +// Classic Mac OS + + #include + #include + + #endif + +#elif ( KERNEL ) + +// Mac OS X Kernel + + #include + + #include + #include + +#elif ( TARGET_OS_FREEBSD ) + +// FreeBSD + #include + #include + #include + #include + #include + +#elif ( TARGET_OS_LINUX ) + +// Linux + + #include + #include + +#elif ( TARGET_OS_SOLARIS ) + +// Solaris + + #include + + #include + #include + + #if ( defined( BYTE_ORDER ) && defined( LITTLE_ENDIAN ) && ( BYTE_ORDER == LITTLE_ENDIAN ) ) + #define TARGET_RT_LITTLE_ENDIAN 1 + #endif + #if ( defined( BYTE_ORDER ) && defined( BIG_ENDIAN ) && ( BYTE_ORDER == BIG_ENDIAN ) ) + #define TARGET_RT_BIG_ENDIAN 1 + #endif + +#elif ( TARGET_OS_PALM ) + +// Palm (no special includes yet). + +#elif ( TARGET_OS_VXWORKS ) + +// VxWorks + + #include "vxWorks.h" + +#elif ( TARGET_OS_WIN32 ) + +// Windows + + #if ( !defined( WIN32_WINDOWS ) ) + #define WIN32_WINDOWS 0x0401 + #endif + + #if ( !defined( _WIN32_WINDOWS ) ) + #define _WIN32_WINDOWS 0x0401 + #endif + + #if ( !defined( WIN32_LEAN_AND_MEAN ) ) + #define WIN32_LEAN_AND_MEAN // Needed to avoid redefinitions by Windows interfaces. + #endif + + #if ( defined( __MWERKS__ ) ) + + #if ( __option( c9x ) ) + #include + #endif + + #include + + #elif ( defined( _MSC_VER ) ) + + #pragma warning( disable:4127 ) // Disable "conditional expression is constant" warning for debug macros. + #pragma warning( disable:4706 ) // Disable "assignment within conditional expression" for Microsoft headers. + + #endif + + #include + #include + #include + + #if ( defined( _MSC_VER ) ) + #pragma warning( default:4706 ) + #endif + #else - #error unknown OS - update this file to support your OS + #error unknown OS - update this file to support your OS #endif -#if( !defined( TARGET_BUILD_MAIN ) ) - #if( !TARGET_OS_VXWORKS ) - #define TARGET_BUILD_MAIN 1 - #endif +#if ( !defined( TARGET_BUILD_MAIN ) ) + #if ( !TARGET_OS_VXWORKS ) + #define TARGET_BUILD_MAIN 1 + #endif #endif -#if( __GNUC__ || !TARGET_OS_VXWORKS ) - #define TARGET_LANGUAGE_C_LIKE 1 +#if ( __GNUC__ || !TARGET_OS_VXWORKS ) + #define TARGET_LANGUAGE_C_LIKE 1 #else - #define TARGET_LANGUAGE_C_LIKE 0 + #define TARGET_LANGUAGE_C_LIKE 0 #endif #if 0 @@ -277,36 +296,36 @@ // PowerPC -#if( !defined( TARGET_CPU_PPC ) ) - #if( defined( __ppc__ ) || defined( __PPC__ ) || defined( powerpc ) || defined( ppc ) || defined( _M_MPPC ) ) - #define TARGET_CPU_PPC 1 - #else - #define TARGET_CPU_PPC 0 - #endif +#if ( !defined( TARGET_CPU_PPC ) ) + #if ( defined( __ppc__ ) || defined( __PPC__ ) || defined( powerpc ) || defined( ppc ) || defined( _M_MPPC ) ) + #define TARGET_CPU_PPC 1 + #else + #define TARGET_CPU_PPC 0 + #endif #endif // x86 -#if( !defined( TARGET_CPU_X86 ) ) - #if( __INTEL__ || defined( __i386__ ) || defined( i386 ) || defined( intel ) || defined( _M_IX86 ) ) - #define TARGET_CPU_X86 1 - #else - #define TARGET_CPU_X86 0 - #endif +#if ( !defined( TARGET_CPU_X86 ) ) + #if ( __INTEL__ || defined( __i386__ ) || defined( i386 ) || defined( intel ) || defined( _M_IX86 ) ) + #define TARGET_CPU_X86 1 + #else + #define TARGET_CPU_X86 0 + #endif #endif // MIPS -#if( !defined( TARGET_CPU_MIPS ) ) - #if( __MIPS__ || defined( MIPS32 ) || defined( R3000 ) || defined( R4000 ) || defined( R4650 ) || defined( _M_MRX000 ) ) - #define TARGET_CPU_MIPS 1 - #else - #define TARGET_CPU_MIPS 0 - #endif +#if ( !defined( TARGET_CPU_MIPS ) ) + #if ( __MIPS__ || defined( MIPS32 ) || defined( R3000 ) || defined( R4000 ) || defined( R4650 ) || defined( _M_MRX000 ) ) + #define TARGET_CPU_MIPS 1 + #else + #define TARGET_CPU_MIPS 0 + #endif #endif -#if( !defined( TARGET_CPU_PPC ) && !defined( TARGET_CPU_X86 ) && !defined( TARGET_CPU_MIPS ) ) - #error unknown CPU - update this file to support your CPU +#if ( !defined( TARGET_CPU_PPC ) && !defined( TARGET_CPU_X86 ) && !defined( TARGET_CPU_MIPS ) ) + #error unknown CPU - update this file to support your CPU #endif #if 0 @@ -319,68 +338,68 @@ // TARGET_RT_LITTLE_ENDIAN -#if( !defined( TARGET_RT_LITTLE_ENDIAN ) ) - #if( MIPSEL || IL_LITTLE_ENDIAN || defined( __LITTLE_ENDIAN__ ) || \ - ( defined( BYTE_ORDER ) && defined( LITTLE_ENDIAN ) && ( BYTE_ORDER == LITTLE_ENDIAN ) ) || \ - ( defined( _BYTE_ORDER ) && defined( _LITTLE_ENDIAN ) && ( _BYTE_ORDER == _LITTLE_ENDIAN ) ) || \ - ( defined( __BYTE_ORDER ) && defined( __LITTLE_ENDIAN ) && ( __BYTE_ORDER == __LITTLE_ENDIAN ) ) || \ - TARGET_CPU_X86 || ( defined( TARGET_RT_BIG_ENDIAN ) && !TARGET_RT_BIG_ENDIAN ) ) - #define TARGET_RT_LITTLE_ENDIAN 1 - #else - #define TARGET_RT_LITTLE_ENDIAN 0 - #endif +#if ( !defined( TARGET_RT_LITTLE_ENDIAN ) ) + #if ( MIPSEL || IL_LITTLE_ENDIAN || defined( __LITTLE_ENDIAN__ ) || \ + ( defined( BYTE_ORDER ) && defined( LITTLE_ENDIAN ) && ( BYTE_ORDER == LITTLE_ENDIAN ) ) || \ + ( defined( _BYTE_ORDER ) && defined( _LITTLE_ENDIAN ) && ( _BYTE_ORDER == _LITTLE_ENDIAN ) ) || \ + ( defined( __BYTE_ORDER ) && defined( __LITTLE_ENDIAN ) && ( __BYTE_ORDER == __LITTLE_ENDIAN ) ) || \ + TARGET_CPU_X86 || ( defined( TARGET_RT_BIG_ENDIAN ) && !TARGET_RT_BIG_ENDIAN ) ) + #define TARGET_RT_LITTLE_ENDIAN 1 + #else + #define TARGET_RT_LITTLE_ENDIAN 0 + #endif #endif // TARGET_RT_BIG_ENDIAN -#if( !defined( TARGET_RT_BIG_ENDIAN ) ) - #if( MIPSEB || IL_BIG_ENDIAN || defined( __BIG_ENDIAN__ ) || \ - ( defined( BYTE_ORDER ) && defined( BIG_ENDIAN ) && ( BYTE_ORDER == BIG_ENDIAN ) ) || \ - ( defined( _BYTE_ORDER ) && defined( _BIG_ENDIAN ) && ( _BYTE_ORDER == _BIG_ENDIAN ) ) || \ - ( defined( __BYTE_ORDER ) && defined( __BIG_ENDIAN ) && ( __BYTE_ORDER == __BIG_ENDIAN ) ) || \ - ( defined( TARGET_RT_LITTLE_ENDIAN ) && !TARGET_RT_LITTLE_ENDIAN ) ) - #define TARGET_RT_BIG_ENDIAN 1 - #else - #define TARGET_RT_BIG_ENDIAN 0 - #endif +#if ( !defined( TARGET_RT_BIG_ENDIAN ) ) + #if ( MIPSEB || IL_BIG_ENDIAN || defined( __BIG_ENDIAN__ ) || \ + ( defined( BYTE_ORDER ) && defined( BIG_ENDIAN ) && ( BYTE_ORDER == BIG_ENDIAN ) ) || \ + ( defined( _BYTE_ORDER ) && defined( _BIG_ENDIAN ) && ( _BYTE_ORDER == _BIG_ENDIAN ) ) || \ + ( defined( __BYTE_ORDER ) && defined( __BIG_ENDIAN ) && ( __BYTE_ORDER == __BIG_ENDIAN ) ) || \ + ( defined( TARGET_RT_LITTLE_ENDIAN ) && !TARGET_RT_LITTLE_ENDIAN ) ) + #define TARGET_RT_BIG_ENDIAN 1 + #else + #define TARGET_RT_BIG_ENDIAN 0 + #endif #endif -#if( defined( TARGET_RT_LITTLE_ENDIAN ) && !defined( TARGET_RT_BIG_ENDIAN ) ) - #if( TARGET_RT_LITTLE_ENDIAN ) - #define TARGET_RT_BIG_ENDIAN 0 - #else - #define TARGET_RT_BIG_ENDIAN 1 - #endif +#if ( defined( TARGET_RT_LITTLE_ENDIAN ) && !defined( TARGET_RT_BIG_ENDIAN ) ) + #if ( TARGET_RT_LITTLE_ENDIAN ) + #define TARGET_RT_BIG_ENDIAN 0 + #else + #define TARGET_RT_BIG_ENDIAN 1 + #endif #endif -#if( defined( TARGET_RT_BIG_ENDIAN ) && !defined( TARGET_RT_LITTLE_ENDIAN ) ) - #if( TARGET_RT_BIG_ENDIAN ) - #define TARGET_RT_LITTLE_ENDIAN 0 - #else - #define TARGET_RT_LITTLE_ENDIAN 1 - #endif +#if ( defined( TARGET_RT_BIG_ENDIAN ) && !defined( TARGET_RT_LITTLE_ENDIAN ) ) + #if ( TARGET_RT_BIG_ENDIAN ) + #define TARGET_RT_LITTLE_ENDIAN 0 + #else + #define TARGET_RT_LITTLE_ENDIAN 1 + #endif #endif -#if( !defined( TARGET_RT_LITTLE_ENDIAN ) || !defined( TARGET_RT_BIG_ENDIAN ) ) - #error unknown byte order - update this file to support your byte order +#if ( !defined( TARGET_RT_LITTLE_ENDIAN ) || !defined( TARGET_RT_BIG_ENDIAN ) ) + #error unknown byte order - update this file to support your byte order #endif // TARGET_RT_BYTE_ORDER -#if( !defined( TARGET_RT_BYTE_ORDER_BIG_ENDIAN ) ) - #define TARGET_RT_BYTE_ORDER_BIG_ENDIAN 1234 +#if ( !defined( TARGET_RT_BYTE_ORDER_BIG_ENDIAN ) ) + #define TARGET_RT_BYTE_ORDER_BIG_ENDIAN 1234 #endif -#if( !defined( TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN ) ) - #define TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN 4321 +#if ( !defined( TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN ) ) + #define TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN 4321 #endif -#if( !defined( TARGET_RT_BYTE_ORDER ) ) - #if( TARGET_RT_LITTLE_ENDIAN ) - #define TARGET_RT_BYTE_ORDER TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN - #else - #define TARGET_RT_BYTE_ORDER TARGET_RT_BYTE_ORDER_BIG_ENDIAN - #endif +#if ( !defined( TARGET_RT_BYTE_ORDER ) ) + #if ( TARGET_RT_LITTLE_ENDIAN ) + #define TARGET_RT_BYTE_ORDER TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN + #else + #define TARGET_RT_BYTE_ORDER TARGET_RT_BYTE_ORDER_BIG_ENDIAN + #endif #endif #if 0 @@ -391,15 +410,15 @@ // Constants //=========================================================================================================================== -#if( !TARGET_OS_MAC ) - #define CR '\r' +#if ( !TARGET_OS_MAC ) + #define CR '\r' #endif -#define LF '\n' -#define CRSTR "\r" -#define LFSTR "\n" -#define CRLF "\r\n" -#define CRCR "\r\r" +#define LF '\n' +#define CRSTR "\r" +#define LFSTR "\n" +#define CRLF "\r\n" +#define CRCR "\r\r" #if 0 #pragma mark == Compatibility == @@ -411,135 +430,135 @@ // Macros to allow the same code to work on Windows and other sockets API-compatible platforms. -#if( TARGET_OS_WIN32 ) - #define close_compat( X ) closesocket( X ) - #define errno_compat() (int) GetLastError() - #define set_errno_compat( X ) SetLastError( X ) - #define EWOULDBLOCK_compat WSAEWOULDBLOCK - #define ETIMEDOUT_compat WSAETIMEDOUT - #define ENOTCONN_compat WSAENOTCONN - #define IsValidSocket( X ) ( ( X ) != INVALID_SOCKET ) - #define kInvalidSocketRef INVALID_SOCKET - #if( TARGET_LANGUAGE_C_LIKE ) - typedef SOCKET SocketRef; - #endif +#if ( TARGET_OS_WIN32 ) + #define close_compat( X ) closesocket( X ) + #define errno_compat() (int) GetLastError() + #define set_errno_compat( X ) SetLastError( X ) + #define EWOULDBLOCK_compat WSAEWOULDBLOCK + #define ETIMEDOUT_compat WSAETIMEDOUT + #define ENOTCONN_compat WSAENOTCONN + #define IsValidSocket( X ) ( ( X ) != INVALID_SOCKET ) + #define kInvalidSocketRef INVALID_SOCKET + #if ( TARGET_LANGUAGE_C_LIKE ) +typedef SOCKET SocketRef; + #endif #else - #define close_compat( X ) close( X ) - #define errno_compat() errno - #define set_errno_compat( X ) do { errno = ( X ); } while( 0 ) - #define EWOULDBLOCK_compat EWOULDBLOCK - #define ETIMEDOUT_compat ETIMEDOUT - #define ENOTCONN_compat ENOTCONN - #define IsValidSocket( X ) ( ( X ) >= 0 ) - #define kInvalidSocketRef -1 - #if( TARGET_LANGUAGE_C_LIKE ) - typedef int SocketRef; - #endif + #define close_compat( X ) close( X ) + #define errno_compat() errno + #define set_errno_compat( X ) do { errno = ( X ); } while( 0 ) + #define EWOULDBLOCK_compat EWOULDBLOCK + #define ETIMEDOUT_compat ETIMEDOUT + #define ENOTCONN_compat ENOTCONN + #define IsValidSocket( X ) ( ( X ) >= 0 ) + #define kInvalidSocketRef -1 + #if ( TARGET_LANGUAGE_C_LIKE ) +typedef int SocketRef; + #endif #endif // socklen_t is not defined on the following platforms so emulate it if not defined: -// +// // - Pre-Panther Mac OS X. Panther defines SO_NOADDRERR so trigger off that. // - Windows SDK prior to 2003. 2003+ SDK's define EAI_AGAIN so trigger off that. // - VxWorks -#if( TARGET_LANGUAGE_C_LIKE ) - #if( ( TARGET_OS_MAC && !defined( SO_NOADDRERR ) ) || ( TARGET_OS_WIN32 && !defined( EAI_AGAIN ) ) || TARGET_OS_VXWORKS ) - typedef int socklen_t; - #endif +#if ( TARGET_LANGUAGE_C_LIKE ) + #if ( ( TARGET_OS_MAC && !defined( SO_NOADDRERR ) ) || ( TARGET_OS_WIN32 && !defined( EAI_AGAIN ) ) || TARGET_OS_VXWORKS ) +typedef int socklen_t; + #endif #endif // ssize_t is not defined on the following platforms so emulate it if not defined: -// +// // - Mac OS X when not building with BSD headers // - Windows -#if( TARGET_LANGUAGE_C_LIKE ) - #if( !defined(_SSIZE_T) && ( TARGET_OS_WIN32 || !defined( _BSD_SSIZE_T_DEFINED_ ) ) && !TARGET_OS_LINUX && !TARGET_OS_VXWORKS && !TARGET_OS_MAC) - typedef int ssize_t; - #endif +#if ( TARGET_LANGUAGE_C_LIKE ) + #if ( !defined(_SSIZE_T) && ( TARGET_OS_WIN32 || !defined( _BSD_SSIZE_T_DEFINED_ ) ) && !TARGET_OS_FREEBSD && !TARGET_OS_LINUX && !TARGET_OS_VXWORKS && !TARGET_OS_MAC) +typedef int ssize_t; + #endif #endif // sockaddr_storage is not supported on non-IPv6 machines so alias it to an IPv4-compatible structure. -#if( TARGET_LANGUAGE_C_LIKE ) - #if( !defined( AF_INET6 ) ) - #define sockaddr_storage sockaddr_in - #define ss_family sin_family - #endif +#if ( TARGET_LANGUAGE_C_LIKE ) + #if ( !defined( AF_INET6 ) ) + #define sockaddr_storage sockaddr_in + #define ss_family sin_family + #endif #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined SOCKADDR_IS_IP_LOOPBACK - - @abstract Determines if a sockaddr is an IPv4 or IPv6 loopback address (if IPv6 is supported). -*/ - -#if( defined( AF_INET6 ) ) - #define SOCKADDR_IS_IP_LOOPBACK( SA ) \ - ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \ - ? ( ( (const struct sockaddr_in *)( SA ) )->sin_addr.s_addr == htonl( INADDR_LOOPBACK ) ) \ - : ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET6 ) \ - ? IN6_IS_ADDR_LOOPBACK( &( (const struct sockaddr_in6 *)( SA ) )->sin6_addr ) \ - : 0 + + @abstract Determines if a sockaddr is an IPv4 or IPv6 loopback address (if IPv6 is supported). + */ + +#if ( defined( AF_INET6 ) ) + #define SOCKADDR_IS_IP_LOOPBACK( SA ) \ + ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \ + ? ( ( (const struct sockaddr_in *)( SA ) )->sin_addr.s_addr == htonl( INADDR_LOOPBACK ) ) \ + : ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET6 ) \ + ? IN6_IS_ADDR_LOOPBACK( &( (const struct sockaddr_in6 *)( SA ) )->sin6_addr ) \ + : 0 #else - #define SOCKADDR_IS_IP_LOOPBACK( SA ) \ - ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \ - ? ( ( (const struct sockaddr_in *)( SA ) )->sin_addr.s_addr == htonl( INADDR_LOOPBACK ) ) \ - : 0 + #define SOCKADDR_IS_IP_LOOPBACK( SA ) \ + ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \ + ? ( ( (const struct sockaddr_in *)( SA ) )->sin_addr.s_addr == htonl( INADDR_LOOPBACK ) ) \ + : 0 #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined SOCKADDR_IS_IP_LINK_LOCAL - - @abstract Determines if a sockaddr is an IPv4 or IPv6 link-local address (if IPv6 is supported). -*/ - -#if( defined( AF_INET6 ) ) - #define SOCKADDR_IS_IP_LINK_LOCAL( SA ) \ - ( ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \ - ? ( ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 0 ] == 169 ) && \ - ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 1 ] == 254 ) ) \ - : IN6_IS_ADDR_LOOPBACK( &( (const struct sockaddr_in6 *)( SA ) )->sin6_addr ) ) + + @abstract Determines if a sockaddr is an IPv4 or IPv6 link-local address (if IPv6 is supported). + */ + +#if ( defined( AF_INET6 ) ) + #define SOCKADDR_IS_IP_LINK_LOCAL( SA ) \ + ( ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \ + ? ( ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 0 ] == 169 ) && \ + ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 1 ] == 254 ) ) \ + : IN6_IS_ADDR_LOOPBACK( &( (const struct sockaddr_in6 *)( SA ) )->sin6_addr ) ) #else - #define SOCKADDR_IS_IP_LINK_LOCAL( SA ) \ - ( ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \ - ? ( ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 0 ] == 169 ) && \ - ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 1 ] == 254 ) ) \ - : 0 ) + #define SOCKADDR_IS_IP_LINK_LOCAL( SA ) \ + ( ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \ + ? ( ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 0 ] == 169 ) && \ + ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 1 ] == 254 ) ) \ + : 0 ) #endif -// _beginthreadex and _endthreadex are not supported on Windows CE 2.1 or later (the C runtime issues with leaking -// resources have apparently been resolved and they seem to have just ripped out support for the API) so map it to +// _beginthreadex and _endthreadex are not supported on Windows CE 2.1 or later (the C runtime issues with leaking +// resources have apparently been resolved and they seem to have just ripped out support for the API) so map it to // CreateThread on Windows CE. -#if( TARGET_OS_WINDOWS_CE ) - #define _beginthreadex_compat( SECURITY_PTR, STACK_SIZE, START_ADDRESS, ARG_LIST, FLAGS, THREAD_ID_PTR ) \ - (uintptr_t) CreateThread( SECURITY_PTR, STACK_SIZE, (LPTHREAD_START_ROUTINE) START_ADDRESS, ARG_LIST, FLAGS, \ - (LPDWORD) THREAD_ID_PTR ) - - #define _endthreadex_compat( RESULT ) ExitThread( (DWORD) RESULT ) -#elif( TARGET_OS_WIN32 ) - #define _beginthreadex_compat _beginthreadex - #define _endthreadex_compat _endthreadex +#if ( TARGET_OS_WINDOWS_CE ) + #define _beginthreadex_compat( SECURITY_PTR, STACK_SIZE, START_ADDRESS, ARG_LIST, FLAGS, THREAD_ID_PTR ) \ + (uintptr_t) CreateThread( SECURITY_PTR, STACK_SIZE, (LPTHREAD_START_ROUTINE) START_ADDRESS, ARG_LIST, FLAGS, \ + (LPDWORD) THREAD_ID_PTR ) + + #define _endthreadex_compat( RESULT ) ExitThread( (DWORD) RESULT ) +#elif ( TARGET_OS_WIN32 ) + #define _beginthreadex_compat _beginthreadex + #define _endthreadex_compat _endthreadex #endif // The C99 "inline" keyword is not supported by Microsoft compilers, but they do support __inline so map it when needed. -#if( defined( _MSC_VER ) ) - #define inline_compat __inline +#if ( defined( _MSC_VER ) ) + #define inline_compat __inline #else - #define inline_compat inline + #define inline_compat inline #endif -// Calling conventions +// Calling conventions -#if( !defined( CALLBACK_COMPAT ) ) - #if( TARGET_OS_WIN32 || TARGET_OS_WINDOWS_CE ) - #define CALLBACK_COMPAT CALLBACK - #else - #define CALLBACK_COMPAT - #endif +#if ( !defined( CALLBACK_COMPAT ) ) + #if ( TARGET_OS_WIN32 || TARGET_OS_WINDOWS_CE ) + #define CALLBACK_COMPAT CALLBACK + #else + #define CALLBACK_COMPAT + #endif #endif #if 0 @@ -549,270 +568,270 @@ //--------------------------------------------------------------------------------------------------------------------------- /*! @defined kSizeCString - @abstract A meta-value to pass to supported routines to indicate the size should be calculated with strlen. -*/ + @abstract A meta-value to pass to supported routines to indicate the size should be calculated with strlen. + */ -#define kSizeCString ( (size_t) -1 ) +#define kSizeCString ( (size_t) -1 ) //--------------------------------------------------------------------------------------------------------------------------- /*! @defined sizeof_array - - @abstract Determines the number of elements in an array. -*/ -#define sizeof_array( X ) ( sizeof( X ) / sizeof( X[ 0 ] ) ) + @abstract Determines the number of elements in an array. + */ + +#define sizeof_array( X ) ( sizeof( X ) / sizeof( X[ 0 ] ) ) //--------------------------------------------------------------------------------------------------------------------------- /*! @defined sizeof_element - - @abstract Determines the size of an array element. -*/ -#define sizeof_element( X ) sizeof( X[ 0 ] ) + @abstract Determines the size of an array element. + */ + +#define sizeof_element( X ) sizeof( X[ 0 ] ) //--------------------------------------------------------------------------------------------------------------------------- /*! @defined sizeof_string - - @abstract Determines the size of a constant C string, excluding the null terminator. -*/ -#define sizeof_string( X ) ( sizeof( ( X ) ) - 1 ) + @abstract Determines the size of a constant C string, excluding the null terminator. + */ + +#define sizeof_string( X ) ( sizeof( ( X ) ) - 1 ) //--------------------------------------------------------------------------------------------------------------------------- /*! @defined sizeof_field - - @abstract Determines the size of a field of a type. -*/ -#define sizeof_field( TYPE, FIELD ) sizeof( ( ( (TYPE *) 0 )->FIELD ) ) + @abstract Determines the size of a field of a type. + */ + +#define sizeof_field( TYPE, FIELD ) sizeof( ( ( (TYPE *) 0 )->FIELD ) ) //--------------------------------------------------------------------------------------------------------------------------- /*! @function RoundUp - @abstract Rounds X up to a multiple of Y. -*/ + @abstract Rounds X up to a multiple of Y. + */ -#define RoundUp( X, Y ) ( ( X ) + ( ( Y ) - ( ( X ) % ( Y ) ) ) ) +#define RoundUp( X, Y ) ( ( X ) + ( ( Y ) -( ( X ) % ( Y ) ) ) ) //--------------------------------------------------------------------------------------------------------------------------- /*! @function IsAligned - @abstract Returns non-zero if X is aligned to a Y byte boundary and 0 if not. Y must be a power of 2. -*/ + @abstract Returns non-zero if X is aligned to a Y byte boundary and 0 if not. Y must be a power of 2. + */ -#define IsAligned( X, Y ) ( ( ( X ) & ( ( Y ) - 1 ) ) == 0 ) +#define IsAligned( X, Y ) ( ( ( X ) &( ( Y ) -1 ) ) == 0 ) //--------------------------------------------------------------------------------------------------------------------------- /*! @function IsFieldAligned - @abstract Returns non-zero if FIELD of type TYPE is aligned to a Y byte boundary and 0 if not. Y must be a power of 2. -*/ + @abstract Returns non-zero if FIELD of type TYPE is aligned to a Y byte boundary and 0 if not. Y must be a power of 2. + */ -#define IsFieldAligned( X, TYPE, FIELD, Y ) IsAligned( ( (uintptr_t)( X ) ) + offsetof( TYPE, FIELD ), ( Y ) ) +#define IsFieldAligned( X, TYPE, FIELD, Y ) IsAligned( ( (uintptr_t)( X ) ) + offsetof( TYPE, FIELD ), ( Y ) ) //--------------------------------------------------------------------------------------------------------------------------- /*! @function AlignDown - @abstract Aligns X down to a Y byte boundary. Y must be a power of 2. -*/ + @abstract Aligns X down to a Y byte boundary. Y must be a power of 2. + */ -#define AlignDown( X, Y ) ( ( X ) & ~( ( Y ) - 1 ) ) +#define AlignDown( X, Y ) ( ( X ) &~( ( Y ) -1 ) ) //--------------------------------------------------------------------------------------------------------------------------- /*! @function AlignUp - @abstract Aligns X up to a Y byte boundary. Y must be a power of 2. -*/ + @abstract Aligns X up to a Y byte boundary. Y must be a power of 2. + */ -#define AlignUp( X, Y ) ( ( ( X ) + ( ( Y ) - 1 ) ) & ~( ( Y ) - 1 ) ) +#define AlignUp( X, Y ) ( ( ( X ) + ( ( Y ) -1 ) ) & ~( ( Y ) -1 ) ) //--------------------------------------------------------------------------------------------------------------------------- /*! @function Min - @abstract Returns the lesser of X and Y. -*/ + @abstract Returns the lesser of X and Y. + */ -#if( !defined( Min ) ) - #define Min( X, Y ) ( ( ( X ) < ( Y ) ) ? ( X ) : ( Y ) ) +#if ( !defined( Min ) ) + #define Min( X, Y ) ( ( ( X ) < ( Y ) ) ? ( X ) : ( Y ) ) #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @function Max - @abstract Returns the greater of X and Y. -*/ + @abstract Returns the greater of X and Y. + */ -#if( !defined( Max ) ) - #define Max( X, Y ) ( ( ( X ) > ( Y ) ) ? ( X ) : ( Y ) ) +#if ( !defined( Max ) ) + #define Max( X, Y ) ( ( ( X ) > ( Y ) ) ? ( X ) : ( Y ) ) #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @function InsertBits - @abstract Inserts BITS (both 0 and 1 bits) into X, controlled by MASK and SHIFT, and returns the result. - - @discussion - - MASK is the bitmask of the bits in the final position. - SHIFT is the number of bits to shift left for 1 to reach the first bit position of MASK. - - For example, if you wanted to insert 0x3 into the leftmost 4 bits of a 32-bit value: - - InsertBits( 0, 0x3, 0xF0000000U, 28 ) == 0x30000000 -*/ + @abstract Inserts BITS (both 0 and 1 bits) into X, controlled by MASK and SHIFT, and returns the result. + + @discussion -#define InsertBits( X, BITS, MASK, SHIFT ) ( ( ( X ) & ~( MASK ) ) | ( ( ( BITS ) << ( SHIFT ) ) & ( MASK ) ) ) + MASK is the bitmask of the bits in the final position. + SHIFT is the number of bits to shift left for 1 to reach the first bit position of MASK. + + For example, if you wanted to insert 0x3 into the leftmost 4 bits of a 32-bit value: + + InsertBits( 0, 0x3, 0xF0000000U, 28 ) == 0x30000000 + */ + +#define InsertBits( X, BITS, MASK, SHIFT ) ( ( ( X ) &~( MASK ) ) | ( ( ( BITS ) << ( SHIFT ) ) & ( MASK ) ) ) //--------------------------------------------------------------------------------------------------------------------------- /*! @function ExtractBits - @abstract Extracts bits from X, controlled by MASK and SHIFT, and returns the result. - - @discussion - - MASK is the bitmask of the bits in the final position. - SHIFT is the number of bits to shift right to right justify MASK. - - For example, if you had a 32-bit value (e.g. 0x30000000) wanted the left-most 4 bits (e.g. 3 in this example): - - ExtractBits( 0x30000000U, 0xF0000000U, 28 ) == 0x3 -*/ + @abstract Extracts bits from X, controlled by MASK and SHIFT, and returns the result. + + @discussion -#define ExtractBits( X, MASK, SHIFT ) ( ( ( X ) >> ( SHIFT ) ) & ( ( MASK ) >> ( SHIFT ) ) ) + MASK is the bitmask of the bits in the final position. + SHIFT is the number of bits to shift right to right justify MASK. + + For example, if you had a 32-bit value (e.g. 0x30000000) wanted the left-most 4 bits (e.g. 3 in this example): + + ExtractBits( 0x30000000U, 0xF0000000U, 28 ) == 0x3 + */ + +#define ExtractBits( X, MASK, SHIFT ) ( ( ( X ) >> ( SHIFT ) ) & ( ( MASK ) >> ( SHIFT ) ) ) //--------------------------------------------------------------------------------------------------------------------------- /*! @function Stringify - @abstract Stringify's an expression. - - @discussion - - Stringify macros to process raw text passed via -D options to C string constants. The double-wrapping is necessary - because the C preprocessor doesn't perform its normal argument expansion pre-scan with stringified macros so the - -D macro needs to be expanded once via the wrapper macro then stringified so the raw text is stringified. Otherwise, - the replacement value would be used instead of the symbolic name (only for preprocessor symbols like #defines). - - For example: - - #define kMyConstant 1 - - printf( "%s", Stringify( kMyConstant ) ); // Prints "kMyConstant" - printf( "%s", StringifyExpansion( kMyConstant ) ); // Prints "1" - - Non-preprocessor symbols do not have this issue. For example: - - enum - { - kMyConstant = 1 - }; - - printf( "%s", Stringify( kMyConstant ) ); // Prints "kMyConstant" - printf( "%s", StringifyExpansion( kMyConstant ) ); // Prints "kMyConstant" - - See for more info on C preprocessor pre-scanning. -*/ - -#define Stringify( X ) # X -#define StringifyExpansion( X ) Stringify( X ) + @abstract Stringify's an expression. + + @discussion + + Stringify macros to process raw text passed via -D options to C string constants. The double-wrapping is necessary + because the C preprocessor doesn't perform its normal argument expansion pre-scan with stringified macros so the + -D macro needs to be expanded once via the wrapper macro then stringified so the raw text is stringified. Otherwise, + the replacement value would be used instead of the symbolic name (only for preprocessor symbols like #defines). + + For example: + + #define kMyConstant 1 + + printf( "%s", Stringify( kMyConstant ) ); // Prints "kMyConstant" + printf( "%s", StringifyExpansion( kMyConstant ) ); // Prints "1" + + Non-preprocessor symbols do not have this issue. For example: + + enum + { + kMyConstant = 1 + }; + + printf( "%s", Stringify( kMyConstant ) ); // Prints "kMyConstant" + printf( "%s", StringifyExpansion( kMyConstant ) ); // Prints "kMyConstant" + + See for more info on C preprocessor pre-scanning. + */ + +#define Stringify( X ) # X +#define StringifyExpansion( X ) Stringify( X ) #if 0 #pragma mark == Types == #endif -#if( TARGET_LANGUAGE_C_LIKE ) +#if ( TARGET_LANGUAGE_C_LIKE ) //=========================================================================================================================== // Standard Types //=========================================================================================================================== -#if( !defined( INT8_MIN ) ) - - #define INT8_MIN SCHAR_MIN - - #if( defined( _MSC_VER ) ) - - // C99 stdint.h not supported in VC++/VS.NET yet. - - typedef INT8 int8_t; - typedef UINT8 uint8_t; - typedef INT16 int16_t; - typedef UINT16 uint16_t; - typedef INT32 int32_t; - typedef UINT32 uint32_t; - typedef __int64 int64_t; - typedef unsigned __int64 uint64_t; - - #elif( TARGET_OS_VXWORKS && ( TORNADO_VERSION < 220 ) ) - typedef long long int64_t; - typedef unsigned long long uint64_t; - #endif - - typedef int8_t int_least8_t; - typedef int16_t int_least16_t; - typedef int32_t int_least32_t; - typedef int64_t int_least64_t; - - typedef uint8_t uint_least8_t; - typedef uint16_t uint_least16_t; - typedef uint32_t uint_least32_t; - typedef uint64_t uint_least64_t; - - typedef int8_t int_fast8_t; - typedef int16_t int_fast16_t; - typedef int32_t int_fast32_t; - typedef int64_t int_fast64_t; - - typedef uint8_t uint_fast8_t; - typedef uint16_t uint_fast16_t; - typedef uint32_t uint_fast32_t; - typedef uint64_t uint_fast64_t; - - #if( !defined( _MSC_VER ) || TARGET_OS_WINDOWS_CE ) - typedef long int intptr_t; - typedef unsigned long int uintptr_t; - #endif +#if ( !defined( INT8_MIN ) ) + + #define INT8_MIN SCHAR_MIN + + #if ( defined( _MSC_VER ) ) + +// C99 stdint.h not supported in VC++/VS.NET yet. + +typedef INT8 int8_t; +typedef UINT8 uint8_t; +typedef INT16 int16_t; +typedef UINT16 uint16_t; +typedef INT32 int32_t; +typedef UINT32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; + + #elif ( TARGET_OS_VXWORKS && ( TORNADO_VERSION < 220 ) ) +typedef long long int64_t; +typedef unsigned long long uint64_t; + #endif + +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; + +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; + +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + + #if ( !defined( _MSC_VER ) || TARGET_OS_WINDOWS_CE ) +typedef long int intptr_t; +typedef unsigned long int uintptr_t; + #endif #endif // Macros for minimum-width integer constants -#if( !defined( INT8_C ) ) - #define INT8_C( value ) value +#if ( !defined( INT8_C ) ) + #define INT8_C( value ) value #endif -#if( !defined( INT16_C ) ) - #define INT16_C( value ) value +#if ( !defined( INT16_C ) ) + #define INT16_C( value ) value #endif -#if( !defined( INT32_C ) ) - #define INT32_C( value ) value ## L +#if ( !defined( INT32_C ) ) + #define INT32_C( value ) value ## L #endif -#if( !defined( INT64_C ) ) - #if( defined( _MSC_VER ) ) - #define INT64_C( value ) value ## i64 - #else - #define INT64_C( value ) value ## LL - #endif +#if ( !defined( INT64_C ) ) + #if ( defined( _MSC_VER ) ) + #define INT64_C( value ) value ## i64 + #else + #define INT64_C( value ) value ## LL + #endif #endif -#if( !defined( UINT8_C ) ) - #define UINT8_C( value ) value ## U +#if ( !defined( UINT8_C ) ) + #define UINT8_C( value ) value ## U #endif -#if( !defined( UINT16_C ) ) - #define UINT16_C( value ) value ## U +#if ( !defined( UINT16_C ) ) + #define UINT16_C( value ) value ## U #endif -#if( !defined( UINT32_C ) ) - #define UINT32_C( value ) value ## UL +#if ( !defined( UINT32_C ) ) + #define UINT32_C( value ) value ## UL #endif -#if( !defined( UINT64_C ) ) - #if( defined( _MSC_VER ) ) - #define UINT64_C( value ) value ## UI64 - #else - #define UINT64_C( value ) value ## ULL - #endif +#if ( !defined( UINT64_C ) ) + #if ( defined( _MSC_VER ) ) + #define UINT64_C( value ) value ## UI64 + #else + #define UINT64_C( value ) value ## ULL + #endif #endif #if 0 @@ -826,102 +845,102 @@ // C++ defines bool, true, and false. Metrowerks allows this to be controlled by the "bool" option though. // C99 defines __bool_true_false_are_defined when bool, true, and false are defined. // MacTypes.h defines true and false (Mac builds only). -// -// Note: The Metrowerks has to be in its own block because Microsoft Visual Studio .NET does not completely +// +// Note: The Metrowerks has to be in its own block because Microsoft Visual Studio .NET does not completely // short-circuit and gets confused by the option( bool ) portion of the conditional. -#if( defined( __MWERKS__ ) ) - - // Note: The following test is done on separate lines because CodeWarrior doesn't like it all on one line. - - #if( !__bool_true_false_are_defined && ( !defined( __cplusplus ) || !__option( bool ) ) ) - #define COMMON_SERVICES_NEEDS_BOOL 1 - #else - #define COMMON_SERVICES_NEEDS_BOOL 0 - #endif - - // Workaround when building with CodeWarrior, but using the Apple stdbool.h header, which uses _Bool. - - #if( __bool_true_false_are_defined && !defined( __cplusplus ) && !__option( c9x ) ) - #define _Bool int - #endif - - // Workaround when building with CodeWarrior for C++ with bool disabled and using the Apple stdbool.h header, - // which defines true and false to map to C++ true and false (which are not enabled). Serenity Now! - - #if( __bool_true_false_are_defined && defined( __cplusplus ) && !__option( bool ) ) - #define true 1 - #define false 0 - #endif +#if ( defined( __MWERKS__ ) ) + +// Note: The following test is done on separate lines because CodeWarrior doesn't like it all on one line. + + #if ( !__bool_true_false_are_defined && ( !defined( __cplusplus ) || !__option( bool ) ) ) + #define COMMON_SERVICES_NEEDS_BOOL 1 + #else + #define COMMON_SERVICES_NEEDS_BOOL 0 + #endif + +// Workaround when building with CodeWarrior, but using the Apple stdbool.h header, which uses _Bool. + + #if ( __bool_true_false_are_defined && !defined( __cplusplus ) && !__option( c9x ) ) + #define _Bool int + #endif + +// Workaround when building with CodeWarrior for C++ with bool disabled and using the Apple stdbool.h header, +// which defines true and false to map to C++ true and false (which are not enabled). Serenity Now! + + #if ( __bool_true_false_are_defined && defined( __cplusplus ) && !__option( bool ) ) + #define true 1 + #define false 0 + #endif #else - #define COMMON_SERVICES_NEEDS_BOOL ( !defined( __cplusplus ) && !__bool_true_false_are_defined ) + #define COMMON_SERVICES_NEEDS_BOOL ( !defined( __cplusplus ) && !__bool_true_false_are_defined ) #endif -#if( COMMON_SERVICES_NEEDS_BOOL ) - - typedef int bool; - - #define bool bool - - #if( !defined( __MACTYPES__ ) && !defined( true ) && !defined( false ) ) - #define true 1 - #define false 0 - #endif - - #define __bool_true_false_are_defined 1 +#if ( COMMON_SERVICES_NEEDS_BOOL ) + +typedef int bool; + + #define bool bool + + #if ( !defined( __MACTYPES__ ) && !defined( true ) && !defined( false ) ) + #define true 1 + #define false 0 + #endif + + #define __bool_true_false_are_defined 1 #endif // IOKit IOTypes.h typedef's bool if TYPE_BOOL is not defined so define it here to prevent redefinition by IOTypes.h. -#if( TARGET_API_MAC_OSX_KERNEL ) - #define TYPE_BOOL 1 +#if ( TARGET_API_MAC_OSX_KERNEL ) + #define TYPE_BOOL 1 #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @typedef CStr255 - - @abstract 255 character null-terminated (C-style) string. -*/ -#if( TARGET_LANGUAGE_C_LIKE ) - typedef char CStr255[ 256 ]; + @abstract 255 character null-terminated (C-style) string. + */ + +#if ( TARGET_LANGUAGE_C_LIKE ) +typedef char CStr255[ 256 ]; #endif -#endif // TARGET_LANGUAGE_C_LIKE +#endif // TARGET_LANGUAGE_C_LIKE //--------------------------------------------------------------------------------------------------------------------------- /*! @defined TYPE_LONGLONG_NATIVE - @abstract Defines whether long long (or its equivalent) is natively supported or requires special libraries. -*/ + @abstract Defines whether long long (or its equivalent) is natively supported or requires special libraries. + */ -#if( !defined( TYPE_LONGLONG_NATIVE ) ) - #if( !TARGET_OS_VXWORKS ) - #define TYPE_LONGLONG_NATIVE 1 - #else - #define TYPE_LONGLONG_NATIVE 0 - #endif +#if ( !defined( TYPE_LONGLONG_NATIVE ) ) + #if ( !TARGET_OS_VXWORKS ) + #define TYPE_LONGLONG_NATIVE 1 + #else + #define TYPE_LONGLONG_NATIVE 0 + #endif #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined long_long_compat - @abstract Compatibility type to map to the closest thing to long long and unsigned long long. - - @discussion - - Neither long long nor unsigned long long are supported by Microsoft compilers, but they do support proprietary - "__int64" and "unsigned __int64" equivalents so map to those types if the real long long is not supported. -*/ + @abstract Compatibility type to map to the closest thing to long long and unsigned long long. -#if( TARGET_LANGUAGE_C_LIKE ) - #if( TARGET_OS_WIN32 ) - typedef __int64 long_long_compat; - typedef unsigned __int64 unsigned_long_long_compat; - #else - typedef signed long long long_long_compat; - typedef unsigned long long unsigned_long_long_compat; - #endif + @discussion + + Neither long long nor unsigned long long are supported by Microsoft compilers, but they do support proprietary + "__int64" and "unsigned __int64" equivalents so map to those types if the real long long is not supported. + */ + +#if ( TARGET_LANGUAGE_C_LIKE ) + #if ( TARGET_OS_WIN32 ) +typedef __int64 long_long_compat; +typedef unsigned __int64 unsigned_long_long_compat; + #else +typedef signed long long long_long_compat; +typedef unsigned long long unsigned_long_long_compat; + #endif #endif #if 0 @@ -931,155 +950,155 @@ //--------------------------------------------------------------------------------------------------------------------------- /*! @enum OSStatus - @abstract Status Code - - @constant kNoErr 0 No error occurred. - @constant kInProgressErr 1 Operation in progress. - @constant kUnknownErr -6700 Unknown error occurred. - @constant kOptionErr -6701 Option was not acceptable. - @constant kSelectorErr -6702 Selector passed in is invalid or unknown. - @constant kExecutionStateErr -6703 Call made in the wrong execution state (e.g. called at interrupt time). - @constant kPathErr -6704 Path is invalid, too long, or otherwise not usable. - @constant kParamErr -6705 Parameter is incorrect, missing, or not appropriate. - @constant kParamCountErr -6706 Incorrect or unsupported number of parameters. - @constant kCommandErr -6707 Command invalid or not supported. - @constant kIDErr -6708 Unknown, invalid, or inappropriate identifier. - @constant kStateErr -6709 Not in appropriate state to perform operation. - @constant kRangeErr -6710 Index is out of range or not valid. - @constant kRequestErr -6711 Request was improperly formed or not appropriate. - @constant kResponseErr -6712 Response was incorrect or out of sequence. - @constant kChecksumErr -6713 Checksum does not match the actual data. - @constant kNotHandledErr -6714 Operation was not handled (or not handled completely). - @constant kVersionErr -6715 Version is not incorrect or not compatibile. - @constant kSignatureErr -6716 Signature did not match what was expected. - @constant kFormatErr -6717 Unknown, invalid, or inappropriate file/data format. - @constant kNotInitializedErr -6718 Action request before needed services were initialized. - @constant kAlreadyInitializedErr -6719 Attempt made to initialize when already initialized. - @constant kNotInUseErr -6720 Object not in use (e.g. cannot abort if not already in use). - @constant kInUseErr -6721 Object is in use (e.g. cannot reuse active param blocks). - @constant kTimeoutErr -6722 Timeout occurred. - @constant kCanceledErr -6723 Operation canceled (successful cancel). - @constant kAlreadyCanceledErr -6724 Operation has already been canceled. - @constant kCannotCancelErr -6725 Operation could not be canceled (maybe already done or invalid). - @constant kDeletedErr -6726 Object has already been deleted. - @constant kNotFoundErr -6727 Something was not found. - @constant kNoMemoryErr -6728 Not enough memory was available to perform the operation. - @constant kNoResourcesErr -6729 Resources unavailable to perform the operation. - @constant kDuplicateErr -6730 Duplicate found or something is a duplicate. - @constant kImmutableErr -6731 Entity is not changeable. - @constant kUnsupportedDataErr -6732 Data is unknown or not supported. - @constant kIntegrityErr -6733 Data is corrupt. - @constant kIncompatibleErr -6734 Data is not compatible or it is in an incompatible format. - @constant kUnsupportedErr -6735 Feature or option is not supported. - @constant kUnexpectedErr -6736 Error occurred that was not expected. - @constant kValueErr -6737 Value is not appropriate. - @constant kNotReadableErr -6738 Could not read or reading is not allowed. - @constant kNotWritableErr -6739 Could not write or writing is not allowed. - @constant kBadReferenceErr -6740 An invalid or inappropriate reference was specified. - @constant kFlagErr -6741 An invalid, inappropriate, or unsupported flag was specified. - @constant kMalformedErr -6742 Something was not formed correctly. - @constant kSizeErr -6743 Size was too big, too small, or not appropriate. - @constant kNameErr -6744 Name was not correct, allowed, or appropriate. - @constant kNotReadyErr -6745 Device or service is not ready. - @constant kReadErr -6746 Could not read. - @constant kWriteErr -6747 Could not write. - @constant kMismatchErr -6748 Something does not match. - @constant kDateErr -6749 Date is invalid or out-of-range. - @constant kUnderrunErr -6750 Less data than expected. - @constant kOverrunErr -6751 More data than expected. - @constant kEndingErr -6752 Connection, session, or something is ending. - @constant kConnectionErr -6753 Connection failed or could not be established. - @constant kAuthenticationErr -6754 Authentication failed or is not supported. - @constant kOpenErr -6755 Could not open file, pipe, device, etc. - @constant kTypeErr -6756 Incorrect or incompatible type (e.g. file, data, etc.). - @constant kSkipErr -6757 Items should be or was skipped. - @constant kNoAckErr -6758 No acknowledge. - @constant kCollisionErr -6759 Collision occurred (e.g. two on bus at same time). - @constant kBackoffErr -6760 Backoff in progress and operation intentionally failed. - @constant kNoAddressAckErr -6761 No acknowledge of address. - @constant kBusyErr -6762 Cannot perform because something is busy. - @constant kNoSpaceErr -6763 Not enough space to perform operation. -*/ - -#if( TARGET_LANGUAGE_C_LIKE ) - #if( !TARGET_OS_MAC && !TARGET_API_MAC_OSX_KERNEL ) - typedef int32_t OSStatus; - #endif -#endif - -#define kNoErr 0 -#define kInProgressErr 1 + @abstract Status Code + + @constant kNoErr 0 No error occurred. + @constant kInProgressErr 1 Operation in progress. + @constant kUnknownErr -6700 Unknown error occurred. + @constant kOptionErr -6701 Option was not acceptable. + @constant kSelectorErr -6702 Selector passed in is invalid or unknown. + @constant kExecutionStateErr -6703 Call made in the wrong execution state (e.g. called at interrupt time). + @constant kPathErr -6704 Path is invalid, too long, or otherwise not usable. + @constant kParamErr -6705 Parameter is incorrect, missing, or not appropriate. + @constant kParamCountErr -6706 Incorrect or unsupported number of parameters. + @constant kCommandErr -6707 Command invalid or not supported. + @constant kIDErr -6708 Unknown, invalid, or inappropriate identifier. + @constant kStateErr -6709 Not in appropriate state to perform operation. + @constant kRangeErr -6710 Index is out of range or not valid. + @constant kRequestErr -6711 Request was improperly formed or not appropriate. + @constant kResponseErr -6712 Response was incorrect or out of sequence. + @constant kChecksumErr -6713 Checksum does not match the actual data. + @constant kNotHandledErr -6714 Operation was not handled (or not handled completely). + @constant kVersionErr -6715 Version is not incorrect or not compatibile. + @constant kSignatureErr -6716 Signature did not match what was expected. + @constant kFormatErr -6717 Unknown, invalid, or inappropriate file/data format. + @constant kNotInitializedErr -6718 Action request before needed services were initialized. + @constant kAlreadyInitializedErr -6719 Attempt made to initialize when already initialized. + @constant kNotInUseErr -6720 Object not in use (e.g. cannot abort if not already in use). + @constant kInUseErr -6721 Object is in use (e.g. cannot reuse active param blocks). + @constant kTimeoutErr -6722 Timeout occurred. + @constant kCanceledErr -6723 Operation canceled (successful cancel). + @constant kAlreadyCanceledErr -6724 Operation has already been canceled. + @constant kCannotCancelErr -6725 Operation could not be canceled (maybe already done or invalid). + @constant kDeletedErr -6726 Object has already been deleted. + @constant kNotFoundErr -6727 Something was not found. + @constant kNoMemoryErr -6728 Not enough memory was available to perform the operation. + @constant kNoResourcesErr -6729 Resources unavailable to perform the operation. + @constant kDuplicateErr -6730 Duplicate found or something is a duplicate. + @constant kImmutableErr -6731 Entity is not changeable. + @constant kUnsupportedDataErr -6732 Data is unknown or not supported. + @constant kIntegrityErr -6733 Data is corrupt. + @constant kIncompatibleErr -6734 Data is not compatible or it is in an incompatible format. + @constant kUnsupportedErr -6735 Feature or option is not supported. + @constant kUnexpectedErr -6736 Error occurred that was not expected. + @constant kValueErr -6737 Value is not appropriate. + @constant kNotReadableErr -6738 Could not read or reading is not allowed. + @constant kNotWritableErr -6739 Could not write or writing is not allowed. + @constant kBadReferenceErr -6740 An invalid or inappropriate reference was specified. + @constant kFlagErr -6741 An invalid, inappropriate, or unsupported flag was specified. + @constant kMalformedErr -6742 Something was not formed correctly. + @constant kSizeErr -6743 Size was too big, too small, or not appropriate. + @constant kNameErr -6744 Name was not correct, allowed, or appropriate. + @constant kNotReadyErr -6745 Device or service is not ready. + @constant kReadErr -6746 Could not read. + @constant kWriteErr -6747 Could not write. + @constant kMismatchErr -6748 Something does not match. + @constant kDateErr -6749 Date is invalid or out-of-range. + @constant kUnderrunErr -6750 Less data than expected. + @constant kOverrunErr -6751 More data than expected. + @constant kEndingErr -6752 Connection, session, or something is ending. + @constant kConnectionErr -6753 Connection failed or could not be established. + @constant kAuthenticationErr -6754 Authentication failed or is not supported. + @constant kOpenErr -6755 Could not open file, pipe, device, etc. + @constant kTypeErr -6756 Incorrect or incompatible type (e.g. file, data, etc.). + @constant kSkipErr -6757 Items should be or was skipped. + @constant kNoAckErr -6758 No acknowledge. + @constant kCollisionErr -6759 Collision occurred (e.g. two on bus at same time). + @constant kBackoffErr -6760 Backoff in progress and operation intentionally failed. + @constant kNoAddressAckErr -6761 No acknowledge of address. + @constant kBusyErr -6762 Cannot perform because something is busy. + @constant kNoSpaceErr -6763 Not enough space to perform operation. + */ + +#if ( TARGET_LANGUAGE_C_LIKE ) + #if ( !TARGET_OS_MAC && !TARGET_API_MAC_OSX_KERNEL ) +typedef int32_t OSStatus; + #endif +#endif + +#define kNoErr 0 +#define kInProgressErr 1 // Generic error codes are in the range -6700 to -6779. -#define kGenericErrorBase -6700 // Starting error code for all generic errors. - -#define kUnknownErr -6700 -#define kOptionErr -6701 -#define kSelectorErr -6702 -#define kExecutionStateErr -6703 -#define kPathErr -6704 -#define kParamErr -6705 -#define kParamCountErr -6706 -#define kCommandErr -6707 -#define kIDErr -6708 -#define kStateErr -6709 -#define kRangeErr -6710 -#define kRequestErr -6711 -#define kResponseErr -6712 -#define kChecksumErr -6713 -#define kNotHandledErr -6714 -#define kVersionErr -6715 -#define kSignatureErr -6716 -#define kFormatErr -6717 -#define kNotInitializedErr -6718 -#define kAlreadyInitializedErr -6719 -#define kNotInUseErr -6720 -#define kInUseErr -6721 -#define kTimeoutErr -6722 -#define kCanceledErr -6723 -#define kAlreadyCanceledErr -6724 -#define kCannotCancelErr -6725 -#define kDeletedErr -6726 -#define kNotFoundErr -6727 -#define kNoMemoryErr -6728 -#define kNoResourcesErr -6729 -#define kDuplicateErr -6730 -#define kImmutableErr -6731 -#define kUnsupportedDataErr -6732 -#define kIntegrityErr -6733 -#define kIncompatibleErr -6734 -#define kUnsupportedErr -6735 -#define kUnexpectedErr -6736 -#define kValueErr -6737 -#define kNotReadableErr -6738 -#define kNotWritableErr -6739 -#define kBadReferenceErr -6740 -#define kFlagErr -6741 -#define kMalformedErr -6742 -#define kSizeErr -6743 -#define kNameErr -6744 -#define kNotReadyErr -6745 -#define kReadErr -6746 -#define kWriteErr -6747 -#define kMismatchErr -6748 -#define kDateErr -6749 -#define kUnderrunErr -6750 -#define kOverrunErr -6751 -#define kEndingErr -6752 -#define kConnectionErr -6753 -#define kAuthenticationErr -6754 -#define kOpenErr -6755 -#define kTypeErr -6756 -#define kSkipErr -6757 -#define kNoAckErr -6758 -#define kCollisionErr -6759 -#define kBackoffErr -6760 -#define kNoAddressAckErr -6761 -#define kBusyErr -6762 -#define kNoSpaceErr -6763 - -#define kGenericErrorEnd -6779 // Last generic error code (inclusive) +#define kGenericErrorBase -6700 // Starting error code for all generic errors. + +#define kUnknownErr -6700 +#define kOptionErr -6701 +#define kSelectorErr -6702 +#define kExecutionStateErr -6703 +#define kPathErr -6704 +#define kParamErr -6705 +#define kParamCountErr -6706 +#define kCommandErr -6707 +#define kIDErr -6708 +#define kStateErr -6709 +#define kRangeErr -6710 +#define kRequestErr -6711 +#define kResponseErr -6712 +#define kChecksumErr -6713 +#define kNotHandledErr -6714 +#define kVersionErr -6715 +#define kSignatureErr -6716 +#define kFormatErr -6717 +#define kNotInitializedErr -6718 +#define kAlreadyInitializedErr -6719 +#define kNotInUseErr -6720 +#define kInUseErr -6721 +#define kTimeoutErr -6722 +#define kCanceledErr -6723 +#define kAlreadyCanceledErr -6724 +#define kCannotCancelErr -6725 +#define kDeletedErr -6726 +#define kNotFoundErr -6727 +#define kNoMemoryErr -6728 +#define kNoResourcesErr -6729 +#define kDuplicateErr -6730 +#define kImmutableErr -6731 +#define kUnsupportedDataErr -6732 +#define kIntegrityErr -6733 +#define kIncompatibleErr -6734 +#define kUnsupportedErr -6735 +#define kUnexpectedErr -6736 +#define kValueErr -6737 +#define kNotReadableErr -6738 +#define kNotWritableErr -6739 +#define kBadReferenceErr -6740 +#define kFlagErr -6741 +#define kMalformedErr -6742 +#define kSizeErr -6743 +#define kNameErr -6744 +#define kNotReadyErr -6745 +#define kReadErr -6746 +#define kWriteErr -6747 +#define kMismatchErr -6748 +#define kDateErr -6749 +#define kUnderrunErr -6750 +#define kOverrunErr -6751 +#define kEndingErr -6752 +#define kConnectionErr -6753 +#define kAuthenticationErr -6754 +#define kOpenErr -6755 +#define kTypeErr -6756 +#define kSkipErr -6757 +#define kNoAckErr -6758 +#define kCollisionErr -6759 +#define kBackoffErr -6760 +#define kNoAddressAckErr -6761 +#define kBusyErr -6762 +#define kNoSpaceErr -6763 + +#define kGenericErrorEnd -6779 // Last generic error code (inclusive) #if 0 #pragma mark == Mac Compatibility == @@ -1091,101 +1110,101 @@ //--------------------------------------------------------------------------------------------------------------------------- /*! @enum Duration - - @abstract Type used to specify a duration of time. - - @constant kDurationImmediate Indicates no delay/wait time. - @constant kDurationMicrosecond Microsecond units. - @constant kDurationMillisecond Millisecond units. - @constant kDurationSecond Second units. - @constant kDurationMinute Minute units. - @constant kDurationHour Hour units. - @constant kDurationDay Day units. - @constant kDurationForever Infinite period of time (no timeout). - - @discussion - - Duration values are intended to be multiplied by the specific interval to achieve an actual duration. For example, - to wait for 5 seconds you would use "5 * kDurationSecond". -*/ - -#if( TARGET_LANGUAGE_C_LIKE ) - #if( !TARGET_OS_MAC ) - typedef int32_t Duration; - #endif -#endif - -#define kDurationImmediate 0L -#define kDurationMicrosecond -1L -#define kDurationMillisecond 1L -#define kDurationSecond ( 1000L * kDurationMillisecond ) -#define kDurationMinute ( 60L * kDurationSecond ) -#define kDurationHour ( 60L * kDurationMinute ) -#define kDurationDay ( 24L * kDurationHour ) -#define kDurationForever 0x7FFFFFFFL + + @abstract Type used to specify a duration of time. + + @constant kDurationImmediate Indicates no delay/wait time. + @constant kDurationMicrosecond Microsecond units. + @constant kDurationMillisecond Millisecond units. + @constant kDurationSecond Second units. + @constant kDurationMinute Minute units. + @constant kDurationHour Hour units. + @constant kDurationDay Day units. + @constant kDurationForever Infinite period of time (no timeout). + + @discussion + + Duration values are intended to be multiplied by the specific interval to achieve an actual duration. For example, + to wait for 5 seconds you would use "5 * kDurationSecond". + */ + +#if ( TARGET_LANGUAGE_C_LIKE ) + #if ( !TARGET_OS_MAC ) +typedef int32_t Duration; + #endif +#endif + +#define kDurationImmediate 0L +#define kDurationMicrosecond -1L +#define kDurationMillisecond 1L +#define kDurationSecond ( 1000L * kDurationMillisecond ) +#define kDurationMinute ( 60L * kDurationSecond ) +#define kDurationHour ( 60L * kDurationMinute ) +#define kDurationDay ( 24L * kDurationHour ) +#define kDurationForever 0x7FFFFFFFL // Seconds <-> Minutes <-> Hours <-> Days <-> Weeks <-> Months <-> Years conversions -#define kNanosecondsPerMicrosecond 1000 -#define kNanosecondsPerMillisecond 1000000 -#define kNanosecondsPerSecond 1000000000 -#define kMicrosecondsPerSecond 1000000 -#define kMicrosecondsPerMillisecond 1000 -#define kMillisecondsPerSecond 1000 -#define kSecondsPerMinute 60 -#define kSecondsPerHour ( 60 * 60 ) // 3600 -#define kSecondsPerDay ( 60 * 60 * 24 ) // 86400 -#define kSecondsPerWeek ( 60 * 60 * 24 * 7 ) // 604800 -#define kMinutesPerHour 60 -#define kMinutesPerDay ( 60 * 24 ) // 1440 -#define kHoursPerDay 24 -#define kDaysPerWeek 7 -#define kWeeksPerYear 52 -#define kMonthsPerYear 12 +#define kNanosecondsPerMicrosecond 1000 +#define kNanosecondsPerMillisecond 1000000 +#define kNanosecondsPerSecond 1000000000 +#define kMicrosecondsPerSecond 1000000 +#define kMicrosecondsPerMillisecond 1000 +#define kMillisecondsPerSecond 1000 +#define kSecondsPerMinute 60 +#define kSecondsPerHour ( 60 * 60 ) // 3600 +#define kSecondsPerDay ( 60 * 60 * 24 ) // 86400 +#define kSecondsPerWeek ( 60 * 60 * 24 * 7 ) // 604800 +#define kMinutesPerHour 60 +#define kMinutesPerDay ( 60 * 24 ) // 1440 +#define kHoursPerDay 24 +#define kDaysPerWeek 7 +#define kWeeksPerYear 52 +#define kMonthsPerYear 12 //--------------------------------------------------------------------------------------------------------------------------- /*! @defined VersionStages - @abstract NumVersion-style version stages. -*/ + @abstract NumVersion-style version stages. + */ -#define kVersionStageDevelopment 0x20 -#define kVersionStageAlpha 0x40 -#define kVersionStageBeta 0x60 -#define kVersionStageFinal 0x80 +#define kVersionStageDevelopment 0x20 +#define kVersionStageAlpha 0x40 +#define kVersionStageBeta 0x60 +#define kVersionStageFinal 0x80 //--------------------------------------------------------------------------------------------------------------------------- /*! @function NumVersionBuild - @abstract Builds a 32-bit Mac-style NumVersion value (e.g. NumVersionBuild( 1, 2, 3, kVersionStageBeta, 4 ) -> 1.2.3b4). -*/ + @abstract Builds a 32-bit Mac-style NumVersion value (e.g. NumVersionBuild( 1, 2, 3, kVersionStageBeta, 4 ) -> 1.2.3b4). + */ -#define NumVersionBuild( MAJOR, MINOR, BUGFIX, STAGE, REV ) \ - ( ( ( ( MAJOR ) & 0xFF ) << 24 ) | \ - ( ( ( MINOR ) & 0x0F ) << 20 ) | \ - ( ( ( BUGFIX ) & 0x0F ) << 16 ) | \ - ( ( ( STAGE ) & 0xFF ) << 8 ) | \ - ( ( ( REV ) & 0xFF ) ) ) +#define NumVersionBuild( MAJOR, MINOR, BUGFIX, STAGE, REV ) \ + ( ( ( ( MAJOR ) & 0xFF ) << 24 ) | \ + ( ( ( MINOR ) & 0x0F ) << 20 ) | \ + ( ( ( BUGFIX ) & 0x0F ) << 16 ) | \ + ( ( ( STAGE ) & 0xFF ) << 8 ) | \ + ( ( ( REV ) & 0xFF ) ) ) -#define NumVersionExtractMajor( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 24 ) & 0xFF ) ) -#define NumVersionExtractMinorAndBugFix( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 16 ) & 0xFF ) ) -#define NumVersionExtractMinor( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 20 ) & 0x0F ) ) -#define NumVersionExtractBugFix( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 16 ) & 0x0F ) ) -#define NumVersionExtractStage( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 8 ) & 0xFF ) ) -#define NumVersionExtractRevision( VERSION ) ( (uint8_t)( ( VERSION ) & 0xFF ) ) +#define NumVersionExtractMajor( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 24 ) & 0xFF ) ) +#define NumVersionExtractMinorAndBugFix( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 16 ) & 0xFF ) ) +#define NumVersionExtractMinor( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 20 ) & 0x0F ) ) +#define NumVersionExtractBugFix( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 16 ) & 0x0F ) ) +#define NumVersionExtractStage( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 8 ) & 0xFF ) ) +#define NumVersionExtractRevision( VERSION ) ( (uint8_t)( ( VERSION ) & 0xFF ) ) //--------------------------------------------------------------------------------------------------------------------------- /*! @function NumVersionCompare - @abstract Compares two NumVersion values and returns the following values: - - left < right -> -1 - left > right -> 1 - left = right -> 0 -*/ + @abstract Compares two NumVersion values and returns the following values: + + left < right -> -1 + left > right -> 1 + left = right -> 0 + */ -#if( TARGET_LANGUAGE_C_LIKE ) - int NumVersionCompare( uint32_t inLeft, uint32_t inRight ); +#if ( TARGET_LANGUAGE_C_LIKE ) +int NumVersionCompare( uint32_t inLeft, uint32_t inRight ); #endif #if 0 @@ -1194,306 +1213,306 @@ //--------------------------------------------------------------------------------------------------------------------------- /*! @defined binary_4 - - @abstract Macro to generate an 4-bit constant using binary notation (e.g. binary_4( 1010 ) == 0xA). -*/ -#define binary_4( a ) binary_4_hex_wrap( hex_digit4( a ) ) -#define binary_4_hex_wrap( a ) binary_4_hex( a ) -#define binary_4_hex( a ) ( 0x ## a ) + @abstract Macro to generate an 4-bit constant using binary notation (e.g. binary_4( 1010 ) == 0xA). + */ + +#define binary_4( a ) binary_4_hex_wrap( hex_digit4( a ) ) +#define binary_4_hex_wrap( a ) binary_4_hex( a ) +#define binary_4_hex( a ) ( 0x ## a ) //--------------------------------------------------------------------------------------------------------------------------- /*! @defined binary_8 - - @abstract Macro to generate an 8-bit constant using binary notation (e.g. binary_8( 01111011 ) == 0x7B). -*/ -#define binary_8( a ) binary_8_hex_wrap( hex_digit8( a ) ) -#define binary_8_hex_wrap( a ) binary_8_hex( a ) -#define binary_8_hex( a ) ( 0x ## a ) + @abstract Macro to generate an 8-bit constant using binary notation (e.g. binary_8( 01111011 ) == 0x7B). + */ + +#define binary_8( a ) binary_8_hex_wrap( hex_digit8( a ) ) +#define binary_8_hex_wrap( a ) binary_8_hex( a ) +#define binary_8_hex( a ) ( 0x ## a ) //--------------------------------------------------------------------------------------------------------------------------- /*! @defined binary_16 - - @abstract Macro to generate an 16-bit constant using binary notation (e.g. binary_16( 01111011, 01111011 ) == 0x7B7B). -*/ -#define binary_16( a, b ) binary_16_hex_wrap( hex_digit8( a ), hex_digit8( b ) ) -#define binary_16_hex_wrap( a, b ) binary_16_hex( a, b ) -#define binary_16_hex( a, b ) ( 0x ## a ## b ) + @abstract Macro to generate an 16-bit constant using binary notation (e.g. binary_16( 01111011, 01111011 ) == 0x7B7B). + */ + +#define binary_16( a, b ) binary_16_hex_wrap( hex_digit8( a ), hex_digit8( b ) ) +#define binary_16_hex_wrap( a, b ) binary_16_hex( a, b ) +#define binary_16_hex( a, b ) ( 0x ## a ## b ) //--------------------------------------------------------------------------------------------------------------------------- /*! @defined binary_32 - - @abstract Macro to generate an 32-bit constant using binary notation - (e.g. binary_32( 01111011, 01111011, 01111011, 01111011 ) == 0x7B7B7B7B). -*/ -#define binary_32( a, b, c, d ) binary_32_hex_wrap( hex_digit8( a ), hex_digit8( b ), hex_digit8( c ), hex_digit8( d ) ) -#define binary_32_hex_wrap( a, b, c, d ) binary_32_hex( a, b, c, d ) -#define binary_32_hex( a, b, c, d ) ( 0x ## a ## b ## c ## d ) + @abstract Macro to generate an 32-bit constant using binary notation + (e.g. binary_32( 01111011, 01111011, 01111011, 01111011 ) == 0x7B7B7B7B). + */ + +#define binary_32( a, b, c, d ) binary_32_hex_wrap( hex_digit8( a ), hex_digit8( b ), hex_digit8( c ), hex_digit8( d ) ) +#define binary_32_hex_wrap( a, b, c, d ) binary_32_hex( a, b, c, d ) +#define binary_32_hex( a, b, c, d ) ( 0x ## a ## b ## c ## d ) // Binary Constant Helpers -#define hex_digit8( a ) HEX_DIGIT_ ## a -#define hex_digit4( a ) HEX_DIGIT_ ## 0000 ## a - -#define HEX_DIGIT_00000000 00 -#define HEX_DIGIT_00000001 01 -#define HEX_DIGIT_00000010 02 -#define HEX_DIGIT_00000011 03 -#define HEX_DIGIT_00000100 04 -#define HEX_DIGIT_00000101 05 -#define HEX_DIGIT_00000110 06 -#define HEX_DIGIT_00000111 07 -#define HEX_DIGIT_00001000 08 -#define HEX_DIGIT_00001001 09 -#define HEX_DIGIT_00001010 0A -#define HEX_DIGIT_00001011 0B -#define HEX_DIGIT_00001100 0C -#define HEX_DIGIT_00001101 0D -#define HEX_DIGIT_00001110 0E -#define HEX_DIGIT_00001111 0F -#define HEX_DIGIT_00010000 10 -#define HEX_DIGIT_00010001 11 -#define HEX_DIGIT_00010010 12 -#define HEX_DIGIT_00010011 13 -#define HEX_DIGIT_00010100 14 -#define HEX_DIGIT_00010101 15 -#define HEX_DIGIT_00010110 16 -#define HEX_DIGIT_00010111 17 -#define HEX_DIGIT_00011000 18 -#define HEX_DIGIT_00011001 19 -#define HEX_DIGIT_00011010 1A -#define HEX_DIGIT_00011011 1B -#define HEX_DIGIT_00011100 1C -#define HEX_DIGIT_00011101 1D -#define HEX_DIGIT_00011110 1E -#define HEX_DIGIT_00011111 1F -#define HEX_DIGIT_00100000 20 -#define HEX_DIGIT_00100001 21 -#define HEX_DIGIT_00100010 22 -#define HEX_DIGIT_00100011 23 -#define HEX_DIGIT_00100100 24 -#define HEX_DIGIT_00100101 25 -#define HEX_DIGIT_00100110 26 -#define HEX_DIGIT_00100111 27 -#define HEX_DIGIT_00101000 28 -#define HEX_DIGIT_00101001 29 -#define HEX_DIGIT_00101010 2A -#define HEX_DIGIT_00101011 2B -#define HEX_DIGIT_00101100 2C -#define HEX_DIGIT_00101101 2D -#define HEX_DIGIT_00101110 2E -#define HEX_DIGIT_00101111 2F -#define HEX_DIGIT_00110000 30 -#define HEX_DIGIT_00110001 31 -#define HEX_DIGIT_00110010 32 -#define HEX_DIGIT_00110011 33 -#define HEX_DIGIT_00110100 34 -#define HEX_DIGIT_00110101 35 -#define HEX_DIGIT_00110110 36 -#define HEX_DIGIT_00110111 37 -#define HEX_DIGIT_00111000 38 -#define HEX_DIGIT_00111001 39 -#define HEX_DIGIT_00111010 3A -#define HEX_DIGIT_00111011 3B -#define HEX_DIGIT_00111100 3C -#define HEX_DIGIT_00111101 3D -#define HEX_DIGIT_00111110 3E -#define HEX_DIGIT_00111111 3F -#define HEX_DIGIT_01000000 40 -#define HEX_DIGIT_01000001 41 -#define HEX_DIGIT_01000010 42 -#define HEX_DIGIT_01000011 43 -#define HEX_DIGIT_01000100 44 -#define HEX_DIGIT_01000101 45 -#define HEX_DIGIT_01000110 46 -#define HEX_DIGIT_01000111 47 -#define HEX_DIGIT_01001000 48 -#define HEX_DIGIT_01001001 49 -#define HEX_DIGIT_01001010 4A -#define HEX_DIGIT_01001011 4B -#define HEX_DIGIT_01001100 4C -#define HEX_DIGIT_01001101 4D -#define HEX_DIGIT_01001110 4E -#define HEX_DIGIT_01001111 4F -#define HEX_DIGIT_01010000 50 -#define HEX_DIGIT_01010001 51 -#define HEX_DIGIT_01010010 52 -#define HEX_DIGIT_01010011 53 -#define HEX_DIGIT_01010100 54 -#define HEX_DIGIT_01010101 55 -#define HEX_DIGIT_01010110 56 -#define HEX_DIGIT_01010111 57 -#define HEX_DIGIT_01011000 58 -#define HEX_DIGIT_01011001 59 -#define HEX_DIGIT_01011010 5A -#define HEX_DIGIT_01011011 5B -#define HEX_DIGIT_01011100 5C -#define HEX_DIGIT_01011101 5D -#define HEX_DIGIT_01011110 5E -#define HEX_DIGIT_01011111 5F -#define HEX_DIGIT_01100000 60 -#define HEX_DIGIT_01100001 61 -#define HEX_DIGIT_01100010 62 -#define HEX_DIGIT_01100011 63 -#define HEX_DIGIT_01100100 64 -#define HEX_DIGIT_01100101 65 -#define HEX_DIGIT_01100110 66 -#define HEX_DIGIT_01100111 67 -#define HEX_DIGIT_01101000 68 -#define HEX_DIGIT_01101001 69 -#define HEX_DIGIT_01101010 6A -#define HEX_DIGIT_01101011 6B -#define HEX_DIGIT_01101100 6C -#define HEX_DIGIT_01101101 6D -#define HEX_DIGIT_01101110 6E -#define HEX_DIGIT_01101111 6F -#define HEX_DIGIT_01110000 70 -#define HEX_DIGIT_01110001 71 -#define HEX_DIGIT_01110010 72 -#define HEX_DIGIT_01110011 73 -#define HEX_DIGIT_01110100 74 -#define HEX_DIGIT_01110101 75 -#define HEX_DIGIT_01110110 76 -#define HEX_DIGIT_01110111 77 -#define HEX_DIGIT_01111000 78 -#define HEX_DIGIT_01111001 79 -#define HEX_DIGIT_01111010 7A -#define HEX_DIGIT_01111011 7B -#define HEX_DIGIT_01111100 7C -#define HEX_DIGIT_01111101 7D -#define HEX_DIGIT_01111110 7E -#define HEX_DIGIT_01111111 7F -#define HEX_DIGIT_10000000 80 -#define HEX_DIGIT_10000001 81 -#define HEX_DIGIT_10000010 82 -#define HEX_DIGIT_10000011 83 -#define HEX_DIGIT_10000100 84 -#define HEX_DIGIT_10000101 85 -#define HEX_DIGIT_10000110 86 -#define HEX_DIGIT_10000111 87 -#define HEX_DIGIT_10001000 88 -#define HEX_DIGIT_10001001 89 -#define HEX_DIGIT_10001010 8A -#define HEX_DIGIT_10001011 8B -#define HEX_DIGIT_10001100 8C -#define HEX_DIGIT_10001101 8D -#define HEX_DIGIT_10001110 8E -#define HEX_DIGIT_10001111 8F -#define HEX_DIGIT_10010000 90 -#define HEX_DIGIT_10010001 91 -#define HEX_DIGIT_10010010 92 -#define HEX_DIGIT_10010011 93 -#define HEX_DIGIT_10010100 94 -#define HEX_DIGIT_10010101 95 -#define HEX_DIGIT_10010110 96 -#define HEX_DIGIT_10010111 97 -#define HEX_DIGIT_10011000 98 -#define HEX_DIGIT_10011001 99 -#define HEX_DIGIT_10011010 9A -#define HEX_DIGIT_10011011 9B -#define HEX_DIGIT_10011100 9C -#define HEX_DIGIT_10011101 9D -#define HEX_DIGIT_10011110 9E -#define HEX_DIGIT_10011111 9F -#define HEX_DIGIT_10100000 A0 -#define HEX_DIGIT_10100001 A1 -#define HEX_DIGIT_10100010 A2 -#define HEX_DIGIT_10100011 A3 -#define HEX_DIGIT_10100100 A4 -#define HEX_DIGIT_10100101 A5 -#define HEX_DIGIT_10100110 A6 -#define HEX_DIGIT_10100111 A7 -#define HEX_DIGIT_10101000 A8 -#define HEX_DIGIT_10101001 A9 -#define HEX_DIGIT_10101010 AA -#define HEX_DIGIT_10101011 AB -#define HEX_DIGIT_10101100 AC -#define HEX_DIGIT_10101101 AD -#define HEX_DIGIT_10101110 AE -#define HEX_DIGIT_10101111 AF -#define HEX_DIGIT_10110000 B0 -#define HEX_DIGIT_10110001 B1 -#define HEX_DIGIT_10110010 B2 -#define HEX_DIGIT_10110011 B3 -#define HEX_DIGIT_10110100 B4 -#define HEX_DIGIT_10110101 B5 -#define HEX_DIGIT_10110110 B6 -#define HEX_DIGIT_10110111 B7 -#define HEX_DIGIT_10111000 B8 -#define HEX_DIGIT_10111001 B9 -#define HEX_DIGIT_10111010 BA -#define HEX_DIGIT_10111011 BB -#define HEX_DIGIT_10111100 BC -#define HEX_DIGIT_10111101 BD -#define HEX_DIGIT_10111110 BE -#define HEX_DIGIT_10111111 BF -#define HEX_DIGIT_11000000 C0 -#define HEX_DIGIT_11000001 C1 -#define HEX_DIGIT_11000010 C2 -#define HEX_DIGIT_11000011 C3 -#define HEX_DIGIT_11000100 C4 -#define HEX_DIGIT_11000101 C5 -#define HEX_DIGIT_11000110 C6 -#define HEX_DIGIT_11000111 C7 -#define HEX_DIGIT_11001000 C8 -#define HEX_DIGIT_11001001 C9 -#define HEX_DIGIT_11001010 CA -#define HEX_DIGIT_11001011 CB -#define HEX_DIGIT_11001100 CC -#define HEX_DIGIT_11001101 CD -#define HEX_DIGIT_11001110 CE -#define HEX_DIGIT_11001111 CF -#define HEX_DIGIT_11010000 D0 -#define HEX_DIGIT_11010001 D1 -#define HEX_DIGIT_11010010 D2 -#define HEX_DIGIT_11010011 D3 -#define HEX_DIGIT_11010100 D4 -#define HEX_DIGIT_11010101 D5 -#define HEX_DIGIT_11010110 D6 -#define HEX_DIGIT_11010111 D7 -#define HEX_DIGIT_11011000 D8 -#define HEX_DIGIT_11011001 D9 -#define HEX_DIGIT_11011010 DA -#define HEX_DIGIT_11011011 DB -#define HEX_DIGIT_11011100 DC -#define HEX_DIGIT_11011101 DD -#define HEX_DIGIT_11011110 DE -#define HEX_DIGIT_11011111 DF -#define HEX_DIGIT_11100000 E0 -#define HEX_DIGIT_11100001 E1 -#define HEX_DIGIT_11100010 E2 -#define HEX_DIGIT_11100011 E3 -#define HEX_DIGIT_11100100 E4 -#define HEX_DIGIT_11100101 E5 -#define HEX_DIGIT_11100110 E6 -#define HEX_DIGIT_11100111 E7 -#define HEX_DIGIT_11101000 E8 -#define HEX_DIGIT_11101001 E9 -#define HEX_DIGIT_11101010 EA -#define HEX_DIGIT_11101011 EB -#define HEX_DIGIT_11101100 EC -#define HEX_DIGIT_11101101 ED -#define HEX_DIGIT_11101110 EE -#define HEX_DIGIT_11101111 EF -#define HEX_DIGIT_11110000 F0 -#define HEX_DIGIT_11110001 F1 -#define HEX_DIGIT_11110010 F2 -#define HEX_DIGIT_11110011 F3 -#define HEX_DIGIT_11110100 F4 -#define HEX_DIGIT_11110101 F5 -#define HEX_DIGIT_11110110 F6 -#define HEX_DIGIT_11110111 F7 -#define HEX_DIGIT_11111000 F8 -#define HEX_DIGIT_11111001 F9 -#define HEX_DIGIT_11111010 FA -#define HEX_DIGIT_11111011 FB -#define HEX_DIGIT_11111100 FC -#define HEX_DIGIT_11111101 FD -#define HEX_DIGIT_11111110 FE -#define HEX_DIGIT_11111111 FF +#define hex_digit8( a ) HEX_DIGIT_ ## a +#define hex_digit4( a ) HEX_DIGIT_ ## 0000 ## a + +#define HEX_DIGIT_00000000 00 +#define HEX_DIGIT_00000001 01 +#define HEX_DIGIT_00000010 02 +#define HEX_DIGIT_00000011 03 +#define HEX_DIGIT_00000100 04 +#define HEX_DIGIT_00000101 05 +#define HEX_DIGIT_00000110 06 +#define HEX_DIGIT_00000111 07 +#define HEX_DIGIT_00001000 08 +#define HEX_DIGIT_00001001 09 +#define HEX_DIGIT_00001010 0A +#define HEX_DIGIT_00001011 0B +#define HEX_DIGIT_00001100 0C +#define HEX_DIGIT_00001101 0D +#define HEX_DIGIT_00001110 0E +#define HEX_DIGIT_00001111 0F +#define HEX_DIGIT_00010000 10 +#define HEX_DIGIT_00010001 11 +#define HEX_DIGIT_00010010 12 +#define HEX_DIGIT_00010011 13 +#define HEX_DIGIT_00010100 14 +#define HEX_DIGIT_00010101 15 +#define HEX_DIGIT_00010110 16 +#define HEX_DIGIT_00010111 17 +#define HEX_DIGIT_00011000 18 +#define HEX_DIGIT_00011001 19 +#define HEX_DIGIT_00011010 1A +#define HEX_DIGIT_00011011 1B +#define HEX_DIGIT_00011100 1C +#define HEX_DIGIT_00011101 1D +#define HEX_DIGIT_00011110 1E +#define HEX_DIGIT_00011111 1F +#define HEX_DIGIT_00100000 20 +#define HEX_DIGIT_00100001 21 +#define HEX_DIGIT_00100010 22 +#define HEX_DIGIT_00100011 23 +#define HEX_DIGIT_00100100 24 +#define HEX_DIGIT_00100101 25 +#define HEX_DIGIT_00100110 26 +#define HEX_DIGIT_00100111 27 +#define HEX_DIGIT_00101000 28 +#define HEX_DIGIT_00101001 29 +#define HEX_DIGIT_00101010 2A +#define HEX_DIGIT_00101011 2B +#define HEX_DIGIT_00101100 2C +#define HEX_DIGIT_00101101 2D +#define HEX_DIGIT_00101110 2E +#define HEX_DIGIT_00101111 2F +#define HEX_DIGIT_00110000 30 +#define HEX_DIGIT_00110001 31 +#define HEX_DIGIT_00110010 32 +#define HEX_DIGIT_00110011 33 +#define HEX_DIGIT_00110100 34 +#define HEX_DIGIT_00110101 35 +#define HEX_DIGIT_00110110 36 +#define HEX_DIGIT_00110111 37 +#define HEX_DIGIT_00111000 38 +#define HEX_DIGIT_00111001 39 +#define HEX_DIGIT_00111010 3A +#define HEX_DIGIT_00111011 3B +#define HEX_DIGIT_00111100 3C +#define HEX_DIGIT_00111101 3D +#define HEX_DIGIT_00111110 3E +#define HEX_DIGIT_00111111 3F +#define HEX_DIGIT_01000000 40 +#define HEX_DIGIT_01000001 41 +#define HEX_DIGIT_01000010 42 +#define HEX_DIGIT_01000011 43 +#define HEX_DIGIT_01000100 44 +#define HEX_DIGIT_01000101 45 +#define HEX_DIGIT_01000110 46 +#define HEX_DIGIT_01000111 47 +#define HEX_DIGIT_01001000 48 +#define HEX_DIGIT_01001001 49 +#define HEX_DIGIT_01001010 4A +#define HEX_DIGIT_01001011 4B +#define HEX_DIGIT_01001100 4C +#define HEX_DIGIT_01001101 4D +#define HEX_DIGIT_01001110 4E +#define HEX_DIGIT_01001111 4F +#define HEX_DIGIT_01010000 50 +#define HEX_DIGIT_01010001 51 +#define HEX_DIGIT_01010010 52 +#define HEX_DIGIT_01010011 53 +#define HEX_DIGIT_01010100 54 +#define HEX_DIGIT_01010101 55 +#define HEX_DIGIT_01010110 56 +#define HEX_DIGIT_01010111 57 +#define HEX_DIGIT_01011000 58 +#define HEX_DIGIT_01011001 59 +#define HEX_DIGIT_01011010 5A +#define HEX_DIGIT_01011011 5B +#define HEX_DIGIT_01011100 5C +#define HEX_DIGIT_01011101 5D +#define HEX_DIGIT_01011110 5E +#define HEX_DIGIT_01011111 5F +#define HEX_DIGIT_01100000 60 +#define HEX_DIGIT_01100001 61 +#define HEX_DIGIT_01100010 62 +#define HEX_DIGIT_01100011 63 +#define HEX_DIGIT_01100100 64 +#define HEX_DIGIT_01100101 65 +#define HEX_DIGIT_01100110 66 +#define HEX_DIGIT_01100111 67 +#define HEX_DIGIT_01101000 68 +#define HEX_DIGIT_01101001 69 +#define HEX_DIGIT_01101010 6A +#define HEX_DIGIT_01101011 6B +#define HEX_DIGIT_01101100 6C +#define HEX_DIGIT_01101101 6D +#define HEX_DIGIT_01101110 6E +#define HEX_DIGIT_01101111 6F +#define HEX_DIGIT_01110000 70 +#define HEX_DIGIT_01110001 71 +#define HEX_DIGIT_01110010 72 +#define HEX_DIGIT_01110011 73 +#define HEX_DIGIT_01110100 74 +#define HEX_DIGIT_01110101 75 +#define HEX_DIGIT_01110110 76 +#define HEX_DIGIT_01110111 77 +#define HEX_DIGIT_01111000 78 +#define HEX_DIGIT_01111001 79 +#define HEX_DIGIT_01111010 7A +#define HEX_DIGIT_01111011 7B +#define HEX_DIGIT_01111100 7C +#define HEX_DIGIT_01111101 7D +#define HEX_DIGIT_01111110 7E +#define HEX_DIGIT_01111111 7F +#define HEX_DIGIT_10000000 80 +#define HEX_DIGIT_10000001 81 +#define HEX_DIGIT_10000010 82 +#define HEX_DIGIT_10000011 83 +#define HEX_DIGIT_10000100 84 +#define HEX_DIGIT_10000101 85 +#define HEX_DIGIT_10000110 86 +#define HEX_DIGIT_10000111 87 +#define HEX_DIGIT_10001000 88 +#define HEX_DIGIT_10001001 89 +#define HEX_DIGIT_10001010 8A +#define HEX_DIGIT_10001011 8B +#define HEX_DIGIT_10001100 8C +#define HEX_DIGIT_10001101 8D +#define HEX_DIGIT_10001110 8E +#define HEX_DIGIT_10001111 8F +#define HEX_DIGIT_10010000 90 +#define HEX_DIGIT_10010001 91 +#define HEX_DIGIT_10010010 92 +#define HEX_DIGIT_10010011 93 +#define HEX_DIGIT_10010100 94 +#define HEX_DIGIT_10010101 95 +#define HEX_DIGIT_10010110 96 +#define HEX_DIGIT_10010111 97 +#define HEX_DIGIT_10011000 98 +#define HEX_DIGIT_10011001 99 +#define HEX_DIGIT_10011010 9A +#define HEX_DIGIT_10011011 9B +#define HEX_DIGIT_10011100 9C +#define HEX_DIGIT_10011101 9D +#define HEX_DIGIT_10011110 9E +#define HEX_DIGIT_10011111 9F +#define HEX_DIGIT_10100000 A0 +#define HEX_DIGIT_10100001 A1 +#define HEX_DIGIT_10100010 A2 +#define HEX_DIGIT_10100011 A3 +#define HEX_DIGIT_10100100 A4 +#define HEX_DIGIT_10100101 A5 +#define HEX_DIGIT_10100110 A6 +#define HEX_DIGIT_10100111 A7 +#define HEX_DIGIT_10101000 A8 +#define HEX_DIGIT_10101001 A9 +#define HEX_DIGIT_10101010 AA +#define HEX_DIGIT_10101011 AB +#define HEX_DIGIT_10101100 AC +#define HEX_DIGIT_10101101 AD +#define HEX_DIGIT_10101110 AE +#define HEX_DIGIT_10101111 AF +#define HEX_DIGIT_10110000 B0 +#define HEX_DIGIT_10110001 B1 +#define HEX_DIGIT_10110010 B2 +#define HEX_DIGIT_10110011 B3 +#define HEX_DIGIT_10110100 B4 +#define HEX_DIGIT_10110101 B5 +#define HEX_DIGIT_10110110 B6 +#define HEX_DIGIT_10110111 B7 +#define HEX_DIGIT_10111000 B8 +#define HEX_DIGIT_10111001 B9 +#define HEX_DIGIT_10111010 BA +#define HEX_DIGIT_10111011 BB +#define HEX_DIGIT_10111100 BC +#define HEX_DIGIT_10111101 BD +#define HEX_DIGIT_10111110 BE +#define HEX_DIGIT_10111111 BF +#define HEX_DIGIT_11000000 C0 +#define HEX_DIGIT_11000001 C1 +#define HEX_DIGIT_11000010 C2 +#define HEX_DIGIT_11000011 C3 +#define HEX_DIGIT_11000100 C4 +#define HEX_DIGIT_11000101 C5 +#define HEX_DIGIT_11000110 C6 +#define HEX_DIGIT_11000111 C7 +#define HEX_DIGIT_11001000 C8 +#define HEX_DIGIT_11001001 C9 +#define HEX_DIGIT_11001010 CA +#define HEX_DIGIT_11001011 CB +#define HEX_DIGIT_11001100 CC +#define HEX_DIGIT_11001101 CD +#define HEX_DIGIT_11001110 CE +#define HEX_DIGIT_11001111 CF +#define HEX_DIGIT_11010000 D0 +#define HEX_DIGIT_11010001 D1 +#define HEX_DIGIT_11010010 D2 +#define HEX_DIGIT_11010011 D3 +#define HEX_DIGIT_11010100 D4 +#define HEX_DIGIT_11010101 D5 +#define HEX_DIGIT_11010110 D6 +#define HEX_DIGIT_11010111 D7 +#define HEX_DIGIT_11011000 D8 +#define HEX_DIGIT_11011001 D9 +#define HEX_DIGIT_11011010 DA +#define HEX_DIGIT_11011011 DB +#define HEX_DIGIT_11011100 DC +#define HEX_DIGIT_11011101 DD +#define HEX_DIGIT_11011110 DE +#define HEX_DIGIT_11011111 DF +#define HEX_DIGIT_11100000 E0 +#define HEX_DIGIT_11100001 E1 +#define HEX_DIGIT_11100010 E2 +#define HEX_DIGIT_11100011 E3 +#define HEX_DIGIT_11100100 E4 +#define HEX_DIGIT_11100101 E5 +#define HEX_DIGIT_11100110 E6 +#define HEX_DIGIT_11100111 E7 +#define HEX_DIGIT_11101000 E8 +#define HEX_DIGIT_11101001 E9 +#define HEX_DIGIT_11101010 EA +#define HEX_DIGIT_11101011 EB +#define HEX_DIGIT_11101100 EC +#define HEX_DIGIT_11101101 ED +#define HEX_DIGIT_11101110 EE +#define HEX_DIGIT_11101111 EF +#define HEX_DIGIT_11110000 F0 +#define HEX_DIGIT_11110001 F1 +#define HEX_DIGIT_11110010 F2 +#define HEX_DIGIT_11110011 F3 +#define HEX_DIGIT_11110100 F4 +#define HEX_DIGIT_11110101 F5 +#define HEX_DIGIT_11110110 F6 +#define HEX_DIGIT_11110111 F7 +#define HEX_DIGIT_11111000 F8 +#define HEX_DIGIT_11111001 F9 +#define HEX_DIGIT_11111010 FA +#define HEX_DIGIT_11111011 FB +#define HEX_DIGIT_11111100 FC +#define HEX_DIGIT_11111101 FD +#define HEX_DIGIT_11111110 FE +#define HEX_DIGIT_11111111 FF #if 0 #pragma mark == Debugging == @@ -1502,17 +1521,17 @@ //--------------------------------------------------------------------------------------------------------------------------- /*! @function CommonServicesTest - @abstract Unit test. -*/ + @abstract Unit test. + */ -#if( DEBUG ) - #if( TARGET_LANGUAGE_C_LIKE ) - OSStatus CommonServicesTest( void ); - #endif +#if ( DEBUG ) + #if ( TARGET_LANGUAGE_C_LIKE ) +OSStatus CommonServicesTest( void ); + #endif #endif -#ifdef __cplusplus - } +#ifdef __cplusplus +} #endif -#endif // __COMMON_SERVICES__ +#endif // __COMMON_SERVICES__ diff --git a/mDNSShared/DebugServices.c b/mDNSShared/DebugServices.c index 6473296..98f876a 100644 --- a/mDNSShared/DebugServices.c +++ b/mDNSShared/DebugServices.c @@ -5,19 +5,19 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - To Do: - - - Use StackWalk on Windows to optionally print stack frames. -*/ + To Do: + + - Use StackWalk on Windows to optionally print stack frames. + */ #if 0 #pragma mark == Includes == @@ -27,39 +27,39 @@ // Includes //=========================================================================================================================== -#if( !KERNEL ) - #include - #include - #include +#if ( !KERNEL ) + #include + #include + #include #endif -#include "CommonServices.h" +#include "CommonServices.h" -#include "DebugServices.h" +#include "DebugServices.h" -#if( DEBUG ) +#if ( DEBUG ) -#if( TARGET_OS_VXWORKS ) - #include "intLib.h" +#if ( TARGET_OS_VXWORKS ) + #include "intLib.h" #endif -#if( TARGET_OS_WIN32 ) - #include - - #if( !TARGET_OS_WINDOWS_CE ) - #include - #include - #endif +#if ( TARGET_OS_WIN32 ) + #include + + #if ( !TARGET_OS_WINDOWS_CE ) + #include + #include + #endif #endif -#if( DEBUG_IDEBUG_ENABLED && TARGET_API_MAC_OSX_KERNEL ) - #include +#if ( DEBUG_IDEBUG_ENABLED && TARGET_API_MAC_OSX_KERNEL ) + #include #endif // If MDNS_DEBUGMSGS is defined (even if defined 0), it is aware of mDNS and it is probably safe to include mDNSEmbeddedAPI.h. -#if( defined( MDNS_DEBUGMSGS ) ) - #include "mDNSEmbeddedAPI.h" +#if ( defined( MDNS_DEBUGMSGS ) ) + #include "mDNSEmbeddedAPI.h" #endif #if 0 @@ -70,7 +70,7 @@ // Macros //=========================================================================================================================== -#define DebugIsPrint( C ) ( ( ( C ) >= 0x20 ) && ( ( C ) <= 0x7E ) ) +#define DebugIsPrint( C ) ( ( ( C ) >= 0x20 ) && ( ( C ) <= 0x7E ) ) #if 0 #pragma mark == Prototypes == @@ -80,86 +80,86 @@ // Prototypes //=========================================================================================================================== -static OSStatus DebugPrint( DebugLevel inLevel, char *inData, size_t inSize ); +static OSStatus DebugPrint( DebugLevel inLevel, char *inData, size_t inSize ); // fprintf -#if( DEBUG_FPRINTF_ENABLED ) - static OSStatus DebugFPrintFInit( DebugOutputTypeFlags inFlags, const char *inFilename ); - static void DebugFPrintFPrint( char *inData, size_t inSize ); +#if ( DEBUG_FPRINTF_ENABLED ) +static OSStatus DebugFPrintFInit( DebugOutputTypeFlags inFlags, const char *inFilename ); +static void DebugFPrintFPrint( char *inData, size_t inSize ); #endif // iDebug (Mac OS X user and kernel) -#if( DEBUG_IDEBUG_ENABLED ) - static OSStatus DebugiDebugInit( void ); - static void DebugiDebugPrint( char *inData, size_t inSize ); +#if ( DEBUG_IDEBUG_ENABLED ) +static OSStatus DebugiDebugInit( void ); +static void DebugiDebugPrint( char *inData, size_t inSize ); #endif // kprintf (Mac OS X Kernel) -#if( DEBUG_KPRINTF_ENABLED ) - static void DebugKPrintFPrint( char *inData, size_t inSize ); +#if ( DEBUG_KPRINTF_ENABLED ) +static void DebugKPrintFPrint( char *inData, size_t inSize ); #endif // Mac OS X IOLog (Mac OS X Kernel) -#if( DEBUG_MAC_OS_X_IOLOG_ENABLED ) - static void DebugMacOSXIOLogPrint( char *inData, size_t inSize ); +#if ( DEBUG_MAC_OS_X_IOLOG_ENABLED ) +static void DebugMacOSXIOLogPrint( char *inData, size_t inSize ); #endif // Mac OS X Log -#if( TARGET_OS_MAC ) - static OSStatus DebugMacOSXLogInit( void ); - static void DebugMacOSXLogPrint( char *inData, size_t inSize ); +#if ( TARGET_OS_MAC ) +static OSStatus DebugMacOSXLogInit( void ); +static void DebugMacOSXLogPrint( char *inData, size_t inSize ); #endif // Windows Debugger -#if( TARGET_OS_WIN32 ) - static void DebugWindowsDebuggerPrint( char *inData, size_t inSize ); +#if ( TARGET_OS_WIN32 ) +static void DebugWindowsDebuggerPrint( char *inData, size_t inSize ); #endif // Windows Event Log -#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) - static OSStatus DebugWindowsEventLogInit( const char *inName, HMODULE inModule ); - static void DebugWindowsEventLogPrint( DebugLevel inLevel, char *inData, size_t inSize ); +#if ( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) +static OSStatus DebugWindowsEventLogInit( const char *inName, HMODULE inModule ); +static void DebugWindowsEventLogPrint( DebugLevel inLevel, char *inData, size_t inSize ); #endif // DebugLib support -#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED ) - static pascal void - DebugAssertOutputHandler( - OSType inComponentSignature, - UInt32 inOptions, - const char * inAssertionString, - const char * inExceptionString, - const char * inErrorString, - const char * inFileName, - long inLineNumber, - void * inValue, - ConstStr255Param inOutputMsg ); +#if ( DEBUG_CORE_SERVICE_ASSERTS_ENABLED ) +static pascal void +DebugAssertOutputHandler( + OSType inComponentSignature, + UInt32 inOptions, + const char * inAssertionString, + const char * inExceptionString, + const char * inErrorString, + const char * inFileName, + long inLineNumber, + void * inValue, + ConstStr255Param inOutputMsg ); #endif // Utilities -static char * DebugNumVersionToString( uint32_t inVersion, char *inString ); +static char * DebugNumVersionToString( uint32_t inVersion, char *inString ); -#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) - static void DebugWinEnableConsole( void ); +#if ( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) +static void DebugWinEnableConsole( void ); #endif -#if( TARGET_OS_WIN32 ) - static TCHAR * - DebugWinCharToTCharString( - const char * inCharString, - size_t inCharCount, - TCHAR * outTCharString, - size_t inTCharCountMax, - size_t * outTCharCount ); +#if ( TARGET_OS_WIN32 ) +static TCHAR * +DebugWinCharToTCharString( + const char * inCharString, + size_t inCharCount, + TCHAR * outTCharString, + size_t inTCharCountMax, + size_t * outTCharCount ); #endif #if 0 @@ -170,62 +170,62 @@ static char * DebugNumVersionToString( uint32_t inVersion, char *inString ); // Private Globals //=========================================================================================================================== -#if( TARGET_OS_VXWORKS ) - // TCP States for inetstatShow. - - extern char ** pTcpstates; // defined in tcpLib.c - - const char * kDebugTCPStates[] = - { - "(0) TCPS_CLOSED", - "(1) TCPS_LISTEN", - "(2) TCPS_SYN_SENT", - "(3) TCPS_SYN_RECEIVED", - "(4) TCPS_ESTABLISHED", - "(5) TCPS_CLOSE_WAIT", - "(6) TCPS_FIN_WAIT_1", - "(7) TCPS_CLOSING", - "(8) TCPS_LAST_ACK", - "(9) TCPS_FIN_WAIT_2", - "(10) TCPS_TIME_WAIT", - }; +#if ( TARGET_OS_VXWORKS ) +// TCP States for inetstatShow. + +extern char ** pTcpstates; // defined in tcpLib.c + +const char * kDebugTCPStates[] = +{ + "(0) TCPS_CLOSED", + "(1) TCPS_LISTEN", + "(2) TCPS_SYN_SENT", + "(3) TCPS_SYN_RECEIVED", + "(4) TCPS_ESTABLISHED", + "(5) TCPS_CLOSE_WAIT", + "(6) TCPS_FIN_WAIT_1", + "(7) TCPS_CLOSING", + "(8) TCPS_LAST_ACK", + "(9) TCPS_FIN_WAIT_2", + "(10) TCPS_TIME_WAIT", +}; #endif // General -static bool gDebugInitialized = false; -static DebugOutputType gDebugOutputType = kDebugOutputTypeNone; -static DebugLevel gDebugPrintLevelMin = kDebugLevelInfo; -static DebugLevel gDebugPrintLevelMax = kDebugLevelMax; -static DebugLevel gDebugBreakLevel = kDebugLevelAssert; -#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED ) - static DebugAssertOutputHandlerUPP gDebugAssertOutputHandlerUPP = NULL; +static bool gDebugInitialized = false; +static DebugOutputType gDebugOutputType = kDebugOutputTypeNone; +static DebugLevel gDebugPrintLevelMin = kDebugLevelInfo; +static DebugLevel gDebugPrintLevelMax = kDebugLevelMax; +static DebugLevel gDebugBreakLevel = kDebugLevelAssert; +#if ( DEBUG_CORE_SERVICE_ASSERTS_ENABLED ) +static DebugAssertOutputHandlerUPP gDebugAssertOutputHandlerUPP = NULL; #endif // Custom -static DebugOutputFunctionPtr gDebugCustomOutputFunction = NULL; -static void * gDebugCustomOutputContext = NULL; +static DebugOutputFunctionPtr gDebugCustomOutputFunction = NULL; +static void * gDebugCustomOutputContext = NULL; // fprintf -#if( DEBUG_FPRINTF_ENABLED ) - static FILE * gDebugFPrintFFile = NULL; +#if ( DEBUG_FPRINTF_ENABLED ) +static FILE * gDebugFPrintFFile = NULL; #endif // MacOSXLog -#if( TARGET_OS_MAC ) - typedef int ( *DebugMacOSXLogFunctionPtr )( const char *inFormat, ... ); - - static DebugMacOSXLogFunctionPtr gDebugMacOSXLogFunction = NULL; +#if ( TARGET_OS_MAC ) +typedef int ( *DebugMacOSXLogFunctionPtr )( const char *inFormat, ... ); + +static DebugMacOSXLogFunctionPtr gDebugMacOSXLogFunction = NULL; #endif // WindowsEventLog -#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) - static HANDLE gDebugWindowsEventLogEventSource = NULL; +#if ( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) +static HANDLE gDebugWindowsEventLogEventSource = NULL; #endif #if 0 @@ -237,179 +237,179 @@ static void * gDebugCustomOutputContext = NULL; // DebugInitialize //=========================================================================================================================== -DEBUG_EXPORT OSStatus DebugInitialize( DebugOutputType inType, ... ) +DEBUG_EXPORT OSStatus DebugInitialize( DebugOutputType inType, ... ) { - OSStatus err; - DebugOutputType type; - va_list args; - - va_start( args, inType ); - -#if( TARGET_OS_VXWORKS ) - // Set up the TCP state strings if they are not already set up by VxWorks (normally not set up for some reason). - - if( !pTcpstates ) - { - pTcpstates = (char **) kDebugTCPStates; - } + OSStatus err; + DebugOutputType type; + va_list args; + + va_start( args, inType ); + +#if ( TARGET_OS_VXWORKS ) + // Set up the TCP state strings if they are not already set up by VxWorks (normally not set up for some reason). + + if( !pTcpstates ) + { + pTcpstates = (char **) kDebugTCPStates; + } #endif - - // Set up DebugLib stuff (if building with Debugging.h). - -#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED ) - if( !gDebugAssertOutputHandlerUPP ) - { - gDebugAssertOutputHandlerUPP = NewDebugAssertOutputHandlerUPP( DebugAssertOutputHandler ); - check( gDebugAssertOutputHandlerUPP ); - if( gDebugAssertOutputHandlerUPP ) - { - InstallDebugAssertOutputHandler( gDebugAssertOutputHandlerUPP ); - } - } + + // Set up DebugLib stuff (if building with Debugging.h). + +#if ( DEBUG_CORE_SERVICE_ASSERTS_ENABLED ) + if( !gDebugAssertOutputHandlerUPP ) + { + gDebugAssertOutputHandlerUPP = NewDebugAssertOutputHandlerUPP( DebugAssertOutputHandler ); + check( gDebugAssertOutputHandlerUPP ); + if( gDebugAssertOutputHandlerUPP ) + { + InstallDebugAssertOutputHandler( gDebugAssertOutputHandlerUPP ); + } + } #endif - - // Pre-process meta-output kind to pick an appropriate output kind for the platform. - - type = inType; - if( type == kDebugOutputTypeMetaConsole ) - { - #if( TARGET_OS_MAC ) - type = kDebugOutputTypeMacOSXLog; - #elif( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) - #if( DEBUG_FPRINTF_ENABLED ) - type = kDebugOutputTypeFPrintF; - #else - type = kDebugOutputTypeWindowsDebugger; - #endif - #elif( TARGET_API_MAC_OSX_KERNEL ) - #if( DEBUG_MAC_OS_X_IOLOG_ENABLED ) - type = kDebugOutputTypeMacOSXIOLog; - #elif( DEBUG_IDEBUG_ENABLED ) - type = kDebugOutputTypeiDebug; - #elif( DEBUG_KPRINTF_ENABLED ) - type = kDebugOutputTypeKPrintF; - #endif - #elif( TARGET_OS_VXWORKS ) - #if( DEBUG_FPRINTF_ENABLED ) - type = kDebugOutputTypeFPrintF; - #else - #error target is VxWorks, but fprintf output is disabled - #endif - #else - #if( DEBUG_FPRINTF_ENABLED ) - type = kDebugOutputTypeFPrintF; - #endif - #endif - } - - // Process output kind. - - gDebugOutputType = type; - switch( type ) - { - case kDebugOutputTypeNone: - err = kNoErr; - break; - - case kDebugOutputTypeCustom: - gDebugCustomOutputFunction = va_arg( args, DebugOutputFunctionPtr ); - gDebugCustomOutputContext = va_arg( args, void * ); - err = kNoErr; - break; - -#if( DEBUG_FPRINTF_ENABLED ) - case kDebugOutputTypeFPrintF: - if( inType == kDebugOutputTypeMetaConsole ) - { - err = DebugFPrintFInit( kDebugOutputTypeFlagsStdErr, NULL ); - } - else - { - DebugOutputTypeFlags flags; - const char * filename; - - flags = (DebugOutputTypeFlags) va_arg( args, unsigned int ); - if( ( flags & kDebugOutputTypeFlagsTypeMask ) == kDebugOutputTypeFlagsFile ) - { - filename = va_arg( args, const char * ); - } - else - { - filename = NULL; - } - err = DebugFPrintFInit( flags, filename ); - } - break; + + // Pre-process meta-output kind to pick an appropriate output kind for the platform. + + type = inType; + if( type == kDebugOutputTypeMetaConsole ) + { + #if ( TARGET_OS_MAC ) + type = kDebugOutputTypeMacOSXLog; + #elif ( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) + #if ( DEBUG_FPRINTF_ENABLED ) + type = kDebugOutputTypeFPrintF; + #else + type = kDebugOutputTypeWindowsDebugger; + #endif + #elif ( TARGET_API_MAC_OSX_KERNEL ) + #if ( DEBUG_MAC_OS_X_IOLOG_ENABLED ) + type = kDebugOutputTypeMacOSXIOLog; + #elif ( DEBUG_IDEBUG_ENABLED ) + type = kDebugOutputTypeiDebug; + #elif ( DEBUG_KPRINTF_ENABLED ) + type = kDebugOutputTypeKPrintF; + #endif + #elif ( TARGET_OS_VXWORKS ) + #if ( DEBUG_FPRINTF_ENABLED ) + type = kDebugOutputTypeFPrintF; + #else + #error target is VxWorks, but fprintf output is disabled + #endif + #else + #if ( DEBUG_FPRINTF_ENABLED ) + type = kDebugOutputTypeFPrintF; + #endif + #endif + } + + // Process output kind. + + gDebugOutputType = type; + switch( type ) + { + case kDebugOutputTypeNone: + err = kNoErr; + break; + + case kDebugOutputTypeCustom: + gDebugCustomOutputFunction = va_arg( args, DebugOutputFunctionPtr ); + gDebugCustomOutputContext = va_arg( args, void * ); + err = kNoErr; + break; + +#if ( DEBUG_FPRINTF_ENABLED ) + case kDebugOutputTypeFPrintF: + if( inType == kDebugOutputTypeMetaConsole ) + { + err = DebugFPrintFInit( kDebugOutputTypeFlagsStdErr, NULL ); + } + else + { + DebugOutputTypeFlags flags; + const char * filename; + + flags = (DebugOutputTypeFlags) va_arg( args, unsigned int ); + if( ( flags & kDebugOutputTypeFlagsTypeMask ) == kDebugOutputTypeFlagsFile ) + { + filename = va_arg( args, const char * ); + } + else + { + filename = NULL; + } + err = DebugFPrintFInit( flags, filename ); + } + break; #endif -#if( DEBUG_IDEBUG_ENABLED ) - case kDebugOutputTypeiDebug: - err = DebugiDebugInit(); - break; +#if ( DEBUG_IDEBUG_ENABLED ) + case kDebugOutputTypeiDebug: + err = DebugiDebugInit(); + break; #endif -#if( DEBUG_KPRINTF_ENABLED ) - case kDebugOutputTypeKPrintF: - err = kNoErr; - break; +#if ( DEBUG_KPRINTF_ENABLED ) + case kDebugOutputTypeKPrintF: + err = kNoErr; + break; #endif -#if( DEBUG_MAC_OS_X_IOLOG_ENABLED ) - case kDebugOutputTypeMacOSXIOLog: - err = kNoErr; - break; +#if ( DEBUG_MAC_OS_X_IOLOG_ENABLED ) + case kDebugOutputTypeMacOSXIOLog: + err = kNoErr; + break; #endif -#if( TARGET_OS_MAC ) - case kDebugOutputTypeMacOSXLog: - err = DebugMacOSXLogInit(); - break; +#if ( TARGET_OS_MAC ) + case kDebugOutputTypeMacOSXLog: + err = DebugMacOSXLogInit(); + break; #endif -#if( TARGET_OS_WIN32 ) - case kDebugOutputTypeWindowsDebugger: - err = kNoErr; - break; +#if ( TARGET_OS_WIN32 ) + case kDebugOutputTypeWindowsDebugger: + err = kNoErr; + break; #endif -#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) - case kDebugOutputTypeWindowsEventLog: - { - const char * name; - HMODULE module; - - name = va_arg( args, const char * ); - module = va_arg( args, HMODULE ); - err = DebugWindowsEventLogInit( name, module ); - } - break; +#if ( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) + case kDebugOutputTypeWindowsEventLog: + { + const char * name; + HMODULE module; + + name = va_arg( args, const char * ); + module = va_arg( args, HMODULE ); + err = DebugWindowsEventLogInit( name, module ); + } + break; #endif - default: - err = kParamErr; - goto exit; - } - gDebugInitialized = true; - + default: + err = kParamErr; + goto exit; + } + gDebugInitialized = true; + exit: - va_end( args ); - return( err ); + va_end( args ); + return( err ); } //=========================================================================================================================== // DebugFinalize //=========================================================================================================================== -DEBUG_EXPORT void DebugFinalize( void ) +DEBUG_EXPORT void DebugFinalize( void ) { -#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED ) - check( gDebugAssertOutputHandlerUPP ); - if( gDebugAssertOutputHandlerUPP ) - { - InstallDebugAssertOutputHandler( NULL ); - DisposeDebugAssertOutputHandlerUPP( gDebugAssertOutputHandlerUPP ); - gDebugAssertOutputHandlerUPP = NULL; - } +#if ( DEBUG_CORE_SERVICE_ASSERTS_ENABLED ) + check( gDebugAssertOutputHandlerUPP ); + if( gDebugAssertOutputHandlerUPP ) + { + InstallDebugAssertOutputHandler( NULL ); + DisposeDebugAssertOutputHandlerUPP( gDebugAssertOutputHandlerUPP ); + gDebugAssertOutputHandlerUPP = NULL; + } #endif } @@ -417,78 +417,78 @@ DEBUG_EXPORT void DebugFinalize( void ) // DebugGetProperty //=========================================================================================================================== -DEBUG_EXPORT OSStatus DebugGetProperty( DebugPropertyTag inTag, ... ) +DEBUG_EXPORT OSStatus DebugGetProperty( DebugPropertyTag inTag, ... ) { - OSStatus err; - va_list args; - DebugLevel * level; - - va_start( args, inTag ); - switch( inTag ) - { - case kDebugPropertyTagPrintLevelMin: - level = va_arg( args, DebugLevel * ); - *level = gDebugPrintLevelMin; - err = kNoErr; - break; - - case kDebugPropertyTagPrintLevelMax: - level = va_arg( args, DebugLevel * ); - *level = gDebugPrintLevelMax; - err = kNoErr; - break; - - case kDebugPropertyTagBreakLevel: - level = va_arg( args, DebugLevel * ); - *level = gDebugBreakLevel; - err = kNoErr; - break; - - default: - err = kUnsupportedErr; - break; - } - va_end( args ); - return( err ); + OSStatus err; + va_list args; + DebugLevel * level; + + va_start( args, inTag ); + switch( inTag ) + { + case kDebugPropertyTagPrintLevelMin: + level = va_arg( args, DebugLevel * ); + *level = gDebugPrintLevelMin; + err = kNoErr; + break; + + case kDebugPropertyTagPrintLevelMax: + level = va_arg( args, DebugLevel * ); + *level = gDebugPrintLevelMax; + err = kNoErr; + break; + + case kDebugPropertyTagBreakLevel: + level = va_arg( args, DebugLevel * ); + *level = gDebugBreakLevel; + err = kNoErr; + break; + + default: + err = kUnsupportedErr; + break; + } + va_end( args ); + return( err ); } //=========================================================================================================================== // DebugSetProperty //=========================================================================================================================== -DEBUG_EXPORT OSStatus DebugSetProperty( DebugPropertyTag inTag, ... ) +DEBUG_EXPORT OSStatus DebugSetProperty( DebugPropertyTag inTag, ... ) { - OSStatus err; - va_list args; - DebugLevel level; - - va_start( args, inTag ); - switch( inTag ) - { - case kDebugPropertyTagPrintLevelMin: - level = va_arg( args, DebugLevel ); - gDebugPrintLevelMin = level; - err = kNoErr; - break; - - case kDebugPropertyTagPrintLevelMax: - level = va_arg( args, DebugLevel ); - gDebugPrintLevelMax = level; - err = kNoErr; - break; - - case kDebugPropertyTagBreakLevel: - level = va_arg( args, DebugLevel ); - gDebugBreakLevel = level; - err = kNoErr; - break; - - default: - err = kUnsupportedErr; - break; - } - va_end( args ); - return( err ); + OSStatus err; + va_list args; + DebugLevel level; + + va_start( args, inTag ); + switch( inTag ) + { + case kDebugPropertyTagPrintLevelMin: + level = va_arg( args, DebugLevel ); + gDebugPrintLevelMin = level; + err = kNoErr; + break; + + case kDebugPropertyTagPrintLevelMax: + level = va_arg( args, DebugLevel ); + gDebugPrintLevelMax = level; + err = kNoErr; + break; + + case kDebugPropertyTagBreakLevel: + level = va_arg( args, DebugLevel ); + gDebugBreakLevel = level; + err = kNoErr; + break; + + default: + err = kUnsupportedErr; + break; + } + va_end( args ); + return( err ); } #if 0 @@ -500,644 +500,644 @@ DEBUG_EXPORT OSStatus DebugSetProperty( DebugPropertyTag inTag, ... ) // DebugPrintF //=========================================================================================================================== -DEBUG_EXPORT size_t DebugPrintF( DebugLevel inLevel, const char *inFormat, ... ) -{ - va_list args; - size_t n; - - // Skip if the level is not in the enabled range.. - - if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) ) - { - n = 0; - goto exit; - } - - va_start( args, inFormat ); - n = DebugPrintFVAList( inLevel, inFormat, args ); - va_end( args ); +DEBUG_EXPORT size_t DebugPrintF( DebugLevel inLevel, const char *inFormat, ... ) +{ + va_list args; + size_t n; + + // Skip if the level is not in the enabled range.. + + if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) ) + { + n = 0; + goto exit; + } + + va_start( args, inFormat ); + n = DebugPrintFVAList( inLevel, inFormat, args ); + va_end( args ); exit: - return( n ); + return( n ); } //=========================================================================================================================== // DebugPrintFVAList //=========================================================================================================================== -DEBUG_EXPORT size_t DebugPrintFVAList( DebugLevel inLevel, const char *inFormat, va_list inArgs ) +DEBUG_EXPORT size_t DebugPrintFVAList( DebugLevel inLevel, const char *inFormat, va_list inArgs ) { - size_t n; - char buffer[ 512 ]; - - // Skip if the level is not in the enabled range.. - - if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) ) - { - n = 0; - goto exit; - } - - n = DebugSNPrintFVAList( buffer, sizeof( buffer ), inFormat, inArgs ); - DebugPrint( inLevel, buffer, (size_t) n ); + size_t n; + char buffer[ 512 ]; + + // Skip if the level is not in the enabled range.. + + if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) ) + { + n = 0; + goto exit; + } + + n = DebugSNPrintFVAList( buffer, sizeof( buffer ), inFormat, inArgs ); + DebugPrint( inLevel, buffer, (size_t) n ); exit: - return( n ); + return( n ); } //=========================================================================================================================== // DebugPrint //=========================================================================================================================== -static OSStatus DebugPrint( DebugLevel inLevel, char *inData, size_t inSize ) +static OSStatus DebugPrint( DebugLevel inLevel, char *inData, size_t inSize ) { - OSStatus err; - - // Skip if the level is not in the enabled range.. - - if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) ) - { - err = kRangeErr; - goto exit; - } - - // Printing is not safe at interrupt time so check for this and warn with an interrupt safe mechanism (if available). - - if( DebugTaskLevel() & kDebugInterruptLevelMask ) - { - #if( TARGET_OS_VXWORKS ) - logMsg( "\ncannot print at interrupt time\n\n", 1, 2, 3, 4, 5, 6 ); - #endif - - err = kExecutionStateErr; - goto exit; - } - - // Initialize the debugging library if it hasn't already been initialized (allows for zero-config usage). - - if( !gDebugInitialized ) - { - debug_initialize( kDebugOutputTypeMetaConsole ); - } - - // Print based on the current output type. - - switch( gDebugOutputType ) - { - case kDebugOutputTypeNone: - break; - - case kDebugOutputTypeCustom: - if( gDebugCustomOutputFunction ) - { - gDebugCustomOutputFunction( inData, inSize, gDebugCustomOutputContext ); - } - break; - -#if( DEBUG_FPRINTF_ENABLED ) - case kDebugOutputTypeFPrintF: - DebugFPrintFPrint( inData, inSize ); - break; + OSStatus err; + + // Skip if the level is not in the enabled range.. + + if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) ) + { + err = kRangeErr; + goto exit; + } + + // Printing is not safe at interrupt time so check for this and warn with an interrupt safe mechanism (if available). + + if( DebugTaskLevel() & kDebugInterruptLevelMask ) + { + #if ( TARGET_OS_VXWORKS ) + logMsg( "\ncannot print at interrupt time\n\n", 1, 2, 3, 4, 5, 6 ); + #endif + + err = kExecutionStateErr; + goto exit; + } + + // Initialize the debugging library if it hasn't already been initialized (allows for zero-config usage). + + if( !gDebugInitialized ) + { + debug_initialize( kDebugOutputTypeMetaConsole ); + } + + // Print based on the current output type. + + switch( gDebugOutputType ) + { + case kDebugOutputTypeNone: + break; + + case kDebugOutputTypeCustom: + if( gDebugCustomOutputFunction ) + { + gDebugCustomOutputFunction( inData, inSize, gDebugCustomOutputContext ); + } + break; + +#if ( DEBUG_FPRINTF_ENABLED ) + case kDebugOutputTypeFPrintF: + DebugFPrintFPrint( inData, inSize ); + break; #endif -#if( DEBUG_IDEBUG_ENABLED ) - case kDebugOutputTypeiDebug: - DebugiDebugPrint( inData, inSize ); - break; +#if ( DEBUG_IDEBUG_ENABLED ) + case kDebugOutputTypeiDebug: + DebugiDebugPrint( inData, inSize ); + break; #endif -#if( DEBUG_KPRINTF_ENABLED ) - case kDebugOutputTypeKPrintF: - DebugKPrintFPrint( inData, inSize ); - break; +#if ( DEBUG_KPRINTF_ENABLED ) + case kDebugOutputTypeKPrintF: + DebugKPrintFPrint( inData, inSize ); + break; #endif -#if( DEBUG_MAC_OS_X_IOLOG_ENABLED ) - case kDebugOutputTypeMacOSXIOLog: - DebugMacOSXIOLogPrint( inData, inSize ); - break; +#if ( DEBUG_MAC_OS_X_IOLOG_ENABLED ) + case kDebugOutputTypeMacOSXIOLog: + DebugMacOSXIOLogPrint( inData, inSize ); + break; #endif -#if( TARGET_OS_MAC ) - case kDebugOutputTypeMacOSXLog: - DebugMacOSXLogPrint( inData, inSize ); - break; +#if ( TARGET_OS_MAC ) + case kDebugOutputTypeMacOSXLog: + DebugMacOSXLogPrint( inData, inSize ); + break; #endif -#if( TARGET_OS_WIN32 ) - case kDebugOutputTypeWindowsDebugger: - DebugWindowsDebuggerPrint( inData, inSize ); - break; +#if ( TARGET_OS_WIN32 ) + case kDebugOutputTypeWindowsDebugger: + DebugWindowsDebuggerPrint( inData, inSize ); + break; #endif -#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) - case kDebugOutputTypeWindowsEventLog: - DebugWindowsEventLogPrint( inLevel, inData, inSize ); - break; +#if ( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) + case kDebugOutputTypeWindowsEventLog: + DebugWindowsEventLogPrint( inLevel, inData, inSize ); + break; #endif - default: - break; - } - err = kNoErr; - + default: + break; + } + err = kNoErr; + exit: - return( err ); + return( err ); } //=========================================================================================================================== // DebugPrintAssert // // Warning: This routine relies on several of the strings being string constants that will exist forever because the -// underlying logMsg API that does the printing is asynchronous so it cannot use temporary/stack-based -// pointer variables (e.g. local strings). The debug macros that invoke this function only use constant +// underlying logMsg API that does the printing is asynchronous so it cannot use temporary/stack-based +// pointer variables (e.g. local strings). The debug macros that invoke this function only use constant // constant strings, but if this function is invoked directly from other places, it must use constant strings. //=========================================================================================================================== DEBUG_EXPORT void - DebugPrintAssert( - int_least32_t inErrorCode, - const char * inAssertString, - const char * inMessage, - const char * inFilename, - int_least32_t inLineNumber, - const char * inFunction ) +DebugPrintAssert( + int_least32_t inErrorCode, + const char * inAssertString, + const char * inMessage, + const char * inFilename, + int_least32_t inLineNumber, + const char * inFunction ) { - // Skip if the level is not in the enabled range.. - - if( ( kDebugLevelAssert < gDebugPrintLevelMin ) || ( kDebugLevelAssert > gDebugPrintLevelMax ) ) - { - return; - } - - if( inErrorCode != 0 ) - { - DebugPrintF( - kDebugLevelAssert, - "\n" - "[ASSERT] error: %ld (%m)\n" - "[ASSERT] where: \"%s\", line %ld, \"%s\"\n" - "\n", - inErrorCode, inErrorCode, - inFilename ? inFilename : "", - inLineNumber, - inFunction ? inFunction : "" ); - } - else - { - DebugPrintF( - kDebugLevelAssert, - "\n" - "[ASSERT] assert: \"%s\" %s\n" - "[ASSERT] where: \"%s\", line %ld, \"%s\"\n" - "\n", - inAssertString ? inAssertString : "", - inMessage ? inMessage : "", - inFilename ? inFilename : "", - inLineNumber, - inFunction ? inFunction : "" ); - } - - // Break into the debugger if enabled. - - #if( TARGET_OS_WIN32 ) - if( gDebugBreakLevel <= kDebugLevelAssert ) - { - if( IsDebuggerPresent() ) - { - DebugBreak(); - } - } - #endif + // Skip if the level is not in the enabled range.. + + if( ( kDebugLevelAssert < gDebugPrintLevelMin ) || ( kDebugLevelAssert > gDebugPrintLevelMax ) ) + { + return; + } + + if( inErrorCode != 0 ) + { + DebugPrintF( + kDebugLevelAssert, + "\n" + "[ASSERT] error: %ld (%m)\n" + "[ASSERT] where: \"%s\", line %ld, \"%s\"\n" + "\n", + inErrorCode, inErrorCode, + inFilename ? inFilename : "", + inLineNumber, + inFunction ? inFunction : "" ); + } + else + { + DebugPrintF( + kDebugLevelAssert, + "\n" + "[ASSERT] assert: \"%s\" %s\n" + "[ASSERT] where: \"%s\", line %ld, \"%s\"\n" + "\n", + inAssertString ? inAssertString : "", + inMessage ? inMessage : "", + inFilename ? inFilename : "", + inLineNumber, + inFunction ? inFunction : "" ); + } + + // Break into the debugger if enabled. + + #if ( TARGET_OS_WIN32 ) + if( gDebugBreakLevel <= kDebugLevelAssert ) + { + if( IsDebuggerPresent() ) + { + DebugBreak(); + } + } + #endif } #if 0 #pragma mark - #endif -#if( DEBUG_FPRINTF_ENABLED ) +#if ( DEBUG_FPRINTF_ENABLED ) //=========================================================================================================================== // DebugFPrintFInit //=========================================================================================================================== -static OSStatus DebugFPrintFInit( DebugOutputTypeFlags inFlags, const char *inFilename ) +static OSStatus DebugFPrintFInit( DebugOutputTypeFlags inFlags, const char *inFilename ) { - OSStatus err; - DebugOutputTypeFlags typeFlags; - - typeFlags = inFlags & kDebugOutputTypeFlagsTypeMask; - if( typeFlags == kDebugOutputTypeFlagsStdOut ) - { - #if( TARGET_OS_WIN32 ) - DebugWinEnableConsole(); - #endif - - gDebugFPrintFFile = stdout; - } - else if( typeFlags == kDebugOutputTypeFlagsStdErr ) - { - #if( TARGET_OS_WIN32 ) - DebugWinEnableConsole(); - #endif - - gDebugFPrintFFile = stdout; - } - else if( typeFlags == kDebugOutputTypeFlagsFile ) - { - require_action_quiet( inFilename && ( *inFilename != '\0' ), exit, err = kOpenErr ); - - gDebugFPrintFFile = fopen( inFilename, "a" ); - require_action_quiet( gDebugFPrintFFile, exit, err = kOpenErr ); - } - else - { - err = kParamErr; - goto exit; - } - err = kNoErr; - + OSStatus err; + DebugOutputTypeFlags typeFlags; + + typeFlags = inFlags & kDebugOutputTypeFlagsTypeMask; + if( typeFlags == kDebugOutputTypeFlagsStdOut ) + { + #if ( TARGET_OS_WIN32 ) + DebugWinEnableConsole(); + #endif + + gDebugFPrintFFile = stdout; + } + else if( typeFlags == kDebugOutputTypeFlagsStdErr ) + { + #if ( TARGET_OS_WIN32 ) + DebugWinEnableConsole(); + #endif + + gDebugFPrintFFile = stdout; + } + else if( typeFlags == kDebugOutputTypeFlagsFile ) + { + require_action_quiet( inFilename && ( *inFilename != '\0' ), exit, err = kOpenErr ); + + gDebugFPrintFFile = fopen( inFilename, "a" ); + require_action_quiet( gDebugFPrintFFile, exit, err = kOpenErr ); + } + else + { + err = kParamErr; + goto exit; + } + err = kNoErr; + exit: - return( err ); + return( err ); } //=========================================================================================================================== // DebugFPrintFPrint //=========================================================================================================================== -static void DebugFPrintFPrint( char *inData, size_t inSize ) +static void DebugFPrintFPrint( char *inData, size_t inSize ) { - char * p; - char * q; - - // Convert \r to \n. fprintf will interpret \n and convert to whatever is appropriate for the platform. - - p = inData; - q = p + inSize; - while( p < q ) - { - if( *p == '\r' ) - { - *p = '\n'; - } - ++p; - } - - // Write the data and flush. - - if( gDebugFPrintFFile ) - { - fprintf( gDebugFPrintFFile, "%.*s", (int) inSize, inData ); - fflush( gDebugFPrintFFile ); - } + char * p; + char * q; + + // Convert \r to \n. fprintf will interpret \n and convert to whatever is appropriate for the platform. + + p = inData; + q = p + inSize; + while( p < q ) + { + if( *p == '\r' ) + { + *p = '\n'; + } + ++p; + } + + // Write the data and flush. + + if( gDebugFPrintFFile ) + { + fprintf( gDebugFPrintFFile, "%.*s", (int) inSize, inData ); + fflush( gDebugFPrintFFile ); + } } -#endif // DEBUG_FPRINTF_ENABLED +#endif // DEBUG_FPRINTF_ENABLED -#if( DEBUG_IDEBUG_ENABLED ) +#if ( DEBUG_IDEBUG_ENABLED ) //=========================================================================================================================== // DebugiDebugInit //=========================================================================================================================== -static OSStatus DebugiDebugInit( void ) +static OSStatus DebugiDebugInit( void ) { - OSStatus err; - - #if( TARGET_API_MAC_OSX_KERNEL ) - - extern uint32_t * _giDebugReserved1; - - // Emulate the iDebugSetOutputType macro in iDebugServices.h. - // Note: This is not thread safe, but neither is iDebugServices.h nor iDebugKext. - - if( !_giDebugReserved1 ) - { - _giDebugReserved1 = (uint32_t *) IOMalloc( sizeof( uint32_t ) ); - require_action_quiet( _giDebugReserved1, exit, err = kNoMemoryErr ); - } - *_giDebugReserved1 = 0x00010000U; - err = kNoErr; + OSStatus err; + + #if ( TARGET_API_MAC_OSX_KERNEL ) + + extern uint32_t * _giDebugReserved1; + + // Emulate the iDebugSetOutputType macro in iDebugServices.h. + // Note: This is not thread safe, but neither is iDebugServices.h nor iDebugKext. + + if( !_giDebugReserved1 ) + { + _giDebugReserved1 = (uint32_t *) IOMalloc( sizeof( uint32_t ) ); + require_action_quiet( _giDebugReserved1, exit, err = kNoMemoryErr ); + } + *_giDebugReserved1 = 0x00010000U; + err = kNoErr; exit: - #else - - __private_extern__ void iDebugSetOutputTypeInternal( uint32_t inType ); - - iDebugSetOutputTypeInternal( 0x00010000U ); - err = kNoErr; - - #endif - - return( err ); + #else + + __private_extern__ void iDebugSetOutputTypeInternal( uint32_t inType ); + + iDebugSetOutputTypeInternal( 0x00010000U ); + err = kNoErr; + + #endif + + return( err ); } //=========================================================================================================================== // DebugiDebugPrint //=========================================================================================================================== -static void DebugiDebugPrint( char *inData, size_t inSize ) +static void DebugiDebugPrint( char *inData, size_t inSize ) { - #if( TARGET_API_MAC_OSX_KERNEL ) - - // Locally declared here so we do not need to include iDebugKext.h. - // Note: IOKit uses a global namespace for all code and only a partial link occurs at build time. When the - // KEXT is loaded, the runtime linker will link in this extern'd symbol (assuming iDebug is present). - // _giDebugLogInternal is actually part of IOKit proper so this should link even if iDebug is not present. - - typedef void ( *iDebugLogFunctionPtr )( uint32_t inLevel, uint32_t inTag, const char *inFormat, ... ); - - extern iDebugLogFunctionPtr _giDebugLogInternal; - - if( _giDebugLogInternal ) - { - _giDebugLogInternal( 0, 0, "%.*s", (int) inSize, inData ); - } - - #else - - __private_extern__ void iDebugLogInternal( uint32_t inLevel, uint32_t inTag, const char *inFormat, ... ); - - iDebugLogInternal( 0, 0, "%.*s", (int) inSize, inData ); - - #endif + #if ( TARGET_API_MAC_OSX_KERNEL ) + + // Locally declared here so we do not need to include iDebugKext.h. + // Note: IOKit uses a global namespace for all code and only a partial link occurs at build time. When the + // KEXT is loaded, the runtime linker will link in this extern'd symbol (assuming iDebug is present). + // _giDebugLogInternal is actually part of IOKit proper so this should link even if iDebug is not present. + + typedef void ( *iDebugLogFunctionPtr )( uint32_t inLevel, uint32_t inTag, const char *inFormat, ... ); + + extern iDebugLogFunctionPtr _giDebugLogInternal; + + if( _giDebugLogInternal ) + { + _giDebugLogInternal( 0, 0, "%.*s", (int) inSize, inData ); + } + + #else + + __private_extern__ void iDebugLogInternal( uint32_t inLevel, uint32_t inTag, const char *inFormat, ... ); + + iDebugLogInternal( 0, 0, "%.*s", (int) inSize, inData ); + + #endif } #endif -#if( DEBUG_KPRINTF_ENABLED ) +#if ( DEBUG_KPRINTF_ENABLED ) //=========================================================================================================================== // DebugKPrintFPrint //=========================================================================================================================== -static void DebugKPrintFPrint( char *inData, size_t inSize ) +static void DebugKPrintFPrint( char *inData, size_t inSize ) { - extern void kprintf( const char *inFormat, ... ); - - kprintf( "%.*s", (int) inSize, inData ); + extern void kprintf( const char *inFormat, ... ); + + kprintf( "%.*s", (int) inSize, inData ); } #endif -#if( DEBUG_MAC_OS_X_IOLOG_ENABLED ) +#if ( DEBUG_MAC_OS_X_IOLOG_ENABLED ) //=========================================================================================================================== // DebugMacOSXIOLogPrint //=========================================================================================================================== -static void DebugMacOSXIOLogPrint( char *inData, size_t inSize ) +static void DebugMacOSXIOLogPrint( char *inData, size_t inSize ) { - extern void IOLog( const char *inFormat, ... ); - - IOLog( "%.*s", (int) inSize, inData ); + extern void IOLog( const char *inFormat, ... ); + + IOLog( "%.*s", (int) inSize, inData ); } #endif -#if( TARGET_OS_MAC ) +#if ( TARGET_OS_MAC ) //=========================================================================================================================== // DebugMacOSXLogInit //=========================================================================================================================== -static OSStatus DebugMacOSXLogInit( void ) +static OSStatus DebugMacOSXLogInit( void ) { - OSStatus err; - CFStringRef path; - CFURLRef url; - CFBundleRef bundle; - CFStringRef functionName; - void * functionPtr; - - bundle = NULL; - - // Create a bundle reference for System.framework. - - path = CFSTR( "/System/Library/Frameworks/System.framework" ); - url = CFURLCreateWithFileSystemPath( NULL, path, kCFURLPOSIXPathStyle, true ); - require_action_quiet( url, exit, err = memFullErr ); - - bundle = CFBundleCreate( NULL, url ); - CFRelease( url ); - require_action_quiet( bundle, exit, err = memFullErr ); - - // Get a ptr to the system's "printf" function from System.framework. - - functionName = CFSTR( "printf" ); - functionPtr = CFBundleGetFunctionPointerForName( bundle, functionName ); - require_action_quiet( functionPtr, exit, err = memFullErr ); - - // Success! Note: The bundle cannot be released because it would invalidate the function ptr. - - gDebugMacOSXLogFunction = (DebugMacOSXLogFunctionPtr) functionPtr; - bundle = NULL; - err = noErr; - + OSStatus err; + CFStringRef path; + CFURLRef url; + CFBundleRef bundle; + CFStringRef functionName; + void * functionPtr; + + bundle = NULL; + + // Create a bundle reference for System.framework. + + path = CFSTR( "/System/Library/Frameworks/System.framework" ); + url = CFURLCreateWithFileSystemPath( NULL, path, kCFURLPOSIXPathStyle, true ); + require_action_quiet( url, exit, err = memFullErr ); + + bundle = CFBundleCreate( NULL, url ); + CFRelease( url ); + require_action_quiet( bundle, exit, err = memFullErr ); + + // Get a ptr to the system's "printf" function from System.framework. + + functionName = CFSTR( "printf" ); + functionPtr = CFBundleGetFunctionPointerForName( bundle, functionName ); + require_action_quiet( functionPtr, exit, err = memFullErr ); + + // Success! Note: The bundle cannot be released because it would invalidate the function ptr. + + gDebugMacOSXLogFunction = (DebugMacOSXLogFunctionPtr) functionPtr; + bundle = NULL; + err = noErr; + exit: - if( bundle ) - { - CFRelease( bundle ); - } - return( err ); + if( bundle ) + { + CFRelease( bundle ); + } + return( err ); } //=========================================================================================================================== // DebugMacOSXLogPrint //=========================================================================================================================== -static void DebugMacOSXLogPrint( char *inData, size_t inSize ) -{ - if( gDebugMacOSXLogFunction ) - { - gDebugMacOSXLogFunction( "%.*s", (int) inSize, inData ); - } +static void DebugMacOSXLogPrint( char *inData, size_t inSize ) +{ + if( gDebugMacOSXLogFunction ) + { + gDebugMacOSXLogFunction( "%.*s", (int) inSize, inData ); + } } #endif -#if( TARGET_OS_WIN32 ) +#if ( TARGET_OS_WIN32 ) //=========================================================================================================================== // DebugWindowsDebuggerPrint //=========================================================================================================================== -void DebugWindowsDebuggerPrint( char *inData, size_t inSize ) +void DebugWindowsDebuggerPrint( char *inData, size_t inSize ) { - TCHAR buffer[ 512 ]; - const char * src; - const char * end; - TCHAR * dst; - char c; - - // Copy locally and null terminate the string. This also converts from char to TCHAR in case we are - // building with UNICODE enabled since the input is always char. Also convert \r to \n in the process. - - src = inData; - if( inSize >= sizeof_array( buffer ) ) - { - inSize = sizeof_array( buffer ) - 1; - } - end = src + inSize; - dst = buffer; - while( src < end ) - { - c = *src++; - if( c == '\r' ) - { - c = '\n'; - } - *dst++ = (TCHAR) c; - } - *dst = 0; - - // Print out the string to the debugger. - - OutputDebugString( buffer ); + TCHAR buffer[ 512 ]; + const char * src; + const char * end; + TCHAR * dst; + char c; + + // Copy locally and null terminate the string. This also converts from char to TCHAR in case we are + // building with UNICODE enabled since the input is always char. Also convert \r to \n in the process. + + src = inData; + if( inSize >= sizeof_array( buffer ) ) + { + inSize = sizeof_array( buffer ) - 1; + } + end = src + inSize; + dst = buffer; + while( src < end ) + { + c = *src++; + if( c == '\r' ) + { + c = '\n'; + } + *dst++ = (TCHAR) c; + } + *dst = 0; + + // Print out the string to the debugger. + + OutputDebugString( buffer ); } #endif -#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) +#if ( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) //=========================================================================================================================== // DebugWindowsEventLogInit //=========================================================================================================================== -static OSStatus DebugWindowsEventLogInit( const char *inName, HMODULE inModule ) +static OSStatus DebugWindowsEventLogInit( const char *inName, HMODULE inModule ) { - OSStatus err; - HKEY key; - TCHAR name[ 128 ]; - const char * src; - TCHAR path[ MAX_PATH ]; - size_t size; - DWORD typesSupported; - DWORD n; - - key = NULL; - - // Use a default name if needed then convert the name to TCHARs so it works on ANSI or Unicode builds. - - if( !inName || ( *inName == '\0' ) ) - { - inName = "DefaultApp"; - } - DebugWinCharToTCharString( inName, kSizeCString, name, sizeof( name ), NULL ); - - // Build the path string using the fixed registry path and app name. - - src = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\"; - DebugWinCharToTCharString( src, kSizeCString, path, sizeof_array( path ), &size ); - DebugWinCharToTCharString( inName, kSizeCString, path + size, sizeof_array( path ) - size, NULL ); - - // Add/Open the source name as a sub-key under the Application key in the EventLog registry key. - - err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, path, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL ); - require_noerr_quiet( err, exit ); - - // Set the path in the EventMessageFile subkey. Add 1 to the TCHAR count to include the null terminator. - - n = GetModuleFileName( inModule, path, sizeof_array( path ) ); - err = translate_errno( n > 0, (OSStatus) GetLastError(), kParamErr ); - require_noerr_quiet( err, exit ); - n += 1; - n *= sizeof( TCHAR ); - - err = RegSetValueEx( key, TEXT( "EventMessageFile" ), 0, REG_EXPAND_SZ, (const LPBYTE) path, n ); - require_noerr_quiet( err, exit ); - - // Set the supported event types in the TypesSupported subkey. - - typesSupported = EVENTLOG_SUCCESS | EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE | - EVENTLOG_AUDIT_SUCCESS | EVENTLOG_AUDIT_FAILURE; - err = RegSetValueEx( key, TEXT( "TypesSupported" ), 0, REG_DWORD, (const LPBYTE) &typesSupported, sizeof( DWORD ) ); - require_noerr_quiet( err, exit ); - - // Set up the event source. - - gDebugWindowsEventLogEventSource = RegisterEventSource( NULL, name ); - err = translate_errno( gDebugWindowsEventLogEventSource, (OSStatus) GetLastError(), kParamErr ); - require_noerr_quiet( err, exit ); - + OSStatus err; + HKEY key; + TCHAR name[ 128 ]; + const char * src; + TCHAR path[ MAX_PATH ]; + size_t size; + DWORD typesSupported; + DWORD n; + + key = NULL; + + // Use a default name if needed then convert the name to TCHARs so it works on ANSI or Unicode builds. + + if( !inName || ( *inName == '\0' ) ) + { + inName = "DefaultApp"; + } + DebugWinCharToTCharString( inName, kSizeCString, name, sizeof( name ), NULL ); + + // Build the path string using the fixed registry path and app name. + + src = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\"; + DebugWinCharToTCharString( src, kSizeCString, path, sizeof_array( path ), &size ); + DebugWinCharToTCharString( inName, kSizeCString, path + size, sizeof_array( path ) - size, NULL ); + + // Add/Open the source name as a sub-key under the Application key in the EventLog registry key. + + err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, path, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL ); + require_noerr_quiet( err, exit ); + + // Set the path in the EventMessageFile subkey. Add 1 to the TCHAR count to include the null terminator. + + n = GetModuleFileName( inModule, path, sizeof_array( path ) ); + err = translate_errno( n > 0, (OSStatus) GetLastError(), kParamErr ); + require_noerr_quiet( err, exit ); + n += 1; + n *= sizeof( TCHAR ); + + err = RegSetValueEx( key, TEXT( "EventMessageFile" ), 0, REG_EXPAND_SZ, (const LPBYTE) path, n ); + require_noerr_quiet( err, exit ); + + // Set the supported event types in the TypesSupported subkey. + + typesSupported = EVENTLOG_SUCCESS | EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE | + EVENTLOG_AUDIT_SUCCESS | EVENTLOG_AUDIT_FAILURE; + err = RegSetValueEx( key, TEXT( "TypesSupported" ), 0, REG_DWORD, (const LPBYTE) &typesSupported, sizeof( DWORD ) ); + require_noerr_quiet( err, exit ); + + // Set up the event source. + + gDebugWindowsEventLogEventSource = RegisterEventSource( NULL, name ); + err = translate_errno( gDebugWindowsEventLogEventSource, (OSStatus) GetLastError(), kParamErr ); + require_noerr_quiet( err, exit ); + exit: - if( key ) - { - RegCloseKey( key ); - } - return( err ); + if( key ) + { + RegCloseKey( key ); + } + return( err ); } //=========================================================================================================================== // DebugWindowsEventLogPrint //=========================================================================================================================== -static void DebugWindowsEventLogPrint( DebugLevel inLevel, char *inData, size_t inSize ) +static void DebugWindowsEventLogPrint( DebugLevel inLevel, char *inData, size_t inSize ) { - WORD type; - TCHAR buffer[ 512 ]; - const char * src; - const char * end; - TCHAR * dst; - char c; - const TCHAR * array[ 1 ]; - - // Map the debug level to a Windows EventLog type. - - if( inLevel <= kDebugLevelNotice ) - { - type = EVENTLOG_INFORMATION_TYPE; - } - else if( inLevel <= kDebugLevelWarning ) - { - type = EVENTLOG_WARNING_TYPE; - } - else - { - type = EVENTLOG_ERROR_TYPE; - } - - // Copy locally and null terminate the string. This also converts from char to TCHAR in case we are - // building with UNICODE enabled since the input is always char. Also convert \r to \n in the process. - - src = inData; - if( inSize >= sizeof_array( buffer ) ) - { - inSize = sizeof_array( buffer ) - 1; - } - end = src + inSize; - dst = buffer; - while( src < end ) - { - c = *src++; - if( c == '\r' ) - { - c = '\n'; - } - *dst++ = (TCHAR) c; - } - *dst = 0; - - // Add the the string to the event log. - - array[ 0 ] = buffer; - if( gDebugWindowsEventLogEventSource ) - { - ReportEvent( gDebugWindowsEventLogEventSource, type, 0, 0x20000001L, NULL, 1, 0, array, NULL ); - } + WORD type; + TCHAR buffer[ 512 ]; + const char * src; + const char * end; + TCHAR * dst; + char c; + const TCHAR * array[ 1 ]; + + // Map the debug level to a Windows EventLog type. + + if( inLevel <= kDebugLevelNotice ) + { + type = EVENTLOG_INFORMATION_TYPE; + } + else if( inLevel <= kDebugLevelWarning ) + { + type = EVENTLOG_WARNING_TYPE; + } + else + { + type = EVENTLOG_ERROR_TYPE; + } + + // Copy locally and null terminate the string. This also converts from char to TCHAR in case we are + // building with UNICODE enabled since the input is always char. Also convert \r to \n in the process. + + src = inData; + if( inSize >= sizeof_array( buffer ) ) + { + inSize = sizeof_array( buffer ) - 1; + } + end = src + inSize; + dst = buffer; + while( src < end ) + { + c = *src++; + if( c == '\r' ) + { + c = '\n'; + } + *dst++ = (TCHAR) c; + } + *dst = 0; + + // Add the the string to the event log. + + array[ 0 ] = buffer; + if( gDebugWindowsEventLogEventSource ) + { + ReportEvent( gDebugWindowsEventLogEventSource, type, 0, 0x20000001L, NULL, 1, 0, array, NULL ); + } } -#endif // TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE +#endif // TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE -#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED ) +#if ( DEBUG_CORE_SERVICE_ASSERTS_ENABLED ) //=========================================================================================================================== // DebugAssertOutputHandler //=========================================================================================================================== -static pascal void - DebugAssertOutputHandler( - OSType inComponentSignature, - UInt32 inOptions, - const char * inAssertString, - const char * inExceptionString, - const char * inErrorString, - const char * inFileName, - long inLineNumber, - void * inValue, - ConstStr255Param inOutputMsg ) +static pascal void +DebugAssertOutputHandler( + OSType inComponentSignature, + UInt32 inOptions, + const char * inAssertString, + const char * inExceptionString, + const char * inErrorString, + const char * inFileName, + long inLineNumber, + void * inValue, + ConstStr255Param inOutputMsg ) { - DEBUG_UNUSED( inComponentSignature ); - DEBUG_UNUSED( inOptions ); - DEBUG_UNUSED( inExceptionString ); - DEBUG_UNUSED( inValue ); - DEBUG_UNUSED( inOutputMsg ); - - DebugPrintAssert( 0, inAssertString, inErrorString, inFileName, (int_least32_t) inLineNumber, "" ); + DEBUG_UNUSED( inComponentSignature ); + DEBUG_UNUSED( inOptions ); + DEBUG_UNUSED( inExceptionString ); + DEBUG_UNUSED( inValue ); + DEBUG_UNUSED( inOutputMsg ); + + DebugPrintAssert( 0, inAssertString, inErrorString, inFileName, (int_least32_t) inLineNumber, "" ); } #endif @@ -1170,848 +1170,848 @@ static pascal void //=========================================================================================================================== DEBUG_EXPORT size_t DebugSNPrintF(char *sbuffer, size_t buflen, const char *fmt, ...) - { - size_t length; - - va_list ptr; - va_start(ptr,fmt); - length = DebugSNPrintFVAList(sbuffer, buflen, fmt, ptr); - va_end(ptr); - - return(length); - } +{ + size_t length; + + va_list ptr; + va_start(ptr,fmt); + length = DebugSNPrintFVAList(sbuffer, buflen, fmt, ptr); + va_end(ptr); + + return(length); +} //=========================================================================================================================== // DebugSNPrintFVAList - va_list version of DebugSNPrintF. See DebugSNPrintF for more info. //=========================================================================================================================== DEBUG_EXPORT size_t DebugSNPrintFVAList(char *sbuffer, size_t buflen, const char *fmt, va_list arg) - { - static const struct DebugSNPrintF_format - { - unsigned leftJustify : 1; - unsigned forceSign : 1; - unsigned zeroPad : 1; - unsigned havePrecision : 1; - unsigned hSize : 1; - char lSize; - char altForm; - char sign; // +, - or space - unsigned int fieldWidth; - unsigned int precision; - } DebugSNPrintF_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - - size_t nwritten = 0; - int c; - if (buflen == 0) return(0); - buflen--; // Pre-reserve one space in the buffer for the terminating nul - if (buflen == 0) goto exit; - - for (c = *fmt; c != 0; c = *++fmt) - { - if (c != '%') - { - *sbuffer++ = (char)c; - if (++nwritten >= buflen) goto exit; - } - else - { - size_t i=0, j; - // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for - // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc. - // The size needs to be enough for a 256-byte domain name plus some error text. - #define mDNS_VACB_Size 300 - char mDNS_VACB[mDNS_VACB_Size]; - #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size]) - #define mDNS_VACB_Remain(s) ((size_t)(mDNS_VACB_Lim - s)) - char *s = mDNS_VACB_Lim; - const char *digits = "0123456789ABCDEF"; - struct DebugSNPrintF_format F = DebugSNPrintF_format_default; - - for(;;) // decode flags - { - c = *++fmt; - if (c == '-') F.leftJustify = 1; - else if (c == '+') F.forceSign = 1; - else if (c == ' ') F.sign = ' '; - else if (c == '#') F.altForm++; - else if (c == '0') F.zeroPad = 1; - else break; - } - - if (c == '*') // decode field width - { - int f = va_arg(arg, int); - if (f < 0) { f = -f; F.leftJustify = 1; } - F.fieldWidth = (unsigned int)f; - c = *++fmt; - } - else - { - for (; c >= '0' && c <= '9'; c = *++fmt) - F.fieldWidth = (10 * F.fieldWidth) + (c - '0'); - } - - if (c == '.') // decode precision - { - if ((c = *++fmt) == '*') - { F.precision = va_arg(arg, unsigned int); c = *++fmt; } - else for (; c >= '0' && c <= '9'; c = *++fmt) - F.precision = (10 * F.precision) + (c - '0'); - F.havePrecision = 1; - } - - if (F.leftJustify) F.zeroPad = 0; - - conv: - switch (c) // perform appropriate conversion - { - #if TYPE_LONGLONG_NATIVE - unsigned_long_long_compat n; - unsigned_long_long_compat base; - #else - unsigned long n; - unsigned long base; - #endif - case 'h' : F.hSize = 1; c = *++fmt; goto conv; - case 'l' : // fall through - case 'L' : F.lSize++; c = *++fmt; goto conv; - case 'd' : - case 'i' : base = 10; - goto canBeSigned; - case 'u' : base = 10; - goto notSigned; - case 'o' : base = 8; - goto notSigned; - case 'b' : base = 2; - goto notSigned; - case 'p' : n = va_arg(arg, uintptr_t); - F.havePrecision = 1; - F.precision = (sizeof(uintptr_t) == 4) ? 8 : 16; - F.sign = 0; - base = 16; - c = 'x'; - goto number; - case 'x' : digits = "0123456789abcdef"; - case 'X' : base = 16; - goto notSigned; - canBeSigned: - #if TYPE_LONGLONG_NATIVE - if (F.lSize == 1) n = (unsigned_long_long_compat)va_arg(arg, long); - else if (F.lSize == 2) n = (unsigned_long_long_compat)va_arg(arg, long_long_compat); - else n = (unsigned_long_long_compat)va_arg(arg, int); - #else - if (F.lSize == 1) n = (unsigned long)va_arg(arg, long); - else if (F.lSize == 2) goto exit; - else n = (unsigned long)va_arg(arg, int); - #endif - if (F.hSize) n = (short) n; - #if TYPE_LONGLONG_NATIVE - if ((long_long_compat) n < 0) { n = (unsigned_long_long_compat)-(long_long_compat)n; F.sign = '-'; } - #else - if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; } - #endif - else if (F.forceSign) F.sign = '+'; - goto number; - - notSigned: if (F.lSize == 1) n = va_arg(arg, unsigned long); - else if (F.lSize == 2) - { - #if TYPE_LONGLONG_NATIVE - n = va_arg(arg, unsigned_long_long_compat); - #else - goto exit; - #endif - } - else n = va_arg(arg, unsigned int); - if (F.hSize) n = (unsigned short) n; - F.sign = 0; - goto number; - - number: if (!F.havePrecision) - { - if (F.zeroPad) - { - F.precision = F.fieldWidth; - if (F.altForm) F.precision -= 2; - if (F.sign) --F.precision; - } - if (F.precision < 1) F.precision = 1; - } - if (F.precision > mDNS_VACB_Size - 1) - F.precision = mDNS_VACB_Size - 1; - for (i = 0; n; n /= base, i++) *--s = (char)(digits[n % base]); - for (; i < F.precision; i++) *--s = '0'; - if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; } - if (F.sign) { *--s = F.sign; i++; } - break; - - case 'a' : { - unsigned char *a = va_arg(arg, unsigned char *); - char pre[4] = ""; - char post[32] = ""; - if (!a) { static char emsg[] = "<>"; s = emsg; i = sizeof(emsg)-1; } - else - { - s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end - if (F.altForm == 1) - { - #if(defined(MDNS_DEBUGMSGS)) - mDNSAddr *ip = (mDNSAddr*)a; - switch (ip->type) - { - case mDNSAddrType_IPv4: F.precision = 4; a = (unsigned char *)&ip->ip.v4; break; - case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break; - default: F.precision = 0; break; - } - #else - F.precision = 0; // mDNSEmbeddedAPI.h not included so no mDNSAddr support - #endif - } - else if (F.altForm == 2) - { - #ifdef AF_INET - const struct sockaddr *sa; - unsigned char *port; - sa = (const struct sockaddr*)a; - switch (sa->sa_family) - { - case AF_INET: F.precision = 4; a = (unsigned char*)&((const struct sockaddr_in *)a)->sin_addr; - port = (unsigned char*)&((const struct sockaddr_in *)sa)->sin_port; - DebugSNPrintF(post, sizeof(post), ":%d", (port[0] << 8) | port[1]); break; - #ifdef AF_INET6 - case AF_INET6: F.precision = 16; a = (unsigned char*)&((const struct sockaddr_in6 *)a)->sin6_addr; - pre[0] = '['; pre[1] = '\0'; - port = (unsigned char*)&((const struct sockaddr_in6 *)sa)->sin6_port; - DebugSNPrintF(post, sizeof(post), "%%%d]:%d", - (int)((const struct sockaddr_in6 *)sa)->sin6_scope_id, - (port[0] << 8) | port[1]); break; - #endif - default: F.precision = 0; break; - } - #else - F.precision = 0; // socket interfaces not included so no sockaddr support - #endif - } - switch (F.precision) - { - case 4: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d%s", - a[0], a[1], a[2], a[3], post); break; - case 6: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X", - a[0], a[1], a[2], a[3], a[4], a[5]); break; - case 8: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", - a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]); break; - case 16: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), - "%s%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X%s", - pre, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], - a[9], a[10], a[11], a[12], a[13], a[14], a[15], post); break; - default: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify address size " - "(i.e. %.4a=IPv4, %.6a=Ethernet, %.8a=Fibre Channel %.16a=IPv6) >>"); break; - } - } - } - break; - - case 'U' : { - unsigned char *a = va_arg(arg, unsigned char *); - if (!a) { static char emsg[] = "<>"; s = emsg; i = sizeof(emsg)-1; } - else - { - s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end - i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", - *((uint32_t*) &a[0]), *((uint16_t*) &a[4]), *((uint16_t*) &a[6]), - a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]); break; - } - } - break; - - case 'c' : *--s = (char)va_arg(arg, int); i = 1; break; - - case 'C' : if (F.lSize) n = va_arg(arg, unsigned long); - else n = va_arg(arg, unsigned int); - if (F.hSize) n = (unsigned short) n; - c = (int)( n & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^'); - c = (int)((n >> 8) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^'); - c = (int)((n >> 16) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^'); - c = (int)((n >> 24) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^'); - i = 4; - break; - - case 's' : s = va_arg(arg, char *); - if (!s) { static char emsg[] = "<>"; s = emsg; i = sizeof(emsg)-1; } - else switch (F.altForm) - { - case 0: i=0; - if (F.havePrecision) // C string - { - while((i < F.precision) && s[i]) i++; - // Make sure we don't truncate in the middle of a UTF-8 character. - // If the last character is part of a multi-byte UTF-8 character, back up to the start of it. - j=0; - while((i > 0) && ((c = s[i-1]) & 0x80)) { j++; i--; if((c & 0xC0) != 0x80) break; } - // If the actual count of UTF-8 characters matches the encoded UTF-8 count, add it back. - if((j > 1) && (j <= 6)) - { - int test = (0xFF << (8-j)) & 0xFF; - int mask = test | (1 << ((8-j)-1)); - if((c & mask) == test) i += j; - } - } - else - while(s[i]) i++; - break; - case 1: i = (unsigned char) *s++; break; // Pascal string - case 2: { // DNS label-sequence name - unsigned char *a = (unsigned char *)s; - s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end - if (*a == 0) *s++ = '.'; // Special case for root DNS name - while (*a) - { - if (*a > 63) { s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "<>", *a); break; } - if (s + *a >= &mDNS_VACB[254]) { s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "<>"); break; } - s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "%#s.", a); - a += 1 + *a; - } - i = (size_t)(s - mDNS_VACB); - s = mDNS_VACB; // Reset s back to the start of the buffer - break; - } - } - if (F.havePrecision && i > F.precision) // Make sure we don't truncate in the middle of a UTF-8 character - { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; } - break; - - case 'S': { // UTF-16 string - unsigned char *a = va_arg(arg, unsigned char *); - uint16_t *u = (uint16_t*)a; - if (!u) { static char emsg[] = "<>"; s = emsg; i = sizeof(emsg)-1; } - if ((!F.havePrecision || F.precision)) - { - if ((a[0] == 0xFE) && (a[1] == 0xFF)) { F.altForm = 1; u += 1; a += 2; F.precision--; } // Big Endian - else if ((a[0] == 0xFF) && (a[1] == 0xFE)) { F.altForm = 2; u += 1; a += 2; F.precision--; } // Little Endian - } - s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end - switch (F.altForm) - { - case 0: while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s)) // Host Endian - { c = u[i]; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; } - break; - case 1: while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s)) // Big Endian - { c = ((a[0] << 8) | a[1]) & 0xFF; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; a += 2; } - break; - case 2: while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s)) // Little Endian - { c = ((a[1] << 8) | a[0]) & 0xFF; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; a += 2; } - break; - } - } - s = mDNS_VACB; // Reset s back to the start of the buffer - break; - - #if TARGET_OS_MAC - case '@': { // Cocoa/CoreFoundation object - CFTypeRef cfObj; - CFStringRef cfStr; - cfObj = (CFTypeRef) va_arg(arg, void *); - cfStr = (CFGetTypeID(cfObj) == CFStringGetTypeID()) ? (CFStringRef)CFRetain(cfObj) : CFCopyDescription(cfObj); - s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end - if (cfStr) - { - CFRange range; - CFIndex m; - range = CFRangeMake(0, CFStringGetLength(cfStr)); - m = 0; - CFStringGetBytes(cfStr, range, kCFStringEncodingUTF8, '^', false, (UInt8*)mDNS_VACB, (CFIndex)sizeof(mDNS_VACB), &m); - CFRelease(cfStr); - i = (size_t) m; - } - else - { - i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%s", "ERROR: " ); - } - } - if (F.havePrecision && i > F.precision) // Make sure we don't truncate in the middle of a UTF-8 character - { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; } - break; - #endif - - case 'm' : { // Error Message - long err; - if (F.lSize) err = va_arg(arg, long); - else err = va_arg(arg, int); - if (F.hSize) err = (short)err; - DebugGetErrorString(err, mDNS_VACB, sizeof(mDNS_VACB)); - s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end - for(i=0;s[i];i++) {} - } - break; - - case 'H' : { // Hex Dump - void *a = va_arg(arg, void *); - size_t size = (size_t)va_arg(arg, int); - size_t max = (size_t)va_arg(arg, int); - DebugFlags flags = - kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoNewLine | - kDebugFlags8BitSeparator | kDebugFlagsNo32BitSeparator | - kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount; - if (F.altForm == 0) flags |= kDebugFlagsNoASCII; - size = (max < size) ? max : size; - s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end - i = DebugHexDump(kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, a, a, size, flags, mDNS_VACB, sizeof(mDNS_VACB)); - } - break; - - case 'v' : { // Version - uint32_t version; - version = va_arg(arg, unsigned int); - DebugNumVersionToString(version, mDNS_VACB); - s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end - for(i=0;s[i];i++) {} - } - break; - - case 'n' : s = va_arg(arg, char *); - if (F.hSize) * (short *) s = (short)nwritten; - else if (F.lSize) * (long *) s = (long)nwritten; - else * (int *) s = (int)nwritten; - continue; - - default: s = mDNS_VACB; - i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "<>", c); - - case '%' : *sbuffer++ = (char)c; - if (++nwritten >= buflen) goto exit; - break; - } - - if (i < F.fieldWidth && !F.leftJustify) // Pad on the left - do { - *sbuffer++ = ' '; - if (++nwritten >= buflen) goto exit; - } while (i < --F.fieldWidth); - - if (i > buflen - nwritten) // Make sure we don't truncate in the middle of a UTF-8 character - { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--; } - for (j=0; j= buflen) goto exit; - - for (; i < F.fieldWidth; i++) // Pad on the right - { - *sbuffer++ = ' '; - if (++nwritten >= buflen) goto exit; - } - } - } - exit: - *sbuffer++ = 0; - return(nwritten); - } +{ + static const struct DebugSNPrintF_format + { + unsigned leftJustify : 1; + unsigned forceSign : 1; + unsigned zeroPad : 1; + unsigned havePrecision : 1; + unsigned hSize : 1; + char lSize; + char altForm; + char sign; // +, - or space + unsigned int fieldWidth; + unsigned int precision; + } DebugSNPrintF_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + size_t nwritten = 0; + int c; + if (buflen == 0) return(0); + buflen--; // Pre-reserve one space in the buffer for the terminating nul + if (buflen == 0) goto exit; + + for (c = *fmt; c != 0; c = *++fmt) + { + if (c != '%') + { + *sbuffer++ = (char)c; + if (++nwritten >= buflen) goto exit; + } + else + { + size_t i=0, j; + // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for + // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc. + // The size needs to be enough for a 256-byte domain name plus some error text. + #define mDNS_VACB_Size 300 + char mDNS_VACB[mDNS_VACB_Size]; + #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size]) + #define mDNS_VACB_Remain(s) ((size_t)(mDNS_VACB_Lim - s)) + char *s = mDNS_VACB_Lim; + const char *digits = "0123456789ABCDEF"; + struct DebugSNPrintF_format F = DebugSNPrintF_format_default; + + for(;;) // decode flags + { + c = *++fmt; + if (c == '-') F.leftJustify = 1; + else if (c == '+') F.forceSign = 1; + else if (c == ' ') F.sign = ' '; + else if (c == '#') F.altForm++; + else if (c == '0') F.zeroPad = 1; + else break; + } + + if (c == '*') // decode field width + { + int f = va_arg(arg, int); + if (f < 0) { f = -f; F.leftJustify = 1; } + F.fieldWidth = (unsigned int)f; + c = *++fmt; + } + else + { + for (; c >= '0' && c <= '9'; c = *++fmt) + F.fieldWidth = (10 * F.fieldWidth) + (c - '0'); + } + + if (c == '.') // decode precision + { + if ((c = *++fmt) == '*') + { F.precision = va_arg(arg, unsigned int); c = *++fmt; } + else for (; c >= '0' && c <= '9'; c = *++fmt) + F.precision = (10 * F.precision) + (c - '0'); + F.havePrecision = 1; + } + + if (F.leftJustify) F.zeroPad = 0; + +conv: + switch (c) // perform appropriate conversion + { + #if TYPE_LONGLONG_NATIVE + unsigned_long_long_compat n; + unsigned_long_long_compat base; + #else + unsigned long n; + unsigned long base; + #endif + case 'h': F.hSize = 1; c = *++fmt; goto conv; + case 'l': // fall through + case 'L': F.lSize++; c = *++fmt; goto conv; + case 'd': + case 'i': base = 10; + goto canBeSigned; + case 'u': base = 10; + goto notSigned; + case 'o': base = 8; + goto notSigned; + case 'b': base = 2; + goto notSigned; + case 'p': n = va_arg(arg, uintptr_t); + F.havePrecision = 1; + F.precision = (sizeof(uintptr_t) == 4) ? 8 : 16; + F.sign = 0; + base = 16; + c = 'x'; + goto number; + case 'x': digits = "0123456789abcdef"; + case 'X': base = 16; + goto notSigned; +canBeSigned: + #if TYPE_LONGLONG_NATIVE + if (F.lSize == 1) n = (unsigned_long_long_compat)va_arg(arg, long); + else if (F.lSize == 2) n = (unsigned_long_long_compat)va_arg(arg, long_long_compat); + else n = (unsigned_long_long_compat)va_arg(arg, int); + #else + if (F.lSize == 1) n = (unsigned long)va_arg(arg, long); + else if (F.lSize == 2) goto exit; + else n = (unsigned long)va_arg(arg, int); + #endif + if (F.hSize) n = (short) n; + #if TYPE_LONGLONG_NATIVE + if ((long_long_compat) n < 0) { n = (unsigned_long_long_compat)-(long_long_compat)n; F.sign = '-'; } + #else + if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; } + #endif + else if (F.forceSign) F.sign = '+'; + goto number; + +notSigned: if (F.lSize == 1) n = va_arg(arg, unsigned long); + else if (F.lSize == 2) + { + #if TYPE_LONGLONG_NATIVE + n = va_arg(arg, unsigned_long_long_compat); + #else + goto exit; + #endif + } + else n = va_arg(arg, unsigned int); + if (F.hSize) n = (unsigned short) n; + F.sign = 0; + goto number; + +number: if (!F.havePrecision) + { + if (F.zeroPad) + { + F.precision = F.fieldWidth; + if (F.altForm) F.precision -= 2; + if (F.sign) --F.precision; + } + if (F.precision < 1) F.precision = 1; + } + if (F.precision > mDNS_VACB_Size - 1) + F.precision = mDNS_VACB_Size - 1; + for (i = 0; n; n /= base, i++) *--s = (char)(digits[n % base]); + for (; i < F.precision; i++) *--s = '0'; + if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; } + if (F.sign) { *--s = F.sign; i++; } + break; + + case 'a': { + unsigned char *a = va_arg(arg, unsigned char *); + char pre[4] = ""; + char post[32] = ""; + if (!a) { static char emsg[] = "<>"; s = emsg; i = sizeof(emsg)-1; } + else + { + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + if (F.altForm == 1) + { + #if (defined(MDNS_DEBUGMSGS)) + mDNSAddr *ip = (mDNSAddr*)a; + switch (ip->type) + { + case mDNSAddrType_IPv4: F.precision = 4; a = (unsigned char *)&ip->ip.v4; break; + case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break; + default: F.precision = 0; break; + } + #else + F.precision = 0; // mDNSEmbeddedAPI.h not included so no mDNSAddr support + #endif + } + else if (F.altForm == 2) + { + #ifdef AF_INET + const struct sockaddr *sa; + unsigned char *port; + sa = (const struct sockaddr*)a; + switch (sa->sa_family) + { + case AF_INET: F.precision = 4; a = (unsigned char*)&((const struct sockaddr_in *)a)->sin_addr; + port = (unsigned char*)&((const struct sockaddr_in *)sa)->sin_port; + DebugSNPrintF(post, sizeof(post), ":%d", (port[0] << 8) | port[1]); break; + #ifdef AF_INET6 + case AF_INET6: F.precision = 16; a = (unsigned char*)&((const struct sockaddr_in6 *)a)->sin6_addr; + pre[0] = '['; pre[1] = '\0'; + port = (unsigned char*)&((const struct sockaddr_in6 *)sa)->sin6_port; + DebugSNPrintF(post, sizeof(post), "%%%d]:%d", + (int)((const struct sockaddr_in6 *)sa)->sin6_scope_id, + (port[0] << 8) | port[1]); break; + #endif + default: F.precision = 0; break; + } + #else + F.precision = 0; // socket interfaces not included so no sockaddr support + #endif + } + switch (F.precision) + { + case 4: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d%s", + a[0], a[1], a[2], a[3], post); break; + case 6: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X", + a[0], a[1], a[2], a[3], a[4], a[5]); break; + case 8: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", + a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]); break; + case 16: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), + "%s%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X%s", + pre, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], + a[9], a[10], a[11], a[12], a[13], a[14], a[15], post); break; + default: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify address size " + "(i.e. %.4a=IPv4, %.6a=Ethernet, %.8a=Fibre Channel %.16a=IPv6) >>"); break; + } + } + } + break; + + case 'U': { + unsigned char *a = va_arg(arg, unsigned char *); + if (!a) { static char emsg[] = "<>"; s = emsg; i = sizeof(emsg)-1; } + else + { + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + *((uint32_t*) &a[0]), *((uint16_t*) &a[4]), *((uint16_t*) &a[6]), + a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]); break; + } + } + break; + + case 'c': *--s = (char)va_arg(arg, int); i = 1; break; + + case 'C': if (F.lSize) n = va_arg(arg, unsigned long); + else n = va_arg(arg, unsigned int); + if (F.hSize) n = (unsigned short) n; + c = (int)( n & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^'); + c = (int)((n >> 8) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^'); + c = (int)((n >> 16) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^'); + c = (int)((n >> 24) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^'); + i = 4; + break; + + case 's': s = va_arg(arg, char *); + if (!s) { static char emsg[] = "<>"; s = emsg; i = sizeof(emsg)-1; } + else switch (F.altForm) + { + case 0: i=0; + if (F.havePrecision) // C string + { + while((i < F.precision) && s[i]) i++; + // Make sure we don't truncate in the middle of a UTF-8 character. + // If the last character is part of a multi-byte UTF-8 character, back up to the start of it. + j=0; + while((i > 0) && ((c = s[i-1]) & 0x80)) { j++; i--; if((c & 0xC0) != 0x80) break;} + // If the actual count of UTF-8 characters matches the encoded UTF-8 count, add it back. + if((j > 1) && (j <= 6)) + { + int test = (0xFF << (8-j)) & 0xFF; + int mask = test | (1 << ((8-j)-1)); + if((c & mask) == test) i += j; + } + } + else + while(s[i]) i++; + break; + case 1: i = (unsigned char) *s++; break; // Pascal string + case 2: { // DNS label-sequence name + unsigned char *a = (unsigned char *)s; + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + if (*a == 0) *s++ = '.'; // Special case for root DNS name + while (*a) + { + if (*a > 63) { s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "<>", *a); break; } + if (s + *a >= &mDNS_VACB[254]) { s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "<>"); break; } + s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "%#s.", a); + a += 1 + *a; + } + i = (size_t)(s - mDNS_VACB); + s = mDNS_VACB; // Reset s back to the start of the buffer + break; + } + } + if (F.havePrecision && i > F.precision) // Make sure we don't truncate in the middle of a UTF-8 character + { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--;} + break; + + case 'S': { // UTF-16 string + unsigned char *a = va_arg(arg, unsigned char *); + uint16_t *u = (uint16_t*)a; + if (!u) { static char emsg[] = "<>"; s = emsg; i = sizeof(emsg)-1; } + if ((!F.havePrecision || F.precision)) + { + if ((a[0] == 0xFE) && (a[1] == 0xFF)) { F.altForm = 1; u += 1; a += 2; F.precision--; } // Big Endian + else if ((a[0] == 0xFF) && (a[1] == 0xFE)) { F.altForm = 2; u += 1; a += 2; F.precision--; } // Little Endian + } + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + switch (F.altForm) + { + case 0: while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s)) // Host Endian + { c = u[i]; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; } + break; + case 1: while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s)) // Big Endian + { c = ((a[0] << 8) | a[1]) & 0xFF; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; a += 2; } + break; + case 2: while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s)) // Little Endian + { c = ((a[1] << 8) | a[0]) & 0xFF; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; a += 2; } + break; + } + } + s = mDNS_VACB; // Reset s back to the start of the buffer + break; + + #if TARGET_OS_MAC + case '@': { // Cocoa/CoreFoundation object + CFTypeRef cfObj; + CFStringRef cfStr; + cfObj = (CFTypeRef) va_arg(arg, void *); + cfStr = (CFGetTypeID(cfObj) == CFStringGetTypeID()) ? (CFStringRef)CFRetain(cfObj) : CFCopyDescription(cfObj); + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + if (cfStr) + { + CFRange range; + CFIndex m; + range = CFRangeMake(0, CFStringGetLength(cfStr)); + m = 0; + CFStringGetBytes(cfStr, range, kCFStringEncodingUTF8, '^', false, (UInt8*)mDNS_VACB, (CFIndex)sizeof(mDNS_VACB), &m); + CFRelease(cfStr); + i = (size_t) m; + } + else + { + i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%s", "ERROR: " ); + } + } + if (F.havePrecision && i > F.precision) // Make sure we don't truncate in the middle of a UTF-8 character + { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--;} + break; + #endif + + case 'm': { // Error Message + long err; + if (F.lSize) err = va_arg(arg, long); + else err = va_arg(arg, int); + if (F.hSize) err = (short)err; + DebugGetErrorString(err, mDNS_VACB, sizeof(mDNS_VACB)); + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + for(i=0; s[i]; i++) {} + } + break; + + case 'H': { // Hex Dump + void *a = va_arg(arg, void *); + size_t size = (size_t)va_arg(arg, int); + size_t max = (size_t)va_arg(arg, int); + DebugFlags flags = + kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoNewLine | + kDebugFlags8BitSeparator | kDebugFlagsNo32BitSeparator | + kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount; + if (F.altForm == 0) flags |= kDebugFlagsNoASCII; + size = (max < size) ? max : size; + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + i = DebugHexDump(kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, a, a, size, flags, mDNS_VACB, sizeof(mDNS_VACB)); + } + break; + + case 'v': { // Version + uint32_t version; + version = va_arg(arg, unsigned int); + DebugNumVersionToString(version, mDNS_VACB); + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + for(i=0; s[i]; i++) {} + } + break; + + case 'n': s = va_arg(arg, char *); + if (F.hSize) *(short *) s = (short)nwritten; + else if (F.lSize) *(long *) s = (long)nwritten; + else *(int *) s = (int)nwritten; + continue; + + default: s = mDNS_VACB; + i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "<>", c); + + case '%': *sbuffer++ = (char)c; + if (++nwritten >= buflen) goto exit; + break; + } + + if (i < F.fieldWidth && !F.leftJustify) // Pad on the left + do { + *sbuffer++ = ' '; + if (++nwritten >= buflen) goto exit; + } while (i < --F.fieldWidth); + + if (i > buflen - nwritten) // Make sure we don't truncate in the middle of a UTF-8 character + { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--;} + for (j=0; j= buflen) goto exit; + + for (; i < F.fieldWidth; i++) // Pad on the right + { + *sbuffer++ = ' '; + if (++nwritten >= buflen) goto exit; + } + } + } +exit: + *sbuffer++ = 0; + return(nwritten); +} //=========================================================================================================================== // DebugGetErrorString //=========================================================================================================================== -DEBUG_EXPORT const char * DebugGetErrorString( int_least32_t inErrorCode, char *inBuffer, size_t inBufferSize ) +DEBUG_EXPORT const char * DebugGetErrorString( int_least32_t inErrorCode, char *inBuffer, size_t inBufferSize ) { - const char * s; - char * dst; - char * end; -#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) - char buffer[ 256 ]; + const char * s; + char * dst; + char * end; +#if ( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) + char buffer[ 256 ]; #endif - - switch( inErrorCode ) - { - #define CaseErrorString( X, STR ) case X: s = STR; break - #define CaseErrorStringify( X ) case X: s = # X; break - #define CaseErrorStringifyHardCode( VALUE, X ) case VALUE: s = # X; break - - // General Errors - - CaseErrorString( 0, "no error" ); - CaseErrorString( 1, "in-progress/waiting" ); - CaseErrorString( -1, "catch-all unknown error" ); - - // ACP Errors - - CaseErrorStringifyHardCode( -2, kACPBadRequestErr ); - CaseErrorStringifyHardCode( -3, kACPNoMemoryErr ); - CaseErrorStringifyHardCode( -4, kACPBadParamErr ); - CaseErrorStringifyHardCode( -5, kACPNotFoundErr ); - CaseErrorStringifyHardCode( -6, kACPBadChecksumErr ); - CaseErrorStringifyHardCode( -7, kACPCommandNotHandledErr ); - CaseErrorStringifyHardCode( -8, kACPNetworkErr ); - CaseErrorStringifyHardCode( -9, kACPDuplicateCommandHandlerErr ); - CaseErrorStringifyHardCode( -10, kACPUnknownPropertyErr ); - CaseErrorStringifyHardCode( -11, kACPImmutablePropertyErr ); - CaseErrorStringifyHardCode( -12, kACPBadPropertyValueErr ); - CaseErrorStringifyHardCode( -13, kACPNoResourcesErr ); - CaseErrorStringifyHardCode( -14, kACPBadOptionErr ); - CaseErrorStringifyHardCode( -15, kACPBadSizeErr ); - CaseErrorStringifyHardCode( -16, kACPBadPasswordErr ); - CaseErrorStringifyHardCode( -17, kACPNotInitializedErr ); - CaseErrorStringifyHardCode( -18, kACPNonReadablePropertyErr ); - CaseErrorStringifyHardCode( -19, kACPBadVersionErr ); - CaseErrorStringifyHardCode( -20, kACPBadSignatureErr ); - CaseErrorStringifyHardCode( -21, kACPBadIndexErr ); - CaseErrorStringifyHardCode( -22, kACPUnsupportedErr ); - CaseErrorStringifyHardCode( -23, kACPInUseErr ); - CaseErrorStringifyHardCode( -24, kACPParamCountErr ); - CaseErrorStringifyHardCode( -25, kACPIDErr ); - CaseErrorStringifyHardCode( -26, kACPFormatErr ); - CaseErrorStringifyHardCode( -27, kACPUnknownUserErr ); - CaseErrorStringifyHardCode( -28, kACPAccessDeniedErr ); - CaseErrorStringifyHardCode( -29, kACPIncorrectFWErr ); - - // Common Services Errors - - CaseErrorStringify( kUnknownErr ); - CaseErrorStringify( kOptionErr ); - CaseErrorStringify( kSelectorErr ); - CaseErrorStringify( kExecutionStateErr ); - CaseErrorStringify( kPathErr ); - CaseErrorStringify( kParamErr ); - CaseErrorStringify( kParamCountErr ); - CaseErrorStringify( kCommandErr ); - CaseErrorStringify( kIDErr ); - CaseErrorStringify( kStateErr ); - CaseErrorStringify( kRangeErr ); - CaseErrorStringify( kRequestErr ); - CaseErrorStringify( kResponseErr ); - CaseErrorStringify( kChecksumErr ); - CaseErrorStringify( kNotHandledErr ); - CaseErrorStringify( kVersionErr ); - CaseErrorStringify( kSignatureErr ); - CaseErrorStringify( kFormatErr ); - CaseErrorStringify( kNotInitializedErr ); - CaseErrorStringify( kAlreadyInitializedErr ); - CaseErrorStringify( kNotInUseErr ); - CaseErrorStringify( kInUseErr ); - CaseErrorStringify( kTimeoutErr ); - CaseErrorStringify( kCanceledErr ); - CaseErrorStringify( kAlreadyCanceledErr ); - CaseErrorStringify( kCannotCancelErr ); - CaseErrorStringify( kDeletedErr ); - CaseErrorStringify( kNotFoundErr ); - CaseErrorStringify( kNoMemoryErr ); - CaseErrorStringify( kNoResourcesErr ); - CaseErrorStringify( kDuplicateErr ); - CaseErrorStringify( kImmutableErr ); - CaseErrorStringify( kUnsupportedDataErr ); - CaseErrorStringify( kIntegrityErr ); - CaseErrorStringify( kIncompatibleErr ); - CaseErrorStringify( kUnsupportedErr ); - CaseErrorStringify( kUnexpectedErr ); - CaseErrorStringify( kValueErr ); - CaseErrorStringify( kNotReadableErr ); - CaseErrorStringify( kNotWritableErr ); - CaseErrorStringify( kBadReferenceErr ); - CaseErrorStringify( kFlagErr ); - CaseErrorStringify( kMalformedErr ); - CaseErrorStringify( kSizeErr ); - CaseErrorStringify( kNameErr ); - CaseErrorStringify( kNotReadyErr ); - CaseErrorStringify( kReadErr ); - CaseErrorStringify( kWriteErr ); - CaseErrorStringify( kMismatchErr ); - CaseErrorStringify( kDateErr ); - CaseErrorStringify( kUnderrunErr ); - CaseErrorStringify( kOverrunErr ); - CaseErrorStringify( kEndingErr ); - CaseErrorStringify( kConnectionErr ); - CaseErrorStringify( kAuthenticationErr ); - CaseErrorStringify( kOpenErr ); - CaseErrorStringify( kTypeErr ); - CaseErrorStringify( kSkipErr ); - CaseErrorStringify( kNoAckErr ); - CaseErrorStringify( kCollisionErr ); - CaseErrorStringify( kBackoffErr ); - CaseErrorStringify( kNoAddressAckErr ); - CaseErrorStringify( kBusyErr ); - CaseErrorStringify( kNoSpaceErr ); - - // mDNS/DNS-SD Errors - - CaseErrorStringifyHardCode( -65537, mStatus_UnknownErr ); - CaseErrorStringifyHardCode( -65538, mStatus_NoSuchNameErr ); - CaseErrorStringifyHardCode( -65539, mStatus_NoMemoryErr ); - CaseErrorStringifyHardCode( -65540, mStatus_BadParamErr ); - CaseErrorStringifyHardCode( -65541, mStatus_BadReferenceErr ); - CaseErrorStringifyHardCode( -65542, mStatus_BadStateErr ); - CaseErrorStringifyHardCode( -65543, mStatus_BadFlagsErr ); - CaseErrorStringifyHardCode( -65544, mStatus_UnsupportedErr ); - CaseErrorStringifyHardCode( -65545, mStatus_NotInitializedErr ); - CaseErrorStringifyHardCode( -65546, mStatus_NoCache ); - CaseErrorStringifyHardCode( -65547, mStatus_AlreadyRegistered ); - CaseErrorStringifyHardCode( -65548, mStatus_NameConflict ); - CaseErrorStringifyHardCode( -65549, mStatus_Invalid ); - CaseErrorStringifyHardCode( -65550, mStatus_GrowCache ); - CaseErrorStringifyHardCode( -65551, mStatus_BadInterfaceErr ); - CaseErrorStringifyHardCode( -65552, mStatus_Incompatible ); - CaseErrorStringifyHardCode( -65791, mStatus_ConfigChanged ); - CaseErrorStringifyHardCode( -65792, mStatus_MemFree ); - - // RSP Errors - - CaseErrorStringifyHardCode( -400000, kRSPUnknownErr ); - CaseErrorStringifyHardCode( -400050, kRSPParamErr ); - CaseErrorStringifyHardCode( -400108, kRSPNoMemoryErr ); - CaseErrorStringifyHardCode( -405246, kRSPRangeErr ); - CaseErrorStringifyHardCode( -409057, kRSPSizeErr ); - CaseErrorStringifyHardCode( -400200, kRSPHardwareErr ); - CaseErrorStringifyHardCode( -401712, kRSPTimeoutErr ); - CaseErrorStringifyHardCode( -402053, kRSPUnsupportedErr ); - CaseErrorStringifyHardCode( -402419, kRSPIDErr ); - CaseErrorStringifyHardCode( -403165, kRSPFlagErr ); - CaseErrorString( -200000, "kRSPControllerStatusBase - 0x50" ); - CaseErrorString( -200080, "kRSPCommandSucceededErr - 0x50" ); - CaseErrorString( -200001, "kRSPCommandFailedErr - 0x01" ); - CaseErrorString( -200051, "kRSPChecksumErr - 0x33" ); - CaseErrorString( -200132, "kRSPCommandTimeoutErr - 0x84" ); - CaseErrorString( -200034, "kRSPPasswordRequiredErr - 0x22 OBSOLETE" ); - CaseErrorString( -200128, "kRSPCanceledErr - 0x02 Async" ); - - // XML Errors - - CaseErrorStringifyHardCode( -100043, kXMLNotFoundErr ); - CaseErrorStringifyHardCode( -100050, kXMLParamErr ); - CaseErrorStringifyHardCode( -100108, kXMLNoMemoryErr ); - CaseErrorStringifyHardCode( -100206, kXMLFormatErr ); - CaseErrorStringifyHardCode( -100586, kXMLNoRootElementErr ); - CaseErrorStringifyHardCode( -101703, kXMLWrongDataTypeErr ); - CaseErrorStringifyHardCode( -101726, kXMLKeyErr ); - CaseErrorStringifyHardCode( -102053, kXMLUnsupportedErr ); - CaseErrorStringifyHardCode( -102063, kXMLMissingElementErr ); - CaseErrorStringifyHardCode( -103026, kXMLParseErr ); - CaseErrorStringifyHardCode( -103159, kXMLBadDataErr ); - CaseErrorStringifyHardCode( -103170, kXMLBadNameErr ); - CaseErrorStringifyHardCode( -105246, kXMLRangeErr ); - CaseErrorStringifyHardCode( -105251, kXMLUnknownElementErr ); - CaseErrorStringifyHardCode( -108739, kXMLMalformedInputErr ); - CaseErrorStringifyHardCode( -109057, kXMLBadSizeErr ); - CaseErrorStringifyHardCode( -101730, kXMLMissingChildElementErr ); - CaseErrorStringifyHardCode( -102107, kXMLMissingParentElementErr ); - CaseErrorStringifyHardCode( -130587, kXMLNonRootElementErr ); - CaseErrorStringifyHardCode( -102015, kXMLDateErr ); - - #if( __MACH__ ) - - // Mach Errors - - CaseErrorStringifyHardCode( 0x00002000, MACH_MSG_IPC_SPACE ); - CaseErrorStringifyHardCode( 0x00001000, MACH_MSG_VM_SPACE ); - CaseErrorStringifyHardCode( 0x00000800, MACH_MSG_IPC_KERNEL ); - CaseErrorStringifyHardCode( 0x00000400, MACH_MSG_VM_KERNEL ); - CaseErrorStringifyHardCode( 0x10000001, MACH_SEND_IN_PROGRESS ); - CaseErrorStringifyHardCode( 0x10000002, MACH_SEND_INVALID_DATA ); - CaseErrorStringifyHardCode( 0x10000003, MACH_SEND_INVALID_DEST ); - CaseErrorStringifyHardCode( 0x10000004, MACH_SEND_TIMED_OUT ); - CaseErrorStringifyHardCode( 0x10000007, MACH_SEND_INTERRUPTED ); - CaseErrorStringifyHardCode( 0x10000008, MACH_SEND_MSG_TOO_SMALL ); - CaseErrorStringifyHardCode( 0x10000009, MACH_SEND_INVALID_REPLY ); - CaseErrorStringifyHardCode( 0x1000000A, MACH_SEND_INVALID_RIGHT ); - CaseErrorStringifyHardCode( 0x1000000B, MACH_SEND_INVALID_NOTIFY ); - CaseErrorStringifyHardCode( 0x1000000C, MACH_SEND_INVALID_MEMORY ); - CaseErrorStringifyHardCode( 0x1000000D, MACH_SEND_NO_BUFFER ); - CaseErrorStringifyHardCode( 0x1000000E, MACH_SEND_TOO_LARGE ); - CaseErrorStringifyHardCode( 0x1000000F, MACH_SEND_INVALID_TYPE ); - CaseErrorStringifyHardCode( 0x10000010, MACH_SEND_INVALID_HEADER ); - CaseErrorStringifyHardCode( 0x10000011, MACH_SEND_INVALID_TRAILER ); - CaseErrorStringifyHardCode( 0x10000015, MACH_SEND_INVALID_RT_OOL_SIZE ); - CaseErrorStringifyHardCode( 0x10004001, MACH_RCV_IN_PROGRESS ); - CaseErrorStringifyHardCode( 0x10004002, MACH_RCV_INVALID_NAME ); - CaseErrorStringifyHardCode( 0x10004003, MACH_RCV_TIMED_OUT ); - CaseErrorStringifyHardCode( 0x10004004, MACH_RCV_TOO_LARGE ); - CaseErrorStringifyHardCode( 0x10004005, MACH_RCV_INTERRUPTED ); - CaseErrorStringifyHardCode( 0x10004006, MACH_RCV_PORT_CHANGED ); - CaseErrorStringifyHardCode( 0x10004007, MACH_RCV_INVALID_NOTIFY ); - CaseErrorStringifyHardCode( 0x10004008, MACH_RCV_INVALID_DATA ); - CaseErrorStringifyHardCode( 0x10004009, MACH_RCV_PORT_DIED ); - CaseErrorStringifyHardCode( 0x1000400A, MACH_RCV_IN_SET ); - CaseErrorStringifyHardCode( 0x1000400B, MACH_RCV_HEADER_ERROR ); - CaseErrorStringifyHardCode( 0x1000400C, MACH_RCV_BODY_ERROR ); - CaseErrorStringifyHardCode( 0x1000400D, MACH_RCV_INVALID_TYPE ); - CaseErrorStringifyHardCode( 0x1000400E, MACH_RCV_SCATTER_SMALL ); - CaseErrorStringifyHardCode( 0x1000400F, MACH_RCV_INVALID_TRAILER ); - CaseErrorStringifyHardCode( 0x10004011, MACH_RCV_IN_PROGRESS_TIMED ); - - // Mach OSReturn Errors - - CaseErrorStringifyHardCode( 0xDC000001, kOSReturnError ); - CaseErrorStringifyHardCode( 0xDC004001, kOSMetaClassInternal ); - CaseErrorStringifyHardCode( 0xDC004002, kOSMetaClassHasInstances ); - CaseErrorStringifyHardCode( 0xDC004003, kOSMetaClassNoInit ); - CaseErrorStringifyHardCode( 0xDC004004, kOSMetaClassNoTempData ); - CaseErrorStringifyHardCode( 0xDC004005, kOSMetaClassNoDicts ); - CaseErrorStringifyHardCode( 0xDC004006, kOSMetaClassNoKModSet ); - CaseErrorStringifyHardCode( 0xDC004007, kOSMetaClassNoInsKModSet ); - CaseErrorStringifyHardCode( 0xDC004008, kOSMetaClassNoSuper ); - CaseErrorStringifyHardCode( 0xDC004009, kOSMetaClassInstNoSuper ); - CaseErrorStringifyHardCode( 0xDC00400A, kOSMetaClassDuplicateClass ); - - // IOKit Errors - - CaseErrorStringifyHardCode( 0xE00002BC, kIOReturnError ); - CaseErrorStringifyHardCode( 0xE00002BD, kIOReturnNoMemory ); - CaseErrorStringifyHardCode( 0xE00002BE, kIOReturnNoResources ); - CaseErrorStringifyHardCode( 0xE00002BF, kIOReturnIPCError ); - CaseErrorStringifyHardCode( 0xE00002C0, kIOReturnNoDevice ); - CaseErrorStringifyHardCode( 0xE00002C1, kIOReturnNotPrivileged ); - CaseErrorStringifyHardCode( 0xE00002C2, kIOReturnBadArgument ); - CaseErrorStringifyHardCode( 0xE00002C3, kIOReturnLockedRead ); - CaseErrorStringifyHardCode( 0xE00002C4, kIOReturnLockedWrite ); - CaseErrorStringifyHardCode( 0xE00002C5, kIOReturnExclusiveAccess ); - CaseErrorStringifyHardCode( 0xE00002C6, kIOReturnBadMessageID ); - CaseErrorStringifyHardCode( 0xE00002C7, kIOReturnUnsupported ); - CaseErrorStringifyHardCode( 0xE00002C8, kIOReturnVMError ); - CaseErrorStringifyHardCode( 0xE00002C9, kIOReturnInternalError ); - CaseErrorStringifyHardCode( 0xE00002CA, kIOReturnIOError ); - CaseErrorStringifyHardCode( 0xE00002CC, kIOReturnCannotLock ); - CaseErrorStringifyHardCode( 0xE00002CD, kIOReturnNotOpen ); - CaseErrorStringifyHardCode( 0xE00002CE, kIOReturnNotReadable ); - CaseErrorStringifyHardCode( 0xE00002CF, kIOReturnNotWritable ); - CaseErrorStringifyHardCode( 0xE00002D0, kIOReturnNotAligned ); - CaseErrorStringifyHardCode( 0xE00002D1, kIOReturnBadMedia ); - CaseErrorStringifyHardCode( 0xE00002D2, kIOReturnStillOpen ); - CaseErrorStringifyHardCode( 0xE00002D3, kIOReturnRLDError ); - CaseErrorStringifyHardCode( 0xE00002D4, kIOReturnDMAError ); - CaseErrorStringifyHardCode( 0xE00002D5, kIOReturnBusy ); - CaseErrorStringifyHardCode( 0xE00002D6, kIOReturnTimeout ); - CaseErrorStringifyHardCode( 0xE00002D7, kIOReturnOffline ); - CaseErrorStringifyHardCode( 0xE00002D8, kIOReturnNotReady ); - CaseErrorStringifyHardCode( 0xE00002D9, kIOReturnNotAttached ); - CaseErrorStringifyHardCode( 0xE00002DA, kIOReturnNoChannels ); - CaseErrorStringifyHardCode( 0xE00002DB, kIOReturnNoSpace ); - CaseErrorStringifyHardCode( 0xE00002DD, kIOReturnPortExists ); - CaseErrorStringifyHardCode( 0xE00002DE, kIOReturnCannotWire ); - CaseErrorStringifyHardCode( 0xE00002DF, kIOReturnNoInterrupt ); - CaseErrorStringifyHardCode( 0xE00002E0, kIOReturnNoFrames ); - CaseErrorStringifyHardCode( 0xE00002E1, kIOReturnMessageTooLarge ); - CaseErrorStringifyHardCode( 0xE00002E2, kIOReturnNotPermitted ); - CaseErrorStringifyHardCode( 0xE00002E3, kIOReturnNoPower ); - CaseErrorStringifyHardCode( 0xE00002E4, kIOReturnNoMedia ); - CaseErrorStringifyHardCode( 0xE00002E5, kIOReturnUnformattedMedia ); - CaseErrorStringifyHardCode( 0xE00002E6, kIOReturnUnsupportedMode ); - CaseErrorStringifyHardCode( 0xE00002E7, kIOReturnUnderrun ); - CaseErrorStringifyHardCode( 0xE00002E8, kIOReturnOverrun ); - CaseErrorStringifyHardCode( 0xE00002E9, kIOReturnDeviceError ); - CaseErrorStringifyHardCode( 0xE00002EA, kIOReturnNoCompletion ); - CaseErrorStringifyHardCode( 0xE00002EB, kIOReturnAborted ); - CaseErrorStringifyHardCode( 0xE00002EC, kIOReturnNoBandwidth ); - CaseErrorStringifyHardCode( 0xE00002ED, kIOReturnNotResponding ); - CaseErrorStringifyHardCode( 0xE00002EE, kIOReturnIsoTooOld ); - CaseErrorStringifyHardCode( 0xE00002EF, kIOReturnIsoTooNew ); - CaseErrorStringifyHardCode( 0xE00002F0, kIOReturnNotFound ); - CaseErrorStringifyHardCode( 0xE0000001, kIOReturnInvalid ); - - // IOKit FireWire Errors - - CaseErrorStringifyHardCode( 0xE0008010, kIOFireWireResponseBase ); - CaseErrorStringifyHardCode( 0xE0008020, kIOFireWireBusReset ); - CaseErrorStringifyHardCode( 0xE0008001, kIOConfigNoEntry ); - CaseErrorStringifyHardCode( 0xE0008002, kIOFireWirePending ); - CaseErrorStringifyHardCode( 0xE0008003, kIOFireWireLastDCLToken ); - CaseErrorStringifyHardCode( 0xE0008004, kIOFireWireConfigROMInvalid ); - CaseErrorStringifyHardCode( 0xE0008005, kIOFireWireAlreadyRegistered ); - CaseErrorStringifyHardCode( 0xE0008006, kIOFireWireMultipleTalkers ); - CaseErrorStringifyHardCode( 0xE0008007, kIOFireWireChannelActive ); - CaseErrorStringifyHardCode( 0xE0008008, kIOFireWireNoListenerOrTalker ); - CaseErrorStringifyHardCode( 0xE0008009, kIOFireWireNoChannels ); - CaseErrorStringifyHardCode( 0xE000800A, kIOFireWireChannelNotAvailable ); - CaseErrorStringifyHardCode( 0xE000800B, kIOFireWireSeparateBus ); - CaseErrorStringifyHardCode( 0xE000800C, kIOFireWireBadSelfIDs ); - CaseErrorStringifyHardCode( 0xE000800D, kIOFireWireLowCableVoltage ); - CaseErrorStringifyHardCode( 0xE000800E, kIOFireWireInsufficientPower ); - CaseErrorStringifyHardCode( 0xE000800F, kIOFireWireOutOfTLabels ); - CaseErrorStringifyHardCode( 0xE0008101, kIOFireWireBogusDCLProgram ); - CaseErrorStringifyHardCode( 0xE0008102, kIOFireWireTalkingAndListening ); - CaseErrorStringifyHardCode( 0xE0008103, kIOFireWireHardwareSlept ); - CaseErrorStringifyHardCode( 0xE00087D0, kIOFWMessageServiceIsRequestingClose ); - CaseErrorStringifyHardCode( 0xE00087D1, kIOFWMessagePowerStateChanged ); - CaseErrorStringifyHardCode( 0xE00087D2, kIOFWMessageTopologyChanged ); - - // IOKit USB Errors - - CaseErrorStringifyHardCode( 0xE0004061, kIOUSBUnknownPipeErr ); - CaseErrorStringifyHardCode( 0xE0004060, kIOUSBTooManyPipesErr ); - CaseErrorStringifyHardCode( 0xE000405F, kIOUSBNoAsyncPortErr ); - CaseErrorStringifyHardCode( 0xE000405E, kIOUSBNotEnoughPipesErr ); - CaseErrorStringifyHardCode( 0xE000405D, kIOUSBNotEnoughPowerErr ); - CaseErrorStringifyHardCode( 0xE0004057, kIOUSBEndpointNotFound ); - CaseErrorStringifyHardCode( 0xE0004056, kIOUSBConfigNotFound ); - CaseErrorStringifyHardCode( 0xE0004051, kIOUSBTransactionTimeout ); - CaseErrorStringifyHardCode( 0xE0004050, kIOUSBTransactionReturned ); - CaseErrorStringifyHardCode( 0xE000404F, kIOUSBPipeStalled ); - CaseErrorStringifyHardCode( 0xE000404E, kIOUSBInterfaceNotFound ); - CaseErrorStringifyHardCode( 0xE000404D, kIOUSBLowLatencyBufferNotPreviouslyAllocated ); - CaseErrorStringifyHardCode( 0xE000404C, kIOUSBLowLatencyFrameListNotPreviouslyAllocated ); - CaseErrorStringifyHardCode( 0xE000404B, kIOUSBHighSpeedSplitError ); - CaseErrorStringifyHardCode( 0xE0004010, kIOUSBLinkErr ); - CaseErrorStringifyHardCode( 0xE000400F, kIOUSBNotSent2Err ); - CaseErrorStringifyHardCode( 0xE000400E, kIOUSBNotSent1Err ); - CaseErrorStringifyHardCode( 0xE000400D, kIOUSBBufferUnderrunErr ); - CaseErrorStringifyHardCode( 0xE000400C, kIOUSBBufferOverrunErr ); - CaseErrorStringifyHardCode( 0xE000400B, kIOUSBReserved2Err ); - CaseErrorStringifyHardCode( 0xE000400A, kIOUSBReserved1Err ); - CaseErrorStringifyHardCode( 0xE0004007, kIOUSBWrongPIDErr ); - CaseErrorStringifyHardCode( 0xE0004006, kIOUSBPIDCheckErr ); - CaseErrorStringifyHardCode( 0xE0004003, kIOUSBDataToggleErr ); - CaseErrorStringifyHardCode( 0xE0004002, kIOUSBBitstufErr ); - CaseErrorStringifyHardCode( 0xE0004001, kIOUSBCRCErr ); - - #endif // __MACH__ - - // Other Errors - - default: - s = NULL; - #if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) - if( inBuffer && ( inBufferSize > 0 ) ) - { - DWORD n; - - n = FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, (DWORD) inErrorCode, - MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), buffer, sizeof( buffer ), NULL ); - if( n > 0 ) - { - // Remove any trailing CR's or LF's since some messages have them. - - while( ( n > 0 ) && isspace( ( (unsigned char *) buffer )[ n - 1 ] ) ) - { - buffer[ --n ] = '\0'; - } - s = buffer; - } - } - #endif - - if( !s ) - { - #if( !TARGET_API_MAC_OSX_KERNEL && !TARGET_OS_WINDOWS_CE ) - s = strerror( inErrorCode ); - #endif - if( !s ) - { - s = ""; - } - } - break; - } - - // Copy the string to the output buffer. If no buffer is supplied or it is empty, return an empty string. - - if( inBuffer && ( inBufferSize > 0 ) ) - { - dst = inBuffer; - end = dst + ( inBufferSize - 1 ); - while( ( ( end - dst ) > 0 ) && ( *s != '\0' ) ) - { - *dst++ = *s++; - } - *dst = '\0'; - s = inBuffer; - } - return( s ); + + switch( inErrorCode ) + { + #define CaseErrorString( X, STR ) case X: s = STR; break + #define CaseErrorStringify( X ) case X: s = # X; break + #define CaseErrorStringifyHardCode( VALUE, X ) case VALUE: s = # X; break + + // General Errors + + CaseErrorString( 0, "no error" ); + CaseErrorString( 1, "in-progress/waiting" ); + CaseErrorString( -1, "catch-all unknown error" ); + + // ACP Errors + + CaseErrorStringifyHardCode( -2, kACPBadRequestErr ); + CaseErrorStringifyHardCode( -3, kACPNoMemoryErr ); + CaseErrorStringifyHardCode( -4, kACPBadParamErr ); + CaseErrorStringifyHardCode( -5, kACPNotFoundErr ); + CaseErrorStringifyHardCode( -6, kACPBadChecksumErr ); + CaseErrorStringifyHardCode( -7, kACPCommandNotHandledErr ); + CaseErrorStringifyHardCode( -8, kACPNetworkErr ); + CaseErrorStringifyHardCode( -9, kACPDuplicateCommandHandlerErr ); + CaseErrorStringifyHardCode( -10, kACPUnknownPropertyErr ); + CaseErrorStringifyHardCode( -11, kACPImmutablePropertyErr ); + CaseErrorStringifyHardCode( -12, kACPBadPropertyValueErr ); + CaseErrorStringifyHardCode( -13, kACPNoResourcesErr ); + CaseErrorStringifyHardCode( -14, kACPBadOptionErr ); + CaseErrorStringifyHardCode( -15, kACPBadSizeErr ); + CaseErrorStringifyHardCode( -16, kACPBadPasswordErr ); + CaseErrorStringifyHardCode( -17, kACPNotInitializedErr ); + CaseErrorStringifyHardCode( -18, kACPNonReadablePropertyErr ); + CaseErrorStringifyHardCode( -19, kACPBadVersionErr ); + CaseErrorStringifyHardCode( -20, kACPBadSignatureErr ); + CaseErrorStringifyHardCode( -21, kACPBadIndexErr ); + CaseErrorStringifyHardCode( -22, kACPUnsupportedErr ); + CaseErrorStringifyHardCode( -23, kACPInUseErr ); + CaseErrorStringifyHardCode( -24, kACPParamCountErr ); + CaseErrorStringifyHardCode( -25, kACPIDErr ); + CaseErrorStringifyHardCode( -26, kACPFormatErr ); + CaseErrorStringifyHardCode( -27, kACPUnknownUserErr ); + CaseErrorStringifyHardCode( -28, kACPAccessDeniedErr ); + CaseErrorStringifyHardCode( -29, kACPIncorrectFWErr ); + + // Common Services Errors + + CaseErrorStringify( kUnknownErr ); + CaseErrorStringify( kOptionErr ); + CaseErrorStringify( kSelectorErr ); + CaseErrorStringify( kExecutionStateErr ); + CaseErrorStringify( kPathErr ); + CaseErrorStringify( kParamErr ); + CaseErrorStringify( kParamCountErr ); + CaseErrorStringify( kCommandErr ); + CaseErrorStringify( kIDErr ); + CaseErrorStringify( kStateErr ); + CaseErrorStringify( kRangeErr ); + CaseErrorStringify( kRequestErr ); + CaseErrorStringify( kResponseErr ); + CaseErrorStringify( kChecksumErr ); + CaseErrorStringify( kNotHandledErr ); + CaseErrorStringify( kVersionErr ); + CaseErrorStringify( kSignatureErr ); + CaseErrorStringify( kFormatErr ); + CaseErrorStringify( kNotInitializedErr ); + CaseErrorStringify( kAlreadyInitializedErr ); + CaseErrorStringify( kNotInUseErr ); + CaseErrorStringify( kInUseErr ); + CaseErrorStringify( kTimeoutErr ); + CaseErrorStringify( kCanceledErr ); + CaseErrorStringify( kAlreadyCanceledErr ); + CaseErrorStringify( kCannotCancelErr ); + CaseErrorStringify( kDeletedErr ); + CaseErrorStringify( kNotFoundErr ); + CaseErrorStringify( kNoMemoryErr ); + CaseErrorStringify( kNoResourcesErr ); + CaseErrorStringify( kDuplicateErr ); + CaseErrorStringify( kImmutableErr ); + CaseErrorStringify( kUnsupportedDataErr ); + CaseErrorStringify( kIntegrityErr ); + CaseErrorStringify( kIncompatibleErr ); + CaseErrorStringify( kUnsupportedErr ); + CaseErrorStringify( kUnexpectedErr ); + CaseErrorStringify( kValueErr ); + CaseErrorStringify( kNotReadableErr ); + CaseErrorStringify( kNotWritableErr ); + CaseErrorStringify( kBadReferenceErr ); + CaseErrorStringify( kFlagErr ); + CaseErrorStringify( kMalformedErr ); + CaseErrorStringify( kSizeErr ); + CaseErrorStringify( kNameErr ); + CaseErrorStringify( kNotReadyErr ); + CaseErrorStringify( kReadErr ); + CaseErrorStringify( kWriteErr ); + CaseErrorStringify( kMismatchErr ); + CaseErrorStringify( kDateErr ); + CaseErrorStringify( kUnderrunErr ); + CaseErrorStringify( kOverrunErr ); + CaseErrorStringify( kEndingErr ); + CaseErrorStringify( kConnectionErr ); + CaseErrorStringify( kAuthenticationErr ); + CaseErrorStringify( kOpenErr ); + CaseErrorStringify( kTypeErr ); + CaseErrorStringify( kSkipErr ); + CaseErrorStringify( kNoAckErr ); + CaseErrorStringify( kCollisionErr ); + CaseErrorStringify( kBackoffErr ); + CaseErrorStringify( kNoAddressAckErr ); + CaseErrorStringify( kBusyErr ); + CaseErrorStringify( kNoSpaceErr ); + + // mDNS/DNS-SD Errors + + CaseErrorStringifyHardCode( -65537, mStatus_UnknownErr ); + CaseErrorStringifyHardCode( -65538, mStatus_NoSuchNameErr ); + CaseErrorStringifyHardCode( -65539, mStatus_NoMemoryErr ); + CaseErrorStringifyHardCode( -65540, mStatus_BadParamErr ); + CaseErrorStringifyHardCode( -65541, mStatus_BadReferenceErr ); + CaseErrorStringifyHardCode( -65542, mStatus_BadStateErr ); + CaseErrorStringifyHardCode( -65543, mStatus_BadFlagsErr ); + CaseErrorStringifyHardCode( -65544, mStatus_UnsupportedErr ); + CaseErrorStringifyHardCode( -65545, mStatus_NotInitializedErr ); + CaseErrorStringifyHardCode( -65546, mStatus_NoCache ); + CaseErrorStringifyHardCode( -65547, mStatus_AlreadyRegistered ); + CaseErrorStringifyHardCode( -65548, mStatus_NameConflict ); + CaseErrorStringifyHardCode( -65549, mStatus_Invalid ); + CaseErrorStringifyHardCode( -65550, mStatus_GrowCache ); + CaseErrorStringifyHardCode( -65551, mStatus_BadInterfaceErr ); + CaseErrorStringifyHardCode( -65552, mStatus_Incompatible ); + CaseErrorStringifyHardCode( -65791, mStatus_ConfigChanged ); + CaseErrorStringifyHardCode( -65792, mStatus_MemFree ); + + // RSP Errors + + CaseErrorStringifyHardCode( -400000, kRSPUnknownErr ); + CaseErrorStringifyHardCode( -400050, kRSPParamErr ); + CaseErrorStringifyHardCode( -400108, kRSPNoMemoryErr ); + CaseErrorStringifyHardCode( -405246, kRSPRangeErr ); + CaseErrorStringifyHardCode( -409057, kRSPSizeErr ); + CaseErrorStringifyHardCode( -400200, kRSPHardwareErr ); + CaseErrorStringifyHardCode( -401712, kRSPTimeoutErr ); + CaseErrorStringifyHardCode( -402053, kRSPUnsupportedErr ); + CaseErrorStringifyHardCode( -402419, kRSPIDErr ); + CaseErrorStringifyHardCode( -403165, kRSPFlagErr ); + CaseErrorString( -200000, "kRSPControllerStatusBase - 0x50" ); + CaseErrorString( -200080, "kRSPCommandSucceededErr - 0x50" ); + CaseErrorString( -200001, "kRSPCommandFailedErr - 0x01" ); + CaseErrorString( -200051, "kRSPChecksumErr - 0x33" ); + CaseErrorString( -200132, "kRSPCommandTimeoutErr - 0x84" ); + CaseErrorString( -200034, "kRSPPasswordRequiredErr - 0x22 OBSOLETE" ); + CaseErrorString( -200128, "kRSPCanceledErr - 0x02 Async" ); + + // XML Errors + + CaseErrorStringifyHardCode( -100043, kXMLNotFoundErr ); + CaseErrorStringifyHardCode( -100050, kXMLParamErr ); + CaseErrorStringifyHardCode( -100108, kXMLNoMemoryErr ); + CaseErrorStringifyHardCode( -100206, kXMLFormatErr ); + CaseErrorStringifyHardCode( -100586, kXMLNoRootElementErr ); + CaseErrorStringifyHardCode( -101703, kXMLWrongDataTypeErr ); + CaseErrorStringifyHardCode( -101726, kXMLKeyErr ); + CaseErrorStringifyHardCode( -102053, kXMLUnsupportedErr ); + CaseErrorStringifyHardCode( -102063, kXMLMissingElementErr ); + CaseErrorStringifyHardCode( -103026, kXMLParseErr ); + CaseErrorStringifyHardCode( -103159, kXMLBadDataErr ); + CaseErrorStringifyHardCode( -103170, kXMLBadNameErr ); + CaseErrorStringifyHardCode( -105246, kXMLRangeErr ); + CaseErrorStringifyHardCode( -105251, kXMLUnknownElementErr ); + CaseErrorStringifyHardCode( -108739, kXMLMalformedInputErr ); + CaseErrorStringifyHardCode( -109057, kXMLBadSizeErr ); + CaseErrorStringifyHardCode( -101730, kXMLMissingChildElementErr ); + CaseErrorStringifyHardCode( -102107, kXMLMissingParentElementErr ); + CaseErrorStringifyHardCode( -130587, kXMLNonRootElementErr ); + CaseErrorStringifyHardCode( -102015, kXMLDateErr ); + + #if ( __MACH__ ) + + // Mach Errors + + CaseErrorStringifyHardCode( 0x00002000, MACH_MSG_IPC_SPACE ); + CaseErrorStringifyHardCode( 0x00001000, MACH_MSG_VM_SPACE ); + CaseErrorStringifyHardCode( 0x00000800, MACH_MSG_IPC_KERNEL ); + CaseErrorStringifyHardCode( 0x00000400, MACH_MSG_VM_KERNEL ); + CaseErrorStringifyHardCode( 0x10000001, MACH_SEND_IN_PROGRESS ); + CaseErrorStringifyHardCode( 0x10000002, MACH_SEND_INVALID_DATA ); + CaseErrorStringifyHardCode( 0x10000003, MACH_SEND_INVALID_DEST ); + CaseErrorStringifyHardCode( 0x10000004, MACH_SEND_TIMED_OUT ); + CaseErrorStringifyHardCode( 0x10000007, MACH_SEND_INTERRUPTED ); + CaseErrorStringifyHardCode( 0x10000008, MACH_SEND_MSG_TOO_SMALL ); + CaseErrorStringifyHardCode( 0x10000009, MACH_SEND_INVALID_REPLY ); + CaseErrorStringifyHardCode( 0x1000000A, MACH_SEND_INVALID_RIGHT ); + CaseErrorStringifyHardCode( 0x1000000B, MACH_SEND_INVALID_NOTIFY ); + CaseErrorStringifyHardCode( 0x1000000C, MACH_SEND_INVALID_MEMORY ); + CaseErrorStringifyHardCode( 0x1000000D, MACH_SEND_NO_BUFFER ); + CaseErrorStringifyHardCode( 0x1000000E, MACH_SEND_TOO_LARGE ); + CaseErrorStringifyHardCode( 0x1000000F, MACH_SEND_INVALID_TYPE ); + CaseErrorStringifyHardCode( 0x10000010, MACH_SEND_INVALID_HEADER ); + CaseErrorStringifyHardCode( 0x10000011, MACH_SEND_INVALID_TRAILER ); + CaseErrorStringifyHardCode( 0x10000015, MACH_SEND_INVALID_RT_OOL_SIZE ); + CaseErrorStringifyHardCode( 0x10004001, MACH_RCV_IN_PROGRESS ); + CaseErrorStringifyHardCode( 0x10004002, MACH_RCV_INVALID_NAME ); + CaseErrorStringifyHardCode( 0x10004003, MACH_RCV_TIMED_OUT ); + CaseErrorStringifyHardCode( 0x10004004, MACH_RCV_TOO_LARGE ); + CaseErrorStringifyHardCode( 0x10004005, MACH_RCV_INTERRUPTED ); + CaseErrorStringifyHardCode( 0x10004006, MACH_RCV_PORT_CHANGED ); + CaseErrorStringifyHardCode( 0x10004007, MACH_RCV_INVALID_NOTIFY ); + CaseErrorStringifyHardCode( 0x10004008, MACH_RCV_INVALID_DATA ); + CaseErrorStringifyHardCode( 0x10004009, MACH_RCV_PORT_DIED ); + CaseErrorStringifyHardCode( 0x1000400A, MACH_RCV_IN_SET ); + CaseErrorStringifyHardCode( 0x1000400B, MACH_RCV_HEADER_ERROR ); + CaseErrorStringifyHardCode( 0x1000400C, MACH_RCV_BODY_ERROR ); + CaseErrorStringifyHardCode( 0x1000400D, MACH_RCV_INVALID_TYPE ); + CaseErrorStringifyHardCode( 0x1000400E, MACH_RCV_SCATTER_SMALL ); + CaseErrorStringifyHardCode( 0x1000400F, MACH_RCV_INVALID_TRAILER ); + CaseErrorStringifyHardCode( 0x10004011, MACH_RCV_IN_PROGRESS_TIMED ); + + // Mach OSReturn Errors + + CaseErrorStringifyHardCode( 0xDC000001, kOSReturnError ); + CaseErrorStringifyHardCode( 0xDC004001, kOSMetaClassInternal ); + CaseErrorStringifyHardCode( 0xDC004002, kOSMetaClassHasInstances ); + CaseErrorStringifyHardCode( 0xDC004003, kOSMetaClassNoInit ); + CaseErrorStringifyHardCode( 0xDC004004, kOSMetaClassNoTempData ); + CaseErrorStringifyHardCode( 0xDC004005, kOSMetaClassNoDicts ); + CaseErrorStringifyHardCode( 0xDC004006, kOSMetaClassNoKModSet ); + CaseErrorStringifyHardCode( 0xDC004007, kOSMetaClassNoInsKModSet ); + CaseErrorStringifyHardCode( 0xDC004008, kOSMetaClassNoSuper ); + CaseErrorStringifyHardCode( 0xDC004009, kOSMetaClassInstNoSuper ); + CaseErrorStringifyHardCode( 0xDC00400A, kOSMetaClassDuplicateClass ); + + // IOKit Errors + + CaseErrorStringifyHardCode( 0xE00002BC, kIOReturnError ); + CaseErrorStringifyHardCode( 0xE00002BD, kIOReturnNoMemory ); + CaseErrorStringifyHardCode( 0xE00002BE, kIOReturnNoResources ); + CaseErrorStringifyHardCode( 0xE00002BF, kIOReturnIPCError ); + CaseErrorStringifyHardCode( 0xE00002C0, kIOReturnNoDevice ); + CaseErrorStringifyHardCode( 0xE00002C1, kIOReturnNotPrivileged ); + CaseErrorStringifyHardCode( 0xE00002C2, kIOReturnBadArgument ); + CaseErrorStringifyHardCode( 0xE00002C3, kIOReturnLockedRead ); + CaseErrorStringifyHardCode( 0xE00002C4, kIOReturnLockedWrite ); + CaseErrorStringifyHardCode( 0xE00002C5, kIOReturnExclusiveAccess ); + CaseErrorStringifyHardCode( 0xE00002C6, kIOReturnBadMessageID ); + CaseErrorStringifyHardCode( 0xE00002C7, kIOReturnUnsupported ); + CaseErrorStringifyHardCode( 0xE00002C8, kIOReturnVMError ); + CaseErrorStringifyHardCode( 0xE00002C9, kIOReturnInternalError ); + CaseErrorStringifyHardCode( 0xE00002CA, kIOReturnIOError ); + CaseErrorStringifyHardCode( 0xE00002CC, kIOReturnCannotLock ); + CaseErrorStringifyHardCode( 0xE00002CD, kIOReturnNotOpen ); + CaseErrorStringifyHardCode( 0xE00002CE, kIOReturnNotReadable ); + CaseErrorStringifyHardCode( 0xE00002CF, kIOReturnNotWritable ); + CaseErrorStringifyHardCode( 0xE00002D0, kIOReturnNotAligned ); + CaseErrorStringifyHardCode( 0xE00002D1, kIOReturnBadMedia ); + CaseErrorStringifyHardCode( 0xE00002D2, kIOReturnStillOpen ); + CaseErrorStringifyHardCode( 0xE00002D3, kIOReturnRLDError ); + CaseErrorStringifyHardCode( 0xE00002D4, kIOReturnDMAError ); + CaseErrorStringifyHardCode( 0xE00002D5, kIOReturnBusy ); + CaseErrorStringifyHardCode( 0xE00002D6, kIOReturnTimeout ); + CaseErrorStringifyHardCode( 0xE00002D7, kIOReturnOffline ); + CaseErrorStringifyHardCode( 0xE00002D8, kIOReturnNotReady ); + CaseErrorStringifyHardCode( 0xE00002D9, kIOReturnNotAttached ); + CaseErrorStringifyHardCode( 0xE00002DA, kIOReturnNoChannels ); + CaseErrorStringifyHardCode( 0xE00002DB, kIOReturnNoSpace ); + CaseErrorStringifyHardCode( 0xE00002DD, kIOReturnPortExists ); + CaseErrorStringifyHardCode( 0xE00002DE, kIOReturnCannotWire ); + CaseErrorStringifyHardCode( 0xE00002DF, kIOReturnNoInterrupt ); + CaseErrorStringifyHardCode( 0xE00002E0, kIOReturnNoFrames ); + CaseErrorStringifyHardCode( 0xE00002E1, kIOReturnMessageTooLarge ); + CaseErrorStringifyHardCode( 0xE00002E2, kIOReturnNotPermitted ); + CaseErrorStringifyHardCode( 0xE00002E3, kIOReturnNoPower ); + CaseErrorStringifyHardCode( 0xE00002E4, kIOReturnNoMedia ); + CaseErrorStringifyHardCode( 0xE00002E5, kIOReturnUnformattedMedia ); + CaseErrorStringifyHardCode( 0xE00002E6, kIOReturnUnsupportedMode ); + CaseErrorStringifyHardCode( 0xE00002E7, kIOReturnUnderrun ); + CaseErrorStringifyHardCode( 0xE00002E8, kIOReturnOverrun ); + CaseErrorStringifyHardCode( 0xE00002E9, kIOReturnDeviceError ); + CaseErrorStringifyHardCode( 0xE00002EA, kIOReturnNoCompletion ); + CaseErrorStringifyHardCode( 0xE00002EB, kIOReturnAborted ); + CaseErrorStringifyHardCode( 0xE00002EC, kIOReturnNoBandwidth ); + CaseErrorStringifyHardCode( 0xE00002ED, kIOReturnNotResponding ); + CaseErrorStringifyHardCode( 0xE00002EE, kIOReturnIsoTooOld ); + CaseErrorStringifyHardCode( 0xE00002EF, kIOReturnIsoTooNew ); + CaseErrorStringifyHardCode( 0xE00002F0, kIOReturnNotFound ); + CaseErrorStringifyHardCode( 0xE0000001, kIOReturnInvalid ); + + // IOKit FireWire Errors + + CaseErrorStringifyHardCode( 0xE0008010, kIOFireWireResponseBase ); + CaseErrorStringifyHardCode( 0xE0008020, kIOFireWireBusReset ); + CaseErrorStringifyHardCode( 0xE0008001, kIOConfigNoEntry ); + CaseErrorStringifyHardCode( 0xE0008002, kIOFireWirePending ); + CaseErrorStringifyHardCode( 0xE0008003, kIOFireWireLastDCLToken ); + CaseErrorStringifyHardCode( 0xE0008004, kIOFireWireConfigROMInvalid ); + CaseErrorStringifyHardCode( 0xE0008005, kIOFireWireAlreadyRegistered ); + CaseErrorStringifyHardCode( 0xE0008006, kIOFireWireMultipleTalkers ); + CaseErrorStringifyHardCode( 0xE0008007, kIOFireWireChannelActive ); + CaseErrorStringifyHardCode( 0xE0008008, kIOFireWireNoListenerOrTalker ); + CaseErrorStringifyHardCode( 0xE0008009, kIOFireWireNoChannels ); + CaseErrorStringifyHardCode( 0xE000800A, kIOFireWireChannelNotAvailable ); + CaseErrorStringifyHardCode( 0xE000800B, kIOFireWireSeparateBus ); + CaseErrorStringifyHardCode( 0xE000800C, kIOFireWireBadSelfIDs ); + CaseErrorStringifyHardCode( 0xE000800D, kIOFireWireLowCableVoltage ); + CaseErrorStringifyHardCode( 0xE000800E, kIOFireWireInsufficientPower ); + CaseErrorStringifyHardCode( 0xE000800F, kIOFireWireOutOfTLabels ); + CaseErrorStringifyHardCode( 0xE0008101, kIOFireWireBogusDCLProgram ); + CaseErrorStringifyHardCode( 0xE0008102, kIOFireWireTalkingAndListening ); + CaseErrorStringifyHardCode( 0xE0008103, kIOFireWireHardwareSlept ); + CaseErrorStringifyHardCode( 0xE00087D0, kIOFWMessageServiceIsRequestingClose ); + CaseErrorStringifyHardCode( 0xE00087D1, kIOFWMessagePowerStateChanged ); + CaseErrorStringifyHardCode( 0xE00087D2, kIOFWMessageTopologyChanged ); + + // IOKit USB Errors + + CaseErrorStringifyHardCode( 0xE0004061, kIOUSBUnknownPipeErr ); + CaseErrorStringifyHardCode( 0xE0004060, kIOUSBTooManyPipesErr ); + CaseErrorStringifyHardCode( 0xE000405F, kIOUSBNoAsyncPortErr ); + CaseErrorStringifyHardCode( 0xE000405E, kIOUSBNotEnoughPipesErr ); + CaseErrorStringifyHardCode( 0xE000405D, kIOUSBNotEnoughPowerErr ); + CaseErrorStringifyHardCode( 0xE0004057, kIOUSBEndpointNotFound ); + CaseErrorStringifyHardCode( 0xE0004056, kIOUSBConfigNotFound ); + CaseErrorStringifyHardCode( 0xE0004051, kIOUSBTransactionTimeout ); + CaseErrorStringifyHardCode( 0xE0004050, kIOUSBTransactionReturned ); + CaseErrorStringifyHardCode( 0xE000404F, kIOUSBPipeStalled ); + CaseErrorStringifyHardCode( 0xE000404E, kIOUSBInterfaceNotFound ); + CaseErrorStringifyHardCode( 0xE000404D, kIOUSBLowLatencyBufferNotPreviouslyAllocated ); + CaseErrorStringifyHardCode( 0xE000404C, kIOUSBLowLatencyFrameListNotPreviouslyAllocated ); + CaseErrorStringifyHardCode( 0xE000404B, kIOUSBHighSpeedSplitError ); + CaseErrorStringifyHardCode( 0xE0004010, kIOUSBLinkErr ); + CaseErrorStringifyHardCode( 0xE000400F, kIOUSBNotSent2Err ); + CaseErrorStringifyHardCode( 0xE000400E, kIOUSBNotSent1Err ); + CaseErrorStringifyHardCode( 0xE000400D, kIOUSBBufferUnderrunErr ); + CaseErrorStringifyHardCode( 0xE000400C, kIOUSBBufferOverrunErr ); + CaseErrorStringifyHardCode( 0xE000400B, kIOUSBReserved2Err ); + CaseErrorStringifyHardCode( 0xE000400A, kIOUSBReserved1Err ); + CaseErrorStringifyHardCode( 0xE0004007, kIOUSBWrongPIDErr ); + CaseErrorStringifyHardCode( 0xE0004006, kIOUSBPIDCheckErr ); + CaseErrorStringifyHardCode( 0xE0004003, kIOUSBDataToggleErr ); + CaseErrorStringifyHardCode( 0xE0004002, kIOUSBBitstufErr ); + CaseErrorStringifyHardCode( 0xE0004001, kIOUSBCRCErr ); + + #endif // __MACH__ + + // Other Errors + + default: + s = NULL; + #if ( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) + if( inBuffer && ( inBufferSize > 0 ) ) + { + DWORD n; + + n = FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, (DWORD) inErrorCode, + MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), buffer, sizeof( buffer ), NULL ); + if( n > 0 ) + { + // Remove any trailing CR's or LF's since some messages have them. + + while( ( n > 0 ) && isspace( ( (unsigned char *) buffer )[ n - 1 ] ) ) + { + buffer[ --n ] = '\0'; + } + s = buffer; + } + } + #endif + + if( !s ) + { + #if ( !TARGET_API_MAC_OSX_KERNEL && !TARGET_OS_WINDOWS_CE ) + s = strerror( inErrorCode ); + #endif + if( !s ) + { + s = ""; + } + } + break; + } + + // Copy the string to the output buffer. If no buffer is supplied or it is empty, return an empty string. + + if( inBuffer && ( inBufferSize > 0 ) ) + { + dst = inBuffer; + end = dst + ( inBufferSize - 1 ); + while( ( ( end - dst ) > 0 ) && ( *s != '\0' ) ) + { + *dst++ = *s++; + } + *dst = '\0'; + s = inBuffer; + } + return( s ); } //=========================================================================================================================== @@ -2019,576 +2019,576 @@ DEBUG_EXPORT const char * DebugGetErrorString( int_least32_t inErrorCode, char * //=========================================================================================================================== DEBUG_EXPORT size_t - DebugHexDump( - DebugLevel inLevel, - int inIndent, - const char * inLabel, - size_t inLabelSize, - int inLabelMinWidth, - const char * inType, - size_t inTypeSize, - const void * inDataStart, - const void * inData, - size_t inDataSize, - DebugFlags inFlags, - char * outBuffer, - size_t inBufferSize ) +DebugHexDump( + DebugLevel inLevel, + int inIndent, + const char * inLabel, + size_t inLabelSize, + int inLabelMinWidth, + const char * inType, + size_t inTypeSize, + const void * inDataStart, + const void * inData, + size_t inDataSize, + DebugFlags inFlags, + char * outBuffer, + size_t inBufferSize ) { - static const char kHexChars[] = "0123456789ABCDEF"; - const uint8_t * start; - const uint8_t * src; - char * dst; - char * end; - size_t n; - int offset; - int width; - const char * newline; - char separator[ 8 ]; - char * s; - - DEBUG_UNUSED( inType ); - DEBUG_UNUSED( inTypeSize ); - - // Set up the function-wide variables. - - if( inLabelSize == kSizeCString ) - { - inLabelSize = strlen( inLabel ); - } - start = (const uint8_t *) inData; - src = start; - dst = outBuffer; - end = dst + inBufferSize; - offset = (int)( (intptr_t) inData - (intptr_t) inDataStart ); - width = ( (int) inLabelSize > inLabelMinWidth ) ? (int) inLabelSize : inLabelMinWidth; - newline = ( inFlags & kDebugFlagsNoNewLine ) ? "" : "\n"; - - // Set up the separator string. This is used to insert spaces on subsequent "lines" when not using newlines. - - s = separator; - if( inFlags & kDebugFlagsNoNewLine ) - { - if( inFlags & kDebugFlags8BitSeparator ) - { - *s++ = ' '; - } - if( inFlags & kDebugFlags16BitSeparator ) - { - *s++ = ' '; - } - if( !( inFlags & kDebugFlagsNo32BitSeparator ) ) - { - *s++ = ' '; - } - check( ( (size_t)( s - separator ) ) < sizeof( separator ) ); - } - *s = '\0'; - - for( ;; ) - { - char prefixString[ 32 ]; - char hexString[ 64 ]; - char asciiString[ 32 ]; - char byteCountString[ 32 ]; - int c; - size_t chunkSize; - size_t i; - - // If this is a label-only item (i.e. no data), print the label (accounting for prefix string spacing) and exit. - - if( inDataSize == 0 ) - { - if( inLabel && ( inLabelSize > 0 ) ) - { - width = 0; - if( !( inFlags & kDebugFlagsNoAddress ) ) - { - width += 8; // "00000000" - if( !( inFlags & kDebugFlagsNoOffset ) ) - { - width += 1; // "+" - } - } - if( inFlags & kDebugFlags32BitOffset ) - { - width += 8; // "00000000" - } - else if( !( inFlags & kDebugFlagsNoOffset ) ) - { - width += 4; // "0000" - } - - if( outBuffer ) - { - dst += DebugSNPrintF( dst, (size_t)( end - dst ), "%*s" "%-*.*s" "%.*s" "%s", - width, "", - ( width > 0 ) ? ": " : "", - width, (int) inLabelSize, inLabel, - newline ); - } - else - { - dst += DebugPrintF( inLevel, "%*s" "%-*.*s" "%.*s" "%s", - width, "", - ( width > 0 ) ? ": " : "", - width, (int) inLabelSize, inLabel, - newline ); - } - } - break; - } - - // Build the prefix string. It will be in one of the following formats: - // - // 1) "00000000+0000[0000]" (address and offset) - // 2) "00000000" (address only) - // 3) "0000[0000]" (offset only) - // 4) "" (no address or offset) - // - // Note: If we're printing multiple "lines", but not printing newlines, a space is used to separate. - - s = prefixString; - if( !( inFlags & kDebugFlagsNoAddress ) ) - { - *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 28 ) & 0xF ]; - *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 24 ) & 0xF ]; - *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 20 ) & 0xF ]; - *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 16 ) & 0xF ]; - *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 12 ) & 0xF ]; - *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 8 ) & 0xF ]; - *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 4 ) & 0xF ]; - *s++ = kHexChars[ ( (uintptr_t) src ) & 0xF ]; - - if( !( inFlags & kDebugFlagsNoOffset ) ) - { - *s++ = '+'; - } - } - if( !( inFlags & kDebugFlagsNoOffset ) ) - { - if( inFlags & kDebugFlags32BitOffset ) - { - *s++ = kHexChars[ ( offset >> 28 ) & 0xF ]; - *s++ = kHexChars[ ( offset >> 24 ) & 0xF ]; - *s++ = kHexChars[ ( offset >> 20 ) & 0xF ]; - *s++ = kHexChars[ ( offset >> 16 ) & 0xF ]; - } - *s++ = kHexChars[ ( offset >> 12 ) & 0xF ]; - *s++ = kHexChars[ ( offset >> 8 ) & 0xF ]; - *s++ = kHexChars[ ( offset >> 4 ) & 0xF ]; - *s++ = kHexChars[ offset & 0xF ]; - } - if( s != prefixString ) - { - *s++ = ':'; - *s++ = ' '; - } - check( ( (size_t)( s - prefixString ) ) < sizeof( prefixString ) ); - *s = '\0'; - - // Build a hex string with a optional spaces after every 1, 2, and/or 4 bytes to make it easier to read. - // Optionally pads the hex string with space to fill the full 16 byte range (so it lines up). - - s = hexString; - chunkSize = ( inDataSize < 16 ) ? inDataSize : 16; - n = ( inFlags & kDebugFlagsNo16ByteHexPad ) ? chunkSize : 16; - for( i = 0; i < n; ++i ) - { - if( ( inFlags & kDebugFlags8BitSeparator ) && ( i > 0 ) ) - { - *s++ = ' '; - } - if( ( inFlags & kDebugFlags16BitSeparator ) && ( i > 0 ) && ( ( i % 2 ) == 0 ) ) - { - *s++ = ' '; - } - if( !( inFlags & kDebugFlagsNo32BitSeparator ) && ( i > 0 ) && ( ( i % 4 ) == 0 ) ) - { - *s++ = ' '; - } - if( i < chunkSize ) - { - *s++ = kHexChars[ src[ i ] >> 4 ]; - *s++ = kHexChars[ src[ i ] & 0xF ]; - } - else - { - *s++ = ' '; - *s++ = ' '; - } - } - check( ( (size_t)( s - hexString ) ) < sizeof( hexString ) ); - *s = '\0'; - - // Build a string with the ASCII version of the data (replaces non-printable characters with '^'). - // Optionally pads the string with '`' to fill the full 16 byte range (so it lines up). - - s = asciiString; - if( !( inFlags & kDebugFlagsNoASCII ) ) - { - *s++ = ' '; - *s++ = '|'; - for( i = 0; i < n; ++i ) - { - if( i < chunkSize ) - { - c = src[ i ]; - if( !DebugIsPrint( c ) ) - { - c = '^'; - } - } - else - { - c = '`'; - } - *s++ = (char) c; - } - *s++ = '|'; - check( ( (size_t)( s - asciiString ) ) < sizeof( asciiString ) ); - } - *s = '\0'; - - // Build a string indicating how bytes are in the hex dump. Only printed on the first line. - - s = byteCountString; - if( !( inFlags & kDebugFlagsNoByteCount ) ) - { - if( src == start ) - { - s += DebugSNPrintF( s, sizeof( byteCountString ), " (%d bytes)", (int) inDataSize ); - } - } - check( ( (size_t)( s - byteCountString ) ) < sizeof( byteCountString ) ); - *s = '\0'; - - // Build the entire line from all the pieces we've previously built. - - if( outBuffer ) - { - if( src == start ) - { - dst += DebugSNPrintF( dst, (size_t)( end - dst ), - "%*s" // Indention - "%s" // Separator (only if needed) - "%s" // Prefix - "%-*.*s" // Label - "%s" // Separator - "%s" // Hex - "%s" // ASCII - "%s" // Byte Count - "%s", // Newline - inIndent, "", - ( src != start ) ? separator : "", - prefixString, - width, (int) inLabelSize, inLabel ? inLabel : "", - ( width > 0 ) ? " " : "", - hexString, - asciiString, - byteCountString, - newline ); - } - else - { - dst += DebugSNPrintF( dst, (size_t)( end - dst ), - "%*s" // Indention - "%s" // Separator (only if needed) - "%s" // Prefix - "%*s" // Label Spacing - "%s" // Separator - "%s" // Hex - "%s" // ASCII - "%s" // Byte Count - "%s", // Newline - inIndent, "", - ( src != start ) ? separator : "", - prefixString, - width, "", - ( width > 0 ) ? " " : "", - hexString, - asciiString, - byteCountString, - newline ); - } - } - else - { - if( src == start ) - { - dst += DebugPrintF( inLevel, - "%*s" // Indention - "%s" // Separator (only if needed) - "%s" // Prefix - "%-*.*s" // Label - "%s" // Separator - "%s" // Hex - "%s" // ASCII - "%s" // Byte Count - "%s", // Newline - inIndent, "", - ( src != start ) ? separator : "", - prefixString, - width, (int) inLabelSize, inLabel, - ( width > 0 ) ? " " : "", - hexString, - asciiString, - byteCountString, - newline ); - } - else - { - dst += DebugPrintF( inLevel, - "%*s" // Indention - "%s" // Separator (only if needed) - "%s" // Prefix - "%*s" // Label Spacing - "%s" // Separator - "%s" // Hex - "%s" // ASCII - "%s" // Byte Count - "%s", // Newline - inIndent, "", - ( src != start ) ? separator : "", - prefixString, - width, "", - ( width > 0 ) ? " " : "", - hexString, - asciiString, - byteCountString, - newline ); - } - } - - // Move to the next chunk. Exit if there is no more data. - - offset += (int) chunkSize; - src += chunkSize; - inDataSize -= chunkSize; - if( inDataSize == 0 ) - { - break; - } - } - - // Note: The "dst - outBuffer" size calculation works even if "outBuffer" is NULL because it's all relative. - - return( (size_t)( dst - outBuffer ) ); + static const char kHexChars[] = "0123456789ABCDEF"; + const uint8_t * start; + const uint8_t * src; + char * dst; + char * end; + size_t n; + int offset; + int width; + const char * newline; + char separator[ 8 ]; + char * s; + + DEBUG_UNUSED( inType ); + DEBUG_UNUSED( inTypeSize ); + + // Set up the function-wide variables. + + if( inLabelSize == kSizeCString ) + { + inLabelSize = strlen( inLabel ); + } + start = (const uint8_t *) inData; + src = start; + dst = outBuffer; + end = dst + inBufferSize; + offset = (int)( (intptr_t) inData - (intptr_t) inDataStart ); + width = ( (int) inLabelSize > inLabelMinWidth ) ? (int) inLabelSize : inLabelMinWidth; + newline = ( inFlags & kDebugFlagsNoNewLine ) ? "" : "\n"; + + // Set up the separator string. This is used to insert spaces on subsequent "lines" when not using newlines. + + s = separator; + if( inFlags & kDebugFlagsNoNewLine ) + { + if( inFlags & kDebugFlags8BitSeparator ) + { + *s++ = ' '; + } + if( inFlags & kDebugFlags16BitSeparator ) + { + *s++ = ' '; + } + if( !( inFlags & kDebugFlagsNo32BitSeparator ) ) + { + *s++ = ' '; + } + check( ( (size_t)( s - separator ) ) < sizeof( separator ) ); + } + *s = '\0'; + + for( ;; ) + { + char prefixString[ 32 ]; + char hexString[ 64 ]; + char asciiString[ 32 ]; + char byteCountString[ 32 ]; + int c; + size_t chunkSize; + size_t i; + + // If this is a label-only item (i.e. no data), print the label (accounting for prefix string spacing) and exit. + + if( inDataSize == 0 ) + { + if( inLabel && ( inLabelSize > 0 ) ) + { + width = 0; + if( !( inFlags & kDebugFlagsNoAddress ) ) + { + width += 8; // "00000000" + if( !( inFlags & kDebugFlagsNoOffset ) ) + { + width += 1; // "+" + } + } + if( inFlags & kDebugFlags32BitOffset ) + { + width += 8; // "00000000" + } + else if( !( inFlags & kDebugFlagsNoOffset ) ) + { + width += 4; // "0000" + } + + if( outBuffer ) + { + dst += DebugSNPrintF( dst, (size_t)( end - dst ), "%*s" "%-*.*s" "%.*s" "%s", + width, "", + ( width > 0 ) ? ": " : "", + width, (int) inLabelSize, inLabel, + newline ); + } + else + { + dst += DebugPrintF( inLevel, "%*s" "%-*.*s" "%.*s" "%s", + width, "", + ( width > 0 ) ? ": " : "", + width, (int) inLabelSize, inLabel, + newline ); + } + } + break; + } + + // Build the prefix string. It will be in one of the following formats: + // + // 1) "00000000+0000[0000]" (address and offset) + // 2) "00000000" (address only) + // 3) "0000[0000]" (offset only) + // 4) "" (no address or offset) + // + // Note: If we're printing multiple "lines", but not printing newlines, a space is used to separate. + + s = prefixString; + if( !( inFlags & kDebugFlagsNoAddress ) ) + { + *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 28 ) & 0xF ]; + *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 24 ) & 0xF ]; + *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 20 ) & 0xF ]; + *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 16 ) & 0xF ]; + *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 12 ) & 0xF ]; + *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 8 ) & 0xF ]; + *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 4 ) & 0xF ]; + *s++ = kHexChars[ ( (uintptr_t) src ) & 0xF ]; + + if( !( inFlags & kDebugFlagsNoOffset ) ) + { + *s++ = '+'; + } + } + if( !( inFlags & kDebugFlagsNoOffset ) ) + { + if( inFlags & kDebugFlags32BitOffset ) + { + *s++ = kHexChars[ ( offset >> 28 ) & 0xF ]; + *s++ = kHexChars[ ( offset >> 24 ) & 0xF ]; + *s++ = kHexChars[ ( offset >> 20 ) & 0xF ]; + *s++ = kHexChars[ ( offset >> 16 ) & 0xF ]; + } + *s++ = kHexChars[ ( offset >> 12 ) & 0xF ]; + *s++ = kHexChars[ ( offset >> 8 ) & 0xF ]; + *s++ = kHexChars[ ( offset >> 4 ) & 0xF ]; + *s++ = kHexChars[ offset & 0xF ]; + } + if( s != prefixString ) + { + *s++ = ':'; + *s++ = ' '; + } + check( ( (size_t)( s - prefixString ) ) < sizeof( prefixString ) ); + *s = '\0'; + + // Build a hex string with a optional spaces after every 1, 2, and/or 4 bytes to make it easier to read. + // Optionally pads the hex string with space to fill the full 16 byte range (so it lines up). + + s = hexString; + chunkSize = ( inDataSize < 16 ) ? inDataSize : 16; + n = ( inFlags & kDebugFlagsNo16ByteHexPad ) ? chunkSize : 16; + for( i = 0; i < n; ++i ) + { + if( ( inFlags & kDebugFlags8BitSeparator ) && ( i > 0 ) ) + { + *s++ = ' '; + } + if( ( inFlags & kDebugFlags16BitSeparator ) && ( i > 0 ) && ( ( i % 2 ) == 0 ) ) + { + *s++ = ' '; + } + if( !( inFlags & kDebugFlagsNo32BitSeparator ) && ( i > 0 ) && ( ( i % 4 ) == 0 ) ) + { + *s++ = ' '; + } + if( i < chunkSize ) + { + *s++ = kHexChars[ src[ i ] >> 4 ]; + *s++ = kHexChars[ src[ i ] & 0xF ]; + } + else + { + *s++ = ' '; + *s++ = ' '; + } + } + check( ( (size_t)( s - hexString ) ) < sizeof( hexString ) ); + *s = '\0'; + + // Build a string with the ASCII version of the data (replaces non-printable characters with '^'). + // Optionally pads the string with '`' to fill the full 16 byte range (so it lines up). + + s = asciiString; + if( !( inFlags & kDebugFlagsNoASCII ) ) + { + *s++ = ' '; + *s++ = '|'; + for( i = 0; i < n; ++i ) + { + if( i < chunkSize ) + { + c = src[ i ]; + if( !DebugIsPrint( c ) ) + { + c = '^'; + } + } + else + { + c = '`'; + } + *s++ = (char) c; + } + *s++ = '|'; + check( ( (size_t)( s - asciiString ) ) < sizeof( asciiString ) ); + } + *s = '\0'; + + // Build a string indicating how bytes are in the hex dump. Only printed on the first line. + + s = byteCountString; + if( !( inFlags & kDebugFlagsNoByteCount ) ) + { + if( src == start ) + { + s += DebugSNPrintF( s, sizeof( byteCountString ), " (%d bytes)", (int) inDataSize ); + } + } + check( ( (size_t)( s - byteCountString ) ) < sizeof( byteCountString ) ); + *s = '\0'; + + // Build the entire line from all the pieces we've previously built. + + if( outBuffer ) + { + if( src == start ) + { + dst += DebugSNPrintF( dst, (size_t)( end - dst ), + "%*s" // Indention + "%s" // Separator (only if needed) + "%s" // Prefix + "%-*.*s" // Label + "%s" // Separator + "%s" // Hex + "%s" // ASCII + "%s" // Byte Count + "%s", // Newline + inIndent, "", + ( src != start ) ? separator : "", + prefixString, + width, (int) inLabelSize, inLabel ? inLabel : "", + ( width > 0 ) ? " " : "", + hexString, + asciiString, + byteCountString, + newline ); + } + else + { + dst += DebugSNPrintF( dst, (size_t)( end - dst ), + "%*s" // Indention + "%s" // Separator (only if needed) + "%s" // Prefix + "%*s" // Label Spacing + "%s" // Separator + "%s" // Hex + "%s" // ASCII + "%s" // Byte Count + "%s", // Newline + inIndent, "", + ( src != start ) ? separator : "", + prefixString, + width, "", + ( width > 0 ) ? " " : "", + hexString, + asciiString, + byteCountString, + newline ); + } + } + else + { + if( src == start ) + { + dst += DebugPrintF( inLevel, + "%*s" // Indention + "%s" // Separator (only if needed) + "%s" // Prefix + "%-*.*s" // Label + "%s" // Separator + "%s" // Hex + "%s" // ASCII + "%s" // Byte Count + "%s", // Newline + inIndent, "", + ( src != start ) ? separator : "", + prefixString, + width, (int) inLabelSize, inLabel, + ( width > 0 ) ? " " : "", + hexString, + asciiString, + byteCountString, + newline ); + } + else + { + dst += DebugPrintF( inLevel, + "%*s" // Indention + "%s" // Separator (only if needed) + "%s" // Prefix + "%*s" // Label Spacing + "%s" // Separator + "%s" // Hex + "%s" // ASCII + "%s" // Byte Count + "%s", // Newline + inIndent, "", + ( src != start ) ? separator : "", + prefixString, + width, "", + ( width > 0 ) ? " " : "", + hexString, + asciiString, + byteCountString, + newline ); + } + } + + // Move to the next chunk. Exit if there is no more data. + + offset += (int) chunkSize; + src += chunkSize; + inDataSize -= chunkSize; + if( inDataSize == 0 ) + { + break; + } + } + + // Note: The "dst - outBuffer" size calculation works even if "outBuffer" is NULL because it's all relative. + + return( (size_t)( dst - outBuffer ) ); } //=========================================================================================================================== // DebugNumVersionToString //=========================================================================================================================== -static char * DebugNumVersionToString( uint32_t inVersion, char *inString ) +static char * DebugNumVersionToString( uint32_t inVersion, char *inString ) { - char * s; - uint8_t majorRev; - uint8_t minor; - uint8_t bugFix; - uint8_t stage; - uint8_t revision; - - check( inString ); - - majorRev = (uint8_t)( ( inVersion >> 24 ) & 0xFF ); - minor = (uint8_t)( ( inVersion >> 20 ) & 0x0F ); - bugFix = (uint8_t)( ( inVersion >> 16 ) & 0x0F ); - stage = (uint8_t)( ( inVersion >> 8 ) & 0xFF ); - revision = (uint8_t)( inVersion & 0xFF ); - - // Convert the major, minor, and bugfix numbers. - - s = inString; - s += sprintf( s, "%u", majorRev ); - s += sprintf( s, ".%u", minor ); - if( bugFix != 0 ) - { - s += sprintf( s, ".%u", bugFix ); - } - - // Convert the version stage and non-release revision number. - - switch( stage ) - { - case kVersionStageDevelopment: - s += sprintf( s, "d%u", revision ); - break; - - case kVersionStageAlpha: - s += sprintf( s, "a%u", revision ); - break; - - case kVersionStageBeta: - s += sprintf( s, "b%u", revision ); - break; - - case kVersionStageFinal: - - // A non-release revision of zero is a special case indicating the software is GM (at the golden master - // stage) and therefore, the non-release revision should not be added to the string. - - if( revision != 0 ) - { - s += sprintf( s, "f%u", revision ); - } - break; - - default: - dlog( kDebugLevelError, "invalid NumVersion stage (0x%02X)\n", stage ); - break; - } - return( inString ); + char * s; + uint8_t majorRev; + uint8_t minor; + uint8_t bugFix; + uint8_t stage; + uint8_t revision; + + check( inString ); + + majorRev = (uint8_t)( ( inVersion >> 24 ) & 0xFF ); + minor = (uint8_t)( ( inVersion >> 20 ) & 0x0F ); + bugFix = (uint8_t)( ( inVersion >> 16 ) & 0x0F ); + stage = (uint8_t)( ( inVersion >> 8 ) & 0xFF ); + revision = (uint8_t)( inVersion & 0xFF ); + + // Convert the major, minor, and bugfix numbers. + + s = inString; + s += sprintf( s, "%u", majorRev ); + s += sprintf( s, ".%u", minor ); + if( bugFix != 0 ) + { + s += sprintf( s, ".%u", bugFix ); + } + + // Convert the version stage and non-release revision number. + + switch( stage ) + { + case kVersionStageDevelopment: + s += sprintf( s, "d%u", revision ); + break; + + case kVersionStageAlpha: + s += sprintf( s, "a%u", revision ); + break; + + case kVersionStageBeta: + s += sprintf( s, "b%u", revision ); + break; + + case kVersionStageFinal: + + // A non-release revision of zero is a special case indicating the software is GM (at the golden master + // stage) and therefore, the non-release revision should not be added to the string. + + if( revision != 0 ) + { + s += sprintf( s, "f%u", revision ); + } + break; + + default: + dlog( kDebugLevelError, "invalid NumVersion stage (0x%02X)\n", stage ); + break; + } + return( inString ); } //=========================================================================================================================== // DebugTaskLevel //=========================================================================================================================== -DEBUG_EXPORT uint32_t DebugTaskLevel( void ) +DEBUG_EXPORT uint32_t DebugTaskLevel( void ) { - uint32_t level; - - level = 0; - -#if( TARGET_OS_VXWORKS ) - if( intContext() ) - { - level |= ( ( 1 << kDebugInterruptLevelShift ) & kDebugInterruptLevelMask ); - } + uint32_t level; + + level = 0; + +#if ( TARGET_OS_VXWORKS ) + if( intContext() ) + { + level |= ( ( 1 << kDebugInterruptLevelShift ) & kDebugInterruptLevelMask ); + } #endif - - return( level ); + + return( level ); } -#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) +#if ( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) //=========================================================================================================================== // DebugWinEnableConsole //=========================================================================================================================== #pragma warning( disable:4311 ) -static void DebugWinEnableConsole( void ) +static void DebugWinEnableConsole( void ) { - static bool sConsoleEnabled = false; - BOOL result; - int fileHandle; - FILE * file; - int err; - - if( sConsoleEnabled ) - { - goto exit; - } - - // Create console window. - - result = AllocConsole(); - require_quiet( result, exit ); - - // Redirect stdin to the console stdin. - - fileHandle = _open_osfhandle( (long) GetStdHandle( STD_INPUT_HANDLE ), _O_TEXT ); - - #if( defined( __MWERKS__ ) ) - file = __handle_reopen( (unsigned long) fileHandle, "r", stdin ); - require_quiet( file, exit ); - #else - file = _fdopen( fileHandle, "r" ); - require_quiet( file, exit ); - - *stdin = *file; - #endif - - err = setvbuf( stdin, NULL, _IONBF, 0 ); - require_noerr_quiet( err, exit ); - - // Redirect stdout to the console stdout. - - fileHandle = _open_osfhandle( (long) GetStdHandle( STD_OUTPUT_HANDLE ), _O_TEXT ); - - #if( defined( __MWERKS__ ) ) - file = __handle_reopen( (unsigned long) fileHandle, "w", stdout ); - require_quiet( file, exit ); - #else - file = _fdopen( fileHandle, "w" ); - require_quiet( file, exit ); - - *stdout = *file; - #endif - - err = setvbuf( stdout, NULL, _IONBF, 0 ); - require_noerr_quiet( err, exit ); - - // Redirect stderr to the console stdout. - - fileHandle = _open_osfhandle( (long) GetStdHandle( STD_OUTPUT_HANDLE ), _O_TEXT ); - - #if( defined( __MWERKS__ ) ) - file = __handle_reopen( (unsigned long) fileHandle, "w", stderr ); - require_quiet( file, exit ); - #else - file = _fdopen( fileHandle, "w" ); - require_quiet( file, exit ); - - *stderr = *file; - #endif - - err = setvbuf( stderr, NULL, _IONBF, 0 ); - require_noerr_quiet( err, exit ); - - sConsoleEnabled = true; - + static bool sConsoleEnabled = false; + BOOL result; + int fileHandle; + FILE * file; + int err; + + if( sConsoleEnabled ) + { + goto exit; + } + + // Create console window. + + result = AllocConsole(); + require_quiet( result, exit ); + + // Redirect stdin to the console stdin. + + fileHandle = _open_osfhandle( (long) GetStdHandle( STD_INPUT_HANDLE ), _O_TEXT ); + + #if ( defined( __MWERKS__ ) ) + file = __handle_reopen( (unsigned long) fileHandle, "r", stdin ); + require_quiet( file, exit ); + #else + file = _fdopen( fileHandle, "r" ); + require_quiet( file, exit ); + + *stdin = *file; + #endif + + err = setvbuf( stdin, NULL, _IONBF, 0 ); + require_noerr_quiet( err, exit ); + + // Redirect stdout to the console stdout. + + fileHandle = _open_osfhandle( (long) GetStdHandle( STD_OUTPUT_HANDLE ), _O_TEXT ); + + #if ( defined( __MWERKS__ ) ) + file = __handle_reopen( (unsigned long) fileHandle, "w", stdout ); + require_quiet( file, exit ); + #else + file = _fdopen( fileHandle, "w" ); + require_quiet( file, exit ); + + *stdout = *file; + #endif + + err = setvbuf( stdout, NULL, _IONBF, 0 ); + require_noerr_quiet( err, exit ); + + // Redirect stderr to the console stdout. + + fileHandle = _open_osfhandle( (long) GetStdHandle( STD_OUTPUT_HANDLE ), _O_TEXT ); + + #if ( defined( __MWERKS__ ) ) + file = __handle_reopen( (unsigned long) fileHandle, "w", stderr ); + require_quiet( file, exit ); + #else + file = _fdopen( fileHandle, "w" ); + require_quiet( file, exit ); + + *stderr = *file; + #endif + + err = setvbuf( stderr, NULL, _IONBF, 0 ); + require_noerr_quiet( err, exit ); + + sConsoleEnabled = true; + exit: - return; + return; } #pragma warning( default:4311 ) -#endif // TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE +#endif // TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE -#if( TARGET_OS_WIN32 ) +#if ( TARGET_OS_WIN32 ) //=========================================================================================================================== // DebugWinCharToTCharString //=========================================================================================================================== static TCHAR * - DebugWinCharToTCharString( - const char * inCharString, - size_t inCharCount, - TCHAR * outTCharString, - size_t inTCharCountMax, - size_t * outTCharCount ) +DebugWinCharToTCharString( + const char * inCharString, + size_t inCharCount, + TCHAR * outTCharString, + size_t inTCharCountMax, + size_t * outTCharCount ) { - const char * src; - TCHAR * dst; - TCHAR * end; - - if( inCharCount == kSizeCString ) - { - inCharCount = strlen( inCharString ); - } - src = inCharString; - dst = outTCharString; - if( inTCharCountMax > 0 ) - { - inTCharCountMax -= 1; - if( inTCharCountMax > inCharCount ) - { - inTCharCountMax = inCharCount; - } - - end = dst + inTCharCountMax; - while( dst < end ) - { - *dst++ = (TCHAR) *src++; - } - *dst = 0; - } - if( outTCharCount ) - { - *outTCharCount = (size_t)( dst - outTCharString ); - } - return( outTCharString ); + const char * src; + TCHAR * dst; + TCHAR * end; + + if( inCharCount == kSizeCString ) + { + inCharCount = strlen( inCharString ); + } + src = inCharString; + dst = outTCharString; + if( inTCharCountMax > 0 ) + { + inTCharCountMax -= 1; + if( inTCharCountMax > inCharCount ) + { + inTCharCountMax = inCharCount; + } + + end = dst + inTCharCountMax; + while( dst < end ) + { + *dst++ = (TCHAR) *src++; + } + *dst = 0; + } + if( outTCharCount ) + { + *outTCharCount = (size_t)( dst - outTCharString ); + } + return( outTCharString ); } #endif @@ -2601,475 +2601,475 @@ static TCHAR * // DebugServicesTest //=========================================================================================================================== -DEBUG_EXPORT OSStatus DebugServicesTest( void ) +DEBUG_EXPORT OSStatus DebugServicesTest( void ) { - OSStatus err; - char s[ 512 ]; - uint8_t * p; - uint8_t data[] = - { - 0x11, 0x22, 0x33, 0x44, - 0x55, 0x66, - 0x77, 0x88, 0x99, 0xAA, - 0xBB, 0xCC, 0xDD, - 0xEE, - 0xFF, - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, - 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, - 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x81, 0x91, 0xA1 - }; - - debug_initialize( kDebugOutputTypeMetaConsole ); - - // check's - - check( 0 && "SHOULD SEE: check" ); - check( 1 && "SHOULD *NOT* SEE: check (valid)" ); - check_string( 0, "SHOULD SEE: check_string" ); - check_string( 1, "SHOULD *NOT* SEE: check_string (valid)" ); - check_noerr( -123 ); - check_noerr( 10038 ); - check_noerr( 22 ); - check_noerr( 0 ); - check_noerr_string( -6712, "SHOULD SEE: check_noerr_string" ); - check_noerr_string( 0, "SHOULD *NOT* SEE: check_noerr_string (valid)" ); - check_translated_errno( 0 >= 0 && "SHOULD *NOT* SEE", -384, -999 ); - check_translated_errno( -1 >= 0 && "SHOULD SEE", -384, -999 ); - check_translated_errno( -1 >= 0 && "SHOULD SEE", 0, -999 ); - check_ptr_overlap( "SHOULD *NOT* SEE" ? 10 : 0, 10, 22, 10 ); - check_ptr_overlap( "SHOULD SEE" ? 10 : 0, 10, 5, 10 ); - check_ptr_overlap( "SHOULD SEE" ? 10 : 0, 10, 12, 6 ); - check_ptr_overlap( "SHOULD SEE" ? 12 : 0, 6, 10, 10 ); - check_ptr_overlap( "SHOULD SEE" ? 12 : 0, 10, 10, 10 ); - check_ptr_overlap( "SHOULD *NOT* SEE" ? 22 : 0, 10, 10, 10 ); - check_ptr_overlap( "SHOULD *NOT* SEE" ? 10 : 0, 10, 20, 10 ); - check_ptr_overlap( "SHOULD *NOT* SEE" ? 20 : 0, 10, 10, 10 ); - - // require's - - require( 0 && "SHOULD SEE", require1 ); - { err = kResponseErr; goto exit; } + OSStatus err; + char s[ 512 ]; + uint8_t * p; + uint8_t data[] = + { + 0x11, 0x22, 0x33, 0x44, + 0x55, 0x66, + 0x77, 0x88, 0x99, 0xAA, + 0xBB, 0xCC, 0xDD, + 0xEE, + 0xFF, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, + 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, + 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x81, 0x91, 0xA1 + }; + + debug_initialize( kDebugOutputTypeMetaConsole ); + + // check's + + check( 0 && "SHOULD SEE: check" ); + check( 1 && "SHOULD *NOT* SEE: check (valid)" ); + check_string( 0, "SHOULD SEE: check_string" ); + check_string( 1, "SHOULD *NOT* SEE: check_string (valid)" ); + check_noerr( -123 ); + check_noerr( 10038 ); + check_noerr( 22 ); + check_noerr( 0 ); + check_noerr_string( -6712, "SHOULD SEE: check_noerr_string" ); + check_noerr_string( 0, "SHOULD *NOT* SEE: check_noerr_string (valid)" ); + check_translated_errno( 0 >= 0 && "SHOULD *NOT* SEE", -384, -999 ); + check_translated_errno( -1 >= 0 && "SHOULD SEE", -384, -999 ); + check_translated_errno( -1 >= 0 && "SHOULD SEE", 0, -999 ); + check_ptr_overlap( "SHOULD *NOT* SEE" ? 10 : 0, 10, 22, 10 ); + check_ptr_overlap( "SHOULD SEE" ? 10 : 0, 10, 5, 10 ); + check_ptr_overlap( "SHOULD SEE" ? 10 : 0, 10, 12, 6 ); + check_ptr_overlap( "SHOULD SEE" ? 12 : 0, 6, 10, 10 ); + check_ptr_overlap( "SHOULD SEE" ? 12 : 0, 10, 10, 10 ); + check_ptr_overlap( "SHOULD *NOT* SEE" ? 22 : 0, 10, 10, 10 ); + check_ptr_overlap( "SHOULD *NOT* SEE" ? 10 : 0, 10, 20, 10 ); + check_ptr_overlap( "SHOULD *NOT* SEE" ? 20 : 0, 10, 10, 10 ); + + // require's + + require( 0 && "SHOULD SEE", require1 ); + { err = kResponseErr; goto exit; } require1: - require( 1 && "SHOULD *NOT* SEE", require2 ); - goto require2Good; + require( 1 && "SHOULD *NOT* SEE", require2 ); + goto require2Good; require2: - { err = kResponseErr; goto exit; } + { err = kResponseErr; goto exit; } require2Good: - require_string( 0 && "SHOULD SEE", require3, "SHOULD SEE: require_string" ); - { err = kResponseErr; goto exit; } + require_string( 0 && "SHOULD SEE", require3, "SHOULD SEE: require_string" ); + { err = kResponseErr; goto exit; } require3: - require_string( 1 && "SHOULD *NOT* SEE", require4, "SHOULD *NOT* SEE: require_string (valid)" ); - goto require4Good; + require_string( 1 && "SHOULD *NOT* SEE", require4, "SHOULD *NOT* SEE: require_string (valid)" ); + goto require4Good; require4: - { err = kResponseErr; goto exit; } + { err = kResponseErr; goto exit; } require4Good: - require_quiet( 0 && "SHOULD SEE", require5 ); - { err = kResponseErr; goto exit; } + require_quiet( 0 && "SHOULD SEE", require5 ); + { err = kResponseErr; goto exit; } require5: - require_quiet( 1 && "SHOULD *NOT* SEE", require6 ); - goto require6Good; + require_quiet( 1 && "SHOULD *NOT* SEE", require6 ); + goto require6Good; require6: - { err = kResponseErr; goto exit; } + { err = kResponseErr; goto exit; } require6Good: - require_noerr( -1, require7 ); - { err = kResponseErr; goto exit; } + require_noerr( -1, require7 ); + { err = kResponseErr; goto exit; } require7: - require_noerr( 0, require8 ); - goto require8Good; + require_noerr( 0, require8 ); + goto require8Good; require8: - { err = kResponseErr; goto exit; } + { err = kResponseErr; goto exit; } require8Good: - require_noerr_string( -2, require9, "SHOULD SEE: require_noerr_string"); - { err = kResponseErr; goto exit; } + require_noerr_string( -2, require9, "SHOULD SEE: require_noerr_string"); + { err = kResponseErr; goto exit; } require9: - require_noerr_string( 0, require10, "SHOULD *NOT* SEE: require_noerr_string (valid)" ); - goto require10Good; + require_noerr_string( 0, require10, "SHOULD *NOT* SEE: require_noerr_string (valid)" ); + goto require10Good; require10: - { err = kResponseErr; goto exit; } + { err = kResponseErr; goto exit; } require10Good: - require_noerr_action_string( -3, require11, dlog( kDebugLevelMax, "action 1 (expected)\n" ), "require_noerr_action_string" ); - { err = kResponseErr; goto exit; } + require_noerr_action_string( -3, require11, dlog( kDebugLevelMax, "action 1 (expected)\n" ), "require_noerr_action_string" ); + { err = kResponseErr; goto exit; } require11: - require_noerr_action_string( 0, require12, dlog( kDebugLevelMax, "action 2\n" ), "require_noerr_action_string (valid)" ); - goto require12Good; + require_noerr_action_string( 0, require12, dlog( kDebugLevelMax, "action 2\n" ), "require_noerr_action_string (valid)" ); + goto require12Good; require12: - { err = kResponseErr; goto exit; } + { err = kResponseErr; goto exit; } require12Good: - require_noerr_quiet( -4, require13 ); - { err = kResponseErr; goto exit; } + require_noerr_quiet( -4, require13 ); + { err = kResponseErr; goto exit; } require13: - require_noerr_quiet( 0, require14 ); - goto require14Good; + require_noerr_quiet( 0, require14 ); + goto require14Good; require14: - { err = kResponseErr; goto exit; } + { err = kResponseErr; goto exit; } require14Good: - require_noerr_action( -5, require15, dlog( kDebugLevelMax, "SHOULD SEE: action 3 (expected)\n" ) ); - { err = kResponseErr; goto exit; } + require_noerr_action( -5, require15, dlog( kDebugLevelMax, "SHOULD SEE: action 3 (expected)\n" ) ); + { err = kResponseErr; goto exit; } require15: - require_noerr_action( 0, require16, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 4\n" ) ); - goto require16Good; + require_noerr_action( 0, require16, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 4\n" ) ); + goto require16Good; require16: - { err = kResponseErr; goto exit; } + { err = kResponseErr; goto exit; } require16Good: - require_noerr_action_quiet( -4, require17, dlog( kDebugLevelMax, "SHOULD SEE: action 5 (expected)\n" ) ); - { err = kResponseErr; goto exit; } + require_noerr_action_quiet( -4, require17, dlog( kDebugLevelMax, "SHOULD SEE: action 5 (expected)\n" ) ); + { err = kResponseErr; goto exit; } require17: - require_noerr_action_quiet( 0, require18, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 6\n" ) ); - goto require18Good; + require_noerr_action_quiet( 0, require18, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 6\n" ) ); + goto require18Good; require18: - { err = kResponseErr; goto exit; } + { err = kResponseErr; goto exit; } require18Good: - require_action( 0 && "SHOULD SEE", require19, dlog( kDebugLevelMax, "SHOULD SEE: action 7 (expected)\n" ) ); - { err = kResponseErr; goto exit; } + require_action( 0 && "SHOULD SEE", require19, dlog( kDebugLevelMax, "SHOULD SEE: action 7 (expected)\n" ) ); + { err = kResponseErr; goto exit; } require19: - require_action( 1 && "SHOULD *NOT* SEE", require20, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 8\n" ) ); - goto require20Good; + require_action( 1 && "SHOULD *NOT* SEE", require20, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 8\n" ) ); + goto require20Good; require20: - { err = kResponseErr; goto exit; } + { err = kResponseErr; goto exit; } require20Good: - require_action_quiet( 0, require21, dlog( kDebugLevelMax, "SHOULD SEE: action 9 (expected)\n" ) ); - { err = kResponseErr; goto exit; } + require_action_quiet( 0, require21, dlog( kDebugLevelMax, "SHOULD SEE: action 9 (expected)\n" ) ); + { err = kResponseErr; goto exit; } require21: - require_action_quiet( 1, require22, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 10\n" ) ); - goto require22Good; + require_action_quiet( 1, require22, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 10\n" ) ); + goto require22Good; require22: - { err = kResponseErr; goto exit; } + { err = kResponseErr; goto exit; } require22Good: - require_action_string( 0, require23, dlog( kDebugLevelMax, "SHOULD SEE: action 11 (expected)\n" ), "SHOULD SEE: require_action_string" ); - { err = kResponseErr; goto exit; } + require_action_string( 0, require23, dlog( kDebugLevelMax, "SHOULD SEE: action 11 (expected)\n" ), "SHOULD SEE: require_action_string" ); + { err = kResponseErr; goto exit; } require23: - require_action_string( 1, require24, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 12\n" ), "SHOULD *NOT* SEE: require_action_string" ); - goto require24Good; + require_action_string( 1, require24, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 12\n" ), "SHOULD *NOT* SEE: require_action_string" ); + goto require24Good; require24: - { err = kResponseErr; goto exit; } + { err = kResponseErr; goto exit; } require24Good: -#if( defined( __MWERKS__ ) ) - #if( defined( __cplusplus ) && __option( exceptions ) ) - #define COMPILER_HAS_EXCEPTIONS 1 - #else - #define COMPILER_HAS_EXCEPTIONS 0 - #endif +#if ( defined( __MWERKS__ ) ) + #if ( defined( __cplusplus ) && __option( exceptions ) ) + #define COMPILER_HAS_EXCEPTIONS 1 + #else + #define COMPILER_HAS_EXCEPTIONS 0 + #endif #else - #if( defined( __cplusplus ) ) - #define COMPILER_HAS_EXCEPTIONS 1 - #else - #define COMPILER_HAS_EXCEPTIONS 0 - #endif + #if ( defined( __cplusplus ) ) + #define COMPILER_HAS_EXCEPTIONS 1 + #else + #define COMPILER_HAS_EXCEPTIONS 0 + #endif #endif -#if( COMPILER_HAS_EXCEPTIONS ) - try - { - require_throw( 1 && "SHOULD *NOT* SEE" ); - require_throw( 0 && "SHOULD SEE" ); - } - catch( ... ) - { - goto require26Good; - } - { err = kResponseErr; goto exit; } +#if ( COMPILER_HAS_EXCEPTIONS ) + try + { + require_throw( 1 && "SHOULD *NOT* SEE" ); + require_throw( 0 && "SHOULD SEE" ); + } + catch(... ) + { + goto require26Good; + } + { err = kResponseErr; goto exit; } require26Good: #endif - // translate_errno - - err = translate_errno( 1 != -1, -123, -567 ); - require( ( err == 0 ) && "SHOULD *NOT* SEE", exit ); - - err = translate_errno( -1 != -1, -123, -567 ); - require( ( err == -123 ) && "SHOULD *NOT* SEE", exit ); - - err = translate_errno( -1 != -1, 0, -567 ); - require( ( err == -567 ) && "SHOULD *NOT* SEE", exit ); - - // debug_string - - debug_string( "debug_string" ); - - // DebugSNPrintF - - DebugSNPrintF( s, sizeof( s ), "%d", 1234 ); - require_action( strcmp( s, "1234" ) == 0, exit, err = -1 ); - - DebugSNPrintF( s, sizeof( s ), "%X", 0x2345 ); - require_action( strcmp( s, "2345" ) == 0, exit, err = -1 ); - - DebugSNPrintF( s, sizeof( s ), "%#s", "\05test" ); - require_action( strcmp( s, "test" ) == 0, exit, err = -1 ); - - DebugSNPrintF( s, sizeof( s ), "%##s", "\03www\05apple\03com" ); - require_action( strcmp( s, "www.apple.com." ) == 0, exit, err = -1 ); - - DebugSNPrintF( s, sizeof( s ), "%ld", (long) INT32_C( 2147483647 ) ); - require_action( strcmp( s, "2147483647" ) == 0, exit, err = -1 ); - - DebugSNPrintF( s, sizeof( s ), "%lu", (unsigned long) UINT32_C( 4294967295 ) ); - require_action( strcmp( s, "4294967295" ) == 0, exit, err = -1 ); - - #if( TYPE_LONGLONG_NATIVE ) - DebugSNPrintF( s, sizeof( s ), "%lld", (long_long_compat) INT64_C( 9223372036854775807 ) ); - require_action( strcmp( s, "9223372036854775807" ) == 0, exit, err = -1 ); - - DebugSNPrintF( s, sizeof( s ), "%lld", (long_long_compat) INT64_C( -9223372036854775807 ) ); - require_action( strcmp( s, "-9223372036854775807" ) == 0, exit, err = -1 ); - - DebugSNPrintF( s, sizeof( s ), "%llu", (unsigned_long_long_compat) UINT64_C( 18446744073709551615 ) ); - require_action( strcmp( s, "18446744073709551615" ) == 0, exit, err = -1 ); - #endif - - DebugSNPrintF( s, sizeof( s ), "%lb", (unsigned long) binary_32( 01111011, 01111011, 01111011, 01111011 ) ); - require_action( strcmp( s, "1111011011110110111101101111011" ) == 0, exit, err = -1 ); - - DebugSNPrintF( s, sizeof( s ), "%C", 0x41624364 ); // 'AbCd' - require_action( strcmp( s, "AbCd" ) == 0, exit, err = -1 ); - - #if( defined( MDNS_DEBUGMSGS ) ) - { - mDNSAddr maddr; - - memset( &maddr, 0, sizeof( maddr ) ); - maddr.type = mDNSAddrType_IPv4; - maddr.ip.v4.b[ 0 ] = 127; - maddr.ip.v4.b[ 1 ] = 0; - maddr.ip.v4.b[ 2 ] = 0; - maddr.ip.v4.b[ 3 ] = 1; - DebugSNPrintF( s, sizeof( s ), "%#a", &maddr ); - require_action( strcmp( s, "127.0.0.1" ) == 0, exit, err = -1 ); - - memset( &maddr, 0, sizeof( maddr ) ); - maddr.type = mDNSAddrType_IPv6; - maddr.ip.v6.b[ 0 ] = 0xFE; - maddr.ip.v6.b[ 1 ] = 0x80; - maddr.ip.v6.b[ 15 ] = 0x01; - DebugSNPrintF( s, sizeof( s ), "%#a", &maddr ); - require_action( strcmp( s, "FE80:0000:0000:0000:0000:0000:0000:0001" ) == 0, exit, err = -1 ); - } - #endif - - #if( AF_INET ) - { - struct sockaddr_in sa4; - - memset( &sa4, 0, sizeof( sa4 ) ); - sa4.sin_family = AF_INET; - p = (uint8_t *) &sa4.sin_port; - p[ 0 ] = (uint8_t)( ( 80 >> 8 ) & 0xFF ); - p[ 1 ] = (uint8_t)( 80 & 0xFF ); - p = (uint8_t *) &sa4.sin_addr.s_addr; - p[ 0 ] = (uint8_t)( ( INADDR_LOOPBACK >> 24 ) & 0xFF ); - p[ 1 ] = (uint8_t)( ( INADDR_LOOPBACK >> 16 ) & 0xFF ); - p[ 2 ] = (uint8_t)( ( INADDR_LOOPBACK >> 8 ) & 0xFF ); - p[ 3 ] = (uint8_t)( INADDR_LOOPBACK & 0xFF ); - DebugSNPrintF( s, sizeof( s ), "%##a", &sa4 ); - require_action( strcmp( s, "127.0.0.1:80" ) == 0, exit, err = -1 ); - } - #endif - - #if( AF_INET6 ) - { - struct sockaddr_in6 sa6; - - memset( &sa6, 0, sizeof( sa6 ) ); - sa6.sin6_family = AF_INET6; - p = (uint8_t *) &sa6.sin6_port; - p[ 0 ] = (uint8_t)( ( 80 >> 8 ) & 0xFF ); - p[ 1 ] = (uint8_t)( 80 & 0xFF ); - sa6.sin6_addr.s6_addr[ 0 ] = 0xFE; - sa6.sin6_addr.s6_addr[ 1 ] = 0x80; - sa6.sin6_addr.s6_addr[ 15 ] = 0x01; - sa6.sin6_scope_id = 2; - DebugSNPrintF( s, sizeof( s ), "%##a", &sa6 ); - require_action( strcmp( s, "[FE80:0000:0000:0000:0000:0000:0000:0001%2]:80" ) == 0, exit, err = -1 ); - } - #endif - - // Unicode - - DebugSNPrintF(s, sizeof(s), "%.*s", 4, "tes" ); - require_action( strcmp( s, "tes" ) == 0, exit, err = kResponseErr ); - - DebugSNPrintF(s, sizeof(s), "%.*s", 4, "test" ); - require_action( strcmp( s, "test" ) == 0, exit, err = kResponseErr ); - - DebugSNPrintF(s, sizeof(s), "%.*s", 4, "testing" ); - require_action( strcmp( s, "test" ) == 0, exit, err = kResponseErr ); - - DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xC3\xA9" ); - require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr ); - - DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xC3\xA9ing" ); - require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr ); - - DebugSNPrintF(s, sizeof(s), "%.*s", 4, "tes\xC3\xA9ing" ); - require_action( strcmp( s, "tes" ) == 0, exit, err = kResponseErr ); - - DebugSNPrintF(s, sizeof(s), "%.*s", 4, "t\xed\x9f\xbf" ); - require_action( strcmp( s, "t\xed\x9f\xbf" ) == 0, exit, err = kResponseErr ); - - DebugSNPrintF(s, sizeof(s), "%.*s", 4, "t\xed\x9f\xbfing" ); - require_action( strcmp( s, "t\xed\x9f\xbf" ) == 0, exit, err = kResponseErr ); - - DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xed\x9f\xbf" ); - require_action( strcmp( s, "te" ) == 0, exit, err = kResponseErr ); - - DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xed\x9f\xbfing" ); - require_action( strcmp( s, "te" ) == 0, exit, err = kResponseErr ); - - DebugSNPrintF(s, sizeof(s), "%.*s", 7, "te\xC3\xA9\xed\x9f\xbfing" ); - require_action( strcmp( s, "te\xC3\xA9\xed\x9f\xbf" ) == 0, exit, err = kResponseErr ); - - DebugSNPrintF(s, sizeof(s), "%.*s", 6, "te\xC3\xA9\xed\x9f\xbfing" ); - require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr ); - - DebugSNPrintF(s, sizeof(s), "%.*s", 5, "te\xC3\xA9\xed\x9f\xbfing" ); - require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr ); - - #if( TARGET_RT_BIG_ENDIAN ) - DebugSNPrintF( s, sizeof( s ), "%S", "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" ); - require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 ); - #else - DebugSNPrintF( s, sizeof( s ), "%S", "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" ); - require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 ); - #endif - - DebugSNPrintF( s, sizeof( s ), "%S", - "\xFE\xFF" "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" ); // Big Endian BOM - require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 ); - - DebugSNPrintF( s, sizeof( s ), "%S", - "\xFF\xFE" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" ); // Little Endian BOM - require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 ); - - DebugSNPrintF( s, sizeof( s ), "%#S", "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" ); // Big Endian - require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 ); - - DebugSNPrintF( s, sizeof( s ), "%##S", "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" ); // Little Endian - require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 ); - - DebugSNPrintF( s, sizeof( s ), "%.*S", - 4, "\xFE\xFF" "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" ); // Big Endian BOM - require_action( strcmp( s, "abc" ) == 0, exit, err = -1 ); - - DebugSNPrintF( s, sizeof( s ), "%.*S", - 4, "\xFF\xFE" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" ); // Little Endian BOM - require_action( strcmp( s, "abc" ) == 0, exit, err = -1 ); - - #if( TARGET_RT_BIG_ENDIAN ) - DebugSNPrintF( s, sizeof( s ), "%.*S", 3, "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" ); - require_action( strcmp( s, "abc" ) == 0, exit, err = -1 ); - #else - DebugSNPrintF( s, sizeof( s ), "%.*S", 3, "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" ); - require_action( strcmp( s, "abc" ) == 0, exit, err = -1 ); - #endif - - DebugSNPrintF( s, sizeof( s ), "%#.*S", 3, "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" ); // Big Endian - require_action( strcmp( s, "abc" ) == 0, exit, err = -1 ); - - DebugSNPrintF( s, sizeof( s ), "%##.*S", 3, "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" ); // Little Endian - require_action( strcmp( s, "abc" ) == 0, exit, err = -1 ); - - // Misc - - DebugSNPrintF( s, sizeof( s ), "%U", "\x10\xb8\xa7\x6b" "\xad\x9d" "\xd1\x11" "\x80\xb4" "\x00\xc0\x4f\xd4\x30\xc8" ); - require_action( strcmp( s, "6ba7b810-9dad-11d1-80b4-00c04fd430c8" ) == 0, exit, err = -1 ); - - DebugSNPrintF( s, sizeof( s ), "%m", 0 ); - require_action( strcmp( s, "no error" ) == 0, exit, err = -1 ); - - DebugSNPrintF( s, sizeof( s ), "%lm", (long) 0 ); - require_action( strcmp( s, "no error" ) == 0, exit, err = -1 ); - - DebugSNPrintF( s, sizeof( s ), "\"%H\"", "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8", 16, 16 ); - DebugPrintF( kDebugLevelMax, "%s\n\n", s ); - - DebugSNPrintF( s, sizeof( s ), "\"%H\"", - "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8" - "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8", - 32, 32 ); - DebugPrintF( kDebugLevelMax, "%s\n\n", s ); - - DebugSNPrintF( s, sizeof( s ), "\"%H\"", "\x6b\xa7", 2, 2 ); - DebugPrintF( kDebugLevelMax, "%s\n\n", s ); - - // Hex Dumps - - s[ 0 ] = '\0'; - DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ), - kDebugFlagsNone, s, sizeof( s ) ); - DebugPrintF( kDebugLevelMax, "%s\n", s ); - - s[ 0 ] = '\0'; - DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), - kDebugFlagsNoAddress | kDebugFlagsNoOffset, s, sizeof( s ) ); - DebugPrintF( kDebugLevelMax, "%s\n", s ); - - s[ 0 ] = '\0'; - DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ), - kDebugFlagsNoAddress | kDebugFlagsNoOffset, s, sizeof( s ) ); - DebugPrintF( kDebugLevelMax, "%s\n", s ); - - s[ 0 ] = '\0'; - DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ), - kDebugFlagsNoAddress, s, sizeof( s ) ); - DebugPrintF( kDebugLevelMax, "%s\n", s ); - - s[ 0 ] = '\0'; - DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), - kDebugFlagsNoOffset, s, sizeof( s ) ); - DebugPrintF( kDebugLevelMax, "%s\n", s ); - - s[ 0 ] = '\0'; - DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), - kDebugFlagsNoAddress, s, sizeof( s ) ); - DebugPrintF( kDebugLevelMax, "%s\n", s ); - - s[ 0 ] = '\0'; - DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), - kDebugFlagsNoOffset, s, sizeof( s ) ); - DebugPrintF( kDebugLevelMax, "%s\n", s ); - - s[ 0 ] = '\0'; - DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), - kDebugFlagsNoByteCount, s, sizeof( s ) ); - DebugPrintF( kDebugLevelMax, "%s\n", s ); - - s[ 0 ] = '\0'; - DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, "\x41\x62\x43\x64", "\x41\x62\x43\x64", 4, // 'AbCd' - kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoNewLine | - kDebugFlagsNo32BitSeparator | kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount, - s, sizeof( s ) ); - DebugPrintF( kDebugLevelMax, "%s\n", s ); - - s[ 0 ] = '\0'; - DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), - kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoASCII | kDebugFlagsNoNewLine | - kDebugFlags16BitSeparator | kDebugFlagsNo32BitSeparator | - kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount, s, sizeof( s ) ); - DebugPrintF( kDebugLevelMax, "%s\n", s ); - - s[ 0 ] = '\0'; - DebugHexDump( kDebugLevelMax, 8, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), kDebugFlagsNone, s, sizeof( s ) ); - DebugPrintF( kDebugLevelMax, "%s\n", s ); - - // dlog's - - dlog( kDebugLevelNotice, "dlog\n" ); - dlog( kDebugLevelNotice, "dlog integer: %d\n", 123 ); - dlog( kDebugLevelNotice, "dlog string: \"%s\"\n", "test string" ); - dlogmem( kDebugLevelNotice, data, sizeof( data ) ); - - // Done - - DebugPrintF( kDebugLevelMax, "\n\nALL TESTS DONE\n\n" ); - err = kNoErr; - + // translate_errno + + err = translate_errno( 1 != -1, -123, -567 ); + require( ( err == 0 ) && "SHOULD *NOT* SEE", exit ); + + err = translate_errno( -1 != -1, -123, -567 ); + require( ( err == -123 ) && "SHOULD *NOT* SEE", exit ); + + err = translate_errno( -1 != -1, 0, -567 ); + require( ( err == -567 ) && "SHOULD *NOT* SEE", exit ); + + // debug_string + + debug_string( "debug_string" ); + + // DebugSNPrintF + + DebugSNPrintF( s, sizeof( s ), "%d", 1234 ); + require_action( strcmp( s, "1234" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%X", 0x2345 ); + require_action( strcmp( s, "2345" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%#s", "\05test" ); + require_action( strcmp( s, "test" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%##s", "\03www\05apple\03com" ); + require_action( strcmp( s, "www.apple.com." ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%ld", (long) INT32_C( 2147483647 ) ); + require_action( strcmp( s, "2147483647" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%lu", (unsigned long) UINT32_C( 4294967295 ) ); + require_action( strcmp( s, "4294967295" ) == 0, exit, err = -1 ); + + #if ( TYPE_LONGLONG_NATIVE ) + DebugSNPrintF( s, sizeof( s ), "%lld", (long_long_compat) INT64_C( 9223372036854775807 ) ); + require_action( strcmp( s, "9223372036854775807" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%lld", (long_long_compat) INT64_C( -9223372036854775807 ) ); + require_action( strcmp( s, "-9223372036854775807" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%llu", (unsigned_long_long_compat) UINT64_C( 18446744073709551615 ) ); + require_action( strcmp( s, "18446744073709551615" ) == 0, exit, err = -1 ); + #endif + + DebugSNPrintF( s, sizeof( s ), "%lb", (unsigned long) binary_32( 01111011, 01111011, 01111011, 01111011 ) ); + require_action( strcmp( s, "1111011011110110111101101111011" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%C", 0x41624364 ); // 'AbCd' + require_action( strcmp( s, "AbCd" ) == 0, exit, err = -1 ); + + #if ( defined( MDNS_DEBUGMSGS ) ) + { + mDNSAddr maddr; + + memset( &maddr, 0, sizeof( maddr ) ); + maddr.type = mDNSAddrType_IPv4; + maddr.ip.v4.b[ 0 ] = 127; + maddr.ip.v4.b[ 1 ] = 0; + maddr.ip.v4.b[ 2 ] = 0; + maddr.ip.v4.b[ 3 ] = 1; + DebugSNPrintF( s, sizeof( s ), "%#a", &maddr ); + require_action( strcmp( s, "127.0.0.1" ) == 0, exit, err = -1 ); + + memset( &maddr, 0, sizeof( maddr ) ); + maddr.type = mDNSAddrType_IPv6; + maddr.ip.v6.b[ 0 ] = 0xFE; + maddr.ip.v6.b[ 1 ] = 0x80; + maddr.ip.v6.b[ 15 ] = 0x01; + DebugSNPrintF( s, sizeof( s ), "%#a", &maddr ); + require_action( strcmp( s, "FE80:0000:0000:0000:0000:0000:0000:0001" ) == 0, exit, err = -1 ); + } + #endif + + #if ( AF_INET ) + { + struct sockaddr_in sa4; + + memset( &sa4, 0, sizeof( sa4 ) ); + sa4.sin_family = AF_INET; + p = (uint8_t *) &sa4.sin_port; + p[ 0 ] = (uint8_t)( ( 80 >> 8 ) & 0xFF ); + p[ 1 ] = (uint8_t)( 80 & 0xFF ); + p = (uint8_t *) &sa4.sin_addr.s_addr; + p[ 0 ] = (uint8_t)( ( INADDR_LOOPBACK >> 24 ) & 0xFF ); + p[ 1 ] = (uint8_t)( ( INADDR_LOOPBACK >> 16 ) & 0xFF ); + p[ 2 ] = (uint8_t)( ( INADDR_LOOPBACK >> 8 ) & 0xFF ); + p[ 3 ] = (uint8_t)( INADDR_LOOPBACK & 0xFF ); + DebugSNPrintF( s, sizeof( s ), "%##a", &sa4 ); + require_action( strcmp( s, "127.0.0.1:80" ) == 0, exit, err = -1 ); + } + #endif + + #if ( AF_INET6 ) + { + struct sockaddr_in6 sa6; + + memset( &sa6, 0, sizeof( sa6 ) ); + sa6.sin6_family = AF_INET6; + p = (uint8_t *) &sa6.sin6_port; + p[ 0 ] = (uint8_t)( ( 80 >> 8 ) & 0xFF ); + p[ 1 ] = (uint8_t)( 80 & 0xFF ); + sa6.sin6_addr.s6_addr[ 0 ] = 0xFE; + sa6.sin6_addr.s6_addr[ 1 ] = 0x80; + sa6.sin6_addr.s6_addr[ 15 ] = 0x01; + sa6.sin6_scope_id = 2; + DebugSNPrintF( s, sizeof( s ), "%##a", &sa6 ); + require_action( strcmp( s, "[FE80:0000:0000:0000:0000:0000:0000:0001%2]:80" ) == 0, exit, err = -1 ); + } + #endif + + // Unicode + + DebugSNPrintF(s, sizeof(s), "%.*s", 4, "tes" ); + require_action( strcmp( s, "tes" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 4, "test" ); + require_action( strcmp( s, "test" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 4, "testing" ); + require_action( strcmp( s, "test" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xC3\xA9" ); + require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xC3\xA9ing" ); + require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 4, "tes\xC3\xA9ing" ); + require_action( strcmp( s, "tes" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 4, "t\xed\x9f\xbf" ); + require_action( strcmp( s, "t\xed\x9f\xbf" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 4, "t\xed\x9f\xbfing" ); + require_action( strcmp( s, "t\xed\x9f\xbf" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xed\x9f\xbf" ); + require_action( strcmp( s, "te" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xed\x9f\xbfing" ); + require_action( strcmp( s, "te" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 7, "te\xC3\xA9\xed\x9f\xbfing" ); + require_action( strcmp( s, "te\xC3\xA9\xed\x9f\xbf" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 6, "te\xC3\xA9\xed\x9f\xbfing" ); + require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 5, "te\xC3\xA9\xed\x9f\xbfing" ); + require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr ); + + #if ( TARGET_RT_BIG_ENDIAN ) + DebugSNPrintF( s, sizeof( s ), "%S", "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" ); + require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 ); + #else + DebugSNPrintF( s, sizeof( s ), "%S", "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" ); + require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 ); + #endif + + DebugSNPrintF( s, sizeof( s ), "%S", + "\xFE\xFF" "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" ); // Big Endian BOM + require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%S", + "\xFF\xFE" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" ); // Little Endian BOM + require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%#S", "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" ); // Big Endian + require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%##S", "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" ); // Little Endian + require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%.*S", + 4, "\xFE\xFF" "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" ); // Big Endian BOM + require_action( strcmp( s, "abc" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%.*S", + 4, "\xFF\xFE" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" ); // Little Endian BOM + require_action( strcmp( s, "abc" ) == 0, exit, err = -1 ); + + #if ( TARGET_RT_BIG_ENDIAN ) + DebugSNPrintF( s, sizeof( s ), "%.*S", 3, "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" ); + require_action( strcmp( s, "abc" ) == 0, exit, err = -1 ); + #else + DebugSNPrintF( s, sizeof( s ), "%.*S", 3, "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" ); + require_action( strcmp( s, "abc" ) == 0, exit, err = -1 ); + #endif + + DebugSNPrintF( s, sizeof( s ), "%#.*S", 3, "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" ); // Big Endian + require_action( strcmp( s, "abc" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%##.*S", 3, "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" ); // Little Endian + require_action( strcmp( s, "abc" ) == 0, exit, err = -1 ); + + // Misc + + DebugSNPrintF( s, sizeof( s ), "%U", "\x10\xb8\xa7\x6b" "\xad\x9d" "\xd1\x11" "\x80\xb4" "\x00\xc0\x4f\xd4\x30\xc8" ); + require_action( strcmp( s, "6ba7b810-9dad-11d1-80b4-00c04fd430c8" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%m", 0 ); + require_action( strcmp( s, "no error" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%lm", (long) 0 ); + require_action( strcmp( s, "no error" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "\"%H\"", "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8", 16, 16 ); + DebugPrintF( kDebugLevelMax, "%s\n\n", s ); + + DebugSNPrintF( s, sizeof( s ), "\"%H\"", + "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8" + "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8", + 32, 32 ); + DebugPrintF( kDebugLevelMax, "%s\n\n", s ); + + DebugSNPrintF( s, sizeof( s ), "\"%H\"", "\x6b\xa7", 2, 2 ); + DebugPrintF( kDebugLevelMax, "%s\n\n", s ); + + // Hex Dumps + + s[ 0 ] = '\0'; + DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ), + kDebugFlagsNone, s, sizeof( s ) ); + DebugPrintF( kDebugLevelMax, "%s\n", s ); + + s[ 0 ] = '\0'; + DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), + kDebugFlagsNoAddress | kDebugFlagsNoOffset, s, sizeof( s ) ); + DebugPrintF( kDebugLevelMax, "%s\n", s ); + + s[ 0 ] = '\0'; + DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ), + kDebugFlagsNoAddress | kDebugFlagsNoOffset, s, sizeof( s ) ); + DebugPrintF( kDebugLevelMax, "%s\n", s ); + + s[ 0 ] = '\0'; + DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ), + kDebugFlagsNoAddress, s, sizeof( s ) ); + DebugPrintF( kDebugLevelMax, "%s\n", s ); + + s[ 0 ] = '\0'; + DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), + kDebugFlagsNoOffset, s, sizeof( s ) ); + DebugPrintF( kDebugLevelMax, "%s\n", s ); + + s[ 0 ] = '\0'; + DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), + kDebugFlagsNoAddress, s, sizeof( s ) ); + DebugPrintF( kDebugLevelMax, "%s\n", s ); + + s[ 0 ] = '\0'; + DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), + kDebugFlagsNoOffset, s, sizeof( s ) ); + DebugPrintF( kDebugLevelMax, "%s\n", s ); + + s[ 0 ] = '\0'; + DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), + kDebugFlagsNoByteCount, s, sizeof( s ) ); + DebugPrintF( kDebugLevelMax, "%s\n", s ); + + s[ 0 ] = '\0'; + DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, "\x41\x62\x43\x64", "\x41\x62\x43\x64", 4, // 'AbCd' + kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoNewLine | + kDebugFlagsNo32BitSeparator | kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount, + s, sizeof( s ) ); + DebugPrintF( kDebugLevelMax, "%s\n", s ); + + s[ 0 ] = '\0'; + DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), + kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoASCII | kDebugFlagsNoNewLine | + kDebugFlags16BitSeparator | kDebugFlagsNo32BitSeparator | + kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount, s, sizeof( s ) ); + DebugPrintF( kDebugLevelMax, "%s\n", s ); + + s[ 0 ] = '\0'; + DebugHexDump( kDebugLevelMax, 8, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), kDebugFlagsNone, s, sizeof( s ) ); + DebugPrintF( kDebugLevelMax, "%s\n", s ); + + // dlog's + + dlog( kDebugLevelNotice, "dlog\n" ); + dlog( kDebugLevelNotice, "dlog integer: %d\n", 123 ); + dlog( kDebugLevelNotice, "dlog string: \"%s\"\n", "test string" ); + dlogmem( kDebugLevelNotice, data, sizeof( data ) ); + + // Done + + DebugPrintF( kDebugLevelMax, "\n\nALL TESTS DONE\n\n" ); + err = kNoErr; + exit: - if( err ) - { - DebugPrintF( kDebugLevelMax, "\n\n### TEST FAILED ###\n\n" ); - } - return( err ); + if( err ) + { + DebugPrintF( kDebugLevelMax, "\n\n### TEST FAILED ###\n\n" ); + } + return( err ); } -#endif // DEBUG +#endif // DEBUG diff --git a/mDNSShared/DebugServices.h b/mDNSShared/DebugServices.h index d4e5c72..108f7f5 100644 --- a/mDNSShared/DebugServices.h +++ b/mDNSShared/DebugServices.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,18 +18,18 @@ //--------------------------------------------------------------------------------------------------------------------------- /*! @header DebugServices - Debugging Library -*/ + Debugging Library + */ -#ifndef __DEBUG_SERVICES__ -#define __DEBUG_SERVICES__ +#ifndef __DEBUG_SERVICES__ +#define __DEBUG_SERVICES__ -#include +#include -#include "CommonServices.h" +#include "CommonServices.h" -#if( TARGET_OS_VXWORKS ) - #include "logLib.h" +#if ( TARGET_OS_VXWORKS ) + #include "logLib.h" #endif #if 0 @@ -42,48 +42,48 @@ // General -#if( !defined( DEBUG ) ) - #define DEBUG 0 +#if ( !defined( DEBUG ) ) + #define DEBUG 0 #endif -#if( defined( NDEBUG ) && DEBUG ) - #error NDEBUG defined and DEBUG is also enabled...they need to be in-sync +#if ( defined( NDEBUG ) && DEBUG ) + #error NDEBUG defined and DEBUG is also enabled...they need to be in-sync #endif - + // AssertMacros.h/Debugging.h overrides. -#if( !defined( DEBUG_OVERRIDE_APPLE_MACROS ) ) - #define DEBUG_OVERRIDE_APPLE_MACROS 1 +#if ( !defined( DEBUG_OVERRIDE_APPLE_MACROS ) ) + #define DEBUG_OVERRIDE_APPLE_MACROS 1 #endif // Routine name. Uses ISO __func__ where possible. Otherwise, uses the best thing that is available (if anything). -#if( defined( __MWERKS__ ) || ( __GNUC__ > 2 ) || ( ( __GNUC__ == 2 ) && ( __GNUC_MINOR__ >= 9 ) ) ) - #define __ROUTINE__ __func__ -#elif( defined( __GNUC__ ) ) - #define __ROUTINE__ __PRETTY_FUNCTION__ -#elif( defined( _MSC_VER ) && !defined( _WIN32_WCE ) ) - #define __ROUTINE__ __FUNCTION__ +#if ( defined( __MWERKS__ ) || ( __GNUC__ > 2 ) || ( ( __GNUC__ == 2 ) && ( __GNUC_MINOR__ >= 9 ) ) ) + #define __ROUTINE__ __func__ +#elif ( defined( __GNUC__ ) ) + #define __ROUTINE__ __PRETTY_FUNCTION__ +#elif ( defined( _MSC_VER ) && !defined( _WIN32_WCE ) ) + #define __ROUTINE__ __FUNCTION__ #else - #define __ROUTINE__ "" + #define __ROUTINE__ "" #endif // Variable argument macro support. Use ANSI C99 __VA_ARGS__ where possible. Otherwise, use the next best thing. -#if( defined( __GNUC__ ) ) - #if( ( __GNUC__ > 3 ) || ( ( __GNUC__ == 3 ) && ( __GNUC_MINOR__ >= 3) ) ) - #define DEBUG_C99_VA_ARGS 1 - #define DEBUG_GNU_VA_ARGS 0 - #else - #define DEBUG_C99_VA_ARGS 0 - #define DEBUG_GNU_VA_ARGS 1 - #endif -#elif( defined( __MWERKS__ ) ) - #define DEBUG_C99_VA_ARGS 1 - #define DEBUG_GNU_VA_ARGS 0 +#if ( defined( __GNUC__ ) ) + #if ( ( __GNUC__ > 3 ) || ( ( __GNUC__ == 3 ) && ( __GNUC_MINOR__ >= 3) ) ) + #define DEBUG_C99_VA_ARGS 1 + #define DEBUG_GNU_VA_ARGS 0 + #else + #define DEBUG_C99_VA_ARGS 0 + #define DEBUG_GNU_VA_ARGS 1 + #endif +#elif ( defined( __MWERKS__ ) ) + #define DEBUG_C99_VA_ARGS 1 + #define DEBUG_GNU_VA_ARGS 0 #else - #define DEBUG_C99_VA_ARGS 0 - #define DEBUG_GNU_VA_ARGS 0 + #define DEBUG_C99_VA_ARGS 0 + #define DEBUG_GNU_VA_ARGS 0 #endif #if 0 @@ -92,122 +92,122 @@ //--------------------------------------------------------------------------------------------------------------------------- /*! @defined DEBUG_FPRINTF_ENABLED - - @abstract Enables ANSI C fprintf output. -*/ - -#if( !defined( DEBUG_FPRINTF_ENABLED ) ) - #if( !TARGET_API_MAC_OSX_KERNEL && !TARGET_OS_WINDOWS_CE ) - #define DEBUG_FPRINTF_ENABLED 1 - #else - #define DEBUG_FPRINTF_ENABLED 0 - #endif + + @abstract Enables ANSI C fprintf output. + */ + +#if ( !defined( DEBUG_FPRINTF_ENABLED ) ) + #if ( !TARGET_API_MAC_OSX_KERNEL && !TARGET_OS_WINDOWS_CE ) + #define DEBUG_FPRINTF_ENABLED 1 + #else + #define DEBUG_FPRINTF_ENABLED 0 + #endif #else - #if( TARGET_API_MAC_OSX_KERNEL || TARGET_OS_WINDOWS_CE ) - #error fprintf enabled, but not supported on Mac OS X kernel or Windows CE - #endif + #if ( TARGET_API_MAC_OSX_KERNEL || TARGET_OS_WINDOWS_CE ) + #error fprintf enabled, but not supported on Mac OS X kernel or Windows CE + #endif #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined DEBUG_MAC_OS_X_IOLOG_ENABLED - - @abstract Enables IOLog (Mac OS X Kernel) output. -*/ -#if( !defined( DEBUG_MAC_OS_X_IOLOG_ENABLED ) ) - #define DEBUG_MAC_OS_X_IOLOG_ENABLED TARGET_API_MAC_OSX_KERNEL + @abstract Enables IOLog (Mac OS X Kernel) output. + */ + +#if ( !defined( DEBUG_MAC_OS_X_IOLOG_ENABLED ) ) + #define DEBUG_MAC_OS_X_IOLOG_ENABLED TARGET_API_MAC_OSX_KERNEL #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined DEBUG_KPRINTF_ENABLED - - @abstract Enables kprintf (Mac OS X Kernel) output. -*/ -#if( !defined( DEBUG_KPRINTF_ENABLED ) ) - #define DEBUG_KPRINTF_ENABLED TARGET_API_MAC_OSX_KERNEL + @abstract Enables kprintf (Mac OS X Kernel) output. + */ + +#if ( !defined( DEBUG_KPRINTF_ENABLED ) ) + #define DEBUG_KPRINTF_ENABLED TARGET_API_MAC_OSX_KERNEL #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined DEBUG_IDEBUG_ENABLED - - @abstract Enables iDebug (Mac OS X user and Kernel) output. - - @discussion - - For Mac OS X kernel development, iDebug is enabled by default because we can dynamically check for the presence - of iDebug via some exported IOKit symbols. Mac OS X app usage doesn't allow dynamic detection because it relies - on statically linking to the iDebugServices.cp file so for Mac OS X app usage, you have to manually enable iDebug. -*/ -#if( !defined( DEBUG_IDEBUG_ENABLED ) ) - #define DEBUG_IDEBUG_ENABLED TARGET_API_MAC_OSX_KERNEL + @abstract Enables iDebug (Mac OS X user and Kernel) output. + + @discussion + + For Mac OS X kernel development, iDebug is enabled by default because we can dynamically check for the presence + of iDebug via some exported IOKit symbols. Mac OS X app usage doesn't allow dynamic detection because it relies + on statically linking to the iDebugServices.cp file so for Mac OS X app usage, you have to manually enable iDebug. + */ + +#if ( !defined( DEBUG_IDEBUG_ENABLED ) ) + #define DEBUG_IDEBUG_ENABLED TARGET_API_MAC_OSX_KERNEL #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined DEBUG_CORE_SERVICE_ASSERTS_ENABLED - - @abstract Controls whether Core Services assert handling is enabled. Enabling requires CoreServices framework. -*/ -#if( !defined( DEBUG_CORE_SERVICE_ASSERTS_ENABLED ) ) - #if( defined( __DEBUGGING__ ) ) - #define DEBUG_CORE_SERVICE_ASSERTS_ENABLED 1 - #else - #define DEBUG_CORE_SERVICE_ASSERTS_ENABLED 0 - #endif + @abstract Controls whether Core Services assert handling is enabled. Enabling requires CoreServices framework. + */ + +#if ( !defined( DEBUG_CORE_SERVICE_ASSERTS_ENABLED ) ) + #if ( defined( __DEBUGGING__ ) ) + #define DEBUG_CORE_SERVICE_ASSERTS_ENABLED 1 + #else + #define DEBUG_CORE_SERVICE_ASSERTS_ENABLED 0 + #endif #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @typedef DebugOutputType - - @abstract Type of debug output (i.e. where the output goes). -*/ - -typedef uint32_t DebugOutputType; - -#define kDebugOutputTypeNone 0x6E6F6E65U // 'none' - no params -#define kDebugOutputTypeCustom 0x63757374U // 'cust' - 1st param = function ptr, 2nd param = context -#define kDebugOutputTypeFPrintF 0x66707269U // 'fpri' - 1st param = DebugOutputTypeFlags [, 2nd param = filename] -#define kDebugOutputTypeiDebug 0x69646267U // 'idbg' - no params -#define kDebugOutputTypeKPrintF 0x6B707266U // 'kprf' - no params -#define kDebugOutputTypeMacOSXIOLog 0x696C6F67U // 'ilog' - no params -#define kDebugOutputTypeMacOSXLog 0x786C6F67U // 'xlog' - no params -#define kDebugOutputTypeWindowsDebugger 0x77696E64U // 'wind' - no params -#define kDebugOutputTypeWindowsEventLog 0x7765766CU // 'wevl' - 1st param = C-string name, 2nd param = HMODULE or NULL. + + @abstract Type of debug output (i.e. where the output goes). + */ + +typedef uint32_t DebugOutputType; + +#define kDebugOutputTypeNone 0x6E6F6E65U // 'none' - no params +#define kDebugOutputTypeCustom 0x63757374U // 'cust' - 1st param = function ptr, 2nd param = context +#define kDebugOutputTypeFPrintF 0x66707269U // 'fpri' - 1st param = DebugOutputTypeFlags [, 2nd param = filename] +#define kDebugOutputTypeiDebug 0x69646267U // 'idbg' - no params +#define kDebugOutputTypeKPrintF 0x6B707266U // 'kprf' - no params +#define kDebugOutputTypeMacOSXIOLog 0x696C6F67U // 'ilog' - no params +#define kDebugOutputTypeMacOSXLog 0x786C6F67U // 'xlog' - no params +#define kDebugOutputTypeWindowsDebugger 0x77696E64U // 'wind' - no params +#define kDebugOutputTypeWindowsEventLog 0x7765766CU // 'wevl' - 1st param = C-string name, 2nd param = HMODULE or NULL. // Console meta output kind - Any kind of Console output (in horizontal order of preference): -// +// // Mac OS X = ANSI printf (viewable in Console.app) // Mac OS X Kernel = IOLog (/var/log/system.log) or kprintf (serial). // Windows = ANSI printf (Console window) or OutputDebugString (debugger). // Other = ANSI printf (viewer varies). -#define kDebugOutputTypeMetaConsole 0x434F4E53U // 'CONS' - no params +#define kDebugOutputTypeMetaConsole 0x434F4E53U // 'CONS' - no params //--------------------------------------------------------------------------------------------------------------------------- /*! @typedef DebugOutputTypeFlags - - @abstract Flags controlling how the output type is configured. - - @constant kDebugOutputTypeFlagsTypeMask Bit mask for the output type (e.g. stdout, stderr, file, etc.). - @constant kDebugOutputTypeFlagsStdOut fprintf should go to stdout. - @constant kDebugOutputTypeFlagsStdErr fprintf should go to stderr. - @constant kDebugOutputTypeFlagsFile fprintf should go to a specific file (filename passed as va_arg). -*/ - -typedef unsigned int DebugOutputTypeFlags; - -#define kDebugOutputTypeFlagsTypeMask 0xF -#define kDebugOutputTypeFlagsStdOut 1 -#define kDebugOutputTypeFlagsStdErr 2 -#define kDebugOutputTypeFlagsFile 10 + + @abstract Flags controlling how the output type is configured. + + @constant kDebugOutputTypeFlagsTypeMask Bit mask for the output type (e.g. stdout, stderr, file, etc.). + @constant kDebugOutputTypeFlagsStdOut fprintf should go to stdout. + @constant kDebugOutputTypeFlagsStdErr fprintf should go to stderr. + @constant kDebugOutputTypeFlagsFile fprintf should go to a specific file (filename passed as va_arg). + */ + +typedef unsigned int DebugOutputTypeFlags; + +#define kDebugOutputTypeFlagsTypeMask 0xF +#define kDebugOutputTypeFlagsStdOut 1 +#define kDebugOutputTypeFlagsStdErr 2 +#define kDebugOutputTypeFlagsFile 10 //--------------------------------------------------------------------------------------------------------------------------- /*! @typedef DebugOutputFunctionPtr - - @abstract Function ptr for a custom callback to print debug output. -*/ + + @abstract Function ptr for a custom callback to print debug output. + */ typedef void ( *DebugOutputFunctionPtr )( char *inData, size_t inSize, void *inContext ); @@ -221,48 +221,48 @@ typedef void ( *DebugOutputFunctionPtr )( char *inData, size_t inSize, void *inC //--------------------------------------------------------------------------------------------------------------------------- /*! @typedef DebugFlags - - @abstract Flags controlling how output is printed. -*/ - -typedef uint32_t DebugFlags; - -#define kDebugFlagsNone 0 -#define kDebugFlagsNoAddress ( 1 << 0 ) -#define kDebugFlagsNoOffset ( 1 << 1 ) -#define kDebugFlags32BitOffset ( 1 << 2 ) -#define kDebugFlagsNoASCII ( 1 << 3 ) -#define kDebugFlagsNoNewLine ( 1 << 4 ) -#define kDebugFlags8BitSeparator ( 1 << 5 ) -#define kDebugFlags16BitSeparator ( 1 << 6 ) -#define kDebugFlagsNo32BitSeparator ( 1 << 7 ) -#define kDebugFlagsNo16ByteHexPad ( 1 << 8 ) -#define kDebugFlagsNoByteCount ( 1 << 9 ) + + @abstract Flags controlling how output is printed. + */ + +typedef uint32_t DebugFlags; + +#define kDebugFlagsNone 0 +#define kDebugFlagsNoAddress ( 1 << 0 ) +#define kDebugFlagsNoOffset ( 1 << 1 ) +#define kDebugFlags32BitOffset ( 1 << 2 ) +#define kDebugFlagsNoASCII ( 1 << 3 ) +#define kDebugFlagsNoNewLine ( 1 << 4 ) +#define kDebugFlags8BitSeparator ( 1 << 5 ) +#define kDebugFlags16BitSeparator ( 1 << 6 ) +#define kDebugFlagsNo32BitSeparator ( 1 << 7 ) +#define kDebugFlagsNo16ByteHexPad ( 1 << 8 ) +#define kDebugFlagsNoByteCount ( 1 << 9 ) //--------------------------------------------------------------------------------------------------------------------------- /*! @enum DebugTaskLevelFlags - - @abstract Flags indicating the task level. -*/ + + @abstract Flags indicating the task level. + */ enum { - kDebugInterruptLevelShift = 0, - kDebugInterruptLevelMask = 0x00000007, - kDebugInVBLTaskMask = 0x00000010, - kDebugInDeferredTaskMask = 0x00000020, - kDebugInSecondaryInterruptHandlerMask = 0x00000040, - kDebugPageFaultFatalMask = 0x00000100, // There should be a "kPageFaultFatalMask" in Debugging.h. - kDebugMPTaskLevelMask = 0x00000200, // There should be a "kMPTaskLevelMask" in Debugging.h. - kDebugInterruptDepthShift = 16, - kDebugInterruptDepthMask = 0x00FF0000 + kDebugInterruptLevelShift = 0, + kDebugInterruptLevelMask = 0x00000007, + kDebugInVBLTaskMask = 0x00000010, + kDebugInDeferredTaskMask = 0x00000020, + kDebugInSecondaryInterruptHandlerMask = 0x00000040, + kDebugPageFaultFatalMask = 0x00000100, // There should be a "kPageFaultFatalMask" in Debugging.h. + kDebugMPTaskLevelMask = 0x00000200, // There should be a "kMPTaskLevelMask" in Debugging.h. + kDebugInterruptDepthShift = 16, + kDebugInterruptDepthMask = 0x00FF0000 }; -#define DebugExtractTaskLevelInterruptLevel( LEVEL ) \ - ( ( ( LEVEL ) & kDebugInterruptLevelMask ) >> kDebugInterruptLevelShift ) - -#define DebugExtractTaskLevelInterruptDepth( LEVEL ) \ - ( ( ( LEVEL ) & kDebugInterruptDepthMask ) >> kDebugInterruptDepthShift ) +#define DebugExtractTaskLevelInterruptLevel( LEVEL ) \ + ( ( ( LEVEL ) &kDebugInterruptLevelMask ) >> kDebugInterruptLevelShift ) + +#define DebugExtractTaskLevelInterruptDepth( LEVEL ) \ + ( ( ( LEVEL ) &kDebugInterruptDepthMask ) >> kDebugInterruptDepthShift ) #if 0 #pragma mark == Levels == @@ -274,60 +274,60 @@ enum //--------------------------------------------------------------------------------------------------------------------------- /*! @typedef DebugLevel - - @abstract Level used to control debug logging. -*/ -typedef int32_t DebugLevel; + @abstract Level used to control debug logging. + */ + +typedef int32_t DebugLevel; // Levels -#define kDebugLevelMask 0x0000FFFF -#define kDebugLevelChatty 100 -#define kDebugLevelVerbose 500 -#define kDebugLevelTrace 800 -#define kDebugLevelInfo 1000 -#define kDebugLevelNotice 3000 -#define kDebugLevelWarning 5000 -#define kDebugLevelAssert 6000 -#define kDebugLevelRequire 7000 -#define kDebugLevelError 8000 -#define kDebugLevelCritical 9000 -#define kDebugLevelAlert 10000 -#define kDebugLevelEmergency 11000 -#define kDebugLevelTragic 12000 -#define kDebugLevelMax 0x0000FFFF +#define kDebugLevelMask 0x0000FFFF +#define kDebugLevelChatty 100 +#define kDebugLevelVerbose 500 +#define kDebugLevelTrace 800 +#define kDebugLevelInfo 1000 +#define kDebugLevelNotice 3000 +#define kDebugLevelWarning 5000 +#define kDebugLevelAssert 6000 +#define kDebugLevelRequire 7000 +#define kDebugLevelError 8000 +#define kDebugLevelCritical 9000 +#define kDebugLevelAlert 10000 +#define kDebugLevelEmergency 11000 +#define kDebugLevelTragic 12000 +#define kDebugLevelMax 0x0000FFFF // Level Flags - -#define kDebugLevelFlagMask 0xFFFF0000 -#define kDebugLevelFlagStackTrace 0x00010000 -#define kDebugLevelFlagDebugBreak 0x00020000 + +#define kDebugLevelFlagMask 0xFFFF0000 +#define kDebugLevelFlagStackTrace 0x00010000 +#define kDebugLevelFlagDebugBreak 0x00020000 //--------------------------------------------------------------------------------------------------------------------------- /*! @typedef LogLevel - - @abstract Level used to control which events are logged. -*/ - -typedef int32_t LogLevel; - -#define kLogLevelUninitialized -1L -#define kLogLevelAll 0L -#define kLogLevelChatty 100L -#define kLogLevelVerbose 500L -#define kLogLevelTrace 800L -#define kLogLevelInfo 1000L -#define kLogLevelNotice 3000L -#define kLogLevelWarning 4000L -#define kLogLevelAssert 6000L -#define kLogLevelRequire 7000L -#define kLogLevelError 8000L -#define kLogLevelCritical 9000L -#define kLogLevelAlert 10000L -#define kLogLevelEmergency 11000L -#define kLogLevelTragic 12000L -#define kLogLevelOff 0x0000FFFEL + + @abstract Level used to control which events are logged. + */ + +typedef int32_t LogLevel; + +#define kLogLevelUninitialized -1L +#define kLogLevelAll 0L +#define kLogLevelChatty 100L +#define kLogLevelVerbose 500L +#define kLogLevelTrace 800L +#define kLogLevelInfo 1000L +#define kLogLevelNotice 3000L +#define kLogLevelWarning 4000L +#define kLogLevelAssert 6000L +#define kLogLevelRequire 7000L +#define kLogLevelError 8000L +#define kLogLevelCritical 9000L +#define kLogLevelAlert 10000L +#define kLogLevelEmergency 11000L +#define kLogLevelTragic 12000L +#define kLogLevelOff 0x0000FFFEL #if 0 #pragma mark == Properties == @@ -335,140 +335,140 @@ typedef int32_t LogLevel; //--------------------------------------------------------------------------------------------------------------------------- /*! @typedef DebugPropertyTag - - @abstract Tag for properties. -*/ -typedef uint32_t DebugPropertyTag; + @abstract Tag for properties. + */ + +typedef uint32_t DebugPropertyTag; -#define kDebugPropertyTagPrintLevelMin 0x6D696E70U // 'minp' Get: 1st param = DebugLevel * - // Set: 1st param = DebugLevel +#define kDebugPropertyTagPrintLevelMin 0x6D696E70U // 'minp' Get: 1st param = DebugLevel * + // Set: 1st param = DebugLevel -#define kDebugPropertyTagPrintLevel kDebugPropertyTagPrintLevelMin +#define kDebugPropertyTagPrintLevel kDebugPropertyTagPrintLevelMin -#define kDebugPropertyTagPrintLevelMax 0x706D786CU // 'maxp' Get: 1st param = DebugLevel * - // Set: 1st param = DebugLevel +#define kDebugPropertyTagPrintLevelMax 0x706D786CU // 'maxp' Get: 1st param = DebugLevel * + // Set: 1st param = DebugLevel -#define kDebugPropertyTagBreakLevel 0x62726B6CU // 'brkl' Get: 1st param = DebugLevel * - // Set: 1st param = DebugLevel +#define kDebugPropertyTagBreakLevel 0x62726B6CU // 'brkl' Get: 1st param = DebugLevel * + // Set: 1st param = DebugLevel #if 0 #pragma mark == General macros == #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined DEBUG_UNUSED - - @abstract Macro to mark a paramter as unused to avoid unused parameter warnings. - - @discussion - - There is no universally supported pragma/attribute for indicating a variable is unused. DEBUG_UNUSED lets us - indicate a variable is unused in a manner that is supported by most compilers. -*/ -#define DEBUG_UNUSED( X ) (void)( X ) + @abstract Macro to mark a paramter as unused to avoid unused parameter warnings. + + @discussion + + There is no universally supported pragma/attribute for indicating a variable is unused. DEBUG_UNUSED lets us + indicate a variable is unused in a manner that is supported by most compilers. + */ + +#define DEBUG_UNUSED( X ) (void)( X ) //--------------------------------------------------------------------------------------------------------------------------- /*! @defined DEBUG_USE_ONLY - - @abstract Macro to mark a variable as used only when debugging is enabled. - - @discussion - - Variables are sometimes needed only for debugging. When debugging is turned off, these debug-only variables generate - compiler warnings about unused variables. To eliminate these warnings, use these macros to indicate variables that - are only used for debugging. -*/ - -#if( DEBUG ) - #define DEBUG_USE_ONLY( X ) + + @abstract Macro to mark a variable as used only when debugging is enabled. + + @discussion + + Variables are sometimes needed only for debugging. When debugging is turned off, these debug-only variables generate + compiler warnings about unused variables. To eliminate these warnings, use these macros to indicate variables that + are only used for debugging. + */ + +#if ( DEBUG ) + #define DEBUG_USE_ONLY( X ) #else - #define DEBUG_USE_ONLY( X ) (void)( X ) + #define DEBUG_USE_ONLY( X ) (void)( X ) #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined DEBUG_LOCAL - - @abstract Macros to make variables and functions static when debugging is off, but extern when debugging is on. - - @discussion - - Rather than using "static" directly, using this macros allows you to access these variables external while - debugging without being penalized for production builds. -*/ - -#if( DEBUG ) - #define DEBUG_LOCAL + + @abstract Macros to make variables and functions static when debugging is off, but extern when debugging is on. + + @discussion + + Rather than using "static" directly, using this macros allows you to access these variables external while + debugging without being penalized for production builds. + */ + +#if ( DEBUG ) + #define DEBUG_LOCAL #else - #define DEBUG_LOCAL static + #define DEBUG_LOCAL static #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined DEBUG_STATIC - - @abstract Macros to make variables and functions static when debugging is off, but extern when debugging is on. - - @discussion - - Rather than using "static" directly, using this macros allows you to access these variables external while - debugging without being penalized for production builds. -*/ - -#if( DEBUG ) - #define DEBUG_STATIC + + @abstract Macros to make variables and functions static when debugging is off, but extern when debugging is on. + + @discussion + + Rather than using "static" directly, using this macros allows you to access these variables external while + debugging without being penalized for production builds. + */ + +#if ( DEBUG ) + #define DEBUG_STATIC #else - #define DEBUG_STATIC static + #define DEBUG_STATIC static #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined DEBUG_EXPORT - - @abstract Macros to export variables. - - @discussion - - "__private_extern__" is a hack for IOKit to allow symbols to be exported from compilation units, but - // not exported outside a driver (IOKit uses a lame global namespace for symbols). This still does not - // solve the problem of multiple drivers in the same dependency chain since they share symbols. -*/ - -#if( TARGET_API_MAC_OSX_KERNEL ) - #define DEBUG_EXPORT __private_extern__ + + @abstract Macros to export variables. + + @discussion + + "__private_extern__" is a hack for IOKit to allow symbols to be exported from compilation units, but + // not exported outside a driver (IOKit uses a lame global namespace for symbols). This still does not + // solve the problem of multiple drivers in the same dependency chain since they share symbols. + */ + +#if ( TARGET_API_MAC_OSX_KERNEL ) + #define DEBUG_EXPORT __private_extern__ #else - #define DEBUG_EXPORT extern + #define DEBUG_EXPORT extern #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined debug_add - - @abstract Macro to add (or subtract if negative) a value when debugging is on. Does nothing if debugging is off. -*/ -#if( DEBUG ) - #define debug_add( A, B ) ( A ) += ( B ) + @abstract Macro to add (or subtract if negative) a value when debugging is on. Does nothing if debugging is off. + */ + +#if ( DEBUG ) + #define debug_add( A, B ) ( A ) += ( B ) #else - #define debug_add( A, B ) + #define debug_add( A, B ) #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined debug_perform - - @abstract Macro to perform something in debug-only builds. -*/ -#if( DEBUG ) - #define debug_perform( X ) do { X; } while( 0 ) + @abstract Macro to perform something in debug-only builds. + */ + +#if ( DEBUG ) + #define debug_perform( X ) do { X; } while( 0 ) #else - #define debug_perform( X ) + #define debug_perform( X ) #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @function translate_errno - @abstract Returns 0 if the test success. If the test fails, returns errno if non-zero and othewise the alternate error. -*/ + @abstract Returns 0 if the test success. If the test fails, returns errno if non-zero and othewise the alternate error. + */ -#define translate_errno( TEST, ERRNO, ALTERNATE_ERROR ) ( ( TEST ) ? 0 : ( ERRNO ) ? ( ERRNO ) : ( ALTERNATE_ERROR ) ) +#define translate_errno( TEST, ERRNO, ALTERNATE_ERROR ) ( ( TEST ) ? 0 : ( ERRNO ) ? ( ERRNO ) : ( ALTERNATE_ERROR ) ) #if 0 #pragma mark == Compile Time macros == @@ -476,58 +476,58 @@ typedef uint32_t DebugPropertyTag; //--------------------------------------------------------------------------------------------------------------------------- /*! @defined check_compile_time - - @abstract Performs a compile-time check of something such as the size of an int. - - @discussion - - This declares an array with a size that is determined by a compile-time expression. If the expression evaluates - to 0, the array has a size of -1, which is illegal and generates a compile-time error. - - For example: - - check_compile_time( sizeof( int ) == 4 ); - - Note: This only works with compile-time expressions. - Note: This only works in places where extern declarations are allowed (e.g. global scope). - - References: - - - - - Note: The following macros differ from the macros on the www.jaggersoft.com web site because those versions do not - work with GCC due to GCC allow a zero-length array. Using a -1 condition turned out to be more portable. -*/ - -#define check_compile_time( X ) extern int debug_compile_time_name[ ( X ) ? 1 : -1 ] + + @abstract Performs a compile-time check of something such as the size of an int. + + @discussion + + This declares an array with a size that is determined by a compile-time expression. If the expression evaluates + to 0, the array has a size of -1, which is illegal and generates a compile-time error. + + For example: + + check_compile_time( sizeof( int ) == 4 ); + + Note: This only works with compile-time expressions. + Note: This only works in places where extern declarations are allowed (e.g. global scope). + + References: + + + + + Note: The following macros differ from the macros on the www.jaggersoft.com web site because those versions do not + work with GCC due to GCC allow a zero-length array. Using a -1 condition turned out to be more portable. + */ + +#define check_compile_time( X ) extern int debug_compile_time_name[ ( X ) ? 1 : -1 ] //--------------------------------------------------------------------------------------------------------------------------- /*! @defined check_compile_time_code - - @abstract Perform a compile-time check, suitable for placement in code, of something such as the size of an int. - - @discussion - - This creates a switch statement with an existing case for 0 and an additional case using the result of a - compile-time expression. A switch statement cannot have two case labels with the same constant so if the - compile-time expression evaluates to 0, it is illegal and generates a compile-time error. If the compile-time - expression does not evaluate to 0, the resulting value is used as the case label and it compiles without error. - - For example: - - check_compile_time_code( sizeof( int ) == 4 ); - - Note: This only works with compile-time expressions. - Note: This does not work in a global scope so it must be inside a function. - - References: - - - -*/ - -#define check_compile_time_code( X ) switch( 0 ) { case 0: case X:; } + + @abstract Perform a compile-time check, suitable for placement in code, of something such as the size of an int. + + @discussion + + This creates a switch statement with an existing case for 0 and an additional case using the result of a + compile-time expression. A switch statement cannot have two case labels with the same constant so if the + compile-time expression evaluates to 0, it is illegal and generates a compile-time error. If the compile-time + expression does not evaluate to 0, the resulting value is used as the case label and it compiles without error. + + For example: + + check_compile_time_code( sizeof( int ) == 4 ); + + Note: This only works with compile-time expressions. + Note: This does not work in a global scope so it must be inside a function. + + References: + + + + */ + +#define check_compile_time_code( X ) switch( 0 ) { case 0: case X:; } #if 0 #pragma mark == check macros == @@ -535,182 +535,182 @@ typedef uint32_t DebugPropertyTag; //--------------------------------------------------------------------------------------------------------------------------- /*! @defined check - - @abstract Check that an expression is true (non-zero). - - @discussion - - If expression evalulates to false, this prints debugging information (actual expression string, file, line number, - function name, etc.) using the default debugging output method. - - Code inside check() statements is not compiled into production builds. -*/ - -#if( DEBUG_OVERRIDE_APPLE_MACROS ) - #undef check -#endif -#if( !defined( check ) ) - #if( DEBUG ) - #define check( X ) \ - do \ - { \ - if( !( X ) ) \ - { \ - debug_print_assert( 0, #X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ - } \ - } while( 0 ) - #else - #define check( X ) - #endif + + @abstract Check that an expression is true (non-zero). + + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method. + + Code inside check() statements is not compiled into production builds. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef check +#endif +#if ( !defined( check ) ) + #if ( DEBUG ) + #define check( X ) \ + do \ + { \ + if( !( X ) ) \ + { \ + debug_print_assert( 0, # X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + } \ + } while( 0 ) + #else + #define check( X ) + #endif #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined check_string - - @abstract Check that an expression is true (non-zero) with an explanation. - - @discussion - - If expression evalulates to false, this prints debugging information (actual expression string, file, line number, - function name, etc.) and a custom explanation string using the default debugging output method. - - Code inside check_string() statements is not compiled into production builds. -*/ - -#if( DEBUG_OVERRIDE_APPLE_MACROS ) - #undef check_string -#endif -#if( !defined( check_string ) ) - #if( DEBUG ) - #define check_string( X, STR ) \ - do \ - { \ - if( !( X ) ) \ - { \ - debug_print_assert( 0, #X, STR, __FILE__, __LINE__, __ROUTINE__ ); \ - } \ - \ - } while( 0 ) - #else - #define check_string( X, STR ) - #endif + + @abstract Check that an expression is true (non-zero) with an explanation. + + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) and a custom explanation string using the default debugging output method. + + Code inside check_string() statements is not compiled into production builds. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef check_string +#endif +#if ( !defined( check_string ) ) + #if ( DEBUG ) + #define check_string( X, STR ) \ + do \ + { \ + if( !( X ) ) \ + { \ + debug_print_assert( 0, # X, STR, __FILE__, __LINE__, __ROUTINE__ ); \ + } \ + \ + } while( 0 ) + #else + #define check_string( X, STR ) + #endif #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined check_noerr - - @abstract Check that an error code is noErr (0). - - @discussion - - If the error code is non-0, this prints debugging information (actual expression string, file, line number, - function name, etc.) using the default debugging output method. - - Code inside check_noerr() statements is not compiled into production builds. -*/ - -#if( DEBUG_OVERRIDE_APPLE_MACROS ) - #undef check_noerr -#endif -#if( !defined( check_noerr ) ) - #if( DEBUG ) - #define check_noerr( ERR ) \ - do \ - { \ - int_least32_t localErr; \ - \ - localErr = (int_least32_t)( ERR ); \ - if( localErr != 0 ) \ - { \ - debug_print_assert( localErr, NULL, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ - } \ - \ - } while( 0 ) - #else - #define check_noerr( ERR ) - #endif + + @abstract Check that an error code is noErr (0). + + @discussion + + If the error code is non-0, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method. + + Code inside check_noerr() statements is not compiled into production builds. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef check_noerr +#endif +#if ( !defined( check_noerr ) ) + #if ( DEBUG ) + #define check_noerr( ERR ) \ + do \ + { \ + int_least32_t localErr; \ + \ + localErr = (int_least32_t)( ERR ); \ + if( localErr != 0 ) \ + { \ + debug_print_assert( localErr, NULL, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + } \ + \ + } while( 0 ) + #else + #define check_noerr( ERR ) + #endif #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined check_noerr_string - - @abstract Check that an error code is noErr (0) with an explanation. - - @discussion - - If the error code is non-0, this prints debugging information (actual expression string, file, line number, - function name, etc.) and a custom explanation string using the default debugging output method. - - Code inside check_noerr_string() statements is not compiled into production builds. -*/ - -#if( DEBUG_OVERRIDE_APPLE_MACROS ) - #undef check_noerr_string -#endif -#if( !defined( check_noerr_string ) ) - #if( DEBUG ) - #define check_noerr_string( ERR, STR ) \ - do \ - { \ - int_least32_t localErr; \ - \ - localErr = (int_least32_t)( ERR ); \ - if( localErr != 0 ) \ - { \ - debug_print_assert( localErr, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \ - } \ - \ - } while( 0 ) - #else - #define check_noerr_string( ERR, STR ) - #endif + + @abstract Check that an error code is noErr (0) with an explanation. + + @discussion + + If the error code is non-0, this prints debugging information (actual expression string, file, line number, + function name, etc.) and a custom explanation string using the default debugging output method. + + Code inside check_noerr_string() statements is not compiled into production builds. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef check_noerr_string +#endif +#if ( !defined( check_noerr_string ) ) + #if ( DEBUG ) + #define check_noerr_string( ERR, STR ) \ + do \ + { \ + int_least32_t localErr; \ + \ + localErr = (int_least32_t)( ERR ); \ + if( localErr != 0 ) \ + { \ + debug_print_assert( localErr, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \ + } \ + \ + } while( 0 ) + #else + #define check_noerr_string( ERR, STR ) + #endif #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined check_translated_errno - - @abstract Check a condition and prints errno (if non-zero) to the log. - - @discussion - - Code inside check_translated_errno() statements is not compiled into production builds. -*/ - -#if( !defined( check_translated_errno ) ) - #if( DEBUG ) - #define check_translated_errno( TEST, ERRNO, ALTERNATE_ERROR ) \ - do \ - { \ - if( !( TEST ) ) \ - { \ - int_least32_t localErr; \ - \ - localErr = (int_least32_t)( ERRNO ); \ - localErr = ( localErr != 0 ) ? localErr : (int_least32_t)( ALTERNATE_ERROR ); \ - debug_print_assert( localErr, #TEST, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ - } \ - \ - } while( 0 ) - #else - #define check_translated_errno( TEST, ERRNO, ALTERNATE_ERROR ) - #endif + + @abstract Check a condition and prints errno (if non-zero) to the log. + + @discussion + + Code inside check_translated_errno() statements is not compiled into production builds. + */ + +#if ( !defined( check_translated_errno ) ) + #if ( DEBUG ) + #define check_translated_errno( TEST, ERRNO, ALTERNATE_ERROR ) \ + do \ + { \ + if( !( TEST ) ) \ + { \ + int_least32_t localErr; \ + \ + localErr = (int_least32_t)( ERRNO ); \ + localErr = ( localErr != 0 ) ? localErr : (int_least32_t)( ALTERNATE_ERROR ); \ + debug_print_assert( localErr, # TEST, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + } \ + \ + } while( 0 ) + #else + #define check_translated_errno( TEST, ERRNO, ALTERNATE_ERROR ) + #endif #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined check_ptr_overlap - - @abstract Checks that two ptrs do not overlap. -*/ - -#define check_ptr_overlap( P1, P1_SIZE, P2, P2_SIZE ) \ - do \ - { \ - check( !( ( (uintptr_t)( P1 ) >= (uintptr_t)( P2 ) ) && \ - ( (uintptr_t)( P1 ) < ( ( (uintptr_t)( P2 ) ) + ( P2_SIZE ) ) ) ) ); \ - check( !( ( (uintptr_t)( P2 ) >= (uintptr_t)( P1 ) ) && \ - ( (uintptr_t)( P2 ) < ( ( (uintptr_t)( P1 ) ) + ( P1_SIZE ) ) ) ) ); \ - \ - } while( 0 ) + + @abstract Checks that two ptrs do not overlap. + */ + +#define check_ptr_overlap( P1, P1_SIZE, P2, P2_SIZE ) \ + do \ + { \ + check( !( ( (uintptr_t)( P1 ) >= (uintptr_t)( P2 ) ) && \ + ( (uintptr_t)( P1 ) < ( ( (uintptr_t)( P2 ) ) + ( P2_SIZE ) ) ) ) ); \ + check( !( ( (uintptr_t)( P2 ) >= (uintptr_t)( P1 ) ) && \ + ( (uintptr_t)( P2 ) < ( ( (uintptr_t)( P1 ) ) + ( P1_SIZE ) ) ) ) ); \ + \ + } while( 0 ) #if 0 #pragma mark == require macros == @@ -718,364 +718,364 @@ typedef uint32_t DebugPropertyTag; //--------------------------------------------------------------------------------------------------------------------------- /*! @defined require - - @abstract Requires that an expression evaluate to true. - - @discussion - - If expression evalulates to false, this prints debugging information (actual expression string, file, line number, - function name, etc.) using the default debugging output method then jumps to a label. -*/ - -#if( DEBUG_OVERRIDE_APPLE_MACROS ) - #undef require -#endif -#if( !defined( require ) ) - #define require( X, LABEL ) \ - do \ - { \ - if( !( X ) ) \ - { \ - debug_print_assert( 0, #X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ - goto LABEL; \ - } \ - \ - } while( 0 ) + + @abstract Requires that an expression evaluate to true. + + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method then jumps to a label. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require +#endif +#if ( !defined( require ) ) + #define require( X, LABEL ) \ + do \ + { \ + if( !( X ) ) \ + { \ + debug_print_assert( 0, # X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + goto LABEL; \ + } \ + \ + } while( 0 ) #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined require_string - - @abstract Requires that an expression evaluate to true with an explanation. - - @discussion - - If expression evalulates to false, this prints debugging information (actual expression string, file, line number, - function name, etc.) and a custom explanation string using the default debugging output method then jumps to a label. -*/ - -#if( DEBUG_OVERRIDE_APPLE_MACROS ) - #undef require_string -#endif -#if( !defined( require_string ) ) - #define require_string( X, LABEL, STR ) \ - do \ - { \ - if( !( X ) ) \ - { \ - debug_print_assert( 0, #X, STR, __FILE__, __LINE__, __ROUTINE__ ); \ - goto LABEL; \ - } \ - \ - } while( 0 ) + + @abstract Requires that an expression evaluate to true with an explanation. + + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) and a custom explanation string using the default debugging output method then jumps to a label. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_string +#endif +#if ( !defined( require_string ) ) + #define require_string( X, LABEL, STR ) \ + do \ + { \ + if( !( X ) ) \ + { \ + debug_print_assert( 0, # X, STR, __FILE__, __LINE__, __ROUTINE__ ); \ + goto LABEL; \ + } \ + \ + } while( 0 ) #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined require_quiet - - @abstract Requires that an expression evaluate to true. - - @discussion - - If expression evalulates to false, this jumps to a label. No debugging information is printed. -*/ - -#if( DEBUG_OVERRIDE_APPLE_MACROS ) - #undef require_quiet -#endif -#if( !defined( require_quiet ) ) - #define require_quiet( X, LABEL ) \ - do \ - { \ - if( !( X ) ) \ - { \ - goto LABEL; \ - } \ - \ - } while( 0 ) + + @abstract Requires that an expression evaluate to true. + + @discussion + + If expression evalulates to false, this jumps to a label. No debugging information is printed. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_quiet +#endif +#if ( !defined( require_quiet ) ) + #define require_quiet( X, LABEL ) \ + do \ + { \ + if( !( X ) ) \ + { \ + goto LABEL; \ + } \ + \ + } while( 0 ) #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined require_noerr - - @abstract Require that an error code is noErr (0). - - @discussion - - If the error code is non-0, this prints debugging information (actual expression string, file, line number, - function name, etc.) using the default debugging output method then jumps to a label. -*/ - -#if( DEBUG_OVERRIDE_APPLE_MACROS ) - #undef require_noerr -#endif -#if( !defined( require_noerr ) ) - #define require_noerr( ERR, LABEL ) \ - do \ - { \ - int_least32_t localErr; \ - \ - localErr = (int_least32_t)( ERR ); \ - if( localErr != 0 ) \ - { \ - debug_print_assert( localErr, NULL, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ - goto LABEL; \ - } \ - \ - } while( 0 ) + + @abstract Require that an error code is noErr (0). + + @discussion + + If the error code is non-0, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method then jumps to a label. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_noerr +#endif +#if ( !defined( require_noerr ) ) + #define require_noerr( ERR, LABEL ) \ + do \ + { \ + int_least32_t localErr; \ + \ + localErr = (int_least32_t)( ERR ); \ + if( localErr != 0 ) \ + { \ + debug_print_assert( localErr, NULL, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + goto LABEL; \ + } \ + \ + } while( 0 ) #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined require_noerr_string - - @abstract Require that an error code is noErr (0). - - @discussion - - If the error code is non-0, this prints debugging information (actual expression string, file, line number, - function name, etc.), and a custom explanation string using the default debugging output method using the - default debugging output method then jumps to a label. -*/ - -#if( DEBUG_OVERRIDE_APPLE_MACROS ) - #undef require_noerr_string -#endif -#if( !defined( require_noerr_string ) ) - #define require_noerr_string( ERR, LABEL, STR ) \ - do \ - { \ - int_least32_t localErr; \ - \ - localErr = (int_least32_t)( ERR ); \ - if( localErr != 0 ) \ - { \ - debug_print_assert( localErr, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \ - goto LABEL; \ - } \ - \ - } while( 0 ) + + @abstract Require that an error code is noErr (0). + + @discussion + + If the error code is non-0, this prints debugging information (actual expression string, file, line number, + function name, etc.), and a custom explanation string using the default debugging output method using the + default debugging output method then jumps to a label. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_noerr_string +#endif +#if ( !defined( require_noerr_string ) ) + #define require_noerr_string( ERR, LABEL, STR ) \ + do \ + { \ + int_least32_t localErr; \ + \ + localErr = (int_least32_t)( ERR ); \ + if( localErr != 0 ) \ + { \ + debug_print_assert( localErr, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \ + goto LABEL; \ + } \ + \ + } while( 0 ) #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined require_noerr_action_string - - @abstract Require that an error code is noErr (0). - - @discussion - - If the error code is non-0, this prints debugging information (actual expression string, file, line number, - function name, etc.), and a custom explanation string using the default debugging output method using the - default debugging output method then executes an action and jumps to a label. -*/ - -#if( DEBUG_OVERRIDE_APPLE_MACROS ) - #undef require_noerr_action_string -#endif -#if( !defined( require_noerr_action_string ) ) - #define require_noerr_action_string( ERR, LABEL, ACTION, STR ) \ - do \ - { \ - int_least32_t localErr; \ - \ - localErr = (int_least32_t)( ERR ); \ - if( localErr != 0 ) \ - { \ - debug_print_assert( localErr, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \ - { ACTION; } \ - goto LABEL; \ - } \ - \ - } while( 0 ) + + @abstract Require that an error code is noErr (0). + + @discussion + + If the error code is non-0, this prints debugging information (actual expression string, file, line number, + function name, etc.), and a custom explanation string using the default debugging output method using the + default debugging output method then executes an action and jumps to a label. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_noerr_action_string +#endif +#if ( !defined( require_noerr_action_string ) ) + #define require_noerr_action_string( ERR, LABEL, ACTION, STR ) \ + do \ + { \ + int_least32_t localErr; \ + \ + localErr = (int_least32_t)( ERR ); \ + if( localErr != 0 ) \ + { \ + debug_print_assert( localErr, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 0 ) #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined require_noerr_quiet - - @abstract Require that an error code is noErr (0). - - @discussion - - If the error code is non-0, this jumps to a label. No debugging information is printed. -*/ - -#if( DEBUG_OVERRIDE_APPLE_MACROS ) - #undef require_noerr_quiet -#endif -#if( !defined( require_noerr_quiet ) ) - #define require_noerr_quiet( ERR, LABEL ) \ - do \ - { \ - if( ( ERR ) != 0 ) \ - { \ - goto LABEL; \ - } \ - \ - } while( 0 ) + + @abstract Require that an error code is noErr (0). + + @discussion + + If the error code is non-0, this jumps to a label. No debugging information is printed. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_noerr_quiet +#endif +#if ( !defined( require_noerr_quiet ) ) + #define require_noerr_quiet( ERR, LABEL ) \ + do \ + { \ + if( ( ERR ) != 0 ) \ + { \ + goto LABEL; \ + } \ + \ + } while( 0 ) #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined require_noerr_action - - @abstract Require that an error code is noErr (0) with an action to execute otherwise. - - @discussion - - If the error code is non-0, this prints debugging information (actual expression string, file, line number, - function name, etc.) using the default debugging output method then executes an action and jumps to a label. -*/ - -#if( DEBUG_OVERRIDE_APPLE_MACROS ) - #undef require_noerr_action -#endif -#if( !defined( require_noerr_action ) ) - #define require_noerr_action( ERR, LABEL, ACTION ) \ - do \ - { \ - int_least32_t localErr; \ - \ - localErr = (int_least32_t)( ERR ); \ - if( localErr != 0 ) \ - { \ - debug_print_assert( localErr, NULL, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ - { ACTION; } \ - goto LABEL; \ - } \ - \ - } while( 0 ) + + @abstract Require that an error code is noErr (0) with an action to execute otherwise. + + @discussion + + If the error code is non-0, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method then executes an action and jumps to a label. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_noerr_action +#endif +#if ( !defined( require_noerr_action ) ) + #define require_noerr_action( ERR, LABEL, ACTION ) \ + do \ + { \ + int_least32_t localErr; \ + \ + localErr = (int_least32_t)( ERR ); \ + if( localErr != 0 ) \ + { \ + debug_print_assert( localErr, NULL, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 0 ) #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined require_noerr_action_quiet - - @abstract Require that an error code is noErr (0) with an action to execute otherwise. - - @discussion - - If the error code is non-0, this executes an action and jumps to a label. No debugging information is printed. -*/ - -#if( DEBUG_OVERRIDE_APPLE_MACROS ) - #undef require_noerr_action_quiet -#endif -#if( !defined( require_noerr_action_quiet ) ) - #define require_noerr_action_quiet( ERR, LABEL, ACTION ) \ - do \ - { \ - if( ( ERR ) != 0 ) \ - { \ - { ACTION; } \ - goto LABEL; \ - } \ - \ - } while( 0 ) + + @abstract Require that an error code is noErr (0) with an action to execute otherwise. + + @discussion + + If the error code is non-0, this executes an action and jumps to a label. No debugging information is printed. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_noerr_action_quiet +#endif +#if ( !defined( require_noerr_action_quiet ) ) + #define require_noerr_action_quiet( ERR, LABEL, ACTION ) \ + do \ + { \ + if( ( ERR ) != 0 ) \ + { \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 0 ) #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined require_action - - @abstract Requires that an expression evaluate to true with an action to execute otherwise. - - @discussion - - If expression evalulates to false, this prints debugging information (actual expression string, file, line number, - function name, etc.) using the default debugging output method then executes an action and jumps to a label. -*/ - -#if( DEBUG_OVERRIDE_APPLE_MACROS ) - #undef require_action -#endif -#if( !defined( require_action ) ) - #define require_action( X, LABEL, ACTION ) \ - do \ - { \ - if( !( X ) ) \ - { \ - debug_print_assert( 0, #X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ - { ACTION; } \ - goto LABEL; \ - } \ - \ - } while( 0 ) + + @abstract Requires that an expression evaluate to true with an action to execute otherwise. + + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method then executes an action and jumps to a label. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_action +#endif +#if ( !defined( require_action ) ) + #define require_action( X, LABEL, ACTION ) \ + do \ + { \ + if( !( X ) ) \ + { \ + debug_print_assert( 0, # X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 0 ) #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined require_action_quiet - - @abstract Requires that an expression evaluate to true with an action to execute otherwise. - - @discussion - - If expression evalulates to false, this executes an action and jumps to a label. No debugging information is printed. -*/ - -#if( DEBUG_OVERRIDE_APPLE_MACROS ) - #undef require_action_quiet -#endif -#if( !defined( require_action_quiet ) ) - #define require_action_quiet( X, LABEL, ACTION ) \ - do \ - { \ - if( !( X ) ) \ - { \ - { ACTION; } \ - goto LABEL; \ - } \ - \ - } while( 0 ) + + @abstract Requires that an expression evaluate to true with an action to execute otherwise. + + @discussion + + If expression evalulates to false, this executes an action and jumps to a label. No debugging information is printed. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_action_quiet +#endif +#if ( !defined( require_action_quiet ) ) + #define require_action_quiet( X, LABEL, ACTION ) \ + do \ + { \ + if( !( X ) ) \ + { \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 0 ) #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined require_action_string - - @abstract Requires that an expression evaluate to true with an explanation and action to execute otherwise. - - @discussion - - If expression evalulates to false, this prints debugging information (actual expression string, file, line number, - function name, etc.) and a custom explanation string using the default debugging output method then executes an - action and jumps to a label. -*/ - -#if( DEBUG_OVERRIDE_APPLE_MACROS ) - #undef require_action_string -#endif -#if( !defined( require_action_string ) ) - #define require_action_string( X, LABEL, ACTION, STR ) \ - do \ - { \ - if( !( X ) ) \ - { \ - debug_print_assert( 0, #X, STR, __FILE__, __LINE__, __ROUTINE__ ); \ - { ACTION; } \ - goto LABEL; \ - } \ - \ - } while( 0 ) + + @abstract Requires that an expression evaluate to true with an explanation and action to execute otherwise. + + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) and a custom explanation string using the default debugging output method then executes an + action and jumps to a label. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_action_string +#endif +#if ( !defined( require_action_string ) ) + #define require_action_string( X, LABEL, ACTION, STR ) \ + do \ + { \ + if( !( X ) ) \ + { \ + debug_print_assert( 0, # X, STR, __FILE__, __LINE__, __ROUTINE__ ); \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 0 ) #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined require_throw - - @abstract Requires that an expression evaluates to true or an exception is thrown. - - @discussion - - If the expression evaluates to false, this prints debugging information (actual expression string, file, - line number, function name, etc.) using the default debugging output method then throws an exception. -*/ - -#if( defined( __cplusplus ) ) - #define require_throw( X ) \ - do \ - { \ - if( !( X ) ) \ - { \ - debug_print_assert( 0, #X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ - throw kUnknownErr; \ - } \ - \ - } while( 0 ) + + @abstract Requires that an expression evaluates to true or an exception is thrown. + + @discussion + + If the expression evaluates to false, this prints debugging information (actual expression string, file, + line number, function name, etc.) using the default debugging output method then throws an exception. + */ + +#if ( defined( __cplusplus ) ) + #define require_throw( X ) \ + do \ + { \ + if( !( X ) ) \ + { \ + debug_print_assert( 0, # X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + throw kUnknownErr; \ + } \ + \ + } while( 0 ) #endif #if 0 @@ -1086,11 +1086,11 @@ typedef uint32_t DebugPropertyTag; // Design-By-Contract macros //=========================================================================================================================== -#define ensure( X ) check( X ) -#define ensure_string( X, STR ) check_string( X, STR ) -#define ensure_noerr( ERR ) check_noerr( ERR ) -#define ensure_noerr_string( ERR, STR ) check_noerr_string( ERR, STR ) -#define ensure_translated_errno( TEST, ERRNO, ALTERNATE_ERROR ) check_translated_errno( TEST, ERRNO, ALTERNATE_ERROR ) +#define ensure( X ) check( X ) +#define ensure_string( X, STR ) check_string( X, STR ) +#define ensure_noerr( ERR ) check_noerr( ERR ) +#define ensure_noerr_string( ERR, STR ) check_noerr_string( ERR, STR ) +#define ensure_translated_errno( TEST, ERRNO, ALTERNATE_ERROR ) check_translated_errno( TEST, ERRNO, ALTERNATE_ERROR ) // Note: Design-By-Contract "require" macros are already defined elsewhere. @@ -1102,37 +1102,37 @@ typedef uint32_t DebugPropertyTag; // Expect macros //=========================================================================================================================== -// Expect macros allow code to include runtime checking of things that should not happen in shipping code (e.g. internal -// programmer errors, such as a NULL parameter where it is not allowed). Once the code has been verified to work correctly -// without asserting, the DEBUG_EXPECT_VERIFIED conditional can be set to eliminate the error checking entirely. It can +// Expect macros allow code to include runtime checking of things that should not happen in shipping code (e.g. internal +// programmer errors, such as a NULL parameter where it is not allowed). Once the code has been verified to work correctly +// without asserting, the DEBUG_EXPECT_VERIFIED conditional can be set to eliminate the error checking entirely. It can // also be useful to measure the cost of error checking code by profiling with it enable and with it disabled. -#if( DEBUG_EXPECT_VERIFIED ) - #define require_expect - #define require_string_expect - #define require_quiet_expect - #define require_noerr_expect - #define require_noerr_string_expect - #define require_noerr_action_string_expect - #define require_noerr_quiet_expect - #define require_noerr_action_expect - #define require_noerr_action_quiet_expect - #define require_action_expect - #define require_action_quiet_expect - #define require_action_string_expect +#if ( DEBUG_EXPECT_VERIFIED ) + #define require_expect + #define require_string_expect + #define require_quiet_expect + #define require_noerr_expect + #define require_noerr_string_expect + #define require_noerr_action_string_expect + #define require_noerr_quiet_expect + #define require_noerr_action_expect + #define require_noerr_action_quiet_expect + #define require_action_expect + #define require_action_quiet_expect + #define require_action_string_expect #else - #define require_expect require - #define require_string_expect require_string - #define require_quiet_expect require_quiet - #define require_noerr_expect require_noerr - #define require_noerr_string_expect require_noerr_string - #define require_noerr_action_string_expect require_noerr_action_string - #define require_noerr_quiet_expect require_noerr_quiet - #define require_noerr_action_expect require_noerr_action - #define require_noerr_action_quiet_expect require_noerr_action_quiet - #define require_action_expect require_action - #define require_action_quiet_expect require_action_quiet - #define require_action_string_expect require_action_string + #define require_expect require + #define require_string_expect require_string + #define require_quiet_expect require_quiet + #define require_noerr_expect require_noerr + #define require_noerr_string_expect require_noerr_string + #define require_noerr_action_string_expect require_noerr_action_string + #define require_noerr_quiet_expect require_noerr_quiet + #define require_noerr_action_expect require_noerr_action + #define require_noerr_action_quiet_expect require_noerr_action_quiet + #define require_action_expect require_action + #define require_action_quiet_expect require_action_quiet + #define require_action_string_expect require_action_string #endif #if 0 @@ -1141,245 +1141,245 @@ typedef uint32_t DebugPropertyTag; //--------------------------------------------------------------------------------------------------------------------------- /*! @defined debug_string - - @abstract Prints a debugging C string. -*/ - -#if( DEBUG_OVERRIDE_APPLE_MACROS ) - #undef debug_string -#endif -#if( !defined( debug_string ) ) - #if( DEBUG ) - #define debug_string( STR ) \ - do \ - { \ - debug_print_assert( 0, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \ - \ - } while( 0 ) - #else - #define debug_string( STR ) - #endif + + @abstract Prints a debugging C string. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef debug_string +#endif +#if ( !defined( debug_string ) ) + #if ( DEBUG ) + #define debug_string( STR ) \ + do \ + { \ + debug_print_assert( 0, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \ + \ + } while( 0 ) + #else + #define debug_string( STR ) + #endif #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined debug_print_assert - - @abstract Prints an assertion. -*/ -#if( DEBUG ) - #define debug_print_assert( ERROR_CODE, ASSERT_STRING, MESSAGE, FILENAME, LINE_NUMBER, FUNCTION ) \ - DebugPrintAssert( ERROR_CODE, ASSERT_STRING, MESSAGE, FILENAME, LINE_NUMBER, FUNCTION ) + @abstract Prints an assertion. + */ + +#if ( DEBUG ) + #define debug_print_assert( ERROR_CODE, ASSERT_STRING, MESSAGE, FILENAME, LINE_NUMBER, FUNCTION ) \ + DebugPrintAssert( ERROR_CODE, ASSERT_STRING, MESSAGE, FILENAME, LINE_NUMBER, FUNCTION ) #else - #define debug_print_assert( ERROR_CODE, ASSERT_STRING, MESSAGE, FILENAME, LINE_NUMBER, FUNCTION ) + #define debug_print_assert( ERROR_CODE, ASSERT_STRING, MESSAGE, FILENAME, LINE_NUMBER, FUNCTION ) #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined dlog - - @abstract Prints a debug-only message. -*/ - -#if( DEBUG ) - #if( DEBUG_C99_VA_ARGS ) - #define dlog( ... ) DebugPrintF( __VA_ARGS__ ) - #elif( DEBUG_GNU_VA_ARGS ) - #define dlog( ARGS... ) DebugPrintF( ## ARGS ) - #else - #define dlog DebugPrintF - #endif + + @abstract Prints a debug-only message. + */ + +#if ( DEBUG ) + #if ( DEBUG_C99_VA_ARGS ) + #define dlog(... ) DebugPrintF( __VA_ARGS__ ) + #elif ( DEBUG_GNU_VA_ARGS ) + #define dlog( ARGS... ) DebugPrintF( ## ARGS ) + #else + #define dlog DebugPrintF + #endif #else - #if( DEBUG_C99_VA_ARGS ) - #define dlog( ... ) - #elif( DEBUG_GNU_VA_ARGS ) - #define dlog( ARGS... ) - #else - #define dlog while( 0 ) - #endif + #if ( DEBUG_C99_VA_ARGS ) + #define dlog(... ) + #elif ( DEBUG_GNU_VA_ARGS ) + #define dlog( ARGS... ) + #else + #define dlog while( 0 ) + #endif #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined dlogv - - @abstract Prints a debug-only message. -*/ -#if( DEBUG ) - #define dlogv( LEVEL, FORMAT, LIST ) DebugPrintFVAList( ( LEVEL ), ( FORMAT ), ( LIST ) ) + @abstract Prints a debug-only message. + */ + +#if ( DEBUG ) + #define dlogv( LEVEL, FORMAT, LIST ) DebugPrintFVAList( ( LEVEL ), ( FORMAT ), ( LIST ) ) #else - #define dlogv( LEVEL, FORMAT, LIST ) + #define dlogv( LEVEL, FORMAT, LIST ) #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined dlogmem - - @abstract Prints a debug-only dump of memory. -*/ -#if( DEBUG ) - #define dlogmem( LEVEL, PTR, SIZE ) \ - DebugHexDump( ( LEVEL ), 0, NULL, 0, 0, NULL, 0, ( PTR ), ( PTR ), ( SIZE ), kDebugFlagsNone, NULL, 0 ) + @abstract Prints a debug-only dump of memory. + */ + +#if ( DEBUG ) + #define dlogmem( LEVEL, PTR, SIZE ) \ + DebugHexDump( ( LEVEL ), 0, NULL, 0, 0, NULL, 0, ( PTR ), ( PTR ), ( SIZE ), kDebugFlagsNone, NULL, 0 ) #else - #define dlogmem( LEVEL, PTR, SIZE ) + #define dlogmem( LEVEL, PTR, SIZE ) #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined DebugNSLog - - @abstract Debug-only macro for the Cocoa NSLog function. -*/ - -#if( DEBUG ) - #if( DEBUG_C99_VA_ARGS ) - #define DebugNSLog( ... ) NSLog( __VA_ARGS__ ) - #elif( DEBUG_GNU_VA_ARGS ) - #define DebugNSLog( ARGS... ) NSLog( ## ARGS ) - #else - #define DebugNSLog NSLog - #endif + + @abstract Debug-only macro for the Cocoa NSLog function. + */ + +#if ( DEBUG ) + #if ( DEBUG_C99_VA_ARGS ) + #define DebugNSLog(... ) NSLog( __VA_ARGS__ ) + #elif ( DEBUG_GNU_VA_ARGS ) + #define DebugNSLog( ARGS... ) NSLog( ## ARGS ) + #else + #define DebugNSLog NSLog + #endif #else - #if( DEBUG_C99_VA_ARGS ) - #define DebugNSLog( ... ) - #elif( DEBUG_GNU_VA_ARGS ) - #define DebugNSLog( ARGS... ) - #else - #define DebugNSLog while( 0 ) - #endif + #if ( DEBUG_C99_VA_ARGS ) + #define DebugNSLog(... ) + #elif ( DEBUG_GNU_VA_ARGS ) + #define DebugNSLog( ARGS... ) + #else + #define DebugNSLog while( 0 ) + #endif #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @defined DebugLogMsg - - @abstract Debug-only macro for the VxWorks logMsg function. -*/ - -#if( TARGET_OS_VXWORKS ) - #if( DEBUG ) - #define DebugLogMsg( LEVEL, FORMAT, P1, P2, P3, P4, P5, P6 ) \ - do \ - { \ - if( ( inLevel >= gDebugPrintLevelMin ) || ( inLevel <= gDebugPrintLevelMax ) ) \ - { \ - logMsg( ( FORMAT ), ( P1 ), ( P2 ), ( P3 ), ( P4 ), ( P5 ), ( P6 ) ); \ - } \ - \ - } while( 0 ) - #else - #define DebugLogMsg( LEVEL, FORMAT, P1, P2, P3, P4, P5, P6 ) - #endif + + @abstract Debug-only macro for the VxWorks logMsg function. + */ + +#if ( TARGET_OS_VXWORKS ) + #if ( DEBUG ) + #define DebugLogMsg( LEVEL, FORMAT, P1, P2, P3, P4, P5, P6 ) \ + do \ + { \ + if( ( inLevel >= gDebugPrintLevelMin ) || ( inLevel <= gDebugPrintLevelMax ) ) \ + { \ + logMsg( ( FORMAT ), ( P1 ), ( P2 ), ( P3 ), ( P4 ), ( P5 ), ( P6 ) ); \ + } \ + \ + } while( 0 ) + #else + #define DebugLogMsg( LEVEL, FORMAT, P1, P2, P3, P4, P5, P6 ) + #endif #else - #define DebugLogMsg dlog + #define DebugLogMsg dlog #endif #if 0 #pragma mark == Routines - General == #endif -#ifdef __cplusplus - extern "C" { +#ifdef __cplusplus +extern "C" { #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @function DebugInitialize - @abstract Initializes the debugging library for a specific kind of output. + @abstract Initializes the debugging library for a specific kind of output. - @param inType - @param varArg Variable number parameters, controlled by the "inType" parameter. -*/ + @param inType + @param varArg Variable number parameters, controlled by the "inType" parameter. + */ -#if( DEBUG ) - DEBUG_EXPORT OSStatus DebugInitialize( DebugOutputType inType, ... ); +#if ( DEBUG ) +DEBUG_EXPORT OSStatus DebugInitialize( DebugOutputType inType, ... ); #endif -#if( DEBUG ) - #if( DEBUG_C99_VA_ARGS ) - #define debug_initialize( ... ) DebugInitialize( __VA_ARGS__ ) - #elif( DEBUG_GNU_VA_ARGS ) - #define debug_initialize( ARGS... ) DebugInitialize( ## ARGS ) - #else - #define debug_initialize DebugInitialize - #endif +#if ( DEBUG ) + #if ( DEBUG_C99_VA_ARGS ) + #define debug_initialize(... ) DebugInitialize( __VA_ARGS__ ) + #elif ( DEBUG_GNU_VA_ARGS ) + #define debug_initialize( ARGS... ) DebugInitialize( ## ARGS ) + #else + #define debug_initialize DebugInitialize + #endif #else - #if( DEBUG_C99_VA_ARGS ) - #define debug_initialize( ... ) - #elif( DEBUG_GNU_VA_ARGS ) - #define debug_initialize( ARGS... ) - #else - #define debug_initialize while( 0 ) - #endif + #if ( DEBUG_C99_VA_ARGS ) + #define debug_initialize(... ) + #elif ( DEBUG_GNU_VA_ARGS ) + #define debug_initialize( ARGS... ) + #else + #define debug_initialize while( 0 ) + #endif #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @function DebugFinalize - @abstract Releases any resources used by the debugging library -*/ + @abstract Releases any resources used by the debugging library + */ -#if( DEBUG ) - DEBUG_EXPORT void DebugFinalize( void ); +#if ( DEBUG ) +DEBUG_EXPORT void DebugFinalize( void ); #endif -#if( DEBUG ) - #define debug_terminate() DebugFinalize() +#if ( DEBUG ) + #define debug_terminate() DebugFinalize() #else - #define debug_terminate() + #define debug_terminate() #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @function DebugGetProperty - @abstract Gets the specified property from the debugging library. -*/ + @abstract Gets the specified property from the debugging library. + */ -#if( DEBUG ) - DEBUG_EXPORT OSStatus DebugGetProperty( DebugPropertyTag inTag, ... ); +#if ( DEBUG ) +DEBUG_EXPORT OSStatus DebugGetProperty( DebugPropertyTag inTag, ... ); #endif -#if( DEBUG ) - #if( DEBUG_C99_VA_ARGS ) - #define debug_get_property( ... ) DebugGetProperty( __VA_ARGS__ ) - #elif( DEBUG_GNU_VA_ARGS ) - #define debug_get_property( ARGS... ) DebugGetProperty( ## ARGS ) - #else - #define debug_get_property DebugGetProperty - #endif +#if ( DEBUG ) + #if ( DEBUG_C99_VA_ARGS ) + #define debug_get_property(... ) DebugGetProperty( __VA_ARGS__ ) + #elif ( DEBUG_GNU_VA_ARGS ) + #define debug_get_property( ARGS... ) DebugGetProperty( ## ARGS ) + #else + #define debug_get_property DebugGetProperty + #endif #else - #if( DEBUG_C99_VA_ARGS ) - #define debug_get_property( ... ) - #elif( DEBUG_GNU_VA_ARGS ) - #define debug_get_property( ARGS... ) - #else - #define debug_get_property while( 0 ) - #endif + #if ( DEBUG_C99_VA_ARGS ) + #define debug_get_property(... ) + #elif ( DEBUG_GNU_VA_ARGS ) + #define debug_get_property( ARGS... ) + #else + #define debug_get_property while( 0 ) + #endif #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @function DebugSetProperty - @abstract Sets the specified property from the debugging library. -*/ + @abstract Sets the specified property from the debugging library. + */ -#if( DEBUG ) - DEBUG_EXPORT OSStatus DebugSetProperty( DebugPropertyTag inTag, ... ); +#if ( DEBUG ) +DEBUG_EXPORT OSStatus DebugSetProperty( DebugPropertyTag inTag, ... ); #endif -#if( DEBUG ) - #if( DEBUG_C99_VA_ARGS ) - #define debug_set_property( ... ) DebugSetProperty( __VA_ARGS__ ) - #elif( DEBUG_GNU_VA_ARGS ) - #define debug_set_property( ARGS... ) DebugSetProperty( ## ARGS ) - #else - #define debug_set_property DebugSetProperty - #endif +#if ( DEBUG ) + #if ( DEBUG_C99_VA_ARGS ) + #define debug_set_property(... ) DebugSetProperty( __VA_ARGS__ ) + #elif ( DEBUG_GNU_VA_ARGS ) + #define debug_set_property( ARGS... ) DebugSetProperty( ## ARGS ) + #else + #define debug_set_property DebugSetProperty + #endif #else - #if( DEBUG_C99_VA_ARGS ) - #define debug_set_property( ... ) - #elif( DEBUG_GNU_VA_ARGS ) - #define debug_set_property( ARGS... ) - #else - #define debug_set_property while( 0 ) - #endif + #if ( DEBUG_C99_VA_ARGS ) + #define debug_set_property(... ) + #elif ( DEBUG_GNU_VA_ARGS ) + #define debug_set_property( ARGS... ) + #else + #define debug_set_property while( 0 ) + #endif #endif #if 0 @@ -1389,68 +1389,68 @@ typedef uint32_t DebugPropertyTag; //--------------------------------------------------------------------------------------------------------------------------- /*! @function DebugPrintF - @abstract Prints a debug message with printf-style formatting. - - @param inLevel Error that generated this assert or noErr. - - @param inFormatString - C string containing assertion text. + @abstract Prints a debug message with printf-style formatting. + + @param inLevel Error that generated this assert or noErr. - @param VAR_ARG - Variable number of arguments depending on the format string. - - @result Number of bytes printed or -1 on error. -*/ + @param inFormatString + C string containing assertion text. -#if( DEBUG ) - DEBUG_EXPORT size_t DebugPrintF( DebugLevel inLevel, const char *inFormat, ... ); + @param VAR_ARG + Variable number of arguments depending on the format string. + + @result Number of bytes printed or -1 on error. + */ + +#if ( DEBUG ) +DEBUG_EXPORT size_t DebugPrintF( DebugLevel inLevel, const char *inFormat, ... ); #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @function DebugPrintFVAList - @abstract va_list version of DebugPrintF. See DebugPrintF for more info. -*/ + @abstract va_list version of DebugPrintF. See DebugPrintF for more info. + */ -#if( DEBUG ) - DEBUG_EXPORT size_t DebugPrintFVAList( DebugLevel inLevel, const char *inFormat, va_list inArgs ); +#if ( DEBUG ) +DEBUG_EXPORT size_t DebugPrintFVAList( DebugLevel inLevel, const char *inFormat, va_list inArgs ); #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @function DebugPrintAssert - @abstract Prints a message describing the reason the (e.g. an assert failed), an optional error message, - an optional source filename, an optional source line number. - - @param inErrorCode Error that generated this assert or noErr. - @param inAssertString C string containing assertion text. - @param inMessage C string containing a message about the assert. - @param inFileName C string containing path of file where the error occurred. - @param inLineNumber Line number in source file where the error occurred. - @param inFunction C string containing name of function where assert occurred. - - @discussion - - Example output: - - [ASSERT] assert: "dataPtr != NULL" allocate memory for object failed - [ASSERT] where: "MyFile.c", line 123, ("MyFunction") - - OR - - [ASSERT] error: -6728 (kNoMemoryErr) - [ASSERT] where: "MyFile.c", line 123, ("MyFunction") -*/ - -#if( DEBUG ) - DEBUG_EXPORT void - DebugPrintAssert( - int_least32_t inErrorCode, - const char * inAssertString, - const char * inMessage, - const char * inFilename, - int_least32_t inLineNumber, - const char * inFunction ); + @abstract Prints a message describing the reason the (e.g. an assert failed), an optional error message, + an optional source filename, an optional source line number. + + @param inErrorCode Error that generated this assert or noErr. + @param inAssertString C string containing assertion text. + @param inMessage C string containing a message about the assert. + @param inFileName C string containing path of file where the error occurred. + @param inLineNumber Line number in source file where the error occurred. + @param inFunction C string containing name of function where assert occurred. + + @discussion + + Example output: + + [ASSERT] assert: "dataPtr != NULL" allocate memory for object failed + [ASSERT] where: "MyFile.c", line 123, ("MyFunction") + + OR + + [ASSERT] error: -6728 (kNoMemoryErr) + [ASSERT] where: "MyFile.c", line 123, ("MyFunction") + */ + +#if ( DEBUG ) +DEBUG_EXPORT void +DebugPrintAssert( + int_least32_t inErrorCode, + const char * inAssertString, + const char * inMessage, + const char * inFilename, + int_least32_t inLineNumber, + const char * inFunction ); #endif #if 0 @@ -1459,149 +1459,149 @@ typedef uint32_t DebugPropertyTag; //--------------------------------------------------------------------------------------------------------------------------- /*! @function DebugSNPrintF - - @abstract Debugging versions of standard C snprintf with extra features. - - @param sbuffer Buffer to receive result. Null terminated unless the buffer size is 0. - @param buflen Size of the buffer including space for the null terminator. - @param fmt printf-style format string. - @param VAR_ARG Variable number of arguments depending on the format string. - - @result Number of characters written (minus the null terminator). - - @discussion - - Extra features over the standard C snprintf: -
-		64-bit support for %d (%lld), %i (%lli), %u (%llu), %o (%llo), %x (%llx), and %b (%llb).
-		%@   - Cocoa/CoreFoundation object. Param is the object. Strings are used directly. Others use CFCopyDescription.
-		%a   - Network Address: %.4a=IPv4, %.6a=Ethernet, %.8a Fibre Channel, %.16a=IPv6. Arg=ptr to network address.
-		%#a  - IPv4 or IPv6 mDNSAddr. Arg=ptr to mDNSAddr.
-		%##a - IPv4 (if AF_INET defined) or IPv6 (if AF_INET6 defined) sockaddr. Arg=ptr to sockaddr.
-		%b   - Binary representation of integer (e.g. 01101011). Modifiers and arg=the same as %d, %x, etc.
-		%C   - Mac-style FourCharCode (e.g. 'APPL'). Arg=32-bit value to print as a Mac-style FourCharCode.
-		%H   - Hex Dump (e.g. "\x6b\xa7" -> "6B A7"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
-		%#H  - Hex Dump & ASCII (e.g. "\x41\x62" -> "6B A7 'Ab'"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
-		%m   - Error Message (e.g. 0 -> "kNoErr"). Modifiers and error code arg=the same as %d, %x, etc.
-		%#s  - Pascal-style length-prefixed string. Arg=ptr to string.
-		%##s - DNS label-sequence name. Arg=ptr to name.
-		%S   - UTF-16 string, 0x0000 terminated. Host order if no BOM. Precision is UTF-16 count. Precision includes BOM.
-		%#S  - Big Endian UTF-16 string (unless BOM overrides). Otherwise, the same as %S.
-		%##S - Little Endian UTF-16 string (unless BOM overrides). Otherwise, the same as %S.
-		%U   - Universally Unique Identifier (UUID) (e.g. 6ba7b810-9dad-11d1-80b4-00c04fd430c8). Arg=ptr to 16-byte UUID.
-	
-*/ - -#if( DEBUG ) - DEBUG_EXPORT size_t DebugSNPrintF(char *sbuffer, size_t buflen, const char *fmt, ...); + + @abstract Debugging versions of standard C snprintf with extra features. + + @param sbuffer Buffer to receive result. Null terminated unless the buffer size is 0. + @param buflen Size of the buffer including space for the null terminator. + @param fmt printf-style format string. + @param VAR_ARG Variable number of arguments depending on the format string. + + @result Number of characters written (minus the null terminator). + + @discussion + + Extra features over the standard C snprintf: +
+        64-bit support for %d (%lld), %i (%lli), %u (%llu), %o (%llo), %x (%llx), and %b (%llb).
+        %@   - Cocoa/CoreFoundation object. Param is the object. Strings are used directly. Others use CFCopyDescription.
+        %a   - Network Address: %.4a=IPv4, %.6a=Ethernet, %.8a Fibre Channel, %.16a=IPv6. Arg=ptr to network address.
+        %#a  - IPv4 or IPv6 mDNSAddr. Arg=ptr to mDNSAddr.
+        %##a - IPv4 (if AF_INET defined) or IPv6 (if AF_INET6 defined) sockaddr. Arg=ptr to sockaddr.
+        %b   - Binary representation of integer (e.g. 01101011). Modifiers and arg=the same as %d, %x, etc.
+        %C   - Mac-style FourCharCode (e.g. 'APPL'). Arg=32-bit value to print as a Mac-style FourCharCode.
+        %H   - Hex Dump (e.g. "\x6b\xa7" -> "6B A7"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
+        %#H  - Hex Dump & ASCII (e.g. "\x41\x62" -> "6B A7 'Ab'"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
+        %m   - Error Message (e.g. 0 -> "kNoErr"). Modifiers and error code arg=the same as %d, %x, etc.
+        %#s  - Pascal-style length-prefixed string. Arg=ptr to string.
+        %##s - DNS label-sequence name. Arg=ptr to name.
+        %S   - UTF-16 string, 0x0000 terminated. Host order if no BOM. Precision is UTF-16 count. Precision includes BOM.
+        %#S  - Big Endian UTF-16 string (unless BOM overrides). Otherwise, the same as %S.
+        %##S - Little Endian UTF-16 string (unless BOM overrides). Otherwise, the same as %S.
+        %U   - Universally Unique Identifier (UUID) (e.g. 6ba7b810-9dad-11d1-80b4-00c04fd430c8). Arg=ptr to 16-byte UUID.
+    
+ */ + +#if ( DEBUG ) +DEBUG_EXPORT size_t DebugSNPrintF(char *sbuffer, size_t buflen, const char *fmt, ...); #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @function DebugSNPrintFVAList - @abstract va_list version of DebugSNPrintF. See DebugSNPrintF for more info. -*/ + @abstract va_list version of DebugSNPrintF. See DebugSNPrintF for more info. + */ -#if( DEBUG ) - DEBUG_EXPORT size_t DebugSNPrintFVAList(char *sbuffer, size_t buflen, const char *fmt, va_list arg); +#if ( DEBUG ) +DEBUG_EXPORT size_t DebugSNPrintFVAList(char *sbuffer, size_t buflen, const char *fmt, va_list arg); #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @function DebugGetErrorString - @abstract Gets an error string from an error code. + @abstract Gets an error string from an error code. - @param inStatus Error code to get the string for. - @param inBuffer Optional buffer to copy the string to for non-static strings. May be null. - @param inBufferSize Size of optional buffer. May be 0. - - @result C string containing error string for the error code. Guaranteed to be a valid, static string. If a - buffer is supplied, the return value will always be a pointer to the supplied buffer, which will - contain the best available description of the error code. If a buffer is not supplied, the return - value will be the best available description of the error code that can be represented as a static - string. This allows code that cannot use a temporary buffer to hold the result to still get a useful - error string in most cases, but also allows code that can use a temporary buffer to get the best - available description. -*/ + @param inStatus Error code to get the string for. + @param inBuffer Optional buffer to copy the string to for non-static strings. May be null. + @param inBufferSize Size of optional buffer. May be 0. -#if( DEBUG ) - DEBUG_EXPORT const char * DebugGetErrorString( int_least32_t inErrorCode, char *inBuffer, size_t inBufferSize ); + @result C string containing error string for the error code. Guaranteed to be a valid, static string. If a + buffer is supplied, the return value will always be a pointer to the supplied buffer, which will + contain the best available description of the error code. If a buffer is not supplied, the return + value will be the best available description of the error code that can be represented as a static + string. This allows code that cannot use a temporary buffer to hold the result to still get a useful + error string in most cases, but also allows code that can use a temporary buffer to get the best + available description. + */ + +#if ( DEBUG ) +DEBUG_EXPORT const char * DebugGetErrorString( int_least32_t inErrorCode, char *inBuffer, size_t inBufferSize ); #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @function DebugHexDump - @abstract Hex dumps data to a string or to the output device. -*/ - -#if( DEBUG ) - DEBUG_EXPORT size_t - DebugHexDump( - DebugLevel inLevel, - int inIndent, - const char * inLabel, - size_t inLabelSize, - int inLabelMinWidth, - const char * inType, - size_t inTypeSize, - const void * inDataStart, - const void * inData, - size_t inDataSize, - DebugFlags inFlags, - char * outBuffer, - size_t inBufferSize ); -#endif - -#if( DEBUG ) - #define dloghex( LEVEL, INDENT, LABEL, LABEL_SIZE, LABEL_MIN_SIZE, TYPE, TYPE_SIZE, DATA_START, DATA, DATA_SIZE, FLAGS, BUFFER, BUFFER_SIZE ) \ - DebugHexDump( ( LEVEL ), (INDENT), ( LABEL ), ( LABEL_SIZE ), ( LABEL_MIN_SIZE ), ( TYPE ), ( TYPE_SIZE ), \ - ( DATA_START ), ( DATA ), ( DATA_SIZE ), ( FLAGS ), ( BUFFER ), ( BUFFER_SIZE ) ) + @abstract Hex dumps data to a string or to the output device. + */ + +#if ( DEBUG ) +DEBUG_EXPORT size_t +DebugHexDump( + DebugLevel inLevel, + int inIndent, + const char * inLabel, + size_t inLabelSize, + int inLabelMinWidth, + const char * inType, + size_t inTypeSize, + const void * inDataStart, + const void * inData, + size_t inDataSize, + DebugFlags inFlags, + char * outBuffer, + size_t inBufferSize ); +#endif + +#if ( DEBUG ) + #define dloghex( LEVEL, INDENT, LABEL, LABEL_SIZE, LABEL_MIN_SIZE, TYPE, TYPE_SIZE, DATA_START, DATA, DATA_SIZE, FLAGS, BUFFER, BUFFER_SIZE ) \ + DebugHexDump( ( LEVEL ), (INDENT), ( LABEL ), ( LABEL_SIZE ), ( LABEL_MIN_SIZE ), ( TYPE ), ( TYPE_SIZE ), \ + ( DATA_START ), ( DATA ), ( DATA_SIZE ), ( FLAGS ), ( BUFFER ), ( BUFFER_SIZE ) ) #else - #define dloghex( LEVEL, INDENT, LABEL, LABEL_SIZE, LABEL_MIN_SIZE, TYPE, TYPE_SIZE, DATA_START, DATA, DATA_SIZE, FLAGS, BUFFER, BUFFER_SIZE ) + #define dloghex( LEVEL, INDENT, LABEL, LABEL_SIZE, LABEL_MIN_SIZE, TYPE, TYPE_SIZE, DATA_START, DATA, DATA_SIZE, FLAGS, BUFFER, BUFFER_SIZE ) #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @function DebugTaskLevel - @abstract Returns the current task level. - - @result Current task level - - @discussion - - Bit masks to isolate portions of the result (note that some masks may also need bit shifts to right justify): -
-		kDebugInterruptLevelMask				- Indicates the current interrupt level (> 0 means interrupt time).
-		kDebugInVBLTaskMask						- Indicates if a VBL task is currently being executed.
-		kDebugInDeferredTaskMask				- Indicates if a Deferred Task is currently being executed.
-		kDebugInSecondaryInterruptHandlerMask	- Indicates if a Secondary Interrupt Handler is currently being executed.
-		kDebugPageFaultFatalMask				- Indicates if it is unsafe to cause a page fault (worse than interrupt time).
-		kDebugMPTaskLevelMask					- Indicates if being called from an MP task.
-		kDebugInterruptDepthMask				- 0 means task level, 1 means in interrupt, > 1 means in nested interrupt.
-	
- - Helpers: -
-		DebugExtractTaskLevelInterruptDepth()   - Macro to extract interrupt depth from task level value.
-	
-*/ - -#if( DEBUG ) - DEBUG_EXPORT uint32_t DebugTaskLevel( void ); + @abstract Returns the current task level. + + @result Current task level + + @discussion + + Bit masks to isolate portions of the result (note that some masks may also need bit shifts to right justify): +
+        kDebugInterruptLevelMask				- Indicates the current interrupt level (> 0 means interrupt time).
+        kDebugInVBLTaskMask						- Indicates if a VBL task is currently being executed.
+        kDebugInDeferredTaskMask				- Indicates if a Deferred Task is currently being executed.
+        kDebugInSecondaryInterruptHandlerMask	- Indicates if a Secondary Interrupt Handler is currently being executed.
+        kDebugPageFaultFatalMask				- Indicates if it is unsafe to cause a page fault (worse than interrupt time).
+        kDebugMPTaskLevelMask					- Indicates if being called from an MP task.
+        kDebugInterruptDepthMask				- 0 means task level, 1 means in interrupt, > 1 means in nested interrupt.
+    
+ + Helpers: +
+        DebugExtractTaskLevelInterruptDepth()   - Macro to extract interrupt depth from task level value.
+    
+ */ + +#if ( DEBUG ) +DEBUG_EXPORT uint32_t DebugTaskLevel( void ); #endif //--------------------------------------------------------------------------------------------------------------------------- /*! @function DebugServicesTest - @abstract Unit test. -*/ + @abstract Unit test. + */ -#if( DEBUG ) - DEBUG_EXPORT OSStatus DebugServicesTest( void ); +#if ( DEBUG ) +DEBUG_EXPORT OSStatus DebugServicesTest( void ); #endif -#ifdef __cplusplus - } +#ifdef __cplusplus +} #endif -#endif // __DEBUG_SERVICES__ +#endif // __DEBUG_SERVICES__ diff --git a/mDNSShared/GenLinkedList.c b/mDNSShared/GenLinkedList.c index 6e371b9..45dbb7c 100644 --- a/mDNSShared/GenLinkedList.c +++ b/mDNSShared/GenLinkedList.c @@ -5,315 +5,315 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - File: GenLinkedList.c + File: GenLinkedList.c - Contains: implementation of generic linked lists. + Contains: implementation of generic linked lists. - Version: 1.0 - Tabs: 4 spaces + Version: 1.0 + Tabs: 4 spaces */ #include "GenLinkedList.h" // Return the link pointer contained within element e at offset o. -#define GETLINK( e, o) ( *(void**)((char*) (e) + (o)) ) +#define GETLINK( e, o) ( *(void**)((char*) (e) + (o)) ) // Assign the link pointer l to element e at offset o. -#define ASSIGNLINK( e, l, o) ( *((void**)((char*) (e) + (o))) = (l)) +#define ASSIGNLINK( e, l, o) ( *((void**)((char*) (e) + (o))) = (l)) // GenLinkedList ///////////////////////////////////////////////////////////// -void InitLinkedList( GenLinkedList *pList, size_t linkOffset) +void InitLinkedList( GenLinkedList *pList, size_t linkOffset) /* Initialize the block of memory pointed to by pList as a linked list. */ { - pList->Head = NULL; - pList->Tail = NULL; - pList->LinkOffset = linkOffset; + pList->Head = NULL; + pList->Tail = NULL; + pList->LinkOffset = linkOffset; } -void AddToTail( GenLinkedList *pList, void *elem) +void AddToTail( GenLinkedList *pList, void *elem) /* Add a linked list element to the tail of the list. */ { - if ( pList->Tail) { - ASSIGNLINK( pList->Tail, elem, pList->LinkOffset); - } else - pList->Head = elem; - ASSIGNLINK( elem, NULL, pList->LinkOffset); + if ( pList->Tail) { + ASSIGNLINK( pList->Tail, elem, pList->LinkOffset); + } else + pList->Head = elem; + ASSIGNLINK( elem, NULL, pList->LinkOffset); - pList->Tail = elem; + pList->Tail = elem; } -void AddToHead( GenLinkedList *pList, void *elem) +void AddToHead( GenLinkedList *pList, void *elem) /* Add a linked list element to the head of the list. */ { - ASSIGNLINK( elem, pList->Head, pList->LinkOffset); - if ( pList->Tail == NULL) - pList->Tail = elem; + ASSIGNLINK( elem, pList->Head, pList->LinkOffset); + if ( pList->Tail == NULL) + pList->Tail = elem; - pList->Head = elem; + pList->Head = elem; } -int RemoveFromList( GenLinkedList *pList, void *elem) +int RemoveFromList( GenLinkedList *pList, void *elem) /* Remove a linked list element from the list. Return 0 if it was not found. */ /* If the element is removed, its link will be set to NULL. */ { -void *iElem, *lastElem; - - for ( iElem = pList->Head, lastElem = NULL; iElem; iElem = GETLINK( iElem, pList->LinkOffset)) { - if ( iElem == elem) { - if ( lastElem) { // somewhere past the head - ASSIGNLINK( lastElem, GETLINK( elem, pList->LinkOffset), pList->LinkOffset); - } else { // at the head - pList->Head = GETLINK( elem, pList->LinkOffset); - } - if ( pList->Tail == elem) - pList->Tail = lastElem ? lastElem : NULL; - ASSIGNLINK( elem, NULL, pList->LinkOffset); // maybe catch a stale reference bug. - return 1; - } - lastElem = iElem; - } - - return 0; + void *iElem, *lastElem; + + for ( iElem = pList->Head, lastElem = NULL; iElem; iElem = GETLINK( iElem, pList->LinkOffset)) { + if ( iElem == elem) { + if ( lastElem) { // somewhere past the head + ASSIGNLINK( lastElem, GETLINK( elem, pList->LinkOffset), pList->LinkOffset); + } else { // at the head + pList->Head = GETLINK( elem, pList->LinkOffset); + } + if ( pList->Tail == elem) + pList->Tail = lastElem ? lastElem : NULL; + ASSIGNLINK( elem, NULL, pList->LinkOffset); // maybe catch a stale reference bug. + return 1; + } + lastElem = iElem; + } + + return 0; } -int ReplaceElem( GenLinkedList *pList, void *elemInList, void *newElem) +int ReplaceElem( GenLinkedList *pList, void *elemInList, void *newElem) /* Replace an element in the list with a new element, in the same position. */ { -void *iElem, *lastElem; - - if ( elemInList == NULL || newElem == NULL) - return 0; - - for ( iElem = pList->Head, lastElem = NULL; iElem; iElem = GETLINK( iElem, pList->LinkOffset)) - { - if ( iElem == elemInList) - { - ASSIGNLINK( newElem, GETLINK( elemInList, pList->LinkOffset), pList->LinkOffset); - if ( lastElem) // somewhere past the head - { - ASSIGNLINK( lastElem, newElem, pList->LinkOffset); - } - else // at the head - { - pList->Head = newElem; - } - if ( pList->Tail == elemInList) - pList->Tail = newElem; - return 1; - } - lastElem = iElem; - } - - return 0; + void *iElem, *lastElem; + + if ( elemInList == NULL || newElem == NULL) + return 0; + + for ( iElem = pList->Head, lastElem = NULL; iElem; iElem = GETLINK( iElem, pList->LinkOffset)) + { + if ( iElem == elemInList) + { + ASSIGNLINK( newElem, GETLINK( elemInList, pList->LinkOffset), pList->LinkOffset); + if ( lastElem) // somewhere past the head + { + ASSIGNLINK( lastElem, newElem, pList->LinkOffset); + } + else // at the head + { + pList->Head = newElem; + } + if ( pList->Tail == elemInList) + pList->Tail = newElem; + return 1; + } + lastElem = iElem; + } + + return 0; } // GenDoubleLinkedList ///////////////////////////////////////////////////////// -void InitDoubleLinkedList( GenDoubleLinkedList *pList, size_t fwdLinkOffset, - size_t backLinkOffset) +void InitDoubleLinkedList( GenDoubleLinkedList *pList, size_t fwdLinkOffset, + size_t backLinkOffset) /* Initialize the block of memory pointed to by pList as a double linked list. */ { - pList->Head = NULL; - pList->Tail = NULL; - pList->FwdLinkOffset = fwdLinkOffset; - pList->BackLinkOffset = backLinkOffset; + pList->Head = NULL; + pList->Tail = NULL; + pList->FwdLinkOffset = fwdLinkOffset; + pList->BackLinkOffset = backLinkOffset; } -void DLLAddToHead( GenDoubleLinkedList *pList, void *elem) +void DLLAddToHead( GenDoubleLinkedList *pList, void *elem) /* Add a linked list element to the head of the list. */ { -void *pNext; + void *pNext; - pNext = pList->Head; + pNext = pList->Head; - // fix up the forward links - ASSIGNLINK( elem, pList->Head, pList->FwdLinkOffset); - pList->Head = elem; + // fix up the forward links + ASSIGNLINK( elem, pList->Head, pList->FwdLinkOffset); + pList->Head = elem; - // fix up the backward links - if ( pNext) { - ASSIGNLINK( pNext, elem, pList->BackLinkOffset); - } else - pList->Tail = elem; - ASSIGNLINK( elem, NULL, pList->BackLinkOffset); + // fix up the backward links + if ( pNext) { + ASSIGNLINK( pNext, elem, pList->BackLinkOffset); + } else + pList->Tail = elem; + ASSIGNLINK( elem, NULL, pList->BackLinkOffset); } -void DLLRemoveFromList( GenDoubleLinkedList *pList, void *elem) +void DLLRemoveFromList( GenDoubleLinkedList *pList, void *elem) /* Remove a linked list element from the list. */ /* When the element is removed, its link will be set to NULL. */ { -void *pNext, *pPrev; - - pNext = GETLINK( elem, pList->FwdLinkOffset); - pPrev = GETLINK( elem, pList->BackLinkOffset); - - // fix up the forward links - if ( pPrev) - ASSIGNLINK( pPrev, pNext, pList->FwdLinkOffset); - else - pList->Head = pNext; - - // fix up the backward links - if ( pNext) - ASSIGNLINK( pNext, pPrev, pList->BackLinkOffset); - else - pList->Tail = pPrev; - - ASSIGNLINK( elem, NULL, pList->FwdLinkOffset); - ASSIGNLINK( elem, NULL, pList->BackLinkOffset); + void *pNext, *pPrev; + + pNext = GETLINK( elem, pList->FwdLinkOffset); + pPrev = GETLINK( elem, pList->BackLinkOffset); + + // fix up the forward links + if ( pPrev) + ASSIGNLINK( pPrev, pNext, pList->FwdLinkOffset); + else + pList->Head = pNext; + + // fix up the backward links + if ( pNext) + ASSIGNLINK( pNext, pPrev, pList->BackLinkOffset); + else + pList->Tail = pPrev; + + ASSIGNLINK( elem, NULL, pList->FwdLinkOffset); + ASSIGNLINK( elem, NULL, pList->BackLinkOffset); } // GenLinkedOffsetList ///////////////////////////////////////////////////// // Extract the Next offset from element -#define GETOFFSET( e, o) ( *(size_t*)((char*) (e) + (o)) ) +#define GETOFFSET( e, o) ( *(size_t*)((char*) (e) + (o)) ) -static void AssignOffsetLink( void *elem, void *link, size_t linkOffset); +static void AssignOffsetLink( void *elem, void *link, size_t linkOffset); -static void AssignOffsetLink( void *elem, void *link, size_t linkOffset) +static void AssignOffsetLink( void *elem, void *link, size_t linkOffset) // Assign link to elem as an offset from elem. Assign 0 to elem if link is NULL. { - GETOFFSET( elem, linkOffset) = link ? (size_t) link - (size_t) elem : 0; + GETOFFSET( elem, linkOffset) = link ? (size_t) link - (size_t) elem : 0; } -void *GetHeadPtr( GenLinkedOffsetList *pList) +void *GetHeadPtr( GenLinkedOffsetList *pList) /* Return a pointer to the head element of a list, or NULL if none. */ { - return pList->Head ? ( (char*) (pList) + pList->Head) : NULL; + return pList->Head ? ( (char*) (pList) + pList->Head) : NULL; } -void *GetTailPtr( GenLinkedOffsetList *pList) +void *GetTailPtr( GenLinkedOffsetList *pList) /* Return a pointer to the tail element of a list, or NULL if none. */ { - return pList->Tail ? ( (char*) (pList) + pList->Tail) : NULL; + return pList->Tail ? ( (char*) (pList) + pList->Tail) : NULL; } -void *GetOffsetLink( GenLinkedOffsetList *pList, void *elem) +void *GetOffsetLink( GenLinkedOffsetList *pList, void *elem) /* Return the link pointer contained within element e for pList, or NULL if it is 0. */ { -size_t nextOffset; + size_t nextOffset; - nextOffset = GETOFFSET( elem, pList->LinkOffset); + nextOffset = GETOFFSET( elem, pList->LinkOffset); - return nextOffset ? (char*) elem + nextOffset : NULL; + return nextOffset ? (char*) elem + nextOffset : NULL; } -void InitLinkedOffsetList( GenLinkedOffsetList *pList, size_t linkOffset) +void InitLinkedOffsetList( GenLinkedOffsetList *pList, size_t linkOffset) /* Initialize the block of memory pointed to by pList as a linked list. */ { - pList->Head = 0; - pList->Tail = 0; - pList->LinkOffset = linkOffset; + pList->Head = 0; + pList->Tail = 0; + pList->LinkOffset = linkOffset; } -void OffsetAddToTail( GenLinkedOffsetList *pList, void *elem) +void OffsetAddToTail( GenLinkedOffsetList *pList, void *elem) /* Add a linked list element to the tail of the list. */ { - if ( pList->Tail) { - AssignOffsetLink( GetTailPtr( pList), elem, pList->LinkOffset); - } else - pList->Head = (size_t) elem - (size_t) pList; - AssignOffsetLink( elem, NULL, pList->LinkOffset); + if ( pList->Tail) { + AssignOffsetLink( GetTailPtr( pList), elem, pList->LinkOffset); + } else + pList->Head = (size_t) elem - (size_t) pList; + AssignOffsetLink( elem, NULL, pList->LinkOffset); - pList->Tail = (size_t) elem - (size_t) pList; + pList->Tail = (size_t) elem - (size_t) pList; } -void OffsetAddToHead( GenLinkedOffsetList *pList, void *elem) +void OffsetAddToHead( GenLinkedOffsetList *pList, void *elem) /* Add a linked list element to the head of the list. */ { - AssignOffsetLink( elem, GetHeadPtr( pList), pList->LinkOffset); - if ( pList->Tail == 0) - pList->Tail = (size_t) elem - (size_t) pList; + AssignOffsetLink( elem, GetHeadPtr( pList), pList->LinkOffset); + if ( pList->Tail == 0) + pList->Tail = (size_t) elem - (size_t) pList; - pList->Head = (size_t) elem - (size_t) pList; + pList->Head = (size_t) elem - (size_t) pList; } -int OffsetRemoveFromList( GenLinkedOffsetList *pList, void *elem) +int OffsetRemoveFromList( GenLinkedOffsetList *pList, void *elem) /* Remove a linked list element from the list. Return 0 if it was not found. */ /* If the element is removed, its link will be set to NULL. */ { -void *iElem, *lastElem; - - for ( iElem = GetHeadPtr( pList), lastElem = NULL; iElem; - iElem = GetOffsetLink( pList, iElem)) - { - if ( iElem == elem) { - if ( lastElem) { // somewhere past the head - AssignOffsetLink( lastElem, GetOffsetLink( pList, elem), pList->LinkOffset); - } else { // at the head - iElem = GetOffsetLink( pList, elem); - pList->Head = iElem ? (size_t) iElem - (size_t) pList : 0; - } - if ( GetTailPtr( pList) == elem) - pList->Tail = lastElem ? (size_t) lastElem - (size_t) pList : 0; - AssignOffsetLink( elem, NULL, pList->LinkOffset); // maybe catch a stale reference bug. - return 1; - } - lastElem = iElem; - } - - return 0; + void *iElem, *lastElem; + + for ( iElem = GetHeadPtr( pList), lastElem = NULL; iElem; + iElem = GetOffsetLink( pList, iElem)) + { + if ( iElem == elem) { + if ( lastElem) { // somewhere past the head + AssignOffsetLink( lastElem, GetOffsetLink( pList, elem), pList->LinkOffset); + } else { // at the head + iElem = GetOffsetLink( pList, elem); + pList->Head = iElem ? (size_t) iElem - (size_t) pList : 0; + } + if ( GetTailPtr( pList) == elem) + pList->Tail = lastElem ? (size_t) lastElem - (size_t) pList : 0; + AssignOffsetLink( elem, NULL, pList->LinkOffset); // maybe catch a stale reference bug. + return 1; + } + lastElem = iElem; + } + + return 0; } -int OffsetReplaceElem( GenLinkedOffsetList *pList, void *elemInList, void *newElem) +int OffsetReplaceElem( GenLinkedOffsetList *pList, void *elemInList, void *newElem) /* Replace an element in the list with a new element, in the same position. */ { -void *iElem, *lastElem; - - if ( elemInList == NULL || newElem == NULL) - return 0; - - for ( iElem = GetHeadPtr( pList), lastElem = NULL; iElem; - iElem = GetOffsetLink( pList, iElem)) - { - if ( iElem == elemInList) - { - AssignOffsetLink( newElem, GetOffsetLink( pList, elemInList), pList->LinkOffset); - if ( lastElem) // somewhere past the head - { - AssignOffsetLink( lastElem, newElem, pList->LinkOffset); - } - else // at the head - { - pList->Head = (size_t) newElem - (size_t) pList; - } - if ( GetTailPtr( pList) == elemInList) - pList->Tail = (size_t) newElem - (size_t) pList; - return 1; - } - lastElem = iElem; - } - - return 0; + void *iElem, *lastElem; + + if ( elemInList == NULL || newElem == NULL) + return 0; + + for ( iElem = GetHeadPtr( pList), lastElem = NULL; iElem; + iElem = GetOffsetLink( pList, iElem)) + { + if ( iElem == elemInList) + { + AssignOffsetLink( newElem, GetOffsetLink( pList, elemInList), pList->LinkOffset); + if ( lastElem) // somewhere past the head + { + AssignOffsetLink( lastElem, newElem, pList->LinkOffset); + } + else // at the head + { + pList->Head = (size_t) newElem - (size_t) pList; + } + if ( GetTailPtr( pList) == elemInList) + pList->Tail = (size_t) newElem - (size_t) pList; + return 1; + } + lastElem = iElem; + } + + return 0; } diff --git a/mDNSShared/GenLinkedList.h b/mDNSShared/GenLinkedList.h index 4e17708..2d0ada6 100644 --- a/mDNSShared/GenLinkedList.h +++ b/mDNSShared/GenLinkedList.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,69 +22,69 @@ #include -struct GenLinkedList +struct GenLinkedList { - void *Head, - *Tail; - size_t LinkOffset; + void *Head, + *Tail; + size_t LinkOffset; }; -typedef struct GenLinkedList GenLinkedList; +typedef struct GenLinkedList GenLinkedList; -void InitLinkedList( GenLinkedList *pList, size_t linkOffset); +void InitLinkedList( GenLinkedList *pList, size_t linkOffset); -void AddToHead( GenLinkedList *pList, void *elem); -void AddToTail( GenLinkedList *pList, void *elem); +void AddToHead( GenLinkedList *pList, void *elem); +void AddToTail( GenLinkedList *pList, void *elem); -int RemoveFromList( GenLinkedList *pList, void *elem); +int RemoveFromList( GenLinkedList *pList, void *elem); -int ReplaceElem( GenLinkedList *pList, void *elemInList, void *newElem); +int ReplaceElem( GenLinkedList *pList, void *elemInList, void *newElem); -struct GenDoubleLinkedList +struct GenDoubleLinkedList { - void *Head, - *Tail; - size_t FwdLinkOffset, - BackLinkOffset; + void *Head, + *Tail; + size_t FwdLinkOffset, + BackLinkOffset; }; -typedef struct GenDoubleLinkedList GenDoubleLinkedList; +typedef struct GenDoubleLinkedList GenDoubleLinkedList; -void InitDoubleLinkedList( GenDoubleLinkedList *pList, size_t fwdLinkOffset, - size_t backLinkOffset); +void InitDoubleLinkedList( GenDoubleLinkedList *pList, size_t fwdLinkOffset, + size_t backLinkOffset); -void DLLAddToHead( GenDoubleLinkedList *pList, void *elem); +void DLLAddToHead( GenDoubleLinkedList *pList, void *elem); -void DLLRemoveFromList( GenDoubleLinkedList *pList, void *elem); +void DLLRemoveFromList( GenDoubleLinkedList *pList, void *elem); /* A GenLinkedOffsetList is like a GenLinkedList that stores the *Next field as a signed */ /* offset from the address of the beginning of the element, rather than as a pointer. */ -struct GenLinkedOffsetList +struct GenLinkedOffsetList { - size_t Head, - Tail; - size_t LinkOffset; + size_t Head, + Tail; + size_t LinkOffset; }; -typedef struct GenLinkedOffsetList GenLinkedOffsetList; +typedef struct GenLinkedOffsetList GenLinkedOffsetList; -void InitLinkedOffsetList( GenLinkedOffsetList *pList, size_t linkOffset); +void InitLinkedOffsetList( GenLinkedOffsetList *pList, size_t linkOffset); -void *GetHeadPtr( GenLinkedOffsetList *pList); -void *GetTailPtr( GenLinkedOffsetList *pList); -void *GetOffsetLink( GenLinkedOffsetList *pList, void *elem); +void *GetHeadPtr( GenLinkedOffsetList *pList); +void *GetTailPtr( GenLinkedOffsetList *pList); +void *GetOffsetLink( GenLinkedOffsetList *pList, void *elem); -void OffsetAddToHead( GenLinkedOffsetList *pList, void *elem); -void OffsetAddToTail( GenLinkedOffsetList *pList, void *elem); +void OffsetAddToHead( GenLinkedOffsetList *pList, void *elem); +void OffsetAddToTail( GenLinkedOffsetList *pList, void *elem); -int OffsetRemoveFromList( GenLinkedOffsetList *pList, void *elem); +int OffsetRemoveFromList( GenLinkedOffsetList *pList, void *elem); -int OffsetReplaceElem( GenLinkedOffsetList *pList, void *elemInList, void *newElem); +int OffsetReplaceElem( GenLinkedOffsetList *pList, void *elemInList, void *newElem); #endif // __GenLinkedList__ diff --git a/mDNSShared/Java/JNISupport.c b/mDNSShared/Java/JNISupport.c index dbbedea..22b4093 100644 --- a/mDNSShared/Java/JNISupport.c +++ b/mDNSShared/Java/JNISupport.c @@ -5,17 +5,17 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - This file contains the platform support for DNSSD and related Java classes. - It is used to shim through to the underlying API. + This file contains the platform support for DNSSD and related Java classes. + It is used to shim through to the underlying API. */ // AUTO_CALLBACKS should be set to 1 if the underlying mDNS implementation fires response @@ -25,8 +25,8 @@ // (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 +#ifndef AUTO_CALLBACKS +#define AUTO_CALLBACKS 0 #endif #if !AUTO_CALLBACKS @@ -46,8 +46,8 @@ #ifdef _WIN32 #include #include -static char * win32_if_indextoname( DWORD ifIndex, char * nameBuff); -static DWORD win32_if_nametoindex( const char * nameStr ); +static char * win32_if_indextoname( DWORD ifIndex, char * nameBuff); +static DWORD win32_if_nametoindex( const char * nameStr ); #define if_indextoname win32_if_indextoname #define if_nametoindex win32_if_nametoindex #define IF_NAMESIZE MAX_ADAPTER_NAME_LENGTH @@ -66,887 +66,887 @@ static DWORD win32_if_nametoindex( const char * nameStr ); //#include -// convenience definition +// convenience definition #ifdef __GNUC__ -#define _UNUSED __attribute__ ((unused)) +#define _UNUSED __attribute__ ((unused)) #else -#define _UNUSED +#define _UNUSED #endif enum { - kInterfaceVersionOne = 1, - kInterfaceVersionCurrent // Must match version in .jar file + kInterfaceVersionOne = 1, + kInterfaceVersionCurrent // Must match version in .jar file }; -typedef struct OpContext OpContext; +typedef struct OpContext OpContext; -struct OpContext +struct OpContext { - DNSServiceRef ServiceRef; - JNIEnv *Env; - jobject JavaObj; - jobject ClientObj; - jmethodID Callback; - jmethodID Callback2; + DNSServiceRef ServiceRef; + JNIEnv *Env; + jobject JavaObj; + jobject ClientObj; + jmethodID Callback; + jmethodID Callback2; }; // For AUTO_CALLBACKS, we must attach the callback thread to the Java VM prior to upcall. #if AUTO_CALLBACKS -JavaVM *gJavaVM = NULL; +JavaVM *gJavaVM = NULL; #endif JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_InitLibrary( JNIEnv *pEnv, jclass cls, - jint callerVersion) + jint callerVersion) { - /* Ensure that caller & interface versions match. */ - if ( callerVersion != kInterfaceVersionCurrent) - return kDNSServiceErr_Incompatible; + /* Ensure that caller & interface versions match. */ + if ( callerVersion != kInterfaceVersionCurrent) + return kDNSServiceErr_Incompatible; #if AUTO_CALLBACKS - { - jsize numVMs; - - if ( 0 != JNI_GetCreatedJavaVMs( &gJavaVM, 1, &numVMs)) - return kDNSServiceErr_BadState; - } + { + jsize numVMs; + + if ( 0 != JNI_GetCreatedJavaVMs( &gJavaVM, 1, &numVMs)) + return kDNSServiceErr_BadState; + } #endif - // Set AppleDNSSD.hasAutoCallbacks - { + // Set AppleDNSSD.hasAutoCallbacks + { #if AUTO_CALLBACKS - jboolean hasAutoC = JNI_TRUE; + jboolean hasAutoC = JNI_TRUE; #else - jboolean hasAutoC = JNI_FALSE; + jboolean hasAutoC = JNI_FALSE; #endif - jfieldID hasAutoCField = (*pEnv)->GetStaticFieldID( pEnv, cls, "hasAutoCallbacks", "Z"); - (*pEnv)->SetStaticBooleanField( pEnv, cls, hasAutoCField, hasAutoC); - } + jfieldID hasAutoCField = (*pEnv)->GetStaticFieldID( pEnv, cls, "hasAutoCallbacks", "Z"); + (*pEnv)->SetStaticBooleanField( pEnv, cls, hasAutoCField, hasAutoC); + } - return kDNSServiceErr_NoError; + return kDNSServiceErr_NoError; } -static const char* SafeGetUTFChars( JNIEnv *pEnv, jstring str) +static const char* SafeGetUTFChars( JNIEnv *pEnv, jstring str) // Wrapper for JNI GetStringUTFChars() that returns NULL for null str. { - return str != NULL ? (*pEnv)->GetStringUTFChars( pEnv, str, 0) : NULL; + return str != NULL ? (*pEnv)->GetStringUTFChars( pEnv, str, 0) : NULL; } -static void SafeReleaseUTFChars( JNIEnv *pEnv, jstring str, const char *buff) +static void SafeReleaseUTFChars( JNIEnv *pEnv, jstring str, const char *buff) // Wrapper for JNI GetStringUTFChars() that handles null str. { - if ( str != NULL) - (*pEnv)->ReleaseStringUTFChars( pEnv, str, buff); + if ( str != NULL) + (*pEnv)->ReleaseStringUTFChars( pEnv, str, buff); } #if AUTO_CALLBACKS -static void SetupCallbackState( JNIEnv **ppEnv) +static void SetupCallbackState( JNIEnv **ppEnv) { - (*gJavaVM)->AttachCurrentThread( gJavaVM, (void**) ppEnv, NULL); + (*gJavaVM)->AttachCurrentThread( gJavaVM, (void**) ppEnv, NULL); } -static void TeardownCallbackState( void ) +static void TeardownCallbackState( void ) { - (*gJavaVM)->DetachCurrentThread( gJavaVM); + (*gJavaVM)->DetachCurrentThread( gJavaVM); } -#else // AUTO_CALLBACKS +#else // AUTO_CALLBACKS -static void SetupCallbackState( JNIEnv **ppEnv _UNUSED) +static void SetupCallbackState( JNIEnv **ppEnv _UNUSED) { - // No setup necessary if ProcessResults() has been called + // No setup necessary if ProcessResults() has been called } -static void TeardownCallbackState( void ) +static void TeardownCallbackState( void ) { - // No teardown necessary if ProcessResults() has been called + // No teardown necessary if ProcessResults() has been called } -#endif // AUTO_CALLBACKS +#endif // AUTO_CALLBACKS -static OpContext *NewContext( JNIEnv *pEnv, jobject owner, - const char *callbackName, const char *callbackSig) +static OpContext *NewContext( JNIEnv *pEnv, jobject owner, + const char *callbackName, const char *callbackSig) // Create and initialize a new OpContext. { - OpContext *pContext = (OpContext*) malloc( sizeof *pContext); - - if ( pContext != NULL) - { - jfieldID clientField = (*pEnv)->GetFieldID( pEnv, (*pEnv)->GetObjectClass( pEnv, owner), - "fListener", "Lcom/apple/dnssd/BaseListener;"); - - pContext->JavaObj = (*pEnv)->NewWeakGlobalRef( pEnv, owner); // must convert local ref to global to cache; - pContext->ClientObj = (*pEnv)->GetObjectField( pEnv, owner, clientField); - pContext->ClientObj = (*pEnv)->NewWeakGlobalRef( pEnv, pContext->ClientObj); // must convert local ref to global to cache - pContext->Callback = (*pEnv)->GetMethodID( pEnv, - (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj), - callbackName, callbackSig); - pContext->Callback2 = NULL; // not always used - } - - return pContext; + OpContext *pContext = (OpContext*) malloc( sizeof *pContext); + + if ( pContext != NULL) + { + jfieldID clientField = (*pEnv)->GetFieldID( pEnv, (*pEnv)->GetObjectClass( pEnv, owner), + "fListener", "Lcom/apple/dnssd/BaseListener;"); + + pContext->JavaObj = (*pEnv)->NewWeakGlobalRef( pEnv, owner); // must convert local ref to global to cache; + pContext->ClientObj = (*pEnv)->GetObjectField( pEnv, owner, clientField); + pContext->ClientObj = (*pEnv)->NewWeakGlobalRef( pEnv, pContext->ClientObj); // must convert local ref to global to cache + pContext->Callback = (*pEnv)->GetMethodID( pEnv, + (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj), + callbackName, callbackSig); + pContext->Callback2 = NULL; // not always used + } + + return pContext; } -static void ReportError( JNIEnv *pEnv, jobject target, jobject service, DNSServiceErrorType err) +static void ReportError( JNIEnv *pEnv, jobject target, jobject service, DNSServiceErrorType err) // Invoke operationFailed() method on target with err. { - jclass cls = (*pEnv)->GetObjectClass( pEnv, target); - jmethodID opFailed = (*pEnv)->GetMethodID( pEnv, cls, "operationFailed", - "(Lcom/apple/dnssd/DNSSDService;I)V"); + jclass cls = (*pEnv)->GetObjectClass( pEnv, target); + jmethodID opFailed = (*pEnv)->GetMethodID( pEnv, cls, "operationFailed", + "(Lcom/apple/dnssd/DNSSDService;I)V"); - (*pEnv)->CallVoidMethod( pEnv, target, opFailed, service, err); + (*pEnv)->CallVoidMethod( pEnv, target, opFailed, service, err); } JNIEXPORT void JNICALL Java_com_apple_dnssd_AppleService_HaltOperation( JNIEnv *pEnv, jobject pThis) /* Deallocate the dns_sd service browser and set the Java object's fNativeContext field to 0. */ { - jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); - jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); - - if ( contextField != 0) - { - OpContext *pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField); - if ( pContext != NULL) - { - // MUST clear fNativeContext first, BEFORE calling DNSServiceRefDeallocate() - (*pEnv)->SetLongField(pEnv, pThis, contextField, 0); - if ( pContext->ServiceRef != NULL) - DNSServiceRefDeallocate( pContext->ServiceRef); - - (*pEnv)->DeleteWeakGlobalRef( pEnv, pContext->JavaObj); - (*pEnv)->DeleteWeakGlobalRef( pEnv, pContext->ClientObj); - free( pContext); - } - } + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + + if ( contextField != 0) + { + OpContext *pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField); + if ( pContext != NULL) + { + // MUST clear fNativeContext first, BEFORE calling DNSServiceRefDeallocate() + (*pEnv)->SetLongField(pEnv, pThis, contextField, 0); + if ( pContext->ServiceRef != NULL) + DNSServiceRefDeallocate( pContext->ServiceRef); + + (*pEnv)->DeleteWeakGlobalRef( pEnv, pContext->JavaObj); + (*pEnv)->DeleteWeakGlobalRef( pEnv, pContext->ClientObj); + free( pContext); + } + } } JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleService_BlockForData( JNIEnv *pEnv, jobject pThis) /* Block until data arrives, or one second passes. Returns 1 if data present, 0 otherwise. */ { -// BlockForData() not supported with AUTO_CALLBACKS +// BlockForData() not supported with AUTO_CALLBACKS #if !AUTO_CALLBACKS - jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); - jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); - - if ( contextField != 0) - { - OpContext *pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField); - if ( pContext != NULL) - { - fd_set readFDs; - int sd = DNSServiceRefSockFD( pContext->ServiceRef); - struct timeval timeout = { 1, 0 }; - FD_ZERO( &readFDs); - FD_SET( sd, &readFDs); - - // Q: Why do we poll here? - // A: Because there's no other thread-safe way to do it. - // Mac OS X terminates a select() call if you close one of the sockets it's listening on, but Linux does not, - // and arguably Linux is correct (See ) - // The problem is that the Mac OS X behaviour assumes that it's okay for one thread to close a socket while - // some other thread is monitoring that socket in select(), but the difficulty is that there's no general way - // to make that thread-safe, because there's no atomic way to enter select() and release a lock simultaneously. - // If we try to do this without holding any lock, then right as we jump to the select() routine, - // some other thread could stop our operation (thereby closing the socket), - // and then that thread (or even some third, unrelated thread) - // could do some other DNS-SD operation (or some other operation that opens a new file descriptor) - // and then we'd blindly resume our fall into the select() call, now blocking on a file descriptor - // that may coincidentally have the same numerical value, but is semantically unrelated - // to the true file descriptor we thought we were blocking on. - // We can't stop this race condition from happening, but at least if we wake up once a second we can detect - // when fNativeContext has gone to zero, and thereby discover that we were blocking on the wrong fd. - - if (select( sd + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &timeout) == 1) return(1); - } - } + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + + if ( contextField != 0) + { + OpContext *pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField); + if ( pContext != NULL) + { + fd_set readFDs; + int sd = DNSServiceRefSockFD( pContext->ServiceRef); + struct timeval timeout = { 1, 0 }; + FD_ZERO( &readFDs); + FD_SET( sd, &readFDs); + + // Q: Why do we poll here? + // A: Because there's no other thread-safe way to do it. + // Mac OS X terminates a select() call if you close one of the sockets it's listening on, but Linux does not, + // and arguably Linux is correct (See ) + // The problem is that the Mac OS X behaviour assumes that it's okay for one thread to close a socket while + // some other thread is monitoring that socket in select(), but the difficulty is that there's no general way + // to make that thread-safe, because there's no atomic way to enter select() and release a lock simultaneously. + // If we try to do this without holding any lock, then right as we jump to the select() routine, + // some other thread could stop our operation (thereby closing the socket), + // and then that thread (or even some third, unrelated thread) + // could do some other DNS-SD operation (or some other operation that opens a new file descriptor) + // and then we'd blindly resume our fall into the select() call, now blocking on a file descriptor + // that may coincidentally have the same numerical value, but is semantically unrelated + // to the true file descriptor we thought we were blocking on. + // We can't stop this race condition from happening, but at least if we wake up once a second we can detect + // when fNativeContext has gone to zero, and thereby discover that we were blocking on the wrong fd. + + if (select( sd + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &timeout) == 1) return(1); + } + } #endif // !AUTO_CALLBACKS - return(0); + return(0); } JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleService_ProcessResults( JNIEnv *pEnv, jobject pThis) /* Call through to DNSServiceProcessResult() while data remains on socket. */ { -#if !AUTO_CALLBACKS // ProcessResults() not supported with AUTO_CALLBACKS - - jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); - jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); - OpContext *pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField); - DNSServiceErrorType err = kDNSServiceErr_BadState; - - if ( pContext != NULL) - { - int sd = DNSServiceRefSockFD( pContext->ServiceRef); - fd_set readFDs; - struct timeval zeroTimeout = { 0, 0 }; - - pContext->Env = pEnv; - - FD_ZERO( &readFDs); - FD_SET( sd, &readFDs); - - err = kDNSServiceErr_NoError; - if (0 < select(sd + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &zeroTimeout)) - { - err = DNSServiceProcessResult(pContext->ServiceRef); - // Use caution here! - // We cannot touch any data structures associated with this operation! - // The DNSServiceProcessResult() routine should have invoked our callback, - // and our callback could have terminated the operation with op.stop(); - // and that means HaltOperation() will have been called, which frees pContext. - // Basically, from here we just have to get out without touching any stale - // data structures that could blow up on us! Particularly, any attempt - // to loop here reading more results from the file descriptor is unsafe. - } - } - return err; +#if !AUTO_CALLBACKS // ProcessResults() not supported with AUTO_CALLBACKS + + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + OpContext *pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField); + DNSServiceErrorType err = kDNSServiceErr_BadState; + + if ( pContext != NULL) + { + int sd = DNSServiceRefSockFD( pContext->ServiceRef); + fd_set readFDs; + struct timeval zeroTimeout = { 0, 0 }; + + pContext->Env = pEnv; + + FD_ZERO( &readFDs); + FD_SET( sd, &readFDs); + + err = kDNSServiceErr_NoError; + if (0 < select(sd + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &zeroTimeout)) + { + err = DNSServiceProcessResult(pContext->ServiceRef); + // Use caution here! + // We cannot touch any data structures associated with this operation! + // The DNSServiceProcessResult() routine should have invoked our callback, + // and our callback could have terminated the operation with op.stop(); + // and that means HaltOperation() will have been called, which frees pContext. + // Basically, from here we just have to get out without touching any stale + // data structures that could blow up on us! Particularly, any attempt + // to loop here reading more results from the file descriptor is unsafe. + } + } + return err; #endif // AUTO_CALLBACKS } -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) +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) { - OpContext *pContext = (OpContext*) context; - - SetupCallbackState( &pContext->Env); - - if ( pContext->ClientObj != NULL && pContext->Callback != NULL) - { - if ( errorCode == kDNSServiceErr_NoError) - { - (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, - ( flags & kDNSServiceFlagsAdd) != 0 ? pContext->Callback : pContext->Callback2, - pContext->JavaObj, flags, interfaceIndex, - (*pContext->Env)->NewStringUTF( pContext->Env, serviceName), - (*pContext->Env)->NewStringUTF( pContext->Env, regtype), - (*pContext->Env)->NewStringUTF( pContext->Env, replyDomain)); - } - else - ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); - } - - TeardownCallbackState(); + OpContext *pContext = (OpContext*) context; + + SetupCallbackState( &pContext->Env); + + if ( pContext->ClientObj != NULL && pContext->Callback != NULL) + { + if ( errorCode == kDNSServiceErr_NoError) + { + (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, + ( flags & kDNSServiceFlagsAdd) != 0 ? pContext->Callback : pContext->Callback2, + pContext->JavaObj, flags, interfaceIndex, + (*pContext->Env)->NewStringUTF( pContext->Env, serviceName), + (*pContext->Env)->NewStringUTF( pContext->Env, regtype), + (*pContext->Env)->NewStringUTF( pContext->Env, replyDomain)); + } + else + ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); + } + + TeardownCallbackState(); } JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleBrowser_CreateBrowser( JNIEnv *pEnv, jobject pThis, - jint flags, jint ifIndex, jstring regType, jstring domain) + jint flags, jint ifIndex, jstring regType, jstring domain) { - jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); - jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); - OpContext *pContext = NULL; - DNSServiceErrorType err = kDNSServiceErr_NoError; - - if ( contextField != 0) - pContext = NewContext( pEnv, pThis, "serviceFound", - "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); - else - err = kDNSServiceErr_BadParam; - - if ( pContext != NULL) - { - const char *regStr = SafeGetUTFChars( pEnv, regType); - const char *domainStr = SafeGetUTFChars( pEnv, domain); - - pContext->Callback2 = (*pEnv)->GetMethodID( pEnv, - (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj), - "serviceLost", "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); - - err = DNSServiceBrowse( &pContext->ServiceRef, flags, ifIndex, regStr, domainStr, ServiceBrowseReply, pContext); - if ( err == kDNSServiceErr_NoError) - { - (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext); - } - - SafeReleaseUTFChars( pEnv, regType, regStr); - SafeReleaseUTFChars( pEnv, domain, domainStr); - } - else - err = kDNSServiceErr_NoMemory; - - return err; + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + + if ( contextField != 0) + pContext = NewContext( pEnv, pThis, "serviceFound", + "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + else + err = kDNSServiceErr_BadParam; + + if ( pContext != NULL) + { + const char *regStr = SafeGetUTFChars( pEnv, regType); + const char *domainStr = SafeGetUTFChars( pEnv, domain); + + pContext->Callback2 = (*pEnv)->GetMethodID( pEnv, + (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj), + "serviceLost", "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + + err = DNSServiceBrowse( &pContext->ServiceRef, flags, ifIndex, regStr, domainStr, ServiceBrowseReply, pContext); + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext); + } + + SafeReleaseUTFChars( pEnv, regType, regStr); + SafeReleaseUTFChars( pEnv, domain, domainStr); + } + else + err = kDNSServiceErr_NoMemory; + + return err; } -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 unsigned char *txtRecord, void *context) +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 unsigned char *txtRecord, void *context) { - OpContext *pContext = (OpContext*) context; - jclass txtCls; - jmethodID txtCtor; - jbyteArray txtBytes; - jobject txtObj; - jbyte *pBytes; - - SetupCallbackState( &pContext->Env); - - txtCls = (*pContext->Env)->FindClass( pContext->Env, "com/apple/dnssd/TXTRecord"); - txtCtor = (*pContext->Env)->GetMethodID( pContext->Env, txtCls, "", "([B)V"); - - if ( pContext->ClientObj != NULL && pContext->Callback != NULL && txtCtor != NULL && - NULL != ( txtBytes = (*pContext->Env)->NewByteArray( pContext->Env, txtLen))) - { - if ( errorCode == kDNSServiceErr_NoError) - { - // Since Java ints are defined to be big-endian, we canonicalize 'port' from a 16-bit - // pattern into a number here. - port = ( ((unsigned char*) &port)[0] << 8) | ((unsigned char*) &port)[1]; - - // Initialize txtBytes with contents of txtRecord - pBytes = (*pContext->Env)->GetByteArrayElements( pContext->Env, txtBytes, NULL); - memcpy( pBytes, txtRecord, txtLen); - (*pContext->Env)->ReleaseByteArrayElements( pContext->Env, txtBytes, pBytes, JNI_COMMIT); - - // Construct txtObj with txtBytes - txtObj = (*pContext->Env)->NewObject( pContext->Env, txtCls, txtCtor, txtBytes); - (*pContext->Env)->DeleteLocalRef( pContext->Env, txtBytes); - - (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback, - pContext->JavaObj, flags, interfaceIndex, - (*pContext->Env)->NewStringUTF( pContext->Env, fullname), - (*pContext->Env)->NewStringUTF( pContext->Env, hosttarget), - port, txtObj); - } - else - ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); - } - - TeardownCallbackState(); + OpContext *pContext = (OpContext*) context; + jclass txtCls; + jmethodID txtCtor; + jbyteArray txtBytes; + jobject txtObj; + jbyte *pBytes; + + SetupCallbackState( &pContext->Env); + + txtCls = (*pContext->Env)->FindClass( pContext->Env, "com/apple/dnssd/TXTRecord"); + txtCtor = (*pContext->Env)->GetMethodID( pContext->Env, txtCls, "", "([B)V"); + + if ( pContext->ClientObj != NULL && pContext->Callback != NULL && txtCtor != NULL && + NULL != ( txtBytes = (*pContext->Env)->NewByteArray( pContext->Env, txtLen))) + { + if ( errorCode == kDNSServiceErr_NoError) + { + // Since Java ints are defined to be big-endian, we canonicalize 'port' from a 16-bit + // pattern into a number here. + port = ( ((unsigned char*) &port)[0] << 8) | ((unsigned char*) &port)[1]; + + // Initialize txtBytes with contents of txtRecord + pBytes = (*pContext->Env)->GetByteArrayElements( pContext->Env, txtBytes, NULL); + memcpy( pBytes, txtRecord, txtLen); + (*pContext->Env)->ReleaseByteArrayElements( pContext->Env, txtBytes, pBytes, JNI_COMMIT); + + // Construct txtObj with txtBytes + txtObj = (*pContext->Env)->NewObject( pContext->Env, txtCls, txtCtor, txtBytes); + (*pContext->Env)->DeleteLocalRef( pContext->Env, txtBytes); + + (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback, + pContext->JavaObj, flags, interfaceIndex, + (*pContext->Env)->NewStringUTF( pContext->Env, fullname), + (*pContext->Env)->NewStringUTF( pContext->Env, hosttarget), + port, txtObj); + } + else + ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); + } + + TeardownCallbackState(); } JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleResolver_CreateResolver( JNIEnv *pEnv, jobject pThis, - jint flags, jint ifIndex, jstring serviceName, jstring regType, jstring domain) + jint flags, jint ifIndex, jstring serviceName, jstring regType, jstring domain) { - jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); - jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); - OpContext *pContext = NULL; - DNSServiceErrorType err = kDNSServiceErr_NoError; - - if ( contextField != 0) - pContext = NewContext( pEnv, pThis, "serviceResolved", - "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;ILcom/apple/dnssd/TXTRecord;)V"); - else - err = kDNSServiceErr_BadParam; - - if ( pContext != NULL) - { - const char *servStr = SafeGetUTFChars( pEnv, serviceName); - const char *regStr = SafeGetUTFChars( pEnv, regType); - const char *domainStr = SafeGetUTFChars( pEnv, domain); - - err = DNSServiceResolve( &pContext->ServiceRef, flags, ifIndex, - servStr, regStr, domainStr, ServiceResolveReply, pContext); - if ( err == kDNSServiceErr_NoError) - { - (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext); - } - - SafeReleaseUTFChars( pEnv, serviceName, servStr); - SafeReleaseUTFChars( pEnv, regType, regStr); - SafeReleaseUTFChars( pEnv, domain, domainStr); - } - else - err = kDNSServiceErr_NoMemory; - - return err; + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + + if ( contextField != 0) + pContext = NewContext( pEnv, pThis, "serviceResolved", + "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;ILcom/apple/dnssd/TXTRecord;)V"); + else + err = kDNSServiceErr_BadParam; + + if ( pContext != NULL) + { + const char *servStr = SafeGetUTFChars( pEnv, serviceName); + const char *regStr = SafeGetUTFChars( pEnv, regType); + const char *domainStr = SafeGetUTFChars( pEnv, domain); + + err = DNSServiceResolve( &pContext->ServiceRef, flags, ifIndex, + servStr, regStr, domainStr, ServiceResolveReply, pContext); + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext); + } + + SafeReleaseUTFChars( pEnv, serviceName, servStr); + SafeReleaseUTFChars( pEnv, regType, regStr); + SafeReleaseUTFChars( pEnv, domain, domainStr); + } + else + err = kDNSServiceErr_NoMemory; + + return err; } -static void DNSSD_API ServiceRegisterReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, - DNSServiceErrorType errorCode, const char *serviceName, - const char *regType, const char *domain, void *context) +static void DNSSD_API ServiceRegisterReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, + DNSServiceErrorType errorCode, const char *serviceName, + const char *regType, const char *domain, void *context) { - OpContext *pContext = (OpContext*) context; - - SetupCallbackState( &pContext->Env); - - if ( pContext->ClientObj != NULL && pContext->Callback != NULL) - { - if ( errorCode == kDNSServiceErr_NoError) - { - (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback, - pContext->JavaObj, flags, - (*pContext->Env)->NewStringUTF( pContext->Env, serviceName), - (*pContext->Env)->NewStringUTF( pContext->Env, regType), - (*pContext->Env)->NewStringUTF( pContext->Env, domain)); - } - else - ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); - } - TeardownCallbackState(); + OpContext *pContext = (OpContext*) context; + + SetupCallbackState( &pContext->Env); + + if ( pContext->ClientObj != NULL && pContext->Callback != NULL) + { + if ( errorCode == kDNSServiceErr_NoError) + { + (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback, + pContext->JavaObj, flags, + (*pContext->Env)->NewStringUTF( pContext->Env, serviceName), + (*pContext->Env)->NewStringUTF( pContext->Env, regType), + (*pContext->Env)->NewStringUTF( pContext->Env, domain)); + } + else + ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); + } + TeardownCallbackState(); } JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRegistration_BeginRegister( JNIEnv *pEnv, jobject pThis, - jint ifIndex, jint flags, jstring serviceName, jstring regType, - jstring domain, jstring host, jint port, jbyteArray txtRecord) + jint ifIndex, jint flags, jstring serviceName, jstring regType, + jstring domain, jstring host, jint port, jbyteArray txtRecord) { - //syslog(LOG_ERR, "BR"); - jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); - jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); - OpContext *pContext = NULL; - DNSServiceErrorType err = kDNSServiceErr_NoError; - jbyte *pBytes; - jsize numBytes; - - //syslog(LOG_ERR, "BR: contextField %d", contextField); - - if ( contextField != 0) - pContext = NewContext( pEnv, pThis, "serviceRegistered", - "(Lcom/apple/dnssd/DNSSDRegistration;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); - else - err = kDNSServiceErr_BadParam; - - if ( pContext != NULL) - { - const char *servStr = SafeGetUTFChars( pEnv, serviceName); - const char *regStr = SafeGetUTFChars( pEnv, regType); - const char *domainStr = SafeGetUTFChars( pEnv, domain); - const char *hostStr = SafeGetUTFChars( pEnv, host); - - //syslog(LOG_ERR, "BR: regStr %s", regStr); - - // Since Java ints are defined to be big-endian, we de-canonicalize 'port' from a - // big-endian number into a 16-bit pattern here. - uint16_t portBits = port; - portBits = ( ((unsigned char*) &portBits)[0] << 8) | ((unsigned char*) &portBits)[1]; - - pBytes = txtRecord ? (*pEnv)->GetByteArrayElements( pEnv, txtRecord, NULL) : NULL; - numBytes = txtRecord ? (*pEnv)->GetArrayLength( pEnv, txtRecord) : 0; - - err = DNSServiceRegister( &pContext->ServiceRef, flags, ifIndex, servStr, regStr, - domainStr, hostStr, portBits, - numBytes, pBytes, ServiceRegisterReply, pContext); - if ( err == kDNSServiceErr_NoError) - { - (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext); - } - - if ( pBytes != NULL) - (*pEnv)->ReleaseByteArrayElements( pEnv, txtRecord, pBytes, 0); - - SafeReleaseUTFChars( pEnv, serviceName, servStr); - SafeReleaseUTFChars( pEnv, regType, regStr); - SafeReleaseUTFChars( pEnv, domain, domainStr); - SafeReleaseUTFChars( pEnv, host, hostStr); - } - else - err = kDNSServiceErr_NoMemory; - - return err; + //syslog(LOG_ERR, "BR"); + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + jbyte *pBytes; + jsize numBytes; + + //syslog(LOG_ERR, "BR: contextField %d", contextField); + + if ( contextField != 0) + pContext = NewContext( pEnv, pThis, "serviceRegistered", + "(Lcom/apple/dnssd/DNSSDRegistration;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + else + err = kDNSServiceErr_BadParam; + + if ( pContext != NULL) + { + const char *servStr = SafeGetUTFChars( pEnv, serviceName); + const char *regStr = SafeGetUTFChars( pEnv, regType); + const char *domainStr = SafeGetUTFChars( pEnv, domain); + const char *hostStr = SafeGetUTFChars( pEnv, host); + + //syslog(LOG_ERR, "BR: regStr %s", regStr); + + // Since Java ints are defined to be big-endian, we de-canonicalize 'port' from a + // big-endian number into a 16-bit pattern here. + uint16_t portBits = port; + portBits = ( ((unsigned char*) &portBits)[0] << 8) | ((unsigned char*) &portBits)[1]; + + pBytes = txtRecord ? (*pEnv)->GetByteArrayElements( pEnv, txtRecord, NULL) : NULL; + numBytes = txtRecord ? (*pEnv)->GetArrayLength( pEnv, txtRecord) : 0; + + err = DNSServiceRegister( &pContext->ServiceRef, flags, ifIndex, servStr, regStr, + domainStr, hostStr, portBits, + numBytes, pBytes, ServiceRegisterReply, pContext); + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext); + } + + if ( pBytes != NULL) + (*pEnv)->ReleaseByteArrayElements( pEnv, txtRecord, pBytes, 0); + + SafeReleaseUTFChars( pEnv, serviceName, servStr); + SafeReleaseUTFChars( pEnv, regType, regStr); + SafeReleaseUTFChars( pEnv, domain, domainStr); + SafeReleaseUTFChars( pEnv, host, hostStr); + } + else + err = kDNSServiceErr_NoMemory; + + return err; } JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRegistration_AddRecord( JNIEnv *pEnv, jobject pThis, - jint flags, jint rrType, jbyteArray rData, jint ttl, jobject destObj) + jint flags, jint rrType, jbyteArray rData, jint ttl, jobject destObj) { - jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); - jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); - jclass destCls = (*pEnv)->GetObjectClass( pEnv, destObj); - jfieldID recField = (*pEnv)->GetFieldID( pEnv, destCls, "fRecord", "J"); - OpContext *pContext = NULL; - DNSServiceErrorType err = kDNSServiceErr_NoError; - jbyte *pBytes; - jsize numBytes; - DNSRecordRef recRef; - - if ( contextField != 0) - pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField); - if ( pContext == NULL || pContext->ServiceRef == NULL) - return kDNSServiceErr_BadParam; - - pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL); - numBytes = (*pEnv)->GetArrayLength( pEnv, rData); - - err = DNSServiceAddRecord( pContext->ServiceRef, &recRef, flags, rrType, numBytes, pBytes, ttl); - if ( err == kDNSServiceErr_NoError) - { - (*pEnv)->SetLongField(pEnv, destObj, recField, (long) recRef); - } - - if ( pBytes != NULL) - (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0); - - return err; + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + jclass destCls = (*pEnv)->GetObjectClass( pEnv, destObj); + jfieldID recField = (*pEnv)->GetFieldID( pEnv, destCls, "fRecord", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + jbyte *pBytes; + jsize numBytes; + DNSRecordRef recRef; + + if ( contextField != 0) + pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField); + if ( pContext == NULL || pContext->ServiceRef == NULL) + return kDNSServiceErr_BadParam; + + pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL); + numBytes = (*pEnv)->GetArrayLength( pEnv, rData); + + err = DNSServiceAddRecord( pContext->ServiceRef, &recRef, flags, rrType, numBytes, pBytes, ttl); + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField(pEnv, destObj, recField, (long) recRef); + } + + if ( pBytes != NULL) + (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0); + + return err; } JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSRecord_Update( JNIEnv *pEnv, jobject pThis, - jint flags, jbyteArray rData, jint ttl) + jint flags, jbyteArray rData, jint ttl) { - jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); - jfieldID ownerField = (*pEnv)->GetFieldID( pEnv, cls, "fOwner", "Lcom/apple/dnssd/AppleService;"); - jfieldID recField = (*pEnv)->GetFieldID( pEnv, cls, "fRecord", "J"); - OpContext *pContext = NULL; - DNSServiceErrorType err = kDNSServiceErr_NoError; - jbyte *pBytes; - jsize numBytes; - DNSRecordRef recRef = NULL; - - if ( ownerField != 0) - { - jobject ownerObj = (*pEnv)->GetObjectField( pEnv, pThis, ownerField); - jclass ownerClass = (*pEnv)->GetObjectClass( pEnv, ownerObj); - jfieldID contextField = (*pEnv)->GetFieldID( pEnv, ownerClass, "fNativeContext", "J"); - if ( contextField != 0) - pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, ownerObj, contextField); - } - if ( recField != 0) - recRef = (DNSRecordRef) (long) (*pEnv)->GetLongField(pEnv, pThis, recField); - if ( pContext == NULL || pContext->ServiceRef == NULL) - return kDNSServiceErr_BadParam; - - pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL); - numBytes = (*pEnv)->GetArrayLength( pEnv, rData); - - err = DNSServiceUpdateRecord( pContext->ServiceRef, recRef, flags, numBytes, pBytes, ttl); - - if ( pBytes != NULL) - (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0); - - return err; + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID ownerField = (*pEnv)->GetFieldID( pEnv, cls, "fOwner", "Lcom/apple/dnssd/AppleService;"); + jfieldID recField = (*pEnv)->GetFieldID( pEnv, cls, "fRecord", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + jbyte *pBytes; + jsize numBytes; + DNSRecordRef recRef = NULL; + + if ( ownerField != 0) + { + jobject ownerObj = (*pEnv)->GetObjectField( pEnv, pThis, ownerField); + jclass ownerClass = (*pEnv)->GetObjectClass( pEnv, ownerObj); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, ownerClass, "fNativeContext", "J"); + if ( contextField != 0) + pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, ownerObj, contextField); + } + if ( recField != 0) + recRef = (DNSRecordRef) (long) (*pEnv)->GetLongField(pEnv, pThis, recField); + if ( pContext == NULL || pContext->ServiceRef == NULL) + return kDNSServiceErr_BadParam; + + pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL); + numBytes = (*pEnv)->GetArrayLength( pEnv, rData); + + err = DNSServiceUpdateRecord( pContext->ServiceRef, recRef, flags, numBytes, pBytes, ttl); + + if ( pBytes != NULL) + (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0); + + return err; } JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSRecord_Remove( JNIEnv *pEnv, jobject pThis) { - jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); - jfieldID ownerField = (*pEnv)->GetFieldID( pEnv, cls, "fOwner", "Lcom/apple/dnssd/AppleService;"); - jfieldID recField = (*pEnv)->GetFieldID( pEnv, cls, "fRecord", "J"); - OpContext *pContext = NULL; - DNSServiceErrorType err = kDNSServiceErr_NoError; - DNSRecordRef recRef = NULL; - - if ( ownerField != 0) - { - jobject ownerObj = (*pEnv)->GetObjectField( pEnv, pThis, ownerField); - jclass ownerClass = (*pEnv)->GetObjectClass( pEnv, ownerObj); - jfieldID contextField = (*pEnv)->GetFieldID( pEnv, ownerClass, "fNativeContext", "J"); - if ( contextField != 0) - pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, ownerObj, contextField); - } - if ( recField != 0) - recRef = (DNSRecordRef) (long) (*pEnv)->GetLongField(pEnv, pThis, recField); - if ( pContext == NULL || pContext->ServiceRef == NULL) - return kDNSServiceErr_BadParam; - - err = DNSServiceRemoveRecord( pContext->ServiceRef, recRef, 0); - - return err; + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID ownerField = (*pEnv)->GetFieldID( pEnv, cls, "fOwner", "Lcom/apple/dnssd/AppleService;"); + jfieldID recField = (*pEnv)->GetFieldID( pEnv, cls, "fRecord", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + DNSRecordRef recRef = NULL; + + if ( ownerField != 0) + { + jobject ownerObj = (*pEnv)->GetObjectField( pEnv, pThis, ownerField); + jclass ownerClass = (*pEnv)->GetObjectClass( pEnv, ownerObj); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, ownerClass, "fNativeContext", "J"); + if ( contextField != 0) + pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, ownerObj, contextField); + } + if ( recField != 0) + recRef = (DNSRecordRef) (long) (*pEnv)->GetLongField(pEnv, pThis, recField); + if ( pContext == NULL || pContext->ServiceRef == NULL) + return kDNSServiceErr_BadParam; + + err = DNSServiceRemoveRecord( pContext->ServiceRef, recRef, 0); + + return err; } JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRecordRegistrar_CreateConnection( JNIEnv *pEnv, jobject pThis) { - jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); - jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); - OpContext *pContext = NULL; - DNSServiceErrorType err = kDNSServiceErr_NoError; - - if ( contextField != 0) - pContext = NewContext( pEnv, pThis, "recordRegistered", "(Lcom/apple/dnssd/DNSRecord;I)V"); - else - err = kDNSServiceErr_BadParam; - - if ( pContext != NULL) - { - err = DNSServiceCreateConnection( &pContext->ServiceRef); - if ( err == kDNSServiceErr_NoError) - { - (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext); - } - } - else - err = kDNSServiceErr_NoMemory; - - return err; + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + + if ( contextField != 0) + pContext = NewContext( pEnv, pThis, "recordRegistered", "(Lcom/apple/dnssd/DNSRecord;I)V"); + else + err = kDNSServiceErr_BadParam; + + if ( pContext != NULL) + { + err = DNSServiceCreateConnection( &pContext->ServiceRef); + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext); + } + } + else + err = kDNSServiceErr_NoMemory; + + return err; } struct RecordRegistrationRef { - OpContext *Context; - jobject RecordObj; + OpContext *Context; + jobject RecordObj; }; -typedef struct RecordRegistrationRef RecordRegistrationRef; +typedef struct RecordRegistrationRef RecordRegistrationRef; -static void DNSSD_API RegisterRecordReply( DNSServiceRef sdRef _UNUSED, - DNSRecordRef recordRef _UNUSED, DNSServiceFlags flags, - DNSServiceErrorType errorCode, void *context) +static void DNSSD_API RegisterRecordReply( DNSServiceRef sdRef _UNUSED, + DNSRecordRef recordRef _UNUSED, DNSServiceFlags flags, + DNSServiceErrorType errorCode, void *context) { - RecordRegistrationRef *regEnvelope = (RecordRegistrationRef*) context; - OpContext *pContext = regEnvelope->Context; - - SetupCallbackState( &pContext->Env); - - if ( pContext->ClientObj != NULL && pContext->Callback != NULL) - { - if ( errorCode == kDNSServiceErr_NoError) - { - (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback, - regEnvelope->RecordObj, flags); - } - else - ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); - } - - (*pContext->Env)->DeleteWeakGlobalRef( pContext->Env, regEnvelope->RecordObj); - free( regEnvelope); - - TeardownCallbackState(); + RecordRegistrationRef *regEnvelope = (RecordRegistrationRef*) context; + OpContext *pContext = regEnvelope->Context; + + SetupCallbackState( &pContext->Env); + + if ( pContext->ClientObj != NULL && pContext->Callback != NULL) + { + if ( errorCode == kDNSServiceErr_NoError) + { + (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback, + regEnvelope->RecordObj, flags); + } + else + ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); + } + + (*pContext->Env)->DeleteWeakGlobalRef( pContext->Env, regEnvelope->RecordObj); + free( regEnvelope); + + TeardownCallbackState(); } -JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRecordRegistrar_RegisterRecord( JNIEnv *pEnv, jobject pThis, - jint flags, jint ifIndex, jstring fullname, jint rrType, jint rrClass, - jbyteArray rData, jint ttl, jobject destObj) +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRecordRegistrar_RegisterRecord( JNIEnv *pEnv, jobject pThis, + jint flags, jint ifIndex, jstring fullname, jint rrType, jint rrClass, + jbyteArray rData, jint ttl, jobject destObj) { - jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); - jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); - jclass destCls = (*pEnv)->GetObjectClass( pEnv, destObj); - jfieldID recField = (*pEnv)->GetFieldID( pEnv, destCls, "fRecord", "J"); - const char *nameStr = SafeGetUTFChars( pEnv, fullname); - OpContext *pContext = NULL; - DNSServiceErrorType err = kDNSServiceErr_NoError; - jbyte *pBytes; - jsize numBytes; - DNSRecordRef recRef; - RecordRegistrationRef *regEnvelope; - - if ( contextField != 0) - pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField); - if ( pContext == NULL || pContext->ServiceRef == NULL || nameStr == NULL) - return kDNSServiceErr_BadParam; - - regEnvelope = calloc( 1, sizeof *regEnvelope); - if ( regEnvelope == NULL) - return kDNSServiceErr_NoMemory; - regEnvelope->Context = pContext; - regEnvelope->RecordObj = (*pEnv)->NewWeakGlobalRef( pEnv, destObj); // must convert local ref to global to cache - - pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL); - numBytes = (*pEnv)->GetArrayLength( pEnv, rData); - - err = DNSServiceRegisterRecord( pContext->ServiceRef, &recRef, flags, ifIndex, - nameStr, rrType, rrClass, numBytes, pBytes, ttl, - RegisterRecordReply, regEnvelope); - - if ( err == kDNSServiceErr_NoError) - { - (*pEnv)->SetLongField(pEnv, destObj, recField, (long) recRef); - } - else - { - if ( regEnvelope->RecordObj != NULL) - (*pEnv)->DeleteWeakGlobalRef( pEnv, regEnvelope->RecordObj); - free( regEnvelope); - } - - if ( pBytes != NULL) - (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0); - - SafeReleaseUTFChars( pEnv, fullname, nameStr); - - return err; + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + jclass destCls = (*pEnv)->GetObjectClass( pEnv, destObj); + jfieldID recField = (*pEnv)->GetFieldID( pEnv, destCls, "fRecord", "J"); + const char *nameStr = SafeGetUTFChars( pEnv, fullname); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + jbyte *pBytes; + jsize numBytes; + DNSRecordRef recRef; + RecordRegistrationRef *regEnvelope; + + if ( contextField != 0) + pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField); + if ( pContext == NULL || pContext->ServiceRef == NULL || nameStr == NULL) + return kDNSServiceErr_BadParam; + + regEnvelope = calloc( 1, sizeof *regEnvelope); + if ( regEnvelope == NULL) + return kDNSServiceErr_NoMemory; + regEnvelope->Context = pContext; + regEnvelope->RecordObj = (*pEnv)->NewWeakGlobalRef( pEnv, destObj); // must convert local ref to global to cache + + pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL); + numBytes = (*pEnv)->GetArrayLength( pEnv, rData); + + err = DNSServiceRegisterRecord( pContext->ServiceRef, &recRef, flags, ifIndex, + nameStr, rrType, rrClass, numBytes, pBytes, ttl, + RegisterRecordReply, regEnvelope); + + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField(pEnv, destObj, recField, (long) recRef); + } + else + { + if ( regEnvelope->RecordObj != NULL) + (*pEnv)->DeleteWeakGlobalRef( pEnv, regEnvelope->RecordObj); + free( regEnvelope); + } + + if ( pBytes != NULL) + (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0); + + SafeReleaseUTFChars( pEnv, fullname, nameStr); + + return err; } -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) +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) { - OpContext *pContext = (OpContext*) context; - jbyteArray rDataObj; - jbyte *pBytes; - - SetupCallbackState( &pContext->Env); - - if ( pContext->ClientObj != NULL && pContext->Callback != NULL && - NULL != ( rDataObj = (*pContext->Env)->NewByteArray( pContext->Env, rdlen))) - { - if ( errorCode == kDNSServiceErr_NoError) - { - // Initialize rDataObj with contents of rdata - pBytes = (*pContext->Env)->GetByteArrayElements( pContext->Env, rDataObj, NULL); - memcpy( pBytes, rdata, rdlen); - (*pContext->Env)->ReleaseByteArrayElements( pContext->Env, rDataObj, pBytes, JNI_COMMIT); - - (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback, - pContext->JavaObj, flags, interfaceIndex, - (*pContext->Env)->NewStringUTF( pContext->Env, serviceName), - rrtype, rrclass, rDataObj, ttl); - } - else - ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); - } - TeardownCallbackState(); + OpContext *pContext = (OpContext*) context; + jbyteArray rDataObj; + jbyte *pBytes; + + SetupCallbackState( &pContext->Env); + + if ( pContext->ClientObj != NULL && pContext->Callback != NULL && + NULL != ( rDataObj = (*pContext->Env)->NewByteArray( pContext->Env, rdlen))) + { + if ( errorCode == kDNSServiceErr_NoError) + { + // Initialize rDataObj with contents of rdata + pBytes = (*pContext->Env)->GetByteArrayElements( pContext->Env, rDataObj, NULL); + memcpy( pBytes, rdata, rdlen); + (*pContext->Env)->ReleaseByteArrayElements( pContext->Env, rDataObj, pBytes, JNI_COMMIT); + + (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback, + pContext->JavaObj, flags, interfaceIndex, + (*pContext->Env)->NewStringUTF( pContext->Env, serviceName), + rrtype, rrclass, rDataObj, ttl); + } + else + ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); + } + TeardownCallbackState(); } JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleQuery_CreateQuery( JNIEnv *pEnv, jobject pThis, - jint flags, jint ifIndex, jstring serviceName, jint rrtype, jint rrclass) + jint flags, jint ifIndex, jstring serviceName, jint rrtype, jint rrclass) { - jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); - jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); - OpContext *pContext = NULL; - DNSServiceErrorType err = kDNSServiceErr_NoError; - - if ( contextField != 0) - pContext = NewContext( pEnv, pThis, "queryAnswered", - "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;II[BI)V"); - else - err = kDNSServiceErr_BadParam; - - if ( pContext != NULL) - { - const char *servStr = SafeGetUTFChars( pEnv, serviceName); - - err = DNSServiceQueryRecord( &pContext->ServiceRef, flags, ifIndex, servStr, - rrtype, rrclass, ServiceQueryReply, pContext); - if ( err == kDNSServiceErr_NoError) - { - (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext); - } - - SafeReleaseUTFChars( pEnv, serviceName, servStr); - } - else - err = kDNSServiceErr_NoMemory; - - return err; + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + + if ( contextField != 0) + pContext = NewContext( pEnv, pThis, "queryAnswered", + "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;II[BI)V"); + else + err = kDNSServiceErr_BadParam; + + if ( pContext != NULL) + { + const char *servStr = SafeGetUTFChars( pEnv, serviceName); + + err = DNSServiceQueryRecord( &pContext->ServiceRef, flags, ifIndex, servStr, + rrtype, rrclass, ServiceQueryReply, pContext); + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext); + } + + SafeReleaseUTFChars( pEnv, serviceName, servStr); + } + else + err = kDNSServiceErr_NoMemory; + + return err; } -static void DNSSD_API DomainEnumReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, - DNSServiceErrorType errorCode, const char *replyDomain, void *context) +static void DNSSD_API DomainEnumReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *replyDomain, void *context) { - OpContext *pContext = (OpContext*) context; - - SetupCallbackState( &pContext->Env); - - if ( pContext->ClientObj != NULL && pContext->Callback != NULL) - { - if ( errorCode == kDNSServiceErr_NoError) - { - (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, - ( flags & kDNSServiceFlagsAdd) != 0 ? pContext->Callback : pContext->Callback2, - pContext->JavaObj, flags, interfaceIndex, - (*pContext->Env)->NewStringUTF( pContext->Env, replyDomain)); - } - else - ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); - } - TeardownCallbackState(); + OpContext *pContext = (OpContext*) context; + + SetupCallbackState( &pContext->Env); + + if ( pContext->ClientObj != NULL && pContext->Callback != NULL) + { + if ( errorCode == kDNSServiceErr_NoError) + { + (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, + ( flags & kDNSServiceFlagsAdd) != 0 ? pContext->Callback : pContext->Callback2, + pContext->JavaObj, flags, interfaceIndex, + (*pContext->Env)->NewStringUTF( pContext->Env, replyDomain)); + } + else + ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); + } + TeardownCallbackState(); } JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDomainEnum_BeginEnum( JNIEnv *pEnv, jobject pThis, - jint flags, jint ifIndex) + jint flags, jint ifIndex) { - jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); - jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); - OpContext *pContext = NULL; - DNSServiceErrorType err = kDNSServiceErr_NoError; - - if ( contextField != 0) - pContext = NewContext( pEnv, pThis, "domainFound", - "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;)V"); - else - err = kDNSServiceErr_BadParam; - - if ( pContext != NULL) - { - pContext->Callback2 = (*pEnv)->GetMethodID( pEnv, - (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj), - "domainLost", "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;)V"); - - err = DNSServiceEnumerateDomains( &pContext->ServiceRef, flags, ifIndex, - DomainEnumReply, pContext); - if ( err == kDNSServiceErr_NoError) - { - (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext); - } - } - else - err = kDNSServiceErr_NoMemory; - - return err; + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + + if ( contextField != 0) + pContext = NewContext( pEnv, pThis, "domainFound", + "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;)V"); + else + err = kDNSServiceErr_BadParam; + + if ( pContext != NULL) + { + pContext->Callback2 = (*pEnv)->GetMethodID( pEnv, + (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj), + "domainLost", "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;)V"); + + err = DNSServiceEnumerateDomains( &pContext->ServiceRef, flags, ifIndex, + DomainEnumReply, pContext); + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext); + } + } + else + err = kDNSServiceErr_NoMemory; + + return err; } JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_ConstructName( JNIEnv *pEnv, jobject pThis _UNUSED, - jstring serviceName, jstring regtype, jstring domain, jobjectArray pOut) + jstring serviceName, jstring regtype, jstring domain, jobjectArray pOut) { - DNSServiceErrorType err = kDNSServiceErr_NoError; - const char *nameStr = SafeGetUTFChars( pEnv, serviceName); - const char *regStr = SafeGetUTFChars( pEnv, regtype); - const char *domStr = SafeGetUTFChars( pEnv, domain); - char buff[ kDNSServiceMaxDomainName + 1]; + DNSServiceErrorType err = kDNSServiceErr_NoError; + const char *nameStr = SafeGetUTFChars( pEnv, serviceName); + const char *regStr = SafeGetUTFChars( pEnv, regtype); + const char *domStr = SafeGetUTFChars( pEnv, domain); + char buff[ kDNSServiceMaxDomainName + 1]; - err = DNSServiceConstructFullName( buff, nameStr, regStr, domStr); + err = DNSServiceConstructFullName( buff, nameStr, regStr, domStr); - if ( err == kDNSServiceErr_NoError) - { - // pOut is expected to be a String[1] array. - (*pEnv)->SetObjectArrayElement( pEnv, pOut, 0, (*pEnv)->NewStringUTF( pEnv, buff)); - } + if ( err == kDNSServiceErr_NoError) + { + // pOut is expected to be a String[1] array. + (*pEnv)->SetObjectArrayElement( pEnv, pOut, 0, (*pEnv)->NewStringUTF( pEnv, buff)); + } - SafeReleaseUTFChars( pEnv, serviceName, nameStr); - SafeReleaseUTFChars( pEnv, regtype, regStr); - SafeReleaseUTFChars( pEnv, domain, domStr); + SafeReleaseUTFChars( pEnv, serviceName, nameStr); + SafeReleaseUTFChars( pEnv, regtype, regStr); + SafeReleaseUTFChars( pEnv, domain, domStr); - return err; + return err; } JNIEXPORT void JNICALL Java_com_apple_dnssd_AppleDNSSD_ReconfirmRecord( JNIEnv *pEnv, jobject pThis _UNUSED, - jint flags, jint ifIndex, jstring fullName, - jint rrtype, jint rrclass, jbyteArray rdata) + jint flags, jint ifIndex, jstring fullName, + jint rrtype, jint rrclass, jbyteArray rdata) { - jbyte *pBytes; - jsize numBytes; - const char *nameStr = SafeGetUTFChars( pEnv, fullName); + jbyte *pBytes; + jsize numBytes; + const char *nameStr = SafeGetUTFChars( pEnv, fullName); - pBytes = (*pEnv)->GetByteArrayElements( pEnv, rdata, NULL); - numBytes = (*pEnv)->GetArrayLength( pEnv, rdata); + pBytes = (*pEnv)->GetByteArrayElements( pEnv, rdata, NULL); + numBytes = (*pEnv)->GetArrayLength( pEnv, rdata); - DNSServiceReconfirmRecord( flags, ifIndex, nameStr, rrtype, rrclass, numBytes, pBytes); + DNSServiceReconfirmRecord( flags, ifIndex, nameStr, rrtype, rrclass, numBytes, pBytes); - if ( pBytes != NULL) - (*pEnv)->ReleaseByteArrayElements( pEnv, rdata, pBytes, 0); + if ( pBytes != NULL) + (*pEnv)->ReleaseByteArrayElements( pEnv, rdata, pBytes, 0); - SafeReleaseUTFChars( pEnv, fullName, nameStr); + SafeReleaseUTFChars( pEnv, fullName, nameStr); } #define LOCAL_ONLY_NAME "loo" #define P2P_NAME "p2p" JNIEXPORT jstring JNICALL Java_com_apple_dnssd_AppleDNSSD_GetNameForIfIndex( JNIEnv *pEnv, jobject pThis _UNUSED, - jint ifIndex) + jint ifIndex) { - char *p = LOCAL_ONLY_NAME, nameBuff[IF_NAMESIZE]; + char *p = LOCAL_ONLY_NAME, nameBuff[IF_NAMESIZE]; - if (ifIndex == (jint) kDNSServiceInterfaceIndexP2P) - p = P2P_NAME; - else if (ifIndex != (jint) kDNSServiceInterfaceIndexLocalOnly) - p = if_indextoname( ifIndex, nameBuff ); + if (ifIndex == (jint) kDNSServiceInterfaceIndexP2P) + p = P2P_NAME; + else if (ifIndex != (jint) kDNSServiceInterfaceIndexLocalOnly) + p = if_indextoname( ifIndex, nameBuff ); - return (*pEnv)->NewStringUTF( pEnv, p); + return (*pEnv)->NewStringUTF( pEnv, p); } JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_GetIfIndexForName( JNIEnv *pEnv, jobject pThis _UNUSED, - jstring ifName) + jstring ifName) { - uint32_t ifIndex = kDNSServiceInterfaceIndexLocalOnly; - const char *nameStr = SafeGetUTFChars( pEnv, ifName); + uint32_t ifIndex = kDNSServiceInterfaceIndexLocalOnly; + const char *nameStr = SafeGetUTFChars( pEnv, ifName); - if (strcmp(nameStr, P2P_NAME) == 0) - ifIndex = kDNSServiceInterfaceIndexP2P; - else if (strcmp(nameStr, LOCAL_ONLY_NAME)) - ifIndex = if_nametoindex( nameStr); + if (strcmp(nameStr, P2P_NAME) == 0) + ifIndex = kDNSServiceInterfaceIndexP2P; + else if (strcmp(nameStr, LOCAL_ONLY_NAME)) + ifIndex = if_nametoindex( nameStr); - SafeReleaseUTFChars( pEnv, ifName, nameStr); + SafeReleaseUTFChars( pEnv, ifName, nameStr); - return ifIndex; + return ifIndex; } @@ -954,109 +954,109 @@ JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_GetIfIndexForName( JNIEnv static char* win32_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; - } + 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; - } + if (pAdapterInfo != NULL) + { + free( pAdapterInfo ); + pAdapterInfo = NULL; + } - return ifName; + return ifName; } static DWORD win32_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; - } + 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; - } + if (pAdapterInfo != NULL) + { + free( pAdapterInfo ); + pAdapterInfo = NULL; + } - return ifIndex; + return ifIndex; } #endif @@ -1064,7 +1064,7 @@ exit: // 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) // NOT static -- otherwise the compiler may optimize it out diff --git a/mDNSShared/PlatformCommon.c b/mDNSShared/PlatformCommon.c index 7a1985a..f650c10 100644 --- a/mDNSShared/PlatformCommon.c +++ b/mDNSShared/PlatformCommon.c @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,182 +15,182 @@ * limitations under the License. */ -#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 // 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 -#include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above +#include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above #include "DNSCommon.h" #include "PlatformCommon.h" #ifdef NOT_HAVE_SOCKLEN_T - typedef unsigned int socklen_t; +typedef unsigned int socklen_t; #endif // Bind a UDP socket to find the source address to a destination mDNSexport void mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst) - { - union { struct sockaddr s; struct sockaddr_in a4; struct sockaddr_in6 a6; } addr; - socklen_t len = sizeof(addr); - socklen_t inner_len = 0; - int sock = socket(AF_INET, SOCK_DGRAM, 0); - src->type = mDNSAddrType_None; - if (sock == -1) return; - if (dst->type == mDNSAddrType_IPv4) - { - inner_len = sizeof(addr.a4); - #ifndef NOT_HAVE_SA_LEN - addr.a4.sin_len = inner_len; - #endif - addr.a4.sin_family = AF_INET; - addr.a4.sin_port = 1; // Not important, any port will do - addr.a4.sin_addr.s_addr = dst->ip.v4.NotAnInteger; - } - else if (dst->type == mDNSAddrType_IPv6) - { - inner_len = sizeof(addr.a6); - #ifndef NOT_HAVE_SA_LEN - addr.a6.sin6_len = inner_len; - #endif - addr.a6.sin6_family = AF_INET6; - addr.a6.sin6_flowinfo = 0; - addr.a6.sin6_port = 1; // Not important, any port will do - addr.a6.sin6_addr = *(struct in6_addr*)&dst->ip.v6; - addr.a6.sin6_scope_id = 0; - } - else return; - - if ((connect(sock, &addr.s, inner_len)) < 0) - { LogMsg("mDNSPlatformSourceAddrForDest: connect %#a failed errno %d (%s)", dst, errno, strerror(errno)); goto exit; } - - if ((getsockname(sock, &addr.s, &len)) < 0) - { LogMsg("mDNSPlatformSourceAddrForDest: getsockname failed errno %d (%s)", errno, strerror(errno)); goto exit; } - - src->type = dst->type; - if (dst->type == mDNSAddrType_IPv4) src->ip.v4.NotAnInteger = addr.a4.sin_addr.s_addr; - else src->ip.v6 = *(mDNSv6Addr*)&addr.a6.sin6_addr; +{ + union { struct sockaddr s; struct sockaddr_in a4; struct sockaddr_in6 a6; } addr; + socklen_t len = sizeof(addr); + socklen_t inner_len = 0; + int sock = socket(AF_INET, SOCK_DGRAM, 0); + src->type = mDNSAddrType_None; + if (sock == -1) return; + if (dst->type == mDNSAddrType_IPv4) + { + inner_len = sizeof(addr.a4); + #ifndef NOT_HAVE_SA_LEN + addr.a4.sin_len = inner_len; + #endif + addr.a4.sin_family = AF_INET; + addr.a4.sin_port = 1; // Not important, any port will do + addr.a4.sin_addr.s_addr = dst->ip.v4.NotAnInteger; + } + else if (dst->type == mDNSAddrType_IPv6) + { + inner_len = sizeof(addr.a6); + #ifndef NOT_HAVE_SA_LEN + addr.a6.sin6_len = inner_len; + #endif + addr.a6.sin6_family = AF_INET6; + addr.a6.sin6_flowinfo = 0; + addr.a6.sin6_port = 1; // Not important, any port will do + addr.a6.sin6_addr = *(struct in6_addr*)&dst->ip.v6; + addr.a6.sin6_scope_id = 0; + } + else return; + + if ((connect(sock, &addr.s, inner_len)) < 0) + { LogMsg("mDNSPlatformSourceAddrForDest: connect %#a failed errno %d (%s)", dst, errno, strerror(errno)); goto exit; } + + if ((getsockname(sock, &addr.s, &len)) < 0) + { LogMsg("mDNSPlatformSourceAddrForDest: getsockname failed errno %d (%s)", errno, strerror(errno)); goto exit; } + + src->type = dst->type; + if (dst->type == mDNSAddrType_IPv4) src->ip.v4.NotAnInteger = addr.a4.sin_addr.s_addr; + else src->ip.v6 = *(mDNSv6Addr*)&addr.a6.sin6_addr; exit: - close(sock); - } + close(sock); +} // dst must be at least MAX_ESCAPED_DOMAIN_NAME bytes, and option must be less than 32 bytes in length mDNSlocal mDNSBool GetConfigOption(char *dst, const char *option, FILE *f) - { - char buf[32+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; - } +{ + char buf[32+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, mDNSBool *DomainDiscoveryDisabled) - { - char buf[MAX_ESCAPED_DOMAIN_NAME] = ""; - mStatus err; - FILE *f = fopen(filename, "r"); - - if (hostname) hostname->c[0] = 0; - if (domain) domain->c[0] = 0; - if (DomainDiscoveryDisabled) *DomainDiscoveryDisabled = mDNSfalse; - - if (f) - { - if (DomainDiscoveryDisabled && GetConfigOption(buf, "DomainDiscoveryDisabled", f) && !strcasecmp(buf, "true")) *DomainDiscoveryDisabled = mDNStrue; - if (hostname && GetConfigOption(buf, "hostname", f) && !MakeDomainNameFromDNSNameString(hostname, buf)) goto badf; - if (domain && GetConfigOption(buf, "zone", f) && !MakeDomainNameFromDNSNameString(domain, buf)) goto badf; - buf[0] = 0; - GetConfigOption(buf, "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 (domain && domain->c[0] && buf[0]) - { - DomainAuthInfo *info = (DomainAuthInfo*)mDNSPlatformMemAllocate(sizeof(*info)); - // for now we assume keyname = service reg domain and we use same key for service and hostname registration - err = mDNS_SetSecretForDomain(m, info, domain, domain, buf, NULL, 0, NULL); - if (err) LogMsg("ERROR: mDNS_SetSecretForDomain returned %d for domain %##s", err, domain->c); - } - - return; - - badf: - LogMsg("ERROR: malformatted config file"); - if (f) fclose(f); - } +{ + char buf[MAX_ESCAPED_DOMAIN_NAME] = ""; + mStatus err; + FILE *f = fopen(filename, "r"); + + if (hostname) hostname->c[0] = 0; + if (domain) domain->c[0] = 0; + if (DomainDiscoveryDisabled) *DomainDiscoveryDisabled = mDNSfalse; + + if (f) + { + if (DomainDiscoveryDisabled && GetConfigOption(buf, "DomainDiscoveryDisabled", f) && !strcasecmp(buf, "true")) *DomainDiscoveryDisabled = mDNStrue; + if (hostname && GetConfigOption(buf, "hostname", f) && !MakeDomainNameFromDNSNameString(hostname, buf)) goto badf; + if (domain && GetConfigOption(buf, "zone", f) && !MakeDomainNameFromDNSNameString(domain, buf)) goto badf; + buf[0] = 0; + GetConfigOption(buf, "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 (domain && domain->c[0] && buf[0]) + { + DomainAuthInfo *info = (DomainAuthInfo*)mDNSPlatformMemAllocate(sizeof(*info)); + // for now we assume keyname = service reg domain and we use same key for service and hostname registration + err = mDNS_SetSecretForDomain(m, info, domain, domain, buf, NULL, 0, mDNSfalse); + if (err) LogMsg("ERROR: mDNS_SetSecretForDomain returned %d for domain %##s", err, domain->c); + } + + return; + +badf: + LogMsg("ERROR: malformatted config file"); + if (f) fclose(f); +} #if MDNS_DEBUGMSGS mDNSexport void mDNSPlatformWriteDebugMsg(const char *msg) - { - fprintf(stderr,"%s\n", msg); - fflush(stderr); - } +{ + fprintf(stderr,"%s\n", msg); + fflush(stderr); +} #endif mDNSexport void mDNSPlatformWriteLogMsg(const char *ident, const char *buffer, mDNSLogLevel_t loglevel) - { +{ #if APPLE_OSX_mDNSResponder && LogTimeStamps - extern mDNS mDNSStorage; - extern mDNSu32 mDNSPlatformClockDivisor; - mDNSs32 t = mDNSStorage.timenow ? mDNSStorage.timenow : mDNSPlatformClockDivisor ? mDNS_TimeNow_NoLock(&mDNSStorage) : 0; - int ms = ((t < 0) ? -t : t) % 1000; + extern mDNS mDNSStorage; + extern mDNSu32 mDNSPlatformClockDivisor; + mDNSs32 t = mDNSStorage.timenow ? mDNSStorage.timenow : mDNSPlatformClockDivisor ? mDNS_TimeNow_NoLock(&mDNSStorage) : 0; + int ms = ((t < 0) ? -t : t) % 1000; #endif - if (mDNS_DebugMode) // In debug mode we write to stderr - { + if (mDNS_DebugMode) // In debug mode we write to stderr + { #if APPLE_OSX_mDNSResponder && LogTimeStamps - if (ident && ident[0] && mDNSPlatformClockDivisor) - fprintf(stderr,"%8d.%03d: %s\n", (int)(t/1000), ms, buffer); - else + if (ident && ident[0] && mDNSPlatformClockDivisor) + fprintf(stderr,"%8d.%03d: %s\n", (int)(t/1000), ms, buffer); + else #endif - fprintf(stderr,"%s\n", buffer); - fflush(stderr); - } - else // else, in production mode, we write to syslog - { - static int log_inited = 0; - - int syslog_level = LOG_ERR; - switch (loglevel) - { - case MDNS_LOG_MSG: syslog_level = LOG_ERR; break; - case MDNS_LOG_OPERATION: syslog_level = LOG_WARNING; break; - case MDNS_LOG_SPS: syslog_level = LOG_NOTICE; break; - case MDNS_LOG_INFO: syslog_level = LOG_INFO; break; - case MDNS_LOG_DEBUG: syslog_level = LOG_DEBUG; break; - default: - fprintf(stderr, "Unknown loglevel %d, assuming LOG_ERR\n", loglevel); - fflush(stderr); - } - - if (!log_inited) { openlog(ident, LOG_CONS, LOG_DAEMON); log_inited++; } + fprintf(stderr,"%s\n", buffer); + fflush(stderr); + } + else // else, in production mode, we write to syslog + { + static int log_inited = 0; + + int syslog_level = LOG_ERR; + switch (loglevel) + { + case MDNS_LOG_MSG: syslog_level = LOG_ERR; break; + case MDNS_LOG_OPERATION: syslog_level = LOG_WARNING; break; + case MDNS_LOG_SPS: syslog_level = LOG_NOTICE; break; + case MDNS_LOG_INFO: syslog_level = LOG_INFO; break; + case MDNS_LOG_DEBUG: syslog_level = LOG_DEBUG; break; + default: + fprintf(stderr, "Unknown loglevel %d, assuming LOG_ERR\n", loglevel); + fflush(stderr); + } + + if (!log_inited) { openlog(ident, LOG_CONS, LOG_DAEMON); log_inited++; } #if APPLE_OSX_mDNSResponder && LogTimeStamps - if (ident && ident[0] && mDNSPlatformClockDivisor) - syslog(syslog_level, "%8d.%03d: %s", (int)(t/1000), ms, buffer); - else + if (ident && ident[0] && mDNSPlatformClockDivisor) + syslog(syslog_level, "%8d.%03d: %s", (int)(t/1000), ms, buffer); + else #endif - syslog(syslog_level, "%s", buffer); - } - } + syslog(syslog_level, "%s", buffer); + } +} diff --git a/mDNSShared/PlatformCommon.h b/mDNSShared/PlatformCommon.h index 631e3d7..2a06871 100644 --- a/mDNSShared/PlatformCommon.h +++ b/mDNSShared/PlatformCommon.h @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/mDNSShared/dns-sd.1 b/mDNSShared/dns-sd.1 index da9f37a..9d8323b 100644 --- a/mDNSShared/dns-sd.1 +++ b/mDNSShared/dns-sd.1 @@ -23,11 +23,25 @@ .Nd Multicast DNS (mDNS) & DNS Service Discovery (DNS-SD) Test Tool \" For whatis .\" .Sh SYNOPSIS +.Nm Fl E +.Pp +.Nm Fl F +.Pp .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 +.Pp +.Nm Fl P Ar name type domain port host IP Op Ar key=value ... +.Pp +.Nm Fl q Ar name rrtype rrclass +.Pp +.Nm Fl Z Ar type domain +.Pp +.Nm Fl G Ns \ v4/v6/v4v6 Ar name +.Pp +.Nm Fl V .\" .Sh DESCRIPTION The @@ -46,7 +60,7 @@ uses is documented in The .Nm command replaces the older -.Xr mDNS 1 +mDNS command. .Pp The @@ -78,7 +92,17 @@ directly call DNS-SD APIs using the dnssd package documented at .br Similar bindings for other languages are also in development. .Pp -.Bl -tag -width R +.Bl -tag -width E +.It Nm Fl E +return a list of domains recommended for registering(advertising) services. +.Pp +.It Nm Fl F +return a list of domains recommended for browsing services. +.Pp +Normally, on your home network, the only domain you are likely to see is "local". +However if your network administrator has created Domain Enumeration records, +then you may also see other recommended domains for registering and browsing. +.Pp .It Nm Fl R Ar name type domain port Op Ar key=value ... register (advertise) a service in the specified .Ar domain @@ -96,7 +120,7 @@ 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 . +.Pa http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml . .Pp .Ar domain is the domain in which to register the service. @@ -115,7 +139,7 @@ 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 . +.Pa http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml . .It Nm Fl B Ar type domain browse for instances of service .Ar type @@ -125,7 +149,7 @@ in For valid .Ar type Ns s see -.Pa http://www.dns-sd.org/ServiceTypes.html +.Pa http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml . as described above. Omitting the .Ar domain or using "." means "pick a sensible default." @@ -135,12 +159,39 @@ 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 +Note that in a typical application, browsing may only happen 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. +.Pp +.It Nm Fl P Ar name type domain port host IP Op Ar key=value ... +create a proxy advertisement for a service running on(offered by) some other machine. +The two new options are Host, a name for the device and IP, the address of it. +.Pp +The service for which you create a proxy advertisement does not necessarily have to be on your local network. +You can set up a local proxy for a website on the Internet. +.Pp +.It Nm Fl q Ar name rrtype rrclass +look up any DNS name, resource record type, and resource record class, +not necessarily DNS-SD names and record types. +If rrtype is not specified, it queries for the IPv4 address of the name, +if rrclass is not specified, IN class is assumed. If the name is not a fully +qualified domain name, then search domains may be appended. +.Pp +.It Nm Fl Z Ar type domain +browse for service instances and display output in zone file format. +.Pp +.It Nm Fl G Ns \ v4/v6/v4v6 Ar name +look up the IP address information of the name. +If v4 is specified, the IPv4 address of the name is looked up, +if v6 is specified the IPv6 address is looked up. If v4v6 is specified both the IPv4 and IPv6 +address is looked up. If the name is not a fully qualified domain name, +then search domains may be appended. +.Pp +.It Nm Fl V +return the version of the currently running daemon/system service. .El .Sh EXAMPLES .Pp @@ -177,11 +228,32 @@ window and you should see the "Remove" event reported to the .Nm Fl B window. .Pp +In the example below, the www.apple.com web page is advertised as a service called "apple", +running on a target host called apple.local, which resolves to 17.149.160.49. +.Pp +.Dl Nm Fl P Ns \ apple _http._tcp \&"\&"\& 80 apple.local 17.149.160.49 +.Pp +The Bonjour menu in the Safari web browser will now show "apple". +The same IP address can be reached by entering apple.local in the web browser. +In either case, the request will be resolved to the IP address and browser will show +contents associated with www.apple.com. +.Pp +If a client wants to be notified of changes in server state, it can +initiate a query for the service's particular record and leave it running. +For example, to monitor the status of an iChat user you can use: +.Pp +.Dl Nm Fl q Ns \ someone@ex1._presence._tcp.local txt +.Pp +Everytime status of that user(someone) changes, you will see a new TXT record result reported. +.Pp +You can also query for a unicast name like www.apple.com and monitor its status. +.Pp +.Dl Nm Fl q Ns \ www.apple.com +.Pp .Sh FILES .Pa /usr/bin/dns-sd \" Pathname .\" .Sh SEE ALSO -.Xr mDNS 1 .Xr mDNSResponder 8 .\" .Sh BUGS diff --git a/mDNSShared/dns_sd.h b/mDNSShared/dns_sd.h index 3caa6a8..c491ab7 100644 --- a/mDNSShared/dns_sd.h +++ b/mDNSShared/dns_sd.h @@ -77,10 +77,10 @@ */ #ifndef _DNS_SD_H -#define _DNS_SD_H 3331000 +#define _DNS_SD_H 3792700 #ifdef __cplusplus - extern "C" { +extern "C" { #endif /* Set to 1 if libdispatch is supported @@ -111,24 +111,24 @@ #elif defined(EFI32) || defined(EFI64) || defined(EFIX64) #include "Tiano.h" #if !defined(_STDINT_H_) -typedef UINT8 uint8_t; -typedef INT8 int8_t; -typedef UINT16 uint16_t; -typedef INT16 int16_t; -typedef UINT32 uint32_t; -typedef INT32 int32_t; +typedef UINT8 uint8_t; +typedef INT8 int8_t; +typedef UINT16 uint16_t; +typedef INT16 int16_t; +typedef UINT32 uint32_t; +typedef INT32 int32_t; #endif /* Windows has its own differences */ #elif defined(_WIN32) #include #define _UNUSED #ifndef _MSL_STDINT_H -typedef UINT8 uint8_t; -typedef INT8 int8_t; -typedef UINT16 uint16_t; -typedef INT16 int16_t; -typedef UINT32 uint32_t; -typedef INT32 int32_t; +typedef UINT8 uint8_t; +typedef INT8 int8_t; +typedef UINT16 uint16_t; +typedef INT16 int16_t; +typedef UINT32 uint32_t; +typedef INT32 int32_t; #endif /* All other Posix platforms use stdint.h */ @@ -169,7 +169,7 @@ struct sockaddr; * if (flags & kDNSServiceFlagsAdd) ... */ enum - { +{ kDNSServiceFlagsMoreComing = 0x1, /* MoreComing indicates to a callback that at least one more result is * queued and will be delivered following immediately after this one. @@ -177,7 +177,7 @@ enum * update their UI, because this can result in a great deal of ugly flickering * on the screen, and can waste a great deal of CPU time repeatedly updating * the screen with content that is then immediately erased, over and over. - * Applications should wait until until MoreComing is not set, and then + * Applications should wait until MoreComing is not set, and then * update their UI when no more changes are imminent. * When MoreComing is not set, that doesn't mean there will be no more * answers EVER, just that there are no more answers immediately @@ -294,7 +294,7 @@ enum * kDNSServiceFlagsMoreComing flag applies collectively to *all* active * operations sharing the same parent DNSServiceRef. If the MoreComing flag is * set it means that there are more results queued on this parent DNSServiceRef, - * but not necessarily more results for this particular callback function. + * but not necessarily more results for this particular callback function. * The implication of this for client programmers is that when a callback * is invoked with the MoreComing flag set, the code should update its * internal data structures with the new result, and set a variable indicating @@ -342,51 +342,64 @@ enum */ kDNSServiceFlagsSuppressUnusable = 0x8000, - /* - * This flag is meaningful only in DNSServiceQueryRecord which suppresses unusable queries on the - * wire. If "hostname" is a wide-area unicast DNS hostname (i.e. not a ".local." name) - * but this host has no routable IPv6 address, then the call will not try to look up IPv6 addresses - * for "hostname", since any addresses it found would be unlikely to be of any use anyway. Similarly, - * if this host has no routable IPv4 address, the call will not try to look up IPv4 addresses for - * "hostname". - */ + /* + * This flag is meaningful only in DNSServiceQueryRecord which suppresses unusable queries on the + * wire. If "hostname" is a wide-area unicast DNS hostname (i.e. not a ".local." name) + * but this host has no routable IPv6 address, then the call will not try to look up IPv6 addresses + * for "hostname", since any addresses it found would be unlikely to be of any use anyway. Similarly, + * if this host has no routable IPv4 address, the call will not try to look up IPv4 addresses for + * "hostname". + */ kDNSServiceFlagsTimeout = 0x10000, - /* - * When kDNServiceFlagsTimeout is passed to DNSServiceQueryRecord or DNSServiceGetAddrInfo, the query is - * stopped after a certain number of seconds have elapsed. The time at which the query will be stopped - * is determined by the system and cannot be configured by the user. The query will be stopped irrespective - * of whether a response was given earlier or not. When the query is stopped, the callback will be called - * with an error code of kDNSServiceErr_Timeout and a NULL sockaddr will be returned for DNSServiceGetAddrInfo - * and zero length rdata will be returned for DNSServiceQueryRecord. - */ + /* + * When kDNServiceFlagsTimeout is passed to DNSServiceQueryRecord or DNSServiceGetAddrInfo, the query is + * stopped after a certain number of seconds have elapsed. The time at which the query will be stopped + * is determined by the system and cannot be configured by the user. The query will be stopped irrespective + * of whether a response was given earlier or not. When the query is stopped, the callback will be called + * with an error code of kDNSServiceErr_Timeout and a NULL sockaddr will be returned for DNSServiceGetAddrInfo + * and zero length rdata will be returned for DNSServiceQueryRecord. + */ kDNSServiceFlagsIncludeP2P = 0x20000, - /* - * Include P2P interfaces when kDNSServiceInterfaceIndexAny is specified. - * By default, specifying kDNSServiceInterfaceIndexAny does not include P2P interfaces. - */ - kDNSServiceFlagsWakeOnResolve = 0x40000 - /* - * This flag is meaningful only in DNSServiceResolve. When set, it tries to send a magic packet - * to wake up the client. - */ - }; + /* + * Include P2P interfaces when kDNSServiceInterfaceIndexAny is specified. + * By default, specifying kDNSServiceInterfaceIndexAny does not include P2P interfaces. + */ + + kDNSServiceFlagsWakeOnResolve = 0x40000, + /* + * This flag is meaningful only in DNSServiceResolve. When set, it tries to send a magic packet + * to wake up the client. + */ + + kDNSServiceFlagsBackgroundTrafficClass = 0x80000, + /* + * This flag is meaningful in DNSServiceBrowse, DNSServiceGetAddrInfo, DNSServiceQueryRecord, + * and DNSServiceResolve. When set, it uses the background traffic + * class for packets that service the request. + */ + + kDNSServiceFlagsIncludeAWDL = 0x100000 + /* + * Include AWDL interface when kDNSServiceInterfaceIndexAny is specified. + */ +}; /* Possible protocols for DNSServiceNATPortMappingCreate(). */ enum - { +{ kDNSServiceProtocol_IPv4 = 0x01, kDNSServiceProtocol_IPv6 = 0x02, /* 0x04 and 0x08 reserved for future internetwork protocols */ - + kDNSServiceProtocol_UDP = 0x10, kDNSServiceProtocol_TCP = 0x20 - /* 0x40 and 0x80 reserved for future transport protocols, e.g. SCTP [RFC 2960] - * or DCCP [RFC 4340]. If future NAT gateways are created that support port - * mappings for these protocols, new constants will be defined here. - */ - }; + /* 0x40 and 0x80 reserved for future transport protocols, e.g. SCTP [RFC 2960] + * or DCCP [RFC 4340]. If future NAT gateways are created that support port + * mappings for these protocols, new constants will be defined here. + */ +}; /* * The values for DNS Classes and Types are listed in RFC 1035, and are available @@ -400,12 +413,12 @@ enum */ enum - { +{ kDNSServiceClass_IN = 1 /* Internet */ - }; +}; enum - { +{ kDNSServiceType_A = 1, /* Host address. */ kDNSServiceType_NS = 2, /* Authoritative server. */ kDNSServiceType_MD = 3, /* Mail destination. */ @@ -473,11 +486,11 @@ enum kDNSServiceType_MAILB = 253, /* Transfer mailbox records. */ kDNSServiceType_MAILA = 254, /* Transfer mail agent records. */ kDNSServiceType_ANY = 255 /* Wildcard match. */ - }; +}; /* possible error code values */ enum - { +{ kDNSServiceErr_NoError = 0, kDNSServiceErr_Unknown = -65537, /* 0xFFFE FFFF */ kDNSServiceErr_NoSuchName = -65538, @@ -511,9 +524,9 @@ enum kDNSServiceErr_PollingMode = -65567, kDNSServiceErr_Timeout = -65568 - /* mDNS Error codes are in the range - * FFFE FF00 (-65792) to FFFE FFFF (-65537) */ - }; + /* mDNS Error codes are in the range + * FFFE FF00 (-65792) to FFFE FFFF (-65537) */ +}; /* Maximum length, in bytes, of a service name represented as a */ /* literal C-String, including the terminating NULL at the end. */ @@ -621,8 +634,8 @@ enum * * If applications pass kDNSServiceInterfaceIndexAny to DNSServiceBrowse * or DNSServiceQueryRecord, they must set the kDNSServiceFlagsIncludeP2P flag - * to include P2P. In this case, if a service instance or the record being queried - * is found over P2P, the resulting ADD event will indicate kDNSServiceInterfaceIndexP2P + * to include P2P. In this case, if a service instance or the record being queried + * is found over P2P, the resulting ADD event will indicate kDNSServiceInterfaceIndexP2P * as the interface index. */ @@ -633,14 +646,14 @@ enum typedef uint32_t DNSServiceFlags; typedef uint32_t DNSServiceProtocol; -typedef int32_t DNSServiceErrorType; +typedef int32_t DNSServiceErrorType; /********************************************************************************************* - * - * Version checking - * - *********************************************************************************************/ +* +* Version checking +* +*********************************************************************************************/ /* DNSServiceGetProperty() Parameters: * @@ -661,11 +674,11 @@ typedef int32_t DNSServiceErrorType; */ DNSServiceErrorType DNSSD_API DNSServiceGetProperty - ( +( const char *property, /* Requested property (i.e. kDNSServiceProperty_DaemonVersion) */ void *result, /* Pointer to place to store result */ uint32_t *size /* size of result location */ - ); +); /* * When requesting kDNSServiceProperty_DaemonVersion, the result pointer must point @@ -693,10 +706,10 @@ DNSServiceErrorType DNSSD_API DNSServiceGetProperty /********************************************************************************************* - * - * Unix Domain Socket access, DNSServiceRef deallocation, and data processing functions - * - *********************************************************************************************/ +* +* Unix Domain Socket access, DNSServiceRef deallocation, and data processing functions +* +*********************************************************************************************/ /* DNSServiceRefSockFD() * @@ -776,10 +789,10 @@ void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef); /********************************************************************************************* - * - * Domain Enumeration - * - *********************************************************************************************/ +* +* Domain Enumeration +* +*********************************************************************************************/ /* DNSServiceEnumerateDomains() * @@ -817,14 +830,14 @@ void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef); */ typedef void (DNSSD_API *DNSServiceDomainEnumReply) - ( - DNSServiceRef sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceErrorType errorCode, +( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *replyDomain, void *context - ); +); /* DNSServiceEnumerateDomains() Parameters: @@ -857,20 +870,20 @@ typedef void (DNSSD_API *DNSServiceDomainEnumReply) */ DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains - ( +( DNSServiceRef *sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceDomainEnumReply callBack, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceDomainEnumReply callBack, void *context /* may be NULL */ - ); +); /********************************************************************************************* - * - * Service Registration - * - *********************************************************************************************/ +* +* Service Registration +* +*********************************************************************************************/ /* Register a service that is discovered via Browse() and Resolve() calls. * @@ -908,15 +921,15 @@ DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains */ typedef void (DNSSD_API *DNSServiceRegisterReply) - ( - DNSServiceRef sdRef, - DNSServiceFlags flags, - DNSServiceErrorType errorCode, +( + DNSServiceRef sdRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context - ); +); /* DNSServiceRegister() Parameters: @@ -974,7 +987,7 @@ typedef void (DNSSD_API *DNSServiceRegisterReply) * bit byte values, including zero bytes. However, due to the nature of * using a C-string-based API, conventional DNS escaping must be used for * dots ('.'), commas (','), backslashes ('\') and zero bytes, as shown below: - * + * * % dns-sd -R Test '_test._tcp,s\.one,s\,two,s\\three,s\000four' local 123 * * domain: If non-NULL, specifies the domain on which to advertise the service. @@ -1022,20 +1035,20 @@ typedef void (DNSSD_API *DNSServiceRegisterReply) */ DNSServiceErrorType DNSSD_API DNSServiceRegister - ( +( DNSServiceRef *sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, + DNSServiceFlags flags, + uint32_t interfaceIndex, const char *name, /* may be NULL */ const char *regtype, const char *domain, /* may be NULL */ const char *host, /* may be NULL */ - uint16_t port, /* In network byte order */ - uint16_t txtLen, + uint16_t port, /* In network byte order */ + uint16_t txtLen, const void *txtRecord, /* may be NULL */ - DNSServiceRegisterReply callBack, /* may be NULL */ + DNSServiceRegisterReply callBack, /* may be NULL */ void *context /* may be NULL */ - ); +); /* DNSServiceAddRecord() @@ -1077,15 +1090,15 @@ DNSServiceErrorType DNSSD_API DNSServiceRegister */ DNSServiceErrorType DNSSD_API DNSServiceAddRecord - ( - DNSServiceRef sdRef, +( + DNSServiceRef sdRef, DNSRecordRef *RecordRef, - DNSServiceFlags flags, - uint16_t rrtype, - uint16_t rdlen, + DNSServiceFlags flags, + uint16_t rrtype, + uint16_t rdlen, const void *rdata, - uint32_t ttl - ); + uint32_t ttl +); /* DNSServiceUpdateRecord @@ -1118,14 +1131,14 @@ DNSServiceErrorType DNSSD_API DNSServiceAddRecord */ DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord - ( - DNSServiceRef sdRef, - DNSRecordRef RecordRef, /* may be NULL */ - DNSServiceFlags flags, - uint16_t rdlen, +( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, /* may be NULL */ + DNSServiceFlags flags, + uint16_t rdlen, const void *rdata, - uint32_t ttl - ); + uint32_t ttl +); /* DNSServiceRemoveRecord @@ -1150,18 +1163,18 @@ DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord */ DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord - ( - DNSServiceRef sdRef, - DNSRecordRef RecordRef, - DNSServiceFlags flags - ); +( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags +); /********************************************************************************************* - * - * Service Discovery - * - *********************************************************************************************/ +* +* Service Discovery +* +*********************************************************************************************/ /* Browse for instances of a service. * @@ -1202,16 +1215,16 @@ DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord */ typedef void (DNSSD_API *DNSServiceBrowseReply) - ( - DNSServiceRef sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceErrorType errorCode, +( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context - ); +); /* DNSServiceBrowse() Parameters: @@ -1252,15 +1265,15 @@ typedef void (DNSSD_API *DNSServiceBrowseReply) */ DNSServiceErrorType DNSSD_API DNSServiceBrowse - ( +( DNSServiceRef *sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, + DNSServiceFlags flags, + uint32_t interfaceIndex, const char *regtype, const char *domain, /* may be NULL */ - DNSServiceBrowseReply callBack, + DNSServiceBrowseReply callBack, void *context /* may be NULL */ - ); +); /* DNSServiceResolve() @@ -1325,18 +1338,18 @@ DNSServiceErrorType DNSSD_API DNSServiceBrowse */ typedef void (DNSSD_API *DNSServiceResolveReply) - ( - DNSServiceRef sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceErrorType errorCode, +( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, - uint16_t port, /* In network byte order */ - uint16_t txtLen, + uint16_t port, /* In network byte order */ + uint16_t txtLen, const unsigned char *txtRecord, void *context - ); +); /* DNSServiceResolve() Parameters @@ -1380,23 +1393,23 @@ typedef void (DNSSD_API *DNSServiceResolveReply) */ DNSServiceErrorType DNSSD_API DNSServiceResolve - ( +( DNSServiceRef *sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, + DNSServiceFlags flags, + uint32_t interfaceIndex, const char *name, const char *regtype, const char *domain, - DNSServiceResolveReply callBack, + DNSServiceResolveReply callBack, void *context /* may be NULL */ - ); +); /********************************************************************************************* - * - * Querying Individual Specific Records - * - *********************************************************************************************/ +* +* Querying Individual Specific Records +* +*********************************************************************************************/ /* DNSServiceQueryRecord * @@ -1443,19 +1456,19 @@ DNSServiceErrorType DNSSD_API DNSServiceResolve */ typedef void (DNSSD_API *DNSServiceQueryRecordReply) - ( - DNSServiceRef sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceErrorType errorCode, +( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *fullname, - uint16_t rrtype, - uint16_t rrclass, - uint16_t rdlen, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, const void *rdata, - uint32_t ttl, + uint32_t ttl, void *context - ); +); /* DNSServiceQueryRecord() Parameters: @@ -1467,11 +1480,8 @@ typedef void (DNSSD_API *DNSServiceQueryRecordReply) * * flags: kDNSServiceFlagsForceMulticast or kDNSServiceFlagsLongLivedQuery. * Pass kDNSServiceFlagsLongLivedQuery 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. + * query to a unicast DNS server that implements the protocol. 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() @@ -1498,23 +1508,23 @@ typedef void (DNSSD_API *DNSServiceQueryRecordReply) */ DNSServiceErrorType DNSSD_API DNSServiceQueryRecord - ( +( DNSServiceRef *sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, + DNSServiceFlags flags, + uint32_t interfaceIndex, const char *fullname, - uint16_t rrtype, - uint16_t rrclass, - DNSServiceQueryRecordReply callBack, + uint16_t rrtype, + uint16_t rrclass, + DNSServiceQueryRecordReply callBack, void *context /* may be NULL */ - ); +); /********************************************************************************************* - * - * Unified lookup of both IPv4 and IPv6 addresses for a fully qualified hostname - * - *********************************************************************************************/ +* +* Unified lookup of both IPv4 and IPv6 addresses for a fully qualified hostname +* +*********************************************************************************************/ /* DNSServiceGetAddrInfo * @@ -1552,16 +1562,16 @@ DNSServiceErrorType DNSSD_API DNSServiceQueryRecord */ typedef void (DNSSD_API *DNSServiceGetAddrInfoReply) - ( - DNSServiceRef sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceErrorType errorCode, +( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *hostname, const struct sockaddr *address, - uint32_t ttl, + uint32_t ttl, void *context - ); +); /* DNSServiceGetAddrInfo() Parameters: @@ -1573,11 +1583,8 @@ typedef void (DNSSD_API *DNSServiceGetAddrInfoReply) * * flags: kDNSServiceFlagsForceMulticast or kDNSServiceFlagsLongLivedQuery. * Pass kDNSServiceFlagsLongLivedQuery 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. + * query to a unicast DNS server that implements the protocol. This flag + * has no effect on link-local multicast queries. * * interfaceIndex: The interface on which to issue the query. Passing 0 causes the query to be * sent on all active interfaces via Multicast or the primary interface via Unicast. @@ -1606,24 +1613,24 @@ typedef void (DNSSD_API *DNSServiceGetAddrInfoReply) */ DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo - ( +( DNSServiceRef *sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceProtocol protocol, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceProtocol protocol, const char *hostname, - DNSServiceGetAddrInfoReply callBack, + DNSServiceGetAddrInfoReply callBack, void *context /* may be NULL */ - ); +); /********************************************************************************************* - * - * Special Purpose Calls: - * DNSServiceCreateConnection(), DNSServiceRegisterRecord(), DNSServiceReconfirmRecord() - * (most applications will not use these) - * - *********************************************************************************************/ +* +* Special Purpose Calls: +* DNSServiceCreateConnection(), DNSServiceRegisterRecord(), DNSServiceReconfirmRecord() +* (most applications will not use these) +* +*********************************************************************************************/ /* DNSServiceCreateConnection() * @@ -1670,14 +1677,14 @@ DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef); * */ - typedef void (DNSSD_API *DNSServiceRegisterRecordReply) - ( - DNSServiceRef sdRef, - DNSRecordRef RecordRef, - DNSServiceFlags flags, - DNSServiceErrorType errorCode, +typedef void (DNSSD_API *DNSServiceRegisterRecordReply) +( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, void *context - ); +); /* DNSServiceRegisterRecord() Parameters: @@ -1725,20 +1732,20 @@ DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef); */ DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord - ( - DNSServiceRef sdRef, +( + DNSServiceRef sdRef, DNSRecordRef *RecordRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, + DNSServiceFlags flags, + uint32_t interfaceIndex, const char *fullname, - uint16_t rrtype, - uint16_t rrclass, - uint16_t rdlen, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, const void *rdata, - uint32_t ttl, - DNSServiceRegisterRecordReply callBack, + uint32_t ttl, + DNSServiceRegisterRecordReply callBack, void *context /* may be NULL */ - ); +); /* DNSServiceReconfirmRecord @@ -1775,22 +1782,22 @@ DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord */ DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord - ( - DNSServiceFlags flags, - uint32_t interfaceIndex, +( + DNSServiceFlags flags, + uint32_t interfaceIndex, const char *fullname, - uint16_t rrtype, - uint16_t rrclass, - uint16_t rdlen, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, const void *rdata - ); +); /********************************************************************************************* - * - * NAT Port Mapping - * - *********************************************************************************************/ +* +* NAT Port Mapping +* +*********************************************************************************************/ /* DNSServiceNATPortMappingCreate * @@ -1890,18 +1897,18 @@ DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord */ typedef void (DNSSD_API *DNSServiceNATPortMappingReply) - ( - DNSServiceRef sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceErrorType errorCode, - uint32_t externalAddress, /* four byte IPv4 address in network byte order */ - DNSServiceProtocol protocol, - uint16_t internalPort, /* In network byte order */ - uint16_t externalPort, /* In network byte order and may be different than the requested port */ - uint32_t ttl, /* may be different than the requested ttl */ +( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + uint32_t externalAddress, /* four byte IPv4 address in network byte order */ + DNSServiceProtocol protocol, + uint16_t internalPort, /* In network byte order */ + uint16_t externalPort, /* In network byte order and may be different than the requested port */ + uint32_t ttl, /* may be different than the requested ttl */ void *context - ); +); /* DNSServiceNATPortMappingCreate() Parameters: @@ -1954,24 +1961,24 @@ typedef void (DNSSD_API *DNSServiceNATPortMappingReply) */ DNSServiceErrorType DNSSD_API DNSServiceNATPortMappingCreate - ( +( DNSServiceRef *sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceProtocol protocol, /* TCP and/or UDP */ - uint16_t internalPort, /* network byte order */ - uint16_t externalPort, /* network byte order */ - uint32_t ttl, /* time to live in seconds */ - DNSServiceNATPortMappingReply callBack, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceProtocol protocol, /* TCP and/or UDP */ + uint16_t internalPort, /* network byte order */ + uint16_t externalPort, /* network byte order */ + uint32_t ttl, /* time to live in seconds */ + DNSServiceNATPortMappingReply callBack, void *context /* may be NULL */ - ); +); /********************************************************************************************* - * - * General Utility Functions - * - *********************************************************************************************/ +* +* General Utility Functions +* +*********************************************************************************************/ /* DNSServiceConstructFullName() * @@ -2000,19 +2007,19 @@ DNSServiceErrorType DNSSD_API DNSServiceNATPortMappingCreate */ DNSServiceErrorType DNSSD_API DNSServiceConstructFullName - ( +( char * const fullName, const char * const service, /* may be NULL */ const char * const regtype, const char * const domain - ); +); /********************************************************************************************* - * - * TXT Record Construction Functions - * - *********************************************************************************************/ +* +* TXT Record Construction Functions +* +*********************************************************************************************/ /* * A typical calling sequence for TXT record construction is something like: @@ -2080,11 +2087,11 @@ typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignmen */ void DNSSD_API TXTRecordCreate - ( +( TXTRecordRef *txtRecord, - uint16_t bufferLen, + uint16_t bufferLen, void *buffer - ); +); /* TXTRecordDeallocate() @@ -2098,9 +2105,9 @@ void DNSSD_API TXTRecordCreate */ void DNSSD_API TXTRecordDeallocate - ( +( TXTRecordRef *txtRecord - ); +); /* TXTRecordSetValue() @@ -2141,12 +2148,12 @@ void DNSSD_API TXTRecordDeallocate */ DNSServiceErrorType DNSSD_API TXTRecordSetValue - ( +( TXTRecordRef *txtRecord, const char *key, - uint8_t valueSize, /* may be zero */ + uint8_t valueSize, /* may be zero */ const void *value /* may be NULL */ - ); +); /* TXTRecordRemoveValue() @@ -2164,10 +2171,10 @@ DNSServiceErrorType DNSSD_API TXTRecordSetValue */ DNSServiceErrorType DNSSD_API TXTRecordRemoveValue - ( +( TXTRecordRef *txtRecord, const char *key - ); +); /* TXTRecordGetLength() @@ -2183,9 +2190,9 @@ DNSServiceErrorType DNSSD_API TXTRecordRemoveValue */ uint16_t DNSSD_API TXTRecordGetLength - ( +( const TXTRecordRef *txtRecord - ); +); /* TXTRecordGetBytesPtr() @@ -2200,16 +2207,16 @@ uint16_t DNSSD_API TXTRecordGetLength */ const void * DNSSD_API TXTRecordGetBytesPtr - ( +( const TXTRecordRef *txtRecord - ); +); /********************************************************************************************* - * - * TXT Record Parsing Functions - * - *********************************************************************************************/ +* +* TXT Record Parsing Functions +* +*********************************************************************************************/ /* * A typical calling sequence for TXT record parsing is something like: @@ -2254,11 +2261,11 @@ const void * DNSSD_API TXTRecordGetBytesPtr */ int DNSSD_API TXTRecordContainsKey - ( - uint16_t txtLen, +( + uint16_t txtLen, const void *txtRecord, const char *key - ); +); /* TXTRecordGetValuePtr() @@ -2283,12 +2290,12 @@ int DNSSD_API TXTRecordContainsKey */ const void * DNSSD_API TXTRecordGetValuePtr - ( - uint16_t txtLen, +( + uint16_t txtLen, const void *txtRecord, const char *key, uint8_t *valueLen - ); +); /* TXTRecordGetCount() @@ -2305,10 +2312,10 @@ const void * DNSSD_API TXTRecordGetValuePtr */ uint16_t DNSSD_API TXTRecordGetCount - ( - uint16_t txtLen, +( + uint16_t txtLen, const void *txtRecord - ); +); /* TXTRecordGetItemAtIndex() @@ -2350,64 +2357,82 @@ uint16_t DNSSD_API TXTRecordGetCount */ DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex - ( - uint16_t txtLen, +( + uint16_t txtLen, const void *txtRecord, - uint16_t itemIndex, - uint16_t keyBufLen, + uint16_t itemIndex, + uint16_t keyBufLen, char *key, uint8_t *valueLen, const void **value - ); +); #if _DNS_SD_LIBDISPATCH /* -* DNSServiceSetDispatchQueue -* -* Allows you to schedule a DNSServiceRef on a serial dispatch queue for receiving asynchronous -* callbacks. It's the clients responsibility to ensure that the provided dispatch queue is running. -* -* A typical application that uses CFRunLoopRun or dispatch_main on its main thread will -* usually schedule DNSServiceRefs on its main queue (which is always a serial queue) -* using "DNSServiceSetDispatchQueue(sdref, dispatch_get_main_queue());" -* -* If there is any error during the processing of events, the application callback will -* be called with an error code. For shared connections, each subordinate DNSServiceRef -* will get its own error callback. Currently these error callbacks only happen -* if the mDNSResponder daemon is manually terminated or crashes, and the error -* code in this case is kDNSServiceErr_ServiceNotRunning. The application must call -* DNSServiceRefDeallocate to free the DNSServiceRef when it gets such an error code. -* These error callbacks are rare and should not normally happen on customer machines, -* but application code should be written defensively to handle such error callbacks -* gracefully if they occur. -* -* After using DNSServiceSetDispatchQueue on a DNSServiceRef, calling DNSServiceProcessResult -* on the same DNSServiceRef will result in undefined behavior and should be avoided. -* -* Once the application successfully schedules a DNSServiceRef on a serial dispatch queue using -* DNSServiceSetDispatchQueue, it cannot remove the DNSServiceRef from the dispatch queue, or use -* DNSServiceSetDispatchQueue a second time to schedule the DNSServiceRef onto a different serial dispatch -* queue. Once scheduled onto a dispatch queue a DNSServiceRef will deliver events to that queue until -* the application no longer requires that operation and terminates it using DNSServiceRefDeallocate. -* -* service: DNSServiceRef that was allocated and returned to the application, when the -* application calls one of the DNSService API. -* -* queue: dispatch queue where the application callback will be scheduled -* -* return value: Returns kDNSServiceErr_NoError on success. -* Returns kDNSServiceErr_NoMemory if it cannot create a dispatch source -* Returns kDNSServiceErr_BadParam if the service param is invalid or the -* queue param is invalid -*/ + * DNSServiceSetDispatchQueue + * + * Allows you to schedule a DNSServiceRef on a serial dispatch queue for receiving asynchronous + * callbacks. It's the clients responsibility to ensure that the provided dispatch queue is running. + * + * A typical application that uses CFRunLoopRun or dispatch_main on its main thread will + * usually schedule DNSServiceRefs on its main queue (which is always a serial queue) + * using "DNSServiceSetDispatchQueue(sdref, dispatch_get_main_queue());" + * + * If there is any error during the processing of events, the application callback will + * be called with an error code. For shared connections, each subordinate DNSServiceRef + * will get its own error callback. Currently these error callbacks only happen + * if the mDNSResponder daemon is manually terminated or crashes, and the error + * code in this case is kDNSServiceErr_ServiceNotRunning. The application must call + * DNSServiceRefDeallocate to free the DNSServiceRef when it gets such an error code. + * These error callbacks are rare and should not normally happen on customer machines, + * but application code should be written defensively to handle such error callbacks + * gracefully if they occur. + * + * After using DNSServiceSetDispatchQueue on a DNSServiceRef, calling DNSServiceProcessResult + * on the same DNSServiceRef will result in undefined behavior and should be avoided. + * + * Once the application successfully schedules a DNSServiceRef on a serial dispatch queue using + * DNSServiceSetDispatchQueue, it cannot remove the DNSServiceRef from the dispatch queue, or use + * DNSServiceSetDispatchQueue a second time to schedule the DNSServiceRef onto a different serial dispatch + * queue. Once scheduled onto a dispatch queue a DNSServiceRef will deliver events to that queue until + * the application no longer requires that operation and terminates it using DNSServiceRefDeallocate. + * + * service: DNSServiceRef that was allocated and returned to the application, when the + * application calls one of the DNSService API. + * + * queue: dispatch queue where the application callback will be scheduled + * + * return value: Returns kDNSServiceErr_NoError on success. + * Returns kDNSServiceErr_NoMemory if it cannot create a dispatch source + * Returns kDNSServiceErr_BadParam if the service param is invalid or the + * queue param is invalid + */ DNSServiceErrorType DNSSD_API DNSServiceSetDispatchQueue - ( - DNSServiceRef service, - dispatch_queue_t queue - ); +( + DNSServiceRef service, + dispatch_queue_t queue +); #endif //_DNS_SD_LIBDISPATCH +#if !defined(_WIN32) +typedef void (DNSSD_API *DNSServiceSleepKeepaliveReply) +( + DNSServiceRef sdRef, + DNSServiceErrorType errorCode, + void *context +); +DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + int fd, + unsigned int timeout, + DNSServiceSleepKeepaliveReply callBack, + void *context +); +#endif + #ifdef __APPLE_API_PRIVATE #define kDNSServiceCompPrivateDNS "PrivateDNS" @@ -2423,12 +2448,12 @@ DNSServiceErrorType DNSSD_API DNSServiceSetDispatchQueue */ struct CompileTimeAssertionChecks_DNS_SD - { +{ char assert0[(sizeof(union _TXTRecordRef_t) == 16) ? 1 : -1]; - }; +}; #ifdef __cplusplus - } +} #endif #endif /* _DNS_SD_H */ diff --git a/mDNSShared/dnsextd.c b/mDNSShared/dnsextd.c index 63c9d19..a5ee500 100644 --- a/mDNSShared/dnsextd.c +++ b/mDNSShared/dnsextd.c @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -46,7 +46,7 @@ extern int daemon(int, int); // Solaris doesn't have daemon(), so we define it here #ifdef NOT_HAVE_DAEMON -#include "../mDNSPosix/mDNSUNP.h" // For daemon() +#include "../mDNSPosix/mDNSUNP.h" // For daemon() #endif // NOT_HAVE_DAEMON #include "dnsextd.h" @@ -65,16 +65,16 @@ extern int daemon(int, int); // mDNSexport const char ProgramName[] = "dnsextd"; -#define LOOPBACK "127.0.0.1" +#define LOOPBACK "127.0.0.1" #if !defined(LISTENQ) -# define LISTENQ 128 // tcp connection backlog +# define LISTENQ 128 // tcp connection backlog #endif -#define RECV_BUFLEN 9000 -#define LEASETABLE_INIT_NBUCKETS 256 // initial hashtable size (doubles as table fills) -#define EXPIRATION_INTERVAL 300 // check for expired records every 5 minutes -#define SRV_TTL 7200 // TTL For _dns-update SRV records -#define CONFIG_FILE "/etc/dnsextd.conf" -#define TCP_SOCKET_FLAGS kTCPSocketFlags_UseTLS +#define RECV_BUFLEN 9000 +#define LEASETABLE_INIT_NBUCKETS 256 // initial hashtable size (doubles as table fills) +#define EXPIRATION_INTERVAL 300 // check for expired records every 5 minutes +#define SRV_TTL 7200 // TTL For _dns-update SRV records +#define CONFIG_FILE "/etc/dnsextd.conf" +#define TCP_SOCKET_FLAGS kTCPSocketFlags_UseTLS // LLQ Lease bounds (seconds) #define LLQ_MIN_LEASE (15 * 60) @@ -100,28 +100,28 @@ mDNSexport const char ProgramName[] = "dnsextd"; // args passed to UDP request handler thread as void* typedef struct - { +{ PktMsg pkt; struct sockaddr_in cliaddr; DaemonInfo *d; - int sd; - } UDPContext; + int sd; +} UDPContext; // args passed to TCP request handler thread as void* typedef struct - { - PktMsg pkt; +{ + PktMsg pkt; struct sockaddr_in cliaddr; TCPSocket *sock; // socket connected to client DaemonInfo *d; - } TCPContext; +} TCPContext; // args passed to UpdateAnswerList thread as void* typedef struct - { +{ DaemonInfo *d; AnswerListElem *a; - } UpdateAnswerListArgs; +} UpdateAnswerListArgs; // // Global Variables @@ -147,55 +147,55 @@ static char * cfgfile = NULL; // 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_DAEMON); - syslog(LOG_ERR, "%s", buffer); - closelog(); - } - } +{ + if (foreground) + { + fprintf(stderr,"%s\n", buffer); + fflush(stderr); + } + else + { + openlog("dnsextd", LOG_CONS, LOG_DAEMON); + syslog(LOG_ERR, "%s", buffer); + closelog(); + } +} // Verbose Logging (conditional on -v option) mDNSlocal void VLog(const char *format, ...) - { - char buffer[512]; - va_list ptr; +{ + 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); - } + 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, ...) - { - char buffer[512]; - va_list ptr; +{ + 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); - } + 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], errbuf[256]; - strerror_r(errno, errbuf, sizeof(errbuf)); - snprintf(buf, sizeof(buf), "%s: %s - %s", fn, operation, errbuf); - PrintLog(buf); - } +{ + char buf[512], errbuf[256]; + strerror_r(errno, errbuf, sizeof(errbuf)); + snprintf(buf, sizeof(buf), "%s: %s - %s", fn, operation, errbuf); + PrintLog(buf); +} // // Networking Utility Routines @@ -203,206 +203,206 @@ mDNSlocal void LogErr(const char *fn, const char *operation) // 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]); - } +{ + // 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); - } +{ + 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); +} // Add socket to event loop mDNSlocal mStatus AddSourceToEventLoop( DaemonInfo * self, TCPSocket *sock, EventCallback callback, void *context ) - { - EventSource * newSource; - mStatus err = mStatus_NoError; - - if ( self->eventSources.LinkOffset == 0 ) - { - InitLinkedList( &self->eventSources, offsetof( EventSource, next)); - } - - newSource = ( EventSource*) malloc( sizeof *newSource ); - if ( newSource == NULL ) - { - err = mStatus_NoMemoryErr; - goto exit; - } - - newSource->callback = callback; - newSource->context = context; - newSource->sock = sock; - newSource->fd = mDNSPlatformTCPGetFD( sock ); - - AddToTail( &self->eventSources, newSource ); +{ + EventSource * newSource; + mStatus err = mStatus_NoError; + + if ( self->eventSources.LinkOffset == 0 ) + { + InitLinkedList( &self->eventSources, offsetof( EventSource, next)); + } + + newSource = ( EventSource*) malloc( sizeof *newSource ); + if ( newSource == NULL ) + { + err = mStatus_NoMemoryErr; + goto exit; + } + + newSource->callback = callback; + newSource->context = context; + newSource->sock = sock; + newSource->fd = mDNSPlatformTCPGetFD( sock ); + + AddToTail( &self->eventSources, newSource ); exit: - return err; - } + return err; +} // Remove socket from event loop mDNSlocal mStatus RemoveSourceFromEventLoop( DaemonInfo * self, TCPSocket *sock ) - { - EventSource * source; - mStatus err; - - for ( source = ( EventSource* ) self->eventSources.Head; source; source = source->next ) - { - if ( source->sock == sock ) - { - RemoveFromList( &self->eventSources, source ); - - free( source ); - err = mStatus_NoError; - goto exit; - } - } - - err = mStatus_NoSuchNameErr; +{ + EventSource * source; + mStatus err; + + for ( source = ( EventSource* ) self->eventSources.Head; source; source = source->next ) + { + if ( source->sock == sock ) + { + RemoveFromList( &self->eventSources, source ); + + free( source ); + err = mStatus_NoError; + goto exit; + } + } + + err = mStatus_NoSuchNameErr; exit: - return err; - } + return err; +} // create a socket connected to nameserver // caller terminates connection via close() mDNSlocal TCPSocket *ConnectToServer(DaemonInfo *d) - { - int ntries = 0, retry = 0; - - while (1) - { - mDNSIPPort port = zeroIPPort; - int fd; - - TCPSocket *sock = mDNSPlatformTCPSocket( NULL, 0, &port ); - if ( !sock ) { LogErr("ConnectToServer", "socket"); return NULL; } - fd = mDNSPlatformTCPGetFD( sock ); - if (!connect( fd, (struct sockaddr *)&d->ns_addr, sizeof(d->ns_addr))) return sock; - mDNSPlatformTCPCloseConnection( sock ); - if (++ntries < 10) - { - LogErr("ConnectToServer", "connect"); - Log("ConnectToServer - retrying connection"); - if (!retry) retry = 500000 + random() % 500000; - usleep(retry); - retry *= 2; - } - else { Log("ConnectToServer - %d failed attempts. Aborting.", ntries); return NULL; } - } - } +{ + int ntries = 0, retry = 0; + + while (1) + { + mDNSIPPort port = zeroIPPort; + int fd; + + TCPSocket *sock = mDNSPlatformTCPSocket( NULL, 0, &port, mDNSfalse ); + if ( !sock ) { LogErr("ConnectToServer", "socket"); return NULL; } + fd = mDNSPlatformTCPGetFD( sock ); + if (!connect( fd, (struct sockaddr *)&d->ns_addr, sizeof(d->ns_addr))) return sock; + mDNSPlatformTCPCloseConnection( sock ); + if (++ntries < 10) + { + LogErr("ConnectToServer", "connect"); + Log("ConnectToServer - retrying connection"); + if (!retry) retry = 500000 + random() % 500000; + usleep(retry); + retry *= 2; + } + else { Log("ConnectToServer - %d failed attempts. Aborting.", ntries); return NULL; } + } +} // send an entire block of data over a connected socket mDNSlocal int MySend(TCPSocket *sock, const void *msg, int len) - { - int selectval, n, nsent = 0; - fd_set wset; - struct timeval timeout = { 3, 0 }; // until we remove all calls from main thread, keep timeout short +{ + int selectval, n, nsent = 0; + fd_set wset; + struct timeval timeout = { 3, 0 }; // until we remove all calls from main thread, keep timeout short - while (nsent < len) - { - int fd; + while (nsent < len) + { + int fd; - FD_ZERO(&wset); + FD_ZERO(&wset); - fd = mDNSPlatformTCPGetFD( sock ); + fd = mDNSPlatformTCPGetFD( sock ); - FD_SET( fd, &wset ); - selectval = select( fd+1, NULL, &wset, NULL, &timeout); - if (selectval < 0) { LogErr("MySend", "select"); return -1; } - if (!selectval || !FD_ISSET(fd, &wset)) { Log("MySend - timeout"); return -1; } + FD_SET( fd, &wset ); + selectval = select( fd+1, NULL, &wset, NULL, &timeout); + if (selectval < 0) { LogErr("MySend", "select"); return -1; } + if (!selectval || !FD_ISSET(fd, &wset)) { Log("MySend - timeout"); return -1; } - n = mDNSPlatformWriteTCP( sock, ( char* ) msg + nsent, len - nsent); + n = mDNSPlatformWriteTCP( sock, ( char* ) msg + nsent, len - nsent); - if (n < 0) { LogErr("MySend", "send"); return -1; } - nsent += n; - } - return 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 SendPacket(TCPSocket *sock, PktMsg *pkt) - { - // send the lenth, in network byte order - mDNSu16 len = htons((mDNSu16)pkt->len); - if (MySend(sock, &len, sizeof(len)) < 0) return -1; - - // send the message - VLog("SendPacket Q:%d A:%d A:%d A:%d ", - ntohs(pkt->msg.h.numQuestions), - ntohs(pkt->msg.h.numAnswers), - ntohs(pkt->msg.h.numAuthorities), - ntohs(pkt->msg.h.numAdditionals)); - return MySend(sock, &pkt->msg, pkt->len); - } +{ + // send the lenth, in network byte order + mDNSu16 len = htons((mDNSu16)pkt->len); + if (MySend(sock, &len, sizeof(len)) < 0) return -1; + + // send the message + VLog("SendPacket Q:%d A:%d A:%d A:%d ", + ntohs(pkt->msg.h.numQuestions), + ntohs(pkt->msg.h.numAnswers), + ntohs(pkt->msg.h.numAuthorities), + ntohs(pkt->msg.h.numAdditionals)); + return MySend(sock, &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(TCPSocket *sock, void *const buf, const int len, mDNSBool * closed) - { +{ // 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. - fd_set rset; - struct timeval timeout = { 3, 0 }; // until we remove all calls from main thread, keep timeout short + fd_set rset; + struct timeval timeout = { 3, 0 }; // until we remove all calls from main thread, keep timeout short int selectval, remaining = len; char *ptr = (char *)buf; - ssize_t num_read; - - while (remaining) - { - int fd; - - fd = mDNSPlatformTCPGetFD( sock ); - - FD_ZERO(&rset); - FD_SET(fd, &rset); - selectval = select(fd+1, &rset, NULL, NULL, &timeout); - if (selectval < 0) { LogErr("my_recv", "select"); return -1; } - if (!selectval || !FD_ISSET(fd, &rset)) - { - Log("my_recv - timeout"); - return -1; - } - - num_read = mDNSPlatformReadTCP( sock, ptr, remaining, closed ); - - if (((num_read == 0) && *closed) || (num_read < 0) || (num_read > remaining)) return -1; - if (num_read == 0) return 0; - ptr += num_read; - remaining -= num_read; - } - return(len); + ssize_t num_read; + + while (remaining) + { + int fd; + + fd = mDNSPlatformTCPGetFD( sock ); + + FD_ZERO(&rset); + FD_SET(fd, &rset); + selectval = select(fd+1, &rset, NULL, NULL, &timeout); + if (selectval < 0) { LogErr("my_recv", "select"); return -1; } + if (!selectval || !FD_ISSET(fd, &rset)) + { + Log("my_recv - timeout"); + return -1; + } + + num_read = mDNSPlatformReadTCP( sock, ptr, remaining, closed ); + + if (((num_read == 0) && *closed) || (num_read < 0) || (num_read > remaining)) return -1; + if (num_read == 0) return 0; + 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, @@ -411,342 +411,342 @@ static int my_recv(TCPSocket *sock, void *const buf, const int len, mDNSBool * c mDNSlocal PktMsg* RecvPacket - ( - TCPSocket * sock, - PktMsg * storage, - mDNSBool * closed - ) - { - int nread; - int allocsize; - mDNSu16 msglen = 0; - PktMsg * pkt = NULL; - unsigned int srclen; - int fd; - mStatus err = 0; - - fd = mDNSPlatformTCPGetFD( sock ); - - nread = my_recv( sock, &msglen, sizeof( msglen ), closed ); - - require_action_quiet( nread != -1, exit, err = mStatus_UnknownErr ); - require_action_quiet( nread > 0, exit, err = mStatus_NoError ); - - msglen = ntohs( msglen ); - require_action_quiet( nread == sizeof( msglen ), exit, err = mStatus_UnknownErr; Log( "Could not read length field of message") ); - - if ( storage ) - { - require_action_quiet( msglen <= sizeof( storage->msg ), exit, err = mStatus_UnknownErr; Log( "RecvPacket: provided buffer too small." ) ); - 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); - require_action_quiet( pkt, exit, err = mStatus_NoMemoryErr; LogErr( "RecvPacket", "malloc" ) ); - mDNSPlatformMemZero( pkt, sizeof( *pkt ) ); - } - - pkt->len = msglen; - srclen = sizeof(pkt->src); - - if ( getpeername( fd, ( struct sockaddr* ) &pkt->src, &srclen ) || ( srclen != sizeof( pkt->src ) ) ) - { - LogErr("RecvPacket", "getpeername"); - mDNSPlatformMemZero(&pkt->src, sizeof(pkt->src)); - } - - nread = my_recv(sock, &pkt->msg, msglen, closed ); - require_action_quiet( nread >= 0, exit, err = mStatus_UnknownErr ; LogErr( "RecvPacket", "recv" ) ); - require_action_quiet( nread == msglen, exit, err = mStatus_UnknownErr ; Log( "Could not read entire message" ) ); - require_action_quiet( pkt->len >= sizeof( DNSMessageHeader ), exit, err = mStatus_UnknownErr ; Log( "RecvPacket: Message too short (%d bytes)", pkt->len ) ); +( + TCPSocket * sock, + PktMsg * storage, + mDNSBool * closed +) +{ + int nread; + int allocsize; + mDNSu16 msglen = 0; + PktMsg * pkt = NULL; + unsigned int srclen; + int fd; + mStatus err = 0; + + fd = mDNSPlatformTCPGetFD( sock ); + + nread = my_recv( sock, &msglen, sizeof( msglen ), closed ); + + require_action_quiet( nread != -1, exit, err = mStatus_UnknownErr ); + require_action_quiet( nread > 0, exit, err = mStatus_NoError ); + + msglen = ntohs( msglen ); + require_action_quiet( nread == sizeof( msglen ), exit, err = mStatus_UnknownErr; Log( "Could not read length field of message") ); + + if ( storage ) + { + require_action_quiet( msglen <= sizeof( storage->msg ), exit, err = mStatus_UnknownErr; Log( "RecvPacket: provided buffer too small." ) ); + 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); + require_action_quiet( pkt, exit, err = mStatus_NoMemoryErr; LogErr( "RecvPacket", "malloc" ) ); + mDNSPlatformMemZero( pkt, sizeof( *pkt ) ); + } + + pkt->len = msglen; + srclen = sizeof(pkt->src); + + if ( getpeername( fd, ( struct sockaddr* ) &pkt->src, &srclen ) || ( srclen != sizeof( pkt->src ) ) ) + { + LogErr("RecvPacket", "getpeername"); + mDNSPlatformMemZero(&pkt->src, sizeof(pkt->src)); + } + + nread = my_recv(sock, &pkt->msg, msglen, closed ); + require_action_quiet( nread >= 0, exit, err = mStatus_UnknownErr ; LogErr( "RecvPacket", "recv" ) ); + require_action_quiet( nread == msglen, exit, err = mStatus_UnknownErr ; Log( "Could not read entire message" ) ); + require_action_quiet( pkt->len >= sizeof( DNSMessageHeader ), exit, err = mStatus_UnknownErr ; Log( "RecvPacket: Message too short (%d bytes)", pkt->len ) ); exit: - if ( err && pkt ) - { - if ( pkt != storage ) - { - free(pkt); - } + if ( err && pkt ) + { + if ( pkt != storage ) + { + free(pkt); + } - pkt = NULL; - } + pkt = NULL; + } - return pkt; - } + return pkt; +} mDNSlocal DNSZone* FindZone - ( - DaemonInfo * self, - domainname * name - ) - { - DNSZone * zone; - - for ( zone = self->zones; zone; zone = zone->next ) - { - if ( SameDomainName( &zone->name, name ) ) - { - break; - } - } +( + DaemonInfo * self, + domainname * name +) +{ + DNSZone * zone; + + for ( zone = self->zones; zone; zone = zone->next ) + { + if ( SameDomainName( &zone->name, name ) ) + { + break; + } + } - return zone; - } + return zone; +} mDNSlocal mDNSBool ZoneHandlesName - ( - const domainname * zname, - const domainname * dname - ) - { - mDNSu16 i = DomainNameLength( zname ); - mDNSu16 j = DomainNameLength( dname ); - - if ( ( i == ( MAX_DOMAIN_NAME + 1 ) ) || ( j == ( MAX_DOMAIN_NAME + 1 ) ) || ( i > j ) || ( memcmp( zname->c, dname->c + ( j - i ), i ) != 0 ) ) - { - return mDNSfalse; - } +( + const domainname * zname, + const domainname * dname +) +{ + mDNSu16 i = DomainNameLength( zname ); + mDNSu16 j = DomainNameLength( dname ); + + if ( ( i == ( MAX_DOMAIN_NAME + 1 ) ) || ( j == ( MAX_DOMAIN_NAME + 1 ) ) || ( i > j ) || ( memcmp( zname->c, dname->c + ( j - i ), i ) != 0 ) ) + { + return mDNSfalse; + } - return mDNStrue; - } + return mDNStrue; +} mDNSlocal mDNSBool IsQuery( PktMsg * pkt ) - { - return ( pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == (mDNSu8) ( kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery ); - } +{ + return ( pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == (mDNSu8) ( kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery ); +} mDNSlocal mDNSBool IsUpdate( PktMsg * pkt ) - { - return ( pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == (mDNSu8) ( kDNSFlag0_OP_Update ); - } +{ + return ( pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == (mDNSu8) ( kDNSFlag0_OP_Update ); +} mDNSlocal mDNSBool IsNotify(PktMsg *pkt) - { - return ( pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == ( mDNSu8) ( kDNSFlag0_OP_Notify ); - } +{ + return ( pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == ( mDNSu8) ( kDNSFlag0_OP_Notify ); +} mDNSlocal mDNSBool IsLLQRequest(PktMsg *pkt) - { - const mDNSu8 *ptr = NULL, *end = (mDNSu8 *)&pkt->msg + pkt->len; - LargeCacheRecord lcr; - int i; - mDNSBool result = mDNSfalse; - - HdrNToH(pkt); - if ((mDNSu8)(pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (mDNSu8)(kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery)) goto end; - - if (!pkt->msg.h.numAdditionals) goto end; - ptr = LocateAdditionals(&pkt->msg, end); - if (!ptr) goto end; - - // find last Additional info. - 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"); goto end; } - } - - if ( lcr.r.resrec.rrtype == kDNSType_OPT && lcr.r.resrec.rdlength >= DNSOpt_LLQData_Space && lcr.r.resrec.rdata->u.opt[0].opt == kDNSOpt_LLQ ) - { - result = mDNStrue; - } - - end: - HdrHToN(pkt); - return result; - } +{ + const mDNSu8 *ptr = NULL, *end = (mDNSu8 *)&pkt->msg + pkt->len; + LargeCacheRecord lcr; + int i; + mDNSBool result = mDNSfalse; + + HdrNToH(pkt); + if ((mDNSu8)(pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (mDNSu8)(kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery)) goto end; + + if (!pkt->msg.h.numAdditionals) goto end; + ptr = LocateAdditionals(&pkt->msg, end); + if (!ptr) goto end; + + // find last Additional info. + 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"); goto end; } + } + + if ( lcr.r.resrec.rrtype == kDNSType_OPT && lcr.r.resrec.rdlength >= DNSOpt_LLQData_Space && lcr.r.resrec.rdata->u.opt[0].opt == kDNSOpt_LLQ ) + { + result = mDNStrue; + } + +end: + HdrHToN(pkt); + return result; +} // !!!KRS implement properly mDNSlocal mDNSBool IsLLQAck(PktMsg *pkt) - { - if ((pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == (mDNSu8) ( kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery ) && - pkt->msg.h.numQuestions && !pkt->msg.h.numAnswers && !pkt->msg.h.numAuthorities) return mDNStrue; - return mDNSfalse; - } +{ + if ((pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == (mDNSu8) ( kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery ) && + pkt->msg.h.numQuestions && !pkt->msg.h.numAnswers && !pkt->msg.h.numAuthorities) return mDNStrue; + return mDNSfalse; +} mDNSlocal mDNSBool IsPublicSRV - ( - DaemonInfo * self, - DNSQuestion * q - ) - { - DNameListElem * elem; - mDNSBool ret = mDNSfalse; - int i = ( int ) DomainNameLength( &q->qname ) - 1; - - for ( elem = self->public_names; elem; elem = elem->next ) - { - int j = ( int ) DomainNameLength( &elem->name ) - 1; - - if ( i > j ) - { - for ( ; i >= 0; i--, j-- ) - { - if ( q->qname.c[ i ] != elem->name.c[ j ] ) - { - ret = mDNStrue; - goto exit; - } - } - } - } +( + DaemonInfo * self, + DNSQuestion * q +) +{ + DNameListElem * elem; + mDNSBool ret = mDNSfalse; + int i = ( int ) DomainNameLength( &q->qname ) - 1; + + for ( elem = self->public_names; elem; elem = elem->next ) + { + int j = ( int ) DomainNameLength( &elem->name ) - 1; + + if ( i > j ) + { + for ( ; i >= 0; i--, j-- ) + { + if ( q->qname.c[ i ] != elem->name.c[ j ] ) + { + ret = mDNStrue; + goto exit; + } + } + } + } exit: - return ret; - } + return ret; +} mDNSlocal void SetZone - ( - DaemonInfo * self, - PktMsg * pkt - ) - { - domainname zname; - mDNSu8 QR_OP; - const mDNSu8 * ptr = pkt->msg.data; - mDNSBool exception = mDNSfalse; +( + DaemonInfo * self, + PktMsg * pkt +) +{ + domainname zname; + mDNSu8 QR_OP; + const mDNSu8 * ptr = pkt->msg.data; + mDNSBool exception = mDNSfalse; - // Initialize + // Initialize - pkt->zone = NULL; - pkt->isZonePublic = mDNStrue; - zname.c[0] = '\0'; + pkt->zone = NULL; + pkt->isZonePublic = mDNStrue; + zname.c[0] = '\0'; - // Figure out what type of packet this is + // Figure out what type of packet this is - QR_OP = ( mDNSu8 ) ( pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ); + QR_OP = ( mDNSu8 ) ( pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ); - if ( IsQuery( pkt ) ) - { - DNSQuestion question; + if ( IsQuery( pkt ) ) + { + DNSQuestion question; - // It's a query + // It's a query - ptr = getQuestion( &pkt->msg, ptr, ( ( mDNSu8* ) &pkt->msg ) + pkt->len, NULL, &question ); + ptr = getQuestion( &pkt->msg, ptr, ( ( mDNSu8* ) &pkt->msg ) + pkt->len, NULL, &question ); - AppendDomainName( &zname, &question.qname ); + AppendDomainName( &zname, &question.qname ); - exception = ( ( question.qtype == kDNSType_SOA ) || ( question.qtype == kDNSType_NS ) || ( ( question.qtype == kDNSType_SRV ) && IsPublicSRV( self, &question ) ) ); - } - else if ( IsUpdate( pkt ) ) - { - DNSQuestion question; + exception = ( ( question.qtype == kDNSType_SOA ) || ( question.qtype == kDNSType_NS ) || ( ( question.qtype == kDNSType_SRV ) && IsPublicSRV( self, &question ) ) ); + } + else if ( IsUpdate( pkt ) ) + { + DNSQuestion question; - // It's an update. The format of the zone section is the same as the format for the question section - // according to RFC 2136, so we'll just treat this as a question so we can get at the zone. + // It's an update. The format of the zone section is the same as the format for the question section + // according to RFC 2136, so we'll just treat this as a question so we can get at the zone. - ptr = getQuestion( &pkt->msg, ptr, ( ( mDNSu8* ) &pkt->msg ) + pkt->len, NULL, &question ); + ptr = getQuestion( &pkt->msg, ptr, ( ( mDNSu8* ) &pkt->msg ) + pkt->len, NULL, &question ); - AppendDomainName( &zname, &question.qname ); + AppendDomainName( &zname, &question.qname ); - exception = mDNSfalse; - } + exception = mDNSfalse; + } - if ( zname.c[0] != '\0' ) - { - // Find the right zone + if ( zname.c[0] != '\0' ) + { + // Find the right zone - for ( pkt->zone = self->zones; pkt->zone; pkt->zone = pkt->zone->next ) - { - if ( ZoneHandlesName( &pkt->zone->name, &zname ) ) - { - VLog( "found correct zone %##s for query", pkt->zone->name.c ); + for ( pkt->zone = self->zones; pkt->zone; pkt->zone = pkt->zone->next ) + { + if ( ZoneHandlesName( &pkt->zone->name, &zname ) ) + { + VLog( "found correct zone %##s for query", pkt->zone->name.c ); - pkt->isZonePublic = ( ( pkt->zone->type == kDNSZonePublic ) || exception ); + pkt->isZonePublic = ( ( pkt->zone->type == kDNSZonePublic ) || exception ); - VLog( "zone %##s is %s", pkt->zone->name.c, ( pkt->isZonePublic ) ? "public" : "private" ); + VLog( "zone %##s is %s", pkt->zone->name.c, ( pkt->isZonePublic ) ? "public" : "private" ); - break; - } - } - } - } + break; + } + } + } +} mDNSlocal int UDPServerTransaction(const DaemonInfo *d, const PktMsg *request, PktMsg *reply, mDNSBool *trunc) - { - fd_set rset; - struct timeval timeout = { 3, 0 }; // until we remove all calls from main thread, keep timeout short - int sd; - int res; - mStatus err = mStatus_NoError; +{ + fd_set rset; + struct timeval timeout = { 3, 0 }; // until we remove all calls from main thread, keep timeout short + int sd; + int res; + mStatus err = mStatus_NoError; - // Initialize + // Initialize - *trunc = mDNSfalse; + *trunc = mDNSfalse; - // Create a socket + // Create a socket - sd = socket( AF_INET, SOCK_DGRAM, 0 ); - require_action( sd >= 0, exit, err = mStatus_UnknownErr; LogErr( "UDPServerTransaction", "socket" ) ); + sd = socket( AF_INET, SOCK_DGRAM, 0 ); + require_action( sd >= 0, exit, err = mStatus_UnknownErr; LogErr( "UDPServerTransaction", "socket" ) ); - // Send the packet to the nameserver + // Send the packet to the nameserver - VLog("UDPServerTransaction Q:%d A:%d A:%d A:%d ", - ntohs(request->msg.h.numQuestions), - ntohs(request->msg.h.numAnswers), - ntohs(request->msg.h.numAuthorities), - ntohs(request->msg.h.numAdditionals)); - res = sendto( sd, (char *)&request->msg, request->len, 0, ( struct sockaddr* ) &d->ns_addr, sizeof( d->ns_addr ) ); - require_action( res == (int) request->len, exit, err = mStatus_UnknownErr; LogErr( "UDPServerTransaction", "sendto" ) ); + VLog("UDPServerTransaction Q:%d A:%d A:%d A:%d ", + ntohs(request->msg.h.numQuestions), + ntohs(request->msg.h.numAnswers), + ntohs(request->msg.h.numAuthorities), + ntohs(request->msg.h.numAdditionals)); + res = sendto( sd, (char *)&request->msg, request->len, 0, ( struct sockaddr* ) &d->ns_addr, sizeof( d->ns_addr ) ); + require_action( res == (int) request->len, exit, err = mStatus_UnknownErr; LogErr( "UDPServerTransaction", "sendto" ) ); - // Wait for reply + // Wait for reply - FD_ZERO( &rset ); - FD_SET( sd, &rset ); - res = select( sd + 1, &rset, NULL, NULL, &timeout ); - require_action( res >= 0, exit, err = mStatus_UnknownErr; LogErr( "UDPServerTransaction", "select" ) ); - require_action( ( res > 0 ) && FD_ISSET( sd, &rset ), exit, err = mStatus_UnknownErr; Log( "UDPServerTransaction - timeout" ) ); + FD_ZERO( &rset ); + FD_SET( sd, &rset ); + res = select( sd + 1, &rset, NULL, NULL, &timeout ); + require_action( res >= 0, exit, err = mStatus_UnknownErr; LogErr( "UDPServerTransaction", "select" ) ); + require_action( ( res > 0 ) && FD_ISSET( sd, &rset ), exit, err = mStatus_UnknownErr; Log( "UDPServerTransaction - timeout" ) ); - // Receive reply + // Receive reply - reply->len = recvfrom( sd, &reply->msg, sizeof(reply->msg), 0, NULL, NULL ); - require_action( ( ( int ) reply->len ) >= 0, exit, err = mStatus_UnknownErr; LogErr( "UDPServerTransaction", "recvfrom" ) ); - require_action( reply->len >= sizeof( DNSMessageHeader ), exit, err = mStatus_UnknownErr; Log( "UDPServerTransaction - Message too short (%d bytes)", reply->len ) ); + reply->len = recvfrom( sd, &reply->msg, sizeof(reply->msg), 0, NULL, NULL ); + require_action( ( ( int ) reply->len ) >= 0, exit, err = mStatus_UnknownErr; LogErr( "UDPServerTransaction", "recvfrom" ) ); + require_action( reply->len >= sizeof( DNSMessageHeader ), exit, err = mStatus_UnknownErr; Log( "UDPServerTransaction - Message too short (%d bytes)", reply->len ) ); - // Check for truncation bit + // Check for truncation bit - if ( reply->msg.h.flags.b[0] & kDNSFlag0_TC ) - { - *trunc = mDNStrue; - } + if ( reply->msg.h.flags.b[0] & kDNSFlag0_TC ) + { + *trunc = mDNStrue; + } exit: - if ( sd >= 0 ) - { - close( sd ); - } + if ( sd >= 0 ) + { + close( sd ); + } - return err; - } + return err; +} // // Dynamic Update Utility Routines @@ -754,47 +754,47 @@ exit: // 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_Mask)) { 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; - } +{ + 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_Mask)) { 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. // Name pointer in CacheRecord object is set to point to the name specified // mDNSlocal CacheRecord *CopyCacheRecord(const CacheRecord *orig, domainname *name) - { - 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->smallrdatastorage; - cr->resrec.name = name; - - return cr; - } +{ + 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->smallrdatastorage; + cr->resrec.name = name; + + return cr; +} // @@ -804,333 +804,333 @@ mDNSlocal CacheRecord *CopyCacheRecord(const CacheRecord *orig, domainname *name // 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; - - VLog("Rehashing lease table (new size %d buckets)", newnbuckets); - new = malloc(sizeof(RRTableElem *) * newnbuckets); - if (!new) { LogErr("RehashTable", "malloc"); return; } - mDNSPlatformMemZero(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; - free(d->table); - d->table = new; - } +{ + RRTableElem *ptr, *tmp, **new; + int i, bucket, newnbuckets = d->nbuckets * 2; + + VLog("Rehashing lease table (new size %d buckets)", newnbuckets); + new = malloc(sizeof(RRTableElem *) * newnbuckets); + if (!new) { LogErr("RehashTable", "malloc"); return; } + mDNSPlatformMemZero(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; + free(d->table); + d->table = new; +} // print entire contents of hashtable, invoked via SIGINFO mDNSlocal void PrintLeaseTable(DaemonInfo *d) - { - int i; - RRTableElem *ptr; - char rrbuf[MaxMsg], 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); - } +{ + int i; + RRTableElem *ptr; + char rrbuf[MaxMsg], 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 +// Startup SRV Registration Routines // Register _dns-update._udp/_tcp. SRV records indicating the port on which -// the daemon accepts requests +// 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); - mDNSPlatformMemZero(ptr+4, sizeof(rr->rroriginalttl) + sizeof(rr->rdlength)); // zero ttl/rdata - msg->h.mDNS_numUpdates++; - return ptr + 10; - } +{ + 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); + mDNSPlatformMemZero(ptr+4, sizeof(rr->rroriginalttl) + sizeof(rr->rdlength)); // zero ttl/rdata + msg->h.mDNS_numUpdates++; + return ptr + 10; +} mDNSlocal mDNSu8 *PutUpdateSRV(DaemonInfo *d, DNSZone * zone, PktMsg *pkt, mDNSu8 *ptr, char *regtype, mDNSIPPort port, mDNSBool registration) - { - AuthRecord rr; - char hostname[1024], buf[MaxMsg]; - mDNSu8 *end = (mDNSu8 *)&pkt->msg + sizeof(DNSMessage); - - ( void ) d; - - mDNS_SetupResourceRecord(&rr, NULL, 0, kDNSType_SRV, SRV_TTL, kDNSRecordTypeUnique, AuthRecordAny, 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 = port; - if (gethostname(hostname, 1024) < 0 || !MakeDomainNameFromDNSNameString(&rr.resrec.rdata->u.srv.target, hostname)) - rr.resrec.rdata->u.srv.target.c[0] = '\0'; - - MakeDomainNameFromDNSNameString(&rr.namestorage, regtype); - AppendDomainName(&rr.namestorage, &zone->name); - 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; - } +{ + AuthRecord rr; + char hostname[1024], buf[MaxMsg]; + mDNSu8 *end = (mDNSu8 *)&pkt->msg + sizeof(DNSMessage); + + ( void ) d; + + mDNS_SetupResourceRecord(&rr, NULL, 0, kDNSType_SRV, SRV_TTL, kDNSRecordTypeUnique, AuthRecordAny, 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 = port; + if (gethostname(hostname, 1024) < 0 || !MakeDomainNameFromDNSNameString(&rr.resrec.rdata->u.srv.target, hostname)) + rr.resrec.rdata->u.srv.target.c[0] = '\0'; + + MakeDomainNameFromDNSNameString(&rr.namestorage, regtype); + AppendDomainName(&rr.namestorage, &zone->name); + 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) - { - TCPSocket *sock = NULL; - DNSZone * zone; - int err = mStatus_NoError; - - sock = ConnectToServer( d ); - require_action( sock, exit, err = mStatus_UnknownErr; Log( "UpdateSRV: ConnectToServer failed" ) ); - - for ( zone = d->zones; zone; zone = zone->next ) - { - PktMsg pkt; - mDNSu8 *ptr = pkt.msg.data; - mDNSu8 *end = (mDNSu8 *)&pkt.msg + sizeof(DNSMessage); - PktMsg *reply = NULL; - mDNSBool closed; - mDNSBool ok; - - // Initialize message - InitializeDNSMessage(&pkt.msg.h, zeroID, UpdateReqFlags); - pkt.src.sin_addr.s_addr = zerov4Addr.NotAnInteger; // address field set solely for verbose logging in subroutines - pkt.src.sin_family = AF_INET; - - // format message body - ptr = putZone(&pkt.msg, ptr, end, &zone->name, mDNSOpaque16fromIntVal(kDNSClass_IN)); - require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) ); - - if ( zone->type == kDNSZonePrivate ) - { +{ + TCPSocket *sock = NULL; + DNSZone * zone; + int err = mStatus_NoError; + + sock = ConnectToServer( d ); + require_action( sock, exit, err = mStatus_UnknownErr; Log( "UpdateSRV: ConnectToServer failed" ) ); + + for ( zone = d->zones; zone; zone = zone->next ) + { + PktMsg pkt; + mDNSu8 *ptr = pkt.msg.data; + mDNSu8 *end = (mDNSu8 *)&pkt.msg + sizeof(DNSMessage); + PktMsg *reply = NULL; + mDNSBool closed; + mDNSBool ok; + + // Initialize message + InitializeDNSMessage(&pkt.msg.h, zeroID, UpdateReqFlags); + pkt.src.sin_addr.s_addr = zerov4Addr.NotAnInteger; // address field set solely for verbose logging in subroutines + pkt.src.sin_family = AF_INET; + + // format message body + ptr = putZone(&pkt.msg, ptr, end, &zone->name, mDNSOpaque16fromIntVal(kDNSClass_IN)); + require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) ); + + if ( zone->type == kDNSZonePrivate ) + { ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-update-tls._tcp.", d->private_port, registration); require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) ); ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-query-tls._tcp.", d->private_port, registration); require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) ); ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-llq-tls._tcp.", d->private_port, registration); require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) ); - - if ( !registration ) - { - ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-update._udp.", d->llq_port, registration); - require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) ); - ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-llq._udp.", d->llq_port, registration); - require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) ); - } - } + + if ( !registration ) + { + ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-update._udp.", d->llq_port, registration); + require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) ); + ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-llq._udp.", d->llq_port, registration); + require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) ); + } + } else + { + if ( !registration ) { - if ( !registration ) - { - ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-update-tls.", d->private_port, registration); - require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) ); - ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-query-tls.", d->private_port, registration); - require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) ); - ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-llq-tls.", d->private_port, registration); - require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) ); - } - - ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-update._udp.", d->llq_port, registration); + ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-update-tls.", d->private_port, registration); + require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) ); + ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-query-tls.", d->private_port, registration); + require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) ); + ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-llq-tls.", d->private_port, registration); + require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) ); + } + + ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-update._udp.", d->llq_port, registration); require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) ); ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-llq._udp.", d->llq_port, registration); require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) ); - } + } - HdrHToN(&pkt); + HdrHToN(&pkt); - if ( zone->updateKeys ) - { - DNSDigest_SignMessage( &pkt.msg, &ptr, zone->updateKeys, 0 ); - require_action( ptr, exit, Log("UpdateSRV: Error constructing lease expiration update" ) ); - } + if ( zone->updateKeys ) + { + DNSDigest_SignMessage( &pkt.msg, &ptr, zone->updateKeys, 0 ); + require_action( ptr, exit, Log("UpdateSRV: Error constructing lease expiration update" ) ); + } - pkt.len = ptr - (mDNSu8 *)&pkt.msg; - - // send message, receive reply + pkt.len = ptr - (mDNSu8 *)&pkt.msg; - err = SendPacket( sock, &pkt ); - require_action( !err, exit, Log( "UpdateSRV: SendPacket failed" ) ); + // send message, receive reply - reply = RecvPacket( sock, NULL, &closed ); - require_action( reply, exit, err = mStatus_UnknownErr; Log( "UpdateSRV: RecvPacket returned NULL" ) ); + err = SendPacket( sock, &pkt ); + require_action( !err, exit, Log( "UpdateSRV: SendPacket failed" ) ); - ok = SuccessfulUpdateTransaction( &pkt, reply ); + reply = RecvPacket( sock, NULL, &closed ); + require_action( reply, exit, err = mStatus_UnknownErr; Log( "UpdateSRV: RecvPacket returned NULL" ) ); - if ( !ok ) - { - Log("SRV record registration failed with rcode %d", reply->msg.h.flags.b[1] & kDNSFlag1_RC_Mask); - } + ok = SuccessfulUpdateTransaction( &pkt, reply ); + + if ( !ok ) + { + Log("SRV record registration failed with rcode %d", reply->msg.h.flags.b[1] & kDNSFlag1_RC_Mask); + } + + free( reply ); + } - free( reply ); - } - exit: - if ( sock ) - { - mDNSPlatformTCPCloseConnection( sock ); - } + if ( sock ) + { + mDNSPlatformTCPCloseConnection( sock ); + } - return err; - } + return err; +} // wrapper routines/macros #define ClearUpdateSRV(d) UpdateSRV(d, 0) // clear any existing records prior to registration mDNSlocal int SetUpdateSRV(DaemonInfo *d) - { - int err; +{ + int err; - err = ClearUpdateSRV(d); // clear any existing record - if (!err) err = UpdateSRV(d, 1); - return err; - } + err = ClearUpdateSRV(d); // clear any existing record + if (!err) err = UpdateSRV(d, 1); + return err; +} // // Argument Parsing and Configuration // mDNSlocal void PrintUsage(void) - { - fprintf(stderr, "Usage: dnsextd [-f ] [-vhd] ...\n" - "Use \"dnsextd -h\" for help\n"); - } +{ + fprintf(stderr, "Usage: dnsextd [-f ] [-vhd] ...\n" + "Use \"dnsextd -h\" for help\n"); +} mDNSlocal void PrintHelp(void) - { - fprintf(stderr, "\n\n"); - PrintUsage(); +{ + fprintf(stderr, "\n\n"); + PrintUsage(); - fprintf(stderr, - "dnsextd is a daemon that implements DNS extensions supporting Dynamic DNS Update Leases\n" + 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" + "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" + "primary master server for this zone.\n\n" - "The options are as follows:\n\n" + "The options are as follows:\n\n" - "-f Specify configuration file. The default is /etc/dnsextd.conf.\n\n" + "-f Specify configuration file. The default is /etc/dnsextd.conf.\n\n" - "-d Run daemon in foreground.\n\n" + "-d Run daemon in foreground.\n\n" - "-h Print help.\n\n" + "-h Print help.\n\n" - "-v Verbose output.\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) - { - DNSZone * zone; - int opt; - int err = 0; - - cfgfile = strdup( CONFIG_FILE ); - require_action( cfgfile, arg_error, err = mStatus_NoMemoryErr ); +{ + DNSZone * zone; + int opt; + int err = 0; + + cfgfile = strdup( CONFIG_FILE ); + require_action( cfgfile, arg_error, err = mStatus_NoMemoryErr ); // defaults, may be overriden by command option - // setup our sockaddr + // setup our sockaddr - mDNSPlatformMemZero( &d->addr, sizeof( d->addr ) ); - d->addr.sin_addr.s_addr = zerov4Addr.NotAnInteger; - d->addr.sin_port = UnicastDNSPort.NotAnInteger; - d->addr.sin_family = AF_INET; + mDNSPlatformMemZero( &d->addr, sizeof( d->addr ) ); + d->addr.sin_addr.s_addr = zerov4Addr.NotAnInteger; + d->addr.sin_port = UnicastDNSPort.NotAnInteger; + d->addr.sin_family = AF_INET; #ifndef NOT_HAVE_SA_LEN - d->addr.sin_len = sizeof( d->addr ); + d->addr.sin_len = sizeof( d->addr ); #endif - // setup nameserver's sockaddr + // setup nameserver's sockaddr - mDNSPlatformMemZero(&d->ns_addr, sizeof(d->ns_addr)); - d->ns_addr.sin_family = AF_INET; - inet_pton( AF_INET, LOOPBACK, &d->ns_addr.sin_addr ); - d->ns_addr.sin_port = NSIPCPort.NotAnInteger; + mDNSPlatformMemZero(&d->ns_addr, sizeof(d->ns_addr)); + d->ns_addr.sin_family = AF_INET; + inet_pton( AF_INET, LOOPBACK, &d->ns_addr.sin_addr ); + d->ns_addr.sin_port = NSIPCPort.NotAnInteger; #ifndef NOT_HAVE_SA_LEN - d->ns_addr.sin_len = sizeof( d->ns_addr ); + d->ns_addr.sin_len = sizeof( d->ns_addr ); #endif - // setup our ports + // setup our ports - d->private_port = PrivateDNSPort; - d->llq_port = DNSEXTPort; + d->private_port = PrivateDNSPort; + d->llq_port = DNSEXTPort; - while ((opt = getopt(argc, argv, "f:hdv")) != -1) - { - switch(opt) - { - case 'f': free( cfgfile ); cfgfile = strdup( optarg ); require_action( cfgfile, arg_error, err = mStatus_NoMemoryErr ); break; - case 'h': PrintHelp(); return -1; - case 'd': foreground = 1; break; // Also used when launched via OS X's launchd mechanism - case 'v': verbose = 1; break; - default: goto arg_error; - } - } + while ((opt = getopt(argc, argv, "f:hdv")) != -1) + { + switch(opt) + { + case 'f': free( cfgfile ); cfgfile = strdup( optarg ); require_action( cfgfile, arg_error, err = mStatus_NoMemoryErr ); break; + case 'h': PrintHelp(); return -1; + case 'd': foreground = 1; break; // Also used when launched via OS X's launchd mechanism + case 'v': verbose = 1; break; + default: goto arg_error; + } + } - err = ParseConfig( d, cfgfile ); - require_noerr( err, arg_error ); + err = ParseConfig( d, cfgfile ); + require_noerr( err, arg_error ); - // Make sure we've specified some zones + // Make sure we've specified some zones - require_action( d->zones, arg_error, err = mStatus_UnknownErr ); + require_action( d->zones, arg_error, err = mStatus_UnknownErr ); - // if we have a shared secret, use it for the entire zone + // if we have a shared secret, use it for the entire zone - for ( zone = d->zones; zone; zone = zone->next ) - { - if ( zone->updateKeys ) - { - AssignDomainName( &zone->updateKeys->domain, &zone->name ); - } - } + for ( zone = d->zones; zone; zone = zone->next ) + { + if ( zone->updateKeys ) + { + AssignDomainName( &zone->updateKeys->domain, &zone->name ); + } + } + + return 0; - return 0; - arg_error: - PrintUsage(); - return -1; - } + PrintUsage(); + return -1; +} // @@ -1139,149 +1139,149 @@ arg_error: // 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; } - mDNSPlatformMemZero(d->table, sizeof(RRTableElem *) * LEASETABLE_INIT_NBUCKETS); - return 0; - } +{ + 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; } + mDNSPlatformMemZero(d->table, sizeof(RRTableElem *) * LEASETABLE_INIT_NBUCKETS); + return 0; +} mDNSlocal int SetupSockets - ( - DaemonInfo * self - ) - { - static const int kOn = 1; - int sockpair[2]; - mDNSBool private = mDNSfalse; - struct sockaddr_in daddr; - DNSZone * zone; - mStatus err = 0; - - // set up sockets on which we all ns requests - - self->tcpsd = socket( AF_INET, SOCK_STREAM, 0 ); - require_action( dnssd_SocketValid(self->tcpsd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) ); +( + DaemonInfo * self +) +{ + static const int kOn = 1; + int sockpair[2]; + mDNSBool private = mDNSfalse; + struct sockaddr_in daddr; + DNSZone * zone; + mStatus err = 0; + + // set up sockets on which we all ns requests + + self->tcpsd = socket( AF_INET, SOCK_STREAM, 0 ); + require_action( dnssd_SocketValid(self->tcpsd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) ); #if defined(SO_REUSEADDR) - err = setsockopt(self->tcpsd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); - require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->tcpsd" ) ); + err = setsockopt(self->tcpsd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); + require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->tcpsd" ) ); #endif - err = bind( self->tcpsd, ( struct sockaddr* ) &self->addr, sizeof( self->addr ) ); - require_action( !err, exit, LogErr( "SetupSockets", "bind self->tcpsd" ) ); + err = bind( self->tcpsd, ( struct sockaddr* ) &self->addr, sizeof( self->addr ) ); + require_action( !err, exit, LogErr( "SetupSockets", "bind self->tcpsd" ) ); - err = listen( self->tcpsd, LISTENQ ); - require_action( !err, exit, LogErr( "SetupSockets", "listen" ) ); + err = listen( self->tcpsd, LISTENQ ); + require_action( !err, exit, LogErr( "SetupSockets", "listen" ) ); - self->udpsd = socket( AF_INET, SOCK_DGRAM, 0 ); - require_action( dnssd_SocketValid(self->udpsd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) ); + self->udpsd = socket( AF_INET, SOCK_DGRAM, 0 ); + require_action( dnssd_SocketValid(self->udpsd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) ); #if defined(SO_REUSEADDR) - err = setsockopt(self->udpsd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); - require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->udpsd" ) ); + err = setsockopt(self->udpsd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); + require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->udpsd" ) ); #endif - err = bind( self->udpsd, ( struct sockaddr* ) &self->addr, sizeof( self->addr ) ); - require_action( !err, exit, LogErr( "SetupSockets", "bind self->udpsd" ) ); - - // set up sockets on which we receive llq requests - - mDNSPlatformMemZero(&self->llq_addr, sizeof(self->llq_addr)); - self->llq_addr.sin_family = AF_INET; - self->llq_addr.sin_addr.s_addr = zerov4Addr.NotAnInteger; - self->llq_addr.sin_port = ( self->llq_port.NotAnInteger ) ? self->llq_port.NotAnInteger : DNSEXTPort.NotAnInteger; - - if (self->llq_addr.sin_port == self->addr.sin_port) - { - self->llq_tcpsd = self->tcpsd; - self->llq_udpsd = self->udpsd; - } - else - { - self->llq_tcpsd = socket( AF_INET, SOCK_STREAM, 0 ); - require_action( dnssd_SocketValid(self->llq_tcpsd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) ); - + err = bind( self->udpsd, ( struct sockaddr* ) &self->addr, sizeof( self->addr ) ); + require_action( !err, exit, LogErr( "SetupSockets", "bind self->udpsd" ) ); + + // set up sockets on which we receive llq requests + + mDNSPlatformMemZero(&self->llq_addr, sizeof(self->llq_addr)); + self->llq_addr.sin_family = AF_INET; + self->llq_addr.sin_addr.s_addr = zerov4Addr.NotAnInteger; + self->llq_addr.sin_port = ( self->llq_port.NotAnInteger ) ? self->llq_port.NotAnInteger : DNSEXTPort.NotAnInteger; + + if (self->llq_addr.sin_port == self->addr.sin_port) + { + self->llq_tcpsd = self->tcpsd; + self->llq_udpsd = self->udpsd; + } + else + { + self->llq_tcpsd = socket( AF_INET, SOCK_STREAM, 0 ); + require_action( dnssd_SocketValid(self->llq_tcpsd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) ); + #if defined(SO_REUSEADDR) - err = setsockopt(self->llq_tcpsd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); - require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->llq_tcpsd" ) ); + err = setsockopt(self->llq_tcpsd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); + require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->llq_tcpsd" ) ); #endif - - err = bind( self->llq_tcpsd, ( struct sockaddr* ) &self->llq_addr, sizeof( self->llq_addr ) ); - require_action( !err, exit, LogErr( "SetupSockets", "bind self->llq_tcpsd" ) ); - - err = listen( self->llq_tcpsd, LISTENQ ); - require_action( !err, exit, LogErr( "SetupSockets", "listen" ) ); - - self->llq_udpsd = socket( AF_INET, SOCK_DGRAM, 0 ); - require_action( dnssd_SocketValid(self->llq_udpsd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) ); - + + err = bind( self->llq_tcpsd, ( struct sockaddr* ) &self->llq_addr, sizeof( self->llq_addr ) ); + require_action( !err, exit, LogErr( "SetupSockets", "bind self->llq_tcpsd" ) ); + + err = listen( self->llq_tcpsd, LISTENQ ); + require_action( !err, exit, LogErr( "SetupSockets", "listen" ) ); + + self->llq_udpsd = socket( AF_INET, SOCK_DGRAM, 0 ); + require_action( dnssd_SocketValid(self->llq_udpsd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) ); + #if defined(SO_REUSEADDR) - err = setsockopt(self->llq_udpsd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); - require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->llq_udpsd" ) ); + err = setsockopt(self->llq_udpsd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); + require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->llq_udpsd" ) ); #endif - - err = bind(self->llq_udpsd, ( struct sockaddr* ) &self->llq_addr, sizeof( self->llq_addr ) ); - require_action( !err, exit, LogErr( "SetupSockets", "bind self->llq_udpsd" ) ); - } - // set up Unix domain socket pair for LLQ polling thread to signal main thread that a change to the zone occurred + err = bind(self->llq_udpsd, ( struct sockaddr* ) &self->llq_addr, sizeof( self->llq_addr ) ); + require_action( !err, exit, LogErr( "SetupSockets", "bind self->llq_udpsd" ) ); + } + + // set up Unix domain socket pair for LLQ polling thread to signal main thread that a change to the zone occurred - err = socketpair( AF_LOCAL, SOCK_STREAM, 0, sockpair ); - require_action( !err, exit, LogErr( "SetupSockets", "socketpair" ) ); + err = socketpair( AF_LOCAL, SOCK_STREAM, 0, sockpair ); + require_action( !err, exit, LogErr( "SetupSockets", "socketpair" ) ); - self->LLQEventListenSock = sockpair[0]; - self->LLQEventNotifySock = sockpair[1]; + self->LLQEventListenSock = sockpair[0]; + self->LLQEventNotifySock = sockpair[1]; - // set up socket on which we receive private requests + // set up socket on which we receive private requests - self->llq_tcpsd = socket( AF_INET, SOCK_STREAM, 0 ); - require_action( dnssd_SocketValid(self->tlssd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) ); - mDNSPlatformMemZero(&daddr, sizeof(daddr)); - daddr.sin_family = AF_INET; - daddr.sin_addr.s_addr = zerov4Addr.NotAnInteger; - daddr.sin_port = ( self->private_port.NotAnInteger ) ? self->private_port.NotAnInteger : PrivateDNSPort.NotAnInteger; + self->llq_tcpsd = socket( AF_INET, SOCK_STREAM, 0 ); + require_action( dnssd_SocketValid(self->tlssd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) ); + mDNSPlatformMemZero(&daddr, sizeof(daddr)); + daddr.sin_family = AF_INET; + daddr.sin_addr.s_addr = zerov4Addr.NotAnInteger; + daddr.sin_port = ( self->private_port.NotAnInteger ) ? self->private_port.NotAnInteger : PrivateDNSPort.NotAnInteger; - self->tlssd = socket( AF_INET, SOCK_STREAM, 0 ); - require_action( dnssd_SocketValid(self->tlssd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) ); + self->tlssd = socket( AF_INET, SOCK_STREAM, 0 ); + require_action( dnssd_SocketValid(self->tlssd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) ); #if defined(SO_REUSEADDR) - err = setsockopt(self->tlssd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); - require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->tlssd" ) ); + err = setsockopt(self->tlssd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); + require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->tlssd" ) ); #endif - err = bind( self->tlssd, ( struct sockaddr* ) &daddr, sizeof( daddr ) ); - require_action( !err, exit, LogErr( "SetupSockets", "bind self->tlssd" ) ); + err = bind( self->tlssd, ( struct sockaddr* ) &daddr, sizeof( daddr ) ); + require_action( !err, exit, LogErr( "SetupSockets", "bind self->tlssd" ) ); - err = listen( self->tlssd, LISTENQ ); - require_action( !err, exit, LogErr( "SetupSockets", "listen" ) ); + err = listen( self->tlssd, LISTENQ ); + require_action( !err, exit, LogErr( "SetupSockets", "listen" ) ); - // Do we have any private zones? + // Do we have any private zones? - for ( zone = self->zones; zone; zone = zone->next ) - { - if ( zone->type == kDNSZonePrivate ) - { - private = mDNStrue; - break; - } - } + for ( zone = self->zones; zone; zone = zone->next ) + { + if ( zone->type == kDNSZonePrivate ) + { + private = mDNStrue; + break; + } + } - if ( private ) - { - err = mDNSPlatformTLSSetupCerts(); - require_action( !err, exit, LogErr( "SetupSockets", "mDNSPlatformTLSSetupCerts" ) ); - } + if ( private ) + { + err = mDNSPlatformTLSSetupCerts(); + require_action( !err, exit, LogErr( "SetupSockets", "mDNSPlatformTLSSetupCerts" ) ); + } exit: - return err; - } + return err; +} // // periodic table updates @@ -1290,81 +1290,81 @@ exit: // Delete a resource record from the nameserver via a dynamic update // sd is a socket already connected to the server mDNSlocal void DeleteOneRecord(DaemonInfo *d, CacheRecord *rr, domainname *zname, TCPSocket *sock) - { - DNSZone * zone; - PktMsg pkt; - mDNSu8 *ptr = pkt.msg.data; - mDNSu8 *end = (mDNSu8 *)&pkt.msg + sizeof(DNSMessage); - char buf[MaxMsg]; - mDNSBool closed; - PktMsg *reply = NULL; - - VLog("Expiring record %s", GetRRDisplayString_rdb(&rr->resrec, &rr->resrec.rdata->u, buf)); - - InitializeDNSMessage(&pkt.msg.h, zeroID, UpdateReqFlags); - - ptr = putZone(&pkt.msg, ptr, end, zname, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); - if (!ptr) goto end; - ptr = putDeletionRecord(&pkt.msg, ptr, &rr->resrec); - if (!ptr) goto end; - - HdrHToN(&pkt); - - zone = FindZone( d, zname ); - - if ( zone && zone->updateKeys) - { - DNSDigest_SignMessage(&pkt.msg, &ptr, zone->updateKeys, 0 ); - if (!ptr) goto end; - } - - pkt.len = ptr - (mDNSu8 *)&pkt.msg; - pkt.src.sin_addr.s_addr = zerov4Addr.NotAnInteger; // address field set solely for verbose logging in subroutines - pkt.src.sin_family = AF_INET; - if (SendPacket( sock, &pkt)) { Log("DeleteOneRecord: SendPacket failed"); } - reply = RecvPacket( sock, NULL, &closed ); - if (reply) HdrNToH(reply); - require_action( reply, end, Log( "DeleteOneRecord: RecvPacket returned NULL" ) ); - - if (!SuccessfulUpdateTransaction(&pkt, reply)) - Log("Expiration update failed with rcode %d", reply ? reply->msg.h.flags.b[1] & kDNSFlag1_RC_Mask : -1); - - end: - if (!ptr) { Log("DeleteOneRecord: Error constructing lease expiration update"); } - if (reply) free(reply); - } +{ + DNSZone * zone; + PktMsg pkt; + mDNSu8 *ptr = pkt.msg.data; + mDNSu8 *end = (mDNSu8 *)&pkt.msg + sizeof(DNSMessage); + char buf[MaxMsg]; + mDNSBool closed; + PktMsg *reply = NULL; + + VLog("Expiring record %s", GetRRDisplayString_rdb(&rr->resrec, &rr->resrec.rdata->u, buf)); + + InitializeDNSMessage(&pkt.msg.h, zeroID, UpdateReqFlags); + + ptr = putZone(&pkt.msg, ptr, end, zname, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); + if (!ptr) goto end; + ptr = putDeletionRecord(&pkt.msg, ptr, &rr->resrec); + if (!ptr) goto end; + + HdrHToN(&pkt); + + zone = FindZone( d, zname ); + + if ( zone && zone->updateKeys) + { + DNSDigest_SignMessage(&pkt.msg, &ptr, zone->updateKeys, 0 ); + if (!ptr) goto end; + } + + pkt.len = ptr - (mDNSu8 *)&pkt.msg; + pkt.src.sin_addr.s_addr = zerov4Addr.NotAnInteger; // address field set solely for verbose logging in subroutines + pkt.src.sin_family = AF_INET; + if (SendPacket( sock, &pkt)) { Log("DeleteOneRecord: SendPacket failed"); } + reply = RecvPacket( sock, NULL, &closed ); + if (reply) HdrNToH(reply); + require_action( reply, end, Log( "DeleteOneRecord: RecvPacket returned NULL" ) ); + + if (!SuccessfulUpdateTransaction(&pkt, reply)) + Log("Expiration update failed with rcode %d", reply ? reply->msg.h.flags.b[1] & kDNSFlag1_RC_Mask : -1); + +end: + if (!ptr) { Log("DeleteOneRecord: Error constructing lease expiration update"); } + if (reply) free(reply); +} // iterate over table, deleting expired records (or all records if DeleteAll is true) mDNSlocal void DeleteRecords(DaemonInfo *d, mDNSBool DeleteAll) - { - struct timeval now; - int i; - TCPSocket *sock = ConnectToServer(d); - if (!sock) { Log("DeleteRecords: ConnectToServer failed"); return; } - if (gettimeofday(&now, NULL)) { LogErr("DeleteRecords ", "gettimeofday"); return; } - if (pthread_mutex_lock(&d->tablelock)) { LogErr("DeleteRecords", "pthread_mutex_lock"); return; } - - for (i = 0; i < d->nbuckets; i++) - { - RRTableElem **ptr = &d->table[i]; - while (*ptr) - { - if (DeleteAll || (*ptr)->expire - now.tv_sec < 0) - { - RRTableElem *fptr; - // delete record from server - DeleteOneRecord(d, &(*ptr)->rr, &(*ptr)->zone, sock); - fptr = *ptr; - *ptr = (*ptr)->next; - free(fptr); - d->nelems--; - } - else ptr = &(*ptr)->next; - } - } - pthread_mutex_unlock(&d->tablelock); - mDNSPlatformTCPCloseConnection( sock ); - } +{ + struct timeval now; + int i; + TCPSocket *sock = ConnectToServer(d); + if (!sock) { Log("DeleteRecords: ConnectToServer failed"); return; } + if (gettimeofday(&now, NULL)) { LogErr("DeleteRecords ", "gettimeofday"); return; } + if (pthread_mutex_lock(&d->tablelock)) { LogErr("DeleteRecords", "pthread_mutex_lock"); return; } + + for (i = 0; i < d->nbuckets; i++) + { + RRTableElem **ptr = &d->table[i]; + while (*ptr) + { + if (DeleteAll || (*ptr)->expire - now.tv_sec < 0) + { + RRTableElem *fptr; + // delete record from server + DeleteOneRecord(d, &(*ptr)->rr, &(*ptr)->zone, sock); + fptr = *ptr; + *ptr = (*ptr)->next; + free(fptr); + d->nelems--; + } + else ptr = &(*ptr)->next; + } + } + pthread_mutex_unlock(&d->tablelock); + mDNSPlatformTCPCloseConnection( sock ); +} // // main update request handling @@ -1372,269 +1372,269 @@ mDNSlocal void DeleteRecords(DaemonInfo *d, mDNSBool DeleteAll) // 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 **rptr, *tmp; - int i, allocsize, bucket; - LargeCacheRecord lcr; - ResourceRecord *rr = &lcr.r.resrec; - const mDNSu8 *ptr, *end; - struct timeval tv; - DNSQuestion zone; - char buf[MaxMsg]; - - 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++) - { - mDNSBool DeleteAllRRSets = mDNSfalse, DeleteOneRRSet = mDNSfalse, DeleteOneRR = mDNSfalse; - - ptr = GetLargeResourceRecord(NULL, &pkt->msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); - if (!ptr || lcr.r.resrec.RecordType == kDNSRecordTypePacketNegative) { Log("UpdateLeaseTable: GetLargeResourceRecord failed"); goto cleanup; } - bucket = rr->namehash % d->nbuckets; - rptr = &d->table[bucket]; - - // handle deletions - if (rr->rrtype == kDNSQType_ANY && !rr->rroriginalttl && rr->rrclass == kDNSQClass_ANY && !rr->rdlength) - DeleteAllRRSets = mDNStrue; // delete all rrsets for a name - else if (!rr->rroriginalttl && rr->rrclass == kDNSQClass_ANY && !rr->rdlength) - DeleteOneRRSet = mDNStrue; - else if (!rr->rroriginalttl && rr->rrclass == kDNSClass_NONE) - DeleteOneRR = mDNStrue; - - if (DeleteAllRRSets || DeleteOneRRSet || DeleteOneRR) - { - while (*rptr) - { - if (SameDomainName((*rptr)->rr.resrec.name, rr->name) && - (DeleteAllRRSets || - (DeleteOneRRSet && (*rptr)->rr.resrec.rrtype == rr->rrtype) || - (DeleteOneRR && IdenticalResourceRecord(&(*rptr)->rr.resrec, rr)))) - { - tmp = *rptr; - VLog("Received deletion update for %s", GetRRDisplayString_rdb(&tmp->rr.resrec, &tmp->rr.resrec.rdata->u, buf)); - *rptr = (*rptr)->next; - free(tmp); - d->nelems--; - } - else rptr = &(*rptr)->next; - } - } - else if (lease > 0) - { - // see if add or refresh - while (*rptr && !IdenticalResourceRecord(&(*rptr)->rr.resrec, rr)) rptr = &(*rptr)->next; - if (*rptr) - { - // refresh - if (gettimeofday(&tv, NULL)) { LogErr("UpdateLeaseTable", "gettimeofday"); goto cleanup; } - (*rptr)->expire = tv.tv_sec + (unsigned)lease; - VLog("Refreshing lease for %s", GetRRDisplayString_rdb(&lcr.r.resrec, &lcr.r.resrec.rdata->u, buf)); - } - else - { - // New record - add to table - if (d->nelems > d->nbuckets) - { - RehashTable(d); - bucket = rr->namehash % d->nbuckets; - rptr = &d->table[bucket]; - } - if (gettimeofday(&tv, NULL)) { LogErr("UpdateLeaseTable", "gettimeofday"); goto cleanup; } - allocsize = sizeof(RRTableElem); - if (rr->rdlength > InlineCacheRDSize) allocsize += (rr->rdlength - InlineCacheRDSize); - tmp = malloc(allocsize); - if (!tmp) { LogErr("UpdateLeaseTable", "malloc"); goto cleanup; } - memcpy(&tmp->rr, &lcr.r, sizeof(CacheRecord) + rr->rdlength - InlineCacheRDSize); - tmp->rr.resrec.rdata = (RData *)&tmp->rr.smallrdatastorage; - AssignDomainName(&tmp->name, rr->name); - tmp->rr.resrec.name = &tmp->name; - tmp->expire = tv.tv_sec + (unsigned)lease; - tmp->cli.sin_addr = pkt->src.sin_addr; - AssignDomainName(&tmp->zone, &zone.qname); - tmp->next = d->table[bucket]; - d->table[bucket] = tmp; - 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); - } +{ + RRTableElem **rptr, *tmp; + int i, allocsize, bucket; + LargeCacheRecord lcr; + ResourceRecord *rr = &lcr.r.resrec; + const mDNSu8 *ptr, *end; + struct timeval tv; + DNSQuestion zone; + char buf[MaxMsg]; + + 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++) + { + mDNSBool DeleteAllRRSets = mDNSfalse, DeleteOneRRSet = mDNSfalse, DeleteOneRR = mDNSfalse; + + ptr = GetLargeResourceRecord(NULL, &pkt->msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); + if (!ptr || lcr.r.resrec.RecordType == kDNSRecordTypePacketNegative) { Log("UpdateLeaseTable: GetLargeResourceRecord failed"); goto cleanup; } + bucket = rr->namehash % d->nbuckets; + rptr = &d->table[bucket]; + + // handle deletions + if (rr->rrtype == kDNSQType_ANY && !rr->rroriginalttl && rr->rrclass == kDNSQClass_ANY && !rr->rdlength) + DeleteAllRRSets = mDNStrue; // delete all rrsets for a name + else if (!rr->rroriginalttl && rr->rrclass == kDNSQClass_ANY && !rr->rdlength) + DeleteOneRRSet = mDNStrue; + else if (!rr->rroriginalttl && rr->rrclass == kDNSClass_NONE) + DeleteOneRR = mDNStrue; + + if (DeleteAllRRSets || DeleteOneRRSet || DeleteOneRR) + { + while (*rptr) + { + if (SameDomainName((*rptr)->rr.resrec.name, rr->name) && + (DeleteAllRRSets || + (DeleteOneRRSet && (*rptr)->rr.resrec.rrtype == rr->rrtype) || + (DeleteOneRR && IdenticalResourceRecord(&(*rptr)->rr.resrec, rr)))) + { + tmp = *rptr; + VLog("Received deletion update for %s", GetRRDisplayString_rdb(&tmp->rr.resrec, &tmp->rr.resrec.rdata->u, buf)); + *rptr = (*rptr)->next; + free(tmp); + d->nelems--; + } + else rptr = &(*rptr)->next; + } + } + else if (lease > 0) + { + // see if add or refresh + while (*rptr && !IdenticalResourceRecord(&(*rptr)->rr.resrec, rr)) rptr = &(*rptr)->next; + if (*rptr) + { + // refresh + if (gettimeofday(&tv, NULL)) { LogErr("UpdateLeaseTable", "gettimeofday"); goto cleanup; } + (*rptr)->expire = tv.tv_sec + (unsigned)lease; + VLog("Refreshing lease for %s", GetRRDisplayString_rdb(&lcr.r.resrec, &lcr.r.resrec.rdata->u, buf)); + } + else + { + // New record - add to table + if (d->nelems > d->nbuckets) + { + RehashTable(d); + bucket = rr->namehash % d->nbuckets; + rptr = &d->table[bucket]; + } + if (gettimeofday(&tv, NULL)) { LogErr("UpdateLeaseTable", "gettimeofday"); goto cleanup; } + allocsize = sizeof(RRTableElem); + if (rr->rdlength > InlineCacheRDSize) allocsize += (rr->rdlength - InlineCacheRDSize); + tmp = malloc(allocsize); + if (!tmp) { LogErr("UpdateLeaseTable", "malloc"); goto cleanup; } + memcpy(&tmp->rr, &lcr.r, sizeof(CacheRecord) + rr->rdlength - InlineCacheRDSize); + tmp->rr.resrec.rdata = (RData *)&tmp->rr.smallrdatastorage; + AssignDomainName(&tmp->name, rr->name); + tmp->rr.resrec.name = &tmp->name; + tmp->expire = tv.tv_sec + (unsigned)lease; + tmp->cli.sin_addr = pkt->src.sin_addr; + AssignDomainName(&tmp->zone, &zone.qname); + tmp->next = d->table[bucket]; + d->table[bucket] = tmp; + 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 = zerov4Addr.NotAnInteger; // 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; - HdrHToN(reply); - return reply; - } +{ + 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 = zerov4Addr.NotAnInteger; // 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; + HdrHToN(reply); + return reply; +} // pkt is thread-local, not requiring locking mDNSlocal PktMsg* HandleRequest - ( - DaemonInfo * self, - PktMsg * request - ) - { - PktMsg * reply = NULL; - PktMsg * leaseReply; - PktMsg buf; - char addrbuf[32]; - TCPSocket * sock = NULL; - mStatus err; - mDNSs32 lease = 0; - if ((request->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) == kDNSFlag0_OP_Update) - { - int i, adds = 0, dels = 0; - const mDNSu8 *ptr, *end = (mDNSu8 *)&request->msg + request->len; - HdrNToH(request); - lease = GetPktLease(&mDNSStorage, &request->msg, end); - ptr = LocateAuthorities(&request->msg, end); - for (i = 0; i < request->msg.h.mDNS_numUpdates; i++) - { - LargeCacheRecord lcr; - ptr = GetLargeResourceRecord(NULL, &request->msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); - if (lcr.r.resrec.RecordType != kDNSRecordTypePacketNegative && lcr.r.resrec.rroriginalttl) adds++; else dels++; - } - HdrHToN(request); - if (adds && !lease) - { - static const mDNSOpaque16 UpdateRefused = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, kDNSFlag1_RC_Refused } }; - Log("Rejecting Update Request with %d additions but no lease", adds); - reply = malloc(sizeof(*reply)); - mDNSPlatformMemZero(&reply->src, sizeof(reply->src)); - reply->len = sizeof(DNSMessageHeader); - reply->zone = NULL; - reply->isZonePublic = 0; - InitializeDNSMessage(&reply->msg.h, request->msg.h.id, UpdateRefused); - return(reply); - } - if (lease > 7200) // Don't allow lease greater than two hours; typically 90-minute renewal period - lease = 7200; - } - // Send msg to server, read reply - - if ( request->len <= 512 ) - { - mDNSBool trunc; - - if ( UDPServerTransaction( self, request, &buf, &trunc) < 0 ) - { - Log("HandleRequest - UDPServerTransaction failed. Trying TCP"); - } - else if ( trunc ) - { - VLog("HandleRequest - answer truncated. Using TCP"); - } - else - { - reply = &buf; // success - } - } - - if ( !reply ) - { - mDNSBool closed; - int res; - - sock = ConnectToServer( self ); - require_action_quiet( sock, exit, err = mStatus_UnknownErr ; Log( "Discarding request from %s due to connection errors", inet_ntop( AF_INET, &request->src.sin_addr, addrbuf, 32 ) ) ); - - res = SendPacket( sock, request ); - require_action_quiet( res >= 0, exit, err = mStatus_UnknownErr ; Log( "Couldn't relay message from %s to server. Discarding.", inet_ntop(AF_INET, &request->src.sin_addr, addrbuf, 32 ) ) ); - - reply = RecvPacket( sock, &buf, &closed ); - } - - // IMPORTANT: reply is in network byte order at this point in the code - // We keep it this way because we send it back to the client in the same form - - // Is it an update? - - if ( reply && ( ( reply->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == ( kDNSFlag0_OP_Update | kDNSFlag0_QR_Response ) ) ) - { - char pingmsg[4]; - mDNSBool ok = SuccessfulUpdateTransaction( request, reply ); - require_action( ok, exit, err = mStatus_UnknownErr; VLog( "Message from %s not a successful update.", inet_ntop(AF_INET, &request->src.sin_addr, addrbuf, 32 ) ) ); - - UpdateLeaseTable( request, self, lease ); - - if ( lease > 0 ) - { - leaseReply = FormatLeaseReply( self, reply, lease ); - - if ( !leaseReply ) - { - Log("HandleRequest - unable to format lease reply"); - } - - // %%% Looks like a potential memory leak -- who frees the original reply? - reply = leaseReply; - } - - // tell the main thread there was an update so it can send LLQs - - if ( send( self->LLQEventNotifySock, pingmsg, sizeof( pingmsg ), 0 ) != sizeof( pingmsg ) ) - { - LogErr("HandleRequest", "send"); - } - } +( + DaemonInfo * self, + PktMsg * request +) +{ + PktMsg * reply = NULL; + PktMsg * leaseReply; + PktMsg buf; + char addrbuf[32]; + TCPSocket * sock = NULL; + mStatus err; + mDNSs32 lease = 0; + if ((request->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) == kDNSFlag0_OP_Update) + { + int i, adds = 0, dels = 0; + const mDNSu8 *ptr, *end = (mDNSu8 *)&request->msg + request->len; + HdrNToH(request); + lease = GetPktLease(&mDNSStorage, &request->msg, end); + ptr = LocateAuthorities(&request->msg, end); + for (i = 0; i < request->msg.h.mDNS_numUpdates; i++) + { + LargeCacheRecord lcr; + ptr = GetLargeResourceRecord(NULL, &request->msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); + if (lcr.r.resrec.RecordType != kDNSRecordTypePacketNegative && lcr.r.resrec.rroriginalttl) adds++;else dels++; + } + HdrHToN(request); + if (adds && !lease) + { + static const mDNSOpaque16 UpdateRefused = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, kDNSFlag1_RC_Refused } }; + Log("Rejecting Update Request with %d additions but no lease", adds); + reply = malloc(sizeof(*reply)); + mDNSPlatformMemZero(&reply->src, sizeof(reply->src)); + reply->len = sizeof(DNSMessageHeader); + reply->zone = NULL; + reply->isZonePublic = 0; + InitializeDNSMessage(&reply->msg.h, request->msg.h.id, UpdateRefused); + return(reply); + } + if (lease > 7200) // Don't allow lease greater than two hours; typically 90-minute renewal period + lease = 7200; + } + // Send msg to server, read reply + + if ( request->len <= 512 ) + { + mDNSBool trunc; + + if ( UDPServerTransaction( self, request, &buf, &trunc) < 0 ) + { + Log("HandleRequest - UDPServerTransaction failed. Trying TCP"); + } + else if ( trunc ) + { + VLog("HandleRequest - answer truncated. Using TCP"); + } + else + { + reply = &buf; // success + } + } + + if ( !reply ) + { + mDNSBool closed; + int res; + + sock = ConnectToServer( self ); + require_action_quiet( sock, exit, err = mStatus_UnknownErr ; Log( "Discarding request from %s due to connection errors", inet_ntop( AF_INET, &request->src.sin_addr, addrbuf, 32 ) ) ); + + res = SendPacket( sock, request ); + require_action_quiet( res >= 0, exit, err = mStatus_UnknownErr ; Log( "Couldn't relay message from %s to server. Discarding.", inet_ntop(AF_INET, &request->src.sin_addr, addrbuf, 32 ) ) ); + + reply = RecvPacket( sock, &buf, &closed ); + } + + // IMPORTANT: reply is in network byte order at this point in the code + // We keep it this way because we send it back to the client in the same form + + // Is it an update? + + if ( reply && ( ( reply->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == ( kDNSFlag0_OP_Update | kDNSFlag0_QR_Response ) ) ) + { + char pingmsg[4]; + mDNSBool ok = SuccessfulUpdateTransaction( request, reply ); + require_action( ok, exit, err = mStatus_UnknownErr; VLog( "Message from %s not a successful update.", inet_ntop(AF_INET, &request->src.sin_addr, addrbuf, 32 ) ) ); + + UpdateLeaseTable( request, self, lease ); + + if ( lease > 0 ) + { + leaseReply = FormatLeaseReply( self, reply, lease ); + + if ( !leaseReply ) + { + Log("HandleRequest - unable to format lease reply"); + } + + // %%% Looks like a potential memory leak -- who frees the original reply? + reply = leaseReply; + } + + // tell the main thread there was an update so it can send LLQs + + if ( send( self->LLQEventNotifySock, pingmsg, sizeof( pingmsg ), 0 ) != sizeof( pingmsg ) ) + { + LogErr("HandleRequest", "send"); + } + } exit: - if ( sock ) - { - mDNSPlatformTCPCloseConnection( sock ); - } + if ( sock ) + { + mDNSPlatformTCPCloseConnection( sock ); + } - if ( reply == &buf ) - { - reply = malloc( sizeof( *reply ) ); + if ( reply == &buf ) + { + reply = malloc( sizeof( *reply ) ); - if ( reply ) - { - reply->len = buf.len; - memcpy(&reply->msg, &buf.msg, buf.len); - } - else - { - LogErr("HandleRequest", "malloc"); - } - } + if ( reply ) + { + reply->len = buf.len; + memcpy(&reply->msg, &buf.msg, buf.len); + } + else + { + LogErr("HandleRequest", "malloc"); + } + } - return reply; - } + return reply; +} // @@ -1643,1452 +1643,1452 @@ exit: // Set fields of an LLQ OPT Resource Record mDNSlocal void FormatLLQOpt(AuthRecord *opt, int opcode, const mDNSOpaque64 *const id, mDNSs32 lease) - { - mDNSPlatformMemZero(opt, sizeof(*opt)); - mDNS_SetupResourceRecord(opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); - opt->resrec.rrclass = NormalMaxDNSMessageData; - opt->resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record - opt->resrec.rdestimate = sizeof(rdataOPT); - opt->resrec.rdata->u.opt[0].opt = kDNSOpt_LLQ; - opt->resrec.rdata->u.opt[0].u.llq.vers = kLLQ_Vers; - opt->resrec.rdata->u.opt[0].u.llq.llqOp = opcode; - opt->resrec.rdata->u.opt[0].u.llq.err = LLQErr_NoError; - opt->resrec.rdata->u.opt[0].u.llq.id = *id; - opt->resrec.rdata->u.opt[0].u.llq.llqlease = lease; - } +{ + mDNSPlatformMemZero(opt, sizeof(*opt)); + mDNS_SetupResourceRecord(opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + opt->resrec.rrclass = NormalMaxDNSMessageData; + opt->resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record + opt->resrec.rdestimate = sizeof(rdataOPT); + opt->resrec.rdata->u.opt[0].opt = kDNSOpt_LLQ; + opt->resrec.rdata->u.opt[0].u.llq.vers = kLLQ_Vers; + opt->resrec.rdata->u.opt[0].u.llq.llqOp = opcode; + opt->resrec.rdata->u.opt[0].u.llq.err = LLQErr_NoError; + opt->resrec.rdata->u.opt[0].u.llq.id = *id; + opt->resrec.rdata->u.opt[0].u.llq.llqlease = 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; - } +{ + struct timeval t; + + gettimeofday(&t, NULL); + if (e->expire < t.tv_sec) return 0; + else return e->expire - t.tv_sec; +} mDNSlocal void DeleteLLQ(DaemonInfo *d, LLQEntry *e) - { - int bucket = DomainNameHashValue(&e->qname) % LLQ_TABLESIZE; - LLQEntry **ptr = &d->LLQTable[bucket]; - AnswerListElem *a = e->AnswerList; - 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); - - if (a && !(--a->refcount) && d->AnswerTableCount >= LLQ_TABLESIZE) - { - // currently, generating initial answers blocks the main thread, so we keep the answer list - // even if the ref count drops to zero. To prevent unbounded table growth, we free shared answers - // if the ref count drops to zero AND there are more table elements than buckets - // !!!KRS update this when we make the table dynamically growable - - CacheRecord *cr = a->KnownAnswers, *tmp; - AnswerListElem **tbl = &d->AnswerTable[bucket]; - - while (cr) - { - tmp = cr; - cr = cr->next; - free(tmp); - } - - while (*tbl && *tbl != a) tbl = &(*tbl)->next; - if (*tbl) { *tbl = (*tbl)->next; free(a); d->AnswerTableCount--; } - else Log("Error: DeleteLLQ - AnswerList not found in table"); - } - - // remove LLQ from table, free memory - while(*ptr && *ptr != e) ptr = &(*ptr)->next; - if (!*ptr) { Log("Error: DeleteLLQ - LLQ not in table"); return; } - *ptr = (*ptr)->next; - free(e); - } +{ + int bucket = DomainNameHashValue(&e->qname) % LLQ_TABLESIZE; + LLQEntry **ptr = &d->LLQTable[bucket]; + AnswerListElem *a = e->AnswerList; + 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); + + if (a && !(--a->refcount) && d->AnswerTableCount >= LLQ_TABLESIZE) + { + // currently, generating initial answers blocks the main thread, so we keep the answer list + // even if the ref count drops to zero. To prevent unbounded table growth, we free shared answers + // if the ref count drops to zero AND there are more table elements than buckets + // !!!KRS update this when we make the table dynamically growable + + CacheRecord *cr = a->KnownAnswers, *tmp; + AnswerListElem **tbl = &d->AnswerTable[bucket]; + + while (cr) + { + tmp = cr; + cr = cr->next; + free(tmp); + } + + while (*tbl && *tbl != a) tbl = &(*tbl)->next; + if (*tbl) { *tbl = (*tbl)->next; free(a); d->AnswerTableCount--; } + else Log("Error: DeleteLLQ - AnswerList not found in table"); + } + + // remove LLQ from table, free memory + while(*ptr && *ptr != e) ptr = &(*ptr)->next; + if (!*ptr) { Log("Error: DeleteLLQ - LLQ not in table"); return; } + *ptr = (*ptr)->next; + free(e); +} mDNSlocal int SendLLQ(DaemonInfo *d, PktMsg *pkt, struct sockaddr_in dst, TCPSocket *sock) - { - char addr[32]; - int err = -1; - - HdrHToN(pkt); - - if ( sock ) - { - if ( SendPacket( sock, pkt ) != 0 ) - { - LogErr("DaemonInfo", "MySend"); - Log("Could not send response to client %s", inet_ntop(AF_INET, &dst.sin_addr, addr, 32)); - } - } - else - { - if (sendto(d->llq_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)); - } - } - - err = 0; - HdrNToH(pkt); - return err; - } +{ + char addr[32]; + int err = -1; + + HdrHToN(pkt); + + if ( sock ) + { + if ( SendPacket( sock, pkt ) != 0 ) + { + LogErr("DaemonInfo", "MySend"); + Log("Could not send response to client %s", inet_ntop(AF_INET, &dst.sin_addr, addr, 32)); + } + } + else + { + if (sendto(d->llq_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)); + } + } + + err = 0; + HdrNToH(pkt); + return err; +} mDNSlocal CacheRecord *AnswerQuestion(DaemonInfo *d, AnswerListElem *e) - { - PktMsg q; - int i; - TCPSocket *sock = NULL; - const mDNSu8 *ansptr; - mDNSu8 *end = q.msg.data; - PktMsg buf, *reply = NULL; - LargeCacheRecord lcr; - CacheRecord *AnswerList = NULL; - mDNSu8 rcode; - - VLog("Querying server for %##s type %d", e->name.c, e->type); - - InitializeDNSMessage(&q.msg.h, zeroID, uQueryFlags); - - end = putQuestion(&q.msg, end, end + AbsoluteMaxDNSMessageData, &e->name, e->type, kDNSClass_IN); - if (!end) { Log("Error: AnswerQuestion - putQuestion returned NULL"); goto end; } - q.len = (int)(end - (mDNSu8 *)&q.msg); - - HdrHToN(&q); - - if (!e->UseTCP) - { - mDNSBool trunc; - - if (UDPServerTransaction(d, &q, &buf, &trunc) < 0) - Log("AnswerQuestion %##s - UDPServerTransaction failed. Trying TCP", e->name.c); - else if (trunc) - { VLog("AnswerQuestion %##s - answer truncated. Using TCP", e->name.c); e->UseTCP = mDNStrue; } - else reply = &buf; // success - } - - if (!reply) - { - mDNSBool closed; - - sock = ConnectToServer(d); - if (!sock) { Log("AnswerQuestion: ConnectToServer failed"); goto end; } - if (SendPacket( sock, &q)) { Log("AnswerQuestion: SendPacket failed"); mDNSPlatformTCPCloseConnection( sock ); goto end; } - reply = RecvPacket( sock, NULL, &closed ); - mDNSPlatformTCPCloseConnection( sock ); - require_action( reply, end, Log( "AnswerQuestion: RecvPacket returned NULL" ) ); - } - - HdrNToH(&q); - if (reply) HdrNToH(reply); - - 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_Mask); - if (rcode && rcode != kDNSFlag1_RC_NXDomain) { Log("AnswerQuestion: %##s type %d - non-zero rcode %d from server", e->name.c, e->type, 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++) - { - ansptr = GetLargeResourceRecord(NULL, &reply->msg, ansptr, end, 0, kDNSRecordTypePacketAns, &lcr); - if (!ansptr) { Log("AnswerQuestions: GetLargeResourceRecord returned NULL"); goto end; } - if (lcr.r.resrec.RecordType != kDNSRecordTypePacketNegative) - { - if (lcr.r.resrec.rrtype != e->type || lcr.r.resrec.rrclass != kDNSClass_IN || !SameDomainName(lcr.r.resrec.name, &e->name)) - { - Log("AnswerQuestion: response %##s type #d does not answer question %##s type #d. Discarding", - lcr.r.resrec.name->c, lcr.r.resrec.rrtype, e->name.c, e->type); - } - else - { - CacheRecord *cr = CopyCacheRecord(&lcr.r, &e->name); - if (!cr) { Log("Error: AnswerQuestion - CopyCacheRecord returned NULL"); goto end; } - cr->next = AnswerList; - AnswerList = cr; - } - } - } - - end: - if (reply && reply != &buf) free(reply); - return AnswerList; - } +{ + PktMsg q; + int i; + TCPSocket *sock = NULL; + const mDNSu8 *ansptr; + mDNSu8 *end = q.msg.data; + PktMsg buf, *reply = NULL; + LargeCacheRecord lcr; + CacheRecord *AnswerList = NULL; + mDNSu8 rcode; + + VLog("Querying server for %##s type %d", e->name.c, e->type); + + InitializeDNSMessage(&q.msg.h, zeroID, uQueryFlags); + + end = putQuestion(&q.msg, end, end + AbsoluteMaxDNSMessageData, &e->name, e->type, kDNSClass_IN); + if (!end) { Log("Error: AnswerQuestion - putQuestion returned NULL"); goto end; } + q.len = (int)(end - (mDNSu8 *)&q.msg); + + HdrHToN(&q); + + if (!e->UseTCP) + { + mDNSBool trunc; + + if (UDPServerTransaction(d, &q, &buf, &trunc) < 0) + Log("AnswerQuestion %##s - UDPServerTransaction failed. Trying TCP", e->name.c); + else if (trunc) + { VLog("AnswerQuestion %##s - answer truncated. Using TCP", e->name.c); e->UseTCP = mDNStrue; } + else reply = &buf; // success + } + + if (!reply) + { + mDNSBool closed; + + sock = ConnectToServer(d); + if (!sock) { Log("AnswerQuestion: ConnectToServer failed"); goto end; } + if (SendPacket( sock, &q)) { Log("AnswerQuestion: SendPacket failed"); mDNSPlatformTCPCloseConnection( sock ); goto end; } + reply = RecvPacket( sock, NULL, &closed ); + mDNSPlatformTCPCloseConnection( sock ); + require_action( reply, end, Log( "AnswerQuestion: RecvPacket returned NULL" ) ); + } + + HdrNToH(&q); + if (reply) HdrNToH(reply); + + 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_Mask); + if (rcode && rcode != kDNSFlag1_RC_NXDomain) { Log("AnswerQuestion: %##s type %d - non-zero rcode %d from server", e->name.c, e->type, 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++) + { + ansptr = GetLargeResourceRecord(NULL, &reply->msg, ansptr, end, 0, kDNSRecordTypePacketAns, &lcr); + if (!ansptr) { Log("AnswerQuestions: GetLargeResourceRecord returned NULL"); goto end; } + if (lcr.r.resrec.RecordType != kDNSRecordTypePacketNegative) + { + if (lcr.r.resrec.rrtype != e->type || lcr.r.resrec.rrclass != kDNSClass_IN || !SameDomainName(lcr.r.resrec.name, &e->name)) + { + Log("AnswerQuestion: response %##s type #d does not answer question %##s type #d. Discarding", + lcr.r.resrec.name->c, lcr.r.resrec.rrtype, e->name.c, e->type); + } + else + { + CacheRecord *cr = CopyCacheRecord(&lcr.r, &e->name); + if (!cr) { Log("Error: AnswerQuestion - CopyCacheRecord returned NULL"); goto end; } + cr->next = AnswerList; + AnswerList = cr; + } + } + } + +end: + if (reply && reply != &buf) free(reply); + return AnswerList; +} // Routine forks a thread to set EventList to contain Add/Remove events, and deletes any removes from the KnownAnswer list mDNSlocal void *UpdateAnswerList(void *args) - { - CacheRecord *cr, *NewAnswers, **na, **ka; // "new answer", "known answer" - DaemonInfo *d = ((UpdateAnswerListArgs *)args)->d; - AnswerListElem *a = ((UpdateAnswerListArgs *)args)->a; - - free(args); - args = NULL; - - // get up to date answers - NewAnswers = AnswerQuestion(d, a); - - // first pass - mark all answers for deletion - for (ka = &a->KnownAnswers; *ka; ka = &(*ka)->next) - (*ka)->resrec.rroriginalttl = (unsigned)-1; // -1 means delete - - // second pass - mark answers pre-existent - for (ka = &a->KnownAnswers; *ka; ka = &(*ka)->next) - { - for (na = &NewAnswers; *na; na = &(*na)->next) - { - if (IdenticalResourceRecord(&(*ka)->resrec, &(*na)->resrec)) - { (*ka)->resrec.rroriginalttl = 0; break; } // 0 means no change - } - } - - // third pass - add new records to Event list - na = &NewAnswers; - while (*na) - { - for (ka = &a->KnownAnswers; *ka; ka = &(*ka)->next) - if (IdenticalResourceRecord(&(*ka)->resrec, &(*na)->resrec)) break; - if (!*ka) - { - // answer is not in list - splice from NewAnswers list, add to Event list - cr = *na; - *na = (*na)->next; // splice from list - cr->next = a->EventList; // add spliced record to event list - a->EventList = cr; - cr->resrec.rroriginalttl = 1; // 1 means add - } - else na = &(*na)->next; - } - - // move all the removes from the answer list to the event list - ka = &a->KnownAnswers; - while (*ka) - { - if ((*ka)->resrec.rroriginalttl == (unsigned)-1) - { - cr = *ka; - *ka = (*ka)->next; - cr->next = a->EventList; - a->EventList = cr; - } - else ka = &(*ka)->next; - } - - // lastly, free the remaining records (known answers) in NewAnswers list - while (NewAnswers) - { - cr = NewAnswers; - NewAnswers = NewAnswers->next; - free(cr); - } - - return NULL; - } +{ + CacheRecord *cr, *NewAnswers, **na, **ka; // "new answer", "known answer" + DaemonInfo *d = ((UpdateAnswerListArgs *)args)->d; + AnswerListElem *a = ((UpdateAnswerListArgs *)args)->a; + + free(args); + args = NULL; + + // get up to date answers + NewAnswers = AnswerQuestion(d, a); + + // first pass - mark all answers for deletion + for (ka = &a->KnownAnswers; *ka; ka = &(*ka)->next) + (*ka)->resrec.rroriginalttl = (unsigned)-1; // -1 means delete + + // second pass - mark answers pre-existent + for (ka = &a->KnownAnswers; *ka; ka = &(*ka)->next) + { + for (na = &NewAnswers; *na; na = &(*na)->next) + { + if (IdenticalResourceRecord(&(*ka)->resrec, &(*na)->resrec)) + { (*ka)->resrec.rroriginalttl = 0; break; } // 0 means no change + } + } + + // third pass - add new records to Event list + na = &NewAnswers; + while (*na) + { + for (ka = &a->KnownAnswers; *ka; ka = &(*ka)->next) + if (IdenticalResourceRecord(&(*ka)->resrec, &(*na)->resrec)) break; + if (!*ka) + { + // answer is not in list - splice from NewAnswers list, add to Event list + cr = *na; + *na = (*na)->next; // splice from list + cr->next = a->EventList; // add spliced record to event list + a->EventList = cr; + cr->resrec.rroriginalttl = 1; // 1 means add + } + else na = &(*na)->next; + } + + // move all the removes from the answer list to the event list + ka = &a->KnownAnswers; + while (*ka) + { + if ((*ka)->resrec.rroriginalttl == (unsigned)-1) + { + cr = *ka; + *ka = (*ka)->next; + cr->next = a->EventList; + a->EventList = cr; + } + else ka = &(*ka)->next; + } + + // lastly, free the remaining records (known answers) in NewAnswers list + while (NewAnswers) + { + cr = NewAnswers; + NewAnswers = NewAnswers->next; + free(cr); + } + + return NULL; +} mDNSlocal void SendEvents(DaemonInfo *d, LLQEntry *e) - { - PktMsg response; - CacheRecord *cr; - mDNSu8 *end = (mDNSu8 *)&response.msg.data; - mDNSOpaque16 msgID; - char rrbuf[MaxMsg], addrbuf[32]; - AuthRecord opt; - - // Should this really be random? Do we use the msgID on the receiving end? - msgID.NotAnInteger = random(); - if (verbose) inet_ntop(AF_INET, &e->cli.sin_addr, addrbuf, 32); - InitializeDNSMessage(&response.msg.h, msgID, ResponseFlags); - end = putQuestion(&response.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN); - if (!end) { Log("Error: SendEvents - putQuestion returned NULL"); return; } - - // put adds/removes in packet - for (cr = e->AnswerList->EventList; cr; cr = cr->next) - { - if (verbose) GetRRDisplayString_rdb(&cr->resrec, &cr->resrec.rdata->u, rrbuf); - VLog("%s (%s): %s", addrbuf, (mDNSs32)cr->resrec.rroriginalttl < 0 ? "Remove": "Add", rrbuf); - end = PutResourceRecordTTLJumbo(&response.msg, end, &response.msg.h.numAnswers, &cr->resrec, cr->resrec.rroriginalttl); - if (!end) { Log("Error: SendEvents - PutResourceRecordTTLJumbo returned NULL"); return; } - } - - FormatLLQOpt(&opt, kLLQOp_Event, &e->id, LLQLease(e)); - end = PutResourceRecordTTLJumbo(&response.msg, end, &response.msg.h.numAdditionals, &opt.resrec, 0); - if (!end) { Log("Error: SendEvents - PutResourceRecordTTLJumbo"); return; } - - response.len = (int)(end - (mDNSu8 *)&response.msg); - if (SendLLQ(d, &response, e->cli, NULL ) < 0) LogMsg("Error: SendEvents - SendLLQ"); - } +{ + PktMsg response; + CacheRecord *cr; + mDNSu8 *end = (mDNSu8 *)&response.msg.data; + mDNSOpaque16 msgID; + char rrbuf[MaxMsg], addrbuf[32]; + AuthRecord opt; + + // Should this really be random? Do we use the msgID on the receiving end? + msgID.NotAnInteger = random(); + if (verbose) inet_ntop(AF_INET, &e->cli.sin_addr, addrbuf, 32); + InitializeDNSMessage(&response.msg.h, msgID, ResponseFlags); + end = putQuestion(&response.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN); + if (!end) { Log("Error: SendEvents - putQuestion returned NULL"); return; } + + // put adds/removes in packet + for (cr = e->AnswerList->EventList; cr; cr = cr->next) + { + if (verbose) GetRRDisplayString_rdb(&cr->resrec, &cr->resrec.rdata->u, rrbuf); + VLog("%s (%s): %s", addrbuf, (mDNSs32)cr->resrec.rroriginalttl < 0 ? "Remove" : "Add", rrbuf); + end = PutResourceRecordTTLJumbo(&response.msg, end, &response.msg.h.numAnswers, &cr->resrec, cr->resrec.rroriginalttl); + if (!end) { Log("Error: SendEvents - PutResourceRecordTTLJumbo returned NULL"); return; } + } + + FormatLLQOpt(&opt, kLLQOp_Event, &e->id, LLQLease(e)); + end = PutResourceRecordTTLJumbo(&response.msg, end, &response.msg.h.numAdditionals, &opt.resrec, 0); + if (!end) { Log("Error: SendEvents - PutResourceRecordTTLJumbo"); return; } + + response.len = (int)(end - (mDNSu8 *)&response.msg); + if (SendLLQ(d, &response, e->cli, NULL ) < 0) LogMsg("Error: SendEvents - SendLLQ"); +} mDNSlocal void PrintLLQAnswers(DaemonInfo *d) - { - int i; - char rrbuf[MaxMsg]; - - Log("Printing LLQ Answer Table contents"); - - for (i = 0; i < LLQ_TABLESIZE; i++) - { - AnswerListElem *a = d->AnswerTable[i]; - while(a) - { - int ancount = 0; - const CacheRecord *rr = a->KnownAnswers; - while (rr) { ancount++; rr = rr->next; } - Log("%p : Question %##s; type %d; referenced by %d LLQs; %d answers:", a, a->name.c, a->type, a->refcount, ancount); - for (rr = a->KnownAnswers; rr; rr = rr->next) Log("\t%s", GetRRDisplayString_rdb(&rr->resrec, &rr->resrec.rdata->u, rrbuf)); - a = a->next; - } - } - } +{ + int i; + char rrbuf[MaxMsg]; + + Log("Printing LLQ Answer Table contents"); + + for (i = 0; i < LLQ_TABLESIZE; i++) + { + AnswerListElem *a = d->AnswerTable[i]; + while(a) + { + int ancount = 0; + const CacheRecord *rr = a->KnownAnswers; + while (rr) { ancount++; rr = rr->next; } + Log("%p : Question %##s; type %d; referenced by %d LLQs; %d answers:", a, a->name.c, a->type, a->refcount, ancount); + for (rr = a->KnownAnswers; rr; rr = rr->next) Log("\t%s", GetRRDisplayString_rdb(&rr->resrec, &rr->resrec.rdata->u, rrbuf)); + a = a->next; + } + } +} 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) - { - char *state; - - switch (e->state) - { - case RequestReceived: state = "RequestReceived"; break; - case ChallengeSent: state = "ChallengeSent"; break; - case Established: state = "Established"; break; - default: state = "unknown"; - } - inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32); - - Log("LLQ from %s in state %s; %##s; type %d; orig lease %d; remaining lease %d; AnswerList %p)", - addr, state, e->qname.c, e->qtype, e->lease, LLQLease(e), e->AnswerList); - e = e->next; - } - } - } +{ + 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) + { + char *state; + + switch (e->state) + { + case RequestReceived: state = "RequestReceived"; break; + case ChallengeSent: state = "ChallengeSent"; break; + case Established: state = "Established"; break; + default: state = "unknown"; + } + inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32); + + Log("LLQ from %s in state %s; %##s; type %d; orig lease %d; remaining lease %d; AnswerList %p)", + addr, state, e->qname.c, e->qtype, e->lease, LLQLease(e), e->AnswerList); + e = e->next; + } + } +} // Send events to clients as a result of a change in the zone mDNSlocal void GenLLQEvents(DaemonInfo *d) - { - LLQEntry **e; - int i; - struct timeval t; - UpdateAnswerListArgs *args; - - VLog("Generating LLQ Events"); - - gettimeofday(&t, NULL); - - // get all answers up to date - for (i = 0; i < LLQ_TABLESIZE; i++) - { - AnswerListElem *a = d->AnswerTable[i]; - while(a) - { - args = malloc(sizeof(*args)); - if (!args) { LogErr("GenLLQEvents", "malloc"); return; } - args->d = d; - args->a = a; - if (pthread_create(&a->tid, NULL, UpdateAnswerList, args) < 0) { LogErr("GenLLQEvents", "pthread_create"); return; } - usleep(1); - a = a->next; - } - } - - for (i = 0; i < LLQ_TABLESIZE; i++) - { - AnswerListElem *a = d->AnswerTable[i]; - while(a) - { - if (pthread_join(a->tid, NULL)) LogErr("GenLLQEvents", "pthread_join"); - a = a->next; - } - } - +{ + LLQEntry **e; + int i; + struct timeval t; + UpdateAnswerListArgs *args; + + VLog("Generating LLQ Events"); + + gettimeofday(&t, NULL); + + // get all answers up to date + for (i = 0; i < LLQ_TABLESIZE; i++) + { + AnswerListElem *a = d->AnswerTable[i]; + while(a) + { + args = malloc(sizeof(*args)); + if (!args) { LogErr("GenLLQEvents", "malloc"); return; } + args->d = d; + args->a = a; + if (pthread_create(&a->tid, NULL, UpdateAnswerList, args) < 0) { LogErr("GenLLQEvents", "pthread_create"); return; } + usleep(1); + a = a->next; + } + } + + for (i = 0; i < LLQ_TABLESIZE; i++) + { + AnswerListElem *a = d->AnswerTable[i]; + while(a) + { + if (pthread_join(a->tid, NULL)) LogErr("GenLLQEvents", "pthread_join"); + a = a->next; + } + } + // for each established LLQ, send events - for (i = 0; i < LLQ_TABLESIZE; i++) - { - e = &d->LLQTable[i]; - while(*e) - { - if ((*e)->expire < t.tv_sec) DeleteLLQ(d, *e); - else - { - if ((*e)->state == Established && (*e)->AnswerList->EventList) SendEvents(d, *e); - e = &(*e)->next; - } - } - } - - // now that all LLQs are updated, we move Add events from the Event list to the Known Answer list, and free Removes - for (i = 0; i < LLQ_TABLESIZE; i++) - { - AnswerListElem *a = d->AnswerTable[i]; - while(a) - { - if (a->EventList) - { - CacheRecord *cr = a->EventList, *tmp; - while (cr) - { - tmp = cr; - cr = cr->next; - if ((signed)tmp->resrec.rroriginalttl < 0) free(tmp); - else - { - tmp->next = a->KnownAnswers; - a->KnownAnswers = tmp; - tmp->resrec.rroriginalttl = 0; - } - } - a->EventList = NULL; - } - a = a->next; - } - } - } + for (i = 0; i < LLQ_TABLESIZE; i++) + { + e = &d->LLQTable[i]; + while(*e) + { + if ((*e)->expire < t.tv_sec) DeleteLLQ(d, *e); + else + { + if ((*e)->state == Established && (*e)->AnswerList->EventList) SendEvents(d, *e); + e = &(*e)->next; + } + } + } + + // now that all LLQs are updated, we move Add events from the Event list to the Known Answer list, and free Removes + for (i = 0; i < LLQ_TABLESIZE; i++) + { + AnswerListElem *a = d->AnswerTable[i]; + while(a) + { + if (a->EventList) + { + CacheRecord *cr = a->EventList, *tmp; + while (cr) + { + tmp = cr; + cr = cr->next; + if ((signed)tmp->resrec.rroriginalttl < 0) free(tmp); + else + { + tmp->next = a->KnownAnswers; + a->KnownAnswers = tmp; + tmp->resrec.rroriginalttl = 0; + } + } + a->EventList = NULL; + } + a = a->next; + } + } +} mDNSlocal void SetAnswerList(DaemonInfo *d, LLQEntry *e) - { - int bucket = DomainNameHashValue(&e->qname) % LLQ_TABLESIZE; - AnswerListElem *a = d->AnswerTable[bucket]; - while (a && (a->type != e->qtype ||!SameDomainName(&a->name, &e->qname))) a = a->next; - if (!a) - { - a = malloc(sizeof(*a)); - if (!a) { LogErr("SetAnswerList", "malloc"); return; } - AssignDomainName(&a->name, &e->qname); - a->type = e->qtype; - a->refcount = 0; - a->EventList = NULL; - a->UseTCP = mDNSfalse; - a->next = d->AnswerTable[bucket]; - d->AnswerTable[bucket] = a; - d->AnswerTableCount++; - a->KnownAnswers = AnswerQuestion(d, a); - } - - e->AnswerList = a; - a->refcount ++; - } - - // Allocate LLQ entry, insert into table +{ + int bucket = DomainNameHashValue(&e->qname) % LLQ_TABLESIZE; + AnswerListElem *a = d->AnswerTable[bucket]; + while (a && (a->type != e->qtype ||!SameDomainName(&a->name, &e->qname))) a = a->next; + if (!a) + { + a = malloc(sizeof(*a)); + if (!a) { LogErr("SetAnswerList", "malloc"); return; } + AssignDomainName(&a->name, &e->qname); + a->type = e->qtype; + a->refcount = 0; + a->EventList = NULL; + a->UseTCP = mDNSfalse; + a->next = d->AnswerTable[bucket]; + d->AnswerTable[bucket] = a; + d->AnswerTableCount++; + a->KnownAnswers = AnswerQuestion(d, a); + } + + e->AnswerList = a; + a->refcount++; +} + +// 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 = DomainNameHashValue(qname) % LLQ_TABLESIZE; - LLQEntry *e; - - 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; - e->id = zeroOpaque64; - e->state = RequestReceived; - e->AnswerList = NULL; - - if (lease < LLQ_MIN_LEASE) lease = LLQ_MIN_LEASE; - else if (lease > LLQ_MAX_LEASE) lease = LLQ_MAX_LEASE; - - gettimeofday(&t, NULL); - e->expire = t.tv_sec + (int)lease; - e->lease = lease; - - // add to table - e->next = d->LLQTable[bucket]; - d->LLQTable[bucket] = e; - - return e; - } +{ + char addr[32]; + struct timeval t; + int bucket = DomainNameHashValue(qname) % LLQ_TABLESIZE; + LLQEntry *e; + + 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; + e->id = zeroOpaque64; + e->state = RequestReceived; + e->AnswerList = NULL; + + if (lease < LLQ_MIN_LEASE) lease = LLQ_MIN_LEASE; + else if (lease > LLQ_MAX_LEASE) lease = LLQ_MAX_LEASE; + + gettimeofday(&t, NULL); + e->expire = t.tv_sec + (int)lease; + e->lease = lease; + + // add to table + 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, TCPSocket *sock ) - { - 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->llqlease ? "Refreshing" : "Deleting", e->qname.c, addr); - - if (llq->llqlease) - { - struct timeval t; - if (llq->llqlease < LLQ_MIN_LEASE) llq->llqlease = LLQ_MIN_LEASE; - else if (llq->llqlease > LLQ_MAX_LEASE) llq->llqlease = LLQ_MIN_LEASE; - gettimeofday(&t, NULL); - e->expire = t.tv_sec + llq->llqlease; - } - - 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->llqlease ? 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, sock)) Log("Error: LLQRefresh"); - - if (llq->llqlease) e->state = Established; - else DeleteLLQ(d, e); - } +{ + 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->llqlease ? "Refreshing" : "Deleting", e->qname.c, addr); + + if (llq->llqlease) + { + struct timeval t; + if (llq->llqlease < LLQ_MIN_LEASE) llq->llqlease = LLQ_MIN_LEASE; + else if (llq->llqlease > LLQ_MAX_LEASE) llq->llqlease = LLQ_MIN_LEASE; + gettimeofday(&t, NULL); + e->expire = t.tv_sec + llq->llqlease; + } + + 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->llqlease ? 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, sock)) Log("Error: LLQRefresh"); + + if (llq->llqlease) 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, TCPSocket *sock) - { - char addr[32]; - CacheRecord *ptr; - AuthRecord opt; - PktMsg ack; - mDNSu8 *end = (mDNSu8 *)&ack.msg.data; - char rrbuf[MaxMsg], addrbuf[32]; - - inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32); - - if (!mDNSSameOpaque64(&llq->id, &e->id) || - llq->vers != kLLQ_Vers || - llq->llqOp != kLLQOp_Setup || - llq->err != LLQErr_NoError || - llq->llqlease > e->lease + LLQ_LEASE_FUDGE || - llq->llqlease < 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) { SetAnswerList(d, e); e->state = Established; } - - if (verbose) inet_ntop(AF_INET, &e->cli.sin_addr, addrbuf, 32); - for (ptr = e->AnswerList->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, sock)) Log("Error: LLQCompleteHandshake"); - } +{ + char addr[32]; + CacheRecord *ptr; + AuthRecord opt; + PktMsg ack; + mDNSu8 *end = (mDNSu8 *)&ack.msg.data; + char rrbuf[MaxMsg], addrbuf[32]; + + inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32); + + if (!mDNSSameOpaque64(&llq->id, &e->id) || + llq->vers != kLLQ_Vers || + llq->llqOp != kLLQOp_Setup || + llq->err != LLQErr_NoError || + llq->llqlease > e->lease + LLQ_LEASE_FUDGE || + llq->llqlease < 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) { SetAnswerList(d, e); e->state = Established; } + + if (verbose) inet_ntop(AF_INET, &e->cli.sin_addr, addrbuf, 32); + for (ptr = e->AnswerList->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, sock)) Log("Error: LLQCompleteHandshake"); +} mDNSlocal void LLQSetupChallenge(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID) - { - struct timeval t; - 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 (!mDNSOpaque64IsZero(&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 (mDNSOpaque64IsZero(&e->id)) // don't regenerate random ID for retransmissions - { - // construct ID