OSDN Git Service

Add PuTTY 0.61 to contrib directory.
[ffftp/ffftp.git] / contrib / putty / WINDOWS / WINPLINK.C
1 /*\r
2  * PLink - a Windows command-line (stdin/stdout) variant of PuTTY.\r
3  */\r
4 \r
5 #include <stdio.h>\r
6 #include <stdlib.h>\r
7 #include <assert.h>\r
8 #include <stdarg.h>\r
9 \r
10 #define PUTTY_DO_GLOBALS               /* actually _define_ globals */\r
11 #include "putty.h"\r
12 #include "storage.h"\r
13 #include "tree234.h"\r
14 \r
15 #define WM_AGENT_CALLBACK (WM_APP + 4)\r
16 \r
17 struct agent_callback {\r
18     void (*callback)(void *, void *, int);\r
19     void *callback_ctx;\r
20     void *data;\r
21     int len;\r
22 };\r
23 \r
24 void fatalbox(char *p, ...)\r
25 {\r
26     va_list ap;\r
27     fprintf(stderr, "FATAL ERROR: ");\r
28     va_start(ap, p);\r
29     vfprintf(stderr, p, ap);\r
30     va_end(ap);\r
31     fputc('\n', stderr);\r
32     if (logctx) {\r
33         log_free(logctx);\r
34         logctx = NULL;\r
35     }\r
36     cleanup_exit(1);\r
37 }\r
38 void modalfatalbox(char *p, ...)\r
39 {\r
40     va_list ap;\r
41     fprintf(stderr, "FATAL ERROR: ");\r
42     va_start(ap, p);\r
43     vfprintf(stderr, p, ap);\r
44     va_end(ap);\r
45     fputc('\n', stderr);\r
46     if (logctx) {\r
47         log_free(logctx);\r
48         logctx = NULL;\r
49     }\r
50     cleanup_exit(1);\r
51 }\r
52 void connection_fatal(void *frontend, char *p, ...)\r
53 {\r
54     va_list ap;\r
55     fprintf(stderr, "FATAL ERROR: ");\r
56     va_start(ap, p);\r
57     vfprintf(stderr, p, ap);\r
58     va_end(ap);\r
59     fputc('\n', stderr);\r
60     if (logctx) {\r
61         log_free(logctx);\r
62         logctx = NULL;\r
63     }\r
64     cleanup_exit(1);\r
65 }\r
66 void cmdline_error(char *p, ...)\r
67 {\r
68     va_list ap;\r
69     fprintf(stderr, "plink: ");\r
70     va_start(ap, p);\r
71     vfprintf(stderr, p, ap);\r
72     va_end(ap);\r
73     fputc('\n', stderr);\r
74     exit(1);\r
75 }\r
76 \r
77 HANDLE inhandle, outhandle, errhandle;\r
78 struct handle *stdin_handle, *stdout_handle, *stderr_handle;\r
79 DWORD orig_console_mode;\r
80 int connopen;\r
81 \r
82 WSAEVENT netevent;\r
83 \r
84 static Backend *back;\r
85 static void *backhandle;\r
86 static Config cfg;\r
87 \r
88 int term_ldisc(Terminal *term, int mode)\r
89 {\r
90     return FALSE;\r
91 }\r
92 void ldisc_update(void *frontend, int echo, int edit)\r
93 {\r
94     /* Update stdin read mode to reflect changes in line discipline. */\r
95     DWORD mode;\r
96 \r
97     mode = ENABLE_PROCESSED_INPUT;\r
98     if (echo)\r
99         mode = mode | ENABLE_ECHO_INPUT;\r
100     else\r
101         mode = mode & ~ENABLE_ECHO_INPUT;\r
102     if (edit)\r
103         mode = mode | ENABLE_LINE_INPUT;\r
104     else\r
105         mode = mode & ~ENABLE_LINE_INPUT;\r
106     SetConsoleMode(inhandle, mode);\r
107 }\r
108 \r
109 char *get_ttymode(void *frontend, const char *mode) { return NULL; }\r
110 \r
111 int from_backend(void *frontend_handle, int is_stderr,\r
112                  const char *data, int len)\r
113 {\r
114     if (is_stderr) {\r
115         handle_write(stderr_handle, data, len);\r
116     } else {\r
117         handle_write(stdout_handle, data, len);\r
118     }\r
119 \r
120     return handle_backlog(stdout_handle) + handle_backlog(stderr_handle);\r
121 }\r
122 \r
123 int from_backend_untrusted(void *frontend_handle, const char *data, int len)\r
124 {\r
125     /*\r
126      * No "untrusted" output should get here (the way the code is\r
127      * currently, it's all diverted by FLAG_STDERR).\r
128      */\r
129     assert(!"Unexpected call to from_backend_untrusted()");\r
130     return 0; /* not reached */\r
131 }\r
132 \r
133 int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)\r
134 {\r
135     int ret;\r
136     ret = cmdline_get_passwd_input(p, in, inlen);\r
137     if (ret == -1)\r
138         ret = console_get_userpass_input(p, in, inlen);\r
139     return ret;\r
140 }\r
141 \r
142 static DWORD main_thread_id;\r
143 \r
144 void agent_schedule_callback(void (*callback)(void *, void *, int),\r
145                              void *callback_ctx, void *data, int len)\r
146 {\r
147     struct agent_callback *c = snew(struct agent_callback);\r
148     c->callback = callback;\r
149     c->callback_ctx = callback_ctx;\r
150     c->data = data;\r
151     c->len = len;\r
152     PostThreadMessage(main_thread_id, WM_AGENT_CALLBACK, 0, (LPARAM)c);\r
153 }\r
154 \r
155 /*\r
156  *  Short description of parameters.\r
157  */\r
158 static void usage(void)\r
159 {\r
160     printf("PuTTY Link: command-line connection utility\n");\r
161     printf("%s\n", ver);\r
162     printf("Usage: plink [options] [user@]host [command]\n");\r
163     printf("       (\"host\" can also be a PuTTY saved session name)\n");\r
164     printf("Options:\n");\r
165     printf("  -V        print version information and exit\n");\r
166     printf("  -pgpfp    print PGP key fingerprints and exit\n");\r
167     printf("  -v        show verbose messages\n");\r
168     printf("  -load sessname  Load settings from saved session\n");\r
169     printf("  -ssh -telnet -rlogin -raw -serial\n");\r
170     printf("            force use of a particular protocol\n");\r
171     printf("  -P port   connect to specified port\n");\r
172     printf("  -l user   connect with specified username\n");\r
173     printf("  -batch    disable all interactive prompts\n");\r
174     printf("The following options only apply to SSH connections:\n");\r
175     printf("  -pw passw login with specified password\n");\r
176     printf("  -D [listen-IP:]listen-port\n");\r
177     printf("            Dynamic SOCKS-based port forwarding\n");\r
178     printf("  -L [listen-IP:]listen-port:host:port\n");\r
179     printf("            Forward local port to remote address\n");\r
180     printf("  -R [listen-IP:]listen-port:host:port\n");\r
181     printf("            Forward remote port to local address\n");\r
182     printf("  -X -x     enable / disable X11 forwarding\n");\r
183     printf("  -A -a     enable / disable agent forwarding\n");\r
184     printf("  -t -T     enable / disable pty allocation\n");\r
185     printf("  -1 -2     force use of particular protocol version\n");\r
186     printf("  -4 -6     force use of IPv4 or IPv6\n");\r
187     printf("  -C        enable compression\n");\r
188     printf("  -i key    private key file for authentication\n");\r
189     printf("  -noagent  disable use of Pageant\n");\r
190     printf("  -agent    enable use of Pageant\n");\r
191     printf("  -m file   read remote command(s) from file\n");\r
192     printf("  -s        remote command is an SSH subsystem (SSH-2 only)\n");\r
193     printf("  -N        don't start a shell/command (SSH-2 only)\n");\r
194     printf("  -nc host:port\n");\r
195     printf("            open tunnel in place of session (SSH-2 only)\n");\r
196     printf("  -sercfg configuration-string (e.g. 19200,8,n,1,X)\n");\r
197     printf("            Specify the serial configuration (serial only)\n");\r
198     exit(1);\r
199 }\r
200 \r
201 static void version(void)\r
202 {\r
203     printf("plink: %s\n", ver);\r
204     exit(1);\r
205 }\r
206 \r
207 char *do_select(SOCKET skt, int startup)\r
208 {\r
209     int events;\r
210     if (startup) {\r
211         events = (FD_CONNECT | FD_READ | FD_WRITE |\r
212                   FD_OOB | FD_CLOSE | FD_ACCEPT);\r
213     } else {\r
214         events = 0;\r
215     }\r
216     if (p_WSAEventSelect(skt, netevent, events) == SOCKET_ERROR) {\r
217         switch (p_WSAGetLastError()) {\r
218           case WSAENETDOWN:\r
219             return "Network is down";\r
220           default:\r
221             return "WSAEventSelect(): unknown error";\r
222         }\r
223     }\r
224     return NULL;\r
225 }\r
226 \r
227 int stdin_gotdata(struct handle *h, void *data, int len)\r
228 {\r
229     if (len < 0) {\r
230         /*\r
231          * Special case: report read error.\r
232          */\r
233         char buf[4096];\r
234         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, -len, 0,\r
235                       buf, lenof(buf), NULL);\r
236         buf[lenof(buf)-1] = '\0';\r
237         if (buf[strlen(buf)-1] == '\n')\r
238             buf[strlen(buf)-1] = '\0';\r
239         fprintf(stderr, "Unable to read from standard input: %s\n", buf);\r
240         cleanup_exit(0);\r
241     }\r
242     noise_ultralight(len);\r
243     if (connopen && back->connected(backhandle)) {\r
244         if (len > 0) {\r
245             return back->send(backhandle, data, len);\r
246         } else {\r
247             back->special(backhandle, TS_EOF);\r
248             return 0;\r
249         }\r
250     } else\r
251         return 0;\r
252 }\r
253 \r
254 void stdouterr_sent(struct handle *h, int new_backlog)\r
255 {\r
256     if (new_backlog < 0) {\r
257         /*\r
258          * Special case: report write error.\r
259          */\r
260         char buf[4096];\r
261         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, -new_backlog, 0,\r
262                       buf, lenof(buf), NULL);\r
263         buf[lenof(buf)-1] = '\0';\r
264         if (buf[strlen(buf)-1] == '\n')\r
265             buf[strlen(buf)-1] = '\0';\r
266         fprintf(stderr, "Unable to write to standard %s: %s\n",\r
267                 (h == stdout_handle ? "output" : "error"), buf);\r
268         cleanup_exit(0);\r
269     }\r
270     if (connopen && back->connected(backhandle)) {\r
271         back->unthrottle(backhandle, (handle_backlog(stdout_handle) +\r
272                                       handle_backlog(stderr_handle)));\r
273     }\r
274 }\r
275 \r
276 int main(int argc, char **argv)\r
277 {\r
278     int sending;\r
279     int portnumber = -1;\r
280     SOCKET *sklist;\r
281     int skcount, sksize;\r
282     int exitcode;\r
283     int errors;\r
284     int got_host = FALSE;\r
285     int use_subsystem = 0;\r
286     long now, next;\r
287 \r
288     sklist = NULL;\r
289     skcount = sksize = 0;\r
290     /*\r
291      * Initialise port and protocol to sensible defaults. (These\r
292      * will be overridden by more or less anything.)\r
293      */\r
294     default_protocol = PROT_SSH;\r
295     default_port = 22;\r
296 \r
297     flags = FLAG_STDERR;\r
298     /*\r
299      * Process the command line.\r
300      */\r
301     do_defaults(NULL, &cfg);\r
302     loaded_session = FALSE;\r
303     default_protocol = cfg.protocol;\r
304     default_port = cfg.port;\r
305     errors = 0;\r
306     {\r
307         /*\r
308          * Override the default protocol if PLINK_PROTOCOL is set.\r
309          */\r
310         char *p = getenv("PLINK_PROTOCOL");\r
311         if (p) {\r
312             const Backend *b = backend_from_name(p);\r
313             if (b) {\r
314                 default_protocol = cfg.protocol = b->protocol;\r
315                 default_port = cfg.port = b->default_port;\r
316             }\r
317         }\r
318     }\r
319     while (--argc) {\r
320         char *p = *++argv;\r
321         if (*p == '-') {\r
322             int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),\r
323                                             1, &cfg);\r
324             if (ret == -2) {\r
325                 fprintf(stderr,\r
326                         "plink: option \"%s\" requires an argument\n", p);\r
327                 errors = 1;\r
328             } else if (ret == 2) {\r
329                 --argc, ++argv;\r
330             } else if (ret == 1) {\r
331                 continue;\r
332             } else if (!strcmp(p, "-batch")) {\r
333                 console_batch_mode = 1;\r
334             } else if (!strcmp(p, "-s")) {\r
335                 /* Save status to write to cfg later. */\r
336                 use_subsystem = 1;\r
337             } else if (!strcmp(p, "-V")) {\r
338                 version();\r
339             } else if (!strcmp(p, "-pgpfp")) {\r
340                 pgp_fingerprints();\r
341                 exit(1);\r
342             } else {\r
343                 fprintf(stderr, "plink: unknown option \"%s\"\n", p);\r
344                 errors = 1;\r
345             }\r
346         } else if (*p) {\r
347             if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) {\r
348                 char *q = p;\r
349                 /*\r
350                  * If the hostname starts with "telnet:", set the\r
351                  * protocol to Telnet and process the string as a\r
352                  * Telnet URL.\r
353                  */\r
354                 if (!strncmp(q, "telnet:", 7)) {\r
355                     char c;\r
356 \r
357                     q += 7;\r
358                     if (q[0] == '/' && q[1] == '/')\r
359                         q += 2;\r
360                     cfg.protocol = PROT_TELNET;\r
361                     p = q;\r
362                     while (*p && *p != ':' && *p != '/')\r
363                         p++;\r
364                     c = *p;\r
365                     if (*p)\r
366                         *p++ = '\0';\r
367                     if (c == ':')\r
368                         cfg.port = atoi(p);\r
369                     else\r
370                         cfg.port = -1;\r
371                     strncpy(cfg.host, q, sizeof(cfg.host) - 1);\r
372                     cfg.host[sizeof(cfg.host) - 1] = '\0';\r
373                     got_host = TRUE;\r
374                 } else {\r
375                     char *r, *user, *host;\r
376                     /*\r
377                      * Before we process the [user@]host string, we\r
378                      * first check for the presence of a protocol\r
379                      * prefix (a protocol name followed by ",").\r
380                      */\r
381                     r = strchr(p, ',');\r
382                     if (r) {\r
383                         const Backend *b;\r
384                         *r = '\0';\r
385                         b = backend_from_name(p);\r
386                         if (b) {\r
387                             default_protocol = cfg.protocol = b->protocol;\r
388                             portnumber = b->default_port;\r
389                         }\r
390                         p = r + 1;\r
391                     }\r
392 \r
393                     /*\r
394                      * A nonzero length string followed by an @ is treated\r
395                      * as a username. (We discount an _initial_ @.) The\r
396                      * rest of the string (or the whole string if no @)\r
397                      * is treated as a session name and/or hostname.\r
398                      */\r
399                     r = strrchr(p, '@');\r
400                     if (r == p)\r
401                         p++, r = NULL; /* discount initial @ */\r
402                     if (r) {\r
403                         *r++ = '\0';\r
404                         user = p, host = r;\r
405                     } else {\r
406                         user = NULL, host = p;\r
407                     }\r
408 \r
409                     /*\r
410                      * Now attempt to load a saved session with the\r
411                      * same name as the hostname.\r
412                      */\r
413                     {\r
414                         Config cfg2;\r
415                         do_defaults(host, &cfg2);\r
416                         if (loaded_session || !cfg_launchable(&cfg2)) {\r
417                             /* No settings for this host; use defaults */\r
418                             /* (or session was already loaded with -load) */\r
419                             strncpy(cfg.host, host, sizeof(cfg.host) - 1);\r
420                             cfg.host[sizeof(cfg.host) - 1] = '\0';\r
421                             cfg.port = default_port;\r
422                             got_host = TRUE;\r
423                         } else {\r
424                             cfg = cfg2;\r
425                             loaded_session = TRUE;\r
426                         }\r
427                     }\r
428 \r
429                     if (user) {\r
430                         /* Patch in specified username. */\r
431                         strncpy(cfg.username, user,\r
432                                 sizeof(cfg.username) - 1);\r
433                         cfg.username[sizeof(cfg.username) - 1] = '\0';\r
434                     }\r
435 \r
436                 }\r
437             } else {\r
438                 char *command;\r
439                 int cmdlen, cmdsize;\r
440                 cmdlen = cmdsize = 0;\r
441                 command = NULL;\r
442 \r
443                 while (argc) {\r
444                     while (*p) {\r
445                         if (cmdlen >= cmdsize) {\r
446                             cmdsize = cmdlen + 512;\r
447                             command = sresize(command, cmdsize, char);\r
448                         }\r
449                         command[cmdlen++]=*p++;\r
450                     }\r
451                     if (cmdlen >= cmdsize) {\r
452                         cmdsize = cmdlen + 512;\r
453                         command = sresize(command, cmdsize, char);\r
454                     }\r
455                     command[cmdlen++]=' '; /* always add trailing space */\r
456                     if (--argc) p = *++argv;\r
457                 }\r
458                 if (cmdlen) command[--cmdlen]='\0';\r
459                                        /* change trailing blank to NUL */\r
460                 cfg.remote_cmd_ptr = command;\r
461                 cfg.remote_cmd_ptr2 = NULL;\r
462                 cfg.nopty = TRUE;      /* command => no terminal */\r
463 \r
464                 break;                 /* done with cmdline */\r
465             }\r
466         }\r
467     }\r
468 \r
469     if (errors)\r
470         return 1;\r
471 \r
472     if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) {\r
473         usage();\r
474     }\r
475 \r
476     /*\r
477      * Trim leading whitespace off the hostname if it's there.\r
478      */\r
479     {\r
480         int space = strspn(cfg.host, " \t");\r
481         memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);\r
482     }\r
483 \r
484     /* See if host is of the form user@host */\r
485     if (cfg_launchable(&cfg)) {\r
486         char *atsign = strrchr(cfg.host, '@');\r
487         /* Make sure we're not overflowing the user field */\r
488         if (atsign) {\r
489             if (atsign - cfg.host < sizeof cfg.username) {\r
490                 strncpy(cfg.username, cfg.host, atsign - cfg.host);\r
491                 cfg.username[atsign - cfg.host] = '\0';\r
492             }\r
493             memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));\r
494         }\r
495     }\r
496 \r
497     /*\r
498      * Perform command-line overrides on session configuration.\r
499      */\r
500     cmdline_run_saved(&cfg);\r
501 \r
502     /*\r
503      * Apply subsystem status.\r
504      */\r
505     if (use_subsystem)\r
506         cfg.ssh_subsys = TRUE;\r
507 \r
508     /*\r
509      * Trim a colon suffix off the hostname if it's there.\r
510      */\r
511     cfg.host[strcspn(cfg.host, ":")] = '\0';\r
512 \r
513     /*\r
514      * Remove any remaining whitespace from the hostname.\r
515      */\r
516     {\r
517         int p1 = 0, p2 = 0;\r
518         while (cfg.host[p2] != '\0') {\r
519             if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {\r
520                 cfg.host[p1] = cfg.host[p2];\r
521                 p1++;\r
522             }\r
523             p2++;\r
524         }\r
525         cfg.host[p1] = '\0';\r
526     }\r
527 \r
528     if (!cfg.remote_cmd_ptr && !*cfg.remote_cmd && !*cfg.ssh_nc_host)\r
529         flags |= FLAG_INTERACTIVE;\r
530 \r
531     /*\r
532      * Select protocol. This is farmed out into a table in a\r
533      * separate file to enable an ssh-free variant.\r
534      */\r
535     back = backend_from_proto(cfg.protocol);\r
536     if (back == NULL) {\r
537         fprintf(stderr,\r
538                 "Internal fault: Unsupported protocol found\n");\r
539         return 1;\r
540     }\r
541 \r
542     /*\r
543      * Select port.\r
544      */\r
545     if (portnumber != -1)\r
546         cfg.port = portnumber;\r
547 \r
548     sk_init();\r
549     if (p_WSAEventSelect == NULL) {\r
550         fprintf(stderr, "Plink requires WinSock 2\n");\r
551         return 1;\r
552     }\r
553 \r
554     logctx = log_init(NULL, &cfg);\r
555     console_provide_logctx(logctx);\r
556 \r
557     /*\r
558      * Start up the connection.\r
559      */\r
560     netevent = CreateEvent(NULL, FALSE, FALSE, NULL);\r
561     {\r
562         const char *error;\r
563         char *realhost;\r
564         /* nodelay is only useful if stdin is a character device (console) */\r
565         int nodelay = cfg.tcp_nodelay &&\r
566             (GetFileType(GetStdHandle(STD_INPUT_HANDLE)) == FILE_TYPE_CHAR);\r
567 \r
568         error = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port,\r
569                            &realhost, nodelay, cfg.tcp_keepalives);\r
570         if (error) {\r
571             fprintf(stderr, "Unable to open connection:\n%s", error);\r
572             return 1;\r
573         }\r
574         back->provide_logctx(backhandle, logctx);\r
575         sfree(realhost);\r
576     }\r
577     connopen = 1;\r
578 \r
579     inhandle = GetStdHandle(STD_INPUT_HANDLE);\r
580     outhandle = GetStdHandle(STD_OUTPUT_HANDLE);\r
581     errhandle = GetStdHandle(STD_ERROR_HANDLE);\r
582 \r
583     /*\r
584      * Turn off ECHO and LINE input modes. We don't care if this\r
585      * call fails, because we know we aren't necessarily running in\r
586      * a console.\r
587      */\r
588     GetConsoleMode(inhandle, &orig_console_mode);\r
589     SetConsoleMode(inhandle, ENABLE_PROCESSED_INPUT);\r
590 \r
591     /*\r
592      * Pass the output handles to the handle-handling subsystem.\r
593      * (The input one we leave until we're through the\r
594      * authentication process.)\r
595      */\r
596     stdout_handle = handle_output_new(outhandle, stdouterr_sent, NULL, 0);\r
597     stderr_handle = handle_output_new(errhandle, stdouterr_sent, NULL, 0);\r
598 \r
599     main_thread_id = GetCurrentThreadId();\r
600 \r
601     sending = FALSE;\r
602 \r
603     now = GETTICKCOUNT();\r
604 \r
605     while (1) {\r
606         int nhandles;\r
607         HANDLE *handles;        \r
608         int n;\r
609         DWORD ticks;\r
610 \r
611         if (!sending && back->sendok(backhandle)) {\r
612             stdin_handle = handle_input_new(inhandle, stdin_gotdata, NULL,\r
613                                             0);\r
614             sending = TRUE;\r
615         }\r
616 \r
617         if (run_timers(now, &next)) {\r
618             ticks = next - GETTICKCOUNT();\r
619             if (ticks < 0) ticks = 0;  /* just in case */\r
620         } else {\r
621             ticks = INFINITE;\r
622         }\r
623 \r
624         handles = handle_get_events(&nhandles);\r
625         handles = sresize(handles, nhandles+1, HANDLE);\r
626         handles[nhandles] = netevent;\r
627         n = MsgWaitForMultipleObjects(nhandles+1, handles, FALSE, ticks,\r
628                                       QS_POSTMESSAGE);\r
629         if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) {\r
630             handle_got_event(handles[n - WAIT_OBJECT_0]);\r
631         } else if (n == WAIT_OBJECT_0 + nhandles) {\r
632             WSANETWORKEVENTS things;\r
633             SOCKET socket;\r
634             extern SOCKET first_socket(int *), next_socket(int *);\r
635             extern int select_result(WPARAM, LPARAM);\r
636             int i, socketstate;\r
637 \r
638             /*\r
639              * We must not call select_result() for any socket\r
640              * until we have finished enumerating within the tree.\r
641              * This is because select_result() may close the socket\r
642              * and modify the tree.\r
643              */\r
644             /* Count the active sockets. */\r
645             i = 0;\r
646             for (socket = first_socket(&socketstate);\r
647                  socket != INVALID_SOCKET;\r
648                  socket = next_socket(&socketstate)) i++;\r
649 \r
650             /* Expand the buffer if necessary. */\r
651             if (i > sksize) {\r
652                 sksize = i + 16;\r
653                 sklist = sresize(sklist, sksize, SOCKET);\r
654             }\r
655 \r
656             /* Retrieve the sockets into sklist. */\r
657             skcount = 0;\r
658             for (socket = first_socket(&socketstate);\r
659                  socket != INVALID_SOCKET;\r
660                  socket = next_socket(&socketstate)) {\r
661                 sklist[skcount++] = socket;\r
662             }\r
663 \r
664             /* Now we're done enumerating; go through the list. */\r
665             for (i = 0; i < skcount; i++) {\r
666                 WPARAM wp;\r
667                 socket = sklist[i];\r
668                 wp = (WPARAM) socket;\r
669                 if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) {\r
670                     static const struct { int bit, mask; } eventtypes[] = {\r
671                         {FD_CONNECT_BIT, FD_CONNECT},\r
672                         {FD_READ_BIT, FD_READ},\r
673                         {FD_CLOSE_BIT, FD_CLOSE},\r
674                         {FD_OOB_BIT, FD_OOB},\r
675                         {FD_WRITE_BIT, FD_WRITE},\r
676                         {FD_ACCEPT_BIT, FD_ACCEPT},\r
677                     };\r
678                     int e;\r
679 \r
680                     noise_ultralight(socket);\r
681                     noise_ultralight(things.lNetworkEvents);\r
682 \r
683                     for (e = 0; e < lenof(eventtypes); e++)\r
684                         if (things.lNetworkEvents & eventtypes[e].mask) {\r
685                             LPARAM lp;\r
686                             int err = things.iErrorCode[eventtypes[e].bit];\r
687                             lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err);\r
688                             connopen &= select_result(wp, lp);\r
689                         }\r
690                 }\r
691             }\r
692         } else if (n == WAIT_OBJECT_0 + nhandles + 1) {\r
693             MSG msg;\r
694             while (PeekMessage(&msg, INVALID_HANDLE_VALUE,\r
695                                WM_AGENT_CALLBACK, WM_AGENT_CALLBACK,\r
696                                PM_REMOVE)) {\r
697                 struct agent_callback *c = (struct agent_callback *)msg.lParam;\r
698                 c->callback(c->callback_ctx, c->data, c->len);\r
699                 sfree(c);\r
700             }\r
701         }\r
702 \r
703         if (n == WAIT_TIMEOUT) {\r
704             now = next;\r
705         } else {\r
706             now = GETTICKCOUNT();\r
707         }\r
708 \r
709         sfree(handles);\r
710 \r
711         if (sending)\r
712             handle_unthrottle(stdin_handle, back->sendbuffer(backhandle));\r
713 \r
714         if ((!connopen || !back->connected(backhandle)) &&\r
715             handle_backlog(stdout_handle) + handle_backlog(stderr_handle) == 0)\r
716             break;                     /* we closed the connection */\r
717     }\r
718     exitcode = back->exitcode(backhandle);\r
719     if (exitcode < 0) {\r
720         fprintf(stderr, "Remote process exit code unavailable\n");\r
721         exitcode = 1;                  /* this is an error condition */\r
722     }\r
723     cleanup_exit(exitcode);\r
724     return 0;                          /* placate compiler warning */\r
725 }\r