/**
   @file

   Main part of libcomm.
*/

/*
  Copyright (c) Andreas Hofmeier
  (www.an-h.de, www.an-h.de.vu, www.lgut.uni-bremen.de/an-h/)
  
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/poll.h>
#include <string.h>


#include "libcomm.h"
#include "md5.h"

#ifndef nothread
  #include <pthread.h>
#endif


#ifndef nothread
/**
   This is a part of socket_accept() and must not called from the
   user. This function is the thread which is started from
   socket_accept() and runs in background.
*/
void socket_accept_thread(struct LIBCOMMPTHREADP *libcommpthreadp) {
  char *buf;
  unsigned int type;
  unsigned int size;
  /* connector's address information */
  struct sockaddr_in their_addr;
  int sin_size;
  int fd;

  while (1) {
    // wait and accept a incomming connection
    sin_size = sizeof(struct sockaddr_in);
    if ((fd = accept(libcommpthreadp -> sockport,
		     (struct sockaddr *) &their_addr,
		     &sin_size)) != -1) {
      char *pip = inet_ntoa(their_addr.sin_addr);

      libcommpthreadp -> socket_accept_do(fd,
					  libcommpthreadp -> id,
					  pip, their_addr);
    }
  }
  //  pthread_exit(NULL);
}


/**
   Start a new thread, wait for connections and start
   socket_accept_do() when someone connects.

   @param sockport (int) descriptor of a tcp socket/port from
   socket_bind()

   @param id (int) arbitrary id of background process / thread. (May
   be it is a good idea to use the portnumber.)

   @param aocket_accept_do(int fd, int id, char *pip, struct
   sockaddr_in their_addr) (function) this function is called if
   somebody connects.  fd is the descriptor of the new socket to the
   connected tcp-tream. id is the same as in socket_accept(). pip
   contains the ip-address of the connected client. The structure
   their_addr contails all known information about the connected
   client.

   @return If all right zero otherwise non zero.
*/
int socket_accept(int sockport, int id,
	   void (*socket_accept_do)(int fd, int id, char *pip,
				    struct sockaddr_in their_addr)) {

  // allocate memory for thread configuration
  struct LIBCOMMPTHREADP *libcommpthreadp; 
  libcommpthreadp = (struct LIBCOMMPTHREADP *)
    malloc(sizeof(struct LIBCOMMPTHREADP));
  if (libcommpthreadp == NULL) {
    perror("malloc()");
    return -1;
  }

  // store all necessary data in it
  libcommpthreadp -> sockport = sockport;
  libcommpthreadp -> id = id;
  libcommpthreadp -> socket_accept_do = socket_accept_do;

  // starting thread
  pthread_attr_init(&(libcommpthreadp -> thrd_2_attr));
  return pthread_create(&(libcommpthreadp -> thrd_2),
			&(libcommpthreadp -> thrd_2_attr),
			(void *) socket_accept_thread,
			libcommpthreadp);

}

#endif


/** Bind a socket to a port (Server side). This function creates a
    socket and binds it to a local port.

   @param port an integer which specifies the port

   @param cqueue an integer how many pending connections queue will
   hold in the waiting queue.

   @return The File Descriptor (FD) which allows access to the bound
   port.
*/

#include <netinet/in.h>

int socket_bind(int port, int cqueue) {
  // FD of the new socket to the bound port
  int sock;
  // address information
  struct sockaddr_in ad;

  // create a socket
  if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
    // cannot created socket, return error
    perror("socket");
    return -1;
  }

  // make ensure that the memory is initiated
  memset(&ad, 0, sizeof(ad));

  // address family: AF_INET: IPv4 Internet protocols
  ad.sin_family = AF_INET;
  // convert and copy port in structure
  ad.sin_port = htons(port);
  // bind to all interfaces -- the port will accept connections to all
  // addresses of the local machine
  ad.sin_addr.s_addr = INADDR_ANY;
  // bind socket
  if (bind(sock, (struct sockaddr *) &ad, sizeof(struct sockaddr)) == -1) {
    // cannot bind, return error
    perror("bind");
    return -1;
  }
  // listen for connections on bound port
  if (listen(sock, cqueue) == -1) {
    // cannot listen, return error
    perror("listen");
    return -1;
  }
  // all right, port is listening. Return the FD as
  // reference for use.
  return sock;
}


#include <netinet/in.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>



/**
  Connect a TCP-stream to a server (Client side). Creates a socket and
  connect it over a TCP-stream to the specified port on the specified
  server.

  @param host a string (char *) which specifies the name or the
  IP-address of the server.

  @param port an integer which specifies the port on the server.

  @return The File Descriptor (FD) which allows access to the
  TCP-stream-socket or -1 if the connection fails.
*/
int socket_connect(char *host, int port) {
  // FD of the new socket to the TCP-stream
  int sock;
  // The IP address in binary form
  in_addr_t inaddr;
  // address information to connect other side (syscall: connect() )
  struct sockaddr_in ad;
  // contains the result of the resolution of a network-name.
  struct hostent *hp;
  
  // make ensure that the memory is initiated
  memset(&ad, 0, sizeof(ad));
  // address family: AF_INET: IPv4 Internet protocols
  ad.sin_family = AF_INET;
  // Try to convert the given IP-address into binary data...
  inaddr = inet_addr(host);
  if (inaddr != INADDR_NONE) {
    // if the IP address was converted copy it in the parameter
    // structure (ad) for later use
    memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));
  } else {
    // if this is not possible (the name and not the IP address is
    // given), try to resolve the name to a binary IP address
    hp = gethostbyname(host);
    if (hp == NULL) {
      // name cannot resolved, return error
      perror("gethostbyname()");
      return -1;
    }
    // copy address in the parameter structure (ad) for later use
    memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
  }
  // convert and copy port-number in the parameter structure (ad) for
  // later use
  ad.sin_port = htons(port);
  // create a socket
  sock = socket(AF_INET, SOCK_STREAM, 0);
  if (sock < 0) {
    // cannot created socket, return error
    perror("socket()");
    return -1;
  }
  // connect the socket over an TCP-stream to the port and the server,
  // which are stored in ad.
  if (connect(sock, (struct sockaddr *) &ad, sizeof(ad)) < 0) {
    // connection is not possible, return error
    perror("connect()");
    return -1;
  }
  // all right, socket is connected an can be used. Return the FD as
  // reference for use.
  return sock;
}


/**
   Get random numbers/bytes. This function reads random numbers/bytes
   from /dev/urandom and stores this bytes in a buffer.

   @param buf (char *) in which the bytes will be stored. If this
   parameter is equal to NULL dynamic memory will be allocated.

   @param size an integer, specifies ths size of the buffer (the
   number of the random bytes). WARNING: If buf is not equal to null,
   n*(size) bytes will be stored in this buffer without any check of
   ths size of this buf.

   @return (char *) a pointer to the buffer in which the random bytes
   are stored.
*/
char *block_random(char *buf, int size) {
  FILE *f;

  // If no momory allocated, allocate memory
  if (buf == NULL) {
    if ((buf = malloc(size)) == NULL) {
      perror("malloc");
      return NULL;
    }
  }

  // Read Random numbers from /dev/urandom and stroe this these in the
  // buffer

  if ((f = fopen("/dev/urandom", "ro")) == NULL) {
    perror("fopen(/dev/urandom)");
    return NULL;
  }

  fread(buf, 1, size, f);

  fclose(f);

  return buf;
}






#ifndef nothread
/**
   This is a part of block_call() and must not called from the
   user. This function is the thread which is started from
   block_call() and runs in background.

   @param libcommpthreads (struct LIBCOMMPTHREADS *) holds pointers to
   the functions to be call, fd (socket discriptor) and id.
*/
void thread1(struct LIBCOMMPTHREADS *libcommpthreads) {
  char *buf;
  unsigned int type;
  unsigned int size;

  while (1) {
    // try to receive a datablock...
    buf = block_receive(libcommpthreads -> fd, &type, NULL, &size, 0, 
			libcommpthreads -> term);
    // failed: call block_call_term() and terminate thread
    if (buf == NULL) {
      libcommpthreads -> block_call_term(libcommpthreads -> fd,
					 libcommpthreads -> id);
      break;
    }
    // datablock OK: call block_call_do(), after this wait for the
    // next datablock
    libcommpthreads -> block_call_do(libcommpthreads -> fd,
				     libcommpthreads -> id,
				     type, buf, size,
				     libcommpthreads -> term);
  }
  pthread_exit(NULL);
}


/**
   Waits in a new thread for a datablock to be received and calls the
   function block_call_do() if this event occurs or block_call_term()
   when the connection terminates.

   @param fd (int) descriptor of socket

   @param id (int) arbitrary id of background process / thread

   @param term (int) 0: do not terminate the buffer, 1: terminate the
   buffer by appending a 0x00.

   @param block_call_do(int fd, int id, unsigned int type, char *buf,
   unsigned int size, int term) (function) this function is called if
   a datablock was received. fd, id and term are the same as in
   block_call(). type describes the type of the received datablock,
   buf is a pointer to this datablock and size is the number of bytes
   of the datablock

   @param block_call_term(int fd, int id) (function) this function is
   called if the connection terminates. fd and id are the same as in
   block_call().

   @return If all right zero otherwise non zero.
*/
int block_call(int fd, int id, int term,
	   void (*block_call_do)(int fd, int id, unsigned int type,
				 char *buf, unsigned int size,
				 int term),
	   void (*block_call_term)(int fd, int id)) {

  // allocate memory for thread configuration
  struct LIBCOMMPTHREADS *libcommpthreads; 
  libcommpthreads = (struct LIBCOMMPTHREADS *)
    malloc(sizeof(struct LIBCOMMPTHREADS));
  if (libcommpthreads == NULL) {
    perror("malloc()");
    return -1;
  }

  // store all necessary data in it
  libcommpthreads -> fd = fd;
  libcommpthreads -> id = id;
  libcommpthreads -> block_call_do = block_call_do;
  libcommpthreads -> block_call_term = block_call_term;

  // starting thread
  pthread_attr_init(&(libcommpthreads -> thrd_1_attr));
  return pthread_create(&(libcommpthreads -> thrd_1),
			&(libcommpthreads -> thrd_1_attr),
			(void *) thread1, libcommpthreads);
}

#endif



/**
   This function tests if new data is available to read on a stream.

   @param fd (int) discriptor of stream to test

   @return (int) 1: Data to read; 0: No data to read
*/
int block_ifdata(int fd) {
  struct pollfd polld;

  polld.fd = fd;
  polld.events = POLLIN | POLLPRI;

  if (poll(&polld, 1, 0)) {
    return 1;
  }
  return 0;
}


/**
   Test if is there data available on the socket's input buffer and
   starts receiving a block if there is. WARNING: The integers (type
   and size; excluding fd) are only 16 bit values (0 - 65535).

   @param fd (int) descriptor of socket

   @param type (unsigned int *) pointer to integer, this value can be
   used as buyer's option

   @param buf (char *) buffer for datablock. Memory will be allocated
   if this parameter is equal to null.

   @param size (unsigned int *) pointer to integer in which the size
   of the received datablock is saved.

   @param maxsize (unsigned int *) describes size of buf. This
   parameter will be ignored if buf is equal to null.

   @param term (int) 0: do not terminate the buffer, 1: terminate the
   buffer by appending a 0x00.

   @return (char *) pointer to buffer which contains the received
   datablock; NULL if fail; 1 if no data available.

*/
char *block_receive_poll(int fd, unsigned int *type, char *buf,
			 unsigned int *size, unsigned int maxsize,
			 int term) {
  // new data available
  if (block_ifdata(fd)) {
    return block_receive(fd, type, buf, size, maxsize, term);
  } else {
  // no new data available
    return (char *) 1L;
  }
}



/**
   Receive a block (composition of: type, size of datablock and
   datablock) from a socket. Waits for a block to be received
   completely. WARNING: The integers (type and size; excluding fd) are
   only 16 bit values (0 - 65535).

   @param fd (int) descriptor of socket   

   @param type (unsigned int *) pointer to integer, this value can be
   used as buyer's option

   @param buf (char *) buffer for datablock. Memory will be allocated
   if this parameter is equal to null.

   @param size (unsigned int *) pointer to integer in which the size
   of the received datablock is saved.

   @param maxsize (unsigned int *) describes size of buf. This
   parameter will be ignored if buf is equal to null.

   @param term (int) 0: do not terminate the buffer, 1: terminate the
   buffer by appending a 0x00.

   @return (char *) pointer to buffer which contains the received
   datablock; NULL if fail.

*/
char *block_receive(int fd, unsigned int *type, char *buf,
		    unsigned int *size, unsigned int maxsize, 
		    int term) {
  // do not trust any user!
  if (term > 1) {
    term = 1;
  }
  if (term < 0) {
    term = 0;
  }

  // receiving type
  if (block_receive_integer(fd, type) < 0) {
    return NULL;
  }
  // receiving size
  if (block_receive_integer(fd, size) < 0) {
    return NULL;
  }

  if (buf == NULL) {
    if ((buf = (char *) malloc(*size + term)) == NULL) {
      perror("malloc()");
      return NULL;
    }
  } else {
    if ((*size + term) > maxsize) {
      fprintf(stderr, "Try to receive more than fit in the buffer\n");
      return NULL;
    }
  }
  // receiving data
  if (block_receive_nbytes(fd, buf, *size) < 0) {
    return NULL;
  }

  if (term) {
    buf[*size] = 0;
  }

  return buf;
}




/**
   Receive an integer (two bytes; 16Bit) from the socket. 

   @param fd (int) descriptor of socket

   @param recvi (unsigned int *) pointer to integer in which the
   received integer is saved.

   @return (int) 2: OK; -1: fail
*/
int block_receive_integer(int fd, unsigned int *recvi) {
  int i, r;
  //  unsigned int recvi;
  int sizeofint = 2; /* sizeof(int); */

  // reset value
  *recvi = 0;

  // receive value
  if (recv(fd, ((char *) recvi), sizeofint, MSG_WAITALL) != sizeofint) {
    perror("recv()");
    return -1;
  }

  return 2; //recvi;
}




/**
   Receive n bytes from socket.

   @param fd (integer) descriptor of socket

   @param buf (char *) buffer for saving the received bytes

   @param n (integer) number of bytes to receive

   @return (integer) n: OK; -1 fial
*/
int block_receive_nbytes(int fd, char *buf, int n) {
  int i, r;
  unsigned int recvi;
  int sizeofint = 2; /* sizeof(int); */

  // receive n bytes to buffer
  if (recv(fd, buf, n, MSG_WAITALL) != n) {
    perror("recv()");
    return -1;
  }

  return n;
}



/**
   Send a block (composition of: type, size of datablock and datablock
   (buf)) to a socket. The function blocks until the whole block is
   transfered to the buffer. If the buffer is full, data has to be
   sent first. WARNING: The integers (type and size; excluding fd) are
   only 16 bit values (0 - 65535).

   @param fd (int) descriptor of the socket to which buf should send

   @param type (unsigned int) This value can be used as buyer's option

   @param buf (char *) which should be send

   @return number of sent bytes, -1 if an error is occurt.
*/

int block_send(int fd, unsigned int type, char *buf,
	       unsigned int size) {
  // add up the number of sent byte, for checking.
  int i, r;

  // send the type of the data
  i = r = 0;
  while (r < 2) {
    if ((i = send(fd, (void *) &type + r, 2 - r, 0)) < 0) {
      return -1;
    }
    r += i;
  }
  // send the size of the buffer
  i = r = 0;
  while (r < 2) {
    if ((i = send(fd, (void *) &size + r, 2 - r, 0)) < 0) {
      return -1;
    }
    r += i;
  }
  // send the data in the buffer it self
  i = r = 0;
  while (r < size) {
    if ((i = send(fd, (void *) buf + r, size - r, 0)) < 0) {
      return -1;
    }
    r += i;
  }

  /*
  // send the type of the data
  if ((r = send(fd, (void *) &type, 2, 0)) < 0) {
    return -1;
  }
  // send the size of the buffer
  if ((i = send(fd, (void *) &size, 2, 0)) < 0) {
    perror("send0()");
    return -1;
  }
  r += i;
  // send the data in the buffer it self
  if ((i = send(fd, buf, size, 0)) < 0) {
    perror("send1()");
    return -1;
  }
  r += i;

  // not the comlete messages was sent.
  if (r = (size + 4)) {
    perror("send2()");
    return -1;
  }
  */

  return r;
}






/**
   Free the memory space which is used by an AUTHINFO structure.

   @param (struct AUTHINFO *) pointer to structure to destroy.
*/
void free_authinfo(struct AUTHINFO *destroy) {
  free(destroy -> netname);
  free(destroy -> name);
  free(destroy -> passwd);
  free(destroy -> keyencrypt);
  free(destroy -> keydecrypt);
  free(destroy);
}



/**
   Do both side authentification. This function is usually called
   just after a socket stream is established. The function must be
   called on both sides.

   WARNING: This authentication can be bypassed simply by using the
   multiple session attack if multiple session are allowd and the same
   password is used for both sides.

   Both sides following these steps:

   1. get auth info ([login] name, passwd) by using getauthinfo() from
   name or netname for remote login

   2. generate random numbers

   3. exchange (first send, then receive) login names

   4. exchange random numbers

   5. calculate md5 checksum over the random numbers (received from other
   side) and the remote passwd.

   6. exchange md5 checksums

   7. get auth info from name (received from other side) for local login

   8. calculate md5 checksum over the local random numbers and the
   local passwd.

   9. check login -- compare the received md5sum (6.) with the
   generated one (8.); send acknowledgement
   
   10. receive remote acknowledgement

   11. return suitable values

   @param fd (int) describes the socket on which the authentication
   has to be done

   @param netname (char *) use netname to resolve [login] name and
   passwd of the remote machine (NULL: not specified)

   @param netname (char *) use [login] name to resolve passwd of the
   remote machine (NULL: not specified; both NULL use first entry in
   file, see getauthinfo())

   @param plocallogin (struct AUTHINFO **) (pointer to pointer to an
   AUTHINFO struct) in this (double pointed) struct the local authinfo
   will be loaded, if the parameter is not null.

   @param premotelogin (struct AUTHINFO **) in this (double pointed)
   struct the remote authinfo will be loaded, if the parameter is not
   null.

   @return (int) 0: Authentication/Login OK; -1: remote login error;
   -2: login error on both sides; -3: local login error; -4: other
   (network) error; -5: cannot load remote auth info; -6: cannot load
   local auth info; 
*/
int socket_md5auth(int fd, char *netname, char *name,
		   struct AUTHINFO **plocallogin,
		   struct AUTHINFO **premotelogin) {
  char rstr0[authrandomstringsize], rstr1[authrandomstringsize];
  char rstr0sum[35], rstr1sum[35];

  int otype;
  char *oname;
  int onamesize;
  char *randblock;
  char *orandblock;
  int orandblocksize;

  struct MD5Context context;
  unsigned char md5_prs[16];
  unsigned char omd5_prs[16];

  int login_ok = 0;

  struct AUTHINFO *locallogin;
  // 1.
  struct AUTHINFO *remotelogin = getauthinfo(netname, name);
  if (remotelogin == NULL) {
    return -5;
  }
  if (premotelogin != NULL) {
    *premotelogin = remotelogin;
  }
  
  // 2. generate random block for local login
  if ((randblock = block_random(NULL, authrandomstringsize))
      == NULL) {
    if (premotelogin == NULL) {
      free_authinfo(remotelogin);
    }
    return -4;
  }

  // 3. exchange login name
  if (block_send(fd, authmessagetype, remotelogin -> name,
		 strlen(remotelogin -> name)) <= 0) {
    if (premotelogin == NULL) {
      free_authinfo(remotelogin);
    }
    free(randblock);
    return -4;
  }
  oname = block_receive(fd, &otype, NULL, &onamesize, 0, true);
  if ((oname == NULL) ||
      (otype != authmessagetype)) {
    if (premotelogin == NULL) {
      free_authinfo(remotelogin);
    }
    free(randblock);
    return -4;
  }


  // 4. exchange random block
  if (block_send(fd, authmessagetype, randblock,
		 authrandomstringsize)
      <= 0) {
    if (premotelogin == NULL) {
      free_authinfo(remotelogin);
    }
    free(randblock);
    free(oname);
    return -4;
  }
  orandblock = block_receive(fd, &otype, NULL, 
			     &orandblocksize, 0, false);
  if ((orandblock == NULL) ||
      (otype != authmessagetype)) {
    if (premotelogin == NULL) {
      free_authinfo(remotelogin);
    }
    free(randblock);
    free(oname);
    free(orandblock);
    return -4;
  }

  // 5. calculate md5 checksum over the random numbers (received from
  // other side) and the remote passwd.
  MD5Init(&context);
  MD5Update(&context, orandblock, orandblocksize);
  MD5Update(&context, remotelogin -> passwd,
	    strlen(remotelogin -> passwd));
  MD5Final(md5_prs, &context);

  // 6. exchange md5 checkumms
  if (block_send(fd, authmessagetype, md5_prs, 16) <= 0) {
    if (premotelogin == NULL) {
      free_authinfo(remotelogin);
    }
    free(randblock);
    free(oname);
    free(orandblock);
    return -4;
  }
  if ((block_receive(fd, &otype, omd5_prs, &orandblocksize,
		     16, false)
      == NULL) ||
    (otype != authmessagetype) ||
    (orandblocksize != 16)) {
    if (premotelogin == NULL) {
      free_authinfo(remotelogin);
    }
    free(randblock);
    free(oname);
    free(orandblock);
    return -4;
  }
  usleep(1);

  // 7. get auth info from name (received from other side) for local login
  locallogin = getauthinfo(NULL, oname);
  if (locallogin == NULL) {
    if (premotelogin == NULL) {
      free_authinfo(remotelogin);
    }
    free(randblock);
    free(oname);
    free(orandblock);
    return -6;
  }
  if (plocallogin != NULL) {
    *plocallogin = locallogin;
  }

  // 8. calculate md5 checksum over the local random numbers and the
  // local passwd.
  MD5Init(&context);
  MD5Update(&context, randblock, authrandomstringsize);
  MD5Update(&context, locallogin -> passwd,
	    strlen(locallogin -> passwd));
  MD5Final(md5_prs, &context);
  
  // 9. check login -- compare the received md5sum (6.) with the
  // generated one (8.); send acknowledgement
  if (memcmp(md5_prs, omd5_prs, 16) == 0) {
    login_ok = 1;
    if (block_send(fd, authmessagetype, "OK", 2) <= 0) {
      if (plocallogin == NULL) {
	free_authinfo(locallogin);
      }
      if (premotelogin == NULL) {
	free_authinfo(remotelogin);
      }
      free(randblock);
      free(oname);
      free(orandblock);
      return -4;
    }
  } else {
    if (block_send(fd, authmessagetype, "FAIL", 4) <= 0) {
      if (plocallogin == NULL) {
	free_authinfo(locallogin);
      }
      if (premotelogin == NULL) {
	free_authinfo(remotelogin);
      }
      free(randblock);
      free(oname);
      free(orandblock);
      return -4;
    }
  }

  // 10. receive remote acknowledgement
  free(orandblock);
  orandblock = block_receive(fd, &otype, NULL, &orandblocksize,
			     0, true);
  if ((orandblock == NULL) ||
      (otype != authmessagetype) ||
      (orandblocksize != 2) ||
      (strcmp(orandblock, "OK") != 0)) {
    if (login_ok) {
      if (plocallogin == NULL) {
	free_authinfo(locallogin);
      }
      if (premotelogin == NULL) {
	free_authinfo(remotelogin);
      }
      free(randblock);
      free(oname);
      return -1;
    } else {
      if (plocallogin == NULL) {
	free_authinfo(locallogin);
      }
      if (premotelogin == NULL) {
	free_authinfo(remotelogin);
      }
      free(randblock);
      free(oname);
      return -2;
    }
  }


  if (plocallogin == NULL) {
    free_authinfo(locallogin);
  }
  if (premotelogin == NULL) {
    free_authinfo(remotelogin);
  }
  free(randblock);
  free(oname);
  free(orandblock);
  
  if (!login_ok) {
    return -3;
  }

  // all right!
  return 0;
}



/**
   Load authentication informations (netname, name, passwd,
   keyencrypt, keydecrypt) from authfile.

   @param netname (char *) specify the network name (may IP). NULL not
   specified.

   @param name (char *) specity the login name. NULL not
   specified.

   @return (struct AUTHINFO *) the first entry from authfile which
   matches network name OR login name. If both values are NULL, the
   first entry of the authfile is given back.
*/
struct AUTHINFO *getauthinfo(char *netname, char *name) {
  // Descriptor for authfile
  FILE *f;
  // buffer for reading one line of the authfile
  char buf[authfilemaxlinelenght];
  // number of fields in the authfile
  int fields = 5;
  // pointer buffer for the five parts of the line
  char *bufsplit[fields];
  // char **bufsplit;
  // control variable, count variable for field
  int i, j;
  struct AUTHINFO *load;
  // temporary pointer
  char *s;

  //  bufsplit = (char **) malloc(sizeof(char *) * fields);

  // allocate memory for auth-structure
  load = (struct AUTHINFO *) malloc(sizeof(struct AUTHINFO));
  if (load == NULL) {
    perror("malloc(sizeof(struct AUTHINFO))");
    return NULL;
  }

  // open authfile
  if ((f = fopen(authfile0, "ro")) == NULL) {
    perror(authfile0);
    if ((f = fopen(authfile1, "ro")) == NULL) {
      perror(authfile0);
      free(load);
      return NULL;
    }
  }

  // read as long as ther is no more data
  while (!feof(f)) {
    // read one line
    fgets(buf, authfilemaxlinelenght - 1, f);

    // split the line into it five components
    j = 0;
    bufsplit[j++] = buf;
    //    load -> netname = buf;
    for (i = 0; i < authfilemaxlinelenght; i++) {
      if (buf[i] == authfilefieldseperator) {
	buf[i] = 0;
	if (j == fields) {
	  break;
	}
	bufsplit[j++] = buf + i + 1;
      }
    }

    // if this the right entry? Compare with parameter.
    if (
	((   name != NULL) && (strcmp(   name, bufsplit[1]) == 0))
	||
	((netname != NULL) && (strcmp(netname, bufsplit[0]) == 0))
	||
	((netname == NULL) && (name == NULL))
       ) {
      
      // allocate Memory
      for (i = 0; i < fields; i++) {
	s = NULL;
	s = (char *) malloc(strlen(bufsplit[i]) + 1);
	if (s == NULL) {
	  perror("malloc()");
	  free(load);
	  return NULL;
	}
	strcpy(s, bufsplit[i]);
	bufsplit[i] = s;
      }

      // store the pointers in the struct
      load -> netname =    bufsplit[0];
      load -> name =       bufsplit[1];
      load -> passwd =     bufsplit[2];
      load -> keyencrypt = bufsplit[3];
      load -> keydecrypt = bufsplit[4];

      // return the pointer to this struct
      return load;
    }
  }
  free(load);
  return NULL;
}




/**
   Thread used by linemonitor_server() NOT for direct usage.
*/
void linemonitor_server_thread(struct LINEMONITOR_THREAD_DATA
			       *linemonitor_thread_data) {
  unsigned char buf;

  // configuration of poll -- waiting for an event of the socket.
  struct pollfd polld;
  polld.fd = linemonitor_thread_data -> sock;
  polld.events = POLLIN | POLLPRI;

  while (1) {
    // Receive a Ping/Byte
    if (recv(linemonitor_thread_data -> sock, ((char *) &buf), 1,
	     MSG_WAITALL) != 1) {
      linemonitor_thread_data -> linemonitor_exception(
		       linemonitor_thread_data -> server,
		       linemonitor_thread_data -> port, 0);
      break;
    }
    // And Send it Back
    if (send(linemonitor_thread_data -> sock, &buf, 1, 0) != 1) {
      linemonitor_thread_data -> linemonitor_exception(
		       linemonitor_thread_data -> server,
		       linemonitor_thread_data -> port, 0);
      break;
    }

    // Test, if next Ping is received within the reload-time plus
    // soft-timeout
    if (poll(&polld, 1, linemonitor_thread_data -> wait_msec)
	<= 0) {

      if (poll(&polld, 1, linemonitor_thread_data -> soft_msec)
	  <= 0) {
	// If not, call exception-function
	linemonitor_thread_data -> linemonitor_exception(
			    linemonitor_thread_data -> server,
			    linemonitor_thread_data -> port, 1);

	// and test if the data is received within the hard-timeout
	if (poll(&polld, 1, linemonitor_thread_data -> hard_msec)
	    <= 0) {
	  // If not, call exception-function
	  linemonitor_thread_data -> linemonitor_exception(
                              linemonitor_thread_data -> server,
			      linemonitor_thread_data -> port, 2);
	}
      }
    }
  }

  pthread_exit(NULL);
}



/**
   Monitor if the "line" is fast enough: Server Application. This
   function opens a port and wait for the first connection on this
   port. All data/pings which is sent by this first connection will
   be sent back. The soft-timeout will called after wait_msec AND
   soft_msec is timeouted. The hard-timeout will called after
   soft-timeout was called AND hard_msec is timeouted.

   @param port (int) port which should be listend

   @param soft_msec (int) timeout in milliseconds which causes
   soft-real-time exception.

   @param hard_msec (int) timeout in milliseconds which causes
   hard-real-time exception.

   @param wait_msec (int) timeout for resent -- sending of the next
   ping.

   @param linemonitor_exception (pointer to function) This function
   will be called if an exception occurs. It becomes the following
   parameters: server name (char *) which is always null, port (int):
   listend port and type (int) of exception which can be: 0: Connicion
   Fault, 1: Soft Real Time Exception, 2: HARD Real Time Exception.

   @return (int) Filediscriptor to the used socket. Only for usage
   with linemonitor_emergencystop().
*/
int linemonitor_server(int port,
		   int soft_msec, int hard_msec, int wait_msec,
		   void (*linemonitor_exception)(char *server, int port,
						 int type)) {
  int  sock;

  /* connector's address information */
  struct sockaddr_in their_addr;
  int sin_size;
  int fd;

  // ID and atributes for the threads
  pthread_t        thrd_2;
  pthread_attr_t   thrd_2_attr;

  // allocate memory to store parameter for the
  // linemonitor_server_thread() function.
  struct LINEMONITOR_THREAD_DATA *linemonitor_thread_data;
  linemonitor_thread_data = (struct LINEMONITOR_THREAD_DATA *)
    malloc(sizeof(struct LINEMONITOR_THREAD_DATA));
  if (linemonitor_thread_data == NULL) {
    perror("malloc()");
    return -1;
  }

  // store all necessary parameters in this memory
  linemonitor_thread_data -> server = NULL;
  linemonitor_thread_data -> port = port;
  linemonitor_thread_data -> soft_msec = soft_msec;
  linemonitor_thread_data -> hard_msec = hard_msec;
  linemonitor_thread_data -> wait_msec = wait_msec;
  linemonitor_thread_data -> linemonitor_exception =
    linemonitor_exception;

  // Bind a port
  sock = socket_bind(port, 10);

  // wait for the first connection
  // only accept the first connection
  sin_size = sizeof(struct sockaddr_in);
  if ((fd = accept(sock, (struct sockaddr *) &their_addr,
		   &sin_size)) != -1) {
    char *pip = inet_ntoa(their_addr.sin_addr);
    
    linemonitor_thread_data -> sock = fd;
  
    // starting linemonitor_server_thread()
    pthread_attr_init(&thrd_2_attr);
    if (pthread_create(&thrd_2,
		       &thrd_2_attr,
		       (void *) linemonitor_server_thread,
		       linemonitor_thread_data) != 0) {
      return -1;
    }

    return fd;
  }

  return -1;
}



/**
   Sends an "Emergency Stop" to the client's side, linemonitor() will
   produce an "Emergency Stop" exception (type 4).
*/
void linemonitor_emergencystop(int sock) {
  unsigned char data = 254;
  send(sock, &data, 1, 0);
}



/**
   Thread used by linemonitor() NOT for direct usage.
*/
int linemonitor_thread(struct LINEMONITOR_THREAD_DATA
		       *linemonitor_thread_data) {
  // buffer for sending a ping
  unsigned char counter;
  // buffer for receiving a ping
  unsigned char rcounter;
  
  // configuration of poll -- waiting for an event of the socket.
  struct pollfd polld;
  polld.fd = linemonitor_thread_data -> sock;
  polld.events = POLLIN | POLLPRI;


  while (1) {
    // increase counter, prevent "Emergency Stop"-Code 254
    counter++;
    if (counter == 254) {
      counter = 0;
    }

    // send a ping
    if (send(linemonitor_thread_data -> sock, &counter, 1, 0) != 1) {
      linemonitor_thread_data -> linemonitor_exception(
                            linemonitor_thread_data -> server,
			    linemonitor_thread_data -> port, 0);
      break;
    }

    // Test, if Ping returns within the soft-timeout time, if not
    // cause exception
    if (poll(&polld, 1, linemonitor_thread_data -> soft_msec) <= 0) {
      linemonitor_thread_data -> linemonitor_exception(
			    linemonitor_thread_data -> server,
			    linemonitor_thread_data -> port, 1);
      // Test, if Ping returns within the soft-timeout plus
      // hard-timeout time, if not cause exception
      if (poll(&polld, 1, linemonitor_thread_data -> hard_msec) <= 0) {
	linemonitor_thread_data -> linemonitor_exception(
                              linemonitor_thread_data -> server,
			      linemonitor_thread_data -> port, 2);
      }
    }

    // receive the ping
    if (recv(linemonitor_thread_data -> sock, ((char *) &rcounter), 1,
	     MSG_WAITALL) != 1) {
      linemonitor_thread_data -> linemonitor_exception(
			    linemonitor_thread_data -> server,
			    linemonitor_thread_data -> port, 0);
      break;
    }

    // If "Emergency Stop" code was received, call "Emergency Stop"
    // exception and retry to receive a ping
    if (rcounter == 254) {
      linemonitor_thread_data -> linemonitor_exception(
			    linemonitor_thread_data -> server,
			    linemonitor_thread_data -> port, 4);

      if (recv(linemonitor_thread_data -> sock, ((char *) &rcounter),
	       1, MSG_WAITALL) != 1) {
	linemonitor_thread_data -> linemonitor_exception(
			    linemonitor_thread_data -> server,
			    linemonitor_thread_data -> port, 0);
	break;
      }
    }

    // Test on right transmission code and call "Transmission Fault"
    // exception if the data is currupted
    if (counter != rcounter) {
      linemonitor_thread_data -> linemonitor_exception(
			    linemonitor_thread_data -> server,
			    linemonitor_thread_data -> port, 3);
    }

    // Wait before sendin next ping
    if (poll(&polld, 1, linemonitor_thread_data -> wait_msec) <= 0) {
    }
  }

  pthread_exit(NULL);
}



/**
   Monitor if the "line" is fast enough: Client/Robot Application. This
   function opens a socket stream, sents pings/bytes and wait for them
   to come back. The soft-timeout will called after soft_msec is
   timeouted. The hard-timeout will called after soft-timeout was
   called AND hard_msec is timeouted. wait_msec specifies the time
   which is waited after a ping is received befor the next one will be
   launched.

   @param server (char *) server to be connected

   @param port (int) port to be connected

   @param soft_msec (int) timeout in milliseconds which causes
   soft-real-time exception.

   @param hard_msec (int) timeout in milliseconds which causes
   hard-real-time exception.

   @param wait_msec (int) timeout for resent -- sending of the next
   ping.

   @param linemonitor_exception (pointer to function) This function
   will be called if an exception occurs. It becomes the following
   parameters: server name (char *) which is always null, port (int):
   listend port and type (int) of exception which can be: 0: Connicion
   Fault, 1: Soft Real Time Exception, 2: HARD Real Time Exception, 3:
   Transmission Fault, 4: Emergency Stop.
*/
int linemonitor(char *server, int port,
		int soft_msec, int hard_msec, int wait_msec,
		void (*linemonitor_exception)(char *server, int port,
					      int type)) {

  // ID and atributes for the threads
  pthread_t        thrd_2;
  pthread_attr_t   thrd_2_attr;

  // allocate memory to store parameter for the
  // linemonitor_thread() function.
  struct LINEMONITOR_THREAD_DATA *linemonitor_thread_data;
  linemonitor_thread_data = (struct LINEMONITOR_THREAD_DATA *)
    malloc(sizeof(struct LINEMONITOR_THREAD_DATA));
  if (linemonitor_thread_data == NULL) {
    perror("malloc()");
    return -1;
  }

  // connect to server
  linemonitor_thread_data -> sock = socket_connect(server, port);
  if (linemonitor_thread_data -> sock <= 0) {
    linemonitor_exception(server, port, 0);
    return -1;
  }

  // store all necessary parameters in this memory
  linemonitor_thread_data -> server = server;
  linemonitor_thread_data -> port = port;
  linemonitor_thread_data -> soft_msec = soft_msec;
  linemonitor_thread_data -> hard_msec = hard_msec;
  linemonitor_thread_data -> wait_msec = wait_msec;
  linemonitor_thread_data -> linemonitor_exception =
    linemonitor_exception;

  // launch linemonitor_thread()
  pthread_attr_init(&thrd_2_attr);
  return pthread_create(&thrd_2,
			&thrd_2_attr,
			(void *) linemonitor_thread,
			linemonitor_thread_data);
}

