//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//-----------------------------------------------------------------------------
namespace System.Net.PeerToPeer
{
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Threading;
using System.Security.Permissions;
using System.Runtime.InteropServices;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
///
/// This is the event args class we give back each time when
/// we have incremental resolution results
///
public class ResolveProgressChangedEventArgs : ProgressChangedEventArgs
{
private PeerNameRecord m_PeerNameRecord;
///
/// We use progress percentage of **0** all times sice
/// we will not no upfront how many records we are going to get
///
///
///
public ResolveProgressChangedEventArgs(PeerNameRecord peerNameRecord,
object userToken) : base(0, userToken)
{
m_PeerNameRecord = peerNameRecord;
}
public PeerNameRecord PeerNameRecord
{
get
{
return m_PeerNameRecord;
}
}
}
///
/// When the resolution completes, we invoke the callback with this event args instance
///
public class ResolveCompletedEventArgs : AsyncCompletedEventArgs
{
private PeerNameRecordCollection m_PeerNameRecordCollection;
public ResolveCompletedEventArgs(
PeerNameRecordCollection peerNameRecordCollection,
Exception error,
bool canceled,
object userToken)
: base(error, canceled, userToken)
{
m_PeerNameRecordCollection = peerNameRecordCollection;
}
public PeerNameRecordCollection PeerNameRecordCollection
{
get
{
return m_PeerNameRecordCollection;
}
}
}
internal class PeerNameResolverHelper : IDisposable
{
private const UInt32 FACILITY_P2P = 99;
private const UInt32 NO_MORE_RECORDS = 0x4003;
private const int PEER_E_NO_MORE = (int)(((int)1 << 31) | ((int)FACILITY_P2P << 16) | NO_MORE_RECORDS);
//------------------------------------------
//userState the user has supplied
//------------------------------------------
internal object m_userState;
//------------------------------------------
//Handle to the resolution process
//------------------------------------------
internal SafePeerNameEndResolve m_SafePeerNameEndResolve;
//------------------------------------------
//Event that the native API sets to indicate that
//information is available and that we should call
//the PeerPnrpGetEndPoint() to get the end point
//------------------------------------------
internal AutoResetEvent m_EndPointInfoAvailableEvent = new AutoResetEvent(false);
//------------------------------------------
//The WaitHandle that hooks up a callback to the
//event
//------------------------------------------
internal RegisteredWaitHandle m_RegisteredWaitHandle;
//------------------------------------------
//PeerName that is being resolved
//------------------------------------------
internal PeerName m_PeerName;
//------------------------------------------
//Cloud in which the resolution must occur
//------------------------------------------
internal Cloud m_Cloud;
//------------------------------------------
//Max number of records to resolve
//------------------------------------------
internal int m_MaxRecords;
//------------------------------------------
//Disposed or not
//------------------------------------------
internal bool m_Disposed;
//-----------------------------------------
//Flag to indicate completed or an exception
//happened. If you set this flag you own
//calling the callback
//-----------------------------------------
internal bool m_CompletedOrException;
//-----------------------------------------
//Flag to indicate that the call is canceled
//If you set this flag you own calling the callback
//-----------------------------------------
internal bool m_Cancelled;
//------------------------------------------
//A place to save the incremental results
//so that we can invoke the completed
//handler with all the results at once
//------------------------------------------
PeerNameRecordCollection m_PeerNameRecordCollection = new PeerNameRecordCollection();
//------------------------------------------
//Async operation to ensure synchornization
//context
//------------------------------------------
AsyncOperation m_AsyncOp;
//------------------------------------------
//A link to the resolver to avoid
//circular dependencies and enable GC
//------------------------------------------
WeakReference m_PeerNameResolverWeakReference;
//------------------------------------------
//Lock to make sure things don't mess up stuff
//------------------------------------------
object m_Lock = new Object();
//------------------------------------------
//EventID or Just a trackig id
//------------------------------------------
int m_TraceEventId;
internal PeerNameResolverHelper(PeerName peerName, Cloud cloud, int MaxRecords, object userState, PeerNameResolver parent, int NewTraceEventId)
{
m_userState = userState;
m_PeerName = peerName;
m_Cloud = cloud;
m_MaxRecords = MaxRecords;
m_PeerNameResolverWeakReference = new WeakReference(parent);
m_TraceEventId = NewTraceEventId;
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "New PeerNameResolverHelper created with TraceEventID {0}", m_TraceEventId);
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
"\tPeerName: {0}, Cloud: {1}, MaxRecords: {2}, userState {3}, ParentReference {4}",
m_PeerName,
m_Cloud,
m_MaxRecords,
userState.GetHashCode(),
m_PeerNameResolverWeakReference.Target.GetHashCode()
);
}
//
//
//
//
//
//
//
//
//
//
//
[System.Security.SecurityCritical]
internal void StartAsyncResolve()
{
//------------------------------------------
//Check for disposal
//------------------------------------------
if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName);
//------------------------------------------
//First wire up a callback
//------------------------------------------
m_RegisteredWaitHandle = ThreadPool.RegisterWaitForSingleObject(m_EndPointInfoAvailableEvent, //Event that triggers the callback
new WaitOrTimerCallback(EndPointInfoAvailableCallback), //callback to be called
null, //state to be passed
-1, //Timeout - aplicable only for timers not for events
false //call us everytime the event is set not just one time
);
//------------------------------------------
//Now call the native API to start the resolution
//process save the handle for later
//------------------------------------------
Int32 result = UnsafeP2PNativeMethods.PeerPnrpStartResolve(m_PeerName.ToString(),
m_Cloud.InternalName,
(UInt32)m_MaxRecords,
m_EndPointInfoAvailableEvent.SafeWaitHandle,
out m_SafePeerNameEndResolve);
if (result != 0)
{
if (!m_SafePeerNameEndResolve.IsInvalid && !m_SafePeerNameEndResolve.IsClosed)
{
m_SafePeerNameEndResolve.Dispose();
}
m_RegisteredWaitHandle.Unregister(null);
m_RegisteredWaitHandle = null;
PeerToPeerException ex = PeerToPeerException.CreateFromHr(SR.GetString(SR.Pnrp_CouldNotStartNameResolution), result);
Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, m_TraceEventId,
"Exception occurred while starting async resolve");
throw ex;
}
//------------------------------------------
//Create an async operation with the given
//user state
//------------------------------------------
m_AsyncOp = AsyncOperationManager.CreateOperation(m_userState);
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
"Successfully started the async resolve. The native handle is {0}", m_SafePeerNameEndResolve.DangerousGetHandle());
}
//
//
//
//
//
//
//
//
//
//
//
//
//
//
[System.Security.SecurityCritical]
public void EndPointInfoAvailableCallback(object state, bool timedOut)
{
//------------------------------------------
//This callback is called whenever there is an endpoint info
//available or the resultion is completed
//------------------------------------------
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
"EndPointInfoAvailableCallback called");
PeerNameRecord record = null;
SafePeerData shEndPointInfo;
Int32 result = 0;
PeerNameResolver parent = null;
if (m_Cancelled)
{
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
"Detected that the async operation is already canceled - before entering the lock");
return;
}
lock (m_Lock)
{
if (m_Cancelled)
{
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
"Detected that the async operation is already canceled - after entering the lock");
return;
}
result = UnsafeP2PNativeMethods.PeerPnrpGetEndpoint(m_SafePeerNameEndResolve.DangerousGetHandle(), out shEndPointInfo);
if (result != 0)
{
if (result == PEER_E_NO_MORE)
{
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
"Native API returned that there are no more records - resolve completed successfully");
}
m_CompletedOrException = true;
m_SafePeerNameEndResolve.Dispose();
}
else
{
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
"Proceeding to retrieve the endpoint information from incremental resolve");
try
{
unsafe
{
PEER_PNRP_ENDPOINT_INFO* pEndPointInfo = (PEER_PNRP_ENDPOINT_INFO*)shEndPointInfo.DangerousGetHandle();
record = new PeerNameRecord();
record.PeerName = new PeerName(Marshal.PtrToStringUni(pEndPointInfo->pwszPeerName));
string comment = Marshal.PtrToStringUni(pEndPointInfo->pwszComment);
if (comment != null && comment.Length > 0)
{
record.Comment = comment;
}
if (pEndPointInfo->payLoad.cbPayload != 0)
{
record.Data = new byte[pEndPointInfo->payLoad.cbPayload];
Marshal.Copy(pEndPointInfo->payLoad.pbPayload, record.Data, 0, (int)pEndPointInfo->payLoad.cbPayload);
}
//record.EndPointList = new IPEndPoint[pEndPointInfo->cAddresses];
IntPtr ppSOCKADDRs = pEndPointInfo->ArrayOfSOCKADDRIN6Pointers;
for (UInt32 j = 0; j < pEndPointInfo->cAddresses; j++)
{
IntPtr pSOCKADDR = Marshal.ReadIntPtr(ppSOCKADDRs);
byte[] AddressFamilyBuffer = new byte[2];
Marshal.Copy(pSOCKADDR, AddressFamilyBuffer, 0, 2);
int addressFamily = 0;
#if BIGENDIAN
addressFamily = AddressFamilyBuffer[1] + ((int)AddressFamilyBuffer[0] << 8);
#else
addressFamily = AddressFamilyBuffer[0] + ((int)AddressFamilyBuffer[1] << 8);
#endif
byte[] buffer = new byte[((AddressFamily)addressFamily == AddressFamily.InterNetwork) ? SystemNetHelpers.IPv4AddressSize : SystemNetHelpers.IPv6AddressSize];
Marshal.Copy(pSOCKADDR, buffer, 0, buffer.Length);
IPEndPoint ipe = SystemNetHelpers.IPEndPointFromSOCKADDRBuffer(buffer);
record.EndPointCollection.Add(ipe);
ppSOCKADDRs = (IntPtr)((long)ppSOCKADDRs + Marshal.SizeOf(typeof(IntPtr)));
}
}
}
finally
{
shEndPointInfo.Dispose();
}
record.TracePeerNameRecord();
m_PeerNameRecordCollection.Add(record);
ResolveProgressChangedEventArgs resolveProgressChangedEventArgs = new ResolveProgressChangedEventArgs(
record, m_AsyncOp.UserSuppliedState);
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
"Proceeding to call progress changed event callback");
parent = m_PeerNameResolverWeakReference.Target as PeerNameResolver;
if (parent != null)
{
parent.PrepareToRaiseProgressChangedEvent(m_AsyncOp, resolveProgressChangedEventArgs);
}
return;
}
}
ResolveCompletedEventArgs resolveCompletedEventArgs;
if (result == PEER_E_NO_MORE)
{
resolveCompletedEventArgs = new ResolveCompletedEventArgs(m_PeerNameRecordCollection,
null, false, m_AsyncOp.UserSuppliedState);
}
else
{
PeerToPeerException ex = PeerToPeerException.CreateFromHr(SR.GetString(SR.Pnrp_ExceptionWhileResolvingAPeerName), result);
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
"Exception occurred when the native API is called to harvest an incremental resolve notification");
resolveCompletedEventArgs = new ResolveCompletedEventArgs(null,
ex, false, m_AsyncOp.UserSuppliedState);
}
parent = m_PeerNameResolverWeakReference.Target as PeerNameResolver;
if (parent != null)
{
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
"Proceeding to call the ResolveCompleted callback");
parent.PrepareToRaiseCompletedEvent(m_AsyncOp, resolveCompletedEventArgs);
}
return;
}
//
//
//
//
[System.Security.SecurityCritical]
public void ContineCancelCallback(object state)
{
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
"ContineCancelCallback called");
try
{
if (m_CompletedOrException)
{
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
"ContinueCancelCallback detected (before acquiring lock) that another thread has already called completed event - so returning without calling cancel");
return;
}
lock (m_Lock)
{
if (m_Cancelled)
{
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
"ContinueCancelCallback detected (after acquiring lock) that cancel has already been called");
return;
}
if (m_CompletedOrException)
{
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
"ContinueCancelCallback detected (after acquiring lock) that another thread has already called completed event - so returning without calling cancel");
return;
}
else
{
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
"ContinueCancelCallback is proceeding to close the handle and call the Completed callback with Cancelled = true");
}
m_Cancelled = true;
m_SafePeerNameEndResolve.Dispose();
}
PeerNameResolver parent = m_PeerNameResolverWeakReference.Target as PeerNameResolver;
if (parent != null)
{
ResolveCompletedEventArgs e = new ResolveCompletedEventArgs(null, null, true, m_AsyncOp.UserSuppliedState);
parent.PrepareToRaiseCompletedEvent(m_AsyncOp, e);
}
}
catch
{
Logging.P2PTraceSource.TraceEvent(TraceEventType.Critical, m_TraceEventId, "Exception while cancelling the call ");
throw;
}
}
//
//
//
[System.Security.SecurityCritical]
public void CancelAsync()
{
//Defer the work to a callback
ThreadPool.QueueUserWorkItem(new WaitCallback(ContineCancelCallback));
}
//
//
//
[System.Security.SecurityCritical]
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
//
//
//
//
//
[System.Security.SecurityCritical]
public void Dispose(bool disposing)
{
if (!m_Disposed)
{
if (!m_SafePeerNameEndResolve.IsInvalid)
{
m_SafePeerNameEndResolve.Dispose();
}
if (m_RegisteredWaitHandle != null)
m_RegisteredWaitHandle.Unregister(null);
m_RegisteredWaitHandle = null;
m_EndPointInfoAvailableEvent.Close();
}
m_Disposed = true;
}
internal int TraceEventId
{
get
{
return m_TraceEventId;
}
}
}
///
/// PeerNameResolver does [....] and async resolves.
/// PeerNameResolver supports multiple outstanding async calls
///
public class PeerNameResolver
{
static PeerNameResolver()
{
//-------------------------------------------------
//Check for the availability of the simpler PNRP APIs
//-------------------------------------------------
if (!PeerToPeerOSHelper.SupportsP2P)
{
throw new PlatformNotSupportedException(SR.GetString(SR.P2P_NotAvailable));
}
}
private event EventHandler m_ResolveProgressChanged;
///
/// When an event handler is hooked up or removed, we demand the permissions.
/// In partial trust cases, this will avoid the security risk of just hooking up an existing instance
/// of the PeerNameResolver and then receiving all notification of
/// in resolution that is happening
///
public event EventHandler ResolveProgressChanged
{
add
{
PnrpPermission.UnrestrictedPnrpPermission.Demand();
m_ResolveProgressChanged += value;
}
remove
{
PnrpPermission.UnrestrictedPnrpPermission.Demand();
m_ResolveProgressChanged -= value;
}
}
private event EventHandler m_ResolveCompleted;
///
/// When an event handler is hooked up or removed, we demand the permissions.
/// In partial trust cases, this will avoid the security risk of just hooking up an existing instance
/// of the PeerNameResolver and then receiving all notification of
/// in resolution that is happening
///
public event EventHandler ResolveCompleted
{
add
{
PnrpPermission.UnrestrictedPnrpPermission.Demand();
m_ResolveCompleted += value;
}
remove
{
PnrpPermission.UnrestrictedPnrpPermission.Demand();
m_ResolveCompleted -= value;
}
}
SendOrPostCallback OnResolveProgressChangedDelegate;
SendOrPostCallback OnResolveCompletedDelegate;
///
/// The following lock and the Sorted Dictionary served
/// the purpose of keeping an account of the multiple outstanding async
/// resolutions. Each outstanding async operation is
/// keyed based on the userState parameter passed in
///
private object m_PeerNameResolverHelperListLock = new object();
private Dictionary