Handles reading and writing of message chunks over a socket.
상속: IDisposable
예제 #1
0
        /// <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);
            }
        }
예제 #2
0
 /// <summary>
 /// Handles a receive error.
 /// </summary>
 public virtual void OnReceiveError(TcpMessageSocket source, ServiceResult result)
 {
     lock (DataLock)
     {
         HandleSocketError(result);
     }
 }
예제 #3
0
        /// <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."));
                }
            }
        }
예제 #4
0
        /// <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");
                }
            }
        }
예제 #5
0
        /// <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.");
            }
        }
예제 #6
0
        /// <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);
        }
예제 #7
0
        /// <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);
            }
        }  
예제 #8
0
        /// <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."));
                }
            }
        }
예제 #9
0
        /// <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."));
            }
        }
예제 #10
0
        /// <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;
            }
        }
예제 #11
0
        /// <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;
        }