## modules/sk/socket.c

/* [<][>][^][v][top][bottom][index][help] */

## FUNCTIONS

This source file includes following functions.

1. log_print
2. SK_atoport
3. func_atexit
4. func_sighup
5. func_sigint
6. SK_close
7. SK_getsock
8. SK_accept_connection
9. sock_read
10. sock_write
11. SK_gets
12. SK_puts
13. SK_getpeername

    /***************************************
      $Revision: 1.5 $

      Example code: A socket module.

      Status: NOT REVUED, NOT TESTED

      +html+ <DL COMPACT>
      +html+ <DT>Online References:
      +html+ <DD><UL>
      +html+   <LI>Adapted from <A HREF="http://www.ibrado.com/sock-faq/sfaq.html#faq65">sample source code</A>.
      +html+ </UL>
      +html+ </DL>
      +html+ <PRE>
      +html+ </PRE>

      ******************/ /******************
      Modification History:
            ottrey (08/03/1999) Created from sockhelp.c.
            ottrey (08/03/1998) Heavily butchered.
            joao   (22/06/1999) Modified socket creation and accepts.
      ******************/ /******************
     REMINDER: PUT THE PROPER COPYRIGHT NOTICE HERE
      ***************************************/
    #include "socket.h"
    #include "constants.h"

    extern int h_errno;

    /*+ String sizes +*/
    #define STR_S   63
    #define STR_M   255
    #define STR_L   1023
    #define STR_XL  4095
    #define STR_XXL 16383

    static void log_print(const char *arg) {
    /* [<][>][^][v][top][bottom][index][help] */
      FILE *logf;
      char *str;

      if (CO_get_socket_logging() == 1) {
        if (strcmp(CO_get_socket_logfile(), "stdout") == 0) {
          printf(arg);
        }
        else {
          logf = fopen(CO_get_socket_logfile(), "a");
          fprintf(logf, arg);
          fclose(logf);
        }
      }

    } /* log_print() */

    /* SK_atoport() */
    /*++++++++++++++++++++++++++++++++++++++
       Take a service name, and a service type, and return a port number.  If the
       service name is not found, it tries it as a decimal number.  The number
       returned is byte ordered for the network.

      char *service   Service name (or port number).

      char *proto     Protocol (eg "tcp").

      More:
      +html+ <PRE>
      Authors:
            ottrey

      +html+ </PRE><DL COMPACT>
      +html+ <DT>Online References:
      +html+ <DD><UL>
      +html+ </UL></DL>

      ++++++++++++++++++++++++++++++++++++++*/
    int SK_atoport(const char *service, const char *proto) {
    /* [<][>][^][v][top][bottom][index][help] */
      int port;
      long int lport;
      struct servent *serv;
      char *errpos;

      /* First try to read it from /etc/services */
      serv = getservbyname(service, proto);
      if (serv != NULL)
        port = serv->s_port;
      else { /* Not in services, maybe a number? */
        lport = strtol(service,&errpos,0);
        if ( (errpos[0] != 0) || (lport < 1) || (lport > 65535) )
          return -1; /* Invalid port address */
        port = htons(lport);
      }
      return port;
    } /* SK_atoport() */

    /* SK_close_listening_socket() */
    /*++++++++++++++++++++++++++++++++++++++
      XXX Note: Not sure how long this function will last.  Shouldn't _really_ need it.

      More:
      +html+ <PRE>
      Authors:
            ottrey

      +html+ </PRE><DL COMPACT>
      +html+ <DT>Online References:
      +html+ <DD><UL>
      +html+ </UL></DL>

      ++++++++++++++++++++++++++++++++++++++*/
    /*void SK_close_listening_socket() {
      close(listening_socket);
    } */ /* SK_close_listening_socket */

    static void func_atexit(void) {
    /* [<][>][^][v][top][bottom][index][help] */
      printf("SK: func_atexit() called\n");
    }

    static void func_sighup(int n) {
    /* [<][>][^][v][top][bottom][index][help] */
      printf("SK: func_sighup(%d) called\n", n);
    }

    static void func_sigint(int n) {
    /* [<][>][^][v][top][bottom][index][help] */
      printf("SK: func_sigint(%d) called\n", n);
    }

    void SK_close(int socket) {
    /* [<][>][^][v][top][bottom][index][help] */
      char print_buf[STR_M];

      sprintf(print_buf, "Closing socket... %d\n", socket); log_print(print_buf); strcpy(print_buf, "");

      close(socket);
    }

    /* SK_getsock() */
    /*++++++++++++++++++++++++++++++++++++++

       This function creates a socket and binds to it

       int      SK_getsock       The new socket

       int      socket_type      SOCK_STREAM or SOCK_DGRAM (TCP or UDP sockets)

       u_short  port             The port to listen on.  Remember that ports < 1024 are
                                 reserved for the root user.  Must be passed in network byte
                                 order (see "man htons").

       uint32_t bind_address     Address to bind to, in network order.
      More:
      +html+ <PRE>
      Authors:
            ottrey
            joao

      +html+ </PRE><DL COMPACT>
      +html+ <DT>Online References:
      +html+ <DD><UL>
      +html+ </UL></DL>

      ++++++++++++++++++++++++++++++++++++++*/
    int SK_getsock(int socket_type, u_short port, uint32_t bind_address) {
    /* [<][>][^][v][top][bottom][index][help] */
      struct sockaddr_in address;
      int listening_socket;
      int new_process;
      int reuse_addr = 1;

      /* Setup internet address information.
         This is used with the bind() call */
      memset((char *) &address, 0, sizeof(address));
      address.sin_family = AF_INET;
      address.sin_port = port;
      address.sin_addr.s_addr = bind_address;

      /* Map all of the signals and exit routine */
      atexit(func_atexit);
      /* signal.h has a full list of signal names */
      signal(SIGHUP, func_sighup);
      signal(SIGINT, func_sigint);

      listening_socket = socket(AF_INET, socket_type, 0);
      if (listening_socket < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
      }

      setsockopt(listening_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse_addr, sizeof(reuse_addr));

      if (bind(listening_socket, (struct sockaddr *) &address, sizeof(address)) < 0) {
        perror("bind");
        close(listening_socket);
        exit(EXIT_FAILURE);
      }

      if (socket_type == SOCK_STREAM) {
        listen(listening_socket, 5); /* Queue up to five connections before
                                      having them automatically rejected. */
      }

      return listening_socket;
    } /* SK_getsock() */

    /*++++++++++++++++++++++++++++++++++++++

       Wait for an incoming connection on the specified socket

       int  SK_accept_connection The socket for communicating to the client

       int  listening_socket     The socket that the server is bound to

      More:
      +html+ <PRE>
      Authors:
            joao
      +html+ </PRE>
      ++++++++++++++++++++++++++++++++++++++*/
    int SK_accept_connection(int listening_socket) {
    /* [<][>][^][v][top][bottom][index][help] */
      int connected_socket = -1;
      char print_buf[STR_L];

      while(connected_socket < 0) {
        sprintf(print_buf, "Going to accept connections on socket : %d\n",listening_socket); log_print(print_buf); strcpy(print_buf, "");
    /* XXX joao - ? - why is this here?
    fflush(NULL);
    */

        connected_socket = accept(listening_socket, NULL, NULL);
        if (connected_socket < 0) {
          /* Either a real error occured, or blocking was interrupted for
             some reason.  Only abort execution if a real error occured. */
          if (errno != EINTR) {
            perror("accept");
            close(listening_socket);
            exit(EXIT_FAILURE);
          } else {
            continue;    /* don't return - do the accept again */
          }
        }
      }
      sprintf(print_buf, "client connected.\n"); log_print(print_buf); strcpy(print_buf, "");

      return connected_socket;
    }

    /* sock_read() */
    /*++++++++++++++++++++++++++++++++++++++

       This is just like the read() system call, except that it will make
       sure that all your data goes through the socket.

       int    sock_read  The number of bytes read.

       int    sockfd    The socket file descriptor.

       char   *buf      The buffer to be read from the socket.

       size_t count     The number of bytes in the buffer.

      More:
      +html+ <PRE>
      Authors:
            ottrey
      +html+ </PRE>
      ++++++++++++++++++++++++++++++++++++++*/
    static int sock_read(int sockfd, char *buf, size_t count) {
    /* [<][>][^][v][top][bottom][index][help] */
      size_t bytes_read = 0;
      int this_read;

      while (bytes_read < count) {
        do
          this_read = read(sockfd, buf, count - bytes_read);
        while ( (this_read < 0) && (errno == EINTR) );
        if (this_read < 0)
          return this_read;
        else if (this_read == 0)
          return bytes_read;
        bytes_read += this_read;
        buf += this_read;
      }

      return count;

    } /* sock_read() */

    /* sock_write() */
    /*++++++++++++++++++++++++++++++++++++++

       This is just like the write() system call, accept that it will
       make sure that all data is transmitted.

       int    sockfd  The socket file descriptor.

       char   *buf    The buffer to be written to the socket.

       size_t count   The number of bytes in the buffer.

      More:
      +html+ <PRE>
      Authors:
            ottrey

      +html+ </PRE><DL COMPACT>
      +html+ <DT>Online References:
      +html+ <DD><UL>
      +html+ </UL></DL>

      ++++++++++++++++++++++++++++++++++++++*/
    static int sock_write(int sockfd, const char *buf, size_t count) {
    /* [<][>][^][v][top][bottom][index][help] */
      size_t  bytes_sent = 0;
      int     this_write;

    /*
      printf("sock_write = { sockfd=[%d], buf=[%s], count=[%d]\n", sockfd, buf, count);
    */
      while (bytes_sent < count) {
        do
          this_write = write(sockfd, buf, count - bytes_sent);
        while ( (this_write < 0) && (errno == EINTR) );
        if (this_write <= 0)
          return this_write;
        bytes_sent += this_write;
        buf += this_write;
      }
      return count;
    } /* sock_write() */

    /* SK_gets() */
    /*++++++++++++++++++++++++++++++++++++++

       This function reads from a socket, until it recieves a linefeed
       character.  It fills the buffer "str" up to the maximum size "count".

       int SK_gets  The total_count of bytes read.

       int    sockfd    The socket file descriptor.

       char   *str      The buffer to be written from the socket.

       size_t count     The number of bytes in the buffer.

      More:
      +html+ <PRE>
      Authors:
            ottrey

      Side Effects:
            This function will return -1 if the socket is closed during the read operation.

            Note that if a single line exceeds the length of count, the extra data
            will be read and discarded!  You have been warned.

      To Do:
            Capture the control-c properly!

      +html+ </PRE>

      ++++++++++++++++++++++++++++++++++++++*/
    int SK_gets(int sockfd, char *str, size_t count) {
    /* [<][>][^][v][top][bottom][index][help] */
      int bytes_read;
      int total_count = 0;
      char *current_position;
      char last_read = 0;

      int control_c = 0;

      current_position = str;
      while (last_read != 10) {
        bytes_read = read(sockfd, &last_read, 1);
        if (bytes_read <= 0) {
          /* The other side may have closed unexpectedly */
          return -1; /* Is this effective on other platforms than linux? */
        }
        if ( (total_count < count) && (last_read != 10) && (last_read !=13) ) {
          *current_position = last_read;
          current_position++;
          total_count++;
        }

        if (last_read == -1) {
          bytes_read = read(sockfd, &last_read, 1);
          if (last_read == -12) {
            printf("Client pressed Control-c.\n");
            control_c = 1;
            printf("returning a -2\n");
            return -2;
          }
        }
      }
      if (count > 0) {
        *current_position = 0;
      }

      return total_count;

    } /* SK_gets() */

    /* SK_puts() */
    /*++++++++++++++++++++++++++++++++++++++

       This function writes a character string out to a socket.

       int SK_puts  The total_count of bytes read.

       int    sockfd    The socket file descriptor.

       char   *str      The buffer to be written from the socket.

      More:
      +html+ <PRE>
      Authors:
            ottrey

      Side Effects:
            This function will return -1 if the socket is closed during the write operation.

            Note that if a single line exceeds the length of count, the extra data
            will be read and discarded!  You have been warned.

      +html+ </PRE>

      ++++++++++++++++++++++++++++++++++++++*/
    int SK_puts(int sockfd, const char *str) {
    /* [<][>][^][v][top][bottom][index][help] */

      return sock_write(sockfd, str, strlen(str));

    } /* SK_puts() */

    /* SK_getpeername() */
    /*++++++++++++++++++++++++++++++++++++++

       This function will tell you who is at the other end of a connected stream socket.
       XXX It's not working.

       int    sockfd    The socket file descriptor.

      More:
      +html+ <PRE>
      Authors:
            ottrey
      +html+ </PRE>

      ++++++++++++++++++++++++++++++++++++++*/
    char *SK_getpeername(int sockfd) {
    /* [<][>][^][v][top][bottom][index][help] */
      char *hostaddress=NULL;
      struct sockaddr_in *addr_in;
      int *namelen;

      addr_in = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
      namelen = (int *)malloc(sizeof(int));
      if (getpeername(sockfd, (struct sockaddr *)addr_in, namelen) != -1) {
        hostaddress = malloc((*namelen)+1);
        strcpy(hostaddress, inet_ntoa(addr_in->sin_addr));
      }
      free(namelen);
      free(addr_in);

      return hostaddress;

    } /* SK_getpeername() */

/* [<][>][^][v][top][bottom][index][help] */