//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ namespace System.Net.PeerToPeer { using System; using System.Collections.Generic; using System.Text; using System.Security.Permissions; using System.Runtime.InteropServices; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.Serialization; /// /// The PeerNameRegistration class registers a peer name record /// [Serializable] public class PeerNameRegistration : IDisposable, ISerializable { //----------------------------------------------------------------------- //Native constant to indicate auto address selection //----------------------------------------------------------------------- private const UInt32 PEER_PNRP_AUTO_ADDRESSES = unchecked((UInt32)(-1)); //----------------------------------------------------------------------- //Internal PeerNameRecord to hold information. //----------------------------------------------------------------------- private PeerNameRecord m_PeerNameRecord = new PeerNameRecord(); private int m_Port; private Cloud m_Cloud; //----------------------------------------------------------------------- //Flag to keep whether We registered or not //----------------------------------------------------------------------- private bool m_IsRegistered; //----------------------------------------------------------------------- //The native handle to the registration //----------------------------------------------------------------------- private SafePeerNameUnregister m_RegistrationHandle; //----------------------------------------------------------------------- //PeerName that is associated with this registation //If they update the PeerName in the PeerNameRecord and call update we //should throw //----------------------------------------------------------------------- private PeerName m_RegisteredPeerName; //----------------------------------------------------------------------- //We should support the scenario where you publish just the data //but no end points. This flag tells us whether to use auto endpoint selection //or not //----------------------------------------------------------------------- private bool m_UseAutoEndPointSelection = true; static PeerNameRegistration() { //------------------------------------------------- //Check for the availability of the simpler PNRP APIs //------------------------------------------------- if (!PeerToPeerOSHelper.SupportsP2P) { throw new PlatformNotSupportedException(SR.GetString(SR.P2P_NotAvailable)); } } /// /// Empty constructor so that users can populate the /// information later /// public PeerNameRegistration() { } /// /// This constuctor popuates the PeerNameRecord and registers automatically /// Registers in all clouds with automatic address selection /// /// PeerName to register /// Port to register on public PeerNameRegistration(PeerName name, int port) : this(name, port, null) { } /// /// This constuctor popuates the PeerNameRecord and registers automatically /// Registers with automatic address selection within the cloud /// /// PeerName to register /// Port to register on /// A specific cloud to regster in/// public PeerNameRegistration(PeerName name, int port, Cloud cloud) { if (name == null) { throw new ArgumentNullException("name"); } if (cloud == null) { cloud = Cloud.Available; } if (port < IPEndPoint.MinPort || port > IPEndPoint.MaxPort) { throw new ArgumentOutOfRangeException("port", SR.GetString(SR.Pnrp_PortOutOfRange)); } m_PeerNameRecord.PeerName = name; m_Port = port; m_Cloud = cloud; Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Created a PeerNameRegistration with PeerName {0}, Port {1}, Cloud {2} - Proceeding to register", name, port, cloud); } /// /// Property accessor to examine/change and perhaps call /// Update() to update the registration information /// internal PeerNameRecord PeerNameRecord { get { if (m_Disposed) { throw new ObjectDisposedException(this.GetType().FullName); } return m_PeerNameRecord; } } public int Port { get { return m_Port; } set { if (value < IPEndPoint.MinPort || value > IPEndPoint.MaxPort) { throw new ArgumentOutOfRangeException("port", SR.GetString(SR.Pnrp_PortOutOfRange)); } m_Port = value; } } public PeerName PeerName { get { return m_PeerNameRecord.PeerName; } set { m_PeerNameRecord.PeerName = value; } } public IPEndPointCollection EndPointCollection { get { return m_PeerNameRecord.EndPointCollection; } } public Cloud Cloud { get { return m_Cloud; } set { m_Cloud = value; } } public string Comment { get { return m_PeerNameRecord.Comment; } set { m_PeerNameRecord.Comment = value; } } public byte[] Data { get { return m_PeerNameRecord.Data; } set { m_PeerNameRecord.Data = value; } } public bool UseAutoEndPointSelection { get { return m_UseAutoEndPointSelection; } set { m_UseAutoEndPointSelection = value; } } /// /// Return the flag to indicate whether we /// registered or not /// /// public bool IsRegistered() { if (m_Disposed) { throw new ObjectDisposedException(this.GetType().FullName); } //------------------------------------------------- //Demand for the Unrestricted Pnrp Permission //------------------------------------------------- PnrpPermission.UnrestrictedPnrpPermission.Demand(); return m_IsRegistered; } /// /// This method is called if empty constructor is used and the /// information in the PeerNameRecord is set. Register needs to /// be called since we did not automatically register through the /// constructor /// // // // [System.Security.SecurityCritical] public void Start() { if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName); Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Proceeding to register through the Register method()"); InternalRegister(); } /// /// This is where the real registration happens /// // // // // // // // // // // // // // // // [System.Security.SecurityCritical] private void InternalRegister() { //------------------------------------------ //If we already registered, get out this place //------------------------------------------ if (m_IsRegistered) { throw new PeerToPeerException(SR.GetString(SR.Pnrp_UseUpdateInsteadOfRegister)); } //------------------------------------------ //Make sure you have the required info //------------------------------------------ if (m_PeerNameRecord.PeerName == null) { throw new ArgumentNullException("PeerName"); } //------------------------------------------ //If auto address selection is turned off //then there must be atleast Data or Endpoints //specified //------------------------------------------ if (!m_UseAutoEndPointSelection) { if ((EndPointCollection.Count == 0) && (Data == null || Data.Length <= 0)) { throw new PeerToPeerException(SR.GetString(SR.Pnrp_BlobOrEndpointListNeeded)); } } //------------------------------------------------- //Demand for the Unrestricted Pnrp Permission //------------------------------------------------- PnrpPermission.UnrestrictedPnrpPermission.Demand(); //--------------------------------------------------------------- //No perf hit here, real native call happens only one time if it //did not already happen //--------------------------------------------------------------- UnsafeP2PNativeMethods.PnrpStartup(); //--------------------------------------------------------------- //Log trace info //--------------------------------------------------------------- if (Logging.P2PTraceSource.Switch.ShouldTrace(TraceEventType.Information)) { Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "InternalRegister() is called with the following Info"); m_PeerNameRecord.TracePeerNameRecord(); } PEER_PNRP_REGISTRATION_INFO regInfo = new PEER_PNRP_REGISTRATION_INFO(); GCHandle handle; //------------------------------------------------- //Set data //------------------------------------------------- if (m_PeerNameRecord.Data != null) { regInfo.payLoad.cbPayload = (UInt32)m_PeerNameRecord.Data.Length; handle = GCHandle.Alloc(m_PeerNameRecord.Data, GCHandleType.Pinned); regInfo.payLoad.pbPayload = handle.AddrOfPinnedObject(); //m_PeerNameRecord.Data; } else { handle = new GCHandle(); } //------------------------------------------------- //Set comment //------------------------------------------------- if (m_PeerNameRecord.Comment != null && m_PeerNameRecord.Comment.Length > 0) { regInfo.pwszComment = m_PeerNameRecord.Comment; } //------------------------------------------------- //Set cloud name //------------------------------------------------- if (m_Cloud != null) { regInfo.pwszCloudName = m_Cloud.InternalName; } try { if (m_PeerNameRecord.EndPointCollection.Count == 0) { //------------------------------------------------- //Set port only if the addresses are null //and then set the selection to auto addresses //------------------------------------------------- regInfo.wport = (ushort)m_Port; if(m_UseAutoEndPointSelection) regInfo.cAddresses = PEER_PNRP_AUTO_ADDRESSES; //------------------------------------------------- //Call the native API to register //------------------------------------------------- int result = UnsafeP2PNativeMethods.PeerPnrpRegister(m_PeerNameRecord.PeerName.ToString(), ref regInfo, out m_RegistrationHandle); if (result != 0) { throw PeerToPeerException.CreateFromHr(SR.GetString(SR.Pnrp_CouldNotRegisterPeerName), result); } } else { //------------------------------------------------- //Set the Endpoint List //------------------------------------------------- int numAddresses = m_PeerNameRecord.EndPointCollection.Count; int cbRequriedBytes = numAddresses * Marshal.SizeOf(typeof(IntPtr)); IntPtr pSocketAddrList = Marshal.AllocHGlobal(cbRequriedBytes); GCHandle[] GCHandles = new GCHandle[numAddresses]; try { unsafe { IntPtr* pAddress = (IntPtr*)pSocketAddrList; for (int i = 0; i < m_PeerNameRecord.EndPointCollection.Count; i++) { byte[] sockaddr = SystemNetHelpers.SOCKADDRFromIPEndPoint(m_PeerNameRecord.EndPointCollection[i]); GCHandles[i] = GCHandle.Alloc(sockaddr, GCHandleType.Pinned); IntPtr psockAddr = GCHandles[i].AddrOfPinnedObject(); pAddress[i] = psockAddr; } } regInfo.ArrayOfSOCKADDRIN6Pointers = pSocketAddrList; regInfo.cAddresses = (UInt32)numAddresses; int result = UnsafeP2PNativeMethods.PeerPnrpRegister(m_PeerNameRecord.PeerName.ToString(), ref regInfo, out m_RegistrationHandle); if (result != 0) { throw PeerToPeerException.CreateFromHr(SR.GetString(SR.Pnrp_CouldNotRegisterPeerName), result); } } finally { if (pSocketAddrList != IntPtr.Zero) Marshal.FreeHGlobal(pSocketAddrList); for (int i = 0; i < GCHandles.Length; i++) { if (GCHandles[i].IsAllocated) GCHandles[i].Free(); } } } } finally { if (handle.IsAllocated) { handle.Free(); } } m_RegisteredPeerName = m_PeerNameRecord.PeerName; m_IsRegistered = true; Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Registration is successful. The handle is {0}", m_RegistrationHandle.DangerousGetHandle()); } /// /// Update is called if an existing registration needs to be updated /// // // // // // // // // // // // // // // [System.Security.SecurityCritical] public void Update() { //------------------------------------------------- //Check for the dead object //------------------------------------------------- if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName); //------------------------------------------------- //If there is no existing registration to update //get out //------------------------------------------------- if (!IsRegistered()) { throw new InvalidOperationException(SR.GetString(SR.Pnrp_CallRegisterBeforeUpdate)); } //------------------------------------------------- //Check for parameters //------------------------------------------------- if (m_PeerNameRecord.PeerName == null) { throw new ArgumentNullException(SR.GetString(SR.Pnrp_InvalidPeerName)); } //------------------------------------------------- //If the current PeerName associated with the //current registration is not the same as the //PeerName given now - throw //------------------------------------------------- if (!m_RegisteredPeerName.Equals(m_PeerNameRecord.PeerName)) { throw new InvalidOperationException(SR.GetString(SR.Pnrp_CantChangePeerNameAfterRegistration)); } //------------------------------------------ //If auto address selection is turned off //then there must be atleast Data or Endpoints //specified //------------------------------------------ if (!m_UseAutoEndPointSelection) { if ((EndPointCollection.Count == 0) || (Data == null || Data.Length <= 0)) { throw new PeerToPeerException(SR.GetString(SR.Pnrp_BlobOrEndpointListNeeded)); } } //------------------------------------------------- //Demand for the Unrestricted Pnrp Permission //------------------------------------------------- PnrpPermission.UnrestrictedPnrpPermission.Demand(); //--------------------------------------------------------------- //Log trace info //--------------------------------------------------------------- if (Logging.P2PTraceSource.Switch.ShouldTrace(TraceEventType.Information)) { Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Update() is called with the following Info"); m_PeerNameRecord.TracePeerNameRecord(); } PEER_PNRP_REGISTRATION_INFO regInfo = new PEER_PNRP_REGISTRATION_INFO(); //------------------------------------------------- //Set data //------------------------------------------------- if (m_PeerNameRecord.Data != null) { regInfo.payLoad.cbPayload = (UInt32)m_PeerNameRecord.Data.Length; //Marshal.All //regInfo.payLoad.pbPayload = m_PeerNameRecord.Data; GCHandle handle = GCHandle.Alloc(m_PeerNameRecord.Data, GCHandleType.Pinned); regInfo.payLoad.pbPayload = handle.AddrOfPinnedObject(); //m_PeerNameRecord.Data; handle.Free(); }; //------------------------------------------------- //Set comment //------------------------------------------------- if (m_PeerNameRecord.Comment != null && m_PeerNameRecord.Comment.Length > 0) { regInfo.pwszComment = m_PeerNameRecord.Comment; } //------------------------------------------------- //Set cloud name //------------------------------------------------- regInfo.pwszCloudName = null; if (m_Cloud != null) { regInfo.pwszCloudName = m_Cloud.InternalName; } if (m_PeerNameRecord.EndPointCollection.Count == 0) { //------------------------------------------------- //Set port only if the addresses are null //and then set the selection to auto addresses //------------------------------------------------- regInfo.wport = (ushort)m_Port; regInfo.cAddresses = PEER_PNRP_AUTO_ADDRESSES; int result = UnsafeP2PNativeMethods.PeerPnrpUpdateRegistration(m_RegistrationHandle, ref regInfo); if (result != 0) { throw PeerToPeerException.CreateFromHr(SR.GetString(SR.Pnrp_CouldNotRegisterPeerName), result); } return; } else { //------------------------------------------------- //Set the Endpoint List //------------------------------------------------- int numAddresses = m_PeerNameRecord.EndPointCollection.Count; int cbRequriedBytes = numAddresses * Marshal.SizeOf(typeof(IntPtr)); IntPtr pSocketAddrList = Marshal.AllocHGlobal(cbRequriedBytes); GCHandle[] GCHandles = new GCHandle[numAddresses]; try { unsafe { IntPtr* pAddress = (IntPtr*)pSocketAddrList; for (int i = 0; i < m_PeerNameRecord.EndPointCollection.Count; i++) { byte[] sockaddr = SystemNetHelpers.SOCKADDRFromIPEndPoint(m_PeerNameRecord.EndPointCollection[i]); GCHandles[i] = GCHandle.Alloc(sockaddr, GCHandleType.Pinned); IntPtr psockAddr = GCHandles[i].AddrOfPinnedObject(); pAddress[i] = psockAddr; } } regInfo.ArrayOfSOCKADDRIN6Pointers = pSocketAddrList; regInfo.cAddresses = (UInt32)numAddresses; int result = UnsafeP2PNativeMethods.PeerPnrpUpdateRegistration(m_RegistrationHandle, ref regInfo); if (result != 0) { throw PeerToPeerException.CreateFromHr(SR.GetString(SR.Pnrp_CouldNotRegisterPeerName), result); } } finally { if (pSocketAddrList != IntPtr.Zero) Marshal.FreeHGlobal(pSocketAddrList); for (int i = 0; i < GCHandles.Length; i++) { if (GCHandles[i].IsAllocated) GCHandles[i].Free(); } } Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Update of existing registration is successful. The handle is {0}", m_RegistrationHandle.DangerousGetHandle()); } } /// /// Unregister the existing registration. /// // // // // // // [System.Security.SecurityCritical] public void Stop() { if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName); //------------------------------------------------- //No registration happened previously - throw //------------------------------------------------- if(m_RegistrationHandle.IsInvalid || m_RegistrationHandle.IsClosed) { throw new InvalidOperationException(SR.GetString(SR.Pnrp_NoRegistrationFound)); } //------------------------------------------------- //Demand for the Unrestricted Pnrp Permission //------------------------------------------------- PnrpPermission.UnrestrictedPnrpPermission.Demand(); m_RegistrationHandle.Dispose(); m_PeerNameRecord = new PeerNameRecord(); m_RegisteredPeerName = null; m_IsRegistered = false; } private bool m_Disposed; /// /// Dispose explicit /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Dispose impl /// /// Whether we are disposing(true) or the system is disposing (false) protected virtual void Dispose(bool disposing) { if (!m_Disposed) { try { Stop(); } catch (ObjectDisposedException) { } catch (InvalidOperationException) { } //rest throw since we don't expect any other exceptions } m_Disposed = true; } /// /// Constructor to enable serialization /// /// /// [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] protected PeerNameRegistration(SerializationInfo info, StreamingContext context) { m_Port = info.GetInt32("_Port"); m_UseAutoEndPointSelection = info.GetBoolean("_UseAutoEndPointSelection"); m_Cloud = info.GetValue("_Cloud", typeof(Cloud)) as Cloud; m_PeerNameRecord = info.GetValue("_PeerNameRecord", typeof(PeerNameRecord)) as PeerNameRecord; } // // // [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase", Justification = "System.Net.dll is still using pre-v4 security model and needs this demand")] [System.Security.SecurityCritical] [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter, SerializationFormatter = true)] void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { GetObjectData(info, context); } /// /// This is made virtual so that derived types can be implemented correctly /// /// /// [SecurityPermission(SecurityAction.LinkDemand, SerializationFormatter = true)] protected virtual void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("_Port", m_Port); info.AddValue("_UseAutoEndPointSelection", m_UseAutoEndPointSelection); info.AddValue("_Cloud", m_Cloud); info.AddValue("_PeerNameRecord", m_PeerNameRecord); } } }