X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/5654298559e80a74bbdadda9b4d1db46b79bcda9..3d88e64dfcf5dc0fd361ce0c504c67a9196ce44c:/psftp.c diff --git a/psftp.c b/psftp.c index 7b67310c..b9f63709 100644 --- a/psftp.c +++ b/psftp.c @@ -25,13 +25,16 @@ */ static int psftp_connect(char *userhost, char *user, int portnumber); -static void do_sftp_init(void); +static int do_sftp_init(void); /* ---------------------------------------------------------------------- * sftp client state. */ char *pwd, *homedir; +static Backend *back; +static void *backhandle; +static Config cfg; /* ---------------------------------------------------------------------- * Higher-level helper functions used in commands. @@ -188,15 +191,15 @@ int sftp_cmd_quit(struct sftp_command *cmd) */ static int sftp_ls_compare(const void *av, const void *bv) { - const struct fxp_name *a = (const struct fxp_name *) av; - const struct fxp_name *b = (const struct fxp_name *) bv; - return strcmp(a->filename, b->filename); + const struct fxp_name *const *a = (const struct fxp_name *const *) av; + const struct fxp_name *const *b = (const struct fxp_name *const *) bv; + return strcmp((*a)->filename, (*b)->filename); } int sftp_cmd_ls(struct sftp_command *cmd) { struct fxp_handle *dirh; struct fxp_names *names; - struct fxp_name *ournames; + struct fxp_name **ournames; int nnames, namesize; char *dir, *cdir; int i; @@ -242,14 +245,12 @@ int sftp_cmd_ls(struct sftp_command *cmd) if (nnames + names->nnames >= namesize) { namesize += names->nnames + 128; - ournames = - srealloc(ournames, namesize * sizeof(*ournames)); + ournames = sresize(ournames, namesize, struct fxp_name *); } for (i = 0; i < names->nnames; i++) - ournames[nnames++] = names->names[i]; + ournames[nnames++] = fxp_dup_name(&names->names[i]); - names->nnames = 0; /* prevent free_names */ fxp_free_names(names); } fxp_close(dirh); @@ -263,8 +264,11 @@ int sftp_cmd_ls(struct sftp_command *cmd) /* * And print them. */ - for (i = 0; i < nnames; i++) - printf("%s\n", ournames[i].longname); + for (i = 0; i < nnames; i++) { + printf("%s\n", ournames[i]->longname); + fxp_free_name(ournames[i]); + } + sfree(ournames); } sfree(cdir); @@ -828,7 +832,7 @@ int sftp_cmd_chmod(struct sftp_command *cmd) if (!(subset & 06777) && (perms &~ subset)) { printf("chmod: file mode '%.*s' contains no user/group/other" " specifier and permissions other than 't' \n", - strcspn(modebegin, ","), modebegin, *mode); + strcspn(modebegin, ","), modebegin); return 0; } perms &= subset; @@ -929,10 +933,10 @@ static int sftp_cmd_lcd(struct sftp_command *cmd) return 0; } - currdir = smalloc(256); + currdir = snewn(256, char); len = GetCurrentDirectory(256, currdir); if (len > 256) - currdir = srealloc(currdir, len); + currdir = sresize(currdir, len, char); GetCurrentDirectory(len, currdir); printf("New local directory is %s\n", currdir); sfree(currdir); @@ -945,10 +949,10 @@ static int sftp_cmd_lpwd(struct sftp_command *cmd) char *currdir; int len; - currdir = smalloc(256); + currdir = snewn(256, char); len = GetCurrentDirectory(256, currdir); if (len > 256) - currdir = srealloc(currdir, len); + currdir = sresize(currdir, len, char); GetCurrentDirectory(len, currdir); printf("Current local directory is %s\n", currdir); sfree(currdir); @@ -1249,7 +1253,7 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags) } fflush(stdout); - cmd = smalloc(sizeof(struct sftp_command)); + cmd = snew(struct sftp_command); cmd->words = NULL; cmd->nwords = 0; cmd->wordssize = 0; @@ -1261,7 +1265,7 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags) char *ret; linesize += 512; - line = srealloc(line, linesize); + line = sresize(line, linesize, char); ret = fgets(line + linelen, linesize - linelen, fp); if (!ret || (linelen == 0 && line[0] == '\0')) { @@ -1293,7 +1297,7 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags) * containing everything else on the line. */ cmd->nwords = cmd->wordssize = 2; - cmd->words = srealloc(cmd->words, cmd->wordssize * sizeof(char *)); + cmd->words = sresize(cmd->words, cmd->wordssize, char *); cmd->words[0] = "!"; cmd->words[1] = p+1; } else { @@ -1336,8 +1340,7 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags) *r = '\0'; if (cmd->nwords >= cmd->wordssize) { cmd->wordssize = cmd->nwords + 16; - cmd->words = - srealloc(cmd->words, cmd->wordssize * sizeof(char *)); + cmd->words = sresize(cmd->words, cmd->wordssize, char *); } cmd->words[cmd->nwords++] = q; } @@ -1361,7 +1364,7 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags) return cmd; } -static void do_sftp_init(void) +static int do_sftp_init(void) { /* * Do protocol initialisation. @@ -1369,7 +1372,7 @@ static void do_sftp_init(void) if (!fxp_init()) { fprintf(stderr, "Fatal: unable to initialise SFTP: %s\n", fxp_error()); - return; + return 1; /* failure */ } /* @@ -1385,6 +1388,7 @@ static void do_sftp_init(void) printf("Remote working directory is %s\n", homedir); } pwd = dupstr(homedir); + return 0; } void do_sftp(int mode, int modeflags, char *batchfile) @@ -1438,214 +1442,53 @@ void do_sftp(int mode, int modeflags, char *batchfile) static int verbose = 0; -void verify_ssh_host_key(char *host, int port, char *keytype, - char *keystr, char *fingerprint) -{ - int ret; - HANDLE hin; - DWORD savemode, i; - - static const char absentmsg[] = - "The server's host key is not cached in the registry. You\n" - "have no guarantee that the server is the computer you\n" - "think it is.\n" - "The server's key fingerprint is:\n" - "%s\n" - "If you trust this host, enter \"y\" to add the key to\n" - "PuTTY's cache and carry on connecting.\n" - "If you want to carry on connecting just once, without\n" - "adding the key to the cache, enter \"n\".\n" - "If you do not trust this host, press Return to abandon the\n" - "connection.\n" - "Store key in cache? (y/n) "; - - static const char wrongmsg[] = - "WARNING - POTENTIAL SECURITY BREACH!\n" - "The server's host key does not match the one PuTTY has\n" - "cached in the registry. This means that either the\n" - "server administrator has changed the host key, or you\n" - "have actually connected to another computer pretending\n" - "to be the server.\n" - "The new key fingerprint is:\n" - "%s\n" - "If you were expecting this change and trust the new key,\n" - "enter \"y\" to update PuTTY's cache and continue connecting.\n" - "If you want to carry on connecting but without updating\n" - "the cache, enter \"n\".\n" - "If you want to abandon the connection completely, press\n" - "Return to cancel. Pressing Return is the ONLY guaranteed\n" - "safe choice.\n" - "Update cached key? (y/n, Return cancels connection) "; - - static const char abandoned[] = "Connection abandoned.\n"; - - char line[32]; - - /* - * Verify the key against the registry. - */ - ret = verify_host_key(host, port, keytype, keystr); - - if (ret == 0) /* success - key matched OK */ - return; - - if (ret == 2) { /* key was different */ - fprintf(stderr, wrongmsg, fingerprint); - fflush(stderr); - } - if (ret == 1) { /* key was absent */ - fprintf(stderr, absentmsg, fingerprint); - fflush(stderr); - } - - hin = GetStdHandle(STD_INPUT_HANDLE); - GetConsoleMode(hin, &savemode); - SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT | - ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)); - ReadFile(hin, line, sizeof(line) - 1, &i, NULL); - SetConsoleMode(hin, savemode); - - if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') { - if (line[0] == 'y' || line[0] == 'Y') - store_host_key(host, port, keytype, keystr); - } else { - fprintf(stderr, abandoned); - exit(0); - } -} - -/* - * Ask whether the selected cipher is acceptable (since it was - * below the configured 'warn' threshold). - * cs: 0 = both ways, 1 = client->server, 2 = server->client - */ -void askcipher(char *ciphername, int cs) -{ - HANDLE hin; - DWORD savemode, i; - - static const char msg[] = - "The first %scipher supported by the server is\n" - "%s, which is below the configured warning threshold.\n" - "Continue with connection? (y/n) "; - static const char abandoned[] = "Connection abandoned.\n"; - - char line[32]; - - fprintf(stderr, msg, - (cs == 0) ? "" : - (cs == 1) ? "client-to-server " : - "server-to-client ", - ciphername); - fflush(stderr); - - hin = GetStdHandle(STD_INPUT_HANDLE); - GetConsoleMode(hin, &savemode); - SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT | - ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)); - ReadFile(hin, line, sizeof(line) - 1, &i, NULL); - SetConsoleMode(hin, savemode); - - if (line[0] == 'y' || line[0] == 'Y') { - return; - } else { - fprintf(stderr, abandoned); - exit(0); - } -} - -/* - * Ask whether to wipe a session log file before writing to it. - * Returns 2 for wipe, 1 for append, 0 for cancel (don't log). - */ -int askappend(char *filename) -{ - HANDLE hin; - DWORD savemode, i; - - static const char msgtemplate[] = - "The session log file \"%.*s\" already exists.\n" - "You can overwrite it with a new session log,\n" - "append your session log to the end of it,\n" - "or disable session logging for this session.\n" - "Enter \"y\" to wipe the file, \"n\" to append to it,\n" - "or just press Return to disable logging.\n" - "Wipe the log file? (y/n, Return cancels logging) "; - - char line[32]; - - fprintf(stderr, msgtemplate, FILENAME_MAX, filename); - fflush(stderr); - - hin = GetStdHandle(STD_INPUT_HANDLE); - GetConsoleMode(hin, &savemode); - SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT | - ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)); - ReadFile(hin, line, sizeof(line) - 1, &i, NULL); - SetConsoleMode(hin, savemode); - - if (line[0] == 'y' || line[0] == 'Y') - return 2; - else if (line[0] == 'n' || line[0] == 'N') - return 1; - else - return 0; -} - -/* - * Warn about the obsolescent key file format. - */ -void old_keyfile_warning(void) -{ - static const char message[] = - "You are loading an SSH 2 private key which has an\n" - "old version of the file format. This means your key\n" - "file is not fully tamperproof. Future versions of\n" - "PuTTY may stop supporting this private key format,\n" - "so we recommend you convert your key to the new\n" - "format.\n" - "\n" - "Once the key is loaded into PuTTYgen, you can perform\n" - "this conversion simply by saving it again.\n"; - - fputs(message, stderr); -} - /* * Print an error message and perform a fatal exit. */ void fatalbox(char *fmt, ...) { - char str[0x100]; /* Make the size big enough */ + char *str, *str2; va_list ap; va_start(ap, fmt); - strcpy(str, "Fatal:"); - vsprintf(str + strlen(str), fmt, ap); + str = dupvprintf(fmt, ap); + str2 = dupcat("Fatal: ", str, "\n", NULL); + sfree(str); va_end(ap); - strcat(str, "\n"); - fputs(stderr, str); + fputs(str2, stderr); + sfree(str2); - exit(1); + cleanup_exit(1); } -void connection_fatal(char *fmt, ...) +void modalfatalbox(char *fmt, ...) { - char str[0x100]; /* Make the size big enough */ + char *str, *str2; va_list ap; va_start(ap, fmt); - strcpy(str, "Fatal:"); - vsprintf(str + strlen(str), fmt, ap); + str = dupvprintf(fmt, ap); + str2 = dupcat("Fatal: ", str, "\n", NULL); + sfree(str); va_end(ap); - strcat(str, "\n"); - fputs(stderr, str); + fputs(str2, stderr); + sfree(str2); - exit(1); + cleanup_exit(1); } - -void logevent(char *string) +void connection_fatal(void *frontend, char *fmt, ...) { + char *str, *str2; + va_list ap; + va_start(ap, fmt); + str = dupvprintf(fmt, ap); + str2 = dupcat("Fatal: ", str, "\n", NULL); + sfree(str); + va_end(ap); + fputs(str2, stderr); + sfree(str2); + + cleanup_exit(1); } -void ldisc_send(char *buf, int len, int interactive) +void ldisc_send(void *handle, char *buf, int len, int interactive) { /* * This is only here because of the calls to ldisc_send(NULL, @@ -1683,11 +1526,13 @@ static unsigned char *outptr; /* where to put the data */ static unsigned outlen; /* how much data required */ static unsigned char *pending = NULL; /* any spare data */ static unsigned pendlen = 0, pendsize = 0; /* length and phys. size of buffer */ -int from_backend(int is_stderr, char *data, int datalen) +int from_backend(void *frontend, int is_stderr, const char *data, int datalen) { unsigned char *p = (unsigned char *) data; unsigned len = (unsigned) datalen; + assert(len > 0); + /* * stderr data is just spouted to local stderr and otherwise * ignored. @@ -1717,10 +1562,7 @@ int from_backend(int is_stderr, char *data, int datalen) if (len > 0) { if (pendsize < pendlen + len) { pendsize = pendlen + len + 4096; - pending = (pending ? srealloc(pending, pendsize) : - smalloc(pendsize)); - if (!pending) - fatalbox("Out of memory"); + pending = sresize(pending, pendsize, unsigned char); } memcpy(pending + pendlen, p, len); pendlen += len; @@ -1769,7 +1611,7 @@ int sftp_recvdata(char *buf, int len) } int sftp_senddata(char *buf, int len) { - back->send((unsigned char *) buf, len); + back->send(backhandle, (unsigned char *) buf, len); return 1; } @@ -1780,7 +1622,7 @@ static void ssh_sftp_init(void) { if (sftp_ssh_socket == INVALID_SOCKET) return; - while (!back->sendok()) { + while (!back->sendok(backhandle)) { fd_set readfds; FD_ZERO(&readfds); FD_SET(sftp_ssh_socket, &readfds); @@ -1790,57 +1632,6 @@ static void ssh_sftp_init(void) } } -static char *password = NULL; -static int get_line(const char *prompt, char *str, int maxlen, int is_pw) -{ - HANDLE hin, hout; - DWORD savemode, newmode, i; - - if (password) { - static int tried_once = 0; - - if (tried_once) { - return 0; - } else { - strncpy(str, password, maxlen); - str[maxlen - 1] = '\0'; - tried_once = 1; - return 1; - } - } - - hin = GetStdHandle(STD_INPUT_HANDLE); - hout = GetStdHandle(STD_OUTPUT_HANDLE); - if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE) { - fprintf(stderr, "Cannot get standard input/output handles\n"); - exit(1); - } - - GetConsoleMode(hin, &savemode); - newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT; - if (is_pw) - newmode &= ~ENABLE_ECHO_INPUT; - else - newmode |= ENABLE_ECHO_INPUT; - SetConsoleMode(hin, newmode); - - WriteFile(hout, prompt, strlen(prompt), &i, NULL); - ReadFile(hin, str, maxlen - 1, &i, NULL); - - SetConsoleMode(hin, savemode); - - if ((int) i > maxlen) - i = maxlen - 1; - else - i = i - 2; - str[i] = '\0'; - - if (is_pw) - WriteFile(hout, "\r\n", 2, &i, NULL); - - return 1; -} - /* * Initialize the Win$ock driver. */ @@ -1852,11 +1643,11 @@ static void init_winsock(void) winsock_ver = MAKEWORD(1, 1); if (WSAStartup(winsock_ver, &wsadata)) { fprintf(stderr, "Unable to initialise WinSock"); - exit(1); + cleanup_exit(1); } if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) { fprintf(stderr, "WinSock version is incompatible with 1.1"); - exit(1); + cleanup_exit(1); } } @@ -1873,9 +1664,15 @@ static void usage(void) printf(" -bc output batchfile commands\n"); printf(" -be don't stop batchfile processing if errors\n"); printf(" -v show verbose messages\n"); + printf(" -load sessname Load settings from saved session\n"); + printf(" -l user connect with specified username\n"); printf(" -P port connect to specified port\n"); printf(" -pw passw login with specified password\n"); - exit(1); + printf(" -1 -2 force use of particular SSH protocol version\n"); + printf(" -C enable compression\n"); + printf(" -i key private key file for authentication\n"); + printf(" -batch disable all interactive prompts\n"); + cleanup_exit(1); } /* @@ -1907,10 +1704,23 @@ static int psftp_connect(char *userhost, char *user, int portnumber) do_defaults(NULL, &cfg); strncpy(cfg.host, host, sizeof(cfg.host) - 1); cfg.host[sizeof(cfg.host) - 1] = '\0'; - cfg.port = 22; } /* + * Force use of SSH. (If they got the protocol wrong we assume the + * port is useless too.) + */ + if (cfg.protocol != PROT_SSH) { + cfg.protocol = PROT_SSH; + cfg.port = 22; + } + + /* + * Enact command-line overrides. + */ + cmdline_run_saved(&cfg); + + /* * Trim leading whitespace off the hostname if it's there. */ { @@ -1936,6 +1746,21 @@ static int psftp_connect(char *userhost, char *user, int portnumber) */ cfg.host[strcspn(cfg.host, ":")] = '\0'; + /* + * Remove any remaining whitespace from the hostname. + */ + { + int p1 = 0, p2 = 0; + while (cfg.host[p2] != '\0') { + if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') { + cfg.host[p1] = cfg.host[p2]; + p1++; + } + p2++; + } + cfg.host[p1] = '\0'; + } + /* Set username */ if (user != NULL && user[0] != '\0') { strncpy(cfg.username, user, sizeof(cfg.username) - 1); @@ -1943,9 +1768,10 @@ static int psftp_connect(char *userhost, char *user, int portnumber) } if (!cfg.username[0]) { printf("login as: "); + fflush(stdout); if (!fgets(cfg.username, sizeof(cfg.username), stdin)) { fprintf(stderr, "psftp: aborting\n"); - exit(1); + cleanup_exit(1); } else { int len = strlen(cfg.username); if (cfg.username[len - 1] == '\n') @@ -1953,9 +1779,6 @@ static int psftp_connect(char *userhost, char *user, int portnumber) } } - if (cfg.protocol != PROT_SSH) - cfg.port = 22; - if (portnumber) cfg.port = portnumber; @@ -2001,17 +1824,31 @@ static int psftp_connect(char *userhost, char *user, int portnumber) back = &ssh_backend; - err = back->init(cfg.host, cfg.port, &realhost, 0); + err = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, &realhost,0); if (err != NULL) { fprintf(stderr, "ssh_init: %s\n", err); return 1; } + logctx = log_init(NULL, &cfg); + back->provide_logctx(backhandle, logctx); + console_provide_logctx(logctx); ssh_sftp_init(); if (verbose && realhost != NULL) printf("Connected to %s\n", realhost); return 0; } +void cmdline_error(char *p, ...) +{ + va_list ap; + fprintf(stderr, "psftp: "); + va_start(ap, p); + vfprintf(stderr, p, ap); + va_end(ap); + fprintf(stderr, "\n try typing \"psftp -h\" for help\n"); + exit(1); +} + /* * Main program. Parse arguments etc. */ @@ -2023,31 +1860,40 @@ int main(int argc, char *argv[]) int mode = 0; int modeflags = 0; char *batchfile = NULL; + int errors = 0; flags = FLAG_STDERR | FLAG_INTERACTIVE; - ssh_get_line = &get_line; + cmdline_tooltype = TOOLTYPE_FILETRANSFER; + ssh_get_line = &console_get_line; init_winsock(); sk_init(); userhost = user = NULL; + errors = 0; for (i = 1; i < argc; i++) { + int ret; if (argv[i][0] != '-') { - if (userhost) - usage(); - else - userhost = dupstr(argv[i]); - } else if (strcmp(argv[i], "-v") == 0) { - verbose = 1, flags |= FLAG_VERBOSE; + if (userhost) + usage(); + else + userhost = dupstr(argv[i]); + continue; + } + ret = cmdline_process_param(argv[i], i+1socket() != NULL) { + if (back != NULL && back->socket(backhandle) != NULL) { char ch; - back->special(TS_EOF); + back->special(backhandle, TS_EOF); sftp_recvdata(&ch, 1); } WSACleanup();