--- /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);
+ }
+ }