source: MondoRescue/trunk/mindi-busybox/networking/telnetd.c@ 839

Last change on this file since 839 was 821, checked in by Bruno Cornec, 18 years ago

Addition of busybox 1.2.1 as a mindi-busybox new package
This should avoid delivering binary files in mindi not built there (Fedora and Debian are quite serious about that)

File size: 16.2 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Simple telnet server
4 * Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
5 *
6 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
7 *
8 * ---------------------------------------------------------------------------
9 * (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN
10 ****************************************************************************
11 *
12 * The telnetd manpage says it all:
13 *
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 *
20 * Vladimir Oleynik <dzo@simtreas.ru> 2001
21 * Set process group corrections, initial busybox port
22 */
23
24/*#define DEBUG 1 */
25#undef DEBUG
26
27#include <sys/socket.h>
28#include <sys/wait.h>
29#include <sys/ioctl.h>
30#include <string.h>
31#include <stdlib.h>
32#include <unistd.h>
33#include <errno.h>
34#include <netinet/in.h>
35#include <arpa/inet.h>
36#include <fcntl.h>
37#include <stdio.h>
38#include <signal.h>
39#include <termios.h>
40#ifdef DEBUG
41#define TELCMDS
42#define TELOPTS
43#endif
44#include <arpa/telnet.h>
45#include <ctype.h>
46#include <sys/syslog.h>
47
48#include "busybox.h"
49
50#define BUFSIZE 4000
51
52#ifdef CONFIG_FEATURE_IPV6
53#define SOCKET_TYPE AF_INET6
54typedef struct sockaddr_in6 sockaddr_type;
55#else
56#define SOCKET_TYPE AF_INET
57typedef struct sockaddr_in sockaddr_type;
58#endif
59
60
61#ifdef CONFIG_LOGIN
62static const char *loginpath = "/bin/login";
63#else
64static const char *loginpath;
65#endif
66static const char *issuefile = "/etc/issue.net";
67
68/* shell name and arguments */
69
70static const char *argv_init[] = {NULL, NULL};
71
72/* structure that describes a session */
73
74struct tsession {
75#ifdef CONFIG_FEATURE_TELNETD_INETD
76 int sockfd_read, sockfd_write, ptyfd;
77#else /* CONFIG_FEATURE_TELNETD_INETD */
78 struct tsession *next;
79 int sockfd, ptyfd;
80#endif /* CONFIG_FEATURE_TELNETD_INETD */
81 int shell_pid;
82 /* two circular buffers */
83 char *buf1, *buf2;
84 int rdidx1, wridx1, size1;
85 int rdidx2, wridx2, size2;
86};
87
88/*
89
90 This is how the buffers are used. The arrows indicate the movement
91 of data.
92
93 +-------+ wridx1++ +------+ rdidx1++ +----------+
94 | | <-------------- | buf1 | <-------------- | |
95 | | size1-- +------+ size1++ | |
96 | pty | | socket |
97 | | rdidx2++ +------+ wridx2++ | |
98 | | --------------> | buf2 | --------------> | |
99 +-------+ size2++ +------+ size2-- +----------+
100
101 Each session has got two buffers.
102
103*/
104
105static int maxfd;
106
107static struct tsession *sessions;
108
109
110/*
111
112 Remove all IAC's from the buffer pointed to by bf (received IACs are ignored
113 and must be removed so as to not be interpreted by the terminal). Make an
114 uninterrupted string of characters fit for the terminal. Do this by packing
115 all characters meant for the terminal sequentially towards the end of bf.
116
117 Return a pointer to the beginning of the characters meant for the terminal.
118 and make *num_totty the number of characters that should be sent to
119 the terminal.
120
121 Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
122 past (bf + len) then that IAC will be left unprocessed and *processed will be
123 less than len.
124
125 FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
126 what is the escape character? We aren't handling that situation here.
127
128 CR-LF ->'s CR mapping is also done here, for convenience
129
130 */
131static char *
132remove_iacs(struct tsession *ts, int *pnum_totty) {
133 unsigned char *ptr0 = (unsigned char *)ts->buf1 + ts->wridx1;
134 unsigned char *ptr = ptr0;
135 unsigned char *totty = ptr;
136 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
137 int processed;
138 int num_totty;
139
140 while (ptr < end) {
141 if (*ptr != IAC) {
142 int c = *ptr;
143 *totty++ = *ptr++;
144 /* We now map \r\n ==> \r for pragmatic reasons.
145 * Many client implementations send \r\n when
146 * the user hits the CarriageReturn key.
147 */
148 if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end)
149 ptr++;
150 }
151 else {
152 /*
153 * TELOPT_NAWS support!
154 */
155 if ((ptr+2) >= end) {
156 /* only the beginning of the IAC is in the
157 buffer we were asked to process, we can't
158 process this char. */
159 break;
160 }
161
162 /*
163 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
164 */
165 else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
166 struct winsize ws;
167 if ((ptr+8) >= end)
168 break; /* incomplete, can't process */
169 ws.ws_col = (ptr[3] << 8) | ptr[4];
170 ws.ws_row = (ptr[5] << 8) | ptr[6];
171 (void) ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
172 ptr += 9;
173 }
174 else {
175 /* skip 3-byte IAC non-SB cmd */
176#ifdef DEBUG
177 fprintf(stderr, "Ignoring IAC %s,%s\n",
178 TELCMD(*(ptr+1)), TELOPT(*(ptr+2)));
179#endif
180 ptr += 3;
181 }
182 }
183 }
184
185 processed = ptr - ptr0;
186 num_totty = totty - ptr0;
187 /* the difference between processed and num_to tty
188 is all the iacs we removed from the stream.
189 Adjust buf1 accordingly. */
190 ts->wridx1 += processed - num_totty;
191 ts->size1 -= processed - num_totty;
192 *pnum_totty = num_totty;
193 /* move the chars meant for the terminal towards the end of the
194 buffer. */
195 return memmove(ptr - num_totty, ptr0, num_totty);
196}
197
198
199static int
200getpty(char *line)
201{
202 int p;
203#ifdef CONFIG_FEATURE_DEVPTS
204 p = open("/dev/ptmx", 2);
205 if (p > 0) {
206 grantpt(p);
207 unlockpt(p);
208 strcpy(line, ptsname(p));
209 return(p);
210 }
211#else
212 struct stat stb;
213 int i;
214 int j;
215
216 strcpy(line, "/dev/ptyXX");
217
218 for (i = 0; i < 16; i++) {
219 line[8] = "pqrstuvwxyzabcde"[i];
220 line[9] = '0';
221 if (stat(line, &stb) < 0) {
222 continue;
223 }
224 for (j = 0; j < 16; j++) {
225 line[9] = j < 10 ? j + '0' : j - 10 + 'a';
226#ifdef DEBUG
227 fprintf(stderr, "Trying to open device: %s\n", line);
228#endif
229 if ((p = open(line, O_RDWR | O_NOCTTY)) >= 0) {
230 line[5] = 't';
231 return p;
232 }
233 }
234 }
235#endif /* CONFIG_FEATURE_DEVPTS */
236 return -1;
237}
238
239
240static void
241send_iac(struct tsession *ts, unsigned char command, int option)
242{
243 /* We rely on that there is space in the buffer for now. */
244 char *b = ts->buf2 + ts->rdidx2;
245 *b++ = IAC;
246 *b++ = command;
247 *b++ = option;
248 ts->rdidx2 += 3;
249 ts->size2 += 3;
250}
251
252
253static struct tsession *
254#ifdef CONFIG_FEATURE_TELNETD_INETD
255make_new_session(void)
256#else /* CONFIG_FEATURE_TELNETD_INETD */
257make_new_session(int sockfd)
258#endif /* CONFIG_FEATURE_TELNETD_INETD */
259{
260 struct termios termbuf;
261 int pty, pid;
262 char tty_name[32];
263 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
264
265 ts->buf1 = (char *)(&ts[1]);
266 ts->buf2 = ts->buf1 + BUFSIZE;
267
268#ifdef CONFIG_FEATURE_TELNETD_INETD
269 ts->sockfd_write = 1;
270#else /* CONFIG_FEATURE_TELNETD_INETD */
271 ts->sockfd = sockfd;
272#endif /* CONFIG_FEATURE_TELNETD_INETD */
273
274 /* Got a new connection, set up a tty and spawn a shell. */
275
276 pty = getpty(tty_name);
277
278 if (pty < 0) {
279 syslog(LOG_ERR, "All terminals in use!");
280 return 0;
281 }
282
283 if (pty > maxfd)
284 maxfd = pty;
285
286 ts->ptyfd = pty;
287
288 /* Make the telnet client understand we will echo characters so it
289 * should not do it locally. We don't tell the client to run linemode,
290 * because we want to handle line editing and tab completion and other
291 * stuff that requires char-by-char support.
292 */
293
294 send_iac(ts, DO, TELOPT_ECHO);
295 send_iac(ts, DO, TELOPT_NAWS);
296 send_iac(ts, DO, TELOPT_LFLOW);
297 send_iac(ts, WILL, TELOPT_ECHO);
298 send_iac(ts, WILL, TELOPT_SGA);
299
300 if ((pid = fork()) < 0) {
301 syslog(LOG_ERR, "Could not fork");
302 }
303 if (pid == 0) {
304 /* In child, open the child's side of the tty. */
305 int i;
306
307 for(i = 0; i <= maxfd; i++)
308 close(i);
309 /* make new process group */
310 setsid();
311
312 if (open(tty_name, O_RDWR /*| O_NOCTTY*/) < 0) {
313 syslog(LOG_ERR, "Could not open tty");
314 exit(1);
315 }
316 dup(0);
317 dup(0);
318
319 tcsetpgrp(0, getpid());
320
321 /* The pseudo-terminal allocated to the client is configured to operate in
322 * cooked mode, and with XTABS CRMOD enabled (see tty(4)).
323 */
324
325 tcgetattr(0, &termbuf);
326 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
327 termbuf.c_oflag |= ONLCR|XTABS;
328 termbuf.c_iflag |= ICRNL;
329 termbuf.c_iflag &= ~IXOFF;
330 /*termbuf.c_lflag &= ~ICANON;*/
331 tcsetattr(0, TCSANOW, &termbuf);
332
333 print_login_issue(issuefile, NULL);
334
335 /* exec shell, with correct argv and env */
336 execv(loginpath, (char *const *)argv_init);
337
338 /* NOT REACHED */
339 syslog(LOG_ERR, "execv error");
340 exit(1);
341 }
342
343 ts->shell_pid = pid;
344
345 return ts;
346}
347
348#ifndef CONFIG_FEATURE_TELNETD_INETD
349static void
350free_session(struct tsession *ts)
351{
352 struct tsession *t = sessions;
353
354 /* Unlink this telnet session from the session list. */
355 if (t == ts)
356 sessions = ts->next;
357 else {
358 while(t->next != ts)
359 t = t->next;
360 t->next = ts->next;
361 }
362
363 kill(ts->shell_pid, SIGKILL);
364
365 wait4(ts->shell_pid, NULL, 0, NULL);
366
367 close(ts->ptyfd);
368 close(ts->sockfd);
369
370 if (ts->ptyfd == maxfd || ts->sockfd == maxfd)
371 maxfd--;
372 if (ts->ptyfd == maxfd || ts->sockfd == maxfd)
373 maxfd--;
374
375 free(ts);
376}
377#endif /* CONFIG_FEATURE_TELNETD_INETD */
378
379int
380telnetd_main(int argc, char **argv)
381{
382#ifndef CONFIG_FEATURE_TELNETD_INETD
383 sockaddr_type sa;
384 int master_fd;
385#endif /* CONFIG_FEATURE_TELNETD_INETD */
386 fd_set rdfdset, wrfdset;
387 int selret;
388#ifndef CONFIG_FEATURE_TELNETD_INETD
389 int on = 1;
390 int portnbr = 23;
391 struct in_addr bind_addr = { .s_addr = 0x0 };
392#endif /* CONFIG_FEATURE_TELNETD_INETD */
393 int c;
394 static const char options[] =
395#ifdef CONFIG_FEATURE_TELNETD_INETD
396 "f:l:";
397#else /* CONFIG_EATURE_TELNETD_INETD */
398 "f:l:p:b:";
399#endif /* CONFIG_FEATURE_TELNETD_INETD */
400 int maxlen, w, r;
401
402#ifndef CONFIG_LOGIN
403 loginpath = DEFAULT_SHELL;
404#endif
405
406 for (;;) {
407 c = getopt( argc, argv, options);
408 if (c == EOF) break;
409 switch (c) {
410 case 'f':
411 issuefile = optarg;
412 break;
413 case 'l':
414 loginpath = optarg;
415 break;
416#ifndef CONFIG_FEATURE_TELNETD_INETD
417 case 'p':
418 portnbr = atoi(optarg);
419 break;
420 case 'b':
421 if (inet_aton(optarg, &bind_addr) == 0)
422 bb_show_usage();
423 break;
424#endif /* CONFIG_FEATURE_TELNETD_INETD */
425 default:
426 bb_show_usage();
427 }
428 }
429
430 if (access(loginpath, X_OK) < 0) {
431 bb_error_msg_and_die ("'%s' unavailable.", loginpath);
432 }
433
434 argv_init[0] = loginpath;
435
436 openlog(bb_applet_name, 0, LOG_USER);
437
438#ifdef CONFIG_FEATURE_TELNETD_INETD
439 maxfd = 1;
440 sessions = make_new_session();
441#else /* CONFIG_EATURE_TELNETD_INETD */
442 sessions = 0;
443
444 /* Grab a TCP socket. */
445
446 master_fd = bb_xsocket(SOCKET_TYPE, SOCK_STREAM, 0);
447 (void)setsockopt(master_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
448
449 /* Set it to listen to specified port. */
450
451 memset((void *)&sa, 0, sizeof(sa));
452#ifdef CONFIG_FEATURE_IPV6
453 sa.sin6_family = AF_INET6;
454 sa.sin6_port = htons(portnbr);
455 /* sa.sin6_addr = bind_addr6; */
456#else
457 sa.sin_family = AF_INET;
458 sa.sin_port = htons(portnbr);
459 sa.sin_addr = bind_addr;
460#endif
461
462 bb_xbind(master_fd, (struct sockaddr *) &sa, sizeof(sa));
463 bb_xlisten(master_fd, 1);
464 bb_xdaemon(0, 0);
465
466 maxfd = master_fd;
467#endif /* CONFIG_FEATURE_TELNETD_INETD */
468
469 do {
470 struct tsession *ts;
471
472 FD_ZERO(&rdfdset);
473 FD_ZERO(&wrfdset);
474
475 /* select on the master socket, all telnet sockets and their
476 * ptys if there is room in their respective session buffers.
477 */
478
479#ifndef CONFIG_FEATURE_TELNETD_INETD
480 FD_SET(master_fd, &rdfdset);
481#endif /* CONFIG_FEATURE_TELNETD_INETD */
482
483 ts = sessions;
484#ifndef CONFIG_FEATURE_TELNETD_INETD
485 while (ts) {
486#endif /* CONFIG_FEATURE_TELNETD_INETD */
487 /* buf1 is used from socket to pty
488 * buf2 is used from pty to socket
489 */
490 if (ts->size1 > 0) {
491 FD_SET(ts->ptyfd, &wrfdset); /* can write to pty */
492 }
493 if (ts->size1 < BUFSIZE) {
494#ifdef CONFIG_FEATURE_TELNETD_INETD
495 FD_SET(ts->sockfd_read, &rdfdset); /* can read from socket */
496#else /* CONFIG_FEATURE_TELNETD_INETD */
497 FD_SET(ts->sockfd, &rdfdset); /* can read from socket */
498#endif /* CONFIG_FEATURE_TELNETD_INETD */
499 }
500 if (ts->size2 > 0) {
501#ifdef CONFIG_FEATURE_TELNETD_INETD
502 FD_SET(ts->sockfd_write, &wrfdset); /* can write to socket */
503#else /* CONFIG_FEATURE_TELNETD_INETD */
504 FD_SET(ts->sockfd, &wrfdset); /* can write to socket */
505#endif /* CONFIG_FEATURE_TELNETD_INETD */
506 }
507 if (ts->size2 < BUFSIZE) {
508 FD_SET(ts->ptyfd, &rdfdset); /* can read from pty */
509 }
510#ifndef CONFIG_FEATURE_TELNETD_INETD
511 ts = ts->next;
512 }
513#endif /* CONFIG_FEATURE_TELNETD_INETD */
514
515 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
516
517 if (!selret)
518 break;
519
520#ifndef CONFIG_FEATURE_TELNETD_INETD
521 /* First check for and accept new sessions. */
522 if (FD_ISSET(master_fd, &rdfdset)) {
523 int fd;
524 socklen_t salen;
525
526 salen = sizeof(sa);
527 if ((fd = accept(master_fd, (struct sockaddr *)&sa,
528 &salen)) < 0) {
529 continue;
530 } else {
531 /* Create a new session and link it into
532 our active list. */
533 struct tsession *new_ts = make_new_session(fd);
534 if (new_ts) {
535 new_ts->next = sessions;
536 sessions = new_ts;
537 if (fd > maxfd)
538 maxfd = fd;
539 } else {
540 close(fd);
541 }
542 }
543 }
544
545 /* Then check for data tunneling. */
546
547 ts = sessions;
548 while (ts) { /* For all sessions... */
549#endif /* CONFIG_FEATURE_TELNETD_INETD */
550#ifndef CONFIG_FEATURE_TELNETD_INETD
551 struct tsession *next = ts->next; /* in case we free ts. */
552#endif /* CONFIG_FEATURE_TELNETD_INETD */
553
554 if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
555 int num_totty;
556 char *ptr;
557 /* Write to pty from buffer 1. */
558
559 ptr = remove_iacs(ts, &num_totty);
560
561 w = write(ts->ptyfd, ptr, num_totty);
562 if (w < 0) {
563#ifdef CONFIG_FEATURE_TELNETD_INETD
564 exit(0);
565#else /* CONFIG_FEATURE_TELNETD_INETD */
566 free_session(ts);
567 ts = next;
568 continue;
569#endif /* CONFIG_FEATURE_TELNETD_INETD */
570 }
571 ts->wridx1 += w;
572 ts->size1 -= w;
573 if (ts->wridx1 == BUFSIZE)
574 ts->wridx1 = 0;
575 }
576
577#ifdef CONFIG_FEATURE_TELNETD_INETD
578 if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
579#else /* CONFIG_FEATURE_TELNETD_INETD */
580 if (ts->size2 && FD_ISSET(ts->sockfd, &wrfdset)) {
581#endif /* CONFIG_FEATURE_TELNETD_INETD */
582 /* Write to socket from buffer 2. */
583 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
584#ifdef CONFIG_FEATURE_TELNETD_INETD
585 w = write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
586 if (w < 0)
587 exit(0);
588#else /* CONFIG_FEATURE_TELNETD_INETD */
589 w = write(ts->sockfd, ts->buf2 + ts->wridx2, maxlen);
590 if (w < 0) {
591 free_session(ts);
592 ts = next;
593 continue;
594 }
595#endif /* CONFIG_FEATURE_TELNETD_INETD */
596 ts->wridx2 += w;
597 ts->size2 -= w;
598 if (ts->wridx2 == BUFSIZE)
599 ts->wridx2 = 0;
600 }
601
602#ifdef CONFIG_FEATURE_TELNETD_INETD
603 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
604#else /* CONFIG_FEATURE_TELNETD_INETD */
605 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd, &rdfdset)) {
606#endif /* CONFIG_FEATURE_TELNETD_INETD */
607 /* Read from socket to buffer 1. */
608 maxlen = MIN(BUFSIZE - ts->rdidx1,
609 BUFSIZE - ts->size1);
610#ifdef CONFIG_FEATURE_TELNETD_INETD
611 r = read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
612 if (!r || (r < 0 && errno != EINTR))
613 exit(0);
614#else /* CONFIG_FEATURE_TELNETD_INETD */
615 r = read(ts->sockfd, ts->buf1 + ts->rdidx1, maxlen);
616 if (!r || (r < 0 && errno != EINTR)) {
617 free_session(ts);
618 ts = next;
619 continue;
620 }
621#endif /* CONFIG_FEATURE_TELNETD_INETD */
622 if (!*(ts->buf1 + ts->rdidx1 + r - 1)) {
623 r--;
624 if (!r)
625 continue;
626 }
627 ts->rdidx1 += r;
628 ts->size1 += r;
629 if (ts->rdidx1 == BUFSIZE)
630 ts->rdidx1 = 0;
631 }
632
633 if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
634 /* Read from pty to buffer 2. */
635 maxlen = MIN(BUFSIZE - ts->rdidx2,
636 BUFSIZE - ts->size2);
637 r = read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
638 if (!r || (r < 0 && errno != EINTR)) {
639#ifdef CONFIG_FEATURE_TELNETD_INETD
640 exit(0);
641#else /* CONFIG_FEATURE_TELNETD_INETD */
642 free_session(ts);
643 ts = next;
644 continue;
645#endif /* CONFIG_FEATURE_TELNETD_INETD */
646 }
647 ts->rdidx2 += r;
648 ts->size2 += r;
649 if (ts->rdidx2 == BUFSIZE)
650 ts->rdidx2 = 0;
651 }
652
653 if (ts->size1 == 0) {
654 ts->rdidx1 = 0;
655 ts->wridx1 = 0;
656 }
657 if (ts->size2 == 0) {
658 ts->rdidx2 = 0;
659 ts->wridx2 = 0;
660 }
661#ifndef CONFIG_FEATURE_TELNETD_INETD
662 ts = next;
663 }
664#endif /* CONFIG_FEATURE_TELNETD_INETD */
665
666 } while (1);
667
668 return 0;
669}
Note: See TracBrowser for help on using the repository browser.