tcpiputils/dnd/src/dns_sock.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 31 Aug 2010 16:45:15 +0300
branchRCL_3
changeset 21 abbed5a4b42a
parent 20 7e41d162e158
child 22 8d540f55e491
permissions -rw-r--r--
Revision: 201035 Kit: 201035

// Copyright (c) 2004-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:
// dns_sock.cpp - the main client module of the DNS.
//

#include <e32math.h>
#include <networking/dnd_err.h>
#include <in_sock.h>
#include "dns_sock.h"
#include "message.h"
#include "inet6log.h"
#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY 
#include <es_enum.h>
#endif


/**
* Max number of consecutive RunL() calls with error condition.
* When this limit is exceeded, the socket it closed.
*/
const TUint KSocketErrorLimit = 20;

//seeds cannot be negative number. Need mask to seed to positive number.
const TInt KNegativeNumberMask = -1;

//
// TRequestQueue
// *************
//
class TRequestQueue : public TDblQue<TDnsRequest>
	{
public:
	// Construct an empty request queue
	TRequestQueue();
	// Construct new request queue and MOVE all requests from another into it
	TRequestQueue(TRequestQueue &);
	// Remove the first request from a queue, return NULL if queue is empty
	TDnsRequest *Remove();
	};


TRequestQueue::TRequestQueue()
	: TDblQue<TDnsRequest>(_FOFF(TDnsRequest, iQueueLink))
	{
	}

TRequestQueue::TRequestQueue(TRequestQueue &aQueue)
	: TDblQue<TDnsRequest>(_FOFF(TDnsRequest, iQueueLink))
	{
	if (aQueue.IsEmpty())
		Reset();
	else
		{
		iHead = aQueue.iHead;
		iHead.iPrev->iNext = &iHead;
		iHead.iNext->iPrev = &iHead;
		}
	aQueue.Reset();
	}

TDnsRequest *TRequestQueue::Remove()
	{
	if (!IsEmpty())
		{
		TDnsRequest *rq = First();
		rq->iQueueLink.Deque();
		return rq;
		}
	return NULL;
	}

class CDnsSocketReader;

// CDnsSocketWriter
// ****************
// Internal "hidden" class to do all the work
class CDnsSocketWriter : public CActive
    {
	CDnsSocketWriter(CDnsSocket &aMaster);
public:
	~CDnsSocketWriter();
	void ConstructL();

	// Create a basic (UDP) reader/writer (not activated)
	static CDnsSocketWriter *NewL(CDnsSocket &aMaster, TInt aTTL);
	// Create a TCP writer/reader (not activated)
	static CDnsSocketWriter *NewTcpL(CDnsSocket &aMaster, const TInetAddr &aServer, TInt aTTL);
	// Create a TCP listening writer/reader (not activated)
	static CDnsSocketWriter *NewTcpListenL(CDnsSocket &aMaster, TInt aTTL);

	// Test if a address matches current connection
	TBool Match(const TInetAddr &aServer) const;

	// (Re)Queue the request for processing with new ID
	void Queue(TDnsRequest &aRequest, const TInt aId = -1);
	// Cancel the request and dequeue it
	void Remove(TDnsRequest &aRequest);
	// Change the bind port (and address if specified)
	void SetBind(const TInetAddr &aBind);
	// Set hoplimit for for transmitted packets.
	void SetHoplimit(const TInt aTTL);
	 // Open and activate the UDP socket for DNS traffic (unless already done)
	TInt ActivateSocket();
	// Process socket errors
	TInt HandleError(TInt aReason, const TInetAddr *aServer = NULL);
	// Close and deactivate the UDP socket for DNS traffic
	void DeactivateSocket(TInt aReason); 
	// Handle receive compeleted, keep receiver active
	void RunReader(const TMsgBuf &aMsg, const TInetAddr &aFrom);
	// Handle send completed, keep sender active (if work to do)
	void RunWriter(TRequestStatus &aStatus);
 	//seed id generator
 	TInt64 SeedIdGenerator(TInt64 aSequence);
	// Return a reference to the RSocket
	inline RSocket &Socket() { return iSocket; }
	inline TBool IsTCP() const { return iTCP != 0; }
	inline TBool IsListen() const { return iListen != 0; }
	inline TBool IsOpened() const { return iOpened; }
	inline void ResetErrorCount() { iErrorCount = 0;}
	// A link chain used by the CDnsSocket to keep track of multiple writers
	CDnsSocketWriter *iNext;
	
private:
	void RunL();
	void DoCancel();
#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY	
	TBool CanUseConnection();
#endif

	CDnsSocket &iMaster;		//< The connection to the CDnsSocket owning this
	RSocket iSocket;			//< The DNS socket
#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY 
	RConnection iAttachedConn;	//< The connection on which the DNS socket may be opened on
#endif		
	TUint iDeactivateCount;		//< Count number of deactivations (needed in reply processing)
	TUint iErrorCount;			//< Count number of consecutive errors in RunL (read and write)
	TUint iListen;				//< = 1, if socket is listening TCP socket, waiting on accept.
	TUint iTCP;					//< = 1, if socket is a connected TCP socket
	TUint iOpened:1;			//< = 1, if the socket is open
	TUint iRunWriter:1;			//< = 1, if executing RunWriter (callback safeguard)
	TUint iRunReader:1;			//< = 1, if executing RunReader (callback safeguard)	
	TInt iTTL;					//< The TTL/Hoplimit to be used
	TInt64 iSequence;			//< Used in computing the ID value for the queries
	TBuf8<KDnsMaxMessage> iOutMsg;	//< For sending the outgoing message
	TInetAddr iBind;			//< Address/Port to bind the socket.
	TInetAddr iTo;				//< Filled with the destination address of the query
	TRequestQueue iSendQueue;	//< Requests waiting to be sent
	TRequestQueue iWaitQueue;	//< Requests waiting for a reply
	TDnsRequest *iSending;		//< The request currently being sent, if non-NULL
	CDnsSocketReader *iReader;	//< The reader object
	};

void TDnsRequest::Cancel()
	{
	CDnsSocketWriter *const writer = iQueueLink.Writer();
	if (writer)
		writer->Remove(*this);
	}


// CDnsSocketReader
// ****************
// Internal "hidden" class for hanlding asynchronous reading
class CDnsSocketReader : public CActive
	{
	friend class CDnsSocketWriter;
public:
	CDnsSocketReader(CDnsSocketWriter &aWriter);
	~CDnsSocketReader();
	void ConstructL(TUint aDnsMaxMessage);
	void Activate();

private:
	void RunL();
	void DoCancel();

	CDnsSocketWriter &iWriter;
	TUint iReadLength:1;
	TInetAddr iFrom;		//< Filled with source address of a received packet
	HBufC8 *iInMsg;			//< The real allocated buffer
	TInt iAllocatedLength;	//< The real max length of the buffer
	TPtr8 iBuf;				//< The current receive buffer
	};

CDnsSocketWriter::CDnsSocketWriter(CDnsSocket &aMaster) : CActive(0), iMaster(aMaster)
	{
	LOG(Log::Printf(_L("CDnsSocketWriter[%u]::CDnsSocketWriter([%u])"), this, &aMaster));
	CActiveScheduler::Add(this);

	TTime seed;
	seed.UniversalTime();
	iSequence = seed.Int64();
	}

void CDnsSocketWriter::ConstructL()
	{
	iReader = new (ELeave) CDnsSocketReader(*this);
	iReader->ConstructL(iMaster.iMaxDnsMessage);
	}


CDnsSocketWriter *CDnsSocketWriter::NewL(CDnsSocket &aMaster, TInt aTTL)
	{
	CDnsSocketWriter *const writer = new (ELeave) CDnsSocketWriter(aMaster);
	CleanupStack::PushL(writer);
	writer->iTTL = aTTL;
	writer->ConstructL();
	CleanupStack::Pop();
	return writer;
	}

CDnsSocketWriter *CDnsSocketWriter::NewTcpL(CDnsSocket &aMaster, const TInetAddr &aServer, TInt aTTL)
	{
	CDnsSocketWriter *const writer = NewL(aMaster, aTTL);
	// Note: with TCP iTo is dummy!
	writer->iTo = aServer;
	writer->iTCP = 1;
	return writer;
	}

CDnsSocketWriter *CDnsSocketWriter::NewTcpListenL(CDnsSocket &aMaster, TInt aTTL)
	{
	CDnsSocketWriter *const writer = NewL(aMaster, aTTL);
	writer->iTCP = 1;
	writer->iListen = 1;
	return writer;
	}



CDnsSocketWriter::~CDnsSocketWriter()
	{
	// As this class is managed by CDnsSocket, it is impossible
	// to get here if IsActive(), or with requests in any queue.
	// (DeactivateSocket is ALWAYS called before destructor)
	//
	ASSERT(!IsActive());
	ASSERT(iSendQueue.IsEmpty());
	ASSERT(iWaitQueue.IsEmpty());

	delete iReader;

	Cancel();	// should not be needed...
	if (IsAdded())
		Deque();
#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY		
	iAttachedConn.Close();
#endif	
	LOG(Log::Printf(_L("CDnsSocketWriter[%u]::~CDnsSocketWriter()"), this));
	}

TBool CDnsSocketWriter::Match(const TInetAddr &aServer) const
	{
	// The match applies only to connected TCP readers/writers
	return iTCP && !iListen && aServer.CmpAddr(iTo) && aServer.Scope() == iTo.Scope();
	}

//
// RunReader
//
void CDnsSocketWriter::RunReader(const TMsgBuf &aMsg, const TInetAddr &aFrom)
	{
	if (iRunReader)
		return;
	iRunReader = 1;
#ifdef _LOG
		{
		TBuf<50> tmp;
		aFrom.OutputWithScope(tmp);
		Log::Printf(_L("CDnsSocketWriter[%u]::RunReader() read %d bytes from %S#%d"), this, (TInt)aMsg.Length(),
			&tmp, aFrom.Port());
		}
#endif
	if (aMsg.Length() >= TInt(sizeof(TDndHeader)))
		{
		const TDndHeader &hdr = aMsg.Header();
		const TUint16 id = (TUint16)hdr.ID();

		if (hdr.QR())
			{
			//
			// This is a reply message, match it with
			// a waiting request, if any...
			//
			// Things get somewhat tricky, because almost anything
			// can happen inside the Reply (like DeactivateSocket,
			// ActivateSocketL, removal of any requests, requeing
			// for sent, etc...
			// Grab the current list into separate list. Note, that
			// Query or Reply callbacks are allowed to remove entries
			// from this temporary queue (the TDnsRequest::Cancel and
			// CDnsSocket::Redmove() still work correctly--they don't
			// care which chain the request belongs!),
			TRequestQueue reply(iWaitQueue);
			TUint mark = iDeactivateCount;
			TDnsRequest *rq;
			for (;;)
				{
				rq = reply.Remove();
				if (rq == NULL)
					{
					// Didn't match any request, punt it into Query()
					iMaster.Query(aMsg, aFrom, iSocket);
					break;
					}
				iWaitQueue.AddLast(*rq);
				if (rq->iId == id && rq->Reply(iMaster, aMsg, aFrom))
					break;
				}
			if (iDeactivateCount == mark)
				{
				//
				// No socket shutdown occurred, just reinsert remaining requests
				//
				while ((rq = reply.Remove()) != NULL)
					iWaitQueue.AddLast(*rq);
				}
			else
				{
				//
				// Socket was shut within Reply, all requests
				// should have been cancelled then...
				//
				while ((rq = reply.Remove()) != NULL)
					{
					rq->iQueueLink.SetWriter(NULL);
					rq->Abort(iMaster, KErrCancel);
					}
				}
			}
		else
			{
			//
			// Not a reply message, let the derived
			// class decide on how to deal with it.
			//
			iMaster.Query(aMsg, aFrom, iSocket);
			}
		}

	iRunReader = 0;
 	if (iOpened ) 	// still open for business?
		{
		// Start a new read
		iReader->Activate();
		}
	}

void CDnsSocketWriter::Queue(TDnsRequest &aRequest, TInt aId)
	{
	aRequest.Cancel();
	//
	// Assign a new random ID for the request
	//
	if (aId < 0)
 		{
 		TInt64 seed = SeedIdGenerator(iSequence);
 		if(seed<0)
 		seed = seed *(KNegativeNumberMask);
 		aRequest.iId = (TUint16)(Math::Rand(seed) >> 8);
 		}
	else
		aRequest.iId = (TUint16)aId;
	aRequest.iQueueLink.SetWriter(this);

	iSendQueue.AddLast(aRequest);
	LOG(Log::Printf(_L("\t\tDNS session [%u] Queued for send with ID=%d"), (TInt)&aRequest, (TInt)aRequest.iId));
	if (!IsActive())
		RunWriter(iStatus);
	}


void CDnsSocketWriter::SetBind(const TInetAddr &aBind)
	{
	iBind = aBind;
	}

void CDnsSocketWriter::SetHoplimit(const TInt aTTL)
	{
	if (aTTL != iTTL)
		{
		iTTL = aTTL;
		iSocket.SetOpt(KSoIp6UnicastHops, KSolInetIp, iTTL);
		iSocket.SetOpt(KSoIp6MulticastHops, KSolInetIp, iTTL);
		}
	}

TInt CDnsSocketWriter::ActivateSocket()
	{
 	if (iOpened) 
		return KErrNone;		// Nothing to do if already opened

	iErrorCount = 0;
#ifndef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY 	
	TInt err = 
		iListen ? iSocket.Open(iMaster.iSS) :
		iTCP ? iSocket.Open(iMaster.iSS, KAfInet, KSockStream, KProtocolInetTcp) :
		iSocket.Open(iMaster.iSS, KAfInet, KSockDatagram, KProtocolInetUdp);
#else			
	TInt err = KErrNone;
	if (iListen)
		{
		err = iSocket.Open(iMaster.iSS);	
		}
	else
		{		
		TUint addrFamily = iTCP ? KSockStream : KSockDatagram;
		TUint protocol = iTCP ? KProtocolInetTcp : KProtocolInetUdp;
		if (CanUseConnection())
			{
			err = iSocket.Open(iMaster.iSS, KAfInet, addrFamily, protocol, iAttachedConn);
			}
		else
			{
			err = iSocket.Open(iMaster.iSS, KAfInet, addrFamily, protocol);	
			}			
		}
#endif			
#ifdef _LOG
	// Initialize for LOG prints
	TBuf<50> tmp;
	iBind.OutputWithScope(tmp);
#endif
	if (err == KErrNone)
		{
		if (iListen || (err = iSocket.Bind(iBind)) == KErrNone)
			{
			iOpened = 1;
			if (iListen)
				{
				LOG(Log::Printf(_L("CDnsSocketWriter[%u]::ActivateSocketL() listening %S#%d"), this, &tmp, iBind.Port()));
				iMaster.iListener.Accept(iSocket, iStatus);
				SetActive();
				}
			else
				{
				iSocket.SetOpt(KSoIp6UnicastHops, KSolInetIp, iTTL);
				iSocket.SetOpt(KSoIp6MulticastHops, KSolInetIp, iTTL);
				if (iTCP)
					{
#ifdef _LOG
					TBuf<50> dst;
					iTo.OutputWithScope(dst);
					Log::Printf(_L("CDnsSocketWriter[%u]::ActivateSocketL() TCP connect src=%S#%d dst=%S#%d"), this,
						&tmp, iBind.Port(),
						&dst, iTo.Port());
#endif
					iSocket.Connect(iTo, iStatus);
					SetActive();
					}
				else
					{
					LOG(Log::Printf(_L("CDnsSocketWriter[%u]::ActivateSocketL() UDP %S#%d"), this, &tmp, iBind.Port()));
					iSocket.SetOpt(KSoUdpReceiveICMPError, KSolInetUdp, 1);
					if (!iRunReader)
						iReader->Activate();
					}
				}
			return KErrNone;
			}
		iSocket.Close();
#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY		
		iAttachedConn.Close();
#endif		
		}
	// Should probably be logged in release too!
	LOG(Log::Printf(_L("CDnsSocketWriter[%u]::ActivateSocketL() bind=%S#%d (or open) failed %d"), this, &tmp, iBind.Port(), err));
	return err;
	}


/**
* Decides on action when the socket returns an error status. Not all
* errors are fatal errors, which require a reset of the socket.
*
* @param aReason	the error code from the socket (iStatus)
* @param aServer	the dns server address, if known (only for Send errors)
*
* @return
* @li	= 1, Error fully processed, Don't activate anything
* @li	= 0, Error handled, keep CActive running
*/
TInt CDnsSocketWriter::HandleError(TInt aReason, const TInetAddr *aServer)
	{
	if (++iErrorCount > KSocketErrorLimit)
		{
		// Too many consecutive errors, deactivate to reset
		LOG(Log::Printf(_L("CDnsSocketWriter[%u]::HandleError(%d) Too many (%d) consecutive errors"), this, aReason, iErrorCount));
		DeactivateSocket(aReason);
		return 1;
		}
	if (IsTCP())
		{
		// Any error on TCP socket means that it is "dead",
		// and we must close the socket to recover!
		DeactivateSocket(KErrDndServerUnusable);
		return 1;
		}
	// The error state should automaticly clear with datagram
	// sockets, but in some releases it does not. Issue a
	// dummy KSOSelectLastError, which in some cases also
	// clears the error code from socket server. (Note: the
	// option parameter is dummy, the error is actually
	// returned as a return value of GetOpt, very bizarre
	// -- msa)
	TInt socket_err = iSocket.GetOpt(KSOSelectLastError, KSOLSocket, socket_err);
	(void)socket_err;	// shut down compiler warning about unreference variable.

	if (aServer == NULL)
		{
		// The only errors on receive side, that we can do something about, are
		// the ICMP errors for some server (other errors are ignored).
		// Try to find out the server.
		TPckgBuf<TSoInetLastErr> opt;
		if (iSocket.GetOpt(KSoInetLastError, KSolInetIp, opt) == KErrNone)
			{
#ifdef _LOG
			TBuf<50> src;
			TBuf<50> dst;
			TBuf<50> err;
			opt().iSrcAddr.OutputWithScope(src);
			opt().iDstAddr.OutputWithScope(dst);
			opt().iErrAddr.OutputWithScope(err);
			Log::Printf(_L("CDnsSocketWriter[%u]::HandleError(%d) last=%d, icmp(%d,%d) src=%S dst=%S err=%S"),
				this, aReason, opt().iStatus, opt().iErrType, opt().iErrCode, &src, &dst, &err);
#endif
			if (opt().iStatus != aReason)
				return 0;		// Error does not match the ICMP last error, just ignore.

			aServer = &opt().iDstAddr;
			}
		else
			{
			DeactivateSocket(KErrDndServerUnusable);
			return 1;
			}			
		}

	// aServer possibly identifies a DNS server with which
	// we have some communication problems. Declare this
	// server unusable for all queries currently using it.
	const TUint mark = iDeactivateCount;
	iMaster.Error(*aServer, aReason);
	// If shutdown occurred within above call, then
	// this return must not try to reactivate the
	// reader or writer.
	return (mark != iDeactivateCount);
	}

void CDnsSocketWriter::DeactivateSocket(TInt aReason)
	{
	LOG(Log::Printf(_L("CDnsSocketWriter[%u]::DeactivateSocket() Entry"), this));
	if (!iOpened )
	    {
	    LOG(Log::Printf(_L("CDnsSocketWriter[%u]::DeactivateSocket() return without action"), this));
		return;			// Nothing to do if not open
	    }
	// Grab current set of requests away, so that
	// potentially newly entered requests won't
	// get affected by this...
	//
	TRequestQueue send(iSendQueue);
	TRequestQueue wait(iWaitQueue);
	//
	// Cancel all activity
	//
//	LOG(Log::Printf(_L("CDnsSocketWriter[%u]::DeactivateSocket() iReader->Cancel()"), this));
	iReader->Cancel();
//	LOG(Log::Printf(_L("CDnsSocketWriter[%u]::DeactivateSocket() Cancel()"), this));
	Cancel();
//	LOG(Log::Printf(_L("CDnsSocketWriter[%u]::DeactivateSocket() iSocket.Close()"), this));
	//
	// Shut down the sessions
	//
	iOpened = 0;
	iSocket.Close();
	iDeactivateCount += 1;
#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY	
	iAttachedConn.Close();
#endif	
	//
	// Remove and abort all requests
	//
	TDnsRequest *rq;
	while ((rq = send.Remove()) != NULL)
		{
		rq->iQueueLink.SetWriter(0);
		rq->Abort(iMaster, aReason);
		}
	while ((rq = wait.Remove()) != NULL)
		{
		rq->iQueueLink.SetWriter(0);
		rq->Abort(iMaster, aReason);
		}
	LOG(Log::Printf(_L("CDnsSocketWriter[%u]::DeactivateSocket() complete"), this));
	}

//
// CDnsSocketWriter::Remove
// ************************
void CDnsSocketWriter::Remove(TDnsRequest &aQuery)
	{
	//
	// Must not be called for request that is not queued
	//
	ASSERT(aQuery.iQueueLink.Writer() == this);
	aQuery.iQueueLink.Deque();
	aQuery.iQueueLink.SetWriter(0);

	if (iSending == &aQuery)
		{
		iSending = NULL;
		// If IsActive() == FALSE, then this Remove is actually
		// called from the Build() callback!
		if (IsActive())
			{
			Cancel();
			// There can be other queries waiting
			// for writer..
			RunWriter(iStatus);
			}
		}
	}

void CDnsSocketWriter::RunL()
	{
	const TInt result = iStatus.Int();
	LOG(Log::Printf(_L("--> CDnsSocketWriter[%u]::RunL() -start- iStatus=%d"), this, result));
#ifdef _LOG
	TBuf<50> tmp;
	iTo.OutputWithScope(tmp);
#endif
	for (;;)	// ...just for easy exits..
		{
		if (result)
			{
			LOG(Log::Printf(_L("CDnsSocketWriter[%u]::RunL() Error on %S#%d"), this, &tmp, iTo.Port()));
			}
		else
			ResetErrorCount();
		if (iListen)
			{
			//
			// Keep listening active, allocate a new reader/writer to listen
			//
			CDnsSocketWriter *writer = NULL;
			TRAPD(err,
				writer = CDnsSocketWriter::NewTcpListenL(iMaster, iTTL);
				err = iMaster.AddSecondaryWriter(writer));
			if (err != KErrNone)
				{
				// Cannot start new accept for some reason, reject the current accept and
				// reinitialize the current writer to wait a new accept instead
				iMaster.DeleteWriter(writer, err);	// <-- safe to call with writer == NULL
				// Reactivate this socket for listening instead (iListen remains set)
				LOG(Log::Printf(_L("CDnsSocketWriter[%u]::RunL() Failed (%d) to activate new Accept() to=%S#%d"),
					this, err, &tmp, iTo.Port()));
				DeactivateSocket(err);
				ActivateSocket();
				break;
				}
			// Succesful Accept, update iTo to the address and port of the
			// remote end.
			iListen = 0;
			iSocket.RemoteName(iTo);
			LOG(iTo.OutputWithScope(tmp));
			LOG(Log::Printf(_L("CDnsSocketWriter[%u]::RunL() Listen accepted to=%S#%d"), this, &tmp, iTo.Port()));
			if (!iReader->IsActive())
				iReader->Activate();
			}
		if (iSending)
			{
			LOG(Log::Printf(_L("\t\tDNS session [%u] Sent to=%S#%d ID=%d"), iSending, &tmp, iTo.Port(), (TInt)iSending->Id()));
			TDnsRequest *query = iSending;
			iSending = NULL;
			if (result == KErrNone)
				query->Sent(iMaster);
			else if (HandleError(result, &iTo))
				break;
			}
		else if (result && HandleError(result))	// Error that cannot be associated with specific sending (does it ever happen?)
			break;
		// Keep writer busy, if not already active.
		if (!IsActive())
			RunWriter(iStatus);
		break;	// ** NOT REAL LOOP, ALWAYS EXIT AT END **
		}
	LOG(Log::Printf(_L("<-- CDnsSocketWriter[%u]::RunL() -exit- iStatus=%d"), this, iStatus.Int()));
	}

void CDnsSocketWriter::RunWriter(TRequestStatus &aStatus)
	{
	if (iRunWriter)
		return;		// This is a callback from Build, return
	iRunWriter = 1;
	//
	// Pick next query to be served
	// ****************************
	//
	iSending = iSendQueue.Remove();
	while ( iSending != NULL)
		{
		iWaitQueue.AddLast(*iSending);
		// Before calling Build, the message is reset into
		// initial state:
		TPtr8 payload((TUint8 *)iOutMsg.Ptr(), iOutMsg.MaxLength());
		if (iTCP)
			payload.Set((TUint8 *)payload.Ptr()+2, 0, payload.MaxLength()-2);
		TMsgBuf &buf = TMsgBuf::Cast(payload);
		buf.SetLength(sizeof(TDndHeader));
		buf.Header().Init(iSending->iId);
		// If Build fails, request(s) are simply moved
		// into iWaitQueue to wait for further actions.
		// (If iSending becomes NULL in Build, then Build has Removed the
		// request--do not start write)
		// Note: if receive buffer > KDnsMaxMessage, then it requests EDNS0. Don't
		// request EDNS0, if TCP is being used (report receive buffer = 0).
		if (iSending->Build(iMaster, buf, iTo, iTCP ? 0 : iReader->iAllocatedLength) && iOpened && iSending)
			{
			const TInt len = buf.Length();
			if (iTCP)
				{
				// Patch in the length
				TUint8 *const p = (TUint8 *)iOutMsg.Ptr();
				p[1] = (TUint8)len;
				p[0] = (TUint8)(len >> 8);
				iOutMsg.SetLength(len+2);
				}
			else
				{
				iOutMsg.SetLength(len);
				}
#ifdef _LOG
			TBuf<50> tmp;
			iTo.OutputWithScope(tmp);
			Log::Printf(_L("\t\tDNS session [%u] Send to=%S#%d %d bytes (TCP=%d)"), iSending, &tmp, iTo.Port(), iOutMsg.Length(), (TInt)iTCP);
#endif
			if (iTCP)
				{
				iSocket.Write(iOutMsg, aStatus);
				if (!iReader->IsActive())
					iReader->Activate();
				}
			else
				iSocket.SendTo(iOutMsg, iTo, 0, aStatus);
			SetActive();
			break;
			}
		}
	iRunWriter = 0;
	}

void CDnsSocketWriter::DoCancel()
	{
	LOG(Log::Printf(_L("CDnsSocketWriter[%u]::DoCancel()"), this));
	if (iListen)
		{
		iMaster.iListener.CancelAll();
		iListen = 0;
		}
	else
		{
		iSocket.CancelWrite();
		iSending = NULL;
		}
	}
	
#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY
TBool CDnsSocketWriter::CanUseConnection()
	{	
	TInt err = iAttachedConn.Open(iMaster.iSS);
	if (KErrNone != err)
        	{
	        LOG(Log::Printf(_L("CDnsSocketWriter[%u]::CanUseConnection() error opening connection: %d"), this, err));
	        return EFalse;
        	}

	    TUint noConnections = 0;
	    err = iAttachedConn.EnumerateConnections(noConnections);
	    if (KErrNone != err)
        	{
	        LOG(Log::Printf(_L("CDnsSocketWriter[%u]::CanUseConnection() error enumerating connections: %d"), this, err));
        	return EFalse;
	        }
	LOG(Log::Printf(_L("CDnsSocketWriter[%u]::CanUseConnection() no of connections: %u"), this, noConnections));

    if (noConnections > 0)
        {
        TPckgBuf<TConnectionInfo> info;
        TConnectionInfo currentInfo;
        TBool foundConnection = EFalse;
        for(TInt i=1; i<=noConnections; i++)
            {
            if (KErrNone != iAttachedConn.GetConnectionInfo(i, info))
                {
                return EFalse;
                }
            currentInfo = info();

            LOG(Log::Printf(_L("CDnsSocketWriter[%u]::CanUseConnection() currentInfo.NetId = %d "), this, currentInfo.iNetId));
            LOG(Log::Printf(_L("CDnsSocketWriter[%u]::CanUseConnection() iMaster.iNetworkId = %d "), this, iMaster.iNetworkId));
            if(currentInfo.iNetId == iMaster.iNetworkId)
                {
                foundConnection = ETrue;
                break;
                }
            }

        if ((foundConnection) && (KErrNone == iAttachedConn.Attach(info, RConnection::EAttachTypeNormal)))
            {
            LOG(Log::Printf(_L("CDnsSocketWriter[%u]::CanUseConnection() attached to existing connection at IAP: [%d] Network: [%d]"), this, info().iIapId, info().iNetId));
            return ETrue;
            }
        else
            {
            LOG(Log::Printf(_L("CDnsSocketWriter[%u]::CanUseConnection() could not find any connection matching the Network: [%d]"), this, iMaster.iNetworkId));
            return EFalse;
            }
        }

    return EFalse;
     }
#endif

// CDnsSocketReader
// ****************
CDnsSocketReader::CDnsSocketReader(CDnsSocketWriter &aWriter) : CActive(0), iWriter(aWriter), iBuf(0,0)
	{
	LOG(Log::Printf(_L("CDnsSocketReader[%u]::CDnsSocketReader()"), this));
	CActiveScheduler::Add(this);
	}

void CDnsSocketReader::ConstructL(TUint aDnsMaxMessage)
	{
	//
	// Any reader MUST have a buffer of capacity at least KDnsMaxMessage
	// octets. If this cannot be allocated, cancel the reader (leave!).
	//
	const TUint len = aDnsMaxMessage > (TUint)KDnsMaxMessage ? aDnsMaxMessage : KDnsMaxMessage;
	iInMsg = HBufC8::NewMaxL(len);
	iAllocatedLength = len;
	}

CDnsSocketReader::~CDnsSocketReader()
	{
	ASSERT(!IsActive());
	delete iInMsg;
	if (IsAdded())
		Deque();
	LOG(Log::Printf(_L("CDnsSocketReader[%u]::~CDnsSocketReader()"), this));
	}

void CDnsSocketReader::Activate()
	{
	if (IsActive())
		return;	// Do nothing, if already active!

	if (iWriter.IsTCP())
		{
		LOG(Log::Printf(_L("CDnsSocketReader[%u]::Activate() TCP"), this));
		// Initialize iFrom for connected socket, if not yet done.
		// (only for debugging purposes, no other functionality at momemnt)
		if (iFrom.IsUnspecified())
			iWriter.Socket().RemoteName(iFrom);
		iReadLength = 1;
		iBuf.Set((TUint8 *)iInMsg->Ptr(), 0, 2);
		iWriter.Socket().Read(iBuf, iStatus);
		}
	else
		{
		LOG(Log::Printf(_L("CDnsSocketReader[%u]::Activate() UDP"), this));
		iReadLength = 0;
		iBuf.Set(iInMsg->Des());
		iWriter.Socket().RecvFrom(iBuf, iFrom, 0, iStatus);
		}
	SetActive();
	}


void CDnsSocketReader::RunL()
	{
	LOG(Log::Printf(_L("--> CDnsSocketReader[%u]::RunL() -start- iStatus=%d"), this, iStatus.Int()));
	if (iStatus.Int())
		{
		LOG(Log::Printf(_L("CDnsSocketReader[%u]::RunL() error=%d"), this, iStatus.Int()));
		if (iWriter.HandleError(iStatus.Int()) == 0)
			{
			// Need to re-issue previous request
			if (iWriter.IsTCP())
				iWriter.Socket().Read(iBuf, iStatus);
			else
				iWriter.Socket().RecvFrom(iBuf, iFrom, 0, iStatus);
			LOG(Log::Printf(_L("CDnsSocketReader[%u]::RunL() soft error, restarted last read (TCP=%d)"), this, (TInt)iWriter.IsTCP()));
			SetActive();
			}
		}
	else if (iReadLength)
		{
		iWriter.ResetErrorCount();
		iReadLength = 0;
		const TUint8 *p = (TUint8 *)iInMsg->Ptr();
		const TInt len = p[0] << 8 | p[1];
		if (iAllocatedLength < len)
			{
			// Oops, the current buffer is too short for the packet.
			delete iInMsg;
			iAllocatedLength = len;
			iInMsg = HBufC8::New(len);
			}
		if (iInMsg == NULL)
			iWriter.DeactivateSocket(KErrNoMemory);
		else
			{
			iBuf.Set((TUint8 *)iInMsg->Ptr(), 0, len);
			iWriter.Socket().Read(iBuf, iStatus);
			SetActive();
			}
		}
	else
		{
		iWriter.ResetErrorCount();
		iWriter.RunReader(TMsgBuf::Cast(iBuf), iFrom);
		}
	LOG(Log::Printf(_L("<-- CDnsSocketReader[%u]::RunL() -exit- iStatus=%d"), this, iStatus.Int()));
	}

void CDnsSocketReader::DoCancel()
	{
	LOG(Log::Printf(_L("CDnsSocketReader[%u]::DoCancel()"), this));
	iWriter.Socket().CancelRead();
	}

// CDnsSocket
// **********
//
CDnsSocket::CDnsSocket(TUint aMaxDnsMessage) : iMaxDnsMessage(aMaxDnsMessage)
	{
	LOG(Log::Printf(_L("CDnsSocket[%u]::CDnsSocket()"), this));
	}

void CDnsSocket::ConstructL()
	{
	// Allocate the primary reader/writer for UDP
	iWriter = CDnsSocketWriter::NewL(*this, -1);
	iWriter->iNext=NULL;
	}

CDnsSocket::~CDnsSocket()
	{
	DeactivateSocket(KErrCancel);

	while (iWriter)
		{
		CDnsSocketWriter *const writer = iWriter;
		iWriter = iWriter->iNext;
		delete writer;
		}
	LOG(Log::Printf(_L("CDnsSocket[%u]::~CDnsSocket() completed"), this));
	}

RSocket &CDnsSocket::Socket()
	{
	return iWriter->Socket();
	}

/**
// Open the DNS socket and start listening incoming
// packets. The socket is bound to current "bind"
// address and port, which default to NONE and 0.
//
// Does nothing, if socket is already active
//
// Leaves, if socket cannot be activated
*/
void CDnsSocket::ActivateSocketL(TUint aNetworkId)
	{
	LOG(Log::Printf(_L("CDnsSocket[%u]::ActivateSocketL([NetId = %d])"), this, aNetworkId));

	if (iConnected && IsOpened())
		{
	   LOG(Log::Printf(_L("CDnsSocket[%u]::ActivateSocketL([NetId = %d]) return without action"), this, aNetworkId));
 	    return;        // Already connected, nothing to do
 	    }

	User::LeaveIfError(iSS.Connect());
	iConnected = 1;
	iNetworkId = aNetworkId;

	const TInt ret = iWriter->ActivateSocket();
	if (ret != KErrNone)
		{
		DeactivateSocket(ret);
		User::Leave(ret);
		}
	}

/**
// Change the current bind address and activate the
// DNS socket. If socket is already active, the bind
// address is set, but will only take effect after
// deactivation and next activate.
//
// Does nothing, if socket is already active.
//
// Leaves if socket cannot be activated.
//
// @param	aBind	specify the address and port for
//					received packets (also the source
//					address and port for any sent
//					messages).
*/
void CDnsSocket::ActivateSocketL(const TInetAddr &aBind)
	{
#ifdef _LOG
	TBuf<50> tmp;
	aBind.OutputWithScope(tmp);
	Log::Printf(_L("CDnsSocket[%u]::ActivateSocketL([%S#%d])"), this, &tmp, aBind.Port());
#endif
	iWriter->SetBind(aBind);
	ActivateSocketL();
	}

/**
// Activate the TCP listening socket.
//
// This will automaticly cancel any previsous listens.
//
// Leaves if listening cannot be activated.
//
// @param	aBind	specify the address and port for
//					received connections.
// @param	aTTL	specify the TTL for accepted connections
*/
void CDnsSocket::ActivateListenL(const TInetAddr &aBind, TInt aTTL = -1)
	{
#ifdef _LOG
	TBuf<50> tmp;
	aBind.OutputWithScope(tmp);
	Log::Printf(_L("CDnsSocket[%u]::ActivateListenL([%S#%d])"), this, &tmp, aBind.Port());
#endif
	ActivateSocketL();	// First, make sure normal socket is up and running.
	//
	// Abort pending listens, if any
	//
	CDnsSocketWriter **head = &iWriter->iNext;
	while (*head != NULL)
		{
		CDnsSocketWriter *writer = *head;
		if (writer->IsListen())
			{
			*head = writer->iNext;
			// Note: IsListen() writers should never have any
			// queued requests, so no Abort() callbacks will
			// occur and thus, we don't need to worry about
			// re-entries into CDnsSocket via callbacks!
			writer->DeactivateSocket(KErrCancel);
			delete writer;
			}
		else
			head = &writer->iNext;
		}

	if (iListening)
		iListener.Close();
	TInetAddr bind(aBind);	// Needed only because RSocket::Bind wants non-const argument! ARRGHH!
	User::LeaveIfError(iListener.Open(iSS, KAfInet, KSockStream, KProtocolInetTcp));
	iListening = 1;			// Socket opened.
	User::LeaveIfError(iListener.Bind(bind));
	(void)iListener.SetOpt(KSoIp6UnicastHops, KSolInetIp, aTTL);
	(void)iListener.SetOpt(KSoUserSocket, KSolInetIp, 0);
	User::LeaveIfError(iListener.Listen(10));

	//
	// Create and append a new socket writer waiting for listen accept
	//
	(void)AddSecondaryWriter(CDnsSocketWriter::NewTcpListenL(*this, aTTL));
	}

/**
// Close the socket and socket server session, if they
// were open. Abort all queued requests with the specifiec
// reason.
//
// @param aReason
//	the abort reason that is passed to any requests which
//	are removed from the system (see TDnsRequest::Abort)
//					
*/
void CDnsSocket::DeactivateSocket(TInt aReason)
	{
	// Need to grab the list away, because
	// re-activation might occur via callbacks.
	//
	CDnsSocketWriter *writer = iWriter->iNext;
	iWriter->iNext = NULL;

	iWriter->DeactivateSocket(aReason);
	while (writer)
		{
		CDnsSocketWriter *tmp = writer;
		writer = tmp->iNext;
		tmp->DeactivateSocket(aReason);
		delete tmp;
		}
	// Oops... Should not close if re-activated ---FIX!
	if (iListening)
		{
		iListening = 0;
		iListener.Close();
		}

	// Oops... Should not close if re-activated ---FIX!
	if (iConnected)
		{
		iConnected = 0;
		iSS.Close();
		}
	}

void CDnsSocket::SetHoplimit(const TInt aTTL)
	/**
	* Set TTL for for transmitted packets.
	*
	* The socket must be activated (open) when this is called. The set TTL
	* is used as is for both unicast and multicast. The value -1 selects
	* the system defaults.
	*
	* The set TTL will remain in effect for the primary writer until
	* changed again.
	* 
	* @param	aTTL	The TTL
	*/
	{
	iWriter->SetHoplimit(aTTL);
	}

/**
// Queue a request for sending. The request may get following
// "callbacks":
// @li	TDnsRequest::Build,
//			just before a request is ready to be sent, build
//			the outgoing packet into specified message buffer
// @li	TDnsRequest::Sent,
//			the packet has been sent to the interface
// @li	TDnsRequest::Reply,
//			a DNS reply packet matching the request id has
//			been received. Need to test whether rest of the
//			packet matches the request, and if so, handle
//			it.
// @li	TDnsRequest::Abort,
//			request is being aborted (usually DNS socket
//			is being deactivated).
//
// @param aRequest	the request to be queued
// @param aId		the request id
//	@li	-1 (< 0),
//		the default if parameter is omitted. The queue
//		method will assign random id automatically.
//	@li >= 0,
//		16 bits of this value is used as id.		
*/
void CDnsSocket::Queue(TDnsRequest &aRequest, const TInt aId)
	{
	iWriter->Queue(aRequest, aId);
	}


// Queue a request for sending with a specific socket
//
TInt CDnsSocket::Queue(TDnsRequest &aRequest, const RSocket &aSocket, const TInt aId)
	{
	for (CDnsSocketWriter *writer = iWriter; writer != NULL; writer = writer->iNext)
		{
		if (&aSocket == &writer->Socket())
			{
			// Found it!
			writer->Queue(aRequest, aId);
			return KErrNone;
			}
		}
	return KErrNotFound;
	}


/**
// Queue a request for sending with TCP.
//
// @param aRequest to be queued
// @param aServer The address and port of the DNS server
// @param aId of the request, if >= 0. If < 0, then a new random ID is generated
// @param aTTL of the connection (= -1, the default, requests the system default). This is
// only effective if the connection is created.
//
// @return KErrNone, if queued successfully, or error code if failed.
*/
TInt CDnsSocket::Queue(TDnsRequest &aRequest, const TInetAddr &aServer, const TInt aId,  const TInt aTTL)
	{
	//
	// Locate or create connected Socket reader/writer instance
	//
	CDnsSocketWriter *writer = iWriter->iNext;
	while (writer != NULL)
		{
		if (writer->Match(aServer))
			{
			// A writer already exists
			writer->Queue(aRequest, aId);
			return KErrNone;
			}
		else
		    {
            //iWriter = iWriter->iNext;
            writer = writer->iNext;
            //do we need to return?
		    }
		}
	//
	// Create and append a new connected socket writer
	//
	TRAPD(err, writer = CDnsSocketWriter::NewTcpL(*this, aServer, aTTL));
	if (writer)
		{
		err = AddSecondaryWriter(writer);
		if (err == KErrNone)
			writer->Queue(aRequest, aId);
		else
			DeleteWriter(writer, err);
		}
	return err;
	}

/**
// Abort whatever the request is currently doing and reque
// it for new send (eventually, a call to DnsRequest::Build
// should happen). This preserves the old id of the request.
//
// If a request is not currently queued, this does an implicit
// Queue. (a new id is generated).
//
// @param aRequest	the request to be resent.
*/
void CDnsSocket::ReSend(TDnsRequest &aRequest)
	{
	Queue(aRequest, aRequest.IsQueued() ? aRequest.Id() : -1);
	}

/**
// Add and activate a new secondary writer/reader unit.
//
// Insert a newly created and constructed reader/writer unit
// to the system and activate it. The secondearies are inserted
// into the iWriter chaing after the first primary unit, which
// always exists, as long as CDnsSocket exists.
//
// @param aWriter	The reader/writer instance
//
// @return KErrNone, if succesfully activated, and an error code otherwise
*/
TInt CDnsSocket::AddSecondaryWriter(CDnsSocketWriter *aWriter)
	{
	ASSERT(iWriter != NULL);		// Primary must always exist!
	ASSERT(aWriter != NULL);		// Silly caller, program error
	if (aWriter == NULL)			// Test again (for release version)
		return KErrArgument;		// Should really not happen!
	ASSERT(aWriter->iNext == NULL);	// The new writer should be just created

	// Insert the new unit after the primary reader/writer, and
	// before all previous secondary units (as the placement in
	// chain should not matter--this is just simplest to do).
	aWriter->iNext = iWriter->iNext;
	iWriter->iNext = aWriter;
	return aWriter->ActivateSocket();
	}

/**
// Deactivate and delete a writer instance.
//
// Deactivate the writer, remove it from the list of writers
// (if present), and delete it.
//
// @param aWriter	The reader/writer to be deleted
// @param aReason	The reason (passed to any requests that are aborted because of this)
*/
void CDnsSocket::DeleteWriter(CDnsSocketWriter *aWriter, TInt aReason)
	{
	// Allow call with NULL, as normal delete does
	if (aWriter == NULL)
		return;

	// Primary writer must not be deleted with this function!
	ASSERT(aWriter != iWriter);
	if (aWriter != iWriter)
		return;

	// Remove instance from the writer chain (allow
	// case where the aWriter is not in the chain!)

	CDnsSocketWriter **head = &iWriter->iNext;
	while (*head != NULL)
		{
		CDnsSocketWriter *const writer = *head;
		if (writer == aWriter)
			{
			*head = writer->iNext;
			break;
			}
		else
			head = &writer->iNext;
		}

	// Note: Deactivate call may cause the CDnsSocket to be deleted,
	// thus it is important that this no longer references it and
	// that the writer has already been removed from the list before
	// this!
	aWriter->DeactivateSocket(aReason);
	delete aWriter;	// a detached instance can now be deleted
	}


TBool CDnsSocket::IsOpened() const
	{
	return iWriter ? iWriter->IsOpened() : EFalse;
	}

TInt64 CDnsSocketWriter::SeedIdGenerator(TInt64 aSequence)
/*
* Function to generate seed for which will be passed to generate another level of sequence number
* The requirement came due the fact that microsoft DNS sequence where gussed. 
* Seed generation is divided into two levels of generation. 
*/
	{
	TInt64 operand = Math::Random() % 5;

	//Generation of random number secure ID which should be mathematically operated with the 
	//Seed. The generation will make guess difficult. Secure ID should not be negative so there is
	//an extra check done to make it positive number.
	TInt64 secureId;	
	secureId = Math::Random();
	if(secureId < 0)
	secureId = secureId * (KNegativeNumberMask);

	switch(operand)
		{
		case 1:
		return (secureId + aSequence);
						
		case 2:
		return (aSequence - secureId);
		
 		default:
		return (secureId * aSequence);
			
		};//end of switch
	}//end of function