// Copyright (c) 2008-2009 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:
// dnsproxymessageproc.cpp
// This class constructs the response packet for matched query packet that
// it gets from the queue. First it will get the name from the database
// checks against the query name. If it matched then generates the answer for
// query packet and appends into buffer then sends to querying host. Otherwise
// generates the authoritive answer and sends back to querying hosts.
//



/**
 @file
 @internalTechnology
*/


#include <e32base.h>
#include <e32math.h>
#include <e32des8.h> 
#include <e32debug.h> 
#include <utf.h>
#include "dns_qry_internal.h"
#include "inet6log.h"

#include "dnsproxymsgproc.h"
#include "dnsproxylistener.h"
#include "dnsproxyengine.h"
#include "message.h"
#include "dnsproxylog.h"


CDnsProxyMessageProcessor* CDnsProxyMessageProcessor::NewL(CDnsProxyListener& aListener)
/**
 * This is the two phase construction method and is leaving function
 * @param aListener - reference CDnsProxyListener class
 *
 * @internalTechnology
 **/

	{
	CDnsProxyMessageProcessor* response = new(ELeave) CDnsProxyMessageProcessor(aListener);
	return response;
	}
	
CDnsProxyMessageProcessor::CDnsProxyMessageProcessor(CDnsProxyListener& aListener):iListener(aListener),iAntype(0)
/**
 * This is the constructor for this class
 * @param aListener - reference CDnsProxyListener class
 * @return - None
 *
 * @internalTechnology
 **/

	{
	}

CDnsProxyMessageProcessor::~CDnsProxyMessageProcessor()
/**
 * This is the destructor  and deletes all the member variables and thus
 * releases all the memory assosicated with those objects
 * @param - None
 * @return - None
 *
 * @internalTechnology
 **/
	{
	}
	
void CDnsProxyMessageProcessor::GetDnsFailureMessage(TMsgBuf& aMsgBuf)
/**
 * This is create DNS Failure message
 
 * @param - TMsgBuf&
 * @return - None
 *
 * @internalTechnology
 **/
	{
	TDndHeader &hdr = aMsgBuf.Header();
	
	hdr.SetQR(1);
	//set dns server failure
	hdr.SetRCode(2);
	}
	
void CDnsProxyMessageProcessor::GetLocalDnsResponseL(TMsgBuf& aMsgbuf)
/**
 * This function constructs the response packet. First it gets the 
 * only query name by filtering the qtype and qclass then it tries to
 * match the names. If it matches then, generates the ANResource reord 
 * otherwise generates the NSResource Record.
 * @param -aMsg - Query packet
 * @internalTechnology
 **/

	{
	TDndQuestion qDns;
	qDns.Create(aMsgbuf, sizeof(TDndHeader));

	TBuf<KDnsMaxName> queryName;
	TInt ret_val = qDns.GetName(queryName);
	if(ret_val==KErrNone)
		{
		HBufC8* q_name = CnvUtfConverter::ConvertFromUnicodeToUtf8L(queryName);
		CleanupStack::PushL(q_name);
		
		TPtrC8 qname = q_name->Right(q_name->Length());
		EDnsQType iQType = qDns.QType();
				
		switch(qDns.QType())
			{
				case EDnsQType_A:
					{
					TDnsProxydbElement* element;
					TDndHeader& hdr = aMsgbuf.Header();
					CDnsProxyEngine& engine = iListener.GetDnsProxyEngine();
					
					element = engine.iDnsproxydb->GetRecordList(qname);
					if(element)
						{
						hdr.SetNsCount(0);
						GetRecordsFromDb(aMsgbuf,*element);
						}
					else	
						{
						SetQHeaderBits(hdr);
						hdr.SetNsCount(1);
						GetNsRecordSetL(aMsgbuf);
						}	
					}
					break;
				case EDnsQType_PTR:
					{
					TDnsProxydbElement* element = NULL;
					element = LookupPTRInDb(aMsgbuf);
	                if(element)
	                    {           
	                    GetPTRRecord(aMsgbuf,element->iHostName);
	                    }
					}
					break;
			}
			CleanupStack::PopAndDestroy(q_name);
		}
	}
	
void CDnsProxyMessageProcessor::SetQHeaderBits(TDndHeader& aHdr)
/**
 * This will set bits fileds of query header as required for 
 * response message
 * @param aHdr - reference to header section of query
 * @internalTechnology
 **/	

	{
	TInt aId = aHdr.ID();
	aHdr.SetId(aId);
	aHdr.SetQR(1);
	aHdr.SetOpcode(0);
	aHdr.SetAA(0);
	aHdr.SetRCode(0);

	TInt qdcount = aHdr.QDCOUNT();
	aHdr.SetQdCount(qdcount);
	aHdr.SetArCount(0);
	}
	
void CDnsProxyMessageProcessor::GetRecordsFromDb(TMsgBuf& aMsg,TDnsProxydbElement& aElement)
/**
 * This will give constructed response message for given query
 * @param aMsg - query 
 * @param - aElement - reference to TDnsProxydbElement class
 * @internalTechnology
 *
 **/	
	{
	iAntype =0;
	TDndHeader &hdr = aMsg.Header();
	SetQHeaderBits(hdr);
	TInt hdr_id = hdr.ID();

	if(aElement.iRecord.Length()==0)
		{
		GetAnRecordSet(aMsg,aElement);
		}
	else
		{
		TBuf8<KDnsMaxMessage>record;
		record = aElement.iRecord;
		aMsg = TMsgBuf::Cast(record);
		TDndHeader& header = aMsg.Header();
		TInt hdid = header.ID();
		LOG(Log::Printf(_L("\t \n --- Transaction Id:[%d]"),hdid));
		header.SetId(hdr_id);
		TUint16 qtype = aElement.iDnsQType;
		if(qtype==EDnsType_A)
			iAntype++;
		}
	hdr.SetAnCount(iAntype);
	
	hdr.SetNsCount(0);
	}
	
void CDnsProxyMessageProcessor::GetAnRecordSet(TMsgBuf& aQMsg,TDnsProxydbElement& aElement)
/**
 * Constructs answer resource record/s and appends it to buffer
 * @param -  aQMsg, dns query message
 * @param -  aElement, reference to TDnsProxydbElement class
 * @leave - system wide errors, buffer overflow
 * @internalTechnology
 **/	
	{
	CDnsProxyEngine& engine = iListener.GetDnsProxyEngine();
			
	aQMsg.Header();
	
	TInetAddr address = aElement.iHostAddr;

	THostName name;
	aQMsg.GetName(sizeof(aQMsg.Header()),name);	
	
	
	TDndQuestion question;
	TInt index = question.Create(aQMsg,sizeof(aQMsg.Header()));

	TInt len = aQMsg.Length();

	TDndRROut rrout(aQMsg);
	
	TDnsProxyConfigParams iEngineConfig = iListener.GetDnsProxyEngine().GetConfig();
	rrout.iType = (TUint16)question.QType();
	rrout.iClass = (TUint16)question.QClass();
	rrout.iTTL = iEngineConfig.iTTL;
	TInt ret = rrout.Append(KNullDesC8, sizeof(TDndHeader));
	iAntype++;
	rrout.AppendRData(address);
	
	engine.iDnsproxydb->AddRRecord(aElement,aQMsg,question.QType());			

	}
	
void CDnsProxyMessageProcessor::GetNsRecordSetL(TMsgBuf& aMsg)
/**
 * constructs and appends the authoritive resource records
 * @param -  aQMsg, dns query message
 * @leave - system wide errors, buffer overflow
 *
 * @internalTechnology
 **/
	{
	TDnsProxyNsRecord nsrecord;
	TBuf8<KDnsMaxMessage> record;

	//read from TTL from config
	CDnsProxyEngine& engine = iListener.GetDnsProxyEngine();
	TUint ttl =  engine.GetConfig().iTTL;
	//Create RBuf
	RBuf8 qname;
	
	TInt offset = sizeof(TDndHeader);
	const TUint8* ptr = (aMsg.Ptr() + offset);
	TPtrC8 ptrc8(aMsg.Ptr() + offset, aMsg.Size() - offset);
	
	qname.Create(ptrc8.Size());
	CleanupClosePushL(qname);
	
	qname.Copy(TPtrC8(&ptr[0],ptrc8.Size()));
	
	nsrecord.ResetBuffer();
	nsrecord.iQType = EDnsType_SOA;
	nsrecord.iCType = EDnsClass_IN;
	nsrecord.iTTL = ttl;
	nsrecord.iSerialNum 	= engine.iConfigParams.iSerialNum;
	nsrecord.iRefreshTime	= engine.iConfigParams.iRefreshTime;
	nsrecord.iRetryTime		= engine.iConfigParams.iRetryTime;
	nsrecord.iExpiretime	= engine.iConfigParams.iExpiretime;
	
	record = nsrecord.CreateNSResourceRecord(qname);
	aMsg.Append(record);
	CleanupStack::PopAndDestroy(&qname);
	}
	
TInt CDnsProxyMessageProcessor::GetQNameSuffixL(TMsgBuf& aMsg)
/**
 * This function gets the suffix of recieved query
 * @param aMsg - reference to dns query message
 * @return ret_val - represents whether query is local or global query
 *
 * @internalTechnology
 **/
	{
    CDnsProxyEngine& engine = iListener.GetDnsProxyEngine();
   	
	TInt retVal = KGlobalQuery;
	
	/*In case suffix information is not available
	  treat it as global query no need of comparison*/
	if(!engine.IsSuffixAvailable())
	    return retVal;
	TDndQuestion qDns;
	qDns.Create(aMsg, sizeof(TDndHeader));
	EDnsQType iQType = qDns.QType();
    
	switch (qDns.QType()) 
		{
		case EDnsQType_A:
			{
			TBuf<KMaxName> buf;
			buf.Copy(engine.iSuffixInfo);
			HBufC8* nsuffix = CnvUtfConverter::ConvertFromUnicodeToUtf8L(buf);
			CleanupStack::PushL(nsuffix);	
			TPtrC8 qsuffix = nsuffix->Right(nsuffix->Length());
			TBuf<KDnsMaxName> queryName;
			TInt err = qDns.GetName(queryName);
			if(err == KErrNone)
				{
				HBufC8* new_name = CnvUtfConverter::ConvertFromUnicodeToUtf8L(queryName);
				TPtrC8 qname = new_name->Right(new_name->Length());
				if(qname.Length() > qsuffix.Length())
					{
					TPtrC8 suffix = qname.Mid((qname.Length())-(qsuffix.Length()),qsuffix.Length());
					TInt errVal = suffix.Compare(qsuffix);
					if(errVal==KErrNone)
						{
						retVal = KLocalQuery;
						__LOG("Processing Local Query-Type-A");
						}
					else
						__LOG("Processing Global Query-Type-A");	
					}
		        		
				delete new_name;
				}
			CleanupStack::PopAndDestroy(nsuffix);	
			}
			break;
		case EDnsQType_PTR:
			{
			TDnsProxydbElement* element = NULL;			
			element = LookupPTRInDb(aMsg);
			if(element)
				{
				retVal = KLocalQuery;
				}
			else 
				{
				retVal = KGlobalQuery;
				}
			}
			break;
		default:
			{
			retVal = KGlobalQuery;
			}
				
		} // end switch
	return retVal;
	}
TDnsProxydbElement* CDnsProxyMessageProcessor::LookupPTRInDb(TMsgBuf& aMsg)
/**
* Looksup in db for existance of the recod. If present then process query as local
* otherwise treats PTR query as a global one
* @param aMsg - Query Message
* @return TDnsProxydbElement - pointer to TDnsProxydbElement object
*
**/
	{
	CDnsProxyEngine& engine = iListener.GetDnsProxyEngine();
	
	TBuf<KPTRSufix>ptrbuf;
	ptrbuf.Copy(KPTRSUFFIX);
	
	TDndQuestion qDns;
	qDns.Create(aMsg, sizeof(TDndHeader));
	
	TBuf<KDnsMaxName> queryName;
	TInt flag = qDns.GetName(queryName);
	
	TDnsProxydbElement* element = NULL;
	if(queryName.Length()>ptrbuf.Length())
		{
		TPtrC16 ipaddr = queryName.Mid(0,(queryName.Length()-(ptrbuf.Length()+1)));
		TInetAddr address;
		address.Input(ipaddr);
		TUint32 ad = address.Address();
		TUint32 swapaddr =(((ad&0x000000FF)<<24)+((ad&0x0000FF00)<<8)+
	   					  ((ad&0x00FF0000)>>8)+((ad&0xFF000000)>>24));
		TInetAddr inetaddr;
		inetaddr.SetAddress(swapaddr);
	
		element = engine.iDnsproxydb->FindIpAddress(inetaddr);
		}
					
	return element;
	}
void CDnsProxyMessageProcessor::GetPTRRecord(TMsgBuf& aMsg,const TDesC8& aName)
/**
* Constructs PTR record and appends to TMsgBuf
* @param aMsg - Query Message
* @param aElement - reference to TDnsProxydbElement class which matched to query name
*
* @internalTechnology
**/
	{
	TDndHeader &hdr = aMsg.Header();
	
	SetQHeaderBits(hdr);
	hdr.SetAA(1);
	hdr.SetAnCount(1);
	
	TDndQuestion question;
	TInt index = question.Create(aMsg,sizeof(TDndHeader));

	TInt len = aMsg.Length();

	TDndRROut rrout(aMsg);
	
	rrout.iType = (TUint16)question.QType();
	rrout.iClass = (TUint16)question.QClass();
	
	rrout.iTTL = iListener.GetDnsProxyEngine().GetConfig().iTTL;
	TInt ret = rrout.Append(KNullDesC8, sizeof(TDndHeader));
	
	TInt err = rrout.AppendRData(aName,0);
	}
TDesC8& TDnsProxyNsRecord::CreateNSResourceRecord(const TDesC8& aMsg)
/**
 * Creates new authorative answer record for given query. This will be 
 * generated when ther is no matching name found in db
 * @param aMsg - Dns query 
 * @return iNSBuf - buffer having constructed authorative answer record
 *
 * @internalTechnology
 **/	
	{
	
	TInt offset_pos = aMsg.Locate(0);
	const TUint8* ptr8 = (aMsg.Ptr());
	TBufC8<KDnsMaxMessage> abuf_ns = TPtrC8(&ptr8[0],offset_pos);
	
	iNSBuf.Append(abuf_ns);
	//this will identify the termination of the QName
	iNSBuf.Append((TChar)0);

	iNSBuf.Append(iQType/0x100);
	iNSBuf.Append(iQType%0x100);
	
	iNSBuf.Append(iCType/0x100);
	iNSBuf.Append(iCType%0x100);
	AppendTTLBuf();
	AppendRRLength(aMsg);

	return iNSBuf;
	}
	
void TDnsProxyNsRecord::AppendTTLBuf()
/**
 * Appends the TTL to ns record set
 *
 * @internalTechnology
 **/
	{
	iNSBuf.Append((TChar)(iTTL / 0x1000000));
	iNSBuf.Append((TChar)((iTTL / 0x10000) % 0x100));
	iNSBuf.Append((TChar)((iTTL / 0x100) % 0x100));
	iNSBuf.Append((TChar)(iTTL % 0x100));
	}
	
void TDnsProxyNsRecord::AppendRRLength(const TDesC8& aMsg)
/**
 * This function creates the NS Resource Record as specified in
 * the RFC1035 section 3.3.13.-SOA RDATA Format
 * @param - aMsg ,reference to buf which will have ns record set
 *
 * @internalTechnology
 **/
	{
	TBuf8<KDnsMaxMessage> buf;
	buf.Append(aMsg);
	buf.Append((TChar)0);
	buf.Append(aMsg);
	buf.Append((TChar)0);
	
	buf.Append((TChar)(iSerialNum / 0x1000000));
	buf.Append((TChar)((iSerialNum / 0x10000) % 0x100));
	buf.Append((TChar)((iSerialNum / 0x100) % 0x100));
	buf.Append((TChar)(iSerialNum % 0x100));

	buf.Append((TChar)(iRefreshTime / 0x1000000));
	buf.Append((TChar)((iRefreshTime / 0x10000) % 0x100));
	buf.Append((TChar)((iRefreshTime / 0x100) % 0x100));
	buf.Append((TChar)(iRefreshTime % 0x100));

	buf.Append((TChar)(iRetryTime / 0x1000000));
	buf.Append((TChar)((iRetryTime / 0x10000) % 0x100));
	buf.Append((TChar)((iRetryTime / 0x100) % 0x100));
	buf.Append((TChar)(iRetryTime % 0x100));

	buf.Append((TChar)(iExpiretime / 0x1000000));
	buf.Append((TChar)((iExpiretime / 0x10000) % 0x100));
	buf.Append((TChar)((iExpiretime / 0x100) % 0x100));
	buf.Append((TChar)(iExpiretime % 0x100));

	buf.Append((TChar)(iTTL / 0x1000000));
	buf.Append((TChar)((iTTL / 0x10000) % 0x100));
	buf.Append((TChar)((iTTL / 0x100) % 0x100));
	buf.Append((TChar)(iTTL % 0x100));

	TInt len = buf.Size();
	iNSBuf.Append((TChar)(len/0x0100));
	iNSBuf.Append((TChar)(len%0x0100));
	
	iNSBuf.Append(buf);

	}

void TDnsProxyNsRecord::ResetBuffer()
/**
 * before appending anything newly, deletes its contents first then append
 *
 * @internalTechnology
 **/
	{
	TInt len = iNSBuf.Length();
	if(len>0)
		iNSBuf.Delete(0,len);
	}
