Files
sics/nread.c
2013-08-09 05:30:53 +10:00

1368 lines
36 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 2000
-----------------------------------------------------------------------------*/
#include <stdlib.h>
#include <assert.h>
#ifdef CYGNUS
#include <sys/socket.h>
#else
#include <sys/select.h>
#endif
#include <sys/time.h>
#include <strlutil.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"
#include "uselect.h"
#include "trace.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;
extern char *ConID(SConnection *pCon); /* from conman.c */
/*--------------------------------------------------------------------------*/
typedef struct __netreader {
pServer pMain; /* The server ds */
int iList; /* the list of sockets to check */
int iPasswdTimeout;
int iReadTimeout;
int iEnd;
long lMagic;
pDynString conList;
} 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));
pNew->conList = CreateDynString(1024, 1024);
if (pNew->iList < 0 || pNew->conList == NULL) {
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);
if (self->conList != NULL) {
DeleteDynString(self->conList);
}
free(self);
}
/*--------------------------------------------------------------------------*/
int NetReadRegister(pNetRead self, mkChannel * pSock, eNRType eType,
SConnection * pCon)
{
NetItem sItem, sEntry;
char buffer[80];
int iRet;
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);
/* check if the entry is already there */
iRet = LLDnodePtr2First(self->iList);
while (iRet != 0) {
LLDnodeDataTo(self->iList, &sEntry);
if (sEntry.pSock->sockid == pSock->sockid) {
snprintf(buffer, sizeof buffer, "NetReadRegister twice %d type %d",
pSock->sockid, eType);
WriteToCommandLog("SYS>", buffer);
return 1;
}
iRet = LLDnodePtr2Next(self->iList);
}
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 */
/* TODO
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)) {
traceCommand(ConID(pItem->pCon),"interrupt: %d",iInt);
TaskSignal(self->pMain->pTasker, SICSINT, &iInt);
snprintf(pBueffel,sizeof(pBueffel)-1, "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) {
strlcat(pItem->pHold, pPtr, 511);
/* DFC locking for protocol zero only */
if (pItem->pCon->iProtocolID == 0 &&
CostaLocked(pItem->pCon->pStack))
iStat = 0;
else
iStat = CostaTop(pItem->pCon->pStack, pItem->pHold);
if (!iStat) {
SCWrite(pItem->pCon, "ERROR: Busy", eError);
}
pItem->pHold[0] = '\0';
} else {
/* no, normal command */
/* DFC locking for protocol zero only */
if (pItem->pCon->iProtocolID == 0 &&
CostaLocked(pItem->pCon->pStack))
iStat = 0;
else
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) {
strlcpy(pItem->pHold, pPtr, 511);
}
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 */
/* TODO
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);
traceCommand(ConID(pItem->pCon),"interrupt: %d",iInt);
snprintf(pError,sizeof(pError)-1, "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':
/* DFC locking for protocol zero only */
if (pItem->pCon->iProtocolID == 0 &&
CostaLocked(pItem->pCon->pStack))
iStat = 0;
else
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! */
snprintf(pError,sizeof(pError)-1, "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;
self = (pNetRead) pData;
assert(self);
if (self->lMagic != NRMAGIC) {
return 0;
}
/* check for end */
if (self->iEnd) {
return 0;
}
ANETprocess();
/* build the select mask */
FD_ZERO(&lMask);
iRet = LLDnodePtr2First(self->iList);
iCount = 0;
DynStringClear(self->conList);
while (iRet != 0) {
LLDnodeDataTo(self->iList, &NItem);
if (!VerifyChannel(NItem.pSock)) {
break;
}
snprintf(num,sizeof(num)-1, "%d, type %d:", NItem.pSock->sockid, NItem.eType);
if (conCount < 100) {
DynStringConcat(self->conList, num);
}
FD_SET(NItem.pSock->sockid, &lMask);
if (NItem.pSock->sockid > iCount) {
iCount = NItem.pSock->sockid;
}
conCount++;
if (conCount > 100) {
WriteToCommandLog("WAYTOMANYCONNECTIONS> ",
GetCharArray(self->conList));
}
iRet = LLDnodePtr2Next(self->iList);
}
if (conCount > 100) {
WriteToCommandLog("WAYTOMANYCONNECTIONS> ",
GetCharArray(self->conList));
}
/*
* This costs a surprising amount of CPU-time, in a test 10%!
* This is why it has been commented away
snprintf(num, sizeof num, "%d", conCount);
IFSetOption(pSICSOptions, "ConnectionCount", num);
IFSetOption(pSICSOptions, "ConMask", GetCharArray(self->conList));
*/
/* the select itself */
tmo.tv_usec = self->iReadTimeout;
iCount++;
iRet = uselect(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;
}
/*===================================================================================
* new code to support the ANET network stuff
* =================================================================================*/
typedef struct {
pDynString command;
int state;
SConnection *pCon;
} CommandCBData, *pCommandCBData;
/*----------------------------------------------------------------------------------*/
static void killCommandCBData(void *data)
{
pCommandCBData self = (pCommandCBData) data;
if (self == NULL) {
return;
}
if (self->command != NULL) {
DeleteDynString(self->command);
}
free(self);
}
/*----------------------------------------------------------------------------------*/
static int testAndInvokeInterrupt(pCommandCBData self, int handle)
{
char *pPtr, *pInt;
char buffer[512];
int iInt;
pPtr = GetCharArray(self->command);
if ((pInt = strstr(pPtr, "INT1712")) != NULL) {
sscanf(pInt, "%s %d", buffer, &iInt);
if (SCMatchRights(self->pCon, usUser)) {
traceCommand(ConID(self->pCon),"interrupt:%d",iInt);
TaskSignal(pServ->pTasker, SICSINT, &iInt);
snprintf(buffer, 512, "INTERRUPT %d issued on sock %d",
iInt, handle);
WriteToCommandLog("SYS>", buffer);
SICSLogWrite(buffer, eInternal);
if (iInt == eEndServer) {
TaskStop(pServ->pTasker);
}
} else {
SCWrite(self->pCon,
"ERROR: insufficient privilege to invoke Interrupt", eError);
}
return 1;
} else if(strstr(pPtr,"Poch") != NULL){
SCPureSockWrite(self->pCon,"Poch\r\n", eLog);
return 1;
}
return 0;
}
/*----------------------------------------------------------------------------------*/
static int CommandDataCB(int handle, void *userData)
{
pCommandCBData self = (pCommandCBData) userData;
int i, length, status;
char *pPtr = NULL;
assert(self != NULL);
pPtr = ANETreadPtr(handle, &length);
if (pPtr == NULL) {
return 1;
}
for (i = 0; i < length; i++) {
switch (self->state) {
case COLLECT:
if (pPtr[i] == '\r' || pPtr[i] == '\n') {
self->state = SKIPTERM;
if (!testAndInvokeInterrupt(self, handle)) {
if (self->pCon->iProtocolID == 0 && CostaLocked(self->pCon->pStack))
status = 0;
else
status = CostaTop(self->pCon->pStack, GetCharArray(self->command));
if (!status) {
SCWrite(self->pCon, "ERROR: Busy", eError);
}
}
DynStringClear(self->command);
} else {
if (pPtr[i] != '\0') {
DynStringConcatChar(self->command, pPtr[i]);
}
}
break;
case SKIPTERM:
if (pPtr[i] != '\r' && pPtr[i] != '\n' && pPtr[i] != '\0') {
DynStringConcatChar(self->command, pPtr[i]);
self->state = COLLECT;
}
break;
}
}
ANETreadConsume(handle, length);
return 1;
}
/*----------------------------------------------------------------------------------*/
static int CommandAcceptCB(int handle, void *userData)
{
SConnection *pCon = NULL;
pCommandCBData usData = NULL;
pCon = SCreateConnection(pServ->pSics, handle, 3);
usData = malloc(sizeof(CommandCBData));
if (pCon == NULL || usData == NULL) {
SICSLogWrite("Failure to allocate new Connection", eInternal);
return 0;
}
usData->command = CreateDynString(256, 256);
if (usData->command == NULL) {
SICSLogWrite("Failure to allocate new Connection", eInternal);
return 0;
}
usData->pCon = pCon;
usData->state = COLLECT;
TaskRegister(pServ->pTasker,
SCTaskFunction,
SCSignalFunction, SCDeleteConnection, pCon, 1);
ANETsetReadCallback(handle, CommandDataCB, usData, killCommandCBData);
SCSendOK(pCon);
return 1;
}
/*-----------------------------------------------------------------------*/
static int ANETTelnetReply(int sockHandle, char code, char cChar)
{
char pReply[3];
pReply[0] = IAC;
pReply[1] = code;
pReply[2] = cChar;
ANETwrite(sockHandle, pReply, 3);
return 1;
}
/*-----------------------------------------------------------------------*/
static int ANETTelnetProcess(int handle, void *usData)
{
pCommandCBData self = NULL;
int length, status, i;
int cChar;
char *pPtr = NULL;
char pError[256];
self = (pCommandCBData) usData;
assert(self != NULL);
pPtr = ANETreadPtr(handle, &length);
/* do telnet analysis of the data buffer */
for (i = 0; i < length; i++) {
cChar = (int) pPtr[i];
#ifdef TELNETDEBUG
if ((cChar > 48) && (cChar < 128)) {
printf("char: %c\n", cChar);
} else {
printf("Control: %d\n", cChar);
}
#endif
/* Telnet status switching */
switch (self->state) {
case tData:
switch (cChar) {
case IAC:
self->state = tIAC;
break;
case '\r':
case '\n':
if (!testAndInvokeInterrupt(self, handle)) {
if (self->pCon->iProtocolID == 0 && CostaLocked(self->pCon->pStack))
status = 0;
else
status = CostaTop(self->pCon->pStack, GetCharArray(self->command));
if (!status) {
SCWrite(self->pCon, "ERROR: Busy", eError);
}
}
self->state = tCR;
DynStringClear(self->command);
break;
case (char) 8: /* backspace */
DynStringBackspace(self->command);
break;
case (char) 0: /* ignore 0 character sent as end of text */
break;
default:
DynStringConcatChar(self->command, (char) cChar);
break;
} /* end of tData case */
break;
case tCR:
if (cChar == '\r' || cChar == '\n' || cChar == '\0') {
continue;
} else {
self->state = tData;
DynStringConcatChar(self->command, (char) cChar);
}
break;
case tIAC:
switch (cChar) {
case IAC:
self->state = tData;
break;
case WILL:
self->state = tWill;
break;
case WONT:
self->state = tWont;
break;
case DONT:
self->state = tDont;
break;
case DO:
self->state = tDo;
break;
case EOR:
self->state = tData;
break;
case SB:
self->state = tSB;
break;
case EC:
DynStringBackspace(self->command);
self->state = tData;
break;
case EL:
DynStringClear(self->command);
self->state = tData;
break;
case IP:
SCSetInterrupt(self->pCon, eAbortBatch);
self->state = tData;
break;
default:
self->state = tData;
break;
} /* end of tIAC */
break;
case tWill: /* we do not do options! */
ANETTelnetReply(handle, DONT, cChar);
self->state = 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
*/
self->state = tData;
break;
case tDo: /* we do not do options! */
ANETTelnetReply(handle, WONT, cChar);
self->state = 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
*/
self->state = 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.
*/
self->state = 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:
self->state = tData;
/* suboption interpretation would go here */
break;
default:
/* copy data to suboption buffer */
break;
}
break;
default:
/* There is something wrong here! */
snprintf(pError,sizeof(pError)-1, "ERROR: bad telnet code %d", cChar);
SICSLogWrite(pError, eInternal);
self->state = tData;
break;
} /* end master swicth */
} /* end for loop */
ANETreadConsume(handle, length);
return 1;
}
/*----------------------------------------------------------------------------------*/
static int TelnetAcceptCB(int handle, void *userData)
{
SConnection *pCon = NULL;
pCommandCBData usData = NULL;
pTelTask pTel = NULL;
pCon = SCreateConnection(pServ->pSics, handle, 3);
usData = malloc(sizeof(CommandCBData));
if (pCon == NULL || usData == NULL) {
SICSLogWrite("Failure to allocate new Connection", eInternal);
return 0;
}
usData->command = CreateDynString(256, 256);
if (usData->command == NULL) {
SICSLogWrite("Failure to allocate new Connection", eInternal);
return 0;
}
usData->pCon = pCon;
usData->state = tData;
/* Create a task object for the telnet connection */
pTel = CreateTelnet(pCon);
if (!pTel) {
SICSLogWrite("Failure to allocate new Telnet Task Object", eInternal);
SCDeleteConnection(pCon);
return 0;
}
/* register connection and task */
pCon->iTelnet = 1;
TaskRegister(pServ->pTasker,
TelnetTask, TelnetSignal, DeleteTelnet, pTel, 1);
ANETsetReadCallback(handle, ANETTelnetProcess,
usData, killCommandCBData);
SCSendOK(pCon);
return 1;
}
/*------------------------------------------------------------------------------------*/
static void NREADlog(int level, char *txt, void *userData)
{
traceSys("anet",txt);
puts(txt);
}
/*------------------------------------------------------------------------------------*/
int NetReadInstallANETPort(pNetRead self, eNRType eType, int iPort)
{
ANETsetLog(NREADlog, NULL);
switch (eType) {
case naccept:
return ANETopenServerPort(iPort, CommandAcceptCB, NULL);
break;
case taccept:
return ANETopenServerPort(iPort, TelnetAcceptCB, NULL);
break;
}
return 0;
}