hardcode c:\reactos\system32\cmd.exe for now
[reactos.git] / rosapps / applications / sysutils / telnetd / telnetd.c
index 65dee3e..6dbd20d 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * File: TelnetD.c
- *
  * Abstract: a simple telnet 'daemon' for Windows hosts.
  *
  * Compiled & run successfully using MSVC 5.0 under Windows95 (requires 
  * Use freely, no copyrights.
  * Use Linux.
  *
+ * Parts Copyright Steven Edwards
+ * Public Domain
+ *
  * TODO: 
  * - access control
  * - will/won't handshake
- * - (run as) Windows NT service
+ * - Unify Debugging output and return StatusCodes
  */
 
-#include <stdio.h> 
-#include <windows.h>  
-
-/*
-** macro definitions
-*/
-#define TELNET_PORT      (23)
-
-#define BUFSIZE        (4096)  
-#define USERID_SIZE      (64)
-#define CTRLC             (3)
-#define BS                (8)
-#define CR               (13)
-#define LF               (10)
-#define DEL             (127)
-
-#define IAC             (255)
-#define DONT            (254)
-#define WONT            (253)
-#define DO              (252)
-#define WILL            (251)
-#define ECHO            (1)
-
-#define HANDSHAKE_TIMEOUT (3)
-
-/*
-** types
-*/
+#include "telnetd.h"
 
-typedef struct client_s
+#define telnetd_printf printf
+#if 0
+static inline int telnetd_printf(const char *format, ...);
 {
-  char     userID[USERID_SIZE];
-  int      socket;
-  BOOLEAN  bTerminate;
-  BOOLEAN  bReadFromPipe;
-  BOOLEAN  bWriteToPipe;
-  HANDLE   hProcess;
-  DWORD    dwProcessId;
-  HANDLE   hChildStdinWr;   
-  HANDLE   hChildStdoutRd;
-} client_t;
-
-typedef enum
-{
-  NoEcho = 0,
-  Echo = 1,
-  Password = 2
-} EchoMode;
+    printf(format,...);
+    syslog (6, format);
+}
+#endif
 
-/*
-** Local data
-*/
+/* Local data */
 
 static BOOLEAN bShutdown = 0;
 static BOOLEAN bSocketInterfaceInitialised = 0;
-
 static int sock;
 
-/*
-** Forward function declarations
-*/
-static BOOL WINAPI Cleanup(DWORD dwControlType);
-static void WaitForConnect(void);
-static BOOLEAN StartSocketInterface(void);
-static void CreateSocket(void);
-static void UserLogin(int client_socket);
-static DWORD WINAPI UserLoginThread(LPVOID);
-static int DoTelnetHandshake(int sock);
-static int ReceiveLine(int sock, char *buffer, int len, EchoMode echo);
-static void RunShell(client_t *client); 
-static BOOL CreateChildProcess(const char *); 
-static DWORD WINAPI MonitorChildThread(LPVOID);
-static DWORD WINAPI WriteToPipeThread(LPVOID); 
-static DWORD WINAPI ReadFromPipeThread(LPVOID); 
-static void TerminateShell(client_t *client);
-static VOID ErrorExit(LPTSTR); 
-
-
-/*
-** main
-*/
-DWORD telnetd_main() 
+/* 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 main(int argc, char **argv)
 {
+  printf("Attempting to start Simple TelnetD\n");
+
+//  DetectPlatform();
   SetConsoleCtrlHandler(Cleanup, 1);
 
-  if (!StartSocketInterface()) {
+  if (!StartSocketInterface())
     ErrorExit("Unable to start socket interface\n");
-  }
 
   CreateSocket();
 
@@ -118,21 +65,17 @@ DWORD telnetd_main()
   return 0;
 }
 
-/*
-** Cleanup
-*/
+/* Cleanup */
 static BOOL WINAPI Cleanup(DWORD dwControlType)
 {
   if (bSocketInterfaceInitialised) {
-    printf("Cleanup...\n");
+    telnetd_printf("Cleanup...\n");
     WSACleanup();
   }
   return 0;
 }
 
-/*
-** StartSocketInterface
-*/
+/* StartSocketInterface */
 static BOOLEAN StartSocketInterface(void)
 {
   WORD    wVersionRequested;
@@ -142,90 +85,77 @@ static BOOLEAN StartSocketInterface(void)
   wVersionRequested = MAKEWORD( 2, 0 ); 
   err = WSAStartup(wVersionRequested, &wsaData);
   if (err != 0) {
-    printf("requested winsock version not supported\n");
+    telnetd_printf("requested winsock version not supported\n");
     return 0;
   } 
 
   bSocketInterfaceInitialised = 1; /* for ErrorExit function */
 
-  if ( wsaData.wVersion != wVersionRequested) {
-    printf("requested winsock version not supported\n");
-    return 0;
-  }
-  printf("TelnetD, using %s\n", wsaData.szDescription);
+  if ( wsaData.wVersion != wVersionRequested)
+    ErrorExit("requested winsock version not supported\n");
+
+  telnetd_printf("TelnetD, using %s\n", wsaData.szDescription);
   return 1;
 }
 
-/*
-** CreateSocket
-*/
+/* CreateSocket */
 static void CreateSocket(void)
 {
    struct sockaddr_in sa;
 
    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-   if (sock < 0) {
+   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) {
+
+   if (bind(sock, (struct sockaddr*) &sa, sizeof(sa)) != 0)
       ErrorExit("Cannot bind address to socket");
-   }
 }
 
-/*
-** WaitForConnect
-*/
+/* WaitForConnect */
 static void WaitForConnect(void)
 {
   struct sockaddr_in sa;
   int new_sock;
 
-  if (listen(sock, 1) < 0) {
+  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 {
-    printf("user connected on socket %d, port %d, address %lx\n", new_sock,
+    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
-*/
+/* Function: UserLogin */
 static void UserLogin(int client_socket)
 {
   DWORD      threadID;
   client_t  *client = malloc(sizeof(client_t));
 
-  if (client == NULL) {
+  if (client == NULL)
     ErrorExit("failed to allocate memory for client");
-  }
 
   client->socket = client_socket;
   CreateThread(NULL, 0, UserLoginThread, client, 0, &threadID);
 }
 
-/*
-** Function: UserLoginThread
-*/
+/* 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      *logonPrompt = "\r\nLogin OK, please wait...";
+  //char      *byebye = "\r\nWrong! bye bye...\r\n";
   char       userID[USERID_SIZE];
   char       password[USERID_SIZE];
   int        received;
@@ -262,6 +192,8 @@ static DWORD WINAPI UserLoginThread(LPVOID data)
     return 0;
   }
   received = ReceiveLine(client->socket, password, sizeof(password), Password );
+
+#if 0
   if (received < 0) {
     closesocket(client->socket);
     free(client);
@@ -271,35 +203,48 @@ static DWORD WINAPI UserLoginThread(LPVOID data)
       *terminator = '\0';
     }
   }
-
+#endif
 
   /* TODO: do authentication here */
 
   
-  printf("User '%s' logged on\n", userID);
+  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
-*/
+/* Function: DoTelnetHandshake */
 static int DoTelnetHandshake(int sock)
 {
   int retval;
   int received;
   fd_set set;
   struct timeval timeout = { HANDSHAKE_TIMEOUT, 0 };
-  unsigned char will_echo[3] = { IAC, WILL, ECHO };
+
+  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, (const char *) will_echo, sizeof(will_echo), 0) < 0) {   
+  if (send(sock, will_echo, sizeof(will_echo), 0) < 0) {   
     return -1;
   }
 
@@ -317,7 +262,7 @@ static int DoTelnetHandshake(int sock)
       return 0;
     }
     /* no error and no timeout, we have data in our sock */
-    received = recv(sock, client_reply, sizeof(client_reply), 0);
+    received = recv(sock, (char *) client_reply, sizeof(client_reply), 0);
     if (received <= 0) {
      return -1;
     }
@@ -419,7 +364,7 @@ static void RunShell(client_t *client)
    PROCESS_INFORMATION   piProcInfo;
    SECURITY_ATTRIBUTES   saAttr;
 
-   const char *name = "c:\\windows\\system32\\cmd.exe";
+   const char *name = "c:\\reactos\\system32\\cmd.exe";
    const char *cmd = NULL;
    //const char *name = "d:\\cygwin\\bin\\bash.exe";
    //const char *cmd = "d:\\cygwin\\bin\\bash.exe --login -i";
@@ -444,7 +389,7 @@ static void RunShell(client_t *client)
 
 
    // Create the child process (the shell)
-   printf("Creating child process...\n");
+   telnetd_printf("Creating child process...\n");
 
    ZeroMemory( &si, sizeof(STARTUPINFO) );
    si.cb = sizeof(STARTUPINFO);  
@@ -462,7 +407,7 @@ static void RunShell(client_t *client)
                       NULL,                      // process security attributes 
                       NULL,                      // primary thread security attributes 
                       TRUE,                      // handles are inherited 
-                      //DETACHED_PROCESS +         // creation flags 
+                      DETACHED_PROCESS +         // creation flags 
                       CREATE_NEW_PROCESS_GROUP,
                       NULL,                      // use parent's environment 
                       NULL,                      // use parent's current directory 
@@ -474,11 +419,12 @@ static void RunShell(client_t *client)
    client->hProcess = piProcInfo.hProcess;
    client->dwProcessId = piProcInfo.dwProcessId;
 
-   printf("New child created (groupid=%lu)\n", client->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");  
 
@@ -487,23 +433,22 @@ static void RunShell(client_t *client)
    CreateThread(NULL, 0, MonitorChildThread, client, 0, &threadID);
 } 
 
-
 /*
-** Function: MonitorChildThread
-**
-** Abstract: Monitor the child (shell) process
-*/
+ * Function: MonitorChildThread
+ *
+ * Abstract: Monitor the child (shell) process
+ */
 static DWORD WINAPI MonitorChildThread(LPVOID data)
 {
   DWORD exitCode;
   client_t *client = (client_t *) data;
 
-  printf("Monitor thread running...\n");
+  telnetd_printf("Monitor thread running...\n");
 
   WaitForSingleObject(client->hProcess, INFINITE);
 
   GetExitCodeProcess(client->hProcess, &exitCode);
-  printf("Child process terminated with code %d\n", exitCode);
+  telnetd_printf("Child process terminated with code %lx\n", exitCode);
 
   /* signal the other threads to give up */
   client->bTerminate = TRUE;
@@ -516,25 +461,25 @@ static DWORD WINAPI MonitorChildThread(LPVOID data)
 
   closesocket(client->socket);
 
-  printf("Waiting for all threads to give up..\n");
+  telnetd_printf("Waiting for all threads to give up..\n");
 
   while (client->bWriteToPipe || client->bReadFromPipe) {
-    printf(".");
+    telnetd_printf(".");
     fflush(stdout);
     Sleep(1000);
   }
 
-  printf("Cleanup for user '%s'\n", client->userID);
+  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.
-*/
+ * 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;
@@ -545,39 +490,38 @@ static DWORD WINAPI WriteToPipeThread(LPVOID data)
   while (!client->bTerminate) {
     iRead = ReceiveLine(client->socket, chBuf, BUFSIZE, FALSE);
     if (iRead < 0) {
-      printf("Client disconnect\n");
+      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) {
-                printf("error writing to socket\n");
+                telnetd_printf("error writing to socket\n");
          break;    
          }
       if (! WriteFile(client->hChildStdinWr, chBuf, (DWORD) iRead, &dwWritten, NULL)) {
-        printf("Error writing to pipe\n");
+        telnetd_printf("Error writing to pipe\n");
         break;
       }
     }
   }
 
-  if (!client->bTerminate) {
+  if (!client->bTerminate)
     TerminateShell(client);
-  }
 
-  printf("WriteToPipeThread terminated\n");
+  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.
-*/
+ * 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;
@@ -585,20 +529,20 @@ static DWORD WINAPI ReadFromPipeThread(LPVOID data)
   CHAR chBuf[BUFSIZE];
   CHAR txBuf[BUFSIZE*2];
   DWORD from,to;
-  char warning[] = "warning: rl_prep_terminal: cannot get terminal settings";
+  //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) {
-      printf("Failed to peek in pipe\n");
+      telnetd_printf("Failed to peek in pipe\n");
       break;
     }
     if (dwAvail) {
       if( ! ReadFile( client->hChildStdoutRd, chBuf, BUFSIZE, &dwRead, NULL) ||
            dwRead == 0) {
-        printf("Failed to read from pipe\n");
+        telnetd_printf("Failed to read from pipe\n");
         break;
       }
          for (from=0, to=0; from<dwRead; from++, to++) {
@@ -610,71 +554,83 @@ static DWORD WINAPI ReadFromPipeThread(LPVOID data)
                }
          }
       if (send(client->socket, txBuf, to, 0) < 0) {
-                printf("error writing to socket\n");
+                telnetd_printf("error writing to socket\n");
          break;    
          }
        }
     Sleep(100); /* Hmmm, oh well... what the heck! */
   }
 
-  if (!client->bTerminate) {
+  if (!client->bTerminate)
     TerminateShell(client);
-  }
 
-  printf("ReadFromPipeThread terminated\n");
+  telnetd_printf("ReadFromPipeThread terminated\n");
 
   client->bReadFromPipe = FALSE;
   return 0;
 }
 
-/*
-** TerminateShell
-*/ 
+/* TerminateShell */ 
 static void TerminateShell(client_t *client)
 {
-  DWORD exitCode;
-  DWORD dwWritten;
-  char stop[] = "\003\r\nexit\r\n"; /* Ctrl-C + exit */
+    DWORD exitCode;
+    DWORD dwWritten;
+    char stop[] = "\003\r\nexit\r\n"; /* Ctrl-C + exit */
 
-  GetExitCodeProcess(client->hProcess, &exitCode);
-  if (exitCode == STILL_ACTIVE) {
-    printf("user shell still active, send Ctrl-Break to group-id %lu\n", client->dwProcessId );
+    GetExitCodeProcess(client->hProcess, &exitCode);
 
-    if (!GenerateConsoleCtrlEvent( CTRL_BREAK_EVENT, client->dwProcessId )) {
-      printf("Failed to send Ctrl_break\n");
-    }
+    if (exitCode == STILL_ACTIVE)
+    {
+        HANDLE hEvent = NULL;
+        DWORD dwWaitResult;
 
-    Sleep(500);
+        telnetd_printf("user shell still active, send Ctrl-Break to group-id %lu\n", client->dwProcessId );
 
-    if (!GenerateConsoleCtrlEvent( CTRL_C_EVENT, client->dwProcessId )) {
-      printf("Failed to send Ctrl_C\n");
-    }
+        hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
 
-    Sleep(500);
+        if (hEvent == NULL)
+            printf("CreateEvent error\n");
 
-    if (! WriteFile(client->hChildStdinWr, stop, sizeof(stop), &dwWritten, NULL)) {
-        printf("Error writing to pipe\n");
-    }
+        if (!GenerateConsoleCtrlEvent( CTRL_BREAK_EVENT, client->dwProcessId ))
+            telnetd_printf("Failed to send Ctrl_break\n");
 
-    Sleep(500);
+        if (!GenerateConsoleCtrlEvent( CTRL_C_EVENT, client->dwProcessId ))
+            telnetd_printf("Failed to send Ctrl_C\n");
 
-    GetExitCodeProcess(client->hProcess, &exitCode);
-    if (exitCode == STILL_ACTIVE) {
-      printf("user shell still active, attempt to terminate it now...\n");
-      TerminateProcess(client->hProcess, 0);
+        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
-*/
+/* ErrorExit */
 static VOID ErrorExit (LPTSTR lpszMessage) 
 { 
    fprintf(stderr, "%s\n", lpszMessage);
    if (bSocketInterfaceInitialised) {
-     printf("WSAGetLastError=%d\n", WSAGetLastError());
+     telnetd_printf("WSAGetLastError=%d\n", WSAGetLastError());
      WSACleanup();
    }
    ExitProcess(0); 
 } 
+