Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/networking/telnetd.c
- Timestamp:
- Feb 25, 2011, 9:26:54 PM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/2.2.9/mindi-busybox/networking/telnetd.c
r1765 r2725 4 4 * Bjorn Wesen, Axis Communications AB (bjornw@axis.com) 5 5 * 6 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.6 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 7 7 * 8 8 * --------------------------------------------------------------------------- … … 12 12 * The telnetd manpage says it all: 13 13 * 14 * Telnetd operates by allocating a pseudo-terminal device (see pty(4))for15 * 16 * 17 * 18 * 14 * Telnetd operates by allocating a pseudo-terminal device (see pty(4)) for 15 * a client, then creating a login process which has the slave side of the 16 * pseudo-terminal as stdin, stdout, and stderr. Telnetd manipulates the 17 * master side of the pseudo-terminal, implementing the telnet protocol and 18 * passing characters between the remote client and the login process. 19 19 * 20 20 * Vladimir Oleynik <dzo@simtreas.ru> 2001 21 * 21 * Set process group corrections, initial busybox port 22 22 */ 23 24 23 #define DEBUG 0 25 24 26 25 #include "libbb.h" 26 #include <syslog.h> 27 27 28 28 #if DEBUG 29 # define TELCMDS30 # define TELOPTS29 # define TELCMDS 30 # define TELOPTS 31 31 #endif 32 32 #include <arpa/telnet.h> 33 #include <sys/syslog.h> 34 35 36 #if ENABLE_LOGIN 37 static const char *loginpath = "/bin/login"; 38 #else 39 static const char *loginpath = DEFAULT_SHELL; 40 #endif 41 42 static const char *issuefile = "/etc/issue.net"; 43 44 /* structure that describes a session */ 33 34 #if ENABLE_FEATURE_UTMP 35 # include <utmp.h> /* LOGIN_PROCESS */ 36 #endif 37 45 38 46 39 struct tsession { 47 40 struct tsession *next; 48 int sockfd_read, sockfd_write, ptyfd; 49 int shell_pid; 41 pid_t shell_pid; 42 int sockfd_read; 43 int sockfd_write; 44 int ptyfd; 45 50 46 /* two circular buffers */ 51 char *buf1, *buf2; 47 /*char *buf1, *buf2;*/ 48 /*#define TS_BUF1(ts) ts->buf1*/ 49 /*#define TS_BUF2(ts) TS_BUF2(ts)*/ 50 #define TS_BUF1(ts) ((unsigned char*)(ts + 1)) 51 #define TS_BUF2(ts) (((unsigned char*)(ts + 1)) + BUFSIZE) 52 52 int rdidx1, wridx1, size1; 53 53 int rdidx2, wridx2, size2; … … 56 56 /* Two buffers are directly after tsession in malloced memory. 57 57 * Make whole thing fit in 4k */ 58 enum { BUFSIZE = (4*1024 - sizeof(struct tsession)) / 2 }; 58 enum { BUFSIZE = (4 * 1024 - sizeof(struct tsession)) / 2 }; 59 60 61 /* Globals */ 62 struct globals { 63 struct tsession *sessions; 64 const char *loginpath; 65 const char *issuefile; 66 int maxfd; 67 } FIX_ALIASING; 68 #define G (*(struct globals*)&bb_common_bufsiz1) 69 #define INIT_G() do { \ 70 G.loginpath = "/bin/login"; \ 71 G.issuefile = "/etc/issue.net"; \ 72 } while (0) 73 59 74 60 75 /* 61 This is how the buffers are used. The arrows indicate the movement 62 of data. 76 Remove all IAC's from buf1 (received IACs are ignored and must be removed 77 so as to not be interpreted by the terminal). Make an uninterrupted 78 string of characters fit for the terminal. Do this by packing 79 all characters meant for the terminal sequentially towards the end of buf. 80 81 Return a pointer to the beginning of the characters meant for the terminal 82 and make *num_totty the number of characters that should be sent to 83 the terminal. 84 85 Note - if an IAC (3 byte quantity) starts before (bf + len) but extends 86 past (bf + len) then that IAC will be left unprocessed and *processed 87 will be less than len. 88 89 CR-LF ->'s CR mapping is also done here, for convenience. 90 91 NB: may fail to remove iacs which wrap around buffer! 92 */ 93 static unsigned char * 94 remove_iacs(struct tsession *ts, int *pnum_totty) 95 { 96 unsigned char *ptr0 = TS_BUF1(ts) + ts->wridx1; 97 unsigned char *ptr = ptr0; 98 unsigned char *totty = ptr; 99 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1); 100 int num_totty; 101 102 while (ptr < end) { 103 if (*ptr != IAC) { 104 char c = *ptr; 105 106 *totty++ = c; 107 ptr++; 108 /* We map \r\n ==> \r for pragmatic reasons. 109 * Many client implementations send \r\n when 110 * the user hits the CarriageReturn key. 111 */ 112 if (c == '\r' && ptr < end && (*ptr == '\n' || *ptr == '\0')) 113 ptr++; 114 continue; 115 } 116 117 if ((ptr+1) >= end) 118 break; 119 if (ptr[1] == NOP) { /* Ignore? (putty keepalive, etc.) */ 120 ptr += 2; 121 continue; 122 } 123 if (ptr[1] == IAC) { /* Literal IAC? (emacs M-DEL) */ 124 *totty++ = ptr[1]; 125 ptr += 2; 126 continue; 127 } 128 129 /* 130 * TELOPT_NAWS support! 131 */ 132 if ((ptr+2) >= end) { 133 /* Only the beginning of the IAC is in the 134 buffer we were asked to process, we can't 135 process this char */ 136 break; 137 } 138 /* 139 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE 140 */ 141 if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) { 142 struct winsize ws; 143 if ((ptr+8) >= end) 144 break; /* incomplete, can't process */ 145 ws.ws_col = (ptr[3] << 8) | ptr[4]; 146 ws.ws_row = (ptr[5] << 8) | ptr[6]; 147 ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws); 148 ptr += 9; 149 continue; 150 } 151 /* skip 3-byte IAC non-SB cmd */ 152 #if DEBUG 153 fprintf(stderr, "Ignoring IAC %s,%s\n", 154 TELCMD(ptr[1]), TELOPT(ptr[2])); 155 #endif 156 ptr += 3; 157 } 158 159 num_totty = totty - ptr0; 160 *pnum_totty = num_totty; 161 /* The difference between ptr and totty is number of iacs 162 we removed from the stream. Adjust buf1 accordingly */ 163 if ((ptr - totty) == 0) /* 99.999% of cases */ 164 return ptr0; 165 ts->wridx1 += ptr - totty; 166 ts->size1 -= ptr - totty; 167 /* Move chars meant for the terminal towards the end of the buffer */ 168 return memmove(ptr - num_totty, ptr0, num_totty); 169 } 170 171 /* 172 * Converting single IAC into double on output 173 */ 174 static size_t iac_safe_write(int fd, const char *buf, size_t count) 175 { 176 const char *IACptr; 177 size_t wr, rc, total; 178 179 total = 0; 180 while (1) { 181 if (count == 0) 182 return total; 183 if (*buf == (char)IAC) { 184 static const char IACIAC[] ALIGN1 = { IAC, IAC }; 185 rc = safe_write(fd, IACIAC, 2); 186 if (rc != 2) 187 break; 188 buf++; 189 total++; 190 count--; 191 continue; 192 } 193 /* count != 0, *buf != IAC */ 194 IACptr = memchr(buf, IAC, count); 195 wr = count; 196 if (IACptr) 197 wr = IACptr - buf; 198 rc = safe_write(fd, buf, wr); 199 if (rc != wr) 200 break; 201 buf += rc; 202 total += rc; 203 count -= rc; 204 } 205 /* here: rc - result of last short write */ 206 if ((ssize_t)rc < 0) { /* error? */ 207 if (total == 0) 208 return rc; 209 rc = 0; 210 } 211 return total + rc; 212 } 213 214 /* Must match getopt32 string */ 215 enum { 216 OPT_WATCHCHILD = (1 << 2), /* -K */ 217 OPT_INETD = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */ 218 OPT_PORT = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p PORT */ 219 OPT_FOREGROUND = (1 << 6) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */ 220 OPT_SYSLOG = (1 << 7) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -S */ 221 OPT_WAIT = (1 << 8) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -w SEC */ 222 }; 223 224 static struct tsession * 225 make_new_session( 226 IF_FEATURE_TELNETD_STANDALONE(int sock) 227 IF_NOT_FEATURE_TELNETD_STANDALONE(void) 228 ) { 229 #if !ENABLE_FEATURE_TELNETD_STANDALONE 230 enum { sock = 0 }; 231 #endif 232 const char *login_argv[2]; 233 struct termios termbuf; 234 int fd, pid; 235 char tty_name[GETPTY_BUFSIZE]; 236 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2); 237 238 /*ts->buf1 = (char *)(ts + 1);*/ 239 /*ts->buf2 = ts->buf1 + BUFSIZE;*/ 240 241 /* Got a new connection, set up a tty */ 242 fd = xgetpty(tty_name); 243 if (fd > G.maxfd) 244 G.maxfd = fd; 245 ts->ptyfd = fd; 246 ndelay_on(fd); 247 close_on_exec_on(fd); 248 249 /* SO_KEEPALIVE by popular demand */ 250 setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1)); 251 #if ENABLE_FEATURE_TELNETD_STANDALONE 252 ts->sockfd_read = sock; 253 ndelay_on(sock); 254 if (sock == 0) { /* We are called with fd 0 - we are in inetd mode */ 255 sock++; /* so use fd 1 for output */ 256 ndelay_on(sock); 257 } 258 ts->sockfd_write = sock; 259 if (sock > G.maxfd) 260 G.maxfd = sock; 261 #else 262 /* ts->sockfd_read = 0; - done by xzalloc */ 263 ts->sockfd_write = 1; 264 ndelay_on(0); 265 ndelay_on(1); 266 #endif 267 268 /* Make the telnet client understand we will echo characters so it 269 * should not do it locally. We don't tell the client to run linemode, 270 * because we want to handle line editing and tab completion and other 271 * stuff that requires char-by-char support. */ 272 { 273 static const char iacs_to_send[] ALIGN1 = { 274 IAC, DO, TELOPT_ECHO, 275 IAC, DO, TELOPT_NAWS, 276 /* This requires telnetd.ctrlSQ.patch (incomplete) */ 277 /*IAC, DO, TELOPT_LFLOW,*/ 278 IAC, WILL, TELOPT_ECHO, 279 IAC, WILL, TELOPT_SGA 280 }; 281 /* This confuses iac_safe_write(), it will try to duplicate 282 * each IAC... */ 283 //memcpy(TS_BUF2(ts), iacs_to_send, sizeof(iacs_to_send)); 284 //ts->rdidx2 = sizeof(iacs_to_send); 285 //ts->size2 = sizeof(iacs_to_send); 286 /* So just stuff it into TCP stream! (no error check...) */ 287 #if ENABLE_FEATURE_TELNETD_STANDALONE 288 safe_write(sock, iacs_to_send, sizeof(iacs_to_send)); 289 #else 290 safe_write(1, iacs_to_send, sizeof(iacs_to_send)); 291 #endif 292 /*ts->rdidx2 = 0; - xzalloc did it */ 293 /*ts->size2 = 0;*/ 294 } 295 296 fflush_all(); 297 pid = vfork(); /* NOMMU-friendly */ 298 if (pid < 0) { 299 free(ts); 300 close(fd); 301 /* sock will be closed by caller */ 302 bb_perror_msg("vfork"); 303 return NULL; 304 } 305 if (pid > 0) { 306 /* Parent */ 307 ts->shell_pid = pid; 308 return ts; 309 } 310 311 /* Child */ 312 /* Careful - we are after vfork! */ 313 314 /* Restore default signal handling ASAP */ 315 bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL); 316 317 if (ENABLE_FEATURE_UTMP) { 318 len_and_sockaddr *lsa = get_peer_lsa(sock); 319 char *hostname = NULL; 320 if (lsa) { 321 hostname = xmalloc_sockaddr2dotted(&lsa->u.sa); 322 free(lsa); 323 } 324 write_new_utmp(pid, LOGIN_PROCESS, tty_name, /*username:*/ "LOGIN", hostname); 325 free(hostname); 326 } 327 328 /* Make new session and process group */ 329 setsid(); 330 331 /* Open the child's side of the tty */ 332 /* NB: setsid() disconnects from any previous ctty's. Therefore 333 * we must open child's side of the tty AFTER setsid! */ 334 close(0); 335 xopen(tty_name, O_RDWR); /* becomes our ctty */ 336 xdup2(0, 1); 337 xdup2(0, 2); 338 pid = getpid(); 339 tcsetpgrp(0, pid); /* switch this tty's process group to us */ 340 341 /* The pseudo-terminal allocated to the client is configured to operate 342 * in cooked mode, and with XTABS CRMOD enabled (see tty(4)) */ 343 tcgetattr(0, &termbuf); 344 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */ 345 termbuf.c_oflag |= ONLCR | XTABS; 346 termbuf.c_iflag |= ICRNL; 347 termbuf.c_iflag &= ~IXOFF; 348 /*termbuf.c_lflag &= ~ICANON;*/ 349 tcsetattr_stdin_TCSANOW(&termbuf); 350 351 /* Uses FILE-based I/O to stdout, but does fflush_all(), 352 * so should be safe with vfork. 353 * I fear, though, that some users will have ridiculously big 354 * issue files, and they may block writing to fd 1, 355 * (parent is supposed to read it, but parent waits 356 * for vforked child to exec!) */ 357 print_login_issue(G.issuefile, tty_name); 358 359 /* Exec shell / login / whatever */ 360 login_argv[0] = G.loginpath; 361 login_argv[1] = NULL; 362 /* exec busybox applet (if PREFER_APPLETS=y), if that fails, 363 * exec external program. 364 * NB: sock is either 0 or has CLOEXEC set on it. 365 * fd has CLOEXEC set on it too. These two fds will be closed here. 366 */ 367 BB_EXECVP(G.loginpath, (char **)login_argv); 368 /* _exit is safer with vfork, and we shouldn't send message 369 * to remote clients anyway */ 370 _exit(EXIT_FAILURE); /*bb_perror_msg_and_die("execv %s", G.loginpath);*/ 371 } 372 373 #if ENABLE_FEATURE_TELNETD_STANDALONE 374 375 static void 376 free_session(struct tsession *ts) 377 { 378 struct tsession *t; 379 380 if (option_mask32 & OPT_INETD) 381 exit(EXIT_SUCCESS); 382 383 /* Unlink this telnet session from the session list */ 384 t = G.sessions; 385 if (t == ts) 386 G.sessions = ts->next; 387 else { 388 while (t->next != ts) 389 t = t->next; 390 t->next = ts->next; 391 } 392 393 #if 0 394 /* It was said that "normal" telnetd just closes ptyfd, 395 * doesn't send SIGKILL. When we close ptyfd, 396 * kernel sends SIGHUP to processes having slave side opened. */ 397 kill(ts->shell_pid, SIGKILL); 398 waitpid(ts->shell_pid, NULL, 0); 399 #endif 400 close(ts->ptyfd); 401 close(ts->sockfd_read); 402 /* We do not need to close(ts->sockfd_write), it's the same 403 * as sockfd_read unless we are in inetd mode. But in inetd mode 404 * we do not reach this */ 405 free(ts); 406 407 /* Scan all sessions and find new maxfd */ 408 G.maxfd = 0; 409 ts = G.sessions; 410 while (ts) { 411 if (G.maxfd < ts->ptyfd) 412 G.maxfd = ts->ptyfd; 413 if (G.maxfd < ts->sockfd_read) 414 G.maxfd = ts->sockfd_read; 415 #if 0 416 /* Again, sockfd_write == sockfd_read here */ 417 if (G.maxfd < ts->sockfd_write) 418 G.maxfd = ts->sockfd_write; 419 #endif 420 ts = ts->next; 421 } 422 } 423 424 #else /* !FEATURE_TELNETD_STANDALONE */ 425 426 /* Used in main() only, thus "return 0" actually is exit(EXIT_SUCCESS). */ 427 #define free_session(ts) return 0 428 429 #endif 430 431 static void handle_sigchld(int sig UNUSED_PARAM) 432 { 433 pid_t pid; 434 struct tsession *ts; 435 int save_errno = errno; 436 437 /* Looping: more than one child may have exited */ 438 while (1) { 439 pid = wait_any_nohang(NULL); 440 if (pid <= 0) 441 break; 442 ts = G.sessions; 443 while (ts) { 444 if (ts->shell_pid == pid) { 445 ts->shell_pid = -1; 446 // man utmp: 447 // When init(8) finds that a process has exited, it locates its utmp entry 448 // by ut_pid, sets ut_type to DEAD_PROCESS, and clears ut_user, ut_host 449 // and ut_time with null bytes. 450 // [same applies to other processes which maintain utmp entries, like telnetd] 451 // 452 // We do not bother actually clearing fields: 453 // it might be interesting to know who was logged in and from where 454 update_utmp(pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL); 455 break; 456 } 457 ts = ts->next; 458 } 459 } 460 461 errno = save_errno; 462 } 463 464 int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 465 int telnetd_main(int argc UNUSED_PARAM, char **argv) 466 { 467 fd_set rdfdset, wrfdset; 468 unsigned opt; 469 int count; 470 struct tsession *ts; 471 #if ENABLE_FEATURE_TELNETD_STANDALONE 472 #define IS_INETD (opt & OPT_INETD) 473 int master_fd = master_fd; /* for compiler */ 474 int sec_linger = sec_linger; 475 char *opt_bindaddr = NULL; 476 char *opt_portnbr; 477 #else 478 enum { 479 IS_INETD = 1, 480 master_fd = -1, 481 }; 482 #endif 483 INIT_G(); 484 485 /* -w NUM, and implies -F. -w and -i don't mix */ 486 IF_FEATURE_TELNETD_INETD_WAIT(opt_complementary = "wF:w+:i--w:w--i";) 487 /* Even if !STANDALONE, we accept (and ignore) -i, thus people 488 * don't need to guess whether it's ok to pass -i to us */ 489 opt = getopt32(argv, "f:l:Ki" 490 IF_FEATURE_TELNETD_STANDALONE("p:b:F") 491 IF_FEATURE_TELNETD_INETD_WAIT("Sw:"), 492 &G.issuefile, &G.loginpath 493 IF_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr) 494 IF_FEATURE_TELNETD_INETD_WAIT(, &sec_linger) 495 ); 496 if (!IS_INETD /*&& !re_execed*/) { 497 /* inform that we start in standalone mode? 498 * May be useful when people forget to give -i */ 499 /*bb_error_msg("listening for connections");*/ 500 if (!(opt & OPT_FOREGROUND)) { 501 /* DAEMON_CHDIR_ROOT was giving inconsistent 502 * behavior with/without -F, -i */ 503 bb_daemonize_or_rexec(0 /*was DAEMON_CHDIR_ROOT*/, argv); 504 } 505 } 506 /* Redirect log to syslog early, if needed */ 507 if (IS_INETD || (opt & OPT_SYSLOG) || !(opt & OPT_FOREGROUND)) { 508 openlog(applet_name, LOG_PID, LOG_DAEMON); 509 logmode = LOGMODE_SYSLOG; 510 } 511 #if ENABLE_FEATURE_TELNETD_STANDALONE 512 if (IS_INETD) { 513 G.sessions = make_new_session(0); 514 if (!G.sessions) /* pty opening or vfork problem, exit */ 515 return 1; /* make_new_session printed error message */ 516 } else { 517 master_fd = 0; 518 if (!(opt & OPT_WAIT)) { 519 unsigned portnbr = 23; 520 if (opt & OPT_PORT) 521 portnbr = xatou16(opt_portnbr); 522 master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr); 523 xlisten(master_fd, 1); 524 } 525 close_on_exec_on(master_fd); 526 } 527 #else 528 G.sessions = make_new_session(); 529 if (!G.sessions) /* pty opening or vfork problem, exit */ 530 return 1; /* make_new_session printed error message */ 531 #endif 532 533 /* We don't want to die if just one session is broken */ 534 signal(SIGPIPE, SIG_IGN); 535 536 if (opt & OPT_WATCHCHILD) 537 signal(SIGCHLD, handle_sigchld); 538 else /* prevent dead children from becoming zombies */ 539 signal(SIGCHLD, SIG_IGN); 540 541 /* 542 This is how the buffers are used. The arrows indicate data flow. 63 543 64 544 +-------+ wridx1++ +------+ rdidx1++ +----------+ … … 70 550 +-------+ size2++ +------+ size2-- +----------+ 71 551 72 Each session has got two buffers. 552 size1: "how many bytes are buffered for pty between rdidx1 and wridx1?" 553 size2: "how many bytes are buffered for socket between rdidx2 and wridx2?" 554 555 Each session has got two buffers. Buffers are circular. If sizeN == 0, 556 buffer is empty. If sizeN == BUFSIZE, buffer is full. In both these cases 557 rdidxN == wridxN. 73 558 */ 74 75 static int maxfd;76 77 static struct tsession *sessions;78 79 80 /*81 Remove all IAC's from the buffer pointed to by bf (received IACs are ignored82 and must be removed so as to not be interpreted by the terminal). Make an83 uninterrupted string of characters fit for the terminal. Do this by packing84 all characters meant for the terminal sequentially towards the end of bf.85 86 Return a pointer to the beginning of the characters meant for the terminal.87 and make *num_totty the number of characters that should be sent to88 the terminal.89 90 Note - If an IAC (3 byte quantity) starts before (bf + len) but extends91 past (bf + len) then that IAC will be left unprocessed and *processed will be92 less than len.93 94 FIXME - if we mean to send 0xFF to the terminal then it will be escaped,95 what is the escape character? We aren't handling that situation here.96 97 CR-LF ->'s CR mapping is also done here, for convenience98 */99 static char *100 remove_iacs(struct tsession *ts, int *pnum_totty)101 {102 unsigned char *ptr0 = (unsigned char *)ts->buf1 + ts->wridx1;103 unsigned char *ptr = ptr0;104 unsigned char *totty = ptr;105 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);106 int processed;107 int num_totty;108 109 while (ptr < end) {110 if (*ptr != IAC) {111 int c = *ptr;112 *totty++ = *ptr++;113 /* We now map \r\n ==> \r for pragmatic reasons.114 * Many client implementations send \r\n when115 * the user hits the CarriageReturn key.116 */117 if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end)118 ptr++;119 } else {120 /*121 * TELOPT_NAWS support!122 */123 if ((ptr+2) >= end) {124 /* only the beginning of the IAC is in the125 buffer we were asked to process, we can't126 process this char. */127 break;128 }129 130 /*131 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE132 */133 else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {134 struct winsize ws;135 if ((ptr+8) >= end)136 break; /* incomplete, can't process */137 ws.ws_col = (ptr[3] << 8) | ptr[4];138 ws.ws_row = (ptr[5] << 8) | ptr[6];139 ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);140 ptr += 9;141 } else {142 /* skip 3-byte IAC non-SB cmd */143 #if DEBUG144 fprintf(stderr, "Ignoring IAC %s,%s\n",145 TELCMD(ptr[1]), TELOPT(ptr[2]));146 #endif147 ptr += 3;148 }149 }150 }151 152 processed = ptr - ptr0;153 num_totty = totty - ptr0;154 /* the difference between processed and num_to tty155 is all the iacs we removed from the stream.156 Adjust buf1 accordingly. */157 ts->wridx1 += processed - num_totty;158 ts->size1 -= processed - num_totty;159 *pnum_totty = num_totty;160 /* move the chars meant for the terminal towards the end of the161 buffer. */162 return memmove(ptr - num_totty, ptr0, num_totty);163 }164 165 166 static int167 getpty(char *line, int size)168 {169 int p;170 #if ENABLE_FEATURE_DEVPTS171 p = open("/dev/ptmx", O_RDWR);172 if (p > 0) {173 const char *name;174 grantpt(p);175 unlockpt(p);176 name = ptsname(p);177 if (!name) {178 bb_perror_msg("ptsname error (is /dev/pts mounted?)");179 return -1;180 }181 safe_strncpy(line, name, size);182 return p;183 }184 #else185 struct stat stb;186 int i;187 int j;188 189 strcpy(line, "/dev/ptyXX");190 191 for (i = 0; i < 16; i++) {192 line[8] = "pqrstuvwxyzabcde"[i];193 line[9] = '0';194 if (stat(line, &stb) < 0) {195 continue;196 }197 for (j = 0; j < 16; j++) {198 line[9] = j < 10 ? j + '0' : j - 10 + 'a';199 if (DEBUG)200 fprintf(stderr, "Trying to open device: %s\n", line);201 p = open(line, O_RDWR | O_NOCTTY);202 if (p >= 0) {203 line[5] = 't';204 return p;205 }206 }207 }208 #endif /* FEATURE_DEVPTS */209 return -1;210 }211 212 213 static void214 send_iac(struct tsession *ts, unsigned char command, int option)215 {216 /* We rely on that there is space in the buffer for now. */217 char *b = ts->buf2 + ts->rdidx2;218 *b++ = IAC;219 *b++ = command;220 *b++ = option;221 ts->rdidx2 += 3;222 ts->size2 += 3;223 }224 225 226 static struct tsession *227 make_new_session(228 USE_FEATURE_TELNETD_STANDALONE(int sock_r, int sock_w)229 SKIP_FEATURE_TELNETD_STANDALONE(void)230 ) {231 const char *login_argv[2];232 struct termios termbuf;233 int fd, pid;234 char tty_name[32];235 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);236 237 ts->buf1 = (char *)(&ts[1]);238 ts->buf2 = ts->buf1 + BUFSIZE;239 240 /* Got a new connection, set up a tty. */241 fd = getpty(tty_name, 32);242 if (fd < 0) {243 bb_error_msg("all terminals in use");244 return NULL;245 }246 if (fd > maxfd) maxfd = fd;247 ndelay_on(ts->ptyfd = fd);248 #if ENABLE_FEATURE_TELNETD_STANDALONE249 if (sock_w > maxfd) maxfd = sock_w;250 if (sock_r > maxfd) maxfd = sock_r;251 ndelay_on(ts->sockfd_write = sock_w);252 ndelay_on(ts->sockfd_read = sock_r);253 #else254 ts->sockfd_write = 1;255 /* xzalloc: ts->sockfd_read = 0; */256 ndelay_on(0);257 ndelay_on(1);258 #endif259 /* Make the telnet client understand we will echo characters so it260 * should not do it locally. We don't tell the client to run linemode,261 * because we want to handle line editing and tab completion and other262 * stuff that requires char-by-char support. */263 send_iac(ts, DO, TELOPT_ECHO);264 send_iac(ts, DO, TELOPT_NAWS);265 send_iac(ts, DO, TELOPT_LFLOW);266 send_iac(ts, WILL, TELOPT_ECHO);267 send_iac(ts, WILL, TELOPT_SGA);268 269 pid = fork();270 if (pid < 0) {271 free(ts);272 close(fd);273 bb_perror_msg("fork");274 return NULL;275 }276 if (pid > 0) {277 /* parent */278 ts->shell_pid = pid;279 return ts;280 }281 282 /* child */283 284 /* make new session and process group */285 setsid();286 287 /* open the child's side of the tty. */288 /* NB: setsid() disconnects from any previous ctty's. Therefore289 * we must open child's side of the tty AFTER setsid! */290 fd = xopen(tty_name, O_RDWR); /* becomes our ctty */291 dup2(fd, 0);292 dup2(fd, 1);293 dup2(fd, 2);294 while (fd > 2) close(fd--);295 tcsetpgrp(0, getpid()); /* switch this tty's process group to us */296 297 /* The pseudo-terminal allocated to the client is configured to operate in298 * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */299 tcgetattr(0, &termbuf);300 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */301 termbuf.c_oflag |= ONLCR|XTABS;302 termbuf.c_iflag |= ICRNL;303 termbuf.c_iflag &= ~IXOFF;304 /*termbuf.c_lflag &= ~ICANON;*/305 tcsetattr(0, TCSANOW, &termbuf);306 307 print_login_issue(issuefile, NULL);308 309 /* exec shell / login /whatever */310 login_argv[0] = loginpath;311 login_argv[1] = NULL;312 execv(loginpath, (char **)login_argv);313 /* Hmmm... this gets sent to the client thru fd#2! Is it ok?? */314 bb_perror_msg_and_die("execv %s", loginpath);315 }316 317 #if ENABLE_FEATURE_TELNETD_STANDALONE318 319 static void320 free_session(struct tsession *ts)321 {322 struct tsession *t = sessions;323 324 /* unlink this telnet session from the session list */325 if (t == ts)326 sessions = ts->next;327 else {328 while (t->next != ts)329 t = t->next;330 t->next = ts->next;331 }332 333 kill(ts->shell_pid, SIGKILL);334 wait4(ts->shell_pid, NULL, 0, NULL);335 close(ts->ptyfd);336 close(ts->sockfd_read);337 /* error if ts->sockfd_read == ts->sockfd_write. So what? ;) */338 close(ts->sockfd_write);339 free(ts);340 341 /* scan all sessions and find new maxfd */342 ts = sessions;343 maxfd = 0;344 while (ts) {345 if (maxfd < ts->ptyfd)346 maxfd = ts->ptyfd;347 if (maxfd < ts->sockfd_read)348 maxfd = ts->sockfd_read;349 if (maxfd < ts->sockfd_write)350 maxfd = ts->sockfd_write;351 ts = ts->next;352 }353 }354 355 #else /* !FEATURE_TELNETD_STANDALONE */356 357 /* Never actually called */358 void free_session(struct tsession *ts);359 360 #endif361 362 363 int telnetd_main(int argc, char **argv);364 int telnetd_main(int argc, char **argv)365 {366 fd_set rdfdset, wrfdset;367 unsigned opt;368 int selret, maxlen, w, r;369 struct tsession *ts;370 #if ENABLE_FEATURE_TELNETD_STANDALONE371 #define IS_INETD (opt & OPT_INETD)372 int master_fd = -1; /* be happy, gcc */373 unsigned portnbr = 23;374 char *opt_bindaddr = NULL;375 char *opt_portnbr;376 #else377 enum {378 IS_INETD = 1,379 master_fd = -1,380 portnbr = 23,381 };382 #endif383 enum {384 OPT_PORT = 4 * ENABLE_FEATURE_TELNETD_STANDALONE,385 OPT_FOREGROUND = 0x10 * ENABLE_FEATURE_TELNETD_STANDALONE,386 OPT_INETD = 0x20 * ENABLE_FEATURE_TELNETD_STANDALONE,387 };388 389 opt = getopt32(argv, "f:l:" USE_FEATURE_TELNETD_STANDALONE("p:b:Fi"),390 &issuefile, &loginpath391 USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));392 /* Redirect log to syslog early, if needed */393 if (IS_INETD || !(opt & OPT_FOREGROUND)) {394 openlog(applet_name, 0, LOG_USER);395 logmode = LOGMODE_SYSLOG;396 }397 //if (opt & 1) // -f398 //if (opt & 2) // -l399 USE_FEATURE_TELNETD_STANDALONE(400 if (opt & OPT_PORT) // -p401 portnbr = xatou16(opt_portnbr);402 //if (opt & 8) // -b403 //if (opt & 0x10) // -F404 //if (opt & 0x20) // -i405 );406 407 /* Used to check access(loginpath, X_OK) here. Pointless.408 * exec will do this for us for free later. */409 410 #if ENABLE_FEATURE_TELNETD_STANDALONE411 if (IS_INETD) {412 sessions = make_new_session(0, 1);413 } else {414 master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);415 xlisten(master_fd, 1);416 if (!(opt & OPT_FOREGROUND))417 bb_daemonize(DAEMON_CHDIR_ROOT);418 }419 #else420 sessions = make_new_session();421 #endif422 423 /* We don't want to die if just one session is broken */424 signal(SIGPIPE, SIG_IGN);425 426 559 again: 427 560 FD_ZERO(&rdfdset); 428 561 FD_ZERO(&wrfdset); 562 563 /* Select on the master socket, all telnet sockets and their 564 * ptys if there is room in their session buffers. 565 * NB: scalability problem: we recalculate entire bitmap 566 * before each select. Can be a problem with 500+ connections. */ 567 ts = G.sessions; 568 while (ts) { 569 struct tsession *next = ts->next; /* in case we free ts */ 570 if (ts->shell_pid == -1) { 571 /* Child died and we detected that */ 572 free_session(ts); 573 } else { 574 if (ts->size1 > 0) /* can write to pty */ 575 FD_SET(ts->ptyfd, &wrfdset); 576 if (ts->size1 < BUFSIZE) /* can read from socket */ 577 FD_SET(ts->sockfd_read, &rdfdset); 578 if (ts->size2 > 0) /* can write to socket */ 579 FD_SET(ts->sockfd_write, &wrfdset); 580 if (ts->size2 < BUFSIZE) /* can read from pty */ 581 FD_SET(ts->ptyfd, &rdfdset); 582 } 583 ts = next; 584 } 429 585 if (!IS_INETD) { 430 586 FD_SET(master_fd, &rdfdset); 431 587 /* This is needed because free_session() does not 432 * take into account master_fd when it finds new 433 * maxfd among remaining fd's: */ 434 if (master_fd > maxfd) 435 maxfd = master_fd; 436 } 437 438 /* select on the master socket, all telnet sockets and their 439 * ptys if there is room in their session buffers. */ 440 ts = sessions; 441 while (ts) { 442 /* buf1 is used from socket to pty 443 * buf2 is used from pty to socket */ 444 if (ts->size1 > 0) /* can write to pty */ 445 FD_SET(ts->ptyfd, &wrfdset); 446 if (ts->size1 < BUFSIZE) /* can read from socket */ 447 FD_SET(ts->sockfd_read, &rdfdset); 448 if (ts->size2 > 0) /* can write to socket */ 449 FD_SET(ts->sockfd_write, &wrfdset); 450 if (ts->size2 < BUFSIZE) /* can read from pty */ 451 FD_SET(ts->ptyfd, &rdfdset); 452 ts = ts->next; 453 } 454 455 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0); 456 if (!selret) 588 * take master_fd into account when it finds new 589 * maxfd among remaining fd's */ 590 if (master_fd > G.maxfd) 591 G.maxfd = master_fd; 592 } 593 594 { 595 struct timeval *tv_ptr = NULL; 596 #if ENABLE_FEATURE_TELNETD_INETD_WAIT 597 struct timeval tv; 598 if ((opt & OPT_WAIT) && !G.sessions) { 599 tv.tv_sec = sec_linger; 600 tv.tv_usec = 0; 601 tv_ptr = &tv; 602 } 603 #endif 604 count = select(G.maxfd + 1, &rdfdset, &wrfdset, NULL, tv_ptr); 605 } 606 if (count == 0) /* "telnetd -w SEC" timed out */ 457 607 return 0; 608 if (count < 0) 609 goto again; /* EINTR or ENOMEM */ 458 610 459 611 #if ENABLE_FEATURE_TELNETD_STANDALONE 460 /* First check for and accept new sessions.*/612 /* Check for and accept new sessions */ 461 613 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) { 462 614 int fd; 463 615 struct tsession *new_ts; 464 616 465 fd = accept(master_fd, NULL, 0);617 fd = accept(master_fd, NULL, NULL); 466 618 if (fd < 0) 467 619 goto again; 468 /* Create a new session and link it into our active list */ 469 new_ts = make_new_session(fd, fd); 620 close_on_exec_on(fd); 621 622 /* Create a new session and link it into active list */ 623 new_ts = make_new_session(fd); 470 624 if (new_ts) { 471 new_ts->next = sessions;472 sessions = new_ts;625 new_ts->next = G.sessions; 626 G.sessions = new_ts; 473 627 } else { 474 628 close(fd); … … 477 631 #endif 478 632 479 /* Then check for data tunneling .*/480 ts = sessions;633 /* Then check for data tunneling */ 634 ts = G.sessions; 481 635 while (ts) { /* For all sessions... */ 482 struct tsession *next = ts->next; /* in case we free ts .*/483 484 if ( ts->size1 &&FD_ISSET(ts->ptyfd, &wrfdset)) {636 struct tsession *next = ts->next; /* in case we free ts */ 637 638 if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) { 485 639 int num_totty; 486 char *ptr;487 /* Write to pty from buffer 1 .*/640 unsigned char *ptr; 641 /* Write to pty from buffer 1 */ 488 642 ptr = remove_iacs(ts, &num_totty); 489 w = safe_write(ts->ptyfd, ptr, num_totty); 490 /* needed? if (w < 0 && errno == EAGAIN) continue; */ 491 if (w < 0) { 492 if (IS_INETD) 493 return 0; 494 free_session(ts); 495 ts = next; 496 continue; 643 count = safe_write(ts->ptyfd, ptr, num_totty); 644 if (count < 0) { 645 if (errno == EAGAIN) 646 goto skip1; 647 goto kill_session; 497 648 } 498 ts-> wridx1 += w;499 ts-> size1 -= w;500 if (ts->wridx1 == BUFSIZE)649 ts->size1 -= count; 650 ts->wridx1 += count; 651 if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */ 501 652 ts->wridx1 = 0; 502 653 } 503 504 if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) { 505 /* Write to socket from buffer 2. */ 506 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2); 507 w = safe_write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen); 508 /* needed? if (w < 0 && errno == EAGAIN) continue; */ 509 if (w < 0) { 510 if (IS_INETD) 511 return 0; 512 free_session(ts); 513 ts = next; 514 continue; 654 skip1: 655 if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) { 656 /* Write to socket from buffer 2 */ 657 count = MIN(BUFSIZE - ts->wridx2, ts->size2); 658 count = iac_safe_write(ts->sockfd_write, (void*)(TS_BUF2(ts) + ts->wridx2), count); 659 if (count < 0) { 660 if (errno == EAGAIN) 661 goto skip2; 662 goto kill_session; 515 663 } 516 ts-> wridx2 += w;517 ts-> size2 -= w;518 if (ts->wridx2 == BUFSIZE)664 ts->size2 -= count; 665 ts->wridx2 += count; 666 if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */ 519 667 ts->wridx2 = 0; 520 668 } 521 522 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) { 523 /* Read from socket to buffer 1. */ 524 maxlen = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1); 525 r = safe_read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen); 526 if (r < 0 && errno == EAGAIN) continue; 527 if (r <= 0) { 528 if (IS_INETD) 529 return 0; 530 free_session(ts); 531 ts = next; 532 continue; 533 } 534 if (!ts->buf1[ts->rdidx1 + r - 1]) 535 if (!--r) 536 continue; 537 ts->rdidx1 += r; 538 ts->size1 += r; 539 if (ts->rdidx1 == BUFSIZE) 540 ts->rdidx1 = 0; 541 } 542 543 if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) { 544 /* Read from pty to buffer 2. */ 545 maxlen = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2); 546 r = safe_read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen); 547 if (r < 0 && errno == EAGAIN) continue; 548 if (r <= 0) { 549 if (IS_INETD) 550 return 0; 551 free_session(ts); 552 ts = next; 553 continue; 554 } 555 ts->rdidx2 += r; 556 ts->size2 += r; 557 if (ts->rdidx2 == BUFSIZE) 558 ts->rdidx2 = 0; 559 } 560 669 skip2: 670 /* Should not be needed, but... remove_iacs is actually buggy 671 * (it cannot process iacs which wrap around buffer's end)! 672 * Since properly fixing it requires writing bigger code, 673 * we rely instead on this code making it virtually impossible 674 * to have wrapped iac (people don't type at 2k/second). 675 * It also allows for bigger reads in common case. */ 561 676 if (ts->size1 == 0) { 562 677 ts->rdidx1 = 0; … … 567 682 ts->wridx2 = 0; 568 683 } 684 685 if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) { 686 /* Read from socket to buffer 1 */ 687 count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1); 688 count = safe_read(ts->sockfd_read, TS_BUF1(ts) + ts->rdidx1, count); 689 if (count <= 0) { 690 if (count < 0 && errno == EAGAIN) 691 goto skip3; 692 goto kill_session; 693 } 694 /* Ignore trailing NUL if it is there */ 695 if (!TS_BUF1(ts)[ts->rdidx1 + count - 1]) { 696 --count; 697 } 698 ts->size1 += count; 699 ts->rdidx1 += count; 700 if (ts->rdidx1 >= BUFSIZE) /* actually == BUFSIZE */ 701 ts->rdidx1 = 0; 702 } 703 skip3: 704 if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) { 705 /* Read from pty to buffer 2 */ 706 count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2); 707 count = safe_read(ts->ptyfd, TS_BUF2(ts) + ts->rdidx2, count); 708 if (count <= 0) { 709 if (count < 0 && errno == EAGAIN) 710 goto skip4; 711 goto kill_session; 712 } 713 ts->size2 += count; 714 ts->rdidx2 += count; 715 if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */ 716 ts->rdidx2 = 0; 717 } 718 skip4: 569 719 ts = next; 570 } 720 continue; 721 kill_session: 722 if (ts->shell_pid > 0) 723 update_utmp(ts->shell_pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL); 724 free_session(ts); 725 ts = next; 726 } 727 571 728 goto again; 572 729 }
Note:
See TracChangeset
for help on using the changeset viewer.