zeroconf/server/src/cqueryhandler.cpp
changeset 14 da856f45b798
--- /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"));		
+	}