The other utilities should do the same processing of the hostname
[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 /*
595 * Trim leading whitespace off the hostname if it's there.
596 */
597 {
598 int space = strspn(cfg.host, " \t");
599 memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
600 }
601
602 /* See if host is of the form user@host */
603 if (cfg.host[0] != '\0') {
604 char *atsign = strchr(cfg.host, '@');
605 /* Make sure we're not overflowing the user field */
606 if (atsign) {
607 if (atsign - cfg.host < sizeof cfg.username) {
608 strncpy(cfg.username, cfg.host, atsign - cfg.host);
609 cfg.username[atsign - cfg.host] = '\0';
610 }
611 memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
612 }
613 }
614
615 /*
616 * Trim a colon suffix off the hostname if it's there.
617 */
618 cfg.host[strcspn(cfg.host, ":")] = '\0';
619
620 if (!*cfg.remote_cmd_ptr)
621 flags |= FLAG_INTERACTIVE;
622
623 /*
624 * Select protocol. This is farmed out into a table in a
625 * separate file to enable an ssh-free variant.
626 */
627 {
628 int i;
629 back = NULL;
630 for (i = 0; backends[i].backend != NULL; i++)
631 if (backends[i].protocol == cfg.protocol) {
632 back = backends[i].backend;
633 break;
634 }
635 if (back == NULL) {
636 fprintf(stderr,
637 "Internal fault: Unsupported protocol found\n");
638 return 1;
639 }
640 }
641
642 /*
643 * Select port.
644 */
645 if (portnumber != -1)
646 cfg.port = portnumber;
647
648 /*
649 * Initialise WinSock.
650 */
651 winsock_ver = MAKEWORD(2, 0);
652 if (WSAStartup(winsock_ver, &wsadata)) {
653 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
654 MB_OK | MB_ICONEXCLAMATION);
655 return 1;
656 }
657 if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 0) {
658 MessageBox(NULL, "WinSock version is incompatible with 2.0",
659 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
660 WSACleanup();
661 return 1;
662 }
663 sk_init();
664
665 /*
666 * Start up the connection.
667 */
668 netevent = CreateEvent(NULL, FALSE, FALSE, NULL);
669 {
670 char *error;
671 char *realhost;
672
673 error = back->init(cfg.host, cfg.port, &realhost);
674 if (error) {
675 fprintf(stderr, "Unable to open connection:\n%s", error);
676 return 1;
677 }
678 sfree(realhost);
679 }
680 connopen = 1;
681
682 stdinevent = CreateEvent(NULL, FALSE, FALSE, NULL);
683 stdoutevent = CreateEvent(NULL, FALSE, FALSE, NULL);
684 stderrevent = CreateEvent(NULL, FALSE, FALSE, NULL);
685
686 inhandle = GetStdHandle(STD_INPUT_HANDLE);
687 outhandle = GetStdHandle(STD_OUTPUT_HANDLE);
688 errhandle = GetStdHandle(STD_ERROR_HANDLE);
689 GetConsoleMode(inhandle, &orig_console_mode);
690 SetConsoleMode(inhandle, ENABLE_PROCESSED_INPUT);
691
692 /*
693 * Turn off ECHO and LINE input modes. We don't care if this
694 * call fails, because we know we aren't necessarily running in
695 * a console.
696 */
697 handles[0] = netevent;
698 handles[1] = stdinevent;
699 handles[2] = stdoutevent;
700 handles[3] = stderrevent;
701 sending = FALSE;
702
703 /*
704 * Create spare threads to write to stdout and stderr, so we
705 * can arrange asynchronous writes.
706 */
707 odata.event = stdoutevent;
708 odata.eventback = CreateEvent(NULL, FALSE, FALSE, NULL);
709 odata.is_stderr = 0;
710 odata.busy = odata.done = 0;
711 if (!CreateThread(NULL, 0, stdout_write_thread,
712 &odata, 0, &out_threadid)) {
713 fprintf(stderr, "Unable to create output thread\n");
714 exit(1);
715 }
716 edata.event = stderrevent;
717 edata.eventback = CreateEvent(NULL, FALSE, FALSE, NULL);
718 edata.is_stderr = 1;
719 edata.busy = edata.done = 0;
720 if (!CreateThread(NULL, 0, stdout_write_thread,
721 &edata, 0, &err_threadid)) {
722 fprintf(stderr, "Unable to create error output thread\n");
723 exit(1);
724 }
725
726 while (1) {
727 int n;
728
729 if (!sending && back->sendok()) {
730 /*
731 * Create a separate thread to read from stdin. This is
732 * a total pain, but I can't find another way to do it:
733 *
734 * - an overlapped ReadFile or ReadFileEx just doesn't
735 * happen; we get failure from ReadFileEx, and
736 * ReadFile blocks despite being given an OVERLAPPED
737 * structure. Perhaps we can't do overlapped reads
738 * on consoles. WHY THE HELL NOT?
739 *
740 * - WaitForMultipleObjects(netevent, console) doesn't
741 * work, because it signals the console when
742 * _anything_ happens, including mouse motions and
743 * other things that don't cause data to be readable
744 * - so we're back to ReadFile blocking.
745 */
746 idata.event = stdinevent;
747 idata.eventback = CreateEvent(NULL, FALSE, FALSE, NULL);
748 if (!CreateThread(NULL, 0, stdin_read_thread,
749 &idata, 0, &in_threadid)) {
750 fprintf(stderr, "Unable to create input thread\n");
751 exit(1);
752 }
753 sending = TRUE;
754 }
755
756 n = WaitForMultipleObjects(4, handles, FALSE, INFINITE);
757 if (n == 0) {
758 WSANETWORKEVENTS things;
759 SOCKET socket;
760 extern SOCKET first_socket(int *), next_socket(int *);
761 extern int select_result(WPARAM, LPARAM);
762 int i, socketstate;
763
764 /*
765 * We must not call select_result() for any socket
766 * until we have finished enumerating within the tree.
767 * This is because select_result() may close the socket
768 * and modify the tree.
769 */
770 /* Count the active sockets. */
771 i = 0;
772 for (socket = first_socket(&socketstate);
773 socket != INVALID_SOCKET;
774 socket = next_socket(&socketstate)) i++;
775
776 /* Expand the buffer if necessary. */
777 if (i > sksize) {
778 sksize = i + 16;
779 sklist = srealloc(sklist, sksize * sizeof(*sklist));
780 }
781
782 /* Retrieve the sockets into sklist. */
783 skcount = 0;
784 for (socket = first_socket(&socketstate);
785 socket != INVALID_SOCKET;
786 socket = next_socket(&socketstate)) {
787 sklist[skcount++] = socket;
788 }
789
790 /* Now we're done enumerating; go through the list. */
791 for (i = 0; i < skcount; i++) {
792 WPARAM wp;
793 socket = sklist[i];
794 wp = (WPARAM) socket;
795 if (!WSAEnumNetworkEvents(socket, NULL, &things)) {
796 noise_ultralight(socket);
797 noise_ultralight(things.lNetworkEvents);
798 if (things.lNetworkEvents & FD_CONNECT)
799 connopen &= select_result(wp, (LPARAM) FD_CONNECT);
800 if (things.lNetworkEvents & FD_READ)
801 connopen &= select_result(wp, (LPARAM) FD_READ);
802 if (things.lNetworkEvents & FD_CLOSE)
803 connopen &= select_result(wp, (LPARAM) FD_CLOSE);
804 if (things.lNetworkEvents & FD_OOB)
805 connopen &= select_result(wp, (LPARAM) FD_OOB);
806 if (things.lNetworkEvents & FD_WRITE)
807 connopen &= select_result(wp, (LPARAM) FD_WRITE);
808 if (things.lNetworkEvents & FD_ACCEPT)
809 connopen &= select_result(wp, (LPARAM) FD_ACCEPT);
810
811 }
812 }
813 } else if (n == 1) {
814 reading = 0;
815 noise_ultralight(idata.len);
816 if (connopen && back->socket() != NULL) {
817 if (idata.len > 0) {
818 back->send(idata.buffer, idata.len);
819 } else {
820 back->special(TS_EOF);
821 }
822 }
823 } else if (n == 2) {
824 odata.busy = 0;
825 if (!odata.writeret) {
826 fprintf(stderr, "Unable to write to standard output\n");
827 exit(0);
828 }
829 bufchain_consume(&stdout_data, odata.lenwritten);
830 if (bufchain_size(&stdout_data) > 0)
831 try_output(0);
832 if (connopen && back->socket() != NULL) {
833 back->unthrottle(bufchain_size(&stdout_data) +
834 bufchain_size(&stderr_data));
835 }
836 } else if (n == 3) {
837 edata.busy = 0;
838 if (!edata.writeret) {
839 fprintf(stderr, "Unable to write to standard output\n");
840 exit(0);
841 }
842 bufchain_consume(&stderr_data, edata.lenwritten);
843 if (bufchain_size(&stderr_data) > 0)
844 try_output(1);
845 if (connopen && back->socket() != NULL) {
846 back->unthrottle(bufchain_size(&stdout_data) +
847 bufchain_size(&stderr_data));
848 }
849 }
850 if (!reading && back->sendbuffer() < MAX_STDIN_BACKLOG) {
851 SetEvent(idata.eventback);
852 reading = 1;
853 }
854 if ((!connopen || back->socket() == NULL) &&
855 bufchain_size(&stdout_data) == 0 &&
856 bufchain_size(&stderr_data) == 0)
857 break; /* we closed the connection */
858 }
859 WSACleanup();
860 return 0;
861 }