diff --git a/reactos/base/services/telnetd/serviceentry.c b/reactos/base/services/telnetd/serviceentry.c new file mode 100644 index 00000000000..015d057d00d --- /dev/null +++ b/reactos/base/services/telnetd/serviceentry.c @@ -0,0 +1,111 @@ +/* + * Copyright 2007 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ +#if 0 +#define WIN32_LEAN_AND_MEAN + +#include + +#define WINE_FIXME printf +#define WINE_TRACE printf + +//#include "wine/debug.h" + +//WINE_DEFAULT_DEBUG_CHANNEL(spoolsv); + +static WCHAR telnetdW[] = {'T','e','l','n','e','t','D',0}; + +static SERVICE_STATUS_HANDLE service_handle; +static HANDLE stop_event; + +static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_data, LPVOID context ) +{ + SERVICE_STATUS status; + + status.dwServiceType = SERVICE_WIN32; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP; + status.dwWin32ExitCode = 0; + status.dwServiceSpecificExitCode = 0; + status.dwCheckPoint = 0; + status.dwWaitHint = 0; + + switch(ctrl) + { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + WINE_TRACE( "shutting down\n" ); + status.dwCurrentState = SERVICE_STOP_PENDING; + status.dwControlsAccepted = 0; + SetServiceStatus( service_handle, &status ); + SetEvent( stop_event ); + return NO_ERROR; + default: + WINE_FIXME( "got service ctrl %x\n", ctrl ); + status.dwCurrentState = SERVICE_RUNNING; + SetServiceStatus( service_handle, &status ); + return NO_ERROR; + } +} + +static void WINAPI serv_main(DWORD argc, LPWSTR *argv) +{ + SERVICE_STATUS status; + int retval; + + WINE_TRACE( "starting service\n" ); + + stop_event = CreateEventW( NULL, TRUE, FALSE, NULL ); + + service_handle = RegisterServiceCtrlHandlerExW( telnetdW, service_handler, NULL ); + if (!service_handle) + return; + + status.dwServiceType = SERVICE_WIN32; + status.dwCurrentState = SERVICE_RUNNING; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + status.dwWin32ExitCode = 0; + status.dwServiceSpecificExitCode = 0; + status.dwCheckPoint = 0; + status.dwWaitHint = 10000; + SetServiceStatus( service_handle, &status ); + + /* Argument Ignored for now */ + retval = kickoff_telnetd(); + + WaitForSingleObject( stop_event, INFINITE ); + + status.dwCurrentState = SERVICE_STOPPED; + status.dwControlsAccepted = 0; + SetServiceStatus( service_handle, &status ); + WINE_TRACE( "service stopped\n" ); +} + +int main(int argc, char **argv) +{ + static const SERVICE_TABLE_ENTRYW servtbl[] = { + {telnetdW, serv_main}, + {NULL, NULL} + }; + + WINE_TRACE("(%d %p)\n", argc, argv); + + StartServiceCtrlDispatcherW(servtbl); + return 0; +} +#endif +/* EOF */ + diff --git a/reactos/base/services/telnetd/syslog.c b/reactos/base/services/telnetd/syslog.c new file mode 100644 index 00000000000..1ee4499a921 --- /dev/null +++ b/reactos/base/services/telnetd/syslog.c @@ -0,0 +1,308 @@ +/* + * syslog-client.c - syslog client implementation for windows + * + * Created by Alexander Yaworsky + * + * THIS SOFTWARE IS NOT COPYRIGHTED + * + * This source code is offered for use in the public domain. You may + * use, modify or distribute it freely. + * + * This code is distributed in the hope that it will be useful but + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY + * DISCLAIMED. This includes but is not limited to warranties of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +/* define SYSLOG_CONF_DIR where syslog.host should be + */ + +#ifndef SYSLOG_CONF_DIR +static const char *syslog_conf_dir = "."; +#else +static const char *syslog_conf_dir = SYSLOG_CONF_DIR; +#endif + +#include +#include +#include +#include +#include "syslog.h" + +#ifdef TEST +# define SYSLOG_DGRAM_SIZE 80 +#else +# define SYSLOG_DGRAM_SIZE 1024 +#endif + +static BOOL initialized = FALSE; +static int log_mask = 0xFF; +static char *syslog_ident; +static int syslog_facility; +static char str_pid[ 40 ]; +static SOCKADDR_IN sa_logger; +static SOCKET sock; +static char local_hostname[ MAX_COMPUTERNAME_LENGTH + 1 ]; +static char datagramm[ SYSLOG_DGRAM_SIZE ]; +static int datagramm_size; + +/****************************************************************************** + * set_syslog_conf_dir + * + * maybe this function will be useful... + */ +const char* set_syslog_conf_dir( const char* dir ) +{ + const char *ret = syslog_conf_dir; + syslog_conf_dir = dir; + return ret; +} + +/****************************************************************************** + * init_logger_addr + * + * Read configuration file syslog.host. This file should contain host address + * and, optionally, port. Initialize sa_logger. If the configuration file does + * not exist, use localhost:514. + * Returns: 0 - ok, -1 - error. + */ +static void init_logger_addr() +{ + char pathname[ FILENAME_MAX ]; + char *p; + FILE *fd; + char host[256]; + struct hostent * phe; + + memset( &sa_logger, 0, sizeof(SOCKADDR_IN) ); + sa_logger.sin_family = AF_INET; + + if( '\\' == syslog_conf_dir[0] || '/' == syslog_conf_dir[0] || ':' == syslog_conf_dir[1] ) + { + /* absolute path */ + strcpy( pathname, syslog_conf_dir ); + } + else + { + /* relative path */ + char *q; + + strcpy( pathname, __argv[0] ); + p = strrchr( pathname, '\\' ) + 1; + q = strrchr( pathname, '/' ) + 1; + if( p < q ) + *q = 0; + else if( p > q ) + *p = 0; + else + pathname[0] = 0; + strcat( pathname, syslog_conf_dir ); + } + p = &pathname[ strlen( pathname ) - 1 ]; + if( '\\' != *p && '/' != *p ) + { + p++; *p = '/'; + } + strcpy( ++p, "syslog.host" ); + + /* read destination host name */ + fd = fopen( pathname, "r" ); + if( !fd ) + goto use_default; + + if( NULL == fgets( host, sizeof(host), fd ) ) + host[0] = 0; + else + { + p = strchr( host, '\n' ); + if( p ) + *p = 0; + p = strchr( host, '\r' ); + if( p ) + *p = 0; + } + fclose( fd ); + + p = strchr( host, ':' ); + if( p ) + *p++ = 0; + + phe = gethostbyname( host ); + if( !phe ) + goto use_default; + + memcpy( &sa_logger.sin_addr.s_addr, phe->h_addr, phe->h_length ); + + if( p ) + sa_logger.sin_port = htons( (unsigned short) strtoul( p, NULL, 0 ) ); + else + sa_logger.sin_port = htons( SYSLOG_PORT ); + return; + +use_default: + sa_logger.sin_addr.S_un.S_addr = htonl( 0x7F000001 ); + sa_logger.sin_port = htons( SYSLOG_PORT ); +} + +/****************************************************************************** + * closelog + * + * Close desriptor used to write to system logger. + */ +void closelog() +{ + if( !initialized ) + return; + closesocket( sock ); + WSACleanup(); + initialized = FALSE; +} + +/****************************************************************************** + * openlog + * + * Open connection to system logger. + */ +void openlog( char* ident, int option, int facility ) +{ + BOOL failed = TRUE, wsa_initialized = FALSE; + WSADATA wsd; + SOCKADDR_IN sa_local; + DWORD n; + int size; + + if( initialized ) + return; + + syslog_facility = facility? facility : LOG_USER; + + /* FIXME: should we reset logmask? */ + + if( option & LOG_PID ) + snprintf( str_pid, sizeof(str_pid), "[%lu]", GetCurrentProcessId() ); + else + str_pid[0] = 0; + + /* FIXME: handle other options */ + + n = sizeof(local_hostname); + if( !GetComputerName( local_hostname, &n ) ) + goto done; + + sock = INVALID_SOCKET; + if( WSAStartup( MAKEWORD( 2, 2 ), &wsd ) ) + goto done; + wsa_initialized = TRUE; + + init_logger_addr(); + + for( n = 0;; n++ ) + { + sock = socket( AF_INET, SOCK_DGRAM, 0 ); + if( INVALID_SOCKET == sock ) + goto done; + + memset( &sa_local, 0, sizeof(SOCKADDR_IN) ); + sa_local.sin_family = AF_INET; + if( bind( sock, (SOCKADDR*) &sa_local, sizeof(SOCKADDR_IN) ) == 0 ) + break; + closesocket( sock ); + sock = INVALID_SOCKET; + if( n == 100 ) + goto done; + Sleep(0); + } + + /* get size of datagramm */ + size = sizeof(datagramm_size); + if( getsockopt( sock, SOL_SOCKET, SO_MAX_MSG_SIZE, (char*) &datagramm_size, &size ) ) + goto done; + if( datagramm_size - strlen(local_hostname) - (ident? strlen(ident) : 0) < 64 ) + goto done; + if( datagramm_size > sizeof(datagramm) ) + datagramm_size = sizeof(datagramm); + + if( atexit( closelog ) ) + goto done; + + syslog_ident = ident; + syslog_facility = facility; + failed = FALSE; + +done: + if( failed ) + { + if( sock != INVALID_SOCKET ) closesocket( sock ); + if( wsa_initialized ) WSACleanup(); + } + initialized = !failed; +} + +/****************************************************************************** + * setlogmask + * + * Set the log mask level. + */ +int setlogmask( int mask ) +{ + int ret = log_mask; + + if( mask ) + log_mask = mask; + return ret; +} + +/****************************************************************************** + * syslog + * + * Generate a log message using FMT string and option arguments. + */ +void syslog( int pri, char* fmt, ... ) +{ + va_list ap; + + va_start( ap, fmt ); + vsyslog( pri, fmt, ap ); + va_end( ap ); +} + +/****************************************************************************** + * vsyslog + * + * Generate a log message using FMT and using arguments pointed to by AP. + */ +void vsyslog( int pri, char* fmt, va_list ap ) +{ + static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + SYSTEMTIME stm; + int len; + char *p; + + if( !(LOG_MASK( LOG_PRI( pri )) & log_mask) ) + return; + + openlog( NULL, 0, pri & LOG_FACMASK ); + if( !initialized ) + return; + + if( !(pri & LOG_FACMASK) ) + pri |= syslog_facility; + + GetLocalTime( &stm ); + len = sprintf( datagramm, "<%d>%s %2d %02d:%02d:%02d %s %s%s: ", + pri, + month[ stm.wMonth - 1 ], stm.wDay, stm.wHour, stm.wMinute, stm.wSecond, + local_hostname, syslog_ident? syslog_ident : "", str_pid ); + vsnprintf( datagramm + len, datagramm_size - len, fmt, ap ); + p = strchr( datagramm, '\n' ); + if( p ) + *p = 0; + p = strchr( datagramm, '\r' ); + if( p ) + *p = 0; + + sendto( sock, datagramm, strlen(datagramm), 0, (SOCKADDR*) &sa_logger, sizeof(SOCKADDR_IN) ); +} + diff --git a/reactos/base/services/telnetd/syslog.h b/reactos/base/services/telnetd/syslog.h new file mode 100644 index 00000000000..2dbf821961a --- /dev/null +++ b/reactos/base/services/telnetd/syslog.h @@ -0,0 +1,197 @@ +/* + * Copyright (c) 1982, 1986, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)syslog.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _SYS_SYSLOG_H +#define _SYS_SYSLOG_H 1 + +#include + +/* + * priorities/facilities are encoded into a single 32-bit quantity, where the + * bottom 3 bits are the priority (0-7) and the top 28 bits are the facility + * (0-big number). Both the priorities and the facilities map roughly + * one-to-one to strings in the syslogd(8) source code. This mapping is + * included in this file. + * + * priorities (these are ordered) + */ +#define LOG_EMERG 0 /* system is unusable */ +#define LOG_ALERT 1 /* action must be taken immediately */ +#define LOG_CRIT 2 /* critical conditions */ +#define LOG_ERR 3 /* error conditions */ +#define LOG_WARNING 4 /* warning conditions */ +#define LOG_NOTICE 5 /* normal but significant condition */ +#define LOG_INFO 6 /* informational */ +#define LOG_DEBUG 7 /* debug-level messages */ + +#define LOG_PRIMASK 0x07 /* mask to extract priority part (internal) */ + /* extract priority */ +#define LOG_PRI(p) ((p) & LOG_PRIMASK) +#define LOG_MAKEPRI(fac, pri) (((fac) << 3) | (pri)) + +#ifdef SYSLOG_NAMES +#define INTERNAL_NOPRI 0x10 /* the "no priority" priority */ + /* mark "facility" */ +#define INTERNAL_MARK LOG_MAKEPRI(LOG_NFACILITIES, 0) +typedef struct _code { + char *c_name; + int c_val; +} CODE; + +CODE prioritynames[] = + { + { "alert", LOG_ALERT }, + { "crit", LOG_CRIT }, + { "debug", LOG_DEBUG }, + { "emerg", LOG_EMERG }, + { "err", LOG_ERR }, + { "error", LOG_ERR }, /* DEPRECATED */ + { "info", LOG_INFO }, + { "none", INTERNAL_NOPRI }, /* INTERNAL */ + { "notice", LOG_NOTICE }, + { "panic", LOG_EMERG }, /* DEPRECATED */ + { "warn", LOG_WARNING }, /* DEPRECATED */ + { "warning", LOG_WARNING }, + { NULL, -1 } + }; +#endif + +/* facility codes */ +#define LOG_KERN (0<<3) /* kernel messages */ +#define LOG_USER (1<<3) /* random user-level messages */ +#define LOG_MAIL (2<<3) /* mail system */ +#define LOG_DAEMON (3<<3) /* system daemons */ +#define LOG_AUTH (4<<3) /* security/authorization messages */ +#define LOG_SYSLOG (5<<3) /* messages generated internally by syslogd */ +#define LOG_LPR (6<<3) /* line printer subsystem */ +#define LOG_NEWS (7<<3) /* network news subsystem */ +#define LOG_UUCP (8<<3) /* UUCP subsystem */ +#define LOG_CRON (9<<3) /* clock daemon */ +#define LOG_AUTHPRIV (10<<3) /* security/authorization messages (private) */ +#define LOG_FTP (11<<3) /* ftp daemon */ + + /* other codes through 15 reserved for system use */ +#define LOG_LOCAL0 (16<<3) /* reserved for local use */ +#define LOG_LOCAL1 (17<<3) /* reserved for local use */ +#define LOG_LOCAL2 (18<<3) /* reserved for local use */ +#define LOG_LOCAL3 (19<<3) /* reserved for local use */ +#define LOG_LOCAL4 (20<<3) /* reserved for local use */ +#define LOG_LOCAL5 (21<<3) /* reserved for local use */ +#define LOG_LOCAL6 (22<<3) /* reserved for local use */ +#define LOG_LOCAL7 (23<<3) /* reserved for local use */ + +#define LOG_NFACILITIES 24 /* current number of facilities */ +#define LOG_FACMASK 0x03f8 /* mask to extract facility part */ + /* facility of pri */ +#define LOG_FAC(p) (((p) & LOG_FACMASK) >> 3) + +#ifdef SYSLOG_NAMES +CODE facilitynames[] = + { + { "auth", LOG_AUTH }, + { "authpriv", LOG_AUTHPRIV }, + { "cron", LOG_CRON }, + { "daemon", LOG_DAEMON }, + { "ftp", LOG_FTP }, + { "kern", LOG_KERN }, + { "lpr", LOG_LPR }, + { "mail", LOG_MAIL }, + { "mark", INTERNAL_MARK }, /* INTERNAL */ + { "news", LOG_NEWS }, + { "security", LOG_AUTH }, /* DEPRECATED */ + { "syslog", LOG_SYSLOG }, + { "user", LOG_USER }, + { "uucp", LOG_UUCP }, + { "local0", LOG_LOCAL0 }, + { "local1", LOG_LOCAL1 }, + { "local2", LOG_LOCAL2 }, + { "local3", LOG_LOCAL3 }, + { "local4", LOG_LOCAL4 }, + { "local5", LOG_LOCAL5 }, + { "local6", LOG_LOCAL6 }, + { "local7", LOG_LOCAL7 }, + { NULL, -1 } + }; +#endif + +/* + * arguments to setlogmask. + */ +#define LOG_MASK(pri) (1 << (pri)) /* mask for one priority */ +#define LOG_UPTO(pri) ((1 << ((pri)+1)) - 1) /* all priorities through pri */ + +/* + * Option flags for openlog. + * + * LOG_ODELAY no longer does anything. + * LOG_NDELAY is the inverse of what it used to be. + */ +#define LOG_PID 0x01 /* log the pid with each message */ +#define LOG_CONS 0x02 /* log on the console if errors in sending */ +#define LOG_ODELAY 0x04 /* delay open until first syslog() (default) */ +#define LOG_NDELAY 0x08 /* don't delay open */ +#define LOG_NOWAIT 0x10 /* don't wait for console forks: DEPRECATED */ +#define LOG_PERROR 0x20 /* log to stderr as well */ + +#define SYSLOG_PORT 514 + +#ifdef __cplusplus +extern "C" { +#endif + +/* Close desriptor used to write to system logger. */ +extern void closelog (void); + +/* Open connection to system logger. */ +extern void openlog (char *__ident, int __option, int __facility); + +/* Set the log mask level. */ +extern int setlogmask (int __mask); + +/* Generate a log message using FMT string and option arguments. */ +extern void syslog (int __pri, char *__fmt, ...); + +/* Generate a log message using FMT and using arguments pointed to by AP. */ +extern void vsyslog (int __pri, char *__fmt, va_list __ap); + +/* windows-specific; + set directory from where syslog.host must be read; + this file contains a single line with hostname and port of syslog daemon; + default is localhost:514 +*/ +extern const char* set_syslog_conf_dir( const char* dir ); + + +#ifdef __cplusplus +} +#endif + +#endif /* syslog.h */ diff --git a/reactos/base/services/telnetd/telnetd.c b/reactos/base/services/telnetd/telnetd.c new file mode 100644 index 00000000000..6dbd20ddc2d --- /dev/null +++ b/reactos/base/services/telnetd/telnetd.c @@ -0,0 +1,636 @@ +/* + * Abstract: a simple telnet 'daemon' for Windows hosts. + * + * Compiled & run successfully using MSVC 5.0 under Windows95 (requires + * Winsock2 update) and Windows98 and MSVC 6.0 under WindowsNT4 + * + * Compiler options : no special options needed + * Linker options : add wsock32.lib or ws2_32.lib + * + * Written by fred.van.lieshout 'at' zonnet.nl + * Use freely, no copyrights. + * Use Linux. + * + * Parts Copyright Steven Edwards + * Public Domain + * + * TODO: + * - access control + * - will/won't handshake + * - Unify Debugging output and return StatusCodes + */ + +#include "telnetd.h" + +#define telnetd_printf printf +#if 0 +static inline int telnetd_printf(const char *format, ...); +{ + printf(format,...); + syslog (6, format); +} +#endif + +/* Local data */ + +static BOOLEAN bShutdown = 0; +static BOOLEAN bSocketInterfaceInitialised = 0; +static int sock; + +/* 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()) + 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) +{ + DWORD threadID; + client_t *client = malloc(sizeof(client_t)); + + if (client == NULL) + ErrorExit("failed to allocate memory for client"); + + client->socket = client_socket; + CreateThread(NULL, 0, UserLoginThread, client, 0, &threadID); +} + +/* 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 ); + +#if 0 + if (received < 0) { + closesocket(client->socket); + free(client); + return 0; + } else if (received) { + if ((terminator = strchr(password, CR)) != NULL) { + *terminator = '\0'; + } + } +#endif + + /* TODO: do authentication here */ + + + 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 */ +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; + } + + /* Now wait for client response (and ignore it) */ + 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; +} + +/* +** Function: ReceiveLine +** +** Abstract: receive until timeout or CR +** In : sock, len +** Out : buffer +** Result : int +** Pre : 'sock' must be valid socket +** Post : (result = the number of bytes read into 'buffer') +** OR (result = -1 and error) +*/ +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 { + /* When we're in echo mode, we do not need a timeout */ + retval = select(0, &set, NULL, NULL, (echo ? NULL : &timeout) ); + /* check for error */ + if (retval < 0) { + return -1; + /* check for timeout */ + } else if (retval == 0) { + /* return number of characters received so far */ + 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) +{ + DWORD threadID; + 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; + //const char *name = "d:\\cygwin\\bin\\bash.exe"; + //const char *cmd = "d:\\cygwin\\bin\\bash.exe --login -i"; + + 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"); + + CreateThread(NULL, 0, WriteToPipeThread, client, 0, &threadID); + CreateThread(NULL, 0, ReadFromPipeThread, client, 0, &threadID); + CreateThread(NULL, 0, MonitorChildThread, client, 0, &threadID); +} + +/* + * 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, FALSE); + 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; fromsocket, 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); +} + diff --git a/reactos/base/services/telnetd/telnetd.h b/reactos/base/services/telnetd/telnetd.h new file mode 100644 index 00000000000..afb1199ce05 --- /dev/null +++ b/reactos/base/services/telnetd/telnetd.h @@ -0,0 +1,87 @@ +#ifndef __TELNETD_H +#define __TELNETD_H + +#define _CRT_SECURE_NO_WARNINGS + +#define WIN32_NO_STATUS +#include +#include +#include +#include + +/* +** 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 "\xff" +#define DONT "\xfe" +#define WONT "\xfc" +#define WILL "\xfb" +#define DO "\xfd" +#define SB "\xfa" +#define SE "\xf0" +#define ECHO "\x01" +#define SUPPRESS_GO_AHEAD "\x03" +#define TERMINAL_TYPE "\x18" +#define NAWS "\x1f" +#define LINEMODE "\x22" +#define NEWENVIRON "\x27" +#define MODE "\x01" + +#define HANDSHAKE_TIMEOUT (3) + +/* +** types +*/ + +typedef struct client_s +{ + 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; + +/* +** 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); +int kickoff_telnetd(void); + +#endif /* __TELNETD_H */ + diff --git a/reactos/base/services/telnetd/telnetd.rbuild b/reactos/base/services/telnetd/telnetd.rbuild new file mode 100644 index 00000000000..3f6325a3665 --- /dev/null +++ b/reactos/base/services/telnetd/telnetd.rbuild @@ -0,0 +1,13 @@ + + + .. + + ntdll + kernel32 + advapi32 + ws2_32 + + telnetd.c + serviceentry.c + telnetd.rc + diff --git a/reactos/base/services/telnetd/telnetd.rc b/reactos/base/services/telnetd/telnetd.rc new file mode 100644 index 00000000000..53760e9b74b --- /dev/null +++ b/reactos/base/services/telnetd/telnetd.rc @@ -0,0 +1,7 @@ +/* $Id$ */ + +#define REACTOS_STR_FILE_DESCRIPTION "ReactOS Simple Telnet Deamon\0" +#define REACTOS_STR_INTERNAL_NAME "telnetd\0" +#define REACTOS_STR_ORIGINAL_FILENAME "telnetd.exe\0" +#define REACTOS_STR_ORIGINAL_COPYRIGHT "fred.van.lieshout 'at' zonnet.nl\0" +#include diff --git a/reactos/base/services/telnetd/telnetd.vcproj b/reactos/base/services/telnetd/telnetd.vcproj new file mode 100644 index 00000000000..96714954efd --- /dev/null +++ b/reactos/base/services/telnetd/telnetd.vcproj @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/reactos/boot/bootdata/packages/reactos.dff b/reactos/boot/bootdata/packages/reactos.dff index e2b2fc566e9..1961332a173 100644 --- a/reactos/boot/bootdata/packages/reactos.dff +++ b/reactos/boot/bootdata/packages/reactos.dff @@ -670,7 +670,6 @@ modules\rosapps\applications\sysutils\man\man.exe modules\rosapps\applications\sysutils\pedump\pedump.exe 1 optional modules\rosapps\applications\sysutils\regexpl\regexpl.exe 1 optional modules\rosapps\applications\sysutils\tcat\tcat.exe 1 optional -modules\rosapps\applications\sysutils\telnetd\telnetd.exe 1 optional modules\rosapps\applications\sysutils\tlist\tlist.exe 1 optional modules\rosapps\applications\sysutils\screenshot\screenshot.exe 1 optional modules\rosapps\applications\sysutils\utils\binpatch\binpatch.exe 1 optional