2 Copyright 2004 by the Massachusetts Institute of Technology
6 Permission to use, copy, modify, and distribute this software and its
7 documentation for any purpose and without fee is hereby granted,
8 provided that the above copyright notice appear in all copies and that
9 both that copyright notice and this permission notice appear in
10 supporting documentation, and that the name of the Massachusetts
11 Institute of Technology (M.I.T.) not be used in advertising or publicity
12 pertaining to distribution of the software without specific, written
15 M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
16 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
17 M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
18 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
19 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
20 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
33 LPWSTR lpConnectionName;
34 Args () : bQuiet (0), lpIPAddr (0), lpSubnetMask (0), lpConnectionName (0) { }
36 if (lpIPAddr) free (lpIPAddr);
37 if (lpSubnetMask) free (lpSubnetMask);
38 if (lpConnectionName) free (lpConnectionName);
51 #include "loopbackutils.h"
53 extern "C" DWORD UnInstallLoopBack(void)
58 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
59 SP_DEVINFO_DATA DeviceInfoData;
64 // initialize the structure size
65 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
67 // copy the net class GUID
68 memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof(GUID_DEVCLASS_NET));
70 // return a device info set contains all installed devices of the Net class
71 hDeviceInfo = SetupDiGetClassDevs(&netGuid, NULL, NULL, DIGCF_PRESENT);
73 if (hDeviceInfo == INVALID_HANDLE_VALUE)
74 return GetLastError();
76 // enumerate the driver info list
81 ok = SetupDiEnumDeviceInfo(hDeviceInfo, index, &DeviceInfoData);
85 if(GetLastError() == ERROR_NO_MORE_ITEMS)
94 // try to get the DeviceDesc registry property
95 ok = SetupDiGetDeviceRegistryProperty(hDeviceInfo,
104 ret = GetLastError();
105 if (ret != ERROR_INSUFFICIENT_BUFFER) {
110 deviceHwid = (TCHAR *)malloc(size);
111 ok = SetupDiGetDeviceRegistryProperty(hDeviceInfo,
125 // something is wrong. This shouldn't have worked with a NULL buffer
126 ReportMessage(0, "GetDeviceRegistryProperty succeeded with a NULL buffer", NULL, NULL, 0);
131 for (TCHAR *t = deviceHwid; t && *t && t < &deviceHwid[size / sizeof(TCHAR)]; t += _tcslen(t) + 1)
133 if(!_tcsicmp(DRIVERHWID, t)) {
152 ret = GetLastError();
153 ReportMessage(0,"Driver does not seem to be installed", DRIVER_DESC, NULL, ret);
157 ok = SetupDiSetSelectedDevice(hDeviceInfo, &DeviceInfoData);
160 ret = GetLastError();
164 ok = SetupDiCallClassInstaller(DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
167 ret = GetLastError();
174 // clean up the device info set
175 if (hDeviceInfo != INVALID_HANDLE_VALUE)
176 SetupDiDestroyDeviceInfoList(hDeviceInfo);
181 BOOL IsLoopbackInstalled(void)
183 HDEVINFO DeviceInfoSet;
184 SP_DEVINFO_DATA DeviceInfoData;
189 // Create a Device Information Set with all present devices.
191 DeviceInfoSet = SetupDiGetClassDevs(NULL, 0, 0, DIGCF_ALLCLASSES | DIGCF_PRESENT ); // All devices present on system
192 if (DeviceInfoSet == INVALID_HANDLE_VALUE)
194 return FALSE; // nothing installed?
198 // Enumerate through all Devices.
201 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
206 TCHAR *p, *buffer = NULL;
207 DWORD buffersize = 0;
209 ok = SetupDiEnumDeviceInfo(DeviceInfoSet, i, &DeviceInfoData);
212 if(GetLastError() == ERROR_NO_MORE_ITEMS)
219 // We won't know the size of the HardwareID buffer until we call
220 // this function. So call it with a null to begin with, and then
221 // use the required buffer size to Alloc the nessicary space.
222 // Keep calling we have success or an unknown failure.
224 while (!SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
232 if (GetLastError() == ERROR_INVALID_DATA)
234 // May be a Legacy Device with no hwid. Continue.
237 else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
239 // We need to change the buffer size.
242 buffer = (TCHAR *)LocalAlloc(LPTR,buffersize);
248 goto cleanup_DeviceInfo;
252 if (GetLastError() == ERROR_INVALID_DATA) {
258 // Compare each entry in the buffer multi-sz list with our hwid.
259 for (p=buffer; *p && (p < &buffer[buffersize]); p += _tcslen(p)+1)
261 if (!_tcsicmp(DRIVERHWID,p))
276 err = GetLastError();
277 SetupDiDestroyDeviceInfoList(DeviceInfoSet);
284 extern "C" DWORD InstallLoopBack(LPCTSTR pConnectionName, LPCTSTR ip, LPCTSTR mask)
289 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
290 SP_DEVINFO_DATA DeviceInfoData;
291 SP_DRVINFO_DATA DriverInfoData;
292 SP_DEVINSTALL_PARAMS DeviceInstallParams;
293 TCHAR className[MAX_PATH];
294 TCHAR temp[MAX_PATH];
297 BOOL registered = FALSE;
298 BOOL destroyList = FALSE;
299 PSP_DRVINFO_DETAIL_DATA pDriverInfoDetail;
300 DWORD detailBuf[2048]; // for our purposes, 8k buffer is more
301 // than enough to obtain the hardware ID
302 // of the loopback driver.
307 TCHAR pCfgGuidString[40];
309 // initialize the structure size
310 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
311 DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
313 // copy the net class GUID
314 memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof(GUID_DEVCLASS_NET));
316 // create an empty device info set associated with the net class GUID
317 hDeviceInfo = SetupDiCreateDeviceInfoList(&netGuid, NULL);
318 if (hDeviceInfo == INVALID_HANDLE_VALUE)
319 return GetLastError();
321 // get the class name from GUID
322 ok = SetupDiClassNameFromGuid(&netGuid, className, MAX_PATH, NULL);
325 ret = GetLastError();
329 // create a device info element and add the new device instance
331 ok = SetupDiCreateDeviceInfo(hDeviceInfo, className, &netGuid, NULL, NULL,
332 DICD_GENERATE_ID, &DeviceInfoData);
335 ret = GetLastError();
339 // select the newly created device info to be the currently
341 ok = SetupDiSetSelectedDevice(hDeviceInfo, &DeviceInfoData);
344 ret = GetLastError();
348 // build a list of class drivers
349 ok = SetupDiBuildDriverInfoList(hDeviceInfo, &DeviceInfoData,
353 ret = GetLastError();
359 // enumerate the driver info list
364 ret = SetupDiEnumDriverInfo(hDeviceInfo, &DeviceInfoData,
365 SPDIT_CLASSDRIVER, index, &DriverInfoData);
367 // if the function failed and GetLastError() returned
368 // ERROR_NO_MORE_ITEMS, then we have reached the end of the
369 // list. Othewise there was something wrong with this
370 // particular driver.
372 if(GetLastError() == ERROR_NO_MORE_ITEMS)
380 pDriverInfoDetail = (PSP_DRVINFO_DETAIL_DATA) detailBuf;
381 pDriverInfoDetail->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
383 // if we successfully find the hardware ID and it turns out to
384 // be the one for the loopback driver, then we are done.
385 if (SetupDiGetDriverInfoDetail(hDeviceInfo,
393 // pDriverInfoDetail->HardwareID is a MULTISZ string. Go through the
394 // whole list and see if there is a match somewhere.
395 t = pDriverInfoDetail->HardwareID;
396 while (t && *t && t < (TCHAR *) &detailBuf[sizeof(detailBuf)/sizeof(detailBuf[0])]) {
397 if (!_tcsicmp(t, DRIVERHWID))
403 if (t && *t && t < (TCHAR *) &detailBuf[sizeof(detailBuf)/sizeof(detailBuf[0])]) {
414 ret = GetLastError();
415 ReportMessage(0,"Could not find the driver to install", DRIVER_DESC, NULL, 0);
419 // set the loopback driver to be the currently selected
420 ok = SetupDiSetSelectedDriver(hDeviceInfo, &DeviceInfoData,
424 ret = GetLastError();
428 // register the phantom device to repare for install
429 ok = SetupDiCallClassInstaller(DIF_REGISTERDEVICE, hDeviceInfo,
433 ret = GetLastError();
437 // registered, but remove if errors occur in the following code
440 // ask the installer if we can install the device
441 ok = SetupDiCallClassInstaller(DIF_ALLOW_INSTALL, hDeviceInfo,
445 ret = GetLastError();
446 if (ret != ERROR_DI_DO_DEFAULT)
454 // install the files first
455 ok = SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES, hDeviceInfo,
459 ret = GetLastError();
463 // get the device install parameters and disable filecopy
464 DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
465 ok = SetupDiGetDeviceInstallParams(hDeviceInfo, &DeviceInfoData,
466 &DeviceInstallParams);
469 DeviceInstallParams.Flags |= DI_NOFILECOPY;
470 ok = SetupDiSetDeviceInstallParams(hDeviceInfo, &DeviceInfoData,
471 &DeviceInstallParams);
474 ret = GetLastError();
480 // Register any device-specific co-installers for this device,
483 ok = SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS,
488 ret = GetLastError();
493 // install any installer-specified interfaces.
494 // and then do the real install
496 ok = SetupDiCallClassInstaller(DIF_INSTALLINTERFACES,
501 ret = GetLastError();
505 ok = SetupDiCallClassInstaller(DIF_INSTALLDEVICE,
510 ret = GetLastError();
515 /* Skip to the end if we aren't setting the name */
516 if (!pConnectionName) goto cleanup;
518 // Figure out NetCfgInstanceId
519 hkey = SetupDiOpenDevRegKey(hDeviceInfo,
525 if (hkey == INVALID_HANDLE_VALUE)
527 ret = GetLastError();
531 cbSize = sizeof(pCfgGuidString);
532 ret = RegQueryValueEx(hkey, _T("NetCfgInstanceId"), NULL,
533 &dwValueType, (LPBYTE)pCfgGuidString, &cbSize);
536 ret = RenameConnection(pCfgGuidString, pConnectionName);
539 ReportMessage(0,"Could not set the connection name", NULL, pConnectionName, 0);
543 if (!ip) goto cleanup;
544 ret = SetIpAddress(pCfgGuidString, ip, mask);
547 ReportMessage(0,"Could not set the ip address and network mask",NULL,NULL,ret);
550 ret = LoopbackBindings(pCfgGuidString);
553 ReportMessage(0,"Could not properly set the bindings",NULL,NULL,0);
556 ret = !UpdateHostsFile( pConnectionName, ip, "hosts", FALSE );
559 ReportMessage(0,"Could not update hosts file",NULL,NULL,0);
562 ret = !UpdateHostsFile( pConnectionName, ip, "lmhosts", TRUE );
565 ReportMessage(0,"Could not update lmhosts file",NULL,NULL,0);
571 // an error has occured, but the device is registered, we must remove it
572 if (ret != 0 && registered)
573 SetupDiCallClassInstaller(DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
575 found = SetupDiDeleteDeviceInfo(hDeviceInfo, &DeviceInfoData);
577 // destroy the driver info list
579 SetupDiDestroyDriverInfoList(hDeviceInfo, &DeviceInfoData,
581 // clean up the device info set
582 if (hDeviceInfo != INVALID_HANDLE_VALUE)
583 SetupDiDestroyDeviceInfoList(hDeviceInfo);
588 /* The following functions provide the RunDll32 interface
589 * RunDll32 loopback_install.dll doLoopBackEntry [Interface Name] [IP address] [Subnet Mask]
592 static void wcsMallocAndCpy (LPWSTR * dst, const LPWSTR src) {
593 *dst = (LPWSTR) malloc ((wcslen (src) + 1) * sizeof (WCHAR));
597 static void display_usage()
600 L"Installation utility for the MS Loopback Adapter\r\n\r\n"
602 L"RunDll32 loopback_install.dll doLoopBackEntry [q|quiet] [Connection Name] [IP address] [Submask]\r\n",
603 L"loopback_install", MB_ICONINFORMATION | MB_OK );
606 static int process_args (LPWSTR lpCmdLine, int skip, Args & args) {
610 argvW = CommandLineToArgvW (lpCmdLine, &iNumArgs);
611 // Skip over the command name
612 for (i = skip; i < iNumArgs; i++)
614 if (wcsstr (argvW[i], L"help")
615 || !_wcsicmp (argvW[i], L"?")
616 || (wcslen(argvW[i]) == 2 && argvW[i][1] == L'?'))
623 if (!_wcsicmp (argvW[i], L"q") || !_wcsicmp (argvW[i], L"quiet")) {
628 if (!args.lpConnectionName) {
629 wcsMallocAndCpy (&args.lpConnectionName, argvW[i]);
633 if (!args.lpIPAddr) {
634 wcsMallocAndCpy (&args.lpIPAddr, argvW[i]);
638 if (!args.lpSubnetMask) {
639 wcsMallocAndCpy (&args.lpSubnetMask, argvW[i]);
648 if (!args.lpConnectionName)
649 wcsMallocAndCpy (&args.lpConnectionName, DEFAULT_NAME);
651 wcsMallocAndCpy (&args.lpIPAddr, DEFAULT_IP);
652 if (!args.lpSubnetMask)
653 wcsMallocAndCpy (&args.lpSubnetMask, DEFAULT_MASK);
660 void CALLBACK doLoopBackEntryW (HWND hwnd, HINSTANCE hinst, LPWSTR lpCmdLine, int nCmdShow)
664 if (!process_args(lpCmdLine, 0, args))
667 InstallLoopBack(args.lpConnectionName, args.lpIPAddr, args.lpSubnetMask);
670 void CALLBACK uninstallLoopBackEntryW (HWND hwnd, HINSTANCE hinst, LPWSTR lpCmdLine, int nCmdSHow)
675 // This and CoInitializeSecurity fail when running under the MSI
676 // engine, but there seems to be no ill effect (the security is now
677 // set on the specific object via CoSetProxyBlanket in loopback_configure)
678 if(CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE | COINIT_APARTMENTTHREADED ))
680 //Don't fail (MSI install will have already initialized COM)
681 //EasyErrorBox(0, L"Failed to initialize COM.");
685 // Initialize COM security (otherwise we'll get permission denied when we try to use WMI or NetCfg)
686 CoInitializeSecurity( NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
693 /* And an MSI installer interface */
695 UINT __stdcall installLoopbackMSI (MSIHANDLE hInstall)
698 DWORD cbValueBuf = 256;
702 SetMsiReporter("InstallLoopback", "Installing loopback adapter", hInstall);
704 /* check if there is already one installed. If there is, we shouldn't try to
707 if(IsLoopbackInstalled())
708 return ERROR_SUCCESS;
710 szValueBuf = (LPWSTR) malloc (cbValueBuf * sizeof (WCHAR));
711 while (rc = MsiGetPropertyW(hInstall, L"CustomActionData", szValueBuf, &cbValueBuf)) {
713 if (rc == ERROR_MORE_DATA) {
715 szValueBuf = (LPWSTR) malloc (cbValueBuf * sizeof (WCHAR));
718 return ERROR_INSTALL_FAILURE;
721 if (!process_args(szValueBuf, 1, args))
722 return ERROR_INSTALL_FAILURE;
724 rc = InstallLoopBack (args.lpConnectionName, args.lpIPAddr, args.lpSubnetMask);
726 if (rc != 2 && rc != 0)
727 return ERROR_INSTALL_FAILURE;
730 MsiDoActionW (hInstall, L"ScheduleReboot");
733 return ERROR_SUCCESS;
736 UINT __stdcall uninstallLoopbackMSI (MSIHANDLE hInstall)
739 DWORD cbValueBuf = 256;
743 SetMsiReporter("RemoveLoopback", "Removing loopback adapter", hInstall);
745 szValueBuf = (LPWSTR) malloc (cbValueBuf * sizeof (WCHAR));
746 while (rc = MsiGetPropertyW(hInstall, L"CustomActionData", szValueBuf, &cbValueBuf)) {
748 if (rc == ERROR_MORE_DATA) {
750 szValueBuf = (LPWSTR) malloc (cbValueBuf * sizeof (WCHAR));
753 return ERROR_INSTALL_FAILURE;
756 if (!process_args(szValueBuf, 1, args))
757 return ERROR_INSTALL_FAILURE;
759 rc = UnInstallLoopBack ();
762 return ERROR_INSTALL_FAILURE;
765 MsiDoActionW (hInstall, L"ScheduleReboot");
768 return ERROR_SUCCESS;
771 DWORD hMsiHandle = 0;
772 DWORD dwReporterType = REPORT_PRINTF;
774 extern "C" void ReportMessage(int level, LPCSTR msg, LPCSTR str, LPCWSTR wstr, DWORD dw) {
775 if(dwReporterType == REPORT_PRINTF)
776 printf("%s:[%s][%S][%d]\n", (msg?msg:""), (str?str:""), (wstr?wstr:L""), dw);
777 else if(dwReporterType == REPORT_MSI && hMsiHandle && level == 0) {
778 MSIHANDLE hRec = MsiCreateRecord(5);
780 MsiRecordClearData(hRec);
781 MsiRecordSetStringA(hRec,1,(msg)?msg:"");
782 MsiRecordSetStringA(hRec,2,(str)?str:"");
783 MsiRecordSetStringW(hRec,3,(wstr)?wstr:L"");
784 MsiRecordSetInteger(hRec,4,dw);
786 MsiProcessMessage(hMsiHandle,INSTALLMESSAGE_ACTIONDATA,hRec);
788 MsiCloseHandle(hRec);
792 extern "C" void SetMsiReporter(LPCSTR strAction, LPCSTR strDesc,DWORD h) {
793 dwReporterType = REPORT_MSI;
797 /* this is performed in the Wix installer */
798 MSIHANDLE hRec = MsiCreateRecord(4);
800 MsiRecordClearData(hRec);
801 MsiRecordSetStringA(hRec,1,strAction);
802 MsiRecordSetStringA(hRec,2,strDesc);
803 MsiRecordSetStringA(hRec,3,"[1]:([2])([3])([4])");
805 MsiProcessMessage(h,INSTALLMESSAGE_ACTIONSTART, hRec);
807 MsiCloseHandle(hRec);