nbname-instead-of-mountroot-20040227
[openafs.git] / src / WINNT / client_config / getlana.cpp
1 // getlana.cpp : Defines the entry point for the console application.
2 //
3
4 #include <afx.h>
5 #include <windows.h>
6 #include <winreg.h>
7 #include <nb30.h>
8 #include <tchar.h>
9 #include <shellapi.h>
10 #include <iostream>
11 #include <objbase.h>
12 #include <shlobj.h>
13 #include <shlwapi.h>
14 #include <wtypes.h>
15 #include <string.h>
16 #include <malloc.h>
17 #include <winsock2.h>
18 #include <getlana.h>
19
20
21
22 #define LANA_INVALID 0xff
23 using namespace std;
24 BOOL lana_IsLoopback(lana_number_t lana, TCHAR*);
25 lana_number_t lana_FindLoopback(TCHAR*);
26
27
28
29 // Use the IShellFolder API to get the connection name for the given Guid.
30 static HRESULT getname_shellfolder(WCHAR *wGuid, WCHAR *wName, int NameSize)
31 {
32     // This is the GUID for the network connections folder. It is constant.
33     // {7007ACC7-3202-11D1-AAD2-00805FC1270E}
34     const GUID CLSID_NetworkConnections = {
35         0x7007ACC7, 0x3202, 0x11D1, {
36             0xAA, 0xD2, 0x00, 0x80, 0x5F, 0xC1, 0x27, 0x0E
37         }
38     };
39     LPITEMIDLIST pidl;
40     IShellFolder *pShellFolder;
41     IMalloc *pShellMalloc;
42
43     // Build the display name in the form "::{GUID}".
44     if (wcslen(wGuid) >= MAX_PATH)
45         return E_INVALIDARG;
46     WCHAR szAdapterGuid[MAX_PATH + 2];
47     swprintf(szAdapterGuid, L"::%ls", wGuid);
48
49     // Initialize COM.
50     CoInitialize(NULL);
51
52     // Get the shell allocator.
53     HRESULT hr = SHGetMalloc(&pShellMalloc);
54     if (SUCCEEDED(hr))
55       {
56         // Create an instance of the network connections folder.
57         hr = CoCreateInstance(CLSID_NetworkConnections, NULL,
58                               CLSCTX_INPROC_SERVER, IID_IShellFolder,
59                               reinterpret_cast<LPVOID *>(&pShellFolder));
60       }
61     if (SUCCEEDED(hr))
62         hr = pShellFolder->ParseDisplayName(NULL, NULL, szAdapterGuid, NULL,
63                                             &pidl, NULL);
64     if (SUCCEEDED(hr))
65       {
66         // Get the display name; this returns the friendly name.
67         STRRET sName;
68         hr = pShellFolder->GetDisplayNameOf(pidl, SHGDN_NORMAL, &sName);
69         if (SUCCEEDED(hr))
70             wcsncpy(wName, sName.pOleStr, NameSize);
71         pShellMalloc->Free(pidl);
72       }
73
74     CoUninitialize();
75     return hr;
76 }
77
78 // Get the Connection Name for the given GUID.
79 static int lana_GetNameFromGuid(char *Guid, char **Name)
80 {
81     typedef HRESULT (WINAPI *HrLanProcAddr)(GUID *, PCWSTR, PWSTR, LPDWORD);
82     HrLanProcAddr HrLanProc = NULL;
83     HMODULE hNetMan;
84     int size;
85     WCHAR *wGuid = NULL;
86     WCHAR wName[MAX_PATH];
87     DWORD NameSize = (sizeof(wName) / sizeof(wName[0]));
88     HRESULT status;
89         
90     // Convert the Guid string to Unicode.  First we ask only for the size
91     // of the converted string.  Then we allocate a buffer of sufficient
92     // size to hold the result of the conversion.
93     size = MultiByteToWideChar(CP_ACP, 0, Guid, -1, NULL, 0);
94     wGuid = (WCHAR *) malloc(size * sizeof(WCHAR));
95     MultiByteToWideChar(CP_ACP, 0, Guid, -1, wGuid, size);
96
97     // First try the IShellFolder interface, which was unimplemented
98     // for the network connections folder before XP.
99     status = getname_shellfolder(wGuid, wName, NameSize);
100     if (status == E_NOTIMPL)
101       {
102         // The IShellFolder interface is not implemented on this platform.
103         // Try the (undocumented) HrLanConnectionNameFromGuidOrPath API
104         // from the netman DLL.
105         hNetMan = LoadLibrary("netman.dll");
106         if (hNetMan == NULL)
107           {
108             free(wGuid);
109             return -1;
110           }
111         HrLanProc =
112           (HrLanProcAddr) GetProcAddress(hNetMan,
113                                         "HrLanConnectionNameFromGuidOrPath");
114         if (HrLanProc == NULL)
115           {
116             FreeLibrary(hNetMan);
117             free(wGuid);
118             return -1;
119           }
120         // Super Secret Microsoft Call
121         status = HrLanProc(NULL, wGuid, wName, &NameSize);
122         FreeLibrary(hNetMan);
123     }
124     free(wGuid);
125     if (FAILED(status))
126       {
127         cerr << "lana_GetNameFromGuid: failed to get connection name (status "
128              << status << ")\r\n";
129         return -1;
130       }
131
132     // Get the required buffer size, and then convert the string.
133     size = WideCharToMultiByte(CP_ACP, 0, wName, -1, NULL, 0, NULL, NULL);
134     *Name = (char *) malloc(size);
135     if (*Name == NULL)
136         return -1;
137     WideCharToMultiByte(CP_ACP, 0, wName, -1, *Name, size, NULL, NULL);
138     return 0;
139 }
140
141 LANAINFO* GetLana(TCHAR* msg, const char *LanaName)
142 {
143     const char RegNetBiosLinkageKeyName[] =
144         "System\\CurrentControlSet\\Services\\NetBios\\Linkage";
145     HKEY hkey;
146     LONG status;
147     struct {
148         BYTE flags;
149         BYTE number;
150     } lanamap[MAX_LANA+1];
151     DWORD lanamapsize = sizeof(lanamap);
152     DWORD type;
153     char *bindpaths = NULL;
154     DWORD bindpathsize;
155     int nlana;
156     int i;
157     char *guid;
158     char *name;
159     char *pBind;
160     char *p;
161
162     LANAINFO* lanainfo;
163
164     // Open the NetBios Linkage key.
165     status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegNetBiosLinkageKeyName, 0, 
166                           KEY_QUERY_VALUE, &hkey);
167         
168     if (status != ERROR_SUCCESS)
169       { 
170               _stprintf(msg, _T("Failed to open NetBios Linkage key (status %d)"),  status);
171         return NULL;
172       }
173
174     // Read the lana map.
175     status = RegQueryValueEx(hkey, "LanaMap", 0, &type,
176                          (BYTE *) &lanamap, &lanamapsize);
177     if (status != ERROR_SUCCESS)
178       {
179         _stprintf(msg, _T("Failed to read LanaMap (status %d)"), status);
180         RegCloseKey(hkey);
181         return NULL;
182       }
183     if (lanamapsize == 0)
184       {
185         _stprintf(msg, _T("No data in LanaMap"));
186         return NULL;
187       }
188     nlana = lanamapsize / sizeof(lanamap[0]);
189
190     // Get the bind paths for NetBios so we can match them up
191     // with the lana map.  First we query for the size, so we
192     // can allocate an appropriate buffer.
193     status = RegQueryValueEx(hkey, "Bind", 0, &type, NULL, &bindpathsize);
194     if (status == ERROR_SUCCESS && bindpathsize != 0)
195       {
196         bindpaths = (char *) malloc(bindpathsize * sizeof(char));
197         if (bindpaths == NULL)
198           {
199             _stprintf(msg, _T("Cannot allocate %d bytes for bindpaths"), bindpathsize);
200                 
201             RegCloseKey(hkey);
202             return NULL;
203           }
204         status = RegQueryValueEx(hkey, "Bind", 0, &type, 
205                                  (BYTE *) bindpaths, &bindpathsize);
206       }
207     RegCloseKey(hkey);
208     if (status != ERROR_SUCCESS)
209       {
210         _stprintf(msg, _T("Failed to read bind paths (status %d)"), status); 
211         if (bindpaths != NULL)
212             free(bindpaths);
213         return NULL;
214       }
215     if (bindpathsize == 0)
216       {
217         _stprintf(msg,  _T("No bindpath data"));
218         if (bindpaths != NULL)
219             free(bindpaths);
220         return NULL;
221     }
222
223     if (LanaName)
224     {
225       lanainfo = new LANAINFO[1];
226       lanainfo[0].lana_number = LANA_INVALID;
227       memset(lanainfo[0].lana_name, 0, sizeof(lanainfo[0].lana_name));
228     }
229     else
230     {
231       lanainfo = new LANAINFO[nlana+1];
232       memset(lanainfo, 0, sizeof(LANAINFO) * nlana);
233     }
234     int index = 0;
235     for (i = 0, pBind = bindpaths; i < nlana;
236          i++, pBind += strlen(pBind) + 1)
237       {
238         // Ignore an invalid map entry.
239         if ((lanamap[i].flags & 1) == 0)
240             continue;
241         // Find the beginning of the GUID.
242         guid = strchr(pBind, '{');
243         if (guid == NULL)
244             continue;                   // Malformed path entry?
245         guid = strdup(guid);
246         if (guid == NULL)
247             continue;
248         // Find the end of the GUID.
249         p = strchr(guid, '}');
250         if (p == NULL)
251           {
252             free(guid);                 // Malformed GUID?
253             continue;
254           }
255         *++p = '\0';                    // Ignore anything after the GUID.
256         status = lana_GetNameFromGuid(guid, &name);
257         if (status == 0)
258         {
259           if (LanaName)
260           {
261             if (strcmp(name, LanaName) ==0)
262             {
263               lanainfo[index].lana_number = lanamap[i].number;
264               _tcscpy(lanainfo[index].lana_name ,name);
265               free(name);
266               free(guid);
267               break;
268             }
269           }
270           else
271           {
272             lanainfo[index].lana_number = lanamap[i].number;
273             _tcscpy(lanainfo[index].lana_name ,name);
274             free(name);
275             index++;
276           }
277         }
278
279         free(guid);
280       }
281     free(bindpaths);
282     return lanainfo;
283
284
285
286 lana_number_t lana_FindLoopback(TCHAR* msg)
287 {
288     NCB ncb;
289     LANA_ENUM lana_list;
290     int status;
291     int i;
292
293     memset(&ncb, 0, sizeof(ncb));
294     ncb.ncb_command = NCBENUM;
295     ncb.ncb_buffer = (UCHAR *) &lana_list;
296     ncb.ncb_length = sizeof(lana_list);
297     status = Netbios(&ncb);
298     if (status != 0) {
299             _stprintf(msg, _T("Netbios NCBENUM failed: status %ld"), status);
300             return LANA_INVALID;
301     }
302     
303     for (i = 0; i < lana_list.length; i++) {
304             if (lana_IsLoopback(lana_list.lana[i], msg)) {
305             // Found one, return it.
306               return lana_list.lana[i];
307       }
308     }
309     // Could not find a loopback adapter.
310     return LANA_INVALID;
311 }
312
313
314 // Is the given lana a Windows Loopback Adapter?
315 BOOL lana_IsLoopback(lana_number_t lana, TCHAR* msg)
316 {
317     NCB ncb;
318     struct {
319         ADAPTER_STATUS status;
320         NAME_BUFFER names[MAX_LANA+1];
321     } astat;
322     unsigned char kWLA_MAC[6] = { 0x02, 0x00, 0x4c, 0x4f, 0x4f, 0x50 };
323     int status;
324
325     // Reset the adapter: in Win32, this is required for every process, and
326     // acts as an init call, not as a real hardware reset.
327     memset(&ncb, 0, sizeof(ncb));
328     ncb.ncb_command = NCBRESET;
329     ncb.ncb_callname[0] = 100;
330     ncb.ncb_callname[2] = 100;
331     ncb.ncb_lana_num = lana;
332     status = Netbios(&ncb);
333     if (status == 0)
334         status = ncb.ncb_retcode;
335     if (status != 0) {
336             sprintf(msg, "NCBRESET failed: lana %u, status %ld", lana, status);
337             return FALSE;
338     }
339
340     // Use the NCBASTAT command to get the adapter address.
341     memset(&ncb, 0, sizeof(ncb));
342     ncb.ncb_command = NCBASTAT;
343     ncb.ncb_lana_num = lana;
344     strcpy((char *) ncb.ncb_callname, "*               ");
345     ncb.ncb_buffer = (UCHAR *) &astat;
346     ncb.ncb_length = sizeof(astat);
347     status = Netbios(&ncb);
348     if (status == 0)
349         status = ncb.ncb_retcode;
350     if (ncb.ncb_retcode != 0) {
351         sprintf(msg, "NCBASTAT failed: lana %u, status %ld", lana, status);
352               return FALSE;
353     }
354     return (memcmp(astat.status.adapter_address, kWLA_MAC, 6) == 0);
355 }
356
357 #define NETBIOS_NAME_FULL 0
358 #define NETBIOS_NAME_SUFFIX 1
359 #define AFSCONFIGKEYNAME TEXT("SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon\\Parameters")
360
361 void GetUncServerName(int lanaNumber, BOOL isGateway, TCHAR* name, int type)
362 {
363     lana_number_t lana = LANA_INVALID;
364     LANAINFO* lanainfo;
365     WSADATA WSAjunk;
366     char cm_HostName[MAX_PATH];
367     TCHAR tmpName[MAX_PATH];
368     TCHAR msg[MAX_PATH];
369     memset(msg, 0, sizeof(msg));
370     memset(tmpName, 0, sizeof(tmpName));
371     memset(cm_HostName, 0, sizeof(cm_HostName));
372         WSAStartup(0x0101, &WSAjunk);
373
374     if (lanaNumber == -1) {
375                 /* Find the default LAN adapter to use.  First look for
376          * the adapter named AFS; otherwise, unless we are doing
377                  * gateway service, look for any valid loopback adapter.
378                  */
379                 lanainfo = GetLana(msg, "AFS");
380         if (lanainfo)
381         {
382             lana = lanainfo[0].lana_number;
383             delete lanainfo;
384         }
385                 if (lana == LANA_INVALID && !isGateway)
386                         lana = lana_FindLoopback(msg);
387                 if (lana != LANA_INVALID)
388             lanaNumber = lana;
389         }
390         /* If we are using a loopback adapter, we can use the preferred
391      * (but non-unique) server name; otherwise, we must fall back to
392          * the <machine>-AFS name.
393      */
394     if (lanaNumber >= 0 && lana_IsLoopback(lanaNumber, msg))
395     {
396         HKEY parmKey;
397         char explicitNetbiosName[MAX_PATH+1];
398         DWORD len=sizeof(explicitNetbiosName)-1;
399         if ((RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSCONFIGKEYNAME,0, KEY_QUERY_VALUE, &parmKey)!= ERROR_SUCCESS) 
400              || (RegQueryValueEx(parmKey, "NetbiosName", NULL, NULL,(LPBYTE)(explicitNetbiosName), &len)!= ERROR_SUCCESS)
401              || (len > sizeof(explicitNetbiosName)-1)
402              ) 
403             strcpy(explicitNetbiosName, "AFS"); 
404         RegCloseKey(parmKey);
405         explicitNetbiosName[len]=0;       /*safety see ms-help://MS.MSDNQTR.2002OCT.1033/sysinfo/base/regqueryvalueex.htm*/
406         _tcscpy(name, explicitNetbiosName);
407     } else {
408         gethostname(cm_HostName, sizeof(cm_HostName));
409                 _tcscpy(tmpName, cm_HostName);
410                 char* ctemp = _tcschr(tmpName, '.');    /* turn ntdfs.* into ntdfs */
411                 if (ctemp) *ctemp = 0;
412                 tmpName[11] = 0; /* ensure that even after adding the -A, we
413                           * leave one byte free for the netbios server
414                           * type.
415                           */
416         if (type == NETBIOS_NAME_FULL) {
417                     _tcscat(tmpName, _T("-afs"));
418             _tcscpy(name, tmpName);
419         } else {
420             _tcscpy(name, _T("-afs"));
421         }
422         }
423 }
424
425 void GetAfsName(int lanaNumber, BOOL isGateway, TCHAR* name)
426 {
427
428     GetUncServerName(lanaNumber, isGateway, name, NETBIOS_NAME_FULL);
429     _stprintf(name, _T("Your UNC name to reach the root of AFS is \\\\%s\\all"), name);
430 }
431