Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657/* * Abstract: a simple telnet 'daemon' for Windows hosts. * * Compiled & run successfully using MSVC 5.0 under Windows95 (requires * Winsock2 update) and Windows98 and MSVC 6.0 under WindowsNT4 * * Compiler options : no special options needed * Linker options : add wsock32.lib or ws2_32.lib * * Written by fred.van.lieshout 'at' zonnet.nl * Use freely, no copyrights. * Use Linux. * * Parts Copyright Steven Edwards * Public Domain * * TODO: * - access control * - will/won't handshake * - Unify Debugging output and return StatusCodes */
#include "telnetd.h"
#define telnetd_printf printf#if 0static inline int telnetd_printf(const char *format, ...);{ printf(format,...); syslog (6, format);}#endif
/* Local data */
static BOOLEAN bShutdown = 0;static BOOLEAN bSocketInterfaceInitialised = 0;static int sock;
/* In the future, some options might be passed here to handle * authentication options in the registry or command line * options passed to the service * * Once you are ready to turn on the service * rename this function * int kickoff_telnetd(void) */int kickoff_telnetd(void){ printf("Attempting to start Simple TelnetD\n");
// DetectPlatform(); SetConsoleCtrlHandler(Cleanup, 1);
if (!StartSocketInterface()) ErrorExit("Unable to start socket interface\n");
CreateSocket();
while(!bShutdown) { WaitForConnect(); }
WSACleanup(); return 0;}
/* Cleanup */static BOOL WINAPI Cleanup(DWORD dwControlType){ if (bSocketInterfaceInitialised) { telnetd_printf("Cleanup...\n"); WSACleanup(); } return 0;}
/* StartSocketInterface */static BOOLEAN StartSocketInterface(void){ WORD wVersionRequested; WSADATA wsaData; int err;
wVersionRequested = MAKEWORD( 2, 0 ); err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { telnetd_printf("requested winsock version not supported\n"); return 0; }
bSocketInterfaceInitialised = 1; /* for ErrorExit function */
if ( wsaData.wVersion != wVersionRequested) ErrorExit("requested winsock version not supported\n");
telnetd_printf("TelnetD, using %s\n", wsaData.szDescription); return 1;}
/* CreateSocket */static void CreateSocket(void){ struct sockaddr_in sa;
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock < 0) ErrorExit("Cannot create socket");
memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_addr.s_addr = INADDR_ANY; sa.sin_port = htons(TELNET_PORT);
if (bind(sock, (struct sockaddr*) &sa, sizeof(sa)) != 0) ErrorExit("Cannot bind address to socket");}
/* WaitForConnect */static void WaitForConnect(void){ struct sockaddr_in sa; int new_sock;
if (listen(sock, 1) < 0) ErrorExit("Cannot listen on socket");
if ((new_sock = accept(sock, (struct sockaddr*) &sa, NULL)) < 0) { fprintf(stderr, "Failed to accept incoming call\n"); } else { telnetd_printf("user connected on socket %d, port %d, address %lx\n", new_sock, htons(sa.sin_port), sa.sin_addr.s_addr); UserLogin(new_sock); }}
/* Function: UserLogin */static void UserLogin(int client_socket){ HANDLE threadHandle; client_t *client = malloc(sizeof(client_t));
if (client == NULL) ErrorExit("failed to allocate memory for client");
client->socket = client_socket; threadHandle = CreateThread(NULL, 0, UserLoginThread, client, 0, NULL); if (threadHandle == NULL) free(client); else CloseHandle(threadHandle);}
/* Function: UserLoginThread */static DWORD WINAPI UserLoginThread(LPVOID data){ client_t *client = (client_t *) data; char welcome[256]; char hostname[64] = "Unknown"; char *pwdPrompt = "\r\npass:"; //char *logonPrompt = "\r\nLogin OK, please wait..."; //char *byebye = "\r\nWrong! bye bye...\r\n"; char userID[USERID_SIZE]; char password[USERID_SIZE]; int received; char *terminator;
if (DoTelnetHandshake(client->socket)) { closesocket(client->socket); free(client); return 0; }
gethostname(hostname, sizeof(hostname)); sprintf(welcome, "\r\nWelcome to %s, please identify yourself\r\n\r\nuser:", hostname);
if (send(client->socket, welcome, strlen(welcome), 0) < 0) { closesocket(client->socket); free(client); return 0; } received = ReceiveLine(client->socket, userID, sizeof(userID), Echo ); if (received < 0) { closesocket(client->socket); free(client); return 0; } else if (received) { if ((terminator = strchr(userID, CR)) != NULL) { *terminator = '\0'; } }
if (send(client->socket, pwdPrompt, strlen(pwdPrompt), 0) < 0) { closesocket(client->socket); free(client); return 0; } received = ReceiveLine(client->socket, password, sizeof(password), Password );
#if 0 if (received < 0) { closesocket(client->socket); free(client); return 0; } else if (received) { if ((terminator = strchr(password, CR)) != NULL) { *terminator = '\0'; } }#endif
/* TODO: do authentication here */
telnetd_printf("User '%p' logged on\n", userID);#if 0 strcpy(client->userID, userID); if (send(client->socket, logonPrompt, strlen(logonPrompt), 0) < 0) { closesocket(client->socket); free(client); return 0; }#endif RunShell(client); return 0;}
/* Function: DoTelnetHandshake */static int DoTelnetHandshake(int sock){ int retval; int received; fd_set set; struct timeval timeout = { HANDSHAKE_TIMEOUT, 0 };
char will_echo[]= IAC DONT ECHO IAC WILL ECHO IAC WILL NAWS IAC WILL SUPPRESS_GO_AHEAD IAC DO SUPPRESS_GO_AHEAD IAC DONT NEWENVIRON IAC WONT NEWENVIRON IAC WONT LINEMODE IAC DO NAWS IAC SB TERMINAL_TYPE "\x01" IAC SE ;
unsigned char client_reply[256];
if (send(sock, will_echo, sizeof(will_echo), 0) < 0) { return -1; }
/* Now wait for client response (and ignore it) */ FD_ZERO(&set); FD_SET(sock, &set);
do { retval = select(0, &set, NULL, NULL, &timeout); /* check for error */ if (retval < 0) { return -1; /* check for timeout */ } else if (retval == 0) { return 0; } /* no error and no timeout, we have data in our sock */ received = recv(sock, (char *) client_reply, sizeof(client_reply), 0); if (received <= 0) { return -1; } } while (retval);
return 0;}
/*** Function: ReceiveLine**** Abstract: receive until timeout or CR** In : sock, len** Out : buffer** Result : int** Pre : 'sock' must be valid socket** Post : (result = the number of bytes read into 'buffer')** OR (result = -1 and error)*/static int ReceiveLine(int sock, char *buffer, int len, EchoMode echo){ int i = 0; int retval; fd_set set; struct timeval timeout = { 0, 100000 }; char del[3] = { BS, ' ', BS }; char asterisk[1] = { '*' };
FD_ZERO(&set); FD_SET(sock, &set);
memset(buffer, '\0', len);
do { /* When we're in echo mode, we do not need a timeout */ retval = select(0, &set, NULL, NULL, (echo ? NULL : &timeout) ); /* check for error */ if (retval < 0) { return -1; /* check for timeout */ } else if (retval == 0) { /* return number of characters received so far */ return i; } /* no error and no timeout, we have data in our sock */ if (recv(sock, &buffer[i], 1, 0) <= 0) { return -1; } if ((buffer[i] == '\0') || (buffer[i] == LF)) { /* ignore null characters and linefeeds from DOS telnet clients */ buffer[i] = '\0'; } else if ((buffer[i] == DEL) || (buffer[i] == BS)) { /* handle delete and backspace */ buffer[i] = '\0'; if (echo) { if (i > 0) { i--; buffer[i] = '\0'; if (send(sock, del, sizeof(del), 0) < 0) { return -1; } } } else { buffer[i] = BS; /* Let shell process handle it */ i++; } } else { /* echo typed characters */ if (echo == Echo && send(sock, &buffer[i], 1, 0) < 0) { return -1; } else if (echo == Password && send(sock, asterisk, sizeof(asterisk), 0) < 0) { return -1; } if (buffer[i] == CR) { i++; buffer[i] = LF; /* append LF for DOS command processor */ i++; return i; }
i++; } } while (i < len);
return i;}
/*** Function: RunShell*/static void RunShell(client_t *client) { HANDLE threadHandle; HANDLE hChildStdinRd; HANDLE hChildStdinWr; HANDLE hChildStdoutRd; HANDLE hChildStdoutWr; STARTUPINFO si; PROCESS_INFORMATION piProcInfo; SECURITY_ATTRIBUTES saAttr; char cmd_path[MAX_PATH];
if (!GetEnvironmentVariableA("COMSPEC", cmd_path, ARRAYSIZE(cmd_path))) { if (GetSystemDirectoryA(cmd_path, ARRAYSIZE(cmd_path))) { StringCchCatA(cmd_path, ARRAYSIZE(cmd_path), "\\cmd.exe"); } else { ErrorExit("GetSystemDirectoryA failed\n"); } }
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; // Create a pipe for the child process's STDOUT. if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) ErrorExit("Stdout pipe creation failed\n");
if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) ErrorExit("Stdin pipe creation failed\n");
client->bTerminate = FALSE; client->bWriteToPipe = TRUE; client->bReadFromPipe = TRUE; client->hChildStdinWr = hChildStdinWr; client->hChildStdoutRd = hChildStdoutRd;
// Create the child process (the shell) telnetd_printf("Creating child process...\n");
ZeroMemory( &si, sizeof(STARTUPINFO) ); si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES; si.hStdInput = hChildStdinRd; si.hStdOutput = hChildStdoutWr; si.hStdError = hChildStdoutWr;
//si.dwFlags |= STARTF_USESHOWWINDOW; //si.wShowWindow = SW_SHOW;
if (!CreateProcess(cmd_path, // executable module NULL, // command line NULL, // process security attributes NULL, // primary thread security attributes TRUE, // handles are inherited DETACHED_PROCESS + // creation flags CREATE_NEW_PROCESS_GROUP, NULL, // use parent's environment NULL, // use parent's current directory &si, // startup info &piProcInfo)) { ErrorExit("Create process failed"); }
client->hProcess = piProcInfo.hProcess; client->dwProcessId = piProcInfo.dwProcessId;
telnetd_printf("New child created (groupid=%lu)\n", client->dwProcessId);
// No longer need these in the parent... if (!CloseHandle(hChildStdoutWr)) ErrorExit("Closing handle failed");
if (!CloseHandle(hChildStdinRd)) ErrorExit("Closing handle failed");
threadHandle = CreateThread(NULL, 0, WriteToPipeThread, client, 0, NULL); if (threadHandle != NULL) CloseHandle(threadHandle);
threadHandle = CreateThread(NULL, 0, ReadFromPipeThread, client, 0, NULL); if (threadHandle != NULL) CloseHandle(threadHandle);
threadHandle = CreateThread(NULL, 0, MonitorChildThread, client, 0, NULL); if (threadHandle != NULL) CloseHandle(threadHandle);}
/* * Function: MonitorChildThread * * Abstract: Monitor the child (shell) process */static DWORD WINAPI MonitorChildThread(LPVOID data){ DWORD exitCode; client_t *client = (client_t *) data;
telnetd_printf("Monitor thread running...\n");
WaitForSingleObject(client->hProcess, INFINITE);
GetExitCodeProcess(client->hProcess, &exitCode); telnetd_printf("Child process terminated with code %lx\n", exitCode);
/* signal the other threads to give up */ client->bTerminate = TRUE;
Sleep(500);
CloseHandle(client->hChildStdoutRd); CloseHandle(client->hChildStdinWr); CloseHandle(client->hProcess);
closesocket(client->socket);
telnetd_printf("Waiting for all threads to give up..\n");
while (client->bWriteToPipe || client->bReadFromPipe) { telnetd_printf("."); fflush(stdout); Sleep(1000); }
telnetd_printf("Cleanup for user '%s'\n", client->userID); free(client); return 0;}
/* * Function: WriteToPipeThread * * Abstract: read data from the telnet client socket * and pass it on to the shell process. */static DWORD WINAPI WriteToPipeThread(LPVOID data){ int iRead; DWORD dwWritten; CHAR chBuf[BUFSIZE]; client_t *client = (client_t *) data;
while (!client->bTerminate) { iRead = ReceiveLine(client->socket, chBuf, BUFSIZE, FALSE); if (iRead < 0) { telnetd_printf("Client disconnect\n"); break; } else if (iRead > 0) { if (strchr(chBuf, CTRLC)) { GenerateConsoleCtrlEvent(CTRL_C_EVENT, client->dwProcessId); } if (send(client->socket, chBuf, iRead, 0) < 0) { telnetd_printf("error writing to socket\n"); break; } if (! WriteFile(client->hChildStdinWr, chBuf, (DWORD) iRead, &dwWritten, NULL)) { telnetd_printf("Error writing to pipe\n"); break; } } }
if (!client->bTerminate) TerminateShell(client);
telnetd_printf("WriteToPipeThread terminated\n");
client->bWriteToPipe = FALSE; return 0;}
/* * Function: ReadFromPipeThread * * Abstract: Read data from the shell's stdout handle and * pass it on to the telnet client socket. */static DWORD WINAPI ReadFromPipeThread(LPVOID data) { DWORD dwRead; DWORD dwAvail; CHAR chBuf[BUFSIZE]; CHAR txBuf[BUFSIZE*2]; DWORD from,to; //char warning[] = "warning: rl_prep_terminal: cannot get terminal settings";
client_t *client = (client_t *) data;
while (!client->bTerminate && client->bWriteToPipe) { // Since we do not want to block, first peek... if (PeekNamedPipe(client->hChildStdoutRd, NULL, 0, NULL, &dwAvail, NULL) == 0) { telnetd_printf("Failed to peek in pipe\n"); break; } if (dwAvail) { if( ! ReadFile( client->hChildStdoutRd, chBuf, BUFSIZE, &dwRead, NULL) || dwRead == 0) { telnetd_printf("Failed to read from pipe\n"); break; } for (from=0, to=0; from<dwRead; from++, to++) { txBuf[to] = chBuf[from]; if (txBuf[to] == '\n') { txBuf[to] = '\r'; to++; txBuf[to] = '\n'; } } if (send(client->socket, txBuf, to, 0) < 0) { telnetd_printf("error writing to socket\n"); break; } } Sleep(100); /* Hmmm, oh well... what the heck! */ }
if (!client->bTerminate) TerminateShell(client);
telnetd_printf("ReadFromPipeThread terminated\n");
client->bReadFromPipe = FALSE; return 0;}
/* TerminateShell */ static void TerminateShell(client_t *client){ DWORD exitCode; DWORD dwWritten; char stop[] = "\003\r\nexit\r\n"; /* Ctrl-C + exit */
GetExitCodeProcess(client->hProcess, &exitCode);
if (exitCode == STILL_ACTIVE) { HANDLE hEvent = NULL; DWORD dwWaitResult;
telnetd_printf("user shell still active, send Ctrl-Break to group-id %lu\n", client->dwProcessId );
hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (hEvent == NULL) printf("CreateEvent error\n");
if (!GenerateConsoleCtrlEvent( CTRL_BREAK_EVENT, client->dwProcessId )) telnetd_printf("Failed to send Ctrl_break\n");
if (!GenerateConsoleCtrlEvent( CTRL_C_EVENT, client->dwProcessId )) telnetd_printf("Failed to send Ctrl_C\n");
if (!WriteFile(client->hChildStdinWr, stop, sizeof(stop), &dwWritten, NULL)) telnetd_printf("Error writing to pipe\n");
/* wait for our handler to be called */ dwWaitResult=WaitForSingleObject(hEvent, 500);
if (WAIT_FAILED==dwWaitResult) telnetd_printf("WaitForSingleObject failed\n");
GetExitCodeProcess(client->hProcess, &exitCode); if (exitCode == STILL_ACTIVE) { telnetd_printf("user shell still active, attempt to terminate it now...\n"); if (hEvent != NULL) { if (!CloseHandle(hEvent)) telnetd_printf("CloseHandle"); } TerminateProcess(client->hProcess, 0); } TerminateProcess(client->hProcess, 0); } TerminateProcess(client->hProcess, 0);}
/* ErrorExit */static VOID ErrorExit (LPTSTR lpszMessage) { fprintf(stderr, "%s\n", lpszMessage); if (bSocketInterfaceInitialised) { telnetd_printf("WSAGetLastError=%d\n", WSAGetLastError()); WSACleanup(); } ExitProcess(0); }