/*---------------------------------------------------------------------------
                        N E T W O R K  R E A D E R
                        
   Implementation file for the network reader. 
   
   copyrigth: see copyright.h
   
   Mark Koennecke, September 1997

   Telnet Functionality added: Mark Koennecke, January 1998   
-----------------------------------------------------------------------------*/
#include <stdlib.h>
#include <assert.h>
#ifdef CYGNUS
#include <sys/socket.h>
#else
#include <sys/select.h>
#endif
#include <sys/time.h>
#include "fortify.h"
#include "lld.h"
#include "network.h"
#include "passwd.h"
#include "conman.h"
#include "SCinter.h"
#include "servlog.h"
#include "costa.h"
#include "task.h"
#include "emon.h"
#include "devexec.h"
#include "nserver.h"
#include "event.h"
#include "interrupt.h"
#include "telnet.h"
#include "nread.h"
#include "commandlog.h"

extern pServer pServ;
extern VerifyChannel(mkChannel *self); /* defined in network.c */
#define NRMAGIC 1061996
/*
#define TELNETDEBUG 1
*/

  typedef enum {
                 tData,
                 tIAC,
                 tWill,
                 tWont,
                 tDo,
                 tDont,
                 tCR,
                 tSB,
                 tSBIN,
                 tSE } TelnetStatus;

/*--------------------------------------------------------------------------*/
   typedef struct __netreader {
                                pServer pMain;  /* The server ds */
                                int iList;         /* the list of sockets to check */
                                int iPasswdTimeout;
                                int iReadTimeout;
                                int iEnd;
                                long lMagic;
                              } NetReader;
/*---------------------------------------------------------------------------
    The structure used for an item in the Net Reader list of connections 
*/
   typedef struct {
                    mkChannel *pSock;
                    SConnection *pCon;
                    eNRType   eType;
                    char pHold[512];
                    int  iEOD;
                    TelnetStatus tStatus;
                    int iReadable;
                  } NetItem, *pNetItem;
/*----------------------------------------------------------------------------*/
  pNetRead CreateNetReader(pServer pTask, int iPasswdTimeout, int iReadTimeout)
  {
     pNetRead pNew = NULL;
     
     assert(pTask);
     assert(iPasswdTimeout > 0);
     assert(iReadTimeout > 0);
     
     pNew = (pNetRead)malloc(sizeof(NetReader));
     if(!pNew)
     {
       return NULL;
     }
     memset(pNew,0,sizeof(NetReader));
     
     pNew->iList = LLDcreate(sizeof(NetItem));
     if(pNew->iList < 0)
     {
       free(pNew);
       return NULL;
     }
     pNew->pMain = pTask;
     pNew->iPasswdTimeout = iPasswdTimeout;
     pNew->iReadTimeout   = iReadTimeout;
     pNew->lMagic = NRMAGIC;
     return pNew;
  }              
/*--------------------------------------------------------------------------*/
  void DeleteNetReader(void *pData)
  {
     pNetRead self = NULL;
     
     self = (pNetRead)pData;
     assert(self);
     if(self->lMagic != NRMAGIC)
     {
       return;
     }
     
     LLDdelete(self->iList);
     free(self);
  }             
/*--------------------------------------------------------------------------*/
  int NetReadRegister(pNetRead self, mkChannel *pSock, eNRType eType,
                      SConnection *pCon)
  {
     NetItem sItem;
  
     assert(self);
     if(!VerifyChannel(pSock))
     {
       return 0;
     }
     
     sItem.pSock = pSock;
     sItem.eType = eType;
     sItem.pCon = pCon;
     sItem.iEOD = 0;
     sItem.tStatus = tData;
     memset(sItem.pHold,0,511);
     
     LLDnodeAppendFrom(self->iList, &sItem);
     return 1;
  }
/*--------------------------------------------------------------------------*/
  int NetReadRemove(pNetRead self, mkChannel *pSock)
  {
    NetItem sItem;
    int iRet;
    
    assert(self);
    if(self->lMagic != NRMAGIC)
    {
       return;
    }
    
    /* find the entry to remove */
    iRet = LLDnodePtr2First(self->iList);
    while(iRet != 0)
    {
      LLDnodeDataTo(self->iList,&sItem);
      if(sItem.pSock == pSock)
      {
         LLDnodeDelete(self->iList);
         return 1;
      }
      iRet = LLDnodePtr2Next(self->iList);
    }
    return 0;
  }
/*-------------------------------------------------------------------------*/                                                         
  static int NetReadAccept(pNetRead self, mkChannel *pSock)
  {
     mkChannel *pNew = NULL;
     char pBuffer[1064];
     int iRet;
     SConnection *pRes = NULL;
     char *pUser = NULL, *pPasswd = NULL;
     
     if(!VerifyChannel(pSock))
     {
       return 0;
     }
     
     /* check for new connection */
     pNew = NETAccept(pSock,0);
     if(pNew)
     {  /* new connection, read user/passwd, verify and work accordingly */
        iRet = NETRead(pNew,pBuffer,1063, self->iPasswdTimeout);
        if(iRet <= 0)
        {
                strcpy(pBuffer,"Timeout waiting for username/password");
                SICSLogWrite(pBuffer,eInternal); 
        	NETClosePort(pNew);
        	free(pNew);
        	return 0;
        }
        else
        {
             pUser = strtok(pBuffer," \t");
             pPasswd = strtok(NULL," \t\r\n"); 
             iRet = IsValidUser(pUser,pPasswd);
             if(iRet < 0)
             {
                sprintf(pBuffer,"SYSTEM ATTACK by %s / %s",pUser,
                        pPasswd);
                SICSLogWrite(pBuffer,eInternal);
                NETClosePort(pNew);
                free(pNew);
                return 0;
             }
             else
             {
               /* create connection object */
               pRes = SCreateConnection(self->pMain->pSics,pNew,iRet);
               if(!pRes)
               {
                  SICSLogWrite("Failure to allocate new Connection",eInternal);
                  NETClosePort(pNew);
                  free(pNew);
                  return 0;
               }
               else
               {
                 /* register the connection and create a task for it here */
                 NetReadRegister(self,pNew,command, pRes);
                 TaskRegister(self->pMain->pTasker,
                              SCTaskFunction,
                              SCSignalFunction,
                              SCDeleteConnection,
                              pRes,
                              1);
                 SCSendOK(pRes);             
                 return 1;
               }
             }
        }
     }
     else
     {
       return 0;
     }
  }
/*--------------------------------------------------------------------------*/
  static int NetReadRead(pNetRead self, pNetItem pItem)
  {
    char *pPtr, *pEnd, *pBufEnd;
    char pBuffer[1024], pMuell[20];
    char pBueffel[80];
    int i, iInt, iRet, iStat;    

    /* read first */
    memset(pBuffer,0,1024);
    iRet = NETRead(pItem->pCon->pSock,pBuffer, 1022,0);
    if(iRet < 0) /* EOF */
    {
      pItem->pCon->iEnd = 1;
      NetReadRemove(self,pItem->pCon->pSock);
      return 0;     
    } 
    else if(iRet == 0) /* should not happen */
    {
       return 1; 
    }
    
    /* iRet is now the number of bytes read. Now we check for command 
       interrupts */
    pPtr = strstr(pBuffer,"INT1712");
    if(pPtr)
    {
      sscanf(pPtr, "%s %d",pMuell, &iInt);
      if(SCMatchRights(pItem->pCon,usUser))
      {
          TaskSignal(self->pMain->pTasker, SICSINT, &iInt);
          sprintf(pBueffel,"INTERRUPT %d issued on sock %d",
                  iInt,pItem->pCon->pSock->sockid);
          WriteToCommandLog("SYS>",pBueffel);
          if(iInt == eEndServer)
          {
              TaskStop(self->pMain->pTasker);
          }
      }
      else
      {
         SCWrite(pItem->pCon,
           "ERROR: insufficient privilege to invoke Interrupt",
                 eError); 
      }    
      return 1; 
    }    
    
    /* split into command lines
       and put into fifo
     */
    pBufEnd = pBuffer + iRet;
    pPtr = pBuffer;
    pEnd = pBuffer;
    while(pEnd < pBufEnd)
    {
      if( (*pEnd != '\r') && (*pEnd != '\n'))
      {
        pEnd++;
      } 
      else
      {
         /* there is a string between pPtr and pEnd */
         *pEnd = '\0';
         /* do we have something in hold ? */
         if(strlen(pItem->pHold) > 0)
         {
           strcat(pItem->pHold,pPtr);
           iStat = CostaTop(pItem->pCon->pStack,pItem->pHold);
           if(!iStat)
           {
             SCWrite(pItem->pCon,"ERROR: Busy",eError);
           }
           pItem->pHold[0] = '\0';
         }
         else
         {
           /* no, normal command */
           iStat = CostaTop(pItem->pCon->pStack,pPtr);
           if(!iStat)
           {
             SCWrite(pItem->pCon,"ERROR: Busy",eError);
           }
         }
         pPtr = pEnd +1;
         pEnd = pPtr;
      }
    }
    /* when we are here we may still have an incomplete command  pending */
    if(pEnd != pPtr)
    {
      strcpy(pItem->pHold,pPtr);
    }
    return 1; 
  }   
/*-------------------------- telnet protocoll code -------------------------*/
  static int TelnetAccept(pNetRead self, mkChannel *pSock)
  {
     mkChannel *pNew = NULL;
     char pBuffer[1064];
     int iRet;
     SConnection *pRes = NULL;
     pTelTask pTel = NULL;
     
     if(!VerifyChannel(pSock))
     {
       return 0;
     }

     /* check for new connection */
     pNew = NETAccept(pSock,0);
     if(pNew)
     {
        /* create connection object */
        pRes = SCreateConnection(self->pMain->pSics,pNew,usSpy);
        if(!pRes)
        {
           SICSLogWrite("Failure to allocate new Connection",eInternal);
           NETClosePort(pNew);
           free(pNew);
           return 0;
        }
        else
        {
           /* Create a task object for the telnet connection */
           pTel = CreateTelnet(pRes);
           if(!pTel)
           {
             SICSLogWrite("Failure to allocate new Telnet Task Object",
                          eInternal);
             SCDeleteConnection(pRes);
             return 0;
           }
           /* register connection and task */
           pRes->iTelnet = 1;
           NetReadRegister(self,pNew,tcommand, pRes);
           TaskRegister(self->pMain->pTasker,
                        TelnetTask,
                        TelnetSignal,
                        DeleteTelnet,
                        pTel,
                        1);
             return 1;
        }
     }
     else
     {
       return 0;
     }
  }
/*------------------------------------------------------------------------
  Telnet is fully described in RFC-854. This implementation is very simple.
  It just supports the NVT and no options. Implementation is via a state
  machine with the state tStatus in the pItem structure. This is necessary
  as single characters may be sent by telnet clients.
-------------------------------------------------------------------------*/
/* Telnet codes */
#define SE    240
#define NOP   241
#define DM    242 /* data mark */
#define BRK   243
#define IP    244
#define AO    245
#define AYT   246
#define EC    247
#define EL    248
#define GA    249
#define SB    250
#define WILL  251
#define WONT  252
#define DO    253
#define DONT  254
#define IAC   255
#define EOR   239
/*-----------------------------------------------------------------------*/
 static int TelnetReply(pNetItem pItem, char code, char cChar)
 {
    char pReply[3];
    
    pReply[0] = IAC;
    pReply[1] = code;
    pReply[2] = cChar;
    
    NETWrite(pItem->pCon->pSock,pReply,3);
    return 1;
 }
/*------------------------------------------------------------------------*/
  static int TelnetRead(pNetRead self, pNetItem pItem)
  {
    char pBuffer[1024], pMuell[20], pError[256];
    int i, iStat, iInt, iRet;
    int  cChar;
    char *pPtr = NULL;
    SConnection *pOwner = NULL;
    
    /* read first */
    memset(pBuffer,0,1023);
    iRet = NETRead(pItem->pCon->pSock,pBuffer, 1022,0);
    if(iRet < 0) /* EOF */
    {
      pItem->pCon->iEnd = 1;
      NetReadRemove(self,pItem->pCon->pSock);
      return 0;     
    } 
    else if(iRet == 0) /* should not happen */
    {
       return 1; 
    }
    
    /* iRet is now the number of bytes read. Now we check for command 
       interrupts */
    pPtr = strstr(pBuffer,"INT1712");
    if(pPtr)
    {
      sscanf(pPtr, "%s %d",pMuell, &iInt);
      if(SCMatchRights(pItem->pCon,usUser))
      {
	/* owners may kill own tasks, otherwise manager 
           privilege is required.
	*/
          pOwner = GetExeOwner(pServ->pExecutor);
          if(pOwner)
	  {
            if(pOwner != pItem->pCon)
	    {
              if(!SCMatchRights(pItem->pCon,usMugger))
	      {
                 SCWrite(pItem->pCon,
           "ERROR: Insufficient privilege to stop other people's experiments",
                  eError);
                 return 1; 
              }
            }
          }
          TaskSignal(self->pMain->pTasker, SICSINT, &iInt);
          sprintf(pError,"INTERRUPT %d issued on sock %d",iInt,
                  pItem->pCon->pSock->sockid);
          WriteToCommandLog("SYS>",pError);
          if(iInt == eEndServer)
          {
              TaskStop(self->pMain->pTasker);
          }
      }
      else
      {
         SCWrite(pItem->pCon,"ERROR: insufficient privilege to invoke Interrupt",
                 eError); 
      }    
      return 1; 
    }    
    
    /* do telnet analysis of the data buffer */
    for(i = 0; i < iRet; i++)
    {
      cChar = (int)pBuffer[i];
#ifdef TELNETDEBUG
      if( (cChar > 48) && (cChar < 128) )
      {
         printf("char: %c\n",cChar);
      }
      else
      {
        printf("Control: %d\n",cChar);
      }
#endif      
      /* Telnet status switching */ 
      switch(pItem->tStatus)
      {
         case tData:
                   switch(cChar)
                   {
                      case IAC:
                                pItem->tStatus = tIAC;
                                break;
                      case '\r':
                      case '\n':
                                iStat = CostaTop(pItem->pCon->pStack,pItem->pHold);
                                if(!iStat)
                                {
                                   SCWrite(pItem->pCon,"ERROR: Busy",eError);
                                }
                                memset(pItem->pHold,0,511);
                                pItem->iEOD = 0;
                                pItem->tStatus = tCR;
                                break;
                     case (char)8: /* backspace */
                                pItem->iEOD--;
                                if(pItem->iEOD < 0)
                                {
                                  pItem->iEOD = 0;
                                }           
                                break;
                     case (char)0:/* ignore 0 character sent as end of text */
                                break;           
                     default:
                                pItem->pHold[pItem->iEOD] = cChar;
                                pItem->iEOD++;
                                break;              
                                         
                   } /* end of tData case */
                   break;
         case tCR: /* ignore the second character after a newline.
                      Telnet gives you two characters for newline
                   */  
                   pItem->tStatus = tData; 
                   break;
         case tIAC:
                   switch(cChar)
                   {
                     case IAC:
                              pItem->tStatus = tData;
                              break;
                     case WILL:
                              pItem->tStatus = tWill;
                              break;
                     case WONT:
                              pItem->tStatus = tWont;
                              break;
                     case DONT:
                              pItem->tStatus = tDont;
                              break;
                     case DO:
                              pItem->tStatus = tDo;
                              break;
                     case EOR:
                              pItem->tStatus = tData;
                              break;
                     case SB:
                              pItem->tStatus = tSB;
                              break;
                     case EC:
                              pItem->iEOD--;
                              if(pItem->iEOD < 0)
                              {
                                 pItem->iEOD = 0;
                              }
                              pItem->tStatus = tData;
                              break;
                     case EL:
                              memset(pItem->pHold,0,511);
                              pItem->iEOD = 0;
                              pItem->tStatus = tData;
                              break;
                     case IP:
                              SCSetInterrupt(pItem->pCon,eAbortBatch);
                              pItem->tStatus = tData;
                              break;                        
                     default:
                             pItem->tStatus = tData;
                             break;                                                              
                   } /* end of tIAC */
                   break;            
         case tWill: /* we do not do options! */ 
                   TelnetReply(pItem,DONT,cChar);
                   pItem->tStatus = tData;
                   break; 
         case tWont: /* we do not do options! A Wont is sent by the client 
                     if it cannot do a option we requested it to have. As
                     we do not try to force options, this should not happen
                     */ 
                   pItem->tStatus = tData;
                   break; 
         case tDo: /* we do not do options! */ 
                   TelnetReply(pItem,WONT,cChar);
                   pItem->tStatus = tData;
                   break; 
         case tDont: /* we do not do options! A Dont is sent by the client 
                     if it cannot do a option we requested it to have. As
                     we do not try to force options, this should not happen
                     */ 
                   pItem->tStatus = tData;
                   break; 
         case tSB: /* as we do not have options, we cannot have suboption
                      negotaitions. Something is seriously wrong when
                      we are here. It is a protocoll error. However, we
                      ignore it silently. tSB marks the start of the 
                      subnegotiation. The current character must be the
                      option code we are dealing with. 
                   */
                   pItem->tStatus = tSE;
                   break;
         case tSE:
                   /* now we are in the suboption parameter. Normally data
                      should be copied to a suboption string buffer here
                      until SE.
                   */
                   switch(cChar)
                   {
                     case IAC:
                             break;
                     case SE:
                             pItem->tStatus = tData;
                             /* suboption interpretation would go here */
                             break;
                     default:
                            /* copy data to suboption buffer */
                            break;                
                   }              
                   break;
                      
         default:
                 /* There is something wrong here! */
                 sprintf(pError,"ERROR: bad telnet code %d", cChar);
                 SICSLogWrite(pError,eInternal);
                 pItem->tStatus = tData;
                 break; 
                                                     
      } 
    }
    return 1; 
  }   
    
/*---------------------------------------------------------------------------*/
  static int NetReadUDP(pNetRead self, pNetItem pItem)
  {
     char pBueffel[512], pMuell[20];
     int iRet, iInt;
     char *pPtr = NULL;
     
     /* read */
     iRet = UDPRead(pItem->pSock,pBueffel,510,0);
     if(iRet > 10) /* something has beeen sent, verfify */
     {
       pPtr = strstr(pBueffel,"SICSINT");
       if(pPtr)
       {
          sscanf(pPtr,"%s %d", pMuell, &iInt);
          TaskSignal(self->pMain->pTasker, SICSINT, &iInt);
          if(iInt == eEndServer)
          {
             TaskStop(self->pMain->pTasker);
          }
          return 1; 
       }
     }
     return 0;
  }
/*-------------------------------------------------------------------------*/
  int NetReaderTask(void *pData)
  {
    pNetRead self = NULL;
    fd_set lMask;
    struct timeval tmo = {0,1};
    int iRet, iStatus;
    int iCount;
    NetItem NItem;
    
    self = (pNetRead)pData;
    assert(self);
    if(self->lMagic != NRMAGIC)
    {
       return 0;
    }
    
    /* check for end */
    if(self->iEnd)
    {
      return 0;
    }
    
    /* build the select  mask */
    FD_ZERO(&lMask);
    iRet = LLDnodePtr2First(self->iList);
    iCount = 0;
    while(iRet != 0)
    {
      LLDnodeDataTo(self->iList,&NItem);
      if(!VerifyChannel(NItem.pSock))
      {
        break;
      }
      FD_SET(NItem.pSock->sockid,&lMask);
      if(NItem.pSock->sockid > iCount)
      {
        iCount = NItem.pSock->sockid;
      }
      iRet = LLDnodePtr2Next(self->iList);
    }
    
    /* the select itself */
    tmo.tv_usec = self->iReadTimeout;
    iCount++;
    iRet = select(iCount, &lMask,NULL,NULL,&tmo);
    if(iRet <= 0) /* no pending request */
    {
      return 1;
    }
    
    
    /* now go through all registered things and handle the message */
    iRet = LLDnodePtr2First(self->iList);
    while(iRet != 0)
    {
      LLDnodeDataTo(self->iList,&NItem);
      if(FD_ISSET(NItem.pSock->sockid,&lMask)) /* data */
      {
         switch(NItem.eType)
         {
            /* lists have been changed after accept, return */
            case naccept:
                     NetReadAccept(self,NItem.pSock);
                     return 1;
                     break;
            case taccept:
                     TelnetAccept(self,NItem.pSock);
                     return 1;
                     break;
            case command:
                     iStatus = NetReadRead(self,&NItem);
                     if(iStatus == 0) /* there was an eof */
                     { 
                       /* do not continue, list is messy */
                       return 1;
                     }
                     break;
            case tcommand:
                     iStatus = TelnetRead(self,&NItem);
                     if(iStatus == 0) /* there was an eof */
                     { 
                       /* do not continue, list is messy */
                       return 1;
                     }
                     break;
            case udp:
                     NetReadUDP(self,&NItem);
                     break;       
            case user:
                     NItem.iReadable = 1;
                     LLDnodeDataFrom(self->iList, &NItem);
                     break;                           
         }
         LLDnodeDataFrom(self->iList,&NItem);
      }
      iRet = LLDnodePtr2Next(self->iList);
    }
    
    /* done, finally */
    return 1;
  }  
/*--------------------------------------------------------------------------*/
  void NetReaderSignal(void *pUser, int iSignal, void *pEventData)
  {
     pNetRead self = NULL;
     int *iInt;
     
     self = (pNetRead)pUser;
     assert(self);
     iInt = (int *)pEventData;
     
     if(iSignal == SICSINT)
     {
       iInt = (int *)pEventData;
       if(*iInt == eEndServer)
       {
         self->iEnd = 1;
       }
     }
  }
/*--------------------------------------------------------------------------*/
  typedef struct {
                   pNetRead pRead;
                   mkChannel *pChan;
                   int iEnd;
                 } ReadWait, *pReadWait;
/*--------------------------------------------------------------------------*/
  static void ReadWaitFree(void *pData)
  {
     pReadWait pWait = NULL;
     
     pWait = (pReadWait)pData;
     if(!pWait)
     {
      return;
     }
     
     if(pWait->pChan)
     {
       free(pWait->pChan);
     }
     free(pWait);
  } 
/*--------------------------------------------------------------------------*/
  static void ReadWaitSignal(void *pUser, int iSignal, void *pSigData)
  {
    pReadWait pWait = NULL;
    int *iInt;
    
    pWait = (pReadWait)pUser;
    assert(pWait);    
  
    if(iSignal == SICSINT)
    {
      iInt = (int *)pSigData;
      if(*iInt != eContinue)
      {
        pWait->iEnd = 1;
      }
    }
  }                  
/*---------------------------------------------------------------------------*/
  static int Wait4ReadTask(void *pData)
  {
    pReadWait  pWait = NULL;
    NetItem sItem;
    int iRet;
    
    pWait = (pReadWait)pData;
    assert(pWait);
    
    if(pWait->iEnd)
    {
      return 0; /* an interrupt occurred */
    }
    
    iRet = LLDnodePtr2First(pWait->pRead->iList);
    while(iRet != 0)
    {
       LLDnodeDataTo(pWait->pRead->iList, &sItem);
       if(sItem.pSock == pWait->pChan)
       {
         if(sItem.iReadable)
         {
           NetReadRemove(pWait->pRead,pWait->pChan);
           return 0;           
         }
       }
       iRet = LLDnodePtr2Next(pWait->pRead->iList);
    }
    return 1;
  }         
/*---------------------------------------------------------------------------*/
  int NetReadWait4Data(pNetRead self, int iSocket)
  {
     pReadWait pNew = NULL;
     mkChannel *pChan = NULL; 
     long lID;
     int iRet;
     
     /* make a new ReadWait */
     pNew = (pReadWait)malloc(sizeof(ReadWait));
     if(!pNew)
     {
       return 0;
     }
     
     /* make a new channel */
     pChan = (mkChannel *)malloc(sizeof(mkChannel));
     if(!pChan)
     {
       free(pNew);
       return 0;
     }
     pChan->sockid = iSocket;
     pChan->lMagic = NETMAGIC;
     
     /* put it into the NetReader */
     iRet = NetReadRegister(self,pChan,user,NULL);
     if(!iRet)
     {
       return 0;
     }
     
     /* start the wait task */
     pNew->pChan = pChan;
     pNew->iEnd = 0;
     pNew->pRead = self;
     lID = TaskRegister(self->pMain->pTasker,
                        Wait4ReadTask,
                        ReadWaitSignal,
                        ReadWaitFree,
                        pNew,0);
     
     /* wait for finish */                   
     TaskWait(self->pMain->pTasker,lID);                   
                        
     return 1;
  }          
/*--------------------------------------------------------------------------*/
  int NetReadRegisterUserSocket(pNetRead self, int iSocket)
  {
     mkChannel *pChan = NULL; 
     int iRet;
     
     
     /* make a new channel */
     pChan = (mkChannel *)malloc(sizeof(mkChannel));
     if(!pChan)
     {
       return 0;
     }
     pChan->sockid = iSocket;
     pChan->lMagic = NETMAGIC;
     
     /* put it into the NetReader */
     iRet = NetReadRegister(self,pChan,user,NULL);
     if(!iRet)
     {
       return 0;
     }
     return 1;
  }   
/*--------------------------------------------------------------------------*/
  int NetReadRemoveUserSocket(pNetRead self, int iSocket)
  {
    NetItem sItem;
    int iRet;
    
    assert(self);
    if(self->lMagic != NRMAGIC)
    {
       return 0;
    }
    
    /* find the entry to remove */
    iRet = LLDnodePtr2First(self->iList);
    while(iRet != 0)
    {
      LLDnodeDataTo(self->iList,&sItem);
      if(sItem.eType == user)
      {
         if(sItem.pSock->sockid == iSocket)
         {
           free(sItem.pSock);
           LLDnodeDelete(self->iList);
           return 1;
         }  
      }
      iRet = LLDnodePtr2Next(self->iList);
    }
    return 0;
  }
/*--------------------------------------------------------------------------*/
  int NetReadReadable(pNetRead self, int iSocket)
  {
    NetItem sItem;
    int iRet;
    
    assert(self);
    
    /* find the entry to remove */
    iRet = LLDnodePtr2First(self->iList);
    while(iRet != 0)
    {
      LLDnodeDataTo(self->iList,&sItem);
      if(sItem.eType == user)
      {
         if(sItem.pSock->sockid == iSocket)
         {
           if(sItem.iReadable)
           {
             return 1;
           }
           else
           {
             return 0;
           }
         }  
      }
      iRet = LLDnodePtr2Next(self->iList);
    }
    return 0;
  }
/*--------------------------------------------------------------------------*/
  int NetReadResetUser(pNetRead self, int iSocket)
  {
    NetItem sItem;
    int iRet;
    
    assert(self);
    
    /* find the entry to remove */
    iRet = LLDnodePtr2First(self->iList);
    while(iRet != 0)
    {
      LLDnodeDataTo(self->iList,&sItem);
      if(sItem.eType == user)
      {
         if(sItem.pSock->sockid == iSocket)
         {
           sItem.iReadable = 0;
           LLDnodeDataFrom(self->iList,&sItem);
           return 1;
         }  
      }
      iRet = LLDnodePtr2Next(self->iList);
    }
    return 0;
  }
