/****************************************************************************
 * TCPserver.c                                                              *
 *   J.Rogers, Jan/2003                                                     *
 *                                                                          *
 *  Pedagogical TCP server example                                          *
 *  Usage:                                                                  *
 *   TCPserver [PORT]                                                       *
 *                                                                          *
 *  Listens at PORT (default 50000)                                         *
 *  Accepts connections                                                     *
 *  Logs connection to stdout                                               *
 *  Echos any data received over connection except "q\r\n" which closes     *
 *    data connection and "x\r\n" which kills server                        *
 *  Logs each message to stdout as it is received                           *
 ****************************************************************************/
#include <stdio.h>       // for printf() and perror()
#include <sys/types.h>   // for system dependent types
#include <sys/socket.h>  // for socket stuff
#include <netinet/in.h>  // for INET protocols and addresses
#include <arpa/inet.h>   // for inet_ntop()
#include <netdb.h>       // for getprotobyname()
#include <errno.h>       // for errno
#include <stdlib.h>      // for strtol()
#include <unistd.h>      // for close()

#define TRUE ( 0 == 0 )
#define FALSE !TRUE

const int DEFAULT_PORT = 50000; // Default server port
const int MAX_QUEUE = 16;       // Maximum number of pending connections
const int BUFLEN = 1024;        // Data buffer length
const int NAMELEN = 16;         // Max length of dotted octet string 

// Uniform error handler
void errorExit( char* errmsg );

// Extract port from command line arguments
int parsePort( int argc, char** argv);


/****************************************************************************
 *  main                                                                    *
 ****************************************************************************/
int main( int argc, char** argv )
{

  int port = parsePort( argc, argv ); // Server port number
  int ssock;                          // Server socket descriptor
  struct sockaddr_in saddr;           // Server socket address (See ip(7))

  int dsock;                          // Data socket descriptor
  struct sockaddr_in daddr;           // Data socket address (See ip(7))
  socklen_t daddr_length = sizeof( daddr );
  char ddots[NAMELEN];                // 
  const int OptionOn = TRUE;          // setsockopt option

  int quit = FALSE;                   // Close connection flag  ("q")
  int die  = FALSE;                   // Exit flag ("x")

  char buffer[ BUFLEN ];              // I/O buffer
  int msglen;                         // length of received message

  // Create socket
  //    getprotobyname() returns a protoent struct
  //      the actual protocol number is in its p_proto field
  ssock = socket( PF_INET,           // IPv4 internet protocols
                  SOCK_STREAM,       // connection oriented reliable transport
                  getprotobyname("tcp")->p_proto);  // Protocol number
  if ( ssock < 0 )
    errorExit( "Could not create server socket" );

  // set SO_REUSEADDR socket option
  //  allows rebinding port without waiting for previous connection to time out
  if ( setsockopt( ssock,                    // Socket
                   SOL_SOCKET,               // Option is Socket level option
                   SO_REUSEADDR,             // Option name
                   &OptionOn, sizeof( OptionOn ) )  // Option value
       < 0 )
    errorExit( "Problems setting REUSEADDR" );

  // Bind socket

  //  Build socket address
  //   sockaddr_in is the structure of internet domain socket addresses:
  //    (See ip(7).  Machine dependent types as given here are typical.)
  //      struct sockaddr_in
  //      {
  //        short    sin_family;         --- should be AF_INET
  //        u_short  sin_port;	         --- Port number
  //        struct   in_addr sin_addr;   --- Internet address.
  //        unsigned char sin_zero[...]  --- pad (unused)        
  //      };
  //   where in_addr is the structure of an internet domain address:
  //    (See ip(7).)
  //    struct in_addr
  //      {
  //        u_long s_addr;
  //      };
  saddr.sin_family = AF_INET;                // IPv4 Address Family
  saddr.sin_addr.s_addr = htonl(INADDR_ANY); // bind to all local interfaces
  saddr.sin_port = htons( port );            // must be in network byte order

  //  Bind socket address
  if ( bind( ssock,                        // Socket
             ( struct sockaddr * ) &saddr, // address
             sizeof( saddr ) )
       < 0 )
    errorExit( "Could not bind server port" );


  // Begin listening at server port
  if ( listen( ssock,       // Socket
               MAX_QUEUE)   // Maximum number of pending connections
       < 0 )
    errorExit( "Could not listen at server port" );

  printf("Echoing at port %d\n", ntohs(saddr.sin_port) );

  // Service connections
  // This is single-threaded, it services connections one at a time
  //  although up to MAX_QUEUE connections may be pending
  
  while ( !die )
    {
      // Accept new data socket
      //  The third parameter is both input and output
      //   When called it must point to var holding the addresss struct length
      //   On exit var contains the actual length of the address returned
      dsock = accept( ssock,                        // Socket
                      ( struct sockaddr * ) &daddr, // Gets client address
                      &daddr_length );              // Length of address
      if ( dsock < 0 )
        errorExit( "Failed to accept connection" );

      // Log connection
      //   inet_ntop() converts network addresses to dotted octet strings
      //   Here we are only interested in the return value, which is a
      //    pointer to the string, but we must provide the buffer in any case
      printf("Connection from %s:%d\n", 
             inet_ntop( AF_INET,         // Address Family
                        &daddr.sin_addr, // address structure (in_addr)
                        ddots, NAMELEN   // buffer to hold string
                        ),
             ntohs(daddr.sin_port)     );

      // Echo until "q\r\n" or "x\r\n" is recvd
      quit = FALSE;
      while ( !( quit || die ) )
        {
          // Recieve data from socket
          //   Last argument holds option flags---none here.
          msglen = recv( dsock, buffer, BUFLEN, 0 );
          if ( msglen < 0 )
            errorExit( " Failed to recv from data socket" );
          
          // Log message
          //   Add string terminator
          if ( msglen < BUFLEN )
            buffer[msglen] = '\0';
          else
            buffer[msglen-1] = '\0';

          printf("Message from %s:%d::%s", 
                 inet_ntop( AF_INET,         // Address Family
                            &daddr.sin_addr, // address structure (in_addr)
                            ddots, NAMELEN   // buffer to hold string
                            ),
                 ntohs(daddr.sin_port),     // convert network -> host order
                 buffer);

          // if message is "x\r\n" set exit flag
          die =  ( msglen == 3 && buffer[0] == 'x' );

          // if message is "q\r\n" or "x\r\n" set close flag
          quit =  ( msglen == 3 && buffer[0] == 'q' ) || die;

          if ( !quit )
            {
              // we assume blocking send
              //   if fewer than msglen chars are sent we take it as an error
              if ( send ( dsock,          // socket
                          buffer, msglen, // message, length
                          MSG_NOSIGNAL )  // Flags: no SIG_PIPE on error
                   < msglen )
                errorExit( "Failed to send echo" );
            }
        }
      //  Close data socket
      printf("Closing %s:%d\n", ddots, ntohs(daddr.sin_port) );
      close( dsock );
    }
  printf("Shutting down\n");
  close( ssock );
  exit( EXIT_SUCCESS );
}


/****************************************************************************
 *  void errorExit( char* errmsg )                                          *
 *   Log errmsg and system explanation of most recent error to stderr       *
 *   Exit with EXIT_FAILURE                                                 *
 ****************************************************************************/
void errorExit( char* errmsg )
{
  perror( errmsg );
  exit( EXIT_FAILURE );
}

/****************************************************************************
 *  int parsePort( int argc, char** argv )                                  *
 *    if no command line arguments returns DEFAULT_PORT                     *
 *    if exactly one command line argument parses that as port number       *
 *    otherwise prints usage message and exits with failure                 *
 ****************************************************************************/
int parsePort( int argc, char** argv)
{
  int portno = DEFAULT_PORT;  // Port number
  
  if ( argc == 2 )
    portno = strtol( argv[1], NULL, 10 );
  if ( argc > 2 )
    {
      printf("USAGE: %s [port]\n", argv[0]);
      exit( EXIT_FAILURE );
    }
  
  return portno;
}

