/*--------------------------------------------------------------------------- N E T W O R K R E A D E R Implementation file for the network reader. copyrigth: see copyright.h Mark Koennecke, September 1997 Telnet Functionality added: Mark Koennecke, January 1998 Revamped login to non telnet connection. Mark Koennecke, October 20000 -----------------------------------------------------------------------------*/ #include #include #ifdef CYGNUS #include #else #include #endif #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" extern pServer pServ; extern int VerifyChannel(mkChannel *self); /* defined in network.c */ #define NRMAGIC 1061996 /* #define TELNETDEBUG 1 */ typedef enum { tData, tIAC, tWill, tWont, tDo, tDont, tCR, tSB, tSBIN, tSE } TelnetStatus; /*--------------------------------------------------------------------------*/ typedef struct __netreader { pServer pMain; /* The server ds */ int iList; /* the list of sockets to check */ int iPasswdTimeout; int iReadTimeout; int iEnd; long lMagic; } NetReader; /*--------------------------------------------------------------------------- The structure used for an item in the Net Reader list of connections */ typedef struct { mkChannel *pSock; SConnection *pCon; eNRType eType; char pHold[512]; int iEOD; TelnetStatus tStatus; int iReadable; } NetItem, *pNetItem; /*----------------------------------------------------------------------------*/ pNetRead CreateNetReader(pServer pTask, int iPasswdTimeout, int iReadTimeout) { pNetRead pNew = NULL; assert(pTask); assert(iPasswdTimeout > 0); assert(iReadTimeout >= 0); pNew = (pNetRead)malloc(sizeof(NetReader)); if(!pNew) { return NULL; } memset(pNew,0,sizeof(NetReader)); pNew->iList = LLDcreate(sizeof(NetItem)); if(pNew->iList < 0) { free(pNew); return NULL; } pNew->pMain = pTask; pNew->iPasswdTimeout = iPasswdTimeout; pNew->iReadTimeout = iReadTimeout; pNew->lMagic = NRMAGIC; return pNew; } /*--------------------------------------------------------------------------*/ void DeleteNetReader(void *pData) { pNetRead self = NULL; self = (pNetRead)pData; assert(self); if(self->lMagic != NRMAGIC) { return; } LLDdelete(self->iList); free(self); } /*--------------------------------------------------------------------------*/ int NetReadRegister(pNetRead self, mkChannel *pSock, eNRType eType, SConnection *pCon) { NetItem sItem; assert(self); if(!VerifyChannel(pSock)) { return 0; } sItem.pSock = pSock; sItem.eType = eType; sItem.pCon = pCon; sItem.iEOD = 0; sItem.tStatus = tData; sItem.iReadable = 0; memset(sItem.pHold,0,511); LLDnodeAppendFrom(self->iList, &sItem); return 1; } /*--------------------------------------------------------------------------*/ int NetReadRemove(pNetRead self, mkChannel *pSock) { NetItem sItem; int iRet; assert(self); if(self->lMagic != NRMAGIC) { return 0; } /* find the entry to remove */ iRet = LLDnodePtr2First(self->iList); while(iRet != 0) { LLDnodeDataTo(self->iList,&sItem); if(sItem.pSock == pSock) { LLDnodeDelete(self->iList); return 1; } iRet = LLDnodePtr2Next(self->iList); } return 0; } /*-------------------------------------------------------------------------*/ static int NetReadAccept(pNetRead self, mkChannel *pSock) { mkChannel *pNew = NULL; char pBuffer[1064]; int iRet; SConnection *pRes = NULL; char *pUser = NULL, *pPasswd = NULL; time_t target; if(!VerifyChannel(pSock)) { return 0; } /* check for new connection */ pNew = NETAccept(pSock,0); if(pNew) { /* create connection object */ pRes = SCreateConnection(self->pMain->pSics,pNew,3); if(!pRes) { SICSLogWrite("Failure to allocate new Connection",eInternal); NETClosePort(pNew); free(pNew); return 0; } else { /* register the connection and create a task for it here */ NetReadRegister(self,pNew,command, pRes); TaskRegister(self->pMain->pTasker, SCTaskFunction, SCSignalFunction, SCDeleteConnection, pRes, 1); SCSendOK(pRes); return 1; } } else { return 0; } } /*--------------------------------------------------------------------------*/ #define COLLECT 0 #define SKIPTERM 1 /*------------------------------------------------------------------------*/ static int NetReadRead(pNetRead self, pNetItem pItem) { char *pPtr, *pEnd, *pBufEnd; char pBuffer[1024], pMuell[20]; char pBueffel[80]; int i, iInt, iRet, iStat, state; /* read first */ memset(pBuffer,0,1024); iRet = NETRead(pItem->pCon->pSock,pBuffer, 1022,0); if(iRet < 0) /* EOF */ { pItem->pCon->iEnd = 1; NetReadRemove(self,pItem->pCon->pSock); return 0; } else if(iRet == 0) /* should not happen */ { return 1; } /* iRet is now the number of bytes read. Now we check for command interrupts */ pPtr = strstr(pBuffer,"INT1712"); if(pPtr) { sscanf(pPtr, "%s %d",pMuell, &iInt); if(SCMatchRights(pItem->pCon,usUser)) { TaskSignal(self->pMain->pTasker, SICSINT, &iInt); sprintf(pBueffel,"INTERRUPT %d issued on sock %d", iInt,pItem->pCon->pSock->sockid); WriteToCommandLog("SYS>",pBueffel); if(iInt == eEndServer) { TaskStop(self->pMain->pTasker); } } else { SCWrite(pItem->pCon, "ERROR: insufficient privilege to invoke Interrupt", eError); } return 0; } /* split into command lines and put into fifo */ pBufEnd = pBuffer + iRet; pPtr = pBuffer; pEnd = pBuffer; state = COLLECT; while(pEnd < pBufEnd) { switch(state) { case COLLECT: if( (*pEnd != '\r') && (*pEnd != '\n')) { pEnd++; } else { /* there is a string between pPtr and pEnd */ *pEnd = '\0'; /* do we have something in hold ? */ if(strlen(pItem->pHold) > 0) { strcat(pItem->pHold,pPtr); iStat = CostaTop(pItem->pCon->pStack,pItem->pHold); if(!iStat) { SCWrite(pItem->pCon,"ERROR: Busy",eError); } pItem->pHold[0] = '\0'; } else { /* no, normal command */ iStat = CostaTop(pItem->pCon->pStack,pPtr); if(!iStat) { SCWrite(pItem->pCon,"ERROR: Busy",eError); } } pPtr = pEnd +1; pEnd = pPtr; state = SKIPTERM; } break; case SKIPTERM: if( (*pEnd != '\r') && (*pEnd != '\n')) { state = COLLECT; pPtr = pEnd; } else { pEnd++; pPtr = pEnd; } break; } } /* when we are here we may still have an incomplete command pending */ if(pEnd != pPtr && strlen(pPtr) > 0) { strcpy(pItem->pHold,pPtr); } return 1; } /*-------------------------- telnet protocoll code -------------------------*/ static int TelnetAccept(pNetRead self, mkChannel *pSock) { mkChannel *pNew = NULL; char pBuffer[1064]; int iRet; SConnection *pRes = NULL; pTelTask pTel = NULL; if(!VerifyChannel(pSock)) { return 0; } /* check for new connection */ pNew = NETAccept(pSock,0); if(pNew) { /* create connection object */ pRes = SCreateConnection(self->pMain->pSics,pNew,usSpy); if(!pRes) { SICSLogWrite("Failure to allocate new Connection",eInternal); NETClosePort(pNew); free(pNew); return 0; } else { /* Create a task object for the telnet connection */ pTel = CreateTelnet(pRes); if(!pTel) { SICSLogWrite("Failure to allocate new Telnet Task Object", eInternal); SCDeleteConnection(pRes); return 0; } /* register connection and task */ pRes->iTelnet = 1; NetReadRegister(self,pNew,tcommand, pRes); TaskRegister(self->pMain->pTasker, TelnetTask, TelnetSignal, DeleteTelnet, pTel, 1); return 1; } } else { return 0; } } /*------------------------------------------------------------------------ Telnet is fully described in RFC-854. This implementation is very simple. It just supports the NVT and no options. Implementation is via a state machine with the state tStatus in the pItem structure. This is necessary as single characters may be sent by telnet clients. -------------------------------------------------------------------------*/ /* Telnet codes */ #define SE 240 #define NOP 241 #define DM 242 /* data mark */ #define BRK 243 #define IP 244 #define AO 245 #define AYT 246 #define EC 247 #define EL 248 #define GA 249 #define SB 250 #define WILL 251 #define WONT 252 #define DO 253 #define DONT 254 #define IAC 255 #define EOR 239 /*-----------------------------------------------------------------------*/ static int TelnetReply(pNetItem pItem, char code, char cChar) { char pReply[3]; pReply[0] = IAC; pReply[1] = code; pReply[2] = cChar; NETWrite(pItem->pCon->pSock,pReply,3); return 1; } /*------------------------------------------------------------------------*/ static int TelnetRead(pNetRead self, pNetItem pItem) { char pBuffer[1024], pMuell[20], pError[256]; int i, iStat, iInt, iRet; int cChar; char *pPtr = NULL; SConnection *pOwner = NULL; /* read first */ memset(pBuffer,0,1023); iRet = NETRead(pItem->pCon->pSock,pBuffer, 1022,0); if(iRet < 0) /* EOF */ { pItem->pCon->iEnd = 1; NetReadRemove(self,pItem->pCon->pSock); return 0; } else if(iRet == 0) /* should not happen */ { return 1; } /* iRet is now the number of bytes read. Now we check for command interrupts */ pPtr = strstr(pBuffer,"INT1712"); if(pPtr) { sscanf(pPtr, "%s %d",pMuell, &iInt); if(SCMatchRights(pItem->pCon,usUser)) { /* owners may kill own tasks, otherwise manager privilege is required. */ pOwner = GetExeOwner(pServ->pExecutor); if(pOwner) { if(pOwner != pItem->pCon) { if(!SCMatchRights(pItem->pCon,usMugger)) { SCWrite(pItem->pCon, "ERROR: Insufficient privilege to stop other people's experiments", eError); return 1; } } } TaskSignal(self->pMain->pTasker, SICSINT, &iInt); sprintf(pError,"INTERRUPT %d issued on sock %d",iInt, pItem->pCon->pSock->sockid); WriteToCommandLog("SYS>",pError); if(iInt == eEndServer) { TaskStop(self->pMain->pTasker); } } else { SCWrite(pItem->pCon,"ERROR: insufficient privilege to invoke Interrupt", eError); } return 1; } /* do telnet analysis of the data buffer */ for(i = 0; i < iRet; i++) { cChar = (int)pBuffer[i]; #ifdef TELNETDEBUG if( (cChar > 48) && (cChar < 128) ) { printf("char: %c\n",cChar); } else { printf("Control: %d\n",cChar); } #endif /* Telnet status switching */ switch(pItem->tStatus) { case tData: switch(cChar) { case IAC: pItem->tStatus = tIAC; break; case '\r': case '\n': iStat = CostaTop(pItem->pCon->pStack,pItem->pHold); /* printf("%s\n",pItem->pHold); */ if(!iStat) { SCWrite(pItem->pCon,"ERROR: Busy",eError); } memset(pItem->pHold,0,511); pItem->iEOD = 0; pItem->tStatus = tCR; break; case (char)8: /* backspace */ pItem->iEOD--; if(pItem->iEOD < 0) { pItem->iEOD = 0; } break; case (char)0:/* ignore 0 character sent as end of text */ break; default: pItem->pHold[pItem->iEOD] = cChar; pItem->iEOD++; break; } /* end of tData case */ break; case tCR: /* ignore the second character after a newline. Telnet gives you two characters for newline */ pItem->tStatus = tData; break; case tIAC: switch(cChar) { case IAC: pItem->tStatus = tData; break; case WILL: pItem->tStatus = tWill; break; case WONT: pItem->tStatus = tWont; break; case DONT: pItem->tStatus = tDont; break; case DO: pItem->tStatus = tDo; break; case EOR: pItem->tStatus = tData; break; case SB: pItem->tStatus = tSB; break; case EC: pItem->iEOD--; if(pItem->iEOD < 0) { pItem->iEOD = 0; } pItem->tStatus = tData; break; case EL: memset(pItem->pHold,0,511); pItem->iEOD = 0; pItem->tStatus = tData; break; case IP: SCSetInterrupt(pItem->pCon,eAbortBatch); pItem->tStatus = tData; break; default: pItem->tStatus = tData; break; } /* end of tIAC */ break; case tWill: /* we do not do options! */ TelnetReply(pItem,DONT,cChar); pItem->tStatus = tData; break; case tWont: /* we do not do options! A Wont is sent by the client if it cannot do a option we requested it to have. As we do not try to force options, this should not happen */ pItem->tStatus = tData; break; case tDo: /* we do not do options! */ TelnetReply(pItem,WONT,cChar); pItem->tStatus = tData; break; case tDont: /* we do not do options! A Dont is sent by the client if it cannot do a option we requested it to have. As we do not try to force options, this should not happen */ pItem->tStatus = tData; break; case tSB: /* as we do not have options, we cannot have suboption negotaitions. Something is seriously wrong when we are here. It is a protocoll error. However, we ignore it silently. tSB marks the start of the subnegotiation. The current character must be the option code we are dealing with. */ pItem->tStatus = tSE; break; case tSE: /* now we are in the suboption parameter. Normally data should be copied to a suboption string buffer here until SE. */ switch(cChar) { case IAC: break; case SE: pItem->tStatus = tData; /* suboption interpretation would go here */ break; default: /* copy data to suboption buffer */ break; } break; default: /* There is something wrong here! */ sprintf(pError,"ERROR: bad telnet code %d", cChar); SICSLogWrite(pError,eInternal); pItem->tStatus = tData; break; } } return 1; } /*---------------------------------------------------------------------------*/ static int NetReadUDP(pNetRead self, pNetItem pItem) { char pBueffel[512], pMuell[20]; int iRet, iInt; char *pPtr = NULL; /* read */ iRet = UDPRead(pItem->pSock,pBueffel,510,0); if(iRet > 10) /* something has beeen sent, verfify */ { pPtr = strstr(pBueffel,"SICSINT"); if(pPtr) { sscanf(pPtr,"%s %d", pMuell, &iInt); TaskSignal(self->pMain->pTasker, SICSINT, &iInt); if(iInt == eEndServer) { TaskStop(self->pMain->pTasker); } return 1; } } return 0; } /*-------------------------------------------------------------------------*/ int NetReaderTask(void *pData) { pNetRead self = NULL; fd_set lMask; struct timeval tmo = {0,1}; int iRet, iStatus; int iCount; NetItem NItem; int conCount = 0; char num[50]; IPair *options = NULL; char buffer[1024]; self = (pNetRead)pData; assert(self); if(self->lMagic != NRMAGIC) { return 0; } /* check for end */ if(self->iEnd) { return 0; } /* build the select mask */ FD_ZERO(&lMask); iRet = LLDnodePtr2First(self->iList); iCount = 0; buffer[0] = '\0'; while(iRet != 0) { LLDnodeDataTo(self->iList,&NItem); if(!VerifyChannel(NItem.pSock)) { break; } sprintf(num,"%d, type %d:", NItem.pSock->sockid, NItem.eType); strcat(buffer,num); FD_SET(NItem.pSock->sockid,&lMask); if(NItem.pSock->sockid > iCount) { iCount = NItem.pSock->sockid; } conCount++; iRet = LLDnodePtr2Next(self->iList); } snprintf(num,50,"%d", conCount); IFSetOption(pSICSOptions,"ConnectionCount",num); IFSetOption(pSICSOptions,"ConMask",buffer); /* the select itself */ tmo.tv_usec = self->iReadTimeout; iCount++; iRet = select(iCount, &lMask,NULL,NULL,&tmo); if(iRet <= 0) /* no pending request */ { return 1; } /* now go through all registered things and handle the message */ iRet = LLDnodePtr2First(self->iList); while(iRet != 0) { LLDnodeDataTo(self->iList,&NItem); if(FD_ISSET(NItem.pSock->sockid,&lMask)) /* data */ { switch(NItem.eType) { /* lists have been changed after accept, return */ case naccept: NetReadAccept(self,NItem.pSock); return 1; break; case taccept: TelnetAccept(self,NItem.pSock); return 1; break; case command: iStatus = NetReadRead(self,&NItem); if(iStatus == 0) /* there was an eof */ { /* do not continue, list is messy */ return 1; } break; case tcommand: iStatus = TelnetRead(self,&NItem); if(iStatus == 0) /* there was an eof */ { /* do not continue, list is messy */ return 1; } break; case udp: NetReadUDP(self,&NItem); break; case user: NItem.iReadable = 1; LLDnodeDataFrom(self->iList, &NItem); break; } LLDnodeDataFrom(self->iList,&NItem); } iRet = LLDnodePtr2Next(self->iList); } /* done, finally */ return 1; } /*--------------------------------------------------------------------------*/ void NetReaderSignal(void *pUser, int iSignal, void *pEventData) { pNetRead self = NULL; int *iInt; self = (pNetRead)pUser; assert(self); iInt = (int *)pEventData; if(iSignal == SICSINT) { iInt = (int *)pEventData; if(*iInt == eEndServer) { self->iEnd = 1; } } } /*--------------------------------------------------------------------------*/ typedef struct { pNetRead pRead; mkChannel *pChan; int iEnd; } ReadWait, *pReadWait; /*--------------------------------------------------------------------------*/ static void ReadWaitFree(void *pData) { pReadWait pWait = NULL; pWait = (pReadWait)pData; if(!pWait) { return; } if(pWait->pChan) { free(pWait->pChan); } free(pWait); } /*--------------------------------------------------------------------------*/ static void ReadWaitSignal(void *pUser, int iSignal, void *pSigData) { pReadWait pWait = NULL; int *iInt; pWait = (pReadWait)pUser; assert(pWait); if(iSignal == SICSINT) { iInt = (int *)pSigData; if(*iInt != eContinue) { pWait->iEnd = 1; } } } /*---------------------------------------------------------------------------*/ static int Wait4ReadTask(void *pData) { pReadWait pWait = NULL; NetItem sItem; int iRet; pWait = (pReadWait)pData; assert(pWait); if(pWait->iEnd) { return 0; /* an interrupt occurred */ } iRet = LLDnodePtr2First(pWait->pRead->iList); while(iRet != 0) { LLDnodeDataTo(pWait->pRead->iList, &sItem); if(sItem.pSock == pWait->pChan) { if(sItem.iReadable) { NetReadRemove(pWait->pRead,pWait->pChan); return 0; } } iRet = LLDnodePtr2Next(pWait->pRead->iList); } return 1; } /*---------------------------------------------------------------------------*/ int NetReadWait4Data(pNetRead self, int iSocket) { pReadWait pNew = NULL; mkChannel *pChan = NULL; long lID; int iRet; /* make a new ReadWait */ pNew = (pReadWait)malloc(sizeof(ReadWait)); if(!pNew) { return 0; } /* make a new channel */ pChan = (mkChannel *)malloc(sizeof(mkChannel)); if(!pChan) { free(pNew); return 0; } pChan->sockid = iSocket; pChan->lMagic = NETMAGIC; /* put it into the NetReader */ iRet = NetReadRegister(self,pChan,user,NULL); if(!iRet) { return 0; } /* start the wait task */ pNew->pChan = pChan; pNew->iEnd = 0; pNew->pRead = self; lID = TaskRegister(self->pMain->pTasker, Wait4ReadTask, ReadWaitSignal, ReadWaitFree, pNew,0); /* wait for finish */ TaskWait(self->pMain->pTasker,lID); return 1; } /*--------------------------------------------------------------------------*/ int NetReadRegisterUserSocket(pNetRead self, int iSocket) { mkChannel *pChan = NULL; int iRet; /* make a new channel */ pChan = (mkChannel *)malloc(sizeof(mkChannel)); if(!pChan) { return 0; } pChan->sockid = iSocket; pChan->lMagic = NETMAGIC; /* put it into the NetReader */ iRet = NetReadRegister(self,pChan,user,NULL); if(!iRet) { return 0; } return 1; } /*--------------------------------------------------------------------------*/ int NetReadRemoveUserSocket(pNetRead self, int iSocket) { NetItem sItem; int iRet; assert(self); if(self->lMagic != NRMAGIC) { return 0; } /* find the entry to remove */ iRet = LLDnodePtr2First(self->iList); while(iRet != 0) { LLDnodeDataTo(self->iList,&sItem); if(sItem.eType == user) { if(sItem.pSock->sockid == iSocket) { free(sItem.pSock); LLDnodeDelete(self->iList); return 1; } } iRet = LLDnodePtr2Next(self->iList); } return 0; } /*--------------------------------------------------------------------------*/ int NetReadReadable(pNetRead self, int iSocket) { NetItem sItem; int iRet; assert(self); /* find the entry to read */ iRet = LLDnodePtr2First(self->iList); while(iRet != 0) { LLDnodeDataTo(self->iList,&sItem); if(sItem.eType == user) { if(sItem.pSock->sockid == iSocket) { if(sItem.iReadable) { return 1; } else { return 0; } } } iRet = LLDnodePtr2Next(self->iList); } return 0; } /*--------------------------------------------------------------------------*/ int NetReadResetUser(pNetRead self, int iSocket) { NetItem sItem; int iRet; assert(self); /* find the entry to remove */ iRet = LLDnodePtr2First(self->iList); while(iRet != 0) { LLDnodeDataTo(self->iList,&sItem); if(sItem.eType == user) { if(sItem.pSock->sockid == iSocket) { sItem.iReadable = 0; LLDnodeDataFrom(self->iList,&sItem); return 1; } } iRet = LLDnodePtr2Next(self->iList); } return 0; }