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:
// 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);
}
}
}
}