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 };
444 rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,AFSREG_CLT_SVC_PARAM_SUBKEY,0,KEY_READ,&hkConfig);
445 if (rv == ERROR_SUCCESS) {
446 rv = RegQueryValueEx(hkConfig, szForceLanaLoopback, NULL, NULL, (LPBYTE) ®Lana, &dummyLen);
447 RegCloseKey(hkConfig);
453 // Reset the adapter: in Win32, this is required for every process, and
454 // acts as an init call, not as a real hardware reset.
455 memset(&ncb, 0, sizeof(ncb));
456 ncb.ncb_command = NCBRESET;
457 ncb.ncb_callname[0] = 100;
458 ncb.ncb_callname[2] = 100;
459 ncb.ncb_lana_num = lana;
460 status = Netbios(&ncb);
462 status = ncb.ncb_retcode;
465 afsi_log("NCBRESET failed: lana %u, status %ld", lana, status);
470 // Use the NCBASTAT command to get the adapter address.
471 memset(&ncb, 0, sizeof(ncb));
472 ncb.ncb_command = NCBASTAT;
473 ncb.ncb_lana_num = lana;
474 strcpy((char *) ncb.ncb_callname, "* ");
475 ncb.ncb_buffer = (UCHAR *) &astat;
476 ncb.ncb_length = sizeof(astat);
477 status = Netbios(&ncb);
479 status = ncb.ncb_retcode;
480 if (ncb.ncb_retcode != 0) {
482 afsi_log("NCBASTAT failed: lana %u, status %ld", lana, status);
486 return (memcmp(astat.status.adapter_address, kWLA_MAC, 6) == 0);
489 // Get the netbios named used/to-be-used by the AFS SMB server.
490 // IF <lana specified> THEN
491 // Use specified lana
493 // Look for an adapter named "AFS", failing which,
494 // look for a loopback adapter.
496 // IF lana is for a loopback && !IsGateway THEN
497 // IF netbios name is specified THEN
498 // use specified netbios name
503 // use netbios name "<hostname>-AFS"
505 // Return ERROR_SUCCESS if netbios name was successfully generated.
506 // Returns the lana number to use in *pLana (if pLana is non-NULL) and also
507 // the IsGateway setting in *pIsGateway (if pIsGateway is non-NULL).
508 // the type of name returned.
510 // buffer is assumed to hold at least MAX_NB_NAME_LENGTH bytes.
513 // LANA_NETBIOS_NAME_IN : Use the values of *pLana and *pIsGateway as [in] parameters.
514 // LANA_NETBIOS_NAME_SUFFIX : Only return the suffix of netbios name
515 // LANA_NETBIOS_NAME_FULL : Return full netbios name
516 extern "C" long lana_GetUncServerNameEx(char *buffer, lana_number_t * pLana, int * pIsGateway, int flags) {
521 int regGateway, regNoFindLanaByName;
522 TCHAR regNbName[MAX_NB_NAME_LENGTH];
523 TCHAR nbName[MAX_NB_NAME_LENGTH];
524 TCHAR hostname[MAX_COMPUTERNAME_LENGTH+1];
526 rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,AFSREG_CLT_SVC_PARAM_SUBKEY,0,KEY_READ,&hkConfig);
527 if(rv == ERROR_SUCCESS) {
528 if(!(flags & LANA_NETBIOS_NAME_IN) || !pLana) {
529 dummyLen = sizeof(regLana);
530 rv = RegQueryValueEx(hkConfig, szLanAdapterValue, NULL, NULL, (LPBYTE) ®Lana, &dummyLen);
531 if(rv != ERROR_SUCCESS) regLana = -1;
535 if(!(flags & LANA_NETBIOS_NAME_IN) || !pIsGateway) {
536 dummyLen = sizeof(regGateway);
537 rv = RegQueryValueEx(hkConfig, szIsGatewayValue, NULL, NULL, (LPBYTE) ®Gateway, &dummyLen);
538 if(rv != ERROR_SUCCESS) regGateway = 0;
540 regGateway = *pIsGateway;
542 dummyLen = sizeof(regNoFindLanaByName);
543 rv = RegQueryValueEx(hkConfig, szNoFindLanaByName, NULL, NULL, (LPBYTE) ®NoFindLanaByName, &dummyLen);
544 if(rv != ERROR_SUCCESS) regNoFindLanaByName = 0;
546 // Do not care if the call fails for insufficient buffer size. We are not interested
547 // in netbios names over 15 chars.
548 dummyLen = sizeof(regNbName);
549 rv = RegQueryValueEx(hkConfig, szNetbiosNameValue, NULL, NULL, (LPBYTE) ®NbName, &dummyLen);
550 if(rv != ERROR_SUCCESS) regNbName[0] = 0;
551 else regNbName[15] = 0;
553 RegCloseKey(hkConfig);
555 if(flags & LANA_NETBIOS_NAME_IN) {
556 regLana = (pLana)? *pLana: -1;
557 regGateway = (pIsGateway)? *pIsGateway: 0;
562 regNoFindLanaByName = 0;
566 if(regLana < 0 || regLana > MAX_LANA)
570 LANAINFO *lanaInfo = NULL;
571 int nLana = LANA_INVALID;
573 if (!regNoFindLanaByName)
574 lanaInfo = lana_FindLanaByName("AFS");
575 if(lanaInfo != NULL) {
576 nLana = lanaInfo[0].lana_number;
579 nLana = LANA_INVALID;
581 if(nLana == LANA_INVALID && !regGateway) {
582 nLana = lana_FindLoopback();
584 if(nLana != LANA_INVALID)
589 (regLana >=0 && lana_IsLoopback((lana_number_t) regLana))) {
590 strncpy(nbName,regNbName,15);
596 if(flags & LANA_NETBIOS_NAME_SUFFIX) {
597 strcpy(nbName,"-AFS");
599 dummyLen = sizeof(hostname);
600 // assume we are not a cluster.
601 rv = GetComputerName(hostname, &dummyLen);
602 if(!SUCCEEDED(rv)) { // should not happen, but...
605 strncpy(nbName, hostname, 11);
607 if(dot = strchr(nbName,'.'))
609 strcat(nbName,"-AFS");
613 if(pLana) *pLana = regLana;
614 if(pIsGateway) *pIsGateway = regGateway;
616 strcpy(buffer, nbName);
618 return ERROR_SUCCESS;
621 extern "C" void lana_GetUncServerNameDynamic(int lanaNumber, BOOL isGateway, TCHAR *name, int type) {
622 char szName[MAX_NB_NAME_LENGTH];
623 lana_number_t lana = (lana_number_t) lanaNumber;
624 int gateway = (int) isGateway;
626 if(SUCCEEDED(lana_GetUncServerNameEx(szName, &lana, &gateway, LANA_NETBIOS_NAME_IN | type))) {
628 mbswcs(name,szName,MAX_NB_NAME_LENGTH);
630 strncpy(name,szName,MAX_NB_NAME_LENGTH);
636 extern "C" void lana_GetUncServerName(TCHAR *name, int type) {
637 char szName[MAX_NB_NAME_LENGTH];
639 if(SUCCEEDED(lana_GetUncServerNameEx(szName,NULL,NULL,type))) {
641 mbswcs(name,szName,MAX_NB_NAME_LENGTH);
643 strncpy(name,szName,MAX_NB_NAME_LENGTH);
650 extern "C" void lana_GetAfsNameString(int lanaNumber, BOOL isGateway, TCHAR* name)
652 TCHAR netbiosName[32];
653 lana_GetUncServerNameDynamic(lanaNumber, isGateway, netbiosName, LANA_NETBIOS_NAME_FULL);
654 _stprintf(name, _T("Your UNC name to reach the root of AFS is \\\\%s\\all"), netbiosName);
657 extern "C" void lana_GetNetbiosName(LPTSTR pszName, int type)
660 TCHAR name[MAX_NB_NAME_LENGTH];
663 memset(name, 0, sizeof(name));
664 if (GetVersion() >= 0x80000000) // not WindowsNT
666 if (type == LANA_NETBIOS_NAME_SUFFIX)
668 _tcscpy(pszName, TEXT("-afs"));
672 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,AFSREG_CLT_SVC_PARAM_SUBKEY,0,KEY_READ,&hkCfg) == ERROR_SUCCESS) {
673 dummyLen = sizeof(name);
674 if(RegQueryValueEx(hkCfg,TEXT("Gateway"),NULL,NULL,(LPBYTE) name,&dummyLen) == ERROR_SUCCESS)
679 if (_tcslen(name) == 0)
681 _tcscpy(pszName, TEXT("unknown"));
685 _tcscpy(pszName, name);
686 _tcscat(pszName, TEXT("-afs"));
690 lana_GetUncServerName(name,type);
692 _tcscpy(pszName, name);