Committing ZeroConf for 10.1 to the FCL.
// 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:
// cqueryhandler.cpp
//
//
/**
@file
@internalTechnology
*/
//User include
#include "cqueryhandler.h"
//System include
#include <mdns/cdnsquestion.h>
#include <mdns/ccacheentry.h>
#include <mdns/tdnsheader.h>
__FLOG_STMT(_LIT8(KComponent,"MDNSServer");)
/*
* Two phase constructor
* @param aMessageHandler reference to the message handler.
*/
CQueryHandler* CQueryHandler::NewL(CMessageHandler& aMessageHandler)
{
CQueryHandler* self = new(ELeave)CQueryHandler(aMessageHandler);
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop();
return self;
}
/*
* Destructor
*/
CQueryHandler::~CQueryHandler()
{
__FLOG(_L8("CQueryHandler::~CQueryHandler - Entry Exit"));
__FLOG_CLOSE;
}
/*
* Handles any query from the network
* @param aMessage message which contains the query.
* @param aSockAddr address form which query id recieved.
*/
void CQueryHandler::HandleIncomingPacketL(CDnsMessage& aMessage ,const TSockAddr& aAddr)
{
__FLOG(_L8("CQueryHandler::HandleIncomingPacketL - Entry"));
if(aMessage.Header().IsQuery())
{
CDnsMessage* ret= CDnsMessage::NewL(aMessage.Header().Id(),EFalse);
CleanupStack::PushL(ret);
TInt qCount = aMessage.Header().QueryCount();
for(TInt i =0; i< qCount;i++)
{
const CDnsQuestion* query = aMessage.Queries()[i];
TBufC8<100> domainName(query->Name());
RPointerArray<CCacheEntry> cacheArray;
TRAPD(error,MessageHandler().DnsCache().FindServiceL(cacheArray,domainName,(TDnsType)query->Type()));
for(TInt i =0 ; i<cacheArray.Count() ;i++)
{
if(cacheArray[i]->IsAuthoritative())
{
TBool suppress = EFalse;
switch(query->Type())
{
case EDnsType_A:
{
suppress = SuppressDuplicateAnswerL(aMessage.Answers(),*(cacheArray[i]->AddressRecord()));
if(suppress == EFalse)
{
ret->AppendAnswerL(cacheArray[i]->AddressRecord()->CloneL());
}
break;
}
case EDnsType_PTR:
{
suppress = SuppressDuplicateAnswerL(aMessage.Answers(),*(cacheArray[i]->PtrRecord()));
if(suppress == EFalse)
{
ret->AppendAnswerL(cacheArray[i]->PtrRecord()->CloneL());
}
break;
}
case EDnsType_SRV:
{
suppress = SuppressDuplicateAnswerL(aMessage.Answers(),*(cacheArray[i]->ServiceRecord()));
if(suppress == EFalse)
{
ret->AppendAnswerL(cacheArray[i]->ServiceRecord()->CloneL());
}
break;
}
case EDnsType_TXT:
{
suppress = SuppressDuplicateAnswerL(aMessage.Answers(),*(cacheArray[i]->TxtRecord()));
if(suppress == EFalse)
{
ret->AppendAnswerL(cacheArray[i]->TxtRecord()->CloneL());
}
break;
}
case EDnsType_AAAA:
{
//HandleAAAAQuestionL();
break;
}
case EDnsQType_Any:
{
HandleAnyQuestionL(ret,aMessage.Answers(),*cacheArray[i]);
break;
}
default:
User::Invariant();
}
}
}
cacheArray.ResetAndDestroy();
cacheArray.Close();
}
if(ret->Header().AnswerCount() == 0 && !IsLastTruncated)
{
CleanupStack::PopAndDestroy(ret);
}
else
{
TDnsHeader& header = const_cast <TDnsHeader&> (ret->Header());
header.SetAuthoritative(ETrue);
if(IsLastTruncated)
{
COutStandingQuery* query = NULL;
TInt index = 0;
for( index =0 ; index< iOutStandingQueryArray.Count();index++)
{
query = iOutStandingQueryArray[index];
if(query->TransactionId() == header.Id() && query->SocketAddress() == aAddr)
{
break;
}
}
if(index < iOutStandingQueryArray.Count())
{
CDnsMessage* message = &(query->DnsMessage());
TInt i = 0;
while(i++ <message->Header().AnswerCount())
{
ret->AppendAnswerL(message->Answers()[i]);
}
}
}
if(aMessage.Header().IsTruncated())
{
IsLastTruncated = ETrue;
TTime time;
time.HomeTime();
time + TTimeIntervalMicroSeconds(500);
COutStandingQuery* outstandingQuery = COutStandingQuery::NewL(ret,ret->Header().Id(),time,aAddr);
iOutStandingQueryArray.Append(outstandingQuery);
if(!IsActive())
{
SetActive();
At(time);
}
}
else
{
const TUint32 KMDnsAddr = INET_ADDR(224, 0, 0, 251);
TInetAddr addr(KMDnsAddr, KMdnsPort);
CSendMessageData* data = CSendMessageData::NewL(ret,ETrue,addr,*this);
MessageHandler().MessageQueue().QueueDnsMessageL(*data);
}
CleanupStack::Pop();
}
}
else
{
User::Leave(KErrGeneral);
}
__FLOG(_L8("CQueryHandler::HandleIncomingPacketL - Exit"));
}
/*
* Constructor
* by default clienthandle is 0
*/
CQueryHandler::CQueryHandler(CMessageHandler& aMessageHandler):CBaseHandler(aMessageHandler)
{
iClientHandle =0 ;
}
/*
* Two phase constructor
*/
void CQueryHandler::ConstructL()
{
__FLOG_OPEN(KMDNSSubsystem, KComponent);
__FLOG(_L8("CQueryHandler::ConstructL - Entry"));
CBaseHandler::ConstructL();
__FLOG(_L8("CQueryHandler::ConstructL - Exit"));
}
/*
* compares the answer section and the authoritative entries in the cache .
* If there is a match returns ETrue. which mean remove the entry , as the
* client is not interested in this record.
* @param aAnswers array of records recieved from the external client.
* @param aResourceData data from the cache to be checked with the answer section.
*/
TBool CQueryHandler::SuppressDuplicateAnswerL(const RPointerArray<CDnsResourceData>& aAnswers,const CDnsResourceData& aResourceData)
{
__FLOG(_L8("CQueryHandler::SuppressDuplicateAnswerL - Entry"));
TBool ret =EFalse;
TInt answersCount = aAnswers.Count();
for(TInt i=0 ;i< answersCount;i++)
{
if(0 == aAnswers[i]->Name().Compare(aResourceData.Name()) && aAnswers[i]->Type() == aResourceData.Type())
{
switch(aResourceData.Type())
{
case EDnsType_A:
{
const CRdTypeA& rdata = (CRdTypeA&)(aResourceData);
const CRdTypeA* rEntry = static_cast<CRdTypeA*>(aAnswers[i]);
if(rdata.Address() == rEntry->Address() && rdata.Ttl() > rEntry->Ttl())
{
ret = ETrue;
}
break;
}
case EDnsType_PTR:
{
const CRdTypePtr& rdata = (CRdTypePtr&)(aResourceData);
const CRdTypePtr* rEntry = static_cast<CRdTypePtr*>(aAnswers[i]);
if(0 ==rdata.Name().Compare(rEntry->Name()) && rdata.Ttl() > rEntry->Ttl())
{
ret = ETrue;
}
break;
}
case EDnsType_SRV:
{
const CRdTypeSrv& rdata = (CRdTypeSrv&)(aResourceData);
const CRdTypeSrv* rEntry = static_cast<CRdTypeSrv*>(aAnswers[i]);
if(0 == rdata.Name().Compare(rEntry->Name()) && rdata.Port() == rEntry->Port() && rdata.Ttl() > rEntry->Ttl())
{
ret = ETrue;
}
break;
}
case EDnsType_TXT:
{
const CRdTypeTxt& rdata = (CRdTypeTxt&)(aResourceData);
const CRdTypeTxt* rEntry = static_cast<CRdTypeTxt*>(aAnswers[i]);
if(rdata.Text().Count() == rEntry->Text().Count())
{
for(TInt i =0; i< rdata.Text().Count();i++)
{
if(0 != rdata.Text()[i].Compare(rEntry->Text()[i]))
{
break;
}
}
ret =ETrue;
}
break;
}
default:
{
User::Invariant();
}
}
}
}
__FLOG(_L8("CQueryHandler::SuppressDuplicateAnswerL - Exit"));
return ret;
}
/*
* Handle query from the network of type *Any
* @param aPacket packet to be sent to the network in response to the query.
* @param aAnswers an array of answers in the query packet.
* @param aEntry authoritative entries in the cache.
*/
void CQueryHandler::HandleAnyQuestionL(CDnsMessage* aPacket,const RPointerArray<CDnsResourceData>& aAnswers,const CCacheEntry& aEntry)
{
__FLOG(_L8("CQueryHandler::HandleAnyQuestionL - Entry"));
if(aEntry.AddressRecord() && !SuppressDuplicateAnswerL(aAnswers,*(aEntry.AddressRecord())))
{
aPacket->AppendAnswerL(aEntry.AddressRecord()->CloneL());
}
if(aEntry.PtrRecord() && !SuppressDuplicateAnswerL(aAnswers,*(aEntry.PtrRecord())))
{
aPacket->AppendAnswerL(aEntry.PtrRecord()->CloneL());
}
if(aEntry.ServiceRecord() && !SuppressDuplicateAnswerL(aAnswers,*(aEntry.ServiceRecord())))
{
aPacket->AppendAnswerL(aEntry.ServiceRecord()->CloneL());
}
if(aEntry.TxtRecord() && !SuppressDuplicateAnswerL(aAnswers,*(aEntry.TxtRecord())))
{
aPacket->AppendAnswerL(aEntry.TxtRecord()->CloneL());
}
__FLOG(_L8("CQueryHandler::HandleAnyQuestionL - Exit"));
}
/*
* Handles query from the application.
* 1.Query for the ptr entries are sent to the network by default.
* 2.Query for the srv or txt record will be sent to the network only if it is not present in the cache
* @param aMessage contains the query sent by the client.
* @param aClientHandle sessionid of the client.
*/
void CQueryHandler::ServiceClientQueryL(CDnsMessage* aMessage,TInt aClientHandle)
{
__FLOG(_L8("CQueryHandler::ServiceClientQueryL - Entry"));
TInt queryCount = aMessage->Header().QueryCount();
TBool isSendQueryToNw = EFalse;
for(TInt i =0 ; i< queryCount ; i++)
{
switch(aMessage->Queries()[i]->Type())
{
case EDnsType_A:
{
RPointerArray<CCacheEntry> iAddressRecords;
TInt err = KErrNone;
TRAPD(error ,err = MessageHandler().DnsCache().FindServiceL(iAddressRecords, aMessage->Queries()[i]->Name(), EDnsType_A));
if(error == KErrNone && err==KErrNotFound /*&& (NULL == iAddressRecords[0]->AddressRecord())*/)
{
isSendQueryToNw = ETrue;
}
else
{
CDnsQuestion* question = aMessage->Queries()[i];
const_cast <RPointerArray<CDnsQuestion>&>(aMessage->Queries()).Remove(i);
delete question;
}
iAddressRecords.ResetAndDestroy();
iAddressRecords.Close();
break;
}
case EDnsType_PTR:
case EDnsQType_Any:
{
RPointerArray<CCacheEntry> ptrRecords;
TRAPD(error,MessageHandler().DnsCache().FindServiceL(ptrRecords,aMessage->Queries()[i]->Name(),EDnsType_PTR));
for(TInt index =0 ; (error == KErrNone) && index < ptrRecords.Count(); index++)
{
aMessage->AppendAnswerL(ptrRecords[index]->PtrRecord()->CloneL());
}
isSendQueryToNw = ETrue;
ptrRecords.ResetAndDestroy();
ptrRecords.Close();
break;
}
case EDnsType_SRV:
case EDnsType_TXT:
{
RPointerArray<CCacheEntry> records;
TRAPD(error,MessageHandler().DnsCache().FindServiceL(records,aMessage->Queries()[i]->Name(),(TDnsType)aMessage->Queries()[i]->Type()));
if(error == KErrNone && 0 != records.Count())
{
CDnsQuestion* question = aMessage->Queries()[i];
const_cast <RPointerArray<CDnsQuestion>&>(aMessage->Queries()).Remove(i);
delete question;
}
else
{
isSendQueryToNw = ETrue;
}
records.ResetAndDestroy();
records.Close();
break;
}
}
}
if(isSendQueryToNw)
{
SendQueryL(aMessage,*this);
iClientHandle = aClientHandle;
//On succesfull sending the packet query handler will be notified there query will be
// added to outstanding queue
}
else
{
delete aMessage;//as the question is not to be send .
TTime time;
time.HomeTime();
ScheduleOutStandingQueryL(aClientHandle,ETrue,time);
}
__FLOG(_L8("CQueryHandler::ServiceClientQueryL - Exit"));
}
/*
* Any queries to be sent to the network goes through this
* @param aMessage contains the query to be sent.
* @param aObserver callback which is interested in getting notified when
* a packet is successfully sent to the network.
*/
void CQueryHandler::SendQueryL(CDnsMessage* aMessage, MMessageHandler& aObserver)
{
__FLOG(_L8("CQueryHandler::SendQueryL - Entry"));
//
//TODO in case in case the size exceeds max size
//
TInetAddr addr(KMDnsAddr, KMdnsPort);
TBuf<255> buf;
addr.Output(buf);
CSendMessageData* data = CSendMessageData::NewL(aMessage,EFalse,addr,aObserver);
CleanupStack::PushL(data);
MessageHandler().MessageQueue().QueueDnsMessageL(*data);
CleanupStack::Pop();//data
__FLOG(_L8("CQueryHandler::SendQueryL - Exit"));
}
/*
* Creates an outstanding query object from the client query and adds it to the outstanding query arry.
* 1. For queries sent to the network ,waits for 120ms before sending the response back .
* 2. For queries not sent to network ,reponse will be sent back immediately.
*/
void CQueryHandler::ScheduleOutStandingQueryL(TInt aClientHandle , TBool aTriggerNow, TTime aInTime)
{
__FLOG(_L8("CQueryHandler::ScheduleOutStandingQueryL - Entry"));
COutStandingQuery* outstandingQuery = COutStandingQuery::NewL(NULL,0,aInTime,NULL);
outstandingQuery->SetClientHandle(aClientHandle);
if(aTriggerNow)
{
if(IsActive())
{
Cancel();
}
iOutStandingQueryArray.Insert(outstandingQuery,0);
TRequestStatus* status = &iStatus;
SetActive();
User::RequestComplete(status,KErrNone);
}
else
{
TInt pos = 0;
for(pos = 0 ; pos< iOutStandingQueryArray.Count(); pos++ )
{
if(outstandingQuery->GetAddTime().Int64() < iOutStandingQueryArray[pos]->GetAddTime().Int64() )
{
break;
}
}
iOutStandingQueryArray.Insert(outstandingQuery, pos);
if(!IsActive())
{
TTime nextReview = iOutStandingQueryArray[0]->GetAddTime();
At(nextReview);
}
}
__FLOG(_L8("CQueryHandler::ScheduleOutStandingQueryL - Exit"));
}
/*Only for the query recieved from the application there should be a delay
* before sending the response .So wait for 120ms before sending the response
* back to client.
* Only for the query recieved from the client ,iClientHandle will not be NULL
* @param aError is the error in case any in delivering the packet.
*/
void CQueryHandler::OnPacketSendL(TInt /*aError*/)
{
__FLOG(_L8("CQueryHandler::OnPacketSendL - Entry"));
if(iClientHandle != 0)
{
TTime time;
time.HomeTime();
time = time + TTimeIntervalMicroSeconds(120000);
ScheduleOutStandingQueryL(iClientHandle,EFalse,time);
iClientHandle =0 ;
}
__FLOG(_L8("CQueryHandler::OnPacketSendL - Exit"));
}