//####COPYRIGHTBEGIN####
//                                                                          
// ----------------------------------------------------------------------------
// Copyright (C) 1998, 1999, 2000 Red Hat, Inc.
//
// This program is part of the eCos host tools.
//
// This program is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the Free 
// Software Foundation; either version 2 of the License, or (at your option) 
// any later version.
// 
// This program is distributed in the hope that it will be useful, but WITHOUT 
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
// more details.
// 
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 
// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// ----------------------------------------------------------------------------
//                                                                          
//####COPYRIGHTEND####
//=================================================================
//
//        eCosTestSocket.cpp
//
//        Socket test class
//
//=================================================================
//=================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):     sdf
// Contributors:  sdf
// Date:          1999-04-01
// Description:   This class abstracts tcp/ip sockets for use in the testing infrastructure
// Usage:
//
//####DESCRIPTIONEND####

#include "eCosStd.h"
#include "eCosTestSocket.h"
#include "eCosTestSerial.h"
#include "eCosTrace.h"

enum {ERR_TIMEOUT=20000, ERR_READ_AFTER_CLOSE=20001};

// Blocking read on one or other of the data sources:
// Result:  0 - error occurred
//          1 - data read from socket
//          2 - data read from serial
int CeCosTestSocket::SSRead (CeCosTestSerial &serial,CeCosTestSocket &socket,void *pBuf,unsigned int nSize,unsigned int &nRead,bool *pbStop)
{
    int rc=0;
    bool bBlocking=serial.GetBlockingReads();
    bool bBlockingModified=false;
    while(0==pbStop || !(*pbStop)){
        if(!socket.Peek(nRead)){
            break;
        } else if(nRead){
            nRead=MIN(nRead,nSize);
            rc=socket.recv(pBuf,nRead)?1:0;
            break;
        } else {
            if(bBlocking){
                serial.SetBlockingReads(false);
                bBlockingModified=true;
                bBlocking=false;
            }
            if(serial.Read(pBuf,nSize,nRead)){
                if(nRead>0){
                    rc=2;
                    break;
                }
            } else {
                break;
            }
        }
        Sleep(1);
    }
    if(bBlockingModified){
        serial.SetBlockingReads(true);
    }
    return rc;
}

// ctors and dtors

CeCosTestSocket::CeCosTestSocket ():
    m_nDefaultTimeout(10*1000),
	m_nSock(-1),
    m_nClient(0)
{
    TRACE(_T("Create socket instance %08x\n"),(unsigned int)this);
}

CeCosTestSocket::CeCosTestSocket (int sock /*result of previous call of Listen*/, bool *pbStop):
    m_nDefaultTimeout(10*1000),
	m_nSock(-1),
    m_nClient(0)
{
    TRACE(_T("Create socket instance %08x\n"),(unsigned int)this);
    Accept(sock,pbStop);
}

CeCosTestSocket::CeCosTestSocket (String strHost,int port,Duration dTimeout):
    m_nDefaultTimeout(10*1000),
	m_nSock(-1),
    m_nClient(0)
{
    TRACE(_T("Create socket instance %08x\n"),(unsigned int)this);
    Connect(strHost,port,dTimeout);
}

bool CeCosTestSocket::Accept(int sock /*result of previous call of Listen*/, bool *pbStop)
{
    m_nSock=-1;
    while(0==pbStop||!*pbStop){
        struct sockaddr cli_addr;
        #ifndef _WIN32
        unsigned 
        #endif
	    int clilen=sizeof(struct sockaddr);
        m_nSock=::accept(sock, (struct sockaddr *) &cli_addr, &clilen);
        SaveError();
        if(-1==m_nSock){ 
            if(WOULDBLOCK==SocketError()){
                Sleep(100);
                continue;
            }
        } else {
            memcpy(&m_nClient,cli_addr.sa_data+2,4);
            TRACE(_T("Connection accepted from %s - socket %d\n"),(LPCTSTR )ClientName(m_nClient),m_nSock);
            SetSocketOptions();
            break;
        }
	} 
    return -1!=m_nSock;
}

int CeCosTestSocket::Listen(int nTcpPort)
{
   // Create socket
    int sock=::socket(AF_INET, SOCK_STREAM, 0);
    if (sock == -1) {
        ERROR(_T("Couldn't create socket\n"));
    } else {
        TRACE(_T("Created socket %d listening on port %d\n"),sock,nTcpPort);
        // Bind socket to address
        struct sockaddr_in serv_addr;
        memset(&serv_addr, 0, sizeof serv_addr);

        serv_addr.sin_family = AF_INET;
        serv_addr.sin_port=htons((short)nTcpPort);
        serv_addr.sin_addr.s_addr = INADDR_ANY;
        
        if (::bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {
            TRACE(_T("Couldn't bind socket on port %d\n"),nTcpPort);
            CloseSocket(sock);
        } else if (-1==::listen(sock, SOMAXCONN)){
            CloseSocket(sock);
            TRACE(_T("socket error on listen - port %d\n"),nTcpPort);
        } else {
	        #ifdef _WIN32
            int nTrue=1;
            bool rc=(0==::ioctlsocket(sock, FIONBIO, (unsigned long *)&nTrue));
	        #else //UNIX
            int flags=::fcntl(sock,F_GETFL);
	          flags|=O_NONBLOCK;
            bool rc=(0==::fcntl (sock, F_SETFL, flags));
	        #endif
	        if(!rc){
                TRACE(_T("Failed to set socket options on socket %d\n"),sock);
            }
        }
    }
    return sock;
}

bool CeCosTestSocket::Connect(String strHost,int port,Duration dTimeout)
{
    dTimeout=TimeoutDuration(dTimeout);
    struct sockaddr_in serv_addr;

    struct hostent* host_dat;

    char ip[16]; // for nnn.nnn.nnn.nnn

    VTRACE(_T("Connect: %s:%d timeout=%d\n"),(LPCTSTR )strHost,port,dTimeout);

    // Get the target host address
    String strErr;
    char *pszHost=strHost.GetCString();
    if (0==(host_dat=::gethostbyname(pszHost))){
        SaveError();
        TRACE(_T("Could not get IP address for host %s - %s\n"),(LPCTSTR )strHost,(LPCTSTR )SocketErrString());
    } else {
        char *c=inet_ntoa( *( (struct in_addr *)host_dat->h_addr_list[0] )  );
        if(0==c){
            SaveError();
            TRACE(_T("Could inet_ntoa %s - %s\n"),(LPCTSTR )strHost,(LPCTSTR )SocketErrString());
        } else {

            strcpy(ip, c);
            memset(&serv_addr, 0, sizeof serv_addr);

            // Create socket
            m_nSock = ::socket(AF_INET, SOCK_STREAM, 0);
            if (-1 == m_nSock) {
                TRACE(_T("Could not create socket [%s:%d]\n"),(LPCTSTR )strHost,port);
            } else {
                #ifdef _WIN32
                SetSocketOptions();
                #endif
                TRACE(_T("Created socket %d connected to %s:%d\n"),m_nSock,(LPCTSTR )strHost,port);
                // Bind socket to address
                serv_addr.sin_family = AF_INET;
                serv_addr.sin_port=htons((short)port);
                SaveError();
                serv_addr.sin_addr.s_addr = inet_addr(ip);

                // Connect to server
                VTRACE(_T("Connect() : connecting to server\n"));
                int cc=::connect(m_nSock, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
                SaveError();
                String strMsg;
                if(-1==cc){
                    if(
    #ifdef _WIN32
                       WOULDBLOCK==SocketError()
    #else // UNIX
                       EINPROGRESS==SocketError()
    #endif
                       ){
                        // Allow dTimeout milliseconds for connect to complete
                        fd_set set;
                        FD_ZERO(&set);
    #ifdef _WIN32
                        #pragma warning( push )
                        #pragma warning( disable : 4127 ) // conditional expression is constant
    #endif
                        FD_SET((unsigned)m_nSock, &set);
    #ifdef _WIN32
                        #pragma warning( pop )
    #endif
                        struct timeval tv;
                        tv.tv_sec = dTimeout/1000;  
                        tv.tv_usec = 1000*(dTimeout % 1000);
                        switch(::select(m_nSock, NULL, &set , NULL, &tv)){
                            case 0:
                                m_nErr=ERR_TIMEOUT;
                                strMsg.Format(_T("attempt timed out after %d seconds"),dTimeout/1000);
                                break;
                            case -1:
                                SaveError();
                                strMsg=SocketErrString();
                                break;
                            default:
                                cc=0;
                        }
                    } else {
                        strMsg=SocketErrString();
                    }
                }

                if(-1==cc){
                    TRACE(_T("Could not connect to %s:%d - %s\n"),(LPCTSTR )strHost,port,(LPCTSTR )strMsg);
                    CloseSocket(m_nSock);
                }  else {
                    #ifndef _WIN32
                    SetSocketOptions();
                    #endif
                }
            }
        }
    }
    delete [] pszHost;
    return -1!=m_nSock;
}

bool CeCosTestSocket::sendrecv(bool bSend,const void *pData,unsigned int nLength,
	LPCTSTR pszMsg,Duration dTimeout,CeCosTestSocket::StopFunc pFnStop,void *pParam)
{
    
  dTimeout=TimeoutDuration(dTimeout);
  
  LPCTSTR pszSR=(bSend?_T("sending"):_T("receiving"));
  LPTSTR c=(LPTSTR )pData;
  Time ft0=Now();
  int nTodo=nLength;
  while((nTodo>0) && ((0==pFnStop) || (!pFnStop(pParam)))){
    int s=bSend?::send(m_nSock, (const char *)c, nTodo, 0): ::recv(m_nSock, (char *)c, nTodo, 0);
    if(0==s && !bSend){
      m_nErr=ERR_READ_AFTER_CLOSE;
    } else {
      SaveError();
    }
    if(-1==s && WOULDBLOCK==SocketError()){
      Duration d=Duration(Now()-ft0);
      if(d>dTimeout){
        TRACE(_T("%d/%d mSec timeout on socket %d %s %s - processed %d/%d bytes\n") ,
          d,dTimeout,m_nSock,pszSR,pszMsg, 
          nLength-nTodo,nLength);
        m_nErr=ERR_TIMEOUT;
        break;
      }
      Sleep(100);
    } else if (s>0) {
      c+=s;
      nTodo-=s;
      ft0=Now();
    } else {
      TRACE(_T("Error on socket %d %s %s - %s\n") ,m_nSock, pszSR, pszMsg, (LPCTSTR )SocketErrString());
      break;
    }
  }
  return 0==nTodo;
}

// Graceful socket closedown
CeCosTestSocket::~CeCosTestSocket()
{
	Close();
    TRACE(_T("Delete socket instance %08x\n"),(unsigned int)this);
}

bool CeCosTestSocket::CloseSocket(int &sock)
{
    bool rc=false;
    if(-1!=sock){
        TRACE(_T("Closing socket %d\n"),sock);
        try{
            shutdown(sock,0);// SD_BOTH
    #ifdef _WIN32
            rc=(0==closesocket(sock));
    #else // UNIX
            rc=(0==close(sock));
    #endif
        }
        catch(...) {
            TRACE(_T("!!! Exception caught in CeCosTestSocket::CloseSocket!!!\n"));
        }
        sock=-1;
    }
	return rc;
}

bool CeCosTestSocket::SetSocketOptions()
{
    bool rc;
    #ifdef _WIN32
      int nTrue=1;
      rc=(0==::ioctlsocket(m_nSock, FIONBIO, (unsigned long *)&nTrue));
      SaveError();
    #else // UNIX
      int flags=::fcntl(m_nSock,F_GETFL);
      SaveError();
      flags|=O_NONBLOCK;
      rc=(0==::fcntl (m_nSock, F_SETFL, flags));
      SaveError();
    #endif
    //LINGER linger;
    //setsockopt(m_nSock,SOL_SOCKET,SO_LINGER,(LPCTSTR )linger, sizeof(LINGER));
	if(!rc){
        TRACE(_T("Failed to set socket options socket %d - %s\n"),m_nSock,(LPCTSTR )SocketErrString());
    }
    return rc;
}

String CeCosTestSocket::SocketErrString(int nErr)
{
    String str;
    #ifdef _WIN32
      switch(nErr){
        case ERR_TIMEOUT: str=_T("Read operation timed out");break;
        case ERR_READ_AFTER_CLOSE: str=_T("Read operation after socket closed");break;

        case WSAEACCES: str=_T("Permission denied");break;
        case WSAEADDRINUSE: str=_T("Address already in use");break;
        case WSAEADDRNOTAVAIL: str=_T("Cannot assign requested address");break;
        case WSAEAFNOSUPPORT: str=_T("Address family not supported by protocol family");break;
        case WSAEALREADY: str=_T("Operation already in progress");break;
        case WSAECONNABORTED: str=_T("Software caused connection abort");break;
        case WSAECONNREFUSED: str=_T("Connection refused");break;
        case WSAECONNRESET: str=_T("Connection reset by peer");break;
        case WSAEDESTADDRREQ: str=_T("Destination address required");break;
        case WSAEFAULT: str=_T("Bad address");break;
        case WSAEHOSTDOWN: str=_T("Host is down");break;
        case WSAEHOSTUNREACH: str=_T("No route to host");break;
        case WSAEINPROGRESS: str=_T("Operation now in progress");break;
        case WSAEINTR: str=_T("Interrupted function call");break;
        case WSAEINVAL: str=_T("Invalid argument");break;
        case WSAEISCONN: str=_T("Socket is already connected");break;
        case WSAEMFILE: str=_T("Too many open files");break;
        case WSAEMSGSIZE: str=_T("Message too long");break;
        case WSAENETDOWN: str=_T("Network is down");break;
        case WSAENETRESET: str=_T("Network dropped connection on reset");break;
        case WSAENETUNREACH: str=_T("Network is unreachable");break;
        case WSAENOBUFS: str=_T("No buffer space available");break;
        case WSAENOPROTOOPT: str=_T("Bad protocol option");break;
        case WSAENOTCONN: str=_T("Socket is not connected");break;
        case WSAENOTSOCK: str=_T("Socket operation on non-socket");break;
        case WSAEOPNOTSUPP: str=_T("Operation not supported");break;
        case WSAEPFNOSUPPORT: str=_T("Protocol family not supported");break;
        case WSAEPROCLIM: str=_T("Too many processes");break;
        case WSAEPROTONOSUPPORT: str=_T("Protocol not supported");break;
        case WSAEPROTOTYPE: str=_T("Protocol wrong type for socket");break;
        case WSAESHUTDOWN: str=_T("Cannot send after socket shutdown");break;
        case WSAESOCKTNOSUPPORT: str=_T("Socket type not supported");break;
        case WSAETIMEDOUT: str=_T("Connection timed out");break;
        case WSATYPE_NOT_FOUND: str=_T("Class type not found");break;
        case WSAEWOULDBLOCK: str=_T("Resource temporarily unavailable");break;
        case WSAHOST_NOT_FOUND: str=_T("Host not found");break;
        case WSA_INVALID_HANDLE: str=_T("Specified event object handle is invalid");break;
        case WSA_INVALID_PARAMETER: str=_T("One or more parameters are invalid");break;
        //case WSAINVALIDPROCTABLE: str=_T("Invalid procedure table from service provider");break;
        //case WSAINVALIDPROVIDER: str=_T("Invalid service provider version number");break;
        case WSA_IO_INCOMPLETE: str=_T("Overlapped I/O event object not in signaled state");break;
        case WSA_IO_PENDING: str=_T("Overlapped operations will complete later");break;
        case WSA_NOT_ENOUGH_MEMORY: str=_T("Insufficient memory available");break;
        case WSANOTINITIALISED: str=_T("Successful case WSAStartup not yet:performed");break;
        case WSANO_DATA: str=_T("Valid name, no data record of requested type");break;
        case WSANO_RECOVERY: str=_T("This is a non-recoverable error");break;
        //case WSAPROVIDERFAILEDINIT: str=_T("Unable to initialize a service provider");break;
        case WSASYSCALLFAILURE: str=_T("System call failure");break;
        case WSASYSNOTREADY: str=_T("Network subsystem is unavailable");break;
        case WSATRY_AGAIN: str=_T("Non-authoritative host not found");break;
        case WSAVERNOTSUPPORTED: str=_T("WINSOCK.DLL version out of range");break;
        case WSAEDISCON: str=_T("Graceful shutdown in progress");break;
        case WSA_OPERATION_ABORTED: str=_T("Overlapped operation aborted");break;
        default:
            str.Format(_T("Unknown error %d (0x%08x)"),nErr,nErr);
    }
#else // UNIX
   switch(nErr){
        case ERR_TIMEOUT: str=_T("Read operation timed out");break;
        case ERR_READ_AFTER_CLOSE: str=_T("Read operation after socket closed");break;
        default:
            str=strerror(errno);
   }
#endif
    return str;
}

bool CeCosTestSocket::sendInteger(int n,LPCTSTR pszMsg,Duration dTimeout)
{
    // This has to support cross-architectural endianness
    unsigned char c[sizeof(int)];
    for(unsigned int i=0;i<sizeof(int);i++){
        c[i]=(unsigned char)(n&0xff);
        n>>=8;
    }
    return send (c, sizeof(int),pszMsg,dTimeout);
}

bool CeCosTestSocket::recvInteger(int & n,LPCTSTR pszMsg,Duration dTimeout)
{
    // This has to support cross-architectural endianness
    unsigned char c[sizeof(int)];
    bool rc=recv (c, sizeof(int),pszMsg,dTimeout);
    n=0;
    if(rc){
        for(int i=sizeof(int)-1;i>=0;--i){
            n<<=8;
            n|=c[i];
        }
    }
    return rc;
}

// Socket communications for strings are always non-UNICODE:
bool CeCosTestSocket::recvString  (String &str,LPCTSTR pszMsg,Duration dTimeout)
{
    int nLength;
    bool rc=false;
    if(recvInteger(nLength,pszMsg,dTimeout)){
        if(0==nLength){
            rc=true;
        } else {
            Buffer b(1+nLength);
            char *c=(char *)b.Data();
            if(c){
                rc=recv(c,nLength,pszMsg,dTimeout);
                c[nLength]='\0';
                str=String::CStrToUnicodeStr(c);
            }
        }
    }
    return rc;
}

// Socket communications for strings are always non-UNICODE:
bool CeCosTestSocket::sendString  (const String &str,LPCTSTR pszMsg,Duration dTimeout)
{
    char *psz=str.GetCString();
    int nLength=strlen(psz); 
    bool rc=sendInteger(nLength,pszMsg,dTimeout) && (0==nLength || send(psz,nLength,pszMsg,dTimeout));
    delete [] psz;
    return rc;
}


// Give indication of bytes available to be read (but don't read them)
bool CeCosTestSocket::Peek (unsigned int &nAvail)
{
    char buf[8192];
    int n=::recv(m_nSock, buf, sizeof buf, MSG_PEEK);
    nAvail=0;
    bool rc=false;
    switch(n) {
        case -1:
            SaveError();
            if(WOULDBLOCK==SocketError()){
                rc=true; // nAvail stays==0
            } else {
                ERROR(_T("Peek: err=%d %s\n"),SocketError(),(LPCTSTR )SocketErrString());
            }
            break;
        case 0:
            m_nErr=ERR_READ_AFTER_CLOSE;
            break;
        default:
            rc=true;
            nAvail=n;
    }
    return rc;
}

// Connect tcp/ip port and serial port together.
// Traffic is passed through pFunc, passed parameter pParam.
// The pFunc function:
//    may reallocate pBuf (using malloc/realloc etc...)
//    must leave pBuf allocated on exit
//    should not close either serial or socket
//    should leave writing to its caller
//    should return false if it wishes to terminate the connection (after caller has written output)
bool CeCosTestSocket::ConnectSocketToSerial (CeCosTestSocket &socket,CeCosTestSerial &serial,FilterFunc *pSerialToSocketFilterFunc/*=0*/,void *pSerialParam/*=0*/,FilterFunc *pSocketToSerialFilterFunc/*=0*/,void *pSocketParam/*=0*/,bool *pbStop/*=0*/)
{
    serial.ClearError();
    enum {BUFSIZE=8192};
    void *pBuf=malloc(BUFSIZE);
    TRACE(_T("ConnectSocketToSerial: connected\n"));
    bool rc=true;
    try {
        /*
        { //hack
            unsigned int nWritten;//hack
            serial.Write(_T("+"),1,nWritten);//hack
        }//hack
        */
        while(rc && (0==pbStop || !(*pbStop))){
            unsigned int nRead=0;
            switch(SSRead (serial,socket,pBuf,BUFSIZE,nRead,pbStop)){
                case 2: 
                    VTRACE(_T("Serial:%d\n"),nRead);
                    if(pSerialToSocketFilterFunc){
                        rc=pSerialToSocketFilterFunc(pBuf,nRead,serial,socket,pSerialParam);
                    }
                    if(nRead && !socket.send(pBuf,nRead)){
                        TRACE(_T("Failed to write to socket\n"));
                        rc=false;
                    }
                    break;
                case 1:
                    unsigned int nWritten;
                    VTRACE(_T("Socket:%d\n"),nRead);
                    if(pSocketToSerialFilterFunc){
                        rc=pSocketToSerialFilterFunc(pBuf,nRead,serial,socket,pSocketParam);
                    }
                    {
                        LPTSTR c=(LPTSTR )pBuf;
                        int nToWrite=nRead;
                        while(nToWrite>0){
                            if(!serial.Write(pBuf,nRead,nWritten)){
                                TRACE(_T("Failed to write to serial\n"));
                                rc=false;
                                break;
                            }
                            nToWrite-=nWritten;
                            c+=nWritten;
                        }
                    }
                    break;
                case 0:
                    TRACE(_T("SSRead returned error indication\n"));
                    rc=false;
                    break;
                    // Error
            }
        }
    }
	catch (...){
        TRACE(_T("!!! ConnectSocketToSerial exception caught!!!\n"));
        free(pBuf);
        throw;
    }
    free(pBuf);
    return rc;
}

// Connect two tcp/ip ports together.
// Traffic is passed through pFunc, passed parameter pParam.
// The pFunc function:
//    may reallocate pBuf (using malloc/realloc etc...)
//    must leave pBuf allocated on exit
//    should not close either serial or socket
//    should leave writing to its caller
//    should return false if it wishes to terminate the connection (after caller has written output)
bool CeCosTestSocket::ConnectSocketToSocket (CeCosTestSocket &o,FilterFunc *pSocketToSocketFilterFunc1,FilterFunc *pSocketToSocketFilterFunc2,void *pParam,bool *pbStop)
{
  enum {BUFSIZE=8192};
  void *pBuf=malloc(BUFSIZE);
  TRACE(_T("ConnectSocketToSocket: connected\n"));
  bool rc=true;
  try {
    while(rc && (0==pbStop || !(*pbStop))){
      fd_set set;
      FD_ZERO(&set);
      FD_SET((unsigned)m_nSock, &set);
      FD_SET((unsigned)o.m_nSock, &set);
      struct timeval tv;
      tv.tv_sec = 1;
      tv.tv_usec = 0;
      switch(::select(m_nSock,&set,0,0,&tv)){
      case -1:
        rc=false;
        break;
      case 1:
      case 2:
        {
          unsigned int nAvail=0;
          if(FD_ISSET((unsigned)m_nSock, &set) && Peek(nAvail) && recv(pBuf,nAvail)){
            //rc=pSocketToSocketFilterFunc1(pBuf,nAvail,socket,this,o);
            o.send(pBuf,nAvail);
          }
          if(FD_ISSET((unsigned)o.m_nSock, &set) && o.Peek(nAvail) && o.recv(pBuf,nAvail)){
            //rc=pSocketToSocketFilterFunc2(pBuf,nAvail,socket,o,this);
            send(pBuf,nAvail);
          }
        }
      case 0:
        break;
      }
    }
  }
  catch (...){
    TRACE(_T("!!! ConnectSocketToSocket exception caught!!!\n"));
    rc=false;
  }
  free(pBuf);
  return rc;
}

bool CeCosTestSocket::ConnectSocketToSerial (
    int nListenSock,LPCTSTR pszPort, int nBaud,
    FilterFunc *pSerialToSocketFilterFunc/*=0*/,void *pSerialParam/*=0*/,FilterFunc *pSocketToSerialFilterFunc/*=0*/,void *pSocketParam/*=0*/,
    bool *pbStop)
{
    bool rc=false;
    try{
        TRACE(_T("ConnectSocketToSerial : socket %d <--> %s\n"),nListenSock,pszPort);

        CeCosTestSerial serial;
        serial.SetBlockingReads(false);
        // Open serial device.
        if (!serial.Open(pszPort,nBaud)){
            ERROR(_T("Couldn't open port %s\n"),pszPort);
        } else {
            // Flush the serial buffer.
            serial.Flush();

            TRACE(_T("ConnectSocketToSerial: waiting for connection...\n"));
            CeCosTestSocket socket;
            if(!socket.Accept(nListenSock,pbStop)){
                ERROR(_T("ConnectSocketToSerial - couldn't accept\n"));
            } else {    
                rc=ConnectSocketToSerial (socket,serial,pSerialToSocketFilterFunc,pSerialParam,pSocketToSerialFilterFunc,pSocketParam,pbStop);
            }
        }
        TRACE(_T("ConnectSocketToSerial : done\n"));
    }
    catch(...){
        TRACE(_T("ConnectSocketToSerial !!!exception handled!!!\n"));
    }
    return rc;
}
    
String CeCosTestSocket::ClientName(int nClient) 
{
    char ip[4];
    memcpy(ip,&nClient,4);
    struct hostent *he=::gethostbyaddr((const char *)ip,4,AF_INET);
    String str;
    if(he){
        str=String::CStrToUnicodeStr(he->h_name);
    } else {
        str.Format(_T("%u.%u.%u.%u"),ip[0],ip[1],ip[2],ip[3]);
    }
    return str;
}

String CeCosTestSocket::HostPort(LPCTSTR pszHost,int nPort) 
{
    String str;
    str.Format(_T("%s:%d"),pszHost,nPort);
    return str;
}

// Split the string into host:port parts.  Result tells us whether it was successful.
bool CeCosTestSocket::ParseHostPort (LPCTSTR pszHostPort, String &strHost, int &nPort)
{
  int n=_stscanf(pszHostPort,_T("%[^:]:%d"),strHost.GetBuffer(_tcslen(pszHostPort)),&nPort);
  strHost.ReleaseBuffer();
  return 2==n && nPort>0 && nPort<=0xffff;
}

// Is the string in the form host:port?
bool CeCosTestSocket::IsLegalHostPort (LPCTSTR pszHostPort)
{
	int nPort=0;
    String strHost;
	return ParseHostPort(pszHostPort,strHost,nPort);
}

// Translate a timeout that may be one of the special values DEFAULTTIMEOUT or NOTIMEOUT to a value in milliseconds.
Duration CeCosTestSocket::TimeoutDuration(Duration dTimeout)
{
    switch(dTimeout){
        case DEFAULTTIMEOUT:
            dTimeout=m_nDefaultTimeout;
            break;
        case NOTIMEOUT:
            dTimeout=0x7fffffff;
            break;
        default:
            break;
    }
    return dTimeout;
}

bool CeCosTestSocket::IsSocketError(int n)
{
  #ifdef _WIN32
    return n>=WSABASEERR && n<30000; // include our own (e.g. ERR_READ_AFTER_CLOSE)
  #else // UNIX
    return true;
  #endif
}

String CeCosTestSocket::SocketErrString() { 
    return SocketErrString(m_nErr); 
}


bool CeCosTestSocket::SameHost(LPCTSTR host1, LPCTSTR host2)
{
  char *h1=String(host1).GetCString();
  char *h2=String(host2).GetCString();
  char ip1[16],ip2[16];
  struct hostent* host_dat;
  bool rc=(0==_tcscmp(host1,host2));
  if (0!=(host_dat=::gethostbyname(h1))){
    char *c=inet_ntoa( *( (struct in_addr *)host_dat->h_addr_list[0] )  );
    if(c){
      strcpy(ip1,c);
      if (0!=(host_dat=::gethostbyname(h2))){
        char *c=inet_ntoa( *( (struct in_addr *)host_dat->h_addr_list[0] )  );
        if(c){
          strcpy(ip2,c);
        }
      }
      rc=(0==strcmp(ip1,ip2));
    }
  }
  delete [] h1;
  delete [] h2;
  return rc;
}
