tcpiputils/dnd/src/dns.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 31 Aug 2010 16:45:15 +0300
branchRCL_3
changeset 57 abbed5a4b42a
parent 53 7e41d162e158
child 58 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.cpp - the main client module of the DNS.
//

#include <e32math.h>
#include <in_sock.h>
#include <in6_opt.h>
#include "dns.h"
#include "dnd.hrh"		// EDndDump
#include <networking/dnd_err.h>
#include "engine.h"
#include "servers.h"
#include "cache.h"
#include "inet6log.h"
#ifdef LLMNR_ENABLED
#	include "llmnrresponder.h"
#endif
#include "dnd_ini.h"

#ifdef EXCLUDE_SYMBIAN_DNS_PUNYCODE
#undef SYMBIAN_DNS_PUNYCODE
#endif //EXCLUDE_SYMBIAN_DNS_PUNYCODE

class CDnsService : public CDndDnsclient
	{
public:
	CDnsService(CDndEngine &aControl, MDnsServerManager &aServerManager) : CDndDnsclient(aControl, aServerManager) {}
	void ConstructL();
	~CDnsService();
	virtual void ConfigurationChanged();

	TInt SetHostName(TUint32 aId, const THostName &aName);
	TInt GetHostName(TUint32 aId, THostName &aName, MDnsResolver &aCallback);

	TInt GetByAddress(TUint32 aId, const TInetAddr &aAddr, TInt aNext, THostName &aName);
	TInt GetByName(TUint32 aId, const THostName &aName, TInt aNext, TInetAddr &aAddr);
private:
#ifdef LLMNR_ENABLED
	CDndLlmnrResponder *iLlmnrResponder; //< Pointer to the LLMNR responder object
#endif
	TUint32 GetNetId(TUint32 aIndex);
	TInetAddressInfo *GetAddressList(TInt &aN);
	};


void CDnsService::ConstructL()
	{
	CDndDnsclient::ConstructL();
#ifdef LLMNR_ENABLED
	iLlmnrResponder = new (ELeave) CDndLlmnrResponder(iControl, iServerManager, iHostNames);
	iLlmnrResponder->ConstructL();
#endif
	}

CDnsService::~CDnsService()
	{
#ifdef LLMNR_ENABLED
	delete iLlmnrResponder;
#endif
	}
	

void CDnsService::ConfigurationChanged()
	{
#ifdef LLMNR_ENABLED
	iLlmnrResponder->ConfigurationChanged();
#endif
	}
//
// Gateway hostname processing to the responder
//
TInt CDnsService::SetHostName(TUint32 aId, const THostName &aName)
	{
	const TInt res = iHostNames.Map(aId, aName);
#ifdef LLMNR_ENABLED
	return res == KErrNone ? iLlmnrResponder->SetHostName(aId, iHostNames.Find(aId)) : res;
#else
	return res;
#endif
	}

TInt CDnsService::GetHostName(TUint32 aId, THostName &aName, MDnsResolver &aCallback)
	{
	aName = iHostNames.Find(aId);
#ifdef LLMNR_ENABLED
	return iLlmnrResponder->GetHostName(aId, aCallback);
#else
	(void)aCallback;
	return KErrNone;
#endif
	}



TUint32 CDnsService::GetNetId(TUint32 aIndex)
	{
	TPckgBuf<TSoInetIfQuery> opt;
	opt().iIndex = aIndex;
	return iControl.iSocket.GetOpt(KSoInetIfQueryByIndex, KSolInetIfQuery, opt) == KErrNone
		? opt().iZone[15] : 0;
	}

TInetAddressInfo *CDnsService::GetAddressList(TInt &aN)
	/**
	* Returns a list of all current addresses.
	*
	* @retval aN	The number of addresses (if return is non-NULL).
	* @return		The address list, or NULL if not available.
	*
	* The returned address list must be freed by the caller
	* as "delete[] list".
	*/
	{
	TPtr8 empty(NULL, 0);
	aN = iControl.iSocket.GetOpt(KSoInetAddressInfo, KSolInetIfQuery, empty);
	if (aN <= 0)
		return NULL;
	TInetAddressInfo *p = new TInetAddressInfo[aN];
	if (p == NULL)
		return NULL;	// No memory available.
	TPtr8 opt((TUint8 *)p, aN*sizeof(TInetAddressInfo));
	aN = iControl.iSocket.GetOpt(KSoInetAddressInfo, KSolInetIfQuery, opt);
	if (aN < 0)
		aN = 0;
	else
		aN = opt.Length() / (TInt)sizeof(TInetAddressInfo);
	return p;
	}


TInt CDnsService::GetByAddress(TUint32 /*aId*/, const TInetAddr &aAddr, TInt /*aNext*/, THostName &aName)
	/**
	* Returns the local hostname, if address is my own.
	*/
	{
	TPckgBuf<TSoInetIfQuery> opt;
	opt().iSrcAddr = aAddr;
	if (iControl.iSocket.GetOpt(KSoInetIfQueryBySrcAddr, KSolInetIfQuery, opt) != KErrNone)
		return KErrNotFound;
	aName = iHostNames.Find(opt().iZone[15]);	// Locate hostname for network id.
	return (aName.Length() > 0) ? KErrNone : KErrNotFound;
	}

TInt CDnsService::GetByName(TUint32 aId, const THostName &aName, TInt /*aNext*/, TInetAddr &aAddr)
	/**
	* Returns one of my own addresses, if the hostname is local.
	*/
	{
	const TDesC &name = iHostNames.Find(aId);
	if (aName.Length() > 0 && name.CompareF(aName) != 0)
		return KErrNotFound;	// The query name is not a local host.
	//
	// Locate a local addres from some interface within the specific network.
	//
	TInt N;
	TInetAddressInfo *list = GetAddressList(N);
	if (list == NULL)
		return KErrNotFound;

	TUint32 iface = 0;
	TUint32 netid = 0;
	TInt scope = -1;
	TInt best = 0;
	for (TInt i = 0; i < N; ++i)
		{
		if (list[i].iState != TInetAddressInfo::EAssigned)
			continue;
		if (list[i].iInterface != iface)
			{
				iface = list[i].iInterface;
				netid = GetNetId(iface);
			}
		if (netid != aId)
			continue;	// Not this address.
		if (list[i].iAddress.Scope() > scope)
			{
			scope = list[i].iAddress.Scope();
			best = i;
			}
		}
	aAddr.SetAddress(list[best].iAddress);
	aAddr.SetScope(list[best].iScopeId);
	delete[] list;
	return scope < 0 ? KErrNotFound : KErrNone;
	}

//
//

CDndDnsclient *CDndDnsclient::NewL(CDndEngine &aControl, MDnsServerManager &aServerManager)
	{
	LOG(Log::Printf(_L("CDndDnsclient::NewL()")));

	CDnsService *const dns = new (ELeave) CDnsService(aControl, aServerManager);
	CleanupStack::PushL(dns);
	dns->ConstructL();
	CleanupStack::Pop();
	return dns;
	}

CDndDnsclient::CDndDnsclient(CDndEngine &aControl, MDnsServerManager &aServerManager)
	: CDnsSocket(aControl.GetConfig().iEDNS0), iControl(aControl), iServerManager(aServerManager)
	{
	}

void CDndDnsclient::ConstructL()
	{
	LOG(Log::Printf(_L("CDndDnsclient::ConstructL() size=%d"), (TInt)sizeof(*this)));
	CDnsSocket::ConstructL();

	iCache = new (ELeave) CDndCache();
	iCache->ConstructL();

	TPtrC localhost;
	if(iControl.FindVar(DND_INI_HOST, DND_INI_HOSTNAME, localhost))
		(void)iHostNames.Reset(localhost);
	else
		(void)iHostNames.Reset(KDndIni_Hostname);
	}




void CDndDnsclient::HandleCommandL(TInt aCommand)
	{
#ifdef _LOG
	switch(aCommand)
		{
		case EDndDump:
			if (iCache)
				iCache->Dump(iControl);
			break;
		default:
			break;
		}
#endif
	if (aCommand == EDndFlush)
		iCache->Flush();
	}

CDndDnsclient::~CDndDnsclient()
	{
	DeactivateSocket();

	// Just to be sure, cancel server list notifys (if any)
	// (this means that SERVER MANAGER MUST NOT BE DELETED
	// before this object!)
	for (TUint i = 0; i < KDndNumRequests; i++)
		iServerManager.CloseList(iDndReqData[i].iFilter);
	delete iCache;
	}

// CDndDnsclient::CheckAddress
// ***************************
TInt CDndDnsclient::CheckAddress(const TInetAddr &aDestination)
	{
	TPckgBuf<TSoInetIfQuery> opt;
	opt().iDstAddr = aDestination;
	
	const TInt err = iControl.iSocket.GetOpt(KSoInetIfQueryByDstAddr, KSolInetIfQuery, opt);
	if (err == KErrNone && TInetAddr::Cast(opt().iSrcAddr).IsUnspecified())
		return KErrNotFound;	// No route or source address.
	return err;
	}



// TDndReqData::PickDefaultServer
// ********************************
TBool TDndReqData::PickDefaultServer()
	{
	TInetAddr tmp;
	TBool ret = FALSE;

	if (iCurrentServer == 0)
		ret = iOwner->iServerManager.OpenList(iFilter, this) > 0;
	iOwner->iCache->GetServerAddress(iOwner->iServerManager.NameSpace(iFilter, iCurrentServer), tmp);
	iFilter.iServerId = iOwner->iServerManager.ServerId(tmp);
	iCurrentServer = iOwner->iServerManager.Next(iFilter, 0);
#ifdef _LOG
	if (iCurrentServer)
		Log::Printf(_L("\t\tDNS session [%u] default nameserver (id=%d) assigned"), (TInt)this, (TInt)iCurrentServer);
	else
		Log::Printf(_L("\t\tDNS session [%u] no nameservers available for this session"), (TInt)this);
#endif
	return ret || iCurrentServer != 0;
	}


// TDndReqData::PickNewServer
// ****************************
TBool TDndReqData::PickNewServer()
	{
	if (iCurrentServer == 0)
		iOwner->iServerManager.BuildServerList();
	iCurrentServer = iOwner->iServerManager.Next(iFilter, iCurrentServer);
#ifdef _LOG
	if (iCurrentServer)
		Log::Printf(_L("\t\tDNS session [%u] next nameserver (id=%d) assigned"), (TInt)this, (TInt)iCurrentServer);
	else
		Log::Printf(_L("\t\tDNS session [%u] next, no alternate nameservers to assign"), (TInt)this);
#endif
	return iCurrentServer != 0;
	}

void TDndReqData::ServerListComplete(const TDnsServerFilter &aFilter, TInt aResult)
	{
	if (&aFilter == &iFilter)
		{
		iCurrentServer = iOwner->iServerManager.Next(iFilter, 0);
		if (iIsReqPending)
			{
			if (aResult == KErrNone)
				{
				if (PickDefaultServer())
					{
					iOwner->ReSend(*this);
					aResult = KDnsNotify_HAVE_SERVERLIST;
					}
				else
					aResult = KErrDndServerUnusable;
				}
			SendResponse(aResult);
			}
		}
	}


// CDndDnsclient::OpenSession
// **************************
/**
// A session is internally represented by a TDndReqData. Find an unused
// entry and assign it. Unused entry is recognized by it having no
// callback function (= NULL).
*/
MDnsSession *CDndDnsclient::OpenSession(MDnsResolver *const aCallback)
	{
	TUint i;
	//
	// Locate available TDndReqData entry...
	//
	for (i = 0;; ++i)
		{
		if (i >= KDndNumRequests)
			return NULL;	// No "sessions" available
		// For now, assume the slot is available, if no callback has been installed
		if (iDndReqData[i].iCallback == NULL)
			break;
		}
	TDndReqData &rq = iDndReqData[i];
	rq.iCallback = aCallback;
	rq.iOwner = this;
	iActivityCount++;	// Count each open session as "activity".
	return &rq;
	}


void CDndDnsclient::Error(const TInetAddr &aServer, TInt /*aError*/)
	{
	const TUint server_id = iServerManager.ServerId(aServer);
	if (server_id == 0)
		return;		// -- nothing to do, address not registered as a server

	//
	// Try to notify all sessions currently using this server
	//
	for (TUint i = 0; i < KDndNumRequests; ++i)
		{
		TDndReqData &rq = iDndReqData[i];
		// For now, assume the slot is in used, if callback has been installed
		if (rq.iCallback == NULL)
			continue;
		if (rq.iCurrentServer == server_id)
			rq.SendResponse(KErrDndServerUnusable);
		}
	}



// TDndReqData::Close
// ******************
void TDndReqData::Close()
	{
	CancelQuery();
	iCallback = NULL;
	if (iRecord)
		{
		iRecord->Unlock(this);
		iRecord = NULL;
		}
	iOwner->iServerManager.CloseList(iFilter);
	// If this was the last active session, then
	// cancel all requests 
	ASSERT(iOwner->iActivityCount > 0);
	if (--iOwner->iActivityCount == 0)
		iOwner->DeactivateSocket();
	}

// TDndReqData::NewQuery
// ***********************
TInt TDndReqData::NewQuery(const TDnsMessage &aQuery, TDnsServerScope aServerScope, TUint32 aFlags)
	{
	iIsReqPending = FALSE;
	iIsNewQuery = TRUE;
	iIsUsingTCP = 0;
	iQdCount = 1;
	iFlags = aFlags;
	iFilter.iServerScope = aServerScope;
	iFilter.iServerId = 0;
	iNetworkId = aQuery.iId;  // Get the networkId information from the Query.

#ifdef SYMBIAN_DNS_PUNYCODE
	if( (aQuery.iScope & 0x80) == 0x80 )
		{
		iIdnEnabled = 1;
		iQuestion.EnableIdn(ETrue);
		}
	else 
		{
		iIdnEnabled = 0;
		iQuestion.EnableIdn(EFalse);
		}
#endif //SYMBIAN_DNS_PUNYCODE

	switch (aQuery.iType)
		{
		case KDnsRequestType_GetByName:
		case KDnsRequestType_GetByAddress:
			{
			const TNameRecord &query = aQuery.NameRecord();

			if (aQuery.iType == KDnsRequestType_GetByName)
				{
				iFilter.iLockId = aQuery.iId;
				iFilter.iLockType = KIp6AddrScopeNetwork;
#ifdef SYMBIAN_DNS_PUNYCODE
				TInt err = iQuestion.SetName(query.iName);
				if( err != KErrNone)
					{
					return err;
					}
#else
				iQuestion.SetName(query.iName);
#endif // SYMBIAN_DNS_PUNYCODE
				}
			else
				{
				iOwner->iServerManager.LockByAddress(TInetAddr::Cast(query.iAddr), aQuery.iId, iFilter);
#ifdef SYMBIAN_DNS_PUNYCODE
				TInt err = iQuestion.SetName(TInetAddr::Cast(query.iAddr));
				if( err != KErrNone)
					{
					return err;
					}
#else
				iQuestion.SetName(TInetAddr::Cast(query.iAddr));
#endif // SYMBIAN_DNS_PUNYCODE
				}
			}
			break;
		case KDnsRequestType_TDnsQuery:
			{
			const TDnsQuery &query = aQuery.Query();
			iQuestion.Copy(query.Data());
			iFilter.iLockId = aQuery.iId;
			iFilter.iLockType = KIp6AddrScopeNetwork;
			}
			break;
		default:
			return KErrNotSupported;
		}
	iCurrentServer = 0;
	//
	// For forward query, the Scope identifier of the aQuery.iAddr is the network id
	// For pointer query, if the address is not of network scope, then need to find
	// the network id based on the scope id... (fix later). -- msa

	iOpcode = EDnsOpcode_QUERY;		// Only EStandard supported! (EInverse is not same as PTR query!!!)
	if (iRecord)
		{
		iRecord->Unlock(this);
		iRecord = NULL;
		}
	return KErrNone;
	}


// TDndReqData::CancelQuery
// **************************
void TDndReqData::CancelQuery()
	{
	iIsReqPending = FALSE;
	Cancel();
	iOwner->iServerManager.CloseList(iFilter);
	}


// TDndReqData::DoNext
// *********************
/**
// @retval aReply	returns the requested Resource Record value, if KErrNone return
// @param aNext		the index of the value to be returned. 0 is the index of the first value
// @returns
//	@li	KErrNotFound, if there is no value at specified index
//	@li	KErrDndCache, if the reply from DNS stored in cache was corrupt
//	@li	KErrNone, if value successfully returned
*/
TInt TDndReqData::DoNext(TDnsMessageBuf &aReply, TInt aNext) const
	{
	if (iRecord == NULL)
		return KErrNotFound;
	TInt ret = iRecord->ErrorCode();
	if (ret < 0)
		return ret;	// No usable record present

	TDndRR tempRR(iRecord->Reply());
#ifdef SYMBIAN_DNS_PUNYCODE
		tempRR.iIdnEnabled = TBool(iIdnEnabled);
#endif //SYMBIAN_DNS_PUNYCODE

	const TInet6HeaderDNS &hdr = iRecord->Header();
	TInt answerCount = hdr.ANCOUNT();

	ret = tempRR.FindRR(iRecord->AnswerOffset(), answerCount, iQuestion.QType(), iQuestion.QClass(), aNext);
	if (ret < 0)
		{
		if (ret == KErrDndCache)
			iRecord->Invalidate();
		return ret;
		}

	TInetAddr *addr = NULL;

	switch (aReply().iType)
		{
		case KDnsRequestType_GetByName:
		case KDnsRequestType_GetByAddress:
			{
			TNameRecord &reply = aReply().NameRecord();
			aReply.SetLength(aReply().HeaderSize() + sizeof(reply));
			if (hdr.AA())
				reply.iFlags |= EDnsAuthoritive;
			ret = GetResponse(tempRR, reply);
			reply.iFlags |= EDnsServer | (iIsFromCache ? EDnsCache : 0);
			addr = &TInetAddr::Cast(reply.iAddr);
			break;
			}
#ifndef NO_DNS_QUERY_SUPPORT
		case KDnsRequestType_TDnsQuery:
			ret = tempRR.GetResponse(aReply, &addr);
			break;
#endif
		default:
			return KErrNotSupported;
		}
	if (ret < 0)
		return ret;		// Some error detected!

	if (addr)
		{
		// Reply contains a TInetAddr. This needs some special processing

		// Supplement IPv6 addresses with the scope value
		// (Should do the same with IPv4 after converting to IPv4 mapped?)
		if (addr->Family() == KAfInet)
			addr->ConvertToV4Mapped();
		if (addr->Family() == KAfInet6)
			{
			const TInt scope_level = addr->Ip6Address().Scope();

			// Add the scope id only for addresses with larger than node
			// local scope (this leaves the scope id as zero, if a loopback
			// address is returned from the name server (The id of the node
			// local scope is the interface index and non-zero value would
			// bind loopback destination to real interface instead of internal
			// loopback interface).
			if (scope_level > KIp6AddrScopeNodeLocal)
				addr->SetScope(iOwner->iServerManager.Scope(iRecord->Server(), *addr));

#ifdef LLMNR_ENABLED
			if(iOwner->iControl.GetConfig().iLlmnrLlOnly) // accept only linklocal replies to LLMNR queries
				if (iFilter.iServerScope == EDnsServerScope_MC_LOCAL)
					{
					// Check compliance w. link-local addressing requirements (ipv6/ipv4)
					if (scope_level != KIp6AddrScopeLinkLocal)
						return KErrNotFound;
					}
#endif

			//
			// A backward compatibility hack: if address is IPv4 global address
			// and the network scope in the query matches the scope of the address,
			// then convert the returned address into old KAfInet format (and lose
			// lose the scope id).
			if (addr->IsV4Mapped() &&
				addr->Scope() == aReply().iId &&
				scope_level == KIp6AddrScopeNetwork)
				addr->SetAddress(addr->Address());
			}
		}
	return KErrNone;
	}

// TDndReqData::DoError
// **********************
/**
// If a session has a DNS reply in cache associated with it, then
// set the state of this reply to indicated error code.
//
// @param aError	the error code to be stored
// @param aTLL		the new "Time To Live" for the record (in seconds). The
//					record (and error state) will expire after this time.
*/
void TDndReqData::DoError(TInt aError, TUint aTTL)
	{
	if (aError >= 0)
		return;		// Only errors can be set!

	if (iRecord == NULL)
		return;
	iRecord->FillErrorCode(aError, aTTL);
	}

// TDndReqData::DoQueryL
// ***********************
/**
// Activate a query of specified type for the loaded query information
// (enable RecvReply callback). Note: the callback may occur already within call!
//
// @param	aRequestTime is the time when the request is received from the application
// @param	aQType	type of the query (all queries assume IN class)
// @returns
//	@li	< 0, serious error, (resolving process should be aborted)
//	@li	= 0, reply found from cache (reply callback has been called)
//	@li	= 1, DNS Query message has been queued for transmission
// @execption
//	LEAVE on any serious error (resolving process should be aborted)
*/
TInt TDndReqData::DoQueryL(const TTime &aRequestTime, const EDnsQType aQType)
	{
	// -- class is now always IN,
	// -- should only look from cache if aQType is not a "wildcard" type
	//    (however, if wildcard, cache should not give hits...)
	iQuestion.SetQType(aQType);
	// -- only IN class queries are supported
	iQuestion.SetQClass(EDnsQClass_IN);

	if (iRecord)
		{
		iRecord->Unlock(this);
		iRecord = NULL;
		}

	if (iCurrentServer == 0)
		{
		if (!PickDefaultServer())
			{
			SendResponse(KErrDndServerUnusable);
			return 0;
			}
		if (iCurrentServer == 0)
			{
			// Cannot check cache nor start any query, if the
			// name space id cannot be determined (no interfaces
			// up). Return "query queued" anyway, and let the
			// resolver retry process try it again later.
			iIsReqPending = TRUE;
			return 1;
			}
		}
	TUint32 id = iOwner->iServerManager.NameSpace(iFilter, iCurrentServer);

	if (id == 0)
		{
		SendResponse(KErrDndServerUnusable);
		return 0;
		}
				
	// Check in the cache first. If the record does not exist
	// in the cache, it will be created now (empty with KErrNotFound).
	iRecord = iOwner->iCache->FindL(
			id,
			iQuestion,
			iQuestion.QType(),
			// *HACK WARNING* To achieve independent caching of answers which
			// are result of queries where RD=1 or RD=0 (recursion desired),
			// the Class value is made different depending on the RD state.
			(EDnsQClass)(iQuestion.QClass() | ((iFlags & KDnsModifier_RD) ? 0 : 0x80)),
			aRequestTime);
	// The above FindL must either leave of return a valid
	// record pointer. Just as a safety measure, if record
	// is not returned, then panic on in DEBUG builds, and
	// in release, leave with KErrDndNoRecord
	// (** however, this should never happen! **)
	ASSERT(iRecord != NULL);
	if (iRecord == NULL)
		User::Leave(KErrDndNoRecord);

	// Prevent record from being deleted while the
	// iRecord pointer exists...
	iRecord->Lock();
	switch(iRecord->ErrorCode())
		{
		// If no error in the record, retrieve the informations and send
		case KErrNone:	
		// For certain errors, send the error code
		case KErrDndBadName:
		case KErrDndNotImplemented:
		case KErrDndRefused:
		case KErrDndNoRecord:
			iIsFromCache = TRUE;
			iIsNewQuery = FALSE;
			if (IsQueued())
				Cancel();
			SendResponse(iRecord->ErrorCode());
			return 0;
		
		// For other errors, try sending the query to the DNS
		default:
			break;
		}

	// Automatic restart of DNS socket, if closed for some reason
	iOwner->ActivateSocketL(iNetworkId);  // pass on the networkId information

	iIsReqPending = TRUE;
	// If the query is probe, do not AssignWork but continue..
	if (!(iFlags & KDnsModifier_PQ) && !iRecord->AssignWork(this))
		return 1;	// just let the other worker do the job.

	// Try to detect retransmissions of the same query and
	// reuse old ID in such case... (don't generate a new)
	if (iIsNewQuery ||
		iQuestion.QType() != aQType ||
		iQuestion.QClass() != EDnsQClass_IN)
		Cancel();				// A new ID required

	iIsNewQuery = FALSE;
	iIsFromCache = FALSE;

	// If the DNS "mode" is Multicast DNS, then assume that PTR queries
	// which are generated from IP address are to be made via TCP. Test
	// this and use TCP if query is PTR query for valid IP address.
	// [specified in draft-ietf-dnsext-mdns-22.txt, but generalized here
	// also for any future Multicast DNS]
	for ( ;iFilter.IsMulticast() && iQuestion.QType() == EDnsQType_PTR;)
		{
		// Use "for" just for easy "break" exits!
		TInetAddr server;

		// Get correct DNS port number into 'server' (actual address is thrown away)
		if (iOwner->iServerManager.Address(iCurrentServer, server) != KErrNone)
			break;
		// Use server address type as a flag, whether Ipv4 or IPv6 is done
		const TInt is_ipv4 = server.IsV4Mapped();

		if (!iQuestion.GetAddress(server) || !server.IsUnicast())
			break;
		// Usually LLMNR has both IPv4 and IPv6 multicast addresses as "servers".
		// There is no point in doing PTR query for the same address twice, thus
		// only do it once per matching "server" address (thus, if there is no
		// IPv4 multicast "server", no IPv4 reverse queries are done either).
		if (is_ipv4 != server.IsV4Mapped())
			{
			SendResponse(KErrDndServerUnusable);
			return 0;
			}

		// Supplement 'server' address with a scope id based on current server
		server.SetScope(iOwner->iServerManager.Scope(iCurrentServer, server));

		// For link local multicast, use TTL = 1 (otherwise, system default is used)
		const TInt ttl = iFilter.iServerScope == EDnsServerScope_MC_LOCAL ? 1 : -1;
		if (iOwner->Queue(*this, server, -1, ttl) != KErrNone)
			break;
		iIsUsingTCP = 1;
		SendResponse(KDnsNotify_USING_TCP);
		return 1;
		}
	iOwner->ReSend(*this);		// (uses old ID, if request queued already)
	return 1;
	}

// TDndReqData::UpdateCacheData
// ******************************
/**
// @param aQuery	the session from which the reply is updated
// @param aMsg		the reply from the DNS server
// @param aAnswerOffset the start offset to the andwer secion in the reply
// @param aErr		the status code of the reply
// @returns
//	@li	TRUE, if cache record updated
//	@li	FALSE, if not updated (error code is "transient", concerns single query)
*/
TBool TDndReqData::UpdateCacheData(const TMsgBuf &aMsg, const TInt aAnswerOffset, const TInt aErr)
	{
	if (iRecord == NULL)
		return FALSE;

#ifdef _LOG
	THostName name;
	iQuestion.GetName(name);
	Log::Printf(_L("\t\tDNS session [%u] -- Update cache: %S (offset=%d) aErr=%d"), (TInt)this, &name, aAnswerOffset, aErr);
#endif
	if (aErr == KErrDndDiscard)
		return FALSE;

	// If there is already have valid answer cached, decide whether the new
	// answer is better and should replace the old?
	const TInet6HeaderDNS &new_hdr = aMsg.Header();
	if (iRecord->ErrorCode() == KErrNone && !new_hdr.AA())
		{
		// If new header is not authoritative, then it will replace the value
		// only if old is non-authoritative and there is no error.
		const TInet6HeaderDNS &old_hdr = iRecord->Header();
		if (old_hdr.AA() || aErr != KErrNone)
			return FALSE; // Not updated, keep previous valid value in cache.
		}

	TBool updated = FALSE;
	if (aErr == KErrNone || aErr == KErrDndBadName || aErr == KErrDndNotImplemented || aErr == KErrDndNoRecord)
		{
		// Try to locate the SOA record from the authority section and
		// use the minttl as the ttl of the negative caching.
		TInt ttl;
		TDndRR soa(aMsg);
#ifdef SYMBIAN_DNS_PUNYCODE
		soa.iIdnEnabled = (TBool) iIdnEnabled;
#endif //SYMBIAN_DNS_PUNYCODE
		TInt off = soa.LocateRR(aAnswerOffset, new_hdr.ANCOUNT()+new_hdr.NSCOUNT(), EDnsQType_SOA, EDnsQClass_IN, new_hdr.ANCOUNT());
		if (off < 0)
			{
			ttl = iOwner->iControl.GetConfig().iMaxTime;
			LOG(Log::Printf(_L("\t\tDNS session [%u] -- Update cache: no SOA, default ttl = %d"), (TInt)this, ttl));
			}
		else if ((off = aMsg.SkipName(soa.iRd)) > 0 &&
				 (off = aMsg.SkipName(off)) > 0 &&
				 off >= (TInt)soa.iRd &&
				 off + 20 <= (TInt)(soa.iRd + soa.iRdLength))
			{
			ttl = BigEndian::Get32(aMsg.Ptr()+off+16);
			LOG(Log::Printf(_L("\t\tDNS session [%u] -- Update cache: SOA minttl = %d"), (TInt)this, ttl));
			}
		else
			{
			ttl = 0;	// The reply is broken in some way, use ttl = 0
			LOG(Log::Printf(_L("\t\tDNS session [%u] -- Update cache: SOA access failed"), (TInt)this));
			}
		iRecord->FillData(aMsg, aAnswerOffset, iCurrentServer, aErr, ttl);
#ifdef SYMBIAN_DNS_PUNYCODE
		
		LOG(iRecord->Print(iOwner->iControl,(TBool)iIdnEnabled));
#else
		LOG(iRecord->Print(iOwner->iControl));
#endif //SYMBIAN_DNS_PUNYCODE
		updated = TRUE;
		}
	//
	// Got some answer from a server, update the "good" server (unless error indicates "bad"
	//
	if (iFilter.IsUnicast() && aErr != KErrDndRefused && aErr != KErrDndServerUnusable)
		{
		TInetAddr addr;
		if (iOwner->iServerManager.Address(iCurrentServer, addr) == KErrNone)
			{
#ifdef _LOG
			// borrow the 'name' from earlier LOG section!
			addr.OutputWithScope(name);
			Log::Printf(_L("\t\tDNS session [%u] -- Update cache: preferred server = %S port=%d ns=%d"),
				(TInt)this, &name, addr.Port(), iOwner->iServerManager.NameSpace(iFilter, iCurrentServer));
#endif
			iOwner->iCache->SetServerAddress(iOwner->iServerManager.NameSpace(iFilter, iCurrentServer), addr, KErrNone);
			}
		}

	return updated;
	}


void CDndDnsclient::QueryBegin()
	{
	++iActivityCount;
	}


void CDndDnsclient::QueryEnd()
	{
	ASSERT(iActivityCount > 0);
	if (--iActivityCount == 0)
		DeactivateSocket();
	}


//
// TDndReqData
//

// TDndReqData::TranslateRCODE
// ***************************
/**
//
// @param	aHdr	The fixed DNS reply header
//
// @returns
//	@li	KErrNone, if reply is ok
//	@li	KErrDndDiscard/KErrDndUnknown
//		if reply does not match the query, has errors
//		in the format or RCODE was unknown
//	@li	KErrDndFormat, RCODE was EDnsRcode_FORMAT_ERROR
//	@li	KErrDndServerFailure, RCODE was EDnsRcode_SERVER_FAILURE
//	@li	KErrDndBadName, RCODE was EDnsRcode_NAME_ERROR
//	@li	KErrDndNotImplemented, RCODE was EDnsRcode_NOT_IMPLEMENTED
//	@li	KErrDndRefuced, RCODE was EDnsRcode_REFUSED
//	@li	KErrDndServerUnusable, if empty reply is not authoritative
*/
TInt TDndReqData::TranslateRCODE(const TDndHeader &aHdr, TInt aRCode) const
	{
	switch (aRCode) 
		{
		case EDnsRcode_NOERROR:
			break;
		case EDnsRcode_FORMAT_ERROR:
			return KErrDndFormat;
		case EDnsRcode_SERVER_FAILURE:
			return KErrDndServerFailure;
		case EDnsRcode_NAME_ERROR:
			return KErrDndBadName;
		case EDnsRcode_NOT_IMPLEMENTED:
			return KErrDndNotImplemented;
		case EDnsRcode_REFUSED:
			return KErrDndRefused;
		default:
			return KErrDndUnknown;
		}	

	if (iOpcode == EDnsOpcode_QUERY && aHdr.QDCOUNT() != iQdCount)
		return KErrDndDiscard;
	//
	// A special heuristics: discard empty replies, if the selected server does
	// not do recursion as requested, and if it is not authority on the queried
	// name. => return a special "server unusable for this query" error
	if (aHdr.ANCOUNT() == 0 && (iFlags & KDnsModifier_RD) != 0 && !aHdr.RA() && !aHdr.AA())
		return KErrDndServerUnusable;
	return KErrNone;
	}

// TDndReqData::CheckQuestion
// **************************
/**
// @param	aOffset starting offset of the question section in the message
// @param	aMsg the reply message from a DNS server
//
// @returns
//	@li	> 0,
//		if message checks ok, the value is the new offset pointing
//		to the next section after the question.
//	@li	= 0, reply does not match the question.
//	@li < 0, bad DNS reply
*/
TInt TDndReqData::CheckQuestion(const TMsgBuf &aMsg, TInt &aRCode) const
	{
	if (!iIsReqPending)
		return 0;	// Not for me.

	TDndQuestion question;
	const TInt offset = aMsg.VerifyMessage(aRCode, question);
	if (offset < 0)
		return offset;	// Invalid message format, just ignore.
	const TDndHeader &hdr = aMsg.Header();

	// Only ONE question supported, sematics of receiving a reply
	// with more than one Question are hairy... [which answers
	// relate to which question?] (and multiple questions are
	// not supported by current servers anyway -- msa)
	if (hdr.QDCOUNT() != 1)
		return KErrDndUnknown;

	// This is supposed to be a REPLY, if not, then "no match".
	if (!hdr.QR())
		return 0;
	// Reply OPCODE match the query?
	if (hdr.OPCODE() != iOpcode)
		return KErrDndDiscard;

	// Does the question match the query?
	//
	if (question.CheckQuestion(iQuestion) != KErrNone)
		return 0;
	return offset;
	}


// TDndReqData::GetResponse
// ************************
/**
// Map the contents of single resource record into TNameRecord.
//
// @param	aRR	the resource from which the reply is extracted
// @retval	aAnswer receives the extracted value
// @returns
//	@li	KErrNone, if extraction successful
//	@li	KErrDndNameTooBig, if answer cannot be fit into aAnswer
//	@li and other errors
*/
TInt TDndReqData::GetResponse(const TDndRR &aRR, TNameRecord &aAnswer) const
	{
	TInt err = aRR.GetResponse(aAnswer.iName, TInetAddr::Cast(aAnswer.iAddr));
	if (err != KErrNone)
		return err;
	aAnswer.iFlags |= (aRR.iType == EDnsType_CNAME) ? (EDnsAlias | EDnsServer) : EDnsServer;
	return KErrNone;
	}


// TDndReqData::SendResponce
// *************************
/**
// @param	aErr is the status,
//	@li	= 0, the request has completed successfully
//	@li > 0, request being processed, just a progress noticification
//	@li	< 0, the request has completed with an error
*/
void TDndReqData::SendResponse(TInt aErr)
	{
	if (aErr <= 0)
		{
		iIsReqPending = FALSE;	// Current request completed
		Cancel();
		}
	if (iCallback)
		iCallback->ReplyCallback(aErr);
	}

// TDndReqData::Build
// ******************
/**
// @retval	aMsg
//		contains the fully constructed message to be sent to the DNS server,
//		if Build succeeds
// @retval	aServer
//		contains the server address for which the message should be sent
// @param	aMaxMessage
//		the size of the current receive buffer (if UDP, zero for TCP)
//
// @returns TRUE, successful Build, and error (< 0) otherwise
*/
TBool TDndReqData::Build(CDnsSocket &aSource, TMsgBuf &aMsg, TInetAddr &aServer, TInt aMaxMessage)
	{
	CDndDnsclient &dns = (CDndDnsclient &)aSource;

	if (dns.iServerManager.Address(iCurrentServer, aServer) != KErrNone)
		return 0;
	ASSERT(aServer.Port() != 0);

	aMsg.SetLength(sizeof(TDndHeader));
	TDndHeader &hdr = (TDndHeader &)aMsg.Header();
	if (aServer.IsMulticast())
		{
		ASSERT(iFilter.IsMulticast());
		hdr.SetRD(0);
		if (aServer.IsV4Mapped())
			{
			if(iQuestion.QType() == EDnsQType_AAAA)
				{
				SendResponse(KErrDndServerUnusable);
				return 0;
				}
			}
		else
			{

			if(iQuestion.QType() == EDnsQType_A)
				{
				SendResponse(KErrDndServerUnusable);
				return 0;
				}
			}
#ifdef LLMNR_ENABLED
		dns.SetHoplimit(dns.iControl.GetConfig().iLlmnrHoplimit);
#endif
		}
	else
		{
#ifdef LLMNR_ENABLED
		dns.SetHoplimit(-1);
#endif
		hdr.SetRD(iFlags & KDnsModifier_RD);
		}
	hdr.SetQdCount(1);
	if (iQuestion.Append(aMsg) < 0)
		return 0;

	// Assume EDNS0 enabled, if the current receive buffer
	// is larger than KDnsMaxMessage (all smaller
	// values are treated as "no EDNS0".
	if (aMaxMessage > KDnsMaxMessage)
		{
		TDndRROut opt(aMsg);
#ifdef SYMBIAN_DNS_PUNYCODE
		opt.iIdnEnabled = (TBool) iIdnEnabled;
#endif //SYMBIAN_DNS_PUNYCODE
		opt.iType = (TUint16)EDnsQType_OPT;
		opt.iClass = (TUint16)aMaxMessage;
		opt.iTTL = 0;	// RCODE = 0, version = 0, Z = 0
		if (opt.Append(KNullDesC8, 0) == KErrNone)
			{
			hdr.SetArCount(1);
			opt.AppendRData();
			}
		}
	return 1;
	}


void TDndReqData::Sent(CDnsSocket &aSource)
	{
	CDndDnsclient &dns = (CDndDnsclient &)aSource;

	dns.iServerManager.CloseList(iFilter);

	SendResponse(KDnsNotify_QUERY_SENT);
	}


TBool TDndReqData::Reply(CDnsSocket &aSource, const TMsgBuf &aBuf, const TInetAddr &aServer)
	{
	TInt rcode = 0;
	const TInt offset = CheckQuestion(aBuf, rcode);
	if (offset < 0)
		return 1;	// Invalid message format, just ignore.
	const TDndHeader &hdr = aBuf.Header();
	TInt err = TranslateRCODE(hdr, rcode);

	CDndDnsclient &dns = (CDndDnsclient &)aSource;
	ASSERT(&dns == iOwner);
	// If configuration requests that "Not found" replies from a server
	// are not to be cached, but ignored, then substitue err with
	// KErrDndServerUnusable (meaning that this server is not usable for
	// this query) This error is not cached!
	//
	if (err == KErrDndBadName && dns.iControl.GetConfig().iSkipNotFound)
		err = KErrDndServerUnusable;
	else if (iIsUsingTCP == 0 && hdr.TC() != 0)
		{
		// The current query was not TCP and got truncated reply,
		// restart query with TCP (if we get truncated reply with
		// TCP, something is broken...)
		//

		// It is assumed that the aServer has the correct port
		// already set (it should be the remote port of the original
		// UDP query)
		ASSERT(aServer.IsUnicast());	// Should always be true.
		if (aServer.IsUnicast())
			{
			if (iOwner->Queue(*this, aServer, hdr.ID()) == KErrNone)
				{
				iIsUsingTCP = 1;
				SendResponse(KDnsNotify_USING_TCP);
				return 1;
				}
			}
		// If cannot use the TCP, then just use the trunctated
		// answer as is...
		}
	//
	// UpdateCacheData updates data in cache only, if err is KErrNone, or
	// updates error status for some specific err codes,
	// otherwise, it does nothing.
	TBool updated = UpdateCacheData(aBuf, offset, err);

#ifdef DEBUG_CACHE
	iCache->Dump(*iControl);
#endif

	if (updated)
		{
		// Cache modified! In addition to the original query,
		// send the responce to every request that is waiting for
		// the same reply... (iRecord pointers are same).
		for (TUint i = 0; i < KDndNumRequests; ++i)
			{
			TDndReqData &rq = dns.iDndReqData[i];
			if (rq.iIsReqPending && iRecord == rq.iRecord)
				{
				rq.Cancel();	// No need to send the query
				rq.SendResponse(err);
				}
			}
		return 1;
		}

	// Cache not updated, send to original query only.
	SendResponse(err);
	return 1;
	}

void TDndReqData::Abort(CDnsSocket &/*aSource*/, const TInt aReason)
	{
	SendResponse(aReason);
	}