You've already forked linux-packaging-mono
Rewrite with hard-coded offsets into the PE file format to discern if a binary is PE32 or PE32+, and then to determine if it contains a "CLR Data Directory" entry that looks valid. Tested with PE32 and PE32+ compiled Mono binaries, PE32 and PE32+ native binaries, and a random assortment of garbage files. Former-commit-id: 9e7ac86ec84f653a2f79b87183efd5b0ebda001b
2269 lines
101 KiB
C#
2269 lines
101 KiB
C#
//------------------------------------------------------------
|
|
// 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();
|
|
}
|
|
|
|
}
|
|
}
|