3 Copyright 2004 by the Massachusetts Institute of Technology
7 Permission to use, copy, modify, and distribute this software and its
8 documentation for any purpose and without fee is hereby granted,
9 provided that the above copyright notice appear in all copies and that
10 both that copyright notice and this permission notice appear in
11 supporting documentation, and that the name of the Massachusetts
12 Institute of Technology (M.I.T.) not be used in advertising or publicity
13 pertaining to distribution of the software without specific, written
16 M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
17 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
18 M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
19 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
20 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
21 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
37 #include <lanahelper.h>
38 #include <WINNT\afsreg.h>
45 void afsi_log(TCHAR *p, ...) {
50 _vstprintf(buffer,p,marker);
52 _tcscat(buffer,_T("\n"));
54 OutputDebugString(buffer);
59 static const char *szNetbiosNameValue = "NetbiosName";
60 static const char *szIsGatewayValue = "IsGateway";
61 static const char *szLanAdapterValue = "LanAdapter";
62 #ifdef USE_FINDLANABYNAME
63 static const char *szNoFindLanaByName = "NoFindLanaByName";
65 static const char *szForceLanaLoopback = "ForceLanaLoopback";
67 // Use the IShellFolder API to get the connection name for the given Guid.
68 static HRESULT lana_ShellGetNameFromGuidW(WCHAR *wGuid, WCHAR *wName, int NameSize)
70 // This is the GUID for the network connections folder. It is constant.
71 // {7007ACC7-3202-11D1-AAD2-00805FC1270E}
72 const GUID CLSID_NetworkConnections = {
73 0x7007ACC7, 0x3202, 0x11D1, {
74 0xAA, 0xD2, 0x00, 0x80, 0x5F, 0xC1, 0x27, 0x0E
78 IShellFolder *pShellFolder;
79 IMalloc *pShellMalloc;
81 // Build the display name in the form "::{GUID}".
82 if (wcslen(wGuid) >= MAX_PATH)
84 WCHAR szAdapterGuid[MAX_PATH + 2];
85 swprintf(szAdapterGuid, L"::%ls", wGuid);
90 // Get the shell allocator.
91 HRESULT hr = SHGetMalloc(&pShellMalloc);
94 // Create an instance of the network connections folder.
95 hr = CoCreateInstance(CLSID_NetworkConnections, NULL,
96 CLSCTX_INPROC_SERVER, IID_IShellFolder,
97 reinterpret_cast<LPVOID *>(&pShellFolder));
101 hr = pShellFolder->ParseDisplayName(NULL, NULL, szAdapterGuid, NULL,
105 // Get the display name; this returns the friendly name.
107 hr = pShellFolder->GetDisplayNameOf(pidl, SHGDN_NORMAL, &sName);
109 wcsncpy(wName, sName.pOleStr, NameSize);
110 pShellMalloc->Free(pidl);
117 // Get the Connection Name for the given GUID.
118 extern "C" int lana_GetNameFromGuid(char *Guid, char **Name)
120 typedef HRESULT (WINAPI *HrLanProcAddr)(GUID *, PCWSTR, PWSTR, LPDWORD);
121 HrLanProcAddr HrLanProc = NULL;
125 WCHAR wName[MAX_PATH];
126 DWORD NameSize = MAX_PATH;
130 // Convert the Guid string to Unicode. First we ask only for the size
131 // of the converted string. Then we allocate a buffer of sufficient
132 // size to hold the result of the conversion.
133 size = MultiByteToWideChar(CP_ACP, 0, Guid, -1, NULL, 0);
134 wGuid = (WCHAR *) malloc(size * sizeof(WCHAR));
135 MultiByteToWideChar(CP_ACP, 0, Guid, -1, wGuid, size);
137 // First try the IShellFolder interface, which was unimplemented
138 // for the network connections folder before XP.
140 /* XXX pbh 9/11/03 - revert to using the undocumented APIs on XP while
141 * waiting to hear back from PSS about the slow reboot issue.
142 * This is an ugly, misleading hack, but is minimally invasive
143 * and will be easy to rollback.
146 //status = getname_shellfolder(wGuid, wName, NameSize);
149 /* XXX end of pbh 9/11/03 temporary hack*/
151 if (status == E_NOTIMPL) {
152 // The IShellFolder interface is not implemented on this platform.
153 // Try the (undocumented) HrLanConnectionNameFromGuidOrPath API
154 // from the netman DLL.
156 afsi_log("IShellFolder API not implemented, trying HrLanConnectionNameFromGuidOrPath");
158 hNetMan = LoadLibrary("netman.dll");
159 if (hNetMan == NULL) {
163 /* Super Secret Microsoft Call */
164 HrLanProc = (HrLanProcAddr) GetProcAddress(hNetMan,
165 "HrLanConnectionNameFromGuidOrPath");
166 if (HrLanProc == NULL) {
167 FreeLibrary(hNetMan);
171 status = HrLanProc(NULL, wGuid, wName, &NameSize);
172 FreeLibrary(hNetMan);
175 if (FAILED(status)) {
177 afsi_log("lana_GetNameFromGuid: failed to get connection name (status %ld)",
183 // Get the required buffer size, and then convert the string.
184 size = WideCharToMultiByte(CP_ACP, 0, wName, -1, NULL, 0, NULL, NULL);
185 name = (char *) malloc(size);
188 WideCharToMultiByte(CP_ACP, 0, wName, -1, name, size, NULL, NULL);
190 afsi_log("Connection name for %s is '%s'", Guid, name);
199 // Return an array of LANAINFOs corresponding to a connection named LanaName
200 // (NULL LanaName matches all connections), and has an IPv4 binding. Returns
201 // NULL if something goes wrong.
202 // NOTE: caller must free the returned block if non NULL.
203 extern "C" LANAINFO * lana_FindLanaByName(const char *LanaName)
205 const char RegNetBiosLinkageKeyName[] =
206 "System\\CurrentControlSet\\Services\\NetBios\\Linkage";
212 } lanamap[MAX_LANA+1];
213 DWORD lanamapsize = sizeof(lanamap);
215 char *bindpaths = NULL;
226 // Open the NetBios Linkage key.
227 status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegNetBiosLinkageKeyName, 0,
228 KEY_QUERY_VALUE, &hkey);
230 if (status != ERROR_SUCCESS) {
232 afsi_log("Failed to open NetBios Linkage key (status %ld)", status);
237 // Read the lana map.
238 status = RegQueryValueEx(hkey, "LanaMap", 0, &type,
239 (BYTE *) &lanamap, &lanamapsize);
240 if (status != ERROR_SUCCESS) {
242 afsi_log("Failed to read LanaMap (status %ld)", status);
247 if (lanamapsize == 0) {
249 afsi_log("No data in LanaMap");
253 nlana = lanamapsize / sizeof(lanamap[0]);
255 // Get the bind paths for NetBios so we can match them up
256 // with the lana map. First we query for the size, so we
257 // can allocate an appropriate buffer.
258 status = RegQueryValueEx(hkey, "Bind", 0, &type, NULL, &bindpathsize);
259 if (status == ERROR_SUCCESS && bindpathsize != 0) {
260 bindpaths = (char *) malloc(bindpathsize * sizeof(char));
261 if (bindpaths == NULL) {
263 afsi_log("Cannot allocate %ld bytes for bindpaths", bindpathsize);
268 status = RegQueryValueEx(hkey, "Bind", 0, &type,
269 (BYTE *) bindpaths, &bindpathsize);
272 if (status != ERROR_SUCCESS) {
274 afsi_log("Failed to read bind paths (status %ld)", status);
276 if (bindpaths != NULL)
280 if (bindpathsize == 0) {
282 afsi_log("No bindpath data");
284 if (bindpaths != NULL)
291 lanainfo = (LANAINFO *) malloc(sizeof(LANAINFO)*2);
292 if(lanainfo == NULL) {
296 memset(lanainfo, 0, sizeof(LANAINFO) * 2);
297 lanainfo[0].lana_number = LANA_INVALID;
301 lanainfo = (LANAINFO *) malloc(sizeof(LANAINFO)*(nlana+1));
302 if(lanainfo == NULL) {
306 memset(lanainfo, 0, sizeof(LANAINFO) * (nlana+1));
311 // Iterate over the lana map entries and bind paths.
312 for (i = 0, pBind = bindpaths; i < nlana;
313 i++, pBind += strlen(pBind) + 1) {
314 // Ignore an invalid map entry.
315 if ((lanamap[i].flags & 1) == 0)
318 // check for an IPv4 binding
319 if(!strstr(pBind,"_Tcpip_"))
322 // Find the beginning of the GUID.
323 guid = strchr(pBind, '{');
325 continue; // Malformed path entry?
329 // Find the end of the GUID.
330 p = strchr(guid, '}');
332 free(guid); // Malformed GUID?
335 *++p = '\0'; // Ignore anything after the GUID.
336 status = lana_GetNameFromGuid(guid, &name);
339 if (status == 0 && name != 0)
343 if (strcmp(name, LanaName) ==0)
345 lanainfo[index].lana_number = lanamap[i].number;
346 _tcscpy(lanainfo[index].lana_name, name);
354 lanainfo[index].lana_number = lanamap[i].number;
355 _tcscpy(lanainfo[index].lana_name, name);
362 lanainfo[index].lana_number = LANA_INVALID;
368 extern "C" lana_number_t lana_FindLoopback(void)
375 memset(&ncb, 0, sizeof(ncb));
376 ncb.ncb_command = NCBENUM;
377 ncb.ncb_buffer = (UCHAR *) &lana_list;
378 ncb.ncb_length = sizeof(lana_list);
379 status = Netbios(&ncb);
382 afsi_log("Netbios NCBENUM failed: status %ld", status);
386 for (i = 0; i < lana_list.length; i++) {
387 if (lana_IsLoopback(lana_list.lana[i],TRUE)) {
388 // Found one, return it.
390 afsi_log("lana_FindLoopback: Found LAN adapter %d",
393 return lana_list.lana[i];
396 // Could not find a loopback adapter.
400 /* Returns TRUE if all adapters are loopback adapters */
401 extern "C" BOOL lana_OnlyLoopback(void)
408 memset(&ncb, 0, sizeof(ncb));
409 ncb.ncb_command = NCBENUM;
410 ncb.ncb_buffer = (UCHAR *) &lana_list;
411 ncb.ncb_length = sizeof(lana_list);
412 status = Netbios(&ncb);
415 afsi_log("Netbios NCBENUM failed: status %ld", status);
419 for (i = 0; i < lana_list.length; i++) {
420 if (!lana_IsLoopback(lana_list.lana[i],FALSE)) {
421 // Found one non-Loopback adapter
425 // All adapters are loopback
429 // Is the given lana a Windows Loopback Adapter?
430 // TODO: implement a better check for loopback
431 // TODO: also check for proper bindings (IPv4)
432 // For VMWare we only check the first five octets since the last one may vary
433 extern "C" BOOL lana_IsLoopback(lana_number_t lana, BOOL reset)
437 ADAPTER_STATUS status;
438 NAME_BUFFER names[MAX_LANA+1];
440 unsigned char kWLA_MAC[6] = { 0x02, 0x00, 0x4c, 0x4f, 0x4f, 0x50 };
441 unsigned char kVista_WLA_MAC[6] = { 0x7F, 0x00, 0x00, 0x01, 0x4f, 0x50 };
442 unsigned char kVMWare_MAC[5] = { 0x00, 0x50, 0x56, 0xC0, 0x00 };
449 rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,AFSREG_CLT_SVC_PARAM_SUBKEY,0,KEY_READ,&hkConfig);
450 if (rv == ERROR_SUCCESS) {
451 rv = RegQueryValueEx(hkConfig, szForceLanaLoopback, NULL, NULL, (LPBYTE) ®Lana, &dummyLen);
452 RegCloseKey(hkConfig);
459 // Reset the adapter: in Win32, this is required for every process, and
460 // acts as an init call, not as a real hardware reset.
461 memset(&ncb, 0, sizeof(ncb));
462 ncb.ncb_command = NCBRESET;
463 ncb.ncb_callname[0] = 100;
464 ncb.ncb_callname[2] = 100;
465 ncb.ncb_lana_num = lana;
466 status = Netbios(&ncb);
468 status = ncb.ncb_retcode;
471 afsi_log("NCBRESET failed: lana %u, status %ld", lana, status);
477 // Use the NCBASTAT command to get the adapter address.
478 memset(&ncb, 0, sizeof(ncb));
479 ncb.ncb_command = NCBASTAT;
480 ncb.ncb_lana_num = lana;
481 strcpy((char *) ncb.ncb_callname, "* ");
482 ncb.ncb_buffer = (UCHAR *) &astat;
483 ncb.ncb_length = sizeof(astat);
484 status = Netbios(&ncb);
486 status = ncb.ncb_retcode;
487 if (ncb.ncb_retcode != 0) {
489 afsi_log("NCBASTAT failed: lana %u, status %ld", lana, status);
493 return (memcmp(astat.status.adapter_address, kWLA_MAC, 6) == 0 ||
494 memcmp(astat.status.adapter_address, kVista_WLA_MAC, 6) == 0 ||
495 memcmp(astat.status.adapter_address, kVMWare_MAC, 5) == 0);
498 // Get the netbios named used/to-be-used by the AFS SMB server.
499 // IF <lana specified> THEN
500 // Use specified lana
502 // Look for an adapter named "AFS", failing which,
503 // look for a loopback adapter.
505 // IF lana is for a loopback && !IsGateway THEN
506 // IF netbios name is specified THEN
507 // use specified netbios name
512 // use netbios name "<hostname>-AFS"
514 // Return ERROR_SUCCESS if netbios name was successfully generated.
515 // Returns the lana number to use in *pLana (if pLana is non-NULL) and also
516 // the IsGateway setting in *pIsGateway (if pIsGateway is non-NULL).
517 // the type of name returned.
519 // buffer is assumed to hold at least MAX_NB_NAME_LENGTH bytes.
522 // LANA_NETBIOS_NAME_IN : Use the values of *pLana and *pIsGateway as [in] parameters.
523 // LANA_NETBIOS_NAME_SUFFIX : Only return the suffix of netbios name
524 // LANA_NETBIOS_NAME_FULL : Return full netbios name
525 extern "C" long lana_GetUncServerNameEx(char *buffer, lana_number_t * pLana, int * pIsGateway, int flags) {
531 #ifdef USE_FINDLANABYNAME
532 int regNoFindLanaByName;
534 TCHAR regNbName[MAX_NB_NAME_LENGTH] = "AFS";
535 TCHAR nbName[MAX_NB_NAME_LENGTH];
536 TCHAR hostname[MAX_COMPUTERNAME_LENGTH+1];
538 rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,AFSREG_CLT_SVC_PARAM_SUBKEY,0,KEY_READ,&hkConfig);
539 if(rv == ERROR_SUCCESS) {
540 if(!(flags & LANA_NETBIOS_NAME_IN) || !pLana) {
541 dummyLen = sizeof(regLana);
542 rv = RegQueryValueEx(hkConfig, szLanAdapterValue, NULL, NULL, (LPBYTE) ®Lana, &dummyLen);
543 if(rv != ERROR_SUCCESS)
548 if(!(flags & LANA_NETBIOS_NAME_IN) || !pIsGateway) {
549 dummyLen = sizeof(regGateway);
550 rv = RegQueryValueEx(hkConfig, szIsGatewayValue, NULL, NULL, (LPBYTE) ®Gateway, &dummyLen);
551 if(rv != ERROR_SUCCESS)
554 regGateway = *pIsGateway;
556 #ifdef USE_FINDLANABYNAME
557 dummyLen = sizeof(regNoFindLanaByName);
558 rv = RegQueryValueEx(hkConfig, szNoFindLanaByName, NULL, NULL, (LPBYTE) ®NoFindLanaByName, &dummyLen);
559 if(rv != ERROR_SUCCESS)
560 regNoFindLanaByName = 0;
563 // Do not care if the call fails for insufficient buffer size. We are not interested
564 // in netbios names over 15 chars.
565 dummyLen = sizeof(regNbName);
566 rv = RegQueryValueEx(hkConfig, szNetbiosNameValue, NULL, NULL, (LPBYTE) ®NbName, &dummyLen);
567 if(rv != ERROR_SUCCESS)
568 strcpy(regNbName, "AFS");
572 RegCloseKey(hkConfig);
574 if(flags & LANA_NETBIOS_NAME_IN) {
575 regLana = (pLana)? *pLana: -1;
576 regGateway = (pIsGateway)? *pIsGateway: 0;
581 #ifdef USE_FINDLANABYNAME
582 regNoFindLanaByName = 0;
584 strcpy(regNbName, "AFS");
587 if(regLana < 0 || regLana > MAX_LANA)
591 LANAINFO *lanaInfo = NULL;
592 int nLana = LANA_INVALID;
594 #ifdef USE_FINDLANABYNAME
595 if (!regNoFindLanaByName)
596 lanaInfo = lana_FindLanaByName("AFS");
598 if(lanaInfo != NULL) {
599 nLana = lanaInfo[0].lana_number;
602 nLana = LANA_INVALID;
604 if(nLana == LANA_INVALID && !regGateway) {
605 nLana = lana_FindLoopback();
607 if(nLana != LANA_INVALID)
612 (regLana >=0 && lana_IsLoopback((lana_number_t) regLana,FALSE)))
614 strncpy(nbName,regNbName,15);
620 if(flags & LANA_NETBIOS_NAME_SUFFIX) {
621 strcpy(nbName,"-AFS");
623 dummyLen = sizeof(hostname);
624 // assume we are not a cluster.
625 rv = GetComputerName(hostname, &dummyLen);
626 if(!SUCCEEDED(rv)) { // should not happen, but...
629 strncpy(nbName, hostname, 11);
631 if(dot = strchr(nbName,'.'))
633 strcat(nbName,"-AFS");
640 *pIsGateway = regGateway;
642 strcpy(buffer, nbName);
644 return ERROR_SUCCESS;
647 extern "C" void lana_GetUncServerNameDynamic(int lanaNumber, BOOL isGateway, TCHAR *name, int type) {
648 char szName[MAX_NB_NAME_LENGTH];
649 lana_number_t lana = (lana_number_t) lanaNumber;
650 int gateway = (int) isGateway;
652 if(SUCCEEDED(lana_GetUncServerNameEx(szName, &lana, &gateway, LANA_NETBIOS_NAME_IN | type))) {
654 mbswcs(name,szName,MAX_NB_NAME_LENGTH);
656 strncpy(name,szName,MAX_NB_NAME_LENGTH);
662 extern "C" void lana_GetUncServerName(TCHAR *name, int type) {
663 char szName[MAX_NB_NAME_LENGTH];
665 if(SUCCEEDED(lana_GetUncServerNameEx(szName,NULL,NULL,type))) {
667 mbswcs(name,szName,MAX_NB_NAME_LENGTH);
669 strncpy(name,szName,MAX_NB_NAME_LENGTH);
676 extern "C" void lana_GetAfsNameString(int lanaNumber, BOOL isGateway, TCHAR* name)
678 TCHAR netbiosName[32];
679 lana_GetUncServerNameDynamic(lanaNumber, isGateway, netbiosName, LANA_NETBIOS_NAME_FULL);
680 _stprintf(name, _T("Your UNC name to reach the root of AFS is \\\\%s\\all"), netbiosName);
683 extern "C" void lana_GetNetbiosName(LPTSTR pszName, int type)
686 TCHAR name[MAX_NB_NAME_LENGTH];
689 memset(name, 0, sizeof(name));
690 if (GetVersion() >= 0x80000000) // not WindowsNT
692 if (type == LANA_NETBIOS_NAME_SUFFIX)
694 _tcscpy(pszName, TEXT("-afs"));
698 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,AFSREG_CLT_SVC_PARAM_SUBKEY,0,KEY_READ,&hkCfg) == ERROR_SUCCESS) {
699 dummyLen = sizeof(name);
700 if(RegQueryValueEx(hkCfg,TEXT("Gateway"),NULL,NULL,(LPBYTE) name,&dummyLen) == ERROR_SUCCESS)
705 if (_tcslen(name) == 0)
707 _tcscpy(pszName, TEXT("unknown"));
711 _tcscpy(pszName, name);
712 _tcscat(pszName, TEXT("-afs"));
716 lana_GetUncServerName(name,type);
718 _tcscpy(pszName, name);