/*
* Copyright (c) 2002-2006 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: 
* Security policy database module. 
* "Runtime" methods of "CSecurityPolicy" class.
*
*/


#include <networking/pfkeyv2.h>
#include "spdb.h"
#include "logvpncommon.h"

//
//  CPolicySelector
//
CPolicySelector::CPolicySelector()
    {}

EXPORT_C CPolicySelector*
CPolicySelector::NewL()
    {
    CPolicySelector* self = new (ELeave) CPolicySelector();
    self->Construct();
    return self;
    }

EXPORT_C void
CPolicySelector::Construct()
    {
    iIcmpType = -1;           // used, if != -1
    iType = -1;               // used, if != -1
    iIcmpCode = -1;           // used, if != -1
    iGlobalSelector = EFalse; // the global flag defaults to false
    iIsFinal = EFalse;
    iIsMerge = EFalse;
    iDirection = KPolicySelector_SYMMETRIC;
    iDropAction = EFalse;
    iProtocol = 0;

    // Init addresses to undefined    
    iLocal.SetFamily(KAFUnspec);
    iLocalMask.SetFamily(KAFUnspec);
    iRemote.SetFamily(KAFUnspec);
    iRemoteMask.SetFamily(KAFUnspec);
    
    // Clear interface name
    iInterface.Zero();
    
    // Init Endpoint names to NULL
    iLocSelEpName = NULL;
    iLocMaskEpName = NULL;
    iRemSelEpName = NULL;
    iRemMaskEpName = NULL;
    
    iSequenceNumber = 0;
    iCompWord = 0;
    }

EXPORT_C CPolicySelector*
CPolicySelector::NewL(CPolicySelector* aPS)
    {
    CPolicySelector* self = new (ELeave) CPolicySelector();
    CleanupStack::PushL(self);
    self->ConstructL(aPS);
    CleanupStack::Pop();
    return self;
    }

EXPORT_C void
CPolicySelector::ConstructL(CPolicySelector* aPS)
    {
    iDirection = aPS->iDirection;
    iRemote = aPS->iRemote;         // including port selector, if port non-zero
    iRemoteMask = aPS->iRemoteMask; // only address part used, as a mask
    iLocal = aPS->iLocal;           // including port selector, if port non-zero
    iLocalMask = aPS->iLocalMask;   // only address part used, as a mask
    iProtocol = aPS->iProtocol;     // used, if non-zero
    iIcmpType = aPS->iIcmpType;     // used, if != -1
    iType = aPS->iType;             // used, if != -1
    iIcmpCode = aPS->iIcmpCode;     // used, if != -1
    iGlobalSelector = aPS->iGlobalSelector;
    iIsFinal = aPS->iIsFinal;
    iIsMerge = aPS->iIsMerge;
    iDropAction = aPS->iDropAction;
    iInterface = aPS->iInterface;
    
    iSequenceNumber = aPS->iSequenceNumber;
    iCompWord = aPS->iSequenceNumber;

    // Copy EndPoint names    
    if (aPS->iLocSelEpName && aPS->iLocSelEpName->Length())
        {
        iLocSelEpName = HBufC8::NewL(aPS->iLocSelEpName->Length());
        *iLocSelEpName = *aPS->iLocSelEpName;
        }
    if (aPS->iLocMaskEpName && aPS->iLocMaskEpName->Length())
        {
        iLocMaskEpName = HBufC8::NewL(aPS->iLocMaskEpName->Length());
        *iLocMaskEpName = *aPS->iLocMaskEpName;
        }
    if (aPS->iRemSelEpName && aPS->iRemSelEpName->Length())
        {
        iRemSelEpName = HBufC8::NewL(aPS->iRemSelEpName->Length());
        *iRemSelEpName = *aPS->iRemSelEpName;
        }
    if (aPS->iRemMaskEpName && aPS->iRemMaskEpName->Length())
        {
        iRemMaskEpName = HBufC8::NewL(aPS->iRemMaskEpName->Length());
        *iRemMaskEpName = *aPS->iRemMaskEpName;
        }

    TSecpolBundleIter iterl(aPS->iBundle);
    CSecpolBundleItem* itemL(NULL);
    CSecpolBundleItem* newItemL(NULL);
    while ((itemL = iterl++) != NULL)
        {
        newItemL = new (ELeave) CSecpolBundleItem;
        
        // Points to the same SA
        newItemL->iSpec = itemL->iSpec;      
        
        // No need to fill iNext. Is filled when adding
        newItemL->iTunnel = itemL->iTunnel;  
        iBundle.AddLast(*newItemL);
        }

    iNext = aPS->iNext;
    }

CSecpolBundleItem*
CPolicySelector::FindBundleL(TInt aIndex)
    {
    TSecpolBundleIter iterL(iBundle);
    CSecpolBundleItem* itemL(NULL);
    CSecpolBundleItem* newItemL(NULL);
    TInt i = 0;
    while (((itemL = iterL++) != NULL) && (i < aIndex))
        {
        i++;
        }

    // The element exists. We create a copy
    if ((i == aIndex) && (itemL != NULL))    
        {
        newItemL = new (ELeave) CSecpolBundleItem;
        
        // Need a copy to have separed bundle lists
        newItemL->iSpec = itemL->iSpec;      
        
        // No need to fill iNext. Is filled when adding
        newItemL->iTunnel = itemL->iTunnel;  
        }

    return newItemL;
    }

CSecpolBundleItem::~CSecpolBundleItem()
    {
    delete iTunnelEpName;
    delete iTunnelEpFQDN;
    }

void CPolicySelector::GetFQDNAddressListL(CSecPolBundleList& aPolList) 
    {
    LOG_("CPolicySelector::GetFQDNAddressListL() entry\n");
    TSecpolBundleIter iterl(iBundle);
    CSecpolBundleItem* itemL(NULL);
    while ((itemL = iterl++) != NULL)
        {
        if (itemL->iTunnelEpFQDN) 
            {
            LOG_1("Found FQDN: '%S', appending to the list\n", &(*itemL->iTunnelEpFQDN));
            aPolList.AppendL(itemL);
            LOG_("Append complete\n");
            }
        else 
            {
            TBuf<40> addrstr;
            itemL->iTunnel.Output(addrstr);
            LOG_1("Found IP: '%S', no need to add\n", &addrstr);
            }
        }
    LOG_("CPolicySelector::GetFQDNAddressListL() exit\n");
    }

EXPORT_C
CPolicySelector::~CPolicySelector()
    {
    TSecpolBundleIter iterl(iBundle);
    CSecpolBundleItem* iteml(NULL);
    while ((iteml = iterl++) != NULL)
        {
        iBundle.Remove(*iteml);
        delete iteml;
        }

    delete iRemSelEpName;
    delete iRemMaskEpName;
    delete iLocMaskEpName;
    delete iLocSelEpName;
    }

// Match a selector against the packet information
//
// Returns 0, if selector does not match the information
//         1, if selector matches the information
TInt
CPolicySelector::Match(
    const TInetAddr &aSrc,         // The src address, and port if known
    const TInetAddr &aDst,         // The dst address, and port if known
    TInt aProtocol,                // Transport protocol, if > 0 (known)
    TInt aIcmpType,                // ICMP Type, if ICMP (-1 otherwise)
    TInt aIcmpCode,                // ICMP Code, if ICMP (-1 otherwise)
    TInt aType) const              // Type code, (-1 if not used)
    {
    return (aDst.Match(iRemote, iRemoteMask)
            && aSrc.Match(iLocal, iLocalMask)
            && (iProtocol == 0 || iProtocol == aProtocol)
            && (iRemote.Port() == 0 || iRemote.Port() == aDst.Port())
            && (iLocal.Port() == 0 || iLocal.Port() == aDst.Port())
            && (iIcmpType == -1 || iIcmpType == aIcmpType)
            && (iIcmpCode == -1 || iIcmpCode == aIcmpCode)
            && (iType == -1 || iType == aType));
    }

// Checks if this policy is using this SA
EXPORT_C TBool
CPolicySelector::UseSA(CPolicySpec* aSA, TInetAddr* tunnel)
    {
    TSecpolBundleIter iterBundle(iBundle);
    CSecpolBundleItem* itemBundle(NULL);

    while ((itemBundle = iterBundle++) != NULL)
        {
        if (itemBundle->iSpec)
            {
            // SA used in a Policy. 2 SA cannot have the same name
            if (!(itemBundle->iSpec->iName->Compare(*aSA->iName)))
                {
                if (tunnel)
                    {
                    *tunnel = itemBundle->iTunnel;
                    }
                return ETrue;
                }
            }
        }

    return EFalse;  // This selector doesn't use the SA
    }

//
// CSAPairList: Translation table used when copying a policy
CSAPairList::~CSAPairList()
    {
    TSAPairNode* next(NULL);
    TSAPairNode* node(iList);
    while (node)
        {
        next = node->iNext;
        delete node;
        node = next;
        }
    }

//
// Add at the begining to make it faster
void
CSAPairList::AddL(CPolicySpec* aOldSA, CPolicySpec* aNewSA)
    {
    TSAPairNode* node = new (ELeave) TSAPairNode;
    node->iOldSA = aOldSA;
    node->iNewSA = aNewSA;
    node->iNext = iList;
    iList = node;
    }

CPolicySpec*
CSAPairList::Translate(CPolicySpec* aOldSA)
    {
    TSAPairNode* node = iList;
    while (node)
        {
        if (node->iOldSA == aOldSA)
            return node->iNewSA;
        node = node->iNext;
        }

    // Not found
    return(NULL);
    }

void CSecurityPolicy::GetFQDNAddressListL(CSecPolBundleList& aPolList) 
    {
    LOG_("CSecurityPolicy::GetFQDNAddressListL()\n");
    if (iSelectors) 
        {
        TInt count(iSelectors->Count());
        for (TInt i = 0; i < count; i++) 
            {
            iSelectors->At(i)->GetFQDNAddressListL(aPolList);
            }
        }
    }

EXPORT_C
CSecurityPolicy::CSecurityPolicy()
    {}

EXPORT_C void
CSecurityPolicy::ConstructL()
    {
    iSpecs = CSAList::NewL(1);
    iSelectors = CSelectorList::NewL(1);
    }

// Creates a Security Policy from an existing one
EXPORT_C void
CSecurityPolicy::ConstructL(CSecurityPolicy* aSecPol)
    {
    // Creates a new SA List with new SA nodes!!!
    if (aSecPol->SAList()->Count() > 0)
        {
        iSpecs = CSAList::NewL(aSecPol->SAList());
        }
    else
        {
        iSpecs = CSAList::NewL(1);
        }

    // The selector bundles use references to the old SA nodes
    // so if we copy only the content we'll have invalid references.
    // Therefore, we need a translations tables from old nodes to new
    // ones to pass it to the selector constructor
    if (aSecPol->SelectorList()->Count() > 0)
        {
        CSAPairList* table = CreateTranslationTableL(aSecPol->SAList(), iSpecs);
        CleanupStack::PushL(table);
        iSelectors = CSelectorList::NewL(aSecPol->SelectorList(), table);

        // The table is not needed anymore
        CleanupStack::PopAndDestroy();
        }
    else
        {
        iSelectors = CSelectorList::NewL(1);
        }
    }

CSAPairList*
CSecurityPolicy::CreateTranslationTableL(
    CSAList* aOldSAList,
    CSAList* aNewSAList)
    {
    CSAPairList* table = new (ELeave) CSAPairList;
    CleanupStack::PushL(table);
    TInt count(aOldSAList->Count());
    for (TInt i = 0; i < count; i++)
        {
        table->AddL(aOldSAList->At(i), aNewSAList->At(i));
        }
    CleanupStack::Pop();
    return (table);
    }

CSecurityPolicy::~CSecurityPolicy()
    {
    if (iSpecs)
        {
        // Deletes all the elems in the list
        TInt count(iSpecs->Count());
        for (TInt i = 0; i < count; i++) 
            {
            delete iSpecs->At(i);
            }
        delete iSpecs;
        iSpecs = NULL;
        }

    if (iSelectors)
        {
        // Deletes all the elems in the list
        TInt count(iSelectors->Count());
        for (TInt i = 0; i < count; i++) 
            {
            delete iSelectors->At(i);
            }
        delete iSelectors;
        iSelectors = NULL;
        }
    }

TSecpolBundle*
CSecurityPolicy::FindBundle(
    TUint aDirection,              // Direction flag
    const TInetAddr& aSrc,         // Source Address (and optionally port)
    const TInetAddr& aDst,         // Destination Address (and optionally port)
    TInt aProtocol,                // Transport protocol (if > 0)
    TInt aIcmpType,                // (if != -1)
    TInt aIcmpCode,                // (if != -1)
    TInt aType)                    // (if != -1)
    {
    TInt count(iSelectors->Count());
    for (TInt i = 0; i < count; i++)
        {
        CPolicySelector* selector = iSelectors->At(i);
        if ((selector->iDirection & aDirection)
            && selector->Match(aSrc, 
                               aDst, 
                               aProtocol, 
                               aIcmpType, 
                               aIcmpCode, 
                               aType))
            {
            return (&selector->iBundle);
            }
        }

    return (NULL);
    }

TInt
CSecurityPolicy::SearchForEPNameL(TDesC& aTokenString)
    {
    TInt err(KErrNotFound);
    HBufC8* name = HBufC8::NewL(aTokenString.Length());
    name->Des().Copy(aTokenString);

    // Iterate through the policy specification list
    TInt count(iSpecs->Count());
    for (TInt i = 0; i < count; i++)
        {
        CPolicySpec* polSpec = iSpecs->At(i);
        // Check if spesification type is EndPoint and name for it exists
        if (polSpec->iSpectype == EPolSpecEP && polSpec->iName)
            {
            // Compare EndPoint name against given name
            if (name->Des().Compare(polSpec->iName->Des()) == 0)
                {
                // Match found so set return value to success
                err = KErrNone;
                break;
                }
            }
        }

    // Free memory allocated for name and then return
    delete name;
    return (err);
    }

EXPORT_C CPolicySpec*
CSecurityPolicy::FindSpec(const TDesC8& aName)
    {
    CPolicySpec* spec(NULL);
    TInt count(iSpecs->Count());
    for (TInt i = 0; i < count; i++)
        {
        spec = iSpecs->At(i);
        if ((*spec->iName).Compare(aName) == 0)
            {
            return spec;
            }
        }
    return (NULL);
    }

//
//  CPolicySpec
//
CPolicySpec::CPolicySpec()
    {}

EXPORT_C CPolicySpec*
CPolicySpec::NewL()
    {
    CPolicySpec* self = new (ELeave) CPolicySpec();
    self->Construct();
    return self;
    }

EXPORT_C void
CPolicySpec::Construct()
    {
    iEpSpec.iIsOptional = EFalse;
    iEpSpec.iEpAddr = NULL;
    }

EXPORT_C CPolicySpec*
CPolicySpec::NewL(TDesC& aName, TPolicySpecType iSpectype)
    {
    CPolicySpec* self = new (ELeave) CPolicySpec();
    CleanupStack::PushL(self);
    self->ConstructL(aName, iSpectype);
    CleanupStack::Pop();
    return self;
    }

EXPORT_C void
CPolicySpec::ConstructL(TDesC& aName, TPolicySpecType aSpectype)
    {
    iName = HBufC8::NewL(aName.Length());
    iName->Des().Copy(aName);
    iSpectype = aSpectype;
    }

// Used to initialize with an existing CPolicySpec
EXPORT_C CPolicySpec*
CPolicySpec::NewL(CPolicySpec* aPolSpec)
    {
    CPolicySpec* self = new (ELeave) CPolicySpec();
    CleanupStack::PushL(self);
    self->ConstructL(aPolSpec);
    CleanupStack::Pop();
    return self;
    }

EXPORT_C void
CPolicySpec::ConstructL(CPolicySpec* aPolSpec)
    {
    // Always bigger than 0
    iName = HBufC8::NewL(aPolSpec->iName->Length()); 
    *iName = *aPolSpec->iName;

    if (aPolSpec->iRemoteIdentity)
        {
        iRemoteIdentity = HBufC8::NewL(aPolSpec->iRemoteIdentity->Length());
        *iRemoteIdentity = *aPolSpec->iRemoteIdentity;
        }

    if (aPolSpec->iLocalIdentity)
        {
        iLocalIdentity = HBufC8::NewL(aPolSpec->iLocalIdentity->Length());
        *iLocalIdentity = *aPolSpec->iLocalIdentity;
        }
    iSpec = aPolSpec->iSpec;
    // Even the queue position is cloned    
    iNext = aPolSpec->iNext;  
    }

EXPORT_C
CPolicySpec::~CPolicySpec()
    {
    delete iName;
    delete iRemoteIdentity;
    delete iLocalIdentity;
    }

// CSAList
CSAList::CSAList(TInt aGranularity) :
    CArrayFixFlat<CPolicySpec *>(aGranularity)
    {}

EXPORT_C CSAList*
CSAList::NewL(TInt aGranularity)
    {
    CSAList* self = new (ELeave) CSAList(aGranularity);
    self->Construct(aGranularity);
    return self;
    }

EXPORT_C void
CSAList::Construct(TInt /* aGranularity */)
    {}

CSAList::CSAList(CSAList* aSAList) :
    CArrayFixFlat<CPolicySpec *>(aSAList->Count())
    {}

EXPORT_C CSAList*
CSAList::NewL(CSAList* aSAList)
    {
    CSAList* self = new (ELeave) CSAList(aSAList);
    CleanupStack::PushL(self);
    self->ConstructL(aSAList);
    CleanupStack::Pop();
    return self;
    }

EXPORT_C void
CSAList::ConstructL(CSAList* aSAList)
    {
    TInt count(aSAList->Count());
    for (TInt i = 0; i < count; i++)
        {
        CPolicySpec* policy = CPolicySpec::NewL(aSAList->At(i));
        CleanupStack::PushL(policy);
        AppendL(policy);
        CleanupStack::Pop();
        }
    }

//
// CSelectorList
//
CSelectorList::CSelectorList(TInt aGranularity) :
    CArrayFixFlat<CPolicySelector *>(aGranularity)
    {}

EXPORT_C CSelectorList*
CSelectorList::NewL(TInt aGranularity)
    {
    CSelectorList* self = new (ELeave) CSelectorList(aGranularity);
    self->Construct(aGranularity);
    return self;
    }

EXPORT_C void
CSelectorList::Construct(TInt /*aGranularity*/)
    {}

CSelectorList::CSelectorList(CSelectorList* aSelList,
                             CSAPairList* /* aTable */) :
    CArrayFixFlat<CPolicySelector *>(aSelList->Count())
    {}

EXPORT_C CSelectorList*
CSelectorList::NewL(CSelectorList* aSelList,
                    CSAPairList* aTable)
    {
    CSelectorList* self = new (ELeave) CSelectorList(aSelList, aTable);
    CleanupStack::PushL(self);
    self->ConstructL(aSelList, aTable);
    CleanupStack::Pop();
    return self;
    }

EXPORT_C void
CSelectorList::ConstructL(CSelectorList* aSelList,
                          CSAPairList* aTable)
    {
    TInt count(aSelList->Count());
    for (TInt i = 0; i < count; i++)
        {
        CPolicySelector* selector = CPolicySelector::NewL(aSelList->At(i));
        // Bundle translation
        TSecpolBundleIter iterL(selector->iBundle);
        CSecpolBundleItem* itemL(NULL);
        while (((itemL = iterL++) != NULL))
            {
            if (itemL->iSpec)
                {
                itemL->iSpec = aTable->Translate(itemL->iSpec);
                }
            }

        // Specs in the Bundle translated
        CleanupStack::PushL(selector);
        AppendL(selector);
        CleanupStack::Pop();
        }
    }

