Files
sics/nread.c
koennecke c780ad9cae Merge branch 'conclean' into rhel7
Conflicts:
	SCinter.c
	telnet.c
2017-04-03 11:44:35 +02:00

933 lines
24 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 "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 "uselect.h"
#include "trace.h"
#include "protocol.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);
Log(ERROR,"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 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) {
Log(ERROR,"sys","%s","WAYTOMANYCONNECTIONS:%s ",
GetCharArray(self->conList));
}
iRet = LLDnodePtr2Next(self->iList);
}
if (conCount > 100) {
Log(ERROR,"sys","%s","WAYTOMANYCONNECTIONS:%s ",
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) {
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 = TaskRegisterN(self->pMain->pTasker, "NetReadWait4Data",
Wait4ReadTask, ReadWaitSignal, ReadWaitFree, pNew, TASK_PRIO_HIGH);
/* 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)) {
Log(INFO, "com","%s:interrupt:%d", ConID(self->pCon), iInt);
TaskSignal(pServ->pTasker, SICSINT, &iInt);
snprintf(buffer, 512, "INTERRUPT %d issued on sock %d",
iInt, handle);
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;
}
/*----------------------------------------------------------------------------------*/
#define COLLECT 0
#define SKIPTERM 1
/*----------------------------------------------------------------------------------*/
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 (SCGetProtocolID(self->pCon) == PROTSICS && SCCostaLocked(self->pCon))
status = 0;
else
status = SCCostaTop(self->pCon, 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;
char buffer[80];
pCon = SCreateConnection(pServ->pSics, handle, 3);
usData = malloc(sizeof(CommandCBData));
if (pCon == NULL || usData == NULL) {
Log(ERROR,"sys","%s","Failure to allocate new Connection");
return 0;
}
usData->command = CreateDynString(256, 256);
if (usData->command == NULL) {
Log(ERROR,"sys","%s","Failure to allocate new Connection");
return 0;
}
usData->pCon = pCon;
usData->state = COLLECT;
snprintf(buffer,sizeof(buffer),"con%ld", SCGetIdent(pCon));
TaskRegisterN(pServ->pTasker,
buffer,
SCTaskFunction,
SCSignalFunction, SCDeleteConnection, pCon, TASK_PRIO_LOW);
ANETsetReadCallback(handle, CommandDataCB, usData, killCommandCBData);
SCSendOK(pCon);
return 1;
}
/*------------------------------------------------------------------------
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 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 (SCGetProtocolID(self->pCon) == PROTSICS && SCCostaLocked(self->pCon))
status = 0;
else
status = SCCostaTop(self->pCon, 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! */
Log(ERROR,"sys","Bad telnet code %d", cChar);
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;
char buffer[80];
pCon = SCreateConnection(pServ->pSics, handle, 3);
usData = malloc(sizeof(CommandCBData));
if (pCon == NULL || usData == NULL) {
Log(ERROR,"sys","%s","Failure to allocate new Connection");
return 0;
}
usData->command = CreateDynString(256, 256);
if (usData->command == NULL) {
Log(ERROR,"sys","%s","Failure to allocate new Connection");
return 0;
}
usData->pCon = pCon;
usData->state = tData;
/* Create a task object for the telnet connection */
pTel = CreateTelnet(pCon);
if (!pTel) {
Log(ERROR,"sys","%s","Failure to allocate new Telnet Task Object");
SCDeleteConnection(pCon);
return 0;
}
/* register connection and task */
SCSetTelnet(pCon,1);
snprintf(buffer,sizeof(buffer),"con%ld", SCGetIdent(pCon));
TaskRegisterN(pServ->pTasker,
buffer,
TelnetTask, TelnetSignal, DeleteTelnet, pTel, TASK_PRIO_LOW);
ANETsetReadCallback(handle, ANETTelnetProcess,
usData, killCommandCBData);
SCSendOK(pCon);
return 1;
}
/*------------------------------------------------------------------------------------*/
static void NREADlog(int level, char *txt, void *userData)
{
/*
suppress the connection messages
*/
if(level != ANETCON){
traceSys("anet","%s",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;
}