1046 lines
29 KiB
C
1046 lines
29 KiB
C
/*---------------------------------------------------------------------------
|
|
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
|
|
|
|
Revamped login to non telnet connection.
|
|
Mark Koennecke, October 20000
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
#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 int 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;
|
|
sItem.iReadable = 0;
|
|
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 0;
|
|
}
|
|
|
|
/* 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;
|
|
time_t target;
|
|
|
|
if(!VerifyChannel(pSock))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* check for new connection */
|
|
pNew = NETAccept(pSock,0);
|
|
if(pNew)
|
|
{
|
|
/* create connection object */
|
|
pRes = SCreateConnection(self->pMain->pSics,pNew,3);
|
|
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;
|
|
}
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
#define COLLECT 0
|
|
#define SKIPTERM 1
|
|
/*------------------------------------------------------------------------*/
|
|
static int NetReadRead(pNetRead self, pNetItem pItem)
|
|
{
|
|
char *pPtr, *pEnd, *pBufEnd;
|
|
char pBuffer[1024], pMuell[20];
|
|
char pBueffel[80];
|
|
int i, iInt, iRet, iStat, state;
|
|
|
|
/* 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 0;
|
|
}
|
|
|
|
/* split into command lines
|
|
and put into fifo
|
|
*/
|
|
pBufEnd = pBuffer + iRet;
|
|
pPtr = pBuffer;
|
|
pEnd = pBuffer;
|
|
state = COLLECT;
|
|
while(pEnd < pBufEnd)
|
|
{
|
|
switch(state)
|
|
{
|
|
case COLLECT:
|
|
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;
|
|
state = SKIPTERM;
|
|
}
|
|
break;
|
|
case SKIPTERM:
|
|
if( (*pEnd != '\r') && (*pEnd != '\n'))
|
|
{
|
|
state = COLLECT;
|
|
pPtr = pEnd;
|
|
}
|
|
else
|
|
{
|
|
pEnd++;
|
|
pPtr = pEnd;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
/* when we are here we may still have an incomplete command pending */
|
|
if(pEnd != pPtr && strlen(pPtr) > 0)
|
|
{
|
|
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);
|
|
/* printf("%s\n",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;
|
|
int conCount = 0;
|
|
char num[50];
|
|
IPair *options = NULL;
|
|
char buffer[1024];
|
|
|
|
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;
|
|
buffer[0] = '\0';
|
|
while(iRet != 0)
|
|
{
|
|
LLDnodeDataTo(self->iList,&NItem);
|
|
if(!VerifyChannel(NItem.pSock))
|
|
{
|
|
break;
|
|
}
|
|
sprintf(num,"%d, type %d:", NItem.pSock->sockid, NItem.eType);
|
|
strcat(buffer,num);
|
|
FD_SET(NItem.pSock->sockid,&lMask);
|
|
if(NItem.pSock->sockid > iCount)
|
|
{
|
|
iCount = NItem.pSock->sockid;
|
|
}
|
|
conCount++;
|
|
iRet = LLDnodePtr2Next(self->iList);
|
|
}
|
|
|
|
snprintf(num,50,"%d", conCount);
|
|
IFSetOption(pSICSOptions,"ConnectionCount",num);
|
|
IFSetOption(pSICSOptions,"ConMask",buffer);
|
|
|
|
|
|
/* 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 read */
|
|
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;
|
|
}
|
|
|
|
|