

/******************************************************************************
 *
 * RCS ID
 * $Id: server,v 1.14 2002/11/17 16:43:22 david Exp $
 *
 * HISTORY
 * $Log: server,v $
 * Revision 1.14  2002/11/17 16:43:22  david
 * Updates to compiler with new 32-bit compiler
 *
 * Revision 1.13  2000/04/23 16:48:31  david
 * Changes to attempt fix for copying large files from Windows to
 * RISCOS problem. Fixes failed!
 *
 * Revision 1.12  2000/04/22 14:50:49  david
 * Updates for nmbd module debugging.
 * Updates for integrating nmbd with main smbserver application
 *
 * Revision 1.11  2000/04/17 16:46:01  david
 * updates after testing event poll word non zero stuff
 *
 * Revision 1.10  2000/04/02 16:57:08  david
 * Updates to use the Internet event, use wimp poll event,
 * use non blocking sockets correctly,
 * compile libs in format suitable for RISCOS module
 * new nmbd module
 *
 * Revision 1.9  2000/02/22 19:41:40  david
 * Added setting of client socket buffer sizes.
 *
 * Revision 1.8  2000/02/17 22:07:15  david
 * Added saved_challenge and challenge_sent to tClients for encryption
 * negprot - changed a DEBUG 0 to 1
 *
 * C
 *
 * CVS:
 *
 * Revision 1.7  2000/02/08 20:16:25  david
 * Tidied up exit calls
 *
 * Revision 1.6  2000/01/29 17:19:51  david
 * Updates to support passwords
 * upped to version 0.06
 *
 * Revision 1.5  1999/11/28 12:05:42  david
 * Changed SAFTEY_MARGIN to be the same as MAX_RECV so that smbserver
 * will always be able to receive a buffer's worth of data even if there
 * is a maximum size message (65539bytes) in the input buffer.
 *
 * Revision 1.4  1999/11/23 22:00:16  david
 * Updates for long filename support.
 * Added files for Coreplus, Lanman1, Lanman2 and NT protorocols.
 *
 * Revision 1.3  1999/11/07 15:11:53  david
 * version:updated!
 * wimp:no longer used -> now using toolbox lib instead
 *
 * Revision 1.2  1999/06/20 13:43:47  david
 * Minor change preparing to call process_smb instead of
 * netbiosin?
 *
 * Revision 1.1  1999/05/16 12:00:07  david
 * Initial revision
 *
 *
 *****************************************************************************/

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

#include "kernel.h"

#include "socklib.h"
#include "unixlib.h"
#include "inetlib.h"
/*#include "sockets:include.h.errno"*/
#include "sys/errno.h"
#include "netdb.h"
/*#include "sockets:include.h.netdb"*/
#include "sys/ioctl.h"


#include "mytypes.h"

#include "local.h"

#include "byteorder.h"


#include "smb.h"

#include "smbserver.h"


#include "smbswi.h"


#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE  1
#endif

/* --- Conversion macros --- */
/* These macros convert between sprite coords and work area coords */



/* --- Program Globals --- */

#define MAX_MSG_LEN MAX_RECV

#define MALLOC(result, type, number) do \
   {                       \
      if (!((result) = (type *) malloc ((number) * sizeof (type))))   \
         exit (1);                               \
   } while (0)

#define FREE(x) (free(x))

#ifndef MAX
#define MAX(A,B) ((A) > (B) ? (A) : (B))
#endif

extern BOOL dbghdr( int level, char *file, char *func, int line );
extern void writeLog(char *s, int fd, char *buf, int n);
extern void wimp_werr(int severity,char *msg);
extern int mySockOpts(int s);
extern int deregisterSocket(int whichSet, int fd);
extern int registerSocket(int whichSet, int fd);
extern int smb_len(char *buf);
extern int netbios_in(tpClients aclient, char *inbuf, char *outbuf,
               int size,
               int bufsize);
extern void show_msg(char *buf);
extern void WimpPoll(void);
extern int lp_socket_rx_buffer_size(void);
extern int lp_socket_tx_buffer_size(void);
extern int svrTest(tpClients aclient, char *inbuf, char *outbuf,
            int bytes_recv, int bufsize);
extern void samba_timeout(void);
extern void restoreDirs(void);




int smbSelect(int whichSet,fd_set *input_set, fd_set *output_set);
int smbd_print_fds(fd_set *set);
int fdReadClear(int whichSet, int fd);


/*************************** TYPE DECLARATIONS  *******************************/

/*
 * text buffer code lifted from some MUD code I had at university -
 * the source the code is not commented or credited or copyrighted
 * to an author - so I am assuming it is public domain and OK to copy
 * I've brought it into line with DPMR coding standard.
 * The use of text buffers is over kill - see comment in BridgeServer()
 */


extern char *getcwd2 (char *, size_t);

tpClients clients;

int serverFd;

/*************** EXTERNAL GLOBAL VARIABLE DECLARATIONS SECTION ****************/

extern int errno;
extern int Client;
extern int smb_read_error;
extern int max_send;
extern int max_recv;

/******************** GLOBAL VARIABLE DECLARATIONS SECTION ********************/

int shutdown_flag=0;
extern char               *OutBuffer;

fd_set input_set;

/**************************** LOCAL ROUTINES SECTION **************************/



/****************************************************************************
 * ROUTINE     : error
 * INPUTS      : char * pStr
 * OUTPUTS     : None
 * RETURNS     : None
 * GLOBALS     : None
 * DESCRIPTION : print an error message and exit.
 ******************************************************************************/
void error (char * pStr)
{
   /*perror (pStr);*/
   DEBUG(0,("%s\n",pStr));
   exit (1);
}

/****************************************************************************
 * ROUTINE     : MakeTextBlock
 * INPUTS      : const char *s,    ( pointer to text buffer )
 *               int n             ( size of block )
 * OUTPUTS     : None
 * RETURNS     : None
 * GLOBALS     : None
 * DESCRIPTION : Given a buffer, creates and returns text block structure.
 ******************************************************************************/
tpTextBlock MakeTextBlock( char *s, int n)
{
   tpTextBlock p;

   MALLOC(p, tTextBlock , 1);
   MALLOC(p->buf, char, n);
   bcopy (s, p->buf, n);
   p->nchars = n;
   p->start = p->buf;
   p->nxt = NULL;
   return p;
}

/****************************************************************************
 * ROUTINE     : FreeTextBlock
 * INPUTS      : tpTextBlock t
 * OUTPUTS     : None
 * RETURNS     : None
 * GLOBALS     : None
 * DESCRIPTION : Frees the given text block.
 ******************************************************************************/
void FreeTextBlock (tpTextBlock t)
{
   FREE (t->buf);
   FREE ((char *) t);
}

/****************************************************************************
 * ROUTINE     : AddToQueue
 * INPUTS      : tpTextQueue q,
 *               const char *b,   ( text buffer )
 *               int n            ( size of buffer )
 * OUTPUTS     : None
 * RETURNS     : None
 * GLOBALS     : None
 * DESCRIPTION : Adds a text block structure containing the text buffer to
 *               the end of the given text queue.
 ******************************************************************************/
void AddToQueue(tpTextQueue q,  char *b, int n)
{
   tpTextBlock p;

   if (n == 0) return;

   p = MakeTextBlock (b, n);
   p->nxt = NULL;
   *q->tail = p;
   q->tail = &p->nxt;
}

void AddToClientQueue(char *b, int n)
{
   tpClients aclient=clients;

   while(aclient!=NULL)
   {

     if (aclient->clientFd!=0)
     {
        AddToQueue(&aclient->clientQ,b,n);
     }

     aclient=aclient->next;
   }
}



/*****************************************************************************
 * ROUTINE     : ProcessOutput
 * INPUTS      : int fd, tpTextQueue Queue
 * OUTPUTS     : None
 * RETURNS     : int
 * GLOBALS     : None
 * DESCRIPTION : Writes all the text buffers in the specified text queue to
 *               the specified file descriptor. Returns 1 if successful
 *               otherwise 0.
 ******************************************************************************/
int ProcessOutput(int fd, tpTextQueue Queue)
{
   tpTextBlock *qp, cur;
   int cnt;
   char logs[100];

   if (Queue->head==NULL)
   {
      return 1;
   }

   /* dodgy for loop if you ask me! DRHB */
   for (qp = &Queue->head; cur = *qp;)
   {
      writeLog("Write", fd, cur -> start, cur -> nchars);

      cnt = write (fd, cur -> start, cur -> nchars);
      sprintf(logs,"bytes written to %d,   %d\n",fd,cnt);
      writeLog(logs,0,NULL,0);

      if (cnt < 0)
      {
/* TM */
/* unecassry EWOULDBLOCK removed */
         return 0;
      }

      if (cnt == cur -> nchars)
      {
         if (!cur -> nxt)
         {
            Queue->tail = qp;
         }
         *qp = cur -> nxt;
         FreeTextBlock (cur);
         continue;           /* do not adv ptr */
      }
      cur -> nchars -= cnt;
      cur -> start += cnt;
      break;
   }
   return 1;
}

/*****************************************************************************
 * ROUTINE     : addrout
 * INPUTS      : long a
 * OUTPUTS     : None
 * RETURNS     : const char *
 * GLOBALS     : None
 * DESCRIPTION : Given an internet address, returns the host name. If host
 *               name lookup fails, returns address as "aa.bb.cc.dd".
 ******************************************************************************/
const char *addrout(long a)
{
   struct  hostent *he;
   extern  char    *inet_ntoa (long);

   he = gethostbyaddr ((char*)&a, sizeof(a), AF_INET);

   if (he)
   {
      return he->h_name;
   }
   else
   {
      return inet_ntoa(a);
   }
}


/*****************************************************************************
 * ROUTINE     : MakeSocket
 * INPUTS      : int port
 * OUTPUTS     : None
 * RETURNS     : int
 * GLOBALS     : None
 * DESCRIPTION : Creats a socket on the given port, binds an address to it
 *               and listens to the socket for connections. Returns file
 *               descriptor of socket.
 ******************************************************************************/
int MakeSocket(int port)
{
   int s;
   struct sockaddr_in server;
   int opt;
   BOOL bound_socket=FALSE;
   char logs[100];

   s = socket (AF_INET, SOCK_STREAM, 0);
   if (s < 0)
   {
      /*perror*/ DEBUG(0,("creating stream socket"));
      exit (3);
   }

   opt = 1;
   if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR,
                    (char *) &opt, sizeof (opt)) < 0)
   {
      /*perror*/ DEBUG(0, ("setsockopt"));
      exit (1);
   }

/*
   if (socketioctl(s, FIONBIO, &opt)<0)
   {
      perror ("socketioctl");
      exit (1);
   }
*/
   server.sin_family = AF_INET;
   server.sin_addr.s_addr = INADDR_ANY;
   server.sin_port = htons (port);
#if 0
   if (bind (s, (struct sockaddr *) & server, sizeof (server)))
   {
       close(s);
       perror ("binding stream socket");
       exit (4);
   }
#else
   while (bound_socket==FALSE)
   {
     if (bind (s, (struct sockaddr *) & server, sizeof (server))<0)
     {
        close(s);
        s = socket (AF_INET, SOCK_STREAM, 0);
        if (s < 0)
        {
           /*perror*/ DEBUG(0, ("creating stream socket"));
           exit (3);
        }
        if (port<1024)
        {
           port = 6789;
        }
        else
        {
           port++;
        }
        server.sin_port = htons (port);
     }
     else
     {
        bound_socket=TRUE;

     }
   }
#endif

   if (port!=139)
   {
      sprintf(logs,"Bind to port 139 failed, using port %d instead!",port);
      wimp_werr(0,logs);
   }

   mySockOpts(s);
   registerSocket(SMBD_SOCK_SET,s);

   listen (s, 5);
   return s;
}

/*****************************************************************************
 * ROUTINE     : ReadData
 * INPUTS      : int *fd,char *buf
 * OUTPUTS     : None
 * RETURNS     : int
 * GLOBALS     : None
 * DESCRIPTION : Reads data from the given file descriptor into the specified
 *               buffer. Returns the size of the buffer read. If the read
 *               fails or the connection closed the descriptor is closed
 *               and set to 0.
 ******************************************************************************/
int ReadData(int *fd,char *buf, int offset)
{
   int     msgSize;
   int     msgSize2=0;
   int     readMore;

   do
   {
     readMore=0;
     msgSize = read (*fd, &buf[offset], MAX_MSG_LEN);

     /*printf("Data from %d : msgSize %d\n",*fd,msgSize);*/


     /* Read error ? */
     if (msgSize < 0)
     {
/* TM */
/* unecassary EWOULDBLOCK removed */
        DEBUG(0,("read failed, errno = %d\n", errno));
        close (*fd);
        /*error ("Read failed");*/
        *fd=0;
        msgSize2=msgSize;
     }

     /* Connection closed ? */
     else if (msgSize == 0)
     {
        /*printf("Connection closed\n");*/
        close (*fd);
        *fd=0;
        msgSize2=msgSize;
     }

     /* else if we filled the buffer try and read more */
     else
     {
        msgSize2+=msgSize;
        if (msgSize == MAX_MSG_LEN)
        {
          readMore=1;
          offset += msgSize;
        }
     }
   }
   while (readMore);

   writeLog("Read",*fd,buf,msgSize2);

   return msgSize2;
}


BOOL isFdSocket(int fd)
{
   tpClients aclient=clients;
   BOOL      result=FALSE;

   while (aclient!=NULL)
   {
      if (fd==aclient->clientFd)
      {
        result=TRUE;
        break;
      }
      aclient=aclient->next;
   }

   return result;
}


void closesocket(int *clientFd)
{
   tpClients aclient=clients;

   while (aclient!=NULL)
   {
      if (aclient->clientFd==*clientFd)
      {
         close(aclient->clientFd);
         aclient->clientFd=0;
         *clientFd=0;
         break;
      }
      aclient=aclient->next;
   }

}


void closesockets(void)
{
   tpClients aclient=clients;

   while (aclient!=NULL)
   {
      if (aclient->clientFd!=0)
      {
         deregisterSocket(SMBD_SOCK_SET,aclient->clientFd);
         close(aclient->clientFd);
         aclient->clientFd=0;
      }
      aclient=aclient->next;
   }

   close(serverFd);
   deregisterSocket(SMBD_SOCK_SET,serverFd);
}



void process_message(tpClients aclient, char *inbuf, char *outbuf,
                     int bytes_recv, int bufsize)
{
   int nsize;
   char logs[100];
   char *inbuf_ptr;
   int  nextMsgLength;
   int  bytes_received;
   int  abort_client=0;
   int  tempDebugLevel;
   extern int DEBUGLEVEL;

   bytes_received = aclient->writePtr+bytes_recv;

   /* find the beginning of the next message */
   inbuf_ptr = &inbuf[aclient->readPtr];

   /*
    * find out size of next message, if read less than 4 bytes then
    * don't know it so need to read more bytes first
    */
   if (bytes_received-aclient->readPtr <4)
      nextMsgLength = 0;
   else
      nextMsgLength = (smb_len(inbuf_ptr)+4);

#if 0
tempDebugLevel=DEBUGLEVEL;
DEBUGLEVEL=10;
#endif
   sprintf(logs,"about to process: readptr %d, writeptr %d, received %d, nextMsgLength %d",
           aclient->readPtr, aclient->writePtr, bytes_received, nextMsgLength);
   writeLog(logs,0,NULL,0);
#if 0
   if (aclient->writePtr==0)
   {
      writeLog("msg",aclient->clientFd,inbuf_ptr,12);
   }
#endif
#if 0
DEBUGLEVEL=tempDebugLevel;
#endif

   /*
    * while there are still bytes to read AND
    *       we know the size of the next message AND
    *       the next message is fully in the buffer...
    */
   while ( (aclient->readPtr < bytes_received)               &&
           nextMsgLength != 0                                 &&
           (aclient->readPtr+nextMsgLength <= bytes_received) &&
           abort_client == 0 )
   {
      sprintf(logs,"Processing: readptr %d, writeptr %d, received %d, nextMsgLength %d",
              aclient->readPtr, aclient->writePtr, bytes_received, nextMsgLength);
      writeLog(logs,0,NULL,0);

      /* advance the read pointer by the size of the message */
      aclient->readPtr += (smb_len(inbuf_ptr)+4);

      if (aclient->readPtr>(/*max_recv*/ BUFFER_SIZE + SAFETY_MARGIN))
      {
         /* fatal error - walked off end of buffer */
         /* according to Samba spec - should abort vitual circuit if this happens */
         abort_client = 1;
         /* exit loop */
         break;
      }

      /* let netbios and then Samaba hanndle the message */
      nsize=netbios_in(aclient, inbuf_ptr, outbuf, bytes_received, bufsize);
      /* nsize=process_smb(aclient, inbuf_ptr, outbuf); */

      /* if there is a reply then store it in the outgoing buffer */
      if(nsize > 0)
      {
         if (CVAL(outbuf,0) == 0)
         {
            show_msg(outbuf);
         }

         if (nsize != smb_len(outbuf) + 4)
         {
            DEBUG(0,("ERROR: Invalid message response size! %d %d\n",
                       nsize, smb_len(outbuf)));
         }
         else
         {
            AddToQueue(&aclient->clientQ,outbuf,nsize);
         }
      }

      /* find the beginning of the next message */
      inbuf_ptr = &inbuf[aclient->readPtr];

      /*
       * find out size of next message, if less than 4 bytes left then
       * don't know it so need to read more bytes first
       */
      if (bytes_received-aclient->readPtr <4)
         nextMsgLength = 0;
      else
         nextMsgLength = (smb_len(inbuf_ptr)+4);
   }


   if (abort_client==1)
   {
      aclient->clientFd=0;
      return ;
   }

   /*
    * If there is some bit of an unprocessed message left in the buffer AND
    * it's not at the beginning of the buffer then shift it down to the
    * beginning.
    */
   if (bytes_received-aclient->readPtr>0 &&
       aclient->readPtr!=0)
   {
      memcpy(inbuf,inbuf_ptr,bytes_received-aclient->readPtr);
      sprintf(logs,"ShiftedDown: leftover %d",
              bytes_received-aclient->readPtr);
      writeLog(logs,0,NULL,0);
   }

   /* work out where to write the next bytes received into */
   if (aclient->readPtr==0)
   {
      aclient->writePtr += bytes_recv;
   }
   else
   {
      aclient->writePtr = bytes_received-aclient->readPtr;
      aclient->readPtr  = 0;
   }

   sprintf(logs,"Processed: readptr %d, writeptr %d, received %d",
           aclient->readPtr, aclient->writePtr, bytes_received);
   writeLog(logs,0,NULL,0);

}

void refreshClients(void)
{
   tpClients aClient,prevClient;

   aClient=clients;
   prevClient=aClient;

   while (aClient!=NULL)
   {


     if (aClient->clientFd==0)
     {
        if (aClient==clients)
        {
DEBUG(0,("client %d is closed\n", aClient->clientFd2));
           deregisterSocket(SMBD_SOCK_SET,aClient->clientFd2);
           FD_CLR(aClient->clientFd2, &input_set);
           clients=aClient->next;
           free(aClient);
           aClient=clients;
           prevClient=aClient;
        }
        else
        {

           prevClient->next=aClient->next;
           free(aClient);
           aClient=prevClient->next;
        }
     }
     else
     {
        prevClient=aClient;
        aClient=aClient->next;
     }

   }

}

void newClient(int newClientFd)
{
   tpClients newClient;

   newClient=(tpClients) calloc(1,sizeof(tClients));

   newClient->clientFd=newClientFd;
   newClient->clientFd2=newClientFd;
   newClient->clientQ.head = NULL;
   newClient->clientQ.tail = &(newClient->clientQ.head);
   newClient->nativeOs[0] = '\0';
   newClient->nativeLanMan[0] = '\0';
   newClient->readPtr=0;
   newClient->writePtr=0;
   newClient->challenge_sent=False;
   newClient->next=clients;

   clients=newClient;
}

BOOL send_smb(int *fd,char *buffer)
{
   struct timeval     timeout2;
   int                maxd;
/* TM */
/* unecassary input_set removed */
   fd_set             output_set;
   BOOL               result=True;
   size_t len;
   size_t nwritten=0;
   int ret;
   len = smb_len(buffer) + 4;

   DEBUG(2,("AAA send_smb %d\n", *fd));


   maxd=(*fd)+1;

   timeout2.tv_sec = 0;
   timeout2.tv_usec = 0;

   while (nwritten < len)
   {

/* TM */
/* unecassary input_set removed */
     /* zero output set */
     FD_ZERO (&output_set);

     /* set "listening" socket */
     FD_SET ((*fd), &output_set);

     if (select (maxd,
                 NULL,
                 &output_set,
                 NULL,
                 &timeout2) < 0)
      {
      /*perror ("select");*/
      return False;
      }
      else
      {
         if (FD_ISSET ((*fd), &output_set))
         {
            ret = write(*fd,buffer+nwritten,len - nwritten);
            if (ret <= 0)
            {
  /* TM */
/* unecassary EWOULDBLOCK removed */
                DEBUG(0,("Error writing %d bytes to client. %d. Errno %d\n",len,ret,errno));
                  /*closesockets();*/
                  /*exit(1);*/
                  *fd=0;
                  Client=0;
                  return False;
            }
            else
            {
              nwritten += ret;
            }
         }
         if (nwritten < len)
         {
            /*event_process();*/
            WimpPoll();
         }
      }
   }

   return result;
}

ssize_t write_non_block(int *fd,char *buffer,size_t len)
{
   struct timeval     timeout2;
   int                maxd;
/* TM */
/* unnecassary input_set removed */
   fd_set             output_set;
   BOOL               result=True;
   size_t nwritten=0;
   int ret;

   DEBUG(2,("AAA write_non_block %d\n", *fd));

   maxd=(*fd)+1;

   timeout2.tv_sec = 0;
   timeout2.tv_usec = 0;

   while (nwritten < len)
   {
/* TM */
/* unecassary input_set removed */
     /* zero output set */
     FD_ZERO (&output_set);

     /* set "listening" socket */
     FD_SET ((*fd), &output_set);

/* TM */
/* unecassary input_set removed */
     if (select (maxd,
                 NULL,
                 &output_set,
                 NULL,
                 &timeout2) < 0)
      {
      /*perror ("select");*/
/* TM */
/* unecassary EINTR removed.
   WimpPoll not reached???
   return is not False but size -1 */
      return -1;
      }
      else
      {
         if (FD_ISSET ((*fd), &output_set))
         {
            ret = write(*fd,buffer+nwritten,len - nwritten);
            if (ret <= 0)
            {
/* TM */
/* unecassary EQOULDBLOCK removed */
                  DEBUG(0,("Error writing %d bytes to client. %d. Exiting\n",len,ret));
                  /*closesockets();*/
                  /*exit(1);*/
                  *fd=0;
                  Client=0;
                  return -1;
            }
            else
            {
              nwritten += ret;
            }
         }
         if (nwritten < len)
         {
            /*event_process();*/
            WimpPoll();
         }
      }
   }

   return nwritten;
}

BOOL read_non_block(int *fd,char *buffer,int bufsize)
{
   struct timeval     timeout2;
   int                maxd;
/* TM */
/* unecassary output_set removed */
   fd_set             input_set;
   BOOL               gotInput=False;
   int                msgsize=0;
   BOOL               result=True;

   DEBUG(2,("AAA read_non_block %d\n", *fd));


   maxd=(*fd)+1;

   timeout2.tv_sec = 0;
   timeout2.tv_usec = 0;

   while (gotInput==False)
   {

      /* zero input set */
      FD_ZERO (&input_set);

/* TM */
/* unecassary output_set removed */
      /* set "listening" socket */
      FD_SET ((*fd), &input_set);

      if (select (maxd,
                  &input_set,
                  NULL,
                  NULL,
                  &timeout2) < 0)
      {
/* TM */
/* unecassary EINTR removed.
   WimpPoll not reached??? */
            smb_read_error=READ_ERROR;
            /*perror ("select");*/
            return False;
      }
      else
      {
         if (FD_ISSET ((*fd), &input_set))
         {
            gotInput=True;

            /* read data from client  */
            msgsize=read(*fd,buffer,bufsize);

            /* if read failed or connection closed, close dest server */
            if (*fd==0)
            {
               result=False;
               msgsize=0;
            }
         }
         else
         {
            /* give RISCOS a look in!*/
            /*event_process();*/
            WimpPoll();
         }
      }
   }

   return msgsize;
}

BOOL receive_next_smb(int *fd,char *buffer,int bufsize, unsigned int timeout)
{
   struct timeval     timeout2;
   int                maxd;
/* TM */
/* unecassary output_set removed */
   fd_set             input_set, output_set;
   BOOL               gotInput=False;
   int                msgsize;
   BOOL               result=True;

   DEBUG(0,("AAA receive_next_smb %d\n", *fd));


   timeout2.tv_sec = 0;
   timeout2.tv_usec = 0;

   while (gotInput==False)
   {

/* TM */
/* unecassary output_set removed */
   /* zero input set */
      FD_ZERO (&input_set);

      /* set "listening" socket */
      FD_SET ((*fd), &input_set);

      maxd=(*fd)+1;

      if (select (maxd,
                  &input_set,
                  NULL,
                  NULL,
                  &timeout2) < 0)
      {
/* unnecassary EINTR removed.
   WimpPoll never reached??? */
            smb_read_error=READ_ERROR;
            /*perror ("select");*/
            return False;

      }
      else
      {
         if (FD_ISSET ((*fd), &input_set))
         {
            gotInput=True;

            /* read data from client  */
            msgsize=ReadData(fd,buffer,0);

            /* if read failed or connection closed, close dest server */
            if (*fd==0)
            {
               result=False;
            }
         }
         else
         {
            /* give RISCOS a look in!*/
            /*event_process();*/
            WimpPoll();
         }
      }
   }

   return result;
}

BOOL doInputs=True;
BOOL doOutputs=True;
BOOL isNullEvent=True;
BOOL callAgain=False;
BOOL didSomething=False;

void do_select(void)
{

   fd_set             event_input_set, output_set;
   struct timeval     timeout;
   int                maxd;
   int                ClientFd=0;
   struct sockaddr_in addr;
   int                addr_len;
   int                msgsize;
   tpClients          aclient;
   char               reportstring[100];
   int                sockRxBufSize = lp_socket_rx_buffer_size();
   int                sockTxBufSize = lp_socket_tx_buffer_size();
   int                sockErr;


   callAgain = FALSE;
   didSomething = FALSE;

   /* zero timeout value for select */

   timeout.tv_sec = 0;
   timeout.tv_usec = 0;

   /* zero input and output set */
   FD_ZERO (&event_input_set);
   FD_ZERO (&output_set);

   /* set "listening" socket */
   FD_SET (serverFd, &event_input_set);

   /*
    * if client file descriptor and server file descriptor are not 0
    * then set their bits in input and out set
    */

   aclient=clients;
   maxd=0;
   while (aclient!=NULL)
   {
      if (aclient->clientFd!=0)
      {
         FD_SET (aclient->clientFd, &event_input_set);
         FD_SET (aclient->clientFd, &output_set);

         if (aclient->clientFd>maxd)
         {
            maxd=aclient->clientFd;
         }
      }

      aclient=aclient->next;
   }

   maxd=MAX(maxd,serverFd)+1;

   /*
    * see if there is any activity on and of the 3 sockets
    * exit if there is an error
    */
   if (select (maxd, &event_input_set, &output_set,
                  (fd_set *) NULL, &timeout) < 0)
   {
     if (errno != EINTR)
     {
        /*perror ("select");*/
        return;
     }
   }
   else /* timeout or input/output detect/required */
   {
/*smbd_print_fds(&event_input_set);*/
      /* is there an incoming connection */
      if (FD_ISSET (serverFd, &event_input_set))
      {

DEBUG(1,("isNullEvent %d, select isset %d\n",isNullEvent,serverFd));

         /* accept the connection - exit if there is an error */
         /* should really put some code here to allow one connection at once! */
         addr_len = sizeof (addr);
         ClientFd = accept (serverFd, (struct sockaddr *) & addr,
                                &addr_len);
         if (ClientFd < 0)
         {
            return ;
         }
         else
         {

            sprintf (reportstring, "ACCEPT from %s(%d) on descriptor %d",
                     addrout (addr.sin_addr.s_addr),
                     ntohs (addr.sin_port), ClientFd);
            writeLog(reportstring,0,0,0);
            newClient(ClientFd);

            FD_SET (ClientFd, &input_set);

            sockErr = setsockopt(ClientFd, SOL_SOCKET, SO_RCVBUF,
                                 &sockRxBufSize, sizeof(sockRxBufSize));
            if (sockErr!=0)
            {
              DEBUG(0,("setsockopt failed for socket %d, buffer size %d\n",
                      ClientFd, sockRxBufSize));
            }
            sockErr = setsockopt(ClientFd, SOL_SOCKET, SO_SNDBUF,
                                 &sockTxBufSize, sizeof(sockTxBufSize));
            if (sockErr!=0)
            {
              DEBUG(0,("setsockopt failed for socket %d, buffer size %d\n",
                      ClientFd, sockTxBufSize));
            }

            mySockOpts(ClientFd);
            registerSocket(SMBD_SOCK_SET,ClientFd);
            callAgain=True;
            didSomething=True;
         }
      } /* if FD_ISSET sock */

      aclient=clients;
      while(aclient!=NULL)
      {
         /* see if there is some input from the client */
         if (aclient->clientFd!=0)
         {
            if (FD_ISSET (aclient->clientFd, &event_input_set))
            {
               didSomething=True;
DEBUG(1,("select isNullEvent %d, input_set isset %d\n",isNullEvent,aclient->clientFd));

if (doInputs==True)
{
               /* read data from client  */
               msgsize=ReadData(&(aclient->clientFd),aclient->inBuffer,aclient->writePtr);

               /* if read failed do nothing */
               if (aclient->clientFd==0)
               {

               }
               else /* process received message */
               {
                  Client=aclient->clientFd;
                  if (svrTest(aclient,aclient->inBuffer,OutBuffer,msgsize,max_send)==0)
                  {
                    process_message(aclient,aclient->inBuffer,OutBuffer,msgsize,max_send);
                  }
                  if (Client==0)
                  {
                     aclient->clientFd==0;
                  }
               }
}
            }
         } /* if ClientFd!=0 */
         aclient=aclient->next;
      }
         /*
          * the use of text buffers and checking IS_SET for output is overkill
          * and OTT, but is fail safe in case a socket is not ready for output
          * for some reason. Orginally I used ReadData to read in buffer
          * and then immediately used WriteData to write the data to the other
          * end which was OK, but I think checking IS_SET is safer if there
          * are problems.
          */

      aclient=clients;
      while(aclient!=NULL && doOutputs==True)
      {
         /* see if there is some input from the client */
         if (aclient->clientFd!=0)
         {

            if (FD_ISSET(aclient->clientFd, &output_set))
            {
DEBUG(1,("select isNullEvent %d, output_set isset %d\n",isNullEvent,aclient->clientFd));
               /* write the client output queue to the client if there is some  */
               /* if write fails close client - should it close server to?      */
               if (!ProcessOutput(aclient->clientFd,&aclient->clientQ))
               {
                  close(aclient->clientFd);
                  aclient->clientFd=0;
               }
            }
         } /* if ClientFd!=0 */

         aclient=aclient->next;
      }

   } /* if select */

   samba_timeout();
   refreshClients();
   restoreDirs();

}

void smbserver_select(void)
{

   fd_set             event_input_set, output_set;
   struct timeval     timeout;
   int                maxd;
   int                ClientFd=0;
   struct sockaddr_in addr;
   int                addr_len;
   int                msgsize;
   tpClients          aclient;
   char               reportstring[100];
   int                sockRxBufSize = lp_socket_rx_buffer_size();
   int                sockTxBufSize = lp_socket_tx_buffer_size();
   int                sockErr;


   callAgain = FALSE;
   didSomething = FALSE;

   DEBUG(0,("doInputs %d\n",doInputs));

   /* zero timeout value for select */

   timeout.tv_sec = 0;
   timeout.tv_usec = 0;

   /* zero input and output set */
   FD_ZERO (&event_input_set);
   FD_ZERO (&output_set);

   /* set "listening" socket */
   FD_SET (serverFd, &event_input_set);

   /*
    * if client file descriptor and server file descriptor are not 0
    * then set their bits in input and out set
    */

   aclient=clients;
   maxd=0;
   while (aclient!=NULL)
   {
      if (aclient->clientFd!=0)
      {
         FD_SET (aclient->clientFd, &event_input_set);
         FD_SET (aclient->clientFd, &output_set);

         if (aclient->clientFd>maxd)
         {
            maxd=aclient->clientFd;
         }
      }

      aclient=aclient->next;
   }

   maxd=MAX(maxd,serverFd)+1;

   /*
    * see if there is any activity on and of the 3 sockets
    * exit if there is an error
    */
   if (smbSelect (SMBD_SOCK_SET, &event_input_set, &output_set) < 0)
   {
     if (errno != EINTR)
     {
        /*perror ("select");*/
        return;
     }
   }
   else /* timeout or input/output detect/required */
   {
      DEBUG(0,("calling smbd_print_fds\n"));
      smbd_print_fds(&event_input_set);

      /* is there an incoming connection */
      if (FD_ISSET (serverFd, &event_input_set))
      {
         fdReadClear(SMBD_SOCK_SET, serverFd);
DEBUG(0,("isNullEvent %d, select isset %d\n",isNullEvent,serverFd));

         /* accept the connection - exit if there is an error */
         /* should really put some code here to allow one connection at once! */
         addr_len = sizeof (addr);
         ClientFd = accept (serverFd, (struct sockaddr *) & addr,
                                &addr_len);
         if (ClientFd < 0)
         {
            return ;
         }
         else
         {

            sprintf (reportstring, "ACCEPT from %s(%d) on descriptor %d",
                     addrout (addr.sin_addr.s_addr),
                     ntohs (addr.sin_port), ClientFd);
            writeLog(reportstring,0,0,0);
            newClient(ClientFd);

            FD_SET (ClientFd, &input_set);

            sockErr = setsockopt(ClientFd, SOL_SOCKET, SO_RCVBUF,
                                 &sockRxBufSize, sizeof(sockRxBufSize));
            if (sockErr!=0)
            {
              DEBUG(0,("setsockopt failed for socket %d, buffer size %d\n",
                      ClientFd, sockRxBufSize));
            }
            sockErr = setsockopt(ClientFd, SOL_SOCKET, SO_SNDBUF,
                                 &sockTxBufSize, sizeof(sockTxBufSize));
            if (sockErr!=0)
            {
              DEBUG(0,("setsockopt failed for socket %d, buffer size %d\n",
                      ClientFd, sockTxBufSize));
            }

            mySockOpts(ClientFd);
            registerSocket(SMBD_SOCK_SET,ClientFd);
            callAgain=True;
            didSomething=True;
         }
      } /* if FD_ISSET sock */

DEBUG(0,("clients 0x%x\n",clients));
      aclient=clients;
      while(aclient!=NULL)
      {
DEBUG(0,("clientFd %d\n",aclient->clientFd));
         /* see if there is some input from the client */
         if (aclient->clientFd!=0)
         {
DEBUG(0,("clientFd %d, isset %d\n",aclient->clientFd, FD_ISSET (aclient->clientFd, &event_input_set)));
            if (FD_ISSET (aclient->clientFd, &event_input_set))
            {
               fdReadClear(SMBD_SOCK_SET, aclient->clientFd);
               didSomething=True;
DEBUG(0,("select isNullEvent %d, input_set isset %d\n",isNullEvent,aclient->clientFd));

if (doInputs==True)
{
               /* read data from client  */
               msgsize=ReadData(&(aclient->clientFd),aclient->inBuffer,aclient->writePtr);

               /* if read failed do nothing */
               if (aclient->clientFd==0)
               {
DEBUG(0,("read failed\n"));
               }
               else /* process received message */
               {
                  Client=aclient->clientFd;
DEBUG(0,("client %d\n",Client));
                  if (svrTest(aclient,aclient->inBuffer,OutBuffer,msgsize,max_send)==0)
                  {
                    process_message(aclient,aclient->inBuffer,OutBuffer,msgsize,max_send);
                  }
DEBUG(0,("client %d\n",Client));
                  if (Client==0)
                  {
                     aclient->clientFd==0;
                  }
               }
}
            }
         } /* if ClientFd!=0 */
         aclient=aclient->next;
      }
         /*
          * the use of text buffers and checking IS_SET for output is overkill
          * and OTT, but is fail safe in case a socket is not ready for output
          * for some reason. Orginally I used ReadData to read in buffer
          * and then immediately used WriteData to write the data to the other
          * end which was OK, but I think checking IS_SET is safer if there
          * are problems.
          */

      aclient=clients;
      while(aclient!=NULL && doOutputs==True)
      {
         /* see if there is some input from the client */
         if (aclient->clientFd!=0)
         {

            if (FD_ISSET(aclient->clientFd, &output_set))
            {
DEBUG(0,("select isNullEvent %d, output_set isset %d\n",isNullEvent,aclient->clientFd));
               /* write the client output queue to the client if there is some  */
               /* if write fails close client - should it close server to?      */
               if (!ProcessOutput(aclient->clientFd,&aclient->clientQ))
               {
                  close(aclient->clientFd);
                  aclient->clientFd=0;
               }
            }
         } /* if ClientFd!=0 */

         aclient=aclient->next;
      }

   } /* if select */

   samba_timeout();
   refreshClients();
   restoreDirs();

}

void init_server(void)
{
    OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);

    serverFd = MakeSocket (139);

    FD_ZERO (&input_set);

    /* set "listening" socket */
    FD_SET (serverFd, &input_set);

    {
      int i;

      for (i=0;i<10;i++)
        do_select();
    }
}


void exit_server(char *reason)
{
   DEBUG(0,("exit server: %s\n",reason));
   closesockets();
   exit(0);
}

int fd_isset(int fd)
{
  return FD_ISSET(fd, &input_set);
}

void event_fd_set(int fd)
{

}


int mySockOpts(int s)
{
  int on = 1;

  if (socketioctl(s, FIONBIO, &on)<0)
  {
    return -1;
  }

  if (socketioctl(s, FIOASYNC, &on) < 0)
  {
    return -1;
  }

  return 0;
}

void close_sockets2(void)
{
  closesockets();
}

int registerSocket(int whichSet, int fd)
{
  _kernel_swi_regs r;

  r.r[0] = whichSet;
  r.r[1] = fd;

  _kernel_swi(SmbServer_FdSet, &r, &r);

  DEBUG(4,("registerSocket %d, set %d\n",fd,whichSet));

  return 0;
}

int deregisterSocket(int whichSet, int fd)
{
  _kernel_swi_regs r;

  DEBUG(4,("deregisterSocket %d, set %d\n",fd,whichSet));

  r.r[0] = whichSet;
  r.r[1] = fd;

  _kernel_swi(SmbServer_FdClr, &r, &r);

  return 0;
}

int fdReadClear(int whichSet, int fd)
{
  _kernel_swi_regs r;

  r.r[0] = whichSet;
  r.r[1] = fd;

  _kernel_swi(SmbServer_FdReadClr, &r, &r);

  return 0;
}

BOOL killSocket(int port,uint32 socket_addr)
{
  int i, fdTableSize;
  BOOL res=False;
  struct sockaddr_in sock;
  int namelen=sizeof(struct sockaddr_in);

  fdTableSize=getstablesize();
  if (fdTableSize>0)
  {
    for (i=0;i<fdTableSize;i++)
    {
      if (getsockname(i,(struct sockaddr *)&sock,&namelen)==0)
      {
         DEBUG(5,("socket %d, address %s, port %d\n",
                 i,inet_ntoa(sock.sin_addr),ntohs(sock.sin_port)));
         if (port==ntohs(sock.sin_port) &&
             socket_addr==ntohl(sock.sin_addr.s_addr))
         {
           DEBUG(0,("omniclient is using socket %d for port %d\n",
                    i,port));
           close(i);
           res=True;
           break;
         }
      }

    }
  }

  return res;
}

int smbSelect(int whichSet,fd_set *input_set, fd_set *output_set)
{
 _kernel_swi_regs r;

  r.r[0] = whichSet;

  _kernel_swi(SmbServer_Select, &r, &r);

  memcpy(input_set, (void*)r.r[1], sizeof(fd_set));
  memcpy(output_set, input_set, sizeof(fd_set));

  return 1;
}

int smbd_print_fds(fd_set *set)
{
  int i;

  DEBUGLVL( 0 );

  for (i=0;i<howmany(FD_SETSIZE, NFDBITS);i++)
  {
     DEBUGADD(0,("%.8lx",set->fds_bits[i]));
  }
  DEBUGADD(0,("\n"));

  return 0;
}







