tcpiputils/dnd/src/resolver.cpp
changeset 0 af10295192d8
child 53 7e41d162e158
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tcpiputils/dnd/src/resolver.cpp	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,1789 @@
+// 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:
+// resolver.cpp - name resolver
+//
+
+#include <in_sock.h>
+#include <timeout.h>
+#include "listener.h"
+#include "resolver.h"
+#include "engine.h"
+#include "hosts.h"
+#include <networking/dnd_err.h>
+#include "dns_ext.h"
+#include "inet6log.h"
+
+#ifdef DND_DCM_EXTENSION
+#include "dnd.hrh"		// EDndFlush
+#endif
+
+#ifdef EXCLUDE_SYMBIAN_DNS_PUNYCODE
+#undef SYMBIAN_DNS_PUNYCODE
+#endif //EXCLUDE_SYMBIAN_DNS_PUNYCODE
+
+struct SQueryStep
+	{
+	EDnsQType iQType;			// the query type
+	};
+
+struct SQueryExtension
+	{
+	const TDesC *iPrefix;
+	SQueryStep iQuery;
+	};
+
+// ...for GetByAddr, perform only PTR query (for each server)
+const SQueryStep KGetByAddress[1] =
+	{
+	{ EDnsQType_PTR }
+	};
+
+// ...for GetByName, try first AAAA and then A (for each server)
+const SQueryStep KGetByName1[2] =
+	{
+	{ EDnsQType_AAAA },
+	{ EDnsQType_A }
+	};
+
+// ...for GetByName, try first A and then AAAA (for each server)
+const SQueryStep KGetByName2[2] =
+	{
+	{ EDnsQType_A },
+	{ EDnsQType_AAAA }
+	};
+
+// Define the search order of the sources
+const TDnsServerScope KSourceOrder[] =
+	{
+	EDnsServerScope_HOSTFILE,	// 1. Search hosts file first
+	EDnsServerScope_UC_GLOBAL,	// 2. Search global DNS
+#ifdef LLMNR_ENABLED
+	EDnsServerScope_MC_LOCAL,	// 3. Try Link Local Multicast
+#endif
+	};
+
+const SQueryExtension KQueryExtension[] =
+	{
+#if 0
+	{ &KDnsExtQType_A,		EDnsQType_A },
+	{ &KDnsExtQType_AAAA,	EDnsQType_AAAA },
+	{ &KDnsExtQType_MX,		EDnsQType_MX },
+	{ &KDnsExtQType_NS,		EDnsQType_NS },
+	{ &KDnsExtQType_SOA,	EDnsQType_SOA },
+	{ &KDnsExtQType_CNAME,	EDnsQType_CNAME },
+	{ &KDnsExtQType_PTR,	EDnsQType_PTR },
+	{ &KDnsExtQType_SRV,	EDnsQType_SRV },
+	{ &KDnsExtQType_NAPTR,	EDnsQType_NAPTR },
+	{ &KDnsExtQType_ANY,	EDnsQType_ANY }
+#else
+	// Workaround for ARMV5 compiler bug? Above declaration always
+	// produces 80 bytes of writable global data. The following
+	// ugly definition does not.
+	{ (const TDesC *)&KDnsExtQType_A.iTypeLength,		EDnsQType_A },
+	{ (const TDesC *)&KDnsExtQType_AAAA.iTypeLength,	EDnsQType_AAAA },
+	{ (const TDesC *)&KDnsExtQType_MX.iTypeLength,		EDnsQType_MX },
+	{ (const TDesC *)&KDnsExtQType_NS.iTypeLength,		EDnsQType_NS },
+	{ (const TDesC *)&KDnsExtQType_SOA.iTypeLength,		EDnsQType_SOA },
+	{ (const TDesC *)&KDnsExtQType_CNAME.iTypeLength,	EDnsQType_CNAME },
+	{ (const TDesC *)&KDnsExtQType_PTR.iTypeLength,		EDnsQType_PTR },
+	{ (const TDesC *)&KDnsExtQType_SRV.iTypeLength,		EDnsQType_SRV },
+	{ (const TDesC *)&KDnsExtQType_NAPTR.iTypeLength,	EDnsQType_NAPTR },
+	{ (const TDesC *)&KDnsExtQType_ANY.iTypeLength,		EDnsQType_ANY }
+#endif
+	};
+
+// KMaxQuerySessions
+// *****************
+/**
+// The maximum number of query sessions for single resolver,
+// for example, supporting A and AAAA query, requires this
+// to be at least 2.
+// (if someone ever adds A6 to the list, it needs to be 3).
+*/
+const TUint KMaxQuerySessions = 2;
+
+// KMaxDeprecated
+// **************
+/**
+// The size of the deprecated answers buffer. When processing
+// the replies for GetByName, the resolver will first return
+// those A and AAAA answers, for which there is an existing
+// route and source address in the current system. Other answers
+// are put into deprecated buffer, and returned after the
+// "good" answers.
+*/
+const TUint KMaxDeprecated = 20;
+
+class CDndResolver;
+
+/**
+// The resolver state automaton for one DNS query.
+//
+// This executes the state automaton for a single DNS query
+// (for example A, AAAA, MX, etc.). The processing includes
+// automatic retransmissions (potentially to multiple DNS
+// servers) and timeout handling 
+//
+// The query process is started by Start() and it will
+// then proceed independently to the conclusion.
+// When the query has completed or failed,
+// CDndResolver::GetNextAnswer() is called.
+//
+// In some cases an error is considered to be non-recoverable,
+// and instead of calling GetNextAnswer(), CDndResolver::QueryDone(error)
+// is called instead. This cancel the main query processing and
+// return the indicated error directly to the client process
+// (RHostResolver).
+//
+*/
+class TQuerySession: public MDnsResolver
+	{
+	friend class TQuerySessionTimeoutLinkage;
+public:
+	// Default constructor
+	TQuerySession();
+	// Supplement default constructor
+	void Init(CDndResolver *const aResolver);
+	// Open a new session to the DNS handler
+	void Open();
+	// Close the session with the DNS handler
+	void Close();
+	// Start a DNS query on the session
+	TBool Start(EDnsQType aQType);
+
+	MDnsSession *iSession;		//< Session handle with DndDnsclient
+	TInt iStatus;				//< Session status / next record to retrieve (if >= 0)
+	EDnsQType iQType;			//< the query type
+private:
+	static inline TInt Max(const TInt a, const TInt b) { return a > b ? a : b;}
+
+	// A callback method when the timeout expires
+	void Timeout(const TTime &aNow);
+	// Request a call to Timeout after specified time (seconds)
+	void SetTimer(TUint aTimeout);
+	// A callback method when the request to DNS handler completes
+	void ReplyCallback(const TInt aErr);
+
+	// Cancel pending operations
+	void Cancel();
+	// Activate a query in the DNS handler
+	void SendDnsQuery();
+
+	// Internal, handles the common part of timeout and reply callbacks
+	void Event(TBool aTimeout);
+	
+	CDndResolver *iResolver;	//< The back pointer to resolver
+
+	TUint8 iQueryRetry;			//< Current retry index (for the current step)
+	TUint8 iQueryTimeouts;		//< Number of true timeouts (used by some resolver modes)
+	TUint8 iServerCount;		//< used in "spray mode" to count the servers.
+	TUint iWaitingQuerySent:1;	//< used in detecting and timing out interface startup.
+	TUint iWaitingAfterSpray:1;	//< used only in "spray mode"
+	TUint iSendingQuery:1;		//< set to 1, when DoQueryL is being called (recursion safeguard in SendDnsQuery)
+	TUint iPendingQuery:1;		//< set to 1, when DoQueryL is required (recursion safeguard in SendDnsQuery)
+	TUint iQueryTimeoutValue;	//< The current timeout for waiting the reply AFTER packet is sent
+public:	// Needed for gcc!
+	RTimeout iTimeout;
+#ifdef _LOG
+	TBuf<2> iName;
+#endif
+	};
+
+// This ungainly manoevure is forced on us because the offset is not evaluated early enough by GCC3.4 to be
+// passed as a template parameter
+#if defined(__X86GCC__) || defined(__GCCE__)
+#define KQuerySessionTimeoutLinkageOffset 28
+__ASSERT_COMPILE(KQuerySessionTimeoutLinkageOffset == _FOFF(TQuerySession, iTimeout));
+#else
+#define KQuerySessionTimeoutLinkageOffset _FOFF(TQuerySession, iTimeout)
+#endif
+
+class TQuerySessionTimeoutLinkage : public TimeoutLinkage<TQuerySession, KQuerySessionTimeoutLinkageOffset>
+	{
+public:
+	static void Timeout(RTimeout &aLink, const TTime &aNow, TAny * /*aPtr*/)
+		{
+		Object(aLink)->Timeout(aNow);
+		}
+	};
+
+/**
+// A structure to remember an answer to be retrieved later.
+*/
+class TDeprecatedAnswer
+	{
+public:
+	inline TDeprecatedAnswer() {}
+	inline TDeprecatedAnswer(TUint8 aStep, TUint8 aIndex) : iStep(aStep), iIndex(aIndex) {}
+
+	TUint8 iStep;		//< query step # (selects session)
+	TUint8 iIndex;		//< answer index within the query reply
+	};
+
+/**
+The real implementation of the MDndResolver.
+
+Function:
+
+@li	Accepts the request (see TDnsRequestBase) from the application for name resolving.
+@li	Tries to resolve the request from host files or DNS sources
+@li	Once the request is over (response obtained or time out),
+	it sends back the response (again TDnsRequestBase) and/or error code
+	to the application which requested it.
+
+See also \ref resolvers
+
+This class is designed so that it can be reused for serving
+different clients. It has the following simplified state diagram:
+
+@verbatim
+                   _________________________
+                 /      reuse               \
+                V                            |
+ construct -> Start() ---> WaitQuery() --> Stop() --> destruct
+                               /  \          ^
+                              |    |         |
+                               \  /         /
+                            QueryDone() ___/
+
+@endverbatim
+@li Start()
+	serving a specific RHostResolver session. This method is called
+	from CDndListener, after it has received a new query request
+	from the stack.
+@li WaitQuery()
+	the resolver is waiting for next incoming request
+	from the RHostResolver.
+@li QueryDone()
+	queue a result to be returned to the RHostResolver.
+@li Stop()
+	serving the current rquest. This may be called either
+	internally or from CDndListener.
+*/
+class CDndResolver : public CBase, public MDndResolver, public MDnsResolver
+	{
+	friend class TQuerySession;
+public:
+	CDndResolver(const TInt aId, CDndEngine &aControl, MDndListener &aListener, 
+					MDnsSource &aDnsclient);
+
+	void Start(const TDnsMessageBuf &aMsg);	// Starts the resolver
+	const TDesC8 &ReplyMessage();			// Return reference to the reply buffer.
+	TUint16 Session() const;				// Return session id of the current query.
+	void Stop();							// Stop the resolver
+	virtual ~CDndResolver();
+
+	static inline TInt Max(const TInt a, const TInt b) { return a > b ? a : b;}
+
+	const TTime &RequestTime() const { return iRequestTime; }
+	// only used for GetHostName
+	void ReplyCallback(const TInt aResult);
+
+private:
+	// Return a result to the application
+	void QueryDone(TInt aResult);
+	// Wait for the next query from the application
+	void WaitQuery();
+	// Select next source for the name resolving (HOSTS, DNS, LLMNR)
+	void NextSource();
+	// Cancel all activity and resources being used for the current source
+	void StopSource();
+	TBool StartSource();
+	// Return next result from HOSTS file source
+	void HOSTFILE_Next();
+
+#ifdef _LOG
+	// Print out debugging information
+	void ShowQuery(const TDnsMessage &aName, TInt aReply);
+#endif
+
+	// Retrieve next answer, possibly starting retrieval of additional information from source
+	void GetNextAnswer();
+	// Used to find alternate DNS servers (in some operating modes)
+	void ProbeServers();
+private:
+	const TInt iId;					//< Resolver instance id (for debugging use)
+	TTime iRequestTime;				//< Time at which the request was received from the application
+
+	// The Query buffer for the reply
+	TDnsMessageBuf iBuffer;
+	// The current orignal query being processed.
+	TDnsMessageBuf iCurrentQuery;
+
+	const TDnsServerScope *iSourceList;	//< Information sources
+	TDnsServerScope iSourceNow;		//< The current source
+	TUint iQueryStepMaxTime;		//< Maximum time allowed for single query step to complete
+	TUint iQueryStepMinTime;		//< Minimum time for query step (usage depends on mode)
+	TUint8 iQueryStepRetries;		//< Additional retries after the primary attempt
+	TInt iSourceCount;				//< Remaining number of sources.
+	TInt iNext;						//< Non-zero, if executing Next operation
+
+	// Specify Current Query state
+
+	const SQueryStep *iQueryStep;	//< The Current Query Process (what queries to make)
+	SQueryStep iSpecificQuery;		//< Used in implementing the Specific Single Queries
+	TQuerySession iSession[KMaxQuerySessions]; //< Currently active queries or completed results
+	TDeprecatedAnswer iDeprecate[KMaxDeprecated];	//< Buffer to store the delayed answers
+	TUint iPhase;
+	TUint8 iDeprecatedTop;		//< Number of delayed top answers in iDeprecate[]
+	TUint8 iDeprecatedBottom;	//< Number of delayed bottom answers in iDeprecate[]
+	TUint8 iQuerySteps;			//< Number of Query Steps in the process
+	TUint8 iStepCount;			//< Number of Query Steps activated so far
+	TUint8 iSessionCount;		//< Number of Query Sessions currently active
+	/*
+	// The scope level of the current query.
+	//
+	// Currently the level for GetByName and Query is always "NETWORK".
+	// But, if implemented, it could be "LINK LOCAL", based on the
+	// queried name, for example, if it ends with ".local".
+	//
+	// The level of GetByAddress is the scope level of the address being
+	// queried.
+	//
+	// The query scope limits the sources available to the query: the scope
+	// of the source must be less or equal to the query scope. (For example,
+	// GetByAddress for a link local address is only asked from link local
+	// name resolvers).
+	*/
+	TUint8 iQueryScope;
+	TUint iQueryActive:1;		//< set to 1, when a query is being processed
+	TUint iQueryIsAddr:1;		//< set to 1, when current query is GetByAddr, = 0, otherwise
+	TUint iQueryIsSpecial:1;	//< set to 1, when current query is special (for exact DNS query type)
+	TUint iReadyForAnswer:1;	//< set to 1, when an answer can be delivered back to RHostResolver.
+	TUint iQueryDoneWait:1;		//< Tells what active object is currently waiting: = 0, if WaitQuery, = 1, if QueryDone
+	TUint iQueryComplete:1;		//< set to 1, when query is completed fully (no Next allowed)
+	TUint32 iQueryFlags;		//< Flags to the NewQuery (KDnsMofidier_RD, ...)
+
+	TQuerySession iProbe;		//< Use for server probing
+	CDndEngine &iControl;		//< The control context (for sending errors etc.)
+	MDndListener &iListener;	//< Only for calling KeepRunning()
+	MDnsSource &iDnsclient;		//< DNS protocol handler
+	};
+
+
+// CDndResolverBase::New
+// *********************
+MDndResolver *MDndResolver::New(const TInt aId, CDndEngine &aControl, MDndListener &aListener, MDnsSource &aDnsClient)
+	/**
+	* Create a new instance of CDndResolver.
+	*
+	* This static function creates the real object instance of
+	* the class that has been derived from CDndResolverBase.
+	*
+	* This way, the real implementation of the class is hidden
+	* from any other module (the real class CDndResolver is only
+	* declared and known within resolver.cpp module).
+	*
+	* @param aId identifies the resolver instance (only for debuggind purpose)
+	* @param aControl provides access to the DND environment
+	* @param aListener
+	*	provides access to the socket listener, which manages the
+	*	pool of CDndResolver instances and delivers the requests
+	*	to the resolvers.
+	* @param aDnsClient
+	*	provides access to the DNS protocol driver, which implements
+	*	the DNS protocol details, and manages the associated UDP and
+	*	TCP sockets.
+	* @return
+	*	@li	NULL, if object could not be created (out of memory)
+	*	@li non-NULL, is a pointer to the newly constructed CDndResolver instance
+	*/
+	{
+	return new(ELeave) CDndResolver(aId, aControl, aListener, aDnsClient);
+	}
+
+// Constructor
+CDndResolver::CDndResolver(const TInt aId, CDndEngine &aControl, MDndListener &aListener, MDnsSource &aDnsclient)
+	: iId(aId), iControl(aControl), iListener(aListener), 
+	  iDnsclient(aDnsclient)
+	{
+	// Because array elements cannot have other than default constructors,
+	// need to initialize the iSession[] and iProbe here..
+	for (TInt i = KMaxQuerySessions; --i >= 0; )
+		{
+		iSession[i].Init(this);
+		LOG(iSession[i].iName.Format(_L("%d"), i));
+		}
+	iProbe.Init(this);
+	LOG(iProbe.iName = _L("*"));
+	}
+
+// Destructor
+CDndResolver::~CDndResolver()
+	{
+	// Need to cancel TQuerySession's (if any active)
+	for (TInt i = KMaxQuerySessions; --i >= 0; )
+		iSession[i].Close();
+	}
+
+
+#ifdef _LOG
+// CDndResolver::ShowQuery(aName)
+// ******************************
+void CDndResolver::ShowQuery(const TDnsMessage &aPacket, TInt aReply)
+	/**
+	* Displays the current query or reply (only a debugging tool).
+	*
+	* @param	aPacket	Query or Reply data
+	* @param	aReply	Query vs. reply
+	*/
+	{
+	_LIT(KReply, " Reply ");
+	_LIT(KQuery, " Query ");
+
+	const TDesC &mode = aReply ? KReply : KQuery;
+
+	switch (aPacket.iType)
+		{
+		case KDnsRequestType_GetByName:
+		case KDnsRequestType_GetByAddress:
+			{
+			const TNameRecord &rec = aPacket.NameRecord();
+			TBuf<50> tmp;
+			TInetAddr::Cast(rec.iAddr).OutputWithScope(tmp);
+			if (aReply)
+
+				Log::Printf(_L("\tresolver[%d] SESSION %d Reply [%d], %S, [%S] result=%d"), iId, aPacket.iSession, rec.iFlags, &rec.iName, &tmp, aPacket.iNext);
+			else
+				Log::Printf(_L("\tresolver[%d] SESSION %d Query (%d), %S, [%S]"), iId, aPacket.iSession, aPacket.iNext, &rec.iName, &tmp);
+			}
+			break;
+		case KDnsRequestType_TDnsQuery:
+			{
+			if (aReply)
+				{
+				const TDndReply &reply = aPacket.Reply();
+				Log::Printf(_L("\tresolver[%d] SESSION %d Special Reply, type=%d result=%d"), iId, aPacket.iSession, (TInt)reply.RRType(), aPacket.iNext);
+				}
+			else
+				{
+				const TDnsQuery &query = aPacket.Query();
+				Log::Printf(_L8("\tresolver[%d] SESSION %d Special Query (%d), %S type=%d"), iId, aPacket.iSession, aPacket.iNext, &query.Data(), (TInt)query.Type());
+				}
+			}
+			break;
+		case KDnsRequestType_SetHostName:
+			Log::Printf(_L("\tresolver[%d] SESSION %d SetHostName(%S)%S%d"), iId, aPacket.iSession, &aPacket.HostName(), &mode, aPacket.iNext);
+			break;
+		case KDnsRequestType_GetHostName:
+			Log::Printf(_L("\tresolver[%d] SESSION %d GetHostName(%S)%S%d"), iId, aPacket.iSession, &aPacket.HostName(), &mode, aPacket.iNext);
+			break;
+		default:
+			Log::Printf(_L("\tresolver[%d] SESSION %d Bad TDnsMessagePacket%S(%d,%d)"), iId, aPacket.iSession, &mode, aPacket.iType, aPacket.iNext);
+			break;
+		}
+	}
+#endif
+
+// CDndResolver::Stop
+// ******************
+void CDndResolver::Stop()
+	/**
+	* Stop serving the current request.
+	*
+	* Terminate all pending activity (if any) and mark the
+	* CDndResolver instace available for a new session.
+	*/
+	{
+	iReadyForAnswer = 0;		// ...just to be safe.
+	StopSource();				// stop all DNS activity related to this client, if any
+	if (iQueryActive)
+		{
+		iQueryActive = 0;
+        iDnsclient.QueryEnd();
+		}
+	LOG(Log::Printf(_L("\tresolver[%d] SESSION %d Stopped"), iId, iCurrentQuery().iSession));
+	iCurrentQuery().iSession = 0;
+	iQueryDoneWait = 0;
+	}
+
+
+// CDndResolver::WaitQuery
+// ***********************
+void CDndResolver::WaitQuery()
+	/**
+	* Put the CDndResolver to wait a next request from the RHostResolver.
+	*
+	* The next request could be a Next, or a new GetByName, GetByAddr or
+	* Query.
+	*/
+	{
+	iReadyForAnswer = 0;
+	if (iQueryActive)
+		{
+		// There could be active sessions
+		// doing some queries while we wait for
+		// client to request something (doing
+		// parallel queries).
+		// Leave "query active" on, if any found.
+		// The information is retrieved during this
+		// time is/will be available only for
+		// the Next() query.
+		for (TInt i = 0;; ++i)
+			{
+			if (i == iSessionCount)
+				{
+				iQueryActive = 0;
+				iDnsclient.QueryEnd();
+				break;
+				}
+			if (iSession[i].iStatus == KRequestPending)
+				break;
+			};
+	  }
+	iQueryDoneWait = 0;
+	}
+
+// CDndResolver::QueryDone
+// ***********************
+void CDndResolver::QueryDone(TInt aResult)
+	/**
+	* A query has completed, return a result to the RHostResolver.
+	*
+	* Prepare for the reply into iBuffer for writing to the
+	* "gateway socket". When the reply sender of the listener
+	* is ready to start the transfer, it calls ReplyMessage()
+	* for the buffer to be sent.
+	*
+	* @param	aResult
+	*	the result code of the query. If KErrNone, the buffer
+	*	will contain one answer (TNameRecord or some
+	*	TDnsQryRespBase variant).
+	*
+	* If the reply is an error (< 0), this stops all resolving
+	* activity that might be still going on. An error reply is
+	* always final, continuation with a Next() is not possible.
+	*/
+	{
+	if (!iReadyForAnswer)
+		return;		// Gateway is not expecting answer now...
+	iReadyForAnswer = 0;
+	iQueryDoneWait = 1;
+	iBuffer().iNext = aResult;
+	if (aResult < 0)
+		{
+		StopSource();
+		iBuffer.SetLength(iBuffer().HeaderSize());
+		}
+	LOG(ShowQuery(iBuffer(), 1));
+	iListener.PostReply();	
+	}
+
+// CDndResolver::ReplyMessage
+// **************************
+const TDesC8 &CDndResolver::ReplyMessage()
+	/**
+	* Return the reply message buffer.
+	*
+	* Called by listener sender when it is ready to send the
+	* reply message to the gateway socket. If this resolver
+	* has a reply to send, this must return the buffer.
+	* If no reply is to be sent for this resolver, this must
+	* return an empty buffer reference.
+	*
+	* @return The reply buffer (empty = no reply to send).
+	*/
+	{
+	_LIT8(KNoReply, "");
+	if (iQueryDoneWait)
+		{
+		const TInt result = iBuffer().iNext;
+		// Only set iQueryComplete, *never* clear it, if already set!
+		if (result != KErrCompletion && result < 0)
+			{
+			iQueryComplete = 1;
+			}
+		iQueryDoneWait = 0;
+		return iBuffer;
+		}
+	else
+		return KNoReply;
+	}
+
+// CDndResolver::Session
+// *********************
+TUint16 CDndResolver::Session() const
+	/**
+	* Return the current session id.
+	*
+	* This function is used by the listener to find out the
+	* session id of the request that is currently being
+	* processed by this resolver.
+	*
+	* If the resolver is idle (no request under process), then
+	* this must return 0. All valid session ids are non-zero.
+	*
+	* @return The session (or 0, if none).
+	*/
+	{
+	return iCurrentQuery().iSession;
+	}
+
+void CDndResolver::GetNextAnswer()
+	/**
+	* Collect, sort and return answers to the application.
+	*
+	* Collecting answers from DNS. The answers are sorted into
+	* three classes:
+	*
+	* @li (1)
+	*	usable addresses (current system has a route and source address for them)
+	* @li (2)
+	*	addresses, for which no route exists are put into "TOP" category (if application
+	*	uses one of these, then most likely, a new interface needs to be activated). These
+	*	are returned after all usable addresses in class (1) have been returned..
+	* @li (3)
+	*	other (non-address) answers to the query
+	*
+	* If a GetByName request needs two queries (for example A and AAAA),
+	* the queries may be performed in parallel or one after the other.
+	* If queries are performed one at time, this method will automaticly
+	* start the second query, after the answers (if any) from the first query
+	* have been processed.
+	*
+	* If a query produces any answer belonging to the class (1), they are
+	* returned to the application as soon as they become available.
+	* If queries are not done in parallel, and application is satisfied
+	* with the first answer (does not issue Next()), then the second
+	* query is never sent.
+	*
+	* If a query produces no answers that sort into class (1), both queries
+	* are always executed fully, before any answers are returned (from class
+	* (2) or (3)) to the application.
+	*/
+	{
+	if (!iReadyForAnswer)
+		return;		// Gateway not expecting answer now...
+
+	iBuffer = iCurrentQuery;			// Initialize reply to orginal query
+	TInt pending = 0;
+	TInt notfound = 0;
+
+	// Initialize query buffer flags.
+	iBuffer().NameRecord().iFlags = 0;
+
+	if (iPhase == 0)
+		{
+		//
+		// Collect Phase
+		//
+		for (TInt i = 0; i < iSessionCount; ++i)
+			{
+			TQuerySession &session = iSession[i];
+
+			if (session.iStatus < 0)
+				{
+				// Either pending or no results available (all processed or some error state)
+				// Detect pending queries (only possible to happen if doing queries in parallel)
+				pending |= (session.iStatus == KRequestPending);
+				notfound |= (session.iStatus == KErrDndBadName);
+				continue;
+				}
+			for (;;)
+				{
+				//
+				// Values iSession[i].iStatus >= 0 [0..n] is used for remembering
+				// the number of the next answer to retrieve from the record
+				const TInt ret = session.iSession->DoNext(iBuffer, session.iStatus);
+				if (ret != KErrNone)
+					{
+					// All available answers have been retrieved.
+					session.iStatus = ret;
+					break;
+					}
+				// NOTE: session.iStatus++!!!
+				const TDeprecatedAnswer answer((TUint8)(i), (TUint8)(session.iStatus++));
+
+				if (iQueryIsSpecial)
+					{
+					// All answers are returned as is for special query
+					// (no sorting or any other processing is done)
+					QueryDone(KErrNone);
+					return;
+					}
+
+				const TInetAddr &addr = TInetAddr::Cast(iBuffer().NameRecord().iAddr);
+				if (addr.IsUnspecified() || (addr.Family() != KAfInet && addr.Family() != KAfInet6))
+					{
+					// ...things like CNAME's fall into here. If running out of
+					// space, then just ignore these (Top entries are more important)
+					if (iDeprecatedTop < iDeprecatedBottom)
+						{
+						iDeprecate[--iDeprecatedBottom] = answer;
+						}
+					}
+				else if (iDnsclient.CheckAddress(addr) != KErrNone)
+					{
+					// There is no route for the address, don't return this yet
+					if (iDeprecatedTop < KMaxDeprecated)
+						{
+						iDeprecate[iDeprecatedTop++] = answer;
+						// If running out of space, overwrite deprecated bottom entries
+						// as needed...
+						if (iDeprecatedTop > iDeprecatedBottom)
+							iDeprecatedBottom = iDeprecatedTop;
+						}
+					}
+				else
+					{
+					// This is a usable address as is, return immediately
+					QueryDone(KErrNone);
+					return;
+					}
+				}
+			}
+		//
+		// RR records from the current query have been processed.
+		//
+		if (pending)
+			{
+			LOG(Log::Printf(_L("\tresolver[%d] SESSION %d Request(s) pending"), iId, iCurrentQuery().iSession));
+			// At least one query session is still in progress, continue waiting
+			// Should set some timeout? what?
+			return;
+			}
+		else if (!notfound && iStepCount < iQuerySteps)
+			{
+			// ... but there are alternate steps left, try the next step...
+			//
+			// If all DNS servers worked right, there should be no reason to probe
+			// servers with A, if the probe for AAAA fails. However, sadly, there
+			// broken things out there, which timeout on AAAA, but do reply to A.
+			// Thus, if probing is active, restart it for new query type.
+			if (iProbe.iSession)
+				(void)iProbe.iSession->PickDefaultServer();
+
+			TQuerySession &session = iSession[iSessionCount++];
+			session.Open();
+			if (session.Start(iQueryStep[iStepCount++].iQType))
+				return;
+			}
+		}
+	//
+	// Return TOP deprecated
+	//
+	while (iPhase < iDeprecatedTop)
+		{
+		const MDnsSession *session = iSession[iDeprecate[iPhase].iStep].iSession;
+		const TInt ret = session->DoNext(iBuffer, iDeprecate[iPhase].iIndex);
+		// There is no route for the address, add info for application.
+		iBuffer().NameRecord().iFlags |= EDnsNoRoute;		
+		iPhase++;
+		if (ret == KErrNone)
+			{
+			QueryDone(KErrNone);
+			return;
+			}
+		}
+	//
+	// Return BOTTOM deprecated
+	// (only for GetByAddr or for Next() after GetByName)
+	//
+	iPhase = KMaxDeprecated;
+	if (iQueryIsAddr || iNext)
+		{
+		while (iDeprecatedBottom < KMaxDeprecated)
+			{
+			const MDnsSession *session = iSession[iDeprecate[iDeprecatedBottom].iStep].iSession;
+			const TInt ret = session->DoNext(iBuffer, iDeprecate[iDeprecatedBottom].iIndex);
+			iDeprecatedBottom++;
+			if (ret == KErrNone)
+				{
+				QueryDone(KErrNone);
+				return;
+				}
+			}
+		}
+	if (iNext)
+		QueryDone(iQueryIsAddr ? KErrDndAddrNotFound : KErrDndNameNotFound);
+	else
+		// No answers returned from current source, try next
+		NextSource();
+	}
+
+// CDndResolver::ProbeServers
+// **************************
+void CDndResolver::ProbeServers()
+	/**
+	* Probing servers.
+	*
+	* Send a dummy duplicate query to the next alternate
+	* server. Used in some resolver modes. The result is ignored
+	* by resolver, but the side effect is that the DNS protocol
+	* handler will detect a good server, and make it the default
+	* for the next try.
+	*/
+	{
+	if (iSessionCount == 0)
+		return;
+	if (iProbe.iSession == NULL)
+		{
+		// Use the first primary session (iSession[0]) as a template
+		// for the "probing".
+		if (iSession[0].iSession == NULL)
+			return;	// ...oops!?
+		iProbe.iQType = iSession[0].iQType;
+		iProbe.iSession = iDnsclient.OpenSession(&iProbe);
+		if (iProbe.iSession == NULL)
+			return;
+		LOG(Log::Printf(_L("\tresolver[%d] SESSION %d Probing QType=%d (%S) -- Assigned DNS session [%u]"),
+			iId, (TInt)iCurrentQuery().iSession, (TInt)iProbe.iQType, &iProbe.iName, (TInt)iProbe.iSession->Instance())); 
+		// Use the current query as a probe starting point
+		//(void)iProbe.iSession->NewQuery(*iSession[0].iSession);
+		(void)iProbe.iSession->NewQuery(iCurrentQuery(),iSourceNow,iQueryFlags | KDnsModifier_PQ);
+		
+		// initialize server (which is not tried)
+		(void)iProbe.iSession->PickDefaultServer();
+		}
+	// Start query, but only if there really is another server address
+	if (iProbe.iSession->PickNewServer())
+		{
+		// Note: if DoQueryL fails, we do nothing. This is just probing...
+		TRAP_IGNORE(iProbe.iSession->DoQueryL(iRequestTime, iProbe.iQType));
+		}
+	}
+
+
+// CDndResolver::StopSource
+// ************************
+void CDndResolver::StopSource()
+	/**
+	* Stop all resolving activity from current source.
+	*
+	* The answer can be searched from different sources (such as hosts file,
+	* global DNS or link local name resolution). Answers from different sources are
+	* never mixed, and the first source that gives an answer is used.
+	*
+	* StopSource() cancels all activity (if any) from current source.
+	*/
+	{
+	// Release sessions, if they were created
+	while (iSessionCount > 0)
+		iSession[--iSessionCount].Close();
+	iProbe.Close();
+	}
+
+
+// CDndResolver::NextSource
+// ************************
+void CDndResolver::NextSource()
+	/**
+	* Activate search from next source.
+	*
+	* The answer can be searched from different sources (such as hosts file,
+	* global DNS or link local name resolution). Answers from different sources are
+	* never mixed, and the first source that gives an answer is used.
+	*
+	* NextSource activates the next query source or terminates the
+	* query, if all sources have been tried.
+	*/
+	{
+
+	while (iSourceCount > 0)
+		{
+		StopSource();	// Cleanup from previous source, if any
+
+		// Replies from different source are never mixed
+		// Restart collecting answers too.
+		iPhase = 0;
+		iDeprecatedTop = 0;
+		iDeprecatedBottom = KMaxDeprecated;
+		iSourceNow = *iSourceList++;
+		iSourceCount -= 1;
+
+		// Is this source valid for the query scope (e.g. if source is global
+		// DNS, one should not try to query site or link local queries from it!)
+		// On the other hand, it is legal to make global scope query to link
+		// local or site scope servers.
+		const TUint level = (TUint)Abs((TInt)iSourceNow);
+		if (iQueryScope < level)
+			continue;	// Query scope is less than servers scope, try another source!
+
+		if (StartSource())
+			return;
+		}
+
+	// The name could not be resolved by any source
+	QueryDone(iQueryIsAddr ? KErrDndAddrNotFound : KErrDndNameNotFound);
+	}
+
+
+// CDndResolver::StartSource
+// *************************
+TBool CDndResolver::StartSource()
+	/**
+	* Initiate a source.
+	*
+	* Start/Activate retrieval of query answers from the current source,
+	* as defined by CDndResolver::iSourceNow. Three classes of
+	* sources are supported:
+	*
+	* @li	HOST file (= 0)
+	* @li	Unicast DNS server ( < 0)
+	* @li	Multicast name resolution service ( > 0)
+	*
+	* The absolute value of iSourceNow is the scope
+	* level of the name resolution source (2 = link local, etc..)  
+	*
+	* @returns
+	*	@li	TRUE, if process has been started (or answer already resolved).
+	*	@li	FALSE, if the source is not available or usable
+	*/
+	{
+	// If you define more complex queries, needing more than current max of
+	// steps, then bump up the KMaxQuerySessions! Panic on debug, on release
+	// just ignore the extra steps.
+	ASSERT(iQuerySteps <= KMaxQuerySessions);
+	if (iQuerySteps > KMaxQuerySessions)
+		iQuerySteps = KMaxQuerySessions;
+
+	
+	const TDndConfigParameters &cf = iControl.GetConfig();
+
+	TUint query_order = cf.iQueryOrder;
+
+	if (iSourceNow == 0)
+		{
+		// HOST file or hostname source
+		if (!iQueryIsSpecial)
+			{
+			// Check if the query is for local host name or my own address.
+			TInt res;
+			if (iQueryIsAddr)
+				res = iDnsclient.GetByAddress(iBuffer().iId, TInetAddr::Cast(iBuffer().NameRecord().iAddr), iNext, iBuffer().NameRecord().iName);
+			else
+				res = iDnsclient.GetByName(iBuffer().iId, iBuffer().NameRecord().iName, iNext, TInetAddr::Cast(iBuffer().NameRecord().iAddr));
+			if (res == KErrNone)
+				{
+				LOG(Log::Printf(_L("\tresolver[%d] SESSION %d matched local hostname/address"), iId, iCurrentQuery().iSession));
+				iBuffer().NameRecord().iFlags = EDnsHostName;
+				QueryDone(KErrNone);
+				return TRUE;
+				}
+			// ..and now the HOSTS file
+			LOG(Log::Printf(_L("\tresolver[%d] SESSION %d SEARCHING hosts file"), iId, iCurrentQuery().iSession));
+			TRequestStatus tmp;
+			if (iQueryIsAddr)
+				iControl.iHostsFile.GetByAddress(iBuffer().NameRecord(), tmp);
+			else
+				iControl.iHostsFile.GetByName(iBuffer().NameRecord(), tmp);
+			if (tmp.Int() == KErrNone)
+				{
+				iBuffer().NameRecord().iFlags = EDnsHostsFile;
+				QueryDone(KErrNone);
+				return TRUE;
+				}
+			}
+	
+		if (iBuffer().iId == 0)
+			{
+			// If there is no scope is specified, then only hosts file
+			// is looked.
+			QueryDone(KErrCompletion);
+			return TRUE;
+			}
+		// No Answer from the host file, move on to the next
+		return FALSE;
+		}
+	else if (iSourceNow < 0)
+		{
+		//
+		// Unicast DNS source
+		//
+		LOG(Log::Printf(_L("\tresolver[%d] SESSION %d SEARCHING unicast DNS"), iId, iCurrentQuery().iSession));
+		// Setup parameters for basic query step (steps are done one after another)
+		iQueryStepMaxTime = Max(cf.iMinTime, cf.iMaxTime / iQuerySteps);
+		iQueryStepMinTime = cf.iMinTime;
+		iQueryStepRetries = (TUint8)cf.iRetries;
+		}
+	else
+		{
+		//
+		// Multicast DNS source
+		//
+		// Setup parameters for basic query step 
+		LOG(Log::Printf(_L("\tresolver[%d] SESSION %d SEARCHING multicast DNS"), iId, iCurrentQuery().iSession));
+		query_order |= KQueryOrder_Parallel;	// Multicast steps are always done in parallel
+#ifdef LLMNR_ENABLED
+		// Assumed: "multicast DNS" == LLMNR!
+		iQueryStepMaxTime = Max(cf.iLlmnrMinTime, cf.iLlmnrMaxTime);
+		iQueryStepMinTime = cf.iLlmnrMinTime;
+		iQueryStepRetries = (TUint8)cf.iLlmnrRetries;
+#else
+		iQueryStepMaxTime = Max(cf.iMinTime, cf.iMaxTime);
+		iQueryStepMinTime = cf.iMinTime;
+		iQueryStepRetries = (TUint8)cf.iRetries;
+#endif
+		}
+	//
+	// Start the query/queries
+	//
+	TBool started = FALSE;
+	iSessionCount = 0;
+	iStepCount = 0;
+	while (iStepCount < iQuerySteps)
+		{
+		TQuerySession &session = iSession[iSessionCount++];
+		session.Open();
+		if (session.iSession == NULL)
+			{
+			// If the required session cannot be allocated,
+			// abort the request totally.
+			QueryDone(session.iStatus);
+			return TRUE;
+			}
+		started |= session.Start(iQueryStep[iStepCount++].iQType);
+		if (started && (query_order & KQueryOrder_Parallel) == 0)
+			break;
+		}
+	return started;
+	}
+
+// CDndResolver::HOSTFILE_Next
+// ***************************
+void CDndResolver::HOSTFILE_Next()
+	/**
+	* Return next answer from HOST file.
+	*
+	* The HOST file answers are not currently sorted in any way. The
+	* answers are returned as is, in whatever order the host file
+	* module returns them.
+	*
+	* Only TNameRecord based queries are asked from the HOST file
+	* (e.g. the standart RHostResolver::GetByName and RHostResolver::GetByAddr).
+	* No special queries.
+	*/
+	{
+	// Should never get called with special queries, but if that
+	// happens, just return KErrEof as a safety measure...
+	TRequestStatus tmp(KErrEof);
+	if (!iQueryIsSpecial)
+		{
+		// Temp. kludge: next indicator for hosts file reader is passed in iFlags!
+		iBuffer().NameRecord().iFlags = iNext;
+		if (iQueryIsAddr)
+			iControl.iHostsFile.GetByAddress(iBuffer().NameRecord(), tmp);
+		else
+			iControl.iHostsFile.GetByName(iBuffer().NameRecord(), tmp);
+		// Note: iHostFile returns the result code in TRequestStatus.
+		iBuffer().NameRecord().iFlags = EDnsHostsFile;
+		}
+	QueryDone(tmp.Int());
+	}
+
+// CDndResolver::Start
+// *******************
+void CDndResolver::Start(const TDnsMessageBuf &aMsg)
+	/**
+	* Process a new request from listener/RHostResolver.
+	*
+	* @param aMsg The request message
+	*/
+	{
+	const TDndConfigParameters &cf = iControl.GetConfig();
+
+	// Sanity checks -- determine the min size of a proper request
+	const TInt min_size = aMsg().HeaderSize() +
+		TInt(aMsg().iType == KDnsRequestType_TDnsQuery ? sizeof(TDnsQuery) :
+			 aMsg().iType == KDnsRequestType_GetByName ? sizeof(TNameRecord) :
+			 aMsg().iType == KDnsRequestType_GetByAddress ? sizeof(TNameRecord) :
+			 sizeof(THostName));
+	if (aMsg.Length() < min_size) // If short query => treat as cancel!
+		{
+		LOG(Log::Printf(_L("\tresolver[%d] SESSION %d Cancelled by client (len=%d)"), iId, iCurrentQuery().iSession,  aMsg.Length()));
+		Stop();
+		return;
+		}
+
+	// Stack should never start new request before delivering the response.
+	// Exception is the Cancel-request which is treated above.
+	ASSERT(iQueryDoneWait == 0);
+	
+	iBuffer = aMsg;
+	//
+	// A new command/query from the application
+	//
+	LOG(ShowQuery(iBuffer(), 0));
+
+	// If query is not active, it will be after this
+	if (iQueryActive == 0)
+		{
+		iQueryActive = 1;
+		iDnsclient.QueryBegin();
+		}
+
+	iReadyForAnswer = 1;			// an answer can now be returned...
+	iRequestTime.UniversalTime();	// Set the time of request
+
+	iNext = iBuffer().iNext;
+	if (iNext)
+		{
+		// This is Next() processing, retrieving
+		// the additional records.
+		if (iQueryComplete)
+			{
+			// An application/something is stubbornly calling Next() after
+			// receiving an error (query complete) or the query was "one shot"
+			// that returned "KErrNone", and was complete at that (no Next).
+			// This error should terminate the Next() loop in app!
+			QueryDone(KErrNotFound);
+			return;
+			}
+		iBuffer = iCurrentQuery;	// Reset to orginal query (Next() cannot change the query)
+		if (iSourceNow == 0)
+			HOSTFILE_Next();
+		else
+			GetNextAnswer();
+		}
+	else
+		{
+		//
+		// Start processing of new query, not Next()
+		//
+		iQueryComplete = 0;
+		iCurrentQuery = iBuffer;		// Remember current original query
+		iQueryFlags = KDnsModifier_RD;	// By default, request recursive querying from the server(s).
+		StopSource();
+		switch (iBuffer().iType)
+			{
+			case KDnsRequestType_GetByName:
+			case KDnsRequestType_GetByAddress:
+				{
+				TNameRecord &rec = iCurrentQuery().NameRecord();
+
+				if (iCurrentQuery().iType == KDnsRequestType_GetByAddress)
+					{
+					iQueryIsAddr = 1;
+					// The scope of GetByAddress query is determined from the scope level
+					// of the address being queried.
+					iQueryScope = (TUint8)TInetAddr::Cast(rec.iAddr).Ip6Address().Scope();
+					iQueryIsSpecial = 0;
+					iQueryStep = KGetByAddress;
+					iQuerySteps = sizeof(KGetByAddress) / sizeof(KGetByAddress[0]);
+					}
+				else
+					{
+					iQueryIsAddr = 0;
+					// All getbyname queries are global for now. As a future feature
+					// It might be considered, that single label or names ending with
+					// ".local" would have have local scope -- msa
+					iQueryScope = KIp6AddrScopeNetwork;
+
+#ifdef DND_DCM_EXTENSION
+					iQueryIsSpecial = 0;
+					TInt i;
+					while ((i = rec.iName.Locate('?')) >= 0)
+						{
+						const TPtrC prefix = rec.iName.Left(i+1);
+						if (prefix.Compare(KDnsNonrecursive) == 0)
+							{
+							iQueryFlags &= ~KDnsModifier_RD;	// Reset recursive flag.
+							}
+						else if (cf.iQueryHack)
+							{
+							// Special Query
+							iQueryIsSpecial = 1;
+							TInt q = sizeof(KQueryExtension) / sizeof(KQueryExtension[0]);
+							while (--q >= 0 && prefix.Compare(*KQueryExtension[q].iPrefix) != 0)
+								/* NOTHING */;
+							if (q < 0)
+								{
+								// Requested query is not supported
+								QueryDone(KErrNotSupported);
+								return;
+								}
+							iQueryStep = &KQueryExtension[q].iQuery;
+							iQuerySteps = 1;
+							}
+						else
+							{
+							QueryDone(KErrNotSupported);
+							return;
+							}
+						rec.iName.Delete(0,i+1);
+						}
+
+					if (!iQueryIsSpecial)
+						{
+						// Standard GetByName
+						if ((cf.iQueryOrder & KQueryOrder_PreferA) == 0)
+							{
+							iQueryStep = KGetByName1;
+							iQuerySteps = sizeof(KGetByName1) / sizeof(KGetByName1[0]);
+							}
+						else
+							{
+							iQueryStep = KGetByName2;
+							iQuerySteps = sizeof(KGetByName2) / sizeof(KGetByName2[0]);
+							}
+						// If query is to be limited to only one (either A or AAAA), do
+						// the preferred query only (the first in the chosen list).
+						if (cf.iQueryOrder & KQueryOrder_OneQuery)
+							iQuerySteps = 1;
+						}
+#else
+					const TInt i = rec.iName.Locate('?');
+
+					if (i < 0)
+						{
+						// Standard GetByName
+						iQueryIsSpecial = 0;
+						if ((cf.iQueryOrder & KQueryOrder_PreferA) == 0)
+							{
+							iQueryStep = KGetByName1;
+							iQuerySteps = sizeof(KGetByName1) / sizeof(KGetByName1[0]);
+							}
+						else
+							{
+							iQueryStep = KGetByName2;
+							iQuerySteps = sizeof(KGetByName2) / sizeof(KGetByName2[0]);
+							}
+						// If query is to be limited to only one (either A or AAAA), do
+						// the preferred query only (the first in the chosen list).
+						if (cf.iQueryOrder & KQueryOrder_OneQuery)
+							iQuerySteps = 1;
+						}
+					else if (cf.iQueryHack)
+						{
+						// Special Query
+						iQueryIsSpecial = 1;
+						const TPtrC prefix = rec.iName.Left(i+1);
+						TInt q = sizeof(KQueryExtension) / sizeof(KQueryExtension[0]);
+						while (--q >= 0 && prefix.Compare(*KQueryExtension[q].iPrefix) != 0)
+							/* NOTHING */;
+						rec.iName.Delete(0,i+1);
+						if (q < 0)
+							{
+							// Requested query is not supported
+							QueryDone(KErrNotSupported);
+							return;
+							}
+						iQueryStep = &KQueryExtension[q].iQuery;
+						iQuerySteps = 1;
+						}
+					else
+						{
+						QueryDone(KErrNotSupported);
+						return;
+						}
+#endif
+					}
+				}
+				break;
+			case KDnsRequestType_TDnsQuery:
+				// Special Query
+				iQueryIsSpecial = 1;
+				iQueryIsAddr = 0;
+				iQueryScope = KIp6AddrScopeNetwork;
+				iSpecificQuery.iQType = EDnsQType(iBuffer().Query().Type());
+				iQueryStep = &iSpecificQuery;
+				iQuerySteps = 1;
+#ifdef DND_DCM_EXTENSION
+				if (iBuffer().Query().Type() == KDnsQTypeCacheClear)
+					{
+					TRAPD(err, iListener.HandleCommandL(EDndFlush));
+					iQueryComplete = 1;	// "Next()" is not allowed!
+					QueryDone(err);
+					return;							
+					}
+#endif
+				break;
+			case KDnsRequestType_SetHostName:
+				iQueryComplete = 1;	// "Next()" is not allowed (only needed for ret == KErrNone case)
+				QueryDone(iDnsclient.SetHostName(iBuffer().iId, iBuffer().HostName()));
+				return;
+			case KDnsRequestType_GetHostName:
+				{
+				const TInt ret = iDnsclient.GetHostName(iBuffer().iId, iBuffer().HostName(), *this);
+				if (ret <=  0)
+					{
+					iQueryComplete = 1;	// "Next()" is not allowed (only needed for ret == KErrNone case)
+					QueryDone(ret);		// GetHostName completed, we are done!
+					}
+				// GetHostName is pending on uniquenes test (ret == 1)
+				return;
+				}
+			default:
+				QueryDone(KErrNotSupported);
+				return;
+			}
+		//
+		// Setup NS source (now fixed, make configurable later)
+		//
+		iSourceList = KSourceOrder;
+		iSourceCount = sizeof(KSourceOrder) / sizeof(KSourceOrder[0]);
+		NextSource();
+		}
+	}
+
+// Only used for GetHostName
+void CDndResolver::ReplyCallback(const TInt aResult)
+	{
+	iBuffer = iCurrentQuery;
+	QueryDone(aResult);
+	}
+
+//
+//
+
+TQuerySession::TQuerySession() : iTimeout(TQuerySessionTimeoutLinkage::Timeout)
+	{
+	}
+
+// TQuerySession::Init
+// *******************
+void TQuerySession::Init(CDndResolver *const aResolver)
+	/**
+	* Initialize structure.
+	*
+	* A separate Init is required, because arrays cannot have non-defaulf
+	* constructors. Need to patch the iResolver link "manually".
+	*/
+	{
+	iResolver = aResolver;
+	// ...for other members, the default constructor is sufficient
+	}
+
+
+// TQuerySession::Open
+// *******************
+void TQuerySession::Open()
+	/**
+	* Activate a query session.
+	*
+	* Assume CDndResolver::iCurrentQuery holds the request from the client.
+	* Establish a session with the DNS protocol provides and initialize
+	* it with the current question (NewQuery).
+	*/
+	{
+	iSession = iResolver->iDnsclient.OpenSession(this);
+	if (iSession == NULL)
+		{
+		// Cannot allocate a session
+		LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- OUT OF DNS SESSIONS!"),
+			iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName));
+		iStatus = KErrNoMemory;
+		}
+	else
+		{
+		LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- Assigned DNS session [%u]"),
+			iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName, (TInt)iSession->Instance()));
+		// ..if NewQuery returns an error, it will be the result of the query. Otherwise
+		// use the KErrEof as initial value.
+		iStatus = iSession->NewQuery(iResolver->iCurrentQuery(), iResolver->iSourceNow, iResolver->iQueryFlags);
+		if (iStatus == KErrNone)
+			iStatus = KErrEof;	// No content yet.
+#ifdef SYMBIAN_DNS_PUNYCODE
+		else // in case the Query String is UTF-16 encoded
+			{
+			iResolver->QueryDone(iStatus);
+			}
+#endif //SYMBIAN_DNS_PUNYCODE
+		}
+	}
+
+// TQuerySession::SetTimer
+// ***********************
+void TQuerySession::SetTimer(TUint aTimeout)
+	/**
+	* Request a Timeout() call after the specied number of seconds.
+	*/
+	{
+	LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- set timeout = %ds"),
+		iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName, (TInt)aTimeout));
+	iResolver->iControl.Timer().Set(iTimeout, aTimeout);
+	}
+
+// TQuerySession::Start
+// ********************
+TBool TQuerySession::Start(EDnsQType aQType)
+	/**
+	* Start the query process for a specific query type.
+	*
+	* @param aQType The query type (A, AAAA, MX, etc.)
+	*/
+	{
+	iQType = aQType;
+	LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- Start"),
+		iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName));
+	iServerCount = 1;
+	iQueryRetry = 0;
+	iQueryTimeouts = 0;
+
+	if (!iSession)
+		return FALSE;
+	
+	iStatus = KErrEof;
+
+	if (iSession->PickDefaultServer())
+		{
+		SendDnsQuery();
+		return TRUE;
+		}
+	return FALSE;
+	}
+
+// TQuerySession::Cancel
+// *********************
+void TQuerySession::Cancel()
+	/**
+	* Cancel and stop all activity (if any) with this session.
+	*
+	* This cancels set timers and any DNS queries that are in
+	* progress.
+	*/
+	{
+	iTimeout.Cancel();
+
+	if (!iSession)
+		return;
+
+	iSession->CancelQuery();
+	if (iStatus == KRequestPending)
+		{
+		LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- CANCELED"),
+			iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName));
+		iStatus = KErrCancel;
+		}
+	}
+
+
+// TQuerySession::Close
+// ********************
+void TQuerySession::Close()
+	/**
+	* Close session.
+	*
+	* First calls Cancel() to call all activity, and then also
+	* closes the session with DNS protocol provider.
+	*/
+	{
+	iPendingQuery = 0;	// Cancel pending queries too!
+	Cancel();
+	if (!iSession)
+		return;
+	iSession->Close();
+	LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- DNS [%u] closed"),
+		iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName, (TInt)iSession));
+	iSession = NULL;
+	}
+
+
+// TQuerySession::RecvReply
+// ***********************
+void TQuerySession::ReplyCallback(TInt aErr)
+	/**
+	* Callback from DNS client.
+	*
+	* DNS client has completed something.
+	*
+	* @param aErr The completion result or some notify.
+	*/
+	{
+	if (iStatus != KRequestPending)
+		{
+		LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- RecvReply(%d) Ignore event"),
+			iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName, aErr));
+		return;	// Unexpected event, ignore!
+		}
+
+	if (aErr > 0)
+		{
+		// Notify Messages, just ignore if not recognised.
+		if (aErr == KDnsNotify_QUERY_SENT)
+			{
+			if (iWaitingQuerySent)
+				{
+				iWaitingQuerySent = 0;	// got it...
+				LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- RecvReply(%d) Query Sent"),
+					iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName, aErr));
+				SetTimer(iQueryTimeoutValue);
+				}
+			}
+		else if (aErr == KDnsNotify_HAVE_SERVERLIST)
+			{
+			// Currently does nothing with this.
+			LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- RecvReply(%d) Has serverlist"),
+				iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName, aErr));
+			}
+		else if (aErr == KDnsNotify_USING_TCP)
+			{
+			iQueryRetry = iResolver->iQueryStepRetries;
+			iQueryTimeoutValue = iResolver->iQueryStepMaxTime;
+			iWaitingQuerySent = 0;
+			LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- RecvReply(%d) Query using TCP, disable retransmit"),
+				iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName, aErr));
+			SetTimer(iQueryTimeoutValue);
+			}
+		}
+	else if (aErr != KErrNone && (aErr > KErrDndNameNotFound || aErr < KErrDndServerUnusable))
+		{
+		// The error is not query specific DND error code, assume it is
+		// something general and abort the query totally.
+		LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- RecvReply(%d) Query completed"),
+			iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName, aErr));
+		iResolver->QueryDone(aErr);
+		}
+	else
+		{
+		iStatus = aErr;
+		iTimeout.Cancel();
+		LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- RecvReply(%d) Handle event"),
+			iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName, aErr));
+		Event(FALSE);
+		}
+	}
+
+// TQuerySession::Timeout
+// **********************
+void TQuerySession::Timeout(const TTime &aNow)
+	/**
+	* No response from the DNS handler within specified time, handle timeout.
+	*
+	* @param aNow The current time
+	*/
+	{
+	LOG(Log::Printf(_L("-->\tresolver[%d] SESSION %d QType=%d (%S) -- timeout start"),
+		iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName));
+	if (iWaitingQuerySent)
+		{
+		//
+		// Don't wait forever, abort resolving if max setup time exceeded
+		//
+		const TDndConfigParameters &cf = iResolver->iControl.GetConfig();
+		TTimeIntervalSeconds seconds;
+		if (aNow.SecondsFrom(iResolver->iRequestTime, seconds) != KErrNone ||
+			seconds.Int() > STATIC_CAST(TInt, cf.iSetupTime))
+			{
+			iStatus = KErrDndServerUnusable;
+			LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- Timeout, max wait expired [%d > %d]: %d"),
+				iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName, seconds.Int(), cf.iSetupTime, iStatus));
+			}
+		else
+			{
+			LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- Timeout, has waited %ds [max %d]"),
+				iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName, seconds.Int(), cf.iSetupTime));
+			}
+		}
+	else
+		{
+		LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- timeout"),
+			iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName));
+		}
+	// Note that Timeout alone does not touch the iStatus, nor does it
+	// cancel the request, if any is active. It's up to Event to decide
+	// on these issues.
+	Event(TRUE);
+	LOG(Log::Printf(_L("<--\tresolver[%d] SESSION %d QType=%d (%S) -- timeout exit"),
+		iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName));
+	}
+
+
+// TQuerySession::Event
+// ********************
+void TQuerySession::Event(TBool aTimeOut)
+	/**
+	* Shared handling of session events: timeouts and DNS query completions.
+	*
+	* @param aTimeOut is TRUE, if this is a timeout event; and FALSE, if
+	*			event caused by DNS completion.
+	*/
+	{
+	const TDndConfigParameters &cf = iResolver->iControl.GetConfig();
+
+	// Handle events from DNS or timeout
+	// *********************************
+	// TIMEOUT or REPLY to a pending query has been received
+	//
+	const TBool server_unusable = (iStatus == KErrDndRefused || iStatus == KErrDndServerUnusable);
+	if (!aTimeOut && !server_unusable)
+		{
+		// This query has been completed with some definite result, notify
+		// the main resolver to handle the reply.
+		//
+		iResolver->GetNextAnswer();
+		return;
+		}
+
+
+	if (iWaitingQuerySent && !server_unusable)
+		{
+		// Still waiting for the first query packet to be sent. Request a
+		// resend attempt of the same query again (without counting it as
+		// retry). This is a wait for getting interfaces up (normal timeouts
+		// do not apply yet).
+
+		// Reload the query too (this is only sometimes needed with PTR
+		// queries if the filter iLockId cannot be determined due to
+		// missing interfaces. And only happens if query address was
+		// without scope id.
+		(void)iSession->NewQuery(iResolver->iCurrentQuery(), iResolver->iSourceNow, iResolver->iQueryFlags);
+		SendDnsQuery();
+		return;
+		}
+
+	if (aTimeOut)
+		iQueryTimeouts++;	// Timeout occurred while resolving the DNS Query
+
+
+	// Careful with iStatus: if timeout occurs, it is probably KRequestPending.
+	// Needs to be changed to something else, if GetNextAnswer is called (otherwise
+	// it will think that there are pending query and may do nothing!)
+	//
+	if (cf.iSprayMode || iResolver->iSourceNow > 0 /* == multicast resolution mode */)
+		{
+		if (iWaitingAfterSpray)
+			{
+			LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- Delay after spray completed"),
+				iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName));
+			//
+			// The delay after "spray" has expired, now start a new round (a retry)
+			// (unless maximum retries has been reached)
+			//
+			iWaitingAfterSpray = 0;
+			iQueryTimeouts = 0;
+			(void)iSession->PickDefaultServer();
+			iStatus = KErrTimedOut;
+			iServerCount = 1;
+			++iQueryRetry;
+			if (iQueryRetry > iResolver->iQueryStepRetries)
+				iResolver->GetNextAnswer();
+			else
+				SendDnsQuery();
+			}
+		else if (iSession->PickNewServer())
+			{
+			LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- More servers to spray"),
+				iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName));
+			//
+			// Yet another server to spray, replicate query
+			//
+			iWaitingAfterSpray = 0;
+			++iServerCount;
+			SendDnsQuery();	// Replicate query to another server
+			}
+		else if (iQueryTimeouts > 0)
+			{
+			// All servers have been sprayed and at least one of them
+			// has not answered, now wait maxtime - (servers * mintime),
+			// before starting the next spraying round
+			//
+			LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- All servers sprayed, now wait results"),
+				iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName));
+			iWaitingAfterSpray = 1;
+			const TInt maxtime = Max(iResolver->iQueryStepMinTime, iResolver->iQueryStepMaxTime / (iResolver->iQueryStepRetries+1));
+			SetTimer(Max(iResolver->iQueryStepMinTime, maxtime - iResolver->iQueryStepMinTime * iServerCount));
+			}
+		else
+			{
+			LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- All servers replied not found"),
+				iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName));
+			// All servers sprayed, and none of the short spray timeouts expired
+			// (make a heuristic GUESS, that all servers replied, but all were either
+			// unusable or "skip not found" is active. Just goto to the next step or
+			// return not found.
+			iResolver->GetNextAnswer();
+			}
+		}
+	else
+		{
+		++iQueryRetry;	// This is a true retry
+		if (iQueryRetry > iResolver->iQueryStepRetries ||
+			(server_unusable && !iSession->PickNewServer()))
+			{
+			iStatus = KErrTimedOut;
+			iResolver->GetNextAnswer();// Activates next query step or returns not found.
+			}
+		else
+			{
+			iResolver->ProbeServers();	// ...keep probing for alternate servers in parallel (at each timeout)
+			SendDnsQuery();
+			}
+		}
+	}
+
+
+// TQuerySession::SendDnsQuery
+// ***************************
+void TQuerySession::SendDnsQuery()
+	/**
+	* Activate a new DNS query to be sent.
+	*/
+	{
+	if (iSendingQuery)
+		{
+		// Doing everyhing in callbacks forces extra work
+		// to be done to prevent deep recursion...
+		LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- NEW QUERY PENDING"),
+			iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName));
+
+		iPendingQuery = 1;
+		return;
+		}
+	// Create an UDP message to the DNS
+	do
+		{
+		LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- SENDING DNS QUERY"),
+			iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName));
+		iStatus = KRequestPending;
+		iSendingQuery = 1;		// ..to detect dangerous recursion
+		iPendingQuery = 0;		// ..to detect that a new query should be sent
+		iWaitingQuerySent = 1; // Mark: waiting for QUERY_SENT
+		TRAPD(err, (void)iSession->DoQueryL(iResolver->iRequestTime, iQType));
+		iSendingQuery = 0;
+		if (err != KErrNone)
+			{
+			// Assume some serious error situation and forget about
+			// possible pending query, cancel everything with error.
+			iResolver->QueryDone(err);
+			return;
+			}
+		}
+	while (iPendingQuery);
+
+	if (iStatus == KRequestPending)
+		{
+		const TDndConfigParameters &cf = iResolver->iControl.GetConfig();
+		const TUint maxtime = iResolver->iQueryStepMaxTime;
+
+		// Compute the timeout for the next query (starts ticking
+		// when the packet has been sent (after QUERY_SENT)
+		//
+		if (cf.iSprayMode || iResolver->iSourceNow > 0)
+			iQueryTimeoutValue = iResolver->iQueryStepMinTime;	// Send to all servers using mintime 
+		else if (cf.iRetries == 0)
+			iQueryTimeoutValue = maxtime;		// No retries, just single query
+		else if (iQueryRetry == 0)
+			iQueryTimeoutValue = iResolver->iQueryStepMinTime;	// This is the first send -- use min time
+		else
+			{
+			// This a retransmission (divide the remaining time even with the retries)
+			iQueryTimeoutValue = Max(iResolver->iQueryStepMinTime, (maxtime - iResolver->iQueryStepMinTime) / iResolver->iQueryStepRetries);
+			}
+		LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- WAITING FOR SEND"),
+			iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName));
+		// Setup "poll" timer, if the
+		// DNS side does not send the QUERY_SENT notification.
+		// (retry count is not incremented, so it will "poll"
+		// as long as notification/reply arrives or user cancels
+		// the resolving).
+		if (iWaitingQuerySent)
+			SetTimer(cf.iSetupPoll);
+		}
+	else
+		{
+		LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- COMPLETED WITHIN"),
+			iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName));
+		}
+	}