lanahelper-library-20040305
[openafs.git] / src / WINNT / afsd / lanahelper.cpp
index 7fd8601..5a8d368 100644 (file)
@@ -6,16 +6,26 @@
 #include <shellapi.h>
 #include <objbase.h>
 #include <shlobj.h>
-#include <shlwapi.h>
 #include <wtypes.h>
 #include <string.h>
 #include <malloc.h>
-#include "lanahelper.h"
+#include <lanahelper.h>
 
+#define NOLOGGING
+#ifndef NOLOGGING
+extern "C" {
+    void afsi_log(...);
+}
+#endif
 
-extern "C" void afsi_log(...);
+static const char *szAFSConfigKeyName = "SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon\\Parameters";
+static const char *szNetbiosNameValue = "NetbiosName";
+static const char *szIsGatewayValue = "IsGateway";
+static const char *szLanAdapterValue = "LanAdapter";
+static const char *szNoFindLanaByName = "NoFindLanaByName";
 
-static HRESULT getname_shellfolder(WCHAR *wGuid, WCHAR *wName, int NameSize)
+// Use the IShellFolder API to get the connection name for the given Guid.
+static HRESULT lana_ShellGetNameFromGuidW(WCHAR *wGuid, WCHAR *wName, int NameSize)
 {
     // This is the GUID for the network connections folder. It is constant.
     // {7007ACC7-3202-11D1-AAD2-00805FC1270E}
@@ -40,13 +50,17 @@ static HRESULT getname_shellfolder(WCHAR *wGuid, WCHAR *wName, int NameSize)
     // Get the shell allocator.
     HRESULT hr = SHGetMalloc(&pShellMalloc);
     if (SUCCEEDED(hr))
+    {
         // Create an instance of the network connections folder.
         hr = CoCreateInstance(CLSID_NetworkConnections, NULL,
                              CLSCTX_INPROC_SERVER, IID_IShellFolder,
                              reinterpret_cast<LPVOID *>(&pShellFolder));
-    if (SUCCEEDED(hr))
+    }
+    if (SUCCEEDED(hr)) 
+    {
         hr = pShellFolder->ParseDisplayName(NULL, NULL, szAdapterGuid, NULL,
                                            &pidl, NULL);
+    }
     if (SUCCEEDED(hr)) {
         // Get the display name; this returns the friendly name.
         STRRET sName;
@@ -98,12 +112,15 @@ extern "C" int lana_GetNameFromGuid(char *Guid, char **Name)
         // The IShellFolder interface is not implemented on this platform.
         // Try the (undocumented) HrLanConnectionNameFromGuidOrPath API
         // from the netman DLL.
+#ifndef NOLOGGING
         afsi_log("IShellFolder API not implemented, trying HrLanConnectionNameFromGuidOrPath");
+#endif
         hNetMan = LoadLibrary("netman.dll");
         if (hNetMan == NULL) {
             free(wGuid);
             return -1;
         }
+        /* Super Secret Microsoft Call */
         HrLanProc = (HrLanProcAddr) GetProcAddress(hNetMan,
                                                    "HrLanConnectionNameFromGuidOrPath");
         if (HrLanProc == NULL) {
@@ -116,8 +133,10 @@ extern "C" int lana_GetNameFromGuid(char *Guid, char **Name)
     }
     free(wGuid);
     if (FAILED(status)) {
+#ifndef NOLOGGING
         afsi_log("lana_GetNameFromGuid: failed to get connection name (status %ld)",
                 status);
+#endif
         return -1;
     }
 
@@ -127,7 +146,9 @@ extern "C" int lana_GetNameFromGuid(char *Guid, char **Name)
     if (name == NULL)
         return -1;
     WideCharToMultiByte(CP_ACP, 0, wName, -1, name, size, NULL, NULL);
+#ifndef NOLOGGING
     afsi_log("Connection name for %s is '%s'", Guid, name);
+#endif
     if (*Name)
         *Name = name;
     else
@@ -135,8 +156,11 @@ extern "C" int lana_GetNameFromGuid(char *Guid, char **Name)
     return 0;
 }
 
-// Find the lana number for the given connection name.
-extern "C" lana_number_t lana_FindLanaByName(const char *LanaName)
+// Return an array of LANAINFOs corresponding to a connection named LanaName
+// (NULL LanaName matches all connections), and has an IPv4 binding. Returns
+// NULL if something goes wrong.
+// NOTE: caller must free the returned block if non NULL.
+extern "C" LANAINFO * lana_FindLanaByName(const char *LanaName)
 {
     const char RegNetBiosLinkageKeyName[] =
         "System\\CurrentControlSet\\Services\\NetBios\\Linkage";
@@ -157,26 +181,34 @@ extern "C" lana_number_t lana_FindLanaByName(const char *LanaName)
     char *pBind;
     char *p;
 
+    LANAINFO * lanainfo;
+
     // Open the NetBios Linkage key.
     status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegNetBiosLinkageKeyName, 0, 
                           KEY_QUERY_VALUE, &hkey);
         
-    if (status != ERROR_SUCCESS) { 
+    if (status != ERROR_SUCCESS) {
+#ifndef NOLOGGING
         afsi_log("Failed to open NetBios Linkage key (status %ld)", status);
-        return LANA_INVALID;
+#endif
+        return NULL;
     }
 
     // Read the lana map.
     status = RegQueryValueEx(hkey, "LanaMap", 0, &type,
                          (BYTE *) &lanamap, &lanamapsize);
     if (status != ERROR_SUCCESS) {
+#ifndef NOLOGGING
         afsi_log("Failed to read LanaMap (status %ld)", status);
+#endif
         RegCloseKey(hkey);
-        return LANA_INVALID;
+        return NULL;
     }
     if (lanamapsize == 0) {
+#ifndef NOLOGGING
         afsi_log("No data in LanaMap");
-        return LANA_INVALID;
+#endif
+        return NULL;
     }
     nlana = lanamapsize / sizeof(lanamap[0]);
 
@@ -187,26 +219,54 @@ extern "C" lana_number_t lana_FindLanaByName(const char *LanaName)
     if (status == ERROR_SUCCESS && bindpathsize != 0) {
         bindpaths = (char *) malloc(bindpathsize * sizeof(char));
         if (bindpaths == NULL) {
+#ifndef NOLOGGING
             afsi_log("Cannot allocate %ld bytes for bindpaths", bindpathsize);
+#endif
             RegCloseKey(hkey);
-            return LANA_INVALID;
+            return NULL;
         }
         status = RegQueryValueEx(hkey, "Bind", 0, &type, 
                                  (BYTE *) bindpaths, &bindpathsize);
     }
     RegCloseKey(hkey);
     if (status != ERROR_SUCCESS) {
+#ifndef NOLOGGING
         afsi_log("Failed to read bind paths (status %ld)", status);
+#endif
         if (bindpaths != NULL)
             free(bindpaths);
-        return LANA_INVALID;
+        return NULL;
       }
     if (bindpathsize == 0) {
+#ifndef NOLOGGING
         afsi_log("No bindpath data");
+#endif
         if (bindpaths != NULL)
             free(bindpaths);
-        return LANA_INVALID;
+        return NULL;
+    }
+
+    if (LanaName)
+    {
+        lanainfo = (LANAINFO *) malloc(sizeof(LANAINFO)*2);
+        if(lanainfo == NULL) {
+            free(bindpaths);
+            return NULL;
+        }
+        memset(lanainfo, 0, sizeof(LANAINFO) * 2);
+        lanainfo[0].lana_number = LANA_INVALID;
+    }
+    else
+    {
+        lanainfo = (LANAINFO *) malloc(sizeof(LANAINFO)*(nlana+1));
+        if(lanainfo == NULL) {
+            free(bindpaths);
+            return NULL;
+        }
+        memset(lanainfo, 0, sizeof(LANAINFO) * (nlana+1));
     }
+    
+    int index = 0;
 
     // Iterate over the lana map entries and bind paths.
     for (i = 0, pBind = bindpaths; i < nlana;
@@ -215,7 +275,7 @@ extern "C" lana_number_t lana_FindLanaByName(const char *LanaName)
         if ((lanamap[i].flags & 1) == 0)
             continue;
 
-               // check for a IPv4 binding
+               // check for an IPv4 binding
                if(!strstr(pBind,"_Tcpip_"))
                        continue;
 
@@ -235,19 +295,34 @@ extern "C" lana_number_t lana_FindLanaByName(const char *LanaName)
         *++p = '\0';                    // Ignore anything after the GUID.
         status = lana_GetNameFromGuid(guid, &name);
         free(guid);
-        if (status == 0 && name != 0) {
-            status = strcmp(name, LanaName);
-            free(name);
-            if (status == 0) {
-                free(bindpaths);
-               afsi_log("lana_FindLanaByName: Found lana %d for %s",
-                        lanamap[i].number, LanaName);
-               return lanamap[i].number;
-           }
-       }
+
+        if (status == 0 && name != 0)
+        {
+            if (LanaName)
+            {
+                if (strcmp(name, LanaName) ==0)
+                {
+                    lanainfo[index].lana_number = lanamap[i].number;
+                    _tcscpy(lanainfo[index].lana_name, name);
+                    free(name);
+                    index++;
+                    break;
+                }
+            }
+            else
+            {
+                lanainfo[index].lana_number = lanamap[i].number;
+                _tcscpy(lanainfo[index].lana_name, name);
+                free(name);
+                index++;
+            }
+        }
     }
+
+    lanainfo[index].lana_number = LANA_INVALID;
+
     free(bindpaths);
-    return LANA_INVALID;
+    return lanainfo;
 }
 
 extern "C" lana_number_t lana_FindLoopback(void)
@@ -263,14 +338,18 @@ extern "C" lana_number_t lana_FindLoopback(void)
     ncb.ncb_length = sizeof(lana_list);
     status = Netbios(&ncb);
     if (status != 0) {
-       afsi_log("Netbios NCBENUM failed: status %ld", status);
-       return LANA_INVALID;
+#ifndef NOLOGGING
+        afsi_log("Netbios NCBENUM failed: status %ld", status);
+#endif
+        return LANA_INVALID;
     }
     for (i = 0; i < lana_list.length; i++) {
        if (lana_IsLoopback(lana_list.lana[i])) {
            // Found one, return it.
+#ifndef NOLOGGING
            afsi_log("lana_FindLoopback: Found LAN adapter %d",
                     lana_list.lana[i]);
+#endif
            return lana_list.lana[i];
        }
     }
@@ -279,6 +358,8 @@ extern "C" lana_number_t lana_FindLoopback(void)
 }
 
 // Is the given lana a Windows Loopback Adapter?
+// TODO: implement a better check for loopback
+// TODO: also check for proper bindings (IPv4)
 extern "C" BOOL lana_IsLoopback(lana_number_t lana)
 {
     NCB ncb;
@@ -300,8 +381,10 @@ extern "C" BOOL lana_IsLoopback(lana_number_t lana)
     if (status == 0)
         status = ncb.ncb_retcode;
     if (status != 0) {
-       afsi_log("NCBRESET failed: lana %u, status %ld", lana, status);
-       return FALSE;
+#ifndef NOLOGGING
+       afsi_log("NCBRESET failed: lana %u, status %ld", lana, status);
+#endif
+        return FALSE;
     }
 
     // Use the NCBASTAT command to get the adapter address.
@@ -315,8 +398,220 @@ extern "C" BOOL lana_IsLoopback(lana_number_t lana)
     if (status == 0)
         status = ncb.ncb_retcode;
     if (ncb.ncb_retcode != 0) {
+#ifndef NOLOGGING   
         afsi_log("NCBASTAT failed: lana %u, status %ld", lana, status);
-       return FALSE;
+#endif
+        return FALSE;
     }
     return (memcmp(astat.status.adapter_address, kWLA_MAC, 6) == 0);
 }
+
+// Get the netbios named used/to-be-used by the AFS SMB server.
+// IF <lana specified> THEN
+//     Use specified lana
+// ELSE
+//        Look for an adapter named "AFS", failing which,
+//     look for a loopback adapter.
+// ENDIF
+// IF lana is for a loopback && !IsGateway THEN
+//    IF netbios name is specified THEN
+//       use specified netbios name
+//    ELSE
+//       use "AFS"
+//    ENDIF
+// ELSE
+//    use netbios name "<hostname>-AFS"
+// ENDIF
+// Return ERROR_SUCCESS if netbios name was successfully generated.
+// Returns the lana number to use in *pLana (if pLana is non-NULL) and also
+//         the IsGateway setting in *pIsGateway (if pIsGateway is non-NULL).
+//         the type of name returned.
+//
+// buffer is assumed to hold at least MAX_NB_NAME_LENGTH bytes.
+//
+// flags :
+//        LANA_NETBIOS_NAME_IN : Use the values of *pLana and *pIsGateway as [in] parameters.
+//        LANA_NETBIOS_NAME_SUFFIX : Only return the suffix of netbios name
+//               LANA_NETBIOS_NAME_FULL : Return full netbios name
+extern "C" long lana_GetUncServerNameEx(char *buffer, lana_number_t * pLana, int * pIsGateway, int flags) {
+    HKEY hkConfig;
+       DWORD dummyLen;
+       LONG rv;
+       int regLana;
+       int regGateway, regNoFindLanaByName;
+       char regNbName[MAX_NB_NAME_LENGTH];
+       char nbName[MAX_NB_NAME_LENGTH];
+       char hostname[MAX_COMPUTERNAME_LENGTH+1];
+
+       rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,szAFSConfigKeyName,0,KEY_READ,&hkConfig);
+       if(rv == ERROR_SUCCESS) {
+               if(!(flags & LANA_NETBIOS_NAME_IN) || !pLana) {
+                       dummyLen = sizeof(regLana);
+                       rv = RegQueryValueEx(hkConfig, szLanAdapterValue, NULL, NULL, (LPBYTE) &regLana, &dummyLen);
+                       if(rv != ERROR_SUCCESS) regLana = -1;
+               } else
+                       regLana = *pLana;
+
+               if(!(flags & LANA_NETBIOS_NAME_IN) || !pIsGateway) {
+                       dummyLen = sizeof(regGateway);
+                       rv = RegQueryValueEx(hkConfig, szIsGatewayValue, NULL, NULL, (LPBYTE) &regGateway, &dummyLen);
+                       if(rv != ERROR_SUCCESS) regGateway = 0;
+               } else
+                       regGateway = *pIsGateway;
+
+               dummyLen = sizeof(regNoFindLanaByName);
+               rv = RegQueryValueEx(hkConfig, szNoFindLanaByName, NULL, NULL, (LPBYTE) &regNoFindLanaByName, &dummyLen);
+               if(rv != ERROR_SUCCESS) regNoFindLanaByName = 0;
+
+               // Do not care if the call fails for insufficient buffer size.  We are not interested
+               // in netbios names over 15 chars.
+               dummyLen = sizeof(regNbName);
+               rv = RegQueryValueEx(hkConfig, szNetbiosNameValue, NULL, NULL, (LPBYTE) &regNbName, &dummyLen);
+               if(rv != ERROR_SUCCESS) regNbName[0] = 0;
+               else regNbName[15] = 0;
+
+               RegCloseKey(hkConfig);
+       } else {
+               if(flags & LANA_NETBIOS_NAME_IN) {
+                       regLana = (pLana)? *pLana: -1;
+                       regGateway = (pIsGateway)? *pIsGateway: 0;
+               } else {
+                       regLana = -1;
+                       regGateway = 0;
+               }
+        regNoFindLanaByName = 0;
+               regNbName[0] = 0;
+       }
+
+    if(regLana < 0 || regLana > MAX_LANA) 
+        regLana = -1;
+
+       if(regLana == -1) {
+               LANAINFO *lanaInfo = NULL;
+        int nLana = LANA_INVALID;
+
+        if (!regNoFindLanaByName)
+            lanaInfo = lana_FindLanaByName("AFS");
+               if(lanaInfo != NULL) {
+            nLana = lanaInfo[0].lana_number;
+                       free(lanaInfo);
+               } else
+                       nLana = LANA_INVALID;
+
+               if(nLana == LANA_INVALID && !regGateway) {
+                       nLana = lana_FindLoopback();
+               }
+               if(nLana != LANA_INVALID) 
+            regLana = nLana;
+       }
+
+       if(regLana >=0 && lana_IsLoopback((lana_number_t) regLana)) {
+               if(regNbName[0]) {
+                       strncpy(nbName,regNbName,15);
+                       nbName[16] = 0;
+                       strupr(nbName);
+               }
+               else
+                       strcpy(nbName,"AFS");
+       } else {
+               char * dot;
+
+               if(flags & LANA_NETBIOS_NAME_SUFFIX) {
+                       strcpy(nbName,"-AFS");
+               } else {
+                       dummyLen = sizeof(hostname);
+                       // assume we are not a cluster.
+                       rv = GetComputerName(hostname, &dummyLen);
+                       if(!SUCCEEDED(rv)) { // should not happen, but...
+                               return rv;
+                       }
+                       strncpy(nbName, hostname, 11);
+                       nbName[11] = 0;
+                       if(dot = strchr(nbName,'.'))
+                               *dot = 0;
+                       strcat(nbName,"-AFS");
+               }
+       }
+
+       if(pLana) *pLana = regLana;
+       if(pIsGateway) *pIsGateway = regGateway;
+
+       strcpy(buffer, nbName);
+
+       return ERROR_SUCCESS;
+}
+
+extern "C" void lana_GetUncServerNameDynamic(int lanaNumber, BOOL isGateway, TCHAR *name, int type) {
+       char szName[MAX_NB_NAME_LENGTH];
+       lana_number_t lana = (lana_number_t) lanaNumber;
+       int gateway = (int) isGateway;
+
+       if(SUCCEEDED(lana_GetUncServerNameEx(szName, &lana, &gateway, LANA_NETBIOS_NAME_IN | type))) {
+#ifdef _UNICODE
+               mbswcs(name,szName,MAX_NB_NAME_LENGTH);
+#else
+               strncpy(name,szName,MAX_NB_NAME_LENGTH);
+#endif
+       } else
+               *name = _T('\0');
+}
+
+extern "C" void lana_GetUncServerName(TCHAR *name, int type) {
+       char szName[MAX_NB_NAME_LENGTH];
+
+       if(SUCCEEDED(lana_GetUncServerNameEx(szName,NULL,NULL,type))) {
+#ifdef _UNICODE
+               mbswcs(name,szName,MAX_NB_NAME_LENGTH);
+#else
+               strncpy(name,szName,MAX_NB_NAME_LENGTH);
+#endif
+       } else {
+        *name = _T('\0');
+       }
+}
+
+extern "C" void lana_GetAfsNameString(int lanaNumber, BOOL isGateway, TCHAR* name)
+{
+    lana_GetUncServerNameDynamic(lanaNumber, isGateway, name,LANA_NETBIOS_NAME_FULL);
+    _stprintf(name, _T("Your UNC name to reach the root of AFS is \\\\%s\\all"), name);
+}
+
+extern "C" void lana_GetNetbiosName(LPTSTR pszName, int type)
+{
+       HKEY hkCfg;
+    TCHAR name[MAX_NB_NAME_LENGTH];
+       DWORD dummyLen;
+
+    memset(name, 0, sizeof(name));
+    if (GetVersion() >= 0x80000000) // not WindowsNT
+    {
+        if (type == LANA_NETBIOS_NAME_SUFFIX)
+        {
+            _tcscpy(pszName, TEXT("-afs"));
+            return;
+        }
+
+               if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,szAFSConfigKeyName,0,KEY_READ,&hkCfg) == ERROR_SUCCESS) {
+                       dummyLen = sizeof(name);
+                       if(RegQueryValueEx(hkCfg,TEXT("Gateway"),NULL,NULL,(LPBYTE) name,&dummyLen) == ERROR_SUCCESS)
+                               name[0] = _T('\0');
+                       RegCloseKey(hkCfg);
+               }
+
+        if (_tcslen(name) == 0)
+        {
+            _tcscpy(pszName, TEXT("unknown"));
+            return;
+        }
+
+               _tcscpy(pszName, name);
+        _tcscat(pszName, TEXT("-afs"));
+        return;
+    }
+
+    lana_GetUncServerName(name,type);
+       _tcslwr(name);
+    _tcscpy(pszName, name);
+    return;
+}
+