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:
// cmdnsprobemanager.cpp
//
//
/**
@file
@internalTechnology
*/
//User include
#include "e32math.h"
#include "cmdnsprobemanager.h"
//System include
#include <mdns/ccacheentry.h>
__FLOG_STMT(_LIT8(KComponent,"MDNSServer");)
/*
* Two phase constructor
* @param aCache reference to cache
* @param aMessageHandler reference to message handler.
* @param aAutoresolveEnabled True if name conflict to be handled.
*/
CMDNSProbeManager* CMDNSProbeManager::NewL(MDNSCacheMgr& aCache,CMessageHandler& aMessageHandler,TBool aAutoResolveEnabled)
{
CMDNSProbeManager* self = CMDNSProbeManager::NewLC(aCache,aMessageHandler,aAutoResolveEnabled);
CleanupStack::Pop(self);
return self;
}
static void CleanUpCache(TAny* aAny)
{
RPointerArray <CCacheEntry> * records = static_cast < RPointerArray <CCacheEntry>* > (aAny);
records->ResetAndDestroy();
records->Close();
}
/*
* Two phase constructor
* @param aCache reference to cache
* @param aMessageHandler reference to message handler.
* @param aAutoresolveEnabled True if name conflict to be handled.
*/
CMDNSProbeManager* CMDNSProbeManager::NewLC(MDNSCacheMgr& aCache,CMessageHandler& aMessageHandler,TBool aAutoResolveEnabled)
{
CMDNSProbeManager* self = new (ELeave)CMDNSProbeManager(aCache,aMessageHandler,aAutoResolveEnabled);
CleanupStack::PushL(self);
self->ConstructL();
return self;
}
/*
* Constructor
*/
CMDNSProbeManager::CMDNSProbeManager(MDNSCacheMgr& aCache,CMessageHandler& aMessageHandler,TBool aAutoResolveEnabled)
:CTimer(CActive::EPriorityStandard),iCache(aCache),iMessageHandler(aMessageHandler),iAutoResolveEnabled(aAutoResolveEnabled)
{
CActiveScheduler::Add(this);
}
/*
* Two phase Constructor
*/
void CMDNSProbeManager::ConstructL()
{
__FLOG_OPEN(KMDNSSubsystem, KComponent);
CTimer::ConstructL();
iCurrentProbeState = EIdle;
}
/*
* Destructor
*/
CMDNSProbeManager::~CMDNSProbeManager()
{
__FLOG(_L8("CMDNSProbeManager::~CMDNSProbeManager - Entry"));
iName.Close();
iTarget.Close();
iProbeName.Close();
iProbeType.Close();
__FLOG(_L8("CMDNSProbeManager::~CMDNSProbeManager - Exit"));
__FLOG_CLOSE;
}
/*
* State machine which probe send announcement and handle conflicts
* if autoresolve enabled.
*/
void CMDNSProbeManager::RunL()
{
__FLOG(_L8("CMDNSProbeManager::RunL - Entry"));
TTime currentTime;
if(iCurrentProbeState <= EProbeComplete)
{
if(DefensiveResponseL())
{
iCurrentProbeState = EStart;
if(iAutoResolveEnabled)
{
GenerateNonConflictingName();
}
// else { }// to intimate server of failure
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);
}
}
}
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();
iMessageHandler.Server().SetHostNameL(iName);
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("CMDNSProbeManager::RunL - Exit"));
}
/*
* Handles any leave from RunL
*/
TInt CMDNSProbeManager::RunError(TInt aError)
{
__FLOG(_L8("CMDNSProbeManager::RunError - Entry Exit"));
return aError;
}
/*
* Probe host
* @param aUnicast True if unicast or else multicast.
*/
void CMDNSProbeManager::SendProbeL(TBool aUnicast)
{
__FLOG(_L8("CMDNSProbeManager::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);
//Form the Record to be filled in the Authoritative Field of the Query
switch(iType)
{
case EDnsType_A:
{
CRdTypeA* addRec = FormAddressRecordL();
//Append to the Authoritative Section
message->AppendAuthorityL(addRec);
}
break;
case EDnsType_SRV:
{
CRdTypeSrv* srvRec = FormServiceRecordL();
//Append to the Authoritative Section
message->AppendAuthorityL(srvRec);
}
break;
}
//Send the query
iMessageHandler.SendQueryL(message,*this);
CleanupStack::Pop();//question
CleanupStack::Pop();//message
__FLOG(_L8("CMDNSProbeManager::SendProbeL - Exit"));
}
/*
* Send an announcement if probing is successfull.
*/
void CMDNSProbeManager::SendAnnouncementL()
{
__FLOG(_L8("CMDNSProbeManager::SendAnnouncementL - Entry"));
RPointerArray<CCacheEntry> entries;
//An API in Cache Interface that returns a list of all Authoritative records
iCache.AuthoritativeEntriesL(entries);
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
CDnsMessage* message = CDnsMessage::NewL(0,EFalse);
CleanupStack::PushL(message);
TDnsHeader header(message->Header());
header.SetAuthoritative(ETrue);
message->SetHeader(header);
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
iMessageHandler.SendQueryL(message,*this);
CleanupStack::Pop();//message
entries.ResetAndDestroy();
entries.Close();
}
__FLOG(_L8("CMDNSProbeManager::SendAnnouncementL - Exit"));
}
/*
* Returns ture hostname exists in the cache.
* True means there is already a record in the network with the same name.
*/
TBool CMDNSProbeManager::DefensiveResponseL()
{
__FLOG(_L8("CMDNSProbeManager::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);
iCache.FindServiceL(entries,buffer,EDnsQType_Any);
if(entries.Count()>0)
{
probeFailed = ETrue;
}
CleanupStack::PopAndDestroy();
entries.ResetAndDestroy();
entries.Close();
__FLOG(_L8("CMDNSProbeManager::DefensiveResponseL - Exit"));
return probeFailed;
}
TInt CMDNSProbeManager::StartNameProbeL(const TDesC8& aHostName,TInetAddr aAddr)
{
__FLOG(_L8("CMDNSProbeManager::StartNameProbeL - Entry"));
iName.Close();
iName.CreateL(aHostName);
iProbeName.Close();
iProbeName.CreateL(KMaxLength);
iProbeName.Append(iName);
iAddr = aAddr;
iCurrentProbeState = EStart;
iType = EDnsType_A;
iMessageHandler.SetStateHostProbing(ETrue);
Schedule();
__FLOG(_L8("CMDNSProbeManager::StartNameProbeL - Exit"));
return KErrNone;
}
/*
* Start probing the service.
*/
TInt CMDNSProbeManager::StartServiceProbeL(const TDesC8& aServiceName,const TDesC8& aTargetMachine,TUint16 aPort)
{
__FLOG(_L8("CMDNSProbeManager::StartServiceProbeL - Entry"));
iName.Close();
iName.CreateL(aServiceName);
iTarget.Close();
iTarget.CreateL(aTargetMachine);
iPort = aPort;
iType = EDnsType_SRV;
iCurrentProbeState = EStart;
Schedule();
__FLOG(_L8("CMDNSProbeManager::StartServiceProbeL - Exit"));
return KErrNone;
}
/*
* Callback method
* Will be notified on successfully sending a packet to the network .
* State will be EIdle when an address is successully announced twice.
*/
void CMDNSProbeManager::OnPacketSendL(TInt /*aError*/)
{
__FLOG(_L8("CMDNSProbeManager::OnPacketSendL - Entry"));
if( iCurrentProbeState != EIdle)
{
After(KProbeDelay*1000);//Give more delay than this
return;
}
iMessageHandler.SetStateHostProbing(EFalse);
__FLOG(_L8("CMDNSProbeManager::OnPacketSendL - Exit"));
}
/*
* Create an address record
* @return returns an address record created.
*/
CRdTypeA* CMDNSProbeManager::FormAddressRecordL()
{
__FLOG(_L8("CMDNSProbeManager::FormAddressRecordL - Entry"));
CRdTypeA* addRec = CRdTypeA::NewL();
CleanupStack::PushL(addRec);
TInt dataLength(4);
addRec->SetNameL(iName);
addRec->SetType(EDnsType_A);
addRec->SetClass(EDnsClass_IN);
addRec->SetFlushBit(EFalse);
addRec->SetAddr(iAddr);
addRec->SetTtl(120);
CleanupStack::Pop();//addRec
__FLOG(_L8("CMDNSProbeManager::FormAddressRecordL - Exit"));
return addRec;
}
/*
* Create a service record
* @return retrun the newly created service record.
*/
CRdTypeSrv* CMDNSProbeManager::FormServiceRecordL()
{
__FLOG(_L8("CMDNSProbeManager::FormServiceRecordL - Entry"));
CRdTypeSrv* srvRec = CRdTypeSrv::NewL();
CleanupStack::PushL(srvRec);
srvRec->SetClass(EDnsClass_IN);
srvRec->SetFlushBit(EFalse);
srvRec->SetNameL(iName);
srvRec->SetPort(iPort);
srvRec->SetPriority(/*Default Priority*/0 );
srvRec->SetTargetL(iTarget);
srvRec->SetTtl(120*60);
srvRec->SetType(EDnsType_SRV);
srvRec->SetWeight(/*Default Weight*/ 0);
CleanupStack::Pop();//srvRec
__FLOG(_L8("CMDNSProbeManager::FormServiceRecordL - Exit"));
return srvRec;
}
void CMDNSProbeManager::InsertInCache()
{
__FLOG(_L8("CMDNSProbeManager::InsertInCache - Entry"));
TBuf8<KMaxLength> name;
_LIT8(KDot,".");
CDnsResourceData* data = NULL;
switch(iType)
{
case EDnsType_A:
{
data = FormAddressRecordL();
}
break;
case EDnsType_SRV:
{
data = FormServiceRecordL();
}
break;
}
//Append a "." at the end of the name
name.Copy(iName);
name.Append(KDot);
data->SetNameL(name);
iCache.UpdateCacheL(*data,ETrue,0/*session Id*/);
delete data;
__FLOG(_L8("CMDNSProbeManager::InsertInCache - Exit"));
}
/*
* If autoresolve enable a non conflict name will be generated using this.
* @return returns the probecount.
*/
TInt CMDNSProbeManager::GenerateNonConflictingName()
{
/*
__FLOG(_L8("CMDNSProbeManager::GenerateNonConflictingName - Entry"));
_LIT8(KDot,".");
if(iProbeCounter)
{
//From 2nd time, chop the last 3 characters
iProbeName.SetLength(iProbeName.Length() - 3);
}
++iProbeCounter;
TBuf8 <KMaxLength> newName;
newName.Append(iProbeName);
_LIT8(KOpenBrace,"(");
newName.Append(KOpenBrace);
newName.AppendNum(iProbeCounter);
_LIT8(KCloseBrace,")");
newName.Append(KCloseBrace);
iProbeName.Close();
iProbeName.Create(KMaxNameLength);
iProbeName.Append(newName);
iName.Close();
iName.Create(KMaxLength);
iName.Append(iProbeName);
iName.Append(KDot);
iName.Append(iProbeType);
__FLOG(_L8("CMDNSProbeManager::GenerateNonConflictingName - Exit"));
return iProbeCounter;
*/
//-------------------------------------------------------
__FLOG(_L8("CMDNSProbeManager::GenerateNonConflictingName- Entry"));
_LIT8(KDot,".");
++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(iName);
__FLOG(_L8("CMDNSProbeManager::GenerateNonConflictingName- Exit"));
return iProbeCounter;
}
/*
* Self complete the request.
*/
void CMDNSProbeManager::Schedule()
{
__FLOG(_L8("CMDNSProbeManager::Schedule - Entry"));
TRequestStatus* status(&iStatus);
*status = KRequestPending;
SetActive();
User::RequestComplete(status, KErrNone);
__FLOG(_L8("CMDNSProbeManager::Schedule - Exit"));
}