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