VTTelnetSession.cpp
上传用户:wsk323
上传日期:2007-01-05
资源大小:403k
文件大小:23k
- /*---------------------------------------------------------------------------
- Copyright: E. Brady Trexler
- Creation: March 13, 1998 (based on MicroSoft Win32.hlp code snippet
- and F. Piette's telnet server demo).
- Description: VTTelnetSession -- class starting a command interpreter with pipes
- inherited from main application. Anonymous pipes are created for
- STDIN, STDOUT, and STDERR. Then, the child process (the command
- interpreter) is started. One thread is started that continuously
- checks for data from a socket and feeds this data to STDIN. Two
- other threads continously check for output from STDOUT and
- STDERR feed that back to the socket. If the "exit" command is
- given to the command interpreter, the STDOUT listening thread is
- exited and the socket is disconnected.
- Legal issues: Copyright (C) 1998 by E. Brady Trexler and Fran鏾is Piette
- This software is provided 'as-is', without any express or
- implied warranty. In no event will the author be held liable
- for any damages arising from the use of this software.
- Permission is granted to anyone to use this software for any
- purpose, excluding commercial applications, and to alter it
- and redistribute it freely, subject to the following
- restrictions:
- 1. The origin of this software must not be misrepresented,
- you must not claim that you wrote the original software.
- If you use this software in a product, an acknowledgment
- in the product documentation would be appreciated but is
- not required.
- 2. Altered source versions must be plainly marked as such, and
- must not be misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source
- distribution.
- Updates:
- ---------------------------------------------------------------------------*/
- //---------------------------------------------------------------------------
- #include <vclvcl.h>
- #pragma hdrstop
- #include <LMACCESS.H>
- #include <LMERR.H>
- #include <LMAPIBUF.H>
- #include <process.h>
- #include <stdio.h>
- #include "VTTelnetSession.h"
- //---------------------------------------------------------------------------
- __fastcall VTSession::VTSession(TVTDaemon *daemon)
- {
- SocketDataIsHere = false;
- SocketSessionIsOpen = false;
- LoginSuccessful = false;
- userOK = false;
- passOK = false;
- EscSeq = 0;
- Leko = true;
- FlushSocket = true;
- numtries = 0;
- HomeDirectory = "C:\";
- TheDaemon = daemon;
- TWSocket *ServerSocket = TheDaemon->SrvSocket;
- SOCKET NewHSocket;
- // sockaddr_in PeerName;
- Socket = new TWSocket(0);
- Socket->Proto = "tcp";
- Socket->SendFlags = wsSendNormal;
- Socket->FlushTimeout = 60;
- Socket->OnDataAvailable = SocketDataAvailable;
- Socket->OnSessionClosed = SocketSessionClosed;
- NewHSocket = ServerSocket->Accept();
- Socket->Dup(NewHSocket);
- // Socket->GetPeerName(PeerName, sizeof(PeerName));
- /* Connected = IntToStr(PeerName.sin_addr.S_un_b.s_b1) + '->' +
- IntToStr(PeerName.sin_addr.S_un_b.s_b2) + '->' +
- IntToStr(PeerName.sin_addr.S_un_b.s_b3) + '->' +
- IntToStr(PeerName.sin_addr.S_un_b.s_b4);
- */
- SocketSessionIsOpen = true;
- FlushSocket = false;
- ChildProcessCreated = false;
- if (TheDaemon->GetNumSessions() > MAXSESSIONS){
- ErrorExit("Maximum number of connections");
- return;
- }
- //get any data the connection sent, i.e. flush socket
- //terminal type info is not used.
- char datagram[2048];
- char toSend[1048];
- int FromLen;
- Socket->ReceiveFrom(datagram, 2048, ConnectedAddr, FromLen);
- //validate username and password
- LoginSuccessful = false;
- gethostname(hostname, 256);
- char greeting[300];
- sprintf(greeting, "rnrnConnected to %s.rnrn", hostname);
- Socket->SendStr(greeting);
- strcpy(datagram, TheDaemon->WelcomeMessage);
- char *line = strtok(datagram, "n");
- while (line != NULL){
- strcpy(toSend, line);
- strcat(toSend, "rn");
- Socket->SendStr(toSend);
- line = strtok(NULL, "n");
- };
- Socket->SendStr("rnrnrnlogin:");
- }
- //---------------------------------------------------------------------------
- void __fastcall VTSession::Validate()
- {
- USER_INFO_1 *uinf;
- wchar_t username[256];
- if (numtries > 4){
- ErrorExit("rnrn Too many attempts. Closing.....rn");
- return;
- }
- numtries++;
- #ifdef SERVICE
- // validate username and password
- LoginSuccessful =
- LogonUser(
- UserName.c_str(), // string that specifies the user name
- NULL, // string that specifies the domain or server, NULL = this machine
- PassWord.c_str(), // string that specifies the password
- LOGON32_LOGON_SERVICE, // specifies the type of logon operation
- LOGON32_PROVIDER_DEFAULT, // specifies the logon provider
- &UserToken // pointer to variable to receive token handle
- );
- if (!LoginSuccessful){
- char greeting[1024];
- sprintf(greeting, "rnTry again.rnrnlogin:", hostname);
- Socket->SendStr(greeting);
- userOK = false;
- passOK = false;
- return;
- }
-
- //get home directory (how ridiculous is this just to get the home dir?)
- NET_API_STATUS ok = NetApiBufferAllocate(
- sizeof(USER_INFO_1),
- &((void *)uinf)
- );
- if (ok==0){
- ok = NetUserGetInfo(NULL, UserName.WideChar(username, 256),
- 1, &((unsigned char *)uinf));
- if (ok==0){
- if (uinf->usri1_home_dir[0] != 0)
- HomeDirectory = uinf->usri1_home_dir;
- }
- NetApiBufferFree(&((void *)uinf));
- }
- #else
- LoginSuccessful = true; //if not service, let them in anyway
- #endif
- Socket->SendStr("rnrn////////////////////////////////////////////"
- "rnYou are connected to VTrex Telnet Daemon on WindowsNT"
- "rnCopyright (c) 1998 by E. Brady Trexlerrn"
- "rnThis is a free daemon that you can download"
- "rnby anonymous ftp from magicbus.aecom.yu.edurnrn"
- "rnrndaemon specific commands are:rn"
- "rn ? -- This Help Screen"
- "rn logout -- end session"
- "rn exit -- end session"
- "rn leko -- turn echo offrn"
- "rn "start" is not allowedrn"
- "////////////////////////////////////////////rnrn");
- StartCommandInterpreterAsChild();
- SECURITY_ATTRIBUTES sa =
- {
- sizeof(SECURITY_ATTRIBUTES), // structure size
- 0, // No security descriptor
- TRUE, // Thread handle is inheritable
- };
- DWORD threadId;
- // Read from pipe that is the standard output for child process.
- _beginthreadNT(FeedSTDOUTDataToSocket,// Thread starting address
- 8192, // Thread stack size
- (void *)this, // Thread start argument
- &sa, // Thread security
- 0, // Create in running state
- &threadId); // Thread ID.
- // Read from pipe that is the standard error for child process.
- _beginthreadNT(FeedSTDERRDataToSocket,// Thread starting address
- 8192, // Thread stack size
- (void *)this, // Thread start argument
- &sa, // Thread security
- 0, // Create in running state
- &threadId); // Thread ID.
- // Write to pipe that is the standard input for a child process.
- _beginthreadNT(FeedSocketDataToSTDIN, // Thread starting address
- 8192, // Thread stack size
- (void *)this, // Thread start argument
- &sa, // Thread security
- 0, // Create in running state
- &threadId); // Thread ID.
- numtries = 0;
- }
- //---------------------------------------------------------------------------
- // Process each character received to do minimal line editing
- //---------------------------------------------------------------------------
- void __fastcall VTSession::ProcessChar(char Ch)
- {
- if (Ch == 'b') { //backspace
- if (FCommand.Length() > 0) {
- FCommand.SetLength(FCommand.Length() - 1);
- Socket->SendStr("b b");
- }
- else
- Socket->SendStr('a'); //beep
- return;
- }
- else if (Ch == 'x03'){ // CTRL-C
- GenerateConsoleCtrlEvent(
- CTRL_BREAK_EVENT, // signal to generate
- piProcInfo.dwProcessId); // process group to get signal
- return;
- }
- else if (Ch == 'x1B') {//escape sequence begin
- EscSeq = 1;
- return;
- }
- else if (Ch == ' ') { //escape sequence begin
- EscSeq = 1;
- return;
- }
- else if (Ch == 'O' && EscSeq == 1) {
- EscSeq = 2;
- return;
- }
- else if (Ch == '[' && EscSeq == 1) {
- EscSeq = 2;
- return;
- }
- else if (EscSeq == 2){
- EscSeq = 0; //eat it
- return;
- }
- else if (Ch == 'r') {
- if (!LoginSuccessful){
- if (!userOK){
- UserName = FCommand;
- FCommand = "";
- Socket->SendStr("rnpassword:");
- userOK = true;
- return;
- }
- else if (userOK && !passOK){
- if (Ch != 'n'){
- PassWord = FCommand;
- FCommand = "";
- Socket->SendStr("rn");
- passOK = true;
- Validate();
- }
- return;
- }
- }
- else{
- FCommand = FCommand + Ch;
- FCommand = FCommand + 'n';
- // FCommand = FCommand + "|morern";
- Socket->SendStr("rn");
- CommandInterpreter();
- return;
- }
- }
- else if (Ch == 'n') {
- // eat it
- return;
- }
- // Ordinary character, add it to buffer
- FCommand = FCommand + Ch;
- if (!LoginSuccessful && userOK && !passOK){
- Socket->SendStr("*");
- return;
- }
- // Echo to client
- if (Leko) Socket->Send(&Ch, 1);
- }
- //---------------------------------------------------------------------------
- // This is the command line interpreter.
- //---------------------------------------------------------------------------
- void __fastcall VTSession::CommandInterpreter()
- {
- AnsiString TheCommand = FCommand.UpperCase();
- // Process Command
- if (TheCommand == "LOGOUTrn"){
- FCommand = "exitrn";
- SocketDataIsHere = true;
- return;
- }
- else if (TheCommand == "?rn"){
- Socket->SendStr("rnrnTelnet specific commands are:rn"
- "rn ? -- This Help Screen"
- "rn logout -- end session"
- "rn exit -- end session"
- "rn leko -- turn echo onoffrn"
- "rn "start" is not allowedrnrn");
- FCommand = "rn";
- SocketDataIsHere = true;
- return;
- }
- else if (TheCommand == "LEKOrn"){
- Leko = !Leko;
- switch (Leko) {
- case true : Socket->SendStr("Character echo is on"); break;
- case false: Socket->SendStr("Character echo is off"); break;
- }
- FCommand = "rn";
- SocketDataIsHere = true;
- return;
- }
- TheCommand.SetLength(5);
- if (TheCommand == "START"){
- Socket->SendStr("rnrnThat command is not allowedrn");
- FCommand = "rn";
- SocketDataIsHere = true;
- return;
- }
- SocketDataIsHere = true;
- }
- //---------------------------------------------------------------------------
- // Event handler for datavailable. Called each time some data is received
- //---------------------------------------------------------------------------
- void __fastcall VTSession::SocketDataAvailable(TObject *Sender, WORD Error)
- {
- TWSocket *Socket;
- char charbuf[256];
- int I, Len;
- Socket = (TWSocket *)Sender;
- Len = Socket->Receive(charbuf, sizeof(charbuf));
- if (FlushSocket){
- Socket->SendStr("Flush");
- return;
- }
- if (Len == 0){
- // Remote has closed
- SocketDataIsHere = false;
- ErrorExit(0);
- }
- else if (Len < 0) {
- // An error has occured
- if (Socket->LastError != WSAEWOULDBLOCK) {
- //handle error
- SocketDataIsHere = false;
- }
- }
- else {
- charbuf[Len] = 0;
- for (I = 0; charbuf[I]; I++)
- ProcessChar(charbuf[I]);
- }
- }
- //---------------------------------------------------------------------------
- void __fastcall VTSession::SocketSessionClosed(TObject *Sender, WORD Error)
- {
- //tell daemon that this connection is closed
- if (!SocketSessionIsOpen) return;
- SocketSessionIsOpen = false;
- PostMessage(TheDaemon->Handle, WM_DISCONNECT, 0, (LPARAM)this);
- }
- //---------------------------------------------------------------------------
- void __fastcall VTSession::ErrorExit (LPTSTR lpszMessage)
- {
- //tell daemon that this connection needs to be closed
- if (!SocketSessionIsOpen) return;
- if (ChildProcessCreated){
- CloseHandle(piProcInfo.hProcess);
- CloseHandle(piProcInfo.hThread);
- }
- char toSend[1024];
- char *line = strtok(lpszMessage, "n");
- while (line != NULL){
- strcpy(toSend, line);
- strcat(toSend, "rn");
- Socket->SendStr(toSend);
- line = strtok(NULL, "n");
- };
- // Socket->SendStr(lpszMessage);
- SocketSessionIsOpen = false;
- PostMessage(TheDaemon->Handle, WM_DISCONNECT, 0, (LPARAM)this);
- }
- //---------------------------------------------------------------------------
- bool __fastcall VTSession::CreateChildProcess()
- {
- if (!LoginSuccessful) return false;
- // Setup members of STARTUPINFO structure.
- siStartInfo.cb = sizeof(STARTUPINFO);
- siStartInfo.lpReserved = NULL;
- siStartInfo.lpReserved2 = NULL;
- siStartInfo.cbReserved2 = 0;
- siStartInfo.lpDesktop = NULL;
- // Create the child process.
- #ifdef SERVICE
- siStartInfo.dwFlags = 0;
- siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
- siStartInfo.hStdInput = hChildStdinRd;
- siStartInfo.hStdOutput = hChildStdoutWr;
- siStartInfo.hStdError = hChildStderrWr;
- siStartInfo.wShowWindow = SW_HIDE;
- return CreateProcessAsUser(
- UserToken, // handle to a token that represents a logged-on user
- NULL, // pointer to name of executable module
- "cmd /q", // pointer to command line string
- NULL, // pointer to process security attributes
- NULL, // pointer to thread security attributes
- TRUE, // new process inherits handles
- CREATE_NEW_PROCESS_GROUP|CREATE_NEW_CONSOLE, // creation flags
- NULL, // pointer to new environment block, NULL = use parent's
- HomeDirectory.c_str(), // pointer to current directory name
- &siStartInfo, // STARTUPINFO pointer
- &piProcInfo); // receives PROCESS_INFORMATION
- #else
- return CreateProcess(
- NULL, // executable module
- "cmd /q",//"command.com", // command line for Win95/98
- NULL, // process security attributes
- NULL, // primary thread security attributes
- TRUE, // handles are inherited
- 0,//CREATE_NEW_PROCESS_GROUP,//|CREATE_NEW_CONSOLE, // creation flags
- NULL, // use parent's environment
- NULL,//HomeDirectory.c_str(), // current directory
- &siStartInfo, // STARTUPINFO pointer
- &piProcInfo); // receives PROCESS_INFORMATION
- #endif
- }
- //---------------------------------------------------------------------------
- void __fastcall VTSession::StartCommandInterpreterAsChild()
- {
- SECURITY_ATTRIBUTES saAttr;
- bool fSuccess;
- // Set the bInheritHandle flag so pipe handles are inherited.
- saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
- saAttr.bInheritHandle = true;
- saAttr.lpSecurityDescriptor = NULL;
- // The steps for redirecting child's STDOUT:
- // 1. Save current STDOUT, to be restored later.
- // 2. Create anonymous pipe to be STDOUT for child.
- // 3. Set STDOUT of parent to be write handle of pipe,
- // so it is inherited by child.
- // Save the handle to the current STDOUT.
- hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);
- // Create a pipe for the child's STDOUT.
- if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
- ErrorExit("Stdout pipe creation failedn");
- // Set a write handle to the pipe to be STDOUT.
- if (! SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr))
- ErrorExit("Redirecting STDOUT failed");
- /////////////////////////////////////////////////////////////////
- //added by ebt///////////////////////////////////////////////////
- // The steps for redirecting child's STDERR:
- // 1. Save current STDERR, to be restored later.
- // 2. Create anonymous pipe to be STDERR for child.
- // 3. Set STDERR of parent to be write handle of pipe,
- // so it is inherited by child.
- // Save the handle to the current STDERR.
- hSaveStderr = GetStdHandle(STD_ERROR_HANDLE);
- // Create a pipe for the child's STDERR.
- if (! CreatePipe(&hChildStderrRd, &hChildStderrWr, &saAttr, 0)){
- ErrorExit("Stderr pipe creation failedn");
- return;
- }
- // Set a write handle to the pipe to be STDERR.
- if (! SetStdHandle(STD_ERROR_HANDLE, hChildStderrWr)){
- ErrorExit("Redirecting STDERR failed");
- return;
- }
- ///tbe////////////////////////////////////////////////////////////
- //////////////////////////////////////////////////////////////////
- // The steps for redirecting child's STDIN:
- // 1. Save current STDIN, to be restored later.
- // 2. Create anonymous pipe to be STDIN for child.
- // 3. Set STDIN of parent to be read handle of pipe, so
- // it is inherited by child.
- // 4. Create a noninheritable duplicate of write handle,
- // and close the inheritable write handle.
- // Save the handle to the current STDIN.
- hSaveStdin = GetStdHandle(STD_INPUT_HANDLE);
- // Create a pipe for the child's STDIN.
- if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)){
- ErrorExit("Stdin pipe creation failedn");
- return;
- }
- // Set a read handle to the pipe to be STDIN.
- if (! SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd)){
- ErrorExit("Redirecting Stdin failed");
- return;
- }
- // Duplicate the write handle to the pipe so it is not inherited.
- fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
- GetCurrentProcess(), &hChildStdinWrDup, 0,
- FALSE, // not inherited
- DUPLICATE_SAME_ACCESS);
- if (! fSuccess){
- ErrorExit("DuplicateHandle failed");
- return;
- }
- CloseHandle(hChildStdinWr);
- // Now create the child process.
- ChildProcessCreated = CreateChildProcess();
- if (!ChildProcessCreated){
- char buf1[1024], buf2[2048];
- DWORD Error = GetLastError();
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, Error, 0, buf1, 1024, NULL);
- sprintf(buf2, "Create process failedrn Error # %d %s", Error, buf1);
- ErrorExit(buf2);
- return;
- }
- // After process creation, restore the saved STDIN and STDOUT( and STDERR [ebt]).
- if (! SetStdHandle(STD_INPUT_HANDLE, hSaveStdin)){
- ErrorExit("Re-redirecting Stdin failedn");
- return;
- }
- if (! SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout)){
- ErrorExit("Re-redirecting Stdout failedn");
- return;
- }
- if (! SetStdHandle(STD_ERROR_HANDLE, hSaveStderr)){
- ErrorExit("Re-redirecting Stderr failedn");
- return;
- }
- // Get a handle to the parent's input file.
- hInputFile = hSaveStdin;
- if (hInputFile == INVALID_HANDLE_VALUE){
- ErrorExit("no input filen");
- return;
- }
- }
- //---------------------------------------------------------------------------
- bool __fastcall VTSession::WaitForSocketData(char *data, DWORD &numchar)
- {
- while (SocketSessionIsOpen){
- if (SocketDataIsHere) {
- strcpy(data,FCommand.c_str());
- numchar = strlen(data);
- SocketDataIsHere = false;
- FCommand = "";
- return true;
- }
- Sleep(100);
- }
- data[0] = 0;
- numchar = 0;
- return false;
- }
- //---------------------------------------------------------------------------
- void FeedSocketDataToSTDIN(void *session)
- {
- DWORD dwRead, dwWritten;
- CHAR chBuf[BUFSIZE];
- VTSession *theSession = (VTSession *)session;
- if (!theSession->SocketSessionIsOpen) return;
- // Get data from the socket and write its contents to the STDIN pipe.
- for (;;)
- {
- if (! theSession->WaitForSocketData(chBuf, dwRead) || dwRead == 0) break;
- if (! WriteFile(theSession->hChildStdinWrDup, chBuf, dwRead, &dwWritten, NULL)) break;
- }
- // Close the pipe handle so the child stops reading.
- if (! CloseHandle(theSession->hChildStdinWrDup)) theSession->ErrorExit("Close pipe failedn");
- }
- //---------------------------------------------------------------------------
- void FeedSTDOUTDataToSocket(void *session)
- {
- DWORD dwRead;
- CHAR chBuf[BUFSIZE];
- // char toSend[BUFSIZE];
- VTSession *theSession = (VTSession *)session;
- if (!theSession->SocketSessionIsOpen) return;
- // Close the write end of the pipe before reading from the
- // read end of the pipe.
- if (!CloseHandle(theSession->hChildStdoutWr)) theSession->ErrorExit("Closing handle failed");
- // Read output from child, and write it to the socket.
- for (;;)
- {
- if (! ReadFile(theSession->hChildStdoutRd, chBuf, BUFSIZE, &dwRead, NULL) ||
- dwRead == 0) break;
- //Put a