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