1262566Sdes/* $OpenBSD: scp.c,v 1.179 2013/11/20 20:53:10 deraadt Exp $ */ 257429Smarkm/* 365668Skris * scp - secure remote copy. This is basically patched BSD rcp which 465668Skris * uses ssh to do the data transfer (instead of using rcmd). 560573Skris * 665668Skris * NOTE: This version should NOT be suid root. (This uses ssh to 765668Skris * do the transfer and ssh has the necessary privileges.) 860573Skris * 957429Smarkm * 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi> 1060573Skris * 1165668Skris * As far as I am concerned, the code I have written for this software 1265668Skris * can be used freely for any purpose. Any derived versions of this 1365668Skris * software must be clearly marked as such, and if the derived work is 1465668Skris * incompatible with the protocol description in the RFC file, it must be 1565668Skris * called by a name other than "ssh" or "Secure Shell". 1665668Skris */ 1765668Skris/* 1876259Sgreen * Copyright (c) 1999 Theo de Raadt. All rights reserved. 1976259Sgreen * Copyright (c) 1999 Aaron Campbell. All rights reserved. 2065668Skris * 2165668Skris * Redistribution and use in source and binary forms, with or without 2265668Skris * modification, are permitted provided that the following conditions 2365668Skris * are met: 2465668Skris * 1. Redistributions of source code must retain the above copyright 2565668Skris * notice, this list of conditions and the following disclaimer. 2665668Skris * 2. Redistributions in binary form must reproduce the above copyright 2765668Skris * notice, this list of conditions and the following disclaimer in the 2865668Skris * documentation and/or other materials provided with the distribution. 2965668Skris * 3065668Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 3165668Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 3265668Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 3365668Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 3465668Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 3565668Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3665668Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3765668Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3865668Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3965668Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 4065668Skris */ 4157429Smarkm 4257429Smarkm/* 4365668Skris * Parts from: 4465668Skris * 4557429Smarkm * Copyright (c) 1983, 1990, 1992, 1993, 1995 4657429Smarkm * The Regents of the University of California. All rights reserved. 4757429Smarkm * 4857429Smarkm * Redistribution and use in source and binary forms, with or without 4957429Smarkm * modification, are permitted provided that the following conditions 5057429Smarkm * are met: 5157429Smarkm * 1. Redistributions of source code must retain the above copyright 5257429Smarkm * notice, this list of conditions and the following disclaimer. 5357429Smarkm * 2. Redistributions in binary form must reproduce the above copyright 5457429Smarkm * notice, this list of conditions and the following disclaimer in the 5557429Smarkm * documentation and/or other materials provided with the distribution. 56124211Sdes * 3. Neither the name of the University nor the names of its contributors 5757429Smarkm * may be used to endorse or promote products derived from this software 5857429Smarkm * without specific prior written permission. 5957429Smarkm * 6057429Smarkm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 6157429Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 6257429Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 6357429Smarkm * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 6457429Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 6557429Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 6657429Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 6757429Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 6857429Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 6957429Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 7057429Smarkm * SUCH DAMAGE. 7157429Smarkm * 7257429Smarkm */ 7357429Smarkm 7457429Smarkm#include "includes.h" 7557429Smarkm 76162856Sdes#include <sys/types.h> 77162856Sdes#include <sys/param.h> 78162856Sdes#ifdef HAVE_SYS_STAT_H 79162856Sdes# include <sys/stat.h> 80162856Sdes#endif 81181111Sdes#ifdef HAVE_POLL_H 82181111Sdes#include <poll.h> 83181111Sdes#else 84181111Sdes# ifdef HAVE_SYS_POLL_H 85181111Sdes# include <sys/poll.h> 86181111Sdes# endif 87181111Sdes#endif 88162856Sdes#ifdef HAVE_SYS_TIME_H 89162856Sdes# include <sys/time.h> 90162856Sdes#endif 91162856Sdes#include <sys/wait.h> 92162856Sdes#include <sys/uio.h> 93162856Sdes 94162856Sdes#include <ctype.h> 95162856Sdes#include <dirent.h> 96162856Sdes#include <errno.h> 97162856Sdes#include <fcntl.h> 98162856Sdes#include <pwd.h> 99162856Sdes#include <signal.h> 100162856Sdes#include <stdarg.h> 101162856Sdes#include <stdio.h> 102162856Sdes#include <stdlib.h> 103162856Sdes#include <string.h> 104162856Sdes#include <time.h> 105162856Sdes#include <unistd.h> 106248619Sdes#if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) 107181111Sdes#include <vis.h> 108181111Sdes#endif 109162856Sdes 11057429Smarkm#include "xmalloc.h" 11176259Sgreen#include "atomicio.h" 11276259Sgreen#include "pathnames.h" 11376259Sgreen#include "log.h" 11492555Sdes#include "misc.h" 115113911Sdes#include "progressmeter.h" 11657429Smarkm 11798937Sdesextern char *__progname; 11898937Sdes 119181111Sdes#define COPY_BUFLEN 16384 120181111Sdes 121162856Sdesint do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout); 122221420Sdesint do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout); 123162856Sdes 12492555Sdes/* Struct for addargs */ 12592555Sdesarglist args; 126221420Sdesarglist remote_remote_args; 12769587Sgreen 128113911Sdes/* Bandwidth limit */ 129221420Sdeslong long limit_kbps = 0; 130221420Sdesstruct bwlimit bwlimit; 13157429Smarkm 13257429Smarkm/* Name of current file being transferred. */ 13357429Smarkmchar *curfile; 13457429Smarkm 13557429Smarkm/* This is set to non-zero to enable verbose mode. */ 13657429Smarkmint verbose_mode = 0; 13757429Smarkm 13857429Smarkm/* This is set to zero if the progressmeter is not desired. */ 13957429Smarkmint showprogress = 1; 14057429Smarkm 141221420Sdes/* 142221420Sdes * This is set to non-zero if remote-remote copy should be piped 143221420Sdes * through this process. 144221420Sdes */ 145221420Sdesint throughlocal = 0; 146221420Sdes 14765668Skris/* This is the program to execute for the secured connection. ("ssh" or -S) */ 14876259Sgreenchar *ssh_program = _PATH_SSH_PROGRAM; 14965668Skris 150113911Sdes/* This is used to store the pid of ssh_program */ 151124211Sdespid_t do_cmd_pid = -1; 152113911Sdes 153124211Sdesstatic void 154124211Sdeskillchild(int signo) 155124211Sdes{ 156147005Sdes if (do_cmd_pid > 1) { 157149753Sdes kill(do_cmd_pid, signo ? signo : SIGTERM); 158147005Sdes waitpid(do_cmd_pid, NULL, 0); 159147005Sdes } 160124211Sdes 161149753Sdes if (signo) 162149753Sdes _exit(1); 163149753Sdes exit(1); 164124211Sdes} 165124211Sdes 166215116Sdesstatic void 167215116Sdessuspchild(int signo) 168215116Sdes{ 169215116Sdes int status; 170215116Sdes 171215116Sdes if (do_cmd_pid > 1) { 172215116Sdes kill(do_cmd_pid, signo); 173215116Sdes while (waitpid(do_cmd_pid, &status, WUNTRACED) == -1 && 174215116Sdes errno == EINTR) 175215116Sdes ; 176215116Sdes kill(getpid(), SIGSTOP); 177215116Sdes } 178215116Sdes} 179215116Sdes 180157019Sdesstatic int 181157019Sdesdo_local_cmd(arglist *a) 182157019Sdes{ 183157019Sdes u_int i; 184157019Sdes int status; 185157019Sdes pid_t pid; 186157019Sdes 187157019Sdes if (a->num == 0) 188157019Sdes fatal("do_local_cmd: no arguments"); 189157019Sdes 190157019Sdes if (verbose_mode) { 191157019Sdes fprintf(stderr, "Executing:"); 192157019Sdes for (i = 0; i < a->num; i++) 193157019Sdes fprintf(stderr, " %s", a->list[i]); 194157019Sdes fprintf(stderr, "\n"); 195157019Sdes } 196157019Sdes if ((pid = fork()) == -1) 197157019Sdes fatal("do_local_cmd: fork: %s", strerror(errno)); 198157019Sdes 199157019Sdes if (pid == 0) { 200157019Sdes execvp(a->list[0], a->list); 201157019Sdes perror(a->list[0]); 202157019Sdes exit(1); 203157019Sdes } 204157019Sdes 205157019Sdes do_cmd_pid = pid; 206157019Sdes signal(SIGTERM, killchild); 207157019Sdes signal(SIGINT, killchild); 208157019Sdes signal(SIGHUP, killchild); 209157019Sdes 210157019Sdes while (waitpid(pid, &status, 0) == -1) 211157019Sdes if (errno != EINTR) 212157019Sdes fatal("do_local_cmd: waitpid: %s", strerror(errno)); 213157019Sdes 214157019Sdes do_cmd_pid = -1; 215157019Sdes 216157019Sdes if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 217157019Sdes return (-1); 218157019Sdes 219157019Sdes return (0); 220157019Sdes} 221157019Sdes 22257429Smarkm/* 22357429Smarkm * This function executes the given command as the specified user on the 22457429Smarkm * given host. This returns < 0 if execution fails, and >= 0 otherwise. This 22557429Smarkm * assigns the input and output file descriptors on success. 22657429Smarkm */ 22757429Smarkm 22860573Skrisint 229162856Sdesdo_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) 23057429Smarkm{ 23157429Smarkm int pin[2], pout[2], reserved[2]; 23257429Smarkm 23357429Smarkm if (verbose_mode) 23492555Sdes fprintf(stderr, 23592555Sdes "Executing: program %s host %s, user %s, command %s\n", 23692555Sdes ssh_program, host, 23792555Sdes remuser ? remuser : "(unspecified)", cmd); 23857429Smarkm 23957429Smarkm /* 24057429Smarkm * Reserve two descriptors so that the real pipes won't get 24157429Smarkm * descriptors 0 and 1 because that will screw up dup2 below. 24257429Smarkm */ 243162856Sdes if (pipe(reserved) < 0) 244162856Sdes fatal("pipe: %s", strerror(errno)); 24557429Smarkm 24657429Smarkm /* Create a socket pair for communicating with ssh. */ 24757429Smarkm if (pipe(pin) < 0) 24857429Smarkm fatal("pipe: %s", strerror(errno)); 24957429Smarkm if (pipe(pout) < 0) 25057429Smarkm fatal("pipe: %s", strerror(errno)); 25157429Smarkm 25257429Smarkm /* Free the reserved descriptors. */ 25357429Smarkm close(reserved[0]); 25457429Smarkm close(reserved[1]); 25557429Smarkm 256215116Sdes signal(SIGTSTP, suspchild); 257215116Sdes signal(SIGTTIN, suspchild); 258215116Sdes signal(SIGTTOU, suspchild); 259215116Sdes 260124211Sdes /* Fork a child to execute the command on the remote host using ssh. */ 261113911Sdes do_cmd_pid = fork(); 262113911Sdes if (do_cmd_pid == 0) { 26357429Smarkm /* Child. */ 26457429Smarkm close(pin[1]); 26557429Smarkm close(pout[0]); 26657429Smarkm dup2(pin[0], 0); 26757429Smarkm dup2(pout[1], 1); 26857429Smarkm close(pin[0]); 26957429Smarkm close(pout[1]); 27057429Smarkm 271157019Sdes replacearg(&args, 0, "%s", ssh_program); 272204917Sdes if (remuser != NULL) { 273204917Sdes addargs(&args, "-l"); 274204917Sdes addargs(&args, "%s", remuser); 275204917Sdes } 276204917Sdes addargs(&args, "--"); 27792555Sdes addargs(&args, "%s", host); 27892555Sdes addargs(&args, "%s", cmd); 27957429Smarkm 28069587Sgreen execvp(ssh_program, args.list); 28165668Skris perror(ssh_program); 28257429Smarkm exit(1); 283113911Sdes } else if (do_cmd_pid == -1) { 284113911Sdes fatal("fork: %s", strerror(errno)); 28557429Smarkm } 28657429Smarkm /* Parent. Close the other side, and return the local side. */ 28757429Smarkm close(pin[0]); 28857429Smarkm *fdout = pin[1]; 28957429Smarkm close(pout[1]); 29057429Smarkm *fdin = pout[0]; 291124211Sdes signal(SIGTERM, killchild); 292124211Sdes signal(SIGINT, killchild); 293124211Sdes signal(SIGHUP, killchild); 29457429Smarkm return 0; 29557429Smarkm} 29657429Smarkm 297221420Sdes/* 298221420Sdes * This functions executes a command simlar to do_cmd(), but expects the 299221420Sdes * input and output descriptors to be setup by a previous call to do_cmd(). 300221420Sdes * This way the input and output of two commands can be connected. 301221420Sdes */ 302221420Sdesint 303221420Sdesdo_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout) 304221420Sdes{ 305221420Sdes pid_t pid; 306221420Sdes int status; 307221420Sdes 308221420Sdes if (verbose_mode) 309221420Sdes fprintf(stderr, 310221420Sdes "Executing: 2nd program %s host %s, user %s, command %s\n", 311221420Sdes ssh_program, host, 312221420Sdes remuser ? remuser : "(unspecified)", cmd); 313221420Sdes 314221420Sdes /* Fork a child to execute the command on the remote host using ssh. */ 315221420Sdes pid = fork(); 316221420Sdes if (pid == 0) { 317221420Sdes dup2(fdin, 0); 318221420Sdes dup2(fdout, 1); 319221420Sdes 320221420Sdes replacearg(&args, 0, "%s", ssh_program); 321221420Sdes if (remuser != NULL) { 322221420Sdes addargs(&args, "-l"); 323221420Sdes addargs(&args, "%s", remuser); 324221420Sdes } 325221420Sdes addargs(&args, "--"); 326221420Sdes addargs(&args, "%s", host); 327221420Sdes addargs(&args, "%s", cmd); 328221420Sdes 329221420Sdes execvp(ssh_program, args.list); 330221420Sdes perror(ssh_program); 331221420Sdes exit(1); 332221420Sdes } else if (pid == -1) { 333221420Sdes fatal("fork: %s", strerror(errno)); 334221420Sdes } 335221420Sdes while (waitpid(pid, &status, 0) == -1) 336221420Sdes if (errno != EINTR) 337221420Sdes fatal("do_cmd2: waitpid: %s", strerror(errno)); 338221420Sdes return 0; 339221420Sdes} 340221420Sdes 34157429Smarkmtypedef struct { 342149753Sdes size_t cnt; 34357429Smarkm char *buf; 34457429Smarkm} BUF; 34557429Smarkm 34657429SmarkmBUF *allocbuf(BUF *, int, int); 34757429Smarkmvoid lostconn(int); 34857429Smarkmint okname(char *); 34957429Smarkmvoid run_err(const char *,...); 35057429Smarkmvoid verifydir(char *); 35157429Smarkm 35257429Smarkmstruct passwd *pwd; 35357429Smarkmuid_t userid; 35457429Smarkmint errs, remin, remout; 35557429Smarkmint pflag, iamremote, iamrecursive, targetshouldbedirectory; 35657429Smarkm 35757429Smarkm#define CMDNEEDS 64 35857429Smarkmchar cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 35957429Smarkm 36057429Smarkmint response(void); 36157429Smarkmvoid rsource(char *, struct stat *); 36257429Smarkmvoid sink(int, char *[]); 36357429Smarkmvoid source(int, char *[]); 36457429Smarkmvoid tolocal(int, char *[]); 36557429Smarkmvoid toremote(char *, int, char *[]); 36657429Smarkmvoid usage(void); 36757429Smarkm 36857429Smarkmint 369124211Sdesmain(int argc, char **argv) 37057429Smarkm{ 371162856Sdes int ch, fflag, tflag, status, n; 372221420Sdes char *targ, **newargv; 373221420Sdes const char *errstr; 37457429Smarkm extern char *optarg; 37557429Smarkm extern int optind; 37657429Smarkm 377157019Sdes /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 378157019Sdes sanitise_stdfd(); 379157019Sdes 380162856Sdes /* Copy argv, because we modify it */ 381162856Sdes newargv = xcalloc(MAX(argc + 1, 1), sizeof(*newargv)); 382162856Sdes for (n = 0; n < argc; n++) 383162856Sdes newargv[n] = xstrdup(argv[n]); 384162856Sdes argv = newargv; 385162856Sdes 386124211Sdes __progname = ssh_get_progname(argv[0]); 38798937Sdes 388157019Sdes memset(&args, '\0', sizeof(args)); 389221420Sdes memset(&remote_remote_args, '\0', sizeof(remote_remote_args)); 390221420Sdes args.list = remote_remote_args.list = NULL; 391157019Sdes addargs(&args, "%s", ssh_program); 39292555Sdes addargs(&args, "-x"); 393221420Sdes addargs(&args, "-oForwardAgent=no"); 394221420Sdes addargs(&args, "-oPermitLocalCommand=no"); 395221420Sdes addargs(&args, "-oClearAllForwardings=yes"); 39669587Sgreen 39757429Smarkm fflag = tflag = 0; 398221420Sdes while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1) 39957429Smarkm switch (ch) { 40057429Smarkm /* User-visible flags. */ 401113911Sdes case '1': 402113911Sdes case '2': 40357429Smarkm case '4': 40457429Smarkm case '6': 40569587Sgreen case 'C': 40692555Sdes addargs(&args, "-%c", ch); 407221420Sdes addargs(&remote_remote_args, "-%c", ch); 40857429Smarkm break; 409221420Sdes case '3': 410221420Sdes throughlocal = 1; 411221420Sdes break; 41269587Sgreen case 'o': 41369587Sgreen case 'c': 41469587Sgreen case 'i': 41592555Sdes case 'F': 416221420Sdes addargs(&remote_remote_args, "-%c", ch); 417221420Sdes addargs(&remote_remote_args, "%s", optarg); 418204917Sdes addargs(&args, "-%c", ch); 419204917Sdes addargs(&args, "%s", optarg); 42069587Sgreen break; 42169587Sgreen case 'P': 422221420Sdes addargs(&remote_remote_args, "-p"); 423221420Sdes addargs(&remote_remote_args, "%s", optarg); 424204917Sdes addargs(&args, "-p"); 425204917Sdes addargs(&args, "%s", optarg); 42669587Sgreen break; 42769587Sgreen case 'B': 428221420Sdes addargs(&remote_remote_args, "-oBatchmode=yes"); 429221420Sdes addargs(&args, "-oBatchmode=yes"); 43069587Sgreen break; 431113911Sdes case 'l': 432221420Sdes limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024, 433221420Sdes &errstr); 434221420Sdes if (errstr != NULL) 435113911Sdes usage(); 436221420Sdes limit_kbps *= 1024; /* kbps */ 437221420Sdes bandwidth_limit_init(&bwlimit, limit_kbps, COPY_BUFLEN); 438113911Sdes break; 43957429Smarkm case 'p': 44057429Smarkm pflag = 1; 44157429Smarkm break; 44257429Smarkm case 'r': 44357429Smarkm iamrecursive = 1; 44457429Smarkm break; 44565668Skris case 'S': 44669587Sgreen ssh_program = xstrdup(optarg); 44765668Skris break; 44869587Sgreen case 'v': 44992555Sdes addargs(&args, "-v"); 450221420Sdes addargs(&remote_remote_args, "-v"); 45169587Sgreen verbose_mode = 1; 45269587Sgreen break; 45369587Sgreen case 'q': 454126277Sdes addargs(&args, "-q"); 455221420Sdes addargs(&remote_remote_args, "-q"); 45669587Sgreen showprogress = 0; 45769587Sgreen break; 45865668Skris 45957429Smarkm /* Server options. */ 46057429Smarkm case 'd': 46157429Smarkm targetshouldbedirectory = 1; 46257429Smarkm break; 46357429Smarkm case 'f': /* "from" */ 46457429Smarkm iamremote = 1; 46557429Smarkm fflag = 1; 46657429Smarkm break; 46757429Smarkm case 't': /* "to" */ 46857429Smarkm iamremote = 1; 46957429Smarkm tflag = 1; 47098937Sdes#ifdef HAVE_CYGWIN 47198937Sdes setmode(0, O_BINARY); 47298937Sdes#endif 47357429Smarkm break; 47457429Smarkm default: 47557429Smarkm usage(); 47657429Smarkm } 47757429Smarkm argc -= optind; 47857429Smarkm argv += optind; 47957429Smarkm 48057429Smarkm if ((pwd = getpwuid(userid = getuid())) == NULL) 481124211Sdes fatal("unknown user %u", (u_int) userid); 48257429Smarkm 483181111Sdes if (!isatty(STDOUT_FILENO)) 48457429Smarkm showprogress = 0; 48557429Smarkm 48657429Smarkm remin = STDIN_FILENO; 48757429Smarkm remout = STDOUT_FILENO; 48857429Smarkm 48976259Sgreen if (fflag) { 49057429Smarkm /* Follow "protocol", send data. */ 49157429Smarkm (void) response(); 49257429Smarkm source(argc, argv); 49357429Smarkm exit(errs != 0); 49457429Smarkm } 49557429Smarkm if (tflag) { 49657429Smarkm /* Receive data. */ 49757429Smarkm sink(argc, argv); 49857429Smarkm exit(errs != 0); 49957429Smarkm } 50057429Smarkm if (argc < 2) 50157429Smarkm usage(); 50257429Smarkm if (argc > 2) 50357429Smarkm targetshouldbedirectory = 1; 50457429Smarkm 50557429Smarkm remin = remout = -1; 506113911Sdes do_cmd_pid = -1; 50757429Smarkm /* Command to be executed on remote system using "ssh". */ 50876259Sgreen (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", 50976259Sgreen verbose_mode ? " -v" : "", 51065668Skris iamrecursive ? " -r" : "", pflag ? " -p" : "", 51165668Skris targetshouldbedirectory ? " -d" : ""); 51257429Smarkm 51357429Smarkm (void) signal(SIGPIPE, lostconn); 51457429Smarkm 51557429Smarkm if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 51657429Smarkm toremote(targ, argc, argv); 51757429Smarkm else { 51857429Smarkm if (targetshouldbedirectory) 51957429Smarkm verifydir(argv[argc - 1]); 520157019Sdes tolocal(argc, argv); /* Dest is local host. */ 52157429Smarkm } 522113911Sdes /* 523113911Sdes * Finally check the exit status of the ssh process, if one was forked 524192595Sdes * and no error has occurred yet 525113911Sdes */ 526113911Sdes if (do_cmd_pid != -1 && errs == 0) { 527113911Sdes if (remin != -1) 528113911Sdes (void) close(remin); 529113911Sdes if (remout != -1) 530113911Sdes (void) close(remout); 531113911Sdes if (waitpid(do_cmd_pid, &status, 0) == -1) 532113911Sdes errs = 1; 533113911Sdes else { 534113911Sdes if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 535113911Sdes errs = 1; 536113911Sdes } 537113911Sdes } 53857429Smarkm exit(errs != 0); 53957429Smarkm} 54057429Smarkm 541221420Sdes/* Callback from atomicio6 to update progress meter and limit bandwidth */ 542221420Sdesstatic int 543221420Sdesscpio(void *_cnt, size_t s) 544181111Sdes{ 545221420Sdes off_t *cnt = (off_t *)_cnt; 546181111Sdes 547221420Sdes *cnt += s; 548221420Sdes if (limit_kbps > 0) 549221420Sdes bandwidth_limit(&bwlimit, s); 550221420Sdes return 0; 551181111Sdes} 552181111Sdes 553255767Sdesstatic int 554255767Sdesdo_times(int fd, int verb, const struct stat *sb) 555255767Sdes{ 556255767Sdes /* strlen(2^64) == 20; strlen(10^6) == 7 */ 557255767Sdes char buf[(20 + 7 + 2) * 2 + 2]; 558255767Sdes 559255767Sdes (void)snprintf(buf, sizeof(buf), "T%llu 0 %llu 0\n", 560255767Sdes (unsigned long long) (sb->st_mtime < 0 ? 0 : sb->st_mtime), 561255767Sdes (unsigned long long) (sb->st_atime < 0 ? 0 : sb->st_atime)); 562255767Sdes if (verb) { 563255767Sdes fprintf(stderr, "File mtime %lld atime %lld\n", 564255767Sdes (long long)sb->st_mtime, (long long)sb->st_atime); 565255767Sdes fprintf(stderr, "Sending file timestamps: %s", buf); 566255767Sdes } 567255767Sdes (void) atomicio(vwrite, fd, buf, strlen(buf)); 568255767Sdes return (response()); 569255767Sdes} 570255767Sdes 57157429Smarkmvoid 572124211Sdestoremote(char *targ, int argc, char **argv) 57357429Smarkm{ 574147005Sdes char *bp, *host, *src, *suser, *thost, *tuser, *arg; 575157019Sdes arglist alist; 576162856Sdes int i; 577221420Sdes u_int j; 57857429Smarkm 579157019Sdes memset(&alist, '\0', sizeof(alist)); 580157019Sdes alist.list = NULL; 581157019Sdes 58257429Smarkm *targ++ = 0; 58357429Smarkm if (*targ == 0) 58457429Smarkm targ = "."; 58557429Smarkm 586147005Sdes arg = xstrdup(argv[argc - 1]); 587147005Sdes if ((thost = strrchr(arg, '@'))) { 58857429Smarkm /* user@host */ 58957429Smarkm *thost++ = 0; 590147005Sdes tuser = arg; 59157429Smarkm if (*tuser == '\0') 59257429Smarkm tuser = NULL; 59357429Smarkm } else { 594147005Sdes thost = arg; 59557429Smarkm tuser = NULL; 59657429Smarkm } 59757429Smarkm 598157019Sdes if (tuser != NULL && !okname(tuser)) { 599255767Sdes free(arg); 600157019Sdes return; 601157019Sdes } 602157019Sdes 60357429Smarkm for (i = 0; i < argc - 1; i++) { 60457429Smarkm src = colon(argv[i]); 605221420Sdes if (src && throughlocal) { /* extended remote to remote */ 606221420Sdes *src++ = 0; 607221420Sdes if (*src == 0) 608221420Sdes src = "."; 609221420Sdes host = strrchr(argv[i], '@'); 610221420Sdes if (host) { 611221420Sdes *host++ = 0; 612221420Sdes host = cleanhostname(host); 613221420Sdes suser = argv[i]; 614221420Sdes if (*suser == '\0') 615221420Sdes suser = pwd->pw_name; 616221420Sdes else if (!okname(suser)) 617221420Sdes continue; 618221420Sdes } else { 619221420Sdes host = cleanhostname(argv[i]); 620221420Sdes suser = NULL; 621221420Sdes } 622240075Sdes xasprintf(&bp, "%s -f %s%s", cmd, 623240075Sdes *src == '-' ? "-- " : "", src); 624221420Sdes if (do_cmd(host, suser, bp, &remin, &remout) < 0) 625221420Sdes exit(1); 626255767Sdes free(bp); 627221420Sdes host = cleanhostname(thost); 628240075Sdes xasprintf(&bp, "%s -t %s%s", cmd, 629240075Sdes *targ == '-' ? "-- " : "", targ); 630221420Sdes if (do_cmd2(host, tuser, bp, remin, remout) < 0) 631221420Sdes exit(1); 632255767Sdes free(bp); 633221420Sdes (void) close(remin); 634221420Sdes (void) close(remout); 635221420Sdes remin = remout = -1; 636221420Sdes } else if (src) { /* standard remote to remote */ 637157019Sdes freeargs(&alist); 638157019Sdes addargs(&alist, "%s", ssh_program); 639157019Sdes addargs(&alist, "-x"); 640221420Sdes addargs(&alist, "-oClearAllForwardings=yes"); 641157019Sdes addargs(&alist, "-n"); 642221420Sdes for (j = 0; j < remote_remote_args.num; j++) { 643221420Sdes addargs(&alist, "%s", 644221420Sdes remote_remote_args.list[j]); 645221420Sdes } 64657429Smarkm *src++ = 0; 64757429Smarkm if (*src == 0) 64857429Smarkm src = "."; 649113911Sdes host = strrchr(argv[i], '@'); 650157019Sdes 65157429Smarkm if (host) { 65257429Smarkm *host++ = 0; 65357429Smarkm host = cleanhostname(host); 65457429Smarkm suser = argv[i]; 65557429Smarkm if (*suser == '\0') 65657429Smarkm suser = pwd->pw_name; 657157019Sdes else if (!okname(suser)) 65857429Smarkm continue; 659157019Sdes addargs(&alist, "-l"); 660157019Sdes addargs(&alist, "%s", suser); 66157429Smarkm } else { 66257429Smarkm host = cleanhostname(argv[i]); 66357429Smarkm } 664204917Sdes addargs(&alist, "--"); 665157019Sdes addargs(&alist, "%s", host); 666157019Sdes addargs(&alist, "%s", cmd); 667157019Sdes addargs(&alist, "%s", src); 668157019Sdes addargs(&alist, "%s%s%s:%s", 669157019Sdes tuser ? tuser : "", tuser ? "@" : "", 670157019Sdes thost, targ); 671157019Sdes if (do_local_cmd(&alist) != 0) 672126277Sdes errs = 1; 67357429Smarkm } else { /* local to remote */ 67457429Smarkm if (remin == -1) { 675240075Sdes xasprintf(&bp, "%s -t %s%s", cmd, 676240075Sdes *targ == '-' ? "-- " : "", targ); 67757429Smarkm host = cleanhostname(thost); 67865668Skris if (do_cmd(host, tuser, bp, &remin, 679162856Sdes &remout) < 0) 68057429Smarkm exit(1); 68157429Smarkm if (response() < 0) 68257429Smarkm exit(1); 683255767Sdes free(bp); 68457429Smarkm } 68557429Smarkm source(1, argv + i); 68657429Smarkm } 68757429Smarkm } 688255767Sdes free(arg); 68957429Smarkm} 69057429Smarkm 69157429Smarkmvoid 692124211Sdestolocal(int argc, char **argv) 69357429Smarkm{ 69457429Smarkm char *bp, *host, *src, *suser; 695157019Sdes arglist alist; 696162856Sdes int i; 69757429Smarkm 698157019Sdes memset(&alist, '\0', sizeof(alist)); 699157019Sdes alist.list = NULL; 700157019Sdes 70157429Smarkm for (i = 0; i < argc - 1; i++) { 70257429Smarkm if (!(src = colon(argv[i]))) { /* Local to local. */ 703157019Sdes freeargs(&alist); 704157019Sdes addargs(&alist, "%s", _PATH_CP); 705157019Sdes if (iamrecursive) 706157019Sdes addargs(&alist, "-r"); 707157019Sdes if (pflag) 708157019Sdes addargs(&alist, "-p"); 709204917Sdes addargs(&alist, "--"); 710157019Sdes addargs(&alist, "%s", argv[i]); 711157019Sdes addargs(&alist, "%s", argv[argc-1]); 712157019Sdes if (do_local_cmd(&alist)) 71357429Smarkm ++errs; 71457429Smarkm continue; 71557429Smarkm } 71657429Smarkm *src++ = 0; 71757429Smarkm if (*src == 0) 71857429Smarkm src = "."; 719113911Sdes if ((host = strrchr(argv[i], '@')) == NULL) { 72057429Smarkm host = argv[i]; 72157429Smarkm suser = NULL; 72257429Smarkm } else { 72357429Smarkm *host++ = 0; 72457429Smarkm suser = argv[i]; 72557429Smarkm if (*suser == '\0') 72657429Smarkm suser = pwd->pw_name; 72757429Smarkm } 72857429Smarkm host = cleanhostname(host); 729240075Sdes xasprintf(&bp, "%s -f %s%s", 730240075Sdes cmd, *src == '-' ? "-- " : "", src); 731162856Sdes if (do_cmd(host, suser, bp, &remin, &remout) < 0) { 732255767Sdes free(bp); 73357429Smarkm ++errs; 73457429Smarkm continue; 73557429Smarkm } 736255767Sdes free(bp); 73757429Smarkm sink(1, argv + argc - 1); 73857429Smarkm (void) close(remin); 73957429Smarkm remin = remout = -1; 74057429Smarkm } 74157429Smarkm} 74257429Smarkm 74357429Smarkmvoid 744124211Sdessource(int argc, char **argv) 74557429Smarkm{ 74657429Smarkm struct stat stb; 74757429Smarkm static BUF buffer; 74857429Smarkm BUF *bp; 749181111Sdes off_t i, statbytes; 750181111Sdes size_t amt; 751149753Sdes int fd = -1, haderr, indx; 752181111Sdes char *last, *name, buf[2048], encname[MAXPATHLEN]; 75376259Sgreen int len; 75457429Smarkm 75557429Smarkm for (indx = 0; indx < argc; ++indx) { 75657429Smarkm name = argv[indx]; 75757429Smarkm statbytes = 0; 75876259Sgreen len = strlen(name); 75976259Sgreen while (len > 1 && name[len-1] == '/') 76076259Sgreen name[--len] = '\0'; 761181111Sdes if ((fd = open(name, O_RDONLY|O_NONBLOCK, 0)) < 0) 762181111Sdes goto syserr; 76392555Sdes if (strchr(name, '\n') != NULL) { 764181111Sdes strnvis(encname, name, sizeof(encname), VIS_NL); 765181111Sdes name = encname; 76692555Sdes } 76757429Smarkm if (fstat(fd, &stb) < 0) { 76857429Smarkmsyserr: run_err("%s: %s", name, strerror(errno)); 76957429Smarkm goto next; 77057429Smarkm } 771181111Sdes if (stb.st_size < 0) { 772181111Sdes run_err("%s: %s", name, "Negative file size"); 773181111Sdes goto next; 774181111Sdes } 775181111Sdes unset_nonblock(fd); 77657429Smarkm switch (stb.st_mode & S_IFMT) { 77757429Smarkm case S_IFREG: 77857429Smarkm break; 77957429Smarkm case S_IFDIR: 78057429Smarkm if (iamrecursive) { 78157429Smarkm rsource(name, &stb); 78257429Smarkm goto next; 78357429Smarkm } 78457429Smarkm /* FALLTHROUGH */ 78557429Smarkm default: 78657429Smarkm run_err("%s: not a regular file", name); 78757429Smarkm goto next; 78857429Smarkm } 78957429Smarkm if ((last = strrchr(name, '/')) == NULL) 79057429Smarkm last = name; 79157429Smarkm else 79257429Smarkm ++last; 79357429Smarkm curfile = last; 79457429Smarkm if (pflag) { 795255767Sdes if (do_times(remout, verbose_mode, &stb) < 0) 79657429Smarkm goto next; 79757429Smarkm } 79857429Smarkm#define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) 79976259Sgreen snprintf(buf, sizeof buf, "C%04o %lld %s\n", 80076259Sgreen (u_int) (stb.st_mode & FILEMODEMASK), 801157019Sdes (long long)stb.st_size, last); 80257429Smarkm if (verbose_mode) { 80357429Smarkm fprintf(stderr, "Sending file modes: %s", buf); 80457429Smarkm } 805124211Sdes (void) atomicio(vwrite, remout, buf, strlen(buf)); 80657429Smarkm if (response() < 0) 80757429Smarkm goto next; 808181111Sdes if ((bp = allocbuf(&buffer, fd, COPY_BUFLEN)) == NULL) { 809157019Sdesnext: if (fd != -1) { 810157019Sdes (void) close(fd); 811157019Sdes fd = -1; 812157019Sdes } 81357429Smarkm continue; 81457429Smarkm } 815113911Sdes if (showprogress) 816113911Sdes start_progress_meter(curfile, stb.st_size, &statbytes); 817181111Sdes set_nonblock(remout); 81857429Smarkm for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 81957429Smarkm amt = bp->cnt; 820181111Sdes if (i + (off_t)amt > stb.st_size) 82157429Smarkm amt = stb.st_size - i; 82257429Smarkm if (!haderr) { 823181111Sdes if (atomicio(read, fd, bp->buf, amt) != amt) 824149753Sdes haderr = errno; 82557429Smarkm } 826181111Sdes /* Keep writing after error to retain sync */ 827181111Sdes if (haderr) { 828181111Sdes (void)atomicio(vwrite, remout, bp->buf, amt); 829181111Sdes continue; 83057429Smarkm } 831221420Sdes if (atomicio6(vwrite, remout, bp->buf, amt, scpio, 832181111Sdes &statbytes) != amt) 833181111Sdes haderr = errno; 83457429Smarkm } 835181111Sdes unset_nonblock(remout); 83657429Smarkm if (showprogress) 837113911Sdes stop_progress_meter(); 83857429Smarkm 839157019Sdes if (fd != -1) { 840157019Sdes if (close(fd) < 0 && !haderr) 841157019Sdes haderr = errno; 842157019Sdes fd = -1; 843157019Sdes } 84457429Smarkm if (!haderr) 845124211Sdes (void) atomicio(vwrite, remout, "", 1); 84657429Smarkm else 84757429Smarkm run_err("%s: %s", name, strerror(haderr)); 84857429Smarkm (void) response(); 84957429Smarkm } 85057429Smarkm} 85157429Smarkm 85257429Smarkmvoid 853124211Sdesrsource(char *name, struct stat *statp) 85457429Smarkm{ 85557429Smarkm DIR *dirp; 85657429Smarkm struct dirent *dp; 857255767Sdes char *last, *vect[1], path[MAXPATHLEN]; 85857429Smarkm 85957429Smarkm if (!(dirp = opendir(name))) { 86057429Smarkm run_err("%s: %s", name, strerror(errno)); 86157429Smarkm return; 86257429Smarkm } 86357429Smarkm last = strrchr(name, '/'); 86457429Smarkm if (last == 0) 86557429Smarkm last = name; 86657429Smarkm else 86757429Smarkm last++; 86857429Smarkm if (pflag) { 869255767Sdes if (do_times(remout, verbose_mode, statp) < 0) { 87057429Smarkm closedir(dirp); 87157429Smarkm return; 87257429Smarkm } 87357429Smarkm } 87476259Sgreen (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n", 87576259Sgreen (u_int) (statp->st_mode & FILEMODEMASK), 0, last); 87657429Smarkm if (verbose_mode) 87757429Smarkm fprintf(stderr, "Entering directory: %s", path); 878124211Sdes (void) atomicio(vwrite, remout, path, strlen(path)); 87957429Smarkm if (response() < 0) { 88057429Smarkm closedir(dirp); 88157429Smarkm return; 88257429Smarkm } 88376259Sgreen while ((dp = readdir(dirp)) != NULL) { 88457429Smarkm if (dp->d_ino == 0) 88557429Smarkm continue; 88657429Smarkm if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 88757429Smarkm continue; 88857429Smarkm if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) { 88957429Smarkm run_err("%s/%s: name too long", name, dp->d_name); 89057429Smarkm continue; 89157429Smarkm } 89276259Sgreen (void) snprintf(path, sizeof path, "%s/%s", name, dp->d_name); 89357429Smarkm vect[0] = path; 89457429Smarkm source(1, vect); 89557429Smarkm } 89657429Smarkm (void) closedir(dirp); 897124211Sdes (void) atomicio(vwrite, remout, "E\n", 2); 89857429Smarkm (void) response(); 89957429Smarkm} 90057429Smarkm 90157429Smarkmvoid 902124211Sdessink(int argc, char **argv) 90357429Smarkm{ 90457429Smarkm static BUF buffer; 90557429Smarkm struct stat stb; 90657429Smarkm enum { 90757429Smarkm YES, NO, DISPLAYED 90857429Smarkm } wrerr; 90957429Smarkm BUF *bp; 910149753Sdes off_t i; 911149753Sdes size_t j, count; 912162856Sdes int amt, exists, first, ofd; 913162856Sdes mode_t mode, omode, mask; 914113911Sdes off_t size, statbytes; 915255767Sdes unsigned long long ull; 91665668Skris int setimes, targisdir, wrerrno = 0; 91757429Smarkm char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; 91869587Sgreen struct timeval tv[2]; 91957429Smarkm 92076259Sgreen#define atime tv[0] 92176259Sgreen#define mtime tv[1] 922147005Sdes#define SCREWUP(str) { why = str; goto screwup; } 92357429Smarkm 92457429Smarkm setimes = targisdir = 0; 92557429Smarkm mask = umask(0); 92657429Smarkm if (!pflag) 92757429Smarkm (void) umask(mask); 92857429Smarkm if (argc != 1) { 92957429Smarkm run_err("ambiguous target"); 93057429Smarkm exit(1); 93157429Smarkm } 93257429Smarkm targ = *argv; 93357429Smarkm if (targetshouldbedirectory) 93457429Smarkm verifydir(targ); 93557429Smarkm 936124211Sdes (void) atomicio(vwrite, remout, "", 1); 93757429Smarkm if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 93857429Smarkm targisdir = 1; 93957429Smarkm for (first = 1;; first = 0) { 94057429Smarkm cp = buf; 941149753Sdes if (atomicio(read, remin, cp, 1) != 1) 94257429Smarkm return; 94357429Smarkm if (*cp++ == '\n') 94457429Smarkm SCREWUP("unexpected <newline>"); 94557429Smarkm do { 94660573Skris if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) 94757429Smarkm SCREWUP("lost connection"); 94857429Smarkm *cp++ = ch; 94957429Smarkm } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); 95057429Smarkm *cp = 0; 951137019Sdes if (verbose_mode) 952137019Sdes fprintf(stderr, "Sink: %s", buf); 95357429Smarkm 95457429Smarkm if (buf[0] == '\01' || buf[0] == '\02') { 95557429Smarkm if (iamremote == 0) 956124211Sdes (void) atomicio(vwrite, STDERR_FILENO, 95776259Sgreen buf + 1, strlen(buf + 1)); 95857429Smarkm if (buf[0] == '\02') 95957429Smarkm exit(1); 96057429Smarkm ++errs; 96157429Smarkm continue; 96257429Smarkm } 96357429Smarkm if (buf[0] == 'E') { 964124211Sdes (void) atomicio(vwrite, remout, "", 1); 96557429Smarkm return; 96657429Smarkm } 96757429Smarkm if (ch == '\n') 96857429Smarkm *--cp = 0; 96957429Smarkm 97057429Smarkm cp = buf; 97157429Smarkm if (*cp == 'T') { 97257429Smarkm setimes++; 97357429Smarkm cp++; 974255767Sdes if (!isdigit((unsigned char)*cp)) 975255767Sdes SCREWUP("mtime.sec not present"); 976255767Sdes ull = strtoull(cp, &cp, 10); 97776259Sgreen if (!cp || *cp++ != ' ') 97857429Smarkm SCREWUP("mtime.sec not delimited"); 979255767Sdes if ((time_t)ull < 0 || 980255767Sdes (unsigned long long)(time_t)ull != ull) 981255767Sdes setimes = 0; /* out of range */ 982255767Sdes mtime.tv_sec = ull; 98376259Sgreen mtime.tv_usec = strtol(cp, &cp, 10); 984255767Sdes if (!cp || *cp++ != ' ' || mtime.tv_usec < 0 || 985255767Sdes mtime.tv_usec > 999999) 98657429Smarkm SCREWUP("mtime.usec not delimited"); 987255767Sdes if (!isdigit((unsigned char)*cp)) 988255767Sdes SCREWUP("atime.sec not present"); 989255767Sdes ull = strtoull(cp, &cp, 10); 99076259Sgreen if (!cp || *cp++ != ' ') 99157429Smarkm SCREWUP("atime.sec not delimited"); 992255767Sdes if ((time_t)ull < 0 || 993255767Sdes (unsigned long long)(time_t)ull != ull) 994255767Sdes setimes = 0; /* out of range */ 995255767Sdes atime.tv_sec = ull; 99676259Sgreen atime.tv_usec = strtol(cp, &cp, 10); 997255767Sdes if (!cp || *cp++ != '\0' || atime.tv_usec < 0 || 998255767Sdes atime.tv_usec > 999999) 99957429Smarkm SCREWUP("atime.usec not delimited"); 1000124211Sdes (void) atomicio(vwrite, remout, "", 1); 100157429Smarkm continue; 100257429Smarkm } 100357429Smarkm if (*cp != 'C' && *cp != 'D') { 100457429Smarkm /* 100557429Smarkm * Check for the case "rcp remote:foo\* local:bar". 100657429Smarkm * In this case, the line "No match." can be returned 100757429Smarkm * by the shell before the rcp command on the remote is 100857429Smarkm * executed so the ^Aerror_message convention isn't 100957429Smarkm * followed. 101057429Smarkm */ 101157429Smarkm if (first) { 101257429Smarkm run_err("%s", cp); 101357429Smarkm exit(1); 101457429Smarkm } 101557429Smarkm SCREWUP("expected control record"); 101657429Smarkm } 101757429Smarkm mode = 0; 101857429Smarkm for (++cp; cp < buf + 5; cp++) { 101957429Smarkm if (*cp < '0' || *cp > '7') 102057429Smarkm SCREWUP("bad mode"); 102157429Smarkm mode = (mode << 3) | (*cp - '0'); 102257429Smarkm } 102357429Smarkm if (*cp++ != ' ') 102457429Smarkm SCREWUP("mode not delimited"); 102557429Smarkm 1026262566Sdes for (size = 0; isdigit((unsigned char)*cp);) 102757429Smarkm size = size * 10 + (*cp++ - '0'); 102857429Smarkm if (*cp++ != ' ') 102957429Smarkm SCREWUP("size not delimited"); 1030137019Sdes if ((strchr(cp, '/') != NULL) || (strcmp(cp, "..") == 0)) { 1031137019Sdes run_err("error: unexpected filename: %s", cp); 1032137019Sdes exit(1); 1033137019Sdes } 103457429Smarkm if (targisdir) { 103557429Smarkm static char *namebuf; 1036149753Sdes static size_t cursize; 103757429Smarkm size_t need; 103857429Smarkm 103957429Smarkm need = strlen(targ) + strlen(cp) + 250; 104076259Sgreen if (need > cursize) { 1041255767Sdes free(namebuf); 104257429Smarkm namebuf = xmalloc(need); 104376259Sgreen cursize = need; 104476259Sgreen } 104576259Sgreen (void) snprintf(namebuf, need, "%s%s%s", targ, 104698675Sdes strcmp(targ, "/") ? "/" : "", cp); 104757429Smarkm np = namebuf; 104857429Smarkm } else 104957429Smarkm np = targ; 105057429Smarkm curfile = cp; 105157429Smarkm exists = stat(np, &stb) == 0; 105257429Smarkm if (buf[0] == 'D') { 105357429Smarkm int mod_flag = pflag; 1054137019Sdes if (!iamrecursive) 1055137019Sdes SCREWUP("received directory without -r"); 105657429Smarkm if (exists) { 105757429Smarkm if (!S_ISDIR(stb.st_mode)) { 105857429Smarkm errno = ENOTDIR; 105957429Smarkm goto bad; 106057429Smarkm } 106157429Smarkm if (pflag) 106257429Smarkm (void) chmod(np, mode); 106357429Smarkm } else { 106457429Smarkm /* Handle copying from a read-only 106557429Smarkm directory */ 106657429Smarkm mod_flag = 1; 106757429Smarkm if (mkdir(np, mode | S_IRWXU) < 0) 106857429Smarkm goto bad; 106957429Smarkm } 107076259Sgreen vect[0] = xstrdup(np); 107157429Smarkm sink(1, vect); 107257429Smarkm if (setimes) { 107357429Smarkm setimes = 0; 107476259Sgreen if (utimes(vect[0], tv) < 0) 107557429Smarkm run_err("%s: set times: %s", 107676259Sgreen vect[0], strerror(errno)); 107757429Smarkm } 107857429Smarkm if (mod_flag) 107976259Sgreen (void) chmod(vect[0], mode); 1080255767Sdes free(vect[0]); 108157429Smarkm continue; 108257429Smarkm } 108357429Smarkm omode = mode; 1084255767Sdes mode |= S_IWUSR; 108592555Sdes if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 108657429Smarkmbad: run_err("%s: %s", np, strerror(errno)); 108757429Smarkm continue; 108857429Smarkm } 1089124211Sdes (void) atomicio(vwrite, remout, "", 1); 1090181111Sdes if ((bp = allocbuf(&buffer, ofd, COPY_BUFLEN)) == NULL) { 109157429Smarkm (void) close(ofd); 109257429Smarkm continue; 109357429Smarkm } 109457429Smarkm cp = bp->buf; 109557429Smarkm wrerr = NO; 109657429Smarkm 109757429Smarkm statbytes = 0; 1098113911Sdes if (showprogress) 1099113911Sdes start_progress_meter(curfile, size, &statbytes); 1100181111Sdes set_nonblock(remin); 1101181111Sdes for (count = i = 0; i < size; i += bp->cnt) { 1102181111Sdes amt = bp->cnt; 110357429Smarkm if (i + amt > size) 110457429Smarkm amt = size - i; 110557429Smarkm count += amt; 110657429Smarkm do { 1107221420Sdes j = atomicio6(read, remin, cp, amt, 1108221420Sdes scpio, &statbytes); 1109149753Sdes if (j == 0) { 1110181111Sdes run_err("%s", j != EPIPE ? 1111181111Sdes strerror(errno) : 111276259Sgreen "dropped connection"); 111357429Smarkm exit(1); 111457429Smarkm } 111557429Smarkm amt -= j; 111657429Smarkm cp += j; 111757429Smarkm } while (amt > 0); 1118126277Sdes 111957429Smarkm if (count == bp->cnt) { 112057429Smarkm /* Keep reading so we stay sync'd up. */ 112157429Smarkm if (wrerr == NO) { 1122149753Sdes if (atomicio(vwrite, ofd, bp->buf, 1123149753Sdes count) != count) { 112457429Smarkm wrerr = YES; 1125149753Sdes wrerrno = errno; 112657429Smarkm } 112757429Smarkm } 112857429Smarkm count = 0; 112957429Smarkm cp = bp->buf; 113057429Smarkm } 113157429Smarkm } 1132181111Sdes unset_nonblock(remin); 113357429Smarkm if (showprogress) 1134113911Sdes stop_progress_meter(); 113557429Smarkm if (count != 0 && wrerr == NO && 1136149753Sdes atomicio(vwrite, ofd, bp->buf, count) != count) { 113757429Smarkm wrerr = YES; 1138149753Sdes wrerrno = errno; 113957429Smarkm } 1140181111Sdes if (wrerr == NO && (!exists || S_ISREG(stb.st_mode)) && 1141181111Sdes ftruncate(ofd, size) != 0) { 114257429Smarkm run_err("%s: truncate: %s", np, strerror(errno)); 114357429Smarkm wrerr = DISPLAYED; 114457429Smarkm } 114557429Smarkm if (pflag) { 114657429Smarkm if (exists || omode != mode) 114798937Sdes#ifdef HAVE_FCHMOD 1148137019Sdes if (fchmod(ofd, omode)) { 114998937Sdes#else /* HAVE_FCHMOD */ 1150137019Sdes if (chmod(np, omode)) { 115198937Sdes#endif /* HAVE_FCHMOD */ 115257429Smarkm run_err("%s: set mode: %s", 115376259Sgreen np, strerror(errno)); 1154137019Sdes wrerr = DISPLAYED; 1155137019Sdes } 115657429Smarkm } else { 115757429Smarkm if (!exists && omode != mode) 115898937Sdes#ifdef HAVE_FCHMOD 1159137019Sdes if (fchmod(ofd, omode & ~mask)) { 116098937Sdes#else /* HAVE_FCHMOD */ 1161137019Sdes if (chmod(np, omode & ~mask)) { 116298937Sdes#endif /* HAVE_FCHMOD */ 116357429Smarkm run_err("%s: set mode: %s", 116476259Sgreen np, strerror(errno)); 1165137019Sdes wrerr = DISPLAYED; 1166137019Sdes } 116757429Smarkm } 116865668Skris if (close(ofd) == -1) { 116965668Skris wrerr = YES; 117065668Skris wrerrno = errno; 117165668Skris } 117257429Smarkm (void) response(); 117357429Smarkm if (setimes && wrerr == NO) { 117457429Smarkm setimes = 0; 117569587Sgreen if (utimes(np, tv) < 0) { 117657429Smarkm run_err("%s: set times: %s", 117776259Sgreen np, strerror(errno)); 117857429Smarkm wrerr = DISPLAYED; 117957429Smarkm } 118057429Smarkm } 118157429Smarkm switch (wrerr) { 118257429Smarkm case YES: 118357429Smarkm run_err("%s: %s", np, strerror(wrerrno)); 118457429Smarkm break; 118557429Smarkm case NO: 1186124211Sdes (void) atomicio(vwrite, remout, "", 1); 118757429Smarkm break; 118857429Smarkm case DISPLAYED: 118957429Smarkm break; 119057429Smarkm } 119157429Smarkm } 119257429Smarkmscrewup: 119357429Smarkm run_err("protocol error: %s", why); 119457429Smarkm exit(1); 119557429Smarkm} 119657429Smarkm 119757429Smarkmint 119892555Sdesresponse(void) 119957429Smarkm{ 120057429Smarkm char ch, *cp, resp, rbuf[2048]; 120157429Smarkm 120260573Skris if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp)) 120357429Smarkm lostconn(0); 120457429Smarkm 120557429Smarkm cp = rbuf; 120657429Smarkm switch (resp) { 120757429Smarkm case 0: /* ok */ 120857429Smarkm return (0); 120957429Smarkm default: 121057429Smarkm *cp++ = resp; 121157429Smarkm /* FALLTHROUGH */ 121257429Smarkm case 1: /* error, followed by error msg */ 121357429Smarkm case 2: /* fatal error, "" */ 121457429Smarkm do { 121560573Skris if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) 121657429Smarkm lostconn(0); 121757429Smarkm *cp++ = ch; 121857429Smarkm } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); 121957429Smarkm 122057429Smarkm if (!iamremote) 1221124211Sdes (void) atomicio(vwrite, STDERR_FILENO, rbuf, cp - rbuf); 122257429Smarkm ++errs; 122357429Smarkm if (resp == 1) 122457429Smarkm return (-1); 122557429Smarkm exit(1); 122657429Smarkm } 122757429Smarkm /* NOTREACHED */ 122857429Smarkm} 122957429Smarkm 123057429Smarkmvoid 123192555Sdesusage(void) 123257429Smarkm{ 123392555Sdes (void) fprintf(stderr, 1234221420Sdes "usage: scp [-12346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" 1235126277Sdes " [-l limit] [-o ssh_option] [-P port] [-S program]\n" 1236181111Sdes " [[user@]host1:]file1 ... [[user@]host2:]file2\n"); 123757429Smarkm exit(1); 123857429Smarkm} 123957429Smarkm 124057429Smarkmvoid 124157429Smarkmrun_err(const char *fmt,...) 124257429Smarkm{ 124357429Smarkm static FILE *fp; 124457429Smarkm va_list ap; 124557429Smarkm 124657429Smarkm ++errs; 1247162856Sdes if (fp != NULL || (remout != -1 && (fp = fdopen(remout, "w")))) { 1248162856Sdes (void) fprintf(fp, "%c", 0x01); 1249162856Sdes (void) fprintf(fp, "scp: "); 1250162856Sdes va_start(ap, fmt); 1251162856Sdes (void) vfprintf(fp, fmt, ap); 1252162856Sdes va_end(ap); 1253162856Sdes (void) fprintf(fp, "\n"); 1254162856Sdes (void) fflush(fp); 1255162856Sdes } 125657429Smarkm 125757429Smarkm if (!iamremote) { 125892555Sdes va_start(ap, fmt); 125957429Smarkm vfprintf(stderr, fmt, ap); 126092555Sdes va_end(ap); 126157429Smarkm fprintf(stderr, "\n"); 126257429Smarkm } 126357429Smarkm} 126457429Smarkm 126557429Smarkmvoid 1266124211Sdesverifydir(char *cp) 126757429Smarkm{ 126857429Smarkm struct stat stb; 126957429Smarkm 127057429Smarkm if (!stat(cp, &stb)) { 127157429Smarkm if (S_ISDIR(stb.st_mode)) 127257429Smarkm return; 127357429Smarkm errno = ENOTDIR; 127457429Smarkm } 127557429Smarkm run_err("%s: %s", cp, strerror(errno)); 1276149753Sdes killchild(0); 127757429Smarkm} 127857429Smarkm 127957429Smarkmint 1280124211Sdesokname(char *cp0) 128157429Smarkm{ 128257429Smarkm int c; 128357429Smarkm char *cp; 128457429Smarkm 128557429Smarkm cp = cp0; 128657429Smarkm do { 128792555Sdes c = (int)*cp; 128857429Smarkm if (c & 0200) 128957429Smarkm goto bad; 1290262566Sdes if (!isalpha(c) && !isdigit((unsigned char)c)) { 1291113911Sdes switch (c) { 1292113911Sdes case '\'': 1293113911Sdes case '"': 1294113911Sdes case '`': 1295113911Sdes case ' ': 1296113911Sdes case '#': 1297113911Sdes goto bad; 1298113911Sdes default: 1299113911Sdes break; 1300113911Sdes } 1301113911Sdes } 130257429Smarkm } while (*++cp); 130357429Smarkm return (1); 130457429Smarkm 130557429Smarkmbad: fprintf(stderr, "%s: invalid user name\n", cp0); 130657429Smarkm return (0); 130757429Smarkm} 130857429Smarkm 130957429SmarkmBUF * 1310124211Sdesallocbuf(BUF *bp, int fd, int blksize) 131157429Smarkm{ 131257429Smarkm size_t size; 131398937Sdes#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE 131457429Smarkm struct stat stb; 131557429Smarkm 131657429Smarkm if (fstat(fd, &stb) < 0) { 131757429Smarkm run_err("fstat: %s", strerror(errno)); 131857429Smarkm return (0); 131957429Smarkm } 1320113911Sdes size = roundup(stb.st_blksize, blksize); 1321113911Sdes if (size == 0) 132257429Smarkm size = blksize; 132398937Sdes#else /* HAVE_STRUCT_STAT_ST_BLKSIZE */ 132498937Sdes size = blksize; 132598937Sdes#endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ 132657429Smarkm if (bp->cnt >= size) 132757429Smarkm return (bp); 132857429Smarkm if (bp->buf == NULL) 132957429Smarkm bp->buf = xmalloc(size); 133057429Smarkm else 1331162856Sdes bp->buf = xrealloc(bp->buf, 1, size); 133292555Sdes memset(bp->buf, 0, size); 133357429Smarkm bp->cnt = size; 133457429Smarkm return (bp); 133557429Smarkm} 133657429Smarkm 133757429Smarkmvoid 1338124211Sdeslostconn(int signo) 133957429Smarkm{ 134057429Smarkm if (!iamremote) 1341255767Sdes (void)write(STDERR_FILENO, "lost connection\n", 16); 134292555Sdes if (signo) 134392555Sdes _exit(1); 134492555Sdes else 134592555Sdes exit(1); 134657429Smarkm} 1347