File: Configuration\MsdtcClusterUtils.cs
Project: ndp\cdf\src\WCF\Tools\WsatConfig\WsatUI.csproj (WsatUI)
//------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------------------------
 
namespace Microsoft.Tools.ServiceModel.WsatConfig
{
    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Security.Permissions;
    using System.Collections.Generic;
    using System.Text;
 
    using Microsoft.Win32;
    
    static class MsdtcClusterUtils
    {
        static string clusterName = null;
        const string MsdtcResourceTypeName = "Distributed Transaction Coordinator";
 
        [SecurityCritical]
        internal static bool IsClusterServer(string machineName)
        {
#pragma warning suppress 56523
            SafeHCluster hCluster = SafeNativeMethods.OpenCluster(Utilities.IsLocalMachineName(machineName) ? null : machineName);
            bool result = false;
            using (hCluster)
            {
                result = !hCluster.IsInvalid;
            }
            return result;
        }
 
#if WSAT_UI
        [SecurityCritical]
        internal static bool ResolveVirtualServerName(string machineName, string dtcResourceName, out string virtualServer)
        {
            virtualServer = string.Empty;
#pragma warning suppress 56523
            SafeHCluster hCluster = SafeNativeMethods.OpenCluster(Utilities.IsLocalMachineName(machineName) ? null : machineName);
            if (hCluster.IsInvalid)
            {
                return false;
            }
 
            using (hCluster)
            {
#pragma warning suppress 56523
                SafeHClusEnum hEnum = SafeNativeMethods.ClusterOpenEnum(hCluster, ClusterEnum.Resource);
                if (hEnum.IsInvalid)
                {
                    return false;
                }
 
                using (hEnum)
                {
                    uint index = 0;
                    bool moreToEnumerate;
                    SafeHResource hResource;
                    do
                    {
                        string theResourceName = string.Empty;
                        moreToEnumerate = GetResourceFromEnumeration(hCluster, hEnum, index, out hResource, out theResourceName);
                        if (moreToEnumerate)
                        {
                            if (IsTransactionManager(hResource))
                            {
                                if (string.CompareOrdinal(theResourceName, dtcResourceName) == 0)
                                {
                                    virtualServer = GetResourceNetworkName(hResource);
                                    return true;
                                }
                            }
                            hResource.Dispose();
                            index++;
                        }
                    } while (moreToEnumerate);
                }
            }
            return false;
        }
#endif
 
        [SecurityCritical]
        internal static SafeHResource GetTransactionManagerClusterResource(string virtualServerName, out string[] nodes)
        {
            nodes = null;
 
#pragma warning suppress 56523
            SafeHCluster hCluster = SafeNativeMethods.OpenCluster(null);
            if (hCluster.IsInvalid)
            {
                return null;
            }
 
            using (hCluster)
            {
                //W2k3 cluster
                if (Utilities.OSMajor <= 5)
                {
                    if (Utilities.IsLocalMachineName(virtualServerName))
                    {
                        virtualServerName = GetClusterName(hCluster);
                    }
                }
#pragma warning suppress 56523
                SafeHClusEnum hEnum = SafeNativeMethods.ClusterOpenEnum(hCluster, ClusterEnum.Resource);
                if (hEnum.IsInvalid)
                {
                    return null;
                }
 
                using (hEnum)
                {
                    uint index = 0;
                    bool moreToEnumerate;
                    SafeHResource hResource;
                    do
                    {
                        string theResourceName = string.Empty; 
                        moreToEnumerate = GetResourceFromEnumeration(hCluster, hEnum, index, out hResource, out theResourceName);
                        if (moreToEnumerate)
                        {                            
                            if (IsTransactionManager(hResource))
                            {
                                string networkName = GetResourceNetworkName(hResource);
                                Utilities.Log("Resource network name: " + networkName);
                                if (Utilities.SafeCompare(networkName, virtualServerName))
                                {
                                    nodes = GetClusterNodes(hCluster);
                                    return hResource;
                                }
                            }
                            hResource.Dispose();
                            index++;
                        }
                    } while (moreToEnumerate);
                }
            }
 
            return null;
        }
 
        static string GetClusterName(SafeHCluster hCluster)
        {
            if (clusterName == null)
            {
                uint cch = 255u;
                StringBuilder sb = new StringBuilder((int)cch);
                int ret = SafeNativeMethods.GetClusterInformation(
                    hCluster,
                    sb,
                    ref cch,
                    IntPtr.Zero);
                if (ret != SafeNativeMethods.ERROR_SUCCESS || ret == SafeNativeMethods.ERROR_MORE_DATA)
                {
                    if (ret == SafeNativeMethods.ERROR_MORE_DATA)
                    {
                        sb = new StringBuilder((int)cch);
                        ret = SafeNativeMethods.GetClusterInformation(
                                                        hCluster,
                                                        sb,
                                                        ref cch,
                                                        IntPtr.Zero);
                        if (ret != SafeNativeMethods.ERROR_SUCCESS)
                        {
                            return null;
                        }
                    }
                }
                clusterName = sb.ToString();
            }
            return clusterName;
        }
 
        static bool IsTransactionManager(SafeHResource hResource)
        {
            Utilities.Log("Entered IsTransactionManager - ");
 
            string resourceType = GetResourceType(hResource);
            Utilities.Log("Resource type name: " + resourceType);
 
            return string.CompareOrdinal(resourceType, MsdtcResourceTypeName) == 0;
        }
 
        static string GetResourceType(SafeHResource hResource)
        {
            return IssueClusterResourceControlString(hResource, ClusterResourceControlCode.GetResourceType);
        }
 
        static string GetResourceNetworkName(SafeHResource hResource)
        {
            // On Vista and above, this API can be called with null and 0 to discover the true length
            // of the return buffer. However, on Win2003 that causes the API to fail with
            // ERROR_DEPENDENCY_NOT_FOUND. To play it safe, we simply pre-allocate a buffer up front
            // that should be enough to catch the two possible cases:
            // - 15-character NetBios names
            // - 63-character DNS labels
            
            uint cch = 64;
            StringBuilder sb = new StringBuilder((int)cch);
#pragma warning suppress 56523
            if (SafeNativeMethods.GetClusterResourceNetworkName(hResource, sb, ref cch))
            {
                return sb.ToString();
            }
 
            return null;
        }
 
        // return true if more items to enumerate, otherwise false
        [SecurityCritical]
        static bool GetResourceFromEnumeration(SafeHCluster hCluster,
                                               SafeHClusEnum hEnum,
                                               uint index, out SafeHResource hResource, out string theResourceName)
        {
            uint type, cch = 0;
            hResource = null;
            theResourceName = string.Empty;
 
            uint ret = SafeNativeMethods.ClusterEnum(hEnum, index, out type, null, ref cch);
            if (ret == SafeNativeMethods.ERROR_NO_MORE_ITEMS)
            {
                return false;
            }
            else if (ret == SafeNativeMethods.ERROR_SUCCESS || ret == SafeNativeMethods.ERROR_MORE_DATA)
            {
                StringBuilder sb = new StringBuilder((int)(++cch));
                ret = SafeNativeMethods.ClusterEnum(hEnum, index, out type, sb, ref cch);
 
                if (ret == SafeNativeMethods.ERROR_SUCCESS)
                {                    
                    string resourceName = sb.ToString();
 
#pragma warning suppress 56523
                    hResource = SafeNativeMethods.OpenClusterResource(hCluster, resourceName);
                    if (hResource.IsInvalid)
                    {
                        hResource = null;
                    }
                    else
                    {
                        theResourceName = resourceName;
                    }
                }
            }
 
            return true;
        }
        
        static string[] GetClusterNodes(SafeHCluster hCluster)
        {
#pragma warning suppress 56523
            SafeHClusEnum hEnum = SafeNativeMethods.ClusterOpenEnum(hCluster, ClusterEnum.Node);
            if (hEnum.IsInvalid)
            {
                return null;
            }
 
            List<string> nodeList = new List<string>(2); // 2 nodes are a typical cluster configuration            
 
            using (hEnum)
            {
                uint ret, index = 0;
                do
                {
                    uint type, cch = 0;
                    ret = SafeNativeMethods.ClusterEnum(hEnum, index, out type, null, ref cch);
                    if (ret == SafeNativeMethods.ERROR_NO_MORE_ITEMS)
                    {
                        break;
                    }
                    else if (ret == SafeNativeMethods.ERROR_SUCCESS || ret == SafeNativeMethods.ERROR_MORE_DATA)
                    {
                        StringBuilder sb = new StringBuilder((int)(++cch));
                        ret = SafeNativeMethods.ClusterEnum(hEnum, index, out type, sb, ref cch);
                        if (ret == SafeNativeMethods.ERROR_SUCCESS)
                        {
                            Utilities.Log("Found a node: [" + sb.ToString() + "]");
                            nodeList.Add(sb.ToString());
                        }
                    }
                    index++;
                } while (ret == SafeNativeMethods.ERROR_SUCCESS);
            }
 
            return nodeList.ToArray();
        }
 
        static byte[] IssueClusterResourceControl(SafeHResource hResource,
                                                  ClusterResourceControlCode code)
        {
            uint cb = 0;
            uint ret = SafeNativeMethods.ClusterResourceControl(hResource,
                                                                IntPtr.Zero,
                                                                code,
                                                                IntPtr.Zero,
                                                                0,
                                                                null,
                                                                0,
                                                                ref cb);
 
            if (ret == SafeNativeMethods.ERROR_SUCCESS || ret == SafeNativeMethods.ERROR_MORE_DATA)
            {
                byte[] buffer = new byte[cb];
                ret = SafeNativeMethods.ClusterResourceControl(hResource,
                                                               IntPtr.Zero,
                                                               code,
                                                               IntPtr.Zero,
                                                               0,
                                                               buffer,
                                                               cb,
                                                               ref cb);
 
                if (ret == SafeNativeMethods.ERROR_SUCCESS)
                {
                    return buffer;
                }
            }
 
            return null;
        }
 
        static string IssueClusterResourceControlString(SafeHResource hResource,
                                                        ClusterResourceControlCode code)
        {
            byte[] buffer = IssueClusterResourceControl(hResource, code);
            if (buffer == null)
            {
                return null;
            }
 
            try
            {
                return Encoding.Unicode.GetString(buffer, 0, buffer.Length - 2);
            }
            catch (ArgumentException)
            {
                return null;
            }
        }
    }
}