From 4aea607d357d16eb4a69db265dc8f5c7faef7405 Mon Sep 17 00:00:00 2001 From: Apple Date: Fri, 6 Jan 2006 01:28:50 +0000 Subject: [PATCH] mDNSResponder-107.5.tar.gz --- Clients/BonjourExample/BonjourExample.cpp | 212 ++ Clients/BonjourExample/BonjourExample.sln | 21 + .../BonjourExample/BonjourExample.vcproj | 137 +- Clients/BonjourExample/stdafx.cpp | 33 + Clients/BonjourExample/stdafx.h | 43 + Clients/DNS-SD.VisualStudio/dns-sd.vcproj | 24 +- Clients/ExplorerPlugin/ExplorerBarWindow.cpp | 29 +- Clients/ExplorerPlugin/ExplorerPlugin.cpp | 29 +- Clients/ExplorerPlugin/ExplorerPlugin.vcproj | 36 +- .../ExplorerPlugin/ExplorerPluginLocRes.rc | 2 +- .../ExplorerPluginLocRes.vcproj | 38 +- Clients/ExplorerPlugin/ExplorerPluginRes.rc | 1 - .../ExplorerPlugin/ExplorerPluginRes.vcproj | 49 +- Clients/ExplorerPlugin/StdAfx.h | 7 + Clients/ExplorerPlugin/res/globe.bmp | Bin 824 -> 0 bytes Clients/ExplorerPlugin/resource_res.h | 1 - Clients/Java/JavaSamples.vcproj | 42 + Clients/Java/nmakefile | 2 +- Clients/Makefile | 13 +- Clients/PrinterSetupWizard/FirstPage.cpp | 43 +- Clients/PrinterSetupWizard/FirstPage.h | 4 + Clients/PrinterSetupWizard/FourthPage.cpp | 19 + Clients/PrinterSetupWizard/FourthPage.h | 4 + .../PrinterSetupWizard.vcproj | 68 +- .../PrinterSetupWizardApp.cpp | 5 +- .../PrinterSetupWizardLocRes.rc | 37 +- .../PrinterSetupWizardLocRes.vcproj | 37 +- .../PrinterSetupWizardRes.vcproj | 49 +- .../PrinterSetupWizardSheet.cpp | 109 +- .../PrinterSetupWizardSheet.h | 37 +- Clients/PrinterSetupWizard/SecondPage.cpp | 124 +- Clients/PrinterSetupWizard/SecondPage.h | 7 + Clients/PrinterSetupWizard/ThirdPage.cpp | 206 +- Clients/PrinterSetupWizard/ThirdPage.h | 13 +- Clients/PrinterSetupWizard/UtilTypes.h | 39 +- .../PrinterSetupWizard/res/NetworkPrinter.ico | Bin 77214 -> 82726 bytes Clients/PrinterSetupWizard/res/Print.ico | Bin 77214 -> 82726 bytes Clients/PrinterSetupWizard/resource.h | 36 +- Clients/PrinterSetupWizard/stdafx.h | 7 + Clients/dns-sd.c | 155 +- Makefile | 2 +- buildResults.xml | 50 + mDNSCore/DNSCommon.c | 22 +- mDNSCore/mDNS.c | 66 +- mDNSCore/mDNSDebug.h | 7 + mDNSCore/mDNSEmbeddedAPI.h | 13 + mDNSCore/uDNS.c | 7 +- mDNSMacOS9/mDNSMacOS9.c | 5 +- mDNSMacOSX/LegacyNATTraversal.c | 152 +- ...iceDiscoveryPref.icns => BonjourPref.icns} | Bin 38889 -> 39196 bytes mDNSMacOSX/PreferencePane/BonjourPref.tiff | Bin 0 -> 2430 bytes .../PreferencePane/ConfigurationAuthority.c | 11 +- .../DNSServiceDiscoveryPref.tiff | Bin 17394 -> 0 bytes .../DNSServiceDiscoveryPref.nib/info.nib | 6 +- .../keyedobjects.nib | Bin 17441 -> 21082 bytes .../PreferencePane/PrivilegedOperations.c | 19 +- .../PreferencePane/PrivilegedOperations.h | 13 +- mDNSMacOSX/PreferencePane/ddnswriteconfig.m | 64 +- mDNSMacOSX/PreferencePane/installtool | 100 + mDNSMacOSX/daemon.c | 137 +- mDNSMacOSX/mDNSMacOSX.c | 63 +- mDNSMacOSX/mDNSMacOSX.h | 5 +- .../mDNSResponder.pbproj/project.pbxproj | 65 +- mDNSPosix/Makefile | 134 +- mDNSPosix/NetMonitor.c | 36 +- mDNSPosix/PosixDaemon.c | 13 +- mDNSPosix/ProxyResponder.c | 4 + mDNSPosix/ReadMe.txt | 63 +- mDNSPosix/Responder.c | 12 +- mDNSPosix/dnsextd.c | 576 ++-- mDNSPosix/mDNSPosix.c | 40 +- mDNSPosix/mDNSUNP.c | 202 +- mDNSPosix/mDNSUNP.h | 26 +- mDNSPosix/nss_mdns.c | 10 +- mDNSResponder.sln | 67 +- mDNSShared/Java/DNSSD.java | 15 +- mDNSShared/Java/DNSSDException.java | 15 + mDNSShared/Java/JNISupport.c | 41 +- mDNSShared/PlatformCommon.c | 12 +- mDNSShared/PlatformCommon.h | 6 +- mDNSShared/dns-sd.1 | 5 +- mDNSShared/dns_sd.h | 36 +- mDNSShared/dnssd_clientlib.c | 23 +- mDNSShared/dnssd_clientstub.c | 34 +- mDNSShared/dnssd_ipc.h | 4 +- mDNSShared/uds_daemon.c | 281 +- mDNSVxWorks/mDNSVxWorks.c | 2789 ++++++++--------- mDNSVxWorks/mDNSVxWorks.h | 143 +- .../ControlPanel/ConfigPropertySheet.cpp | 5 +- mDNSWindows/ControlPanel/ControlPanel.rc | 29 +- mDNSWindows/ControlPanel/ControlPanel.vcproj | 42 +- mDNSWindows/ControlPanel/FirstPage.cpp | 38 +- mDNSWindows/ControlPanel/SecondPage.cpp | 81 +- mDNSWindows/ControlPanel/SecondPage.h | 4 + mDNSWindows/ControlPanel/SharedSecret.cpp | 50 +- mDNSWindows/ControlPanel/SharedSecret.h | 8 +- mDNSWindows/ControlPanel/ThirdPage.cpp | 375 +-- mDNSWindows/ControlPanel/resource.h | 2 + mDNSWindows/ControlPanel/stdafx.h | 7 + mDNSWindows/DLL.NET/Stdafx.h | 7 + mDNSWindows/DLL.NET/dnssd_NET.vcproj | 30 +- mDNSWindows/DLL/dllmain.c | 84 + mDNSWindows/DLL/dnssd.vcproj | 40 +- mDNSWindows/Installer/Main.ism | Bin 195584 -> 0 bytes mDNSWindows/Installer/SDK.ism | Bin 189952 -> 0 bytes mDNSWindows/Java/Java.vcproj | 42 + mDNSWindows/Java/makefile | 5 +- mDNSWindows/NSPTool/NSPTool.vcproj | 20 +- mDNSWindows/RegNames.h | 5 + mDNSWindows/SystemService/Firewall.cpp | 14 +- mDNSWindows/SystemService/Service.c | 308 +- mDNSWindows/SystemService/Service.vcproj | 66 +- mDNSWindows/SystemServiceTest/Tool.c | 851 ----- mDNSWindows/SystemServiceTest/Tool.mcp | Bin 210145 -> 0 bytes mDNSWindows/VPCDetect.cpp | 153 + .../Prefix.h => VPCDetect.h} | 37 +- mDNSWindows/WinVersRes.h | 71 +- mDNSWindows/dDNS.c | 63 +- mDNSWindows/dDNS.h | 5 + mDNSWindows/loclibrary.c | 24 +- mDNSWindows/mDNSWin32.c | 1106 +++++-- mDNSWindows/mDNSWin32.h | 4 + mDNSWindows/mdnsNSP/mdnsNSP.c | 800 ++++- mDNSWindows/mdnsNSP/mdnsNSP.vcproj | 28 +- 124 files changed, 7112 insertions(+), 4404 deletions(-) create mode 100644 Clients/BonjourExample/BonjourExample.cpp create mode 100644 Clients/BonjourExample/BonjourExample.sln rename mDNSWindows/SystemServiceTest/Tool2002.vcproj => Clients/BonjourExample/BonjourExample.vcproj (51%) create mode 100644 Clients/BonjourExample/stdafx.cpp create mode 100644 Clients/BonjourExample/stdafx.h delete mode 100755 Clients/ExplorerPlugin/res/globe.bmp create mode 100755 Clients/Java/JavaSamples.vcproj create mode 100644 buildResults.xml rename mDNSMacOSX/PreferencePane/{DNSServiceDiscoveryPref.icns => BonjourPref.icns} (56%) create mode 100644 mDNSMacOSX/PreferencePane/BonjourPref.tiff delete mode 100644 mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.tiff create mode 100755 mDNSMacOSX/PreferencePane/installtool delete mode 100644 mDNSWindows/Installer/Main.ism delete mode 100644 mDNSWindows/Installer/SDK.ism create mode 100755 mDNSWindows/Java/Java.vcproj delete mode 100644 mDNSWindows/SystemServiceTest/Tool.c delete mode 100644 mDNSWindows/SystemServiceTest/Tool.mcp create mode 100755 mDNSWindows/VPCDetect.cpp rename mDNSWindows/{SystemServiceTest/Prefix.h => VPCDetect.h} (67%) diff --git a/Clients/BonjourExample/BonjourExample.cpp b/Clients/BonjourExample/BonjourExample.cpp new file mode 100644 index 0000000..ec448f4 --- /dev/null +++ b/Clients/BonjourExample/BonjourExample.cpp @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: BonjourExample.cpp,v $ +Revision 1.1 2005/05/20 22:01:01 bradley +Bonjour for Windows example code to browse for HTTP services and deliver via Window messages. + +*/ + +#include "stdafx.h" + +#include +#include + +#include "dns_sd.h" + +// Constants + +#define BONJOUR_EVENT ( WM_USER + 0x100 ) // Message sent to the Window when a Bonjour event occurs. + +// Prototypes + +static LRESULT CALLBACK WndProc( HWND inWindow, UINT inMsg, WPARAM inWParam, LPARAM inLParam ); + +static void DNSSD_API + BrowserCallBack( + DNSServiceRef inServiceRef, + DNSServiceFlags inFlags, + uint32_t inIFI, + DNSServiceErrorType inError, + const char * inName, + const char * inType, + const char * inDomain, + void * inContext ); + +// Globals + +DNSServiceRef gServiceRef = NULL; + +// Main entry point for application. + +int _tmain( int argc, _TCHAR *argv[] ) +{ + HINSTANCE instance; + WNDCLASSEX wcex; + HWND wind; + MSG msg; + DNSServiceErrorType err; + + (void) argc; // Unused + (void) argv; // Unused + + // Create the window. This window won't actually be shown, but it demonstrates how to use Bonjour + // with Windows GUI applications by having Bonjour events processed as messages to a Window. + + instance = GetModuleHandle( NULL ); + assert( instance ); + + wcex.cbSize = sizeof( wcex ); + wcex.style = 0; + wcex.lpfnWndProc = (WNDPROC) WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = instance; + wcex.hIcon = NULL; + wcex.hCursor = NULL; + wcex.hbrBackground = NULL; + wcex.lpszMenuName = NULL; + wcex.lpszClassName = TEXT( "BonjourExample" ); + wcex.hIconSm = NULL; + RegisterClassEx( &wcex ); + + wind = CreateWindow( wcex.lpszClassName, wcex.lpszClassName, 0, CW_USEDEFAULT, 0, CW_USEDEFAULT, + 0, NULL, NULL, instance, NULL ); + assert( wind ); + + // Start browsing for services and associate the Bonjour browser with our window using the + // WSAAsyncSelect mechanism. Whenever something related to the Bonjour browser occurs, our + // private Windows message will be sent to our window so we can give Bonjour a chance to + // process it. This allows Bonjour to avoid using a secondary thread (and all the issues + // with synchronization that would introduce), but still process everything asynchronously. + // This also simplifies app code because Bonjour will only run when we explicitly call it. + + err = DNSServiceBrowse( + &gServiceRef, // Receives reference to Bonjour browser object. + 0, // No flags. + kDNSServiceInterfaceIndexAny, // Browse on all network interfaces. + "_http._tcp", // Browse for HTTP service types. + NULL, // Browse on the default domain (e.g. local.). + BrowserCallBack, // Callback function when Bonjour events occur. + NULL ); // No callback context needed. + assert( err == kDNSServiceErr_NoError ); + + err = WSAAsyncSelect( (SOCKET) DNSServiceRefSockFD( gServiceRef ), wind, BONJOUR_EVENT, FD_READ | FD_CLOSE ); + assert( err == kDNSServiceErr_NoError ); + + fprintf( stderr, "Browsing for _http._tcp\n" ); + + // Main event loop for the application. All Bonjour events are dispatched while in this loop. + + while( GetMessage( &msg, NULL, 0, 0 ) ) + { + TranslateMessage( &msg ); + DispatchMessage( &msg ); + } + + // Clean up Bonjour. This is not strictly necessary since the normal process cleanup will + // close Bonjour socket(s) and release memory, but it's here to demonstrate how to do it. + + if( gServiceRef ) + { + WSAAsyncSelect( (SOCKET) DNSServiceRefSockFD( gServiceRef ), wind, BONJOUR_EVENT, 0 ); + DNSServiceRefDeallocate( gServiceRef ); + } + return( 0 ); +} + +// Callback for the Window. Bonjour events are delivered here. + +static LRESULT CALLBACK WndProc( HWND inWindow, UINT inMsg, WPARAM inWParam, LPARAM inLParam ) +{ + LRESULT result; + DNSServiceErrorType err; + + switch( inMsg ) + { + case BONJOUR_EVENT: + + // Process the Bonjour event. All Bonjour callbacks occur from within this function. + // If an error occurs while trying to process the result, it most likely means that + // something serious has gone wrong with Bonjour, such as it being terminated. This + // does not normally occur, but code should be prepared to handle it. If the error + // is ignored, the window will receive a constant stream of BONJOUR_EVENT messages so + // if an error occurs, we disassociate the DNSServiceRef from the window, deallocate + // it, and invalidate the reference so we don't try to deallocate it again on quit. + // Since this is a simple example app, if this error occurs, we quit the app too. + + err = DNSServiceProcessResult( gServiceRef ); + if( err != kDNSServiceErr_NoError ) + { + fprintf( stderr, "### ERROR! serious Bonjour error: %d\n", err ); + + WSAAsyncSelect( (SOCKET) DNSServiceRefSockFD( gServiceRef ), inWindow, BONJOUR_EVENT, 0 ); + DNSServiceRefDeallocate( gServiceRef ); + gServiceRef = NULL; + + PostQuitMessage( 0 ); + } + result = 0; + break; + + default: + result = DefWindowProc( inWindow, inMsg, inWParam, inLParam ); + break; + } + return( result ); +} + +// Callback for Bonjour browser events. Called when services are added or removed. + +static void DNSSD_API + BrowserCallBack( + DNSServiceRef inServiceRef, + DNSServiceFlags inFlags, + uint32_t inIFI, + DNSServiceErrorType inError, + const char * inName, + const char * inType, + const char * inDomain, + void * inContext ) +{ + (void) inServiceRef; // Unused + (void) inContext; // Unused + + if( inError == kDNSServiceErr_NoError ) + { + const char * action; + const char * more; + + if( inFlags & kDNSServiceFlagsAdd ) action = "ADD"; + else action = "RMV"; + if( inFlags & kDNSServiceFlagsMoreComing ) more = " (MORE)"; + else more = ""; + + fprintf( stderr, "%s %30s.%s%s on interface %d%s\n", action, inName, inType, inDomain, (int) inIFI, more ); + } + else + { + fprintf( stderr, "Bonjour browser error occurred: %d\n", inError ); + } +} diff --git a/Clients/BonjourExample/BonjourExample.sln b/Clients/BonjourExample/BonjourExample.sln new file mode 100644 index 0000000..fb80330 --- /dev/null +++ b/Clients/BonjourExample/BonjourExample.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BonjourExample", "BonjourExample.vcproj", "{0A842379-799E-414C-BF1F-BF11A8D3A8A8}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {0A842379-799E-414C-BF1F-BF11A8D3A8A8}.Debug.ActiveCfg = Debug|Win32 + {0A842379-799E-414C-BF1F-BF11A8D3A8A8}.Debug.Build.0 = Debug|Win32 + {0A842379-799E-414C-BF1F-BF11A8D3A8A8}.Release.ActiveCfg = Release|Win32 + {0A842379-799E-414C-BF1F-BF11A8D3A8A8}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/mDNSWindows/SystemServiceTest/Tool2002.vcproj b/Clients/BonjourExample/BonjourExample.vcproj similarity index 51% rename from mDNSWindows/SystemServiceTest/Tool2002.vcproj rename to Clients/BonjourExample/BonjourExample.vcproj index 0afc4b3..b58f3b1 100644 --- a/mDNSWindows/SystemServiceTest/Tool2002.vcproj +++ b/Clients/BonjourExample/BonjourExample.vcproj @@ -1,9 +1,9 @@ - + + CharacterSet="1"> + DebugInformationFormat="4"/> + + + + CharacterSet="1"> + CompileAs="2"/> + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + RelativePath=".\BonjourExample.cpp"> + RelativePath="..\..\mDNSWindows\Dll\release\dnssd.lib"> + RelativePath=".\stdafx.cpp"> + + + + + + + RelativePath=".\stdafx.h"> diff --git a/Clients/BonjourExample/stdafx.cpp b/Clients/BonjourExample/stdafx.cpp new file mode 100644 index 0000000..2b2483e --- /dev/null +++ b/Clients/BonjourExample/stdafx.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: stdafx.cpp,v $ +Revision 1.1 2005/05/20 22:01:02 bradley +Bonjour for Windows example code to browse for HTTP services and deliver via Window messages. + +*/ + +// Standard source file to build the pre-compiled header. + +#include "stdafx.h" diff --git a/Clients/BonjourExample/stdafx.h b/Clients/BonjourExample/stdafx.h new file mode 100644 index 0000000..6ded040 --- /dev/null +++ b/Clients/BonjourExample/stdafx.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: stdafx.h,v $ +Revision 1.1 2005/05/20 22:01:02 bradley +Bonjour for Windows example code to browse for HTTP services and deliver via Window messages. + +*/ + +// Standard Windows pre-compiled header file. + +#pragma once + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#include +#include + +#include +#include +#include +#include diff --git a/Clients/DNS-SD.VisualStudio/dns-sd.vcproj b/Clients/DNS-SD.VisualStudio/dns-sd.vcproj index dda633b..4c3a515 100755 --- a/Clients/DNS-SD.VisualStudio/dns-sd.vcproj +++ b/Clients/DNS-SD.VisualStudio/dns-sd.vcproj @@ -1,7 +1,7 @@ - + @@ -20,7 +20,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="../../mDNSShared" - PreprocessorDefinitions="WIN32;_WIN32;_DEBUG;_CONSOLE;NOT_HAVE_GETOPT;NOT_HAVE_SETLINEBUF" + PreprocessorDefinitions="WIN32;_WIN32;_DEBUG;_CONSOLE;NOT_HAVE_GETOPT;NOT_HAVE_SETLINEBUF;WIN32_LEAN_AND_MEAN" MinimalRebuild="TRUE" BasicRuntimeChecks="3" RuntimeLibrary="5" @@ -52,12 +52,18 @@ AdditionalIncludeDirectories="../../mDNSWindows"/> + + + @@ -67,7 +73,7 @@ InlineFunctionExpansion="1" OmitFramePointers="TRUE" AdditionalIncludeDirectories="../../mDNSShared" - PreprocessorDefinitions="WIN32;_WIN32;NDEBUG;_CONSOLE;NOT_HAVE_GETOPT;NOT_HAVE_SETLINEBUF" + PreprocessorDefinitions="WIN32;_WIN32;NDEBUG;_CONSOLE;NOT_HAVE_GETOPT;NOT_HAVE_SETLINEBUF;WIN32_LEAN_AND_MEAN" StringPooling="TRUE" RuntimeLibrary="4" EnableFunctionLevelLinking="TRUE" @@ -100,10 +106,18 @@ AdditionalIncludeDirectories="../../mDNSWindows"/> + + + + + Use the product icon instead of globe icon for 'About' link. + +Revision 1.20 2005/03/18 02:43:02 shersche + Use standard IE website icon for 'About Bonjour', only using globe icon if standard icon cannot be loaded + +Revision 1.19 2005/03/16 03:46:27 shersche + Use Bonjour icon for all discovered sites + Revision 1.18 2005/02/26 01:24:05 shersche Remove display lines in tree control @@ -143,6 +152,10 @@ static char THIS_FILE[] = __FILE__; #define kTXTRecordKeyPath "path" +// IE Icon resource + +#define kIEIconResource 32529 + #if 0 #pragma mark == Prototypes == @@ -205,6 +218,7 @@ int ExplorerBarWindow::OnCreate( LPCREATESTRUCT inCreateStruct ) { AFX_MANAGE_STATE( AfxGetStaticModuleState() ); + HINSTANCE module = NULL; OSStatus err; CRect rect; CBitmap bitmap; @@ -231,7 +245,7 @@ int ExplorerBarWindow::OnCreate( LPCREATESTRUCT inCreateStruct ) mServiceHandlers.Add( e ); s.LoadString( IDS_ABOUT ); - m_about = mTree.InsertItem( s, 1, 1 ); + m_about = mTree.InsertItem( s, 0, 0 ); err = DNSServiceBrowse( &e->ref, 0, 0, e->type, NULL, BrowseCallBack, e ); require_noerr( err, exit ); @@ -241,17 +255,22 @@ int ExplorerBarWindow::OnCreate( LPCREATESTRUCT inCreateStruct ) m_serviceRefs.push_back(e->ref); - m_imageList.Create( 16, 16, ILC_COLORDDB, 2, 0); - bitmap.Attach( ::LoadBitmap( GetNonLocalizedResources(), MAKEINTRESOURCE( IDB_GLOBE ) ) ); - m_imageList.Add( &bitmap, (CBitmap*) NULL ); - bitmap.Detach(); + m_imageList.Create( 16, 16, ILC_MASK | ILC_COLOR16, 2, 0); + bitmap.Attach( ::LoadBitmap( GetNonLocalizedResources(), MAKEINTRESOURCE( IDB_LOGO ) ) ); m_imageList.Add( &bitmap, (CBitmap*) NULL ); + bitmap.Detach(); mTree.SetImageList(&m_imageList, TVSIL_NORMAL); exit: + if ( module ) + { + FreeLibrary( module ); + module = NULL; + } + // Cannot talk to the mDNSResponder service. Show the error message and exit (with kNoErr so they can see it). if ( err ) { diff --git a/Clients/ExplorerPlugin/ExplorerPlugin.cpp b/Clients/ExplorerPlugin/ExplorerPlugin.cpp index 766c173..34b981e 100644 --- a/Clients/ExplorerPlugin/ExplorerPlugin.cpp +++ b/Clients/ExplorerPlugin/ExplorerPlugin.cpp @@ -23,6 +23,9 @@ Change History (most recent first): $Log: ExplorerPlugin.cpp,v $ +Revision 1.8 2005/06/30 18:01:54 shersche + Cause IE to rebuild cache so we don't have to reboot following an install. + Revision 1.7 2005/02/23 02:00:45 shersche Delete all the registry entries when component is unregistered @@ -125,6 +128,14 @@ GetLocalizedResources() return g_localizedResources; } +// This is the class GUID for an undocumented hook into IE that will allow us to register +// and have IE notice our new ExplorerBar without rebooting. +// {8C7461EF-2B13-11d2-BE35-3078302C2030} + +DEFINE_GUID(CLSID_CompCatCacheDaemon, +0x8C7461EF, 0x2b13, 0x11d2, 0xbe, 0x35, 0x30, 0x78, 0x30, 0x2c, 0x20, 0x30); + + #if 0 #pragma mark == Globals == #endif @@ -248,9 +259,10 @@ exit: STDAPI DllRegisterServer( void ) { - HRESULT err; - BOOL ok; - CString s; + IRunnableTask * pTask = NULL; + HRESULT err; + BOOL ok; + CString s; dlog( kDebugLevelTrace, "DllRegisterServer\n" ); @@ -262,7 +274,16 @@ STDAPI DllRegisterServer( void ) err = RegisterCOMCategory( CLSID_ExplorerBar, CATID_InfoBand, TRUE ); require_noerr( err, exit ); - + + // Clear IE cache so it will rebuild the cache when it runs next. This + // will allow us to install and not reboot + + err = CoCreateInstance(CLSID_CompCatCacheDaemon, NULL, CLSCTX_INPROC, IID_IRunnableTask, (void**) &pTask); + require_noerr( err, exit ); + + pTask->Run(); + pTask->Release(); + exit: return( err ); } diff --git a/Clients/ExplorerPlugin/ExplorerPlugin.vcproj b/Clients/ExplorerPlugin/ExplorerPlugin.vcproj index c0f9af3..4d875a8 100644 --- a/Clients/ExplorerPlugin/ExplorerPlugin.vcproj +++ b/Clients/ExplorerPlugin/ExplorerPlugin.vcproj @@ -1,7 +1,7 @@ - + + + + + + + + + + + RelativePath="..\..\mDNSWindows\isocode.h"> + RelativePath="..\..\mDNSWindows\loclibrary.c"> + RelativePath="..\..\mDNSWindows\loclibrary.h"> + RelativePath="..\..\mDNSWindows\WinServices.cpp"> + RelativePath="..\..\mDNSWindows\WinServices.h"> + RelativePath="resource_dll.h"> + RelativePath="StdAfx.h"> + + CommandLine="if not exist Debug\ExplorerPlugin.Resources mkdir Debug\ExplorerPlugin.Resources +if not exist Debug\ExplorerPlugin.Resources\en.lproj mkdir Debug\ExplorerPlugin.Resources\en.lproj +"/> + + + + CommandLine="if not exist Release mkdir Release +if not exist "Release\ExplorerPlugin.Resources" mkdir "Release\ExplorerPlugin.Resources" +if not exist "Release\ExplorerPlugin.Resources\en.lproj" mkdir "Release\ExplorerPlugin.Resources\en.lproj" +"/> + + + + + + + CommandLine="if not exist Debug\ExplorerPlugin.Resources mkdir Debug\ExplorerPlugin.Resources"/> + + + + CommandLine="if not exist Release mkdir Release +if not exist "Release\ExplorerPlugin.Resources" mkdir "Release\ExplorerPlugin.Resources" +"/> + + + + + - - + RelativePath="res\about.bmp"> - - @@ -193,14 +198,20 @@ if not exist "Release\Root\Program Files\Bonjour\Resources\ExplorerPlugin.d + + + + + RelativePath="Web.ico"> diff --git a/Clients/ExplorerPlugin/StdAfx.h b/Clients/ExplorerPlugin/StdAfx.h index ca625a1..407ff3c 100644 --- a/Clients/ExplorerPlugin/StdAfx.h +++ b/Clients/ExplorerPlugin/StdAfx.h @@ -23,6 +23,9 @@ Change History (most recent first): $Log: StdAfx.h,v $ +Revision 1.3 2005/10/19 19:50:34 herscher +Workaround a bug in the latest Microsoft Platform SDK when compiling C++ files that include (directly or indirectly) + Revision 1.2 2004/07/13 21:24:21 rpantos Fix for . @@ -43,6 +46,10 @@ Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within #define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers #endif +#if !defined(_WSPIAPI_COUNTOF) +# 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 diff --git a/Clients/ExplorerPlugin/res/globe.bmp b/Clients/ExplorerPlugin/res/globe.bmp deleted file mode 100755 index af43b4e5ecf8e2efa6501ad1c15551553243d8f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 824 zcmbV}?@JSL9LKME(1V^tk9yYAh@J#t5h2vG%)p{Y1xXZQL?sxBp<7CzZl<2UHgHbe z{ISIS(0TLb>9$+*mOAE!yMxo5=X7f>ox9Du;ca+*@nnC1AKsrgA3opL=felS51-t< z0G&h;@arHipXNE}>Pi1Rc)xN<%992#@l3I{Ne4+EhFeX@fEiI+XVr*ZW1kKsx$-ln-G0hlgN4! zCW+T;zc~BLNXsW?PM&c8ND=L6FAOlC9G~R(!t_k-!pLjAu1>nYQ(o*;6)lgv2$?_7 z)74?LhQaES8+eXq<3UqsvT9}gja&Oz-CQd1t)K5;ii;lggQcOTena^tp^dIilT%d` zQWrG7Umkf$Xi6OLjn4Y}65rKSIbI84N{ASGz8@vGS8XKLw1#&t43rZ6kKC$zj`nL4 za;U4NQsQ;P8u%47^%V-u!5q80=`*JO(j=yEb>4P$+?-WF$Tz6F0@9E5NxXlJmQ|El zT8e2xm*o$Rcvj%kz?lHzx72uso|(VjYW!FdTtEHdbT*56aAm{0#Tl4O#f5BI$Yy}p ze+=c(ELdBV4R39~TmX~y15a|?wA + + + + + + + + + + + + + + + + + + + diff --git a/Clients/Java/nmakefile b/Clients/Java/nmakefile index 6fa95cb..4c01633 100644 --- a/Clients/Java/nmakefile +++ b/Clients/Java/nmakefile @@ -35,7 +35,7 @@ ############################################################################ -JDK = \javasdk +JDK = $(JAVA_HOME) CP = copy RM = del /Q diff --git a/Clients/Makefile b/Clients/Makefile index 709bd2b..b79bf6f 100755 --- a/Clients/Makefile +++ b/Clients/Makefile @@ -20,6 +20,9 @@ # @APPLE_LICENSE_HEADER_END@ # # $Log: Makefile,v $ +# Revision 1.7 2006/01/06 01:06:17 cheshire +# Compile library and client programs in one pass +# # Revision 1.6 2004/09/24 21:15:26 cheshire # Library "libmdns" misnamed; should be "libdns_sd" # @@ -48,12 +51,12 @@ ############################################################################# - -# If library /usr/lib/libdns_sd.* exists, then link it -ifneq "$(wildcard /usr/lib/libdns_sd.*)" "" -LIBS = -ldns_sd -else +# On OS X the dns_sd library functions are included in libSystem, which is implicitly linked with every executable +# If /usr/lib/libSystem.dylib exists, then we're on OS X, so we don't need also to link the "dns_sd" shared library +ifneq "$(wildcard /usr/lib/libSystem.dylib)" "" LIBS = +else +LIBS = -L../mDNSPosix/build/prod/ -ldns_sd endif targets: build/dns-sd diff --git a/Clients/PrinterSetupWizard/FirstPage.cpp b/Clients/PrinterSetupWizard/FirstPage.cpp index 530b26c..b5131da 100644 --- a/Clients/PrinterSetupWizard/FirstPage.cpp +++ b/Clients/PrinterSetupWizard/FirstPage.cpp @@ -23,6 +23,12 @@ Change History (most recent first): $Log: FirstPage.cpp,v $ +Revision 1.5 2005/07/07 17:53:20 shersche +Fix problems associated with the CUPS printer workaround fix. + +Revision 1.4 2005/03/16 01:41:29 shersche + Remove info icon from first page + Revision 1.3 2005/01/25 08:58:08 shersche Load icons at run-time from resource DLLs Bug #: 3911084 @@ -39,6 +45,7 @@ First checked in #include "stdafx.h" #include "PrinterSetupWizardApp.h" +#include "PrinterSetupWizardSheet.h" #include "FirstPage.h" #include @@ -77,33 +84,41 @@ void CFirstPage::DoDataExchange(CDataExchange* pDX) BOOL CFirstPage::OnSetActive() { - static bool firstTime = true; - - if ( firstTime ) - { - CStatic * image = (CStatic*) GetDlgItem( IDC_INFO ); - check( image ); - - image->SetIcon( LoadIcon( GetNonLocalizedResources(), MAKEINTRESOURCE( IDI_INFO ) ) ); - - firstTime = false; - } + CPrinterSetupWizardSheet * psheet; + CString greetingText; - CPropertySheet* psheet = (CPropertySheet*) GetParent(); + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); psheet->SetWizardButtons(PSWIZB_NEXT); m_greeting.SetFont(&m_largeFont); - CString greetingText; - greetingText.LoadString(IDS_GREETING); m_greeting.SetWindowText(greetingText); +exit: + return CPropertyPage::OnSetActive(); } +BOOL +CFirstPage::OnKillActive() +{ + CPrinterSetupWizardSheet * psheet; + + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + psheet->SetLastPage(this); + +exit: + + return CPropertyPage::OnKillActive(); +} + + BEGIN_MESSAGE_MAP(CFirstPage, CPropertyPage) END_MESSAGE_MAP() diff --git a/Clients/PrinterSetupWizard/FirstPage.h b/Clients/PrinterSetupWizard/FirstPage.h index 5c23bdf..a798eb0 100644 --- a/Clients/PrinterSetupWizard/FirstPage.h +++ b/Clients/PrinterSetupWizard/FirstPage.h @@ -23,6 +23,9 @@ Change History (most recent first): $Log: FirstPage.h,v $ +Revision 1.2 2005/07/07 17:53:20 shersche +Fix problems associated with the CUPS printer workaround fix. + Revision 1.1 2004/06/18 04:36:57 rpantos First checked in @@ -49,6 +52,7 @@ public: protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support virtual BOOL OnSetActive(); + virtual BOOL OnKillActive(); DECLARE_MESSAGE_MAP() diff --git a/Clients/PrinterSetupWizard/FourthPage.cpp b/Clients/PrinterSetupWizard/FourthPage.cpp index a2a7a50..876e28b 100644 --- a/Clients/PrinterSetupWizard/FourthPage.cpp +++ b/Clients/PrinterSetupWizard/FourthPage.cpp @@ -23,6 +23,9 @@ Change History (most recent first): $Log: FourthPage.cpp,v $ +Revision 1.7 2005/07/07 17:53:20 shersche +Fix problems associated with the CUPS printer workaround fix. + Revision 1.6 2005/02/08 21:45:06 shersche Default to Generic PostScript or PCL if unable to match driver @@ -150,3 +153,19 @@ exit: return CPropertyPage::OnSetActive(); } + + +BOOL +CFourthPage::OnKillActive() +{ + CPrinterSetupWizardSheet * psheet; + + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + psheet->SetLastPage(this); + +exit: + + return CPropertyPage::OnKillActive(); +} diff --git a/Clients/PrinterSetupWizard/FourthPage.h b/Clients/PrinterSetupWizard/FourthPage.h index b488e20..fca8cd1 100644 --- a/Clients/PrinterSetupWizard/FourthPage.h +++ b/Clients/PrinterSetupWizard/FourthPage.h @@ -23,6 +23,9 @@ Change History (most recent first): $Log: FourthPage.h,v $ +Revision 1.3 2005/07/07 17:53:20 shersche +Fix problems associated with the CUPS printer workaround fix. + Revision 1.2 2005/01/06 08:17:08 shersche Display the selected protocol ("Raw", "LPR", "IPP") rather than the port name @@ -50,6 +53,7 @@ public: enum { IDD = IDD_FOURTH_PAGE }; virtual BOOL OnSetActive(); + virtual BOOL OnKillActive(); protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizard.vcproj b/Clients/PrinterSetupWizard/PrinterSetupWizard.vcproj index 4bff36a..c407853 100644 --- a/Clients/PrinterSetupWizard/PrinterSetupWizard.vcproj +++ b/Clients/PrinterSetupWizard/PrinterSetupWizard.vcproj @@ -1,7 +1,7 @@ - + @@ -20,7 +20,7 @@ + + + + + + + + + RelativePath="stdafx.cpp"> + RelativePath=".\StdioFileEx.cpp"> + RelativePath="ThirdPage.cpp"> + RelativePath="resource.h"> + RelativePath="resource_exe.h"> + RelativePath="SecondPage.h"> + RelativePath="stdafx.h"> + RelativePath=".\StdioFileEx.h"> + RelativePath="ThirdPage.h"> + RelativePath=".\UtilTypes.h"> + + + + @@ -215,12 +235,6 @@ - - - - @@ -237,12 +251,6 @@ - - - - @@ -261,6 +269,12 @@ + + + + diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizardApp.cpp b/Clients/PrinterSetupWizard/PrinterSetupWizardApp.cpp index 08a0505..7b65443 100644 --- a/Clients/PrinterSetupWizard/PrinterSetupWizardApp.cpp +++ b/Clients/PrinterSetupWizard/PrinterSetupWizardApp.cpp @@ -23,6 +23,9 @@ Change History (most recent first): $Log: PrinterSetupWizardApp.cpp,v $ +Revision 1.8 2005/04/13 17:43:39 shersche + Change "PrinterWizard.dll" to "PrinterWizardResources.dll" + Revision 1.7 2005/02/15 07:50:09 shersche Update name @@ -123,7 +126,7 @@ BOOL CPrinterSetupWizardApp::InitInstance() // Load Resources - res = PathForResource( NULL, L"PrinterWizard.dll", resource, MAX_PATH ); + res = PathForResource( NULL, L"PrinterWizardResources.dll", resource, MAX_PATH ); err = translate_errno( res != 0, kUnknownErr, kUnknownErr ); require_noerr( err, exit ); diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizardLocRes.rc b/Clients/PrinterSetupWizard/PrinterSetupWizardLocRes.rc index 7a2842f..56319c3 100755 --- a/Clients/PrinterSetupWizard/PrinterSetupWizardLocRes.rc +++ b/Clients/PrinterSetupWizard/PrinterSetupWizardLocRes.rc @@ -79,11 +79,11 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US IDD_ABOUTBOX DIALOGEX 0, 0, 235, 55 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "About Printer Setup Wizard" +CAPTION "About Printer Wizard" FONT 8, "MS Shell Dlg", 0, 0, 0x1 BEGIN ICON 128,IDC_STATIC,11,17,20,20 - LTEXT "Printer Setup Wizard Version 1.0",IDC_STATIC,40,10,119, + LTEXT "Printer Wizard Version 1.0",IDC_STATIC,40,10,119, 8,SS_NOPREFIX LTEXT "Copyright (C) 2002",IDC_STATIC,40,25,119,8 DEFPUSHBUTTON "OK",IDOK,178,7,50,16,WS_GROUP @@ -93,7 +93,7 @@ IDD_PRINTERSETUPWIZARD_DIALOG DIALOGEX 0, 0, 320, 200 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_APPWINDOW -CAPTION "Printer Setup Wizard" +CAPTION "Bonjour Printer Wizard" FONT 8, "MS Shell Dlg", 0, 0, 0x1 BEGIN DEFPUSHBUTTON "OK",IDOK,263,7,50,16 @@ -123,12 +123,11 @@ STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION CAPTION "Bonjour Printer Wizard" FONT 8, "MS Shell Dlg", 0, 0, 0x0 BEGIN - LTEXT "Welcome to the Bonjour Printer Setup Wizard", + LTEXT "Welcome to the Bonjour Printer Wizard", IDC_GREETING,114,7,171,46 - LTEXT "Click next to continue.",IDC_STATIC,115,188,143,8 + LTEXT "To continue, click Next.",IDC_STATIC,115,188,143,8 LTEXT "This wizard helps you connect to a shared printer using Bonjour. Make sure your printer is turned on and connected to your network.", - IDC_STATIC,146,60,139,62 - ICON "",IDC_INFO,118,60,20,20,SS_REALSIZEIMAGE + IDC_STATIC,114,60,171,62 END IDD_THIRD_PAGE DIALOGEX 0, 0, 290, 154 @@ -143,9 +142,9 @@ BEGIN CONTROL "",IDC_PRINTER_MANUFACTURER,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_SORTASCENDING | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,2,58,105,76 - ICON 1017,1,0,0,20,27 + ICON 1017,1,3,5,20,27 LTEXT "",IDC_PRINTER_NAME,40,5,173,8 - LTEXT "The Bonjour Printer Wizard has auto-selected the following printer settings. Click 'Next' to continue installing this printer.", + LTEXT "The Bonjour Printer Wizard has auto-selected the following printer settings. To continue installing this printer, click Next.", IDC_PRINTER_SELECTION_TEXT,40,18,243,33 CONTROL "Use this printer as the default printer", IDC_DEFAULT_PRINTER,"Button",BS_AUTOCHECKBOX | @@ -160,20 +159,20 @@ FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN LTEXT "Completing the Bonjour Printer Wizard",IDC_GOODBYE, 116,7,171,27 - LTEXT "You have successfully completed the Bonjour Printer Wizard. The printer has the following settings:", - IDC_STATIC,116,42,158,31 + LTEXT "You are ready to complete the Bonjour Printer Wizard. The printer has the following settings:", + IDC_STATIC,116,42,171,31 LTEXT "Name:",IDC_STATIC,116,78,22,8 LTEXT "Manufacturer:",IDC_STATIC,116,91,47,8 LTEXT "Model:",IDC_STATIC,116,104,22,8 LTEXT "Protocol:",IDC_STATIC,116,117,38,8 LTEXT "Default:",IDC_STATIC,116,130,27,8 - LTEXT "",IDC_PRINTER_NAME,172,78,113,8 + LTEXT "",IDC_PRINTER_NAME,172,78,113,8,SS_ENDELLIPSIS LTEXT "",IDC_PRINTER_MANUFACTURER,172,91,113,8 LTEXT "",IDC_PRINTER_MODEL,172,104,113,8 LTEXT "",IDC_PRINTER_PROTOCOL,172,117,113,8 LTEXT "",IDC_PRINTER_DEFAULT,172,130,113,8 - LTEXT "To close this wizard, click Finish.",IDC_STATIC,116,187, - 103,8 + LTEXT "To complete the installation, click Finish.",IDC_STATIC,116,187, + 171,8 END IDD_DIALOG1 DIALOGEX 0, 0, 265, 130 @@ -236,15 +235,15 @@ END STRINGTABLE BEGIN - IDS_ABOUTBOX "&About Printer Setup Wizard..." - IDS_GOODBYE "Completing the Bonjour Printer Setup Wizard." - IDS_GREETING "Welcome to the Bonjour Printer Setup Wizard" + IDS_ABOUTBOX "&About Bonjour Printer Wizard..." + IDS_GOODBYE "Completing the Bonjour Printer Wizard." + IDS_GREETING "Welcome to the Bonjour Printer Wizard" IDS_BROWSE_TITLE "Browse for Bonjour Printers" IDS_BROWSE_SUBTITLE "Select the printer you want to use from the list below." IDS_CAPTION "Bonjour Printer Wizard" - IDS_GOODBYE_GOOD1 "You have successfully completed the Bonjour Printer Wizard. The printer has the following settings:" + IDS_GOODBYE_GOOD1 "You are ready to complete the Bonjour Printer Wizard. The printer has the following settings:" IDS_SEARCHING "Searching for printers..." - IDS_GOODBYTE_GOOD2 "To close this wizard, click Finish." + IDS_GOODBYTE_GOOD2 "To complete the installation, click Finish." IDS_INSTALL_TITLE "Install Bonjour Printer" IDS_INSTALL_SUBTITLE "The manufacturer and model determine which printer software to use." END diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizardLocRes.vcproj b/Clients/PrinterSetupWizard/PrinterSetupWizardLocRes.vcproj index c97e842..b961304 100755 --- a/Clients/PrinterSetupWizard/PrinterSetupWizardLocRes.vcproj +++ b/Clients/PrinterSetupWizard/PrinterSetupWizardLocRes.vcproj @@ -1,7 +1,7 @@ - + @@ -12,7 +12,7 @@ + + + + CommandLine="if not exist Release mkdir Release +if not exist "Release\PrinterWizard.Resources" mkdir "Release\PrinterWizard.Resources" +if not exist "Release\PrinterWizard.Resources\en.lproj" mkdir "Release\PrinterWizard.Resources\en.lproj" +"/> + + + + + + @@ -12,7 +12,7 @@ + CommandLine="if not exist Debug\PrinterWizard.Resources mkdir Debug\PrinterWizard.Resources"/> + + + + CommandLine="if not exist Release mkdir Release +if not exist "Release\PrinterWizard.Resources" mkdir "Release\PrinterWizard.Resources" +"/> + + + + + + + + + @@ -172,12 +189,6 @@ if not exist "Release\Root\Program Files\Bonjour\Resources\PrinterWizard.ex - - - - diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp b/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp index 75cda89..6c0c9f3 100644 --- a/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp +++ b/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp @@ -23,6 +23,21 @@ Change History (most recent first): $Log: PrinterSetupWizardSheet.cpp,v $ +Revision 1.34 2005/10/05 17:32:51 herscher + Use a case insensitive compare operation to check whether a printer with the same name has already been installed. + +Revision 1.33 2005/07/11 20:17:15 shersche + UI fixes associated with CUPS printer workaround fix. + +Revision 1.32 2005/07/07 17:53:20 shersche +Fix problems associated with the CUPS printer workaround fix. + +Revision 1.31 2005/06/30 18:02:54 shersche + Workaround for Mac OS X Printer Sharing bug + +Revision 1.30 2005/04/13 17:46:22 shersche + Generic PCL not selected when printers advertise multiple text records + Revision 1.29 2005/02/14 20:48:37 shersche Default pdl key to "application/postscript" @@ -163,7 +178,8 @@ CPrinterSetupWizardSheet::CPrinterSetupWizardSheet(UINT nIDCaption, CWnd* pParen m_driverThreadFinished( false ), m_pdlBrowser( NULL ), m_ippBrowser( NULL ), - m_lprBrowser( NULL ) + m_lprBrowser( NULL ), + m_lastPage( NULL ) { m_arrow = LoadCursor(0, IDC_ARROW); m_wait = LoadCursor(0, IDC_APPSTARTING); @@ -245,7 +261,7 @@ CPrinterSetupWizardSheet::LoadPrinterNames() { PRINTER_INFO_4 * lppi4 = (PRINTER_INFO_4*) (buffer + index * sizeof(PRINTER_INFO_4)); - m_printerNames[lppi4->pPrinterName] = lppi4->pPrinterName; + m_printerNames.push_back( lppi4->pPrinterName ); } } @@ -289,6 +305,7 @@ CPrinterSetupWizardSheet::InstallPrinter(Printer * printer) // // if the driver isn't installed, then install it // + if ( !printer->driverInstalled ) { DWORD dwResult; @@ -398,7 +415,7 @@ CPrinterSetupWizardSheet::InstallPrinterPDLAndLPR(Printer * printer, Service * s ok = OpenPrinter(L",XcvMonitor Standard TCP/IP Port", &hXcv, &printerDefaults); err = translate_errno( ok, errno_compat(), kUnknownErr ); require_noerr( err, exit ); - + // // BUGBUG: MSDN said this is not required, but my experience shows it is required // @@ -429,7 +446,7 @@ CPrinterSetupWizardSheet::InstallPrinterPDLAndLPR(Printer * printer, Service * s wcscpy(portData.sztQueue, q->name); wcscpy(portData.sztIPAddress, service->hostname); wcscpy(portData.sztHostAddress, service->hostname); - + ok = XcvData(hXcv, L"AddPort", (PBYTE) &portData, sizeof(PORT_DATA_1), pOutputData, cbInputData, &cbOutputNeeded, &dwStatus); err = translate_errno( ok, errno_compat(), kUnknownErr ); require_noerr( err, exit ); @@ -445,7 +462,7 @@ CPrinterSetupWizardSheet::InstallPrinterPDLAndLPR(Printer * printer, Service * s pInfo.pPortName = printer->portName.GetBuffer(); pInfo.pDriverName = printer->modelName.GetBuffer(); pInfo.pComment = printer->displayModelName.GetBuffer(); - pInfo.pLocation = service->location.GetBuffer(); + pInfo.pLocation = q->location.GetBuffer(); pInfo.pDevMode = NULL; pInfo.pDevMode = NULL; pInfo.pSepFile = L""; @@ -489,9 +506,12 @@ CPrinterSetupWizardSheet::InstallPrinterIPP(Printer * printer, Service * service { DEBUG_UNUSED( service ); + Queue * q = service->SelectedQueue(); HANDLE hPrinter = NULL; PRINTER_INFO_2 pInfo; OSStatus err; + + check( q ); // // add the printer @@ -502,7 +522,7 @@ CPrinterSetupWizardSheet::InstallPrinterIPP(Printer * printer, Service * service pInfo.pPortName = printer->portName.GetBuffer(); pInfo.pDriverName = printer->modelName.GetBuffer(); pInfo.pPrintProcessor = L"winprint"; - pInfo.pLocation = service->location.GetBuffer(); + pInfo.pLocation = q->location.GetBuffer(); pInfo.pComment = printer->displayModelName.GetBuffer(); pInfo.Attributes = PRINTER_ATTRIBUTE_NETWORK | PRINTER_ATTRIBUTE_LOCAL; @@ -886,8 +906,6 @@ CPrinterSetupWizardSheet::OnResolve( CPrinterSetupWizardSheet * self; Service * service; Queue * q; - uint32_t qpriority = kDefaultPriority; - CString qname; int idx; OSStatus err; @@ -925,13 +943,6 @@ CPrinterSetupWizardSheet::OnResolve( // service->portNumber = ntohs(inPort); - // - // parse the text record. - // - - err = self->ParseTextRecord( service, inTXTSize, inTXT, qname, qpriority ); - require_noerr( err, exit ); - if ( service->qtotal == 1 ) { // @@ -948,10 +959,13 @@ CPrinterSetupWizardSheet::OnResolve( require_action( q, exit, err = E_OUTOFMEMORY ); + // + // parse the text record. + // + + err = self->ParseTextRecord( service, q, inTXTSize, inTXT ); + require_noerr( err, exit ); - q->name = qname; - q->priority = qpriority; - service->queues.push_back( q ); // @@ -1033,7 +1047,7 @@ CPrinterSetupWizardSheet::OnQuery( require_action( q, exit, err = E_OUTOFMEMORY ); - err = service->printer->window->ParseTextRecord( service, inRDLen, inTXT, q->name, q->priority ); + err = service->printer->window->ParseTextRecord( service, q, inRDLen, inTXT ); require_noerr( err, exit ); // @@ -1121,9 +1135,18 @@ CPrinterSetupWizardSheet::OnAddPrinter( for (;;) { - CPrinterSetupWizardSheet::PrinterNameMap::iterator it; + CPrinterSetupWizardSheet::PrinterNames::iterator it; + + // Don't use find to do comparisons because we need to + // do a case insensitive string comparison - it = m_printerNames.find(printer->actualName); + for ( it = m_printerNames.begin(); it != m_printerNames.end(); it++ ) + { + if ( (*it).CompareNoCase( printer->actualName ) == 0 ) + { + break; + } + } if (it != m_printerNames.end()) { @@ -1267,7 +1290,7 @@ CPrinterSetupWizardSheet::OnResolveService( Service * service ) { // Make sure that the active page is page 2 - check( GetActivePage() == &m_pgSecond ); + require_quiet( GetActivePage() == &m_pgSecond, exit ); if ( !--service->printer->resolving ) { @@ -1289,6 +1312,10 @@ CPrinterSetupWizardSheet::OnResolveService( Service * service ) m_pgSecond.OnResolveService( service ); } + +exit: + + return; } @@ -1512,8 +1539,11 @@ exit: OSStatus -CPrinterSetupWizardSheet::ParseTextRecord( Service * service, uint16_t inTXTSize, const char * inTXT, CString & qname, uint32_t & qpriority ) +CPrinterSetupWizardSheet::ParseTextRecord( Service * service, Queue * q, uint16_t inTXTSize, const char * inTXT ) { + check( service ); + check( q ); + // Use TXTRecord APIs declared in dns_sd.h bool qtotalDefined = false; @@ -1524,11 +1554,11 @@ CPrinterSetupWizardSheet::ParseTextRecord( Service * service, uint16_t inTXTSize // Default to queue "lp" - qname = L"lp"; + q->name = L"lp"; // Default pdl key to be "application/postscript" - service->pdl = L"application/postscript"; + q->pdl = L"application/postscript"; if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "rp", &len ) ) != NULL ) { @@ -1537,7 +1567,7 @@ CPrinterSetupWizardSheet::ParseTextRecord( Service * service, uint16_t inTXTSize memcpy( buf, val, len ); buf[len] = '\0'; - err = UTF8StringToStringObject( buf, qname ); + err = UTF8StringToStringObject( buf, q->name ); require_noerr( err, exit ); } @@ -1548,7 +1578,7 @@ CPrinterSetupWizardSheet::ParseTextRecord( Service * service, uint16_t inTXTSize memcpy( buf, val, len ); buf[len] = '\0'; - err = UTF8StringToStringObject( buf, service->pdl ); + err = UTF8StringToStringObject( buf, q->pdl ); require_noerr( err, exit ); } @@ -1560,7 +1590,7 @@ CPrinterSetupWizardSheet::ParseTextRecord( Service * service, uint16_t inTXTSize memcpy( buf, val, len ); buf[len] = '\0'; - err = UTF8StringToStringObject( buf, service->usb_MFG ); + err = UTF8StringToStringObject( buf, q->usb_MFG ); require_noerr( err, exit ); } @@ -1572,7 +1602,7 @@ CPrinterSetupWizardSheet::ParseTextRecord( Service * service, uint16_t inTXTSize memcpy( buf, val, len ); buf[len] = '\0'; - err = UTF8StringToStringObject( buf, service->usb_MDL ); + err = UTF8StringToStringObject( buf, q->usb_MDL ); require_noerr( err, exit ); } @@ -1583,7 +1613,7 @@ CPrinterSetupWizardSheet::ParseTextRecord( Service * service, uint16_t inTXTSize memcpy( buf, val, len ); buf[len] = '\0'; - err = UTF8StringToStringObject( buf, service->description ); + err = UTF8StringToStringObject( buf, q->description ); require_noerr( err, exit ); } @@ -1594,7 +1624,7 @@ CPrinterSetupWizardSheet::ParseTextRecord( Service * service, uint16_t inTXTSize memcpy( buf, val, len ); buf[len] = '\0'; - err = UTF8StringToStringObject( buf, service->product ); + err = UTF8StringToStringObject( buf, q->product ); require_noerr( err, exit ); } @@ -1605,7 +1635,7 @@ CPrinterSetupWizardSheet::ParseTextRecord( Service * service, uint16_t inTXTSize memcpy( buf, val, len ); buf[len] = '\0'; - err = UTF8StringToStringObject( buf, service->location ); + err = UTF8StringToStringObject( buf, q->location ); require_noerr( err, exit ); } @@ -1627,20 +1657,27 @@ CPrinterSetupWizardSheet::ParseTextRecord( Service * service, uint16_t inTXTSize memcpy( buf, val, len ); buf[len] = '\0'; - qpriority = atoi( buf ); + q->priority = atoi( buf ); + } + + // Was this printer discovered via OS X Printer Sharing? + + if ( TXTRecordContainsKey( inTXTSize, inTXT, "printer-state" ) || TXTRecordContainsKey( inTXTSize, inTXT, "printer-type" ) ) + { + service->printer->isSharedFromOSX = true; } exit: // The following code is to fix a problem with older HP // printers that don't include "qtotal" in their text - // record. We'll check to see if the qname is "TEXT" + // record. We'll check to see if the q->name is "TEXT" // and if so, we're going to modify it to be "lp" so // that we don't use the wrong queue - if ( !err && !qtotalDefined && ( qname == L"TEXT" ) ) + if ( !err && !qtotalDefined && ( q->name == L"TEXT" ) ) { - qname = "lp"; + q->name = "lp"; } return err; diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.h b/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.h index cf7f096..63dc0c0 100644 --- a/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.h +++ b/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.h @@ -23,6 +23,15 @@ Change History (most recent first): $Log: PrinterSetupWizardSheet.h,v $ +Revision 1.11 2005/10/05 17:32:51 herscher + Use a case insensitive compare operation to check whether a printer with the same name has already been installed. + +Revision 1.10 2005/07/07 17:53:19 shersche +Fix problems associated with the CUPS printer workaround fix. + +Revision 1.9 2005/04/13 17:46:22 shersche + Generic PCL not selected when printers advertise multiple text records + Revision 1.8 2005/02/08 18:53:33 shersche Remove qtotalDefined parameter from ParseTextRecord() @@ -86,6 +95,12 @@ public: CPrinterSetupWizardSheet(UINT nIDCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0); virtual ~CPrinterSetupWizardSheet(); + CPropertyPage* + GetLastPage(); + + void + SetLastPage(CPropertyPage * page ); + void SetSelectedPrinter(Printer * printer); @@ -241,7 +256,7 @@ private: StopResolve( Service * service ); OSStatus - ParseTextRecord( Service * service, uint16_t inTXTSize, const char * inTXT, CString & qname, uint32_t & qpriority ); + ParseTextRecord( Service * service, Queue * q, uint16_t inTXTSize, const char * inTXT ); OSStatus LoadPrinterNames(); @@ -261,10 +276,10 @@ private: static unsigned WINAPI InstallDriverThread( LPVOID inParam ); - typedef std::map PrinterNameMap; + typedef std::list PrinterNames; typedef std::list ServiceRefList; static CPrinterSetupWizardSheet * m_self; - PrinterNameMap m_printerNames; + PrinterNames m_printerNames; Printer * m_selectedPrinter; bool m_driverThreadFinished; DWORD m_driverThreadExitCode; @@ -273,6 +288,8 @@ private: DNSServiceRef m_lprBrowser; DNSServiceRef m_ippBrowser; DNSServiceRef m_resolver; + + CPropertyPage * m_lastPage; }; @@ -290,6 +307,20 @@ CPrinterSetupWizardSheet::GetCursor() } +inline CPropertyPage* +CPrinterSetupWizardSheet::GetLastPage() +{ + return m_lastPage; +} + + +inline void +CPrinterSetupWizardSheet::SetLastPage(CPropertyPage * lastPage) +{ + m_lastPage = lastPage; +} + + // Service Types #define kPDLServiceType "_pdl-datastream._tcp." diff --git a/Clients/PrinterSetupWizard/SecondPage.cpp b/Clients/PrinterSetupWizard/SecondPage.cpp index 63665ee..c64030d 100644 --- a/Clients/PrinterSetupWizard/SecondPage.cpp +++ b/Clients/PrinterSetupWizard/SecondPage.cpp @@ -23,6 +23,21 @@ Change History (most recent first): $Log: SecondPage.cpp,v $ +Revision 1.18 2005/07/20 17:44:54 shersche + UI fixes for CUPS workaround + +Revision 1.17 2005/07/11 20:17:15 shersche + UI fixes associated with CUPS printer workaround fix. + +Revision 1.16 2005/07/07 17:53:20 shersche +Fix problems associated with the CUPS printer workaround fix. + +Revision 1.15 2005/04/13 17:46:22 shersche + Generic PCL not selected when printers advertise multiple text records + +Revision 1.14 2005/03/20 20:08:37 shersche + Second screen should not select a printer by default + Revision 1.13 2005/02/15 07:50:10 shersche Update name @@ -115,6 +130,12 @@ CSecondPage::InitBrowseList() psheet = reinterpret_cast(GetParent()); require_quiet( psheet, exit ); + // Initialize so that nothing is selected when we add to the list + + psheet->SetSelectedPrinter( NULL ); + m_gotChoice = false; + m_browseList.Select( NULL, TVGN_FIRSTVISIBLE ); + // // load the no printers message until something shows up in the browse list // @@ -131,6 +152,8 @@ CSecondPage::InitBrowseList() // disable the printer information box // SetPrinterInformationState( FALSE ); + m_descriptionField.SetWindowText( L"" ); + m_locationField.SetWindowText( L"" ); exit: @@ -182,44 +205,63 @@ CSecondPage::OnSetActive() Printer * printer; Printers::iterator it; OSStatus err = kNoErr; + BOOL b; + + b = CPropertyPage::OnSetActive(); psheet = reinterpret_cast(GetParent()); require_action( psheet, exit, err = kUnknownErr ); + // Stash the selected printer if any + + printer = psheet->GetSelectedPrinter(); + // initialize the browse list...this will remove everything currently // in it, and add the no printers item InitBrowseList(); - // And populate the list with any printers that we currently know about + // Populate the list with any printers that we currently know about for ( it = psheet->m_printers.begin(); it != psheet->m_printers.end(); it++ ) { OnAddPrinter( *it, false ); } - printer = psheet->GetSelectedPrinter(); + // And if we hit 'Back' from page 3, then re-select printer - if ( printer != NULL ) + if ( ( psheet->GetLastPage() == psheet->GetPage( 2 ) ) && printer ) { + psheet->SetSelectedPrinter( printer ); m_browseList.Select( printer->item, TVGN_FIRSTVISIBLE ); } exit: - return CPropertyPage::OnSetActive(); + return b; } BOOL CSecondPage::OnKillActive() { + CPrinterSetupWizardSheet * psheet; + + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + psheet->SetLastPage(this); + +exit: + return CPropertyPage::OnKillActive(); } BEGIN_MESSAGE_MAP(CSecondPage, CPropertyPage) ON_NOTIFY(TVN_SELCHANGED, IDC_BROWSE_LIST, OnTvnSelchangedBrowseList) + ON_NOTIFY(NM_CLICK, IDC_BROWSE_LIST, OnNmClickBrowseList) + ON_NOTIFY(TVN_KEYDOWN, IDC_BROWSE_LIST, OnTvnKeyDownBrowseList) ON_WM_SETCURSOR() END_MESSAGE_MAP() @@ -245,11 +287,6 @@ CSecondPage::OnAddPrinter( m_browseList.SetItemData( printer->item, (DWORD_PTR) printer ); m_browseList.SortChildren(TVI_ROOT); - - if ( printer->name == m_selectedName ) - { - m_browseList.SelectItem( printer->item ); - } // // if the searching item is still in the list @@ -333,9 +370,14 @@ CSecondPage::OnResolveService( Service * service ) { CPrinterSetupWizardSheet * psheet = reinterpret_cast(GetParent()); require_quiet( psheet, exit ); - + check( service ); + Queue * q = service->SelectedQueue(); + + check( q ); + + // // and set it to selected // @@ -347,8 +389,8 @@ CSecondPage::OnResolveService( Service * service ) // SetPrinterInformationState( TRUE ); - m_descriptionField.SetWindowText( service->description ); - m_locationField.SetWindowText( service->location ); + m_descriptionField.SetWindowText( q->description ); + m_locationField.SetWindowText( q->location ); // // reset the cursor @@ -366,15 +408,43 @@ void CSecondPage::OnTvnSelchangedBrowseList(NMHDR *pNMHDR, LRESULT *pResult) { LPNMTREEVIEW pNMTreeView = reinterpret_cast(pNMHDR); CPrinterSetupWizardSheet * psheet; + Printer * printer; int err = 0; - HTREEITEM item = m_browseList.GetSelectedItem(); - require_quiet( item, exit ); - psheet = reinterpret_cast(GetParent()); - require_action( psheet, exit, err = kUnknownErr ); + require_action( psheet, exit, err = kUnknownErr ); - Printer * printer; + // The strange code here is to workaround a bug in the CTreeCtrl, whereupon the item + // we selected isn't passed through correctly to this callback routine. + + if ( !m_gotChoice ) + { + printer = psheet->GetSelectedPrinter(); + + // If we really haven't selected a printer, then re-select NULL and exit + + if ( !printer ) + { + m_browseList.SelectItem( NULL ); + + goto exit; + } + + // If we already have selected a printer, fake like we've clicked on it, but only + // if the CTreeCtrl hasn't already selected it + + else if ( printer->item != m_browseList.GetSelectedItem() ) + { + m_gotChoice = true; + + m_browseList.SelectItem( printer->item ); + + goto exit; + } + } + + HTREEITEM item = m_browseList.GetSelectedItem(); + require_quiet( item, exit ); printer = reinterpret_cast(m_browseList.GetItemData( item ) ); require_quiet( printer, exit ); @@ -410,6 +480,26 @@ exit: } +void CSecondPage::OnNmClickBrowseList(NMHDR *pNMHDR, LRESULT *pResult) +{ + DEBUG_UNUSED( pNMHDR ); + + m_gotChoice = true; + + *pResult = 0; +} + + +void CSecondPage::OnTvnKeyDownBrowseList( NMHDR * pNMHDR, LRESULT * pResult) +{ + DEBUG_UNUSED( pNMHDR ); + + m_gotChoice = true; + + *pResult = 0; +} + + void CSecondPage::LoadTextAndDisableWindow( CString & text ) { diff --git a/Clients/PrinterSetupWizard/SecondPage.h b/Clients/PrinterSetupWizard/SecondPage.h index aa08260..5cf1852 100644 --- a/Clients/PrinterSetupWizard/SecondPage.h +++ b/Clients/PrinterSetupWizard/SecondPage.h @@ -23,6 +23,9 @@ Change History (most recent first): $Log: SecondPage.h,v $ +Revision 1.8 2005/03/20 20:08:37 shersche + Second screen should not select a printer by default + Revision 1.7 2005/01/31 23:54:30 shersche Start browsing when printer wizard starts. Move browsing logic from CSecondPage object to CPrinterSetupWizardSheet object. @@ -95,6 +98,8 @@ public: 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 ); OSStatus OnAddPrinter( @@ -126,4 +131,6 @@ private: CStatic m_descriptionField; CStatic m_locationLabel; CStatic m_locationField; + + bool m_gotChoice; }; diff --git a/Clients/PrinterSetupWizard/ThirdPage.cpp b/Clients/PrinterSetupWizard/ThirdPage.cpp index acd8d3c..bf16fe2 100644 --- a/Clients/PrinterSetupWizard/ThirdPage.cpp +++ b/Clients/PrinterSetupWizard/ThirdPage.cpp @@ -23,6 +23,27 @@ Change History (most recent first): $Log: ThirdPage.cpp,v $ +Revision 1.27 2005/10/05 21:41:45 herscher + Use "application/octet-stream" to determine if CUPS shared queue supports raw + +Revision 1.26 2005/07/11 20:17:15 shersche + UI fixes associated with CUPS printer workaround fix. + +Revision 1.25 2005/07/07 17:53:20 shersche +Fix problems associated with the CUPS printer workaround fix. + +Revision 1.24 2005/06/30 18:02:54 shersche + Workaround for Mac OS X Printer Sharing bug + +Revision 1.23 2005/04/18 02:33:47 shersche + Default printer option cannot be deselected + +Revision 1.22 2005/04/13 17:46:22 shersche + Generic PCL not selected when printers advertise multiple text records + +Revision 1.21 2005/03/30 02:09:55 shersche +Auto-resize the column width to account for differing fonts and font sizes + Revision 1.20 2005/03/05 02:27:45 shersche Generic drivers don't do color @@ -183,7 +204,7 @@ CThirdPage::CThirdPage() require_noerr(err, exit); // - // and lastly load our own special generic printer defs + // load our own special generic printer defs // err = LoadGenericPrintDriverDefs( m_manufacturers ); require_noerr( err, exit ); @@ -231,7 +252,7 @@ CThirdPage::~CThirdPage() // // ---------------------------------------------------- void -CThirdPage::SelectMatch(Printer * printer, Service * service, Manufacturers & manufacturers, Manufacturer * manufacturer, Model * model) +CThirdPage::SelectMatch(Printer * printer, Service * service, Manufacturer * manufacturer, Model * model) { LVFINDINFO info; int nIndex; @@ -240,8 +261,6 @@ CThirdPage::SelectMatch(Printer * printer, Service * service, Manufacturers & ma check( manufacturer != NULL ); check( model != NULL ); - PopulateUI( manufacturers ); - // // select the manufacturer // @@ -276,6 +295,15 @@ CThirdPage::SelectMatch(Printer * printer, Service * service, Manufacturers & ma } +void +CThirdPage::SelectMatch(Manufacturers & manufacturers, Printer * printer, Service * service, Manufacturer * manufacturer, Model * model) +{ + PopulateUI( manufacturers ); + + SelectMatch( printer, service, manufacturer, model ); +} + + // -------------------------------------------------------- // CopyPrinterSettings // @@ -824,7 +852,7 @@ CThirdPage::LoadGenericPrintDriverDefs( Manufacturers & manufacturers ) // First try and find our generic driver names - iter = manufacturers.find(L"HP"); + iter = m_manufacturers.find(L"HP"); require_action( iter != manufacturers.end(), exit, err = kUnknownErr ); manufacturer = iter->second; @@ -1028,7 +1056,7 @@ CThirdPage::NormalizeManufacturerName( const CString & name ) // MatchManufacturer and MatchModel in turn. // -OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * printer, Service * service) +OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * printer, Service * service, bool useCUPSWorkaround) { CString normalizedProductName; Manufacturer * manufacturer = NULL; @@ -1039,20 +1067,27 @@ OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * print CString text; OSStatus err = kNoErr; + check( printer ); + check( service ); + + Queue * q = service->SelectedQueue(); + + check( q ); + // // first look to see if we have a usb_MFG descriptor // - if (service->usb_MFG.GetLength() > 0) + if ( q->usb_MFG.GetLength() > 0) { - manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( service->usb_MFG ) ); + manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( q->usb_MFG ) ); } if ( manufacturer == NULL ) { - service->product.Remove('('); - service->product.Remove(')'); + q->product.Remove('('); + q->product.Remove(')'); - manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( service->product ) ); + manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( q->product ) ); } // @@ -1060,25 +1095,48 @@ OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * print // if ( manufacturer != NULL ) { - if (service->usb_MDL.GetLength() > 0) + if ( q->usb_MDL.GetLength() > 0 ) { - model = MatchModel ( manufacturer, ConvertToModelName ( service->usb_MDL ) ); + model = MatchModel ( manufacturer, ConvertToModelName ( q->usb_MDL ) ); } - if ( ( model == NULL ) && ( service->product.GetLength() > 0 ) ) + if ( ( model == NULL ) && ( q->product.GetLength() > 0 ) ) { - service->product.Remove('('); - service->product.Remove(')'); + q->product.Remove('('); + q->product.Remove(')'); - model = MatchModel ( manufacturer, ConvertToModelName ( service->product ) ); + model = MatchModel ( manufacturer, ConvertToModelName ( q->product ) ); } if ( model != NULL ) { - Manufacturers manufacturers; - - manufacturers[manufacturer->name] = manufacturer; - SelectMatch(printer, service, manufacturers, manufacturer, model); + // Offer Generic printers if printer advertises Postscript or PCL. Workaround + // bug in OS X CUPS printer sharing by selecting Generic driver instead of matched printer. + + bool hasGenericDriver = false; + + if ( MatchGeneric( manufacturers, printer, service, &genericManufacturer, &genericModel ) ) + { + hasGenericDriver = true; + } + + // Use "application/octet-stream" to determine if CUPS + // shared queue supports raw + + if ( q->pdl.Find( L"application/octet-stream" ) != -1 ) + { + useCUPSWorkaround = false; + } + + if ( useCUPSWorkaround && printer->isSharedFromOSX && hasGenericDriver ) + { + SelectMatch(manufacturers, printer, service, genericManufacturer, genericModel ); + } + else + { + SelectMatch(manufacturers, printer, service, manufacturer, model); + } + found = true; } } @@ -1091,26 +1149,18 @@ OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * print { text.LoadString(IDS_PRINTER_MATCH_GOOD); } - else if ( MatchGeneric( printer, service, &genericManufacturer, &genericModel ) ) - { - Manufacturers * pManufacturers; - Manufacturers manufacturers; - - text.LoadString(IDS_PRINTER_MATCH_MAYBE); - - if ( manufacturer ) + else if ( MatchGeneric( manufacturers, printer, service, &genericManufacturer, &genericModel ) ) + { + if ( printer->isSharedFromOSX ) { - manufacturers[genericManufacturer->name] = genericManufacturer; - manufacturers[manufacturer->name] = manufacturer; - - pManufacturers = &manufacturers; + text.LoadString(IDS_PRINTER_MATCH_GOOD); } else { - pManufacturers = &m_manufacturers; + text.LoadString(IDS_PRINTER_MATCH_MAYBE); } - SelectMatch( printer, service, *pManufacturers, genericManufacturer, genericModel ); + SelectMatch( manufacturers, printer, service, genericManufacturer, genericModel ); } else { @@ -1247,19 +1297,25 @@ CThirdPage::MatchModel(Manufacturer * manufacturer, const CString & name) // specifically // BOOL -CThirdPage::MatchGeneric( Printer * printer, Service * service, Manufacturer ** manufacturer, Model ** model ) +CThirdPage::MatchGeneric( Manufacturers & manufacturers, Printer * printer, Service * service, Manufacturer ** manufacturer, Model ** model ) { CString pdl; BOOL ok = FALSE; DEBUG_UNUSED( printer ); - Manufacturers::iterator iter = m_manufacturers.find( kGenericManufacturer ); - require_action_quiet( iter != m_manufacturers.end(), exit, ok = FALSE ); + check( service ); + + Queue * q = service->SelectedQueue(); + + check( q ); + + Manufacturers::iterator iter = manufacturers.find( kGenericManufacturer ); + require_action_quiet( iter != manufacturers.end(), exit, ok = FALSE ); *manufacturer = iter->second; - pdl = service->pdl; + pdl = q->pdl; pdl.MakeLower(); if ( pdl.Find( kPDLPCLKey ) != -1 ) @@ -1323,11 +1379,11 @@ OSStatus CThirdPage::OnInitPage() // selection notice // header.LoadString(IDS_MANUFACTURER_HEADING); - m_manufacturerListCtrl.InsertColumn(0, header, LVCFMT_LEFT, 138); + m_manufacturerListCtrl.InsertColumn(0, header, LVCFMT_LEFT, -1 ); m_manufacturerSelected = NULL; header.LoadString(IDS_MODEL_HEADING); - m_modelListCtrl.InsertColumn(0, header, LVCFMT_LEFT, 247); + m_modelListCtrl.InsertColumn(0, header, LVCFMT_LEFT, -1 ); m_modelSelected = NULL; return (err); @@ -1363,15 +1419,7 @@ CThirdPage::OnSetActive() psheet = reinterpret_cast(GetParent()); require_quiet( psheet, exit ); - if ((m_manufacturerListCtrl.GetFirstSelectedItemPosition() != NULL) && - (m_modelListCtrl.GetFirstSelectedItemPosition() != NULL)) - { - psheet->SetWizardButtons( PSWIZB_BACK|PSWIZB_NEXT ); - } - else - { - psheet->SetWizardButtons( PSWIZB_BACK ); - } + psheet->SetWizardButtons( PSWIZB_BACK ); printer = psheet->GetSelectedPrinter(); require_quiet( printer, exit ); @@ -1402,7 +1450,15 @@ CThirdPage::OnSetActive() // // and try and match the printer // - MatchPrinter( m_manufacturers, printer, service ); + + if ( psheet->GetLastPage() == psheet->GetPage(1) ) + { + MatchPrinter( m_manufacturers, printer, service, true ); + } + else + { + SelectMatch(printer, service, m_manufacturerSelected, m_modelSelected); + } exit: @@ -1410,6 +1466,22 @@ exit: } +BOOL +CThirdPage::OnKillActive() +{ + CPrinterSetupWizardSheet * psheet; + + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + psheet->SetLastPage(this); + +exit: + + return CPropertyPage::OnKillActive(); +} + + // ------------------------------------------------------- // PopulateUI // @@ -1431,6 +1503,8 @@ CThirdPage::PopulateUI(Manufacturers & manufacturers) nIndex = m_manufacturerListCtrl.InsertItem(0, manufacturer->name); m_manufacturerListCtrl.SetItemData(nIndex, (DWORD_PTR) manufacturer); + + m_manufacturerListCtrl.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER ); } return 0; @@ -1471,6 +1545,8 @@ void CThirdPage::OnLvnItemchangedManufacturer(NMHDR *pNMHDR, LRESULT *pResult) int nItem = m_modelListCtrl.InsertItem( 0, model->displayName ); m_modelListCtrl.SetItemData(nItem, (DWORD_PTR) model); + + m_modelListCtrl.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER ); } m_modelListCtrl.SetRedraw(TRUE); @@ -1531,7 +1607,7 @@ void CThirdPage::OnBnClickedDefaultPrinter() printer = psheet->GetSelectedPrinter(); require_quiet( printer, exit ); - printer->deflt = m_defaultPrinterCtrl.GetState() ? true : false; + printer->deflt = ( m_defaultPrinterCtrl.GetCheck() == BST_CHECKED ) ? true : false; exit: @@ -1543,6 +1619,7 @@ void CThirdPage::OnBnClickedHaveDisk() CPrinterSetupWizardSheet * psheet; Printer * printer; Service * service; + Manufacturers manufacturers; CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY|OFN_FILEMUSTEXIST, L"Setup Information (*.inf)|*.inf||", this); @@ -1555,16 +1632,29 @@ void CThirdPage::OnBnClickedHaveDisk() service = printer->services.front(); require_quiet( service, exit ); - if ( dlg.DoModal() == IDOK ) + for ( ;; ) { - Manufacturers manufacturers; - CString filename = dlg.GetPathName(); + if ( dlg.DoModal() == IDOK ) + { + CString filename = dlg.GetPathName(); - LoadPrintDriverDefsFromFile( manufacturers, filename, true ); + LoadPrintDriverDefsFromFile( manufacturers, filename, true ); - PopulateUI( manufacturers ); + // Sanity check - MatchPrinter( manufacturers, printer, service ); + if ( manufacturers.size() > 0 ) + { + PopulateUI( manufacturers ); + + MatchPrinter( manufacturers, printer, service, false ); + + break; + } + } + else + { + break; + } } exit: diff --git a/Clients/PrinterSetupWizard/ThirdPage.h b/Clients/PrinterSetupWizard/ThirdPage.h index a5a9bb6..8331421 100644 --- a/Clients/PrinterSetupWizard/ThirdPage.h +++ b/Clients/PrinterSetupWizard/ThirdPage.h @@ -23,6 +23,9 @@ Change History (most recent first): $Log: ThirdPage.h,v $ +Revision 1.5 2005/07/07 17:53:20 shersche +Fix problems associated with the CUPS printer workaround fix. + Revision 1.4 2005/02/08 21:45:06 shersche Default to Generic PostScript or PCL if unable to match driver @@ -67,6 +70,7 @@ public: protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support virtual BOOL OnSetActive(); + virtual BOOL OnKillActive(); DECLARE_MESSAGE_MAP() @@ -107,7 +111,7 @@ private: // // Tries to match printer based on manufacturer and model // - OSStatus MatchPrinter(Manufacturers & manufacturers, Printer * printer, Service * service); + OSStatus MatchPrinter(Manufacturers & manufacturers, Printer * printer, Service * service, bool useCUPSWorkaround); // // OnInitPage @@ -125,12 +129,13 @@ private: Manufacturer * MatchManufacturer( Manufacturers & manufacturer, const CString & name ); Model * MatchModel( Manufacturer * manufacturer, const CString & name ); - BOOL MatchGeneric( Printer * printer, Service * service, Manufacturer ** manufacturer, Model ** model ); - void SelectMatch(Printer * printer, Service * service, Manufacturers & manufacturers, Manufacturer * manufacturer, Model * model); + 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); Manufacturers m_manufacturers; - + CListCtrl m_manufacturerListCtrl; Manufacturer * m_manufacturerSelected; diff --git a/Clients/PrinterSetupWizard/UtilTypes.h b/Clients/PrinterSetupWizard/UtilTypes.h index 5e2fe19..e5512b9 100644 --- a/Clients/PrinterSetupWizard/UtilTypes.h +++ b/Clients/PrinterSetupWizard/UtilTypes.h @@ -23,6 +23,15 @@ Change History (most recent first): $Log: UtilTypes.h,v $ +Revision 1.14 2005/06/30 18:02:54 shersche + Workaround for Mac OS X Printer Sharing bug + +Revision 1.13 2005/04/13 17:46:22 shersche + Generic PCL not selected when printers advertise multiple text records + +Revision 1.12 2005/03/16 03:12:28 shersche + Generic PCL driver isn't selected correctly on Win2K + Revision 1.11 2005/03/05 02:27:46 shersche Generic drivers don't do color @@ -135,6 +144,11 @@ namespace PrinterSetupWizard 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 isSharedFromOSX; + // // state // @@ -149,6 +163,9 @@ namespace PrinterSetupWizard ~Service(); + Queue* + SelectedQueue(); + void EmptyQueues(); @@ -163,12 +180,6 @@ namespace PrinterSetupWizard DNSServiceRef serviceRef; CString hostname; unsigned short portNumber; - CString pdl; - CString usb_MFG; - CString usb_MDL; - CString description; - CString location; - CString product; CString protocol; unsigned short qtotal; @@ -194,6 +205,12 @@ namespace PrinterSetupWizard CString name; uint32_t priority; + CString pdl; + CString usb_MFG; + CString usb_MDL; + CString description; + CString location; + CString product; }; @@ -219,6 +236,8 @@ namespace PrinterSetupWizard inline Printer::Printer() + : + isSharedFromOSX( false ) { } @@ -269,6 +288,12 @@ namespace PrinterSetupWizard EmptyQueues(); } + inline Queue* + Service::SelectedQueue() + { + return queues.front(); + } + inline void Service::EmptyQueues() { @@ -301,7 +326,7 @@ namespace PrinterSetupWizard { Model * model = *it; - if ( model->name = name ) + if ( model->name == name ) { return model; } diff --git a/Clients/PrinterSetupWizard/res/NetworkPrinter.ico b/Clients/PrinterSetupWizard/res/NetworkPrinter.ico index 22130b3de3ee242e78b347bc07d177cf3cd44a10..b737f473253cde006f562cdca30f025db8646439 100644 GIT binary patch literal 82726 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 diff --git a/Clients/PrinterSetupWizard/res/Print.ico b/Clients/PrinterSetupWizard/res/Print.ico index 22130b3de3ee242e78b347bc07d177cf3cd44a10..b737f473253cde006f562cdca30f025db8646439 100644 GIT binary patch literal 82726 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 diff --git a/Clients/PrinterSetupWizard/resource.h b/Clients/PrinterSetupWizard/resource.h index 639d2d9..5d21d40 100644 --- a/Clients/PrinterSetupWizard/resource.h +++ b/Clients/PrinterSetupWizard/resource.h @@ -1,24 +1,24 @@ /* * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ Change History (most recent first): diff --git a/Clients/PrinterSetupWizard/stdafx.h b/Clients/PrinterSetupWizard/stdafx.h index 160644b..8ec4bdb 100644 --- a/Clients/PrinterSetupWizard/stdafx.h +++ b/Clients/PrinterSetupWizard/stdafx.h @@ -23,6 +23,9 @@ Change History (most recent first): $Log: stdafx.h,v $ +Revision 1.2 2005/10/19 19:50:35 herscher +Workaround a bug in the latest Microsoft Platform SDK when compiling C++ files that include (directly or indirectly) + Revision 1.1 2004/06/18 04:36:58 rpantos First checked in @@ -59,6 +62,10 @@ First checked in // 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])) +#endif + #include // MFC core and standard components #include // MFC extensions #include // MFC Automation classes diff --git a/Clients/dns-sd.c b/Clients/dns-sd.c index 699a932..34af2d6 100644 --- a/Clients/dns-sd.c +++ b/Clients/dns-sd.c @@ -58,7 +58,7 @@ POSIX systems: gcc dns-sd.c -o dns-sd -I../mDNSShared -ldns_sd Windows: -cl dns-sd.c -I../mDNSShared -DNOT_HAVE_GETOPT -DNOT_HAVE_SETLINEBUF ws2_32.lib ..\mDNSWindows\DLL\Release\dnssd.lib +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) */ @@ -67,20 +67,25 @@ cl dns-sd.c -I../mDNSShared -DNOT_HAVE_GETOPT -DNOT_HAVE_SETLINEBUF ws2_32.lib . #include // For stdout, stderr #include // For exit() #include // For strlen(), strcpy(), bzero() -#include // For errno, EINTR +#include // For errno, EINTR #include -#include // For u_char +#include // For u_char #ifdef _WIN32 +#include +#include #include -typedef int pid_t; -#define getpid _getpid -#define strcasecmp _stricmp -#define snprintf _snprintf +typedef int pid_t; +#define getpid _getpid +#define strcasecmp _stricmp +#define snprintf _snprintf #else +#include // For getopt() and optind +#include // For getaddrinfo() #include // For struct timeval -#include // For getopt() and optind #include // For inet_addr() +#include // For struct sockaddr_in() +#include // For AF_INET #endif @@ -214,9 +219,10 @@ static const char *GetNextLabel(const char *cstr, char label[64]) return(cstr); } -static void DNSSD_API enum_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t ifIndex, +static void DNSSD_API enum_reply(DNSServiceRef client, 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[128]; @@ -233,10 +239,7 @@ static void DNSSD_API enum_reply(DNSServiceRef client, DNSServiceFlags flags, ui printtimestamp(); printf("%-10s", DomainMsg(flags)); printf("%-8s", (flags & kDNSServiceFlagsMoreComing) ? "(More)" : ""); - flags &= ~kDNSServiceFlagsMoreComing; - flags &= ~kDNSServiceFlagsAdd; - flags &= ~kDNSServiceFlagsDefault; - if (flags) printf("Flags: %4X ", flags); + if (partialflags) printf("Flags: %4X ", partialflags); else printf(" "); // 2. Count the labels @@ -270,68 +273,59 @@ static void DNSSD_API enum_reply(DNSServiceRef client, DNSServiceFlags flags, ui printf("> %s\n", text); } - fflush( stdout ); + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); } -static void DNSSD_API browse_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, +static void DNSSD_API browse_reply(DNSServiceRef client, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, const char *replyName, const char *replyType, const char *replyDomain, void *context) { char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv"; (void)client; // Unused (void)errorCode; // Unused (void)context; // Unused - if (num_printed++ == 0) printf("Timestamp A/R Flags if %-24s %-24s %s\n", "Domain", "Service Type", "Instance Name"); + if (num_printed++ == 0) printf("Timestamp A/R Flags if %-25s %-25s %s\n", "Domain", "Service Type", "Instance Name"); printtimestamp(); - printf("%s%6X%3d %-24s %-24s %s\n", op, flags, ifIndex, replyDomain, replyType, replyName); - fflush( stdout ); + printf("%s%6X%3d %-25s %-25s %s\n", op, flags, ifIndex, replyDomain, replyType, replyName); + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); } -static void DNSSD_API resolve_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, +static void DNSSD_API resolve_reply(DNSServiceRef client, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t opaqueport, uint16_t txtLen, const char *txtRecord, void *context) { - const char *src = txtRecord; union { uint16_t s; u_char b[2]; } port = { opaqueport }; uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1]; (void)client; // Unused (void)ifIndex; // Unused (void)errorCode; // Unused - (void)txtLen; // Unused (void)context; // Unused printtimestamp(); printf("%s can be reached at %s:%u", fullname, hosttarget, PortAsNumber); if (flags) printf(" Flags: %X", flags); - if (*src) + if (txtLen > 1) // Don't show degenerate TXT records containing nothing but a single empty string { - 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) + const char *ptr = txtRecord; + const char *max = txtRecord + txtLen; + printf(" TXT"); + while (ptr < max) { - if (*src == '\\') *dst++ = '\\'; // '\' displays as "\\" - if (*src >= ' ') *dst++ = *src++; // Display normal characters as-is - else + const char *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 < end) { - *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++; + if (*ptr == '\\') printf("\\\\"); // '\' displays as "\\" + else if (*ptr == ' ' ) printf("\\ "); // ' ' displays as "\ " + else if (*ptr > ' ' ) printf("%c", *ptr); // Display normal characters as-is + else printf("\\x%02X", *ptr); // ther chararacters displayed as "\xHH" + ptr++; } } - *dst++ = 0; - printf(" TXT %s", txtInfo); } printf("\n"); - fflush( stdout ); + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); } static void myTimerCallBack(void) @@ -387,7 +381,7 @@ static void myTimerCallBack(void) } } -static void DNSSD_API reg_reply(DNSServiceRef client, DNSServiceFlags flags, DNSServiceErrorType errorCode, +static void DNSSD_API reg_reply(DNSServiceRef client, const DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context) { (void)client; // Unused @@ -403,10 +397,10 @@ static void DNSSD_API reg_reply(DNSServiceRef client, DNSServiceFlags flags, DNS } if (operation == 'A' || operation == 'U' || operation == 'N') timeOut = 5; - fflush( stdout ); + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); } -static void DNSSD_API qr_reply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, +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"; @@ -433,7 +427,7 @@ static void DNSSD_API qr_reply(DNSServiceRef sdRef, DNSServiceFlags flags, uint3 if (num_printed++ == 0) printf("Timestamp A/R Flags if %-30s%4s%4s Rdata\n", "Name", "T", "C"); printtimestamp(); printf("%s%6X%3d %-30s%4d%4d %s\n", op, flags, ifIndex, fullname, rrtype, rrclass, rdb); - fflush( stdout ); + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); } //************************************************************************************************************* @@ -507,7 +501,7 @@ static int getfirstoption( int argc, char **argv, const char *optstr, int *pOptI } #endif -static void DNSSD_API MyRegisterRecordCallback(DNSServiceRef service, DNSRecordRef record, DNSServiceFlags flags, +static void DNSSD_API MyRegisterRecordCallback(DNSServiceRef service, DNSRecordRef record, const DNSServiceFlags flags, DNSServiceErrorType errorCode, void * context) { char *name = (char *)context; @@ -523,16 +517,43 @@ static void DNSSD_API MyRegisterRecordCallback(DNSServiceRef service, DNSRecordR case kDNSServiceErr_NameConflict: printf("Name in use, please choose another\n"); exit(-1); default: printf("Error %d\n", errorCode); return; } - fflush( stdout ); + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); + } + +static unsigned long getip(const char *const name) + { + unsigned long ip = 0; + struct addrinfo hints; + struct addrinfo * addrs = NULL; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + + if (getaddrinfo(name, NULL, &hints, &addrs) == 0) + { + ip = ((struct sockaddr_in*) addrs->ai_addr)->sin_addr.s_addr; + } + + if (addrs) + { + freeaddrinfo(addrs); + } + + return(ip); } static DNSServiceErrorType RegisterProxyAddressRecord(DNSServiceRef *sdRef, const char *host, const char *ip) { - unsigned long addr = inet_addr(ip); + // 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 make sure DNSServiceCreateConnection() is called before getip() is. + unsigned long addr = 0; DNSServiceErrorType err = DNSServiceCreateConnection(sdRef); if (err) { fprintf(stderr, "DNSServiceCreateConnection returned %d\n", err); return(err); } + addr = getip(ip); return(DNSServiceRegisterRecord(*sdRef, &record, kDNSServiceFlagsUnique, kDNSServiceInterfaceIndexAny, host, kDNSServiceType_A, kDNSServiceClass_IN, sizeof(addr), &addr, 240, MyRegisterRecordCallback, (void*)host)); + // Note, should probably add support for creating proxy AAAA records too, one day } static DNSServiceErrorType RegisterService(DNSServiceRef *sdRef, @@ -547,18 +568,23 @@ static DNSServiceErrorType RegisterService(DNSServiceRef *sdRef, 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\n", port); + for (i = 0; i < argc; i++) { - unsigned char *len = ptr++; - *len = strlen(argv[i]); - strcpy((char*)ptr, argv[i]); - ptr += *len; + int length = strlen(argv[i]); + if (length <= 255) + { + *ptr++ = (unsigned char)length; + strcpy((char*)ptr, argv[i]); + ptr += length; + printf("TXT %s\n", argv[i]); + } } - printf("Registering Service %s.%s%s", nam, typ, dom); - if (host && *host) printf(" host %s", host); - printf(" port %s %s\n", port, txt); - return(DNSServiceRegister(sdRef, /* kDNSServiceFlagsAllowRemoteQuery */ 0, opinterface, nam, typ, dom, host, registerPort.NotAnInteger, ptr-txt, txt, reg_reply, NULL)); + return(DNSServiceRegister(sdRef, /* kDNSServiceFlagsAllowRemoteQuery */ 0, opinterface, nam, typ, dom, host, registerPort.NotAnInteger, (uint16_t) (ptr-txt), txt, reg_reply, NULL)); } int main(int argc, char **argv) @@ -572,9 +598,6 @@ int main(int argc, char **argv) char *dom; int optind; const char *progname = strrchr(argv[0], kFilePathSep) ? strrchr(argv[0], kFilePathSep) + 1 : argv[0]; -#ifndef NOT_HAVE_SETLINEBUF - setlinebuf(stdout); // Want to see lines as they appear, not block buffered -#endif if (argc > 1 && !strcmp(argv[1], "-lo")) { @@ -584,6 +607,14 @@ int main(int argc, char **argv) printf("Using LocalOnly\n"); } + if (argc > 2 && !strcmp(argv[1], "-i") && atoi(argv[2])) + { + opinterface = atoi(argv[2]); + argc -= 2; + argv += 2; + printf("Using interface %d\n", opinterface); + } + if (argc < 2) goto Fail; // Minimum command line is the command name and one argument operation = getfirstoption( argc, argv, "EFBLQRPAUNTMI", &optind); if (operation == -1) goto Fail; @@ -605,7 +636,7 @@ int main(int argc, char **argv) case 'B': if (argc < optind+1) goto Fail; dom = (argc < optind+2) ? "" : argv[optind+1]; if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string - printf("Browsing for %s%s\n", argv[optind+0], dom); + printf("Browsing for %s%s%s\n", argv[optind+0], dom[0] ? "." : "", dom); err = DNSServiceBrowse(&client, 0, opinterface, argv[optind+0], dom, browse_reply, NULL); break; diff --git a/Makefile b/Makefile index 56dedd9..e4a7574 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ include /Developer/Makefiles/pb_makefiles/platform.make -MVERS = "mDNSResponder-107.4" +MVERS = "mDNSResponder-107.5" install: cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild install OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) diff --git a/buildResults.xml b/buildResults.xml new file mode 100644 index 0000000..b45ac9a --- /dev/null +++ b/buildResults.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mDNSCore/DNSCommon.c b/mDNSCore/DNSCommon.c index 2ce2489..a1b7dc4 100644 --- a/mDNSCore/DNSCommon.c +++ b/mDNSCore/DNSCommon.c @@ -1,4 +1,5 @@ -/* +/* -*- Mode: C; tab-width: 4 -*- + * * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ @@ -23,6 +24,14 @@ Change History (most recent first): $Log: DNSCommon.c,v $ +Revision 1.92 2005/09/16 21:06:49 cheshire +Use mDNS_TimeNow_NoLock macro, instead of writing "mDNSPlatformRawTime() + m->timenow_adjust" all over the place + +Revision 1.91 2005/07/10 22:10:37 cheshire +The getOptRdata routine implicitly assumes the destination ResourceRecord is large enough to +hold MaximumRDSize bytes, but its parameter was a generic ResourceRecord, which need not be that +large. Changing the parameter to a LargeCacheRecord makes it clearer what the routine requires. + Revision 1.90 2005/03/21 00:33:51 shersche Fix build warnings on Win32 platform @@ -1383,9 +1392,10 @@ mDNSlocal mDNSu16 getVal16(const mDNSu8 **ptr) return val; } -mDNSlocal const mDNSu8 *getOptRdata(const mDNSu8 *ptr, const mDNSu8 *limit, ResourceRecord *rr, mDNSu16 pktRDLen) +mDNSlocal const mDNSu8 *getOptRdata(const mDNSu8 *ptr, const mDNSu8 *const limit, LargeCacheRecord *const cr, mDNSu16 pktRDLen) { int nread = 0; + ResourceRecord *const rr = &cr->r.resrec; rdataOpt *opt = (rdataOpt *)rr->rdata->u.data; while (nread < pktRDLen && (mDNSu8 *)opt < rr->rdata->u.data + MaximumRDSize - sizeof(rdataOpt)) @@ -1884,7 +1894,7 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage rr->resrec.rdata->u.soa.min = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]); break; - case kDNSType_OPT: getOptRdata(ptr, end, &rr->resrec, pktrdlength); break; + case kDNSType_OPT: getOptRdata(ptr, end, largecr, pktrdlength); break; default: if (pktrdlength > rr->resrec.rdata->MaxRDLength) { @@ -2047,14 +2057,14 @@ mDNSexport void mDNS_Lock(mDNS *const m) if (m->mDNS_busy == 0) { if (m->timenow) - LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m->timenow, mDNSPlatformRawTime() + m->timenow_adjust); - m->timenow = mDNSPlatformRawTime() + m->timenow_adjust; + LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", 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("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy); - m->timenow = mDNSPlatformRawTime() + m->timenow_adjust; + m->timenow = mDNS_TimeNow_NoLock(m); if (m->timenow == 0) m->timenow = 1; } diff --git a/mDNSCore/mDNS.c b/mDNSCore/mDNS.c index db6d0e5..2098e1e 100755 --- a/mDNSCore/mDNS.c +++ b/mDNSCore/mDNS.c @@ -45,6 +45,25 @@ Change History (most recent first): $Log: mDNS.c,v $ +Revision 1.532 2005/12/02 20:24:36 cheshire + Adjust cutoff time for KA list by one second + +Revision 1.531 2005/12/02 19:05:42 cheshire +Tidy up constants + +Revision 1.530 2005/11/07 01:49:48 cheshire +For consistency, use NonZeroTime() function instead of ?: expression + +Revision 1.529 2005/10/25 23:42:24 cheshire + Error in ResolveSimultaneousProbe() when type or class don't match +Changed switch statement to an "if" + +Revision 1.528 2005/10/25 23:34:22 cheshire + RequireGoodbye state not set/respected sometimes when machine going to sleep + +Revision 1.527 2005/10/25 22:43:59 cheshire +Add clarifying comments + Revision 1.526 2005/10/20 00:10:33 cheshire Add check to avoid crashing NAT gateways that have buggy DNS relay code @@ -1747,12 +1766,12 @@ mDNSexport const mDNSv6Addr AllDNSLinkGroupv6 = { { 0xFF,0x02,0x00,0x00, 0x00,0 mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 251 } } } }; mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } }; -mDNSexport const mDNSOpaque16 zeroID = { { 0, 0 } }; -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 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 } }; +mDNSexport const mDNSOpaque16 zeroID = { { 0, 0 } }; +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 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 } }; // Any records bigger than this are considered 'large' records #define SmallRecordLimit 1024 @@ -3121,7 +3140,7 @@ mDNSlocal void SendResponses(mDNS *const m) numDereg++; responseptr = newptr; } - else if (rr->NewRData) // If we have new data for this record + else if (rr->NewRData && !m->SleepState) // If we have new data for this record { RData *OldRData = rr->resrec.rdata; mDNSu16 oldrdlength = rr->resrec.rdlength; @@ -3132,6 +3151,7 @@ mDNSlocal void SendResponses(mDNS *const m) if (!newptr && m->omsg.h.numAnswers) break; numDereg++; responseptr = newptr; + rr->RequireGoodbye = mDNSfalse; } // Now try to see if we can fit the update in the same packet (not fatal if we can't) SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); @@ -3139,7 +3159,7 @@ mDNSlocal void SendResponses(mDNS *const m) 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) responseptr = newptr; + if (newptr) { responseptr = newptr; rr->RequireGoodbye = mDNStrue; } SetNewRData(&rr->resrec, OldRData, oldrdlength); } else @@ -3367,7 +3387,8 @@ mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **quer rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList && // which is not already in the known answer list rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet ResourceRecordAnswersQuestion(&rr->resrec, q) && // which answers our question - rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow >= 0) // and it is less than half-way to expiry + rr->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) { *ka = rr; // Link this record into our known answer chain ka = &rr->NextInKAList; @@ -3892,7 +3913,7 @@ mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *c if (threshhold - RRExpireTime(rr) >= 0) // If we have records about to expire within a second if (delay - RRExpireTime(rr) < 0) // then delay until after they've been deleted delay = RRExpireTime(rr); - if (delay - start > 0) return(delay ? delay : 1); // Make sure we return non-zero if we want to delay + if (delay - start > 0) return(NonZeroTime(delay)); else return(0); } @@ -4651,7 +4672,7 @@ mDNSlocal int CompareRData(AuthRecord *our, CacheRecord *pkt) if (*pktptr > *ourptr) return(-1); // Our data is numerically lower; We lost if (*pktptr < *ourptr) return(+1); // Packet data is numerically lower; We won - debugf("CompareRData: How did we get here?"); + LogMsg("CompareRData ERROR: Invalid state"); return(-1); } @@ -4754,14 +4775,13 @@ mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const q int result = (int)our->resrec.rrclass - (int)m->rec.r.resrec.rrclass; if (!result) result = (int)our->resrec.rrtype - (int)m->rec.r.resrec.rrtype; if (!result) result = CompareRData(our, &m->rec.r); - switch (result) + if (result > 0) + debugf("ResolveSimultaneousProbe: %##s (%s): We won", our->resrec.name->c, DNSTypeName(our->resrec.rrtype)); + else if (result < 0) { - case 1: debugf("ResolveSimultaneousProbe: %##s (%s): We won", our->resrec.name->c, DNSTypeName(our->resrec.rrtype)); - break; - case 0: break; - case -1: debugf("ResolveSimultaneousProbe: %##s (%s): We lost", our->resrec.name->c, DNSTypeName(our->resrec.rrtype)); - mDNS_Deregister_internal(m, our, mDNS_Dereg_conflict); - goto exit; + debugf("ResolveSimultaneousProbe: %##s (%s): We lost", our->resrec.name->c, DNSTypeName(our->resrec.rrtype)); + mDNS_Deregister_internal(m, our, mDNS_Dereg_conflict); + goto exit; } } } @@ -5251,8 +5271,14 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, const mDNSInterfaceID InterfaceID) { int i; - const mDNSu8 *ptr = LocateAnswers(response, end); // We ignore questions (if any) in a DNS response packet - CacheRecord *CacheFlushRecords = (CacheRecord*)1; // "(CacheRecord*)1" is special (non-zero) end-of-list marker + + // We ignore questions (if any) in a DNS response packet + const mDNSu8 *ptr = LocateAnswers(response, end); + + // "(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 diff --git a/mDNSCore/mDNSDebug.h b/mDNSCore/mDNSDebug.h index 11c9737..0ff368f 100755 --- a/mDNSCore/mDNSDebug.h +++ b/mDNSCore/mDNSDebug.h @@ -23,6 +23,9 @@ Change History (most recent first): $Log: mDNSDebug.h,v $ +Revision 1.26 2005/07/04 22:40:26 cheshire +Additional debugging code to help catch memory corruption + Revision 1.25 2004/12/14 21:34:16 cheshire Add "#define ANSWER_REMOTE_HOSTNAME_QUERIES 0" and comment @@ -154,6 +157,8 @@ extern void LogMsgNoIdent(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1, #if MACOSX_MDNS_MALLOC_DEBUGGING >= 1 extern void *mallocL(char *msg, unsigned int size); extern void freeL(char *msg, void *x); +extern void LogMemCorruption(const char *format, ...); +extern void uds_validatelists(void); #else #define mallocL(X,Y) malloc(Y) #define freeL(X,Y) free(Y) @@ -179,6 +184,8 @@ extern void freeL(char *msg, void *x); #define LogOperation debugf #endif +#define ForceAlerts 0 + #ifdef __cplusplus } #endif diff --git a/mDNSCore/mDNSEmbeddedAPI.h b/mDNSCore/mDNSEmbeddedAPI.h index e2d402d..9f81dc8 100755 --- a/mDNSCore/mDNSEmbeddedAPI.h +++ b/mDNSCore/mDNSEmbeddedAPI.h @@ -60,6 +60,9 @@ Change History (most recent first): $Log: mDNSEmbeddedAPI.h,v $ +Revision 1.288 2005/12/21 03:24:58 cheshire + Code changes required to compile on EFI + Revision 1.287 2005/10/20 00:10:33 cheshire Add check to avoid crashing NAT gateways that have buggy DNS relay code @@ -998,7 +1001,17 @@ Merge in license terms from Quinn's copy, in preparation for Darwin release #ifndef __mDNSClientAPI_h #define __mDNSClientAPI_h +#if defined(EFI32) || defined(EFI64) +// EFI doesn't have stdarg.h +#include "Tiano.h" +#define va_list VA_LIST +#define va_start(a, b) VA_START(a, b) +#define va_end(a) VA_END(a) +#define va_arg(a, b) VA_ARG(a, b) +#else #include // stdarg.h is required for for va_list support for the mDNS_vsnprintf declaration +#endif + #include "mDNSDebug.h" #ifdef __cplusplus diff --git a/mDNSCore/uDNS.c b/mDNSCore/uDNS.c index c129f9d..2e7c354 100755 --- a/mDNSCore/uDNS.c +++ b/mDNSCore/uDNS.c @@ -23,6 +23,11 @@ Change History (most recent first): $Log: uDNS.c,v $ +Revision 1.226 2005/12/20 02:46:33 cheshire + mDNSPosix wide-area registration broken +Check too strict -- we can still do wide-area registration (without NAT-PMP) +without having to know our gateway address + Revision 1.225 2005/10/21 22:51:17 cheshire Add check to avoid crashing NAT gateways that have buggy DNS relay code Refinement: Shorten "check-for-broken-dns-relay" to just "dnsbugtest" @@ -1962,7 +1967,7 @@ mDNSexport void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, co if (router) u->Router = *router; else u->Router.ip.v4.NotAnInteger = 0; // setting router to zero indicates that nat mappings must be reestablished when router is reset - if ((v4Changed || RouterChanged || v6Changed) && (v4addr && router)) + if ((v4Changed || RouterChanged || v6Changed) && v4addr) { // don't update these unless we've got V4 UpdateHostnameRegistrations(m); diff --git a/mDNSMacOS9/mDNSMacOS9.c b/mDNSMacOS9/mDNSMacOS9.c index 61da0a0..0dbbe5e 100644 --- a/mDNSMacOS9/mDNSMacOS9.c +++ b/mDNSMacOS9/mDNSMacOS9.c @@ -23,6 +23,9 @@ Change History (most recent first): $Log: mDNSMacOS9.c,v $ +Revision 1.44 2005/09/16 21:06:50 cheshire +Use mDNS_TimeNow_NoLock macro, instead of writing "mDNSPlatformRawTime() + m->timenow_adjust" all over the place + Revision 1.43 2004/12/17 23:37:49 cheshire Guard against repeating wireless dissociation/re-association (and other repetitive configuration changes) @@ -705,7 +708,7 @@ mDNSlocal void ScheduleNextTimerCallback(const mDNS *const m) { if (m->mDNSPlatformStatus == mStatus_NoError) { - SInt32 interval = m->NextScheduledEvent - (mDNSPlatformRawTime() + m->timenow_adjust); + 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; diff --git a/mDNSMacOSX/LegacyNATTraversal.c b/mDNSMacOSX/LegacyNATTraversal.c index 7a57d92..3816d58 100644 --- a/mDNSMacOSX/LegacyNATTraversal.c +++ b/mDNSMacOSX/LegacyNATTraversal.c @@ -3,14 +3,14 @@ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -18,12 +18,18 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_LICENSE_HEADER_END@ Change History (most recent first): $Log: LegacyNATTraversal.c,v $ +Revision 1.14 2005/12/08 03:00:33 cheshire + Byte order bugs in Legacy NAT traversal code + +Revision 1.13 2005/09/07 18:23:05 ksekar + Off-by-one overflow in LegacyNATTraversal + Revision 1.12 2005/07/22 21:36:16 ksekar Fix GCC 4.0/Intel compiler warnings @@ -106,6 +112,8 @@ Revision 1.1 2004/08/18 17:35:41 ksekar // TODO: remove later and do variable length #define MAX_SOAPMSGSIZE 65536 +// This code accidentally closes fd 0 all over the place +// To stop that messing up the mDNSResponder core, we trap it and prevent it static int safe_close(int fd) { if (fd < 3) { /* LogMsg("safe_close: ERROR sd %d < 3", fd); */ return(-1); } @@ -114,6 +122,10 @@ static int safe_close(int fd) #define close safe_close +// This code uses fprintf(stderr, ...) and similar to log error messages +// We redirect all of them to syslog using our LogMsg mechanism +#define fprintf(file, ...) LogMsg(__VA_ARGS__) + //////////////////////////////////////////////////////////////////////// // NetAddr Functions //////////////////////////////////////////////////////////////////////// @@ -436,7 +448,7 @@ typedef struct tagIPINFO { int iFlags; char szIfName[IFNAMELEN]; /* Interface name */ - unsigned char abIP[IPLEN]; /* IP in host byte order */ + unsigned char abIP[IPLEN]; unsigned short wPort; } IPINFO, *PIPINFO, **PPIPINFO; @@ -444,10 +456,13 @@ typedef struct hostent HOSTENT, *PHOSTENT; static unsigned long GetNATIPNetmask(unsigned long dwIP) { - if ((dwIP & 0xFF000000) == 0x0A000000) return 0xFF000000; - if ((dwIP & 0xFFF00000) == 0xAC100000) return 0xFFF00000; - if ((dwIP & 0xFFFF0000) == 0xC0a80000) return 0xFFFF0000; - + static const union { uint8_t b[4]; uint32_t l; } mask_10 = { { 255, 0, 0, 0 } }; // Mask for 10/8 + static const union { uint8_t b[4]; uint32_t l; } mask172 = { { 255, 240, 0, 0 } }; // Mask for 172.16/12 + static const union { uint8_t b[4]; uint32_t l; } mask192 = { { 255, 255, 0, 0 } }; // Mask for 192.168/16 + uint8_t *p = (uint8_t *)&dwIP; + if (p[0] == 10 ) return mask_10.l; + if (p[0] == 172 && (p[1] & 0xF0) == 16) return mask172.l; + if (p[0] == 192 && p[1] == 168 ) return mask192.l; return 0; /* No NAT IP */ } @@ -531,8 +546,7 @@ static int GetIPInfo(PPIPINFO ppIPInfo) { case AF_INET: memcpy(pIPInfo[iNum].szIfName, ifr->ifr_name, IFNAMELEN); - dwIP = - ntohl(((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr.s_addr); + dwIP = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr.s_addr; memcpy(pIPInfo[iNum].abIP, &dwIP, sizeof(unsigned long)); if (ifrcopy.ifr_flags & IFF_POINTOPOINT) pIPInfo[iNum].iFlags |= ISPPP; @@ -605,7 +619,7 @@ static int SSDPListen() saddr.sin_family = AF_INET; //saddr.sin_addr.s_addr = inet_addr(SSDP_IP); //saddr.sin_port = htons(SSDP_PORT); - saddr.sin_addr.s_addr = htonl(g_dwLocalIP); + saddr.sin_addr.s_addr = g_dwLocalIP; saddr.sin_port = 0; // and set the multicast add_member structure @@ -650,7 +664,7 @@ static int EventListen() bzero(&saddr, sizeof(saddr)); saddr.sin_len = sizeof(saddr); saddr.sin_family = AF_INET; - saddr.sin_addr.s_addr = htonl(g_dwLocalIP); + saddr.sin_addr.s_addr = g_dwLocalIP; saddr.sin_port = htons(g_wEventPort); // return if okay @@ -721,7 +735,7 @@ static void DumpHex(char *buf, int len) nexti = i + 16; endj = (nexti > len) ? len : nexti; for (j = i; j < endj; j++) - fprintf(g_log, "%02x ", buf[j] & 0xff); + fprintf(g_log, "%02x %c ", buf[j] & 0xff, buf[j]); if (j == len) { if ((j % 16) != 0) { char pad[3 * 16 + 1]; // don't need the last 3 bytes anyway @@ -760,7 +774,7 @@ static char *FindHTTPHeaderNewLine(char *pbuf, int iBufSize, int *pfEOH) result = memchr(pbuf, '\r', iBufSize); if (result == NULL) { if (g_fLogging & NALOG_INFO0) { - fprintf(g_log, "FindHTTPHeaderNewLine: er @(%d)\n", i); + fprintf(g_log, "FindHTTPHeaderNewLine: er @(%d/%d)\n", i, iBufSize); fflush(g_log); } return NULL; @@ -898,7 +912,15 @@ static PHTTPResponse NewHTTPResponse_sz( pszEOL = FindHTTPHeaderNewLine(pszEOL, iBufferSize - (pszEOL - pBuf), // remainder size &fEOH); - if (pszEOL == NULL) goto cleanup; // syntax error + if (pszEOL == NULL) { + if (g_fLogging & NALOG_INFO0) { + fprintf(g_log, "NewHTTPResponse_sz: er reading header field %d @ %lu / %lu\n", + iNumHeaders, pHeader->pszName - pBuf, iBufferSize); + DumpHex(pszHTTPResponse, iBufferSize); + fflush(g_log); + } + goto cleanup; // syntax error + } *pszEOL = '\0'; // terminate this string pszEOL += 2; // point to beginning of next line @@ -1308,12 +1330,9 @@ static void *TCPProc(void *in) char response[2000]; PHTTPResponse resp; int n; - sprintf(callback, "%lu.%lu.%lu.%lu:%u", - (g_dwLocalIP >> 24) & 0xFF, - (g_dwLocalIP >> 16) & 0xFF, - (g_dwLocalIP >> 8) & 0xFF, - (g_dwLocalIP >> 0) & 0xFF, - g_wEventPort); + sprintf(callback, "%u.%u.%u.%u:%u", + ((uint8_t*)&g_dwLocalIP)[0], ((uint8_t*)&g_dwLocalIP)[1], + ((uint8_t*)&g_dwLocalIP)[2], ((uint8_t*)&g_dwLocalIP)[3], g_wEventPort); n = sprintf((char *)buf, szEventMsgSubscribeFMT, @@ -1328,7 +1347,7 @@ static void *TCPProc(void *in) if (n > 0) { response[n] = '\0'; - resp = NewHTTPResponse_sz((char *)buf, n, TRUE); + resp = NewHTTPResponse_sz((char *)response, n+1, TRUE); if (NULL != resp) { ////TracePrint(ELL_TRACE, "UPnP Subscribe returns %s/%d\n", resp->pszStatus, n); @@ -1504,7 +1523,7 @@ static void *UDPProc(void *in) if (!FD_ISSET(g_sUDP, &readfds)) continue; recvaddrlen = sizeof(recvaddr); - n = recvfrom(g_sUDP, buf, sizeof(buf), 0, + n = recvfrom(g_sUDP, buf, sizeof(buf)-1, 0, (struct sockaddr *)&recvaddr, &recvaddrlen); if (n < 0) { if (g_fLogging & NALOG_ERROR) @@ -1517,7 +1536,7 @@ static void *UDPProc(void *in) } buf[n] = '\0'; if (strncmp((char *)buf, "HTTP/1.1", 8) == 0) { - PHTTPResponse pResponse = NewHTTPResponse_sz((char *)buf, n, TRUE); + PHTTPResponse pResponse = NewHTTPResponse_sz((char *)buf, n+1, TRUE); PrintHTTPResponse(pResponse); if (DiscoverRouter(pResponse) == 0) { @@ -1536,7 +1555,7 @@ static void *UDPProc(void *in) // temporarily use this to fudge - will have the exact same // parsing, only status/reason set to "*" and "HTTP/1.1". // TODO: add support for HTTP requests - PHTTPResponse pResponse = NewHTTPResponse_sz((char *)buf, n, TRUE); + PHTTPResponse pResponse = NewHTTPResponse_sz((char *)buf, n+1, TRUE); if (DiscoverRouter(pResponse) == 0) { time_t now = time(NULL); @@ -1858,13 +1877,14 @@ GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed); FD_ZERO(&readfds); FD_SET(s, &readfds); - //timeout.tv_sec = g_iFunctionTimeout / U_TOGRAN; - //timeout.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN; - // just do flat 2 sec now, since connection already established - timeout.tv_sec = 1; - timeout.tv_usec = 0; - + // In testing, the Linksys Wireless-G Broadband Router "WRT54GS" takes + // up to four seconds to respond, and even then only a partial response, + // with the remainder coming in a second TCP segment half a second later. + // Accordingly, we wait up to five seconds for the initial data, and then after that + // wait one second after subsequent TCP segments, in care more data is still coming. + timeout.tv_sec = iBufLen ? 1 : 5; + timeout.tv_usec = 0; iRet = select(s+1, &readfds, NULL, NULL, &timeout); if (iRet <= 0) { @@ -1929,7 +1949,7 @@ GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed); //fprintf(stderr, "2 -- \n"); if (g_fLogging & NALOG_INFO1) - fprintf(g_log, "done recv @%lu\n", time(NULL)); + fprintf(g_log, "SendTCPMsg_saddr_2part done recv %d @ %lu\n", iBufLen, time(NULL)); if (result == NULL) { // if caller just want to send/display msgs if (g_fLogging & NALOG_DUMP) @@ -2018,7 +2038,7 @@ static int SendTCPMsg_saddr_parse( } if (g_fLogging & NALOG_INFO1) - fprintf(g_log, "SendTCPMsg/parse: Before Sending TCP Msg: %d == %lu?\n", + fprintf(g_log, "SendTCPMsg_saddr_parse: Before Sending TCP Msg: %d == %lu?\n", iLen, strlen(msg)); if (g_fLogging & NALOG_DUMP) fprintf(g_log,"Sending TCP msg:\n[%s]\n", msg); @@ -2070,12 +2090,14 @@ static int SendTCPMsg_saddr_parse( FD_ZERO(&readfds); FD_SET(s, &readfds); - //timeout.tv_sec = g_iFunctionTimeout / U_TOGRAN; - //timeout.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN; - // just do flat 2 sec now, since connection already established - timeout.tv_sec = 1; - timeout.tv_usec = 0; + // In testing, the Linksys Wireless-G Broadband Router "WRT54GS" takes + // up to four seconds to respond, and even then only a partial response, + // with the remainder coming in a second TCP segment half a second later. + // Accordingly, we wait up to five seconds for the initial data, and then after that + // wait one second after subsequent TCP segments, in care more data is still coming. + timeout.tv_sec = iBufLen ? 1 : 5; + timeout.tv_usec = 0; iRet = select(s+1, &readfds, NULL, NULL, &timeout); if (iRet <= 0) { //fprintf(stderr, "**********: select failed (%d/%d)\n", iRet, errno); @@ -2102,6 +2124,8 @@ static int SendTCPMsg_saddr_parse( if (resultSize <= iBufLen) { char t[1000]; i = recv(s, &t, 1000, 0); + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "SendTCPMsg_saddr_parse discarding %d bytes\n", i); if (i== 0) break; // Note that there's no dump here - prevents DoS attack from // flooding the logs/diskspace @@ -2109,6 +2133,8 @@ static int SendTCPMsg_saddr_parse( } i = recv(s, result + iBufLen, resultSize - iBufLen, 0); + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "SendTCPMsg_saddr_parse read %d bytes (%d/%d)\n", i, iBufLen, resultSize); if (0 == i) { break; @@ -2131,7 +2157,7 @@ static int SendTCPMsg_saddr_parse( //fprintf(stderr, "p -- \n"); if (g_fLogging & NALOG_INFO1) - fprintf(g_log, "done recv @%lu\n", time(NULL)); + fprintf(g_log, "SendTCPMsg_saddr_parse done recv %d @ %lu\n", iBufLen, time(NULL)); if (result == NULL) { // if caller just want to send/display msgs if (g_fLogging & NALOG_DUMP) @@ -2243,6 +2269,8 @@ static PHTTPResponse SendSOAPMsgControlAction( &g_saddrRouterSOAP); } + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "SendSOAPMsgControlAction iResultLen %d\n", iResultLen); if (iResultLen > 0) { if (iResultLen > MAX_SOAPMSGSIZE) { if (g_fLogging & NALOG_ALERT) @@ -2473,10 +2501,8 @@ static void ParseURL( szBuf, pszHostPort?pszHostPort:"", pszPath?pszPath:"", - (psaddr->sin_addr.s_addr >> 24) & 0xff, - (psaddr->sin_addr.s_addr >> 16) & 0xff, - (psaddr->sin_addr.s_addr >> 8) & 0xff, - (psaddr->sin_addr.s_addr >> 0) & 0xff, + ((uint8_t*)&psaddr->sin_addr.s_addr)[0], ((uint8_t*)&psaddr->sin_addr.s_addr)[1], + ((uint8_t*)&psaddr->sin_addr.s_addr)[2], ((uint8_t*)&psaddr->sin_addr.s_addr)[3], psaddr->sin_port); #endif } @@ -2606,17 +2632,13 @@ static void GetIPByName(char *hostname, unsigned long *ip_ret) if (pHEnt == NULL) { if (g_fLogging & NALOG_ALERT) fprintf(g_log, "Can't translate [%s] to IP...\n", hostname); - g_dwLocalIP = htonl(INADDR_ANY); + g_dwLocalIP = INADDR_ANY; return; } - ip = ntohl(*(unsigned long *)(pHEnt->h_addr)); + ip = *(unsigned long *)(pHEnt->h_addr); if (g_fLogging & NALOG_INFO1) - fprintf(g_log, "hostname [%s] to ip: %ld.%ld.%ld.%ld\n", - hostname, - (ip >> 24) & 0xff, - (ip >> 16) & 0xff, - (ip >> 8) & 0xff, - (ip >> 0) & 0xff); + fprintf(g_log, "hostname [%s] to ip: %u.%u.%u.%u\n", hostname, + ((uint8_t*)&ip)[0], ((uint8_t*)&ip)[1], ((uint8_t*)&ip)[2], ((uint8_t*)&ip)[3]); } *ip_ret = ip; } @@ -2684,9 +2706,8 @@ mStatus LNT_UnmapPort(mDNSIPPort PubPort, mDNSBool tcp) //unsigned long dwIP; Property propArgs[3]; PHTTPResponse resp; - unsigned short port = PubPort.NotAnInteger; int protocol = tcp ? IPPROTO_TCP : IPPROTO_UDP; - sprintf(szEPort, "%u", port); + sprintf(szEPort, "%u", mDNSVal16(PubPort)); bzero(propArgs, sizeof(propArgs)); propArgs[0].pszName = "NewRemoteHost"; @@ -2734,26 +2755,19 @@ extern mStatus LNT_MapPort(mDNSIPPort priv, mDNSIPPort pub, mDNSBool tcp) char descr[40]; Property propArgs[8]; PHTTPResponse resp; - unsigned short iport = priv.NotAnInteger; - unsigned short eport = pub.NotAnInteger; int protocol = tcp ? IPPROTO_TCP : IPPROTO_UDP; - - if (NA_E_EXISTS == GetMappingUnused(eport, protocol)) + if (NA_E_EXISTS == GetMappingUnused(mDNSVal16(pub), protocol)) return mStatus_AlreadyRegistered; //DeletePortMapping(eport, protocol); - sprintf(szEPort, "%u", eport); - - sprintf(szIPort, "%u", iport); + sprintf(szEPort, "%u", mDNSVal16(pub)); + sprintf(szIPort, "%u", mDNSVal16(priv)); dwIP = g_dwLocalIP; sprintf(szLocalIP, "%u.%u.%u.%u", - (unsigned int)((dwIP >> 24) & 0xff), - (unsigned int)((dwIP >> 16) & 0xff), - (unsigned int)((dwIP >> 8) & 0xff), - (unsigned int)((dwIP >> 0) & 0xff)); + ((uint8_t*)&dwIP)[0], ((uint8_t*)&dwIP)[1], ((uint8_t*)&dwIP)[2], ((uint8_t*)&dwIP)[3]); bzero(propArgs, sizeof(propArgs)); propArgs[0].pszName = "NewRemoteHost"; @@ -2783,7 +2797,7 @@ extern mStatus LNT_MapPort(mDNSIPPort priv, mDNSIPPort pub, mDNSBool tcp) propArgs[5].pszValue = "1"; propArgs[5].pszType = "boolean"; propArgs[6].pszName = "NewPortMappingDescription"; - sprintf(descr, "iC%u", eport); + sprintf(descr, "iC%u", mDNSVal16(pub)); //propArgs[6].pszValue = "V"; propArgs[6].pszValue = descr; propArgs[6].pszType = "string"; @@ -2791,9 +2805,15 @@ extern mStatus LNT_MapPort(mDNSIPPort priv, mDNSIPPort pub, mDNSBool tcp) propArgs[7].pszValue = "0"; propArgs[7].pszType = "ui4"; + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "Sending AddPortMapping priv %u pub %u\n", mDNSVal16(priv), mDNSVal16(pub)); + resp = SendSOAPMsgControlAction( "AddPortMapping", 8, propArgs, FALSE); + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "AddPortMapping resp %p\n", resp); + if (resp == NULL) { return mStatus_NATTraversal; } @@ -2914,11 +2934,13 @@ int LegacyNATInit(void) pthread_attr_t attr; int iRet; //struct timeval tv; + LogOperation("LegacyNATInit"); static int fFirstInitLocks = TRUE; FILE *log = NULL; g_fLogging = 0; + //g_fLogging = ~0; // Turns ALL logging on g_log = stderr; SetLocalIP(); diff --git a/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.icns b/mDNSMacOSX/PreferencePane/BonjourPref.icns similarity index 56% rename from mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.icns rename to mDNSMacOSX/PreferencePane/BonjourPref.icns index 028a3a2d883e2a9be53e1bdd6b96c0cc57520edb..97b560f6e303dff0cf5b1151c411dd18d4eafae0 100644 GIT binary patch delta 5845 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 TznELR<@KV?VF36s!RzziY3ZbN literal 0 HcmV?d00001 diff --git a/mDNSMacOSX/PreferencePane/ConfigurationAuthority.c b/mDNSMacOSX/PreferencePane/ConfigurationAuthority.c index b0464a8..a42d9a5 100644 --- a/mDNSMacOSX/PreferencePane/ConfigurationAuthority.c +++ b/mDNSMacOSX/PreferencePane/ConfigurationAuthority.c @@ -44,6 +44,9 @@ Change History (most recent first): $Log: ConfigurationAuthority.c,v $ +Revision 1.2 2005/08/07 22:48:05 mkrochma + Bonjour Pref Pane returns -927 when "system.preferences" is not shared + Revision 1.1 2005/02/05 02:28:22 cheshire Add Preference Pane to facilitate testing of DDNS & wide-area features @@ -144,8 +147,6 @@ OSStatus InitConfigAuthority(void) } require_noerr( err, AuthSetFailed); - (void) AttemptAcquireAuthority( false); - AuthSetFailed: GetStrFailed: NewAuthFailed: @@ -177,11 +178,7 @@ OSStatus ReleaseAuthority(void) Boolean CurrentlyAuthorized(void) { - OSStatus err; - - err = AuthorizationCopyRights(gAuthRef, &gAuthSet, (AuthorizationEnvironment*) NULL, - (AuthorizationFlags) 0, (AuthorizationRights**) NULL); - + OSStatus err = AttemptAcquireAuthority(true); return err == noErr; } diff --git a/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.tiff b/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.tiff deleted file mode 100644 index 985ae197b065eec4b20bd10cd963749f05e48bb0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17394 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 diff --git a/mDNSMacOSX/PreferencePane/English.lproj/DNSServiceDiscoveryPref.nib/info.nib b/mDNSMacOSX/PreferencePane/English.lproj/DNSServiceDiscoveryPref.nib/info.nib index 3759d87..e8dbd92 100644 --- a/mDNSMacOSX/PreferencePane/English.lproj/DNSServiceDiscoveryPref.nib/info.nib +++ b/mDNSMacOSX/PreferencePane/English.lproj/DNSServiceDiscoveryPref.nib/info.nib @@ -5,14 +5,14 @@ IBDocumentLocation 32 63 547 281 0 0 1024 746 IBFramework Version - 364.0 + 439.0 IBOpenObjects - 12 255 333 + 12 IBSystem Version - 7U13 + 8F23 diff --git a/mDNSMacOSX/PreferencePane/English.lproj/DNSServiceDiscoveryPref.nib/keyedobjects.nib b/mDNSMacOSX/PreferencePane/English.lproj/DNSServiceDiscoveryPref.nib/keyedobjects.nib index d024da1bdedbb92d7a5eb555d2a430fbb2e6b9c9..eec01ee4ea446697e9234bbacc843e0b268ca2a9 100644 GIT binary patch literal 21082 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 literal 17441 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 ddnswriteconfig (Bonjour PreferencePane) vulnerability +Use installtool instead of requiring ddnswriteconfig to self-install + Revision 1.2 2005/02/10 22:35:20 cheshire Update name @@ -88,7 +92,8 @@ OSStatus EnsureToolInstalled(void) int status; OSStatus err = noErr; const char *args[] = { kToolPath, "0", "V", NULL }; - char toolPath[PATH_MAX] = {}; + char toolSourcePath[PATH_MAX] = {}; + char toolInstallerPath[PATH_MAX] = {}; if (gToolApproved) return noErr; @@ -106,15 +111,17 @@ OSStatus EnsureToolInstalled(void) bundleURL = CFBundleCopyBundleURL(CFBundleGetBundleWithIdentifier(CFSTR("com.apple.preference.bonjour")) ); if (bundleURL != NULL) { - CFURLGetFileSystemRepresentation(bundleURL, false, (UInt8*) toolPath, sizeof toolPath); - strcat(toolPath, "/Contents/Resources/" kToolName); + 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(toolPath), toolPath, 0 }; + AuthorizationItem aewpRight = { kAuthorizationRightExecute, strlen(toolInstallerPath), toolInstallerPath, 0 }; AuthorizationItemSet rights = { 1, &aewpRight }; AuthorizationRef authRef; @@ -123,8 +130,8 @@ OSStatus EnsureToolInstalled(void) kAuthorizationFlagPreAuthorize, &authRef); if (err == noErr) { - args[2] = "I"; - err = AuthorizationExecuteWithPrivileges(authRef, toolPath, 0, (char * const *)&args[1], (FILE**) NULL); + char *installerargs[] = { toolSourcePath, NULL }; + err = AuthorizationExecuteWithPrivileges(authRef, toolInstallerPath, 0, installerargs, (FILE**) NULL); if (err == noErr) gToolApproved = true; (void) AuthorizationFree(authRef, kAuthorizationFlagDestroyRights); diff --git a/mDNSMacOSX/PreferencePane/PrivilegedOperations.h b/mDNSMacOSX/PreferencePane/PrivilegedOperations.h index 706be05..c5c68a0 100644 --- a/mDNSMacOSX/PreferencePane/PrivilegedOperations.h +++ b/mDNSMacOSX/PreferencePane/PrivilegedOperations.h @@ -42,6 +42,10 @@ Change History (most recent first): $Log: PrivilegedOperations.h,v $ +Revision 1.4 2005/06/04 04:50:00 cheshire + ddnswriteconfig (Bonjour PreferencePane) vulnerability +Use installtool instead of requiring ddnswriteconfig to self-install + Revision 1.3 2005/02/16 00:17:35 cheshire Don't create empty arrays -- CFArrayGetValueAtIndex(array,0) returns an essentially random (non-null) result for empty arrays, which can lead to code crashing if it's not sufficiently defensive. @@ -57,12 +61,11 @@ Add Preference Pane to facilitate testing of DDNS & wide-area features #include #include -#define PRIV_OP_TOOL_VERS 2 +#define PRIV_OP_TOOL_VERS 3 -#define kToolHome "/Library/Application Support/" -#define kToolDir "Bonjour" -#define kToolName "ddnswriteconfig" -#define kToolPath kToolHome kToolDir "/" kToolName +#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") diff --git a/mDNSMacOSX/PreferencePane/ddnswriteconfig.m b/mDNSMacOSX/PreferencePane/ddnswriteconfig.m index f9383c6..ff3080e 100644 --- a/mDNSMacOSX/PreferencePane/ddnswriteconfig.m +++ b/mDNSMacOSX/PreferencePane/ddnswriteconfig.m @@ -44,6 +44,10 @@ Change History (most recent first): $Log: ddnswriteconfig.m,v $ +Revision 1.4 2005/06/04 04:47:47 cheshire + ddnswriteconfig (Bonjour PreferencePane) vulnerability +Remove self-installing capability of ddnswriteconfig + Revision 1.3 2005/02/16 00:17:35 cheshire Don't create empty arrays -- CFArrayGetValueAtIndex(array,0) returns an essentially random (non-null) result for empty arrays, which can lead to code crashing if it's not sufficiently defensive. @@ -80,54 +84,6 @@ Add Preference Pane to facilitate testing of DDNS & wide-area features static AuthorizationRef gAuthRef = 0; -int -CopySUIDTool(const char *srcPath, const char *dstPath) -// Copy a tool from srcPath to dstPath and set its 'x' and SUID bits. Return 0 on success. -{ - int srcFD, dstFD, err = 0; - off_t len, written; - void *pSrc; - - srcFD = open( srcPath, O_RDONLY, (mode_t) 0); - require_action( srcFD > 0, OpenSrcFailed, err=errno;); - - len = lseek( srcFD, 0, SEEK_END); - require_action( len > 0, GetSrcLenFailed, err=errno;); - pSrc = mmap( NULL, len, PROT_READ, MAP_FILE, srcFD, 0); - require_action( pSrc != (void*)-1, MMapFailed, err=errno;); - - dstFD = open( dstPath, O_RDWR | O_CREAT | O_TRUNC, (mode_t) 0); - require_action( dstFD > 0, OpenDstFailed, err=errno;); - - written = write( dstFD, pSrc, len); - require_action( written == len, WriteFailed, err=errno;); - - err = fchmod( dstFD, S_IRUSR | S_IXUSR | S_ISUID | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); - -WriteFailed: - close( dstFD); -OpenDstFailed: - munmap( pSrc, len); -MMapFailed: -GetSrcLenFailed: - close( srcFD); -OpenSrcFailed: - return err; -} - - -int -InstallRootTool( const char *srcPath) -{ - if (geteuid() != 0) - return -1; // failure; not running as root - - (void) mkdir(kToolHome kToolDir, S_IRUSR | S_IXUSR | S_IWUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); - - return CopySUIDTool( srcPath, kToolPath); -} - - OSStatus WriteArrayToDynDNS(CFStringRef arrayKey, CFArrayRef domainArray) { @@ -435,7 +391,6 @@ int main( int argc, char **argv) /* argv[0] is the exec path; argv[1] is a fd for input data; argv[2]... are operation codes. The tool supports the following operations: V -- exit with status PRIV_OP_TOOL_VERS - I -- install self as suid-root tool into system (must be run as root) A -- read AuthInfo from input pipe Wd -- write registration domain to dynamic store Wb -- write browse domain to dynamic store @@ -444,19 +399,10 @@ int main( int argc, char **argv) */ { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - int commFD = -1, iArg, savedUID, result = 0; - - if ( argc == 3 && 0 == strcmp( argv[2], "I")) { - return InstallRootTool( argv[0]); - } + int commFD = -1, iArg, result = 0; - savedUID = geteuid(); -#if 1 if ( 0 != seteuid( 0)) return -1; -#else - sleep( 10); -#endif if ( argc == 3 && 0 == strcmp( argv[2], "V")) return PRIV_OP_TOOL_VERS; diff --git a/mDNSMacOSX/PreferencePane/installtool b/mDNSMacOSX/PreferencePane/installtool new file mode 100755 index 0000000..8fa79ad --- /dev/null +++ b/mDNSMacOSX/PreferencePane/installtool @@ -0,0 +1,100 @@ +#!/usr/bin/perl +# +# File: installtool +# +# Abstract: Copy "ddnswriteconfig" to Application Support and make it setuid root. +# +# Copyright: (c) Copyright 2005 Apple Computer, Inc. All rights reserved. +# +# Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. +# ("Apple") in consideration of your agreement to the following terms, and your +# use, installation, modification or redistribution of this Apple software +# constitutes acceptance of these terms. If you do not agree with these terms, +# please do not use, install, modify or redistribute this Apple software. +# +# In consideration of your agreement to abide by the following terms, and subject +# to these terms, Apple grants you a personal, non-exclusive license, under Apple's +# copyrights in this original Apple software (the "Apple Software"), to use, +# reproduce, modify and redistribute the Apple Software, with or without +# modifications, in source and/or binary forms; provided that if you redistribute +# the Apple Software in its entirety and without modifications, you must retain +# this notice and the following text and disclaimers in all such redistributions of +# the Apple Software. Neither the name, trademarks, service marks or logos of +# Apple Computer, Inc. may be used to endorse or promote products derived from the +# Apple Software without specific prior written permission from Apple. Except as +# expressly stated in this notice, no other rights or licenses, express or implied, +# are granted by Apple herein, including but not limited to any patent rights that +# may be infringed by your derivative works or by other works in which the Apple +# Software may be incorporated. +# +# The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO +# WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED +# WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN +# COMBINATION WITH YOUR PRODUCTS. +# +# IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION +# OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT +# (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Change History (most recent first): +# +# $Log: installtool,v $ +# Revision 1.1 2005/06/04 04:51:48 cheshire +# ddnswriteconfig (Bonjour PreferencePane) vulnerability +# Added separate "installtool" script instead of making ddnswriteconfig self-install +# +# Create the Bonjour subdirectory. +# Copy ARGV[0] to $dest and set owner and suid permissions. +# +# This script will be run as root by the AEWP trampoline. +# + +use File::Temp qw/ :mktemp /; + +$dest_dir = "/Library/Application Support/Bonjour"; +$dest = $dest_dir . "/ddnswriteconfig"; + +$template = ".XXXXXX"; + +# Perl seems to think this code is running setuid root, so it applies its security checks. +# See . +# In fact this is NOT a setuid script. It is a normal unprivileged user-level script -- +# but it is run as root when properly authorized by a user with an admin password, +# via the AuthorizationExecuteWithPrivileges() call. +# We therefore have to do this trick pattern match to 'untaint' the source file specified in $ARGV[0]. +if ($ARGV[0] =~ /^(.+)$/) { $src = $1; } + +# Also clear $ENV{PATH} so we don't get "Insecure $ENV{PATH}" fatal errors +$ENV{PATH} = ""; + +if (! -d $dest_dir) { + $dest_tmp_dir = mkdtemp ($dest_dir . $template); + (chown 0, 80, $dest_tmp_dir) or cleanup_dir(); + (chmod 0755, $dest_tmp_dir) or cleanup_dir(); + (rename $dest_tmp_dir, $dest_dir) or cleanup_dir(); +} + +$dest_tmp = mktemp ($dest . $template); + +if ($src ne '') { + system ('/bin/cp', '-f', $src, $dest_tmp) and cleanup(); + (chown 0, 80, $dest_tmp) or cleanup(); + (chmod 04555, $dest_tmp) or cleanup(); + (rename $dest_tmp, $dest) or cleanup(); +} +exit (0); + +sub cleanup { + unlink $dest_tmp; + exit (1); +} + +sub cleanup_dir { + unlink $dest_tmp_dir; + exit (1); +} diff --git a/mDNSMacOSX/daemon.c b/mDNSMacOSX/daemon.c index 7794479..d2b1b16 100644 --- a/mDNSMacOSX/daemon.c +++ b/mDNSMacOSX/daemon.c @@ -36,9 +36,21 @@ Change History (most recent first): $Log: daemon.c,v $ -Revision 1.255.2.1 2005/07/22 21:45:04 ksekar +Revision 1.260 2005/11/07 01:51:58 cheshire + Include list of configured DNS servers in SIGINFO output + +Revision 1.259 2005/07/22 21:50:55 ksekar Fix GCC 4.0/Intel compiler warnings +Revision 1.258 2005/07/04 22:40:26 cheshire +Additional debugging code to help catch memory corruption + +Revision 1.257 2005/03/28 19:28:55 cheshire +Fix minor typos in LogOperation() messages + +Revision 1.256 2005/03/17 22:01:22 cheshire +Tidy up alignment of lines to make code more readable + Revision 1.255 2005/03/09 00:48:43 cheshire QU packets getting sent too early on wake from sleep Move "m->p->NetworkChanged = 0;" line from caller to callee @@ -749,59 +761,104 @@ static DNSServiceRegistration *DNSServiceRegistrationList = NULL; 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", 0, buffer); + } + mDNSlocal void validatelists(mDNS *const m) { + // Check Mach client lists + DNSServiceDomainEnumeration *e; - DNSServiceBrowser *b; - DNSServiceResolver *l; - DNSServiceRegistration *r; - AuthRecord *rr; - CacheGroup *cg; - CacheRecord *cr; - DNSQuestion *q; - mDNSu32 slot; - NetworkInterfaceInfoOSX *i; - for (e = DNSServiceDomainEnumerationList; e; e=e->next) if (e->ClientMachPort == 0 || e->ClientMachPort == (mach_port_t)~0) - LogMsg("!!!! DNSServiceDomainEnumerationList: %p is garbage (%X) !!!!", e, e->ClientMachPort); + LogMemCorruption("DNSServiceDomainEnumerationList: %p is garbage (%X)", e, e->ClientMachPort); + DNSServiceBrowser *b; for (b = DNSServiceBrowserList; b; b=b->next) if (b->ClientMachPort == 0 || b->ClientMachPort == (mach_port_t)~0) - LogMsg("!!!! DNSServiceBrowserList: %p is garbage (%X) !!!!", b, b->ClientMachPort); + LogMemCorruption("DNSServiceBrowserList: %p is garbage (%X)", b, b->ClientMachPort); + DNSServiceResolver *l; for (l = DNSServiceResolverList; l; l=l->next) if (l->ClientMachPort == 0 || l->ClientMachPort == (mach_port_t)~0) - LogMsg("!!!! DNSServiceResolverList: %p is garbage (%X) !!!!", l, l->ClientMachPort); + LogMemCorruption("DNSServiceResolverList: %p is garbage (%X)", l, l->ClientMachPort); + DNSServiceRegistration *r; for (r = DNSServiceRegistrationList; r; r=r->next) if (r->ClientMachPort == 0 || r->ClientMachPort == (mach_port_t)~0) - LogMsg("!!!! DNSServiceRegistrationList: %p is garbage (%X) !!!!", r, r->ClientMachPort); + LogMemCorruption("DNSServiceRegistrationList: %p is garbage (%X)", r, r->ClientMachPort); + + // Check UDS client lists + uds_validatelists(); + // Check core mDNS lists + AuthRecord *rr; for (rr = m->ResourceRecords; rr; rr=rr->next) { if (rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) - LogMsg("!!!! ResourceRecords list: %p is garbage (%X) !!!!", rr, rr->resrec.RecordType); + LogMemCorruption("ResourceRecords list: %p is garbage (%X)", rr, rr->resrec.RecordType); if (rr->resrec.name != &rr->namestorage) - LogMsg("!!!! ResourceRecords list: %p name %p does not point to namestorage %p %##s", + 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->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) - LogMsg("!!!! DuplicateRecords list: %p is garbage (%X) !!!!", rr, rr->resrec.RecordType); + LogMemCorruption("DuplicateRecords list: %p is garbage (%X)", rr, rr->resrec.RecordType); + DNSQuestion *q; for (q = m->Questions; q; q=q->next) if (q->ThisQInterval == (mDNSs32)~0) - LogMsg("!!!! Questions list: %p is garbage (%lX) !!!!", q, q->ThisQInterval); + LogMemCorruption("Questions list: %p is garbage (%lX)", q, q->ThisQInterval); + CacheGroup *cg; + CacheRecord *cr; + mDNSu32 slot; FORALL_CACHERECORDS(slot, cg, cr) if (cr->resrec.RecordType == 0 || cr->resrec.RecordType == 0xFF) - LogMsg("!!!! Cache slot %lu: %p is garbage (%X) !!!!", slot, rr, rr->resrec.RecordType); + LogMemCorruption("Cache slot %lu: %p is garbage (%X)", slot, rr, rr->resrec.RecordType); + + // Check platform-layer lists + NetworkInterfaceInfoOSX *i; for (i = m->p->InterfaceList; i; i = i->next) if (!i->ifa_name) - LogMsg("!!!! InterfaceList: %p is garbage !!!!", i); + LogMemCorruption("InterfaceList: %p is garbage", i); + + // Check uDNS lists + + for (q = m->uDNS_info.ActiveQueries; q; q=q->next) + if (*(long*)q == (mDNSs32)~0) + LogMemCorruption("uDNS_info.ActiveQueries: %p is garbage (%lX)", q, *(long*)q); + + ServiceRecordSet *s; + for (s = m->uDNS_info.ServiceRegistrations; s; s=s->next) + if (s->next == (ServiceRecordSet*)~0) + LogMemCorruption("uDNS_info.ServiceRegistrations: %p is garbage (%lX)", s, s->next); + + for (rr = m->uDNS_info.RecordRegistrations; rr; rr=rr->next) + { + if (rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) + LogMemCorruption("uDNS_info.RecordRegistrations: %p is garbage (%X)", rr, rr->resrec.RecordType); + if (rr->resrec.name != &rr->namestorage) + LogMemCorruption("uDNS_info.RecordRegistrations: %p name %p does not point to namestorage %p %##s", + rr, rr->resrec.name->c, rr->namestorage.c, rr->namestorage.c); + } + + NATTraversalInfo *n; + for (n = m->uDNS_info.NATTraversals; n; n=n->next) + if (n->op > 2) LogMemCorruption("uDNS_info.NATTraversals: %p is garbage", n); + + for (n = m->uDNS_info.LLQNatInfo; n; n=n->next) + if (n->op > 2) LogMemCorruption("uDNS_info.LLQNatInfo: %p is garbage", n); } void *mallocL(char *msg, unsigned int size) @@ -902,8 +959,8 @@ mDNSlocal void AbortClient(mach_port_t ClientMachPort, void *m) while (qptr) { if (m && m != x) - LogMsg("%5d: DNSServiceBrowser(%##s) STOP; WARNING m %p != x %p", ClientMachPort, qptr->q.qname.c, m, x); - else LogOperation("%5d: DNSServiceBrowser(%##s) STOP", ClientMachPort, qptr->q.qname.c); + 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; @@ -925,8 +982,8 @@ mDNSlocal void AbortClient(mach_port_t ClientMachPort, void *m) DNSServiceResolver *x = *l; *l = (*l)->next; if (m && m != x) - LogMsg("%5d: DNSServiceResolver(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->i.name.c, m, x); - else LogOperation("%5d: DNSServiceResolver(%##s) STOP", ClientMachPort, x->i.name.c); + 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; @@ -976,19 +1033,21 @@ mDNSlocal void AbortClientWithLogMessage(mach_port_t c, char *reason, char *msg, 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); + + 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); + { + 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); + { + 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); } @@ -1410,7 +1469,7 @@ mDNSexport kern_return_t provide_DNSServiceResolverResolve_rpc(mach_port_t unuse DNSServiceResolverList = x; // Do the operation - LogOperation("%5d: DNSServiceResolver(%##s) START", client, x->i.name.c); + 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; } @@ -2283,6 +2342,7 @@ mDNSlocal void INFOCallback(void) DNSServiceResolver *l; DNSServiceRegistration *r; NetworkInterfaceInfoOSX *i; + DNSServer *s; LogMsgIdent(mDNSResponderVersionString, "---- BEGIN STATE LOG ----"); @@ -2324,6 +2384,9 @@ mDNSlocal void INFOCallback(void) &i->ifinfo.ip); } + for (s = mDNSStorage.uDNS_info.Servers; s; s = s->next) + LogMsgNoIdent("DNS Server %#a %##s", &s->addr, s->domain.c); + LogMsgIdent(mDNSResponderVersionString, "---- END STATE LOG ----"); } diff --git a/mDNSMacOSX/mDNSMacOSX.c b/mDNSMacOSX/mDNSMacOSX.c index 59806c4..d926cb1 100644 --- a/mDNSMacOSX/mDNSMacOSX.c +++ b/mDNSMacOSX/mDNSMacOSX.c @@ -3,14 +3,14 @@ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -18,12 +18,27 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_LICENSE_HEADER_END@ Change History (most recent first): $Log: mDNSMacOSX.c,v $ +Revision 1.323 2006/01/05 21:45:27 cheshire + Fix uninitialized structure member in IPv6 code + +Revision 1.322 2006/01/05 21:41:50 cheshire + Reword "mach_absolute_time went backwards" dialog + +Revision 1.321 2006/01/05 21:35:06 cheshire +Add (commented out) trigger value for testing "mach_absolute_time went backwards" notice + +Revision 1.320 2005/12/03 01:39:28 cheshire + Improve diagnostic message to indicate that message will not appear to customers + +Revision 1.319 2005/12/02 00:02:15 cheshire +Include recvmsg return value in error message + Revision 1.318 2005/10/20 00:10:34 cheshire Add check to avoid crashing NAT gateways that have buggy DNS relay code @@ -1116,7 +1131,7 @@ mDNSlocal void RemoveDefRegDomain(domainname *d) debugf("Requested removal of default registration domain %##s not in contained in list", d->c); } -mDNSexport void NotifyOfElusiveBug(const char *title, mDNSu32 radarid, const char *msg) +mDNSexport void NotifyOfElusiveBug(const char *title, const char *msg) // Both strings are UTF-8 text { static int notifyCount = 0; if (notifyCount) return; @@ -1138,11 +1153,15 @@ mDNSexport void NotifyOfElusiveBug(const char *title, mDNSu32 radarid, const cha } #endif - // Send a notification to the user to contact coreos-networking + LogMsg("%s", title); + LogMsg("%s", msg); + // Display a notification to the user notifyCount++; - CFStringRef alertHeader = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8); - CFStringRef alertFormat = CFSTR("Congratulations, you've reproduced an elusive bug. Please contact the owner of . %s"); - CFStringRef alertMessage = CFStringCreateWithFormat(NULL, NULL, alertFormat, radarid, msg); + 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); CFUserNotificationDisplayNotice(0.0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL, alertHeader, alertMessage, NULL); } @@ -1342,8 +1361,8 @@ mDNSlocal ssize_t myrecvfrom(const int s, void *const buffer, const size_t max, } if (msg.msg_controllen < (int)sizeof(struct cmsghdr)) { - if (numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) msg.msg_controllen %d < sizeof(struct cmsghdr) %lu", - s, msg.msg_controllen, 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) @@ -1518,8 +1537,10 @@ mDNSlocal void myCFSocketCallBack(const CFSocketRef cfs, const CFSocketCallBackT LogMsg("myCFSocketCallBack recvfrom skt %d error %d errno %d (%s) select %d (%spackets waiting) so_error %d so_nread %d fionread %d count %d", s1, err, save_errno, strerror(save_errno), selectresult, FD_ISSET(s1, &readfds) ? "" : "*NO* ", so_error, so_nread, fionread, count); if (numLogMessages > 5) - NotifyOfElusiveBug("Flaw in Kernel (select/recvfrom mismatch)", 3375328, - "Alternatively, you can send email to radar-3387020@group.apple.com. " + 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 @@ -1981,7 +2002,7 @@ mDNSlocal mStatus SetupSocket(mDNS *const m, CFSocketSet *cp, mDNSBool mcast, co listening_sockaddr6.sin6_family = AF_INET6; listening_sockaddr6.sin6_port = port.NotAnInteger; listening_sockaddr6.sin6_flowinfo = 0; -// listening_sockaddr6.sin6_addr = IN6ADDR_ANY_INIT; // Want to receive multicasts AND unicasts on this socket + listening_sockaddr6.sin6_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; } @@ -1999,8 +2020,10 @@ mDNSlocal mStatus SetupSocket(mDNS *const m, CFSocketSet *cp, mDNSBool mcast, co fail: LogMsg("%s error %ld errno %d (%s)", errstr, err, errno, strerror(errno)); if (!strcmp(errstr, "bind") && errno == EADDRINUSE) - NotifyOfElusiveBug("Setsockopt SO_REUSEPORT failed", 3814904, - "Alternatively, you can send email to radar-3387020@group.apple.com. " + 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."); close(skt); return(err); @@ -3643,6 +3666,7 @@ mDNSexport mDNSs32 mDNSPlatformRawTime(void) if (clockdivisor == 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) { @@ -3650,9 +3674,14 @@ mDNSexport mDNSs32 mDNSPlatformRawTime(void) 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; - // Only show "mach_absolute_time went backwards" notice on 10.4 (build 8xyyy) or later + // Only show "mach_absolute_time went backwards" notice on 10.4 (build 8xyyy) or later. + // (This bug happens all the time on 10.3, and we know that's not going to be fixed.) if (mDNSMacOSXSystemBuildNumber(NULL) >= 8) - NotifyOfElusiveBug("mach_absolute_time went backwards!", 3438376, ""); + 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; diff --git a/mDNSMacOSX/mDNSMacOSX.h b/mDNSMacOSX/mDNSMacOSX.h index 7dd31be..638f095 100644 --- a/mDNSMacOSX/mDNSMacOSX.h +++ b/mDNSMacOSX/mDNSMacOSX.h @@ -23,6 +23,9 @@ Change History (most recent first): $Log: mDNSMacOSX.h,v $ +Revision 1.52 2006/01/05 21:41:49 cheshire + Reword "mach_absolute_time went backwards" dialog + Revision 1.51 2005/07/04 22:24:36 cheshire Export NotifyOfElusiveBug() so other files can call it @@ -258,7 +261,7 @@ struct mDNS_PlatformSupport_struct CFRunLoopSourceRef PowerRLS; }; -extern void NotifyOfElusiveBug(const char *title, mDNSu32 radarid, const char *msg); +extern void NotifyOfElusiveBug(const char *title, const char *msg); // Both strings are UTF-8 text extern void mDNSMacOSXNetworkChanged(mDNS *const m); extern int mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring); diff --git a/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj b/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj index 8eac6b3..6c92377 100644 --- a/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj +++ b/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj @@ -22,11 +22,11 @@ buildSettings = { FRAMEWORK_SEARCH_PATHS = ""; GCC_TREAT_WARNINGS_AS_ERRORS = YES; - HEADER_SEARCH_PATHS = "../mDNSShared \"$(APPLE_INTERNAL_DEVELOPER_DIR)/Headers\" \"$(OBJROOT)\""; - LIBRARY_SEARCH_PATHS = ""; + HEADER_SEARCH_PATHS = "../mDNSShared \"${APPLE_INTERNAL_DEVELOPER_DIR}/Headers\" \"${OBJROOT}/mDNSResponder.build\""; + LIBRARY_SEARCH_PATHS = "\"${OBJROOT}/mDNSResponder.build\""; MACOSX_DEPLOYMENT_TARGET = 10.2; OPTIMIZATION_CFLAGS = "-O0"; - OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic -D__MACOSX__ -DmDNSResponderVersion=$(MVERS) -D_LEGACY_NAT_TRAVERSAL_ -DMDNS_DEBUGMSGS=1"; + OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic -D__MACOSX__ -DmDNSResponderVersion=${MVERS} -D_LEGACY_NAT_TRAVERSAL_ -DMDNS_DEBUGMSGS=1"; OTHER_LDFLAGS = "-ldnsinfo"; OTHER_REZFLAGS = ""; PRODUCT_NAME = mDNSResponder.debug; @@ -329,11 +329,11 @@ buildSettings = { FRAMEWORK_SEARCH_PATHS = ""; GCC_TREAT_WARNINGS_AS_ERRORS = YES; - HEADER_SEARCH_PATHS = "../mDNSShared \"$(APPLE_INTERNAL_DEVELOPER_DIR)/Headers\" \"$(OBJROOT)\""; + HEADER_SEARCH_PATHS = "../mDNSShared \"${APPLE_INTERNAL_DEVELOPER_DIR}/Headers\" \"${OBJROOT}/mDNSResponder.build\""; INSTALL_PATH = /usr/sbin; - LIBRARY_SEARCH_PATHS = ""; + LIBRARY_SEARCH_PATHS = "\"${OBJROOT}/mDNSResponder.build\""; MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic -D__MACOSX__ -DmDNSResponderVersion=$(MVERS) -D_LEGACY_NAT_TRAVERSAL_"; + OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic -D__MACOSX__ -DmDNSResponderVersion=${MVERS} -D_LEGACY_NAT_TRAVERSAL_"; OTHER_LDFLAGS = "-ldnsinfo"; OTHER_REZFLAGS = ""; PRODUCT_NAME = mDNSResponder; @@ -346,7 +346,7 @@ ); isa = PBXToolTarget; name = mDNSResponder; - productInstallPath = "$(HOME)/bin"; + productInstallPath = "${HOME}/bin"; productName = mDNSResponder; productReference = 034768E2FF38A6DC11DB9C8B; }; @@ -623,7 +623,7 @@ GCC_TREAT_WARNINGS_AS_ERRORS = YES; INSTALL_PATH = /usr/bin; MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic -D__MACOSX__ -DmDNSResponderVersion=$(MVERS)"; + OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic -D__MACOSX__ -DmDNSResponderVersion=${MVERS}"; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; PRODUCT_NAME = mDNS; @@ -949,7 +949,7 @@ buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - INSTALL_PATH = "$(SYSTEM_LIBRARY_DIR)/Java/Extensions"; + INSTALL_PATH = "${SYSTEM_LIBRARY_DIR}/Java/Extensions"; JAVA_ARCHIVE_CLASSES = YES; JAVA_ARCHIVE_COMPRESSION = YES; JAVA_ARCHIVE_TYPE = JAR; @@ -988,7 +988,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "javah -force -classpath ${OBJROOT}/mDNSResponder.build/dns_sd.jar.build/JavaClasses/ -o ${OBJROOT}/mDNSResponder.build/dns_sd.jar.build/DNSSD.java.h com.apple.dnssd.AppleDNSSD com.apple.dnssd.AppleBrowser com.apple.dnssd.AppleResolver com.apple.dnssd.AppleRegistration com.apple.dnssd.AppleQuery com.apple.dnssd.AppleDomainEnum com.apple.dnssd.AppleService"; + shellScript = "if [ -d ${OBJROOT}/mDNSResponder.build/${CONFIGURATION} ]; then BUILD_DIR=${OBJROOT}/mDNSResponder.build/${CONFIGURATION}; else BUILD_DIR=${OBJROOT}/mDNSResponder.build; fi\njavah -force -classpath ${BUILD_DIR}/dns_sd.jar.build/JavaClasses/ -o ${BUILD_DIR}/dns_sd.jar.build/DNSSD.java.h com.apple.dnssd.AppleDNSSD com.apple.dnssd.AppleBrowser com.apple.dnssd.AppleResolver com.apple.dnssd.AppleRegistration com.apple.dnssd.AppleQuery com.apple.dnssd.AppleDomainEnum com.apple.dnssd.AppleService"; }; DB2CC4560662DE4500335AB3 = { fileRef = DB2CC4430662DD1100335AB3; @@ -1103,7 +1103,7 @@ DEBUGGING_SYMBOLS = NO; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - HEADER_SEARCH_PATHS = "../mDNSShared \"$(SYSTEM_LIBRARY_DIR)/Frameworks/JavaVM.framework/Versions/A/Headers\" \"$(SYSTEM_LIBRARY_DIR)/Frameworks/JavaVM.framework/Versions/1.3.1/Headers\" \"$(OBJROOT)/mDNSResponder.build/dns_sd.jar.build\""; + HEADER_SEARCH_PATHS = "../mDNSShared \"${SYSTEM_LIBRARY_DIR}/Frameworks/JavaVM.framework/Versions/A/Headers\" \"${SYSTEM_LIBRARY_DIR}/Frameworks/JavaVM.framework/Versions/1.3.1/Headers\" \"${OBJROOT}/mDNSResponder.build/dns_sd.jar.build\" \"${OBJROOT}/mDNSResponder.build/${CONFIGURATION}/dns_sd.jar.build\""; INSTALL_PATH = /usr/lib/java; LIBRARY_STYLE = DYNAMIC; MACOSX_DEPLOYMENT_TARGET = 10.2; @@ -1316,7 +1316,7 @@ GCC_TREAT_WARNINGS_AS_ERRORS = YES; INSTALL_PATH = /usr/bin; MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic -D__MACOSX__ -DmDNSResponderVersion=$(MVERS) -I../mDNSShared"; + OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic -D__MACOSX__ -DmDNSResponderVersion=${MVERS} -I../mDNSShared"; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; PRODUCT_NAME = "dns-sd"; @@ -1414,11 +1414,11 @@ buildSettings = { FRAMEWORK_SEARCH_PATHS = ""; GCC_TREAT_WARNINGS_AS_ERRORS = YES; - HEADER_SEARCH_PATHS = "\"$(APPLE_INTERNAL_DEVELOPER_DIR)/Headers\""; + HEADER_SEARCH_PATHS = "\"${APPLE_INTERNAL_DEVELOPER_DIR}/Headers\""; INSTALL_PATH = /usr/sbin; LIBRARY_SEARCH_PATHS = ""; MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic -D__MACOSX__ -DmDNSResponderVersion=$(MVERS)"; + OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic -D__MACOSX__ -DmDNSResponderVersion=${MVERS}"; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; PRODUCT_NAME = dnsextd; @@ -1624,6 +1624,7 @@ FF260A4D07B4477F00CE10E5, FF2A870607B447EF00B14068, FF08480707CEB8E800AE6769, + FF354EB208516C63007C00E1, ); isa = PBXResourcesBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -1667,7 +1668,7 @@ ); buildSettings = { EXPORTED_SYMBOLS_FILE = ""; - INSTALL_PATH = "$(SYSTEM_LIBRARY_DIR)/PreferencePanes"; + INSTALL_PATH = "${SYSTEM_LIBRARY_DIR}/PreferencePanes"; MACOSX_DEPLOYMENT_TARGET = 10.2; OTHER_CFLAGS = ""; OTHER_LDFLAGS = "-twolevel_namespace"; @@ -1683,7 +1684,7 @@ ); isa = PBXBundleTarget; name = PreferencePane; - productInstallPath = "$(SYSTEM_LIBRARY_DIR)/PreferencePanes"; + productInstallPath = "${SYSTEM_LIBRARY_DIR}/PreferencePanes"; productName = PreferencePane; productReference = FF2609E207B440DD00CE10E5; productSettingsXML = " @@ -1697,7 +1698,7 @@ CFBundleGetInfoString CFBundleIconFile - DNSServiceDiscoveryPref + BonjourPref CFBundleIdentifier com.apple.preference.bonjour CFBundleInfoDictionaryVersion @@ -1715,7 +1716,7 @@ NSMainNibFile DNSServiceDiscoveryPref NSPrefPaneIconFile - DNSServiceDiscoveryPref.tiff + BonjourPref.tiff NSPrefPaneIconLabel Bonjour NSPrincipalClass @@ -1811,6 +1812,7 @@ FF260A2A07B4464B00CE10E5, FF260A3207B4466900CE10E5, FF260A3307B4466900CE10E5, + FF354EB108516C63007C00E1, FF260A4807B4475600CE10E5, FF260A4B07B4477F00CE10E5, ); @@ -1904,14 +1906,14 @@ }; FF260A3207B4466900CE10E5 = { isa = PBXFileReference; - name = DNSServiceDiscoveryPref.icns; - path = PreferencePane/DNSServiceDiscoveryPref.icns; + name = BonjourPref.icns; + path = PreferencePane/BonjourPref.icns; refType = 2; }; FF260A3307B4466900CE10E5 = { isa = PBXFileReference; - name = DNSServiceDiscoveryPref.tiff; - path = PreferencePane/DNSServiceDiscoveryPref.tiff; + name = BonjourPref.tiff; + path = PreferencePane/BonjourPref.tiff; refType = 2; }; FF260A3407B4466900CE10E5 = { @@ -1979,6 +1981,19 @@ isa = PBXTargetDependency; target = FF2609E107B440DD00CE10E5; }; + FF354EB108516C63007C00E1 = { + fileEncoding = 4; + isa = PBXExecutableFileReference; + name = installtool; + path = PreferencePane/installtool; + refType = 2; + }; + FF354EB208516C63007C00E1 = { + fileRef = FF354EB108516C63007C00E1; + isa = PBXBuildFile; + settings = { + }; + }; FF37BE9207614059003C0420 = { buildActionMask = 2147483647; files = ( @@ -1990,7 +2005,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if [ -e /usr/local/include/dnsinfo.h ]\nthen\nrm -f \"${OBJROOT}/dnsinfo.h\"\nrm -f \"${OBJROOT}/libdnsinfo.a\"\nelse\necho \"#define MDNS_NO_DNSINFO 1\" > ${OBJROOT}/dnsinfo.h\ntouch ${OBJROOT}/empty.c\ncc ${OBJROOT}/empty.c -c -o \"${OBJROOT}/libdnsinfo.a\"\nrm -f ${OBJROOT}/empty.c\nfi"; + shellScript = "if [ -e /usr/local/include/dnsinfo.h ]\nthen\nrm -f \"${OBJROOT}/mDNSResponder.build/dnsinfo.h\"\nrm -f \"${OBJROOT}/mDNSResponder.build/libdnsinfo.a\"\nelse\necho \"#define MDNS_NO_DNSINFO 1\" > ${OBJROOT}/mDNSResponder.build/dnsinfo.h\ntouch ${OBJROOT}/mDNSResponder.build/empty.c\ncc ${OBJROOT}/mDNSResponder.build/empty.c -c -o \"${OBJROOT}/mDNSResponder.build/libdnsinfo.a\"\nrm -f ${OBJROOT}/mDNSResponder.build/empty.c\nfi"; }; FF485D5105632E0000130380 = { fileEncoding = 4; @@ -2072,7 +2087,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "rm -f ${BUILD_DIR}/dns_sd"; + shellScript = "rm -f ${BUILD_DIR}/${CONFIGURATION}/dns_sd"; }; FFD41DDF06641BBB00F0C438 = { isa = PBXTargetDependency; @@ -2139,7 +2154,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if [ -e /usr/local/include/dnsinfo.h ]\nthen\nrm -f \"${OBJROOT}/dnsinfo.h\"\nrm -f \"${OBJROOT}/libdnsinfo.a\"\nelse\necho \"#define MDNS_NO_DNSINFO 1\" > ${OBJROOT}/dnsinfo.h\ntouch ${OBJROOT}/empty.c\ncc ${OBJROOT}/empty.c -c -o \"${OBJROOT}/libdnsinfo.a\"\nrm -f ${OBJROOT}/empty.c\nfi"; + shellScript = "if [ -e /usr/local/include/dnsinfo.h ]\nthen\nrm -f \"${OBJROOT}/mDNSResponder.build/dnsinfo.h\"\nrm -f \"${OBJROOT}/mDNSResponder.build/libdnsinfo.a\"\nelse\necho \"#define MDNS_NO_DNSINFO 1\" > ${OBJROOT}/mDNSResponder.build/dnsinfo.h\ntouch ${OBJROOT}/mDNSResponder.build/empty.c\ncc ${OBJROOT}/mDNSResponder.build/empty.c -c -o \"${OBJROOT}/mDNSResponder.build/libdnsinfo.a\"\nrm -f ${OBJROOT}/mDNSResponder.build/empty.c\nfi"; }; FFFB0DA407B43BED00B88D48 = { children = ( diff --git a/mDNSPosix/Makefile b/mDNSPosix/Makefile index 638b56d..7176e0e 100755 --- a/mDNSPosix/Makefile +++ b/mDNSPosix/Makefile @@ -24,6 +24,39 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # $Log: Makefile,v $ +# Revision 1.66 2006/01/06 01:06:17 cheshire +# Compile library and client programs in one pass +# +# Revision 1.65 2005/12/21 21:15:57 cheshire +# Add missing dependency: Identify.c textually imports mDNS.c +# +# Revision 1.64 2005/10/25 23:55:47 cheshire +# Add tiger to list of target platforms +# +# Revision 1.63 2005/10/11 21:30:44 cheshire +# Add "-Wunreachable-code" (commented out for now) +# +# Revision 1.62 2005/06/30 21:46:55 cheshire +# Solaris should use unix domain sockets, not loopback +# +# Revision 1.61 2005/06/30 20:46:05 cheshire +# Added Makefile rule to build threadsafe object files where necessary using "-D_REENTRANT". +# +# Revision 1.60 2005/06/30 10:42:38 cheshire +# Turn on "-Werror" and "-O" for better error reporting +# +# Revision 1.59 2005/04/14 21:07:10 rpantos +# Bug #: 4089257, Clean build broken for Java support on POSIX +# Submitted by: Roger Pantos +# Reviewed by: Kiren Sekar +# +# Revision 1.58 2005/04/08 21:37:57 ksekar +# get_ifi_info doesn't return IPv6 interfaces on Linux +# +# Revision 1.57 2005/03/17 04:02:28 cheshire +# mDNSResponder won't compile with gcc4 on Tiger +# Changed Makefile to link using gcc instead of libtool +# # Revision 1.56 2005/02/02 02:25:21 cheshire # /var/run/mDNSResponder should be /var/run/mdnsd on Linux # @@ -221,7 +254,6 @@ RM = rm LN = ln -s -f CFLAGS_COMMON = -I. -I$(COREDIR) -I$(SHAREDDIR) -W -Wall -DPID_FILE=\"/var/run/mdnsd.pid\" -DMDNS_UDS_SERVERPATH=\"/var/run/mdnsd\" LIBFLAGS = -DNSEXT_FLAGS = -D_REENTRANT -g -Wall -lpthread LDSUFFIX = so JAVACFLAGS_OS = -fPIC -shared -ldns_sd @@ -233,7 +265,10 @@ OBJDIR = objects/debug BUILDDIR = build/debug STRIP = echo else -CFLAGS_DEBUG = -O0 -DMDNS_DEBUGMSGS=0 +# We use -Os for two reasons: +# 1. We want to make small binaries, suitable for putting into hardware devices +# 2. Some of the code analysis warnings only work when some form of optimization is enabled +CFLAGS_DEBUG = -Os -DMDNS_DEBUGMSGS=0 OBJDIR = objects/prod BUILDDIR = build/prod STRIP = strip -S @@ -242,7 +277,7 @@ endif # Configure per-OS peculiarities ifeq ($(os),solaris) CFLAGS_OS = -DNOT_HAVE_DAEMON -DNOT_HAVE_SA_LEN -DNOT_HAVE_SOCKLEN_T -DNOT_HAVE_IF_NAMETOINDEX \ - -DLOG_PERROR=0 -D_XPG4_2 -D__EXTENSIONS__ -DHAVE_BROKEN_RECVIF_NAME -DUSE_TCP_LOOPBACK + -DLOG_PERROR=0 -D_XPG4_2 -D__EXTENSIONS__ -DHAVE_BROKEN_RECVIF_NAME CC = gcc LD = gcc -shared LIBFLAGS = -lsocket -lnsl -lresolv @@ -253,7 +288,7 @@ endif else ifeq ($(os),linux) -CFLAGS_OS = -DNOT_HAVE_SA_LEN -DUSES_NETLINK +CFLAGS_OS = -DNOT_HAVE_SA_LEN -DUSES_NETLINK -DHAVE_LINUX JAVACFLAGS_OS += -I$(JDK)/include/linux OPTIONALTARG = nss_mdns OPTINSTALL = InstalledNSS @@ -281,7 +316,7 @@ LDCONFIG = ldconfig else ifeq ($(os),jaguar) -CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp -DNOT_HAVE_SOCKLEN_T +CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp -Werror -DNOT_HAVE_SOCKLEN_T LD = libtool -dynamic LIBFLAGS = -lSystem LDSUFFIX = dylib @@ -290,7 +325,7 @@ JAVACFLAGS_OS = -dynamiclib -I/System/Library/Frameworks/JavaVM.framework/Header else ifeq ($(os),panther) -CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp #-pedantic +CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp -Werror LD = libtool -dynamic LIBFLAGS = -lSystem LDSUFFIX = dylib @@ -299,9 +334,9 @@ JAVACFLAGS_OS = -dynamiclib -I/System/Library/Frameworks/JavaVM.framework/Header else ifeq ($(os),tiger) -CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp -Wdeclaration-after-statement +CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp -Werror -Wdeclaration-after-statement #-Wunreachable-code CC = @gcc-4.0 -LD = libtool -dynamic +LD = $(CC) -dynamiclib LIBFLAGS = -lSystem LDSUFFIX = dylib JDK = /System/Library/Frameworks/JavaVM.framework/Home @@ -309,7 +344,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=tiger [target]".\ -Supported operating systems include: jaguar, panther, linux, netbsd, freebsd, openbsd, solaris) +Supported operating systems include: jaguar, panther, tiger, linux, netbsd, freebsd, openbsd, solaris) endif endif endif @@ -370,9 +405,9 @@ CFLAGS = $(CFLAGS_COMMON) $(CFLAGS_OS) $(CFLAGS_DEBUG) ############################################################################# -all: setup Daemon libdns_sd Client Responder ProxyResponder Identify NetMonitor dnsextd $(OPTIONALTARG) +all: setup Daemon libdns_sd Clients SAClient SAResponder SAProxyResponder Identify NetMonitor dnsextd $(OPTIONALTARG) -install: setup InstalledDaemon InstalledLib InstalledStartup InstalledManPages $(OPTINSTALL) +install: setup InstalledDaemon InstalledStartup InstalledLib InstalledManPages InstalledClients $(OPTINSTALL) # 'setup' sets up the build directory structure the way we want setup: @@ -383,8 +418,9 @@ setup: # clean removes targets and objects clean: - if test -d $(OBJDIR) ; then rm -r $(OBJDIR) ; fi - if test -d $(BUILDDIR) ; then rm -r $(BUILDDIR) ; fi + @if test -d $(OBJDIR) ; then rm -r $(OBJDIR) ; fi + @if test -d $(BUILDDIR) ; then rm -r $(BUILDDIR) ; fi + @$(MAKE) -C ../Clients clean ############################################################################# @@ -410,6 +446,12 @@ $(BUILDDIR)/libdns_sd.$(LDSUFFIX): $(CLIENTLIBOBJS) @$(LD) $(LIBFLAGS) -o $@ $+ @$(STRIP) $@ +Clients: setup libdns_sd ../Clients/build/dns-sd + @echo "Clients done" + +../Clients/build/dns-sd: + @$(MAKE) -C ../Clients + # nss_mdns target builds the Name Service Switch module nss_mdns: setup $(BUILDDIR)/$(NSSLIBFILE) @echo "Name Service Switch module done" @@ -430,13 +472,18 @@ InstalledLib: $(INSTBASE)/lib/libdns_sd.$(LDSUFFIX).$(LIBVERS) $(INSTBASE)/inclu InstalledStartup: $(STARTUPSCRIPTDIR)/$(STARTUPSCRIPTNAME) @echo $+ " installed" -InstalledNSS: $(NSSINSTPATH)/$(NSSLINKNAME) /etc/nss_mdns.conf $(MANPATH)/man5/nss_mdns.conf.5 $(MANPATH)/man8/libnss_mdns.8 +InstalledManPages: $(MANPATH)/man8/mdnsd.8 @echo $+ " installed" -InstalledManPages: $(MANPATH)/man8/mdnsd.8 +InstalledClients: $(INSTBASE)/bin/dns-sd + @echo $+ " installed" + +InstalledNSS: $(NSSINSTPATH)/$(NSSLINKNAME) /etc/nss_mdns.conf $(MANPATH)/man5/nss_mdns.conf.5 $(MANPATH)/man8/libnss_mdns.8 @echo $+ " installed" +# Note: If daemon already installed, we make sure it's stopped before overwriting it $(INSTBASE)/sbin/mdnsd: $(BUILDDIR)/mdnsd + @if test -x $@; then $(STARTUPSCRIPTDIR)/$(STARTUPSCRIPTNAME) stop; fi $(CP) $< $@ $(INSTBASE)/lib/libdns_sd.$(LDSUFFIX).$(LIBVERS): $(BUILDDIR)/libdns_sd.$(LDSUFFIX) @@ -450,9 +497,12 @@ endif $(INSTBASE)/include/dns_sd.h: $(SHAREDDIR)/dns_sd.h $(CP) $< $@ -$(STARTUPSCRIPTDIR)/$(STARTUPSCRIPTNAME): mdnsd.sh $(STARTUPSCRIPTDIR) +# We make this target dependent on $(INSTBASE)/sbin/mdnsd because we need to ensure +# that the daemon is installed *before* we try to execute the command to start it. +$(STARTUPSCRIPTDIR)/$(STARTUPSCRIPTNAME): mdnsd.sh $(STARTUPSCRIPTDIR) $(INSTBASE)/sbin/mdnsd $(CP) $< $@ chmod ugo+x $@ + $@ start ifdef RUNLEVELSCRIPTSDIR ifeq ($(wildcard $(RUNLEVELSCRIPTSDIR)/runlevels/default), $(RUNLEVELSCRIPTSDIR)/runlevels/default) $(LN) $@ $(RUNLEVELSCRIPTSDIR)/runlevels/default/mdns @@ -466,6 +516,21 @@ else endif endif +$(MANPATH)/man5/%.5: %.5 + cp $< $@ + chmod 444 $@ + +$(MANPATH)/man8/%.8: %.8 + cp $< $@ + chmod 444 $@ + +$(MANPATH)/man8/mdnsd.8: $(SHAREDDIR)/mDNSResponder.8 + cp $< $@ + chmod 444 $@ + +$(INSTBASE)/bin/dns-sd: ../Clients/build/dns-sd + $(CP) $< $@ + $(NSSINSTPATH)/$(NSSLINKNAME): $(NSSINSTPATH)/$(NSSLIBFILE) $(LN) $< $@ ldconfig @@ -482,18 +547,6 @@ $(NSSINSTPATH)/$(NSSLIBFILE): $(BUILDDIR)/$(NSSLIBFILE) cp -f /etc/nsswitch.conf /etc/nsswitch.conf.pre-mdns sed -e '/mdns/!s/^\(hosts:.*\)dns\(.*\)/\1mdns dns\2/' /etc/nsswitch.conf.pre-mdns > /etc/nsswitch.conf -$(MANPATH)/man5/%.5: %.5 - cp $< $@ - chmod 444 $@ - -$(MANPATH)/man8/%.8: %.8 - cp $< $@ - chmod 444 $@ - -$(MANPATH)/man8/mdnsd.8: $(SHAREDDIR)/mDNSResponder.8 - cp $< $@ - chmod 444 $@ - ############################################################################# # The following targets build Java wrappers for the dns-sd.h API. @@ -509,8 +562,8 @@ Java: setup $(BUILDDIR)/dns_sd.jar $(BUILDDIR)/libjdns_sd.$(LDSUFFIX) JAVASRC = $(SHAREDDIR)/Java JARCONTENTS = $(OBJDIR)/com/apple/dnssd/DNSSDService.class \ - $(OBJDIR)/com/apple/dnssd/DNSRecord.class \ $(OBJDIR)/com/apple/dnssd/DNSSDException.class \ + $(OBJDIR)/com/apple/dnssd/DNSRecord.class \ $(OBJDIR)/com/apple/dnssd/TXTRecord.class \ $(OBJDIR)/com/apple/dnssd/DNSSDRegistration.class \ $(OBJDIR)/com/apple/dnssd/BaseListener.class \ @@ -521,11 +574,11 @@ JARCONTENTS = $(OBJDIR)/com/apple/dnssd/DNSSDService.class \ $(OBJDIR)/com/apple/dnssd/DomainListener.class \ $(OBJDIR)/com/apple/dnssd/DNSSD.class -$(BUILDDIR)/dns_sd.jar: $(JARCONTENTS) +$(BUILDDIR)/dns_sd.jar: $(JARCONTENTS) setup $(JAR) -cf $@ -C $(OBJDIR) com -$(BUILDDIR)/libjdns_sd.$(LDSUFFIX): $(JAVASRC)/JNISupport.c $(OBJDIR)/DNSSD.java.h - $(CC) -o $@ $< $(JAVACFLAGS) -I$(OBJDIR) +$(BUILDDIR)/libjdns_sd.$(LDSUFFIX): $(JAVASRC)/JNISupport.c $(OBJDIR)/DNSSD.java.h setup libdns_sd + $(CC) -o $@ $< $(JAVACFLAGS) -I$(OBJDIR) -L$(BUILDDIR) $(OBJDIR)/com/apple/dnssd/%.class: $(JAVASRC)/%.java $(JAVAC) -d $(OBJDIR) -classpath $(OBJDIR) $< @@ -544,7 +597,7 @@ $(OBJDIR)/DNSSD.java.h: $(OBJDIR)/com/apple/dnssd/DNSSD.class # The following target builds documentation for the Java wrappers. -JavaDoc: setup Java +JavaDoc: Java setup $(JAVADOC) $(JAVASRC)/*.java -classpath $(OBJDIR) -d $(BUILDDIR) -public ############################################################################# @@ -554,13 +607,13 @@ SPECIALOBJ = $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNSDebug.c COMMONOBJ = $(SPECIALOBJ) $(OBJDIR)/mDNS.c.o APPOBJ = $(COMMONOBJ) $(OBJDIR)/ExampleClientApp.c.o -Client: setup $(BUILDDIR)/mDNSClientPosix +SAClient: setup $(BUILDDIR)/mDNSClientPosix @echo "Embedded Standalone Client done" -Responder: setup $(BUILDDIR)/mDNSResponderPosix +SAResponder: setup $(BUILDDIR)/mDNSResponderPosix @echo "Embedded Standalone Responder done" -ProxyResponder: setup $(BUILDDIR)/mDNSProxyResponderPosix +SAProxyResponder: setup $(BUILDDIR)/mDNSProxyResponderPosix @echo "Embedded Standalone ProxyResponder done" Identify: setup $(BUILDDIR)/mDNSIdentify @@ -584,11 +637,13 @@ $(BUILDDIR)/mDNSProxyResponderPosix: $(COMMONOBJ) $(OBJDIR)/ProxyResponder.c.o $(BUILDDIR)/mDNSIdentify: $(SPECIALOBJ) $(OBJDIR)/Identify.c.o $(CC) $+ -o $@ $(LIBFLAGS) +$(OBJDIR)/Identify.c.o: $(COREDIR)/mDNS.c # Note: Identify.c textually imports mDNS.c + $(BUILDDIR)/mDNSNetMonitor: $(SPECIALOBJ) $(OBJDIR)/NetMonitor.c.o $(CC) $+ -o $@ $(LIBFLAGS) -$(BUILDDIR)/dnsextd: $(SPECIALOBJ) $(OBJDIR)/dnsextd.c.o - $(CC) $+ -o $@ $(LIBFLAGS) $(DNSEXT_FLAGS) +$(BUILDDIR)/dnsextd: $(SPECIALOBJ) $(OBJDIR)/dnsextd.c.threadsafe.o + $(CC) $+ -o $@ $(LIBFLAGS) -lpthread ############################################################################# @@ -602,6 +657,9 @@ $(OBJDIR)/%.c.o: $(COREDIR)/%.c $(OBJDIR)/%.c.o: $(SHAREDDIR)/%.c $(CC) $(CFLAGS) -c -o $@ $< +$(OBJDIR)/%.c.threadsafe.o: %.c + $(CC) $(CFLAGS) -D_REENTRANT -c -o $@ $< + $(OBJDIR)/%.c.so.o: %.c $(CC) $(CFLAGS) -c -fPIC -o $@ $< diff --git a/mDNSPosix/NetMonitor.c b/mDNSPosix/NetMonitor.c index 4fce88d..6c53cc8 100644 --- a/mDNSPosix/NetMonitor.c +++ b/mDNSPosix/NetMonitor.c @@ -37,6 +37,18 @@ Change History (most recent first): $Log: NetMonitor.c,v $ +Revision 1.75 2006/01/05 22:33:58 cheshire +Use IFNAMSIZ (more portable) instead of IF_NAMESIZE + +Revision 1.74 2005/12/02 20:08:39 cheshire +Update "No HINFO" message + +Revision 1.73 2005/12/02 19:19:53 cheshire + Include interface index and name in mDNSNetMonitor output + +Revision 1.72 2005/11/07 01:47:45 cheshire + Include interface index in mDNSNetMonitor output + Revision 1.71 2004/12/16 20:17:11 cheshire Cache memory management improvements @@ -286,6 +298,7 @@ Added NetMonitor.c #include // For gethostbyname() #include // For AF_INET, AF_INET6, etc. #include // For inet_addr() +#include // For IF_NAMESIZE #include // For INADDR_NONE #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform @@ -595,7 +608,7 @@ mDNSlocal void ShowSortedHostList(HostList *list, int max) if (e->pkts[HostPkt_B]) mprintf("Bad: %8lu", e->pkts[HostPkt_B]); mprintf("\n"); if (!e->HISoftware.c[0] && e->NumQueries > 2) - mDNSPlatformMemCopy("\x0E*** Jaguar ***", &e->HISoftware, 15); + mDNSPlatformMemCopy("\x27*** Unknown (Jaguar, Windows, etc.) ***", &e->HISoftware, 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); } @@ -695,21 +708,20 @@ mDNSlocal const mDNSu8 *FindUpdate(mDNS *const m, const DNSMessage *const query, return(mDNSNULL); } -mDNSlocal void DisplayTimestamp(void) +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); + 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\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec); - } - -mDNSlocal void DisplayPacketHeader(const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr) - { - const char *const ptype = (msg->h.flags.b[0] & kDNSFlag0_QR_Response) ? "-R- " : - (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) ? "-Q- " : "-LQ-"; + 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); - DisplayTimestamp(); mprintf("%#-16a %s Q:%3d Ans:%3d Auth:%3d Add:%3d Size:%5d bytes", srcaddr, ptype, msg->h.numQuestions, msg->h.numAnswers, msg->h.numAuthorities, msg->h.numAdditionals, end - (mDNSu8 *)msg); @@ -822,7 +834,7 @@ mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mD HostEntry *entry = GotPacketFromHost(srcaddr, MQ ? HostPkt_Q : HostPkt_L, msg->h.id); LargeCacheRecord pkt; - DisplayPacketHeader(msg, end, srcaddr, srcport, dstaddr); + DisplayPacketHeader(m, msg, end, srcaddr, srcport, dstaddr, InterfaceID); if (msg->h.id.NotAnInteger != 0xFFFF) { if (MQ) NumPktQ++; else NumPktL++; @@ -888,7 +900,7 @@ mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R, msg->h.id); LargeCacheRecord pkt; - DisplayPacketHeader(msg, end, srcaddr, srcport, dstaddr); + DisplayPacketHeader(m, msg, end, srcaddr, srcport, dstaddr, InterfaceID); if (msg->h.id.NotAnInteger != 0xFFFF) NumPktR++; for (i=0; ih.numQuestions; i++) diff --git a/mDNSPosix/PosixDaemon.c b/mDNSPosix/PosixDaemon.c index 6b991ec..5963f20 100644 --- a/mDNSPosix/PosixDaemon.c +++ b/mDNSPosix/PosixDaemon.c @@ -28,6 +28,12 @@ Change History (most recent first): $Log: PosixDaemon.c,v $ +Revision 1.29 2005/08/04 03:37:45 mkrochma +Temporary workaround to fix posix after mDNS_SetPrimaryInterfaceInfo changed + +Revision 1.28 2005/07/19 11:21:09 cheshire + Unix Domain Socket leak in mdnsd + Revision 1.27 2005/02/04 00:39:59 cheshire Move ParseDNSServers() from PosixDaemon.c to mDNSPosix.c so all Posix client layers can use it @@ -143,14 +149,14 @@ extern const char mDNSResponderVersionString[]; static void Reconfigure(mDNS *m) { mDNSAddr DynDNSIP; - mDNS_SetPrimaryInterfaceInfo(m, NULL, NULL); + mDNS_SetPrimaryInterfaceInfo(m, NULL, NULL, NULL); mDNS_DeleteDNSServers(m); if (ParseDNSServers(m, uDNS_SERVERS_FILE) < 0) LogMsg("Unable to parse DNS server list. Unicast DNS-SD unavailable"); ReadDDNSSettingsFromConfFile(m, CONFIG_FILE, &DynDNSHostname, &DynDNSZone, NULL); FindDefaultRouteIP(&DynDNSIP); if (DynDNSHostname.c[0]) mDNS_AddDynDNSHostName(m, &DynDNSHostname, NULL, NULL); - if (DynDNSIP.type) mDNS_SetPrimaryInterfaceInfo(m, &DynDNSIP, NULL); + if (DynDNSIP.type) mDNS_SetPrimaryInterfaceInfo(m, &DynDNSIP, NULL, NULL); } // Do appropriate things at startup with command line arguments. Calls exit() if unhappy. @@ -288,8 +294,9 @@ mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *cont mStatus udsSupportRemoveFDFromEventLoop(int fd) // Note: This also CLOSES the file descriptor { - return mDNSPosixRemoveFDFromEventLoop(fd); + mStatus err = mDNSPosixRemoveFDFromEventLoop(fd); close(fd); + return err; } mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay) diff --git a/mDNSPosix/ProxyResponder.c b/mDNSPosix/ProxyResponder.c index f113d65..293ee0a 100644 --- a/mDNSPosix/ProxyResponder.c +++ b/mDNSPosix/ProxyResponder.c @@ -24,6 +24,9 @@ Change History (most recent first): $Log: ProxyResponder.c,v $ +Revision 1.36 2005/08/04 03:12:47 mkrochma + Register reverse PTR record using multicast + Revision 1.35 2004/12/16 20:17:11 cheshire Cache memory management improvements @@ -171,6 +174,7 @@ mDNSlocal mStatus mDNS_RegisterProxyHost(mDNS *m, ProxyHost *p) mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", p->ip.b[3], p->ip.b[2], p->ip.b[1], p->ip.b[0]); MakeDomainNameFromDNSNameString(p->RR_PTR.resrec.name, buffer); + p->RR_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); diff --git a/mDNSPosix/ReadMe.txt b/mDNSPosix/ReadMe.txt index 80f8fa3..35be2b1 100755 --- a/mDNSPosix/ReadMe.txt +++ b/mDNSPosix/ReadMe.txt @@ -1,24 +1,31 @@ ReadMe About mDNSPosix ---------------------- -mDNSPosix is a port of Apple's core mDNS code to Posix platforms. +mDNSPosix is a port of Apple's Multicast DNS and DNS Service Discovery +code to Posix platforms. -mDNS is short for "multicast DNS", which is a technology that allows you -to register IP services and browse the network for those services. For -more information about mDNS, see the mDNS web site. +Multicast DNS and DNS Service Discovery are technologies that allow you +to register IP-based services and browse the network for those services. +For more information about mDNS, see the mDNS web site. -mDNS is part of a family of technologies resulting from the efforts of -the IETF zeroconf working group. For information about other zeroconf -technologies, see the zeroconf web site. +Multicast DNS is part of a family of technologies resulting from the +efforts of the IETF Zeroconf working group. For information about +other Zeroconf technologies, see the Zeroconf web site. Apple uses the trade mark "Bonjour" to describe our implementation of -zeroconf technologies. This sample is designed to show how easy it is +Zeroconf technologies. This sample is designed to show how easy it is to make a device "Bonjour compatible". +The "Bonjour" trade mark can also be licensed at no charge for +inclusion on your own products, packaging, manuals, promotional +materials, or web site. For details and licensing terms, see + + + The code in this sample was compiled and tested on Mac OS X (10.1.x, 10.2, 10.3), Solaris (SunOS 5.8), Linux (Redhat 2.4.9-21, Fedora Core 1), and OpenBSD (2.9). YMMV. @@ -79,26 +86,30 @@ o Standalone products for dedicated devices (printer, network camera, etc.) - mDNSResponderPosix - mDNSProxyResponderPosix -o Debugging tools +o Testing and Debugging tools + - dns-sd command-line tool (from the "Clients" folder) - mDNSNetMonitor - mDNSIdentify -As root type "make install" to install six things: +As root type "make install" to install eight things: o mdnsd (usually in /usr/sbin) o libmdns (usually in /usr/lib) o dns_sd.h (usually in /usr/include) o startup scripts (e.g. in /etc/rc.d) o manual pages (usually in /usr/share/man) +o dns-sd tool (usually in /usr/bin) o nss_mdns (usually in /lib) o nss configuration files (usually in /etc) -Once you've installed the files in their respective places, -you need to start the daemon running, either by rebooting, -or by running the startup script "/etc/init.d/mdns start" -(the exact path may be different on your system). -Then you can cd to the "Clients" folder and type "make". -This builds a test client showing how to exercise all the major -functionality of the daemon. +The "make install" concludes by executing the startup script +(usually "/etc/init.d/mdns start") to start the daemon running. +You shouldn't need to reboot unless you really want to. + +Once the daemon is running, you can use the dns-sd test tool +to exercise all the major functionality of the daemon. Running +"dns-sd" with no arguments gives a summary of the available options. +This test tool is also described in detail, with several examples, +in Chapter 6 of the O'Reilly "Zero Configuration Networking" book. How It Works @@ -243,17 +254,19 @@ Caveats ------- Currently the program uses a simple make file. -There are various problems with loopback-only self discovery. The code -will attempt service discovery on the loopback interface only if no -other interfaces are available. However, this exposes a number of -problems with the underlying network stack (at least on Mac OS X). +The Multicast DNS protocol can also operate locally over the loopback +interface, but this exposed some problems with the underlying network +stack in early versions of Mac OS X and may expose problems with other +network stacks too. -o On Mac OS X 10.1.x the code fails to start on the loopback interface +o On Mac OS X 10.1.x the code failed to start on the loopback interface because the IP_ADD_MEMBERSHIP option returns ENOBUFS. -o On Mac OS X 10.2 the loopback-only case fails because - mDNSPlatformSendUDP's call to "sendto" fails with error EHOSTUNREACH - [Radar ID 3016042]. +o On Mac OS X 10.2 the loopback-only case failed because + "sendto" calls fails with error EHOSTUNREACH. (3016042) + +Consequently, the code will attempt service discovery on the loopback +interface only if no other interfaces are available. I haven't been able to test the loopback-only case on other platforms because I don't have access to the physical machine. diff --git a/mDNSPosix/Responder.c b/mDNSPosix/Responder.c index 7765669..986333b 100755 --- a/mDNSPosix/Responder.c +++ b/mDNSPosix/Responder.c @@ -24,6 +24,9 @@ Change History (most recent first): $Log: Responder.c,v $ +Revision 1.30 2005/10/26 22:21:16 cheshire + Potential buffer overflow in mDNSResponderPosix + Revision 1.29 2005/03/04 21:35:33 cheshire Services.txt file not parsed properly when it contains more than one service @@ -591,7 +594,7 @@ static mStatus RegisterServicesInFile(const char *filePath) const char *dom = kDefaultServiceDomain; char rawText[1024]; mDNSu8 text[sizeof(RDataBody)]; - mDNSu16 textLen = 0; + unsigned int textLen = 0; char port[256]; // Skip over any blank lines. @@ -626,10 +629,11 @@ static mStatus RegisterServicesInFile(const char *filePath) len = strlen(rawText); if (len <= 255) { + unsigned int newlen = textLen + 1 + len; + if (len == 0 || newlen >= sizeof(text)) break; text[textLen] = len; - if (text[textLen] == 0) break; - memcpy(text + textLen + 1, rawText, text[textLen]); - textLen += 1 + text[textLen]; + memcpy(text + textLen + 1, rawText, len); + textLen = newlen; } else fprintf(stderr, "%s: TXT attribute too long for name = %s, type = %s, port = %s\n", diff --git a/mDNSPosix/dnsextd.c b/mDNSPosix/dnsextd.c index 6acf92e..ab1071d 100644 --- a/mDNSPosix/dnsextd.c +++ b/mDNSPosix/dnsextd.c @@ -24,9 +24,30 @@ Change History (most recent first): $Log: dnsextd.c,v $ -Revision 1.33.2.1 2005/08/05 21:14:00 ksekar - Long-lived queries not working on windows -Change constant names +Revision 1.41 2005/09/24 01:10:54 cheshire +Fix comment typos + +Revision 1.40 2005/09/07 21:54:37 ksekar + dnsextd doesn't clean up on exit +Close sockets before cleanup so clients can't make new requests + +Revision 1.39 2005/08/22 23:30:30 ksekar + memory leak in dnsextd.c + +Revision 1.38 2005/06/29 10:03:56 cheshire +Fix compile errors and warnings + +Revision 1.37 2005/06/27 22:12:17 ksekar + dnsextd performance improvements + +Revision 1.36 2005/06/14 23:14:41 ksekar + Unable to refresh LLQs + +Revision 1.35 2005/03/17 03:57:43 cheshire +LEASE_OPT_SIZE is now LEASE_OPT_RDLEN; LLQ_OPT_SIZE is now LLQ_OPT_RDLEN + +Revision 1.34 2005/03/16 18:47:37 ksekar + dnsextd doesn't clean up on exit Revision 1.33 2005/03/11 19:09:02 ksekar Fixed ZERO_LLQID macro @@ -156,6 +177,7 @@ Revision 1.1 2004/08/11 00:43:26 ksekar #include #include #include +#include #include #include @@ -233,7 +255,9 @@ typedef struct AnswerListElem mDNSu16 type; CacheRecord *KnownAnswers; // All valid answers delivered to client CacheRecord *EventList; // New answers (adds/removes) to be sent to client - int refcount; + int refcount; + mDNSBool UseTCP; // Use TCP if UDP would cause truncation + pthread_t tid; // Allow parallel list updates } AnswerListElem; // llq table entry @@ -254,7 +278,7 @@ typedef struct LLQEntry typedef struct { // server variables - read only after initialization (no locking) - struct in_addr saddr; // server address + struct sockaddr_in saddr; // server address domainname zone; // zone being updated int tcpsd; // listening TCP socket int udpsd; // listening UDP socket @@ -272,8 +296,9 @@ typedef struct // LLQ table variables LLQEntry *LLQTable[LLQ_TABLESIZE]; // !!!KRS change this and RRTable to use a common data structure AnswerListElem *AnswerTable[LLQ_TABLESIZE]; - int LLQEventListenSock; // Unix domain socket pair - polling thread writes to ServPollSock, which wakes - int LLQServPollSock; // the main thread listening on EventListenSock, indicating that the zone has changed + int AnswerTableCount; + int LLQEventNotifySock; // Unix domain socket pair - update handling thread writes to EventNotifySock, which wakes + int LLQEventListenSock; // the main thread listening on EventListenSock, indicating that the zone has changed } DaemonInfo; // args passed to UDP request handler thread as void* @@ -292,6 +317,13 @@ typedef struct DaemonInfo *d; } TCPRequestArgs; +// args passed to UpdateAnswerList thread as void* +typedef struct + { + DaemonInfo *d; + AnswerListElem *a; + } UpdateAnswerListArgs; + // // Global Variables // @@ -353,11 +385,12 @@ mDNSlocal void Log(const char *format, ...) // Error Logging // prints message "dnsextd : - " -// must be compiled w/ -D_REENTRANT for thread-safe errno usage +// must be compiled w/ -D_REENTRANT for thread-safe errno usage mDNSlocal void LogErr(const char *fn, const char *operation) { - char buf[512]; - snprintf(buf, sizeof(buf), "%s: %s - %s", fn, operation, strerror(errno)); + char buf[512], errbuf[256]; + strerror_r(errno, errbuf, sizeof(errbuf)); + snprintf(buf, sizeof(buf), "%s: %s - %s", fn, operation, errbuf); PrintLog(buf); } @@ -400,30 +433,40 @@ mDNSlocal void HdrHToN(PktMsg *pkt) // caller terminates connection via close() mDNSlocal int ConnectToServer(DaemonInfo *d) { - struct sockaddr_in servaddr; - int sd; - - bzero(&servaddr, sizeof(servaddr)); - if (d->saddr.s_addr) servaddr.sin_addr = d->saddr; - else inet_pton(AF_INET, LOOPBACK, &d->saddr); // use loopback if server not explicitly specified - servaddr.sin_port = htons(NS_PORT); - servaddr.sin_family = AF_INET; -#ifndef NOT_HAVE_SA_LEN - servaddr.sin_len = sizeof(servaddr); -#endif - sd = socket(AF_INET, SOCK_STREAM, 0); - if (sd < 0) { LogErr("ConnectToServer", "socket"); return -1; } - if (connect(sd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { LogErr("ConnectToServer", "connect"); return -1; } - return sd; + int ntries = 0, retry = 0; + + while (1) + { + int sd = socket(AF_INET, SOCK_STREAM, 0); + if (sd < 0) { LogErr("ConnectToServer", "socket"); return -1; } + if (!connect(sd, (struct sockaddr *)&d->saddr, sizeof(d->saddr))) return sd; + close(sd); + 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 -1; } + } } -// send an entire block of data over a connected socket, blocking if buffers are full +// send an entire block of data over a connected socket mDNSlocal int MySend(int sd, const void *msg, int len) { - int n, nsent = 0; - + 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) { + FD_ZERO(&wset); + FD_SET(sd, &wset); + selectval = select(sd+1, NULL, &wset, NULL, &timeout); + if (selectval < 0) { LogErr("MySend", "select"); return -1; } + if (!selectval || !FD_ISSET(sd, &wset)) { Log("MySend - timeout"); return -1; } n = send(sd, (char *)msg + nsent, len - nsent, 0); if (n < 0) { LogErr("MySend", "send"); return -1; } nsent += n; @@ -450,11 +493,21 @@ static int my_recv(const int sd, void *const buf, const int len) // use an explicit while() loop instead. // Also, don't try to do '+=' arithmetic on the original "void *" pointer -- // arithmetic on "void *" pointers is compiler-dependent. - int remaining = len; + + 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; - while (remaining) + ssize_t num_read; + + while (remaining) { - ssize_t num_read = recv(sd, ptr, remaining, 0); + FD_ZERO(&rset); + FD_SET(sd, &rset); + selectval = select(sd+1, &rset, NULL, NULL, &timeout); + if (selectval < 0) { LogErr("my_recv", "select"); return -1; } + if (!selectval || !FD_ISSET(sd, &rset)) { Log("my_recv - timeout"); return -1; } + num_read = recv(sd, ptr, remaining, 0); if ((num_read == 0) || (num_read < 0) || (num_read > remaining)) return -1; ptr += num_read; remaining -= num_read; @@ -475,7 +528,7 @@ mDNSlocal PktMsg *ReadTCPMsg(int sd, PktMsg *storage) unsigned int srclen; nread = my_recv(sd, &msglen, sizeof(msglen)); - if (nread < 0) { LogErr("TCPRequestForkFn", "recv"); goto error; } + if (nread < 0) { LogErr("ReadTCPMsg", "recv"); goto error; } msglen = ntohs(msglen); if (nread != sizeof(msglen)) { Log("Could not read length field of message"); goto error; } @@ -499,7 +552,7 @@ mDNSlocal PktMsg *ReadTCPMsg(int sd, PktMsg *storage) if (getpeername(sd, (struct sockaddr *)&pkt->src, &srclen) || srclen != sizeof(pkt->src)) { LogErr("ReadTCPMsg", "getpeername"); bzero(&pkt->src, sizeof(pkt->src)); } nread = my_recv(sd, &pkt->msg, msglen); - if (nread < 0) { LogErr("TCPRequestForkFn", "recv"); goto error; } + if (nread < 0) { LogErr("ReadTCPMsg", "recv"); goto error; } if (nread != msglen) { Log("Could not read entire message"); goto error; } if (pkt->len < sizeof(DNSMessageHeader)) { Log("ReadTCPMsg: Message too short (%d bytes)", pkt->len); goto error; } @@ -511,6 +564,33 @@ mDNSlocal PktMsg *ReadTCPMsg(int sd, PktMsg *storage) return NULL; } +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 selectval, err = -1, sd = socket(AF_INET, SOCK_DGRAM, 0); + + *trunc = mDNSfalse; + if (sd < 0) { LogErr("UDPServerTransaction", "socket"); goto end; } + if (sendto(sd, (char *)&request->msg, request->len, 0, (struct sockaddr *)&d->saddr, sizeof(d->saddr)) != (int)request->len) + { LogErr("UDPServerTransaction", "sendto"); goto end; } + + FD_ZERO(&rset); + FD_SET(sd, &rset); + selectval = select(sd+1, &rset, NULL, NULL, &timeout); + if (selectval < 0) { LogErr("UDPServerTransaction", "select"); goto end; } + if (!selectval || !FD_ISSET(sd, &rset)) { Log("UDPServerTransaction - timeout"); goto end; } + reply->len = recvfrom(sd, &reply->msg, sizeof(reply->msg), 0, NULL, NULL); + if ((int)reply->len < 0) { LogErr("UDPServerTransaction", "recvfrom"); goto end; } + if (reply->len < sizeof(DNSMessageHeader)) { Log("UDPServerTransaction - Message too short (%d bytes)", reply->len); goto end; } + if (reply->msg.h.flags.b[0] & kDNSFlag0_TC) *trunc = mDNStrue; + err = 0; + + end: + if (sd >= 0) close(sd); + return err; + } + // // Dynamic Update Utility Routines // @@ -846,10 +926,14 @@ mDNSlocal void PrintHelp(void) mDNSlocal int ProcessArgs(int argc, char *argv[], DaemonInfo *d) { int opt; - + struct in_addr server; + if (argc < 2) goto arg_error; + + // defaults, may be overriden by command option + d->port.NotAnInteger = htons(DAEMON_PORT); + inet_pton(AF_INET, LOOPBACK, &server); // use loopback if server not explicitly specified - d->port.NotAnInteger = htons(DAEMON_PORT); // default, may be overriden by command option while ((opt = getopt(argc, argv, "z:p:hfvs:k:")) != -1) { switch(opt) @@ -860,8 +944,8 @@ mDNSlocal int ProcessArgs(int argc, char *argv[], DaemonInfo *d) case 'h': PrintHelp(); return -1; case 'f': foreground = 1; break; case 'v': verbose = 1; break; - case 's': if (!inet_pton(AF_INET, optarg, &d->saddr)) goto arg_error; - break; + case 's': if (!inet_pton(AF_INET, optarg, &server)) goto arg_error; + break; case 'k': if (ReadAuthKey(argc, argv, d) < 0) goto arg_error; break; case 'z': if (!MakeDomainNameFromDNSNameString(&d->zone, optarg)) @@ -876,6 +960,16 @@ mDNSlocal int ProcessArgs(int argc, char *argv[], DaemonInfo *d) if (!d->zone.c[0]) goto arg_error; // zone is the only required argument if (d->AuthInfo) AssignDomainName(&d->AuthInfo->zone, &d->zone); // if we have a shared secret, use it for the entire zone + + // setup server's sockaddr + bzero(&d->saddr, sizeof(d->saddr)); + d->saddr.sin_addr = server; + d->saddr.sin_port = htons(NS_PORT); + d->saddr.sin_family = AF_INET; +#ifndef NOT_HAVE_SA_LEN + d->saddr.sin_len = sizeof(d->saddr); +#endif + return 0; arg_error: @@ -924,7 +1018,7 @@ mDNSlocal int SetupSockets(DaemonInfo *daemon) // set up Unix domain socket pair for LLQ polling thread to signal main thread that a change to the zone occurred if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockpair) < 0) { LogErr("SetupSockets", "socketpair"); return -1; } daemon->LLQEventListenSock = sockpair[0]; - daemon->LLQServPollSock = sockpair[1]; + daemon->LLQEventNotifySock = sockpair[1]; return 0; } @@ -933,9 +1027,9 @@ mDNSlocal int SetupSockets(DaemonInfo *daemon) // // Delete a resource record from the nameserver via a dynamic update -mDNSlocal void DeleteRecord(DaemonInfo *d, CacheRecord *rr, domainname *zone) +// sd is a socket already connected to the server +mDNSlocal void DeleteOneRecord(DaemonInfo *d, CacheRecord *rr, domainname *zone, int sd) { - int sd = -1; mDNSOpaque16 id; PktMsg pkt; mDNSu8 *ptr = pkt.msg.data; @@ -945,8 +1039,6 @@ mDNSlocal void DeleteRecord(DaemonInfo *d, CacheRecord *rr, domainname *zone) PktMsg *reply = NULL; VLog("Expiring record %s", GetRRDisplayString_rdb(&rr->resrec, &rr->resrec.rdata->u, buf)); - sd = ConnectToServer(d); - if (sd < 0) { Log("DeleteRecord: ConnectToServer failed"); goto end; } id.NotAnInteger = 0; InitializeDNSMessage(&pkt.msg.h, id, UpdateReqFlags); @@ -968,51 +1060,45 @@ mDNSlocal void DeleteRecord(DaemonInfo *d, CacheRecord *rr, domainname *zone) pkt.len = ptr - (mDNSu8 *)&pkt.msg; pkt.src.sin_addr.s_addr = htonl(INADDR_ANY); // address field set solely for verbose logging in subroutines pkt.src.sin_family = AF_INET; - if (SendTCPMsg(sd, &pkt)) { Log("DeleteRecord: SendTCPMsg failed"); } + if (SendTCPMsg(sd, &pkt)) { Log("DeleteOneRecord: SendTCPMsg failed"); } reply = ReadTCPMsg(sd, NULL); if (!SuccessfulUpdateTransaction(&pkt, reply)) - Log("Expiration update failed with rcode %d", reply->msg.h.flags.b[1] & kDNSFlag1_RC); + Log("Expiration update failed with rcode %d", reply ? reply->msg.h.flags.b[1] & kDNSFlag1_RC : -1); end: - if (!ptr) { Log("DeleteRecord: Error constructing lease expiration update"); } - if (sd >= 0) close(sd); + if (!ptr) { Log("DeleteOneRecord: Error constructing lease expiration update"); } if (reply) free(reply); } -// iterate over table, deleting expired records -mDNSlocal void DeleteExpiredRecords(DaemonInfo *d) +// iterate over table, deleting expired records (or all records if DeleteAll is true) +mDNSlocal void DeleteRecords(DaemonInfo *d, mDNSBool DeleteAll) { - int i; - RRTableElem *ptr, *prev, *fptr; struct timeval now; + int i, sd = ConnectToServer(d); + if (sd < 0) { 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; } - if (gettimeofday(&now, NULL)) { LogErr("DeleteExpiredRecords ", "gettimeofday"); return; } - if (pthread_mutex_lock(&d->tablelock)) { LogErr("DeleteExpiredRecords", "pthread_mutex_lock"); return; } for (i = 0; i < d->nbuckets; i++) { - ptr = d->table[i]; - prev = NULL; - while (ptr) + RRTableElem **ptr = &d->table[i]; + while (*ptr) { - if (ptr->expire - now.tv_sec < 0) + if (DeleteAll || (*ptr)->expire - now.tv_sec < 0) { + RRTableElem *fptr; // delete record from server - DeleteRecord(d, &ptr->rr, &ptr->zone); - if (prev) prev->next = ptr->next; - else d->table[i] = ptr->next; - fptr = ptr; - ptr = ptr->next; + DeleteOneRecord(d, &(*ptr)->rr, &(*ptr)->zone, sd); + fptr = *ptr; + *ptr = (*ptr)->next; free(fptr); d->nelems--; } - else - { - prev = ptr; - ptr = ptr->next; - } + else ptr = &(*ptr)->next; } } pthread_mutex_unlock(&d->tablelock); + close(sd); } // @@ -1149,32 +1235,54 @@ mDNSlocal PktMsg *FormatLeaseReply(DaemonInfo *d, PktMsg *orig, mDNSu32 lease) mDNSlocal PktMsg *HandleRequest(PktMsg *pkt, DaemonInfo *d) { int sd = -1; - PktMsg *reply = NULL, *LeaseReply; + PktMsg *reply = NULL, *LeaseReply, buf; mDNSs32 lease; - char buf[32]; + char addrbuf[32], pingmsg[4]; // send msg to server, read reply - sd = ConnectToServer(d); - if (sd < 0) - { Log("Discarding request from %s due to connection errors", inet_ntop(AF_INET, &pkt->src.sin_addr, buf, 32)); goto cleanup; } - if (SendTCPMsg(sd, pkt) < 0) - { Log("Couldn't relay message from %s to server. Discarding.", inet_ntop(AF_INET, &pkt->src.sin_addr, buf, 32)); goto cleanup; } - reply = ReadTCPMsg(sd, NULL); - + + if (pkt->len <= 512) + { + mDNSBool trunc; + if (UDPServerTransaction(d, pkt, &buf, &trunc) < 0) + Log("HandleRequest - UDPServerTransaction failed. Trying TCP"); + else if (trunc) VLog("HandleRequest - answer truncated. Using TCP"); + else reply = &buf; // success + } + + if (!reply) + { + sd = ConnectToServer(d); + if (sd < 0) + { Log("Discarding request from %s due to connection errors", inet_ntop(AF_INET, &pkt->src.sin_addr, addrbuf, 32)); goto cleanup; } + if (SendTCPMsg(sd, pkt) < 0) + { Log("Couldn't relay message from %s to server. Discarding.", inet_ntop(AF_INET, &pkt->src.sin_addr, addrbuf, 32)); goto cleanup; } + reply = ReadTCPMsg(sd, &buf); + } + // process reply if (!SuccessfulUpdateTransaction(pkt, reply)) - { VLog("Message from %s not a successful update.", inet_ntop(AF_INET, &pkt->src.sin_addr, buf, 32)); goto cleanup; } + { VLog("Message from %s not a successful update.", inet_ntop(AF_INET, &pkt->src.sin_addr, addrbuf, 32)); goto cleanup; } lease = GetPktLease(pkt); UpdateLeaseTable(pkt, d, lease); if (lease > 0) { LeaseReply = FormatLeaseReply(d, reply, lease); if (!LeaseReply) Log("HandleRequest - unable to format lease reply"); - free(reply); reply = LeaseReply; } + + // tell the main thread there was an update so it can send LLQs + if (send(d->LLQEventNotifySock, pingmsg, sizeof(pingmsg), 0) != sizeof(pingmsg)) LogErr("HandleRequest", "send"); + cleanup: if (sd >= 0) close(sd); + if (reply == &buf) + { + reply = malloc(sizeof(*reply)); + if (!reply) LogErr("HandleRequest", "malloc"); + else { reply->len = buf.len; memcpy(&reply->msg, &buf.msg, buf.len); } + } return reply; } @@ -1219,9 +1327,13 @@ mDNSlocal void DeleteLLQ(DaemonInfo *d, LLQEntry *e) inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32); VLog("Deleting LLQ table entry for %##s client %s", e->qname.c, addr); - // free shared answer structure if ref count drops to zero - if (a && !(--a->refcount)) + 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]; @@ -1233,7 +1345,7 @@ mDNSlocal void DeleteLLQ(DaemonInfo *d, LLQEntry *e) } while (*tbl && *tbl != a) tbl = &(*tbl)->next; - if (*tbl) { *tbl = (*tbl)->next; free(a); } + if (*tbl) { *tbl = (*tbl)->next; free(a); d->AnswerTableCount--; } else Log("Error: DeleteLLQ - AnswerList not found in table"); } @@ -1260,20 +1372,17 @@ mDNSlocal int SendLLQ(DaemonInfo *d, PktMsg *pkt, struct sockaddr_in dst) return err; } -// if non-negative, sd is a TCP socket connected to the nameserver -// otherwise, this routine creates and closes its own socket -mDNSlocal CacheRecord *AnswerQuestion(DaemonInfo *d, AnswerListElem *e, int sd) +mDNSlocal CacheRecord *AnswerQuestion(DaemonInfo *d, AnswerListElem *e) { PktMsg q; - int i; + int i, sd = -1; const mDNSu8 *ansptr; mDNSu8 *end = q.msg.data; mDNSOpaque16 id, flags = QueryFlags; - PktMsg *reply = NULL; + PktMsg buf, *reply = NULL; LargeCacheRecord lcr; CacheRecord *AnswerList = NULL; mDNSu8 rcode; - mDNSBool CloseSDOnExit = sd < 0; VLog("Querying server for %##s type %d", e->name.c, e->type); @@ -1284,11 +1393,26 @@ mDNSlocal CacheRecord *AnswerQuestion(DaemonInfo *d, AnswerListElem *e, int sd) 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); + + 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 (sd < 0) sd = ConnectToServer(d); - if (sd < 0) { Log("AnswerQuestion: ConnectToServer failed"); goto end; } - if (SendTCPMsg(sd, &q)) { Log("AnswerQuestion: SendTCPMsg failed"); close(sd); goto end; } - reply = ReadTCPMsg(sd, NULL); + if (!reply) + { + sd = ConnectToServer(d); + if (sd < 0) { Log("AnswerQuestion: ConnectToServer failed"); goto end; } + if (SendTCPMsg(sd, &q)) { Log("AnswerQuestion: SendTCPMsg failed"); close(sd); goto end; } + reply = ReadTCPMsg(sd, NULL); + close(sd); + } 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; } @@ -1318,18 +1442,22 @@ mDNSlocal CacheRecord *AnswerQuestion(DaemonInfo *d, AnswerListElem *e, int sd) } end: - if (sd > -1 && CloseSDOnExit) close(sd); - if (reply) free(reply); + if (reply && reply != &buf) free(reply); return AnswerList; } -// Routine sets EventList to contain Add/Remove events, and deletes any removes from the KnownAnswer list -mDNSlocal void UpdateAnswerList(DaemonInfo *d, AnswerListElem *a, int sd) +// 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, sd); + NewAnswers = AnswerQuestion(d, a); // first pass - mark all answers for deletion for (ka = &a->KnownAnswers; *ka; ka = &(*ka)->next) @@ -1383,7 +1511,9 @@ mDNSlocal void UpdateAnswerList(DaemonInfo *d, AnswerListElem *a, int sd) cr = NewAnswers; NewAnswers = NewAnswers->next; free(cr); - } + } + + return NULL; } mDNSlocal void SendEvents(DaemonInfo *d, LLQEntry *e) @@ -1407,7 +1537,7 @@ mDNSlocal void SendEvents(DaemonInfo *d, LLQEntry *e) 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 - UpdateAnswerList returned NULL"); return; } + if (!end) { Log("Error: SendEvents - PutResourceRecordTTLJumbo returned NULL"); return; } } FormatLLQOpt(&opt, kLLQOp_Event, e->id, LLQLease(e)); @@ -1418,6 +1548,28 @@ mDNSlocal void SendEvents(DaemonInfo *d, LLQEntry *e) if (SendLLQ(d, &response, e->cli) < 0) LogMsg("Error: SendEvents - SendLLQ"); } +mDNSlocal void PrintLLQAnswers(DaemonInfo *d) + { + int i; + char rrbuf[80]; + + 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; @@ -1431,9 +1583,19 @@ mDNSlocal void PrintLLQTable(DaemonInfo *d) e = d->LLQTable[i]; while(e) { - inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32); - Log("LLQ from %##s type %d lease %d (%d remaining)", - addr, e->qname.c, e->qtype, e->lease, LLQLease(e)); + 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; } } @@ -1443,14 +1605,13 @@ mDNSlocal void PrintLLQTable(DaemonInfo *d) mDNSlocal void GenLLQEvents(DaemonInfo *d) { LLQEntry **e; - int i, sd; + int i; struct timeval t; - + UpdateAnswerListArgs *args; + VLog("Generating LLQ Events"); gettimeofday(&t, NULL); - sd = ConnectToServer(d); - if (sd < 0) { Log("GenLLQEvents: ConnectToServer failed"); return; } // get all answers up to date for (i = 0; i < LLQ_TABLESIZE; i++) @@ -1458,11 +1619,26 @@ mDNSlocal void GenLLQEvents(DaemonInfo *d) AnswerListElem *a = d->AnswerTable[i]; while(a) { - UpdateAnswerList(d, a, sd); + 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++) { @@ -1504,79 +1680,6 @@ mDNSlocal void GenLLQEvents(DaemonInfo *d) a = a->next; } } - - close(sd); - } - -// Monitor zone for changes that may produce LLQ events -mDNSlocal void *LLQEventMonitor(void *DInfoPtr) - { - DaemonInfo *d = DInfoPtr; - PktMsg q; - mDNSu8 *end = q.msg.data; - const mDNSu8 *ptr; - mDNSOpaque16 id, flags = QueryFlags; - PktMsg reply; - mDNSs32 serial = 0; - mDNSBool SerialInitialized = mDNSfalse; - int sd; - LargeCacheRecord lcr; - ResourceRecord *rr = &lcr.r.resrec; - int i, sleeptime = 0; - domainname zone; - char pingmsg[4]; - - // create question - id.NotAnInteger = 0; - InitializeDNSMessage(&q.msg.h, id, flags); - AssignDomainName(&zone, &d->zone); - end = putQuestion(&q.msg, end, end + AbsoluteMaxDNSMessageData, &zone, kDNSType_SOA, kDNSClass_IN); - if (!end) { Log("Error: LLQEventMonitor - putQuestion returned NULL"); return NULL; } - q.len = (int)(end - (mDNSu8 *)&q.msg); - - sd = ConnectToServer(d); - if (sd < 0) { Log("LLQEventMonitor: ConnectToServer failed"); return NULL; } - - while(1) - { - usleep(sleeptime); - sleeptime = LLQ_MONITOR_ERR_INTERVAL; // if we bail on error below, rate limit retry - - // send message, receive response - if (SendTCPMsg(sd, &q)) { Log("LLQEventMonitor: SendTCPMsg failed"); continue; } - if (!ReadTCPMsg(sd, &reply)) { Log("LLQEventMonitor: ReadTCPMsg failed"); continue; } - end = (mDNSu8 *)&reply.msg + reply.len; - if (reply.msg.h.flags.b[1] & kDNSFlag1_RC) { Log("LLQEventMonitor - received non-zero rcode"); continue; } - - // find answer - ptr = LocateAnswers(&reply.msg, end); - if (!ptr) { Log("Error: LLQEventMonitor - LocateAnswers returned NULL"); continue; } - for (i = 0; i < reply.msg.h.numAnswers; i++) - { - ptr = GetLargeResourceRecord(NULL, &reply.msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); - if (!ptr) { Log("Error: LLQEventMonitor - GetLargeResourceRecord returned NULL"); continue; } - if (rr->rrtype != kDNSType_SOA || rr->rrclass != kDNSClass_IN || !SameDomainName(rr->name, &zone)) continue; - if (!SerialInitialized) - { - // first time through loop - SerialInitialized = mDNStrue; - serial = rr->rdata->u.soa.serial; - sleeptime = LLQ_MONITOR_INTERVAL; - break; - } - else if (rr->rdata->u.soa.serial != serial) - { - // update serial, wake main thread - serial = rr->rdata->u.soa.serial; - VLog("LLQEventMonitor: zone changed. Signaling main thread."); - if (send(d->LLQServPollSock, pingmsg, sizeof(pingmsg), 0) != sizeof(pingmsg)) - { LogErr("LLQEventMonitor", "send"); break; } - } - sleeptime = LLQ_MONITOR_INTERVAL; - break; - } - if (!ptr) Log("LLQEventMonitor: response to query did not contain SOA"); - } } mDNSlocal void SetAnswerList(DaemonInfo *d, LLQEntry *e) @@ -1591,15 +1694,12 @@ mDNSlocal void SetAnswerList(DaemonInfo *d, LLQEntry *e) AssignDomainName(&a->name, &e->qname); a->type = e->qtype; a->refcount = 0; - a->KnownAnswers = NULL; a->EventList = NULL; + a->UseTCP = mDNSfalse; a->next = d->AnswerTable[bucket]; d->AnswerTable[bucket] = a; - - // to get initial answer list, call UpdateAnswerList and move cache records from EventList to KnownAnswers - UpdateAnswerList(d, a, -1); - a->KnownAnswers = a->EventList; - a->EventList = NULL; + d->AnswerTableCount++; + a->KnownAnswers = AnswerQuestion(d, a); } e->AnswerList = a; @@ -1654,8 +1754,11 @@ mDNSlocal void LLQRefresh(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaqu if (llq->lease) { + struct timeval t; if (llq->lease < LLQ_MIN_LEASE) llq->lease = LLQ_MIN_LEASE; else if (llq->lease > LLQ_MAX_LEASE) llq->lease = LLQ_MIN_LEASE; + gettimeofday(&t, NULL); + e->expire = t.tv_sec + llq->lease; } ack.src.sin_addr.s_addr = 0; // unused @@ -1987,6 +2090,7 @@ mDNSlocal void *TCPRequestForkFn(void *vptr) } cleanup: + close(req->sd); free(req); if (in) free(in); if (out) free(out); @@ -1995,31 +2099,34 @@ mDNSlocal void *TCPRequestForkFn(void *vptr) mDNSlocal int RecvTCPRequest(int sd, DaemonInfo *d) { - TCPRequestArgs *req; + TCPRequestArgs *req = NULL; pthread_t tid; unsigned int clilen = sizeof(req->cliaddr); req = malloc(sizeof(TCPRequestArgs)); - if (!req) { LogErr("RecvTCPRequest", "malloc"); return -1; } + if (!req) { LogErr("RecvTCPRequest", "malloc"); goto error; } bzero(req, sizeof(*req)); req->d = d; req->sd = accept(sd, (struct sockaddr *)&req->cliaddr, &clilen); - if (req->sd < 0) { LogErr("RecvTCPRequest", "accept"); return -1; } - if (clilen != sizeof(req->cliaddr)) { Log("Client address of unknown size %d", clilen); free(req); return -1; } - if (pthread_create(&tid, NULL, TCPRequestForkFn, req)) { LogErr("RecvTCPRequest", "pthread_create"); free(req); return -1; } + if (req->sd < 0) { LogErr("RecvTCPRequest", "accept"); goto error; } + if (clilen != sizeof(req->cliaddr)) { Log("Client address of unknown size %d", clilen); goto error; } + if (pthread_create(&tid, NULL, TCPRequestForkFn, req)) { LogErr("RecvTCPRequest", "pthread_create"); goto error; } pthread_detach(tid); return 0; + + error: + if (req) free(req); + return -1; } // main event loop // listen for incoming requests, periodically check table for expired records, respond to signals mDNSlocal int ListenForUpdates(DaemonInfo *d) { - int err; - int maxfdp1; + int maxfdp1, nfds; fd_set rset; - struct timeval timenow, timeout = { 0, 0 }; - long NextTableCheck = 0; + struct timeval timenow, timeout, EventTS, tablecheck = { 0, 0 }; + mDNSBool EventsPending = mDNSfalse; VLog("Listening for requests..."); @@ -2030,42 +2137,72 @@ mDNSlocal int ListenForUpdates(DaemonInfo *d) while(1) { - // expire records if necessary, set timeout + // set timeout + timeout.tv_sec = timeout.tv_usec = 0; if (gettimeofday(&timenow, NULL)) { LogErr("ListenForUpdates", "gettimeofday"); return -1; } - if (timenow.tv_sec >= NextTableCheck) + if (EventsPending) { - DeleteExpiredRecords(d); - NextTableCheck = timenow.tv_sec + EXPIRATION_INTERVAL; + if (timenow.tv_sec - EventTS.tv_sec >= 5) // if we've been waiting 5 seconds for a "quiet" period to send + { GenLLQEvents(d); EventsPending = mDNSfalse; } // events, we go ahead and do it now + else timeout.tv_usec = 500000; // else do events after 1/2 second with no new events or LLQs + } + if (!EventsPending) + { + // if no pending events, timeout when we need to check for expired records + if (tablecheck.tv_sec && timenow.tv_sec - tablecheck.tv_sec >= 0) + { DeleteRecords(d, mDNSfalse); tablecheck.tv_sec = 0; } // table check overdue + if (!tablecheck.tv_sec) tablecheck.tv_sec = timenow.tv_sec + EXPIRATION_INTERVAL; + timeout.tv_sec = tablecheck.tv_sec - timenow.tv_sec; } - timeout.tv_sec = NextTableCheck - timenow.tv_sec; FD_SET(d->tcpsd, &rset); FD_SET(d->udpsd, &rset); FD_SET(d->LLQEventListenSock, &rset); - err = select(maxfdp1, &rset, NULL, NULL, &timeout); - if (err < 0) + nfds = select(maxfdp1, &rset, NULL, NULL, &timeout); + if (nfds < 0) { if (errno == EINTR) { - if (terminate) { DeleteExpiredRecords(d); return 0; } - else if (dumptable) { PrintLeaseTable(d); PrintLLQTable(d); dumptable = 0; } + if (terminate) + { + // close sockets to prevent clients from making new requests during shutdown + close(d->tcpsd); + close(d->udpsd); + d->tcpsd = d->udpsd = -1; + DeleteRecords(d, mDNStrue); + return 0; + } + else if (dumptable) { PrintLeaseTable(d); PrintLLQTable(d); PrintLLQAnswers(d); dumptable = 0; } else Log("Received unhandled signal - continuing"); } - else { LogErr("ListenForUpdates", "select"); return -1; } + else + { + LogErr("ListenForUpdates", "select"); return -1; + } } - else + else if (nfds) { if (FD_ISSET(d->tcpsd, &rset)) RecvTCPRequest(d->tcpsd, d); if (FD_ISSET(d->udpsd, &rset)) RecvUDPRequest(d->udpsd, d); if (FD_ISSET(d->LLQEventListenSock, &rset)) { // clear signalling data off socket - char buf[32]; - recv(d->LLQEventListenSock, buf, 32, 0); - GenLLQEvents(d); + char buf[256]; + recv(d->LLQEventListenSock, buf, 256, 0); + if (!EventsPending) + { + EventsPending = mDNStrue; + if (gettimeofday(&EventTS, NULL)) { LogErr("ListenForUpdates", "gettimeofday"); return -1; } + } } } + else + { + // timeout + if (EventsPending) { GenLLQEvents(d); EventsPending = mDNSfalse; } + else { DeleteRecords(d, mDNSfalse); tablecheck.tv_sec = 0; } + } } return 0; } @@ -2080,17 +2217,26 @@ mDNSlocal void HndlSignal(int sig) int main(int argc, char *argv[]) { - pthread_t LLQtid; DaemonInfo *d; - + struct rlimit rlim; + d = malloc(sizeof(*d)); if (!d) { LogErr("main", "malloc"); exit(1); } - bzero(d, sizeof(DaemonInfo)); + bzero(d, sizeof(DaemonInfo)); if (signal(SIGTERM, HndlSignal) == SIG_ERR) perror("Can't catch SIGTERM"); if (signal(INFO_SIGNAL, HndlSignal) == SIG_ERR) perror("Can't catch SIGINFO"); if (signal(SIGINT, HndlSignal) == SIG_ERR) perror("Can't catch SIGINT"); if (signal(SIGPIPE, SIG_IGN ) == SIG_ERR) perror("Can't ignore SIGPIPE"); + + // remove open file limit + rlim.rlim_max = RLIM_INFINITY; + rlim.rlim_cur = RLIM_INFINITY; + if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) + { + LogErr("main", "setrlimit"); + Log("Using default file descriptor resource limit"); + } if (ProcessArgs(argc, argv, d) < 0) exit(1); @@ -2107,14 +2253,8 @@ int main(int argc, char *argv[]) if (InitLeaseTable(d) < 0) exit(1); if (SetupSockets(d) < 0) exit(1); if (SetUpdateSRV(d) < 0) exit(1); - - if (pthread_create(&LLQtid, NULL, LLQEventMonitor, d)) { LogErr("main", "pthread_create"); } - else - { - pthread_detach(LLQtid); - ListenForUpdates(d); - } - + + ListenForUpdates(d); if (ClearUpdateSRV(d) < 0) exit(1); // clear update srv's even if ListenForUpdates or pthread_create returns an error free(d); exit(0); diff --git a/mDNSPosix/mDNSPosix.c b/mDNSPosix/mDNSPosix.c index 3970b4b..4b0ab0b 100755 --- a/mDNSPosix/mDNSPosix.c +++ b/mDNSPosix/mDNSPosix.c @@ -37,6 +37,19 @@ Change History (most recent first): $Log: mDNSPosix.c,v $ +Revision 1.75 2006/01/05 22:04:57 cheshire + Log error message when send fails with "operation not permitted" + +Revision 1.74 2006/01/05 21:45:27 cheshire + Fix uninitialized structure member in IPv6 code + +Revision 1.73 2005/10/11 21:31:46 cheshire + Don't depend on IP_RECVTTL succeeding (not available on all platforms) + +Revision 1.72 2005/09/08 20:45:26 cheshire +Default dot-local host name should be "Computer" not "Macintosh", +since the machine this is running on is most likely NOT a Mac. + Revision 1.71 2005/02/26 01:29:12 cheshire Ignore multicasts accidentally delivered to our unicast receiving socket @@ -434,11 +447,15 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const ms if (err > 0) err = 0; else if (err < 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 (thisIntf) - verbosedebugf("mDNSPlatformSendUDP got error %d (%s) sending packet to %#a on interface %#a/%s/%d", + 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 - verbosedebugf("mDNSPlatformSendUDP got error %d (%s) sending packet to %#a", errno, strerror(errno), dst); + LogMsg("mDNSPlatformSendUDP got error %d (%s) sending packet to %#a", errno, strerror(errno), dst); } return PosixErrorToStatus(err); @@ -733,6 +750,7 @@ mDNSlocal int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interf 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); @@ -790,13 +808,13 @@ mDNSlocal int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interf #if defined(IP_RECVTTL) // Linux if (err == 0) { - err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVTTL, &kOn, sizeof(kOn)); - if (err < 0) { err = errno; perror("setsockopt - IP_RECVTTL"); } + 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 && port.NotAnInteger) + if (err == 0 && JoinMulticastGroup) { imr.imr_multiaddr.s_addr = AllDNSLinkGroupv4.NotAnInteger; imr.imr_interface = ((struct sockaddr_in*)intfAddr)->sin_addr; @@ -805,7 +823,7 @@ mDNSlocal int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interf } // Specify outgoing interface too - if (err == 0 && port.NotAnInteger) + 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"); } @@ -862,7 +880,7 @@ mDNSlocal int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interf #endif // Add multicast group membership on this interface - if (err == 0 && port.NotAnInteger) + if (err == 0 && JoinMulticastGroup) { imr6.ipv6mr_multiaddr = *(const struct in6_addr*)&AllDNSLinkGroupv6; imr6.ipv6mr_interface = interfaceIndex; @@ -876,7 +894,7 @@ mDNSlocal int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interf } // Specify outgoing interface too - if (err == 0 && port.NotAnInteger) + if (err == 0 && JoinMulticastGroup) { u_int multicast_if = interfaceIndex; err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_if, sizeof(multicast_if)); @@ -918,7 +936,7 @@ mDNSlocal int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interf bindAddr6.sin6_family = AF_INET6; bindAddr6.sin6_port = port.NotAnInteger; bindAddr6.sin6_flowinfo = 0; -// bindAddr6.sin6_addr.s_addr = IN6ADDR_ANY_INIT; // Want to receive multicasts AND unicasts on this socket + bindAddr6.sin6_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); } @@ -1345,12 +1363,12 @@ mDNSexport mStatus mDNSPlatformInit(mDNS *const m) // Set up the nice label m->nicelabel.c[0] = 0; GetUserSpecifiedFriendlyComputerName(&m->nicelabel); - if (m->nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->nicelabel, "Macintosh"); + 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, "Macintosh"); + if (m->hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->hostlabel, "Computer"); mDNS_SetFQDN(m); diff --git a/mDNSPosix/mDNSUNP.c b/mDNSPosix/mDNSUNP.c index 32a016c..f9bd4a6 100755 --- a/mDNSPosix/mDNSUNP.c +++ b/mDNSPosix/mDNSUNP.c @@ -24,6 +24,36 @@ Change History (most recent first): $Log: mDNSUNP.c,v $ +Revision 1.32 2005/12/21 02:56:43 cheshire + get_ifi_info() should fake ifi_index when SIOCGIFINDEX undefined + +Revision 1.31 2005/12/21 02:46:05 cheshire + mDNSUNP.c needs to include on 4.4BSD Lite + +Revision 1.30 2005/11/29 20:03:02 mkrochma +Wrapped sin_len with #ifndef NOT_HAVE_SA_LEN + +Revision 1.29 2005/11/12 02:23:10 cheshire + mDNSUNP.c needs to deal with lame results from SIOCGIFNETMASK, SIOCGIFBRDADDR and SIOCGIFDSTADDR + +Revision 1.28 2005/10/31 22:09:45 cheshire +Buffer "char addr6[33]" was seven bytes too small + +Revision 1.27 2005/06/29 15:54:21 cheshire + mDNSResponder-107.1 does not work on FreeBSD +Refine last checkin so that it (hopefully) doesn't break get_ifi_info() for every other OS + +Revision 1.26 2005/04/08 21:43:59 ksekar + mDNSPosix (v98) retrieve interface list bug on AMD64 architecture +Submitted by Andrew de Quincey + +Revision 1.25 2005/04/08 21:37:57 ksekar + get_ifi_info doesn't return IPv6 interfaces on Linux + +Revision 1.24 2005/04/08 21:30:16 ksekar + Compiling problems with mDNSResponder-98 on Solaris/Sparc v9 +Patch submitted by Bernd Kuhls + Revision 1.23 2004/12/01 04:25:05 cheshire Darwin patches for Solaris and Suse Provide daemon() for platforms that don't have it @@ -111,6 +141,15 @@ First checkin #include #include +/* Some weird platforms derived from 4.4BSD Lite (e.g. EFI) need the ALIGN(P) + 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 @@ -129,10 +168,135 @@ First checkin #include #endif -#if defined(AF_INET6) && HAVE_IPV6 +#if defined(AF_INET6) && HAVE_IPV6 && !HAVE_LINUX #include #endif +#if defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX +#include +#include + +/* 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; + } + } + +/* 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[8], 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 */ + } +#endif // defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX + struct ifi_info *get_ifi_info(int family, int doaliases) { int junk; @@ -150,7 +314,11 @@ struct ifi_info *get_ifi_info(int family, int doaliases) struct sockaddr_in6 *sinptr6; #endif - sockfd = -1; +#if defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX + if(family == AF_INET6) return get_ifi_info_linuxv6(family, doaliases); +#endif + + sockfd = -1; sockf6 = -1; buf = NULL; ifihead = NULL; @@ -190,10 +358,13 @@ struct ifi_info *get_ifi_info(int family, int doaliases) for (ptr = buf; ptr < buf + ifc.ifc_len; ) { ifr = (struct ifreq *) ptr; - len = GET_SA_LEN(ifr->ifr_addr); - ptr += sizeof(ifr->ifr_name) + len; /* for next one in buffer */ - -// fprintf(stderr, "intf %d name=%s AF=%d\n", index, ifr->ifr_name, ifr->ifr_addr.sa_family); + /* Advance to next one in buffer */ + if (sizeof(struct ifreq) > sizeof(ifr->ifr_name) + GET_SA_LEN(ifr->ifr_addr)) + ptr += sizeof(struct ifreq); + else + 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 */ @@ -230,9 +401,11 @@ struct ifi_info *get_ifi_info(int family, int doaliases) ifi->ifi_index = if_nametoindex(ifr->ifr_name); #else ifrcopy = *ifr; +#ifdef SIOCGIFINDEX 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 */ #endif memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME); @@ -254,6 +427,11 @@ struct ifi_info *get_ifi_info(int family, int doaliases) 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); +#endif + sinptr->sin_family = AF_INET; memcpy(ifi->ifi_netmask, sinptr, sizeof(struct sockaddr_in)); #endif @@ -263,6 +441,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 */ +#ifndef NOT_HAVE_SA_LEN + sinptr->sin_len = sizeof( struct sockaddr_in ); +#endif + sinptr->sin_family = AF_INET; ifi->ifi_brdaddr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in)); if (ifi->ifi_brdaddr == NULL) { goto gotError; @@ -277,6 +460,11 @@ struct ifi_info *get_ifi_info(int family, int doaliases) goto gotError; } 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 ); +#endif + sinptr->sin_family = AF_INET; ifi->ifi_dstaddr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in)); if (ifi->ifi_dstaddr == NULL) { goto gotError; @@ -534,6 +722,8 @@ struct in_pktinfo #ifdef NOT_HAVE_DAEMON #include #include +#include + int daemon(int nochdir, int noclose) { switch (fork()) diff --git a/mDNSPosix/mDNSUNP.h b/mDNSPosix/mDNSUNP.h index 786fef7..912c9d8 100755 --- a/mDNSPosix/mDNSUNP.h +++ b/mDNSPosix/mDNSUNP.h @@ -24,6 +24,9 @@ Change History (most recent first): $Log: mDNSUNP.h,v $ +Revision 1.18 2005/04/08 21:37:57 ksekar + get_ifi_info doesn't return IPv6 interfaces on Linux + Revision 1.17 2004/12/17 19:32:43 cheshire Add missing semicolon @@ -90,6 +93,10 @@ First checkin #include #include +#ifdef HAVE_LINUX +#include +#endif + #ifdef __cplusplus extern "C" { #endif @@ -99,8 +106,12 @@ First checkin #endif #if !defined(_SS_MAXSIZE) - #define sockaddr_storage sockaddr -#endif +#if HAVE_IPV6 +#define sockaddr_storage sockaddr_in6 +#else +#define sockaddr_storage sockaddr +#endif // HAVE_IPV6 +#endif // !defined(_SS_MAXSIZE) #ifndef NOT_HAVE_SA_LEN #define GET_SA_LEN(X) (sizeof(struct sockaddr) > ((struct sockaddr*)&(X))->sa_len ? \ @@ -146,6 +157,17 @@ struct ifi_info { 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): */ diff --git a/mDNSPosix/nss_mdns.c b/mDNSPosix/nss_mdns.c index 6baecca..35e02fc 100755 --- a/mDNSPosix/nss_mdns.c +++ b/mDNSPosix/nss_mdns.c @@ -868,6 +868,7 @@ mdns_lookup_name ( DNSServiceErrorType errcode; DNSServiceRef sdref; ns_type_t rrtype; + nss_status status; if (MDNS_VERBOSE) syslog (LOG_DEBUG, @@ -919,7 +920,9 @@ mdns_lookup_name ( return set_err_mdns_failed (result); } - return handle_events (sdref, result, fullname); + status = handle_events (sdref, result, fullname); + DNSServiceRefDeallocate (sdref); + return status; } @@ -952,6 +955,7 @@ mdns_lookup_addr ( { DNSServiceErrorType errcode; DNSServiceRef sdref; + nss_status status; if (MDNS_VERBOSE) syslog (LOG_DEBUG, @@ -991,7 +995,9 @@ mdns_lookup_addr ( return set_err_mdns_failed (result); } - return handle_events (sdref, result, addr_str); + status = handle_events (sdref, result, addr_str); + DNSServiceRefDeallocate (sdref); + return status; } diff --git a/mDNSResponder.sln b/mDNSResponder.sln index 423805f..353c2de 100755 --- a/mDNSResponder.sln +++ b/mDNSResponder.sln @@ -1,43 +1,78 @@ -Microsoft Visual Studio Solution File, Format Version 7.00 +Microsoft Visual Studio Solution File, Format Version 8.00 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DLL", "mDNSWindows\DLL\dnssd.vcproj", "{AB581101-18F0-46F6-B56A-83A6B1EA657E}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mDNSResponder", "mDNSWindows\SystemService\Service.vcproj", "{C1D98254-BA27-4427-A3BE-A68CA2CC5F69}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NSPTool", "mDNSWindows\NSPTool\NSPTool.vcproj", "{208B3A9F-1CA0-4D1D-9D6C-C61616F94705}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mdnsNSP", "mDNSWindows\mdnsNSP\mdnsNSP.vcproj", "{F4F15529-F0EB-402F-8662-73C5797EE557}" + ProjectSection(ProjectDependencies) = postProject + {AB581101-18F0-46F6-B56A-83A6B1EA657E} = {AB581101-18F0-46F6-B56A-83A6B1EA657E} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExplorerPlugin", "Clients\ExplorerPlugin\ExplorerPlugin.vcproj", "{BB8AC1B5-6587-4163-BDC6-788B157705CA}" + ProjectSection(ProjectDependencies) = postProject + {AB581101-18F0-46F6-B56A-83A6B1EA657E} = {AB581101-18F0-46F6-B56A-83A6B1EA657E} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PrinterSetupWizard", "Clients\PrinterSetupWizard\PrinterSetupWizard.vcproj", "{B1D2CDA2-CC8F-45D5-A694-2EE45B0308CF}" + ProjectSection(ProjectDependencies) = postProject + {AB581101-18F0-46F6-B56A-83A6B1EA657E} = {AB581101-18F0-46F6-B56A-83A6B1EA657E} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DLL.NET", "mDNSWindows\DLL.NET\dnssd_NET.vcproj", "{9C6701E2-82B7-44B7-9B5E-3897D9153F79}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ControlPanel", "mDNSWindows\ControlPanel\ControlPanel.vcproj", "{F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}" + ProjectSection(ProjectDependencies) = postProject + {AB581101-18F0-46F6-B56A-83A6B1EA657E} = {AB581101-18F0-46F6-B56A-83A6B1EA657E} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PrinterSetupWizardLocRes", "Clients\PrinterSetupWizard\PrinterSetupWizardLocRes.vcproj", "{B1D2CDA2-CC8F-45D5-A694-2EE45B0308CF}" + ProjectSection(ProjectDependencies) = postProject + {AB581101-18F0-46F6-B56A-83A6B1EA657E} = {AB581101-18F0-46F6-B56A-83A6B1EA657E} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PrinterSetupWizardRes", "Clients\PrinterSetupWizard\PrinterSetupWizardRes.vcproj", "{B1D2CDA2-CC8F-45D5-A694-2EE45B0308CF}" + ProjectSection(ProjectDependencies) = postProject + {AB581101-18F0-46F6-B56A-83A6B1EA657E} = {AB581101-18F0-46F6-B56A-83A6B1EA657E} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExplorerPluginLocRes", "Clients\ExplorerPlugin\ExplorerPluginLocRes.vcproj", "{BB8AC1B5-6587-4163-BDC6-788B157705CA}" + ProjectSection(ProjectDependencies) = postProject + {AB581101-18F0-46F6-B56A-83A6B1EA657E} = {AB581101-18F0-46F6-B56A-83A6B1EA657E} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExplorerPluginRes", "Clients\ExplorerPlugin\ExplorerPluginRes.vcproj", "{BB8AC1B5-6587-4163-BDC6-788B157705CA}" + ProjectSection(ProjectDependencies) = postProject + {AB581101-18F0-46F6-B56A-83A6B1EA657E} = {AB581101-18F0-46F6-B56A-83A6B1EA657E} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dns-sd", "Clients\DNS-SD.VisualStudio\dns-sd.vcproj", "{AA230639-E115-4A44-AA5A-44A61235BA50}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Java", "mDNSWindows\Java\Java.vcproj", "{9CE2568A-3170-41C6-9F20-A0188A9EC114}" + ProjectSection(ProjectDependencies) = postProject + {AB581101-18F0-46F6-B56A-83A6B1EA657E} = {AB581101-18F0-46F6-B56A-83A6B1EA657E} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JavaSamples", "Clients\Java\JavaSamples.vcproj", "{A987A0C1-344F-475C-869C-F082EB11EEBA}" + ProjectSection(ProjectDependencies) = postProject + {9CE2568A-3170-41C6-9F20-A0188A9EC114} = {9CE2568A-3170-41C6-9F20-A0188A9EC114} + EndProjectSection EndProject Global GlobalSection(SolutionConfiguration) = preSolution - ConfigName.0 = Debug - ConfigName.1 = Release - EndGlobalSection - GlobalSection(ProjectDependencies) = postSolution - {F4F15529-F0EB-402F-8662-73C5797EE557}.0 = {AB581101-18F0-46F6-B56A-83A6B1EA657E} - {BB8AC1B5-6587-4163-BDC6-788B157705CA}.0 = {AB581101-18F0-46F6-B56A-83A6B1EA657E} - {B1D2CDA2-CC8F-45D5-A694-2EE45B0308CF}.0 = {AB581101-18F0-46F6-B56A-83A6B1EA657E} - {B1D2CDA2-CC8F-45D5-A694-2EE45B0308CF}.0 = {AB581101-18F0-46F6-B56A-83A6B1EA657E} - {B1D2CDA2-CC8F-45D5-A694-2EE45B0308CF}.0 = {AB581101-18F0-46F6-B56A-83A6B1EA657E} - {BB8AC1B5-6587-4163-BDC6-788B157705CA}.0 = {AB581101-18F0-46F6-B56A-83A6B1EA657E} - {BB8AC1B5-6587-4163-BDC6-788B157705CA}.0 = {AB581101-18F0-46F6-B56A-83A6B1EA657E} + Debug = Debug + Release = Release EndGlobalSection GlobalSection(ProjectConfiguration) = postSolution {AB581101-18F0-46F6-B56A-83A6B1EA657E}.Debug.ActiveCfg = Debug|Win32 @@ -92,6 +127,14 @@ Global {AA230639-E115-4A44-AA5A-44A61235BA50}.Debug.Build.0 = Debug|Win32 {AA230639-E115-4A44-AA5A-44A61235BA50}.Release.ActiveCfg = Release|Win32 {AA230639-E115-4A44-AA5A-44A61235BA50}.Release.Build.0 = Release|Win32 + {9CE2568A-3170-41C6-9F20-A0188A9EC114}.Debug.ActiveCfg = Debug|Win32 + {9CE2568A-3170-41C6-9F20-A0188A9EC114}.Debug.Build.0 = Debug|Win32 + {9CE2568A-3170-41C6-9F20-A0188A9EC114}.Release.ActiveCfg = Release|Win32 + {9CE2568A-3170-41C6-9F20-A0188A9EC114}.Release.Build.0 = Release|Win32 + {A987A0C1-344F-475C-869C-F082EB11EEBA}.Debug.ActiveCfg = Debug|Win32 + {A987A0C1-344F-475C-869C-F082EB11EEBA}.Debug.Build.0 = Debug|Win32 + {A987A0C1-344F-475C-869C-F082EB11EEBA}.Release.ActiveCfg = Release|Win32 + {A987A0C1-344F-475C-869C-F082EB11EEBA}.Release.Build.0 = Release|Win32 EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution EndGlobalSection diff --git a/mDNSShared/Java/DNSSD.java b/mDNSShared/Java/DNSSD.java index d8a7ce1..50d29e4 100644 --- a/mDNSShared/Java/DNSSD.java +++ b/mDNSShared/Java/DNSSD.java @@ -23,6 +23,9 @@ Change History (most recent first): $Log: DNSSD.java,v $ +Revision 1.9 2005/10/26 01:52:24 cheshire + Race condition in Java code (doesn't work at all on Linux) + Revision 1.8 2005/07/11 01:55:21 cheshire Race condition in Java API @@ -652,8 +655,8 @@ class AppleService implements DNSSDService, Runnable public void stop() { this.HaltOperation(); } - /* Block for timeout ms (or forever if -1). Returns 1 if data present, 0 if timed out, -1 if not browsing. */ - protected native int BlockForData( int msTimeout); + /* Block until data arrives, or one second passes. Returns 1 if data present, 0 otherwise. */ + protected native int BlockForData(); /* Call ProcessResults when data appears on socket descriptor. */ protected native int ProcessResults(); @@ -672,7 +675,9 @@ class AppleService implements DNSSDService, Runnable { while ( true ) { - // We have to be very careful here. Suppose our DNS-SD operation is stopped from some other thread, + // Note: We want to allow our DNS-SD operation to be stopped from other threads, so we have to + // block waiting for data *outside* the synchronized section. Because we're doing this unsynchronized + // we have to write some careful code. Suppose our DNS-SD operation is stopped from some other thread, // and then immediately afterwards that thread (or some third, unrelated thread) starts a new DNS-SD // operation. The Unix kernel always allocates the lowest available file descriptor to a new socket, // so the same file descriptor is highly likely to be reused for the new operation, and if our old @@ -691,11 +696,11 @@ class AppleService implements DNSSDService, Runnable // locking DOESN'T prevent the callback routine from stopping its own operation, but DOES prevent // any other thread from stopping it until after the callback has completed and returned to us here. - int result = BlockForData(-1); - if (result != 1) break; // If socket has been closed, time to terminate this thread + int result = BlockForData(); synchronized (this) { if (fNativeContext == 0) break; // Some other thread stopped our DNSSD operation; time to terminate this thread + if (result == 0) continue; // If BlockForData() said there was no data, go back and block again result = ProcessResults(); if (fNativeContext == 0) break; // Event listener stopped its own DNSSD operation; terminate this thread if (result != 0) { fListener.operationFailed(this, result); break; } // If error, notify listener diff --git a/mDNSShared/Java/DNSSDException.java b/mDNSShared/Java/DNSSDException.java index 595e405..0ab0587 100644 --- a/mDNSShared/Java/DNSSDException.java +++ b/mDNSShared/Java/DNSSDException.java @@ -23,6 +23,9 @@ Change History (most recent first): $Log: DNSSDException.java,v $ +Revision 1.3 2005/07/10 22:19:01 cheshire +Add missing error codes to list of public static final ints + Revision 1.2 2004/04/30 21:48:27 rpantos Change line endings for CVS. @@ -50,11 +53,23 @@ abstract public class DNSSDException extends Exception public static final int BAD_FLAGS = -65543; public static final int UNSUPPORTED = -65544; public static final int NOT_INITIALIZED = -65545; + public static final int NO_CACHE = -65546; public static final int ALREADY_REGISTERED = -65547; public static final int NAME_CONFLICT = -65548; public static final int INVALID = -65549; + public static final int FIREWALL = -65550; public static final int INCOMPATIBLE = -65551; public static final int BAD_INTERFACE_INDEX = -65552; + public static final int REFUSED = -65553; + public static final int NOSUCHRECORD = -65554; + public static final int NOAUTH = -65555; + public static final int NOSUCHKEY = -65556; + public static final int NATTRAVERSAL = -65557; + public static final int DOUBLENAT = -65558; + public static final int BADTIME = -65559; + public static final int BADSIG = -65560; + public static final int BADKEY = -65561; + public static final int TRANSIENT = -65562; /** Returns the sub-code that identifies the particular error. */ abstract public int getErrorCode(); diff --git a/mDNSShared/Java/JNISupport.c b/mDNSShared/Java/JNISupport.c index 902e85d..d7c6243 100644 --- a/mDNSShared/Java/JNISupport.c +++ b/mDNSShared/Java/JNISupport.c @@ -23,6 +23,9 @@ Change History (most recent first): $Log: JNISupport.c,v $ +Revision 1.13 2005/10/26 01:52:24 cheshire + Race condition in Java code (doesn't work at all on Linux) + Revision 1.12 2005/07/13 19:20:32 cheshire Race condition in Java API Additional cleanup suggested by Roger -- NewContext() doesn't need ownerClass parameter any more @@ -262,15 +265,13 @@ JNIEXPORT void JNICALL Java_com_apple_dnssd_AppleService_HaltOperation( JNIEnv * } -JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleService_BlockForData( JNIEnv *pEnv, jobject pThis, jint msTimeout) -/* Block for timeout ms (or forever if -1). Returns 1 if data present, 0 if timed out, -1 if not browsing. */ +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. */ { -#if AUTO_CALLBACKS - return -1; // BlockForData() not supported with AUTO_CALLBACKS -#else // AUTO_CALLBACKS +// BlockForData() not supported with AUTO_CALLBACKS +#if !AUTO_CALLBACKS jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "I"); - jint rc = -1; if ( contextField != 0) { @@ -279,18 +280,32 @@ JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleService_BlockForData( JNIEnv *p { fd_set readFDs; int sd = DNSServiceRefSockFD( pContext->ServiceRef); - struct timeval timeout = { msTimeout / 1000, 10 * (msTimeout % 1000) }; - struct timeval *pTimeout = msTimeout == -1 ? NULL : &timeout; - + struct timeval timeout = { 1, 0 }; FD_ZERO( &readFDs); FD_SET( sd, &readFDs); - rc = select( sd + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, pTimeout); + // 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); } } - - return rc; -#endif // AUTO_CALLBACKS +#endif // !AUTO_CALLBACKS + return(0); } diff --git a/mDNSShared/PlatformCommon.c b/mDNSShared/PlatformCommon.c index 27b1417..6ec40da 100644 --- a/mDNSShared/PlatformCommon.c +++ b/mDNSShared/PlatformCommon.c @@ -3,14 +3,14 @@ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -18,12 +18,16 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_LICENSE_HEADER_END@ Change History (most recent first): $Log: PlatformCommon.c,v $ +Revision 1.6 2005/04/08 21:30:16 ksekar + Compiling problems with mDNSResponder-98 on Solaris/Sparc v9 +Patch submitted by Bernd Kuhls + Revision 1.5 2005/02/01 19:33:30 ksekar Keychain format too restrictive @@ -44,7 +48,7 @@ Move ReadDDNSSettingsFromConfFile() from mDNSMacOSX.c to PlatformCommon.c #include // Needed for fopen() etc. #include // Needed for close() #include // Needed for strlen() etc. -#include // Needed for errno etc. +#include // Needed for errno etc. #include // Needed for socket() etc. #include // Needed for sockaddr_in diff --git a/mDNSShared/PlatformCommon.h b/mDNSShared/PlatformCommon.h index 624c370..46a672a 100644 --- a/mDNSShared/PlatformCommon.h +++ b/mDNSShared/PlatformCommon.h @@ -3,14 +3,14 @@ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -18,7 +18,7 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_LICENSE_HEADER_END@ Change History (most recent first): diff --git a/mDNSShared/dns-sd.1 b/mDNSShared/dns-sd.1 index 3611200..6089ae6 100644 --- a/mDNSShared/dns-sd.1 +++ b/mDNSShared/dns-sd.1 @@ -20,6 +20,9 @@ .\" @APPLE_LICENSE_HEADER_END@ .\" .\" $Log: dns-sd.1,v $ +.\" Revision 1.5 2005/07/04 23:12:35 cheshire +.\" The dns-sd command first appeared in Mac OS X 10.4 (Tiger) +.\" .\" Revision 1.4 2005/02/16 02:29:32 cheshire .\" Update terminology .\" @@ -211,4 +214,4 @@ bugs are tracked in Apple Radar component "mDNSResponder". .Sh HISTORY The .Nm -command first appeared in Mac OS X 10.3 (Panther). +command first appeared in Mac OS X 10.4 (Tiger). diff --git a/mDNSShared/dns_sd.h b/mDNSShared/dns_sd.h index 81e0856..3fd58ef 100755 --- a/mDNSShared/dns_sd.h +++ b/mDNSShared/dns_sd.h @@ -33,27 +33,46 @@ #endif /* standard calling convention under Win32 is __stdcall */ -#if defined(_WIN32) +/* Note: When compiling Intel EFI (Extensible Firmware Interface) under MS Visual Studio, the */ +/* _WIN32 symbol is defined by the compiler even though it's NOT compiling code for Windows32 */ +#if defined(_WIN32) && !defined(EFI32) && !defined(EFI64) #define DNSSD_API __stdcall #else #define DNSSD_API #endif -#if defined(__FreeBSD_version) && (__FreeBSD_version < 500000) /* stdint.h does not exist on FreeBSD 4.x; its types are defined in sys/types.h instead */ +#if defined(__FreeBSD_version) && (__FreeBSD_version < 500000) #include + +/* Likewise, on Sun, standard integer types are in sys/types.h */ #elif defined(__sun__) #include + +/* EFI does not have stdint.h, or anything else equivalent */ +#elif defined(EFI32) || defined(EFI64) +typedef UINT8 uint8_t; +typedef INT8 int8_t; +typedef UINT16 uint16_t; +typedef INT16 int16_t; +typedef UINT32 uint32_t; +typedef INT32 int32_t; + +/* Windows has its own differences */ #elif defined(_WIN32) #include #define _UNUSED #define bzero(a, b) memset(a, 0, b) +#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; +#endif + +/* All other Posix platforms use stdint.h */ #else #include #endif @@ -1298,7 +1317,7 @@ int DNSSD_API DNSServiceConstructFullName * Note: Represents a DNS-SD TXT record. */ -typedef struct _TXTRecordRef_t { char privatedata[16]; } TXTRecordRef; +typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignment; } TXTRecordRef; /* TXTRecordCreate() @@ -1661,6 +1680,17 @@ DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser #endif //__APPLE_API_PRIVATE +// Some C compiler cleverness. We can make the compiler check certain things for us, +// and report errors at compile-time if anything is wrong. The usual way to do this would +// be to use a run-time "if" statement or the conventional run-time "assert" mechanism, but +// then you don't find out what's wrong until you run the software. This way, if the assertion +// condition is false, the array size is negative, and the complier complains immediately. + +struct DNS_SD_CompileTimeAssertionChecks + { + char assert0[(sizeof(union _TXTRecordRef_t) == 16) ? 1 : -1]; + }; + #ifdef __cplusplus } #endif diff --git a/mDNSShared/dnssd_clientlib.c b/mDNSShared/dnssd_clientlib.c index 40ab0d0..6284b1e 100755 --- a/mDNSShared/dnssd_clientlib.c +++ b/mDNSShared/dnssd_clientlib.c @@ -27,6 +27,9 @@ Change History (most recent first): $Log: dnssd_clientlib.c,v $ +Revision 1.10 2005/04/06 02:06:56 shersche +Add DNSSD_API macro to TXTRecord API calls + Revision 1.9 2004/10/06 02:22:19 cheshire Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)" @@ -196,7 +199,7 @@ struct dnssd_clientlib_CompileTimeAssertionCheck char assert0[(sizeof(TXTRecordRefRealType) <= 16) ? 1 : -1]; }; -void TXTRecordCreate +void DNSSD_API TXTRecordCreate ( TXTRecordRef *txtRecord, uint16_t bufferLen, @@ -209,12 +212,12 @@ void TXTRecordCreate txtRec->malloced = 0; } -void TXTRecordDeallocate(TXTRecordRef *txtRecord) +void DNSSD_API TXTRecordDeallocate(TXTRecordRef *txtRecord) { if (txtRec->malloced) free(txtRec->buffer); } -DNSServiceErrorType TXTRecordSetValue +DNSServiceErrorType DNSSD_API TXTRecordSetValue ( TXTRecordRef *txtRecord, const char *key, @@ -259,7 +262,7 @@ DNSServiceErrorType TXTRecordSetValue return(kDNSServiceErr_NoError); } -DNSServiceErrorType TXTRecordRemoveValue +DNSServiceErrorType DNSSD_API TXTRecordRemoveValue ( TXTRecordRef *txtRecord, const char *key @@ -276,8 +279,8 @@ DNSServiceErrorType TXTRecordRemoveValue return(kDNSServiceErr_NoError); } -uint16_t TXTRecordGetLength (const TXTRecordRef *txtRecord) { return(txtRec->datalen); } -const void * TXTRecordGetBytesPtr(const TXTRecordRef *txtRecord) { return(txtRec->buffer); } +uint16_t DNSSD_API TXTRecordGetLength (const TXTRecordRef *txtRecord) { return(txtRec->datalen); } +const void * DNSSD_API TXTRecordGetBytesPtr(const TXTRecordRef *txtRecord) { return(txtRec->buffer); } /********************************************************************************************* * @@ -285,7 +288,7 @@ const void * TXTRecordGetBytesPtr(const TXTRecordRef *txtRecord) { return(txtR * *********************************************************************************************/ -int TXTRecordContainsKey +int DNSSD_API TXTRecordContainsKey ( uint16_t txtLen, const void *txtRecord, @@ -296,7 +299,7 @@ int TXTRecordContainsKey return (InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen) ? 1 : 0); } -const void * TXTRecordGetValuePtr +const void * DNSSD_API TXTRecordGetValuePtr ( uint16_t txtLen, const void *txtRecord, @@ -311,7 +314,7 @@ const void * TXTRecordGetValuePtr return (item + 1 + keylen + 1); } -uint16_t TXTRecordGetCount +uint16_t DNSSD_API TXTRecordGetCount ( uint16_t txtLen, const void *txtRecord @@ -324,7 +327,7 @@ uint16_t TXTRecordGetCount return((p>e) ? (uint16_t)0 : count); } -DNSServiceErrorType TXTRecordGetItemAtIndex +DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex ( uint16_t txtLen, const void *txtRecord, diff --git a/mDNSShared/dnssd_clientstub.c b/mDNSShared/dnssd_clientstub.c index 8cf98d3..b558259 100755 --- a/mDNSShared/dnssd_clientstub.c +++ b/mDNSShared/dnssd_clientstub.c @@ -28,6 +28,16 @@ Change History (most recent first): $Log: dnssd_clientstub.c,v $ +Revision 1.48 2005/06/30 18:01:00 shersche + Clients shouldn't wait ten seconds to connect to mDNSResponder + +Revision 1.47 2005/03/31 02:19:56 cheshire + Fix build warnings +Reviewed by: Scott Herscher + +Revision 1.46 2005/03/21 00:39:31 shersche + Fix build warnings on Win32 platform + Revision 1.45 2005/02/01 01:25:06 shersche Define sleep() to be Sleep() for Windows compatibility @@ -171,6 +181,8 @@ Update to APSL 2.0 #include #define sockaddr_mdns sockaddr_in #define AF_MDNS AF_INET +extern BOOL +IsSystemServiceDisabled(); #else #include #include @@ -195,6 +207,11 @@ static int g_initWinsock = 0; #endif +// Specifies how many times we'll try and connect to the +// server. + +#define DNSSD_CLIENT_MAXTRIES 4 + #define CTL_PATH_PREFIX "/tmp/dnssd_clippath." // error socket (if needed) is named "dnssd_clipath.[pid].xxx:n" where xxx are the // last 3 digits of the time (in seconds) and n is the 6-digit microsecond time @@ -266,7 +283,9 @@ static ipc_msg_hdr *create_hdr(uint32_t op, size_t *len, char **data_start, int char *msg = NULL; ipc_msg_hdr *hdr; int datalen; +#if !defined(USE_TCP_LOOPBACK) char ctrl_path[256]; +#endif if (!reuse_socket) { @@ -325,6 +344,15 @@ static DNSServiceRef connect_to_server(void) if (err != 0) return NULL; } + + // If the system service is disabled, we only want to try + // to connect once + + if ( IsSystemServiceDisabled() ) + { + NumTries = DNSSD_CLIENT_MAXTRIES; + } + #endif sdr = malloc(sizeof(_DNSServiceRef_t)); @@ -346,9 +374,9 @@ static DNSServiceRef connect_to_server(void) // If we failed, then it may be because the daemon is still launching. // This can happen for processes that launch early in the boot process, while the // daemon is still coming up. Rather than fail here, we'll wait a bit and try again. - // If, after ten seconds, we still can't connect to the daemon, + // If, after four seconds, we still can't connect to the daemon, // then we give up and return a failure code. - if (++NumTries < 10) + if (++NumTries < DNSSD_CLIENT_MAXTRIES) sleep(1); // Sleep a bit, then try again else { @@ -369,7 +397,7 @@ static DNSServiceErrorType deliver_request(void *msg, DNSServiceRef sdr, int reu char *data = (char *)msg + sizeof(ipc_msg_hdr); dnssd_sock_t listenfd = dnssd_InvalidSocket, errsd = dnssd_InvalidSocket; int ret; - unsigned int len = sizeof(caddr); + dnssd_socklen_t len = (dnssd_socklen_t) sizeof(caddr); DNSServiceErrorType err = kDNSServiceErr_Unknown; if (!hdr || sdr->sockfd < 0) return kDNSServiceErr_Unknown; diff --git a/mDNSShared/dnssd_ipc.h b/mDNSShared/dnssd_ipc.h index a04eacc..32d7872 100644 --- a/mDNSShared/dnssd_ipc.h +++ b/mDNSShared/dnssd_ipc.h @@ -27,6 +27,9 @@ Change History (most recent first): $Log: dnssd_ipc.h,v $ +Revision 1.21 2005/09/29 06:38:13 herscher +Remove #define MSG_WAITALL on Windows. We don't use this macro anymore, and it's presence causes warnings to be emitted when compiling against the latest Microsoft Platform SDK. + Revision 1.20 2005/03/21 00:39:31 shersche Fix build warnings on Win32 platform @@ -90,7 +93,6 @@ Update to APSL 2.0 # define dnssd_InvalidSocket INVALID_SOCKET # define dnssd_EWOULDBLOCK WSAEWOULDBLOCK # define dnssd_EINTR WSAEINTR -# define MSG_WAITALL 0 # define dnssd_sock_t SOCKET # define dnssd_socklen_t int # define dnssd_sockbuf_t const char* diff --git a/mDNSShared/uds_daemon.c b/mDNSShared/uds_daemon.c index 406b7ab..9ba0ccc 100644 --- a/mDNSShared/uds_daemon.c +++ b/mDNSShared/uds_daemon.c @@ -24,6 +24,25 @@ Change History (most recent first): $Log: uds_daemon.c,v $ +Revision 1.189 2006/01/06 00:56:31 cheshire + Should remove PID file on exit + +Revision 1.188 2005/10/11 22:15:03 cheshire + Add memory corruption safeguards to uds_daemon.c +Only compile uds_validatelists() when building for Mac OS X + +Revision 1.187 2005/10/11 20:30:27 cheshire + Add memory corruption safeguards to uds_daemon.c + +Revision 1.186 2005/09/12 07:11:53 herscher + Lazily call RegisterSearchDomains to workaround crashes of several routers. This code is conditionally compiled, and currently is only enabled on Windows platforms. + +Revision 1.185 2005/07/29 00:55:10 ksekar +Removed validation check in uds_validatelists which generated false alarms + +Revision 1.184 2005/07/04 22:40:26 cheshire +Additional debugging code to help catch memory corruption + Revision 1.183 2005/06/13 22:39:11 cheshire Missing return statement in handle_enum_request() error handling @@ -590,9 +609,10 @@ Update to APSL 2.0 #if defined(_WIN32) #include +#define MDNS_LAZY_REGISTER_SEARCH_DOMAINS #define dnssd_strerror(X) win32_strerror(X) #define usleep(X) Sleep(((X)+999)/1000) -static char * win32_strerror(int inErrorCode); +static char * win32_strerror(int inErrorCode); #else #include #include @@ -619,6 +639,10 @@ static char * win32_strerror(int inErrorCode); #endif // LOCAL_PEERCRED #endif //__MACOSX__ +#if defined(MDNS_LAZY_REGISTER_SEARCH_DOMAINS) +extern mStatus dDNS_RegisterSearchDomains( mDNS * const m ); +#endif + // Types and Data Structures // ---------------------------------------------------------------------- @@ -819,53 +843,53 @@ static request_state * all_requests = NULL; #define MSG_PAD_BYTES 5 // pad message buffer (read from client) with n zero'd bytes to guarantee // n get_string() calls w/o buffer overrun // private function prototypes -static void connect_callback(void *info); -static int read_msg(request_state *rs); -static int send_msg(reply_state *rs); -static void abort_request(request_state *rs); -static void request_callback(void *info); -static void handle_resolve_request(request_state *rstate); -static void question_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord); -static void question_termination_callback(void *context); -static void handle_browse_request(request_state *request); -static void browse_termination_callback(void *context); -static void browse_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord); -static void handle_regservice_request(request_state *request); -static void regservice_termination_callback(void *context); -static void process_service_registration(ServiceRecordSet *const srs, mDNSBool SuppressError); -static void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result); -static mStatus handle_add_request(request_state *rstate); -static mStatus handle_update_request(request_state *rstate); -static mStatus gen_rr_response(domainname *servicename, mDNSInterfaceID id, request_state *request, reply_state **rep); -static void append_reply(request_state *req, reply_state *rep); -static int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain); -static void enum_termination_callback(void *context); -static void enum_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord); -static void handle_query_request(request_state *rstate); -static reply_state *format_enumeration_reply(request_state *rstate, const char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err); -static void handle_enum_request(request_state *rstate); -static mStatus handle_regrecord_request(request_state *rstate); -static void regrecord_callback(mDNS *const m, AuthRecord * rr, mStatus result); -static void connected_registration_termination(void *context); -static void handle_reconfirm_request(request_state *rstate); -static AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int ttl, int validate_flags); -static mStatus handle_removerecord_request(request_state *rstate); -static void reset_connected_rstate(request_state *rstate); -static int deliver_error(request_state *rstate, mStatus err); -static int deliver_async_error(request_state *rs, reply_op_t op, mStatus err); -static transfer_state send_undelivered_error(request_state *rs); -static reply_state *create_reply(reply_op_t op, size_t datalen, request_state *request); -static void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd); -static void my_perror(char *errmsg); -static void unlink_request(request_state *rs); -static void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord); -static void resolve_termination_callback(void *context); -static int validate_message(request_state *rstate); -static mStatus remove_extra(request_state *rstate, service_instance *serv); -static mStatus remove_record(request_state *rstate); -static void free_service_instance(service_instance *srv); -static uint32_t dnssd_htonl(uint32_t l); -static void handle_setdomain_request(request_state *rstate); +mDNSlocal void connect_callback(void *info); +mDNSlocal int read_msg(request_state *rs); +mDNSlocal int send_msg(reply_state *rs); +mDNSlocal void abort_request(request_state *rs); +mDNSlocal void request_callback(void *info); +mDNSlocal void handle_resolve_request(request_state *rstate); +mDNSlocal void question_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord); +mDNSlocal void question_termination_callback(void *context); +mDNSlocal void handle_browse_request(request_state *request); +mDNSlocal void browse_termination_callback(void *context); +mDNSlocal void browse_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord); +mDNSlocal void handle_regservice_request(request_state *request); +mDNSlocal void regservice_termination_callback(void *context); +mDNSlocal void process_service_registration(ServiceRecordSet *const srs, mDNSBool SuppressError); +mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result); +mDNSlocal mStatus handle_add_request(request_state *rstate); +mDNSlocal mStatus handle_update_request(request_state *rstate); +mDNSlocal mStatus gen_rr_response(domainname *servicename, mDNSInterfaceID id, request_state *request, reply_state **rep); +mDNSlocal void append_reply(request_state *req, reply_state *rep); +mDNSlocal int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain); +mDNSlocal void enum_termination_callback(void *context); +mDNSlocal void enum_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord); +mDNSlocal void handle_query_request(request_state *rstate); +mDNSlocal reply_state *format_enumeration_reply(request_state *rstate, const char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err); +mDNSlocal void handle_enum_request(request_state *rstate); +mDNSlocal mStatus handle_regrecord_request(request_state *rstate); +mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord * rr, mStatus result); +mDNSlocal void connected_registration_termination(void *context); +mDNSlocal void handle_reconfirm_request(request_state *rstate); +mDNSlocal AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int ttl, int validate_flags); +mDNSlocal mStatus handle_removerecord_request(request_state *rstate); +mDNSlocal void reset_connected_rstate(request_state *rstate); +mDNSlocal int deliver_error(request_state *rstate, mStatus err); +mDNSlocal int deliver_async_error(request_state *rs, reply_op_t op, mStatus err); +mDNSlocal transfer_state send_undelivered_error(request_state *rs); +mDNSlocal reply_state *create_reply(reply_op_t op, size_t datalen, request_state *request); +mDNSlocal void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd); +mDNSlocal void my_perror(char *errmsg); +mDNSlocal void unlink_request(request_state *rs); +mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord); +mDNSlocal void resolve_termination_callback(void *context); +mDNSlocal int validate_message(request_state *rstate); +mDNSlocal mStatus remove_extra(request_state *rstate, service_instance *serv); +mDNSlocal mStatus remove_record(request_state *rstate); +mDNSlocal void free_service_instance(service_instance *srv); +mDNSlocal uint32_t dnssd_htonl(uint32_t l); +mDNSlocal void handle_setdomain_request(request_state *rstate); // initialization, setup/teardown functions @@ -900,7 +924,7 @@ mDNSlocal void LogClientInfo(request_state *req) } } -static void FatalError(char *errmsg) +mDNSlocal void FatalError(char *errmsg) { LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno())); *(long*)0 = 0; // On OS X abort() doesn't generate a crash log, but writing to zero does @@ -1037,6 +1061,8 @@ int udsserver_exit(void) debugf("Unable to remove %s", MDNS_UDS_SERVERPATH); #endif + if (PID_FILE[0]) unlink(PID_FILE); + return 0; } @@ -1088,7 +1114,7 @@ mDNSs32 udsserver_idle(mDNSs32 nextevent) else if (nextevent - now > mDNSPlatformOneSecond) nextevent = now + mDNSPlatformOneSecond; // try again in a second } if (result == t_terminated || result == t_error) - //since we're already doing a list traversal, we unlink the request manunally instead of calling unlink_request() + //since we're already doing a list traversal, we unlink the request manually instead of calling unlink_request() { tmp = req; if (prev) prev->next = req->next; @@ -1105,7 +1131,7 @@ mDNSs32 udsserver_idle(mDNSs32 nextevent) return nextevent; } -void udsserver_info(mDNS *const m) +mDNSexport void udsserver_info(mDNS *const m) { mDNSs32 now = mDNS_TimeNow(m); mDNSu32 CacheUsed = 0, CacheActive = 0; @@ -1146,7 +1172,17 @@ void udsserver_info(mDNS *const m) LogMsgNoIdent("Timenow 0x%08lX (%ld)", (mDNSu32)now, now); } -static void rename_service(service_instance *srv) +#if __MACOSX__ && MACOSX_MDNS_MALLOC_DEBUGGING +mDNSexport void uds_validatelists(void) + { + request_state *req; + for (req = all_requests; req; req=req->next) + if (req->sd < 0 && req->sd != -2) + LogMemCorruption("UDS request list: %p is garbage (%X)", req, req->sd); + } +#endif + +mDNSlocal void rename_service(service_instance *srv) { if (srv->autoname && !SameDomainLabel(srv->name.c, gmDNS->nicelabel.c)) { @@ -1156,11 +1192,10 @@ static void rename_service(service_instance *srv) } } -void udsserver_handle_configchange(void) +mDNSexport void udsserver_handle_configchange(void) { request_state *req; - for (req = all_requests; req; req = req->next) { if (req->service_registration) @@ -1172,7 +1207,7 @@ void udsserver_handle_configchange(void) } } -static void connect_callback(void *info) +mDNSlocal void connect_callback(void *info) { dnssd_sock_t sd; dnssd_socklen_t len; @@ -1229,7 +1264,7 @@ static void connect_callback(void *info) } // handler -static void request_callback(void *info) +mDNSlocal void request_callback(void *info) { request_state *rstate = info; transfer_state result; @@ -1381,7 +1416,7 @@ static void request_callback(void *info) // massage the name parameters appropriately, but the rest of the operations (making the query call, // delivering the result to the client, and termination) are identical. -static void handle_query_request(request_state *rstate) +mDNSlocal void handle_query_request(request_state *rstate) { DNSServiceFlags flags; uint32_t ifi; @@ -1447,7 +1482,7 @@ error: return; } -static void handle_resolve_request(request_state *rstate) +mDNSlocal void handle_resolve_request(request_state *rstate) { DNSServiceFlags flags; uint32_t interfaceIndex; @@ -1547,7 +1582,7 @@ bad_param: unlink_request(rstate); } -static void resolve_termination_callback(void *context) +mDNSlocal void resolve_termination_callback(void *context) { resolve_termination_t *term = context; request_state *rs; @@ -1567,7 +1602,7 @@ static void resolve_termination_callback(void *context) rs->termination_context = NULL; } -static void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) +mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) { size_t len = 0; char fullname[MAX_ESCAPED_DOMAIN_NAME], target[MAX_ESCAPED_DOMAIN_NAME]; @@ -1650,7 +1685,7 @@ static void resolve_result_callback(mDNS *const m, DNSQuestion *question, const } // what gets called when a resolve is completed and we need to send the data back to the client -static void question_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) +mDNSlocal void question_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) { char *data; char name[MAX_ESCAPED_DOMAIN_NAME]; @@ -1690,7 +1725,7 @@ static void question_result_callback(mDNS *const m, DNSQuestion *question, const return; } -static void question_termination_callback(void *context) +mDNSlocal void question_termination_callback(void *context) { DNSQuestion *q = context; LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) STOP", ((request_state *)q->QuestionContext)->sd, q->qname.c, DNSTypeName(q->qtype)); @@ -1701,7 +1736,7 @@ static void question_termination_callback(void *context) // If there's a comma followed by another character, // FindFirstSubType overwrites the comma with a nul and returns the pointer to the next character. // Otherwise, it returns a pointer to the final nul at the end of the string -static char *FindFirstSubType(char *p) +mDNSlocal char *FindFirstSubType(char *p) { while (*p) { @@ -1716,7 +1751,7 @@ static char *FindFirstSubType(char *p) // FindNextSubType overwrites the comma with a nul and returns the pointer to the next character. // If it finds an illegal unescaped dot in the subtype name, it returns mDNSNULL // Otherwise, it returns a pointer to the final nul at the end of the string -static char *FindNextSubType(char *p) +mDNSlocal char *FindNextSubType(char *p) { while (*p) { @@ -1770,14 +1805,14 @@ mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p) } #ifdef _HAVE_SETDOMAIN_SUPPORT_ -static void free_defdomain(mDNS *const m, AuthRecord *const rr, mStatus result) +mDNSlocal void free_defdomain(mDNS *const m, AuthRecord *const rr, mStatus result) { (void)m; // unused if (result == mStatus_MemFree) free(rr->RecordContext); // context is the enclosing list structure } #endif -static void handle_setdomain_request(request_state *request) +mDNSlocal void handle_setdomain_request(request_state *request) { mStatus err = mStatus_NoError; char *ptr; @@ -1866,7 +1901,7 @@ static void handle_setdomain_request(request_state *request) unlink_request(request); } -static mStatus add_domain_to_browser(browser_info_t *info, const domainname *d) +mDNSlocal mStatus add_domain_to_browser(browser_info_t *info, const domainname *d) { browser_t *b, *p; mStatus err; @@ -1894,7 +1929,7 @@ static mStatus add_domain_to_browser(browser_info_t *info, const domainname *d) return err; } -static void handle_browse_request(request_state *request) +mDNSlocal void handle_browse_request(request_state *request) { DNSServiceFlags flags; uint32_t interfaceIndex; @@ -1928,6 +1963,13 @@ static void handle_browse_request(request_state *request) InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex); if (interfaceIndex && !InterfaceID) { err = mStatus_BadParamErr; goto error; } +#if defined(MDNS_LAZY_REGISTER_SEARCH_DOMAINS) + if ( !domain || ( domain[0] == '\0' ) ) + { + dDNS_RegisterSearchDomains( gmDNS ); + } +#endif + typedn.c[0] = 0; NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes if (NumSubTypes < 0 || NumSubTypes > 1) { err = mStatus_BadParamErr; goto error; } @@ -1989,7 +2031,7 @@ error: unlink_request(request); } -static void browse_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) +mDNSlocal void browse_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) { request_state *req = question->QuestionContext; reply_state *rep; @@ -2013,7 +2055,7 @@ static void browse_result_callback(mDNS *const m, DNSQuestion *question, const R return; } -static void browse_termination_callback(void *context) +mDNSlocal void browse_termination_callback(void *context) { browser_info_t *info = context; browser_t *ptr; @@ -2108,7 +2150,7 @@ mDNSexport int CountExistingRegistrations(domainname *srv, mDNSIPPort port) return(count); } -static mStatus register_service_instance(request_state *request, const domainname *domain) +mDNSlocal mStatus register_service_instance(request_state *request, const domainname *domain) { service_info *info = request->service_registration; service_instance *ptr, *instance; @@ -2193,7 +2235,7 @@ mDNSexport void udsserver_default_reg_domain_changed(const domainname *d, mDNSBo } // service registration -static void handle_regservice_request(request_state *request) +mDNSlocal void handle_regservice_request(request_state *request) { DNSServiceFlags flags; uint32_t ifi; @@ -2246,7 +2288,7 @@ static void handle_regservice_request(request_state *request) if (!service->txtdata) { my_perror("ERROR: malloc"); result = mStatus_NoMemoryErr; goto finish; } memcpy(service->txtdata, get_rdata(&ptr, service->txtlen), service->txtlen); } - else service->txtdata = NULL; + else service->txtdata = NULL; // Check for sub-types after the service type service->num_subtypes = ChopSubTypes(service->type_as_string); // Note: Modifies regtype string to remove trailing subtypes @@ -2342,7 +2384,7 @@ bad_param: // handles name conflicts, and delivers completed registration information to the client (via // process_service_registraion()) -static void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result) +mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result) { mStatus err; mDNSBool SuppressError = mDNSfalse; @@ -2356,7 +2398,7 @@ static void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mSta service_info *info = instance->request->service_registration; if (info->default_domain && !instance->default_local) SuppressError = mDNStrue; // don't send errors up to client for wide-area, empty-string registrations - } + } if (result == mStatus_NoError) LogOperation("%3d: DNSServiceRegister(%##s, %u) REGISTERED ", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port)); @@ -2453,7 +2495,7 @@ mDNSexport void FreeExtraRR(mDNS *const m, AuthRecord *const rr, mStatus result) freeL("ExtraResourceRecord", extra); } -static mStatus add_record_to_service(request_state *rstate, service_instance *instance, uint16_t rrtype, uint16_t rdlen, char *rdata, uint32_t ttl) +mDNSlocal mStatus add_record_to_service(request_state *rstate, service_instance *instance, uint16_t rrtype, uint16_t rdlen, char *rdata, uint32_t ttl) { ServiceRecordSet *srs = &instance->srs; ExtraResourceRecord *extra; @@ -2483,7 +2525,7 @@ static mStatus add_record_to_service(request_state *rstate, service_instance *in return result; } -static mStatus handle_add_request(request_state *rstate) +mDNSlocal mStatus handle_add_request(request_state *rstate) { uint32_t ttl; uint16_t rrtype, rdlen; @@ -2517,7 +2559,7 @@ static mStatus handle_add_request(request_state *rstate) return(result); } -static mStatus update_record(AuthRecord *rr, uint16_t rdlen, char *rdata, uint32_t ttl) +mDNSlocal mStatus update_record(AuthRecord *rr, uint16_t rdlen, char *rdata, uint32_t ttl) { int rdsize; RData *newrd; @@ -2540,7 +2582,7 @@ static mStatus update_record(AuthRecord *rr, uint16_t rdlen, char *rdata, uint32 return result; } -static mStatus handle_update_request(request_state *rstate) +mDNSlocal mStatus handle_update_request(request_state *rstate) { uint16_t rdlen; char *ptr, *rdata; @@ -2599,13 +2641,13 @@ end: return(result); } -static void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd) +mDNSlocal void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd) { (void)m; // Unused if (oldrd != &rr->rdatastorage) freeL("update_callback", oldrd); } -static void process_service_registration(ServiceRecordSet *const srs, mDNSBool SuppressError) +mDNSlocal void process_service_registration(ServiceRecordSet *const srs, mDNSBool SuppressError) { reply_state *rep; transfer_state send_result; @@ -2635,7 +2677,7 @@ static void process_service_registration(ServiceRecordSet *const srs, mDNSBool S else append_reply(req, rep); } -static void free_service_instance(service_instance *srv) +mDNSlocal void free_service_instance(service_instance *srv) { request_state *rstate = srv->request; ExtraResourceRecord *e = srv->srs.Extras, *tmp; @@ -2669,7 +2711,7 @@ static void free_service_instance(service_instance *srv) freeL("regservice_callback", srv); } -static void regservice_termination_callback(void *context) +mDNSlocal void regservice_termination_callback(void *context) { service_info *info = context; service_instance *i, *p; @@ -2690,7 +2732,7 @@ static void regservice_termination_callback(void *context) freeL("service_info", info); } -static mStatus handle_regrecord_request(request_state *rstate) +mDNSlocal mStatus handle_regrecord_request(request_state *rstate) { AuthRecord *rr; registered_record_entry *re; @@ -2733,7 +2775,7 @@ static mStatus handle_regrecord_request(request_state *rstate) return(result); } -static void regrecord_callback(mDNS *const m, AuthRecord * rr, mStatus result) +mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord * rr, mStatus result) { registered_record_entry *re = rr->RecordContext; request_state *rstate = re ? re->rstate : NULL; @@ -2785,7 +2827,7 @@ static void regrecord_callback(mDNS *const m, AuthRecord * rr, mStatus result) else if (ts == t_morecoming) append_reply(rstate, reply); // client is blocked, link reply into list } -static void connected_registration_termination(void *context) +mDNSlocal void connected_registration_termination(void *context) { int shared; registered_record_entry *fptr, *ptr = ((request_state *)context)->reg_recs; @@ -2800,7 +2842,7 @@ static void connected_registration_termination(void *context) } } -static mStatus handle_removerecord_request(request_state *rstate) +mDNSlocal mStatus handle_removerecord_request(request_state *rstate) { mStatus err = mStatus_BadReferenceErr; char *ptr; @@ -2828,7 +2870,7 @@ static mStatus handle_removerecord_request(request_state *rstate) } // remove a resource record registered via DNSServiceRegisterRecord() -static mStatus remove_record(request_state *rstate) +mDNSlocal mStatus remove_record(request_state *rstate) { int shared; mStatus err = mStatus_UnknownErr; @@ -2852,7 +2894,7 @@ static mStatus remove_record(request_state *rstate) return err; } -static mStatus remove_extra(request_state *rstate, service_instance *serv) +mDNSlocal mStatus remove_extra(request_state *rstate, service_instance *serv) { mStatus err = mStatus_BadReferenceErr; ExtraResourceRecord *ptr; @@ -2866,7 +2908,7 @@ static mStatus remove_extra(request_state *rstate, service_instance *serv) } // domain enumeration -static void handle_enum_request(request_state *rstate) +mDNSlocal void handle_enum_request(request_state *rstate) { DNSServiceFlags flags; uint32_t ifi; @@ -2902,6 +2944,10 @@ static void handle_enum_request(request_state *rstate) term = mallocL("handle_enum_request", sizeof(enum_termination_t)); if (!def || !all || !term) FatalError("ERROR: malloc"); +#if defined(MDNS_LAZY_REGISTER_SEARCH_DOMAINS) + dDNS_RegisterSearchDomains( gmDNS ); +#endif + // enumeration requires multiple questions, so we must link all the context pointers so that // necessary context can be reached from the callbacks def->rstate = rstate; @@ -2938,7 +2984,7 @@ static void handle_enum_request(request_state *rstate) } } -static void enum_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) +mDNSlocal void enum_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) { char domain[MAX_ESCAPED_DOMAIN_NAME]; domain_enum_t *de = question->QuestionContext; @@ -2954,7 +3000,7 @@ static void enum_result_callback(mDNS *const m, DNSQuestion *question, const Res flags |= kDNSServiceFlagsAdd; if (de->type == mDNS_DomainTypeRegistrationDefault || de->type == mDNS_DomainTypeBrowseDefault) flags |= kDNSServiceFlagsDefault; - } + } ConvertDomainNameToCString(&answer->rdata->u.name, domain); // note that we do NOT propagate specific interface indexes to the client - for example, a domain we learn from // a machine's system preferences may be discovered on the LocalOnly interface, but should be browsed on the @@ -2970,18 +3016,17 @@ static void enum_result_callback(mDNS *const m, DNSQuestion *question, const Res return; } -static reply_state *format_enumeration_reply(request_state *rstate, const char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err) +mDNSlocal reply_state *format_enumeration_reply(request_state *rstate, const char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err) { size_t len; reply_state *reply; char *data; - - + len = sizeof(DNSServiceFlags); len += sizeof(uint32_t); len += sizeof(DNSServiceErrorType); len += strlen(domain) + 1; - + reply = create_reply(enumeration_reply, len, rstate); reply->rhdr->flags = dnssd_htonl(flags); reply->rhdr->ifi = dnssd_htonl(ifi); @@ -2991,7 +3036,7 @@ static reply_state *format_enumeration_reply(request_state *rstate, const char * return reply; } -static void enum_termination_callback(void *context) +mDNSlocal void enum_termination_callback(void *context) { enum_termination_t *t = context; mDNS *coredata = gmDNS; @@ -3004,7 +3049,7 @@ static void enum_termination_callback(void *context) freeL("enum_termination_callback", t); } -static void handle_reconfirm_request(request_state *rstate) +mDNSlocal void handle_reconfirm_request(request_state *rstate) { AuthRecord *rr; @@ -3018,7 +3063,7 @@ static void handle_reconfirm_request(request_state *rstate) } // setup rstate to accept new reg/dereg requests -static void reset_connected_rstate(request_state *rstate) +mDNSlocal void reset_connected_rstate(request_state *rstate) { rstate->ts = t_morecoming; rstate->hdr_bytes = 0; @@ -3031,7 +3076,7 @@ static void reset_connected_rstate(request_state *rstate) // returns a resource record (allocated w/ malloc) containing the data found in an IPC message // data must be in format flags, interfaceIndex, name, rrtype, rrclass, rdlen, rdata, (optional)ttl // (ttl only extracted/set if ttl argument is non-zero). returns NULL for a bad-parameter error -static AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int GetTTL, int validate_flags) +mDNSlocal AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int GetTTL, int validate_flags) { char *rdata, name[256]; AuthRecord *rr; @@ -3092,7 +3137,7 @@ static AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int GetTTL, int validate_f // identical callback signature. on successful completion rep is set to point to a malloc'd reply_state struct, // and mStatus_NoError is returned. otherwise the appropriate error is returned. -static mStatus gen_rr_response(domainname *servicename, mDNSInterfaceID id, request_state *request, reply_state **rep) +mDNSlocal mStatus gen_rr_response(domainname *servicename, mDNSInterfaceID id, request_state *request, reply_state **rep) { char *data; int len; @@ -3133,7 +3178,7 @@ static mStatus gen_rr_response(domainname *servicename, mDNSInterfaceID id, requ return mStatus_NoError; } -static int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain) +mDNSlocal int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain) { domainlabel n; domainname d, t; @@ -3146,7 +3191,7 @@ static int build_domainname_from_strings(domainname *srv, char *name, char *regt } // append a reply to the list in a request object -static void append_reply(request_state *req, reply_state *rep) +mDNSlocal void append_reply(request_state *req, reply_state *rep) { reply_state *ptr; @@ -3163,7 +3208,7 @@ static void append_reply(request_state *req, reply_state *rep) // read_msg may be called any time when the transfer state (rs->ts) is t_morecoming. // returns the current state of the request (morecoming, error, complete, terminated.) // if there is no data on the socket, the socket will be closed and t_terminated will be returned -static int read_msg(request_state *rs) +mDNSlocal int read_msg(request_state *rs) { uint32_t nleft; int nread; @@ -3266,7 +3311,7 @@ rerror: return t_error; } -static int send_msg(reply_state *rs) +mDNSlocal int send_msg(reply_state *rs) { ssize_t nwriten; @@ -3318,12 +3363,11 @@ static int send_msg(reply_state *rs) return rs->ts; } -static reply_state *create_reply(reply_op_t op, size_t datalen, request_state *request) +mDNSlocal reply_state *create_reply(reply_op_t op, size_t datalen, request_state *request) { reply_state *reply; int totallen; - if ((unsigned)datalen < sizeof(reply_hdr)) { LogMsg("ERROR: create_reply - data length less than lenght of required fields"); @@ -3350,7 +3394,7 @@ static reply_state *create_reply(reply_op_t op, size_t datalen, request_state *r return reply; } -static int deliver_error(request_state *rstate, mStatus err) +mDNSlocal int deliver_error(request_state *rstate, mStatus err) { int nwritten = -1; undelivered_error_t *undeliv; @@ -3382,7 +3426,7 @@ static int deliver_error(request_state *rstate, mStatus err) } // returns 0 on success, -1 if send is incomplete, or on terminal failure (request is aborted) -static transfer_state send_undelivered_error(request_state *rs) +mDNSlocal transfer_state send_undelivered_error(request_state *rs) { int nwritten; @@ -3410,7 +3454,7 @@ static transfer_state send_undelivered_error(request_state *rs) // send bogus data along with an error code to the app callback // returns 0 on success (linking reply into list of not fully delivered), // -1 on failure (request should be aborted) -static int deliver_async_error(request_state *rs, reply_op_t op, mStatus err) +mDNSlocal int deliver_async_error(request_state *rs, reply_op_t op, mStatus err) { int len; reply_state *reply; @@ -3431,7 +3475,7 @@ static int deliver_async_error(request_state *rs, reply_op_t op, mStatus err) return 0; } -static void abort_request(request_state *rs) +mDNSlocal void abort_request(request_state *rs) { reply_state *rep, *ptr; @@ -3439,7 +3483,10 @@ static void abort_request(request_state *rs) if (rs->msgbuf) freeL("abort_request", rs->msgbuf); LogOperation("%3d: Removing FD", rs->sd); udsSupportRemoveFDFromEventLoop(rs->sd); // Note: This also closes file descriptor rs->sd for us - rs->sd = dnssd_InvalidSocket; + + // Don't use dnssd_InvalidSocket (-1) because that's the sentinel value MACOSX_MDNS_MALLOC_DEBUGGING uses + // for detecting when the memory for an object is inadvertently freed while the object is still on some list + rs->sd = -2; // free pending replies rep = rs->replies; @@ -3458,7 +3505,7 @@ static void abort_request(request_state *rs) } } -static void unlink_request(request_state *rs) +mDNSlocal void unlink_request(request_state *rs) { request_state *ptr; @@ -3478,7 +3525,7 @@ static void unlink_request(request_state *rs) } //hack to search-replace perror's to LogMsg's -static void my_perror(char *errmsg) +mDNSlocal void my_perror(char *errmsg) { LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno())); } @@ -3487,7 +3534,7 @@ static void my_perror(char *errmsg) // without overrunning it. // returns 0 on success, -1 on error. -static int validate_message(request_state *rstate) +mDNSlocal int validate_message(request_state *rstate) { uint32_t min_size; @@ -3546,7 +3593,7 @@ static int validate_message(request_state *rstate) } -static uint32_t dnssd_htonl(uint32_t l) +mDNSlocal uint32_t dnssd_htonl(uint32_t l) { uint32_t ret; char * data; @@ -3560,7 +3607,7 @@ static uint32_t dnssd_htonl(uint32_t l) #if defined(_WIN32) -static char * win32_strerror(int inErrorCode) +mDNSlocal char * win32_strerror(int inErrorCode) { static char buffer[1024]; DWORD n; diff --git a/mDNSVxWorks/mDNSVxWorks.c b/mDNSVxWorks/mDNSVxWorks.c index dc116b5..988a41c 100644 --- a/mDNSVxWorks/mDNSVxWorks.c +++ b/mDNSVxWorks/mDNSVxWorks.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2005 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -20,130 +20,48 @@ * * @APPLE_LICENSE_HEADER_END@ - Contains: mDNS platform plugin for VxWorks. - - Copyright: Copyright (C) 2002-2004 Apple Computer, Inc., All Rights Reserved. - - Change History (most recent first): + Change History (most recent first): $Log: mDNSVxWorks.c,v $ -Revision 1.27 2004/12/17 23:37:49 cheshire - Guard against repeating wireless dissociation/re-association -(and other repetitive configuration changes) - -Revision 1.26 2004/10/28 02:00:35 cheshire - Call pipeDevDelete when disposing of commandPipe - -Revision 1.25 2004/10/16 00:17:01 cheshire - Replace IP TTL 255 check with local subnet source address check - -Revision 1.24 2004/09/21 21:02:56 cheshire -Set up ifname before calling mDNS_RegisterInterface() - -Revision 1.23 2004/09/17 01:08:57 cheshire -Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h - The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces - declared in that file are ONLY appropriate to single-address-space embedded applications. - For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. - -Revision 1.22 2004/09/17 00:19:11 cheshire -For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4 - -Revision 1.21 2004/09/16 00:24:50 cheshire - Fix unsafe use of mDNSPlatformTimeNow() - -Revision 1.20 2004/09/14 23:42:36 cheshire - Need to seed random number generator from platform-layer data - -Revision 1.19 2004/09/14 23:16:09 cheshire -mDNS_SetFQDNs has been renamed to mDNS_SetFQDN - -Revision 1.18 2004/08/14 03:22:42 cheshire - Dynamic DNS UI <-> mDNSResponder glue -Add GetUserSpecifiedDDNSName() routine -Convert ServiceRegDomain to domainname instead of C string -Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs - -Revision 1.17 2004/07/29 19:26:03 ksekar -Plaform-level changes for NATPMP support - -Revision 1.16 2004/04/22 05:11:28 bradley -Added mDNSPlatformUTC for TSIG signed dynamic updates. - -Revision 1.15 2004/04/21 02:49:12 cheshire -To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx' - -Revision 1.14 2004/04/09 17:43:04 cheshire -Make sure to set the McastTxRx field so that duplicate suppression works correctly - -Revision 1.13 2004/01/27 20:15:24 cheshire -: Time to prune obsolete code for listening on port 53 - -Revision 1.12 2004/01/24 09:12:37 bradley -Avoid TOS socket options to workaround a TOS routing problem with VxWorks and multiple interfaces -when sending unicast responses, which resulted in packets going out the wrong interface. - -Revision 1.11 2004/01/24 04:59:16 cheshire -Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again - -Revision 1.10 2003/11/14 21:27:09 cheshire -: Security: Crashing bug in mDNSResponder -Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers. - -Revision 1.9 2003/11/14 20:59:09 cheshire -Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. -Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file. - -Revision 1.8 2003/10/28 10:08:27 bradley -Removed legacy port 53 support as it is no longer needed. - -Revision 1.7 2003/08/20 05:58:54 bradley -Removed dependence on modified mDNSCore: define structures/prototypes locally. - -Revision 1.6 2003/08/18 23:19:05 cheshire - mDNSResponder divide by zero in mDNSPlatformRawTime() - -Revision 1.5 2003/08/15 00:05:04 bradley -Updated to use name/InterfaceID from new AuthRecord resrec field. Added output of new record sizes. - -Revision 1.4 2003/08/14 02:19:55 cheshire - Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord - -Revision 1.3 2003/08/12 19:56:27 cheshire -Update to APSL 2.0 - -Revision 1.2 2003/08/05 23:58:34 cheshire -Update code to compile with the new mDNSCoreReceive() function that requires a TTL -Right now this platform layer just reports 255 instead of returning the real value -- we should fix this - -Revision 1.1 2003/08/02 10:06:48 bradley -mDNS platform plugin for VxWorks. - - - Notes for non-Apple platforms: - - TARGET_NON_APPLE should be defined to 1 to avoid relying on Apple-only header files, macros, or functions. - - To Do: +Revision 1.28 2005/05/30 07:36:38 bradley +New implementation of the mDNS platform plugin for VxWorks 5.5 or later with IPv6 support. - - Add support for IPv6 (needs VxWorks IPv6 support). */ -// Set up the debug library to use the default category (see DebugServicesLite.h for details). - -#if( !TARGET_NON_APPLE ) - #define DEBUG_USE_DEFAULT_CATEGORY 1 +#if 0 +#pragma mark == Configuration == #endif +//=========================================================================================================================== +// Configuration +//=========================================================================================================================== + +#define DEBUG_NAME "[mDNS] " +#define MDNS_AAAA_OVER_IPV4 1 // 1=Send AAAA & A records over IPv4 & IPv6, 0=Send AAAA over IPv6, A over IPv4. +#define MDNS_EXCLUDE_IPV4_ROUTABLE_IPV6 1 // 1=Don't use IPv6 socket if non-link-local IPv4 available on same interface. +#define MDNS_ENABLE_PPP 0 // 1=Enable Unicast DNS over PPP interfaces. 0=Don't enable it. +#define MDNS_DEBUG_PACKETS 1 // 1=Enable debug output for packet send/recv if debug level high enough. +#define MDNS_DEBUG_SHOW 1 // 1=Enable console show routines. +#define DEBUG_USE_DEFAULT_CATEGORY 1 // Set up to use the default category (see DebugServices.h for details). + #include #include #include #include #include +#include + +#include "vxWorks.h" +#include "config.h" #include + #include -#include +#include +#include +#include +#include +#include #include #include #include @@ -151,7 +69,6 @@ mDNS platform plugin for VxWorks. #include #include -#include "vxWorks.h" #include "ifLib.h" #include "inetLib.h" #include "pipeDrv.h" @@ -162,30 +79,13 @@ mDNS platform plugin for VxWorks. #include "taskLib.h" #include "tickLib.h" -#include "config.h" - -#if( !TARGET_NON_APPLE ) - #include "ACP/ACPUtilities.h" - #include "Support/DebugServicesLite.h" - #include "Support/MiscUtilities.h" -#endif - +#include "CommonServices.h" +#include "DebugServices.h" +#include "DNSCommon.h" #include "mDNSEmbeddedAPI.h" #include "mDNSVxWorks.h" -#if 0 -#pragma mark == Preprocessor == -#endif - -//=========================================================================================================================== -// Preprocessor -//=========================================================================================================================== - -#if( !TARGET_NON_APPLE ) - debug_log_new_default_category( mdns ); -#endif - #if 0 #pragma mark == Constants == #endif @@ -194,118 +94,49 @@ mDNS platform plugin for VxWorks. // Constants //=========================================================================================================================== -#define DEBUG_NAME "[mDNS] " - -#define kMDNSDefaultName "My-Device" - -#define kMDNSTaskName "tMDNS" -#define kMDNSTaskPriority 102 -#define kMDNSTaskStackSize 49152 - -#define kMDNSPipeName "/pipe/mDNS" -#define kMDNSPipeMessageQueueSize 32 -#define kMDNSPipeMessageSize 1 - -#define kInvalidSocketRef -1 - typedef uint8_t MDNSPipeCommandCode; -enum -{ - kMDNSPipeCommandCodeInvalid = 0, - kMDNSPipeCommandCodeReschedule = 1, - kMDNSPipeCommandCodeReconfigure = 2, - kMDNSPipeCommandCodeQuit = 3 -}; - -#if 0 -#pragma mark == Structures == -#endif - -//=========================================================================================================================== -// Structures -//=========================================================================================================================== -typedef int MDNSSocketRef; - -struct MDNSInterfaceItem -{ - MDNSInterfaceItem * next; - char name[ 32 ]; - MDNSSocketRef multicastSocketRef; - MDNSSocketRef sendingSocketRef; - NetworkInterfaceInfo hostSet; - mDNSBool hostRegistered; - - int sendMulticastCounter; - int sendUnicastCounter; - int sendErrorCounter; - - int recvCounter; - int recvErrorCounter; - int recvLoopCounter; -}; +#define kMDNSPipeCommandCodeInvalid 0 +#define kMDNSPipeCommandCodeReschedule 1 +#define kMDNSPipeCommandCodeReconfigure 2 +#define kMDNSPipeCommandCodeQuit 3 #if 0 -#pragma mark == Macros == +#pragma mark == Prototypes == #endif //=========================================================================================================================== -// Macros +// Prototypes //=========================================================================================================================== -#if( TARGET_NON_APPLE ) - - // Do-nothing versions of the debugging macros for non-Apple platforms. - - #define check(assertion) - #define check_string( assertion, cstring ) - #define check_noerr(err) - #define check_noerr_string( error, cstring ) - #define check_errno( assertion, errno_value ) - #define debug_string( cstring ) - #define require( assertion, label ) do { if( !(assertion) ) goto label; } while(0) - #define require_string( assertion, label, string ) require(assertion, label) - #define require_quiet( assertion, label ) require( assertion, label ) - #define require_noerr( error, label ) do { if( (error) != 0 ) goto label; } while(0) - #define require_noerr_quiet( assertion, label ) require_noerr( assertion, label ) - #define require_noerr_action( error, label, action ) do { if( (error) != 0 ) { {action;}; goto label; } } while(0) - #define require_noerr_action_quiet( assertion, label, action ) require_noerr_action( assertion, label, action ) - #define require_action( assertion, label, action ) do { if( !(assertion) ) { {action;}; goto label; } } while(0) - #define require_action_quiet( assertion, label, action ) require_action( assertion, label, action ) - #define require_action_string( assertion, label, action, cstring ) do { if( !(assertion) ) { {action;}; goto label; } } while(0) - #define require_errno( assertion, errno_value, label ) do { if( !(assertion) ) goto label; } while(0) - #define require_errno_action( assertion, errno_value, label, action ) do { if( !(assertion) ) { {action;}; goto label; } } while(0) - - #define dlog( ARGS... ) +#if( DEBUG ) + mDNSlocal void DebugMsg( DebugLevel inLevel, const char *inFormat, ... ); - #define DEBUG_UNUSED( X ) (void)( X ) + #define dmsg( LEVEL, ARGS... ) DebugMsg( LEVEL, ## ARGS ) +#else + #define dmsg( LEVEL, ARGS... ) #endif -#if 0 -#pragma mark == Prototypes == +#if( DEBUG && MDNS_DEBUG_PACKETS ) + #define dpkt( LEVEL, ARGS... ) DebugMsg( LEVEL, ## ARGS ) +#else + #define dpkt( LEVEL, ARGS... ) #endif -//=========================================================================================================================== -// Prototypes -//=========================================================================================================================== - -// ifIndexToIfp is in net/if.c, but not exported by net/if.h so provide it here. - -extern struct ifnet * ifIndexToIfp(int ifIndex); +#define ForgetSem( X ) do { if( *( X ) ) { semDelete( ( *X ) ); *( X ) = 0; } } while( 0 ) +#define ForgetSocket( X ) do { if( IsValidSocket( *( X ) ) ) { close_compat( *( X ) ); *( X ) = kInvalidSocketRef; } } while( 0 ) -// Platform Internals +// Interfaces -mDNSlocal void SetupNames( mDNS * const inMDNS ); -mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS ); -mDNSlocal mStatus TearDownInterfaceList( mDNS * const inMDNS ); -mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inAddr, MDNSInterfaceItem **outItem ); -mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, MDNSInterfaceItem *inItem ); -mDNSlocal mStatus - SetupSocket( - mDNS * const inMDNS, - const struct ifaddrs * inAddr, - mDNSIPPort inPort, - MDNSSocketRef * outSocketRef ); +mDNSlocal mStatus UpdateInterfaceList( mDNS *const inMDNS, mDNSs32 inUTC ); +mDNSlocal NetworkInterfaceInfoVxWorks * AddInterfaceToList( mDNS *const inMDNS, struct ifaddrs *inIFA, mDNSs32 inUTC ); +mDNSlocal int SetupActiveInterfaces( mDNS *const inMDNS, mDNSs32 inUTC ); +mDNSlocal void MarkAllInterfacesInactive( mDNS *const inMDNS, mDNSs32 inUTC ); +mDNSlocal int ClearInactiveInterfaces( mDNS *const inMDNS, mDNSs32 inUTC, mDNSBool inClosing ); +mDNSlocal NetworkInterfaceInfoVxWorks * FindRoutableIPv4( mDNS *const inMDNS, mDNSu32 inScopeID ); +mDNSlocal NetworkInterfaceInfoVxWorks * FindInterfaceByIndex( mDNS *const inMDNS, int inFamily, mDNSu32 inIndex ); +mDNSlocal mStatus SetupSocket( mDNS *const inMDNS, const mDNSAddr *inAddr, mDNSBool inMcast, int inFamily, SocketSet *inSS ); +mDNSlocal mStatus SockAddrToMDNSAddr( const struct sockaddr * const inSA, mDNSAddr *outIP ); // Commands @@ -313,26 +144,26 @@ mDNSlocal mStatus SetupCommandPipe( mDNS * const inMDNS ); mDNSlocal mStatus TearDownCommandPipe( mDNS * const inMDNS ); mDNSlocal mStatus SendCommand( const mDNS * const inMDNS, MDNSPipeCommandCode inCommandCode ); mDNSlocal mStatus ProcessCommand( mDNS * const inMDNS ); -mDNSlocal void ProcessCommandReconfigure( mDNS *inMDNS ); // Threads -mDNSlocal mStatus SetupTask( mDNS * const inMDNS ); -mDNSlocal mStatus TearDownTask( mDNS * const inMDNS ); mDNSlocal void Task( mDNS *inMDNS ); mDNSlocal mStatus TaskInit( mDNS *inMDNS ); -mDNSlocal void TaskSetupReadSet( mDNS *inMDNS, fd_set *outReadSet, int *outMaxSocket ); -mDNSlocal void TaskSetupTimeout( mDNS *inMDNS, mDNSs32 inNextTaskTime, struct timeval *outTimeout ); -mDNSlocal void TaskProcessPacket( mDNS *inMDNS, MDNSInterfaceItem *inItem, MDNSSocketRef inSocketRef ); - -// Utilities - -#if( TARGET_NON_APPLE ) - mDNSlocal void GenerateUniqueHostName( char *outName, long *ioSeed ); - mDNSlocal void GenerateUniqueDNSName( char *outName, long *ioSeed ); -#endif - -// Platform Accessors +mDNSlocal void TaskTerm( mDNS *inMDNS ); +mDNSlocal void TaskSetupSelect( mDNS *inMDNS, fd_set *outSet, int *outMaxFd, mDNSs32 inNextEvent, struct timeval *outTimeout ); +mDNSlocal void TaskProcessPackets( mDNS *inMDNS, SocketSet *inSS, SocketRef inSock ); +mDNSlocal ssize_t + mDNSRecvMsg( + SocketRef inSock, + void * inBuffer, + size_t inBufferSize, + void * outFrom, + size_t inFromSize, + size_t * outFromSize, + mDNSAddr * outDstAddr, + uint32_t * outIndex ); + +// DNSServices compatibility. When all clients move to DNS-SD, this section can be removed. #ifdef __cplusplus extern "C" { @@ -360,17 +191,19 @@ mDNSexport mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInter // Globals //=========================================================================================================================== -mDNSlocal mDNS * gMDNSPtr = NULL; -mDNSlocal mDNS_PlatformSupport gMDNSPlatformSupport; -mDNSlocal mDNSs32 gMDNSTicksToMicrosecondsMultiplier = 0; - -// Platform support +debug_log_new_default_category( mdns ); -mDNSs32 mDNSPlatformOneSecond; +mDNSexport mDNSs32 mDNSPlatformOneSecond; +mDNSlocal mDNSs32 gMDNSTicksToMicro = 0; +mDNSlocal mDNS * gMDNSPtr = NULL; +mDNSlocal mDNS_PlatformSupport gMDNSPlatformSupport; +mDNSlocal mDNSBool gMDNSDeferIPv4 = mDNSfalse; +#if( DEBUG ) + DebugLevel gMDNSDebugOverrideLevel = kDebugLevelMax; +#endif #if 0 #pragma mark - -#pragma mark == Public APIs == #endif //=========================================================================================================================== @@ -378,18 +211,21 @@ mDNSs32 mDNSPlatformOneSecond; //=========================================================================================================================== void mDNSReconfigure( void ) +{ + if( gMDNSPtr ) SendCommand( gMDNSPtr, kMDNSPipeCommandCodeReconfigure ); +} + +//=========================================================================================================================== +// mDNSDeferIPv4 +//=========================================================================================================================== + +void mDNSDeferIPv4( mDNSBool inDefer ) { - // Send a "reconfigure" command to the MDNS task. - - if( gMDNSPtr ) - { - SendCommand( gMDNSPtr, kMDNSPipeCommandCodeReconfigure ); - } + gMDNSDeferIPv4 = inDefer; } #if 0 #pragma mark - -#pragma mark == Platform Support == #endif //=========================================================================================================================== @@ -399,51 +235,48 @@ void mDNSReconfigure( void ) mStatus mDNSPlatformInit( mDNS * const inMDNS ) { mStatus err; + int id; - dlog( kDebugLevelInfo, DEBUG_NAME "platform init\n" ); + mDNSPlatformOneSecond = sysClkRateGet(); + gMDNSTicksToMicro = ( 1000000L / mDNSPlatformOneSecond ); - // Initialize variables. - - memset( &gMDNSPlatformSupport, 0, sizeof( gMDNSPlatformSupport ) ); - inMDNS->p = &gMDNSPlatformSupport; - inMDNS->p->commandPipe = ERROR; - inMDNS->p->task = ERROR; - inMDNS->p->rescheduled = 1; // Default to rescheduled until fully initialized. - mDNSPlatformOneSecond = sysClkRateGet(); - gMDNSTicksToMicrosecondsMultiplier = ( 1000000L / mDNSPlatformOneSecond ); + // Do minimal initialization to get the task started and so we can cleanup safely if an error occurs. - // Allocate semaphores. + memset( &gMDNSPlatformSupport, 0, sizeof( gMDNSPlatformSupport ) ); + if( !inMDNS->p ) inMDNS->p = &gMDNSPlatformSupport; + inMDNS->p->unicastSS.info = NULL; + inMDNS->p->unicastSS.sockV4 = kInvalidSocketRef; + inMDNS->p->unicastSS.sockV6 = kInvalidSocketRef; + inMDNS->p->initErr = mStatus_NotInitializedErr; + inMDNS->p->commandPipe = ERROR; + inMDNS->p->taskID = ERROR; - inMDNS->p->lockID = semMCreate( SEM_Q_FIFO ); - require_action( inMDNS->p->lockID, exit, err = mStatus_NoMemoryErr ); + inMDNS->p->lock = semMCreate( SEM_Q_FIFO ); + require_action( inMDNS->p->lock, exit, err = mStatus_NoMemoryErr ); - inMDNS->p->readyEvent = semBCreate( SEM_Q_FIFO, SEM_EMPTY ); - require_action( inMDNS->p->readyEvent, exit, err = mStatus_NoMemoryErr ); + inMDNS->p->initEvent = semBCreate( SEM_Q_FIFO, SEM_EMPTY ); + require_action( inMDNS->p->initEvent, exit, err = mStatus_NoMemoryErr ); inMDNS->p->quitEvent = semBCreate( SEM_Q_FIFO, SEM_EMPTY ); require_action( inMDNS->p->quitEvent, exit, err = mStatus_NoMemoryErr ); - gMDNSPtr = inMDNS; - - // Set up the task and wait for it to initialize. Initialization is done from the task instead of here to avoid - // stack space issues. Some of the initialization may require a larger stack than the current task supports. + // Start the task and wait for it to initialize. The task does the full initialization from its own context + // to avoid potential issues with stack space and APIs that key off the current task (e.g. watchdog timers). + // We wait here until the init is complete because it needs to be ready to use as soon as this function returns. - err = SetupTask( inMDNS ); + id = taskSpawn( "tMDNS", 102, 0, 16384, (FUNCPTR) Task, (int) inMDNS, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + err = translate_errno( id != ERROR, errno_compat(), mStatus_NoMemoryErr ); require_noerr( err, exit ); - err = semTake( inMDNS->p->readyEvent, WAIT_FOREVER ); - require_noerr( err, exit ); - err = inMDNS->p->taskInitErr; + err = semTake( inMDNS->p->initEvent, WAIT_FOREVER ); + if( err == OK ) err = inMDNS->p->initErr; require_noerr( err, exit ); + gMDNSPtr = inMDNS; mDNSCoreInitComplete( inMDNS, err ); exit: - if( err ) - { - mDNSPlatformClose( inMDNS ); - } - dlog( kDebugLevelInfo, DEBUG_NAME "platform init done (err=%ld)\n", err ); + if( err ) mDNSPlatformClose( inMDNS ); return( err ); } @@ -455,41 +288,30 @@ void mDNSPlatformClose( mDNS * const inMDNS ) { mStatus err; - dlog( kDebugLevelInfo, DEBUG_NAME "platform close\n" ); check( inMDNS ); - // Tear everything down. - - err = TearDownTask( inMDNS ); - check_noerr( err ); - - err = TearDownInterfaceList( inMDNS ); - check_noerr( err ); - - err = TearDownCommandPipe( inMDNS ); - check_noerr( err ); - gMDNSPtr = NULL; - // Release semaphores. + // Signal the task to quit and wait for it to signal back that it exited. Timeout in 10 seconds to handle a hung thread. - if( inMDNS->p->quitEvent ) - { - semDelete( inMDNS->p->quitEvent ); - inMDNS->p->quitEvent = 0; - } - if( inMDNS->p->readyEvent ) + if( inMDNS->p->taskID != ERROR ) { - semDelete( inMDNS->p->readyEvent ); - inMDNS->p->readyEvent = 0; - } - if( inMDNS->p->lockID ) - { - semDelete( inMDNS->p->lockID ); - inMDNS->p->lockID = 0; + SendCommand( inMDNS, kMDNSPipeCommandCodeQuit ); + if( inMDNS->p->quitEvent ) + { + err = semTake( inMDNS->p->quitEvent, sysClkRateGet() * 10 ); + check_noerr( err ); + } + inMDNS->p->taskID = ERROR; } - dlog( kDebugLevelInfo, DEBUG_NAME "platform close done\n" ); + // Clean up resources set up in mDNSPlatformInit. All other resources should have been cleaned up already by TaskTerm. + + ForgetSem( &inMDNS->p->quitEvent ); + ForgetSem( &inMDNS->p->initEvent ); + ForgetSem( &inMDNS->p->lock ); + + dmsg( kDebugLevelNotice, DEBUG_NAME "CLOSED\n" ); } //=========================================================================================================================== @@ -499,70 +321,89 @@ void mDNSPlatformClose( mDNS * const inMDNS ) mStatus mDNSPlatformSendUDP( const mDNS * const inMDNS, - const void * const inMsg, - const mDNSu8 * const inMsgEnd, + const void * const inMsg, + const mDNSu8 * const inEnd, mDNSInterfaceID inInterfaceID, const mDNSAddr * inDstIP, mDNSIPPort inDstPort ) { - mStatus err; - MDNSInterfaceItem * item; - struct sockaddr_in addr; - int n; - - dlog( kDebugLevelChatty, DEBUG_NAME "platform send UDP\n" ); + mStatus err; + NetworkInterfaceInfoVxWorks * info; + SocketRef sock; + struct sockaddr_storage to; + int n; - // Check parameters. + // Set up the sockaddr to sent to and the socket to send on. - check( inMDNS ); - check( inMsg ); - check( inMsgEnd ); - check( inInterfaceID ); - check( inDstIP ); - if( inDstIP->type != mDNSAddrType_IPv4 ) + info = (NetworkInterfaceInfoVxWorks *) inInterfaceID; + if( inDstIP->type == mDNSAddrType_IPv4 ) + { + struct sockaddr_in * sa4; + + sa4 = (struct sockaddr_in *) &to; + sa4->sin_len = sizeof( *sa4 ); + sa4->sin_family = AF_INET; + sa4->sin_port = inDstPort.NotAnInteger; + sa4->sin_addr.s_addr = inDstIP->ip.v4.NotAnInteger; + sock = info ? info->ss.sockV4 : inMDNS->p->unicastSS.sockV4; + } + else if( inDstIP->type == mDNSAddrType_IPv6 ) + { + struct sockaddr_in6 * sa6; + + sa6 = (struct sockaddr_in6 *) &to; + sa6->sin6_len = sizeof( *sa6 ); + sa6->sin6_family = AF_INET6; + sa6->sin6_port = inDstPort.NotAnInteger; + sa6->sin6_flowinfo = 0; + sa6->sin6_addr = *( (struct in6_addr *) &inDstIP->ip.v6 ); + sa6->sin6_scope_id = info ? info->scopeID : 0; + sock = info ? info->ss.sockV6 : inMDNS->p->unicastSS.sockV6; + } + else { + dmsg( kDebugLevelError, DEBUG_NAME "%s: ERROR! destination is not an IPv4 or IPv6 address\n", __ROUTINE__ ); err = mStatus_BadParamErr; goto exit; } - -#if( DEBUG ) - // Make sure the InterfaceID is valid. - for( item = inMDNS->p->interfaceList; item; item = item->next ) + // Send the packet if we've got a valid socket of this type. Note: mDNSCore may ask us to send an IPv4 packet and then + // an IPv6 multicast packet. If we don't have the corresponding type of socket available, quietly return an error. + + n = (int)( (mDNSu8 *) inEnd - (mDNSu8 *) inMsg ); + if( !IsValidSocket( sock ) ) { - if( item == (MDNSInterfaceItem *) inInterfaceID ) - { - break; - } + dpkt( kDebugLevelChatty - 1, + DEBUG_NAME "DROP: %4d bytes, DST=[%#39a]:%5hu, IF=%8s(%u) %#p\n", + n, inDstIP, mDNSVal16( inDstPort ), info ? info->ifinfo.ifname : "unicast", info ? info->scopeID : 0, info ); + err = mStatus_Invalid; + goto exit; } - require_action( item, exit, err = mStatus_NoSuchNameErr ); -#endif - - // Send the packet. - - item = (MDNSInterfaceItem *) inInterfaceID; - check( item->sendingSocketRef != kInvalidSocketRef ); - - memset( &addr, 0, sizeof( addr ) ); - addr.sin_family = AF_INET; - addr.sin_port = inDstPort.NotAnInteger; - addr.sin_addr.s_addr = inDstIP->ip.v4.NotAnInteger; - - n = inMsgEnd - ( (const mDNSu8 * const) inMsg ); - n = sendto( item->sendingSocketRef, (char *) inMsg, n, 0, (struct sockaddr *) &addr, sizeof( addr ) ); - check_errno( n, errno ); - item->sendErrorCounter += ( n < 0 ); - item->sendMulticastCounter += ( inDstPort.NotAnInteger == MulticastDNSPort.NotAnInteger ); - item->sendUnicastCounter += ( inDstPort.NotAnInteger != MulticastDNSPort.NotAnInteger ); + dpkt( kDebugLevelChatty, + DEBUG_NAME "SEND %4d bytes, DST=[%#39a]:%5hu, IF=%8s(%u) %#p\n", + n, inDstIP, mDNSVal16( inDstPort ), info ? info->ifinfo.ifname : "unicast", info ? info->scopeID : 0, info ); - dlog( kDebugLevelChatty, DEBUG_NAME "sent (to=%u.%u.%u.%u:%hu)\n", - inDstIP->ip.v4.b[ 0 ], inDstIP->ip.v4.b[ 1 ], inDstIP->ip.v4.b[ 2 ], inDstIP->ip.v4.b[ 3 ], - htons( inDstPort.NotAnInteger ) ); + n = sendto( sock, (mDNSu8 *) inMsg, n, 0, (struct sockaddr *) &to, to.ss_len ); + if( n < 0 ) + { + // Don't warn about ARP failures or no route to host for unicast destinations. + + err = errno_compat(); + if( ( ( err == EHOSTDOWN ) || ( err == ENETDOWN ) || ( err == EHOSTUNREACH ) ) && !mDNSAddressIsAllDNSLinkGroup( inDstIP ) ) + { + goto exit; + } + + dmsg( kDebugLevelError, "%s: ERROR! sendto failed on %8s(%u) to %#a:%d, sock %d, err %d, time %u\n", + __ROUTINE__, info ? info->ifinfo.ifname : "unicast", info ? info->scopeID : 0, inDstIP, mDNSVal16( inDstPort ), + sock, err, (unsigned int) inMDNS->timenow ); + if( err == 0 ) err = mStatus_UnknownErr; + goto exit; + } err = mStatus_NoError; - + exit: - dlog( kDebugLevelChatty, DEBUG_NAME "platform send UDP done\n" ); return( err ); } @@ -591,7 +432,7 @@ mDNSexport int mDNSPlatformReadTCP(int sd, void *buf, int buflen) { (void)sd; // Unused (void)buf; // Unused - (void)buflen; // Unused + (void)buflen; // Unused return(0); } @@ -609,16 +450,19 @@ mDNSexport int mDNSPlatformWriteTCP(int sd, const char *msg, int len) void mDNSPlatformLock( const mDNS * const inMDNS ) { - check( inMDNS->p->lockID ); + check_string( inMDNS->p && ( inMDNS->p->taskID != ERROR ), "mDNS task not started" ); - if( inMDNS->p->lockID ) +#if( DEBUG ) + if( semTake( inMDNS->p->lock, 60 * sysClkRateGet() ) != OK ) { - #if( TARGET_NON_APPLE ) - semTake( inMDNS->p->lockID, WAIT_FOREVER ); - #else - semTakeDeadlockDetect( inMDNS->p->lockID, WAIT_FOREVER ); - #endif + dmsg( kDebugLevelTragic, "\n### DEADLOCK DETECTED ### (sem=%#p, task=%#p)\n\n", inMDNS->p->lock, taskIdSelf() ); + debug_stack_trace(); // 1) Print Stack Trace. + semShow( inMDNS->p->lock, 1 ); // 2) Print semaphore info, including which tasks are pending on it. + taskSuspend( 0 ); // 3) Suspend task. Can be resumed from the console for debugging. } +#else + semTake( inMDNS->p->lock, WAIT_FOREVER ); +#endif } //=========================================================================================================================== @@ -627,38 +471,23 @@ void mDNSPlatformLock( const mDNS * const inMDNS ) void mDNSPlatformUnlock( const mDNS * const inMDNS ) { - check( inMDNS ); - check( inMDNS->p ); - check( inMDNS->p->lockID ); - check_string( inMDNS->p->task != ERROR, "mDNS task not started" ); - - // When an API routine is called, "m->NextScheduledEvent" is reset to "timenow" before calling mDNSPlatformUnlock() - // Since our main mDNS_Execute() loop is on a different thread, we need to wake up that thread to: - // (a) handle immediate work (if any) resulting from this API call - // (b) calculate the next sleep time between now and the next interesting event + check_string( inMDNS->p && ( inMDNS->p->taskID != ERROR ), "mDNS task not started" ); - if( ( mDNS_TimeNow(inMDNS) - inMDNS->NextScheduledEvent ) >= 0 ) - { - // We only need to send the reschedule event when called from a task other than the mDNS task since if we are - // called from mDNS task, we'll loop back and call mDNS_Execute. This avoids filling up the command queue. - - if( ( inMDNS->p->rescheduled++ == 0 ) && ( taskIdSelf() != inMDNS->p->task ) ) - { - SendCommand( inMDNS, kMDNSPipeCommandCodeReschedule ); - } - } + // Wake up the mDNS task to handle any work initiated by an API call and to calculate the next event time. + // We only need to wake up if we're not already inside the task. This avoids filling up the command queue. - if( inMDNS->p->lockID ) + if( taskIdSelf() != inMDNS->p->taskID ) { - semGive( inMDNS->p->lockID ); + SendCommand( inMDNS, kMDNSPipeCommandCodeReschedule ); } + semGive( inMDNS->p->lock ); } //=========================================================================================================================== // mDNSPlatformStrLen //=========================================================================================================================== -mDNSu32 mDNSPlatformStrLen( const void *inSrc ) +mDNSu32 mDNSPlatformStrLen( const void *inSrc ) { check( inSrc ); @@ -735,15 +564,14 @@ mDNSexport void * mDNSPlatformMemAllocate( mDNSu32 inSize ) mDNSexport void mDNSPlatformMemFree( void *inMem ) { check( inMem ); - - free( inMem ); + if( inMem ) free( inMem ); } //=========================================================================================================================== // mDNSPlatformRandomSeed //=========================================================================================================================== -mDNSexport mDNSu32 mDNSPlatformRandomSeed(void) +mDNSexport mDNSu32 mDNSPlatformRandomSeed( void ) { return( tickGet() ); } @@ -752,9 +580,10 @@ mDNSexport mDNSu32 mDNSPlatformRandomSeed(void) // mDNSPlatformTimeInit //=========================================================================================================================== -mDNSexport mStatus mDNSPlatformTimeInit( void ) +mDNSexport mStatus mDNSPlatformTimeInit( void ) { // No special setup is required on VxWorks -- we just use tickGet(). + return( mStatus_NoError ); } @@ -773,102 +602,102 @@ mDNSs32 mDNSPlatformRawTime( void ) mDNSexport mDNSs32 mDNSPlatformUTC( void ) { - return( -1 ); + return( (mDNSs32) time( NULL ) ); } //=========================================================================================================================== -// mDNSPlatformInterfaceNameToID +// mDNSPlatformInterfaceIDfromInterfaceIndex //=========================================================================================================================== -mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID ) +mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex( const mDNS *const inMDNS, mDNSu32 inIndex ) { - mStatus err; - MDNSInterfaceItem * ifd; + NetworkInterfaceInfoVxWorks * i; - check( inMDNS ); - check( inMDNS->p ); - check( inName ); - - // Search for an interface with the specified name, - - for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next ) + if( inIndex == (mDNSu32) -1 ) return( mDNSInterface_LocalOnly ); + if( inIndex != 0 ) { - if( strcmp( ifd->name, inName ) == 0 ) + for( i = inMDNS->p->interfaceList; i; i = i->next ) { - break; + // Don't get tricked by inactive interfaces with no InterfaceID set. + + if( i->ifinfo.InterfaceID && ( i->scopeID == inIndex ) ) return( i->ifinfo.InterfaceID ); } } - if( !ifd ) - { - err = mStatus_NoSuchNameErr; - goto exit; - } - - // Success! + return( NULL ); +} + +//=========================================================================================================================== +// mDNSPlatformInterfaceIndexfromInterfaceID +//=========================================================================================================================== + +mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID( const mDNS *const inMDNS, mDNSInterfaceID inID ) +{ + NetworkInterfaceInfoVxWorks * i; - if( outID ) + if( inID == mDNSInterface_LocalOnly ) return( (mDNSu32) -1 ); + if( inID ) { - *outID = (mDNSInterfaceID) ifd; + // Don't use i->ifinfo.InterfaceID here, because we DO want to find inactive interfaces. + + for( i = inMDNS->p->interfaceList; i && ( (mDNSInterfaceID) i != inID ); i = i->next ) {} + if( i ) return( i->scopeID ); } - err = mStatus_NoError; - -exit: - return( err ); + return( 0 ); } //=========================================================================================================================== -// mDNSPlatformInterfaceIDToInfo +// mDNSPlatformInterfaceNameToID //=========================================================================================================================== -mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo ) +mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID ) { - mStatus err; - MDNSInterfaceItem * ifd; - - check( inMDNS ); - check( inID ); - check( outInfo ); - - // Search for an interface with the specified ID, + NetworkInterfaceInfoVxWorks * i; - for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next ) + for( i = inMDNS->p->interfaceList; i; i = i->next ) { - if( ifd == (MDNSInterfaceItem *) inID ) + // Don't get tricked by inactive interfaces with no InterfaceID set. + + if( i->ifinfo.InterfaceID && ( strcmp( i->ifinfo.ifname, inName ) == 0 ) ) { - break; + *outID = (mDNSInterfaceID) i; + return( mStatus_NoError ); } } - if( !ifd ) - { - err = mStatus_NoSuchNameErr; - goto exit; - } + return( mStatus_NoSuchNameErr ); +} + +//=========================================================================================================================== +// mDNSPlatformInterfaceIDToInfo +//=========================================================================================================================== + +mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo ) +{ + NetworkInterfaceInfoVxWorks * i; - // Success! + // Don't use i->ifinfo.InterfaceID here, because we DO want to find inactive interfaces. - outInfo->name = ifd->name; - outInfo->ip = ifd->hostSet.ip; - err = mStatus_NoError; + for( i = inMDNS->p->interfaceList; i && ( (mDNSInterfaceID) i != inID ); i = i->next ) {} + if( !i ) return( mStatus_NoSuchNameErr ); -exit: - return( err ); + outInfo->name = i->ifinfo.ifname; + outInfo->ip = i->ifinfo.ip; + return( mStatus_NoError ); } //=========================================================================================================================== // debugf_ //=========================================================================================================================== -#if( MDNS_DEBUGMSGS ) -mDNSexport void debugf_( const char *format, ... ) +#if( MDNS_DEBUGMSGS > 0 ) +mDNSexport void debugf_( const char *inFormat, ... ) { char buffer[ 512 ]; - va_list args; - mDNSu32 length; - - va_start( args, format ); - length = mDNS_vsnprintf( buffer, sizeof( buffer ), format, args ); + va_list args; + + va_start( args, inFormat ); + mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args ); va_end( args ); - + dlog( kDebugLevelInfo, "%s\n", buffer ); } #endif @@ -878,14 +707,13 @@ mDNSexport void debugf_( const char *format, ... ) //=========================================================================================================================== #if( MDNS_DEBUGMSGS > 1 ) -mDNSexport void verbosedebugf_( const char *format, ... ) +mDNSexport void verbosedebugf_( const char *inFormat, ... ) { char buffer[ 512 ]; - va_list args; - mDNSu32 length; - - va_start( args, format ); - length = mDNS_vsnprintf( buffer, sizeof( buffer ), format, args ); + va_list args; + + va_start( args, inFormat ); + mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args ); va_end( args ); dlog( kDebugLevelVerbose, "%s\n", buffer ); @@ -896,406 +724,769 @@ mDNSexport void verbosedebugf_( const char *format, ... ) // LogMsg //=========================================================================================================================== -void LogMsg( const char *inFormat, ... ) +mDNSexport void LogMsg( const char *inFormat, ... ) { +#if( DEBUG ) char buffer[ 512 ]; - va_list args; - mDNSu32 length; + va_list args; va_start( args, inFormat ); - length = mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args ); + mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args ); va_end( args ); dlog( kDebugLevelWarning, "%s\n", buffer ); -} - -#if 0 -#pragma mark - -#pragma mark == Platform Internals == +#else + DEBUG_UNUSED( inFormat ); #endif +} +#if( DEBUG ) //=========================================================================================================================== -// SetupNames +// DebugMsg //=========================================================================================================================== -mDNSlocal void SetupNames( mDNS * const inMDNS ) +mDNSlocal void DebugMsg( DebugLevel inLevel, const char *inFormat, ... ) { - char tempCString[ 128 ]; - mDNSu8 tempPString[ 128 ]; - mDNSu8 * namePtr; - - // Set up the host name. - - tempCString[ 0 ] = '\0'; - GenerateUniqueHostName( tempCString, NULL ); - check( tempCString[ 0 ] != '\0' ); - if( tempCString[ 0 ] == '\0' ) - { - // No name so use the default. - - strcpy( tempCString, kMDNSDefaultName ); - } - inMDNS->nicelabel.c[ 0 ] = strlen( tempCString ); - memcpy( &inMDNS->nicelabel.c[ 1 ], tempCString, inMDNS->nicelabel.c[ 0 ] ); - check( inMDNS->nicelabel.c[ 0 ] > 0 ); - - // Set up the DNS name. + char buffer[ 512 ]; + va_list args; - tempCString[ 0 ] = '\0'; - GenerateUniqueDNSName( tempCString, NULL ); - if( tempCString[ 0 ] != '\0' ) - { - tempPString[ 0 ] = strlen( tempCString ); - memcpy( &tempPString[ 1 ], tempCString, tempPString[ 0 ] ); - namePtr = tempPString; - } - else - { - // No DNS name so use the host name. - - namePtr = inMDNS->nicelabel.c; - } - ConvertUTF8PstringToRFC1034HostLabel( namePtr, &inMDNS->hostlabel ); - if( inMDNS->hostlabel.c[ 0 ] == 0 ) - { - // Nice name has no characters that are representable as an RFC 1034 name (e.g. Japanese) so use the default. - - MakeDomainLabelFromLiteralString( &inMDNS->hostlabel, kMDNSDefaultName ); - } - check( inMDNS->hostlabel.c[ 0 ] > 0 ); - - mDNS_SetFQDN( inMDNS ); + va_start( args, inFormat ); + mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args ); + va_end( args ); - dlog( kDebugLevelInfo, DEBUG_NAME "nice name \"%.*s\"\n", inMDNS->nicelabel.c[ 0 ], &inMDNS->nicelabel.c[ 1 ] ); - dlog( kDebugLevelInfo, DEBUG_NAME "host name \"%.*s\"\n", inMDNS->hostlabel.c[ 0 ], &inMDNS->hostlabel.c[ 1 ] ); + if( inLevel >= gMDNSDebugOverrideLevel ) inLevel = kDebugLevelMax; + dlog( inLevel, "%s", buffer ); } +#endif + +#if 0 +#pragma mark - +#pragma mark == Interfaces == +#endif //=========================================================================================================================== -// SetupInterfaceList +// UpdateInterfaceList //=========================================================================================================================== -mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS ) -{ - mStatus err; - struct ifaddrs * addrs; - struct ifaddrs * p; - uint32_t flagMask; - uint32_t flagTest; - MDNSInterfaceItem ** next; - MDNSInterfaceItem * item; +#if( MDNS_ENABLE_PPP ) - addrs = NULL; - - dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface list\n" ); - check( inMDNS ); + // Note: This includes PPP dial-in interfaces (pppXYZ), but not PPP dial-out interface (pppdXYZ). - // Tear down any existing interfaces that may be set up. - - TearDownInterfaceList( inMDNS ); - inMDNS->p->interfaceList = NULL; - next = &inMDNS->p->interfaceList; + #define IsCompatibleInterface( IFA ) \ + ( ( ( IFA )->ifa_flags & IFF_UP ) && \ + ( ( ( IFA )->ifa_addr->sa_family == AF_INET ) || ( ( IFA )->ifa_addr->sa_family == AF_INET6 ) ) && \ + ( ( IFA )->ifa_netmask && ( ( IFA )->ifa_addr->sa_family == ( IFA )->ifa_netmask->sa_family ) ) && \ + ( !( ( IFA )->ifa_flags & IFF_POINTOPOINT ) || ( strncmp( ( IFA )->ifa_name, "pppd", 4 ) != 0 ) ) ) +#else + #define IsCompatibleInterface( IFA ) \ + ( ( ( ( IFA )->ifa_flags & ( IFF_UP | IFF_MULTICAST | IFF_POINTOPOINT ) ) == ( IFF_UP | IFF_MULTICAST ) ) && \ + ( ( ( IFA )->ifa_addr->sa_family == AF_INET ) || ( ( IFA )->ifa_addr->sa_family == AF_INET6 ) ) && \ + ( ( IFA )->ifa_netmask && ( ( IFA )->ifa_addr->sa_family == ( IFA )->ifa_netmask->sa_family ) ) ) +#endif + +#define IsLinkLocalSockAddr( 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_LINKLOCAL( &( (const struct sockaddr_in6 *)( SA ) )->sin6_addr ) ) + +#define FamilyToString( X ) \ + ( ( ( X ) == AF_INET ) ? "AF_INET" : \ + ( ( ( X ) == AF_INET6 ) ? "AF_INET6" : \ + ( ( ( X ) == AF_LINK ) ? "AF_LINK" : \ + "UNKNOWN" ) ) ) + +mDNSlocal mStatus UpdateInterfaceList( mDNS *const inMDNS, mDNSs32 inUTC ) +{ + mStatus err; + struct ifaddrs * ifaList; + struct ifaddrs * ifa; + int family; + mDNSBool foundV4; + mDNSBool foundV6; + struct ifaddrs * loopbackV4; + struct ifaddrs * loopbackV6; + mDNSEthAddr primaryMAC; + SocketRef infoSock; + char defaultName[ 64 ]; + NetworkInterfaceInfoVxWorks * i; + domainlabel nicelabel; + domainlabel hostlabel; + domainlabel tmp; + + ifaList = NULL; + foundV4 = mDNSfalse; + foundV6 = mDNSfalse; + loopbackV4 = NULL; + loopbackV6 = NULL; + primaryMAC = zeroEthAddr; + + // Set up an IPv6 socket so we can check the state of interfaces using SIOCGIFAFLAG_IN6. + + infoSock = socket( AF_INET6, SOCK_DGRAM, 0 ); + check_translated_errno( IsValidSocket( infoSock ), errno_compat(), kUnknownErr ); + + // Run through the entire list of interfaces. + + err = getifaddrs( &ifaList ); + check_translated_errno( err == 0, errno_compat(), kUnknownErr ); + + for( ifa = ifaList; ifa; ifa = ifa->ifa_next ) + { + int flags; + + family = ifa->ifa_addr->sa_family; + dmsg( kDebugLevelVerbose, DEBUG_NAME "%s: %8s(%d), Flags 0x%08X, Family %8s(%2d)\n", __ROUTINE__, + ifa->ifa_name, if_nametoindex( ifa->ifa_name ), ifa->ifa_flags, FamilyToString( family ), family ); + + // Save off the MAC address of the first Ethernet-ish interface. + + if( family == AF_LINK ) + { + struct sockaddr_dl * sdl; + + sdl = (struct sockaddr_dl *) ifa->ifa_addr; + if( ( sdl->sdl_type == IFT_ETHER ) && ( sdl->sdl_alen == sizeof( primaryMAC ) && + mDNSSameEthAddress( &primaryMAC, &zeroEthAddr ) ) ) + { + memcpy( primaryMAC.b, sdl->sdl_data + sdl->sdl_nlen, 6 ); + } + } + + if( !IsCompatibleInterface( ifa ) ) continue; + + // If this is a link-local address and there's a non-link-local address on this interface, skip this alias. + + if( IsLinkLocalSockAddr( ifa->ifa_addr ) ) + { + struct ifaddrs * ifaLL; + + for( ifaLL = ifaList; ifaLL; ifaLL = ifaLL->ifa_next ) + { + if( ifaLL->ifa_addr->sa_family != family ) continue; + if( !IsCompatibleInterface( ifaLL ) ) continue; + if( strcmp( ifaLL->ifa_name, ifa->ifa_name ) != 0 ) continue; + if( !IsLinkLocalSockAddr( ifaLL->ifa_addr ) ) break; + } + if( ifaLL ) + { + dmsg( kDebugLevelInfo, DEBUG_NAME "%s: %8s(%d) skipping link-local alias\n", __ROUTINE__, + ifa->ifa_name, if_nametoindex( ifa->ifa_name ) ); + continue; + } + } + + // If this is an IPv6 interface, perform additional checks to make sure it is really ready for use. + // If this is a loopback interface, save it off since we may add it later if there are no other interfaces. + // Otherwise, add the interface to the list. + + flags = 0; + if( ( family == AF_INET6 ) && IsValidSocket( infoSock ) ) + { + struct sockaddr_in6 * sa6; + struct in6_ifreq ifr6; + + sa6 = (struct sockaddr_in6 *) ifa->ifa_addr; + memset( &ifr6, 0, sizeof( ifr6 ) ); + strcpy( ifr6.ifr_name, ifa->ifa_name ); + ifr6.ifr_addr = *sa6; + if( ioctl( infoSock, SIOCGIFAFLAG_IN6, (int) &ifr6 ) != -1 ) + { + flags = ifr6.ifr_ifru.ifru_flags6; + } + } + + // HACK: This excludes interfaces with IN6_IFF_DUPLICATED set instead of using IN6_IFF_NOTREADY (which is + // HACK: IN6_IFF_TENTATIVE | IN6_IFF_DUPLICATED) because we currently do not get a notification when an + // HACK: interface goes from the tentative state to the fully ready state. So as a short-term workaround, + // HACK: this allows tentative interfaces to be registered. We should revisit if we get notification hooks. + + if( flags & ( IN6_IFF_DUPLICATED | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED | IN6_IFF_TEMPORARY ) ) + { + dmsg( kDebugLevelNotice, DEBUG_NAME "%s: %8s(%d), SIOCGIFAFLAG_IN6 not ready yet (0x%X)\n", __ROUTINE__, + ifa->ifa_name, if_nametoindex( ifa->ifa_name ), flags ); + continue; + } + if( ifa->ifa_flags & IFF_LOOPBACK ) + { + if( family == AF_INET ) loopbackV4 = ifa; + else loopbackV6 = ifa; + } + else + { + if( ( family == AF_INET ) && gMDNSDeferIPv4 && IsLinkLocalSockAddr( ifa->ifa_addr ) ) continue; + i = AddInterfaceToList( inMDNS, ifa, inUTC ); + if( i && i->multicast ) + { + if( family == AF_INET ) foundV4 = mDNStrue; + else foundV6 = mDNStrue; + } + } + } - // Set up each interface that is active, multicast-capable, and not the loopback interface or point-to-point. + // For efficiency, we don't register a loopback interface when other interfaces of that family are available. - flagMask = IFF_UP | IFF_MULTICAST | IFF_LOOPBACK | IFF_POINTOPOINT; - flagTest = IFF_UP | IFF_MULTICAST; + if( !foundV4 && loopbackV4 ) AddInterfaceToList( inMDNS, loopbackV4, inUTC ); + if( !foundV6 && loopbackV6 ) AddInterfaceToList( inMDNS, loopbackV6, inUTC ); + freeifaddrs( ifaList ); + if( IsValidSocket( infoSock ) ) close_compat( infoSock ); - err = getifaddrs( &addrs ); - require_noerr( err, exit ); + // The list is complete. Set the McastTxRx setting for each interface. We always send and receive using IPv4. + // To reduce traffic, we send and receive using IPv6 only on interfaces that have no routable IPv4 address. + // Having a routable IPv4 address assigned is a reasonable indicator of being on a large, configured network, + // which means there's a good chance that most or all the other devices on that network should also have v4. + // By doing this we lose the ability to talk to true v6-only devices on that link, but we cut the packet rate in half. + // At this time, reducing the packet rate is more important than v6-only devices on a large configured network, + // so we are willing to make that sacrifice. - for( p = addrs; p; p = p->ifa_next ) + for( i = inMDNS->p->interfaceList; i; i = i->next ) { - if( ( p->ifa_flags & flagMask ) == flagTest ) + if( i->exists ) { - err = SetupInterface( inMDNS, p, &item ); - require_noerr( err, exit ); + mDNSBool txrx; - *next = item; - next = &item->next; + txrx = i->multicast && ( ( i->ifinfo.ip.type == mDNSAddrType_IPv4 ) || !FindRoutableIPv4( inMDNS, i->scopeID ) ); + if( i->ifinfo.McastTxRx != txrx ) + { + i->ifinfo.McastTxRx = txrx; + i->exists = 2; // 2=state change; need to de-register and re-register this interface. + } } } - err = mStatus_NoError; -exit: - if( addrs ) + // Set up the user-specified, friendly name, which is allowed to be full UTF-8. + + mDNS_snprintf( defaultName, sizeof( defaultName ), "Device-%02X:%02X:%02X:%02X:%02X:%02X", + primaryMAC.b[ 0 ], primaryMAC.b[ 1 ], primaryMAC.b[ 2 ], primaryMAC.b[ 3 ], primaryMAC.b[ 4 ], primaryMAC.b[ 5 ] ); + + MakeDomainLabelFromLiteralString( &nicelabel, "Put Nice Name Here" ); // $$$ Implementers: Fill in nice name of device. + if( nicelabel.c[ 0 ] == 0 ) MakeDomainLabelFromLiteralString( &nicelabel, defaultName ); + + // Set up the RFC 1034-compliant label. If not set or it is not RFC 1034 compliant, try the user-specified nice name. + + MakeDomainLabelFromLiteralString( &tmp, "Put-DNS-Name-Here" ); // $$$ Implementers: Fill in DNS name of device. + ConvertUTF8PstringToRFC1034HostLabel( tmp.c, &hostlabel ); + if( hostlabel.c[ 0 ] == 0 ) ConvertUTF8PstringToRFC1034HostLabel( nicelabel.c, &hostlabel ); + if( hostlabel.c[ 0 ] == 0 ) MakeDomainLabelFromLiteralString( &hostlabel, defaultName ); + + // Update our globals and mDNS with the new labels. + + if( !SameDomainLabel( inMDNS->p->userNiceLabel.c, nicelabel.c ) ) { - freeifaddrs( addrs ); + dmsg( kDebugLevelInfo, DEBUG_NAME "Updating nicelabel to \"%#s\"\n", nicelabel.c ); + inMDNS->p->userNiceLabel = nicelabel; + inMDNS->nicelabel = nicelabel; } - if( err ) + if( !SameDomainLabel( inMDNS->p->userHostLabel.c, hostlabel.c ) ) { - TearDownInterfaceList( inMDNS ); + dmsg( kDebugLevelInfo, DEBUG_NAME "Updating hostlabel to \"%#s\"\n", hostlabel.c ); + inMDNS->p->userHostLabel = hostlabel; + inMDNS->hostlabel = hostlabel; + mDNS_SetFQDN( inMDNS ); } - dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface list done (err=%ld)\n", err ); - return( err ); + return( mStatus_NoError ); } //=========================================================================================================================== -// TearDownInterfaceList +// AddInterfaceToList //=========================================================================================================================== -mDNSlocal mStatus TearDownInterfaceList( mDNS * const inMDNS ) +mDNSlocal NetworkInterfaceInfoVxWorks * AddInterfaceToList( mDNS *const inMDNS, struct ifaddrs *inIFA, mDNSs32 inUTC ) { - dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down interface list\n" ); - check( inMDNS ); + mStatus err; + mDNSAddr ip; + mDNSAddr mask; + mDNSu32 scopeID; + NetworkInterfaceInfoVxWorks ** p; + NetworkInterfaceInfoVxWorks * i; + + i = NULL; + + err = SockAddrToMDNSAddr( inIFA->ifa_addr, &ip ); + require_noerr( err, exit ); - // Tear down all the interfaces. + err = SockAddrToMDNSAddr( inIFA->ifa_netmask, &mask ); + require_noerr( err, exit ); + + // Search for an existing interface with the same info. If found, just return that one. - while( inMDNS->p->interfaceList ) + scopeID = if_nametoindex( inIFA->ifa_name ); + check( scopeID ); + for( p = &inMDNS->p->interfaceList; *p; p = &( *p )->next ) { - MDNSInterfaceItem * item; - - item = inMDNS->p->interfaceList; - inMDNS->p->interfaceList = item->next; - - TearDownInterface( inMDNS, item ); + if( ( scopeID == ( *p )->scopeID ) && mDNSSameAddress( &ip, &( *p )->ifinfo.ip ) ) + { + dmsg( kDebugLevelInfo, DEBUG_NAME "%s: Found existing interface %u with address %#a at %#p\n", __ROUTINE__, + scopeID, &ip, *p ); + ( *p )->exists = mDNStrue; + i = *p; + goto exit; + } } - dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down interface list done\n" ); - return( mStatus_NoError ); + // Allocate the new interface info and fill it out. + + i = (NetworkInterfaceInfoVxWorks *) calloc( 1, sizeof( *i ) ); + require( i, exit ); + + dmsg( kDebugLevelInfo, DEBUG_NAME "%s: Making new interface %u with address %#a at %#p\n", __ROUTINE__, scopeID, &ip, i ); + strncpy( i->ifinfo.ifname, inIFA->ifa_name, sizeof( i->ifinfo.ifname ) ); + i->ifinfo.ifname[ sizeof( i->ifinfo.ifname ) - 1 ] = '\0'; + i->ifinfo.InterfaceID = NULL; + i->ifinfo.ip = ip; + i->ifinfo.mask = mask; + i->ifinfo.Advertise = inMDNS->AdvertiseLocalAddresses; + i->ifinfo.McastTxRx = mDNSfalse; // For now; will be set up later at the end of UpdateInterfaceList. + + i->next = NULL; + i->exists = mDNStrue; + i->lastSeen = inUTC; + i->scopeID = scopeID; + i->family = inIFA->ifa_addr->sa_family; + i->multicast = ( inIFA->ifa_flags & IFF_MULTICAST ) && !( inIFA->ifa_flags & IFF_POINTOPOINT ); + + i->ss.info = i; + i->ss.sockV4 = kInvalidSocketRef; + i->ss.sockV6 = kInvalidSocketRef; + *p = i; + +exit: + return( i ); } //=========================================================================================================================== -// SetupInterface +// SetupActiveInterfaces +// +// Returns a count of non-link local IPv4 addresses registered. //=========================================================================================================================== -mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inAddr, MDNSInterfaceItem **outItem ) +#define mDNSAddressIsNonLinkLocalIPv4( X ) \ + ( ( ( X )->type == mDNSAddrType_IPv4 ) && ( ( ( X )->ip.v4.b[ 0 ] != 169 ) || ( ( X )->ip.v4.b[ 1 ] != 254 ) ) ) + +mDNSlocal int SetupActiveInterfaces( mDNS *const inMDNS, mDNSs32 inUTC ) { - mStatus err; - MDNSInterfaceItem * item; - MDNSSocketRef socketRef; - const struct sockaddr_in * ipv4, *mask; - - dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface (name=%s)\n", inAddr->ifa_name ); - check( inMDNS ); - check( inAddr ); - check( inAddr->ifa_addr ); - ipv4 = (const struct sockaddr_in *) inAddr->ifa_addr; - mask = (const struct sockaddr_in *) inAddr->ifa_netmask; - check( outItem ); - - // Allocate memory for the info item. + int count; + NetworkInterfaceInfoVxWorks * i; - item = (MDNSInterfaceItem *) calloc( 1, sizeof( *item ) ); - require_action( item, exit, err = mStatus_NoMemoryErr ); - strcpy( item->name, inAddr->ifa_name ); - item->multicastSocketRef = kInvalidSocketRef; - item->sendingSocketRef = kInvalidSocketRef; - - // Set up the multicast DNS (port 5353) socket for this interface. - - err = SetupSocket( inMDNS, inAddr, MulticastDNSPort, &socketRef ); - require_noerr( err, exit ); - item->multicastSocketRef = socketRef; + count = 0; + for( i = inMDNS->p->interfaceList; i; i = i->next ) + { + NetworkInterfaceInfo * n; + NetworkInterfaceInfoVxWorks * primary; - // Set up the sending socket for this interface. - - err = SetupSocket( inMDNS, inAddr, zeroIPPort, &socketRef ); - require_noerr( err, exit ); - item->sendingSocketRef = socketRef; - - // Register this interface with mDNS. - - item->hostSet.InterfaceID = (mDNSInterfaceID) item; - item->hostSet.ip .type = mDNSAddrType_IPv4; - item->hostSet.ip .ip.v4.NotAnInteger = ipv4->sin_addr.s_addr; - item->hostSet.mask.type = mDNSAddrType_IPv4; - item->hostSet.mask.ip.v4.NotAnInteger = mask->sin_addr.s_addr; - item->hostSet.ifname[0] = 0; - item->hostSet.Advertise = inMDNS->AdvertiseLocalAddresses; - item->hostSet.McastTxRx = mDNStrue; + if( !i->exists ) continue; + + // Search for the primary interface and sanity check it. + + n = &i->ifinfo; + primary = FindInterfaceByIndex( inMDNS, i->family, i->scopeID ); + if( !primary ) + { + dmsg( kDebugLevelError, DEBUG_NAME "%s: ERROR! didn't find %s(%u)\n", __ROUTINE__, i->ifinfo.ifname, i->scopeID ); + continue; + } + if( n->InterfaceID && ( n->InterfaceID != (mDNSInterfaceID) primary ) ) + { + dmsg( kDebugLevelError, DEBUG_NAME "%s: ERROR! n->InterfaceID %#p != primary %#p\n", __ROUTINE__, + n->InterfaceID, primary ); + n->InterfaceID = NULL; + } + + // If n->InterfaceID is set, it means we've already called mDNS_RegisterInterface() for this interface. + // so we don't need to call it again. Otherwise, register the interface with mDNS. + + if( !n->InterfaceID ) + { + mDNSBool flapping; + + n->InterfaceID = (mDNSInterfaceID) primary; + + // If lastSeen == inUTC, then this is a brand-new interface, or an interface that never went away. + // If lastSeen != inUTC, then this is an old interface, that went away for (inUTC - lastSeen) seconds. + // If it's is an old one that went away and came back in less than a minute, we're in a flapping scenario. + + flapping = ( ( inUTC - i->lastSeen ) > 0 ) && ( ( inUTC - i->lastSeen ) < 60 ); + mDNS_RegisterInterface( inMDNS, n, flapping ? mDNSPlatformOneSecond * 5 : 0 ); + if( mDNSAddressIsNonLinkLocalIPv4( &i->ifinfo.ip ) ) ++count; + + dmsg( kDebugLevelInfo, DEBUG_NAME "%s: Registered %8s(%u) InterfaceID %#p %#a%s%s\n", __ROUTINE__, + i->ifinfo.ifname, i->scopeID, primary, &n->ip, + flapping ? " (Flapping)" : "", + n->InterfaceActive ? " (Primary)" : "" ); + } + + // Set up a socket if it's not already set up. If multicast is not enabled on this interface then we + // don't need a socket since unicast traffic will be handled on the unicast socket. + + if( n->McastTxRx ) + { + mStatus err; + + if( ( ( i->family == AF_INET ) && !IsValidSocket( primary->ss.sockV4 ) ) || + ( ( i->family == AF_INET6 ) && !IsValidSocket( primary->ss.sockV6 ) ) ) + { + err = SetupSocket( inMDNS, &i->ifinfo.ip, mDNStrue, i->family, &primary->ss ); + check_noerr( err ); + } + } + else + { + dmsg( kDebugLevelInfo, DEBUG_NAME "%s: No Tx/Rx on %8s(%u) InterfaceID %#p %#a\n", __ROUTINE__, + i->ifinfo.ifname, i->scopeID, primary, &n->ip ); + } + } + return( count ); +} - err = mDNS_RegisterInterface( inMDNS, &item->hostSet, 0 ); - require_noerr( err, exit ); - item->hostRegistered = mDNStrue; - - dlog( kDebugLevelInfo, DEBUG_NAME "Registered IP address: %u.%u.%u.%u\n", - item->hostSet.ip.ip.v4.b[ 0 ], item->hostSet.ip.ip.v4.b[ 1 ], - item->hostSet.ip.ip.v4.b[ 2 ], item->hostSet.ip.ip.v4.b[ 3 ] ); - - // Success! - - *outItem = item; - item = NULL; +//=========================================================================================================================== +// MarkAllInterfacesInactive +//=========================================================================================================================== + +mDNSlocal void MarkAllInterfacesInactive( mDNS *const inMDNS, mDNSs32 inUTC ) +{ + NetworkInterfaceInfoVxWorks * i; -exit: - if( item ) + for( i = inMDNS->p->interfaceList; i; i = i->next ) { - TearDownInterface( inMDNS, item ); + if( !i->exists ) continue; + i->lastSeen = inUTC; + i->exists = mDNSfalse; } - dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface done (name=%s, err=%ld)\n", inAddr->ifa_name, err ); - return( err ); } //=========================================================================================================================== -// TearDownInterface +// ClearInactiveInterfaces +// +// Returns count of non-link local IPv4 addresses de-registered. //=========================================================================================================================== -mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, MDNSInterfaceItem *inItem ) +mDNSlocal int ClearInactiveInterfaces( mDNS *const inMDNS, mDNSs32 inUTC, mDNSBool inClosing ) { - MDNSSocketRef socketRef; - - check( inMDNS ); - check( inItem ); - - // Deregister this interface with mDNS. - - dlog( kDebugLevelInfo, DEBUG_NAME "Deregistering IP address: %u.%u.%u.%u\n", - inItem->hostSet.ip.ip.v4.b[ 0 ], inItem->hostSet.ip.ip.v4.b[ 1 ], - inItem->hostSet.ip.ip.v4.b[ 2 ], inItem->hostSet.ip.ip.v4.b[ 3 ] ); - - if( inItem->hostRegistered ) + int count; + NetworkInterfaceInfoVxWorks * i; + NetworkInterfaceInfoVxWorks ** p; + + // First pass: + // If an interface is going away, then de-register it from mDNSCore. + // We also have to de-register it if the primary interface that it's using for its InterfaceID is going away. + // We have to do this because mDNSCore will use that InterfaceID when sending packets, and if the memory + // it refers to has gone away, we'll crash. Don't actually close the sockets or free the memory yet though: + // When the last representative of an interface goes away mDNSCore may want to send goodbye packets on that + // interface. (Not yet implemented, but a good idea anyway.). + + count = 0; + for( i = inMDNS->p->interfaceList; i; i = i->next ) { - inItem->hostRegistered = mDNSfalse; - mDNS_DeregisterInterface( inMDNS, &inItem->hostSet ); + NetworkInterfaceInfoVxWorks * primary; + + // 1. If this interface is no longer active, or its InterfaceID is changing, de-register it. + + if( !i->ifinfo.InterfaceID ) continue; + primary = FindInterfaceByIndex( inMDNS, i->family, i->scopeID ); + if( ( i->exists == 0 ) || ( i->exists == 2 ) || ( i->ifinfo.InterfaceID != (mDNSInterfaceID) primary ) ) + { + dmsg( kDebugLevelInfo, DEBUG_NAME "%s: Deregistering %8s(%u) InterfaceID %#p %#a%s\n", __ROUTINE__, + i->ifinfo.ifname, i->scopeID, i->ifinfo.InterfaceID, &i->ifinfo.ip, + i->ifinfo.InterfaceActive ? " (Primary)" : "" ); + + mDNS_DeregisterInterface( inMDNS, &i->ifinfo ); + i->ifinfo.InterfaceID = NULL; + if( mDNSAddressIsNonLinkLocalIPv4( &i->ifinfo.ip ) ) ++count; + } } - // Close the multicast socket. + // Second pass: + // Now that everything that's going to de-register has done so, we can close sockets and free the memory. - socketRef = inItem->multicastSocketRef; - inItem->multicastSocketRef = kInvalidSocketRef; - if( socketRef != kInvalidSocketRef ) + p = &inMDNS->p->interfaceList; + while( *p ) { - dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down multicast socket %d\n", socketRef ); - close( socketRef ); - } + i = *p; + + // 2. Close all our sockets. We'll recreate them later as needed. + // (We may have previously had both v4 and v6, and we may not need both any more.). - // Close the sending socket. + ForgetSocket( &i->ss.sockV4 ); + ForgetSocket( &i->ss.sockV6 ); + + // 3. If no longer active, remove the interface from the list and free its memory. + + if( !i->exists ) + { + mDNSBool deleteIt; + + if( inClosing ) + { + check_string( NumCacheRecordsForInterfaceID( inMDNS, (mDNSInterfaceID) i ) == 0, "closing with in-use records!" ); + deleteIt = mDNStrue; + } + else + { + if( i->lastSeen == inUTC ) i->lastSeen = inUTC - 1; + deleteIt = ( NumCacheRecordsForInterfaceID( inMDNS, (mDNSInterfaceID) i ) == 0 ) && ( ( inUTC - i->lastSeen ) >= 60 ); + } + dmsg( kDebugLevelInfo, DEBUG_NAME "%s: %-13s %8s(%u) InterfaceID %#p %#a Age %d%s\n", __ROUTINE__, + deleteIt ? "Deleting" : "Holding", i->ifinfo.ifname, i->scopeID, i->ifinfo.InterfaceID, &i->ifinfo.ip, + inUTC - i->lastSeen, i->ifinfo.InterfaceActive ? " (Primary)" : "" ); + if( deleteIt ) + { + *p = i->next; + free( i ); + continue; + } + } + p = &i->next; + } + return( count ); +} + +//=========================================================================================================================== +// FindRoutableIPv4 +//=========================================================================================================================== + +mDNSlocal NetworkInterfaceInfoVxWorks * FindRoutableIPv4( mDNS *const inMDNS, mDNSu32 inScopeID ) +{ +#if( MDNS_EXCLUDE_IPV4_ROUTABLE_IPV6 ) + NetworkInterfaceInfoVxWorks * i; - socketRef = inItem->sendingSocketRef; - inItem->sendingSocketRef = kInvalidSocketRef; - if( socketRef != kInvalidSocketRef ) + for( i = inMDNS->p->interfaceList; i; i = i->next ) { - dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down sending socket %d\n", socketRef ); - close( socketRef ); + if( i->exists && ( i->scopeID == inScopeID ) && mDNSAddressIsNonLinkLocalIPv4( &i->ifinfo.ip ) ) + { + break; + } } + return( i ); +#else + DEBUG_UNUSED( inMDNS ); + DEBUG_UNUSED( inScopeID ); + + return( NULL ); +#endif +} + +//=========================================================================================================================== +// FindInterfaceByIndex +//=========================================================================================================================== + +mDNSlocal NetworkInterfaceInfoVxWorks * FindInterfaceByIndex( mDNS *const inMDNS, int inFamily, mDNSu32 inIndex ) +{ + NetworkInterfaceInfoVxWorks * i; - // Free the memory used by the interface info. + check( inIndex != 0 ); - free( inItem ); - return( mStatus_NoError ); + for( i = inMDNS->p->interfaceList; i; i = i->next ) + { + if( i->exists && ( i->scopeID == inIndex ) && + ( MDNS_AAAA_OVER_IPV4 || + ( ( inFamily == AF_INET ) && ( i->ifinfo.ip.type == mDNSAddrType_IPv4 ) ) || + ( ( inFamily == AF_INET6 ) && ( i->ifinfo.ip.type == mDNSAddrType_IPv6 ) ) ) ) + { + return( i ); + } + } + return( NULL ); } //=========================================================================================================================== // SetupSocket //=========================================================================================================================== -mDNSlocal mStatus - SetupSocket( - mDNS * const inMDNS, - const struct ifaddrs * inAddr, - mDNSIPPort inPort, - MDNSSocketRef * outSocketRef ) +mDNSlocal mStatus SetupSocket( mDNS *const inMDNS, const mDNSAddr *inAddr, mDNSBool inMcast, int inFamily, SocketSet *inSS ) { - mStatus err; - MDNSSocketRef socketRef; - int option; - unsigned char optionByte; - struct ip_mreq mreq; - const struct sockaddr_in * ipv4; - struct sockaddr_in addr; - mDNSv4Addr ip; - - dlog( kDebugLevelVerbose, DEBUG_NAME "setting up socket done\n" ); - check( inMDNS ); + mStatus err; + SocketRef * sockPtr; + mDNSIPPort port; + SocketRef sock; + const int on = 1; + check( inAddr ); - check( inAddr->ifa_addr ); - ipv4 = (const struct sockaddr_in *) inAddr->ifa_addr; - check( outSocketRef ); + check( inSS ); - // Set up a UDP socket for multicast DNS. + sockPtr = ( inFamily == AF_INET ) ? &inSS->sockV4 : &inSS->sockV6; + port = ( inMcast || inMDNS->CanReceiveUnicastOn5353 ) ? MulticastDNSPort : zeroIPPort; - socketRef = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); - require_errno_action( socketRef, errno, exit, err = mStatus_UnknownErr ); - - // A port of zero means this socket is for sending and should be set up for sending. Otherwise, it is for receiving - // and should be set up for receiving. The reason for separate sending vs receiving sockets is to workaround problems - // with VxWorks IP stack when using dynamic IP configuration such as DHCP (problems binding to wildcard IP when the - // IP address later changes). Since we have to bind the Multicast DNS address to workaround these issues we have to - // use a separate sending socket since it is illegal to send a packet with a multicast source address (RFC 1122). + sock = socket( inFamily, SOCK_DGRAM, IPPROTO_UDP ); + err = translate_errno( IsValidSocket( sock ), errno_compat(), mStatus_UnknownErr ); + require_noerr( err, exit ); - if( inPort.NotAnInteger != zeroIPPort.NotAnInteger ) + // Allow multiple listeners if this is a multicast port. + + if( port.NotAnInteger ) { - // Turn on reuse port option so multiple servers can listen for Multicast DNS packets. + err = setsockopt( sock, SOL_SOCKET, SO_REUSEPORT, (char *) &on, sizeof( on ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + } + + // Set up the socket based on the family (IPv4 or IPv6). + + if( inFamily == AF_INET ) + { + const int ttlV4 = 255; + const u_char ttlV4Mcast = 255; + struct sockaddr_in sa4; - option = 1; - err = setsockopt( socketRef, SOL_SOCKET, SO_REUSEADDR, (char *) &option, sizeof( option ) ); - check_errno( err, errno ); + // Receive destination addresses so we know which address the packet was sent to. - // Join the all-DNS multicast group so we receive Multicast DNS packets. + err = setsockopt( sock, IPPROTO_IP, IP_RECVDSTADDR, (char *) &on, sizeof( on ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); - ip.NotAnInteger = ipv4->sin_addr.s_addr; - mreq.imr_multiaddr.s_addr = AllDNSLinkGroupv4.NotAnInteger; - mreq.imr_interface.s_addr = ip.NotAnInteger; - err = setsockopt( socketRef, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof( mreq ) ); - check_errno( err, errno ); + // Receive interface indexes so we know which interface received the packet. - // Bind to the multicast DNS address and port 5353. + err = setsockopt( sock, IPPROTO_IP, IP_RECVIF, (char *) &on, sizeof( on ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); - memset( &addr, 0, sizeof( addr ) ); - addr.sin_family = AF_INET; - addr.sin_port = inPort.NotAnInteger; - addr.sin_addr.s_addr = AllDNSLinkGroupv4.NotAnInteger; - err = bind( socketRef, (struct sockaddr *) &addr, sizeof( addr ) ); - check_errno( err, errno ); + // Join the multicast group on this interface and specify the outgoing interface, if it's for multicast receiving. - dlog( kDebugLevelVerbose, DEBUG_NAME "setting up socket done (%s, %u.%u.%u.%u:%u, %d)\n", - inAddr->ifa_name, ip.b[ 0 ], ip.b[ 1 ], ip.b[ 2 ], ip.b[ 3 ], ntohs( inPort.NotAnInteger ), socketRef ); + if( inMcast ) + { + struct in_addr addrV4; + struct ip_mreq mreqV4; + + addrV4.s_addr = inAddr->ip.v4.NotAnInteger; + mreqV4.imr_multiaddr.s_addr = AllDNSLinkGroupv4.NotAnInteger; + mreqV4.imr_interface = addrV4; + err = setsockopt( sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreqV4, sizeof( mreqV4 ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_IF, (char *) &addrV4, sizeof( addrV4 ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + } + + // Send unicast packets with TTL 255 (helps against spoofing). + + err = setsockopt( sock, IPPROTO_IP, IP_TTL, (char *) &ttlV4, sizeof( ttlV4 ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + // Send multicast packets with TTL 255 (helps against spoofing). + + err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &ttlV4Mcast, sizeof( ttlV4Mcast ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + // Start listening for packets. + + memset( &sa4, 0, sizeof( sa4 ) ); + sa4.sin_len = sizeof( sa4 ); + sa4.sin_family = AF_INET; + sa4.sin_port = port.NotAnInteger; + sa4.sin_addr.s_addr = htonl( INADDR_ANY ); // We want to receive multicasts AND unicasts on this socket. + err = bind( sock, (struct sockaddr *) &sa4, sizeof( sa4 ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); } - else + else if( inFamily == AF_INET6 ) { - // Bind to the interface address and multicast DNS port. + struct sockaddr_in6 sa6; + const int ttlV6 = 255; - ip.NotAnInteger = ipv4->sin_addr.s_addr; - memset( &addr, 0, sizeof( addr ) ); - addr.sin_family = AF_INET; - addr.sin_port = MulticastDNSPort.NotAnInteger; - addr.sin_addr.s_addr = ip.NotAnInteger; - err = bind( socketRef, (struct sockaddr *) &addr, sizeof( addr ) ); - check_errno( err, errno ); + // Receive destination addresses and interface index so we know where the packet was received and intended. - // Direct multicast packets to the specified interface. + err = setsockopt( sock, IPPROTO_IPV6, IPV6_PKTINFO, (char *) &on, sizeof( on ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); - addr.sin_addr.s_addr = ip.NotAnInteger; - err = setsockopt( socketRef, IPPROTO_IP, IP_MULTICAST_IF, (char *) &addr.sin_addr, sizeof( addr.sin_addr ) ); - check_errno( err, errno ); + // Receive only IPv6 packets because otherwise, we may get IPv4 addresses as IPv4-mapped IPv6 addresses. - // Set the TTL of outgoing unicast packets to 255 (helps against spoofing). + err = setsockopt( sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &on, sizeof( on ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); - option = 255; - err = setsockopt( socketRef, IPPROTO_IP, IP_TTL, (char *) &option, sizeof( option ) ); - check_errno( err, errno ); + // Join the multicast group on this interface and specify the outgoing interface, if it's for multicast receiving. - // Set the TTL of outgoing multicast packets to 255 (helps against spoofing). + if( inMcast ) + { + u_int ifindex; + struct ipv6_mreq mreqV6; + + ifindex = inSS->info->scopeID; + mreqV6.ipv6mr_interface = ifindex; + mreqV6.ipv6mr_multiaddr = *( (struct in6_addr * ) &AllDNSLinkGroupv6 ); + err = setsockopt( sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *) &mreqV6, sizeof( mreqV6 ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *) &ifindex, sizeof( ifindex ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + } - optionByte = 255; - err = setsockopt( socketRef, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &optionByte, sizeof( optionByte ) ); - check_errno( err, errno ); + // Send unicast packets with TTL 255 (helps against spoofing). - // WARNING: Setting this option causes unicast responses to be routed to the wrong interface so they are - // WARNING: disabled. These options were only hints to improve 802.11 performance (and not implemented) anyway. + err = setsockopt( sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *) &ttlV6, sizeof( ttlV6 ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); -#if 0 - // Mark packets as high-throughput/low-delay (i.e. lowest reliability) to maximize 802.11 multicast rate. + // Send multicast packets with TTL 255 (helps against spoofing). - option = IPTOS_LOWDELAY | IPTOS_THROUGHPUT; - err = setsockopt( socketRef, IPPROTO_IP, IP_TOS, (char *) &option, sizeof( option ) ); - check_errno( err, errno ); -#endif - - dlog( kDebugLevelVerbose, DEBUG_NAME "setting up sending socket done (%s, %u.%u.%u.%u, %d)\n", - inAddr->ifa_name, ip.b[ 0 ], ip.b[ 1 ], ip.b[ 2 ], ip.b[ 3 ], socketRef ); + err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *) &ttlV6, sizeof( ttlV6 ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + // Receive our own packets for same-machine operation. + + err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &on, sizeof( on ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + // Start listening for packets. + + memset( &sa6, 0, sizeof( sa6 ) ); + sa6.sin6_len = sizeof( sa6 ); + sa6.sin6_family = AF_INET6; + sa6.sin6_port = port.NotAnInteger; + sa6.sin6_flowinfo = 0; + sa6.sin6_addr = in6addr_any; // We want to receive multicasts AND unicasts on this socket. + sa6.sin6_scope_id = 0; + err = bind( sock, (struct sockaddr *) &sa6, sizeof( sa6 ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + } + else + { + dmsg( kDebugLevelError, DEBUG_NAME "%s: unsupport socket family (%d)\n", __ROUTINE__, inFamily ); + err = kUnsupportedErr; + goto exit; } - // Success! + // Make the socket non-blocking so we can potentially get multiple packets per select call. - *outSocketRef = socketRef; - socketRef = kInvalidSocketRef; + err = ioctl( sock, FIONBIO, (int) &on ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + *sockPtr = sock; + sock = kInvalidSocketRef; err = mStatus_NoError; exit: - if( socketRef != kInvalidSocketRef ) + if( IsValidSocket( sock ) ) close_compat( sock ); + return( err ); +} + +//=========================================================================================================================== +// SockAddrToMDNSAddr +//=========================================================================================================================== + +mDNSlocal mStatus SockAddrToMDNSAddr( const struct sockaddr * const inSA, mDNSAddr *outIP ) +{ + mStatus err; + + check( inSA ); + check( outIP ); + + if( inSA->sa_family == AF_INET ) + { + struct sockaddr_in * sa4; + + sa4 = (struct sockaddr_in *) inSA; + outIP->type = mDNSAddrType_IPv4; + outIP->ip.v4.NotAnInteger = sa4->sin_addr.s_addr; + err = mStatus_NoError; + } + else if( inSA->sa_family == AF_INET6 ) + { + struct sockaddr_in6 * sa6; + + sa6 = (struct sockaddr_in6 *) inSA; + outIP->type = mDNSAddrType_IPv6; + outIP->ip.v6 = *( (mDNSv6Addr *) &sa6->sin6_addr ); + if( IN6_IS_ADDR_LINKLOCAL( &sa6->sin6_addr ) ) outIP->ip.v6.w[ 1 ] = 0; + err = mStatus_NoError; + } + else { - close( socketRef ); + dmsg( kDebugLevelError, DEBUG_NAME "%s: invalid sa_family (%d)\n", __ROUTINE__, inSA->sa_family ); + err = mStatus_BadParamErr; } return( err ); } @@ -1312,19 +1503,13 @@ exit: mDNSlocal mStatus SetupCommandPipe( mDNS * const inMDNS ) { mStatus err; + + err = pipeDevCreate( "/pipe/mDNS", 32, 1 ); + check_translated_errno( err == 0, errno_compat(), kUnknownErr ); - // Clean up any leftover command pipe. - - TearDownCommandPipe( inMDNS ); - - // Create the pipe device and open it. - - pipeDevCreate( kMDNSPipeName, kMDNSPipeMessageQueueSize, kMDNSPipeMessageSize ); - - inMDNS->p->commandPipe = open( kMDNSPipeName, O_RDWR, 0 ); - require_errno_action( inMDNS->p->commandPipe, errno, exit, err = mStatus_UnsupportedErr ); - - err = mStatus_NoError; + inMDNS->p->commandPipe = open( "/pipe/mDNS", O_RDWR, 0 ); + err = translate_errno( inMDNS->p->commandPipe != ERROR, errno_compat(), mStatus_UnsupportedErr ); + require_noerr( err, exit ); exit: return( err ); @@ -1336,15 +1521,17 @@ exit: mDNSlocal mStatus TearDownCommandPipe( mDNS * const inMDNS ) { + mStatus err; + if( inMDNS->p->commandPipe != ERROR ) { - close( inMDNS->p->commandPipe ); -#ifdef _WRS_VXWORKS_5_X - // pipeDevDelete is not defined in older versions of VxWorks - pipeDevDelete( kMDNSPipeName, FALSE ); -#endif + err = close( inMDNS->p->commandPipe ); + check_translated_errno( err == 0, errno_compat(), kUnknownErr ); inMDNS->p->commandPipe = ERROR; - } + + err = pipeDevDelete( "/pipe/mDNS", FALSE ); + check_translated_errno( err == 0, errno_compat(), kUnknownErr ); + } return( mStatus_NoError ); } @@ -1356,12 +1543,11 @@ mDNSlocal mStatus SendCommand( const mDNS * const inMDNS, MDNSPipeCommandCode in { mStatus err; - require_action( inMDNS->p->commandPipe != ERROR, exit, err = mStatus_NotInitializedErr ); + require_action_quiet( inMDNS->p->commandPipe != ERROR, exit, err = mStatus_NotInitializedErr ); err = write( inMDNS->p->commandPipe, &inCommandCode, sizeof( inCommandCode ) ); - require_errno( err, errno, exit ); - - err = mStatus_NoError; + err = translate_errno( err >= 0, errno_compat(), kWriteErr ); + require_noerr( err, exit ); exit: return( err ); @@ -1374,241 +1560,129 @@ exit: mDNSlocal mStatus ProcessCommand( mDNS * const inMDNS ) { mStatus err; - MDNSPipeCommandCode commandCode; - - require_action( inMDNS->p->commandPipe != ERROR, exit, err = mStatus_NotInitializedErr ); - - // Read the command code from the pipe and dispatch it. - - err = read( inMDNS->p->commandPipe, &commandCode, sizeof( commandCode ) ); - require_errno( err, errno, exit ); + MDNSPipeCommandCode cmd; + mDNSs32 utc; + + err = read( inMDNS->p->commandPipe, &cmd, sizeof( cmd ) ); + err = translate_errno( err >= 0, errno_compat(), kReadErr ); + require_noerr( err, exit ); - switch( commandCode ) + switch( cmd ) { - case kMDNSPipeCommandCodeReschedule: - - // Reschedule event. Do nothing here, but this will cause mDNS_Execute to run before waiting again. - - dlog( kDebugLevelChatty, DEBUG_NAME "reschedule\n" ); - break; - - case kMDNSPipeCommandCodeReconfigure: - ProcessCommandReconfigure( inMDNS ); + case kMDNSPipeCommandCodeReschedule: // Reschedule: just break out to re-run mDNS_Execute. break; - case kMDNSPipeCommandCodeQuit: - - // Quit requested. Set quit flag and bump the config ID to let the thread exit normally. + case kMDNSPipeCommandCodeReconfigure: // Reconfigure: rebuild the interface list after a config change. + dmsg( kDebugLevelInfo, DEBUG_NAME "*** NETWORK CONFIGURATION CHANGE ***\n" ); + mDNSPlatformLock( inMDNS ); - dlog( kDebugLevelVerbose, DEBUG_NAME "processing pipe quit command\n" ); - inMDNS->p->quit = mDNStrue; - ++inMDNS->p->configID; - break; - - default: - dlog( kDebugLevelError, DEBUG_NAME "unknown pipe command code (code=0x%08X)\n", commandCode ); - err = mStatus_BadParamErr; - goto exit; + utc = mDNSPlatformUTC(); + MarkAllInterfacesInactive( inMDNS, utc ); + UpdateInterfaceList( inMDNS, utc ); + ClearInactiveInterfaces( inMDNS, utc, mDNSfalse ); + SetupActiveInterfaces( inMDNS, utc ); + + mDNSPlatformUnlock( inMDNS ); + if( inMDNS->MainCallback ) inMDNS->MainCallback( inMDNS, mStatus_ConfigChanged ); break; + + case kMDNSPipeCommandCodeQuit: // Quit: just set a flag so the task exits cleanly. + inMDNS->p->quit = mDNStrue; + break; + + default: + dmsg( kDebugLevelError, DEBUG_NAME "unknown pipe command (%d)\n", cmd ); + err = mStatus_BadParamErr; + goto exit; } - err = mStatus_NoError; - -exit: - return( err ); -} - -//=========================================================================================================================== -// ProcessCommandReconfigure -//=========================================================================================================================== - -mDNSlocal void ProcessCommandReconfigure( mDNS *inMDNS ) -{ - mStatus err; - - dlog( kDebugLevelVerbose, DEBUG_NAME "processing pipe reconfigure command\n" ); - - // Tear down the existing interfaces and set up new ones using the new IP info. - - mDNSPlatformLock( inMDNS ); - - err = TearDownInterfaceList( inMDNS ); - check_noerr( err ); - - err = SetupInterfaceList( inMDNS ); - check_noerr( err ); - - mDNSPlatformUnlock( inMDNS ); - - // Inform clients of the change. - - if( inMDNS->MainCallback ) - { - inMDNS->MainCallback( inMDNS, mStatus_ConfigChanged ); - } - - // Force mDNS to update. - - mDNSCoreMachineSleep( inMDNS, mDNSfalse ); - - // Bump the config ID so the main processing loop detects the configuration change. - - ++inMDNS->p->configID; -} - -#if 0 -#pragma mark - -#pragma mark == Threads == -#endif - -//=========================================================================================================================== -// SetupTask -//=========================================================================================================================== - -mDNSlocal mStatus SetupTask( mDNS * const inMDNS ) -{ - mStatus err; - int task; - - dlog( kDebugLevelVerbose, DEBUG_NAME "setting up thread\n" ); - check( inMDNS ); - - // Create our main thread. Note: The task will save off its ID in the globals. We cannot do it here because the - // task invokes code that needs it and the task may begin execution before taskSpawn returns the task ID. - // This also means code in this thread context cannot rely on the task ID until the task has fully initialized. - - task = taskSpawn( kMDNSTaskName, kMDNSTaskPriority, 0, kMDNSTaskStackSize, (FUNCPTR) Task, - (int) inMDNS, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); - require_action( task != ERROR, exit, err = mStatus_NoMemoryErr ); - - err = mStatus_NoError; - -exit: - dlog( kDebugLevelVerbose, DEBUG_NAME "setting up thread done (err=%ld, id=%d)\n", err, task ); - return( err ); -} - -//=========================================================================================================================== -// TearDownTask -//=========================================================================================================================== - -mDNSlocal mStatus TearDownTask( mDNS * const inMDNS ) -{ - mStatus err; - - dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down thread\n" ); - check( inMDNS ); - - // Send a quit command to cause the thread to exit. - - SendCommand( inMDNS, kMDNSPipeCommandCodeQuit ); - - // Wait for the thread to signal it has exited. Timeout in 10 seconds to handle a hung thread. - - if( inMDNS->p->quitEvent ) - { - err = semTake( inMDNS->p->quitEvent, sysClkRateGet() * 10 ); - check_noerr( err ); - } - err = mStatus_NoError; - dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down thread done (err=%ld)\n", err ); +exit: return( err ); } +#if 0 +#pragma mark - +#pragma mark == Threads == +#endif + //=========================================================================================================================== // Task //=========================================================================================================================== mDNSlocal void Task( mDNS *inMDNS ) { - mStatus err; - fd_set allReadSet; - MDNSInterfaceItem * item; - int maxSocket; - long configID; - struct timeval timeout; + mStatus err; + mDNSs32 nextEvent; + fd_set readSet; + int maxFd; + struct timeval timeout; + NetworkInterfaceInfoVxWorks * i; + int fd; + int n; - dlog( kDebugLevelVerbose, DEBUG_NAME "task starting\n" ); check( inMDNS ); - // Set up everything up. - err = TaskInit( inMDNS ); require_noerr( err, exit ); - // Main Processing Loop. - while( !inMDNS->p->quit ) { - // Set up the read set here to avoid the overhead of setting it up each iteration of the main processing loop. - // If the configuration changes, the server ID will be bumped, causing this code to set up the read set again. + // Let mDNSCore do its work then wait for an event. On idle timeouts (n == 0), just loop back to mDNS_Exceute. - TaskSetupReadSet( inMDNS, &allReadSet, &maxSocket ); - configID = inMDNS->p->configID; - dlog( kDebugLevelVerbose, DEBUG_NAME "task starting processing loop (configID=%ld)\n", configID ); + nextEvent = mDNS_Execute( inMDNS ); + TaskSetupSelect( inMDNS, &readSet, &maxFd, nextEvent, &timeout ); + n = select( maxFd + 1, &readSet, NULL, NULL, &timeout ); + check_translated_errno( n >= 0, errno_compat(), kUnknownErr ); + if( n == 0 ) continue; - while( configID == inMDNS->p->configID ) + // Process interface-specific sockets with pending data. + + n = 0; + for( i = inMDNS->p->interfaceList; i; i = i->next ) { - mDNSs32 nextTaskTime; - fd_set readSet; - int n; - - // Give the mDNS core a chance to do its work. Reset the rescheduled flag before calling mDNS_Execute - // so anything that needs processing during or after causes a re-schedule to wake up the thread. The - // reschedule flag is set to 1 after processing a waking up to prevent redundant reschedules while - // processing packets. This introduces a window for a race condition because the thread wake-up and - // reschedule set are not atomic, but this would be benign. Even if the reschedule flag is "corrupted" - // like this, it would only result in a redundant reschedule since it will loop back to mDNS_Execute. - - inMDNS->p->rescheduled = 0; - nextTaskTime = mDNS_Execute( inMDNS ); - TaskSetupTimeout( inMDNS, nextTaskTime, &timeout ); - - // Wait until something occurs (e.g. command, incoming packet, or timeout). - - readSet = allReadSet; - n = select( maxSocket + 1, &readSet, NULL, NULL, &timeout ); - inMDNS->p->rescheduled = 1; - check_errno( n, errno ); - dlog( kDebugLevelChatty - 1, DEBUG_NAME "task select result = %d\n", n ); - if( n == 0 ) - { - // Next task timeout occurred. Loop back up to give mDNS core a chance to work. - - dlog( kDebugLevelChatty, DEBUG_NAME "next task timeout occurred (%ld)\n", mDNS_TimeNow(inMDNS) ); - continue; - } - - // Scan the read set to determine if any sockets have something pending and process them. - - n = 0; - for( item = inMDNS->p->interfaceList; item; item = item->next ) + fd = i->ss.sockV4; + if( IsValidSocket( fd ) && FD_ISSET( fd, &readSet ) ) { - if( FD_ISSET( item->multicastSocketRef, &readSet ) ) - { - TaskProcessPacket( inMDNS, item, item->multicastSocketRef ); - ++n; - } + TaskProcessPackets( inMDNS, &i->ss, fd ); + ++n; } - - // Check for a pending command and process it. - - if( FD_ISSET( inMDNS->p->commandPipe, &readSet ) ) + fd = i->ss.sockV6; + if( IsValidSocket( fd ) && FD_ISSET( fd, &readSet ) ) { - ProcessCommand( inMDNS ); + TaskProcessPackets( inMDNS, &i->ss, fd ); ++n; } - check( n > 0 ); } + + // Process unicast sockets with pending data. + + fd = inMDNS->p->unicastSS.sockV4; + if( IsValidSocket( fd ) && FD_ISSET( fd, &readSet ) ) + { + TaskProcessPackets( inMDNS, &inMDNS->p->unicastSS, fd ); + ++n; + } + fd = inMDNS->p->unicastSS.sockV6; + if( IsValidSocket( fd ) && FD_ISSET( fd, &readSet ) ) + { + TaskProcessPackets( inMDNS, &inMDNS->p->unicastSS, fd ); + ++n; + } + + // Processing pending commands. + + fd = inMDNS->p->commandPipe; + check( fd >= 0 ); + if( FD_ISSET( fd, &readSet ) ) + { + ProcessCommand( inMDNS ); + ++n; + } + check_string( n > 0, "select said something was readable, but nothing was" ); } exit: - // Signal we've quit. - - check( inMDNS->p->quitEvent ); - semGive( inMDNS->p->quitEvent ); - - dlog( kDebugLevelInfo, DEBUG_NAME "task ended\n" ); + TaskTerm( inMDNS ); } //=========================================================================================================================== @@ -1617,86 +1691,155 @@ exit: mDNSlocal mStatus TaskInit( mDNS *inMDNS ) { - mStatus err; - - dlog( kDebugLevelVerbose, DEBUG_NAME "task init\n" ); - check( inMDNS->p->readyEvent ); + mStatus err; + mDNSs32 utc; + socklen_t len; - inMDNS->p->task = taskIdSelf(); + inMDNS->p->taskID = taskIdSelf(); err = SetupCommandPipe( inMDNS ); require_noerr( err, exit ); - SetupNames( inMDNS ); + inMDNS->CanReceiveUnicastOn5353 = mDNStrue; - err = SetupInterfaceList( inMDNS ); - require_noerr( err, exit ); + // Set up the HINFO string using the description property (e.g. "Device V1.0"). + + inMDNS->HIHardware.c[ 0 ] = 11; + memcpy( &inMDNS->HIHardware.c[ 1 ], "Device V1.0", inMDNS->HIHardware.c[ 0 ] ); // $$$ Implementers: Fill in real info. + + // Set up the unicast sockets. + + err = SetupSocket( inMDNS, &zeroAddr, mDNSfalse, AF_INET, &inMDNS->p->unicastSS ); + check_noerr( err ); + if( err == mStatus_NoError ) + { + struct sockaddr_in sa4; + + len = sizeof( sa4 ); + err = getsockname( inMDNS->p->unicastSS.sockV4, (struct sockaddr *) &sa4, &len ); + check_translated_errno( err == 0, errno_compat(), kUnknownErr ); + if( err == 0 ) inMDNS->UnicastPort4.NotAnInteger = sa4.sin_port; + } + + err = SetupSocket( inMDNS, &zeroAddr, mDNSfalse, AF_INET6, &inMDNS->p->unicastSS ); + check_noerr( err ); + if( err == mStatus_NoError ) + { + struct sockaddr_in6 sa6; + + len = sizeof( sa6 ); + err = getsockname( inMDNS->p->unicastSS.sockV6, (struct sockaddr *) &sa6, &len ); + check_translated_errno( err == 0, errno_compat(), kUnknownErr ); + if( err == 0 ) inMDNS->UnicastPort6.NotAnInteger = sa6.sin6_port; + } + + // Set up the interfaces. + + utc = mDNSPlatformUTC(); + UpdateInterfaceList( inMDNS, utc ); + SetupActiveInterfaces( inMDNS, utc ); + err = mStatus_NoError; exit: // Signal the "ready" semaphore to indicate the task initialization code has completed (success or not). - inMDNS->p->taskInitErr = err; - semGive( inMDNS->p->readyEvent ); - - dlog( kDebugLevelVerbose, DEBUG_NAME "task init done (err=%ld)\n", err ); + inMDNS->p->initErr = err; + semGive( inMDNS->p->initEvent ); return( err ); } //=========================================================================================================================== -// TaskSetupReadSet +// TaskTerm //=========================================================================================================================== -mDNSlocal void TaskSetupReadSet( mDNS *inMDNS, fd_set *outReadSet, int *outMaxSocket ) +mDNSlocal void TaskTerm( mDNS *inMDNS ) { - MDNSInterfaceItem * item; - int maxSocket; + mStatus err; + mDNSs32 utc; - dlog( kDebugLevelVerbose, DEBUG_NAME "task setting up read set\n" ); - check( inMDNS ); - check( outReadSet ); - check( outMaxSocket ); + // Tear down all interfaces. + + utc = mDNSPlatformUTC(); + MarkAllInterfacesInactive( inMDNS, utc ); + ClearInactiveInterfaces( inMDNS, utc, mDNStrue ); + check_string( !inMDNS->p->interfaceList, "LEAK: closing without deleting all interfaces" ); + + // Close unicast sockets. + + ForgetSocket( &inMDNS->p->unicastSS.sockV4); + ForgetSocket( &inMDNS->p->unicastSS.sockV6 ); + + // Tear down everything else that was set up in TaskInit then signal back that we're done. + + err = TearDownCommandPipe( inMDNS ); + check_noerr( err ); - // Initialize the read set. Default the max socket to -1 so "maxSocket + 1" (as needed by select) is zero. This - // should never happen since we should always have at least one interface, but it's just to be safe. + err = semGive( inMDNS->p->quitEvent ); + check_translated_errno( err == 0, errno_compat(), kUnknownErr ); +} + +//=========================================================================================================================== +// TaskSetupSelect +//=========================================================================================================================== + +mDNSlocal void TaskSetupSelect( mDNS *inMDNS, fd_set *outSet, int *outMaxFd, mDNSs32 inNextEvent, struct timeval *outTimeout ) +{ + NetworkInterfaceInfoVxWorks * i; + int maxFd; + int fd; + mDNSs32 delta; - FD_ZERO( outReadSet ); - maxSocket = -1; + FD_ZERO( outSet ); + maxFd = -1; - // Add all the receiving sockets to the read set. + // Add the interface-specific sockets. - for( item = inMDNS->p->interfaceList; item; item = item->next ) + for( i = inMDNS->p->interfaceList; i; i = i->next ) { - FD_SET( item->multicastSocketRef, outReadSet ); - if( item->multicastSocketRef > maxSocket ) + fd = i->ss.sockV4; + if( IsValidSocket( fd ) ) + { + FD_SET( fd, outSet ); + if( fd > maxFd ) maxFd = fd; + } + + fd = i->ss.sockV6; + if( IsValidSocket( fd ) ) { - maxSocket = item->multicastSocketRef; + FD_SET( fd, outSet ); + if( fd > maxFd ) maxFd = fd; } } - // Add the command pipe to the read set. + // Add the unicast sockets. - FD_SET( inMDNS->p->commandPipe, outReadSet ); - if( inMDNS->p->commandPipe > maxSocket ) + fd = inMDNS->p->unicastSS.sockV4; + if( IsValidSocket( fd ) ) { - maxSocket = inMDNS->p->commandPipe; + FD_SET( fd, outSet ); + if( fd > maxFd ) maxFd = fd; } - check( maxSocket > 0 ); - *outMaxSocket = maxSocket; - dlog( kDebugLevelVerbose, DEBUG_NAME "task setting up read set done (maxSocket=%d)\n", maxSocket ); -} - -//=========================================================================================================================== -// TaskSetupTimeout -//=========================================================================================================================== - -mDNSlocal void TaskSetupTimeout( mDNS *inMDNS, mDNSs32 inNextTaskTime, struct timeval *outTimeout ) -{ - mDNSs32 delta; + fd = inMDNS->p->unicastSS.sockV6; + if( IsValidSocket( fd ) ) + { + FD_SET( fd, outSet ); + if( fd > maxFd ) maxFd = fd; + } + + // Add the command pipe. + + fd = inMDNS->p->commandPipe; + check( fd >= 0 ); + FD_SET( fd, outSet ); + if( fd > maxFd ) maxFd = fd; + + check( maxFd > 0 ); + *outMaxFd = maxFd; // Calculate how long to wait before performing idle processing. - delta = inNextTaskTime - mDNS_TimeNow(inMDNS); + delta = inNextEvent - mDNS_TimeNow( inMDNS ); if( delta <= 0 ) { // The next task time is now or in the past. Set the timeout to fire immediately. @@ -1709,345 +1852,195 @@ mDNSlocal void TaskSetupTimeout( mDNS *inMDNS, mDNSs32 inNextTaskTime, struct ti // Calculate the seconds and microseconds until the timeout should occur. Add one to the ticks remainder // before multiplying to account for integer rounding error and avoid firing the timeout too early. - outTimeout->tv_sec = delta / mDNSPlatformOneSecond; - outTimeout->tv_usec = ( ( delta % mDNSPlatformOneSecond ) + 1 ) * gMDNSTicksToMicrosecondsMultiplier; - - // Check if the microseconds is more than 1 second. If so, bump the seconds instead. - + outTimeout->tv_sec = delta / mDNSPlatformOneSecond; + outTimeout->tv_usec = ( ( delta % mDNSPlatformOneSecond ) + 1 ) * gMDNSTicksToMicro; if( outTimeout->tv_usec >= 1000000L ) { outTimeout->tv_sec += 1; outTimeout->tv_usec = 0; } } - - dlog( kDebugLevelChatty, DEBUG_NAME "next task in %ld:%ld seconds (%ld)\n", - outTimeout->tv_sec, outTimeout->tv_usec, inNextTaskTime ); -} -//=========================================================================================================================== -// TaskProcessPacket -//=========================================================================================================================== - -mDNSlocal void TaskProcessPacket( mDNS *inMDNS, MDNSInterfaceItem *inItem, MDNSSocketRef inSocketRef ) -{ - int n; - DNSMessage packet; - struct sockaddr_in addr; - int addrSize; - mDNSu8 * packetEndPtr; - mDNSAddr srcAddr; - mDNSIPPort srcPort; - mDNSAddr dstAddr; - mDNSIPPort dstPort; - - // Receive the packet. - - addrSize = sizeof( addr ); - n = recvfrom( inSocketRef, (char *) &packet, sizeof( packet ), 0, (struct sockaddr *) &addr, &addrSize ); - check( n >= 0 ); - if( n >= 0 ) - { - // Set up the src/dst/interface info. - - srcAddr.type = mDNSAddrType_IPv4; - srcAddr.ip.v4.NotAnInteger = addr.sin_addr.s_addr; - srcPort.NotAnInteger = addr.sin_port; - dstAddr.type = mDNSAddrType_IPv4; - dstAddr.ip.v4 = AllDNSLinkGroupv4; - dstPort = MulticastDNSPort; - - dlog( kDebugLevelChatty, DEBUG_NAME "packet received\n" ); - dlog( kDebugLevelChatty, DEBUG_NAME " size = %d\n", n ); - dlog( kDebugLevelChatty, DEBUG_NAME " src = %u.%u.%u.%u:%hu\n", - srcAddr.ip.v4.b[ 0 ], srcAddr.ip.v4.b[ 1 ], srcAddr.ip.v4.b[ 2 ], srcAddr.ip.v4.b[ 3 ], - ntohs( srcPort.NotAnInteger ) ); - dlog( kDebugLevelChatty, DEBUG_NAME " dst = %u.%u.%u.%u:%hu\n", - dstAddr.ip.v4.b[ 0 ], dstAddr.ip.v4.b[ 1 ], dstAddr.ip.v4.b[ 2 ], dstAddr.ip.v4.b[ 3 ], - ntohs( dstPort.NotAnInteger ) ); - dlog( kDebugLevelChatty, DEBUG_NAME " interface = 0x%08X\n", (int) inItem->hostSet.InterfaceID ); - dlog( kDebugLevelChatty, DEBUG_NAME "--\n" ); - - // Dispatch the packet to mDNS. - - packetEndPtr = ( (mDNSu8 *) &packet ) + n; - mDNSCoreReceive( inMDNS, &packet, packetEndPtr, &srcAddr, srcPort, &dstAddr, dstPort, inItem->hostSet.InterfaceID ); - } - - // Update counters. - - inItem->recvCounter += 1; - inItem->recvErrorCounter += ( n < 0 ); -} - -#if 0 -#pragma mark - -#pragma mark == Utilities == -#endif - -#if( TARGET_NON_APPLE ) -//=========================================================================================================================== -// GenerateUniqueHostName -// -// Non-Apple platform stub routine to generate a unique name for the device. Should be implemented to return a unique name. -//=========================================================================================================================== - -mDNSlocal void GenerateUniqueHostName( char *outName, long *ioSeed ) -{ - DEBUG_UNUSED( ioSeed ); - - // $$$ Non-Apple Platforms: Fill in appropriate name for device. - - mDNSPlatformStrCopy( kMDNSDefaultName, outName ); -} - -//=========================================================================================================================== -// GenerateUniqueDNSName -// -// Non-Apple platform stub routine to generate a unique RFC 1034-compatible DNS name for the device. Should be -// implemented to return a unique name. -//=========================================================================================================================== - -mDNSlocal void GenerateUniqueDNSName( char *outName, long *ioSeed ) -{ - DEBUG_UNUSED( ioSeed ); - - // $$$ Non-Apple Platforms: Fill in appropriate DNS name for device. - - mDNSPlatformStrCopy( kMDNSDefaultName, outName ); } -#endif - -#if 0 -#pragma mark - -#endif //=========================================================================================================================== -// getifaddrs +// TaskProcessPackets //=========================================================================================================================== -int getifaddrs( struct ifaddrs **outAddrs ) +mDNSlocal void TaskProcessPackets( mDNS *inMDNS, SocketSet *inSS, SocketRef inSock ) { - int err; - struct ifaddrs * head; - struct ifaddrs ** next; - struct ifaddrs * ifa; - int i; - struct ifnet * ifp; - char ipString[ INET_ADDR_LEN ]; - int n; - - head = NULL; - next = &head; - - i = 1; + mDNSu32 ifindex; + ssize_t n; + mDNSu8 * buf; + size_t size; + struct sockaddr_storage from; + size_t fromSize; + mDNSAddr destAddr; + mDNSAddr senderAddr; + mDNSIPPort senderPort; + mDNSInterfaceID id; + + buf = (mDNSu8 *) &inMDNS->imsg; + size = sizeof( inMDNS->imsg ); for( ;; ) { - ifp = ifIndexToIfp( i ); - if( !ifp ) - { - break; - } - ++i; - - // Allocate and initialize the ifaddrs structure and attach it to the linked list. - - ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) ); - require_action( ifa, exit, err = ENOMEM ); - - *next = ifa; - next = &ifa->ifa_next; - - // Fetch the name. - - ifa->ifa_name = (char *) malloc( 16 ); - require_action( ifa->ifa_name, exit, err = ENOMEM ); - - n = sprintf( ifa->ifa_name, "%s%d", ifp->if_name, ifp->if_unit ); - require_action( n < 16, exit, err = ENOBUFS ); - - // Fetch the address. - - ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( struct sockaddr_in ) ); - require_action( ifa->ifa_addr, exit, err = ENOMEM ); - - ipString[ 0 ] = '\0'; - #if( TARGET_NON_APPLE ) - err = ifAddrGet( ifa->ifa_name, ipString ); - require_noerr( err, exit ); - #else - err = ifAddrGetNonAlias( ifa->ifa_name, ipString ); - require_noerr( err, exit ); - #endif - - err = sock_pton( ipString, AF_INET, ifa->ifa_addr, 0, NULL ); - require_noerr( err, exit ); - - // Fetch flags. - - ifa->ifa_flags = ifp->if_flags; - } - - // Success! - - if( outAddrs ) - { - *outAddrs = head; - head = NULL; - } - err = 0; - -exit: - if( head ) - { - freeifaddrs( head ); - } - return( err ); -} - -//=========================================================================================================================== -// freeifaddrs -//=========================================================================================================================== - -void freeifaddrs( struct ifaddrs *inAddrs ) -{ - struct ifaddrs * p; - struct ifaddrs * q; - - // Free each piece of the structure. Set to null after freeing to handle macro-aliased fields. - - for( p = inAddrs; p; p = q ) - { - q = p->ifa_next; - - if( p->ifa_name ) + ifindex = 0; + n = mDNSRecvMsg( inSock, buf, size, &from, sizeof( from ), &fromSize, &destAddr, &ifindex ); + if( n < 0 ) break; + if( from.ss_family == AF_INET ) { - free( p->ifa_name ); - p->ifa_name = NULL; + struct sockaddr_in * sa4; + + sa4 = (struct sockaddr_in *) &from; + senderAddr.type = mDNSAddrType_IPv4; + senderAddr.ip.v4.NotAnInteger = sa4->sin_addr.s_addr; + senderPort.NotAnInteger = sa4->sin_port; } - if( p->ifa_addr ) + else if( from.ss_family == AF_INET6 ) { - free( p->ifa_addr ); - p->ifa_addr = NULL; + struct sockaddr_in6 * sa6; + + sa6 = (struct sockaddr_in6 *) &from; + senderAddr.type = mDNSAddrType_IPv6; + senderAddr.ip.v6 = *( (mDNSv6Addr *) &sa6->sin6_addr ); + senderPort.NotAnInteger = sa6->sin6_port; } - if( p->ifa_netmask ) + else { - free( p->ifa_netmask ); - p->ifa_netmask = NULL; + dmsg( kDebugLevelWarning, DEBUG_NAME "%s: WARNING! from addr unknown family %d\n", __ROUTINE__, from.ss_family ); + continue; } - if( p->ifa_dstaddr ) + + // Even though we indicated a specific interface when joining the multicast group, a weirdness of the + // sockets API means that even though this socket has only officially joined the multicast group + // on one specific interface, the kernel will still deliver multicast packets to it no matter which + // interface they arrive on. According to the official Unix Powers That Be, this is Not A Bug. + // To work around this weirdness, we use the IP_RECVIF/IPV6_PKTINFO options to find the interface + // on which the packet arrived, and ignore the packet if it really arrived on some other interface. + + if( mDNSAddrIsDNSMulticast( &destAddr ) ) { - free( p->ifa_dstaddr ); - p->ifa_dstaddr = NULL; + if( !inSS->info || !inSS->info->exists ) + { + dpkt( kDebugLevelChatty - 3, DEBUG_NAME " ignored mcast, src=[%#39a], dst=[%#39a], if= unicast socket %d\n", + &senderAddr, &destAddr, inSock ); + continue; + } + if( ifindex != inSS->info->scopeID ) + { + #if( DEBUG && MDNS_DEBUG_PACKETS ) + char ifname[ IF_NAMESIZE ]; + #endif + + dpkt( kDebugLevelChatty - 3, + DEBUG_NAME " ignored mcast, src=[%#39a] dst=[%#39a], if=%8s(%u) -- really for %8s(%u)\n", + &senderAddr, &destAddr, inSS->info->ifinfo.ifname, inSS->info->scopeID, + if_indextoname( ifindex, ifname ), ifindex ); + continue; + } + + id = inSS->info->ifinfo.InterfaceID; + dpkt( kDebugLevelChatty - 2, DEBUG_NAME "recv %4d bytes, src=[%#39a]:%5hu, dst=[%#39a], if=%8s(%u) %#p\n", + n, &senderAddr, mDNSVal16( senderPort ), &destAddr, inSS->info->ifinfo.ifname, inSS->info->scopeID, id ); } - if( p->ifa_data ) + else { - free( p->ifa_data ); - p->ifa_data = NULL; + NetworkInterfaceInfoVxWorks * i; + + // For unicast packets, try to find the matching interface. + + for( i = inMDNS->p->interfaceList; i && ( i->scopeID != ifindex ); i = i->next ) {} + if( i ) id = i->ifinfo.InterfaceID; + else id = NULL; } - free( p ); + mDNSCoreReceive( inMDNS, buf, buf + n, &senderAddr, senderPort, &destAddr, MulticastDNSPort, id ); } } //=========================================================================================================================== -// sock_pton +// mDNSRecvMsg //=========================================================================================================================== -int sock_pton( const char *inString, int inFamily, void *outAddr, size_t inAddrSize, size_t *outAddrSize ) +mDNSlocal ssize_t + mDNSRecvMsg( + SocketRef inSock, + void * inBuffer, + size_t inBufferSize, + void * outFrom, + size_t inFromSize, + size_t * outFromSize, + mDNSAddr * outDstAddr, + uint32_t * outIndex ) { - int err; + struct msghdr msg; + struct iovec iov; + ssize_t n; + char ancillary[ 1024 ]; + struct cmsghdr * cmPtr; + int err; - if( inFamily == AF_INET ) + // Read a packet and any ancillary data. Note: EWOULDBLOCK errors are expected when we've read all pending packets. + + iov.iov_base = (char *) inBuffer; + iov.iov_len = inBufferSize; + msg.msg_name = (caddr_t) outFrom; + msg.msg_namelen = inFromSize; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = (caddr_t) &ancillary; + msg.msg_controllen = sizeof( ancillary ); + msg.msg_flags = 0; + n = recvmsg( inSock, &msg, 0 ); + if( n < 0 ) { - struct sockaddr_in * ipv4; - - if( inAddrSize == 0 ) - { - inAddrSize = sizeof( struct sockaddr_in ); - } - if( inAddrSize < sizeof( struct sockaddr_in ) ) - { - err = EINVAL; - goto exit; - } - - ipv4 = (struct sockaddr_in *) outAddr; - err = inet_aton( (char *) inString, &ipv4->sin_addr ); - if( err == 0 ) - { - ipv4->sin_family = AF_INET; - if( outAddrSize ) - { - *outAddrSize = sizeof( struct sockaddr_in ); - } - } + err = errno_compat(); + if( err != EWOULDBLOCK ) dmsg( kDebugLevelWarning, DEBUG_NAME "%s: recvmsg(%d) returned %d, errno %d\n", + __ROUTINE__, inSock, n, err ); + goto exit; } -#if( defined( AF_INET6 ) ) - else if( inFamily == AF_INET6 ) // $$$ TO DO: Add IPv6 support. + if( msg.msg_controllen < sizeof( struct cmsghdr ) ) { - err = EAFNOSUPPORT; + dmsg( kDebugLevelWarning, DEBUG_NAME "%s: recvmsg(%d) msg_controllen %d < sizeof( struct cmsghdr ) %u\n", + __ROUTINE__, inSock, msg.msg_controllen, sizeof( struct cmsghdr ) ); + n = mStatus_UnknownErr; goto exit; } -#endif - else + if( msg.msg_flags & MSG_CTRUNC ) { - err = EAFNOSUPPORT; + dmsg( kDebugLevelWarning, DEBUG_NAME "%s: recvmsg(%d) MSG_CTRUNC (%d recv'd)\n", __ROUTINE__, inSock, n ); + n = mStatus_BadFlagsErr; goto exit; } - -exit: - return( err ); -} - -//=========================================================================================================================== -// sock_ntop -//=========================================================================================================================== - -char * sock_ntop( const void *inAddr, size_t inAddrSize, char *inBuffer, size_t inBufferSize ) -{ - const struct sockaddr * addr; + *outFromSize = msg.msg_namelen; - addr = (const struct sockaddr *) inAddr; - if( addr->sa_family == AF_INET ) + // Parse each option out of the ancillary data. + + for( cmPtr = CMSG_FIRSTHDR( &msg ); cmPtr; cmPtr = CMSG_NXTHDR( &msg, cmPtr ) ) { - struct sockaddr_in * ipv4; - - if( inAddrSize == 0 ) + if( ( cmPtr->cmsg_level == IPPROTO_IP ) && ( cmPtr->cmsg_type == IP_RECVDSTADDR ) ) { - inAddrSize = sizeof( struct sockaddr_in ); + outDstAddr->type = mDNSAddrType_IPv4; + outDstAddr->ip.v4.NotAnInteger = *( (mDNSu32 *) CMSG_DATA( cmPtr ) ); } - if( inAddrSize < sizeof( struct sockaddr_in ) ) + else if( ( cmPtr->cmsg_level == IPPROTO_IP ) && ( cmPtr->cmsg_type == IP_RECVIF ) ) { - errno = EINVAL; - inBuffer = NULL; - goto exit; + struct sockaddr_dl * sdl; + + sdl = (struct sockaddr_dl *) CMSG_DATA( cmPtr ); + *outIndex = sdl->sdl_index; } - if( inBufferSize < 16 ) + else if( ( cmPtr->cmsg_level == IPPROTO_IPV6 ) && ( cmPtr->cmsg_type == IPV6_PKTINFO ) ) { - errno = ENOBUFS; - inBuffer = NULL; - goto exit; + struct in6_pktinfo * pi6; + + pi6 = (struct in6_pktinfo *) CMSG_DATA( cmPtr ); + outDstAddr->type = mDNSAddrType_IPv6; + outDstAddr->ip.v6 = *( (mDNSv6Addr *) &pi6->ipi6_addr ); + *outIndex = pi6->ipi6_ifindex; } - - ipv4 = (struct sockaddr_in *) addr; - inet_ntoa_b( ipv4->sin_addr, inBuffer ); - } -#if( defined( AF_INET6 ) ) - else if( addr->sa_family == AF_INET6 ) // $$$ TO DO: Add IPv6 support. - { - errno = EAFNOSUPPORT; - inBuffer = NULL; - goto exit; - } -#endif - else - { - errno = EAFNOSUPPORT; - inBuffer = NULL; - goto exit; } exit: - return( inBuffer ); + return( n ); } #if 0 @@ -2055,139 +2048,113 @@ exit: #pragma mark == Debugging == #endif -#if( DEBUG ) - -void mDNSShow( BOOL inShowRecords ); -void mDNSShowRecords( void ); -void mDNSShowTXT( const void *inTXT, size_t inTXTSize ); - +#if( DEBUG && MDNS_DEBUG_SHOW ) //=========================================================================================================================== // mDNSShow //=========================================================================================================================== -void mDNSShow( BOOL inShowRecords ) +void mDNSShow( void ); + +void mDNSShow( void ) { - MDNSInterfaceItem * item; - mDNSAddr ip; - int n; + NetworkInterfaceInfoVxWorks * i; + int num; + AuthRecord * r; + mDNSs32 utc; + // Globals + + dmsg( kDebugLevelMax, "\n-- mDNS globals --\n" ); + dmsg( kDebugLevelMax, " sizeof( mDNS ) = %d\n", (int) sizeof( mDNS ) ); + dmsg( kDebugLevelMax, " sizeof( ResourceRecord ) = %d\n", (int) sizeof( ResourceRecord ) ); + dmsg( kDebugLevelMax, " sizeof( AuthRecord ) = %d\n", (int) sizeof( AuthRecord ) ); + dmsg( kDebugLevelMax, " sizeof( CacheRecord ) = %d\n", (int) sizeof( CacheRecord ) ); + dmsg( kDebugLevelMax, " mDNSPlatformOneSecond = %ld\n", mDNSPlatformOneSecond ); + dmsg( kDebugLevelMax, " gMDNSTicksToMicro = %ld\n", gMDNSTicksToMicro ); + dmsg( kDebugLevelMax, " gMDNSPtr = %#p\n", gMDNSPtr ); if( !gMDNSPtr ) { - printf( "### mDNS not initialized\n" ); + dmsg( kDebugLevelMax, "### mDNS not initialized\n" ); return; } - - // Globals - - printf( "\n-- mDNS globals --\n" ); - printf( " sizeof( mDNS ) = %d\n", (int) sizeof( mDNS ) ); - printf( " sizeof( ResourceRecord ) = %d\n", (int) sizeof( ResourceRecord ) ); - printf( " sizeof( AuthRecord ) = %d\n", (int) sizeof( AuthRecord ) ); - printf( " sizeof( CacheRecord ) = %d\n", (int) sizeof( CacheRecord ) ); - printf( " gMDNSPtr = 0x%08lX\n", (unsigned long) gMDNSPtr ); - printf( " gMDNSTicksToMicrosecondsMultiplier = %ld\n", gMDNSTicksToMicrosecondsMultiplier ); - printf( " lockID = 0x%08lX\n", (unsigned long) gMDNSPtr->p->lockID ); - printf( " readyEvent = 0x%08lX\n", (unsigned long) gMDNSPtr->p->readyEvent ); - printf( " taskInitErr = %ld\n", gMDNSPtr->p->taskInitErr ); - printf( " quitEvent = 0x%08lX\n", (unsigned long) gMDNSPtr->p->quitEvent ); - printf( " commandPipe = %d\n", gMDNSPtr->p->commandPipe ); - printf( " task = 0x%08lX\n", (unsigned long) gMDNSPtr->p->task ); - printf( " quit = %d\n", gMDNSPtr->p->quit ); - printf( " configID = %ld\n", gMDNSPtr->p->configID ); - printf( " rescheduled = %d\n", gMDNSPtr->p->rescheduled ); - printf( " nicelabel = \"%.*s\"\n", gMDNSPtr->nicelabel.c[ 0 ], (char *) &gMDNSPtr->nicelabel.c[ 1 ] ); - printf( " hostLabel = \"%.*s\"\n", gMDNSPtr->hostlabel.c[ 0 ], (char *) &gMDNSPtr->hostlabel.c[ 1 ] ); - printf( "\n"); + dmsg( kDebugLevelMax, " nicelabel = \"%#s\"\n", gMDNSPtr->nicelabel.c ); + dmsg( kDebugLevelMax, " hostLabel = \"%#s\"\n", gMDNSPtr->hostlabel.c ); + dmsg( kDebugLevelMax, " MulticastHostname = \"%##s\"\n", gMDNSPtr->MulticastHostname.c ); + dmsg( kDebugLevelMax, " HIHardware = \"%#s\"\n", gMDNSPtr->HIHardware.c ); + dmsg( kDebugLevelMax, " HISoftware = \"%#s\"\n", gMDNSPtr->HISoftware.c ); + dmsg( kDebugLevelMax, " UnicastPort4/6 = %d/%d\n", + mDNSVal16( gMDNSPtr->UnicastPort4 ), mDNSVal16( gMDNSPtr->UnicastPort6 ) ); + dmsg( kDebugLevelMax, " unicastSS.sockV4/V6 = %d/%d\n", + gMDNSPtr->p->unicastSS.sockV4, gMDNSPtr->p->unicastSS.sockV6 ); + dmsg( kDebugLevelMax, " lock = %#p\n", gMDNSPtr->p->lock ); + dmsg( kDebugLevelMax, " initEvent = %#p\n", gMDNSPtr->p->initEvent ); + dmsg( kDebugLevelMax, " initErr = %ld\n", gMDNSPtr->p->initErr ); + dmsg( kDebugLevelMax, " quitEvent = %#p\n", gMDNSPtr->p->quitEvent ); + dmsg( kDebugLevelMax, " commandPipe = %d\n", gMDNSPtr->p->commandPipe ); + dmsg( kDebugLevelMax, " taskID = %#p\n", gMDNSPtr->p->taskID ); + dmsg( kDebugLevelMax, "\n" ); // Interfaces - printf( "\n-- mDNS interfaces --\n" ); - n = 1; - for( item = gMDNSPtr->p->interfaceList; item; item = item->next ) + utc = mDNSPlatformUTC(); + dmsg( kDebugLevelMax, "-- mDNS interfaces --\n" ); + num = 0; + for( i = gMDNSPtr->p->interfaceList; i; i = i->next ) { - printf( " -- interface %u --\n", n ); - printf( " name = \"%s\"\n", item->name ); - printf( " multicastSocketRef = %d\n", item->multicastSocketRef ); - printf( " sendingSocketRef = %d\n", item->sendingSocketRef ); - ip = item->hostSet.ip; - printf( " hostSet.ip = %u.%u.%u.%u\n", ip.ip.v4.b[ 0 ], ip.ip.v4.b[ 1 ], - ip.ip.v4.b[ 2 ], ip.ip.v4.b[ 3 ] ); - printf( " hostSet.advertise = %s\n", item->hostSet.Advertise ? "YES" : "NO" ); - printf( " hostRegistered = %s\n", item->hostRegistered ? "YES" : "NO" ); - printf( " --\n" ); - printf( " sendMulticastCounter = %d\n", item->sendMulticastCounter ); - printf( " sendUnicastCounter = %d\n", item->sendUnicastCounter ); - printf( " sendErrorCounter = %d\n", item->sendErrorCounter ); - printf( " recvCounter = %d\n", item->recvCounter ); - printf( " recvErrorCounter = %d\n", item->recvErrorCounter ); - printf( " recvLoopCounter = %d\n", item->recvLoopCounter ); - printf( "\n" ); - ++n; + dmsg( kDebugLevelMax, " interface %2d %8s(%u) [%#39a] %s, sockV4 %2d, sockV6 %2d, Age %d\n", + num, i->ifinfo.ifname, i->scopeID, &i->ifinfo.ip, + i->ifinfo.InterfaceID ? " REGISTERED" : "*NOT* registered", + i->ss.sockV4, i->ss.sockV6, utc - i->lastSeen ); + ++num; } + dmsg( kDebugLevelMax, "\n" ); // Resource Records - if( inShowRecords ) - { - mDNSShowRecords(); - } -} - -//=========================================================================================================================== -// mDNSShowRecords -//=========================================================================================================================== - -void mDNSShowRecords( void ) -{ - MDNSInterfaceItem * item; - int n; - AuthRecord * record; - char name[ MAX_ESCAPED_DOMAIN_NAME ]; - - printf( "\n-- mDNS resource records --\n" ); - n = 1; - for( record = gMDNSPtr->ResourceRecords; record; record = record->next ) - { - item = (MDNSInterfaceItem *) record->resrec.InterfaceID; - ConvertDomainNameToCString( &record->resrec.name, name ); - printf( " -- record %d --\n", n ); - printf( " interface = 0x%08X (%s)\n", (int) item, item ? item->name : "" ); - printf( " name = \"%s\"\n", name ); - printf( "\n" ); - ++n; - } - printf( "\n"); -} - -//=========================================================================================================================== -// mDNSShowTXT -//=========================================================================================================================== - -void mDNSShowTXT( const void *inTXT, size_t inTXTSize ) -{ - const mDNSu8 * p; - const mDNSu8 * end; - int i; - mDNSu8 size; - - printf( "\nTXT record (%u bytes):\n\n", inTXTSize ); - - p = (const mDNSu8 *) inTXT; - end = p + inTXTSize; - i = 0; - - while( p < end ) + dmsg( kDebugLevelMax, "-- mDNS resource records --\n" ); + num = 0; + for( r = gMDNSPtr->ResourceRecords; r; r = r->next ) { - size = *p++; - if( ( p + size ) > end ) + i = (NetworkInterfaceInfoVxWorks *) r->resrec.InterfaceID; + if( r->resrec.rrtype == kDNSType_TXT ) { - printf( "\n### MALFORMED TXT RECORD (length byte too big for record)\n\n" ); - break; + RDataBody * rd; + const mDNSu8 * txt; + const mDNSu8 * end; + mDNSu8 size; + int nEntries; + + rd = &r->resrec.rdata->u; + dmsg( kDebugLevelMax, " record %2d: %#p %8s(%u): %4d %##s %s:\n", num, i, + i ? i->ifinfo.ifname : "", + i ? i->scopeID : 0, + r->resrec.rdlength, r->resrec.name->c, DNSTypeName( r->resrec.rrtype ) ); + + nEntries = 0; + txt = rd->txt.c; + end = txt + r->resrec.rdlength; + while( txt < end ) + { + size = *txt++; + if( ( txt + size ) > end ) + { + dmsg( kDebugLevelMax, " ### ERROR! txt length byte too big (%u, %u max)\n", size, end - txt ); + break; + } + dmsg( kDebugLevelMax, " string %2d (%3d bytes): \"%.*s\"\n", nEntries, size, size, txt ); + txt += size; + ++nEntries; + } + } + else + { + dmsg( kDebugLevelMax, " record %2d: %#p %8s(%u): %s\n", num, i, + i ? i->ifinfo.ifname : "", + i ? i->scopeID : 0, + ARDisplayString( gMDNSPtr, r ) ); } - printf( "%2d (%3d bytes): \"%.*s\"\n", i, size, size, p ); - p += size; - ++i; + ++num; } - printf( "\n" ); + dmsg( kDebugLevelMax, "\n"); } -#endif // DEBUG +#endif // DEBUG && MDNS_DEBUG_SHOW diff --git a/mDNSVxWorks/mDNSVxWorks.h b/mDNSVxWorks/mDNSVxWorks.h index 9526ae5..50552f3 100644 --- a/mDNSVxWorks/mDNSVxWorks.h +++ b/mDNSVxWorks/mDNSVxWorks.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2005 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -20,34 +20,24 @@ * * @APPLE_LICENSE_HEADER_END@ - Contains: mDNS platform plugin for VxWorks. - - Copyright: Copyright (C) 2002-2003 Apple Computer, Inc., All Rights Reserved. - - Change History (most recent first): + Change History (most recent first): $Log: mDNSVxWorks.h,v $ -Revision 1.3 2004/09/17 01:08:57 cheshire -Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h - The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces - declared in that file are ONLY appropriate to single-address-space embedded applications. - For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. - -Revision 1.2 2003/08/12 19:56:27 cheshire -Update to APSL 2.0 - -Revision 1.1 2003/08/02 10:06:49 bradley -mDNS platform plugin for VxWorks. +Revision 1.4 2005/05/30 07:36:38 bradley +New implementation of the mDNS platform plugin for VxWorks 5.5 or later with IPv6 support. */ -#ifndef __MDNS_VXWORKS__ -#define __MDNS_VXWORKS__ +#ifndef __MDNS_VXWORKS_H__ +#define __MDNS_VXWORKS_H__ #include "vxWorks.h" +#include "config.h" + #include "semLib.h" -#include "mDNSEmbeddedAPI.h" +#include "CommonServices.h" +#include "DebugServices.h" #ifdef __cplusplus extern "C" { @@ -55,96 +45,91 @@ mDNS platform plugin for VxWorks. // Forward Declarations -typedef struct MDNSInterfaceItem MDNSInterfaceItem; +typedef struct NetworkInterfaceInfoVxWorks NetworkInterfaceInfoVxWorks; //--------------------------------------------------------------------------------------------------------------------------- -/*! @struct mDNS_PlatformSupport_struct +/*! @struct SocketSet - @abstract Structure containing platform-specific data. + @abstract Data for IPv4 and IPv6 sockets. */ -struct mDNS_PlatformSupport_struct +typedef struct SocketSet SocketSet; +struct SocketSet { - SEM_ID lockID; - SEM_ID readyEvent; - mStatus taskInitErr; - SEM_ID quitEvent; - MDNSInterfaceItem * interfaceList; - int commandPipe; - int task; - mDNSBool quit; - long configID; - int rescheduled; + NetworkInterfaceInfoVxWorks * info; + SocketRef sockV4; + SocketRef sockV6; }; //--------------------------------------------------------------------------------------------------------------------------- -/*! @function mDNSReconfigure - - @abstract Tell mDNS that the configuration has changed. Call when IP address changes, link goes up after being down, etc. - - @discussion - - VxWorks does not provide a generic mechanism for getting notified when network interfaces change so this routines - provides a way for BSP-specific code to signal mDNS that something has changed and it should re-build its interfaces. -*/ - -void mDNSReconfigure( void ); - -//--------------------------------------------------------------------------------------------------------------------------- -/*! @struct ifaddrs +/*! @struct NetworkInterfaceInfoVxWorks - @abstract Interface information + @abstract Interface info for VxWorks. */ -struct ifaddrs +struct NetworkInterfaceInfoVxWorks { - struct ifaddrs * ifa_next; - char * ifa_name; - u_int ifa_flags; - struct sockaddr * ifa_addr; - struct sockaddr * ifa_netmask; - struct sockaddr * ifa_dstaddr; - void * ifa_data; + NetworkInterfaceInfo ifinfo; // MUST be the first element in this structure. + NetworkInterfaceInfoVxWorks * next; + mDNSu32 exists; // 1 = currently exists in getifaddrs list; 0 = doesn't. + // 2 = exists, but McastTxRx state changed. + mDNSs32 lastSeen; // If exists == 0, last time this interface appeared in getifaddrs list. + mDNSu32 scopeID; // Interface index / IPv6 scope ID. + int family; // Socket address family of the primary socket. + mDNSBool multicast; + SocketSet ss; }; //--------------------------------------------------------------------------------------------------------------------------- -/*! @function getifaddrs - - @abstract Builds a linked list of interfaces. Caller must free using freeifaddrs if successful. -*/ - -int getifaddrs( struct ifaddrs **outAddrs ); - -//--------------------------------------------------------------------------------------------------------------------------- -/*! @function freeifaddrs +/*! @struct mDNS_PlatformSupport_struct - @abstract Frees a linked list of interfaces built with getifaddrs. + @abstract Data for mDNS platform plugin. */ -void freeifaddrs( struct ifaddrs *inAddrs ); +struct mDNS_PlatformSupport_struct +{ + NetworkInterfaceInfoVxWorks * interfaceList; + SocketSet unicastSS; + domainlabel userNiceLabel; + domainlabel userHostLabel; + + SEM_ID lock; + SEM_ID initEvent; + mStatus initErr; + SEM_ID quitEvent; + int commandPipe; + int taskID; + mDNSBool quit; +}; //--------------------------------------------------------------------------------------------------------------------------- -/*! @function sock_pton - - @abstract Converts a 'p'resentation address string into a 'n'umeric sockaddr structure. +/*! @function mDNSReconfigure - @result 0 if successful or an error code on failure. + @abstract Tell mDNS that the configuration has changed. Call when IP address changes, link goes up after being down, etc. + + @discussion + + VxWorks does not provide a generic mechanism for getting notified when network interfaces change so this routines + provides a way for BSP-specific code to signal mDNS that something has changed and it should re-build its interfaces. */ -int sock_pton( const char *inString, int inFamily, void *outAddr, size_t inAddrSize, size_t *outAddrSize ); +void mDNSReconfigure( void ); //--------------------------------------------------------------------------------------------------------------------------- -/*! @function sock_ntop - - @abstract Converts a 'n'umeric sockaddr structure into a 'p'resentation address string. +/*! @function mDNSDeferIPv4 + + @abstract Tells mDNS whether to defer advertising of IPv4 interfaces. + + @discussion - @result Ptr to 'p'resentation address string buffer if successful or NULL on failure. + To workaround problems with clients getting a link-local IPv4 address before a DHCP address is acquired, this allows + external code to defer advertising of IPv4 addresses until a DHCP lease has been acquired (or it times out). */ -char * sock_ntop( const void *inAddr, size_t inAddrSize, char *inBuffer, size_t inBufferSize ); +void mDNSDeferIPv4( mDNSBool inDefer ); #ifdef __cplusplus } #endif -#endif // __MDNS_VXWORKS__ +#endif // __MDNS_VXWORKS_H__ diff --git a/mDNSWindows/ControlPanel/ConfigPropertySheet.cpp b/mDNSWindows/ControlPanel/ConfigPropertySheet.cpp index 07160cf..c9a4123 100755 --- a/mDNSWindows/ControlPanel/ConfigPropertySheet.cpp +++ b/mDNSWindows/ControlPanel/ConfigPropertySheet.cpp @@ -23,6 +23,9 @@ Change History (most recent first): $Log: ConfigPropertySheet.cpp,v $ +Revision 1.4 2005/10/05 20:46:50 herscher + Move Wide-Area preferences to another part of the registry so they don't removed during an update-install. + Revision 1.3 2005/03/03 19:55:22 shersche ControlPanel source code isn't saving CVS log info @@ -268,7 +271,7 @@ CConfigPropertySheet::SetupRegistryNotifications() check( m_threadExited == NULL ); check( m_thread == NULL ); - err = RegCreateKey( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\" kServiceName L"\\Parameters\\DynDNS\\State\\Hostnames", &m_statusKey ); + err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\DynDNS\\State\\Hostnames", &m_statusKey ); require_noerr( err, exit ); m_threadExited = CreateEvent( NULL, FALSE, FALSE, NULL ); diff --git a/mDNSWindows/ControlPanel/ControlPanel.rc b/mDNSWindows/ControlPanel/ControlPanel.rc index c44dc20..3111c3e 100644 --- a/mDNSWindows/ControlPanel/ControlPanel.rc +++ b/mDNSWindows/ControlPanel/ControlPanel.rc @@ -60,7 +60,6 @@ BEGIN END END - #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // @@ -120,13 +119,13 @@ CAPTION "Hostname" FONT 8, "MS Sans Serif", 0, 0, 0x0 BEGIN LTEXT "Enter a hostname for this computer. Other computers on the Internet will be able to reach your computer using this hostname.", - IDC_STATIC,7,19,248,28 - LTEXT "Hostname:",IDC_STATIC,15,55,35,8 - EDITTEXT IDC_EDIT1,52,53,187,14,ES_AUTOHSCROLL - PUSHBUTTON "Password...",IDC_BUTTON1,52,72,67,14 - ICON IDI_FAILURE,IDC_FAILURE,240,50,21,20,SS_CENTERIMAGE | + IDC_STATIC,10,19,245,28 + LTEXT "Hostname:",IDC_STATIC,13,55,35,8 + EDITTEXT IDC_EDIT1,55,53,184,14,ES_AUTOHSCROLL + PUSHBUTTON "Password...",IDC_BUTTON1,55,72,65,14 + ICON IDI_FAILURE,IDC_FAILURE,240,50,20,20,SS_CENTERIMAGE | SS_REALSIZEIMAGE - ICON IDI_SUCCESS,IDC_SUCCESS,240,50,21,20,SS_CENTERIMAGE | + ICON IDI_SUCCESS,IDC_SUCCESS,240,50,20,20,SS_CENTERIMAGE | SS_REALSIZEIMAGE | NOT WS_VISIBLE END @@ -136,12 +135,12 @@ CAPTION "Registration" FONT 8, "MS Sans Serif", 0, 0, 0x0 BEGIN CONTROL "Domain:",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,13,54,42,10 - COMBOBOX IDC_COMBO2,59,53,189,46,CBS_DROPDOWN | CBS_SORT | + WS_TABSTOP,13,54,41,10 + COMBOBOX IDC_COMBO2,55,53,193,46,CBS_DROPDOWN | CBS_SORT | WS_DISABLED | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Password...",IDC_BUTTON1,59,72,65,14 + PUSHBUTTON "Password...",IDC_BUTTON1,55,72,65,14 LTEXT "Check the box and enter a registration domain to enable Bonjour advertising beyond the local subnet. ", - IDC_STATIC,14,20,233,23 + IDC_STATIC,10,19,233,23 END IDR_SECRET DIALOGEX 0, 0, 251, 90 @@ -153,9 +152,9 @@ BEGIN DEFPUSHBUTTON "OK",IDOK,139,69,50,14 PUSHBUTTON "Cancel",IDCANCEL,194,69,50,14 LTEXT "Name:",IDC_STATIC,9,28,22,8 - EDITTEXT IDC_SECRET_NAME,36,26,208,14,ES_AUTOHSCROLL - LTEXT "Secret:",IDC_STATIC,9,44,24,8 - EDITTEXT IDC_SECRET,36,42,208,14,ES_PASSWORD | ES_AUTOHSCROLL + EDITTEXT IDC_KEY,49,26,195,14,ES_AUTOHSCROLL + LTEXT "Password:",IDC_STATIC,9,44,32,8 + EDITTEXT IDC_SECRET,49,42,195,14,ES_PASSWORD | ES_AUTOHSCROLL LTEXT "Enter a Password if your DNS server requires authentication.", IDC_STATIC,7,7,237,15 END @@ -182,7 +181,7 @@ FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN DEFPUSHBUTTON "OK",IDOK,117,74,50,14 PUSHBUTTON "Cancel",IDCANCEL,173,74,50,14 - COMBOBOX IDC_COMBO1,35,42,188,30,CBS_DROPDOWN | CBS_SORT | + COMBOBOX IDC_COMBO1,35,42,188,100,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP LTEXT "Domain:",IDC_STATIC,7,43,27,8 LTEXT "The following domain will be added to your list of Bonjour browse domains.", diff --git a/mDNSWindows/ControlPanel/ControlPanel.vcproj b/mDNSWindows/ControlPanel/ControlPanel.vcproj index e322593..19bdcc1 100755 --- a/mDNSWindows/ControlPanel/ControlPanel.vcproj +++ b/mDNSWindows/ControlPanel/ControlPanel.vcproj @@ -1,7 +1,7 @@ - + + + + + + + + + - - + + + RelativePath="stdafx.h"> + RelativePath="ThirdPage.h"> + RelativePath="res\configurator.ico"> + RelativePath="res\controlpanel.ico"> + RelativePath="ControlPanel.rc"> + RelativePath="res\ControlPanel.rc2"> @@ -318,13 +332,13 @@ RelativePath="..\DebugServices.h"> + RelativePath="..\..\mDNSShared\dns_sd.h"> + RelativePath="..\WinServices.cpp"> + RelativePath="..\WinServices.h"> diff --git a/mDNSWindows/ControlPanel/FirstPage.cpp b/mDNSWindows/ControlPanel/FirstPage.cpp index 9dfe401..375d0f4 100755 --- a/mDNSWindows/ControlPanel/FirstPage.cpp +++ b/mDNSWindows/ControlPanel/FirstPage.cpp @@ -23,6 +23,12 @@ Change History (most recent first): $Log: FirstPage.cpp,v $ +Revision 1.5 2005/10/05 20:46:50 herscher + Move Wide-Area preferences to another part of the registry so they don't removed during an update-install. + +Revision 1.4 2005/04/05 03:52:14 shersche + Registering with shared secret key doesn't work. Additionally, mDNSResponder wasn't dynamically re-reading it's DynDNS setup after setting a shared secret key. + Revision 1.3 2005/03/07 18:27:42 shersche Fix problem when ControlPanel commits changes to the browse domain list @@ -51,17 +57,18 @@ CFirstPage::CFirstPage() : CPropertyPage(CFirstPage::IDD), m_ignoreHostnameChange( false ), - m_statusKey( NULL ) + m_statusKey( NULL ), + m_setupKey( NULL ) { //{{AFX_DATA_INIT(CFirstPage) //}}AFX_DATA_INIT OSStatus err; - err = RegCreateKey( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\" kServiceName L"\\Parameters\\DynDNS\\State\\Hostnames", &m_statusKey ); + err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\DynDNS\\State\\Hostnames", &m_statusKey ); check_noerr( err ); - err = RegCreateKey( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\" kServiceName L"\\Parameters\\DynDNS\\Setup\\Hostnames", &m_setupKey ); + err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\DynDNS\\Setup\\Hostnames", &m_setupKey ); check_noerr( err ); } @@ -128,12 +135,33 @@ void CFirstPage::OnBnClickedSharedSecret() CSharedSecret dlg; - dlg.m_secretName = name; + dlg.m_key = name; if ( dlg.DoModal() == IDOK ) { - dlg.Commit(); + DWORD wakeup = 0; + DWORD dwSize = sizeof( DWORD ); + OSStatus err; + + dlg.Commit( name ); + + // We have now updated the secret, however the system service + // doesn't know about it yet. So we're going to update the + // registry with a dummy value which will cause the system + // service to re-initialize it's DynDNS setup + // + + RegQueryValueEx( m_setupKey, L"Wakeup", NULL, NULL, (LPBYTE) &wakeup, &dwSize ); + + wakeup++; + + err = RegSetValueEx( m_setupKey, L"Wakeup", 0, REG_DWORD, (LPBYTE) &wakeup, sizeof( DWORD ) ); + require_noerr( err, exit ); } + +exit: + + return; } diff --git a/mDNSWindows/ControlPanel/SecondPage.cpp b/mDNSWindows/ControlPanel/SecondPage.cpp index a330e84..83058b3 100755 --- a/mDNSWindows/ControlPanel/SecondPage.cpp +++ b/mDNSWindows/ControlPanel/SecondPage.cpp @@ -23,6 +23,15 @@ Change History (most recent first): $Log: SecondPage.cpp,v $ +Revision 1.6 2005/10/05 20:46:50 herscher + Move Wide-Area preferences to another part of the registry so they don't removed during an update-install. + +Revision 1.5 2005/04/05 04:15:46 shersche +RegQueryString was returning uninitialized strings if the registry key couldn't be found, so always initialize strings before checking the registry key. + +Revision 1.4 2005/04/05 03:52:14 shersche + Registering with shared secret key doesn't work. Additionally, mDNSResponder wasn't dynamically re-reading it's DynDNS setup after setting a shared secret key. + Revision 1.3 2005/03/03 19:55:22 shersche ControlPanel source code isn't saving CVS log info @@ -46,10 +55,16 @@ IMPLEMENT_DYNCREATE(CSecondPage, CPropertyPage) CSecondPage::CSecondPage() : - CPropertyPage(CSecondPage::IDD) + CPropertyPage(CSecondPage::IDD), + m_setupKey( NULL ) { //{{AFX_DATA_INIT(CSecondPage) //}}AFX_DATA_INIT + + OSStatus err; + + err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\DynDNS\\Setup\\" kServiceDynDNSRegistrationDomains, &m_setupKey ); + check_noerr( err ); } @@ -59,6 +74,11 @@ CSecondPage::CSecondPage() CSecondPage::~CSecondPage() { + if ( m_setupKey ) + { + RegCloseKey( m_setupKey ); + m_setupKey = NULL; + } } @@ -109,7 +129,6 @@ BOOL CSecondPage::OnSetActive() { CConfigPropertySheet * psheet; - HKEY key = NULL; DWORD dwSize; DWORD enabled; DWORD err; @@ -126,20 +145,15 @@ CSecondPage::OnSetActive() // Now populate the registration domain box - err = RegCreateKey( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\" kServiceName L"\\Parameters\\DynDNS\\Setup\\" kServiceDynDNSRegistrationDomains, &key ); - require_noerr( err, exit ); - - err = Populate( m_regDomainsBox, key, psheet->m_regDomains ); + err = Populate( m_regDomainsBox, m_setupKey, psheet->m_regDomains ); check_noerr( err ); dwSize = sizeof( DWORD ); - err = RegQueryValueEx( key, L"Enabled", NULL, NULL, (LPBYTE) &enabled, &dwSize ); + err = RegQueryValueEx( m_setupKey, L"Enabled", NULL, NULL, (LPBYTE) &enabled, &dwSize ); m_advertiseServicesButton.SetCheck( ( !err && enabled ) ? BST_CHECKED : BST_UNCHECKED ); m_regDomainsBox.EnableWindow( ( !err && enabled ) ); m_sharedSecretButton.EnableWindow( (!err && enabled ) ); - RegCloseKey( key ); - exit: return b; @@ -168,20 +182,12 @@ CSecondPage::OnOK() void CSecondPage::Commit() { - HKEY key = NULL; - DWORD err; - - err = RegCreateKey( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\" kServiceName L"\\Parameters\\DynDNS\\Setup\\" kServiceDynDNSRegistrationDomains, &key ); - require_noerr( err, exit ); - - err = Commit( m_regDomainsBox, key, m_advertiseServicesButton.GetCheck() == BST_CHECKED ); - check_noerr( err ); - -exit: + DWORD err; - if ( key ) + if ( m_setupKey != NULL ) { - RegCloseKey( key ); + err = Commit( m_regDomainsBox, m_setupKey, m_advertiseServicesButton.GetCheck() == BST_CHECKED ); + check_noerr( err ); } } @@ -244,15 +250,39 @@ CSecondPage::Commit( CComboBox & box, HKEY key, DWORD enabled ) void CSecondPage::OnBnClickedSharedSecret() { - CString string; + CString name; - m_regDomainsBox.GetWindowText( string ); + m_regDomainsBox.GetWindowText( name ); CSharedSecret dlg; - dlg.m_secretName = string; + dlg.m_key = name; + + if ( dlg.DoModal() == IDOK ) + { + DWORD wakeup = 0; + DWORD dwSize = sizeof( DWORD ); + OSStatus err; + + dlg.Commit( name ); + + // We have now updated the secret, however the system service + // doesn't know about it yet. So we're going to update the + // registry with a dummy value which will cause the system + // service to re-initialize it's DynDNS setup + // + + RegQueryValueEx( m_setupKey, L"Wakeup", NULL, NULL, (LPBYTE) &wakeup, &dwSize ); + + wakeup++; + + err = RegSetValueEx( m_setupKey, L"Wakeup", 0, REG_DWORD, (LPBYTE) &wakeup, sizeof( DWORD ) ); + require_noerr( err, exit ); + } + +exit: - dlg.DoModal(); + return; } @@ -470,6 +500,7 @@ CSecondPage::RegQueryString( HKEY key, CString valueName, CString & value ) string = (TCHAR*) malloc( stringLen ); require_action( string, exit, err = kUnknownErr ); + *string = '\0'; err = RegQueryValueEx( key, valueName, 0, NULL, (LPBYTE) string, &stringLen ); diff --git a/mDNSWindows/ControlPanel/SecondPage.h b/mDNSWindows/ControlPanel/SecondPage.h index a8d1f09..917608e 100755 --- a/mDNSWindows/ControlPanel/SecondPage.h +++ b/mDNSWindows/ControlPanel/SecondPage.h @@ -23,6 +23,9 @@ Change History (most recent first): $Log: SecondPage.h,v $ +Revision 1.4 2005/04/05 03:52:14 shersche + Registering with shared secret key doesn't work. Additionally, mDNSResponder wasn't dynamically re-reading it's DynDNS setup after setting a shared secret key. + Revision 1.3 2005/03/03 19:55:21 shersche ControlPanel source code isn't saving CVS log info @@ -113,6 +116,7 @@ private: CButton m_advertiseServicesButton; CButton m_sharedSecretButton; BOOL m_modified; + HKEY m_setupKey; public: afx_msg void OnCbnSelChange(); diff --git a/mDNSWindows/ControlPanel/SharedSecret.cpp b/mDNSWindows/ControlPanel/SharedSecret.cpp index e690b49..7ba6a2a 100644 --- a/mDNSWindows/ControlPanel/SharedSecret.cpp +++ b/mDNSWindows/ControlPanel/SharedSecret.cpp @@ -23,6 +23,12 @@ Change History (most recent first): $Log: SharedSecret.cpp,v $ +Revision 1.4 2005/10/18 06:13:41 herscher + Prepend "$" to key name to ensure that secure updates work if the domain name and key name are the same + +Revision 1.3 2005/04/06 02:04:49 shersche + Registering with shared secret doesn't work + Revision 1.2 2005/03/03 19:55:22 shersche ControlPanel source code isn't saving CVS log info @@ -61,8 +67,8 @@ IMPLEMENT_DYNAMIC(CSharedSecret, CDialog) CSharedSecret::CSharedSecret(CWnd* pParent /*=NULL*/) : CDialog(CSharedSecret::IDD, pParent) + , m_key(_T("")) , m_secret(_T("")) - , m_secretName(_T("")) { } @@ -83,8 +89,8 @@ CSharedSecret::~CSharedSecret() void CSharedSecret::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); - DDX_Text(pDX, IDC_SECRET, m_secret); - DDX_Text(pDX, IDC_SECRET_NAME, m_secretName); + DDX_Text(pDX, IDC_KEY, m_key ); + DDX_Text(pDX, IDC_SECRET, m_secret ); } @@ -98,27 +104,41 @@ END_MESSAGE_MAP() //--------------------------------------------------------------------------------------------------------------------------- void -CSharedSecret::Commit() +CSharedSecret::Commit( CString zone ) { LSA_OBJECT_ATTRIBUTES attrs; LSA_HANDLE handle = NULL; NTSTATUS res; + LSA_UNICODE_STRING lucZoneName; LSA_UNICODE_STRING lucKeyName; - LSA_UNICODE_STRING lucPrivateData; + LSA_UNICODE_STRING lucSecretName; BOOL ok; OSStatus err; // If there isn't a trailing dot, add one because the mDNSResponder // presents names with the trailing dot. - if ( m_secretName.ReverseFind( '.' ) != m_secretName.GetLength() ) + if ( zone.ReverseFind( '.' ) != zone.GetLength() ) + { + zone += '.'; + } + + if ( m_key.ReverseFind( '.' ) != m_key.GetLength() ) { - m_secretName += '.'; + m_key += '.'; } + // + // + // Prepend "$" to the key name, so that there will + // be no conflict between the zone name and the key + // name + + m_key.Insert( 0, L"$" ); + // attrs are reserved, so initialize to zeroes. - ZeroMemory(&attrs, sizeof( attrs ) ); + ZeroMemory( &attrs, sizeof( attrs ) ); // Get a handle to the Policy object on the local system @@ -128,17 +148,25 @@ CSharedSecret::Commit() // Intializing PLSA_UNICODE_STRING structures - ok = InitLsaString( &lucKeyName, m_secretName ); + ok = InitLsaString( &lucZoneName, zone ); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + ok = InitLsaString( &lucKeyName, m_key ); err = translate_errno( ok, errno_compat(), kUnknownErr ); require_noerr( err, exit ); - ok = InitLsaString( &lucPrivateData, m_secret ); + ok = InitLsaString( &lucSecretName, m_secret ); err = translate_errno( ok, errno_compat(), kUnknownErr ); require_noerr( err, exit ); // Store the private data. - res = LsaStorePrivateData( handle, &lucKeyName, &lucPrivateData ); + res = LsaStorePrivateData( handle, &lucZoneName, &lucKeyName ); + err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr ); + require_noerr( err, exit ); + + res = LsaStorePrivateData( handle, &lucKeyName, &lucSecretName ); err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr ); require_noerr( err, exit ); diff --git a/mDNSWindows/ControlPanel/SharedSecret.h b/mDNSWindows/ControlPanel/SharedSecret.h index 9982d78..c757df9 100644 --- a/mDNSWindows/ControlPanel/SharedSecret.h +++ b/mDNSWindows/ControlPanel/SharedSecret.h @@ -23,6 +23,9 @@ Change History (most recent first): $Log: SharedSecret.h,v $ +Revision 1.3 2005/04/06 02:04:49 shersche + Registering with shared secret doesn't work + Revision 1.2 2005/03/03 19:55:21 shersche ControlPanel source code isn't saving CVS log info @@ -51,7 +54,7 @@ public: enum { IDD = IDR_SECRET }; void - Commit(); + Commit( CString zone ); protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support @@ -59,6 +62,7 @@ protected: DECLARE_MESSAGE_MAP() public: + + CString m_key; CString m_secret; - CString m_secretName; }; diff --git a/mDNSWindows/ControlPanel/ThirdPage.cpp b/mDNSWindows/ControlPanel/ThirdPage.cpp index bfb56bd..b8cef51 100755 --- a/mDNSWindows/ControlPanel/ThirdPage.cpp +++ b/mDNSWindows/ControlPanel/ThirdPage.cpp @@ -23,6 +23,9 @@ Change History (most recent first): $Log: ThirdPage.cpp,v $ +Revision 1.4 2005/10/05 20:46:50 herscher + Move Wide-Area preferences to another part of the registry so they don't removed during an update-install. + Revision 1.3 2005/03/07 18:27:42 shersche Fix problem when ControlPanel commits changes to the browse domain list @@ -40,7 +43,7 @@ Revision 1.2 2005/03/03 19:55:22 shersche #include -#define MAX_KEY_LENGTH 255 +#define MAX_KEY_LENGTH 255 IMPLEMENT_DYNCREATE(CThirdPage, CPropertyPage) @@ -79,16 +82,16 @@ void CThirdPage::DoDataExchange(CDataExchange* pDX) CPropertyPage::DoDataExchange(pDX); //{{AFX_DATA_MAP(CThirdPage) //}}AFX_DATA_MAP - DDX_Control(pDX, IDC_BROWSE_LIST, m_browseListCtrl); - DDX_Control(pDX, IDC_REMOVE_BROWSE_DOMAIN, m_removeButton); + DDX_Control(pDX, IDC_BROWSE_LIST, m_browseListCtrl); + DDX_Control(pDX, IDC_REMOVE_BROWSE_DOMAIN, m_removeButton); } BEGIN_MESSAGE_MAP(CThirdPage, CPropertyPage) //{{AFX_MSG_MAP(CThirdPage) //}}AFX_MSG_MAP - ON_BN_CLICKED(IDC_ADD_BROWSE_DOMAIN, OnBnClickedAddBrowseDomain) - ON_BN_CLICKED(IDC_REMOVE_BROWSE_DOMAIN, OnBnClickedRemoveBrowseDomain) - ON_NOTIFY(LVN_ITEMCHANGED, IDC_BROWSE_LIST, OnLvnItemchangedBrowseList) + ON_BN_CLICKED(IDC_ADD_BROWSE_DOMAIN, OnBnClickedAddBrowseDomain) + ON_BN_CLICKED(IDC_REMOVE_BROWSE_DOMAIN, OnBnClickedRemoveBrowseDomain) + ON_NOTIFY(LVN_ITEMCHANGED, IDC_BROWSE_LIST, OnLvnItemchangedBrowseList) END_MESSAGE_MAP() @@ -117,11 +120,11 @@ CThirdPage::OnSetActive() DWORD dwSize; DWORD enabled; DWORD err; - TCHAR subKeyName[MAX_KEY_LENGTH]; - DWORD cSubKeys = 0; - DWORD cbMaxSubKey; - DWORD cchMaxClass; - int nIndex; + TCHAR subKeyName[MAX_KEY_LENGTH]; + DWORD cSubKeys = 0; + DWORD cbMaxSubKey; + DWORD cchMaxClass; + int nIndex; DWORD i; BOOL b = CPropertyPage::OnSetActive(); @@ -148,39 +151,39 @@ CThirdPage::OnSetActive() // Now populate the browse domain box - err = RegCreateKey( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\" kServiceName L"\\Parameters\\DynDNS\\Setup\\" kServiceDynDNSBrowseDomains, &key ); + err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\DynDNS\\Setup\\" kServiceDynDNSBrowseDomains, &key ); require_noerr( err, exit ); - // Get information about this node - - err = RegQueryInfoKey( key, NULL, NULL, NULL, &cSubKeys, &cbMaxSubKey, &cchMaxClass, NULL, NULL, NULL, NULL, NULL ); + // Get information about this node + + err = RegQueryInfoKey( key, NULL, NULL, NULL, &cSubKeys, &cbMaxSubKey, &cchMaxClass, NULL, NULL, NULL, NULL, NULL ); require_noerr( err, exit ); - - for ( i = 0; i < cSubKeys; i++) - { - dwSize = MAX_KEY_LENGTH; - - err = RegEnumKeyEx( key, i, subKeyName, &dwSize, NULL, NULL, NULL, NULL ); - require_noerr( err, exit ); - - err = RegOpenKey( key, subKeyName, &subKey ); - require_noerr( err, exit ); - - dwSize = sizeof( DWORD ); - err = RegQueryValueEx( subKey, L"Enabled", NULL, NULL, (LPBYTE) &enabled, &dwSize ); - require_noerr( err, exit ); - + + for ( i = 0; i < cSubKeys; i++) + { + dwSize = MAX_KEY_LENGTH; + + err = RegEnumKeyEx( key, i, subKeyName, &dwSize, NULL, NULL, NULL, NULL ); + require_noerr( err, exit ); + + err = RegOpenKey( key, subKeyName, &subKey ); + require_noerr( err, exit ); + + dwSize = sizeof( DWORD ); + err = RegQueryValueEx( subKey, L"Enabled", NULL, NULL, (LPBYTE) &enabled, &dwSize ); + require_noerr( err, exit ); + nIndex = m_browseListCtrl.InsertItem( m_browseListCtrl.GetItemCount(), L""); - m_browseListCtrl.SetItemText( nIndex, 1, subKeyName ); - m_browseListCtrl.SetCheck( nIndex, enabled ); - - RegCloseKey( subKey ); - subKey = NULL; - } - - m_browseListCtrl.SortItems( SortFunc, (DWORD_PTR) this ); - - m_removeButton.EnableWindow( FALSE ); + m_browseListCtrl.SetItemText( nIndex, 1, subKeyName ); + m_browseListCtrl.SetCheck( nIndex, enabled ); + + RegCloseKey( subKey ); + subKey = NULL; + } + + m_browseListCtrl.SortItems( SortFunc, (DWORD_PTR) this ); + + m_removeButton.EnableWindow( FALSE ); exit: @@ -198,8 +201,8 @@ exit: return b; } - - + + //--------------------------------------------------------------------------------------------------------------------------- // CThirdPage::OnOK @@ -225,31 +228,31 @@ CThirdPage::Commit() { HKEY key = NULL; HKEY subKey = NULL; - TCHAR subKeyName[MAX_KEY_LENGTH]; - DWORD cSubKeys = 0; - DWORD cbMaxSubKey; + TCHAR subKeyName[MAX_KEY_LENGTH]; + DWORD cSubKeys = 0; + DWORD cbMaxSubKey; DWORD cchMaxClass; DWORD dwSize; int i; DWORD err; - err = RegCreateKey( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\" kServiceName L"\\Parameters\\DynDNS\\Setup\\" kServiceDynDNSBrowseDomains, &key ); + err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\DynDNS\\Setup\\" kServiceDynDNSBrowseDomains, &key ); require_noerr( err, exit ); - // First, remove all the entries that are there - - err = RegQueryInfoKey( key, NULL, NULL, NULL, &cSubKeys, &cbMaxSubKey, &cchMaxClass, NULL, NULL, NULL, NULL, NULL ); + // First, remove all the entries that are there + + err = RegQueryInfoKey( key, NULL, NULL, NULL, &cSubKeys, &cbMaxSubKey, &cchMaxClass, NULL, NULL, NULL, NULL, NULL ); require_noerr( err, exit ); - - for ( i = 0; i < (int) cSubKeys; i++ ) - { - dwSize = MAX_KEY_LENGTH; - - err = RegEnumKeyEx( key, 0, subKeyName, &dwSize, NULL, NULL, NULL, NULL ); - require_noerr( err, exit ); - - err = RegDeleteKey( key, subKeyName ); - require_noerr( err, exit ); + + for ( i = 0; i < (int) cSubKeys; i++ ) + { + dwSize = MAX_KEY_LENGTH; + + err = RegEnumKeyEx( key, 0, subKeyName, &dwSize, NULL, NULL, NULL, NULL ); + require_noerr( err, exit ); + + err = RegDeleteKey( key, subKeyName ); + require_noerr( err, exit ); } // Now re-populate @@ -297,9 +300,9 @@ CThirdPage::OnBnClickedAddBrowseDomain() int nIndex; nIndex = m_browseListCtrl.InsertItem( m_browseListCtrl.GetItemCount(), L""); - m_browseListCtrl.SetItemText( nIndex, 1, dlg.m_text ); - m_browseListCtrl.SetCheck( nIndex, 1 ); - + m_browseListCtrl.SetItemText( nIndex, 1, dlg.m_text ); + m_browseListCtrl.SetCheck( nIndex, 1 ); + m_browseListCtrl.SortItems( SortFunc, (DWORD_PTR) this ); m_browseListCtrl.Invalidate(); @@ -316,141 +319,141 @@ CThirdPage::OnBnClickedAddBrowseDomain() void CThirdPage::OnBnClickedRemoveBrowseDomain() { - UINT selectedCount = m_browseListCtrl.GetSelectedCount(); - int nItem = -1; - UINT i; - - // Update all of the selected items. - - for ( i = 0; i < selectedCount; i++ ) - { - nItem = m_browseListCtrl.GetNextItem( -1, LVNI_SELECTED ); - check( nItem != -1 ); - - m_browseListCtrl.DeleteItem( nItem ); - - SetModified( TRUE ); - } - - m_removeButton.EnableWindow( FALSE ); + UINT selectedCount = m_browseListCtrl.GetSelectedCount(); + int nItem = -1; + UINT i; + + // Update all of the selected items. + + for ( i = 0; i < selectedCount; i++ ) + { + nItem = m_browseListCtrl.GetNextItem( -1, LVNI_SELECTED ); + check( nItem != -1 ); + + m_browseListCtrl.DeleteItem( nItem ); + + SetModified( TRUE ); + } + + m_removeButton.EnableWindow( FALSE ); } -void -CThirdPage::OnLvnItemchangedBrowseList(NMHDR *pNMHDR, LRESULT *pResult) -{ - if ( m_browseListCtrl.GetSelectedCount() ) - { - m_removeButton.EnableWindow( TRUE ); - } - - if ( m_initialized ) - { - NM_LISTVIEW * pNMListView = (NM_LISTVIEW*)pNMHDR; - - BOOL bPrevState = (BOOL) ( ( ( pNMListView->uOldState & LVIS_STATEIMAGEMASK ) >> 12 ) - 1 ); - - if ( bPrevState < 0 ) - { - bPrevState = 0; - } - - - BOOL bChecked = ( BOOL ) ( ( ( pNMListView->uNewState & LVIS_STATEIMAGEMASK ) >> 12) - 1 ); - - if ( bChecked < 0 ) - { - bChecked = 0; - } - - if ( bPrevState != bChecked ) - { - SetModified( TRUE ); - } - } - - *pResult = 0; +void +CThirdPage::OnLvnItemchangedBrowseList(NMHDR *pNMHDR, LRESULT *pResult) +{ + if ( m_browseListCtrl.GetSelectedCount() ) + { + m_removeButton.EnableWindow( TRUE ); + } + + if ( m_initialized ) + { + NM_LISTVIEW * pNMListView = (NM_LISTVIEW*)pNMHDR; + + BOOL bPrevState = (BOOL) ( ( ( pNMListView->uOldState & LVIS_STATEIMAGEMASK ) >> 12 ) - 1 ); + + if ( bPrevState < 0 ) + { + bPrevState = 0; + } + + + BOOL bChecked = ( BOOL ) ( ( ( pNMListView->uNewState & LVIS_STATEIMAGEMASK ) >> 12) - 1 ); + + if ( bChecked < 0 ) + { + bChecked = 0; + } + + if ( bPrevState != bChecked ) + { + SetModified( TRUE ); + } + } + + *pResult = 0; } -int CALLBACK -CThirdPage::SortFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) -{ - CString str1; - CString str2; - int ret = 0; - +int CALLBACK +CThirdPage::SortFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) +{ + CString str1; + CString str2; + int ret = 0; + CThirdPage * self = reinterpret_cast( lParamSort ); require_quiet( self, exit ); - - str1 = self->m_browseListCtrl.GetItemText( (int) lParam1, 1 ); - str2 = self->m_browseListCtrl.GetItemText( (int) lParam2, 1 ); - - ret = str1.Compare( str2 ); - -exit: - - return ret; + + str1 = self->m_browseListCtrl.GetItemText( (int) lParam1, 1 ); + str2 = self->m_browseListCtrl.GetItemText( (int) lParam2, 1 ); + + ret = str1.Compare( str2 ); + +exit: + + return ret; +} + + +// CAddBrowseDomain dialog + +IMPLEMENT_DYNAMIC(CAddBrowseDomain, CDialog) +CAddBrowseDomain::CAddBrowseDomain(CWnd* pParent /*=NULL*/) + : CDialog(CAddBrowseDomain::IDD, pParent) +{ +} + +CAddBrowseDomain::~CAddBrowseDomain() +{ +} + +void CAddBrowseDomain::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Control(pDX, IDC_COMBO1, m_comboBox); } - - -// CAddBrowseDomain dialog - -IMPLEMENT_DYNAMIC(CAddBrowseDomain, CDialog) -CAddBrowseDomain::CAddBrowseDomain(CWnd* pParent /*=NULL*/) - : CDialog(CAddBrowseDomain::IDD, pParent) -{ -} - -CAddBrowseDomain::~CAddBrowseDomain() -{ -} - -void CAddBrowseDomain::DoDataExchange(CDataExchange* pDX) -{ - CDialog::DoDataExchange(pDX); - DDX_Control(pDX, IDC_COMBO1, m_comboBox); -} - - -BOOL -CAddBrowseDomain::OnInitDialog() -{ - CConfigPropertySheet * psheet; - CConfigPropertySheet::StringList::iterator it; - - BOOL b = CDialog::OnInitDialog(); - + + +BOOL +CAddBrowseDomain::OnInitDialog() +{ + CConfigPropertySheet * psheet; + CConfigPropertySheet::StringList::iterator it; + + BOOL b = CDialog::OnInitDialog(); + psheet = reinterpret_cast(GetParent()); - require_quiet( psheet, exit ); - - for ( it = psheet->m_browseDomains.begin(); it != psheet->m_browseDomains.end(); it++ ) - { - CString text = *it; - + require_quiet( psheet, exit ); + + for ( it = psheet->m_browseDomains.begin(); it != psheet->m_browseDomains.end(); it++ ) + { + CString text = *it; + if ( m_comboBox.FindStringExact( -1, *it ) == CB_ERR ) { m_comboBox.AddString( *it ); - } - } - -exit: - - return b; -} - - -void -CAddBrowseDomain::OnOK() -{ - m_comboBox.GetWindowText( m_text ); - - CDialog::OnOK(); -} - - - -BEGIN_MESSAGE_MAP(CAddBrowseDomain, CDialog) -END_MESSAGE_MAP() - + } + } + +exit: + + return b; +} + + +void +CAddBrowseDomain::OnOK() +{ + m_comboBox.GetWindowText( m_text ); + + CDialog::OnOK(); +} + + + +BEGIN_MESSAGE_MAP(CAddBrowseDomain, CDialog) +END_MESSAGE_MAP() + diff --git a/mDNSWindows/ControlPanel/resource.h b/mDNSWindows/ControlPanel/resource.h index 9baf23c..3a0fec7 100644 --- a/mDNSWindows/ControlPanel/resource.h +++ b/mDNSWindows/ControlPanel/resource.h @@ -23,6 +23,8 @@ #define IDC_FAILURE 1008 #define IDC_SUCCESS 1009 #define IDC_SECRET_NAME 1010 +#define IDC_NAME 1010 +#define IDC_KEY 1010 #define IDC_LIST1 1011 #define IDC_BROWSE_LIST 1011 #define IDC_BUTTON2 1012 diff --git a/mDNSWindows/ControlPanel/stdafx.h b/mDNSWindows/ControlPanel/stdafx.h index 6bdeb92..7e63ac4 100755 --- a/mDNSWindows/ControlPanel/stdafx.h +++ b/mDNSWindows/ControlPanel/stdafx.h @@ -23,6 +23,9 @@ Change History (most recent first): $Log: stdafx.h,v $ +Revision 1.3 2005/10/19 19:50:35 herscher +Workaround a bug in the latest Microsoft Platform SDK when compiling C++ files that include (directly or indirectly) + Revision 1.2 2005/03/03 19:55:21 shersche ControlPanel source code isn't saving CVS log info @@ -59,6 +62,10 @@ Revision 1.2 2005/03/03 19:55:21 shersche // 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])) +#endif + #include // MFC core and standard components #include // MFC extensions #include // MFC Automation classes diff --git a/mDNSWindows/DLL.NET/Stdafx.h b/mDNSWindows/DLL.NET/Stdafx.h index 7eeb488..00a0820 100755 --- a/mDNSWindows/DLL.NET/Stdafx.h +++ b/mDNSWindows/DLL.NET/Stdafx.h @@ -23,6 +23,9 @@ Change History (most recent first): $Log: Stdafx.h,v $ +Revision 1.4 2005/10/19 19:50:35 herscher +Workaround a bug in the latest Microsoft Platform SDK when compiling C++ files that include (directly or indirectly) + Revision 1.3 2005/02/05 02:40:59 cheshire Convert newlines to Unix-style (ASCII 10) @@ -40,6 +43,10 @@ Initial revision #pragma once +#if !defined(_WSPIAPI_COUNTOF) +# define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0])) +#endif + #using #using diff --git a/mDNSWindows/DLL.NET/dnssd_NET.vcproj b/mDNSWindows/DLL.NET/dnssd_NET.vcproj index 719de8d..d857d4c 100755 --- a/mDNSWindows/DLL.NET/dnssd_NET.vcproj +++ b/mDNSWindows/DLL.NET/dnssd_NET.vcproj @@ -1,7 +1,7 @@ - + @@ -54,8 +54,14 @@ AdditionalIncludeDirectories="../"/> + + + + + + + + + RelativePath=".\dnssd_NET.cpp"> + RelativePath=".\Stdafx.cpp"> + RelativePath=".\dnssd_NET.h"> + RelativePath="PString.h"> + RelativePath=".\resource.h"> + RelativePath=".\Stdafx.h"> Implement ISSystemServiceDisabled(). This is used to determine how long we should wait to connect to the system service. + Revision 1.1 2004/06/18 03:55:11 rpantos Move DLL up to main level; additional integration from Scott. @@ -32,6 +38,7 @@ DLL wrapper for DNS-SD API. */ #include +#include BOOL APIENTRY DllMain( HANDLE inModule, DWORD inReason, LPVOID inReserved ) { @@ -48,3 +55,80 @@ BOOL APIENTRY DllMain( HANDLE inModule, DWORD inReason, LPVOID inReserved ) } return( TRUE ); } + + +BOOL +IsSystemServiceDisabled() +{ + ENUM_SERVICE_STATUS * lpService = NULL; + SC_HANDLE sc; + BOOL ret = FALSE; + BOOL ok; + DWORD bytesNeeded = 0; + DWORD srvCount; + DWORD resumeHandle = 0; + DWORD srvType; + DWORD srvState; + DWORD dwBytes = 0; + DWORD i; + OSStatus err; + + sc = OpenSCManager( NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE ); + err = translate_errno( sc, GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + srvType = SERVICE_WIN32; + srvState = SERVICE_STATE_ALL; + + for ( ;; ) + { + // Call EnumServicesStatus using the handle returned by OpenSCManager + + ok = EnumServicesStatus ( sc, srvType, srvState, lpService, dwBytes, &bytesNeeded, &srvCount, &resumeHandle ); + + if ( ok || ( GetLastError() != ERROR_MORE_DATA ) ) + { + break; + } + + if ( lpService ) + { + free( lpService ); + } + + dwBytes = bytesNeeded; + + lpService = ( ENUM_SERVICE_STATUS* ) malloc( dwBytes ); + require_action( lpService, exit, ret = FALSE ); + } + + err = translate_errno( ok, GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + for ( i = 0; i < srvCount; i++ ) + { + if ( strcmp( lpService[i].lpServiceName, "Bonjour Service" ) == 0 ) + { + if ( ( lpService[i].ServiceStatus.dwCurrentState == SERVICE_PAUSED ) || ( lpService[i].ServiceStatus.dwCurrentState == SERVICE_STOPPED ) ) + { + ret = TRUE; + } + + break; + } + } + +exit: + + if ( lpService ) + { + free( lpService ); + } + + if ( sc ) + { + CloseServiceHandle ( sc ); + } + + return ret; +} diff --git a/mDNSWindows/DLL/dnssd.vcproj b/mDNSWindows/DLL/dnssd.vcproj index 3975048..383a90a 100644 --- a/mDNSWindows/DLL/dnssd.vcproj +++ b/mDNSWindows/DLL/dnssd.vcproj @@ -1,7 +1,7 @@ - + @@ -33,7 +33,8 @@ Detect64BitPortabilityProblems="TRUE" DebugInformationFormat="4" CallingConvention="2" - CompileAs="0"/> + CompileAs="0" + DisableSpecificWarnings="4127;4204"/> + + + @@ -79,7 +87,8 @@ Detect64BitPortabilityProblems="TRUE" DebugInformationFormat="3" CallingConvention="2" - CompileAs="0"/> + CompileAs="0" + DisableSpecificWarnings="4127;4204"/> + + + + + - - @@ -138,6 +153,9 @@ + + - - @@ -161,6 +176,9 @@ + + diff --git a/mDNSWindows/Installer/Main.ism b/mDNSWindows/Installer/Main.ism deleted file mode 100644 index c430d40b181fea53399446cfeca72269dcf155d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 195584 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

n}hsO~0UTS`Wc8qaD z8SXB&xMsR3ln$;5*-5ax*;h9>BGW$M#JP&!7}#tNYwDapt9>&^zjTmNr0IGlj*kyA zpUHaD8)8e@IO$5d=m{Pf$=g%9@GZ7BszqVLX-dR5(wvhluN~}!jME8CpMMqSbo8b@S07l%5g-Az5ih0ix7x%Zjn@ZOmP59N@gdw^x3 z2AGb_Grdh5{kW0iuGQJ~r{F0V(tv+8xLbY|>|zL`e%l!ygw)(b!fBZDxzV-@PxnbW zlp-7Bg#wIGl(FKKjGT!6QCs!SjH5LnupLv(X2Wi-e$=cQZAUBu1+fC`z8slM2dT|R&L5`}Y;5yam5Eb0R- zgq)HP6RWC_NP$WjIefLv+LCqWYIExb(}6npM8yNm0(ChP2ICjf%o!CE-JcG|XJ>-g z{o#|?XJ}4?-bE@1G3%jdqnAW`GU(I3mw@CrgpydX;)ODs=o$4Ad56hZ$4YOt>#ek> zB#hj&ihDOy9+9bYF*OSzODyb}^mY+%7Tm*rW^Z=f!F?e5e}*+DFN&;ok`QXTjD?>X()0$qjMH*$(St_6%tJ+a(J$dGhnRn1^m*o~9KQ2mSj;L1DB1C&!IgfekqSSeNp3+ zm+MPqXV~W?&M*dY*$qv>A=^O<>K4?#72M2*e4rG@nDKw*<=Q&JrtX144b4zl$VYJ} zX|0Kq0uQk_>||4et&6?SasF>c<)W(ojrOJp77uXCUnGU1F)O`xRi||FDcTo5uZ{M) z`X->LO4qKkTxF84>7?;?q%>R``220?!u3Iy*X4D8<)H9FsQ69BF@0ZEM1tdupEME& z-=glfE|It{T)|Xnnn!F#{ET+3K^TXlSjg0kBKT*Zeu37JZXEFLqOazGR)AYKIEq{Z z@#^hh&y$CcZ*cswR;&Akk76kfqnBzFO{TZnn?9E^XPpL3CATIs8Cq$ff2$YlusDc- zN{BruC#|ohjI0u3KaGp4V)-`WcbaX-}afRJDB~cm=7+jh8#MUGpX}TE9nJT^XuBjXQ)9t#k z8z&#z_8|tWXXZVw6bIqydd()Dq0XvBu>)mOLDj{D+bsxtpce=XMa}5}78Ww*q{?WO ziOQx#5{cZTSG{{wpN!uvT#6AR58FC$T(c&ND0TKRq=SAt2c0_hd+mt-sWHPyP#aG5 zFP)w%&vJlsdG3tHU&#gv>(dzC^S7v>DA_EiZeoJ;V6PC^hm!aQVOhjX&p5zzaQR_j zl&!|wv@j2$3)Iq{Srt!de*}%c@)YO{HG%ZViYuM9&QnqxowFw0a6B*I zv%Jp}Uv>r00V?kWKyI^u>5m^vudjx?q)CZqnyUm-`rR zDue6<92=ddr;z+M9n*cpOMo8&(Rd(TDAp45X6I*rAe*Grjr+9up zu~ml$uuxEbIx|vj9vIy>9bWz$D(|KURW7Nm%sj~r8Kb2X!X0?ybJWv^B zE1>cVKe9nkJIS6vI1ql+U!Da{hIipqc$A;Q3FnF{h`R90ygvXakC(|yI9EHXZ62qV z(x0o(scf;G)#fT!><22Z-qlX3xA3U*PlXf35nj|Egj;aVK5!U#tZUgTZpi?w+AnV_;A(V48WE72*LUO)B0 zH9&SSYTK^?!kPNL#1Ym( zA~;yz{ERSBKRNg;<9ujIQ#=guE}C=LO~l+``w_=$xuTIK~HLd&loC0PBoFfe3_xx(Zw5+hpdy;92U53W0v`H*aCJk?i8^1tkly5G* z99rt)d#(OffMCDqD>JJ*Hw*M#GJnMdsUFRbCc`MysJy$yvV`NIGRp3OsJJ{9(!)4Vn z1QMh zQJHCq$Sb7S42UtoOcGq_X?a;=_Z_yjniwg)?mcacSf#ipI4aZr-MH_}3mnlX}L!i%>{t251+qziV^D?DJyM?P2>@o!*lcBqXM7Ah{ z#s0Qy=Fu^ef_GZJtXfvWv_j^9dQ3@W>4UL4rZgcry}H`X2ENi=r*}pEUfn5#WZxs3 zpyf$-{keqKqed|mBMzXiF9*Tnt3KlzX?C2 zE^QOLP>4_qf^$kjR#M)n5{nSqSOKTI=>e=dX*RY>RxOH-?Je7Bm^vyEzj~d8yIZ-} zoW48N(ROb$BWr6YYCu6w*VnGsG*)bc*q}7ADN8*(kti{+x7uu6&+BcrEqKD)%l_Vp z@m9={zS3c1m81eOAg%00 z*TeqP%=socT1Qu=arm*8gf^S)PQV?Q$ac8OH_l3?@k=yeqITUr8>3rnjDlgn%s{va zGn#2r)I>K{QTmBkAe+&rkIDrkY@MFEdhVUh;6~itgq`^Zsx0E?q-`ynb7(!KiR|BY zv|I02#j=(7WktCh)9?(P0Eo&nW6O}K&%}+0F&5CIi<{C5iUO2Nrw@j1EtQ`*c}9h{ zKAFaI3|gG2k@8xyML}DrV0O&l%IwJ*8g^UFi>3LCm(HKNaQ?!@(&<-g%eA$7b%R@; z*!07W+FAddF;Sro=TJ0FaZEkgfZRw7*vcgfad#(ccd@unyna2)#nA zF*@V>aiBp-(CT8NzEECbkH7Hl%jsp>BkbKtibXfUgbOJxHlrA#1qC1*_e^mkGs6Q^ zx}}&@&E&xCP9`k;!ee7}r#X0Vx)kz=#G#>CTKcv}8Xzx*^qE9@LG%bk!y z;mlJkIguHHT;)<~ZcAv*mU3!4nGr~_t3};J4l{tby~-oUiiY$xuc@(E(W^ z-YB9z3Jo$XpMtX6=w6$aAOL-}IrX?CcKS zkaHq6 zR65<~irtcHc#w0_Nf^wT4(m+Xml!=sxN)Cp$O#=Tno-0)H(4{aDSVUGMkQ`U<2(q% zL!7sxoK_J{ObaH|5mi9}J32>m@nt@~oQp5#69PZQsxj}<5aj%v;%$wXz-@q&UGkCb zgp@;lJIuA{N2q%>HT+3PZZ zu?(krTUzu%pX$^b!uf*bkLKkYqIFB=Q zHXW|3Ys1x;p~@hb9`R+vSajg9#O$c{%a!XMfRr-f0$)(2cz$s)c# z$lu;uXG1&`Q+T(oln0%_Z0=>V-Qn0x0%jOynZ5;~;g1w_*X&&J!zHL6R=;IxwIjl)Yab1v}_EI_f-aK1UtV_w3bK$Q&?ClmVLaJ*l& z3pKQ33gwKNj{~6K+nLTK22I-67!yC2Kvs(rLA7JW_7k(LcYfOQiDmcwUs|Lv%V;@E zyZY_LWG6$c9SIjT%i2;%TqVnZmBkv;2e&X~^OZW4q!MCFol*8oqA&x34RhiG5>Ayp;mXb1I5q-YQthKY)Gm3;Ymu`&26fj*R|7Tg^QfJi}*#0 z@8OeA))%+~d3B?*wpv|TTB&sVHDpyvndlspK$NiDHA6nDd4Bxs5TZ3+R zmjAQo*PFYDKrRJbUa#vQs-i|-ZI6`*W&}ew1MG50jBk9bHjO!YYwjJDrLnQWD3jQ0 z84i8J&sBKx$;2%>O@uYe;%2uHICGl_9h#vKI=G#iU3lYqqrSXeTYJ5-XotLbQ7dbq zR@L1;M~WcCi1$g?Uau`;>atw9vRG5(tDJY#xVBKQEUvuj)k30QU8}FIZ@gYxYjE{6 zCz3YEL%xUmfQ|{@mBocAad4FwbY*3IV|8V1ePem$$tUSftCi(9HXWU9~l0*3&f z3m;KA6F${?Z?0Tl+n{eRT(43f3K9Geql)AN=_QUkQjtmWGn;(p+S4MlBnRNuZ*J72ka<+<833l}Q$&z!H#&OcMRGQaT5r7H{1U%XmJJBjd6h`PRID0@?^lqL8O23jEKax*^(!`2n7k*OFI?`BjeLkER7FRB27)utc1KM0 zvhy}c8h&`+5tvb%mfiNO{fn%vZ0CSzQ&ysU&Ve#Phhqs_c@QQFO`T4>6gh_RH6+-( z3BAozglx;mCo4hMlECV+hKMd&&1~DxZN4Osnm6veu(F;j(M0cb4#Lwa%7yNpbrI4mXA$w*`Ctm z#%^M2Ih}35oY+IW%NDw*Q}fv(6Ew&2sW!GqCpTXKHoM^ln2q{y37IhxPZ?=1baytg z_V^8y(XZSn8tuX%D>-|MY8arO|ducYtJksQR z`P3e04zs+@XUWxv6P}!ey+YRxhrB3GSbp6Rj?j?D0WgprNb8lzEDUV%huGYjD-&@I z1~(+(qWNUA@HQY@3Y{IGd*x*FAX^IATgc8qwj-B#>Rw^l9muXhb{F!~9e%Q*kbQ(~ z6J*<=^B!amB0Gb4&Vy`66i)URvY`;jO>txgp|l>sRsM>jn?T(*!_T3341e8qeT}DJ z0GuGikY^82XxVzGe7Xlt@2Z1rXH<_b1G2wZ1nwgroe81zWltpA3E4=fPO_6wT;ZX@ z_Z{G?z^?}00XFfg@zk9cs)KAul;$@9x5=3~_9fn51aynKaH;yJ9Oo>I+vUinMCFtn zi1J+mWXqvcg|`$`U)eyZb!5YFmUrPzX+8oLRbJV)2%oBp&NWdO*@LLgy1UtJVRV0o z+Xo5fYAcLHtzXnUhF z3upIZF(X_lp3-jvN_!6YF+lxKet!On-@J`0@w|@w!BD4a_Mf-efoTr zO`AbJ8KoUmAC~!=#ZXS zaurQYCcV|72gadrrC-9{G^0@37Fn4*)z!~1Bi*5|{E7=Vn|vEl`06>?>7FCy8>~w( zEY)XL5u@-jq(`Kxm1(97VLLj??u|S>=2I!l3Y*mL`ZUmDep>5&?+yc9lYY*0hGC%~ zru1sErcHZJ(yJ!Ubv10W{DjXkogar#h@+0(=m_r*r0_ENYFNwr0eMH{7^)bQleUP< z=I2gl&*o=GNI0xey(~YF*YZM|lpO=yveOu?Pb8XoMRJzV(cz(t*^M4Fq4;bVTD1Mz zc3bho@^J5Tw#nQ}vEH=n46XYy98sCMnncnMPkY*#y=8VArY)E;u}SmUKuVkTD578$ zP@aJ#`*})EDJ48Jbvp(-YG|0(rpA)(R`GK zxkXjSV~bd9v%QqNZdK@PBRf$iWtTaXFaf5wh{VDflVFKpbQH)zC}Kc;KvVeA+RhNA z7n4hcrdi~i<`r3BzN+)Pvh!2Xk6Um}9rdV@OXrc0E|N;u)=lwKRug4IXv{`GIk)XD zRfb3?cd$+aMOLpBW_ay}w0bS<9F~My}2jAH--^kn9R!B9}t6Am|SvO3%2Q$e2dDF9FS7 zne)f5^+)h?eBHv3{0)v18%(7dxFa~Rb0Tc&Dv*wh?b0}4@TzQ_J~|kYz8ihehGrKV zh=?^q?7cA{$b@%^!KRiX9~x5#SWar)uFdVDG;QuMYMs3mry*(stO~gACTGNv@oMG+HqVbu(|Noo;6(F8K3tOH;0}nT1HL zhHjZc!SF=egu6V#Dk3QTHAc!@x^VfqNZK+vpoyF`;l+toBUiiX?h4u z&t^ZPTcTr)*pk`81i4#~ZF5w8eCzb2K*hlx&SQA7>2c_Xa3FGc6!U9{vrM_ z*^@dEdBW4ypE2t=kR@@>W}b;OjtKWC3mvfTj z0+UkQ!y!jsA@Aol9zG?|39Quus;vb?Ksed3B?1x#pJc#<)b!#9eu!&QvZXE42x7Dy z#N8o4WWx$gN*kJ-xy-IlU+j3PLezMCDJ(R*&PSzlb4uNAZ(%j9Eicwf3b@acG(m*< zQGU0ur1097Lxra+Cf_fS zjL}QX)pQJjaRkB{RbuE$`wXHKJr4zb()*So8Y7la$BQQjo3dQ1A27f6DpzpXX-B2g zYtn?js$D*3OIKtJWfvUHJ~Mye!r77nzF1l-eX%ru;o=uw_2<{;zrcYD*xmK_bVihI zEg~ClI_-ohR|XsoYkw};-{F>KUYEKyv&Rv)ViJJNF2vaz)@jj--btMm%GTUakXF`i1p-j6 zY-+}o@Yj`GWLEp?x^!D+y;N{Sul4Gcf(yE_*NQG&cDqz`LE*L3?j3kWh1h7j%%??{ z8gV5?!Be;0n{T&UT`2mX0t`Njl9LoYxsWz^h9h$d-obY)#gNrxY9Xg-DP_vWfH7$p zkwADgr&Lx-#@v5um31L+7G$i5x|=uin=Ghq>dm~T6<0U+DnQJ*vZ;12;6``(7J|gE z#ZVwC4K-d)P?ma6DJqytLrg2}yd$%^P}!U)=DZ^kzp3S$cO+I9#tNppt;x2!Fj*qq zaxJf2wrNOQw%pz+RC(TUm-(;uhedx!oT9F>5@)_GdOOA}-)Z%6jJfypDmlh1#8|D) zIik$V{uFXP=cqD|X=py@xC&lV%686?76VKz-8siz2s5P+j=kU?9>roh{(SWw8eqH) z&N&hbVaD6xoMSQ%Lx72N9G8VK1>d;423*m>O-CQI2$OS(Yme_urxD1EZ@ zjndIRjK)xU$0a~I;|~G5fOPaP@$3SMDSd3zcb2hIZ0AHR;qB=U_<0crJcoeFp>RJAdP(iSM6 z6ja7n2&1wnu0T3joeLpduRwTx0Bp$~KxI%I;a~^&{lE_cs-w;#xu2&X>WhUJy$hso z7U)C>ed|1qhjGI(t|6!p7$K`jNxKFDy#BSS)y)S@l_r@<*%p8si(q7kFBTl_Ik=M#?>>1i~h0| zNd5ILkltI*@%X}x+*MXRV}9`|9LhaD^`3^0b&KI+d18LiJ;safhdpC@(O-54iW}<} z%b|SKX0abedm7na{78Tw{7< z(pE>=auzE$Q{K;6+^`Dq+=H;?pMLJP<}KVgtli$KcU+?R;dgCh>MW76D%rZr@u#Y1 z?p}Xi?1LJsUA_Up@kz80Uf6RcKV%vfH6}wDn2yx-yvQbeU8A-%#kblcYSKbGdV?VC zv*QAAJjEvDQ7=zG-ZmP=Ry>hUjZ9nl-aC*G1(jt}!*xLrY4^k!aeIUXc-nN%M!Dy?vHQti!^VPm_eivMVJkcNB_Bu#_!KgO)rl>|&Tq z0?lC$`4w}Qr(RaJI~wi3@a(gOu8plT0RJ`HRJ@6!tT?7_aIg)$q+!$$-7 zxo0k1xL|Pp!t8~j|9Wk)wt8)4xi&Rbt<&@HsWp6kVWY9WhWYkf!Jo@rE9*5G(Noub zjID#O-TS)!=4x%jP*z$%LSuPU-XE5}zj9|HEY}$ox4*BJ_ErLoy{THDy`qt%za&m^tZez4ZAHl)&6u ztAqYGkFOp7%(35a{M-yY+02ERi!=WiM&bDV_uT)X`~TF3UY>bn=Bvkl?B1^(ug(1U zy?^|^wR^74eC_yWj<3wD&OCAaYsWcz>iAb@-X!ijGhd(Cm{~l2b*4SDJ+m{jJJX%{ z^nGi`ze)H} z_pf`8sl=80-n#GjiTh9J`8J`}j+c&o?f63{Jj~aQKX&44#~(lO#EB1|SUmn$@Bh>Z z=Fbm(`ow2&eUkS-aNiG{cF%oP>gwt+-95dtMn|KYkwD4EiLZgbhS1$0J$o2O8ry}z z$Zd!*)EH(AH%1u88Y7JpjR{5*=_F&KQDO8|$BBK^8bSgV+^$Vf8wwxjUm0HmUq{?5 ze`9=W#5Tq@Dl3)M%Ie+gr8TkjyWs|G?71PaVZcUm6r;PeX7_q!bpcH3K#Z8+NbH-3Yf*s$@=Cox34-W6sK4NCkbJm*e^zXhQOKZ^$7Xd~I@85|4CM z&y_u)ksrGQq4fhGkN8M)a)R#$Ag_9%yaucnHsr3)-N4E?0NPWIVX36uZsg7U-J6L< zPO9t)nWSByd16B@^v?A(B#E#GS!(_6Rb(H8Hv8#U4p<2a*=iO1l}vi>O3G*V`kw1^ zR_34#JvZ#$04r6Zv?vR?94}|2U8z(ISYHo%5``pF%7iD6a!ah*jWUwl-78OCnShl@ z>!4l);zVjnOSXS%CGz%cMZQ$&VS(Sh(knGlk)!V@1<}^$0=FAEkQ3mfN}>P<8l-vr zEImuj!<25oMrAd$SefhDb2l_a9CD~6q!(#}wt~84ccP=*Xe(YxJPBTItEp^W`H&`e zbM9vFp#&&B(t_J>Rj3J)RtcM~kHN8c&Sokrk6OW5dcq}s|w?F?8S+sN!fwx>Fiu0jYU#@*{rWJj_d zS&qu8Aw;$U4ziIJxWIWB#3?$L=79rZS96=>0@Lny>YwbyB$95haqcR#=~d7UJ=V7$c&W5rFGU`(!{*)4 zS-L8ykDjmudM#NC645irhUn12C@^59unzJn{q~5q)EpM|(!zq2j%<$B z&+_sqEG}K8R7jQ7$1z$}Lh5>qC&*7)!`d6w1#XXU}Enw9(G!9dnz$lH8gtSGOHrCr*52KHA8PY-h6qD@@N6CLGQ9=?(4#9E;(nrKZy%GyLB0 zH2kYE`{5tX*3c(;*OwG4lZYFH* zyd1o-3%#V9pHhxYN!okg?Zz8Ad?o>lg?#_*4uanMRCZDYhjH;W5KP5p#Mm!SBdm17 zn*L%3lWeSQ=xj@yOnG(i)Z;H8=kp@rOo;af+8K)3u}3VYH@*>=Nm{2IPwAsF(K_ca zJ@GN-Oyn-(F}d#5yVt0GeFu)5kcp!!Lk3?CO0DlSe3mLt)^vITzp~Q_etoAC{3=f) zWW%@5Qif?!RnT2$yCLOD#%;{K7Wu&o2l#x`8CLMBy;8S-PHL59cM`m>zGH7=L2i1bFafQZ; zj9GU|9WngD$Nzx}tum9MLSslqNvttuBz5V7PoLs|P$5n!O!YLXre0VhO_ejE2!{YN zwkk$xhmp$rZ<6Ezu}tRjc{8;#pOmR_GE%16W0Xvl$0(T^5uEkp2eI_-si^+!EOeX5okB$y|>C*&fc*3x+>{G+ZOInUTBj-Jc_%jw%?*tdN z2)xs>VWm=^zx*%}|C&X5;Q}4#x3mS6Grsl1&LGY9<^b8t);YDQ@*1bNeRSL>Lyu*~ z?vmq|Br8YOCO0--fNeGW%r;w6&XBd?aE7eyhcmRFm%|SFuoFJ>eLU~fei8gXO<>a{ zuc@Tez@F6XKwF_HqMDCv;ZMHUud(;OrqLL()-#Tj(DzdDHIzd7bT6CO&}1TO?Q%0@ z8eF|<S0=zDbH_*C_!KoohX_8Y zNfe({I!efzf}U%`@mw1cey*{RpAF64PrH-FipouvE=ouiGD^r+)}$>X39@CKE$3_* zXUmsAV4`K_PZv4^$1K60E>TY2&+(Z|3_h&hf`3?y(IID^G+^Ns5@2$B;m{U+qPdh# zZ-u|rjL$82qZ)cyKAY!zL(|cIzYIjQ-;;=xZoLO#k?AJO1;-BYw^nHxcgz`vZFkHv z2bKofbPDxWo)^8QNl}7#WBh^($K<^po*cTvh2?zxFs}3N9%R)y2VVk7Q-Doa!zv}K z3V)teG!rLd=<5+h-lr0H*I^s#DWrD%z+Gssp3ej_u~#Q5DT@xja*flqX_uQr|KnQ? zL;IohVR!E*2AG;9Ke6PWfeQ}Xm3oDwqsTalTaCO9846hqJMU(h%HOF+GE;sSkomt-;p;2EYVZ02|-{TmbRZ zgkK9#+mF|UUk^|pu%C}>0RI3#d=UJD0S*25A-+$t8YA2U&=kCbXb8Kxd7rJ zINcAQ0e^uXR&na56|C7&_)Pd*_(AwX*ewWn)8dK;i%rC1#j#?sI7^%-E)dTbw~8~R zNo25>JRGgs-xA>8fY!G)>>Pwqt;D3P8+A? zX;ZagZI(7qo3GuWeWrb(?bLqKe$`~%)@$kY^(K0b-cmn8&()9AkJ9_w%g2V^8>`=4klT7YpFK@aMp<0e>-klDz_c9r)+LuM7Wn_|%W@hfn!^icA{8 zr+%ttaEG8xP^+MJ!CJ;=XdUD}_uLO?C(QkU_QKq5v>WD7w2(8=mZ&vF-N9&I(z!dx@-73Nx^&!oE}y?)daeJRxq_xcv~u2eS-y^Xs< z^gHHed;RZh^g(j#GP*0!JIP&>!QF&j%iS&Lzk$08eL2wBS+wV$0!nZjuyB&7+sia zfKev1Yw3+XtuX@87(}BGjYHYpaTud$lwvLqqZN%;%oStYqLGWac^JWH4Eww84vc<( z-O+f-+)j*}-2LQ@pub`iWlq)uXJcHYk(Ign7-7?06O6jt?ggFlIA18e=z&-ZXwQHwEK3cQY`e)0ocPT#W9_B{0e-FwS$g93wt= z=XqoQI*k9^eTXLkbD!afkm`QG6C!Yb;Az1ehFsriczT4LgJ%hIHStVgu7US#IS9`e z=9=I+li9V$^M{^5+1y|}n^N70cvdloS4AmqsrLliiKkfL=zB|e;9=yaGrkU<6gA*t z!l8mGTH+9KDDE+g=U)ihg&pu4isyvl0VN0tq9{tDEGnWZYT|at87V4wiZm2lu?Eg% z(5Z|%kZ~~1H#EWtiKb#RjQt~VZlVq_$BQT6l*V}RL~(*hCnio7^KgcP=%+%8CC&!* zbn$afn>vUs5EEjBxKLaqE*2}rC1RDImzm6|8OHgw;sxS5@j~$;@nS#6u{c-3q>|=q z#j*0|LL=lwp)=%hkgC86+l5!<*W~TqNs>B9zeD~~-YI`2e=UC_e=C0{@521%d}W8w zMd_+^Q@Se_?6pu?qzr+UBc(HyvRYws4^|t3(oD@!55ozM?I>{q_WVk2skTyE zt8LUHJo*=~bRAe_oY+(CrS?|)sD0HT;t0TKHRhdS>4ou~omU~7^zu%wOb3nE6O}B8 z)et6%7pfPj7pv>lOVkbOrRrtsci?I z7}uXrn~6`Vif7Ab)g3q!^OE|q`ilB0O50a`8~69r_mlJw)sNIu#jWZ#bw7c(mS(7z zi%~P*s;u>_6~9AxKhz-OI92>seO3NL{ZrkYJeNdPY$y`O)kK8*LbE!Wg4SIJ)_G8+ zJP*(g)DF@P)*5P!w7zO9jqKJ+YooQ(aKzmSoNPAd&M0-?wOnY2=LVHqsN_$#+MtfF!PJ13R;Zvd0c5R0?6fLB$x>Nf~ z`x^Q-Lm9tSf7O1|e%Jm${8X_s%BAS4j_;G{j=n=^qt@2z=${Mw>HF&q^aJz*^@H?- z^@e&Q{SdvGx}V-uZ-x>5F#QW5Pt3vDuU~M!2_u8wpDxBcx4q6ziBX;Kb7IGfbWV)- zi}rfj~(TI49Ou-L6#_4TTBb>9D>kJ1a(~#A;*JT^r+W zsv``iq|!N-@dqh0g#sQXgQey2IF7h@YR-|WO{y-Om!;y;f)pIXWHISMnjkMOHJm38 zbDvA4IG04BKpw|)ZCDD%&pElYzz^i6rsFyVQqptgQW!5!!SmuNnOvSvm@Ar&%L{x; z6~t0Q)zMNp1Iwi5$YW7Sgy%FSDbPNk5ryMZ^J8fPI?tcWV$ylqC^yfU(V3J~`8++x z2ApATt|?JDCrcT`f>4x)r4DrDA(jJ=?VZo{<=hnK@&kEHm#`jO+W^NUu=HW=8Bh2g zPR;pw`qY$^il=0LK<5;WNvAUk&waFmCzJYNw|Do+Sq*sSq(i3o*og8aXF{l0tmf(&UA9G8i{w>U1t z_emx}^h!h!6c0m~O!&WjiHVG5<+e*i1V`f$@*bn%^b`&QqG?wm4Fg0}=W6OXV|tRg zhcuLqfL{p&2(n#?Y=j7YZ}}q0_9_~c9gXuc6F!ZW1qbwAU5N}_NnEDB!TGDBah`qt zf87EJXh0CX5HjJ}&x_K9(`E~& zJ=>8k)0GGzDa?UC<3i*hSWUm7^#9V8MRo#03Fu0AcXrDb4zaMH-$oL=t)wwzK}6?B zK3gISJE4gd21L_zCVKQr0Kp+lBivqHu$^}ib1XH_y-H6^7!eau<}fZC=6rjL)hV@g$&hVg4xZ-qJWXwa{%i5D4PEX+^M9ReFP(|`^*bmDgd^FB^ zv!(aa9FlH#F3Zy^l=l?W5doJQ^|R&wcln~}_9h8}!$xHoRUro%@`R(Y z{#Bp%>F5~B@g$#zxzF>&NvZ^eqtA~hoyY&T`q4VeR*r0Z+_pr|#*&wGo9xj{XxX*dRK5v&{9EG#tqkPrHGs&QG(naaQdfgZ5 z+k3JprKkSRUJ`1*xHyjdXgo~YQ+Tk^ z6NfJc(?QGR)sl{5{_X2vm^rx8WiiYGyve3cwKavC(vdC;BG@kU_j-G{cF5=ZshUJm zvxEVig%*gA>R*4Rl%FBlsY=d4KI7-M>Zyjcq%hB+rAJwZ^It4A&xhNB(HV`)pjzmu zw)bj-C>+nYc%Cfw&$*{+8P+RGX^Ar{b?A6+HP;Tg3C=iu=(oNd=;jpu%(voaI-?Zh zWMNoHOO-IcXKb_C@@xqSbe^`GX8Y2^5>vz7yxce+_rpBJ;4o4Dik!q14o=F_OSAlrM!c)1z3%TYdiYWu5G$TQ3{nT)tZ^o5D!mDJ_q9E zl)y$z0<#)V#c=$dEl(zwrR2QXd?uO8=YE)*Nn_gbdzk+34QJ9QJ;%M{N>9fy(bUm6 zPe=G@{V)v21}PaYPZ{JB<_LILh)W^vfG>!#8cn4z$>DpL&cb0#X31gfp3{VL<6)+I zYEGP%*=ME6Oe3tAsOA0Mkixa-hEty8YT-OPQECYzM7q=O3kg0)m^gVnQ%DyLoIoc> z7s(=EA(jW>nRN1-fV-AJX(*=q`RN{(QW=m(be4+fqz9AExG9Ig5uH--i=^{1uzV>; zrVo=!rK$y)EFI-VapG+#yd^Wum^3Dp(HV~bOEIaWK`o(5VJV5q^a{8cF3^@_c2-j@ z!=-X8X-cP7DHQ?Jk(G%o!?a}9W_02SxGDDExtKQ0Vx%u=o~kFqGCPrujNTU(VI?4+ zU97yda3-C_DGi00bi(yjX=cpiHxwj|m78>+v@Awp&cS?2&#{3|a#_k$>rsqz1tE?h zpRk-F`@B33LNYazODU)nJQk)$rKZYeoGc~d42J_vxb+D`J?#gLY0($I=Aaak&Md%E zvodo%nIDd&V_1#Roq8m*Gue(yqB`;NL48n<=Jm*VnWZ^D`AyLGn65$Hk_485aZ;Q@ zEEZ5G&hjI_iHJk#;AE|YB(Twf>A_@p>}YosqdWE0pE28I;|&ipjfj`%lpi0H0uG)x z)kH$h%!4@5Hm^d8wFNK}LSHjoAH^Lh6T=Be6JfH-j)Uuu> z&Qa&8^~L?ggT#7bU2#9Lfp~y;pm?Zwh!_(ai#g(9VsmknI9i-47Kw%8ByqAhMU0En z#OY$bc#1efoGF%zC1ROaDxN0J5$B5Y#WTd^;#uNK@l3>)ip#_m;wtfMakcMQUK^4* zT`687t`{#AFB2~puMn>ik5?(j6V!6Cjb0}Hq>htMl#iEBkQd5}o=OkpNaZNyXl1do zM5$DkDreyn;b$u=lvT>P%6ZBf6)u8)Wg;GYIn7Z+DYxG=BjPg zc4`N;o7zJ?Nb%t7?26aF-4`Qh`@}G(=#SG$6^)YXhep-DBzcGDYeL;OreG}yxs=lVa zuD*fNzN@~2`#bPC{bThL^$Ybg^>cOqP#vyE&3&itQdv!W58Llje^Gx_e^!50e^Y+~ z#nu!})ih1V55P^$(rRh7wVEELhE`wOU#q9>r`6S(Xw9`8?NIG7?GRvMT4SxL)=X=m z9UdB0kI>p{9kec5S8aecP#dHT)`n<9wPD(DZG?8LHc}g9!$7?5mK0%wPour+t z#kDEgWNn6)uT9rZ(Mq*4tz0`zo2$;z=E6T+o1IpiuS7Zn)bT( zhW4iRmiD&xj`ps$MSD+sU;9A&Q2R*xSo=i#RNJa;(>_OuzVu4c6t}Rv))bbuJ_QXPI!Iv(R=B=^#S@2{TO|aK3E^957kHM z)ZUNPN9tqr)i2OD=$Gl2>Zgbo>+AJP^lkdf@+bPI z`d0lH^$VT1p3n84)b08&`tSO$`fvJA`j7fa>M#0k{ZB6}87maoPz=>D4a2Yv-C#YB zcxo6w>8^2-THmN=4AJ*9_BReN4m27Wjg3QqjTwg;ha0(wwKUoq?SN@zv@%-b9}C(T zM;M)qE=E`5XrqrY$mnVGGx{4x8NH0&;Oc7}V+=G#8>5Ud#&O12<9Oo)W1KPGm}H!6 zsu&;J8YhX2$!u`i$ME#QAT zjQt5I|2Q?0(D#A%2lU227R`v^lisJ`Yj3g9as0s!J{!yEO+fe4`yas^RW!A1b{@XK z*QcnYxTwUF8p!f_<0Y+zjvCl1$c*wLS$*h#wkMZ$i^aVEyD7j|L)5(WyAEAjcj(=rck5nlyR>WF zKDTYV{$1Mj?bJKB*L*PU#r3GT+*oO>GFBVs8f%QT#yaC7W4*D#xXif1xZ1eZxZb$Y zxXHN1*ks&p+-clx+-ux#JYYOzJYqa%JYhU#JYzg(ykNX!ykfj&ykWd$ykl%J-Zwrp zJ~lozwi%xr+l?=cE74z97-t!08|N738Rr`p7#A8B8ljcLYoBj1=|6c~j@u~A}_8f8YgG0T{3%rWK~^NiDt`NkQ>0;9-? z`|cFu6k{ghr+TgkA?8jqxTDXb(AV@W&o{l#rLeo@;Thcly;dT%nsy^jkoijB!s(K% zbe3v&0>@F3Jcb_5-TNzel2RMdOOY;K*-WfxoY*sFA1vo|?FG_np2= z|3?2--=Tl0@6^B2zfL~M_qGJbiKPD}Qcq}pu`Fs=>(}bn>l^i(_1pA2^t<(Y_51Y) z^au5a^+)x`^e6PE^r!V__2=~$^_TTm_1E+_^tbf4^>_4l^)32)`UmewTiaexJTs|5JKMe+1aa^(XZu@-zB#`uqAz`YXC#<5vAm z{Vw@#`5yT*@izGr`n=k|^%|m#S7s?^C^NM}tw{UKm1rOfU4P=!I-#f^#AxK5kI&@O zdC2IQ$aD!DvqRmfeywg-zf`|cF&=s;Nh+uB$!~=(2X@9+7LUPK5+~wIhSv!{2|o+J z2)_!y3BL;qq^qSnq|Wlk@~854xkR0%o`)HG5g@qAQevysbJTOy3)J(~wQ8|PS)-m^ z@qNYTi(-owFS@DX!irlKAHT3(N53Ab-B7i zJxi@qV-;-i3R<)v%va~D3B(qu6r*d5_ySt7AgomP&(KyFPqupt|Lc!bhduCB%-;9{ zCe`IAe97{7e65n|G>$J(o`Nq=7USPx%kfpodHAQv3SqHOC7g-BAvzo1$~+%`o^r9U zLAXq~Qn&_fV3Tl%aF=keuvvIictUtqctLnscujayct?0o_)z#n*d}}{d?)M@{uDk# zt0+ag_{#m-{l@*)UE|eC&_Y-nIMr(jES#+*+3~!E75gnJ8z+5lvE0ik;Ha_W?26~k zX6>)+tn*gCR8?~J;zd_hT)L{knrqLWQ%UXdt+Rez)pJRg3ogDuSoiblzUQBK&h6(l zJ@>V>51n)Rn&PzsR(`r-Vdaf0Yn^rQij$W$KQpo7_=O*?xnSv##m?G-swC@}}DfJat z=>PA~uNdim&){r{+#!N1<6D|?)HRY~QWKv#3jJ#x`prXLzi6U1Rb%R*YGb_jBG*od zI)+bL9Zzy8p(Ip%29N)Lkdtt|Cv~kv?L?hK-9)`a{ltEFzQ+=WCWbDqU%6l9{*?_X z52!q_@}SCtD;riesyw7JR(WV;8erD_d2zu543zL}lB` zxs|6?&Z|7Va(?9*l?y5pl@*l>E4Ng>SNVSB2bH~+^j^|uN#7;?mh@k8%#s01u3B>S zl53V+yX3kh*DtwY$&E`kF1cyR%}Z`sa_f>!OUx>(%C2&%+^QN?HLGS;6;>5h6<3{F zRZ>-2RaRAAHLGfN)tsuiRi{mOOv9eydMA@KRs$8aAu3Vv9sa&O8tz4sAt6ZmCuiT*AsBBbj zQf^jmQEpW>DYq%ND|aY&Dt9S&EB7e(D)%Y(E1Q)Eln0fEl!ui^lt-1vl*g4PlqZ#^ zl&6(vlxLOal;@Qfloyqkl$VuPlvkD4l-HFvlsA>Pl(&_4ly{Xa%6rQD$_L7a%16q_ z$|uUF%2s8Y@|p6v@`bWp*`a)?>{Px|zE-|bzE!?cb}8R0KPW#cKPf*ezbL;dzbU^f ze<*({IBuZ+lds-@bhqq=GhwWeB2t*zEk>#FtC`s#k_{%QmD0QJDg zsFiFTdkowmUL?OKe&zGD?}Cj8 la)!z6lJQCugp+pDn-hv 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 VT60_CF#bQ+@weTaGriDv{y*Lex~%{J diff --git a/mDNSWindows/Java/Java.vcproj b/mDNSWindows/Java/Java.vcproj new file mode 100755 index 0000000..a846ec9 --- /dev/null +++ b/mDNSWindows/Java/Java.vcproj @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/mDNSWindows/Java/makefile b/mDNSWindows/Java/makefile index b590e30..8a115ee 100644 --- a/mDNSWindows/Java/makefile +++ b/mDNSWindows/Java/makefile @@ -20,6 +20,9 @@ # @APPLE_LICENSE_HEADER_END@ # # $Log: makefile,v $ +# Revision 1.7 2005/10/19 17:19:56 herscher +# Change JDK to use JAVA_HOME environment variable +# # Revision 1.6 2005/02/10 22:35:36 cheshire # Update name # @@ -64,7 +67,7 @@ COREDIR = ..\..\mDNSCore SHAREDDIR = ..\..\mDNSShared -JDK = \javasdk +JDK = $(JAVA_HOME) CC = cl RC = rc diff --git a/mDNSWindows/NSPTool/NSPTool.vcproj b/mDNSWindows/NSPTool/NSPTool.vcproj index 7b3fc0e..8253f10 100644 --- a/mDNSWindows/NSPTool/NSPTool.vcproj +++ b/mDNSWindows/NSPTool/NSPTool.vcproj @@ -1,7 +1,7 @@ - + @@ -58,12 +58,18 @@ AdditionalIncludeDirectories="..\"/> + + + @@ -102,10 +108,18 @@ AdditionalIncludeDirectories="..\"/> + + + + + Save Wide-Area preferences in a different spot in the registry so they don't get removed when doing an update install. + Revision 1.1 2005/03/03 02:31:37 shersche Consolidates all registry key names and can safely be included in any component that needs it @@ -35,6 +38,7 @@ Consolidates all registry key names and can safely be included in any component #if defined(UNICODE) +# define kServiceParametersNode L"SOFTWARE\\Apple Computer, Inc.\\Bonjour" # define kServiceName L"Bonjour Service" # define kServiceDynDNSBrowseDomains L"BrowseDomains" # define kServiceDynDNSHostNames L"HostNames" @@ -48,6 +52,7 @@ Consolidates all registry key names and can safely be included in any component # else +# define kServiceParametersNode "SOFTWARE\\Apple Computer, Inc.\\Bonjour" # define kServiceName "Bonjour Service" # define kServiceDynDNSBrowseDomains "BrowseDomains" # define kServiceDynDNSHostNames "HostNames" diff --git a/mDNSWindows/SystemService/Firewall.cpp b/mDNSWindows/SystemService/Firewall.cpp index 15eb7f8..ba235c8 100755 --- a/mDNSWindows/SystemService/Firewall.cpp +++ b/mDNSWindows/SystemService/Firewall.cpp @@ -23,6 +23,9 @@ Change History (most recent first): $Log: Firewall.cpp,v $ +Revision 1.3 2005/09/29 06:33:54 herscher + Fix compilation error when using latest Microsoft Platform SDK. + Revision 1.2 2004/09/15 09:39:53 shersche Retry the method INetFwPolicy::get_CurrentProfile on error @@ -32,7 +35,16 @@ Wrapper for Windows Firewall API code */ -#define _WIN32_DCOM +// Doesn't compile correctly with latest Platform SDK + +#if !defined(_WIN32_DCOM) +# define _WIN32_DCOM +#endif + + +#if !defined(_WSPIAPI_COUNTOF) +# define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0])) +#endif #include "Firewall.h" #include diff --git a/mDNSWindows/SystemService/Service.c b/mDNSWindows/SystemService/Service.c index 451c239..768b589 100644 --- a/mDNSWindows/SystemService/Service.c +++ b/mDNSWindows/SystemService/Service.c @@ -23,6 +23,33 @@ Change History (most recent first): $Log: Service.c,v $ +Revision 1.38 2005/10/05 20:55:15 herscher + Don't call SetLLRoute on loopback interface + +Revision 1.37 2005/10/05 18:05:28 herscher + Save Wide-Area preferences in a different spot in the registry so they don't get removed when doing an update install. + +Revision 1.36 2005/09/11 22:12:42 herscher + Remove dependency on WMI. Ensure that the Windows firewall is turned on before trying to configure it. + +Revision 1.35 2005/06/30 18:29:49 shersche + Don't overwrite the localized service description text + +Revision 1.34 2005/04/22 07:34:23 shersche +Check an interface's address and make sure it's valid before using it to set link-local routes. + +Revision 1.33 2005/04/13 17:48:23 shersche + Make sure there is only one default route for link-local addresses. + +Revision 1.32 2005/04/06 01:32:05 shersche +Remove default route for link-local addressing when another interface comes up with a routable IPv4 address + +Revision 1.31 2005/04/06 01:00:11 shersche + GetFullPathName() should be passed the number of TCHARs in the path buffer, not the size in bytes of the path buffer. + +Revision 1.30 2005/04/06 00:52:43 shersche + Only add default route if there are no other routable IPv4 addresses on any of the other interfaces. More work needs to be done to correctly configure the routing table when multiple interfaces are extant and none of them have routable IPv4 addresses. + Revision 1.29 2005/03/06 05:21:56 shersche Fix corrupt UTF-8 name when non-ASCII system name used, enabled unicode support @@ -169,8 +196,9 @@ mDNSResponder Windows Service. Provides global Bonjour support with an IPC inter #define DEBUG_NAME "[Server] " #define kServiceFirewallName L"Bonjour" -#define kServiceDependencies TEXT("Tcpip\0winmgmt\0\0") +#define kServiceDependencies TEXT("Tcpip\0\0") #define kDNSServiceCacheEntryCountDefault 512 +#define kRetryFirewallPeriod 30 * 1000 #define RR_CACHE_SIZE 500 static CacheEntity gRRCache[RR_CACHE_SIZE]; @@ -257,8 +285,9 @@ static mDNSs32 udsIdle(mDNS * const inMDNS, mDNSs32 interval); static void CoreCallback(mDNS * const inMDNS, mStatus result); static void HostDescriptionChanged(mDNS * const inMDNS); static OSStatus GetRouteDestination(DWORD * ifIndex, DWORD * address); -static bool HaveLLRoute(PMIB_IPFORWARDROW rowExtant); -static OSStatus SetLLRoute(); +static OSStatus SetLLRoute( mDNS * const inMDNS ); +static bool HaveRoute( PMIB_IPFORWARDROW rowExtant, unsigned long addr ); +static bool IsValidAddress( const char * addr ); #if defined(UNICODE) # define StrLen(X) wcslen(X) @@ -301,6 +330,7 @@ DEBUG_LOCAL HANDLE * gWaitList = NULL; DEBUG_LOCAL HANDLE gStopEvent = NULL; DEBUG_LOCAL CRITICAL_SECTION gEventSourceLock; DEBUG_LOCAL GenLinkedList gEventSources; +DEBUG_LOCAL BOOL gRetryFirewall = FALSE; #if 0 @@ -478,7 +508,7 @@ static OSStatus InstallService( LPCTSTR inName, LPCTSTR inDisplayName, LPCTSTR i // Get a full path to the executable since a relative path may have been specified. - size = GetFullPathName( inPath, sizeof( fullPath ), fullPath, &namePtr ); + size = GetFullPathName( inPath, MAX_PATH, fullPath, &namePtr ); err = translate_errno( size > 0, (OSStatus) GetLastError(), kPathErr ); require_noerr( err, exit ); @@ -589,7 +619,6 @@ static OSStatus SetServiceParameters() DWORD value; DWORD valueLen = sizeof(DWORD); DWORD type; - LPCTSTR s; OSStatus err; HKEY key; @@ -598,8 +627,7 @@ static OSStatus SetServiceParameters() // // Add/Open Parameters section under service entry in registry // - s = TEXT("SYSTEM\\CurrentControlSet\\Services\\") kServiceName TEXT("\\Parameters"); - err = RegCreateKey( HKEY_LOCAL_MACHINE, s, &key ); + err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode, &key ); require_noerr( err, exit ); // @@ -636,7 +664,6 @@ static OSStatus GetServiceParameters() DWORD value; DWORD valueLen; DWORD type; - LPCTSTR s; OSStatus err; HKEY key; @@ -645,8 +672,7 @@ static OSStatus GetServiceParameters() // // Add/Open Parameters section under service entry in registry // - s = TEXT("SYSTEM\\CurrentControlSet\\Services\\") kServiceName TEXT("\\Parameters"); - err = RegCreateKey( HKEY_LOCAL_MACHINE, s, &key ); + err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode, &key ); require_noerr( err, exit ); valueLen = sizeof(DWORD); @@ -680,21 +706,80 @@ exit: static OSStatus CheckFirewall() { - DWORD value; - DWORD valueLen; - DWORD type; - LPCTSTR s; - HKEY key = NULL; - OSStatus err = kUnknownErr; - + DWORD value; + DWORD valueLen; + DWORD type; + ENUM_SERVICE_STATUS * lpService = NULL; + SC_HANDLE sc = NULL; + HKEY key = NULL; + BOOL ok; + DWORD bytesNeeded = 0; + DWORD srvCount; + DWORD resumeHandle = 0; + DWORD srvType; + DWORD srvState; + DWORD dwBytes = 0; + DWORD i; + BOOL isRunning = FALSE; + OSStatus err = kUnknownErr; + + // Check to see if the firewall service is running. If it isn't, then + // we want to return immediately + + sc = OpenSCManager( NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE ); + err = translate_errno( sc, GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + srvType = SERVICE_WIN32; + srvState = SERVICE_STATE_ALL; + + for ( ;; ) + { + // Call EnumServicesStatus using the handle returned by OpenSCManager + + ok = EnumServicesStatus ( sc, srvType, srvState, lpService, dwBytes, &bytesNeeded, &srvCount, &resumeHandle ); + + if ( ok || ( GetLastError() != ERROR_MORE_DATA ) ) + { + break; + } + + if ( lpService ) + { + free( lpService ); + } + + dwBytes = bytesNeeded; + + lpService = ( ENUM_SERVICE_STATUS* ) malloc( dwBytes ); + require_action( lpService, exit, err = mStatus_NoMemoryErr ); + } + + err = translate_errno( ok, GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + for ( i = 0; i < srvCount; i++ ) + { + if ( wcscmp( lpService[i].lpServiceName, L"SharedAccess" ) == 0 ) + { + if ( lpService[i].ServiceStatus.dwCurrentState == SERVICE_RUNNING ) + { + isRunning = TRUE; + } + + break; + } + } + + require_action( isRunning, exit, err = kUnknownErr ); + // Check to see if we've managed the firewall. // This package might have been installed, then // the OS was upgraded to SP2 or above. If that's // the case, then we need to manipulate the firewall // so networking works correctly. - s = TEXT("SYSTEM\\CurrentControlSet\\Services\\") kServiceName TEXT("\\Parameters"); - err = RegCreateKey( HKEY_LOCAL_MACHINE, s, &key ); + err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode, &key ); require_noerr( err, exit ); valueLen = sizeof(DWORD); @@ -725,6 +810,16 @@ exit: { RegCloseKey( key ); } + + if ( lpService ) + { + free( lpService ); + } + + if ( sc ) + { + CloseServiceHandle ( sc ); + } return( err ); } @@ -889,7 +984,6 @@ static void WINAPI ServiceMain( DWORD argc, LPTSTR argv[] ) { OSStatus err; BOOL ok; - TCHAR desc[ 256 ]; err = ServiceSetupEventLogging(); check_noerr( err ); @@ -911,13 +1005,6 @@ static void WINAPI ServiceMain( DWORD argc, LPTSTR argv[] ) err = translate_errno( gServiceStatusHandle, (OSStatus) GetLastError(), kInUseErr ); require_noerr( err, exit ); - // Setup the description. This should be done by the installer, but it doesn't support that yet. - - desc[ 0 ] = '\0'; - LoadString( GetModuleHandle( NULL ), IDS_SERVICE_DESCRIPTION, desc, sizeof( desc ) ); - err = SetServiceInfo( NULL, kServiceName, desc ); - check_noerr( err ); - // Mark the service as starting. gServiceStatus.dwCurrentState = SERVICE_START_PENDING; @@ -1083,6 +1170,11 @@ static OSStatus ServiceRun( int argc, LPTSTR argv[] ) err = CheckFirewall(); check_noerr( err ); + + if ( err ) + { + gRetryFirewall = TRUE; + } // Run the service-specific stuff. This does not return until the service quits or is stopped. @@ -1158,11 +1250,14 @@ static OSStatus ServiceSpecificInitialize( int argc, LPTSTR argv[] ) require_noerr( err, exit); // - // set a route to link local addresses (169.254.0.0) + // Don't call SetLLRoute on loopback + // + // Otherwise, set a route to link local addresses (169.254.0.0) // - if (gServiceManageLLRouting == true) + + if ( gServiceManageLLRouting && !gPlatformStorage.registeredLoopback4 ) { - SetLLRoute(); + SetLLRoute( &gMDNSRecord ); } exit: @@ -1179,16 +1274,32 @@ exit: static OSStatus ServiceSpecificRun( int argc, LPTSTR argv[] ) { + DWORD timeout; DWORD result; DEBUG_UNUSED( argc ); DEBUG_UNUSED( argv ); // Main event loop. Process connection requests and state changes (i.e. quit). - while( (result = WaitForSingleObject(gStopEvent, INFINITE)) != WAIT_OBJECT_0 ) + + timeout = ( gRetryFirewall ) ? kRetryFirewallPeriod : INFINITE; + + while( (result = WaitForSingleObject( gStopEvent, timeout ) ) != WAIT_OBJECT_0 ) { - // Unexpected wait result. - dlog( kDebugLevelWarning, DEBUG_NAME "%s: unexpected wait result (result=0x%08X)\n", __ROUTINE__, result ); + if ( result == WAIT_TIMEOUT ) + { + OSStatus err; + + err = CheckFirewall(); + check_noerr( err ); + + timeout = INFINITE; + } + else + { + // Unexpected wait result. + dlog( kDebugLevelWarning, DEBUG_NAME "%s: unexpected wait result (result=0x%08X)\n", __ROUTINE__, result ); + } } return kNoErr; @@ -1246,13 +1357,17 @@ static void ServiceSpecificFinalize( int argc, LPTSTR argv[] ) static void CoreCallback(mDNS * const inMDNS, mStatus status) { - DEBUG_UNUSED( inMDNS ); - if (status == mStatus_ConfigChanged) { - if (gServiceManageLLRouting == true) + // + // Don't call SetLLRoute on loopback + // + // Otherwise, set a route to link local addresses (169.254.0.0) + // + + if ( gServiceManageLLRouting && !inMDNS->p->registeredLoopback4 ) { - SetLLRoute(); + SetLLRoute( inMDNS ); } } } @@ -1658,11 +1773,11 @@ EventSourceUnlock() //=========================================================================================================================== -// HaveLLRoute +// HaveRoute //=========================================================================================================================== static bool -HaveLLRoute(PMIB_IPFORWARDROW rowExtant) +HaveRoute( PMIB_IPFORWARDROW rowExtant, unsigned long addr ) { PMIB_IPFORWARDTABLE pIpForwardTable = NULL; DWORD dwSize = 0; @@ -1694,7 +1809,7 @@ HaveLLRoute(PMIB_IPFORWARDROW rowExtant) // for ( i = 0; i < pIpForwardTable->dwNumEntries; i++) { - if (pIpForwardTable->table[i].dwForwardDest == inet_addr(kLLNetworkAddr)) + if ( pIpForwardTable->table[i].dwForwardDest == addr ) { memcpy( rowExtant, &(pIpForwardTable->table[i]), sizeof(*rowExtant) ); found = true; @@ -1713,12 +1828,23 @@ exit: } +//=========================================================================================================================== +// IsValidAddress +//=========================================================================================================================== + +static bool +IsValidAddress( const char * addr ) +{ + return ( addr && ( strcmp( addr, "0.0.0.0" ) != 0 ) ) ? true : false; +} + + //=========================================================================================================================== // SetLLRoute //=========================================================================================================================== static OSStatus -SetLLRoute() +SetLLRoute( mDNS * const inMDNS ) { DWORD ifIndex; MIB_IPFORWARDROW rowExtant; @@ -1748,7 +1874,7 @@ SetLLRoute() // // check to make sure we don't already have a route // - if (HaveLLRoute(&rowExtant)) + if ( HaveRoute( &rowExtant, inet_addr( kLLNetworkAddr ) ) ) { // // set the age to 0 so that we can do a memcmp. @@ -1782,13 +1908,32 @@ SetLLRoute() } // - // see if this address is a link local address + // Now we want to see if we should install a default route for this interface. + // We want to do this if the following are true: // - if ((row.dwForwardNextHop & 0xFFFF) == row.dwForwardDest) + // 1. This interface has a link-local address + // 2. This is the only IPv4 interface + // + + if ( ( row.dwForwardNextHop & 0xFFFF ) == row.dwForwardDest ) { - // - // if so, set up a route to ARP everything - // + mDNSInterfaceData * ifd; + int numLinkLocalInterfaces = 0; + int numInterfaces = 0; + + for ( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next ) + { + if ( ifd->defaultAddr.type == mDNSAddrType_IPv4 ) + { + numInterfaces++; + + if ( ( ifd->interfaceInfo.ip.ip.v4.b[0] == 169 ) && ( ifd->interfaceInfo.ip.ip.v4.b[1] == 254 ) ) + { + numLinkLocalInterfaces++; + } + } + } + row.dwForwardDest = 0; row.dwForwardIfIndex = ifIndex; row.dwForwardMask = 0; @@ -1796,16 +1941,26 @@ SetLLRoute() row.dwForwardProto = MIB_IPPROTO_NETMGMT; row.dwForwardAge = 0; row.dwForwardPolicy = 0; - row.dwForwardMetric1 = 1; + row.dwForwardMetric1 = 20; row.dwForwardMetric2 = (DWORD) - 1; row.dwForwardMetric3 = (DWORD) - 1; row.dwForwardMetric4 = (DWORD) - 1; row.dwForwardMetric5 = (DWORD) - 1; - - err = CreateIpForwardEntry(&row); - - require_noerr( err, exit ); + + if ( numInterfaces == numLinkLocalInterfaces ) + { + if ( !HaveRoute( &row, 0 ) ) + { + err = CreateIpForwardEntry(&row); + require_noerr( err, exit ); + } + } + else + { + DeleteIpForwardEntry( &row ); + } } + exit: return ( err ); @@ -1823,6 +1978,7 @@ GetRouteDestination(DWORD * ifIndex, DWORD * address) IP_ADAPTER_INFO * pAdapterInfo = NULL; IP_ADAPTER_INFO * pAdapter = NULL; ULONG bufLen; + mDNSBool done = mDNSfalse; OSStatus err; // @@ -1882,24 +2038,41 @@ GetRouteDestination(DWORD * ifIndex, DWORD * address) pAdapter = pAdapter->Next; } - pAdapter = pAdapterInfo; - err = kUnknownErr; - - while (pAdapter) + while ( !done ) { - // - // if we don't have an interface selected, choose the first one - // - if ((pAdapter->Type == MIB_IF_TYPE_ETHERNET) && (!(*ifIndex) || (pAdapter->Index == (*ifIndex)))) + pAdapter = pAdapterInfo; + err = kUnknownErr; + + while (pAdapter) { - *address = inet_addr( pAdapter->IpAddressList.IpAddress.String ); - *ifIndex = pAdapter->Index; - err = kNoErr; - break; + // If we don't have an interface selected, choose the first one that is of type ethernet and + // has a valid IP Address + + if ((pAdapter->Type == MIB_IF_TYPE_ETHERNET) && ( IsValidAddress( pAdapter->IpAddressList.IpAddress.String ) ) && (!(*ifIndex) || (pAdapter->Index == (*ifIndex)))) + { + *address = inet_addr( pAdapter->IpAddressList.IpAddress.String ); + *ifIndex = pAdapter->Index; + err = kNoErr; + break; + } + + pAdapter = pAdapter->Next; } - - pAdapter = pAdapter->Next; - } + + // If we found the right interface, or we weren't trying to find a specific interface then we're done + + if ( !err || !( *ifIndex) ) + { + done = mDNStrue; + } + + // Otherwise, try again by wildcarding the interface + + else + { + *ifIndex = 0; + } + } exit: @@ -1910,4 +2083,3 @@ exit: return( err ); } - diff --git a/mDNSWindows/SystemService/Service.vcproj b/mDNSWindows/SystemService/Service.vcproj index cbc4e14..f77394e 100644 --- a/mDNSWindows/SystemService/Service.vcproj +++ b/mDNSWindows/SystemService/Service.vcproj @@ -1,7 +1,7 @@ - + @@ -19,8 +19,8 @@ + + + + + + + + - - + RelativePath="..\dDNS.c"> + RelativePath="..\..\mDNSCore\DNSCommon.c"> + RelativePath="..\..\mDNSCore\DNSDigest.c"> + RelativePath="..\..\mDNSShared\dnssd_ipc.c"> + RelativePath="Firewall.cpp"> + RelativePath="..\..\mDNSShared\GenLinkedList.c"> @@ -144,6 +155,12 @@ + + + + @@ -159,22 +176,19 @@ RelativePath="..\CommonServices.h"> + RelativePath="..\dDNS.h"> - - + RelativePath="..\..\mDNSCore\DNSCommon.h"> + RelativePath="..\..\mDNSShared\dnssd_ipc.h"> + RelativePath="..\..\mDNSShared\GenLinkedList.h"> @@ -185,6 +199,12 @@ + + + + diff --git a/mDNSWindows/SystemServiceTest/Tool.c b/mDNSWindows/SystemServiceTest/Tool.c deleted file mode 100644 index f4d675a..0000000 --- a/mDNSWindows/SystemServiceTest/Tool.c +++ /dev/null @@ -1,851 +0,0 @@ -/* - * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log: Tool.c,v $ -Revision 1.2 2004/07/13 21:24:28 rpantos -Fix for . - -Revision 1.1 2004/06/18 04:17:43 rpantos -Move up one level. - -Revision 1.3 2004/04/09 21:03:15 bradley -Changed port numbers to use network byte order for consistency with other platforms. - -Revision 1.2 2004/04/08 09:43:43 bradley -Changed callback calling conventions to __stdcall so they can be used with C# delegates. - -Revision 1.1 2004/01/30 02:58:57 bradley -Test tool for the mDNSResponder Windows service. - -*/ - -#include -#include - -#include "CommonServices.h" -#include "DebugServices.h" -#include "DNSSD.h" - -//=========================================================================================================================== -// Structures -//=========================================================================================================================== - -#define MAX_DOMAIN_LABEL 63 -#define MAX_DOMAIN_NAME 255 - -typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16; - -typedef struct { u_char c[ 64]; } domainlabel; -typedef struct { u_char c[256]; } domainname; - -typedef struct - { - uint16_t priority; - uint16_t weight; - uint16_t port; - domainname target; - } srv_rdata; - -//=========================================================================================================================== -// Prototypes -//=========================================================================================================================== - -int main( int argc, char* argv[] ); -static void Usage( void ); -static int ProcessArgs( int argc, char* argv[] ); - -#if( defined( WINVER ) ) - static BOOL WINAPI ConsoleControlHandler( DWORD inControlEvent ); -#endif - -static void CALLBACK_COMPAT - EnumerateDomainsCallBack( - DNSServiceRef inRef, - DNSServiceFlags inFlags, - uint32_t inInterfaceIndex, - DNSServiceErrorType inErrorCode, - const char * inDomain, - void * inContext ); - -static void CALLBACK_COMPAT - BrowseCallBack( - DNSServiceRef inRef, - DNSServiceFlags inFlags, - uint32_t inInterfaceIndex, - DNSServiceErrorType inErrorCode, - const char * inName, - const char * inType, - const char * inDomain, - void * inContext ); - -static void CALLBACK_COMPAT - ResolveCallBack( - DNSServiceRef inRef, - DNSServiceFlags inFlags, - uint32_t inInterfaceIndex, - DNSServiceErrorType inErrorCode, - const char * inFullName, - const char * inHostName, - uint16_t inPort, - uint16_t inTXTSize, - const char * inTXT, - void * inContext ); - -static void CALLBACK_COMPAT - RegisterCallBack( - DNSServiceRef inRef, - DNSServiceFlags inFlags, - DNSServiceErrorType inErrorCode, - const char * inName, - const char * inType, - const char * inDomain, - void * inContext ); - -static void CALLBACK_COMPAT - RecordCallBack( - DNSServiceRef inRef, - DNSRecordRef inRecordRef, - DNSServiceFlags inFlags, - DNSServiceErrorType inErrorCode, - void * inContext ); - -static void CALLBACK_COMPAT - QueryCallBack( - const DNSServiceRef inRef, - const DNSServiceFlags inFlags, - const uint32_t inInterfaceIndex, - const DNSServiceErrorType inErrorCode, - const char * inName, - const uint16_t inRRType, - const uint16_t inRRClass, - const uint16_t inRDataSize, - const void * inRData, - const uint32_t inTTL, - void * inContext ); - -static void PrintRData( uint16_t inRRType, size_t inRDataSize, const uint8_t *inRData ); - -static char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc); -static char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc); - -//=========================================================================================================================== -// Globals -//=========================================================================================================================== - -#if( defined( WINVER ) ) - static volatile int gQuit = 0; -#endif - -//=========================================================================================================================== -// main -//=========================================================================================================================== - -int main( int argc, char *argv[] ) -{ - OSStatus err; - - debug_initialize( kDebugOutputTypeMetaConsole ); - debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelTrace ); - - SetConsoleCtrlHandler( ConsoleControlHandler, TRUE ); - err = ProcessArgs( argc, argv ); - return( (int) err ); -} - -//=========================================================================================================================== -// Usage -//=========================================================================================================================== - -static void Usage( void ) -{ - fprintf( stderr, "\n" ); - fprintf( stderr, "DNSServiceTest 1.0d1\n" ); - fprintf( stderr, "\n" ); - fprintf( stderr, " -server Set Remote Server\n" ); - fprintf( stderr, " -cv Check Version\n" ); - fprintf( stderr, " -bd Browse for Browse Domains\n" ); - fprintf( stderr, " -bs Browse for Services\n" ); - fprintf( stderr, " -rsi Resolve Service Instance\n" ); - fprintf( stderr, " -rs Register Service\n" ); - fprintf( stderr, " -rr Register Records\n" ); - fprintf( stderr, " -qr Query Record\n" ); - fprintf( stderr, " -cr Reconfirm Record\n" ); - fprintf( stderr, " -cp Copy Property\n" ); - fprintf( stderr, " -h[elp] Help\n" ); - fprintf( stderr, "\n" ); -} - -DEBUG_LOCAL DNSServiceRef gRef = NULL; -DEBUG_LOCAL DNSRecordRef gRecordRef = NULL; -DEBUG_LOCAL const char * gServer = NULL; - -//=========================================================================================================================== -// ProcessArgs -//=========================================================================================================================== - -static int ProcessArgs( int argc, char* argv[] ) -{ - OSStatus err; - int i; - const char * name; - const char * type; - const char * domain; - uint16_t port; - const char * host; - const char * txt; - uint16_t txtSize; - uint8_t txtStorage[ 256 ]; - uint32_t ipv4; - char s[ 256 ]; - DNSRecordRef records[ 10 ]; - char fullName[ kDNSServiceMaxDomainName ]; - uint16_t rrType; - - err = DNSServiceInitialize( kDNSServiceInitializeFlagsNoServerCheck, 0 ); - require_noerr( err, exit ); - - // Parse the command line arguments (ignore first argument since it's just the program name). - - if( argc <= 1 ) - { - Usage(); - err = 0; - goto exit; - } - for( i = 1; i < argc; ++i ) - { - if( strcmp( argv[ i ], "-server" ) == 0 ) - { - require_action( argc > ( i + 1 ), exit, err = kParamErr ); - gServer = argv[ ++i ]; - - printf( "Server set to \"%s\"\n", gServer ); - } - else if( strcmp( argv[ i ], "-cv" ) == 0 ) - { - // Check Version - - err = DNSServiceCheckVersion(); - printf( "CheckVersion: %ld\n", err ); - err = kNoErr; - goto exit; - } - else if( strcmp( argv[ i ], "-bd" ) == 0 ) - { - err = DNSServiceEnumerateDomains( &gRef, kDNSServiceFlagsBrowseDomains, 0, - EnumerateDomainsCallBack, NULL ); - require_noerr( err, exit ); - } - else if( strcmp( argv[ i ], "-bs" ) == 0 ) - { - // Browse service - - if( argc > ( i + 2 ) ) - { - type = argv[ ++i ]; - domain = argv[ ++i ]; - } - else - { - type = "_http._tcp"; - domain = ""; - } - if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) ) - { - domain = "local."; - } - - err = DNSServiceBrowse( &gRef, 0, 0, type, domain, BrowseCallBack, NULL ); - require_noerr( err, exit ); - } - else if( strcmp( argv[ i ], "-rsi" ) == 0 ) - { - // Resolve Service Instance - - if( argc > ( i + 3 ) ) - { - name = argv[ ++i ]; - type = argv[ ++i ]; - domain = argv[ ++i ]; - } - else - { - name = "test service"; - type = "_http._tcp"; - domain = ""; - } - if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) ) - { - domain = "local."; - } - - err = DNSServiceResolve( &gRef, 0, 0, name, type, domain, ResolveCallBack, NULL ); - require_noerr( err, exit ); - } - else if( strcmp( argv[ i ], "-rs" ) == 0 ) - { - // Register Service - - if( argc > ( i + 6 ) ) - { - name = argv[ ++i ]; - type = argv[ ++i ]; - domain = argv[ ++i ]; - host = argv[ ++i ]; - port = (uint16_t) atoi( argv[ ++i ] ); - txt = argv[ ++i ]; - } - else - { - name = "test service"; - type = "_http._tcp"; - domain = ""; - host = ""; - port = 80; - txt = "My TXT Record"; - } - if( *txt != '\0' ) - { - txtStorage[ 0 ] = (uint8_t) strlen( txt ); - memcpy( &txtStorage[ 1 ], txt, txtStorage[ 0 ] ); - txtSize = (uint16_t)( 1 + txtStorage[ 0 ] ); - txt = (const char *) txtStorage; - } - else - { - txt = NULL; - txtSize = 0; - } - if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) ) - { - domain = "local."; - } - - err = DNSServiceRegister( &gRef, 0, 0, name, type, domain, host, htons( port ), txtSize, txt, - RegisterCallBack, NULL ); - require_noerr( err, exit ); - - #if( TEST_SERVICE_RECORDS ) - ipv4 = 0x11223344; - err = DNSServiceAddRecord( gRef, &gRecordRef, 0, kDNSServiceDNSType_A, kDNSServiceDNSClass_IN, &ipv4, 60 ); - require_noerr( err, exit ); - - Sleep( 10000 ); - - ipv4 = 0x22334455; - err = DNSServiceUpdateRecord( gRef, gRecordRef, 0, 4, &ipv4, 60 ); - require_noerr( err, exit ); - - Sleep( 10000 ); - - err = DNSServiceRemoveRecord( gRef, gRecordRef, 0 ); - require_noerr( err, exit ); - gRecordRef = NULL; - - Sleep( 10000 ); - #endif - } - else if( strcmp( argv[ i ], "-rr" ) == 0 ) - { - // Register Records - - err = DNSServiceCreateConnection( &gRef ); - require_noerr( err, exit ); - - printf( "registering 10 address records...\n" ); - ipv4 = 0x11223310; - for( i = 0; i < 10; ++i ) - { - sprintf( s, "testhost-%d.local.", i ); - ++ipv4; - err = DNSServiceRegisterRecord( gRef, &records[ i ], kDNSServiceFlagsUnique, 0, s, - kDNSServiceDNSType_A, kDNSServiceDNSClass_IN, 4, &ipv4, 60, RecordCallBack, NULL ); - check_noerr( err ); - } - Sleep( 10000 ); - - printf( "deregistering half of the records\n" ); - for( i = 0; i < 10; ++i ) - { - if( i % 2 ) - { - err = DNSServiceRemoveRecord( gRef, records[ i ], 0 ); - check_noerr( err ); - records[ i ] = NULL; - } - } - Sleep( 10000 ); - - printf( "updating the remaining records\n" ); - for( i = 0; i < 10; ++i ) - { - if( records[ i ] ) - { - ++ipv4; - err = DNSServiceUpdateRecord( gRef, records[ i ], 0, 4, &ipv4, 60 ); - check_noerr( err ); - } - } - Sleep( 10000 ); - - printf( "deregistering all remaining records\n" ); - DNSServiceRefDeallocate( gRef ); - - Sleep( 5000 ); - } - else if( strcmp( argv[ i ], "-qr" ) == 0 ) - { - // Query Record - - if( argc > ( i + 4 ) ) - { - name = argv[ ++i ]; - type = argv[ ++i ]; - domain = argv[ ++i ]; - rrType = (uint16_t) atoi( argv[ ++i ] ); - } - else - { - name = "test"; - type = ""; - domain = ""; - rrType = 1; // Address - } - if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) ) - { - domain = "local."; - } - err = DNSServiceConstructFullName( fullName, name, type, domain ); - require_noerr( err, exit ); - - printf( "resolving fullname %s type %d\n", fullName, rrType ); - err = DNSServiceQueryRecord( &gRef, 0, 0, fullName, rrType, kDNSServiceDNSClass_IN, QueryCallBack, NULL ); - require_noerr( err, exit ); - } - else if( strcmp( argv[ i ], "-cr" ) == 0 ) - { - // Reconfirm Record - - if( argc > ( i + 4 ) ) - { - name = argv[ ++i ]; - type = argv[ ++i ]; - domain = argv[ ++i ]; - rrType = (uint16_t) atoi( argv[ ++i ] ); - } - else - { - name = "test"; - type = ""; - domain = ""; - rrType = 1; // Address - } - if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) ) - { - domain = "local."; - } - err = DNSServiceConstructFullName( fullName, name, type, domain ); - require_noerr( err, exit ); - - printf( "reconfirming record fullname %s type %d\n", fullName, rrType ); - ipv4 = 0x11223310; - DNSServiceReconfirmRecord( 0, 0, fullName, rrType, kDNSServiceDNSClass_IN, 4, &ipv4 ); - } - else if( strcmp( argv[ i ], "-cp" ) == 0 ) - { - DNSPropertyCode code; - DNSPropertyData data; - - // Copy Property - - if( argc > ( i + 1 ) ) - { - name = argv[ ++i ]; - require_action( strlen( name ) == 4, exit, err = kParamErr ); - - code = (DNSPropertyCode)( name[ 0 ] << 24 ); - code |= (DNSPropertyCode)( name[ 1 ] << 16 ); - code |= (DNSPropertyCode)( name[ 2 ] << 8 ); - code |= (DNSPropertyCode) name[ 3 ]; - } - else - { - code = kDNSPropertyCodeVersion; - name = "vers"; - } - - err = DNSServiceCopyProperty( code, &data ); - require_noerr( err, exit ); - - printf( "'%s' property:\n", name ); - if( code == kDNSPropertyCodeVersion ) - { - printf( " clientCurrentVersion: 0x%08X\n", data.u.version.clientCurrentVersion ); - printf( " clientOldestServerVersion: 0x%08X\n", data.u.version.clientOldestServerVersion ); - printf( " serverCurrentVersion: 0x%08X\n", data.u.version.serverCurrentVersion ); - printf( " serverOldestClientVersion: 0x%08X\n", data.u.version.serverOldestClientVersion ); - } - } - else if( ( strcmp( argv[ i ], "-help" ) == 0 ) || ( strcmp( argv[ i ], "-h" ) == 0 ) ) - { - // Help - - Usage(); - err = 0; - goto exit; - } - else - { - // Unknown parameter. - - dlog( kDebugLevelError, "unknown parameter (%s)\n", argv[ i ] ); - err = kParamErr; - goto exit; - } - } - - // Run until control-C'd. - - while( !gQuit ) - { - Sleep( 100 ); - } - err = kNoErr; - -exit: - DNSServiceFinalize(); - if( err ) - { - Usage(); - } - return( err ); -} - -//=========================================================================================================================== -// ConsoleControlHandler -//=========================================================================================================================== - -static BOOL WINAPI ConsoleControlHandler( DWORD inControlEvent ) -{ - BOOL handled; - - handled = 0; - switch( inControlEvent ) - { - case CTRL_C_EVENT: - case CTRL_BREAK_EVENT: - case CTRL_CLOSE_EVENT: - case CTRL_LOGOFF_EVENT: - case CTRL_SHUTDOWN_EVENT: - gQuit = 1; - handled = 1; - break; - - default: - break; - } - return( handled ); -} - -//=========================================================================================================================== -// EnumerateDomainsCallBack -//=========================================================================================================================== - -static void CALLBACK_COMPAT - EnumerateDomainsCallBack( - DNSServiceRef inRef, - DNSServiceFlags inFlags, - uint32_t inInterfaceIndex, - DNSServiceErrorType inErrorCode, - const char * inDomain, - void * inContext ) -{ - printf( "inRef: 0x%08X\n", (uintptr_t) inRef ); - printf( "inFlags: 0x%08X\n", (int) inFlags ); - printf( "inInterfaceIndex: 0x%08X\n", (int) inInterfaceIndex ); - printf( "inErrorCode: %ld\n", inErrorCode ); - printf( "inDomain: \"%s\"\n", inDomain ? inDomain : "" ); - printf( "inContext: 0x%08X\n", (uintptr_t) inContext ); - printf( "\n" ); -} - -//=========================================================================================================================== -// BrowseCallBack -//=========================================================================================================================== - -static void CALLBACK_COMPAT - BrowseCallBack( - DNSServiceRef inRef, - DNSServiceFlags inFlags, - uint32_t inInterfaceIndex, - DNSServiceErrorType inErrorCode, - const char * inName, - const char * inType, - const char * inDomain, - void * inContext ) -{ - printf( "inRef: 0x%08X\n", (uintptr_t) inRef ); - printf( "inFlags: 0x%08X\n", (int) inFlags ); - printf( "inInterfaceIndex: 0x%08X\n", (int) inInterfaceIndex ); - printf( "inErrorCode: %ld\n", inErrorCode ); - printf( "inName: \"%s\"\n", inName ? inName : "" ); - printf( "inType: \"%s\"\n", inType ? inType : "" ); - printf( "inDomain: \"%s\"\n", inDomain ? inDomain : "" ); - printf( "inContext: 0x%08X\n", (uintptr_t) inContext ); - printf( "\n" ); -} - -//=========================================================================================================================== -// ResolveCallBack -//=========================================================================================================================== - -static void CALLBACK_COMPAT - ResolveCallBack( - DNSServiceRef inRef, - DNSServiceFlags inFlags, - uint32_t inInterfaceIndex, - DNSServiceErrorType inErrorCode, - const char * inFullName, - const char * inHostName, - uint16_t inPort, - uint16_t inTXTSize, - const char * inTXT, - void * inContext ) -{ - printf( "inRef: 0x%08X\n", (uintptr_t) inRef ); - printf( "inFlags: 0x%08X\n", (int) inFlags ); - printf( "inInterfaceIndex: 0x%08X\n", (int) inInterfaceIndex ); - printf( "inErrorCode: %ld\n", inErrorCode ); - printf( "inFullName: \"%s\"\n", inFullName ? inFullName : "" ); - printf( "inHostName: \"%s\"\n", inHostName ? inHostName : "" ); - printf( "inPort: %d\n", ntohs( inPort ) ); - printf( "inTXTSize: %ld\n", inTXTSize ); - printf( "inTXT: 0x%08X\n", (uintptr_t) inTXT ); - printf( "inContext: 0x%08X\n", (uintptr_t) inContext ); - printf( "\n" ); -} - -//=========================================================================================================================== -// RegisterCallBack -//=========================================================================================================================== - -static void CALLBACK_COMPAT - RegisterCallBack( - DNSServiceRef inRef, - DNSServiceFlags inFlags, - DNSServiceErrorType inErrorCode, - const char * inName, - const char * inType, - const char * inDomain, - void * inContext ) -{ - printf( "inRef: 0x%08X\n", (uintptr_t) inRef ); - printf( "inFlags: 0x%08X\n", (int) inFlags ); - printf( "inErrorCode: %ld\n", inErrorCode ); - printf( "inName: \"%s\"\n", inName ? inName : "" ); - printf( "inType: \"%s\"\n", inType ? inType : "" ); - printf( "inDomain: \"%s\"\n", inDomain ? inDomain : "" ); - printf( "inContext: 0x%08X\n", (uintptr_t) inContext ); - printf( "\n" ); -} - -//=========================================================================================================================== -// RecordCallBack -//=========================================================================================================================== - -static void CALLBACK_COMPAT - RecordCallBack( - DNSServiceRef inRef, - DNSRecordRef inRecordRef, - DNSServiceFlags inFlags, - DNSServiceErrorType inErrorCode, - void * inContext ) -{ - DEBUG_UNUSED( inRef ); - DEBUG_UNUSED( inRecordRef ); - DEBUG_UNUSED( inFlags ); - DEBUG_UNUSED( inContext ); - - if( inErrorCode == kDNSServiceErr_NoError ) - { - printf( "RecordCallBack: no errors\n" ); - } - else - { - printf( "RecordCallBack: %ld error\n", inErrorCode ); - } -} - -//=========================================================================================================================== -// QueryCallBack -//=========================================================================================================================== - -static void CALLBACK_COMPAT - QueryCallBack( - const DNSServiceRef inRef, - const DNSServiceFlags inFlags, - const uint32_t inInterfaceIndex, - const DNSServiceErrorType inErrorCode, - const char * inName, - const uint16_t inRRType, - const uint16_t inRRClass, - const uint16_t inRDataSize, - const void * inRData, - const uint32_t inTTL, - void * inContext ) -{ - DEBUG_UNUSED( inRef ); - DEBUG_UNUSED( inRRClass ); - DEBUG_UNUSED( inTTL ); - DEBUG_UNUSED( inContext ); - - if( inErrorCode == kDNSServiceErr_NoError ) - { - if( inFlags & kDNSServiceFlagsAdd ) - { - printf( "Add" ); - } - else - { - printf( "Rmv" ); - } - if( inFlags & kDNSServiceFlagsMoreComing ) - { - printf( "+" ); - } - else - { - printf( " " ); - } - printf(" 0x%04X %d %s rdata ", inFlags, inInterfaceIndex, inName ); - PrintRData( inRRType, (size_t) inRDataSize, (const uint8_t *) inRData ); - } - else - { - printf( "QueryCallback: %ld error\n", inErrorCode ); - } -} - -//=========================================================================================================================== -// PrintRData -//=========================================================================================================================== - -static void PrintRData( uint16_t inRRType, size_t inRDataSize, const uint8_t *inRData ) -{ - size_t i; - srv_rdata * srv; - char s[ 1005 ]; - struct in_addr in; - - switch( inRRType ) - { - case kDNSServiceDNSType_TXT: - - // Print all the alphanumeric and punctuation characters - - for( i = 0; i < inRDataSize; ++i ) - { - if( ( inRData[ i ] >= 32 ) && ( inRData[ i ] <= 127 ) ) - { - printf( "%c", inRData[ i ] ); - } - } - printf( "\n" ); - break; - - case kDNSServiceDNSType_SRV: - srv = (srv_rdata *)inRData; - ConvertDomainNameToCString_withescape(&srv->target, s, 0); - printf("pri=%d, w=%d, port=%d, target=%s\n", srv->priority, srv->weight, srv->port, s); - break; - - case kDNSServiceDNSType_A: - check( inRDataSize == 4 ); - memcpy( &in, inRData, sizeof( in ) ); - printf( "%s\n", inet_ntoa( in ) ); - break; - - case kDNSServiceDNSType_PTR: - ConvertDomainNameToCString_withescape( (domainname *) inRData, s, 0 ); - break; - - case kDNSServiceDNSType_AAAA: - check( inRDataSize == 16 ); - printf( "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X\n", - inRData[0], inRData[1], inRData[2], inRData[3], inRData[4], inRData[5], inRData[6], inRData[7], inRData[8], - inRData[9], inRData[10], inRData[11], inRData[12], inRData[13], inRData[14], inRData[15] ); - break; - - default: - printf( "ERROR: I dont know how to print inRData of type %d\n", inRRType ); - return; - } -} - -static char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc) - { - const unsigned char * src = label->c; // Domain label we're reading - const unsigned char len = *src++; // Read length of this (non-null) label - const unsigned char *const end = src + len; // Work out where the label ends - if (len > 63) return(NULL); // If illegal label, abort - while (src < end) // While we have characters in the label - { - unsigned char c = *src++; - if (esc) - { - if (c == '.') // If character is a dot, - *ptr++ = esc; // Output escape character - else if (c <= ' ') // If non-printing ascii, - { // Output decimal escape sequence - *ptr++ = esc; - *ptr++ = (char) ('0' + (c / 100) ); - *ptr++ = (char) ('0' + (c / 10) % 10); - c = (unsigned char)('0' + (c ) % 10); - } - } - *ptr++ = (char)c; // Copy the character - } - *ptr = 0; // Null-terminate the string - return(ptr); // and return - } - -static char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc) - { - const unsigned char *src = name->c; // Domain name we're reading - const unsigned char *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid - - if (*src == 0) *ptr++ = '.'; // Special case: For root, just write a dot - - while (*src) // While more characters in the domain name - { - if (src + 1 + *src >= max) return(NULL); - ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc); - if (!ptr) return(NULL); - src += 1 + *src; - *ptr++ = '.'; // Write the dot after the label - } - - *ptr++ = 0; // Null-terminate the string - return(ptr); // and return - } diff --git a/mDNSWindows/SystemServiceTest/Tool.mcp b/mDNSWindows/SystemServiceTest/Tool.mcp deleted file mode 100644 index 2f38283556de75bcc00a25ccd1fbb8f14bd0392a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 210145 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 X#hu Workaround Virtual PC bug that incorrectly modifies incoming mDNS packets + +*/ + +#define _WIN32_DCOM +#include "VPCDetect.h" +#include "DebugServices.h" +#include +#include + +# pragma comment(lib, "wbemuuid.lib") + +static BOOL g_doneCheck = FALSE; +static BOOL g_isVPC = FALSE; + + +BOOL +IsVPCRunning() +{ + IWbemLocator * pLoc = 0; + IWbemServices * pSvc = 0; + IEnumWbemClassObject * pEnumerator = NULL; + bool coInit = false; + HRESULT hres; + + // Short circuit if we've already done this + + require_action_quiet( !g_doneCheck, exit, g_doneCheck = TRUE ); + + // Initialize COM. + + hres = CoInitializeEx(0, COINIT_MULTITHREADED); + require_action( SUCCEEDED( hres ), exit, g_isVPC = false ); + coInit = true; + + // Initialize Security + + hres = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL ); + require_action( SUCCEEDED( hres ), exit, g_isVPC = false ); + + + // Obtain the initial locator to Windows Management on a particular host computer. + + hres = CoCreateInstance( CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLoc ); + require_action( SUCCEEDED( hres ), exit, g_isVPC = false ); + + // Connect to the root\cimv2 namespace with the + // current user and obtain pointer pSvc + // to make IWbemServices calls. + + hres = pLoc->ConnectServer( _bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &pSvc ); + require_action( SUCCEEDED( hres ), exit, g_isVPC = false ); + + // Set the IWbemServices proxy so that impersonation + // of the user (client) occurs. + + hres = CoSetProxyBlanket( pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE ); + require_action( SUCCEEDED( hres ), exit, g_isVPC = false ); + + // Use the IWbemServices pointer to make requests of WMI. + // Make requests here: + + hres = pSvc->ExecQuery( bstr_t("WQL"), bstr_t("SELECT * from Win32_BaseBoard"), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); + + require_action( SUCCEEDED( hres ), exit, g_isVPC = false ); + + do + { + IWbemClassObject* pInstance = NULL; + ULONG dwCount = NULL; + + hres = pEnumerator->Next( WBEM_INFINITE, 1, &pInstance, &dwCount); + + if ( pInstance ) + { + VARIANT v; + BSTR strClassProp = SysAllocString(L"Manufacturer"); + HRESULT hr; + + hr = pInstance->Get(strClassProp, 0, &v, 0, 0); + SysFreeString(strClassProp); + + // check the HRESULT to see if the action succeeded. + + if (SUCCEEDED(hr) && (V_VT(&v) == VT_BSTR)) + { + wchar_t * wstring = wcslwr( V_BSTR( &v ) ); + + if (wcscmp( wstring, L"microsoft corporation" ) == 0 ) + { + g_isVPC = true; + } + } + + VariantClear(&v); + } + } while (hres == WBEM_S_NO_ERROR); + +exit: + + if ( pSvc != NULL ) + { + pSvc->Release(); + } + + if ( pLoc != NULL ) + { + pLoc->Release(); + } + + if ( coInit ) + { + CoUninitialize(); + } + + if ( !g_doneCheck ) + { + g_doneCheck = TRUE; + + if ( g_isVPC ) + { + dlog( kDebugLevelTrace, "Virtual PC detected" ); + } + } + + return g_isVPC; +} diff --git a/mDNSWindows/SystemServiceTest/Prefix.h b/mDNSWindows/VPCDetect.h similarity index 67% rename from mDNSWindows/SystemServiceTest/Prefix.h rename to mDNSWindows/VPCDetect.h index 53fb893..ed709ac 100644 --- a/mDNSWindows/SystemServiceTest/Prefix.h +++ b/mDNSWindows/VPCDetect.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -21,27 +21,28 @@ * @APPLE_LICENSE_HEADER_END@ Change History (most recent first): + +$Log: VPCDetect.h,v $ +Revision 1.1 2005/11/27 20:21:16 herscher + Workaround Virtual PC bug that incorrectly modifies incoming mDNS packets -$Log: Prefix.h,v $ -Revision 1.1 2004/06/18 04:17:43 rpantos -Move up one level. - -Revision 1.1 2004/01/30 02:58:57 bradley -Test tool for the mDNSResponder Windows service. - */ -#ifndef __PREFIX__ -#define __PREFIX__ +#pragma once + +#include + -#if( defined( _DEBUG ) ) - #define DEBUG 1 - #define MDNS_DEBUGMSGS 1 -#else - #define DEBUG 0 +#if defined(__cplusplus) +extern "C" +{ #endif -#define DNS_SD_DIRECT_ENABLED 0 -#define DNS_SD_CLIENT_ENABLED 1 -#endif // __PREFIX__ +extern BOOL +IsVPCRunning(); + + +#if defined(__cplusplus) +} +#endif diff --git a/mDNSWindows/WinVersRes.h b/mDNSWindows/WinVersRes.h index 884d661..8256ddf 100644 --- a/mDNSWindows/WinVersRes.h +++ b/mDNSWindows/WinVersRes.h @@ -23,6 +23,69 @@ Change History (most recent first): $Log: WinVersRes.h,v $ +Revision 1.51 2005/11/28 19:49:56 herscher +Bump to 1.0.2.9 + +Revision 1.50 2005/10/28 18:48:04 herscher +Bump to version 1.0.2.8 + +Revision 1.49 2005/10/25 05:24:27 herscher +Bump to 1.0.2.7 + +Revision 1.48 2005/10/18 06:17:05 herscher +Bump to 1.0.2.6 + +Revision 1.47 2005/10/05 22:16:57 herscher +Bump version number to 1.0.2.5 + +Revision 1.46 2005/09/29 06:43:07 herscher +Bump to version 1.0.2.4 + +Revision 1.45 2005/09/22 07:11:35 herscher +Bump version to 1.0.2.3 + +Revision 1.44 2005/09/13 01:07:40 herscher +Bump to 1.0.2.2 + +Revision 1.43 2005/09/12 06:13:32 herscher +Bump version 1.0.2.1 + +Revision 1.42 2005/07/22 19:41:44 ksekar +Update version + +Revision 1.41 2005/07/15 15:22:20 shersche +Bump to 1.0.1.1 + +Revision 1.40 2005/07/11 20:42:03 shersche +Bump version to 1.0.0.68 + +Revision 1.39 2005/07/07 19:12:47 shersche +Bump to 1.0.0.67 + +Revision 1.38 2005/04/25 21:59:42 shersche +Bump to 1.0.0.66 + +Revision 1.37 2005/04/22 07:39:48 shersche +Bump to 1.0.0.65 + +Revision 1.36 2005/04/19 07:25:56 shersche +Bump version to 1.0.0.64 + +Revision 1.35 2005/04/13 17:48:58 shersche +Bump to 1.0.0.63 + +Revision 1.34 2005/04/06 01:00:55 shersche +Bump to 1.0.0.62 + +Revision 1.33 2005/03/30 07:37:41 shersche +Bump to 1.0.0.61 + +Revision 1.32 2005/03/23 00:39:46 shersche +Bump to version 1.0.0.60 + +Revision 1.31 2005/03/16 03:52:06 shersche +Bump to 1.0.0.59 + Revision 1.30 2005/03/07 19:18:18 shersche Update Windows build to 1.0.0.58 @@ -121,10 +184,10 @@ First checked in. #define MASTER_PROD_NAME "Bonjour" // Define the product version for mDNSResponder on Windows -#define MASTER_PROD_VERS 1,0,0,58 -#define MASTER_PROD_VERS_STR "1,0,0,58" -#define MASTER_PROD_VERS_STR2 "1.0.0.58" -#define MASTER_PROD_VERS_STR3 "Explorer Plugin 1.0.0.58" +#define MASTER_PROD_VERS 1,0,2,9 +#define MASTER_PROD_VERS_STR "1,0,2,9" +#define MASTER_PROD_VERS_STR2 "1.0.2.9" +#define MASTER_PROD_VERS_STR3 "Explorer Plugin 1.0.2.9" // Define the legal copyright #define MASTER_LEGAL_COPYRIGHT "Copyright (C) 2003-2005 Apple Computer, Inc." diff --git a/mDNSWindows/dDNS.c b/mDNSWindows/dDNS.c index 08cbc88..0435517 100755 --- a/mDNSWindows/dDNS.c +++ b/mDNSWindows/dDNS.c @@ -22,10 +22,22 @@ Change History (most recent first): +$Log: dDNS.c,v $ +Revision 1.8 2005/09/12 07:13:33 herscher + Workaround for router crash. Lazily call RegisterSearchDomains rather than call it always at startup. + +Revision 1.7 2005/08/10 01:43:23 herscher +Pass NULL in for the IPv6 paramter to mDNS_SetPrimaryInterfaceInfo to get this code to compile on Windows. + +Revision 1.6 2005/03/23 05:54:48 cheshire + Fix build warnings +Fix %s where it should be %##s in debugf & LogMsg calls + */ #include "dDNS.h" #include "DNSCommon.h" +#include "uds_daemon.h" #include #include #include @@ -52,7 +64,11 @@ static ARListElem *SCPrefBrowseDomains = mDNSNULL; // manually generated local-o static domainname dDNSRegDomain; // Default wide-area zone for service registration static DNameListElem * dDNSBrowseDomains; // Default wide-area zone for legacy ("empty string") browses static domainname dDNSHostname; +static mDNSBool dDNSRegisterSearchDomains = mDNSfalse; +mDNSlocal mStatus RegisterNameServers( mDNS *const m ); +mDNSlocal mStatus RegisterSearchDomains( mDNS *const m ); + mStatus dDNS_SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa) { @@ -81,8 +97,20 @@ mStatus dDNS_SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa) LogMsg("SetupAddr invalid sa_family %d", sa->sa_family); return(mStatus_Invalid); + } + + +mStatus dDNS_RegisterSearchDomains( mDNS * const m ) + { + mStatus err = mStatus_NoError; + + dDNSRegisterSearchDomains = mDNStrue; + RegisterSearchDomains( m ); + + return err; } + mDNSlocal void MarkSearchListElem(domainname *domain) { SearchListElem *new, *ptr; @@ -166,7 +194,7 @@ mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceR { if (SameDomainName(&ptr->ar.resrec.rdata->u.name, &answer->rdata->u.name)) { - debugf("Deregistering PTR %s -> %s", ptr->ar.resrec.name->c, ptr->ar.resrec.rdata->u.name.c); + debugf("Deregistering PTR %##s -> %##s", ptr->ar.resrec.name->c, ptr->ar.resrec.rdata->u.name.c); dereg = &ptr->ar; if (prev) prev->next = ptr->next; else slElem->AuthRecs = ptr->next; @@ -271,7 +299,7 @@ mDNSlocal void FoundDefBrowseDomain(mDNS *const m, DNSQuestion *question, const prev = ptr; ptr = ptr->next; } - LogMsg("FoundDefBrowseDomain: Got remove event for domain %s not in list", answer->rdata->u.name.c); + LogMsg("FoundDefBrowseDomain: Got remove event for domain %##s not in list", answer->rdata->u.name.c); } } @@ -359,7 +387,7 @@ mDNSlocal mStatus RegisterSearchDomains( mDNS *const m ) { AuthRecord *dereg = &arList->ar; arList = arList->next; - debugf("Deregistering PTR %s -> %s", dereg->resrec.name->c, dereg->resrec.rdata->u.name.c); + debugf("Deregistering PTR %##s -> %##s", dereg->resrec.name->c, dereg->resrec.rdata->u.name.c); err = mDNS_Deregister(m, dereg); if (err) LogMsg("ERROR: RegisterSearchDomains mDNS_Deregister returned %d", err); } @@ -471,7 +499,7 @@ mDNSlocal void SetSCPrefsBrowseDomains(mDNS *m, DNameListElem * browseDomains, m { if ( !browseDomain->name.c[0] ) { - LogMsg("SetSCPrefsBrowseDomains bad DDNS browse domain: %s", browseDomain->name.c[0] ? browseDomain->name.c : "(unknown)"); + LogMsg("SetSCPrefsBrowseDomains bad DDNS browse domain: %##s", browseDomain->name.c[0] ? (char*) browseDomain->name.c : "(unknown)"); } else { @@ -554,9 +582,13 @@ mStatus dDNS_Setup( mDNS *const m ) // YO CFRelease(key); // handle any changes to search domains and DNS server addresses + if ( dDNSPlatformRegisterSplitDNS(m) != mStatus_NoError) if (dict) RegisterNameServers( m ); // fall back to non-split DNS aware configuration on failure - RegisterSearchDomains( m ); // note that we register name servers *before* search domains + + if ( dDNSRegisterSearchDomains == mDNStrue ) + dDNS_RegisterSearchDomains( m ); // note that we register name servers *before* search domains + // if (dict) CFRelease(dict); // get IPv4 settings @@ -588,7 +620,10 @@ mStatus dDNS_Setup( mDNS *const m ) if ( dDNSPlatformGetPrimaryInterface( m, &ip, &r ) == mStatus_NoError ) { - mDNS_SetPrimaryInterfaceInfo(m, &ip, r.ip.v4.NotAnInteger ? &r : mDNSNULL); + // For now, we're going to pass NULL for the IPv6 parameter so that the Windows code compiles. What needs + // to happen is that the implementation of dDNSPlatformGetPrimaryInterface() needs to call the + // IPv6 aware "GetAdaptersAddresses" rather than GetAdaptersInfo(). + mDNS_SetPrimaryInterfaceInfo(m, &ip, NULL, r.ip.v4.NotAnInteger ? &r : mDNSNULL); } return mStatus_NoError; @@ -608,14 +643,28 @@ mStatus dDNS_Setup( mDNS *const m ) mStatus dDNS_InitDNSConfig(mDNS *const m) { mStatus err; + static AuthRecord LocalRegPTR; // start query for domains to be used in default (empty string domain) browses err = mDNS_GetDomains(m, &LegacyBrowseDomainQ, mDNS_DomainTypeBrowseLegacy, NULL, mDNSInterface_LocalOnly, FoundDefBrowseDomain, NULL); // provide .local automatically SetSCPrefsBrowseDomain(m, &localdomain, mDNStrue); + + // dns-sd -E does not return "local." + // register registration domain "local" + mDNS_SetupResourceRecord(&LocalRegPTR, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, NULL, NULL); + MakeDomainNameFromDNSNameString(LocalRegPTR.resrec.name, mDNS_DomainTypeNames[mDNS_DomainTypeRegistration]); + AppendDNSNameString (LocalRegPTR.resrec.name, "local"); + AssignDomainName(&LocalRegPTR.resrec.rdata->u.name, &localdomain); + err = mDNS_Register(m, &LocalRegPTR); + if (err) + { + LogMsg("ERROR: dDNS_InitDNSConfig - mDNS_Register returned error %d", err); + } + return mStatus_NoError; -} + } void dDNS_FreeIPAddrList(IPAddrListElem * list) diff --git a/mDNSWindows/dDNS.h b/mDNSWindows/dDNS.h index e1b148e..12c56da 100755 --- a/mDNSWindows/dDNS.h +++ b/mDNSWindows/dDNS.h @@ -35,6 +35,11 @@ #pragma mark - DynDNS structures #endif +#if WIN32 +// named type definition in parentheses +# pragma warning( disable: 4115 ) +#endif + typedef struct IPAddrListElem { mDNSAddr addr; diff --git a/mDNSWindows/loclibrary.c b/mDNSWindows/loclibrary.c index 7a90e66..7084d16 100755 --- a/mDNSWindows/loclibrary.c +++ b/mDNSWindows/loclibrary.c @@ -72,7 +72,7 @@ static int _getISOCode(LANGID wLangID, char *isoLangCode, int codeLen) { int startIndex = i * MODULO_ISOCODES; langCode = (ISOCODES[startIndex] << 8); - langCode += ( (unsigned short) (ISOCODES[startIndex + 1]) ); + langCode = langCode + ( (unsigned short) (ISOCODES[startIndex + 1]) ); if (langCode == wLangID) { char *langStr = (char *)&(ISOCODES[startIndex+2]); @@ -118,6 +118,7 @@ int PathForResourceA ( HMODULE module, const char *name, char *locFile, int locF if ( !strcmp( appPathNameA, "" ) ) { char folder[MAX_PATH]; + char * ext; char * app; GetModuleFileNameA( module, folder, MAX_PATH ); @@ -126,10 +127,16 @@ int PathForResourceA ( HMODULE module, const char *name, char *locFile, int locF app = strrchr( folder, '\\' ); require_action( app, exit, ret = 0 ); - *app++ = '\0'; - snprintf( appPathNameA, MAX_PATH, "%s\\Resources\\%s", folder, app ); + // Strip the extension + + if ( ( ( ext = strstr( app, ".exe" ) ) != NULL ) || ( ( ext = strstr( app, ".dll" ) ) != NULL ) ) + { + *ext = '\0'; + } + + snprintf( appPathNameA, MAX_PATH, "%s\\%s", folder, app ); } ret = PathForResourceWithPathA (appPathNameA, name, locFile, locFileLen); @@ -149,6 +156,7 @@ int PathForResourceW ( HMODULE module, const wchar_t *name, wchar_t *locFile, in { wchar_t folder[MAX_PATH]; wchar_t * app; + wchar_t * ext; GetModuleFileNameW( module, folder, MAX_PATH); @@ -156,10 +164,16 @@ int PathForResourceW ( HMODULE module, const wchar_t *name, wchar_t *locFile, in app = wcsrchr( folder, '\\' ); require_action( app, exit, ret = 0 ); - *app++ = '\0'; - swprintf( appPathNameW, MAX_PATH, L"%ls\\Resources\\%ls", folder, app ); + // Strip the extension + + if ( ( ( ext = wcsstr( app, L".exe" ) ) != NULL ) || ( ( ext = wcsstr( app, L".dll" ) ) != NULL ) ) + { + *ext = '\0'; + } + + swprintf( appPathNameW, MAX_PATH, L"%ls\\%ls", folder, app ); } ret = PathForResourceWithPathW (appPathNameW, name, locFile, locFileLen); diff --git a/mDNSWindows/mDNSWin32.c b/mDNSWindows/mDNSWin32.c index 6e7bb36..93a14a7 100755 --- a/mDNSWindows/mDNSWin32.c +++ b/mDNSWindows/mDNSWin32.c @@ -23,6 +23,73 @@ Change History (most recent first): $Log: mDNSWin32.c,v $ +Revision 1.105 2005/11/27 20:21:16 herscher + Workaround Virtual PC bug that incorrectly modifies incoming mDNS packets + +Revision 1.104 2005/10/19 19:42:59 herscher + Use the registry to determine the domain name, rather than using GetNetworkParams(). GetNetworkParams() does not reliably return domain information for the current network configuration. + +Revision 1.103 2005/10/18 06:13:20 herscher + Prepend "$" to key name to ensure that secure updates work if the domain name and key name are the same + +Revision 1.102 2005/10/05 20:55:14 herscher + Don't call SetLLRoute on loopback interface + +Revision 1.101 2005/10/05 18:05:28 herscher + Save Wide-Area preferences in a different spot in the registry so they don't get removed when doing an update install. + +Revision 1.100 2005/09/29 06:36:00 herscher +Change check( err ) to check( !err ). This was a typo that was introduced in a previous checkin. + +Revision 1.99 2005/09/29 06:31:46 herscher + Fall back to calling getifaddrs_ipv4 if getifaddrs_ipv6 fails + +Revision 1.98 2005/09/24 01:11:56 cheshire +Add comment about GetWindowsVersionString + +Revision 1.97 2005/09/22 07:10:44 herscher + Don't send domain enumeration query if domain is empty string or "." + +Revision 1.96 2005/09/22 07:06:06 herscher + Don't loop uncontrollably upon detection of error in main event loop + +Revision 1.95 2005/09/11 22:51:40 herscher + Obtain Hostname by using GetComputerNameEx, rather than gethostname. + +Revision 1.94 2005/09/11 21:43:15 herscher + Don't create HINFO records on Windows + +Revision 1.93 2005/07/15 06:06:40 shersche + Change all WinSock allocation loops to bail out after 100 tries to eliminate the possibility of any infinite loops + +Revision 1.92 2005/07/11 20:32:17 shersche + Fix crash when logging into Cisco VPN + +Revision 1.91 2005/04/25 21:34:28 shersche + Wide-Area services don't disappear when interface goes away + +Revision 1.90 2005/04/25 21:18:08 shersche + mDNSResponder crash when interface goes away. This error seems to be caused by the Windows platform code not returning mStatus_TransientErr when there is a problem with a udp unicast send. + +Revision 1.89 2005/04/22 07:32:24 shersche + PPP connection disables Bonjour .local lookups + mDNSResponder ignores Point-to-Point interfaces + +Revision 1.88 2005/04/05 03:53:03 shersche + Registering with shared secret key doesn't work. + +Revision 1.87 2005/04/03 08:03:12 shersche + mDNSResponder won't start on Windows 2000. + +Revision 1.86 2005/03/30 07:37:14 shersche +Use prefix to compute IPv4 subnet mask, falling back to calling AddressToIndexAndMask only if prefix is zero. + +Revision 1.85 2005/03/30 07:34:52 shersche + Interface index being returned is 512 + +Revision 1.84 2005/03/29 19:19:47 shersche + Windows is not accepting unicast responses. This bug was a result of an error in obtaining the subnet mask for IPv4 interfaces. + Revision 1.83 2005/03/07 18:27:42 shersche Fix problem when ControlPanel commits changes to the browse domain list @@ -333,6 +400,7 @@ Multicast DNS platform plugin for Win32 #include "CommonServices.h" #include "DebugServices.h" +#include "VPCDetect.h" #include "RegNames.h" #include @@ -361,6 +429,7 @@ Multicast DNS platform plugin for Win32 #define MDNS_WINDOWS_ENABLE_IPV4 1 #define MDNS_WINDOWS_ENABLE_IPV6 1 #define MDNS_FIX_IPHLPAPI_PREFIX_BUG 1 +#define MDNS_SET_HINFO_STRINGS 0 #define kMDNSDefaultName "My Computer" @@ -381,6 +450,8 @@ Multicast DNS platform plugin for Win32 static GUID kWSARecvMsgGUID = WSAID_WSARECVMSG; #endif +#define kIPv6IfIndexBase (10000000L) + #if 0 #pragma mark == Prototypes == @@ -446,6 +517,16 @@ mDNSexport mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInter // Utilities +typedef struct PolyString PolyString; + +struct PolyString +{ + domainname m_dname; + char m_utf8[256]; + PLSA_UNICODE_STRING m_lsa; +}; + + #if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS ) mDNSlocal int getifaddrs_ipv6( struct ifaddrs **outAddrs ); #endif @@ -458,15 +539,18 @@ mDNSexport mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInter mDNSlocal int getifaddrs_ce( struct ifaddrs **outAddrs ); #endif -mDNSlocal mDNSBool CanReceiveUnicast( void ); +mDNSlocal DWORD GetPrimaryInterface(); +mDNSlocal mStatus AddressToIndexAndMask( struct sockaddr * address, uint32_t * index, struct sockaddr * mask ); +mDNSlocal mDNSBool CanReceiveUnicast( void ); +mDNSlocal mDNSBool IsPointToPoint( IP_ADAPTER_UNICAST_ADDRESS * addr ); mDNSlocal mStatus StringToAddress( mDNSAddr * ip, LPSTR string ); mDNSlocal mStatus RegQueryString( HKEY key, LPCSTR param, LPSTR * string, DWORD * stringLen, DWORD * enabled ); mDNSlocal struct ifaddrs* myGetIfAddrs(int refresh); mDNSlocal OSStatus TCHARtoUTF8( const TCHAR *inString, char *inBuffer, size_t inBufferSize ); mDNSlocal OSStatus WindowsLatin1toUTF8( const char *inString, char *inBuffer, size_t inBufferSize ); -mDNSlocal OSStatus ConvertUTF8ToLsaString( const char * input, PLSA_UNICODE_STRING output ); -mDNSlocal OSStatus ConvertLsaStringToUTF8( PLSA_UNICODE_STRING input, char ** output ); +mDNSlocal OSStatus MakeLsaStringFromUTF8String( PLSA_UNICODE_STRING output, const char * input ); +mDNSlocal OSStatus MakeUTF8StringFromLsaString( char * output, size_t len, PLSA_UNICODE_STRING input ); mDNSlocal void FreeTCPConnectionData( mDNSTCPConnectionData * data ); #ifdef __cplusplus @@ -543,8 +627,11 @@ mStatus mDNSPlatformInit( mDNS * const inMDNS ) // Setup the HINFO HW/SW strings. +#if ( MDNS_SET_HINFO_STRINGS ) err = GetWindowsVersionString( (char *) &inMDNS->HIHardware.c[ 1 ], sizeof( inMDNS->HIHardware.c ) - 2 ); check_noerr( err ); + // Note that GetWindowsVersionString guarantees that the resulting string is always null-terminated, + // so the following strlen call is safe inMDNS->HIHardware.c[ 0 ] = (mDNSu8) mDNSPlatformStrLen( &inMDNS->HIHardware.c[ 1 ] ); dlog( kDebugLevelInfo, DEBUG_NAME "HIHardware: %#s\n", inMDNS->HIHardware.c ); @@ -552,6 +639,7 @@ mStatus mDNSPlatformInit( mDNS * const inMDNS ) "mDNSResponder (%s %s)", __DATE__, __TIME__ ); inMDNS->HISoftware.c[ 0 ] = (mDNSu8) mDNSPlatformStrLen( &inMDNS->HISoftware.c[ 1 ] ); dlog( kDebugLevelInfo, DEBUG_NAME "HISoftware: %#s\n", inMDNS->HISoftware.c ); +#endif // Set up the IPv4 unicast socket @@ -578,10 +666,18 @@ mStatus mDNSPlatformInit( mDNS * const inMDNS ) { DWORD size; - err = WSAIoctl( inMDNS->p->unicastSock4, SIO_GET_EXTENSION_FUNCTION_POINTER, &kWSARecvMsgGUID, - sizeof( kWSARecvMsgGUID ), &inMDNS->p->unicastSock4RecvMsgPtr, sizeof( inMDNS->p->unicastSock4RecvMsgPtr ), &size, NULL, NULL ); + // If we are running inside VPC, then we won't use WSARecvMsg because it will give us bogus information due to + // a bug in VPC itself. + + err = IsVPCRunning(); + + if ( !err ) + { + err = WSAIoctl( inMDNS->p->unicastSock4, SIO_GET_EXTENSION_FUNCTION_POINTER, &kWSARecvMsgGUID, + sizeof( kWSARecvMsgGUID ), &inMDNS->p->unicastSock4RecvMsgPtr, sizeof( inMDNS->p->unicastSock4RecvMsgPtr ), &size, NULL, NULL ); + } - if ( err != 0 ) + if ( err ) { inMDNS->p->unicastSock4RecvMsgPtr = NULL; } @@ -793,7 +889,20 @@ mStatus { n = sendto( sendingsocket, (char *) inMsg, n, 0, (struct sockaddr *) &addr, sizeof( addr ) ); err = translate_errno( n > 0, errno_compat(), kWriteErr ); - require_noerr( err, exit ); + + if ( err ) + { + // Don't report EHOSTDOWN (i.e. ARP failure), ENETDOWN, or no route to host for unicast destinations + + if ( !mDNSAddressIsAllDNSLinkGroup( inDstIP ) && ( WSAGetLastError() == WSAEHOSTDOWN || WSAGetLastError() == WSAENETDOWN || WSAGetLastError() == WSAEHOSTUNREACH || WSAGetLastError() == WSAENETUNREACH ) ) + { + err = mStatus_TransientErr; + } + else + { + require_noerr( err, exit ); + } + } } exit: @@ -960,7 +1069,7 @@ mDNSs32 mDNSPlatformRawTime( void ) mDNSexport mDNSs32 mDNSPlatformUTC( void ) { - return( -1 ); + return ( mDNSs32 ) time( NULL ); } //=========================================================================================================================== @@ -1305,7 +1414,7 @@ dDNSPlatformGetConfig(domainname * const fqdn, domainname *const regDomain, DNam *browseDomains = NULL; - err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\") kServiceName TEXT("\\Parameters\\DynDNS\\Setup\\") kServiceDynDNSHostNames, &key ); + err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSHostNames, &key ); require_noerr( err, exit ); err = RegQueryString( key, "", &name, &dwSize, &enabled ); @@ -1329,7 +1438,7 @@ dDNSPlatformGetConfig(domainname * const fqdn, domainname *const regDomain, DNam name = NULL; } - err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\") kServiceName TEXT("\\Parameters\\DynDNS\\Setup\\") kServiceDynDNSBrowseDomains, &key ); + err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSBrowseDomains, &key ); require_noerr( err, exit ); // Get information about this node @@ -1382,7 +1491,7 @@ dDNSPlatformGetConfig(domainname * const fqdn, domainname *const regDomain, DNam key = NULL; } - err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\") kServiceName TEXT("\\Parameters\\DynDNS\\Setup\\") kServiceDynDNSRegistrationDomains, &key ); + err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSRegistrationDomains, &key ); require_noerr( err, exit ); err = RegQueryString( key, "", &name, &dwSize, &enabled ); @@ -1438,7 +1547,7 @@ dDNSPlatformSetNameStatus(domainname *const dname, mStatus status) } check( strlen( p ) <= MAX_ESCAPED_DOMAIN_NAME ); - name = TEXT("SYSTEM\\CurrentControlSet\\Services\\") kServiceName TEXT("\\Parameters\\DynDNS\\State\\HostNames"); + name = kServiceParametersNode TEXT("\\DynDNS\\State\\HostNames"); err = RegCreateKey( HKEY_LOCAL_MACHINE, name, &key ); require_noerr( err, exit ); @@ -1462,36 +1571,38 @@ exit: //=========================================================================================================================== void -dDNSPlatformSetSecretForDomain( mDNS *m, const domainname * domain ) +dDNSPlatformSetSecretForDomain( mDNS *m, const domainname * inDomain ) { - char dstring[MAX_ESCAPED_DOMAIN_NAME]; - domainname * d; - domainname canon; + PolyString domain; + PolyString key; + PolyString secret; size_t i; size_t dlen; LSA_OBJECT_ATTRIBUTES attrs; LSA_HANDLE handle = NULL; - LSA_UNICODE_STRING keyName = { 0, 0, NULL }; - LSA_UNICODE_STRING * secret = NULL; - char * converted = NULL; NTSTATUS res; OSStatus err; + // Initialize PolyStrings + + domain.m_lsa = NULL; + key.m_lsa = NULL; + secret.m_lsa = NULL; + // canonicalize name by converting to lower case (keychain and some name servers are case sensitive) - ConvertDomainNameToCString(domain, dstring); - dlen = strlen(dstring); - for (i = 0; i < dlen; i++) + ConvertDomainNameToCString( inDomain, domain.m_utf8 ); + dlen = strlen( domain.m_utf8 ); + for ( i = 0; i < dlen; i++ ) { - dstring[i] = (char) tolower(dstring[i]); // canonicalize -> lower case + domain.m_utf8[i] = (char) tolower( domain.m_utf8[i] ); // canonicalize -> lower case } - MakeDomainNameFromDNSNameString(&canon, dstring); - d = &canon; + MakeDomainNameFromDNSNameString( &domain.m_dname, domain.m_utf8 ); // attrs are reserved, so initialize to zeroes. - ZeroMemory(&attrs, sizeof( attrs ) ); + ZeroMemory( &attrs, sizeof( attrs ) ); // Get a handle to the Policy object on the local system @@ -1501,38 +1612,63 @@ dDNSPlatformSetSecretForDomain( mDNS *m, const domainname * domain ) // Get the encrypted data - err = ConvertUTF8ToLsaString( dstring, &keyName ); + domain.m_lsa = ( PLSA_UNICODE_STRING) malloc( sizeof( LSA_UNICODE_STRING ) ); + require_action( domain.m_lsa != NULL, exit, err = mStatus_NoMemoryErr ); + err = MakeLsaStringFromUTF8String( domain.m_lsa, domain.m_utf8 ); require_noerr( err, exit ); - res = LsaRetrievePrivateData( handle, &keyName, &secret ); + // Retrieve the key + + res = LsaRetrievePrivateData( handle, domain.m_lsa, &key.m_lsa ); err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr ); require_noerr_quiet( err, exit ); - // Convert the unicode to string to 8 bit + // Lsa secrets use a flat naming space. Therefore, we will prepend "$" to the keyname to + // make sure it doesn't conflict with a zone name. + + // Convert the key to a domainname. Strip off the "$" prefix. - err = ConvertLsaStringToUTF8( secret, &converted ); + err = MakeUTF8StringFromLsaString( key.m_utf8, sizeof( key.m_utf8 ), key.m_lsa ); require_noerr( err, exit ); + require_action( key.m_utf8[0] == '$', exit, err = kUnknownErr ); + MakeDomainNameFromDNSNameString( &key.m_dname, key.m_utf8 + 1 ); + + // Retrieve the secret - mDNS_SetSecretForZone( m, d, d, converted ); + res = LsaRetrievePrivateData( handle, key.m_lsa, &secret.m_lsa ); + err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr ); + require_noerr_quiet( err, exit ); + + // Convert the secret to UTF8 string + + err = MakeUTF8StringFromLsaString( secret.m_utf8, sizeof( secret.m_utf8 ), secret.m_lsa ); + require_noerr( err, exit ); + + // And finally, tell the core about this secret + + debugf("Setting shared secret for zone %s with key %##s", domain.m_utf8, key.m_dname.c); + mDNS_SetSecretForZone( m, &domain.m_dname, &key.m_dname, secret.m_utf8 ); exit: - if ( converted ) + if ( domain.m_lsa != NULL ) { - free( converted ); - converted = NULL; + if ( domain.m_lsa->Buffer != NULL ) + { + free( domain.m_lsa->Buffer ); + } + + free( domain.m_lsa ); } - if ( secret ) + if ( key.m_lsa != NULL ) { - LsaFreeMemory( secret ); - secret = NULL; + LsaFreeMemory( key.m_lsa ); } - if ( keyName.Buffer ) + if ( secret.m_lsa != NULL ) { - free( keyName.Buffer ); - keyName.Buffer = NULL; + LsaFreeMemory( secret.m_lsa ); } if ( handle ) @@ -1569,26 +1705,29 @@ dDNSPlatformGetSearchDomainList( void ) tok = strtok( searchList, "," ); while ( tok ) { - domainname domain; - - if ( MakeDomainNameFromDNSNameString( &domain, tok ) ) + if ( ( strcmp( tok, "" ) != 0 ) && ( strcmp( tok, "." ) != 0 ) ) { - DNameListElem * last = current; + domainname domain; - current = (DNameListElem*) malloc( sizeof( DNameListElem ) ); - require_action( current, exit, err = mStatus_NoMemoryErr ); - - AssignDomainName( ¤t->name, &domain ); - current->next = NULL; - - if ( !head ) + if ( MakeDomainNameFromDNSNameString( &domain, tok ) ) { - head = current; - } + DNameListElem * last = current; + + current = (DNameListElem*) malloc( sizeof( DNameListElem ) ); + require_action( current, exit, err = mStatus_NoMemoryErr ); + + AssignDomainName( ¤t->name, &domain ); + current->next = NULL; + + if ( !head ) + { + head = current; + } - if ( last ) - { - last->next = current; + if ( last ) + { + last->next = current; + } } } @@ -1680,35 +1819,74 @@ exit: IPAddrListElem* dDNSPlatformGetDNSServers( void ) { - FIXED_INFO * fixedInfo = NULL; - ULONG bufLen = sizeof( FIXED_INFO ); - IP_ADDR_STRING * ipAddr; - IPAddrListElem * head = NULL; - IPAddrListElem * current = NULL; - int i = 0; - mStatus err; + PIP_PER_ADAPTER_INFO pAdapterInfo = NULL; + FIXED_INFO * fixedInfo = NULL; + ULONG bufLen = 0; + IP_ADDR_STRING * dnsServerList; + IP_ADDR_STRING * ipAddr; + IPAddrListElem * head = NULL; + IPAddrListElem * current = NULL; + DWORD index; + int i = 0; + mStatus err = kUnknownErr; + + // Get the primary interface. - while ( 1 ) + index = GetPrimaryInterface(); + + // This should have the interface index of the primary index. Fall back in cases where + // it can't be determined. + + if ( index ) { - if ( fixedInfo ) + bufLen = 0; + + for ( i = 0; i < 100; i++ ) { - GlobalFree( fixedInfo ); - fixedInfo = NULL; + err = GetPerAdapterInfo( index, pAdapterInfo, &bufLen ); + + if ( err != ERROR_BUFFER_OVERFLOW ) + { + break; + } + + pAdapterInfo = (PIP_PER_ADAPTER_INFO) realloc( pAdapterInfo, bufLen ); + require_action( pAdapterInfo, exit, err = mStatus_NoMemoryErr ); } - fixedInfo = (FIXED_INFO*) GlobalAlloc( GPTR, bufLen ); - - err = GetNetworkParams( fixedInfo, &bufLen ); + require_noerr( err, exit ); + + dnsServerList = &pAdapterInfo->DnsServerList; + } + else + { + bufLen = sizeof( FIXED_INFO ); - if ( ( err != ERROR_BUFFER_OVERFLOW ) || ( i++ == 100 ) ) + for ( i = 0; i < 100; i++ ) { - break; + if ( fixedInfo ) + { + GlobalFree( fixedInfo ); + fixedInfo = NULL; + } + + fixedInfo = (FIXED_INFO*) GlobalAlloc( GPTR, bufLen ); + require_action( fixedInfo, exit, err = mStatus_NoMemoryErr ); + + err = GetNetworkParams( fixedInfo, &bufLen ); + + if ( err != ERROR_BUFFER_OVERFLOW ) + { + break; + } } - } - require_noerr( err, exit ); + require_noerr( err, exit ); - for ( ipAddr = &fixedInfo->DnsServerList; ipAddr; ipAddr = ipAddr->Next ) + dnsServerList = &fixedInfo->DnsServerList; + } + + for ( ipAddr = dnsServerList; ipAddr; ipAddr = ipAddr->Next ) { mDNSAddr addr; IPAddrListElem * last = current; @@ -1739,6 +1917,11 @@ dDNSPlatformGetDNSServers( void ) exit: + if ( pAdapterInfo ) + { + free( pAdapterInfo ); + } + if ( fixedInfo ) { GlobalFree( fixedInfo ); @@ -1755,55 +1938,97 @@ exit: DNameListElem* dDNSPlatformGetDomainName( void ) { - FIXED_INFO * fixedInfo = NULL; - ULONG bufLen = sizeof( FIXED_INFO ); DNameListElem * head = NULL; int i = 0; - mStatus err; + IP_ADAPTER_INFO * pAdapterInfo; + IP_ADAPTER_INFO * pAdapter; + DWORD bufLen; + DWORD index; + HKEY key = NULL; + LPSTR domain = NULL; + domainname dname; + DWORD dwSize; + mStatus err = mStatus_NoError; - while ( 1 ) + pAdapterInfo = NULL; + + for ( i = 0; i < 100; i++ ) { - if ( fixedInfo ) - { - GlobalFree( fixedInfo ); - fixedInfo = NULL; - } - - fixedInfo = (FIXED_INFO*) GlobalAlloc( GPTR, bufLen ); - - err = GetNetworkParams( fixedInfo, &bufLen ); + err = GetAdaptersInfo( pAdapterInfo, &bufLen); - if ( ( err != ERROR_BUFFER_OVERFLOW ) || ( i++ == 100 ) ) + if ( err != ERROR_BUFFER_OVERFLOW ) { break; } + + pAdapterInfo = (IP_ADAPTER_INFO*) realloc( pAdapterInfo, bufLen ); + require_action( pAdapterInfo, exit, err = kNoMemoryErr ); } require_noerr( err, exit ); - if ( fixedInfo->DomainName ) - { - domainname dname; + index = GetPrimaryInterface(); - if ( MakeDomainNameFromDNSNameString( &dname, fixedInfo->DomainName ) || !dname.c[0] ) + for ( pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next ) + { + if ( pAdapter->IpAddressList.IpAddress.String && + pAdapter->IpAddressList.IpAddress.String[0] && + pAdapter->GatewayList.IpAddress.String && + pAdapter->GatewayList.IpAddress.String[0] && + ( !index || ( pAdapter->Index == index ) ) ) { - head = (DNameListElem*) malloc( sizeof( DNameListElem ) ); - require_action( head, exit, err = mStatus_NoMemoryErr ); + // Found one that will work - AssignDomainName( &head->name, &dname ); - head->next = NULL; - } - else - { - dlog( kDebugLevelError, "bad DDNS host name from domain name: %s", fixedInfo->DomainName ); + char keyName[1024]; + + _snprintf( keyName, 1024, "%s%s", "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\", pAdapter->AdapterName ); + + err = RegCreateKeyA( HKEY_LOCAL_MACHINE, keyName, &key ); + require_noerr( err, exit ); + + err = RegQueryString( key, "Domain", &domain, &dwSize, NULL ); + check_noerr( err ); + + if ( !domain || !domain[0] ) + { + if ( domain ) + { + free( domain ); + domain = NULL; + } + + err = RegQueryString( key, "DhcpDomain", &domain, &dwSize, NULL ); + check_noerr( err ); + } + + if ( domain && domain[0] && ( MakeDomainNameFromDNSNameString( &dname, domain ) || !dname.c[0] ) ) + { + head = (DNameListElem*) malloc( sizeof( DNameListElem ) ); + require_action( head, exit, err = mStatus_NoMemoryErr ); + + AssignDomainName( &head->name, &dname ); + head->next = NULL; + } + + break; } } exit: - if ( fixedInfo ) + if ( pAdapterInfo ) { - GlobalFree( fixedInfo ); + free( pAdapterInfo ); + } + + if ( domain ) + { + free( domain ); + } + + if ( key ) + { + RegCloseKey( key ); } return head; @@ -1830,41 +2055,36 @@ dDNSPlatformRegisterSplitDNS( mDNS * m ) mStatus dDNSPlatformGetPrimaryInterface( mDNS * m, mDNSAddr * primary, mDNSAddr * router ) { - IP_ADAPTER_INFO * pAdapterInfo = NULL; + IP_ADAPTER_INFO * pAdapterInfo; IP_ADAPTER_INFO * pAdapter; - DWORD bufLen = sizeof( IP_ADAPTER_INFO ); + DWORD bufLen; int i; BOOL found; + DWORD index; mStatus err = mStatus_NoError; DEBUG_UNUSED( m ); - pAdapterInfo = NULL; - found = FALSE; + pAdapterInfo = NULL; + bufLen = 0; + found = FALSE; for ( i = 0; i < 100; i++ ) { - if ( pAdapterInfo ) - { - free( pAdapterInfo ); - pAdapterInfo = NULL; - } - - pAdapterInfo = (IP_ADAPTER_INFO*) malloc( bufLen ); - require_action( pAdapterInfo, exit, err = kNoMemoryErr ); - err = GetAdaptersInfo( pAdapterInfo, &bufLen); if ( err != ERROR_BUFFER_OVERFLOW ) { break; } + + pAdapterInfo = (IP_ADAPTER_INFO*) realloc( pAdapterInfo, bufLen ); + require_action( pAdapterInfo, exit, err = kNoMemoryErr ); } - // Windows doesn't really have a concept of a primary adapter, - // so we're just going to iterate through all the adapters and - // pick the first one that has an IP address assigned and - // a gateway assigned + require_noerr( err, exit ); + + index = GetPrimaryInterface(); for ( pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next ) { @@ -1873,7 +2093,8 @@ dDNSPlatformGetPrimaryInterface( mDNS * m, mDNSAddr * primary, mDNSAddr * router pAdapter->GatewayList.IpAddress.String && pAdapter->GatewayList.IpAddress.String[0] && ( StringToAddress( primary, pAdapter->IpAddressList.IpAddress.String ) == mStatus_NoError ) && - ( StringToAddress( router, pAdapter->GatewayList.IpAddress.String ) == mStatus_NoError ) ) + ( StringToAddress( router, pAdapter->GatewayList.IpAddress.String ) == mStatus_NoError ) && + ( !index || ( pAdapter->Index == index ) ) ) { // Found one that will work @@ -1882,16 +2103,6 @@ dDNSPlatformGetPrimaryInterface( mDNS * m, mDNSAddr * primary, mDNSAddr * router } } - if ( !found ) - { - // If we couldn't find one, then let's try the first one in the list - - err = StringToAddress( primary, pAdapter->IpAddressList.IpAddress.String ); - require_noerr( err, exit ); - - found = TRUE; - } - exit: if ( pAdapterInfo ) @@ -2119,8 +2330,12 @@ mDNSlocal mStatus SetupNiceName( mDNS * const inMDNS ) // if we can't find it in the registry, then use the hostname of the machine if ( err || ( utf8[ 0 ] == '\0' ) ) { - err = gethostname( tempString, sizeof( tempString ) - 1 ); - check_translated_errno( err == 0, errno_compat(), kNameErr ); + DWORD tempStringLen = sizeof( tempString ); + BOOL ok; + + ok = GetComputerNameExA( ComputerNamePhysicalDnsHostname, tempString, &tempStringLen ); + err = translate_errno( ok, (mStatus) GetLastError(), kNameErr ); + check_noerr( err ); if( !err ) { @@ -2159,7 +2374,9 @@ mDNSlocal mStatus SetupHostName( mDNS * const inMDNS ) { mStatus err = 0; char tempString[ 256 ]; + DWORD tempStringLen; domainlabel tempLabel; + BOOL ok; check( inMDNS ); @@ -2167,8 +2384,10 @@ mDNSlocal mStatus SetupHostName( mDNS * const inMDNS ) tempString[ 0 ] = '\0'; // use the hostname of the machine - err = gethostname( tempString, sizeof( tempString ) - 1 ); - check_translated_errno( err == 0, errno_compat(), kNameErr ); + tempStringLen = sizeof( tempString ); + ok = GetComputerNameExA( ComputerNamePhysicalDnsHostname, tempString, &tempStringLen ); + err = translate_errno( ok, (mStatus) GetLastError(), kNameErr ); + check_noerr( err ); // if we can't get the hostname if( err || ( tempString[ 0 ] == '\0' ) ) @@ -2232,19 +2451,25 @@ mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS ) mDNSInterfaceData * ifd; struct ifaddrs * addrs; struct ifaddrs * p; - struct ifaddrs * loopback; + struct ifaddrs * loopbackv4; + struct ifaddrs * loopbackv6; u_int flagMask; u_int flagTest; - BOOL foundUnicastSock4DestAddr; - BOOL foundUnicastSock6DestAddr; + mDNSBool foundv4; + mDNSBool foundv6; + mDNSBool foundUnicastSock4DestAddr; + mDNSBool foundUnicastSock6DestAddr; dlog( kDebugLevelTrace, DEBUG_NAME "setting up interface list\n" ); check( inMDNS ); check( inMDNS->p ); - addrs = NULL; - foundUnicastSock4DestAddr = FALSE; - foundUnicastSock6DestAddr = FALSE; + inMDNS->p->registeredLoopback4 = mDNSfalse; + addrs = NULL; + foundv4 = mDNSfalse; + foundv6 = mDNSfalse; + foundUnicastSock4DestAddr = mDNSfalse; + foundUnicastSock6DestAddr = mDNSfalse; // Tear down any existing interfaces that may be set up. @@ -2266,10 +2491,11 @@ mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS ) err = getifaddrs( &addrs ); require_noerr( err, exit ); - loopback = NULL; + loopbackv4 = NULL; + loopbackv6 = NULL; next = &inMDNS->p->interfaceList; - - flagMask = IFF_UP | IFF_MULTICAST | IFF_POINTTOPOINT; + + flagMask = IFF_UP | IFF_MULTICAST; flagTest = IFF_UP | IFF_MULTICAST; #if( MDNS_WINDOWS_ENABLE_IPV4 ) @@ -2281,9 +2507,9 @@ mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS ) } if( p->ifa_flags & IFF_LOOPBACK ) { - if( !loopback ) + if( !loopbackv4 ) { - loopback = p; + loopbackv4 = p; } continue; } @@ -2292,7 +2518,16 @@ mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS ) err = SetupInterface( inMDNS, p, &ifd ); require_noerr( err, exit ); - + + // If this guy is point-to-point (ifd->interfaceInfo.McastTxRx == 0 ) we still want to + // register him, but we also want to note that we haven't found a v4 interface + // so that we register loopback so same host operations work + + if ( ifd->interfaceInfo.McastTxRx == mDNStrue ) + { + foundv4 = mDNStrue; + } + // If we're on a platform that doesn't have WSARecvMsg(), there's no way // of determing the destination address of a packet that is sent to us. // For multicast packets, that's easy to determine. But for the unicast @@ -2308,8 +2543,6 @@ mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS ) *next = ifd; next = &ifd->next; ++inMDNS->p->interfaceCount; - - } #endif @@ -2324,9 +2557,9 @@ mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS ) } if( p->ifa_flags & IFF_LOOPBACK ) { - if( !loopback ) + if( !loopbackv6 ) { - loopback = p; + loopbackv6 = p; } continue; } @@ -2336,6 +2569,15 @@ mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS ) err = SetupInterface( inMDNS, p, &ifd ); require_noerr( err, exit ); + // If this guy is point-to-point (ifd->interfaceInfo.McastTxRx == 0 ) we still want to + // register him, but we also want to note that we haven't found a v4 interface + // so that we register loopback so same host operations work + + if ( ifd->interfaceInfo.McastTxRx == mDNStrue ) + { + foundv6 = mDNStrue; + } + // If we're on a platform that doesn't have WSARecvMsg(), there's no way // of determing the destination address of a packet that is sent to us. // For multicast packets, that's easy to determine. But for the unicast @@ -2371,19 +2613,22 @@ mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS ) { continue; } - loopback = p; + + v4loopback = p; break; } #endif - if( !inMDNS->p->interfaceList && loopback ) + if ( !foundv4 && loopbackv4 ) { dlog( kDebugLevelVerbose, DEBUG_NAME "Interface %40s (0x%08X) %##a\n", - loopback->ifa_name ? loopback->ifa_name : "", loopback->ifa_extra.index, loopback->ifa_addr ); + loopbackv4->ifa_name ? loopbackv4->ifa_name : "", loopbackv4->ifa_extra.index, loopbackv4->ifa_addr ); - err = SetupInterface( inMDNS, loopback, &ifd ); + err = SetupInterface( inMDNS, loopbackv4, &ifd ); require_noerr( err, exit ); + + inMDNS->p->registeredLoopback4 = mDNStrue; #if( MDNS_WINDOWS_ENABLE_IPV4 ) @@ -2497,7 +2742,6 @@ mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inI ifd->sock = kInvalidSocketRef; ifd->index = inIFA->ifa_extra.index; ifd->scopeID = inIFA->ifa_extra.index; - check( strlen( inIFA->ifa_name ) < sizeof( ifd->name ) ); strncpy( ifd->name, inIFA->ifa_name, sizeof( ifd->name ) - 1 ); ifd->name[ sizeof( ifd->name ) - 1 ] = '\0'; @@ -2512,7 +2756,7 @@ mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inI // but we cut the packet rate in half. At this time, reducing the packet rate is more important than v6-only // devices on a large configured network, so we are willing to make that sacrifice. - ifd->interfaceInfo.McastTxRx = mDNStrue; + ifd->interfaceInfo.McastTxRx = ( ( inIFA->ifa_flags & IFF_MULTICAST ) && !( inIFA->ifa_flags & IFF_POINTTOPOINT ) ) ? mDNStrue : mDNSfalse; ifd->interfaceInfo.InterfaceID = NULL; for( p = inMDNS->p->interfaceList; p; p = p->next ) @@ -2521,7 +2765,6 @@ mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inI { if (!ifd->interfaceInfo.InterfaceID) { - p->scopeID = ifd->scopeID; ifd->interfaceInfo.InterfaceID = (mDNSInterfaceID) p; } @@ -2556,9 +2799,18 @@ mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inI { DWORD size; - err = WSAIoctl( sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &kWSARecvMsgGUID, sizeof( kWSARecvMsgGUID ), - &ifd->wsaRecvMsgFunctionPtr, sizeof( ifd->wsaRecvMsgFunctionPtr ), &size, NULL, NULL ); - if( err != 0 ) + // If we are running inside VPC, then we won't use WSARecvMsg because it will give us bogus information due to + // a bug in VPC itself. + + err = IsVPCRunning(); + + if ( !err ) + { + err = WSAIoctl( sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &kWSARecvMsgGUID, sizeof( kWSARecvMsgGUID ), + &ifd->wsaRecvMsgFunctionPtr, sizeof( ifd->wsaRecvMsgFunctionPtr ), &size, NULL, NULL ); + } + + if ( err ) { ifd->wsaRecvMsgFunctionPtr = NULL; } @@ -2964,7 +3216,7 @@ mDNSlocal mStatus SetupNotifications( mDNS * const inMDNS ) err = translate_errno( inMDNS->p->ddnsChangedEvent, (mStatus) GetLastError(), kUnknownErr ); require_noerr( err, exit ); - err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\") kServiceName TEXT("\\Parameters\\DynDNS\\Setup"), &inMDNS->p->ddnsKey ); + err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode TEXT("\\DynDNS\\Setup"), &inMDNS->p->ddnsKey ); require_noerr( err, exit ); err = RegNotifyChangeKeyValue(inMDNS->p->ddnsKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, inMDNS->p->ddnsChangedEvent, TRUE); @@ -3116,7 +3368,7 @@ mDNSlocal unsigned WINAPI ProcessingThread( LPVOID inParam ) m = (mDNS *) inParam; err = ProcessingThreadInitialize( m ); - require_noerr( err, exit ); + check( !err ); done = 0; while( !done ) @@ -3154,131 +3406,143 @@ mDNSlocal unsigned WINAPI ProcessingThread( LPVOID inParam ) // Wait until something occurs (e.g. cancel, incoming packet, or timeout). result = WaitForMultipleObjects( (DWORD) waitListCount, waitList, FALSE, (DWORD) interval ); - if( result == WAIT_TIMEOUT ) - { - // Next task timeout occurred. Loop back up to give mDNS core a chance to work. - - dlog( kDebugLevelChatty - 1, DEBUG_NAME "timeout\n" ); - continue; - } - else if( result == kWaitListCancelEvent ) - { - // Cancel event. Set the done flag and break to exit. - - dlog( kDebugLevelVerbose, DEBUG_NAME "canceling...\n" ); - done = 1; - break; - } - else if( result == kWaitListInterfaceListChangedEvent ) - { - // Interface list changed event. Break out of the inner loop to re-setup the wait list. - - ProcessingThreadInterfaceListChanged( m ); - break; - } - else if( result == kWaitListWakeupEvent ) - { - // Wakeup event due to an mDNS API call. Loop back to call mDNS_Execute. - - dlog( kDebugLevelChatty - 1, DEBUG_NAME "wakeup for mDNS_Execute\n" ); - continue; - } - else if ( result == kWaitListComputerDescriptionEvent ) - { - // - // The computer description might have changed - // - ProcessingThreadComputerDescriptionChanged( m ); - break; - } - else if ( result == kWaitListTCPIPEvent ) - { - // - // The TCP/IP might have changed - // - ProcessingThreadTCPIPConfigChanged( m ); - break; - } - else if ( result == kWaitListDynDNSEvent ) - { - // - // The DynDNS config might have changed - // - ProcessingThreadDynDNSConfigChanged( m ); - break; - } - else + check( result != WAIT_FAILED ); + + if ( result != WAIT_FAILED ) { - int waitItemIndex; - - // Socket data available event. Determine which socket and process the packet. - - waitItemIndex = (int)( ( (int) result ) - WAIT_OBJECT_0 ); - dlog( kDebugLevelChatty, DEBUG_NAME "socket data available on socket index %d\n", waitItemIndex ); - check( ( waitItemIndex >= 0 ) && ( waitItemIndex < waitListCount ) ); - if( ( waitItemIndex >= 0 ) && ( waitItemIndex < waitListCount ) ) + if( result == WAIT_TIMEOUT ) { - HANDLE signaledObject; - int n = 0; - mDNSInterfaceData * ifd; - mDNSTCPConnectionData * tcd; + // Next task timeout occurred. Loop back up to give mDNS core a chance to work. - signaledObject = waitList[ waitItemIndex ]; - -#if ( MDNS_WINDOWS_ENABLE_IPV4 ) - if ( m->p->unicastSock4ReadEvent == signaledObject ) + dlog( kDebugLevelChatty - 1, DEBUG_NAME "timeout\n" ); + continue; + } + else if( result == kWaitListCancelEvent ) + { + // Cancel event. Set the done flag and break to exit. + + dlog( kDebugLevelVerbose, DEBUG_NAME "canceling...\n" ); + done = 1; + break; + } + else if( result == kWaitListInterfaceListChangedEvent ) + { + // Interface list changed event. Break out of the inner loop to re-setup the wait list. + + ProcessingThreadInterfaceListChanged( m ); + break; + } + else if( result == kWaitListWakeupEvent ) + { + // Wakeup event due to an mDNS API call. Loop back to call mDNS_Execute. + + dlog( kDebugLevelChatty - 1, DEBUG_NAME "wakeup for mDNS_Execute\n" ); + continue; + } + else if ( result == kWaitListComputerDescriptionEvent ) + { + // + // The computer description might have changed + // + ProcessingThreadComputerDescriptionChanged( m ); + break; + } + else if ( result == kWaitListTCPIPEvent ) + { + // + // The TCP/IP might have changed + // + ProcessingThreadTCPIPConfigChanged( m ); + break; + } + else if ( result == kWaitListDynDNSEvent ) + { + // + // The DynDNS config might have changed + // + ProcessingThreadDynDNSConfigChanged( m ); + break; + } + else + { + int waitItemIndex; + + // Socket data available event. Determine which socket and process the packet. + + waitItemIndex = (int)( ( (int) result ) - WAIT_OBJECT_0 ); + dlog( kDebugLevelChatty, DEBUG_NAME "socket data available on socket index %d\n", waitItemIndex ); + check( ( waitItemIndex >= 0 ) && ( waitItemIndex < waitListCount ) ); + if( ( waitItemIndex >= 0 ) && ( waitItemIndex < waitListCount ) ) { - ProcessingThreadProcessPacket( m, NULL, m->p->unicastSock4 ); - ++n; - } + HANDLE signaledObject; + int n = 0; + mDNSInterfaceData * ifd; + mDNSTCPConnectionData * tcd; + + signaledObject = waitList[ waitItemIndex ]; + +#if ( MDNS_WINDOWS_ENABLE_IPV4 ) + if ( m->p->unicastSock4ReadEvent == signaledObject ) + { + ProcessingThreadProcessPacket( m, NULL, m->p->unicastSock4 ); + ++n; + } #endif - + #if ( MDNS_WINDOWS_ENABLE_IPV6 ) - if ( m->p->unicastSock6ReadEvent == signaledObject ) - { - ProcessingThreadProcessPacket( m, NULL, m->p->unicastSock6 ); - ++n; - } + if ( m->p->unicastSock6ReadEvent == signaledObject ) + { + ProcessingThreadProcessPacket( m, NULL, m->p->unicastSock6 ); + ++n; + } #endif - for( ifd = m->p->interfaceList; ifd; ifd = ifd->next ) - { - if( ifd->readPendingEvent == signaledObject ) + for( ifd = m->p->interfaceList; ifd; ifd = ifd->next ) { - ProcessingThreadProcessPacket( m, ifd, ifd->sock ); - ++n; + if( ifd->readPendingEvent == signaledObject ) + { + ProcessingThreadProcessPacket( m, ifd, ifd->sock ); + ++n; + } } - } - - for ( tcd = gTCPConnectionList; tcd; tcd = tcd->next ) - { - if ( tcd->pendingEvent == signaledObject ) + + for ( tcd = gTCPConnectionList; tcd; tcd = tcd->next ) { - mDNSBool connect = FALSE; - - if ( !tcd->connected ) + if ( tcd->pendingEvent == signaledObject ) { - tcd->connected = mDNStrue; - connect = mDNStrue; + mDNSBool connect = FALSE; + + if ( !tcd->connected ) + { + tcd->connected = mDNStrue; + connect = mDNStrue; + } + + tcd->callback( ( int ) tcd->sock, tcd->context, connect ); + + ++n; + + break; } - - tcd->callback( ( int ) tcd->sock, tcd->context, connect ); - - ++n; - - break; } + + check( n > 0 ); + } + else + { + // Unexpected wait result. + + dlog( kDebugLevelWarning, DEBUG_NAME "%s: unexpected wait result (result=0x%08X)\n", __ROUTINE__, result ); } - - check( n > 0 ); - } - else - { - // Unexpected wait result. - - dlog( kDebugLevelWarning, DEBUG_NAME "%s: unexpected wait result (result=0x%08X)\n", __ROUTINE__, result ); } } + else + { + Sleep( 3 * 1000 ); + err = ProcessingThreadInitialize( m ); + check( err ); + break; + } } // Release the wait list. @@ -3496,7 +3760,7 @@ mDNSlocal void ProcessingThreadProcessPacket( mDNS *inMDNS, mDNSInterfaceData *i if ( inIFD ) { - require_action( ipv4PacketInfo->ipi_ifindex == ( inIFD->index >> 8 ), exit, err = kMismatchErr ); + require_action( ipv4PacketInfo->ipi_ifindex == inIFD->index, exit, err = kMismatchErr ); } dstAddr.type = mDNSAddrType_IPv4; @@ -3510,7 +3774,7 @@ mDNSlocal void ProcessingThreadProcessPacket( mDNS *inMDNS, mDNSInterfaceData *i if ( inIFD ) { - require_action( ipv6PacketInfo->ipi6_ifindex == inIFD->index, exit, err = kMismatchErr ); + require_action( ipv6PacketInfo->ipi6_ifindex == ( inIFD->index - kIPv6IfIndexBase), exit, err = kMismatchErr ); } dstAddr.type = mDNSAddrType_IPv6; @@ -3577,6 +3841,13 @@ mDNSlocal void ProcessingThreadInterfaceListChanged( mDNS *inMDNS ) err = SetupInterfaceList( inMDNS ); check_noerr( err ); + err = dDNS_Setup( inMDNS ); + check_noerr( err ); + + // so that LLQs are restarted against the up to date name servers + + mDNS_UpdateLLQs( inMDNS ); + mDNSPlatformUnlock( inMDNS ); // Inform clients of the change. @@ -3638,6 +3909,10 @@ mDNSlocal void ProcessingThreadTCPIPConfigChanged( mDNS * inMDNS ) err = dDNS_Setup( inMDNS ); check_noerr( err ); + // so that LLQs are restarted against the up to date name servers + + mDNS_UpdateLLQs( inMDNS ); + // and reset the event handler if ( ( inMDNS->p->tcpipKey != NULL ) && ( inMDNS->p->tcpipChangedEvent ) ) @@ -3665,6 +3940,10 @@ mDNSlocal void ProcessingThreadDynDNSConfigChanged( mDNS *inMDNS ) err = dDNS_Setup( inMDNS ); check_noerr( err ); + // so that LLQs are restarted against the up to date name servers + + mDNS_UpdateLLQs( inMDNS ); + // and reset the event handler if ((inMDNS->p->ddnsKey != NULL) && (inMDNS->p->ddnsChangedEvent)) @@ -3714,13 +3993,9 @@ int getifaddrs( struct ifaddrs **outAddrs ) } // Use the new IPv6-capable routine if supported. Otherwise, fall back to the old and compatible IPv4-only code. + // Fall back to using getifaddrs_ipv4 if getifaddrs_ipv6 fails - if( gGetAdaptersAddressesFunctionPtr ) - { - err = getifaddrs_ipv6( outAddrs ); - require_noerr( err, exit ); - } - else + if( !gGetAdaptersAddressesFunctionPtr || ( ( err = getifaddrs_ipv6( outAddrs ) ) != mStatus_NoError ) ) { err = getifaddrs_ipv4( outAddrs ); require_noerr( err, exit ); @@ -3770,7 +4045,7 @@ mDNSlocal int getifaddrs_ipv6( struct ifaddrs **outAddrs ) // This loops to handle the case where the interface changes in the window after getting the size, but before the // second call completes. A limit of 100 retries is enforced to prevent infinite loops if something else is wrong. - flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME; + flags = GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME; i = 0; for( ;; ) { @@ -3868,16 +4143,27 @@ mDNSlocal int getifaddrs_ipv6( struct ifaddrs **outAddrs ) ifa->ifa_flags = 0; if( iaa->OperStatus == IfOperStatusUp ) ifa->ifa_flags |= IFF_UP; if( iaa->IfType == IF_TYPE_SOFTWARE_LOOPBACK ) ifa->ifa_flags |= IFF_LOOPBACK; + else if ( IsPointToPoint( addr ) ) ifa->ifa_flags |= IFF_POINTTOPOINT; if( !( iaa->Flags & IP_ADAPTER_NO_MULTICAST ) ) ifa->ifa_flags |= IFF_MULTICAST; + - // Get the interface index. Windows does not have a uniform scheme for IPv4 and IPv6 interface indexes - // so the following is a hack to put IPv4 interface indexes in the upper 24-bits and IPv6 interface indexes - // in the lower 8-bits. This allows the IPv6 interface index to be usable as an IPv6 scope ID directly. + // Interface index being returned is 512 + // + // Windows does not have a uniform scheme for IPv4 and IPv6 interface indexes. + // This code used to shift the IPv4 index up to ensure uniqueness between + // it and IPv6 indexes. Although this worked, it was somewhat confusing to developers, who + // then see interface indexes passed back that don't correspond to anything + // that is seen in Win32 APIs or command line tools like "route". As a relatively + // small percentage of developers are actively using IPv6, it seems to + // make sense to make our use of IPv4 as confusion free as possible. + // So now, IPv6 interface indexes will be shifted up by a + // constant value which will serve to uniquely identify them, and we will + // leave IPv4 interface indexes unmodified. switch( family ) { - case AF_INET: ifa->ifa_extra.index = iaa->IfIndex << 8; break; - case AF_INET6: ifa->ifa_extra.index = ipv6IfIndex; break; + case AF_INET: ifa->ifa_extra.index = iaa->IfIndex; break; + case AF_INET6: ifa->ifa_extra.index = ipv6IfIndex + kIPv6IfIndexBase; break; default: break; } @@ -3913,7 +4199,7 @@ mDNSlocal int getifaddrs_ipv6( struct ifaddrs **outAddrs ) { case AF_INET: { - struct sockaddr_in * sa4; + struct sockaddr_in * sa4; require_action( prefixLength <= 32, exit, err = ERROR_INVALID_DATA ); @@ -3921,12 +4207,21 @@ mDNSlocal int getifaddrs_ipv6( struct ifaddrs **outAddrs ) require_action( sa4, exit, err = WSAENOBUFS ); sa4->sin_family = AF_INET; - if( prefixLength == 0 ) + + if ( prefixLength != 0 ) { - dlog( kDebugLevelWarning, DEBUG_NAME "%s: IPv4 netmask 0, defaulting to 255.255.255.255\n", __ROUTINE__ ); - prefixLength = 32; + sa4->sin_addr.s_addr = htonl( 0xFFFFFFFFU << ( 32 - prefixLength ) ); } - sa4->sin_addr.s_addr = htonl( 0xFFFFFFFFU << ( 32 - prefixLength ) ); + else + { + uint32_t index; + + dlog( kDebugLevelWarning, DEBUG_NAME "%s: IPv4 prefixLength is 0\n", __ROUTINE__ ); + err = AddressToIndexAndMask( ifa->ifa_addr, &index, (struct sockaddr*) sa4 ); + require_noerr( err, exit ); + } + + dlog( kDebugLevelInfo, DEBUG_NAME "%s: IPv4 mask = %s\n", __ROUTINE__, inet_ntoa( sa4->sin_addr ) ); ifa->ifa_netmask = (struct sockaddr *) sa4; break; } @@ -4073,32 +4368,31 @@ mDNSlocal int getifaddrs_ipv4( struct ifaddrs **outAddrs ) // Get addresses. - switch( ifInfo->iiAddress.Address.sa_family ) + if ( ifInfo->iiAddress.Address.sa_family == AF_INET ) { - case AF_INET: - { - struct sockaddr_in * sa4; - - sa4 = &ifInfo->iiAddress.AddressIn; - ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sa4 ) ); - require_action( ifa->ifa_addr, exit, err = WSAENOBUFS ); - memcpy( ifa->ifa_addr, sa4, sizeof( *sa4 ) ); + struct sockaddr_in * sa4; + + sa4 = &ifInfo->iiAddress.AddressIn; + ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sa4 ) ); + require_action( ifa->ifa_addr, exit, err = WSAENOBUFS ); + memcpy( ifa->ifa_addr, sa4, sizeof( *sa4 ) ); - sa4 = &ifInfo->iiNetmask.AddressIn; - ifa->ifa_netmask = (struct sockaddr*) calloc(1, sizeof( *sa4 ) ); - require_action( ifa->ifa_netmask, exit, err = WSAENOBUFS ); - memcpy( ifa->ifa_netmask, sa4, sizeof( *sa4 ) ); + ifa->ifa_netmask = (struct sockaddr*) calloc(1, sizeof( *sa4 ) ); - break; - } - - default: - break; + // Service won't start on Win2K. The address + // family field was not being initialized. + + ifa->ifa_netmask->sa_family = AF_INET; + require_action( ifa->ifa_netmask, exit, err = WSAENOBUFS ); + err = AddressToIndexAndMask( ifa->ifa_addr, &ifa->ifa_extra.index, ifa->ifa_netmask ); + require_noerr( err, exit ); } + else + { + // Emulate an interface index. - // Emulate an interface index. - - ifa->ifa_extra.index = (uint32_t)( i + 1 ); + ifa->ifa_extra.index = (uint32_t)( i + 1 ); + } } // Success! @@ -4297,6 +4591,127 @@ void freeifaddrs( struct ifaddrs *inIFAs ) } } + +//=========================================================================================================================== +// GetPrimaryInterface +//=========================================================================================================================== + +mDNSlocal DWORD +GetPrimaryInterface() +{ + PMIB_IPFORWARDTABLE pIpForwardTable = NULL; + DWORD dwSize = 0; + BOOL bOrder = FALSE; + OSStatus err; + DWORD index = 0; + DWORD metric = 0; + unsigned long int i; + + // Find out how big our buffer needs to be. + + err = GetIpForwardTable(NULL, &dwSize, bOrder); + require_action( err == ERROR_INSUFFICIENT_BUFFER, exit, err = kUnknownErr ); + + // Allocate the memory for the table + + pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc( dwSize ); + require_action( pIpForwardTable, exit, err = kNoMemoryErr ); + + // Now get the table. + + err = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder); + require_noerr( err, exit ); + + + // Search for the row in the table we want. + + for ( i = 0; i < pIpForwardTable->dwNumEntries; i++) + { + // Look for a default route + + if ( pIpForwardTable->table[i].dwForwardDest == 0 ) + { + if ( index && ( pIpForwardTable->table[i].dwForwardMetric1 >= metric ) ) + { + continue; + } + + index = pIpForwardTable->table[i].dwForwardIfIndex; + metric = pIpForwardTable->table[i].dwForwardMetric1; + } + } + +exit: + + if ( pIpForwardTable != NULL ) + { + free( pIpForwardTable ); + } + + return index; +} + + +//=========================================================================================================================== +// AddressToIndexAndMask +//=========================================================================================================================== + +mDNSlocal mStatus +AddressToIndexAndMask( struct sockaddr * addr, uint32_t * ifIndex, struct sockaddr * mask ) +{ + // Before calling AddIPAddress we use GetIpAddrTable to get + // an adapter to which we can add the IP. + + PMIB_IPADDRTABLE pIPAddrTable = NULL; + DWORD dwSize = 0; + mStatus err = mStatus_UnknownErr; + DWORD i; + + // For now, this is only for IPv4 addresses. That is why we can safely cast + // addr's to sockaddr_in. + + require_action( addr->sa_family == AF_INET, exit, err = mStatus_UnknownErr ); + + // Make an initial call to GetIpAddrTable to get the + // necessary size into the dwSize variable + + for ( i = 0; i < 100; i++ ) + { + err = GetIpAddrTable( pIPAddrTable, &dwSize, 0 ); + + if ( err != ERROR_INSUFFICIENT_BUFFER ) + { + break; + } + + pIPAddrTable = (MIB_IPADDRTABLE *) realloc( pIPAddrTable, dwSize ); + require_action( pIPAddrTable, exit, err = WSAENOBUFS ); + } + + require_noerr( err, exit ); + + for ( i = 0; i < pIPAddrTable->dwNumEntries; i++ ) + { + if ( ( ( struct sockaddr_in* ) addr )->sin_addr.s_addr == pIPAddrTable->table[i].dwAddr ) + { + *ifIndex = pIPAddrTable->table[i].dwIndex; + ( ( struct sockaddr_in*) mask )->sin_addr.s_addr = pIPAddrTable->table[i].dwMask; + err = mStatus_NoError; + break; + } + } + +exit: + + if ( pIPAddrTable ) + { + free( pIPAddrTable ); + } + + return err; +} + + //=========================================================================================================================== // CanReceiveUnicast //=========================================================================================================================== @@ -4327,6 +4742,49 @@ mDNSlocal mDNSBool CanReceiveUnicast( void ) return( ok ); } + +//=========================================================================================================================== +// IsPointToPoint +//=========================================================================================================================== + +mDNSlocal mDNSBool IsPointToPoint( IP_ADAPTER_UNICAST_ADDRESS * addr ) +{ + struct ifaddrs * addrs = NULL; + struct ifaddrs * p = NULL; + OSStatus err; + mDNSBool ret = mDNSfalse; + + // For now, only works for IPv4 interfaces + + if ( addr->Address.lpSockaddr->sa_family == AF_INET ) + { + // The getifaddrs_ipv4 call will give us correct information regarding IFF_POINTTOPOINT flags. + + err = getifaddrs_ipv4( &addrs ); + require_noerr( err, exit ); + + for ( p = addrs; p; p = p->ifa_next ) + { + if ( ( addr->Address.lpSockaddr->sa_family == p->ifa_addr->sa_family ) && + ( ( ( struct sockaddr_in* ) addr->Address.lpSockaddr )->sin_addr.s_addr == ( ( struct sockaddr_in* ) p->ifa_addr )->sin_addr.s_addr ) ) + { + ret = ( p->ifa_flags & IFF_POINTTOPOINT ) ? mDNStrue : mDNSfalse; + break; + } + } + } + +exit: + + if ( addrs ) + { + freeifaddrs( addrs ); + } + + return ret; +} + + //=========================================================================================================================== // GetWindowsVersionString //=========================================================================================================================== @@ -4504,6 +4962,7 @@ static mStatus StringToAddress( mDNSAddr * ip, LPSTR string ) dwSize = sizeof( sa4 ); err = WSAStringToAddressA( string, AF_INET, NULL, (struct sockaddr*) &sa4, &dwSize ); + err = translate_errno( err == 0, WSAGetLastError(), kUnknownErr ); require_noerr( err, exit ); err = dDNS_SetupAddr( ip, (struct sockaddr*) &sa4 ); @@ -4606,7 +5065,7 @@ exit: //=========================================================================================================================== mDNSlocal OSStatus -ConvertUTF8ToLsaString( const char * input, PLSA_UNICODE_STRING output ) +MakeLsaStringFromUTF8String( PLSA_UNICODE_STRING output, const char * input ) { int size; OSStatus err; @@ -4650,41 +5109,36 @@ exit: //=========================================================================================================================== static OSStatus -ConvertLsaStringToUTF8( PLSA_UNICODE_STRING input, char ** output ) +MakeUTF8StringFromLsaString( char * output, size_t len, PLSA_UNICODE_STRING input ) { - int size; + size_t size; OSStatus err = kNoErr; // The Length field of this structure holds the number of bytes, // but WideCharToMultiByte expects the number of wchar_t's. So // we divide by sizeof(wchar_t) to get the correct number. - size = WideCharToMultiByte(CP_UTF8, 0, input->Buffer, ( input->Length / sizeof( wchar_t ) ), NULL, 0, NULL, NULL); + size = (size_t) WideCharToMultiByte(CP_UTF8, 0, input->Buffer, ( input->Length / sizeof( wchar_t ) ), NULL, 0, NULL, NULL); err = translate_errno( size != 0, GetLastError(), kUnknownErr ); require_noerr( err, exit ); - // Add one for trailing '\0' + // Ensure that we have enough space (Add one for trailing '\0') + + require_action( ( size + 1 ) <= len, exit, err = mStatus_NoMemoryErr ); - *output = (char*) malloc( size + 1 ); - require_action( *output, exit, err = mStatus_NoMemoryErr ); + // Convert the string - size = WideCharToMultiByte(CP_UTF8, 0, input->Buffer, ( input->Length / sizeof( wchar_t ) ), *output, size, NULL, NULL); + size = (size_t) WideCharToMultiByte( CP_UTF8, 0, input->Buffer, ( input->Length / sizeof( wchar_t ) ), output, (int) size, NULL, NULL); err = translate_errno( size != 0, GetLastError(), kUnknownErr ); require_noerr( err, exit ); // have to add the trailing 0 because WideCharToMultiByte doesn't do it, // although it does return the correct size - (*output)[size] = '\0'; + output[size] = '\0'; exit: - if ( err && *output ) - { - free( *output ); - *output = NULL; - } - return err; } diff --git a/mDNSWindows/mDNSWin32.h b/mDNSWindows/mDNSWin32.h index f5c9c26..1a9755d 100755 --- a/mDNSWindows/mDNSWin32.h +++ b/mDNSWindows/mDNSWin32.h @@ -23,6 +23,9 @@ Change History (most recent first): $Log: mDNSWin32.h,v $ +Revision 1.23 2005/10/05 20:55:14 herscher + Don't call SetLLRoute on loopback interface + Revision 1.22 2005/03/04 22:44:53 shersche mDNSResponder did not notice changes to DNS server config @@ -199,6 +202,7 @@ struct mDNS_PlatformSupport_struct HKEY tcpipKey; HKEY ddnsKey; mStatus initStatus; + mDNSBool registeredLoopback4; SocketRef interfaceListChangedSocket; int interfaceCount; mDNSInterfaceData * interfaceList; diff --git a/mDNSWindows/mdnsNSP/mdnsNSP.c b/mDNSWindows/mdnsNSP/mdnsNSP.c index c690459..f0937f5 100644 --- a/mDNSWindows/mdnsNSP/mdnsNSP.c +++ b/mDNSWindows/mdnsNSP/mdnsNSP.c @@ -23,6 +23,30 @@ Change History (most recent first): $Log: mdnsNSP.c,v $ +Revision 1.18 2005/10/17 05:45:36 herscher +Fix typo in previous checkin + +Revision 1.17 2005/10/17 05:30:00 herscher + NSP should handle IPv6 AAAA queries for dot-local names + +Revision 1.16 2005/09/16 22:22:48 herscher + No longer set the PATH variable when NSP is loaded. + +Revision 1.15 2005/07/14 22:12:00 shersche + Delay load dnssd.dll so that it gracefully handles library loading problems immediately after installing Bonjour + +Revision 1.14 2005/03/29 20:35:28 shersche + Remove reverse lookup implementation due to NSP framework limitation + +Revision 1.13 2005/03/29 19:42:47 shersche +Do label check before checking etc/hosts file + +Revision 1.12 2005/03/21 00:42:45 shersche + Fix build warnings on Win32 platform + +Revision 1.11 2005/03/16 03:04:51 shersche + Don't issue multicast query multilabel dot-local names + Revision 1.10 2005/02/23 22:16:07 shersche Unregister the NSP before registering to workaround an installer problem during upgrade installs @@ -66,6 +90,7 @@ mDNS NameSpace Provider (NSP). Hooks into the Windows name resolution system to */ + #include #include #include @@ -73,11 +98,22 @@ mDNS NameSpace Provider (NSP). Hooks into the Windows name resolution system to #include "CommonServices.h" #include "DebugServices.h" +#include #include #include +#include + + #include "dns_sd.h" +#pragma comment(lib, "DelayImp.lib") + +#ifdef _MSC_VER +#define swprintf _snwprintf +#define snprintf _snprintf +#endif + #if 0 #pragma mark == Structures == #endif @@ -95,15 +131,21 @@ struct Query DWORD querySetFlags; WSAQUERYSETW * querySet; size_t querySetSize; - HANDLE dataEvent; + HANDLE data4Event; + HANDLE data6Event; HANDLE cancelEvent; - HANDLE waitHandles[ 2 ]; + HANDLE waitHandles[ 3 ]; DWORD waitCount; - DNSServiceRef resolver; + DNSServiceRef resolver4; + DNSServiceRef resolver6; char name[ kDNSServiceMaxDomainName ]; size_t nameSize; - uint32_t addr; - bool addrValid; + uint8_t numValidAddrs; + uint32_t addr4; + bool addr4Valid; + uint8_t addr6[16]; + u_long addr6ScopeId; + bool addr6Valid; }; #define BUFFER_INITIAL_SIZE 4192 @@ -182,7 +224,21 @@ DEBUG_LOCAL OSStatus QueryRetain( QueryRef inRef ); DEBUG_LOCAL OSStatus QueryRelease( QueryRef inRef ); DEBUG_LOCAL void CALLBACK_COMPAT - QueryRecordCallback( + QueryRecordCallback4( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL, + void * inContext ); + +DEBUG_LOCAL void CALLBACK_COMPAT + QueryRecordCallback6( DNSServiceRef inRef, DNSServiceFlags inFlags, uint32_t inInterfaceIndex, @@ -227,6 +283,12 @@ DEBUG_LOCAL OSStatus HostsFileOpen( HostsFile ** self, const char * fname ); DEBUG_LOCAL OSStatus HostsFileClose( HostsFile * self ); DEBUG_LOCAL void HostsFileInfoFree( HostsFileInfo * info ); DEBUG_LOCAL OSStatus HostsFileNext( HostsFile * self, HostsFileInfo ** hInfo ); +DEBUG_LOCAL const char * GetNextLabel( const char *cstr, char label[64] ); +DEBUG_LOCAL DWORD GetScopeId( DWORD ifIndex ); + +#ifdef ENABLE_REVERSE_LOOKUP +DEBUG_LOCAL OSStatus IsReverseLookup( LPCWSTR name, size_t size ); +#endif #if 0 @@ -244,9 +306,20 @@ DEBUG_LOCAL GUID gNSPGUID = { 0xb600e6e9, 0x553b, 0x4a19, { 0x86, 0x96, 0x DEBUG_LOCAL LONG gRefCount = 0; DEBUG_LOCAL CRITICAL_SECTION gLock; DEBUG_LOCAL bool gLockInitialized = false; -DEBUG_LOCAL bool gDNSSDInitialized = false; DEBUG_LOCAL QueryRef gQueryList = NULL; DEBUG_LOCAL HostsFileInfo * gHostsFileInfo = NULL; +typedef DWORD + ( WINAPI * GetAdaptersAddressesFunctionPtr )( + ULONG inFamily, + DWORD inFlags, + PVOID inReserved, + PIP_ADAPTER_ADDRESSES inAdapter, + PULONG outBufferSize ); + +DEBUG_LOCAL HMODULE gIPHelperLibraryInstance = NULL; +DEBUG_LOCAL GetAdaptersAddressesFunctionPtr gGetAdaptersAddressesFunctionPtr = NULL; + + #if 0 #pragma mark - @@ -260,16 +333,17 @@ BOOL APIENTRY DllMain( HINSTANCE inInstance, DWORD inReason, LPVOID inReserved ) { DEBUG_USE_ONLY( inInstance ); DEBUG_UNUSED( inReserved ); - + switch( inReason ) { case DLL_PROCESS_ATTACH: gInstance = inInstance; gHostsFileInfo = NULL; debug_initialize( kDebugOutputTypeWindowsEventLog, "mDNS NSP", inInstance ); - debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelInfo ); + debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelNotice ); dlog( kDebugLevelTrace, "\n" ); dlog( kDebugLevelVerbose, "%s: process attach\n", __ROUTINE__ ); + break; case DLL_PROCESS_DETACH: @@ -290,6 +364,7 @@ BOOL APIENTRY DllMain( HINSTANCE inInstance, DWORD inReason, LPVOID inReserved ) dlog( kDebugLevelNotice, "%s: unknown reason code (%d)\n", __ROUTINE__, inReason ); break; } + return( TRUE ); } @@ -395,6 +470,18 @@ int WSPAPI NSPStartup( LPGUID inProviderID, LPNSP_ROUTINE outRoutines ) outRoutines->NSPRemoveServiceClass = NSPRemoveServiceClass; outRoutines->NSPGetServiceClassInfo = NSPGetServiceClassInfo; + // See if we can get the address for the GetAdaptersAddresses() API. This is only in XP, but we want our + // code to run on older versions of Windows + + if ( !gIPHelperLibraryInstance ) + { + gIPHelperLibraryInstance = LoadLibrary( TEXT( "Iphlpapi" ) ); + if( gIPHelperLibraryInstance ) + { + gGetAdaptersAddressesFunctionPtr = (GetAdaptersAddressesFunctionPtr) GetProcAddress( gIPHelperLibraryInstance, "GetAdaptersAddresses" ); + } + } + err = NO_ERROR; exit: @@ -444,17 +531,20 @@ int WSPAPI NSPCleanup( LPGUID inProviderID ) NSPUnlock(); } - // Shut down DNS-SD and release our resources. - - if( gDNSSDInitialized ) - { - gDNSSDInitialized = false; - } if( gLockInitialized ) { gLockInitialized = false; DeleteCriticalSection( &gLock ); } + + if( gIPHelperLibraryInstance ) + { + BOOL ok; + + ok = FreeLibrary( gIPHelperLibraryInstance ); + check_translated_errno( ok, GetLastError(), kUnknownErr ); + gIPHelperLibraryInstance = NULL; + } exit: dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); @@ -545,80 +635,55 @@ DEBUG_LOCAL int WSPAPI ( ( p[ 4 ] != 'A' ) && ( p[ 4 ] != 'a' ) ) || ( ( p[ 5 ] != 'L' ) && ( p[ 5 ] != 'l' ) ) ) ) { - require_action_quiet( size > sizeof_string( ".0.8.e.f.ip6.arpa" ), exit, err = WSASERVICE_NOT_FOUND ); - - p = name + ( size - 1 ); - p = ( *p == '.' ) ? ( p - sizeof_string( ".0.8.e.f.ip6.arpa" ) ) : ( ( p - sizeof_string( ".0.8.e.f.ip6.arpa" ) ) + 1 ); - - if ( ( ( p[ 0 ] != '.' ) || - ( ( p[ 1 ] != '0' ) ) || - ( ( p[ 2 ] != '.' ) ) || - ( ( p[ 3 ] != '8' ) ) || - ( ( p[ 4 ] != '.' ) ) || - ( ( p[ 5 ] != 'E' ) && ( p[ 5 ] != 'e' ) ) || - ( ( p[ 6 ] != '.' ) ) || - ( ( p[ 7 ] != 'F' ) && ( p[ 7 ] != 'f' ) ) || - ( ( p[ 8 ] != '.' ) ) || - ( ( p[ 9 ] != 'I' ) && ( p[ 9 ] != 'i' ) ) || - ( ( p[ 10 ] != 'P' ) && ( p[ 10 ] != 'p' ) ) || - ( ( p[ 11 ] != '6' ) ) || - ( ( p[ 12 ] != '.' ) ) || - ( ( p[ 13 ] != 'A' ) && ( p[ 13 ] != 'a' ) ) || - ( ( p[ 14 ] != 'R' ) && ( p[ 14 ] != 'r' ) ) || - ( ( p[ 15 ] != 'P' ) && ( p[ 15 ] != 'p' ) ) || - ( ( p[ 16 ] != 'A' ) && ( p[ 16 ] != 'a' ) ) ) ) - { - require_action_quiet( size > sizeof_string( ".254.169.in-addr.arpa" ), exit, err = WSASERVICE_NOT_FOUND ); - - p = name + ( size - 1 ); - p = ( *p == '.' ) ? ( p - sizeof_string( ".254.169.in-addr.arpa" ) ) : ( ( p - sizeof_string( ".254.169.in-addr.arpa" ) ) + 1 ); - - require_action_quiet( ( ( p[ 0 ] == '.' ) && - ( ( p[ 1 ] == '2' ) ) && - ( ( p[ 2 ] == '5' ) ) && - ( ( p[ 3 ] == '4' ) ) && - ( ( p[ 4 ] == '.' ) ) && - ( ( p[ 5 ] == '1' ) ) && - ( ( p[ 6 ] == '6' ) ) && - ( ( p[ 7 ] == '9' ) ) && - ( ( p[ 8 ] == '.' ) ) && - ( ( p[ 9 ] == 'I' ) || ( p[ 9 ] == 'i' ) ) && - ( ( p[ 10 ] == 'N' ) || ( p[ 10 ] == 'n' ) ) && - ( ( p[ 11 ] == '-' ) ) && - ( ( p[ 12 ] == 'A' ) || ( p[ 12 ] == 'a' ) ) && - ( ( p[ 13 ] == 'D' ) || ( p[ 13 ] == 'd' ) ) && - ( ( p[ 14 ] == 'D' ) || ( p[ 14 ] == 'd' ) ) && - ( ( p[ 15 ] == 'R' ) || ( p[ 15 ] == 'r' ) ) && - ( ( p[ 16 ] == '.' ) ) && - ( ( p[ 17 ] == 'A' ) || ( p[ 17 ] == 'a' ) ) && - ( ( p[ 18 ] == 'R' ) || ( p[ 18 ] == 'r' ) ) && - ( ( p[ 19 ] == 'P' ) || ( p[ 19 ] == 'p' ) ) && - ( ( p[ 20 ] == 'A' ) || ( p[ 20 ] == 'a' ) ) ), - exit, err = WSASERVICE_NOT_FOUND ); - } +#ifdef ENABLE_REVERSE_LOOKUP + + err = IsReverseLookup( name, size ); + +#else + + err = WSASERVICE_NOT_FOUND; + +#endif + + require_noerr( err, exit ); } else { + const char * replyDomain; + char translated[ kDNSServiceMaxDomainName ]; + int n; + int labels = 0; + const char * label[128]; + char text[64]; + + n = WideCharToMultiByte( CP_UTF8, 0, name, -1, translated, sizeof( translated ), NULL, NULL ); + require_action( n > 0, exit, err = WSASERVICE_NOT_FOUND ); + + // + + // Don't resolve multi-label name + + replyDomain = translated; + + while ( *replyDomain ) + { + label[labels++] = replyDomain; + replyDomain = GetNextLabel(replyDomain, text); + } + + require_action( labels == 2, exit, err = WSASERVICE_NOT_FOUND ); + // // // Check to see if the name of this host is in the hosts table. If so, // don't try and resolve it - char translated[ kDNSServiceMaxDomainName ]; - int n; - - n = WideCharToMultiByte( CP_UTF8, 0, name, -1, translated, sizeof( translated ), NULL, NULL ); - require_action( n > 0, exit, err = WSASERVICE_NOT_FOUND ); require_action( InHostsTable( translated ) == FALSE, exit, err = WSASERVICE_NOT_FOUND ); } // The name ends in .local ( and isn't in the hosts table ), .0.8.e.f.ip6.arpa, or .254.169.in-addr.arpa so start the resolve operation. Lazy initialize DNS-SD if needed. NSPLock(); - if( !gDNSSDInitialized ) - { - gDNSSDInitialized = true; - } err = QueryCreate( inQuerySet, inFlags, &obj ); NSPUnlock(); @@ -651,6 +716,8 @@ DEBUG_LOCAL int WSPAPI LPDWORD ioSize, LPWSAQUERYSETW outResults ) { + BOOL data4; + BOOL data6; OSStatus err; QueryRef obj; DWORD waitResult; @@ -660,6 +727,8 @@ DEBUG_LOCAL int WSPAPI dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + data4 = FALSE; + data6 = FALSE; obj = NULL; NSPLock(); err = QueryRetain( (QueryRef) inLookup ); @@ -675,12 +744,56 @@ DEBUG_LOCAL int WSPAPI NSPUnlock(); waitResult = WaitForMultipleObjects( obj->waitCount, obj->waitHandles, FALSE, 2 * 1000 ); NSPLock(); - require_action_quiet( waitResult != ( WAIT_OBJECT_0 + 1 ), exit, err = WSA_E_CANCELLED ); - err = translate_errno( waitResult == WAIT_OBJECT_0, (OSStatus) GetLastError(), WSASERVICE_NOT_FOUND ); + require_action_quiet( waitResult != ( WAIT_OBJECT_0 ), exit, err = WSA_E_CANCELLED ); + err = translate_errno( ( waitResult == WAIT_OBJECT_0 + 1 ) || ( waitResult == WAIT_OBJECT_0 + 2 ), (OSStatus) GetLastError(), WSASERVICE_NOT_FOUND ); require_noerr_quiet( err, exit ); - DNSServiceProcessResult(obj->resolver); - require_action_quiet( obj->addrValid, exit, err = WSA_E_NO_MORE ); - + + // If we've received an IPv4 reply, then hang out briefly for an IPv6 reply + + if ( waitResult == WAIT_OBJECT_0 + 1 ) + { + data4 = TRUE; + data6 = WaitForSingleObject( obj->data6Event, 100 ) == WAIT_OBJECT_0 ? TRUE : FALSE; + } + + // Else we've received an IPv6 reply, so hang out briefly for an IPv4 reply + + else if ( waitResult == WAIT_OBJECT_0 + 2 ) + { + data4 = WaitForSingleObject( obj->data4Event, 100 ) == WAIT_OBJECT_0 ? TRUE : FALSE; + data6 = TRUE; + } + + if ( data4 ) + { + __try + { + err = DNSServiceProcessResult(obj->resolver4); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + err = kUnknownErr; + } + + require_noerr( err, exit ); + } + + if ( data6 ) + { + __try + { + err = DNSServiceProcessResult( obj->resolver6 ); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + err = kUnknownErr; + } + + require_noerr( err, exit ); + } + + require_action_quiet( obj->addr4Valid || obj->addr6Valid, exit, err = WSA_E_NO_MORE ); + // Copy the externalized query results to the callers buffer (if it fits). size = QueryCopyQuerySetSize( obj, obj->querySet, obj->querySetFlags ); @@ -688,8 +801,9 @@ DEBUG_LOCAL int WSPAPI QueryCopyQuerySetTo( obj, obj->querySet, obj->querySetFlags, outResults ); outResults->dwOutputFlags = RESULT_IS_ADDED; - obj->addrValid = false; - + obj->addr4Valid = false; + obj->addr6Valid = false; + exit: if( obj ) { @@ -851,7 +965,9 @@ DEBUG_LOCAL OSStatus QueryCreate( const WSAQUERYSETW *inQuerySet, DWORD inQueryS char name[ kDNSServiceMaxDomainName ]; int n; QueryRef * p; - + SOCKET s4; + SOCKET s6; + obj = NULL; check( inQuerySet ); check( inQuerySet->lpszServiceInstanceName ); @@ -873,27 +989,84 @@ DEBUG_LOCAL OSStatus QueryCreate( const WSAQUERYSETW *inQuerySet, DWORD inQueryS for( p = &gQueryList; *p; p = &( *p )->next ) {} // Find the end of the list. *p = obj; - // Set up events to signal when data is ready and when cancelling. - - obj->dataEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); - require_action( obj->dataEvent, exit, err = WSA_NOT_ENOUGH_MEMORY ); - + // Set up cancel event + obj->cancelEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); require_action( obj->cancelEvent, exit, err = WSA_NOT_ENOUGH_MEMORY ); + + // Set up events to signal when A record data is ready - // Start the query. + obj->data4Event = CreateEvent( NULL, TRUE, FALSE, NULL ); + require_action( obj->data4Event, exit, err = WSA_NOT_ENOUGH_MEMORY ); + + // Start the query. Handle delay loaded DLL errors. + + __try + { + err = DNSServiceQueryRecord( &obj->resolver4, 0, 0, name, kDNSServiceType_A, kDNSServiceClass_IN, QueryRecordCallback4, obj ); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + err = kUnknownErr; + } - err = DNSServiceQueryRecord( &obj->resolver, 0, 0, name, kDNSServiceType_A, kDNSServiceClass_IN, - QueryRecordCallback, obj ); require_noerr( err, exit ); // Attach the socket to the event - WSAEventSelect(DNSServiceRefSockFD(obj->resolver), obj->dataEvent, FD_READ|FD_CLOSE); + __try + { + s4 = DNSServiceRefSockFD(obj->resolver4); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + s4 = INVALID_SOCKET; + } + + err = translate_errno( s4 != INVALID_SOCKET, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + WSAEventSelect(s4, obj->data4Event, FD_READ|FD_CLOSE); + + // Set up events to signal when AAAA record data is ready + + obj->data6Event = CreateEvent( NULL, TRUE, FALSE, NULL ); + require_action( obj->data6Event, exit, err = WSA_NOT_ENOUGH_MEMORY ); + // Start the query. Handle delay loaded DLL errors. + + __try + { + err = DNSServiceQueryRecord( &obj->resolver6, 0, 0, name, kDNSServiceType_AAAA, kDNSServiceClass_IN, QueryRecordCallback6, obj ); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + err = kUnknownErr; + } + + require_noerr( err, exit ); + + // Attach the socket to the event + + __try + { + s6 = DNSServiceRefSockFD(obj->resolver6); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + s6 = INVALID_SOCKET; + } + + err = translate_errno( s6 != INVALID_SOCKET, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + WSAEventSelect(s6, obj->data6Event, FD_READ|FD_CLOSE); + obj->waitCount = 0; - obj->waitHandles[ obj->waitCount++ ] = obj->dataEvent; obj->waitHandles[ obj->waitCount++ ] = obj->cancelEvent; + obj->waitHandles[ obj->waitCount++ ] = obj->data4Event; + obj->waitHandles[ obj->waitCount++ ] = obj->data6Event; + check( obj->waitCount == sizeof_array( obj->waitHandles ) ); // Copy the QuerySet so it can be returned later. @@ -977,10 +1150,30 @@ DEBUG_LOCAL OSStatus QueryRelease( QueryRef inRef ) // Stop the query. - if( inRef->resolver ) + if( inRef->resolver4 ) + { + __try + { + DNSServiceRefDeallocate( inRef->resolver4 ); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + } + + inRef->resolver4 = NULL; + } + + if ( inRef->resolver6 ) { - DNSServiceRefDeallocate( inRef->resolver ); - inRef->resolver = NULL; + __try + { + DNSServiceRefDeallocate( inRef->resolver6 ); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + } + + inRef->resolver6 = NULL; } // Decrement the refCount. Fully release if it drops to 0. If still referenced, just exit. @@ -999,9 +1192,14 @@ DEBUG_LOCAL OSStatus QueryRelease( QueryRef inRef ) ok = CloseHandle( inRef->cancelEvent ); check_translated_errno( ok, GetLastError(), WSAEINVAL ); } - if( inRef->dataEvent ) + if( inRef->data4Event ) + { + ok = CloseHandle( inRef->data4Event ); + check_translated_errno( ok, GetLastError(), WSAEINVAL ); + } + if( inRef->data6Event ) { - ok = CloseHandle( inRef->dataEvent ); + ok = CloseHandle( inRef->data6Event ); check_translated_errno( ok, GetLastError(), WSAEINVAL ); } if( inRef->querySet ) @@ -1016,11 +1214,11 @@ exit: } //=========================================================================================================================== -// QueryRecordCallback +// QueryRecordCallback4 //=========================================================================================================================== DEBUG_LOCAL void CALLBACK_COMPAT - QueryRecordCallback( + QueryRecordCallback4( DNSServiceRef inRef, DNSServiceFlags inFlags, uint32_t inInterfaceIndex, @@ -1071,19 +1269,27 @@ DEBUG_LOCAL void CALLBACK_COMPAT // Copy the data. - memcpy( &obj->addr, inRData, inRDataSize ); - obj->addrValid = true; + memcpy( &obj->addr4, inRData, inRDataSize ); + obj->addr4Valid = true; + obj->numValidAddrs++; // Signal that a result is ready. - check( obj->dataEvent ); - ok = SetEvent( obj->dataEvent ); + check( obj->data4Event ); + ok = SetEvent( obj->data4Event ); check_translated_errno( ok, GetLastError(), WSAEINVAL ); // Stop the resolver after the first response. - DNSServiceRefDeallocate( inRef ); - obj->resolver = NULL; + __try + { + DNSServiceRefDeallocate( inRef ); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + } + + obj->resolver4 = NULL; exit: NSPUnlock(); @@ -1093,6 +1299,96 @@ exit: #pragma mark - #endif + +//=========================================================================================================================== +// QueryRecordCallback6 +//=========================================================================================================================== + +DEBUG_LOCAL void CALLBACK_COMPAT + QueryRecordCallback6( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL, + void * inContext ) +{ + QueryRef obj; + const char * src; + char * dst; + BOOL ok; + + DEBUG_UNUSED( inFlags ); + DEBUG_UNUSED( inInterfaceIndex ); + DEBUG_UNUSED( inTTL ); + + NSPLock(); + obj = (QueryRef) inContext; + check( obj ); + require_noerr( inErrorCode, exit ); + require_quiet( inFlags & kDNSServiceFlagsAdd, exit ); + require( inRRClass == kDNSServiceClass_IN, exit ); + require( inRRType == kDNSServiceType_AAAA, exit ); + require( inRDataSize == 16, exit ); + + dlog( kDebugLevelTrace, "%s (flags=0x%08X, name=%s, rrType=%d, rDataSize=%d)\n", + __ROUTINE__, inFlags, inName, inRRType, inRDataSize ); + + // Copy the name if needed. + + if( obj->name[ 0 ] == '\0' ) + { + src = inName; + dst = obj->name; + while( *src != '\0' ) + { + *dst++ = *src++; + } + *dst = '\0'; + obj->nameSize = (size_t)( dst - obj->name ); + check( obj->nameSize < sizeof( obj->name ) ); + } + + // Copy the data. + + memcpy( &obj->addr6, inRData, inRDataSize ); + + obj->addr6ScopeId = GetScopeId( inInterfaceIndex ); + require( obj->addr6ScopeId, exit ); + obj->addr6Valid = true; + obj->numValidAddrs++; + + // Signal that we're done + + check( obj->data6Event ); + ok = SetEvent( obj->data6Event ); + check_translated_errno( ok, GetLastError(), WSAEINVAL ); + + // Stop the resolver after the first response. + + __try + { + DNSServiceRefDeallocate( inRef ); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + } + + obj->resolver6 = NULL; + +exit: + + + + NSPUnlock(); +} + + //=========================================================================================================================== // QueryCopyQuerySet // @@ -1136,6 +1432,8 @@ exit: return( err ); } + + //=========================================================================================================================== // QueryCopyQuerySetTo // @@ -1227,6 +1525,7 @@ DEBUG_LOCAL void } n = inQuerySet->dwNumberOfProtocols; + if( n > 0 ) { check( inQuerySet->lpafpProtocols ); @@ -1250,28 +1549,55 @@ DEBUG_LOCAL void // Copy the address(es). - if( ( inQuerySetFlags & LUP_RETURN_ADDR ) && inRef->addrValid ) + if( ( inQuerySetFlags & LUP_RETURN_ADDR ) && ( inRef->numValidAddrs > 0 ) ) { - struct sockaddr_in * addr; + struct sockaddr_in * addr4; + struct sockaddr_in6 * addr6; + int index; - outQuerySet->dwNumberOfCsAddrs = 1; - outQuerySet->lpcsaBuffer = (LPCSADDR_INFO) dst; - dst += sizeof( *outQuerySet->lpcsaBuffer ); + outQuerySet->dwNumberOfCsAddrs = inRef->numValidAddrs; + outQuerySet->lpcsaBuffer = (LPCSADDR_INFO) dst; + dst += ( sizeof( *outQuerySet->lpcsaBuffer ) ) * ( inRef->numValidAddrs ) ; + index = 0; - outQuerySet->lpcsaBuffer[ 0 ].LocalAddr.lpSockaddr = NULL; - outQuerySet->lpcsaBuffer[ 0 ].LocalAddr.iSockaddrLength = 0; + if ( inRef->addr4Valid ) + { + outQuerySet->lpcsaBuffer[ index ].LocalAddr.lpSockaddr = NULL; + outQuerySet->lpcsaBuffer[ index ].LocalAddr.iSockaddrLength = 0; - outQuerySet->lpcsaBuffer[ 0 ].RemoteAddr.lpSockaddr = (LPSOCKADDR) dst; - outQuerySet->lpcsaBuffer[ 0 ].RemoteAddr.iSockaddrLength = sizeof( struct sockaddr_in ); + outQuerySet->lpcsaBuffer[ index ].RemoteAddr.lpSockaddr = (LPSOCKADDR) dst; + outQuerySet->lpcsaBuffer[ index ].RemoteAddr.iSockaddrLength = sizeof( struct sockaddr_in ); - addr = (struct sockaddr_in *) dst; - memset( addr, 0, sizeof( *addr ) ); - addr->sin_family = AF_INET; - memcpy( &addr->sin_addr, &inRef->addr, 4 ); - dst += sizeof( *addr ); + addr4 = (struct sockaddr_in *) dst; + memset( addr4, 0, sizeof( *addr4 ) ); + addr4->sin_family = AF_INET; + memcpy( &addr4->sin_addr, &inRef->addr4, 4 ); + dst += sizeof( *addr4 ); + + outQuerySet->lpcsaBuffer[ index ].iSocketType = AF_INET; // Emulate Tcpip NSP + outQuerySet->lpcsaBuffer[ index ].iProtocol = IPPROTO_UDP; // Emulate Tcpip NSP + + index++; + } + + if ( inRef->addr6Valid ) + { + outQuerySet->lpcsaBuffer[ index ].LocalAddr.lpSockaddr = NULL; + outQuerySet->lpcsaBuffer[ index ].LocalAddr.iSockaddrLength = 0; - outQuerySet->lpcsaBuffer[ 0 ].iSocketType = AF_INET; // Emulate Tcpip NSP - outQuerySet->lpcsaBuffer[ 0 ].iProtocol = IPPROTO_UDP; // Emulate Tcpip NSP + outQuerySet->lpcsaBuffer[ index ].RemoteAddr.lpSockaddr = (LPSOCKADDR) dst; + outQuerySet->lpcsaBuffer[ index ].RemoteAddr.iSockaddrLength = sizeof( struct sockaddr_in6 ); + + addr6 = (struct sockaddr_in6 *) dst; + memset( addr6, 0, sizeof( *addr6 ) ); + addr6->sin6_family = AF_INET6; + addr6->sin6_scope_id = inRef->addr6ScopeId; + memcpy( &addr6->sin6_addr, &inRef->addr6, 16 ); + dst += sizeof( *addr6 ); + + outQuerySet->lpcsaBuffer[ index ].iSocketType = AF_INET6; // Emulate Tcpip NSP + outQuerySet->lpcsaBuffer[ index ].iProtocol = IPPROTO_UDP; // Emulate Tcpip NSP + } } else { @@ -1281,12 +1607,12 @@ DEBUG_LOCAL void // Copy the hostent blob. - if( ( inQuerySetFlags & LUP_RETURN_BLOB ) && inRef->addrValid ) + if( ( inQuerySetFlags & LUP_RETURN_BLOB ) && inRef->addr4Valid ) { uint8_t * base; struct hostent * he; uintptr_t * p; - + outQuerySet->lpBlob = (LPBLOB) dst; dst += sizeof( *outQuerySet->lpBlob ); @@ -1312,14 +1638,14 @@ DEBUG_LOCAL void *p++ = (uintptr_t)( dst - base ); *p++ = 0; p = (uintptr_t *) dst; - *p++ = (uintptr_t) inRef->addr; + *p++ = (uintptr_t) inRef->addr4; dst = (uint8_t *) p; outQuerySet->lpBlob->cbSize = (ULONG)( dst - base ); outQuerySet->lpBlob->pBlobData = (BYTE *) base; } dlog_query_set( kDebugLevelVerbose, outQuerySet ); - + check( (size_t)( dst - ( (uint8_t *) outQuerySet ) ) == debugSize ); } @@ -1392,15 +1718,21 @@ DEBUG_LOCAL size_t QueryCopyQuerySetSize( QueryRef inRef, const WSAQUERYSETW *in // Calculate the size of the address(es). - if( ( inQuerySetFlags & LUP_RETURN_ADDR ) && inRef->addrValid ) + if( ( inQuerySetFlags & LUP_RETURN_ADDR ) && inRef->addr4Valid ) { size += sizeof( *inQuerySet->lpcsaBuffer ); size += sizeof( struct sockaddr_in ); } + + if( ( inQuerySetFlags & LUP_RETURN_ADDR ) && inRef->addr6Valid ) + { + size += sizeof( *inQuerySet->lpcsaBuffer ); + size += sizeof( struct sockaddr_in6 ); + } // Calculate the size of the hostent blob. - if( ( inQuerySetFlags & LUP_RETURN_BLOB ) && inRef->addrValid ) + if( ( inQuerySetFlags & LUP_RETURN_BLOB ) && inRef->addr4Valid ) { size += sizeof( *inQuerySet->lpBlob ); // Blob ptr/size structure size += sizeof( struct hostent ); // Old-style hostent structure @@ -1984,3 +2316,215 @@ exit: return err; } + + +//=========================================================================================================================== +// GetNextLabel +//=========================================================================================================================== +DEBUG_LOCAL 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 == '\\') + { + c = *cstr++; + if (isdigit(cstr[-1]) && isdigit(cstr[0]) && isdigit(cstr[1])) + { + int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal + int v1 = cstr[ 0] - '0'; + int v2 = cstr[ 1] - '0'; + int val = v0 * 100 + v1 * 10 + v2; + if (val <= 255) { c = (char)val; cstr += 2; } // If valid three-digit decimal value, use it + } + } + *ptr++ = c; + if (ptr >= label+64) return(NULL); + } + if (*cstr) cstr++; // Skip over the trailing dot (if present) + *ptr++ = 0; + return(cstr); +} + + +#ifdef ENABLE_REVERSE_LOOKUP +//=========================================================================================================================== +// IsReverseLookup +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus +IsReverseLookup( LPCWSTR name, size_t size ) +{ + LPCWSTR p; + OSStatus err = kNoErr; + + require_action_quiet( size > sizeof_string( ".0.8.e.f.ip6.arpa" ), exit, err = WSASERVICE_NOT_FOUND ); + + p = name + ( size - 1 ); + p = ( *p == '.' ) ? ( p - sizeof_string( ".0.8.e.f.ip6.arpa" ) ) : ( ( p - sizeof_string( ".0.8.e.f.ip6.arpa" ) ) + 1 ); + + if ( ( ( p[ 0 ] != '.' ) || + ( ( p[ 1 ] != '0' ) ) || + ( ( p[ 2 ] != '.' ) ) || + ( ( p[ 3 ] != '8' ) ) || + ( ( p[ 4 ] != '.' ) ) || + ( ( p[ 5 ] != 'E' ) && ( p[ 5 ] != 'e' ) ) || + ( ( p[ 6 ] != '.' ) ) || + ( ( p[ 7 ] != 'F' ) && ( p[ 7 ] != 'f' ) ) || + ( ( p[ 8 ] != '.' ) ) || + ( ( p[ 9 ] != 'I' ) && ( p[ 9 ] != 'i' ) ) || + ( ( p[ 10 ] != 'P' ) && ( p[ 10 ] != 'p' ) ) || + ( ( p[ 11 ] != '6' ) ) || + ( ( p[ 12 ] != '.' ) ) || + ( ( p[ 13 ] != 'A' ) && ( p[ 13 ] != 'a' ) ) || + ( ( p[ 14 ] != 'R' ) && ( p[ 14 ] != 'r' ) ) || + ( ( p[ 15 ] != 'P' ) && ( p[ 15 ] != 'p' ) ) || + ( ( p[ 16 ] != 'A' ) && ( p[ 16 ] != 'a' ) ) ) ) + { + require_action_quiet( size > sizeof_string( ".254.169.in-addr.arpa" ), exit, err = WSASERVICE_NOT_FOUND ); + + p = name + ( size - 1 ); + p = ( *p == '.' ) ? ( p - sizeof_string( ".254.169.in-addr.arpa" ) ) : ( ( p - sizeof_string( ".254.169.in-addr.arpa" ) ) + 1 ); + + require_action_quiet( ( ( p[ 0 ] == '.' ) && + ( ( p[ 1 ] == '2' ) ) && + ( ( p[ 2 ] == '5' ) ) && + ( ( p[ 3 ] == '4' ) ) && + ( ( p[ 4 ] == '.' ) ) && + ( ( p[ 5 ] == '1' ) ) && + ( ( p[ 6 ] == '6' ) ) && + ( ( p[ 7 ] == '9' ) ) && + ( ( p[ 8 ] == '.' ) ) && + ( ( p[ 9 ] == 'I' ) || ( p[ 9 ] == 'i' ) ) && + ( ( p[ 10 ] == 'N' ) || ( p[ 10 ] == 'n' ) ) && + ( ( p[ 11 ] == '-' ) ) && + ( ( p[ 12 ] == 'A' ) || ( p[ 12 ] == 'a' ) ) && + ( ( p[ 13 ] == 'D' ) || ( p[ 13 ] == 'd' ) ) && + ( ( p[ 14 ] == 'D' ) || ( p[ 14 ] == 'd' ) ) && + ( ( p[ 15 ] == 'R' ) || ( p[ 15 ] == 'r' ) ) && + ( ( p[ 16 ] == '.' ) ) && + ( ( p[ 17 ] == 'A' ) || ( p[ 17 ] == 'a' ) ) && + ( ( p[ 18 ] == 'R' ) || ( p[ 18 ] == 'r' ) ) && + ( ( p[ 19 ] == 'P' ) || ( p[ 19 ] == 'p' ) ) && + ( ( p[ 20 ] == 'A' ) || ( p[ 20 ] == 'a' ) ) ), + exit, err = WSASERVICE_NOT_FOUND ); + } + + // It's a reverse lookup + + check( err == kNoErr ); + +exit: + + return err; +} +#endif + +//=========================================================================================================================== +// GetScopeId +//=========================================================================================================================== + +DEBUG_LOCAL DWORD +GetScopeId( DWORD ifIndex ) +{ + DWORD err; + int i; + DWORD flags; + struct ifaddrs * head; + struct ifaddrs ** next; + IP_ADAPTER_ADDRESSES * iaaList; + ULONG iaaListSize; + IP_ADAPTER_ADDRESSES * iaa; + DWORD scopeId = 0; + + head = NULL; + next = &head; + iaaList = NULL; + + require( gGetAdaptersAddressesFunctionPtr, exit ); + + // Get the list of interfaces. The first call gets the size and the second call gets the actual data. + // This loops to handle the case where the interface changes in the window after getting the size, but before the + // second call completes. A limit of 100 retries is enforced to prevent infinite loops if something else is wrong. + + flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME; + i = 0; + for( ;; ) + { + iaaListSize = 0; + err = gGetAdaptersAddressesFunctionPtr( AF_UNSPEC, flags, NULL, NULL, &iaaListSize ); + check( err == ERROR_BUFFER_OVERFLOW ); + check( iaaListSize >= sizeof( IP_ADAPTER_ADDRESSES ) ); + + iaaList = (IP_ADAPTER_ADDRESSES *) malloc( iaaListSize ); + require_action( iaaList, exit, err = ERROR_NOT_ENOUGH_MEMORY ); + + err = gGetAdaptersAddressesFunctionPtr( AF_UNSPEC, flags, NULL, iaaList, &iaaListSize ); + if( err == ERROR_SUCCESS ) break; + + free( iaaList ); + iaaList = NULL; + ++i; + require( i < 100, exit ); + dlog( kDebugLevelWarning, "%s: retrying GetAdaptersAddresses after %d failure(s) (%d %m)\n", __ROUTINE__, i, err, err ); + } + + for( iaa = iaaList; iaa; iaa = iaa->Next ) + { + DWORD ipv6IfIndex; + + if ( iaa->IfIndex > 0xFFFFFF ) + { + continue; + } + if ( iaa->Ipv6IfIndex > 0xFF ) + { + continue; + } + + // For IPv4 interfaces, there seems to be a bug in iphlpapi.dll that causes the + // following code to crash when iterating through the prefix list. This seems + // to occur when iaa->Ipv6IfIndex != 0 when IPv6 is not installed on the host. + // This shouldn't happen according to Microsoft docs which states: + // + // "Ipv6IfIndex contains 0 if IPv6 is not available on the interface." + // + // So the data structure seems to be corrupted when we return from + // GetAdaptersAddresses(). The bug seems to occur when iaa->Length < + // sizeof(IP_ADAPTER_ADDRESSES), so when that happens, we'll manually + // modify iaa to have the correct values. + + if ( iaa->Length >= sizeof( IP_ADAPTER_ADDRESSES ) ) + { + ipv6IfIndex = iaa->Ipv6IfIndex; + } + else + { + ipv6IfIndex = 0; + } + + // Skip psuedo and tunnel interfaces. + + if( ( ipv6IfIndex == 1 ) || ( iaa->IfType == IF_TYPE_TUNNEL ) ) + { + continue; + } + + if ( iaa->IfIndex == ifIndex ) + { + scopeId = iaa->Ipv6IfIndex; + break; + } + } + +exit: + + if( iaaList ) + { + free( iaaList ); + } + + return scopeId; +} diff --git a/mDNSWindows/mdnsNSP/mdnsNSP.vcproj b/mDNSWindows/mdnsNSP/mdnsNSP.vcproj index 4648cff..ed11f5a 100644 --- a/mDNSWindows/mdnsNSP/mdnsNSP.vcproj +++ b/mDNSWindows/mdnsNSP/mdnsNSP.vcproj @@ -1,7 +1,7 @@ - + @@ -37,13 +37,15 @@ Name="VCCustomBuildTool"/> + + + @@ -87,15 +95,17 @@ Name="VCCustomBuildTool"/> + + + + +