933 lines
24 KiB
C
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;
|
|
}
|