// sos.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "sos.h"
#include "string.h"
#include "winsock2.h"
#include "process.h"
#include <sys/types.h>
#include <sys/timeb.h>

#include <io.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include "signal.h"

/********************************************************************
*                                                                   *
*    Echo  Server   (Gennaio 2001)                                  *
*                                                                   *
*    Port 3123   TCP/IP protocol                                    *
*                                                                   *
*    Il client invia un messaggio formato da un numero              *
*    intero (4 byte) seguito dai byte di cui si vuole avere l'echo. *
*    Il server risponde allo stesso modo.                           *
*                                                                   *
*    Il progetto va fatto come "applicazione da console"            *
*    Linkare con ws2_32.lib                                         *
*                                                                   *
*    ATTENZIONE: ci sono due problemi:                              *
*       non funziona la free di una variabile dinamica              *
*           (controllare il manuale della libreria MS               *
*       il time out sul close socket (aspetta finche' non e' stato  *
*            spedito tutto il contenuto del buffer) funziona su     *
*            WindowsNT ma non su Windows98.                         *
*            Lasciare lo Sleep di 1 sec.                            *
*                                                                   *
*********************************************************************/


// CWinApp theApp;  // trick to use C++ classes

using namespace std;

void main(int argc, TCHAR* argv[], TCHAR* envp[])
{
  SOCKADDR_IN accept_sin;      // Receives the address of the 
                                  // connecting entity
  int  accept_sin_len;         // Length of accept_sin

  // initialize MFC and print an error on failure
  if (!AfxWinInit(::GetModuleHandle(NULL), NULL, 
                  ::GetCommandLine(), 0))
    {
     ErrF1 (SMFC, 0);
     exit(1);
    }

  clog << "Start echo server" << endl;
  clog << endl;

  signal(SIGINT, intr_ctrC);  // catch ^C

  startConn();   // starts TCP/IP connection

  accept_sin_len = sizeof (accept_sin);
  for (;;)
   {
    // Accept an incoming connection attempt on WinSocket.
    tmpSock=accept (WinSocket,
        (struct sockaddr *) &accept_sin,(int *) &accept_sin_len);

    // Stop listening for connections from clients.
    if (tmpSock == INVALID_SOCKET) 
      {
       ErrF1(Sacc, WSAGetLastError());
       continue;
      }

    flag=1;
    errorThread=_beginthread(parse, 0, NULL);

    if (errorThread < 0)
     {
       flag=0;
       ErrF1(Sthread, errno);
       closesocket(tmpSock);
       continue;
     }

    while (flag) ;   // tmpSock and endthread can be reassigned

  }  // end for
}

// thread function
void parse (void *dummy)
{
  SOCKET ClientSock = INVALID_SOCKET;   // Socket for connection
  unsigned long IdThread;               // thread identifier
  char * msg=NULL;
  int  len;

  ClientSock=tmpSock;
  IdThread = errorThread;
  flag=0;   // =1 until the socket and thread id have been copied

  clog << "begin thread " << IdThread << endl;

  options(ClientSock, IdThread); // set socket options
  
  if ((recvS (ClientSock, IdThread, (char *)&len, 4))<0) goto exitT;
  if ((len <= 0))
    ErrF2(ClientSock, IdThread, Ssize, 0);
  msg=Alloc(ClientSock, IdThread, len);
  if ((recvS (ClientSock, IdThread, msg, len))<0) goto exitT;
  if ((sendS(ClientSock, IdThread, (char *)&len, 4))<0) goto exitT;
  if ((sendS(ClientSock, IdThread, msg, len))<0) goto exitT;
  Sleep (1000);

exitT:
//  if (msg != NULL) free((void *)msg);   non funziona!!!
  closesocket(ClientSock);
  clog << "end Thread " << IdThread << endl;
  _endthread();
}


/************************************************
 ************************************************
 *******                                 ********
 *******      Auxiliary Routines         ********
 *******                                 ********
 ************************************************
 ************************************************/

/* create WinSocket, the primary socket, and get socket options */ 
void startConn()
{
  WSADATA WSAData;            // Contains details of the Windows
  SOCKADDR_IN local_sin;      // Local socket address
  int len, ret;

  // Initiate Windows Sockets (WinSock Version 1.1)
  if (WSAStartup (MAKEWORD(1,1), &WSAData) != 0) 
    {
     ErrF1(SWSA, WSAGetLastError());
     WSACleanup();
     exit(1);
    }

  // Create a TCP/IP socket, WinSocket.
  if ((WinSocket = socket (AF_INET, SOCK_STREAM, 
                  IPPROTO_TCP)) == INVALID_SOCKET) 
    {
     ErrF1(Salloc, WSAGetLastError());
     WSACleanup();
     exit(1);
    }

  len=4;   // get the send buffer length
  ret=getsockopt(WinSocket, SOL_SOCKET, SO_SNDBUF, 
                 (char *)&sendBuf, &len);
  if (ret==SOCKET_ERROR) 
     ErrF0(WinSocket, SsendT, WSAGetLastError());
  clog << "send buffer length = " << sendBuf << endl;

  len=4;   // get the recv buffer length
  ret=getsockopt(WinSocket, SOL_SOCKET, SO_RCVBUF, 
                 (char *)&recvBuf, &len);
  if (ret==SOCKET_ERROR) 
     ErrF0(WinSocket, Sopt, WSAGetLastError());
  clog << "recv buffer length = " << recvBuf << endl;

  // Fill out the local socket's address information.
  local_sin.sin_family = AF_INET;
  local_sin.sin_port = htons (PORT);  
  local_sin.sin_addr.s_addr = htonl (INADDR_ANY);

  // Associate the local address with WinSocket.
  if (bind (WinSocket, (struct sockaddr *) &local_sin,
              sizeof (local_sin)) == SOCKET_ERROR) 
     ErrF0(WinSocket, Sbind, WSAGetLastError());

  // Establish a socket to listen for incoming connections.
  if (listen (WinSocket, BACKLOG) == SOCKET_ERROR) 
     ErrF0(WinSocket, Slisten, WSAGetLastError());  
}

/* set winsock options */
void options(SOCKET LocalSock, unsigned long IdThread) 
{
  int optval, ret;    

  optval=0;  // consider time out on close
  ret=setsockopt(LocalSock, SOL_SOCKET, SO_DONTLINGER, 
                 (char *)&optval, 4);
  if (ret==SOCKET_ERROR) 
     ErrF2(LocalSock, IdThread, Sdont, WSAGetLastError());

  optval=CLOSE_TIMEOUT;  // set the timeout value on close socket
  ret=setsockopt(LocalSock, SOL_SOCKET, SO_LINGER,
                 (char *)&optval, 4);
  if (ret==SOCKET_ERROR) 
     ErrF2(LocalSock, IdThread, Slinger, WSAGetLastError());
  clog << "closesocket time out = " << CLOSE_TIMEOUT << endl;

  optval=RCV_TIMEOUT;   // time out on recv
  ret=setsockopt(LocalSock, SOL_SOCKET, SO_RCVTIMEO, 
                (char *)&optval, 4);
  if (ret==SOCKET_ERROR) 
     ErrF2(LocalSock, IdThread, SrecvT, WSAGetLastError());
  clog << "recv time out = " << RCV_TIMEOUT << endl;

  optval=SND_TIMEOUT;   // time out on send
  ret=setsockopt(LocalSock, SOL_SOCKET, SO_SNDTIMEO, 
                (char *)&optval, 4);
  if (ret==SOCKET_ERROR) 
     ErrF2(LocalSock, IdThread, SsendT, WSAGetLastError());
  clog << "send time out = " << SND_TIMEOUT << endl;
}


/* Error procedures */
void ErrF0(SOCKET LocalSocket, char *strError, int error)
{
  ErrF1(strError, error);
  closesocket(LocalSocket);
  WSACleanup();
  exit(1);
}

void ErrF1 (char * strError, int error)
{
  char szError[40];

  if (error!=0)   wsprintf(szError, "%s %d ", strError, error);
  else wsprintf(szError, "%s ", strError);
  MessageBox (NULL, szError, TEXT("Error"), MB_OK);
}

void ErrF2 (SOCKET LocalSock, unsigned long IdThread, 
            char * strError, int error)
{
  cerr << IdThread << "  " << strError << error << endl;
  closesocket(LocalSock);
  _endthread();
}

/* send exactly nbytes bytes or close the connection */
int sendS (SOCKET LocalSock, unsigned long IdThread, 
            char *ptr, int nbytes)
 {
  int   nleft, nwritten;

  nleft = nbytes;
  while (nleft > 0)
   {
    nwritten = send (LocalSock, ptr, nleft, 0);
    if (nwritten == SOCKET_ERROR)
        {
      cerr << IdThread << "  " << Ssend << WSAGetLastError() << endl;
          return -1;
        }
    nleft -= nwritten;
    ptr += nwritten;
   }
  return 0;
}

/* recv a string (ptr_in) sized len or close connection. */
int recvS (SOCKET LocalSock, unsigned long IdThread, 
            char *ptr_in, int len)
{
  int   nleft, nrecv;
  char * ptr;

  ptr=ptr_in;
  nleft=len;
  while (nleft > 0)
   {
    nrecv = recv (LocalSock, ptr, nleft, 0);
    if (nrecv == 0)  // connection closed by the client
        {
      cerr << IdThread << "  " << Snocon << endl;
      return -1;
        }

    if (nrecv == SOCKET_ERROR)
        {
      cerr << IdThread << "  " << Srecv << WSAGetLastError() << endl;
      return -1;
        }

    nleft -= nrecv;
    ptr += nrecv;
   }
  ptr_in[len]='\0';
  return 0;
}

void intr_ctrC(int sig)   // cattura ^C e termina su richiesta
{
        char ch[2];

        signal(SIGINT, intr_ctrC);  // catch ^C
        printf("Do you really want to abort? (y) > ");
        
    scanf("%s", ch);
    if ('y'==ch[0]) exit(1);
        return;
}

/* This function allocates len characters */
char *  Alloc(SOCKET LocalSock, unsigned long IdThread, int len)
 {
   char * String;

   String = (char *) malloc(len);
   if( String == NULL )
      {
       cerr << IdThread << "  " << _T("Insufficient memory available  ") << endl; 
       closesocket(LocalSock);
       WSACleanup ();
       exit(1);
      }
    return String;
}

File header

// sos.h

//////////////////////////////////////////////////////////

#include "string.h"
#include "winsock2.h"
#include "process.h"
#include <sys/types.h>
#include <sys/timeb.h>


const   int PORT=3123;            // server port number
const   int BACKLOG=4;            // max. connection in listen state

// socket options
const   int SND_TIMEOUT=100000;     
const   int RCV_TIMEOUT=100000;
const   int CLOSE_TIMEOUT=40000;        // SO_LINGER option

#define SMFC    "Fatal Error: MFC initialization failed. "
#define SWSA    "WSAStartup failed.  Error: "
#define Salloc  "Allocating socket failed.  Error: "
#define Sbind   "Binding socket failed.  Error: "
#define Slisten "Listening to the client failed.  Error: "
#define Sacc    "Accepting connection with client failed. Error "
#define Sopt    "Socket option Error: "
#define Sdont   "SO_DONTLINGER option not accepted. Error: "
#define Slinger "SO_LINGER option not accepted. Error: "
#define SrecvT  "SO_RCVTIMEO option not accepted. Error: "
#define SsendT  "SO_SNDTIMEO option not accepted. Error: "
#define Sthread "Thread cannot be started. Error: "      // threadErr
#define Ssend   "Sending data to client failed. Error: "
#define Srecv   "Recv failed. Error: "
#define Snocon  "Connection closed by the client "
#define Ssize   "Error: Wrong recv string size  "

//int recvI (SOCKET LocalSock, unsigned long IdThread, int *val);
int recvS (SOCKET LocalSock, unsigned long IdThread, 
            char *ptr_in, int len);
//int sendI (SOCKET LocalSock, unsigned long IdThread, int *val);
int sendS (SOCKET LocalSock, unsigned long IdThread, 
            char *ptr, int nbytes);
void startConn();                // start TCP/IP connection
void options(SOCKET LocalSock, unsigned long IdThread);       
                                 // set winsock options

char *  Alloc(SOCKET LocalSock, unsigned long IdThread, int len);
void ErrF0(SOCKET LocalSocket, char *strError, int error);
void ErrF1 (char * strError, int error);
void ErrF2 (SOCKET LocalSock, unsigned long IdThread, 
            char * strError, int error);

void parse (void *dummy);    // thread function

void intr_ctrC(int sig);   // cattura ^C e termina su richiesta

SOCKET WinSocket = INVALID_SOCKET, // Window socket
       tmpSock = INVALID_SOCKET;   // Socket for connection 

unsigned long errorThread;         // thread identifier

int flag;                    // synchronization with thread
int  sendBuf;                // send buffer length
int  recvBuf;                // recv buffer lenght