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