Esempio n. 1
0
        /// <summary>
        /// Handles the client communication
        /// </summary>
        /// <param name="client">The client.</param>
        private void HandleClientComm(object client)
        {
            KeePassRPCClientConnection keePassRPCClient = null;
            TcpClient     tcpClient    = null;
            NetworkStream clientStream = null;
            SslStream     sslStream    = null;

            if (KeePassRPCPlugin.logger != null)
            {
                KeePassRPCPlugin.logger.WriteLine("HandleClientComm: ");
            }

            if (_useSSL)
            {
                // A client has connected. Create the
                // SslStream using the client's network stream.
                ServicePointManager.ServerCertificateValidationCallback = delegate { return(true); };
                sslStream = new SslStream(((TcpClient)client).GetStream(), false);//, new RemoteCertificateValidationCallback (ValidateServerCertificate),
                //    new LocalCertificateSelectionCallback(SelectLocalCertificate)
                //  );
            }
            else
            {
                tcpClient    = (TcpClient)client;
                clientStream = tcpClient.GetStream();
            }

            if (KeePassRPCPlugin.logger != null)
            {
                KeePassRPCPlugin.logger.WriteLine("stream ready to be authenticated");
            }
            try
            {
                if (_useSSL)
                {
                    // Authenticate the server but don't require the client to
                    // authenticate - we've got our own authentication requirements
                    sslStream.AuthenticateAsServer(
                        _serverCertificate, false, SslProtocols.Ssl3 | SslProtocols.Tls, false);
                    if (KeePassRPCPlugin.logger != null)
                    {
                        KeePassRPCPlugin.logger.WriteLine("stream authenticated");
                    }
                    sslStream.ReadTimeout  = -1;
                    sslStream.WriteTimeout = -1;
                }
                else
                {
                    clientStream.ReadTimeout  = -1;
                    clientStream.WriteTimeout = -1;
                }

                byte[] message = new byte[4096];
                int    bytesRead;
                //bool authorised = false;

                //TODO2: creation of this client probably should happen later
                // but we need to know that this connection needs to be closed
                // during shutdown, even if the client never
                // successfully authenticates.
                keePassRPCClient = _useSSL ? new KeePassRPCClientConnection(sslStream, false) : new KeePassRPCClientConnection(tcpClient, false);
                KeePassRPCPlugin.AddRPCClientConnection(keePassRPCClient);

                // send an "invitation to authenticate" to the new RPC client
                keePassRPCClient.Signal(KeePassRPC.DataExchangeModel.Signal.PLEASE_AUTHENTICATE, "KPRPCListener");

                int           tokenCurlyCount        = 0;
                int           tokenSquareCount       = 0;
                int           adjacentBackslashCount = 0;
                bool          parsingStringContents  = false;
                StringBuilder currentJSONPacket      = new StringBuilder(50);

                // Keep reading data from the network stream whenever it's available
                while (true)
                {
                    bytesRead = 0;

                    try
                    {
                        //blocks until a client sends a message
                        bytesRead = _useSSL ? sslStream.Read(message, 0, 4096) : clientStream.Read(message, 0, 4096);
                    }
                    catch (Exception ex)
                    {
                        if (KeePassRPCPlugin.logger != null)
                        {
                            KeePassRPCPlugin.logger.WriteLine("a socket error has occured:" + ex.ToString());
                        }
                        break;
                    }

                    if (bytesRead == 0)
                    {
                        //the client has disconnected from the server
                        break;
                    }

                    // Can we ever receive a partial UTF8 character? if so, this could go wrong, albeit rarely
                    string receivedData         = System.Text.Encoding.UTF8.GetString(message, 0, bytesRead);
                    int    jsonPacketStartIndex = 0;

                    for (int i = 0; i < receivedData.Length; i++)
                    {
                        bool incrementAdjacentBackslashCount = false;

                        // Use the simple structure of JSON-RPC to extract
                        // complete messages from the network stream
                        switch (receivedData[i])
                        {
                        case TOKEN_QUOT: if (adjacentBackslashCount % 2 == 0)
                            {
                                parsingStringContents = parsingStringContents ? false : true;
                            }
                            break;

                        case TOKEN_BACKSLASH: incrementAdjacentBackslashCount = true;
                            break;

                        case TOKEN_CURLY_START: if (!parsingStringContents)
                            {
                                tokenCurlyCount++;
                            }
                            break;

                        case TOKEN_CURLY_END: if (!parsingStringContents)
                            {
                                tokenCurlyCount--;
                            }
                            break;

                        case TOKEN_SQUARE_START: if (!parsingStringContents)
                            {
                                tokenSquareCount++;
                            }
                            break;

                        case TOKEN_SQUARE_END: if (!parsingStringContents)
                            {
                                tokenSquareCount--;
                            }
                            break;
                        }

                        if (incrementAdjacentBackslashCount)
                        {
                            adjacentBackslashCount++;
                        }
                        else
                        {
                            adjacentBackslashCount = 0;
                        }

                        // When both counts are zero, we know we have
                        // reached the end of a JSON-RPC request
                        if (tokenCurlyCount == 0 && tokenSquareCount == 0)
                        {
                            currentJSONPacket.Append(receivedData.Substring(
                                                         jsonPacketStartIndex, i - jsonPacketStartIndex + 1));
                            DispatchToRPCService(currentJSONPacket.ToString(), keePassRPCClient);
                            currentJSONPacket    = new StringBuilder(50);
                            jsonPacketStartIndex = i + 1;
                        }
                    }

                    // If the JSON request is not complete we store what we have already found
                    if (tokenCurlyCount != 0 || tokenSquareCount != 0)
                    {
                        currentJSONPacket.Append(receivedData);
                    }
                    // http://groups.google.com/group/jayrock/browse_thread/thread/59cf6a58bc63f0df/a7775c3097cf6957?lnk=gst&q=thread+JsonRpcDispatcher+#a7775c3097cf6957
                }
            }
            catch (AuthorisationException authEx)
            {
                // Send a JSON message down the pipe
                byte[] bytes = System.Text.Encoding.UTF8.GetBytes(authEx.AsJSONResult());
                keePassRPCClient.ConnectionStreamWrite(bytes);
            }
            catch (AuthenticationException ex)
            {
                if (KeePassRPCPlugin.logger != null)
                {
                    KeePassRPCPlugin.logger.WriteLine("Authentication exception: " + ex.ToString());
                }
                // Nothing we can do about this since client can't
                // receive messages over an invalid network stream
            }
            catch (Exception e)
            {
                if (KeePassRPCPlugin.logger != null)
                {
                    KeePassRPCPlugin.logger.WriteLine("Unknown exception: " + e.ToString());
                }
                //TODO2: send a JSON message down the pipe
                // ex.AsJSONResult();
            }
            finally
            {
                if (KeePassRPCPlugin.logger != null)
                {
                    KeePassRPCPlugin.logger.WriteLine("!!!Hit finally");
                }
                if (keePassRPCClient != null)
                {
                    KeePassRPCPlugin.RemoveRPCClientConnection(keePassRPCClient);
                }
                if (_useSSL)
                {
                    try {
                        sslStream.Close();
                    }
                    catch (IOException ioex)
                    {
                        // This is okay
                    }
                }
                else
                {
                    clientStream.Close();
                }
            }
        }