
/*****************************************************************************\
 *
 * TNet.c
 *
 * Author: PCJohn (peciva _AT fit.vutbr.cz)
 *
 * Contributors:
 *
 * ----------------------------------------------------------------------------
 *
 * THIS SOFTWARE IS NOT COPYRIGHTED
 *
 * This source code is offered for use in the public domain.
 * You may use, modify or distribute it freely.
 *
 * This source code is distributed in the hope that it will be useful but
 * WITHOUT ANY WARRANTY.  ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
 * DISCLAIMED.  This includes but is not limited to warranties of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * If you find the source code useful, authors will kindly welcome
 * if you give them credit and keep their names with their source code,
 * but do not feel to be forced to do so.
 *
\*****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdarg.h>
#include <string.h> // for strerror

// time functions
#if defined(__WIN32__) || defined(_WIN32)
#else
#include <sys/time.h>
#endif


#ifdef COIN_INTERNAL // Are we compiled inside Coin?
#include <Inventor/cve/TNet.h>
#else
#include "TNet.h"
#endif

#if defined(_WIN32) || defined(__WIN32__)
# include <winsock2.h>
# include <io.h> // _open_osfhandle
# include <fcntl.h> // _O_TEXT
# pragma comment(lib, "wsock32.lib")
  typedef int socklen_t;
# ifdef errno
#  undef errno
# endif
# define errno WSAGetLastError()
# define setLastError(i) WSASetLastError(i);
# define ECONNRESET   WSAECONNRESET
# define ETIMEDOUT    WSAETIMEDOUT
# define ECONNABORTED WSAECONNABORTED
# define ENETRESET    WSAENETRESET
# define EWOULDBLOCK  WSAEWOULDBLOCK
# define ECONNREFUSED WSAECONNREFUSED
# define EINPROGRESS  WSAEINPROGRESS
# define ENOTCONN     WSAENOTCONN
# define EHOSTUNREACH WSAEHOSTUNREACH
# define ioctl ioctlsocket
# define DOUBLE0x100000000 4294967296.  // Some compilers (G++) does not accept (double)0x100000000.
                                        // Let's use this macro instead.
#else
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/ioctl.h>
# include <sys/time.h>
# include <sys/select.h>
# include <unistd.h>
# include <netinet/in.h>
# include <netinet/tcp.h>
# include <arpa/inet.h>
# include <errno.h>
# include <netdb.h> // gethostbyname
# define closesocket close
# define SOCKET int
# define INVALID_SOCKET -1
# define SOCKET_ERROR   -1
# define setLastError(i) errno = i;
// Constants for shutdown()
# define SD_RECEIVE     0x00
# define SD_SEND        0x01
# define SD_BOTH        0x02
#endif



#define DEFAULT_BUFFER_SIZE 4032 // 4KB without 64 bytes that are left for memory manager to be 4KB aligned
#define MAX_HOST_NAME_LEN 128

static bool_t initialized = 0;
static int globalLatency = -1;
static int globalLatencyDispersion = -1;
static socklen_t intSize = sizeof(int);
static TNErrorCb *errorCb;
#if defined(__WIN32__) || defined(_WIN32) // used on Windows only
static struct timeval zeroTimeval = { 0, 0 };
#endif
static int totalBytesSent = 0;
static int totalBytesReceived = 0;



// ConnectionStruct keeps connection data
enum SL_STATE { SL_NO_ACTIVE, SL_ACTIVE, SL_WAITING_FLUSH, SL_SHUTDOWN };
typedef struct {
  enum TNState state;
  union {
    SOCKET socket;
    TNConnection nextFree;
  } uni;
  int numBytesSent;
  int numBytesReceived;
  struct dsStruct *dsFirst;
  struct dsStruct *dsLast;
  unsigned int numUnsentData;
  ip_t localIP;
  port_t localPort;
  ip_t remoteIP;
  port_t remotePort;
  int recvBufferSize;
  int sendBufferSize;
  int appBufferSize;
  int slLatency_ms;
  int slDispersion_ms;
  double nextDeliverTime;
  struct {
    bool_t disconIncompleteCon : 1;
    enum SL_STATE slState : 3;
    bool_t dsQueueShutdownScheduled : 1;
  } bits;
  struct slStruct *slFirst;
  struct slStruct *slLast;
} ConnectionStruct;


// connection list
static ConnectionStruct *csList = NULL;
static int csListSize = 0;
static TNConnection csFirstFree = 0;


// delayed send data struct
typedef struct dsStruct {
  char *buf;
  char *pos;
  char *endp;
  struct dsStruct *next;
} dsStruct;


// simulated latency structure
typedef struct slStruct {
  char *buf;
  char *pos;
  char *endp;
  double time;
  struct slStruct *next;
} slStruct;



// functions that works with ConnectionStruct
static void csAlloc(ConnectionStruct **cs, TNConnection *c);
static void csRelease(ConnectionStruct *cs, TNConnection c);
static void csOptimizeList();

enum CLOSE_STATE { CLOSE_OK, CLOSE_BLOCK, CLOSE_FAIL };
static void csCreateSocket(ConnectionStruct *cs);
static void csCloseSocket(ConnectionStruct *cs);
static void csDefaultInit(ConnectionStruct *cs);
static void csDefaultSettings(ConnectionStruct *cs);
static enum CLOSE_STATE csTryCloseSocket(ConnectionStruct *cs);
static enum CLOSE_STATE sockClose(SOCKET s);

enum CONNECT_STATE { CONNECT_FAIL, CONNECT_INPROGRESS, CONNECT_OK };
static enum CONNECT_STATE csConnect(ConnectionStruct *cs, ip_t ip, port_t port);
static void csMakeListening(ConnectionStruct *cs);
static bool_t csAccept(ConnectionStruct *cs, SOCKET *newSocket,
    ip_t *remoteIP, port_t *remotePort);
static void csShutDown(ConnectionStruct *cs);
static void csOnShutDownRecv(TNConnection c, ConnectionStruct *cs);

enum RECV_STATE { RECV_FAIL, RECV_SHUTDOWN, RECV_EMPTY, RECV_DATA };
enum SEND_STATE { SEND_FAIL, SEND_BLOCK, SEND_OK };
enum TEST_VALUE { TEST_FAIL, TEST_NO, TEST_YES };
static int csSend(ConnectionStruct *cs, const void *buf, unsigned int bufsize);
static bool_t csSendDelayedData(ConnectionStruct *cs);
static int csRecv(ConnectionStruct *cs, void *buf, int bufsize);
static enum RECV_STATE csTestRecv(ConnectionStruct *cs);
static enum SEND_STATE csTestSend(ConnectionStruct *cs);
static enum TEST_VALUE csTestError(ConnectionStruct *cs);
//static enum TEST_VALUE csTestSelectRecv(ConnectionStruct *cs); // this function is never used; it is here just for completeness and experimental purposes
#if defined(__WIN32__) || defined(_WIN32) // functions used on Windows only (otherwise commented out to avoid compiler warnings)
static enum TEST_VALUE csTestSelectSend(ConnectionStruct *cs);
static enum TEST_VALUE csTestSelectError(ConnectionStruct *cs);
#endif
enum NAME_STATE { NAME_FAIL, NAME_STILL_UNKNOWN, NAME_UPDATED };
static void csOnConnected(ConnectionStruct *cs);

static void csSetNonBlocking(ConnectionStruct *cs, bool_t nb);
enum LINGER_ENUM { LIN_AUTOMATIC, LIN_TIMEOUT, LIN_IMMEDIATE };
static void csSetLinger(ConnectionStruct *cs, enum LINGER_ENUM l);
static void csSetNonNagle(ConnectionStruct *cs, bool_t nn);
static void csSetReuseAddr(ConnectionStruct *cs, bool_t ra);
static void csBind(ConnectionStruct *cs);


static void debugInfo(const ConnectionStruct *cs, const char *msg, int errorCode);
static void debugError(const ConnectionStruct *cs, const char *msg, int errorCode);
static void debugErrorS(int appendChars, const char *msg, ...);
static void defaultErrorCb(enum TNErrorSeverity severity, TNConnection c, const char *str, int errorCode);

static void csReleaseSlQueue(ConnectionStruct *cs);
static void csReleaseDsQueue(ConnectionStruct *cs);

static double getTime();
static int randi(int dispersion);
static double randd(int dispersion);


#define errnoBrokenConnection() \
(errno == ECONNRESET || errno == ETIMEDOUT || \
 errno == ECONNABORTED || errno == ENETRESET)




#if 0 // inline functions makes problems to some compilers

static inline ConnectionStruct* getConnectionStructNoCheck(
    const TNConnection connection)
{ return &csList[connection-1]; }

static inline ConnectionStruct* getConnectionStruct(
    const TNConnection connection)
{
  assert(connection >= 1 && connection <= csListSize &&
         csList[connection-1].state != TN_FREE && "Invalid socketid");
  return getConnectionStructNoCheck(connection);
}

static inline TNConnection getConnectionByStruct(
    const ConnectionStruct *cs)
{
  TNConnection c = cs - csList + 1;
  assert(c > 0 && c <= csListSize && "Invalid ConnectionStruct");
  return c;
}

#else

#define getConnectionStructNoCheck(connection) \
  (&csList[(connection)-1])

#define getConnectionStruct(connection) \
( assert(connection >= 1 && connection <= csListSize && \
         csList[connection-1].state != TN_FREE && "Invalid socketid"), \
  getConnectionStructNoCheck(connection))

#define getConnectionByStruct(cs) \
( assert(cs >= csList && cs - csList < csListSize && "Invalid ConnectionStruct"), \
  cs - csList + 1)

#endif


/*
  Function call dependency
  (I or E means function may print info or error)

tnClose - csSetLinger, csCloseSocket, csRelease
csDefaultInit - csCreateSocket, csSetNonBlocking, csSetNonNagle, csSetReuseAddr,  csSetLinger, csBind
tnConnect - tnConnectFromIP
tnConnectFromIP - csAlloc, csDefaultInit, csConnect, tnClose
tnListen - tnListenOnIP
tnListenOnIP - csAlloc, csDefaultInit, csMakeListening, tnClose
tnAccept - csAccept, csAlloc, sockClose
tnDisconnect - tnGetState, csShutDown, csCloseSocket
tnGetState - csTestSend, csTestSelectSend, csTestRecv -> E, csTestError -> E, csCloseSocket, tnGetState, tnDisconnect, csTestRecv -> E, csTryCloseSocket -> E, csTestSend -> IE
tnSend - tnGetState, "send" -> IEE, csCloseSocket
tnRecv - tnGetState, "recv" -> IE, csCloseSocket
tnInit - tnInitDebug
tnInitDebug - "WSAStartup" -> EpEp
tnCleanUp - tnClose, csOptimizeList
tnGetLocalIPAddresses
tnGetSockIP - csUpdateLocalName
tnGetSockPort - csUpdateLocalName
tnGetPeerIP
tnGetPeerPort
tnGetHostName - EpEp
tnGetNumConnections
tnIP2Str
tnStr2IP
tnReadIP - tnStr2IP

csCreateSocket - "socket" -> E
sockClose - Es
csTryCloseSocket - sockClose
csCloseSocket - csTryCloseSocket -> E2
csConnect - "connect" -> E, csCloseSocket
csMakeListening - "listen" -> E, csCloseSocket
csAccept - "accept" -> E
csShutDown - "shutdown" -> E, csCloseSocket
csTestRecv - "recv"
csTestSend - "send"
csTestError - "getsockopt"
csTestSelectRecv - "select"
csTestSelectSend - "select"
csTestSelectError - "select", csTestError
csUpdateLocalName - "getsockname" -> E
csUpdateConnectingState - csTestSend -> I, csCloseSocket
csSetNonBlocking - "ioctl" -> E, csCloseSocket
csSetLinger - "setsockopt" -> E
csSetNonNagle - "setsockopt" -> E
csSetReuseAddr - "setsockopt" -> E, csCloseSocket
csBind - "bind" -> E, csCloseSocket
csAlloc - "realloc" -> Es
csRelease - csOptimizeList
csOptimizeList

*/



TNET_DLL_API void tnClose(TNConnection c)
{
  ConnectionStruct *cs;

  if (!initialized) return;

  cs = getConnectionStruct(c);
  switch (cs->state) {

  case TN_CONNECTING:
  case TN_CONNECTED:
  case TN_DISCONNECTING:
  case TN_LISTENING:      csCloseSocket(cs);
  case TN_DISCONNECTED:
  case TN_BROKEN:         csRelease(cs, c);
                          break;

  default:                assert(0);
  }
}



static void csDefaultInit(ConnectionStruct *cs)
{
  csCreateSocket(cs);
  csDefaultSettings(cs);
  csBind(cs);
}



static void csDefaultSettings(ConnectionStruct *cs)
{
  csSetNonBlocking(cs, 1);
  csSetNonNagle(cs, 1);
  csSetReuseAddr(cs, 1);
  csSetLinger(cs, LIN_IMMEDIATE);
}



TNET_DLL_API TNConnection tnConnect(ip_t remoteIP, port_t remotePort, port_t localPort)
{
  return tnConnectFromIP(remoteIP, remotePort, INADDR_ANY, localPort);
}



TNET_DLL_API TNConnection tnConnectFromIP(ip_t remoteIP, port_t remotePort, ip_t localIP, port_t localPort)
{
  ConnectionStruct *cs;
  TNConnection c;
  enum CONNECT_STATE result;

  if (!initialized) return -1;

  csAlloc(&cs, &c);
  if (c == INVALID_SOCKET)
    return -1;

  cs->state = TN_CONNECTING;
  cs->localIP = localIP;
  cs->localPort = localPort;
  cs->remoteIP = remoteIP;
  cs->remotePort = remotePort;

  csDefaultInit(cs);
  result = csConnect(cs, remoteIP, remotePort);

  switch (result) {
  case CONNECT_FAIL:
      tnClose(c);
      return -1;
  case CONNECT_INPROGRESS:
      // connection state is TN_CONNECTING
      break;
  case CONNECT_OK:
      cs->state = TN_CONNECTED;
      csOnConnected(cs);
      break;
  }

  return c;
}



TNET_DLL_API TNConnection tnListen(port_t port)
{
  return tnListenOnIP(INADDR_ANY, port);
}



TNET_DLL_API TNConnection tnListenOnIP(ip_t ip, port_t port)
{
  ConnectionStruct *cs;
  TNConnection c;

  if (!initialized) return -1;

  csAlloc(&cs, &c);
  if (c == INVALID_SOCKET)
    return -1;

  cs->state = TN_CONNECTING;
  cs->localIP = ip;
  cs->localPort = port;

  csDefaultInit(cs);
  csMakeListening(cs);

  if (cs->state == TN_BROKEN) {
    tnClose(c);
    return -1;
  }

  cs->state = TN_LISTENING;

  return c;
}



TNET_DLL_API TNConnection tnAccept(TNConnection lc)
{
  ConnectionStruct *lcs;
  ConnectionStruct *cs;
  TNConnection c;
  SOCKET socket;
  ip_t remoteIP;
  port_t remotePort;

  if (!initialized) return -1;

  lcs = getConnectionStruct(lc);

  if (lcs->state != TN_LISTENING) {
    assert(0 && "tnAccept() called for non-listening socket.");
    return -1;
  }

  // accept
  if (!csAccept(lcs, &socket, &remoteIP, &remotePort))
    return -1;

  csAlloc(&cs, &c);
  if (c == INVALID_SOCKET) {
    enum CLOSE_STATE r = sockClose(socket);
    assert(r == CLOSE_OK && "Something is wrong with new sockets.");
    return -1;
  }

  cs->state = TN_CONNECTED;
  cs->uni.socket = socket;

  cs->localIP = 0;   // necesary initialization for the cases when state will turn
  cs->localPort = 0; // to TN_BROKEN before end of this function is reached

  cs->remoteIP = remoteIP;
  cs->remotePort = remotePort;

  // On Windows, all properties of listening socket are copied to accepted socket.
  // On Linux/Unices, some socket properties like non-blocking are not derived
  // from the listening socket. To be sure, we set all properties here.
  csDefaultSettings(cs);

  csOnConnected(cs);

  return c;
}



TNET_DLL_API void tnDisconnect(TNConnection c)
{
  ConnectionStruct *cs;

  if (!initialized) return;

  cs = getConnectionStruct(c);
  switch (cs->state) {

  case TN_CONNECTING:     cs->bits.disconIncompleteCon = 1;
                          cs->state = TN_DISCONNECTING;
                          tnGetState(c); // to turn into "real" disconnecting state as soon as possible
                          break;

  case TN_CONNECTED:      csReleaseSlQueue(cs);
                          if (cs->bits.slState == SL_SHUTDOWN) {
                            csReleaseDsQueue(cs);
                            csShutDown(cs);
                          } else {
                            if (csSendDelayedData(cs))
                              csShutDown(cs);
                            else
                              cs->bits.dsQueueShutdownScheduled = 1;
                          }
                          if (cs->state == TN_BROKEN)  break;
                          cs->state = TN_DISCONNECTING;
                          tnGetState(c); // to turn into TN_DISCONNECTED state if possible
                          break;

  case TN_LISTENING:      csCloseSocket(cs);
                          cs->state = TN_DISCONNECTED;
                          break;

  case TN_DISCONNECTING:
  case TN_DISCONNECTED:
  case TN_BROKEN:         break;

  default:                assert(0);
  }
}



TNET_DLL_API enum TNState tnPeekState(TNConnection c)
{
  ConnectionStruct *cs;

  if (!initialized) return TN_BROKEN;

  cs = getConnectionStruct(c);
  return cs->state;
}



TNET_DLL_API enum TNState tnGetState(TNConnection c)
{
  ConnectionStruct *cs;

  if (!initialized) return TN_BROKEN;

  cs = getConnectionStruct(c);
  switch (cs->state) {

  case TN_CONNECTING:
#if defined(__WIN32__) || defined(_WIN32)
      switch (csTestSelectSend(cs)) {
      case TEST_YES:  cs->state = TN_CONNECTED; csOnConnected(cs); break;
      case TEST_NO:   switch (csTestSelectError(cs)) {
                      case TEST_NO:   break;
                      case TEST_YES:  debugInfo(cs, "Connection cannot be established.", errno);
                                      csCloseSocket(cs);
                                      cs->state = TN_BROKEN;
                                      break;
                      case TEST_FAIL: debugError(cs, "tnGetState() failed in csTestSelectError function.", errno);
                                      csCloseSocket(cs);
                                      cs->state = TN_BROKEN;
                                      break;
                      }
                      break;
      case TEST_FAIL: debugError(cs, "tnGetState() failed in csTestSelectSend function.", errno);
                      csCloseSocket(cs);
                      cs->state = TN_BROKEN;
                      break;
      }
#else
      switch (csTestSend(cs)) {
      case SEND_OK:    cs->state = TN_CONNECTED; csOnConnected(cs); break;
      case SEND_BLOCK: break;
      case SEND_FAIL:  debugInfo(cs, "Connection cannot be established.", errno);
                       csCloseSocket(cs);
                       cs->state = TN_BROKEN;
                       break;
      }
#endif
      break;

  case TN_CONNECTED:
      {
        enum RECV_STATE r;

        csSendDelayedData(cs);
        if (cs->state == TN_BROKEN)
          break;

        if (cs->bits.slState == SL_SHUTDOWN)
          break;

        r = csTestRecv(cs);
        switch (r) {

        case RECV_DATA:
        case RECV_EMPTY:
            break;

        case RECV_SHUTDOWN:
            csOnShutDownRecv(c,cs);
            break;

        case RECV_FAIL:
            debugError(cs, "tnGetState() failed in csTestRecv function.", errno);
            csCloseSocket(cs);
            cs->state = TN_BROKEN;
            break;

        default: assert(0);
        }
      }
      break;

  case TN_LISTENING:
      {
        enum TEST_VALUE r = csTestError(cs);
        switch (r) {
        case TEST_NO: break;
        case TEST_YES:
        case TEST_FAIL:
            debugError(cs, "tnGetState() detected error on listening socket.", errno);
            csCloseSocket(cs);
            cs->state = TN_BROKEN;
            break;
        default: assert(0);
        }
      }
      break;

  case TN_DISCONNECTING:
      if (cs->bits.disconIncompleteCon) {

        // treat connection as TN_CONNECTING and when connected, disconnect it
        cs->state = TN_CONNECTING;
        tnGetState(c);
        switch (cs->state) {
        case TN_CONNECTING: cs->state = TN_DISCONNECTING;
                            break;
        case TN_CONNECTED:  cs->bits.disconIncompleteCon = 0;
                            tnDisconnect(c);
                            break;
        case TN_BROKEN:     break;
        default:            assert(0 && "This should never happen.");
        }
      }

      // send delayed data before shutdown
      if (cs->bits.dsQueueShutdownScheduled)
        if (csSendDelayedData(cs)) {
          csShutDown(cs);
          cs->bits.dsQueueShutdownScheduled = 0;
        }

      // read all data from the socket before disconnecting
      if (!cs->bits.disconIncompleteCon) {
        char buf[4096];
        int r;
        do {
          r = csRecv(cs, &buf, 4096);
          if (r > 0) continue; // on data
          if (r < 0) break;    // on errors and no data
          if (r == 0) {        // on shutdown receive
            csCloseSocket(cs);
            cs->state = TN_DISCONNECTED;
            break;
          }
        } while (0);
      }
      break;

  case TN_DISCONNECTED:
  case TN_BROKEN:
      break;

  default:
      assert(0 && "Wrong connection state.");
  }

  return cs->state;
}



TNET_DLL_API bool_t tnIsSendBufferFull(TNConnection c)
{
  ConnectionStruct *cs;

  if (!initialized) return 0;

  cs = getConnectionStruct(c);

  return (cs->dsFirst != NULL);
}



/*! Returns:
 *  - >=0 - amount of data that was sent
 *  -  -1 - error occured and BROKEN state was set
 */
static int csSend(ConnectionStruct *cs, const void *buf, unsigned int bufsize)
{
  // send data
  int r = send(cs->uni.socket, (const char*)buf, bufsize, 0);

  if (r >= 0)
    return r;

  if (errno == EWOULDBLOCK)
    return 0;

  // broken connection
  if (errnoBrokenConnection()) {
    debugInfo(cs, "csSend() detected broken connection.", errno);
    csCloseSocket(cs);
    cs->state = TN_BROKEN;
    return -1;
  }

  // unknown errors
  debugError(cs, "csSend() failed because of unknown error.", errno);
  csCloseSocket(cs);
  cs->state = TN_BROKEN;
  return -1;
}



/*! Returns:
 *  - 1 if ds queue is empty when returning
 *  - 0 if ds queue is not empty or error occured (if error BROKEN state is set)
 */
static bool_t csSendDelayedData(ConnectionStruct *cs)
{
  if (cs->dsFirst == NULL)
    return 1;

  assert(cs->state == TN_CONNECTED || cs->state == TN_DISCONNECTING && 
         "csSendDelayedData() can be called on the connections in TN_CONNECTED state.");

  while (cs->dsFirst != NULL) {
    dsStruct *next;
    int s = cs->dsFirst->endp - cs->dsFirst->pos;
    int r = csSend(cs, cs->dsFirst->pos, s);

    if (r == -1)
      return 0;

    if (s != r) {
      cs->numUnsentData -= r;
      cs->dsFirst->pos += r;
      return 0;
    }

    cs->numUnsentData -= r;
    next = cs->dsFirst->next;
    free(cs->dsFirst->buf);
    free(cs->dsFirst);
    cs->dsFirst = next;
  }

  cs->dsLast = NULL;

  // perform shutdown if scheduled
  if (cs->bits.dsQueueShutdownScheduled)
    csShutDown(cs);
  return 1;
}



TNET_DLL_API void tnSend(TNConnection c, const void *buf, unsigned int bufsize)
{
  ConnectionStruct *cs;
  int r;

  if (!initialized) return;

  cs = getConnectionStruct(c);

  if (cs->state != TN_CONNECTED) {
    assert(cs->state != TN_LISTENING && "tnSend() called for listening socket.");

    if (cs->state == TN_CONNECTING) {
      debugError(cs, "tnSend() failed because the connection was not fully established yet "
                     "(the connection state is still TN_CONNECTING).", 0);
      csCloseSocket(cs);
      cs->state = TN_BROKEN;
    }
    return;
  }

  // send delayed data
  if (!csSendDelayedData(cs))
    r = (cs->state==TN_BROKEN ? -1 : 0);
  else
    // send current data
    r = csSend(cs, buf, bufsize);

  // update traffic counters
  if (r > 0) {
    cs->numBytesSent += r;
    totalBytesSent += r;
  }
  
  if (r == (int)bufsize)
    return;

  if (r != SOCKET_ERROR || errno == EWOULDBLOCK) {
    
    // socket buffer overflow detected, let's store the rest of data in our private memory
    unsigned int dsSize = bufsize - r;

    if (cs->numUnsentData + dsSize > (unsigned)cs->appBufferSize) {
      csCloseSocket(cs);
      cs->state = TN_BROKEN;
      debugError(cs, "App buffer overflow in tnSend().", 0);
      return;
    }

    dsStruct *ds = (dsStruct*)malloc(sizeof(dsStruct));

    ds->buf = (char*)malloc(dsSize);
    ds->pos = ds->buf;
    ds->endp = ds->buf + dsSize;
    ds->next = NULL;

    memcpy(ds->buf, (const char*)buf + r, dsSize);

    cs->numUnsentData += dsSize;
    
    if (cs->dsLast == NULL) {
      cs->dsFirst = ds;
      cs->dsLast = ds;
    } else {
      cs->dsLast->next = ds;
      cs->dsLast = ds;
    }

    // update traffic counters
    cs->numBytesSent += dsSize;
    totalBytesSent += dsSize;

    return;
  }

}



/*! Returns:
 *  - >=1 if data was received,
 *  -   0 on connection shutdown,
 *  -  -1 on connection error (state is set to TN_BROKEN),
 *  -  -2 no data on socket (connection state is left unchanged)
 */
static int csRecv(ConnectionStruct *cs, void *buf, int bufsize)
{
  int r = recv(cs->uni.socket, (char*)buf, bufsize, 0);

  // handle errors
  if (r == SOCKET_ERROR) {

    // no data
    if (errno == EWOULDBLOCK)
      return -2;

    // broken connection
    if (errnoBrokenConnection()) {
      debugInfo(cs, "tnRecv() detected broken connection.", errno);
      csCloseSocket(cs);
      cs->state = TN_BROKEN;
      return -1;
    }

    // unknown errors
    debugError(cs, "tnRecv() failed because of unknown error.", errno);
    csCloseSocket(cs);
    cs->state = TN_BROKEN;
    return -1;
  }

  cs->numBytesReceived += r;
  totalBytesReceived += r;

  return r;
}



void csGenerateNextDeliverTime(ConnectionStruct *cs, double dataTime)
{
  int latency = globalLatency!=-1 ? globalLatency : cs->slLatency_ms;
  int dispersion = globalLatencyDispersion!=-1 ? globalLatencyDispersion : cs->slDispersion_ms;

  latency += randi(dispersion);
  if (latency < 0)  latency = 0;

  cs->nextDeliverTime = dataTime + double(latency)*1e-3;
}



TNET_DLL_API int tnRecv(TNConnection c, void *buf, int bufsize)
{
  ConnectionStruct *cs;
  int r;

  if (!initialized) return 0;

  cs = getConnectionStruct(c);

  if (cs->state != TN_CONNECTED) {
    assert(cs->state != TN_LISTENING && "tnRecv() called for listening socket.");
    return 0;
  }

  if (cs->bits.slState == SL_NO_ACTIVE) {

    // regular recv
    r = csRecv(cs, buf, bufsize);
    if (r > 0) return r; // successful calls are returning here (TN_CONNECTED state are kept)
    if (r == 0) csOnShutDownRecv(c,cs); // shutdowns are handled here
                                        // (state is changed to TN_DISCONNECTING or TN_DISCONNECTED)
    return 0; // shutdowns and errors returns here

  } else {

    // simulated latency receive

    double ct = getTime();
    char *destp;
    char *endp;

    if (cs->bits.slState == SL_ACTIVE) {

      do {
        char *xbuf = (char*)malloc(DEFAULT_BUFFER_SIZE);
        r = csRecv(cs, xbuf, DEFAULT_BUFFER_SIZE);

        // data has been received
        if (r > 0) {
          slStruct *sl;

          // memory saving code
          if (r != DEFAULT_BUFFER_SIZE) {
            char *tmp = (char*)realloc(xbuf, r);
            if (tmp)  xbuf = tmp;
            else r = DEFAULT_BUFFER_SIZE;
          }
          assert(xbuf != NULL);

          sl = (slStruct*)malloc(sizeof(slStruct));
          sl->buf = xbuf;
          sl->pos = xbuf;
          sl->endp = sl->buf + r;
          sl->time = ct;
          sl->next = NULL;

          if (cs->slFirst == NULL) {
            cs->slFirst = sl;
            cs->slLast = sl;
          } else {
            cs->slLast->next = sl;
            cs->slLast = sl;
          }
        } else {

          free(xbuf);

          // zero means shutdown has been received
          if (r == 0) {
            cs->bits.slState = SL_SHUTDOWN;
          } else
          if (r == -1); // errors (handled in csRecv())
          else; // -2 means no data => do nothing
        }

      } while (r == DEFAULT_BUFFER_SIZE);
    }

    // return delayed data

    destp = (char*)buf;
    endp = destp + bufsize;

    while (1) {
      slStruct *sl = cs->slFirst;
      int slBufSize;
      int count;

      // empty delay queue
      if (sl == NULL) {
        switch (cs->bits.slState) {

          case SL_ACTIVE: break;

          case SL_WAITING_FLUSH: {
              cs->bits.slState = SL_NO_ACTIVE;

              // receive socket data
              if (destp < endp && cs->state == TN_CONNECTED) {
                r = csRecv(cs, destp, endp-destp);
                if (r > 0)
                  destp += r;
                else
                  if (r == 0)
                    csOnShutDownRecv(c,cs);
                  else;
              }
              break;
            }

          case SL_SHUTDOWN: {
              csOnShutDownRecv(c,cs);
              cs->bits.slState = SL_NO_ACTIVE;
              break;
            }

          default: assert(0 && "Unhandled cs->bits.slState value.");
        }
        return destp - ((char*)buf);
      }

      // full return buffer
      if (destp>=endp) {
        assert(destp==endp && "Writing behind the buffer.");
        return bufsize;
      }

      // check simulated latency
      if (cs->nextDeliverTime > ct)
        return destp - ((char*)buf);
      csGenerateNextDeliverTime(cs, sl->time);

      slBufSize = sl->endp - sl->pos;
      if (slBufSize > endp-destp)  count = endp-destp;
      else  count = slBufSize;

      memcpy(destp, sl->pos, count);
      destp += count;
      sl->pos += count;

      // update slStruct
      if (sl->pos == sl->endp) {
        // free slStruct
        free(sl->buf);
        cs->slFirst = sl->next;
        free(sl);
      }
    }

    assert(0 && "This point should be never reached.");
    return 0;
  }
}



bool_t tnInit()
{
  return tnInitDebug(NULL, 0);
}



bool_t tnInitDebug(TNErrorCb *cb, int debugLevel)
{
#if defined(_WIN32) || defined(__WIN32__)
  WORD versionRequested = 0x0202;
  WSADATA wsaData;
  int r;
#endif

  // update globals
  errorCb = (cb) ? cb : defaultErrorCb;
  //ps.pfTests = (debugLevel >= TN_PERFORM_TESTS);
  //  ps.debugLevel = (debugLevel & (TN_PERFORM_TESTS-1));

  // to avoid multiple initialization
  if (initialized)
    return 1;

  // Win32's WinSock initialization
#if defined(_WIN32) || defined(__WIN32__)

  r = WSAStartup(versionRequested, &wsaData);
  if (r != 0) {
    debugErrorS(0, "tnInit() failed in WSAStartup function.");
    return 0;
  }

  if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2 ) {
    WSACleanup();
    debugErrorS(0, "tnInit() failed because of wrong WinSock version.");
    return 0;
  }
#endif

  initialized = 1;

#if 0
  if (ps.pfTests) {

    ip_t ip1;
    port_t p1;
    ip_t ipUnreach;
    bool_t connectBySend;
    bool_t connectBySelectSend;

    // two listening sockets on the same port
    l1 = tnListen(0);
    l2 = tnListen(tnGetSockPort(l1));
    if (tnGetState(l1) != TN_LISTENING)
      debugMsgC(TN_ERROR, l1, "Behaviour of two listening "
                              "sockets on the same port is wrong. This should not happen.");
    if (tnGetState(l2) != TN_BROKEN)
      debugMsgC(TN_ERROR, l2, "Behaviour of two listening "
                              "sockets on the same port is wrong.");

    // connecting to an unreachable machine
    c1 = tnConnect(ipUnreach, 22, 0);
    if (c1 != -1) {
      enum TNState s = tnGetState(c1);
      if (s != TN_CONNECTING)
        if (s == TN_CONNECTED)
          debugMsgC(TN_WARNING, c1, "Unreachable machine connected.");
        else
          debugMsgC(TN_ERROR, c1, "Platform tests failed - tnConnect.");
      else
        debugMsgC(TN_INFO, c1, "TN_CONNECTING state is ok.");
    }
#error uncorrected csTestSend and csTestSelectSend
    connectBySend = (csTestSend(getConnectionStruct(c1)) == SEND_BLOCK);
    connectBySelectSend = (csTestSelectSend(getConnectionStruct(c1)) == TEST_NO);
    debugMsgC((!connectBySend && !connectBySelectSend && tnGetState(c1) == TN_CONNECTING)
              ? TN_ERROR : TN_INFO, c1,
              "Connecting functionality:\nsend: %c\nselect send: %c",
              (connectBySend ? "YES" : "NO"), (connectBySelectSend ? "YES" : "NO"));

  }

#if 0
  // tests for platform specific behaviour
  l1 = tnListen(0);
  assert(l1 != -1 && tnGetLocalPort(l1) != 0 &&
         "Problems detected while trying to listen on port 0 (any port).");

//  c1 = tnConnect(0xc0a800a0, tnGetLocalPort(l1), 0);
  c1 = tnConnect(0x7f000001, tnGetLocalPort(l1)+1, 0);
  if (c1 != -1) {
#error uncorrected csTestSend and csTestSelectSend
    ps.connectByTestSend = (csTestSend(getConnectionStruct(c1)) == SEND_BLOCK);
    ps.connectBySelectSend = (csTestSelectSend(getConnectionStruct(c1)) == TEST_NO);
    printf("%i %i\n", ps.connectByTestSend, ps.connectBySelectSend);
  }

  if (c1 == -1 || tnGetState(c1) != TN_CONNECTING) {
    ps.loopback = 0;
    debugErrorP("Loopback interface looks like not working properly "
                "(tnConnect failed).");
  } else {

    a1 = tnAccept(l1);
    if (a1 == -1 || tnGetState(a1) != TN_CONNECTED) {
      ps.loopback = 0;
      debugErrorP("Loopback interface looks like not working properly "
                  "(tnAccept failed).");
    } else
      ps.loopback = 1;

  }
#endif

  tnClose(l1);
  tnClose(l2);
#endif

  return 1;
}



void tnCleanUp()
{
  int i;

  if (!initialized)
    return;

  // kill all connections
  for (i=1; i<=csListSize; i++)
    if (getConnectionStructNoCheck(i)->state != TN_FREE)
      tnClose(i);

  // free connection list
  csOptimizeList();
  assert(csListSize == 0 && "This should never happen "
         "- something is wrong in csOptimizeList.");

#if defined(_WIN32) || defined(__WIN32__)
  WSACleanup();
#endif

  initialized = 0;
}



#define GET_CONNECTION_STRUCT(_rval_) \
  if (c > 0) \
    cs = getConnectionStruct(c); \
  else { \
    switch (param) { \
    case TN_GLOBAL_LATENCY: \
    case TN_GLOBAL_LATENCY_DISPERSION: \
    case TN_NUM_CONNECTIONS: \
    case TN_TOTAL_BYTES_SENT: \
    case TN_TOTAL_BYTES_RECEIVED: \
        cs = NULL; break; \
    default: return _rval_; \
    } \
  }


int tnGet(int param, TNConnection c)
{
  ConnectionStruct *cs;

  if (!initialized)  return 0;

  GET_CONNECTION_STRUCT(0);

  switch (param) {
  case TN_SOCKET:     return cs->uni.socket;
  case TN_PEER_IP:    if (cs->state == TN_LISTENING) return 0;
                      return cs->remoteIP;
  case TN_PEER_PORT:  if (cs->state == TN_LISTENING) return 0;
                      return cs->remotePort;
  case TN_LOCAL_IP:   if (cs->state == TN_CONNECTING) return 0;
                      return cs->localIP;
  case TN_LOCAL_PORT: if (cs->state == TN_CONNECTING) return 0;
                      return cs->localPort;
  case TN_ERROR:      assert(0 && "Not implemented."); return 0;

  case TN_SEND_BUFFER_SIZE:  if (cs->sendBufferSize == -1) {
                               int value;
                               if (getsockopt(cs->uni.socket, SOL_SOCKET, SO_SNDBUF, (char*)&value, &intSize) == SOCKET_ERROR) {
                                 debugError(cs, "tnGetInteger() failed in getsockopt().", errno);
                                 return 0;
                               }
                               cs->sendBufferSize = value;
                             }
                             return cs->sendBufferSize;
  case TN_RECV_BUFFER_SIZE:  if (cs->recvBufferSize == -1) {
                               int value;
                               if (getsockopt(cs->uni.socket, SOL_SOCKET, SO_RCVBUF, (char*)&value, &intSize) == SOCKET_ERROR) {
                                 debugError(cs, "tnGetInteger() failed in getsockopt().", errno);
                                 return 0;
                               }
                               cs->recvBufferSize = value;
                             }
                             return cs->recvBufferSize;
  case TN_APP_BUFFER_SIZE:   return cs->appBufferSize;

  case TN_LATENCY:                   return cs->slLatency_ms;
  case TN_LATENCY_DISPERSION:        return cs->slDispersion_ms;
  case TN_GLOBAL_LATENCY:            return globalLatency;
  case TN_GLOBAL_LATENCY_DISPERSION: return globalLatencyDispersion;
  
  case TN_NUM_CONNECTIONS:      {
                                  int n = csListSize;
                                  int i;
                                  for (i=1; i<=csListSize; i++)
                                    if (getConnectionStructNoCheck(i)->state == TN_FREE)  n--;
                                  return n;
                                }
  case TN_BYTES_SENT:           return cs->numBytesSent;
  case TN_BYTES_RECEIVED:       return cs->numBytesReceived;
  case TN_TOTAL_BYTES_SENT:     return totalBytesSent;
  case TN_TOTAL_BYTES_RECEIVED: return totalBytesReceived;
  default: assert(0 && "Not implemented."); return 0;
  }
}



void tnSet(int param, TNConnection c, int value)
{
  ConnectionStruct *cs;

  if (!initialized)  return;

  GET_CONNECTION_STRUCT(;);

  switch (param) {
  case TN_SEND_BUFFER_SIZE:  if (setsockopt(cs->uni.socket, SOL_SOCKET, SO_SNDBUF, (char*)&value, intSize) == SOCKET_ERROR) {
                               debugError(cs, "tnSetInteger() failed in setsockopt().", errno);
                             }
                             cs->sendBufferSize = -1;
                             return;
  case TN_RECV_BUFFER_SIZE:  if (setsockopt(cs->uni.socket, SOL_SOCKET, SO_RCVBUF, (char*)&value, intSize) == SOCKET_ERROR) {
                               debugError(cs, "tnSetInteger() failed in setsockopt().", errno);
                             }
                             cs->recvBufferSize = -1;
                             return;
  case TN_APP_BUFFER_SIZE:   if (cs->appBufferSize < value && cs->numUnsentData > (unsigned)value) {
                               csCloseSocket(cs);
                               cs->state = TN_BROKEN;
                               debugError(cs, "App buffer overflow in tnSetInteger().", 0);
                               return;
                             }
                             cs->appBufferSize = value;
                             return;

  case TN_LATENCY:  {
                      // protect against negative values
                      if (value < 0)  value = 0;

                      // if not global latency, mark latency data for flushing
                      if (globalLatency == -1) {
                        if (cs->state == TN_CONNECTED || cs->state == TN_CONNECTING) {
                          if (value == 0 && cs->slLatency_ms > 0)
                            cs->bits.slState = SL_WAITING_FLUSH;
                          else
                            if (cs->bits.slState != SL_SHUTDOWN && value > 0)
                              cs->bits.slState = SL_ACTIVE;
                        }
                      }

                      // set value
                      cs->slLatency_ms = value;
                      csGenerateNextDeliverTime(cs, getTime());
                      return;
                    }
  case TN_LATENCY_DISPERSION: cs->slDispersion_ms = value;
                              csGenerateNextDeliverTime(cs, getTime());
                              return;
  case TN_GLOBAL_LATENCY: {
                            // protect against all negative values except -1
                            if (value < -1)  value = -1;
                            if (value == globalLatency)  return;

                            // switch global latency off
                            if (globalLatency >= 0 && value <= 0) {
                              int i;
                              for (i=1; i<=csListSize; i++) {
                                cs = getConnectionStructNoCheck(i);
                                if (cs->state != TN_FREE) {
                                  if (cs->slLatency_ms == 0) {
                                    if (cs->state == TN_CONNECTED || cs->state == TN_CONNECTING)
                                      cs->bits.slState = SL_WAITING_FLUSH;
                                  } else
                                    csGenerateNextDeliverTime(cs, getTime());
                                }
                              }
                            } else
                            // switch global latency on
                            if (globalLatency <= 0 && value >= 0) {
                              int i;
                              for (i=1; i<=csListSize; i++) {
                                cs = getConnectionStructNoCheck(i);
                                if (cs->state != TN_FREE) {
                                  if (cs->slLatency_ms > 0) {
                                    if (cs->state == TN_CONNECTED || cs->state == TN_CONNECTING)
                                      if (cs->bits.slState != SL_SHUTDOWN)
                                        cs->bits.slState = SL_ACTIVE;
                                  } else
                                    csGenerateNextDeliverTime(cs, getTime());
                                }
                              }
                            }

                            globalLatency = value;
                            return;
                          }
  case TN_GLOBAL_LATENCY_DISPERSION: globalLatencyDispersion = value; return;
  
  case TN_BYTES_SENT:           cs->numBytesSent = value;
  case TN_BYTES_RECEIVED:       cs->numBytesReceived = value;
  case TN_TOTAL_BYTES_SENT:     totalBytesSent = value;
  case TN_TOTAL_BYTES_RECEIVED: totalBytesReceived = value;
  default: assert(0 && "Not implemented."); return;
  }
}



#if 0 // currently disabled
const char* tnGetHostName()
{
  if (!initialized) return "";

  // get name
  if (gethostname(hostNameBuf, MAX_HOST_NAME_LEN) != 0) {
    hostNameBuf[0] = '\0';
    assert(0 && "tnGetHostName() failed in gethostname function.");
    debugErrorS(0, "tnGetHostName() failed in gethostname function.");
  }

  return hostNameBuf;
}
#endif



ip_t* tnGetLocalIPAddresses()
{
  int i, localhost;
  ip_t *list;
  char hostName[MAX_HOST_NAME_LEN];
  struct hostent *info;

  if (!initialized) return 0;

  if (gethostname(hostName, MAX_HOST_NAME_LEN) != 0) {
    hostName[0] = '\0';
    debugError(NULL, "tnGetLocalIPAddresses() failed in gethostname function.", errno);
    assert(0 && "tnGetLocalIPAddresses() failed in gethostname function.");
    return 0;
  }

  info = gethostbyname(hostName);
  if (info == NULL) {
    debugError(NULL, "tnGetLocalIPAddresses() failed because of gethostbyname() error.", errno);
    assert(0 && "gethostbyname function does not work.");
    return 0;
  }

  // count number of IP addresses
  localhost = 0;
  for (i=0;; i++) {
    if (info->h_addr_list[i] == NULL) break;
    if (ntohl(*((unsigned int*)info->h_addr_list[i])) == 0x7f000001)
      localhost = 1;
  }
  if (!localhost) i++;
  i++;

  list = (ip_t*)malloc(sizeof(ip_t)*i);
  assert(list && "Out of memory.");

  for (i=0;; i++) {
    if (info->h_addr_list[i] == NULL) break;
    list[i] = ntohl(*((unsigned int*)info->h_addr_list[i]));
  }
  if (!localhost)
    list[i++] = 0x7f000001;

  // the last record is zero
  list[i] = 0;

  return list;
}



void tnFreeMem(void *ptr)
{
  free(ptr);
}



/*! Converts IP in integer representation into the zero terminated string.
 *  If the buffer is not large enough, it is left unmodified and 0 is returned.
 */
bool_t tnIP2Str(ip_t ip, char *buf, int bufsize)
{

#if 0 // we can use standard inet_ntoa function instead of our own code

static int bufNeeds(const int a)
{
  assert(a>=0 && a<=255 && "Wrong usability. Parameter a is supposed to be just single byte (in 0..255 range).");

  if (a>=100) return 3;
  else if (a>=10) return 2;
  else return 1;
}

  int parts[4];
  int i;
  int x;
  int bufsizeNeeded;

  assert(buf != NULL || bufsize == 0 && "Passing NULL buffer to the tnIP2Str() function.");

  parts[0] = (ip >> 24);
  parts[1] = (ip >> 16) & 0xff;
  parts[2] = (ip >>  8) & 0xff;
  parts[3] = ip & 0xff;

  bufsizeNeeded = 3+1 + bufNeeds(parts[0]) + bufNeeds(parts[1]) +
                  bufNeeds(parts[2]) + bufNeeds(parts[3]);

  if (bufsize < bufsizeNeeded)
    return 0;

  for (i=0; i<4; i++) {
    // hundreds
    x = parts[i] / 100;
    if (x != 0)
      *(buf++) = x +'0';
    // teens
    x = parts[i] % 100 / 10;
    if (x != 0)
      *(buf++) = x +'0';
    // ones
    x = parts[i] % 10;
    *(buf++) = x +'0';
    // dot
    *(buf++) = '.';
  }

  *(--buf) = 0;
  return 1;

#else

  struct in_addr addr;
  char *r;

  assert(buf != NULL || bufsize == 0 && "Passing NULL buffer to the tnIP2Str() function.");

  addr.s_addr = htonl(ip);
  r = inet_ntoa(addr);

  if (r == NULL)
    return 0;
  if ((signed)(strlen(r)+1) > bufsize)
    return 0;

  strcpy(buf, r);
  return 1;

#endif
}



/*! Converts IP address in the string representation into the integer representation.
 *  The IP address string must be zero terminated.
 *
 *  The function works for hexadecimal and octal numbers also.
 *  The address must be composed of four parts.
 */
bool_t tnStr2IP(const char *s, ip_t *ip)
{
#if 0 // inet_addr function that are used for conversion has problems with converting address
      // 255.255.255.255. Therefore, we are using our own code.

  if (s == NULL)
    return 0;

  ip_t r = ntohl(inet_addr(s));

  if (r != INADDR_NONE) {
    *ip = r;
    return 1;
  } else
    return 0;

#else

  int a,b,c,d,r;

  if (s == NULL)
    return 0;

  r = sscanf(s, "%i.%i.%i.%i", &a,&b,&c,&d);
  if (r != 4)
    return 0;

  if (a<0 || a>255 || b<0 || b>255 ||
      c<0 || c>255 || d<0 || d>255)
    return 0;

  (*ip) = a<<24 | b<<16 | c<<8 | d;
  return 1;

#endif
}



/*! The function reads IP address from the string given by parameter s.
 *  The string s is text from which IP address is extracted.
 *  After the extraction, s is updated to point after the IP address.
 *  The end of IP address is detected by character different from 0..9, ".", x, and X.
 *  X and x characters are listed here, because hexadecimal numbers in the form 0x00 can be
 *  used in the address also.
 *  In the case of error, nor s, nor ip is updated.
 */
bool_t tnReadIP(const char **s, ip_t *ip)
{
  char buf[21];
  int i;
  bool_t r;
  const char *w;

  assert(s != NULL && "Passing NULL pointer to the tnReadIP() function.");
  if (s == NULL || *s == NULL)
    return 0;

  i = 0;
  w = *s;
  do {
    buf[i] = *w;
    if (*w == 0) break;
    i++;
    w++;
  } while (i<21);
  buf[20] = 0;

  for (i=0; i<21; i++) {
    char c = buf[i];
    if (c>='0' && c<='9')
      continue;
    if (c=='.' || c=='x' || c=='X')
      continue;
    break;
  }
  buf[i] = 0;

  r = tnStr2IP(buf, ip);
  if (r)
    (*s) += i;
  return r;
}



static void csCreateSocket(ConnectionStruct *cs)
{
  assert(cs->state == TN_CONNECTING);

  // create socket
  cs->uni.socket = socket(AF_INET, SOCK_STREAM, 0);
  if (cs->uni.socket == INVALID_SOCKET) {
    debugError(cs, "csCreate() failed while creating a socket.", errno);
    cs->state = TN_BROKEN;
  }
}



static enum CLOSE_STATE sockClose(SOCKET s)
{
  if (closesocket(s) == 0)  return CLOSE_OK;
  else {
    if (errno == EWOULDBLOCK)  return CLOSE_BLOCK;
    else {
      debugErrorS(30, "csCloseSocket() failed in close or closesocket function.\n"
                      "Details:\n"
                      "socket: %i,\n"
                      "errno: %i.\n", s, errno);
      return CLOSE_FAIL;
    }
  }
}



static enum CLOSE_STATE csTryCloseSocket(ConnectionStruct *cs)
{
  enum CLOSE_STATE r;

  // avoid invalid sockets
  assert(cs->state != TN_FREE);
  assert(cs->uni.socket != INVALID_SOCKET && "Invalid socket.");

  // close socket
  r = sockClose(cs->uni.socket);

  if (r == CLOSE_OK) {
    cs->uni.socket = INVALID_SOCKET;
  }

  return r;
}



static void csCloseSocket(ConnectionStruct *cs)
{
  enum CLOSE_STATE r;

  // avoid invalid sockets
  assert(cs->state != TN_FREE);
  assert(cs->uni.socket != INVALID_SOCKET && "Invalid socket.");

  // release queues
  csReleaseSlQueue(cs);
  csReleaseDsQueue(cs);
  
  // close socket
  r = csTryCloseSocket(cs);

  if (r == CLOSE_BLOCK || r == CLOSE_FAIL) {
    debugError(cs, "csCloseSocket() failed (probably forgot to set LINGER to IMMEDIATE).", errno);
    cs->uni.socket = INVALID_SOCKET;
  }
}



static enum CONNECT_STATE csConnect(ConnectionStruct *cs, ip_t ip, port_t port)
{
  struct sockaddr_in sockAddr;
  int r;

  if (cs->state == TN_BROKEN)
    return CONNECT_FAIL;

  assert(cs->state == TN_CONNECTING && "Wrong state.");
  assert(cs->uni.socket != INVALID_SOCKET && "Invalid socket.");

  // connect
  sockAddr.sin_family = AF_INET;
  sockAddr.sin_port = htons(port);
  sockAddr.sin_addr.s_addr = htonl(ip);
  r = connect(cs->uni.socket, (struct sockaddr*)&sockAddr, sizeof(sockAddr));
  if (r == 0)
    return CONNECT_OK;
  else {
    if (errno == EINPROGRESS || errno == EWOULDBLOCK)
        // on Linux connect returns EINPROGRESS, on Windows EWOULDBLOCK
      return CONNECT_INPROGRESS;
    else {
      debugError(cs, "csConnect() failed in connect function.", errno);
      csCloseSocket(cs);
      cs->state = TN_BROKEN;
      return CONNECT_FAIL;
    }
  }
}



static void csMakeListening(ConnectionStruct *cs)
{
  if (cs->state == TN_BROKEN)
    return;

  assert(cs->state == TN_CONNECTING && "Wrong state.");
  assert(cs->uni.socket != INVALID_SOCKET && "Invalid socket.");

  // listen
  if (listen(cs->uni.socket, 5) != 0) {
    debugError(cs, "csMakeListening() failed in listen function.", errno);
    csCloseSocket(cs);
    cs->state = TN_BROKEN;
    return;
  }
}



static bool_t csAccept(ConnectionStruct *cs, SOCKET *newSocket,
    ip_t *remoteIP, port_t *remotePort)
{
  struct sockaddr_in sockAddr;
  socklen_t addrSize;

  if (cs->state == TN_BROKEN)
    return 0;

  assert(cs->state == TN_LISTENING);
  assert(cs->uni.socket != INVALID_SOCKET && "Invalid socket.");

  addrSize = sizeof(sockAddr);
  *newSocket = accept(cs->uni.socket, (struct sockaddr*)&sockAddr, &addrSize);

  if (*newSocket == INVALID_SOCKET) {
    if (errno == EWOULDBLOCK)  return 0;
    else {
      debugError(cs, "csAccept failed in accept function.", errno);
      return 0;
    }
  }

  *remoteIP = ntohl(sockAddr.sin_addr.s_addr);
  *remotePort = ntohs(sockAddr.sin_port);

  return 1;
}



static void csShutDown(ConnectionStruct *cs)
{
  if (cs->state == TN_BROKEN)
    return;

  assert(cs->uni.socket != INVALID_SOCKET && "Invalid socket.");

  if (shutdown(cs->uni.socket, SD_SEND) != 0) {
    debugError(cs, "csShutDown() failed in shutdown function.", errno);
    csCloseSocket(cs);
    cs->state = TN_BROKEN;
  }
}



static void csOnShutDownRecv(TNConnection c, ConnectionStruct *cs)
{
  csReleaseDsQueue(cs);
  if (cs->slFirst)
    cs->bits.slState = SL_SHUTDOWN;
  else {
    csShutDown(cs);
    if (cs->state == TN_BROKEN)
      return;
    cs->state = TN_DISCONNECTING;
    tnGetState(c); // turn to disconnected if possible
  }
}



static enum RECV_STATE csTestRecv(ConnectionStruct *cs)
{
  char buf;
  int n;

  if (cs->state == TN_BROKEN)
    return RECV_FAIL;

  assert(cs->uni.socket != INVALID_SOCKET && "Invalid socket.");

  n = recv(cs->uni.socket, &buf, sizeof(buf), MSG_PEEK);

  switch (n) {

  // empty socket and socket errors
  case SOCKET_ERROR:
      if (errno == EWOULDBLOCK)  return RECV_EMPTY;
      else  return RECV_FAIL;

  // closing socket (requested by peer)
  case 0:
      return RECV_SHUTDOWN;

  // data to read
  default: // now, n must be greater than 0
      return RECV_DATA;
  }
}



static enum SEND_STATE csTestSend(ConnectionStruct *cs)
{
  char buf;
  int n;

  if (cs->state == TN_BROKEN)
    return SEND_FAIL;

  assert(cs->uni.socket != INVALID_SOCKET && "Invalid socket.");

  n = send(cs->uni.socket, &buf, 0, 0);

  assert(n <= 0 && "send() is not working properly!");

  // socket is ok
  if (n != SOCKET_ERROR)  return SEND_OK;

  // blocking for some reason
  if (errno == EWOULDBLOCK)  return SEND_BLOCK;

  // socket errors
  return SEND_FAIL;
}



static enum TEST_VALUE csTestError(ConnectionStruct *cs)
{
  int e;
  int r;

  if (cs->state == TN_BROKEN)
    return TEST_FAIL;

  assert(cs->uni.socket != INVALID_SOCKET && "Invalid socket.");

  r = getsockopt(cs->uni.socket, SOL_SOCKET, SO_ERROR,
                     (char*)&e, &intSize);

  // getsockopt failed
  if (r == SOCKET_ERROR) {
    debugError(cs, "csTestError() failed in getsockopt function.\n"
                   "Something is wrong with your network.", errno);
    return TEST_FAIL;
  }

  // no error
  if (e == 0)
    return TEST_NO;

  // error detected
  setLastError(e);
  return TEST_YES;
}



#if 0 // this function is never used; it is here just for completeness and experimental purposes
static enum TEST_VALUE csTestSelectRecv(ConnectionStruct *cs)
{
  fd_set set;
  int r;

  if (cs->state == TN_BROKEN)
    return TEST_FAIL;

  assert(cs->uni.socket != INVALID_SOCKET && "Invalid socket.");

  FD_ZERO(&set);
  FD_SET(cs->uni.socket, &set);

  r = select(cs->uni.socket+1, &set, NULL, NULL, &zeroTimeval);

  if (r == SOCKET_ERROR) {
    debugError(cs, "csTestSelectRecv() failed in select function.\n"
                   "Something is wrong with your network.", errno);
    return TEST_FAIL;
  }

  return r > 0 ? TEST_YES : TEST_NO;
}
#endif



// functions used on Windows only (otherwise commented out to avoid compiler warnings
#if defined(__WIN32__) || defined(_WIN32)
static enum TEST_VALUE csTestSelectSend(ConnectionStruct *cs)
{
  fd_set set;
  int r;

  if (cs->state == TN_BROKEN)
    return TEST_FAIL;

  assert(cs->uni.socket != INVALID_SOCKET && "Invalid socket.");

  FD_ZERO(&set);
  FD_SET(cs->uni.socket, &set);

  r = select(cs->uni.socket+1, NULL, &set, NULL, &zeroTimeval);

  if (r == SOCKET_ERROR) {
    debugError(cs, "csTestSelectSend() failed in select function.\n"
                   "Something is wrong with your network.", errno);
    return TEST_FAIL;
  }

  return r > 0 ? TEST_YES : TEST_NO;
}

static enum TEST_VALUE csTestSelectError(ConnectionStruct *cs)
{
  fd_set set;
  int e;

  if (cs->state == TN_BROKEN)
    return TEST_FAIL;

  assert(cs->uni.socket != INVALID_SOCKET && "Invalid socket.");

  FD_ZERO(&set);
  FD_SET(cs->uni.socket, &set);

  e = select(cs->uni.socket+1, NULL, NULL, &set, &zeroTimeval);

  // select failed
  if (e == SOCKET_ERROR) {
    debugError(cs, "csTestSelectError() failed in select function.\n"
                   "Something is wrong with your network.", errno);
    return TEST_FAIL;
  }

  // no error present
  if (e == 0)
    return TEST_NO;

  // error detected
  switch(csTestError(cs)) {
  case TEST_YES:  return TEST_YES;
  case TEST_NO:   assert(0 && "select says there is an error on the socket and getsockopt says no error.");
                  return TEST_NO;
  case TEST_FAIL:
  default:        return TEST_FAIL;
  }
}
#endif



static void csOnConnected(ConnectionStruct *cs)
{
  struct sockaddr_in sockAddr;
  socklen_t addrSize = sizeof(sockAddr);
  int r;

  if (cs->state == TN_BROKEN)
    return;

  assert(cs->state == TN_CONNECTED && "csOnConnected() called for connection in different state then TN_CONNECTED.");
  assert(cs->uni.socket != INVALID_SOCKET && "Invalid socket.");

  r = getsockname(cs->uni.socket, (struct sockaddr*)&sockAddr, &addrSize);
  if (addrSize == 0 || r == SOCKET_ERROR) {
    debugError(cs, "csOnConnected failed because of getsockname() error.", errno);
    return;
  }

  cs->localIP = ntohl(sockAddr.sin_addr.s_addr);
  cs->localPort = ntohs(sockAddr.sin_port);
}



static void csSetNonBlocking(ConnectionStruct *cs, bool_t nb)
{
  unsigned long nonblocking;

  if (cs->state == TN_BROKEN)
    return;

  assert(cs->uni.socket != INVALID_SOCKET && "Invalid socket.");

  // make socket non-blocking
  nonblocking = nb ? 1 : 0;
  if (ioctl(cs->uni.socket, FIONBIO, &nonblocking) != 0) {
    // FIXME: handle "run-time" connection errors
    debugError(cs, "csSetNonBlocking() failed while setting "
                   "non-blocking mode.", errno);
    csCloseSocket(cs);
    cs->state = TN_BROKEN;
  }
}



static void csSetLinger(ConnectionStruct *cs, enum LINGER_ENUM l)
{
  struct linger lin;

  if (cs->state == TN_BROKEN)
    return;

  assert(cs->uni.socket != INVALID_SOCKET && "Invalid socket.");

  lin.l_onoff = (l == LIN_AUTOMATIC ? 0 : 1);
  lin.l_linger = (l == LIN_TIMEOUT ? 1 : 0);

  if (setsockopt(cs->uni.socket, SOL_SOCKET, SO_LINGER,
      (const char*)&lin, sizeof(lin)) != 0) {
    // FIXME: handle "run-time" connection errors
    debugError(cs, "csSetLinger failed in setsockopt function.", errno);
  }
}



static void csSetNonNagle(ConnectionStruct *cs, bool_t nn)
{
  int ndis;

  if (cs->state == TN_BROKEN)
    return;

  assert(cs->uni.socket != INVALID_SOCKET && "Invalid socket.");

  // disable Nagle Algorithm
  ndis = nn ? 1 : 0;
  if (setsockopt(cs->uni.socket, IPPROTO_TCP, TCP_NODELAY,
      (const char*)&ndis, sizeof(ndis)) != 0) {
    debugError(cs, "csSetNonNagle() failed in setsockopt function.", errno);
    // note: broken state is not set here, because it is not a critical error
  }
}



static void csSetReuseAddr(ConnectionStruct *cs, bool_t ra)
{
  int reuse;
  if (cs->state == TN_BROKEN)
    return;

  assert(cs->uni.socket != INVALID_SOCKET && "Invalid socket.");

  // set reuse-addr to true
  reuse = ra ? 1 : 0;
  if (setsockopt(cs->uni.socket, SOL_SOCKET, SO_REUSEADDR,
      (const char*)&reuse, sizeof(reuse)) != 0) {
    debugError(cs, "csSetReuseAddr() failed in setsockopt function.", errno);
    csCloseSocket(cs);
    cs->state = TN_BROKEN;
  }
}



static void csBind(ConnectionStruct *cs)
{
  struct sockaddr_in sockAddr;

  if (cs->state == TN_BROKEN)
    return;

  assert(cs->state == TN_CONNECTING && "Wrong state.");
  assert(cs->uni.socket != INVALID_SOCKET && "Invalid socket.");

  // bind port
  sockAddr.sin_family = AF_INET;
  sockAddr.sin_port = htons(cs->localPort);
  sockAddr.sin_addr.s_addr = htonl(cs->localIP);
  if (bind(cs->uni.socket, (struct sockaddr*)&sockAddr, sizeof(sockAddr)) != 0) {
    debugError(cs, "csBind() failed in bind function.", errno);
    csCloseSocket(cs);
    cs->state = TN_BROKEN;
  }
}



static void csAlloc(ConnectionStruct **cs, TNConnection *c)
{
  if (csFirstFree == 0) {
    ConnectionStruct *temp = (ConnectionStruct*)realloc(csList,
        (csListSize+1) * sizeof(ConnectionStruct));
    if (temp == NULL) {
      debugErrorS(0, "csAlloc() can not realloc memory.");
      *cs = NULL;
      *c = 0;
      return;
    }
    csList = temp;
    *c = ++csListSize;
    *cs = getConnectionStructNoCheck(csListSize);
    (*cs)->state = TN_FREE;
  } else {
    *c = csFirstFree;
    *cs = getConnectionStructNoCheck(csFirstFree);
    csFirstFree = (*cs)->uni.nextFree;
  }
  (*cs)->uni.socket = INVALID_SOCKET;
  assert((*cs)->state == TN_FREE && "Debugging code.");

  (*cs)->numBytesSent = 0;
  (*cs)->numBytesReceived = 0;
  (*cs)->bits.disconIncompleteCon = 0;
  (*cs)->dsFirst = NULL;
  (*cs)->dsLast = NULL;
  (*cs)->bits.dsQueueShutdownScheduled = 0;
  (*cs)->numUnsentData = 0;
  (*cs)->slFirst = NULL;
  (*cs)->slLast = NULL;
  (*cs)->recvBufferSize = -1;
  (*cs)->sendBufferSize = -1;
  (*cs)->appBufferSize = 0;

  // set simulated latency
  (*cs)->slLatency_ms = 0;
  (*cs)->slDispersion_ms = 0;
  if (globalLatency == -1)  (*cs)->bits.slState = SL_NO_ACTIVE;
  else  (*cs)->bits.slState = SL_ACTIVE;
}



static void csRelease(ConnectionStruct *cs, TNConnection c)
{
  // to avoid possible bugs
  assert(c > 0 && c <= csListSize && "Invalid connection index.");
  assert(cs->uni.socket == INVALID_SOCKET && "The socket is still valid.");
  assert(cs->state != TN_FREE && "Invalid state.");
  assert(cs->dsFirst == NULL && cs->numUnsentData == 0 &&
         "Delayed send queue has not been released.");
  assert(cs->slFirst == NULL && "Simulated latency queue has not been released.");

  // free struct
  cs->state = TN_FREE;
  cs->uni.nextFree = csFirstFree;
  csFirstFree = c;

  csOptimizeList();
}



static void csOptimizeList()
{
  int origSocketCount;
  bool_t modif;

  if (csFirstFree == 0) return;

  origSocketCount = csListSize;

  do {
    modif = 0;

    if (csFirstFree == csListSize) {
      csFirstFree = getConnectionStructNoCheck(csFirstFree)->uni.nextFree;
      csListSize--;
      modif = 1;
    } else {
      TNConnection item = getConnectionStructNoCheck(csFirstFree)->uni.nextFree;
      TNConnection prevItem = csFirstFree;
      while (item != 0) {
        modif = (item == csListSize);
        if (modif) {
          getConnectionStructNoCheck(prevItem)->uni.nextFree =
              getConnectionStructNoCheck(item)->uni.nextFree;
          csListSize--;
          break;
        }
        prevItem = item;
        item = getConnectionStructNoCheck(item)->uni.nextFree;
      }
    }

    if (csFirstFree == 0) return;
  } while (modif);

  if (origSocketCount != csListSize) {
    if (csListSize > 0) {
      ConnectionStruct *temp = (ConnectionStruct*)realloc(csList,
          csListSize*sizeof(ConnectionStruct));
      if (temp != NULL)
        csList = temp;
    } else {
      free(csList);
      csList = NULL;
    }
  }
}




#if 0
static const char* tnStateToStr(enum TNState s)
{
  switch (s) {
  case TN_FREE:           return "TN_FREE";
  case TN_CONNECTING:     return "TN_CONNECTING";
  case TN_CONNECTED:      return "TN_CONNECTED";
  case TN_LISTENING:      return "TN_LISTENING";
  case TN_DISCONNECTING:  return "TN_DISCONNECTING";
  case TN_DISCONNECT_REQ: return "TN_DISCONNECT_REQ";
  case TN_DISCONNECTED:   return "TN_DISCONNECTED";
  case TN_BROKEN:         return "TN_BROKEN";
  default:                return "INVALID_STATE";
  }
}
#endif



const char* tnError2Str(int err)
{
#if !defined(_WIN32) && !defined(__WIN32__)

  return strerror(err);

#else

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



static void debugInfo(const ConnectionStruct *cs, const char *msg, int errorCode)
{
#if 0
  va_list va;
  va_start (va, msg);
  debugPrint("TNet info:", cs, msg, &va);
  va_end(va);
#else
  TNConnection c = (cs == NULL) ? -1 : getConnectionByStruct(cs);
  errorCb(TN_SEV_INFO, c, msg, errorCode);
#endif
}



static void debugError(const ConnectionStruct *cs, const char *msg, int errorCode)
{
  TNConnection c = (cs == NULL) ? -1 : getConnectionByStruct(cs);
  errorCb(TN_SEV_ERROR, c, msg, errorCode);
}



static void debugErrorS(int appendChars, const char *msg, ...)
{
  int size,realSize;
  char *buf;

  va_list va;
  va_start (va, msg);

  size = strlen(msg) + appendChars + 1;
  buf = (char*)malloc(size);

  if (buf == NULL) {
    assert(0 && "Can not allocate memory.");
    goto end;
  }

  realSize = vsprintf(buf, msg, va) + 1;
  assert(realSize<=size && "Message buffer overflow.");

  debugError(NULL, buf, 0);

end:
  va_end(va);
}



void defaultErrorCb(enum TNErrorSeverity severity, TNConnection c, const char *str, int errorCode)
{
#ifdef _WIN32
  // This code takes care about opening console window on Windows and directing stderr to it.
  static int consoleAllocated = 0;
  if (consoleAllocated == 0) {
    int hCrt;
    FILE *hf;
    consoleAllocated = 1;
    
    if (!AllocConsole()) {
#if 0 // This assert is commented out since its look like that if console exists,
      // AllocConsole() fails. And I did not find appropriate routine to detect
      // whether console is already allocated. PCJohn 2005-08-29
      assert(0 && "AllocConsole() failed.");  
#endif
    }

    HANDLE hstderr = GetStdHandle(STD_ERROR_HANDLE);
    if (hstderr == INVALID_HANDLE_VALUE) {
      assert(0 && "GetStdHandle() failed.");  
    }

    hCrt = _open_osfhandle((long)hstderr , _O_TEXT);
    hf = _fdopen(hCrt, "w");
    *stderr = *hf;
    if (setvbuf(stderr, NULL, _IONBF, 0) != 0)
      assert(0 && "setvbuf() failed.");
  }
#endif

  if (severity == TN_SEV_ERROR)
    fprintf(stderr, "Network error:\n");
  else
    fprintf(stderr, "Network info:\n");

  fprintf(stderr, "  %s\n"
                  "  Connection id: %i, errno: %i, error string: %s\n",
                  str, c, errorCode, tnError2Str(errorCode));
}



static double getTime()
{
#if !defined(_WIN32) && !defined(__WIN32__)

  struct timeval tv;
  int r = gettimeofday(&tv, NULL);
  assert(r == 0 && "System function gettimeofday() failed.");
  return ((double)tv.tv_sec) + ((double)tv.tv_usec)*1e-6;

#else

  static bool_t initialized = 0;
  static double frequency;
  static double startupTime;
  LARGE_INTEGER v;
  bool_t b;

  if (!initialized) {

    bool_t available;
    
    initialized = 1;
    available = (QueryPerformanceFrequency(&v) != 0);
    if (!available) {
      assert(0 && "QueryPerformanceFrequency() does not work. This should not ever "
             "happen since QueryPerformanceFrequency works on all known systems "
             "as far as developers of this code know.");
      exit(EXIT_FAILURE);
    }

    frequency = 1./(DOUBLE0x100000000*v.HighPart + v.LowPart);
    b = (QueryPerformanceCounter(&v) != 0);
    assert(b && "QueryPerformanceCounter() failed although QueryPerformanceFrequency() worked.");
    startupTime = (DOUBLE0x100000000*v.HighPart + v.LowPart)*frequency;

    return 0.;
  }

  b = (QueryPerformanceCounter(&v) != 0);
  assert(b && "QueryPerformanceCounter() failed although QueryPerformanceFrequency() worked.");
  return (DOUBLE0x100000000*v.HighPart + v.LowPart)*frequency - startupTime;

#endif
}



static const int randAdd    = 1537L;
static const int randMult   = 1220703125L;
static const int randMaxInt = 0x7fffffff;
static int randSeed = randAdd;

static int randCore()
{
  randSeed *= randMult;
  randSeed &= randMaxInt; // keep sign bit zero
  return randSeed;
}

static int randi(int dispersion)
{
  int v,d,r;

  // protect against zero dispersion
  if (dispersion <= 0)
    return 0;

  dispersion *= 2; // extend dispersion to negative values also
  dispersion += 1; // make possible to generate values equal to dispersion
  
  // the loop is neccessary for linear value distribution
  do {
    v = randCore();
    d = v / dispersion;
    r = v % dispersion;
  } while (v >= randMaxInt-dispersion && // <- this is optimization for next line
           v >= randMaxInt / dispersion * dispersion);

  return r - dispersion;
}

static double randd(double dispersion)
{
  return double(randCore())/randMaxInt * 2. * dispersion - dispersion;
}



static void csReleaseSlQueue(ConnectionStruct *cs)
{
  slStruct *sl = cs->slFirst;
  slStruct *tmp;
  while (sl != NULL) {
    tmp = sl;
    sl = sl->next;
    free(tmp);
  }
  cs->slFirst = NULL;
  cs->slLast = NULL;
}



static void csReleaseDsQueue(ConnectionStruct *cs)
{
  dsStruct *ds = cs->dsFirst;
  dsStruct *tmp;
  while (ds != NULL) {
    tmp = ds;
    cs->numUnsentData -= ds->endp - ds->pos;
    ds = ds->next;
    free(tmp);
  }
  cs->dsFirst = NULL;
  cs->dsLast = NULL;
}



#undef MAX_HOST_NAME_LEN
