1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4 
5 namespace System.ServiceModel.Channels
6 {
7     using System.Collections;
8     using System.Collections.Generic;
9     using System.Collections.ObjectModel;
10     using System.Diagnostics;
11     using System.Diagnostics.CodeAnalysis;
12     using System.Globalization;
13     using System.Net;
14     using System.Net.Sockets;
15     using System.Runtime;
16     using System.Runtime.CompilerServices;
17     using System.Runtime.ConstrainedExecution;
18     using System.Runtime.InteropServices;
19     using System.Runtime.Versioning;
20     using System.Security.Permissions;
21     using System.ServiceModel;
22     using System.ServiceModel.Diagnostics;
23     using System.ServiceModel.PeerResolvers;
24     using System.ServiceProcess;
25     using System.Text;
26     using System.Threading;
27     using Microsoft.Win32.SafeHandles;
28 
29     sealed class PnrpPeerResolver : PeerResolver
30     {
31         UnsafePnrpNativeMethods.PeerNameRegistrar registrar = new UnsafePnrpNativeMethods.PeerNameRegistrar();
32         static bool isPnrpAvailable;
33         static bool isPnrpInstalled;
34         const UnsafePnrpNativeMethods.PnrpResolveCriteria resolutionScope = UnsafePnrpNativeMethods.PnrpResolveCriteria.NearestNonCurrentProcess;
35         public const int PNRPINFO_HINT = 0x00000001;
36 
37         internal const int CommentLength = 80;
38         internal const byte TcpTransport = 0x01;
39         internal const byte PayloadVersion = 0x01;
40         internal const char PathSeparator = '/';
41         internal const int MinGuids = 1;
42         internal const int MaxGuids = 2;
43         internal const byte GuidEscape = 0xFF;
44         internal const int MaxAddressEntries = 10;
45         internal const int MaxAddressEntriesV1 = 4;
46         internal const int MaxPathLength = 200; //this is known prefix+any guids
47         static TimeSpan MaxTimeout = new TimeSpan(0, 10, 0); //PNRP validates the timeout to be no greater than 10 minutes
48         static TimeSpan MaxResolveTimeout = new TimeSpan(0, 0, 45);
49         internal const string GlobalCloudName = "Global_";
50         static object SharedLock = new object();
51         static Random randomGenerator = new Random();
52         static TimeSpan TimeToWaitForStatus = TimeSpan.FromSeconds(15);
53         PeerReferralPolicy referralPolicy = PeerReferralPolicy.Share;
54 
55         [Flags]
56         internal enum PnrpResolveScope
57         {
58             None = 0,
59             Global = 1,
60             SiteLocal = 2,
61             LinkLocal = 4,
62             All = Global | SiteLocal | LinkLocal
63         }
64 
PnrpPeerResolver()65         static PnrpPeerResolver()
66         {
67             // determine if PNRP is installed
68             isPnrpAvailable = false;
69             using (UnsafePnrpNativeMethods.DiscoveryBase db = new UnsafePnrpNativeMethods.DiscoveryBase())
70             {
71                 isPnrpInstalled = db.IsPnrpInstalled();
72                 isPnrpAvailable = db.IsPnrpAvailable(TimeToWaitForStatus);
73             }
74         }
75 
PnrpPeerResolver()76         internal PnrpPeerResolver() : this(PeerReferralPolicy.Share) { }
PnrpPeerResolver(PeerReferralPolicy referralPolicy)77         internal PnrpPeerResolver(PeerReferralPolicy referralPolicy)
78         {
79             this.referralPolicy = referralPolicy;
80         }
81 
82         static Encoding PnrpEncoder
83         {
84             get
85             {
86                 return System.Text.Encoding.UTF8;
87             }
88         }
89 
90         public static bool IsPnrpAvailable
91         {
92             get { return isPnrpAvailable; }
93         }
94 
95         public static bool IsPnrpInstalled
96         {
97             get { return isPnrpInstalled; }
98         }
99 
GetHint()100         public static IPEndPoint GetHint()
101         {
102             byte[] bytes = new byte[16];
103             lock (SharedLock)
104             {
105                 randomGenerator.NextBytes(bytes);
106             }
107             return new IPEndPoint(new IPAddress(bytes), 0);
108         }
109 
110         // Get the hint for the node in this process that handles this meshid
111         // Get the nodeid for the current node - If the factory is using PrivatePeerNode then this can throw.
112         // in which case use a hint of 0
113         // The resolver prepends 0. to the meshid so we strip it off before locating the node.
114         // false means that the search must include the current process.
HasPeerNodeForMesh(string meshId)115         public static bool HasPeerNodeForMesh(string meshId)
116         {
117             PeerNodeImplementation node = null;
118             return PeerNodeImplementation.TryGet(meshId, out node);
119         }
120 
121         // PNRP doesn't support registering the same peername by the same identity in the same process.
122         // Thus, we cannot test the PNRP resolver between two nodes in the same process without a little help.
123         // By calling SetMeshExtensions, the resolver will register and resolver different ids, allowing two
124         // nodes to work in the same process.
125         string localExtension;
126         string remoteExtension;
SetMeshExtensions(string local, string remote)127         internal void SetMeshExtensions(string local, string remote)
128         {
129             localExtension = local;
130             remoteExtension = remote;
131         }
132 
EnumerateClouds(bool forResolve, Dictionary<uint, string> LinkCloudNames, Dictionary<uint, string> SiteCloudNames)133         internal PnrpResolveScope EnumerateClouds(bool forResolve, Dictionary<uint, string> LinkCloudNames, Dictionary<uint, string> SiteCloudNames)
134         {
135             bool foundActive = false;
136             PnrpResolveScope currentScope = PnrpResolveScope.None;
137             LinkCloudNames.Clear();
138             SiteCloudNames.Clear();
139             UnsafePnrpNativeMethods.CloudInfo[] cloudInfos = UnsafePnrpNativeMethods.PeerCloudEnumerator.GetClouds();
140 
141             // If we are resolving we should first look for active clouds only
142             // If we find some then we should return those to the caller
143             // otherwise we should just load up with clouds
144             if (forResolve)
145             {
146                 foreach (UnsafePnrpNativeMethods.CloudInfo cloud in cloudInfos)
147                 {
148                     if (cloud.State == UnsafePnrpNativeMethods.PnrpCloudState.Active)
149                     {
150                         if (cloud.Scope == UnsafePnrpNativeMethods.PnrpScope.Global)
151                         {
152                             currentScope |= PnrpResolveScope.Global;
153                             foundActive = true;
154                         }
155                         else if (cloud.Scope == UnsafePnrpNativeMethods.PnrpScope.LinkLocal)
156                         {
157                             Fx.Assert(!String.IsNullOrEmpty(cloud.Name), "Unknown scope id in the IPAddress");
158                             LinkCloudNames.Add(cloud.ScopeId, cloud.Name);
159                             currentScope |= PnrpResolveScope.LinkLocal;
160                             foundActive = true;
161                         }
162                         else if (cloud.Scope == UnsafePnrpNativeMethods.PnrpScope.SiteLocal)
163                         {
164                             Fx.Assert(!String.IsNullOrEmpty(cloud.Name), "Unknown scope id in the IPAddress");
165                             SiteCloudNames.Add(cloud.ScopeId, cloud.Name);
166                             currentScope |= PnrpResolveScope.SiteLocal;
167                             foundActive = true;
168                         }
169                     }
170                 }
171             }
172 
173             if (!foundActive)
174             {
175                 foreach (UnsafePnrpNativeMethods.CloudInfo cloud in cloudInfos)
176                 {
177                     if (!((cloud.State == UnsafePnrpNativeMethods.PnrpCloudState.Dead)
178                         || (cloud.State == UnsafePnrpNativeMethods.PnrpCloudState.Disabled)
179                         || (cloud.State == UnsafePnrpNativeMethods.PnrpCloudState.NoNet))
180                        )
181                     {
182                         if (cloud.Scope == UnsafePnrpNativeMethods.PnrpScope.Global)
183                         {
184                             currentScope |= PnrpResolveScope.Global;
185                             continue;
186                         }
187                         if (cloud.Scope == UnsafePnrpNativeMethods.PnrpScope.LinkLocal)
188                         {
189                             Fx.Assert(!String.IsNullOrEmpty(cloud.Name), "Unknown scope id in the IPAddress");
190                             LinkCloudNames.Add(cloud.ScopeId, cloud.Name);
191                             currentScope |= PnrpResolveScope.LinkLocal;
192                         }
193                         else if (cloud.Scope == UnsafePnrpNativeMethods.PnrpScope.SiteLocal)
194                         {
195                             Fx.Assert(!String.IsNullOrEmpty(cloud.Name), "Unknown scope id in the IPAddress");
196                             SiteCloudNames.Add(cloud.ScopeId, cloud.Name);
197                             currentScope |= PnrpResolveScope.SiteLocal;
198                         }
199                     }
200                 }
201             }
202             return currentScope;
203         }
204 
205         class RegistrationHandle
206         {
207             public string PeerName;
208             public List<string> Clouds;
RegistrationHandle(string peerName)209             public RegistrationHandle(string peerName)
210             {
211                 this.PeerName = peerName;
212                 Clouds = new List<string>();
213             }
AddCloud(string name)214             public void AddCloud(string name)
215             {
216                 this.Clouds.Add(name);
217             }
218         }
219 
220         public override bool CanShareReferrals
221         {
222             get
223             {
224                 return referralPolicy != PeerReferralPolicy.DoNotShare;
225             }
226         }
Register(string meshId, PeerNodeAddress nodeAddress, TimeSpan timeout)227         public override object Register(string meshId, PeerNodeAddress nodeAddress, TimeSpan timeout)
228         {
229             ThrowIfNoPnrp();
230 
231             PnrpRegistration globalEntry = null;
232             PnrpRegistration[] linkEntries = null;
233             PnrpRegistration[] siteEntries = null;
234 
235             RegistrationHandle regHandle = new RegistrationHandle(meshId);
236             Dictionary<uint, string> SiteCloudNames = new Dictionary<uint, string>();
237             Dictionary<uint, string> LinkCloudNames = new Dictionary<uint, string>();
238 
239             PnrpResolveScope availableScope = EnumerateClouds(false, LinkCloudNames, SiteCloudNames);
240 
241             if (availableScope == PnrpResolveScope.None)
242             {
243                 //could not find any clouds.
244                 PeerExceptionHelper.ThrowInvalidOperation_PnrpNoClouds();
245             }
246 
247             if (localExtension != null)
248                 meshId += localExtension;
249 
250             try
251             {
252                 PeerNodeAddressToPnrpRegistrations(meshId, LinkCloudNames, SiteCloudNames, nodeAddress, out linkEntries, out siteEntries, out globalEntry);
253             }
254             catch (Exception e)
255             {
256                 if (Fx.IsFatal(e)) throw;
257                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.PeerPnrpIllegalUri), e));
258             }
259             TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
260 
261             try
262             {
263                 PnrpResolveScope currentScope = PnrpResolveScope.None;
264                 if (globalEntry != null)
265                 {
266                     if (globalEntry.Addresses.Length > 0 && (availableScope & PnrpResolveScope.Global) != 0)
267                     {
268                         registrar.Register(globalEntry, timeoutHelper.RemainingTime());
269                         regHandle.AddCloud(globalEntry.CloudName);
270                         currentScope |= PnrpResolveScope.Global;
271                     }
272                 }
273                 if (linkEntries.Length > 0)
274                 {
275                     foreach (PnrpRegistration entry in linkEntries)
276                     {
277                         if (entry.Addresses.Length > 0)
278                         {
279                             registrar.Register(entry, timeoutHelper.RemainingTime());
280                             regHandle.AddCloud(entry.CloudName);
281                         }
282                     }
283                     currentScope |= PnrpResolveScope.LinkLocal;
284                 }
285                 if (siteEntries.Length > 0)
286                 {
287                     foreach (PnrpRegistration entry in siteEntries)
288                     {
289                         if (entry.Addresses.Length > 0)
290                         {
291                             registrar.Register(entry, timeoutHelper.RemainingTime());
292                             regHandle.AddCloud(entry.CloudName);
293                         }
294                     }
295                     currentScope |= PnrpResolveScope.SiteLocal;
296                 }
297                 if (currentScope == PnrpResolveScope.None)
298                 {
299                     // We have addresses but no cloud that corresponds to them
300                     // so we should throw an exception
301                     PeerExceptionHelper.ThrowInvalidOperation_PnrpAddressesUnsupported();
302                 }
303             }
304             catch (SocketException)
305             {
306                 try
307                 {
308                     Unregister(regHandle, timeoutHelper.RemainingTime());
309                 }
310                 catch (SocketException e)
311                 {
312                     DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
313                 }
314                 throw;
315             }
316 
317             if (DiagnosticUtility.ShouldTraceInformation)
318             {
319                 PnrpRegisterTraceRecord record = new PnrpRegisterTraceRecord(meshId, globalEntry, siteEntries, linkEntries);
320                 TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.PnrpRegisteredAddresses,
321                     SR.GetString(SR.TraceCodePnrpRegisteredAddresses),
322                     record, this, null);
323             }
324 
325             return regHandle;
326         }
327 
ThrowIfNoPnrp()328         void ThrowIfNoPnrp()
329         {
330             if (!isPnrpAvailable)
331             {
332                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
333                     SR.GetString(SR.PeerPnrpNotAvailable)));
334             }
335         }
336 
Unregister(object registrationId, TimeSpan timeout)337         public override void Unregister(object registrationId, TimeSpan timeout)
338         {
339             RegistrationHandle regHandle = registrationId as RegistrationHandle;
340             if (regHandle == null || String.IsNullOrEmpty(regHandle.PeerName))
341                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.PeerInvalidRegistrationId, regHandle), "registrationId"));
342             string meshId = regHandle.PeerName;
343 
344             // prepend a 0. for unsecured peername
345             string peerName = string.Format(CultureInfo.InvariantCulture, "0.{0}", meshId);
346             registrar.Unregister(peerName, regHandle.Clouds, timeout);
347 
348             if (DiagnosticUtility.ShouldTraceInformation)
349             {
350                 PnrpPeerResolverTraceRecord record = new PnrpPeerResolverTraceRecord(meshId, new List<PeerNodeAddress>());
351                 TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.PnrpUnregisteredAddresses,
352                     SR.GetString(SR.TraceCodePnrpUnregisteredAddresses),
353                     record, this, null);
354             }
355         }
356 
Update(object registrationId, PeerNodeAddress updatedNodeAddress, TimeSpan timeout)357         public override void Update(object registrationId, PeerNodeAddress updatedNodeAddress, TimeSpan timeout)
358         {
359             RegistrationHandle regHandle = registrationId as RegistrationHandle;
360             if (regHandle == null || string.IsNullOrEmpty(regHandle.PeerName))
361                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.PeerInvalidRegistrationId, regHandle), "registrationId"));
362 
363             string meshId = regHandle.PeerName;
364             Register(meshId, updatedNodeAddress, timeout);
365         }
366 
367         //return null in case of unrecognized format. consider adding logging.
PeerNodeAddressFromPnrpRegistration(PnrpRegistration input)368         PeerNodeAddress PeerNodeAddressFromPnrpRegistration(PnrpRegistration input)
369         {
370             List<IPAddress> addresses = new List<IPAddress>();
371             PeerNodeAddress result = null;
372             Guid[] guids;
373             StringBuilder pathBuilder = new StringBuilder(MaxPathLength);
374             int version = 0;
375             string protocolScheme;
376 
377             try
378             {
379                 if (input == null || String.IsNullOrEmpty(input.Comment))
380                     return null;
381                 Array.ForEach(input.Addresses, delegate(IPEndPoint obj) { addresses.Add(obj.Address); });
382                 if (addresses.Count != 0)
383                 {
384                     UriBuilder uriBuilder = new UriBuilder();
385                     uriBuilder.Port = input.Addresses[0].Port;
386                     uriBuilder.Host = addresses[0].ToString();
387                     pathBuilder.Append(PeerStrings.KnownServiceUriPrefix);
388                     CharEncoder.Decode(input.Comment, out version, out protocolScheme, out guids);
389 
390                     if (
391                         (version == PayloadVersion) &&
392                         (guids != null) && (guids.Length <= MaxGuids) &&
393                         (guids.Length >= MinGuids)
394                     )
395                     {
396                         uriBuilder.Scheme = protocolScheme;
397                         Array.ForEach(guids, delegate(Guid guid)
398                                             {
399                                                 pathBuilder.Append(PathSeparator + String.Format(CultureInfo.InvariantCulture, "{0}", guid.ToString()));
400                                             }
401                         );
402                         uriBuilder.Path = String.Format(CultureInfo.InvariantCulture, "{0}", pathBuilder.ToString());
403                         result = new PeerNodeAddress(new EndpointAddress(uriBuilder.Uri), new ReadOnlyCollection<IPAddress>(addresses));
404                     }
405                 }
406             }
407             catch (ArgumentException e)
408             {
409                 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
410             }
411             catch (FormatException e)
412             {
413                 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
414             }
415             catch (IndexOutOfRangeException e)
416             {
417                 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
418             }
419 
420             return result;
421 
422         }
423 
TrimToMaxAddresses(List<IPEndPoint> addressList)424         void TrimToMaxAddresses(List<IPEndPoint> addressList)
425         {
426             if (addressList.Count > MaxAddressEntries)
427             {
428                 addressList.RemoveRange(MaxAddressEntries, addressList.Count - MaxAddressEntries);
429             }
430         }
431 
PeerNodeAddressToPnrpRegistrations(string meshName, Dictionary<uint, string> LinkCloudNames, Dictionary<uint, string> SiteCloudNames, PeerNodeAddress input, out PnrpRegistration[] linkRegs, out PnrpRegistration[] siteRegs, out PnrpRegistration global)432         void PeerNodeAddressToPnrpRegistrations(string meshName, Dictionary<uint, string> LinkCloudNames, Dictionary<uint, string> SiteCloudNames, PeerNodeAddress input, out PnrpRegistration[] linkRegs, out PnrpRegistration[] siteRegs, out PnrpRegistration global)
433         {
434             PnrpRegistration reg = new PnrpRegistration();
435 
436             Dictionary<uint, PnrpRegistration> resultsLink = new Dictionary<uint, PnrpRegistration>();
437             Dictionary<uint, PnrpRegistration> resultsSite = new Dictionary<uint, PnrpRegistration>();
438             PnrpRegistration entry = null;
439             string scheme;
440             Guid[] guids;
441             ParseServiceUri(input.EndpointAddress.Uri, out scheme, out guids);
442             int port = input.EndpointAddress.Uri.Port;
443             if (port <= 0)
444                 port = TcpUri.DefaultPort;
445             string peerName = string.Format(CultureInfo.InvariantCulture, "0.{0}", meshName);
446             string comment = CharEncoder.Encode(PayloadVersion, scheme, guids);
447             global = null;
448             string cloudName = string.Empty;
449             foreach (IPAddress address in input.IPAddresses)
450             {
451                 if (address.AddressFamily == AddressFamily.InterNetworkV6
452                     &&
453                     ((address.IsIPv6LinkLocal) || (address.IsIPv6SiteLocal))
454                 )
455                 {
456                     if (address.IsIPv6LinkLocal)
457                     {
458                         if (!resultsLink.TryGetValue((uint)address.ScopeId, out entry))
459                         {
460                             if (!LinkCloudNames.TryGetValue((uint)address.ScopeId, out cloudName))
461                             {
462                                 continue;
463                             }
464                             entry = PnrpRegistration.Create(peerName, comment, cloudName);
465                             resultsLink.Add((uint)address.ScopeId, entry);
466                         }
467                     }
468                     else
469                     {
470                         if (!resultsSite.TryGetValue((uint)address.ScopeId, out entry))
471                         {
472                             if (!SiteCloudNames.TryGetValue((uint)address.ScopeId, out cloudName))
473                             {
474                                 continue;
475                             }
476                             entry = PnrpRegistration.Create(peerName, comment, cloudName);
477                             resultsSite.Add((uint)address.ScopeId, entry);
478                         }
479                     }
480                     entry.addressList.Add(new IPEndPoint(address, port));
481                 }
482                 else
483                 {
484                     if (global == null)
485                     {
486                         global = PnrpRegistration.Create(peerName, comment, GlobalCloudName);
487                     }
488                     global.addressList.Add(new IPEndPoint(address, port));
489                 }
490             }
491             if (global != null)
492             {
493                 if (global.addressList != null)
494                 {
495                     TrimToMaxAddresses(global.addressList);
496                     global.Addresses = global.addressList.ToArray();
497                 }
498                 else
499                     global.Addresses = new IPEndPoint[0];
500             }
501 
502             if (resultsLink.Count != 0)
503             {
504                 foreach (PnrpRegistration tempLink in resultsLink.Values)
505                 {
506                     if (tempLink.addressList != null)
507                     {
508                         TrimToMaxAddresses(tempLink.addressList);
509                         tempLink.Addresses = tempLink.addressList.ToArray();
510                     }
511                     else
512                     {
513                         tempLink.Addresses = new IPEndPoint[0];
514                     }
515                 }
516                 linkRegs = new PnrpRegistration[resultsLink.Count];
517                 resultsLink.Values.CopyTo(linkRegs, 0);
518             }
519             else
520                 linkRegs = new PnrpRegistration[0];
521             if (resultsSite.Count != 0)
522             {
523                 foreach (PnrpRegistration tempSite in resultsSite.Values)
524                 {
525                     if (tempSite.addressList != null)
526                     {
527                         TrimToMaxAddresses(tempSite.addressList);
528                         tempSite.Addresses = tempSite.addressList.ToArray();
529                     }
530                     else
531                     {
532                         tempSite.Addresses = new IPEndPoint[0];
533                     }
534                 }
535                 siteRegs = new PnrpRegistration[resultsSite.Count];
536                 resultsSite.Values.CopyTo(siteRegs, 0);
537             }
538             else
539                 siteRegs = new PnrpRegistration[0];
540         }
541 
ProtocolFromName(string name)542         static int ProtocolFromName(string name)
543         {
544             if (name == Uri.UriSchemeNetTcp)
545             {
546                 return TcpTransport;
547             }
548 
549             throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("name", SR.GetString(SR.PeerPnrpIllegalUri));
550         }
551 
NameFromProtocol(byte number)552         static string NameFromProtocol(byte number)
553         {
554             switch (number)
555             {
556                 case TcpTransport:
557                     return Uri.UriSchemeNetTcp;
558                 default:
559                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.PeerPnrpIllegalUri)));
560             }
561         }
562 
ParseServiceUri(Uri uri, out string scheme, out Guid[] result)563         void ParseServiceUri(Uri uri, out string scheme, out Guid[] result)
564         {
565             if (uri != null)
566             {
567                 if ((ProtocolFromName(uri.Scheme) != 0) && !String.IsNullOrEmpty(uri.AbsolutePath))
568                 {
569                     scheme = uri.Scheme;
570                     string[] parts = uri.AbsolutePath.Trim(new char[] { ' ', PathSeparator }).Split(PathSeparator);
571                     if ((0 == String.Compare(parts[0], PeerStrings.KnownServiceUriPrefix, StringComparison.OrdinalIgnoreCase)))
572                     {
573                         if (parts.Length >= MinGuids && parts.Length <= MaxGuids + 1)
574                         {
575                             result = new Guid[parts.Length - 1];
576                             try
577                             {
578                                 for (int i = 1; i < parts.Length; i++)
579                                     result[i - 1] = Fx.CreateGuid(parts[i]);
580                                 return;
581                             }
582                             catch (FormatException e)
583                             {
584                                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.PeerPnrpIllegalUri), e));
585                             }
586                         }
587                     }
588                 }
589             }
590             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.PeerPnrpIllegalUri)));
591         }
592 
MergeResults(Dictionary<string, PnrpRegistration> results, List<PnrpRegistration> regs)593         void MergeResults(Dictionary<string, PnrpRegistration> results, List<PnrpRegistration> regs)
594         {
595             PnrpRegistration entry = null;
596             foreach (PnrpRegistration reg in regs)
597             {
598                 if (!results.TryGetValue(reg.Comment, out entry))
599                 {
600                     entry = reg;
601                     results.Add(reg.Comment, reg);
602                     entry.addressList = new List<IPEndPoint>();
603                 }
604                 entry.addressList.AddRange(reg.Addresses);
605                 reg.Addresses = null;
606             }
607         }
608 
MergeResults(List<PeerNodeAddress> nodeAddressList, List<PnrpRegistration> globalRegistrations, List<PnrpRegistration> linkRegistrations, List<PnrpRegistration> siteRegistrations)609         void MergeResults(List<PeerNodeAddress> nodeAddressList, List<PnrpRegistration> globalRegistrations, List<PnrpRegistration> linkRegistrations, List<PnrpRegistration> siteRegistrations)
610         {
611             Dictionary<string, PnrpRegistration> results = new Dictionary<string, PnrpRegistration>();
612             MergeResults(results, globalRegistrations);
613             MergeResults(results, siteRegistrations);
614             MergeResults(results, linkRegistrations);
615             PeerNodeAddress result;
616             foreach (PnrpRegistration reg in results.Values)
617             {
618                 reg.Addresses = reg.addressList.ToArray();
619                 result = PeerNodeAddressFromPnrpRegistration(reg);
620                 if (result != null)
621                     nodeAddressList.Add(result);
622             }
623         }
624 
Resolve(string meshId, int maxAddresses, TimeSpan timeout)625         public override ReadOnlyCollection<PeerNodeAddress> Resolve(string meshId, int maxAddresses, TimeSpan timeout)
626         {
627             ThrowIfNoPnrp();
628             UnsafePnrpNativeMethods.PeerNameResolver resolver;
629             List<UnsafePnrpNativeMethods.PeerNameResolver> resolvers = new List<UnsafePnrpNativeMethods.PeerNameResolver>();
630             List<PnrpRegistration> globalRegistrations = new List<PnrpRegistration>();
631             List<PnrpRegistration> linkRegistrations = new List<PnrpRegistration>();
632             List<PnrpRegistration> siteRegistrations = new List<PnrpRegistration>();
633             List<WaitHandle> handles = new List<WaitHandle>();
634             Dictionary<uint, string> SiteCloudNames = new Dictionary<uint, string>();
635             Dictionary<uint, string> LinkCloudNames = new Dictionary<uint, string>();
636             UnsafePnrpNativeMethods.PnrpResolveCriteria targetScope = resolutionScope;
637             TimeoutHelper timeoutHelper = new TimeoutHelper(TimeSpan.Compare(timeout, MaxResolveTimeout) <= 0 ? timeout : MaxResolveTimeout);
638 
639             if (!HasPeerNodeForMesh(meshId))
640                 targetScope = UnsafePnrpNativeMethods.PnrpResolveCriteria.Any;
641             PnrpResolveScope currentScope = EnumerateClouds(true, LinkCloudNames, SiteCloudNames);
642 
643             if (remoteExtension != null)
644                 meshId += remoteExtension;
645 
646             // prepend a 0. for unsecured peername
647             string peerName = string.Format(CultureInfo.InvariantCulture, "0.{0}", meshId);
648             if ((currentScope & PnrpResolveScope.Global) != 0)
649             {
650                 resolver = new UnsafePnrpNativeMethods.PeerNameResolver(
651                                     peerName, maxAddresses, targetScope, 0, GlobalCloudName, timeoutHelper.RemainingTime(), globalRegistrations);
652                 handles.Add(resolver.AsyncWaitHandle);
653                 resolvers.Add(resolver);
654             }
655 
656             if ((currentScope & PnrpResolveScope.LinkLocal) != 0)
657             {
658                 foreach (KeyValuePair<uint, string> linkEntry in LinkCloudNames)
659                 {
660                     resolver = new UnsafePnrpNativeMethods.PeerNameResolver(
661                                     peerName, maxAddresses, targetScope, linkEntry.Key, linkEntry.Value, timeoutHelper.RemainingTime(), linkRegistrations);
662                     handles.Add(resolver.AsyncWaitHandle);
663                     resolvers.Add(resolver);
664                 }
665             }
666 
667             if ((currentScope & PnrpResolveScope.SiteLocal) != 0)
668             {
669                 foreach (KeyValuePair<uint, string> siteEntry in SiteCloudNames)
670                 {
671                     resolver = new UnsafePnrpNativeMethods.PeerNameResolver(
672                                     peerName, maxAddresses, targetScope, siteEntry.Key, siteEntry.Value, timeoutHelper.RemainingTime(), siteRegistrations);
673                     handles.Add(resolver.AsyncWaitHandle);
674                     resolvers.Add(resolver);
675                 }
676             }
677             if (handles.Count == 0)
678             {
679                 //could not find any clouds.
680                 if (DiagnosticUtility.ShouldTraceWarning)
681                 {
682                     Exception exception = new InvalidOperationException(SR.GetString(SR.PnrpNoClouds));
683                     PnrpResolveExceptionTraceRecord record = new PnrpResolveExceptionTraceRecord(meshId, string.Empty, exception);
684                     TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.PnrpResolvedAddresses,
685                         SR.GetString(SR.TraceCodePnrpResolvedAddresses),
686                         record, this, null);
687                 }
688                 return new ReadOnlyCollection<PeerNodeAddress>(new List<PeerNodeAddress>());
689             }
690 
691             Exception lastException = null;
692             foreach (UnsafePnrpNativeMethods.PeerNameResolver handle in resolvers)
693             {
694                 try
695                 {
696                     handle.End();
697                 }
698                 catch (SocketException e)
699                 {
700                     DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
701                     lastException = e;
702                 }
703             }
704 
705             List<PeerNodeAddress> nodeAddressList = new List<PeerNodeAddress>();
706             MergeResults(nodeAddressList, globalRegistrations, linkRegistrations, siteRegistrations);
707             if ((lastException != null) && (nodeAddressList.Count == 0))
708                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(lastException);
709             if (DiagnosticUtility.ShouldTraceInformation)
710             {
711                 PnrpPeerResolverTraceRecord record = new PnrpPeerResolverTraceRecord(meshId, nodeAddressList);
712                 TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.PnrpResolvedAddresses,
713                     SR.GetString(SR.TraceCodePnrpResolvedAddresses),
714                     record, this, null);
715             }
716             return new ReadOnlyCollection<PeerNodeAddress>(nodeAddressList);
717         }
718 
719 
720         // contains the friendly PNRP information
721         internal class PnrpRegistration
722         {
723             public string PeerName;
724             public string CloudName;
725             public string Comment;
726             public IPEndPoint[] Addresses;
727             public List<IPEndPoint> addressList;
728 
Create(string peerName, string comment, string cloudName)729             internal static PnrpRegistration Create(string peerName, string comment, string cloudName)
730             {
731                 PnrpRegistration reg = new PnrpRegistration();
732                 reg.Comment = comment;
733                 reg.CloudName = cloudName;
734                 reg.PeerName = peerName;
735                 reg.addressList = new List<IPEndPoint>();
736                 return reg;
737             }
738         }
739 
740         internal class CharEncoder
741         {
CheckAtLimit(int current)742             static void CheckAtLimit(int current)
743             {
744                 if (current + 1 >= CommentLength)
745                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.PeerPnrpIllegalUri)));
746             }
EncodeByte(byte b, ref int offset, byte[] bytes)747             static void EncodeByte(byte b, ref int offset, byte[] bytes)
748             {
749                 if (b == 0 || b == GuidEscape)
750                 {
751                     CheckAtLimit(offset);
752                     bytes[offset++] = GuidEscape;
753                 }
754                 CheckAtLimit(offset);
755                 bytes[offset++] = b;
756             }
757 
Encode(int version, string protocolName, Guid[] guids)758             static internal string Encode(int version, string protocolName, Guid[] guids)
759             {
760                 byte[] bytes = new byte[CommentLength];
761                 int i = 0;
762                 int protocol = ProtocolFromName(protocolName);
763                 EncodeByte(Convert.ToByte(version), ref i, bytes);
764                 EncodeByte(Convert.ToByte(protocol), ref i, bytes);
765                 EncodeByte(Convert.ToByte(guids.Length), ref i, bytes);
766                 foreach (Guid guid in guids)
767                 {
768                     foreach (byte b in guid.ToByteArray())
769                     {
770                         EncodeByte(Convert.ToByte(b), ref i, bytes);
771                     }
772                 }
773                 if (i % 2 != 0 && i < bytes.Length)
774                     bytes[i] = GuidEscape;
775                 // Now we have a collection of bytes lets turn it into a string
776                 int length = i;
777                 int clength = (length / 2) + (length % 2);         // Pack 2 bytes per char
778                 char[] chars = new char[clength];
779                 i = 0;
780                 for (int j = 0; j < clength; j++)
781                 {
782                     chars[j] = Convert.ToChar(bytes[i++] * 0x100 + bytes[i++]);
783                 }
784                 return new string(chars);
785             }
786 
GetByte(int offset, char[] chars)787             static byte GetByte(int offset, char[] chars)
788             {
789                 int p = offset / 2;
790                 int lo = offset % 2;
791                 return Convert.ToByte(lo == 1 ? chars[p] & GuidEscape : chars[p] / 0x100);
792             }
793 
DecodeByte(ref int offset, char[] chars)794             static byte DecodeByte(ref int offset, char[] chars)
795             {
796                 byte b = GetByte(offset++, chars);
797                 if (b == 0xff)
798                 {
799                     b = GetByte(offset++, chars);
800                 }
801                 return b;
802             }
803 
Decode(string buffer, out int version, out string protocolName, out Guid[] guids)804             static internal void Decode(string buffer, out int version, out string protocolName, out Guid[] guids)
805             {
806                 char[] chars = buffer.ToCharArray();
807                 byte protocol;
808                 int i = 0;
809 
810                 version = DecodeByte(ref i, chars);
811                 protocol = DecodeByte(ref i, chars);
812                 protocolName = NameFromProtocol(protocol);
813                 int length = DecodeByte(ref i, chars);
814                 guids = new Guid[length];
815 
816                 for (int g = 0; g < length; g++)
817                 {
818                     byte[] bytes = new byte[16];
819                     for (int j = 0; j < 16; j++)
820                     {
821                         bytes[j] = DecodeByte(ref i, chars);
822                     }
823                     guids[g] = new Guid(bytes);
824                 }
825             }
826         }
827 
828         internal enum PnrpErrorCodes
829         {
830             WSA_PNRP_ERROR_BASE = 11500,
831             WSA_PNRP_CLOUD_NOT_FOUND = 11501,
832             WSA_PNRP_CLOUD_DISABLED = 11502,
833             //these error codes are not relevant for now
834             //            WSA_PNRP_INVALID_IDENTITY = 11503,
835             //            WSA_PNRP_TOO_MUCH_LOAD = 11504,
836             WSA_PNRP_CLOUD_IS_RESOLVE_ONLY = 11505,
837             //            WSA_PNRP_CLIENT_INVALID_COMPARTMENT_ID = 11506,
838             WSA_PNRP_FW_PORT_BLOCKED = 11507,
839             WSA_PNRP_DUPLICATE_PEER_NAME = 11508,
840         }
841 
842         internal class PnrpException : SocketException
843         {
844             string message;
845 
PnrpException(int errorCode, string cloud)846             internal PnrpException(int errorCode, string cloud)
847                 : base(errorCode)
848             {
849                 LoadMessage(errorCode, cloud);
850             }
851 
852             public override string Message
853             {
854                 get
855                 {
856                     if (!String.IsNullOrEmpty(message))
857                         return message;
858                     else
859                         return base.Message;
860                 }
861             }
862 
LoadMessage(int errorCode, string cloud)863             void LoadMessage(int errorCode, string cloud)
864             {
865                 string formatString;
866                 switch ((PnrpErrorCodes)errorCode)
867                 {
868                     case PnrpErrorCodes.WSA_PNRP_CLOUD_DISABLED:
869                         formatString = SR.PnrpCloudDisabled;
870                         break;
871                     case PnrpErrorCodes.WSA_PNRP_CLOUD_NOT_FOUND:
872                         formatString = SR.PnrpCloudNotFound;
873                         break;
874                     case PnrpErrorCodes.WSA_PNRP_CLOUD_IS_RESOLVE_ONLY:
875                         formatString = SR.PnrpCloudResolveOnly;
876                         break;
877                     case PnrpErrorCodes.WSA_PNRP_FW_PORT_BLOCKED:
878                         formatString = SR.PnrpPortBlocked;
879                         break;
880                     case PnrpErrorCodes.WSA_PNRP_DUPLICATE_PEER_NAME:
881                         formatString = SR.PnrpDuplicatePeerName;
882                         break;
883                     default:
884                         formatString = null;
885                         break;
886                 }
887                 if (formatString != null)
888                     message = SR.GetString(formatString, cloud);
889             }
890         }
891 
892         internal static class UnsafePnrpNativeMethods
893         {
894             // WSA import functions
895             [DllImport("ws2_32.dll", CharSet = CharSet.Unicode)]
896             [ResourceExposure(ResourceScope.None)]
WSASetService(CriticalAllocHandle querySet, WsaSetServiceOp essOperation, int dwControlFlags)897             static extern int WSASetService(CriticalAllocHandle querySet, WsaSetServiceOp essOperation, int dwControlFlags);
898 
899             [DllImport("ws2_32.dll", CharSet = CharSet.Unicode)]
900             [ResourceExposure(ResourceScope.None)]
WSALookupServiceNext(CriticalLookupHandle hLookup, WsaNspControlFlags dwControlFlags, ref int lpdwBufferLength, IntPtr Results)901             static extern int WSALookupServiceNext(CriticalLookupHandle hLookup,
902                 WsaNspControlFlags dwControlFlags, ref int lpdwBufferLength, IntPtr Results);
903             [DllImport("ws2_32.dll", CharSet = CharSet.Unicode), ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
904             [ResourceExposure(ResourceScope.None)]
WSALookupServiceEnd(IntPtr hLookup)905             static extern int WSALookupServiceEnd(IntPtr hLookup);
906             [DllImport("ws2_32.dll", CharSet = CharSet.Unicode)]
907             [ResourceExposure(ResourceScope.None)]
WSALookupServiceBegin(CriticalAllocHandle query, WsaNspControlFlags dwControlFlags, out CriticalLookupHandle hLookup)908             static extern int WSALookupServiceBegin(CriticalAllocHandle query, WsaNspControlFlags dwControlFlags, out CriticalLookupHandle hLookup);
909             [DllImport("ws2_32.dll", CharSet = CharSet.Ansi)]
910             [ResourceExposure(ResourceScope.None)]
WSAStartup(Int16 wVersionRequested, ref WsaData lpWSAData)911             static extern int WSAStartup(Int16 wVersionRequested, ref WsaData lpWSAData);
912             [DllImport("ws2_32.dll", CharSet = CharSet.Ansi)]
913             [ResourceExposure(ResourceScope.None)]
WSACleanup()914             static extern int WSACleanup();
915             [DllImport("ws2_32.dll", CharSet = CharSet.Ansi)]
916             [ResourceExposure(ResourceScope.None)]
WSAGetLastError()917             static extern int WSAGetLastError();
918             [DllImport("ws2_32.dll", CharSet = CharSet.Unicode)]
919             [ResourceExposure(ResourceScope.None)]
WSAEnumNameSpaceProviders(ref int lpdwBufferLength, IntPtr lpnspBuffer)920             static extern int WSAEnumNameSpaceProviders(ref int lpdwBufferLength, IntPtr lpnspBuffer);
921 
922             // PNRP namespace identifiers
923             static Guid SvcIdCloud = new Guid(0xc2239ce6, 0x00c0, 0x4fbf, 0xba, 0xd6, 0x18, 0x13, 0x93, 0x85, 0xa4, 0x9a);
924             static Guid SvcIdNameV1 = new Guid(0xc2239ce5, 0x00c0, 0x4fbf, 0xba, 0xd6, 0x18, 0x13, 0x93, 0x85, 0xa4, 0x9a);
925             static Guid SvcIdName = new Guid(0xc2239ce7, 0x00c0, 0x4fbf, 0xba, 0xd6, 0x18, 0x13, 0x93, 0x85, 0xa4, 0x9a);
926             static Guid NsProviderName = new Guid(0x03fe89cd, 0x766d, 0x4976, 0xb9, 0xc1, 0xbb, 0x9b, 0xc4, 0x2c, 0x7b, 0x4d);
927             static Guid NsProviderCloud = new Guid(0x03fe89ce, 0x766d, 0x4976, 0xb9, 0xc1, 0xbb, 0x9b, 0xc4, 0x2c, 0x7b, 0x4d);
928 
929             const int MaxAddresses = 10;
930             const int MaxAddressesV1 = 4;
931             const Int16 RequiredWinsockVersion = 0x0202;
932 
933             // specifies the namespace used used by a specified WSAQUERYSET
934             [Serializable]
935             internal enum NspNamespaces
936             {
937                 Cloud = 39,
938                 Name = 38,
939             }
940 
941             [Serializable]
942             [Flags]
943             internal enum PnrpCloudFlags
944             {
945                 None = 0x0000,
946                 LocalName = 0x0001, //  Name not valid on other computers
947             }
948 
949             [Serializable]
950             internal enum PnrpCloudState
951             {
952                 Virtual = 0,        //  Not initialized
953                 Synchronizing = 1,  //  The cache is initializing
954                 Active = 2,         //  Cloud is active
955                 Dead = 3,            //  Initialized but lost network
956                 Disabled = 4,       //disabled in the registry
957                 NoNet = 5,          //active but lost network
958                 Alone = 6,
959             }
960 
961             [Serializable]
962             internal enum PnrpExtendedPayloadType
963             {
964                 None = 0,
965                 Binary,
966                 String
967             }
968 
969             // internal because it is exposed by PeerNameResolver
970             [Serializable]
971             internal enum PnrpResolveCriteria
972             {
973                 Default = 0,                    // Default = PNRP_RESOLVE_CRITERIA_NON_CURRENT_PROCESS_PEER_NAME
974                 Remote = 1,                     // match first 128 bits (remote node)
975                 NearestRemote = 2,              // match first 128 bits, and close to top 64 bits
976                 // of the second 128 bits (remote node)
977                 NonCurrentProcess = 3,          //  match first 128 bits (not in the current process)
978                 NearestNonCurrentProcess = 4,   // match first 128 bits, and close to top 64 bits
979                 // of the second 128 bits (not in the current process)
980                 Any = 5,                        // match first 128 bits (any node)
981                 Nearest = 6                     // match first 128 bits, and close to top 64 bits
982                 // of the second 128 bits (any node)
983             }
984 
985             [Serializable]
986             internal enum PnrpRegisteredIdState
987             {
988                 Ok = 1,     //  Id is active in cloud
989                 Problem = 2 //  Id is no longer registered in cloud
990             }
991 
992             internal enum PnrpScope
993             {
994                 Any = 0,
995                 Global = 1,
996                 SiteLocal = 2,
997                 LinkLocal = 3,
998             }
999 
1000             // primary use in this code is to specify what information should be returned by WSALookupServiceNext
1001             [Flags]
1002             internal enum WsaNspControlFlags
1003             {
1004                 Deep = 0x0001,
1005                 Containers = 0x0002,
1006                 NoContainers = 0x0004,
1007                 Nearest = 0x0008,
1008                 ReturnName = 0x0010,
1009                 ReturnType = 0x0020,
1010                 ReturnVersion = 0x0040,
1011                 ReturnComment = 0x0080,
1012                 ReturnAddr = 0x0100,
1013                 ReturnBlob = 0x0200,
1014                 ReturnAliases = 0x0400,
1015                 ReturnQueryString = 0x0800,
1016                 ReturnAll = 0x0FF0,
1017                 ResService = 0x8000,
1018                 FlushCache = 0x1000,
1019                 FlushPrevious = 0x2000,
1020             }
1021 
1022             internal enum WsaError
1023             {
1024                 WSAEINVAL = 10022,
1025                 WSAEFAULT = 10014,
1026                 WSAENOMORE = 10102,
1027                 WSA_E_NO_MORE = 10110,
1028                 WSANO_DATA = 11004
1029             }
1030 
1031             // specifies the operation of WSASetService
1032             internal enum WsaSetServiceOp
1033             {
1034                 Register = 0,
1035                 Deregister,
1036                 Delete
1037             }
1038 
1039             internal struct BlobSafe
1040             {
1041                 public int cbSize;
1042                 public CriticalAllocHandle pBlobData;
1043             }
1044 
1045             internal struct BlobNative
1046             {
1047                 public int cbSize;
1048                 public IntPtr pBlobData;
1049             }
1050 
1051 
1052             // PnrpResolver does not currently support any cloud except Global. If this needs to be changed, we will
1053             // need to be able to enumerate clouds.
1054 
1055             // managed equivalent of both PNRPCLOUDINFO and PNRP_CLOUD_ID
1056             // internal because it is exposed by PeerCloudEnumerator
1057             internal class CloudInfo
1058             {
1059                 public string Name;
1060                 public PnrpScope Scope;
1061                 public uint ScopeId;
1062                 public PnrpCloudState State;
1063                 public PnrpCloudFlags Flags;
1064             }
1065 
1066             [Serializable]
1067             [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
1068             internal struct CsAddrInfo
1069             {
1070                 public IPEndPoint LocalAddr;
1071                 public IPEndPoint RemoteAddr;
1072                 public int iSocketType;
1073                 public int iProtocol;
1074             }
1075 
1076             [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
1077             internal class CsAddrInfoSafe : IDisposable
1078             {
1079                 public SOCKET_ADDRESS_SAFE LocalAddr;
1080                 public SOCKET_ADDRESS_SAFE RemoteAddr;
1081                 public int iSocketType;
1082                 public int iProtocol;
1083                 bool disposed;
1084 
FromAddresses(CsAddrInfo[] addresses)1085                 public static CsAddrInfoSafe[] FromAddresses(CsAddrInfo[] addresses)
1086                 {
1087                     CsAddrInfoSafe addr;
1088                     CsAddrInfoSafe[] result = null;
1089                     if (addresses == null || addresses.Length == 0)
1090                         return null;
1091 
1092                     result = new CsAddrInfoSafe[addresses.Length];
1093                     int i = 0;
1094                     foreach (CsAddrInfo info in addresses)
1095                     {
1096                         addr = new CsAddrInfoSafe();
1097                         addr.LocalAddr = SOCKET_ADDRESS_SAFE.SocketAddressFromIPEndPoint(info.LocalAddr);
1098                         addr.RemoteAddr = SOCKET_ADDRESS_SAFE.SocketAddressFromIPEndPoint(info.RemoteAddr);
1099                         addr.iProtocol = info.iProtocol;
1100                         addr.iSocketType = info.iSocketType;
1101                         result[i++] = addr;
1102                     }
1103                     return result;
1104                 }
StructureToPtr(CsAddrInfoSafe input, IntPtr target)1105                 public static void StructureToPtr(CsAddrInfoSafe input, IntPtr target)
1106                 {
1107                     CsAddrInfoNative native;
1108                     native.iProtocol = input.iProtocol;
1109                     native.iSocketType = input.iSocketType;
1110                     native.LocalAddr.iSockaddrLength = input.LocalAddr.iSockaddrLength;
1111                     native.LocalAddr.lpSockAddr = input.LocalAddr.lpSockAddr;
1112                     native.RemoteAddr.iSockaddrLength = input.RemoteAddr.iSockaddrLength;
1113                     native.RemoteAddr.lpSockAddr = input.RemoteAddr.lpSockAddr;
1114 
1115                     Marshal.StructureToPtr(native, target, false);
1116                 }
~CsAddrInfoSafe()1117                 ~CsAddrInfoSafe()
1118                 {
1119                     Dispose(false);
1120                 }
Dispose()1121                 public virtual void Dispose()
1122                 {
1123                     Dispose(true);
1124                     GC.SuppressFinalize(this);
1125                 }
Dispose(bool disposing)1126                 void Dispose(bool disposing)
1127                 {
1128                     if (disposed)
1129                     {
1130                         if (disposing)
1131                         {
1132                             LocalAddr.Dispose();
1133                             RemoteAddr.Dispose();
1134                         }
1135                     }
1136                     disposed = true;
1137                 }
1138 
1139             }
1140 
1141             [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
1142             internal struct CsAddrInfoNative
1143             {
1144                 public SOCKET_ADDRESS_NATIVE LocalAddr;
1145                 public SOCKET_ADDRESS_NATIVE RemoteAddr;
1146                 public int iSocketType;
1147                 public int iProtocol;
1148             }
1149 
1150             [Serializable]
1151             [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
1152             internal struct PnrpCloudId
1153             {
1154                 public int AddressFamily; // should be AF_INET6
1155                 public PnrpScope Scope;     // Global, site, or link
1156                 public uint ScopeId;       // specifies interface
1157 
1158             }
1159 
1160             internal struct PnrpCloudInfo
1161             {
1162                 public int dwSize;            // size of this struct
1163                 public PnrpCloudId Cloud;       // network cloud information
1164                 public PnrpCloudState dwCloudState; // state of cloud
1165                 public PnrpCloudFlags Flags;
1166             }
1167 
1168             //native equivalent for easy marshalling.
1169             //should be exactly like PnrpInfo except CriticalHandles
1170             internal struct PnrpInfoNative
1171             {
1172                 public int dwSize;            // size of this struct
1173                 public string lpwszIdentity;  // identity name string
1174                 public int nMaxResolve;       // number of desired resolutions
1175                 public int dwTimeout;         // time in seconds to wait for responses
1176                 public int dwLifetime;        // time in seconds for validity
1177                 public PnrpResolveCriteria enResolveCriteria; // criteria for resolve matches
1178                 public int dwFlags;           // set of flags
1179                 public SOCKET_ADDRESS_NATIVE saHint; // IPv6 addr use for location
1180                 public PnrpRegisteredIdState enNameState; // state of registered name
1181             }
1182 
1183             [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
1184             internal struct PnrpInfo
1185             {
1186                 public int dwSize;            // size of this struct
1187                 public string lpwszIdentity;  // identity name string
1188                 public int nMaxResolve;       // number of desired resolutions
1189                 public int dwTimeout;         // time in seconds to wait for responses
1190                 public int dwLifetime;        // time in seconds for validity
1191                 public PnrpResolveCriteria enResolveCriteria; // criteria for resolve matches
1192                 public int dwFlags;           // set of flags
1193                 public SOCKET_ADDRESS_SAFE saHint; // IPv6 addr use for location
1194                 public PnrpRegisteredIdState enNameState; // state of registered name
ToPnrpInfoNativeSystem.ServiceModel.Channels.PnrpPeerResolver.UnsafePnrpNativeMethods.PnrpInfo1195                 public static void ToPnrpInfoNative(PnrpInfo source, ref PnrpInfoNative target)
1196                 {
1197                     target.dwSize = source.dwSize;
1198                     target.lpwszIdentity = source.lpwszIdentity;
1199                     target.nMaxResolve = source.nMaxResolve;
1200                     target.dwTimeout = source.dwTimeout;
1201                     target.dwLifetime = source.dwLifetime;
1202                     target.enResolveCriteria = source.enResolveCriteria;
1203                     target.dwFlags = source.dwFlags;
1204                     if (source.saHint != null)
1205                     {
1206                         target.saHint.lpSockAddr = source.saHint.lpSockAddr;
1207                         target.saHint.iSockaddrLength = source.saHint.iSockaddrLength;
1208                     }
1209                     else
1210                     {
1211                         target.saHint.lpSockAddr = IntPtr.Zero;
1212                         target.saHint.iSockaddrLength = 0;
1213                     }
1214                     target.enNameState = source.enNameState;
1215                 }
1216             }
1217 
1218             [Serializable]
1219             [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
1220             internal struct sockaddr_in
1221             {
1222                 public short sin_family;
1223                 public ushort sin_port;
1224                 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
1225                 public byte[] sin_addr;
1226                 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
1227                 public byte[] sin_zero;
1228             }
1229 
1230             [Serializable]
1231             [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
1232             internal struct sockaddr_in6
1233             {
1234                 public short sin6_family;
1235                 public ushort sin6_port;
1236                 public uint sin6_flowinfo;
1237                 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
1238                 public byte[] sin6_addr;
1239                 public uint sin6_scope_id;
1240             }
1241 
1242             internal class SOCKET_ADDRESS_SAFE : IDisposable
1243             {
1244                 public CriticalAllocHandle lpSockAddr;
1245                 public int iSockaddrLength;
1246                 bool disposed;
SocketAddressFromIPEndPoint(IPEndPoint endpoint)1247                 public static SOCKET_ADDRESS_SAFE SocketAddressFromIPEndPoint(IPEndPoint endpoint)
1248                 {
1249                     SOCKET_ADDRESS_SAFE socketAddress = new SOCKET_ADDRESS_SAFE();
1250                     if (endpoint == null)
1251                         return socketAddress;
1252 
1253                     if (endpoint.AddressFamily == AddressFamily.InterNetwork)
1254                     {
1255                         socketAddress.iSockaddrLength = Marshal.SizeOf(typeof(sockaddr_in));
1256                         socketAddress.lpSockAddr = CriticalAllocHandle.FromSize(socketAddress.iSockaddrLength);
1257                         sockaddr_in sa = new sockaddr_in();
1258                         sa.sin_family = (short)AddressFamily.InterNetwork;
1259                         sa.sin_port = (ushort)endpoint.Port;
1260                         sa.sin_addr = endpoint.Address.GetAddressBytes();
1261                         Marshal.StructureToPtr(sa, (IntPtr)socketAddress.lpSockAddr, false);
1262                     }
1263                     else if (endpoint.AddressFamily == AddressFamily.InterNetworkV6)
1264                     {
1265                         socketAddress.iSockaddrLength = Marshal.SizeOf(typeof(sockaddr_in6));
1266                         socketAddress.lpSockAddr = CriticalAllocHandle.FromSize(socketAddress.iSockaddrLength);
1267                         sockaddr_in6 sa = new sockaddr_in6();
1268                         sa.sin6_family = (short)AddressFamily.InterNetworkV6;
1269                         sa.sin6_port = (ushort)endpoint.Port;
1270                         sa.sin6_addr = endpoint.Address.GetAddressBytes();
1271                         sa.sin6_scope_id = (uint)endpoint.Address.ScopeId;
1272                         Marshal.StructureToPtr(sa, (IntPtr)socketAddress.lpSockAddr, false);
1273                     }
1274                     return socketAddress;
1275                 }
1276 
~SOCKET_ADDRESS_SAFE()1277                 ~SOCKET_ADDRESS_SAFE()
1278                 {
1279                     Dispose(false);
1280                 }
1281 
Dispose()1282                 public virtual void Dispose()
1283                 {
1284                     Dispose(true);
1285                     GC.SuppressFinalize(this);
1286                 }
1287 
Dispose(bool disposing)1288                 void Dispose(bool disposing)
1289                 {
1290                     if (!disposed)
1291                     {
1292                         if (disposing)
1293                             lpSockAddr.Dispose();
1294                     }
1295                     disposed = true;
1296                 }
1297 
1298             }
1299 
1300             [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
1301             internal struct SOCKET_ADDRESS_NATIVE
1302             {
1303                 public IntPtr lpSockAddr;
1304                 public int iSockaddrLength;
1305             }
1306 
1307             [Serializable]
1308             [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
1309             internal struct WsaData
1310             {
1311                 public Int16 wVersion;
1312                 public Int16 wHighVersion;
1313                 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)]
1314                 public string szDescription;
1315                 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 129)]
1316                 public string szSystemStatus;
1317 
1318                 public Int16 iMaxSockets;
1319                 public Int16 iMaxUdpDg;
1320                 public IntPtr lpVendorInfo;
1321             }
1322 
1323             [Serializable]
1324             [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
1325             internal struct WsaNamespaceInfo
1326             {
1327                 public Guid NSProviderId;
1328                 public int dwNameSpace;
1329                 public int fActive;
1330                 public int dwVersion;
1331                 // don't bother marshalling this as a string since we don't need to look at it
1332                 public IntPtr lpszIdentifier;
1333             }
1334 
1335             // managed equivalent of WSAQUERYSET
1336             internal class WsaQuerySet
1337             {
1338                 public string ServiceInstanceName;
1339                 public Guid ServiceClassId;
1340                 public string Comment;
1341                 public NspNamespaces NameSpace;
1342                 public Guid NSProviderId;
1343                 public string Context;
1344                 public CsAddrInfo[] CsAddrInfos;
1345                 public object Blob;
ToWsaQuerySetSafe(WsaQuerySet input)1346                 static public WsaQuerySetSafe ToWsaQuerySetSafe(WsaQuerySet input)
1347                 {
1348 
1349                     WsaQuerySetSafe result = new WsaQuerySetSafe();
1350                     if (input == null)
1351                         return result;
1352 
1353                     result.dwSize = Marshal.SizeOf(typeof(WsaQuerySetNative));
1354                     result.lpszServiceInstanceName = CriticalAllocHandleString.FromString(input.ServiceInstanceName);
1355                     result.lpServiceClassId = CriticalAllocHandleGuid.FromGuid(input.ServiceClassId);
1356                     result.lpszComment = CriticalAllocHandleString.FromString(input.Comment);
1357                     result.dwNameSpace = input.NameSpace;
1358                     result.lpNSProviderId = CriticalAllocHandleGuid.FromGuid(input.NSProviderId);
1359                     result.lpszContext = CriticalAllocHandleString.FromString(input.Context);
1360                     result.dwNumberOfProtocols = 0;
1361                     result.lpafpProtocols = IntPtr.Zero; // not used
1362                     result.lpszQueryString = IntPtr.Zero;
1363 
1364                     if (input.CsAddrInfos != null)
1365                     {
1366                         result.dwNumberOfCsAddrs = input.CsAddrInfos.Length;
1367                         result.addressList = CsAddrInfoSafe.FromAddresses(input.CsAddrInfos);
1368                     }
1369                     result.dwOutputFlags = 0;
1370                     result.lpBlob = CriticalAllocHandlePnrpBlob.FromPnrpBlob(input.Blob);
1371 
1372                     return result;
1373                 }
1374             }
1375 
1376             internal class CriticalAllocHandlePnrpBlob : CriticalAllocHandle
1377             {
FromPnrpBlob(object input)1378                 public static CriticalAllocHandle FromPnrpBlob(object input)
1379                 {
1380                     BlobSafe blob = new BlobSafe();
1381                     if (input != null)
1382                     {
1383                         if (input.GetType() == typeof(PnrpInfo))
1384                         {
1385                             int blobSize = Marshal.SizeOf(typeof(PnrpInfoNative));
1386                             blob.pBlobData = CriticalAllocHandle.FromSize(blobSize + Marshal.SizeOf(typeof(BlobNative)));
1387 
1388                             //write the BlobSafe fields first,
1389                             BlobNative nativeBlob;
1390                             nativeBlob.cbSize = blobSize;
1391                             nativeBlob.pBlobData = (IntPtr)(((IntPtr)blob.pBlobData).ToInt64() + Marshal.SizeOf(typeof(BlobNative)));
1392                             Marshal.StructureToPtr(nativeBlob, (IntPtr)blob.pBlobData, false);
1393                             PnrpInfo pnrpInfo = (PnrpInfo)input;
1394                             pnrpInfo.dwSize = blobSize;
1395                             PnrpInfoNative nativeInfo = new PnrpInfoNative();
1396                             PnrpInfo.ToPnrpInfoNative(pnrpInfo, ref nativeInfo);
1397                             Marshal.StructureToPtr(nativeInfo, (IntPtr)nativeBlob.pBlobData, false);
1398                             blob.cbSize = blobSize;
1399                         }
1400                         else if (input.GetType() == typeof(PnrpCloudInfo))
1401                         {
1402                             int blobSize = Marshal.SizeOf(input.GetType());
1403                             blob.pBlobData = CriticalAllocHandle.FromSize(blobSize + Marshal.SizeOf(typeof(BlobNative)));
1404 
1405                             //write the BlobSafe fields first,
1406                             BlobNative nativeBlob;
1407                             nativeBlob.cbSize = blobSize;
1408                             nativeBlob.pBlobData = (IntPtr)(((IntPtr)blob.pBlobData).ToInt64() + Marshal.SizeOf(typeof(BlobNative)));
1409                             Marshal.StructureToPtr(nativeBlob, (IntPtr)blob.pBlobData, false);
1410                             PnrpCloudInfo cloudInfo = (PnrpCloudInfo)input;
1411                             cloudInfo.dwSize = Marshal.SizeOf(typeof(PnrpCloudInfo));
1412                             Marshal.StructureToPtr(cloudInfo, (IntPtr)nativeBlob.pBlobData, false);
1413                             blob.cbSize = blobSize;
1414                         }
1415                         else
1416                         {
1417                             throw Fx.AssertAndThrow("Unknown payload type!");
1418                         }
1419                     }
1420                     return blob.pBlobData;
1421 
1422                 }
1423             }
1424 
1425             internal class CriticalAllocHandleString : CriticalAllocHandle
1426             {
FromString(string input)1427                 public static CriticalAllocHandle FromString(string input)
1428                 {
1429                     CriticalAllocHandleString result = new CriticalAllocHandleString();
1430                     RuntimeHelpers.PrepareConstrainedRegions();
1431                     try { }
1432                     finally
1433                     {
1434                         result.SetHandle(Marshal.StringToHGlobalUni(input));
1435                     }
1436                     return result;
1437                 }
1438             }
1439 
1440             internal class CriticalAllocHandleWsaQuerySetSafe : CriticalAllocHandle
1441             {
CalculateSize(WsaQuerySetSafe safeQuerySet)1442                 static int CalculateSize(WsaQuerySetSafe safeQuerySet)
1443                 {
1444                     int structSize = Marshal.SizeOf(typeof(WsaQuerySetNative));
1445                     if (safeQuerySet.addressList != null)
1446                         structSize += safeQuerySet.addressList.Length * Marshal.SizeOf(typeof(CsAddrInfoNative));
1447                     return structSize;
1448                 }
1449 
FromWsaQuerySetSafe(WsaQuerySetSafe safeQuerySet)1450                 public static CriticalAllocHandle FromWsaQuerySetSafe(WsaQuerySetSafe safeQuerySet)
1451                 {
1452                     CriticalAllocHandle result = CriticalAllocHandle.FromSize(CalculateSize(safeQuerySet));
1453                     WsaQuerySetSafe.StructureToPtr(safeQuerySet, (IntPtr)result);
1454                     return result;
1455                 }
1456             }
1457 
1458             [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
1459             internal class WsaQuerySetSafe : IDisposable
1460             {
1461                 public int dwSize;
1462                 public CriticalAllocHandle lpszServiceInstanceName;
1463                 public CriticalAllocHandle lpServiceClassId;
1464                 public IntPtr lpVersion; // not used
1465                 public CriticalAllocHandle lpszComment;
1466                 public NspNamespaces dwNameSpace;
1467                 public CriticalAllocHandle lpNSProviderId;
1468                 public CriticalAllocHandle lpszContext;
1469                 public int dwNumberOfProtocols; // 0
1470                 public IntPtr lpafpProtocols; // not used
1471                 public IntPtr lpszQueryString; // not used
1472                 public int dwNumberOfCsAddrs;
1473                 public CsAddrInfoSafe[] addressList;
1474                 public int dwOutputFlags; // 0
1475                 public CriticalAllocHandle lpBlob;
1476                 bool disposed;
1477 
~WsaQuerySetSafe()1478                 ~WsaQuerySetSafe()
1479                 {
1480                     Dispose(false);
1481                 }
1482 
Dispose()1483                 public virtual void Dispose()
1484                 {
1485                     Dispose(true);
1486                     GC.SuppressFinalize(this);
1487                 }
1488 
Dispose(bool disposing)1489                 void Dispose(bool disposing)
1490                 {
1491                     if (!disposed)
1492                     {
1493                         if (disposing)
1494                         {
1495                             if (lpszServiceInstanceName != null)
1496                                 lpszServiceInstanceName.Dispose();
1497                             if (lpServiceClassId != null)
1498                                 lpServiceClassId.Dispose();
1499                             if (lpszComment != null)
1500                                 lpszComment.Dispose();
1501                             if (lpNSProviderId != null)
1502                                 lpNSProviderId.Dispose();
1503                             if (lpBlob != null)
1504                                 lpBlob.Dispose();
1505                             if (addressList != null)
1506                             {
1507                                 foreach (CsAddrInfoSafe addr in addressList)
1508                                 {
1509                                     addr.Dispose();
1510                                 }
1511                             }
1512                         }
1513                     }
1514                     disposed = true;
1515                 }
1516 
StructureToPtr(WsaQuerySetSafe input, IntPtr target)1517                 static public void StructureToPtr(WsaQuerySetSafe input, IntPtr target)
1518                 {
1519                     WsaQuerySetNative native = new WsaQuerySetNative();
1520                     native.dwSize = input.dwSize;
1521                     native.lpszServiceInstanceName = input.lpszServiceInstanceName;
1522                     native.lpServiceClassId = input.lpServiceClassId;
1523                     native.lpVersion = IntPtr.Zero; // not used
1524                     native.lpszComment = input.lpszComment;
1525                     native.dwNameSpace = input.dwNameSpace;
1526                     native.lpNSProviderId = input.lpNSProviderId;
1527                     native.lpszContext = input.lpszContext;
1528                     native.dwNumberOfProtocols = 0; // 0
1529                     native.lpafpProtocols = IntPtr.Zero; // not used
1530                     native.lpszQueryString = IntPtr.Zero; // not used
1531                     native.dwNumberOfCsAddrs = input.dwNumberOfCsAddrs;
1532                     native.dwOutputFlags = 0; // 0
1533                     native.lpBlob = input.lpBlob;
1534 
1535                     Int64 sockAddressStart = target.ToInt64() + Marshal.SizeOf(typeof(WsaQuerySetNative));
1536                     native.lpcsaBuffer = (IntPtr)sockAddressStart;
1537 
1538                     Marshal.StructureToPtr(native, target, false);
1539                     MarshalSafeAddressesToNative(input, (IntPtr)sockAddressStart);
1540 
1541                 }
1542 
MarshalSafeAddressesToNative(WsaQuerySetSafe safeQuery, IntPtr target)1543                 public static void MarshalSafeAddressesToNative(WsaQuerySetSafe safeQuery, IntPtr target)
1544                 {
1545                     // marshal the addresses
1546                     if (safeQuery.addressList != null && safeQuery.addressList.Length > 0)
1547                     {
1548                         int sizeOfCsAddrInfo = Marshal.SizeOf(typeof(CsAddrInfoNative));
1549                         Int64 start = target.ToInt64();
1550                         Fx.Assert(start % IntPtr.Size == 0, "Invalid alignment!!");
1551                         foreach (CsAddrInfoSafe safeAddress in safeQuery.addressList)
1552                         {
1553                             CsAddrInfoSafe.StructureToPtr(safeAddress, (IntPtr)start);
1554                             start += sizeOfCsAddrInfo;
1555                         }
1556                     }
1557                 }
1558 
1559             }
1560 
1561             [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
1562             internal struct WsaQuerySetNative
1563             {
1564                 public int dwSize;
1565                 public IntPtr lpszServiceInstanceName;
1566                 public IntPtr lpServiceClassId;
1567                 public IntPtr lpVersion; // not used
1568                 public IntPtr lpszComment;
1569                 public NspNamespaces dwNameSpace;
1570                 public IntPtr lpNSProviderId;
1571                 public IntPtr lpszContext;
1572                 public int dwNumberOfProtocols; // 0
1573                 public IntPtr lpafpProtocols; // not used
1574                 public IntPtr lpszQueryString; // not used
1575                 public int dwNumberOfCsAddrs;
1576                 public IntPtr lpcsaBuffer;
1577                 public int dwOutputFlags; // 0
1578                 public IntPtr lpBlob;
1579             }
1580 
1581             internal class CriticalLookupHandle : CriticalHandleZeroOrMinusOneIsInvalid
1582             {
ReleaseHandle()1583                 protected override bool ReleaseHandle()
1584                 {
1585                     return WSALookupServiceEnd(handle) == 0;
1586                 }
1587             }
1588 
1589             // base class for ref-counting WSA uses and calling WSAStartup/WSAShutdown
1590             internal class DiscoveryBase : MarshalByRefObject, IDisposable
1591             {
1592                 static int refCount = 0;
1593                 static object refCountLock = new object();
1594                 bool disposed;
1595 
DiscoveryBase()1596                 public DiscoveryBase()
1597                 {
1598                     lock (refCountLock)
1599                     {
1600                         if (refCount == 0)
1601                         {
1602                             WsaData WinsockVersion = new WsaData();
1603                             int ret = WSAStartup(UnsafePnrpNativeMethods.RequiredWinsockVersion, ref WinsockVersion);
1604                             if (ret != 0)
1605                             {
1606                                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SocketException(ret));
1607                             }
1608                         }
1609                         refCount++;
1610                     }
1611                 }
1612 
Dispose()1613                 public void Dispose()
1614                 {
1615                     Dispose(true);
1616                     GC.SuppressFinalize(this);
1617                 }
1618 
Dispose(bool disposing)1619                 public void Dispose(bool disposing)
1620                 {
1621                     if (!disposed)
1622                     {
1623                         lock (refCountLock)
1624                         {
1625                             refCount--;
1626                             if (refCount == 0)
1627                             {
1628                                 WSACleanup();
1629                             }
1630                         }
1631                     }
1632                     disposed = true;
1633                 }
1634 
~DiscoveryBase()1635                 ~DiscoveryBase()
1636                 {
1637                     this.Dispose(false);
1638                 }
1639 
1640                 [SuppressMessage(FxCop.Category.Security, FxCop.Rule.AptcaMethodsShouldOnlyCallAptcaMethods, Justification = "ServiceController has demands for ServiceControllerPermission.")]
IsPnrpServiceRunning(TimeSpan waitForService)1641                 public bool IsPnrpServiceRunning(TimeSpan waitForService)
1642                 {
1643                     TimeoutHelper timeoutHelper = new TimeoutHelper(waitForService);
1644                     try
1645                     {
1646                         using (ServiceController sc = new ServiceController("pnrpsvc"))
1647                         {
1648                             try
1649                             {
1650                                 if (sc.Status == ServiceControllerStatus.StopPending)
1651                                 {
1652                                     sc.WaitForStatus(ServiceControllerStatus.Stopped, timeoutHelper.RemainingTime());
1653                                 }
1654                                 if (sc.Status == ServiceControllerStatus.Stopped)
1655                                 {
1656                                     sc.Start();
1657                                 }
1658                                 sc.WaitForStatus(ServiceControllerStatus.Running, timeoutHelper.RemainingTime());
1659                             }
1660                             catch (Exception e)
1661                             {
1662                                 if (Fx.IsFatal(e)) throw;
1663                                 if (e is InvalidOperationException || e is TimeoutException)
1664                                     return false;
1665                                 else
1666                                     throw;
1667                             }
1668                             return (sc.Status == ServiceControllerStatus.Running);
1669                         }
1670                     }
1671                     catch (InvalidOperationException e)
1672                     {
1673                         DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
1674                         Fx.Assert("IsPnrpServiceRunning should be called after IsPnrpInstalled");
1675                         return false;
1676                     }
1677                 }
1678 
IsPnrpAvailable(TimeSpan waitForService)1679                 public bool IsPnrpAvailable(TimeSpan waitForService)
1680                 {
1681                     if (!IsPnrpInstalled())
1682                         return false;
1683 
1684                     //make sure that the service is running
1685                     if (!IsPnrpServiceRunning(waitForService))
1686                         return false;
1687                     // If PNRP is installed, ensure that it supports extended payload by attempting to register with
1688                     // an invalid query set. If extended payload is not available, "WSASERVICE_NOT_FOUND" is returned.
1689                     // Otherwise, "WSAEINVAL" is returned.
1690 
1691                     //UPDATE: we will work with PNRP 1.0 if it is available.
1692                     // a separate implementation will work with payload support when available.
1693                     WsaQuerySet querySet = new WsaQuerySet();
1694                     querySet.NSProviderId = NsProviderName;
1695                     querySet.ServiceClassId = SvcIdNameV1;
1696                     int res = InvokeService(querySet, WsaSetServiceOp.Register, 0);
1697 
1698                     //on xp 64bit, WSANO_DATA is returned
1699                     if (res == (int)WsaError.WSAEINVAL || res == (int)WsaError.WSANO_DATA)
1700                         return true;
1701 
1702                     // if the call didn't fail or returned any other error, PNRP clearly isn't working properly
1703                     return false;
1704 
1705                 }
1706 
1707                 // determine if any version of PNRP is installed and available
IsPnrpInstalled()1708                 public bool IsPnrpInstalled()
1709                 {
1710                     int size = 0;
1711                     int nProviders;
1712                     CriticalAllocHandle dataPtr = null;
1713                     // retrieve the list of installed namespace providers
1714                     // implemented in a loop in case the size changes between the first and second calls
1715                     while (true)
1716                     {
1717                         nProviders = WSAEnumNameSpaceProviders(ref size, (IntPtr)dataPtr);
1718                         if (nProviders != -1) // success
1719                             break;
1720 
1721                         int error = WSAGetLastError();
1722                         if (error != (int)WsaError.WSAEFAULT) // buffer length to small
1723                             return false; // any other error effectively means that PNRP isn't usable
1724 
1725                         dataPtr = CriticalAllocHandle.FromSize(size);
1726                     }
1727 
1728                     // loop through the providers
1729                     for (int i = 0; i < nProviders; i++)
1730                     {
1731                         IntPtr nsInfoPtr = (IntPtr)(((IntPtr)dataPtr).ToInt64() + i *
1732                             Marshal.SizeOf(typeof(WsaNamespaceInfo)));
1733                         WsaNamespaceInfo nsInfo = (WsaNamespaceInfo)Marshal.PtrToStructure(nsInfoPtr,
1734                             typeof(WsaNamespaceInfo));
1735 
1736                         // if this is the PNRP name namespace provider and it is active, it is installed
1737                         if (nsInfo.NSProviderId == NsProviderName && nsInfo.fActive != 0)
1738                             return true;
1739                     }
1740 
1741                     // no PNRP name namespace provider found
1742                     return false;
1743                 }
1744 
InvokeService(WsaQuerySet registerQuery, WsaSetServiceOp op, int flags)1745                 int InvokeService(WsaQuerySet registerQuery, WsaSetServiceOp op, int flags)
1746                 {
1747                     WsaQuerySetSafe native = WsaQuerySet.ToWsaQuerySetSafe(registerQuery);
1748                     int error = 0;
1749                     using (native)
1750                     {
1751                         CriticalAllocHandle handle = CriticalAllocHandleWsaQuerySetSafe.FromWsaQuerySetSafe(native);
1752                         using (handle)
1753                         {
1754                             int retval = WSASetService(handle, op, flags);
1755                             if (retval != 0)
1756                             {
1757                                 error = WSAGetLastError();
1758                             }
1759                         }
1760                     }
1761                     return error;
1762                 }
1763 
1764             }
1765 
1766             public class PeerCloudEnumerator : DiscoveryBase
1767             {
GetClouds()1768                 static public CloudInfo[] GetClouds()
1769                 {
1770                     int retval = 0;
1771                     ArrayList clouds = new ArrayList();
1772                     WsaQuerySet querySet = new WsaQuerySet();
1773                     CriticalLookupHandle hLookup;
1774 
1775                     PnrpCloudInfo cloudInfo = new PnrpCloudInfo();
1776                     cloudInfo.dwSize = Marshal.SizeOf(typeof(PnrpCloudInfo));
1777                     cloudInfo.Cloud.Scope = PnrpScope.Any;
1778                     cloudInfo.dwCloudState = (PnrpCloudState)0;
1779                     cloudInfo.Flags = PnrpCloudFlags.None;
1780                     querySet.NameSpace = NspNamespaces.Cloud;
1781                     querySet.NSProviderId = NsProviderCloud;
1782                     querySet.ServiceClassId = SvcIdCloud;
1783                     querySet.Blob = cloudInfo;
1784 
1785                     WsaQuerySetSafe native = WsaQuerySet.ToWsaQuerySetSafe(querySet);
1786                     using (native)
1787                     {
1788                         CriticalAllocHandle handle = CriticalAllocHandleWsaQuerySetSafe.FromWsaQuerySetSafe(native);
1789                         retval = WSALookupServiceBegin(handle, WsaNspControlFlags.ReturnAll, out hLookup);
1790                     }
1791                     if (retval != 0)
1792                     {
1793                         // unable to start the enumeration
1794                         SocketException exception = new SocketException(WSAGetLastError());
1795                         Utility.CloseInvalidOutCriticalHandle(hLookup);
1796                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(exception);
1797                     }
1798 
1799                     // start with a sensible default size
1800                     int size = Marshal.SizeOf(typeof(WsaQuerySetSafe)) + 200;
1801                     //wrap in CriticalAllocHandle when PAYLOAD is enabled
1802                     CriticalAllocHandle nativeQuerySetPtr = CriticalAllocHandle.FromSize(size);
1803                     using (hLookup)
1804                     {
1805                         while (true)
1806                         {
1807                             retval = WSALookupServiceNext(hLookup, 0, ref size, (IntPtr)nativeQuerySetPtr);
1808                             if (retval != 0)
1809                             {
1810                                 int error = WSAGetLastError();
1811                                 if (error == (int)WsaError.WSAENOMORE || error == (int)WsaError.WSA_E_NO_MORE)
1812                                 {
1813                                     // no more
1814                                     break;
1815                                 }
1816                                 if (error == (int)WsaError.WSAEFAULT)
1817                                 {
1818                                     // buffer too small, allocate a bigger one of the specified size
1819                                     if (nativeQuerySetPtr != null)
1820                                     {
1821                                         nativeQuerySetPtr.Dispose();
1822                                         nativeQuerySetPtr = null;
1823                                     }
1824                                     //wrap in CriticalAllocHandle when PAYLOAD is enabled
1825                                     nativeQuerySetPtr = CriticalAllocHandle.FromSize(size);
1826                                     continue;
1827                                 }
1828 
1829                                 // unexpected error
1830                                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SocketException(error));
1831                             }
1832                             else
1833                             {
1834                                 if (nativeQuerySetPtr != IntPtr.Zero)
1835                                 {
1836                                     // marshal the results into something usable
1837                                     WsaQuerySet resultQuerySet = PeerNameResolver.MarshalWsaQuerySetNativeToWsaQuerySet(nativeQuerySetPtr, 0);
1838                                     // extract out the friendly cloud attributes
1839                                     CloudInfo resultCloudInfo = new CloudInfo();
1840                                     PnrpCloudInfo prnpCloudInfo = (PnrpCloudInfo)resultQuerySet.Blob;
1841                                     resultCloudInfo.Name = resultQuerySet.ServiceInstanceName;
1842                                     resultCloudInfo.Scope = prnpCloudInfo.Cloud.Scope;
1843                                     resultCloudInfo.ScopeId = prnpCloudInfo.Cloud.ScopeId;
1844                                     resultCloudInfo.State = prnpCloudInfo.dwCloudState;
1845                                     resultCloudInfo.Flags = prnpCloudInfo.Flags;
1846 
1847                                     // add it to the list to return later
1848                                     clouds.Add(resultCloudInfo);
1849                                 }
1850                             }
1851                         }
1852                     }
1853 
1854                     // package up the results into a nice array
1855                     return (CloudInfo[])clouds.ToArray(typeof(CloudInfo));
1856                 }
1857 
1858             }
1859 
1860             internal class PeerNameRegistrar : DiscoveryBase
1861             {
1862                 const int RegistrationLifetime = 60 * 60; // 1 hour
1863 
PeerNameRegistrar()1864                 public PeerNameRegistrar()
1865                     : base()
1866                 {
1867                 }
1868 
Register(PnrpRegistration registration, TimeSpan timeout)1869                 public void Register(PnrpRegistration registration, TimeSpan timeout)
1870                 {
1871                     // fill in the PnrpInfo blob using the defaults
1872                     PnrpInfo pnrpInfo = new PnrpInfo();
1873                     pnrpInfo.dwLifetime = RegistrationLifetime;
1874                     pnrpInfo.lpwszIdentity = null;
1875                     pnrpInfo.dwSize = Marshal.SizeOf(pnrpInfo);
1876                     pnrpInfo.dwFlags = PNRPINFO_HINT;
1877                     IPEndPoint hint = PnrpPeerResolver.GetHint();
1878                     pnrpInfo.saHint = SOCKET_ADDRESS_SAFE.SocketAddressFromIPEndPoint(hint);
1879 
1880                     // fill in the query set
1881                     WsaQuerySet registerQuery = new WsaQuerySet();
1882                     registerQuery.NameSpace = NspNamespaces.Name;
1883                     registerQuery.NSProviderId = NsProviderName;
1884                     registerQuery.ServiceClassId = SvcIdNameV1;
1885                     registerQuery.ServiceInstanceName = registration.PeerName;
1886                     registerQuery.Comment = registration.Comment;
1887                     registerQuery.Context = registration.CloudName;
1888 
1889                     // copy over the addresses
1890                     if (registration.Addresses != null)
1891                     {
1892                         Fx.Assert(registration.Addresses.Length <= 4, "Pnrp supports only 4 addresses");
1893                         registerQuery.CsAddrInfos = new CsAddrInfo[registration.Addresses.Length];
1894                         for (int i = 0; i < registration.Addresses.Length; i++)
1895                         {
1896                             // the only interesting part of the CsAddrInfo is the LocalAddress
1897                             registerQuery.CsAddrInfos[i].LocalAddr = registration.Addresses[i];
1898                             registerQuery.CsAddrInfos[i].iProtocol = (int)ProtocolType.Tcp;
1899                             registerQuery.CsAddrInfos[i].iSocketType = (int)SocketType.Stream;
1900                         }
1901                     }
1902 
1903                     // copy the blob
1904                     registerQuery.Blob = pnrpInfo;
1905                     RegisterService(registerQuery);
1906                 }
1907 
Unregister(string peerName, List<string> clouds, TimeSpan timeout)1908                 public void Unregister(string peerName, List<string> clouds, TimeSpan timeout)
1909                 {
1910                     TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
1911                     foreach (string cloud in clouds)
1912                     {
1913                         try
1914                         {
1915                             Unregister(peerName, cloud, timeoutHelper.RemainingTime());
1916                         }
1917                         catch (SocketException e)
1918                         {
1919                             DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
1920                         }
1921                     }
1922                 }
1923 
Unregister(string peerName, string cloudName, TimeSpan timeout)1924                 public void Unregister(string peerName, string cloudName, TimeSpan timeout)
1925                 {
1926                     // fill in the PnrpInfo with defaults
1927                     PnrpInfo identityInfo = new PnrpInfo();
1928                     identityInfo.lpwszIdentity = null;
1929                     identityInfo.dwSize = Marshal.SizeOf(typeof(PnrpInfo));
1930 
1931                     // fill in the query set
1932                     WsaQuerySet registerQuery = new WsaQuerySet();
1933                     registerQuery.NameSpace = NspNamespaces.Name;
1934                     registerQuery.NSProviderId = NsProviderName;
1935                     registerQuery.ServiceClassId = SvcIdNameV1;
1936                     registerQuery.ServiceInstanceName = peerName;
1937                     registerQuery.Context = cloudName;
1938                     registerQuery.Blob = identityInfo;
1939 
1940                     DeleteService(registerQuery);
1941                 }
1942 
RegisterService(WsaQuerySet registerQuery)1943                 void RegisterService(WsaQuerySet registerQuery)
1944                 {
1945                     try
1946                     {
1947                         InvokeService(registerQuery, WsaSetServiceOp.Register, 0);
1948                     }
1949                     catch (PnrpException)
1950                     {
1951                         if (PnrpPeerResolver.MaxAddressEntriesV1 < registerQuery.CsAddrInfos.Length)
1952                         {
1953                             List<CsAddrInfo> infos = new List<CsAddrInfo>(registerQuery.CsAddrInfos);
1954                             infos.RemoveRange(PnrpPeerResolver.MaxAddressEntriesV1, registerQuery.CsAddrInfos.Length - PnrpPeerResolver.MaxAddressEntriesV1);
1955                             registerQuery.CsAddrInfos = infos.ToArray();
1956                             InvokeService(registerQuery, WsaSetServiceOp.Register, 0);
1957                         }
1958                         else
1959                             throw;
1960                     }
1961                 }
1962 
DeleteService(WsaQuerySet registerQuery)1963                 void DeleteService(WsaQuerySet registerQuery)
1964                 {
1965                     InvokeService(registerQuery, WsaSetServiceOp.Delete, 0);
1966                 }
1967 
InvokeService(WsaQuerySet registerQuery, WsaSetServiceOp op, int flags)1968                 static void InvokeService(WsaQuerySet registerQuery, WsaSetServiceOp op, int flags)
1969                 {
1970                     WsaQuerySetSafe native = WsaQuerySet.ToWsaQuerySetSafe(registerQuery);
1971                     using (native)
1972                     {
1973                         CriticalAllocHandle handle = CriticalAllocHandleWsaQuerySetSafe.FromWsaQuerySetSafe(native);
1974                         int retval = WSASetService(handle, op, flags);
1975                         if (retval != 0)
1976                         {
1977                             int error = WSAGetLastError();
1978                             PeerExceptionHelper.ThrowPnrpError(error, registerQuery.Context);
1979                         }
1980                     }
1981                 }
1982             }
1983 
1984             internal class PeerNameResolver : AsyncResult
1985             {
1986                 WsaQuerySet resolveQuery;
1987                 List<PnrpRegistration> results;
1988                 uint scopeId;
1989                 Exception lastException;
1990                 TimeoutHelper timeoutHelper;
1991 
PeerNameResolver(string peerName, int numberOfResultsRequested, PnrpResolveCriteria resolveCriteria, TimeSpan timeout, List<PnrpRegistration> results)1992                 public PeerNameResolver(string peerName, int numberOfResultsRequested,
1993                     PnrpResolveCriteria resolveCriteria, TimeSpan timeout, List<PnrpRegistration> results)
1994                     : this(peerName, numberOfResultsRequested, resolveCriteria, 0, GlobalCloudName, timeout, results)
1995                 {
1996                 }
1997 
PeerNameResolver(string peerName, int numberOfResultsRequested, PnrpResolveCriteria resolveCriteria, uint scopeId, string cloudName, TimeSpan timeout, List<PnrpRegistration> results)1998                 public PeerNameResolver(string peerName, int numberOfResultsRequested,
1999                     PnrpResolveCriteria resolveCriteria, uint scopeId, string cloudName, TimeSpan timeout, List<PnrpRegistration> results)
2000                     : base(null, null)
2001                 {
2002                     // pnrp has a hard-coded limit on the timeout value that can be passed to it
2003                     // maximum value is 10 minutes
2004                     if (timeout > MaxTimeout)
2005                     {
2006                         timeout = MaxTimeout;
2007                     }
2008                     timeoutHelper = new TimeoutHelper(timeout);
2009                     PnrpInfo resolveQueryInfo = new PnrpInfo();
2010                     resolveQueryInfo.dwSize = Marshal.SizeOf(typeof(PnrpInfo));
2011                     resolveQueryInfo.nMaxResolve = numberOfResultsRequested;
2012                     resolveQueryInfo.dwTimeout = (int)timeout.TotalSeconds;
2013                     resolveQueryInfo.dwLifetime = 0;
2014                     resolveQueryInfo.enNameState = 0;
2015                     resolveQueryInfo.lpwszIdentity = null;
2016                     resolveQueryInfo.dwFlags = PNRPINFO_HINT;
2017                     IPEndPoint hint = PnrpPeerResolver.GetHint();
2018                     resolveQueryInfo.enResolveCriteria = resolveCriteria;
2019                     resolveQueryInfo.saHint = SOCKET_ADDRESS_SAFE.SocketAddressFromIPEndPoint(hint);
2020                     resolveQuery = new WsaQuerySet();
2021                     resolveQuery.ServiceInstanceName = peerName;
2022                     resolveQuery.ServiceClassId = SvcIdNameV1;
2023                     resolveQuery.NameSpace = NspNamespaces.Name;
2024                     resolveQuery.NSProviderId = NsProviderName;
2025                     resolveQuery.Context = cloudName;
2026                     resolveQuery.Blob = resolveQueryInfo;
2027                     this.results = results;
2028                     this.scopeId = scopeId;
2029                     ActionItem.Schedule(new Action<object>(SyncEnumeration), null);
2030                 }
2031 
End()2032                 public void End()
2033                 {
2034                     AsyncResult.End<PeerNameResolver>(this);
2035                 }
2036 
SyncEnumeration(object state)2037                 public void SyncEnumeration(object state)
2038                 {
2039                     int retval = 0;
2040                     CriticalLookupHandle hLookup;
2041                     WsaQuerySetSafe native = WsaQuerySet.ToWsaQuerySetSafe(resolveQuery);
2042                     using (native)
2043                     {
2044                         CriticalAllocHandle handle = CriticalAllocHandleWsaQuerySetSafe.FromWsaQuerySetSafe(native);
2045                         retval = WSALookupServiceBegin(handle, WsaNspControlFlags.ReturnAll, out hLookup);
2046                     }
2047                     if (retval != 0)
2048                     {
2049                         lastException = new PnrpException(WSAGetLastError(), resolveQuery.Context);
2050                         Utility.CloseInvalidOutCriticalHandle(hLookup);
2051                         Complete(false, lastException);
2052                         return;
2053                     }
2054                     WsaQuerySet querySet = new WsaQuerySet();
2055 
2056                     // start with a sensible default size
2057                     int size = Marshal.SizeOf(typeof(WsaQuerySetSafe)) + 400;
2058                     CriticalAllocHandle nativeQuerySetPtr = CriticalAllocHandle.FromSize(size);
2059                     try
2060                     {
2061                         using (hLookup)
2062                         {
2063                             while (true)
2064                             {
2065                                 if (timeoutHelper.RemainingTime() == TimeSpan.Zero)
2066                                 {
2067                                     break;
2068                                 }
2069                                 retval = WSALookupServiceNext(hLookup, 0, ref size, (IntPtr)nativeQuerySetPtr);
2070                                 if (retval != 0)
2071                                 {
2072                                     int error = WSAGetLastError();
2073                                     if (error == (int)WsaError.WSAENOMORE || error == (int)WsaError.WSA_E_NO_MORE)
2074                                     {
2075                                         // no more
2076                                         break;
2077                                     }
2078 
2079                                     if (error == (int)WsaError.WSAEFAULT)
2080                                     {
2081                                         nativeQuerySetPtr = CriticalAllocHandle.FromSize(size);
2082                                         continue;
2083                                     }
2084 
2085                                     // unexpected error
2086                                     PeerExceptionHelper.ThrowPnrpError(error, querySet.Context);
2087                                 }
2088                                 else
2089                                 {
2090                                     if (nativeQuerySetPtr != IntPtr.Zero)
2091                                     {
2092                                         // marshal the results into something useful
2093                                         querySet = MarshalWsaQuerySetNativeToWsaQuerySet(nativeQuerySetPtr, scopeId);
2094 
2095                                         // allocate the friendly PnrpRegistration and fill it in
2096                                         PnrpRegistration pnrpRegistration = new PnrpRegistration();
2097                                         pnrpRegistration.CloudName = querySet.Context;
2098                                         pnrpRegistration.Comment = querySet.Comment;
2099                                         pnrpRegistration.PeerName = querySet.ServiceInstanceName;
2100                                         pnrpRegistration.Addresses = new IPEndPoint[querySet.CsAddrInfos.Length];
2101                                         for (int i = 0; i < querySet.CsAddrInfos.Length; i++)
2102                                             pnrpRegistration.Addresses[i] = querySet.CsAddrInfos[i].LocalAddr;
2103 
2104                                         // add it to the list to return later.
2105                                         // all cloud enumeratos in the same scope will reference the same list and hence the lock.
2106                                         lock (results)
2107                                         {
2108                                             results.Add(pnrpRegistration);
2109                                         }
2110                                     }
2111                                 }
2112                             }
2113                         }
2114                     }
2115                     catch (Exception e)
2116                     {
2117                         if (Fx.IsFatal(e)) throw;
2118                         DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
2119                         if (DiagnosticUtility.ShouldTraceInformation)
2120                         {
2121                             PnrpResolveExceptionTraceRecord record = new PnrpResolveExceptionTraceRecord(resolveQuery.ServiceInstanceName, resolveQuery.Context, e);
2122                             if (DiagnosticUtility.ShouldTraceError)
2123                             {
2124                                 TraceUtility.TraceEvent(TraceEventType.Error, TraceCode.PnrpResolveException,
2125                                     SR.GetString(SR.TraceCodePnrpResolveException), record, this, null);
2126                             }
2127                         }
2128                         lastException = e;
2129                     }
2130                     finally
2131                     {
2132                         Complete(false, lastException);
2133                     }
2134                 }
2135 
MarshalWsaQuerySetNativeToWsaQuerySet(IntPtr pNativeData)2136                 static internal WsaQuerySet MarshalWsaQuerySetNativeToWsaQuerySet(IntPtr pNativeData)
2137                 {
2138                     return MarshalWsaQuerySetNativeToWsaQuerySet(pNativeData, 0);
2139                 }
2140 
MarshalWsaQuerySetNativeToWsaQuerySet(IntPtr pNativeData, uint scopeId)2141                 static internal WsaQuerySet MarshalWsaQuerySetNativeToWsaQuerySet(IntPtr pNativeData, uint scopeId)
2142                 {
2143                     if (pNativeData == IntPtr.Zero)
2144                         return null;
2145 
2146                     WsaQuerySet querySet = new WsaQuerySet();
2147                     // build a native structure from the raw memory
2148                     WsaQuerySetNative nativeQuerySet;
2149                     nativeQuerySet = (WsaQuerySetNative)Marshal.PtrToStructure(pNativeData,
2150                         typeof(WsaQuerySetNative));
2151                     CsAddrInfoNative nativeCsAddrInfo;
2152                     int sizeOfCsAddrInfo = Marshal.SizeOf(typeof(CsAddrInfoNative));
2153 
2154                     // copy over the simple fields
2155                     querySet.Context = Marshal.PtrToStringUni(nativeQuerySet.lpszContext);
2156                     querySet.NameSpace = nativeQuerySet.dwNameSpace;
2157                     querySet.ServiceInstanceName = Marshal.PtrToStringUni(nativeQuerySet.lpszServiceInstanceName);
2158                     querySet.Comment = Marshal.PtrToStringUni(nativeQuerySet.lpszComment);
2159 
2160                     // copy the addresses
2161                     querySet.CsAddrInfos = new CsAddrInfo[nativeQuerySet.dwNumberOfCsAddrs];
2162                     for (int i = 0; i < nativeQuerySet.dwNumberOfCsAddrs; i++)
2163                     {
2164                         IntPtr addressPtr = (IntPtr)(nativeQuerySet.lpcsaBuffer.ToInt64() + (i * sizeOfCsAddrInfo));
2165                         nativeCsAddrInfo = (CsAddrInfoNative)Marshal.PtrToStructure(addressPtr,
2166                             typeof(CsAddrInfoNative));
2167                         querySet.CsAddrInfos[i].iProtocol = nativeCsAddrInfo.iProtocol;
2168                         querySet.CsAddrInfos[i].iSocketType = nativeCsAddrInfo.iSocketType;
2169                         querySet.CsAddrInfos[i].LocalAddr = IPEndPointFromSocketAddress(nativeCsAddrInfo.LocalAddr, scopeId);
2170                         querySet.CsAddrInfos[i].RemoteAddr = IPEndPointFromSocketAddress(nativeCsAddrInfo.RemoteAddr, scopeId);
2171                     }
2172 
2173                     // copy the GUIDs
2174                     if (nativeQuerySet.lpNSProviderId != IntPtr.Zero)
2175                         querySet.NSProviderId = (Guid)Marshal.PtrToStructure(nativeQuerySet.lpNSProviderId,
2176                             typeof(Guid));
2177 
2178                     if (nativeQuerySet.lpServiceClassId != IntPtr.Zero)
2179                         querySet.ServiceClassId = (Guid)Marshal.PtrToStructure(nativeQuerySet.lpServiceClassId,
2180                             typeof(Guid));
2181 
2182                     // marshal the BLOB according to namespace
2183                     if (querySet.NameSpace == NspNamespaces.Cloud)
2184                     {
2185                         if (nativeQuerySet.lpBlob != IntPtr.Zero)
2186                         {
2187                             // give it a default value
2188                             querySet.Blob = new PnrpCloudInfo();
2189                             // marshal the blob in order to get the pointer
2190                             BlobNative blob = (BlobNative)Marshal.PtrToStructure(nativeQuerySet.lpBlob, typeof(BlobNative));
2191                             // marshal the actual PnrpCloudInfo
2192                             if (blob.pBlobData != IntPtr.Zero)
2193                                 querySet.Blob = (PnrpCloudInfo)Marshal.PtrToStructure(blob.pBlobData,
2194                                     typeof(PnrpCloudInfo));
2195                         }
2196                     }
2197 
2198                     else if (querySet.NameSpace == NspNamespaces.Name)
2199                     {
2200                         if (nativeQuerySet.lpBlob != IntPtr.Zero)
2201                         {
2202                             // give it a default value
2203                             querySet.Blob = new PnrpInfo();
2204                             // marshal the blob in order to get the pointer
2205                             BlobSafe blob = (BlobSafe)Marshal.PtrToStructure(nativeQuerySet.lpBlob, typeof(BlobSafe));
2206                             // marshal the actual PnrpInfo
2207                             if (blob.pBlobData != IntPtr.Zero)
2208                             {
2209                                 PnrpInfo pnrpInfo = (PnrpInfo)Marshal.PtrToStructure(blob.pBlobData,
2210                                     typeof(PnrpInfo));
2211                                 querySet.Blob = pnrpInfo;
2212                             }
2213                         }
2214                     }
2215 
2216                     return querySet;
2217                 }
2218 
IPEndPointFromSocketAddress(SOCKET_ADDRESS_NATIVE socketAddress, uint scopeId)2219                 static IPEndPoint IPEndPointFromSocketAddress(SOCKET_ADDRESS_NATIVE socketAddress, uint scopeId)
2220                 {
2221                     IPEndPoint endPoint = null;
2222                     if (socketAddress.lpSockAddr != IntPtr.Zero)
2223                     {
2224                         AddressFamily addressFamily = (AddressFamily)Marshal.ReadInt16(socketAddress.lpSockAddr);
2225                         if (addressFamily == AddressFamily.InterNetwork)
2226                         {
2227                             // if the sockaddr length is not the sizeof(sockaddr_in), the data is invalid so
2228                             // return an null endpoint
2229                             if (socketAddress.iSockaddrLength == Marshal.SizeOf(typeof(sockaddr_in)))
2230                             {
2231                                 sockaddr_in sa = (sockaddr_in)Marshal.PtrToStructure(socketAddress.lpSockAddr,
2232                                     typeof(sockaddr_in));
2233                                 endPoint = new IPEndPoint(new IPAddress(sa.sin_addr), sa.sin_port);
2234                             }
2235                         }
2236                         else if (addressFamily == AddressFamily.InterNetworkV6)
2237                         {
2238                             // if the sockaddr length is not the sizeof(sockaddr_in6), the data is invalid so
2239                             // return an null endpoint
2240                             if (socketAddress.iSockaddrLength == Marshal.SizeOf(typeof(sockaddr_in6)))
2241                             {
2242                                 sockaddr_in6 sa = (sockaddr_in6)Marshal.PtrToStructure(socketAddress.lpSockAddr,
2243                                     typeof(sockaddr_in6));
2244                                 if (scopeId != 0 && sa.sin6_scope_id != 0)
2245                                     scopeId = sa.sin6_scope_id;
2246                                 endPoint = new IPEndPoint(new IPAddress(sa.sin6_addr, scopeId), sa.sin6_port);
2247                             }
2248                         }
2249                         // else this is an unknown address family, so return null
2250                     }
2251 
2252                     return endPoint;
2253                 }
2254 
2255             }
2256         }
Equals(object other)2257         public override bool Equals(object other)
2258         {
2259             return ((other as PnrpPeerResolver) != null);
2260         }
2261 
GetHashCode()2262         public override int GetHashCode()
2263         {
2264             return base.GetHashCode();
2265         }
2266 
2267     }
2268 }
2269