networkingutils/nameresolverutility/src/engine.cpp
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:30:50 +0100
branchRCL_3
changeset 20 493058e57c8c
parent 0 9736f095102e
permissions -rw-r--r--
Revert incorrect RCL_3 drop: 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:
// engine.cpp - nslookup client engine
//

#include <e32math.h>
#include <e32std.h>
#include <eikenv.h>
#include <plpsess.h>	//Used for RRemoteLink
#include <networking/dnd_err.h>
#include <dns_ext.h>
#include <dns_qry.h>
#include "engine.h"
#include <nslookup.rsg>
#include "nslookup.h"

CNslookup::CNslookup(): CActive(EPriorityStandard)
	{
	CActiveScheduler::Add(this);	//Adds itself to the scheduler only the first time
	}

//Sets the remote link to off
void CNslookup::DisableRemoteLink()
	{
	RRemoteLink link;
	TRemoteLinkStatus state;

	TInt err=link.Open();
	if (err==KErrNone)
		{
		if (link.Status(state)!=KErrNone)
			return;
		if (state.iStatus!=TRemoteLinkStatus::EDisabled)
			{
			iConsole->WriteLine(_L("Disabling Remote link\n"));
			link.Disable(); 
			}
		link.Close();
		}
	}

//Sets all default values. Actually it's no more a L function
void CNslookup::ConstructL(const TPreferences& aPref)
	{

	// Base class second-phase construction.

	iHostname=aPref.iHostname;
	}

//return the current preferences
void CNslookup::GetPreferences(TPreferences &aPref) const
	{	
	aPref.iHostname=iHostname;		//Address to Ping
	}

void CNslookup::DefaultPreferences(TPreferences &aPref)
	{	
	aPref.iHostname=_L("127.0.0.1");
	}


const TDesC* CNslookup::GetHostName() const
	{
	return &iHostname;
	}

void CNslookup::SetHostName(const TDes& aHostname)
	{
	iHostname = aHostname;
	}

void CNslookup::SetConsole(CNslookupContainer* aConsole)
	{
	iConsole = aConsole;
	}

CNslookup::~CNslookup()
	{
	Cancel();
	}

//Shows the error and set the application as not running. 
//Requires a return after calling it!

void CNslookup::Error(const TDesC& string,TInt error)
	{
	TBuf<150> aux;
	TBuf<100> errtxt;

	CEikonEnv::Static()->GetErrorText( errtxt,error);
	aux.Append(string);	
	aux.Append(_L(": "));
	aux.Append(errtxt);
	aux.AppendFormat(_L(" (%d)\n"), error);
	iConsole->WriteLine(aux);
	}


void CNslookup::Stop()
	{
	iHostResolv.Close();
	iSockServ.Close();
	CEikonEnv::Static()->BusyMsgCancel();
	}


void CNslookup::BeginL()
	{
	TInt err=0;

	if (IsRunning())	// There's another instance running
		return;

	//INITIALIZATION

	DisableRemoteLink();

	iConsole->WriteHostL(iHostname);
		
	//connecting the Socket Server
	err = iSockServ.Connect();	//KESockDefaultMessageSlots
	if (err!=KErrNone)
		{
		Error(_L("Socket Server Error (Connect)"),err);
		return;
		}

	err = iHostResolv.Open(iSockServ, KAfInet, KProtocolInetUdp);	// Address Resolver 
	if (err != KErrNone)
		{
		Error(_L("Resolver Error (Open)"),err);
		iSockServ.Close();
		return;
		}
	iConsole->WriteLine(_L("\nResolving...\n"));	
	CEikonEnv::Static()->BusyMsgL(R_RESOLVING_NAME);

	TUint16 querytype = KDnsRRTypeInvalid;

	// This lengthy code checks what the user selected as query type in UI dialog
	switch (iQueryType)
		{
	case 0:		// Default (GetName)
		break;
	
	case 1:		// Query A
		querytype = KDnsRRTypeA;
		break;
		
	case 2:		// Query NS
		querytype = KDnsRRTypeNS;
		break;
	
	case 3:		// Query CNAME
		querytype = KDnsRRTypeCNAME;
		break;
	
	case 4:		// Query WKS
		querytype = KDnsRRTypeWKS;
		break;
	
	case 5:		// Query PTR
		querytype = KDnsRRTypePTR;
		break;
	
	case 6:		// Query HINFO
		querytype = KDnsRRTypeHINFO;
		break;
	
	case 7:		// Query MX
		querytype = KDnsRRTypeMX;
		break;
	
	case 8:		// Query TXT
		querytype = KDnsRRTypeTXT;
		break;
	
	case 9:		// Query AAAA
		querytype = KDnsRRTypeAAAA;
		break;
		
	case 10:	// Query SRV
		querytype = KDnsRRTypeSRV;
		break;
	
	case 11:	// Query NAPTR
		querytype = KDnsRRTypeNAPTR;
		break;
	
	case 12:	// Query Any
		querytype = KDnsQTypeANY;
		break;

#ifdef DND_DCM_EXTENSION				
	case 13:	// Cache Clear
		querytype = KDnsQTypeCacheClear;
		break;
#endif

	default:
		break;
		}
		
	// If query type was something else than default using GetName(), use the new
	// Query() interface instead
	if (querytype != KDnsRRTypeInvalid)
		{
#ifdef __RHOSTRESOLV_QUERY_IF
		TBuf8<KHostNameLimit> name8;
		name8.Copy(iHostname);
		TDnsQuery query(name8, querytype);
		TPckgC<TDnsQuery> querybuf(query);	

		// Hmm... Esock seems to use the current length when allocating a buffer for result
		// output. It should use MaxLength() instead, and now we have to do this to get
		// over with it
		iResult.SetLength(iResult.MaxLength());
		iHostResolv.Query(querybuf, iResult, iStatus);
#else
		TBuf<80> aux;
		aux.Copy(_L("Query() interface not supported\n"));
		iConsole->WriteLine(aux);
		iQueryType = 0;
		Stop();
		return;
#endif
		}
	else
		{
		if (iAddress.Input(iHostname) == KErrNone)
			{
			iHostResolv.GetByAddress(iAddress, iEntry, iStatus);
			}
		else
			{
			iHostResolv.GetByName(iHostname, iEntry, iStatus);
			}
		}

	iCount = 0;
	SetActive();	//Sets the object as Active.
	}

static TPtrC ErrorTextL(const TInt aCode)
	{
	switch (aCode)
		{
		case KErrNotFound:			return _L("Name not found");

//		case KErrDndTimedOut:		return _L("timed out");
//		case KErrDndNoHost:			return _L("no host");	// [dubious error code -- eliminate? ]
//		case KErrDndNoMemorySend:	return _L("Out of memory on send");
//		case KErrDndNotSent:		return _L("Query not sent");
		case KErrDndCache:			return _L("Cache error");

		case KErrDndFormat:			return _L("Bad DNS reply format");
		case KErrDndServerFailure:	return _L("DNS server failed");
		case KErrDndBadName:		return _L("Name does not exist in DNS");
		case KErrDndNotImplemented:	return _L("Query not implemented by DNS server");
		case KErrDndRefused:		return _L("DNS Server refused query");

//		case KErrDndNoMemoryProc:	return _L("Insufficient memory - can not process the response");
		case KErrDndNoRecord:		return _L("No record found of the desired type and class"); 
		case KErrDndNameTooBig:		return _L("Buffer overflow");
		case KErrDndUnknown:		return _L("Error in DND");
		case KErrDndServerUnusable:	return _L("No answer available from current server");
		default:
			break;

		}
	User::Leave(aCode);
	// NOTREACHED
	return _L("");  // to make compiler happy
	}

void CNslookup::RunL()
	{
	TInt src_addr = 0;

	TBuf<100> textIPaddress;	//text address to be displayed
	TBuf<356> aux;

	if (iStatus != KErrNone)
		{
		if (iCount == 0)
			{
			// An error message is only shown, if the primary query
			// failes, and not for subsequent Next() operations.
			TBuf<100> msg(iHostname);

			TRAPD(err,
				TPtrC errText = ErrorTextL(iStatus.Int());
				msg.AppendFormat(_L(" - %S\n"), &errText));
			if (err == KErrNone)
				iConsole->WriteLine(msg);
			else
				Error(msg, iStatus.Int());
			}
		Stop();
		return;
		}	

	// Check if we are using the query interface instead of GetName()
	if (iQueryType != KDnsRRTypeInvalid)
		{
#ifdef __RHOSTRESOLV_QUERY_IF
		QueryResponse();
		
		// See explanation of this SetLength() hack above
		iResult.SetLength(iResult.MaxLength());
		iHostResolv.QueryGetNext(iResult, iStatus);
		iCount++;
		SetActive();
#endif		
		return;
		}

	aux.Append(iEntry().iName);   // maybe the main name is not the entered
	switch (iEntry().iAddr.Family())
		{
	case KAfInet:
	case KAfInet6:
		aux.Append(_L(" is "));
		iHostAddr = TInetAddr::Cast(iEntry().iAddr);	//host address
		iHostAddr.Output(textIPaddress);
		aux.Append(textIPaddress);
		src_addr = iShowSource && !iHostAddr.IsUnspecified();
		break;
	case KAfDns:
		{
		SDnsRR &rr = TInetDnsRR::Cast(iEntry().iAddr).RR();
		if (rr.iClass == 1 /* IN */)
			{
			if (rr.iType == 2 /* NS */)
				{
				aux.Append(_L(" NS"));
				break;
				}
			else if (rr.iType == 6 /* SOA */)
				{
				aux.AppendFormat(_L(" SOA serial=%u refresh=%u retry=%u expire=%u min=%u"),
					(TUint)rr.iSOA.iSerial,
					(TUint)rr.iSOA.iRefresh,
					(TUint)rr.iSOA.iRetry,
					(TUint)rr.iSOA.iExpire,
					(TUint)rr.iSOA.iMinimum);
				break;
				}
			else if (rr.iType == 15 /* MX */)
				{
				aux.AppendFormat(_L(" MX preference=%d"), (TInt)rr.iMX.iPreference);
				break;
				}
			else if (rr.iType == 33 /* SRV */)
				{
				aux.AppendFormat(_L(" SRV port=%d priority=%d weight=%d"),
					(TInt)iEntry().iAddr.Port(), (TInt)rr.iSRV.iPriority, (TInt)rr.iSRV.iWeight);
				break;
				}
			else if (rr.iType == 35 /* NAPTR */)
				{
				TPtrC replacement = rr.iNAPTR.REPLACEMENT(iEntry().iName);
				TPtrC services = rr.iNAPTR.SERVICES(iEntry().iName);
				TPtrC regexp = rr.iNAPTR.REGEXP(iEntry().iName);

				aux.AppendFormat(_L(" NAPTR order=%d, preference=%d repl=%S services=%S regexp=%S"),
					(TInt)rr.iNAPTR.iOrder,
					(TInt)rr.iNAPTR.iPreference,
					&replacement,
					&services,
					&regexp);

				break;
				}
			}
		aux.AppendFormat(_L(" class=%d type=%d"), (TInt)rr.iClass, (TInt)rr.iType);
		}
		break;
	default:
		break;
		}

	const TUint flags = iEntry().iFlags;
	if (flags)
		{
		aux.Append(_L(" ( "));
		if (flags & EDnsAlias)
			aux.Append(_L("Alias "));
		if (flags & EDnsAuthoritive)
			aux.Append(_L("Authoritative "));
		if (flags & EDnsHostsFile)
			aux.Append(_L("Hostfile "));
		if (flags & EDnsServer)
			aux.Append(_L("DNS "));
		if (flags & EDnsHostName)
			aux.Append(_L("MyName "));
		if (flags & EDnsCache)
			aux.Append(_L("Cached "));
		aux.Append(')');
		}
	aux.Append(_L("\n"));
	iConsole->WriteLine(aux);
	iCount++;
	if (src_addr)
		{
		// Show a matching SRC address.
		RSocket socket;
		TPckgBuf<TSoInetIfQuery> opt;
		opt().iDstAddr = iHostAddr;
		if (socket.Open(iSockServ, KAfInet, KSockDatagram, KProtocolInetUdp) == KErrNone)
			{
			_LIT(KIsVIF, " (VIF)");
			_LIT(KIsIF, "");
			_LIT(KNoRoute, "(no route)");

			(void)socket.GetOpt(KSoInetIfQueryByDstAddr, KSolInetIfQuery, opt);
			socket.Close();
			opt().iSrcAddr.OutputWithScope(textIPaddress);
			aux.Format(_L("   src= %S @ %S%S\n"), &textIPaddress, &opt().iName,
				opt().iName.Length() == 0 ? &KNoRoute() : opt().iIsUp ? &KIsIF() : &KIsVIF());
			iConsole->WriteLine(aux);
			}
		else
			iConsole->WriteLine(_L("cannot find src, UDP socket open failed\n"));
		}
	//
	// Get next address
	//
	iHostResolv.Next(iEntry, iStatus);
	SetActive();
	}

void CNslookup::DoCancel()
	{
	// Called only from Cancel() if there is pending resolve going on... (IsActive())
	iHostResolv.Cancel();
	}


// Outputs some text about the Query() response to the console
void CNslookup::QueryResponse()
	{
	TBuf<256> aux;
	TBuf<128> addrbuf;

	if (iResult.Length() < (TInt)sizeof(TDnsQryRespBase))
		{
		aux.AppendFormat(_L("Malformed response (length: %d)\n"), iResult.Length());
		return;
		}

	TDnsQryRespBase *respbase = (TDnsQryRespBase *)iResult.Ptr(); //lint !e826 // area length checked above
	aux.Append(iHostname);
	aux.AppendFormat(_L(": cl: %d  ttl: %d  "),
			respbase->RRClass(), respbase->RRTtl());

	// ugh... I'll do this the hard way: temporary buffer for converting from 8-bit
	// DNS result descriptors to 16-bit descriptor and the append to output buffer.
	// A better solution will follow a bit later...
	switch(respbase->RRType())
		{
	case KDnsRRTypeA:
		{
		const TDnsRespA *const respA = (TDnsRespA *) respbase;
		respA->HostAddress().Output(addrbuf);
		aux.Append(_L("type: A  "));
		aux.Append(_L("addr: "));
		aux.Append(addrbuf);
		}
		break;
		
	case KDnsRRTypeAAAA:
		{
		const TDnsRespAAAA *const respAAAA = (TDnsRespAAAA *) respbase;
		respAAAA->HostAddress().Output(addrbuf);
		aux.Append(_L("type: AAAA  "));
		aux.Append(_L("addr: "));
		aux.Append(addrbuf);
		}
		break;

	case KDnsRRTypePTR:
		{
		const TDnsRespPTR *const respPTR = (TDnsRespPTR *) respbase;
		aux.Append(_L("type: PTR  "));
		aux.Append(_L("name: "));
		addrbuf.Copy(respPTR->HostName());
		aux.Append(addrbuf);
		}
		break;
		
	case KDnsRRTypeMX:
		{
		const TDnsRespMX *const respMX = (TDnsRespMX *) respbase;
		aux.Append(_L("type: MX  "));
		aux.AppendFormat(_L("pref: %d  "), respMX->Pref());
		aux.Append(_L("name: "));
		addrbuf.Copy(respMX->HostName());
		aux.Append(addrbuf);
		}
		break;
		
	case KDnsRRTypeSRV:
		{
		const TDnsRespSRV *const respSRV = (TDnsRespSRV *) respbase;
		aux.Append(_L("type: SRV  "));
		aux.AppendFormat(_L("prio: %d  wght: %d  port: %d  targ: "),
				respSRV->Priority(), respSRV->Weight(), respSRV->Port());
		addrbuf.Copy(respSRV->Target());
		aux.Append(addrbuf);
		}
		break;
		
	case KDnsRRTypeNAPTR:
		{
		const TDnsRespNAPTR *const respNAPTR = (TDnsRespNAPTR *) respbase;
		aux.Append(_L("type: NAPTR  "));
		aux.AppendFormat(_L("ordr: %d  pref: %d  flag: "));
		addrbuf.Copy(respNAPTR->Flags());
		aux.Append(addrbuf);
		aux.Append(_L("  serv: "));
		addrbuf.Copy(respNAPTR->Service());
		aux.Append(addrbuf);
		aux.Append(_L("  regx: "));
		addrbuf.Copy(respNAPTR->Regexp());
		aux.Append(addrbuf);
		aux.Append(_L("  repl: "));
		addrbuf.Copy(respNAPTR->Replacement());
		aux.Append(addrbuf);
		}
		break;
		
#ifdef DND_DCM_EXTENSION	
	case KDnsQTypeCacheClear:
		{
		aux.Append(_L("  OK"));
		break;
		}
#endif

	default:
		aux.AppendFormat(_L("Unknown response type: %d"), respbase->RRType());
		break;
		}
			
	aux.Append(_L("\n"));
	iConsole->WriteLine(aux);			
	}

// Stops NSLOOKUP

void CNslookup::EndNslookup()
	{	
	Cancel();
	Stop();
	}

// Just checks if sending packets from a previous ping

TBool CNslookup::IsRunning() const
	{
	return IsActive();
	}