networkprotocols/tcpipv4v6prt/src/udp.cpp
changeset 0 af10295192d8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/networkprotocols/tcpipv4v6prt/src/udp.cpp	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,509 @@
+// 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.cpp - UDP protocol for IPv6/IPv4
+//
+
+#include "udp.h"
+#include <icmp6_hdr.h>
+#include <ip4_hdr.h>
+#include <in6_if.h>
+#include "tcpip_ini.h"
+#include "inet6log.h"
+
+#define SYMBIAN_NETWORKING_UPS
+
+//
+// UDP Protocol Description
+//
+
+void CProtocolUDP6::Describe(TServerProtocolDesc &aDesc)
+	{
+	aDesc.iName		  = _S("udp");
+	aDesc.iAddrFamily	  = KAfInet;
+	aDesc.iSockType	  = KSockDatagram;
+	aDesc.iProtocol	  = KProtocolInetUdp;
+	aDesc.iVersion	  = TVersion(KInet6MajorVersionNumber,
+								KInet6MinorVersionNumber,
+								KInet6BuildVersionNumber);
+	aDesc.iByteOrder	  = EBigEndian;
+	aDesc.iServiceInfo	  = KSIConnectionLess | KSIDatagram |
+							KSIGracefulClose | KSIPeekData |
+							KSIRequiresOwnerInfo;
+	aDesc.iNamingServices	  = KNSNameResolution | KNSRequiresConnectionStartup;
+	aDesc.iSecurity	  = KSocketNoSecurity;
+	aDesc.iMessageSize	  = 65536-128; /*KSocketMessageSizeUndefined;*/
+	aDesc.iServiceTypeInfo  = ESocketSupport | ETransport |
+							EPreferMBufChains | ENeedMBufs |
+							EUseCanSend;
+	aDesc.iNumSockets	  = KUnlimitedSockets;
+	}
+
+
+
+CProtocolUDP6::CProtocolUDP6()
+	{
+	__DECLARE_NAME(_S("CProtocolUDP6"));
+	}
+
+
+CProtocolUDP6::~CProtocolUDP6()
+	{
+	}
+
+
+CServProviderBase* CProtocolUDP6::NewSAPL(TUint aSockType)
+	{
+	LOG(Log::Printf(_L("NewSAPL\tudp SockType=%d"), aSockType));
+	if (aSockType!=KSockDatagram)
+		User::Leave(KErrNotSupported);
+	CProviderUDP6 *sap = new (ELeave) CProviderUDP6(this);
+	CleanupStack::PushL(sap);
+	sap->InitL();
+	CleanupStack::Pop();
+	LOG(Log::Printf(_L("NewSAPL\tudp SAP[%u] OK"), (TUint)sap));
+	return sap;
+	}
+
+void CProtocolUDP6::InitL(TDesC& aTag)
+	{
+	CProtocolInet6Transport::InitL(aTag);
+	}
+
+
+void CProtocolUDP6::StartL()
+	{
+	CProtocolInet6Transport::StartL();
+
+	iRecvBuf = GetIniValue(TCPIP_INI_UDP, TCPIP_INI_UDP_RECV_BUF, KUdpDefaultRecvBuf, 1, KMaxTInt);
+
+	// Flags on by default
+	iWaitNif = GetIniValue(TCPIP_INI_UDP, TCPIP_INI_UDP_WAIT_NIF, 1, 0, 1);
+	}
+
+
+void CProtocolUDP6::Identify(TServerProtocolDesc *aInfo) const
+	{
+	Describe(*aInfo);
+	}
+
+TInt CProtocolUDP6::Send(RMBufChain& aPacket,CProtocolBase* /*aSourceProtocol=NULL*/)
+	{
+	LOG(Log::Printf(_L("\tudp Send(%d bytes)"), RMBufSendPacket::PeekInfoInChain(aPacket)->iLength));
+	return iNetwork->Send(aPacket);
+	}
+
+
+void CProtocolUDP6::Process(RMBufChain& aPacket, CProtocolBase* /*aSourceProtocol*/)
+	{
+	RMBufRecvPacket datagram;
+	datagram.Assign(aPacket);
+	RMBufRecvInfo* info = datagram.Unpack();
+	TInet6PacketUDP pkt(datagram, info->iOffset);
+	TInet6Packet<TInet6HeaderIP4> ip4;
+	TInet6Packet<TInet6HeaderIP>  ip6;
+	TInt err = KErrNone;
+	TInt version(0);
+	
+	CProviderUDP6 *sap = NULL;
+	TInetAddr icmpSender;
+
+#ifdef _LOG
+	TBuf<50> src, dst;
+	LOG(Log::Printf(_L("\tudp Process(%d bytes)"), datagram.Length()));
+#endif
+
+	ASSERT(info->iLength == datagram.Length());
+
+	//
+	// Preliminary checking for both UDP and ICMP
+	//
+	switch (info->iProtocol)
+		{
+	case KProtocolInetUdp:
+		// Sanity checking
+		if (!pkt.iHdr)
+			{
+			LOG(Log::Printf(_L("\tudp Process() Runt packet discarded")));
+			datagram.Free();
+			return;
+			}
+
+#ifdef _LOGx
+		TInetAddr(info->iSrcAddr).OutputWithScope(src); TInetAddr(info->iDstAddr).OutputWithScope(dst);
+		LOG(Log::Printf(_L("\tudp Process(%d bytes) {%S,%d} -> {%S,%d}"),
+			datagram.Length(), &src, pkt.iHdr->SrcPort(), &dst, pkt.iHdr->DstPort()));
+#endif
+
+		// More sanity checking.
+		if (pkt.iHdr->SrcPort() == KInetPortNone || pkt.iHdr->DstPort() == KInetPortNone)
+			{
+			LOG(Log::Printf(_L("\tudp Process() Zero port. Packet discarded.")));
+			datagram.Free();
+			return;
+			}
+		break;
+
+	default:
+		LOG(Log::Printf(_L("\tudp Process() Unknown protocol. Packet discarded.")));
+		datagram.Free();
+		return;
+		}
+
+	switch (info->iIcmp)
+		{
+	case 0:
+
+		// Check source address
+		if (!TInetAddr::Cast(info->iSrcAddr).IsUnicast() && !TInetAddr::Cast(info->iSrcAddr).IsUnspecified())
+			{
+			LOG(Log::Printf(_L("\tudp Process() Invalid source address. Packet discarded.")));
+			datagram.Free();
+			break;
+			}
+
+		// Sanity checking
+		if (info->iLength != info->iOffset + pkt.iHdr->Length())
+			{
+			LOG(Log::Printf(_L("\tudp Process() Size mismatch. Packet discarded.")));
+			datagram.Free();
+			break;
+			}
+
+		// Process UDP checksum. Check for no checksum (legal in UDP/IPv4)
+		if (pkt.iHdr->Checksum() ? !pkt.VerifyChecksum(datagram, info, info->iOffset) : info->iVersion != 4)
+			{
+			LOG(Log::Printf(_L("\tudp Process() Bad checksum. Packet discarded.")));
+			datagram.Free();
+			return;
+			}
+
+		//LOG(Log::Printf(_L("Verified UDP checksum %04x."), pkt.iHdr->Checksum()));
+
+		// Get port numbers from header
+		info->iSrcAddr.SetPort(pkt.iHdr->SrcPort());
+		info->iDstAddr.SetPort(pkt.iHdr->DstPort());
+
+#ifdef _LOG
+		TInetAddr(info->iSrcAddr).OutputWithScope(src); TInetAddr(info->iDstAddr).OutputWithScope(dst);
+		LOG(Log::Printf(_L("\tudp Process() UDP datagram {%S,%d} -> {%S,%d}"),
+			&src, info->iSrcAddr.Port(), &dst, info->iDstAddr.Port()));
+#endif
+
+		// Advance iOffset over the UDP header
+		info->iOffset += pkt.iHdr->HeaderLength();
+
+		//
+		// Replicate packets to all receivers.
+		// If there is only a single receiver,
+		// no extra packet copies will be made.
+		//
+		CProviderUDP6 *s;
+		version = info->iVersion == 4 ? KAfInet : KAfInet6;
+
+		// Locate hash bucket and traverse hash chain
+		s = (CProviderUDP6*)CProtocolInet6Base::LocateProvider(info->iDstAddr.Port());
+		// Locate best match if exists
+	    if (s)
+	    	{
+	    	if (!s->iNextSAP) // only one possible receiver in hash entry
+	    		{
+				// Locate best match
+#ifndef SYMBIAN_STRICT_EXPLICIT_SOCKET_BINDING
+				sap = (CProviderUDP6*)LocateSap(EMatchServerUnspecAddr,
+					version, info->iDstAddr, info->iSrcAddr, s);	    		
+#else
+				sap = (CProviderUDP6*)LocateSap(EMatchServerUnspecAddr,
+					version, info->iDstAddr, info->iSrcAddr, s,info->iInterfaceIndex);	    		
+#endif //SYMBIAN_STRICT_EXPLICIT_SOCKET_BINDING
+	    		}
+	    	else // Multiple possible receivers in hash entry
+	    		{
+#ifndef SYMBIAN_STRICT_EXPLICIT_SOCKET_BINDING
+				sap = (CProviderUDP6*)LocateSap(EMatchServerSpecAddr, //Since this is UDP we are only interested in the dst Ip addr
+					version, info->iDstAddr, info->iSrcAddr, s);
+#else
+				sap = (CProviderUDP6*)LocateSap(EMatchServerSpecAddr, //Since this is UDP we are only interested in the dst Ip addr
+					version, info->iDstAddr, info->iSrcAddr, s,info->iInterfaceIndex);
+#endif //SYMBIAN_STRICT_EXPLICIT_SOCKET_BINDING
+			
+				if (!sap) // no explicit connections
+					{
+					for (; s != NULL; s = (CProviderUDP6*)s->iNextSAP)
+						{
+						// Locate next match
+#ifndef SYMBIAN_STRICT_EXPLICIT_SOCKET_BINDING
+						s = (CProviderUDP6*)LocateSap(EMatchServerUnspecAddr,
+							version, info->iDstAddr, info->iSrcAddr, s);
+#else
+						s = (CProviderUDP6*)LocateSap(EMatchServerUnspecAddr,
+							version, info->iDstAddr, info->iSrcAddr, s,info->iInterfaceIndex);
+#endif //SYMBIAN_STRICT_EXPLICIT_SOCKET_BINDING
+						// No match?
+						if (s == NULL)
+							break;
+
+#ifdef SYMBIAN_NETWORKING_UPS
+						if (!s->HasNetworkServices() && (!s->ConnectionInfoSet())&& (info->iFlags & KIpLoopbackPacket) == 0)
+#else
+						if (!s->HasNetworkServices() && (info->iFlags & KIpLoopbackPacket) == 0)
+#endif
+							{
+							LOG(Log::Printf(_L("\tudp SAP[%u] Not allowed to receive external packets"), (TInt)s));
+							continue;
+							}
+
+						// More than one receiver found? Copy the packet.
+						if (sap != NULL)
+							{
+							RMBufRecvPacket copy;
+							TRAPD(err, datagram.CopyL(copy);datagram.CopyInfoL(copy));
+							if (err == KErrNone)
+								{
+								copy.Pack();
+								sap->Process(copy);
+								}
+							copy.Free();
+							}
+						// Remember last match
+						sap = s;
+						}
+					}
+	    		}
+	    	}
+		// No match? Respond with ICMP
+		if (sap == NULL)
+			{
+            /* From RFC1122:
+			If a datagram arrives addressed to a UDP port for which
+			there is no pending LISTEN call, UDP SHOULD send an ICMP
+			Port Unreachable message.*/
+			LOG(LogProviders(info->iDstAddr.Port()));
+
+			// Include UDP header in ICMP payload
+			info->iOffset -= pkt.iHdr->HeaderLength();
+
+			if (info->iVersion == 4)
+				iNetwork->Icmp4Send(datagram, KInet4ICMP_Unreachable, 3 /* port unreachable*/);
+			else
+				iNetwork->Icmp6Send(datagram, KInet6ICMP_Unreachable, 4 /* port unreachable*/);
+			}
+		else
+			{
+#ifdef SYMBIAN_NETWORKING_UPS
+			if (!sap->HasNetworkServices() && (!sap->ConnectionInfoSet()) && (info->iFlags & KIpLoopbackPacket) == 0)
+#else
+			if (!sap->HasNetworkServices() && (info->iFlags & KIpLoopbackPacket) == 0)
+#endif
+				{
+				LOG(Log::Printf(_L("\tudp SAP[%u] Not allowed to receive external packets"), (TInt)sap));
+				datagram.Free();
+				break;
+				}
+			const TInt iface = info->iOriginalIndex;
+
+			// Punt the packet to the SAP
+			datagram.Pack();
+			sap->Process(datagram);
+
+			// Reset idle timers
+			Interfacer()->PacketAccepted(iface);
+			}
+		break;
+
+	case KProtocolInetIcmp:
+	case KProtocolInet6Icmp:
+
+		// Get port numbers from header
+		info->iSrcAddr.SetPort(pkt.iHdr->SrcPort());
+		info->iDstAddr.SetPort(pkt.iHdr->DstPort());
+
+#ifdef _LOG
+		TInetAddr(info->iSrcAddr).OutputWithScope(src); TInetAddr(info->iDstAddr).OutputWithScope(dst);
+		LOG(Log::Printf(_L("\tudp Process() ICMP reply to UDP datagram {%S,%d} -> {%S,%d}"),
+			&src, info->iSrcAddr.Port(), &dst, info->iDstAddr.Port()));
+		LOG(Log::Printf(_L("\tudp Process() ICMP type=%d code=%d param=%d received."),
+			info->iType, info->iCode, info->iParameter));
+#endif
+
+		sap = (CProviderUDP6*)LocateSap(EMatchServerUnspecAddr,
+			info->iVersion == 4 ? KAfInet : KAfInet6,
+			info->iSrcAddr, info->iDstAddr);
+		if (sap == NULL)
+			{
+			LOG(Log::Printf(_L("\tudp Process() No socket. Discarding ICMP message.")));
+			datagram.Free();
+			break;
+			}
+
+		err = KErrUnknown;
+		if (info->iIcmp == KProtocolInetIcmp)
+			{
+			//
+			// ICMPv4 message processing
+			//
+			ip4.Set(datagram, 0, TInet6HeaderIP4::MinHeaderLength());
+			if (!ip4.iHdr)
+				{
+				LOG(Log::Printf(_L("\tudp Process() Invalid IPv4 header in ICMP.")));
+				datagram.Free();
+				break;
+				}
+			icmpSender.SetAddress(ip4.iHdr->SrcAddr());
+			switch (info->iType)
+				{
+			case KInet4ICMP_Unreachable:
+/*
+   Code           0 = net unreachable
+                  1 = host unreachable
+                  2 = protocol unreachable
+                  3 = port unreachable
+                  4 = fragmentation needed and DF set
+                  5 = source route failed
+                  6 = destination network unknown
+                  7 = destination host unknown
+                  8 = source host isolated
+                  9 = communication with destination network administratively prohibited
+                 10 = communication with destination host administratively prohibited
+                 11 = network unreachable for type of service
+                 12 = host unreachable for type of service
+				*/
+				switch (info->iCode)
+					{
+				case 2:
+					err = KErrNoProtocolOpt;
+					break;
+
+				case 3:
+					err = KErrCouldNotConnect;
+					break;
+
+				case 4:
+					err = KErrTooBig;
+					break;
+
+				case 0:
+				case 5:
+				case 6:
+				case 8:
+				case 9:
+				case 11:
+					err = KErrNetUnreach;
+					break;
+
+				case 1:
+				case 7:
+				case 10:
+				case 12:
+					err = KErrHostUnreach;
+					break;
+
+				default:
+					break;
+					}
+				break;
+
+			case KInet4ICMP_SourceQuench:
+				break;
+
+			case KInet4ICMP_TimeExceeded:
+				err = KErrTimedOut;
+				break;
+
+			case KInet4ICMP_ParameterProblem:
+				err = KErrArgument;
+				break;
+
+			default:
+				break;
+				}
+			}
+		else
+			{
+			//
+			// ICMPv6 message processing
+			//
+			ip6.Set(datagram, 0, TInet6HeaderIP::MinHeaderLength());
+			if (!ip6.iHdr)
+				{
+				LOG(Log::Printf(_L("\tudp  Process() Invalid IPv6 header in ICMP.")));
+				datagram.Free();
+				break;
+				}
+			icmpSender.SetAddress(ip6.iHdr->SrcAddr());
+			switch (info->iType)
+				{
+			case KInet6ICMP_Unreachable:
+/*
+   Code           0 - no route to destination
+                  1 - communication with destination administratively prohibited
+                  2 - (not assigned)
+                  3 - address unreachable
+                  4 - port unreachable
+				*/
+				switch (info->iCode)
+					{
+				case 3:
+					err = KErrHostUnreach;
+					break;
+
+				case 4:
+					err = KErrCouldNotConnect;
+					break;
+
+				default:
+					err = KErrNetUnreach;
+					break;
+					}
+				break;
+
+			case KInet6ICMP_PacketTooBig:
+				err = KErrTooBig;
+				break;
+
+			case KInet6ICMP_TimeExceeded:
+/*
+   Code           0 - hop limit exceeded in transit
+                  1 - fragment reassembly time exceeded
+				*/
+				err = KErrTimedOut;
+				break;
+
+			case KInet6ICMP_ParameterProblem:
+/*
+   Code           0 - erroneous header field encountered
+                  1 - unrecognized Next Header type encountered
+                  2 - unrecognized IPv6 option encountered
+				*/
+				err = KErrArgument;
+				break;
+
+			default:
+				break;
+				}
+			}
+
+		// Store and report
+		sap->IcmpError(err, MSocketNotify::EErrorSend|MSocketNotify::EErrorRecv,
+			info->iType, info->iCode,
+			TInetAddr::Cast(info->iSrcAddr),
+			TInetAddr::Cast(info->iDstAddr),
+			TInetAddr::Cast(icmpSender));
+		datagram.Free();
+		break;
+
+	default:
+		datagram.Free();
+		break;
+		}
+	}