Extensive changes that _should_ fix the socket buffering problems,
[u/mdw/putty] / plink.c
CommitLineData
12dc4ec0 1/*
2 * PLink - a command-line (stdin/stdout) variant of PuTTY.
3 */
4
4d331a77 5#ifndef AUTO_WINSOCK
12dc4ec0 6#include <winsock2.h>
4d331a77 7#endif
12dc4ec0 8#include <windows.h>
9#include <stdio.h>
49bad831 10#include <stdlib.h>
12dc4ec0 11#include <stdarg.h>
12
32874aea 13#define PUTTY_DO_GLOBALS /* actually _define_ globals */
12dc4ec0 14#include "putty.h"
a9422f39 15#include "storage.h"
8df7a775 16#include "tree234.h"
12dc4ec0 17
5471d09a 18#define MAX_STDIN_BACKLOG 4096
19
32874aea 20void fatalbox(char *p, ...)
21{
12dc4ec0 22 va_list ap;
49bad831 23 fprintf(stderr, "FATAL ERROR: ");
12dc4ec0 24 va_start(ap, p);
25 vfprintf(stderr, p, ap);
26 va_end(ap);
27 fputc('\n', stderr);
28 WSACleanup();
29 exit(1);
30}
32874aea 31void connection_fatal(char *p, ...)
32{
8d5de777 33 va_list ap;
49bad831 34 fprintf(stderr, "FATAL ERROR: ");
8d5de777 35 va_start(ap, p);
36 vfprintf(stderr, p, ap);
37 va_end(ap);
38 fputc('\n', stderr);
39 WSACleanup();
40 exit(1);
41}
12dc4ec0 42
d8426c54 43static char *password = NULL;
44
32874aea 45void logevent(char *string)
46{
47}
a9422f39 48
49void verify_ssh_host_key(char *host, int port, char *keytype,
32874aea 50 char *keystr, char *fingerprint)
51{
a9422f39 52 int ret;
fcbb94d3 53 HANDLE hin;
54 DWORD savemode, i;
a9422f39 55
56 static const char absentmsg[] =
32874aea 57 "The server's host key is not cached in the registry. You\n"
58 "have no guarantee that the server is the computer you\n"
59 "think it is.\n"
60 "The server's key fingerprint is:\n"
61 "%s\n"
62 "If you trust this host, enter \"y\" to add the key to\n"
63 "PuTTY's cache and carry on connecting.\n"
d0718310 64 "If you want to carry on connecting just once, without\n"
65 "adding the key to the cache, enter \"n\".\n"
66 "If you do not trust this host, press Return to abandon the\n"
67 "connection.\n"
68 "Store key in cache? (y/n) ";
a9422f39 69
70 static const char wrongmsg[] =
32874aea 71 "WARNING - POTENTIAL SECURITY BREACH!\n"
72 "The server's host key does not match the one PuTTY has\n"
73 "cached in the registry. This means that either the\n"
74 "server administrator has changed the host key, or you\n"
75 "have actually connected to another computer pretending\n"
76 "to be the server.\n"
77 "The new key fingerprint is:\n"
78 "%s\n"
79 "If you were expecting this change and trust the new key,\n"
80 "enter \"y\" to update PuTTY's cache and continue connecting.\n"
81 "If you want to carry on connecting but without updating\n"
82 "the cache, enter \"n\".\n"
83 "If you want to abandon the connection completely, press\n"
84 "Return to cancel. Pressing Return is the ONLY guaranteed\n"
85 "safe choice.\n"
86 "Update cached key? (y/n, Return cancels connection) ";
a9422f39 87
88 static const char abandoned[] = "Connection abandoned.\n";
89
90 char line[32];
91
92 /*
93 * Verify the key against the registry.
94 */
95 ret = verify_host_key(host, port, keytype, keystr);
96
32874aea 97 if (ret == 0) /* success - key matched OK */
98 return;
fcbb94d3 99
b4453f49 100 if (ret == 2) { /* key was different */
32874aea 101 fprintf(stderr, wrongmsg, fingerprint);
b4453f49 102 fflush(stderr);
103 }
104 if (ret == 1) { /* key was absent */
32874aea 105 fprintf(stderr, absentmsg, fingerprint);
b4453f49 106 fflush(stderr);
107 }
fcbb94d3 108
109 hin = GetStdHandle(STD_INPUT_HANDLE);
110 GetConsoleMode(hin, &savemode);
111 SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
32874aea 112 ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
113 ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
fcbb94d3 114 SetConsoleMode(hin, savemode);
115
d0718310 116 if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
32874aea 117 if (line[0] == 'y' || line[0] == 'Y')
118 store_host_key(host, port, keytype, keystr);
d0718310 119 } else {
120 fprintf(stderr, abandoned);
121 exit(0);
a9422f39 122 }
123}
989b10e9 124
0965bee0 125HANDLE inhandle, outhandle, errhandle;
6f34e365 126DWORD orig_console_mode;
127
8df7a775 128WSAEVENT netevent;
129
32874aea 130int term_ldisc(int mode)
131{
132 return FALSE;
133}
134void ldisc_update(int echo, int edit)
135{
0965bee0 136 /* Update stdin read mode to reflect changes in line discipline. */
137 DWORD mode;
138
139 mode = ENABLE_PROCESSED_INPUT;
140 if (echo)
32874aea 141 mode = mode | ENABLE_ECHO_INPUT;
0965bee0 142 else
32874aea 143 mode = mode & ~ENABLE_ECHO_INPUT;
0965bee0 144 if (edit)
32874aea 145 mode = mode | ENABLE_LINE_INPUT;
0965bee0 146 else
32874aea 147 mode = mode & ~ENABLE_LINE_INPUT;
0965bee0 148 SetConsoleMode(inhandle, mode);
149}
150
fa17a66e 151static int get_line(const char *prompt, char *str, int maxlen, int is_pw)
67779be7 152{
153 HANDLE hin, hout;
fa17a66e 154 DWORD savemode, newmode, i;
67779be7 155
fa17a66e 156 if (is_pw && password) {
32874aea 157 static int tried_once = 0;
158
159 if (tried_once) {
160 return 0;
161 } else {
162 strncpy(str, password, maxlen);
163 str[maxlen - 1] = '\0';
164 tried_once = 1;
165 return 1;
166 }
67779be7 167 }
67779be7 168
169 hin = GetStdHandle(STD_INPUT_HANDLE);
170 hout = GetStdHandle(STD_OUTPUT_HANDLE);
171 if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE) {
32874aea 172 fprintf(stderr, "Cannot get standard input/output handles");
173 return 0;
67779be7 174 }
175
176 GetConsoleMode(hin, &savemode);
fa17a66e 177 newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
178 if (is_pw)
32874aea 179 newmode &= ~ENABLE_ECHO_INPUT;
fa17a66e 180 else
32874aea 181 newmode |= ENABLE_ECHO_INPUT;
fa17a66e 182 SetConsoleMode(hin, newmode);
67779be7 183
184 WriteFile(hout, prompt, strlen(prompt), &i, NULL);
32874aea 185 ReadFile(hin, str, maxlen - 1, &i, NULL);
67779be7 186
187 SetConsoleMode(hin, savemode);
188
32874aea 189 if ((int) i > maxlen)
190 i = maxlen - 1;
191 else
192 i = i - 2;
67779be7 193 str[i] = '\0';
194
fa17a66e 195 if (is_pw)
32874aea 196 WriteFile(hout, "\r\n", 2, &i, NULL);
67779be7 197
198 return 1;
199}
200
5471d09a 201struct input_data {
202 DWORD len;
203 char buffer[4096];
204 HANDLE event, eventback;
205};
206
32874aea 207static DWORD WINAPI stdin_read_thread(void *param)
208{
209 struct input_data *idata = (struct input_data *) param;
12dc4ec0 210 HANDLE inhandle;
211
212 inhandle = GetStdHandle(STD_INPUT_HANDLE);
213
214 while (ReadFile(inhandle, idata->buffer, sizeof(idata->buffer),
32874aea 215 &idata->len, NULL) && idata->len > 0) {
216 SetEvent(idata->event);
217 WaitForSingleObject(idata->eventback, INFINITE);
12dc4ec0 218 }
219
220 idata->len = 0;
221 SetEvent(idata->event);
222
223 return 0;
224}
225
5471d09a 226struct output_data {
227 DWORD len, lenwritten;
228 int writeret;
229 char *buffer;
230 int is_stderr, done;
231 HANDLE event, eventback;
232 int busy;
233};
234
235static DWORD WINAPI stdout_write_thread(void *param)
236{
237 struct output_data *odata = (struct output_data *) param;
238 HANDLE outhandle, errhandle;
239
240 outhandle = GetStdHandle(STD_OUTPUT_HANDLE);
241 errhandle = GetStdHandle(STD_ERROR_HANDLE);
242
243 while (1) {
244 WaitForSingleObject(odata->eventback, INFINITE);
245 if (odata->done)
246 break;
247 odata->writeret =
248 WriteFile(odata->is_stderr ? errhandle : outhandle,
249 odata->buffer, odata->len, &odata->lenwritten, NULL);
250 SetEvent(odata->event);
251 }
252
253 return 0;
254}
255
256bufchain stdout_data, stderr_data;
257struct output_data odata, edata;
258
259void try_output(int is_stderr)
260{
261 struct output_data *data = (is_stderr ? &edata : &odata);
262 void *senddata;
263 int sendlen;
264
265 if (!data->busy) {
266 bufchain_prefix(is_stderr ? &stderr_data : &stdout_data,
267 &senddata, &sendlen);
268 data->buffer = senddata;
269 data->len = sendlen;
270 SetEvent(data->eventback);
271 data->busy = 1;
272 }
273}
274
275int from_backend(int is_stderr, char *data, int len)
276{
277 int pos;
278 DWORD ret;
279 HANDLE h = (is_stderr ? errhandle : outhandle);
280 void *writedata;
281 int writelen;
282 int osize, esize;
283
284 if (is_stderr) {
285 bufchain_add(&stderr_data, data, len);
286 try_output(1);
287 } else {
288 bufchain_add(&stdout_data, data, len);
289 try_output(0);
290 }
291
292 osize = bufchain_size(&stdout_data);
293 esize = bufchain_size(&stderr_data);
294
295 return osize + esize;
296}
297
d8426c54 298/*
299 * Short description of parameters.
300 */
301static void usage(void)
302{
303 printf("PuTTY Link: command-line connection utility\n");
304 printf("%s\n", ver);
305 printf("Usage: plink [options] [user@]host [command]\n");
306 printf("Options:\n");
307 printf(" -v show verbose messages\n");
308 printf(" -ssh force use of ssh protocol\n");
309 printf(" -P port connect to specified port\n");
310 printf(" -pw passw login with specified password\n");
96621a84 311 printf(" -m file read remote command(s) from file\n");
d8426c54 312 exit(1);
313}
314
32874aea 315char *do_select(SOCKET skt, int startup)
316{
8df7a775 317 int events;
318 if (startup) {
d74d141c 319 events = FD_READ | FD_WRITE | FD_OOB | FD_CLOSE | FD_ACCEPT;
8df7a775 320 } else {
321 events = 0;
322 }
32874aea 323 if (WSAEventSelect(skt, netevent, events) == SOCKET_ERROR) {
324 switch (WSAGetLastError()) {
325 case WSAENETDOWN:
326 return "Network is down";
327 default:
328 return "WSAAsyncSelect(): unknown error";
329 }
8df7a775 330 }
331 return NULL;
332}
333
32874aea 334int main(int argc, char **argv)
335{
12dc4ec0 336 WSADATA wsadata;
337 WORD winsock_ver;
5471d09a 338 WSAEVENT stdinevent, stdoutevent, stderrevent;
339 HANDLE handles[4];
340 DWORD in_threadid, out_threadid, err_threadid;
12dc4ec0 341 struct input_data idata;
5471d09a 342 int reading;
12dc4ec0 343 int sending;
d8426c54 344 int portnumber = -1;
8df7a775 345 SOCKET *sklist;
346 int skcount, sksize;
347 int connopen;
12dc4ec0 348
fa17a66e 349 ssh_get_line = get_line;
67779be7 350
32874aea 351 sklist = NULL;
352 skcount = sksize = 0;
c9bdcd96 353 /*
354 * Initialise port and protocol to sensible defaults. (These
355 * will be overridden by more or less anything.)
356 */
357 default_protocol = PROT_SSH;
358 default_port = 22;
8df7a775 359
67779be7 360 flags = FLAG_STDERR;
12dc4ec0 361 /*
362 * Process the command line.
363 */
a9422f39 364 do_defaults(NULL, &cfg);
e7a7383f 365 default_protocol = cfg.protocol;
366 default_port = cfg.port;
8cb9c947 367 {
32874aea 368 /*
369 * Override the default protocol if PLINK_PROTOCOL is set.
370 */
371 char *p = getenv("PLINK_PROTOCOL");
372 int i;
373 if (p) {
374 for (i = 0; backends[i].backend != NULL; i++) {
375 if (!strcmp(backends[i].name, p)) {
376 default_protocol = cfg.protocol = backends[i].protocol;
377 default_port = cfg.port =
378 backends[i].backend->default_port;
379 break;
380 }
381 }
382 }
8cb9c947 383 }
12dc4ec0 384 while (--argc) {
32874aea 385 char *p = *++argv;
386 if (*p == '-') {
387 if (!strcmp(p, "-ssh")) {
12dc4ec0 388 default_protocol = cfg.protocol = PROT_SSH;
389 default_port = cfg.port = 22;
32874aea 390 } else if (!strcmp(p, "-telnet")) {
9d33ebdd 391 default_protocol = cfg.protocol = PROT_TELNET;
392 default_port = cfg.port = 23;
32874aea 393 } else if (!strcmp(p, "-raw")) {
9d33ebdd 394 default_protocol = cfg.protocol = PROT_RAW;
67779be7 395 } else if (!strcmp(p, "-v")) {
32874aea 396 flags |= FLAG_VERBOSE;
12dc4ec0 397 } else if (!strcmp(p, "-log")) {
32874aea 398 logfile = "putty.log";
399 } else if (!strcmp(p, "-pw") && argc > 1) {
400 --argc, password = *++argv;
401 } else if (!strcmp(p, "-l") && argc > 1) {
402 char *username;
403 --argc, username = *++argv;
404 strncpy(cfg.username, username, sizeof(cfg.username));
405 cfg.username[sizeof(cfg.username) - 1] = '\0';
406 } else if (!strcmp(p, "-m") && argc > 1) {
407 char *filename, *command;
408 int cmdlen, cmdsize;
409 FILE *fp;
410 int c, d;
411
412 --argc, filename = *++argv;
413
414 cmdlen = cmdsize = 0;
415 command = NULL;
416 fp = fopen(filename, "r");
417 if (!fp) {
418 fprintf(stderr, "plink: unable to open command "
419 "file \"%s\"\n", filename);
420 return 1;
421 }
422 do {
423 c = fgetc(fp);
424 d = c;
425 if (c == EOF)
426 d = 0;
427 if (cmdlen >= cmdsize) {
428 cmdsize = cmdlen + 512;
429 command = srealloc(command, cmdsize);
430 }
431 command[cmdlen++] = d;
432 } while (c != EOF);
433 cfg.remote_cmd_ptr = command;
434 cfg.nopty = TRUE; /* command => no terminal */
435 } else if (!strcmp(p, "-P") && argc > 1) {
436 --argc, portnumber = atoi(*++argv);
437 }
12dc4ec0 438 } else if (*p) {
32874aea 439 if (!*cfg.host) {
440 char *q = p;
441 /*
442 * If the hostname starts with "telnet:", set the
443 * protocol to Telnet and process the string as a
444 * Telnet URL.
445 */
446 if (!strncmp(q, "telnet:", 7)) {
447 char c;
448
449 q += 7;
450 if (q[0] == '/' && q[1] == '/')
451 q += 2;
452 cfg.protocol = PROT_TELNET;
453 p = q;
454 while (*p && *p != ':' && *p != '/')
455 p++;
456 c = *p;
457 if (*p)
458 *p++ = '\0';
459 if (c == ':')
460 cfg.port = atoi(p);
461 else
462 cfg.port = -1;
463 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
464 cfg.host[sizeof(cfg.host) - 1] = '\0';
465 } else {
466 char *r;
467 /*
468 * Before we process the [user@]host string, we
469 * first check for the presence of a protocol
470 * prefix (a protocol name followed by ",").
471 */
472 r = strchr(p, ',');
473 if (r) {
474 int i, j;
475 for (i = 0; backends[i].backend != NULL; i++) {
476 j = strlen(backends[i].name);
477 if (j == r - p &&
478 !memcmp(backends[i].name, p, j)) {
479 default_protocol = cfg.protocol =
480 backends[i].protocol;
481 portnumber =
482 backends[i].backend->default_port;
483 p = r + 1;
484 break;
485 }
486 }
487 }
488
489 /*
490 * Three cases. Either (a) there's a nonzero
491 * length string followed by an @, in which
492 * case that's user and the remainder is host.
493 * Or (b) there's only one string, not counting
494 * a potential initial @, and it exists in the
495 * saved-sessions database. Or (c) only one
496 * string and it _doesn't_ exist in the
497 * database.
498 */
499 r = strrchr(p, '@');
500 if (r == p)
501 p++, r = NULL; /* discount initial @ */
502 if (r == NULL) {
503 /*
504 * One string.
505 */
506 Config cfg2;
507 do_defaults(p, &cfg2);
508 if (cfg2.host[0] == '\0') {
509 /* No settings for this host; use defaults */
510 strncpy(cfg.host, p, sizeof(cfg.host) - 1);
511 cfg.host[sizeof(cfg.host) - 1] = '\0';
512 cfg.port = default_port;
513 } else {
514 cfg = cfg2;
515 cfg.remote_cmd_ptr = cfg.remote_cmd;
516 }
517 } else {
518 *r++ = '\0';
519 strncpy(cfg.username, p, sizeof(cfg.username) - 1);
520 cfg.username[sizeof(cfg.username) - 1] = '\0';
521 strncpy(cfg.host, r, sizeof(cfg.host) - 1);
522 cfg.host[sizeof(cfg.host) - 1] = '\0';
523 cfg.port = default_port;
524 }
525 }
526 } else {
527 int len = sizeof(cfg.remote_cmd) - 1;
528 char *cp = cfg.remote_cmd;
529 int len2;
530
531 strncpy(cp, p, len);
532 cp[len] = '\0';
533 len2 = strlen(cp);
534 len -= len2;
535 cp += len2;
536 while (--argc) {
537 if (len > 0)
538 len--, *cp++ = ' ';
539 strncpy(cp, *++argv, len);
540 cp[len] = '\0';
541 len2 = strlen(cp);
542 len -= len2;
543 cp += len2;
544 }
545 cfg.nopty = TRUE; /* command => no terminal */
546 break; /* done with cmdline */
547 }
12dc4ec0 548 }
549 }
550
d8426c54 551 if (!*cfg.host) {
32874aea 552 usage();
d8426c54 553 }
d8426c54 554
96621a84 555 if (!*cfg.remote_cmd_ptr)
32874aea 556 flags |= FLAG_INTERACTIVE;
67779be7 557
12dc4ec0 558 /*
559 * Select protocol. This is farmed out into a table in a
560 * separate file to enable an ssh-free variant.
561 */
562 {
32874aea 563 int i;
564 back = NULL;
565 for (i = 0; backends[i].backend != NULL; i++)
566 if (backends[i].protocol == cfg.protocol) {
567 back = backends[i].backend;
568 break;
569 }
570 if (back == NULL) {
571 fprintf(stderr,
572 "Internal fault: Unsupported protocol found\n");
573 return 1;
574 }
12dc4ec0 575 }
576
577 /*
8cb9c947 578 * Select port.
579 */
580 if (portnumber != -1)
32874aea 581 cfg.port = portnumber;
8cb9c947 582
583 /*
12dc4ec0 584 * Initialise WinSock.
585 */
586 winsock_ver = MAKEWORD(2, 0);
587 if (WSAStartup(winsock_ver, &wsadata)) {
588 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
589 MB_OK | MB_ICONEXCLAMATION);
590 return 1;
591 }
592 if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 0) {
593 MessageBox(NULL, "WinSock version is incompatible with 2.0",
594 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
595 WSACleanup();
596 return 1;
597 }
8df7a775 598 sk_init();
12dc4ec0 599
600 /*
601 * Start up the connection.
602 */
8df7a775 603 netevent = CreateEvent(NULL, FALSE, FALSE, NULL);
12dc4ec0 604 {
605 char *error;
606 char *realhost;
607
32874aea 608 error = back->init(cfg.host, cfg.port, &realhost);
12dc4ec0 609 if (error) {
610 fprintf(stderr, "Unable to open connection:\n%s", error);
611 return 1;
612 }
6e1ebb76 613 sfree(realhost);
12dc4ec0 614 }
8df7a775 615 connopen = 1;
12dc4ec0 616
12dc4ec0 617 stdinevent = CreateEvent(NULL, FALSE, FALSE, NULL);
5471d09a 618 stdoutevent = CreateEvent(NULL, FALSE, FALSE, NULL);
619 stderrevent = CreateEvent(NULL, FALSE, FALSE, NULL);
12dc4ec0 620
0965bee0 621 inhandle = GetStdHandle(STD_INPUT_HANDLE);
12dc4ec0 622 outhandle = GetStdHandle(STD_OUTPUT_HANDLE);
fe50e814 623 errhandle = GetStdHandle(STD_ERROR_HANDLE);
0965bee0 624 GetConsoleMode(inhandle, &orig_console_mode);
625 SetConsoleMode(inhandle, ENABLE_PROCESSED_INPUT);
12dc4ec0 626
627 /*
12dc4ec0 628 * Turn off ECHO and LINE input modes. We don't care if this
629 * call fails, because we know we aren't necessarily running in
630 * a console.
631 */
12dc4ec0 632 handles[0] = netevent;
633 handles[1] = stdinevent;
5471d09a 634 handles[2] = stdoutevent;
635 handles[3] = stderrevent;
12dc4ec0 636 sending = FALSE;
5471d09a 637
638 /*
639 * Create spare threads to write to stdout and stderr, so we
640 * can arrange asynchronous writes.
641 */
642 odata.event = stdoutevent;
643 odata.eventback = CreateEvent(NULL, FALSE, FALSE, NULL);
644 odata.is_stderr = 0;
645 odata.busy = odata.done = 0;
646 if (!CreateThread(NULL, 0, stdout_write_thread,
647 &odata, 0, &out_threadid)) {
648 fprintf(stderr, "Unable to create output thread\n");
649 exit(1);
650 }
651 edata.event = stderrevent;
652 edata.eventback = CreateEvent(NULL, FALSE, FALSE, NULL);
653 edata.is_stderr = 1;
654 edata.busy = edata.done = 0;
655 if (!CreateThread(NULL, 0, stdout_write_thread,
656 &edata, 0, &err_threadid)) {
657 fprintf(stderr, "Unable to create error output thread\n");
658 exit(1);
659 }
660
12dc4ec0 661 while (1) {
32874aea 662 int n;
663
664 if (!sending && back->sendok()) {
665 /*
666 * Create a separate thread to read from stdin. This is
667 * a total pain, but I can't find another way to do it:
668 *
669 * - an overlapped ReadFile or ReadFileEx just doesn't
670 * happen; we get failure from ReadFileEx, and
671 * ReadFile blocks despite being given an OVERLAPPED
672 * structure. Perhaps we can't do overlapped reads
673 * on consoles. WHY THE HELL NOT?
674 *
675 * - WaitForMultipleObjects(netevent, console) doesn't
676 * work, because it signals the console when
677 * _anything_ happens, including mouse motions and
678 * other things that don't cause data to be readable
679 * - so we're back to ReadFile blocking.
680 */
681 idata.event = stdinevent;
682 idata.eventback = CreateEvent(NULL, FALSE, FALSE, NULL);
683 if (!CreateThread(NULL, 0, stdin_read_thread,
5471d09a 684 &idata, 0, &in_threadid)) {
685 fprintf(stderr, "Unable to create input thread\n");
32874aea 686 exit(1);
687 }
688 sending = TRUE;
689 }
690
5471d09a 691 n = WaitForMultipleObjects(4, handles, FALSE, INFINITE);
32874aea 692 if (n == 0) {
693 WSANETWORKEVENTS things;
8df7a775 694 SOCKET socket;
d2371c81 695 extern SOCKET first_socket(int *), next_socket(int *);
8df7a775 696 extern int select_result(WPARAM, LPARAM);
32874aea 697 int i, socketstate;
698
699 /*
700 * We must not call select_result() for any socket
701 * until we have finished enumerating within the tree.
702 * This is because select_result() may close the socket
703 * and modify the tree.
704 */
705 /* Count the active sockets. */
706 i = 0;
707 for (socket = first_socket(&socketstate);
708 socket != INVALID_SOCKET;
709 socket = next_socket(&socketstate)) i++;
710
711 /* Expand the buffer if necessary. */
712 if (i > sksize) {
713 sksize = i + 16;
714 sklist = srealloc(sklist, sksize * sizeof(*sklist));
715 }
716
717 /* Retrieve the sockets into sklist. */
718 skcount = 0;
719 for (socket = first_socket(&socketstate);
720 socket != INVALID_SOCKET;
d2371c81 721 socket = next_socket(&socketstate)) {
32874aea 722 sklist[skcount++] = socket;
723 }
724
725 /* Now we're done enumerating; go through the list. */
726 for (i = 0; i < skcount; i++) {
727 WPARAM wp;
728 socket = sklist[i];
729 wp = (WPARAM) socket;
ffb959c7 730 if (!WSAEnumNetworkEvents(socket, NULL, &things)) {
32874aea 731 noise_ultralight(socket);
732 noise_ultralight(things.lNetworkEvents);
8df7a775 733 if (things.lNetworkEvents & FD_READ)
32874aea 734 connopen &= select_result(wp, (LPARAM) FD_READ);
8df7a775 735 if (things.lNetworkEvents & FD_CLOSE)
32874aea 736 connopen &= select_result(wp, (LPARAM) FD_CLOSE);
8df7a775 737 if (things.lNetworkEvents & FD_OOB)
32874aea 738 connopen &= select_result(wp, (LPARAM) FD_OOB);
8df7a775 739 if (things.lNetworkEvents & FD_WRITE)
32874aea 740 connopen &= select_result(wp, (LPARAM) FD_WRITE);
d74d141c 741 if (things.lNetworkEvents & FD_ACCEPT)
742 connopen &= select_result(wp, (LPARAM) FD_ACCEPT);
743
8df7a775 744 }
745 }
32874aea 746 } else if (n == 1) {
5471d09a 747 reading = 0;
32874aea 748 noise_ultralight(idata.len);
749 if (idata.len > 0) {
750 back->send(idata.buffer, idata.len);
751 } else {
752 back->special(TS_EOF);
753 }
5471d09a 754 } else if (n == 2) {
755 odata.busy = 0;
756 if (!odata.writeret) {
757 fprintf(stderr, "Unable to write to standard output\n");
758 exit(0);
759 }
760 bufchain_consume(&stdout_data, odata.lenwritten);
761 if (bufchain_size(&stdout_data) > 0)
762 try_output(0);
763 back->unthrottle(bufchain_size(&stdout_data) +
764 bufchain_size(&stderr_data));
765 } else if (n == 3) {
766 edata.busy = 0;
767 if (!edata.writeret) {
768 fprintf(stderr, "Unable to write to standard output\n");
769 exit(0);
770 }
771 bufchain_consume(&stderr_data, edata.lenwritten);
772 if (bufchain_size(&stderr_data) > 0)
773 try_output(1);
774 back->unthrottle(bufchain_size(&stdout_data) +
775 bufchain_size(&stderr_data));
776 }
777 if (!reading && back->sendbuffer() < MAX_STDIN_BACKLOG) {
32874aea 778 SetEvent(idata.eventback);
5471d09a 779 reading = 1;
32874aea 780 }
781 if (!connopen || back->socket() == NULL)
782 break; /* we closed the connection */
12dc4ec0 783 }
784 WSACleanup();
785 return 0;
786}