Fix the SSH2 key re-exchange bug. Session id != exchange hash,
[u/mdw/putty] / scp.c
1 /*
2 * scp.c - Scp (Secure Copy) client for PuTTY.
3 * Joris van Rantwijk, Simon Tatham
4 *
5 * This is mainly based on ssh-1.2.26/scp.c by Timo Rinne & Tatu Ylonen.
6 * They, in turn, used stuff from BSD rcp.
7 *
8 * Adaptations to enable connecting a GUI by L. Gunnarsson - Sept 2000
9 */
10
11 #include <windows.h>
12 #ifndef AUTO_WINSOCK
13 #ifdef WINSOCK_TWO
14 #include <winsock2.h>
15 #else
16 #include <winsock.h>
17 #endif
18 #endif
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <time.h>
23 #include <assert.h>
24 /* GUI Adaptation - Sept 2000 */
25 #include <winuser.h>
26 #include <winbase.h>
27
28 #define PUTTY_DO_GLOBALS
29 #include "putty.h"
30 #include "winstuff.h"
31 #include "storage.h"
32
33 #define TIME_POSIX_TO_WIN(t, ft) (*(LONGLONG*)&(ft) = \
34 ((LONGLONG) (t) + (LONGLONG) 11644473600) * (LONGLONG) 10000000)
35 #define TIME_WIN_TO_POSIX(ft, t) ((t) = (unsigned long) \
36 ((*(LONGLONG*)&(ft)) / (LONGLONG) 10000000 - (LONGLONG) 11644473600))
37
38 /* GUI Adaptation - Sept 2000 */
39 #define WM_APP_BASE 0x8000
40 #define WM_STD_OUT_CHAR ( WM_APP_BASE+400 )
41 #define WM_STD_ERR_CHAR ( WM_APP_BASE+401 )
42 #define WM_STATS_CHAR ( WM_APP_BASE+402 )
43 #define WM_STATS_SIZE ( WM_APP_BASE+403 )
44 #define WM_STATS_PERCENT ( WM_APP_BASE+404 )
45 #define WM_STATS_ELAPSED ( WM_APP_BASE+405 )
46 #define WM_RET_ERR_CNT ( WM_APP_BASE+406 )
47 #define WM_LS_RET_ERR_CNT ( WM_APP_BASE+407 )
48
49 static int verbose = 0;
50 static int recursive = 0;
51 static int preserve = 0;
52 static int targetshouldbedirectory = 0;
53 static int statistics = 1;
54 static int portnumber = 0;
55 static char *password = NULL;
56 static int errs = 0;
57 /* GUI Adaptation - Sept 2000 */
58 #define NAME_STR_MAX 2048
59 static char statname[NAME_STR_MAX+1];
60 static unsigned long statsize = 0;
61 static int statperct = 0;
62 static unsigned long statelapsed = 0;
63 static int gui_mode = 0;
64 static char *gui_hwnd = NULL;
65
66 static void source(char *src);
67 static void rsource(char *src);
68 static void sink(char *targ, char *src);
69 /* GUI Adaptation - Sept 2000 */
70 static void tell_char(FILE *stream, char c);
71 static void tell_str(FILE *stream, char *str);
72 static void tell_user(FILE *stream, char *fmt, ...);
73 static void send_char_msg(unsigned int msg_id, char c);
74 static void send_str_msg(unsigned int msg_id, char *str);
75 static void gui_update_stats(char *name, unsigned long size,
76 int percentage, unsigned long elapsed);
77
78 void logevent(char *string) { }
79
80 void ldisc_send(char *buf, int len) {
81 /*
82 * This is only here because of the calls to ldisc_send(NULL,
83 * 0) in ssh.c. Nothing in PSCP actually needs to use the ldisc
84 * as an ldisc. So if we get called with any real data, I want
85 * to know about it.
86 */
87 assert(len == 0);
88 }
89
90 void verify_ssh_host_key(char *host, int port, char *keytype,
91 char *keystr, char *fingerprint) {
92 int ret;
93
94 static const char absentmsg[] =
95 "The server's host key is not cached in the registry. You\n"
96 "have no guarantee that the server is the computer you\n"
97 "think it is.\n"
98 "The server's key fingerprint is:\n"
99 "%s\n"
100 "If you trust this host, enter \"y\" to add the key to\n"
101 "PuTTY's cache and carry on connecting.\n"
102 "If you do not trust this host, enter \"n\" to abandon the\n"
103 "connection.\n"
104 "Continue connecting? (y/n) ";
105
106 static const char wrongmsg[] =
107 "WARNING - POTENTIAL SECURITY BREACH!\n"
108 "The server's host key does not match the one PuTTY has\n"
109 "cached in the registry. This means that either the\n"
110 "server administrator has changed the host key, or you\n"
111 "have actually connected to another computer pretending\n"
112 "to be the server.\n"
113 "The new key fingerprint is:\n"
114 "%s\n"
115 "If you were expecting this change and trust the new key,\n"
116 "enter Yes to update PuTTY's cache and continue connecting.\n"
117 "If you want to carry on connecting but without updating\n"
118 "the cache, enter No.\n"
119 "If you want to abandon the connection completely, press\n"
120 "Return to cancel. Pressing Return is the ONLY guaranteed\n"
121 "safe choice.\n"
122 "Update cached key? (y/n, Return cancels connection) ";
123
124 static const char abandoned[] = "Connection abandoned.\n";
125
126 char line[32];
127
128 /*
129 * Verify the key against the registry.
130 */
131 ret = verify_host_key(host, port, keytype, keystr);
132
133 if (ret == 0) /* success - key matched OK */
134 return;
135 if (ret == 2) { /* key was different */
136 fprintf(stderr, wrongmsg, fingerprint);
137 if (fgets(line, sizeof(line), stdin) &&
138 line[0] != '\0' && line[0] != '\n') {
139 if (line[0] == 'y' || line[0] == 'Y')
140 store_host_key(host, port, keytype, keystr);
141 } else {
142 fprintf(stderr, abandoned);
143 exit(0);
144 }
145 }
146 if (ret == 1) { /* key was absent */
147 fprintf(stderr, absentmsg, fingerprint);
148 if (fgets(line, sizeof(line), stdin) &&
149 (line[0] == 'y' || line[0] == 'Y'))
150 store_host_key(host, port, keytype, keystr);
151 else {
152 fprintf(stderr, abandoned);
153 exit(0);
154 }
155 }
156 }
157
158 /* GUI Adaptation - Sept 2000 */
159 static void send_msg(HWND h, UINT message, WPARAM wParam)
160 {
161 while (!PostMessage( h, message, wParam, 0))
162 SleepEx(1000,TRUE);
163 }
164
165 static void tell_char(FILE *stream, char c)
166 {
167 if (!gui_mode)
168 fputc(c, stream);
169 else
170 {
171 unsigned int msg_id = WM_STD_OUT_CHAR;
172 if (stream == stderr) msg_id = WM_STD_ERR_CHAR;
173 send_msg( (HWND)atoi(gui_hwnd), msg_id, (WPARAM)c );
174 }
175 }
176
177 static void tell_str(FILE *stream, char *str)
178 {
179 unsigned int i;
180
181 for( i = 0; i < strlen(str); ++i )
182 tell_char(stream, str[i]);
183 }
184
185 static void tell_user(FILE *stream, char *fmt, ...)
186 {
187 char str[0x100]; /* Make the size big enough */
188 va_list ap;
189 va_start(ap, fmt);
190 vsprintf(str, fmt, ap);
191 va_end(ap);
192 strcat(str, "\n");
193 tell_str(stream, str);
194 }
195
196 static void gui_update_stats(char *name, unsigned long size, int percentage, unsigned long elapsed)
197 {
198 unsigned int i;
199
200 if (strcmp(name,statname) != 0)
201 {
202 for( i = 0; i < strlen(name); ++i )
203 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_CHAR, (WPARAM)name[i]);
204 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_CHAR, (WPARAM)'\n' );
205 strcpy(statname,name);
206 }
207 if (statsize != size)
208 {
209 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_SIZE, (WPARAM)size );
210 statsize = size;
211 }
212 if (statelapsed != elapsed)
213 {
214 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_ELAPSED, (WPARAM)elapsed );
215 statelapsed = elapsed;
216 }
217 if (statperct != percentage)
218 {
219 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_PERCENT, (WPARAM)percentage );
220 statperct = percentage;
221 }
222 }
223
224 /*
225 * Print an error message and perform a fatal exit.
226 */
227 void fatalbox(char *fmt, ...)
228 {
229 char str[0x100]; /* Make the size big enough */
230 va_list ap;
231 va_start(ap, fmt);
232 strcpy(str, "Fatal:");
233 vsprintf(str+strlen(str), fmt, ap);
234 va_end(ap);
235 strcat(str, "\n");
236 tell_str(stderr, str);
237
238 exit(1);
239 }
240 void connection_fatal(char *fmt, ...)
241 {
242 char str[0x100]; /* Make the size big enough */
243 va_list ap;
244 va_start(ap, fmt);
245 strcpy(str, "Fatal:");
246 vsprintf(str+strlen(str), fmt, ap);
247 va_end(ap);
248 strcat(str, "\n");
249 tell_str(stderr, str);
250
251 exit(1);
252 }
253
254 /*
255 * Be told what socket we're supposed to be using.
256 */
257 static SOCKET scp_ssh_socket;
258 char *do_select(SOCKET skt, int startup) {
259 if (startup)
260 scp_ssh_socket = skt;
261 else
262 scp_ssh_socket = INVALID_SOCKET;
263 return NULL;
264 }
265 extern int select_result(WPARAM, LPARAM);
266
267 /*
268 * Receive a block of data from the SSH link. Block until all data
269 * is available.
270 *
271 * To do this, we repeatedly call the SSH protocol module, with our
272 * own trap in from_backend() to catch the data that comes back. We
273 * do this until we have enough data.
274 */
275
276 static unsigned char *outptr; /* where to put the data */
277 static unsigned outlen; /* how much data required */
278 static unsigned char *pending = NULL; /* any spare data */
279 static unsigned pendlen=0, pendsize=0; /* length and phys. size of buffer */
280 void from_backend(int is_stderr, char *data, int datalen) {
281 unsigned char *p = (unsigned char *)data;
282 unsigned len = (unsigned)datalen;
283
284 /*
285 * stderr data is just spouted to local stderr and otherwise
286 * ignored.
287 */
288 if (is_stderr) {
289 fwrite(data, 1, len, stderr);
290 return;
291 }
292
293 inbuf_head = 0;
294
295 /*
296 * If this is before the real session begins, just return.
297 */
298 if (!outptr)
299 return;
300
301 if (outlen > 0) {
302 unsigned used = outlen;
303 if (used > len) used = len;
304 memcpy(outptr, p, used);
305 outptr += used; outlen -= used;
306 p += used; len -= used;
307 }
308
309 if (len > 0) {
310 if (pendsize < pendlen + len) {
311 pendsize = pendlen + len + 4096;
312 pending = (pending ? srealloc(pending, pendsize) :
313 smalloc(pendsize));
314 if (!pending)
315 fatalbox("Out of memory");
316 }
317 memcpy(pending+pendlen, p, len);
318 pendlen += len;
319 }
320 }
321 static int ssh_scp_recv(unsigned char *buf, int len) {
322 outptr = buf;
323 outlen = len;
324
325 /*
326 * See if the pending-input block contains some of what we
327 * need.
328 */
329 if (pendlen > 0) {
330 unsigned pendused = pendlen;
331 if (pendused > outlen)
332 pendused = outlen;
333 memcpy(outptr, pending, pendused);
334 memmove(pending, pending+pendused, pendlen-pendused);
335 outptr += pendused;
336 outlen -= pendused;
337 pendlen -= pendused;
338 if (pendlen == 0) {
339 pendsize = 0;
340 sfree(pending);
341 pending = NULL;
342 }
343 if (outlen == 0)
344 return len;
345 }
346
347 while (outlen > 0) {
348 fd_set readfds;
349
350 FD_ZERO(&readfds);
351 FD_SET(scp_ssh_socket, &readfds);
352 if (select(1, &readfds, NULL, NULL, NULL) < 0)
353 return 0; /* doom */
354 select_result((WPARAM)scp_ssh_socket, (LPARAM)FD_READ);
355 }
356
357 return len;
358 }
359
360 /*
361 * Loop through the ssh connection and authentication process.
362 */
363 static void ssh_scp_init(void) {
364 if (scp_ssh_socket == INVALID_SOCKET)
365 return;
366 while (!back->sendok()) {
367 fd_set readfds;
368 FD_ZERO(&readfds);
369 FD_SET(scp_ssh_socket, &readfds);
370 if (select(1, &readfds, NULL, NULL, NULL) < 0)
371 return; /* doom */
372 select_result((WPARAM)scp_ssh_socket, (LPARAM)FD_READ);
373 }
374 }
375
376 /*
377 * Print an error message and exit after closing the SSH link.
378 */
379 static void bump(char *fmt, ...)
380 {
381 char str[0x100]; /* Make the size big enough */
382 va_list ap;
383 va_start(ap, fmt);
384 strcpy(str, "Fatal:");
385 vsprintf(str+strlen(str), fmt, ap);
386 va_end(ap);
387 strcat(str, "\n");
388 tell_str(stderr, str);
389
390 if (back != NULL && back->socket() != NULL) {
391 char ch;
392 back->special(TS_EOF);
393 ssh_scp_recv(&ch, 1);
394 }
395 exit(1);
396 }
397
398 static int get_password(const char *prompt, char *str, int maxlen)
399 {
400 HANDLE hin, hout;
401 DWORD savemode, i;
402
403 if (password) {
404 static int tried_once = 0;
405
406 if (tried_once) {
407 return 0;
408 } else {
409 strncpy(str, password, maxlen);
410 str[maxlen-1] = '\0';
411 tried_once = 1;
412 return 1;
413 }
414 }
415
416 /* GUI Adaptation - Sept 2000 */
417 if (gui_mode) {
418 if (maxlen>0) str[0] = '\0';
419 } else {
420 hin = GetStdHandle(STD_INPUT_HANDLE);
421 hout = GetStdHandle(STD_OUTPUT_HANDLE);
422 if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE)
423 bump("Cannot get standard input/output handles");
424
425 GetConsoleMode(hin, &savemode);
426 SetConsoleMode(hin, (savemode & (~ENABLE_ECHO_INPUT)) |
427 ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT);
428
429 WriteFile(hout, prompt, strlen(prompt), &i, NULL);
430 ReadFile(hin, str, maxlen-1, &i, NULL);
431
432 SetConsoleMode(hin, savemode);
433
434 if ((int)i > maxlen) i = maxlen-1; else i = i - 2;
435 str[i] = '\0';
436
437 WriteFile(hout, "\r\n", 2, &i, NULL);
438 }
439
440 return 1;
441 }
442
443 /*
444 * Open an SSH connection to user@host and execute cmd.
445 */
446 static void do_cmd(char *host, char *user, char *cmd)
447 {
448 char *err, *realhost;
449 DWORD namelen;
450
451 if (host == NULL || host[0] == '\0')
452 bump("Empty host name");
453
454 /* Try to load settings for this host */
455 do_defaults(host, &cfg);
456 if (cfg.host[0] == '\0') {
457 /* No settings for this host; use defaults */
458 do_defaults(NULL, &cfg);
459 strncpy(cfg.host, host, sizeof(cfg.host)-1);
460 cfg.host[sizeof(cfg.host)-1] = '\0';
461 cfg.port = 22;
462 }
463
464 /* Set username */
465 if (user != NULL && user[0] != '\0') {
466 strncpy(cfg.username, user, sizeof(cfg.username)-1);
467 cfg.username[sizeof(cfg.username)-1] = '\0';
468 } else if (cfg.username[0] == '\0') {
469 namelen = 0;
470 if (GetUserName(user, &namelen) == FALSE)
471 bump("Empty user name");
472 user = smalloc(namelen * sizeof(char));
473 GetUserName(user, &namelen);
474 if (verbose) tell_user(stderr, "Guessing user name: %s", user);
475 strncpy(cfg.username, user, sizeof(cfg.username)-1);
476 cfg.username[sizeof(cfg.username)-1] = '\0';
477 free(user);
478 }
479
480 if (cfg.protocol != PROT_SSH)
481 cfg.port = 22;
482
483 if (portnumber)
484 cfg.port = portnumber;
485
486 strncpy(cfg.remote_cmd, cmd, sizeof(cfg.remote_cmd));
487 cfg.remote_cmd[sizeof(cfg.remote_cmd)-1] = '\0';
488 cfg.nopty = TRUE;
489
490 back = &ssh_backend;
491
492 err = back->init(cfg.host, cfg.port, &realhost);
493 if (err != NULL)
494 bump("ssh_init: %s", err);
495 ssh_scp_init();
496 if (verbose && realhost != NULL)
497 tell_user(stderr, "Connected to %s\n", realhost);
498 }
499
500 /*
501 * Update statistic information about current file.
502 */
503 static void print_stats(char *name, unsigned long size, unsigned long done,
504 time_t start, time_t now)
505 {
506 float ratebs;
507 unsigned long eta;
508 char etastr[10];
509 int pct;
510
511 /* GUI Adaptation - Sept 2000 */
512 if (gui_mode)
513 gui_update_stats(name, size, (int)(100 * (done*1.0/size)),
514 (unsigned long)difftime(now, start));
515 else {
516 if (now > start)
517 ratebs = (float) done / (now - start);
518 else
519 ratebs = (float) done;
520
521 if (ratebs < 1.0)
522 eta = size - done;
523 else
524 eta = (unsigned long) ((size - done) / ratebs);
525 sprintf(etastr, "%02ld:%02ld:%02ld",
526 eta / 3600, (eta % 3600) / 60, eta % 60);
527
528 pct = (int) (100.0 * (float) done / size);
529
530 printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%",
531 name, done / 1024, ratebs / 1024.0,
532 etastr, pct);
533
534 if (done == size)
535 printf("\n");
536 }
537 }
538
539 /*
540 * Find a colon in str and return a pointer to the colon.
541 * This is used to separate hostname from filename.
542 */
543 static char * colon(char *str)
544 {
545 /* We ignore a leading colon, since the hostname cannot be
546 empty. We also ignore a colon as second character because
547 of filenames like f:myfile.txt. */
548 if (str[0] == '\0' ||
549 str[0] == ':' ||
550 str[1] == ':')
551 return (NULL);
552 while (*str != '\0' &&
553 *str != ':' &&
554 *str != '/' &&
555 *str != '\\')
556 str++;
557 if (*str == ':')
558 return (str);
559 else
560 return (NULL);
561 }
562
563 /*
564 * Wait for a response from the other side.
565 * Return 0 if ok, -1 if error.
566 */
567 static int response(void)
568 {
569 char ch, resp, rbuf[2048];
570 int p;
571
572 if (ssh_scp_recv(&resp, 1) <= 0)
573 bump("Lost connection");
574
575 p = 0;
576 switch (resp) {
577 case 0: /* ok */
578 return (0);
579 default:
580 rbuf[p++] = resp;
581 /* fallthrough */
582 case 1: /* error */
583 case 2: /* fatal error */
584 do {
585 if (ssh_scp_recv(&ch, 1) <= 0)
586 bump("Protocol error: Lost connection");
587 rbuf[p++] = ch;
588 } while (p < sizeof(rbuf) && ch != '\n');
589 rbuf[p-1] = '\0';
590 if (resp == 1)
591 tell_user(stderr, "%s\n", rbuf);
592 else
593 bump("%s", rbuf);
594 errs++;
595 return (-1);
596 }
597 }
598
599 /*
600 * Send an error message to the other side and to the screen.
601 * Increment error counter.
602 */
603 static void run_err(const char *fmt, ...)
604 {
605 char str[2048];
606 va_list ap;
607 va_start(ap, fmt);
608 errs++;
609 strcpy(str, "scp: ");
610 vsprintf(str+strlen(str), fmt, ap);
611 strcat(str, "\n");
612 back->send(str, strlen(str));
613 tell_user(stderr, "%s",str);
614 va_end(ap);
615 }
616
617 /*
618 * Execute the source part of the SCP protocol.
619 */
620 static void source(char *src)
621 {
622 char buf[2048];
623 unsigned long size;
624 char *last;
625 HANDLE f;
626 DWORD attr;
627 unsigned long i;
628 unsigned long stat_bytes;
629 time_t stat_starttime, stat_lasttime;
630
631 attr = GetFileAttributes(src);
632 if (attr == (DWORD)-1) {
633 run_err("%s: No such file or directory", src);
634 return;
635 }
636
637 if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
638 if (recursive) {
639 /*
640 * Avoid . and .. directories.
641 */
642 char *p;
643 p = strrchr(src, '/');
644 if (!p)
645 p = strrchr(src, '\\');
646 if (!p)
647 p = src;
648 else
649 p++;
650 if (!strcmp(p, ".") || !strcmp(p, ".."))
651 /* skip . and .. */;
652 else
653 rsource(src);
654 } else {
655 run_err("%s: not a regular file", src);
656 }
657 return;
658 }
659
660 if ((last = strrchr(src, '/')) == NULL)
661 last = src;
662 else
663 last++;
664 if (strrchr(last, '\\') != NULL)
665 last = strrchr(last, '\\') + 1;
666 if (last == src && strchr(src, ':') != NULL)
667 last = strchr(src, ':') + 1;
668
669 f = CreateFile(src, GENERIC_READ, FILE_SHARE_READ, NULL,
670 OPEN_EXISTING, 0, 0);
671 if (f == INVALID_HANDLE_VALUE) {
672 run_err("%s: Cannot open file", src);
673 return;
674 }
675
676 if (preserve) {
677 FILETIME actime, wrtime;
678 unsigned long mtime, atime;
679 GetFileTime(f, NULL, &actime, &wrtime);
680 TIME_WIN_TO_POSIX(actime, atime);
681 TIME_WIN_TO_POSIX(wrtime, mtime);
682 sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime);
683 back->send(buf, strlen(buf));
684 if (response())
685 return;
686 }
687
688 size = GetFileSize(f, NULL);
689 sprintf(buf, "C0644 %lu %s\n", size, last);
690 if (verbose)
691 tell_user(stderr, "Sending file modes: %s", buf);
692 back->send(buf, strlen(buf));
693 if (response())
694 return;
695
696 if (statistics) {
697 stat_bytes = 0;
698 stat_starttime = time(NULL);
699 stat_lasttime = 0;
700 }
701
702 for (i = 0; i < size; i += 4096) {
703 char transbuf[4096];
704 DWORD j, k = 4096;
705 if (i + k > size) k = size - i;
706 if (! ReadFile(f, transbuf, k, &j, NULL) || j != k) {
707 if (statistics) printf("\n");
708 bump("%s: Read error", src);
709 }
710 back->send(transbuf, k);
711 if (statistics) {
712 stat_bytes += k;
713 if (time(NULL) != stat_lasttime ||
714 i + k == size) {
715 stat_lasttime = time(NULL);
716 print_stats(last, size, stat_bytes,
717 stat_starttime, stat_lasttime);
718 }
719 }
720 }
721 CloseHandle(f);
722
723 back->send("", 1);
724 (void) response();
725 }
726
727 /*
728 * Recursively send the contents of a directory.
729 */
730 static void rsource(char *src)
731 {
732 char buf[2048];
733 char *last;
734 HANDLE dir;
735 WIN32_FIND_DATA fdat;
736 int ok;
737
738 if ((last = strrchr(src, '/')) == NULL)
739 last = src;
740 else
741 last++;
742 if (strrchr(last, '\\') != NULL)
743 last = strrchr(last, '\\') + 1;
744 if (last == src && strchr(src, ':') != NULL)
745 last = strchr(src, ':') + 1;
746
747 /* maybe send filetime */
748
749 sprintf(buf, "D0755 0 %s\n", last);
750 if (verbose)
751 tell_user(stderr, "Entering directory: %s", buf);
752 back->send(buf, strlen(buf));
753 if (response())
754 return;
755
756 sprintf(buf, "%s/*", src);
757 dir = FindFirstFile(buf, &fdat);
758 ok = (dir != INVALID_HANDLE_VALUE);
759 while (ok) {
760 if (strcmp(fdat.cFileName, ".") == 0 ||
761 strcmp(fdat.cFileName, "..") == 0) {
762 } else if (strlen(src) + 1 + strlen(fdat.cFileName) >=
763 sizeof(buf)) {
764 run_err("%s/%s: Name too long", src, fdat.cFileName);
765 } else {
766 sprintf(buf, "%s/%s", src, fdat.cFileName);
767 source(buf);
768 }
769 ok = FindNextFile(dir, &fdat);
770 }
771 FindClose(dir);
772
773 sprintf(buf, "E\n");
774 back->send(buf, strlen(buf));
775 (void) response();
776 }
777
778 /*
779 * Execute the sink part of the SCP protocol.
780 */
781 static void sink(char *targ, char *src)
782 {
783 char buf[2048];
784 char namebuf[2048];
785 char ch;
786 int targisdir = 0;
787 int settime;
788 int exists;
789 DWORD attr;
790 HANDLE f;
791 unsigned long mtime, atime;
792 unsigned int mode;
793 unsigned long size, i;
794 int wrerror = 0;
795 unsigned long stat_bytes;
796 time_t stat_starttime, stat_lasttime;
797 char *stat_name;
798
799 attr = GetFileAttributes(targ);
800 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
801 targisdir = 1;
802
803 if (targetshouldbedirectory && !targisdir)
804 bump("%s: Not a directory", targ);
805
806 back->send("", 1);
807 while (1) {
808 settime = 0;
809 gottime:
810 if (ssh_scp_recv(&ch, 1) <= 0)
811 return;
812 if (ch == '\n')
813 bump("Protocol error: Unexpected newline");
814 i = 0;
815 buf[i++] = ch;
816 do {
817 if (ssh_scp_recv(&ch, 1) <= 0)
818 bump("Lost connection");
819 buf[i++] = ch;
820 } while (i < sizeof(buf) && ch != '\n');
821 buf[i-1] = '\0';
822 switch (buf[0]) {
823 case '\01': /* error */
824 tell_user(stderr, "%s\n", buf+1);
825 errs++;
826 continue;
827 case '\02': /* fatal error */
828 bump("%s", buf+1);
829 case 'E':
830 back->send("", 1);
831 return;
832 case 'T':
833 if (sscanf(buf, "T%ld %*d %ld %*d",
834 &mtime, &atime) == 2) {
835 settime = 1;
836 back->send("", 1);
837 goto gottime;
838 }
839 bump("Protocol error: Illegal time format");
840 case 'C':
841 case 'D':
842 break;
843 default:
844 bump("Protocol error: Expected control record");
845 }
846
847 if (sscanf(buf+1, "%u %lu %[^\n]", &mode, &size, namebuf) != 3)
848 bump("Protocol error: Illegal file descriptor format");
849 /* Security fix: ensure the file ends up where we asked for it. */
850 if (targisdir) {
851 char t[2048];
852 char *p;
853 strcpy(t, targ);
854 if (targ[0] != '\0')
855 strcat(t, "/");
856 p = namebuf + strlen(namebuf);
857 while (p > namebuf && p[-1] != '/' && p[-1] != '\\')
858 p--;
859 strcat(t, p);
860 strcpy(namebuf, t);
861 } else {
862 strcpy(namebuf, targ);
863 }
864 attr = GetFileAttributes(namebuf);
865 exists = (attr != (DWORD)-1);
866
867 if (buf[0] == 'D') {
868 if (exists && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
869 run_err("%s: Not a directory", namebuf);
870 continue;
871 }
872 if (!exists) {
873 if (! CreateDirectory(namebuf, NULL)) {
874 run_err("%s: Cannot create directory",
875 namebuf);
876 continue;
877 }
878 }
879 sink(namebuf, NULL);
880 /* can we set the timestamp for directories ? */
881 continue;
882 }
883
884 f = CreateFile(namebuf, GENERIC_WRITE, 0, NULL,
885 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
886 if (f == INVALID_HANDLE_VALUE) {
887 run_err("%s: Cannot create file", namebuf);
888 continue;
889 }
890
891 back->send("", 1);
892
893 if (statistics) {
894 stat_bytes = 0;
895 stat_starttime = time(NULL);
896 stat_lasttime = 0;
897 if ((stat_name = strrchr(namebuf, '/')) == NULL)
898 stat_name = namebuf;
899 else
900 stat_name++;
901 if (strrchr(stat_name, '\\') != NULL)
902 stat_name = strrchr(stat_name, '\\') + 1;
903 }
904
905 for (i = 0; i < size; i += 4096) {
906 char transbuf[4096];
907 DWORD j, k = 4096;
908 if (i + k > size) k = size - i;
909 if (ssh_scp_recv(transbuf, k) == 0)
910 bump("Lost connection");
911 if (wrerror) continue;
912 if (! WriteFile(f, transbuf, k, &j, NULL) || j != k) {
913 wrerror = 1;
914 if (statistics)
915 printf("\r%-25.25s | %50s\n",
916 stat_name,
917 "Write error.. waiting for end of file");
918 continue;
919 }
920 if (statistics) {
921 stat_bytes += k;
922 if (time(NULL) > stat_lasttime ||
923 i + k == size) {
924 stat_lasttime = time(NULL);
925 print_stats(stat_name, size, stat_bytes,
926 stat_starttime, stat_lasttime);
927 }
928 }
929 }
930 (void) response();
931
932 if (settime) {
933 FILETIME actime, wrtime;
934 TIME_POSIX_TO_WIN(atime, actime);
935 TIME_POSIX_TO_WIN(mtime, wrtime);
936 SetFileTime(f, NULL, &actime, &wrtime);
937 }
938
939 CloseHandle(f);
940 if (wrerror) {
941 run_err("%s: Write error", namebuf);
942 continue;
943 }
944 back->send("", 1);
945 }
946 }
947
948 /*
949 * We will copy local files to a remote server.
950 */
951 static void toremote(int argc, char *argv[])
952 {
953 char *src, *targ, *host, *user;
954 char *cmd;
955 int i;
956
957 targ = argv[argc-1];
958
959 /* Separate host from filename */
960 host = targ;
961 targ = colon(targ);
962 if (targ == NULL)
963 bump("targ == NULL in toremote()");
964 *targ++ = '\0';
965 if (*targ == '\0')
966 targ = ".";
967 /* Substitute "." for emtpy target */
968
969 /* Separate host and username */
970 user = host;
971 host = strrchr(host, '@');
972 if (host == NULL) {
973 host = user;
974 user = NULL;
975 } else {
976 *host++ = '\0';
977 if (*user == '\0')
978 user = NULL;
979 }
980
981 if (argc == 2) {
982 /* Find out if the source filespec covers multiple files
983 if so, we should set the targetshouldbedirectory flag */
984 HANDLE fh;
985 WIN32_FIND_DATA fdat;
986 if (colon(argv[0]) != NULL)
987 bump("%s: Remote to remote not supported", argv[0]);
988 fh = FindFirstFile(argv[0], &fdat);
989 if (fh == INVALID_HANDLE_VALUE)
990 bump("%s: No such file or directory\n", argv[0]);
991 if (FindNextFile(fh, &fdat))
992 targetshouldbedirectory = 1;
993 FindClose(fh);
994 }
995
996 cmd = smalloc(strlen(targ) + 100);
997 sprintf(cmd, "scp%s%s%s%s -t %s",
998 verbose ? " -v" : "",
999 recursive ? " -r" : "",
1000 preserve ? " -p" : "",
1001 targetshouldbedirectory ? " -d" : "",
1002 targ);
1003 do_cmd(host, user, cmd);
1004 sfree(cmd);
1005
1006 (void) response();
1007
1008 for (i = 0; i < argc - 1; i++) {
1009 HANDLE dir;
1010 WIN32_FIND_DATA fdat;
1011 src = argv[i];
1012 if (colon(src) != NULL) {
1013 tell_user(stderr, "%s: Remote to remote not supported\n", src);
1014 errs++;
1015 continue;
1016 }
1017 dir = FindFirstFile(src, &fdat);
1018 if (dir == INVALID_HANDLE_VALUE) {
1019 run_err("%s: No such file or directory", src);
1020 continue;
1021 }
1022 do {
1023 char *last;
1024 char namebuf[2048];
1025 /*
1026 * Ensure that . and .. are never matched by wildcards,
1027 * but only by deliberate action.
1028 */
1029 if (!strcmp(fdat.cFileName, ".") ||
1030 !strcmp(fdat.cFileName, "..")) {
1031 /*
1032 * Find*File has returned a special dir. We require
1033 * that _either_ `src' ends in a backslash followed
1034 * by that string, _or_ `src' is precisely that
1035 * string.
1036 */
1037 int len = strlen(src), dlen = strlen(fdat.cFileName);
1038 if (len == dlen && !strcmp(src, fdat.cFileName)) {
1039 /* ok */;
1040 } else if (len > dlen+1 && src[len-dlen-1] == '\\' &&
1041 !strcmp(src+len-dlen, fdat.cFileName)) {
1042 /* ok */;
1043 } else
1044 continue; /* ignore this one */
1045 }
1046 if (strlen(src) + strlen(fdat.cFileName) >=
1047 sizeof(namebuf)) {
1048 tell_user(stderr, "%s: Name too long", src);
1049 continue;
1050 }
1051 strcpy(namebuf, src);
1052 if ((last = strrchr(namebuf, '/')) == NULL)
1053 last = namebuf;
1054 else
1055 last++;
1056 if (strrchr(last, '\\') != NULL)
1057 last = strrchr(last, '\\') + 1;
1058 if (last == namebuf && strrchr(namebuf, ':') != NULL)
1059 last = strchr(namebuf, ':') + 1;
1060 strcpy(last, fdat.cFileName);
1061 source(namebuf);
1062 } while (FindNextFile(dir, &fdat));
1063 FindClose(dir);
1064 }
1065 }
1066
1067 /*
1068 * We will copy files from a remote server to the local machine.
1069 */
1070 static void tolocal(int argc, char *argv[])
1071 {
1072 char *src, *targ, *host, *user;
1073 char *cmd;
1074
1075 if (argc != 2)
1076 bump("More than one remote source not supported");
1077
1078 src = argv[0];
1079 targ = argv[1];
1080
1081 /* Separate host from filename */
1082 host = src;
1083 src = colon(src);
1084 if (src == NULL)
1085 bump("Local to local copy not supported");
1086 *src++ = '\0';
1087 if (*src == '\0')
1088 src = ".";
1089 /* Substitute "." for empty filename */
1090
1091 /* Separate username and hostname */
1092 user = host;
1093 host = strrchr(host, '@');
1094 if (host == NULL) {
1095 host = user;
1096 user = NULL;
1097 } else {
1098 *host++ = '\0';
1099 if (*user == '\0')
1100 user = NULL;
1101 }
1102
1103 cmd = smalloc(strlen(src) + 100);
1104 sprintf(cmd, "scp%s%s%s%s -f %s",
1105 verbose ? " -v" : "",
1106 recursive ? " -r" : "",
1107 preserve ? " -p" : "",
1108 targetshouldbedirectory ? " -d" : "",
1109 src);
1110 do_cmd(host, user, cmd);
1111 sfree(cmd);
1112
1113 sink(targ, src);
1114 }
1115
1116 /*
1117 * We will issue a list command to get a remote directory.
1118 */
1119 static void get_dir_list(int argc, char *argv[])
1120 {
1121 char *src, *host, *user;
1122 char *cmd, *p, *q;
1123 char c;
1124
1125 src = argv[0];
1126
1127 /* Separate host from filename */
1128 host = src;
1129 src = colon(src);
1130 if (src == NULL)
1131 bump("Local to local copy not supported");
1132 *src++ = '\0';
1133 if (*src == '\0')
1134 src = ".";
1135 /* Substitute "." for empty filename */
1136
1137 /* Separate username and hostname */
1138 user = host;
1139 host = strrchr(host, '@');
1140 if (host == NULL) {
1141 host = user;
1142 user = NULL;
1143 } else {
1144 *host++ = '\0';
1145 if (*user == '\0')
1146 user = NULL;
1147 }
1148
1149 cmd = smalloc(4*strlen(src) + 100);
1150 strcpy(cmd, "ls -la '");
1151 p = cmd + strlen(cmd);
1152 for (q = src; *q; q++) {
1153 if (*q == '\'') {
1154 *p++ = '\''; *p++ = '\\'; *p++ = '\''; *p++ = '\'';
1155 } else {
1156 *p++ = *q;
1157 }
1158 }
1159 *p++ = '\'';
1160 *p = '\0';
1161
1162 do_cmd(host, user, cmd);
1163 sfree(cmd);
1164
1165 while (ssh_scp_recv(&c, 1) > 0)
1166 tell_char(stdout, c);
1167 }
1168
1169 /*
1170 * Initialize the Win$ock driver.
1171 */
1172 static void init_winsock(void)
1173 {
1174 WORD winsock_ver;
1175 WSADATA wsadata;
1176
1177 winsock_ver = MAKEWORD(1, 1);
1178 if (WSAStartup(winsock_ver, &wsadata))
1179 bump("Unable to initialise WinSock");
1180 if (LOBYTE(wsadata.wVersion) != 1 ||
1181 HIBYTE(wsadata.wVersion) != 1)
1182 bump("WinSock version is incompatible with 1.1");
1183 }
1184
1185 /*
1186 * Short description of parameters.
1187 */
1188 static void usage(void)
1189 {
1190 printf("PuTTY Secure Copy client\n");
1191 printf("%s\n", ver);
1192 printf("Usage: pscp [options] [user@]host:source target\n");
1193 printf(" pscp [options] source [source...] [user@]host:target\n");
1194 printf(" pscp [options] -ls user@host:filespec\n");
1195 printf("Options:\n");
1196 printf(" -p preserve file attributes\n");
1197 printf(" -q quiet, don't show statistics\n");
1198 printf(" -r copy directories recursively\n");
1199 printf(" -v show verbose messages\n");
1200 printf(" -P port connect to specified port\n");
1201 printf(" -pw passw login with specified password\n");
1202 /* GUI Adaptation - Sept 2000 */
1203 printf(" -gui hWnd GUI mode with the windows handle for receiving messages\n");
1204 exit(1);
1205 }
1206
1207 /*
1208 * Main program (no, really?)
1209 */
1210 int main(int argc, char *argv[])
1211 {
1212 int i;
1213 int list = 0;
1214
1215 default_protocol = PROT_TELNET;
1216
1217 flags = FLAG_STDERR;
1218 ssh_get_password = &get_password;
1219 init_winsock();
1220 sk_init();
1221
1222 for (i = 1; i < argc; i++) {
1223 if (argv[i][0] != '-')
1224 break;
1225 if (strcmp(argv[i], "-v") == 0)
1226 verbose = 1, flags |= FLAG_VERBOSE;
1227 else if (strcmp(argv[i], "-r") == 0)
1228 recursive = 1;
1229 else if (strcmp(argv[i], "-p") == 0)
1230 preserve = 1;
1231 else if (strcmp(argv[i], "-q") == 0)
1232 statistics = 0;
1233 else if (strcmp(argv[i], "-h") == 0 ||
1234 strcmp(argv[i], "-?") == 0)
1235 usage();
1236 else if (strcmp(argv[i], "-P") == 0 && i+1 < argc)
1237 portnumber = atoi(argv[++i]);
1238 else if (strcmp(argv[i], "-pw") == 0 && i+1 < argc)
1239 password = argv[++i];
1240 else if (strcmp(argv[i], "-gui") == 0 && i+1 < argc) {
1241 gui_hwnd = argv[++i];
1242 gui_mode = 1;
1243 } else if (strcmp(argv[i], "-ls") == 0)
1244 list = 1;
1245 else if (strcmp(argv[i], "--") == 0)
1246 { i++; break; }
1247 else
1248 usage();
1249 }
1250 argc -= i;
1251 argv += i;
1252 back = NULL;
1253
1254 if (list) {
1255 if (argc != 1)
1256 usage();
1257 get_dir_list(argc, argv);
1258
1259 } else {
1260
1261 if (argc < 2)
1262 usage();
1263 if (argc > 2)
1264 targetshouldbedirectory = 1;
1265
1266 if (colon(argv[argc-1]) != NULL)
1267 toremote(argc, argv);
1268 else
1269 tolocal(argc, argv);
1270 }
1271
1272 if (back != NULL && back->socket() != NULL) {
1273 char ch;
1274 back->special(TS_EOF);
1275 ssh_scp_recv(&ch, 1);
1276 }
1277 WSACleanup();
1278 random_save_seed();
1279
1280 /* GUI Adaptation - August 2000 */
1281 if (gui_mode) {
1282 unsigned int msg_id = WM_RET_ERR_CNT;
1283 if (list) msg_id = WM_LS_RET_ERR_CNT;
1284 while (!PostMessage( (HWND)atoi(gui_hwnd), msg_id, (WPARAM)errs, 0/*lParam*/ ) )
1285 SleepEx(1000,TRUE);
1286 }
1287 return (errs == 0 ? 0 : 1);
1288 }
1289
1290 /* end */