sdkcreationmw/sdkruntimes/wsock/src/WinsockServProvider.cpp
changeset 0 b26acd06ea60
child 1 ac50fd48361b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sdkcreationmw/sdkruntimes/wsock/src/WinsockServProvider.cpp	Mon Mar 08 12:09:11 2010 +0530
@@ -0,0 +1,1930 @@
+/*
+* Copyright (c) 2004-2007 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: 
+*
+*/
+
+
+#define TRACE_PREFIX "WSOCK: ServProvider: "
+#include <es_mbuf.h>
+#include "wsock.h"
+#include "WinsockProtocol.h"
+#include "WinsockProtocolFamily.h"
+#include "WinsockServProvider.h"
+#include "WinsockUtils.h"
+
+#define KMaxInetAddrBufSize 64
+
+// CWinsockServProvider::CWriteData
+class CWinsockServProvider::CWriteData : public CBase
+{
+public:
+    TDblQueLink iLink;
+    TSockAddr* iAddr;
+    HBufC8* iData;
+public:
+    static CWriteData* New(const TDesC8& aData, TSockAddr* aAddr);
+    ~CWriteData();
+};
+
+// CWinsockServProvider::CWriteRequest
+class CWinsockServProvider::CWriteRequest : public CActive
+{
+private:
+    CWinsockServProvider* iProvider;
+    RTimer iTimer;
+    TBool iTimerCreated;
+    TBool iTimerActive;
+public:
+    CWriteRequest(CWinsockServProvider* aProvider);
+    ~CWriteRequest();
+    void Submit(TInt aDelay = 0);
+
+    // CActive
+    virtual void DoCancel();
+    virtual void RunL();
+};
+
+// CWinsockServProvider::CSelectRequest
+class CWinsockServProvider::CSelectRequest : public CActive,
+                                             public MSelectRequest
+{
+private:
+    CWinsockServProvider* iProvider;
+    RThread iServerThread;
+    TInt iSelectMask;
+public:
+    static CSelectRequest* NewLC(CWinsockServProvider* aProvider);
+    virtual ~CSelectRequest();
+    void SubmitL();
+
+    // CActive
+    virtual void DoCancel();
+    virtual void RunL();
+
+    // MSelectRequest
+    virtual TUint Socket();
+    virtual TInt SelectMask();
+    virtual void SelectComplete(TInt aSelectMask);
+
+private:
+    CSelectRequest(CWinsockServProvider* aProvider);
+    void ConstructL();
+};
+
+// CWinsockServProvider::CNewDataNotifier
+class CWinsockServProvider::CNewDataNotifier : public CActive
+{
+private:
+    CWinsockServProvider* iProvider;
+public:
+    CNewDataNotifier(CWinsockServProvider* aProvider);
+    void Submit();
+
+    // CActive
+    virtual void DoCancel();
+    virtual void RunL();
+};
+
+// CWinsockServProvider
+#define SUPER CServProviderBase
+CWinsockServProvider* CWinsockServProvider::NewL(CWinsockProtocol* aProtocol)
+{
+    return new(ELeave)CWinsockServProvider(aProtocol);
+}
+
+CWinsockServProvider* CWinsockServProvider::FromSocket(CWinsockProtocol* aPro,
+                                                       TInt aSocket,
+                                                       TInt aFamily)
+{
+    ASSERT(aFamily != 0);
+    ASSERT(aSocket != INVALID_SOCKET);
+    CWinsockServProvider* self = new CWinsockServProvider(aPro);
+    if (self) {
+        self->iWinSocket = aSocket;
+        self->iFamily = aFamily;
+        self->iFlags |= (EFlagConnect | EFlagNeedSelect);
+        // Select will be called from SetNotify. Otherwise, under some rare
+        // circumstances select may complete and NewDataCheck is invoked
+        // before SetNotify has been invoked and therefore iSocket is still
+        // NULL.
+    }
+    return self;
+}
+
+CWinsockServProvider::CWinsockServProvider(CWinsockProtocol* aProtocol) :
+iProtocol(aProtocol),
+iWinSocket(INVALID_SOCKET),
+iWriteQ(_FOFF(CWriteData,iLink)),
+iTcpSendWinSize(8192),
+iTcpRecvWinSize(8192),
+iTcpMaxSegSize(1460),
+iUdpRecvBuf(8192)
+{
+    WTRACE("created");
+}
+
+CWinsockServProvider::~CWinsockServProvider()
+{
+    iProtocol->SetReadPending(this, EFalse);
+    if (iWrite)
+    {
+        iWrite->Cancel();
+        delete iWrite;
+    }
+    if (iNewData)
+    {
+        iNewData->Cancel();
+        delete iNewData;
+    }
+    if (iSelect)
+    {
+        CWinsockSelectThread::Static()->Cancel(iSelect);
+        iSelect->Cancel();
+    }
+    if (iAdapterInfo)
+    {
+        User::Free(iAdapterInfo);
+    }
+    if (iWinSocket != INVALID_SOCKET)
+    {
+        closesocket(iWinSocket);
+    }
+#ifdef V1_5_PRT_INTERFACE
+    if (iDataBuf)
+    {
+        delete iDataBuf;
+    }
+#endif
+    WTRACE("destroyed");
+}
+
+// Returns the socket, or creates one using default address family.
+TInt CWinsockServProvider::Socket() const
+{
+    CWinsockServProvider* This = (CWinsockServProvider*)this;
+    return This->Socket(Family());
+}
+
+// Returns the socket, creating one if necessary. Note that the requested
+// address family may not match the protocol family given to us in the
+// constructor. That's because some Symbian code (such as HTTP protocol
+// framework) may, for example, create an IPv4 socket and then connect
+// it to an IPv6 address. Also note that once we have created the Windows
+// socket we can't change its address family.
+TInt CWinsockServProvider::Socket(TUint aFamily)
+{
+    if (iWinSocket == INVALID_SOCKET)
+    {
+        if (!aFamily) aFamily = Family();
+        TInt af = (aFamily == KAfInet6) ? AF_INET6 : AF_INET;
+        TInt type = iProtocol->ProtocolDesc()->iWinSockType;
+        TInt protocol = iProtocol->ProtocolDesc()->iWinProtocol;
+        iWinSocket = socket(af, type, protocol);
+        if (iWinSocket == INVALID_SOCKET)
+        {
+            WTRACE4("socket(%d,%d,%d) failed, err %d",af, type, protocol,
+            WSAGetLastError());
+        }
+        else
+        {
+            // Remember the actual address family
+            iFamily = aFamily;
+            WTRACE2("created %s %S socket", (af == AF_INET6) ?
+                _S("IPv6") : _S("IPv4"), &iProtocol->ProtocolDesc()->iName);
+
+            // Connection-less sockets immediately start accepting
+            // incoming datagrams
+            if (iProtocol->IsDatagramProtocol())
+            {
+                Select();
+            }
+        }
+    }
+    else
+    {
+        // This ASSERT means that we have already created the Windows socket
+        // and then we were asked to connect or bind it to the address with
+        // a different address family. That's bad.
+        ASSERT(!aFamily || aFamily == iFamily);
+    }
+    return iWinSocket;
+}
+
+// Returns address family of this socket in Symbian format,
+// i.e. KAfInet or KAfInet6
+TUint CWinsockServProvider::Family() const
+{
+    if (iFamily)
+    {
+        // The socket has already been created
+        return iFamily;
+    }
+    else if (iRemAddr.Family())
+    {
+        // Assume the same address family as in remote address. That makes
+        // URLs like http://[fe80::a00:20ff:fef9:bf8]/ work in the browser.
+        // In that case we get the following sequence of calls:
+        //    SetRemName -> AutoBind -> ActiveOpen
+        // This allows us to create the socket of the right type in AutoBind.
+        return iRemAddr.Family();
+    }
+    else
+    {
+        // With no additional information, assume the address family from
+        // the protocol object.
+        return iProtocol->ProtocolDesc()->iAddrFamily;
+    }
+}
+
+// CServProviderBase
+void CWinsockServProvider::Start()
+{
+    WTRACE("Start()");
+}
+
+void CWinsockServProvider::SetNotify(MSocketNotify* aSocket)
+{
+    WTRACE1("SetNotify(%08X)",aSocket);
+    SUPER::SetNotify(aSocket);
+    if (iFlags & EFlagNeedSelect)
+    {
+        iFlags &= ~EFlagNeedSelect;
+        Select();
+    }
+}
+
+typedef int (WSAAPI *GetSockNameProc)(
+    SOCKET s,
+    struct sockaddr* name,
+    int* namelen
+);
+
+static void GetSockName(SOCKET sock, TSockAddr& aAddr,
+                        GetSockNameProc aFunction,
+                        const TText* DEBUG_ONLY(aFunctionName))
+{
+    WSockAddr sa;
+    int namelen = SOCKADDR_SIZE;
+    int err = aFunction(sock, &sa.Address, &namelen);
+    if (!err)
+    {
+        WinsockUtils::ToInetAddr(&aAddr,&sa);
+    }
+    else
+    {
+        TRACE2("%s failed, error %d\n",aFunctionName,err);
+    }
+}
+
+void CWinsockServProvider::LocalName(TSockAddr& aAddr) const
+{
+    if (Socket() == INVALID_SOCKET)
+    {
+        WTRACE("LocalName() - no socket");
+    }
+    else
+    {
+        GetSockName(iWinSocket,aAddr,getsockname,_S("getsockname"));
+    }
+}
+
+// Wrapper around WinsockUtils::ToSockAddr which attempts to convert
+// the address into the form compatible with the address family of
+// this socket.
+int CWinsockServProvider::ToSockAddr(union _WSockAddr* aToAddr,
+                                     const TSockAddr* aFromAddr)
+{
+    if (Family() == KAfInet6 && aFromAddr->Family() == KAfInet)
+    {
+        // Convert IPv4 address to IPv4-mapped IPv6 address.
+        TInetAddr ipv6(*aFromAddr);
+        ipv6.ConvertToV4Mapped();
+        ASSERT(ipv6.Family() == KAfInet6);
+        return WinsockUtils::ToSockAddr(aToAddr, &ipv6);
+    }
+
+    if (Family() == KAfInet && aFromAddr->Family() == KAfInet6)
+    {
+        TInetAddr& ipv6 = TInetAddr::Cast(aFromAddr);
+        if (ipv6.IsV4Mapped() || ipv6.IsV4Compat())
+        {
+            // Convert IPv4 compatible IPv6 address to IPv4
+            TInetAddr ipv4(ipv6);
+            ipv4.ConvertToV4();
+            return WinsockUtils::ToSockAddr(aToAddr, &ipv4);
+        }
+    }
+
+    // Do the default conversion
+    return WinsockUtils::ToSockAddr(aToAddr, aFromAddr);
+}
+
+TInt CWinsockServProvider::SetLocalName(TSockAddr& aAddr)
+{
+#ifdef _REALLY_DEBUG
+    TBuf<KMaxInetAddrBufSize> buf;
+    TInetAddr::Cast(aAddr).OutputWithScope(buf);
+    if (buf.Length() == 0) buf.Append('*');
+    WTRACE2("SetLocalName(%S,%d)",&buf,aAddr.Port());
+#endif // _REALLY_DEBUG
+
+    if (Socket(aAddr.Family()) == INVALID_SOCKET)
+    {
+        WTRACE("SetLocalName - no socket");
+        iFlags |= EFlagError;
+        return KErrNotSupported;
+    }
+
+    // Bind new socket to the specified address
+    WSockAddr sa;
+    int len = ToSockAddr(&sa, &aAddr);
+    int err = bind(iWinSocket, &sa.Address, len);
+    if (err)
+    {
+        WTRACE1("bind failed, err %d",WSAGetLastError());
+        return KErrArgument;
+    }
+    else
+    {
+#ifdef _REALLY_DEBUG
+        TInetAddr localAddr;
+        GetSockName(iWinSocket,localAddr,getsockname,_S("getsockname"));
+        localAddr.OutputWithScope(buf);
+        WTRACE2("bound to %S port %d",&buf,localAddr.Port());
+#endif // _REALLY_DEBUG
+
+        return KErrNone;
+    }
+}
+
+void CWinsockServProvider::RemName(TSockAddr& aAddr) const
+{
+    if (iFlags & EFlagConnect)
+    {
+        GetSockName(iWinSocket,aAddr,getpeername,_S("getpeername"));
+    }
+    else
+    {
+        aAddr = iRemAddr;
+    }
+}
+
+TInt CWinsockServProvider::SetRemName(TSockAddr& aAddr)
+{
+    iRemAddr = TInetAddr::Cast(aAddr);
+
+#ifdef _REALLY_DEBUG
+    TBuf<KMaxInetAddrBufSize> buf;
+    iRemAddr.OutputWithScope(buf);
+    WTRACE2("SetRemName(%S,%d)",&buf,iRemAddr.Port());
+#endif // _REALLY_DEBUG
+
+    if (iProtocol->IsDatagramProtocol())
+    {
+        // application called connect() on datagram-socket. Propagate call to
+        // Winsock. (In case of stream-socket, connect() appears to happen
+        // ActiveOpen()-call, but that is not called in case of udp-socket.)
+        Connect();
+    }
+    return KErrNone;
+}
+
+void CWinsockServProvider::Ioctl(TUint DEBUG_ONLY(aLevel),
+                                 TUint DEBUG_ONLY(aName),
+                                 TDes8* /*anOption*/)
+{
+    WTRACE2("Ioctl(%d,%08X)",aLevel,aName);
+}
+
+void CWinsockServProvider::CancelIoctl(TUint DEBUG_ONLY(aLevel),
+                                       TUint DEBUG_ONLY(aName))
+{
+    WTRACE2("CancelIoctl(%d,%08X)",aLevel,aName);
+}
+
+TInt CWinsockServProvider::SetOption(TUint aLevel, TUint aName,
+                                     const TDesC8& aOption)
+{
+    switch (aLevel)
+    {
+    case KSOLSocket:
+        WTRACE1("SetOption(KSOLSocket,%08X)",aName);
+        return KErrNone;
+
+    case KSolInetIfCtrl:
+        switch (aName)
+        {
+        case KSoInetEnumInterfaces:
+            WTRACE("SetOption(KSoInetEnumInterfaces)");
+            if (iAdapterInfo)
+            {
+                User::Free(iAdapterInfo);
+                iAdapterInfo = NULL;
+            }
+            iAdapterInfo = iProtocol->GetAdapterInfo();
+            iNextAdapter = iAdapterInfo;
+            iLastAdapter = NULL;
+            return KErrNone;
+        default:
+            WTRACE1("SetOption(KSolInetIfCtrl,%08X)",aName);
+            break;
+        }
+
+    case KSOLProvider:
+        switch (aName)
+        {
+        case KSoConnectionInfo:
+            iConnectionInfo = *((TSoIfConnectionInfo*)aOption.Ptr());
+            WTRACE2("SetOption(KSoConnectionInfo) IAP %d, network %d",
+                   iConnectionInfo.iIAPId, iConnectionInfo.iNetworkId);
+            return KErrNone;
+        default:
+            WTRACE1("SetOption(KSOLProvider,%08X) - UNSUPPORTED",aName);
+            break;
+        }
+        break;
+
+    case KSolInetIp:
+        switch (aName)
+        {
+        case KSoIpTTL:
+            WTRACE1("SetOption(KSoIpTTL) %d",*((int*)aOption.Ptr()));
+            if (!setsockopt(Socket(), IPPROTO_IP, IP_TTL,
+                (char*)aOption.Ptr(), aOption.Length()))
+            {
+                return KErrNone;
+            }
+            WTRACE1("setsockopt(IP_TTL) error %d",WSAGetLastError());
+            break;
+        case KSoReuseAddr:
+            WTRACE1("SetOption(KSoReuseAddr) %d",*((int*)aOption.Ptr()));
+            if (!setsockopt(Socket(), SOL_SOCKET, SO_REUSEADDR,
+                (char*)aOption.Ptr(), aOption.Length()))
+            {
+                return KErrNone;
+            }
+            WTRACE1("setsockopt(SO_REUSEADDR) error %d",WSAGetLastError());
+            break;
+        default:
+            WTRACE1("SetOption(KSolInetIp,%08X)",aName);
+            break;
+        }
+        break;
+
+    case KSolInetTcp:
+        if (iProtocol->ProtocolDesc()->iProtocol == KProtocolInetTcp) {
+            switch (aName)
+            {
+            case KSoTcpSendWinSize:
+                WTRACE("SetOption(KSoTcpSendWinSize)");
+                if (iFlags & EFlagConnect) return KErrLocked;
+                if (aOption.Length() != sizeof(TInt)) return KErrArgument;
+                iTcpSendWinSize = *((TInt*)aOption.Ptr());
+                return KErrNone;
+            case KSoTcpRecvWinSize:
+                WTRACE("SetOption(KSoTcpSendWinSize)");
+                if (iFlags & EFlagConnect) return KErrLocked;
+                if (aOption.Length() != sizeof(TInt)) return KErrArgument;
+                iTcpRecvWinSize = *((TInt*)aOption.Ptr());
+                return KErrNone;
+            case KSoTcpMaxSegSize:
+                WTRACE("SetOption(KSoTcpMaxSegSize)");
+                if (iFlags & EFlagConnect) return KErrLocked;
+                if (aOption.Length() != sizeof(TInt)) return KErrArgument;
+                iTcpMaxSegSize = *((TInt*)aOption.Ptr());
+                return KErrNone;
+            case KSoTcpNoDelay:
+                WTRACE1("SetOption(KSoTcpNoDelay) %d",*((TInt*)aOption.Ptr()));
+                if (!setsockopt(Socket(), IPPROTO_TCP, TCP_NODELAY,
+                    (char*)aOption.Ptr(),aOption.Length())) return KErrNone;
+                WTRACE1("setsockopt(TCP_NODELAY) error %d",WSAGetLastError());
+                break;
+            case KSoTcpKeepAlive:
+                WTRACE1("SetOption(KSoTcpKeepAlive) %d",*((TInt*)aOption.Ptr()));
+                if (!setsockopt(Socket(), SOL_SOCKET, SO_KEEPALIVE,
+                    (char*)aOption.Ptr(),aOption.Length())) return KErrNone;
+                WTRACE1("setsockopt(SO_KEEPALIVE) error %d",WSAGetLastError());
+                break;
+            default:
+                WTRACE1("SetOption(KSolInetTcp,%08X) - UNSUPPORTED",aName);
+                break;
+            }
+        } else {
+            WTRACE1("SetOption(KSolInetTcp,%08X) - not a TCP socket!",aName);
+        }
+        break;
+
+    case KSolInetUdp:
+        if (iProtocol->ProtocolDesc()->iProtocol == KProtocolInetUdp) {
+            switch (aName)
+            {
+            case KSoUdpReceiveICMPError:
+                WTRACE("SetOption(KSoUdpReceiveICMPError)");
+                if (aOption.Length() != sizeof(TInt)) return KErrArgument;
+                iUdpReceiveICMPError = *((TInt*)aOption.Ptr());
+                return KErrNone;
+            case KSoUdpRecvBuf:
+                WTRACE("SetOption(KSoUdpRecvBuf)");
+                if (aOption.Length() != sizeof(TInt)) return KErrArgument;
+                iUdpRecvBuf = *((TInt*)aOption.Ptr());
+                return KErrNone;
+            case KSoUdpSynchronousSend:
+                WTRACE("SetOption(KSoUdpSynchronousSend)");
+                if (aOption.Length() != sizeof(TInt)) return KErrArgument;
+                iUdpSynchronousSend = *((TInt*)aOption.Ptr());
+                return KErrNone;
+            default:
+                WTRACE1("SetOption(KSolInetUdp,%08X) - UNSUPPORTED",aName);
+                break;
+            }
+        } else {
+            WTRACE1("SetOption(KSolInetUdp,%08X) - not a UDP socket!",aName);
+        }
+        break;
+
+    default:
+        WTRACE2("SetOption(%d,%08X) - UNSUPPORTED",aLevel,aName);
+        break;
+    }
+    return KErrNotSupported;
+}
+
+TInt CWinsockServProvider::GetOption(TUint aLevel, TUint aName,
+                                     TDes8& aOption) const
+{
+    TInt len;
+    switch (aLevel)
+    {
+    case KSOLSocket:
+        switch (aName)
+        {
+        // This one used by SIP code:
+        case KSOReadBytesPending:
+            if (aOption.MaxLength() < sizeof(TInt)) return KErrArgument;
+            aOption.SetLength(sizeof(TInt));
+            if (!ioctlsocket(Socket(), FIONREAD, (u_long*)aOption.Ptr()))
+            {
+                WTRACE1("GetOption(KSOReadBytesPending) %d",
+                    *((TInt*)aOption.Ptr()));
+                return KErrNone;
+            }
+            WTRACE1("GetOption(KSOReadBytesPending) - ioctl(FIONREAD) err %d",
+                WSAGetLastError());
+            break;
+
+        case KSORecvBuf:
+        case KSOSendBuf:
+            WTRACE1("GetOption(KSOLSocket,%08X)",aName);
+            if (aOption.MaxLength() < sizeof(TInt)) return KErrArgument;
+            aOption.SetLength(sizeof(TInt));
+            *((TInt*)aOption.Ptr()) = KSocketDefaultBufferSize;
+            return KErrNone;
+
+        default:
+            WTRACE1("GetOption(KSOLSocket,%08X) - UNSUPPORTED",aName);
+            break;
+        }
+        break;
+
+    case KSolInetIfCtrl:
+        switch (aName)
+        {
+        case KSoInetNextInterface:
+            WTRACE("GetOption(KSoInetNextInterface)");
+            iLastAdapter = iNextAdapter;
+            if (iLastAdapter)
+            {
+                iNextAdapter = iLastAdapter->Next;
+
+                // Copy interface information into the output buffer
+                TSoInetInterfaceInfo *opt;
+                aOption.SetLength(sizeof(*opt));
+                opt = (TSoInetInterfaceInfo*)aOption.Ptr();
+                opt->iName.Copy(TPtrC8(iLastAdapter->AdapterName));
+                const WinsockIpAddrString*adr = iLastAdapter->CurrentIpAddress;
+                if (!adr) adr = &iLastAdapter->IpAddressList;
+                TUint ip = inet_addr((char*)adr->IpAddress.String);
+                TUint mask = inet_addr((char*)adr->IpMask.String);
+                opt->iAddress.SetAddress(ntohl(ip));
+                opt->iNetMask.SetAddress(ntohl(mask));
+                opt->iState = EIfUp;
+                opt->iMtu = 1500;
+                opt->iSpeedMetric = 10000;
+                opt->iFeatures = 0;
+                return KErrNone;
+            }
+            else
+            {
+                return KErrNotFound;
+            }
+        default:
+            WTRACE1("GetOption(KSolInetIfCtrl,%08X)",aName);
+            break;
+        }
+        break;
+
+    case KSolInetIfQuery:
+        switch (aName)
+        {
+        case KSoInetIfQueryByName:
+            WTRACE("GetOption(KSoInetIfQueryByName)");
+            if (!iAdapterInfo)
+            {
+                iAdapterInfo = iProtocol->GetAdapterInfo();
+            }
+            if (iAdapterInfo)
+            {
+                TSoInetIfQuery* query = (TSoInetIfQuery*)aOption.Ptr();
+                WinsockIpAdapterInfo* adapter = iAdapterInfo;
+                while (adapter)
+                {
+                    TUint len = User::StringLength(adapter->AdapterName);
+                    HBufC* buf = HBufC::New(len);
+                    if (buf)
+                    {
+                        TPtr16 ptr(buf->Des());
+                        ptr.Copy(TPtrC8(adapter->AdapterName));
+                        if (ptr.Compare(query->iName) == 0)
+                        {
+                            query->iIndex = adapter->Index;
+                            query->iZone[0] = adapter->Index;
+                            query->iZone[1] = iConnectionInfo.iIAPId;
+                            query->iZone[15] = iConnectionInfo.iNetworkId;
+                            for (TInt i=2; i<15; i++) query->iZone[i] = 0;
+                            query->iIsUp = ETrue;
+                            delete buf;
+                            return KErrNone;
+                        }
+                        delete buf;
+                    }
+                    adapter = adapter->Next;
+                }
+            }
+            return KErrNotFound;
+        default:
+            WTRACE1("GetOption(KSolInetIfQuery,%08X)",aName);
+            break;
+        }
+        break;
+
+    case KSolInetIp:
+        switch (aName)
+        {
+        case KSoIpTTL:
+            WTRACE("GetOption(KSoIpTTL)");
+            if (aOption.MaxLength() < sizeof(TInt)) return KErrArgument;
+            aOption.SetLength(len = sizeof(TInt));
+            if (!getsockopt(Socket(), IPPROTO_IP, IP_TTL,
+                (char*)aOption.Ptr(), &len))
+            {
+                aOption.SetLength(len);
+                return KErrNone;
+            }
+            WTRACE1("getsockopt(IP_TTL) error %d",WSAGetLastError());
+            break;
+        case KSoReuseAddr:
+            WTRACE("GetOption(KSoReuseAddr)");
+            if (aOption.MaxLength() < sizeof(TInt)) return KErrArgument;
+            aOption.SetLength(len = sizeof(TInt));
+            if (!getsockopt(Socket(), SOL_SOCKET, SO_REUSEADDR,
+                (char*)aOption.Ptr(), &len))
+            {
+                return KErrNone;
+            }
+            WTRACE1("getsockopt(SO_REUSEADDR) error %d",WSAGetLastError());
+            break;
+        default:
+            WTRACE1("SetOption(KSolInetIp,%08X)",aName);
+            break;
+        }
+        break;
+
+    case KSolInetTcp:
+        if (iProtocol->ProtocolDesc()->iProtocol == KProtocolInetTcp) {
+            switch (aName)
+            {
+            case KSoTcpSendWinSize:
+                WTRACE("GetOption(KSoTcpSendWinSize)");
+                if (aOption.MaxLength() < sizeof(TInt)) return KErrArgument;
+                aOption.SetLength(sizeof(TInt));
+                *((TInt*)aOption.Ptr()) = iTcpSendWinSize;
+                return KErrNone;
+            case KSoTcpRecvWinSize:
+                WTRACE("GetOption(KSoTcpSendWinSize)");
+                if (aOption.MaxLength() < sizeof(TInt)) return KErrArgument;
+                aOption.SetLength(sizeof(TInt));
+                *((TInt*)aOption.Ptr()) = iTcpRecvWinSize;
+                return KErrNone;
+            case KSoTcpMaxSegSize:
+                WTRACE("GetOption(KSoTcpMaxSegSize)");
+                if (aOption.MaxLength() < sizeof(TInt)) return KErrArgument;
+                aOption.SetLength(sizeof(TInt));
+                *((TInt*)aOption.Ptr()) = iTcpMaxSegSize;
+                return KErrNone;
+            case KSoTcpNoDelay:
+                WTRACE("GetOption(KSoTcpNoDelay)");
+                if (aOption.MaxLength() < sizeof(TInt)) return KErrArgument;
+                aOption.SetLength(len = sizeof(TInt));
+                if (!getsockopt(Socket(), IPPROTO_TCP, TCP_NODELAY,
+                    (char*)aOption.Ptr(),&len))
+                {
+                    aOption.SetLength(len);
+                    return KErrNone;
+                }
+                WTRACE1("getsockopt(TCP_NODELAY) error %d",WSAGetLastError());
+                break;
+            case KSoTcpKeepAlive:
+                WTRACE("GetOption(KSoTcpKeepAlive)");
+                if (aOption.MaxLength() < sizeof(TInt)) return KErrArgument;
+                aOption.SetLength(len = sizeof(TInt));
+                if (!getsockopt(Socket(), SOL_SOCKET, SO_KEEPALIVE,
+                    (char*)aOption.Ptr(),&len))
+                {
+                    aOption.SetLength(len);
+                    return KErrNone;
+                }
+                WTRACE1("getsockopt(SO_KEEPALIVE) error %d",WSAGetLastError());
+                break;
+            case KSoTcpSendBytesPending:
+                WTRACE("GetOption(KSoTcpSendBytesPending)");
+                if (aOption.MaxLength() < sizeof(TInt)) return KErrArgument;
+                aOption.FillZ(sizeof(TInt));
+                return KErrNone;
+            // This is a useful one:
+            case KSoTcpReadBytesPending:
+                WTRACE("GetOption(KSoTcpReadBytesPending)");
+                if (aOption.MaxLength() < sizeof(TInt)) return KErrArgument;
+                aOption.SetLength(sizeof(TInt));
+                if (!ioctlsocket(Socket(), FIONREAD, (u_long*)aOption.Ptr()))
+                {
+                    WTRACE1("%d bytes in socket",*((TInt*)aOption.Ptr()));
+                    return KErrNone;
+                }
+                WTRACE1("ioctl(FIONREAD) error %d",WSAGetLastError());
+                break;
+            case KSoTcpListening:
+                WTRACE("GetOption(KSoTcpListening)");
+                if (aOption.MaxLength() < sizeof(TInt)) return KErrArgument;
+                aOption.SetLength(sizeof(TInt));
+                *((TInt*)aOption.Ptr()) = (iFlags & EFlagListen) ? 1 : 0;
+                return KErrNone;
+            case KSoTcpNumSockets:
+                WTRACE("GetOption(KSoTcpNumSockets)");
+                if (aOption.MaxLength() < sizeof(TInt)) return KErrArgument;
+                aOption.SetLength(sizeof(TInt));
+                *((TInt*)aOption.Ptr()) = 1; 
+                return KErrNone;
+            default:
+                WTRACE1("SetOption(KSolInetTcp,%08X) - UNSUPPORTED",aName);
+                break;
+            }
+        } else {
+            WTRACE1("SetOption(KSolInetTcp,%08X) - not a TCP socket!",aName);
+        }
+        break;
+
+    case KSolInetUdp:
+        if (iProtocol->ProtocolDesc()->iProtocol == KProtocolInetUdp) {
+            switch (aName)
+            {
+            case KSoUdpReceiveICMPError:
+                WTRACE("SetOption(KSoUdpReceiveICMPError)");
+                if (aOption.MaxLength() < sizeof(TInt)) return KErrArgument;
+                aOption.SetLength(sizeof(TInt));
+                *((TInt*)aOption.Ptr()) = iUdpReceiveICMPError;
+                return KErrNone;
+            case KSoUdpRecvBuf:
+                WTRACE("SetOption(KSoUdpRecvBuf)");
+                if (aOption.MaxLength() < sizeof(TInt)) return KErrArgument;
+                aOption.SetLength(sizeof(TInt));
+                *((TInt*)aOption.Ptr()) = iUdpRecvBuf;
+                return KErrNone;
+            case KSoUdpSynchronousSend:
+                WTRACE("SetOption(KSoUdpSynchronousSend)");
+                if (aOption.MaxLength() < sizeof(TInt)) return KErrArgument;
+                aOption.SetLength(sizeof(TInt));
+                *((TInt*)aOption.Ptr()) = iUdpSynchronousSend;
+                return KErrNone;
+            default:
+                WTRACE1("SetOption(KSolInetUdp,%08X) - UNSUPPORTED",aName);
+                break;
+            }
+        } else {
+            WTRACE1("SetOption(KSolInetUdp,%08X) - not a UDP socket!",aName);
+        }
+        break;
+
+    default:
+        WTRACE2("GetOption(%d,%08X) - UNSUPPORTED",aLevel,aName);
+        break;
+    }
+    aOption.SetLength(0);
+    return KErrNotSupported;
+}
+
+TBool CWinsockServProvider::Connect()
+{
+    if (Socket(iRemAddr.Family()) == INVALID_SOCKET)
+    {
+        WTRACE("can't connect, no socket");
+        iFlags |= EFlagError;
+        iSocket->Error(KErrCouldNotConnect);
+        return EFalse;
+    }
+    else
+    {
+        WSockAddr sa;
+        int len = ToSockAddr(&sa, &iRemAddr);
+        int err = connect(iWinSocket, &sa.Address, len);
+        if (err)
+        {
+            WTRACE1("connect failed, err %d",WSAGetLastError());
+            switch (WSAGetLastError())
+            {
+            case WSAENETUNREACH:
+                iSocket->Error(KErrNetUnreach);
+                break;
+            case WSAEHOSTUNREACH:
+                iSocket->Error(KErrHostUnreach);
+                break;
+            default:
+                iSocket->Error(KErrCouldNotConnect);
+                break;
+            }
+            iFlags |= EFlagError;
+            return EFalse;
+        }
+        else
+        {
+            iFlags |= EFlagConnect;
+            return ETrue;
+        }
+    }
+}
+
+TInt CWinsockServProvider::Listen(TInt aBacklog)
+{
+    ASSERT(iProtocol->IsStreamProtocol());
+    if (Socket() == INVALID_SOCKET)
+    {
+        WTRACE("can't listen, no socket");
+        return KErrGeneral;
+    }
+    else
+    {
+        int err = listen(iWinSocket, aBacklog);
+        if (err)
+        {
+            WTRACE1("listen failed, err %d",WSAGetLastError());
+            return KErrArgument;
+        }
+        else
+        {
+            return KErrNone;
+        }
+    }
+}
+
+// Creates and submits a new request
+CWinsockServProvider::CSelectRequest*
+CWinsockServProvider::CreateAndSubmitNewRequestL()
+{
+    CSelectRequest* request = CSelectRequest::NewLC(this);
+    request->SubmitL();
+    CleanupStack::Pop(request);
+    return request;
+}
+
+// Schedules CWriteRequest
+TBool CWinsockServProvider::ScheduleWriteRequest(TInt aDelay)
+{
+    if (!iWrite)
+    {
+        iWrite = new CWriteRequest(this);
+        if (!iWrite)
+        {
+            return EFalse;
+        }
+    }
+    iWrite->Submit(aDelay);
+    return ETrue;
+}
+
+// Schedules CNewDataNotifier
+TBool CWinsockServProvider::ScheduleNewDataCheck()
+{
+    if (!iNewData)
+    {
+        iNewData = new CNewDataNotifier(this);
+        if (!iNewData)
+        {
+            return EFalse;
+        }
+    }
+    iNewData->Submit();
+    return ETrue;
+}
+
+TInt CWinsockServProvider::Select()
+{
+    TInt err = KErrNone;
+    if (!iSelect)
+    {
+        TRAP(err,iSelect = CreateAndSubmitNewRequestL());
+    }
+
+    ASSERT(iSelect || err != KErrNone);
+    if (iSelect && !iSelect->IsActive())
+    {
+        TRAP(err, iSelect->SubmitL());
+    }
+    return err;
+}
+
+void CWinsockServProvider::ActiveOpen()
+{
+#ifdef _REALLY_DEBUG
+    TBuf<KMaxInetAddrBufSize> buf;
+    iRemAddr.OutputWithScope(buf);
+    WTRACE2("ActiveOpen(%S,%d)",&buf,iRemAddr.Port());
+#endif // _REALLY_DEBUG
+    ASSERT(iProtocol->IsStreamProtocol());
+    if (Connect())
+    {
+        iSocket->ConnectComplete();
+        TInt err = Select();
+        if (err != KErrNone)
+        {
+            iFlags |= EFlagError;
+            iSocket->Error(err);
+        }
+    }
+}
+
+void CWinsockServProvider::ActiveOpen(const TDesC8& /*aConnectionData*/)
+{
+    ASSERT(FALSE);
+    iSocket->Error(KErrNotSupported);
+}
+
+TInt CWinsockServProvider::PassiveOpen(TUint aQueSize)
+{
+    WTRACE1("PassiveOpen(%d)",aQueSize);
+    ASSERT(iProtocol->IsStreamProtocol());
+    TInt err = Listen(aQueSize);
+    if (err == KErrNone)
+    {
+        iFlags |= EFlagListen;
+        err = Select();
+    }
+    return err;
+}
+
+TInt CWinsockServProvider::PassiveOpen(TUint aQueSize,const TDesC8& /*aData*/)
+{
+    ASSERT(FALSE);
+    return PassiveOpen(aQueSize);
+}
+
+void CWinsockServProvider::Shutdown(TCloseType anOption)
+{
+    WTRACE1("Shutdown(%08X)",anOption);
+    int how;
+    switch (anOption)
+    {
+    default:
+    case EImmediate:
+    case ENormal: how = SD_BOTH; break;
+    case EStopInput: how = SD_RECEIVE; break;
+    case EStopOutput: how = SD_SEND; break;
+    }
+    if (iWinSocket != INVALID_SOCKET)
+    {
+        shutdown(iWinSocket, how);
+    }
+    iFlags |= EFlagShutdown;
+    if (!(iFlags & EFlagError) && anOption != EImmediate)
+    {
+        // CSocket panics if we call CanClose() after reporting an error.
+        // It also gets pretty upset if we call CanClose() from Shutdown
+        // call with EImmediate parameter.
+        iSocket->CanClose();
+    }
+}
+
+void CWinsockServProvider::Shutdown(TCloseType anOption,const TDesC8& /*aData*/)
+{
+    Shutdown(anOption);
+}
+
+void CWinsockServProvider::AutoBind()
+{
+    if (Socket() == INVALID_SOCKET)
+    {
+        WTRACE("AutoBind - no socket");
+    }
+    else
+    {
+        WTRACE("AutoBind");
+        TInetAddr bindAddr;
+        bindAddr.Init(Family());
+        WSockAddr sa;
+        int len = WinsockUtils::ToSockAddr(&sa, &bindAddr);
+        int err = bind(iWinSocket, &sa.Address, len);
+        if (err)
+        {
+            WTRACE1("bind failed, err %d",WSAGetLastError());
+            iFlags |= EFlagError;
+            iSocket->Error(err);
+        }
+    }
+}
+
+// The reason for not writing the data immediately is that socket server
+// is processing both read and write requests on the same thread. If we
+// are writing data faster than we are reading them, eventually send
+// will block forever. We try to give the reader a chance.
+void CWinsockServProvider::DoWrite()
+{
+    if (!iWriteQ.IsEmpty())
+    {
+        ASSERT(iWinSocket != INVALID_SOCKET);
+
+        // Check if the socket is writable
+        TBool canWrite = ETrue;
+        fd_set writefs;
+        FD_ZERO(&writefs);
+        FD_SET(iWinSocket,&writefs);
+        struct timeval tv;
+        tv.tv_sec = 0;
+        tv.tv_usec = 0;
+
+        BEGIN_WIN32();
+        int nfd = select(iWinSocket+1, NULL, &writefs, NULL, &tv);
+        END_WIN32();
+
+        if (nfd == SOCKET_ERROR)
+        {
+            WTRACE1("select err %d",WSAGetLastError());
+            iSocket->Error(KErrWrite);
+        }
+        else
+        {
+            if (FD_ISSET(iWinSocket,&writefs))
+            {
+                // Send exactly one block of data
+                CWriteData* data = iWriteQ.First();
+                data->iLink.Deque();
+                TUint nBytes = SendNow(*data->iData, data->iAddr);
+                if (nBytes)
+                {
+                    WTRACE1("sent %d bytes",nBytes);
+                    iProtocol->DataSent(nBytes);
+                }
+                delete data;
+
+                // Have more data to send?
+                if (!iWriteQ.IsEmpty())
+                {
+                    ScheduleWriteRequest(0);
+                }
+            }
+            else
+            {
+                // Delay the retry to ease the CPU load and give the
+                // reader a chance to catch up
+                WTRACE("socket is not writable, waiting...");
+                ScheduleWriteRequest(500000);
+            }
+        }
+    }
+}
+
+TUint CWinsockServProvider::SendLater(const TDesC8& aData, TSockAddr* aAddr)
+{
+    CWriteData* data = CWriteData::New(aData, aAddr);
+    if (data)
+    {
+        iWriteQ.AddLast(*data);
+        ScheduleWriteRequest(0);
+        return aData.Length();
+    }
+    return 0;
+}
+
+TUint CWinsockServProvider::SendNow(const TDesC8& aData, TSockAddr* aAddr)
+{
+    int nbytes;
+    const char * buf = (char*)aData.Ptr();
+    int len = aData.Length();
+    if (iFlags & EFlagConnect)
+    {
+        BEGIN_WIN32();
+        nbytes = send(iWinSocket, buf, len, 0);
+        END_WIN32();
+    }
+    else
+    {
+        ASSERT(aAddr);
+        WSockAddr to;
+        int tolen = WinsockUtils::ToSockAddr(&to, aAddr);
+
+        BEGIN_WIN32();
+        nbytes = sendto(Socket(aAddr->Family()),buf,len,0,&to.Address,tolen);
+        END_WIN32();
+
+        if (nbytes < len) nbytes = 0;
+    }
+    if (nbytes == SOCKET_ERROR)
+    {
+        WTRACE1("send failed, err %d",WSAGetLastError());
+        iSocket->Error(KErrWrite);
+        return 0;
+    }
+    else
+    {
+        if (nbytes < len)
+        {
+            iSocket->Error(KErrWrite);
+        }
+        return nbytes;
+    }
+}
+
+TUint CWinsockServProvider::Write(const TDesC8& aData, TUint, TSockAddr* aAddr)
+{
+    if (Socket() == INVALID_SOCKET)
+    {
+        WTRACE("Write - no socket");
+        iSocket->Error(KErrWrite);
+        return 0;
+    }
+    else
+    {
+        WTRACE1("sending %d bytes",aData.Length());
+        if (!iWriteQ.IsEmpty() || iProtocol->ReadPending())
+        {
+            // If have asynchronous writes pending, we have to send this
+            // chunk of data asynchronously too, otherwise the order of
+            // chunks will change. If there's any unread data in one of
+            // the sockets, we also want to be asynchronous to avoid
+            // starving the reading thread, which can result in filling
+            // up TCP buffer, blocking socket server thread and finally
+            // deadlock.
+            return SendLater(aData, aAddr);
+        }
+        else
+        {
+            // No read or writes pending, send it now.
+            return SendNow(aData, aAddr);
+        }
+    }
+}
+
+#ifdef V1_5_PRT_INTERFACE
+
+TInt CWinsockServProvider::Receive(TDes8& aData, TUint aOpt, TSockAddr* aAddr)
+{
+    ASSERT(aData.Length() > 0);
+    ASSERT(!(iFlags & (EFlagEndOfData | EFlagShutdown)));
+
+    WSockAddr from;
+    int maxbytes = Min(aData.Length(), iDataAvailable);
+    char* buf = (char*)&aData[0];
+
+    int retval;     // Return value from recv or recvfrom
+    int nbytes;     // How much is actually in the buffer
+
+    ASSERT(!(aOpt & KIpHeaderIncluded));        // Option is not implemented
+    // ASSERT(!(aOpt & KSockReadContinuation)); // ignore, does not affect operation
+    int opt = ((aOpt & KSockReadPeek) ? MSG_PEEK : 0);
+
+    if (aAddr)
+    {
+        int fromlen = SOCKADDR_SIZE;
+        retval = recvfrom(Socket(), buf, maxbytes, opt, (struct sockaddr*)
+            &from, &fromlen);
+    }
+    else
+    {
+        // No address information is required, probably a TCP socket
+        retval = recv(Socket(), buf, maxbytes, opt);
+    }
+
+    if (retval == SOCKET_ERROR && WSAGetLastError() == WSAEMSGSIZE)
+    {
+        ASSERT( EFalse ); // should not happen anymore; whole datagram is read
+        WTRACE1("datagram truncated to %d bytes",maxbytes);
+        nbytes = maxbytes;
+    }
+    else
+    {
+        nbytes = retval;
+    }
+
+    if (nbytes == SOCKET_ERROR)
+    {
+        WTRACE1("recv err %d",WSAGetLastError());
+        iFlags |= EFlagError;
+        aData.SetLength(0);
+        retval = (iProtocol->IsStreamProtocol()) ? KErrDisconnected : KErrGeneral;
+    }
+    else if (retval == 0)
+    {
+        // the connection has been gracefully closed
+        iFlags |= EFlagEndOfData;
+        aData.SetLength(0);
+        WTRACE("end of data");
+        ScheduleNewDataCheck();
+    }
+    else
+    {
+        if (aAddr) WinsockUtils::ToInetAddr(aAddr, &from);
+        aData.SetLength(nbytes);
+
+        if (iProtocol->IsStreamProtocol())
+        {
+            ASSERT(iDataAvailable >= nbytes);
+            iDataAvailable -= nbytes;
+        }
+        else
+        {
+            // In case of UDP we always indicate no more than one datagram.
+            // After socket server has called our GetData function, it thinks
+            // that it has received the only available datagram and that we
+            // no longer have any data in the receive queue. We need
+            // to reset iDataAvailable to zero and start from scratch.
+            iDataAvailable = 0;
+            
+            retval = 1; // indicate, that one datagram was read
+        }
+
+        if (iDataAvailable <= 0)
+        {
+            // Find out how much data still left
+            TUint32 size = 0;
+            int err = ioctlsocket(iWinSocket, FIONREAD, &size);
+            iProtocol->SetReadPending(this, !err && (size > 0));
+            if (err == SOCKET_ERROR)
+            {
+                WTRACE1("ioctl(FIONREAD) err %d",WSAGetLastError());
+                iFlags |= EFlagError;
+                retval = KErrGeneral;
+            }
+            else
+            {
+                TInt bytesInSocket = size;
+                WTRACE2("%d bytes in socket, %d bytes unread",
+                    bytesInSocket, iDataAvailable);
+                if (bytesInSocket > iDataAvailable)
+                {
+                    // iSocket->NewData is not reentrant. If we call it from
+                    // here, it will get into an infinite loop
+                    WTRACE("scheduling new data check");
+                    ScheduleNewDataCheck();
+                }
+                else
+                {
+                    // Re-submit the select request
+                    TInt err = Select();
+                    if (err != KErrNone)
+                    {
+                        iFlags |= EFlagError;
+                        retval = KErrGeneral;
+                    }
+                }
+            }
+        }
+    }
+    return retval; // error-code, nbytes (if stream) or 1 (if dgram)
+}
+
+TInt CWinsockServProvider::GetData(RMBufChain& aData, TUint aLength,
+                                   TUint aOptions, TSockAddr* aAddr)
+{
+    if (!iDataAvailable)
+    {
+        // This should only happen for datagram protocols
+        WTRACE1("%d bytes requested, nothing is available",
+               aLength & ~KGetDataWholeDatagram);
+        ASSERT(iProtocol->IsDatagramProtocol());
+        return KErrNotReady;
+    }
+    else if (Socket() == INVALID_SOCKET)
+    {
+        WTRACE("GetData - no socket");
+        return KErrGeneral;
+    }
+    else
+    {
+        WTRACE2("%d bytes requested, %d bytes unread", 
+               (aLength & ~KGetDataWholeDatagram), iDataAvailable);
+
+        if (aLength & KGetDataWholeDatagram)
+        {
+            // we're asked to read the whole dgram, even if client wants less
+            aLength &= ~KGetDataWholeDatagram;
+            aLength = Max(aLength, iDataAvailable);
+        }
+        
+        // alloc buffer
+        TInt extraNeeded = aLength - aData.Length();
+        if (extraNeeded > 0)
+        {
+            TRAPD(err, aData.AppendL(extraNeeded));
+            if (err != KErrNone)
+            {
+                return KErrNoMBufs;
+            }
+        }
+
+        // the new GetData-inteface with RMBufChain-param (i.e. v1.5 PRT inter-
+        // face) is somewhat problematic from Winsock's point of view.
+        // RMBufChain is essentially a linked list of buffers. But especially
+        // when UDP-datagram bigger than the a single buffer is being read,
+        // the windows-socket recvfrom-operation has to be done in a single
+        // operation - otherwise datagram gets truncated.
+        // Therefore we have to have a local buffer where to read the data - 
+        // and then copy it into RMBufChain.
+        //
+        // reference: \src\common\generic\comms-infras\documentation\
+        // Comms Framework Migration Guide for NIFs and v1.5 PRTs.doc
+        //
+        // The code below is more or less copied from 
+        // \src\common\generic\comms-infras\esock\ssock\SS_PROT.CPP
+
+        if(!iDataBuf)
+        {
+            iDataBuf = HBufC8::New(aLength);
+            if(!iDataBuf)
+            {
+                return KErrNoMemory;
+            }
+        }
+        else if(iDataBuf->Size() < TInt(aLength))
+        {
+            // We need extra room for data. As ReAlloc returns NULL if fails, 
+            // we need to temporary save the previous storage in order to 
+            // clear it in the case of ReAlloc failure.
+            HBufC8* aTempPtr = iDataBuf;
+            iDataBuf = iDataBuf->ReAlloc(aLength);
+            if(!iDataBuf)
+            {
+                delete aTempPtr;
+                return KErrNoMemory;
+            }
+        }
+        TPtr8 des = iDataBuf->Des();
+        des.SetLength(aLength);
+
+        TInt retval = Receive( des, aOptions, aAddr );
+
+        TInt bytesReceived = des.Length();
+        aData.CopyIn(des);
+
+        if(des.Length() < TInt(aLength))
+        {
+            aData.TrimEnd(des.Length());
+        }
+        WTRACE2("received %d bytes, %d bytes left", bytesReceived, iDataAvailable);
+        if (bytesReceived > 0)
+        {
+            iProtocol->DataReceived(bytesReceived);
+        }
+
+        return retval;
+    }
+}
+
+#else // PRT v1.0 interface
+
+void CWinsockServProvider::Receive(TDes8& aDesc, TUint aOpt, TSockAddr* aAddr)
+{
+    ASSERT(aDesc.Length() > 0);
+    ASSERT(!(iFlags & (EFlagEndOfData | EFlagShutdown)));
+
+    WSockAddr from;
+    int maxbytes = Min(aDesc.Length(), iDataAvailable);
+    char* buf = (char*)&aDesc[0];
+
+    int retval;     // Return value from recv or recvfrom
+    int nbytes;     // How much is actually in the buffer
+
+    ASSERT(!(aOpt & KIpHeaderIncluded));            // Option is not implemented
+    ASSERT(!(aOpt & KSockReadContinuation));        // Option is not implemented
+    int opt = ((aOpt & KSockReadPeek) ? MSG_PEEK : 0);
+
+    if (aAddr)
+    {
+        int fromlen = SOCKADDR_SIZE;
+        retval = recvfrom(Socket(), buf, maxbytes, opt, (struct sockaddr*)
+            &from, &fromlen);
+    }
+    else
+    {
+        // No address information is required, probably a TCP socket
+        retval = recv(Socket(), buf, maxbytes, opt);
+    }
+
+    // How to handle truncated datagrams?
+    if (retval == SOCKET_ERROR && WSAGetLastError() == WSAEMSGSIZE)
+    {
+        WTRACE1("datagram truncated to %d bytes",maxbytes);
+        nbytes = maxbytes;
+    }
+    else
+    {
+        nbytes = retval;
+    }
+
+    if (nbytes == SOCKET_ERROR)
+    {
+        WTRACE1("recv err %d",WSAGetLastError());
+        iFlags |= EFlagError;
+        aDesc.SetLength(0);
+        if (iProtocol->IsStreamProtocol())
+        {
+            iSocket->Error(KErrDisconnected);
+        }
+        else
+        {
+            iSocket->Error(KErrGeneral);
+        }
+    }
+    else if (retval == 0)
+    {
+        // the connection has been gracefully closed
+        iFlags |= EFlagEndOfData;
+        aDesc.SetLength(0);
+        WTRACE("end of data");
+        ScheduleNewDataCheck();
+    }
+    else
+    {
+        if (aAddr) WinsockUtils::ToInetAddr(aAddr, &from);
+        aDesc.SetLength(nbytes);
+
+        if (iProtocol->IsStreamProtocol())
+        {
+            ASSERT(iDataAvailable >= nbytes);
+            iDataAvailable -= nbytes;
+        }
+        else
+        {
+            // In case of UDP we always indicate no more than one datagram.
+            // After socket server has called our GetData function, it thinks
+            // that it has received the only available datagram and that we
+            // no longer have any data in the receive queue. Also, datagram
+            // may have been truncated and in that case we don't really know
+            // how much data are still sitting in the receive queue. We need
+            // to reset iDataAvailable to zero and start from scratch.
+            iDataAvailable = 0;
+        }
+        if (iDataAvailable <= 0)
+        {
+            // Find out how much data still left
+            TUint32 size = 0;
+            int err = ioctlsocket(iWinSocket, FIONREAD, &size);
+            iProtocol->SetReadPending(this, !err && (size > 0));
+            if (err == SOCKET_ERROR)
+            {
+                WTRACE1("ioctl(FIONREAD) err %d",WSAGetLastError());
+                iFlags |= EFlagError;
+                iSocket->Error(KErrGeneral);
+            }
+            else
+            {
+                TInt bytesInSocket = size;
+                WTRACE2("%d bytes in socket, %d bytes unread",
+                    bytesInSocket, iDataAvailable);
+                if (bytesInSocket > iDataAvailable)
+                {
+                    // iSocket->NewData is not reentrant. If we call it from
+                    // here, it will get into an infinite loop
+                    WTRACE("scheduling new data check");
+                    ScheduleNewDataCheck();
+                }
+                else
+                {
+                    // Re-submit the select request
+                    TInt err = Select();
+                    if (err != KErrNone)
+                    {
+                        iFlags |= EFlagError;
+                        iSocket->Error(KErrGeneral);
+                    }
+                }
+            }
+        }
+    }
+}
+
+void CWinsockServProvider::GetData(TDes8& aDesc, TUint aOptions, TSockAddr* aAddr)
+{
+    if (Socket() == INVALID_SOCKET)
+    {
+        WTRACE("GetData - no socket");
+        iSocket->Error(KErrGeneral);
+    }
+    else
+    {
+        WTRACE2("%d bytes requested, %d bytes unread",
+               aDesc.Length(),iDataAvailable);
+        if (!iDataAvailable)
+        {
+            aDesc.SetLength(0);
+        }
+        else
+        {
+            Receive(aDesc, aOptions, aAddr);
+            TInt bytesReceived = aDesc.Length();
+            WTRACE2("received %d bytes, %d bytes left",
+                   bytesReceived, iDataAvailable);
+            if (bytesReceived > 0)
+            {
+                iProtocol->DataReceived(bytesReceived);
+            }
+        }
+    }
+}
+
+TInt CWinsockServProvider::GetData(RMBufChain& aData, TUint aLength,
+                                   TUint aOptions, TSockAddr* anAddr)
+{
+    if (!iDataAvailable)
+    {
+        // This should only happen for datagram protocols
+        WTRACE1("%d bytes requested, nothing is available",
+               aLength & ~KGetDataWholeDatagram);
+        ASSERT(iProtocol->IsDatagramProtocol());
+        return KErrNotReady;
+    }
+
+    // The base class never returns an error
+    TInt ret = SUPER::GetData(aData, aLength, aOptions, anAddr);
+    if (iFlags & EFlagError)
+    {
+        if (iProtocol->IsDatagramProtocol())
+        {
+            return KErrGeneral;
+        }
+        else
+        {
+            return KErrDisconnected;
+        }
+    }
+    else
+    {
+        return ret;
+    }
+}
+
+#endif // V1_5_PRT_INTERFACE
+
+// upcall from CSelectRequest invoked in the context of the thread
+// submitted the request
+void CWinsockServProvider::SelectComplete(TInt aMask)
+{
+    if (aMask & ESelectError)
+    {
+        if (!(iFlags & EFlagError))
+        {
+            iFlags |= EFlagError;
+            iSocket->Error(KErrDisconnected);
+        }
+    }
+    else if (!(iFlags & EFlagError) && (aMask & ESelectRead))
+    {
+        if (iFlags & EFlagListen)
+        {
+            // Accept new connection
+            WSockAddr addr;
+            int addrlen = SOCKADDR_SIZE;
+            SOCKET sock = accept(iWinSocket,&addr.Address,&addrlen);
+            if (sock == INVALID_SOCKET)
+            {
+                // ignore this error
+                WTRACE1("accept failed, err %d",WSAGetLastError());
+            }
+            else
+            {
+                WTRACE("accepted incoming connection");
+                CWinsockServProvider* newSock = CWinsockServProvider::
+                    FromSocket(iProtocol, sock, iFamily);
+                if (newSock)
+                {
+                    iSocket->ConnectComplete(*newSock);
+                }
+            }
+
+            // Re-submit the request
+            TInt err = Select();
+            if (err != KErrNone)
+            {
+                iFlags |= EFlagError;
+                iSocket->Error(KErrCouldNotConnect);
+            }
+        }
+        else
+        {
+            NewDataCheck();
+        }
+    }
+}
+
+// Upcall from CNewDataNotifier. Also invoked from SelectComplete
+void CWinsockServProvider::NewDataCheck()
+{
+    if (iFlags & EFlagShutdown)
+    {
+        return;
+    }
+
+    if (iFlags & EFlagEndOfData)
+    {
+        if (!(iFlags & EFlagDisconnected))
+        {
+            WTRACE("indicating end of data");
+            iSocket->NewData(KNewDataEndofData);
+        }
+        return;
+    }
+
+    TUint32 size = 0;
+    ASSERT(iWinSocket != INVALID_SOCKET);
+    int err = ioctlsocket(iWinSocket, FIONREAD, &size);
+    iProtocol->SetReadPending(this, !err && (size > 0));
+    if (err == SOCKET_ERROR)
+    {
+        WTRACE1("ioctl(FIONREAD) err %d",WSAGetLastError());
+        iFlags |= EFlagError;
+        iSocket->Error(KErrGeneral);
+    }
+    else
+    {
+        TInt bytesInSocket = size;
+        WTRACE2("%d bytes in socket, %d bytes unread",
+            bytesInSocket, iDataAvailable);
+        if (bytesInSocket > iDataAvailable)
+        {
+            // Report more data
+            TInt newData = bytesInSocket - iDataAvailable;
+            iDataAvailable = bytesInSocket;
+            if (iProtocol->IsStreamProtocol())
+            {
+                WTRACE1("indicating %d bytes",newData);
+                iSocket->NewData(newData);
+            }
+            else
+            {
+                WTRACE("indicating (at least) 1 datagram");
+                iSocket->NewData(1);
+            }
+        }
+        else if (iProtocol->IsStreamProtocol())
+        {
+            // For connection-oriented sockets, readability may indicate that
+            // a request to close the socket has been received from the peer.
+            // If the connection was closed gracefully, and all data was
+            // received, then a recv will return immediately with zero bytes
+            // read. If the connection was reset, then a recv will complete
+            // immediately with an error code
+            if (!bytesInSocket && !iDataAvailable)
+            {
+                BEGIN_WIN32();
+                char buf;
+                int ret = recv(iWinSocket, &buf, 1, 0);
+                END_WIN32();
+
+                if (ret == 0)
+                {
+                    iFlags |= EFlagEndOfData;
+                    WTRACE("end of data, I guess");
+                    iSocket->NewData(KNewDataEndofData);
+                }
+                else if (ret == SOCKET_ERROR)
+                {
+                    WTRACE1("recv err %d",WSAGetLastError());
+                    iFlags |= EFlagError;
+                    iSocket->Error(KErrDisconnected);
+                }
+                else
+                {
+                    // This means that we were wrong about the connection
+                    // being either reset or close. Too bad...
+                    WTRACE("oops... didn't expect any data");
+                    ASSERT(FALSE);
+                    iFlags |= EFlagError;
+                    iSocket->Error(KErrGeneral);
+                }
+            }
+        }
+    }
+}
+
+// CWinsockServProvider::CSelectRequest
+CWinsockServProvider::CSelectRequest*
+CWinsockServProvider::CSelectRequest::NewLC(CWinsockServProvider* aProvider)
+{
+    CSelectRequest* self = new(ELeave)CSelectRequest(aProvider);
+    CleanupStack::PushL(self);
+    self->ConstructL();
+    return self;
+}
+
+CWinsockServProvider::CSelectRequest::CSelectRequest(CWinsockServProvider* aP) :
+CActive(EPriorityStandard), iProvider(aP)
+{
+    CActiveScheduler::Add(this);
+}
+
+CWinsockServProvider::CSelectRequest::~CSelectRequest()
+{
+    iServerThread.Close();
+}
+
+void CWinsockServProvider::CSelectRequest::ConstructL()
+{
+    LEAVE_IF_ERROR(iServerThread.Open(RThread().Id()));
+}
+
+void CWinsockServProvider::CSelectRequest::SubmitL()
+{
+    if (!IsActive())
+    {
+        iStatus = KRequestPending;
+        SetActive();
+    }
+    LEAVE_IF_ERROR(CWinsockSelectThread::StaticL().Submit(this));
+}
+
+TUint CWinsockServProvider::CSelectRequest::Socket()
+{
+    ASSERT(iProvider->iWinSocket != INVALID_SOCKET);
+    return iProvider->iWinSocket;
+}
+
+TInt CWinsockServProvider::CSelectRequest::SelectMask()
+{
+    return (ESelectRead|ESelectError);
+}
+
+void CWinsockServProvider::CSelectRequest::SelectComplete(TInt aSelectMask)
+{
+    iSelectMask = aSelectMask;
+    TRequestStatus *status = (&iStatus);
+    iServerThread.RequestComplete(status, KErrNone);
+}
+
+void CWinsockServProvider::CSelectRequest::DoCancel()
+{
+    TRequestStatus *status = (&iStatus);
+    User::RequestComplete(status, KErrCancel);
+}
+
+void CWinsockServProvider::CSelectRequest::RunL()
+{
+    iProvider->SelectComplete(iSelectMask);
+}
+
+// CWinsockServProvider::CWriteData
+CWinsockServProvider::CWriteData*
+CWinsockServProvider::CWriteData::New(const TDesC8& aData, TSockAddr* aAddr)
+{
+    CWriteData* data = new(CWriteData);
+    if (data)
+    {
+        data->iData = aData.Alloc();
+        if (data->iData)
+        {
+            if (aAddr)
+            {
+                data->iAddr = new(TSockAddr);
+                if (data->iAddr) (*data->iAddr) = *aAddr;
+            }
+            if (!aAddr || data->iAddr)
+            {
+                return data;
+            }
+        }
+        delete data;
+    }
+    return NULL;
+}
+
+CWinsockServProvider::CWriteData::~CWriteData()
+{
+    delete iAddr;
+    delete iData;
+}
+
+// CWinsockServProvider::CWriteRequest
+CWinsockServProvider::CWriteRequest::CWriteRequest(CWinsockServProvider* aP) :
+CActive(EPriorityStandard), iProvider(aP)
+{
+    CActiveScheduler::Add(this);
+}
+
+CWinsockServProvider::CWriteRequest::~CWriteRequest()
+{
+    Cancel();
+    if (iTimerCreated)
+    {
+        iTimer.Close();
+    }
+}
+
+void CWinsockServProvider::CWriteRequest::Submit(TInt aDelay)
+{
+    if (aDelay)
+    {
+        if (!iTimerCreated)
+        {
+            // Create timer first time we need a delay
+            TInt err = iTimer.CreateLocal();
+            if (err == KErrNone) iTimerCreated = ETrue;
+        }
+        if (iTimerCreated)
+        {
+            Cancel();
+            SetActive();
+            iTimer.After(iStatus, aDelay);
+            iTimerActive = ETrue;
+            return;
+        }
+    }
+    if (!IsActive())
+    {
+        // Complete request immediately
+        SetActive();
+        TRequestStatus *status = (&iStatus);
+        User::RequestComplete(status, KErrCancel);
+    }
+}
+
+void CWinsockServProvider::CWriteRequest::DoCancel()
+{
+    TRequestStatus *status = (&iStatus);
+    if (iTimerActive)
+    {
+        iTimerActive = EFalse;
+        iTimer.Cancel();
+    }
+    else
+    {
+        User::RequestComplete(status, KErrCancel);
+    }
+}
+
+void CWinsockServProvider::CWriteRequest::RunL()
+{
+    iTimerActive = EFalse;
+    iProvider->DoWrite();
+}
+
+// CWinsockServProvider::CNewDataNotifier
+CWinsockServProvider::CNewDataNotifier::CNewDataNotifier(CWinsockServProvider* aP) :
+CActive(EPriorityStandard), iProvider(aP)
+{
+    CActiveScheduler::Add(this);
+}
+
+void CWinsockServProvider::CNewDataNotifier::Submit()
+{
+    if (!IsActive())
+    {
+        SetActive();
+        TRequestStatus *status = (&iStatus);
+        User::RequestComplete(status, KErrCancel);
+    }
+}
+
+void CWinsockServProvider::CNewDataNotifier::DoCancel()
+{
+    TRequestStatus *status = (&iStatus);
+    User::RequestComplete(status, KErrCancel);
+}
+
+void CWinsockServProvider::CNewDataNotifier::RunL()
+{
+    iProvider->NewDataCheck();
+}
+
+/**
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */