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