//------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ namespace System.ServiceModel.Channels { using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Net; using System.Runtime; using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Diagnostics; using System.ServiceModel.Dispatcher; using System.ServiceModel.Security; using System.Text; using System.Threading; using System.Xml; partial class PeerNodeImplementation : IPeerNodeMessageHandling { const int maxViaSize = 4096; public delegate void MessageAvailableCallback(Message message); // configuration int connectTimeout; IPAddress listenIPAddress; Uri listenUri; int port; long maxReceivedMessageSize; int minNeighbors; int idealNeighbors; int maxNeighbors; int maxReferrals; string meshId; PeerMessagePropagationFilter messagePropagationFilter; SynchronizationContext messagePropagationFilterContext; int maintainerInterval = PeerTransportConstants.MaintainerInterval; // milliseconds before a maintainer kicks in PeerResolver resolver; PeerNodeConfig config; PeerSecurityManager securityManager; internal MessageEncodingBindingElement EncodingElement; // internal state ManualResetEvent connectCompletedEvent; // raised when maintainer has connected or given up MessageEncoder encoder; // used for encoding internal messages // Double-checked locking pattern requires volatile for read/write synchronization volatile bool isOpen; Exception openException; // exception to be thrown from Open Dictionary messageFilters; int refCount; // number of factories/channels that are using this instance SimpleStateManager stateManager; // manages open/close operations object thisLock = new Object(); PeerNodeTraceRecord traceRecord; PeerNodeTraceRecord completeTraceRecord; // contains address info as well // primary infrastructure components internal PeerConnector connector; // Purely for testing do not take a internal dependency on this PeerMaintainer maintainer; internal PeerFlooder flooder; // Purely for testing do not take an internal dependency on this PeerNeighborManager neighborManager; PeerIPHelper ipHelper; PeerService service; object resolverRegistrationId; bool registered; public event EventHandler Offline; public event EventHandler Online; Dictionary uri2SecurityProtocol; Dictionary serviceHandlers; BufferManager bufferManager = null; internal static byte[] DefaultId = new byte[0]; XmlDictionaryReaderQuotas readerQuotas; long maxBufferPoolSize; internal int MaxSendQueue = 128, MaxReceiveQueue = 128; public PeerNodeImplementation() { // intialize default configuration connectTimeout = PeerTransportConstants.ConnectTimeout; maxReceivedMessageSize = TransportDefaults.MaxReceivedMessageSize; minNeighbors = PeerTransportConstants.MinNeighbors; idealNeighbors = PeerTransportConstants.IdealNeighbors; maxNeighbors = PeerTransportConstants.MaxNeighbors; maxReferrals = PeerTransportConstants.MaxReferrals; port = PeerTransportDefaults.Port; // initialize internal state connectCompletedEvent = new ManualResetEvent(false); encoder = new BinaryMessageEncodingBindingElement().CreateMessageEncoderFactory().Encoder; messageFilters = new Dictionary(); stateManager = new SimpleStateManager(this); uri2SecurityProtocol = new Dictionary(); readerQuotas = new XmlDictionaryReaderQuotas(); this.maxBufferPoolSize = TransportDefaults.MaxBufferPoolSize; } // To facilitate testing public event EventHandler NeighborClosed; public event EventHandler NeighborClosing; public event EventHandler NeighborConnected; public event EventHandler NeighborOpened; public event EventHandler Aborted; public PeerNodeConfig Config { get { return this.config; } private set { Fx.Assert(value != null, "PeerNodeImplementation.Config can not be set to null"); this.config = value; } } public bool IsOnline { get { lock (ThisLock) { if (isOpen) return neighborManager.IsOnline; else return false; } } } internal bool IsOpen { get { return isOpen; } } public IPAddress ListenIPAddress { get { return listenIPAddress; } set { // No validation necessary at this point. When the service is opened, it will throw if the IP address is invalid lock (ThisLock) { ThrowIfOpen(); listenIPAddress = value; } } } public Uri ListenUri { get { return listenUri; } set { if (value == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); if (value.Scheme != PeerStrings.Scheme) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("value", SR.GetString(SR.InvalidUriScheme, value.Scheme, PeerStrings.Scheme)); } Fx.Assert(value.PathAndQuery == "/", "PeerUriCannotContainPath"); lock (ThisLock) { ThrowIfOpen(); listenUri = value; } } } public long MaxBufferPoolSize { get { return maxBufferPoolSize; } set { lock (ThisLock) { ThrowIfOpen(); maxBufferPoolSize = value; } } } public long MaxReceivedMessageSize { get { return maxReceivedMessageSize; } set { if (!(value >= PeerTransportConstants.MinMessageSize)) { throw Fx.AssertAndThrow("invalid MaxReceivedMessageSize"); } lock (ThisLock) { ThrowIfOpen(); maxReceivedMessageSize = value; } } } public string MeshId { get { lock (ThisLock) { ThrowIfNotOpen(); return meshId; } } } public PeerMessagePropagationFilter MessagePropagationFilter { get { return messagePropagationFilter; } set { lock (ThisLock) { // null is ok and causes optimised flooding codepath messagePropagationFilter = value; messagePropagationFilterContext = ThreadBehavior.GetCurrentSynchronizationContext(); } } } // Made internal to facilitate testing public PeerNeighborManager NeighborManager { get { return neighborManager; } } public ulong NodeId { get { ThrowIfNotOpen(); return config.NodeId; } } public int Port { get { return port; } set { lock (ThisLock) { ThrowIfOpen(); port = value; } } } public int ListenerPort { get { ThrowIfNotOpen(); return config.ListenerPort; } } public XmlDictionaryReaderQuotas ReaderQuotas { get { return this.readerQuotas; } } public PeerResolver Resolver { get { return resolver; } set { Fx.Assert(value != null, "null Resolver"); lock (ThisLock) { ThrowIfOpen(); resolver = value; } } } public PeerSecurityManager SecurityManager { get { return this.securityManager; } set { this.securityManager = value; } } internal PeerService Service { get { return this.service; } set { lock (ThisLock) { ThrowIfNotOpen(); this.service = value; } } } object ThisLock { get { return thisLock; } } public void Abort() { stateManager.Abort(); } public IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback callback, object state) { return stateManager.BeginClose(timeout, callback, state); } public IAsyncResult BeginOpen(TimeSpan timeout, AsyncCallback callback, object state, bool waitForOnline) { return stateManager.BeginOpen(timeout, callback, state, waitForOnline); } public Guid ProcessOutgoingMessage(Message message, Uri via) { Guid result = Guid.NewGuid(); System.Xml.UniqueId messageId = new System.Xml.UniqueId(result); if (-1 != message.Headers.FindHeader(PeerStrings.MessageId, PeerStrings.Namespace)) PeerExceptionHelper.ThrowInvalidOperation_ConflictingHeader(PeerStrings.MessageId); if (-1 != message.Headers.FindHeader(PeerOperationNames.PeerTo, PeerStrings.Namespace)) PeerExceptionHelper.ThrowInvalidOperation_ConflictingHeader(PeerOperationNames.PeerTo); if (-1 != message.Headers.FindHeader(PeerOperationNames.PeerVia, PeerStrings.Namespace)) PeerExceptionHelper.ThrowInvalidOperation_ConflictingHeader(PeerOperationNames.PeerVia); if (-1 != message.Headers.FindHeader(PeerOperationNames.Flood, PeerStrings.Namespace, PeerOperationNames.Demuxer)) PeerExceptionHelper.ThrowInvalidOperation_ConflictingHeader(PeerOperationNames.Flood); message.Headers.Add(PeerDictionaryHeader.CreateMessageIdHeader(messageId)); message.Properties.Via = via; message.Headers.Add(MessageHeader.CreateHeader(PeerOperationNames.PeerTo, PeerStrings.Namespace, message.Headers.To)); message.Headers.Add(PeerDictionaryHeader.CreateViaHeader(via)); message.Headers.Add(PeerDictionaryHeader.CreateFloodRole()); return result; } public void SecureOutgoingMessage(ref Message message, Uri via, TimeSpan timeout, SecurityProtocol securityProtocol) { if (securityProtocol != null) { securityProtocol.SecureOutgoingMessage(ref message, timeout); } } public IAsyncResult BeginSend(object registrant, Message message, Uri via, ITransportFactorySettings settings, TimeSpan timeout, AsyncCallback callback, object state, SecurityProtocol securityProtocol) { PeerFlooder localFlooder; int factoryMaxReceivedMessageSize; TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); MessageBuffer messageBuffer = null; Message securedMessage = null; ulong hopcount = PeerTransportConstants.MaxHopCount; PeerMessagePropagation propagateFlags = PeerMessagePropagation.LocalAndRemote; int messageSize = (int)-1; byte[] id; SendAsyncResult result = new SendAsyncResult(callback, state); AsyncCallback onFloodComplete = Fx.ThunkCallback(new AsyncCallback(result.OnFloodComplete)); try { lock (ThisLock) { ThrowIfNotOpen(); localFlooder = flooder; } // we know this will fit in an int because of our MaxReceivedMessageSize restrictions factoryMaxReceivedMessageSize = (int)Math.Min(maxReceivedMessageSize, settings.MaxReceivedMessageSize); Guid guid = ProcessOutgoingMessage(message, via); SecureOutgoingMessage(ref message, via, timeout, securityProtocol); if ((message is SecurityAppliedMessage)) { ArraySegment buffer = encoder.WriteMessage(message, int.MaxValue, bufferManager); securedMessage = encoder.ReadMessage(buffer, bufferManager); id = (message as SecurityAppliedMessage).PrimarySignatureValue; messageSize = (int)buffer.Count; } else { securedMessage = message; id = guid.ToByteArray(); } messageBuffer = securedMessage.CreateBufferedCopy(factoryMaxReceivedMessageSize); string contentType = settings.MessageEncoderFactory.Encoder.ContentType; if (this.messagePropagationFilter != null) { using (Message filterMessage = messageBuffer.CreateMessage()) { propagateFlags = ((IPeerNodeMessageHandling)this).DetermineMessagePropagation(filterMessage, PeerMessageOrigination.Local); } } if ((propagateFlags & PeerMessagePropagation.Remote) != PeerMessagePropagation.None) { if (hopcount == 0) propagateFlags &= ~PeerMessagePropagation.Remote; } // flood it out IAsyncResult ar = null; if ((propagateFlags & PeerMessagePropagation.Remote) != 0) { ar = localFlooder.BeginFloodEncodedMessage(id, messageBuffer, timeoutHelper.RemainingTime(), onFloodComplete, null); if (DiagnosticUtility.ShouldTraceVerbose) { TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.PeerChannelMessageSent, SR.GetString(SR.TraceCodePeerChannelMessageSent), this, message); } } else { ar = new CompletedAsyncResult(onFloodComplete, null); } if (ar == null) { Fx.Assert("SendAsyncResult must have an Async Result for onFloodComplete"); } // queue up the pre-encoded message for local channels if ((propagateFlags & PeerMessagePropagation.Local) != 0) { using (Message msg = messageBuffer.CreateMessage()) { int i = msg.Headers.FindHeader(SecurityJan2004Strings.Security, SecurityJan2004Strings.Namespace); if (i >= 0) { msg.Headers.AddUnderstood(i); } using (MessageBuffer clientBuffer = msg.CreateBufferedCopy(factoryMaxReceivedMessageSize)) { DeliverMessageToClientChannels(registrant, clientBuffer, via, message.Headers.To, contentType, messageSize, -1, null); } } } result.OnLocalDispatchComplete(result); } finally { message.Close(); if (securedMessage != null) securedMessage.Close(); if (messageBuffer != null) messageBuffer.Close(); } return result; } public void Close(TimeSpan timeout) { stateManager.Close(timeout); } void CloseCore(TimeSpan timeout, bool graceful) { PeerService lclService; PeerMaintainer lclMaintainer; PeerNeighborManager lclNeighborManager; PeerConnector lclConnector; PeerIPHelper lclIPHelper; PeerNodeConfig lclConfig; PeerFlooder lclFlooder; Exception exception = null; TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); if (DiagnosticUtility.ShouldTraceInformation) { TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.PeerNodeClosing, SR.GetString(SR.TraceCodePeerNodeClosing), this.traceRecord, this, null); } lock (ThisLock) { isOpen = false; lclMaintainer = maintainer; lclNeighborManager = neighborManager; lclConnector = connector; lclIPHelper = ipHelper; lclService = service; lclConfig = config; lclFlooder = flooder; } // only unregister if we are doing a g----ful shutdown try { if (graceful) { UnregisterAddress(timeout); } else { if (lclConfig != null) { ActionItem.Schedule(new Action(UnregisterAddress), lclConfig.UnregisterTimeout); } } } catch (Exception e) { if (Fx.IsFatal(e)) throw; DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); if (exception == null) exception = e; } try { if (lclConnector != null) lclConnector.Closing(); if (lclService != null) { try { lclService.Abort(); } catch (Exception e) { if (Fx.IsFatal(e)) throw; DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); if (exception == null) exception = e; } } if (lclMaintainer != null) { try { lclMaintainer.Close(); } catch (Exception e) { if (Fx.IsFatal(e)) throw; DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); if (exception == null) exception = e; } } if (lclIPHelper != null) { try { lclIPHelper.Close(); lclIPHelper.AddressChanged -= new EventHandler(stateManager.OnIPAddressesChanged); } catch (Exception e) { if (Fx.IsFatal(e)) throw; DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); if (exception == null) exception = e; } } if (lclNeighborManager != null) { lclNeighborManager.NeighborConnected -= new EventHandler(OnNeighborConnected); lclNeighborManager.NeighborOpened -= new EventHandler(this.securityManager.OnNeighborOpened); this.securityManager.OnNeighborAuthenticated -= new EventHandler(this.OnNeighborAuthenticated); lclNeighborManager.Online -= new EventHandler(FireOnline); lclNeighborManager.Offline -= new EventHandler(FireOffline); try { lclNeighborManager.Shutdown(graceful, timeoutHelper.RemainingTime()); } catch (Exception e) { if (Fx.IsFatal(e)) throw; DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); if (exception == null) exception = e; } // unregister for neighbor close events once shutdown has completed lclNeighborManager.NeighborClosed -= new EventHandler(OnNeighborClosed); lclNeighborManager.NeighborClosing -= new EventHandler(OnNeighborClosing); lclNeighborManager.Close(); } if (lclConnector != null) { try { lclConnector.Close(); } catch (Exception e) { if (Fx.IsFatal(e)) throw; DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); if (exception == null) exception = e; } } if (lclFlooder != null) { try { lclFlooder.Close(); } catch (Exception e) { if (Fx.IsFatal(e)) throw; DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); if (exception == null) exception = e; } } } catch (Exception e) { if (Fx.IsFatal(e)) throw; if (exception == null) exception = e; } // reset object for next call to open EventHandler abortedHandler = null; lock (ThisLock) { // clear out old components (so they can be garbage collected) neighborManager = null; connector = null; maintainer = null; flooder = null; ipHelper = null; service = null; // reset generated config config = null; meshId = null; abortedHandler = Aborted; } // Notify anyone who is interested that abort has occured if (!graceful && abortedHandler != null) { try { abortedHandler(this, EventArgs.Empty); } catch (Exception e) { if (Fx.IsFatal(e)) throw; DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); if (exception == null) exception = e; } } if (DiagnosticUtility.ShouldTraceInformation) { TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.PeerNodeClosed, SR.GetString(SR.TraceCodePeerNodeClosed), this.traceRecord, this, null); } if (exception != null && graceful == true) // Swallows all non fatal exceptions during Abort { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(exception); } } // Performs case-insensitive comparison of two vias bool CompareVia(Uri via1, Uri via2) { return (Uri.Compare(via1, via2, (UriComponents.Scheme | UriComponents.UserInfo | UriComponents.Host | UriComponents.Port | UriComponents.Path), UriFormat.SafeUnescaped, StringComparison.OrdinalIgnoreCase) == 0); } public static void EndClose(IAsyncResult result) { if (result == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result"); SimpleStateManager.EndClose(result); } public static void EndOpen(IAsyncResult result) { if (result == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result"); SimpleStateManager.EndOpen(result); } public static void EndSend(IAsyncResult result) { if (result == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result"); SendAsyncResult.End(result); } // Necessary to allow access of the EventHandlers which can only be done from inside the class void FireOffline(object sender, EventArgs e) { if (!isOpen) { return; } EventHandler handler = Offline; if (handler != null) { handler(this, EventArgs.Empty); } } // Necessary to allow access of the EventHandlers which can only be done from inside the class void FireOnline(object sender, EventArgs e) { if (!isOpen) { return; } EventHandler handler = Online; if (handler != null) { handler(this, EventArgs.Empty); } } // static Uri -> PeerNode mapping static internal Dictionary peerNodes = new Dictionary(); internal static PeerNodeImplementation Get(Uri listenUri) { PeerNodeImplementation node = null; if (!TryGet(listenUri, out node)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new InvalidOperationException(SR.GetString(SR.NoTransportManagerForUri, listenUri))); } return node; } internal protected static bool TryGet(Uri listenUri, out PeerNodeImplementation result) { if (listenUri == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("listenUri"); } if (listenUri.Scheme != PeerStrings.Scheme) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("listenUri", SR.GetString(SR.InvalidUriScheme, listenUri.Scheme, PeerStrings.Scheme)); } result = null; bool success = false; // build base uri Uri baseUri = new UriBuilder(PeerStrings.Scheme, listenUri.Host).Uri; lock (peerNodes) { if (peerNodes.ContainsKey(baseUri)) { result = peerNodes[baseUri]; success = true; } } return success; } public static bool TryGet(string meshId, out PeerNodeImplementation result) { UriBuilder uriBuilder = new UriBuilder(); uriBuilder.Host = meshId; uriBuilder.Scheme = PeerStrings.Scheme; bool success = PeerNodeImplementation.TryGet(uriBuilder.Uri, out result); return success; } // internal method to return an existing PeerNode or create a new one with the given settings public static PeerNodeImplementation Get(Uri listenUri, Registration registration) { if (listenUri == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("listenUri"); if (listenUri.Scheme != PeerStrings.Scheme) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("listenUri", SR.GetString(SR.InvalidUriScheme, listenUri.Scheme, PeerStrings.Scheme)); } // build base uri Uri baseUri = new UriBuilder(PeerStrings.Scheme, listenUri.Host).Uri; lock (peerNodes) { PeerNodeImplementation peerNodeImpl = null; PeerNodeImplementation peerNode = null; if (peerNodes.TryGetValue(baseUri, out peerNode)) { peerNodeImpl = (PeerNodeImplementation)peerNode; // ensure that the PeerNode is compatible registration.CheckIfCompatible(peerNodeImpl, listenUri); peerNodeImpl.refCount++; return peerNodeImpl; } // create a new PeerNode, and add it to the dictionary peerNodeImpl = registration.CreatePeerNode(); peerNodes[baseUri] = peerNodeImpl; peerNodeImpl.refCount = 1; return peerNodeImpl; } } // SimpleStateManager callback - Called on final release of PeerNode. void InternalClose(TimeSpan timeout, bool graceful) { CloseCore(timeout, graceful); lock (ThisLock) { messageFilters.Clear(); } } protected void OnAbort() { InternalClose(TimeSpan.FromTicks(0), false); } protected void OnClose(TimeSpan timeout) { InternalClose(timeout, true); } // called when the maintainer has completed the connection attempt (successful or not) void OnConnectionAttemptCompleted(Exception e) { // store the exception if one occured when trying to connect, so that it can be rethrown from Open Fx.Assert(openException == null, "OnConnectionAttemptCompleted twice"); openException = e; if (openException == null && DiagnosticUtility.ShouldTraceInformation) { TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.PeerNodeOpened, SR.GetString(SR.TraceCodePeerNodeOpened), this.completeTraceRecord, this, null); } else if (openException != null && DiagnosticUtility.ShouldTraceError) { TraceUtility.TraceEvent(TraceEventType.Error, TraceCode.PeerNodeOpenFailed, SR.GetString(SR.TraceCodePeerNodeOpenFailed), this.completeTraceRecord, this, e); } connectCompletedEvent.Set(); } bool IPeerNodeMessageHandling.ValidateIncomingMessage(ref Message message, Uri via) { SecurityProtocol protocol = null; if (via == null) { Fx.Assert("FloodMessage doesn't contain Via header!"); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.PeerMessageMustHaveVia, message.Headers.Action))); } if (TryGetSecurityProtocol(via, out protocol)) { protocol.VerifyIncomingMessage(ref message, ServiceDefaults.SendTimeout, null); return true; } return false; } internal bool TryGetSecurityProtocol(Uri via, out SecurityProtocol protocol) { lock (ThisLock) { RefCountedSecurityProtocol wrapper = null; bool result = false; protocol = null; if (uri2SecurityProtocol.TryGetValue(via, out wrapper)) { protocol = wrapper.Protocol; result = true; } return result; } } void IPeerNodeMessageHandling.HandleIncomingMessage(MessageBuffer messageBuffer, PeerMessagePropagation propagateFlags, int index, MessageHeader hopHeader, Uri via, Uri to) { if (DiagnosticUtility.ShouldTraceVerbose) { TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.PeerFloodedMessageReceived, SR.GetString(SR.TraceCodePeerFloodedMessageReceived), this.traceRecord, this, null); } if (via == null) { Fx.Assert("No VIA in the forwarded message!"); using (Message message = messageBuffer.CreateMessage()) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.PeerMessageMustHaveVia, message.Headers.Action))); } } if ((propagateFlags & PeerMessagePropagation.Local) != 0) { DeliverMessageToClientChannels(null, messageBuffer, via, to, messageBuffer.MessageContentType, (int)maxReceivedMessageSize, index, hopHeader); messageBuffer = null; } else { if (DiagnosticUtility.ShouldTraceVerbose) { using (Message traceMessage = messageBuffer.CreateMessage()) { TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.PeerFloodedMessageNotPropagated, SR.GetString(SR.TraceCodePeerFloodedMessageNotPropagated), this.traceRecord, this, null, traceMessage); } } } } PeerMessagePropagation IPeerNodeMessageHandling.DetermineMessagePropagation(Message message, PeerMessageOrigination origination) { PeerMessagePropagation propagateFlags = PeerMessagePropagation.LocalAndRemote; PeerMessagePropagationFilter filter = MessagePropagationFilter; if (filter != null) { try { SynchronizationContext context = messagePropagationFilterContext; if (context != null) { context.Send(delegate(object state) { propagateFlags = filter.ShouldMessagePropagate(message, origination); }, null); } else { propagateFlags = filter.ShouldMessagePropagate(message, origination); } } catch (Exception e) { if (Fx.IsFatal(e)) throw; throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(SR.GetString(SR.MessagePropagationException), e); } } // Don't flood if the Node is closed if (!isOpen) { propagateFlags = PeerMessagePropagation.None; } return propagateFlags; } // Queued callback to actually process the address change // The design is such that any address change notifications are queued just like Open/Close operations. // So, we need not worry about address changes racing with other address changes or Open/Close operations. // Abort can happen at any time. However, Abort skips unregistering addresses, so this method doesn't have // to worry about undoing its work if Abort happens. void OnIPAddressChange() { string lclMeshId = null; PeerNodeAddress nodeAddress = null; object lclResolverRegistrationId = null; bool lclRegistered = false; PeerIPHelper lclIPHelper = ipHelper; PeerNodeConfig lclconfig = config; bool processChange = false; TimeoutHelper timeoutHelper = new TimeoutHelper(ServiceDefaults.SendTimeout); // Determine if IP addresses have really changed before notifying the resolver // since it is possible that another change notification ahead of this one in the queue // may have already completed notifying the resolver of the most current change. if (lclIPHelper != null && config != null) { nodeAddress = lclconfig.GetListenAddress(false); processChange = lclIPHelper.AddressesChanged(nodeAddress.IPAddresses); if (processChange) { // Build the nodeAddress with the updated IP addresses nodeAddress = new PeerNodeAddress( nodeAddress.EndpointAddress, lclIPHelper.GetLocalAddresses()); } } lock (ThisLock) { // Skip processing if the node isn't open anymore or if addresses haven't changed if (processChange && isOpen) { lclMeshId = meshId; lclResolverRegistrationId = resolverRegistrationId; lclRegistered = registered; config.SetListenAddress(nodeAddress); completeTraceRecord = new PeerNodeTraceRecord(config.NodeId, meshId, nodeAddress); } else { return; } } //#57954 - log and ---- non-critical exceptions during network change event notifications try { // Do we have any addresses? If so, update or re-register. Otherwise, unregister. if (nodeAddress.IPAddresses.Count > 0) { if (lclRegistered) { resolver.Update(lclResolverRegistrationId, nodeAddress, timeoutHelper.RemainingTime()); } else { RegisterAddress(lclMeshId, nodeAddress, timeoutHelper.RemainingTime()); } } else { UnregisterAddress(timeoutHelper.RemainingTime()); } } catch (Exception e) { if (Fx.IsFatal(e)) throw; DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning); } PingConnections(); if (DiagnosticUtility.ShouldTraceInformation) { TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.PeerNodeAddressChanged, SR.GetString(SR.TraceCodePeerNodeAddressChanged), this.completeTraceRecord, this, null); } } // Register with the resolver void RegisterAddress(string lclMeshId, PeerNodeAddress nodeAddress, TimeSpan timeout) { // Register only if we have any addresses if (nodeAddress.IPAddresses.Count > 0) { object lclResolverRegistrationId = null; try { lclResolverRegistrationId = resolver.Register(lclMeshId, nodeAddress, timeout); } catch (Exception e) { if (Fx.IsFatal(e)) throw; throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(SR.GetString(SR.ResolverException), e)); } lock (ThisLock) { if (!(!registered)) { throw Fx.AssertAndThrow("registered expected to be false"); } registered = true; resolverRegistrationId = lclResolverRegistrationId; } } } // Unregister that should only be called from non-user threads. //since this is invoked on background threads, we log and ---- all non-critical exceptions //#57972 void UnregisterAddress(object timeout) { try { UnregisterAddress((TimeSpan)timeout); } catch (Exception e) { if (Fx.IsFatal(e)) throw; DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning); } } void UnregisterAddress(TimeSpan timeout) { bool needToUnregister = false; object lclResolverRegistrationId = null; lock (ThisLock) { if (registered) { needToUnregister = true; lclResolverRegistrationId = resolverRegistrationId; registered = false; // this ensures that the current thread will do unregistration } resolverRegistrationId = null; } if (needToUnregister) { try { resolver.Unregister(lclResolverRegistrationId, timeout); } catch (Exception e) { if (Fx.IsFatal(e)) throw; throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(SR.GetString(SR.ResolverException), e)); } } } void OnNeighborClosed(object sender, PeerNeighborCloseEventArgs e) { IPeerNeighbor neighbor = (IPeerNeighbor)sender; PeerConnector localConnector; PeerMaintainer localMaintainer; PeerFlooder localFlooder; localConnector = connector; localMaintainer = maintainer; localFlooder = flooder; UtilityExtension.OnNeighborClosed(neighbor); PeerChannelAuthenticatorExtension.OnNeighborClosed(neighbor); if (localConnector != null) localConnector.OnNeighborClosed(neighbor); if (localMaintainer != null) localMaintainer.OnNeighborClosed(neighbor); if (localFlooder != null) localFlooder.OnNeighborClosed(neighbor); // Finally notify any Peernode client EventHandler handler = NeighborClosed; if (handler != null) { handler(this, e); } } void OnNeighborClosing(object sender, PeerNeighborCloseEventArgs e) { IPeerNeighbor neighbor = (IPeerNeighbor)sender; PeerConnector localConnector; localConnector = connector; if (localConnector != null) localConnector.OnNeighborClosing(neighbor, e.Reason); // Finally notify any Peernode client EventHandler handler = NeighborClosing; if (handler != null) { handler(this, e); } } void OnNeighborConnected(object sender, EventArgs e) { IPeerNeighbor neighbor = (IPeerNeighbor)sender; PeerMaintainer localMaintainer = maintainer; PeerFlooder localFlooder = flooder; if (localFlooder != null) localFlooder.OnNeighborConnected(neighbor); if (localMaintainer != null) localMaintainer.OnNeighborConnected(neighbor); UtilityExtension.OnNeighborConnected(neighbor); // Finally notify any Peernode client EventHandler handler = NeighborConnected; if (handler != null) { handler(this, EventArgs.Empty); } } // raised by the neighbor manager when any connection has reached the opened state void OnNeighborAuthenticated(object sender, EventArgs e) { IPeerNeighbor n = (IPeerNeighbor)sender; //hand the authenticated neighbor over to connector. //If neighbor is aborted before PeerConnector localConnector = connector; if (localConnector != null) connector.OnNeighborAuthenticated(n); // Finally notify any Peernode client EventHandler handler = NeighborOpened; if (handler != null) { handler(this, EventArgs.Empty); } } // Open blocks the thread until either Online happens or Open times out. void OnOpen(TimeSpan timeout, bool waitForOnline) { bool aborted = false; EventHandler connectedHandler = delegate(object source, EventArgs args) { connectCompletedEvent.Set(); }; EventHandler abortHandler = delegate(object source, EventArgs args) { aborted = true; connectCompletedEvent.Set(); }; openException = null; // clear out the open exception from the last Open attempt TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); try { NeighborConnected += connectedHandler; Aborted += abortHandler; OpenCore(timeout); if (waitForOnline) { if (!TimeoutHelper.WaitOne(connectCompletedEvent, timeoutHelper.RemainingTime())) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException()); } } if (aborted) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationObjectAbortedException(SR.GetString(SR.PeerNodeAborted))); } // retrieve listen addresses and register with the resolver if (isOpen) { if (openException != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(openException); } else { string lclMeshId = null; PeerNodeConfig lclConfig = null; lock (ThisLock) { lclMeshId = meshId; lclConfig = config; } // The design is such that any address change notifications are queued behind Open operation // So, we need not worry about address changes racing with the initial registration. RegisterAddress(lclMeshId, lclConfig.GetListenAddress(false), timeoutHelper.RemainingTime()); } } } catch (Exception e) { if (Fx.IsFatal(e)) throw; CloseCore(TimeSpan.FromTicks(0), false); throw; } finally { NeighborConnected -= connectedHandler; Aborted -= abortHandler; } } internal void Open(TimeSpan timeout, bool waitForOnline) { stateManager.Open(timeout, waitForOnline); } // the core functionality of open (all but waiting for a connection) void OpenCore(TimeSpan timeout) { TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); PeerMaintainer lclMaintainer; PeerNodeConfig lclConfig; string lclMeshId; lock (ThisLock) { if (ListenUri == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ListenUriNotSet, this.GetType()))); } // extract mesh id from listen uri meshId = ListenUri.Host; // generate the node id byte[] bytes = new byte[sizeof(ulong)]; ulong nodeId = 0; do { System.ServiceModel.Security.CryptoHelper.FillRandomBytes(bytes); for (int i = 0; i < sizeof(ulong); i++) nodeId |= ((ulong)bytes[i]) << i * 8; } while (nodeId == PeerTransportConstants.InvalidNodeId); // now that the node id has been generated, create the trace record that describes this traceRecord = new PeerNodeTraceRecord(nodeId, meshId); if (DiagnosticUtility.ShouldTraceInformation) { TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.PeerNodeOpening, SR.GetString(SR.TraceCodePeerNodeOpening), this.traceRecord, this, null); } // create the node configuration config = new PeerNodeConfig(meshId, nodeId, resolver, messagePropagationFilter, encoder, ListenUri, listenIPAddress, port, maxReceivedMessageSize, minNeighbors, idealNeighbors, maxNeighbors, maxReferrals, connectTimeout, maintainerInterval, securityManager, this.readerQuotas, this.maxBufferPoolSize, this.MaxSendQueue, this.MaxReceiveQueue); // create components if (listenIPAddress != null) ipHelper = new PeerIPHelper(listenIPAddress); else ipHelper = new PeerIPHelper(); bufferManager = BufferManager.CreateBufferManager(64 * config.MaxReceivedMessageSize, (int)config.MaxReceivedMessageSize); neighborManager = new PeerNeighborManager(ipHelper, config, this); flooder = PeerFlooder.CreateFlooder(config, neighborManager, this); maintainer = new PeerMaintainer(config, neighborManager, flooder); connector = new PeerConnector(config, neighborManager, maintainer); Dictionary services = serviceHandlers; if (services == null) { services = new Dictionary(); services.Add(typeof(IPeerConnectorContract), connector); services.Add(typeof(IPeerFlooderContract), flooder); } service = new PeerService(this.config, neighborManager.ProcessIncomingChannel, neighborManager.GetNeighborFromProxy, services, this); this.securityManager.MeshId = this.meshId; service.Open(timeoutHelper.RemainingTime()); // register for events neighborManager.NeighborClosed += new EventHandler(OnNeighborClosed); neighborManager.NeighborClosing += new EventHandler(OnNeighborClosing); neighborManager.NeighborConnected += new EventHandler(OnNeighborConnected); neighborManager.NeighborOpened += new EventHandler(this.SecurityManager.OnNeighborOpened); this.securityManager.OnNeighborAuthenticated += new EventHandler(this.OnNeighborAuthenticated); neighborManager.Online += new EventHandler(FireOnline); neighborManager.Offline += new EventHandler(FireOffline); ipHelper.AddressChanged += new EventHandler(stateManager.OnIPAddressesChanged); // open components ipHelper.Open(); // Set the listen address before opening any more components PeerNodeAddress nodeAddress = new PeerNodeAddress(service.GetListenAddress(), ipHelper.GetLocalAddresses()); config.SetListenAddress(nodeAddress); neighborManager.Open(service.Binding, service); connector.Open(); maintainer.Open(); flooder.Open(); isOpen = true; completeTraceRecord = new PeerNodeTraceRecord(nodeId, meshId, nodeAddress); // Set these locals inside the lock (Abort may occur whilst Opening) lclMaintainer = maintainer; lclMeshId = meshId; lclConfig = config; openException = null; } // retrieve listen addresses and register with the resolver if (isOpen) { // attempt to connect to the mesh lclMaintainer.ScheduleConnect(new PeerMaintainer.ConnectCallback(OnConnectionAttemptCompleted)); } } void DeliverMessageToClientChannels( object registrant, MessageBuffer messageBuffer, Uri via, Uri peerTo, string contentType, int messageSize, int index, MessageHeader hopHeader) { Message message = null; try { // create a list of callbacks so they can each be called outside the lock ArrayList callbacks = new ArrayList(); Uri to = peerTo; Fx.Assert(peerTo != null, "Invalid To header value!"); if (isOpen) { lock (ThisLock) { if (isOpen) { foreach (MessageFilterRegistration mfr in messageFilters.Values) { // first, the via's must match bool match = CompareVia(via, mfr.via); if (messageSize < 0) { //messageSize <0 indicates that this message is coming from BeginSend //and the size is not computed yet. if (message == null) { message = messageBuffer.CreateMessage(); Fx.Assert(message.Headers.To == to, "To Header is inconsistent in Send() case!"); Fx.Assert(message.Properties.Via == via, "Via property is inconsistent in Send() case!"); } //incoming message need not be verified MaxReceivedSize //only do this for local channels if (registrant != null) { ArraySegment buffer = encoder.WriteMessage(message, int.MaxValue, bufferManager); messageSize = (int)buffer.Count; } } // only queue the message for registrants expecting this size match = match && (messageSize <= mfr.settings.MaxReceivedMessageSize); // if a filter is specified, it must match as well if (match && mfr.filters != null) { for (int i = 0; match && i < mfr.filters.Length; i++) { match = mfr.filters[i].Match(via, to); } } if (match) { callbacks.Add(mfr.callback); } } } } } foreach (MessageAvailableCallback callback in callbacks) { Message localCopy; try { //this copy is free'd by SFx. localCopy = messageBuffer.CreateMessage(); localCopy.Properties.Via = via; localCopy.Headers.To = to; //mark security header as understood. try { int i = localCopy.Headers.FindHeader(SecurityJan2004Strings.Security, SecurityJan2004Strings.Namespace); if (i >= 0) { localCopy.Headers.AddUnderstood(i); } } catch (MessageHeaderException e) { DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning); } catch (SerializationException e) { DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning); } catch (XmlException e) { DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning); } if (index != -1) { localCopy.Headers.ReplaceAt(index, hopHeader); } callback(localCopy); } catch (ObjectDisposedException e) { DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); } catch (CommunicationObjectAbortedException e) { DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); } catch (CommunicationObjectFaultedException e) { DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); } } } finally { if (message != null) message.Close(); } } public void RefreshConnection() { PeerMaintainer lclMaintainer = null; lock (ThisLock) { ThrowIfNotOpen(); lclMaintainer = maintainer; } if (lclMaintainer != null) { lclMaintainer.RefreshConnection(); } } public void PingConnections() { PeerMaintainer lclMaintainer = null; lock (ThisLock) { lclMaintainer = maintainer; } if (lclMaintainer != null) { lclMaintainer.PingConnections(); } } //always call methods from inside a lock (of the container) class RefCountedSecurityProtocol { int refCount; public SecurityProtocol Protocol; public RefCountedSecurityProtocol(SecurityProtocol securityProtocol) { this.Protocol = securityProtocol; this.refCount = 1; } public int AddRef() { return ++refCount; } public int Release() { return --refCount; } } // internal message filtering internal void RegisterMessageFilter(object registrant, Uri via, PeerMessageFilter[] filters, ITransportFactorySettings settings, MessageAvailableCallback callback, SecurityProtocol securityProtocol) { MessageFilterRegistration registration = new MessageFilterRegistration(); registration.registrant = registrant; registration.via = via; registration.filters = filters; registration.settings = settings; registration.callback = callback; registration.securityProtocol = securityProtocol; lock (ThisLock) { messageFilters.Add(registrant, registration); RefCountedSecurityProtocol protocolWrapper = null; if (!this.uri2SecurityProtocol.TryGetValue(via, out protocolWrapper)) { protocolWrapper = new RefCountedSecurityProtocol(securityProtocol); this.uri2SecurityProtocol.Add(via, protocolWrapper); } else protocolWrapper.AddRef(); } } // internal method to release the reference on an existing PeerNode internal void Release() { lock (peerNodes) { if (peerNodes.ContainsValue(this)) { if (--refCount == 0) { // no factories/channels are using this instance (although the application may still be // referring to it directly). either way, we remove this from the registry peerNodes.Remove(listenUri); } } } } // Call with null to reset to our implementation public void SetServiceHandlers(Dictionary services) { lock (ThisLock) { serviceHandlers = services; } } void ThrowIfNotOpen() { if (!isOpen) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.TransportManagerNotOpen))); } } void ThrowIfOpen() { if (isOpen) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString( SR.TransportManagerOpen))); } } public override string ToString() { lock (ThisLock) { // if open return the mesh id, otherwise return the type if (isOpen) return string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} ({1})", MeshId, NodeId); else return this.GetType().ToString(); } } internal void UnregisterMessageFilter(object registrant, Uri via) { lock (ThisLock) { messageFilters.Remove(registrant); RefCountedSecurityProtocol protocolWrapper = null; if (uri2SecurityProtocol.TryGetValue(via, out protocolWrapper)) { if (protocolWrapper.Release() == 0) uri2SecurityProtocol.Remove(via); } else Fx.Assert(false, "Corresponding SecurityProtocol is not Found!"); } } internal static void ValidateVia(Uri uri) { int viaSize = Encoding.UTF8.GetByteCount(uri.OriginalString); if (viaSize > maxViaSize) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataException(SR.GetString( SR.PeerChannelViaTooLong, uri, viaSize, maxViaSize))); } internal class ChannelRegistration { public object registrant; public Uri via; public ITransportFactorySettings settings; public SecurityProtocol securityProtocol; public Type channelType; } // holds the registration information passed in by channels and listeners. This informtaion is used // to determine which channels and listeners will receive an incoming message class MessageFilterRegistration : ChannelRegistration { public PeerMessageFilter[] filters; public MessageAvailableCallback callback; } // represents the settings of a PeerListenerFactory or PeerChannelFactory, used to create a new // PeerNode or compare settings to an existing PeerNode internal class Registration { IPAddress listenIPAddress; Uri listenUri; long maxReceivedMessageSize; int port; PeerResolver resolver; PeerSecurityManager securityManager; XmlDictionaryReaderQuotas readerQuotas; long maxBufferPoolSize; public Registration(Uri listenUri, IPeerFactory factory) { if (factory.Resolver == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new InvalidOperationException(SR.GetString(SR.PeerResolverRequired))); } if (factory.ListenIPAddress != null) { listenIPAddress = factory.ListenIPAddress; } this.listenUri = new UriBuilder(PeerStrings.Scheme, listenUri.Host).Uri; this.port = factory.Port; this.maxReceivedMessageSize = factory.MaxReceivedMessageSize; this.resolver = factory.Resolver; this.securityManager = factory.SecurityManager; this.readerQuotas = new XmlDictionaryReaderQuotas(); factory.ReaderQuotas.CopyTo(this.readerQuotas); this.maxBufferPoolSize = factory.MaxBufferPoolSize; } bool HasMismatchedReaderQuotas(XmlDictionaryReaderQuotas existingOne, XmlDictionaryReaderQuotas newOne, out string result) { //check for properties that affect the message result = null; if (existingOne.MaxArrayLength != newOne.MaxArrayLength) result = PeerBindingPropertyNames.ReaderQuotasDotArrayLength; else if (existingOne.MaxStringContentLength != newOne.MaxStringContentLength) result = PeerBindingPropertyNames.ReaderQuotasDotStringLength; else if (existingOne.MaxDepth != newOne.MaxDepth) result = PeerBindingPropertyNames.ReaderQuotasDotMaxDepth; else if (existingOne.MaxNameTableCharCount != newOne.MaxNameTableCharCount) result = PeerBindingPropertyNames.ReaderQuotasDotMaxCharCount; else if (existingOne.MaxBytesPerRead != newOne.MaxBytesPerRead) result = PeerBindingPropertyNames.ReaderQuotasDotMaxBytesPerRead; return result != null; } public void CheckIfCompatible(PeerNodeImplementation peerNode, Uri via) { string mismatch = null; // test the settings that must be identical if (listenUri != peerNode.ListenUri) mismatch = PeerBindingPropertyNames.ListenUri; else if (port != peerNode.Port) mismatch = PeerBindingPropertyNames.Port; else if (maxReceivedMessageSize != peerNode.MaxReceivedMessageSize) mismatch = PeerBindingPropertyNames.MaxReceivedMessageSize; else if (maxBufferPoolSize != peerNode.MaxBufferPoolSize) mismatch = PeerBindingPropertyNames.MaxBufferPoolSize; else if (HasMismatchedReaderQuotas(peerNode.ReaderQuotas, readerQuotas, out mismatch)) { } else if (resolver.GetType() != peerNode.Resolver.GetType()) mismatch = PeerBindingPropertyNames.Resolver; else if (!resolver.Equals(peerNode.Resolver)) mismatch = PeerBindingPropertyNames.ResolverSettings; else if (listenIPAddress != peerNode.ListenIPAddress) { if ((listenIPAddress == null || peerNode.ListenIPAddress == null) || (!listenIPAddress.Equals(peerNode.ListenIPAddress))) mismatch = PeerBindingPropertyNames.ListenIPAddress; } else if ((securityManager == null) && (peerNode.SecurityManager != null)) mismatch = PeerBindingPropertyNames.Security; if (mismatch != null) PeerExceptionHelper.ThrowInvalidOperation_PeerConflictingPeerNodeSettings(mismatch); securityManager.CheckIfCompatibleNodeSettings(peerNode.SecurityManager); } public PeerNodeImplementation CreatePeerNode() { PeerNodeImplementation peerNode = new PeerNodeImplementation(); peerNode.ListenIPAddress = listenIPAddress; peerNode.ListenUri = listenUri; peerNode.MaxReceivedMessageSize = maxReceivedMessageSize; peerNode.Port = port; peerNode.Resolver = resolver; peerNode.SecurityManager = securityManager; this.readerQuotas.CopyTo(peerNode.readerQuotas); peerNode.MaxBufferPoolSize = maxBufferPoolSize; return peerNode; } } class SendAsyncResult : AsyncResult { bool floodComplete = false; bool localDispatchComplete = false; object thisLock = new object(); object ThisLock { get { return thisLock; } } Exception floodException = null; public SendAsyncResult(AsyncCallback callback, object state) : base(callback, state) { } public void OnFloodComplete(IAsyncResult result) { if (this.floodComplete || this.IsCompleted) return; bool complete = false; lock (this.ThisLock) { if (this.localDispatchComplete) complete = true; this.floodComplete = true; } try { PeerFlooder.EndFloodEncodedMessage(result); } catch (Exception e) { if (Fx.IsFatal(e)) throw; DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); floodException = e; } if (complete) { this.Complete(result.CompletedSynchronously, floodException); } } public void OnLocalDispatchComplete(IAsyncResult result) { SendAsyncResult sr = (SendAsyncResult)result; if (this.localDispatchComplete || this.IsCompleted) return; bool complete = false; lock (this.ThisLock) { if (this.floodComplete) complete = true; this.localDispatchComplete = true; } if (complete) { this.Complete(true, floodException); } } public static void End(IAsyncResult result) { AsyncResult.End(result); } } bool IPeerNodeMessageHandling.HasMessagePropagation { get { return this.messagePropagationFilter != null; } } bool IPeerNodeMessageHandling.IsKnownVia(Uri via) { bool result = false; lock (ThisLock) { result = uri2SecurityProtocol.ContainsKey(via); } return result; } bool IPeerNodeMessageHandling.IsNotSeenBefore(Message message, out byte[] id, out int cacheMiss) { PeerFlooder lclFlooder = flooder; id = DefaultId; cacheMiss = -1; return (lclFlooder != null && lclFlooder.IsNotSeenBefore(message, out id, out cacheMiss)); } public MessageEncodingBindingElement EncodingBindingElement { get { return this.EncodingElement; } } } interface IPeerNodeMessageHandling { void HandleIncomingMessage(MessageBuffer messageBuffer, PeerMessagePropagation propagateFlags, int index, MessageHeader header, Uri via, Uri to); PeerMessagePropagation DetermineMessagePropagation(Message message, PeerMessageOrigination origination); bool HasMessagePropagation { get; } bool ValidateIncomingMessage(ref Message data, Uri via); bool IsKnownVia(Uri via); bool IsNotSeenBefore(Message message, out byte[] id, out int cacheMiss); MessageEncodingBindingElement EncodingBindingElement { get; } } }