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>
44 void afsi_log(TCHAR *p, ...) {
49 _vstprintf(buffer,p,marker);
51 _tcscat(buffer,_T("\n"));
53 OutputDebugString(buffer);
58 static const char *szAFSConfigKeyName = "SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon\\Parameters";
59 static const char *szNetbiosNameValue = "NetbiosName";
60 static const char *szIsGatewayValue = "IsGateway";
61 static const char *szLanAdapterValue = "LanAdapter";
62 static const char *szNoFindLanaByName = "NoFindLanaByName";
64 // Use the IShellFolder API to get the connection name for the given Guid.
65 static HRESULT lana_ShellGetNameFromGuidW(WCHAR *wGuid, WCHAR *wName, int NameSize)
67 // This is the GUID for the network connections folder. It is constant.
68 // {7007ACC7-3202-11D1-AAD2-00805FC1270E}
69 const GUID CLSID_NetworkConnections = {
70 0x7007ACC7, 0x3202, 0x11D1, {
71 0xAA, 0xD2, 0x00, 0x80, 0x5F, 0xC1, 0x27, 0x0E
75 IShellFolder *pShellFolder;
76 IMalloc *pShellMalloc;
78 // Build the display name in the form "::{GUID}".
79 if (wcslen(wGuid) >= MAX_PATH)
81 WCHAR szAdapterGuid[MAX_PATH + 2];
82 swprintf(szAdapterGuid, L"::%ls", wGuid);
87 // Get the shell allocator.
88 HRESULT hr = SHGetMalloc(&pShellMalloc);
91 // Create an instance of the network connections folder.
92 hr = CoCreateInstance(CLSID_NetworkConnections, NULL,
93 CLSCTX_INPROC_SERVER, IID_IShellFolder,
94 reinterpret_cast<LPVOID *>(&pShellFolder));
98 hr = pShellFolder->ParseDisplayName(NULL, NULL, szAdapterGuid, NULL,
102 // Get the display name; this returns the friendly name.
104 hr = pShellFolder->GetDisplayNameOf(pidl, SHGDN_NORMAL, &sName);
106 wcsncpy(wName, sName.pOleStr, NameSize);
107 pShellMalloc->Free(pidl);
114 // Get the Connection Name for the given GUID.
115 extern "C" int lana_GetNameFromGuid(char *Guid, char **Name)
117 typedef HRESULT (WINAPI *HrLanProcAddr)(GUID *, PCWSTR, PWSTR, LPDWORD);
118 HrLanProcAddr HrLanProc = NULL;
122 WCHAR wName[MAX_PATH];
123 DWORD NameSize = MAX_PATH;
127 // Convert the Guid string to Unicode. First we ask only for the size
128 // of the converted string. Then we allocate a buffer of sufficient
129 // size to hold the result of the conversion.
130 size = MultiByteToWideChar(CP_ACP, 0, Guid, -1, NULL, 0);
131 wGuid = (WCHAR *) malloc(size * sizeof(WCHAR));
132 MultiByteToWideChar(CP_ACP, 0, Guid, -1, wGuid, size);
134 // First try the IShellFolder interface, which was unimplemented
135 // for the network connections folder before XP.
137 /* XXX pbh 9/11/03 - revert to using the undocumented APIs on XP while
138 * waiting to hear back from PSS about the slow reboot issue.
139 * This is an ugly, misleading hack, but is minimally invasive
140 * and will be easy to rollback.
143 //status = getname_shellfolder(wGuid, wName, NameSize);
146 /* XXX end of pbh 9/11/03 temporary hack*/
148 if (status == E_NOTIMPL) {
149 // The IShellFolder interface is not implemented on this platform.
150 // Try the (undocumented) HrLanConnectionNameFromGuidOrPath API
151 // from the netman DLL.
153 afsi_log("IShellFolder API not implemented, trying HrLanConnectionNameFromGuidOrPath");
155 hNetMan = LoadLibrary("netman.dll");
156 if (hNetMan == NULL) {
160 /* Super Secret Microsoft Call */
161 HrLanProc = (HrLanProcAddr) GetProcAddress(hNetMan,
162 "HrLanConnectionNameFromGuidOrPath");
163 if (HrLanProc == NULL) {
164 FreeLibrary(hNetMan);
168 status = HrLanProc(NULL, wGuid, wName, &NameSize);
169 FreeLibrary(hNetMan);
172 if (FAILED(status)) {
174 afsi_log("lana_GetNameFromGuid: failed to get connection name (status %ld)",
180 // Get the required buffer size, and then convert the string.
181 size = WideCharToMultiByte(CP_ACP, 0, wName, -1, NULL, 0, NULL, NULL);
182 name = (char *) malloc(size);
185 WideCharToMultiByte(CP_ACP, 0, wName, -1, name, size, NULL, NULL);
187 afsi_log("Connection name for %s is '%s'", Guid, name);
196 // Return an array of LANAINFOs corresponding to a connection named LanaName
197 // (NULL LanaName matches all connections), and has an IPv4 binding. Returns
198 // NULL if something goes wrong.
199 // NOTE: caller must free the returned block if non NULL.
200 extern "C" LANAINFO * lana_FindLanaByName(const char *LanaName)
202 const char RegNetBiosLinkageKeyName[] =
203 "System\\CurrentControlSet\\Services\\NetBios\\Linkage";
209 } lanamap[MAX_LANA+1];
210 DWORD lanamapsize = sizeof(lanamap);
212 char *bindpaths = NULL;
223 // Open the NetBios Linkage key.
224 status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegNetBiosLinkageKeyName, 0,
225 KEY_QUERY_VALUE, &hkey);
227 if (status != ERROR_SUCCESS) {
229 afsi_log("Failed to open NetBios Linkage key (status %ld)", status);
234 // Read the lana map.
235 status = RegQueryValueEx(hkey, "LanaMap", 0, &type,
236 (BYTE *) &lanamap, &lanamapsize);
237 if (status != ERROR_SUCCESS) {
239 afsi_log("Failed to read LanaMap (status %ld)", status);
244 if (lanamapsize == 0) {
246 afsi_log("No data in LanaMap");
250 nlana = lanamapsize / sizeof(lanamap[0]);
252 // Get the bind paths for NetBios so we can match them up
253 // with the lana map. First we query for the size, so we
254 // can allocate an appropriate buffer.
255 status = RegQueryValueEx(hkey, "Bind", 0, &type, NULL, &bindpathsize);
256 if (status == ERROR_SUCCESS && bindpathsize != 0) {
257 bindpaths = (char *) malloc(bindpathsize * sizeof(char));
258 if (bindpaths == NULL) {
260 afsi_log("Cannot allocate %ld bytes for bindpaths", bindpathsize);
265 status = RegQueryValueEx(hkey, "Bind", 0, &type,
266 (BYTE *) bindpaths, &bindpathsize);
269 if (status != ERROR_SUCCESS) {
271 afsi_log("Failed to read bind paths (status %ld)", status);
273 if (bindpaths != NULL)
277 if (bindpathsize == 0) {
279 afsi_log("No bindpath data");
281 if (bindpaths != NULL)
288 lanainfo = (LANAINFO *) malloc(sizeof(LANAINFO)*2);
289 if(lanainfo == NULL) {
293 memset(lanainfo, 0, sizeof(LANAINFO) * 2);
294 lanainfo[0].lana_number = LANA_INVALID;
298 lanainfo = (LANAINFO *) malloc(sizeof(LANAINFO)*(nlana+1));
299 if(lanainfo == NULL) {
303 memset(lanainfo, 0, sizeof(LANAINFO) * (nlana+1));
308 // Iterate over the lana map entries and bind paths.
309 for (i = 0, pBind = bindpaths; i < nlana;
310 i++, pBind += strlen(pBind) + 1) {
311 // Ignore an invalid map entry.
312 if ((lanamap[i].flags & 1) == 0)
315 // check for an IPv4 binding
316 if(!strstr(pBind,"_Tcpip_"))
319 // Find the beginning of the GUID.
320 guid = strchr(pBind, '{');
322 continue; // Malformed path entry?
326 // Find the end of the GUID.
327 p = strchr(guid, '}');
329 free(guid); // Malformed GUID?
332 *++p = '\0'; // Ignore anything after the GUID.
333 status = lana_GetNameFromGuid(guid, &name);
336 if (status == 0 && name != 0)
340 if (strcmp(name, LanaName) ==0)
342 lanainfo[index].lana_number = lanamap[i].number;
343 _tcscpy(lanainfo[index].lana_name, name);
351 lanainfo[index].lana_number = lanamap[i].number;
352 _tcscpy(lanainfo[index].lana_name, name);
359 lanainfo[index].lana_number = LANA_INVALID;
365 extern "C" lana_number_t lana_FindLoopback(void)
372 memset(&ncb, 0, sizeof(ncb));
373 ncb.ncb_command = NCBENUM;
374 ncb.ncb_buffer = (UCHAR *) &lana_list;
375 ncb.ncb_length = sizeof(lana_list);
376 status = Netbios(&ncb);
379 afsi_log("Netbios NCBENUM failed: status %ld", status);
383 for (i = 0; i < lana_list.length; i++) {
384 if (lana_IsLoopback(lana_list.lana[i])) {
385 // Found one, return it.
387 afsi_log("lana_FindLoopback: Found LAN adapter %d",
390 return lana_list.lana[i];
393 // Could not find a loopback adapter.
397 /* Returns TRUE if all adapters are loopback adapters */
398 extern "C" BOOL lana_OnlyLoopback(void)
405 memset(&ncb, 0, sizeof(ncb));
406 ncb.ncb_command = NCBENUM;
407 ncb.ncb_buffer = (UCHAR *) &lana_list;
408 ncb.ncb_length = sizeof(lana_list);
409 status = Netbios(&ncb);
412 afsi_log("Netbios NCBENUM failed: status %ld", status);
416 for (i = 0; i < lana_list.length; i++) {
417 if (!lana_IsLoopback(lana_list.lana[i])) {
418 // Found one non-Loopback adapter
422 // All adapters are loopback
426 // Is the given lana a Windows Loopback Adapter?
427 // TODO: implement a better check for loopback
428 // TODO: also check for proper bindings (IPv4)
429 extern "C" BOOL lana_IsLoopback(lana_number_t lana)
433 ADAPTER_STATUS status;
434 NAME_BUFFER names[MAX_LANA+1];
436 unsigned char kWLA_MAC[6] = { 0x02, 0x00, 0x4c, 0x4f, 0x4f, 0x50 };
439 // Reset the adapter: in Win32, this is required for every process, and
440 // acts as an init call, not as a real hardware reset.
441 memset(&ncb, 0, sizeof(ncb));
442 ncb.ncb_command = NCBRESET;
443 ncb.ncb_callname[0] = 100;
444 ncb.ncb_callname[2] = 100;
445 ncb.ncb_lana_num = lana;
446 status = Netbios(&ncb);
448 status = ncb.ncb_retcode;
451 afsi_log("NCBRESET failed: lana %u, status %ld", lana, status);
456 // Use the NCBASTAT command to get the adapter address.
457 memset(&ncb, 0, sizeof(ncb));
458 ncb.ncb_command = NCBASTAT;
459 ncb.ncb_lana_num = lana;
460 strcpy((char *) ncb.ncb_callname, "* ");
461 ncb.ncb_buffer = (UCHAR *) &astat;
462 ncb.ncb_length = sizeof(astat);
463 status = Netbios(&ncb);
465 status = ncb.ncb_retcode;
466 if (ncb.ncb_retcode != 0) {
468 afsi_log("NCBASTAT failed: lana %u, status %ld", lana, status);
472 return (memcmp(astat.status.adapter_address, kWLA_MAC, 6) == 0);
475 // Get the netbios named used/to-be-used by the AFS SMB server.
476 // IF <lana specified> THEN
477 // Use specified lana
479 // Look for an adapter named "AFS", failing which,
480 // look for a loopback adapter.
482 // IF lana is for a loopback && !IsGateway THEN
483 // IF netbios name is specified THEN
484 // use specified netbios name
489 // use netbios name "<hostname>-AFS"
491 // Return ERROR_SUCCESS if netbios name was successfully generated.
492 // Returns the lana number to use in *pLana (if pLana is non-NULL) and also
493 // the IsGateway setting in *pIsGateway (if pIsGateway is non-NULL).
494 // the type of name returned.
496 // buffer is assumed to hold at least MAX_NB_NAME_LENGTH bytes.
499 // LANA_NETBIOS_NAME_IN : Use the values of *pLana and *pIsGateway as [in] parameters.
500 // LANA_NETBIOS_NAME_SUFFIX : Only return the suffix of netbios name
501 // LANA_NETBIOS_NAME_FULL : Return full netbios name
502 extern "C" long lana_GetUncServerNameEx(char *buffer, lana_number_t * pLana, int * pIsGateway, int flags) {
507 int regGateway, regNoFindLanaByName;
508 TCHAR regNbName[MAX_NB_NAME_LENGTH];
509 TCHAR nbName[MAX_NB_NAME_LENGTH];
510 TCHAR hostname[MAX_COMPUTERNAME_LENGTH+1];
512 rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,szAFSConfigKeyName,0,KEY_READ,&hkConfig);
513 if(rv == ERROR_SUCCESS) {
514 if(!(flags & LANA_NETBIOS_NAME_IN) || !pLana) {
515 dummyLen = sizeof(regLana);
516 rv = RegQueryValueEx(hkConfig, szLanAdapterValue, NULL, NULL, (LPBYTE) ®Lana, &dummyLen);
517 if(rv != ERROR_SUCCESS) regLana = -1;
521 if(!(flags & LANA_NETBIOS_NAME_IN) || !pIsGateway) {
522 dummyLen = sizeof(regGateway);
523 rv = RegQueryValueEx(hkConfig, szIsGatewayValue, NULL, NULL, (LPBYTE) ®Gateway, &dummyLen);
524 if(rv != ERROR_SUCCESS) regGateway = 0;
526 regGateway = *pIsGateway;
528 dummyLen = sizeof(regNoFindLanaByName);
529 rv = RegQueryValueEx(hkConfig, szNoFindLanaByName, NULL, NULL, (LPBYTE) ®NoFindLanaByName, &dummyLen);
530 if(rv != ERROR_SUCCESS) regNoFindLanaByName = 0;
532 // Do not care if the call fails for insufficient buffer size. We are not interested
533 // in netbios names over 15 chars.
534 dummyLen = sizeof(regNbName);
535 rv = RegQueryValueEx(hkConfig, szNetbiosNameValue, NULL, NULL, (LPBYTE) ®NbName, &dummyLen);
536 if(rv != ERROR_SUCCESS) regNbName[0] = 0;
537 else regNbName[15] = 0;
539 RegCloseKey(hkConfig);
541 if(flags & LANA_NETBIOS_NAME_IN) {
542 regLana = (pLana)? *pLana: -1;
543 regGateway = (pIsGateway)? *pIsGateway: 0;
548 regNoFindLanaByName = 0;
552 if(regLana < 0 || regLana > MAX_LANA)
556 LANAINFO *lanaInfo = NULL;
557 int nLana = LANA_INVALID;
559 if (!regNoFindLanaByName)
560 lanaInfo = lana_FindLanaByName("AFS");
561 if(lanaInfo != NULL) {
562 nLana = lanaInfo[0].lana_number;
565 nLana = LANA_INVALID;
567 if(nLana == LANA_INVALID && !regGateway) {
568 nLana = lana_FindLoopback();
570 if(nLana != LANA_INVALID)
575 (regLana >=0 && lana_IsLoopback((lana_number_t) regLana))) {
576 strncpy(nbName,regNbName,15);
582 if(flags & LANA_NETBIOS_NAME_SUFFIX) {
583 strcpy(nbName,"-AFS");
585 dummyLen = sizeof(hostname);
586 // assume we are not a cluster.
587 rv = GetComputerName(hostname, &dummyLen);
588 if(!SUCCEEDED(rv)) { // should not happen, but...
591 strncpy(nbName, hostname, 11);
593 if(dot = strchr(nbName,'.'))
595 strcat(nbName,"-AFS");
599 if(pLana) *pLana = regLana;
600 if(pIsGateway) *pIsGateway = regGateway;
602 strcpy(buffer, nbName);
604 return ERROR_SUCCESS;
607 extern "C" void lana_GetUncServerNameDynamic(int lanaNumber, BOOL isGateway, TCHAR *name, int type) {
608 char szName[MAX_NB_NAME_LENGTH];
609 lana_number_t lana = (lana_number_t) lanaNumber;
610 int gateway = (int) isGateway;
612 if(SUCCEEDED(lana_GetUncServerNameEx(szName, &lana, &gateway, LANA_NETBIOS_NAME_IN | type))) {
614 mbswcs(name,szName,MAX_NB_NAME_LENGTH);
616 strncpy(name,szName,MAX_NB_NAME_LENGTH);
622 extern "C" void lana_GetUncServerName(TCHAR *name, int type) {
623 char szName[MAX_NB_NAME_LENGTH];
625 if(SUCCEEDED(lana_GetUncServerNameEx(szName,NULL,NULL,type))) {
627 mbswcs(name,szName,MAX_NB_NAME_LENGTH);
629 strncpy(name,szName,MAX_NB_NAME_LENGTH);
636 extern "C" void lana_GetAfsNameString(int lanaNumber, BOOL isGateway, TCHAR* name)
638 TCHAR netbiosName[32];
639 lana_GetUncServerNameDynamic(lanaNumber, isGateway, netbiosName, LANA_NETBIOS_NAME_FULL);
640 _stprintf(name, _T("Your UNC name to reach the root of AFS is \\\\%s\\all"), netbiosName);
643 extern "C" void lana_GetNetbiosName(LPTSTR pszName, int type)
646 TCHAR name[MAX_NB_NAME_LENGTH];
649 memset(name, 0, sizeof(name));
650 if (GetVersion() >= 0x80000000) // not WindowsNT
652 if (type == LANA_NETBIOS_NAME_SUFFIX)
654 _tcscpy(pszName, TEXT("-afs"));
658 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,szAFSConfigKeyName,0,KEY_READ,&hkCfg) == ERROR_SUCCESS) {
659 dummyLen = sizeof(name);
660 if(RegQueryValueEx(hkCfg,TEXT("Gateway"),NULL,NULL,(LPBYTE) name,&dummyLen) == ERROR_SUCCESS)
665 if (_tcslen(name) == 0)
667 _tcscpy(pszName, TEXT("unknown"));
671 _tcscpy(pszName, name);
672 _tcscat(pszName, TEXT("-afs"));
676 lana_GetUncServerName(name,type);
678 _tcscpy(pszName, name);