networkprotocols/tcpipv4v6prt/src/udp_sap.cpp
changeset 0 af10295192d8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/networkprotocols/tcpipv4v6prt/src/udp_sap.cpp	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,551 @@
+// Copyright (c) 2006-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:
+// udp_sap.cpp - UDP service access point
+// UDP service access point
+//
+
+
+
+/**
+ @file udp_sap.cpp
+*/
+
+#include "udp.h"
+#include "inet6log.h"
+#include <in6_opt.h>
+#include <nifman_internal.h>
+
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
+#include <in_sock_internal.h>
+#endif
+
+CProviderUDP6::CProviderUDP6(CProtocolInet6Base* aProtocol)
+	: CProviderInet6Transport(aProtocol)
+	{
+	__DECLARE_NAME(_S("CProviderUDP6"));
+	iProtocolId = KProtocolInetUdp;
+	}
+
+CProviderUDP6::~CProviderUDP6()
+	{
+	iProtocol->UnbindProvider(this);
+	iSockInQ.Free();
+	iSockOutBuf.Free();
+	}
+
+
+void CProviderUDP6::InitL()
+	{
+	CProviderInet6Transport::InitL();
+	iFlow.SetProtocol(KProtocolInetUdp);
+	iFlow.SetNotify(this);
+	iSockInQLen = 0;
+	iSockInBufSize = Protocol()->RecvBuf();
+	}
+
+void CProviderUDP6::Ioctl(TUint aLevel, TUint aName, TDes8* aOption)
+	{
+	// LOG provided by the base class
+	CProviderInet6Transport::Ioctl(aLevel, aName, aOption);
+	}
+
+
+void CProviderUDP6::CancelIoctl(TUint aLevel, TUint aName)
+	{
+	// LOG provided by the base class
+	CProviderInet6Transport::CancelIoctl(aLevel, aName);
+	}
+
+
+TInt CProviderUDP6::SetOption(TUint aLevel, TUint aName, const TDesC8& aOption)
+	{
+	TInt ret = KErrNotSupported;
+	TInt intValue = 0;
+
+	switch (aLevel)
+		{
+	case KSolInetUdp:
+		switch (aName)
+			{
+		case KSoUdpReceiveICMPError:
+			ret = GetOptionInt(aOption, intValue);
+			if (ret == KErrNone)
+				iSockFlags.iReportIcmp = intValue ? TRUE : FALSE;
+#ifdef _LOG
+			Log::Printf(_L("SetOpt\tudp SAP[%u] KSoUdpReceiveICMPError=%d err=%d"),
+				(TInt)this, (TInt)iSockFlags.iReportIcmp, ret);
+#endif
+			break;
+
+		case KSoUdpSynchronousSend:
+			ret = GetOptionInt(aOption, intValue);
+			if (ret == KErrNone)
+				iSynchSend = intValue ? TRUE : FALSE;
+#ifdef _LOG
+			Log::Printf(_L("SetOpt\tudp SAP[%u] KSoUdpSynchronousSend = %d err=%d"),
+				(TInt)this, iSynchSend, ret);
+#endif
+			break;
+			
+		case KSoUdpRecvBuf:
+			ret = GetOptionInt(aOption, intValue);
+			if (ret == KErrNone)
+				iSockInBufSize = intValue;
+#ifdef _LOG
+			Log::Printf(_L("SetOpt\tudp SAP[%u] KSoUdpRecvBuf = %d err=%d"),
+				(TInt)this, iSockInBufSize, ret);
+#endif
+			break;
+
+		case KSoUdpAddressSet:
+			ret = GetOptionInt(aOption, intValue);
+			if (ret == KErrNone)
+				iSockFlags.iAddressSet = intValue ? TRUE : FALSE;
+#ifdef _LOG
+			Log::Printf(_L("SetOpt\tudp SAP[%u] KSoUdpAddressSet = %d err=%d"),
+				(TInt)this, (TInt)iSockFlags.iAddressSet, ret);
+#endif
+			break;
+
+		default:
+			break;
+			}
+		break;
+
+
+	case KSolInetIp: 
+		{ 
+        if (aName == KSoReuseAddr) 
+        	{ 
+            ret = CheckPolicy(KPolicyNetworkControl, 0); 
+            if (ret == KErrNone) 
+      	      { 
+              TInt intValue; 
+              ret = GetOptionInt(aOption, intValue); 
+              if (ret == KErrNone) 
+      	        iSockFlags.iReuse = intValue ? TRUE : FALSE; 
+              } 
+#ifdef _LOG 
+            Log::Printf(_L("SetOpt\tudp SAP[%u] KSoReuseAddr = %d err=%d"), 
+      		               (TInt)this, (TInt)iSockFlags.iReuse, ret); 
+#endif 
+            return ret; // NOTE! We must not let CProviderInet6Transport handle this (security issue) 
+            } 
+        } 
+        break; 
+        
+	default:
+		break;
+		}
+
+	if (ret == KErrNotSupported)
+		ret = CProviderInet6Transport::SetOption(aLevel, aName, aOption);
+
+	return ret;
+	}
+
+
+TInt CProviderUDP6::GetOption(TUint aLevel, TUint aName, TDes8& aOption) const
+	{
+	TInt ret = KErrNotSupported;
+
+	switch (aLevel)
+		{
+	case KSOLSocket:
+		if (aName == KSOReadBytesPending)
+			ret = SetOptionInt(aOption, iSockInQ.IsEmpty() ? 0 : iSockInQ.First().Length() - iSockInQ.First().First()->Length());
+		break;
+	case KSolInetUdp:
+		switch (aName)
+			{
+		case KSoUdpReceiveICMPError:
+			ret = SetOptionInt(aOption, iSockFlags.iReportIcmp);
+			break;
+
+		case KSoUdpSynchronousSend:
+			ret = SetOptionInt(aOption, iSynchSend);
+			break;
+
+		case KSoUdpRecvBuf:
+			ret = SetOptionInt(aOption, iSockInBufSize);
+			break;
+
+		default:
+			break;
+			}
+		break;
+
+	default:
+		break;
+		}
+
+	if (ret == KErrNotSupported)
+		ret = CProviderInet6Transport::GetOption(aLevel, aName, aOption);
+
+	return ret;
+	}
+
+TInt CProviderUDP6::SetRemName(TSockAddr &aAddr)
+	{
+	TInt err;
+	TInetAddr addr = aAddr;
+
+	if (err = CProviderInet6Transport::SetRemName(addr), err == KErrNone)
+		{
+		if (iFlow.FlowContext()->LocalPort() == KInetPortNone)
+			CProviderInet6Transport::AutoBind();
+		if (iFlow.FlowContext()->LocalPort() != KInetPortNone)
+			err = iFlow.Connect();
+		else
+			{
+			iSockFlags.iConnected = EFalse;
+			iSockFlags.iReportIcmp = ETrue;
+			err = KErrInUse;
+			}
+		}
+
+	return (err < KErrNone) ? err : KErrNone;
+	}
+
+
+void CProviderUDP6::Shutdown(TCloseType aOption)
+	{
+	LOG(Log::Printf(_L("Shutdown\tudp SAP[%u] TCloseType=%d"), (TInt)this, aOption));
+	switch(aOption)
+		{
+	case EStopInput:
+		iSockFlags.iRecvClose = ETrue;
+		iSockInQ.Free();
+		iSockInQLen = 0;
+		iSocket->Error(KErrNone,MSocketNotify::EErrorClose);
+		Nif::SetSocketState(ENifSocketConnected, this);
+		break;
+
+	case EStopOutput:
+		iSockFlags.iSendClose = ETrue;
+		iSocket->Error(KErrNone,MSocketNotify::EErrorClose);
+		Nif::SetSocketState(ENifSocketConnected, this);
+		break;
+
+	case ENormal:
+		iSocket->CanClose();
+		break;
+
+	case EImmediate:
+		break;
+
+	default:
+		Panic(EInet6Panic_NotSupported);
+		break;
+		}
+	}
+
+
+//
+// PRTv1.0 API
+//
+void CProviderUDP6::GetData(TDes8 &aDesc, TUint aOptions, TSockAddr *aAddr)
+	{
+	TDualBufPtr buf(aDesc);
+	Recv(buf, aDesc.Length(), aOptions, aAddr);
+	}
+
+//
+// PRTv1.5 API
+//
+TInt CProviderUDP6::GetData(RMBufChain& aData, TUint aLength, TUint aOptions, TSockAddr* aAddr)
+	{
+	TDualBufPtr buf(aData);
+	return Recv(buf, aLength, aOptions, aAddr);
+	}
+
+static void FillUdpHeader(TInet6Checksum<TInet6HeaderUDP> &aPkt, RMBufSendPacket &aPacket, RMBufSendInfo &aInfo, TUint aOffset)
+	{
+	//
+	// Build UDP header
+	aPkt.iHdr->SetSrcPort(aInfo.iSrcAddr.Port());
+	aPkt.iHdr->SetDstPort(aInfo.iDstAddr.Port());
+	aPkt.iHdr->SetLength(aInfo.iLength);
+	// Compute checksum
+	aPkt.ComputeChecksum(aPacket, &aInfo, aOffset);
+	if (aPkt.iHdr->Checksum() == 0)  // Zero indicates "no checksum" in IPv4
+		aPkt.iHdr->SetChecksum(0xffff);
+	}
+
+TInt CProviderUDP6::DoWrite(RMBufSendPacket &aPacket, RMBufSendInfo &aInfo, TUint /*aOptions*/, TUint aOffset)
+	{
+	// Whenever a new packet to send arrives, the buffered packet, if any, is dropped!
+	// [Because the new packet may make the flow selectors different from the buffered packet]
+	LOG(if (!iSockOutBuf.IsEmpty()) Log::Printf(_L("\tudp SAP[%u] No space. Unsent packet DROPPED"), (TInt)this));
+	iSockOutBuf.Free();
+
+	if (aOffset == 0)
+		{
+		// When offset is 0, the packet is not using the KIpHeaderIncluded.
+		// The UDP header needs to be allocated in front of the payload.
+		TInt err = aPacket.Prepend(TInet6HeaderUDP::MinHeaderLength());
+		if (err != KErrNone)
+			return KErrNoMBufs;
+		aInfo.iLength += TInet6HeaderUDP::MinHeaderLength();
+		}
+	TInet6Checksum<TInet6HeaderUDP> pkt(aPacket, aOffset);
+	if (pkt.iHdr == NULL)
+		{
+		// Can happen if user uses KIpHeaderIncluded without UDP header, but
+		// may catch other unexpected problems with the RMBuf handling. In
+		// any case, this packet cannot be sent (no retries allowed).
+		return KErrInet6ShortPacket;
+		}
+	if (aOffset > 0)
+		{
+		// The packet is sent using KIpHeaderIncluded option, extract the UDP
+		// ports from the included header.
+		aInfo.iSrcAddr.SetPort(pkt.iHdr->SrcPort());
+		if (aInfo.iDstAddr.Port() == 0)
+			{
+			// aToAddr was not specified, or it had ZERO port. copy the port
+			// from the UDP header.
+			aInfo.iDstAddr.SetPort(pkt.iHdr->DstPort());
+			}
+		// Change the flow to match the included header! The header gives
+		// both source and destination, and will OVERRIDE any previous
+		// Bind() or Connect() for the socket.
+		//
+		// [Alternate: if socket is explicitly bound or connected,
+		// do not accept HeaderIncluded, and return error?]
+		// Should the port ranges be checked here or not?
+		//
+		// The source address may still be unknown and will become
+		// known only after flow Open succeeds (watch out for
+		// iSockOutBuf buffering case--address becomes known in
+		// CanSend!)
+		iFlow.SetLocalAddr(aInfo.iSrcAddr);
+		iFlow.SetRemoteAddr(aInfo.iDstAddr);
+		}
+	else
+		{
+		// Packet is not using KIpHeaderIncluded.
+		if (aInfo.iDstAddr.Family())
+			{
+			// [Alternate: if socket is connected, do not accept explicit
+			// destination setting, and return error?]
+			// If aToAddr specified, then iDstAddr already includes a copy of it.
+			// Check port range
+			if (aInfo.iDstAddr.Port() < 1 || aInfo.iDstAddr.Port() > 65535)
+				{
+				return KErrGeneral;
+				}
+			iFlow.SetRemoteAddr(aInfo.iDstAddr);
+			}
+		}
+
+	const TInt status = aInfo.iFlow.Open(iFlow, &aInfo);
+	if (status == EFlow_READY)
+		{
+		// Flow is ready for send
+		//
+		// Build UDP header
+		FillUdpHeader(pkt, aPacket, aInfo, aOffset);
+		return KErrNone;
+		}
+	else if (status < 0)
+		return status;
+
+	// status > 0
+
+
+	// Block if the flow is blocked and application has requested synchronous send
+	// or if we're waiting for an interface to come up
+	//
+	if (iSynchSend || (status == EFlow_PENDING && Protocol()->WaitNif()))
+		{
+		if (aOffset == 0)
+			{
+			// Cancel the UDP header allocation.
+			aPacket.TrimStart(TInet6HeaderUDP::MinHeaderLength());
+			}
+		LOG(Log::Printf(_L("\tudp SAP[%u] Flow not ready (%d), BLOCKING the socket"), (TInt)this, status));
+		// The socket write will be blocked!
+		return status;
+		}
+
+	// Flow is not ready for sending, buffer the packet for a case where the flow
+	// becomes ready before the application sends another packet.
+	LOG(Log::Printf(_L("\tudp SAP[%u] Flow not ready (%d), BUFFER one packet"), (TInt) this, status));
+	aPacket.Pack();
+	iSockOutBuf.Assign(aPacket);
+	iSockOutOffset = aOffset;
+	return KErrNone;
+	}
+
+TInt CProviderUDP6::Recv(TDualBufPtr& aBuf, TUint aLength, TUint aOptions, TSockAddr* aAddr)
+	{
+//	LOG(Log::Printf(_L("CProviderUDP6::Recv(%d, %d)\r\n"), aLength, aOptions));
+	ASSERT(iSockFlags.iRecvClose == EFalse);
+
+	RMBufRecvPacket packet;
+	RMBufRecvInfo *info;
+	TInt off;
+
+
+	if (!iSockInQ.Remove(packet))
+		Panic(EInet6Panic_NoData);
+
+	info = packet.Unpack();
+	off = (/*iSockFlags.*/iRawMode || (aOptions & KIpHeaderIncluded)) ? 0 : info->iOffset;
+	aLength = Min(STATIC_CAST(TInt, aLength), info->iLength - off);
+
+	ASSERT(info->iLength == packet.Length());
+
+	// Get remote address
+	LOG(TBuf<70> tmp(_L("NULL")));
+	if (aAddr != NULL)
+		{
+		*aAddr = info->iSrcAddr;
+		LOG(TInetAddr::Cast(*aAddr).OutputWithScope(tmp));
+		if (iAppFamily == KAfInet)
+			TInetAddr::Cast(*aAddr).ConvertToV4();
+		}
+
+	if (aOptions & KSockReadPeek)
+		{
+		TInt err = aBuf.CopyIn(packet, off, aLength);
+		packet.Pack();
+		iSockInQ.Prepend(packet);
+		LOG(Log::Printf(_L("GetData\tudp SAP[%u] Peek len=%d err=%d from=%S, calling NewData(1)"), (TInt)this, aLength, err, &tmp));
+		iSocket->NewData(1);
+		if (err != KErrNone)
+			return KErrNoMBufs;
+		}
+	else
+		{
+		iSockInQLen -= info->iLength;
+		if (off > 0)
+			packet.TrimStart(off);
+		aBuf.Consume(packet, aLength, iBufAllocator);
+		packet.Free();
+		LOG(Log::Printf(_L("GetData\tudp SAP[%u] length=%d from=%S"), (TInt)this, aLength, &tmp));
+		}
+
+	return 1;
+	}
+
+
+void CProviderUDP6::Process(RMBufChain& aPacket, CProtocolBase* /*aSourceProtocol*/)
+	{
+	TUint length = 0;
+	RMBufRecvInfo *const info = RMBufRecvPacket::PeekInfoInChain(aPacket);
+	if (info)
+		{
+		length = info->iLength;
+
+		//
+		// ESock does not properly buffer multiple datagrams in the receive buffer,
+		// so we need to do it here. The worst part is that we don't know the size
+		// of the receive buffer requested by the application, because ESock does not
+		// pass the socket option to us. We use a TCPIP.INI parameter/Udp socket option instead.
+		//
+		if (iSockFlags.iRecvClose || !iSockFlags.iNotify ||
+			(!iSockInQ.IsEmpty() && iSockInQLen + length > iSockInBufSize))
+			{
+			LOG(Log::Printf(_L("\tudp SAP[%u] No space. Incoming packet DROPPED"), (TInt)this));
+			}
+		else
+			{
+			iSockInQLen += length;
+			iSockInQ.Append(aPacket);
+			LOG(Log::Printf(_L("\tudp SAP[%u] Packet queued, QLen=%d, calling NewData(1)"), (TInt)this, iSockInQLen));
+			iSocket->NewData(1);
+			return;
+			}
+		}
+	aPacket.Free();
+	}
+
+void CProviderUDP6::Error(TInt aError, TUint aOperationMask)
+	{
+	// Report errors by interrupting send and receive operations
+	aOperationMask &= MSocketNotify::EErrorSend|MSocketNotify::EErrorRecv;
+	CProviderInet6Transport::Error(aError, aOperationMask);
+
+	//
+	// Force the flow to reconnect on the next send, clearing the error state.
+	//
+	iFlow.FlowContext()->SetChanged();
+	}
+
+void CProviderUDP6::CanSend()
+	{
+
+	//
+	// Check flow status.
+	//
+	// Note: calling iFlow.Status() here also makes sure that a connect()
+	//       operation on the UDP socket will bring the flow up completely
+	//       without requiring the application to follow it up with a
+	//       send(). This is required by, e,g. the QoS framework.
+	//
+	TInt status;
+	if (iSockOutBuf.IsEmpty())
+		{
+		// No buffered packet.
+		status = iFlow.Status();
+		if (status == EFlow_READY)
+			{
+			LOG(Log::Printf(_L("\tudp SAP[%u] CanSend() Nothing buffered, flow ready, wakeup"), (TInt)this));
+			// Wake application
+			CProviderInet6Transport::CanSend();
+			return;
+			}
+		LOG(Log::Printf(_L("\tudp SAP[%u] CanSend() Nothing buffered, flow status %d"), (TInt)this, status));
+		}
+	else
+		{
+		// A packet is waiting for this!
+		RMBufSendInfo *info = iSockOutBuf.PeekInfo();
+		status = info->iFlow.Open(iFlow, info);
+		if (status == EFlow_READY)
+			{
+			RMBufSendPacket packet;
+			packet.Assign(iSockOutBuf);
+			packet.Unpack();
+			TInet6Checksum<TInet6HeaderUDP> pkt(packet, iSockOutOffset);
+			if (pkt.iHdr == NULL)
+				{
+				// Can happen if user uses KIpHeaderIncluded without UDP header, but
+				// may catch other unexpected problems with the RMBuf handling. In
+				// any case, this packet cannot be sent (no retries allowed).
+				LOG(Log::Printf(_L("CProviderUDP6::CanSend() Bad buffered UDP packet dropped")));
+				info->iFlow.Close();
+				packet.Free();
+				}
+			else
+				{
+				LOG(Log::Printf(_L("\tudp SAP[%u] CanSend() Sending buffered UDP packet"), (TInt)this));
+				FillUdpHeader(pkt, packet, *info, iSockOutOffset);
+				packet.Pack();
+				iProtocol->Send(packet);
+				}
+			// Note: When packet is buffered, then the SocketServer is not blocked
+			// on this socket. Thus, there is no need to call iSocket->Notify()!
+			return;
+			}
+		LOG(Log::Printf(_L("\tudp SAP[%u] CanSend() Buffered packet waiting, flow status %d"), (TInt)this, status));
+		}
+
+	if (status < 0)
+		{
+		Error(status, MSocketNotify::EErrorSend|MSocketNotify::EErrorRecv);
+		}
+	}