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(BOOL reset)
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], reset)) {
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 };
443 unsigned char kVMWare_MAC[5] = { 0x00, 0x50, 0x56, 0xC0, 0x00 };
451 rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,AFSREG_CLT_SVC_PARAM_SUBKEY,0,KEY_READ,&hkConfig);
452 if (rv == ERROR_SUCCESS) {
453 dummyLen = sizeof(regLana);
454 rv = RegQueryValueEx(hkConfig, szForceLanaLoopback, NULL, NULL, (LPBYTE) ®Lana, &dummyLen);
455 RegCloseKey(hkConfig);
462 // Reset the adapter: in Win32, this is required for every process, and
463 // acts as an init call, not as a real hardware reset.
464 memset(&ncb, 0, sizeof(ncb));
465 ncb.ncb_command = NCBRESET;
466 ncb.ncb_callname[0] = 100;
467 ncb.ncb_callname[2] = 100;
468 ncb.ncb_lana_num = lana;
469 status = Netbios(&ncb);
471 status = ncb.ncb_retcode;
474 afsi_log("NCBRESET failed: lana %u, status %ld", lana, status);
480 // Use the NCBASTAT command to get the adapter address.
481 memset(&ncb, 0, sizeof(ncb));
482 ncb.ncb_command = NCBASTAT;
483 ncb.ncb_lana_num = lana;
484 strcpy((char *) ncb.ncb_callname, "* ");
485 ncb.ncb_buffer = (UCHAR *) &astat;
486 ncb.ncb_length = sizeof(astat);
487 status = Netbios(&ncb);
489 status = ncb.ncb_retcode;
490 if (ncb.ncb_retcode != 0) {
492 afsi_log("NCBASTAT failed: lana %u, status %ld", lana, status);
496 return (memcmp(astat.status.adapter_address, kWLA_MAC, 6) == 0 ||
497 memcmp(astat.status.adapter_address, kVista_WLA_MAC, 6) == 0
499 || memcmp(astat.status.adapter_address, kVMWare_MAC, 5) == 0
504 // Get the netbios named used/to-be-used by the AFS SMB server.
505 // IF <lana specified> THEN
506 // Use specified lana
508 // Look for an adapter named "AFS", failing which,
509 // look for a loopback adapter.
511 // IF lana is for a loopback && !IsGateway THEN
512 // IF netbios name is specified THEN
513 // use specified netbios name
518 // use netbios name "<hostname>-AFS"
520 // Return ERROR_SUCCESS if netbios name was successfully generated.
521 // Returns the lana number to use in *pLana (if pLana is non-NULL) and also
522 // the IsGateway setting in *pIsGateway (if pIsGateway is non-NULL).
523 // the type of name returned.
525 // buffer is assumed to hold at least MAX_NB_NAME_LENGTH bytes.
528 // LANA_NETBIOS_NAME_IN : Use the values of *pLana and *pIsGateway as [in] parameters.
529 // LANA_NETBIOS_NAME_SUFFIX : Only return the suffix of netbios name
530 // LANA_NETBIOS_NAME_FULL : Return full netbios name
531 // LANA_NETBIOS_NO_RESET : Do not reset the netbios adapter state when finding the loopback
532 extern "C" long lana_GetUncServerNameEx(char *buffer, lana_number_t * pLana, int * pIsGateway, int flags) {
538 #ifdef USE_FINDLANABYNAME
539 int regNoFindLanaByName;
541 TCHAR regNbName[MAX_NB_NAME_LENGTH] = "AFS";
542 TCHAR nbName[MAX_NB_NAME_LENGTH];
543 TCHAR hostname[MAX_COMPUTERNAME_LENGTH+1];
545 rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,AFSREG_CLT_SVC_PARAM_SUBKEY,0,KEY_READ,&hkConfig);
546 if(rv == ERROR_SUCCESS) {
547 if(!(flags & LANA_NETBIOS_NAME_IN) || !pLana) {
548 dummyLen = sizeof(regLana);
549 rv = RegQueryValueEx(hkConfig, szLanAdapterValue, NULL, NULL, (LPBYTE) ®Lana, &dummyLen);
550 if(rv != ERROR_SUCCESS)
555 if(!(flags & LANA_NETBIOS_NAME_IN) || !pIsGateway) {
556 dummyLen = sizeof(regGateway);
557 rv = RegQueryValueEx(hkConfig, szIsGatewayValue, NULL, NULL, (LPBYTE) ®Gateway, &dummyLen);
558 if(rv != ERROR_SUCCESS)
561 regGateway = *pIsGateway;
563 #ifdef USE_FINDLANABYNAME
564 dummyLen = sizeof(regNoFindLanaByName);
565 rv = RegQueryValueEx(hkConfig, szNoFindLanaByName, NULL, NULL, (LPBYTE) ®NoFindLanaByName, &dummyLen);
566 if(rv != ERROR_SUCCESS)
567 regNoFindLanaByName = 0;
570 // Do not care if the call fails for insufficient buffer size. We are not interested
571 // in netbios names over 15 chars.
572 dummyLen = sizeof(regNbName);
573 rv = RegQueryValueEx(hkConfig, szNetbiosNameValue, NULL, NULL, (LPBYTE) ®NbName, &dummyLen);
574 if(rv != ERROR_SUCCESS)
575 strcpy(regNbName, "AFS");
577 /* Max Suffix Length is 6 */
581 RegCloseKey(hkConfig);
583 if(flags & LANA_NETBIOS_NAME_IN) {
584 regLana = (pLana)? *pLana: -1;
585 regGateway = (pIsGateway)? *pIsGateway: 0;
590 #ifdef USE_FINDLANABYNAME
591 regNoFindLanaByName = 0;
593 strcpy(regNbName, "AFS");
596 if(regLana < 0 || regLana > MAX_LANA)
600 LANAINFO *lanaInfo = NULL;
601 int nLana = LANA_INVALID;
603 #ifdef USE_FINDLANABYNAME
604 if (!regNoFindLanaByName)
605 lanaInfo = lana_FindLanaByName("AFS");
607 if(lanaInfo != NULL) {
608 nLana = lanaInfo[0].lana_number;
611 nLana = LANA_INVALID;
613 if(nLana == LANA_INVALID && !regGateway) {
614 nLana = lana_FindLoopback(!(flags & LANA_NETBIOS_NO_RESET));
616 if(nLana != LANA_INVALID)
621 (regLana >=0 && lana_IsLoopback((lana_number_t) regLana, FALSE)))
623 strncpy(nbName,regNbName,14);
627 if(flags & LANA_NETBIOS_NAME_SUFFIX) {
628 _snprintf(nbName, MAX_NB_NAME_LENGTH, "-%s", regNbName);
633 dummyLen = sizeof(hostname);
634 // assume we are not a cluster.
635 rv = GetComputerName(hostname, &dummyLen);
636 if(!SUCCEEDED(rv)) { // should not happen, but...
639 strncpy(nbName, hostname, MAX_NB_NAME_LENGTH);
640 nbName[MAX_NB_NAME_LENGTH-1] = 0;
641 nbName[MAX_NB_NAME_LENGTH-strlen(regNbName)-2] = 0;
642 if(dot = strchr(nbName,'.'))
644 strncat(nbName, "-", MAX_NB_NAME_LENGTH);
645 strncat(nbName, regNbName, MAX_NB_NAME_LENGTH);
646 nbName[MAX_NB_NAME_LENGTH-1] = 0;
653 *pIsGateway = regGateway;
655 strcpy(buffer, nbName);
657 return ERROR_SUCCESS;
660 extern "C" void lana_GetUncServerNameDynamic(int lanaNumber, BOOL isGateway, TCHAR *name, int type) {
661 char szName[MAX_NB_NAME_LENGTH];
662 lana_number_t lana = (lana_number_t) lanaNumber;
663 int gateway = (int) isGateway;
665 if(SUCCEEDED(lana_GetUncServerNameEx(szName, &lana, &gateway, LANA_NETBIOS_NAME_IN | type))) {
667 mbswcs(name,szName,MAX_NB_NAME_LENGTH);
669 strncpy(name,szName,MAX_NB_NAME_LENGTH);
675 extern "C" void lana_GetUncServerName(TCHAR *name, int type) {
676 char szName[MAX_NB_NAME_LENGTH];
678 if(SUCCEEDED(lana_GetUncServerNameEx(szName,NULL,NULL,type))) {
680 mbswcs(name,szName,MAX_NB_NAME_LENGTH);
682 strncpy(name,szName,MAX_NB_NAME_LENGTH);
689 extern "C" void lana_GetAfsNameString(int lanaNumber, BOOL isGateway, TCHAR* name)
691 TCHAR netbiosName[32];
692 lana_GetUncServerNameDynamic(lanaNumber, isGateway, netbiosName, LANA_NETBIOS_NAME_FULL);
693 _stprintf(name, _T("Your UNC name to reach the root of AFS is \\\\%s\\all"), netbiosName);
696 extern "C" void lana_GetNetbiosName(LPTSTR pszName, int type)
699 TCHAR name[MAX_NB_NAME_LENGTH];
702 memset(name, 0, sizeof(name));
703 if (GetVersion() >= 0x80000000) // not WindowsNT
705 if (type == LANA_NETBIOS_NAME_SUFFIX)
707 _tcscpy(pszName, TEXT("-afs"));
711 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,AFSREG_CLT_SVC_PARAM_SUBKEY,0,KEY_READ,&hkCfg) == ERROR_SUCCESS) {
712 dummyLen = sizeof(name);
713 if(RegQueryValueEx(hkCfg,TEXT("Gateway"),NULL,NULL,(LPBYTE) name,&dummyLen) == ERROR_SUCCESS)
718 if (_tcslen(name) == 0)
720 _tcscpy(pszName, TEXT("unknown"));
724 _tcscpy(pszName, name);
725 _tcscat(pszName, TEXT("-afs"));
729 lana_GetUncServerName(name,type);
731 _tcscpy(pszName, name);