Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygentelnetd.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
1.7.6.1
|