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 static const char *szNoFindLanaByName = "NoFindLanaByName";
63 static const char *szForceLanaLoopback = "ForceLanaLoopback";
65 // Use the IShellFolder API to get the connection name for the given Guid.
66 static HRESULT lana_ShellGetNameFromGuidW(WCHAR *wGuid, WCHAR *wName, int NameSize)
68 // This is the GUID for the network connections folder. It is constant.
69 // {7007ACC7-3202-11D1-AAD2-00805FC1270E}
70 const GUID CLSID_NetworkConnections = {
71 0x7007ACC7, 0x3202, 0x11D1, {
72 0xAA, 0xD2, 0x00, 0x80, 0x5F, 0xC1, 0x27, 0x0E
76 IShellFolder *pShellFolder;
77 IMalloc *pShellMalloc;
79 // Build the display name in the form "::{GUID}".
80 if (wcslen(wGuid) >= MAX_PATH)
82 WCHAR szAdapterGuid[MAX_PATH + 2];
83 swprintf(szAdapterGuid, L"::%ls", wGuid);
88 // Get the shell allocator.
89 HRESULT hr = SHGetMalloc(&pShellMalloc);
92 // Create an instance of the network connections folder.
93 hr = CoCreateInstance(CLSID_NetworkConnections, NULL,
94 CLSCTX_INPROC_SERVER, IID_IShellFolder,
95 reinterpret_cast<LPVOID *>(&pShellFolder));
99 hr = pShellFolder->ParseDisplayName(NULL, NULL, szAdapterGuid, NULL,
103 // Get the display name; this returns the friendly name.
105 hr = pShellFolder->GetDisplayNameOf(pidl, SHGDN_NORMAL, &sName);
107 wcsncpy(wName, sName.pOleStr, NameSize);
108 pShellMalloc->Free(pidl);
115 // Get the Connection Name for the given GUID.
116 extern "C" int lana_GetNameFromGuid(char *Guid, char **Name)
118 typedef HRESULT (WINAPI *HrLanProcAddr)(GUID *, PCWSTR, PWSTR, LPDWORD);
119 HrLanProcAddr HrLanProc = NULL;
123 WCHAR wName[MAX_PATH];
124 DWORD NameSize = MAX_PATH;
128 // Convert the Guid string to Unicode. First we ask only for the size
129 // of the converted string. Then we allocate a buffer of sufficient
130 // size to hold the result of the conversion.
131 size = MultiByteToWideChar(CP_ACP, 0, Guid, -1, NULL, 0);
132 wGuid = (WCHAR *) malloc(size * sizeof(WCHAR));
133 MultiByteToWideChar(CP_ACP, 0, Guid, -1, wGuid, size);
135 // First try the IShellFolder interface, which was unimplemented
136 // for the network connections folder before XP.
138 /* XXX pbh 9/11/03 - revert to using the undocumented APIs on XP while
139 * waiting to hear back from PSS about the slow reboot issue.
140 * This is an ugly, misleading hack, but is minimally invasive
141 * and will be easy to rollback.
144 //status = getname_shellfolder(wGuid, wName, NameSize);
147 /* XXX end of pbh 9/11/03 temporary hack*/
149 if (status == E_NOTIMPL) {
150 // The IShellFolder interface is not implemented on this platform.
151 // Try the (undocumented) HrLanConnectionNameFromGuidOrPath API
152 // from the netman DLL.
154 afsi_log("IShellFolder API not implemented, trying HrLanConnectionNameFromGuidOrPath");
156 hNetMan = LoadLibrary("netman.dll");
157 if (hNetMan == NULL) {
161 /* Super Secret Microsoft Call */
162 HrLanProc = (HrLanProcAddr) GetProcAddress(hNetMan,
163 "HrLanConnectionNameFromGuidOrPath");
164 if (HrLanProc == NULL) {
165 FreeLibrary(hNetMan);
169 status = HrLanProc(NULL, wGuid, wName, &NameSize);
170 FreeLibrary(hNetMan);
173 if (FAILED(status)) {
175 afsi_log("lana_GetNameFromGuid: failed to get connection name (status %ld)",
181 // Get the required buffer size, and then convert the string.
182 size = WideCharToMultiByte(CP_ACP, 0, wName, -1, NULL, 0, NULL, NULL);
183 name = (char *) malloc(size);
186 WideCharToMultiByte(CP_ACP, 0, wName, -1, name, size, NULL, NULL);
188 afsi_log("Connection name for %s is '%s'", Guid, name);
197 // Return an array of LANAINFOs corresponding to a connection named LanaName
198 // (NULL LanaName matches all connections), and has an IPv4 binding. Returns
199 // NULL if something goes wrong.
200 // NOTE: caller must free the returned block if non NULL.
201 extern "C" LANAINFO * lana_FindLanaByName(const char *LanaName)
203 const char RegNetBiosLinkageKeyName[] =
204 "System\\CurrentControlSet\\Services\\NetBios\\Linkage";
210 } lanamap[MAX_LANA+1];
211 DWORD lanamapsize = sizeof(lanamap);
213 char *bindpaths = NULL;
224 // Open the NetBios Linkage key.
225 status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegNetBiosLinkageKeyName, 0,
226 KEY_QUERY_VALUE, &hkey);
228 if (status != ERROR_SUCCESS) {
230 afsi_log("Failed to open NetBios Linkage key (status %ld)", status);
235 // Read the lana map.
236 status = RegQueryValueEx(hkey, "LanaMap", 0, &type,
237 (BYTE *) &lanamap, &lanamapsize);
238 if (status != ERROR_SUCCESS) {
240 afsi_log("Failed to read LanaMap (status %ld)", status);
245 if (lanamapsize == 0) {
247 afsi_log("No data in LanaMap");
251 nlana = lanamapsize / sizeof(lanamap[0]);
253 // Get the bind paths for NetBios so we can match them up
254 // with the lana map. First we query for the size, so we
255 // can allocate an appropriate buffer.
256 status = RegQueryValueEx(hkey, "Bind", 0, &type, NULL, &bindpathsize);
257 if (status == ERROR_SUCCESS && bindpathsize != 0) {
258 bindpaths = (char *) malloc(bindpathsize * sizeof(char));
259 if (bindpaths == NULL) {
261 afsi_log("Cannot allocate %ld bytes for bindpaths", bindpathsize);
266 status = RegQueryValueEx(hkey, "Bind", 0, &type,
267 (BYTE *) bindpaths, &bindpathsize);
270 if (status != ERROR_SUCCESS) {
272 afsi_log("Failed to read bind paths (status %ld)", status);
274 if (bindpaths != NULL)
278 if (bindpathsize == 0) {
280 afsi_log("No bindpath data");
282 if (bindpaths != NULL)
289 lanainfo = (LANAINFO *) malloc(sizeof(LANAINFO)*2);
290 if(lanainfo == NULL) {
294 memset(lanainfo, 0, sizeof(LANAINFO) * 2);
295 lanainfo[0].lana_number = LANA_INVALID;
299 lanainfo = (LANAINFO *) malloc(sizeof(LANAINFO)*(nlana+1));
300 if(lanainfo == NULL) {
304 memset(lanainfo, 0, sizeof(LANAINFO) * (nlana+1));
309 // Iterate over the lana map entries and bind paths.
310 for (i = 0, pBind = bindpaths; i < nlana;
311 i++, pBind += strlen(pBind) + 1) {
312 // Ignore an invalid map entry.
313 if ((lanamap[i].flags & 1) == 0)
316 // check for an IPv4 binding
317 if(!strstr(pBind,"_Tcpip_"))
320 // Find the beginning of the GUID.
321 guid = strchr(pBind, '{');
323 continue; // Malformed path entry?
327 // Find the end of the GUID.
328 p = strchr(guid, '}');
330 free(guid); // Malformed GUID?
333 *++p = '\0'; // Ignore anything after the GUID.
334 status = lana_GetNameFromGuid(guid, &name);
337 if (status == 0 && name != 0)
341 if (strcmp(name, LanaName) ==0)
343 lanainfo[index].lana_number = lanamap[i].number;
344 _tcscpy(lanainfo[index].lana_name, name);
352 lanainfo[index].lana_number = lanamap[i].number;
353 _tcscpy(lanainfo[index].lana_name, name);
360 lanainfo[index].lana_number = LANA_INVALID;
366 extern "C" lana_number_t lana_FindLoopback(void)
373 memset(&ncb, 0, sizeof(ncb));
374 ncb.ncb_command = NCBENUM;
375 ncb.ncb_buffer = (UCHAR *) &lana_list;
376 ncb.ncb_length = sizeof(lana_list);
377 status = Netbios(&ncb);
380 afsi_log("Netbios NCBENUM failed: status %ld", status);
384 for (i = 0; i < lana_list.length; i++) {
385 if (lana_IsLoopback(lana_list.lana[i])) {
386 // Found one, return it.
388 afsi_log("lana_FindLoopback: Found LAN adapter %d",
391 return lana_list.lana[i];
394 // Could not find a loopback adapter.
398 /* Returns TRUE if all adapters are loopback adapters */
399 extern "C" BOOL lana_OnlyLoopback(void)
406 memset(&ncb, 0, sizeof(ncb));
407 ncb.ncb_command = NCBENUM;
408 ncb.ncb_buffer = (UCHAR *) &lana_list;
409 ncb.ncb_length = sizeof(lana_list);
410 status = Netbios(&ncb);
413 afsi_log("Netbios NCBENUM failed: status %ld", status);
417 for (i = 0; i < lana_list.length; i++) {
418 if (!lana_IsLoopback(lana_list.lana[i])) {
419 // Found one non-Loopback adapter
423 // All adapters are loopback
427 // Is the given lana a Windows Loopback Adapter?
428 // TODO: implement a better check for loopback
429 // TODO: also check for proper bindings (IPv4)
430 extern "C" BOOL lana_IsLoopback(lana_number_t lana)
434 ADAPTER_STATUS status;
435 NAME_BUFFER names[MAX_LANA+1];
437 unsigned char kWLA_MAC[6] = { 0x02, 0x00, 0x4c, 0x4f, 0x4f, 0x50 };
438 unsigned char kVista_WLA_MAC[6] = { 0x7F, 0x00, 0x00, 0x01, 0x4f, 0x50 };
445 rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,AFSREG_CLT_SVC_PARAM_SUBKEY,0,KEY_READ,&hkConfig);
446 if (rv == ERROR_SUCCESS) {
447 rv = RegQueryValueEx(hkConfig, szForceLanaLoopback, NULL, NULL, (LPBYTE) ®Lana, &dummyLen);
448 RegCloseKey(hkConfig);
454 // Reset the adapter: in Win32, this is required for every process, and
455 // acts as an init call, not as a real hardware reset.
456 memset(&ncb, 0, sizeof(ncb));
457 ncb.ncb_command = NCBRESET;
458 ncb.ncb_callname[0] = 100;
459 ncb.ncb_callname[2] = 100;
460 ncb.ncb_lana_num = lana;
461 status = Netbios(&ncb);
463 status = ncb.ncb_retcode;
466 afsi_log("NCBRESET failed: lana %u, status %ld", lana, status);
471 // Use the NCBASTAT command to get the adapter address.
472 memset(&ncb, 0, sizeof(ncb));
473 ncb.ncb_command = NCBASTAT;
474 ncb.ncb_lana_num = lana;
475 strcpy((char *) ncb.ncb_callname, "* ");
476 ncb.ncb_buffer = (UCHAR *) &astat;
477 ncb.ncb_length = sizeof(astat);
478 status = Netbios(&ncb);
480 status = ncb.ncb_retcode;
481 if (ncb.ncb_retcode != 0) {
483 afsi_log("NCBASTAT failed: lana %u, status %ld", lana, status);
487 return (memcmp(astat.status.adapter_address, kWLA_MAC, 6) == 0 ||
488 memcmp(astat.status.adapter_address, kVista_WLA_MAC, 6) == 0);
491 // Get the netbios named used/to-be-used by the AFS SMB server.
492 // IF <lana specified> THEN
493 // Use specified lana
495 // Look for an adapter named "AFS", failing which,
496 // look for a loopback adapter.
498 // IF lana is for a loopback && !IsGateway THEN
499 // IF netbios name is specified THEN
500 // use specified netbios name
505 // use netbios name "<hostname>-AFS"
507 // Return ERROR_SUCCESS if netbios name was successfully generated.
508 // Returns the lana number to use in *pLana (if pLana is non-NULL) and also
509 // the IsGateway setting in *pIsGateway (if pIsGateway is non-NULL).
510 // the type of name returned.
512 // buffer is assumed to hold at least MAX_NB_NAME_LENGTH bytes.
515 // LANA_NETBIOS_NAME_IN : Use the values of *pLana and *pIsGateway as [in] parameters.
516 // LANA_NETBIOS_NAME_SUFFIX : Only return the suffix of netbios name
517 // LANA_NETBIOS_NAME_FULL : Return full netbios name
518 extern "C" long lana_GetUncServerNameEx(char *buffer, lana_number_t * pLana, int * pIsGateway, int flags) {
523 int regGateway, regNoFindLanaByName;
524 TCHAR regNbName[MAX_NB_NAME_LENGTH];
525 TCHAR nbName[MAX_NB_NAME_LENGTH];
526 TCHAR hostname[MAX_COMPUTERNAME_LENGTH+1];
528 rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,AFSREG_CLT_SVC_PARAM_SUBKEY,0,KEY_READ,&hkConfig);
529 if(rv == ERROR_SUCCESS) {
530 if(!(flags & LANA_NETBIOS_NAME_IN) || !pLana) {
531 dummyLen = sizeof(regLana);
532 rv = RegQueryValueEx(hkConfig, szLanAdapterValue, NULL, NULL, (LPBYTE) ®Lana, &dummyLen);
533 if(rv != ERROR_SUCCESS) regLana = -1;
537 if(!(flags & LANA_NETBIOS_NAME_IN) || !pIsGateway) {
538 dummyLen = sizeof(regGateway);
539 rv = RegQueryValueEx(hkConfig, szIsGatewayValue, NULL, NULL, (LPBYTE) ®Gateway, &dummyLen);
540 if(rv != ERROR_SUCCESS) regGateway = 0;
542 regGateway = *pIsGateway;
544 dummyLen = sizeof(regNoFindLanaByName);
545 rv = RegQueryValueEx(hkConfig, szNoFindLanaByName, NULL, NULL, (LPBYTE) ®NoFindLanaByName, &dummyLen);
546 if(rv != ERROR_SUCCESS) regNoFindLanaByName = 0;
548 // Do not care if the call fails for insufficient buffer size. We are not interested
549 // in netbios names over 15 chars.
550 dummyLen = sizeof(regNbName);
551 rv = RegQueryValueEx(hkConfig, szNetbiosNameValue, NULL, NULL, (LPBYTE) ®NbName, &dummyLen);
552 if(rv != ERROR_SUCCESS) regNbName[0] = 0;
553 else regNbName[15] = 0;
555 RegCloseKey(hkConfig);
557 if(flags & LANA_NETBIOS_NAME_IN) {
558 regLana = (pLana)? *pLana: -1;
559 regGateway = (pIsGateway)? *pIsGateway: 0;
564 regNoFindLanaByName = 0;
568 if(regLana < 0 || regLana > MAX_LANA)
572 LANAINFO *lanaInfo = NULL;
573 int nLana = LANA_INVALID;
575 if (!regNoFindLanaByName)
576 lanaInfo = lana_FindLanaByName("AFS");
577 if(lanaInfo != NULL) {
578 nLana = lanaInfo[0].lana_number;
581 nLana = LANA_INVALID;
583 if(nLana == LANA_INVALID && !regGateway) {
584 nLana = lana_FindLoopback();
586 if(nLana != LANA_INVALID)
591 (regLana >=0 && lana_IsLoopback((lana_number_t) regLana))) {
592 strncpy(nbName,regNbName,15);
598 if(flags & LANA_NETBIOS_NAME_SUFFIX) {
599 strcpy(nbName,"-AFS");
601 dummyLen = sizeof(hostname);
602 // assume we are not a cluster.
603 rv = GetComputerName(hostname, &dummyLen);
604 if(!SUCCEEDED(rv)) { // should not happen, but...
607 strncpy(nbName, hostname, 11);
609 if(dot = strchr(nbName,'.'))
611 strcat(nbName,"-AFS");
615 if(pLana) *pLana = regLana;
616 if(pIsGateway) *pIsGateway = regGateway;
618 strcpy(buffer, nbName);
620 return ERROR_SUCCESS;
623 extern "C" void lana_GetUncServerNameDynamic(int lanaNumber, BOOL isGateway, TCHAR *name, int type) {
624 char szName[MAX_NB_NAME_LENGTH];
625 lana_number_t lana = (lana_number_t) lanaNumber;
626 int gateway = (int) isGateway;
628 if(SUCCEEDED(lana_GetUncServerNameEx(szName, &lana, &gateway, LANA_NETBIOS_NAME_IN | type))) {
630 mbswcs(name,szName,MAX_NB_NAME_LENGTH);
632 strncpy(name,szName,MAX_NB_NAME_LENGTH);
638 extern "C" void lana_GetUncServerName(TCHAR *name, int type) {
639 char szName[MAX_NB_NAME_LENGTH];
641 if(SUCCEEDED(lana_GetUncServerNameEx(szName,NULL,NULL,type))) {
643 mbswcs(name,szName,MAX_NB_NAME_LENGTH);
645 strncpy(name,szName,MAX_NB_NAME_LENGTH);
652 extern "C" void lana_GetAfsNameString(int lanaNumber, BOOL isGateway, TCHAR* name)
654 TCHAR netbiosName[32];
655 lana_GetUncServerNameDynamic(lanaNumber, isGateway, netbiosName, LANA_NETBIOS_NAME_FULL);
656 _stprintf(name, _T("Your UNC name to reach the root of AFS is \\\\%s\\all"), netbiosName);
659 extern "C" void lana_GetNetbiosName(LPTSTR pszName, int type)
662 TCHAR name[MAX_NB_NAME_LENGTH];
665 memset(name, 0, sizeof(name));
666 if (GetVersion() >= 0x80000000) // not WindowsNT
668 if (type == LANA_NETBIOS_NAME_SUFFIX)
670 _tcscpy(pszName, TEXT("-afs"));
674 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,AFSREG_CLT_SVC_PARAM_SUBKEY,0,KEY_READ,&hkCfg) == ERROR_SUCCESS) {
675 dummyLen = sizeof(name);
676 if(RegQueryValueEx(hkCfg,TEXT("Gateway"),NULL,NULL,(LPBYTE) name,&dummyLen) == ERROR_SUCCESS)
681 if (_tcslen(name) == 0)
683 _tcscpy(pszName, TEXT("unknown"));
687 _tcscpy(pszName, name);
688 _tcscat(pszName, TEXT("-afs"));
692 lana_GetUncServerName(name,type);
694 _tcscpy(pszName, name);