int main(int argc, char **argv) { struct sockaddr_in sa; int master_fd; fd_set rdfdset, wrfdset; int selret; int on = 1; int portnbr = 23; int c, ii; int daemonize = 0; char *interface_name = NULL; struct ifreq interface; #ifdef USE_SYSLOG char *appname; appname = strrchr (argv [0], '/'); if (!appname) appname = argv [0]; else appname++; #endif /* check if user supplied a port number */ for (;;) { c = getopt( argc, argv, "i:p:l:hd"); if (c == EOF) break; switch (c) { case 'p': portnbr = atoi(optarg); break; case 'i': interface_name = strdup(optarg); break; case 'l': loginpath = strdup(optarg); break; case 'd': daemonize = 1; break; case 'h': default: show_usage(); exit(1); } } if (!loginpath) { loginpath = SHELLPATH; if (access(loginpath, X_OK) < 0) loginpath = "/bin/sh"; } if (access(loginpath, X_OK) < 0) { /* workaround: error_msg_and_die has doesn't understand variable argument lists yet */ fprintf(stderr,"\"%s\"",loginpath); perror_msg_and_die(" is no valid executable!\n"); } printf("telnetd: starting\n"); printf(" port: %i; interface: %s; login program: %s\n", portnbr, (interface_name)?interface_name:"any", loginpath); argv_init[0] = loginpath; sessions = 0; /* Grab a TCP socket. */ master_fd = socket(AF_INET, SOCK_STREAM, 0); if (master_fd < 0) { perror("socket"); return 1; } (void)setsockopt(master_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); /* Set it to listen to specified port */ memset((void *)&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = htons(portnbr); /* Set it to listen on the specified interface */ if (interface_name) { strcpy(interface.ifr_name, interface_name); /* use ioctl() here as BSD does not have setsockopt() */ if (ioctl(master_fd, SIOCGIFADDR, &interface) < 0) { printf("Please check the NIC you specified with -i option\n"); perror("ioctl SIOCGFADDR"); return 1; } sa.sin_addr = ((struct sockaddr_in *)(&interface.ifr_addr))->sin_addr; } else sa.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(master_fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { perror("bind"); return 1; } if (listen(master_fd, 1) < 0) { perror("listen"); return 1; } if (daemonize) { DEBUG_OUT(" daemonizing\n"); if (daemon(0, 1) < 0) perror_msg_and_die("daemon"); } #ifdef USE_SYSLOG openlog(appname , LOG_NDELAY | LOG_PID, LOG_DAEMON); syslog(LOG_INFO, "%s (port: %i, ifname: %s, login: %s) startup succeeded\n"\ , appname, portnbr, (interface_name)?interface_name:"any", loginpath); closelog(); #endif maxfd = master_fd; do { struct tsession *ts; FD_ZERO(&rdfdset); FD_ZERO(&wrfdset); /* select on the master socket, all telnet sockets and their * ptys if there is room in their respective session buffers. */ FD_SET(master_fd, &rdfdset); ts = sessions; while (ts) { /* buf1 is used from socket to pty * buf2 is used from pty to socket */ if (ts->size1 > 0) { FD_SET(ts->ptyfd, &wrfdset); /* can write to pty */ } if (ts->size1 < BUFSIZE) { FD_SET(ts->sockfd, &rdfdset); /* can read from socket */ } if (ts->size2 > 0) { FD_SET(ts->sockfd, &wrfdset); /* can write to socket */ } if (ts->size2 < BUFSIZE) { FD_SET(ts->ptyfd, &rdfdset); /* can read from pty */ } ts = ts->next; } selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0); if (!selret) break; /* First check for and accept new sessions. */ if (FD_ISSET(master_fd, &rdfdset)) { int fd, salen; salen = sizeof(sa); if ((fd = accept(master_fd, (struct sockaddr *)&sa, &salen)) < 0) { continue; } else { /* Create a new session and link it into our active list. */ struct tsession *new_ts; #ifdef USE_SYSLOG openlog(appname , LOG_NDELAY, LOG_DAEMON); syslog(LOG_INFO, "connection from: %s\n", inet_ntoa(sa.sin_addr)); closelog(); #endif new_ts = make_new_session(fd); if (new_ts) { new_ts->next = sessions; sessions = new_ts; if (fd > maxfd) maxfd = fd; } else { close(fd); } } } /* Then check for data tunneling. */ ts = sessions; while (ts) { /* For all sessions... */ int maxlen, w, r; struct tsession *next = ts->next; /* in case we free ts. */ if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) { int processed, num_totty; char *ptr; /* Write to pty from buffer 1. */ maxlen = MIN(BUFSIZE - ts->wridx1, ts->size1); ptr = remove_iacs(ts->buf1 + ts->wridx1, maxlen, &processed, &num_totty); /* the difference between processed and num_totty is all the iacs we removed from the stream. Adjust buf1 accordingly. */ ts->wridx1 += processed - num_totty; ts->size1 -= processed - num_totty; w = write(ts->ptyfd, ptr, num_totty); if (w < 0) { perror("write"); free_session(ts); ts = next; continue; } ts->wridx1 += w; ts->size1 -= w; if (ts->wridx1 == BUFSIZE) ts->wridx1 = 0; } if (ts->size2 && FD_ISSET(ts->sockfd, &wrfdset)) { /* Write to socket from buffer 2. */ maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2); w = write(ts->sockfd, ts->buf2 + ts->wridx2, maxlen); if (w < 0) { perror("write"); free_session(ts); ts = next; continue; } ts->wridx2 += w; ts->size2 -= w; if (ts->wridx2 == BUFSIZE) ts->wridx2 = 0; } if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd, &rdfdset)) { /* Read from socket to buffer 1. */ maxlen = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1); r = read(ts->sockfd, ts->buf1 + ts->rdidx1, maxlen); if (!r || (r < 0 && errno != EINTR)) { free_session(ts); ts = next; continue; } if(!*(ts->buf1 + ts->rdidx1 + r - 1)) { r--; if(!r) continue; } ts->rdidx1 += r; ts->size1 += r; if (ts->rdidx1 == BUFSIZE) ts->rdidx1 = 0; } if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) { /* Read from pty to buffer 2. */ maxlen = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2); r = read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen); if (!r || (r < 0 && errno != EINTR)) { free_session(ts); ts = next; continue; } for (ii=0; ii < r; ii++) if (*(ts->buf2 + ts->rdidx2 + ii) == 3) fprintf(stderr, "found <CTRL>-<C> in data!\n"); ts->rdidx2 += r; ts->size2 += r; if (ts->rdidx2 == BUFSIZE) ts->rdidx2 = 0; } if (ts->size1 == 0) { ts->rdidx1 = 0; ts->wridx1 = 0; } if (ts->size2 == 0) { ts->rdidx2 = 0; ts->wridx2 = 0; } ts = next; } } while (1); return 0; }
int telnetd_main(int argc, char **argv) { #if !defined(CONFIG_FEATURE_TELNETD_INETD) || defined(IFX_INETD_ENHANCEMENT) int master_fd; #endif #ifndef CONFIG_FEATURE_TELNETD_INETD struct sockaddr_in sa; #endif /* CONFIG_FEATURE_TELNETD_INETD */ fd_set rdfdset, wrfdset; int selret; #ifndef CONFIG_FEATURE_TELNETD_INETD int on = 1; int portnbr = 23; #endif /* CONFIG_FEATURE_TELNETD_INETD */ int c; static const char options[] = #ifdef CONFIG_FEATURE_TELNETD_INETD #ifdef CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT "f:l:t:"; #else /*CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT*/ "f:l:"; #endif /* CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT */ #else /* CONFIG_EATURE_TELNETD_INETD */ #ifdef CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT "f:l:p:t:"; #else /* CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT */ "f:l:p:"; #endif /*CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT*/ #endif /* CONFIG_FEATURE_TELNETD_INETD */ int maxlen, w, r; #ifdef CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT struct timeval time_val; int daemon_timeout_val = time(NULL) + 120; #endif #ifndef CONFIG_LOGIN loginpath = DEFAULT_SHELL; #endif for (;;) { c = getopt( argc, argv, options); if (c == EOF) break; switch (c) { case 'f': issuefile = optarg; break; case 'l': loginpath = optarg; break; #ifndef CONFIG_FEATURE_TELNETD_INETD case 'p': portnbr = atoi(optarg); break; #endif /* CONFIG_FEATURE_TELNETD_INETD */ #ifdef CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT case 't': TIMEOUT = atoi(optarg); break; #endif /* CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT*/ default: bb_show_usage(); } } if (access(loginpath, X_OK) < 0) { bb_error_msg_and_die ("'%s' unavailable.", loginpath); } argv_init[0] = loginpath; openlog(bb_applet_name, 0, LOG_USER); #ifndef IFX_INETD_ENHANCEMENT #ifdef CONFIG_FEATURE_TELNETD_INETD maxfd = 1; sessions = make_new_session(); #else /* CONFIG_EATURE_TELNETD_INETD */ sessions = 0; /* Grab a TCP socket. */ master_fd = socket(AF_INET, SOCK_STREAM, 0); if (master_fd < 0) { bb_perror_msg_and_die("socket"); } (void)setsockopt(master_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); /* Set it to listen to specified port. */ memset((void *)&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = htons(portnbr); if (bind(master_fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { bb_perror_msg_and_die("bind"); } if (listen(master_fd, 1) < 0) { bb_perror_msg_and_die("listen"); } if (daemon(0, 0) < 0) bb_perror_msg_and_die("daemon"); maxfd = master_fd; #endif /* CONFIG_FEATURE_TELNETD_INETD */ #else /* IFX_INETD_ENHANCEMENT */ sessions = 0; master_fd = 0; maxfd = master_fd; #endif /* IFX_INETD_ENHANCEMENT */ do { struct tsession *ts; FD_ZERO(&rdfdset); FD_ZERO(&wrfdset); /* select on the master socket, all telnet sockets and their * ptys if there is room in their respective session buffers. */ #ifndef CONFIG_FEATURE_TELNETD_INETD FD_SET(master_fd, &rdfdset); #endif /* CONFIG_FEATURE_TELNETD_INETD */ ts = sessions; #ifndef CONFIG_FEATURE_TELNETD_INETD while (ts) { #endif /* CONFIG_FEATURE_TELNETD_INETD */ /* buf1 is used from socket to pty * buf2 is used from pty to socket */ if (ts->size1 > 0) { FD_SET(ts->ptyfd, &wrfdset); /* can write to pty */ } if (ts->size1 < BUFSIZE) { #ifdef CONFIG_FEATURE_TELNETD_INETD FD_SET(ts->sockfd_read, &rdfdset); /* can read from socket */ #else /* CONFIG_FEATURE_TELNETD_INETD */ FD_SET(ts->sockfd, &rdfdset); /* can read from socket */ #endif /* CONFIG_FEATURE_TELNETD_INETD */ } if (ts->size2 > 0) { #ifdef CONFIG_FEATURE_TELNETD_INETD FD_SET(ts->sockfd_write, &wrfdset); /* can write to socket */ #else /* CONFIG_FEATURE_TELNETD_INETD */ FD_SET(ts->sockfd, &wrfdset); /* can write to socket */ #endif /* CONFIG_FEATURE_TELNETD_INETD */ } if (ts->size2 < BUFSIZE) { FD_SET(ts->ptyfd, &rdfdset); /* can read from pty */ } #ifndef CONFIG_FEATURE_TELNETD_INETD ts = ts->next; } #endif /* CONFIG_FEATURE_TELNETD_INETD */ #ifdef CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT time_val.tv_sec = 10; time_val.tv_usec = 0; selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, &time_val); #else selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0); #endif if (selret < 0) break; #ifdef CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT // check if the currtime exceeded timeout time #ifndef CONFIG_FEATURE_TELNETD_INETD ts= sessions; while(ts) { struct tsession *next = ts->next; /* in case we free ts. */ #endif /* CONFIG_FEATURE_TELNETD_INETD */ if(ts->timeout_time < time(NULL)) { printf("Timed out \n"); syslog(LOG_ERR,"Session timed out\n"); #ifdef CONFIG_FEATURE_TELNETD_INETD exit(0); #else /* CONFIG_FEATURE_TELNETD_INETD */ daemon_timeout_val = time(NULL) + 120; free_session(ts); #endif/* CONFIG_FEATURE_TELNETD_INETD */ } #ifndef CONFIG_FEATURE_TELNETD_INETD ts= next; } #endif /* CONFIG_FEATURE_TELNETD_INETD */ if(selret == 0) { if(sessions == NULL && daemon_timeout_val < time(NULL)) { printf("Telnetd Timed out \n"); syslog(LOG_ERR,"Telnetd timed out\n"); exit(0); } else continue; } #endif /* CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT */ #ifndef CONFIG_FEATURE_TELNETD_INETD daemon_timeout_val = time(NULL) + 120; /* First check for and accept new sessions. */ if (FD_ISSET(master_fd, &rdfdset)) { int fd, salen; salen = sizeof(sa); if ((fd = accept(master_fd, (struct sockaddr *)&sa, &salen)) < 0) { continue; } else { /* Create a new session and link it into our active list. */ struct tsession *new_ts = make_new_session(fd); if (new_ts) { new_ts->next = sessions; sessions = new_ts; if (fd > maxfd) maxfd = fd; } else { close(fd); } } } /* Then check for data tunneling. */ ts = sessions; while (ts) { /* For all sessions... */ #endif /* CONFIG_FEATURE_TELNETD_INETD */ #ifndef CONFIG_FEATURE_TELNETD_INETD struct tsession *next = ts->next; /* in case we free ts. */ #endif /* CONFIG_FEATURE_TELNETD_INETD */ if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) { int num_totty; char *ptr; /* Write to pty from buffer 1. */ ptr = remove_iacs(ts, &num_totty); w = write(ts->ptyfd, ptr, num_totty); if (w < 0) { #ifdef CONFIG_FEATURE_TELNETD_INETD exit(0); #else /* CONFIG_FEATURE_TELNETD_INETD */ free_session(ts); ts = next; continue; #endif /* CONFIG_FEATURE_TELNETD_INETD */ } ts->wridx1 += w; ts->size1 -= w; if (ts->wridx1 == BUFSIZE) ts->wridx1 = 0; #ifdef CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT //some activity in this session reset the timer ts->timeout_time = time(NULL)+ TIMEOUT; #endif /* CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT */ } #ifdef CONFIG_FEATURE_TELNETD_INETD if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) #else /* CONFIG_FEATURE_TELNETD_INETD */ if (ts->size2 && FD_ISSET(ts->sockfd, &wrfdset)) #endif /* CONFIG_FEATURE_TELNETD_INETD */ { /* Write to socket from buffer 2. */ maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2); #ifdef CONFIG_FEATURE_TELNETD_INETD w = write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen); if (w < 0) exit(0); #ifdef CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT else ts->timeout_time = time(NULL)+TIMEOUT; #endif /*CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT*/ #else /* CONFIG_FEATURE_TELNETD_INETD */ w = write(ts->sockfd, ts->buf2 + ts->wridx2, maxlen); if (w < 0) { free_session(ts); ts = next; continue; } #endif /* CONFIG_FEATURE_TELNETD_INETD */ ts->wridx2 += w; ts->size2 -= w; if (ts->wridx2 == BUFSIZE) ts->wridx2 = 0; #ifdef CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT //some activity in this session reset the timer ts->timeout_time = time(NULL)+ TIMEOUT; #endif /* CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT */ } #ifdef CONFIG_FEATURE_TELNETD_INETD if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) #else /* CONFIG_FEATURE_TELNETD_INETD */ if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd, &rdfdset)) #endif /* CONFIG_FEATURE_TELNETD_INETD */ { /* Read from socket to buffer 1. */ maxlen = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1); #ifdef CONFIG_FEATURE_TELNETD_INETD r = read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen); if (!r || (r < 0 && errno != EINTR)) exit(0); #else /* CONFIG_FEATURE_TELNETD_INETD */ r = read(ts->sockfd, ts->buf1 + ts->rdidx1, maxlen); if (!r || (r < 0 && errno != EINTR)) { free_session(ts); ts = next; continue; } #endif /* CONFIG_FEATURE_TELNETD_INETD */ #ifdef CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT //some activity in this session reset the timer ts->timeout_time = time(NULL)+ TIMEOUT; #endif /* CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT */ if(!*(ts->buf1 + ts->rdidx1 + r - 1)) { r--; if(!r) continue; } ts->rdidx1 += r; ts->size1 += r; if (ts->rdidx1 == BUFSIZE) ts->rdidx1 = 0; } if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) { /* Read from pty to buffer 2. */ maxlen = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2); r = read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen); if (!r || (r < 0 && errno != EINTR)) { #ifdef CONFIG_FEATURE_TELNETD_INETD exit(0); #else /* CONFIG_FEATURE_TELNETD_INETD */ free_session(ts); ts = next; continue; #endif /* CONFIG_FEATURE_TELNETD_INETD */ } ts->rdidx2 += r; ts->size2 += r; if (ts->rdidx2 == BUFSIZE) ts->rdidx2 = 0; #ifdef CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT //some activity in this session reset the timer ts->timeout_time = time(NULL)+ TIMEOUT; #endif /* CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT */ } if (ts->size1 == 0) { ts->rdidx1 = 0; ts->wridx1 = 0; } if (ts->size2 == 0) { ts->rdidx2 = 0; ts->wridx2 = 0; } #ifndef CONFIG_FEATURE_TELNETD_INETD ts = next; } #endif /* CONFIG_FEATURE_TELNETD_INETD */ } while (1); return 0; }
static void *misc_thread_func(void *arg) { fd_set r_fds,w_fds,except_fds; struct timeval tv; int retval, max_fd, tmp_fd; save_ori_io(); fd_server=tcp_socket_init(NULL, server_port); if (fd_server<0) { ERR_DBG_PRINT_QUIT("create telnetd server socket failed."); } listen(fd_server,0); while (1) { FD_ZERO(&r_fds); FD_ZERO(&w_fds); FD_ZERO(&except_fds); FD_SET(fd_server, &r_fds); max_fd=fd_server; if (fd_conn>0) { FD_SET(fd_conn, &r_fds); FD_SET(fd_conn, &except_fds); max_fd=(fd_conn>max_fd)?fd_conn:max_fd; FD_SET(fd_pty_master, &r_fds); max_fd=(fd_pty_master>max_fd)?fd_pty_master:max_fd; } tv.tv_sec = 0; tv.tv_usec = 200000; retval = select(max_fd + 1, &r_fds, &w_fds, &except_fds, &tv); if (retval <= 0) { continue; } if (FD_ISSET(fd_server, &r_fds)) { tmp_fd=accept(fd_server, NULL, NULL); if (tmp_fd<0) { continue; } if (fd_conn>0) { printf_to_fd(fd_conn, "A new login happens, so we shutdown your session...\r\n"); } term_session(); if (make_new_session(tmp_fd)) { close(tmp_fd); } continue; } if (shell_quit_occurred || FD_ISSET(fd_conn, &except_fds)) { term_session(); continue; } if (FD_ISSET(fd_conn, &r_fds)) { trans_data_sock2pty(); } if (FD_ISSET(fd_pty_master, &r_fds)|| (pty2sock_cache_len>0)) { if (trans_data_pty2sock()) { term_session(); continue; } } } return NULL; }