56dc4d1269fb03aedc5ddbb9aba22fabca1b0cf0
[openafs.git] / src / WINNT / afsd / lanahelper.cpp
1 #include <afx.h>
2 #include <windows.h>
3 #include <winreg.h>
4 #include <nb30.h>
5 #include <tchar.h>
6 #include <shellapi.h>
7 #include <objbase.h>
8 #include <shlobj.h>
9 #include <wtypes.h>
10 #include <string.h>
11 #include <malloc.h>
12 #include <lanahelper.h>
13
14 #define NOLOGGING
15 #ifndef NOLOGGING
16 extern "C" {
17 #include<stdio.h>
18 #include<stdarg.h>
19
20     void afsi_log(TCHAR *p, ...) {
21         va_list marker;
22         TCHAR buffer[200];
23
24         va_start(marker,p);
25         _vstprintf(buffer,p,marker);
26         va_end(marker);
27         _tcscat(buffer,_T("\n"));
28
29         OutputDebugString(buffer);
30     }
31 }
32 #endif
33
34 static const char *szAFSConfigKeyName = "SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon\\Parameters";
35 static const char *szNetbiosNameValue = "NetbiosName";
36 static const char *szIsGatewayValue = "IsGateway";
37 static const char *szLanAdapterValue = "LanAdapter";
38 static const char *szNoFindLanaByName = "NoFindLanaByName";
39
40 // Use the IShellFolder API to get the connection name for the given Guid.
41 static HRESULT lana_ShellGetNameFromGuidW(WCHAR *wGuid, WCHAR *wName, int NameSize)
42 {
43     // This is the GUID for the network connections folder. It is constant.
44     // {7007ACC7-3202-11D1-AAD2-00805FC1270E}
45     const GUID CLSID_NetworkConnections = {
46         0x7007ACC7, 0x3202, 0x11D1, {
47             0xAA, 0xD2, 0x00, 0x80, 0x5F, 0xC1, 0x27, 0x0E
48         }
49     };
50     LPITEMIDLIST pidl;
51     IShellFolder *pShellFolder;
52     IMalloc *pShellMalloc;
53
54     // Build the display name in the form "::{GUID}".
55     if (wcslen(wGuid) >= MAX_PATH)
56         return E_INVALIDARG;
57     WCHAR szAdapterGuid[MAX_PATH + 2];
58     swprintf(szAdapterGuid, L"::%ls", wGuid);
59
60     // Initialize COM.
61     CoInitialize(NULL);
62
63     // Get the shell allocator.
64     HRESULT hr = SHGetMalloc(&pShellMalloc);
65     if (SUCCEEDED(hr))
66     {
67         // Create an instance of the network connections folder.
68         hr = CoCreateInstance(CLSID_NetworkConnections, NULL,
69                               CLSCTX_INPROC_SERVER, IID_IShellFolder,
70                               reinterpret_cast<LPVOID *>(&pShellFolder));
71     }
72     if (SUCCEEDED(hr)) 
73     {
74         hr = pShellFolder->ParseDisplayName(NULL, NULL, szAdapterGuid, NULL,
75                                             &pidl, NULL);
76     }
77     if (SUCCEEDED(hr)) {
78         // Get the display name; this returns the friendly name.
79         STRRET sName;
80         hr = pShellFolder->GetDisplayNameOf(pidl, SHGDN_NORMAL, &sName);
81         if (SUCCEEDED(hr))
82             wcsncpy(wName, sName.pOleStr, NameSize);
83         pShellMalloc->Free(pidl);
84     }
85
86     CoUninitialize();
87     return hr;
88 }
89
90 // Get the Connection Name for the given GUID.
91 extern "C" int lana_GetNameFromGuid(char *Guid, char **Name)
92 {
93     typedef HRESULT (WINAPI *HrLanProcAddr)(GUID *, PCWSTR, PWSTR, LPDWORD);
94     HrLanProcAddr HrLanProc = NULL;
95     HMODULE hNetMan;
96     int size;
97     WCHAR *wGuid = NULL;
98     WCHAR wName[MAX_PATH];
99     DWORD NameSize = MAX_PATH;
100     char *name = NULL;
101     HRESULT status;
102         
103     // Convert the Guid string to Unicode.  First we ask only for the size
104     // of the converted string.  Then we allocate a buffer of sufficient
105     // size to hold the result of the conversion.
106     size = MultiByteToWideChar(CP_ACP, 0, Guid, -1, NULL, 0);
107     wGuid = (WCHAR *) malloc(size * sizeof(WCHAR));
108     MultiByteToWideChar(CP_ACP, 0, Guid, -1, wGuid, size);
109
110     // First try the IShellFolder interface, which was unimplemented
111     // for the network connections folder before XP.
112
113     /* XXX pbh 9/11/03 - revert to using the undocumented APIs on XP while
114      *   waiting to hear back from PSS about the slow reboot issue.
115      *   This is an ugly, misleading hack, but is minimally invasive
116      *   and will be easy to rollback.
117      */
118
119     //status = getname_shellfolder(wGuid, wName, NameSize);
120     status = E_NOTIMPL;
121
122     /* XXX end of pbh 9/11/03 temporary hack*/  
123
124     if (status == E_NOTIMPL) {
125         // The IShellFolder interface is not implemented on this platform.
126         // Try the (undocumented) HrLanConnectionNameFromGuidOrPath API
127         // from the netman DLL.
128 #ifndef NOLOGGING
129         afsi_log("IShellFolder API not implemented, trying HrLanConnectionNameFromGuidOrPath");
130 #endif
131         hNetMan = LoadLibrary("netman.dll");
132         if (hNetMan == NULL) {
133             free(wGuid);
134             return -1;
135         }
136         /* Super Secret Microsoft Call */
137         HrLanProc = (HrLanProcAddr) GetProcAddress(hNetMan,
138                                                    "HrLanConnectionNameFromGuidOrPath");
139         if (HrLanProc == NULL) {
140             FreeLibrary(hNetMan);
141             free(wGuid);
142             return -1;
143         }
144         status = HrLanProc(NULL, wGuid, wName, &NameSize);
145         FreeLibrary(hNetMan);
146     }
147     free(wGuid);
148     if (FAILED(status)) {
149 #ifndef NOLOGGING
150         afsi_log("lana_GetNameFromGuid: failed to get connection name (status %ld)",
151                  status);
152 #endif
153         return -1;
154     }
155
156     // Get the required buffer size, and then convert the string.
157     size = WideCharToMultiByte(CP_ACP, 0, wName, -1, NULL, 0, NULL, NULL);
158     name = (char *) malloc(size);
159     if (name == NULL)
160         return -1;
161     WideCharToMultiByte(CP_ACP, 0, wName, -1, name, size, NULL, NULL);
162 #ifndef NOLOGGING
163     afsi_log("Connection name for %s is '%s'", Guid, name);
164 #endif
165     if (*Name)
166         *Name = name;
167     else
168         free(name);
169     return 0;
170 }
171
172 // Return an array of LANAINFOs corresponding to a connection named LanaName
173 // (NULL LanaName matches all connections), and has an IPv4 binding. Returns
174 // NULL if something goes wrong.
175 // NOTE: caller must free the returned block if non NULL.
176 extern "C" LANAINFO * lana_FindLanaByName(const char *LanaName)
177 {
178     const char RegNetBiosLinkageKeyName[] =
179         "System\\CurrentControlSet\\Services\\NetBios\\Linkage";
180     HKEY hkey;
181     LONG status;
182     struct {
183         BYTE flags;
184         BYTE number;
185     } lanamap[MAX_LANA+1];
186     DWORD lanamapsize = sizeof(lanamap);
187     DWORD type;
188     char *bindpaths = NULL;
189     DWORD bindpathsize;
190     int nlana;
191     int i;
192     char *guid;
193     char *name;
194     char *pBind;
195     char *p;
196
197     LANAINFO * lanainfo;
198
199     // Open the NetBios Linkage key.
200     status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegNetBiosLinkageKeyName, 0, 
201                           KEY_QUERY_VALUE, &hkey);
202         
203     if (status != ERROR_SUCCESS) {
204 #ifndef NOLOGGING
205         afsi_log("Failed to open NetBios Linkage key (status %ld)", status);
206 #endif
207         return NULL;
208     }
209
210     // Read the lana map.
211     status = RegQueryValueEx(hkey, "LanaMap", 0, &type,
212                          (BYTE *) &lanamap, &lanamapsize);
213     if (status != ERROR_SUCCESS) {
214 #ifndef NOLOGGING
215         afsi_log("Failed to read LanaMap (status %ld)", status);
216 #endif
217         RegCloseKey(hkey);
218         return NULL;
219     }
220     if (lanamapsize == 0) {
221 #ifndef NOLOGGING
222         afsi_log("No data in LanaMap");
223 #endif
224         return NULL;
225     }
226     nlana = lanamapsize / sizeof(lanamap[0]);
227
228     // Get the bind paths for NetBios so we can match them up
229     // with the lana map.  First we query for the size, so we
230     // can allocate an appropriate buffer.
231     status = RegQueryValueEx(hkey, "Bind", 0, &type, NULL, &bindpathsize);
232     if (status == ERROR_SUCCESS && bindpathsize != 0) {
233         bindpaths = (char *) malloc(bindpathsize * sizeof(char));
234         if (bindpaths == NULL) {
235 #ifndef NOLOGGING
236             afsi_log("Cannot allocate %ld bytes for bindpaths", bindpathsize);
237 #endif
238             RegCloseKey(hkey);
239             return NULL;
240         }
241         status = RegQueryValueEx(hkey, "Bind", 0, &type, 
242                                  (BYTE *) bindpaths, &bindpathsize);
243     }
244     RegCloseKey(hkey);
245     if (status != ERROR_SUCCESS) {
246 #ifndef NOLOGGING
247         afsi_log("Failed to read bind paths (status %ld)", status);
248 #endif
249         if (bindpaths != NULL)
250             free(bindpaths);
251         return NULL;
252       }
253     if (bindpathsize == 0) {
254 #ifndef NOLOGGING
255         afsi_log("No bindpath data");
256 #endif
257         if (bindpaths != NULL)
258             free(bindpaths);
259         return NULL;
260     }
261
262     if (LanaName)
263     {
264         lanainfo = (LANAINFO *) malloc(sizeof(LANAINFO)*2);
265         if(lanainfo == NULL) {
266             free(bindpaths);
267             return NULL;
268         }
269         memset(lanainfo, 0, sizeof(LANAINFO) * 2);
270         lanainfo[0].lana_number = LANA_INVALID;
271     }
272     else
273     {
274         lanainfo = (LANAINFO *) malloc(sizeof(LANAINFO)*(nlana+1));
275         if(lanainfo == NULL) {
276             free(bindpaths);
277             return NULL;
278         }
279         memset(lanainfo, 0, sizeof(LANAINFO) * (nlana+1));
280     }
281     
282     int index = 0;
283
284     // Iterate over the lana map entries and bind paths.
285     for (i = 0, pBind = bindpaths; i < nlana;
286          i++, pBind += strlen(pBind) + 1) {
287         // Ignore an invalid map entry.
288         if ((lanamap[i].flags & 1) == 0)
289             continue;
290
291                 // check for an IPv4 binding
292                 if(!strstr(pBind,"_Tcpip_"))
293                         continue;
294
295         // Find the beginning of the GUID.
296         guid = strchr(pBind, '{');
297         if (guid == NULL)
298             continue;                   // Malformed path entry?
299         guid = strdup(guid);
300         if (guid == NULL)
301             continue;
302         // Find the end of the GUID.
303         p = strchr(guid, '}');
304         if (p == NULL) {
305             free(guid);                 // Malformed GUID?
306             continue;
307         }
308         *++p = '\0';                    // Ignore anything after the GUID.
309         status = lana_GetNameFromGuid(guid, &name);
310         free(guid);
311
312         if (status == 0 && name != 0)
313         {
314             if (LanaName)
315             {
316                 if (strcmp(name, LanaName) ==0)
317                 {
318                     lanainfo[index].lana_number = lanamap[i].number;
319                     _tcscpy(lanainfo[index].lana_name, name);
320                     free(name);
321                     index++;
322                     break;
323                 }
324             }
325             else
326             {
327                 lanainfo[index].lana_number = lanamap[i].number;
328                 _tcscpy(lanainfo[index].lana_name, name);
329                 free(name);
330                 index++;
331             }
332         }
333     }
334
335     lanainfo[index].lana_number = LANA_INVALID;
336
337     free(bindpaths);
338     return lanainfo;
339 }
340
341 extern "C" lana_number_t lana_FindLoopback(void)
342 {
343     NCB ncb;
344     LANA_ENUM lana_list;
345     int status;
346     int i;
347
348     memset(&ncb, 0, sizeof(ncb));
349     ncb.ncb_command = NCBENUM;
350     ncb.ncb_buffer = (UCHAR *) &lana_list;
351     ncb.ncb_length = sizeof(lana_list);
352     status = Netbios(&ncb);
353     if (status != 0) {
354 #ifndef NOLOGGING
355         afsi_log("Netbios NCBENUM failed: status %ld", status);
356 #endif
357         return LANA_INVALID;
358     }
359     for (i = 0; i < lana_list.length; i++) {
360         if (lana_IsLoopback(lana_list.lana[i])) {
361             // Found one, return it.
362 #ifndef NOLOGGING
363             afsi_log("lana_FindLoopback: Found LAN adapter %d",
364                      lana_list.lana[i]);
365 #endif
366             return lana_list.lana[i];
367         }
368     }
369     // Could not find a loopback adapter.
370     return LANA_INVALID;
371 }
372
373 // Is the given lana a Windows Loopback Adapter?
374 // TODO: implement a better check for loopback
375 // TODO: also check for proper bindings (IPv4)
376 extern "C" BOOL lana_IsLoopback(lana_number_t lana)
377 {
378     NCB ncb;
379     struct {
380         ADAPTER_STATUS status;
381         NAME_BUFFER names[MAX_LANA+1];
382     } astat;
383     unsigned char kWLA_MAC[6] = { 0x02, 0x00, 0x4c, 0x4f, 0x4f, 0x50 };
384     int status;
385
386     // Reset the adapter: in Win32, this is required for every process, and
387     // acts as an init call, not as a real hardware reset.
388     memset(&ncb, 0, sizeof(ncb));
389     ncb.ncb_command = NCBRESET;
390     ncb.ncb_callname[0] = 100;
391     ncb.ncb_callname[2] = 100;
392     ncb.ncb_lana_num = lana;
393     status = Netbios(&ncb);
394     if (status == 0)
395         status = ncb.ncb_retcode;
396     if (status != 0) {
397 #ifndef NOLOGGING
398        afsi_log("NCBRESET failed: lana %u, status %ld", lana, status);
399 #endif
400         return FALSE;
401     }
402
403     // Use the NCBASTAT command to get the adapter address.
404     memset(&ncb, 0, sizeof(ncb));
405     ncb.ncb_command = NCBASTAT;
406     ncb.ncb_lana_num = lana;
407     strcpy((char *) ncb.ncb_callname, "*               ");
408     ncb.ncb_buffer = (UCHAR *) &astat;
409     ncb.ncb_length = sizeof(astat);
410     status = Netbios(&ncb);
411     if (status == 0)
412         status = ncb.ncb_retcode;
413     if (ncb.ncb_retcode != 0) {
414 #ifndef NOLOGGING   
415         afsi_log("NCBASTAT failed: lana %u, status %ld", lana, status);
416 #endif
417         return FALSE;
418     }
419     return (memcmp(astat.status.adapter_address, kWLA_MAC, 6) == 0);
420 }
421
422 // Get the netbios named used/to-be-used by the AFS SMB server.
423 // IF <lana specified> THEN
424 //     Use specified lana
425 // ELSE
426 //         Look for an adapter named "AFS", failing which,
427 //     look for a loopback adapter.
428 // ENDIF
429 // IF lana is for a loopback && !IsGateway THEN
430 //    IF netbios name is specified THEN
431 //       use specified netbios name
432 //    ELSE
433 //       use "AFS"
434 //    ENDIF
435 // ELSE
436 //    use netbios name "<hostname>-AFS"
437 // ENDIF
438 // Return ERROR_SUCCESS if netbios name was successfully generated.
439 // Returns the lana number to use in *pLana (if pLana is non-NULL) and also
440 //         the IsGateway setting in *pIsGateway (if pIsGateway is non-NULL).
441 //         the type of name returned.
442 //
443 // buffer is assumed to hold at least MAX_NB_NAME_LENGTH bytes.
444 //
445 // flags :
446 //        LANA_NETBIOS_NAME_IN : Use the values of *pLana and *pIsGateway as [in] parameters.
447 //        LANA_NETBIOS_NAME_SUFFIX : Only return the suffix of netbios name
448 //                LANA_NETBIOS_NAME_FULL : Return full netbios name
449 extern "C" long lana_GetUncServerNameEx(char *buffer, lana_number_t * pLana, int * pIsGateway, int flags) {
450     HKEY hkConfig;
451         DWORD dummyLen;
452         LONG rv;
453         int regLana;
454         int regGateway, regNoFindLanaByName;
455         TCHAR regNbName[MAX_NB_NAME_LENGTH];
456         TCHAR nbName[MAX_NB_NAME_LENGTH];
457         TCHAR hostname[MAX_COMPUTERNAME_LENGTH+1];
458
459         rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,szAFSConfigKeyName,0,KEY_READ,&hkConfig);
460         if(rv == ERROR_SUCCESS) {
461                 if(!(flags & LANA_NETBIOS_NAME_IN) || !pLana) {
462                         dummyLen = sizeof(regLana);
463                         rv = RegQueryValueEx(hkConfig, szLanAdapterValue, NULL, NULL, (LPBYTE) &regLana, &dummyLen);
464                         if(rv != ERROR_SUCCESS) regLana = -1;
465                 } else
466                         regLana = *pLana;
467
468                 if(!(flags & LANA_NETBIOS_NAME_IN) || !pIsGateway) {
469                         dummyLen = sizeof(regGateway);
470                         rv = RegQueryValueEx(hkConfig, szIsGatewayValue, NULL, NULL, (LPBYTE) &regGateway, &dummyLen);
471                         if(rv != ERROR_SUCCESS) regGateway = 0;
472                 } else
473                         regGateway = *pIsGateway;
474
475                 dummyLen = sizeof(regNoFindLanaByName);
476                 rv = RegQueryValueEx(hkConfig, szNoFindLanaByName, NULL, NULL, (LPBYTE) &regNoFindLanaByName, &dummyLen);
477                 if(rv != ERROR_SUCCESS) regNoFindLanaByName = 0;
478
479                 // Do not care if the call fails for insufficient buffer size.  We are not interested
480                 // in netbios names over 15 chars.
481                 dummyLen = sizeof(regNbName);
482                 rv = RegQueryValueEx(hkConfig, szNetbiosNameValue, NULL, NULL, (LPBYTE) &regNbName, &dummyLen);
483                 if(rv != ERROR_SUCCESS) regNbName[0] = 0;
484                 else regNbName[15] = 0;
485
486                 RegCloseKey(hkConfig);
487         } else {
488                 if(flags & LANA_NETBIOS_NAME_IN) {
489                         regLana = (pLana)? *pLana: -1;
490                         regGateway = (pIsGateway)? *pIsGateway: 0;
491                 } else {
492                         regLana = -1;
493                         regGateway = 0;
494                 }
495         regNoFindLanaByName = 0;
496                 regNbName[0] = 0;
497         }
498
499     if(regLana < 0 || regLana > MAX_LANA) 
500         regLana = -1;
501
502         if(regLana == -1) {
503                 LANAINFO *lanaInfo = NULL;
504         int nLana = LANA_INVALID;
505
506         if (!regNoFindLanaByName)
507             lanaInfo = lana_FindLanaByName("AFS");
508                 if(lanaInfo != NULL) {
509             nLana = lanaInfo[0].lana_number;
510                         free(lanaInfo);
511                 } else
512                         nLana = LANA_INVALID;
513
514                 if(nLana == LANA_INVALID && !regGateway) {
515                         nLana = lana_FindLoopback();
516                 }
517                 if(nLana != LANA_INVALID) 
518             regLana = nLana;
519         }
520
521         if(rebNbName[0] &&
522        (regLana >=0 && lana_IsLoopback((lana_number_t) regLana))) {
523         strncpy(nbName,regNbName,15);
524         nbName[16] = 0;
525         strupr(nbName);
526         } else {
527                 char * dot;
528
529                 if(flags & LANA_NETBIOS_NAME_SUFFIX) {
530                         strcpy(nbName,"-AFS");
531                 } else {
532                         dummyLen = sizeof(hostname);
533                         // assume we are not a cluster.
534                         rv = GetComputerName(hostname, &dummyLen);
535                         if(!SUCCEEDED(rv)) { // should not happen, but...
536                                 return rv;
537                         }
538                         strncpy(nbName, hostname, 11);
539                         nbName[11] = 0;
540                         if(dot = strchr(nbName,'.'))
541                                 *dot = 0;
542                         strcat(nbName,"-AFS");
543                 }
544         }
545
546         if(pLana) *pLana = regLana;
547         if(pIsGateway) *pIsGateway = regGateway;
548
549         strcpy(buffer, nbName);
550
551         return ERROR_SUCCESS;
552 }
553
554 extern "C" void lana_GetUncServerNameDynamic(int lanaNumber, BOOL isGateway, TCHAR *name, int type) {
555         char szName[MAX_NB_NAME_LENGTH];
556         lana_number_t lana = (lana_number_t) lanaNumber;
557         int gateway = (int) isGateway;
558
559         if(SUCCEEDED(lana_GetUncServerNameEx(szName, &lana, &gateway, LANA_NETBIOS_NAME_IN | type))) {
560 #ifdef _UNICODE
561                 mbswcs(name,szName,MAX_NB_NAME_LENGTH);
562 #else
563                 strncpy(name,szName,MAX_NB_NAME_LENGTH);
564 #endif
565         } else
566                 *name = _T('\0');
567 }
568
569 extern "C" void lana_GetUncServerName(TCHAR *name, int type) {
570         char szName[MAX_NB_NAME_LENGTH];
571
572         if(SUCCEEDED(lana_GetUncServerNameEx(szName,NULL,NULL,type))) {
573 #ifdef _UNICODE
574                 mbswcs(name,szName,MAX_NB_NAME_LENGTH);
575 #else
576                 strncpy(name,szName,MAX_NB_NAME_LENGTH);
577 #endif
578         } else {
579         *name = _T('\0');
580         }
581 }
582
583 extern "C" void lana_GetAfsNameString(int lanaNumber, BOOL isGateway, TCHAR* name)
584 {
585     TCHAR netbiosName[32];
586     lana_GetUncServerNameDynamic(lanaNumber, isGateway, netbiosName, LANA_NETBIOS_NAME_FULL);
587     _stprintf(name, _T("Your UNC name to reach the root of AFS is \\\\%s\\all"), netbiosName);
588 }
589
590 extern "C" void lana_GetNetbiosName(LPTSTR pszName, int type)
591 {
592         HKEY hkCfg;
593     TCHAR name[MAX_NB_NAME_LENGTH];
594         DWORD dummyLen;
595
596     memset(name, 0, sizeof(name));
597     if (GetVersion() >= 0x80000000) // not WindowsNT
598     {
599         if (type == LANA_NETBIOS_NAME_SUFFIX)
600         {
601             _tcscpy(pszName, TEXT("-afs"));
602             return;
603         }
604
605                 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,szAFSConfigKeyName,0,KEY_READ,&hkCfg) == ERROR_SUCCESS) {
606                         dummyLen = sizeof(name);
607                         if(RegQueryValueEx(hkCfg,TEXT("Gateway"),NULL,NULL,(LPBYTE) name,&dummyLen) == ERROR_SUCCESS)
608                                 name[0] = _T('\0');
609                         RegCloseKey(hkCfg);
610                 }
611
612         if (_tcslen(name) == 0)
613         {
614             _tcscpy(pszName, TEXT("unknown"));
615             return;
616         }
617
618                 _tcscpy(pszName, name);
619         _tcscat(pszName, TEXT("-afs"));
620         return;
621     }
622
623     lana_GetUncServerName(name,type);
624         _tcslwr(name);
625     _tcscpy(pszName, name);
626     return;
627 }
628