/// <summary> /// Attaches the channel to an existing socket. /// </summary> public void Attach(uint channelId, Socket socket) { if (socket == null) { throw new ArgumentNullException(nameof(socket)); } lock (DataLock) { // check for existing socket. if (Socket != null) { throw new InvalidOperationException("Channel is already attached to a socket."); } ChannelId = channelId; State = TcpChannelState.Connecting; Socket = new TcpMessageSocket(this, socket, BufferManager, Quotas.MaxBufferSize); Utils.Trace("{0} SOCKET ATTACHED: {1:X8}, ChannelId={2}", ChannelName, Socket.Handle, ChannelId); Socket.ReadNextMessage(); // automatically clean up the channel if no hello received. StartCleanupTimer(StatusCodes.BadTimeout); } }
/// <summary> /// Handles a receive error. /// </summary> public virtual void OnReceiveError(TcpMessageSocket source, ServiceResult result) { lock (DataLock) { HandleSocketError(result); } }
/// <summary> /// Handles a reconnect request. /// </summary> public void Reconnect( TcpMessageSocket socket, uint requestId, uint sequenceNumber, X509Certificate2 clientCertificate, TcpChannelToken token, OpenSecureChannelRequest request) { if (socket == null) { throw new ArgumentNullException("socket"); } lock (DataLock) { // make sure the same client certificate is being used. CompareCertificates(ClientCertificate, clientCertificate, false); // check for replay attacks. if (!VerifySequenceNumber(sequenceNumber, "Reconnect")) { throw new ServiceResultException(StatusCodes.BadSequenceNumberInvalid); } try { // replace the socket. Socket = socket; Utils.Trace("TCPSERVERCHANNEL SOCKET RECONNECTED: {0:X8}, ChannelId={1}", Socket.Handle, ChannelId); Socket.ChangeSink(this); // need to assign a new token id. token.TokenId = GetNewTokenId(); // put channel back in open state. ActivateToken(token); State = TcpChannelState.Open; // no need to cleanup. if (m_cleanupTimer != null) { m_cleanupTimer.Dispose(); m_cleanupTimer = null; } // send response. SendOpenSecureChannelResponse(requestId, token, request); // send any queue responses. ThreadPool.QueueUserWorkItem(new WaitCallback(OnChannelReconnected), m_queuedResponses); m_queuedResponses = new SortedDictionary <uint, IServiceResponse>(); } catch (Exception e) { SendServiceFault(token, requestId, ServiceResult.Create(e, StatusCodes.BadTcpInternalError, "Unexpected error processing request.")); } } }
/// <summary> /// Processes an incoming message. /// </summary> public virtual void OnMessageReceived(TcpMessageSocket source, ArraySegment <byte> message) { lock (DataLock) { try { uint messageType = BitConverter.ToUInt32(message.Array, message.Offset); // Utils.Trace("{1} Message Received: {0} bytes", messageChunk.Count, messageType); if (!HandleIncomingMessage(messageType, message)) { BufferManager.ReturnBuffer(message.Array, "OnMessageReceived"); } } catch (Exception e) { HandleMessageProcessingError(e, StatusCodes.BadTcpInternalError, "An error occurred receiving a message."); BufferManager.ReturnBuffer(message.Array, "OnMessageReceived"); } } }
/// <summary> /// Handles requests arriving from a channel. /// </summary> private void OnRequestReceived(TcpServerChannel channel, uint requestId, IServiceRequest request) { // HONEYPOT - obtain the remote IP address and port if possible TcpMessageSocket tcpSocket = channel.Socket as TcpMessageSocket; IPAddress remoteIP = null; int remotePort = 0; if (tcpSocket != null) { EndPoint remoteEndpoint = tcpSocket.getRemoteEndpoint(); if (remoteEndpoint != null) { IPEndPoint ipEndpoint = remoteEndpoint as IPEndPoint; if (ipEndpoint != null) { remoteIP = ipEndpoint.Address; remotePort = ipEndpoint.Port; } } } try { if (m_callback != null) { IAsyncResult result = m_callback.BeginProcessRequest( channel.GlobalChannelId, channel.EndpointDescription, request, OnProcessRequestComplete, new object[] { channel, requestId, request }, remoteIP, // HONEYPOT remotePort); // HONEYPOT } } catch (Exception e) { Utils.Trace(e, "TCPLISTENER - Unexpected error processing request."); } }
/// <summary> /// Binds a new socket to an existing channel. /// </summary> internal bool ReconnectToExistingChannel( TcpMessageSocket socket, uint requestId, uint sequenceNumber, uint channelId, X509Certificate2 clientCertificate, TcpChannelToken token, OpenSecureChannelRequest request) { TcpServerChannel channel = null; lock (m_lock) { if (!m_channels.TryGetValue(channelId, out channel)) { throw ServiceResultException.Create(StatusCodes.BadTcpSecureChannelUnknown, "Could not find secure channel referenced in the OpenSecureChannel request."); } } channel.Reconnect(socket, requestId, sequenceNumber, clientCertificate, token, request); return(true); }
/// <summary> /// Attaches the channel to an existing socket. /// </summary> public void Attach(uint channelId, Socket socket) { if (socket == null) throw new ArgumentNullException("socket"); lock (DataLock) { // check for existing socket. if (Socket != null) { throw new InvalidOperationException("Channel is already attached to a socket."); } ChannelId = channelId; State = TcpChannelState.Connecting; Socket = new TcpMessageSocket(this, socket, BufferManager, Quotas.MaxBufferSize); Utils.Trace("TCPSERVERCHANNEL SOCKET ATTACHED: {0:X8}, ChannelId={1}", Socket.Handle, ChannelId); Socket.ReadNextMessage(); // automatically clean up the channel if no hello recieved. StartCleanupTimer(StatusCodes.BadTimeout); } }
/// <summary> /// Handles a reconnect request. /// </summary> public void Reconnect( TcpMessageSocket socket, uint requestId, uint sequenceNumber, X509Certificate2 clientCertificate, TcpChannelToken token, OpenSecureChannelRequest request) { if (socket == null) throw new ArgumentNullException("socket"); lock (DataLock) { // make sure the same client certificate is being used. CompareCertificates(ClientCertificate, clientCertificate, false); // check for replay attacks. if (!VerifySequenceNumber(sequenceNumber, "Reconnect")) { throw new ServiceResultException(StatusCodes.BadSequenceNumberInvalid); } try { // replace the socket. Socket = socket; Utils.Trace("TCPSERVERCHANNEL SOCKET RECONNECTED: {0:X8}, ChannelId={1}", Socket.Handle, ChannelId); Socket.ChangeSink(this); // need to assign a new token id. token.TokenId = GetNewTokenId(); // put channel back in open state. ActivateToken(token); State = TcpChannelState.Open; // no need to cleanup. if (m_cleanupTimer != null) { m_cleanupTimer.Dispose(); m_cleanupTimer = null; } // send response. SendOpenSecureChannelResponse(requestId, token, request); // send any queue responses. ThreadPool.QueueUserWorkItem(new WaitCallback(OnChannelReconnected), m_queuedResponses); m_queuedResponses = new SortedDictionary<uint,IServiceResponse>(); } catch (Exception e) { SendServiceFault(token, requestId, ServiceResult.Create(e, StatusCodes.BadTcpInternalError, "Unexpected error processing request.")); } } }
/// <summary> /// Called when it is time to do a handshake. /// </summary> private void OnScheduledHandshake(object state) { try { // Utils.Trace("Channel {0}: Scheduled Handshake Starting: TokenId={1}", ChannelId, CurrentToken.TokenId); lock (DataLock) { // check if renewing a token. TcpChannelToken token = state as TcpChannelToken; if (token == CurrentToken) { Utils.Trace("TCP CHANNEL {0}: Attempting Renew Token Now: TokenId={1}", ChannelId, token.TokenId); // do nothing if not connected. if (State != TcpChannelState.Open) { return; } // begin the operation. m_handshakeOperation = BeginOperation(Int32.MaxValue, m_HandshakeComplete, token); // send the request. SendOpenSecureChannelRequest(true); return; } // must be reconnecting - check if successfully reconnected. if (!m_reconnecting) { return; } Utils.Trace("Channel {0}: Attempting Reconnect Now.", ChannelId); // cancel any previous attempt. if (m_handshakeOperation != null) { m_handshakeOperation.Fault(StatusCodes.BadTimeout); m_handshakeOperation = null; } // close the socket and reconnect. State = TcpChannelState.Closed; if (Socket != null) { Utils.Trace("TCPCLIENTCHANNEL SOCKET CLOSED: {0:X8}, ChannelId={1}", Socket.Handle, ChannelId); Socket.Close(); Socket = null; } // create an operation. m_handshakeOperation = BeginOperation(Int32.MaxValue, m_HandshakeComplete, null); State = TcpChannelState.Connecting; Socket = new TcpMessageSocket(this, BufferManager, Quotas.MaxBufferSize); Socket.BeginConnect(m_via, m_ConnectCallback, m_handshakeOperation); } } catch (Exception e) { Utils.Trace("Channel {0}: Reconnect Failed {1}.", ChannelId, e.Message); ForceReconnect(ServiceResult.Create(e, StatusCodes.BadUnexpectedError, "Unexpected error reconnecting or renewing a token.")); } }
/// <summary> /// Creates a connection with the server. /// </summary> public IAsyncResult BeginConnect(Uri url, int timeout, AsyncCallback callback, object state) { if (url == null) throw new ArgumentNullException("url"); if (timeout <= 0) throw new ArgumentException("Timeout must be greater than zero.", "timeout"); lock (DataLock) { if (State != TcpChannelState.Closed) { throw new InvalidOperationException("Channel is already connected."); } m_url = url; m_via = url; // check if configured to use a proxy. if (EndpointDescription != null && EndpointDescription.ProxyUrl != null) { m_via = EndpointDescription.ProxyUrl; } // do not attempt reconnect on failure. m_waitBetweenReconnects = Timeout.Infinite; WriteOperation operation = BeginOperation(timeout, callback, state); m_handshakeOperation = operation; State = TcpChannelState.Connecting; Socket = new TcpMessageSocket(this, BufferManager, Quotas.MaxBufferSize); try { Socket.BeginConnect(m_via, m_ConnectCallback, operation); } catch (SocketException e) { Shutdown(StatusCodes.Bad); throw e; } return operation; } }
/// <summary> /// Binds a new socket to an existing channel. /// </summary> internal bool ReconnectToExistingChannel( TcpMessageSocket socket, uint requestId, uint sequenceNumber, uint channelId, X509Certificate2 clientCertificate, TcpChannelToken token, OpenSecureChannelRequest request) { TcpServerChannel channel = null; lock (m_lock) { if (!m_channels.TryGetValue(channelId, out channel)) { throw ServiceResultException.Create(StatusCodes.BadTcpSecureChannelUnknown, "Could not find secure channel referenced in the OpenSecureChannel request."); } } channel.Reconnect(socket, requestId, sequenceNumber, clientCertificate, token, request); // Utils.Trace("Channel {0} reconnected", channelId); return true; }