--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/zeroconf/server/src/cqueryhandler.cpp Thu Jun 24 19:09:47 2010 +0530
@@ -0,0 +1,497 @@
+// 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"));
+ }