--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/networkprotocols/tcpipv4v6prt/src/res.cpp Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,1696 @@
+// 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:
+// res.cpp - name resolver
+// This is an implementation of the name service, which is based
+// on a external resolver application, and translating the queries
+// into socket read/writes.
+//
+
+
+
+/**
+ @file res.cpp
+*/
+
+#define SYMBIAN_NETWORKING_UPS
+
+#include <in_sock.h>
+#include "res.h"
+#include <in_bind.h> // CProtocolBaseUnbind, MInterfaceManager
+#include <timeout.h>
+#include <in6_event.h>
+#include <in6_opt.h>
+#include "iface.h"
+#include "inet6log.h"
+#include "tcpip_ini.h"
+#include "networkinfo.h"
+#include "res_sock.h"
+#include "inet6err.h"
+#include <networking/dnd_err.h>
+
+# include <comms-infras/nifif.h> // Need for TSoIfConnectionInfo
+#include <es_prot_internal.h>
+
+#ifdef SYMBIAN_DNS_PUNYCODE
+#define ENABLEIDN_SCOPE 0x80
+#endif //SYMBIAN_DNS_PUNYCODE
+
+_LIT_SECURITY_POLICY_C1(KPolicyNetworkControl, ECapabilityNetworkControl);
+_LIT_SECURITY_POLICY_C1(KPolicyNetworkServices, ECapabilityNetworkServices);
+
+class TDnsRequest : public TDnsRequestBase
+ /**
+ * The internal representation of a pending query from application/SocketServer.
+ */
+ {
+public:
+ TDnsRequest();
+ TDnsRequest(const THostName &aName, TDes &aResponse, TInt aType);
+ TDnsRequest(TNameRecord &aQuery, TInt aNext, TInt aType);
+ TDnsRequest(const TDesC8 &aQuery, TDes8 &aResponse, TInt aNext);
+ void BuildMessage(TUint16 aId, TDes8 &aBuffer) const;
+
+ TBool IsPending() const { return iQuery.Length() > 0; }
+
+ TPtrC8 iQuery; //< The query buffer
+ // Reply data
+ union
+ {
+ TUint8 *iResponse; //< For initializing.
+ TNameRecord *iNameRecord; //< GetByAddr/GetByName
+ TDes8 *iQueryResponse; //< Query response
+ TDes *iHostName; //< GetHostName/SetHostName
+ };
+ };
+
+// CProtocolRes
+// ************
+class CHostResolver;
+class CProviderRes;
+class CDndSession;
+class CProtocolRes : public CProtocolBaseUnbind, public MEventListener
+ /**
+ * The "resolver protocol".
+ */
+ {
+ friend class CProviderRes;
+ friend class CHostResolver;
+public:
+ CProtocolRes(CIfManager &aInterfacer);
+ virtual ~CProtocolRes();
+ virtual CServProviderBase *NewSAPL(TUint aProtocol);
+ virtual CHostResolvProvdBase* NewHostResolverL();
+ virtual CServiceResolvProvdBase *NewServiceResolverL();
+ virtual CNetDBProvdBase *NewNetDatabaseL();
+ virtual void Identify(TServerProtocolDesc *) const;
+ virtual void BindL(CProtocolBase* protocol, TUint id);
+ virtual void Unbind(CProtocolBase* protocol, TUint id);
+ virtual void StartL();
+ static void FillinInfo(TServerProtocolDesc &anEntry);
+ //
+ // Resolver specific Methods
+ //
+ inline MInterfaceManager &Interfacer() const { return iInterfacer; }
+
+ CHostResolver *FindPending() const;
+ CProviderRes *NewQuery(CHostResolver *aResolver);
+private:
+ void CancelSAP(const CProviderRes &aSAP); // ..for ~CProviderRes() only!
+ void CancelQuery(const CHostResolver &aQuery); // ..for ~CHostResolver() only!
+ virtual void Notify(TUint aEventClass, TUint aEventType, const void *aData);
+
+ CProviderRes *iDND; //< The Domain Name Resolver Daemon (DND)
+ CHostResolver *iQueries; //< List of Queries (RHostResolver sessions)
+ CIfManager &iInterfacer; //< The Interface manager link
+ TUint iConfigureDone:1; //< = 1, when the Configure request has been sent to DND
+ };
+
+
+// CProviderRes
+// ************
+class CHostResolver;
+class CDndSession;
+class CProviderRes: public CServProviderBase
+ /**
+ * The "DND provider" (SAP).
+ */
+ {
+ friend class CProtocolRes;
+public:
+ CProviderRes(CProtocolRes &aProtocol);
+ ~CProviderRes();
+
+ // 'resolver' has no use for local or remote addresses or ports.
+ // (these methods should never be called by DND) .None of the Open's are called
+ // (resolver is always "connectionless datagram socket"). Just silence the
+ // compiler by defining dummy implementations for all those functions.
+ virtual void ActiveOpen() {}
+ virtual void ActiveOpen(const TDesC8 &) {}
+ virtual TInt PassiveOpen(TUint) { return KErrNone; }
+ virtual TInt PassiveOpen(TUint ,const TDesC8 &) { return KErrNone; };
+ virtual void LocalName(TSockAddr &) const {}
+ virtual TInt SetLocalName(TSockAddr &) { return KErrNone; }
+ virtual void RemName(TSockAddr &) const {}
+ virtual TInt SetRemName(TSockAddr &) { return KErrNone; }
+ virtual void AutoBind() {}
+
+ // These are potentially useful, but don't do much currently
+ virtual void Shutdown(TCloseType option,const TDesC8 &aDisconnectionData);
+ virtual void Shutdown(TCloseType option);
+ virtual TInt GetOption(TUint level,TUint name,TDes8 &anOption) const;
+ virtual void Ioctl(TUint level,TUint name,TDes8* anOption);
+ virtual void CancelIoctl(TUint aLevel, TUint aName);
+ virtual TInt SetOption(TUint level,TUint name, const TDesC8 &anOption);
+
+ // The real "beef"...
+ virtual TUint Write(const TDesC8 &aDesc,TUint options, TSockAddr* aAddr=NULL);
+ virtual void GetData(TDes8 &aDesc,TUint options,TSockAddr *anAddr=NULL);
+ virtual void Start();
+ TInt SecurityCheck(MProvdSecurityChecker *aChecker);
+
+ // Resolver methods
+ void Unlink(); //< Called by iResolver to remove the iResolver
+ void Activate(); //< Used when an attached Session needs servicing.
+ TBool AssignSession(CHostResolver &aHR);//< Assign a session for the Host Resolver
+ CProtocolRes &iProtocol; //< Protocol instance of the 'resolver'
+protected:
+ const CDndSession *Find(const TUint16 aId) const;
+
+ CDndSession *iSessions; //< List of associated sessions.
+ TUint16 iSessionId; //< Last used session id.
+ TInt iAvailable; //< Available sessions in DND.
+ };
+
+// CDndSession
+// ***********
+class CDndSession : public CBase
+ /**
+ * The session assigned to the host resolver.
+ *
+ * When the DND accepts a host resolver to be served,
+ * this class is created to represent the session.
+ *
+ * Note: This is needed separate from the CHostResolver,
+ * because
+ * - the destruction of host resolver is controlled by the socket server,
+ * - after host resolver has been deleted, the cancel message needs to be delivered to DND
+ * - when detached from host resolver, this class represents pending session cancel.
+ */
+ {
+ friend class CProviderRes;
+public:
+ void Link(CHostResolver &aHR); //< Attach Host Resolver to session.
+ void Unlink(); //< Detach Host Resolver, if any.
+ void Reply(const TDnsMessage &aReply, const TDesC8 &aPayload) const;
+ void Submit();
+ void Answered();
+ CDndSession *Delete(); //< Trigger destruction of the session.
+
+ const TUint16 iSessionId; //< The session id
+
+private:
+ CDndSession(CProviderRes &aDnd, TUint16 aId, CDndSession *aNext);
+ ~CDndSession(); //< Private destructor, delete only from Delete()
+
+
+ CProviderRes &iDnd; //< The provider (DND) associated with this session
+ CDndSession *iNext; //< Links sessions under provider together
+ CHostResolver *iResolver; //< Non-null, if this session is (still) associated with RHostResolver
+ TTime iAnswerTime; //< Time of last good answer (only valid if iAnswered == 1).
+ TUint iPending:1; //< = 1, when resolver is pending, and query not yet delivered to DND
+ TUint iAnswered:1; //< = 1, if at least ONE good answer has been returned.
+#ifdef SYMBIAN_DNS_PUNYCODE
+ TUint iEnableIdn:1; //< = 1, if support for resolving International Domain Names are enabled
+#endif //SYMBIAN_DNS_PUNYCODE
+ };
+
+
+// CHostResolver
+// *************
+class CHostResolver : public CHostResolvProvdBase
+ {
+ /**
+ * The host resolver.
+ *
+ * This is the representative of the application RHostResolver within
+ * the TCPIP stack. This is created by the CProtocolRes::NewHostResolverL.
+ */
+public:
+ CHostResolver(CProtocolRes &aProtocol);
+ ~CHostResolver();
+ void GetByName(TNameRecord &aName);
+ void GetByAddress(TNameRecord &aName);
+ void SetHostName(TDes& aNameBuf);
+ void GetHostName(TDes& aNameBuf);
+ void CancelCurrentOperation();
+ TInt SetOption(TUint level, TUint aName,const TDesC8 &option);
+
+ void Unlink(); //< Called by iSession, to remove the iSession link
+ void Reply(const TDnsMessage &aReply, const TDesC8 &aPayload); //< Called by iSession, when DND has replied.
+ void QueryComplete(TInt aResult); //< Called by iSession, when query completed ??
+ void Query(const TDesC8& aQuery, TDes8& aResult, TInt aCount);
+ TInt SecurityCheck(MProvdSecurityChecker *aChecker);
+ TBool IsPending() { return iRequest.IsPending(); }
+ void BuildMessage(TUint16 aId, TDes8 &aBuffer) const { iRequest.BuildMessage(aId, aBuffer); }
+#ifdef SYMBIAN_DNS_PUNYCODE
+ TBool IsIdnEnabled();
+#endif //SYMBIAN_DNS_PUNYCODE
+
+ CHostResolver *iNext; //< Next resolver link chain
+ CDndSession *iSession; //< Assigned DND session or NULL, if none yet.
+private:
+ LOG(TInt Session() {return iSession ? (TInt)iSession->iSessionId : 0;})
+
+ void Submit();
+
+ CProtocolRes &iProtocol; //< Link to the 'resolver' protocol instance
+ TUint32 iNetworkId; //< The default network ID.
+ TUint32 iCurrentId; //< The network ID for the current query
+ TDnsRequest iRequest; //< The query being served
+ THostName iHostName; //< The hostname (only used for SetHostName)
+ TUint iNoNext:1; //< if = 1, next should return Not Found
+ TUint iHasNetworkServices:1;//< = 1, if client has network services.
+#ifdef SYMBIAN_DNS_PUNYCODE
+ TUint iEnableIdn:1; // if =1 , Idn support is enabled.
+#endif //SYMBIAN_DNS_PUNYCODE
+ MProvdSecurityChecker *iSecurityChecker;
+public:
+ void NoDndAvailable();
+ RTimeout iTimeout;
+ };
+
+// CHostResolverLinkage
+// ***********************
+// Glue to bind timeout callback from the timeout manager. Only used for
+// detecting a missing or misbehaving DND.
+
+// 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 KHostResolverTimeoutOffset 584
+__ASSERT_COMPILE(KHostResolverTimeoutOffset == _FOFF(CHostResolver, iTimeout));
+#else
+#define KHostResolverTimeoutOffset _FOFF(CHostResolver, iTimeout)
+#endif
+
+class CHostResolverLinkage : public TimeoutLinkage<CHostResolver, KHostResolverTimeoutOffset>
+ {
+public:
+ static void Timeout(RTimeout &aLink, const TTime & /*aNow*/, TAny * /*aPtr*/)
+ {
+ Object(aLink)->NoDndAvailable();
+ }
+ };
+
+//
+// RES Class Implementation
+
+// RES::Identify
+// *************
+void RES::Identify(TServerProtocolDesc &aEntry)
+ {
+ _LIT(KResolver, "resolver");
+
+ aEntry.iName = KResolver;
+ aEntry.iAddrFamily = KAfInet;
+ aEntry.iSockType = KSockDatagram;
+ aEntry.iProtocol = KProtocolInet6Res;
+ aEntry.iVersion = TVersion(1, 0, 0);
+ aEntry.iByteOrder = ELittleEndian;
+ aEntry.iServiceInfo = KSIConnectionLess | KSIMessageBased | KSIRequiresOwnerInfo;
+ aEntry.iNamingServices = 0;
+ aEntry.iSecurity = KSocketNoSecurity;
+ aEntry.iMessageSize = KSocketMessageSizeNoLimit;
+ aEntry.iServiceTypeInfo = ESocketSupport;
+ aEntry.iNumSockets = 1; // Only 1 DND allowed.
+ }
+
+CProtocolBase *RES::NewL(CIfManager *const aInterfacer)
+ {
+ return new (ELeave) CProtocolRes(*aInterfacer);
+ }
+
+// Constructors for the TDnsRequest
+// ********************************
+
+TDnsRequest::TDnsRequest() :
+ TDnsRequestBase(),
+ iQuery(TPtrC8(0, 0)),
+ iResponse(NULL)
+ /**
+ * Construct empty request = No Query Active
+ */
+ {
+ }
+
+TDnsRequest::TDnsRequest(TNameRecord &aQuery, TInt aNext, TInt aType) :
+ TDnsRequestBase(aType, aNext),
+ iQuery(TPtrC8((TUint8 *)&aQuery, sizeof(aQuery))),
+ iResponse((TUint8 *)&aQuery)
+ /**
+ * Construct a GetByName/GetByAddress request.
+ *
+ * @param aQuery The query buffer in the SocketServer
+ * @param aNext = 0 for the actual query, > 0 for additional results.
+ * @param aType GetByName or GetByAddress.
+ */
+ {
+ }
+
+TDnsRequest::TDnsRequest(const TDesC8 &aQuery, TDes8 &aResponse, TInt aNext) :
+ TDnsRequestBase(KDnsRequestType_TDnsQuery, aNext),
+ iQuery(aQuery),
+ iResponse((TUint8 *)&aResponse)
+ /**
+ * Construct a special DNS query request.
+ *
+ * @param aQuery The query buffer in the SocketServer
+ * @param aResponce The responece buffer in the SocketServer
+ * @param aNext = 0 for the actual query, > 0 for additional results.
+ */
+ {
+ }
+
+TDnsRequest::TDnsRequest(const THostName &aName, TDes &aResponse, TInt aType) :
+ TDnsRequestBase(aType, 0),
+ iQuery(TPtrC8((TUint8 *)&aName, sizeof(aName))),
+ iResponse((TUint8 *)&aResponse)
+ /**
+ * Construct Get/Set HostName.
+ *
+ * @param aName The hostname (for SetHostName)
+ * @param aResponce The responce buffer in the SocketServer
+ * @param aType Set or get hostname.
+ */
+ {
+ }
+
+void TDnsRequest::BuildMessage(TUint16 aId, TDes8 &aBuffer) const
+ /**
+ * Build the message from the request parameters.
+ */
+ {
+ aBuffer.SetLength(0);
+ if (aBuffer.MaxLength() >= (TInt)sizeof(TDnsRequestBase) + iQuery.Length())
+ {
+ aBuffer = Header();
+ ((TDnsRequestBase *)aBuffer.Ptr())->iSession = aId;
+ aBuffer.Append(iQuery);
+ }
+ else
+ {
+ // This means that DND is trying to read with too short buffer.
+ LOG(Log::Printf(_L("\tres *** DND buffer is short ***")));
+ }
+ }
+
+//
+// CProtocolRes Class Implementation
+
+CProtocolRes::CProtocolRes(CIfManager &aInterfacer) : iInterfacer(aInterfacer)
+ {
+ LOG(Log::Printf(_L("new\tres resolver[%u] construct"), this));
+ }
+
+void CProtocolRes::BindL(CProtocolBase * /*aProtocol*/, TUint /*aId*/)
+ /** Dummy. No real functionality. */
+ {
+ // Allow anyone to bind, and forget about it...
+ }
+
+void CProtocolRes::Unbind(CProtocolBase * /*aProtocol*/, TUint /*aId*/)
+ /** Dummy. No real functionality. */
+ {
+ // Just pass any unbind too..
+ }
+
+
+// CProtocolRes::StartL
+// ********************
+void CProtocolRes::StartL()
+ {
+ /**
+ * Register for the event service.
+ * Called after all binding is complete. Register for the event
+ * service to receive configuration changes in the interfaces.
+ */
+ MEventService &mgr = *IMPORT_API_L((&iInterfacer), MEventService);
+ mgr.RegisterListener(this, EClassAddress);
+ mgr.RegisterListener(this, EClassInterface);
+ }
+
+// CProtocolRes::Notify
+// ********************
+void CProtocolRes::Notify(TUint aEventClass, TUint aEventType, const void *aData)
+ /**
+ * Configuration changed event.
+ *
+ * This is called by the event manager (MEventService) for the registered events.
+ * Analyze the event and if the conditions are met, request a "configuration changed"
+ * message to be sent to the DND.
+ *
+ * @param aEventClass The event class
+ * @param aEventType The event type
+ * @param aData The event infrormation (TInetAddressInfo or TInetInterfaceInfo)
+ */
+ {
+ if (!iConfigureDone)
+ return; // Configure request is already pending, nothing to do!
+
+ if (aEventClass == EClassAddress)
+ {
+ // ...for now, all address events require reconfigure
+ const TInetAddressInfo &ai = *(TInetAddressInfo *)aData;
+ if ((ai.iFlags & TInetAddressInfo::EF_Id) == 0)
+ return; // Only interested in addresses, not prefixes.
+ if (aEventType != EventTypeDelete && (ai.iState != TInetAddressInfo::EAssigned))
+ return; // Only interested in valid or deleted addresses.
+#ifdef _LOG
+ TInetAddr addr(ai.iAddress, 0);
+ TBuf<70> tmp;
+ addr.Output(tmp);
+ Log::Printf(_L("<>\tres Address Event(%d) IF %d [%S]"), aEventType, ai.iInterface, &tmp);
+#endif
+ }
+ else if (aEventClass == EClassInterface)
+ {
+ // ...for now all interface events require reconfigure
+#ifdef _LOG
+ const TInetInterfaceInfo &ii = *(TInetInterfaceInfo *)aData;
+ Log::Printf(_L("<>\tres Interface Event(%d) IF %d [%S]"), aEventType, ii.iIndex, &ii.iName);
+#endif
+ }
+ else
+ return;
+ //
+ // An event that requires DND reconfiguration has occurred.
+ //
+ iConfigureDone = 0;
+ if (iDND)
+ iDND->Activate();
+
+ }
+
+// CProtocolRes::NewSAPL
+// **********************
+CServProviderBase* CProtocolRes::NewSAPL(TUint /*aSockType*/)
+ /**
+ * Create a new DNS SAP.
+ * This SAP should be the DND server offering the
+ * name resolution services through this datagram
+ * socket.
+ *
+ * Only ONE DND server at any time can be active. Multiple
+ * SAP creations are rejected.
+ */
+ {
+ LOG(Log::Printf(_L("NewSAPL\tres resolver[%u]"), this));
+ if (iDND)
+ User::Leave(KErrAlreadyExists); // Only one DND is allowed (should not get here)
+ iDND = new (ELeave) CProviderRes(*this);
+ LOG(Log::Printf(_L("\tres DND[%u] new"), iDND));
+ return iDND;
+ }
+
+// CProtocolRes::NewHostResolverL
+// ******************************
+CHostResolvProvdBase *CProtocolRes::NewHostResolverL()
+ /**
+ * Create a resolver object.
+ *
+ * Create the internal object match the client RHostResolver. These
+ * objects are stored in the linked list (iQueries) at CProtocolRes.
+ *
+ * @return The host resolver
+ */
+ {
+ CHostResolver *hr = new (ELeave) CHostResolver(*this);
+ hr->iNext = iQueries;
+ iQueries = hr;
+ return hr;
+ }
+
+
+// CProtocolRes::NewServiceResolverL
+// *********************************
+CServiceResolvProvdBase *CProtocolRes::NewServiceResolverL()
+ /**
+ * NewServiceResolverL is not supported.
+ * (Override the default Panic implementation with KErrNotSupported Leave!)
+ */
+ {
+ User::Leave(KErrNotSupported);
+ // NOTREACHED
+ return NULL;
+ }
+
+// CProtocolRes::NewNetDataBaseL
+// *****************************
+CNetDBProvdBase *CProtocolRes::NewNetDatabaseL()
+ /**
+ * NewNetDatabaseL is not supported.
+ * (Override the default Panic implementation with KErrNotSupported Leave!)
+ */
+ {
+ User::Leave(KErrNotSupported);
+ // NOTREACHED
+ return NULL;
+ }
+
+// CProtocolRes::CancelSAP
+// ***********************
+void CProtocolRes::CancelSAP(const CProviderRes &aSAP)
+ /**
+ * The DND has exited.
+ *
+ * Only the ~CProviderRes() destructor calls this. This
+ * removes the reference iDND to the instance.
+ */
+ {
+ LOG(Log::Printf(_L("\tres DND[%u] terminating"), &aSAP));
+ if (&aSAP == iDND)
+ iDND = NULL;
+ iConfigureDone = 0; // Clear for the new DND (if any coming).
+ }
+
+// CProtocolRes::CancelQuery
+// *************************
+void CProtocolRes::CancelQuery(const CHostResolver &aQuery)
+ /**
+ * The host resolver is being deleted.
+ *
+ * Only the ~CHostResolver() destructor calls this. This
+ * removes the reference from the iQueries list.
+ */
+ {
+ CHostResolver **h, *p;
+ h = &iQueries;
+ while ((p = *h) != NULL)
+ if (p == &aQuery)
+ {
+ *h = p->iNext;
+ return;
+ }
+ else
+ h = &p->iNext;
+ }
+
+// CProtocolRes::FindPending
+// *************************
+CHostResolver *CProtocolRes::FindPending() const
+ /**
+ * Find a pending request without a session.
+ *
+ * Looks through all queries and returns the first one
+ * which is waiting for a request, but does not yet have
+ * session object (CDndSession) assigned.
+ *
+ * There can be pending requests without session ONLY IF
+ * DND is not yet running, or if CProviderRes::AssignSession()
+ * has failed due to all resolver slots being reserved.
+ * The latter condition should be a rare occurrence
+ * (should never happen!).
+ *
+ * @return The host resolver or NULL if none found.
+ */
+ {
+ for (CHostResolver *hr = iQueries; hr != NULL; hr = hr->iNext)
+ if (hr->iSession == NULL && hr->IsPending())
+ return hr;
+ return NULL;
+ }
+
+// CProtocolRes::~CProtocolRes
+// ***************************
+CProtocolRes::~CProtocolRes()
+ {
+ LOG(Log::Printf(_L("\tres resolver[%u] destruct"), this));
+
+ ASSERT(iDND == NULL); // -- cannot get here if there is a DND attached
+ ASSERT(iQueries == NULL); // -- cannot get here if there are RHostResolver's
+
+ TRAP_IGNORE(MEventService &mgr = *IMPORT_API_L((&iInterfacer), MEventService);
+ mgr.RemoveListener(this, EClassAddress);
+ mgr.RemoveListener(this, EClassInterface);
+ );
+ }
+
+// CProtocolRes::Identify
+// **********************
+void CProtocolRes::Identify(TServerProtocolDesc *aInfo) const
+ {
+ RES::Identify(*aInfo);
+ }
+
+//
+// CDndSession
+
+CDndSession::CDndSession(CProviderRes &aDnd, TUint16 aSession, CDndSession *aNext)
+ : iSessionId(aSession), iDnd(aDnd), iNext(aNext)
+ {
+ }
+
+CDndSession::~CDndSession()
+ {
+ }
+
+// CDndSession::Delete
+// *******************
+CDndSession *CDndSession::Delete()
+ /**
+ * Delete self.
+ *
+ * Deletes self, but returns the link to the
+ * next session in chain (or NULL, if last).
+ *
+ * @return The next in chain.
+ */
+ {
+ CDndSession *const next = iNext; // Return iNext field.
+
+ ASSERT(iResolver == NULL);
+
+ delete this;
+ return next;
+ }
+
+// CDndSession::Submit
+// *******************
+void CDndSession::Submit()
+ /**
+ * Requests a service from DND.
+ *
+ * Do the basic details for requesting service for this
+ * session. Actual nature of the request is not considered
+ * here (it's up to the caller of this function).
+ */
+ {
+ iPending = 1;
+ iAnswered = 0;
+ iDnd.Activate();
+ }
+
+// CDndSession::Answered
+// *********************
+void CDndSession::Answered()
+ /**
+ * Mark session as answered.
+ *
+ * The associated host resolver of this session has received
+ * an answer for the request. This function exists only to
+ * handle the situation where system does not have enough
+ * host resolver slots to use. If there is a pending
+ * request without a session, then this session is
+ * given to that request. This action makes prevents
+ * use of the "Next()" action for previous owner of the
+ * session.
+ */
+ {
+ CHostResolver *const hr = iDnd.iProtocol.FindPending();
+ if (hr)
+ {
+ // Exceptional branch, reassign session to another.
+ iResolver->Unlink();
+ iResolver = NULL;
+ Link(*hr);
+ iDnd.Activate();
+ }
+ else
+ {
+ // Normal branch.
+ iAnswerTime.UniversalTime();
+ iAnswered = 1;
+ }
+ }
+
+// CDndSession::Reply
+// ******************
+void CDndSession::Reply(const TDnsMessage &aReply, const TDesC8 &aPayload) const
+ /**
+ * A reply from DND.
+ *
+ * Relay the reply from DND to the host resolver. If the host resolver
+ * has already gone away, the reply is simply dropped.
+ *
+ * @param aReply The DND reply message (for the header part).
+ * @param aPayload The DND "payload" part of the reply message.
+ */
+ {
+ if (iResolver)
+ iResolver->Reply(aReply, aPayload);
+ }
+
+// CDndSession::Link
+// *****************
+void CDndSession::Link(CHostResolver &aHR)
+ /**
+ * Link the host resolver to session.
+ *
+ * @param aHR The host resolver with query pending
+ */
+ {
+ ASSERT(aHR.IsPending());
+ ASSERT(aHR.iSession == NULL);
+ ASSERT(iResolver == NULL);
+ aHR.iTimeout.Cancel();
+ aHR.iSession = this;
+ iResolver = &aHR;
+ iPending = 1;
+#ifdef SYMBIAN_DNS_PUNYCODE
+ if( aHR.IsIdnEnabled() )
+ {
+ iEnableIdn = 1;
+ }
+#endif //SYMBIAN_DNS_PUNYCODE
+ LOG(Log::Printf(_L("\tres HR[%u] SESSION %u assigned HR"), iResolver, (TInt)iSessionId));
+ }
+
+// CDndSession::Unlink()
+// *********************
+void CDndSession::Unlink()
+ /**
+ * Unlink host resolver from session.
+ *
+ * This is called from CHostResolver, when it does not
+ * need the session any more.
+ */
+ {
+ ASSERT(iResolver != NULL); // Should never be called with NULL iResolver!
+ LOG(Log::Printf(_L("\tres HR[%u] SESSION %u detached HR"), iResolver, (TInt)iSessionId));
+ iResolver = NULL;
+ iPending = 0;
+ // See, if a new resolver should be assigned to this session
+ CHostResolver *const hr = iDnd.iProtocol.FindPending();
+ if (hr)
+ Link(*hr);
+ // Activate needed always, either to pass a new query or
+ // cancel, if no new resolver was assigned.
+ iDnd.Activate();
+ }
+
+//
+// CProviderRes Implementation
+
+CProviderRes::CProviderRes(CProtocolRes &aProtocol) : iProtocol(aProtocol)
+ {
+ }
+
+CProviderRes::~CProviderRes()
+ /**
+ * DND server has terminated.
+ * Cleanup all sessions
+ */
+ {
+ while (iSessions)
+ iSessions = iSessions->Delete();
+ iProtocol.CancelSAP(*this);
+ }
+
+
+// CProviderRes::Activate
+// **********************
+void CProviderRes::Activate()
+ {
+ /**
+ * Call socket NewData.
+ */
+ if (iSocket)
+ iSocket->NewData(1);
+ }
+
+// CProviderRes::Find
+// ******************
+const CDndSession *CProviderRes::Find(const TUint16 aId) const
+ /**
+ * Find a session by id.
+ *
+ * @param aId The session id.
+ * @return The session or NULL, if not found.
+ */
+ {
+ const CDndSession *s = iSessions;
+ for ( ;s != NULL; s = s->iNext)
+ if (s->iSessionId == aId)
+ break;
+ return s;
+ }
+
+// CProviderRes::Write
+// *******************
+TUint CProviderRes::Write(const TDesC8 &aDesc, TUint /* aResult */, TSockAddr* /*aAddr =NULL*/)
+ /**
+ * The reply from DND.
+ *
+ * The resolver is now returning some reply. Deliver the reply
+ * to the matching host resolver (matched by the session id).
+ *
+ * @param aDesc The TDnsMessage containing the reply.
+ * @return Always 1 (= data accepted).
+ */
+ {
+ const TDnsMessage &reply = *(TDnsMessage *)aDesc.Ptr();
+ //
+ // Locate the session for which the reply belongs
+ //
+ const CDndSession *const s = Find(reply.iSession);
+ if (s == NULL)
+ {
+ // Ooops! DND is still sending replies to a session that do not
+ // exist any more. Should there be implicitly generated cancel
+ // message?
+ LOG(Log::Printf(_L("Write\tres SESSION %u not found--DND reply ignored"), (TInt)reply.iSession));
+ }
+ else
+ {
+ const TPtrC8 payload = reply.Payload(aDesc.Length());
+ s->Reply(reply, payload);
+ }
+ return 1;
+ }
+
+
+// CProviderRes::AssignSession
+// ***************************
+TBool CProviderRes::AssignSession(CHostResolver &aHR)
+ /**
+ * Assign a session for a host resolver.
+ *
+ * Try to acquire a session for the host resolver.
+ *
+ * @param The Host resolver
+ * @return ETrue, if session assiged, and EFalse otherwise.
+ */
+ {
+ ASSERT(aHR.IsPending()); // Host resolver must have a query to be done
+ ASSERT(aHR.iSession == NULL); // Host resolver must be without a session.
+
+ // Check the existing sessions
+
+ CDndSession *a = NULL;
+ for (CDndSession *s = iSessions; s != NULL; s = s->iNext)
+ {
+ if (s->iResolver == NULL)
+ {
+ // There is already a session queued for "cancel" request.
+ // This session can be efficiently reassigned to the new
+ // host resolver.
+ s->Link(aHR);
+ // There is no need to Activate() anything, because there must
+ // already be a pending Activate() for the cancel message.
+ return ETrue;
+ }
+ else if (s->iAnswered)
+ {
+ if (a == NULL || a->iAnswerTime > s->iAnswerTime)
+ a = s;
+ }
+ }
+
+ if (iAvailable > 0)
+ {
+ // Assign unused session id.
+ do
+ {
+ if (++iSessionId == 0)
+ iSessionId = 1; // Avoid ZERO as id!
+ }
+ while (Find(iSessionId) != NULL);
+
+ a = new CDndSession(*this, iSessionId, iSessions);
+ if (a)
+ {
+ // Created a new session, assign to query.
+ iAvailable -= 1;
+ iSessions = a;
+ a->Link(aHR);
+ // Activate() is needed, because this is a new session.
+ Activate();
+ return ETrue;
+ }
+ else
+ {
+ aHR.QueryComplete(KErrNoMemory); // <-- check!!!
+ return EFalse;
+ }
+ }
+
+ if (a)
+ {
+ // Found a session that can be stolen from it's host resolver.
+ a->iResolver->Unlink();
+ a->iResolver = NULL;
+ a->Link(aHR);
+ // Activate is required
+ Activate();
+ return ETrue;
+ }
+ // No sessions available
+ return EFalse;
+ }
+
+// CProviderRes::GetData
+// *********************
+void CProviderRes::GetData(TDes8 &aDesc, TUint /* aOptions */, TSockAddr * /*anAddr=NULL*/)
+ /**
+ * Prepare the query message for the DND.
+ *
+ * Build and return the query message for the DND. The buffer (aDesc) MUST be long enough
+ * to receive any query.
+ *
+ * @retval aDesc The query message.
+ */
+ {
+ if (!iProtocol.iConfigureDone)
+ {
+ //
+ // Generate a DNS Configure request
+ //
+ iProtocol.iConfigureDone = 1;
+ aDesc.SetLength(sizeof(TDnsRequestBase));
+ aDesc.FillZ();
+ ((TDnsRequestBase *)aDesc.Ptr())->iType = KDnsRequestType_Configure;
+ LOG(Log::Printf(_L("GetData\tres DND Configure msg(%d)"), aDesc.Length()));
+ return;
+ }
+ // Find a session that needs to be serviced, return it. If this was
+ // a cancelled session, then delete entry.
+ //
+ // Note: The search starts always from the beginning of the Sessions list,
+ // and the first one needing service is served. This is not "fair", but
+ // it is assumed that in practise DND can accept all requests fast.
+ CDndSession *s;
+ for (CDndSession **h = &iSessions; (s = *h) != NULL; h = &s->iNext)
+ {
+ if (s->iResolver == NULL)
+ {
+ // Build a cancel session message with s->iSession
+ // (just a header and session id is treated as cancel).
+ aDesc.SetLength(sizeof(TDnsRequestBase));
+ aDesc.FillZ();
+ ((TDnsRequestBase *)aDesc.Ptr())->iSession = s->iSessionId;
+ LOG(Log::Printf(_L("GetData\tres SESSION %u DND Cancel msg(%d)"), (TInt)s->iSessionId, aDesc.Length()));
+ *h = s->Delete();
+ iAvailable += 1;
+ return;
+ }
+ else if (s->iPending)
+ {
+ ASSERT(s->iResolver->IsPending()); // There must be a pending request!
+ s->iPending = 0; // Prevent this same request from being sent again to DND.
+ s->iResolver->BuildMessage(s->iSessionId, aDesc);
+ LOG(Log::Printf(_L("GetData\tres HR[%u] SESSION %u DND Query msg(%d)"), s->iResolver, (TInt)s->iSessionId, aDesc.Length()));
+ return;
+ }
+ }
+
+ // This path is reached ONLY when session is assigned to a host resolver
+ // and queued (Activated) for processing, but is cancelled by application
+ // before the DND gets to process the Activate...
+ LOG(Log::Printf(_L("GetData\tres Nothing found, empty DND Query")));
+ aDesc.SetLength(0); // No data, nothing to do.
+ }
+
+
+void CProviderRes::Start()
+ /** The DND is becoming ready to serve. */
+ {
+ LOG(Log::Printf(_L("\tres DND[%u] Start"), this));
+ // Reconfiguration needed
+ if (!iProtocol.iConfigureDone)
+ Activate();
+ }
+
+void CProviderRes::Shutdown(TCloseType aOption, const TDesC8& /*aDisconnectionData*/)
+ {
+ /** The DND is shutting down */
+ Shutdown(aOption);
+ }
+
+void CProviderRes::Shutdown(TCloseType aOption)
+ /** The DND is shutting down */
+ {
+ LOG(Log::Printf(_L("Shutdown\res DND[%u] type=%d"), this, (TInt)aOption));
+ if (aOption != EImmediate)
+ iSocket->CanClose();
+ }
+
+void CProviderRes::Ioctl(TUint /*aLevel*/, TUint /*aName*/, TDes8* /*aOption*/)
+ /** Dummy. No real functionality. */
+ {
+ LOG(Log::Printf(_L("ioctl\tres DND[%u]"), this));
+ }
+
+
+void CProviderRes::CancelIoctl(TUint /*aLevel*/, TUint /*aName*/)
+ /** Dummy. No real functionality. */
+ {
+ LOG(Log::Printf(_L("ioctl\tres DND[%u] Cancel"), this));
+ }
+
+TInt CProviderRes::SetOption(TUint aLevel, TUint aName, const TDesC8& aOption)
+ /**
+ * SetOption.
+ *
+ * This implements a resolver gate specific option:
+ *
+ * level= #KSolDnd, name= #KSoDndSessions
+ *
+ * The DND must tell the number of available resolver slots to
+ * the resolver gateway code using this SetOption.
+ *
+ * Other options are passed to the interface manager.
+ */
+ {
+ if (aLevel == KSolDnd)
+ {
+ if (aName != KSoDndSessions)
+ return KErrNotSupported;
+
+
+ if (aOption.Length() < (TInt)sizeof(TInt))
+ return KErrGeneral;
+ // note: here it is assumed that the Ptr() is properly
+ // aligned, but for debug check it! -- msa)
+ ASSERT((((TUint)aOption.Ptr()) & 0x3) == 0);
+ iAvailable = *((TInt *)aOption.Ptr());
+ // Count current sessions and subtract them from iAvailable
+ // (it's ok, even if result is negative!)
+ for (CDndSession *s = iSessions; s != NULL; s = s->iNext)
+ iAvailable -= 1;
+ LOG(Log::Printf(_L("SetOpt\tres DND[%u] Sessions=%d"), this, iAvailable));
+ // Activate additional pending sessions, if possible.
+ CHostResolver *hr;
+ while ((hr = iProtocol.FindPending()) != NULL)
+ {
+ if (!AssignSession(*hr))
+ break;
+ }
+ return KErrNone;
+ }
+ LOG(Log::Printf(_L("SetOpt\tres DND[%u] level=%d, name=%d"), this, aLevel, aName));
+ return iProtocol.Interfacer().SetOption(aLevel, aName, aOption);
+ }
+
+
+TInt CProviderRes::GetOption(TUint aLevel, TUint aName, TDes8& aOption) const
+ /**
+ * GetOption.
+ *
+ * There is no local options, the call is passed to the interface manager.
+ */
+ {
+ LOG(Log::Printf(_L("GetOpt\tres DND[%u] level=%d, name=%d"), this, aLevel, aName));
+ return iProtocol.Interfacer().GetOption(aLevel, aName, aOption);
+ }
+
+
+// CProviderRes::SecurityCheck
+// ***************************
+TInt CProviderRes::SecurityCheck(MProvdSecurityChecker *aChecker)
+ /**
+ * Check security of the DND implementor.
+ * This represents a socket for the DND server. Check that the
+ * application opening this socket actually has the sufficient
+ * capability to be the DND.
+ */
+ {
+ return aChecker->CheckPolicy(KPolicyNetworkControl, "DND Server");
+ }
+
+//
+// CHostResolver Class Implementation
+//
+
+CHostResolver::CHostResolver(CProtocolRes &aProtocol) : iProtocol(aProtocol), iTimeout(CHostResolverLinkage::Timeout)
+ {
+ LOG(Log::Printf(_L("new\tres HR[%u]"), this));
+ SocketServExt::OpenSession();
+ // Any host resolver must count as "user" to prevent system
+ // from killing the DND while doing resolving.
+ iProtocol.Interfacer().IncUsers();
+ }
+
+
+CHostResolver::~CHostResolver()
+ {
+ // Because CancelCurrentOperation() is virtual, avoid using
+ // it in desctructor!
+ iTimeout.Cancel();
+ iProtocol.CancelQuery(*this);
+ if (iSession)
+ iSession->Unlink();
+ iProtocol.Interfacer().DecUsers();
+ LOG(Log::Printf(_L("~\tres HR[%u] deleted"), this));
+ SocketServExt::CloseSession();
+ }
+
+// CHostResolver::NoDndAvailable
+// *****************************
+void CHostResolver::NoDndAvailable()
+ /**
+ * The timeout expired.
+ * This should only happen when there is no DND running.
+ * See CHostResolver::Submit
+ */
+ {
+ if (iProtocol.iDND == NULL)
+ QueryComplete(KErrInetNoDnsResolver);
+ }
+
+//
+// CHostResolver::Submit
+// *********************
+void CHostResolver::Submit()
+ /**
+ * Submit the request to the DND.
+ *
+ * If there is no DND yet, set a short timer which calls
+ * CHostResolver::NoDndAvailable when it expires. Normally,
+ * there is always DND running. Only when the stack is
+ * starting there is a short time period when host resolvers
+ * can request service while DND is still starting up. Any
+ * longer delay indicates that the DND startup has failed
+ * and the timeout will expire the requests with a specific
+ * error code (#KErrInetNoDnsResolver).
+ */
+ {
+ //
+ // Complete the request with network id
+ //
+ iRequest.iId = iCurrentId;
+#ifdef SYMBIAN_DNS_PUNYCODE
+ iRequest.iScope |= EScopeType_NET;
+#else
+ iRequest.iScope = EScopeType_NET;
+#endif //SYMBIAN_DNS_PUNYCODE
+
+
+ if (iSession == NULL)
+ {
+ if (iProtocol.iDND != NULL)
+ (void)iProtocol.iDND->AssignSession(*this);
+ else
+ {
+ // Set short timeout, if DND is not yet running.
+ iProtocol.iInterfacer.SetTimer(iTimeout, 10);
+ }
+ }
+ else
+ iSession->Submit();
+ }
+
+// CHostResolver::SetOption
+// ************************
+TInt CHostResolver::SetOption(TUint aLevel, TUint aName, const TDesC8& aOption)
+ /**
+ * SetOption.
+ * Implements the #KSoConnectionInfo for the host resolver instance. Other options
+ * are passed to the CProtocolRes.
+ */
+ {
+#ifdef SYMBIAN_NETWORKING_UPS
+
+ if (aLevel == static_cast<TUint>(KSOLProvider))
+ {
+ if (aName == static_cast<TUint>(KSoConnectionInfo))
+ {
+ if (aOption.Length() >= sizeof(TSoIfConnectionInfo))
+ {
+ const TSoIfConnectionInfo& opt = *reinterpret_cast<const TSoIfConnectionInfo*>(aOption.Ptr());
+ iNetworkId = opt.iNetworkId;
+ return KErrNone;
+ }
+ else
+ {
+ return KErrArgument;
+ }
+ }
+ else
+ if (aName == static_cast<TUint>(KSoGetErrorCode))
+ {
+ // Return a TCP/IP failure code appropriate to the last operation.
+ // Kludge - SetOption does not allow for any return value via aOption (being const), so
+ // return a positive value representing the error code.
+ return (iRequest.iType == KDnsRequestType_GetByAddress) ? -KErrDndAddrNotFound : -KErrDndNameNotFound;
+ }
+ }
+
+#else
+
+ if (aLevel == STATIC_CAST(TUint, KSOLProvider) && aName == STATIC_CAST(TUint, KSoConnectionInfo))
+ if (STATIC_CAST(TUint, aOption.Length()) >= sizeof(TSoIfConnectionInfo))
+ {
+ // If the client does not have network services, don't allow setting
+ // of the network id (=> limit queries to local host data only!).
+ if (!iHasNetworkServices)
+ return KErrNone;
+ TSoIfConnectionInfo &opt = *(TSoIfConnectionInfo*)aOption.Ptr();
+ iNetworkId = opt.iNetworkId;
+ return KErrNone;
+ }
+ else
+ return KErrArgument;
+
+#endif
+#ifdef SYMBIAN_DNS_PUNYCODE
+ else if (aLevel == KSolInetDns )
+ {
+ if(aName == static_cast<TUint>(KSoDnsEnableIdn) )
+ {
+ TPckgC<TBool>* setOptPckg=(TPckgC<TBool>*)(&aOption);
+ const TBool& enableIdn=(*setOptPckg)();
+
+ if(enableIdn)
+ {
+ iEnableIdn = 1;
+ }
+ else
+ {
+ if(iEnableIdn == 1)
+ {
+ iEnableIdn = 0;
+ }
+ }
+ return KErrNone;
+ }
+ else
+ {
+ return KErrArgument;
+ }
+ }
+#endif //SYMBIAN_DNS_PUNYCODE
+
+ return iProtocol.SetOption(aLevel, aName, aOption);
+ }
+
+// CHostResolver::Unlink
+// ************************
+void CHostResolver::Unlink()
+ /**
+ * Unlink resolver from session.
+ * Called from the destructor of the attached
+ * session to remove the reference to it. If a
+ * query is active at this point, the caller will take
+ * care of any necessary QueryComplete notifys *after*
+ * this! This only needs to remove the link!
+ */
+ {
+ LOG(Log::Printf(_L("\tres HR[%u] SESSION %d detached"), this, Session()));
+ ASSERT(iSession != NULL); // Should never get called with iSession == NULL!
+ iSession = NULL;
+ iNoNext = 1; // Next() cannot be used if session is disconnected.
+ }
+
+// CHostResolver::QueryComplete
+// ****************************
+void CHostResolver::QueryComplete(TInt aResult)
+ /**
+ * Pass query completion to the Socket Server.
+ *
+ * Called by attached resolver service to notify that query has
+ * been completed. A passthrough to the socket server. If the
+ * result is KErrNone, the reply content has already been copied
+ * to the buffer of the socket server.
+ *
+ * @param aResult The query result code.
+ */
+ {
+ if (iRequest.IsPending())
+ {
+#ifndef SYMBIAN_NETWORKING_UPS
+ // The result KErrCompletion indicates that the request could not be resolved
+ // without use of name servers. If the client does not have network services
+ // capability, just return the appropriate "not found" error status.
+ if (!iHasNetworkServices && aResult == KErrCompletion)
+ aResult = iRequest.iType == KDnsRequestType_GetByAddress ? KErrDndAddrNotFound : KErrDndNameNotFound;
+#endif //SYMBIAN_NETWORKING_UPS
+ (void)new (&iRequest) TDnsRequest();
+ if (aResult == KErrNone)
+ {
+ // If session attached, mark it answered
+ // (note: this information is only used when
+ // system is running out of available sessions)
+ if (iSession)
+ iSession->Answered();
+ }
+ else if (aResult != KErrCompletion)
+ // Any error indicates that there cannot be any further use
+ // for the socket gateway to the DND, thus tear it down to
+ // release resources. However, KErrCompletion is such that
+ // a new call will normally follow, so make exception for it.
+ CancelCurrentOperation();
+ // Note: CancelCurrentOperation (if executed) must be done before
+ // the QueryComplete, because QueryComplete may call directly
+ // GetByName or GetByAddress (may happen with KErrCompletion)
+ LOG(Log::Printf(_L("\tres HR[%u] SESSION %d QueryComplete(%d)"), this, Session(), aResult));
+ iNotify->QueryComplete(aResult);
+ }
+ else
+ CancelCurrentOperation();
+ }
+
+// CHostResolver::Reply
+// ********************
+void CHostResolver::Reply(const TDnsMessage &aReply, const TDesC8 &aPayload)
+ /**
+ * The reply has become available.
+ * The DND has returned a reply to the query. Pass the results back
+ * to application/socket server. Called via CDndSession as follows:
+ *
+ * DND socket send - CProverRes::Write - CDndSession::Reply - this
+ *
+ * @param aReply The reply message (for header)
+ * @param aPayload The actual reply content.
+ */
+ {
+ TInt result = aReply.iNext;
+ if (result == KErrNone)
+ {
+ if (aReply.iType != iRequest.iType)
+ result = KErrGeneral; // ...reply format does not match the query format!
+ else switch (aReply.iType)
+ {
+ case KDnsRequestType_GetByName:
+ case KDnsRequestType_GetByAddress:
+ // The payload is the TNameRecord
+ *iRequest.iNameRecord = aReply.NameRecord();
+#ifdef _LOG
+ {
+ TBuf<70> tmp;
+ TInetAddr::Cast(iRequest.iNameRecord->iAddr).OutputWithScope(tmp);
+ Log::Printf(_L("Write\tres HR[%u] SESSION %d DND Reply = %S [%S]"), this, Session(), &iRequest.iNameRecord->iName, &tmp);
+ }
+#endif
+ break;
+ case KDnsRequestType_TDnsQuery:
+ if (iRequest.iQueryResponse->MaxSize() >= aPayload.Size())
+ {
+ *iRequest.iQueryResponse = aPayload;
+ LOG(Log::Printf(_L("write\tres HR[%u] DND Reply to query, length=%d"),
+ this, iRequest.iQueryResponse->Length()));
+ }
+ else
+ result = KErrTooBig;
+ break;
+ case KDnsRequestType_GetHostName:
+ if (iRequest.iHostName->MaxLength() >= aReply.HostName().Length())
+ {
+ *iRequest.iHostName = aReply.HostName();
+ LOG(Log::Printf(_L("Write\tres HR[%u] SESSION %d DND Reply to GetHostName, %S"),
+ this, Session(), iRequest.iHostName));
+ }
+ else
+ result = KErrTooBig;
+ break;
+ case KDnsRequestType_SetHostName: // does not return any data.
+ LOG(Log::Printf(_L("Write\tres HR[%u] SESSION %d DND Reply to SetHostName"), this, Session()));
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ LOG(Log::Printf(_L("Write\tres HR[%u] SESSION %d DND Reply Error=%d"), this, Session(), result));
+
+ QueryComplete(result);
+ }
+
+// CHostResolver::CancelCurrentOperation
+// *************************************
+void CHostResolver::CancelCurrentOperation()
+ /**
+ * Release all extra resources assigned for the host resolver.
+ *
+ * This function is called when the host resolver does not need
+ * any additional resources. Currently this means the release of
+ * of the session with DND. The host resolver does not need the
+ * session when
+ *
+ * - current query ends with an error (no additional info is available with session)
+ * - the current request is being cancelled
+ * - the host resolver is going to be deleted
+ */
+ {
+ LOG(Log::Printf(_L("\tres HR[%u] SESSION %d CancelCurrentOperation"), this, Session()));
+
+ (void)new (&iRequest) TDnsRequest();
+ if (iSession)
+ {
+ iSession->Unlink();
+ iSession = NULL;
+ }
+ }
+
+// CHostResolver::GetHostName
+// **************************
+void CHostResolver::GetHostName(TDes &aNameBuf)
+ /**
+ * GetHostName handler.
+ * This implements the RHostResolver::GetHostName from the application.
+ */
+ {
+ iHostName.SetLength(0);
+ (void)new (&iRequest) TDnsRequest(iHostName, aNameBuf, KDnsRequestType_GetHostName);
+ iCurrentId = iNetworkId;
+ LOG(Log::Printf(_L("<>\tres HR[%u] SESSION %d GetHostName(maxlen=%d) NID=%d"),
+ this, Session(), aNameBuf.MaxLength(), iNetworkId));
+ Submit();
+ }
+
+// CHostResolver::SetHostName
+// **************************
+void CHostResolver::SetHostName(TDes& aNameBuf)
+ /**
+ * SetHostName handler.
+ * This implements the RHostResolver::SetHostName from the application.
+ */
+ {
+ TInt result;
+ for (;;) // ...NOT A LOOP, JUST FOR BREAK EXITS!
+ {
+ result = iSecurityChecker->CheckPolicy(KPolicyNetworkControl, "SetHostName");
+ if (result != KErrNone)
+ break; // ...operation not allowed for this user
+ if (iHostName.MaxLength() < aNameBuf.Length())
+ {
+ result = KErrTooBig;
+ break; // ...the name is too long
+ }
+ iHostName = aNameBuf;
+ (void)new (&iRequest) TDnsRequest(iHostName, iHostName, KDnsRequestType_SetHostName);
+ iCurrentId = iNetworkId;
+ LOG(Log::Printf(_L("<>\tres HR[%u] SESSION %d SetHostName(%S) NID=%d"), this, Session(), &aNameBuf, iNetworkId));
+ Submit();
+ // SetHostName has been succesfully submitted
+ // to the DND, DND issues the completion later.
+ return;
+ }
+ // SetHostName not done!
+ LOG(Log::Printf(_L("<>\tres HR[%u] SESSION %d SetHostName(%S) not done: %d"), this, Session(), &aNameBuf, result));
+ iNotify->QueryComplete(result);
+ }
+
+// CHostResolver::GetByName
+// ************************
+void CHostResolver::GetByName(TNameRecord &aName)
+ /**
+ * GetByName handler.
+ * This implements the RHostResolver::GetByName and RHostResolver::Next() (for the name) from the application.
+ *
+ * @retval aName The result (and the query in aName.iName as input)
+ */
+ {
+ ASSERT(!IsPending()); // Cannot be in pending state!
+ LOG(Log::Printf(_L("ByName\tres HR[%u] SESSION %d Next=%d NID=%d Name=%S"), this, Session(),
+ aName.iFlags, iNetworkId, &aName.iName));
+ //
+ // Note: aName.iFlage > 0 indicate resolver.Next() request
+ //
+ (void)new (&iRequest) TDnsRequest(aName, aName.iFlags, KDnsRequestType_GetByName);
+#ifdef SYMBIAN_DNS_PUNYCODE
+ if(this->iEnableIdn)
+ {
+ iRequest.iScope |= ENABLEIDN_SCOPE;
+ }
+#endif //SYMBIAN_DNS_PUNYCODE
+
+ TInt result = KErrNotFound;
+ for (;;)// ** Just to allow easy exits from a block with 'break'
+ {
+ TInetAddr &addr = TInetAddr::Cast(aName.iAddr);
+
+ if (aName.iFlags == 0)
+ {
+ iNoNext = 1;
+
+ if (addr.Input(aName.iName) == KErrNone)
+ {
+ // The name was all numeric IPv4 or IPv6 address
+ // (possibly with numeric %scope notation), just
+ // return the result as is.
+ result = KErrNone;
+ break;
+ }
+
+ iCurrentId = iNetworkId;
+ const TInt i = aName.iName.LocateReverse('%');
+ if (i >= 0)
+ {
+
+ // The name is using the "name%interface" notation. Retrieve
+ // the network ID based on the interface, and override
+ // the default setting.
+ const MInterface *const mi = iProtocol.Interfacer().Interface(aName.iName.Right(aName.iName.Length() - i - 1));
+ if (mi == NULL)
+ break; // use default return KErrNotFound, if interface
+ // cannot be located.
+ iCurrentId = mi->Scope(EScopeType_NET);
+ // Remove the "%"-part from the name.
+ aName.iName.SetLength(i);
+ if (addr.Input(aName.iName) == KErrNone)
+ {
+ // Remaining part was numeric address, fill in the
+ // appropriate scope id. Always return IPv6 (KAfInet6)
+ // format address.
+ if (addr.Family() != KAfInet6)
+ addr.ConvertToV4Mapped();
+ addr.SetScope(mi->Scope((TScopeType)(addr.Ip6Address().Scope() - 1)));
+ result = KErrNone;
+ break;
+ }
+ // ...was "name%interface" notation, which overrides the
+ // default network id.
+ // Ignore %-notation without network service capability.
+ // (maybe too harsh, but easiest to solve the problem: the
+ // notation allows initial non-zero id, without the completion
+ // phase (KErrCompletion). Thus, "hostname%RealIf" would allow use
+ // network for DNS queries without network services capability!)
+ //
+ // No change for SYMBIAN_NETWORKING_UPS. This is an undocumented feature which is not
+ // worth changing the TCP/IP stack and ESock to get working with UPS. The
+ // user can make use of an explicit RHostResolver to achieve the same effect.
+ if (!iHasNetworkServices)
+ iCurrentId = 0;
+ }
+ iNoNext = 0; // Allow Next Processing
+ }
+ else if (iNoNext)
+ break; // KErrNotFound
+
+ Submit();
+ return; // ** NEVER FORGET TO TERMINATE THE 'FAKE' LOOP! **
+ }
+ QueryComplete(result);
+ }
+
+// CHostResolver::GetByAddress
+// ***************************
+void CHostResolver::GetByAddress(TNameRecord &aName)
+ /**
+ * GetByAddr handler.
+ * This implements the RHostResolver::GetByAddr and RHostResolver::Next() (for the addr) from the application.
+ *
+ * @retval aName The result (and query aName.iAddr as input)
+ */
+ {
+ ASSERT(!IsPending()); // Cannot be in pending state!
+#ifdef _LOG
+ TBuf<100> tmp;
+ TInetAddr::Cast(aName.iAddr).OutputWithScope(tmp);
+ Log::Printf(_L("ByAddr\tres HR[%u] SESSION %d Next=%d NID=%d Addr=%S"),
+ this, Session(), aName.iFlags, iNetworkId, &tmp);
+#endif
+ //
+ // Note: aName.iFlage > 0 indicate resolver.Next() request
+ //
+ (void)new (&iRequest) TDnsRequest(aName, aName.iFlags, KDnsRequestType_GetByAddress);
+#ifdef SYMBIAN_DNS_PUNYCODE
+ if(this->iEnableIdn)
+ {
+ iRequest.iScope |= ENABLEIDN_SCOPE ;
+ }
+#endif //SYMBIAN_DNS_PUNYCODE
+
+ TInetAddr &addr = TInetAddr::Cast(aName.iAddr);
+ if (aName.iFlags == 0)
+ {
+ iCurrentId = iNetworkId;
+ if (addr.Family() != KAfInet6)
+ addr.ConvertToV4Mapped();
+ addr.SetPort(0);
+ if (addr.Scope() == 0)
+ {
+ // Pick default scope, if none specified.
+ addr.SetScope(iProtocol.Interfacer().RemoteScope(addr.Ip6Address(), iNetworkId, EScopeType_NET));
+ }
+ iNoNext = 0; // Allow Next Processing
+ }
+ else if (iNoNext)
+ {
+ QueryComplete(KErrNotFound);
+ return;
+ }
+ Submit();
+ }
+
+#ifdef SYMBIAN_DNS_PUNYCODE
+// CHostResolver::EnableIdn
+// ****************
+/**
+// @param None from the start of the message to the start of domain name
+// @returns
+// @li ETrue , if the IDN support is enabled
+// @li EFalse, if the IDN support is disabled
+*/
+// ************************
+TBool CHostResolver::IsIdnEnabled()
+/**
+ * IDN Support Enabler
+ * This implements the RHostResolver::EnableIdn from the application.
+ *
+ * @param aEnable Boolean variable
+ */
+ {
+ return (iEnableIdn == 1)? ETrue : EFalse;
+ }
+#endif //SYMBIAN_DNS_PUNYCODE
+
+// CHostResolver::Query
+// ********************
+void CHostResolver::Query(const TDesC8& aQuery, TDes8& aResult, TInt aCount)
+ /**
+ * Query handler.
+ * This implements the RHostResolver::Query and RHostResolver::Next() (for the query) from the application.
+ *
+ * @param aQuery The query
+ * @retval aResult The reply buffer
+ * @param aCount The next indicator (0 = first query, > 0 = next).
+ */
+ {
+ ASSERT(!IsPending()); // Cannot be in pending state!
+ iCurrentId = iNetworkId;
+ if (aQuery.Length() > 0)
+ {
+ (void)new (&iRequest) TDnsRequest(aQuery, aResult, aCount);
+ LOG(Log::Printf(_L("Query\tres HR[%u] SESSION %d Query(%d, %d, %d) NID=%d"),
+ this, Session(), aQuery.Length(), aResult.MaxLength(), aCount, iNetworkId));
+#ifdef SYMBIAN_DNS_PUNYCODE
+ if(this->iEnableIdn)
+ {
+ iRequest.iScope |= ENABLEIDN_SCOPE ;
+ }
+#endif //SYMBIAN_DNS_PUNYCODE
+ Submit();
+ }
+ else
+ {
+ LOG(Log::Printf(_L("Query\tres HR[%u]::BAD QUERY-ZERO LENGTH"), this));
+ iNotify->QueryComplete(KErrArgument);
+ }
+ }
+
+// CHostResolver::SecurityCheck
+// ****************************
+TInt CHostResolver::SecurityCheck(MProvdSecurityChecker *aChecker)
+ /**
+ * Check the security of RHostResolver user.
+ * This represents the host resolver of some application. Check if this application
+ * has the capability for network services. If it does not have the capability,
+ * it can only query information that can be resolved locally (e.g. hosts file).
+ */
+ {
+ iSecurityChecker = aChecker;
+ iHasNetworkServices = aChecker->CheckPolicy(KPolicyNetworkServices, 0) == KErrNone;
+ return KErrNone;
+ }