9c0a14a191e8113fe240d253db80e4f6e7dad1a3
[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(" (\"host\" can also be a PuTTY saved session name)\n");
343 printf("Options:\n");
344 printf(" -v show verbose messages\n");
345 printf(" -ssh force use of ssh protocol\n");
346 printf(" -P port connect to specified port\n");
347 printf(" -pw passw login with specified password\n");
348 printf(" -m file read remote command(s) from file\n");
349 exit(1);
350 }
351
352 char *do_select(SOCKET skt, int startup)
353 {
354 int events;
355 if (startup) {
356 events = (FD_CONNECT | FD_READ | FD_WRITE |
357 FD_OOB | FD_CLOSE | FD_ACCEPT);
358 } else {
359 events = 0;
360 }
361 if (WSAEventSelect(skt, netevent, events) == SOCKET_ERROR) {
362 switch (WSAGetLastError()) {
363 case WSAENETDOWN:
364 return "Network is down";
365 default:
366 return "WSAAsyncSelect(): unknown error";
367 }
368 }
369 return NULL;
370 }
371
372 int main(int argc, char **argv)
373 {
374 WSADATA wsadata;
375 WORD winsock_ver;
376 WSAEVENT stdinevent, stdoutevent, stderrevent;
377 HANDLE handles[4];
378 DWORD in_threadid, out_threadid, err_threadid;
379 struct input_data idata;
380 int reading;
381 int sending;
382 int portnumber = -1;
383 SOCKET *sklist;
384 int skcount, sksize;
385 int connopen;
386
387 ssh_get_line = get_line;
388
389 sklist = NULL;
390 skcount = sksize = 0;
391 /*
392 * Initialise port and protocol to sensible defaults. (These
393 * will be overridden by more or less anything.)
394 */
395 default_protocol = PROT_SSH;
396 default_port = 22;
397
398 flags = FLAG_STDERR;
399 /*
400 * Process the command line.
401 */
402 do_defaults(NULL, &cfg);
403 default_protocol = cfg.protocol;
404 default_port = cfg.port;
405 {
406 /*
407 * Override the default protocol if PLINK_PROTOCOL is set.
408 */
409 char *p = getenv("PLINK_PROTOCOL");
410 int i;
411 if (p) {
412 for (i = 0; backends[i].backend != NULL; i++) {
413 if (!strcmp(backends[i].name, p)) {
414 default_protocol = cfg.protocol = backends[i].protocol;
415 default_port = cfg.port =
416 backends[i].backend->default_port;
417 break;
418 }
419 }
420 }
421 }
422 while (--argc) {
423 char *p = *++argv;
424 if (*p == '-') {
425 if (!strcmp(p, "-ssh")) {
426 default_protocol = cfg.protocol = PROT_SSH;
427 default_port = cfg.port = 22;
428 } else if (!strcmp(p, "-telnet")) {
429 default_protocol = cfg.protocol = PROT_TELNET;
430 default_port = cfg.port = 23;
431 } else if (!strcmp(p, "-raw")) {
432 default_protocol = cfg.protocol = PROT_RAW;
433 } else if (!strcmp(p, "-v")) {
434 flags |= FLAG_VERBOSE;
435 } else if (!strcmp(p, "-log")) {
436 logfile = "putty.log";
437 } else if (!strcmp(p, "-pw") && argc > 1) {
438 --argc, password = *++argv;
439 } else if (!strcmp(p, "-l") && argc > 1) {
440 char *username;
441 --argc, username = *++argv;
442 strncpy(cfg.username, username, sizeof(cfg.username));
443 cfg.username[sizeof(cfg.username) - 1] = '\0';
444 } else if (!strcmp(p, "-m") && argc > 1) {
445 char *filename, *command;
446 int cmdlen, cmdsize;
447 FILE *fp;
448 int c, d;
449
450 --argc, filename = *++argv;
451
452 cmdlen = cmdsize = 0;
453 command = NULL;
454 fp = fopen(filename, "r");
455 if (!fp) {
456 fprintf(stderr, "plink: unable to open command "
457 "file \"%s\"\n", filename);
458 return 1;
459 }
460 do {
461 c = fgetc(fp);
462 d = c;
463 if (c == EOF)
464 d = 0;
465 if (cmdlen >= cmdsize) {
466 cmdsize = cmdlen + 512;
467 command = srealloc(command, cmdsize);
468 }
469 command[cmdlen++] = d;
470 } while (c != EOF);
471 cfg.remote_cmd_ptr = command;
472 cfg.remote_cmd_ptr2 = NULL;
473 cfg.nopty = TRUE; /* command => no terminal */
474 } else if (!strcmp(p, "-P") && argc > 1) {
475 --argc, portnumber = atoi(*++argv);
476 }
477 } else if (*p) {
478 if (!*cfg.host) {
479 char *q = p;
480 /*
481 * If the hostname starts with "telnet:", set the
482 * protocol to Telnet and process the string as a
483 * Telnet URL.
484 */
485 if (!strncmp(q, "telnet:", 7)) {
486 char c;
487
488 q += 7;
489 if (q[0] == '/' && q[1] == '/')
490 q += 2;
491 cfg.protocol = PROT_TELNET;
492 p = q;
493 while (*p && *p != ':' && *p != '/')
494 p++;
495 c = *p;
496 if (*p)
497 *p++ = '\0';
498 if (c == ':')
499 cfg.port = atoi(p);
500 else
501 cfg.port = -1;
502 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
503 cfg.host[sizeof(cfg.host) - 1] = '\0';
504 } else {
505 char *r;
506 /*
507 * Before we process the [user@]host string, we
508 * first check for the presence of a protocol
509 * prefix (a protocol name followed by ",").
510 */
511 r = strchr(p, ',');
512 if (r) {
513 int i, j;
514 for (i = 0; backends[i].backend != NULL; i++) {
515 j = strlen(backends[i].name);
516 if (j == r - p &&
517 !memcmp(backends[i].name, p, j)) {
518 default_protocol = cfg.protocol =
519 backends[i].protocol;
520 portnumber =
521 backends[i].backend->default_port;
522 p = r + 1;
523 break;
524 }
525 }
526 }
527
528 /*
529 * Three cases. Either (a) there's a nonzero
530 * length string followed by an @, in which
531 * case that's user and the remainder is host.
532 * Or (b) there's only one string, not counting
533 * a potential initial @, and it exists in the
534 * saved-sessions database. Or (c) only one
535 * string and it _doesn't_ exist in the
536 * database.
537 */
538 r = strrchr(p, '@');
539 if (r == p)
540 p++, r = NULL; /* discount initial @ */
541 if (r == NULL) {
542 /*
543 * One string.
544 */
545 Config cfg2;
546 do_defaults(p, &cfg2);
547 if (cfg2.host[0] == '\0') {
548 /* No settings for this host; use defaults */
549 strncpy(cfg.host, p, sizeof(cfg.host) - 1);
550 cfg.host[sizeof(cfg.host) - 1] = '\0';
551 cfg.port = default_port;
552 } else {
553 cfg = cfg2;
554 cfg.remote_cmd_ptr = cfg.remote_cmd;
555 }
556 } else {
557 *r++ = '\0';
558 strncpy(cfg.username, p, sizeof(cfg.username) - 1);
559 cfg.username[sizeof(cfg.username) - 1] = '\0';
560 strncpy(cfg.host, r, sizeof(cfg.host) - 1);
561 cfg.host[sizeof(cfg.host) - 1] = '\0';
562 cfg.port = default_port;
563 }
564 }
565 } else {
566 int len = sizeof(cfg.remote_cmd) - 1;
567 char *cp = cfg.remote_cmd;
568 int len2;
569
570 strncpy(cp, p, len);
571 cp[len] = '\0';
572 len2 = strlen(cp);
573 len -= len2;
574 cp += len2;
575 while (--argc) {
576 if (len > 0)
577 len--, *cp++ = ' ';
578 strncpy(cp, *++argv, len);
579 cp[len] = '\0';
580 len2 = strlen(cp);
581 len -= len2;
582 cp += len2;
583 }
584 cfg.nopty = TRUE; /* command => no terminal */
585 break; /* done with cmdline */
586 }
587 }
588 }
589
590 if (!*cfg.host) {
591 usage();
592 }
593
594 if (!*cfg.remote_cmd_ptr)
595 flags |= FLAG_INTERACTIVE;
596
597 /*
598 * Select protocol. This is farmed out into a table in a
599 * separate file to enable an ssh-free variant.
600 */
601 {
602 int i;
603 back = NULL;
604 for (i = 0; backends[i].backend != NULL; i++)
605 if (backends[i].protocol == cfg.protocol) {
606 back = backends[i].backend;
607 break;
608 }
609 if (back == NULL) {
610 fprintf(stderr,
611 "Internal fault: Unsupported protocol found\n");
612 return 1;
613 }
614 }
615
616 /*
617 * Select port.
618 */
619 if (portnumber != -1)
620 cfg.port = portnumber;
621
622 /*
623 * Initialise WinSock.
624 */
625 winsock_ver = MAKEWORD(2, 0);
626 if (WSAStartup(winsock_ver, &wsadata)) {
627 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
628 MB_OK | MB_ICONEXCLAMATION);
629 return 1;
630 }
631 if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 0) {
632 MessageBox(NULL, "WinSock version is incompatible with 2.0",
633 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
634 WSACleanup();
635 return 1;
636 }
637 sk_init();
638
639 /*
640 * Start up the connection.
641 */
642 netevent = CreateEvent(NULL, FALSE, FALSE, NULL);
643 {
644 char *error;
645 char *realhost;
646
647 error = back->init(cfg.host, cfg.port, &realhost);
648 if (error) {
649 fprintf(stderr, "Unable to open connection:\n%s", error);
650 return 1;
651 }
652 sfree(realhost);
653 }
654 connopen = 1;
655
656 stdinevent = CreateEvent(NULL, FALSE, FALSE, NULL);
657 stdoutevent = CreateEvent(NULL, FALSE, FALSE, NULL);
658 stderrevent = CreateEvent(NULL, FALSE, FALSE, NULL);
659
660 inhandle = GetStdHandle(STD_INPUT_HANDLE);
661 outhandle = GetStdHandle(STD_OUTPUT_HANDLE);
662 errhandle = GetStdHandle(STD_ERROR_HANDLE);
663 GetConsoleMode(inhandle, &orig_console_mode);
664 SetConsoleMode(inhandle, ENABLE_PROCESSED_INPUT);
665
666 /*
667 * Turn off ECHO and LINE input modes. We don't care if this
668 * call fails, because we know we aren't necessarily running in
669 * a console.
670 */
671 handles[0] = netevent;
672 handles[1] = stdinevent;
673 handles[2] = stdoutevent;
674 handles[3] = stderrevent;
675 sending = FALSE;
676
677 /*
678 * Create spare threads to write to stdout and stderr, so we
679 * can arrange asynchronous writes.
680 */
681 odata.event = stdoutevent;
682 odata.eventback = CreateEvent(NULL, FALSE, FALSE, NULL);
683 odata.is_stderr = 0;
684 odata.busy = odata.done = 0;
685 if (!CreateThread(NULL, 0, stdout_write_thread,
686 &odata, 0, &out_threadid)) {
687 fprintf(stderr, "Unable to create output thread\n");
688 exit(1);
689 }
690 edata.event = stderrevent;
691 edata.eventback = CreateEvent(NULL, FALSE, FALSE, NULL);
692 edata.is_stderr = 1;
693 edata.busy = edata.done = 0;
694 if (!CreateThread(NULL, 0, stdout_write_thread,
695 &edata, 0, &err_threadid)) {
696 fprintf(stderr, "Unable to create error output thread\n");
697 exit(1);
698 }
699
700 while (1) {
701 int n;
702
703 if (!sending && back->sendok()) {
704 /*
705 * Create a separate thread to read from stdin. This is
706 * a total pain, but I can't find another way to do it:
707 *
708 * - an overlapped ReadFile or ReadFileEx just doesn't
709 * happen; we get failure from ReadFileEx, and
710 * ReadFile blocks despite being given an OVERLAPPED
711 * structure. Perhaps we can't do overlapped reads
712 * on consoles. WHY THE HELL NOT?
713 *
714 * - WaitForMultipleObjects(netevent, console) doesn't
715 * work, because it signals the console when
716 * _anything_ happens, including mouse motions and
717 * other things that don't cause data to be readable
718 * - so we're back to ReadFile blocking.
719 */
720 idata.event = stdinevent;
721 idata.eventback = CreateEvent(NULL, FALSE, FALSE, NULL);
722 if (!CreateThread(NULL, 0, stdin_read_thread,
723 &idata, 0, &in_threadid)) {
724 fprintf(stderr, "Unable to create input thread\n");
725 exit(1);
726 }
727 sending = TRUE;
728 }
729
730 n = WaitForMultipleObjects(4, handles, FALSE, INFINITE);
731 if (n == 0) {
732 WSANETWORKEVENTS things;
733 SOCKET socket;
734 extern SOCKET first_socket(int *), next_socket(int *);
735 extern int select_result(WPARAM, LPARAM);
736 int i, socketstate;
737
738 /*
739 * We must not call select_result() for any socket
740 * until we have finished enumerating within the tree.
741 * This is because select_result() may close the socket
742 * and modify the tree.
743 */
744 /* Count the active sockets. */
745 i = 0;
746 for (socket = first_socket(&socketstate);
747 socket != INVALID_SOCKET;
748 socket = next_socket(&socketstate)) i++;
749
750 /* Expand the buffer if necessary. */
751 if (i > sksize) {
752 sksize = i + 16;
753 sklist = srealloc(sklist, sksize * sizeof(*sklist));
754 }
755
756 /* Retrieve the sockets into sklist. */
757 skcount = 0;
758 for (socket = first_socket(&socketstate);
759 socket != INVALID_SOCKET;
760 socket = next_socket(&socketstate)) {
761 sklist[skcount++] = socket;
762 }
763
764 /* Now we're done enumerating; go through the list. */
765 for (i = 0; i < skcount; i++) {
766 WPARAM wp;
767 socket = sklist[i];
768 wp = (WPARAM) socket;
769 if (!WSAEnumNetworkEvents(socket, NULL, &things)) {
770 noise_ultralight(socket);
771 noise_ultralight(things.lNetworkEvents);
772 if (things.lNetworkEvents & FD_CONNECT)
773 connopen &= select_result(wp, (LPARAM) FD_CONNECT);
774 if (things.lNetworkEvents & FD_READ)
775 connopen &= select_result(wp, (LPARAM) FD_READ);
776 if (things.lNetworkEvents & FD_CLOSE)
777 connopen &= select_result(wp, (LPARAM) FD_CLOSE);
778 if (things.lNetworkEvents & FD_OOB)
779 connopen &= select_result(wp, (LPARAM) FD_OOB);
780 if (things.lNetworkEvents & FD_WRITE)
781 connopen &= select_result(wp, (LPARAM) FD_WRITE);
782 if (things.lNetworkEvents & FD_ACCEPT)
783 connopen &= select_result(wp, (LPARAM) FD_ACCEPT);
784
785 }
786 }
787 } else if (n == 1) {
788 reading = 0;
789 noise_ultralight(idata.len);
790 if (idata.len > 0) {
791 back->send(idata.buffer, idata.len);
792 } else {
793 back->special(TS_EOF);
794 }
795 } else if (n == 2) {
796 odata.busy = 0;
797 if (!odata.writeret) {
798 fprintf(stderr, "Unable to write to standard output\n");
799 exit(0);
800 }
801 bufchain_consume(&stdout_data, odata.lenwritten);
802 if (bufchain_size(&stdout_data) > 0)
803 try_output(0);
804 back->unthrottle(bufchain_size(&stdout_data) +
805 bufchain_size(&stderr_data));
806 } else if (n == 3) {
807 edata.busy = 0;
808 if (!edata.writeret) {
809 fprintf(stderr, "Unable to write to standard output\n");
810 exit(0);
811 }
812 bufchain_consume(&stderr_data, edata.lenwritten);
813 if (bufchain_size(&stderr_data) > 0)
814 try_output(1);
815 back->unthrottle(bufchain_size(&stdout_data) +
816 bufchain_size(&stderr_data));
817 }
818 if (!reading && back->sendbuffer() < MAX_STDIN_BACKLOG) {
819 SetEvent(idata.eventback);
820 reading = 1;
821 }
822 if (!connopen || back->socket() == NULL)
823 break; /* we closed the connection */
824 }
825 WSACleanup();
826 return 0;
827 }