3e4b42cfaad9d0e8af1754c28da7506b9c5cbbe5
[oweals/busybox.git] / networking / telnetd.c
1 /* vi:set ts=4:*/
2 /*
3  * Simple telnet server
4  * Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
5  *
6  * Licensed under GPL, 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
26 #include <sys/time.h>
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
54 typedef struct sockaddr_in6 sockaddr_type;
55 #else
56 #define SOCKET_TYPE     AF_INET
57 typedef struct sockaddr_in sockaddr_type;
58 #endif
59
60
61 #ifdef CONFIG_LOGIN
62 static const char *loginpath = "/bin/login";
63 #else
64 static const char *loginpath;
65 #endif
66 static const char *issuefile = "/etc/issue.net";
67
68 /* shell name and arguments */
69
70 static const char *argv_init[] = {NULL, NULL};
71
72 /* structure that describes a session */
73
74 struct 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
105 static int maxfd;
106
107 static struct tsession *sessions;
108
109
110 /*
111
112    Remove all IAC's from the buffer pointed to by bf (recieved 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   */
131 static char *
132 remove_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
199 static int
200 getpty(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                         if ((p = open(line, O_RDWR | O_NOCTTY)) >= 0) {
227                                 line[5] = 't';
228                                 return p;
229                         }
230                 }
231         }
232 #endif /* CONFIG_FEATURE_DEVPTS */
233         return -1;
234 }
235
236
237 static void
238 send_iac(struct tsession *ts, unsigned char command, int option)
239 {
240         /* We rely on that there is space in the buffer for now.  */
241         char *b = ts->buf2 + ts->rdidx2;
242         *b++ = IAC;
243         *b++ = command;
244         *b++ = option;
245         ts->rdidx2 += 3;
246         ts->size2 += 3;
247 }
248
249
250 static struct tsession *
251 #ifdef CONFIG_FEATURE_TELNETD_INETD
252 make_new_session(void)
253 #else /* CONFIG_FEATURE_TELNETD_INETD */
254 make_new_session(int sockfd)
255 #endif /* CONFIG_FEATURE_TELNETD_INETD */
256 {
257         struct termios termbuf;
258         int pty, pid;
259         char tty_name[32];
260         struct tsession *ts = malloc(sizeof(struct tsession) + BUFSIZE * 2);
261
262         ts->buf1 = (char *)(&ts[1]);
263         ts->buf2 = ts->buf1 + BUFSIZE;
264
265 #ifdef CONFIG_FEATURE_TELNETD_INETD
266         ts->sockfd_read = 0;
267         ts->sockfd_write = 1;
268 #else /* CONFIG_FEATURE_TELNETD_INETD */
269         ts->sockfd = sockfd;
270 #endif /* CONFIG_FEATURE_TELNETD_INETD */
271
272         ts->rdidx1 = ts->wridx1 = ts->size1 = 0;
273         ts->rdidx2 = ts->wridx2 = ts->size2 = 0;
274
275         /* Got a new connection, set up a tty and spawn a shell.  */
276
277         pty = getpty(tty_name);
278
279         if (pty < 0) {
280                 syslog(LOG_ERR, "All network ports in use!");
281                 return 0;
282         }
283
284         if (pty > maxfd)
285                 maxfd = pty;
286
287         ts->ptyfd = pty;
288
289         /* Make the telnet client understand we will echo characters so it
290          * should not do it locally. We don't tell the client to run linemode,
291          * because we want to handle line editing and tab completion and other
292          * stuff that requires char-by-char support.
293          */
294
295         send_iac(ts, DO, TELOPT_ECHO);
296         send_iac(ts, DO, TELOPT_NAWS);
297         send_iac(ts, DO, TELOPT_LFLOW);
298         send_iac(ts, WILL, TELOPT_ECHO);
299         send_iac(ts, WILL, TELOPT_SGA);
300
301
302         if ((pid = fork()) < 0) {
303                 syslog(LOG_ERR, "Can`t forking");
304         }
305         if (pid == 0) {
306                 /* In child, open the child's side of the tty.  */
307                 int i;
308
309                 for(i = 0; i <= maxfd; i++)
310                         close(i);
311                 /* make new process group */
312                 setsid();
313
314                 if (open(tty_name, O_RDWR /*| O_NOCTTY*/) < 0) {
315                         syslog(LOG_ERR, "Could not open tty");
316                         exit(1);
317                         }
318                 dup(0);
319                 dup(0);
320
321                 tcsetpgrp(0, getpid());
322
323                 /* The pseudo-terminal allocated to the client is configured to operate in
324                  * cooked mode, and with XTABS CRMOD enabled (see tty(4)).
325                  */
326
327                 tcgetattr(0, &termbuf);
328                 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
329                 termbuf.c_oflag |= ONLCR|XTABS;
330                 termbuf.c_iflag |= ICRNL;
331                 termbuf.c_iflag &= ~IXOFF;
332                 /*termbuf.c_lflag &= ~ICANON;*/
333                 tcsetattr(0, TCSANOW, &termbuf);
334
335                 print_login_issue(issuefile, NULL);
336
337                 /* exec shell, with correct argv and env */
338                 execv(loginpath, (char *const *)argv_init);
339
340                 /* NOT REACHED */
341                 syslog(LOG_ERR, "execv error");
342                 exit(1);
343         }
344
345         ts->shell_pid = pid;
346
347         return ts;
348 }
349
350 #ifndef CONFIG_FEATURE_TELNETD_INETD
351 static void
352 free_session(struct tsession *ts)
353 {
354         struct tsession *t = sessions;
355
356         /* Unlink this telnet session from the session list.  */
357         if(t == ts)
358                 sessions = ts->next;
359         else {
360                 while(t->next != ts)
361                         t = t->next;
362                 t->next = ts->next;
363         }
364
365         kill(ts->shell_pid, SIGKILL);
366
367         wait4(ts->shell_pid, NULL, 0, NULL);
368
369         close(ts->ptyfd);
370         close(ts->sockfd);
371
372         if(ts->ptyfd == maxfd || ts->sockfd == maxfd)
373                 maxfd--;
374         if(ts->ptyfd == maxfd || ts->sockfd == maxfd)
375                 maxfd--;
376
377         free(ts);
378 }
379 #endif /* CONFIG_FEATURE_TELNETD_INETD */
380
381 int
382 telnetd_main(int argc, char **argv)
383 {
384 #ifndef CONFIG_FEATURE_TELNETD_INETD
385         sockaddr_type sa;
386         int master_fd;
387 #endif /* CONFIG_FEATURE_TELNETD_INETD */
388         fd_set rdfdset, wrfdset;
389         int selret;
390 #ifndef CONFIG_FEATURE_TELNETD_INETD
391         int on = 1;
392         int portnbr = 23;
393         struct in_addr bind_addr = { .s_addr = 0x0 };
394 #endif /* CONFIG_FEATURE_TELNETD_INETD */
395         int c;
396         static const char options[] =
397 #ifdef CONFIG_FEATURE_TELNETD_INETD
398                 "f:l:";
399 #else /* CONFIG_EATURE_TELNETD_INETD */
400                 "f:l:p:b:";
401 #endif /* CONFIG_FEATURE_TELNETD_INETD */
402         int maxlen, w, r;
403
404 #ifndef CONFIG_LOGIN
405         loginpath = DEFAULT_SHELL;
406 #endif
407
408         for (;;) {
409                 c = getopt( argc, argv, options);
410                 if (c == EOF) break;
411                 switch (c) {
412                         case 'f':
413                                 issuefile = optarg;
414                                 break;
415                         case 'l':
416                                 loginpath = optarg;
417                                 break;
418 #ifndef CONFIG_FEATURE_TELNETD_INETD
419                         case 'p':
420                                 portnbr = atoi(optarg);
421                                 break;
422                         case 'b':
423                                 if (inet_aton(optarg, &bind_addr) == 0)
424                                         bb_show_usage();
425                                 break;
426 #endif /* CONFIG_FEATURE_TELNETD_INETD */
427                         default:
428                                 bb_show_usage();
429                 }
430         }
431
432         if (access(loginpath, X_OK) < 0) {
433                 bb_error_msg_and_die ("'%s' unavailable.", loginpath);
434         }
435
436         argv_init[0] = loginpath;
437
438         openlog(bb_applet_name, 0, LOG_USER);
439
440 #ifdef CONFIG_FEATURE_TELNETD_INETD
441         maxfd = 1;
442         sessions = make_new_session();
443 #else /* CONFIG_EATURE_TELNETD_INETD */
444         sessions = 0;
445
446         /* Grab a TCP socket.  */
447
448         master_fd = bb_xsocket(SOCKET_TYPE, SOCK_STREAM, 0);
449         (void)setsockopt(master_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
450
451         /* Set it to listen to specified port.  */
452
453         memset((void *)&sa, 0, sizeof(sa));
454 #ifdef CONFIG_FEATURE_IPV6
455         sa.sin6_family = AF_INET6;
456         sa.sin6_port = htons(portnbr);
457         /* sa.sin6_addr = bind_addr6; */
458 #else
459         sa.sin_family = AF_INET;
460         sa.sin_port = htons(portnbr);
461         sa.sin_addr = bind_addr;
462 #endif
463
464         if (bind(master_fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
465                 bb_perror_msg_and_die("bind");
466         }
467
468         if (listen(master_fd, 1) < 0) {
469                 bb_perror_msg_and_die("listen");
470         }
471
472         if (daemon(0, 0) < 0)
473                 bb_perror_msg_and_die("daemon");
474
475
476         maxfd = master_fd;
477 #endif /* CONFIG_FEATURE_TELNETD_INETD */
478
479         do {
480                 struct tsession *ts;
481
482                 FD_ZERO(&rdfdset);
483                 FD_ZERO(&wrfdset);
484
485                 /* select on the master socket, all telnet sockets and their
486                  * ptys if there is room in their respective session buffers.
487                  */
488
489 #ifndef CONFIG_FEATURE_TELNETD_INETD
490                 FD_SET(master_fd, &rdfdset);
491 #endif /* CONFIG_FEATURE_TELNETD_INETD */
492
493                 ts = sessions;
494 #ifndef CONFIG_FEATURE_TELNETD_INETD
495                 while (ts) {
496 #endif /* CONFIG_FEATURE_TELNETD_INETD */
497                         /* buf1 is used from socket to pty
498                          * buf2 is used from pty to socket
499                          */
500                         if (ts->size1 > 0) {
501                                 FD_SET(ts->ptyfd, &wrfdset);  /* can write to pty */
502                         }
503                         if (ts->size1 < BUFSIZE) {
504 #ifdef CONFIG_FEATURE_TELNETD_INETD
505                                 FD_SET(ts->sockfd_read, &rdfdset); /* can read from socket */
506 #else /* CONFIG_FEATURE_TELNETD_INETD */
507                                 FD_SET(ts->sockfd, &rdfdset); /* can read from socket */
508 #endif /* CONFIG_FEATURE_TELNETD_INETD */
509                         }
510                         if (ts->size2 > 0) {
511 #ifdef CONFIG_FEATURE_TELNETD_INETD
512                                 FD_SET(ts->sockfd_write, &wrfdset); /* can write to socket */
513 #else /* CONFIG_FEATURE_TELNETD_INETD */
514                                 FD_SET(ts->sockfd, &wrfdset); /* can write to socket */
515 #endif /* CONFIG_FEATURE_TELNETD_INETD */
516                         }
517                         if (ts->size2 < BUFSIZE) {
518                                 FD_SET(ts->ptyfd, &rdfdset);  /* can read from pty */
519                         }
520 #ifndef CONFIG_FEATURE_TELNETD_INETD
521                         ts = ts->next;
522                 }
523 #endif /* CONFIG_FEATURE_TELNETD_INETD */
524
525                 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
526
527                 if (!selret)
528                         break;
529
530 #ifndef CONFIG_FEATURE_TELNETD_INETD
531                 /* First check for and accept new sessions.  */
532                 if (FD_ISSET(master_fd, &rdfdset)) {
533                         int fd, salen;
534
535                         salen = sizeof(sa);
536                         if ((fd = accept(master_fd, (struct sockaddr *)&sa,
537                                                 &salen)) < 0) {
538                                 continue;
539                         } else {
540                                 /* Create a new session and link it into
541                                         our active list.  */
542                                 struct tsession *new_ts = make_new_session(fd);
543                                 if (new_ts) {
544                                         new_ts->next = sessions;
545                                         sessions = new_ts;
546                                         if (fd > maxfd)
547                                                 maxfd = fd;
548                                 } else {
549                                         close(fd);
550                                 }
551                         }
552                 }
553
554                 /* Then check for data tunneling.  */
555
556                 ts = sessions;
557                 while (ts) { /* For all sessions...  */
558 #endif /* CONFIG_FEATURE_TELNETD_INETD */
559 #ifndef CONFIG_FEATURE_TELNETD_INETD
560                         struct tsession *next = ts->next; /* in case we free ts. */
561 #endif /* CONFIG_FEATURE_TELNETD_INETD */
562
563                         if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
564                                 int num_totty;
565                                 char *ptr;
566                                 /* Write to pty from buffer 1.  */
567
568                                 ptr = remove_iacs(ts, &num_totty);
569
570                                 w = write(ts->ptyfd, ptr, num_totty);
571                                 if (w < 0) {
572 #ifdef CONFIG_FEATURE_TELNETD_INETD
573                                         exit(0);
574 #else /* CONFIG_FEATURE_TELNETD_INETD */
575                                         free_session(ts);
576                                         ts = next;
577                                         continue;
578 #endif /* CONFIG_FEATURE_TELNETD_INETD */
579                                 }
580                                 ts->wridx1 += w;
581                                 ts->size1 -= w;
582                                 if (ts->wridx1 == BUFSIZE)
583                                         ts->wridx1 = 0;
584                         }
585
586 #ifdef CONFIG_FEATURE_TELNETD_INETD
587                         if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
588 #else /* CONFIG_FEATURE_TELNETD_INETD */
589                         if (ts->size2 && FD_ISSET(ts->sockfd, &wrfdset)) {
590 #endif /* CONFIG_FEATURE_TELNETD_INETD */
591                                 /* Write to socket from buffer 2.  */
592                                 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
593 #ifdef CONFIG_FEATURE_TELNETD_INETD
594                                 w = write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
595                                 if (w < 0)
596                                         exit(0);
597 #else /* CONFIG_FEATURE_TELNETD_INETD */
598                                 w = write(ts->sockfd, ts->buf2 + ts->wridx2, maxlen);
599                                 if (w < 0) {
600                                         free_session(ts);
601                                         ts = next;
602                                         continue;
603                                 }
604 #endif /* CONFIG_FEATURE_TELNETD_INETD */
605                                 ts->wridx2 += w;
606                                 ts->size2 -= w;
607                                 if (ts->wridx2 == BUFSIZE)
608                                         ts->wridx2 = 0;
609                         }
610
611 #ifdef CONFIG_FEATURE_TELNETD_INETD
612                         if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
613 #else /* CONFIG_FEATURE_TELNETD_INETD */
614                         if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd, &rdfdset)) {
615 #endif /* CONFIG_FEATURE_TELNETD_INETD */
616                                 /* Read from socket to buffer 1. */
617                                 maxlen = MIN(BUFSIZE - ts->rdidx1,
618                                                 BUFSIZE - ts->size1);
619 #ifdef CONFIG_FEATURE_TELNETD_INETD
620                                 r = read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
621                                 if (!r || (r < 0 && errno != EINTR))
622                                         exit(0);
623 #else /* CONFIG_FEATURE_TELNETD_INETD */
624                                 r = read(ts->sockfd, ts->buf1 + ts->rdidx1, maxlen);
625                                 if (!r || (r < 0 && errno != EINTR)) {
626                                         free_session(ts);
627                                         ts = next;
628                                         continue;
629                                 }
630 #endif /* CONFIG_FEATURE_TELNETD_INETD */
631                                 if(!*(ts->buf1 + ts->rdidx1 + r - 1)) {
632                                         r--;
633                                         if(!r)
634                                                 continue;
635                                 }
636                                 ts->rdidx1 += r;
637                                 ts->size1 += r;
638                                 if (ts->rdidx1 == BUFSIZE)
639                                         ts->rdidx1 = 0;
640                         }
641
642                         if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
643                                 /* Read from pty to buffer 2.  */
644                                 maxlen = MIN(BUFSIZE - ts->rdidx2,
645                                                 BUFSIZE - ts->size2);
646                                 r = read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
647                                 if (!r || (r < 0 && errno != EINTR)) {
648 #ifdef CONFIG_FEATURE_TELNETD_INETD
649                                         exit(0);
650 #else /* CONFIG_FEATURE_TELNETD_INETD */
651                                         free_session(ts);
652                                         ts = next;
653                                         continue;
654 #endif /* CONFIG_FEATURE_TELNETD_INETD */
655                                 }
656                                 ts->rdidx2 += r;
657                                 ts->size2 += r;
658                                 if (ts->rdidx2 == BUFSIZE)
659                                         ts->rdidx2 = 0;
660                         }
661
662                         if (ts->size1 == 0) {
663                                 ts->rdidx1 = 0;
664                                 ts->wridx1 = 0;
665                         }
666                         if (ts->size2 == 0) {
667                                 ts->rdidx2 = 0;
668                                 ts->wridx2 = 0;
669                         }
670 #ifndef CONFIG_FEATURE_TELNETD_INETD
671                         ts = next;
672                 }
673 #endif /* CONFIG_FEATURE_TELNETD_INETD */
674
675         } while (1);
676
677         return 0;
678 }