Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Net / net / PeerToPeer / PeerNameResolver.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="PeerNameResolver.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6 namespace System.Net.PeerToPeer
7 {
8     using System;
9     using System.Collections.Generic;
10     using System.Text;
11     using System.ComponentModel;
12     using System.Threading;
13     using System.Security.Permissions;
14     using System.Runtime.InteropServices;
15     using System.Net;
16     using System.Net.Sockets;
17     using System.Diagnostics;
18     
19     /// <summary>
20     /// This is the event args class we give back each time when 
21     /// we have incremental resolution results
22     /// </summary>
23     public class ResolveProgressChangedEventArgs : ProgressChangedEventArgs
24     {
25         private PeerNameRecord m_PeerNameRecord;
26
27         /// <summary>
28         /// We use progress percentage of **0** all times sice
29         /// we will not no upfront how many records we are going to get
30         /// </summary>
31         /// <param name="peerNameRecord"></param>
32         /// <param name="userToken"></param>
33         public ResolveProgressChangedEventArgs(PeerNameRecord peerNameRecord,
34                                                 object userToken) : base(0, userToken) 
35         {
36             m_PeerNameRecord = peerNameRecord;
37         }
38         public PeerNameRecord PeerNameRecord
39         {
40             get
41             {
42                 return m_PeerNameRecord;
43             }
44         }
45     }
46
47     /// <summary>
48     /// When the resolution completes, we invoke the callback with this event args instance
49     /// </summary>
50     public class ResolveCompletedEventArgs : AsyncCompletedEventArgs
51     {
52         private PeerNameRecordCollection m_PeerNameRecordCollection;
53         public ResolveCompletedEventArgs(
54                                                 PeerNameRecordCollection peerNameRecordCollection,
55                                                 Exception error,
56                                                 bool canceled,
57                                                 object userToken)
58             : base(error, canceled, userToken)
59         {
60             m_PeerNameRecordCollection = peerNameRecordCollection;
61         }
62         public PeerNameRecordCollection PeerNameRecordCollection
63         {
64             get
65             {
66                 return m_PeerNameRecordCollection;
67             }
68         }
69     }
70
71     
72     internal class PeerNameResolverHelper : IDisposable
73     {
74         private const UInt32 FACILITY_P2P = 99;
75         private const UInt32 NO_MORE_RECORDS = 0x4003;
76         private const int PEER_E_NO_MORE = (int)(((int)1 << 31) | ((int)FACILITY_P2P << 16) | NO_MORE_RECORDS); 
77      
78
79         //------------------------------------------
80         //userState the user has supplied
81         //------------------------------------------
82         internal object m_userState;
83
84         //------------------------------------------
85         //Handle to the resolution process
86         //------------------------------------------
87         internal SafePeerNameEndResolve m_SafePeerNameEndResolve;
88
89         //------------------------------------------
90         //Event that the native API sets to indicate that 
91         //information is available and that we should call 
92         //the PeerPnrpGetEndPoint() to get the end point
93         //------------------------------------------
94         internal AutoResetEvent m_EndPointInfoAvailableEvent = new AutoResetEvent(false);
95
96         //------------------------------------------
97         //The WaitHandle that hooks up a callback to the 
98         //event
99         //------------------------------------------
100         internal RegisteredWaitHandle m_RegisteredWaitHandle;
101
102         //------------------------------------------
103         //PeerName that is being resolved
104         //------------------------------------------
105         internal PeerName m_PeerName;
106
107         //------------------------------------------
108         //Cloud in which the resolution must occur
109         //------------------------------------------
110         internal Cloud m_Cloud;
111
112         //------------------------------------------
113         //Max number of records to resolve
114         //------------------------------------------
115         internal int m_MaxRecords;
116
117         //------------------------------------------
118         //Disposed or not
119         //------------------------------------------
120         internal bool m_Disposed;
121
122
123         //-----------------------------------------
124         //Flag to indicate completed or an exception
125         //happened. If you set this flag you own
126         //calling the callback
127         //-----------------------------------------
128         internal bool m_CompletedOrException;
129
130         //-----------------------------------------
131         //Flag to indicate that the call is canceled
132         //If you set this flag you own calling the callback
133         //-----------------------------------------
134         internal bool m_Cancelled;
135
136         //------------------------------------------
137         //A place to save the incremental results 
138         //so that we can invoke the completed 
139         //handler with all the results at once
140         //------------------------------------------
141         PeerNameRecordCollection m_PeerNameRecordCollection = new PeerNameRecordCollection();
142
143         //------------------------------------------
144         //Async operation to ensure synchornization
145         //context
146         //------------------------------------------
147         AsyncOperation m_AsyncOp;
148
149         //------------------------------------------
150         //A link to the resolver to avoid 
151         //circular dependencies and enable GC 
152         //------------------------------------------
153         WeakReference m_PeerNameResolverWeakReference;
154
155         //------------------------------------------
156         //Lock to make sure things don't mess up stuff
157         //------------------------------------------
158         object m_Lock = new Object();
159
160         //------------------------------------------
161         //EventID or Just a trackig id
162         //------------------------------------------
163         int m_TraceEventId;
164
165         internal PeerNameResolverHelper(PeerName peerName, Cloud cloud, int MaxRecords, object userState, PeerNameResolver parent, int NewTraceEventId)
166         {
167             m_userState = userState;
168             m_PeerName = peerName;
169             m_Cloud = cloud;
170             m_MaxRecords = MaxRecords;
171             m_PeerNameResolverWeakReference = new WeakReference(parent);
172             m_TraceEventId = NewTraceEventId;
173             Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "New PeerNameResolverHelper created with TraceEventID {0}", m_TraceEventId);
174             Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
175                 "\tPeerName: {0}, Cloud: {1}, MaxRecords: {2}, userState {3}, ParentReference {4}",
176                 m_PeerName, 
177                 m_Cloud, 
178                 m_MaxRecords, 
179                 userState.GetHashCode(), 
180                 m_PeerNameResolverWeakReference.Target.GetHashCode()
181                 );
182         }
183
184         // <SecurityKernel Critical="True" Ring="0">
185         // <SatisfiesLinkDemand Name="WaitHandle.get_SafeWaitHandle():Microsoft.Win32.SafeHandles.SafeWaitHandle" />
186         // <SatisfiesLinkDemand Name="SafeHandle.get_IsInvalid():System.Boolean" />
187         // <SatisfiesLinkDemand Name="SafeHandle.get_IsClosed():System.Boolean" />
188         // <SatisfiesLinkDemand Name="SafeHandle.Dispose():System.Void" />
189         // <SatisfiesLinkDemand Name="SafeHandle.DangerousGetHandle():System.IntPtr" />
190         // <CallsSuppressUnmanagedCode Name="UnsafeP2PNativeMethods.PeerPnrpStartResolve(System.String,System.String,System.UInt32,Microsoft.Win32.SafeHandles.SafeWaitHandle,System.Net.PeerToPeer.SafePeerNameEndResolve&):System.Int32" />
191         // <ReferencesCritical Name="Method: EndPointInfoAvailableCallback(Object, Boolean):Void" Ring="1" />
192         // <ReferencesCritical Name="Field: m_SafePeerNameEndResolve" Ring="1" />
193         // <ReferencesCritical Name="Method: PeerToPeerException.CreateFromHr(System.String,System.Int32):System.Net.PeerToPeer.PeerToPeerException" Ring="1" />
194         // </SecurityKernel>
195         [System.Security.SecurityCritical]
196         internal void StartAsyncResolve()
197         {
198             //------------------------------------------
199             //Check for disposal
200             //------------------------------------------
201             if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName);
202
203             //------------------------------------------
204             //First wire up a callback
205             //------------------------------------------
206             m_RegisteredWaitHandle = ThreadPool.RegisterWaitForSingleObject(m_EndPointInfoAvailableEvent, //Event that triggers the callback
207                                                     new WaitOrTimerCallback(EndPointInfoAvailableCallback), //callback to be called 
208                                                     null, //state to be passed
209                                                     -1,   //Timeout - aplicable only for timers not for events 
210                                                     false //call us everytime the event is set not just one time
211                                                     );
212
213             //------------------------------------------
214             //Now call the native API to start the resolution 
215             //process save the handle for later
216             //------------------------------------------
217             Int32 result = UnsafeP2PNativeMethods.PeerPnrpStartResolve(m_PeerName.ToString(),
218                                                         m_Cloud.InternalName,
219                                                         (UInt32)m_MaxRecords,
220                                                         m_EndPointInfoAvailableEvent.SafeWaitHandle, 
221                                                         out m_SafePeerNameEndResolve);
222             if (result != 0)
223             {
224                 if (!m_SafePeerNameEndResolve.IsInvalid && !m_SafePeerNameEndResolve.IsClosed)
225                 {
226                     m_SafePeerNameEndResolve.Dispose();
227                 }
228                 m_RegisteredWaitHandle.Unregister(null);
229                 m_RegisteredWaitHandle = null;
230                 PeerToPeerException ex = PeerToPeerException.CreateFromHr(SR.GetString(SR.Pnrp_CouldNotStartNameResolution), result);
231                 Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, m_TraceEventId,
232                             "Exception occurred while starting async resolve");
233                 throw ex;
234             }
235
236             //------------------------------------------
237             //Create an async operation with the given 
238             //user state
239             //------------------------------------------
240             m_AsyncOp = AsyncOperationManager.CreateOperation(m_userState);
241
242             Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
243                         "Successfully started the async resolve. The native handle is {0}", m_SafePeerNameEndResolve.DangerousGetHandle());
244
245         }
246
247         // <SecurityKernel Critical="True" Ring="0">
248         // <UsesUnsafeCode Name="Local pEndPointInfo of type: PEER_PNRP_ENDPOINT_INFO*" />
249         // <UsesUnsafeCode Name="Method: IntPtr.op_Explicit(System.IntPtr):System.Void*" />
250         // <SatisfiesLinkDemand Name="SafeHandle.DangerousGetHandle():System.IntPtr" />
251         // <SatisfiesLinkDemand Name="SafeHandle.Dispose():System.Void" />
252         // <SatisfiesLinkDemand Name="Marshal.PtrToStringUni(System.IntPtr):System.String" />
253         // <SatisfiesLinkDemand Name="Marshal.Copy(System.IntPtr,System.Byte[],System.Int32,System.Int32):System.Void" />
254         // <SatisfiesLinkDemand Name="Marshal.ReadIntPtr(System.IntPtr):System.IntPtr" />
255         // <SatisfiesLinkDemand Name="Marshal.SizeOf(System.Type):System.Int32" />
256         // <CallsSuppressUnmanagedCode Name="UnsafeP2PNativeMethods.PeerPnrpGetEndpoint(System.IntPtr,System.Net.PeerToPeer.SafePeerData&):System.Int32" />
257         // <ReferencesCritical Name="Local shEndPointInfo of type: SafePeerData" Ring="1" />
258         // <ReferencesCritical Name="Field: m_SafePeerNameEndResolve" Ring="1" />
259         // <ReferencesCritical Name="Method: PeerToPeerException.CreateFromHr(System.String,System.Int32):System.Net.PeerToPeer.PeerToPeerException" Ring="1" />
260         // </SecurityKernel>
261         [System.Security.SecurityCritical]
262         public void EndPointInfoAvailableCallback(object state, bool timedOut)
263         {
264             //------------------------------------------
265             //This callback is called whenever there is an endpoint info
266             //available or the resultion is completed
267             //------------------------------------------
268             Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
269                         "EndPointInfoAvailableCallback called");
270             PeerNameRecord record = null;
271             SafePeerData shEndPointInfo;
272             Int32 result = 0;
273             PeerNameResolver parent = null;
274             if (m_Cancelled)
275             {
276                 Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
277                             "Detected that the async operation is already canceled  - before entering the lock");
278                 return;
279             }
280             lock (m_Lock)
281             {
282                 if (m_Cancelled)
283                 {
284                     Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
285                                 "Detected that the async operation is already canceled - after entering the lock");
286                     return;
287                 }
288                 result = UnsafeP2PNativeMethods.PeerPnrpGetEndpoint(m_SafePeerNameEndResolve.DangerousGetHandle(), out shEndPointInfo);
289                 if (result != 0)
290                 {
291                     if (result == PEER_E_NO_MORE)
292                     {
293                         Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
294                                     "Native API returned that there are no more records - resolve completed successfully");
295                     }
296                     m_CompletedOrException = true;
297                     m_SafePeerNameEndResolve.Dispose();
298                 }
299                 else
300                 {
301                     Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
302                                 "Proceeding to retrieve the endpoint information from incremental resolve");
303                     try
304                     {
305                         unsafe
306                         {
307                             PEER_PNRP_ENDPOINT_INFO* pEndPointInfo = (PEER_PNRP_ENDPOINT_INFO*)shEndPointInfo.DangerousGetHandle();
308                             record = new PeerNameRecord();
309                             record.PeerName = new PeerName(Marshal.PtrToStringUni(pEndPointInfo->pwszPeerName));
310                             string comment = Marshal.PtrToStringUni(pEndPointInfo->pwszComment);
311                             if (comment != null && comment.Length > 0)
312                             {
313                                 record.Comment = comment;
314                             }
315                             if (pEndPointInfo->payLoad.cbPayload != 0)
316                             {
317                                 record.Data = new byte[pEndPointInfo->payLoad.cbPayload];
318                                 Marshal.Copy(pEndPointInfo->payLoad.pbPayload, record.Data, 0, (int)pEndPointInfo->payLoad.cbPayload);
319                             }
320                             //record.EndPointList = new IPEndPoint[pEndPointInfo->cAddresses];
321                             IntPtr ppSOCKADDRs = pEndPointInfo->ArrayOfSOCKADDRIN6Pointers;
322                             for (UInt32 j = 0; j < pEndPointInfo->cAddresses; j++)
323                             {
324                                 IntPtr pSOCKADDR = Marshal.ReadIntPtr(ppSOCKADDRs);
325
326                                 byte[] AddressFamilyBuffer = new byte[2];
327                                 Marshal.Copy(pSOCKADDR, AddressFamilyBuffer, 0, 2);
328                                 int addressFamily = 0;
329 #if BIGENDIAN
330                             addressFamily = AddressFamilyBuffer[1] + ((int)AddressFamilyBuffer[0] << 8);
331 #else
332                                 addressFamily = AddressFamilyBuffer[0] + ((int)AddressFamilyBuffer[1] << 8);
333 #endif
334                                 byte[] buffer = new byte[((AddressFamily)addressFamily == AddressFamily.InterNetwork) ? SystemNetHelpers.IPv4AddressSize : SystemNetHelpers.IPv6AddressSize];
335                                 Marshal.Copy(pSOCKADDR, buffer, 0, buffer.Length);
336                                 IPEndPoint ipe = SystemNetHelpers.IPEndPointFromSOCKADDRBuffer(buffer);
337                                 record.EndPointCollection.Add(ipe);
338                                 ppSOCKADDRs = (IntPtr)((long)ppSOCKADDRs + Marshal.SizeOf(typeof(IntPtr)));
339                             }
340                         }
341                     }
342                     finally
343                     {
344                         shEndPointInfo.Dispose();
345                     }
346                     record.TracePeerNameRecord();
347                     m_PeerNameRecordCollection.Add(record);
348
349                     ResolveProgressChangedEventArgs resolveProgressChangedEventArgs = new ResolveProgressChangedEventArgs(
350                                                                             record, m_AsyncOp.UserSuppliedState);
351
352
353                     Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
354                                 "Proceeding to call progress changed event callback");
355                     parent = m_PeerNameResolverWeakReference.Target as PeerNameResolver;
356                     if (parent != null)
357                     {
358                         parent.PrepareToRaiseProgressChangedEvent(m_AsyncOp, resolveProgressChangedEventArgs);
359                     }
360                     return;
361                 }
362             }
363
364             ResolveCompletedEventArgs resolveCompletedEventArgs;
365             if (result == PEER_E_NO_MORE)
366             {
367                 resolveCompletedEventArgs = new ResolveCompletedEventArgs(m_PeerNameRecordCollection,
368                                                        null, false, m_AsyncOp.UserSuppliedState);
369             }
370             else
371             {
372                 PeerToPeerException ex = PeerToPeerException.CreateFromHr(SR.GetString(SR.Pnrp_ExceptionWhileResolvingAPeerName), result);
373                 Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
374                             "Exception occurred when the native API is called to harvest an incremental resolve notification");
375                 resolveCompletedEventArgs = new ResolveCompletedEventArgs(null,
376                                                        ex, false, m_AsyncOp.UserSuppliedState);
377
378             }
379             parent = m_PeerNameResolverWeakReference.Target as PeerNameResolver;
380             if (parent != null)
381             {
382                 Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
383                             "Proceeding to call the ResolveCompleted callback");
384                 parent.PrepareToRaiseCompletedEvent(m_AsyncOp, resolveCompletedEventArgs);
385             }
386             return;
387         }
388
389         // <SecurityKernel Critical="True" Ring="0">
390         // <SatisfiesLinkDemand Name="SafeHandle.Dispose():System.Void" />
391         // <ReferencesCritical Name="Field: m_SafePeerNameEndResolve" Ring="1" />
392         // </SecurityKernel>
393         [System.Security.SecurityCritical]
394         public void ContineCancelCallback(object state)
395         {
396             Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
397                         "ContineCancelCallback called");
398             try
399             {
400                 if (m_CompletedOrException)
401                 {
402                     Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
403                                         "ContinueCancelCallback detected (before acquiring lock) that another thread has already called completed event - so returning without calling cancel");
404                     return;
405                 }
406                 lock (m_Lock)
407                 {
408                     if (m_Cancelled)
409                     {
410                         Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
411                                             "ContinueCancelCallback detected (after acquiring lock) that cancel has already been called");
412                         return;
413
414                     }
415                     if (m_CompletedOrException)
416                     {
417                         Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
418                                             "ContinueCancelCallback detected (after acquiring lock) that another thread has already called completed event - so returning without calling cancel");
419                         return;
420                     }
421                     else
422                     {
423                         Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, 
424                                             "ContinueCancelCallback is proceeding to close the handle and call the Completed callback with Cancelled = true");
425                     }
426                     m_Cancelled = true;
427                     m_SafePeerNameEndResolve.Dispose();
428                 }
429                 PeerNameResolver parent = m_PeerNameResolverWeakReference.Target as PeerNameResolver;
430                 if (parent != null)
431                 {
432                     ResolveCompletedEventArgs e = new ResolveCompletedEventArgs(null, null, true, m_AsyncOp.UserSuppliedState);
433                     parent.PrepareToRaiseCompletedEvent(m_AsyncOp, e);
434                 }
435             }
436             catch
437             {
438                 Logging.P2PTraceSource.TraceEvent(TraceEventType.Critical, m_TraceEventId, "Exception while cancelling the call ");
439                 throw;
440             }
441         }
442         // <SecurityKernel Critical="True" Ring="1">
443         // <ReferencesCritical Name="Method: ContineCancelCallback(Object):Void" Ring="1" />
444         // </SecurityKernel>
445         [System.Security.SecurityCritical]
446         public void CancelAsync()
447         {
448             //Defer the work to a callback
449             ThreadPool.QueueUserWorkItem(new WaitCallback(ContineCancelCallback));
450         }
451         // <SecurityKernel Critical="True" Ring="1">
452         // <ReferencesCritical Name="Method: Dispose(Boolean):Void" Ring="1" />
453         // </SecurityKernel>
454         [System.Security.SecurityCritical]
455         public void Dispose()
456         {
457             Dispose(true);
458             GC.SuppressFinalize(this);
459         }
460         // <SecurityKernel Critical="True" Ring="0">
461         // <SatisfiesLinkDemand Name="SafeHandle.get_IsInvalid():System.Boolean" />
462         // <SatisfiesLinkDemand Name="SafeHandle.Dispose():System.Void" />
463         // <ReferencesCritical Name="Field: m_SafePeerNameEndResolve" Ring="1" />
464         // </SecurityKernel>
465         [System.Security.SecurityCritical]
466         public void Dispose(bool disposing)
467         {
468             if (!m_Disposed)
469             {
470                 if (!m_SafePeerNameEndResolve.IsInvalid)
471                 {
472                     m_SafePeerNameEndResolve.Dispose();
473                 }
474                 if (m_RegisteredWaitHandle != null)
475                     m_RegisteredWaitHandle.Unregister(null);
476                 m_RegisteredWaitHandle = null;
477                 m_EndPointInfoAvailableEvent.Close();
478             }
479             m_Disposed = true;
480         }
481
482         internal int TraceEventId
483         {
484             get
485             {
486                 return m_TraceEventId;
487             }
488         }
489     }
490     
491
492     /// <summary>
493     /// PeerNameResolver does [....] and async resolves. 
494     /// PeerNameResolver supports multiple outstanding async calls
495     /// </summary>
496     public class PeerNameResolver
497     {
498         static PeerNameResolver()
499         {
500             //-------------------------------------------------
501             //Check for the availability of the simpler PNRP APIs
502             //-------------------------------------------------
503             if (!PeerToPeerOSHelper.SupportsP2P)
504             {
505                 throw new PlatformNotSupportedException(SR.GetString(SR.P2P_NotAvailable));
506             }
507         }
508
509         private event EventHandler<ResolveProgressChangedEventArgs> m_ResolveProgressChanged;
510         /// <summary>
511         /// When an event handler is hooked up or removed, we demand the permissions. 
512         /// In partial trust cases, this will avoid the security risk of just hooking up an existing instance 
513         /// of the PeerNameResolver and then receiving all notification of 
514         /// in resolution that is happening 
515         /// </summary>
516         public event EventHandler<ResolveProgressChangedEventArgs> ResolveProgressChanged
517         {
518             add
519             {
520                 PnrpPermission.UnrestrictedPnrpPermission.Demand();
521                 m_ResolveProgressChanged += value;
522             }
523             remove
524             {
525                 PnrpPermission.UnrestrictedPnrpPermission.Demand();
526                 m_ResolveProgressChanged -= value;
527             }
528         }
529
530         private event EventHandler<ResolveCompletedEventArgs> m_ResolveCompleted;
531
532         /// <summary>
533         /// When an event handler is hooked up or removed, we demand the permissions. 
534         /// In partial trust cases, this will avoid the security risk of just hooking up an existing instance 
535         /// of the PeerNameResolver and then receiving all notification of 
536         /// in resolution that is happening 
537         /// </summary>
538         public event EventHandler<ResolveCompletedEventArgs> ResolveCompleted
539         {
540             add
541             {
542                 PnrpPermission.UnrestrictedPnrpPermission.Demand();
543                 m_ResolveCompleted += value;
544             }
545             remove
546             {
547                 PnrpPermission.UnrestrictedPnrpPermission.Demand();
548                 m_ResolveCompleted -= value;
549             }
550         }
551
552         SendOrPostCallback OnResolveProgressChangedDelegate;
553         SendOrPostCallback OnResolveCompletedDelegate;
554
555         /// <summary>
556         /// The following lock and the Sorted Dictionary served
557         /// the purpose of keeping an account of the multiple outstanding async
558         /// resolutions. Each outstanding async operation is 
559         /// keyed based on the userState parameter passed in 
560         /// </summary>
561         private object m_PeerNameResolverHelperListLock = new object();
562         private Dictionary<object, PeerNameResolverHelper> m_PeerNameResolverHelperList = new Dictionary<object, PeerNameResolverHelper>();
563
564         
565         public PeerNameResolver()
566         {
567             OnResolveProgressChangedDelegate = new SendOrPostCallback(ResolveProgressChangedWaitCallback);
568             OnResolveCompletedDelegate = new SendOrPostCallback(ResolveCompletedWaitCallback);
569         }
570         public PeerNameRecordCollection Resolve(PeerName peerName)
571         {
572             return Resolve(peerName, Cloud.Available, int.MaxValue);
573         }
574         public PeerNameRecordCollection Resolve(PeerName peerName, Cloud cloud)
575         {
576             return Resolve(peerName, cloud, int.MaxValue);
577         }
578         public PeerNameRecordCollection Resolve(PeerName peerName, int maxRecords)
579         {
580             return Resolve(peerName, Cloud.Available, maxRecords);
581         }
582
583         /// <summary>
584         /// Implements [....] resolve of the PeerName in the cloud given
585         /// </summary>
586         /// <param name="peerName"></param>
587         /// <param name="cloud"></param>
588         /// <param name="MaxRecords"></param>
589         /// <returns></returns>
590         // <SecurityKernel Critical="True" Ring="0">
591         // <UsesUnsafeCode Name="Local pEndPoints of type: PEER_PNRP_ENDPOINT_INFO*" />
592         // <UsesUnsafeCode Name="Local pEndPointInfo of type: PEER_PNRP_ENDPOINT_INFO*" />
593         // <UsesUnsafeCode Name="Method: IntPtr.op_Explicit(System.IntPtr):System.Void*" />
594         // <SatisfiesLinkDemand Name="SafeHandle.DangerousGetHandle():System.IntPtr" />
595         // <SatisfiesLinkDemand Name="Marshal.PtrToStringUni(System.IntPtr):System.String" />
596         // <SatisfiesLinkDemand Name="Marshal.Copy(System.IntPtr,System.Byte[],System.Int32,System.Int32):System.Void" />
597         // <SatisfiesLinkDemand Name="Marshal.ReadIntPtr(System.IntPtr):System.IntPtr" />
598         // <SatisfiesLinkDemand Name="Marshal.SizeOf(System.Type):System.Int32" />
599         // <SatisfiesLinkDemand Name="SafeHandle.Dispose():System.Void" />
600         // <CallsSuppressUnmanagedCode Name="UnsafeP2PNativeMethods.PeerPnrpResolve(System.String,System.String,System.UInt32&,System.Net.PeerToPeer.SafePeerData&):System.Int32" />
601         // <ReferencesCritical Name="Local shEndPointInfoArray of type: SafePeerData" Ring="1" />
602         // <ReferencesCritical Name="Method: UnsafeP2PNativeMethods.PnrpStartup():System.Void" Ring="1" />
603         // <ReferencesCritical Name="Method: PeerToPeerException.CreateFromHr(System.String,System.Int32):System.Net.PeerToPeer.PeerToPeerException" Ring="1" />
604         // </SecurityKernel>
605         [System.Security.SecurityCritical]
606         public PeerNameRecordCollection Resolve(PeerName peerName, Cloud cloud, int maxRecords)
607         {
608             
609             //---------------------------------------------------
610             //Check arguments
611             //---------------------------------------------------
612             if (peerName == null)
613             {
614                 throw new ArgumentNullException(SR.GetString(SR.Pnrp_PeerNameCantBeNull), "peerName");
615             }
616
617             if (maxRecords <= 0)
618             {
619                 throw new ArgumentOutOfRangeException("maxRecords", SR.GetString(SR.Pnrp_MaxRecordsParameterMustBeGreaterThanZero));
620             }
621
622             //---------------------------------------------------
623             //Assume all clouds if the clould passed is null?
624             //---------------------------------------------------
625             if (cloud == null)
626             {
627                 cloud = Cloud.Available;
628             }
629             
630             //---------------------------------------------------
631             //Demand CAS permissions
632             //---------------------------------------------------
633             PnrpPermission.UnrestrictedPnrpPermission.Demand();
634
635             //---------------------------------------------------------------
636             //No perf hit here, real native call happens only one time if it 
637             //did not already happen
638             //---------------------------------------------------------------
639             UnsafeP2PNativeMethods.PnrpStartup();
640
641             //---------------------------------------------------------------
642             //Trace log
643             //---------------------------------------------------------------
644             Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "[....] Resolve called with PeerName: {0}, Cloud: {1}, MaxRecords {2}", peerName, cloud, maxRecords);
645
646             SafePeerData shEndPointInfoArray;
647             string NativeCloudName = cloud.InternalName;
648             UInt32 ActualCountOfEndPoints = (UInt32)maxRecords;
649             int result = UnsafeP2PNativeMethods.PeerPnrpResolve(peerName.ToString(), 
650                                                                 NativeCloudName, 
651                                                                 ref ActualCountOfEndPoints, 
652                                                                 out shEndPointInfoArray);
653             if (result != 0)
654             {
655                 throw PeerToPeerException.CreateFromHr(SR.GetString(SR.Pnrp_CouldNotStartNameResolution), result);
656             }
657
658             //---------------------------------------------------
659             //If there are no endpoints returned, return 
660             //an empty PeerNameRecord Collection
661             //---------------------------------------------------
662             PeerNameRecordCollection PeerNameRecords = new PeerNameRecordCollection();
663             if (ActualCountOfEndPoints != 0)
664             {
665                 try
666                 {
667                     unsafe
668                     {
669                         IntPtr pEndPointInfoArray = shEndPointInfoArray.DangerousGetHandle();
670                         PEER_PNRP_ENDPOINT_INFO* pEndPoints = (PEER_PNRP_ENDPOINT_INFO*)pEndPointInfoArray;
671                         for (int i = 0; i < ActualCountOfEndPoints; i++)
672                         {
673                             PeerNameRecord record = new PeerNameRecord();
674                             PEER_PNRP_ENDPOINT_INFO* pEndPointInfo = &pEndPoints[i];
675                             record.PeerName = new PeerName(Marshal.PtrToStringUni(pEndPointInfo->pwszPeerName));
676                             string comment = Marshal.PtrToStringUni(pEndPointInfo->pwszComment);
677                             if (comment != null && comment.Length > 0)
678                             {
679                                 record.Comment = comment;
680                             }
681     
682                             if (pEndPointInfo->payLoad.cbPayload != 0)
683                             {
684                                 record.Data = new byte[pEndPointInfo->payLoad.cbPayload];
685                                 Marshal.Copy(pEndPointInfo->payLoad.pbPayload, record.Data, 0, (int)pEndPointInfo->payLoad.cbPayload);
686     
687                             }
688                             //record.EndPointList = new IPEndPoint[pEndPointInfo->cAddresses];
689                             IntPtr ppSOCKADDRs = pEndPointInfo->ArrayOfSOCKADDRIN6Pointers;
690                             for (UInt32 j = 0; j < pEndPointInfo->cAddresses; j++)
691                             {
692                                 IntPtr pSOCKADDR = Marshal.ReadIntPtr(ppSOCKADDRs);
693     
694                                 byte[] AddressFamilyBuffer = new byte[2];
695                                 Marshal.Copy(pSOCKADDR, AddressFamilyBuffer, 0, 2);
696                                 int addressFamily = 0;
697     #if BIGENDIAN
698                                 addressFamily = AddressFamilyBuffer[1] + ((int)AddressFamilyBuffer[0] << 8);
699     #else
700                                 addressFamily = AddressFamilyBuffer[0] + ((int)AddressFamilyBuffer[1] << 8);
701     #endif
702                                 byte[] buffer = new byte[((AddressFamily)addressFamily == AddressFamily.InterNetwork) ? SystemNetHelpers.IPv4AddressSize : SystemNetHelpers.IPv6AddressSize];
703                                 Marshal.Copy(pSOCKADDR, buffer, 0, buffer.Length);
704                                 IPEndPoint ipe = SystemNetHelpers.IPEndPointFromSOCKADDRBuffer(buffer);
705                                 record.EndPointCollection.Add(ipe);
706                                 ppSOCKADDRs = (IntPtr)((long)ppSOCKADDRs + Marshal.SizeOf(typeof(IntPtr)));
707                             }
708                             //----------------------------------
709                             //Dump for trace
710                             //----------------------------------
711                             record.TracePeerNameRecord();
712                             //----------------------------------                
713                             //Add to collection
714                             //----------------------------------
715                             PeerNameRecords.Add(record);
716                         }
717                     }
718                 }
719                 finally
720                 {
721                     shEndPointInfoArray.Dispose();
722                 }
723             }
724             Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "[....] Resolve returnig with PeerNameRecord count :{0}", PeerNameRecords.Count);
725             return PeerNameRecords;
726         }
727         
728         [HostProtection(ExternalThreading = true)]
729         public void ResolveAsync(PeerName peerName, object userState)
730         {
731             ResolveAsync(peerName, Cloud.Available, Int32.MaxValue, userState);
732         }
733         [HostProtection(ExternalThreading = true)]
734         public void ResolveAsync(PeerName peerName, Cloud cloud, object userState)
735         {
736             ResolveAsync(peerName, cloud, Int32.MaxValue, userState);
737         }
738         [HostProtection(ExternalThreading = true)]
739         public void ResolveAsync(PeerName peerName, int maxRecords, object userState)
740         {
741             ResolveAsync(peerName, Cloud.Available, maxRecords, userState);
742         }
743         
744         // <SecurityKernel Critical="True" Ring="1">
745         // <ReferencesCritical Name="Method: UnsafeP2PNativeMethods.PnrpStartup():System.Void" Ring="1" />
746         // <ReferencesCritical Name="Method: PeerNameResolverHelper.StartAsyncResolve():System.Void" Ring="1" />
747         // </SecurityKernel>
748         [System.Security.SecurityCritical]
749         [HostProtection(ExternalThreading = true)]
750         public void ResolveAsync(PeerName peerName, Cloud cloud, int maxRecords, object userState)
751         {
752             //-------------------------------------------------
753             //Check arguments
754             //-------------------------------------------------
755             if (peerName == null)
756             {
757                 throw new ArgumentNullException(SR.GetString(SR.Pnrp_PeerNameCantBeNull), "peerName");
758             }
759             if (cloud == null)
760             {
761                 cloud = Cloud.Available;
762             }
763             if (maxRecords <= 0)
764             {
765                 throw new ArgumentOutOfRangeException("maxRecords", SR.GetString(SR.Pnrp_MaxRecordsParameterMustBeGreaterThanZero));
766             }
767
768             if (m_ResolveCompleted == null)
769             {
770                 throw new PeerToPeerException(SR.GetString(SR.Pnrp_AtleastOneEvenHandlerNeeded));
771             }
772             //---------------------------------------------------
773             //Demand CAS permissions
774             //---------------------------------------------------
775             PnrpPermission.UnrestrictedPnrpPermission.Demand();
776
777             //---------------------------------------------------------------
778             //No perf hit here, real native call happens only one time if it 
779             //did not already happen
780             //---------------------------------------------------------------
781             UnsafeP2PNativeMethods.PnrpStartup();
782
783             //----------------------------------------------------
784             //userToken can't be null
785             //----------------------------------------------------
786             if (userState == null)
787             {
788                 throw new ArgumentNullException(SR.GetString(SR.NullUserToken), "userState");
789             }
790
791             PeerNameResolverHelper peerNameResolverHelper = null;
792             //---------------------------------------------------
793             //The userToken can't be duplicate of what is in the 
794             //current list. These are the requriments for the new Async model 
795             //that supports multiple outstanding async calls
796             //---------------------------------------------------
797             int newTraceEventId  = NewTraceEventId;
798             lock (m_PeerNameResolverHelperListLock)
799             {
800                 if (m_PeerNameResolverHelperList.ContainsKey(userState))
801                 {
802                     throw new ArgumentException(SR.GetString(SR.DuplicateUserToken));
803                 }
804                 Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, newTraceEventId, 
805                                                 "PeerNameResolverHelper is being created with TraceEventId {0}", newTraceEventId);
806                 peerNameResolverHelper = new PeerNameResolverHelper(peerName, cloud, maxRecords, userState, this, newTraceEventId);
807                 m_PeerNameResolverHelperList[userState] = peerNameResolverHelper;
808             }
809
810             try
811             {
812                 //---------------------------------------------------
813                 //Start resolution on that resolver
814                 //---------------------------------------------------
815                 peerNameResolverHelper.StartAsyncResolve();
816             }
817             catch
818             {
819                 //---------------------------------------------------
820                 //If an exception happens clear the userState from the 
821                 //list so that that token can be reused
822                 //---------------------------------------------------
823                 lock (m_PeerNameResolverHelperListLock)
824                 {
825                     m_PeerNameResolverHelperList.Remove(userState);
826                     Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, newTraceEventId,
827                         "Removing userState token from pending list {0}", userState.GetHashCode());
828                 }
829                 throw;
830             }
831         }
832         
833         protected void OnResolveProgressChanged(ResolveProgressChangedEventArgs e)
834         {
835             if (m_ResolveProgressChanged != null)
836             {
837                 m_ResolveProgressChanged(this, e);
838             }
839         }
840         void ResolveProgressChangedWaitCallback(object operationState)
841         {
842             OnResolveProgressChanged((ResolveProgressChangedEventArgs)operationState);
843         }
844         internal void PrepareToRaiseProgressChangedEvent(AsyncOperation asyncOP, ResolveProgressChangedEventArgs args)
845         {
846             asyncOP.Post(OnResolveProgressChangedDelegate, args);
847         }
848
849         protected void OnResolveCompleted(ResolveCompletedEventArgs e)
850         {
851             if (m_ResolveCompleted != null)
852             {
853                 m_ResolveCompleted(this, e);
854             }
855         }
856         void ResolveCompletedWaitCallback(object operationState)
857         {
858             OnResolveCompleted((ResolveCompletedEventArgs)operationState);
859         }
860         internal void PrepareToRaiseCompletedEvent(AsyncOperation asyncOP, ResolveCompletedEventArgs args)
861         {
862             asyncOP.PostOperationCompleted(OnResolveCompletedDelegate, args);
863             lock (m_PeerNameResolverHelperListLock)
864             {
865                 PeerNameResolverHelper helper = m_PeerNameResolverHelperList[args.UserState];
866                 if (helper == null)
867                 {
868                     Logging.P2PTraceSource.TraceEvent(TraceEventType.Critical, 0, "userState for which we are about to call Completed event does not exist in the pending async list");
869                 }
870                 else
871                 {
872                     Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, helper.TraceEventId, 
873                         "userState {0} is being removed from the pending async list", args.UserState.GetHashCode());
874                     m_PeerNameResolverHelperList.Remove(args.UserState);
875                 }
876
877             }
878         }
879         
880
881         // <SecurityKernel Critical="True" Ring="2">
882         // <ReferencesCritical Name="Method: PeerNameResolverHelper.CancelAsync():System.Void" Ring="2" />
883         // </SecurityKernel>
884         [System.Security.SecurityCritical]
885         public void ResolveAsyncCancel(object userState)
886         {
887             PnrpPermission.UnrestrictedPnrpPermission.Demand();
888             if (userState == null)
889             {
890                 return;
891             }
892             PeerNameResolverHelper helper;
893             lock (m_PeerNameResolverHelperListLock)
894             {
895                 if (!m_PeerNameResolverHelperList.TryGetValue(userState, out helper))
896                 {
897                     Logging.P2PTraceSource.TraceEvent(TraceEventType.Warning, 0, "ResolveAsyncCancel called with a userState token that is not in the pending async list - returning");
898                     return;
899                 }
900             }
901             Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, helper.TraceEventId, 
902                     "Proceeding to cancel the pending async");
903             helper.CancelAsync();
904         }
905
906
907         private static int s_TraceEventId;
908         private static int NewTraceEventId
909         {
910             get
911             {
912                 Interlocked.CompareExchange(ref s_TraceEventId, 0, int.MaxValue);
913                 Interlocked.Increment(ref s_TraceEventId);
914                 return s_TraceEventId;
915             }
916         }        
917
918     }
919 }