/*--------------------------------------------------------------------------- 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 #include #ifdef CYGNUS #include #else #include #endif #include #include #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; }