zeroconf/server/src/cadvertizehandler.cpp
changeset 14 da856f45b798
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zeroconf/server/src/cadvertizehandler.cpp	Thu Jun 24 19:09:47 2010 +0530
@@ -0,0 +1,470 @@
+// 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:
+// cadvertizehandler.cpp
+// 
+//
+/**
+@file
+@internalTechnology
+*/
+//System include 
+#include <mdns/ccacheentry.h>
+
+//UserInclude
+#include "cadvertizehandler.h"
+__FLOG_STMT(_LIT8(KComponent,"MDNSServer");)
+
+/*
+ * Two phase constructor
+ * @param aMessagHandler areference to messagehandler object.
+ * @param aAutoResolveEnabled If true sercice name conflict will be handled.
+ * aAutoResolveEnabled is True by default
+ */
+CAdvertizeHandler* CAdvertizeHandler::NewL(CMessageHandler& aMessageHandler,TBool aAutoResolveEnabled)
+	{
+	CAdvertizeHandler* self = new (ELeave) CAdvertizeHandler(aMessageHandler,aAutoResolveEnabled);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop();
+	return self;
+	}
+	
+/*
+ * Destructor
+ */
+CAdvertizeHandler::~CAdvertizeHandler()
+	{
+	__FLOG(_L8("CAdvertizeHandler::~CAdvertizeHandler- Entry"));
+	iData.ResetAndDestroy();
+	iData.Close();
+	iName.Close();
+	iProbeName.Close();
+	iProbeType.Close();
+	__FLOG(_L8("CAdvertizeHandler::~CAdvertizeHandler- Exit"));
+	__FLOG_CLOSE;
+	}
+/*
+ * No incoming packet for this so no need to handle
+ */	
+void CAdvertizeHandler::HandleIncomingPacketL(CDnsMessage& /*aMessage*/)
+	{
+	__FLOG(_L8("CAdvertizeHandler::HandleIncomingPacketL- Entry"));
+	//Nothing To Do ............
+	__FLOG(_L8("CAdvertizeHandler::HandleIncomingPacketL- Exit"));
+	}
+	
+static void CleanUpCache(TAny* aAny)
+    {
+    
+    //RPointerArray <CCacheEntry>  records = static_cast < RPointerArray <CCacheEntry> > (*aAny);
+    RPointerArray <CCacheEntry>*  records = static_cast < RPointerArray <CCacheEntry> *> (aAny);
+    records->ResetAndDestroy();
+    records->Close();
+    }
+/*
+ * Constructor
+ * @param aMessageHandler reference to messagehandler
+ * @param aAutoResolveEnabled will be true by default ,if true servicename conflict
+ * will be handled by default
+ */
+CAdvertizeHandler::CAdvertizeHandler(CMessageHandler& aMessageHandler,TBool aAutoResolveEnabled):CBaseHandler(aMessageHandler)
+	{
+	__FLOG(_L8("CAdvertizeHandler::CAdvertizeHandler- Entry"));
+	iAutoResolveEnabled = aAutoResolveEnabled;
+	iCurrentProbeState = EIdle;
+	iProbeCounter = 0 ;
+	__FLOG(_L8("CAdvertizeHandler::CAdvertizeHandler- Exit"));
+	}
+	
+/*
+ * Twophase constructor
+ */
+void CAdvertizeHandler::ConstructL()
+	{
+	__FLOG_OPEN(KMDNSSubsystem, KComponent);
+	__FLOG(_L8("CAdvertizeHandler::ConstructL- Entry"));
+	CBaseHandler::ConstructL();
+	//Nothing 
+	__FLOG(_L8("CAdvertizeHandler::ConstructL- Exit"));
+	}
+/*
+ * Keeps a copy of the records to be published and starts the active object.
+ * @param aData an array of records to be published.
+ * @param aSessionId session id initiating the advertizement.
+ */
+void CAdvertizeHandler::AdvertizePacketL(const RPointerArray<CDnsResourceData> aData, TInt aSessionId,TBool aIsUpdate)
+    {
+    __FLOG(_L8("CAdvertizeHandler::AdvertizePacketL- Entry"));
+    iName.Close();
+    iName.Create(aData[0]->Name());
+    iProbeName.Close();
+    iProbeName.CreateL(KMaxLength);
+    iProbeName.Append(iName);
+    iCurrentProbeState = EStart;
+    iData.ResetAndDestroy();
+    for(TInt i =0 ; i<aData.Count();i++)
+        {
+        iData.AppendL(aData[i]);
+        }
+     // assign the session id so that, we can send bye-bye packet when the session is closed
+    iSessionId = aSessionId;
+    if(aIsUpdate)
+        {
+        iCurrentProbeState = EIdle;
+        TBool isExist = DefensiveResponseL();
+        if(!isExist)
+            {
+            User::Leave(KErrNotFound);
+            }
+        SendAnnouncementL();
+        InsertInCache();
+        return;
+        }
+    Schedule();
+    __FLOG(_L8("CAdvertizeHandler::AdvertizePacketL- Exit"));
+    }
+/*
+ * This is a callback function will be notified whenever a packet sent from this
+ * client is sent to the network.
+ * @param aError any error in sending the packet.
+ */
+void CAdvertizeHandler::OnPacketSendL(TInt aError)
+    {
+    __FLOG(_L8("CAdvertizeHandler::OnPacketSendL- Entry"));
+    if(aError != KErrNone)
+        {
+        MessageHandler().NotifyServicePublishL(iName,EFail,iSessionId);
+        }
+    
+    if( iCurrentProbeState != EIdle)
+            {
+            After(KProbeDelay*1000);//Give more delay than this
+            return;
+            }
+    //When a service is published successfully ,iCurrentProbe state will be 
+    //EIdle ,this state change will be used to notify the client .
+    MessageHandler().NotifyServicePublishL(iName,ESuccess,iSessionId);
+    __FLOG(_L8("CAdvertizeHandler::OnPacketSendL- Exit"));
+    }
+
+void CAdvertizeHandler::RunL()
+    {    
+    __FLOG(_L8("CAdvertizeHandler::RunL- Entry"));
+    TTime currentTime;
+
+    if(iCurrentProbeState <= EProbeComplete)
+        {
+        if(DefensiveResponseL())
+            {       
+            iCurrentProbeState = EStart;                
+            if(iAutoResolveEnabled)
+                {
+                GenerateNonConflictingName(); 
+                if(iProbeCounter >= KMaxFailures)
+                    {
+                    //If Probe Failure rate is greater then 15 per second, wait for 
+                    //5 seconds before probing again
+                    iProbeCounter = 0;
+                    After(5*1000*1000);             
+                    }
+                }       
+              else //  to intimate server of failure
+                  {
+                  MessageHandler().NotifyServicePublishL(iName,EConflictWithoutAutoResolve,iSessionId);
+                  }
+            
+            }       
+        }
+
+    switch(iCurrentProbeState)
+        {       
+        case EIdle:
+        break;//Do Nothing
+    
+        case EStart:
+            {
+            currentTime.UniversalTime();
+            TInt64 randseed = currentTime.Int64();                  
+    
+            //Random time between 0 & 250 ms
+            TInt delay = Math::Rand(randseed) % 250;
+            iCurrentProbeState = EProbeFirstUnicast;
+            //Convert to microsecond
+    
+            After(delay*1000);          
+            }               
+            break;
+    
+        case EProbeFirstUnicast:
+            {
+            SendProbeL(ETrue);  
+            iCurrentProbeState = EProbeSecondUnicast;                           
+            }
+            break;
+    
+        case EProbeSecondUnicast:
+            {               
+            SendProbeL(ETrue);  
+            iCurrentProbeState = EProbeMulticast;                                       
+            }
+            break;      
+    
+        case EProbeMulticast:
+            {
+            SendProbeL(EFalse); 
+            iCurrentProbeState = EProbeComplete;            
+            }
+            break;
+    
+        case EProbeComplete:
+            {
+            iCurrentProbeState = EFirstAnnouncement;
+            //Probe succeeded ,insert in cache 
+            InsertInCache();    
+            After(KProbeDelay*1000);
+            //Some Delay ?? Required or defensive response HAS TO come within 750ms?
+            }
+            break;
+    
+        case EFirstAnnouncement:
+            {
+            SendAnnouncementL();
+            iCurrentProbeState = ESecondAnnouncement;
+            //After(1000);// A Millisecond delay: Required??    
+            }
+            break;
+    
+        case ESecondAnnouncement:
+            {
+            SendAnnouncementL();
+            iCurrentProbeState = EIdle;
+            }
+            break;  
+        }
+    __FLOG(_L8("CAdvertizeHandler::RunL- Exit"));
+    }
+/*
+ * Handles any leave in RunL
+ * @param aError error with which runL leaves.
+ */
+TInt CAdvertizeHandler::RunError(TInt aError)
+    {
+    __FLOG(_L8("CAdvertizeHandler::RunError- Entry"));
+    return aError;
+    }
+
+/*
+ * Self completes the request.
+ */
+void CAdvertizeHandler::Schedule()
+    {
+    __FLOG(_L8("CAdvertizeHandler::Schedule- Entry"));
+    TRequestStatus* status(&iStatus);
+    *status = KRequestPending;
+    SetActive();
+    User::RequestComplete(status, KErrNone);
+    __FLOG(_L8("CAdvertizeHandler::Schedule- Exit"));
+    }
+
+/*
+ * Function will be called when there is a conflict.
+ * this will change the publishing name by appending a 
+ * number at the end of it. 
+ * @return iProbeCounter returns probecounter value.
+ */
+TInt CAdvertizeHandler::GenerateNonConflictingName()
+    {
+    __FLOG(_L8("CAdvertizeHandler::GenerateNonConflictingName- Entry"));
+    _LIT8(KDot,".");
+
+        RBuf8 oldName;
+        oldName.CreateL(iName);
+        
+        ++iProbeCounter;
+        TBuf8 <KMaxLength> newName;
+        iName.Close();  
+        iName.Create(KMaxLength);
+        iName.Append(iProbeName);
+        
+        TInt pos = iName.Locate('.');
+        _LIT8(KOpenBrace,"(");
+        newName.Append(KOpenBrace);
+        newName.AppendNum(iProbeCounter);
+        _LIT8(KCloseBrace,")");
+        newName.Append(KCloseBrace);
+        iName.Insert(pos,newName); 
+        ChangeDomainL(oldName);
+        oldName.Close();
+        __FLOG(_L8("CAdvertizeHandler::GenerateNonConflictingName- Exit"));
+        return iProbeCounter;
+    }
+
+/*
+ * Finds whether the service already exists in the network .
+ * If service is in the network ,same will be present in the cache.
+ * @return probFailed returns true if probe has failed.
+ */
+TBool CAdvertizeHandler::DefensiveResponseL()
+    {
+    __FLOG(_L8("CAdvertizeHandler::DefensiveResponseL- Entry"));
+    TBool probeFailed(EFalse);      
+    RPointerArray <CCacheEntry> entries;
+    CleanupStack::PushL(TCleanupItem(TCleanupOperation(&CleanUpCache),&entries));
+            
+     _LIT8(KDot,".");
+     TBuf8 <KMaxLength> buffer;  
+        
+     buffer.Copy(iName);
+     buffer.Append(KDot);
+     MessageHandler().DnsCache().FindServiceL(entries,buffer,EDnsQType_Any);   
+     if(entries.Count()>0)
+        {
+        probeFailed = ETrue;
+        }
+     CleanupStack::PopAndDestroy();	
+     //entries.ResetAndDestroy();
+     //entries.Close();
+     __FLOG(_L8("CAdvertizeHandler::DefensiveResponseL- Exit"));
+     return probeFailed;
+    }
+
+/*
+ * creates a DnsMessage object and send it to the messagehandler to handle it.
+ * @param aUnicast true if the packet to be sent is an unicast one;false for multicast.
+ * 
+ */
+void CAdvertizeHandler::SendProbeL(TBool aUnicast)
+    {
+    __FLOG(_L8("CAdvertizeHandler::SendProbeL- Entry"));
+    //Construct DNS Message
+    CDnsMessage* message = CDnsMessage::NewL(0,ETrue);
+    CleanupStack::PushL(message);
+
+    //Form the Query/Question part of the message
+    CDnsQuestion* question = CDnsQuestion::NewL();
+    CleanupStack::PushL(question);
+    question->SetNameL(iName);
+    question->SetClass(EDnsClass_IN);
+    question->SetType(EDnsQType_Any);
+    if(aUnicast)
+        {
+        question->SetUnicast(ETrue);
+        }
+
+    //Append the Query to the Message
+    message->AppendQueryL(question);
+    //Append to the Authoritative Section
+    for(TInt i =0 ; i < iData.Count();i++)
+        {
+        message->AppendAuthorityL(iData[i]->CloneL());
+        }
+         
+    //Send the query
+    MessageHandler().SendQueryL(message,*this);
+
+    CleanupStack::Pop();//question
+    CleanupStack::Pop();//message
+    __FLOG(_L8("CAdvertizeHandler::SendProbeL- Exit"));
+    }
+
+/*
+ * If probing is successfull ,new service record will 
+ * be added to the cache using this.
+ */
+void CAdvertizeHandler::InsertInCache()
+    {
+    __FLOG(_L8("CAdvertizeHandler::InsertInCache- Entry"));
+    TBuf8<KMaxLength> name;
+    _LIT8(KDot,".");
+    //TODO name should be appended with dot
+    
+    for(TInt i =0 ; i<iData.Count();i++)
+        {
+        //Append  a "." at the end of the name
+       name.Copy(iData[i]->Name());
+       name.Append(KDot);  
+       iData[i]->SetNameL(name);
+       MessageHandler().DnsCache().UpdateCacheL(*(iData[i]),ETrue,iSessionId);
+        }
+    __FLOG(_L8("CAdvertizeHandler::InsertInCache- Exit"));   
+    }
+
+/*
+ * Announces the new service.
+ */
+void CAdvertizeHandler::SendAnnouncementL()
+    {
+    __FLOG(_L8("CAdvertizeHandler::SendAnnouncementL- Entry"));
+    RPointerArray<CCacheEntry> entries;
+    //An API in Cache Interface that returns a list of all Authoritative records
+    MessageHandler().DnsCache().AuthoritativeEntriesL(entries);
+    CDnsMessage* message = CDnsMessage::NewL(0,EFalse);
+    CleanupStack::PushL(message);  
+    
+    TDnsHeader header(message->Header());
+    header.SetAuthoritative(ETrue);
+    message->SetHeader(header);
+
+    //Append to the Authoritative Section
+    for(TInt i =0 ; i < iData.Count();i++)
+        {
+        message->AppendAuthorityL(iData[i]->CloneL());
+        }
+             
+/*    
+    for(TInt index =0; index<entries.Count();index++)
+        {
+        CCacheEntry* entry = entries[index];
+        //Send Announcements for all the Services we have published, and entered in Cache           
+            
+
+        if(entry->AddressRecord())
+            message->AppendAnswerL(entry->AddressRecord()->CloneL());
+        if(entry->ServiceRecord())
+            message->AppendAnswerL(entry->ServiceRecord()->CloneL());
+        if(entry->PtrRecord())
+            message->AppendAnswerL(entry->PtrRecord()->CloneL());
+        if(entry->TxtRecord())
+            message->AppendAnswerL(entry->TxtRecord()->CloneL());
+               
+        //should've been a new API sendresponse
+                  
+        }
+*/        
+    MessageHandler().SendQueryL(message,*this);
+    CleanupStack::Pop();//message
+    entries.ResetAndDestroy();
+    entries.Close();
+    __FLOG(_L8("CAdvertizeHandler::SendAnnouncementL- Exit"));
+    }
+void CAdvertizeHandler::ChangeDomainL(TDesC8& aName)
+    {
+    for(TInt i =0 ; i<iData.Count();i++)
+        {
+        if(iData[i]->Name().Compare(aName) == 0)
+            {
+            iData[i]->SetNameL(iName);
+            }
+        if(iData[i]->Type()==EDnsType_PTR)
+            {
+            CRdTypePtr* ptr = static_cast<CRdTypePtr*>(iData[i]);
+            if(ptr->DomainName().Compare(aName) == 0)
+                {
+                ptr->SetDomainNameL(iName);
+                }
+            
+            }
+        
+        }
+    }