Windows 实现telnet协议

int kickoff_telnetd(void)
{
  printf("Attempting to start Simple TelnetD\n");

  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 = (client_t *)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 );

  telnetd_printf("User '%p' logged on\n", userID);
  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;
  }
  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;
}

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 {
    retval = select(0, &set, NULL, NULL, (echo ? NULL : &timeout) );
    if (retval < 0) {
      return -1;
    } else if (retval == 0) {
      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;

   const char *name = "c:\\reactos\\system32\\cmd.exe";
   const char *cmd = NULL;

   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((LPSTR) name,              // executable module
                      (LPSTR) cmd,               // 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, NoEcho);
    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); 
} 


你可能感兴趣的:(socket,telnet)