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 // Is the given lana a Windows Loopback Adapter?
398 // TODO: implement a better check for loopback
399 // TODO: also check for proper bindings (IPv4)
400 extern "C" BOOL lana_IsLoopback(lana_number_t lana)
404 ADAPTER_STATUS status;
405 NAME_BUFFER names[MAX_LANA+1];
407 unsigned char kWLA_MAC[6] = { 0x02, 0x00, 0x4c, 0x4f, 0x4f, 0x50 };
410 // Reset the adapter: in Win32, this is required for every process, and
411 // acts as an init call, not as a real hardware reset.
412 memset(&ncb, 0, sizeof(ncb));
413 ncb.ncb_command = NCBRESET;
414 ncb.ncb_callname[0] = 100;
415 ncb.ncb_callname[2] = 100;
416 ncb.ncb_lana_num = lana;
417 status = Netbios(&ncb);
419 status = ncb.ncb_retcode;
422 afsi_log("NCBRESET failed: lana %u, status %ld", lana, status);
427 // Use the NCBASTAT command to get the adapter address.
428 memset(&ncb, 0, sizeof(ncb));
429 ncb.ncb_command = NCBASTAT;
430 ncb.ncb_lana_num = lana;
431 strcpy((char *) ncb.ncb_callname, "* ");
432 ncb.ncb_buffer = (UCHAR *) &astat;
433 ncb.ncb_length = sizeof(astat);
434 status = Netbios(&ncb);
436 status = ncb.ncb_retcode;
437 if (ncb.ncb_retcode != 0) {
439 afsi_log("NCBASTAT failed: lana %u, status %ld", lana, status);
443 return (memcmp(astat.status.adapter_address, kWLA_MAC, 6) == 0);
446 // Get the netbios named used/to-be-used by the AFS SMB server.
447 // IF <lana specified> THEN
448 // Use specified lana
450 // Look for an adapter named "AFS", failing which,
451 // look for a loopback adapter.
453 // IF lana is for a loopback && !IsGateway THEN
454 // IF netbios name is specified THEN
455 // use specified netbios name
460 // use netbios name "<hostname>-AFS"
462 // Return ERROR_SUCCESS if netbios name was successfully generated.
463 // Returns the lana number to use in *pLana (if pLana is non-NULL) and also
464 // the IsGateway setting in *pIsGateway (if pIsGateway is non-NULL).
465 // the type of name returned.
467 // buffer is assumed to hold at least MAX_NB_NAME_LENGTH bytes.
470 // LANA_NETBIOS_NAME_IN : Use the values of *pLana and *pIsGateway as [in] parameters.
471 // LANA_NETBIOS_NAME_SUFFIX : Only return the suffix of netbios name
472 // LANA_NETBIOS_NAME_FULL : Return full netbios name
473 extern "C" long lana_GetUncServerNameEx(char *buffer, lana_number_t * pLana, int * pIsGateway, int flags) {
478 int regGateway, regNoFindLanaByName;
479 TCHAR regNbName[MAX_NB_NAME_LENGTH];
480 TCHAR nbName[MAX_NB_NAME_LENGTH];
481 TCHAR hostname[MAX_COMPUTERNAME_LENGTH+1];
483 rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,szAFSConfigKeyName,0,KEY_READ,&hkConfig);
484 if(rv == ERROR_SUCCESS) {
485 if(!(flags & LANA_NETBIOS_NAME_IN) || !pLana) {
486 dummyLen = sizeof(regLana);
487 rv = RegQueryValueEx(hkConfig, szLanAdapterValue, NULL, NULL, (LPBYTE) ®Lana, &dummyLen);
488 if(rv != ERROR_SUCCESS) regLana = -1;
492 if(!(flags & LANA_NETBIOS_NAME_IN) || !pIsGateway) {
493 dummyLen = sizeof(regGateway);
494 rv = RegQueryValueEx(hkConfig, szIsGatewayValue, NULL, NULL, (LPBYTE) ®Gateway, &dummyLen);
495 if(rv != ERROR_SUCCESS) regGateway = 0;
497 regGateway = *pIsGateway;
499 dummyLen = sizeof(regNoFindLanaByName);
500 rv = RegQueryValueEx(hkConfig, szNoFindLanaByName, NULL, NULL, (LPBYTE) ®NoFindLanaByName, &dummyLen);
501 if(rv != ERROR_SUCCESS) regNoFindLanaByName = 0;
503 // Do not care if the call fails for insufficient buffer size. We are not interested
504 // in netbios names over 15 chars.
505 dummyLen = sizeof(regNbName);
506 rv = RegQueryValueEx(hkConfig, szNetbiosNameValue, NULL, NULL, (LPBYTE) ®NbName, &dummyLen);
507 if(rv != ERROR_SUCCESS) regNbName[0] = 0;
508 else regNbName[15] = 0;
510 RegCloseKey(hkConfig);
512 if(flags & LANA_NETBIOS_NAME_IN) {
513 regLana = (pLana)? *pLana: -1;
514 regGateway = (pIsGateway)? *pIsGateway: 0;
519 regNoFindLanaByName = 0;
523 if(regLana < 0 || regLana > MAX_LANA)
527 LANAINFO *lanaInfo = NULL;
528 int nLana = LANA_INVALID;
530 if (!regNoFindLanaByName)
531 lanaInfo = lana_FindLanaByName("AFS");
532 if(lanaInfo != NULL) {
533 nLana = lanaInfo[0].lana_number;
536 nLana = LANA_INVALID;
538 if(nLana == LANA_INVALID && !regGateway) {
539 nLana = lana_FindLoopback();
541 if(nLana != LANA_INVALID)
546 (regLana >=0 && lana_IsLoopback((lana_number_t) regLana))) {
547 strncpy(nbName,regNbName,15);
553 if(flags & LANA_NETBIOS_NAME_SUFFIX) {
554 strcpy(nbName,"-AFS");
556 dummyLen = sizeof(hostname);
557 // assume we are not a cluster.
558 rv = GetComputerName(hostname, &dummyLen);
559 if(!SUCCEEDED(rv)) { // should not happen, but...
562 strncpy(nbName, hostname, 11);
564 if(dot = strchr(nbName,'.'))
566 strcat(nbName,"-AFS");
570 if(pLana) *pLana = regLana;
571 if(pIsGateway) *pIsGateway = regGateway;
573 strcpy(buffer, nbName);
575 return ERROR_SUCCESS;
578 extern "C" void lana_GetUncServerNameDynamic(int lanaNumber, BOOL isGateway, TCHAR *name, int type) {
579 char szName[MAX_NB_NAME_LENGTH];
580 lana_number_t lana = (lana_number_t) lanaNumber;
581 int gateway = (int) isGateway;
583 if(SUCCEEDED(lana_GetUncServerNameEx(szName, &lana, &gateway, LANA_NETBIOS_NAME_IN | type))) {
585 mbswcs(name,szName,MAX_NB_NAME_LENGTH);
587 strncpy(name,szName,MAX_NB_NAME_LENGTH);
593 extern "C" void lana_GetUncServerName(TCHAR *name, int type) {
594 char szName[MAX_NB_NAME_LENGTH];
596 if(SUCCEEDED(lana_GetUncServerNameEx(szName,NULL,NULL,type))) {
598 mbswcs(name,szName,MAX_NB_NAME_LENGTH);
600 strncpy(name,szName,MAX_NB_NAME_LENGTH);
607 extern "C" void lana_GetAfsNameString(int lanaNumber, BOOL isGateway, TCHAR* name)
609 TCHAR netbiosName[32];
610 lana_GetUncServerNameDynamic(lanaNumber, isGateway, netbiosName, LANA_NETBIOS_NAME_FULL);
611 _stprintf(name, _T("Your UNC name to reach the root of AFS is \\\\%s\\all"), netbiosName);
614 extern "C" void lana_GetNetbiosName(LPTSTR pszName, int type)
617 TCHAR name[MAX_NB_NAME_LENGTH];
620 memset(name, 0, sizeof(name));
621 if (GetVersion() >= 0x80000000) // not WindowsNT
623 if (type == LANA_NETBIOS_NAME_SUFFIX)
625 _tcscpy(pszName, TEXT("-afs"));
629 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,szAFSConfigKeyName,0,KEY_READ,&hkCfg) == ERROR_SUCCESS) {
630 dummyLen = sizeof(name);
631 if(RegQueryValueEx(hkCfg,TEXT("Gateway"),NULL,NULL,(LPBYTE) name,&dummyLen) == ERROR_SUCCESS)
636 if (_tcslen(name) == 0)
638 _tcscpy(pszName, TEXT("unknown"));
642 _tcscpy(pszName, name);
643 _tcscat(pszName, TEXT("-afs"));
647 lana_GetUncServerName(name,type);
649 _tcscpy(pszName, name);