zeroconf/server/src/cqueryhandler.cpp
changeset 14 da856f45b798
equal deleted inserted replaced
12:78fbd574edf4 14:da856f45b798
       
     1 // Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 // cqueryhandler.cpp
       
    15 // 
       
    16 //
       
    17 /**
       
    18 @file
       
    19 @internalTechnology
       
    20 */
       
    21 
       
    22 //User include 
       
    23 #include "cqueryhandler.h"
       
    24 //System include
       
    25 #include <mdns/cdnsquestion.h>
       
    26 #include <mdns/ccacheentry.h>
       
    27 #include <mdns/tdnsheader.h>
       
    28 __FLOG_STMT(_LIT8(KComponent,"MDNSServer");)
       
    29 /*
       
    30  * Two phase constructor
       
    31  * @param aMessageHandler reference to the message handler.
       
    32  */
       
    33 CQueryHandler* CQueryHandler::NewL(CMessageHandler& aMessageHandler)
       
    34 	{
       
    35 	CQueryHandler* self = new(ELeave)CQueryHandler(aMessageHandler);	
       
    36 	CleanupStack::PushL(self);
       
    37 	self->ConstructL();
       
    38 	CleanupStack::Pop();
       
    39 	return self;
       
    40 	}
       
    41 	
       
    42 /*
       
    43  * Destructor
       
    44  */
       
    45 CQueryHandler::~CQueryHandler()
       
    46 	{
       
    47 	__FLOG(_L8("CQueryHandler::~CQueryHandler - Entry Exit"));
       
    48 	__FLOG_CLOSE;	
       
    49 	}
       
    50 
       
    51 /*
       
    52  * Handles any query from the network 
       
    53  * @param aMessage message which contains the query.
       
    54  * @param aSockAddr address form which query id recieved.
       
    55  */
       
    56 void CQueryHandler::HandleIncomingPacketL(CDnsMessage& aMessage ,const TSockAddr& aAddr)
       
    57 	{
       
    58 	__FLOG(_L8("CQueryHandler::HandleIncomingPacketL - Entry"));
       
    59 	if(aMessage.Header().IsQuery())
       
    60 		{
       
    61 		CDnsMessage* ret= CDnsMessage::NewL(aMessage.Header().Id(),EFalse);
       
    62 		CleanupStack::PushL(ret);
       
    63 		TInt qCount = aMessage.Header().QueryCount();
       
    64 		for(TInt i =0; i< qCount;i++)
       
    65 			{
       
    66 			const CDnsQuestion* query = aMessage.Queries()[i];
       
    67 			TBufC8<100> domainName(query->Name());
       
    68 			RPointerArray<CCacheEntry> cacheArray;
       
    69 			TRAPD(error,MessageHandler().DnsCache().FindServiceL(cacheArray,domainName,(TDnsType)query->Type()));
       
    70 			for(TInt i =0 ; i<cacheArray.Count()  ;i++)
       
    71 				{
       
    72 				if(cacheArray[i]->IsAuthoritative())
       
    73 					{
       
    74 					TBool suppress = EFalse;
       
    75 					switch(query->Type())
       
    76 							{
       
    77 							case  EDnsType_A:
       
    78 								{
       
    79 								suppress = SuppressDuplicateAnswerL(aMessage.Answers(),*(cacheArray[i]->AddressRecord()));
       
    80 								if(suppress == EFalse)
       
    81 									 {
       
    82 									 ret->AppendAnswerL(cacheArray[i]->AddressRecord()->CloneL());
       
    83 									 	
       
    84 									 }
       
    85 								break;	
       
    86 								}
       
    87 							case  EDnsType_PTR:
       
    88 								{
       
    89 								suppress = SuppressDuplicateAnswerL(aMessage.Answers(),*(cacheArray[i]->PtrRecord()));
       
    90 								if(suppress == EFalse)
       
    91 									 {
       
    92 									 ret->AppendAnswerL(cacheArray[i]->PtrRecord()->CloneL());
       
    93 									 }
       
    94 								break;	
       
    95 								}
       
    96 							case  EDnsType_SRV:
       
    97 								{
       
    98 								suppress = SuppressDuplicateAnswerL(aMessage.Answers(),*(cacheArray[i]->ServiceRecord()));
       
    99 								if(suppress == EFalse)
       
   100 									 {
       
   101 									 ret->AppendAnswerL(cacheArray[i]->ServiceRecord()->CloneL());
       
   102 									 }
       
   103 								break;	
       
   104 								}
       
   105 							case  EDnsType_TXT:
       
   106 								{
       
   107 								suppress = SuppressDuplicateAnswerL(aMessage.Answers(),*(cacheArray[i]->TxtRecord()));
       
   108 								if(suppress == EFalse)
       
   109 									 {
       
   110 									 ret->AppendAnswerL(cacheArray[i]->TxtRecord()->CloneL());
       
   111 									 }
       
   112 								break;
       
   113 								}
       
   114 							case  EDnsType_AAAA:
       
   115 								{
       
   116 								//HandleAAAAQuestionL();
       
   117 								break;	
       
   118 								}
       
   119 							case  EDnsQType_Any:
       
   120 								{
       
   121 								HandleAnyQuestionL(ret,aMessage.Answers(),*cacheArray[i]);
       
   122 								break;	
       
   123 								}	
       
   124 							default:
       
   125 								User::Invariant();				
       
   126 								
       
   127 							}
       
   128 					 						
       
   129 					}
       
   130 				}
       
   131             cacheArray.ResetAndDestroy();
       
   132             cacheArray.Close();
       
   133 			}
       
   134 		if(ret->Header().AnswerCount() == 0 && !IsLastTruncated)
       
   135 			{
       
   136 			CleanupStack::PopAndDestroy(ret);
       
   137 			}
       
   138 		else
       
   139 			{
       
   140 			TDnsHeader& header = const_cast <TDnsHeader&> (ret->Header());
       
   141 			header.SetAuthoritative(ETrue);
       
   142 			if(IsLastTruncated)
       
   143 				{
       
   144 				COutStandingQuery* query = NULL;
       
   145 				TInt index = 0;
       
   146 				for( index =0 ; index< iOutStandingQueryArray.Count();index++)
       
   147 					{
       
   148 					 query = iOutStandingQueryArray[index];
       
   149 					if(query->TransactionId() == header.Id() && query->SocketAddress() == aAddr)
       
   150 						{
       
   151 						break;	
       
   152 						}
       
   153 					}
       
   154 				if(index < iOutStandingQueryArray.Count())
       
   155 					{
       
   156 					CDnsMessage* message = &(query->DnsMessage());
       
   157 					TInt i = 0;
       
   158 					while(i++ <message->Header().AnswerCount())
       
   159 						{
       
   160 						ret->AppendAnswerL(message->Answers()[i]);	
       
   161 						}
       
   162 					}
       
   163 				
       
   164 				}
       
   165 				
       
   166 			if(aMessage.Header().IsTruncated())
       
   167 				{
       
   168 				IsLastTruncated = ETrue;
       
   169 				TTime time;
       
   170 				time.HomeTime();
       
   171 				time + TTimeIntervalMicroSeconds(500); 
       
   172 				COutStandingQuery* outstandingQuery = COutStandingQuery::NewL(ret,ret->Header().Id(),time,aAddr);
       
   173 				iOutStandingQueryArray.Append(outstandingQuery);
       
   174 				if(!IsActive())
       
   175 					{
       
   176 					SetActive();
       
   177 					At(time);	
       
   178 					}
       
   179 				
       
   180 				}
       
   181 			else
       
   182 				{
       
   183 				const TUint32 KMDnsAddr = INET_ADDR(224, 0, 0, 251);
       
   184 				TInetAddr addr(KMDnsAddr, KMdnsPort);
       
   185 				CSendMessageData* data = CSendMessageData::NewL(ret,ETrue,addr,*this);
       
   186 				MessageHandler().MessageQueue().QueueDnsMessageL(*data);	
       
   187 				}
       
   188 			CleanupStack::Pop();	
       
   189 			}
       
   190 		}
       
   191 	else
       
   192 		{
       
   193 		User::Leave(KErrGeneral);	
       
   194 		}
       
   195 	__FLOG(_L8("CQueryHandler::HandleIncomingPacketL - Exit"));		
       
   196 	}
       
   197 /*
       
   198  * Constructor
       
   199  * by default clienthandle is 0
       
   200  */	
       
   201 CQueryHandler::CQueryHandler(CMessageHandler& aMessageHandler):CBaseHandler(aMessageHandler)
       
   202 	{
       
   203 	
       
   204 	iClientHandle =0 ;	
       
   205 	}
       
   206 
       
   207 /*
       
   208  * Two phase constructor
       
   209  */
       
   210 void CQueryHandler::ConstructL()
       
   211 	{
       
   212 	__FLOG_OPEN(KMDNSSubsystem, KComponent);
       
   213 	__FLOG(_L8("CQueryHandler::ConstructL - Entry"));
       
   214 	CBaseHandler::ConstructL();
       
   215 	__FLOG(_L8("CQueryHandler::ConstructL - Exit"));
       
   216 	}
       
   217 	
       
   218 /*
       
   219  * compares the answer section and the authoritative entries in the cache .
       
   220  * If there is a match returns ETrue. which mean remove the entry , as the 
       
   221  * client is not interested in this record.
       
   222  * @param aAnswers array of records recieved from the external client.
       
   223  * @param aResourceData data from the cache to be checked with the answer section.
       
   224  */
       
   225 TBool CQueryHandler::SuppressDuplicateAnswerL(const RPointerArray<CDnsResourceData>& aAnswers,const CDnsResourceData& aResourceData)
       
   226 	{
       
   227 	__FLOG(_L8("CQueryHandler::SuppressDuplicateAnswerL - Entry"));
       
   228 	TBool ret =EFalse;
       
   229 	TInt answersCount = aAnswers.Count();
       
   230 	for(TInt i=0 ;i< answersCount;i++)
       
   231 		{
       
   232 		if(0 == aAnswers[i]->Name().Compare(aResourceData.Name()) && aAnswers[i]->Type() == aResourceData.Type())
       
   233 			{
       
   234 			switch(aResourceData.Type())
       
   235 				{
       
   236 					case EDnsType_A:
       
   237 						{
       
   238 						const CRdTypeA& rdata = (CRdTypeA&)(aResourceData);
       
   239 						const CRdTypeA* rEntry = static_cast<CRdTypeA*>(aAnswers[i]);
       
   240 						if(rdata.Address() == rEntry->Address() && rdata.Ttl() > rEntry->Ttl())
       
   241 							{
       
   242 							ret = ETrue;
       
   243 							}
       
   244 						break;
       
   245 						}
       
   246 					case EDnsType_PTR:
       
   247 						{
       
   248 						const CRdTypePtr& rdata = (CRdTypePtr&)(aResourceData);
       
   249 						const CRdTypePtr* rEntry = static_cast<CRdTypePtr*>(aAnswers[i]);
       
   250 						if(0 ==rdata.Name().Compare(rEntry->Name()) && rdata.Ttl() > rEntry->Ttl())
       
   251 							{
       
   252 							ret = ETrue;
       
   253 							}
       
   254 						break;
       
   255 						}
       
   256 					case EDnsType_SRV:
       
   257 						{
       
   258 						const CRdTypeSrv& rdata = (CRdTypeSrv&)(aResourceData);
       
   259 						const CRdTypeSrv* rEntry = static_cast<CRdTypeSrv*>(aAnswers[i]);
       
   260 						if(0 == rdata.Name().Compare(rEntry->Name()) && rdata.Port() == rEntry->Port() && rdata.Ttl() > rEntry->Ttl())
       
   261 							{
       
   262 							ret = ETrue;
       
   263 							}
       
   264 						break;
       
   265 						}
       
   266 					case EDnsType_TXT:
       
   267 						{
       
   268 						const CRdTypeTxt& rdata = (CRdTypeTxt&)(aResourceData);
       
   269 						const CRdTypeTxt* rEntry = static_cast<CRdTypeTxt*>(aAnswers[i]);
       
   270 						if(rdata.Text().Count() == rEntry->Text().Count())
       
   271 							{
       
   272 							for(TInt i =0; i< rdata.Text().Count();i++)
       
   273 								{
       
   274 								if(0 != rdata.Text()[i].Compare(rEntry->Text()[i]))
       
   275 									{
       
   276 									break;	
       
   277 									}
       
   278 								}
       
   279 							ret =ETrue;	
       
   280 							}
       
   281 						break;
       
   282 						}
       
   283 					default:
       
   284 						{
       
   285 						User::Invariant();	
       
   286 						}
       
   287 									
       
   288 				}
       
   289 			}
       
   290 		}
       
   291 	__FLOG(_L8("CQueryHandler::SuppressDuplicateAnswerL - Exit"));
       
   292 	return ret;
       
   293 	
       
   294 	}
       
   295 	
       
   296 /*
       
   297  * Handle query from the network of type *Any
       
   298  * @param aPacket packet to be sent to the network in response to the query.
       
   299  * @param aAnswers an array of answers in the query packet.
       
   300  * @param aEntry authoritative entries in the cache.
       
   301  */
       
   302 void CQueryHandler::HandleAnyQuestionL(CDnsMessage* aPacket,const RPointerArray<CDnsResourceData>& aAnswers,const CCacheEntry& aEntry)
       
   303 	{
       
   304 	__FLOG(_L8("CQueryHandler::HandleAnyQuestionL - Entry"));
       
   305 	if(aEntry.AddressRecord() && !SuppressDuplicateAnswerL(aAnswers,*(aEntry.AddressRecord())))
       
   306 		{
       
   307 		aPacket->AppendAnswerL(aEntry.AddressRecord()->CloneL());	
       
   308 		}
       
   309 	if(aEntry.PtrRecord() && !SuppressDuplicateAnswerL(aAnswers,*(aEntry.PtrRecord())))
       
   310 		{
       
   311 		aPacket->AppendAnswerL(aEntry.PtrRecord()->CloneL());	
       
   312 		}
       
   313 	if(aEntry.ServiceRecord() && !SuppressDuplicateAnswerL(aAnswers,*(aEntry.ServiceRecord())))
       
   314 		{
       
   315 		aPacket->AppendAnswerL(aEntry.ServiceRecord()->CloneL());	
       
   316 		}
       
   317 	if(aEntry.TxtRecord() && !SuppressDuplicateAnswerL(aAnswers,*(aEntry.TxtRecord())))
       
   318 		{
       
   319 		aPacket->AppendAnswerL(aEntry.TxtRecord()->CloneL());	
       
   320 		}
       
   321 	__FLOG(_L8("CQueryHandler::HandleAnyQuestionL - Exit"));
       
   322 	}
       
   323 
       
   324 /*
       
   325  * Handles query from the application.
       
   326  * 1.Query for the ptr entries are sent to the network by default.
       
   327  * 2.Query for the srv or txt record will be sent to the network only if it is not present in the cache
       
   328  * @param aMessage contains the query sent by the client.
       
   329  * @param aClientHandle sessionid of the client.
       
   330  */
       
   331 void CQueryHandler::ServiceClientQueryL(CDnsMessage* aMessage,TInt aClientHandle)
       
   332 	{
       
   333 	__FLOG(_L8("CQueryHandler::ServiceClientQueryL - Entry"));
       
   334 	TInt queryCount = aMessage->Header().QueryCount();
       
   335 	TBool isSendQueryToNw = EFalse;
       
   336 	for(TInt i =0 ; i< queryCount ; i++)
       
   337 		{
       
   338 		switch(aMessage->Queries()[i]->Type())
       
   339 			{
       
   340 			case EDnsType_A:
       
   341 				{
       
   342 				RPointerArray<CCacheEntry> iAddressRecords;
       
   343 				TInt err = KErrNone;
       
   344 				TRAPD(error ,err = MessageHandler().DnsCache().FindServiceL(iAddressRecords, aMessage->Queries()[i]->Name(), EDnsType_A));
       
   345 				if(error == KErrNone && err==KErrNotFound /*&& (NULL == iAddressRecords[0]->AddressRecord())*/)	
       
   346 					{
       
   347 					isSendQueryToNw = ETrue;	
       
   348 					}
       
   349 				else
       
   350 					{
       
   351 					CDnsQuestion* question = aMessage->Queries()[i];
       
   352 					const_cast <RPointerArray<CDnsQuestion>&>(aMessage->Queries()).Remove(i);
       
   353 					delete question;	
       
   354 					}
       
   355 				iAddressRecords.ResetAndDestroy();
       
   356 				iAddressRecords.Close();
       
   357 				break;	
       
   358 				}
       
   359 		
       
   360 			case EDnsType_PTR:
       
   361 			case EDnsQType_Any:
       
   362 				{
       
   363 				RPointerArray<CCacheEntry> ptrRecords;
       
   364 				TRAPD(error,MessageHandler().DnsCache().FindServiceL(ptrRecords,aMessage->Queries()[i]->Name(),EDnsType_PTR));
       
   365 				for(TInt index =0 ; (error == KErrNone) &&  index < ptrRecords.Count(); index++)
       
   366 					{
       
   367 					aMessage->AppendAnswerL(ptrRecords[index]->PtrRecord()->CloneL());	
       
   368 					}
       
   369 				isSendQueryToNw = ETrue;	
       
   370 				ptrRecords.ResetAndDestroy();
       
   371 				ptrRecords.Close();
       
   372 				break;
       
   373 				}
       
   374 			
       
   375 			case EDnsType_SRV:
       
   376 			case EDnsType_TXT:
       
   377 				{
       
   378 				RPointerArray<CCacheEntry> records;
       
   379 				TRAPD(error,MessageHandler().DnsCache().FindServiceL(records,aMessage->Queries()[i]->Name(),(TDnsType)aMessage->Queries()[i]->Type()));
       
   380 				if(error == KErrNone && 0 != records.Count())
       
   381 					{
       
   382 					CDnsQuestion* question = aMessage->Queries()[i];
       
   383 					const_cast <RPointerArray<CDnsQuestion>&>(aMessage->Queries()).Remove(i);
       
   384 					delete question;
       
   385 					}
       
   386 				else
       
   387 					{
       
   388 					isSendQueryToNw = ETrue;
       
   389 					}	
       
   390 				records.ResetAndDestroy();
       
   391 				records.Close();
       
   392 				break;
       
   393 				}	
       
   394 				
       
   395 					
       
   396 			}
       
   397 		}
       
   398 		
       
   399 	if(isSendQueryToNw)	
       
   400 		{
       
   401 		SendQueryL(aMessage,*this);
       
   402 		iClientHandle = aClientHandle;
       
   403 		//On succesfull sending the packet query handler will be notified there query will be 
       
   404 		// added to outstanding queue
       
   405 		}
       
   406 	else
       
   407 		{
       
   408 		delete aMessage;//as the question is not to be send .
       
   409 		TTime time;
       
   410 		time.HomeTime();
       
   411 		ScheduleOutStandingQueryL(aClientHandle,ETrue,time);	
       
   412 		}
       
   413 	__FLOG(_L8("CQueryHandler::ServiceClientQueryL - Exit"));		
       
   414 	}
       
   415 /*
       
   416  * Any queries to be sent to the network goes through this
       
   417  * @param aMessage contains the query to be sent.
       
   418  * @param aObserver callback which is interested in getting notified when 
       
   419  * a packet is successfully sent to the network. 
       
   420  */
       
   421 void CQueryHandler::SendQueryL(CDnsMessage* aMessage, MMessageHandler& aObserver)
       
   422 	{
       
   423 	__FLOG(_L8("CQueryHandler::SendQueryL - Entry"));
       
   424 	//
       
   425 	//TODO in case in case the size exceeds max size
       
   426 	//
       
   427 	TInetAddr addr(KMDnsAddr, KMdnsPort);
       
   428 	TBuf<255> buf;
       
   429 	addr.Output(buf);
       
   430 	CSendMessageData* data = CSendMessageData::NewL(aMessage,EFalse,addr,aObserver);
       
   431 	CleanupStack::PushL(data);
       
   432 	MessageHandler().MessageQueue().QueueDnsMessageL(*data);
       
   433 	CleanupStack::Pop();//data
       
   434 	__FLOG(_L8("CQueryHandler::SendQueryL - Exit"));
       
   435 	}	
       
   436 	    
       
   437 
       
   438 /*
       
   439  * Creates an outstanding query object from the client query and adds it to the outstanding query arry.
       
   440  * 1. For queries sent to the network ,waits for 120ms before sending the response back .
       
   441  * 2. For queries not sent to network ,reponse will be sent back immediately. 
       
   442  */
       
   443 void CQueryHandler::ScheduleOutStandingQueryL(TInt aClientHandle , TBool aTriggerNow, TTime aInTime)
       
   444 	{
       
   445 	__FLOG(_L8("CQueryHandler::ScheduleOutStandingQueryL - Entry"));
       
   446 	COutStandingQuery* outstandingQuery = COutStandingQuery::NewL(NULL,0,aInTime,NULL);
       
   447 	outstandingQuery->SetClientHandle(aClientHandle);
       
   448 	if(aTriggerNow)
       
   449 		{
       
   450 		if(IsActive())
       
   451 			{
       
   452 			Cancel();
       
   453 			}
       
   454 		iOutStandingQueryArray.Insert(outstandingQuery,0);
       
   455 		TRequestStatus* status = &iStatus;
       
   456 		SetActive();
       
   457 		User::RequestComplete(status,KErrNone);	
       
   458 		}
       
   459 	else
       
   460 		{
       
   461 		TInt pos = 0;
       
   462 		for(pos = 0 ; pos< iOutStandingQueryArray.Count(); pos++ )
       
   463 			{
       
   464 			if(outstandingQuery->GetAddTime().Int64() < iOutStandingQueryArray[pos]->GetAddTime().Int64() )
       
   465 				{
       
   466 				break;	
       
   467 				}
       
   468 			}
       
   469 		iOutStandingQueryArray.Insert(outstandingQuery, pos);
       
   470 		if(!IsActive())
       
   471 			{
       
   472 			TTime nextReview = iOutStandingQueryArray[0]->GetAddTime();
       
   473 			At(nextReview);	
       
   474 			}	
       
   475 		}	
       
   476 	__FLOG(_L8("CQueryHandler::ScheduleOutStandingQueryL - Exit"));	
       
   477 	}
       
   478 
       
   479 /*Only for the query recieved from the application there should be a delay
       
   480  * before sending the response .So wait for 120ms before sending the response
       
   481  * back to client.
       
   482  * Only for the query recieved from the client ,iClientHandle will not be NULL
       
   483  * @param aError is the error in case any in delivering the packet.
       
   484  */
       
   485 void CQueryHandler::OnPacketSendL(TInt /*aError*/)	
       
   486 	{
       
   487 	__FLOG(_L8("CQueryHandler::OnPacketSendL - Entry"));
       
   488 	if(iClientHandle != 0)
       
   489 		{
       
   490 		TTime time;
       
   491 		time.HomeTime();
       
   492 		time = time + TTimeIntervalMicroSeconds(120000);
       
   493 		ScheduleOutStandingQueryL(iClientHandle,EFalse,time);
       
   494 		iClientHandle =0 ;	
       
   495 		}
       
   496 	__FLOG(_L8("CQueryHandler::OnPacketSendL - Exit"));		
       
   497 	}