Logo Search packages:      
Sourcecode: samhain version File versions  Download package

sh_forward.c

/* SAMHAIN file system integrity testing                                   */
/* Copyright (C) 1999, 2000 Rainer Wichmann                                */
/*                                                                         */
/*  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 "config_xor.h"

#include <string.h>
#include <stdio.h>
#include <stdlib.h>


/* Must be early on FreeBSD
 */
#include <sys/types.h>

/* must be .le. than (1020 * 64)
 * (see sh_tools.c -- put_header)
 *
 * also: must be  (N * 16), otherwise
 * binary files cannot be transferred encrypted
 *
 * 65280 = (1020*64)
 * #define TRANS_BYTES 8000  V0.8
 */
#ifdef  SH_ENCRYPT_2
#define TRANS_BYTES 65120
#else
#define TRANS_BYTES 65280
#endif

/* timeout for session key
 */
#define TIMEOUT_KEY 7200

/* max time between connection attempts
 */
#define TIMEOUT_CON 2048 

/* #undef  SRP_DEBUG */
/* #define SRP_DEBUG */

#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif

#if TIME_WITH_SYS_TIME
#include <sys/time.h>
#include <time.h>
#else
#if HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <time.h>
#endif
#endif

/*
#ifdef TM_IN_SYS_TIME
#include <sys/time.h>
#else
#include <time.h>
#endif
*/

#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif

#ifdef  HAVE_UNISTD_H
#include <errno.h>
#include <signal.h>
#include <setjmp.h>
#include <pwd.h>
#include <grp.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <unistd.h>
#endif

#ifndef FD_SET
#define NFDBITS         32
#define FD_SET(n, p)    ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
#define FD_CLR(n, p)    ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
#define FD_ISSET(n, p)  ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
#endif /* !FD_SET */
#ifndef FD_SETSIZE
#define FD_SETSIZE      32
#endif
#ifndef FD_ZERO
#define FD_ZERO(p)      memset((char *)(p), '\0', sizeof(*(p)))
#endif

#if defined(HAVE_MLOCK) && !defined(HAVE_BROKEN_MLOCK)
#include <sys/mman.h>
#endif


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

#include "samhain.h"
#include "sh_tiger.h"
#include "sh_utils.h"
#include "sh_unix.h"
#include "sh_forward.h"
#include "sh_srp.h"
#include "sh_fifo.h"
#include "sh_tools.h"
#include "sh_entropy.h"
#include "sh_html.h"
#include "sh_mail.h"
#include "sh_socket.h"
#define SH_NEED_GETHOSTBYXXX
#include "sh_static.h"

#ifdef SH_ENCRYPT
#include "rijndael-api-fst.h"
char * sh_tools_makePack (unsigned char * header, 
                    char * payload, int payload_size,
                    keyInstance * keyInstE);
char * sh_tools_revertPack (unsigned char * header, char * message,
                      keyInstance * keyInstE, 
                      unsigned long message_size);
#endif

/* define this if you want to debug the client/server communication */
/* #define SH_DBG_PROT 1 */

#ifdef  SH_DBG_PROT
#define SH_SHOWPROT(c,d) sh_tools_show_header((c), (d))
#else
#define SH_SHOWPROT(c,d) 
#endif

/* the port client will be connecting to 
 */
#ifndef SH_DEFAULT_PORT
#define SH_DEFAULT_PORT 49777    
#endif

#ifndef SH_SELECT_REPEAT
#define SH_SELECT_REPEAT 60
#endif

#ifndef SH_HEADER_SIZE
#define SH_HEADER_SIZE 7
#endif

#ifndef SH_CHALLENGE_SIZE
#define SH_CHALLENGE_SIZE 9
#endif

#undef  FIL__
#define FIL__  _("sh_forward.c")

int     clt_class = (-1);

#ifndef SH_STANDALONE

#if defined(WITH_TRACE) || defined(WITH_TPT) 
char * hu_trans(const char * ihu)
{
  static char ohu[17];
  sprintf(ohu, _("%c%03o"), '\\',                   /* known to fit  */
        (unsigned char) ihu[0]);
  sprintf(&(ohu[4]), _("%c%03o"), '\\',             /* known to fit  */
        (unsigned char) ihu[1]);
  sprintf(&(ohu[8]), _("%c%03o"), '\\',             /* known to fit  */
        (unsigned char) ihu[2]);
  sprintf(&(ohu[12]), _("%c%03o"), '\\',            /* known to fit  */
        (unsigned char) ihu[3]);
  ohu[16] = '\0';
  return ohu;
}
#endif

static int StripDomain = S_TRUE;

int sh_forward_set_strip (char * str)
{
  static int fromcl = 0;
  char dummy[2] = "F";

  if (fromcl == 1)
    return 0;

  if (str == NULL)
    {
      fromcl = 1;
      return (sh_util_flagval(dummy, &StripDomain));
    }
  else
    return (sh_util_flagval(str, &StripDomain));
#if 0
  StripDomain = BAD;
  if (dummy)
    return GOOD;
  else
    return GOOD;
#endif
}

#include <ctype.h>

const char * sh_strip_domain (char *name)
{
  char *          first;
  static char     name_2[SH_MINIBUF+1];
  register int    i = 0;

  SL_ENTER(_("sh_strip_domain"));

  if (StripDomain == S_FALSE || (first  = strchr(name, '.')) == NULL) 
    {
      SL_RETURN( name, _("sh_strip_domain"));
    }
  else
    {

      /* check whether it is in dotted number format
       * --> last part must be kept
       */
      if (0 != is_numeric(name))
      {
        SL_RETURN( name, _("sh_strip_domain"));
        /*
        i = sl_strlen(name) - 1;
        while (name[i] != '.' && i >= 0)
          --i;
        if (name[i] == '.') ++i;
        sl_strlcpy( name_2, &name[i], SH_MINIBUF +1 );
        */
      }
      else
      {
        memset (name_2, 0, SH_MINIBUF+1);
        first = name;
        while (i < SH_MINIBUF && *first != '.' && *first != '\0')
          {
            name_2[i] = *first;
            ++first; ++i;
          }
        name_2[SH_MINIBUF] = '\0';
      }
    }

  SL_RETURN( name_2, _("sh_strip_domain"));
}

/* #ifndef SH_STANDALONE */
#endif

#ifndef USE_SRP_PROTOCOL
static
void sh_passwd (char * salt, char * password, char * nounce, char *hash)
{

  char           *combi;
  size_t          len;
  register int i;
  unsigned char * dez = NULL;

  if (password == NULL)
    dez = (unsigned char *) &(skey->pw[0]);
  else if (sl_strlen(password) < PW_LEN)
    {
      fprintf(stderr, _("Password has less than %d chars !\n"),
               PW_LEN);
      _exit(EXIT_FAILURE);
    }

  if (password == NULL)
    {
      /* --- copy password ---
       */
      for (i = 0; i < PW_LEN; ++i)
      { 
        skey->vernam[i] = (char)(*dez); 
        ++dez; 
      }
      (void) sl_strlcpy (skey->vernam,
                   sh_tiger_hash(skey->vernam, TIGER_DATA, PW_LEN), 
                   KEY_LEN+1);
    }
  else
    {
      (void) sl_strlcpy (skey->vernam, password, KEY_LEN+1);
    }

  len = sl_strlen(salt) + sl_strlen(skey->vernam) + 1;
  if (nounce != NULL) 
    len += sl_strlen(nounce);
  
  /* H(s,P)
   */
  combi = SH_ALLOC(len);
  (void) sl_strlcpy (combi, salt, len);
  (void) sl_strlcat (combi, skey->vernam, len);
  if (nounce != NULL)
    (void) sl_strlcat (combi, nounce, len);
  (void) sl_strlcpy (hash, 
                 sh_tiger_hash(combi, TIGER_DATA, 
                           (unsigned long) sl_strlen(combi)),
                 KEY_LEN+1);

  /*
    fprintf(stderr, "DD: A: <%s>\n", salt);
    fprintf(stderr, "DD: P: <%s>\n", skey->pw); 
    fprintf(stderr, "DD: V: <%s>\n", skey->vernam); 
    fprintf(stderr, "DD: C: <%s>\n", combi);
    fprintf(stderr, "DD: H: <%s>\n", hash); 
  */

  SH_FREE (combi);
  hash[KEY_LEN] = '\0';
  return;
}
#endif

#ifdef SH_WITH_CLIENT

static int count_dev_server = 0;

void reset_count_dev_server(void)
{
  count_dev_server = 0;
  return;
}

int sh_forward_setlogserver (char * address)
{
  SL_ENTER(_("sh_forward_setlogserver"));

  if (address != NULL && count_dev_server < 2 
      && sl_strlen(address) < SH_PATHBUF && sl_strlen(address) > 0) 
    {
      if (count_dev_server == 0)
      (void) sl_strlcpy (sh.srvexport.name, address, SH_PATHBUF);
      else
      (void) sl_strlcpy (sh.srvexport.alt,  address, SH_PATHBUF);

      ++count_dev_server;
      SL_RETURN (0, _("sh_forward_setlogserver"));
    }
  SL_RETURN (-1, _("sh_forward_setlogserver"));
}

static
int sh_forward_send_intern (int mysocket, char protocol, char * micro, 
                      char * msgbuf, unsigned long length, int docrypt)
{
  unsigned long           numbytes, countbytes;
  int                     flag_err = 0;
  unsigned char           head[SH_HEADER_SIZE];
  char                  * outbuf;

#ifdef SH_ENCRYPT

  unsigned long           blkfac;
  int                     rem;
  unsigned long           length2;
  char                  * msg2buf = NULL;
  char                  * p, * q;
  RIJ_BYTE                    inBlock[B_SIZ]; 
  RIJ_BYTE                    outBlock[B_SIZ];
  unsigned long           j;
  cipherInstance          cipherInst;
  int                     err_num;
#else
  docrypt = SL_FALSE; /* dummy to fix compiler warning */
#endif

  SL_ENTER(_("sh_forward_send_intern"));

#ifdef SH_ENCRYPT
  if      ((S_TRUE == docrypt) && ((protocol & SH_PROTO_EN2) != (char)0))
    {
      put_header (head, (int)protocol, &length, micro);
      msg2buf  = sh_tools_makePack (head, msgbuf, (int) length, 
                            &(skey->keyInstE));
      /*@-usedef@*/
      length   = (unsigned long) (256 * (unsigned int)head[1] + 
                          (unsigned int)head[2]);
      /*@+usedef@*/
      outbuf   = msg2buf;
    }
  else if ((S_TRUE == docrypt) && ((protocol & SH_PROTO_ENC) != (char)0))
    {
      blkfac  = length/B_SIZ;
      rem     = (int) (length - (B_SIZ * blkfac));
      length2 = (B_SIZ * blkfac) + ((rem == 0) ? 0 : B_SIZ);

      msg2buf = SH_ALLOC((size_t)length2);
      p       = msgbuf;
      q       = msg2buf;
      
      err_num = cipherInit (&cipherInst, (RIJ_BYTE)MODE_CBC, NULL);

      if (err_num < 0)
      sh_error_handle((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN,
                  errorExplain(err_num), 
                  _("sh_forward_send_intern: cipherInit"));


      for (j = 0; j < blkfac; ++j)
      {
        memcpy(inBlock, p, B_SIZ);
        err_num = blockEncrypt(&cipherInst, &(skey->keyInstE), 
                         inBlock, 128 * BNUM, outBlock);
        if (err_num < 0)
          sh_error_handle((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN,
                      errorExplain(err_num), 
                      _("sh_forward_send_intern: blockEncrypt"));
        memcpy(q, outBlock, B_SIZ);
        p += B_SIZ;
        q += B_SIZ;
      }
      if (rem > 0)
      {
        memset(inBlock, 0, B_SIZ);
        memcpy(inBlock, p, (size_t)rem);
        err_num = blockEncrypt(&cipherInst, &(skey->keyInstE), 
                         inBlock, 128 * BNUM, outBlock);
        if (err_num < 0)
          sh_error_handle((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN,
                      errorExplain(err_num), 
                      _("sh_forward_send_intern: blockEncrypt"));
        memcpy(q, outBlock, B_SIZ);
        q += B_SIZ;
      }

      outbuf = msg2buf;
      length = length2;
      put_header (head, (int)protocol, &length, micro);
    }
  else
    {
      outbuf = msgbuf;
      put_header (head, (int)protocol, &length, micro);
    }
#else
  outbuf = msgbuf;
  put_header (head, (int)protocol, &length, micro);
#endif

  SH_SHOWPROT(head,'>');
  
  numbytes     = SH_HEADER_SIZE;
  countbytes   = write_port (mysocket, 
                       (char *) head, numbytes,
                       &flag_err, 300);

  if (countbytes == numbytes && outbuf != NULL)
    {
      numbytes     = (length);
      countbytes   = write_port (mysocket, 
                         outbuf, numbytes,
                         &flag_err, 300);
    }

#ifdef SH_ENCRYPT
  /*@-usedef@*/
  if (msg2buf != NULL)
    SH_FREE(msg2buf);
  /*@+usedef@*/
#endif

  if (countbytes == numbytes)
    {
      SL_RETURN( 0, _("sh_forward_send_intern"));
    }
  else
    {
      SL_RETURN( flag_err, _("sh_forward_send_intern"));
    }
}
static
int sh_forward_send (int mysocket, char protocol, char * micro, 
                 char * msgbuf, unsigned long length)
{
  int i;
  SL_ENTER(_("sh_forward_send"));
  TPT(( 0, FIL__, __LINE__, _("msg=<Send.>\n")));
  i =  sh_forward_send_intern (mysocket, protocol, micro, 
                         msgbuf, length, S_FALSE);
  SL_RETURN(i, _("sh_forward_send"));
}
static
int sh_forward_send_crypt (int mysocket, char protocol, char * micro, 
                     char * msgbuf, unsigned long length)
{
  int i;
  SL_ENTER(_("sh_forward_send_crypt"));
#ifdef SH_ENCRYPT
  TPT(( 0, FIL__, __LINE__, _("msg=<Send encrypted.>\n")));
#else
  TPT(( 0, FIL__, __LINE__, _("msg=<Send.>\n")));
#endif
  i = sh_forward_send_intern (mysocket, protocol, micro, 
                        msgbuf, length, S_TRUE);
  SL_RETURN(i, _("sh_forward_send_crypt"));
}


/* receive answer, add a trailing NULL to terminate string
 * rev 0.8
 */
static
long sh_forward_receive_intern (int mysocket, char protocol, char * micro,     
                        char *  msgbuf, unsigned long length, 
                        int docrypt)
{
  unsigned long numbytes, countbytes;
  int           flag_err = -1;
  unsigned char head[SH_HEADER_SIZE];

#ifdef SH_ENCRYPT

  unsigned long           head_length;
  unsigned long           blkfac;
  /* unsigned long           length2; */
  char                  * p, * q, * tmp;
  RIJ_BYTE                    inBlock[B_SIZ]; 
  RIJ_BYTE                    outBlock[B_SIZ];
  unsigned long           j;
  cipherInstance          cipherInst;
  int                     err_num;
#else
  docrypt = SL_FALSE; /* dummy to fix compiler warning */
#endif

  SL_ENTER(_("sh_forward_receive_intern"));

  if (micro != NULL)
    micro[4]     = '\0';

  if (msgbuf != NULL)
    msgbuf[0]     = '\0';

  numbytes     = SH_HEADER_SIZE;
  countbytes   = read_port (mysocket, 
                      (char *) head, numbytes,
                      &flag_err, 300);

  if (countbytes != numbytes)
    {
      TPT(( 0, FIL__, __LINE__, _("msg=<countbytes != numbytes>\n")));
      SL_RETURN(flag_err, _("sh_forward_receive_intern"));
    }
  /*@-usedef +ignoresigns@*/
  else if (head[0] != protocol &&  
         (head[0] & SH_PROTO_SRP) == (char)0 /* not set */)
    {
      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_MISMATCH);
      SL_RETURN((-1), _("sh_forward_receive_intern"));
    }
  /*@+usedef -ignoresigns@*/
  else
    {
      get_header (head, &numbytes, micro);
      SH_SHOWPROT(head, '<');

      if (numbytes > 0)
      {
        numbytes = (numbytes > length ? length : numbytes);

        countbytes   = read_port (mysocket, 
                            msgbuf, numbytes,
                            &flag_err, 300);

        if (countbytes < length)
          msgbuf[countbytes] = '\0';
        else
          msgbuf[length-1] = '\0';

        if (flag_err != 0)
          {
            TPT(( 0, FIL__, __LINE__, _("msg=<read error>\n")));
            SL_RETURN((-1), _("sh_forward_receive_intern"));
          }
      }
    }

#ifdef SH_ENCRYPT
  if      ((S_TRUE == docrypt) && ((protocol & SH_PROTO_EN2) != (char)0))
    {
      tmp = SH_ALLOC((size_t)length);
      memcpy(tmp, msgbuf, (size_t)length);
      tmp = sh_tools_revertPack (head, tmp, &(skey->keyInstD), countbytes);

      head_length = (unsigned long) (256 * (unsigned int)head[1] + 
                             (unsigned int)head[2]);
      head_length = (head_length > length ? length : head_length);
      length      = head_length;

      memcpy(msgbuf, tmp, (size_t)length);
      msgbuf[length] = '\0';
      SH_FREE(tmp);
      if (countbytes == numbytes) 
      {
        countbytes = length; /* to avoid error on return, see below */
      }
      numbytes = length;
    }
  else if ((S_TRUE == docrypt) && ((protocol & SH_PROTO_ENC) != (char)0))
    {
      /* Decrypt only complete blocks. If there is an incomplete block,
       * something is wrong anyway.
       * Decrypt in place.
       */
      blkfac  = countbytes/B_SIZ;
      /* length2 = (B_SIZ * blkfac); */
      p       = msgbuf;
      q       = msgbuf;
      
      err_num = cipherInit (&cipherInst, (RIJ_BYTE)MODE_CBC, NULL);

      if (err_num < 0)
      sh_error_handle((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN,
                  errorExplain(err_num), 
                  _("sh_forward_receive_intern: cipherInit"));

      for (j = 0; j < blkfac; ++j)
      {
        memcpy(inBlock, p, B_SIZ);
        err_num = blockDecrypt(&cipherInst, &(skey->keyInstD), 
                         inBlock, 128 * BNUM, outBlock);
        if (err_num < 0)
          sh_error_handle((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN,
                      errorExplain(err_num), 
                      _("sh_forward_receive_intern: blockDecrypt"));
        memcpy(q, outBlock, B_SIZ);
        p += 16;
        q += 16;
      }
    }
#endif

  if (countbytes == numbytes)
    {
      SL_RETURN(((long)numbytes), _("sh_forward_receive_intern"));
    }
  else
    {
      TPT(( 0, FIL__, __LINE__, _("msg=<short read>\n")));
      SL_RETURN(flag_err, _("sh_forward_receive_intern"));
    }
}

static
long sh_forward_receive (int mysocket, char protocol, char * micro,     
                   char * msgbuf, unsigned long length)
{
  long i;
  SL_ENTER(_("sh_forward_receive"));
  TPT(( 0, FIL__, __LINE__, _("msg=<Receive.>\n")));
  i = sh_forward_receive_intern (mysocket, protocol, micro, 
                         msgbuf, length, S_FALSE);
  SL_RETURN(i, _("sh_forward_receive"));
}

static
long sh_forward_receive_crypt (int mysocket, char protocol, char * micro,     
                         char * msgbuf, unsigned long length)
{
  long i;
  SL_ENTER(_("sh_forward_receive_crypt"));
#ifdef SH_ENCRYPT
  TPT(( 0, FIL__, __LINE__, _("msg=<Receive encrypted.>\n")));
#else
  TPT(( 0, FIL__, __LINE__, _("msg=<Receive.>\n")));
#endif
  i = sh_forward_receive_intern (mysocket, protocol, micro, 
                         msgbuf, length, S_TRUE);
  SL_RETURN(i, _("sh_forward_receive"));
}

/**************************************************
 *
 *
 *  C L I E N T  
 *
 *
 ***************************************************/


#include <time.h>

static SH_FIFO * fifo = NULL;

static long sh_forward_try (char * errmsg);


long sh_forward (char * errmsg)
{
  static int           have_server = GOOD;
  long   status;
  char * popmsg;
  static int failed = GOOD;

  SL_ENTER(_("sh_forward"));

  /* --- No log server available. ---
   */
  if (have_server == GOOD && sh.srvexport.name[0] == '\0')
    {
      have_server = BAD;
      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_NONAME);
      SL_RETURN (-1, _("sh_forward"));
    }
  else if (have_server == BAD)
    {
      SL_RETURN (-1, _("sh_forward"));
    }

  /* --- Allocate fifo. ---
   */
  if (fifo == NULL)
    {
      fifo = SH_ALLOC(sizeof(SH_FIFO));
      fifo_init(fifo);
    }

  /* --- Check for messages on the queue, and send them first. ---
   */
  while (NULL != (popmsg = pop_list(fifo)) )
    {
      status = sh_forward_try (popmsg);
      if (status != 0)
      {
        (void) push_tail_list (fifo, popmsg); 
        SH_FREE(popmsg);
        if (SH_FIFO_MAX == push_list (fifo, errmsg))
          {
            SL_RETURN (-2, _("sh_forward"));
          }
        SL_RETURN (-1, _("sh_forward"));
      }
      SH_FREE(popmsg);
    }

  /* --- Now send the error message. ---
   */ 
  status = sh_forward_try (errmsg);
  if (status != 0)
    {
      if (failed == GOOD)
      sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_SRV_FAIL,
                   _("log server"), 
                   sh.srvexport.name);
      failed = BAD;
      if (SH_FIFO_MAX == push_list (fifo, errmsg))    /* push message on stack */
      {
        SL_RETURN (-2, _("sh_forward"));
      }
      SL_RETURN (-1, _("sh_forward"));
    }

  failed = GOOD;
  SL_RETURN (0, _("sh_forward"));  
}

static long sh_forward_try_impl (char * errmsg, char what);

static long sh_forward_try (char * errmsg)
{
  long i;
  SL_ENTER(_("sh_forward_try"));
  i = sh_forward_try_impl (errmsg, (char)SH_PROTO_MSG);
  SL_RETURN(i, _("sh_forward_try")); 
}

long sh_forward_req_file (char * file)
{
  long i;
  char tmp_file[8];
  SL_ENTER(_("sh_forward_req_file"));
  (void) sl_strlcpy(tmp_file, file, 8);
  i = sh_forward_try_impl (tmp_file, (char)SH_PROTO_BIG);
  SL_RETURN(i, _("sh_forward_req_file")); 
}

static  long sh_forward_try_impl (char * errmsg, char what)
{
  static int           initialized = BAD;
  static int           conn_state  = GOOD;
  int                  sockfd;
  int                  flag_err;
  char               * answer;

  unsigned char        theProto;

  char                 hash[KEY_LEN+1];
  size_t               len;
  char               * buffer;
#ifdef SH_ENCRYPT_2
  size_t               pos;  /* for the server command */
#endif
  char   head_u[5];

  char   nsrv[KEY_LEN+1];
  char   nclt[KEY_LEN+1];
  SL_TICKET sfd = -1;
  int    transfercount;

  char   foo_M1[KEY_LEN+1];
  UINT32 ticks;

  char         error_msg[256];
  char         error_call[SH_MINIBUF];
  int          error_num = 0;

#ifdef USE_SRP_PROTOCOL
  char   u_real[SH_CHALLENGE_SIZE];
  char * foo_A;
  char * foo_Sc;
  char * M;
#else
  char   nounce[KEY_LEN+1];
  char   temp[2*KEY_LEN+1];
  char   nonce_u[KEY_LEN+1];
#endif
  
#ifdef SH_ENCRYPT
  int err_num;
#endif

  static time_t time_now  = 1200;
  static time_t time_last =    0;

  static time_t timeout_val =  1;

  SL_ENTER(_("sh_forward_try_impl"));

  /* --- No message to transmit. ---
   */
  if (errmsg == NULL && initialized == GOOD)
    SL_RETURN( 0, _("sh_forward_try_impl"));
  
  /* --- Connection in bad state. ---
   */
  if (initialized == BAD || conn_state == BAD)
    {
      timeout_val = 
      (time_t)((timeout_val > TIMEOUT_CON) ? TIMEOUT_CON : timeout_val);

      /* --- Retry bad attempt only after some time. ---
       */
      time_now  = time (NULL);
      if ((time_now - time_last) < timeout_val) 
      {
        TPT(( 0, FIL__, __LINE__, _("msg=<Within deadtime, no retry.>\n")));
        SL_RETURN( (-1), _("sh_forward_try_impl"));
      }
      TPT(( 0, FIL__, __LINE__, _("msg=<Retry.>\n")));
    }
  time_last  = time (NULL);


  /* --- Try to connect to log server. ---
   */
  error_call[0] = '\0';

  sockfd = connect_port_2 (sh.srvexport.name, sh.srvexport.alt, 
                     SH_DEFAULT_PORT, 
                     error_call, &error_num, error_msg, 256);

  if (sockfd < 0)
    {
      conn_state = BAD;
      timeout_val *= 2;
      sh_error_handle ((-1), FIL__, __LINE__, error_num, 
                   MSG_E_NET, error_msg, error_call,
                   _("export"), sh.srvexport.name);
      SL_RETURN( (-1), _("sh_forward_try_impl"));
    }

  conn_state = GOOD;

  /*************************
   *
   *  initialization
   * 
   */

  flag_err = 0;
  answer   = SH_ALLOC(512);
  MLOCK(answer, 512);


#ifndef USE_SRP_PROTOCOL

  /**************************************************
   *
   * --- challenge/response authentication ---
   *
   **************************************************/

  if (initialized == BAD)
    {
      theProto = (unsigned char) SH_PROTO_SRP;

      TPT(( 0, FIL__, __LINE__, _("msg=<c/r: entry>\n")));

      (void) sl_strlcpy (answer, sh.host.name, 512);

      flag_err = sh_forward_send (sockfd, (char) theProto, _("SALT"), 
                          answer,  (unsigned long)sl_strlen(answer));

      TPT(( 0, FIL__, __LINE__, _("msg=<c/r: sent SALT, flag_err = %d>\n"), 
          flag_err));

      /* get nonce from server
       */
      if (flag_err == 0)
      {
        flag_err = (int) sh_forward_receive (sockfd, (char)theProto, head_u, 
                                     answer,  511);
        flag_err = (flag_err < 0) ? flag_err : 0;
        TPT(( 0, FIL__, __LINE__, 
            _("msg=<c/r: rcvt nonce, flag_err = %d>\n"), 
            flag_err));
      }

      /* entry point for jump from message forward if session key must
       * be re-initialized
       */    
 initBlock:

      if (0 == check_request (head_u, _("INIT")) && 
        flag_err == 0                         &&
        sl_strlen(answer) >  KEY_LEN )
      (void) sl_strlcpy(nounce, &answer[KEY_LEN], KEY_LEN+1);
      else
      flag_err = (-1);

      TPT(( 0, FIL__, __LINE__, _("msg=<c/r: rcvt INIT, flag_err = %d>\n"), 
          flag_err));

      /* verify random nonce v from server H(v, P)v
       */
      sh_passwd (nounce, NULL, NULL, temp);
      if ( 0 != sl_strncmp(temp, answer, KEY_LEN))
      flag_err = (-1);

      TPT(( 0, FIL__, __LINE__, _("msg=<c/r: vrfy nonce, flag_err = %d>\n"), 
          flag_err));


      /* --- Create own nonce. ---
       */
      ticks = (UINT32) taus_get (&(skey->rng0[0]), 
                         &(skey->rng1[0]),
                         &(skey->rng2[0]));
      (void) sl_strlcpy(nonce_u, 
                  sh_tiger_hash((char *) &ticks, 
                              TIGER_DATA, 
                              (unsigned long)sizeof(UINT32)),
                  KEY_LEN+1);

      /* --- Form the message H(H(u,v),P)u ---
       */
      (void) sl_strlcpy(temp, nonce_u, 2*KEY_LEN+1); 
      (void) sl_strlcat(temp,  nounce, 2*KEY_LEN+1); 
      (void) sl_strlcpy(temp, 
                  sh_tiger_hash(temp, 
                              TIGER_DATA, 
                              (unsigned long)sl_strlen(temp)),
                  KEY_LEN+1);
      sh_passwd (temp, NULL, NULL, foo_M1);
      (void) sl_strlcpy(temp, foo_M1, 2*KEY_LEN+1);
      (void) sl_strlcat(temp, nonce_u, 2*KEY_LEN+1);

      /* --- Send it to server. ---
       */
      if (flag_err == 0)
      {
        flag_err = (int) sh_forward_send (sockfd, 
                                  (char)(theProto|SH_PROTO_SRP), 
                                  _("PASS"), temp, 
                                  (unsigned long)sl_strlen(temp));
        TPT(( 0, FIL__, __LINE__, _("msg=<c/r: sent PASS, flag_err = %d>\n"),
            flag_err));
      }

      if (flag_err == 0)
      {
        flag_err = (int)sh_forward_receive (sockfd,
                                    (char)(theProto|SH_PROTO_SRP), 
                                    head_u, answer,  511);  
        sh_passwd (nounce, NULL, nonce_u, foo_M1);
        (void) sl_strlcpy (skey->session, foo_M1, KEY_LEN+1);
#ifdef SH_ENCRYPT
        err_num = makeKey(&(skey->keyInstE), 
                      (RIJ_BYTE)DIR_ENCRYPT, 192, skey->session);
        if (err_num < 0)
          sh_error_handle((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN,
                      errorExplain(err_num), 
                      _("sh_forward_try_impl: makeKey"));

        err_num = makeKey(&(skey->keyInstD), 
                      (RIJ_BYTE)DIR_DECRYPT, 192, skey->session);
        if (err_num < 0)
          sh_error_handle((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN,
                      errorExplain(err_num), 
                      _("sh_forward_try_impl: make_key"));
#endif
        initialized = GOOD;
      }

      if (initialized == BAD)
      {
        timeout_val *= 2;
        sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_NOAUTH);
        memset(answer, 0, sizeof(answer));
        MUNLOCK(answer, 512);
        SH_FREE(answer);
        SL_RETURN( (-1), _("sh_forward_try_impl"));
      }
      else
      {
        sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_AUTH);
      }
    }    

#else


  /* This is the SRP authenticated key exchange protocol.
   * Produces a session key skey->session.
   */
  if (initialized == BAD)
    {
      TPT(( 0, FIL__, __LINE__, _("msg=<srp: entry>\n")));

      theProto = SH_PROTO_SRP;

      sl_strlcpy (answer, sh.host.name, 512);
      flag_err = sh_forward_send    (sockfd, theProto, _("SALT "), 
                             answer,  sl_strlen(answer)); 

      TPT(( 0, FIL__, __LINE__, _("msg=<srp: sent SALT, flag_err = %d>\n"), 
          flag_err));

      if (flag_err == 0)
      {
        flag_err = sh_forward_receive (sockfd, theProto, head_u, 
                               answer,  511);
        flag_err = (flag_err < 0) ? flag_err : 0;
        TPT(( 0, FIL__, __LINE__, 
            _("msg=<srp: rcvt nonce, flag_err = %d>\n"), 
            flag_err));
      }

      /* Entry point for jump from message forward if session key must
       * be re-initialized.
       */    
    initBlock:
      TPT(( 0, FIL__, __LINE__, _("msg=<srp: INIT>\n")));

      if (flag_err == 0 &&
        (0 == check_request (head_u, _("INIT"))))
      {
        if (0 != sh_srp_init())
          sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_EBGN);
        else /* if (0 == sh_srp_init()) */
          {
            TPT(( 0, FIL__, __LINE__, _("msg=<srp: bignum initialized>\n")));

            sh_srp_x (answer, NULL);  /* x        password      */
            sh_srp_make_a ();         /* a        random number */
            foo_A = sh_srp_A();       /* g^a                    */

            TPT(( 0, FIL__, __LINE__, _("msg=<srp: A = %s>\n"), foo_A));

            if (foo_A == NULL)
            flag_err = (-1);

            if (flag_err == 0)
            flag_err = sh_forward_send    (sockfd, 
                                     (theProto|SH_PROTO_SRP), 
                                     _("PC01"),
                                     foo_A, sl_strlen(foo_A)+1); 
            if (flag_err == 0)
            {
              flag_err = sh_forward_receive (sockfd, 
                                     (theProto|SH_PROTO_SRP),
                                     head_u,
                                     answer, 511);
              flag_err = (flag_err < 0) ? flag_err : 0;
              TPT(( 0, FIL__, __LINE__, _("msg=<srp: B = %s>\n"), answer));
              TPT(( 0, FIL__, __LINE__, _("msg=<srp: u = %03o-%03o-%03o-%03o>\n"), head_u[0], head_u[1], head_u[2], head_u[3]));
            }

            /*                     u        nounce        */
            /*                     B        answer        */
            /*                     S = (B-g^x)^(a+ux)     */
            
            if (flag_err == 0)
            { 
              if (0 != sh_srp_check_zero (answer))
                sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_EZERO);
              else /* if (0 != sh_srp_check_zero (answer)) */
                {
                  sl_strlcpy(u_real, sh_tiger_hash(head_u, TIGER_DATA, 4), 
                         SH_CHALLENGE_SIZE);
                  foo_Sc = sh_srp_S_c (u_real, answer);

                  TPT(( 0, FIL__, __LINE__, _("msg=<srp: U = %s>\n"), 
                      u_real));
                  TPT(( 0, FIL__, __LINE__, _("msg=<srp:Sc = %s>\n"), 
                      foo_Sc));

                  /* --- Now send H(A,B,H(Sc)) and check. --- 
                   */
                  if (foo_Sc != NULL)
                  {
                    sl_strlcpy(foo_M1, 
                             sh_srp_M(foo_A, 
                                    answer, 
                                    sh_tiger_hash(foo_Sc, 
                                              TIGER_DATA, 
                                              sl_strlen(foo_Sc))),
                             KEY_LEN+1);

                    TPT(( 0, FIL__, __LINE__, _("msg=<srp:M1 = %s>\n"), 
                        foo_M1));

                    flag_err = sh_forward_send(sockfd, 
                                         (theProto|SH_PROTO_SRP), 
                                         _("PC02"),
                                         foo_M1, KEY_LEN+1);
                  }
                  else
                  {
                    flag_err = (-1);
                  }

                  if (flag_err == 0)
                  {
                    flag_err =sh_forward_receive(sockfd, 
                                           (theProto|SH_PROTO_SRP),
                                           head_u, 
                                           answer, 511);
                    flag_err = (flag_err < 0) ? flag_err : 0;
                    TPT(( 0, FIL__, __LINE__, _("msg=<srp: M = %s>\n"), 
                        answer));
                  }

                  if (flag_err == 0   &&
                    (0 == check_request (head_u, _("PARP"))))
                  {
                    /* ------  verify M2 = H(A, M1, K) --------
                     */
                    M = sh_srp_M (foo_A, foo_M1,
                              sh_tiger_hash(foo_Sc,
                                          TIGER_DATA,
                                          sl_strlen(foo_Sc)));
                    if (M != NULL && 
                        0 == sl_strncmp (answer, M, KEY_LEN+1))
                      {
                        sl_strlcpy (skey->session, 
                                sh_tiger_hash(foo_Sc, 
                                          TIGER_DATA,
                                          sl_strlen(foo_Sc)),
                                KEY_LEN+1);
                        TPT(( 0, FIL__, __LINE__, 
                            _("msg=<srp: Key = %s>\n"), 
                            skey->session));

#ifdef SH_ENCRYPT
                        err_num = makeKey(&(skey->keyInstE), 
                                    DIR_ENCRYPT, 
                                    192, skey->session);
                        if (err_num < 0)
                        sh_error_handle((-1), FIL__, __LINE__, -1, 
                                    MSG_E_SUBGEN,
                                    errorExplain(err_num), 
                                    _("sh_forward_try_impl: makeKey"));
                        err_num = makeKey(&(skey->keyInstD), 
                                    DIR_DECRYPT, 
                                    192, skey->session);
                        if (err_num < 0)
                        sh_error_handle((-1), FIL__, __LINE__, -1, 
                                    MSG_E_SUBGEN,
                                    errorExplain(err_num), 
                                    _("sh_forward_try_impl: makeKey"));
#endif
                        initialized = GOOD;
                      }
                  }
                  if (foo_Sc != NULL)
                  SH_FREE(foo_Sc);
                }
            }
            if (foo_A != NULL)
            SH_FREE(foo_A);
            sh_srp_exit();
          }
      }

      if (initialized == BAD)
      {
        timeout_val *= 2;
        sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_NOAUTH);
        memset(answer, '\0', sizeof(answer));
        MUNLOCK(answer, 512);
        SH_FREE(answer);
        SL_RETURN( (-1), _("sh_forward_try_impl"));
      }
      else
      {
        sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_AUTH);
      }
    }

#endif

  /* no message, just session key negotiated
   */
  if (errmsg == NULL)
    {
      timeout_val = 1;
      memset(answer, 0, sizeof(answer));
      MUNLOCK(answer, 512);
      SH_FREE(answer);
      TPT(( 0, FIL__, __LINE__, _("msg=<No message.>\n")));
      SL_RETURN( (0), _("sh_forward_try_impl"));
    }
  else if (what == (char)SH_PROTO_BIG)
    {
      MUNLOCK(answer, 512);
      SH_FREE (answer);
      answer   = SH_ALLOC(TRANS_BYTES + 256);
      MLOCK(answer, TRANS_BYTES + 256);
      TPT(( 0, FIL__, __LINE__, _("msg=<File transfer.>\n")));
    }


  (void) sl_strlcpy (answer, sh_util_siggen(skey->session,
                                  sh.host.name,
                                  sl_strlen(sh.host.name)), 
            KEY_LEN+1);
  TPT((0, FIL__, __LINE__, _("msg=<host %s>\n"), sh.host.name));
  TPT((0, FIL__, __LINE__, _("msg=<ckey %s>\n"), skey->session));
  TPT((0, FIL__, __LINE__, _("msg=<sign %s>\n"), answer));

    
  (void) sl_strlcat (answer, sh.host.name, 512);

  TPT((0, FIL__, __LINE__, _("msg=<mesg %s>\n"), answer));

  /***********************************************
   *
   * send the message
   *
   */

  if (what == (char) SH_PROTO_MSG)
    {
      theProto = (unsigned char)SH_PROTO_MSG;

      /* say HELO       
       */

      flag_err = sh_forward_send    (sockfd, 
                             (char)theProto, _("HELO"),
                             answer, 
                             (unsigned long)sl_strlen(answer)); 
      TPT(( 0, FIL__, __LINE__, _("msg=<Sent %s, status %d.>\n"), 
          answer, flag_err));
      if (flag_err == 0)
      { 
        /* --- Get challenge. ---  
         */
        flag_err = (int) sh_forward_receive (sockfd, 
                                     (char)SH_PROTO_MSG, head_u, 
                                     answer, 255);   
        TPT(( 0, FIL__, __LINE__, _("msg=<Rcvt %s, u %s, status %d.>\n"), 
          answer, hu_trans(head_u), flag_err));
        flag_err = (flag_err < 0) ? flag_err : 0;
        
        if (flag_err == 0)
          {
            
            /* --- Re-negotiate key. ---
             */
            if (0 == check_request_nerr(head_u, _("INIT")))
            {
              flag_err    = 0;
              initialized = BAD;
              goto initBlock;
            }
            
            else if (0 == check_request(head_u, _("TALK")))
            {

              /* --- Save the challenge. ---  
               */
              (void) sl_strlcpy(nsrv, answer, KEY_LEN + 1);
              
              /* --- Hash(msg,challenge,sessionkey). ---  
               */
              len    = sl_strlen(errmsg) + sl_strlen(answer) 
                + KEY_LEN + 1;
              len = (size_t)((len < 256) ? 256 : len);
              buffer = SH_ALLOC(len);
              MLOCK(buffer, len);
              (void) sl_strlcpy(buffer, errmsg, len);
              (void) sl_strlcat(buffer, answer, len);
              (void) sl_strlcpy(hash, 
                            sh_util_siggen (skey->session, 
                                        buffer, 
                                        sl_strlen(buffer)), 
                            KEY_LEN+1);
              TPT((0, FIL__, __LINE__, _("msg=<sign %s.>\n"),
                   sh_util_siggen(skey->session, buffer, 
                              sl_strlen(buffer))));               

              (void) sl_strlcpy(buffer, errmsg, len);
              (void) sl_strlcat(buffer, hash,   len);

              flag_err = 
                sh_forward_send_crypt (sockfd, 
#ifdef SH_ENCRYPT
#ifdef SH_ENCRYPT_2
                                 (char)(SH_PROTO_MSG|SH_PROTO_ENC|SH_PROTO_EN2),
#else
                                 (char)(SH_PROTO_MSG|SH_PROTO_ENC),
#endif
#else
                                 (char)(SH_PROTO_MSG),
#endif
                                 _("MESG"),
                                 buffer, 
                                 (unsigned long)(sl_strlen(buffer)+1));
              TPT(( 0, FIL__, __LINE__, 
                  _("msg=<Sent %s, status %d.>\n"), 
                  answer, flag_err));

              /* --- Get confirmation. ---
               */
              if (flag_err == 0)
                {
                  flag_err = (int)
                  sh_forward_receive_crypt (sockfd, 
#ifdef SH_ENCRYPT
#ifdef SH_ENCRYPT_2
                                      (char)(SH_PROTO_MSG|SH_PROTO_ENC|SH_PROTO_EN2|SH_PROTO_END),
#else
                                      (char)(SH_PROTO_MSG|SH_PROTO_ENC|SH_PROTO_END),
#endif
#else
                                      (char)(SH_PROTO_MSG|SH_PROTO_END),
#endif
                                      head_u, 
                                      answer, 255);   
                  TPT(( 0, FIL__, __LINE__, 
                      _("msg=<Rcvt %s, u %s, status %d.>\n"), 
                      answer, hu_trans(head_u), flag_err));
                  flag_err = (flag_err < 0) ? flag_err : 0;
                }


              /* --- Check confirmation. ---
               */
              if (flag_err == 0)
                {
                  /*   CLIENT CONF RECV
                   * 
                   *   first KEY_LEN bytes must be
                   *   sig(skey->session (errmsg nsrv))
                   *
                   */
                  (void) sl_strlcpy(buffer, errmsg, len);
                  (void) sl_strlcat(buffer, nsrv,   len);
                  flag_err = sl_strncmp(answer,
                                  sh_util_siggen(skey->session, 
                                             buffer,
                                             sl_strlen(buffer)),
                                  KEY_LEN);
                  TPT((0, FIL__, __LINE__, _("msg=<sign %s.>\n"),
                     sh_util_siggen(skey->session, buffer, 
                                sl_strlen(buffer))));

                  if (flag_err != 0)
                  {
#ifdef ENOMSG
                    flag_err = ENOMSG;
#else
                    flag_err = EIO;
#endif
                    sh_error_handle((-1), FIL__, __LINE__, flag_err,
                                MSG_TCP_NOCONF);
                  }
                  else
                  {
#ifdef SH_ENCRYPT_2
                    /* --- SERVER CMD --- */
                    if (answer[KEY_LEN] != '\0' && 
                        sl_strlen(answer) > (2*KEY_LEN))
                      {
                        pos = sl_strlen(answer) - (2*KEY_LEN);
                        /*
                         * buffer is  >= 256
                         * answer has <= 255 bytes
                         */
                        (void) sl_strlcpy(buffer, &answer[KEY_LEN], 
                                    pos+1);
                        flag_err = 
                        sl_strncmp(&answer[KEY_LEN+pos],
                                 sh_util_siggen(skey->session, 
                                            buffer,
                                            pos),
                                 KEY_LEN);
                        
                        TPT((0, FIL__, __LINE__, 
                           _("CONF RECV <%d> <%s>\n"),
                           flag_err, &answer[KEY_LEN]));
                        
                        if (flag_err != 0) {
                        sh_error_handle((-1), FIL__, __LINE__, 
                                    flag_err,
                                    MSG_TCP_NOCONF);
                        } else {
                        sh_socket_server_cmd(buffer);
                        }
                        flag_err = 0;

                      } else {
                        
                        TPT((0, FIL__, __LINE__, 
                           _("CONF RECV <0> <[null]>\n")));
                        
                      }
                    /* --- SERVER CMD END --- */
#endif
                    sh_error_handle((-1), FIL__, __LINE__, 0,
                                MSG_TCP_CONF);
                  }
                }

              memset(buffer, 0, len);
              MUNLOCK(buffer, len);
              SH_FREE(buffer);
            }
            else
            {
              /* --- Unexpected reply from server. ---
               */
              sh_error_handle((-1), FIL__, __LINE__, 0,
                          MSG_TCP_UNEXP);
              flag_err = (-1);
            }
          }
      }
    }


  else if (what == (char)SH_PROTO_BIG)
    {
      theProto = (unsigned char) SH_PROTO_BIG;

      /* --- Say HELO  ---       
       */
      flag_err = sh_forward_send    (sockfd, (char) theProto, _("HELO"),
                             answer, (unsigned long)sl_strlen(answer));
      TPT(( 0, FIL__, __LINE__, _("msg=<Sent %s, status %d.>\n"), 
          answer, flag_err));

      if (flag_err == 0)
      { 
        /* --- Get NSRV. ---  
         */
        flag_err = (int) sh_forward_receive (sockfd, 
                                     (char)SH_PROTO_BIG, head_u, 
                                     answer, 255);
        TPT(( 0, FIL__, __LINE__, _("msg=<Rcvt %s, u %s, status %d.>\n"), 
          answer, hu_trans(head_u), flag_err));
        flag_err = (flag_err < 0) ? flag_err : 0;
      }   

      if (flag_err == 0)
      {
        
        /* --- Re-negotiate key. ---
         */
        if (0 == check_request_nerr(head_u, _("INIT")))
          {
            flag_err    = 0;
            initialized = BAD;
            goto initBlock;
          }
        
 
        else if (0 == check_request(head_u, _("NSRV")))
          {
#ifdef SH_ENCRYPT
            /* --- Set encryption flag. ---
             */
#ifdef SH_ENCRYPT_2
            theProto = 
            (unsigned char)(SH_PROTO_BIG | SH_PROTO_ENC | SH_PROTO_EN2);
#else
            theProto = (unsigned char)(SH_PROTO_BIG | SH_PROTO_ENC);
#endif
#endif

            (void) sl_strlcpy(nsrv, answer, KEY_LEN+1);
            
            /* --- Generate a nonce. ---
             */
            ticks = (UINT32) taus_get (&(skey->rng0[0]), 
                               &(skey->rng1[0]),
                               &(skey->rng2[0]));
              
            (void) sl_strlcpy(nclt, 
                        sh_tiger_hash((char *) &ticks, 
                                    TIGER_DATA, 
                                    (unsigned long)sizeof(UINT32)),
                        KEY_LEN+1);

            /* --- Compute H(nsrv, nclt, skey). ---
             */
            buffer = sh_util_strconcat (nsrv, nclt, 
                                skey->session, NULL);
            (void)sl_strlcpy(foo_M1, 
                         sh_tiger_hash(buffer, TIGER_DATA,
                                   (unsigned long)sl_strlen(buffer)),
                         KEY_LEN+1);
            memset (buffer, 0, sl_strlen(buffer));

            /* --- Send (nclt, msg) ---
             */
            (void) sl_strlcpy(buffer, nclt, KEY_LEN+1);
            (void) sl_strlcat(buffer, errmsg, KEY_LEN+5);

#ifndef SH_ENCRYPT
            buffer[KEY_LEN+4] = theProto;
            buffer[KEY_LEN+5] = '\0';
            sh_tools_hash_add(foo_M1, buffer, KEY_LEN+5);
#endif

            flag_err = 
            sh_forward_send_crypt (sockfd, (char) theProto, _("NCLT"),
                               buffer, 
                               (unsigned long) sl_strlen(buffer));
 
            TPT(( 0, FIL__, __LINE__, _("msg=<Sent %s, status %d.>\n"), 
                buffer, flag_err));
            SH_FREE (buffer);
          }   
      }

      if (flag_err == 0)
      {
        /* --- Receive the file. ---
         */

          /* --- Open a temporary file. ---
           */
        
          if ( (sfd = open_tmp ()) < 0)
          {
            flag_err = (-1);
            sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_EFIL);
          }
        else
          {
            /* --- Read from socket into tmp file. ---
             */
            transfercount = 0;
            flag_err      = 0;

            do {
            flag_err = (int)
              sh_forward_receive_crypt (sockfd, 
#ifdef SH_ENCRYPT
#ifdef SH_ENCRYPT_2
                                  (char)(SH_PROTO_BIG|SH_PROTO_EN2|SH_PROTO_ENC),
#else
                                  (char)(SH_PROTO_BIG|SH_PROTO_ENC),
#endif
#else
                                  (char)(SH_PROTO_BIG),
#endif
                                  head_u, 
                                  answer, 
                                  TRANS_BYTES + 256);

            TPT(( 0, FIL__, __LINE__, 
                  _("msg=<Received: %d bytes, marked %s.>\n"),
                  flag_err, hu_trans(head_u)));

            if (flag_err > 0 && 0 == check_request_nerr(head_u, _("FILE")))
              {
                if (0 == hash_check (foo_M1, answer, flag_err))
                  {
                  (void) sl_write(sfd, &answer[KEY_LEN], 
                              flag_err-KEY_LEN);
                  ++transfercount;
                  flag_err = 
                    sh_forward_send_crypt (sockfd, (char) theProto, 
                                     _("RECV"),
                                     nclt, 
                                     (unsigned long)sl_strlen(nclt));

                  }
                else
                  {
                  TPT(( 0, FIL__, __LINE__, 
                        _("msg=<File transfer: Hash check failed.>\n")));
                  break;
                  }
              }
            else
              {
                TPT(( 0, FIL__, __LINE__, 
                    _("msg=<File transfer: No more data.>\n")));
                break;
              }
            } while (transfercount < 32000); /* 64 Mbyte */
                
            if (0 == check_request_nerr(head_u, _("EEOT")) &&
              0 <  flag_err                             &&
              0 == hash_check (foo_M1, answer, (int)strlen(answer)))
            {
              flag_err = 
                sh_forward_send_crypt (sockfd, (char) theProto, 
                                 _("EOTE"),
                                 nclt, 
                                 (unsigned int) sl_strlen(nclt));

              (void) rewind_tmp (sfd);
              (void) sl_sync(sfd);
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_FOK);
            }
            else
            {
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_FBAD);
              (void) sl_close (sfd);
              sfd = (-1);
            }

            (void) close (sockfd);
            memset(answer, 0, TRANS_BYTES + 256);
            MUNLOCK(answer, TRANS_BYTES + 256);
            SH_FREE(answer);
            timeout_val = 1;

            SL_RETURN( (sfd), _("sh_forward_try_impl"));
          }
      }

      (void) close (sockfd);
      memset(answer, 0, TRANS_BYTES + 256);
      MUNLOCK(answer, TRANS_BYTES + 256);
      SH_FREE(answer);
      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_FBAD);
      timeout_val *= 2;

      SL_RETURN( (-1), _("sh_forward_try_impl"));
    }
              
      
 
  (void) close (sockfd);
  memset(answer, 0, 512);
  MUNLOCK(answer, 512);
  SH_FREE(answer);

#ifndef EIO
#define EIO 5
#endif
  

#ifdef SH_ERROR_H  
  if (flag_err != 0)
    {
      conn_state = BAD;
      timeout_val *= 2;
      if (flag_err < 0 || NULL == sh_error_message(flag_err))
      flag_err = EIO;
      sh_error_handle((-1), FIL__, __LINE__, flag_err, MSG_TCP_ECONN,
                  sh_error_message(flag_err));
      SL_RETURN( (-1), _("sh_forward_try_impl"));
    }
#endif
  timeout_val = 1;

  SL_RETURN( (0), _("sh_forward_try_impl"));
}

/* #ifdef SH_WITH_CLIENT */
#endif


#if defined (SH_WITH_SERVER)

#ifndef USE_SRP_PROTOCOL

int sh_forward_make_client (char * str)
{
  /* char *          safer; */
  char            key[KEY_LEN+1];
  unsigned char   in[PW_LEN+1];
  int    i = 0, j, k, l = 0;
  
  if (sl_strlen(str) != (PW_LEN * 2)) 
    {
      fprintf(stderr, 
            _("Input must be a %d digit hexadecimal number"\
            " (only 0-9, a-f, A-F allowed in input)\n"),
            (PW_LEN * 2));
      _exit(EXIT_FAILURE);
    }
  
  while (i < (PW_LEN * 2))
    {
      k = sh_util_hexchar(str[i]); j = sh_util_hexchar(str[i+1]); 
      if (k != -1 && j != -1) 
        {
          in[l] = (k * 16 + j);
          ++l; i+= 2;
        }
      else
        {
          fprintf(stderr, _("Invalid char %c\n"), str[i]);
          _exit(EXIT_FAILURE);
        }
    }
  in[PW_LEN] = '\0';

  sl_strlcpy ((char *)key, 
            sh_tiger_hash ((char*)in, TIGER_DATA, PW_LEN), 
            KEY_LEN+1);
  key[KEY_LEN] = '\0';
  
  fprintf(stdout, _("Client entry: Client=HOSTNAME@00000000@%s\n"), 
        key);
  fflush(stdout);

  _exit(EXIT_SUCCESS);
  return 0;
}

#else

int sh_forward_make_client (char * str)
{
  char * foo_v;

  char   salt[17];
  char   key[KEY_LEN+1];
  char   in[PW_LEN];
  int    i = 0, j, k, l = 0;
  
  if (sl_strlen(str) != (PW_LEN*2)) 
    {
      fprintf(stderr, 
            _("Input must be a %d digit hexadecimal number"\
            " (only 0-9, a-f, A-F allowed in input)\n"),
            (PW_LEN*2));
      _exit(EXIT_FAILURE);
    }

    while (i < (PW_LEN*2))
      {
        k = sh_util_hexchar(str[i]); j = sh_util_hexchar(str[i+1]); 
        if (k != -1 && j != -1) 
          {
            in[l] = (k * 16 + j);
            ++l; i+= 2;
          }
        else
          {
            fprintf(stderr, _("Invalid char %c\n"), str[i]);
            _exit(EXIT_FAILURE);
          }
      }
    
  
    if (0 == sh_srp_init())
      {
      sh_util_keyinit(key, KEY_LEN);
      sl_strlcpy(salt, sh_tiger_hash(key, TIGER_DATA, KEY_LEN), 17); 
      sh_srp_x (salt, in);
      foo_v  = sh_srp_verifier ();
      fprintf(stdout, _("Client=HOSTNAME@%s@%s\n"), 
            salt, foo_v);
      fflush(stdout);
      SH_FREE(foo_v);
      sh_srp_exit();
      _exit(EXIT_SUCCESS);
      }
    fprintf(stdout, _("ERROR initializing BigNum library.\n"));
    fflush (stdout);
    _exit(EXIT_FAILURE);
    return -1;
}
#endif


int sh_forward_create_password (char * dummy)
{
  UINT32   val[2]; 
  char     output[KEY_LEN+1];

  val[0] = taus_get (&(skey->rng0[0]), &(skey->rng0[1]), &(skey->rng0[2]));
  val[1] = taus_get (&(skey->rng0[0]), &(skey->rng0[1]), &(skey->rng0[2]));

  sl_strlcpy (output, 
            sh_tiger_hash((char *)(&val[0]), TIGER_DATA, 2*sizeof(UINT32)),
            KEY_LEN);

  output[16] = '\0';

  fprintf(stdout, _("%s\n"), output);
  fflush (stdout);

  if (dummy)
    _exit(EXIT_SUCCESS);
  else
    _exit(EXIT_SUCCESS);  
  return (0);  /* avoid compiler warning */
}

/* #if defined (SH_WITH_SERVER) */
#endif

/**************************************************
 *
 *
 *  S E R V E R   
 *
 *
 ***************************************************/

#ifdef SH_WITH_SERVER

#include "sh_readconf.h"


#define CONN_FREE    0
#define CONN_READING 1
#define CONN_SENDING 2
#define CONN_PAUSE   3
#define CONN_BUSY    4

char * clt_stat[] = {
  N_("Inactive"),
  N_("Started"),
  N_("ILLEGAL"),
  N_("FAILED"),
  N_("Exited"),
  N_("PANIC"),
  N_("POLICY"),
  N_("File_transfer"),
  N_("Message"),
  N_("TIMEOUT_EXCEEDED"),
  N_("Suspended"),
  N_("Filecheck"),
};

#include <time.h>

/* in sh_html.h:
 *  typedef struct client_entry {
 *  } client_t;
 */

#include "zAVLTree.h"

/* Function to return the argument */
zAVLKey sh_avl_key (void const * arg)
{
  const client_t * sa = (const client_t *) arg;
  return (zAVLKey) sa->hostname;
}

zAVLTree * all_clients = NULL;

void sh_forward_html_write()
{
  SL_ENTER(_("sh_forward_html_write"));
  sh_html_write(all_clients);
  SL_RET0(_("sh_forward_html_write"));
}


int sh_forward_use_clt_class (char * c)
{
  int i;
  SL_ENTER(_("sh_forward_use_clt_class"));
  i = sh_util_flagval(c, &(sh.flag.client_class));
  SL_RETURN(i, _("sh_forward_use_clt_class"));
}

int sh_forward_use_clt_sev (char * c)
{
  int i;
  SL_ENTER(_("sh_forward_use_clt_sev"));
  i = sh_util_flagval(c, &(sh.flag.client_severity));
  SL_RETURN(i, _("sh_forward_use_clt_sev"));  
}


/* the destructor
 */
void free_client(void * inptr)
{
  client_t * here;

  SL_ENTER(_("free_client"));
  if (inptr == NULL)
    SL_RET0(_("free_client"));
  else
    here = (client_t *) inptr;

  if (here->hostname != NULL)
    SH_FREE(here->hostname);
  if (here->salt != NULL)
    SH_FREE(here->salt);
  if (here->verifier != NULL)
    SH_FREE(here->verifier);
  SH_FREE(here);
  SL_RET0(_("free_client"));
}

int sh_forward_register_client (char * str)
{
  client_t   * newclt;
  client_t   * testclt;

  char       * ptr;
  int          sepnum = 0;
  int          sep[2];
  register int i = 0;
  int          siz_str = 0;

  SL_ENTER(_("sh_forward_register_client"));

  ptr = str; 
  while (*ptr) {
    if (*ptr == '@' && sepnum < 2 ) 
      { 
      sep[sepnum] = i;
      ++sepnum;
      } 
    ++ptr; ++i; 
  }

  if (all_clients == NULL)
    all_clients = zAVLAllocTree (sh_avl_key);

  
  if (sepnum == 2 && sep[0] > 0)
    {
      newclt = SH_ALLOC (sizeof(client_t));
      newclt->hostname = SH_ALLOC (sep[0]+1);
      newclt->salt     = SH_ALLOC (sep[1]-sep[0]);
      newclt->verifier = SH_ALLOC (sl_strlen(str)-sep[1]+1);
      newclt->exit_flag         = 0;
      newclt->dead_flag         = 0;
#ifdef SH_ENCRYPT
#ifdef SH_ENCRYPT_2
      newclt->encf_flag         = SH_PROTO_ENC|SH_PROTO_EN2;
      newclt->ency_flag         = SH_PROTO_ENC|SH_PROTO_EN2;
#else
      newclt->encf_flag         = SH_PROTO_ENC;
      newclt->ency_flag         = SH_PROTO_ENC;
#endif
#else
      newclt->encf_flag         = 0;
      newclt->ency_flag         = 0;
#endif
      newclt->session_key[0]    = '\0';
      newclt->last_connect      = (time_t) 0;
      newclt->session_key_timer = (time_t) 0;
      newclt->status_now        = CLT_INACTIVE;
      for (i = 0; i < CLT_MAX; ++i) 
      newclt->status_arr[i] = CLT_INACTIVE;
      sl_strlcpy(newclt->timestamp[CLT_INACTIVE],   sh_unix_time(0), TIM_MAX);
      sl_strlcpy(newclt->hostname,  &str[0],        sep[0]+1);
      sl_strlcpy(newclt->salt,      &str[sep[0]+1], sep[1]-sep[0]);
      sl_strlcpy(newclt->verifier,  &str[sep[1]+1], sl_strlen(str)-sep[1]+1);

      testclt = (client_t *) zAVLSearch (all_clients, newclt->hostname);

      if (testclt != NULL)
      {
        SH_FREE(testclt->verifier);
        siz_str = strlen (newclt->verifier) + 1;
        testclt->verifier = SH_ALLOC (siz_str);
        sl_strlcpy(testclt->verifier, newclt->verifier, siz_str);

        SH_FREE(testclt->salt);
        siz_str = strlen (newclt->salt) + 1;
        testclt->salt = SH_ALLOC (siz_str);
        sl_strlcpy(testclt->salt, newclt->salt, siz_str);

        testclt->dead_flag = 0;
            
        free_client(newclt);
        SL_RETURN( 0, _("sh_forward_register_client"));
      }
      else
      {
        if (0 == zAVLInsert (all_clients, newclt))
          {
            sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_CREG,
                        newclt->hostname, 
                        newclt->salt, newclt->verifier);
            SL_RETURN( 0, _("sh_forward_register_client"));
          }
      }
    }
  SL_RETURN (-1, _("sh_forward_register_client"));
}

typedef struct {
  int             state;
  int             fd;
  char          * buf;
  unsigned char   head[SH_HEADER_SIZE];
  char            challenge[SH_CHALLENGE_SIZE];
  char            peer[SH_MINIBUF+1];
  client_t      * client_entry;
  char          * K;
  char          * M1;
  char          * A;
  int             headcount;
  unsigned long   bytecount;
  unsigned long   bytes_to_send;
  unsigned long   bytes_to_get;
  int             pass;
  unsigned long   timer;

  char          * FileName;
  unsigned long   FileLength;
  unsigned long   FileSent;
  char            FileType[5];

  struct sockaddr_in addr_peer;
} sh_conn_t;



void sh_forward_do_free (sh_conn_t * conn)
{
  SL_ENTER(_("sh_forward_do_free"));

  if (conn->K != NULL) 
    {
      SH_FREE(conn->K);
      conn->K           = NULL;
    }
  if (conn->A != NULL) 
    {
      SH_FREE(conn->A);
      conn->A           = NULL;
    }
  if (conn->M1 != NULL) 
    {
      SH_FREE(conn->M1);
      conn->M1           = NULL;
    }
  if (conn->buf != NULL) 
    {
      SH_FREE(conn->buf);
      conn->buf          = NULL;
    }
  if (conn->fd != (-1))
    {
      close (conn->fd);
      conn->fd            = -1;
    }
  memset(conn->challenge, '\0', SH_CHALLENGE_SIZE);
  conn->state         = CONN_FREE;
  conn->headcount     = 0;
  conn->bytecount     = 0;
  conn->bytes_to_send = 0;
  conn->bytes_to_get  = 0;
  conn->pass          = 0;
  conn->timer         = 0;
  conn->client_entry  = NULL;

  if (conn->FileName != NULL) 
    {
      SH_FREE(conn->FileName);
      conn->FileName     = NULL;
    }
  conn->FileLength     = 0;
  conn->FileSent       = 0;
  memset(conn->FileType, '\0', 5);

  --server_status.conn_open;
  
  SL_RET0(_("sh_forward_do_free"));
}

/****************************************
 *
 *   -- Reconfiguration. --
 *
 *   (1) Mark all clients as 'dead'.
 *   (2) Reload configuration - clients
 *       in config are non-dead now.
 *   (3) Remove all clients still
 *       marked as 'dead'.
 */

/* -- Mark all clients as dead.
 */
void sh_forward_mark_dead ()
{
  zAVLCursor avlcursor;
  client_t * item;

  SL_ENTER(_("sh_forward_mark_dead"));

  for (item = (client_t *) zAVLFirst(&avlcursor, all_clients); item;
       item = (client_t *) zAVLNext(&avlcursor))
    {
      item->dead_flag = 1;
    }
  SL_RET0(_("sh_forward_mark_dead"));
}


/* -- Clean tree from dead clients.
 */
void sh_forward_clean_tree ()
{
  zAVLCursor avlcursor;
  client_t * item;

  SL_ENTER(_("sh_forward_clean_tree"));

 repeat_search:

  for (item = (client_t *) zAVLFirst(&avlcursor, all_clients); item;
       item = (client_t *) zAVLNext(&avlcursor))
    {
      if (item->dead_flag == 1)
      {
        zAVLDelete (all_clients, item->hostname);
        free_client (item);
        goto repeat_search;
      }
    }
  SL_RET0(_("sh_forward_clean_tree"));
}

/*
 *
 **********************************************/



/* -- SERVER SEND FUNKTION. --
 */
void sh_forward_prep_send_int (sh_conn_t * conn, 
                         char * msg, unsigned long length,
                         char * u, char protocol,
                         int docrypt)
{
  register unsigned long i;
  unsigned long           length2;

#ifdef SH_ENCRYPT
  unsigned long           blkfac = 0;
  int                     rem = 0;
  char                  * p, * q;
  RIJ_BYTE                    inBlock[B_SIZ]; 
  RIJ_BYTE                    outBlock[B_SIZ];
  unsigned int            j;
  cipherInstance          cipherInst;
  int                     err_num;
#else
  (void) docrypt;
#endif

  SL_ENTER(_("sh_forward_prep_send_int"));

  TPT((0, FIL__, __LINE__, _("msg=<%s>, docrypt=<%d>\n"), msg, docrypt ));

#ifdef SH_ENCRYPT
  if      ((S_TRUE == docrypt) && ((protocol & SH_PROTO_EN2) != 0) )
    {
      length2 = length;
    }
  else if ((S_TRUE == docrypt) && ((protocol & SH_PROTO_ENC) != 0) )
    {
      blkfac  = length/B_SIZ;
      rem     = length - (B_SIZ * blkfac);
      length2 = (B_SIZ * blkfac) + ((rem == 0) ? 0 : B_SIZ);
    }
  else
    {
      length2 = length;
    }
#else
  length2 = length;
#endif

  conn->headcount     = 0;
  conn->bytecount     = 0;
  conn->bytes_to_send = 0;
  conn->bytes_to_get  = 0;

  if (conn->buf != NULL) 
    {
      SH_FREE(conn->buf);
      conn->buf           = NULL;
    }


  put_header (conn->head, protocol, &length2, u);
  SH_SHOWPROT(conn->head,'>');

  TPT((0, FIL__, __LINE__, _("msg=<put_header done>\n") ));

  if (msg == NULL) 
    length2 = 0;
  
#ifdef SH_ENCRYPT
  if      ((S_TRUE == docrypt) && ((protocol & SH_PROTO_EN2) != 0))
    {
      TPT((0, FIL__, __LINE__, _("encrypting (version 2)\n")));
      
      conn->buf = sh_tools_makePack (conn->head, msg, length2,
                             &(conn->client_entry->keyInstE));
    }
  else if ((S_TRUE == docrypt) && ((protocol & SH_PROTO_ENC) != 0))
    {
      conn->buf       = SH_ALLOC(length2 + 1);

      p       = msg;
      q       = conn->buf;

      TPT((0, FIL__, __LINE__, _("encrypting (version 1)\n")));

      err_num = cipherInit (&cipherInst, MODE_CBC, NULL);
      if (err_num < 0)
      sh_error_handle((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN,
                  errorExplain(err_num), 
                  _("sh_forward_prep_send_int: cipherInit"));

      for (j = 0; j < blkfac; ++j)
      {
        memcpy(inBlock, p, B_SIZ);
        err_num = blockEncrypt(&cipherInst, &(conn->client_entry->keyInstE), 
                   inBlock, 128 * BNUM, outBlock);
        if (err_num < 0)
          sh_error_handle((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN,
                      errorExplain(err_num), 
                      _("sh_forward_prep_send_int: blockEncrypt"));
        memcpy(q, outBlock, B_SIZ);
        p += B_SIZ;
        q += B_SIZ;
      }
      if (rem > 0)
      {
        /* incomplete block at end
         */
        memset(inBlock, '\0', B_SIZ);
        memcpy(inBlock, p, rem);
        err_num = blockEncrypt(&cipherInst, &(conn->client_entry->keyInstE), 
                   inBlock, 128 * BNUM, outBlock);
        if (err_num < 0)
          sh_error_handle((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN,
                      errorExplain(err_num), 
                      _("sh_forward_prep_send_int: blockEncrypt"));
        memcpy(q, outBlock, B_SIZ);
        q += B_SIZ;
      }

      TPT((0, FIL__, __LINE__, _("msg=<encryption done>\n") ));
    }
  else
    {
      conn->buf       = SH_ALLOC(length2 + 1);

      for (i = 0; i < length2; ++i) 
      conn->buf[i] = msg[i];
      conn->buf[length2] = '\0';
      TPT((0, FIL__, __LINE__, _("msg=<no encryption done>\n") ));
    }
#else
  conn->buf       = SH_ALLOC(length2 + 1);

  for (i = 0; i < length; ++i) 
    conn->buf[i] = msg[i];
  conn->buf[length2] = '\0';
  TPT((0, FIL__, __LINE__, _("msg=<no encryption done>\n") ));
#endif

  conn->state     = CONN_SENDING;
  SL_RET0(_("sh_forward_prep_send_int"));
}

/* -- Send/Receive. --
 */
void sh_forward_prep_send (sh_conn_t * conn, 
                     char * msg, unsigned long length,
                     char * u, char protocol)
{
  SL_ENTER(_("sh_forward_prep_send"));
  sh_forward_prep_send_int (conn,  msg, length, u, protocol, S_FALSE);
  SL_RET0(_("sh_forward_prep_send"));
}  

void sh_forward_prep_send_crypt (sh_conn_t * conn, 
                         char * msg, unsigned long length,
                         char * u, char protocol)
{
  SL_ENTER(_("sh_forward_prep_send_crypt"));
  sh_forward_prep_send_int (conn,  msg, length, u, protocol, S_TRUE);
  SL_RET0(_("sh_forward_prep_send_crypt"));
}  

/* #include <sys/times.h> */

#if defined(WITH_EXTERNAL)
#include "sh_extern.h"
#endif

/* -- Update the client status. --
 *
 * Update the status array for the client,
 * and eventually call external program.
 */
static void status_update (client_t * conn, int status)
{ 
#if defined(WITH_EXTERNAL)
  char msg[2 * SH_MINIBUF + TIM_MAX + 3];
#endif

  SL_ENTER(_("status_update"));

  if (conn == NULL || 
      status < 0   || status >= CLT_MAX)
    SL_RET0(_("status_update"));

  conn->status_now = status;
  conn->status_arr[status] = status;
  sl_strlcpy(conn->timestamp[status],
           sh_unix_time(0), 
           TIM_MAX);

#if defined(WITH_EXTERNAL)
  sprintf(msg, _("%s %s %s"),                       /* known to fit  */
        conn->hostname, 
        conn->timestamp[status],
        _(clt_stat[status]));
  sh_ext_execute('s', 'r', 'v', msg, 0);
#endif

  SL_RET0(_("status_update"));
}

static time_t time_client_limit = 86400;

int sh_forward_set_time_limit (char * c)
{
  long val;

  SL_ENTER(_("sh_forward_set_time_limit"));

  val = strtol (c, (char **)NULL, 10);
  if (val <= 0)
    SL_RETURN( (-1), _("sh_forward_set_time_limit"));

  val = (val < 0 ? 0 : val);

  time_client_limit = (time_t) val;
  SL_RETURN( (0), _("sh_forward_set_time_limit"));
}


/* -- Check for time limit exceeded. --
 */
static int client_time_check()
{
  zAVLCursor avlcursor;
  client_t * item;

  SL_ENTER(_("client_time_check"));

  if (time_client_limit == (time_t) 0)
    SL_RETURN( 0, _("client_time_check"));

  for (item = (client_t *) zAVLFirst(&avlcursor, all_clients); item;
       item = (client_t *) zAVLNext(&avlcursor))
    {
      if (item->exit_flag == 0 && item->last_connect != (time_t) 0)
      {
        if ( (time(NULL) - item->last_connect) > time_client_limit)
          {
            if (item->status_now != CLT_TOOLONG)
            {
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_TIMEXC,
                          item->hostname);
              status_update (item, CLT_TOOLONG);
            }
          }
      }
    }
  SL_RETURN( 0, _("client_time_check"));
}

static int lookup_err = SH_ERR_SEVERE;

int sh_forward_lookup_level (char * c)
{
  int ci =  sh_error_convert_level (c);

  SL_ENTER(_("sh_forward_lookup_level"));

  if (ci >= 0)
    {
      lookup_err = ci;
      SL_RETURN( 0, _("sh_forward_lookup_level"));
    }
  else
    SL_RETURN( (-1), _("sh_forward_lookup_level"));
}

#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN  127
#endif

int check_addr (const char * claim, struct sockaddr_in addr_peer)
{
  char               h_name[MAXHOSTNAMELEN + 1];
  char               h_peer[MAXHOSTNAMELEN + 1];
  char               h_peer_IP[16];
  char               tmp_peer_IP[16];
  struct hostent   * he;
  char            ** p = NULL;
  int                i;
  int                flag = 0;

  SL_ENTER(_("check_addr"));

  if (claim == NULL)
    {
      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN,
                  _("NULL input"), _("check_addr"));
      SL_RETURN ((-1), _("check_addr"));
    }

  /* Make sure we have the canonical name for the client
   */
  he = sh_gethostbyname (claim);

  if (he != NULL && he->h_name != NULL)
    {
      if (NULL == strchr(he->h_name, '.') && he->h_addr_list != NULL)
      {
        he = sh_gethostbyaddr(he->h_addr_list[0], 
                        he->h_length, 
                        he->h_addrtype);
      }
    }

  /* copy canonical name into h_name
   */
  if (he != NULL && he->h_name != NULL)
    sl_strlcpy(h_name, he->h_name, MAXHOSTNAMELEN + 1);
  else
    {
      sh_error_handle(lookup_err, FIL__, __LINE__, 0, MSG_TCP_RESCLT,
                  claim);
      SL_RETURN ((0), _("check_addr"));
    }


  /* get canonical name of socket peer
   */
  he = sh_gethostbyaddr ((char *) &(addr_peer.sin_addr), 
                   sizeof(addr_peer.sin_addr),
                   AF_INET);

  if (he != NULL && he->h_name != NULL)
    {
      if (0 == sl_strcmp(he->h_name, _("localhost")))
      sl_strlcpy(h_peer, sh.host.name, MAXHOSTNAMELEN + 1);
      else
      sl_strlcpy(h_peer, he->h_name, MAXHOSTNAMELEN + 1);
    }
  else
    {
      sl_strlcpy(tmp_peer_IP,
             inet_ntoa (*(struct in_addr *) &(addr_peer.sin_addr)),
             16);
      sh_error_handle(lookup_err, FIL__, __LINE__, 0, MSG_TCP_RESPEER,
                  claim, tmp_peer_IP);
      SL_RETURN ((0), _("check_addr"));
    }

  sl_strlcpy(h_peer_IP, 
           inet_ntoa (*(struct in_addr *) he->h_addr),
           16);

#if 0
  if (S_FALSE == DoReverseLookup)
    {
      SL_RETURN ((0), _("check_addr"));
    }
#endif

  /* reverse lookup
   */
  if (0 != sl_strncmp(_("127."), 
                  inet_ntoa (*(struct in_addr *) &(addr_peer.sin_addr)),
                  4))
    {
      he = sh_gethostbyname(h_peer);
      
      if (he != NULL)
      {
        for (p = he->h_addr_list; *p; ++p)
          {
            if (0 == memcmp (*p, &(addr_peer.sin_addr), 
                         sizeof(addr_peer.sin_addr))) 
            break;
            ++i;
          }
      }
      if (he == NULL || *p == NULL) 
      {
        sl_strlcpy(tmp_peer_IP,
                 inet_ntoa (*(struct in_addr *) &(addr_peer.sin_addr)),
                 16);
        sh_error_handle(lookup_err, FIL__, __LINE__, 0, MSG_TCP_LOOKERS,
                    claim, h_peer, tmp_peer_IP);
        SL_RETURN ((0), _("check_addr"));
      }
    }


  if ((0 == sl_strcmp(h_peer, h_name)) || (0 == sl_strcmp(h_peer_IP, h_name)))
    {
      SL_RETURN ((0), _("check_addr"));
    }
  else
    {
      i = 0;
      while (he->h_aliases[i] != NULL)
      {
        if (0 == sl_strcmp(he->h_aliases[i], h_name))
          {
            flag = 1;
            break;
          }
        ++i;
      }
      if (flag == 0) 
      sh_error_handle(lookup_err, FIL__, __LINE__, 0, MSG_TCP_LOOKUP,
                  claim, h_peer);
    }

  SL_RETURN ((0), _("check_addr"));
}

static int UseSocketPeer = S_FALSE;

int set_socket_peer (char * c)
{
  return sh_util_flagval(c, &UseSocketPeer);
}


/* -- Search register. --
 */
client_t * search_register(sh_conn_t * conn, int pos)
{
  client_t * this_client;
  char       peer_ip[16];
  char       peer_name[MAXHOSTNAMELEN+1];
  char     * search_string;
  struct sockaddr_in peer_addr;
  struct hostent   * he;
  char    ** p = NULL;

  SL_ENTER(_("search_register"));

  if (UseSocketPeer == S_TRUE)
    {
      peer_addr = conn->addr_peer;
      sl_strlcpy(peer_ip, 
             inet_ntoa (*(struct in_addr *) &(peer_addr.sin_addr)), 16);

      /* get canonical name of socket peer
       */
      he = sh_gethostbyaddr ((char *) &(peer_addr.sin_addr), 
                       sizeof(peer_addr.sin_addr),
                       AF_INET);

      if (he != NULL && he->h_name != NULL)
      {
        if (0 == sl_strcmp(he->h_name, _("localhost")))
          sl_strlcpy(peer_name, sh.host.name, MAXHOSTNAMELEN + 1);
        else
          sl_strlcpy(peer_name, he->h_name, MAXHOSTNAMELEN + 1);

        /* Reverse lookup 
         */
        if (0 != sl_strncmp(peer_ip, _("127."), 4))
          {
            he = sh_gethostbyname(peer_name);
            
            if (he != NULL)
            {
            for (p = he->h_addr_list; *p; ++p)
              {
                if (0 == memcmp (*p, &(peer_addr.sin_addr), 
                             sizeof(peer_addr.sin_addr))) 
                  break;
              }
            }
            if (he == NULL || *p == NULL) 
            {
            /*
            sh_error_handle(lookup_err, FIL__, __LINE__, 0, 
                        MSG_TCP_LOOKERS,
                        conn->buf[pos], peer_name, peer_ip);
            */
            sl_strlcpy(peer_name, peer_ip, MAXHOSTNAMELEN + 1);
            }
          }
      }
      else
      {
        sl_strlcpy(peer_name, peer_ip, MAXHOSTNAMELEN + 1);
      }
      search_string = peer_name;
    }
  else
    {
      search_string = &(conn->buf[pos]);

      if (0 != check_addr (search_string, conn->addr_peer))
      {
        sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_BADCONN,
                    _("Reverse lookup failed"), search_string);
        sh_forward_do_free (conn);
        SL_RETURN( NULL, _("search_register"));
      } 
    }

  /* ----  search the register  -----
   */
  this_client = zAVLSearch(all_clients, search_string);

  if (this_client == NULL)
    {
      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_BADCONN,
                  _("Not in client list"), search_string);
      sh_forward_do_free (conn);
      SL_RETURN( NULL, _("search_register"));
    } 
  if (this_client->exit_flag == 1)
    {
      TPT((0, FIL__, __LINE__, _("msg=<this_client->exit_flag == 1>\n")));
      this_client->session_key_timer = (time_t) 0;
      this_client->session_key[0]    = '\0';
      this_client->exit_flag         = 0;    
    }
  TPT((0, FIL__, __LINE__, _("msg=<search_register: client %s>\n"), 
       this_client->hostname));
  TPT((0, FIL__, __LINE__, _("msg=<search_register: key %s>\n"), 
       this_client->session_key));
  SL_RETURN( this_client, _("search_register"));
}  


/************************************************************************
 *
 * Here we check the message received, and decide on the answer to send
 * (if any). The connection is in CONN_PAUSED state, thus we must:
 * (i)   define the proper reaction
 * (ii)  reset to CONN_READING or CONN_WRITING or CONN_FREE
 * (iii) eventually reset the connection entry
 *
 *************************************************************************/
static
void check_protocol(sh_conn_t * conn, int state)
{
  client_t * this_client; 

  char     * cmd;

  char       hash[SH_MAXMSGLEN + KEY_LEN + KEY_LEN + 1];
  char     * buffer;
  long       len;

  int        clt_sev;
  char     * ptok;

  UINT32     ticks;
  int        i;
  char     * test;
  char       u[5] = "OOOO";

  SL_TICKET  sfd = -1;
  char     * read_buf = 0;
  char     * send_buf;
  int        bytes;

#ifdef SH_ENCRYPT
  int        blkfac;
  int        rem;
  int        send_bytes;
  int        err_num;
#endif


#ifdef USE_SRP_PROTOCOL
  char     * foo_B;
  char     * foo_Ss;
#endif

  SL_ENTER(_("check_protocol"));

  /* seed / re-seed the PRNG if required
   */
  (void) taus_seed();


  /* protocols: 
   * -- (iii)    file transfer
   * -- (ii)     authenticated message transfer
   * -- (i)      SRP key exchange
   */

  /* --------- FILE TRANSFER  -----------
   */
  if ( (conn->head[0] & SH_PROTO_SRP) == 0  &&
       (conn->head[0] & SH_PROTO_BIG) != 0  /* is set */ )
    {

      if (state == SH_DO_READ)        /* finished reading */
      {
        TPT(( 0, FIL__, __LINE__, _("msg=<File transfer - entry.>\n")));

        /* -- Client requests challenge. --
         */
        if (0 == check_request_nerr ((char *) &(conn->head[3]), _("HELO")))
          {

            TPT(( 0, FIL__, __LINE__, 
                _("msg=<File transfer - HELO (1).>\n")));

            if (conn->buf == NULL || sl_strlen(conn->buf) <= KEY_LEN)
            {
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_NOCLT);
              sh_forward_do_free (conn);
              SL_RET0(_("check_protocol"));
            } 

            /* ----  search the register  -----
             */
            
            this_client = search_register (conn, KEY_LEN);
            if (this_client == NULL)
            SL_RET0(_("check_protocol"));

            /* ---- force authentication -----
             */

            if (this_client->session_key[0] == '\0' ||
              (time(NULL) - this_client->session_key_timer) 
              > (time_t) TIMEOUT_KEY )
            {
              /* fake an auth request and jump there
               */
              conn->head[0]  = (conn->head[0] | SH_PROTO_SRP);
              conn->head[3]  = 'S';
              conn->head[4]  = 'A';
              conn->head[5]  = 'L';
              conn->head[6]  = 'T';
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_FAUTH,
                          &(conn->buf[KEY_LEN]));
              strcpy(conn->buf,                      /* known to fit  */
                   &(conn->buf[KEY_LEN])); 
              this_client->session_key[0]    = '\0';
              this_client->session_key_timer = (time_t) 1;
              goto servInit;
            }

            /* --- check whether hostname is properly signed ---
             */ 
            if (conn->K != NULL) 
            {
              SH_FREE(conn->K);
              conn->K = NULL;
            }
            i = sl_strlen(&(conn->buf[KEY_LEN])) + KEY_LEN + 1;
            conn->K = SH_ALLOC(i);

            sl_strlcpy (conn->K, 
                    sh_util_siggen(this_client->session_key,
                               &(conn->buf[KEY_LEN]),
                               sl_strlen(&(conn->buf[KEY_LEN]))),
                    KEY_LEN+1);
            TPT((0, FIL__, __LINE__, _("msg=<host %s>\n"),
               &(conn->buf[KEY_LEN])));
            TPT((0, FIL__, __LINE__, _("msg=<ckey %s>\n"),
               this_client->session_key));
            TPT((0, FIL__, __LINE__, _("msg=<sign %s.>\n"),
               sh_util_siggen(this_client->session_key, 
                          &(conn->buf[KEY_LEN]), 
                          sl_strlen(&(conn->buf[KEY_LEN])))));

            if (0 != sl_strncmp(conn->K, conn->buf, KEY_LEN))
            {
              TPT((0, FIL__, __LINE__, _("msg=<clt %s>\n"), conn->buf));
              TPT((0, FIL__, __LINE__, _("msg=<srv %s>\n"), conn->K));
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_BADCONN,
                          _("Signature mismatch"), 
                          &(conn->buf[KEY_LEN]));

              this_client->session_key_timer =
                time(NULL) - (2*TIMEOUT_KEY);

              sh_forward_do_free (conn);
              SL_RET0(_("check_protocol"));
            }
            SH_FREE(conn->K); 
            conn->K = NULL;

            /* --- create and send a nonce ---
             */
            
            conn->client_entry = this_client;
            sl_strlcpy (conn->peer, &(conn->buf[KEY_LEN]), SH_MINIBUF+1);

            ticks = (UINT32) taus_get (&(skey->rng0[0]), 
                               &(skey->rng1[0]),
                               &(skey->rng2[0]));
            if (conn->K != NULL) 
            {
              SH_FREE(conn->K);
              conn->K = NULL;
            }
            conn->K = SH_ALLOC(KEY_LEN+1);
            sl_strlcpy (conn->K, 
                    sh_tiger_hash ((char *) &ticks, 
                               TIGER_DATA, sizeof(UINT32)), 
                    KEY_LEN+1);

            TPT((0, FIL__, __LINE__, _("msg=<send nonce>\n")));
            sh_forward_prep_send (conn, conn->K, KEY_LEN+1, _("NSRV"), 
                            SH_PROTO_BIG);
          }

        /* --- Client has send a message. Check state and message. ---
         */
        else if (0 == check_request_nerr((char *)&(conn->head[3]), _("NCLT")) &&
               conn->client_entry != NULL                           &&
               sl_strlen(conn->buf) > KEY_LEN                       &&
               conn->K != NULL)
          {

            TPT(( 0, FIL__, __LINE__, 
                _("msg=<File transfer - NCLT (3).>\n")));

            /* --- get client nonce and compute hash ---
             */
            if (conn->A != NULL)
            {
              SH_FREE(conn->A);
              conn->A = NULL;
            }
            conn->A = SH_ALLOC(3*KEY_LEN+1);
            sl_strlcpy (conn->A, conn->K, KEY_LEN+1); 
            sl_strlcat(conn->A, conn->buf, /* ignore remainder */
                   2*KEY_LEN+1);
            sl_strlcat(conn->A, conn->client_entry->session_key, 
                   3*KEY_LEN+1);
            sl_strlcpy (conn->K, sh_tiger_hash(conn->A,TIGER_DATA,3*KEY_LEN),
                    KEY_LEN+1);
            SH_FREE(conn->A); 
            conn->A = NULL;


#ifdef SH_ENCRYPT
            if ((conn->client_entry->encf_flag != 0) &&
              ((conn->head[0] & SH_PROTO_ENC) == 0)) 
            {
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_MISENC,
                          _("file download"),
#ifdef SH_ENCRYPT_2
                          _("version2"),
#else
                          _("version1"),
#endif
                          _("none"));
              if (sl_strlen(conn->buf) > (KEY_LEN + 5)) {
                if (sh_tools_hash_vfy(conn->K, conn->buf, KEY_LEN+5)) {
                  if (conn->buf[KEY_LEN+4] == conn->head[0]) {
                  /* conn->client_entry->encf_flag = 0 */ ; /* FIXME */
                  }
                }
              }
            }
            else if ((conn->client_entry->encf_flag != 0) &&
                   ((conn->head[0] & SH_MASK_ENC) != 
                  conn->client_entry->encf_flag))
            {
              sh_error_handle(SH_ERR_NOTICE, FIL__, __LINE__, 0, 
                          MSG_TCP_MISENC,
                          _("file download"),
#ifdef SH_ENCRYPT_2
                          _("version2"),
#else
                          _("version1"),
#endif
                          ((conn->head[0] & SH_PROTO_EN2) == SH_PROTO_EN2) ? _("version2") : _("version1")
                          );
              conn->client_entry->encf_flag = 
                (conn->head[0] & SH_MASK_ENC);
            }
#else
            if ((conn->head[0] & SH_PROTO_ENC) != 0) 
            {
              sh_error_handle((-1), FIL__, __LINE__, 0, 
                          MSG_TCP_MISENC,
                          _("file download"),
                          _("none"), 
                          ((conn->head[0] & SH_PROTO_EN2) == SH_PROTO_EN2) ? _("version2") : _("version1"));
            }
#endif


            /* ---- K = H(NSRV, NCLT, session_key) -------
             */ 

            if (conn->FileName != NULL)
            {
              SH_FREE(conn->FileName);
              conn->FileName = NULL;
            }

            if (0 == sl_strncmp (_("CONF"), &(conn->buf[KEY_LEN]), 4))
            {
              strcpy(conn->FileType, _("CONF"));     /* known to fit  */
              conn->FileName = get_client_conf_file(conn->peer, 
                                          &(conn->FileLength));
              conn->FileSent = 0;
            }
            else  if (0 == sl_strncmp (_("DATA"), &(conn->buf[KEY_LEN]), 4))
            {
              strcpy(conn->FileType, _("DATA"));     /* known to fit  */
              conn->FileName = get_client_data_file(conn->peer, 
                                          &(conn->FileLength));
              conn->FileSent = 0;
            }
            else
            {
              ptok = sh_util_safe_name(&(conn->buf[KEY_LEN]));
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_FFILE,
                          conn->peer, 
                          ptok);
              SH_FREE(ptok);
              status_update (conn->client_entry, CLT_FAILED);
              sh_forward_do_free (conn);
            }

            bytes = -1;

            if (conn != NULL && conn->FileName != NULL)
            {
              sfd = sl_open_read(conn->FileName, SL_YESPRIV);
              if (!SL_ISERROR(sfd))
                {
                  read_buf = SH_ALLOC(TRANS_BYTES);
                  bytes = sl_read (sfd, read_buf, TRANS_BYTES);
                  sl_close(sfd);
                }
              if (bytes >= 0)
                {
#ifdef SH_ENCRYPT
                  /* need to send N * B_SIZ bytes
                   */ 
                  blkfac = bytes / B_SIZ;
                  rem    = bytes - (blkfac * B_SIZ);
                  if (rem != 0)
                  {
                    memset(&read_buf[bytes], '\n', (B_SIZ-rem));
                    ++blkfac;
                    send_bytes = blkfac * B_SIZ;
                  }
                  else
                  send_bytes = bytes;

                  send_buf = hash_me(conn->K, read_buf, 
                               send_bytes);

                  sh_forward_prep_send_crypt (conn, send_buf, 
                                      send_bytes+KEY_LEN, 
                                      _("FILE"),  
                                      SH_PROTO_BIG|conn->client_entry->encf_flag);
#else
                  send_buf = hash_me(conn->K, read_buf, bytes);
                  sh_forward_prep_send_crypt (conn, send_buf, 
                                      bytes+KEY_LEN, 
                                      _("FILE"),  SH_PROTO_BIG);
#endif
                  conn->FileSent += bytes;
                  if (send_buf != NULL) 
                  {
                    SH_FREE(send_buf);
                  }
                  SH_FREE(read_buf);
                }
            }

            if (conn == NULL    || conn->FileName == NULL || 
              SL_ISERROR(sfd) || bytes < 0)
            {
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_NFILE,
                          conn->peer, 
                          (conn->FileName == NULL) ? 
                          _("(NULL)") : conn->FileName);
              status_update (conn->client_entry, CLT_FAILED);
              sh_forward_do_free (conn);
            }

          }
        
       else if (0 == check_request_nerr((char *)&(conn->head[3]), 
                                _("RECV"))                    &&
               conn->client_entry != NULL                           &&
               conn->K != NULL                                      &&
               conn->FileName != NULL)
          {

            TPT(( 0, FIL__, __LINE__, 
                _("msg=<File transfer - RCVT (5+).>\n")));

            if (conn->FileSent == conn->FileLength)
            {
              send_buf = hash_me(conn->K, conn->peer, 
                             sl_strlen(conn->peer));
#ifdef SH_ENCRYPT
              sh_forward_prep_send_crypt (conn, send_buf, 
                                    sl_strlen(conn->peer)+KEY_LEN, 
                                    _("EEOT"),  
                                    SH_PROTO_BIG|conn->client_entry->encf_flag);
#else
              sh_forward_prep_send_crypt (conn, send_buf, 
                                    sl_strlen(conn->peer)+KEY_LEN, 
                                    _("EEOT"),  
                                    SH_PROTO_BIG);
#endif
              SH_FREE(send_buf);
            }
            else
            {
              bytes = -1;
              sfd = sl_open_read(conn->FileName, SL_YESPRIV);
              if (!SL_ISERROR(sfd))
                {
                  read_buf = SH_ALLOC(TRANS_BYTES);
                  sl_seek (sfd, (off_t) conn->FileSent);
                  bytes = sl_read (sfd, read_buf, TRANS_BYTES);
                  sl_close(sfd);
                }
              if (bytes >= 0)
                {
#ifdef SH_ENCRYPT
                  /* need to send N * B_SIZ bytes
                   */ 
                  blkfac = bytes / B_SIZ;
                  rem    = bytes - (blkfac * B_SIZ);
                  if (rem != 0)
                  {
                    memset(&read_buf[bytes], '\n', (B_SIZ-rem));
                    ++blkfac;
                    send_bytes = blkfac * B_SIZ;
                  }
                  else
                  send_bytes = bytes;

                  send_buf = hash_me(conn->K, read_buf, 
                               send_bytes);

                  sh_forward_prep_send_crypt (conn, send_buf, 
                                      send_bytes+KEY_LEN, 
                                      _("FILE"),  
                                      SH_PROTO_BIG|conn->client_entry->encf_flag);
#else

                  send_buf = hash_me(conn->K, read_buf, bytes);
                  sh_forward_prep_send_crypt (conn, send_buf, 
                                      bytes+KEY_LEN, 
                                      _("FILE"),  
                                      SH_PROTO_BIG);
#endif

                  conn->FileSent += bytes;
                  SH_FREE(send_buf);
                  SH_FREE(read_buf);
                }
              else
                {
                  sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_NFILE,
                              conn->peer,
                              (conn->FileName == NULL) ? 
                              _("(NULL)") : conn->FileName);
                  status_update (conn->client_entry, CLT_FAILED);
                  sh_forward_do_free (conn);
                }
            }
          }


        else if (0 == check_request_nerr((char *)&(conn->head[3]), 
                                 _("EOTE")) &&
               conn->client_entry != NULL)
          {

            TPT(( 0, FIL__, __LINE__, 
                _("msg=<File transfer - EOTE (7).>\n")));

            sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_OKFILE,
                        conn->peer);

            if ((conn->client_entry->status_now != CLT_SUSPEND) &&
              (conn->client_entry->status_now != CLT_TOOLONG))
            { status_update (conn->client_entry, CLT_FILE); }
            else
            { conn->client_entry->session_key[0]    = '\0'; }
            conn->client_entry->last_connect = time (NULL);
            sh_forward_do_free (conn);
          }
          

        /* client does something unexpected
         */
        else  /* ---- ??? ----- */
          {
            sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_FINV,
                        1, conn->pass, conn->peer,  
                        '\\', conn->head[3], '\\',conn->head[4],
                        '\\', conn->head[5], '\\',conn->head[6]);
            status_update (conn->client_entry, CLT_FAILED);
            sh_forward_do_free (conn);
          }
      }

      else if (state == SH_DO_WRITE)  /* finished writing */
      {
        TPT(( 0, FIL__, __LINE__, _("msg=<File transfer - (wait).>\n")));

        /* challenge is sent, now wait for message from client
         */
        conn->headcount     = 0;
        conn->bytecount     = 0;
        conn->bytes_to_send = 0;
        conn->bytes_to_get  = 0;
        if (conn->buf != NULL) 
          {
            SH_FREE(conn->buf);
            conn->buf           = NULL;
          }
        conn->state     = CONN_READING;
      }
      SL_RET0(_("check_protocol"));
    }

  /* ---------  message exchange  -----------
   */
  if ((conn->head[0] & SH_PROTO_SRP) == 0  && 
      (conn->head[0] & SH_PROTO_MSG) != 0  /* is set */ )
    {

      if (state == SH_DO_READ)        /* finished reading */
      {

        TPT(( 0, FIL__, __LINE__, _("msg=<Message transfer - entry.>\n")));

        /* client requests challenge
         */
       if (0 == check_request_nerr ((char *)&(conn->head[3]), _("HELO")))
          {

            TPT(( 0, FIL__, __LINE__, 
                _("msg=<Message transfer - HELO (1).>\n")));

            if (conn->buf == NULL || sl_strlen(conn->buf) <= KEY_LEN )
            {
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_NOCLT);
              sh_forward_do_free (conn);
              SL_RET0(_("check_protocol"));
            } 

            TPT(( 0, FIL__, __LINE__, _("msg=<Rcvt %s.>\n"), conn->buf));

            /* ----  search the register  -----
             */
            this_client = search_register (conn, KEY_LEN);
            if (NULL == this_client)
            SL_RET0(_("check_protocol"));

            /* ---- force authentication -----
             */
            if ( (this_client->session_key[0] == '\0') || 
               ((time(NULL)-this_client->session_key_timer) 
                > (time_t) TIMEOUT_KEY)
               )
            {

              /* fake an auth request and jump there
               */
              conn->head[0]  = (conn->head[0] | SH_PROTO_SRP);
              conn->head[3]  = 'S';
              conn->head[4]  = 'A';
              conn->head[5]  = 'L';
              conn->head[6]  = 'T';
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_FAUTH,
                          &(conn->buf[KEY_LEN]));
              strcpy(conn->buf,                      /* known to fit  */
                   &(conn->buf[KEY_LEN]));
              this_client->session_key[0]    = '\0';
              this_client->session_key_timer = (time_t) 1;

              goto servInit;
            }
            
            /* check whether hostname is properly signed
             */ 
            if (conn->K != NULL)
            { 
              SH_FREE(conn->K);
              conn->K = NULL;
            }
            i = sl_strlen(&(conn->buf[KEY_LEN])) + KEY_LEN + 1;
            conn->K = SH_ALLOC(i);

            sl_strlcpy (conn->K, 
                    sh_util_siggen(this_client->session_key,
                               &(conn->buf[KEY_LEN]),
                               sl_strlen(&(conn->buf[KEY_LEN]))),
                    KEY_LEN+1);
            TPT((0, FIL__, __LINE__, _("msg=<host %s>\n"), 
               &(conn->buf[KEY_LEN])));
            TPT((0, FIL__, __LINE__, _("msg=<ckey %s>\n"), 
               this_client->session_key));
            TPT((0, FIL__, __LINE__, _("msg=<sign %s>\n"), conn->K));

            if (0 != sl_strncmp(conn->K, conn->buf, KEY_LEN))
            {
              TPT(( 0, FIL__, __LINE__, _("msg=<Rcvt %s>\n"), conn->buf));
              TPT(( 0, FIL__, __LINE__, _("msg=<Want %s>\n"), conn->K));
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_BADCONN,
                          _("Signature mismatch"), 
                          &(conn->buf[KEY_LEN]));

              this_client->session_key_timer = 
                time(NULL) - (2*TIMEOUT_KEY);

              sh_forward_do_free (conn);
              SL_RET0(_("check_protocol"));
            }
            SH_FREE(conn->K); 
            conn->K = NULL;

            /* -- create a nonce and send it --
             */

            conn->client_entry = this_client;
            sl_strlcpy (conn->peer, &(conn->buf[KEY_LEN]), SH_MINIBUF+1);

            ticks = (UINT32) taus_get (&(skey->rng0[0]), 
                               &(skey->rng1[0]),
                               &(skey->rng2[0]));
            test = (char *) &ticks;
            sh_util_cpylong (conn->challenge, test, 4);
            conn->challenge[4] = '\0';
            for (i = 0; i < 4; ++i)
            if (conn->challenge[i] == '\0')
              conn->challenge[i] = 0x01;

            sh_forward_prep_send (conn, conn->challenge, 5, _("TALK"), 
                            SH_PROTO_MSG);
            TPT(( 0, FIL__, __LINE__, _("msg=<Sent %s.>\n"), 
                hu_trans(conn->challenge)));
          }

        /* Client has send a message. Check whether we are in proper
         * state, and verify message.
         */
        else if (0 == 
               check_request_nerr((char *)&(conn->head[3]), _("MESG")) &&
               conn->client_entry != NULL                           &&
               conn->client_entry->session_key[0] != '\0'           &&
               (len = sl_strlen(conn->buf) - KEY_LEN) > 0           &&
               sl_strlen(conn->challenge) == 4)
          {
            TPT(( 0, FIL__, __LINE__, 
                _("msg=<Message transfer - MESG (3).>\n")));

#ifdef SH_ENCRYPT
            if (conn->client_entry->encf_flag == 0) {
            conn->client_entry->ency_flag = 0;
            }
            if ((conn->client_entry->ency_flag != 0) && 
              ((conn->head[0] & SH_PROTO_ENC) == 0)) 
            {
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_MISENC,
                          _("message transfer"), 
#ifdef SH_ENCRYPT_2
                          _("version2"),
#else
                          _("version1"),
#endif 
                          _("none"));
              /* conn->client_entry->ency_flag = 0; */
            }
            else if ((conn->client_entry->ency_flag != 0) &&
                   ((conn->head[0] & SH_MASK_ENC) != 
                  conn->client_entry->ency_flag))
            {
              sh_error_handle(SH_ERR_NOTICE, FIL__, __LINE__, 0, 
                          MSG_TCP_MISENC,
                          _("message transfer"), 
#ifdef SH_ENCRYPT_2
                          _("version2"),
#else
                          _("version1"),
#endif 
                          ((conn->head[0] & SH_PROTO_EN2) == SH_PROTO_EN2) ? _("version2") : _("version1"));
              conn->client_entry->ency_flag = 
                (conn->head[0] & SH_MASK_ENC); 
            }
#else
            if ((conn->head[0] & SH_PROTO_ENC) != 0) 
            {
              sh_error_handle((-1), FIL__, __LINE__, 0, 
                          MSG_TCP_MISENC,
                          _("message transfer"), 
                          _("none"), 
                          ((conn->head[0] & SH_PROTO_EN2) == SH_PROTO_EN2) ? _("version2") : _("version1"));
            }
#endif

            TPT(( 0, FIL__, __LINE__, _("msg=<Rcvt %s.>\n"), conn->buf));
            /* get hash from message end, truncate message
             */
            sl_strlcpy(hash, &(conn->buf[len]), KEY_LEN+1);
            conn->buf[len] = '\0';
            
            /* verify hash
             */
            buffer = sh_util_strconcat(conn->buf, conn->challenge, NULL);
            i =  sl_strncmp(hash, 
                        sh_util_siggen(conn->client_entry->session_key,
                                   buffer,
                                   sl_strlen(buffer)),
                        KEY_LEN);
            TPT((0, FIL__, __LINE__, _("msg=<sign %s.>\n"),
               sh_util_siggen(conn->client_entry->session_key,
                          buffer,
                          sl_strlen(buffer))));


            if (0 != i)
            {
              TPT((0, FIL__, __LINE__, _("msg=<update status>\n")));
              status_update (conn->client_entry, CLT_FAILED);
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_BADCONN,
                          _("Msg signature mismatch"), conn->peer);
              conn->client_entry->session_key_timer = 
                time(NULL) - (2*TIMEOUT_KEY);
              sh_forward_do_free (conn);
              SL_RET0(_("check_protocol"));
            }
            else
            {
              conn->client_entry->last_connect = time (NULL);

              if (NULL != sl_strstr(conn->buf,      _("EXIT")))
                {
                  TPT((0, FIL__, __LINE__, _("msg=<update status>\n")));
                  conn->client_entry->exit_flag = 1;
                  status_update (conn->client_entry, CLT_EXITED);
                }
              else if (NULL != sl_strstr(conn->buf, _("PANIC")))
                {
                  TPT((0, FIL__, __LINE__, _("msg=<update status>\n")));
                  status_update (conn->client_entry, CLT_PANIC);
                }
              else if (NULL != sl_strstr(conn->buf, _("SUSPEND")))
                {
                  TPT((0, FIL__, __LINE__, _("msg=<update status>\n")));
                  status_update (conn->client_entry, CLT_SUSPEND);
                }
              else if (NULL != sl_strstr(conn->buf, _("POLICY")))
                {
                  TPT((0, FIL__, __LINE__, _("msg=<update status>\n")));
                  status_update (conn->client_entry, CLT_POLICY);
                }
              else if (NULL != sl_strstr(conn->buf, 
                                   _("File check completed")))
                {
                  TPT((0, FIL__, __LINE__, _("msg=<update status>\n")));
                  status_update (conn->client_entry, CLT_CHECK);
                }
              else if (NULL != sl_strstr(conn->buf, _("START")))
                {
                  TPT((0, FIL__, __LINE__, _("msg=<update status>\n")));
                  sh_socket_add2reload (conn->client_entry->hostname);
                  if (conn->client_entry->status_now == CLT_SUSPEND) {
                  status_update (conn->client_entry, CLT_ILLEGAL);
                  sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_ILL,
                              conn->peer);
                  }
                  else
                  status_update (conn->client_entry, CLT_STARTED);
                }
              else
                {
                  TPT((0, FIL__, __LINE__, _("msg=<update status>\n")));
                  if (0 != sl_strcmp(conn->buf, 
                               _("Runtime configuration reloaded")))
                  {
                    sh_socket_add2reload (conn->client_entry->hostname);
                  }
                  status_update (conn->client_entry, CLT_MSG);
                }

              TPT((0, FIL__, __LINE__, _("msg=<status updated>\n")));
              clt_sev   = atoi(conn->buf);
              clt_class = (-1);
              ptok    = strchr(conn->buf, '?');
              if (ptok != NULL)
                {
                  ++ptok;
                  if (ptok != NULL && sh.flag.client_class == S_TRUE) 
                  clt_class = atoi(ptok);  /* is a global */
                  ptok = strchr(ptok, '?');
                  if (ptok != NULL) 
                  ++ptok;
                }
              if (sh.flag.client_severity == S_FALSE)
                clt_sev = (-1);

              /* here we expect an xml formatted message, thus we don't
                 escape xml special chars (flag == 0) */
              ptok = 
                sh_tools_safe_name ((ptok!=NULL) ? ptok : conn->buf, 0);

              /* push client name to error routine
                   */
                  sh_error_set_peer(sh_strip_domain (conn->peer));
              sh_error_handle(clt_sev, FIL__, __LINE__, 0, MSG_TCP_MSG,
                          sh_strip_domain (conn->peer), 
                          ptok);
                  sh_error_set_peer(NULL);

              TPT((0, FIL__, __LINE__, _("msg=<%s>\n"), ptok));
              SH_FREE(ptok);
              clt_class = (-1);
            }
            memset(buffer, '\0', sl_strlen(buffer));
            SH_FREE(buffer);

            /* SERVER CONF SEND
             */
            buffer = sh_util_strconcat(conn->buf,
                               conn->challenge,
                               NULL);
            sl_strlcpy(hash, 
                   sh_util_siggen ( conn->client_entry->session_key,
                                buffer,
                                sl_strlen(buffer)),
                   KEY_LEN+1);
            
            /* --- SERVER CMD --- */
            cmd = sh_socket_check (conn->peer);

            if (cmd != NULL)
            {
              /* max cmd size is SH_MAXMSGLEN bytes
               */
              sl_strlcpy(&hash[KEY_LEN], cmd, SH_MAXMSGLEN);
              sl_strlcat(&hash[KEY_LEN],
                       sh_util_siggen ( conn->client_entry->session_key,
                                    &hash[KEY_LEN],
                                    sl_strlen(&hash[KEY_LEN])),
                       SH_MAXMSGLEN+KEY_LEN+1);
              
              TPT((0, FIL__, __LINE__, _("CONF SEND <0> <%s>\n"), 
                    &hash[KEY_LEN]));
              
            } else {
              
              TPT((0, FIL__, __LINE__, _("CONF SEND <0> <[NULL]>\n")));
              
            }
            /* --- SERVER CMD END --- */

            TPT((0, FIL__, __LINE__, _("msg=<sign %s.>\n"),
               sh_util_siggen(conn->client_entry->session_key,
                          buffer,
                          sl_strlen(buffer))));
        
#ifdef SH_ENCRYPT
            sh_forward_prep_send_crypt (conn, hash, 
                                sl_strlen(hash) /* KEY_LEN */, 
                                _("CONF"), 
                                SH_PROTO_MSG|SH_PROTO_END|conn->client_entry->ency_flag);
#else
            sh_forward_prep_send_crypt (conn, hash, 
                                sl_strlen(hash) /* KEY_LEN */, 
                                _("CONF"), 
                                SH_PROTO_MSG|SH_PROTO_END);
#endif

            memset(buffer, '\0', sl_strlen(buffer));
            SH_FREE(buffer);
            
            /* sh_forward_do_free (conn); */
          }
        
        /* client does something unexpected
         */
        else  /* ---- ??? ----- */
          {
            sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_FINV,
                        2, conn->pass, conn->peer,  
                        '\\', conn->head[3], '\\',conn->head[4],
                        '\\', conn->head[5], '\\',conn->head[6]);
            status_update (conn->client_entry, CLT_FAILED);
            conn->client_entry->session_key_timer = 
            time(NULL) - (2*TIMEOUT_KEY);
            sh_forward_do_free (conn);
          }
      }
      else if (state == SH_DO_WRITE)  /* finished writing */
      {
        if (0 != (conn->head[0] & SH_PROTO_END))
          {
            sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_OKMSG,
                        sh_strip_domain (conn->peer));
            sh_forward_do_free (conn);
            SL_RET0(_("check_protocol"));
          }

        TPT(( 0, FIL__, __LINE__, _("msg=<Msg transfer - (wait).>\n")));

        /* challenge is sent, now wait for message from client
         */
        conn->headcount     = 0;
        conn->bytecount     = 0;
        conn->bytes_to_send = 0;
        conn->bytes_to_get  = 0;
        if (conn->buf != NULL) 
          {
            SH_FREE(conn->buf);
            conn->buf           = NULL;
          }
        conn->state     = CONN_READING;
      }
      TPT((0, FIL__, __LINE__, _("msg=<return>\n") ));
      SL_RET0(_("check_protocol"));
    }

  /* ---------  authentication  -----------
   */

  /* entry point for jump from message forward if session key must
   * be re-initialized
   */  
 servInit:

  if ( (conn->head[0] & SH_PROTO_SRP) != 0   /* is set */ )
    {

#ifndef USE_SRP_PROTOCOL

      if (state == SH_DO_READ)        /* finished reading */
      {
        TPT((0, FIL__, __LINE__, _("msg=<Authentication - entry.>\n")));

        /* first pass -- client request salt  
         */
        if (conn->pass    == 1) 
          {

            TPT((0, FIL__, __LINE__, 
               _("msg=<Authentication - SALT (1).>\n")));

            if (conn->buf == NULL || sl_strlen(conn->buf) == 0)
            {
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_NOCLT);
              sh_forward_do_free (conn);
              SL_RET0(_("check_protocol"));
            } 
              

            /* search the register
             */

            this_client = search_register (conn, 0);
            if (NULL == this_client)
            SL_RET0(_("check_protocol"));


            conn->client_entry = this_client;
            sl_strlcpy (conn->peer, conn->buf, SH_MINIBUF+1);

            if (0 != check_request_s((char *)&(conn->head[3]), 
                               _("SALT"),conn->peer))
            {
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_BADCONN,
                          _("No salt requested"), conn->peer);
              status_update (conn->client_entry, CLT_FAILED);
              conn->client_entry->session_key_timer = 
                time(NULL) - (2*TIMEOUT_KEY);
              sh_forward_do_free (conn);
              SL_RET0(_("check_protocol"));
            } 

             
            /* -- create server nounce v --
             */
            ticks = (UINT32) taus_get (&(skey->rng0[0]), 
                               &(skey->rng1[0]),
                               &(skey->rng2[0]));
            
            if (conn->A != NULL)
            {
              SH_FREE(conn->A);
              conn->A = NULL;
            }
            conn->A = SH_ALLOC(KEY_LEN+1);

            sl_strlcpy(conn->A, 
                   sh_tiger_hash((char *) &ticks, 
                               TIGER_DATA, sizeof(UINT32)),
                   KEY_LEN+1);
            u[0] = 'I'; u[1] = 'N'; u[2] = 'I'; u[3] = 'T'; u[4] = '\0';

            if (conn->M1 != NULL)
            {
              SH_FREE(conn->M1);
              conn->M1 = NULL;
            }
            conn->M1 = SH_ALLOC(2*KEY_LEN+1);

            /* compute hash key H(v(server), P)v(server)
             */
            sh_passwd (conn->A, conn->client_entry->verifier, 
                   NULL, conn->M1);

            sl_strlcat(conn->M1, conn->A, 2*KEY_LEN+1);


            /* --- send H(v(server), P)v(server) ----
             */
                  sh_forward_prep_send (conn, 
                            conn->M1, 
                            sl_strlen(conn->M1), 
                            u, 
                            (conn->head[0]|SH_PROTO_SRP));

            SH_FREE(conn->M1); 
            conn->M1 = NULL;
          }

        /* client -- third pass
         * Message is H(H(u,v),P)u
         *
         * A := v, verifier := H(password), 
         */
        else if (conn->pass    == 3                   && 
               conn->client_entry != NULL)
          {

            TPT((0, FIL__, __LINE__, 
               _("msg=<Authentication - PASS (3).>\n")));
                          
            if (0 != check_request_s((char *) &(conn->head[3]), _("PASS"), 
                               conn->peer)                    ||
              sl_strlen(conn->buf) <= KEY_LEN                        ||
              conn->A == NULL)
            {
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_BADCONN,
                          _("Invalid client request"), conn->peer);
              status_update (conn->client_entry, CLT_FAILED);
              conn->client_entry->session_key_timer = 
                time(NULL) - (2*TIMEOUT_KEY);
              sh_forward_do_free (conn);
              SL_RET0(_("check_protocol"));
            } 

            /* store random nonce u from client
             */
            if (conn->K != NULL)
            {
              SH_FREE(conn->K);
              conn->K = NULL;
            }
            conn->K = SH_ALLOC(KEY_LEN+1);
            sl_strlcpy(conn->K, &(conn->buf[KEY_LEN]), KEY_LEN+1);

            /* verify random nonce u from client
             */
            if (conn->M1 != NULL)
            {
              SH_FREE(conn->M1);
              conn->M1 = NULL;
            }
            conn->M1 = sh_util_strconcat(conn->K, conn->A, NULL);

            TPT((0, FIL__, __LINE__, _("msg=<c/r: K = %s>\n"), conn->K));
            TPT((0, FIL__, __LINE__, _("msg=<c/r: A = %s>\n"), conn->A));
            TPT((0, FIL__, __LINE__, _("msg=<c/r: M = %s>\n"), conn->M1));

            sl_strlcpy(hash, sh_tiger_hash (conn->M1, 
                                    TIGER_DATA, 
                                    sl_strlen(conn->M1)), KEY_LEN+1); 
            sh_passwd (hash, conn->client_entry->verifier, NULL, conn->M1);

            TPT((0, FIL__, __LINE__, _("msg=<c/r: H = %s>\n"), hash));
            TPT((0, FIL__, __LINE__, _("msg=<c/r: P = %s>\n"), conn->M1));

            if ( 0 != sl_strncmp(conn->M1, conn->buf, KEY_LEN))
            {
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_BADCONN,
                          _("Session key mismatch"), conn->peer);
              status_update (conn->client_entry, CLT_FAILED);
              conn->client_entry->session_key_timer = 
                time(NULL) - (2*TIMEOUT_KEY);
              sh_forward_do_free (conn);
              SL_RET0(_("check_protocol"));
            } 
              

            /* ---- compute hash key H(v, P, u) ----
             */
            
            sh_passwd (conn->A, conn->client_entry->verifier, conn->K,
                   conn->M1);

            sl_strlcpy(conn->client_entry->session_key, 
                   conn->M1, KEY_LEN+1);
            TPT((0, FIL__, __LINE__, _("msg=<c/r: Key = %s>\n"), 
               conn->client_entry->session_key));

#ifdef SH_ENCRYPT
             err_num = makeKey(&(conn->client_entry->keyInstE), 
                         DIR_ENCRYPT, 192, 
                         conn->client_entry->session_key);
             if (err_num < 0)
             sh_error_handle((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN,
                         errorExplain(err_num), 
                         _("check_protocol: makeKey"));
             err_num = makeKey(&(conn->client_entry->keyInstD), 
                         DIR_DECRYPT, 192, 
                         conn->client_entry->session_key);
             if (err_num < 0)
             sh_error_handle((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN,
                         errorExplain(err_num), 
                         _("check_protocol: makeKey"));
#endif

            if (conn->K  != NULL) SH_FREE (conn->K);
            conn->K  = NULL;
            if (conn->A  != NULL) SH_FREE (conn->A);
            conn->A  = NULL;
            if (conn->M1 != NULL) SH_FREE (conn->M1);
            conn->M1 = NULL;

            /* if (conn->client_entry->status_now == CLT_STARTED */
            if (((conn->client_entry->status_now != CLT_INACTIVE) &&
               (conn->client_entry->status_now != CLT_EXITED)   &&
               (conn->client_entry->status_now != CLT_SUSPEND))
              && conn->client_entry->session_key_timer > (time_t) 1)
            {
              status_update (conn->client_entry, CLT_ILLEGAL);
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_ILL,
                          conn->peer);
            }
            else if (conn->client_entry->session_key_timer == (time_t) 0)
            {
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_NEW,
                          conn->peer);
              if (conn->client_entry->status_now != CLT_SUSPEND)
                status_update (conn->client_entry, CLT_STARTED);
            }

            conn->client_entry->session_key_timer = time (NULL);
            conn->client_entry->last_connect = time (NULL);

            /* put in read state
             */
            sh_forward_prep_send (conn, 
                            _("AUTH"),
                            5, 
                            _("AUTH"), 
                            (conn->head[0]|SH_PROTO_SRP));

          }
        else
          {
            sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_FINV,
                        3, conn->pass, conn->peer, 
                        '\\', conn->head[3], '\\', conn->head[4],
                        '\\', conn->head[5], '\\', conn->head[6]);
            sh_forward_do_free (conn);
          }
      }

#else
      /* use SRP                    */


      if (state == SH_DO_READ)        /* finished reading */
      {

        TPT((0, FIL__, __LINE__, _("msg=<Authentication - entry.>\n")));

        /* first pass -- client request salt  
         */
        if (conn->pass    == 1)
          {
            TPT((0, FIL__, __LINE__, 
               _("msg=<Authentication - SALT (1).>\n")));

            if (conn->buf == NULL)
            {
              sh_error_handle( (-1), FIL__, __LINE__, 0, MSG_TCP_NOCLT);
              sh_forward_do_free (conn);
              SL_RET0(_("check_protocol"));
            } 

            /* search the register
             */
            this_client = search_register(conn, 0);
            if (NULL == this_client)
            SL_RET0(_("check_protocol"));

            conn->client_entry = this_client;
            sl_strlcpy (conn->peer, conn->buf, SH_MINIBUF+1);

            if (0 != check_request_s((char *)&(conn->head[3]), _("SALT"),
                               conn->peer))
            {
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_BADCONN,
                          _("No salt requested"), conn->peer);
              status_update (conn->client_entry, CLT_FAILED);
              conn->client_entry->session_key_timer = 
                time(NULL) - (2*TIMEOUT_KEY);
              sh_forward_do_free (conn);
              SL_RET0(_("check_protocol"));
            } 
              

            u[0] = 'I'; u[1] = 'N'; u[2] = 'I'; u[3] = 'T'; u[4] = '\0';
            
            sh_forward_prep_send (conn, 
                            conn->client_entry->salt, 
                            sl_strlen(conn->client_entry->salt), 
                            u, 
                            (conn->head[0]|SH_PROTO_SRP));
          }

        /* client has sent A -- third pass
         */
        else if (conn->pass == 3                    && 
               conn->client_entry != NULL)
          {

            TPT((0, FIL__, __LINE__, 
               _("msg=<Authentication - PC01 (3).>\n")));

            if (0 != check_request_s((char *)&(conn->head[3]),_("PC01"),conn->peer)||
              conn->buf == NULL
              )
            {
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_BADCONN,
                          _("Invalid client request"), conn->peer);
              status_update (conn->client_entry, CLT_FAILED);
              conn->client_entry->session_key_timer = 
                time(NULL) - (2*TIMEOUT_KEY);
              sh_forward_do_free (conn);
              SL_RET0(_("check_protocol"));
            } 

            if (0 != sh_srp_init())
            {
              status_update (conn->client_entry, CLT_FAILED);
              sh_error_handle(SH_ERR_SEVERE, FIL__, __LINE__, 0, 
                          MSG_TCP_EBGN);
              sh_forward_do_free (conn);
              SL_RET0(_("check_protocol"));
            }
              

            /* check A, only send B if correct 
             */
            if ( sl_strlen(conn->buf) < SH_BUFSIZE && 
               0 == sh_srp_check_zero (conn->buf) )
            {
              len = sl_strlen(conn->buf)+1;

              if (conn->A != NULL)
                {
                  SH_FREE(conn->A);
                  conn->A = NULL;
                }
              conn->A = SH_ALLOC(len);
              sl_strlcpy (conn->A, conn->buf, len);
              
              /* 
               * compute B 
               */
              if (0 != sh_srp_make_a ())     /* b        random number */
                {
                  status_update (conn->client_entry, CLT_FAILED);
                  
                  sh_error_handle(SH_ERR_SEVERE, FIL__, __LINE__, 0, 
                              MSG_TCP_EBGN);
                  sh_srp_exit();
                  sh_forward_do_free (conn);
                  SL_RET0(_("check_protocol"));
                }
             
              foo_B = sh_srp_B               /* B = v + g^b            */
                (conn->client_entry->verifier);

              if (foo_B == NULL)
                {
                  status_update (conn->client_entry, CLT_FAILED);
                  
                  sh_error_handle(SH_ERR_SEVERE, FIL__, __LINE__, 0, 
                              MSG_TCP_EBGN);
                  sh_srp_exit();
                  sh_forward_do_free (conn);
                  SL_RET0(_("check_protocol"));
                }

              TPT((0, FIL__, __LINE__, _("msg=<srp: A = %s>\n"), conn->A));
              TPT((0, FIL__, __LINE__, _("msg=<srp: B = %s>\n"), foo_B));

              /* 
               * create nonce u 
               */
              ticks = (UINT32) taus_get (&(skey->rng0[0]), 
                                   &(skey->rng1[0]),
                                   &(skey->rng2[0])); 
              test = (char *) &ticks;
              sh_util_cpylong (u, test, 4);  /* u        nounce        */
              u[4] = '\0';
              sl_strlcpy(conn->challenge, 
                       sh_tiger_hash(u, TIGER_DATA, 4),
                       SH_CHALLENGE_SIZE);

              TPT((0, FIL__, __LINE__, _("msg=<srp: u = %03o-%03o-%03o-%03o>\n"), u[0], u[1], u[2], u[3]));
              TPT((0, FIL__, __LINE__, _("msg=<srp: U = %s>\n"), 
                   conn->challenge));

              /* 
               * compute the session key K and M1 = Hash(A,B,K)
               */
              foo_Ss = sh_srp_S_s (conn->challenge, 
                               conn->A, 
                               conn->client_entry->verifier);
              if (foo_Ss == NULL)
                {
                  status_update (conn->client_entry, CLT_FAILED);
                  
                  sh_error_handle(SH_ERR_SEVERE, FIL__, __LINE__, 0, 
                              MSG_TCP_EBGN);
                  sh_srp_exit();
                  sh_forward_do_free (conn);
                  SL_RET0(_("check_protocol"));
                }

              if (conn->K != NULL)
                {
                  SH_FREE(conn->K);
                  conn->K = NULL;
                }
              conn->K = SH_ALLOC(KEY_LEN+1);
              sl_strlcpy(conn->K, 
                       sh_tiger_hash(foo_Ss, TIGER_DATA, 
                                 sl_strlen(foo_Ss)),
                       KEY_LEN+1);

              if (conn->M1 != NULL)
                {
                  SH_FREE(conn->M1);
                  conn->M1 = NULL;
                }
              conn->M1 = SH_ALLOC(KEY_LEN+1);
              sl_strlcpy(conn->M1, 
                    sh_srp_M (conn->A, foo_B, conn->K),
                    KEY_LEN+1);

              TPT((0, FIL__, __LINE__, _("msg=<srp:Ss = %s>\n"), foo_Ss));
              TPT((0, FIL__, __LINE__, _("msg=<srp: K = %s>\n"), conn->K));
              TPT((0, FIL__, __LINE__, _("msg=<srp:M1 = %s>\n"),conn->M1));

              /*
               * send B
               */
              sh_forward_prep_send (conn, 
                              foo_B,
                              sl_strlen(foo_B)+1,
                              u,
                              (conn->head[0]|SH_PROTO_SRP));
              if (foo_Ss != NULL)
                {
                  SH_FREE(foo_Ss);
                  foo_Ss = NULL;
                }
              if (foo_B  != NULL)
                {
                  SH_FREE(foo_B);
                  foo_B = NULL;
                }
            }
            else
            {
              status_update (conn->client_entry, CLT_FAILED);

              sh_error_handle(SH_ERR_SEVERE, FIL__, __LINE__, 0, 
                          MSG_TCP_EZERO);
              sh_forward_do_free (conn);
            }

            sh_srp_exit();
          }
              
        /* client has sent M1 -- fifth pass
         */
        else if (conn->pass    == 5           && 
               conn->client_entry != NULL) 
          {
            TPT((0, FIL__, __LINE__, 
               _("msg=<Authentication - PC02 (5).>\n")));

            /* check that the state is valid
             */
            if (0 != check_request_s((char *)&(conn->head[3]), _("PC02"),
                               conn->peer)                   ||
              conn->A == NULL || conn->K == NULL || conn->M1 == NULL)
            {
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_BADCONN,
                          _("Invalid client request"), conn->peer);
              status_update (conn->client_entry, CLT_FAILED);
              conn->client_entry->session_key_timer = 
                time(NULL) - (2*TIMEOUT_KEY);
              sh_forward_do_free (conn);
              SL_RET0(_("check_protocol"));
            } 

            /* ------ verify M1 = H(A,  B, K) -------
             * -----    send M2 = H(A, M1, K) -------
             */
            if (conn->buf != NULL && 
              sl_strncmp(conn->buf, conn->M1, KEY_LEN) == 0)
            {
              /*
               * send M2
               */
              sh_forward_prep_send (conn, 
                              sh_srp_M (conn->A, conn->M1, conn->K),
                              KEY_LEN+1,
                              _("PARP"),
                              (conn->head[0]|SH_PROTO_SRP));

              if (conn->A  != NULL) SH_FREE(conn->A);  conn->A  = NULL;
              if (conn->M1 != NULL) SH_FREE(conn->M1); conn->M1 = NULL;
              sl_strlcpy(conn->client_entry->session_key, 
                       conn->K, KEY_LEN+1);
              TPT((0, FIL__, __LINE__, _("msg=<key %s>\n"), 
                   conn->client_entry->session_key));

#ifdef SH_ENCRYPT
              err_num = makeKey(&(conn->client_entry->keyInstE), 
                            DIR_ENCRYPT, 192, 
                            conn->client_entry->session_key);
              if (err_num < 0)
                sh_error_handle((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN,
                            errorExplain(err_num), 
                            _("sh_forward_prep_send_int: makeKey"));
              err_num = makeKey(&(conn->client_entry->keyInstD), 
                            DIR_DECRYPT, 192, 
                            conn->client_entry->session_key);
              if (err_num < 0)
                sh_error_handle((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN,
                            errorExplain(err_num), 
                            _("sh_forward_prep_send_int: makeKey"));
#endif

              if (conn->K  != NULL) SH_FREE(conn->K);  conn->K  = NULL;

              conn->client_entry->last_connect = time (NULL);
              
              if (((conn->client_entry->status_now != CLT_INACTIVE) &&
                   (conn->client_entry->status_now != CLT_EXITED)   &&
                   (conn->client_entry->status_now != CLT_SUSPEND))
                  && conn->client_entry->session_key_timer > (time_t) 1)
                {
                  status_update (conn->client_entry, CLT_ILLEGAL);

                  sh_error_handle((-1), FIL__, __LINE__, 0, 
                              MSG_TCP_ILL,
                              conn->peer);
                }
              else if (conn->client_entry->session_key_timer == (time_t) 0)
                {
                  sh_error_handle((-1), FIL__, __LINE__, 0, 
                              MSG_TCP_NEW,
                              conn->peer);
                  if (conn->client_entry->status_now != CLT_SUSPEND)
                  status_update (conn->client_entry, CLT_STARTED);
                }
              conn->client_entry->session_key_timer = time (NULL);

            }
            else
            {
              status_update (conn->client_entry, CLT_FAILED);
              conn->client_entry->session_key_timer = 
                time(NULL) - (2*TIMEOUT_KEY);

              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_BADCONN,
                          _("Session key mismatch"), conn->peer);
              sh_forward_do_free (conn);
            } 
          }

        else
          {
            sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_FINV,
                        4, conn->pass, conn->peer, 
                        '\\', conn->head[3], '\\', conn->head[4],
                        '\\', conn->head[5], '\\', conn->head[6]);
            sh_forward_do_free (conn);
          }
      }

#endif

      else if (state == SH_DO_WRITE)  /* finished writing */
      {
        TPT((0, FIL__, __LINE__, _("msg=<Authentication -- (wait).>\n")));

        conn->headcount     = 0;
        conn->bytecount     = 0;
        conn->bytes_to_send = 0;
        conn->bytes_to_get  = 0;
        if (conn->buf != NULL) 
          {
            SH_FREE(conn->buf);
            conn->buf           = NULL;
          }
        conn->state     = CONN_READING;
      }
    }
  SL_RET0(_("check_protocol"));
}


/***********************************************************
 *
 *    SERVER RECEIVE FUNCTION
 *
 ***********************************************************
 */
int sh_forward_do_read (sh_conn_t * conn)
{
  unsigned long   byteread;     /* bytes read         */

#ifdef SH_ENCRYPT

  unsigned long           blkfac = 0;
  /* unsigned long           length2; */
  char                  * p = NULL, * q = NULL;
  RIJ_BYTE                    inBlock[B_SIZ]; 
  RIJ_BYTE                    outBlock[B_SIZ];
  unsigned int            j;
  cipherInstance          cipherInst;
  int                     err_num;
#endif

  SL_ENTER(_("sh_forward_do_read"));

  if (conn->state == CONN_SENDING)
    {
      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_SYNC,
                  conn->peer);
      SL_RETURN( (-1), _("sh_forward_do_read"));
    }

  if (conn->headcount < SH_HEADER_SIZE) 
    {
      conn->bytes_to_get = SH_HEADER_SIZE - conn->headcount;
      byteread           = read (conn->fd, &(conn->head[conn->headcount]),
                            conn->bytes_to_get);
      if (byteread > 0 || errno == EINTR) 
      {
        if (byteread > 0) 
          conn->headcount += byteread;
        if (conn->headcount == SH_HEADER_SIZE)
          {
            conn->bytes_to_get = 
              (256 * (unsigned int)conn->head[1] + 
               (unsigned int)conn->head[2]);
            SH_SHOWPROT(conn->head, '<');
            conn->bytecount = 0;
          }
      }
      else
      {
        goto conn_reset;
      }
      SL_RETURN( (0), _("sh_forward_do_read"));
    }


  /* limit message size
   */
  /*
  conn->bytes_to_get = 
    (conn->bytes_to_get > (16*SH_BUFSIZE - 1)) ? 
    (16*SH_BUFSIZE - 1) : conn->bytes_to_get;
  */
  conn->bytes_to_get = (conn->bytes_to_get > TRANS_BYTES) ? 
    TRANS_BYTES : conn->bytes_to_get;

  if (conn->headcount == SH_HEADER_SIZE && conn->bytes_to_get > 0)
    {
      if ((conn->bytecount > 0) && (conn->bytes_to_get > conn->bytecount))
      {
        /* do nothing */;
      }
      else
      {
        if (conn->buf != NULL)
          SH_FREE (conn->buf);
        conn->buf       = SH_ALLOC (conn->bytes_to_get + 1);
        conn->bytecount = 0;
      }

      byteread           = read (conn->fd, &(conn->buf[conn->bytecount]),
                         conn->bytes_to_get - conn->bytecount);
      if (byteread > 0 || errno == EINTR) 
      {
        if (byteread > 0) 
          conn->bytecount    += byteread;
        if (conn->bytecount == conn->bytes_to_get) 
          {
            ++conn->pass;
            /* always terminate with NULL - we might use sl_strcmp()
             */
            conn->buf[conn->bytecount] = '\0';
            conn->state                = CONN_PAUSE;

#ifdef SH_ENCRYPT
            if      ((conn->head[0] & SH_PROTO_EN2) != 0) /* if encrypted */
            {
              conn->buf = 
                sh_tools_revertPack (conn->head, conn->buf,
                               &(conn->client_entry->keyInstD),
                               conn->bytecount);
            }
            else if ((conn->head[0] & SH_PROTO_ENC) != 0) /* if encrypted */
            {
              /* Decrypt only complete blocks. 
               * If there is an incomplete block,
               * something is wrong anyway.
               * Decrypt in place.
               */
              blkfac  = conn->bytecount / B_SIZ;
              /* length2 = (B_SIZ * blkfac); */
              p       = conn->buf;
              q       = conn->buf;
      
              err_num = cipherInit (&cipherInst, MODE_CBC, NULL);
              if (err_num < 0)
                sh_error_handle((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN,
                            errorExplain(err_num), 
                            _("sh_forward_do_read: cipherInit"));

              for (j = 0; j < blkfac; ++j)
                {
                  memcpy(inBlock, p, B_SIZ);
                  err_num = blockDecrypt(&cipherInst, 
                                   &(conn->client_entry->keyInstD), 
                                   inBlock, 128 * BNUM, outBlock);
                  if (err_num < 0)
                  sh_error_handle((-1), FIL__, __LINE__, -1, 
                              MSG_E_SUBGEN,
                              errorExplain(err_num), 
                              _("sh_forward_do_read: blockDecrypt"));
                  memcpy(q, outBlock, B_SIZ);
                  p += 16;
                  q += 16;
                }
            }
#endif

            /* ------  HERE CALL check_protocol(conn) -------  */
            check_protocol(conn, SH_DO_READ);
          }
      }
      else
      {
        goto conn_reset;
      }
    }

  else if (conn->headcount == SH_HEADER_SIZE && conn->bytes_to_get == 0)
    {
      if (conn->buf != NULL)
      SH_FREE (conn->buf);
      conn->buf       = NULL;
      conn->bytecount = 0;
      ++conn->pass;
      conn->state     = CONN_PAUSE;
      /* fprintf(stderr, "\n**** FIXME null read ****\n\n"); */
      /* ------  HERE CALL check_protocol(conn) -------  */
      check_protocol(conn, SH_DO_READ);
    }
      
  SL_RETURN( (0), _("sh_forward_do_read"));

 conn_reset:
  sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_RESET,
              conn->peer);
  sh_forward_do_free ( conn );
  SL_RETURN( (-1), _("sh_forward_do_read"));
}

#if !defined(O_NONBLOCK)
#if defined(O_NDELAY)
#define O_NONBLOCK  O_NDELAY
#else
#define O_NONBLOCK  0
#endif
#endif

/* send to the client
 */
int sh_forward_do_write (sh_conn_t * conn)
{
  int    flags;
  long   arg = 0;
  long   bytesent;     /* bytes read         */

  SL_ENTER(_("sh_forward_do_write"));

  /* ---- consistency check ------
   */
  if (conn->state == CONN_READING)
    {
      sh_error_handle( (-1), FIL__, __LINE__, 0, MSG_TCP_SYNC,
                  conn->peer);
      SL_RETURN( (-1), _("sh_forward_do_write"));
    }
      
  
  flags = retry_fcntl (FIL__, __LINE__, conn->fd, F_GETFL, arg);
  retry_fcntl (FIL__, __LINE__, conn->fd, F_SETFL,  flags|O_NONBLOCK);

  /* ---- send the header ------
   */
  if (conn->headcount < SH_HEADER_SIZE) 
    {
      conn->bytes_to_send = SH_HEADER_SIZE - conn->headcount;
      bytesent            = write (conn->fd, 
                           &(conn->head[conn->headcount]), 
                           conn->bytes_to_send);
      if (bytesent >= 0 || errno == EINTR || errno == EAGAIN) 
      {
        if (bytesent > 0) 
          conn->headcount += bytesent;
        if (conn->headcount == SH_HEADER_SIZE) 
          {
            conn->bytes_to_send = 
            (256 * (int)conn->head[1] + (int)conn->head[2]);
          }
      }
      else 
      {
        goto conn_reset_w;
      }
      if (conn->fd >= 0)
      retry_fcntl (FIL__, __LINE__, conn->fd, F_SETFL,  flags);
      SL_RETURN( (0), _("sh_forward_do_write"));
    }


  /* ---- send the body ------
   */

  if (conn->headcount == SH_HEADER_SIZE && conn->bytes_to_send > 0 &&
      conn->buf != NULL)
    {
      bytesent           = write (conn->fd, &(conn->buf[conn->bytecount]), 
                             conn->bytes_to_send - conn->bytecount);
      if (bytesent >= 0 || errno == EINTR || errno == EAGAIN) 
      {
        if (bytesent > 0) 
          conn->bytecount    += bytesent;
        if (conn->bytecount == conn->bytes_to_send) 
          {
            ++conn->pass;
            conn->state         = CONN_PAUSE;
            /* ------  HERE CALL check_protocol(conn) -------  */
            check_protocol(conn, SH_DO_WRITE);
          }
      }
      else
      {
        goto conn_reset_w;
      }
    }
      
  else if (conn->headcount == SH_HEADER_SIZE && conn->bytes_to_send == 0)
    {
      ++conn->pass;
      conn->state     = CONN_PAUSE;
      /* fprintf(stderr, "\n**** FIXME null write ****\n\n"); */
      /* ------  HERE CALL check_protocol(conn) -------  */
      check_protocol(conn, SH_DO_WRITE);
    }

  if (conn->fd >= 0)
    retry_fcntl (FIL__, __LINE__, conn->fd, F_SETFL,  flags);
  SL_RETURN( (0), _("sh_forward_do_write"));

 conn_reset_w:
  sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_RESET,
              conn->peer);
  sh_forward_do_free ( conn );
  SL_RETURN( (-1), _("sh_forward_do_write"));
}

/* accept a connection from a client
 */ 
#include <syslog.h>
#ifdef SH_USE_LIBWRAP
#include <tcpd.h>

#ifndef ALLOW_SEVERITY 
#define ALLOW_SEVERITY LOG_INFO
#define DENY_SEVERITY  LOG_WARNING
#endif

int allow_severity;
int deny_severity;
#endif

int sh_forward_accept (int sock, sh_conn_t * newconn)
{
  int                errflag;
  int                rc;
  struct sockaddr_in addr;
#ifdef SH_USE_LIBWRAP
  struct request_info request;
  char                errbuf[128];
  char                daemon[128];
#endif
  
  /* handle AIX (size_t addrlen) in wrapper
   */
  int                addrlen = sizeof(addr);

  SL_ENTER(_("sh_forward_accept"));

  rc = retry_accept(FIL__, __LINE__, sock, 
                (struct sockaddr *) &addr, &addrlen);

  if (rc >= 0)
    {

      if (addrlen == 0)
      {
        sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN,
                    _("Connecting entity unknown"), _("accept"));
        newconn->fd    = -1;
        newconn->state = CONN_FREE;
        close(rc);
        SL_RETURN( (-1), _("sh_forward_accept"));
      }

#ifdef SH_USE_LIBWRAP
      sl_strlcpy(daemon, SH_INSTALL_NAME, 128);
      request_init(&request, RQ_DAEMON, daemon, RQ_FILE, rc, 0);
      fromhost(&request);
      if (!hosts_access(&request)) 
      {
        sl_strlcpy(errbuf, _("Refused connection from "), 128);
        sl_strlcat(errbuf,   eval_client(&request), 128);

        sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN,
                    errbuf, _("libwrap"));
        newconn->fd    = -1;
        newconn->state = CONN_FREE;
        close(rc);
        SL_RETURN( (-1), _("sh_forward_accept"));
      }
#endif

      memcpy (&(newconn->addr_peer), &addr, sizeof(struct sockaddr_in));

      /* prepare for usage of connection
       */
      (void) retry_fcntl( FIL__, __LINE__, rc, F_SETFD, 1 );
      newconn->fd           = rc;
      newconn->state        = CONN_READING;
      newconn->timer        = (unsigned long) time (NULL);

      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_CNEW, newconn->fd);

      SL_RETURN( (0), _("sh_forward_accept"));
    }
  else
    {
      errflag = errno;
      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN,
                  sh_error_message(errflag), _("accept"));
      newconn->fd    = -1;
      newconn->state = CONN_FREE;
      SL_RETURN( (-1), _("sh_forward_accept"));
    }
}

extern char sh_sig_msg[64];  /* defined in sh_unix.c */

/* ------------  port and interface -------
 */
static unsigned int server_port = SH_DEFAULT_PORT;

int sh_forward_set_port (char * str)
{
  int retval = 0;
  unsigned long   i;
  char * endptr = str;
  
  SL_ENTER(_("sh_forward_set_port"));
  i = strtoul (str, &endptr, 0);
  if (endptr == str) {
    retval = -1;
  } else if (i > 65535) {
    retval = -1;
  } else {
    server_port = i;
  }
  SL_RETURN( (retval), _("sh_forward_set_port"));
}

static struct in_addr server_interface;
static int            use_server_interface = 0;

int sh_forward_set_interface (char * str)
{
  if (0 == strcmp(str, _("INADDR_ANY")))
    {
      use_server_interface = 0;
      return 0;
    }
  if (0 == /*@-unrecog@*/inet_aton(str, &server_interface)/*@+unrecog@*/) 
    {
      use_server_interface = 0;
      return -1;
    }
  use_server_interface = 1;
  return 0;
}

/* ------------  print error --------------
 */
struct sock_err_st {
  char msg[128];
  int  errnum;
  int  port;
  int  line;
  int  euid;
};

static struct sock_err_st sock_err[2];

void sh_forward_printerr(char * str, int errnum, unsigned int port, int line)
{
  int slot = 0;

  if (port != server_port)
    slot = 1;
  if (str == NULL)
    sock_err[slot].msg[0] = '\0';
  else
    sl_strlcpy(sock_err[slot].msg, str, 128);
  sock_err[slot].errnum = errnum;
  sock_err[slot].port   = port;
  sock_err[slot].line   = line;
  sock_err[slot].euid   = (int) geteuid();
}

int sh_forward_printerr_final(int slot)
{
  SL_ENTER(_("sh_forward_printerr_final"));
  if (sock_err[slot].msg[0] != '\0')
    {
      dlog(1, FIL__, __LINE__, 
         _("Could not set up the listening socket for the server because of the\nfollowing error: %s\nPossible reasons include:\n - insufficient privilege for UID %d, or\n - the port %d is already used by another program.\n"),
         sh_error_message(sock_err[slot].errnum), sock_err[slot].euid, 
         sock_err[slot].port);
      sh_error_handle((-1), FIL__, sock_err[slot].line, 
                  sock_err[slot].errnum, MSG_EXIT_ABORTS,
                  sh_error_message(sock_err[slot].errnum),
                  sh.prg_name,
                  sock_err[slot].msg);
      SL_RETURN((-1), _("sh_forward_printerr_final"));
    }
  SL_RETURN(0, _("sh_forward_printerr_final"));
}

static   sh_conn_t        * conns = NULL;
#define  TIME_OUT_DEF 300
static   int  maxconn = 0;  /* maximum number of simultaneous connections */


#ifdef INET_SYSLOG
#define INET_SUSPEND_TIME 180       /* equal to 3 minutes */
#define SH_MINSOCK 3
int create_syslog_socket (int flag);
static int recv_syslog_socket   (int fd);
static int syslog_sock = -1;
#else
#define SH_MINSOCK 2
#endif

extern int pf_unix_fd;

/* the tcp socket, and the function to establish it
 */
static int sh_tcp_sock = -1;

int sh_create_tcp_socket ()
{
  struct sockaddr_in addr;
  int addrlen      = sizeof(addr);

  int sock   = -1;
  int errnum = 0;
  int flag   = 1; /* non-zero to enable an option */

  SL_ENTER(_("sh_create_tcp_socket"));

  sh_forward_printerr (NULL, 0, server_port, __LINE__);

  /* create the socket, bind() it and listen()
   */
  if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
    {
      errnum = errno; 
      sh_forward_printerr (_("socket"), errnum, server_port, __LINE__);
      SL_RETURN((-1), _("sl_create_tcp_socket"));
    }
  (void) retry_fcntl( FIL__, __LINE__, sock, F_SETFD, 1 );
  
  if ( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
              (void *) &flag, sizeof(flag)) < 0 )
    {
      errnum = errno;
      sh_forward_printerr (_("setsockopt"), errnum, server_port, __LINE__);
      SL_RETURN((-1), _("sl_create_tcp_socket"));
    }
  
  addr.sin_family      = AF_INET;
  addr.sin_port        = htons(server_port);
  if (use_server_interface == 0)
    addr.sin_addr.s_addr = INADDR_ANY;
  else
    memcpy(&addr.sin_addr, &server_interface, sizeof(struct in_addr));
  
  if ( bind(sock, (struct sockaddr *) &addr, addrlen) < 0) 
    {
      errnum = errno;
      sh_forward_printerr (_("bind"), errnum, server_port, __LINE__);
      SL_RETURN((-1), _("sl_create_tcp_socket"));
    }
  
  if ( retry_fcntl( FIL__, __LINE__, sock, F_SETFL, O_NONBLOCK ) < 0 )
    {
      errnum = errno;
      sh_forward_printerr (_("fcntl"), errnum, server_port, __LINE__);
      SL_RETURN((-1), _("sl_create_tcp_socket"));
    }
  
  if ( listen(sock, 5) < 0)
    {
      errnum = errno;
      sh_forward_printerr (_("listen"), errnum, server_port, __LINE__);
      SL_RETURN((-1), _("sl_create_tcp_socket"));
    }

  sh_tcp_sock = sock;

  SL_RETURN((sock), _("sl_create_tcp_socket"));
}

/*****************************************
 *
 * This is the server main loop.
 *
 * The server is set up for listening, and
 * and starts a select() loop.
 *
 *****************************************/

void sh_receive()
{
#ifdef SH_USE_XML
  extern int  sh_log_file    (char * message, char * inet_peer);
#endif

  int                sock = -1;
  sh_conn_t        * cx;
  fd_set             readset;
  fd_set             writeset;
  struct timeval     tv;
  int                num_sel;
  int                errnum;
  int                nowconn;
  int                status;
  int                high_fd;
  register int       i;
  unsigned long      time_now;
  unsigned long      time_last = 0;
  unsigned long      time_out = TIME_OUT_DEF;  
  
  time_t told;
  time_t tcurrent;

  unsigned long tchkold;

  struct  sigaction  new_act;
  struct  sigaction  old_act;
  
  SL_ENTER(_("sh_receive"));

  /* ignore SIGPIPE (instead get EPIPE if connection is closed)
   *      --- we have called sh_unix_init() already ---
   */
  new_act.sa_handler = SIG_IGN;
  retry_sigaction (FIL__, __LINE__, SIGPIPE, &new_act, &old_act);

  if ( sh_forward_printerr_final(0) < 0)
    {
      aud_exit(FIL__, __LINE__, EXIT_FAILURE);
    }
  sock = sh_tcp_sock;

  /* ****************************************************************
   *
   * This is a non-forking server. We use select() on the listen()
   * socket to watch for new connections. For new connections, accept()
   * will return a new socket that is put in the read/write filesets.
   * Data about active connections are kept in the 'conns' table. 
   *
   ******************************************************************/
  
  /* The table to hold info on sockets.
   * We reserve 6 file descriptors for misc. use.
   * The POSIX lower limit on open files seems to be eight. 
   */
  maxconn = get_open_max() - 6;
  maxconn = (((int)FD_SETSIZE) < maxconn) ? FD_SETSIZE : maxconn;

  conns   = SH_ALLOC (sizeof(sh_conn_t) * maxconn);

  sh_error_handle((-1), FIL__, __LINE__, 0, MSG_START_SRV,
              (maxconn-1), sock);

  /* timer
   */
  tcurrent                   = (unsigned long) time (NULL);
  told                       = tcurrent;

  tchkold                    = tcurrent;
  
  for (i = SH_MINSOCK; i < maxconn; ++i)
    {
      conns[i].buf         = NULL;
      conns[i].K           = NULL;
      conns[i].A           = NULL;
      conns[i].M1          = NULL;
      conns[i].FileName    = NULL;
      conns[i].fd          = -1;
      sh_forward_do_free ( &conns[i]);
    }
  
  /* status init
   */
  server_status.conn_open  = 0;
  server_status.conn_total = 0;
  server_status.conn_max   = maxconn-1;
  server_status.start      = time (NULL);
  server_status.last       = (time_t) 0;

  nowconn    = 1;
  tv.tv_sec  = 5;
  tv.tv_usec = 0;
  
  /* conns[0] is the listen() socket. Always in read mode.
   */
  conns[0].fd    = sock;
  conns[0].state = CONN_READING;
  high_fd = sock;
  
  conns[1].fd    = pf_unix_fd;
  conns[1].state = CONN_READING;
  high_fd = (pf_unix_fd > high_fd) ? pf_unix_fd : high_fd;
  
#ifdef INET_SYSLOG
  conns[2].fd = -1;
  if ( sh_forward_printerr_final(1) < 0)
    {
      SH_FREE(conns);
      conns = NULL;
      aud_exit(FIL__, __LINE__, EXIT_FAILURE);
    }
  sock = syslog_sock;

  if (sock >= 0)
    {
      conns[2].fd    = sock;
      conns[2].state = CONN_READING;
      high_fd = (high_fd > conns[2].fd) ? high_fd : conns[2].fd;
    }
#endif
  
  sh_html_write(all_clients);
  
  /* This is the select() loop.
   */
  while (1 == 1)
    {

    if (sig_raised > 0)
      {
      TPT((0, FIL__, __LINE__, _("msg=<Process a signal.>\n")))

      if (sig_termfast == 1)  /* SIGTERM */
        {
          TPT((0, FIL__, __LINE__, _("msg=<Terminate.>\n")));
          strncpy (sh_sig_msg, _("SIGTERM"), 20);
          --sig_raised; --sig_urgent;
          aud_exit (FIL__, __LINE__, EXIT_SUCCESS);
        }
        
      if (sig_config_read_again == 1)
        {
          TPT((0, FIL__, __LINE__, _("msg=<Re-read configuration.>\n")));
          sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_RECONF);


          /* -- Delete the name server cache. --
           */

          delete_cache();
#if defined(WITH_EXTERNAL)
          /* -- Delete list of external tasks. --
           */
          (void) sh_ext_cleanup();
#endif
          /* - mark all clients dead
           * - read configuration file
           * - remove clients still dead
           */
          sh_forward_mark_dead ();

#if defined(SH_WITH_MAIL)
          reset_count_dev_mail();
#endif
          reset_count_dev_console();
          reset_count_dev_time();
          sl_trust_purge_user();

          (void) sh_readconf_read ();
          for (i = SH_MINSOCK; i < maxconn; ++i)
            if (conns[i].state != CONN_FREE   && 
              conns[i].client_entry != NULL &&
              conns[i].client_entry->dead_flag == 1)
            sh_forward_do_free ( &conns[i]);
          sh_forward_clean_tree ();

          sig_config_read_again = 0;
          --sig_raised;
        }

      if (sig_fresh_trail == 1) /* SIGIOT */
        {
          /* Logfile access 
           */
#ifdef SH_USE_XML
          sh_log_file (NULL, NULL);
#endif
          TPT((0, FIL__, __LINE__, _("msg=<Logfile stop/restart.>\n")));
          sh_error_only_stderr (S_TRUE);
          sh_unix_rm_lock_file(sh.srvlog.name);
          retry_msleep(3, 0);
          sh.flag.log_start = S_TRUE;
          sh_error_only_stderr (S_FALSE);
          sig_fresh_trail       = 0;
          --sig_raised;
        }
      
        
      if (sig_terminate == 1 && nowconn < 2)  /* SIGQUIT */
        {
          TPT((0, FIL__, __LINE__, _("msg=<Terminate.>\n")));
          strncpy (sh_sig_msg, _("SIGQUIT"), 20);
          --sig_raised; --sig_urgent;
          aud_exit (FIL__, __LINE__, EXIT_SUCCESS);
        }
      
        
      if (sig_debug_switch == 1)  /* SIGUSR1 */
        {
          TPT((0, FIL__, __LINE__, _("msg=<Debug switch.>\n")));
          sh_error_dbg_switch();
          sig_debug_switch = 0;
          --sig_raised;
        }
      
      if (sig_suspend_switch == 1)  /* SIGUSR2 */
        {
          TPT((0, FIL__, __LINE__, _("msg=<Suspend switch.>\n")));
          if (sh_global_suspend_flag == 1) {
            sh_global_suspend_flag = 0;
          } else {
            sh_error_handle((-1), FIL__, __LINE__, 0, MSG_SUSPEND, 
                        sh.prg_name);
            sh_global_suspend_flag = 1;
          }
          sig_suspend_switch = 0;
          --sig_raised; --sig_urgent;
        }

      sig_raised = (sig_raised < 0) ? 0 : sig_raised;
      sig_urgent = (sig_urgent < 0) ? 0 : sig_urgent;
      TPT((0, FIL__, __LINE__, _("msg=<End signal processing.>\n")));
      }
      
      if (sh_global_suspend_flag == 1)
      {
        (void) retry_msleep (1, 0);
        continue;
      }

      /* Recompute the descriptor set. select() modifies it,
       * thus we update it using the info from the connection table.
       * Also recompute the number of open connections.
       */
      FD_ZERO( &readset );
      FD_ZERO( &writeset );
      FD_SET(conns[0].fd, &readset );
      high_fd   = conns[0].fd;

      if (conns[1].fd > -1)
      {
        FD_SET(conns[1].fd, &readset );
        high_fd   = (high_fd > conns[1].fd) ? high_fd : conns[1].fd;
      }

#ifdef INET_SYSLOG
      if (conns[2].fd > -1)
      {
        FD_SET(conns[2].fd, &readset );
        high_fd   = (high_fd > conns[2].fd) ? high_fd : conns[2].fd;
      }
#endif

      time_now  = (unsigned long) time (NULL);
      nowconn   = 1;
      
      for (i = SH_MINSOCK; i < maxconn; ++i)
      {
        /* eliminate timed out connections
         */
        if (conns[i].state != CONN_FREE) 
          {
            if (time_now-conns[i].timer > time_out)
            {
              sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_TIMOUT,
                          conns[i].peer);
              sh_forward_do_free ( &conns[i]);
            }
            else
            ++nowconn;
          }
        
        
        if       (conns[i].state   == CONN_READING)
          { 
            FD_SET(conns[i].fd, &readset);
            high_fd = (high_fd < conns[i].fd ? conns[i].fd : high_fd);
          }
        else if  (conns[i].state   == CONN_SENDING)
          {
            FD_SET(conns[i].fd, &writeset);
            high_fd = (high_fd < conns[i].fd ? conns[i].fd : high_fd);
          }
      }

      /* -- Exponentially reduce timeout limit if more than 1/2 full. --
       */
      if (nowconn > (maxconn/2))
      time_out = ( (time_out/2) > 1) ? (time_out/2) : 1;
      else
      time_out = TIME_OUT_DEF;
      
      
      
      /* -- Do the select(). --
       */
      num_sel = select(high_fd+1, &readset, &writeset, NULL, &tv);
      errnum  = errno;
      
      /* reset timeout - modified by select() on some systems
       */
      tv.tv_sec  = 5;
      tv.tv_usec = 0;
      

      if ( (time_now - time_last) > 2L)
      {
        time_last = time_now;
        if (sh_html_write(all_clients) < 0)
          sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_HTML);
      }
      
      
      /* Error handling.
       */
      if ( num_sel < 0 )        /* some error             */
      {
        if (sig_raised == 1)
          {
            sig_raised = 2;
            continue;
          }
        if ( errnum == EINTR)
          continue;       /* try again              */
        sh_error_handle((-1), FIL__, __LINE__, errnum, MSG_EXIT_ABORTS,
                    sh_error_message(errnum), 
                    sh.prg_name,
                    _("select"));
        aud_exit(FIL__, __LINE__,  EXIT_FAILURE );
      }
      

      /* log the timestamp
       */
      if ((tcurrent - told) > sh.looptime )
      {
        told = tcurrent;
#ifdef MEM_DEBUG
        sh_mem_check();
        sh_unix_count_mlock();
#else
        sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_STAMP);
#endif
      }

#if defined(SH_WITH_MAIL)
      /* 
       * flush the mail queue
       */
      if (tcurrent - sh.mailTime.alarm_last > sh.mailTime.alarm_interval) 
      {
        TPT((0, FIL__, __LINE__, _("msg=<Flush mail queue.>\n")))
        (void) sh_mail_msg (NULL);
        sh.mailTime.alarm_last = tcurrent;
      }
#endif
#ifdef MEM_DEBUG
      sh_mem_dump();
#endif

      tcurrent = (unsigned long) time (NULL);

      /* select() timeout handling.
       */
      if ( num_sel == 0 )       /* timeout - no connection */ 
      {
        if (sh_html_write(all_clients) < 0)
          sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_HTML);
        continue;
      }

      /* New connection.
       */
      if ( FD_ISSET(conns[0].fd , &readset )) /* a new connection   */
      {
        --num_sel;
        status = 0;
        if (nowconn < maxconn && sig_terminate == 0 && sig_termfast == 0)
          {
            i = SH_MINSOCK;
            while (i < maxconn)
            {
              if (conns[i].state == CONN_FREE)
                {
                  status = sh_forward_accept (conns[0].fd, &conns[i]);
                  if (status == 0)
                  {
                    high_fd = 
                      (high_fd > conns[i].fd ? high_fd : conns[i].fd);
                    ++server_status.conn_open;
                    ++server_status.conn_total;
                    server_status.last = time (NULL);
                  }
                  break;
                }
              ++i;
            }
          }
        if (status == 0) 
          continue;
      }
      
      /* check for commands on the socket
       */
      if (conns[1].fd > (-1) && FD_ISSET(conns[1].fd , &readset ))
      {
        sh_socket_poll();
      }

#ifdef INET_SYSLOG
      if (conns[2].fd > (-1) && FD_ISSET(conns[2].fd , &readset ))
      {
        recv_syslog_socket (conns[2].fd);
      }
#endif

      /* Check for pending read/write on the rest of the sockets.
       */
      for ( i = SH_MINSOCK; num_sel > 0 && i < maxconn; ++i )
      {
        if (sig_termfast == 1)
          break;

        cx = &conns[i];
        if ( cx->state == CONN_READING &&
             FD_ISSET( cx->fd, &readset ) )
          {
            --num_sel;
            sh_forward_do_read ( cx );
          }
        else if ( cx->state == CONN_SENDING &&
                FD_ISSET( cx->fd, &writeset ) )
          {
            --num_sel;
            sh_forward_do_write ( cx );
          }
      }

      /* check for time limit exceeded
       */
      if ((tcurrent - tchkold) > (unsigned int) 3 )
      {
        tchkold = tcurrent;
        client_time_check(/* all_clients */);
      }
      tcurrent = tcurrent;
      
      /* seed / re-seed the PRNG if required
       */
      (void) taus_seed();
      
    }
  /* notreached */
}

void  free_client_tree ()
{
  SL_ENTER(_("free_client_tree"));
  zAVLFreeTree (all_clients, free_client);
  SL_RET0(_("free_client_tree"));
}

void sh_forward_free_all ()
{
  register int i;
  
  SL_ENTER(_("sh_forward_free_all"));

  if (conns != NULL)
    for (i = SH_MINSOCK; i < maxconn; ++i)
      {
      sh_forward_do_free ( &conns[i]);
      }


  free_client_tree ();

  if (conns != NULL)
    SH_FREE (conns);

  SL_RET0(_("sh_forward_free_all"));
}

#ifdef INET_SYSLOG

#ifdef HAVE_INET_ATON
static char * my_inet_ntoa(struct in_addr in)
{
  return inet_ntoa(in);
}
#else
static char * my_inet_ntoa(struct in_addr in)
{
  unsigned char a, b, c, d;
  static char   foo[16];
  char          bar[4];
  memcpy (bar, &(in.s_addr), 4); /* memory alignment (?) */
  memcpy (&a, &bar[0], 1);
  memcpy (&b, &bar[1], 1);
  memcpy (&c, &bar[2], 1);
  memcpy (&d, &bar[3], 1);
  sprintf(foo, "%d.%d.%d.%d",                       /* known to fit  */
        (int) a, (int) b, (int) c, (int) d);
  return foo;
}
#endif


/* Unlike Linux / FreeBSD, most systems don't define the stuff below
 * in syslog.h
 */

#ifndef LOG_FAC
#define LOG_FAC(p)      (((p) & LOG_FACMASK) >> 3)
#endif

#ifndef LOG_PRI
#define LOG_PRI(p)      ((p) & LOG_PRIMASK)
#endif

typedef struct sh_code {
        char    *c_name;
        int     c_val;
} SH_CODE;

SH_CODE sh_facilitynames[] =
{
#ifdef LOG_AUTH
  { N_("auth"), LOG_AUTH },
#endif
#ifdef LOG_AUTHPRIV 
  { N_("authpriv"), LOG_AUTHPRIV },
#endif
#ifdef LOG_CRON
  { N_("cron"), LOG_CRON },
#endif
#ifdef LOG_DAEMON
  { N_("daemon"), LOG_DAEMON },
#endif
#ifdef LOG_FTP
  { N_("ftp"), LOG_FTP },
#endif
#ifdef LOG_KERN
  { N_("kern"), LOG_KERN },
#endif
#ifdef LOG_LPR
  { N_("lpr"), LOG_LPR },
#endif
#ifdef LOG_MAIL
  { N_("mail"), LOG_MAIL },
#endif
#ifdef INTERNAL_MARK
  { N_("mark"), INTERNAL_MARK },          /* INTERNAL */
#endif
#ifdef LOG_NEWS
  { N_("news"), LOG_NEWS },
#endif
#ifdef LOG_AUTH
  { N_("security"), LOG_AUTH },           /* DEPRECATED */
#endif
#ifdef LOG_SYSLOG
  { N_("syslog"), LOG_SYSLOG },
#endif
#ifdef LOG_USER
  { N_("user"), LOG_USER },
#endif
#ifdef LOG_UUCP
  { N_("uucp"), LOG_UUCP },
#endif
#ifdef LOG_LOCAL0
  { N_("local0"), LOG_LOCAL0 },
#endif
#ifdef LOG_LOCAL1
  { N_("local1"), LOG_LOCAL1 },
#endif
#ifdef LOG_LOCAL2 
  { N_("local2"), LOG_LOCAL2 },
#endif
#ifdef LOG_LOCAL3
  { N_("local3"), LOG_LOCAL3 },
#endif
#ifdef LOG_LOCAL4
  { N_("local4"), LOG_LOCAL4 },
#endif
#ifdef LOG_LOCAL5
  { N_("local5"), LOG_LOCAL5 },
#endif
#ifdef LOG_LOCAL6
  { N_("local6"), LOG_LOCAL6 },
#endif
#ifdef LOG_LOCAL7
  { N_("local7"), LOG_LOCAL7 },
#endif
  { NULL, -1 }
};
 

SH_CODE sh_prioritynames[] =
{  
#ifdef LOG_ALERT
  { N_("alert"), LOG_ALERT },
#endif
#ifdef LOG_CRIT
  { N_("crit"), LOG_CRIT },
#endif
#ifdef LOG_DEBUG
  { N_("debug"), LOG_DEBUG },
#endif
#ifdef LOG_EMERG
  { N_("emerg"), LOG_EMERG },
#endif
#ifdef LOG_ERR
  { N_("err"), LOG_ERR },
#endif
#ifdef LOG_ERR
  { N_("error"), LOG_ERR },               /* DEPRECATED */
#endif
#ifdef LOG_INFO
  { N_("info"), LOG_INFO },
#endif
#ifdef INTERNAL_NOPRI
  { N_("none"), INTERNAL_NOPRI },         /* INTERNAL */
#endif
#ifdef LOG_NOTICE
  { N_("notice"), LOG_NOTICE },
#endif
#ifdef LOG_EMERG
  { N_("panic"), LOG_EMERG },             /* DEPRECATED */
#endif
#ifdef LOG_WARNING
  { N_("warn"), LOG_WARNING },            /* DEPRECATED */
#endif
#ifdef LOG_WARNING
  { N_("warning"), LOG_WARNING },
#endif
  { NULL, -1 }
};

static int enable_syslog_socket = S_FALSE;

static int recv_syslog_socket (int fd)
{
  static time_t      return_next = 0;
  int                priority = 0;
  int                fac, pri;
  int                i;
  char             * cfac = NULL;
  char             * cpri = NULL;
  int                res;
  char             * tmp;
  char             * bptr;
  char             * ptr = NULL;
  char               buf[1048];
  struct sockaddr_in from;

  /* The 6th argument in recvfrom is *socklen_t in Linux and *BSD, 
   * but *int everywhere else. Because socklen_t is unsigned int, there
   * should be no problem as long as  sizeof(struct sockaddr_in) < INT_MAX ...
   */
  int                fromlen = sizeof(from);

  if (enable_syslog_socket == S_FALSE)
    return 0;

  SL_ENTER(_("recv_syslog_socket"));

  if (return_next > 0)
    {
      if ( (time(NULL) - return_next) < 2)
      SL_RETURN( 0, _("recv_syslog_socket"));
      else
      return_next = 0;
    }

  res = recvfrom(fd,  buf,  1047, 0, (struct sockaddr *) &from, &fromlen);

  if (res > 0)
    {
      res = (res < 1047) ? res : 1047; 
      buf[res] = '\0';
      if (res > 1 && buf[res-1] == '\n')
      buf[res-1] = '\0';

      /* here we expect an xml formatted message, thus we don't
       escape xml special chars (flag == 0) */
      /* commented out to not escape twice    */
      /* bptr = sh_tools_safe_name(buf, 0);   */
      bptr = buf;

      if (!bptr || !(*bptr))
      {
        res = errno;
        TPT(( 0, FIL__, __LINE__, _("msg=<UDP error: %d>\n"), res));
        sh_error_handle((-1), FIL__, __LINE__, res, MSG_ERR_SYSLOG,
                    sh_error_message(res), my_inet_ntoa(from.sin_addr));
        SL_RETURN( (-1), _("recv_syslog_socket"));
      }      

      TPT(( 0, FIL__, __LINE__, _("msg=<UDP message from %s>\n"),
          my_inet_ntoa(from.sin_addr)));
      ptr = bptr;
      i = 0;
      if (*ptr == '<') 
      {
        ++ptr; ++i;
        while (i < res &&
             (unsigned char) *ptr > 47 && (unsigned char) *ptr < 58)
          {
            priority = 10 * priority + (*ptr - '0');
            ++ptr;
            ++i;
          }
        if (*ptr == '>')
          ++ptr;
      }
      fac = LOG_FAC(priority);
      i = 0; 
      while (sh_facilitynames[i].c_name != NULL)
      {
        if (sh_facilitynames[i].c_val == (fac<<3))
          { cfac = sh_util_strdup(_(sh_facilitynames[i].c_name)); break; }
        ++i;
      }
      pri = LOG_PRI(priority);
      i = 0; 
      while (sh_prioritynames[i].c_name != NULL)
      {
        if (sh_prioritynames[i].c_val == pri)
          { cpri = sh_util_strdup(_(sh_prioritynames[i].c_name)); break; }
        ++i;
      }

      /* here we do not expect an xml formatted message, thus we escape
       xml special chars (flag == 1) */
      tmp = sh_tools_safe_name (ptr, 1);
      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_INET_SYSLOG,
                  my_inet_ntoa(from.sin_addr), 
                  (cfac == NULL) ? _("none") : cfac, 
                  (cpri == NULL) ? _("none") : cpri, 
                  (ptr  == NULL) ? _("none") : ptr);
      if (cfac != NULL)
      SH_FREE(cfac);
      if (cpri != NULL)
      SH_FREE(cpri);
      SH_FREE(tmp);
      /* SH_FREE(bptr); */
    }

  else if (res < 0 && errno != EINTR)
    {
      res = errno;
      TPT(( 0, FIL__, __LINE__, _("msg=<UDP error: %d>\n"), res));
      sh_error_handle((-1), FIL__, __LINE__, res, MSG_ERR_SYSLOG,
                  sh_error_message(res), my_inet_ntoa(from.sin_addr));

      /* don't accept anything the next 2 seconds
       */
      return_next = time(NULL);
      SL_RETURN( (-1), _("recv_syslog_socket"));
    }      
  SL_RETURN( (0), _("recv_syslog_socket"));
}

int set_syslog_active(char * c)
{
  return sh_util_flagval(c, &enable_syslog_socket);
}

/* callerFlag == S_TRUE means override the enable_syslog_socket flag
 */
int create_syslog_socket (int callerFlag)
{
  int                flag = 1;  /* non-zero to enable an option */
  int sock;
  int errnum;
  int res;
  struct sockaddr_in addr;
  int addrlen      = sizeof(addr);

  SL_ENTER(_("create_syslog_socket"));

  if (callerFlag == S_FALSE)
    {
      if (enable_syslog_socket == S_FALSE && syslog_sock >= 0)
      {
        /* user does not wish to use this facility
         */
        TPT(( 0, FIL__, __LINE__, _("msg=<close syslog socket>\n")));
        close(syslog_sock);
        syslog_sock = -1;
      }
      SL_RETURN((-1), _("create_syslog_socket"));
    }

  sh_forward_printerr (NULL, 0, 514, __LINE__);

  /* create the socket, bind() it and listen()
   */
  sock = socket(AF_INET, SOCK_DGRAM, 0);

  if (sock < 0)
    {
      errnum = errno; 
      sh_forward_printerr (_("syslog socket"), errnum, 514, __LINE__);
      SL_RETURN((-1), _("create_syslog_socket"));
    }
  (void) retry_fcntl( FIL__, __LINE__, sock, F_SETFD, 1 );
  
  if ( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
              (void *) &flag, sizeof(flag)) < 0 )
    {
      errnum = errno;
      sh_forward_printerr (_("syslog setsockopt SO_REUSEADDR"), 
                     errnum, 514, __LINE__);
      SL_RETURN((-1), _("create_syslog_socket"));
    }

#if defined(SO_BSDCOMPAT)
  if ( setsockopt(sock, SOL_SOCKET, SO_BSDCOMPAT,
              (void *) &flag, sizeof(flag)) < 0 )
    {
      errnum = errno;
      sh_forward_printerr (_("syslog setsockopt SO_BSDCOMPAT"), 
                     errnum, 514, __LINE__);
      SL_RETURN((-1), _("create_syslog_socket"));
    }
#endif
  
  memset(&addr, 0, sizeof(addr));
  addr.sin_family      = AF_INET;
  addr.sin_port        = htons(514);
  
  res = bind(sock, (struct sockaddr *) &addr, addrlen);

  if ( res < 0) 
    {
      errnum = errno;
      sh_forward_printerr (_("syslog bind"), errnum, 514, __LINE__);
      close(sock);
      SL_RETURN((-1), _("create_syslog_socket"));
    }

  syslog_sock = sock;

  SL_RETURN((sock), _("create_syslog_socket"));
}
/* #ifdef INET_SYSLOG */
#endif



/* #ifdef SH_WITH_SERVER */
#endif


  




Generated by  Doxygen 1.6.0   Back to index