new-loopback-dll-20040622
[openafs.git] / src / WINNT / install / loopback / loopbackutils.cpp
1 /*
2    Copyright 2004 by the Massachusetts Institute of Technology              
3                                                                             
4    All rights reserved.                                                     
5                                                                             
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     
13    prior permission.                                                        
14                                                                             
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      
21    SOFTWARE.                                                                
22 */
23
24 #define _WIN32_DCOM
25 #include <windows.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28
29 struct Args {
30         bool bQuiet;
31         LPWSTR lpIPAddr;
32         LPWSTR lpSubnetMask;
33     LPWSTR lpConnectionName;
34         Args () : bQuiet (0), lpIPAddr (0), lpSubnetMask (0), lpConnectionName (0) { }
35         ~Args () {
36                 if (lpIPAddr) free (lpIPAddr);
37                 if (lpSubnetMask) free (lpSubnetMask);
38         if (lpConnectionName) free (lpConnectionName);
39         }
40 };
41
42 #include <shellapi.h>
43 #define INITGUID
44 #include <guiddef.h>
45 #include <devguid.h>
46 #include <objbase.h>
47 #include <setupapi.h>
48 #include <tchar.h>
49 #include <Msi.h>
50 #include <Msiquery.h>
51 #include "loopbackutils.h"
52
53 extern "C" DWORD UnInstallLoopBack(void)
54 {
55     BOOL ok;
56     DWORD ret = 0;
57     GUID netGuid;
58     HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
59     SP_DEVINFO_DATA DeviceInfoData;
60     TCHAR* deviceDesc;
61     DWORD index = 0;
62     BOOL found = FALSE;
63     DWORD size = 0;
64
65     // initialize the structure size
66     DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
67
68     // copy the net class GUID
69     memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof(GUID_DEVCLASS_NET));
70
71     // return a device info set contains all installed devices of the Net class
72     hDeviceInfo = SetupDiGetClassDevs(&netGuid, NULL, NULL, DIGCF_PRESENT);
73
74     if (hDeviceInfo == INVALID_HANDLE_VALUE)
75         return GetLastError();
76
77     deviceDesc = (TCHAR *)malloc(MAX_PATH*sizeof(TCHAR));
78     // enumerate the driver info list
79     while (SetupDiEnumDeviceInfo(hDeviceInfo, index, &DeviceInfoData))
80     {
81         // try to get the DeviceDesc registry property
82         ok = SetupDiGetDeviceRegistryProperty(hDeviceInfo, &DeviceInfoData,
83                                               SPDRP_DEVICEDESC,
84                                               NULL, (PBYTE)deviceDesc,
85                                               MAX_PATH * sizeof(TCHAR), &size);
86         if (!ok)
87         {
88             ret = GetLastError();
89             if (ret != ERROR_INSUFFICIENT_BUFFER)
90                 break;
91             // if the buffer is too small, reallocate
92             free(deviceDesc);
93             deviceDesc = (TCHAR *)malloc(size);
94             ok = SetupDiGetDeviceRegistryProperty(hDeviceInfo,
95                                                   &DeviceInfoData,
96                                                   SPDRP_DEVICEDESC,
97                                                   NULL, (PBYTE)deviceDesc,
98                                                   size, NULL);
99             if (!ok)
100                 break;
101         }
102
103         // case insensitive comparison
104         _tcslwr(deviceDesc);
105         if( _tcsstr(deviceDesc, DRIVER))
106         {
107             found = TRUE;
108             break;
109         }
110
111         index++;
112     }
113
114     free(deviceDesc);
115     
116     if (found == FALSE)
117     {
118         ret = GetLastError();
119         printf("The %s does not seem to be installed\n", DRIVER_DESC);
120         goto cleanup;
121     }
122
123     ok = SetupDiSetSelectedDevice(hDeviceInfo, &DeviceInfoData);
124     if (!ok)
125     {
126         ret = GetLastError();
127         goto cleanup;
128     }
129     ok = SetupDiCallClassInstaller(DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
130     if (!ok)
131     {
132         ret = GetLastError();
133         goto cleanup;
134     }
135
136 cleanup:
137     // clean up the device info set
138     if (hDeviceInfo != INVALID_HANDLE_VALUE)
139         SetupDiDestroyDeviceInfoList(hDeviceInfo);
140
141     return ret;
142 }
143
144 BOOL IsLoopbackInstalled(void)
145 {
146     TCHAR * hwid = _T("*MSLOOP");
147     HDEVINFO DeviceInfoSet;
148     SP_DEVINFO_DATA DeviceInfoData;
149     DWORD i,err;
150     BOOL found;
151     
152     //
153     // Create a Device Information Set with all present devices.
154     //
155     DeviceInfoSet = SetupDiGetClassDevs(NULL, 0, 0, DIGCF_ALLCLASSES | DIGCF_PRESENT ); // All devices present on system
156     if (DeviceInfoSet == INVALID_HANDLE_VALUE)
157     {
158         return FALSE; // nothing installed?
159     }
160     
161     //
162     //  Enumerate through all Devices.
163     //
164     found = FALSE;
165     DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
166     for (i=0; SetupDiEnumDeviceInfo(DeviceInfoSet,i,&DeviceInfoData); i++)
167     {
168         DWORD DataT;
169         TCHAR *p, *buffer = NULL;
170         DWORD buffersize = 0;
171         
172         //
173         // We won't know the size of the HardwareID buffer until we call
174         // this function. So call it with a null to begin with, and then 
175         // use the required buffer size to Alloc the nessicary space.
176         // Keep calling we have success or an unknown failure.
177         //
178         while (!SetupDiGetDeviceRegistryProperty(DeviceInfoSet,&DeviceInfoData,SPDRP_HARDWAREID,&DataT,(PBYTE)buffer,buffersize,&buffersize))
179         {
180             if (GetLastError() == ERROR_INVALID_DATA)
181             {
182                 // May be a Legacy Device with no hwid. Continue.
183                 break;
184             }
185             else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
186             {
187                 // We need to change the buffer size.
188                 if (buffer) 
189                     LocalFree(buffer);
190                 buffer = (TCHAR *)LocalAlloc(LPTR,buffersize);
191             }
192             else
193             {
194                 goto cleanup_DeviceInfo;
195             }            
196         }
197         
198         if (GetLastError() == ERROR_INVALID_DATA) 
199             continue;
200         
201         // Compare each entry in the buffer multi-sz list with our hwid.
202         for (p=buffer; *p && (p < &buffer[buffersize]); p += _tcslen(p)+1)
203         {
204             if (!_tcsicmp(hwid,p))
205             {
206                 found = TRUE;
207                 break;
208             }
209         }
210         
211         if (buffer) LocalFree(buffer);
212         if (found) break;
213     }
214     
215     //  Cleanup.
216 cleanup_DeviceInfo:
217     err = GetLastError();
218     SetupDiDestroyDeviceInfoList(DeviceInfoSet);
219     SetLastError(err);
220     
221     return found;
222 }
223
224
225 extern "C" DWORD InstallLoopBack(LPCTSTR pConnectionName, LPCTSTR ip, LPCTSTR mask)
226 {
227     BOOL ok;
228     DWORD ret = 0;
229     GUID netGuid;
230     HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
231     SP_DEVINFO_DATA DeviceInfoData;
232     SP_DRVINFO_DATA DriverInfoData;
233     SP_DEVINSTALL_PARAMS  DeviceInstallParams;
234     TCHAR className[MAX_PATH];
235     TCHAR temp[MAX_PATH];
236     DWORD index = 0;
237     BOOL found = FALSE;
238     BOOL registered = FALSE;
239     BOOL destroyList = FALSE;
240
241     HKEY hkey = NULL;
242     DWORD cbSize;
243     DWORD dwValueType;
244     TCHAR pCfgGuidString[40];
245
246     // initialize the structure size
247     DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
248     DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
249
250     // copy the net class GUID
251     memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof(GUID_DEVCLASS_NET));
252
253     // create an empty device info set associated with the net class GUID
254     hDeviceInfo = SetupDiCreateDeviceInfoList(&netGuid, NULL);
255     if (hDeviceInfo == INVALID_HANDLE_VALUE)
256         return GetLastError();
257
258     // get the class name from GUID
259     ok = SetupDiClassNameFromGuid(&netGuid, className, MAX_PATH, NULL);
260     if (!ok)
261     {
262         ret = GetLastError();
263         goto cleanup;
264     }
265
266     // create a device info element and add the new device instance
267     // key to registry
268     ok = SetupDiCreateDeviceInfo(hDeviceInfo, className, &netGuid, NULL, NULL,
269                                  DICD_GENERATE_ID, &DeviceInfoData);
270     if (!ok)
271     {
272         ret = GetLastError();
273         goto cleanup;
274     }
275
276     // select the newly created device info to be the currently
277     // selected member
278     ok = SetupDiSetSelectedDevice(hDeviceInfo, &DeviceInfoData);
279     if (!ok)
280     {
281         ret = GetLastError();
282         goto cleanup;
283     }
284
285     // build a list of class drivers
286     ok = SetupDiBuildDriverInfoList(hDeviceInfo, &DeviceInfoData,
287                                     SPDIT_CLASSDRIVER);
288     if (!ok)
289     {
290         ret = GetLastError();
291         goto cleanup;
292     }
293
294     destroyList = TRUE;
295
296     // enumerate the driver info list
297     while (SetupDiEnumDriverInfo(hDeviceInfo, &DeviceInfoData,
298                                  SPDIT_CLASSDRIVER, index, &DriverInfoData))
299     {
300         // if the manufacture is microsoft
301         if (_tcsicmp(DriverInfoData.MfgName, MANUFACTURE) == 0)
302         {
303             // case insensitive search for loopback
304             _tcscpy(temp, DriverInfoData.Description);
305             _tcslwr(temp);
306             if( _tcsstr(temp, DRIVER))
307             {
308                 found = TRUE;
309                 break;
310             }
311         }
312         index++;
313     }
314
315     if (!found)
316     {
317         ret = GetLastError();
318         printf("Could not find the %s driver to install\n", DRIVER_DESC);
319         goto cleanup;
320     }
321
322     // set the loopback driver to be the currently selected
323     ok = SetupDiSetSelectedDriver(hDeviceInfo, &DeviceInfoData,
324                                   &DriverInfoData);
325     if (!ok)
326     {
327         ret = GetLastError();
328         goto cleanup;
329     }
330
331     // register the phantom device to repare for install
332     ok = SetupDiCallClassInstaller(DIF_REGISTERDEVICE, hDeviceInfo,
333                                    &DeviceInfoData);
334     if (!ok)
335     {
336         ret = GetLastError();
337         goto cleanup;
338     }
339
340     // registered, but remove if errors occur in the following code
341     registered = TRUE;
342
343     // ask the installer if we can install the device
344     ok = SetupDiCallClassInstaller(DIF_ALLOW_INSTALL, hDeviceInfo,
345                                    &DeviceInfoData);
346     if (!ok)
347     {
348         ret = GetLastError();
349         if (ret != ERROR_DI_DO_DEFAULT)
350         {
351             goto cleanup;
352         }
353         else
354             ret = 0;
355     }
356
357     // install the files first
358     ok = SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES, hDeviceInfo,
359                                    &DeviceInfoData);
360     if (!ok)
361     {
362         ret = GetLastError();
363         goto cleanup;
364     }
365
366     // get the device install parameters and disable filecopy
367     DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
368     ok = SetupDiGetDeviceInstallParams(hDeviceInfo, &DeviceInfoData,
369                                        &DeviceInstallParams);
370     if (ok)
371     {
372         DeviceInstallParams.Flags |= DI_NOFILECOPY;
373         ok = SetupDiSetDeviceInstallParams(hDeviceInfo, &DeviceInfoData,
374                                            &DeviceInstallParams);
375         if (!ok)
376         {
377             ret = GetLastError();
378             goto cleanup;
379         }
380     }
381
382     //
383     // Register any device-specific co-installers for this device,
384     //
385
386     ok = SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS,
387                                    hDeviceInfo,
388                                    &DeviceInfoData);
389     if (!ok)
390     {
391         ret = GetLastError();
392         goto cleanup;
393     }
394
395     //
396     // install any  installer-specified interfaces.
397     // and then do the real install
398     //
399     ok = SetupDiCallClassInstaller(DIF_INSTALLINTERFACES,
400                                    hDeviceInfo,
401                                    &DeviceInfoData);
402     if (!ok)
403     {
404         ret = GetLastError();
405         goto cleanup;
406     }
407     PAUSE;
408     ok = SetupDiCallClassInstaller(DIF_INSTALLDEVICE,
409                                    hDeviceInfo,
410                                    &DeviceInfoData);
411     if (!ok)
412     {
413         ret = GetLastError();
414         PAUSE;
415         goto cleanup;
416     }
417
418     /* Skip to the end if we aren't setting the name */
419     if (!pConnectionName) goto cleanup;
420
421     // Figure out NetCfgInstanceId
422     hkey = SetupDiOpenDevRegKey(hDeviceInfo,
423                                 &DeviceInfoData,
424                                 DICS_FLAG_GLOBAL,
425                                 0,
426                                 DIREG_DRV,
427                                 KEY_READ);
428     if (hkey == INVALID_HANDLE_VALUE)
429     {
430         ret = GetLastError();
431         goto cleanup;
432     }
433
434     cbSize = sizeof(pCfgGuidString);
435     ret = RegQueryValueEx(hkey, _T("NetCfgInstanceId"), NULL,
436                           &dwValueType, (LPBYTE)pCfgGuidString, &cbSize);
437     RegCloseKey(hkey);
438
439     ret = RenameConnection(pCfgGuidString, pConnectionName);
440     if (ret)
441     {
442         printf("Could not set the connection name to \"%S\"\n",
443                pConnectionName);
444         goto cleanup;
445     }
446
447     if (!ip) goto cleanup;
448     ret = SetIpAddress(pCfgGuidString, ip, mask);
449     if (ret)
450     {
451         printf("Could not set the ip address and network mask\n");
452         goto cleanup;
453     }
454     ret = LoopbackBindings(pCfgGuidString);
455     if (ret)
456     {
457         printf("Could not properly set the bindings\n");
458         goto cleanup;
459     }
460     ret = !UpdateHostsFile( pConnectionName, ip, "hosts", FALSE );
461     if (ret)
462     {
463         printf("Could not update hosts file\n");
464         goto cleanup;
465     }
466     ret = !UpdateHostsFile( pConnectionName, ip, "lmhosts", TRUE );
467     if (ret)
468     {
469         printf("Could not update lmhosts file\n");
470         goto cleanup;
471     }
472
473
474 cleanup:
475     // an error has occured, but the device is registered, we must remove it
476     if (ret != 0 && registered)
477         SetupDiCallClassInstaller(DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
478
479     found = SetupDiDeleteDeviceInfo(hDeviceInfo, &DeviceInfoData);
480
481     // destroy the driver info list
482     if (destroyList)
483         SetupDiDestroyDriverInfoList(hDeviceInfo, &DeviceInfoData,
484                                      SPDIT_CLASSDRIVER);
485     // clean up the device info set
486     if (hDeviceInfo != INVALID_HANDLE_VALUE)
487         SetupDiDestroyDeviceInfoList(hDeviceInfo);
488
489     return ret;
490 };
491
492 /* The following functions provide the RunDll32 interface 
493  *    RunDll32 loopback_install.dll doLoopBackEntry [Interface Name] [IP address] [Subnet Mask]
494  */
495
496 static void wcsMallocAndCpy (LPWSTR * dst, const LPWSTR src) {
497         *dst = (LPWSTR) malloc ((wcslen (src) + 1) * sizeof (WCHAR));
498         wcscpy (*dst, src);
499 }
500
501 static void display_usage()
502 {
503     MessageBoxW( NULL, 
504                  L"Installation utility for the MS Loopback Adapter\r\n\r\n"
505                  L"Usage:\r\n"
506                  L"RunDll32 loopback_install.dll doLoopBackEntry [q|quiet] [Connection Name] [IP address] [Submask]\r\n",
507                  L"loopback_install", MB_ICONINFORMATION | MB_OK );
508 }
509
510 static int process_args (LPWSTR lpCmdLine, Args & args) {
511         int i, iNumArgs;
512         LPWSTR * argvW;
513
514         argvW = CommandLineToArgvW (lpCmdLine, &iNumArgs);
515         for (i = 0; i < iNumArgs; i++)
516         {
517                 if (wcsstr (argvW[i], L"help")
518                         || !_wcsicmp (argvW[i], L"?")
519                         || (wcslen(argvW[i]) == 2 && argvW[i][1] == L'?'))
520                 {
521                         display_usage();
522                         GlobalFree (argvW);
523                         return 0;
524                 }
525
526                 if (!_wcsicmp (argvW[i], L"q") || !_wcsicmp (argvW[i], L"quiet")) {
527                         args.bQuiet = true;
528                         continue;
529                 }
530
531                 if (!args.lpConnectionName) {
532                         wcsMallocAndCpy (&args.lpConnectionName, argvW[i]);
533                         continue;
534                 }
535
536                 if (!args.lpIPAddr) {
537                         wcsMallocAndCpy (&args.lpIPAddr, argvW[i]);
538                         continue;
539                 }
540
541                 if (!args.lpSubnetMask) {
542                         wcsMallocAndCpy (&args.lpSubnetMask, argvW[i]);
543                         continue;
544                 }
545
546                 display_usage();
547                 GlobalFree (argvW);
548                 return 0;
549         }
550
551         if (!args.lpConnectionName)
552                 wcsMallocAndCpy (&args.lpConnectionName, DEFAULT_NAME);
553         if (!args.lpIPAddr)
554                 wcsMallocAndCpy (&args.lpIPAddr, DEFAULT_IP);
555         if (!args.lpSubnetMask)
556                 wcsMallocAndCpy (&args.lpSubnetMask, DEFAULT_MASK);
557
558         GlobalFree (argvW);
559         return 1;
560 }
561
562 void CALLBACK doLoopBackEntryW (HWND hwnd, HINSTANCE hinst, LPWSTR lpCmdLine, int nCmdShow)
563 {
564         Args args;
565
566         if (!process_args(lpCmdLine, args)) 
567         return;
568
569         InstallLoopBack(args.lpConnectionName, args.lpIPAddr, args.lpSubnetMask);
570 }
571
572 void CALLBACK uninstallLoopBackEntryW (HWND hwnd, HINSTANCE hinst, LPWSTR lpCmdLine, int nCmdSHow)
573 {
574     Args args;
575
576     // initialize COM
577         // This and CoInitializeSecurity fail when running under the MSI
578         // engine, but there seems to be no ill effect (the security is now
579         // set on the specific object via CoSetProxyBlanket in loopback_configure)
580     if(CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE | COINIT_APARTMENTTHREADED ))
581     {
582                 //Don't fail (MSI install will have already initialized COM)
583         //EasyErrorBox(0, L"Failed to initialize COM.");
584         //return 1;
585     }
586
587         // Initialize COM security (otherwise we'll get permission denied when we try to use WMI or NetCfg)
588     CoInitializeSecurity( NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
589
590     UnInstallLoopBack();
591
592         CoUninitialize();
593 }
594
595 /* And an MSI installer interface */
596
597 UINT __stdcall installLoopbackMSI (MSIHANDLE hInstall)
598 {
599         LPWSTR szValueBuf;
600         DWORD cbValueBuf = 256;
601         Args args;
602         UINT rc;
603
604         szValueBuf = (LPWSTR) malloc (cbValueBuf * sizeof (WCHAR));
605         while (rc = MsiGetPropertyW(hInstall, L"CustomActionData", szValueBuf, &cbValueBuf)) {
606                 free (szValueBuf);
607                 if (rc == ERROR_MORE_DATA) {
608                         cbValueBuf++;
609                         szValueBuf = (LPWSTR) malloc (cbValueBuf * sizeof (WCHAR));
610                 } 
611         else 
612             return ERROR_INSTALL_FAILURE;
613         }
614
615         if (!process_args(szValueBuf, args)) 
616         return ERROR_INSTALL_FAILURE;
617                 
618         rc = InstallLoopBack (args.lpConnectionName, args.lpIPAddr, args.lpSubnetMask);
619
620         if (rc == 1) 
621         return ERROR_INSTALL_FAILURE;
622
623         if (rc == 2) {
624                 MsiDoActionW (hInstall, L"ScheduleReboot");
625         }
626
627         return ERROR_SUCCESS;
628 }
629