1323124Sdes/* $OpenBSD: scp.c,v 1.186 2016/05/25 23:48:45 schwarze 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> 98295367Sdes#include <limits.h> 99323124Sdes#include <locale.h> 100162856Sdes#include <pwd.h> 101162856Sdes#include <signal.h> 102162856Sdes#include <stdarg.h> 103162856Sdes#include <stdio.h> 104162856Sdes#include <stdlib.h> 105162856Sdes#include <string.h> 106162856Sdes#include <time.h> 107162856Sdes#include <unistd.h> 108248619Sdes#if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) 109181111Sdes#include <vis.h> 110181111Sdes#endif 111162856Sdes 11257429Smarkm#include "xmalloc.h" 11376259Sgreen#include "atomicio.h" 11476259Sgreen#include "pathnames.h" 11576259Sgreen#include "log.h" 11692555Sdes#include "misc.h" 117113911Sdes#include "progressmeter.h" 118323124Sdes#include "utf8.h" 11957429Smarkm 12098937Sdesextern char *__progname; 12198937Sdes 122181111Sdes#define COPY_BUFLEN 16384 123181111Sdes 124162856Sdesint do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout); 125221420Sdesint do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout); 126162856Sdes 12792555Sdes/* Struct for addargs */ 12892555Sdesarglist args; 129221420Sdesarglist remote_remote_args; 13069587Sgreen 131113911Sdes/* Bandwidth limit */ 132221420Sdeslong long limit_kbps = 0; 133221420Sdesstruct bwlimit bwlimit; 13457429Smarkm 13557429Smarkm/* Name of current file being transferred. */ 13657429Smarkmchar *curfile; 13757429Smarkm 13857429Smarkm/* This is set to non-zero to enable verbose mode. */ 13957429Smarkmint verbose_mode = 0; 14057429Smarkm 14157429Smarkm/* This is set to zero if the progressmeter is not desired. */ 14257429Smarkmint showprogress = 1; 14357429Smarkm 144221420Sdes/* 145221420Sdes * This is set to non-zero if remote-remote copy should be piped 146221420Sdes * through this process. 147221420Sdes */ 148221420Sdesint throughlocal = 0; 149221420Sdes 15065668Skris/* This is the program to execute for the secured connection. ("ssh" or -S) */ 15176259Sgreenchar *ssh_program = _PATH_SSH_PROGRAM; 15265668Skris 153113911Sdes/* This is used to store the pid of ssh_program */ 154124211Sdespid_t do_cmd_pid = -1; 155113911Sdes 156124211Sdesstatic void 157124211Sdeskillchild(int signo) 158124211Sdes{ 159147005Sdes if (do_cmd_pid > 1) { 160149753Sdes kill(do_cmd_pid, signo ? signo : SIGTERM); 161147005Sdes waitpid(do_cmd_pid, NULL, 0); 162147005Sdes } 163124211Sdes 164149753Sdes if (signo) 165149753Sdes _exit(1); 166149753Sdes exit(1); 167124211Sdes} 168124211Sdes 169215116Sdesstatic void 170215116Sdessuspchild(int signo) 171215116Sdes{ 172215116Sdes int status; 173215116Sdes 174215116Sdes if (do_cmd_pid > 1) { 175215116Sdes kill(do_cmd_pid, signo); 176215116Sdes while (waitpid(do_cmd_pid, &status, WUNTRACED) == -1 && 177215116Sdes errno == EINTR) 178215116Sdes ; 179215116Sdes kill(getpid(), SIGSTOP); 180215116Sdes } 181215116Sdes} 182215116Sdes 183157019Sdesstatic int 184157019Sdesdo_local_cmd(arglist *a) 185157019Sdes{ 186157019Sdes u_int i; 187157019Sdes int status; 188157019Sdes pid_t pid; 189157019Sdes 190157019Sdes if (a->num == 0) 191157019Sdes fatal("do_local_cmd: no arguments"); 192157019Sdes 193157019Sdes if (verbose_mode) { 194157019Sdes fprintf(stderr, "Executing:"); 195157019Sdes for (i = 0; i < a->num; i++) 196323124Sdes fmprintf(stderr, " %s", a->list[i]); 197157019Sdes fprintf(stderr, "\n"); 198157019Sdes } 199157019Sdes if ((pid = fork()) == -1) 200157019Sdes fatal("do_local_cmd: fork: %s", strerror(errno)); 201157019Sdes 202157019Sdes if (pid == 0) { 203157019Sdes execvp(a->list[0], a->list); 204157019Sdes perror(a->list[0]); 205157019Sdes exit(1); 206157019Sdes } 207157019Sdes 208157019Sdes do_cmd_pid = pid; 209157019Sdes signal(SIGTERM, killchild); 210157019Sdes signal(SIGINT, killchild); 211157019Sdes signal(SIGHUP, killchild); 212157019Sdes 213157019Sdes while (waitpid(pid, &status, 0) == -1) 214157019Sdes if (errno != EINTR) 215157019Sdes fatal("do_local_cmd: waitpid: %s", strerror(errno)); 216157019Sdes 217157019Sdes do_cmd_pid = -1; 218157019Sdes 219157019Sdes if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 220157019Sdes return (-1); 221157019Sdes 222157019Sdes return (0); 223157019Sdes} 224157019Sdes 22557429Smarkm/* 22657429Smarkm * This function executes the given command as the specified user on the 22757429Smarkm * given host. This returns < 0 if execution fails, and >= 0 otherwise. This 22857429Smarkm * assigns the input and output file descriptors on success. 22957429Smarkm */ 23057429Smarkm 23160573Skrisint 232162856Sdesdo_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) 23357429Smarkm{ 23457429Smarkm int pin[2], pout[2], reserved[2]; 23557429Smarkm 23657429Smarkm if (verbose_mode) 237323124Sdes fmprintf(stderr, 23892555Sdes "Executing: program %s host %s, user %s, command %s\n", 23992555Sdes ssh_program, host, 24092555Sdes remuser ? remuser : "(unspecified)", cmd); 24157429Smarkm 24257429Smarkm /* 24357429Smarkm * Reserve two descriptors so that the real pipes won't get 24457429Smarkm * descriptors 0 and 1 because that will screw up dup2 below. 24557429Smarkm */ 246162856Sdes if (pipe(reserved) < 0) 247162856Sdes fatal("pipe: %s", strerror(errno)); 24857429Smarkm 24957429Smarkm /* Create a socket pair for communicating with ssh. */ 25057429Smarkm if (pipe(pin) < 0) 25157429Smarkm fatal("pipe: %s", strerror(errno)); 25257429Smarkm if (pipe(pout) < 0) 25357429Smarkm fatal("pipe: %s", strerror(errno)); 25457429Smarkm 25557429Smarkm /* Free the reserved descriptors. */ 25657429Smarkm close(reserved[0]); 25757429Smarkm close(reserved[1]); 25857429Smarkm 259215116Sdes signal(SIGTSTP, suspchild); 260215116Sdes signal(SIGTTIN, suspchild); 261215116Sdes signal(SIGTTOU, suspchild); 262215116Sdes 263124211Sdes /* Fork a child to execute the command on the remote host using ssh. */ 264113911Sdes do_cmd_pid = fork(); 265113911Sdes if (do_cmd_pid == 0) { 26657429Smarkm /* Child. */ 26757429Smarkm close(pin[1]); 26857429Smarkm close(pout[0]); 26957429Smarkm dup2(pin[0], 0); 27057429Smarkm dup2(pout[1], 1); 27157429Smarkm close(pin[0]); 27257429Smarkm close(pout[1]); 27357429Smarkm 274157019Sdes replacearg(&args, 0, "%s", ssh_program); 275204917Sdes if (remuser != NULL) { 276204917Sdes addargs(&args, "-l"); 277204917Sdes addargs(&args, "%s", remuser); 278204917Sdes } 279204917Sdes addargs(&args, "--"); 28092555Sdes addargs(&args, "%s", host); 28192555Sdes addargs(&args, "%s", cmd); 28257429Smarkm 28369587Sgreen execvp(ssh_program, args.list); 28465668Skris perror(ssh_program); 28557429Smarkm exit(1); 286113911Sdes } else if (do_cmd_pid == -1) { 287113911Sdes fatal("fork: %s", strerror(errno)); 28857429Smarkm } 28957429Smarkm /* Parent. Close the other side, and return the local side. */ 29057429Smarkm close(pin[0]); 29157429Smarkm *fdout = pin[1]; 29257429Smarkm close(pout[1]); 29357429Smarkm *fdin = pout[0]; 294124211Sdes signal(SIGTERM, killchild); 295124211Sdes signal(SIGINT, killchild); 296124211Sdes signal(SIGHUP, killchild); 29757429Smarkm return 0; 29857429Smarkm} 29957429Smarkm 300221420Sdes/* 301221420Sdes * This functions executes a command simlar to do_cmd(), but expects the 302221420Sdes * input and output descriptors to be setup by a previous call to do_cmd(). 303221420Sdes * This way the input and output of two commands can be connected. 304221420Sdes */ 305221420Sdesint 306221420Sdesdo_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout) 307221420Sdes{ 308221420Sdes pid_t pid; 309221420Sdes int status; 310221420Sdes 311221420Sdes if (verbose_mode) 312323124Sdes fmprintf(stderr, 313221420Sdes "Executing: 2nd program %s host %s, user %s, command %s\n", 314221420Sdes ssh_program, host, 315221420Sdes remuser ? remuser : "(unspecified)", cmd); 316221420Sdes 317221420Sdes /* Fork a child to execute the command on the remote host using ssh. */ 318221420Sdes pid = fork(); 319221420Sdes if (pid == 0) { 320221420Sdes dup2(fdin, 0); 321221420Sdes dup2(fdout, 1); 322221420Sdes 323221420Sdes replacearg(&args, 0, "%s", ssh_program); 324221420Sdes if (remuser != NULL) { 325221420Sdes addargs(&args, "-l"); 326221420Sdes addargs(&args, "%s", remuser); 327221420Sdes } 328221420Sdes addargs(&args, "--"); 329221420Sdes addargs(&args, "%s", host); 330221420Sdes addargs(&args, "%s", cmd); 331221420Sdes 332221420Sdes execvp(ssh_program, args.list); 333221420Sdes perror(ssh_program); 334221420Sdes exit(1); 335221420Sdes } else if (pid == -1) { 336221420Sdes fatal("fork: %s", strerror(errno)); 337221420Sdes } 338221420Sdes while (waitpid(pid, &status, 0) == -1) 339221420Sdes if (errno != EINTR) 340221420Sdes fatal("do_cmd2: waitpid: %s", strerror(errno)); 341221420Sdes return 0; 342221420Sdes} 343221420Sdes 34457429Smarkmtypedef struct { 345149753Sdes size_t cnt; 34657429Smarkm char *buf; 34757429Smarkm} BUF; 34857429Smarkm 34957429SmarkmBUF *allocbuf(BUF *, int, int); 35057429Smarkmvoid lostconn(int); 35157429Smarkmint okname(char *); 35257429Smarkmvoid run_err(const char *,...); 35357429Smarkmvoid verifydir(char *); 35457429Smarkm 35557429Smarkmstruct passwd *pwd; 35657429Smarkmuid_t userid; 35757429Smarkmint errs, remin, remout; 35857429Smarkmint pflag, iamremote, iamrecursive, targetshouldbedirectory; 35957429Smarkm 36057429Smarkm#define CMDNEEDS 64 36157429Smarkmchar cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 36257429Smarkm 36357429Smarkmint response(void); 36457429Smarkmvoid rsource(char *, struct stat *); 36557429Smarkmvoid sink(int, char *[]); 36657429Smarkmvoid source(int, char *[]); 36757429Smarkmvoid tolocal(int, char *[]); 36857429Smarkmvoid toremote(char *, int, char *[]); 36957429Smarkmvoid usage(void); 37057429Smarkm 37157429Smarkmint 372124211Sdesmain(int argc, char **argv) 37357429Smarkm{ 374162856Sdes int ch, fflag, tflag, status, n; 375221420Sdes char *targ, **newargv; 376221420Sdes const char *errstr; 37757429Smarkm extern char *optarg; 37857429Smarkm extern int optind; 37957429Smarkm 380157019Sdes /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 381157019Sdes sanitise_stdfd(); 382157019Sdes 383323124Sdes setlocale(LC_CTYPE, ""); 384323124Sdes 385162856Sdes /* Copy argv, because we modify it */ 386162856Sdes newargv = xcalloc(MAX(argc + 1, 1), sizeof(*newargv)); 387162856Sdes for (n = 0; n < argc; n++) 388162856Sdes newargv[n] = xstrdup(argv[n]); 389162856Sdes argv = newargv; 390162856Sdes 391124211Sdes __progname = ssh_get_progname(argv[0]); 39298937Sdes 393157019Sdes memset(&args, '\0', sizeof(args)); 394221420Sdes memset(&remote_remote_args, '\0', sizeof(remote_remote_args)); 395221420Sdes args.list = remote_remote_args.list = NULL; 396157019Sdes addargs(&args, "%s", ssh_program); 39792555Sdes addargs(&args, "-x"); 398221420Sdes addargs(&args, "-oForwardAgent=no"); 399221420Sdes addargs(&args, "-oPermitLocalCommand=no"); 400221420Sdes addargs(&args, "-oClearAllForwardings=yes"); 40169587Sgreen 40257429Smarkm fflag = tflag = 0; 403221420Sdes while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1) 40457429Smarkm switch (ch) { 40557429Smarkm /* User-visible flags. */ 406113911Sdes case '1': 407113911Sdes case '2': 40857429Smarkm case '4': 40957429Smarkm case '6': 41069587Sgreen case 'C': 41192555Sdes addargs(&args, "-%c", ch); 412221420Sdes addargs(&remote_remote_args, "-%c", ch); 41357429Smarkm break; 414221420Sdes case '3': 415221420Sdes throughlocal = 1; 416221420Sdes break; 41769587Sgreen case 'o': 41869587Sgreen case 'c': 41969587Sgreen case 'i': 42092555Sdes case 'F': 421221420Sdes addargs(&remote_remote_args, "-%c", ch); 422221420Sdes addargs(&remote_remote_args, "%s", optarg); 423204917Sdes addargs(&args, "-%c", ch); 424204917Sdes addargs(&args, "%s", optarg); 42569587Sgreen break; 42669587Sgreen case 'P': 427221420Sdes addargs(&remote_remote_args, "-p"); 428221420Sdes addargs(&remote_remote_args, "%s", optarg); 429204917Sdes addargs(&args, "-p"); 430204917Sdes addargs(&args, "%s", optarg); 43169587Sgreen break; 43269587Sgreen case 'B': 433221420Sdes addargs(&remote_remote_args, "-oBatchmode=yes"); 434221420Sdes addargs(&args, "-oBatchmode=yes"); 43569587Sgreen break; 436113911Sdes case 'l': 437221420Sdes limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024, 438221420Sdes &errstr); 439221420Sdes if (errstr != NULL) 440113911Sdes usage(); 441221420Sdes limit_kbps *= 1024; /* kbps */ 442221420Sdes bandwidth_limit_init(&bwlimit, limit_kbps, COPY_BUFLEN); 443113911Sdes break; 44457429Smarkm case 'p': 44557429Smarkm pflag = 1; 44657429Smarkm break; 44757429Smarkm case 'r': 44857429Smarkm iamrecursive = 1; 44957429Smarkm break; 45065668Skris case 'S': 45169587Sgreen ssh_program = xstrdup(optarg); 45265668Skris break; 45369587Sgreen case 'v': 45492555Sdes addargs(&args, "-v"); 455221420Sdes addargs(&remote_remote_args, "-v"); 45669587Sgreen verbose_mode = 1; 45769587Sgreen break; 45869587Sgreen case 'q': 459126277Sdes addargs(&args, "-q"); 460221420Sdes addargs(&remote_remote_args, "-q"); 46169587Sgreen showprogress = 0; 46269587Sgreen break; 46365668Skris 46457429Smarkm /* Server options. */ 46557429Smarkm case 'd': 46657429Smarkm targetshouldbedirectory = 1; 46757429Smarkm break; 46857429Smarkm case 'f': /* "from" */ 46957429Smarkm iamremote = 1; 47057429Smarkm fflag = 1; 47157429Smarkm break; 47257429Smarkm case 't': /* "to" */ 47357429Smarkm iamremote = 1; 47457429Smarkm tflag = 1; 47598937Sdes#ifdef HAVE_CYGWIN 47698937Sdes setmode(0, O_BINARY); 47798937Sdes#endif 47857429Smarkm break; 47957429Smarkm default: 48057429Smarkm usage(); 48157429Smarkm } 48257429Smarkm argc -= optind; 48357429Smarkm argv += optind; 48457429Smarkm 48557429Smarkm if ((pwd = getpwuid(userid = getuid())) == NULL) 486124211Sdes fatal("unknown user %u", (u_int) userid); 48757429Smarkm 488181111Sdes if (!isatty(STDOUT_FILENO)) 48957429Smarkm showprogress = 0; 49057429Smarkm 491296781Sdes if (pflag) { 492296781Sdes /* Cannot pledge: -p allows setuid/setgid files... */ 493296781Sdes } else { 494296781Sdes if (pledge("stdio rpath wpath cpath fattr tty proc exec", 495296781Sdes NULL) == -1) { 496296781Sdes perror("pledge"); 497296781Sdes exit(1); 498296781Sdes } 499296781Sdes } 500296781Sdes 50157429Smarkm remin = STDIN_FILENO; 50257429Smarkm remout = STDOUT_FILENO; 50357429Smarkm 50476259Sgreen if (fflag) { 50557429Smarkm /* Follow "protocol", send data. */ 50657429Smarkm (void) response(); 50757429Smarkm source(argc, argv); 50857429Smarkm exit(errs != 0); 50957429Smarkm } 51057429Smarkm if (tflag) { 51157429Smarkm /* Receive data. */ 51257429Smarkm sink(argc, argv); 51357429Smarkm exit(errs != 0); 51457429Smarkm } 51557429Smarkm if (argc < 2) 51657429Smarkm usage(); 51757429Smarkm if (argc > 2) 51857429Smarkm targetshouldbedirectory = 1; 51957429Smarkm 52057429Smarkm remin = remout = -1; 521113911Sdes do_cmd_pid = -1; 52257429Smarkm /* Command to be executed on remote system using "ssh". */ 52376259Sgreen (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", 52476259Sgreen verbose_mode ? " -v" : "", 52565668Skris iamrecursive ? " -r" : "", pflag ? " -p" : "", 52665668Skris targetshouldbedirectory ? " -d" : ""); 52757429Smarkm 52857429Smarkm (void) signal(SIGPIPE, lostconn); 52957429Smarkm 53057429Smarkm if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 53157429Smarkm toremote(targ, argc, argv); 53257429Smarkm else { 53357429Smarkm if (targetshouldbedirectory) 53457429Smarkm verifydir(argv[argc - 1]); 535157019Sdes tolocal(argc, argv); /* Dest is local host. */ 53657429Smarkm } 537113911Sdes /* 538113911Sdes * Finally check the exit status of the ssh process, if one was forked 539192595Sdes * and no error has occurred yet 540113911Sdes */ 541113911Sdes if (do_cmd_pid != -1 && errs == 0) { 542113911Sdes if (remin != -1) 543113911Sdes (void) close(remin); 544113911Sdes if (remout != -1) 545113911Sdes (void) close(remout); 546113911Sdes if (waitpid(do_cmd_pid, &status, 0) == -1) 547113911Sdes errs = 1; 548113911Sdes else { 549113911Sdes if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 550113911Sdes errs = 1; 551113911Sdes } 552113911Sdes } 55357429Smarkm exit(errs != 0); 55457429Smarkm} 55557429Smarkm 556221420Sdes/* Callback from atomicio6 to update progress meter and limit bandwidth */ 557221420Sdesstatic int 558221420Sdesscpio(void *_cnt, size_t s) 559181111Sdes{ 560221420Sdes off_t *cnt = (off_t *)_cnt; 561181111Sdes 562221420Sdes *cnt += s; 563221420Sdes if (limit_kbps > 0) 564221420Sdes bandwidth_limit(&bwlimit, s); 565221420Sdes return 0; 566181111Sdes} 567181111Sdes 568255767Sdesstatic int 569255767Sdesdo_times(int fd, int verb, const struct stat *sb) 570255767Sdes{ 571255767Sdes /* strlen(2^64) == 20; strlen(10^6) == 7 */ 572255767Sdes char buf[(20 + 7 + 2) * 2 + 2]; 573255767Sdes 574255767Sdes (void)snprintf(buf, sizeof(buf), "T%llu 0 %llu 0\n", 575255767Sdes (unsigned long long) (sb->st_mtime < 0 ? 0 : sb->st_mtime), 576255767Sdes (unsigned long long) (sb->st_atime < 0 ? 0 : sb->st_atime)); 577255767Sdes if (verb) { 578255767Sdes fprintf(stderr, "File mtime %lld atime %lld\n", 579255767Sdes (long long)sb->st_mtime, (long long)sb->st_atime); 580255767Sdes fprintf(stderr, "Sending file timestamps: %s", buf); 581255767Sdes } 582255767Sdes (void) atomicio(vwrite, fd, buf, strlen(buf)); 583255767Sdes return (response()); 584255767Sdes} 585255767Sdes 58657429Smarkmvoid 587124211Sdestoremote(char *targ, int argc, char **argv) 58857429Smarkm{ 589147005Sdes char *bp, *host, *src, *suser, *thost, *tuser, *arg; 590157019Sdes arglist alist; 591162856Sdes int i; 592221420Sdes u_int j; 59357429Smarkm 594157019Sdes memset(&alist, '\0', sizeof(alist)); 595157019Sdes alist.list = NULL; 596157019Sdes 59757429Smarkm *targ++ = 0; 59857429Smarkm if (*targ == 0) 59957429Smarkm targ = "."; 60057429Smarkm 601147005Sdes arg = xstrdup(argv[argc - 1]); 602147005Sdes if ((thost = strrchr(arg, '@'))) { 60357429Smarkm /* user@host */ 60457429Smarkm *thost++ = 0; 605147005Sdes tuser = arg; 60657429Smarkm if (*tuser == '\0') 60757429Smarkm tuser = NULL; 60857429Smarkm } else { 609147005Sdes thost = arg; 61057429Smarkm tuser = NULL; 61157429Smarkm } 61257429Smarkm 613157019Sdes if (tuser != NULL && !okname(tuser)) { 614255767Sdes free(arg); 615157019Sdes return; 616157019Sdes } 617157019Sdes 61857429Smarkm for (i = 0; i < argc - 1; i++) { 61957429Smarkm src = colon(argv[i]); 620221420Sdes if (src && throughlocal) { /* extended remote to remote */ 621221420Sdes *src++ = 0; 622221420Sdes if (*src == 0) 623221420Sdes src = "."; 624221420Sdes host = strrchr(argv[i], '@'); 625221420Sdes if (host) { 626221420Sdes *host++ = 0; 627221420Sdes host = cleanhostname(host); 628221420Sdes suser = argv[i]; 629221420Sdes if (*suser == '\0') 630221420Sdes suser = pwd->pw_name; 631221420Sdes else if (!okname(suser)) 632221420Sdes continue; 633221420Sdes } else { 634221420Sdes host = cleanhostname(argv[i]); 635221420Sdes suser = NULL; 636221420Sdes } 637240075Sdes xasprintf(&bp, "%s -f %s%s", cmd, 638240075Sdes *src == '-' ? "-- " : "", src); 639221420Sdes if (do_cmd(host, suser, bp, &remin, &remout) < 0) 640221420Sdes exit(1); 641255767Sdes free(bp); 642221420Sdes host = cleanhostname(thost); 643240075Sdes xasprintf(&bp, "%s -t %s%s", cmd, 644240075Sdes *targ == '-' ? "-- " : "", targ); 645221420Sdes if (do_cmd2(host, tuser, bp, remin, remout) < 0) 646221420Sdes exit(1); 647255767Sdes free(bp); 648221420Sdes (void) close(remin); 649221420Sdes (void) close(remout); 650221420Sdes remin = remout = -1; 651221420Sdes } else if (src) { /* standard remote to remote */ 652157019Sdes freeargs(&alist); 653157019Sdes addargs(&alist, "%s", ssh_program); 654157019Sdes addargs(&alist, "-x"); 655221420Sdes addargs(&alist, "-oClearAllForwardings=yes"); 656157019Sdes addargs(&alist, "-n"); 657221420Sdes for (j = 0; j < remote_remote_args.num; j++) { 658221420Sdes addargs(&alist, "%s", 659221420Sdes remote_remote_args.list[j]); 660221420Sdes } 66157429Smarkm *src++ = 0; 66257429Smarkm if (*src == 0) 66357429Smarkm src = "."; 664113911Sdes host = strrchr(argv[i], '@'); 665157019Sdes 66657429Smarkm if (host) { 66757429Smarkm *host++ = 0; 66857429Smarkm host = cleanhostname(host); 66957429Smarkm suser = argv[i]; 67057429Smarkm if (*suser == '\0') 67157429Smarkm suser = pwd->pw_name; 672157019Sdes else if (!okname(suser)) 67357429Smarkm continue; 674157019Sdes addargs(&alist, "-l"); 675157019Sdes addargs(&alist, "%s", suser); 67657429Smarkm } else { 67757429Smarkm host = cleanhostname(argv[i]); 67857429Smarkm } 679204917Sdes addargs(&alist, "--"); 680157019Sdes addargs(&alist, "%s", host); 681157019Sdes addargs(&alist, "%s", cmd); 682157019Sdes addargs(&alist, "%s", src); 683157019Sdes addargs(&alist, "%s%s%s:%s", 684157019Sdes tuser ? tuser : "", tuser ? "@" : "", 685157019Sdes thost, targ); 686157019Sdes if (do_local_cmd(&alist) != 0) 687126277Sdes errs = 1; 68857429Smarkm } else { /* local to remote */ 68957429Smarkm if (remin == -1) { 690240075Sdes xasprintf(&bp, "%s -t %s%s", cmd, 691240075Sdes *targ == '-' ? "-- " : "", targ); 69257429Smarkm host = cleanhostname(thost); 69365668Skris if (do_cmd(host, tuser, bp, &remin, 694162856Sdes &remout) < 0) 69557429Smarkm exit(1); 69657429Smarkm if (response() < 0) 69757429Smarkm exit(1); 698255767Sdes free(bp); 69957429Smarkm } 70057429Smarkm source(1, argv + i); 70157429Smarkm } 70257429Smarkm } 703255767Sdes free(arg); 70457429Smarkm} 70557429Smarkm 70657429Smarkmvoid 707124211Sdestolocal(int argc, char **argv) 70857429Smarkm{ 70957429Smarkm char *bp, *host, *src, *suser; 710157019Sdes arglist alist; 711162856Sdes int i; 71257429Smarkm 713157019Sdes memset(&alist, '\0', sizeof(alist)); 714157019Sdes alist.list = NULL; 715157019Sdes 71657429Smarkm for (i = 0; i < argc - 1; i++) { 71757429Smarkm if (!(src = colon(argv[i]))) { /* Local to local. */ 718157019Sdes freeargs(&alist); 719157019Sdes addargs(&alist, "%s", _PATH_CP); 720157019Sdes if (iamrecursive) 721157019Sdes addargs(&alist, "-r"); 722157019Sdes if (pflag) 723157019Sdes addargs(&alist, "-p"); 724204917Sdes addargs(&alist, "--"); 725157019Sdes addargs(&alist, "%s", argv[i]); 726157019Sdes addargs(&alist, "%s", argv[argc-1]); 727157019Sdes if (do_local_cmd(&alist)) 72857429Smarkm ++errs; 72957429Smarkm continue; 73057429Smarkm } 73157429Smarkm *src++ = 0; 73257429Smarkm if (*src == 0) 73357429Smarkm src = "."; 734113911Sdes if ((host = strrchr(argv[i], '@')) == NULL) { 73557429Smarkm host = argv[i]; 73657429Smarkm suser = NULL; 73757429Smarkm } else { 73857429Smarkm *host++ = 0; 73957429Smarkm suser = argv[i]; 74057429Smarkm if (*suser == '\0') 74157429Smarkm suser = pwd->pw_name; 74257429Smarkm } 74357429Smarkm host = cleanhostname(host); 744240075Sdes xasprintf(&bp, "%s -f %s%s", 745240075Sdes cmd, *src == '-' ? "-- " : "", src); 746162856Sdes if (do_cmd(host, suser, bp, &remin, &remout) < 0) { 747255767Sdes free(bp); 74857429Smarkm ++errs; 74957429Smarkm continue; 75057429Smarkm } 751255767Sdes free(bp); 75257429Smarkm sink(1, argv + argc - 1); 75357429Smarkm (void) close(remin); 75457429Smarkm remin = remout = -1; 75557429Smarkm } 75657429Smarkm} 75757429Smarkm 75857429Smarkmvoid 759124211Sdessource(int argc, char **argv) 76057429Smarkm{ 76157429Smarkm struct stat stb; 76257429Smarkm static BUF buffer; 76357429Smarkm BUF *bp; 764181111Sdes off_t i, statbytes; 765295367Sdes size_t amt, nr; 766149753Sdes int fd = -1, haderr, indx; 767295367Sdes char *last, *name, buf[2048], encname[PATH_MAX]; 76876259Sgreen int len; 76957429Smarkm 77057429Smarkm for (indx = 0; indx < argc; ++indx) { 77157429Smarkm name = argv[indx]; 77257429Smarkm statbytes = 0; 77376259Sgreen len = strlen(name); 77476259Sgreen while (len > 1 && name[len-1] == '/') 77576259Sgreen name[--len] = '\0'; 776181111Sdes if ((fd = open(name, O_RDONLY|O_NONBLOCK, 0)) < 0) 777181111Sdes goto syserr; 77892555Sdes if (strchr(name, '\n') != NULL) { 779181111Sdes strnvis(encname, name, sizeof(encname), VIS_NL); 780181111Sdes name = encname; 78192555Sdes } 78257429Smarkm if (fstat(fd, &stb) < 0) { 78357429Smarkmsyserr: run_err("%s: %s", name, strerror(errno)); 78457429Smarkm goto next; 78557429Smarkm } 786181111Sdes if (stb.st_size < 0) { 787181111Sdes run_err("%s: %s", name, "Negative file size"); 788181111Sdes goto next; 789181111Sdes } 790181111Sdes unset_nonblock(fd); 79157429Smarkm switch (stb.st_mode & S_IFMT) { 79257429Smarkm case S_IFREG: 79357429Smarkm break; 79457429Smarkm case S_IFDIR: 79557429Smarkm if (iamrecursive) { 79657429Smarkm rsource(name, &stb); 79757429Smarkm goto next; 79857429Smarkm } 79957429Smarkm /* FALLTHROUGH */ 80057429Smarkm default: 80157429Smarkm run_err("%s: not a regular file", name); 80257429Smarkm goto next; 80357429Smarkm } 80457429Smarkm if ((last = strrchr(name, '/')) == NULL) 80557429Smarkm last = name; 80657429Smarkm else 80757429Smarkm ++last; 80857429Smarkm curfile = last; 80957429Smarkm if (pflag) { 810255767Sdes if (do_times(remout, verbose_mode, &stb) < 0) 81157429Smarkm goto next; 81257429Smarkm } 81357429Smarkm#define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) 81476259Sgreen snprintf(buf, sizeof buf, "C%04o %lld %s\n", 81576259Sgreen (u_int) (stb.st_mode & FILEMODEMASK), 816157019Sdes (long long)stb.st_size, last); 817323124Sdes if (verbose_mode) 818323124Sdes fmprintf(stderr, "Sending file modes: %s", buf); 819124211Sdes (void) atomicio(vwrite, remout, buf, strlen(buf)); 82057429Smarkm if (response() < 0) 82157429Smarkm goto next; 822181111Sdes if ((bp = allocbuf(&buffer, fd, COPY_BUFLEN)) == NULL) { 823157019Sdesnext: if (fd != -1) { 824157019Sdes (void) close(fd); 825157019Sdes fd = -1; 826157019Sdes } 82757429Smarkm continue; 82857429Smarkm } 829113911Sdes if (showprogress) 830113911Sdes start_progress_meter(curfile, stb.st_size, &statbytes); 831181111Sdes set_nonblock(remout); 83257429Smarkm for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 83357429Smarkm amt = bp->cnt; 834181111Sdes if (i + (off_t)amt > stb.st_size) 83557429Smarkm amt = stb.st_size - i; 83657429Smarkm if (!haderr) { 837295367Sdes if ((nr = atomicio(read, fd, 838295367Sdes bp->buf, amt)) != amt) { 839149753Sdes haderr = errno; 840295367Sdes memset(bp->buf + nr, 0, amt - nr); 841295367Sdes } 84257429Smarkm } 843181111Sdes /* Keep writing after error to retain sync */ 844181111Sdes if (haderr) { 845181111Sdes (void)atomicio(vwrite, remout, bp->buf, amt); 846295367Sdes memset(bp->buf, 0, amt); 847181111Sdes continue; 84857429Smarkm } 849221420Sdes if (atomicio6(vwrite, remout, bp->buf, amt, scpio, 850181111Sdes &statbytes) != amt) 851181111Sdes haderr = errno; 85257429Smarkm } 853181111Sdes unset_nonblock(remout); 85457429Smarkm 855157019Sdes if (fd != -1) { 856157019Sdes if (close(fd) < 0 && !haderr) 857157019Sdes haderr = errno; 858157019Sdes fd = -1; 859157019Sdes } 86057429Smarkm if (!haderr) 861124211Sdes (void) atomicio(vwrite, remout, "", 1); 86257429Smarkm else 86357429Smarkm run_err("%s: %s", name, strerror(haderr)); 86457429Smarkm (void) response(); 865323124Sdes if (showprogress) 866323124Sdes stop_progress_meter(); 86757429Smarkm } 86857429Smarkm} 86957429Smarkm 87057429Smarkmvoid 871124211Sdesrsource(char *name, struct stat *statp) 87257429Smarkm{ 87357429Smarkm DIR *dirp; 87457429Smarkm struct dirent *dp; 875295367Sdes char *last, *vect[1], path[PATH_MAX]; 87657429Smarkm 87757429Smarkm if (!(dirp = opendir(name))) { 87857429Smarkm run_err("%s: %s", name, strerror(errno)); 87957429Smarkm return; 88057429Smarkm } 88157429Smarkm last = strrchr(name, '/'); 882296781Sdes if (last == NULL) 88357429Smarkm last = name; 88457429Smarkm else 88557429Smarkm last++; 88657429Smarkm if (pflag) { 887255767Sdes if (do_times(remout, verbose_mode, statp) < 0) { 88857429Smarkm closedir(dirp); 88957429Smarkm return; 89057429Smarkm } 89157429Smarkm } 89276259Sgreen (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n", 89376259Sgreen (u_int) (statp->st_mode & FILEMODEMASK), 0, last); 89457429Smarkm if (verbose_mode) 895323124Sdes fmprintf(stderr, "Entering directory: %s", path); 896124211Sdes (void) atomicio(vwrite, remout, path, strlen(path)); 89757429Smarkm if (response() < 0) { 89857429Smarkm closedir(dirp); 89957429Smarkm return; 90057429Smarkm } 90176259Sgreen while ((dp = readdir(dirp)) != NULL) { 90257429Smarkm if (dp->d_ino == 0) 90357429Smarkm continue; 90457429Smarkm if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 90557429Smarkm continue; 90657429Smarkm if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) { 90757429Smarkm run_err("%s/%s: name too long", name, dp->d_name); 90857429Smarkm continue; 90957429Smarkm } 91076259Sgreen (void) snprintf(path, sizeof path, "%s/%s", name, dp->d_name); 91157429Smarkm vect[0] = path; 91257429Smarkm source(1, vect); 91357429Smarkm } 91457429Smarkm (void) closedir(dirp); 915124211Sdes (void) atomicio(vwrite, remout, "E\n", 2); 91657429Smarkm (void) response(); 91757429Smarkm} 91857429Smarkm 91957429Smarkmvoid 920124211Sdessink(int argc, char **argv) 92157429Smarkm{ 92257429Smarkm static BUF buffer; 92357429Smarkm struct stat stb; 92457429Smarkm enum { 92557429Smarkm YES, NO, DISPLAYED 92657429Smarkm } wrerr; 92757429Smarkm BUF *bp; 928149753Sdes off_t i; 929149753Sdes size_t j, count; 930162856Sdes int amt, exists, first, ofd; 931162856Sdes mode_t mode, omode, mask; 932113911Sdes off_t size, statbytes; 933255767Sdes unsigned long long ull; 93465668Skris int setimes, targisdir, wrerrno = 0; 935323124Sdes char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048]; 93669587Sgreen struct timeval tv[2]; 93757429Smarkm 93876259Sgreen#define atime tv[0] 93976259Sgreen#define mtime tv[1] 940147005Sdes#define SCREWUP(str) { why = str; goto screwup; } 94157429Smarkm 94257429Smarkm setimes = targisdir = 0; 94357429Smarkm mask = umask(0); 94457429Smarkm if (!pflag) 94557429Smarkm (void) umask(mask); 94657429Smarkm if (argc != 1) { 94757429Smarkm run_err("ambiguous target"); 94857429Smarkm exit(1); 94957429Smarkm } 95057429Smarkm targ = *argv; 95157429Smarkm if (targetshouldbedirectory) 95257429Smarkm verifydir(targ); 95357429Smarkm 954124211Sdes (void) atomicio(vwrite, remout, "", 1); 95557429Smarkm if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 95657429Smarkm targisdir = 1; 95757429Smarkm for (first = 1;; first = 0) { 95857429Smarkm cp = buf; 959149753Sdes if (atomicio(read, remin, cp, 1) != 1) 96057429Smarkm return; 96157429Smarkm if (*cp++ == '\n') 96257429Smarkm SCREWUP("unexpected <newline>"); 96357429Smarkm do { 96460573Skris if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) 96557429Smarkm SCREWUP("lost connection"); 96657429Smarkm *cp++ = ch; 96757429Smarkm } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); 96857429Smarkm *cp = 0; 969137019Sdes if (verbose_mode) 970323124Sdes fmprintf(stderr, "Sink: %s", buf); 97157429Smarkm 97257429Smarkm if (buf[0] == '\01' || buf[0] == '\02') { 973323124Sdes if (iamremote == 0) { 974323124Sdes (void) snmprintf(visbuf, sizeof(visbuf), 975323124Sdes NULL, "%s", buf + 1); 976124211Sdes (void) atomicio(vwrite, STDERR_FILENO, 977323124Sdes visbuf, strlen(visbuf)); 978323124Sdes } 97957429Smarkm if (buf[0] == '\02') 98057429Smarkm exit(1); 98157429Smarkm ++errs; 98257429Smarkm continue; 98357429Smarkm } 98457429Smarkm if (buf[0] == 'E') { 985124211Sdes (void) atomicio(vwrite, remout, "", 1); 98657429Smarkm return; 98757429Smarkm } 98857429Smarkm if (ch == '\n') 98957429Smarkm *--cp = 0; 99057429Smarkm 99157429Smarkm cp = buf; 99257429Smarkm if (*cp == 'T') { 99357429Smarkm setimes++; 99457429Smarkm cp++; 995255767Sdes if (!isdigit((unsigned char)*cp)) 996255767Sdes SCREWUP("mtime.sec not present"); 997255767Sdes ull = strtoull(cp, &cp, 10); 99876259Sgreen if (!cp || *cp++ != ' ') 99957429Smarkm SCREWUP("mtime.sec not delimited"); 1000255767Sdes if ((time_t)ull < 0 || 1001255767Sdes (unsigned long long)(time_t)ull != ull) 1002255767Sdes setimes = 0; /* out of range */ 1003255767Sdes mtime.tv_sec = ull; 100476259Sgreen mtime.tv_usec = strtol(cp, &cp, 10); 1005255767Sdes if (!cp || *cp++ != ' ' || mtime.tv_usec < 0 || 1006255767Sdes mtime.tv_usec > 999999) 100757429Smarkm SCREWUP("mtime.usec not delimited"); 1008255767Sdes if (!isdigit((unsigned char)*cp)) 1009255767Sdes SCREWUP("atime.sec not present"); 1010255767Sdes ull = strtoull(cp, &cp, 10); 101176259Sgreen if (!cp || *cp++ != ' ') 101257429Smarkm SCREWUP("atime.sec not delimited"); 1013255767Sdes if ((time_t)ull < 0 || 1014255767Sdes (unsigned long long)(time_t)ull != ull) 1015255767Sdes setimes = 0; /* out of range */ 1016255767Sdes atime.tv_sec = ull; 101776259Sgreen atime.tv_usec = strtol(cp, &cp, 10); 1018255767Sdes if (!cp || *cp++ != '\0' || atime.tv_usec < 0 || 1019255767Sdes atime.tv_usec > 999999) 102057429Smarkm SCREWUP("atime.usec not delimited"); 1021124211Sdes (void) atomicio(vwrite, remout, "", 1); 102257429Smarkm continue; 102357429Smarkm } 102457429Smarkm if (*cp != 'C' && *cp != 'D') { 102557429Smarkm /* 102657429Smarkm * Check for the case "rcp remote:foo\* local:bar". 102757429Smarkm * In this case, the line "No match." can be returned 102857429Smarkm * by the shell before the rcp command on the remote is 102957429Smarkm * executed so the ^Aerror_message convention isn't 103057429Smarkm * followed. 103157429Smarkm */ 103257429Smarkm if (first) { 103357429Smarkm run_err("%s", cp); 103457429Smarkm exit(1); 103557429Smarkm } 103657429Smarkm SCREWUP("expected control record"); 103757429Smarkm } 103857429Smarkm mode = 0; 103957429Smarkm for (++cp; cp < buf + 5; cp++) { 104057429Smarkm if (*cp < '0' || *cp > '7') 104157429Smarkm SCREWUP("bad mode"); 104257429Smarkm mode = (mode << 3) | (*cp - '0'); 104357429Smarkm } 104457429Smarkm if (*cp++ != ' ') 104557429Smarkm SCREWUP("mode not delimited"); 104657429Smarkm 1047262566Sdes for (size = 0; isdigit((unsigned char)*cp);) 104857429Smarkm size = size * 10 + (*cp++ - '0'); 104957429Smarkm if (*cp++ != ' ') 105057429Smarkm SCREWUP("size not delimited"); 1051343098Semaste if (*cp == '\0' || strchr(cp, '/') != NULL || 1052343098Semaste strcmp(cp, ".") == 0 || strcmp(cp, "..") == 0) { 1053137019Sdes run_err("error: unexpected filename: %s", cp); 1054137019Sdes exit(1); 1055137019Sdes } 105657429Smarkm if (targisdir) { 105757429Smarkm static char *namebuf; 1058149753Sdes static size_t cursize; 105957429Smarkm size_t need; 106057429Smarkm 106157429Smarkm need = strlen(targ) + strlen(cp) + 250; 106276259Sgreen if (need > cursize) { 1063255767Sdes free(namebuf); 106457429Smarkm namebuf = xmalloc(need); 106576259Sgreen cursize = need; 106676259Sgreen } 106776259Sgreen (void) snprintf(namebuf, need, "%s%s%s", targ, 106898675Sdes strcmp(targ, "/") ? "/" : "", cp); 106957429Smarkm np = namebuf; 107057429Smarkm } else 107157429Smarkm np = targ; 107257429Smarkm curfile = cp; 107357429Smarkm exists = stat(np, &stb) == 0; 107457429Smarkm if (buf[0] == 'D') { 107557429Smarkm int mod_flag = pflag; 1076137019Sdes if (!iamrecursive) 1077137019Sdes SCREWUP("received directory without -r"); 107857429Smarkm if (exists) { 107957429Smarkm if (!S_ISDIR(stb.st_mode)) { 108057429Smarkm errno = ENOTDIR; 108157429Smarkm goto bad; 108257429Smarkm } 108357429Smarkm if (pflag) 108457429Smarkm (void) chmod(np, mode); 108557429Smarkm } else { 108657429Smarkm /* Handle copying from a read-only 108757429Smarkm directory */ 108857429Smarkm mod_flag = 1; 108957429Smarkm if (mkdir(np, mode | S_IRWXU) < 0) 109057429Smarkm goto bad; 109157429Smarkm } 109276259Sgreen vect[0] = xstrdup(np); 109357429Smarkm sink(1, vect); 109457429Smarkm if (setimes) { 109557429Smarkm setimes = 0; 109676259Sgreen if (utimes(vect[0], tv) < 0) 109757429Smarkm run_err("%s: set times: %s", 109876259Sgreen vect[0], strerror(errno)); 109957429Smarkm } 110057429Smarkm if (mod_flag) 110176259Sgreen (void) chmod(vect[0], mode); 1102255767Sdes free(vect[0]); 110357429Smarkm continue; 110457429Smarkm } 110557429Smarkm omode = mode; 1106255767Sdes mode |= S_IWUSR; 110792555Sdes if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 110857429Smarkmbad: run_err("%s: %s", np, strerror(errno)); 110957429Smarkm continue; 111057429Smarkm } 1111124211Sdes (void) atomicio(vwrite, remout, "", 1); 1112181111Sdes if ((bp = allocbuf(&buffer, ofd, COPY_BUFLEN)) == NULL) { 111357429Smarkm (void) close(ofd); 111457429Smarkm continue; 111557429Smarkm } 111657429Smarkm cp = bp->buf; 111757429Smarkm wrerr = NO; 111857429Smarkm 111957429Smarkm statbytes = 0; 1120113911Sdes if (showprogress) 1121113911Sdes start_progress_meter(curfile, size, &statbytes); 1122181111Sdes set_nonblock(remin); 1123181111Sdes for (count = i = 0; i < size; i += bp->cnt) { 1124181111Sdes amt = bp->cnt; 112557429Smarkm if (i + amt > size) 112657429Smarkm amt = size - i; 112757429Smarkm count += amt; 112857429Smarkm do { 1129221420Sdes j = atomicio6(read, remin, cp, amt, 1130221420Sdes scpio, &statbytes); 1131149753Sdes if (j == 0) { 1132181111Sdes run_err("%s", j != EPIPE ? 1133181111Sdes strerror(errno) : 113476259Sgreen "dropped connection"); 113557429Smarkm exit(1); 113657429Smarkm } 113757429Smarkm amt -= j; 113857429Smarkm cp += j; 113957429Smarkm } while (amt > 0); 1140126277Sdes 114157429Smarkm if (count == bp->cnt) { 114257429Smarkm /* Keep reading so we stay sync'd up. */ 114357429Smarkm if (wrerr == NO) { 1144149753Sdes if (atomicio(vwrite, ofd, bp->buf, 1145149753Sdes count) != count) { 114657429Smarkm wrerr = YES; 1147149753Sdes wrerrno = errno; 114857429Smarkm } 114957429Smarkm } 115057429Smarkm count = 0; 115157429Smarkm cp = bp->buf; 115257429Smarkm } 115357429Smarkm } 1154181111Sdes unset_nonblock(remin); 115557429Smarkm if (count != 0 && wrerr == NO && 1156149753Sdes atomicio(vwrite, ofd, bp->buf, count) != count) { 115757429Smarkm wrerr = YES; 1158149753Sdes wrerrno = errno; 115957429Smarkm } 1160181111Sdes if (wrerr == NO && (!exists || S_ISREG(stb.st_mode)) && 1161181111Sdes ftruncate(ofd, size) != 0) { 116257429Smarkm run_err("%s: truncate: %s", np, strerror(errno)); 116357429Smarkm wrerr = DISPLAYED; 116457429Smarkm } 116557429Smarkm if (pflag) { 116657429Smarkm if (exists || omode != mode) 116798937Sdes#ifdef HAVE_FCHMOD 1168137019Sdes if (fchmod(ofd, omode)) { 116998937Sdes#else /* HAVE_FCHMOD */ 1170137019Sdes if (chmod(np, omode)) { 117198937Sdes#endif /* HAVE_FCHMOD */ 117257429Smarkm run_err("%s: set mode: %s", 117376259Sgreen np, strerror(errno)); 1174137019Sdes wrerr = DISPLAYED; 1175137019Sdes } 117657429Smarkm } else { 117757429Smarkm if (!exists && omode != mode) 117898937Sdes#ifdef HAVE_FCHMOD 1179137019Sdes if (fchmod(ofd, omode & ~mask)) { 118098937Sdes#else /* HAVE_FCHMOD */ 1181137019Sdes if (chmod(np, omode & ~mask)) { 118298937Sdes#endif /* HAVE_FCHMOD */ 118357429Smarkm run_err("%s: set mode: %s", 118476259Sgreen np, strerror(errno)); 1185137019Sdes wrerr = DISPLAYED; 1186137019Sdes } 118757429Smarkm } 118865668Skris if (close(ofd) == -1) { 118965668Skris wrerr = YES; 119065668Skris wrerrno = errno; 119165668Skris } 119257429Smarkm (void) response(); 1193323124Sdes if (showprogress) 1194323124Sdes stop_progress_meter(); 119557429Smarkm if (setimes && wrerr == NO) { 119657429Smarkm setimes = 0; 119769587Sgreen if (utimes(np, tv) < 0) { 119857429Smarkm run_err("%s: set times: %s", 119976259Sgreen np, strerror(errno)); 120057429Smarkm wrerr = DISPLAYED; 120157429Smarkm } 120257429Smarkm } 120357429Smarkm switch (wrerr) { 120457429Smarkm case YES: 120557429Smarkm run_err("%s: %s", np, strerror(wrerrno)); 120657429Smarkm break; 120757429Smarkm case NO: 1208124211Sdes (void) atomicio(vwrite, remout, "", 1); 120957429Smarkm break; 121057429Smarkm case DISPLAYED: 121157429Smarkm break; 121257429Smarkm } 121357429Smarkm } 121457429Smarkmscrewup: 121557429Smarkm run_err("protocol error: %s", why); 121657429Smarkm exit(1); 121757429Smarkm} 121857429Smarkm 121957429Smarkmint 122092555Sdesresponse(void) 122157429Smarkm{ 1222323124Sdes char ch, *cp, resp, rbuf[2048], visbuf[2048]; 122357429Smarkm 122460573Skris if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp)) 122557429Smarkm lostconn(0); 122657429Smarkm 122757429Smarkm cp = rbuf; 122857429Smarkm switch (resp) { 122957429Smarkm case 0: /* ok */ 123057429Smarkm return (0); 123157429Smarkm default: 123257429Smarkm *cp++ = resp; 123357429Smarkm /* FALLTHROUGH */ 123457429Smarkm case 1: /* error, followed by error msg */ 123557429Smarkm case 2: /* fatal error, "" */ 123657429Smarkm do { 123760573Skris if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) 123857429Smarkm lostconn(0); 123957429Smarkm *cp++ = ch; 124057429Smarkm } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); 124157429Smarkm 1242323124Sdes if (!iamremote) { 1243323124Sdes cp[-1] = '\0'; 1244323124Sdes (void) snmprintf(visbuf, sizeof(visbuf), 1245323124Sdes NULL, "%s\n", rbuf); 1246323124Sdes (void) atomicio(vwrite, STDERR_FILENO, 1247323124Sdes visbuf, strlen(visbuf)); 1248323124Sdes } 124957429Smarkm ++errs; 125057429Smarkm if (resp == 1) 125157429Smarkm return (-1); 125257429Smarkm exit(1); 125357429Smarkm } 125457429Smarkm /* NOTREACHED */ 125557429Smarkm} 125657429Smarkm 125757429Smarkmvoid 125892555Sdesusage(void) 125957429Smarkm{ 126092555Sdes (void) fprintf(stderr, 1261221420Sdes "usage: scp [-12346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" 1262126277Sdes " [-l limit] [-o ssh_option] [-P port] [-S program]\n" 1263181111Sdes " [[user@]host1:]file1 ... [[user@]host2:]file2\n"); 126457429Smarkm exit(1); 126557429Smarkm} 126657429Smarkm 126757429Smarkmvoid 126857429Smarkmrun_err(const char *fmt,...) 126957429Smarkm{ 127057429Smarkm static FILE *fp; 127157429Smarkm va_list ap; 127257429Smarkm 127357429Smarkm ++errs; 1274162856Sdes if (fp != NULL || (remout != -1 && (fp = fdopen(remout, "w")))) { 1275162856Sdes (void) fprintf(fp, "%c", 0x01); 1276162856Sdes (void) fprintf(fp, "scp: "); 1277162856Sdes va_start(ap, fmt); 1278162856Sdes (void) vfprintf(fp, fmt, ap); 1279162856Sdes va_end(ap); 1280162856Sdes (void) fprintf(fp, "\n"); 1281162856Sdes (void) fflush(fp); 1282162856Sdes } 128357429Smarkm 128457429Smarkm if (!iamremote) { 128592555Sdes va_start(ap, fmt); 1286323124Sdes vfmprintf(stderr, fmt, ap); 128792555Sdes va_end(ap); 128857429Smarkm fprintf(stderr, "\n"); 128957429Smarkm } 129057429Smarkm} 129157429Smarkm 129257429Smarkmvoid 1293124211Sdesverifydir(char *cp) 129457429Smarkm{ 129557429Smarkm struct stat stb; 129657429Smarkm 129757429Smarkm if (!stat(cp, &stb)) { 129857429Smarkm if (S_ISDIR(stb.st_mode)) 129957429Smarkm return; 130057429Smarkm errno = ENOTDIR; 130157429Smarkm } 130257429Smarkm run_err("%s: %s", cp, strerror(errno)); 1303149753Sdes killchild(0); 130457429Smarkm} 130557429Smarkm 130657429Smarkmint 1307124211Sdesokname(char *cp0) 130857429Smarkm{ 130957429Smarkm int c; 131057429Smarkm char *cp; 131157429Smarkm 131257429Smarkm cp = cp0; 131357429Smarkm do { 131492555Sdes c = (int)*cp; 131557429Smarkm if (c & 0200) 131657429Smarkm goto bad; 1317262566Sdes if (!isalpha(c) && !isdigit((unsigned char)c)) { 1318113911Sdes switch (c) { 1319113911Sdes case '\'': 1320113911Sdes case '"': 1321113911Sdes case '`': 1322113911Sdes case ' ': 1323113911Sdes case '#': 1324113911Sdes goto bad; 1325113911Sdes default: 1326113911Sdes break; 1327113911Sdes } 1328113911Sdes } 132957429Smarkm } while (*++cp); 133057429Smarkm return (1); 133157429Smarkm 1332323124Sdesbad: fmprintf(stderr, "%s: invalid user name\n", cp0); 133357429Smarkm return (0); 133457429Smarkm} 133557429Smarkm 133657429SmarkmBUF * 1337124211Sdesallocbuf(BUF *bp, int fd, int blksize) 133857429Smarkm{ 133957429Smarkm size_t size; 134098937Sdes#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE 134157429Smarkm struct stat stb; 134257429Smarkm 134357429Smarkm if (fstat(fd, &stb) < 0) { 134457429Smarkm run_err("fstat: %s", strerror(errno)); 134557429Smarkm return (0); 134657429Smarkm } 1347113911Sdes size = roundup(stb.st_blksize, blksize); 1348113911Sdes if (size == 0) 134957429Smarkm size = blksize; 135098937Sdes#else /* HAVE_STRUCT_STAT_ST_BLKSIZE */ 135198937Sdes size = blksize; 135298937Sdes#endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ 135357429Smarkm if (bp->cnt >= size) 135457429Smarkm return (bp); 135557429Smarkm if (bp->buf == NULL) 135657429Smarkm bp->buf = xmalloc(size); 135757429Smarkm else 1358295367Sdes bp->buf = xreallocarray(bp->buf, 1, size); 135992555Sdes memset(bp->buf, 0, size); 136057429Smarkm bp->cnt = size; 136157429Smarkm return (bp); 136257429Smarkm} 136357429Smarkm 136457429Smarkmvoid 1365124211Sdeslostconn(int signo) 136657429Smarkm{ 136757429Smarkm if (!iamremote) 1368255767Sdes (void)write(STDERR_FILENO, "lost connection\n", 16); 136992555Sdes if (signo) 137092555Sdes _exit(1); 137192555Sdes else 137292555Sdes exit(1); 137357429Smarkm} 1374