7fd86019343374e1bfac6e7a2d810770f9397a34
[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 <shlwapi.h>
10 #include <wtypes.h>
11 #include <string.h>
12 #include <malloc.h>
13 #include "lanahelper.h"
14
15
16 extern "C" void afsi_log(...);
17
18 static HRESULT getname_shellfolder(WCHAR *wGuid, WCHAR *wName, int NameSize)
19 {
20     // This is the GUID for the network connections folder. It is constant.
21     // {7007ACC7-3202-11D1-AAD2-00805FC1270E}
22     const GUID CLSID_NetworkConnections = {
23         0x7007ACC7, 0x3202, 0x11D1, {
24             0xAA, 0xD2, 0x00, 0x80, 0x5F, 0xC1, 0x27, 0x0E
25         }
26     };
27     LPITEMIDLIST pidl;
28     IShellFolder *pShellFolder;
29     IMalloc *pShellMalloc;
30
31     // Build the display name in the form "::{GUID}".
32     if (wcslen(wGuid) >= MAX_PATH)
33         return E_INVALIDARG;
34     WCHAR szAdapterGuid[MAX_PATH + 2];
35     swprintf(szAdapterGuid, L"::%ls", wGuid);
36
37     // Initialize COM.
38     CoInitialize(NULL);
39
40     // Get the shell allocator.
41     HRESULT hr = SHGetMalloc(&pShellMalloc);
42     if (SUCCEEDED(hr))
43         // Create an instance of the network connections folder.
44         hr = CoCreateInstance(CLSID_NetworkConnections, NULL,
45                               CLSCTX_INPROC_SERVER, IID_IShellFolder,
46                               reinterpret_cast<LPVOID *>(&pShellFolder));
47     if (SUCCEEDED(hr))
48         hr = pShellFolder->ParseDisplayName(NULL, NULL, szAdapterGuid, NULL,
49                                             &pidl, NULL);
50     if (SUCCEEDED(hr)) {
51         // Get the display name; this returns the friendly name.
52         STRRET sName;
53         hr = pShellFolder->GetDisplayNameOf(pidl, SHGDN_NORMAL, &sName);
54         if (SUCCEEDED(hr))
55             wcsncpy(wName, sName.pOleStr, NameSize);
56         pShellMalloc->Free(pidl);
57     }
58
59     CoUninitialize();
60     return hr;
61 }
62
63 // Get the Connection Name for the given GUID.
64 extern "C" int lana_GetNameFromGuid(char *Guid, char **Name)
65 {
66     typedef HRESULT (WINAPI *HrLanProcAddr)(GUID *, PCWSTR, PWSTR, LPDWORD);
67     HrLanProcAddr HrLanProc = NULL;
68     HMODULE hNetMan;
69     int size;
70     WCHAR *wGuid = NULL;
71     WCHAR wName[MAX_PATH];
72     DWORD NameSize = MAX_PATH;
73     char *name = NULL;
74     HRESULT status;
75         
76     // Convert the Guid string to Unicode.  First we ask only for the size
77     // of the converted string.  Then we allocate a buffer of sufficient
78     // size to hold the result of the conversion.
79     size = MultiByteToWideChar(CP_ACP, 0, Guid, -1, NULL, 0);
80     wGuid = (WCHAR *) malloc(size * sizeof(WCHAR));
81     MultiByteToWideChar(CP_ACP, 0, Guid, -1, wGuid, size);
82
83     // First try the IShellFolder interface, which was unimplemented
84     // for the network connections folder before XP.
85
86     /* XXX pbh 9/11/03 - revert to using the undocumented APIs on XP while
87      *   waiting to hear back from PSS about the slow reboot issue.
88      *   This is an ugly, misleading hack, but is minimally invasive
89      *   and will be easy to rollback.
90      */
91
92     //status = getname_shellfolder(wGuid, wName, NameSize);
93     status = E_NOTIMPL;
94
95     /* XXX end of pbh 9/11/03 temporary hack*/  
96
97     if (status == E_NOTIMPL) {
98         // The IShellFolder interface is not implemented on this platform.
99         // Try the (undocumented) HrLanConnectionNameFromGuidOrPath API
100         // from the netman DLL.
101         afsi_log("IShellFolder API not implemented, trying HrLanConnectionNameFromGuidOrPath");
102         hNetMan = LoadLibrary("netman.dll");
103         if (hNetMan == NULL) {
104             free(wGuid);
105             return -1;
106         }
107         HrLanProc = (HrLanProcAddr) GetProcAddress(hNetMan,
108                                                    "HrLanConnectionNameFromGuidOrPath");
109         if (HrLanProc == NULL) {
110             FreeLibrary(hNetMan);
111             free(wGuid);
112             return -1;
113         }
114         status = HrLanProc(NULL, wGuid, wName, &NameSize);
115         FreeLibrary(hNetMan);
116     }
117     free(wGuid);
118     if (FAILED(status)) {
119         afsi_log("lana_GetNameFromGuid: failed to get connection name (status %ld)",
120                  status);
121         return -1;
122     }
123
124     // Get the required buffer size, and then convert the string.
125     size = WideCharToMultiByte(CP_ACP, 0, wName, -1, NULL, 0, NULL, NULL);
126     name = (char *) malloc(size);
127     if (name == NULL)
128         return -1;
129     WideCharToMultiByte(CP_ACP, 0, wName, -1, name, size, NULL, NULL);
130     afsi_log("Connection name for %s is '%s'", Guid, name);
131     if (*Name)
132         *Name = name;
133     else
134         free(name);
135     return 0;
136 }
137
138 // Find the lana number for the given connection name.
139 extern "C" lana_number_t lana_FindLanaByName(const char *LanaName)
140 {
141     const char RegNetBiosLinkageKeyName[] =
142         "System\\CurrentControlSet\\Services\\NetBios\\Linkage";
143     HKEY hkey;
144     LONG status;
145     struct {
146         BYTE flags;
147         BYTE number;
148     } lanamap[MAX_LANA+1];
149     DWORD lanamapsize = sizeof(lanamap);
150     DWORD type;
151     char *bindpaths = NULL;
152     DWORD bindpathsize;
153     int nlana;
154     int i;
155     char *guid;
156     char *name;
157     char *pBind;
158     char *p;
159
160     // Open the NetBios Linkage key.
161     status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegNetBiosLinkageKeyName, 0, 
162                           KEY_QUERY_VALUE, &hkey);
163         
164     if (status != ERROR_SUCCESS) { 
165         afsi_log("Failed to open NetBios Linkage key (status %ld)", status);
166         return LANA_INVALID;
167     }
168
169     // Read the lana map.
170     status = RegQueryValueEx(hkey, "LanaMap", 0, &type,
171                          (BYTE *) &lanamap, &lanamapsize);
172     if (status != ERROR_SUCCESS) {
173         afsi_log("Failed to read LanaMap (status %ld)", status);
174         RegCloseKey(hkey);
175         return LANA_INVALID;
176     }
177     if (lanamapsize == 0) {
178         afsi_log("No data in LanaMap");
179         return LANA_INVALID;
180     }
181     nlana = lanamapsize / sizeof(lanamap[0]);
182
183     // Get the bind paths for NetBios so we can match them up
184     // with the lana map.  First we query for the size, so we
185     // can allocate an appropriate buffer.
186     status = RegQueryValueEx(hkey, "Bind", 0, &type, NULL, &bindpathsize);
187     if (status == ERROR_SUCCESS && bindpathsize != 0) {
188         bindpaths = (char *) malloc(bindpathsize * sizeof(char));
189         if (bindpaths == NULL) {
190             afsi_log("Cannot allocate %ld bytes for bindpaths", bindpathsize);
191             RegCloseKey(hkey);
192             return LANA_INVALID;
193         }
194         status = RegQueryValueEx(hkey, "Bind", 0, &type, 
195                                  (BYTE *) bindpaths, &bindpathsize);
196     }
197     RegCloseKey(hkey);
198     if (status != ERROR_SUCCESS) {
199         afsi_log("Failed to read bind paths (status %ld)", status);
200         if (bindpaths != NULL)
201             free(bindpaths);
202         return LANA_INVALID;
203       }
204     if (bindpathsize == 0) {
205         afsi_log("No bindpath data");
206         if (bindpaths != NULL)
207             free(bindpaths);
208         return LANA_INVALID;
209     }
210
211     // Iterate over the lana map entries and bind paths.
212     for (i = 0, pBind = bindpaths; i < nlana;
213          i++, pBind += strlen(pBind) + 1) {
214         // Ignore an invalid map entry.
215         if ((lanamap[i].flags & 1) == 0)
216             continue;
217
218                 // check for a IPv4 binding
219                 if(!strstr(pBind,"_Tcpip_"))
220                         continue;
221
222         // Find the beginning of the GUID.
223         guid = strchr(pBind, '{');
224         if (guid == NULL)
225             continue;                   // Malformed path entry?
226         guid = strdup(guid);
227         if (guid == NULL)
228             continue;
229         // Find the end of the GUID.
230         p = strchr(guid, '}');
231         if (p == NULL) {
232             free(guid);                 // Malformed GUID?
233             continue;
234         }
235         *++p = '\0';                    // Ignore anything after the GUID.
236         status = lana_GetNameFromGuid(guid, &name);
237         free(guid);
238         if (status == 0 && name != 0) {
239             status = strcmp(name, LanaName);
240             free(name);
241             if (status == 0) {
242                 free(bindpaths);
243                 afsi_log("lana_FindLanaByName: Found lana %d for %s",
244                          lanamap[i].number, LanaName);
245                 return lanamap[i].number;
246             }
247         }
248     }
249     free(bindpaths);
250     return LANA_INVALID;
251 }
252
253 extern "C" lana_number_t lana_FindLoopback(void)
254 {
255     NCB ncb;
256     LANA_ENUM lana_list;
257     int status;
258     int i;
259
260     memset(&ncb, 0, sizeof(ncb));
261     ncb.ncb_command = NCBENUM;
262     ncb.ncb_buffer = (UCHAR *) &lana_list;
263     ncb.ncb_length = sizeof(lana_list);
264     status = Netbios(&ncb);
265     if (status != 0) {
266         afsi_log("Netbios NCBENUM failed: status %ld", status);
267         return LANA_INVALID;
268     }
269     for (i = 0; i < lana_list.length; i++) {
270         if (lana_IsLoopback(lana_list.lana[i])) {
271             // Found one, return it.
272             afsi_log("lana_FindLoopback: Found LAN adapter %d",
273                      lana_list.lana[i]);
274             return lana_list.lana[i];
275         }
276     }
277     // Could not find a loopback adapter.
278     return LANA_INVALID;
279 }
280
281 // Is the given lana a Windows Loopback Adapter?
282 extern "C" BOOL lana_IsLoopback(lana_number_t lana)
283 {
284     NCB ncb;
285     struct {
286         ADAPTER_STATUS status;
287         NAME_BUFFER names[MAX_LANA+1];
288     } astat;
289     unsigned char kWLA_MAC[6] = { 0x02, 0x00, 0x4c, 0x4f, 0x4f, 0x50 };
290     int status;
291
292     // Reset the adapter: in Win32, this is required for every process, and
293     // acts as an init call, not as a real hardware reset.
294     memset(&ncb, 0, sizeof(ncb));
295     ncb.ncb_command = NCBRESET;
296     ncb.ncb_callname[0] = 100;
297     ncb.ncb_callname[2] = 100;
298     ncb.ncb_lana_num = lana;
299     status = Netbios(&ncb);
300     if (status == 0)
301         status = ncb.ncb_retcode;
302     if (status != 0) {
303         afsi_log("NCBRESET failed: lana %u, status %ld", lana, status);
304         return FALSE;
305     }
306
307     // Use the NCBASTAT command to get the adapter address.
308     memset(&ncb, 0, sizeof(ncb));
309     ncb.ncb_command = NCBASTAT;
310     ncb.ncb_lana_num = lana;
311     strcpy((char *) ncb.ncb_callname, "*               ");
312     ncb.ncb_buffer = (UCHAR *) &astat;
313     ncb.ncb_length = sizeof(astat);
314     status = Netbios(&ncb);
315     if (status == 0)
316         status = ncb.ncb_retcode;
317     if (ncb.ncb_retcode != 0) {
318         afsi_log("NCBASTAT failed: lana %u, status %ld", lana, status);
319         return FALSE;
320     }
321     return (memcmp(astat.status.adapter_address, kWLA_MAC, 6) == 0);
322 }