Files
sics/nread.c
koennecke c75f556fa5 - Fixed an off by one in a message from killfile
- Fixed a nasty reentrency bug in nread.c
_ protected devexec.c against the same bug
2007-09-06 13:25:51 +00:00

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;
}