pkiutilities/untrustedcertificatedialog/tsrc/tlsconntest/tlsconnection.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 31 Aug 2010 16:04:40 +0300
branchRCL_3
changeset 21 09b1ac925e3f
permissions -rw-r--r--
Revision: 201033 Kit: 201035

/*
* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  Secure connections test application
*
*/

#include "tlsconnection.h"
#include <e32debug.h>
#include <SecureSocket.h>
#include <ssl_internal.h>       // KSolInetSSL, KSoSSLDomainName

_LIT( KData, "GET index.html" );
_LIT( KSecureProtocol, "TLS1.0" );
_LIT( KCommDBIapId, "IAP\\Id" );
_LIT8( KNewLine, "\n" );

const TInt KRecvBufferLength = 0x4000;
const TInt KOneSecondInMicroseconds = 1000000;


// ======== MEMBER FUNCTIONS ========

// ---------------------------------------------------------
// CTlsConnection::NewL
// ---------------------------------------------------------
//
CTlsConnection* CTlsConnection::NewL( MTlsConnectionObserver& aObs )
    {
    RDebug::Print( _L("CTlsConnection::NewL()") );
    CTlsConnection* self = new ( ELeave ) CTlsConnection( aObs );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }

// -----------------------------------------------------------------------------
// CTlsConnection::~CTlsConnection()
// -----------------------------------------------------------------------------
//
CTlsConnection::~CTlsConnection()
    {
    RDebug::Print( _L("CTlsConnection::~CTlsConnection()") );
    Cancel();
    iDelayTimer.Close();
    CloseSession();
    delete iSendBuffer;
    delete iSendBufPtr;
    }

// -----------------------------------------------------------------------------
// CTlsConnection::ConnectL()
// -----------------------------------------------------------------------------
//
void CTlsConnection::ConnectL( const TDesC& aHostName, TInt aPort, TInt aDelay )
    {
    RDebug::Print( _L("CTlsConnection::Connect() host '%S', port %d, delay %d"),
        &aHostName, aPort, aDelay );
    CloseConnection();

    // Parameters
    if( iHostName )
        {
        delete iHostName;
        iHostName = NULL;
        }
    iHostName = aHostName.AllocL();
    iPort = aPort;
    iDelay = aDelay;

    // Save host name in 8-bit form for server certificate checking
    if( iHostNameForCertCheck )
        {
        delete iHostNameForCertCheck;
        iHostNameForCertCheck = NULL;
        }
    iHostNameForCertCheck = HBufC8::NewL( aHostName.Length() );
    TPtr8 ptr( iHostNameForCertCheck->Des() );
    ptr.Copy( aHostName );

    // Convert send data to 8-bit and add newline at the end
    if( iSendBuffer || iSendBufPtr )
        {
        delete iSendBuffer;
        iSendBuffer = NULL;
        delete iSendBufPtr;
        iSendBufPtr = NULL;
        }
    iSendBuffer = HBufC8::NewL( KData().Length() + 1 );
    iSendBufPtr = new( ELeave ) TPtr8( iSendBuffer->Des() );
    iSendBufPtr->Copy( KData );
    iSendBufPtr->Append( KNewLine );

    // Connect
    RDebug::Printf( "CTlsConnection::ConnectL iConnection.Open" );
    User::LeaveIfError( iConnection.Open( iSockServer ) );
    RDebug::Printf( "CTlsConnection::ConnectL iConnPref.SetDirection" );
    iConnPref.SetDirection( ECommDbConnectionDirectionOutgoing );
    if( iCurrentIap )
        {
        // uses the same IAP as before
        RDebug::Printf( "CTlsConnection::ConnectL SetIapId(%d)", iCurrentIap );
        iConnPref.SetIapId( iCurrentIap );
        iConnPref.SetDialogPreference( ECommDbDialogPrefDoNotPrompt );
        }
    else
        {
        // not defined, prompts the IAP from user
        RDebug::Printf( "CTlsConnection::ConnectL ECommDbDialogPrefPrompt" );
        iConnPref.SetDialogPreference( ECommDbDialogPrefPrompt );
        }
    StateChange( EConnectingNetwork );
    RDebug::Printf( "CTlsConnection::ConnectL iConnection.Start" );
    iConnection.Start( iConnPref, iStatus );
    SetActive();
    RDebug::Printf( "CTlsConnection::ConnectL end" );
    }

// -----------------------------------------------------------------------------
// CTlsConnection::Disconnect()
// -----------------------------------------------------------------------------
//
void CTlsConnection::Disconnect()
    {
    Cancel();
    CloseConnection();
    StateChange( EDisconnected );
    }

// -----------------------------------------------------------------------------
// CTlsConnection::RunL
// -----------------------------------------------------------------------------
//
void CTlsConnection::RunL()
    {
    if( iState == EReading && ( iStatus.Int() == KErrEof || iStatus.Int() == KErrSSLAlertCloseNotify ) )
        {
        RDebug::Printf( "CTlsConnection::RunL: EReading, EOF: iStatus = %d", iStatus.Int() );
        StateChange( EAllDone );
        }
    else if( iStatus.Int() == KErrNone )
        {
        switch ( iState )
            {
            case EConnectingNetwork:
                RDebug::Printf( "CTlsConnection::RunL: EConnectingNetwork" );
                if( !iCurrentIap )
                    {
                    iConnection.GetIntSetting( KCommDBIapId, iCurrentIap );
                    }
                if( iDelay )
                    {
                    TTimeIntervalMicroSeconds32 delay( iDelay * KOneSecondInMicroseconds );
                    iDelayTimer.After( iStatus, delay );
                    StateChange( EOpeningDelay );
                    }
                else
                    {
                    iConnection.ProgressNotification( iProgress, iStatus );
                    StateChange( EResolvingHostName );
                    }
                SetActive();
                break;

            case EOpeningDelay:
                iConnection.ProgressNotification( iProgress, iStatus );
                StateChange( EResolvingHostName );
                SetActive();
                break;

            case EResolvingHostName:
                {
                RDebug::Printf( "CTlsConnection::RunL: EResolvingHostName" );
                TInt err = iHostResolver.Open( iSockServer, KAfInet, KProtocolInetUdp, iConnection );
                User::LeaveIfError( err );
                iHostResolver.GetByName( *iHostName, iNameEntry, iStatus );
                StateChange( EConnectingServer );
                SetActive();
                }
                break;

            case EConnectingServer:
                {
                iRemoteAddr.SetFamily( KAfInet );
                iRemoteAddr = TInetAddr( iNameEntry().iAddr );
                iRemoteAddr.SetPort( iPort );

                RDebug::Printf( "CTlsConnection::RunL: EConnectingServer" );
                TInt err = iSocket.Open( iSockServer, KAfInet, KSockStream, KProtocolInetTcp, iConnection );
                RDebug::Printf( "CTlsConnection::RunL: iSocket.Open ret %d", err );
                User::LeaveIfError( err );
                iSocket.Connect( iRemoteAddr, iStatus );
                StateChange( EHandshaking );
                SetActive();
                }
                break;

            case EHandshaking:
                {
                RDebug::Printf( "CTlsConnection::RunL: EHandshaking" );
                if( iSecureSocket )
                    {
                    delete iSecureSocket;
                    iSecureSocket = NULL;
                    }
                iSecureSocket = CSecureSocket::NewL( iSocket, KSecureProtocol );
                iSecureSocket->SetDialogMode( EDialogModeAttended );
                // Server name must be set, otherwise CCTSecurityDialogsAO fails with KErrArgument
                iSecureSocket->SetOpt( KSoSSLDomainName, KSolInetSSL, *iHostNameForCertCheck );
                iSecureSocket->StartClientHandshake( iStatus );
                StateChange( EConnecting );
                SetActive();
                }
                break;

            case EConnecting:
                {
                RDebug::Printf( "CTlsConnection::RunL: EConnecting" );
                // Check if connection is closed
                const TInt stage( iProgress().iStage );
                if( stage == KConnectionClosed || stage == KLinkLayerClosed )
                    {
                    StateChange( EDisconnected );
                    }
                else
                    {
                    iConnection.ProgressNotification( iProgress, iStatus );
                    StateChange( ESending );
                    SetActive();
                    }
                }
                break;

            case ESending:
                RDebug::Printf( "CTlsConnection::RunL: ESending" );
                iSecureSocket->Send( *iSendBufPtr, iStatus, iSendLength );
                StateChange( EReading );
                SetActive();
                break;

            case EReading:
                iTotalRecvCount += iRecvLength();
                RDebug::Printf( "CTlsConnection::RunL: EReading, read %d bytes", iTotalRecvCount );
                iObserver.HandleTransferData( iRecvBuffer->Des(), iTotalRecvCount );

                iRecvBufPtr->Zero();
                iSecureSocket->RecvOneOrMore( *iRecvBufPtr, iStatus, iRecvLength );
                SetActive();
                break;

            default:
                break;
            }
        }
    else
        {
        RDebug::Printf( "CTlsConnection::RunL: ERROR iStatus %d", iStatus.Int() );
        CloseConnection();
        StateChange( EDisconnected, iStatus.Int() );
        }
    }

// ---------------------------------------------------------------------------
// CTlsConnection::DoCancel()
// ---------------------------------------------------------------------------
//
void CTlsConnection::DoCancel()
    {
    RDebug::Printf( "CTlsConnection::DoCancel(), iState=%d", iState );

    switch( iState )
        {
        case EConnectingNetwork:
            iConnection.Stop();
            break;

        case EOpeningDelay:
            iDelayTimer.Cancel();
            iConnection.Stop();
            break;

        case EResolvingHostName:
            iConnection.CancelProgressNotification();
            iConnection.Stop();
            break;

        case EConnectingServer:
            iHostResolver.Cancel();
            iConnection.Stop();
            break;

        case EHandshaking:
            iSocket.CancelConnect();
            iConnection.Stop();
            break;

        case EConnecting:
            if( iSecureSocket )
                {
                iSecureSocket->CancelHandshake();
                delete iSecureSocket;
                iSecureSocket = NULL;
                }
            iSocket.Close();
            iConnection.Stop();
            break;

        case ESending:
            if( iSecureSocket )
                {
                iSecureSocket->CancelSend();
                delete iSecureSocket;
                iSecureSocket = NULL;
                }
            iSocket.Close();
            iConnection.Stop();
            break;

        case EReading:
            if( iSecureSocket )
                {
                iSecureSocket->CancelRecv();
                delete iSecureSocket;
                iSecureSocket = NULL;
                }
            iSocket.Close();
            iConnection.Stop();
            break;

        case EAllDone:
        case EIdle:
        case EDisconnected:
        default:
            break;
        }

    StateChange( EDisconnected );
    }


// -----------------------------------------------------------------------------
// CTlsConnection::RunError()
// -----------------------------------------------------------------------------
//
TInt CTlsConnection::RunError( TInt aError )
    {
    RDebug::Printf( "CTlsConnection::RunError, aError: %d", aError );
    CloseConnection();
    StateChange( EDisconnected, aError );
    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CTlsConnection::CTlsConnection()
// -----------------------------------------------------------------------------
//
CTlsConnection::CTlsConnection( MTlsConnectionObserver& aObs ) :
        CActive( CActive::EPriorityStandard ), iObserver( aObs ), iState( ENotInitialized )
    {
    CActiveScheduler::Add( this );
    }

// -----------------------------------------------------------------------------
// CTlsConnection::ConstructL()
// -----------------------------------------------------------------------------
//
void CTlsConnection::ConstructL()
    {
    RDebug::Printf( "CTlsConnection::ConstructL begin" );

    User::LeaveIfError( iDelayTimer.CreateLocal() );
    RDebug::Printf( "CTlsConnection::ConstructL iSockServer" );
    User::LeaveIfError( iSockServer.Connect() );
    RDebug::Printf( "CTlsConnection::ConstructL iConnection" );
    User::LeaveIfError( iConnection.Open( iSockServer ) );
    RDebug::Printf( "CTlsConnection::ConstructL buffers" );
    iRecvBuffer = HBufC8::NewL( KRecvBufferLength );
    iRecvBufPtr = new( ELeave ) TPtr8( iRecvBuffer->Des() );
    StateChange( EIdle );

    RDebug::Printf( "CTlsConnection::ConstructL end" );
    }

// ---------------------------------------------------------------------------
// CTlsConnection::CloseConnection()
// ---------------------------------------------------------------------------
//
void CTlsConnection::CloseConnection()
    {
    RDebug::Printf( "CTlsConnection::CloseConnection begin" );
    if( iSecureSocket )
        {
        delete iSecureSocket;
        iSecureSocket = NULL;
        }
    iSocket.Close();
    iHostResolver.Close();
    if( iConnection.SubSessionHandle() )
        {
        if( iState >= EConnectingNetwork )
            {
            TInt err = iConnection.Stop();
            if( err != KErrNone )
                {
                RDebug::Printf( "iConnection.Stop() failed: %d", err );
                }
            }
        iConnection.Close();
        }
    RDebug::Printf( "CTlsConnection::CloseConnection end" );
    }

// ---------------------------------------------------------------------------
// CTlsConnection::CloseSession()
// ---------------------------------------------------------------------------
//
void CTlsConnection::CloseSession()
    {
    RDebug::Printf( "CTlsConnection::CloseSession" );
    CloseConnection();
    iSockServer.Close();
    delete iHostName;
    iHostName = NULL;
    delete iHostNameForCertCheck;
    iHostNameForCertCheck = NULL;
    delete iRecvBuffer;
    iRecvBuffer = NULL;
    delete iRecvBufPtr;
    iRecvBufPtr = NULL;
    StateChange( EDisconnected );
    }

// -----------------------------------------------------------------------------
// CTlsConnection::StateChange()
// -----------------------------------------------------------------------------
//
void CTlsConnection::StateChange( TTlsConnectionState aNewState, TInt aError )
    {
    RDebug::Printf( "CTlsConnection::StateChange, aNewState=%d, aError=%d", aNewState, aError );
    if( aNewState != iState && iState != EAllDone )
        {
        iState = aNewState;
        iObserver.HandleNetworkEvent( aNewState, aError );
        }
    }

// ---------------------------------------------------------------------------
// CTlsConnection::ReadL()
// ---------------------------------------------------------------------------
//
void CTlsConnection::ReadL()
    {
    RDebug::Printf( "CTlsConnection::ReadL(), iState=%d", iState );

    TPtr8 recvBuffer = iRecvBuffer->Des();
    recvBuffer.Zero();
    iSecureSocket->RecvOneOrMore( recvBuffer, iStatus, iRecvLength );
    SetActive();
    }