2 * psftp.c: front end for PSFTP.
12 #define PUTTY_DO_GLOBALS
19 /* ----------------------------------------------------------------------
20 * String handling routines.
23 char *dupstr(char *s
) {
25 char *p
= smalloc(len
+1);
30 /* Allocate the concatenation of N strings. Terminate arg list with NULL. */
31 char *dupcat(char *s1
, ...) {
39 sn
= va_arg(ap
, char *);
52 sn
= va_arg(ap
, char *);
63 /* ----------------------------------------------------------------------
69 /* ----------------------------------------------------------------------
70 * Higher-level helper functions used in commands.
74 * Attempt to canonify a pathname starting from the pwd. If
75 * canonification fails, at least fall back to returning a _valid_
76 * pathname (though it may be ugly, eg /home/simon/../foobar).
78 char *canonify(char *name
) {
79 char *fullname
, *canonname
;
82 fullname
= dupstr(name
);
85 if (pwd
[strlen(pwd
)-1] == '/')
89 fullname
= dupcat(pwd
, slash
, name
, NULL
);
92 canonname
= fxp_realpath(fullname
);
101 /* ----------------------------------------------------------------------
102 * Actual sftp commands.
104 struct sftp_command
{
106 int nwords
, wordssize
;
107 int (*obey
)(struct sftp_command
*);/* returns <0 to quit */
110 int sftp_cmd_null(struct sftp_command
*cmd
) {
114 int sftp_cmd_unknown(struct sftp_command
*cmd
) {
115 printf("psftp: unknown command \"%s\"\n", cmd
->words
[0]);
119 int sftp_cmd_quit(struct sftp_command
*cmd
) {
124 * List a directory. If no arguments are given, list pwd; otherwise
125 * list the directory given in words[1].
127 static int sftp_ls_compare(const void *av
, const void *bv
) {
128 const struct fxp_name
*a
= (const struct fxp_name
*)av
;
129 const struct fxp_name
*b
= (const struct fxp_name
*)bv
;
130 return strcmp(a
->filename
, b
->filename
);
132 int sftp_cmd_ls(struct sftp_command
*cmd
) {
133 struct fxp_handle
*dirh
;
134 struct fxp_names
*names
;
135 struct fxp_name
*ournames
;
136 int nnames
, namesize
;
145 cdir
= canonify(dir
);
147 printf("%s: %s\n", dir
, fxp_error());
151 printf("Listing directory %s\n", cdir
);
153 dirh
= fxp_opendir(cdir
);
155 printf("Unable to open %s: %s\n", dir
, fxp_error());
157 nnames
= namesize
= 0;
162 names
= fxp_readdir(dirh
);
164 if (fxp_error_type() == SSH_FX_EOF
)
166 printf("Reading directory %s: %s\n", dir
, fxp_error());
169 if (names
->nnames
== 0) {
170 fxp_free_names(names
);
174 if (nnames
+ names
->nnames
>= namesize
) {
175 namesize
+= names
->nnames
+ 128;
176 ournames
= srealloc(ournames
, namesize
* sizeof(*ournames
));
179 for (i
= 0; i
< names
->nnames
; i
++)
180 ournames
[nnames
++] = names
->names
[i
];
182 names
->nnames
= 0; /* prevent free_names */
183 fxp_free_names(names
);
188 * Now we have our filenames. Sort them by actual file
189 * name, and then output the longname parts.
191 qsort(ournames
, nnames
, sizeof(*ournames
), sftp_ls_compare
);
196 for (i
= 0; i
< nnames
; i
++)
197 printf("%s\n", ournames
[i
].longname
);
206 * Change directories. We do this by canonifying the new name, then
207 * trying to OPENDIR it. Only if that succeeds do we set the new pwd.
209 int sftp_cmd_cd(struct sftp_command
*cmd
) {
210 struct fxp_handle
*dirh
;
214 dir
= dupstr(homedir
);
216 dir
= canonify(cmd
->words
[1]);
219 printf("%s: %s\n", dir
, fxp_error());
223 dirh
= fxp_opendir(dir
);
225 printf("Directory %s: %s\n", dir
, fxp_error());
234 printf("Remote directory is now %s\n", pwd
);
240 * Get a file and save it at the local end.
242 int sftp_cmd_get(struct sftp_command
*cmd
) {
243 struct fxp_handle
*fh
;
244 char *fname
, *outfname
;
248 if (cmd
->nwords
< 2) {
249 printf("get: expects a filename\n");
253 fname
= canonify(cmd
->words
[1]);
255 printf("%s: %s\n", cmd
->words
[1], fxp_error());
258 outfname
= (cmd
->nwords
== 2 ? cmd
->words
[1] : cmd
->words
[2]);
260 fh
= fxp_open(fname
, SSH_FXF_READ
);
262 printf("%s: %s\n", fname
, fxp_error());
266 fp
= fopen(outfname
, "wb");
268 printf("local: unable to open %s\n", outfname
);
274 printf("remote:%s => local:%s\n", fname
, outfname
);
276 offset
= uint64_make(0,0);
279 * FIXME: we can use FXP_FSTAT here to get the file size, and
280 * thus put up a progress bar.
287 len
= fxp_read(fh
, buffer
, offset
, sizeof(buffer
));
288 if ((len
== -1 && fxp_error_type() == SSH_FX_EOF
) ||
292 printf("error while reading: %s\n", fxp_error());
298 wlen
= fwrite(buffer
, 1, len
-wpos
, fp
);
300 printf("error while writing local file\n");
305 if (wpos
< len
) /* we had an error */
307 offset
= uint64_add32(offset
, len
);
318 * Send a file and store it at the remote end.
320 int sftp_cmd_put(struct sftp_command
*cmd
) {
321 struct fxp_handle
*fh
;
322 char *fname
, *origoutfname
, *outfname
;
326 if (cmd
->nwords
< 2) {
327 printf("put: expects a filename\n");
331 fname
= cmd
->words
[1];
332 origoutfname
= (cmd
->nwords
== 2 ? cmd
->words
[1] : cmd
->words
[2]);
333 outfname
= canonify(origoutfname
);
335 printf("%s: %s\n", origoutfname
, fxp_error());
339 fp
= fopen(fname
, "rb");
341 printf("local: unable to open %s\n", fname
);
346 fh
= fxp_open(outfname
, SSH_FXF_WRITE
| SSH_FXF_CREAT
| SSH_FXF_TRUNC
);
348 printf("%s: %s\n", outfname
, fxp_error());
353 printf("local:%s => remote:%s\n", fname
, outfname
);
355 offset
= uint64_make(0,0);
358 * FIXME: we can use FXP_FSTAT here to get the file size, and
359 * thus put up a progress bar.
365 len
= fread(buffer
, 1, sizeof(buffer
), fp
);
367 printf("error while reading local file\n");
369 } else if (len
== 0) {
372 if (!fxp_write(fh
, buffer
, offset
, len
)) {
373 printf("error while writing: %s\n", fxp_error());
376 offset
= uint64_add32(offset
, len
);
386 static struct sftp_cmd_lookup
{
388 int (*obey
)(struct sftp_command
*);
391 * List of sftp commands. This is binary-searched so it MUST be
394 {"bye", sftp_cmd_quit
},
396 {"dir", sftp_cmd_ls
},
397 {"exit", sftp_cmd_quit
},
398 {"get", sftp_cmd_get
},
400 {"put", sftp_cmd_put
},
401 {"quit", sftp_cmd_quit
},
404 /* ----------------------------------------------------------------------
405 * Command line reading and parsing.
407 struct sftp_command
*sftp_getcmd(void) {
409 int linelen
, linesize
;
410 struct sftp_command
*cmd
;
417 cmd
= smalloc(sizeof(struct sftp_command
));
423 linesize
= linelen
= 0;
429 line
= srealloc(line
, linesize
);
430 ret
= fgets(line
+linelen
, linesize
-linelen
, stdin
);
432 if (!ret
|| (linelen
== 0 && line
[0] == '\0')) {
433 cmd
->obey
= sftp_cmd_quit
;
435 return cmd
; /* eof */
437 len
= linelen
+ strlen(line
+linelen
);
439 if (line
[linelen
-1] == '\n') {
441 line
[linelen
] = '\0';
447 * Parse the command line into words. The syntax is:
448 * - double quotes are removed, but cause spaces within to be
449 * treated as non-separating.
450 * - a double-doublequote pair is a literal double quote, inside
451 * _or_ outside quotes. Like this:
453 * firstword "second word" "this has ""quotes"" in" sodoes""this""
459 * >this has "quotes" in<
464 /* skip whitespace */
465 while (*p
&& (*p
== ' ' || *p
== '\t')) p
++;
466 /* mark start of word */
467 q
= r
= p
; /* q sits at start, r writes word */
470 if (!quoting
&& (*p
== ' ' || *p
== '\t'))
471 break; /* reached end of word */
472 else if (*p
== '"' && p
[1] == '"')
473 p
+=2, *r
++ = '"'; /* a literal quote */
475 p
++, quoting
= !quoting
;
479 if (*p
) p
++; /* skip over the whitespace */
481 if (cmd
->nwords
>= cmd
->wordssize
) {
482 cmd
->wordssize
= cmd
->nwords
+ 16;
483 cmd
->words
= srealloc(cmd
->words
, cmd
->wordssize
*sizeof(char *));
485 cmd
->words
[cmd
->nwords
++] = q
;
489 * Now parse the first word and assign a function.
492 if (cmd
->nwords
== 0)
493 cmd
->obey
= sftp_cmd_null
;
497 cmd
->obey
= sftp_cmd_unknown
;
500 j
= sizeof(sftp_lookup
) / sizeof(*sftp_lookup
);
503 cmp
= strcmp(cmd
->words
[0], sftp_lookup
[k
].name
);
509 cmd
->obey
= sftp_lookup
[k
].obey
;
520 * Do protocol initialisation.
524 "Fatal: unable to initialise SFTP: %s\n",
529 * Find out where our home directory is.
531 homedir
= fxp_realpath(".");
534 "Warning: failed to resolve home directory: %s\n",
536 homedir
= dupstr(".");
538 printf("Remote working directory is %s\n", homedir
);
540 pwd
= dupstr(homedir
);
542 /* ------------------------------------------------------------------
543 * Now we're ready to do Real Stuff.
546 struct sftp_command
*cmd
;
550 if (cmd
->obey(cmd
) < 0)
555 /* ----------------------------------------------------------------------
556 * Dirty bits: integration with PuTTY.
559 static int verbose
= 0;
561 void verify_ssh_host_key(char *host
, int port
, char *keytype
,
562 char *keystr
, char *fingerprint
) {
565 static const char absentmsg
[] =
566 "The server's host key is not cached in the registry. You\n"
567 "have no guarantee that the server is the computer you\n"
569 "The server's key fingerprint is:\n"
571 "If you trust this host, enter \"y\" to add the key to\n"
572 "PuTTY's cache and carry on connecting.\n"
573 "If you do not trust this host, enter \"n\" to abandon the\n"
575 "Continue connecting? (y/n) ";
577 static const char wrongmsg
[] =
578 "WARNING - POTENTIAL SECURITY BREACH!\n"
579 "The server's host key does not match the one PuTTY has\n"
580 "cached in the registry. This means that either the\n"
581 "server administrator has changed the host key, or you\n"
582 "have actually connected to another computer pretending\n"
583 "to be the server.\n"
584 "The new key fingerprint is:\n"
586 "If you were expecting this change and trust the new key,\n"
587 "enter Yes to update PuTTY's cache and continue connecting.\n"
588 "If you want to carry on connecting but without updating\n"
589 "the cache, enter No.\n"
590 "If you want to abandon the connection completely, press\n"
591 "Return to cancel. Pressing Return is the ONLY guaranteed\n"
593 "Update cached key? (y/n, Return cancels connection) ";
595 static const char abandoned
[] = "Connection abandoned.\n";
600 * Verify the key against the registry.
602 ret
= verify_host_key(host
, port
, keytype
, keystr
);
604 if (ret
== 0) /* success - key matched OK */
606 if (ret
== 2) { /* key was different */
607 fprintf(stderr
, wrongmsg
, fingerprint
);
608 if (fgets(line
, sizeof(line
), stdin
) &&
609 line
[0] != '\0' && line
[0] != '\n') {
610 if (line
[0] == 'y' || line
[0] == 'Y')
611 store_host_key(host
, port
, keytype
, keystr
);
613 fprintf(stderr
, abandoned
);
617 if (ret
== 1) { /* key was absent */
618 fprintf(stderr
, absentmsg
, fingerprint
);
619 if (fgets(line
, sizeof(line
), stdin
) &&
620 (line
[0] == 'y' || line
[0] == 'Y'))
621 store_host_key(host
, port
, keytype
, keystr
);
623 fprintf(stderr
, abandoned
);
630 * Print an error message and perform a fatal exit.
632 void fatalbox(char *fmt
, ...)
634 char str
[0x100]; /* Make the size big enough */
637 strcpy(str
, "Fatal:");
638 vsprintf(str
+strlen(str
), fmt
, ap
);
641 fprintf(stderr
, str
);
645 void connection_fatal(char *fmt
, ...)
647 char str
[0x100]; /* Make the size big enough */
650 strcpy(str
, "Fatal:");
651 vsprintf(str
+strlen(str
), fmt
, ap
);
654 fprintf(stderr
, str
);
659 void logevent(char *string
) { }
661 void ldisc_send(char *buf
, int len
) {
663 * This is only here because of the calls to ldisc_send(NULL,
664 * 0) in ssh.c. Nothing in PSFTP actually needs to use the
665 * ldisc as an ldisc. So if we get called with any real data, I
666 * want to know about it.
672 * Be told what socket we're supposed to be using.
674 static SOCKET sftp_ssh_socket
;
675 char *do_select(SOCKET skt
, int startup
) {
677 sftp_ssh_socket
= skt
;
679 sftp_ssh_socket
= INVALID_SOCKET
;
682 extern int select_result(WPARAM
, LPARAM
);
685 * Receive a block of data from the SSH link. Block until all data
688 * To do this, we repeatedly call the SSH protocol module, with our
689 * own trap in from_backend() to catch the data that comes back. We
690 * do this until we have enough data.
693 static unsigned char *outptr
; /* where to put the data */
694 static unsigned outlen
; /* how much data required */
695 static unsigned char *pending
= NULL
; /* any spare data */
696 static unsigned pendlen
=0, pendsize
=0; /* length and phys. size of buffer */
697 void from_backend(int is_stderr
, char *data
, int datalen
) {
698 unsigned char *p
= (unsigned char *)data
;
699 unsigned len
= (unsigned)datalen
;
702 * stderr data is just spouted to local stderr and otherwise
706 fwrite(data
, 1, len
, stderr
);
711 * If this is before the real session begins, just return.
717 unsigned used
= outlen
;
718 if (used
> len
) used
= len
;
719 memcpy(outptr
, p
, used
);
720 outptr
+= used
; outlen
-= used
;
721 p
+= used
; len
-= used
;
725 if (pendsize
< pendlen
+ len
) {
726 pendsize
= pendlen
+ len
+ 4096;
727 pending
= (pending ?
srealloc(pending
, pendsize
) :
730 fatalbox("Out of memory");
732 memcpy(pending
+pendlen
, p
, len
);
736 int sftp_recvdata(char *buf
, int len
) {
737 outptr
= (unsigned char *)buf
;
741 * See if the pending-input block contains some of what we
745 unsigned pendused
= pendlen
;
746 if (pendused
> outlen
)
748 memcpy(outptr
, pending
, pendused
);
749 memmove(pending
, pending
+pendused
, pendlen
-pendused
);
766 FD_SET(sftp_ssh_socket
, &readfds
);
767 if (select(1, &readfds
, NULL
, NULL
, NULL
) < 0)
769 select_result((WPARAM
)sftp_ssh_socket
, (LPARAM
)FD_READ
);
774 int sftp_senddata(char *buf
, int len
) {
775 back
->send((unsigned char *)buf
, len
);
780 * Loop through the ssh connection and authentication process.
782 static void ssh_sftp_init(void) {
783 if (sftp_ssh_socket
== INVALID_SOCKET
)
785 while (!back
->sendok()) {
788 FD_SET(sftp_ssh_socket
, &readfds
);
789 if (select(1, &readfds
, NULL
, NULL
, NULL
) < 0)
791 select_result((WPARAM
)sftp_ssh_socket
, (LPARAM
)FD_READ
);
795 static char *password
= NULL
;
796 static int get_password(const char *prompt
, char *str
, int maxlen
)
802 static int tried_once
= 0;
807 strncpy(str
, password
, maxlen
);
808 str
[maxlen
-1] = '\0';
814 hin
= GetStdHandle(STD_INPUT_HANDLE
);
815 hout
= GetStdHandle(STD_OUTPUT_HANDLE
);
816 if (hin
== INVALID_HANDLE_VALUE
|| hout
== INVALID_HANDLE_VALUE
) {
817 fprintf(stderr
, "Cannot get standard input/output handles\n");
821 GetConsoleMode(hin
, &savemode
);
822 SetConsoleMode(hin
, (savemode
& (~ENABLE_ECHO_INPUT
)) |
823 ENABLE_PROCESSED_INPUT
| ENABLE_LINE_INPUT
);
825 WriteFile(hout
, prompt
, strlen(prompt
), &i
, NULL
);
826 ReadFile(hin
, str
, maxlen
-1, &i
, NULL
);
828 SetConsoleMode(hin
, savemode
);
830 if ((int)i
> maxlen
) i
= maxlen
-1; else i
= i
- 2;
833 WriteFile(hout
, "\r\n", 2, &i
, NULL
);
839 * Initialize the Win$ock driver.
841 static void init_winsock(void)
846 winsock_ver
= MAKEWORD(1, 1);
847 if (WSAStartup(winsock_ver
, &wsadata
)) {
848 fprintf(stderr
, "Unable to initialise WinSock");
851 if (LOBYTE(wsadata
.wVersion
) != 1 ||
852 HIBYTE(wsadata
.wVersion
) != 1) {
853 fprintf(stderr
, "WinSock version is incompatible with 1.1");
859 * Short description of parameters.
861 static void usage(void)
863 printf("PuTTY Secure File Transfer (SFTP) client\n");
865 printf("Usage: psftp [options] user@host\n");
866 printf("Options:\n");
867 printf(" -v show verbose messages\n");
868 printf(" -P port connect to specified port\n");
869 printf(" -pw passw login with specified password\n");
874 * Main program. Parse arguments etc.
876 int main(int argc
, char *argv
[])
880 char *user
, *host
, *userhost
, *realhost
;
884 ssh_get_password
= &get_password
;
888 userhost
= user
= NULL
;
890 for (i
= 1; i
< argc
; i
++) {
891 if (argv
[i
][0] != '-') {
895 userhost
= dupstr(argv
[i
]);
896 } else if (strcmp(argv
[i
], "-v") == 0) {
897 verbose
= 1, flags
|= FLAG_VERBOSE
;
898 } else if (strcmp(argv
[i
], "-h") == 0 ||
899 strcmp(argv
[i
], "-?") == 0) {
901 } else if (strcmp(argv
[i
], "-l") == 0 && i
+1 < argc
) {
903 } else if (strcmp(argv
[i
], "-P") == 0 && i
+1 < argc
) {
904 portnumber
= atoi(argv
[++i
]);
905 } else if (strcmp(argv
[i
], "-pw") == 0 && i
+1 < argc
) {
906 password
= argv
[++i
];
907 } else if (strcmp(argv
[i
], "--") == 0) {
918 if (argc
> 0 || !userhost
)
921 /* Separate host and username */
923 host
= strrchr(host
, '@');
929 printf("psftp: multiple usernames specified; using \"%s\"\n", user
);
934 /* Try to load settings for this host */
935 do_defaults(host
, &cfg
);
936 if (cfg
.host
[0] == '\0') {
937 /* No settings for this host; use defaults */
938 do_defaults(NULL
, &cfg
);
939 strncpy(cfg
.host
, host
, sizeof(cfg
.host
)-1);
940 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
945 if (user
!= NULL
&& user
[0] != '\0') {
946 strncpy(cfg
.username
, user
, sizeof(cfg
.username
)-1);
947 cfg
.username
[sizeof(cfg
.username
)-1] = '\0';
949 if (!cfg
.username
[0]) {
950 printf("login as: ");
951 if (!fgets(cfg
.username
, sizeof(cfg
.username
), stdin
)) {
952 fprintf(stderr
, "psftp: aborting\n");
955 int len
= strlen(cfg
.username
);
956 if (cfg
.username
[len
-1] == '\n')
957 cfg
.username
[len
-1] = '\0';
961 if (cfg
.protocol
!= PROT_SSH
)
965 cfg
.port
= portnumber
;
967 /* SFTP uses SSH2 by default always */
970 /* Set up subsystem name. FIXME: fudge for SSH1. */
971 strcpy(cfg
.remote_cmd
, "sftp");
972 cfg
.ssh_subsys
= TRUE
;
977 err
= back
->init(cfg
.host
, cfg
.port
, &realhost
);
979 fprintf(stderr
, "ssh_init: %s", err
);
983 if (verbose
&& realhost
!= NULL
)
984 printf("Connected to %s\n", realhost
);
988 if (back
!= NULL
&& back
->socket() != NULL
) {
990 back
->special(TS_EOF
);
991 sftp_recvdata(&ch
, 1);