File: System\ServiceModel\Channels\PnrpPeerResolver.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
 
namespace System.ServiceModel.Channels
{
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Net;
    using System.Net.Sockets;
    using System.Runtime;
    using System.Runtime.CompilerServices;
    using System.Runtime.ConstrainedExecution;
    using System.Runtime.InteropServices;
    using System.Runtime.Versioning;
    using System.Security.Permissions;
    using System.ServiceModel;
    using System.ServiceModel.Diagnostics;
    using System.ServiceModel.PeerResolvers;
    using System.ServiceProcess;
    using System.Text;
    using System.Threading;
    using Microsoft.Win32.SafeHandles;
 
    sealed class PnrpPeerResolver : PeerResolver
    {
        UnsafePnrpNativeMethods.PeerNameRegistrar registrar = new UnsafePnrpNativeMethods.PeerNameRegistrar();
        static bool isPnrpAvailable;
        static bool isPnrpInstalled;
        const UnsafePnrpNativeMethods.PnrpResolveCriteria resolutionScope = UnsafePnrpNativeMethods.PnrpResolveCriteria.NearestNonCurrentProcess;
        public const int PNRPINFO_HINT = 0x00000001;
 
        internal const int CommentLength = 80;
        internal const byte TcpTransport = 0x01;
        internal const byte PayloadVersion = 0x01;
        internal const char PathSeparator = '/';
        internal const int MinGuids = 1;
        internal const int MaxGuids = 2;
        internal const byte GuidEscape = 0xFF;
        internal const int MaxAddressEntries = 10;
        internal const int MaxAddressEntriesV1 = 4;
        internal const int MaxPathLength = 200; //this is known prefix+any guids
        static TimeSpan MaxTimeout = new TimeSpan(0, 10, 0); //PNRP validates the timeout to be no greater than 10 minutes
        static TimeSpan MaxResolveTimeout = new TimeSpan(0, 0, 45);
        internal const string GlobalCloudName = "Global_";
        static object SharedLock = new object();
        static Random randomGenerator = new Random();
        static TimeSpan TimeToWaitForStatus = TimeSpan.FromSeconds(15);
        PeerReferralPolicy referralPolicy = PeerReferralPolicy.Share;
 
        [Flags]
        internal enum PnrpResolveScope
        {
            None = 0,
            Global = 1,
            SiteLocal = 2,
            LinkLocal = 4,
            All = Global | SiteLocal | LinkLocal
        }
 
        static PnrpPeerResolver()
        {
            // determine if PNRP is installed
            isPnrpAvailable = false;
            using (UnsafePnrpNativeMethods.DiscoveryBase db = new UnsafePnrpNativeMethods.DiscoveryBase())
            {
                isPnrpInstalled = db.IsPnrpInstalled();
                isPnrpAvailable = db.IsPnrpAvailable(TimeToWaitForStatus);
            }
        }
 
        internal PnrpPeerResolver() : this(PeerReferralPolicy.Share) { }
        internal PnrpPeerResolver(PeerReferralPolicy referralPolicy)
        {
            this.referralPolicy = referralPolicy;
        }
 
        static Encoding PnrpEncoder
        {
            get
            {
                return System.Text.Encoding.UTF8;
            }
        }
 
        public static bool IsPnrpAvailable
        {
            get { return isPnrpAvailable; }
        }
 
        public static bool IsPnrpInstalled
        {
            get { return isPnrpInstalled; }
        }
 
        public static IPEndPoint GetHint()
        {
            byte[] bytes = new byte[16];
            lock (SharedLock)
            {
                randomGenerator.NextBytes(bytes);
            }
            return new IPEndPoint(new IPAddress(bytes), 0);
        }
 
        // Get the hint for the node in this process that handles this meshid
        // Get the nodeid for the current node - If the factory is using PrivatePeerNode then this can throw.
        // in which case use a hint of 0
        // The resolver prepends 0. to the meshid so we strip it off before locating the node.
        // false means that the search must include the current process.
        public static bool HasPeerNodeForMesh(string meshId)
        {
            PeerNodeImplementation node = null;
            return PeerNodeImplementation.TryGet(meshId, out node);
        }
 
        // PNRP doesn't support registering the same peername by the same identity in the same process.
        // Thus, we cannot test the PNRP resolver between two nodes in the same process without a little help.
        // By calling SetMeshExtensions, the resolver will register and resolver different ids, allowing two
        // nodes to work in the same process.
        string localExtension;
        string remoteExtension;
        internal void SetMeshExtensions(string local, string remote)
        {
            localExtension = local;
            remoteExtension = remote;
        }
 
        internal PnrpResolveScope EnumerateClouds(bool forResolve, Dictionary<uint, string> LinkCloudNames, Dictionary<uint, string> SiteCloudNames)
        {
            bool foundActive = false;
            PnrpResolveScope currentScope = PnrpResolveScope.None;
            LinkCloudNames.Clear();
            SiteCloudNames.Clear();
            UnsafePnrpNativeMethods.CloudInfo[] cloudInfos = UnsafePnrpNativeMethods.PeerCloudEnumerator.GetClouds();
 
            // If we are resolving we should first look for active clouds only
            // If we find some then we should return those to the caller
            // otherwise we should just load up with clouds
            if (forResolve)
            {
                foreach (UnsafePnrpNativeMethods.CloudInfo cloud in cloudInfos)
                {
                    if (cloud.State == UnsafePnrpNativeMethods.PnrpCloudState.Active)
                    {
                        if (cloud.Scope == UnsafePnrpNativeMethods.PnrpScope.Global)
                        {
                            currentScope |= PnrpResolveScope.Global;
                            foundActive = true;
                        }
                        else if (cloud.Scope == UnsafePnrpNativeMethods.PnrpScope.LinkLocal)
                        {
                            Fx.Assert(!String.IsNullOrEmpty(cloud.Name), "Unknown scope id in the IPAddress");
                            LinkCloudNames.Add(cloud.ScopeId, cloud.Name);
                            currentScope |= PnrpResolveScope.LinkLocal;
                            foundActive = true;
                        }
                        else if (cloud.Scope == UnsafePnrpNativeMethods.PnrpScope.SiteLocal)
                        {
                            Fx.Assert(!String.IsNullOrEmpty(cloud.Name), "Unknown scope id in the IPAddress");
                            SiteCloudNames.Add(cloud.ScopeId, cloud.Name);
                            currentScope |= PnrpResolveScope.SiteLocal;
                            foundActive = true;
                        }
                    }
                }
            }
 
            if (!foundActive)
            {
                foreach (UnsafePnrpNativeMethods.CloudInfo cloud in cloudInfos)
                {
                    if (!((cloud.State == UnsafePnrpNativeMethods.PnrpCloudState.Dead)
                        || (cloud.State == UnsafePnrpNativeMethods.PnrpCloudState.Disabled)
                        || (cloud.State == UnsafePnrpNativeMethods.PnrpCloudState.NoNet))
                       )
                    {
                        if (cloud.Scope == UnsafePnrpNativeMethods.PnrpScope.Global)
                        {
                            currentScope |= PnrpResolveScope.Global;
                            continue;
                        }
                        if (cloud.Scope == UnsafePnrpNativeMethods.PnrpScope.LinkLocal)
                        {
                            Fx.Assert(!String.IsNullOrEmpty(cloud.Name), "Unknown scope id in the IPAddress");
                            LinkCloudNames.Add(cloud.ScopeId, cloud.Name);
                            currentScope |= PnrpResolveScope.LinkLocal;
                        }
                        else if (cloud.Scope == UnsafePnrpNativeMethods.PnrpScope.SiteLocal)
                        {
                            Fx.Assert(!String.IsNullOrEmpty(cloud.Name), "Unknown scope id in the IPAddress");
                            SiteCloudNames.Add(cloud.ScopeId, cloud.Name);
                            currentScope |= PnrpResolveScope.SiteLocal;
                        }
                    }
                }
            }
            return currentScope;
        }
 
        class RegistrationHandle
        {
            public string PeerName;
            public List<string> Clouds;
            public RegistrationHandle(string peerName)
            {
                this.PeerName = peerName;
                Clouds = new List<string>();
            }
            public void AddCloud(string name)
            {
                this.Clouds.Add(name);
            }
        }
 
        public override bool CanShareReferrals
        {
            get
            {
                return referralPolicy != PeerReferralPolicy.DoNotShare;
            }
        }
        public override object Register(string meshId, PeerNodeAddress nodeAddress, TimeSpan timeout)
        {
            ThrowIfNoPnrp();
 
            PnrpRegistration globalEntry = null;
            PnrpRegistration[] linkEntries = null;
            PnrpRegistration[] siteEntries = null;
 
            RegistrationHandle regHandle = new RegistrationHandle(meshId);
            Dictionary<uint, string> SiteCloudNames = new Dictionary<uint, string>();
            Dictionary<uint, string> LinkCloudNames = new Dictionary<uint, string>();
 
            PnrpResolveScope availableScope = EnumerateClouds(false, LinkCloudNames, SiteCloudNames);
 
            if (availableScope == PnrpResolveScope.None)
            {
                //could not find any clouds.
                PeerExceptionHelper.ThrowInvalidOperation_PnrpNoClouds();
            }
 
            if (localExtension != null)
                meshId += localExtension;
 
            try
            {
                PeerNodeAddressToPnrpRegistrations(meshId, LinkCloudNames, SiteCloudNames, nodeAddress, out linkEntries, out siteEntries, out globalEntry);
            }
            catch (Exception e)
            {
                if (Fx.IsFatal(e)) throw;
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.PeerPnrpIllegalUri), e));
            }
            TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
 
            try
            {
                PnrpResolveScope currentScope = PnrpResolveScope.None;
                if (globalEntry != null)
                {
                    if (globalEntry.Addresses.Length > 0 && (availableScope & PnrpResolveScope.Global) != 0)
                    {
                        registrar.Register(globalEntry, timeoutHelper.RemainingTime());
                        regHandle.AddCloud(globalEntry.CloudName);
                        currentScope |= PnrpResolveScope.Global;
                    }
                }
                if (linkEntries.Length > 0)
                {
                    foreach (PnrpRegistration entry in linkEntries)
                    {
                        if (entry.Addresses.Length > 0)
                        {
                            registrar.Register(entry, timeoutHelper.RemainingTime());
                            regHandle.AddCloud(entry.CloudName);
                        }
                    }
                    currentScope |= PnrpResolveScope.LinkLocal;
                }
                if (siteEntries.Length > 0)
                {
                    foreach (PnrpRegistration entry in siteEntries)
                    {
                        if (entry.Addresses.Length > 0)
                        {
                            registrar.Register(entry, timeoutHelper.RemainingTime());
                            regHandle.AddCloud(entry.CloudName);
                        }
                    }
                    currentScope |= PnrpResolveScope.SiteLocal;
                }
                if (currentScope == PnrpResolveScope.None)
                {
                    // We have addresses but no cloud that corresponds to them
                    // so we should throw an exception
                    PeerExceptionHelper.ThrowInvalidOperation_PnrpAddressesUnsupported();
                }
            }
            catch (SocketException)
            {
                try
                {
                    Unregister(regHandle, timeoutHelper.RemainingTime());
                }
                catch (SocketException e)
                {
                    DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                }
                throw;
            }
 
            if (DiagnosticUtility.ShouldTraceInformation)
            {
                PnrpRegisterTraceRecord record = new PnrpRegisterTraceRecord(meshId, globalEntry, siteEntries, linkEntries);
                TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.PnrpRegisteredAddresses,
                    SR.GetString(SR.TraceCodePnrpRegisteredAddresses),
                    record, this, null);
            }
 
            return regHandle;
        }
 
        void ThrowIfNoPnrp()
        {
            if (!isPnrpAvailable)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                    SR.GetString(SR.PeerPnrpNotAvailable)));
            }
        }
 
        public override void Unregister(object registrationId, TimeSpan timeout)
        {
            RegistrationHandle regHandle = registrationId as RegistrationHandle;
            if (regHandle == null || String.IsNullOrEmpty(regHandle.PeerName))
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.PeerInvalidRegistrationId, regHandle), "registrationId"));
            string meshId = regHandle.PeerName;
 
            // prepend a 0. for unsecured peername
            string peerName = string.Format(CultureInfo.InvariantCulture, "0.{0}", meshId);
            registrar.Unregister(peerName, regHandle.Clouds, timeout);
 
            if (DiagnosticUtility.ShouldTraceInformation)
            {
                PnrpPeerResolverTraceRecord record = new PnrpPeerResolverTraceRecord(meshId, new List<PeerNodeAddress>());
                TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.PnrpUnregisteredAddresses,
                    SR.GetString(SR.TraceCodePnrpUnregisteredAddresses),
                    record, this, null);
            }
        }
 
        public override void Update(object registrationId, PeerNodeAddress updatedNodeAddress, TimeSpan timeout)
        {
            RegistrationHandle regHandle = registrationId as RegistrationHandle;
            if (regHandle == null || string.IsNullOrEmpty(regHandle.PeerName))
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.PeerInvalidRegistrationId, regHandle), "registrationId"));
 
            string meshId = regHandle.PeerName;
            Register(meshId, updatedNodeAddress, timeout);
        }
 
        //return null in case of unrecognized format. consider adding logging.
        PeerNodeAddress PeerNodeAddressFromPnrpRegistration(PnrpRegistration input)
        {
            List<IPAddress> addresses = new List<IPAddress>();
            PeerNodeAddress result = null;
            Guid[] guids;
            StringBuilder pathBuilder = new StringBuilder(MaxPathLength);
            int version = 0;
            string protocolScheme;
 
            try
            {
                if (input == null || String.IsNullOrEmpty(input.Comment))
                    return null;
                Array.ForEach(input.Addresses, delegate(IPEndPoint obj) { addresses.Add(obj.Address); });
                if (addresses.Count != 0)
                {
                    UriBuilder uriBuilder = new UriBuilder();
                    uriBuilder.Port = input.Addresses[0].Port;
                    uriBuilder.Host = addresses[0].ToString();
                    pathBuilder.Append(PeerStrings.KnownServiceUriPrefix);
                    CharEncoder.Decode(input.Comment, out version, out protocolScheme, out guids);
 
                    if (
                        (version == PayloadVersion) &&
                        (guids != null) && (guids.Length <= MaxGuids) &&
                        (guids.Length >= MinGuids)
                    )
                    {
                        uriBuilder.Scheme = protocolScheme;
                        Array.ForEach(guids, delegate(Guid guid)
                                            {
                                                pathBuilder.Append(PathSeparator + String.Format(CultureInfo.InvariantCulture, "{0}", guid.ToString()));
                                            }
                        );
                        uriBuilder.Path = String.Format(CultureInfo.InvariantCulture, "{0}", pathBuilder.ToString());
                        result = new PeerNodeAddress(new EndpointAddress(uriBuilder.Uri), new ReadOnlyCollection<IPAddress>(addresses));
                    }
                }
            }
            catch (ArgumentException e)
            {
                DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
            }
            catch (FormatException e)
            {
                DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
            }
            catch (IndexOutOfRangeException e)
            {
                DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
            }
 
            return result;
 
        }
 
        void TrimToMaxAddresses(List<IPEndPoint> addressList)
        {
            if (addressList.Count > MaxAddressEntries)
            {
                addressList.RemoveRange(MaxAddressEntries, addressList.Count - MaxAddressEntries);
            }
        }
 
        void PeerNodeAddressToPnrpRegistrations(string meshName, Dictionary<uint, string> LinkCloudNames, Dictionary<uint, string> SiteCloudNames, PeerNodeAddress input, out PnrpRegistration[] linkRegs, out PnrpRegistration[] siteRegs, out PnrpRegistration global)
        {
            PnrpRegistration reg = new PnrpRegistration();
 
            Dictionary<uint, PnrpRegistration> resultsLink = new Dictionary<uint, PnrpRegistration>();
            Dictionary<uint, PnrpRegistration> resultsSite = new Dictionary<uint, PnrpRegistration>();
            PnrpRegistration entry = null;
            string scheme;
            Guid[] guids;
            ParseServiceUri(input.EndpointAddress.Uri, out scheme, out guids);
            int port = input.EndpointAddress.Uri.Port;
            if (port <= 0)
                port = TcpUri.DefaultPort;
            string peerName = string.Format(CultureInfo.InvariantCulture, "0.{0}", meshName);
            string comment = CharEncoder.Encode(PayloadVersion, scheme, guids);
            global = null;
            string cloudName = string.Empty;
            foreach (IPAddress address in input.IPAddresses)
            {
                if (address.AddressFamily == AddressFamily.InterNetworkV6
                    &&
                    ((address.IsIPv6LinkLocal) || (address.IsIPv6SiteLocal))
                )
                {
                    if (address.IsIPv6LinkLocal)
                    {
                        if (!resultsLink.TryGetValue((uint)address.ScopeId, out entry))
                        {
                            if (!LinkCloudNames.TryGetValue((uint)address.ScopeId, out cloudName))
                            {
                                continue;
                            }
                            entry = PnrpRegistration.Create(peerName, comment, cloudName);
                            resultsLink.Add((uint)address.ScopeId, entry);
                        }
                    }
                    else
                    {
                        if (!resultsSite.TryGetValue((uint)address.ScopeId, out entry))
                        {
                            if (!SiteCloudNames.TryGetValue((uint)address.ScopeId, out cloudName))
                            {
                                continue;
                            }
                            entry = PnrpRegistration.Create(peerName, comment, cloudName);
                            resultsSite.Add((uint)address.ScopeId, entry);
                        }
                    }
                    entry.addressList.Add(new IPEndPoint(address, port));
                }
                else
                {
                    if (global == null)
                    {
                        global = PnrpRegistration.Create(peerName, comment, GlobalCloudName);
                    }
                    global.addressList.Add(new IPEndPoint(address, port));
                }
            }
            if (global != null)
            {
                if (global.addressList != null)
                {
                    TrimToMaxAddresses(global.addressList);
                    global.Addresses = global.addressList.ToArray();
                }
                else
                    global.Addresses = new IPEndPoint[0];
            }
 
            if (resultsLink.Count != 0)
            {
                foreach (PnrpRegistration tempLink in resultsLink.Values)
                {
                    if (tempLink.addressList != null)
                    {
                        TrimToMaxAddresses(tempLink.addressList);
                        tempLink.Addresses = tempLink.addressList.ToArray();
                    }
                    else
                    {
                        tempLink.Addresses = new IPEndPoint[0];
                    }
                }
                linkRegs = new PnrpRegistration[resultsLink.Count];
                resultsLink.Values.CopyTo(linkRegs, 0);
            }
            else
                linkRegs = new PnrpRegistration[0];
            if (resultsSite.Count != 0)
            {
                foreach (PnrpRegistration tempSite in resultsSite.Values)
                {
                    if (tempSite.addressList != null)
                    {
                        TrimToMaxAddresses(tempSite.addressList);
                        tempSite.Addresses = tempSite.addressList.ToArray();
                    }
                    else
                    {
                        tempSite.Addresses = new IPEndPoint[0];
                    }
                }
                siteRegs = new PnrpRegistration[resultsSite.Count];
                resultsSite.Values.CopyTo(siteRegs, 0);
            }
            else
                siteRegs = new PnrpRegistration[0];
        }
 
        static int ProtocolFromName(string name)
        {
            if (name == Uri.UriSchemeNetTcp)
            {
                return TcpTransport;
            }
 
            throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("name", SR.GetString(SR.PeerPnrpIllegalUri));
        }
 
        static string NameFromProtocol(byte number)
        {
            switch (number)
            {
                case TcpTransport:
                    return Uri.UriSchemeNetTcp;
                default:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.PeerPnrpIllegalUri)));
            }
        }
 
        void ParseServiceUri(Uri uri, out string scheme, out Guid[] result)
        {
            if (uri != null)
            {
                if ((ProtocolFromName(uri.Scheme) != 0) && !String.IsNullOrEmpty(uri.AbsolutePath))
                {
                    scheme = uri.Scheme;
                    string[] parts = uri.AbsolutePath.Trim(new char[] { ' ', PathSeparator }).Split(PathSeparator);
                    if ((0 == String.Compare(parts[0], PeerStrings.KnownServiceUriPrefix, StringComparison.OrdinalIgnoreCase)))
                    {
                        if (parts.Length >= MinGuids && parts.Length <= MaxGuids + 1)
                        {
                            result = new Guid[parts.Length - 1];
                            try
                            {
                                for (int i = 1; i < parts.Length; i++)
                                    result[i - 1] = Fx.CreateGuid(parts[i]);
                                return;
                            }
                            catch (FormatException e)
                            {
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.PeerPnrpIllegalUri), e));
                            }
                        }
                    }
                }
            }
            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.PeerPnrpIllegalUri)));
        }
 
        void MergeResults(Dictionary<string, PnrpRegistration> results, List<PnrpRegistration> regs)
        {
            PnrpRegistration entry = null;
            foreach (PnrpRegistration reg in regs)
            {
                if (!results.TryGetValue(reg.Comment, out entry))
                {
                    entry = reg;
                    results.Add(reg.Comment, reg);
                    entry.addressList = new List<IPEndPoint>();
                }
                entry.addressList.AddRange(reg.Addresses);
                reg.Addresses = null;
            }
        }
 
        void MergeResults(List<PeerNodeAddress> nodeAddressList, List<PnrpRegistration> globalRegistrations, List<PnrpRegistration> linkRegistrations, List<PnrpRegistration> siteRegistrations)
        {
            Dictionary<string, PnrpRegistration> results = new Dictionary<string, PnrpRegistration>();
            MergeResults(results, globalRegistrations);
            MergeResults(results, siteRegistrations);
            MergeResults(results, linkRegistrations);
            PeerNodeAddress result;
            foreach (PnrpRegistration reg in results.Values)
            {
                reg.Addresses = reg.addressList.ToArray();
                result = PeerNodeAddressFromPnrpRegistration(reg);
                if (result != null)
                    nodeAddressList.Add(result);
            }
        }
 
        public override ReadOnlyCollection<PeerNodeAddress> Resolve(string meshId, int maxAddresses, TimeSpan timeout)
        {
            ThrowIfNoPnrp();
            UnsafePnrpNativeMethods.PeerNameResolver resolver;
            List<UnsafePnrpNativeMethods.PeerNameResolver> resolvers = new List<UnsafePnrpNativeMethods.PeerNameResolver>();
            List<PnrpRegistration> globalRegistrations = new List<PnrpRegistration>();
            List<PnrpRegistration> linkRegistrations = new List<PnrpRegistration>();
            List<PnrpRegistration> siteRegistrations = new List<PnrpRegistration>();
            List<WaitHandle> handles = new List<WaitHandle>();
            Dictionary<uint, string> SiteCloudNames = new Dictionary<uint, string>();
            Dictionary<uint, string> LinkCloudNames = new Dictionary<uint, string>();
            UnsafePnrpNativeMethods.PnrpResolveCriteria targetScope = resolutionScope;
            TimeoutHelper timeoutHelper = new TimeoutHelper(TimeSpan.Compare(timeout, MaxResolveTimeout) <= 0 ? timeout : MaxResolveTimeout);
 
            if (!HasPeerNodeForMesh(meshId))
                targetScope = UnsafePnrpNativeMethods.PnrpResolveCriteria.Any;
            PnrpResolveScope currentScope = EnumerateClouds(true, LinkCloudNames, SiteCloudNames);
 
            if (remoteExtension != null)
                meshId += remoteExtension;
 
            // prepend a 0. for unsecured peername
            string peerName = string.Format(CultureInfo.InvariantCulture, "0.{0}", meshId);
            if ((currentScope & PnrpResolveScope.Global) != 0)
            {
                resolver = new UnsafePnrpNativeMethods.PeerNameResolver(
                                    peerName, maxAddresses, targetScope, 0, GlobalCloudName, timeoutHelper.RemainingTime(), globalRegistrations);
                handles.Add(resolver.AsyncWaitHandle);
                resolvers.Add(resolver);
            }
 
            if ((currentScope & PnrpResolveScope.LinkLocal) != 0)
            {
                foreach (KeyValuePair<uint, string> linkEntry in LinkCloudNames)
                {
                    resolver = new UnsafePnrpNativeMethods.PeerNameResolver(
                                    peerName, maxAddresses, targetScope, linkEntry.Key, linkEntry.Value, timeoutHelper.RemainingTime(), linkRegistrations);
                    handles.Add(resolver.AsyncWaitHandle);
                    resolvers.Add(resolver);
                }
            }
 
            if ((currentScope & PnrpResolveScope.SiteLocal) != 0)
            {
                foreach (KeyValuePair<uint, string> siteEntry in SiteCloudNames)
                {
                    resolver = new UnsafePnrpNativeMethods.PeerNameResolver(
                                    peerName, maxAddresses, targetScope, siteEntry.Key, siteEntry.Value, timeoutHelper.RemainingTime(), siteRegistrations);
                    handles.Add(resolver.AsyncWaitHandle);
                    resolvers.Add(resolver);
                }
            }
            if (handles.Count == 0)
            {
                //could not find any clouds.
                if (DiagnosticUtility.ShouldTraceWarning)
                {
                    Exception exception = new InvalidOperationException(SR.GetString(SR.PnrpNoClouds));
                    PnrpResolveExceptionTraceRecord record = new PnrpResolveExceptionTraceRecord(meshId, string.Empty, exception);
                    TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.PnrpResolvedAddresses,
                        SR.GetString(SR.TraceCodePnrpResolvedAddresses),
                        record, this, null);
                }
                return new ReadOnlyCollection<PeerNodeAddress>(new List<PeerNodeAddress>());
            }
 
            Exception lastException = null;
            foreach (UnsafePnrpNativeMethods.PeerNameResolver handle in resolvers)
            {
                try
                {
                    handle.End();
                }
                catch (SocketException e)
                {
                    DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                    lastException = e;
                }
            }
 
            List<PeerNodeAddress> nodeAddressList = new List<PeerNodeAddress>();
            MergeResults(nodeAddressList, globalRegistrations, linkRegistrations, siteRegistrations);
            if ((lastException != null) && (nodeAddressList.Count == 0))
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(lastException);
            if (DiagnosticUtility.ShouldTraceInformation)
            {
                PnrpPeerResolverTraceRecord record = new PnrpPeerResolverTraceRecord(meshId, nodeAddressList);
                TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.PnrpResolvedAddresses,
                    SR.GetString(SR.TraceCodePnrpResolvedAddresses),
                    record, this, null);
            }
            return new ReadOnlyCollection<PeerNodeAddress>(nodeAddressList);
        }
 
 
        // contains the friendly PNRP information
        internal class PnrpRegistration
        {
            public string PeerName;
            public string CloudName;
            public string Comment;
            public IPEndPoint[] Addresses;
            public List<IPEndPoint> addressList;
 
            internal static PnrpRegistration Create(string peerName, string comment, string cloudName)
            {
                PnrpRegistration reg = new PnrpRegistration();
                reg.Comment = comment;
                reg.CloudName = cloudName;
                reg.PeerName = peerName;
                reg.addressList = new List<IPEndPoint>();
                return reg;
            }
        }
 
        internal class CharEncoder
        {
            static void CheckAtLimit(int current)
            {
                if (current + 1 >= CommentLength)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.PeerPnrpIllegalUri)));
            }
            static void EncodeByte(byte b, ref int offset, byte[] bytes)
            {
                if (b == 0 || b == GuidEscape)
                {
                    CheckAtLimit(offset);
                    bytes[offset++] = GuidEscape;
                }
                CheckAtLimit(offset);
                bytes[offset++] = b;
            }
 
            static internal string Encode(int version, string protocolName, Guid[] guids)
            {
                byte[] bytes = new byte[CommentLength];
                int i = 0;
                int protocol = ProtocolFromName(protocolName);
                EncodeByte(Convert.ToByte(version), ref i, bytes);
                EncodeByte(Convert.ToByte(protocol), ref i, bytes);
                EncodeByte(Convert.ToByte(guids.Length), ref i, bytes);
                foreach (Guid guid in guids)
                {
                    foreach (byte b in guid.ToByteArray())
                    {
                        EncodeByte(Convert.ToByte(b), ref i, bytes);
                    }
                }
                if (i % 2 != 0 && i < bytes.Length)
                    bytes[i] = GuidEscape;
                // Now we have a collection of bytes lets turn it into a string
                int length = i;
                int clength = (length / 2) + (length % 2);         // Pack 2 bytes per char
                char[] chars = new char[clength];
                i = 0;
                for (int j = 0; j < clength; j++)
                {
                    chars[j] = Convert.ToChar(bytes[i++] * 0x100 + bytes[i++]);
                }
                return new string(chars);
            }
 
            static byte GetByte(int offset, char[] chars)
            {
                int p = offset / 2;
                int lo = offset % 2;
                return Convert.ToByte(lo == 1 ? chars[p] & GuidEscape : chars[p] / 0x100);
            }
 
            static byte DecodeByte(ref int offset, char[] chars)
            {
                byte b = GetByte(offset++, chars);
                if (b == 0xff)
                {
                    b = GetByte(offset++, chars);
                }
                return b;
            }
 
            static internal void Decode(string buffer, out int version, out string protocolName, out Guid[] guids)
            {
                char[] chars = buffer.ToCharArray();
                byte protocol;
                int i = 0;
 
                version = DecodeByte(ref i, chars);
                protocol = DecodeByte(ref i, chars);
                protocolName = NameFromProtocol(protocol);
                int length = DecodeByte(ref i, chars);
                guids = new Guid[length];
 
                for (int g = 0; g < length; g++)
                {
                    byte[] bytes = new byte[16];
                    for (int j = 0; j < 16; j++)
                    {
                        bytes[j] = DecodeByte(ref i, chars);
                    }
                    guids[g] = new Guid(bytes);
                }
            }
        }
 
        internal enum PnrpErrorCodes
        {
            WSA_PNRP_ERROR_BASE = 11500,
            WSA_PNRP_CLOUD_NOT_FOUND = 11501,
            WSA_PNRP_CLOUD_DISABLED = 11502,
            //these error codes are not relevant for now
            //            WSA_PNRP_INVALID_IDENTITY = 11503,
            //            WSA_PNRP_TOO_MUCH_LOAD = 11504,
            WSA_PNRP_CLOUD_IS_RESOLVE_ONLY = 11505,
            //            WSA_PNRP_CLIENT_INVALID_COMPARTMENT_ID = 11506,
            WSA_PNRP_FW_PORT_BLOCKED = 11507,
            WSA_PNRP_DUPLICATE_PEER_NAME = 11508,
        }
 
        internal class PnrpException : SocketException
        {
            string message;
 
            internal PnrpException(int errorCode, string cloud)
                : base(errorCode)
            {
                LoadMessage(errorCode, cloud);
            }
 
            public override string Message
            {
                get
                {
                    if (!String.IsNullOrEmpty(message))
                        return message;
                    else
                        return base.Message;
                }
            }
 
            void LoadMessage(int errorCode, string cloud)
            {
                string formatString;
                switch ((PnrpErrorCodes)errorCode)
                {
                    case PnrpErrorCodes.WSA_PNRP_CLOUD_DISABLED:
                        formatString = SR.PnrpCloudDisabled;
                        break;
                    case PnrpErrorCodes.WSA_PNRP_CLOUD_NOT_FOUND:
                        formatString = SR.PnrpCloudNotFound;
                        break;
                    case PnrpErrorCodes.WSA_PNRP_CLOUD_IS_RESOLVE_ONLY:
                        formatString = SR.PnrpCloudResolveOnly;
                        break;
                    case PnrpErrorCodes.WSA_PNRP_FW_PORT_BLOCKED:
                        formatString = SR.PnrpPortBlocked;
                        break;
                    case PnrpErrorCodes.WSA_PNRP_DUPLICATE_PEER_NAME:
                        formatString = SR.PnrpDuplicatePeerName;
                        break;
                    default:
                        formatString = null;
                        break;
                }
                if (formatString != null)
                    message = SR.GetString(formatString, cloud);
            }
        }
 
        internal static class UnsafePnrpNativeMethods
        {
            // WSA import functions
            [DllImport("ws2_32.dll", CharSet = CharSet.Unicode)]
            [ResourceExposure(ResourceScope.None)]
            static extern int WSASetService(CriticalAllocHandle querySet, WsaSetServiceOp essOperation, int dwControlFlags);
 
            [DllImport("ws2_32.dll", CharSet = CharSet.Unicode)]
            [ResourceExposure(ResourceScope.None)]
            static extern int WSALookupServiceNext(CriticalLookupHandle hLookup,
                WsaNspControlFlags dwControlFlags, ref int lpdwBufferLength, IntPtr Results);
            [DllImport("ws2_32.dll", CharSet = CharSet.Unicode), ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
            [ResourceExposure(ResourceScope.None)]
            static extern int WSALookupServiceEnd(IntPtr hLookup);
            [DllImport("ws2_32.dll", CharSet = CharSet.Unicode)]
            [ResourceExposure(ResourceScope.None)]
            static extern int WSALookupServiceBegin(CriticalAllocHandle query, WsaNspControlFlags dwControlFlags, out CriticalLookupHandle hLookup);
            [DllImport("ws2_32.dll", CharSet = CharSet.Ansi)]
            [ResourceExposure(ResourceScope.None)]
            static extern int WSAStartup(Int16 wVersionRequested, ref WsaData lpWSAData);
            [DllImport("ws2_32.dll", CharSet = CharSet.Ansi)]
            [ResourceExposure(ResourceScope.None)]
            static extern int WSACleanup();
            [DllImport("ws2_32.dll", CharSet = CharSet.Ansi)]
            [ResourceExposure(ResourceScope.None)]
            static extern int WSAGetLastError();
            [DllImport("ws2_32.dll", CharSet = CharSet.Unicode)]
            [ResourceExposure(ResourceScope.None)]
            static extern int WSAEnumNameSpaceProviders(ref int lpdwBufferLength, IntPtr lpnspBuffer);
 
            // PNRP namespace identifiers
            static Guid SvcIdCloud = new Guid(0xc2239ce6, 0x00c0, 0x4fbf, 0xba, 0xd6, 0x18, 0x13, 0x93, 0x85, 0xa4, 0x9a);
            static Guid SvcIdNameV1 = new Guid(0xc2239ce5, 0x00c0, 0x4fbf, 0xba, 0xd6, 0x18, 0x13, 0x93, 0x85, 0xa4, 0x9a);
            static Guid SvcIdName = new Guid(0xc2239ce7, 0x00c0, 0x4fbf, 0xba, 0xd6, 0x18, 0x13, 0x93, 0x85, 0xa4, 0x9a);
            static Guid NsProviderName = new Guid(0x03fe89cd, 0x766d, 0x4976, 0xb9, 0xc1, 0xbb, 0x9b, 0xc4, 0x2c, 0x7b, 0x4d);
            static Guid NsProviderCloud = new Guid(0x03fe89ce, 0x766d, 0x4976, 0xb9, 0xc1, 0xbb, 0x9b, 0xc4, 0x2c, 0x7b, 0x4d);
 
            const int MaxAddresses = 10;
            const int MaxAddressesV1 = 4;
            const Int16 RequiredWinsockVersion = 0x0202;
 
            // specifies the namespace used used by a specified WSAQUERYSET
            [Serializable]
            internal enum NspNamespaces
            {
                Cloud = 39,
                Name = 38,
            }
 
            [Serializable]
            [Flags]
            internal enum PnrpCloudFlags
            {
                None = 0x0000,
                LocalName = 0x0001, //  Name not valid on other computers
            }
 
            [Serializable]
            internal enum PnrpCloudState
            {
                Virtual = 0,        //  Not initialized
                Synchronizing = 1,  //  The cache is initializing
                Active = 2,         //  Cloud is active
                Dead = 3,            //  Initialized but lost network
                Disabled = 4,       //disabled in the registry
                NoNet = 5,          //active but lost network
                Alone = 6,
            }
 
            [Serializable]
            internal enum PnrpExtendedPayloadType
            {
                None = 0,
                Binary,
                String
            }
 
            // internal because it is exposed by PeerNameResolver
            [Serializable]
            internal enum PnrpResolveCriteria
            {
                Default = 0,                    // Default = PNRP_RESOLVE_CRITERIA_NON_CURRENT_PROCESS_PEER_NAME
                Remote = 1,                     // match first 128 bits (remote node)
                NearestRemote = 2,              // match first 128 bits, and close to top 64 bits
                // of the second 128 bits (remote node)
                NonCurrentProcess = 3,          //  match first 128 bits (not in the current process) 
                NearestNonCurrentProcess = 4,   // match first 128 bits, and close to top 64 bits
                // of the second 128 bits (not in the current process)   
                Any = 5,                        // match first 128 bits (any node)
                Nearest = 6                     // match first 128 bits, and close to top 64 bits
                // of the second 128 bits (any node)   
            }
 
            [Serializable]
            internal enum PnrpRegisteredIdState
            {
                Ok = 1,     //  Id is active in cloud
                Problem = 2 //  Id is no longer registered in cloud
            }
 
            internal enum PnrpScope
            {
                Any = 0,
                Global = 1,
                SiteLocal = 2,
                LinkLocal = 3,
            }
 
            // primary use in this code is to specify what information should be returned by WSALookupServiceNext
            [Flags]
            internal enum WsaNspControlFlags
            {
                Deep = 0x0001,
                Containers = 0x0002,
                NoContainers = 0x0004,
                Nearest = 0x0008,
                ReturnName = 0x0010,
                ReturnType = 0x0020,
                ReturnVersion = 0x0040,
                ReturnComment = 0x0080,
                ReturnAddr = 0x0100,
                ReturnBlob = 0x0200,
                ReturnAliases = 0x0400,
                ReturnQueryString = 0x0800,
                ReturnAll = 0x0FF0,
                ResService = 0x8000,
                FlushCache = 0x1000,
                FlushPrevious = 0x2000,
            }
 
            internal enum WsaError
            {
                WSAEINVAL = 10022,
                WSAEFAULT = 10014,
                WSAENOMORE = 10102,
                WSA_E_NO_MORE = 10110,
                WSANO_DATA = 11004
            }
 
            // specifies the operation of WSASetService
            internal enum WsaSetServiceOp
            {
                Register = 0,
                Deregister,
                Delete
            }
 
            internal struct BlobSafe
            {
                public int cbSize;
                public CriticalAllocHandle pBlobData;
            }
 
            internal struct BlobNative
            {
                public int cbSize;
                public IntPtr pBlobData;
            }
 
 
            // PnrpResolver does not currently support any cloud except Global. If this needs to be changed, we will
            // need to be able to enumerate clouds.
 
            // managed equivalent of both PNRPCLOUDINFO and PNRP_CLOUD_ID
            // internal because it is exposed by PeerCloudEnumerator
            internal class CloudInfo
            {
                public string Name;
                public PnrpScope Scope;
                public uint ScopeId;
                public PnrpCloudState State;
                public PnrpCloudFlags Flags;
            }
 
            [Serializable]
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
            internal struct CsAddrInfo
            {
                public IPEndPoint LocalAddr;
                public IPEndPoint RemoteAddr;
                public int iSocketType;
                public int iProtocol;
            }
 
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
            internal class CsAddrInfoSafe : IDisposable
            {
                public SOCKET_ADDRESS_SAFE LocalAddr;
                public SOCKET_ADDRESS_SAFE RemoteAddr;
                public int iSocketType;
                public int iProtocol;
                bool disposed;
 
                public static CsAddrInfoSafe[] FromAddresses(CsAddrInfo[] addresses)
                {
                    CsAddrInfoSafe addr;
                    CsAddrInfoSafe[] result = null;
                    if (addresses == null || addresses.Length == 0)
                        return null;
 
                    result = new CsAddrInfoSafe[addresses.Length];
                    int i = 0;
                    foreach (CsAddrInfo info in addresses)
                    {
                        addr = new CsAddrInfoSafe();
                        addr.LocalAddr = SOCKET_ADDRESS_SAFE.SocketAddressFromIPEndPoint(info.LocalAddr);
                        addr.RemoteAddr = SOCKET_ADDRESS_SAFE.SocketAddressFromIPEndPoint(info.RemoteAddr);
                        addr.iProtocol = info.iProtocol;
                        addr.iSocketType = info.iSocketType;
                        result[i++] = addr;
                    }
                    return result;
                }
                public static void StructureToPtr(CsAddrInfoSafe input, IntPtr target)
                {
                    CsAddrInfoNative native;
                    native.iProtocol = input.iProtocol;
                    native.iSocketType = input.iSocketType;
                    native.LocalAddr.iSockaddrLength = input.LocalAddr.iSockaddrLength;
                    native.LocalAddr.lpSockAddr = input.LocalAddr.lpSockAddr;
                    native.RemoteAddr.iSockaddrLength = input.RemoteAddr.iSockaddrLength;
                    native.RemoteAddr.lpSockAddr = input.RemoteAddr.lpSockAddr;
 
                    Marshal.StructureToPtr(native, target, false);
                }
                ~CsAddrInfoSafe()
                {
                    Dispose(false);
                }
                public virtual void Dispose()
                {
                    Dispose(true);
                    GC.SuppressFinalize(this);
                }
                void Dispose(bool disposing)
                {
                    if (disposed)
                    {
                        if (disposing)
                        {
                            LocalAddr.Dispose();
                            RemoteAddr.Dispose();
                        }
                    }
                    disposed = true;
                }
 
            }
 
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
            internal struct CsAddrInfoNative
            {
                public SOCKET_ADDRESS_NATIVE LocalAddr;
                public SOCKET_ADDRESS_NATIVE RemoteAddr;
                public int iSocketType;
                public int iProtocol;
            }
 
            [Serializable]
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
            internal struct PnrpCloudId
            {
                public int AddressFamily; // should be AF_INET6
                public PnrpScope Scope;     // Global, site, or link
                public uint ScopeId;       // specifies interface
 
            }
 
            internal struct PnrpCloudInfo
            {
                public int dwSize;            // size of this struct
                public PnrpCloudId Cloud;       // network cloud information
                public PnrpCloudState dwCloudState; // state of cloud
                public PnrpCloudFlags Flags;
            }
 
            //native equivalent for easy marshalling. 
            //should be exactly like PnrpInfo except CriticalHandles
            internal struct PnrpInfoNative
            {
                public int dwSize;            // size of this struct
                public string lpwszIdentity;  // identity name string
                public int nMaxResolve;       // number of desired resolutions
                public int dwTimeout;         // time in seconds to wait for responses
                public int dwLifetime;        // time in seconds for validity
                public PnrpResolveCriteria enResolveCriteria; // criteria for resolve matches
                public int dwFlags;           // set of flags
                public SOCKET_ADDRESS_NATIVE saHint; // IPv6 addr use for location
                public PnrpRegisteredIdState enNameState; // state of registered name
            }
 
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
            internal struct PnrpInfo
            {
                public int dwSize;            // size of this struct
                public string lpwszIdentity;  // identity name string
                public int nMaxResolve;       // number of desired resolutions
                public int dwTimeout;         // time in seconds to wait for responses
                public int dwLifetime;        // time in seconds for validity
                public PnrpResolveCriteria enResolveCriteria; // criteria for resolve matches
                public int dwFlags;           // set of flags
                public SOCKET_ADDRESS_SAFE saHint; // IPv6 addr use for location
                public PnrpRegisteredIdState enNameState; // state of registered name
                public static void ToPnrpInfoNative(PnrpInfo source, ref PnrpInfoNative target)
                {
                    target.dwSize = source.dwSize;
                    target.lpwszIdentity = source.lpwszIdentity;
                    target.nMaxResolve = source.nMaxResolve;
                    target.dwTimeout = source.dwTimeout;
                    target.dwLifetime = source.dwLifetime;
                    target.enResolveCriteria = source.enResolveCriteria;
                    target.dwFlags = source.dwFlags;
                    if (source.saHint != null)
                    {
                        target.saHint.lpSockAddr = source.saHint.lpSockAddr;
                        target.saHint.iSockaddrLength = source.saHint.iSockaddrLength;
                    }
                    else
                    {
                        target.saHint.lpSockAddr = IntPtr.Zero;
                        target.saHint.iSockaddrLength = 0;
                    }
                    target.enNameState = source.enNameState;
                }
            }
 
            [Serializable]
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
            internal struct sockaddr_in
            {
                public short sin_family;
                public ushort sin_port;
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
                public byte[] sin_addr;
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
                public byte[] sin_zero;
            }
 
            [Serializable]
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
            internal struct sockaddr_in6
            {
                public short sin6_family;
                public ushort sin6_port;
                public uint sin6_flowinfo;
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
                public byte[] sin6_addr;
                public uint sin6_scope_id;
            }
 
            internal class SOCKET_ADDRESS_SAFE : IDisposable
            {
                public CriticalAllocHandle lpSockAddr;
                public int iSockaddrLength;
                bool disposed;
                public static SOCKET_ADDRESS_SAFE SocketAddressFromIPEndPoint(IPEndPoint endpoint)
                {
                    SOCKET_ADDRESS_SAFE socketAddress = new SOCKET_ADDRESS_SAFE();
                    if (endpoint == null)
                        return socketAddress;
 
                    if (endpoint.AddressFamily == AddressFamily.InterNetwork)
                    {
                        socketAddress.iSockaddrLength = Marshal.SizeOf(typeof(sockaddr_in));
                        socketAddress.lpSockAddr = CriticalAllocHandle.FromSize(socketAddress.iSockaddrLength);
                        sockaddr_in sa = new sockaddr_in();
                        sa.sin_family = (short)AddressFamily.InterNetwork;
                        sa.sin_port = (ushort)endpoint.Port;
                        sa.sin_addr = endpoint.Address.GetAddressBytes();
                        Marshal.StructureToPtr(sa, (IntPtr)socketAddress.lpSockAddr, false);
                    }
                    else if (endpoint.AddressFamily == AddressFamily.InterNetworkV6)
                    {
                        socketAddress.iSockaddrLength = Marshal.SizeOf(typeof(sockaddr_in6));
                        socketAddress.lpSockAddr = CriticalAllocHandle.FromSize(socketAddress.iSockaddrLength);
                        sockaddr_in6 sa = new sockaddr_in6();
                        sa.sin6_family = (short)AddressFamily.InterNetworkV6;
                        sa.sin6_port = (ushort)endpoint.Port;
                        sa.sin6_addr = endpoint.Address.GetAddressBytes();
                        sa.sin6_scope_id = (uint)endpoint.Address.ScopeId;
                        Marshal.StructureToPtr(sa, (IntPtr)socketAddress.lpSockAddr, false);
                    }
                    return socketAddress;
                }
 
                ~SOCKET_ADDRESS_SAFE()
                {
                    Dispose(false);
                }
 
                public virtual void Dispose()
                {
                    Dispose(true);
                    GC.SuppressFinalize(this);
                }
 
                void Dispose(bool disposing)
                {
                    if (!disposed)
                    {
                        if (disposing)
                            lpSockAddr.Dispose();
                    }
                    disposed = true;
                }
 
            }
 
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
            internal struct SOCKET_ADDRESS_NATIVE
            {
                public IntPtr lpSockAddr;
                public int iSockaddrLength;
            }
 
            [Serializable]
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
            internal struct WsaData
            {
                public Int16 wVersion;
                public Int16 wHighVersion;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)]
                public string szDescription;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 129)]
                public string szSystemStatus;
 
                public Int16 iMaxSockets;
                public Int16 iMaxUdpDg;
                public IntPtr lpVendorInfo;
            }
 
            [Serializable]
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
            internal struct WsaNamespaceInfo
            {
                public Guid NSProviderId;
                public int dwNameSpace;
                public int fActive;
                public int dwVersion;
                // don't bother marshalling this as a string since we don't need to look at it
                public IntPtr lpszIdentifier;
            }
 
            // managed equivalent of WSAQUERYSET
            internal class WsaQuerySet
            {
                public string ServiceInstanceName;
                public Guid ServiceClassId;
                public string Comment;
                public NspNamespaces NameSpace;
                public Guid NSProviderId;
                public string Context;
                public CsAddrInfo[] CsAddrInfos;
                public object Blob;
                static public WsaQuerySetSafe ToWsaQuerySetSafe(WsaQuerySet input)
                {
 
                    WsaQuerySetSafe result = new WsaQuerySetSafe();
                    if (input == null)
                        return result;
 
                    result.dwSize = Marshal.SizeOf(typeof(WsaQuerySetNative));
                    result.lpszServiceInstanceName = CriticalAllocHandleString.FromString(input.ServiceInstanceName);
                    result.lpServiceClassId = CriticalAllocHandleGuid.FromGuid(input.ServiceClassId);
                    result.lpszComment = CriticalAllocHandleString.FromString(input.Comment);
                    result.dwNameSpace = input.NameSpace;
                    result.lpNSProviderId = CriticalAllocHandleGuid.FromGuid(input.NSProviderId);
                    result.lpszContext = CriticalAllocHandleString.FromString(input.Context);
                    result.dwNumberOfProtocols = 0;
                    result.lpafpProtocols = IntPtr.Zero; // not used
                    result.lpszQueryString = IntPtr.Zero;
 
                    if (input.CsAddrInfos != null)
                    {
                        result.dwNumberOfCsAddrs = input.CsAddrInfos.Length;
                        result.addressList = CsAddrInfoSafe.FromAddresses(input.CsAddrInfos);
                    }
                    result.dwOutputFlags = 0;
                    result.lpBlob = CriticalAllocHandlePnrpBlob.FromPnrpBlob(input.Blob);
 
                    return result;
                }
            }
 
            internal class CriticalAllocHandlePnrpBlob : CriticalAllocHandle
            {
                public static CriticalAllocHandle FromPnrpBlob(object input)
                {
                    BlobSafe blob = new BlobSafe();
                    if (input != null)
                    {
                        if (input.GetType() == typeof(PnrpInfo))
                        {
                            int blobSize = Marshal.SizeOf(typeof(PnrpInfoNative));
                            blob.pBlobData = CriticalAllocHandle.FromSize(blobSize + Marshal.SizeOf(typeof(BlobNative)));
 
                            //write the BlobSafe fields first,
                            BlobNative nativeBlob;
                            nativeBlob.cbSize = blobSize;
                            nativeBlob.pBlobData = (IntPtr)(((IntPtr)blob.pBlobData).ToInt64() + Marshal.SizeOf(typeof(BlobNative)));
                            Marshal.StructureToPtr(nativeBlob, (IntPtr)blob.pBlobData, false);
                            PnrpInfo pnrpInfo = (PnrpInfo)input;
                            pnrpInfo.dwSize = blobSize;
                            PnrpInfoNative nativeInfo = new PnrpInfoNative();
                            PnrpInfo.ToPnrpInfoNative(pnrpInfo, ref nativeInfo);
                            Marshal.StructureToPtr(nativeInfo, (IntPtr)nativeBlob.pBlobData, false);
                            blob.cbSize = blobSize;
                        }
                        else if (input.GetType() == typeof(PnrpCloudInfo))
                        {
                            int blobSize = Marshal.SizeOf(input.GetType());
                            blob.pBlobData = CriticalAllocHandle.FromSize(blobSize + Marshal.SizeOf(typeof(BlobNative)));
 
                            //write the BlobSafe fields first,
                            BlobNative nativeBlob;
                            nativeBlob.cbSize = blobSize;
                            nativeBlob.pBlobData = (IntPtr)(((IntPtr)blob.pBlobData).ToInt64() + Marshal.SizeOf(typeof(BlobNative)));
                            Marshal.StructureToPtr(nativeBlob, (IntPtr)blob.pBlobData, false);
                            PnrpCloudInfo cloudInfo = (PnrpCloudInfo)input;
                            cloudInfo.dwSize = Marshal.SizeOf(typeof(PnrpCloudInfo));
                            Marshal.StructureToPtr(cloudInfo, (IntPtr)nativeBlob.pBlobData, false);
                            blob.cbSize = blobSize;
                        }
                        else
                        {
                            throw Fx.AssertAndThrow("Unknown payload type!");
                        }
                    }
                    return blob.pBlobData;
 
                }
            }
 
            internal class CriticalAllocHandleString : CriticalAllocHandle
            {
                public static CriticalAllocHandle FromString(string input)
                {
                    CriticalAllocHandleString result = new CriticalAllocHandleString();
                    RuntimeHelpers.PrepareConstrainedRegions();
                    try { }
                    finally
                    {
                        result.SetHandle(Marshal.StringToHGlobalUni(input));
                    }
                    return result;
                }
            }
 
            internal class CriticalAllocHandleWsaQuerySetSafe : CriticalAllocHandle
            {
                static int CalculateSize(WsaQuerySetSafe safeQuerySet)
                {
                    int structSize = Marshal.SizeOf(typeof(WsaQuerySetNative));
                    if (safeQuerySet.addressList != null)
                        structSize += safeQuerySet.addressList.Length * Marshal.SizeOf(typeof(CsAddrInfoNative));
                    return structSize;
                }
 
                public static CriticalAllocHandle FromWsaQuerySetSafe(WsaQuerySetSafe safeQuerySet)
                {
                    CriticalAllocHandle result = CriticalAllocHandle.FromSize(CalculateSize(safeQuerySet));
                    WsaQuerySetSafe.StructureToPtr(safeQuerySet, (IntPtr)result);
                    return result;
                }
            }
 
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
            internal class WsaQuerySetSafe : IDisposable
            {
                public int dwSize;
                public CriticalAllocHandle lpszServiceInstanceName;
                public CriticalAllocHandle lpServiceClassId;
                public IntPtr lpVersion; // not used
                public CriticalAllocHandle lpszComment;
                public NspNamespaces dwNameSpace;
                public CriticalAllocHandle lpNSProviderId;
                public CriticalAllocHandle lpszContext;
                public int dwNumberOfProtocols; // 0
                public IntPtr lpafpProtocols; // not used
                public IntPtr lpszQueryString; // not used
                public int dwNumberOfCsAddrs;
                public CsAddrInfoSafe[] addressList;
                public int dwOutputFlags; // 0
                public CriticalAllocHandle lpBlob;
                bool disposed;
 
                ~WsaQuerySetSafe()
                {
                    Dispose(false);
                }
 
                public virtual void Dispose()
                {
                    Dispose(true);
                    GC.SuppressFinalize(this);
                }
 
                void Dispose(bool disposing)
                {
                    if (!disposed)
                    {
                        if (disposing)
                        {
                            if (lpszServiceInstanceName != null)
                                lpszServiceInstanceName.Dispose();
                            if (lpServiceClassId != null)
                                lpServiceClassId.Dispose();
                            if (lpszComment != null)
                                lpszComment.Dispose();
                            if (lpNSProviderId != null)
                                lpNSProviderId.Dispose();
                            if (lpBlob != null)
                                lpBlob.Dispose();
                            if (addressList != null)
                            {
                                foreach (CsAddrInfoSafe addr in addressList)
                                {
                                    addr.Dispose();
                                }
                            }
                        }
                    }
                    disposed = true;
                }
 
                static public void StructureToPtr(WsaQuerySetSafe input, IntPtr target)
                {
                    WsaQuerySetNative native = new WsaQuerySetNative();
                    native.dwSize = input.dwSize;
                    native.lpszServiceInstanceName = input.lpszServiceInstanceName;
                    native.lpServiceClassId = input.lpServiceClassId;
                    native.lpVersion = IntPtr.Zero; // not used
                    native.lpszComment = input.lpszComment;
                    native.dwNameSpace = input.dwNameSpace;
                    native.lpNSProviderId = input.lpNSProviderId;
                    native.lpszContext = input.lpszContext;
                    native.dwNumberOfProtocols = 0; // 0
                    native.lpafpProtocols = IntPtr.Zero; // not used
                    native.lpszQueryString = IntPtr.Zero; // not used
                    native.dwNumberOfCsAddrs = input.dwNumberOfCsAddrs;
                    native.dwOutputFlags = 0; // 0
                    native.lpBlob = input.lpBlob;
 
                    Int64 sockAddressStart = target.ToInt64() + Marshal.SizeOf(typeof(WsaQuerySetNative));
                    native.lpcsaBuffer = (IntPtr)sockAddressStart;
 
                    Marshal.StructureToPtr(native, target, false);
                    MarshalSafeAddressesToNative(input, (IntPtr)sockAddressStart);
 
                }
 
                public static void MarshalSafeAddressesToNative(WsaQuerySetSafe safeQuery, IntPtr target)
                {
                    // marshal the addresses
                    if (safeQuery.addressList != null && safeQuery.addressList.Length > 0)
                    {
                        int sizeOfCsAddrInfo = Marshal.SizeOf(typeof(CsAddrInfoNative));
                        Int64 start = target.ToInt64();
                        Fx.Assert(start % IntPtr.Size == 0, "Invalid alignment!!");
                        foreach (CsAddrInfoSafe safeAddress in safeQuery.addressList)
                        {
                            CsAddrInfoSafe.StructureToPtr(safeAddress, (IntPtr)start);
                            start += sizeOfCsAddrInfo;
                        }
                    }
                }
 
            }
 
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
            internal struct WsaQuerySetNative
            {
                public int dwSize;
                public IntPtr lpszServiceInstanceName;
                public IntPtr lpServiceClassId;
                public IntPtr lpVersion; // not used
                public IntPtr lpszComment;
                public NspNamespaces dwNameSpace;
                public IntPtr lpNSProviderId;
                public IntPtr lpszContext;
                public int dwNumberOfProtocols; // 0
                public IntPtr lpafpProtocols; // not used
                public IntPtr lpszQueryString; // not used
                public int dwNumberOfCsAddrs;
                public IntPtr lpcsaBuffer;
                public int dwOutputFlags; // 0
                public IntPtr lpBlob;
            }
 
            internal class CriticalLookupHandle : CriticalHandleZeroOrMinusOneIsInvalid
            {
                protected override bool ReleaseHandle()
                {
                    return WSALookupServiceEnd(handle) == 0;
                }
            }
 
            // base class for ref-counting WSA uses and calling WSAStartup/WSAShutdown
            internal class DiscoveryBase : MarshalByRefObject, IDisposable
            {
                static int refCount = 0;
                static object refCountLock = new object();
                bool disposed;
 
                public DiscoveryBase()
                {
                    lock (refCountLock)
                    {
                        if (refCount == 0)
                        {
                            WsaData WinsockVersion = new WsaData();
                            int ret = WSAStartup(UnsafePnrpNativeMethods.RequiredWinsockVersion, ref WinsockVersion);
                            if (ret != 0)
                            {
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SocketException(ret));
                            }
                        }
                        refCount++;
                    }
                }
 
                public void Dispose()
                {
                    Dispose(true);
                    GC.SuppressFinalize(this);
                }
 
                public void Dispose(bool disposing)
                {
                    if (!disposed)
                    {
                        lock (refCountLock)
                        {
                            refCount--;
                            if (refCount == 0)
                            {
                                WSACleanup();
                            }
                        }
                    }
                    disposed = true;
                }
 
                ~DiscoveryBase()
                {
                    this.Dispose(false);
                }
 
                [SuppressMessage(FxCop.Category.Security, FxCop.Rule.AptcaMethodsShouldOnlyCallAptcaMethods, Justification = "ServiceController has demands for ServiceControllerPermission.")]
                public bool IsPnrpServiceRunning(TimeSpan waitForService)
                {
                    TimeoutHelper timeoutHelper = new TimeoutHelper(waitForService);
                    try
                    {
                        using (ServiceController sc = new ServiceController("pnrpsvc"))
                        {
                            try
                            {
                                if (sc.Status == ServiceControllerStatus.StopPending)
                                {
                                    sc.WaitForStatus(ServiceControllerStatus.Stopped, timeoutHelper.RemainingTime());
                                }
                                if (sc.Status == ServiceControllerStatus.Stopped)
                                {
                                    sc.Start();
                                }
                                sc.WaitForStatus(ServiceControllerStatus.Running, timeoutHelper.RemainingTime());
                            }
                            catch (Exception e)
                            {
                                if (Fx.IsFatal(e)) throw;
                                if (e is InvalidOperationException || e is TimeoutException)
                                    return false;
                                else
                                    throw;
                            }
                            return (sc.Status == ServiceControllerStatus.Running);
                        }
                    }
                    catch (InvalidOperationException e)
                    {
                        DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                        Fx.Assert("IsPnrpServiceRunning should be called after IsPnrpInstalled");
                        return false;
                    }
                }
 
                public bool IsPnrpAvailable(TimeSpan waitForService)
                {
                    if (!IsPnrpInstalled())
                        return false;
 
                    //make sure that the service is running
                    if (!IsPnrpServiceRunning(waitForService))
                        return false;
                    // If PNRP is installed, ensure that it supports extended payload by attempting to register with
                    // an invalid query set. If extended payload is not available, "WSASERVICE_NOT_FOUND" is returned.
                    // Otherwise, "WSAEINVAL" is returned.
 
                    //UPDATE: we will work with PNRP 1.0 if it is available.
                    // a separate implementation will work with payload support when available.
                    WsaQuerySet querySet = new WsaQuerySet();
                    querySet.NSProviderId = NsProviderName;
                    querySet.ServiceClassId = SvcIdNameV1;
                    int res = InvokeService(querySet, WsaSetServiceOp.Register, 0);
 
                    //on xp 64bit, WSANO_DATA is returned
                    if (res == (int)WsaError.WSAEINVAL || res == (int)WsaError.WSANO_DATA)
                        return true;
 
                    // if the call didn't fail or returned any other error, PNRP clearly isn't working properly
                    return false;
 
                }
 
                // determine if any version of PNRP is installed and available
                public bool IsPnrpInstalled()
                {
                    int size = 0;
                    int nProviders;
                    CriticalAllocHandle dataPtr = null;
                    // retrieve the list of installed namespace providers
                    // implemented in a loop in case the size changes between the first and second calls
                    while (true)
                    {
                        nProviders = WSAEnumNameSpaceProviders(ref size, (IntPtr)dataPtr);
                        if (nProviders != -1) // success
                            break;
 
                        int error = WSAGetLastError();
                        if (error != (int)WsaError.WSAEFAULT) // buffer length to small
                            return false; // any other error effectively means that PNRP isn't usable
 
                        dataPtr = CriticalAllocHandle.FromSize(size);
                    }
 
                    // loop through the providers
                    for (int i = 0; i < nProviders; i++)
                    {
                        IntPtr nsInfoPtr = (IntPtr)(((IntPtr)dataPtr).ToInt64() + i *
                            Marshal.SizeOf(typeof(WsaNamespaceInfo)));
                        WsaNamespaceInfo nsInfo = (WsaNamespaceInfo)Marshal.PtrToStructure(nsInfoPtr,
                            typeof(WsaNamespaceInfo));
 
                        // if this is the PNRP name namespace provider and it is active, it is installed
                        if (nsInfo.NSProviderId == NsProviderName && nsInfo.fActive != 0)
                            return true;
                    }
 
                    // no PNRP name namespace provider found
                    return false;
                }
 
                int InvokeService(WsaQuerySet registerQuery, WsaSetServiceOp op, int flags)
                {
                    WsaQuerySetSafe native = WsaQuerySet.ToWsaQuerySetSafe(registerQuery);
                    int error = 0;
                    using (native)
                    {
                        CriticalAllocHandle handle = CriticalAllocHandleWsaQuerySetSafe.FromWsaQuerySetSafe(native);
                        using (handle)
                        {
                            int retval = WSASetService(handle, op, flags);
                            if (retval != 0)
                            {
                                error = WSAGetLastError();
                            }
                        }
                    }
                    return error;
                }
 
            }
 
            public class PeerCloudEnumerator : DiscoveryBase
            {
                static public CloudInfo[] GetClouds()
                {
                    int retval = 0;
                    ArrayList clouds = new ArrayList();
                    WsaQuerySet querySet = new WsaQuerySet();
                    CriticalLookupHandle hLookup;
 
                    PnrpCloudInfo cloudInfo = new PnrpCloudInfo();
                    cloudInfo.dwSize = Marshal.SizeOf(typeof(PnrpCloudInfo));
                    cloudInfo.Cloud.Scope = PnrpScope.Any;
                    cloudInfo.dwCloudState = (PnrpCloudState)0;
                    cloudInfo.Flags = PnrpCloudFlags.None;
                    querySet.NameSpace = NspNamespaces.Cloud;
                    querySet.NSProviderId = NsProviderCloud;
                    querySet.ServiceClassId = SvcIdCloud;
                    querySet.Blob = cloudInfo;
 
                    WsaQuerySetSafe native = WsaQuerySet.ToWsaQuerySetSafe(querySet);
                    using (native)
                    {
                        CriticalAllocHandle handle = CriticalAllocHandleWsaQuerySetSafe.FromWsaQuerySetSafe(native);
                        retval = WSALookupServiceBegin(handle, WsaNspControlFlags.ReturnAll, out hLookup);
                    }
                    if (retval != 0)
                    {
                        // unable to start the enumeration
                        SocketException exception = new SocketException(WSAGetLastError());
                        Utility.CloseInvalidOutCriticalHandle(hLookup);
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(exception);
                    }
 
                    // start with a sensible default size
                    int size = Marshal.SizeOf(typeof(WsaQuerySetSafe)) + 200;
                    //wrap in CriticalAllocHandle when PAYLOAD is enabled
                    CriticalAllocHandle nativeQuerySetPtr = CriticalAllocHandle.FromSize(size);
                    using (hLookup)
                    {
                        while (true)
                        {
                            retval = WSALookupServiceNext(hLookup, 0, ref size, (IntPtr)nativeQuerySetPtr);
                            if (retval != 0)
                            {
                                int error = WSAGetLastError();
                                if (error == (int)WsaError.WSAENOMORE || error == (int)WsaError.WSA_E_NO_MORE)
                                {
                                    // no more
                                    break;
                                }
                                if (error == (int)WsaError.WSAEFAULT)
                                {
                                    // buffer too small, allocate a bigger one of the specified size
                                    if (nativeQuerySetPtr != null)
                                    {
                                        nativeQuerySetPtr.Dispose();
                                        nativeQuerySetPtr = null;
                                    }
                                    //wrap in CriticalAllocHandle when PAYLOAD is enabled
                                    nativeQuerySetPtr = CriticalAllocHandle.FromSize(size);
                                    continue;
                                }
 
                                // unexpected error
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SocketException(error));
                            }
                            else
                            {
                                if (nativeQuerySetPtr != IntPtr.Zero)
                                {
                                    // marshal the results into something usable
                                    WsaQuerySet resultQuerySet = PeerNameResolver.MarshalWsaQuerySetNativeToWsaQuerySet(nativeQuerySetPtr, 0);
                                    // extract out the friendly cloud attributes
                                    CloudInfo resultCloudInfo = new CloudInfo();
                                    PnrpCloudInfo prnpCloudInfo = (PnrpCloudInfo)resultQuerySet.Blob;
                                    resultCloudInfo.Name = resultQuerySet.ServiceInstanceName;
                                    resultCloudInfo.Scope = prnpCloudInfo.Cloud.Scope;
                                    resultCloudInfo.ScopeId = prnpCloudInfo.Cloud.ScopeId;
                                    resultCloudInfo.State = prnpCloudInfo.dwCloudState;
                                    resultCloudInfo.Flags = prnpCloudInfo.Flags;
 
                                    // add it to the list to return later
                                    clouds.Add(resultCloudInfo);
                                }
                            }
                        }
                    }
 
                    // package up the results into a nice array
                    return (CloudInfo[])clouds.ToArray(typeof(CloudInfo));
                }
 
            }
 
            internal class PeerNameRegistrar : DiscoveryBase
            {
                const int RegistrationLifetime = 60 * 60; // 1 hour
 
                public PeerNameRegistrar()
                    : base()
                {
                }
 
                public void Register(PnrpRegistration registration, TimeSpan timeout)
                {
                    // fill in the PnrpInfo blob using the defaults
                    PnrpInfo pnrpInfo = new PnrpInfo();
                    pnrpInfo.dwLifetime = RegistrationLifetime;
                    pnrpInfo.lpwszIdentity = null;
                    pnrpInfo.dwSize = Marshal.SizeOf(pnrpInfo);
                    pnrpInfo.dwFlags = PNRPINFO_HINT;
                    IPEndPoint hint = PnrpPeerResolver.GetHint();
                    pnrpInfo.saHint = SOCKET_ADDRESS_SAFE.SocketAddressFromIPEndPoint(hint);
 
                    // fill in the query set
                    WsaQuerySet registerQuery = new WsaQuerySet();
                    registerQuery.NameSpace = NspNamespaces.Name;
                    registerQuery.NSProviderId = NsProviderName;
                    registerQuery.ServiceClassId = SvcIdNameV1;
                    registerQuery.ServiceInstanceName = registration.PeerName;
                    registerQuery.Comment = registration.Comment;
                    registerQuery.Context = registration.CloudName;
 
                    // copy over the addresses
                    if (registration.Addresses != null)
                    {
                        Fx.Assert(registration.Addresses.Length <= 4, "Pnrp supports only 4 addresses");
                        registerQuery.CsAddrInfos = new CsAddrInfo[registration.Addresses.Length];
                        for (int i = 0; i < registration.Addresses.Length; i++)
                        {
                            // the only interesting part of the CsAddrInfo is the LocalAddress
                            registerQuery.CsAddrInfos[i].LocalAddr = registration.Addresses[i];
                            registerQuery.CsAddrInfos[i].iProtocol = (int)ProtocolType.Tcp;
                            registerQuery.CsAddrInfos[i].iSocketType = (int)SocketType.Stream;
                        }
                    }
 
                    // copy the blob
                    registerQuery.Blob = pnrpInfo;
                    RegisterService(registerQuery);
                }
 
                public void Unregister(string peerName, List<string> clouds, TimeSpan timeout)
                {
                    TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
                    foreach (string cloud in clouds)
                    {
                        try
                        {
                            Unregister(peerName, cloud, timeoutHelper.RemainingTime());
                        }
                        catch (SocketException e)
                        {
                            DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                        }
                    }
                }
 
                public void Unregister(string peerName, string cloudName, TimeSpan timeout)
                {
                    // fill in the PnrpInfo with defaults
                    PnrpInfo identityInfo = new PnrpInfo();
                    identityInfo.lpwszIdentity = null;
                    identityInfo.dwSize = Marshal.SizeOf(typeof(PnrpInfo));
 
                    // fill in the query set
                    WsaQuerySet registerQuery = new WsaQuerySet();
                    registerQuery.NameSpace = NspNamespaces.Name;
                    registerQuery.NSProviderId = NsProviderName;
                    registerQuery.ServiceClassId = SvcIdNameV1;
                    registerQuery.ServiceInstanceName = peerName;
                    registerQuery.Context = cloudName;
                    registerQuery.Blob = identityInfo;
 
                    DeleteService(registerQuery);
                }
 
                void RegisterService(WsaQuerySet registerQuery)
                {
                    try
                    {
                        InvokeService(registerQuery, WsaSetServiceOp.Register, 0);
                    }
                    catch (PnrpException)
                    {
                        if (PnrpPeerResolver.MaxAddressEntriesV1 < registerQuery.CsAddrInfos.Length)
                        {
                            List<CsAddrInfo> infos = new List<CsAddrInfo>(registerQuery.CsAddrInfos);
                            infos.RemoveRange(PnrpPeerResolver.MaxAddressEntriesV1, registerQuery.CsAddrInfos.Length - PnrpPeerResolver.MaxAddressEntriesV1);
                            registerQuery.CsAddrInfos = infos.ToArray();
                            InvokeService(registerQuery, WsaSetServiceOp.Register, 0);
                        }
                        else
                            throw;
                    }
                }
 
                void DeleteService(WsaQuerySet registerQuery)
                {
                    InvokeService(registerQuery, WsaSetServiceOp.Delete, 0);
                }
 
                static void InvokeService(WsaQuerySet registerQuery, WsaSetServiceOp op, int flags)
                {
                    WsaQuerySetSafe native = WsaQuerySet.ToWsaQuerySetSafe(registerQuery);
                    using (native)
                    {
                        CriticalAllocHandle handle = CriticalAllocHandleWsaQuerySetSafe.FromWsaQuerySetSafe(native);
                        int retval = WSASetService(handle, op, flags);
                        if (retval != 0)
                        {
                            int error = WSAGetLastError();
                            PeerExceptionHelper.ThrowPnrpError(error, registerQuery.Context);
                        }
                    }
                }
            }
 
            internal class PeerNameResolver : AsyncResult
            {
                WsaQuerySet resolveQuery;
                List<PnrpRegistration> results;
                uint scopeId;
                Exception lastException;
                TimeoutHelper timeoutHelper;
 
                public PeerNameResolver(string peerName, int numberOfResultsRequested,
                    PnrpResolveCriteria resolveCriteria, TimeSpan timeout, List<PnrpRegistration> results)
                    : this(peerName, numberOfResultsRequested, resolveCriteria, 0, GlobalCloudName, timeout, results)
                {
                }
 
                public PeerNameResolver(string peerName, int numberOfResultsRequested,
                    PnrpResolveCriteria resolveCriteria, uint scopeId, string cloudName, TimeSpan timeout, List<PnrpRegistration> results)
                    : base(null, null)
                {
                    // pnrp has a hard-coded limit on the timeout value that can be passed to it
                    // maximum value is 10 minutes
                    if (timeout > MaxTimeout)
                    {
                        timeout = MaxTimeout;
                    }
                    timeoutHelper = new TimeoutHelper(timeout);
                    PnrpInfo resolveQueryInfo = new PnrpInfo();
                    resolveQueryInfo.dwSize = Marshal.SizeOf(typeof(PnrpInfo));
                    resolveQueryInfo.nMaxResolve = numberOfResultsRequested;
                    resolveQueryInfo.dwTimeout = (int)timeout.TotalSeconds;
                    resolveQueryInfo.dwLifetime = 0;
                    resolveQueryInfo.enNameState = 0;
                    resolveQueryInfo.lpwszIdentity = null;
                    resolveQueryInfo.dwFlags = PNRPINFO_HINT;
                    IPEndPoint hint = PnrpPeerResolver.GetHint();
                    resolveQueryInfo.enResolveCriteria = resolveCriteria;
                    resolveQueryInfo.saHint = SOCKET_ADDRESS_SAFE.SocketAddressFromIPEndPoint(hint);
                    resolveQuery = new WsaQuerySet();
                    resolveQuery.ServiceInstanceName = peerName;
                    resolveQuery.ServiceClassId = SvcIdNameV1;
                    resolveQuery.NameSpace = NspNamespaces.Name;
                    resolveQuery.NSProviderId = NsProviderName;
                    resolveQuery.Context = cloudName;
                    resolveQuery.Blob = resolveQueryInfo;
                    this.results = results;
                    this.scopeId = scopeId;
                    ActionItem.Schedule(new Action<object>(SyncEnumeration), null);
                }
 
                public void End()
                {
                    AsyncResult.End<PeerNameResolver>(this);
                }
 
                public void SyncEnumeration(object state)
                {
                    int retval = 0;
                    CriticalLookupHandle hLookup;
                    WsaQuerySetSafe native = WsaQuerySet.ToWsaQuerySetSafe(resolveQuery);
                    using (native)
                    {
                        CriticalAllocHandle handle = CriticalAllocHandleWsaQuerySetSafe.FromWsaQuerySetSafe(native);
                        retval = WSALookupServiceBegin(handle, WsaNspControlFlags.ReturnAll, out hLookup);
                    }
                    if (retval != 0)
                    {
                        lastException = new PnrpException(WSAGetLastError(), resolveQuery.Context);
                        Utility.CloseInvalidOutCriticalHandle(hLookup);
                        Complete(false, lastException);
                        return;
                    }
                    WsaQuerySet querySet = new WsaQuerySet();
 
                    // start with a sensible default size
                    int size = Marshal.SizeOf(typeof(WsaQuerySetSafe)) + 400;
                    CriticalAllocHandle nativeQuerySetPtr = CriticalAllocHandle.FromSize(size);
                    try
                    {
                        using (hLookup)
                        {
                            while (true)
                            {
                                if (timeoutHelper.RemainingTime() == TimeSpan.Zero)
                                {
                                    break;
                                }
                                retval = WSALookupServiceNext(hLookup, 0, ref size, (IntPtr)nativeQuerySetPtr);
                                if (retval != 0)
                                {
                                    int error = WSAGetLastError();
                                    if (error == (int)WsaError.WSAENOMORE || error == (int)WsaError.WSA_E_NO_MORE)
                                    {
                                        // no more
                                        break;
                                    }
 
                                    if (error == (int)WsaError.WSAEFAULT)
                                    {
                                        nativeQuerySetPtr = CriticalAllocHandle.FromSize(size);
                                        continue;
                                    }
 
                                    // unexpected error
                                    PeerExceptionHelper.ThrowPnrpError(error, querySet.Context);
                                }
                                else
                                {
                                    if (nativeQuerySetPtr != IntPtr.Zero)
                                    {
                                        // marshal the results into something useful
                                        querySet = MarshalWsaQuerySetNativeToWsaQuerySet(nativeQuerySetPtr, scopeId);
 
                                        // allocate the friendly PnrpRegistration and fill it in
                                        PnrpRegistration pnrpRegistration = new PnrpRegistration();
                                        pnrpRegistration.CloudName = querySet.Context;
                                        pnrpRegistration.Comment = querySet.Comment;
                                        pnrpRegistration.PeerName = querySet.ServiceInstanceName;
                                        pnrpRegistration.Addresses = new IPEndPoint[querySet.CsAddrInfos.Length];
                                        for (int i = 0; i < querySet.CsAddrInfos.Length; i++)
                                            pnrpRegistration.Addresses[i] = querySet.CsAddrInfos[i].LocalAddr;
 
                                        // add it to the list to return later.
                                        // all cloud enumeratos in the same scope will reference the same list and hence the lock.
                                        lock (results)
                                        {
                                            results.Add(pnrpRegistration);
                                        }
                                    }
                                }
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e)) throw;
                        DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                        if (DiagnosticUtility.ShouldTraceInformation)
                        {
                            PnrpResolveExceptionTraceRecord record = new PnrpResolveExceptionTraceRecord(resolveQuery.ServiceInstanceName, resolveQuery.Context, e);
                            if (DiagnosticUtility.ShouldTraceError)
                            {
                                TraceUtility.TraceEvent(TraceEventType.Error, TraceCode.PnrpResolveException,
                                    SR.GetString(SR.TraceCodePnrpResolveException), record, this, null);
                            }
                        }
                        lastException = e;
                    }
                    finally
                    {
                        Complete(false, lastException);
                    }
                }
 
                static internal WsaQuerySet MarshalWsaQuerySetNativeToWsaQuerySet(IntPtr pNativeData)
                {
                    return MarshalWsaQuerySetNativeToWsaQuerySet(pNativeData, 0);
                }
 
                static internal WsaQuerySet MarshalWsaQuerySetNativeToWsaQuerySet(IntPtr pNativeData, uint scopeId)
                {
                    if (pNativeData == IntPtr.Zero)
                        return null;
 
                    WsaQuerySet querySet = new WsaQuerySet();
                    // build a native structure from the raw memory
                    WsaQuerySetNative nativeQuerySet;
                    nativeQuerySet = (WsaQuerySetNative)Marshal.PtrToStructure(pNativeData,
                        typeof(WsaQuerySetNative));
                    CsAddrInfoNative nativeCsAddrInfo;
                    int sizeOfCsAddrInfo = Marshal.SizeOf(typeof(CsAddrInfoNative));
 
                    // copy over the simple fields
                    querySet.Context = Marshal.PtrToStringUni(nativeQuerySet.lpszContext);
                    querySet.NameSpace = nativeQuerySet.dwNameSpace;
                    querySet.ServiceInstanceName = Marshal.PtrToStringUni(nativeQuerySet.lpszServiceInstanceName);
                    querySet.Comment = Marshal.PtrToStringUni(nativeQuerySet.lpszComment);
 
                    // copy the addresses
                    querySet.CsAddrInfos = new CsAddrInfo[nativeQuerySet.dwNumberOfCsAddrs];
                    for (int i = 0; i < nativeQuerySet.dwNumberOfCsAddrs; i++)
                    {
                        IntPtr addressPtr = (IntPtr)(nativeQuerySet.lpcsaBuffer.ToInt64() + (i * sizeOfCsAddrInfo));
                        nativeCsAddrInfo = (CsAddrInfoNative)Marshal.PtrToStructure(addressPtr,
                            typeof(CsAddrInfoNative));
                        querySet.CsAddrInfos[i].iProtocol = nativeCsAddrInfo.iProtocol;
                        querySet.CsAddrInfos[i].iSocketType = nativeCsAddrInfo.iSocketType;
                        querySet.CsAddrInfos[i].LocalAddr = IPEndPointFromSocketAddress(nativeCsAddrInfo.LocalAddr, scopeId);
                        querySet.CsAddrInfos[i].RemoteAddr = IPEndPointFromSocketAddress(nativeCsAddrInfo.RemoteAddr, scopeId);
                    }
 
                    // copy the GUIDs
                    if (nativeQuerySet.lpNSProviderId != IntPtr.Zero)
                        querySet.NSProviderId = (Guid)Marshal.PtrToStructure(nativeQuerySet.lpNSProviderId,
                            typeof(Guid));
 
                    if (nativeQuerySet.lpServiceClassId != IntPtr.Zero)
                        querySet.ServiceClassId = (Guid)Marshal.PtrToStructure(nativeQuerySet.lpServiceClassId,
                            typeof(Guid));
 
                    // marshal the BLOB according to namespace
                    if (querySet.NameSpace == NspNamespaces.Cloud)
                    {
                        if (nativeQuerySet.lpBlob != IntPtr.Zero)
                        {
                            // give it a default value
                            querySet.Blob = new PnrpCloudInfo();
                            // marshal the blob in order to get the pointer
                            BlobNative blob = (BlobNative)Marshal.PtrToStructure(nativeQuerySet.lpBlob, typeof(BlobNative));
                            // marshal the actual PnrpCloudInfo
                            if (blob.pBlobData != IntPtr.Zero)
                                querySet.Blob = (PnrpCloudInfo)Marshal.PtrToStructure(blob.pBlobData,
                                    typeof(PnrpCloudInfo));
                        }
                    }
 
                    else if (querySet.NameSpace == NspNamespaces.Name)
                    {
                        if (nativeQuerySet.lpBlob != IntPtr.Zero)
                        {
                            // give it a default value
                            querySet.Blob = new PnrpInfo();
                            // marshal the blob in order to get the pointer
                            BlobSafe blob = (BlobSafe)Marshal.PtrToStructure(nativeQuerySet.lpBlob, typeof(BlobSafe));
                            // marshal the actual PnrpInfo
                            if (blob.pBlobData != IntPtr.Zero)
                            {
                                PnrpInfo pnrpInfo = (PnrpInfo)Marshal.PtrToStructure(blob.pBlobData,
                                    typeof(PnrpInfo));
                                querySet.Blob = pnrpInfo;
                            }
                        }
                    }
 
                    return querySet;
                }
 
                static IPEndPoint IPEndPointFromSocketAddress(SOCKET_ADDRESS_NATIVE socketAddress, uint scopeId)
                {
                    IPEndPoint endPoint = null;
                    if (socketAddress.lpSockAddr != IntPtr.Zero)
                    {
                        AddressFamily addressFamily = (AddressFamily)Marshal.ReadInt16(socketAddress.lpSockAddr);
                        if (addressFamily == AddressFamily.InterNetwork)
                        {
                            // if the sockaddr length is not the sizeof(sockaddr_in), the data is invalid so
                            // return an null endpoint
                            if (socketAddress.iSockaddrLength == Marshal.SizeOf(typeof(sockaddr_in)))
                            {
                                sockaddr_in sa = (sockaddr_in)Marshal.PtrToStructure(socketAddress.lpSockAddr,
                                    typeof(sockaddr_in));
                                endPoint = new IPEndPoint(new IPAddress(sa.sin_addr), sa.sin_port);
                            }
                        }
                        else if (addressFamily == AddressFamily.InterNetworkV6)
                        {
                            // if the sockaddr length is not the sizeof(sockaddr_in6), the data is invalid so
                            // return an null endpoint
                            if (socketAddress.iSockaddrLength == Marshal.SizeOf(typeof(sockaddr_in6)))
                            {
                                sockaddr_in6 sa = (sockaddr_in6)Marshal.PtrToStructure(socketAddress.lpSockAddr,
                                    typeof(sockaddr_in6));
                                if (scopeId != 0 && sa.sin6_scope_id != 0)
                                    scopeId = sa.sin6_scope_id;
                                endPoint = new IPEndPoint(new IPAddress(sa.sin6_addr, scopeId), sa.sin6_port);
                            }
                        }
                        // else this is an unknown address family, so return null
                    }
 
                    return endPoint;
                }
 
            }
        }
        public override bool Equals(object other)
        {
            return ((other as PnrpPeerResolver) != null);
        }
 
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
 
    }
}