2 * PLink - a command-line (stdin/stdout) variant of PuTTY.
13 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
18 #define MAX_STDIN_BACKLOG 4096
20 void fatalbox(char *p, ...)
23 fprintf(stderr, "FATAL ERROR: ");
25 vfprintf(stderr, p, ap);
31 void connection_fatal(char *p, ...)
34 fprintf(stderr, "FATAL ERROR: ");
36 vfprintf(stderr, p, ap);
43 static char *password = NULL;
45 void logevent(char *string)
49 void verify_ssh_host_key(char *host, int port, char *keytype,
50 char *keystr, char *fingerprint)
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"
60 "The server's key fingerprint is:\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"
68 "Store key in cache? (y/n) ";
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"
77 "The new key fingerprint is:\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"
86 "Update cached key? (y/n, Return cancels connection) ";
88 static const char abandoned[] = "Connection abandoned.\n";
93 * Verify the key against the registry.
95 ret = verify_host_key(host, port, keytype, keystr);
97 if (ret == 0) /* success - key matched OK */
100 if (ret == 2) { /* key was different */
101 fprintf(stderr, wrongmsg, fingerprint);
104 if (ret == 1) { /* key was absent */
105 fprintf(stderr, absentmsg, fingerprint);
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);
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);
120 fprintf(stderr, abandoned);
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
130 void askcipher(char *ciphername, int cs)
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";
145 (cs == 1) ? "client-to-server " :
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);
157 if (line[0] == 'y' || line[0] == 'Y') {
160 fprintf(stderr, abandoned);
165 HANDLE inhandle, outhandle, errhandle;
166 DWORD orig_console_mode;
170 int term_ldisc(int mode)
174 void ldisc_update(int echo, int edit)
176 /* Update stdin read mode to reflect changes in line discipline. */
179 mode = ENABLE_PROCESSED_INPUT;
181 mode = mode | ENABLE_ECHO_INPUT;
183 mode = mode & ~ENABLE_ECHO_INPUT;
185 mode = mode | ENABLE_LINE_INPUT;
187 mode = mode & ~ENABLE_LINE_INPUT;
188 SetConsoleMode(inhandle, mode);
191 static int get_line(const char *prompt, char *str, int maxlen, int is_pw)
194 DWORD savemode, newmode, i;
196 if (is_pw && password) {
197 static int tried_once = 0;
202 strncpy(str, password, maxlen);
203 str[maxlen - 1] = '\0';
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");
216 GetConsoleMode(hin, &savemode);
217 newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
219 newmode &= ~ENABLE_ECHO_INPUT;
221 newmode |= ENABLE_ECHO_INPUT;
222 SetConsoleMode(hin, newmode);
224 WriteFile(hout, prompt, strlen(prompt), &i, NULL);
225 ReadFile(hin, str, maxlen - 1, &i, NULL);
227 SetConsoleMode(hin, savemode);
229 if ((int) i > maxlen)
236 WriteFile(hout, "\r\n", 2, &i, NULL);
244 HANDLE event, eventback;
247 static DWORD WINAPI stdin_read_thread(void *param)
249 struct input_data *idata = (struct input_data *) param;
252 inhandle = GetStdHandle(STD_INPUT_HANDLE);
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);
261 SetEvent(idata->event);
267 DWORD len, lenwritten;
271 HANDLE event, eventback;
275 static DWORD WINAPI stdout_write_thread(void *param)
277 struct output_data *odata = (struct output_data *) param;
278 HANDLE outhandle, errhandle;
280 outhandle = GetStdHandle(STD_OUTPUT_HANDLE);
281 errhandle = GetStdHandle(STD_ERROR_HANDLE);
284 WaitForSingleObject(odata->eventback, INFINITE);
288 WriteFile(odata->is_stderr ? errhandle : outhandle,
289 odata->buffer, odata->len, &odata->lenwritten, NULL);
290 SetEvent(odata->event);
296 bufchain stdout_data, stderr_data;
297 struct output_data odata, edata;
299 void try_output(int is_stderr)
301 struct output_data *data = (is_stderr ? &edata : &odata);
306 bufchain_prefix(is_stderr ? &stderr_data : &stdout_data,
307 &senddata, &sendlen);
308 data->buffer = senddata;
310 SetEvent(data->eventback);
315 int from_backend(int is_stderr, char *data, int len)
317 HANDLE h = (is_stderr ? errhandle : outhandle);
321 bufchain_add(&stderr_data, data, len);
324 bufchain_add(&stdout_data, data, len);
328 osize = bufchain_size(&stdout_data);
329 esize = bufchain_size(&stderr_data);
331 return osize + esize;
335 * Short description of parameters.
337 static void usage(void)
339 printf("PuTTY Link: command-line connection utility\n");
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");
351 char *do_select(SOCKET skt, int startup)
355 events = (FD_CONNECT | FD_READ | FD_WRITE |
356 FD_OOB | FD_CLOSE | FD_ACCEPT);
360 if (WSAEventSelect(skt, netevent, events) == SOCKET_ERROR) {
361 switch (WSAGetLastError()) {
363 return "Network is down";
365 return "WSAAsyncSelect(): unknown error";
371 int main(int argc, char **argv)
375 WSAEVENT stdinevent, stdoutevent, stderrevent;
377 DWORD in_threadid, out_threadid, err_threadid;
378 struct input_data idata;
386 ssh_get_line = get_line;
389 skcount = sksize = 0;
391 * Initialise port and protocol to sensible defaults. (These
392 * will be overridden by more or less anything.)
394 default_protocol = PROT_SSH;
399 * Process the command line.
401 do_defaults(NULL, &cfg);
402 default_protocol = cfg.protocol;
403 default_port = cfg.port;
406 * Override the default protocol if PLINK_PROTOCOL is set.
408 char *p = getenv("PLINK_PROTOCOL");
411 for (i = 0; backends[i].backend != NULL; i++) {
412 if (!strcmp(backends[i].name, p)) {
413 default_protocol = cfg.protocol = backends[i].protocol;
414 default_port = cfg.port =
415 backends[i].backend->default_port;
424 if (!strcmp(p, "-ssh")) {
425 default_protocol = cfg.protocol = PROT_SSH;
426 default_port = cfg.port = 22;
427 } else if (!strcmp(p, "-telnet")) {
428 default_protocol = cfg.protocol = PROT_TELNET;
429 default_port = cfg.port = 23;
430 } else if (!strcmp(p, "-raw")) {
431 default_protocol = cfg.protocol = PROT_RAW;
432 } else if (!strcmp(p, "-v")) {
433 flags |= FLAG_VERBOSE;
434 } else if (!strcmp(p, "-log")) {
435 logfile = "putty.log";
436 } else if (!strcmp(p, "-pw") && argc > 1) {
437 --argc, password = *++argv;
438 } else if (!strcmp(p, "-l") && argc > 1) {
440 --argc, username = *++argv;
441 strncpy(cfg.username, username, sizeof(cfg.username));
442 cfg.username[sizeof(cfg.username) - 1] = '\0';
443 } else if (!strcmp(p, "-m") && argc > 1) {
444 char *filename, *command;
449 --argc, filename = *++argv;
451 cmdlen = cmdsize = 0;
453 fp = fopen(filename, "r");
455 fprintf(stderr, "plink: unable to open command "
456 "file \"%s\"\n", filename);
464 if (cmdlen >= cmdsize) {
465 cmdsize = cmdlen + 512;
466 command = srealloc(command, cmdsize);
468 command[cmdlen++] = d;
470 cfg.remote_cmd_ptr = command;
471 cfg.remote_cmd_ptr2 = NULL;
472 cfg.nopty = TRUE; /* command => no terminal */
473 } else if (!strcmp(p, "-P") && argc > 1) {
474 --argc, portnumber = atoi(*++argv);
480 * If the hostname starts with "telnet:", set the
481 * protocol to Telnet and process the string as a
484 if (!strncmp(q, "telnet:", 7)) {
488 if (q[0] == '/' && q[1] == '/')
490 cfg.protocol = PROT_TELNET;
492 while (*p && *p != ':' && *p != '/')
501 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
502 cfg.host[sizeof(cfg.host) - 1] = '\0';
506 * Before we process the [user@]host string, we
507 * first check for the presence of a protocol
508 * prefix (a protocol name followed by ",").
513 for (i = 0; backends[i].backend != NULL; i++) {
514 j = strlen(backends[i].name);
516 !memcmp(backends[i].name, p, j)) {
517 default_protocol = cfg.protocol =
518 backends[i].protocol;
520 backends[i].backend->default_port;
528 * Three cases. Either (a) there's a nonzero
529 * length string followed by an @, in which
530 * case that's user and the remainder is host.
531 * Or (b) there's only one string, not counting
532 * a potential initial @, and it exists in the
533 * saved-sessions database. Or (c) only one
534 * string and it _doesn't_ exist in the
539 p++, r = NULL; /* discount initial @ */
545 do_defaults(p, &cfg2);
546 if (cfg2.host[0] == '\0') {
547 /* No settings for this host; use defaults */
548 strncpy(cfg.host, p, sizeof(cfg.host) - 1);
549 cfg.host[sizeof(cfg.host) - 1] = '\0';
550 cfg.port = default_port;
553 cfg.remote_cmd_ptr = cfg.remote_cmd;
557 strncpy(cfg.username, p, sizeof(cfg.username) - 1);
558 cfg.username[sizeof(cfg.username) - 1] = '\0';
559 strncpy(cfg.host, r, sizeof(cfg.host) - 1);
560 cfg.host[sizeof(cfg.host) - 1] = '\0';
561 cfg.port = default_port;
565 int len = sizeof(cfg.remote_cmd) - 1;
566 char *cp = cfg.remote_cmd;
577 strncpy(cp, *++argv, len);
583 cfg.nopty = TRUE; /* command => no terminal */
584 break; /* done with cmdline */
593 if (!*cfg.remote_cmd_ptr)
594 flags |= FLAG_INTERACTIVE;
597 * Select protocol. This is farmed out into a table in a
598 * separate file to enable an ssh-free variant.
603 for (i = 0; backends[i].backend != NULL; i++)
604 if (backends[i].protocol == cfg.protocol) {
605 back = backends[i].backend;
610 "Internal fault: Unsupported protocol found\n");
618 if (portnumber != -1)
619 cfg.port = portnumber;
622 * Initialise WinSock.
624 winsock_ver = MAKEWORD(2, 0);
625 if (WSAStartup(winsock_ver, &wsadata)) {
626 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
627 MB_OK | MB_ICONEXCLAMATION);
630 if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 0) {
631 MessageBox(NULL, "WinSock version is incompatible with 2.0",
632 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
639 * Start up the connection.
641 netevent = CreateEvent(NULL, FALSE, FALSE, NULL);
646 error = back->init(cfg.host, cfg.port, &realhost);
648 fprintf(stderr, "Unable to open connection:\n%s", error);
655 stdinevent = CreateEvent(NULL, FALSE, FALSE, NULL);
656 stdoutevent = CreateEvent(NULL, FALSE, FALSE, NULL);
657 stderrevent = CreateEvent(NULL, FALSE, FALSE, NULL);
659 inhandle = GetStdHandle(STD_INPUT_HANDLE);
660 outhandle = GetStdHandle(STD_OUTPUT_HANDLE);
661 errhandle = GetStdHandle(STD_ERROR_HANDLE);
662 GetConsoleMode(inhandle, &orig_console_mode);
663 SetConsoleMode(inhandle, ENABLE_PROCESSED_INPUT);
666 * Turn off ECHO and LINE input modes. We don't care if this
667 * call fails, because we know we aren't necessarily running in
670 handles[0] = netevent;
671 handles[1] = stdinevent;
672 handles[2] = stdoutevent;
673 handles[3] = stderrevent;
677 * Create spare threads to write to stdout and stderr, so we
678 * can arrange asynchronous writes.
680 odata.event = stdoutevent;
681 odata.eventback = CreateEvent(NULL, FALSE, FALSE, NULL);
683 odata.busy = odata.done = 0;
684 if (!CreateThread(NULL, 0, stdout_write_thread,
685 &odata, 0, &out_threadid)) {
686 fprintf(stderr, "Unable to create output thread\n");
689 edata.event = stderrevent;
690 edata.eventback = CreateEvent(NULL, FALSE, FALSE, NULL);
692 edata.busy = edata.done = 0;
693 if (!CreateThread(NULL, 0, stdout_write_thread,
694 &edata, 0, &err_threadid)) {
695 fprintf(stderr, "Unable to create error output thread\n");
702 if (!sending && back->sendok()) {
704 * Create a separate thread to read from stdin. This is
705 * a total pain, but I can't find another way to do it:
707 * - an overlapped ReadFile or ReadFileEx just doesn't
708 * happen; we get failure from ReadFileEx, and
709 * ReadFile blocks despite being given an OVERLAPPED
710 * structure. Perhaps we can't do overlapped reads
711 * on consoles. WHY THE HELL NOT?
713 * - WaitForMultipleObjects(netevent, console) doesn't
714 * work, because it signals the console when
715 * _anything_ happens, including mouse motions and
716 * other things that don't cause data to be readable
717 * - so we're back to ReadFile blocking.
719 idata.event = stdinevent;
720 idata.eventback = CreateEvent(NULL, FALSE, FALSE, NULL);
721 if (!CreateThread(NULL, 0, stdin_read_thread,
722 &idata, 0, &in_threadid)) {
723 fprintf(stderr, "Unable to create input thread\n");
729 n = WaitForMultipleObjects(4, handles, FALSE, INFINITE);
731 WSANETWORKEVENTS things;
733 extern SOCKET first_socket(int *), next_socket(int *);
734 extern int select_result(WPARAM, LPARAM);
738 * We must not call select_result() for any socket
739 * until we have finished enumerating within the tree.
740 * This is because select_result() may close the socket
741 * and modify the tree.
743 /* Count the active sockets. */
745 for (socket = first_socket(&socketstate);
746 socket != INVALID_SOCKET;
747 socket = next_socket(&socketstate)) i++;
749 /* Expand the buffer if necessary. */
752 sklist = srealloc(sklist, sksize * sizeof(*sklist));
755 /* Retrieve the sockets into sklist. */
757 for (socket = first_socket(&socketstate);
758 socket != INVALID_SOCKET;
759 socket = next_socket(&socketstate)) {
760 sklist[skcount++] = socket;
763 /* Now we're done enumerating; go through the list. */
764 for (i = 0; i < skcount; i++) {
767 wp = (WPARAM) socket;
768 if (!WSAEnumNetworkEvents(socket, NULL, &things)) {
769 noise_ultralight(socket);
770 noise_ultralight(things.lNetworkEvents);
771 if (things.lNetworkEvents & FD_CONNECT)
772 connopen &= select_result(wp, (LPARAM) FD_CONNECT);
773 if (things.lNetworkEvents & FD_READ)
774 connopen &= select_result(wp, (LPARAM) FD_READ);
775 if (things.lNetworkEvents & FD_CLOSE)
776 connopen &= select_result(wp, (LPARAM) FD_CLOSE);
777 if (things.lNetworkEvents & FD_OOB)
778 connopen &= select_result(wp, (LPARAM) FD_OOB);
779 if (things.lNetworkEvents & FD_WRITE)
780 connopen &= select_result(wp, (LPARAM) FD_WRITE);
781 if (things.lNetworkEvents & FD_ACCEPT)
782 connopen &= select_result(wp, (LPARAM) FD_ACCEPT);
788 noise_ultralight(idata.len);
790 back->send(idata.buffer, idata.len);
792 back->special(TS_EOF);
796 if (!odata.writeret) {
797 fprintf(stderr, "Unable to write to standard output\n");
800 bufchain_consume(&stdout_data, odata.lenwritten);
801 if (bufchain_size(&stdout_data) > 0)
803 back->unthrottle(bufchain_size(&stdout_data) +
804 bufchain_size(&stderr_data));
807 if (!edata.writeret) {
808 fprintf(stderr, "Unable to write to standard output\n");
811 bufchain_consume(&stderr_data, edata.lenwritten);
812 if (bufchain_size(&stderr_data) > 0)
814 back->unthrottle(bufchain_size(&stdout_data) +
815 bufchain_size(&stderr_data));
817 if (!reading && back->sendbuffer() < MAX_STDIN_BACKLOG) {
818 SetEvent(idata.eventback);
821 if (!connopen || back->socket() == NULL)
822 break; /* we closed the connection */