121c65f9853eef028188adc9489d771e231dda10
2 * PLink - a command-line (stdin/stdout) variant of PuTTY.
13 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
18 void fatalbox (char *p
, ...) {
20 fprintf(stderr
, "FATAL ERROR: ");
22 vfprintf(stderr
, p
, ap
);
28 void connection_fatal (char *p
, ...) {
30 fprintf(stderr
, "FATAL ERROR: ");
32 vfprintf(stderr
, p
, ap
);
39 static char *password
= NULL
;
41 void logevent(char *string
) { }
43 void verify_ssh_host_key(char *host
, int port
, char *keytype
,
44 char *keystr
, char *fingerprint
) {
49 static const char absentmsg
[] =
50 "The server's host key is not cached in the registry. You\n"
51 "have no guarantee that the server is the computer you\n"
53 "The server's key fingerprint is:\n"
55 "If you trust this host, enter \"y\" to add the key to\n"
56 "PuTTY's cache and carry on connecting.\n"
57 "If you do not trust this host, enter \"n\" to abandon the\n"
59 "Continue connecting? (y/n) ";
61 static const char wrongmsg
[] =
62 "WARNING - POTENTIAL SECURITY BREACH!\n"
63 "The server's host key does not match the one PuTTY has\n"
64 "cached in the registry. This means that either the\n"
65 "server administrator has changed the host key, or you\n"
66 "have actually connected to another computer pretending\n"
68 "The new key fingerprint is:\n"
70 "If you were expecting this change and trust the new key,\n"
71 "enter \"y\" to update PuTTY's cache and continue connecting.\n"
72 "If you want to carry on connecting but without updating\n"
73 "the cache, enter \"n\".\n"
74 "If you want to abandon the connection completely, press\n"
75 "Return to cancel. Pressing Return is the ONLY guaranteed\n"
77 "Update cached key? (y/n, Return cancels connection) ";
79 static const char abandoned
[] = "Connection abandoned.\n";
84 * Verify the key against the registry.
86 ret
= verify_host_key(host
, port
, keytype
, keystr
);
88 if (ret
== 0) /* success - key matched OK */
91 if (ret
== 2) /* key was different */
92 fprintf(stderr
, wrongmsg
, fingerprint
);
93 if (ret
== 1) /* key was absent */
94 fprintf(stderr
, absentmsg
, fingerprint
);
96 hin
= GetStdHandle(STD_INPUT_HANDLE
);
97 GetConsoleMode(hin
, &savemode
);
98 SetConsoleMode(hin
, (savemode
| ENABLE_ECHO_INPUT
|
99 ENABLE_PROCESSED_INPUT
| ENABLE_LINE_INPUT
));
100 ReadFile(hin
, line
, sizeof(line
)-1, &i
, NULL
);
101 SetConsoleMode(hin
, savemode
);
103 if (ret
== 2) { /* key was different */
104 if (line
[0] != '\0' && line
[0] != '\r' && line
[0] != '\n') {
105 if (line
[0] == 'y' || line
[0] == 'Y')
106 store_host_key(host
, port
, keytype
, keystr
);
108 fprintf(stderr
, abandoned
);
112 if (ret
== 1) { /* key was absent */
113 if (line
[0] == 'y' || line
[0] == 'Y')
114 store_host_key(host
, port
, keytype
, keystr
);
116 fprintf(stderr
, abandoned
);
122 HANDLE inhandle
, outhandle
, errhandle
;
123 DWORD orig_console_mode
;
127 void from_backend(int is_stderr
, char *data
, int len
) {
130 HANDLE h
= (is_stderr ? errhandle
: outhandle
);
134 if (!WriteFile(h
, data
+pos
, len
-pos
, &ret
, NULL
))
135 return; /* give up in panic */
140 int term_ldisc(int mode
) { return FALSE
; }
141 void ldisc_update(int echo
, int edit
) {
142 /* Update stdin read mode to reflect changes in line discipline. */
145 mode
= ENABLE_PROCESSED_INPUT
;
147 mode
= mode
| ENABLE_ECHO_INPUT
;
149 mode
= mode
&~ ENABLE_ECHO_INPUT
;
151 mode
= mode
| ENABLE_LINE_INPUT
;
153 mode
= mode
&~ ENABLE_LINE_INPUT
;
154 SetConsoleMode(inhandle
, mode
);
160 HANDLE event
, eventback
;
163 static int get_line(const char *prompt
, char *str
, int maxlen
, int is_pw
)
166 DWORD savemode
, newmode
, i
;
168 if (is_pw
&& password
) {
169 static int tried_once
= 0;
174 strncpy(str
, password
, maxlen
);
175 str
[maxlen
-1] = '\0';
181 hin
= GetStdHandle(STD_INPUT_HANDLE
);
182 hout
= GetStdHandle(STD_OUTPUT_HANDLE
);
183 if (hin
== INVALID_HANDLE_VALUE
|| hout
== INVALID_HANDLE_VALUE
) {
184 fprintf(stderr
, "Cannot get standard input/output handles");
188 GetConsoleMode(hin
, &savemode
);
189 newmode
= savemode
| ENABLE_PROCESSED_INPUT
| ENABLE_LINE_INPUT
;
191 newmode
&= ~ENABLE_ECHO_INPUT
;
193 newmode
|= ENABLE_ECHO_INPUT
;
194 SetConsoleMode(hin
, newmode
);
196 WriteFile(hout
, prompt
, strlen(prompt
), &i
, NULL
);
197 ReadFile(hin
, str
, maxlen
-1, &i
, NULL
);
199 SetConsoleMode(hin
, savemode
);
201 if ((int)i
> maxlen
) i
= maxlen
-1; else i
= i
- 2;
205 WriteFile(hout
, "\r\n", 2, &i
, NULL
);
210 static DWORD WINAPI
stdin_read_thread(void *param
) {
211 struct input_data
*idata
= (struct input_data
*)param
;
214 inhandle
= GetStdHandle(STD_INPUT_HANDLE
);
216 while (ReadFile(inhandle
, idata
->buffer
, sizeof(idata
->buffer
),
217 &idata
->len
, NULL
) && idata
->len
> 0) {
218 SetEvent(idata
->event
);
219 WaitForSingleObject(idata
->eventback
, INFINITE
);
223 SetEvent(idata
->event
);
229 * Short description of parameters.
231 static void usage(void)
233 printf("PuTTY Link: command-line connection utility\n");
235 printf("Usage: plink [options] [user@]host [command]\n");
236 printf("Options:\n");
237 printf(" -v show verbose messages\n");
238 printf(" -ssh force use of ssh protocol\n");
239 printf(" -P port connect to specified port\n");
240 printf(" -pw passw login with specified password\n");
244 char *do_select(SOCKET skt
, int startup
) {
247 events
= FD_READ
| FD_WRITE
| FD_OOB
| FD_CLOSE
;
251 if (WSAEventSelect (skt
, netevent
, events
) == SOCKET_ERROR
) {
252 switch (WSAGetLastError()) {
253 case WSAENETDOWN
: return "Network is down";
254 default: return "WSAAsyncSelect(): unknown error";
260 int main(int argc
, char **argv
) {
266 struct input_data idata
;
273 ssh_get_line
= get_line
;
275 sklist
= NULL
; skcount
= sksize
= 0;
279 * Process the command line.
281 do_defaults(NULL
, &cfg
);
282 default_protocol
= cfg
.protocol
;
283 default_port
= cfg
.port
;
286 * Override the default protocol if PLINK_PROTOCOL is set.
288 char *p
= getenv("PLINK_PROTOCOL");
291 for (i
= 0; backends
[i
].backend
!= NULL
; i
++) {
292 if (!strcmp(backends
[i
].name
, p
)) {
293 default_protocol
= cfg
.protocol
= backends
[i
].protocol
;
294 default_port
= cfg
.port
= backends
[i
].backend
->default_port
;
303 if (!strcmp(p
, "-ssh")) {
304 default_protocol
= cfg
.protocol
= PROT_SSH
;
305 default_port
= cfg
.port
= 22;
306 } else if (!strcmp(p
, "-telnet")) {
307 default_protocol
= cfg
.protocol
= PROT_TELNET
;
308 default_port
= cfg
.port
= 23;
309 } else if (!strcmp(p
, "-raw")) {
310 default_protocol
= cfg
.protocol
= PROT_RAW
;
311 } else if (!strcmp(p
, "-v")) {
312 flags
|= FLAG_VERBOSE
;
313 } else if (!strcmp(p
, "-log")) {
314 logfile
= "putty.log";
315 } else if (!strcmp(p
, "-pw") && argc
> 1) {
316 --argc
, password
= *++argv
;
317 } else if (!strcmp(p
, "-l") && argc
> 1) {
319 --argc
, username
= *++argv
;
320 strncpy(cfg
.username
, username
, sizeof(cfg
.username
));
321 cfg
.username
[sizeof(cfg
.username
)-1] = '\0';
322 } else if (!strcmp(p
, "-P") && argc
> 1) {
323 --argc
, portnumber
= atoi(*++argv
);
329 * If the hostname starts with "telnet:", set the
330 * protocol to Telnet and process the string as a
333 if (!strncmp(q
, "telnet:", 7)) {
337 if (q
[0] == '/' && q
[1] == '/')
339 cfg
.protocol
= PROT_TELNET
;
341 while (*p
&& *p
!= ':' && *p
!= '/') p
++;
349 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
350 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
354 * Before we process the [user@]host string, we
355 * first check for the presence of a protocol
356 * prefix (a protocol name followed by ",").
361 for (i
= 0; backends
[i
].backend
!= NULL
; i
++) {
362 j
= strlen(backends
[i
].name
);
364 !memcmp(backends
[i
].name
, p
, j
)) {
365 default_protocol
= cfg
.protocol
= backends
[i
].protocol
;
366 portnumber
= backends
[i
].backend
->default_port
;
374 * Three cases. Either (a) there's a nonzero
375 * length string followed by an @, in which
376 * case that's user and the remainder is host.
377 * Or (b) there's only one string, not counting
378 * a potential initial @, and it exists in the
379 * saved-sessions database. Or (c) only one
380 * string and it _doesn't_ exist in the
384 if (r
== p
) p
++, r
= NULL
; /* discount initial @ */
390 do_defaults (p
, &cfg2
);
391 if (cfg2
.host
[0] == '\0') {
392 /* No settings for this host; use defaults */
393 strncpy(cfg
.host
, p
, sizeof(cfg
.host
)-1);
394 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
395 cfg
.port
= default_port
;
400 strncpy(cfg
.username
, p
, sizeof(cfg
.username
)-1);
401 cfg
.username
[sizeof(cfg
.username
)-1] = '\0';
402 strncpy(cfg
.host
, r
, sizeof(cfg
.host
)-1);
403 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
404 cfg
.port
= default_port
;
408 int len
= sizeof(cfg
.remote_cmd
) - 1;
409 char *cp
= cfg
.remote_cmd
;
412 strncpy(cp
, p
, len
); cp
[len
] = '\0';
413 len2
= strlen(cp
); len
-= len2
; cp
+= len2
;
417 strncpy(cp
, *++argv
, len
); cp
[len
] = '\0';
418 len2
= strlen(cp
); len
-= len2
; cp
+= len2
;
420 cfg
.nopty
= TRUE
; /* command => no terminal */
421 break; /* done with cmdline */
430 if (!*cfg
.remote_cmd
)
431 flags
|= FLAG_INTERACTIVE
;
434 * Select protocol. This is farmed out into a table in a
435 * separate file to enable an ssh-free variant.
440 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
441 if (backends
[i
].protocol
== cfg
.protocol
) {
442 back
= backends
[i
].backend
;
446 fprintf(stderr
, "Internal fault: Unsupported protocol found\n");
454 if (portnumber
!= -1)
455 cfg
.port
= portnumber
;
458 * Initialise WinSock.
460 winsock_ver
= MAKEWORD(2, 0);
461 if (WSAStartup(winsock_ver
, &wsadata
)) {
462 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
463 MB_OK
| MB_ICONEXCLAMATION
);
466 if (LOBYTE(wsadata
.wVersion
) != 2 || HIBYTE(wsadata
.wVersion
) != 0) {
467 MessageBox(NULL
, "WinSock version is incompatible with 2.0",
468 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
475 * Start up the connection.
477 netevent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
482 error
= back
->init (cfg
.host
, cfg
.port
, &realhost
);
484 fprintf(stderr
, "Unable to open connection:\n%s", error
);
490 stdinevent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
492 inhandle
= GetStdHandle(STD_INPUT_HANDLE
);
493 outhandle
= GetStdHandle(STD_OUTPUT_HANDLE
);
494 errhandle
= GetStdHandle(STD_ERROR_HANDLE
);
495 GetConsoleMode(inhandle
, &orig_console_mode
);
496 SetConsoleMode(inhandle
, ENABLE_PROCESSED_INPUT
);
499 * Turn off ECHO and LINE input modes. We don't care if this
500 * call fails, because we know we aren't necessarily running in
503 handles
[0] = netevent
;
504 handles
[1] = stdinevent
;
509 if (!sending
&& back
->sendok()) {
511 * Create a separate thread to read from stdin. This is
512 * a total pain, but I can't find another way to do it:
514 * - an overlapped ReadFile or ReadFileEx just doesn't
515 * happen; we get failure from ReadFileEx, and
516 * ReadFile blocks despite being given an OVERLAPPED
517 * structure. Perhaps we can't do overlapped reads
518 * on consoles. WHY THE HELL NOT?
520 * - WaitForMultipleObjects(netevent, console) doesn't
521 * work, because it signals the console when
522 * _anything_ happens, including mouse motions and
523 * other things that don't cause data to be readable
524 * - so we're back to ReadFile blocking.
526 idata
.event
= stdinevent
;
527 idata
.eventback
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
528 if (!CreateThread(NULL
, 0, stdin_read_thread
,
529 &idata
, 0, &threadid
)) {
530 fprintf(stderr
, "Unable to create second thread\n");
536 n
= WaitForMultipleObjects(2, handles
, FALSE
, INFINITE
);
538 WSANETWORKEVENTS things
;
541 extern SOCKET
first_socket(enum234
*), next_socket(enum234
*);
542 extern int select_result(WPARAM
, LPARAM
);
546 * We must not call select_result() for any socket
547 * until we have finished enumerating within the tree.
548 * This is because select_result() may close the socket
549 * and modify the tree.
551 /* Count the active sockets. */
553 for (socket
= first_socket(&e
); socket
!= INVALID_SOCKET
;
554 socket
= next_socket(&e
))
557 /* Expand the buffer if necessary. */
560 sklist
= srealloc(sklist
, sksize
* sizeof(*sklist
));
563 /* Retrieve the sockets into sklist. */
565 for (socket
= first_socket(&e
); socket
!= INVALID_SOCKET
;
566 socket
= next_socket(&e
)) {
567 sklist
[skcount
++] = socket
;
570 /* Now we're done enumerating; go through the list. */
571 for (i
= 0; i
< skcount
; i
++) {
575 if (!WSAEnumNetworkEvents(socket
, NULL
, &things
)) {
576 noise_ultralight(socket
);
577 noise_ultralight(things
.lNetworkEvents
);
578 if (things
.lNetworkEvents
& FD_READ
)
579 connopen
&= select_result(wp
, (LPARAM
)FD_READ
);
580 if (things
.lNetworkEvents
& FD_CLOSE
)
581 connopen
&= select_result(wp
, (LPARAM
)FD_CLOSE
);
582 if (things
.lNetworkEvents
& FD_OOB
)
583 connopen
&= select_result(wp
, (LPARAM
)FD_OOB
);
584 if (things
.lNetworkEvents
& FD_WRITE
)
585 connopen
&= select_result(wp
, (LPARAM
)FD_WRITE
);
589 noise_ultralight(idata
.len
);
591 back
->send(idata
.buffer
, idata
.len
);
593 back
->special(TS_EOF
);
595 SetEvent(idata
.eventback
);
597 if (!connopen
|| back
->socket() == NULL
)
598 break; /* we closed the connection */