1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#define DEBUG 0
25
26#include "libbb.h"
27#include <syslog.h>
28
29#if DEBUG
30#define TELCMDS
31#define TELOPTS
32#endif
33#include <arpa/telnet.h>
34
35
36struct tsession {
37 struct tsession *next;
38 pid_t shell_pid;
39 int sockfd_read, sockfd_write, ptyfd;
40
41
42
43
44
45#define TS_BUF1 ((unsigned char*)(ts + 1))
46#define TS_BUF2 (((unsigned char*)(ts + 1)) + BUFSIZE)
47 int rdidx1, wridx1, size1;
48 int rdidx2, wridx2, size2;
49};
50
51
52
53enum { BUFSIZE = (4 * 1024 - sizeof(struct tsession)) / 2 };
54
55
56
57static int maxfd;
58static struct tsession *sessions;
59static const char *loginpath = "/bin/login";
60static const char *issuefile = "/etc/issue.net";
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81static unsigned char *
82remove_iacs(struct tsession *ts, int *pnum_totty)
83{
84 unsigned char *ptr0 = TS_BUF1 + ts->wridx1;
85 unsigned char *ptr = ptr0;
86 unsigned char *totty = ptr;
87 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
88 int num_totty;
89
90 while (ptr < end) {
91 if (*ptr != IAC) {
92 char c = *ptr;
93
94 *totty++ = c;
95 ptr++;
96
97
98
99
100 if (c == '\r' && ptr < end && (*ptr == '\n' || *ptr == '\0'))
101 ptr++;
102 continue;
103 }
104
105 if ((ptr+1) >= end)
106 break;
107 if (ptr[1] == NOP) {
108 ptr += 2;
109 continue;
110 }
111 if (ptr[1] == IAC) {
112 *totty++ = ptr[1];
113 ptr += 2;
114 continue;
115 }
116
117
118
119
120 if ((ptr+2) >= end) {
121
122
123
124 break;
125 }
126
127
128
129 if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
130 struct winsize ws;
131 if ((ptr+8) >= end)
132 break;
133 ws.ws_col = (ptr[3] << 8) | ptr[4];
134 ws.ws_row = (ptr[5] << 8) | ptr[6];
135 ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
136 ptr += 9;
137 continue;
138 }
139
140#if DEBUG
141 fprintf(stderr, "Ignoring IAC %s,%s\n",
142 TELCMD(ptr[1]), TELOPT(ptr[2]));
143#endif
144 ptr += 3;
145 }
146
147 num_totty = totty - ptr0;
148 *pnum_totty = num_totty;
149
150
151 if ((ptr - totty) == 0)
152 return ptr0;
153 ts->wridx1 += ptr - totty;
154 ts->size1 -= ptr - totty;
155
156 return memmove(ptr - num_totty, ptr0, num_totty);
157}
158
159
160
161
162static size_t iac_safe_write(int fd, const char *buf, size_t count)
163{
164 const char *IACptr;
165 size_t wr, rc, total;
166
167 total = 0;
168 while (1) {
169 if (count == 0)
170 return total;
171 if (*buf == (char)IAC) {
172 static const char IACIAC[] ALIGN1 = { IAC, IAC };
173 rc = safe_write(fd, IACIAC, 2);
174 if (rc != 2)
175 break;
176 buf++;
177 total++;
178 count--;
179 continue;
180 }
181
182 IACptr = memchr(buf, IAC, count);
183 wr = count;
184 if (IACptr)
185 wr = IACptr - buf;
186 rc = safe_write(fd, buf, wr);
187 if (rc != wr)
188 break;
189 buf += rc;
190 total += rc;
191 count -= rc;
192 }
193
194 if ((ssize_t)rc < 0) {
195 if (total == 0)
196 return rc;
197 rc = 0;
198 }
199 return total + rc;
200}
201
202
203enum {
204 OPT_WATCHCHILD = (1 << 2),
205 OPT_INETD = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE,
206 OPT_PORT = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE,
207 OPT_FOREGROUND = (1 << 6) * ENABLE_FEATURE_TELNETD_STANDALONE,
208};
209
210static struct tsession *
211make_new_session(
212 USE_FEATURE_TELNETD_STANDALONE(int master_fd, int sock)
213 SKIP_FEATURE_TELNETD_STANDALONE(void)
214) {
215 const char *login_argv[2];
216 struct termios termbuf;
217 int fd, pid;
218 char tty_name[GETPTY_BUFSIZE];
219 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
220
221
222
223
224
225 fd = xgetpty(tty_name);
226 if (fd > maxfd)
227 maxfd = fd;
228 ts->ptyfd = fd;
229 ndelay_on(fd);
230#if ENABLE_FEATURE_TELNETD_STANDALONE
231 ts->sockfd_read = sock;
232
233 setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
234 ndelay_on(sock);
235 if (!sock) {
236 sock++;
237 ndelay_on(sock);
238 }
239 ts->sockfd_write = sock;
240 if (sock > maxfd)
241 maxfd = sock;
242#else
243
244 setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
245
246 ts->sockfd_write = 1;
247 ndelay_on(0);
248 ndelay_on(1);
249#endif
250
251
252
253
254 {
255 static const char iacs_to_send[] ALIGN1 = {
256 IAC, DO, TELOPT_ECHO,
257 IAC, DO, TELOPT_NAWS,
258
259
260 IAC, WILL, TELOPT_ECHO,
261 IAC, WILL, TELOPT_SGA
262 };
263
264
265
266
267
268
269#if ENABLE_FEATURE_TELNETD_STANDALONE
270 safe_write(sock, iacs_to_send, sizeof(iacs_to_send));
271#else
272 safe_write(1, iacs_to_send, sizeof(iacs_to_send));
273#endif
274
275
276 }
277
278 fflush(NULL);
279 pid = vfork();
280 if (pid < 0) {
281 free(ts);
282 close(fd);
283
284 bb_perror_msg("vfork");
285 return NULL;
286 }
287 if (pid > 0) {
288
289 ts->shell_pid = pid;
290 return ts;
291 }
292
293
294
295
296
297 bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL);
298
299#if ENABLE_FEATURE_TELNETD_STANDALONE
300 if (!(option_mask32 & OPT_INETD)) {
301 struct tsession *tp = sessions;
302 while (tp) {
303 close(tp->ptyfd);
304 close(tp->sockfd_read);
305
306
307 tp = tp->next;
308 }
309 }
310#endif
311
312
313 setsid();
314
315 close(fd);
316#if ENABLE_FEATURE_TELNETD_STANDALONE
317 close(sock);
318 if (master_fd >= 0)
319 close(master_fd);
320#endif
321
322
323
324
325 close(0);
326 xopen(tty_name, O_RDWR);
327 xdup2(0, 1);
328 xdup2(0, 2);
329 tcsetpgrp(0, getpid());
330
331
332
333 tcgetattr(0, &termbuf);
334 termbuf.c_lflag |= ECHO;
335 termbuf.c_oflag |= ONLCR | XTABS;
336 termbuf.c_iflag |= ICRNL;
337 termbuf.c_iflag &= ~IXOFF;
338
339 tcsetattr_stdin_TCSANOW(&termbuf);
340
341
342
343
344
345
346
347 print_login_issue(issuefile, tty_name);
348
349
350 login_argv[0] = loginpath;
351 login_argv[1] = NULL;
352
353
354 BB_EXECVP(loginpath, (char **)login_argv);
355
356
357 _exit(EXIT_FAILURE);
358}
359
360#if ENABLE_FEATURE_TELNETD_STANDALONE
361
362static void
363free_session(struct tsession *ts)
364{
365 struct tsession *t = sessions;
366
367 if (option_mask32 & OPT_INETD)
368 exit(EXIT_SUCCESS);
369
370
371 if (t == ts)
372 sessions = ts->next;
373 else {
374 while (t->next != ts)
375 t = t->next;
376 t->next = ts->next;
377 }
378
379#if 0
380
381
382
383 kill(ts->shell_pid, SIGKILL);
384 waitpid(ts->shell_pid, NULL, 0);
385#endif
386 close(ts->ptyfd);
387 close(ts->sockfd_read);
388
389
390
391 free(ts);
392
393
394 maxfd = 0;
395 ts = sessions;
396 while (ts) {
397 if (maxfd < ts->ptyfd)
398 maxfd = ts->ptyfd;
399 if (maxfd < ts->sockfd_read)
400 maxfd = ts->sockfd_read;
401#if 0
402
403 if (maxfd < ts->sockfd_write)
404 maxfd = ts->sockfd_write;
405#endif
406 ts = ts->next;
407 }
408}
409
410#else
411
412
413#define free_session(ts) return 0
414
415#endif
416
417static void handle_sigchld(int sig UNUSED_PARAM)
418{
419 pid_t pid;
420 struct tsession *ts;
421
422
423 while (1) {
424 pid = wait_any_nohang(NULL);
425 if (pid <= 0)
426 break;
427 ts = sessions;
428 while (ts) {
429 if (ts->shell_pid == pid) {
430 ts->shell_pid = -1;
431 break;
432 }
433 ts = ts->next;
434 }
435 }
436}
437
438int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
439int telnetd_main(int argc UNUSED_PARAM, char **argv)
440{
441 fd_set rdfdset, wrfdset;
442 unsigned opt;
443 int count;
444 struct tsession *ts;
445#if ENABLE_FEATURE_TELNETD_STANDALONE
446#define IS_INETD (opt & OPT_INETD)
447 int master_fd = master_fd;
448 unsigned portnbr = 23;
449 char *opt_bindaddr = NULL;
450 char *opt_portnbr;
451#else
452 enum {
453 IS_INETD = 1,
454 master_fd = -1,
455 portnbr = 23,
456 };
457#endif
458
459
460 opt = getopt32(argv, "f:l:Ki" USE_FEATURE_TELNETD_STANDALONE("p:b:F"),
461 &issuefile, &loginpath
462 USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));
463 if (!IS_INETD ) {
464
465
466
467 if (!(opt & OPT_FOREGROUND)) {
468
469
470 bb_daemonize_or_rexec(0 , argv);
471 }
472 }
473
474 if (IS_INETD || !(opt & OPT_FOREGROUND)) {
475 openlog(applet_name, LOG_PID, LOG_DAEMON);
476 logmode = LOGMODE_SYSLOG;
477 }
478 USE_FEATURE_TELNETD_STANDALONE(
479 if (opt & OPT_PORT)
480 portnbr = xatou16(opt_portnbr);
481 );
482
483
484
485
486#if ENABLE_FEATURE_TELNETD_STANDALONE
487 if (IS_INETD) {
488 sessions = make_new_session(-1, 0);
489 if (!sessions)
490 return 1;
491 } else {
492 master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
493 xlisten(master_fd, 1);
494 }
495#else
496 sessions = make_new_session();
497 if (!sessions)
498 return 1;
499#endif
500
501
502 signal(SIGPIPE, SIG_IGN);
503
504 if (opt & OPT_WATCHCHILD)
505 signal(SIGCHLD, handle_sigchld);
506 else
507 signal(SIGCHLD, SIG_IGN);
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527 again:
528 FD_ZERO(&rdfdset);
529 FD_ZERO(&wrfdset);
530
531
532
533
534
535 ts = sessions;
536 while (ts) {
537 struct tsession *next = ts->next;
538 if (ts->shell_pid == -1) {
539
540 free_session(ts);
541 } else {
542 if (ts->size1 > 0)
543 FD_SET(ts->ptyfd, &wrfdset);
544 if (ts->size1 < BUFSIZE)
545 FD_SET(ts->sockfd_read, &rdfdset);
546 if (ts->size2 > 0)
547 FD_SET(ts->sockfd_write, &wrfdset);
548 if (ts->size2 < BUFSIZE)
549 FD_SET(ts->ptyfd, &rdfdset);
550 }
551 ts = next;
552 }
553 if (!IS_INETD) {
554 FD_SET(master_fd, &rdfdset);
555
556
557
558 if (master_fd > maxfd)
559 maxfd = master_fd;
560 }
561
562 count = select(maxfd + 1, &rdfdset, &wrfdset, NULL, NULL);
563 if (count < 0)
564 goto again;
565
566#if ENABLE_FEATURE_TELNETD_STANDALONE
567
568 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
569 int fd;
570 struct tsession *new_ts;
571
572 fd = accept(master_fd, NULL, NULL);
573 if (fd < 0)
574 goto again;
575
576 new_ts = make_new_session(master_fd, fd);
577 if (new_ts) {
578 new_ts->next = sessions;
579 sessions = new_ts;
580 } else {
581 close(fd);
582 }
583 }
584#endif
585
586
587 ts = sessions;
588 while (ts) {
589 struct tsession *next = ts->next;
590
591 if ( FD_ISSET(ts->ptyfd, &wrfdset)) {
592 int num_totty;
593 unsigned char *ptr;
594
595 ptr = remove_iacs(ts, &num_totty);
596 count = safe_write(ts->ptyfd, ptr, num_totty);
597 if (count < 0) {
598 if (errno == EAGAIN)
599 goto skip1;
600 goto kill_session;
601 }
602 ts->size1 -= count;
603 ts->wridx1 += count;
604 if (ts->wridx1 >= BUFSIZE)
605 ts->wridx1 = 0;
606 }
607 skip1:
608 if ( FD_ISSET(ts->sockfd_write, &wrfdset)) {
609
610 count = MIN(BUFSIZE - ts->wridx2, ts->size2);
611 count = iac_safe_write(ts->sockfd_write, (void*)(TS_BUF2 + ts->wridx2), count);
612 if (count < 0) {
613 if (errno == EAGAIN)
614 goto skip2;
615 goto kill_session;
616 }
617 ts->size2 -= count;
618 ts->wridx2 += count;
619 if (ts->wridx2 >= BUFSIZE)
620 ts->wridx2 = 0;
621 }
622 skip2:
623
624
625
626
627
628
629 if (ts->size1 == 0) {
630 ts->rdidx1 = 0;
631 ts->wridx1 = 0;
632 }
633 if (ts->size2 == 0) {
634 ts->rdidx2 = 0;
635 ts->wridx2 = 0;
636 }
637
638 if ( FD_ISSET(ts->sockfd_read, &rdfdset)) {
639
640 count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
641 count = safe_read(ts->sockfd_read, TS_BUF1 + ts->rdidx1, count);
642 if (count <= 0) {
643 if (count < 0 && errno == EAGAIN)
644 goto skip3;
645 goto kill_session;
646 }
647
648 if (!TS_BUF1[ts->rdidx1 + count - 1]) {
649 --count;
650 }
651 ts->size1 += count;
652 ts->rdidx1 += count;
653 if (ts->rdidx1 >= BUFSIZE)
654 ts->rdidx1 = 0;
655 }
656 skip3:
657 if ( FD_ISSET(ts->ptyfd, &rdfdset)) {
658
659 count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
660 count = safe_read(ts->ptyfd, TS_BUF2 + ts->rdidx2, count);
661 if (count <= 0) {
662 if (count < 0 && errno == EAGAIN)
663 goto skip4;
664 goto kill_session;
665 }
666 ts->size2 += count;
667 ts->rdidx2 += count;
668 if (ts->rdidx2 >= BUFSIZE)
669 ts->rdidx2 = 0;
670 }
671 skip4:
672 ts = next;
673 continue;
674 kill_session:
675 free_session(ts);
676 ts = next;
677 }
678
679 goto again;
680}
681