diff -r 000000000000 -r af10295192d8 tcpiputils/dnd/src/dns_sock.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tcpiputils/dnd/src/dns_sock.cpp Tue Jan 26 15:23:49 2010 +0200 @@ -0,0 +1,1365 @@ +// Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// dns_sock.cpp - the main client module of the DNS. +// + +#include +#include +#include +#include "dns_sock.h" +#include "message.h" +#include "inet6log.h" +#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY +#include +#endif + + +/** +* Max number of consecutive RunL() calls with error condition. +* When this limit is exceeded, the socket it closed. +*/ +const TUint KSocketErrorLimit = 20; + +//seeds cannot be negative number. Need mask to seed to positive number. +const TInt KNegativeNumberMask = -1; + +// +// TRequestQueue +// ************* +// +class TRequestQueue : public TDblQue + { +public: + // Construct an empty request queue + TRequestQueue(); + // Construct new request queue and MOVE all requests from another into it + TRequestQueue(TRequestQueue &); + // Remove the first request from a queue, return NULL if queue is empty + TDnsRequest *Remove(); + }; + + +TRequestQueue::TRequestQueue() + : TDblQue(_FOFF(TDnsRequest, iQueueLink)) + { + } + +TRequestQueue::TRequestQueue(TRequestQueue &aQueue) + : TDblQue(_FOFF(TDnsRequest, iQueueLink)) + { + if (aQueue.IsEmpty()) + Reset(); + else + { + iHead = aQueue.iHead; + iHead.iPrev->iNext = &iHead; + iHead.iNext->iPrev = &iHead; + } + aQueue.Reset(); + } + +TDnsRequest *TRequestQueue::Remove() + { + if (!IsEmpty()) + { + TDnsRequest *rq = First(); + rq->iQueueLink.Deque(); + return rq; + } + return NULL; + } + +class CDnsSocketReader; + +// CDnsSocketWriter +// **************** +// Internal "hidden" class to do all the work +class CDnsSocketWriter : public CActive + { + CDnsSocketWriter(CDnsSocket &aMaster); +public: + ~CDnsSocketWriter(); + void ConstructL(); + + // Create a basic (UDP) reader/writer (not activated) + static CDnsSocketWriter *NewL(CDnsSocket &aMaster, TInt aTTL); + // Create a TCP writer/reader (not activated) + static CDnsSocketWriter *NewTcpL(CDnsSocket &aMaster, const TInetAddr &aServer, TInt aTTL); + // Create a TCP listening writer/reader (not activated) + static CDnsSocketWriter *NewTcpListenL(CDnsSocket &aMaster, TInt aTTL); + + // Test if a address matches current connection + TBool Match(const TInetAddr &aServer) const; + + // (Re)Queue the request for processing with new ID + void Queue(TDnsRequest &aRequest, const TInt aId = -1); + // Cancel the request and dequeue it + void Remove(TDnsRequest &aRequest); + // Change the bind port (and address if specified) + void SetBind(const TInetAddr &aBind); + // Set hoplimit for for transmitted packets. + void SetHoplimit(const TInt aTTL); + // Open and activate the UDP socket for DNS traffic (unless already done) + TInt ActivateSocket(); + // Process socket errors + TInt HandleError(TInt aReason, const TInetAddr *aServer = NULL); + // Close and deactivate the UDP socket for DNS traffic + void DeactivateSocket(TInt aReason); + // Handle receive compeleted, keep receiver active + void RunReader(const TMsgBuf &aMsg, const TInetAddr &aFrom); + // Handle send completed, keep sender active (if work to do) + void RunWriter(TRequestStatus &aStatus); + //seed id generator + TInt64 SeedIdGenerator(TInt64 aSequence); + // Return a reference to the RSocket + inline RSocket &Socket() { return iSocket; } + inline TBool IsTCP() const { return iTCP != 0; } + inline TBool IsListen() const { return iListen != 0; } + inline TBool IsOpened() const { return iOpened; } + inline void ResetErrorCount() { iErrorCount = 0;} + // A link chain used by the CDnsSocket to keep track of multiple writers + CDnsSocketWriter *iNext; + +private: + void RunL(); + void DoCancel(); +#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY + TBool CanUseConnection(); +#endif + + CDnsSocket &iMaster; //< The connection to the CDnsSocket owning this + RSocket iSocket; //< The DNS socket +#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY + RConnection iAttachedConn; //< The connection on which the DNS socket may be opened on +#endif + TUint iDeactivateCount; //< Count number of deactivations (needed in reply processing) + TUint iErrorCount; //< Count number of consecutive errors in RunL (read and write) + TUint iListen; //< = 1, if socket is listening TCP socket, waiting on accept. + TUint iTCP; //< = 1, if socket is a connected TCP socket + TUint iOpened:1; //< = 1, if the socket is open + TUint iRunWriter:1; //< = 1, if executing RunWriter (callback safeguard) + TUint iRunReader:1; //< = 1, if executing RunReader (callback safeguard) + TInt iTTL; //< The TTL/Hoplimit to be used + TInt64 iSequence; //< Used in computing the ID value for the queries + TBuf8 iOutMsg; //< For sending the outgoing message + TInetAddr iBind; //< Address/Port to bind the socket. + TInetAddr iTo; //< Filled with the destination address of the query + TRequestQueue iSendQueue; //< Requests waiting to be sent + TRequestQueue iWaitQueue; //< Requests waiting for a reply + TDnsRequest *iSending; //< The request currently being sent, if non-NULL + CDnsSocketReader *iReader; //< The reader object + }; + +void TDnsRequest::Cancel() + { + CDnsSocketWriter *const writer = iQueueLink.Writer(); + if (writer) + writer->Remove(*this); + } + + +// CDnsSocketReader +// **************** +// Internal "hidden" class for hanlding asynchronous reading +class CDnsSocketReader : public CActive + { + friend class CDnsSocketWriter; +public: + CDnsSocketReader(CDnsSocketWriter &aWriter); + ~CDnsSocketReader(); + void ConstructL(TUint aDnsMaxMessage); + void Activate(); + +private: + void RunL(); + void DoCancel(); + + CDnsSocketWriter &iWriter; + TUint iReadLength:1; + TInetAddr iFrom; //< Filled with source address of a received packet + HBufC8 *iInMsg; //< The real allocated buffer + TInt iAllocatedLength; //< The real max length of the buffer + TPtr8 iBuf; //< The current receive buffer + }; + +CDnsSocketWriter::CDnsSocketWriter(CDnsSocket &aMaster) : CActive(0), iMaster(aMaster) + { + LOG(Log::Printf(_L("CDnsSocketWriter[%u]::CDnsSocketWriter([%u])"), this, &aMaster)); + CActiveScheduler::Add(this); + + TTime seed; + seed.UniversalTime(); + iSequence = seed.Int64(); + } + +void CDnsSocketWriter::ConstructL() + { + iReader = new (ELeave) CDnsSocketReader(*this); + iReader->ConstructL(iMaster.iMaxDnsMessage); + } + + +CDnsSocketWriter *CDnsSocketWriter::NewL(CDnsSocket &aMaster, TInt aTTL) + { + CDnsSocketWriter *const writer = new (ELeave) CDnsSocketWriter(aMaster); + CleanupStack::PushL(writer); + writer->iTTL = aTTL; + writer->ConstructL(); + CleanupStack::Pop(); + return writer; + } + +CDnsSocketWriter *CDnsSocketWriter::NewTcpL(CDnsSocket &aMaster, const TInetAddr &aServer, TInt aTTL) + { + CDnsSocketWriter *const writer = NewL(aMaster, aTTL); + // Note: with TCP iTo is dummy! + writer->iTo = aServer; + writer->iTCP = 1; + return writer; + } + +CDnsSocketWriter *CDnsSocketWriter::NewTcpListenL(CDnsSocket &aMaster, TInt aTTL) + { + CDnsSocketWriter *const writer = NewL(aMaster, aTTL); + writer->iTCP = 1; + writer->iListen = 1; + return writer; + } + + + +CDnsSocketWriter::~CDnsSocketWriter() + { + // As this class is managed by CDnsSocket, it is impossible + // to get here if IsActive(), or with requests in any queue. + // (DeactivateSocket is ALWAYS called before destructor) + // + ASSERT(!IsActive()); + ASSERT(iSendQueue.IsEmpty()); + ASSERT(iWaitQueue.IsEmpty()); + + delete iReader; + + Cancel(); // should not be needed... + if (IsAdded()) + Deque(); +#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY + iAttachedConn.Close(); +#endif + LOG(Log::Printf(_L("CDnsSocketWriter[%u]::~CDnsSocketWriter()"), this)); + } + +TBool CDnsSocketWriter::Match(const TInetAddr &aServer) const + { + // The match applies only to connected TCP readers/writers + return iTCP && !iListen && aServer.CmpAddr(iTo) && aServer.Scope() == iTo.Scope(); + } + +// +// RunReader +// +void CDnsSocketWriter::RunReader(const TMsgBuf &aMsg, const TInetAddr &aFrom) + { + if (iRunReader) + return; + iRunReader = 1; +#ifdef _LOG + { + TBuf<50> tmp; + aFrom.OutputWithScope(tmp); + Log::Printf(_L("CDnsSocketWriter[%u]::RunReader() read %d bytes from %S#%d"), this, (TInt)aMsg.Length(), + &tmp, aFrom.Port()); + } +#endif + if (aMsg.Length() >= TInt(sizeof(TDndHeader))) + { + const TDndHeader &hdr = aMsg.Header(); + const TUint16 id = (TUint16)hdr.ID(); + + if (hdr.QR()) + { + // + // This is a reply message, match it with + // a waiting request, if any... + // + // Things get somewhat tricky, because almost anything + // can happen inside the Reply (like DeactivateSocket, + // ActivateSocketL, removal of any requests, requeing + // for sent, etc... + // Grab the current list into separate list. Note, that + // Query or Reply callbacks are allowed to remove entries + // from this temporary queue (the TDnsRequest::Cancel and + // CDnsSocket::Redmove() still work correctly--they don't + // care which chain the request belongs!), + TRequestQueue reply(iWaitQueue); + TUint mark = iDeactivateCount; + TDnsRequest *rq; + for (;;) + { + rq = reply.Remove(); + if (rq == NULL) + { + // Didn't match any request, punt it into Query() + iMaster.Query(aMsg, aFrom, iSocket); + break; + } + iWaitQueue.AddLast(*rq); + if (rq->iId == id && rq->Reply(iMaster, aMsg, aFrom)) + break; + } + if (iDeactivateCount == mark) + { + // + // No socket shutdown occurred, just reinsert remaining requests + // + while ((rq = reply.Remove()) != NULL) + iWaitQueue.AddLast(*rq); + } + else + { + // + // Socket was shut within Reply, all requests + // should have been cancelled then... + // + while ((rq = reply.Remove()) != NULL) + { + rq->iQueueLink.SetWriter(NULL); + rq->Abort(iMaster, KErrCancel); + } + } + } + else + { + // + // Not a reply message, let the derived + // class decide on how to deal with it. + // + iMaster.Query(aMsg, aFrom, iSocket); + } + } + + iRunReader = 0; + if (iOpened ) // still open for business? + { + // Start a new read + iReader->Activate(); + } + } + +void CDnsSocketWriter::Queue(TDnsRequest &aRequest, TInt aId) + { + aRequest.Cancel(); + // + // Assign a new random ID for the request + // + if (aId < 0) + { + TInt64 seed = SeedIdGenerator(iSequence); + if(seed<0) + seed = seed *(KNegativeNumberMask); + aRequest.iId = (TUint16)(Math::Rand(seed) >> 8); + } + else + aRequest.iId = (TUint16)aId; + aRequest.iQueueLink.SetWriter(this); + + iSendQueue.AddLast(aRequest); + LOG(Log::Printf(_L("\t\tDNS session [%u] Queued for send with ID=%d"), (TInt)&aRequest, (TInt)aRequest.iId)); + if (!IsActive()) + RunWriter(iStatus); + } + + +void CDnsSocketWriter::SetBind(const TInetAddr &aBind) + { + iBind = aBind; + } + +void CDnsSocketWriter::SetHoplimit(const TInt aTTL) + { + if (aTTL != iTTL) + { + iTTL = aTTL; + iSocket.SetOpt(KSoIp6UnicastHops, KSolInetIp, iTTL); + iSocket.SetOpt(KSoIp6MulticastHops, KSolInetIp, iTTL); + } + } + +TInt CDnsSocketWriter::ActivateSocket() + { + if (iOpened) + return KErrNone; // Nothing to do if already opened + + iErrorCount = 0; +#ifndef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY + TInt err = + iListen ? iSocket.Open(iMaster.iSS) : + iTCP ? iSocket.Open(iMaster.iSS, KAfInet, KSockStream, KProtocolInetTcp) : + iSocket.Open(iMaster.iSS, KAfInet, KSockDatagram, KProtocolInetUdp); +#else + TInt err = KErrNone; + if (iListen) + { + err = iSocket.Open(iMaster.iSS); + } + else + { + TUint addrFamily = iTCP ? KSockStream : KSockDatagram; + TUint protocol = iTCP ? KProtocolInetTcp : KProtocolInetUdp; + if (CanUseConnection()) + { + err = iSocket.Open(iMaster.iSS, KAfInet, addrFamily, protocol, iAttachedConn); + } + else + { + err = iSocket.Open(iMaster.iSS, KAfInet, addrFamily, protocol); + } + } +#endif +#ifdef _LOG + // Initialize for LOG prints + TBuf<50> tmp; + iBind.OutputWithScope(tmp); +#endif + if (err == KErrNone) + { + if (iListen || (err = iSocket.Bind(iBind)) == KErrNone) + { + iOpened = 1; + if (iListen) + { + LOG(Log::Printf(_L("CDnsSocketWriter[%u]::ActivateSocketL() listening %S#%d"), this, &tmp, iBind.Port())); + iMaster.iListener.Accept(iSocket, iStatus); + SetActive(); + } + else + { + iSocket.SetOpt(KSoIp6UnicastHops, KSolInetIp, iTTL); + iSocket.SetOpt(KSoIp6MulticastHops, KSolInetIp, iTTL); + if (iTCP) + { +#ifdef _LOG + TBuf<50> dst; + iTo.OutputWithScope(dst); + Log::Printf(_L("CDnsSocketWriter[%u]::ActivateSocketL() TCP connect src=%S#%d dst=%S#%d"), this, + &tmp, iBind.Port(), + &dst, iTo.Port()); +#endif + iSocket.Connect(iTo, iStatus); + SetActive(); + } + else + { + LOG(Log::Printf(_L("CDnsSocketWriter[%u]::ActivateSocketL() UDP %S#%d"), this, &tmp, iBind.Port())); + iSocket.SetOpt(KSoUdpReceiveICMPError, KSolInetUdp, 1); + if (!iRunReader) + iReader->Activate(); + } + } + return KErrNone; + } + iSocket.Close(); +#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY + iAttachedConn.Close(); +#endif + } + // Should probably be logged in release too! + LOG(Log::Printf(_L("CDnsSocketWriter[%u]::ActivateSocketL() bind=%S#%d (or open) failed %d"), this, &tmp, iBind.Port(), err)); + return err; + } + + +/** +* Decides on action when the socket returns an error status. Not all +* errors are fatal errors, which require a reset of the socket. +* +* @param aReason the error code from the socket (iStatus) +* @param aServer the dns server address, if known (only for Send errors) +* +* @return +* @li = 1, Error fully processed, Don't activate anything +* @li = 0, Error handled, keep CActive running +*/ +TInt CDnsSocketWriter::HandleError(TInt aReason, const TInetAddr *aServer) + { + if (++iErrorCount > KSocketErrorLimit) + { + // Too many consecutive errors, deactivate to reset + LOG(Log::Printf(_L("CDnsSocketWriter[%u]::HandleError(%d) Too many (%d) consecutive errors"), this, aReason, iErrorCount)); + DeactivateSocket(aReason); + return 1; + } + if (IsTCP()) + { + // Any error on TCP socket means that it is "dead", + // and we must close the socket to recover! + DeactivateSocket(KErrDndServerUnusable); + return 1; + } + // The error state should automaticly clear with datagram + // sockets, but in some releases it does not. Issue a + // dummy KSOSelectLastError, which in some cases also + // clears the error code from socket server. (Note: the + // option parameter is dummy, the error is actually + // returned as a return value of GetOpt, very bizarre + // -- msa) + TInt socket_err = iSocket.GetOpt(KSOSelectLastError, KSOLSocket, socket_err); + (void)socket_err; // shut down compiler warning about unreference variable. + + if (aServer == NULL) + { + // The only errors on receive side, that we can do something about, are + // the ICMP errors for some server (other errors are ignored). + // Try to find out the server. + TPckgBuf opt; + if (iSocket.GetOpt(KSoInetLastError, KSolInetIp, opt) == KErrNone) + { +#ifdef _LOG + TBuf<50> src; + TBuf<50> dst; + TBuf<50> err; + opt().iSrcAddr.OutputWithScope(src); + opt().iDstAddr.OutputWithScope(dst); + opt().iErrAddr.OutputWithScope(err); + Log::Printf(_L("CDnsSocketWriter[%u]::HandleError(%d) last=%d, icmp(%d,%d) src=%S dst=%S err=%S"), + this, aReason, opt().iStatus, opt().iErrType, opt().iErrCode, &src, &dst, &err); +#endif + if (opt().iStatus != aReason) + return 0; // Error does not match the ICMP last error, just ignore. + + aServer = &opt().iDstAddr; + } + else + { + DeactivateSocket(KErrDndServerUnusable); + return 1; + } + } + + // aServer possibly identifies a DNS server with which + // we have some communication problems. Declare this + // server unusable for all queries currently using it. + const TUint mark = iDeactivateCount; + iMaster.Error(*aServer, aReason); + // If shutdown occurred within above call, then + // this return must not try to reactivate the + // reader or writer. + return (mark != iDeactivateCount); + } + +void CDnsSocketWriter::DeactivateSocket(TInt aReason) + { + LOG(Log::Printf(_L("CDnsSocketWriter[%u]::DeactivateSocket() Entry"), this)); + if (!iOpened ) + { + LOG(Log::Printf(_L("CDnsSocketWriter[%u]::DeactivateSocket() return without action"), this)); + return; // Nothing to do if not open + } + // Grab current set of requests away, so that + // potentially newly entered requests won't + // get affected by this... + // + TRequestQueue send(iSendQueue); + TRequestQueue wait(iWaitQueue); + // + // Cancel all activity + // +// LOG(Log::Printf(_L("CDnsSocketWriter[%u]::DeactivateSocket() iReader->Cancel()"), this)); + iReader->Cancel(); +// LOG(Log::Printf(_L("CDnsSocketWriter[%u]::DeactivateSocket() Cancel()"), this)); + Cancel(); +// LOG(Log::Printf(_L("CDnsSocketWriter[%u]::DeactivateSocket() iSocket.Close()"), this)); + // + // Shut down the sessions + // + iOpened = 0; + iSocket.Close(); + iDeactivateCount += 1; +#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY + iAttachedConn.Close(); +#endif + // + // Remove and abort all requests + // + TDnsRequest *rq; + while ((rq = send.Remove()) != NULL) + { + rq->iQueueLink.SetWriter(0); + rq->Abort(iMaster, aReason); + } + while ((rq = wait.Remove()) != NULL) + { + rq->iQueueLink.SetWriter(0); + rq->Abort(iMaster, aReason); + } + LOG(Log::Printf(_L("CDnsSocketWriter[%u]::DeactivateSocket() complete"), this)); + } + +// +// CDnsSocketWriter::Remove +// ************************ +void CDnsSocketWriter::Remove(TDnsRequest &aQuery) + { + // + // Must not be called for request that is not queued + // + ASSERT(aQuery.iQueueLink.Writer() == this); + aQuery.iQueueLink.Deque(); + aQuery.iQueueLink.SetWriter(0); + + if (iSending == &aQuery) + { + iSending = NULL; + // If IsActive() == FALSE, then this Remove is actually + // called from the Build() callback! + if (IsActive()) + { + Cancel(); + // There can be other queries waiting + // for writer.. + RunWriter(iStatus); + } + } + } + +void CDnsSocketWriter::RunL() + { + const TInt result = iStatus.Int(); + LOG(Log::Printf(_L("--> CDnsSocketWriter[%u]::RunL() -start- iStatus=%d"), this, result)); +#ifdef _LOG + TBuf<50> tmp; + iTo.OutputWithScope(tmp); +#endif + for (;;) // ...just for easy exits.. + { + if (result) + { + LOG(Log::Printf(_L("CDnsSocketWriter[%u]::RunL() Error on %S#%d"), this, &tmp, iTo.Port())); + } + else + ResetErrorCount(); + if (iListen) + { + // + // Keep listening active, allocate a new reader/writer to listen + // + CDnsSocketWriter *writer = NULL; + TRAPD(err, + writer = CDnsSocketWriter::NewTcpListenL(iMaster, iTTL); + err = iMaster.AddSecondaryWriter(writer)); + if (err != KErrNone) + { + // Cannot start new accept for some reason, reject the current accept and + // reinitialize the current writer to wait a new accept instead + iMaster.DeleteWriter(writer, err); // <-- safe to call with writer == NULL + // Reactivate this socket for listening instead (iListen remains set) + LOG(Log::Printf(_L("CDnsSocketWriter[%u]::RunL() Failed (%d) to activate new Accept() to=%S#%d"), + this, err, &tmp, iTo.Port())); + DeactivateSocket(err); + ActivateSocket(); + break; + } + // Succesful Accept, update iTo to the address and port of the + // remote end. + iListen = 0; + iSocket.RemoteName(iTo); + LOG(iTo.OutputWithScope(tmp)); + LOG(Log::Printf(_L("CDnsSocketWriter[%u]::RunL() Listen accepted to=%S#%d"), this, &tmp, iTo.Port())); + if (!iReader->IsActive()) + iReader->Activate(); + } + if (iSending) + { + LOG(Log::Printf(_L("\t\tDNS session [%u] Sent to=%S#%d ID=%d"), iSending, &tmp, iTo.Port(), (TInt)iSending->Id())); + TDnsRequest *query = iSending; + iSending = NULL; + if (result == KErrNone) + query->Sent(iMaster); + else if (HandleError(result, &iTo)) + break; + } + else if (result && HandleError(result)) // Error that cannot be associated with specific sending (does it ever happen?) + break; + // Keep writer busy, if not already active. + if (!IsActive()) + RunWriter(iStatus); + break; // ** NOT REAL LOOP, ALWAYS EXIT AT END ** + } + LOG(Log::Printf(_L("<-- CDnsSocketWriter[%u]::RunL() -exit- iStatus=%d"), this, iStatus.Int())); + } + +void CDnsSocketWriter::RunWriter(TRequestStatus &aStatus) + { + if (iRunWriter) + return; // This is a callback from Build, return + iRunWriter = 1; + // + // Pick next query to be served + // **************************** + // + iSending = iSendQueue.Remove(); + while ( iSending != NULL) + { + iWaitQueue.AddLast(*iSending); + // Before calling Build, the message is reset into + // initial state: + TPtr8 payload((TUint8 *)iOutMsg.Ptr(), iOutMsg.MaxLength()); + if (iTCP) + payload.Set((TUint8 *)payload.Ptr()+2, 0, payload.MaxLength()-2); + TMsgBuf &buf = TMsgBuf::Cast(payload); + buf.SetLength(sizeof(TDndHeader)); + buf.Header().Init(iSending->iId); + // If Build fails, request(s) are simply moved + // into iWaitQueue to wait for further actions. + // (If iSending becomes NULL in Build, then Build has Removed the + // request--do not start write) + // Note: if receive buffer > KDnsMaxMessage, then it requests EDNS0. Don't + // request EDNS0, if TCP is being used (report receive buffer = 0). + if (iSending->Build(iMaster, buf, iTo, iTCP ? 0 : iReader->iAllocatedLength) && iOpened && iSending) + { + const TInt len = buf.Length(); + if (iTCP) + { + // Patch in the length + TUint8 *const p = (TUint8 *)iOutMsg.Ptr(); + p[1] = (TUint8)len; + p[0] = (TUint8)(len >> 8); + iOutMsg.SetLength(len+2); + } + else + { + iOutMsg.SetLength(len); + } +#ifdef _LOG + TBuf<50> tmp; + iTo.OutputWithScope(tmp); + Log::Printf(_L("\t\tDNS session [%u] Send to=%S#%d %d bytes (TCP=%d)"), iSending, &tmp, iTo.Port(), iOutMsg.Length(), (TInt)iTCP); +#endif + if (iTCP) + { + iSocket.Write(iOutMsg, aStatus); + if (!iReader->IsActive()) + iReader->Activate(); + } + else + iSocket.SendTo(iOutMsg, iTo, 0, aStatus); + SetActive(); + break; + } + } + iRunWriter = 0; + } + +void CDnsSocketWriter::DoCancel() + { + LOG(Log::Printf(_L("CDnsSocketWriter[%u]::DoCancel()"), this)); + if (iListen) + { + iMaster.iListener.CancelAll(); + iListen = 0; + } + else + { + iSocket.CancelWrite(); + iSending = NULL; + } + } + +#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY +TBool CDnsSocketWriter::CanUseConnection() + { + TInt err = iAttachedConn.Open(iMaster.iSS); + if (KErrNone != err) + { + LOG(Log::Printf(_L("CDnsSocketWriter[%u]::CanUseConnection() error opening connection: %d"), this, err)); + return EFalse; + } + + TUint noConnections = 0; + err = iAttachedConn.EnumerateConnections(noConnections); + if (KErrNone != err) + { + LOG(Log::Printf(_L("CDnsSocketWriter[%u]::CanUseConnection() error enumerating connections: %d"), this, err)); + return EFalse; + } + LOG(Log::Printf(_L("CDnsSocketWriter[%u]::CanUseConnection() no of connections: %u"), this, noConnections)); + + if (noConnections > 0) + { + TPckgBuf info; + TConnectionInfo currentInfo; + TBool foundConnection = EFalse; + for(TInt i=1; i<=noConnections; i++) + { + if (KErrNone != iAttachedConn.GetConnectionInfo(i, info)) + { + return EFalse; + } + currentInfo = info(); + + LOG(Log::Printf(_L("CDnsSocketWriter[%u]::CanUseConnection() currentInfo.NetId = %d "), this, currentInfo.iNetId)); + LOG(Log::Printf(_L("CDnsSocketWriter[%u]::CanUseConnection() iMaster.iNetworkId = %d "), this, iMaster.iNetworkId)); + if(currentInfo.iNetId == iMaster.iNetworkId) + { + foundConnection = ETrue; + break; + } + } + + if ((foundConnection) && (KErrNone == iAttachedConn.Attach(info, RConnection::EAttachTypeNormal))) + { + LOG(Log::Printf(_L("CDnsSocketWriter[%u]::CanUseConnection() attached to existing connection at IAP: [%d] Network: [%d]"), this, info().iIapId, info().iNetId)); + return ETrue; + } + else + { + LOG(Log::Printf(_L("CDnsSocketWriter[%u]::CanUseConnection() could not find any connection matching the Network: [%d]"), this, iMaster.iNetworkId)); + return EFalse; + } + } + + return EFalse; + } +#endif + +// CDnsSocketReader +// **************** +CDnsSocketReader::CDnsSocketReader(CDnsSocketWriter &aWriter) : CActive(0), iWriter(aWriter), iBuf(0,0) + { + LOG(Log::Printf(_L("CDnsSocketReader[%u]::CDnsSocketReader()"), this)); + CActiveScheduler::Add(this); + } + +void CDnsSocketReader::ConstructL(TUint aDnsMaxMessage) + { + // + // Any reader MUST have a buffer of capacity at least KDnsMaxMessage + // octets. If this cannot be allocated, cancel the reader (leave!). + // + const TUint len = aDnsMaxMessage > (TUint)KDnsMaxMessage ? aDnsMaxMessage : KDnsMaxMessage; + iInMsg = HBufC8::NewMaxL(len); + iAllocatedLength = len; + } + +CDnsSocketReader::~CDnsSocketReader() + { + ASSERT(!IsActive()); + delete iInMsg; + if (IsAdded()) + Deque(); + LOG(Log::Printf(_L("CDnsSocketReader[%u]::~CDnsSocketReader()"), this)); + } + +void CDnsSocketReader::Activate() + { + if (IsActive()) + return; // Do nothing, if already active! + + if (iWriter.IsTCP()) + { + LOG(Log::Printf(_L("CDnsSocketReader[%u]::Activate() TCP"), this)); + // Initialize iFrom for connected socket, if not yet done. + // (only for debugging purposes, no other functionality at momemnt) + if (iFrom.IsUnspecified()) + iWriter.Socket().RemoteName(iFrom); + iReadLength = 1; + iBuf.Set((TUint8 *)iInMsg->Ptr(), 0, 2); + iWriter.Socket().Read(iBuf, iStatus); + } + else + { + LOG(Log::Printf(_L("CDnsSocketReader[%u]::Activate() UDP"), this)); + iReadLength = 0; + iBuf.Set(iInMsg->Des()); + iWriter.Socket().RecvFrom(iBuf, iFrom, 0, iStatus); + } + SetActive(); + } + + +void CDnsSocketReader::RunL() + { + LOG(Log::Printf(_L("--> CDnsSocketReader[%u]::RunL() -start- iStatus=%d"), this, iStatus.Int())); + if (iStatus.Int()) + { + LOG(Log::Printf(_L("CDnsSocketReader[%u]::RunL() error=%d"), this, iStatus.Int())); + if (iWriter.HandleError(iStatus.Int()) == 0) + { + // Need to re-issue previous request + if (iWriter.IsTCP()) + iWriter.Socket().Read(iBuf, iStatus); + else + iWriter.Socket().RecvFrom(iBuf, iFrom, 0, iStatus); + LOG(Log::Printf(_L("CDnsSocketReader[%u]::RunL() soft error, restarted last read (TCP=%d)"), this, (TInt)iWriter.IsTCP())); + SetActive(); + } + } + else if (iReadLength) + { + iWriter.ResetErrorCount(); + iReadLength = 0; + const TUint8 *p = (TUint8 *)iInMsg->Ptr(); + const TInt len = p[0] << 8 | p[1]; + if (iAllocatedLength < len) + { + // Oops, the current buffer is too short for the packet. + delete iInMsg; + iAllocatedLength = len; + iInMsg = HBufC8::New(len); + } + if (iInMsg == NULL) + iWriter.DeactivateSocket(KErrNoMemory); + else + { + iBuf.Set((TUint8 *)iInMsg->Ptr(), 0, len); + iWriter.Socket().Read(iBuf, iStatus); + SetActive(); + } + } + else + { + iWriter.ResetErrorCount(); + iWriter.RunReader(TMsgBuf::Cast(iBuf), iFrom); + } + LOG(Log::Printf(_L("<-- CDnsSocketReader[%u]::RunL() -exit- iStatus=%d"), this, iStatus.Int())); + } + +void CDnsSocketReader::DoCancel() + { + LOG(Log::Printf(_L("CDnsSocketReader[%u]::DoCancel()"), this)); + iWriter.Socket().CancelRead(); + } + +// CDnsSocket +// ********** +// +CDnsSocket::CDnsSocket(TUint aMaxDnsMessage) : iMaxDnsMessage(aMaxDnsMessage) + { + LOG(Log::Printf(_L("CDnsSocket[%u]::CDnsSocket()"), this)); + } + +void CDnsSocket::ConstructL() + { + // Allocate the primary reader/writer for UDP + iWriter = CDnsSocketWriter::NewL(*this, -1); + iWriter->iNext=NULL; + } + +CDnsSocket::~CDnsSocket() + { + DeactivateSocket(KErrCancel); + + while (iWriter) + { + CDnsSocketWriter *const writer = iWriter; + iWriter = iWriter->iNext; + delete writer; + } + LOG(Log::Printf(_L("CDnsSocket[%u]::~CDnsSocket() completed"), this)); + } + +RSocket &CDnsSocket::Socket() + { + return iWriter->Socket(); + } + +/** +// Open the DNS socket and start listening incoming +// packets. The socket is bound to current "bind" +// address and port, which default to NONE and 0. +// +// Does nothing, if socket is already active +// +// Leaves, if socket cannot be activated +*/ +void CDnsSocket::ActivateSocketL(TUint aNetworkId) + { + LOG(Log::Printf(_L("CDnsSocket[%u]::ActivateSocketL([NetId = %d])"), this, aNetworkId)); + + if (iConnected && IsOpened()) + { + LOG(Log::Printf(_L("CDnsSocket[%u]::ActivateSocketL([NetId = %d]) return without action"), this, aNetworkId)); + return; // Already connected, nothing to do + } + + User::LeaveIfError(iSS.Connect()); + iConnected = 1; + iNetworkId = aNetworkId; + + const TInt ret = iWriter->ActivateSocket(); + if (ret != KErrNone) + { + DeactivateSocket(ret); + User::Leave(ret); + } + } + +/** +// Change the current bind address and activate the +// DNS socket. If socket is already active, the bind +// address is set, but will only take effect after +// deactivation and next activate. +// +// Does nothing, if socket is already active. +// +// Leaves if socket cannot be activated. +// +// @param aBind specify the address and port for +// received packets (also the source +// address and port for any sent +// messages). +*/ +void CDnsSocket::ActivateSocketL(const TInetAddr &aBind) + { +#ifdef _LOG + TBuf<50> tmp; + aBind.OutputWithScope(tmp); + Log::Printf(_L("CDnsSocket[%u]::ActivateSocketL([%S#%d])"), this, &tmp, aBind.Port()); +#endif + iWriter->SetBind(aBind); + ActivateSocketL(); + } + +/** +// Activate the TCP listening socket. +// +// This will automaticly cancel any previsous listens. +// +// Leaves if listening cannot be activated. +// +// @param aBind specify the address and port for +// received connections. +// @param aTTL specify the TTL for accepted connections +*/ +void CDnsSocket::ActivateListenL(const TInetAddr &aBind, TInt aTTL = -1) + { +#ifdef _LOG + TBuf<50> tmp; + aBind.OutputWithScope(tmp); + Log::Printf(_L("CDnsSocket[%u]::ActivateListenL([%S#%d])"), this, &tmp, aBind.Port()); +#endif + ActivateSocketL(); // First, make sure normal socket is up and running. + // + // Abort pending listens, if any + // + CDnsSocketWriter **head = &iWriter->iNext; + while (*head != NULL) + { + CDnsSocketWriter *writer = *head; + if (writer->IsListen()) + { + *head = writer->iNext; + // Note: IsListen() writers should never have any + // queued requests, so no Abort() callbacks will + // occur and thus, we don't need to worry about + // re-entries into CDnsSocket via callbacks! + writer->DeactivateSocket(KErrCancel); + delete writer; + } + else + head = &writer->iNext; + } + + if (iListening) + iListener.Close(); + TInetAddr bind(aBind); // Needed only because RSocket::Bind wants non-const argument! ARRGHH! + User::LeaveIfError(iListener.Open(iSS, KAfInet, KSockStream, KProtocolInetTcp)); + iListening = 1; // Socket opened. + User::LeaveIfError(iListener.Bind(bind)); + (void)iListener.SetOpt(KSoIp6UnicastHops, KSolInetIp, aTTL); + (void)iListener.SetOpt(KSoUserSocket, KSolInetIp, 0); + User::LeaveIfError(iListener.Listen(10)); + + // + // Create and append a new socket writer waiting for listen accept + // + (void)AddSecondaryWriter(CDnsSocketWriter::NewTcpListenL(*this, aTTL)); + } + +/** +// Close the socket and socket server session, if they +// were open. Abort all queued requests with the specifiec +// reason. +// +// @param aReason +// the abort reason that is passed to any requests which +// are removed from the system (see TDnsRequest::Abort) +// +*/ +void CDnsSocket::DeactivateSocket(TInt aReason) + { + // Need to grab the list away, because + // re-activation might occur via callbacks. + // + CDnsSocketWriter *writer = iWriter->iNext; + iWriter->iNext = NULL; + + iWriter->DeactivateSocket(aReason); + while (writer) + { + CDnsSocketWriter *tmp = writer; + writer = tmp->iNext; + tmp->DeactivateSocket(aReason); + delete tmp; + } + // Oops... Should not close if re-activated ---FIX! + if (iListening) + { + iListening = 0; + iListener.Close(); + } + + // Oops... Should not close if re-activated ---FIX! + if (iConnected) + { + iConnected = 0; + iSS.Close(); + } + } + +void CDnsSocket::SetHoplimit(const TInt aTTL) + /** + * Set TTL for for transmitted packets. + * + * The socket must be activated (open) when this is called. The set TTL + * is used as is for both unicast and multicast. The value -1 selects + * the system defaults. + * + * The set TTL will remain in effect for the primary writer until + * changed again. + * + * @param aTTL The TTL + */ + { + iWriter->SetHoplimit(aTTL); + } + +/** +// Queue a request for sending. The request may get following +// "callbacks": +// @li TDnsRequest::Build, +// just before a request is ready to be sent, build +// the outgoing packet into specified message buffer +// @li TDnsRequest::Sent, +// the packet has been sent to the interface +// @li TDnsRequest::Reply, +// a DNS reply packet matching the request id has +// been received. Need to test whether rest of the +// packet matches the request, and if so, handle +// it. +// @li TDnsRequest::Abort, +// request is being aborted (usually DNS socket +// is being deactivated). +// +// @param aRequest the request to be queued +// @param aId the request id +// @li -1 (< 0), +// the default if parameter is omitted. The queue +// method will assign random id automatically. +// @li >= 0, +// 16 bits of this value is used as id. +*/ +void CDnsSocket::Queue(TDnsRequest &aRequest, const TInt aId) + { + iWriter->Queue(aRequest, aId); + } + + +// Queue a request for sending with a specific socket +// +TInt CDnsSocket::Queue(TDnsRequest &aRequest, const RSocket &aSocket, const TInt aId) + { + for (CDnsSocketWriter *writer = iWriter; writer != NULL; writer = writer->iNext) + { + if (&aSocket == &writer->Socket()) + { + // Found it! + writer->Queue(aRequest, aId); + return KErrNone; + } + } + return KErrNotFound; + } + + +/** +// Queue a request for sending with TCP. +// +// @param aRequest to be queued +// @param aServer The address and port of the DNS server +// @param aId of the request, if >= 0. If < 0, then a new random ID is generated +// @param aTTL of the connection (= -1, the default, requests the system default). This is +// only effective if the connection is created. +// +// @return KErrNone, if queued successfully, or error code if failed. +*/ +TInt CDnsSocket::Queue(TDnsRequest &aRequest, const TInetAddr &aServer, const TInt aId, const TInt aTTL) + { + // + // Locate or create connected Socket reader/writer instance + // + CDnsSocketWriter *writer = iWriter->iNext; + while (writer != NULL) + { + if (writer->Match(aServer)) + { + // A writer already exists + writer->Queue(aRequest, aId); + return KErrNone; + } + else + { + //iWriter = iWriter->iNext; + writer = writer->iNext; + //do we need to return? + } + } + // + // Create and append a new connected socket writer + // + TRAPD(err, writer = CDnsSocketWriter::NewTcpL(*this, aServer, aTTL)); + if (writer) + { + err = AddSecondaryWriter(writer); + if (err == KErrNone) + writer->Queue(aRequest, aId); + else + DeleteWriter(writer, err); + } + return err; + } + +/** +// Abort whatever the request is currently doing and reque +// it for new send (eventually, a call to DnsRequest::Build +// should happen). This preserves the old id of the request. +// +// If a request is not currently queued, this does an implicit +// Queue. (a new id is generated). +// +// @param aRequest the request to be resent. +*/ +void CDnsSocket::ReSend(TDnsRequest &aRequest) + { + Queue(aRequest, aRequest.IsQueued() ? aRequest.Id() : -1); + } + +/** +// Add and activate a new secondary writer/reader unit. +// +// Insert a newly created and constructed reader/writer unit +// to the system and activate it. The secondearies are inserted +// into the iWriter chaing after the first primary unit, which +// always exists, as long as CDnsSocket exists. +// +// @param aWriter The reader/writer instance +// +// @return KErrNone, if succesfully activated, and an error code otherwise +*/ +TInt CDnsSocket::AddSecondaryWriter(CDnsSocketWriter *aWriter) + { + ASSERT(iWriter != NULL); // Primary must always exist! + ASSERT(aWriter != NULL); // Silly caller, program error + if (aWriter == NULL) // Test again (for release version) + return KErrArgument; // Should really not happen! + ASSERT(aWriter->iNext == NULL); // The new writer should be just created + + // Insert the new unit after the primary reader/writer, and + // before all previous secondary units (as the placement in + // chain should not matter--this is just simplest to do). + aWriter->iNext = iWriter->iNext; + iWriter->iNext = aWriter; + return aWriter->ActivateSocket(); + } + +/** +// Deactivate and delete a writer instance. +// +// Deactivate the writer, remove it from the list of writers +// (if present), and delete it. +// +// @param aWriter The reader/writer to be deleted +// @param aReason The reason (passed to any requests that are aborted because of this) +*/ +void CDnsSocket::DeleteWriter(CDnsSocketWriter *aWriter, TInt aReason) + { + // Allow call with NULL, as normal delete does + if (aWriter == NULL) + return; + + // Primary writer must not be deleted with this function! + ASSERT(aWriter != iWriter); + if (aWriter != iWriter) + return; + + // Remove instance from the writer chain (allow + // case where the aWriter is not in the chain!) + + CDnsSocketWriter **head = &iWriter->iNext; + while (*head != NULL) + { + CDnsSocketWriter *const writer = *head; + if (writer == aWriter) + { + *head = writer->iNext; + break; + } + else + head = &writer->iNext; + } + + // Note: Deactivate call may cause the CDnsSocket to be deleted, + // thus it is important that this no longer references it and + // that the writer has already been removed from the list before + // this! + aWriter->DeactivateSocket(aReason); + delete aWriter; // a detached instance can now be deleted + } + + +TBool CDnsSocket::IsOpened() const + { + return iWriter ? iWriter->IsOpened() : EFalse; + } + +TInt64 CDnsSocketWriter::SeedIdGenerator(TInt64 aSequence) +/* +* Function to generate seed for which will be passed to generate another level of sequence number +* The requirement came due the fact that microsoft DNS sequence where gussed. +* Seed generation is divided into two levels of generation. +*/ + { + TInt64 operand = Math::Random() % 5; + + //Generation of random number secure ID which should be mathematically operated with the + //Seed. The generation will make guess difficult. Secure ID should not be negative so there is + //an extra check done to make it positive number. + TInt64 secureId; + secureId = Math::Random(); + if(secureId < 0) + secureId = secureId * (KNegativeNumberMask); + + switch(operand) + { + case 1: + return (secureId + aSequence); + + case 2: + return (aSequence - secureId); + + default: + return (secureId * aSequence); + + };//end of switch + }//end of function