Новичок
Профиль
Группа: Участник
Сообщений: 2
Регистрация: 1.3.2010
Репутация: нет Всего: нет
|
Написал пример демона simple-telnetd, он позволяет удаленно запускать некоторое ограниченное подмножество команд и выводить пользователю результат их выполнения. 1) simple-telnetd запускает только разрешенные программы, перечисленные в файле /etc/simple-telnetd.conf (пример конфигурационного файла прилагается). Демон перечитывает конфигурационный файл по сигналу SIGHUP. Примечание: в конфиг заносятся корректные команды с путем и без параметров. Пустые строки в конфиг лучше не заносить. 2) запускаемые пользовтелем команды могут иметь параметры командной строки, интерактивное взаимодействие не поддерживается. 3) обработка спец. символов не производится 4) через параметр командной строки демону может передаваться таймаут выполнения одной команды. Параметр задается в секундах, значение по умолчанию - 1 секунда. 5) работоспособность демона проверена на Linux, Mac, FreeBSD. 6) реализовано журналирование с уровнями важности DEBUG, INFO, и ERR от имени средства daemon. Для запуска: 0) создать конфигурационный файл (пример ниже) в /etc/simple-telnetd.conf 1) компиляция c++ stelnetd.cpp 2) запуск без параметров: ./a.out запуск с указанием таймаута в секундах: ./a.out 2 3) для подключения к демону используется telnet: telnet localhost 6023 4) в сессии telnet можно вводить команды, например: /bin/ls /bin/ls -l / /bin/pwd 5) для перезагрузки конфигурационного файла демону послается сигнал HUP: killall -s SIGHUP a.out 6) для остановки демона послается сигнал TERM или KILL killall -s SIGKILL a.out Пример конфига: Код демона: | Код | //////////////////////////// // Simple telnetd daemon // by d2s1 ///////////////////////////
/* * configuration file: /etc/simple-telnetd.conf * SIGHUP - reload configuration file * default socket: 0.0.0.0:6023 */
#include <stdio.h> #include <syslog.h>
#include <stdarg.h>
#include <signal.h> #include <sys/types.h> #include <sys/param.h> #include <sys/resource.h>
#include <sys/socket.h> #include <netinet/in.h>
#include <fstream> #include <iostream>
#include <errno.h> #include <sys/stat.h> // umask #include <fcntl.h> // open #include <sys/ioctl.h>
#include <string.h> #include <stdlib.h>
using namespace std;
// // return values (error codes) // const int NO_ERROR = 0; const int CONFIG_ERROR = 1; const int SOCKET_ERROR = 2; const int BIND_ERROR = 3; const int ACCEPT_ERROR = 4; const int PIPE_ERROR = 5; const int WRITE_ERROR = 6; const int READ_ERROR = 7; const int SETSID_ERROR = 8; const int CHDIR_ERROR = 9; const int OPEN_ERROR = 10;
// // globals // char valid_command_list[255][255]; // 255 commands int list_length=0; // actual number of commands bool finalize=false; // termination flag int pio[2]; // pipes for std io: 0 - input, 1 - output & error int timeout=1; // command execution timeout (sec) int sock,newsock;
// load conf file static bool load_conf() { char par[255]; // filename maximum length in HFS+ and ext3 ifstream conf("/etc/simple-telnetd.conf"); if (!conf) { syslog(LOG_ERR, "can not access config file: /etc/simple-telnetd.conf"); return false; } int line=0; list_length=0; memset(valid_command_list, 0, 255 * 255); while((!conf.eof()) && (line < 255)) { conf.getline(par, 255); size_t cmd_len = strcspn(par, "\r\n\t "); // space symbols (delimiters) memcpy(valid_command_list[line], par, cmd_len); syslog(LOG_INFO, "loaded command %s", valid_command_list[line]); line++; } list_length=line; syslog(LOG_INFO, "configuration loaded."); return true; }
// sighup handler - reload conf static void sighup_handler(int signo) { signal(SIGHUP, sighup_handler); close(sock); close(newsock); load_conf(); }
// sigterm handler - finalize static void sigterm_handler(int signo) { syslog(LOG_INFO, "terminated by SIGTERM."); finalize=true; }
// server logic static int server(int port) { // constants const int BUF_SIZE = 1024; //locals int clen, readed_data_lenght; char buf[BUF_SIZE]; struct sockaddr_in serv_addr, cli_addr; struct timeval tv; tv.tv_sec=240; tv.tv_usec=0; // socket connect sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { syslog(LOG_ERR, "can not create socket. %d %s", errno, strerror(errno)); return SOCKET_ERROR; } setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); memset((char *) &serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(port); if (bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { syslog(LOG_ERR, "can not bind socket. %d %s", errno, strerror(errno)); close(sock); return BIND_ERROR; } while(!finalize) { listen(sock, 1); clen = sizeof(cli_addr); newsock = accept(sock, (struct sockaddr *) &cli_addr, (socklen_t *) &clen); if (newsock < 0) { syslog(LOG_ERR, "can not accept connection. %d %s", errno, strerror(errno)); close(sock); return ACCEPT_ERROR; } syslog(LOG_INFO, "connection open from %a", &cli_addr.sin_addr); //data read while(!finalize) { memset(buf, 0, BUF_SIZE); readed_data_lenght = read(newsock, buf, BUF_SIZE-1); if(readed_data_lenght < 0) { if(errno == EWOULDBLOCK) { syslog(LOG_ERR, "socket timeout."); } else { syslog(LOG_ERR, "read error. %d %s", errno, strerror(errno)); } } else if (readed_data_lenght > 0) { size_t cmd_len = strcspn(buf, "\r\n\t "); // space symbols (delimiters) char buffer[cmd_len+1]; buffer[cmd_len] = 0; memcpy(buffer, buf, cmd_len); bool cmd_valid = false; for (int cmd=0; cmd < list_length; cmd++) { // syslog(LOG_DEBUG, "compare:%svs%s", buffer, valid_command_list[cmd]); if (strcmp(buffer, valid_command_list[cmd]) == 0) { cmd_valid = true; break; } } if (!cmd_valid) { syslog(LOG_ERR, "invalid cmd: %s", buf); // todo or not todo: send error message to user? } else { syslog(LOG_INFO, "cmd valid: %s", buf); char *arg[10]; // max possible arguments: 0..9 int num = 1; arg[0] = new char(strlen(buffer)); strcpy(arg[0], buffer); char *p = strtok(buf, "\r\n\t "); do { p = strtok(NULL, "\r\n\t "); if(p) { arg[num] = new char(strlen(p)); strcpy(arg[num],p); syslog(LOG_DEBUG, "arg[%d]=%s", num, arg[num]); num++; } } while(p); char *ar[num+1]; // actual number of arguments ar[num] = NULL; for (int i = 0; i < num; i++) { ar[i] = new char(strlen(arg[i])); strcpy(ar[i],arg[i]); syslog(LOG_DEBUG, "ar[%d]=%s", i, ar[i]); } char answer[4096]; memset(answer, 0, 4096); int pid = fork(); if (pid == 0) { // child int execvretcode = 0; if (num == 1) { syslog(LOG_DEBUG, "execv with NULL"); execvretcode = execv(buffer, NULL); } else { syslog(LOG_DEBUG, "execv %s with argv", buffer); for (int ii=0; ii<num; ii++) { syslog(LOG_DEBUG, "ar[%d]=%s", ii, ar[ii]); } execvretcode = execv(buffer, ar); } if (execvretcode < 0) { syslog(LOG_ERR, "execv error. %d %s", errno, strerror(errno)); cerr<<strerror(errno)<<'\n'; } else { // we are another process now } } else { // parent sleep(timeout); int bytes = -1; // cout<<"output\n"; syslog(LOG_DEBUG, "ioctl"); if (ioctl (pio[0], FIONREAD, &bytes) != 0) { syslog(LOG_ERR, "ioctl error. %d %s", errno, strerror(errno)); bytes = -1; } if (bytes > 0) { syslog(LOG_DEBUG, "read"); if (read(pio[0], answer, 4096) < 0) { syslog(LOG_ERR, "read error. %d %s", errno, strerror(errno)); } } syslog(LOG_DEBUG, "kill"); kill(pid, SIGKILL); } syslog(LOG_DEBUG, "send"); if (send(newsock, answer, strlen(answer), 0) != strlen(answer)) { syslog(LOG_ERR, "send error: %d %s", errno, strerror(errno)); } } } else // readed_data_lenght == 0 { syslog(LOG_INFO, "connection closed by remote host."); break; } } } close(newsock); close(sock); return NO_ERROR; }
// entry point int main (int argc, char *argv[]) { int fd; int oldfd[3]; struct rlimit flim; // open syslog using daemon facility openlog("Simple telnetd", LOG_PID | LOG_CONS, LOG_DAEMON); syslog(LOG_INFO, "starting..."); // ignore terminal's signals if (getppid() != 1) { signal(SIGTTOU, SIG_IGN); signal(SIGTTIN, SIG_IGN); signal(SIGTSTP, SIG_IGN); } // handle SIGHUP todo use sigset_t signal(SIGHUP, sighup_handler); signal(SIGTERM, sigterm_handler); // for soft termination // check command line parameters if (argc > 1) { syslog(LOG_DEBUG, "argv[1]=%s", argv[1]); int to = atoi(argv[1]); if (to > 0) { syslog(LOG_INFO, "set timeout %d", to); timeout = to; } } // allocate pipes if (pipe(pio) < 0) { syslog(LOG_ERR, "can not allocate i/o pipes. %d %s", errno, strerror(errno)); return PIPE_ERROR; } // switch std io to pipes oldfd[0]=dup(STDIN_FILENO); oldfd[1]=dup(STDOUT_FILENO); oldfd[2]=dup(STDERR_FILENO); dup2(pio[1], STDOUT_FILENO); dup2(pio[0], STDIN_FILENO); dup2(pio[1], STDERR_FILENO); // create new group and session leader if (fork() != 0) { return 0; } umask(0); if (setsid() < 0) // todo BSD - create group, open terminal and ioctl TIOCNOTTY { syslog(LOG_ERR, "setsid error: %d %s", errno, strerror(errno)); return SETSID_ERROR; } // getrlimit(RLIMIT_NOFILE, &flim); //for (fd = 0; fd < flim.rlim_max; fd++) //{ //if ((fd != STDIN_FILENO) && (fd != STDOUT_FILENO) && (fd != STDERR_FILENO)) //close(fd); // }
// set current dir to root if (chdir("/") < 0) { syslog(LOG_ERR, "chdir error: %d %s", errno, strerror(errno)); return CHDIR_ERROR; } if (!load_conf()) { closelog(); return CONFIG_ERROR; } syslog(LOG_INFO, "started.");
int result_code = 0; while (!finalize) { result_code = server(6023); }
// restore std io dup2(oldfd[0], STDIN_FILENO); dup2(oldfd[1], STDOUT_FILENO); dup2(oldfd[2], STDERR_FILENO); syslog(LOG_INFO, "stoped."); closelog(); return result_code; }
|
Примечания: - демон однопоточный, одновременно с ним может работать не более 1 клиента; - есть небольшие утечки памяти в функции server (3 new и 0 delete)  ; - закомментированный участок в функции main не работает под Mac'ом (потому и закоменчен) Это сообщение отредактировал(а) d2s1 - 5.3.2010, 10:49
|