📄 scp.c
字号:
}/* * Execute the sink part of the SCP protocol. */static void sink(char *targ, char *src){ char *destfname; int targisdir = 0; int exists; int attr; WFile *f; unsigned long received; int wrerror = 0; unsigned long stat_bytes; time_t stat_starttime, stat_lasttime; char *stat_name; attr = file_type(targ); if (attr == FILE_TYPE_DIRECTORY) targisdir = 1; if (targetshouldbedirectory && !targisdir) bump("%s: Not a directory", targ); scp_sink_init(); while (1) { struct scp_sink_action act; if (scp_get_sink_action(&act)) return; if (act.action == SCP_SINK_ENDDIR) return; if (act.action == SCP_SINK_RETRY) continue; if (targisdir) { /* * Prevent the remote side from maliciously writing to * files outside the target area by sending a filename * containing `../'. In fact, it shouldn't be sending * filenames with any slashes or colons in at all; so * we'll find the last slash, backslash or colon in the * filename and use only the part after that. (And * warn!) * * In addition, we also ensure here that if we're * copying a single file and the target is a directory * (common usage: `pscp host:filename .') the remote * can't send us a _different_ file name. We can * distinguish this case because `src' will be non-NULL * and the last component of that will fail to match * (the last component of) the name sent. * * Well, not always; if `src' is a wildcard, we do * expect to get back filenames that don't correspond * exactly to it. Ideally in this case, we would like * to ensure that the returned filename actually * matches the wildcard pattern - but one of SCP's * protocol infelicities is that wildcard matching is * done at the server end _by the server's rules_ and * so in general this is infeasible. Hence, we only * accept filenames that don't correspond to `src' if * unsafe mode is enabled or we are using SFTP (which * resolves remote wildcards on the client side and can * be trusted). */ char *striptarget, *stripsrc; striptarget = stripslashes(act.name, 1); if (striptarget != act.name) { tell_user(stderr, "warning: remote host sent a compound" " pathname '%s'", act.name); tell_user(stderr, " renaming local file to '%s'", striptarget); } /* * Also check to see if the target filename is '.' or * '..', or indeed '...' and so on because Windows * appears to interpret those like '..'. */ if (is_dots(striptarget)) { bump("security violation: remote host attempted to write to" " a '.' or '..' path!"); } if (src) { stripsrc = stripslashes(src, 1); if (strcmp(striptarget, stripsrc) && !using_sftp && !scp_unsafe_mode) { tell_user(stderr, "warning: remote host tried to write " "to a file called '%s'", striptarget); tell_user(stderr, " when we requested a file " "called '%s'.", stripsrc); tell_user(stderr, " If this is a wildcard, " "consider upgrading to SSH 2 or using"); tell_user(stderr, " the '-unsafe' option. Renaming" " of this file has been disallowed."); /* Override the name the server provided with our own. */ striptarget = stripsrc; } } if (targ[0] != '\0') destfname = dir_file_cat(targ, striptarget); else destfname = dupstr(striptarget); } else { /* * In this branch of the if, the target area is a * single file with an explicitly specified name in any * case, so there's no danger. */ destfname = dupstr(targ); } attr = file_type(destfname); exists = (attr != FILE_TYPE_NONEXISTENT); if (act.action == SCP_SINK_DIR) { if (exists && attr != FILE_TYPE_DIRECTORY) { run_err("%s: Not a directory", destfname); continue; } if (!exists) { if (!create_directory(destfname)) { run_err("%s: Cannot create directory", destfname); continue; } } sink(destfname, NULL); /* can we set the timestamp for directories ? */ continue; } f = open_new_file(destfname); if (f == NULL) { run_err("%s: Cannot create file", destfname); continue; } if (scp_accept_filexfer()) return; stat_bytes = 0; stat_starttime = time(NULL); stat_lasttime = 0; stat_name = stripslashes(destfname, 1); received = 0; while (received < act.size) { char transbuf[4096]; unsigned long blksize; int read; blksize = 4096; if (blksize > (act.size - received)) blksize = act.size - received; read = scp_recv_filedata(transbuf, (int)blksize); if (read <= 0) bump("Lost connection"); if (wrerror) continue; if (write_to_file(f, transbuf, read) != (int)read) { wrerror = 1; /* FIXME: in sftp we can actually abort the transfer */ if (statistics) printf("\r%-25.25s | %50s\n", stat_name, "Write error.. waiting for end of file"); continue; } if (statistics) { stat_bytes += read; if (time(NULL) > stat_lasttime || received + read == act.size) { stat_lasttime = time(NULL); print_stats(stat_name, act.size, stat_bytes, stat_starttime, stat_lasttime); } } received += read; } if (act.settime) { set_file_times(f, act.mtime, act.atime); } close_wfile(f); if (wrerror) { run_err("%s: Write error", destfname); continue; } (void) scp_finish_filerecv(); sfree(destfname); sfree(act.buf); }}/* * We will copy local files to a remote server. */static void toremote(int argc, char *argv[]){ char *src, *targ, *host, *user; char *cmd; int i, wc_type; targ = argv[argc - 1]; /* Separate host from filename */ host = targ; targ = colon(targ); if (targ == NULL) bump("targ == NULL in toremote()"); *targ++ = '\0'; if (*targ == '\0') targ = "."; /* Substitute "." for emtpy target */ /* Separate host and username */ user = host; host = strrchr(host, '@'); if (host == NULL) { host = user; user = NULL; } else { *host++ = '\0'; if (*user == '\0') user = NULL; } if (argc == 2) { if (colon(argv[0]) != NULL) bump("%s: Remote to remote not supported", argv[0]); wc_type = test_wildcard(argv[0], 1); if (wc_type == WCTYPE_NONEXISTENT) bump("%s: No such file or directory\n", argv[0]); else if (wc_type == WCTYPE_WILDCARD) targetshouldbedirectory = 1; } cmd = dupprintf("scp%s%s%s%s -t %s", verbose ? " -v" : "", recursive ? " -r" : "", preserve ? " -p" : "", targetshouldbedirectory ? " -d" : "", targ); do_cmd(host, user, cmd); sfree(cmd); scp_source_setup(targ, targetshouldbedirectory); for (i = 0; i < argc - 1; i++) { src = argv[i]; if (colon(src) != NULL) { tell_user(stderr, "%s: Remote to remote not supported\n", src); errs++; continue; } wc_type = test_wildcard(src, 1); if (wc_type == WCTYPE_NONEXISTENT) { run_err("%s: No such file or directory", src); continue; } else if (wc_type == WCTYPE_FILENAME) { source(src); continue; } else { WildcardMatcher *wc; char *filename; wc = begin_wildcard_matching(src); if (wc == NULL) { run_err("%s: No such file or directory", src); continue; } while ((filename = wildcard_get_filename(wc)) != NULL) { source(filename); sfree(filename); } finish_wildcard_matching(wc); } }}/* * We will copy files from a remote server to the local machine. */static void tolocal(int argc, char *argv[]){ char *src, *targ, *host, *user; char *cmd; if (argc != 2) bump("More than one remote source not supported"); src = argv[0]; targ = argv[1]; /* Separate host from filename */ host = src; src = colon(src); if (src == NULL) bump("Local to local copy not supported"); *src++ = '\0'; if (*src == '\0') src = "."; /* Substitute "." for empty filename */ /* Separate username and hostname */ user = host; host = strrchr(host, '@'); if (host == NULL) { host = user; user = NULL; } else { *host++ = '\0'; if (*user == '\0') user = NULL; } cmd = dupprintf("scp%s%s%s%s -f %s", verbose ? " -v" : "", recursive ? " -r" : "", preserve ? " -p" : "", targetshouldbedirectory ? " -d" : "", src); do_cmd(host, user, cmd); sfree(cmd); if (scp_sink_setup(src, preserve, recursive)) return; sink(targ, src);}/* * We will issue a list command to get a remote directory. */static void get_dir_list(int argc, char *argv[]){ char *src, *host, *user; char *cmd, *p, *q; char c; src = argv[0]; /* Separate host from filename */ host = src; src = colon(src); if (src == NULL) bump("Local to local copy not supported"); *src++ = '\0'; if (*src == '\0') src = "."; /* Substitute "." for empty filename */ /* Separate username and hostname */ user = host; host = strrchr(host, '@'); if (host == NULL) { host = user; user = NULL; } else { *host++ = '\0'; if (*user == '\0') user = NULL; } cmd = snewn(4 * strlen(src) + 100, char); strcpy(cmd, "ls -la '"); p = cmd + strlen(cmd); for (q = src; *q; q++) { if (*q == '\'') { *p++ = '\''; *p++ = '\\'; *p++ = '\''; *p++ = '\''; } else { *p++ = *q; } } *p++ = '\''; *p = '\0'; do_cmd(host, user, cmd); sfree(cmd); if (using_sftp) { scp_sftp_listdir(src); } else { while (ssh_scp_recv((unsigned char *) &c, 1) > 0) tell_char(stdout, c); }}/* * Short description of parameters. */static void usage(void){ printf("PuTTY Secure Copy client\n"); printf("%s\n", ver); printf("Usage: pscp [options] [user@]host:source target\n"); printf (" pscp [options] source [source...] [user@]host:target\n"); printf(" pscp [options] -ls [user@]host:filespec\n"); printf("Options:\n"); printf(" -p preserve file attributes\n"); printf(" -q quiet, don't show statistics\n"); printf(" -r copy directories recursively\n"); printf(" -v show verbose messages\n"); printf(" -load sessname Load settings from saved session\n"); printf(" -P port connect to specified port\n"); printf(" -l user connect with specified username\n"); printf(" -pw passw login with specified password\n"); 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"); printf(" -unsafe allow server-side wildcards (DANGEROUS)\n"); printf(" -V print version information\n"); printf(" -sftp force use of SFTP protocol\n"); printf(" -scp force use of SCP protocol\n");#if 0 /* * -gui is an internal option, used by GUI front ends to get * pscp to pass progress reports back to them. It's not an * ordinary user-accessible option, so it shouldn't be part of * the command-line help. The only people who need to know * about it are programmers, and they can read the source. */ printf (" -gui hWnd GUI mode with the windows handle for receiving messages\n");#endif cleanup_exit(1);}void version(void){ printf("pscp: %s\n", ver); cleanup_exit(1);}void cmdline_error(char *p, ...){ va_list ap; fprintf(stderr, "pscp: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fprintf(stderr, "\n try typing just \"pscp\" for help\n"); exit(1);}/* * Main program. (Called `psftp_main' because it gets called from * *sftp.c; bit silly, I know, but it had to be called _something_.) */int psftp_main(int argc, char *argv[]){ int i; statics()->default_protocol = PROT_TELNET; statics()->flags = FLAG_STDERR#ifdef FLAG_SYNCAGENT | FLAG_SYNCAGENT#endif ; cmdline_tooltype = TOOLTYPE_FILETRANSFER; statics()->ssh_get_line = &console_get_line; sk_init(); /* Load Default Settings before doing anything else. */ do_defaults(NULL, &cfg); loaded_session = FALSE; for (i = 1; i < argc; i++) { int ret; if (argv[i][0] != '-') break; ret = cmdline_process_param(argv[i], i+1<argc?argv[i+1]:NULL, 1, &cfg); if (ret == -2) { cmdline_error("option \"%s\" requires an argument", argv[i]); } else if (ret == 2) { i++; /* skip next argument */ } else if (ret == 1) { /* We have our own verbosity in addition to `flags'. */ if (statics()->flags & FLAG_VERBOSE) verbose = 1; } else if (strcmp(argv[i], "-r") == 0) { recursive = 1; } else if (strcmp(argv[i], "-p") == 0) { preserve = 1; } else if (strcmp(argv[i], "-q") == 0) { statistics = 0; } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0) { usage(); } else if (strcmp(argv[i], "-V") == 0) { version(); } else if (strcmp(argv[i], "-gui") == 0 && i + 1 < argc) { gui_enable(argv[++i]); gui_mode = 1; console_batch_mode = TRUE; } else if (strcmp(argv[i], "-ls") == 0) { list = 1; } else if (strcmp(argv[i], "-batch") == 0) { console_batch_mode = 1; } else if (strcmp(argv[i], "-unsafe") == 0) { scp_unsafe_mode = 1; } else if (strcmp(argv[i], "-sftp") == 0) { try_scp = 0; try_sftp = 1; } else if (strcmp(argv[i], "-scp") == 0) { try_scp = 1; try_sftp = 0; } else if (strcmp(argv[i], "--") == 0) { i++; break; } else { cmdline_error("unknown option \"%s\"", argv[i]); } } argc -= i; argv += i; back = NULL; if (list) { if (argc != 1) usage(); get_dir_list(argc, argv); } else { if (argc < 2) usage(); if (argc > 2) targetshouldbedirectory = 1; if (colon(argv[argc - 1]) != NULL) toremote(argc, argv); else tolocal(argc, argv); } if (back != NULL && back->socket(backhandle) != NULL) { char ch; back->special(backhandle, TS_EOF); ssh_scp_recv((unsigned char *) &ch, 1); } random_save_seed(); if (gui_mode) gui_send_errcount(list, errs); cmdline_cleanup(); console_provide_logctx(NULL); back->free(backhandle); backhandle = NULL; back = NULL; sk_cleanup(); return (errs == 0 ? 0 : 1);}/* end */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -