diff options
author | Rob Landley <rob@landley.net> | 2013-12-23 09:33:48 -0600 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2013-12-23 09:33:48 -0600 |
commit | c3cc96479f0ccadb48731d15b7236b82bd6278ea (patch) | |
tree | 242904d0ba5df41c22f1151f0d64d2d20ac9cdc5 /toys/pending | |
parent | 712b12597de002bb168c23cbc76206c82ef69e2f (diff) | |
download | toybox-c3cc96479f0ccadb48731d15b7236b82bd6278ea.tar.gz |
telnet and telnetd from Ashwini Sharma's guys.
Diffstat (limited to 'toys/pending')
-rw-r--r-- | toys/pending/telnet.c | 386 | ||||
-rw-r--r-- | toys/pending/telnetd.c | 451 |
2 files changed, 837 insertions, 0 deletions
diff --git a/toys/pending/telnet.c b/toys/pending/telnet.c new file mode 100644 index 00000000..e0f9f6f0 --- /dev/null +++ b/toys/pending/telnet.c @@ -0,0 +1,386 @@ +/* telnet.c - Telnet client. + * + * Copyright 2012 Madhur Verma <mad.flexi@gmail.com> + * Copyright 2013 Kyungwan Han <asura321@gmail.com> + * Modified by Ashwini Kumar <ak.ashwini1981@gmail.com> + * + * Not in SUSv4. + +USE_TELNET(NEWTOY(telnet, "<1>2", TOYFLAG_BIN)) + +config TELNET + bool "telnet" + default n + help + usage: telnet HOST [PORT] + Connect to telnet server +*/ + +#define FOR_telnet +#include "toys.h" +#include <arpa/telnet.h> +#include <netinet/in.h> +#include <sys/poll.h> + +GLOBALS( + int port; + int sfd; + char buff[128]; + int pbuff; + char iac[256]; + int piac; + char *ttype; + struct termios def_term; + struct termios raw_term; + uint8_t term_ok; + uint8_t term_mode; + uint8_t flags; + unsigned win_width; + unsigned win_height; + unsigned signalno; +) + +#define DATABUFSIZE 128 +#define IACBUFSIZE 256 +#define CM_TRY 0 +#define CM_ON 1 +#define CM_OFF 2 +#define UF_ECHO 0x01 +#define UF_SGA 0x02 + +/* + * creates a socket of family INET/INET6 and protocol TCP and connects + * it to HOST at PORT. + * if successful then returns SOCK othrwise error + */ +static int xconnect_inet_tcp(char *host, int port) +{ + int ret; + struct addrinfo *info, *rp; + char buf[32]; + + rp = xzalloc(sizeof(struct addrinfo)); + rp->ai_family = AF_UNSPEC; + rp->ai_socktype = SOCK_STREAM; + rp->ai_protocol = IPPROTO_TCP; + sprintf(buf, "%d", port); + + ret = getaddrinfo(host, buf, rp, &info); + if(ret || !info) perror_exit("BAD ADDRESS: can't find : %s ", host); + free(rp); + + for (rp = info; rp; rp = rp->ai_next) + if ( (rp->ai_family == AF_INET) || (rp->ai_family == AF_INET6)) break; + + if (!rp) error_exit("Invalid IP %s", host); + + ret = xsocket(rp->ai_family, SOCK_STREAM, IPPROTO_TCP); + if(connect(ret, rp->ai_addr, rp->ai_addrlen) == -1) perror_exit("connect"); + + freeaddrinfo(info); + return ret; +} + +// sets terminal mode: LINE or CHARACTER based om internal stat. +static char const es[] = "\r\nEscape character is "; +static void set_mode(void) +{ + if (TT.flags & UF_ECHO) { + if (TT.term_mode == CM_TRY) { + TT.term_mode = CM_ON; + printf("\r\nEntering character mode%s'^]'.\r\n", es); + if (TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.raw_term); + } + } else { + if (TT.term_mode != CM_OFF) { + TT.term_mode = CM_OFF; + printf("\r\nEntering line mode%s'^C'.\r\n", es); + if (TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term); + } + } +} + +// flushes all data in IAC buff to server. +static void flush_iac(void) +{ + int wlen = write(TT.sfd, TT.iac, TT.piac); + + if(wlen <= 0) error_msg("IAC : send failed."); + TT.piac = 0; +} + +// puts DATA in iac buff of length LEN and updates iac buff pointer. +static void put_iac(int len, ...) +{ + va_list va; + + if(TT.piac + len >= IACBUFSIZE) flush_iac(); + va_start(va, len); + for(;len > 0; TT.iac[TT.piac++] = (uint8_t)va_arg(va, int), len--); + va_end(va); +} + +// puts string STR in iac buff and updates iac buff pointer. +static void str_iac(char *str) +{ + int len = strlen(str); + + if(TT.piac + len + 1 >= IACBUFSIZE) flush_iac(); + strcpy(&TT.iac[TT.piac], str); + TT.piac += len+1; +} + +static void handle_esc(void) +{ + char input; + + if(TT.signalno && TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.raw_term); + write(1,"\r\nConsole escape. Commands are:\r\n\n" + " l go to line mode\r\n" + " c go to character mode\r\n" + " z suspend telnet\r\n" + " e exit telnet\r\n", 114); + + if (read(STDIN_FILENO, &input, 1) <= 0) { + if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term); + exit(0); + } + + switch (input) { + case 'l': + if (!TT.signalno) { + TT.term_mode = CM_TRY; + TT.flags &= ~(UF_ECHO | UF_SGA); + set_mode(); + put_iac(6, IAC,DONT,TELOPT_ECHO,IAC,DONT, TELOPT_SGA); + flush_iac(); + goto ret; + } + break; + case 'c': + if (TT.signalno) { + TT.term_mode = CM_TRY; + TT.flags |= (UF_ECHO | UF_SGA); + set_mode(); + put_iac(6, IAC,DO,TELOPT_ECHO,IAC,DO,TELOPT_SGA); + flush_iac(); + goto ret; + } + break; + case 'z': + if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term); + kill(0, SIGTSTP); + if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.raw_term); + break; + case 'e': + if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term); + exit(0); + default: break; + } + + write(1, "continuing...\r\n", 15); + if (TT.signalno && TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term); + +ret: + TT.signalno = 0; +} + +/* + * handles telnet SUB NEGOTIATIONS + * only terminal type is supported. + */ +static void handle_negotiations(void) +{ + char opt = TT.buff[TT.pbuff++]; + + switch(opt) { + case TELOPT_TTYPE: + opt = TT.buff[TT.pbuff++]; + if(opt == TELQUAL_SEND) { + put_iac(4, IAC,SB,TELOPT_TTYPE,TELQUAL_IS); + str_iac(TT.ttype); + put_iac(2, IAC,SE); + } + break; + default: break; + } +} + +/* + * handles server's DO DONT WILL WONT requests. + * supports ECHO, SGA, TTYPE, NAWS + */ +static void handle_ddww(char ddww) +{ + char opt = TT.buff[TT.pbuff++]; + + switch (opt) { + case TELOPT_ECHO: /* ECHO */ + if (ddww == DO) put_iac(3, IAC,WONT,TELOPT_ECHO); + if(ddww == DONT) break; + if (TT.flags & UF_ECHO) { + if (ddww == WILL) return; + } else if (ddww == WONT) return; + if (TT.term_mode != CM_OFF) TT.flags ^= UF_ECHO; + (TT.flags & UF_ECHO)? put_iac(3, IAC,DO,TELOPT_ECHO) : + put_iac(3, IAC,DONT,TELOPT_ECHO); + set_mode(); + printf("\r\n"); + break; + + case TELOPT_SGA: /* Supress GO Ahead */ + if (TT.flags & UF_SGA){ if (ddww == WILL) return; + } else if (ddww == WONT) return; + + TT.flags ^= UF_SGA; + (TT.flags & UF_SGA)? put_iac(3, IAC,DO,TELOPT_SGA) : + put_iac(3, IAC,DONT,TELOPT_SGA); + break; + + case TELOPT_TTYPE: /* Terminal Type */ + (TT.ttype)? put_iac(3, IAC,WILL,TELOPT_TTYPE): + put_iac(3, IAC,WONT,TELOPT_TTYPE); + break; + + case TELOPT_NAWS: /* Window Size */ + put_iac(3, IAC,WILL,TELOPT_NAWS); + put_iac(9, IAC,SB,TELOPT_NAWS,(TT.win_width >> 8) & 0xff, + TT.win_width & 0xff,(TT.win_height >> 8) & 0xff, + TT.win_height & 0xff,IAC,SE); + break; + + default: /* Default behaviour is to say NO */ + if(ddww == WILL) put_iac(3, IAC,DONT,opt); + if(ddww == DO) put_iac(3, IAC,WONT,opt); + break; + } +} + +/* + * parses data which is read from server of length LEN. + * and passes it to console. + */ +static int read_server(int len) +{ + int i = 0; + char curr; + TT.pbuff = 0; + + do { + curr = TT.buff[TT.pbuff++]; + if (curr == IAC) { + curr = TT.buff[TT.pbuff++]; + switch (curr) { + case DO: /* FALLTHROUGH */ + case DONT: /* FALLTHROUGH */ + case WILL: /* FALLTHROUGH */ + case WONT: + handle_ddww(curr); + break; + case SB: + handle_negotiations(); + break; + case SE: + break; + default: break; + } + } else { + toybuf[i++] = curr; + if (curr == '\r') { curr = TT.buff[TT.pbuff++]; + if (curr != '\0') TT.pbuff--; + } + } + } while (TT.pbuff < len); + + if (i) write(STDIN_FILENO, toybuf, i); + return 0; +} + +/* + * parses data which is read from console of length LEN + * and passes it to server. + */ +static void write_server(int len) +{ + char *c = (char*)TT.buff; + int i = 0; + + for (; len > 0; len--, c++) { + if (*c == 0x1d) { + handle_esc(); + return; + } + toybuf[i++] = *c; + if (*c == IAC) toybuf[i++] = *c; /* IAC -> IAC IAC */ + else if (*c == '\r') toybuf[i++] = '\0'; /* CR -> CR NUL */ + } + if(i) write(TT.sfd, toybuf, i); +} + +/* + * SIGINT signal handling. + * only sets signalno which get handle in main loop. + */ +static void handle_sigint(int signo) +{ + TT.signalno = signo; +} + +void telnet_main(void) +{ + int set = 1, len; + struct pollfd pfds[2]; + + TT.port = 23; //TELNET_PORT + TT.win_width = 80; //columns + TT.win_height = 24; //rows + + if(toys.optc == 2) TT.port = atoi(toys.optargs[1]); + if(TT.port <= 0 || TT.port > 65535) + error_exit("PORT can be non-zero upto 65535."); + + TT.ttype = getenv("TERM"); + if(!TT.ttype) TT.ttype = ""; + if(strlen(TT.ttype) > IACBUFSIZE-1) TT.ttype[IACBUFSIZE - 1] = '\0'; + + if (!tcgetattr(0, &TT.def_term)) { + TT.term_ok = 1; + TT.raw_term = TT.def_term; + cfmakeraw(&TT.raw_term); + } + terminal_size(&TT.win_width, &TT.win_height); + + TT.sfd = xconnect_inet_tcp(toys.optargs[0], TT.port); + setsockopt(TT.sfd, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set)); + setsockopt(TT.sfd, SOL_SOCKET, SO_KEEPALIVE, &set, sizeof(set)); + + pfds[0].fd = STDIN_FILENO; + pfds[0].events = POLLIN; + pfds[1].fd = TT.sfd; + pfds[1].events = POLLIN; + + TT.piac = TT.pbuff = 0; + TT.signalno = 0; + signal(SIGINT, handle_sigint); + while(1) { + if(TT.piac) flush_iac(); + if(poll(pfds, 2, -1) < 0) { + (TT.signalno)?handle_esc():sleep(1); + continue; + } + if(pfds[0].revents) { + len = read(STDIN_FILENO, TT.buff, DATABUFSIZE); + if(len > 0) write_server(len); + else return; + } + if(pfds[1].revents) { + len = read(TT.sfd, TT.buff, DATABUFSIZE); + if(len > 0) read_server(len); + else { + printf("Connection closed by foreign host\r\n"); + if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term); + exit(1); + } + } + } +} diff --git a/toys/pending/telnetd.c b/toys/pending/telnetd.c new file mode 100644 index 00000000..115b3840 --- /dev/null +++ b/toys/pending/telnetd.c @@ -0,0 +1,451 @@ +/* telnetd.c - Telnet Server + * + * Copyright 2013 Sandeep Sharma <sandeep.jack2756@gmail.com> + * Copyright 2013 Kyungwan Han <asura321@gmail.com> + * +USE_TELNETD(NEWTOY(telnetd, "w#<0b:p#<0>65535=23f:l:FSKi[!wi]", TOYFLAG_USR|TOYFLAG_BIN)) + +config TELNETD + bool "telnetd" + default n + help + Handle incoming telnet connections + + -l LOGIN Exec LOGIN on connect + -f ISSUE_FILE Display ISSUE_FILE instead of /etc/issue + -K Close connection as soon as login exits + -p PORT Port to listen on + -b ADDR[:PORT] Address to bind to + -F Run in foreground + -i Inetd mode + -w SEC Inetd 'wait' mode, linger time SEC + -S Log to syslog (implied by -i or without -F and -w) +*/ + +#define FOR_telnetd +#include "toys.h" +#include <utmp.h> +GLOBALS( + char *login_path; + char *issue_path; + int port; + char *host_addr; + long w_sec; + + int gmax_fd; + int sig; + pid_t fork_pid; +) + + +# define IAC 255 /* interpret as command: */ +# define DONT 254 /* you are not to use option */ +# define DO 253 /* please, you use option */ +# define WONT 252 /* I won't use option */ +# define WILL 251 /* I will use option */ +# define SB 250 /* interpret as subnegotiation */ +# define SE 240 /* end sub negotiation */ +# define NOP 241 /* No Operation */ +# define TELOPT_ECHO 1 /* echo */ +# define TELOPT_SGA 3 /* suppress go ahead */ +# define TELOPT_TTYPE 24 /* terminal type */ +# define TELOPT_NAWS 31 /* window size */ + +#define BUFSIZE 4*1024 +struct term_session { + int new_fd, pty_fd; + pid_t child_pid; + int buff1_avail, buff2_avail; + int buff1_written, buff2_written; + int rem; //unprocessed data from socket + char buff1[BUFSIZE], buff2[BUFSIZE]; + struct term_session *next; +}; + +struct term_session *session_list = NULL; + +static void get_sockaddr(char *host, void *buf) +{ + in_port_t port_num = htons(TT.port); + struct addrinfo hints, *result; + int status, af = AF_UNSPEC; + char *s; + + // [ipv6]:port or exactly one : + if (*host == '[') { + host++; + s = strchr(host, ']'); + if (s) *s++ = 0; + else error_exit("bad address '%s'", host-1); + af = AF_INET6; + } else { + s = strrchr(host, ':'); + if (s && strchr(host, ':') == s) { + *s = 0; + af = AF_INET; + } else if (s && strchr(host, ':') != s) { + af = AF_INET6; + s = 0; + } + } + + if (s++) { + char *ss; + unsigned long p = strtoul(s, &ss, 0); + if (!*s || *ss || p > 65535) error_exit("bad port '%s'", s); + port_num = htons(p); + } + + memset(&hints, 0 , sizeof(struct addrinfo)); + hints.ai_family = af; + hints.ai_socktype = SOCK_STREAM; + + status = getaddrinfo(host, NULL, &hints, &result); + if (status) error_exit("bad address '%s' : %s", host, gai_strerror(status)); + + memcpy(buf, result->ai_addr, result->ai_addrlen); + freeaddrinfo(result); + + if (af == AF_INET) ((struct sockaddr_in*)buf)->sin_port = port_num; + else ((struct sockaddr_in6*)buf)->sin6_port = port_num; +} + +static void utmp_entry(void) +{ + struct utmp entry; + struct utmp *utp_ptr; + pid_t pid = getpid(); + + utmpname(_PATH_UTMP); + setutent(); //start from start + while ((utp_ptr = getutent()) != NULL) { + if (utp_ptr->ut_pid == pid && utp_ptr->ut_type >= INIT_PROCESS) break; + } + if (!utp_ptr) entry.ut_type = DEAD_PROCESS; + time(&entry.ut_time); + setutent(); + pututline(&entry); +} + +static int listen_socket(void) +{ + int s, af = AF_INET, yes = 1; + char buf[sizeof(struct sockaddr_storage)]; + + memset(buf, 0, sizeof(buf)); + if (toys.optflags & FLAG_b) { + get_sockaddr(TT.host_addr, buf); + af = ((struct sockaddr *)buf)->sa_family; + } else { + ((struct sockaddr_in*)buf)->sin_port = htons(TT.port); + ((struct sockaddr_in*)buf)->sin_family = af; + } + s = xsocket(af, SOCK_STREAM, 0); + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)) == -1) + perror_exit("setsockopt"); + + if (bind(s, (struct sockaddr *)buf, ((af == AF_INET)? + (sizeof(struct sockaddr_in)):(sizeof(struct sockaddr_in6)))) == -1) { + close(s); + perror_exit("bind"); + } + + if (listen(s, 1) < 0) perror_exit("listen"); + return s; +} + +static void write_issue(char *tty) +{ + int size; + char ch = 0; + struct utsname u; + int fd = open(TT.issue_path, O_RDONLY); + + if (fd < 0) return ; + uname(&u); + while ((size = readall(fd, &ch, 1)) > 0) { + if (ch == '\\' || ch == '%') { + if (readall(fd, &ch, 1) <= 0) perror_exit("readall!"); + if (ch == 's') fputs(u.sysname, stdout); + if (ch == 'n'|| ch == 'h') fputs(u.nodename, stdout); + if (ch == 'r') fputs(u.release, stdout); + if (ch == 'm') fputs(u.machine, stdout); + if (ch == 'l') fputs(tty, stdout); + } + else if (ch == '\n') { + fputs("\n\r\0", stdout); + } else fputc(ch, stdout); + } + fflush(NULL); + close(fd); +} + +static int new_session(int sockfd) +{ + char *argv_login[2]; //arguments for execvp cmd, NULL + char tty_name[30]; //tty name length. + int fd, flags, i = 1; + char intial_iacs[] = {IAC, DO, TELOPT_ECHO, IAC, DO, TELOPT_NAWS, + IAC, WILL, TELOPT_ECHO, IAC, WILL, TELOPT_SGA }; + + setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &i, sizeof(i)); + flags = fcntl(sockfd, F_GETFL); + fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); + if (toys.optflags & FLAG_i) fcntl((sockfd + 1), F_SETFL, flags | O_NONBLOCK); + + writeall((toys.optflags & FLAG_i)?1:sockfd, intial_iacs, sizeof(intial_iacs)); + if ((TT.fork_pid = forkpty(&fd, tty_name, NULL, NULL)) > 0) { + flags = fcntl(fd, F_GETFL); + fcntl(fd, F_SETFL, flags | O_NONBLOCK); + return fd; + } + if (TT.fork_pid < 0) perror_exit("fork"); + write_issue(tty_name); + argv_login[0] = strdup(TT.login_path); + argv_login[1] = NULL; + execvp(argv_login[0], argv_login); + exit(EXIT_FAILURE); +} + +static int handle_iacs(struct term_session *tm, int c, int fd) +{ + char *curr ,*start,*end; + int i = 0; + + curr = start = tm->buff2+tm->buff2_avail; + end = tm->buff2 + c -1; + tm->rem = 0; + while (curr <= end) { + if (*curr != IAC){ + + if (*curr != '\r') { + toybuf[i++] = *curr++; + continue; + } else { + toybuf[i++] = *curr++; + curr++; + if (curr < end && (*curr == '\n' || *curr == '\0')) + curr++; + continue; + } + } + + if ((curr + 1) > end) { + tm->rem = 1; + break; + } + if (*(curr+1) == IAC) { //IAC as data --> IAC IAC + toybuf[i++] = *(curr+1); + curr += 2; //IAC IAC --> 2 bytes + continue; + } + if (*(curr + 1) == NOP || *(curr + 1) == SE) { + curr += 2; + continue; + } + + if (*(curr + 1) == SB ) { + if (*(curr+2) == TELOPT_NAWS) { + struct winsize ws; + if ((curr+8) >= end) { //ensure we have data to process. + tm->rem = end - curr; + break; + } + ws.ws_col = (curr[3] << 8) | curr[4]; + ws.ws_row = (curr[5] << 8) | curr[6]; + ioctl(fd, TIOCSWINSZ, (char *)&ws); + curr += 9; + continue; + } else { //eat non-supported sub neg. options. + curr++, tm->rem++; + while (*curr != IAC && curr <= end) { + curr++; + tm->rem++; + } + if (*curr == IAC) { + tm->rem = 0; + continue; + } else break; + } + } + curr += 3; //skip non-supported 3 bytes. + } + memcpy(start, toybuf, i); + memcpy(start + i, end - tm->rem, tm->rem); //put remaining if we break; + return i; +} + +static int dup_iacs(char *start, int fd, int len) +{ + char arr[] = {IAC, IAC}; + char *needle = NULL; + int ret = 0, c, count = 0; + + while (len) { + if (*start == IAC) { + count = writeall(fd, arr, sizeof(arr)); + if (count != 2) break; //short write + start++; + ret++; + len--; + continue; + } + needle = memchr(start, IAC, len); + if (needle) c = needle - start; + else c = len; + count = writeall(fd, start, c); + if (count < 0) break; + len -= count; + ret += count; + start += count; + } + return ret; +} + +static void kill_session(void) +{ + int status; + struct term_session *tm, *prev = NULL; + pid_t pid = wait(&status); + TT.sig = 0; //ASAP + + tm = session_list; + if (toys.optflags & FLAG_i) exit(EXIT_SUCCESS); + + if (pid < 0) return; + while (tm) { + if (tm->child_pid == pid) break; + prev = tm; + tm = tm->next; + } + if (!tm) return; //paranoia + if (!prev) session_list = session_list->next; + else prev->next = tm->next; + utmp_entry(); + xclose(tm->pty_fd); + xclose(tm->new_fd); + free(tm); +} + +static void session_handler(int sig) +{ + TT.sig = sig; +} + +void telnetd_main(void) +{ + errno = 0; + fd_set rd, wr; + struct term_session *tm = NULL; + struct timeval tv, *tv_ptr = NULL; + int pty_fd, new_fd, c = 0, w, master_fd = 0; + int inetd_m = (toys.optflags & FLAG_i); + + TT.sig = 0; + if (!(toys.optflags & FLAG_l)) TT.login_path = "/bin/login"; + if (!(toys.optflags & FLAG_f)) TT.issue_path = "/etc/issue.net"; + if (toys.optflags & FLAG_w) toys.optflags |= FLAG_F; + if (!inetd_m) { + master_fd = listen_socket(); + fcntl(master_fd, F_SETFD, FD_CLOEXEC); + if (master_fd > TT.gmax_fd) TT.gmax_fd = master_fd; + if (!(toys.optflags & FLAG_F)) daemonize(); + } else { + pty_fd = new_session(master_fd); //master_fd = 0 + if (pty_fd > TT.gmax_fd) TT.gmax_fd = pty_fd; + tm = xzalloc(sizeof(struct term_session)); + tm->child_pid = TT.fork_pid; + tm->new_fd = 0; + tm->pty_fd = pty_fd; + if (session_list) { + tm->next = session_list; + session_list = tm; + } else session_list = tm; + } + + if ((toys.optflags & FLAG_w) && !session_list) { + tv.tv_sec = TT.w_sec; + tv.tv_usec = 0; + tv_ptr = &tv; + } + signal(SIGCHLD, session_handler); + + for (;;) { + FD_ZERO(&rd); + FD_ZERO(&wr); + if (!inetd_m) FD_SET(master_fd, &rd); + + tm = session_list; + while (tm) { + + if (tm->pty_fd > 0 && tm->buff1_avail < BUFSIZE) FD_SET(tm->pty_fd, &rd); + if (tm->new_fd >= 0 && tm->buff2_avail < BUFSIZE) FD_SET(tm->new_fd, &rd); + if (tm->pty_fd > 0 && (tm->buff2_avail - tm->buff2_written) > 0) + FD_SET(tm->pty_fd, &wr); + if (tm->new_fd >= 0 && (tm->buff1_avail - tm->buff1_written) > 0) + FD_SET(tm->new_fd, &wr); + tm = tm->next; + } + + + int r = select(TT.gmax_fd + 1, &rd, &wr, NULL, tv_ptr); + if (!r) return; //timeout + if (r < -1) continue; + + if (!inetd_m && FD_ISSET(master_fd, &rd)) { //accept new connection + new_fd = accept(master_fd, NULL, NULL); + if (new_fd < 0) continue; + tv_ptr = NULL; + fcntl(new_fd, F_SETFD, FD_CLOEXEC); + if (new_fd > TT.gmax_fd) TT.gmax_fd = new_fd; + pty_fd = new_session(new_fd); + if (pty_fd > TT.gmax_fd) TT.gmax_fd = pty_fd; + + tm = xzalloc(sizeof(struct term_session)); + tm->child_pid = TT.fork_pid; + tm->new_fd = new_fd; + tm->pty_fd = pty_fd; + if (session_list) { + tm->next = session_list; + session_list = tm; + } else session_list = tm; + } + + tm = session_list; + for (;tm;tm=tm->next) { + if (FD_ISSET(tm->pty_fd, &rd)) { + if ((c = read(tm->pty_fd, tm->buff1 + tm->buff1_avail, + BUFSIZE-tm->buff1_avail)) <= 0) break; + tm->buff1_avail += c; + if ((w = dup_iacs(tm->buff1 + tm->buff1_written, tm->new_fd + inetd_m, + tm->buff1_avail - tm->buff1_written)) < 0) break; + tm->buff1_written += w; + } + if (FD_ISSET(tm->new_fd, &rd)) { + if ((c = read(tm->new_fd, tm->buff2+tm->buff2_avail, + BUFSIZE-tm->buff2_avail)) <= 0) break; + c = handle_iacs(tm, c, tm->pty_fd); + tm->buff2_avail += c; + if ((w = write(tm->pty_fd, tm->buff2+ tm->buff2_written, + tm->buff2_avail - tm->buff2_written)) < 0) break; + tm->buff2_written += w; + } + if (FD_ISSET(tm->pty_fd, &wr)) { + if ((w = write(tm->pty_fd, tm->buff2 + tm->buff2_written, + tm->buff2_avail - tm->buff2_written)) < 0) break; + tm->buff2_written += w; + } + if (FD_ISSET(tm->new_fd, &wr)) { + if ((w = dup_iacs(tm->buff1 + tm->buff1_written, tm->new_fd + inetd_m, + tm->buff1_avail - tm->buff1_written)) < 0) break; + tm->buff1_written += w; + } + if (tm->buff1_written == tm->buff1_avail) + tm->buff1_written = tm->buff1_avail = 0; + if (tm->buff2_written == tm->buff2_avail) + tm->buff2_written = tm->buff2_avail = 0; + fflush(NULL); + } + if (TT.sig) kill_session(); + } +} |