ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

telnetd.c
Go to the documentation of this file.
00001 /*
00002  * Abstract: a simple telnet 'daemon' for Windows hosts.
00003  *
00004  * Compiled & run successfully using MSVC 5.0 under Windows95 (requires 
00005  * Winsock2 update) and Windows98 and MSVC 6.0 under WindowsNT4
00006  *
00007  * Compiler options : no special options needed
00008  * Linker options   : add wsock32.lib or ws2_32.lib
00009  *
00010  * Written by fred.van.lieshout 'at' zonnet.nl
00011  * Use freely, no copyrights.
00012  * Use Linux.
00013  *
00014  * Parts Copyright Steven Edwards
00015  * Public Domain
00016  *
00017  * TODO: 
00018  * - access control
00019  * - will/won't handshake
00020  * - Unify Debugging output and return StatusCodes
00021  */
00022 
00023 #include "telnetd.h"
00024 
00025 #define telnetd_printf printf
00026 #if 0
00027 static inline int telnetd_printf(const char *format, ...);
00028 {
00029     printf(format,...);
00030     syslog (6, format);
00031 }
00032 #endif
00033 
00034 /* Local data */
00035 
00036 static BOOLEAN bShutdown = 0;
00037 static BOOLEAN bSocketInterfaceInitialised = 0;
00038 static int sock;
00039 
00040 /* In the future, some options might be passed here to handle
00041  * authentication options in the registry or command line
00042  * options passed to the service
00043  *
00044  * Once you are ready to turn on the service
00045  * rename this function
00046  * int kickoff_telnetd(void)
00047  */
00048 int kickoff_telnetd(void)
00049 {
00050   printf("Attempting to start Simple TelnetD\n");
00051 
00052 //  DetectPlatform();
00053   SetConsoleCtrlHandler(Cleanup, 1);
00054 
00055   if (!StartSocketInterface())
00056     ErrorExit("Unable to start socket interface\n");
00057 
00058   CreateSocket();
00059 
00060   while(!bShutdown) {
00061     WaitForConnect();
00062   }
00063 
00064   WSACleanup();
00065   return 0;
00066 }
00067 
00068 /* Cleanup */
00069 static BOOL WINAPI Cleanup(DWORD dwControlType)
00070 {
00071   if (bSocketInterfaceInitialised) {
00072     telnetd_printf("Cleanup...\n");
00073     WSACleanup();
00074   }
00075   return 0;
00076 }
00077 
00078 /* StartSocketInterface */
00079 static BOOLEAN StartSocketInterface(void)
00080 {
00081   WORD    wVersionRequested;
00082   WSADATA wsaData;
00083   int     err; 
00084 
00085   wVersionRequested = MAKEWORD( 2, 0 ); 
00086   err = WSAStartup(wVersionRequested, &wsaData);
00087   if (err != 0) {
00088     telnetd_printf("requested winsock version not supported\n");
00089     return 0;
00090   } 
00091 
00092   bSocketInterfaceInitialised = 1; /* for ErrorExit function */
00093 
00094   if ( wsaData.wVersion != wVersionRequested)
00095     ErrorExit("requested winsock version not supported\n");
00096 
00097   telnetd_printf("TelnetD, using %s\n", wsaData.szDescription);
00098   return 1;
00099 }
00100 
00101 /* CreateSocket */
00102 static void CreateSocket(void)
00103 {
00104    struct sockaddr_in sa;
00105 
00106    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
00107    if (sock < 0)
00108      ErrorExit("Cannot create socket");
00109 
00110    memset(&sa, 0, sizeof(sa));
00111    sa.sin_family = AF_INET;
00112    sa.sin_addr.s_addr = INADDR_ANY;
00113    sa.sin_port = htons(TELNET_PORT);
00114 
00115    if (bind(sock, (struct sockaddr*) &sa, sizeof(sa)) != 0)
00116       ErrorExit("Cannot bind address to socket");
00117 }
00118 
00119 /* WaitForConnect */
00120 static void WaitForConnect(void)
00121 {
00122   struct sockaddr_in sa;
00123   int new_sock;
00124 
00125   if (listen(sock, 1) < 0)
00126      ErrorExit("Cannot listen on socket");
00127 
00128   if ((new_sock = accept(sock, (struct sockaddr*) &sa, NULL)) < 0) {
00129     fprintf(stderr, "Failed to accept incoming call\n");
00130   } else {
00131     telnetd_printf("user connected on socket %d, port %d, address %lx\n", new_sock,
00132                                        htons(sa.sin_port), sa.sin_addr.s_addr);
00133     UserLogin(new_sock);
00134   }
00135 }
00136 
00137 /* Function: UserLogin */
00138 static void UserLogin(int client_socket)
00139 {
00140   HANDLE     threadHandle;
00141   client_t  *client = malloc(sizeof(client_t));
00142 
00143   if (client == NULL)
00144     ErrorExit("failed to allocate memory for client");
00145 
00146   client->socket = client_socket;
00147   threadHandle = CreateThread(NULL, 0, UserLoginThread, client, 0, NULL);
00148   if (threadHandle == NULL)
00149     free(client);
00150   else
00151     CloseHandle(threadHandle);
00152 }
00153 
00154 /* Function: UserLoginThread */
00155 static DWORD WINAPI UserLoginThread(LPVOID data)
00156 {
00157   client_t  *client = (client_t *) data;
00158   char       welcome[256];
00159   char       hostname[64] = "Unknown";
00160   char      *pwdPrompt = "\r\npass:";
00161   //char      *logonPrompt = "\r\nLogin OK, please wait...";
00162   //char      *byebye = "\r\nWrong! bye bye...\r\n";
00163   char       userID[USERID_SIZE];
00164   char       password[USERID_SIZE];
00165   int        received;
00166   char      *terminator;
00167 
00168   if (DoTelnetHandshake(client->socket)) {
00169     closesocket(client->socket);
00170     free(client);
00171     return 0;
00172   }
00173 
00174   gethostname(hostname, sizeof(hostname));
00175   sprintf(welcome, "\r\nWelcome to %s, please identify yourself\r\n\r\nuser:", hostname);
00176 
00177   if (send(client->socket, welcome, strlen(welcome), 0) < 0) {   
00178     closesocket(client->socket);
00179     free(client);
00180     return 0;
00181   }
00182   received = ReceiveLine(client->socket, userID, sizeof(userID), Echo );
00183   if (received < 0) {
00184     closesocket(client->socket);
00185     free(client);
00186     return 0;
00187   } else if (received) {
00188     if ((terminator = strchr(userID, CR)) != NULL) {
00189       *terminator = '\0';
00190     }
00191   }
00192 
00193   if (send(client->socket, pwdPrompt, strlen(pwdPrompt), 0) < 0) {   
00194     closesocket(client->socket);
00195     free(client);
00196     return 0;
00197   }
00198   received = ReceiveLine(client->socket, password, sizeof(password), Password );
00199 
00200 #if 0
00201   if (received < 0) {
00202     closesocket(client->socket);
00203     free(client);
00204     return 0;
00205   } else if (received) {
00206     if ((terminator = strchr(password, CR)) != NULL) {
00207       *terminator = '\0';
00208     }
00209   }
00210 #endif
00211 
00212   /* TODO: do authentication here */
00213 
00214   
00215   telnetd_printf("User '%p' logged on\n", userID);
00216 #if 0
00217   strcpy(client->userID, userID);
00218   if (send(client->socket, logonPrompt, strlen(logonPrompt), 0) < 0) {   
00219     closesocket(client->socket);
00220     free(client);
00221     return 0;
00222   }
00223 #endif
00224   RunShell(client);
00225   return 0;
00226 }
00227 
00228 /* Function: DoTelnetHandshake */
00229 static int DoTelnetHandshake(int sock)
00230 {
00231   int retval;
00232   int received;
00233   fd_set set;
00234   struct timeval timeout = { HANDSHAKE_TIMEOUT, 0 };
00235 
00236   char will_echo[]=
00237       IAC DONT ECHO
00238       IAC WILL ECHO
00239       IAC WILL NAWS
00240       IAC WILL SUPPRESS_GO_AHEAD
00241       IAC DO SUPPRESS_GO_AHEAD
00242       IAC DONT NEWENVIRON
00243       IAC WONT NEWENVIRON
00244       IAC WONT LINEMODE
00245       IAC DO NAWS
00246       IAC SB TERMINAL_TYPE "\x01" IAC SE
00247       ;
00248 
00249   unsigned char client_reply[256];
00250 
00251   if (send(sock, will_echo, sizeof(will_echo), 0) < 0) {   
00252     return -1;
00253   }
00254 
00255   /* Now wait for client response (and ignore it) */
00256   FD_ZERO(&set);
00257   FD_SET(sock, &set);
00258 
00259   do {
00260     retval = select(0, &set, NULL, NULL, &timeout);
00261     /* check for error */
00262     if (retval < 0) {
00263       return -1;
00264       /* check for timeout */
00265     } else if (retval == 0) {
00266       return 0;
00267     }
00268     /* no error and no timeout, we have data in our sock */
00269     received = recv(sock, (char *) client_reply, sizeof(client_reply), 0);
00270     if (received <= 0) {
00271      return -1;
00272     }
00273   } while (retval);
00274 
00275   return 0;
00276 }
00277 
00278 /*
00279 ** Function: ReceiveLine
00280 **
00281 ** Abstract: receive until timeout or CR
00282 ** In      : sock, len
00283 ** Out     : buffer
00284 ** Result  : int
00285 ** Pre     : 'sock' must be valid socket
00286 ** Post    : (result = the number of bytes read into 'buffer')
00287 **           OR (result = -1 and error)
00288 */
00289 static int ReceiveLine(int sock, char *buffer, int len, EchoMode echo)
00290 {
00291   int            i = 0;
00292   int            retval;
00293   fd_set         set;
00294   struct timeval timeout = { 0, 100000 };
00295   char           del[3] = { BS, ' ', BS };
00296   char           asterisk[1] = { '*' };
00297 
00298   FD_ZERO(&set);
00299   FD_SET(sock, &set);
00300 
00301   memset(buffer, '\0', len);
00302 
00303   do {
00304     /* When we're in echo mode, we do not need a timeout */
00305     retval = select(0, &set, NULL, NULL, (echo ? NULL : &timeout) );
00306     /* check for error */
00307     if (retval < 0) {
00308       return -1;
00309       /* check for timeout */
00310     } else if (retval == 0) {
00311       /* return number of characters received so far */
00312       return i;
00313     }
00314     /* no error and no timeout, we have data in our sock */
00315     if (recv(sock, &buffer[i], 1, 0) <= 0) {
00316       return -1;
00317     }
00318     if ((buffer[i] == '\0') || (buffer[i] == LF)) {
00319       /* ignore null characters and linefeeds from DOS telnet clients */
00320       buffer[i] = '\0';
00321     } else if ((buffer[i] == DEL) || (buffer[i] == BS)) {
00322       /* handle delete and backspace */
00323       buffer[i] = '\0';
00324       if (echo) {
00325           if (i > 0) {
00326           i--;
00327           buffer[i] = '\0';
00328           if (send(sock, del, sizeof(del), 0) < 0) {
00329             return -1;
00330           }
00331         }
00332       } else {
00333         buffer[i] = BS;  /* Let shell process handle it */
00334           i++;
00335       }
00336     } else {
00337       /* echo typed characters */
00338       if (echo == Echo && send(sock, &buffer[i], 1, 0) < 0) {
00339         return -1;
00340       } else if (echo == Password && send(sock, asterisk, sizeof(asterisk), 0) < 0) {
00341         return -1;
00342       }
00343       if (buffer[i] == CR) {
00344         i++;
00345         buffer[i] = LF; /* append LF for DOS command processor */
00346         i++;
00347         return i;
00348       }
00349 
00350       i++;
00351     }    
00352   } while (i < len);
00353 
00354   return i;
00355 }
00356 
00357 /*
00358 ** Function: RunShell
00359 */
00360 static void RunShell(client_t *client) 
00361 { 
00362    HANDLE                threadHandle;
00363    HANDLE                hChildStdinRd;
00364    HANDLE                hChildStdinWr;
00365    HANDLE                hChildStdoutRd;
00366    HANDLE                hChildStdoutWr;
00367    STARTUPINFO           si;
00368    PROCESS_INFORMATION   piProcInfo;
00369    SECURITY_ATTRIBUTES   saAttr;
00370 
00371    const char *name = "c:\\reactos\\system32\\cmd.exe";
00372    const char *cmd = NULL;
00373    //const char *name = "d:\\cygwin\\bin\\bash.exe";
00374    //const char *cmd = "d:\\cygwin\\bin\\bash.exe --login -i";
00375    
00376    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
00377    saAttr.bInheritHandle = TRUE; 
00378    saAttr.lpSecurityDescriptor = NULL; 
00379    
00380    // Create a pipe for the child process's STDOUT.  
00381    if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) 
00382       ErrorExit("Stdout pipe creation failed\n");  
00383 
00384    if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) 
00385       ErrorExit("Stdin pipe creation failed\n");  
00386 
00387 
00388    client->bTerminate = FALSE;
00389    client->bWriteToPipe = TRUE;
00390    client->bReadFromPipe = TRUE;
00391    client->hChildStdinWr = hChildStdinWr;   
00392    client->hChildStdoutRd = hChildStdoutRd;
00393 
00394 
00395    // Create the child process (the shell)
00396    telnetd_printf("Creating child process...\n");
00397 
00398    ZeroMemory( &si, sizeof(STARTUPINFO) );
00399    si.cb = sizeof(STARTUPINFO);  
00400 
00401    si.dwFlags = STARTF_USESTDHANDLES;
00402    si.hStdInput = hChildStdinRd;
00403    si.hStdOutput = hChildStdoutWr;
00404    si.hStdError = hChildStdoutWr;
00405 
00406    //si.dwFlags |= STARTF_USESHOWWINDOW;
00407    //si.wShowWindow = SW_SHOW;
00408 
00409    if (!CreateProcess((LPSTR) name,              // executable module
00410                       (LPSTR) cmd,               // command line 
00411                       NULL,                      // process security attributes 
00412                       NULL,                      // primary thread security attributes 
00413                       TRUE,                      // handles are inherited 
00414                       DETACHED_PROCESS +         // creation flags 
00415                       CREATE_NEW_PROCESS_GROUP,
00416                       NULL,                      // use parent's environment 
00417                       NULL,                      // use parent's current directory 
00418                       &si,                       // startup info
00419                       &piProcInfo)) {
00420      ErrorExit("Create process failed");
00421    }
00422 
00423    client->hProcess = piProcInfo.hProcess;
00424    client->dwProcessId = piProcInfo.dwProcessId;
00425 
00426    telnetd_printf("New child created (groupid=%lu)\n", client->dwProcessId);
00427 
00428    // No longer need these in the parent...
00429    if (!CloseHandle(hChildStdoutWr)) 
00430      ErrorExit("Closing handle failed");  
00431 
00432    if (!CloseHandle(hChildStdinRd)) 
00433      ErrorExit("Closing handle failed");  
00434 
00435    threadHandle = CreateThread(NULL, 0, WriteToPipeThread, client, 0, NULL);
00436    if (threadHandle != NULL)
00437      CloseHandle(threadHandle);
00438 
00439    threadHandle = CreateThread(NULL, 0, ReadFromPipeThread, client, 0, NULL);
00440    if (threadHandle != NULL)
00441      CloseHandle(threadHandle);
00442 
00443    threadHandle = CreateThread(NULL, 0, MonitorChildThread, client, 0, NULL);
00444    if (threadHandle != NULL)
00445      CloseHandle(threadHandle);
00446 } 
00447 
00448 /*
00449  * Function: MonitorChildThread
00450  *
00451  * Abstract: Monitor the child (shell) process
00452  */
00453 static DWORD WINAPI MonitorChildThread(LPVOID data)
00454 {
00455   DWORD exitCode;
00456   client_t *client = (client_t *) data;
00457 
00458   telnetd_printf("Monitor thread running...\n");
00459 
00460   WaitForSingleObject(client->hProcess, INFINITE);
00461 
00462   GetExitCodeProcess(client->hProcess, &exitCode);
00463   telnetd_printf("Child process terminated with code %lx\n", exitCode);
00464 
00465   /* signal the other threads to give up */
00466   client->bTerminate = TRUE;
00467 
00468   Sleep(500);
00469 
00470   CloseHandle(client->hChildStdoutRd);
00471   CloseHandle(client->hChildStdinWr);       
00472   CloseHandle(client->hProcess);
00473 
00474   closesocket(client->socket);
00475 
00476   telnetd_printf("Waiting for all threads to give up..\n");
00477 
00478   while (client->bWriteToPipe || client->bReadFromPipe) {
00479     telnetd_printf(".");
00480     fflush(stdout);
00481     Sleep(1000);
00482   }
00483 
00484   telnetd_printf("Cleanup for user '%s'\n", client->userID);
00485   free(client);
00486   return 0;
00487 }
00488 
00489 /*
00490  * Function: WriteToPipeThread
00491  * 
00492  * Abstract: read data from the telnet client socket
00493  *           and pass it on to the shell process.
00494  */
00495 static DWORD WINAPI WriteToPipeThread(LPVOID data)
00496 {
00497   int       iRead;
00498   DWORD     dwWritten;
00499   CHAR      chBuf[BUFSIZE];
00500   client_t *client = (client_t *) data;
00501 
00502   while (!client->bTerminate) {
00503     iRead = ReceiveLine(client->socket, chBuf, BUFSIZE, FALSE);
00504     if (iRead < 0) {
00505       telnetd_printf("Client disconnect\n");
00506       break;
00507     } else if (iRead > 0) {
00508       if (strchr(chBuf, CTRLC)) {
00509         GenerateConsoleCtrlEvent(CTRL_C_EVENT, client->dwProcessId);
00510       }
00511       if (send(client->socket, chBuf, iRead, 0) < 0) {
00512          telnetd_printf("error writing to socket\n");
00513          break;    
00514       }
00515       if (! WriteFile(client->hChildStdinWr, chBuf, (DWORD) iRead, &dwWritten, NULL)) {
00516         telnetd_printf("Error writing to pipe\n");
00517         break;
00518       }
00519     }
00520   }
00521 
00522   if (!client->bTerminate)
00523     TerminateShell(client);
00524 
00525   telnetd_printf("WriteToPipeThread terminated\n");
00526 
00527   client->bWriteToPipe = FALSE;
00528   return 0;
00529 }
00530 
00531 /*
00532  * Function: ReadFromPipeThread
00533  *
00534  * Abstract: Read data from the shell's stdout handle and
00535  *           pass it on to the telnet client socket.
00536  */
00537 static DWORD WINAPI ReadFromPipeThread(LPVOID data) 
00538 {    
00539   DWORD dwRead;
00540   DWORD dwAvail;
00541   CHAR chBuf[BUFSIZE];
00542   CHAR txBuf[BUFSIZE*2];
00543   DWORD from,to;
00544   //char warning[] = "warning: rl_prep_terminal: cannot get terminal settings";
00545 
00546   client_t *client = (client_t *) data;
00547 
00548   while (!client->bTerminate && client->bWriteToPipe) {
00549     // Since we do not want to block, first peek...
00550     if (PeekNamedPipe(client->hChildStdoutRd, NULL, 0, NULL, &dwAvail, NULL) == 0) {
00551       telnetd_printf("Failed to peek in pipe\n");
00552       break;
00553     }
00554     if (dwAvail) {
00555       if( ! ReadFile( client->hChildStdoutRd, chBuf, BUFSIZE, &dwRead, NULL) ||
00556            dwRead == 0) {
00557         telnetd_printf("Failed to read from pipe\n");
00558         break;
00559       }
00560       for (from=0, to=0; from<dwRead; from++, to++) {
00561         txBuf[to] = chBuf[from];
00562         if (txBuf[to] == '\n') {
00563             txBuf[to] = '\r';
00564             to++;
00565             txBuf[to] = '\n';
00566         }
00567       }
00568       if (send(client->socket, txBuf, to, 0) < 0) {
00569          telnetd_printf("error writing to socket\n");
00570          break;    
00571       }
00572     }
00573     Sleep(100); /* Hmmm, oh well... what the heck! */
00574   }
00575 
00576   if (!client->bTerminate)
00577     TerminateShell(client);
00578 
00579   telnetd_printf("ReadFromPipeThread terminated\n");
00580 
00581   client->bReadFromPipe = FALSE;
00582   return 0;
00583 }
00584 
00585 /* TerminateShell */ 
00586 static void TerminateShell(client_t *client)
00587 {
00588     DWORD exitCode;
00589     DWORD dwWritten;
00590     char stop[] = "\003\r\nexit\r\n"; /* Ctrl-C + exit */
00591 
00592     GetExitCodeProcess(client->hProcess, &exitCode);
00593 
00594     if (exitCode == STILL_ACTIVE)
00595     {
00596         HANDLE hEvent = NULL;
00597         DWORD dwWaitResult;
00598 
00599         telnetd_printf("user shell still active, send Ctrl-Break to group-id %lu\n", client->dwProcessId );
00600 
00601         hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
00602 
00603         if (hEvent == NULL)
00604             printf("CreateEvent error\n");
00605 
00606         if (!GenerateConsoleCtrlEvent( CTRL_BREAK_EVENT, client->dwProcessId ))
00607             telnetd_printf("Failed to send Ctrl_break\n");
00608 
00609         if (!GenerateConsoleCtrlEvent( CTRL_C_EVENT, client->dwProcessId ))
00610             telnetd_printf("Failed to send Ctrl_C\n");
00611 
00612         if (!WriteFile(client->hChildStdinWr, stop, sizeof(stop), &dwWritten, NULL))
00613             telnetd_printf("Error writing to pipe\n");
00614 
00615         /* wait for our handler to be called */
00616         dwWaitResult=WaitForSingleObject(hEvent, 500);
00617 
00618         if (WAIT_FAILED==dwWaitResult)
00619             telnetd_printf("WaitForSingleObject failed\n");
00620 
00621         GetExitCodeProcess(client->hProcess, &exitCode);
00622         if (exitCode == STILL_ACTIVE) 
00623         {
00624             telnetd_printf("user shell still active, attempt to terminate it now...\n");
00625         
00626             if (hEvent != NULL) 
00627             {
00628                 if (!CloseHandle(hEvent)) 
00629                    telnetd_printf("CloseHandle");
00630             }
00631             TerminateProcess(client->hProcess, 0);
00632         }
00633         TerminateProcess(client->hProcess, 0);
00634     }
00635     TerminateProcess(client->hProcess, 0);
00636 }
00637 
00638 /* ErrorExit */
00639 static VOID ErrorExit (LPTSTR lpszMessage) 
00640 { 
00641    fprintf(stderr, "%s\n", lpszMessage);
00642    if (bSocketInterfaceInitialised) {
00643      telnetd_printf("WSAGetLastError=%d\n", WSAGetLastError());
00644      WSACleanup();
00645    }
00646    ExitProcess(0); 
00647 } 
00648 

Generated on Sat May 26 2012 04:16:38 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.