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