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