sdkcreationmw/sdkruntimes/wsock/src/WinsockServProvider.cpp
author rajpuroh
Wed, 21 Apr 2010 09:56:53 +0530
changeset 1 ac50fd48361b
parent 0 b26acd06ea60
permissions -rw-r--r--
Second Contribution

/*
* 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 the License "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:
 */