diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-10-15 15:19:36 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-10-15 15:19:36 +0000 |
commit | 59d7c43dbe502ef7425f6828bb43528c5adfc9a9 (patch) | |
tree | 0b9a9cb8fd39f35fa361aaa380f3024385faa5e3 /networking/telnetd.c | |
parent | d898b8600cd64310dfb60e4ffb236b64a00d1cc2 (diff) | |
download | busybox-w32-59d7c43dbe502ef7425f6828bb43528c5adfc9a9.tar.gz busybox-w32-59d7c43dbe502ef7425f6828bb43528c5adfc9a9.tar.bz2 busybox-w32-59d7c43dbe502ef7425f6828bb43528c5adfc9a9.zip |
telnetd: some simplifications and better error hadling.
telnetd: don't SIGKILL child when closing the session.
kernel will seng SIGHUP for us.
static.iacs_to_send - 15 +15
.rodata 123418 123429 +11
make_new_session 549 525 -24
send_iac 26 - -26
free_session 144 118 -26
telnetd_main 1303 1261 -42
------------------------------------------------------------------------------
(add/remove: 1/1 grow/shrink: 1/3 up/down: 26/-118) Total: -92 bytes
text data bss dec hex filename
676341 2538 12104 690983 a8b27 busybox_old
676234 2538 12104 690876 a8abc busybox_unstripped
Diffstat (limited to 'networking/telnetd.c')
-rw-r--r-- | networking/telnetd.c | 270 |
1 files changed, 150 insertions, 120 deletions
diff --git a/networking/telnetd.c b/networking/telnetd.c index 27dde1ae4..9ce793fb7 100644 --- a/networking/telnetd.c +++ b/networking/telnetd.c | |||
@@ -33,63 +33,51 @@ | |||
33 | #include <sys/syslog.h> | 33 | #include <sys/syslog.h> |
34 | 34 | ||
35 | 35 | ||
36 | #if ENABLE_LOGIN | 36 | /* Structure that describes a session */ |
37 | static const char *loginpath = "/bin/login"; | ||
38 | #else | ||
39 | static const char *loginpath = DEFAULT_SHELL; | ||
40 | #endif | ||
41 | |||
42 | static const char *issuefile = "/etc/issue.net"; | ||
43 | |||
44 | /* structure that describes a session */ | ||
45 | |||
46 | struct tsession { | 37 | struct tsession { |
47 | struct tsession *next; | 38 | struct tsession *next; |
48 | int sockfd_read, sockfd_write, ptyfd; | 39 | int sockfd_read, sockfd_write, ptyfd; |
49 | int shell_pid; | 40 | /*int shell_pid;*/ |
41 | |||
50 | /* two circular buffers */ | 42 | /* two circular buffers */ |
51 | char *buf1, *buf2; | 43 | /*char *buf1, *buf2;*/ |
44 | /*#define TS_BUF1 ts->buf1*/ | ||
45 | /*#define TS_BUF2 TS_BUF2*/ | ||
46 | #define TS_BUF1 ((char*)(ts + 1)) | ||
47 | #define TS_BUF2 (((char*)(ts + 1)) + BUFSIZE) | ||
52 | int rdidx1, wridx1, size1; | 48 | int rdidx1, wridx1, size1; |
53 | int rdidx2, wridx2, size2; | 49 | int rdidx2, wridx2, size2; |
54 | }; | 50 | }; |
55 | 51 | ||
56 | /* Two buffers are directly after tsession in malloced memory. | 52 | /* Two buffers are directly after tsession in malloced memory. |
57 | * Make whole thing fit in 4k */ | 53 | * Make whole thing fit in 4k */ |
58 | enum { BUFSIZE = (4*1024 - sizeof(struct tsession)) / 2 }; | 54 | enum { BUFSIZE = (4 * 1024 - sizeof(struct tsession)) / 2 }; |
59 | |||
60 | /* | ||
61 | This is how the buffers are used. The arrows indicate the movement | ||
62 | of data. | ||
63 | |||
64 | +-------+ wridx1++ +------+ rdidx1++ +----------+ | ||
65 | | | <-------------- | buf1 | <-------------- | | | ||
66 | | | size1-- +------+ size1++ | | | ||
67 | | pty | | socket | | ||
68 | | | rdidx2++ +------+ wridx2++ | | | ||
69 | | | --------------> | buf2 | --------------> | | | ||
70 | +-------+ size2++ +------+ size2-- +----------+ | ||
71 | 55 | ||
72 | Each session has got two buffers. | ||
73 | */ | ||
74 | 56 | ||
57 | /* Globals */ | ||
75 | static int maxfd; | 58 | static int maxfd; |
76 | |||
77 | static struct tsession *sessions; | 59 | static struct tsession *sessions; |
60 | #if ENABLE_LOGIN | ||
61 | static const char *loginpath = "/bin/login"; | ||
62 | #else | ||
63 | static const char *loginpath = DEFAULT_SHELL; | ||
64 | #endif | ||
65 | static const char *issuefile = "/etc/issue.net"; | ||
78 | 66 | ||
79 | 67 | ||
80 | /* | 68 | /* |
81 | Remove all IAC's from the buffer pointed to by bf (received IACs are ignored | 69 | Remove all IAC's from buf1 (received IACs are ignored and must be removed |
82 | and must be removed so as to not be interpreted by the terminal). Make an | 70 | so as to not be interpreted by the terminal). Make an uninterrupted |
83 | uninterrupted string of characters fit for the terminal. Do this by packing | 71 | string of characters fit for the terminal. Do this by packing |
84 | all characters meant for the terminal sequentially towards the end of bf. | 72 | all characters meant for the terminal sequentially towards the end of buf. |
85 | 73 | ||
86 | Return a pointer to the beginning of the characters meant for the terminal. | 74 | Return a pointer to the beginning of the characters meant for the terminal. |
87 | and make *num_totty the number of characters that should be sent to | 75 | and make *num_totty the number of characters that should be sent to |
88 | the terminal. | 76 | the terminal. |
89 | 77 | ||
90 | Note - If an IAC (3 byte quantity) starts before (bf + len) but extends | 78 | Note - If an IAC (3 byte quantity) starts before (bf + len) but extends |
91 | past (bf + len) then that IAC will be left unprocessed and *processed will be | 79 | past (bf + len) then that IAC will be left unprocessed and *processed |
92 | less than len. | 80 | will be less than len. |
93 | 81 | ||
94 | FIXME - if we mean to send 0xFF to the terminal then it will be escaped, | 82 | FIXME - if we mean to send 0xFF to the terminal then it will be escaped, |
95 | what is the escape character? We aren't handling that situation here. | 83 | what is the escape character? We aren't handling that situation here. |
@@ -99,7 +87,7 @@ static struct tsession *sessions; | |||
99 | static char * | 87 | static char * |
100 | remove_iacs(struct tsession *ts, int *pnum_totty) | 88 | remove_iacs(struct tsession *ts, int *pnum_totty) |
101 | { | 89 | { |
102 | unsigned char *ptr0 = (unsigned char *)ts->buf1 + ts->wridx1; | 90 | unsigned char *ptr0 = (unsigned char *)TS_BUF1 + ts->wridx1; |
103 | unsigned char *ptr = ptr0; | 91 | unsigned char *ptr = ptr0; |
104 | unsigned char *totty = ptr; | 92 | unsigned char *totty = ptr; |
105 | unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1); | 93 | unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1); |
@@ -210,19 +198,6 @@ getpty(char *line, int size) | |||
210 | } | 198 | } |
211 | 199 | ||
212 | 200 | ||
213 | static void | ||
214 | send_iac(struct tsession *ts, unsigned char command, int option) | ||
215 | { | ||
216 | /* We rely on that there is space in the buffer for now. */ | ||
217 | char *b = ts->buf2 + ts->rdidx2; | ||
218 | *b++ = IAC; | ||
219 | *b++ = command; | ||
220 | *b++ = option; | ||
221 | ts->rdidx2 += 3; | ||
222 | ts->size2 += 3; | ||
223 | } | ||
224 | |||
225 | |||
226 | static struct tsession * | 201 | static struct tsession * |
227 | make_new_session( | 202 | make_new_session( |
228 | USE_FEATURE_TELNETD_STANDALONE(int sock_r, int sock_w) | 203 | USE_FEATURE_TELNETD_STANDALONE(int sock_r, int sock_w) |
@@ -234,25 +209,28 @@ make_new_session( | |||
234 | char tty_name[32]; | 209 | char tty_name[32]; |
235 | struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2); | 210 | struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2); |
236 | 211 | ||
237 | ts->buf1 = (char *)(&ts[1]); | 212 | /*ts->buf1 = (char *)(ts + 1);*/ |
238 | ts->buf2 = ts->buf1 + BUFSIZE; | 213 | /*ts->buf2 = ts->buf1 + BUFSIZE;*/ |
239 | 214 | ||
240 | /* Got a new connection, set up a tty. */ | 215 | /* Got a new connection, set up a tty. */ |
241 | fd = getpty(tty_name, 32); | 216 | fd = getpty(tty_name, 32); |
242 | if (fd < 0) { | 217 | if (fd < 0) { |
243 | bb_error_msg("all terminals in use"); | 218 | bb_error_msg("can't create pty"); |
244 | return NULL; | 219 | return NULL; |
245 | } | 220 | } |
246 | if (fd > maxfd) maxfd = fd; | 221 | if (fd > maxfd) maxfd = fd; |
247 | ndelay_on(ts->ptyfd = fd); | 222 | ts->ptyfd = fd; |
223 | ndelay_on(fd); | ||
248 | #if ENABLE_FEATURE_TELNETD_STANDALONE | 224 | #if ENABLE_FEATURE_TELNETD_STANDALONE |
249 | if (sock_w > maxfd) maxfd = sock_w; | 225 | if (sock_w > maxfd) maxfd = sock_w; |
226 | ts->sockfd_write = sock_w; | ||
227 | ndelay_on(sock_w); | ||
250 | if (sock_r > maxfd) maxfd = sock_r; | 228 | if (sock_r > maxfd) maxfd = sock_r; |
251 | ndelay_on(ts->sockfd_write = sock_w); | 229 | ts->sockfd_read = sock_r; |
252 | ndelay_on(ts->sockfd_read = sock_r); | 230 | ndelay_on(sock_r); |
253 | #else | 231 | #else |
254 | ts->sockfd_write = 1; | 232 | ts->sockfd_write = 1; |
255 | /* xzalloc: ts->sockfd_read = 0; */ | 233 | /* ts->sockfd_read = 0; - done by xzalloc */ |
256 | ndelay_on(0); | 234 | ndelay_on(0); |
257 | ndelay_on(1); | 235 | ndelay_on(1); |
258 | #endif | 236 | #endif |
@@ -260,26 +238,36 @@ make_new_session( | |||
260 | * should not do it locally. We don't tell the client to run linemode, | 238 | * should not do it locally. We don't tell the client to run linemode, |
261 | * because we want to handle line editing and tab completion and other | 239 | * because we want to handle line editing and tab completion and other |
262 | * stuff that requires char-by-char support. */ | 240 | * stuff that requires char-by-char support. */ |
263 | send_iac(ts, DO, TELOPT_ECHO); | 241 | { |
264 | send_iac(ts, DO, TELOPT_NAWS); | 242 | static const char iacs_to_send[] ALIGN1 = { |
265 | send_iac(ts, DO, TELOPT_LFLOW); | 243 | IAC, DO, TELOPT_ECHO, |
266 | send_iac(ts, WILL, TELOPT_ECHO); | 244 | IAC, DO, TELOPT_NAWS, |
267 | send_iac(ts, WILL, TELOPT_SGA); | 245 | IAC, DO, TELOPT_LFLOW, |
246 | IAC, WILL, TELOPT_ECHO, | ||
247 | IAC, WILL, TELOPT_SGA | ||
248 | }; | ||
249 | memcpy(TS_BUF2, iacs_to_send, sizeof(iacs_to_send)); | ||
250 | ts->rdidx2 = sizeof(iacs_to_send); | ||
251 | ts->size2 = sizeof(iacs_to_send); | ||
252 | } | ||
268 | 253 | ||
269 | pid = fork(); | 254 | fflush(NULL); /* flush all streams */ |
255 | pid = vfork(); /* NOMMU-friendly */ | ||
270 | if (pid < 0) { | 256 | if (pid < 0) { |
271 | free(ts); | 257 | free(ts); |
272 | close(fd); | 258 | close(fd); |
273 | bb_perror_msg("fork"); | 259 | /* sock_r and sock_w will be closed by caller */ |
260 | bb_perror_msg("vfork"); | ||
274 | return NULL; | 261 | return NULL; |
275 | } | 262 | } |
276 | if (pid > 0) { | 263 | if (pid > 0) { |
277 | /* parent */ | 264 | /* Parent */ |
278 | ts->shell_pid = pid; | 265 | /*ts->shell_pid = pid;*/ |
279 | return ts; | 266 | return ts; |
280 | } | 267 | } |
281 | 268 | ||
282 | /* child */ | 269 | /* Child */ |
270 | /* Careful - we are after vfork! */ | ||
283 | 271 | ||
284 | /* make new session and process group */ | 272 | /* make new session and process group */ |
285 | setsid(); | 273 | setsid(); |
@@ -298,20 +286,24 @@ make_new_session( | |||
298 | * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */ | 286 | * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */ |
299 | tcgetattr(0, &termbuf); | 287 | tcgetattr(0, &termbuf); |
300 | termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */ | 288 | termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */ |
301 | termbuf.c_oflag |= ONLCR|XTABS; | 289 | termbuf.c_oflag |= ONLCR | XTABS; |
302 | termbuf.c_iflag |= ICRNL; | 290 | termbuf.c_iflag |= ICRNL; |
303 | termbuf.c_iflag &= ~IXOFF; | 291 | termbuf.c_iflag &= ~IXOFF; |
304 | /*termbuf.c_lflag &= ~ICANON;*/ | 292 | /*termbuf.c_lflag &= ~ICANON;*/ |
305 | tcsetattr(0, TCSANOW, &termbuf); | 293 | tcsetattr(0, TCSANOW, &termbuf); |
306 | 294 | ||
295 | /* Uses FILE-based I/O to stdout, but does fflush(stdout), | ||
296 | * so should be safe with vfork. | ||
297 | * I fear, though, that some users will have ridiculously big | ||
298 | * issue files, and they may block writing to fd 1. */ | ||
307 | print_login_issue(issuefile, NULL); | 299 | print_login_issue(issuefile, NULL); |
308 | 300 | ||
309 | /* exec shell / login /whatever */ | 301 | /* Exec shell / login / whatever */ |
310 | login_argv[0] = loginpath; | 302 | login_argv[0] = loginpath; |
311 | login_argv[1] = NULL; | 303 | login_argv[1] = NULL; |
312 | execv(loginpath, (char **)login_argv); | 304 | execvp(loginpath, (char **)login_argv); |
313 | /* Hmmm... this gets sent to the client thru fd#2! Is it ok?? */ | 305 | /* Safer with vfork, and we shouldn't send this to the client anyway */ |
314 | bb_perror_msg_and_die("execv %s", loginpath); | 306 | _exit(1); /*bb_perror_msg_and_die("execv %s", loginpath);*/ |
315 | } | 307 | } |
316 | 308 | ||
317 | #if ENABLE_FEATURE_TELNETD_STANDALONE | 309 | #if ENABLE_FEATURE_TELNETD_STANDALONE |
@@ -321,7 +313,7 @@ free_session(struct tsession *ts) | |||
321 | { | 313 | { |
322 | struct tsession *t = sessions; | 314 | struct tsession *t = sessions; |
323 | 315 | ||
324 | /* unlink this telnet session from the session list */ | 316 | /* Unlink this telnet session from the session list */ |
325 | if (t == ts) | 317 | if (t == ts) |
326 | sessions = ts->next; | 318 | sessions = ts->next; |
327 | else { | 319 | else { |
@@ -330,15 +322,18 @@ free_session(struct tsession *ts) | |||
330 | t->next = ts->next; | 322 | t->next = ts->next; |
331 | } | 323 | } |
332 | 324 | ||
333 | kill(ts->shell_pid, SIGKILL); | 325 | /* It was said that "normal" telnetd just closes ptyfd, |
334 | wait4(ts->shell_pid, NULL, 0, NULL); | 326 | * doesn't send SIGKILL. When we close ptyfd, |
327 | * kernel sends SIGHUP to processes having slave side opened. */ | ||
328 | /*kill(ts->shell_pid, SIGKILL); | ||
329 | wait4(ts->shell_pid, NULL, 0, NULL);*/ | ||
335 | close(ts->ptyfd); | 330 | close(ts->ptyfd); |
336 | close(ts->sockfd_read); | 331 | close(ts->sockfd_read); |
337 | /* error if ts->sockfd_read == ts->sockfd_write. So what? ;) */ | 332 | /* error if ts->sockfd_read == ts->sockfd_write. So what? ;) */ |
338 | close(ts->sockfd_write); | 333 | close(ts->sockfd_write); |
339 | free(ts); | 334 | free(ts); |
340 | 335 | ||
341 | /* scan all sessions and find new maxfd */ | 336 | /* Scan all sessions and find new maxfd */ |
342 | ts = sessions; | 337 | ts = sessions; |
343 | maxfd = 0; | 338 | maxfd = 0; |
344 | while (ts) { | 339 | while (ts) { |
@@ -369,7 +364,7 @@ int telnetd_main(int argc, char **argv) | |||
369 | struct tsession *ts; | 364 | struct tsession *ts; |
370 | #if ENABLE_FEATURE_TELNETD_STANDALONE | 365 | #if ENABLE_FEATURE_TELNETD_STANDALONE |
371 | #define IS_INETD (opt & OPT_INETD) | 366 | #define IS_INETD (opt & OPT_INETD) |
372 | int master_fd = -1; /* be happy, gcc */ | 367 | int master_fd = master_fd; /* be happy, gcc */ |
373 | unsigned portnbr = 23; | 368 | unsigned portnbr = 23; |
374 | char *opt_bindaddr = NULL; | 369 | char *opt_bindaddr = NULL; |
375 | char *opt_portnbr; | 370 | char *opt_portnbr; |
@@ -381,12 +376,14 @@ int telnetd_main(int argc, char **argv) | |||
381 | }; | 376 | }; |
382 | #endif | 377 | #endif |
383 | enum { | 378 | enum { |
384 | OPT_PORT = 4 * ENABLE_FEATURE_TELNETD_STANDALONE, | 379 | OPT_INETD = (1 << 2) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */ |
385 | OPT_FOREGROUND = 0x10 * ENABLE_FEATURE_TELNETD_STANDALONE, | 380 | OPT_PORT = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p */ |
386 | OPT_INETD = 0x20 * ENABLE_FEATURE_TELNETD_STANDALONE, | 381 | OPT_FOREGROUND = (1 << 5) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */ |
387 | }; | 382 | }; |
388 | 383 | ||
389 | opt = getopt32(argv, "f:l:" USE_FEATURE_TELNETD_STANDALONE("p:b:Fi"), | 384 | /* If !STANDALONE, we accept (and ignore) -i, thus people |
385 | * don't need to guess whether it's ok to pass -i to us */ | ||
386 | opt = getopt32(argv, "f:l:i" USE_FEATURE_TELNETD_STANDALONE("p:b:F"), | ||
390 | &issuefile, &loginpath | 387 | &issuefile, &loginpath |
391 | USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr)); | 388 | USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr)); |
392 | /* Redirect log to syslog early, if needed */ | 389 | /* Redirect log to syslog early, if needed */ |
@@ -410,19 +407,43 @@ int telnetd_main(int argc, char **argv) | |||
410 | #if ENABLE_FEATURE_TELNETD_STANDALONE | 407 | #if ENABLE_FEATURE_TELNETD_STANDALONE |
411 | if (IS_INETD) { | 408 | if (IS_INETD) { |
412 | sessions = make_new_session(0, 1); | 409 | sessions = make_new_session(0, 1); |
410 | if (!sessions) /* pty opening or vfork problem, exit */ | ||
411 | return 1; /* make_new_session prints error message */ | ||
413 | } else { | 412 | } else { |
413 | //vda: inform that we start in standalone mode? | ||
414 | master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr); | 414 | master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr); |
415 | xlisten(master_fd, 1); | 415 | xlisten(master_fd, 1); |
416 | if (!(opt & OPT_FOREGROUND)) | 416 | if (!(opt & OPT_FOREGROUND)) |
417 | //vda: NOMMU? | ||
417 | bb_daemonize(DAEMON_CHDIR_ROOT); | 418 | bb_daemonize(DAEMON_CHDIR_ROOT); |
418 | } | 419 | } |
419 | #else | 420 | #else |
420 | sessions = make_new_session(); | 421 | sessions = make_new_session(); |
422 | if (!sessions) /* pty opening or vfork problem, exit */ | ||
423 | return 1; /* make_new_session prints error message */ | ||
421 | #endif | 424 | #endif |
422 | 425 | ||
423 | /* We don't want to die if just one session is broken */ | 426 | /* We don't want to die if just one session is broken */ |
424 | signal(SIGPIPE, SIG_IGN); | 427 | signal(SIGPIPE, SIG_IGN); |
425 | 428 | ||
429 | /* | ||
430 | This is how the buffers are used. The arrows indicate the movement | ||
431 | of data. | ||
432 | +-------+ wridx1++ +------+ rdidx1++ +----------+ | ||
433 | | | <-------------- | buf1 | <-------------- | | | ||
434 | | | size1-- +------+ size1++ | | | ||
435 | | pty | | socket | | ||
436 | | | rdidx2++ +------+ wridx2++ | | | ||
437 | | | --------------> | buf2 | --------------> | | | ||
438 | +-------+ size2++ +------+ size2-- +----------+ | ||
439 | |||
440 | size1: "how many bytes are buffered for pty between rdidx1 and wridx1?" | ||
441 | size2: "how many bytes are buffered for socket between rdidx2 and wridx2?" | ||
442 | |||
443 | Each session has got two buffers. Buffers are circular. If sizeN == 0, | ||
444 | buffer is empty. If sizeN == BUFSIZE, buffer is full. In both these cases | ||
445 | rdidxN == wridxN. | ||
446 | */ | ||
426 | again: | 447 | again: |
427 | FD_ZERO(&rdfdset); | 448 | FD_ZERO(&rdfdset); |
428 | FD_ZERO(&wrfdset); | 449 | FD_ZERO(&wrfdset); |
@@ -435,12 +456,12 @@ int telnetd_main(int argc, char **argv) | |||
435 | maxfd = master_fd; | 456 | maxfd = master_fd; |
436 | } | 457 | } |
437 | 458 | ||
438 | /* select on the master socket, all telnet sockets and their | 459 | /* Select on the master socket, all telnet sockets and their |
439 | * ptys if there is room in their session buffers. */ | 460 | * ptys if there is room in their session buffers. |
461 | * NB: scalability problem: we recalculate entire bitmap | ||
462 | * before each select. Can be a problem with 500+ connections. */ | ||
440 | ts = sessions; | 463 | ts = sessions; |
441 | while (ts) { | 464 | while (ts) { |
442 | /* buf1 is used from socket to pty | ||
443 | * buf2 is used from pty to socket */ | ||
444 | if (ts->size1 > 0) /* can write to pty */ | 465 | if (ts->size1 > 0) /* can write to pty */ |
445 | FD_SET(ts->ptyfd, &wrfdset); | 466 | FD_SET(ts->ptyfd, &wrfdset); |
446 | if (ts->size1 < BUFSIZE) /* can read from socket */ | 467 | if (ts->size1 < BUFSIZE) /* can read from socket */ |
@@ -452,9 +473,9 @@ int telnetd_main(int argc, char **argv) | |||
452 | ts = ts->next; | 473 | ts = ts->next; |
453 | } | 474 | } |
454 | 475 | ||
455 | selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0); | 476 | selret = select(maxfd + 1, &rdfdset, &wrfdset, NULL, NULL); |
456 | if (!selret) | 477 | if (selret < 0) |
457 | return 0; | 478 | goto again; /* EINTR or ENOMEM */ |
458 | 479 | ||
459 | #if ENABLE_FEATURE_TELNETD_STANDALONE | 480 | #if ENABLE_FEATURE_TELNETD_STANDALONE |
460 | /* First check for and accept new sessions. */ | 481 | /* First check for and accept new sessions. */ |
@@ -462,7 +483,7 @@ int telnetd_main(int argc, char **argv) | |||
462 | int fd; | 483 | int fd; |
463 | struct tsession *new_ts; | 484 | struct tsession *new_ts; |
464 | 485 | ||
465 | fd = accept(master_fd, NULL, 0); | 486 | fd = accept(master_fd, NULL, NULL); |
466 | if (fd < 0) | 487 | if (fd < 0) |
467 | goto again; | 488 | goto again; |
468 | /* Create a new session and link it into our active list */ | 489 | /* Create a new session and link it into our active list */ |
@@ -481,91 +502,100 @@ int telnetd_main(int argc, char **argv) | |||
481 | while (ts) { /* For all sessions... */ | 502 | while (ts) { /* For all sessions... */ |
482 | struct tsession *next = ts->next; /* in case we free ts. */ | 503 | struct tsession *next = ts->next; /* in case we free ts. */ |
483 | 504 | ||
484 | if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) { | 505 | if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) { |
485 | int num_totty; | 506 | int num_totty; |
486 | char *ptr; | 507 | char *ptr; |
487 | /* Write to pty from buffer 1. */ | 508 | /* Write to pty from buffer 1. */ |
488 | ptr = remove_iacs(ts, &num_totty); | 509 | ptr = remove_iacs(ts, &num_totty); |
489 | w = safe_write(ts->ptyfd, ptr, num_totty); | 510 | w = safe_write(ts->ptyfd, ptr, num_totty); |
490 | /* needed? if (w < 0 && errno == EAGAIN) continue; */ | ||
491 | if (w < 0) { | 511 | if (w < 0) { |
512 | if (errno == EAGAIN) | ||
513 | goto skip1; | ||
492 | if (IS_INETD) | 514 | if (IS_INETD) |
493 | return 0; | 515 | return 0; |
494 | free_session(ts); | 516 | free_session(ts); |
495 | ts = next; | 517 | ts = next; |
496 | continue; | 518 | continue; |
497 | } | 519 | } |
498 | ts->wridx1 += w; | ||
499 | ts->size1 -= w; | 520 | ts->size1 -= w; |
500 | if (ts->wridx1 == BUFSIZE) | 521 | ts->wridx1 += w; |
522 | if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */ | ||
501 | ts->wridx1 = 0; | 523 | ts->wridx1 = 0; |
502 | } | 524 | } |
503 | 525 | skip1: | |
504 | if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) { | 526 | if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) { |
505 | /* Write to socket from buffer 2. */ | 527 | /* Write to socket from buffer 2. */ |
506 | maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2); | 528 | maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2); |
507 | w = safe_write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen); | 529 | w = safe_write(ts->sockfd_write, TS_BUF2 + ts->wridx2, maxlen); |
508 | /* needed? if (w < 0 && errno == EAGAIN) continue; */ | ||
509 | if (w < 0) { | 530 | if (w < 0) { |
531 | if (errno == EAGAIN) | ||
532 | goto skip2; | ||
510 | if (IS_INETD) | 533 | if (IS_INETD) |
511 | return 0; | 534 | return 0; |
512 | free_session(ts); | 535 | free_session(ts); |
513 | ts = next; | 536 | ts = next; |
514 | continue; | 537 | continue; |
515 | } | 538 | } |
516 | ts->wridx2 += w; | ||
517 | ts->size2 -= w; | 539 | ts->size2 -= w; |
518 | if (ts->wridx2 == BUFSIZE) | 540 | ts->wridx2 += w; |
541 | if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */ | ||
519 | ts->wridx2 = 0; | 542 | ts->wridx2 = 0; |
520 | } | 543 | } |
521 | 544 | skip2: | |
522 | if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) { | 545 | #if 0 |
546 | /* Not strictly needed, but allows for bigger reads in common case */ | ||
547 | if (ts->size1 == 0) { | ||
548 | ts->rdidx1 = 0; | ||
549 | ts->wridx1 = 0; | ||
550 | } | ||
551 | if (ts->size2 == 0) { | ||
552 | ts->rdidx2 = 0; | ||
553 | ts->wridx2 = 0; | ||
554 | } | ||
555 | #endif | ||
556 | if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) { | ||
523 | /* Read from socket to buffer 1. */ | 557 | /* Read from socket to buffer 1. */ |
524 | maxlen = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1); | 558 | maxlen = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1); |
525 | r = safe_read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen); | 559 | r = safe_read(ts->sockfd_read, TS_BUF1 + ts->rdidx1, maxlen); |
526 | if (r < 0 && errno == EAGAIN) continue; | ||
527 | if (r <= 0) { | 560 | if (r <= 0) { |
561 | if (r < 0 && errno == EAGAIN) | ||
562 | goto skip3; | ||
528 | if (IS_INETD) | 563 | if (IS_INETD) |
529 | return 0; | 564 | return 0; |
530 | free_session(ts); | 565 | free_session(ts); |
531 | ts = next; | 566 | ts = next; |
532 | continue; | 567 | continue; |
533 | } | 568 | } |
534 | if (!ts->buf1[ts->rdidx1 + r - 1]) | 569 | /* Ignore trailing NUL if it is there */ |
570 | if (!TS_BUF1[ts->rdidx1 + r - 1]) { | ||
535 | if (!--r) | 571 | if (!--r) |
536 | continue; | 572 | goto skip3; |
537 | ts->rdidx1 += r; | 573 | } |
538 | ts->size1 += r; | 574 | ts->size1 += r; |
539 | if (ts->rdidx1 == BUFSIZE) | 575 | ts->rdidx1 += r; |
576 | if (ts->rdidx1 >= BUFSIZE) /* actually == BUFSIZE */ | ||
540 | ts->rdidx1 = 0; | 577 | ts->rdidx1 = 0; |
541 | } | 578 | } |
542 | 579 | skip3: | |
543 | if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) { | 580 | if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) { |
544 | /* Read from pty to buffer 2. */ | 581 | /* Read from pty to buffer 2. */ |
545 | maxlen = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2); | 582 | maxlen = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2); |
546 | r = safe_read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen); | 583 | r = safe_read(ts->ptyfd, TS_BUF2 + ts->rdidx2, maxlen); |
547 | if (r < 0 && errno == EAGAIN) continue; | ||
548 | if (r <= 0) { | 584 | if (r <= 0) { |
585 | if (r < 0 && errno == EAGAIN) | ||
586 | goto skip4; | ||
549 | if (IS_INETD) | 587 | if (IS_INETD) |
550 | return 0; | 588 | return 0; |
551 | free_session(ts); | 589 | free_session(ts); |
552 | ts = next; | 590 | ts = next; |
553 | continue; | 591 | continue; |
554 | } | 592 | } |
555 | ts->rdidx2 += r; | ||
556 | ts->size2 += r; | 593 | ts->size2 += r; |
557 | if (ts->rdidx2 == BUFSIZE) | 594 | ts->rdidx2 += r; |
595 | if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */ | ||
558 | ts->rdidx2 = 0; | 596 | ts->rdidx2 = 0; |
559 | } | 597 | } |
560 | 598 | skip4: | |
561 | if (ts->size1 == 0) { | ||
562 | ts->rdidx1 = 0; | ||
563 | ts->wridx1 = 0; | ||
564 | } | ||
565 | if (ts->size2 == 0) { | ||
566 | ts->rdidx2 = 0; | ||
567 | ts->wridx2 = 0; | ||
568 | } | ||
569 | ts = next; | 599 | ts = next; |
570 | } | 600 | } |
571 | goto again; | 601 | goto again; |