eaeb323d672d9e48f5a611c1e114db50b3bd43a6
[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 = (sizeof(wName) / sizeof(wName[0]));
73     HRESULT status;
74         
75     // Convert the Guid string to Unicode.  First we ask only for the size
76     // of the converted string.  Then we allocate a buffer of sufficient
77     // size to hold the result of the conversion.
78     size = MultiByteToWideChar(CP_ACP, 0, Guid, -1, NULL, 0);
79     wGuid = (WCHAR *) malloc(size * sizeof(WCHAR));
80     MultiByteToWideChar(CP_ACP, 0, Guid, -1, wGuid, size);
81
82     // First try the IShellFolder interface, which was unimplemented
83     // for the network connections folder before XP.
84
85     /* XXX pbh 9/11/03 - revert to using the undocumented APIs on XP while
86      *   waiting tohear back from PSS about the slow reboot issue.
87      *   This is an ungly, misleading hack, but is minimally invasive
88      *   and will be easy to rollback.
89      */
90
91     //status = getname_shellfolder(wGuid, wName, NameSize);
92       status = E_NOTIMPL;
93
94     /* XXX end of pbh 9/11/03 temporary hack*/  
95
96     if (status == E_NOTIMPL) {
97         // The IShellFolder interface is not implemented on this platform.
98         // Try the (undocumented) HrLanConnectionNameFromGuidOrPath API
99         // from the netman DLL.
100         afsi_log("IShellFolder API not implemented, trying HrLanConnectionNameFromGuidOrPath");
101         hNetMan = LoadLibrary("netman.dll");
102         if (hNetMan == NULL) {
103             free(wGuid);
104             return -1;
105         }
106         HrLanProc =
107           (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     return 0;
132 }
133
134 // Find the lana number for the given connection name.
135 extern "C" lana_number_t lana_FindLanaByName(const char *LanaName)
136 {
137     const char RegNetBiosLinkageKeyName[] =
138         "System\\CurrentControlSet\\Services\\NetBios\\Linkage";
139     HKEY hkey;
140     LONG status;
141     struct {
142         BYTE flags;
143         BYTE number;
144     } lanamap[MAX_LANA+1];
145     DWORD lanamapsize = sizeof(lanamap);
146     DWORD type;
147     char *bindpaths = NULL;
148     DWORD bindpathsize;
149     int nlana;
150     int i;
151     char *guid;
152     char *name;
153     char *pBind;
154     char *p;
155
156     // Open the NetBios Linkage key.
157     status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegNetBiosLinkageKeyName, 0, 
158                           KEY_QUERY_VALUE, &hkey);
159         
160     if (status != ERROR_SUCCESS) { 
161         afsi_log("Failed to open NetBios Linkage key (status %ld)", status);
162         return LANA_INVALID;
163     }
164
165     // Read the lana map.
166     status = RegQueryValueEx(hkey, "LanaMap", 0, &type,
167                          (BYTE *) &lanamap, &lanamapsize);
168     if (status != ERROR_SUCCESS) {
169         afsi_log("Failed to read LanaMap (status %ld)", status);
170         RegCloseKey(hkey);
171         return LANA_INVALID;
172     }
173     if (lanamapsize == 0) {
174         afsi_log("No data in LanaMap");
175         return LANA_INVALID;
176     }
177     nlana = lanamapsize / sizeof(lanamap[0]);
178
179     // Get the bind paths for NetBios so we can match them up
180     // with the lana map.  First we query for the size, so we
181     // can allocate an appropriate buffer.
182     status = RegQueryValueEx(hkey, "Bind", 0, &type, NULL, &bindpathsize);
183     if (status == ERROR_SUCCESS && bindpathsize != 0) {
184         bindpaths = (char *) malloc(bindpathsize * sizeof(char));
185         if (bindpaths == NULL) {
186             afsi_log("Cannot allocate %ld bytes for bindpaths", bindpathsize);
187             RegCloseKey(hkey);
188             return LANA_INVALID;
189         }
190         status = RegQueryValueEx(hkey, "Bind", 0, &type, 
191                                  (BYTE *) bindpaths, &bindpathsize);
192     }
193     RegCloseKey(hkey);
194     if (status != ERROR_SUCCESS) {
195         afsi_log("Failed to read bind paths (status %ld)", status);
196         if (bindpaths != NULL)
197             free(bindpaths);
198         return LANA_INVALID;
199       }
200     if (bindpathsize == 0) {
201         afsi_log("No bindpath data");
202         if (bindpaths != NULL)
203             free(bindpaths);
204         return LANA_INVALID;
205     }
206
207     // Iterate over the lana map entries and bind paths.
208     for (i = 0, pBind = bindpaths; i < nlana;
209          i++, pBind += strlen(pBind) + 1) {
210         // Ignore an invalid map entry.
211         if ((lanamap[i].flags & 1) == 0)
212             continue;
213
214                 // check for a IPv4 binding
215                 if(!strstr(pBind,"_Tcpip_"))
216                         continue;
217
218         // Find the beginning of the GUID.
219         guid = strchr(pBind, '{');
220         if (guid == NULL)
221             continue;                   // Malformed path entry?
222         guid = strdup(guid);
223         if (guid == NULL)
224             continue;
225         // Find the end of the GUID.
226         p = strchr(guid, '}');
227         if (p == NULL) {
228             free(guid);                 // Malformed GUID?
229             continue;
230         }
231         *++p = '\0';                    // Ignore anything after the GUID.
232         status = lana_GetNameFromGuid(guid, &name);
233         free(guid);
234         if (status == 0) {
235             status = strcmp(name, LanaName);
236             free(name);
237             if (status == 0) {
238                 free(bindpaths);
239                 afsi_log("lana_FindLanaByName: Found lana %d for %s",
240                          lanamap[i].number, LanaName);
241                 return lanamap[i].number;
242             }
243         }
244     }
245     free(bindpaths);
246     return LANA_INVALID;
247 }
248
249 extern "C" lana_number_t lana_FindLoopback(void)
250 {
251     NCB ncb;
252     LANA_ENUM lana_list;
253     int status;
254     int i;
255
256     memset(&ncb, 0, sizeof(ncb));
257     ncb.ncb_command = NCBENUM;
258     ncb.ncb_buffer = (UCHAR *) &lana_list;
259     ncb.ncb_length = sizeof(lana_list);
260     status = Netbios(&ncb);
261     if (status != 0) {
262         afsi_log("Netbios NCBENUM failed: status %ld", status);
263         return LANA_INVALID;
264     }
265     for (i = 0; i < lana_list.length; i++) {
266         if (lana_IsLoopback(lana_list.lana[i])) {
267             // Found one, return it.
268             afsi_log("lana_FindLoopback: Found LAN adapter %d",
269                      lana_list.lana[i]);
270             return lana_list.lana[i];
271         }
272     }
273     // Could not find a loopback adapter.
274     return LANA_INVALID;
275 }
276
277 // Is the given lana a Windows Loopback Adapter?
278 extern "C" BOOL lana_IsLoopback(lana_number_t lana)
279 {
280     NCB ncb;
281     struct {
282         ADAPTER_STATUS status;
283         NAME_BUFFER names[MAX_LANA+1];
284     } astat;
285     unsigned char kWLA_MAC[6] = { 0x02, 0x00, 0x4c, 0x4f, 0x4f, 0x50 };
286     int status;
287
288     // Reset the adapter: in Win32, this is required for every process, and
289     // acts as an init call, not as a real hardware reset.
290     memset(&ncb, 0, sizeof(ncb));
291     ncb.ncb_command = NCBRESET;
292     ncb.ncb_callname[0] = 100;
293     ncb.ncb_callname[2] = 100;
294     ncb.ncb_lana_num = lana;
295     status = Netbios(&ncb);
296     if (status == 0)
297         status = ncb.ncb_retcode;
298     if (status != 0) {
299         afsi_log("NCBRESET failed: lana %u, status %ld", lana, status);
300         return FALSE;
301     }
302
303     // Use the NCBASTAT command to get the adapter address.
304     memset(&ncb, 0, sizeof(ncb));
305     ncb.ncb_command = NCBASTAT;
306     ncb.ncb_lana_num = lana;
307     strcpy((char *) ncb.ncb_callname, "*               ");
308     ncb.ncb_buffer = (UCHAR *) &astat;
309     ncb.ncb_length = sizeof(astat);
310     status = Netbios(&ncb);
311     if (status == 0)
312         status = ncb.ncb_retcode;
313     if (ncb.ncb_retcode != 0) {
314         afsi_log("NCBASTAT failed: lana %u, status %ld", lana, status);
315         return FALSE;
316     }
317     return (memcmp(astat.status.adapter_address, kWLA_MAC, 6) == 0);
318 }