/* INETD.C - Win32 inetd (kinda) */ //#define THREAD_DEBUG //#define PIPE_DEBUG //#define SHOW_IAC /* * inetd - inet daemon for Windows NT WIN32. * * Revision history: * 13-Dec-1996 S. Freyder Initial version. */ #include #include #include #include #include #define TELNET_PORT 23 #define IAC_SIZE 3 #define SE 240 /* end of subnegotiation parameters */ #define NOP 241 /* no-op */ #define DATAMARK 242 /* data mark */ #define BRK 243 /* break */ #define IP 244 /* interrupt process */ #define AO 245 /* abort output */ #define AYT 246 /* are you there */ #define EC 247 /* erase character */ #define EL 248 /* erase line */ #define GA 249 /* Go Ahead */ #define SB 250 /* subnegotiation begin */ #define WILL 251 /* will do something */ #define WONT 252 /* wont do something */ #define DO 253 /* you do something */ #define DONT 254 /* you dont do something */ #define IAC 255 /* data byte 255 or introducer */ #define ECHO 1 /* echo command */ typedef struct { int NumThreads ; unsigned char LastInputChar ; HANDLE Socket ; HANDLE hStdinWrite ; HANDLE hStdoutRead ; struct sockaddr_in RemoteAddr ; struct sockaddr_in LocalAddr ; OVERLAPPED NetRead ; OVERLAPPED NetWrite ; CRITICAL_SECTION WriteSocketSection ; CRITICAL_SECTION RefSection ; PROCESS_INFORMATION ProcInfo ; int IacIndex ; unsigned char PipeBuf[5120] ; unsigned char NetBuf[512] ; unsigned char IacBuf[32] ; } TELNET_DATA ; #define LOCK(x) EnterCriticalSection(&(x)->RefSection) #define UNLOCK(x) LeaveCriticalSection(&(x)->RefSection) #define IFCloseHandle(x) \ if ((x) != INVALID_HANDLE_VALUE) { \ CloseHandle(x) ; \ x = INVALID_HANDLE_VALUE ; \ } static unsigned char EchoOut[] = { IAC,WILL,ECHO,IAC,DONT,ECHO } ; static char *sCommandLine = "loginout" ; static void Err( char *why ){ fprintf(stderr,"%s: Error %lu SockError %lu\n",why, GetLastError(),WSAGetLastError()) ; exit(2) ; } static void linger( int s, int onoff, int secs ){ struct linger linger; linger.l_onoff = onoff ; linger.l_linger = secs ; if (setsockopt(s, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(linger)) < 0) { Err("setting linger") ; } } void ShutSocket( TELNET_DATA *pTelnet ){ EnterCriticalSection(&pTelnet->WriteSocketSection) ; if (pTelnet->Socket != INVALID_SOCKET) { /* shutdown(x,0=recv, 1=send, 2=both) */ if (shutdown((int)pTelnet->Socket,1) == SOCKET_ERROR) { fprintf(stderr,"Shutdown error %lu\n",WSAGetLastError()) ; } } LeaveCriticalSection(&pTelnet->WriteSocketSection) ; } int WriteSocket( TELNET_DATA *pTelnet, unsigned char *Buf, unsigned Len ){ DWORD wnb ; EnterCriticalSection(&pTelnet->WriteSocketSection) ; if (pTelnet->Socket != INVALID_SOCKET) { ResetEvent(pTelnet->NetWrite.hEvent) ; WriteFile(pTelnet->Socket,Buf,Len,&wnb, &pTelnet->NetWrite) ; if (GetOverlappedResult(pTelnet->Socket, &pTelnet->NetWrite,&wnb,TRUE)) { } else { wnb = 0 ; } } else { wnb = 0 ; } LeaveCriticalSection(&pTelnet->WriteSocketSection) ; return(wnb) ; } static BOOLEAN bDoIac( TELNET_DATA *pTelnet ){ pTelnet->IacIndex = 0 ; #if defined(SHOW_IAC) fprintf(stderr,"IAC %03d %03d\n",pTelnet->IacBuf[1],pTelnet->IacBuf[2]) ; #endif return(TRUE) ; } static unsigned FixInput( TELNET_DATA *pTelnet, unsigned char *s, unsigned l ){ /* * prepares the specified input for injection into the Stdin pipe and also * generates the echo buffer if we are in echo mode. */ unsigned i ; unsigned char *d = s ; unsigned char c ; for (i=0 ; iLastInputChar == '\r') { /* previous was CR */ break ; /* then drop newline */ } /* fall through */ } default: { *d++ = c ; break ; } } pTelnet->LastInputChar = c ; } return(d-s) ; } static BOOLEAN bProcessNetRead( TELNET_DATA *pTelnet, DWORD nb ){ /* * processes data in NetBuf length nb. worries about IACs and writes non-IAC * stuff to the hStdinWrite pipe. */ unsigned i, l, n ; unsigned char *s ; DWORD wnb ; i = 0 ; while (i < nb) { /* * if we're inside or starting an IAC, gather the remainder. */ if (pTelnet->IacIndex > 0 || pTelnet->NetBuf[i] == IAC) { pTelnet->IacBuf[pTelnet->IacIndex++] = pTelnet->NetBuf[i++] ; if (pTelnet->IacIndex == IAC_SIZE) { if (!bDoIac(pTelnet)) { return(FALSE) ; } } else { /* need more of IAC */ continue ; /* get it if we have it */ } } /* * at this point, we are not inside an IAC - see if we can find * one. if so, transfer everything up to that point. */ l = i ; for (s=&pTelnet->NetBuf[i] ; *s++ != IAC && l < nb ; l++) ; l -= i ; /* compute length we can write */ if (l > 0) { #if defined(PIPE_DEBUG) unsigned z ; fprintf(stderr,"Write pipe: ") ; for (z=0 ; zNetBuf[z+i], pTelnet->NetBuf[z+i]) ; } fprintf(stderr,"\n") ; #endif if ((n = FixInput(pTelnet,&pTelnet->NetBuf[i],l)) > 0) { if (!WriteFile(pTelnet->hStdinWrite,&pTelnet->NetBuf[i],n,&wnb, NULL)) { return(FALSE) ; } } i += l ; /* advance past bytes just processed */ } } return(TRUE) ; } static HANDLE hDuplicate( HANDLE hSource, BOOLEAN bInherit, DWORD dwOptions, DWORD dwAccess ){ HANDLE hResult ; HANDLE hMyProcess = GetCurrentProcess() ; if (DuplicateHandle(hMyProcess,hSource,hMyProcess,&hResult, dwAccess,bInherit,dwOptions)) { return(hResult) ; } return(INVALID_HANDLE_VALUE) ; } static BOOLEAN bCreateCmdProcess( TELNET_DATA *pTelnet, char *sEnv ){ STARTUPINFO StartupInfo ; HANDLE hStdinRead ; HANDLE hStdoutWrite ; BOOLEAN bSpawnOk ; if (!CreatePipe(&hStdinRead,&pTelnet->hStdinWrite,NULL, sizeof pTelnet->NetBuf)) { return(FALSE) ; } if (!CreatePipe(&pTelnet->hStdoutRead,&hStdoutWrite,NULL, sizeof pTelnet->PipeBuf)) { return(FALSE) ; } ZeroMemory(&StartupInfo,sizeof StartupInfo) ; StartupInfo.cb = sizeof StartupInfo ; StartupInfo.hStdInput = hDuplicate(hStdinRead,TRUE, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE,0) ; StartupInfo.hStdOutput = hDuplicate(hStdoutWrite,TRUE, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE,0) ; StartupInfo.hStdError = GetStdHandle(STD_OUTPUT_HANDLE) ; StartupInfo.dwFlags = STARTF_USESTDHANDLES ; bSpawnOk = CreateProcess( NULL, // application sCommandLine, // command line NULL, // process attributes NULL, // thread attributes TRUE, // inherit handles CREATE_NEW_PROCESS_GROUP, // creation flags sEnv, // environment NULL, // current directory &StartupInfo, &pTelnet->ProcInfo ) ; /* * now close the handles we passed to the child process. */ CloseHandle(StartupInfo.hStdInput) ; CloseHandle(StartupInfo.hStdOutput) ; CloseHandle(pTelnet->ProcInfo.hThread) ; /* don't need thread handle */ return(bSpawnOk) ; } static int iTelnetNumThreads( TELNET_DATA *pTelnet, int iThreadDelta ){ EnterCriticalSection(&pTelnet->RefSection) ; pTelnet->NumThreads += iThreadDelta ; iThreadDelta = pTelnet->NumThreads ; LeaveCriticalSection(&pTelnet->RefSection) ; return(iThreadDelta) ; } static void vTelnetEndThread( TELNET_DATA *pTelnet ){ if (iTelnetNumThreads(pTelnet,-1) == 0) { /* if time to destroy it */ IFCloseHandle(pTelnet->hStdoutRead) ; IFCloseHandle(pTelnet->hStdinWrite) ; IFCloseHandle(pTelnet->NetRead.hEvent) ; IFCloseHandle(pTelnet->NetWrite.hEvent) ; IFCloseHandle(pTelnet->ProcInfo.hProcess) ; if (pTelnet->Socket != INVALID_HANDLE_VALUE) { int n ; while ((n=recv((int)pTelnet->Socket,pTelnet->NetBuf,1,0)) != 0) { if (n == SOCKET_ERROR) { fprintf(stderr,"Recv status %lu\n",WSAGetLastError()) ; break ; } else if (n > 0) { fprintf(stderr," recv %d",n) ; } else { break ; } } #if defined(CLOSE_DEBUG) fprintf(stderr," Closing.\n") ; #endif if (closesocket((unsigned)pTelnet->Socket) != 0) { fprintf(stderr,"Close error %lu\n",WSAGetLastError()) ; } } DeleteCriticalSection(&pTelnet->RefSection) ; DeleteCriticalSection(&pTelnet->WriteSocketSection) ; free(pTelnet) ; } _endthread() ; } static void vCmdOutputToNetwork( TELNET_DATA *pTelnet ){ /* * copies data from output of command process to network. */ DWORD nb ; while (ReadFile(pTelnet->hStdoutRead,pTelnet->PipeBuf, sizeof pTelnet->PipeBuf,&nb,NULL)) { #if defined(NET_DEBUG) unsigned z ; fprintf(stderr,"Write net: ") ; for (z=0 ; zPipeBuf[z], pTelnet->PipeBuf[z]) ; } fprintf(stderr,"\n") ; #endif #if defined(THREAD_DEBUG) fprintf(stderr,"Writing Cmd output to socket.\n") ; #endif if (nb > 0) { if (WriteSocket(pTelnet,pTelnet->PipeBuf,nb) != (int)nb) { break ; } } else { break ; } #if defined(THREAD_DEBUG) fprintf(stderr,"Reading Cmd output.\n") ; #endif } #if defined(THREAD_DEBUG) fprintf(stderr,"CmdToNet exiting.\n") ; #endif ShutSocket(pTelnet) ; LOCK(pTelnet) ; IFCloseHandle(pTelnet->hStdoutRead) ; UNLOCK(pTelnet) ; vTelnetEndThread(pTelnet) ; } static void telnetd( void *vSocket ){ TELNET_DATA *pTelnet = calloc(1,sizeof *pTelnet) ; DWORD nb ; DWORD Error ; HANDLE hEvent[2] ; int iAddrLen ; int n ; char *sEnv = pTelnet->PipeBuf ; BOOLEAN bDoNetRead = TRUE ; pTelnet->Socket = (HANDLE)vSocket ; /* store socket */ linger((int)pTelnet->Socket,1,20) ; InitializeCriticalSection(&pTelnet->RefSection) ; iTelnetNumThreads(pTelnet,1) ; /* count self */ pTelnet->NetRead.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL) ; pTelnet->NetWrite.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL) ; InitializeCriticalSection(&pTelnet->WriteSocketSection) ; /* * construct the environment. note that the double-trailing NUL is implicit * because pTelnet is calloc()'ed. */ iAddrLen = sizeof pTelnet->RemoteAddr ; getpeername((int)pTelnet->Socket,(struct sockaddr *)&pTelnet->RemoteAddr, &iAddrLen) ; iAddrLen = sizeof pTelnet->LocalAddr ; getsockname((int)pTelnet->Socket,(struct sockaddr *)&pTelnet->LocalAddr, &iAddrLen) ; n = sprintf(sEnv,"CONNECTION=%s %u", inet_ntoa(pTelnet->RemoteAddr.sin_addr), ntohs(pTelnet->RemoteAddr.sin_port) ) ; n += sprintf(&sEnv[n]," %s %u", inet_ntoa(pTelnet->LocalAddr.sin_addr), ntohs(pTelnet->LocalAddr.sin_port) ) ; n++ ; n += sprintf(&sEnv[n],"PATH=%s",getenv("PATH")) ; if (!bCreateCmdProcess(pTelnet,sEnv)) { fprintf(stderr,"Command creation error %lu\n",GetLastError()) ; vTelnetEndThread(pTelnet) ; } /* * we have the command process running - crank up a thread to handle the * transfer of the process' output to the network, and we'll handle the * other direction here. */ iTelnetNumThreads(pTelnet,1) ; /* count this thread */ if (_beginthread(vCmdOutputToNetwork,0,pTelnet) == -1) { iTelnetNumThreads(pTelnet,-1) ; /* discount thread not started */ } else { int nObj = 1 ; /* only wait on net for now */ for (;;) { hEvent[0] = pTelnet->NetRead.hEvent ; hEvent[1] = pTelnet->ProcInfo.hProcess ; ResetEvent(pTelnet->NetRead.hEvent) ; ReadFile(pTelnet->Socket,pTelnet->NetBuf,sizeof pTelnet->NetBuf, &nb,&pTelnet->NetRead) ; if ((Error=WaitForMultipleObjects(nObj,hEvent,FALSE,INFINITE)) == WAIT_OBJECT_0) { GetOverlappedResult(pTelnet->Socket, &pTelnet->NetRead,&nb,TRUE) ; Error = GetLastError() ; if (Error != NO_ERROR && Error != ERROR_IO_PENDING) { // fprintf(stderr,"Bad error %lu\n",Error) ; break ; } if (nb > 0) { if (bDoNetRead) { if (!bProcessNetRead(pTelnet,nb)) { bDoNetRead = FALSE ; } } } else { /* network read failed */ #if defined(NET_DEBUG) fprintf(stderr,"Net read fail.\n") ; #endif break ; } } else if (Error == (WAIT_OBJECT_0+1)) { break ; } else { fprintf(stderr,"Bogus wait status %lu\n",Error) ; } } } LOCK(pTelnet) ; IFCloseHandle(pTelnet->hStdinWrite) ; IFCloseHandle(pTelnet->ProcInfo.hProcess) ; UNLOCK(pTelnet) ; #if defined(THREAD_DEBUG) fprintf(stderr,"vTelnet status %lu\n",GetLastError()) ; #endif vTelnetEndThread(pTelnet) ; } main( int argc, char *argv[] ){ int Socket ; int AccSocket ; struct sockaddr_in SockAddr ; struct sockaddr ConAddr ; int iAddrLen ; WORD wVer ; WSADATA wsaData ; char sTrustedNet[128] ; char sRemoteAddr[128] ; struct sockaddr_in RemoteAddr ; char *s ; int on = 1 ; if ((s=getenv("TRUSTED_NET")) != NULL) { /* have a trusted net to check */ strcpy(sTrustedNet,s) ; } else { sTrustedNet[0] = 0 ; } SetErrorMode( SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX ) ; wVer = MAKEWORD(1,1) ; // request WinSock version 1.1 if (WSAStartup(wVer,&wsaData) != 0) { // if startup failed Err("wsastartup") ; } Socket = (int)hDuplicate( (HANDLE)socket(AF_INET,SOCK_STREAM,IPPROTO_TCP), FALSE,DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE,0) ; if (argc > 2) { sCommandLine = argv[2] ; } ZeroMemory((void *)&SockAddr,sizeof SockAddr) ; /* zero sockaddr */ SockAddr.sin_family = AF_INET ; SockAddr.sin_port = htons((u_short)(argc > 1 ? atoi(argv[1]) : TELNET_PORT)) ; SockAddr.sin_addr.s_addr = INADDR_ANY ; if ((setsockopt(Socket, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(int))) != 0) { Err("SO_REUSEADDR") ; } if (bind(Socket,(struct sockaddr *)&SockAddr, sizeof SockAddr) != 0) { Err("bind") ; } if (listen(Socket,5) != 0) { Err("listen") ; } /* * wait for next connection, accept it, and launch the thread. */ for (;;) { iAddrLen = sizeof ConAddr ; AccSocket = accept(Socket, (struct sockaddr *)&ConAddr,&iAddrLen) ; if (AccSocket == INVALID_SOCKET) { Err("accept") ; } iAddrLen = sizeof RemoteAddr ; getpeername(AccSocket,(struct sockaddr *)&RemoteAddr,&iAddrLen) ; strcpy(sRemoteAddr,inet_ntoa(RemoteAddr.sin_addr)) ; if (strncmp(sRemoteAddr,sTrustedNet,strlen(sTrustedNet)) == 0) { AccSocket = (int)hDuplicate((HANDLE)AccSocket, FALSE,DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE,0) ; if (_beginthread(telnetd,0,(void *)AccSocket) == -1) { closesocket(AccSocket) ; } } else { fprintf(stderr,"Connection from %s rejected.\n",sRemoteAddr) ; closesocket(AccSocket) ; } } return(0) ; }