scp.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* scp - secure remote copy. This is basically patched BSD rcp which
* uses ssh to do the data transfer (instead of using rcmd).
*
* NOTE: This version should NOT be suid root. (This uses ssh to
* do the transfer and ssh has the necessary privileges.)
*
* 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi>
*
* As far as I am concerned, the code I have written for this software
* can be used freely for any purpose. Any derived versions of this
* software must be clearly marked as such, and if the derived work is
* incompatible with the protocol description in the RFC file, it must be
* called by a name other than "ssh" or "Secure Shell".
*/
/*
* Copyright (c) 1999 Theo de Raadt. All rights reserved.
* Copyright (c) 1999 Aaron Campbell. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Parts from:
*
* Copyright (c) 1983, 1990, 1992, 1993, 1995
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include "includes.h"
#pragma ident "%Z%%M% %I% %E% SMI"
#include "xmalloc.h"
#include "atomicio.h"
#include "pathnames.h"
#include "log.h"
#include "misc.h"
#ifdef HAVE___PROGNAME
extern char *__progname;
#else
char *__progname;
#endif
/* For progressmeter() -- number of seconds before xfer considered "stalled" */
#define STALLTIME 5
/* alarm() interval for updating progress meter */
#define PROGRESSTIME 1
/* Visual statistics about files as they are transferred. */
void progressmeter(int);
/* Returns width of the terminal (for progress meter calculations). */
int getttywidth(void);
int argc);
/* Struct for addargs */
/* Time a transfer started. */
/* Number of bytes of current file transferred so far. */
/* Total size of current file. */
off_t totalbytes = 0;
/* Name of current file being transferred. */
char *curfile;
/* This is set to non-zero to enable verbose mode. */
int verbose_mode = 0;
/* This is set to zero if the progressmeter is not desired. */
int showprogress = 1;
/* This is the program to execute for the secured connection. ("ssh" or -S) */
char *ssh_program = _PATH_SSH_PROGRAM;
/* This is used to store the pid of ssh_program */
static pid_t do_cmd_pid;
/*
* This function executes the given command as the specified user on the
* given host. This returns < 0 if execution fails, and >= 0 otherwise. This
* assigns the input and output file descriptors on success.
*/
int
{
if (verbose_mode)
gettext("Executing: program %s host %s, "
"user %s, command %s\n"),
/*
* Reserve two descriptors so that the real pipes won't get
* descriptors 0 and 1 because that will screw up dup2 below.
*/
/* Create a socket pair for communicating with ssh. */
/* Free the reserved descriptors. */
/* For a child to execute the command on the remote host using ssh. */
if ((do_cmd_pid = fork()) == 0) {
/* Child. */
exit(1);
/* fork() failed */
}
/* Parent. Close the other side, and return the local side. */
return (0);
}
typedef struct {
int cnt;
char *buf;
} BUF;
void lostconn(int);
void nospace(void);
int okname(char *);
void run_err(const char *, ...);
void verifydir(char *);
#define CMDNEEDS 64
int response(void);
void sink(int, char *[]);
void source(int, char *[]);
void tolocal(int, char *[]);
void toremote(char *, int, char *[]);
void usage(void);
int
int argc;
char *argv[];
{
char *targ;
extern char *optarg;
extern int optind;
switch (ch) {
/* User-visible flags. */
case '4':
case '6':
case 'C':
break;
case 'o':
case 'c':
case 'i':
case 'F':
break;
case 'P':
break;
case 'B':
break;
case 'p':
pflag = 1;
break;
case 'r':
iamrecursive = 1;
break;
case 'S':
break;
case 'v':
verbose_mode = 1;
break;
case 'q':
showprogress = 0;
break;
/* Server options. */
case 'd':
break;
case 'f': /* "from" */
iamremote = 1;
fflag = 1;
break;
case 't': /* "to" */
iamremote = 1;
tflag = 1;
#ifdef HAVE_CYGWIN
#endif
break;
default:
usage();
}
if (!isatty(STDERR_FILENO))
showprogress = 0;
if (fflag) {
/* Follow "protocol", send data. */
(void) response();
}
if (tflag) {
/* Receive data. */
}
if (argc < 2)
usage();
if (argc > 2)
/* Command to be executed on remote system using "ssh". */
else {
}
/*
* Finally check the exit status of the ssh process, if one was forked
* and no error has occurred yet
*/
if (remin != -1) {
}
if (remout != -1) {
}
errs = 1;
errs = 1;
}
}
return (errs != 0);
}
void
int argc;
{
int i, len;
*targ++ = 0;
if (*targ == 0)
targ = ".";
/* user@host */
*thost++ = 0;
if (*tuser == '\0')
exit(1);
} else {
}
for (i = 0; i < argc - 1; i++) {
if (src) { /* remote to remote */
static char *ssh_options =
"-x -o'ClearAllForwardings yes'";
*src++ = 0;
if (*src == 0)
src = ".";
if (host) {
*host++ = 0;
if (*suser == '\0')
continue;
"%s%s %s -n "
"-l %s %s %s %s '%s%s%s:%s'",
} else {
"exec %s%s %s -n %s "
"%s %s '%s%s%s:%s'",
}
if (verbose_mode)
} else { /* local to remote */
if (remin == -1) {
exit(1);
if (response() < 0)
exit(1);
}
}
}
}
void
int argc;
char *argv[];
{
int i, len;
for (i = 0; i < argc - 1; i++) {
if (verbose_mode)
++errs;
continue;
}
*src++ = 0;
if (*src == 0)
src = ".";
} else {
*host++ = 0;
if (*suser == '\0')
continue;
}
++errs;
continue;
}
}
}
void
int argc;
char *argv[];
{
int len;
statbytes = 0;
run_err("%s: skipping, filename contains a newline",
name);
goto next;
}
goto syserr;
goto next;
}
case S_IFREG:
break;
case S_IFDIR:
if (iamrecursive) {
goto next;
}
/* FALLTHROUGH */
default:
goto next;
}
else
++last;
if (pflag) {
/*
* Make it compatible with possible future
* versions expecting microseconds.
*/
if (response() < 0)
goto next;
}
#ifdef HAVE_LONG_LONG_INT
#else
/* XXX: Handle integer overflow? */
#endif
if (verbose_mode) {
}
if (response() < 0)
goto next;
continue;
}
if (showprogress) {
progressmeter(-1);
}
/* Keep writing after an error so that we stay sync'd up. */
if (!haderr) {
}
if (haderr)
else {
}
}
if (showprogress)
progressmeter(1);
if (!haderr)
else
(void) response();
}
}
void
char *name;
{
return;
}
if (last == 0)
else
last++;
if (pflag) {
if (response() < 0) {
return;
}
}
if (verbose_mode)
if (response() < 0) {
return;
}
continue;
continue;
sizeof (path) - 1) {
continue;
}
}
(void) response();
}
void
int argc;
char *argv[];
{
enum {
} wrerr;
off_t i, j;
if (!pflag)
if (argc != 1) {
run_err("ambiguous target");
exit(1);
}
targisdir = 1;
return;
if (*cp++ == '\n')
SCREWUP("unexpected <newline>")
do {
sizeof (ch))
SCREWUP("lost connection")
*cp = 0;
if (iamremote == 0)
if (buf[0] == '\02')
exit(1);
++errs;
continue;
}
if (buf[0] == 'E') {
return;
}
if (ch == '\n')
*--cp = 0;
if (*cp == 'T') {
setimes++;
cp++;
SCREWUP("mtime.usec not delimited")
SCREWUP("atime.usec not delimited")
continue;
}
/*
* Check for the case "rcp remote:foo\* local:bar".
* In this case, the line "No match." can be returned
* by the shell before the rcp command on the remote is
* executed so the ^Aerror_message convention isn't
* followed.
*/
if (first) {
exit(1);
}
SCREWUP("expected control record")
}
mode = 0;
SCREWUP("bad mode")
}
if (*cp++ != ' ')
SCREWUP("mode not delimited")
if (*cp++ != ' ')
SCREWUP("size not delimited")
if (targisdir) {
static char *namebuf;
static int cursize;
if (namebuf)
}
} else
if (buf[0] == 'D') {
if (exists) {
goto bad;
}
if (pflag)
} else {
/*
* Handle copying from a read-only
* directory
*/
mod_flag = 1;
goto bad;
}
if (setimes) {
setimes = 0;
run_err("%s: set times: %s",
}
if (mod_flag)
if (vect[0])
continue;
}
continue;
}
continue;
}
if (showprogress) {
totalbytes = size;
progressmeter(-1);
}
statbytes = 0;
amt = 4096;
do {
continue;
} else if (j <= 0) {
"dropped connection");
exit(1);
}
amt -= j;
cp += j;
statbytes += j;
} while (amt > 0);
/* Keep reading so we stay sync'd up. */
count);
if (j != count) {
}
}
count = 0;
}
}
if (showprogress)
progressmeter(1);
}
}
if (pflag) {
#ifdef HAVE_FCHMOD
#else /* HAVE_FCHMOD */
#endif /* HAVE_FCHMOD */
run_err("%s: set mode: %s",
} else {
#ifdef HAVE_FCHMOD
#else /* HAVE_FCHMOD */
#endif /* HAVE_FCHMOD */
run_err("%s: set mode: %s",
}
}
(void) response();
setimes = 0;
run_err("%s: set times: %s",
}
}
switch (wrerr) {
case YES:
break;
case NO:
break;
case DISPLAYED:
break;
}
}
exit(1);
}
int
response(void)
{
lostconn(0);
switch (resp) {
case 0: /* ok */
return (0);
default:
/* FALLTHROUGH */
case 1: /* error, followed by error msg */
case 2: /* fatal error, "" */
do {
sizeof (ch))
lostconn(0);
if (!iamremote)
++errs;
if (resp == 1)
return (-1);
exit(1);
}
/* NOTREACHED */
}
void
usage(void)
{
"Usage: scp [-pqrvBC46] [-F config] [-S program] [-P port]\n"
" [-c cipher] [-i identity] [-o option]\n"
" [[user@]host1:]file1 [...] "
"[[user@]host2:]file2\n"));
exit(1);
}
/* PRINTFLIKE1 */
void
{
++errs;
return;
if (!iamremote) {
}
}
void
char *cp;
{
return;
}
exit(1);
}
int
char *cp0;
{
int c;
char *cp;
do {
c = (int)*cp;
if (c & 0200)
goto bad;
c != '_' && c != '-' && c != '.' && c != '+')
goto bad;
} while (*++cp);
return (1);
return (0);
}
BUF *
{
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
return (0);
}
if (stb.st_blksize == 0)
else
#else /* HAVE_STRUCT_STAT_ST_BLKSIZE */
#endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */
return (bp);
else
return (bp);
}
void
int signo;
{
if (!iamremote)
if (signo)
_exit(1);
else
exit(1);
}
static void
{
int save_errno = errno;
progressmeter(0);
errno = save_errno;
}
static int
foregroundproc(void)
{
int ctty_pgrp;
if (pgrp == -1)
#ifdef HAVE_TCGETPGRP
#else
#endif
}
void
progressmeter(int flag)
{
static const char prefixes[] = " KMGTP";
static struct timeval lastupdate;
double elapsed;
char buf[512];
if (flag == -1) {
lastupdate = start;
lastsize = 0;
}
if (foregroundproc() == 0)
return;
if (totalbytes != 0) {
} else
ratio = 100;
if (barlength > 0) {
"|%.*s%*s|", i,
"*******************************************************"
"*******************************************************"
"*******************************************************"
"*******************************************************"
"*******************************************************"
"*******************************************************"
"*******************************************************",
barlength - i, "");
}
i = 0;
i++;
abbrevsize >>= 10;
}
(unsigned long) abbrevsize, prefixes[i],
lastupdate = now;
}
}
if (flag != 1 &&
" --:-- ETA");
" - stalled -");
} else {
if (flag != 1)
elapsed);
else
i = remaining / 3600;
if (i)
"%2d:", i);
else
" ");
i = remaining % 3600;
"%02d:%02d%s", i / 60, i % 60,
}
if (flag == -1) {
} else if (flag == 1) {
alarm(0);
statbytes = 0;
}
}
int
getttywidth(void)
{
else
return (80);
}