9f0eedcc80e9591edeca4a5e4a07832c713f5f7d
[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 #undef DEBUG
26
27 #include <sys/socket.h>
28 #include <sys/wait.h>
29 #include <sys/ioctl.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
36 #include <fcntl.h>
37 #include <stdio.h>
38 #include <signal.h>
39 #include <termios.h>
40 #ifdef DEBUG
41 #define TELCMDS
42 #define TELOPTS
43 #endif
44 #include <arpa/telnet.h>
45 #include <ctype.h>
46 #include <sys/syslog.h>
47
48 #include "busybox.h"
49
50 #define BUFSIZE 4000
51
52 #ifdef CONFIG_FEATURE_IPV6
53 #define SOCKET_TYPE     AF_INET6
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 (received IACs are ignored
113    and must be removed so as to not be interpreted by the terminal).  Make an
114    uninterrupted string of characters fit for the terminal.  Do this by packing
115    all characters meant for the terminal sequentially towards the end of bf.
116
117    Return a pointer to the beginning of the characters meant for the terminal.
118    and make *num_totty the number of characters that should be sent to
119    the terminal.
120
121    Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
122    past (bf + len) then that IAC will be left unprocessed and *processed will be
123    less than len.
124
125    FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
126    what is the escape character?  We aren't handling that situation here.
127
128    CR-LF ->'s CR mapping is also done here, for convenience
129
130   */
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 #ifdef DEBUG
227                         fprintf(stderr, "Trying to open device: %s\n", line);
228 #endif
229                         if ((p = open(line, O_RDWR | O_NOCTTY)) >= 0) {
230                                 line[5] = 't';
231                                 return p;
232                         }
233                 }
234         }
235 #endif /* CONFIG_FEATURE_DEVPTS */
236         return -1;
237 }
238
239
240 static void
241 send_iac(struct tsession *ts, unsigned char command, int option)
242 {
243         /* We rely on that there is space in the buffer for now.  */
244         char *b = ts->buf2 + ts->rdidx2;
245         *b++ = IAC;
246         *b++ = command;
247         *b++ = option;
248         ts->rdidx2 += 3;
249         ts->size2 += 3;
250 }
251
252
253 static struct tsession *
254 #ifdef CONFIG_FEATURE_TELNETD_INETD
255 make_new_session(void)
256 #else /* CONFIG_FEATURE_TELNETD_INETD */
257 make_new_session(int sockfd)
258 #endif /* CONFIG_FEATURE_TELNETD_INETD */
259 {
260         struct termios termbuf;
261         int pty, pid;
262         char tty_name[32];
263         struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
264
265         ts->buf1 = (char *)(&ts[1]);
266         ts->buf2 = ts->buf1 + BUFSIZE;
267
268 #ifdef CONFIG_FEATURE_TELNETD_INETD
269         ts->sockfd_write = 1;
270 #else /* CONFIG_FEATURE_TELNETD_INETD */
271         ts->sockfd = sockfd;
272 #endif /* CONFIG_FEATURE_TELNETD_INETD */
273
274         /* Got a new connection, set up a tty and spawn a shell.  */
275
276         pty = getpty(tty_name);
277
278         if (pty < 0) {
279                 syslog(LOG_ERR, "All terminals in use!");
280                 return 0;
281         }
282
283         if (pty > maxfd)
284                 maxfd = pty;
285
286         ts->ptyfd = pty;
287
288         /* Make the telnet client understand we will echo characters so it
289          * should not do it locally. We don't tell the client to run linemode,
290          * because we want to handle line editing and tab completion and other
291          * stuff that requires char-by-char support.
292          */
293
294         send_iac(ts, DO, TELOPT_ECHO);
295         send_iac(ts, DO, TELOPT_NAWS);
296         send_iac(ts, DO, TELOPT_LFLOW);
297         send_iac(ts, WILL, TELOPT_ECHO);
298         send_iac(ts, WILL, TELOPT_SGA);
299
300         if ((pid = fork()) < 0) {
301                 syslog(LOG_ERR, "Could not fork");
302         }
303         if (pid == 0) {
304                 /* In child, open the child's side of the tty.  */
305                 int i;
306
307                 for(i = 0; i <= maxfd; i++)
308                         close(i);
309                 /* make new process group */
310                 setsid();
311
312                 if (open(tty_name, O_RDWR /*| O_NOCTTY*/) < 0) {
313                         syslog(LOG_ERR, "Could not open tty");
314                         exit(1);
315                 }
316                 dup(0);
317                 dup(0);
318
319                 tcsetpgrp(0, getpid());
320
321                 /* The pseudo-terminal allocated to the client is configured to operate in
322                  * cooked mode, and with XTABS CRMOD enabled (see tty(4)).
323                  */
324
325                 tcgetattr(0, &termbuf);
326                 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
327                 termbuf.c_oflag |= ONLCR|XTABS;
328                 termbuf.c_iflag |= ICRNL;
329                 termbuf.c_iflag &= ~IXOFF;
330                 /*termbuf.c_lflag &= ~ICANON;*/
331                 tcsetattr(0, TCSANOW, &termbuf);
332
333                 print_login_issue(issuefile, NULL);
334
335                 /* exec shell, with correct argv and env */
336                 execv(loginpath, (char *const *)argv_init);
337
338                 /* NOT REACHED */
339                 syslog(LOG_ERR, "execv error");
340                 exit(1);
341         }
342
343         ts->shell_pid = pid;
344
345         return ts;
346 }
347
348 #ifndef CONFIG_FEATURE_TELNETD_INETD
349 static void
350 free_session(struct tsession *ts)
351 {
352         struct tsession *t = sessions;
353
354         /* Unlink this telnet session from the session list.  */
355         if (t == ts)
356                 sessions = ts->next;
357         else {
358                 while(t->next != ts)
359                         t = t->next;
360                 t->next = ts->next;
361         }
362
363         kill(ts->shell_pid, SIGKILL);
364
365         wait4(ts->shell_pid, NULL, 0, NULL);
366
367         close(ts->ptyfd);
368         close(ts->sockfd);
369
370         if (ts->ptyfd == maxfd || ts->sockfd == maxfd)
371                 maxfd--;
372         if (ts->ptyfd == maxfd || ts->sockfd == maxfd)
373                 maxfd--;
374
375         free(ts);
376 }
377 #endif /* CONFIG_FEATURE_TELNETD_INETD */
378
379 int
380 telnetd_main(int argc, char **argv)
381 {
382 #ifndef CONFIG_FEATURE_TELNETD_INETD
383         sockaddr_type sa;
384         int master_fd;
385 #endif /* CONFIG_FEATURE_TELNETD_INETD */
386         fd_set rdfdset, wrfdset;
387         int selret;
388 #ifndef CONFIG_FEATURE_TELNETD_INETD
389         int on = 1;
390         int portnbr = 23;
391         struct in_addr bind_addr = { .s_addr = 0x0 };
392 #endif /* CONFIG_FEATURE_TELNETD_INETD */
393         int c;
394         static const char options[] =
395 #ifdef CONFIG_FEATURE_TELNETD_INETD
396                 "f:l:";
397 #else /* CONFIG_EATURE_TELNETD_INETD */
398                 "f:l:p:b:";
399 #endif /* CONFIG_FEATURE_TELNETD_INETD */
400         int maxlen, w, r;
401
402 #ifndef CONFIG_LOGIN
403         loginpath = DEFAULT_SHELL;
404 #endif
405
406         for (;;) {
407                 c = getopt( argc, argv, options);
408                 if (c == EOF) break;
409                 switch (c) {
410                         case 'f':
411                                 issuefile = optarg;
412                                 break;
413                         case 'l':
414                                 loginpath = optarg;
415                                 break;
416 #ifndef CONFIG_FEATURE_TELNETD_INETD
417                         case 'p':
418                                 portnbr = atoi(optarg);
419                                 break;
420                         case 'b':
421                                 if (inet_aton(optarg, &bind_addr) == 0)
422                                         bb_show_usage();
423                                 break;
424 #endif /* CONFIG_FEATURE_TELNETD_INETD */
425                         default:
426                                 bb_show_usage();
427                 }
428         }
429
430         if (access(loginpath, X_OK) < 0) {
431                 bb_error_msg_and_die ("'%s' unavailable.", loginpath);
432         }
433
434         argv_init[0] = loginpath;
435
436         openlog(bb_applet_name, 0, LOG_USER);
437
438 #ifdef CONFIG_FEATURE_TELNETD_INETD
439         maxfd = 1;
440         sessions = make_new_session();
441 #else /* CONFIG_EATURE_TELNETD_INETD */
442         sessions = 0;
443
444         /* Grab a TCP socket.  */
445
446         master_fd = bb_xsocket(SOCKET_TYPE, SOCK_STREAM, 0);
447         (void)setsockopt(master_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
448
449         /* Set it to listen to specified port.  */
450
451         memset((void *)&sa, 0, sizeof(sa));
452 #ifdef CONFIG_FEATURE_IPV6
453         sa.sin6_family = AF_INET6;
454         sa.sin6_port = htons(portnbr);
455         /* sa.sin6_addr = bind_addr6; */
456 #else
457         sa.sin_family = AF_INET;
458         sa.sin_port = htons(portnbr);
459         sa.sin_addr = bind_addr;
460 #endif
461
462         bb_xbind(master_fd, (struct sockaddr *) &sa, sizeof(sa));
463         bb_xlisten(master_fd, 1);
464         bb_xdaemon(0, 0);
465
466         maxfd = master_fd;
467 #endif /* CONFIG_FEATURE_TELNETD_INETD */
468
469         do {
470                 struct tsession *ts;
471
472                 FD_ZERO(&rdfdset);
473                 FD_ZERO(&wrfdset);
474
475                 /* select on the master socket, all telnet sockets and their
476                  * ptys if there is room in their respective session buffers.
477                  */
478
479 #ifndef CONFIG_FEATURE_TELNETD_INETD
480                 FD_SET(master_fd, &rdfdset);
481 #endif /* CONFIG_FEATURE_TELNETD_INETD */
482
483                 ts = sessions;
484 #ifndef CONFIG_FEATURE_TELNETD_INETD
485                 while (ts) {
486 #endif /* CONFIG_FEATURE_TELNETD_INETD */
487                         /* buf1 is used from socket to pty
488                          * buf2 is used from pty to socket
489                          */
490                         if (ts->size1 > 0) {
491                                 FD_SET(ts->ptyfd, &wrfdset);  /* can write to pty */
492                         }
493                         if (ts->size1 < BUFSIZE) {
494 #ifdef CONFIG_FEATURE_TELNETD_INETD
495                                 FD_SET(ts->sockfd_read, &rdfdset); /* can read from socket */
496 #else /* CONFIG_FEATURE_TELNETD_INETD */
497                                 FD_SET(ts->sockfd, &rdfdset); /* can read from socket */
498 #endif /* CONFIG_FEATURE_TELNETD_INETD */
499                         }
500                         if (ts->size2 > 0) {
501 #ifdef CONFIG_FEATURE_TELNETD_INETD
502                                 FD_SET(ts->sockfd_write, &wrfdset); /* can write to socket */
503 #else /* CONFIG_FEATURE_TELNETD_INETD */
504                                 FD_SET(ts->sockfd, &wrfdset); /* can write to socket */
505 #endif /* CONFIG_FEATURE_TELNETD_INETD */
506                         }
507                         if (ts->size2 < BUFSIZE) {
508                                 FD_SET(ts->ptyfd, &rdfdset);  /* can read from pty */
509                         }
510 #ifndef CONFIG_FEATURE_TELNETD_INETD
511                         ts = ts->next;
512                 }
513 #endif /* CONFIG_FEATURE_TELNETD_INETD */
514
515                 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
516
517                 if (!selret)
518                         break;
519
520 #ifndef CONFIG_FEATURE_TELNETD_INETD
521                 /* First check for and accept new sessions.  */
522                 if (FD_ISSET(master_fd, &rdfdset)) {
523                         int fd;
524                         socklen_t salen;
525
526                         salen = sizeof(sa);
527                         if ((fd = accept(master_fd, (struct sockaddr *)&sa,
528                                                 &salen)) < 0) {
529                                 continue;
530                         } else {
531                                 /* Create a new session and link it into
532                                         our active list.  */
533                                 struct tsession *new_ts = make_new_session(fd);
534                                 if (new_ts) {
535                                         new_ts->next = sessions;
536                                         sessions = new_ts;
537                                         if (fd > maxfd)
538                                                 maxfd = fd;
539                                 } else {
540                                         close(fd);
541                                 }
542                         }
543                 }
544
545                 /* Then check for data tunneling.  */
546
547                 ts = sessions;
548                 while (ts) { /* For all sessions...  */
549 #endif /* CONFIG_FEATURE_TELNETD_INETD */
550 #ifndef CONFIG_FEATURE_TELNETD_INETD
551                         struct tsession *next = ts->next; /* in case we free ts. */
552 #endif /* CONFIG_FEATURE_TELNETD_INETD */
553
554                         if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
555                                 int num_totty;
556                                 char *ptr;
557                                 /* Write to pty from buffer 1.  */
558
559                                 ptr = remove_iacs(ts, &num_totty);
560
561                                 w = write(ts->ptyfd, ptr, num_totty);
562                                 if (w < 0) {
563 #ifdef CONFIG_FEATURE_TELNETD_INETD
564                                         exit(0);
565 #else /* CONFIG_FEATURE_TELNETD_INETD */
566                                         free_session(ts);
567                                         ts = next;
568                                         continue;
569 #endif /* CONFIG_FEATURE_TELNETD_INETD */
570                                 }
571                                 ts->wridx1 += w;
572                                 ts->size1 -= w;
573                                 if (ts->wridx1 == BUFSIZE)
574                                         ts->wridx1 = 0;
575                         }
576
577 #ifdef CONFIG_FEATURE_TELNETD_INETD
578                         if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
579 #else /* CONFIG_FEATURE_TELNETD_INETD */
580                         if (ts->size2 && FD_ISSET(ts->sockfd, &wrfdset)) {
581 #endif /* CONFIG_FEATURE_TELNETD_INETD */
582                                 /* Write to socket from buffer 2.  */
583                                 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
584 #ifdef CONFIG_FEATURE_TELNETD_INETD
585                                 w = write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
586                                 if (w < 0)
587                                         exit(0);
588 #else /* CONFIG_FEATURE_TELNETD_INETD */
589                                 w = write(ts->sockfd, ts->buf2 + ts->wridx2, maxlen);
590                                 if (w < 0) {
591                                         free_session(ts);
592                                         ts = next;
593                                         continue;
594                                 }
595 #endif /* CONFIG_FEATURE_TELNETD_INETD */
596                                 ts->wridx2 += w;
597                                 ts->size2 -= w;
598                                 if (ts->wridx2 == BUFSIZE)
599                                         ts->wridx2 = 0;
600                         }
601
602 #ifdef CONFIG_FEATURE_TELNETD_INETD
603                         if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
604 #else /* CONFIG_FEATURE_TELNETD_INETD */
605                         if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd, &rdfdset)) {
606 #endif /* CONFIG_FEATURE_TELNETD_INETD */
607                                 /* Read from socket to buffer 1. */
608                                 maxlen = MIN(BUFSIZE - ts->rdidx1,
609                                                 BUFSIZE - ts->size1);
610 #ifdef CONFIG_FEATURE_TELNETD_INETD
611                                 r = read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
612                                 if (!r || (r < 0 && errno != EINTR))
613                                         exit(0);
614 #else /* CONFIG_FEATURE_TELNETD_INETD */
615                                 r = read(ts->sockfd, ts->buf1 + ts->rdidx1, maxlen);
616                                 if (!r || (r < 0 && errno != EINTR)) {
617                                         free_session(ts);
618                                         ts = next;
619                                         continue;
620                                 }
621 #endif /* CONFIG_FEATURE_TELNETD_INETD */
622                                 if (!*(ts->buf1 + ts->rdidx1 + r - 1)) {
623                                         r--;
624                                         if (!r)
625                                                 continue;
626                                 }
627                                 ts->rdidx1 += r;
628                                 ts->size1 += r;
629                                 if (ts->rdidx1 == BUFSIZE)
630                                         ts->rdidx1 = 0;
631                         }
632
633                         if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
634                                 /* Read from pty to buffer 2.  */
635                                 maxlen = MIN(BUFSIZE - ts->rdidx2,
636                                                 BUFSIZE - ts->size2);
637                                 r = read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
638                                 if (!r || (r < 0 && errno != EINTR)) {
639 #ifdef CONFIG_FEATURE_TELNETD_INETD
640                                         exit(0);
641 #else /* CONFIG_FEATURE_TELNETD_INETD */
642                                         free_session(ts);
643                                         ts = next;
644                                         continue;
645 #endif /* CONFIG_FEATURE_TELNETD_INETD */
646                                 }
647                                 ts->rdidx2 += r;
648                                 ts->size2 += r;
649                                 if (ts->rdidx2 == BUFSIZE)
650                                         ts->rdidx2 = 0;
651                         }
652
653                         if (ts->size1 == 0) {
654                                 ts->rdidx1 = 0;
655                                 ts->wridx1 = 0;
656                         }
657                         if (ts->size2 == 0) {
658                                 ts->rdidx2 = 0;
659                                 ts->wridx2 = 0;
660                         }
661 #ifndef CONFIG_FEATURE_TELNETD_INETD
662                         ts = next;
663                 }
664 #endif /* CONFIG_FEATURE_TELNETD_INETD */
665
666         } while (1);
667
668         return 0;
669 }