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