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 | }
|
---|