⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pscp.c

📁 putty
💻 C
📖 第 1 页 / 共 4 页
字号:
/*
 * scp.c  -  Scp (Secure Copy) client for PuTTY.
 * Joris van Rantwijk, Simon Tatham
 *
 * This is mainly based on ssh-1.2.26/scp.c by Timo Rinne & Tatu Ylonen.
 * They, in turn, used stuff from BSD rcp.
 * 
 * (SGT, 2001-09-10: Joris van Rantwijk assures me that although
 * this file as originally submitted was inspired by, and
 * _structurally_ based on, ssh-1.2.26's scp.c, there wasn't any
 * actual code duplicated, so the above comment shouldn't give rise
 * to licensing issues.)
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <time.h>
#include <assert.h>

#define PUTTY_DO_GLOBALS
#include "putty.h"
#include "psftp.h"
#include "ssh.h"
#include "sftp.h"
#include "storage.h"
#include "int64.h"

static int list = 0;
static int verbose = 0;
static int recursive = 0;
static int preserve = 0;
static int targetshouldbedirectory = 0;
static int statistics = 1;
static int prev_stats_len = 0;
static int scp_unsafe_mode = 0;
static int errs = 0;
static int try_scp = 1;
static int try_sftp = 1;
static int main_cmd_is_sftp = 0;
static int fallback_cmd_is_sftp = 0;
static int using_sftp = 0;

static Backend *back;
static void *backhandle;
static Config cfg;

static void source(char *src);
static void rsource(char *src);
static void sink(char *targ, char *src);

/*
 * The maximum amount of queued data we accept before we stop and
 * wait for the server to process some.
 */
#define MAX_SCP_BUFSIZE 16384

void ldisc_send(void *handle, char *buf, int len, int interactive)
{
    /*
     * This is only here because of the calls to ldisc_send(NULL,
     * 0) in ssh.c. Nothing in PSCP actually needs to use the ldisc
     * as an ldisc. So if we get called with any real data, I want
     * to know about it.
     */
    assert(len == 0);
}

static void tell_char(FILE * stream, char c)
{
    fputc(c, stream);
}

static void tell_str(FILE * stream, char *str)
{
    unsigned int i;

    for (i = 0; i < strlen(str); ++i)
	tell_char(stream, str[i]);
}

static void tell_user(FILE * stream, char *fmt, ...)
{
    char *str, *str2;
    va_list ap;
    va_start(ap, fmt);
    str = dupvprintf(fmt, ap);
    va_end(ap);
    str2 = dupcat(str, "\n", NULL);
    sfree(str);
    tell_str(stream, str2);
    sfree(str2);
}

/*
 *  Print an error message and perform a fatal exit.
 */
void fatalbox(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);
    tell_str(stderr, str2);
    sfree(str2);
    errs++;

    cleanup_exit(1);
}
void modalfatalbox(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);
    tell_str(stderr, str2);
    sfree(str2);
    errs++;

    cleanup_exit(1);
}
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);
    tell_str(stderr, str2);
    sfree(str2);
    errs++;

    cleanup_exit(1);
}

/*
 * In pscp, all agent requests should be synchronous, so this is a
 * never-called stub.
 */
void agent_schedule_callback(void (*callback)(void *, void *, int),
			     void *callback_ctx, void *data, int len)
{
    assert(!"We shouldn't be here");
}

/*
 * Receive a block of data from the SSH link. Block until all data
 * is available.
 *
 * To do this, we repeatedly call the SSH protocol module, with our
 * own trap in from_backend() to catch the data that comes back. We
 * do this until we have enough data.
 */

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(void *frontend, int is_stderr, const char *data, int datalen)
{
    unsigned char *p = (unsigned char *) data;
    unsigned len = (unsigned) datalen;

    /*
     * stderr data is just spouted to local stderr and otherwise
     * ignored.
     */
    if (is_stderr) {
	if (len > 0)
	    fwrite(data, 1, len, stderr);
	return 0;
    }

    /*
     * If this is before the real session begins, just return.
     */
    if (!outptr)
	return 0;

    if ((outlen > 0) && (len > 0)) {
	unsigned used = outlen;
	if (used > len)
	    used = len;
	memcpy(outptr, p, used);
	outptr += used;
	outlen -= used;
	p += used;
	len -= used;
    }

    if (len > 0) {
	if (pendsize < pendlen + len) {
	    pendsize = pendlen + len + 4096;
	    pending = sresize(pending, pendsize, unsigned char);
	}
	memcpy(pending + pendlen, p, len);
	pendlen += len;
    }

    return 0;
}
int from_backend_untrusted(void *frontend_handle, const char *data, int len)
{
    /*
     * No "untrusted" output should get here (the way the code is
     * currently, it's all diverted by FLAG_STDERR).
     */
    assert(!"Unexpected call to from_backend_untrusted()");
    return 0; /* not reached */
}
static int ssh_scp_recv(unsigned char *buf, int len)
{
    outptr = buf;
    outlen = len;

    /*
     * See if the pending-input block contains some of what we
     * need.
     */
    if (pendlen > 0) {
	unsigned pendused = pendlen;
	if (pendused > outlen)
	    pendused = outlen;
	memcpy(outptr, pending, pendused);
	memmove(pending, pending + pendused, pendlen - pendused);
	outptr += pendused;
	outlen -= pendused;
	pendlen -= pendused;
	if (pendlen == 0) {
	    pendsize = 0;
	    sfree(pending);
	    pending = NULL;
	}
	if (outlen == 0)
	    return len;
    }

    while (outlen > 0) {
	if (back->exitcode(backhandle) >= 0 || ssh_sftp_loop_iteration() < 0)
	    return 0;		       /* doom */
    }

    return len;
}

/*
 * Loop through the ssh connection and authentication process.
 */
static void ssh_scp_init(void)
{
    while (!back->sendok(backhandle)) {
	if (ssh_sftp_loop_iteration() < 0)
	    return;		       /* doom */
    }

    /* Work out which backend we ended up using. */
    if (!ssh_fallback_cmd(backhandle))
	using_sftp = main_cmd_is_sftp;
    else
	using_sftp = fallback_cmd_is_sftp;

    if (verbose) {
	if (using_sftp)
	    tell_user(stderr, "Using SFTP");
	else
	    tell_user(stderr, "Using SCP1");
    }
}

/*
 *  Print an error message and exit after closing the SSH link.
 */
static void bump(char *fmt, ...)
{
    char *str, *str2;
    va_list ap;
    va_start(ap, fmt);
    str = dupvprintf(fmt, ap);
    va_end(ap);
    str2 = dupcat(str, "\n", NULL);
    sfree(str);
    tell_str(stderr, str2);
    sfree(str2);
    errs++;

    if (back != NULL && back->connected(backhandle)) {
	char ch;
	back->special(backhandle, TS_EOF);
	ssh_scp_recv((unsigned char *) &ch, 1);
    }

    cleanup_exit(1);
}

/*
 *  Open an SSH connection to user@host and execute cmd.
 */
static void do_cmd(char *host, char *user, char *cmd)
{
    const char *err;
    char *realhost;
    void *logctx;

    if (host == NULL || host[0] == '\0')
	bump("Empty host name");

    /*
     * Remove fiddly bits of address: remove a colon suffix, and
     * the square brackets around an IPv6 literal address.
     */
    if (host[0] == '[') {
	host++;
	host[strcspn(host, "]")] = '\0';
    } else {
	host[strcspn(host, ":")] = '\0';
    }

    /*
     * If we haven't loaded session details already (e.g., from -load),
     * try looking for a session called "host".
     */
    if (!loaded_session) {
	/* Try to load settings for `host' into a temporary config */
	Config cfg2;
	cfg2.host[0] = '\0';
	do_defaults(host, &cfg2);
	if (cfg2.host[0] != '\0') {
	    /* Settings present and include hostname */
	    /* Re-load data into the real config. */
	    do_defaults(host, &cfg);
	} else {
	    /* Session doesn't exist or mention a hostname. */
	    /* Use `host' as a bare hostname. */
	    strncpy(cfg.host, host, sizeof(cfg.host) - 1);
	    cfg.host[sizeof(cfg.host) - 1] = '\0';
	}
    } else {
	/* Patch in hostname `host' to session details. */
	strncpy(cfg.host, host, sizeof(cfg.host) - 1);
	cfg.host[sizeof(cfg.host) - 1] = '\0';
    }

    /*
     * 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.
     */
    {
	int space = strspn(cfg.host, " \t");
	memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
    }

    /* See if host is of the form user@host */
    if (cfg.host[0] != '\0') {
	char *atsign = strrchr(cfg.host, '@');
	/* Make sure we're not overflowing the user field */
	if (atsign) {
	    if (atsign - cfg.host < sizeof cfg.username) {
		strncpy(cfg.username, cfg.host, atsign - cfg.host);
		cfg.username[atsign - cfg.host] = '\0';
	    }
	    memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
	}
    }

    /*
     * 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);
	cfg.username[sizeof(cfg.username) - 1] = '\0';
    } else if (cfg.username[0] == '\0') {
	user = get_username();
	if (!user)
	    bump("Empty user name");
	else {
	    if (verbose)
		tell_user(stderr, "Guessing user name: %s", user);
	    strncpy(cfg.username, user, sizeof(cfg.username) - 1);
	    cfg.username[sizeof(cfg.username) - 1] = '\0';
	    sfree(user);
	}
    }

    /*
     * Disable scary things which shouldn't be enabled for simple
     * things like SCP and SFTP: agent forwarding, port forwarding,
     * X forwarding.
     */
    cfg.x11_forward = 0;
    cfg.agentfwd = 0;
    cfg.portfwd[0] = cfg.portfwd[1] = '\0';

    /*
     * Set up main and possibly fallback command depending on
     * options specified by user.
     * Attempt to start the SFTP subsystem as a first choice,
     * falling back to the provided scp command if that fails.
     */
    cfg.remote_cmd_ptr2 = NULL;
    if (try_sftp) {
	/* First choice is SFTP subsystem. */
	main_cmd_is_sftp = 1;
	strcpy(cfg.remote_cmd, "sftp");
	cfg.ssh_subsys = TRUE;
	if (try_scp) {
	    /* Fallback is to use the provided scp command. */
	    fallback_cmd_is_sftp = 0;
	    cfg.remote_cmd_ptr2 = cmd;
	    cfg.ssh_subsys2 = FALSE;
	} else {
	    /* Since we're not going to try SCP, we may as well try
	     * harder to find an SFTP server, since in the current
	     * implementation we have a spare slot. */
	    fallback_cmd_is_sftp = 1;
	    /* see psftp.c for full explanation of this kludge */
	    cfg.remote_cmd_ptr2 = 
		"test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n"
		"test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n"
		"exec sftp-server";
	    cfg.ssh_subsys2 = FALSE;
	}
    } else {
	/* Don't try SFTP at all; just try the scp command. */
	main_cmd_is_sftp = 0;
	cfg.remote_cmd_ptr = cmd;
	cfg.ssh_subsys = FALSE;
    }
    cfg.nopty = TRUE;

    back = &ssh_backend;

    err = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, &realhost, 
		     0, cfg.tcp_keepalives);
    if (err != NULL)
	bump("ssh_init: %s", err);
    logctx = log_init(NULL, &cfg);
    back->provide_logctx(backhandle, logctx);
    console_provide_logctx(logctx);
    ssh_scp_init();
    if (verbose && realhost != NULL)
	tell_user(stderr, "Connected to %s\n", realhost);
    sfree(realhost);
}

/*
 *  Update statistic information about current file.
 */
static void print_stats(char *name, uint64 size, uint64 done,
			time_t start, time_t now)
{
    float ratebs;
    unsigned long eta;
    char *etastr;
    int pct;
    int len;
    int elap;
    double donedbl;
    double sizedbl;

    elap = (unsigned long) difftime(now, start);

    if (now > start)
	ratebs = (float) (uint64_to_double(done) / elap);
    else
	ratebs = (float) uint64_to_double(done);

    if (ratebs < 1.0)
	eta = (unsigned long) (uint64_to_double(uint64_subtract(size, done)));
    else {
        eta = (unsigned long)
	    ((uint64_to_double(uint64_subtract(size, done)) / ratebs));
    }

    etastr = dupprintf("%02ld:%02ld:%02ld",
		       eta / 3600, (eta % 3600) / 60, eta % 60);

    donedbl = uint64_to_double(done);
    sizedbl = uint64_to_double(size);
    pct = (int) (100 * (donedbl * 1.0 / sizedbl));

    {
	char donekb[40];
	/* divide by 1024 to provide kB */
	uint64_decimal(uint64_shift_right(done, 10), donekb);
	len = printf("\r%-25.25s | %s kB | %5.1f kB/s | ETA: %8s | %3d%%",
		     name,
		     donekb, ratebs / 1024.0, etastr, pct);
	if (len < prev_stats_len)
	    printf("%*s", prev_stats_len - len, "");
	prev_stats_len = len;

	if (uint64_compare(done, size) == 0)
	    printf("\n");

	fflush(stdout);
    }

    free(etastr);
}

/*
 *  Find a colon in str and return a pointer to the colon.
 *  This is used to separate hostname from filename.
 */
static char *colon(char *str)
{
    /* We ignore a leading colon, since the hostname cannot be
       empty. We also ignore a colon as second character because
       of filenames like f:myfile.txt. */
    if (str[0] == '\0' || str[0] == ':' ||
        (str[0] != '[' && str[1] == ':'))
	return (NULL);
    while (*str != '\0' && *str != ':' && *str != '/' && *str != '\\') {
	if (*str == '[') {
	    /* Skip over IPv6 literal addresses
	     * (eg: 'jeroen@[2001:db8::1]:myfile.txt') */
	    char *ipv6_end = strchr(str, ']');
	    if (ipv6_end) {
		str = ipv6_end;
	    }
	}
	str++;
    }
    if (*str == ':')
	return (str);
    else
	return (NULL);
}

/*
 * Return a pointer to the portion of str that comes after the last
 * slash (or backslash or colon, if `local' is TRUE).
 */
static char *stripslashes(char *str, int local)
{
    char *p;

    if (local) {
        p = strchr(str, ':');
        if (p) str = p+1;
    }

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -