1f374ba41SEd Maste /* $OpenBSD: scp.c,v 1.252 2023/01/10 23:22:15 millert Exp $ */ 2511b41d2SMark Murray /* 3b66f2d16SKris Kennaway * scp - secure remote copy. This is basically patched BSD rcp which 4b66f2d16SKris Kennaway * uses ssh to do the data transfer (instead of using rcmd). 5511b41d2SMark Murray * 6b66f2d16SKris Kennaway * NOTE: This version should NOT be suid root. (This uses ssh to 7b66f2d16SKris Kennaway * do the transfer and ssh has the necessary privileges.) 8511b41d2SMark Murray * 9511b41d2SMark Murray * 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi> 10511b41d2SMark Murray * 11b66f2d16SKris Kennaway * As far as I am concerned, the code I have written for this software 12b66f2d16SKris Kennaway * can be used freely for any purpose. Any derived versions of this 13b66f2d16SKris Kennaway * software must be clearly marked as such, and if the derived work is 14b66f2d16SKris Kennaway * incompatible with the protocol description in the RFC file, it must be 15b66f2d16SKris Kennaway * called by a name other than "ssh" or "Secure Shell". 16b66f2d16SKris Kennaway */ 17b66f2d16SKris Kennaway /* 18b66f2d16SKris Kennaway * Copyright (c) 1999 Theo de Raadt. All rights reserved. 19b66f2d16SKris Kennaway * Copyright (c) 1999 Aaron Campbell. All rights reserved. 20b66f2d16SKris Kennaway * 21b66f2d16SKris Kennaway * Redistribution and use in source and binary forms, with or without 22b66f2d16SKris Kennaway * modification, are permitted provided that the following conditions 23b66f2d16SKris Kennaway * are met: 24b66f2d16SKris Kennaway * 1. Redistributions of source code must retain the above copyright 25b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer. 26b66f2d16SKris Kennaway * 2. Redistributions in binary form must reproduce the above copyright 27b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer in the 28b66f2d16SKris Kennaway * documentation and/or other materials provided with the distribution. 29b66f2d16SKris Kennaway * 30b66f2d16SKris Kennaway * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 31b66f2d16SKris Kennaway * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 32b66f2d16SKris Kennaway * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 33b66f2d16SKris Kennaway * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 34b66f2d16SKris Kennaway * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 35b66f2d16SKris Kennaway * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 36b66f2d16SKris Kennaway * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37b66f2d16SKris Kennaway * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 38b66f2d16SKris Kennaway * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 39b66f2d16SKris Kennaway * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40511b41d2SMark Murray */ 41511b41d2SMark Murray 42511b41d2SMark Murray /* 43b66f2d16SKris Kennaway * Parts from: 44b66f2d16SKris Kennaway * 45511b41d2SMark Murray * Copyright (c) 1983, 1990, 1992, 1993, 1995 46511b41d2SMark Murray * The Regents of the University of California. All rights reserved. 47511b41d2SMark Murray * 48511b41d2SMark Murray * Redistribution and use in source and binary forms, with or without 49511b41d2SMark Murray * modification, are permitted provided that the following conditions 50511b41d2SMark Murray * are met: 51511b41d2SMark Murray * 1. Redistributions of source code must retain the above copyright 52511b41d2SMark Murray * notice, this list of conditions and the following disclaimer. 53511b41d2SMark Murray * 2. Redistributions in binary form must reproduce the above copyright 54511b41d2SMark Murray * notice, this list of conditions and the following disclaimer in the 55511b41d2SMark Murray * documentation and/or other materials provided with the distribution. 56cf2b5f3bSDag-Erling Smørgrav * 3. Neither the name of the University nor the names of its contributors 57511b41d2SMark Murray * may be used to endorse or promote products derived from this software 58511b41d2SMark Murray * without specific prior written permission. 59511b41d2SMark Murray * 60511b41d2SMark Murray * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 61511b41d2SMark Murray * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 62511b41d2SMark Murray * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 63511b41d2SMark Murray * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 64511b41d2SMark Murray * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 65511b41d2SMark Murray * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 66511b41d2SMark Murray * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 67511b41d2SMark Murray * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 68511b41d2SMark Murray * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 69511b41d2SMark Murray * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 70511b41d2SMark Murray * SUCH DAMAGE. 71511b41d2SMark Murray * 72511b41d2SMark Murray */ 73511b41d2SMark Murray 74511b41d2SMark Murray #include "includes.h" 75333ee039SDag-Erling Smørgrav 76333ee039SDag-Erling Smørgrav #include <sys/types.h> 77333ee039SDag-Erling Smørgrav #ifdef HAVE_SYS_STAT_H 78333ee039SDag-Erling Smørgrav # include <sys/stat.h> 79333ee039SDag-Erling Smørgrav #endif 80d4af9e69SDag-Erling Smørgrav #ifdef HAVE_POLL_H 81d4af9e69SDag-Erling Smørgrav #include <poll.h> 82d4af9e69SDag-Erling Smørgrav #else 83d4af9e69SDag-Erling Smørgrav # ifdef HAVE_SYS_POLL_H 84d4af9e69SDag-Erling Smørgrav # include <sys/poll.h> 85d4af9e69SDag-Erling Smørgrav # endif 86d4af9e69SDag-Erling Smørgrav #endif 87333ee039SDag-Erling Smørgrav #ifdef HAVE_SYS_TIME_H 88333ee039SDag-Erling Smørgrav # include <sys/time.h> 89333ee039SDag-Erling Smørgrav #endif 90333ee039SDag-Erling Smørgrav #include <sys/wait.h> 91333ee039SDag-Erling Smørgrav #include <sys/uio.h> 92333ee039SDag-Erling Smørgrav 93333ee039SDag-Erling Smørgrav #include <ctype.h> 94333ee039SDag-Erling Smørgrav #include <dirent.h> 95333ee039SDag-Erling Smørgrav #include <errno.h> 96333ee039SDag-Erling Smørgrav #include <fcntl.h> 9719261079SEd Maste #ifdef HAVE_FNMATCH_H 98afde5170SEd Maste #include <fnmatch.h> 9919261079SEd Maste #endif 10019261079SEd Maste #ifdef USE_SYSTEM_GLOB 10119261079SEd Maste # include <glob.h> 10219261079SEd Maste #else 10319261079SEd Maste # include "openbsd-compat/glob.h" 10419261079SEd Maste #endif 10519261079SEd Maste #ifdef HAVE_LIBGEN_H 10619261079SEd Maste #include <libgen.h> 10719261079SEd Maste #endif 108bc5531deSDag-Erling Smørgrav #include <limits.h> 109f374ba41SEd Maste #ifdef HAVE_UTIL_H 110f374ba41SEd Maste # include <util.h> 111f374ba41SEd Maste #endif 112076ad2f8SDag-Erling Smørgrav #include <locale.h> 113333ee039SDag-Erling Smørgrav #include <pwd.h> 114333ee039SDag-Erling Smørgrav #include <signal.h> 115333ee039SDag-Erling Smørgrav #include <stdarg.h> 1164f52dfbbSDag-Erling Smørgrav #ifdef HAVE_STDINT_H 1174f52dfbbSDag-Erling Smørgrav # include <stdint.h> 1184f52dfbbSDag-Erling Smørgrav #endif 119333ee039SDag-Erling Smørgrav #include <stdio.h> 120333ee039SDag-Erling Smørgrav #include <stdlib.h> 121333ee039SDag-Erling Smørgrav #include <string.h> 122333ee039SDag-Erling Smørgrav #include <time.h> 123333ee039SDag-Erling Smørgrav #include <unistd.h> 1246888a9beSDag-Erling Smørgrav #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) 125d4af9e69SDag-Erling Smørgrav #include <vis.h> 126d4af9e69SDag-Erling Smørgrav #endif 127511b41d2SMark Murray 128511b41d2SMark Murray #include "xmalloc.h" 12947dd1d1bSDag-Erling Smørgrav #include "ssh.h" 1301e8db6e2SBrian Feldman #include "atomicio.h" 1311e8db6e2SBrian Feldman #include "pathnames.h" 1321e8db6e2SBrian Feldman #include "log.h" 133ae1f160dSDag-Erling Smørgrav #include "misc.h" 134e73e9afaSDag-Erling Smørgrav #include "progressmeter.h" 135076ad2f8SDag-Erling Smørgrav #include "utf8.h" 1361323ec57SEd Maste #include "sftp.h" 137511b41d2SMark Murray 13819261079SEd Maste #include "sftp-common.h" 13919261079SEd Maste #include "sftp-client.h" 14019261079SEd Maste 14183d2307dSDag-Erling Smørgrav extern char *__progname; 14283d2307dSDag-Erling Smørgrav 143d4af9e69SDag-Erling Smørgrav #define COPY_BUFLEN 16384 144d4af9e69SDag-Erling Smørgrav 14519261079SEd Maste int do_cmd(char *, char *, char *, int, int, char *, int *, int *, pid_t *); 14619261079SEd Maste int do_cmd2(char *, char *, int, char *, int, int); 147511b41d2SMark Murray 148ae1f160dSDag-Erling Smørgrav /* Struct for addargs */ 149ae1f160dSDag-Erling Smørgrav arglist args; 1504a421b63SDag-Erling Smørgrav arglist remote_remote_args; 1515b9b2fafSBrian Feldman 152e73e9afaSDag-Erling Smørgrav /* Bandwidth limit */ 1534a421b63SDag-Erling Smørgrav long long limit_kbps = 0; 1544a421b63SDag-Erling Smørgrav struct bwlimit bwlimit; 155511b41d2SMark Murray 156511b41d2SMark Murray /* Name of current file being transferred. */ 157511b41d2SMark Murray char *curfile; 158511b41d2SMark Murray 159511b41d2SMark Murray /* This is set to non-zero to enable verbose mode. */ 160511b41d2SMark Murray int verbose_mode = 0; 16119261079SEd Maste LogLevel log_level = SYSLOG_LEVEL_INFO; 162511b41d2SMark Murray 163511b41d2SMark Murray /* This is set to zero if the progressmeter is not desired. */ 164511b41d2SMark Murray int showprogress = 1; 165511b41d2SMark Murray 1664a421b63SDag-Erling Smørgrav /* 1674a421b63SDag-Erling Smørgrav * This is set to non-zero if remote-remote copy should be piped 1684a421b63SDag-Erling Smørgrav * through this process. 1694a421b63SDag-Erling Smørgrav */ 17019261079SEd Maste int throughlocal = 1; 1714a421b63SDag-Erling Smørgrav 17247dd1d1bSDag-Erling Smørgrav /* Non-standard port to use for the ssh connection or -1. */ 17347dd1d1bSDag-Erling Smørgrav int sshport = -1; 17447dd1d1bSDag-Erling Smørgrav 175b66f2d16SKris Kennaway /* This is the program to execute for the secured connection. ("ssh" or -S) */ 1761e8db6e2SBrian Feldman char *ssh_program = _PATH_SSH_PROGRAM; 177b66f2d16SKris Kennaway 178e73e9afaSDag-Erling Smørgrav /* This is used to store the pid of ssh_program */ 179cf2b5f3bSDag-Erling Smørgrav pid_t do_cmd_pid = -1; 18019261079SEd Maste pid_t do_cmd_pid2 = -1; 18119261079SEd Maste 182f374ba41SEd Maste /* SFTP copy parameters */ 183f374ba41SEd Maste size_t sftp_copy_buflen; 184f374ba41SEd Maste size_t sftp_nrequests; 185f374ba41SEd Maste 18619261079SEd Maste /* Needed for sftp */ 18719261079SEd Maste volatile sig_atomic_t interrupted = 0; 18819261079SEd Maste 18919261079SEd Maste int remote_glob(struct sftp_conn *, const char *, int, 19019261079SEd Maste int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ 191cf2b5f3bSDag-Erling Smørgrav 192cf2b5f3bSDag-Erling Smørgrav static void 193cf2b5f3bSDag-Erling Smørgrav killchild(int signo) 194cf2b5f3bSDag-Erling Smørgrav { 195aa49c926SDag-Erling Smørgrav if (do_cmd_pid > 1) { 196d4ecd108SDag-Erling Smørgrav kill(do_cmd_pid, signo ? signo : SIGTERM); 197aa49c926SDag-Erling Smørgrav waitpid(do_cmd_pid, NULL, 0); 198aa49c926SDag-Erling Smørgrav } 19919261079SEd Maste if (do_cmd_pid2 > 1) { 20019261079SEd Maste kill(do_cmd_pid2, signo ? signo : SIGTERM); 20119261079SEd Maste waitpid(do_cmd_pid2, NULL, 0); 20219261079SEd Maste } 203cf2b5f3bSDag-Erling Smørgrav 204d4ecd108SDag-Erling Smørgrav if (signo) 205cf2b5f3bSDag-Erling Smørgrav _exit(1); 206d4ecd108SDag-Erling Smørgrav exit(1); 207cf2b5f3bSDag-Erling Smørgrav } 208e73e9afaSDag-Erling Smørgrav 209e2f6069cSDag-Erling Smørgrav static void 21019261079SEd Maste suspone(int pid, int signo) 211e2f6069cSDag-Erling Smørgrav { 212e2f6069cSDag-Erling Smørgrav int status; 213e2f6069cSDag-Erling Smørgrav 21419261079SEd Maste if (pid > 1) { 21519261079SEd Maste kill(pid, signo); 21619261079SEd Maste while (waitpid(pid, &status, WUNTRACED) == -1 && 217e2f6069cSDag-Erling Smørgrav errno == EINTR) 218e2f6069cSDag-Erling Smørgrav ; 219e2f6069cSDag-Erling Smørgrav } 220e2f6069cSDag-Erling Smørgrav } 221e2f6069cSDag-Erling Smørgrav 22219261079SEd Maste static void 22319261079SEd Maste suspchild(int signo) 22419261079SEd Maste { 22519261079SEd Maste suspone(do_cmd_pid, signo); 22619261079SEd Maste suspone(do_cmd_pid2, signo); 22719261079SEd Maste kill(getpid(), SIGSTOP); 22819261079SEd Maste } 22919261079SEd Maste 230b74df5b2SDag-Erling Smørgrav static int 231b74df5b2SDag-Erling Smørgrav do_local_cmd(arglist *a) 232b74df5b2SDag-Erling Smørgrav { 233b74df5b2SDag-Erling Smørgrav u_int i; 234b74df5b2SDag-Erling Smørgrav int status; 235b74df5b2SDag-Erling Smørgrav pid_t pid; 236b74df5b2SDag-Erling Smørgrav 237b74df5b2SDag-Erling Smørgrav if (a->num == 0) 238b74df5b2SDag-Erling Smørgrav fatal("do_local_cmd: no arguments"); 239b74df5b2SDag-Erling Smørgrav 240b74df5b2SDag-Erling Smørgrav if (verbose_mode) { 241b74df5b2SDag-Erling Smørgrav fprintf(stderr, "Executing:"); 242b74df5b2SDag-Erling Smørgrav for (i = 0; i < a->num; i++) 243076ad2f8SDag-Erling Smørgrav fmprintf(stderr, " %s", a->list[i]); 244b74df5b2SDag-Erling Smørgrav fprintf(stderr, "\n"); 245b74df5b2SDag-Erling Smørgrav } 246b74df5b2SDag-Erling Smørgrav if ((pid = fork()) == -1) 247b74df5b2SDag-Erling Smørgrav fatal("do_local_cmd: fork: %s", strerror(errno)); 248b74df5b2SDag-Erling Smørgrav 249b74df5b2SDag-Erling Smørgrav if (pid == 0) { 250b74df5b2SDag-Erling Smørgrav execvp(a->list[0], a->list); 251b74df5b2SDag-Erling Smørgrav perror(a->list[0]); 252b74df5b2SDag-Erling Smørgrav exit(1); 253b74df5b2SDag-Erling Smørgrav } 254b74df5b2SDag-Erling Smørgrav 255b74df5b2SDag-Erling Smørgrav do_cmd_pid = pid; 25619261079SEd Maste ssh_signal(SIGTERM, killchild); 25719261079SEd Maste ssh_signal(SIGINT, killchild); 25819261079SEd Maste ssh_signal(SIGHUP, killchild); 259b74df5b2SDag-Erling Smørgrav 260b74df5b2SDag-Erling Smørgrav while (waitpid(pid, &status, 0) == -1) 261b74df5b2SDag-Erling Smørgrav if (errno != EINTR) 262b74df5b2SDag-Erling Smørgrav fatal("do_local_cmd: waitpid: %s", strerror(errno)); 263b74df5b2SDag-Erling Smørgrav 264b74df5b2SDag-Erling Smørgrav do_cmd_pid = -1; 265b74df5b2SDag-Erling Smørgrav 266b74df5b2SDag-Erling Smørgrav if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 267b74df5b2SDag-Erling Smørgrav return (-1); 268b74df5b2SDag-Erling Smørgrav 269b74df5b2SDag-Erling Smørgrav return (0); 270b74df5b2SDag-Erling Smørgrav } 271b74df5b2SDag-Erling Smørgrav 272511b41d2SMark Murray /* 273511b41d2SMark Murray * This function executes the given command as the specified user on the 274511b41d2SMark Murray * given host. This returns < 0 if execution fails, and >= 0 otherwise. This 275511b41d2SMark Murray * assigns the input and output file descriptors on success. 276511b41d2SMark Murray */ 277511b41d2SMark Murray 278511b41d2SMark Murray int 27919261079SEd Maste do_cmd(char *program, char *host, char *remuser, int port, int subsystem, 28019261079SEd Maste char *cmd, int *fdin, int *fdout, pid_t *pid) 281511b41d2SMark Murray { 282f374ba41SEd Maste #ifdef USE_PIPES 283f374ba41SEd Maste int pin[2], pout[2]; 284f374ba41SEd Maste #else 285f374ba41SEd Maste int sv[2]; 286f374ba41SEd Maste #endif 287511b41d2SMark Murray 288511b41d2SMark Murray if (verbose_mode) 289076ad2f8SDag-Erling Smørgrav fmprintf(stderr, 290ae1f160dSDag-Erling Smørgrav "Executing: program %s host %s, user %s, command %s\n", 29119261079SEd Maste program, host, 292ae1f160dSDag-Erling Smørgrav remuser ? remuser : "(unspecified)", cmd); 293511b41d2SMark Murray 29447dd1d1bSDag-Erling Smørgrav if (port == -1) 29547dd1d1bSDag-Erling Smørgrav port = sshport; 29647dd1d1bSDag-Erling Smørgrav 297f374ba41SEd Maste #ifdef USE_PIPES 298f374ba41SEd Maste if (pipe(pin) == -1 || pipe(pout) == -1) 299333ee039SDag-Erling Smørgrav fatal("pipe: %s", strerror(errno)); 300f374ba41SEd Maste #else 301511b41d2SMark Murray /* Create a socket pair for communicating with ssh. */ 302f374ba41SEd Maste if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) 303f374ba41SEd Maste fatal("socketpair: %s", strerror(errno)); 304f374ba41SEd Maste #endif 305511b41d2SMark Murray 30619261079SEd Maste ssh_signal(SIGTSTP, suspchild); 30719261079SEd Maste ssh_signal(SIGTTIN, suspchild); 30819261079SEd Maste ssh_signal(SIGTTOU, suspchild); 309e2f6069cSDag-Erling Smørgrav 310cf2b5f3bSDag-Erling Smørgrav /* Fork a child to execute the command on the remote host using ssh. */ 31119261079SEd Maste *pid = fork(); 312f374ba41SEd Maste switch (*pid) { 313f374ba41SEd Maste case -1: 314f374ba41SEd Maste fatal("fork: %s", strerror(errno)); 315f374ba41SEd Maste case 0: 316511b41d2SMark Murray /* Child. */ 317f374ba41SEd Maste #ifdef USE_PIPES 318f374ba41SEd Maste if (dup2(pin[0], STDIN_FILENO) == -1 || 319f374ba41SEd Maste dup2(pout[1], STDOUT_FILENO) == -1) { 320f374ba41SEd Maste error("dup2: %s", strerror(errno)); 321f374ba41SEd Maste _exit(1); 322f374ba41SEd Maste } 323f374ba41SEd Maste close(pin[0]); 324511b41d2SMark Murray close(pin[1]); 325511b41d2SMark Murray close(pout[0]); 326511b41d2SMark Murray close(pout[1]); 327f374ba41SEd Maste #else 328f374ba41SEd Maste if (dup2(sv[0], STDIN_FILENO) == -1 || 329f374ba41SEd Maste dup2(sv[0], STDOUT_FILENO) == -1) { 330f374ba41SEd Maste error("dup2: %s", strerror(errno)); 331f374ba41SEd Maste _exit(1); 332f374ba41SEd Maste } 333f374ba41SEd Maste close(sv[0]); 334f374ba41SEd Maste close(sv[1]); 335f374ba41SEd Maste #endif 33619261079SEd Maste replacearg(&args, 0, "%s", program); 33747dd1d1bSDag-Erling Smørgrav if (port != -1) { 33847dd1d1bSDag-Erling Smørgrav addargs(&args, "-p"); 33947dd1d1bSDag-Erling Smørgrav addargs(&args, "%d", port); 34047dd1d1bSDag-Erling Smørgrav } 341b15c8340SDag-Erling Smørgrav if (remuser != NULL) { 342b15c8340SDag-Erling Smørgrav addargs(&args, "-l"); 343b15c8340SDag-Erling Smørgrav addargs(&args, "%s", remuser); 344b15c8340SDag-Erling Smørgrav } 34519261079SEd Maste if (subsystem) 34619261079SEd Maste addargs(&args, "-s"); 347b15c8340SDag-Erling Smørgrav addargs(&args, "--"); 348ae1f160dSDag-Erling Smørgrav addargs(&args, "%s", host); 349ae1f160dSDag-Erling Smørgrav addargs(&args, "%s", cmd); 350511b41d2SMark Murray 35119261079SEd Maste execvp(program, args.list); 35219261079SEd Maste perror(program); 353f374ba41SEd Maste _exit(1); 354f374ba41SEd Maste default: 355511b41d2SMark Murray /* Parent. Close the other side, and return the local side. */ 356f374ba41SEd Maste #ifdef USE_PIPES 357511b41d2SMark Murray close(pin[0]); 358511b41d2SMark Murray close(pout[1]); 359f374ba41SEd Maste *fdout = pin[1]; 360511b41d2SMark Murray *fdin = pout[0]; 361f374ba41SEd Maste #else 362f374ba41SEd Maste close(sv[0]); 363f374ba41SEd Maste *fdin = sv[1]; 364f374ba41SEd Maste *fdout = sv[1]; 365f374ba41SEd Maste #endif 36619261079SEd Maste ssh_signal(SIGTERM, killchild); 36719261079SEd Maste ssh_signal(SIGINT, killchild); 36819261079SEd Maste ssh_signal(SIGHUP, killchild); 369511b41d2SMark Murray return 0; 370511b41d2SMark Murray } 371f374ba41SEd Maste } 372511b41d2SMark Murray 3734a421b63SDag-Erling Smørgrav /* 374190cef3dSDag-Erling Smørgrav * This function executes a command similar to do_cmd(), but expects the 3754a421b63SDag-Erling Smørgrav * input and output descriptors to be setup by a previous call to do_cmd(). 3764a421b63SDag-Erling Smørgrav * This way the input and output of two commands can be connected. 3774a421b63SDag-Erling Smørgrav */ 3784a421b63SDag-Erling Smørgrav int 37919261079SEd Maste do_cmd2(char *host, char *remuser, int port, char *cmd, 38019261079SEd Maste int fdin, int fdout) 3814a421b63SDag-Erling Smørgrav { 3824a421b63SDag-Erling Smørgrav int status; 38319261079SEd Maste pid_t pid; 3844a421b63SDag-Erling Smørgrav 3854a421b63SDag-Erling Smørgrav if (verbose_mode) 386076ad2f8SDag-Erling Smørgrav fmprintf(stderr, 3874a421b63SDag-Erling Smørgrav "Executing: 2nd program %s host %s, user %s, command %s\n", 3884a421b63SDag-Erling Smørgrav ssh_program, host, 3894a421b63SDag-Erling Smørgrav remuser ? remuser : "(unspecified)", cmd); 3904a421b63SDag-Erling Smørgrav 39147dd1d1bSDag-Erling Smørgrav if (port == -1) 39247dd1d1bSDag-Erling Smørgrav port = sshport; 39347dd1d1bSDag-Erling Smørgrav 3944a421b63SDag-Erling Smørgrav /* Fork a child to execute the command on the remote host using ssh. */ 3954a421b63SDag-Erling Smørgrav pid = fork(); 3964a421b63SDag-Erling Smørgrav if (pid == 0) { 3974a421b63SDag-Erling Smørgrav dup2(fdin, 0); 3984a421b63SDag-Erling Smørgrav dup2(fdout, 1); 3994a421b63SDag-Erling Smørgrav 4004a421b63SDag-Erling Smørgrav replacearg(&args, 0, "%s", ssh_program); 40147dd1d1bSDag-Erling Smørgrav if (port != -1) { 40247dd1d1bSDag-Erling Smørgrav addargs(&args, "-p"); 40347dd1d1bSDag-Erling Smørgrav addargs(&args, "%d", port); 40447dd1d1bSDag-Erling Smørgrav } 4054a421b63SDag-Erling Smørgrav if (remuser != NULL) { 4064a421b63SDag-Erling Smørgrav addargs(&args, "-l"); 4074a421b63SDag-Erling Smørgrav addargs(&args, "%s", remuser); 4084a421b63SDag-Erling Smørgrav } 40919261079SEd Maste addargs(&args, "-oBatchMode=yes"); 4104a421b63SDag-Erling Smørgrav addargs(&args, "--"); 4114a421b63SDag-Erling Smørgrav addargs(&args, "%s", host); 4124a421b63SDag-Erling Smørgrav addargs(&args, "%s", cmd); 4134a421b63SDag-Erling Smørgrav 4144a421b63SDag-Erling Smørgrav execvp(ssh_program, args.list); 4154a421b63SDag-Erling Smørgrav perror(ssh_program); 4164a421b63SDag-Erling Smørgrav exit(1); 4174a421b63SDag-Erling Smørgrav } else if (pid == -1) { 4184a421b63SDag-Erling Smørgrav fatal("fork: %s", strerror(errno)); 4194a421b63SDag-Erling Smørgrav } 4204a421b63SDag-Erling Smørgrav while (waitpid(pid, &status, 0) == -1) 4214a421b63SDag-Erling Smørgrav if (errno != EINTR) 4224a421b63SDag-Erling Smørgrav fatal("do_cmd2: waitpid: %s", strerror(errno)); 4234a421b63SDag-Erling Smørgrav return 0; 4244a421b63SDag-Erling Smørgrav } 4254a421b63SDag-Erling Smørgrav 426511b41d2SMark Murray typedef struct { 427d4ecd108SDag-Erling Smørgrav size_t cnt; 428511b41d2SMark Murray char *buf; 429511b41d2SMark Murray } BUF; 430511b41d2SMark Murray 431511b41d2SMark Murray BUF *allocbuf(BUF *, int, int); 432511b41d2SMark Murray void lostconn(int); 433511b41d2SMark Murray int okname(char *); 43419261079SEd Maste void run_err(const char *,...) 43519261079SEd Maste __attribute__((__format__ (printf, 1, 2))) 43619261079SEd Maste __attribute__((__nonnull__ (1))); 43719261079SEd Maste int note_err(const char *,...) 43819261079SEd Maste __attribute__((__format__ (printf, 1, 2))); 439511b41d2SMark Murray void verifydir(char *); 440511b41d2SMark Murray 441511b41d2SMark Murray struct passwd *pwd; 442511b41d2SMark Murray uid_t userid; 44319261079SEd Maste int errs, remin, remout, remin2, remout2; 444afde5170SEd Maste int Tflag, pflag, iamremote, iamrecursive, targetshouldbedirectory; 445511b41d2SMark Murray 446511b41d2SMark Murray #define CMDNEEDS 64 447511b41d2SMark Murray char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 448511b41d2SMark Murray 44919261079SEd Maste enum scp_mode_e { 45019261079SEd Maste MODE_SCP, 45119261079SEd Maste MODE_SFTP 45219261079SEd Maste }; 45319261079SEd Maste 454511b41d2SMark Murray int response(void); 455511b41d2SMark Murray void rsource(char *, struct stat *); 456afde5170SEd Maste void sink(int, char *[], const char *); 457511b41d2SMark Murray void source(int, char *[]); 45819261079SEd Maste void tolocal(int, char *[], enum scp_mode_e, char *sftp_direct); 45919261079SEd Maste void toremote(int, char *[], enum scp_mode_e, char *sftp_direct); 460511b41d2SMark Murray void usage(void); 461511b41d2SMark Murray 46219261079SEd Maste void source_sftp(int, char *, char *, struct sftp_conn *); 46319261079SEd Maste void sink_sftp(int, char *, const char *, struct sftp_conn *); 46419261079SEd Maste void throughlocal_sftp(struct sftp_conn *, struct sftp_conn *, 46519261079SEd Maste char *, char *); 46619261079SEd Maste 467511b41d2SMark Murray int 468cf2b5f3bSDag-Erling Smørgrav main(int argc, char **argv) 469511b41d2SMark Murray { 470f374ba41SEd Maste int ch, fflag, tflag, status, r, n; 47119261079SEd Maste char **newargv, *argv0; 4724a421b63SDag-Erling Smørgrav const char *errstr; 473511b41d2SMark Murray extern char *optarg; 474511b41d2SMark Murray extern int optind; 475fb5aabcbSEd Maste enum scp_mode_e mode = MODE_SFTP; 47619261079SEd Maste char *sftp_direct = NULL; 477f374ba41SEd Maste long long llv; 478511b41d2SMark Murray 479b74df5b2SDag-Erling Smørgrav /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 480b74df5b2SDag-Erling Smørgrav sanitise_stdfd(); 481b74df5b2SDag-Erling Smørgrav 482ca86bcf2SDag-Erling Smørgrav msetlocale(); 483076ad2f8SDag-Erling Smørgrav 484333ee039SDag-Erling Smørgrav /* Copy argv, because we modify it */ 48519261079SEd Maste argv0 = argv[0]; 486ca86bcf2SDag-Erling Smørgrav newargv = xcalloc(MAXIMUM(argc + 1, 1), sizeof(*newargv)); 487333ee039SDag-Erling Smørgrav for (n = 0; n < argc; n++) 488333ee039SDag-Erling Smørgrav newargv[n] = xstrdup(argv[n]); 489333ee039SDag-Erling Smørgrav argv = newargv; 490333ee039SDag-Erling Smørgrav 491cf2b5f3bSDag-Erling Smørgrav __progname = ssh_get_progname(argv[0]); 49283d2307dSDag-Erling Smørgrav 493e9e8876aSEd Maste log_init(argv0, log_level, SYSLOG_FACILITY_USER, 2); 49419261079SEd Maste 495b74df5b2SDag-Erling Smørgrav memset(&args, '\0', sizeof(args)); 4964a421b63SDag-Erling Smørgrav memset(&remote_remote_args, '\0', sizeof(remote_remote_args)); 4974a421b63SDag-Erling Smørgrav args.list = remote_remote_args.list = NULL; 498b74df5b2SDag-Erling Smørgrav addargs(&args, "%s", ssh_program); 499ae1f160dSDag-Erling Smørgrav addargs(&args, "-x"); 5004a421b63SDag-Erling Smørgrav addargs(&args, "-oPermitLocalCommand=no"); 5014a421b63SDag-Erling Smørgrav addargs(&args, "-oClearAllForwardings=yes"); 50247dd1d1bSDag-Erling Smørgrav addargs(&args, "-oRemoteCommand=none"); 50347dd1d1bSDag-Erling Smørgrav addargs(&args, "-oRequestTTY=no"); 5045b9b2fafSBrian Feldman 505afde5170SEd Maste fflag = Tflag = tflag = 0; 506afde5170SEd Maste while ((ch = getopt(argc, argv, 507f374ba41SEd Maste "12346ABCTdfOpqRrstvD:F:J:M:P:S:c:i:l:o:X:")) != -1) { 508511b41d2SMark Murray switch (ch) { 509511b41d2SMark Murray /* User-visible flags. */ 510e73e9afaSDag-Erling Smørgrav case '1': 5114f52dfbbSDag-Erling Smørgrav fatal("SSH protocol v.1 is no longer supported"); 5124f52dfbbSDag-Erling Smørgrav break; 513e73e9afaSDag-Erling Smørgrav case '2': 5144f52dfbbSDag-Erling Smørgrav /* Ignored */ 5154f52dfbbSDag-Erling Smørgrav break; 51619261079SEd Maste case 'A': 517511b41d2SMark Murray case '4': 518511b41d2SMark Murray case '6': 5195b9b2fafSBrian Feldman case 'C': 520ae1f160dSDag-Erling Smørgrav addargs(&args, "-%c", ch); 5214a421b63SDag-Erling Smørgrav addargs(&remote_remote_args, "-%c", ch); 5224a421b63SDag-Erling Smørgrav break; 52319261079SEd Maste case 'D': 52419261079SEd Maste sftp_direct = optarg; 52519261079SEd Maste break; 5264a421b63SDag-Erling Smørgrav case '3': 5274a421b63SDag-Erling Smørgrav throughlocal = 1; 5285b9b2fafSBrian Feldman break; 52919261079SEd Maste case 'R': 53019261079SEd Maste throughlocal = 0; 53119261079SEd Maste break; 5325b9b2fafSBrian Feldman case 'o': 5335b9b2fafSBrian Feldman case 'c': 5345b9b2fafSBrian Feldman case 'i': 535ae1f160dSDag-Erling Smørgrav case 'F': 53619261079SEd Maste case 'J': 5374a421b63SDag-Erling Smørgrav addargs(&remote_remote_args, "-%c", ch); 5384a421b63SDag-Erling Smørgrav addargs(&remote_remote_args, "%s", optarg); 539b15c8340SDag-Erling Smørgrav addargs(&args, "-%c", ch); 540b15c8340SDag-Erling Smørgrav addargs(&args, "%s", optarg); 5415b9b2fafSBrian Feldman break; 54219261079SEd Maste case 'O': 54319261079SEd Maste mode = MODE_SCP; 54419261079SEd Maste break; 54519261079SEd Maste case 's': 54619261079SEd Maste mode = MODE_SFTP; 54719261079SEd Maste break; 5485b9b2fafSBrian Feldman case 'P': 54947dd1d1bSDag-Erling Smørgrav sshport = a2port(optarg); 55047dd1d1bSDag-Erling Smørgrav if (sshport <= 0) 55147dd1d1bSDag-Erling Smørgrav fatal("bad port \"%s\"\n", optarg); 5525b9b2fafSBrian Feldman break; 5535b9b2fafSBrian Feldman case 'B': 5544a421b63SDag-Erling Smørgrav addargs(&remote_remote_args, "-oBatchmode=yes"); 5554a421b63SDag-Erling Smørgrav addargs(&args, "-oBatchmode=yes"); 556511b41d2SMark Murray break; 557e73e9afaSDag-Erling Smørgrav case 'l': 5584a421b63SDag-Erling Smørgrav limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024, 5594a421b63SDag-Erling Smørgrav &errstr); 5604a421b63SDag-Erling Smørgrav if (errstr != NULL) 561e73e9afaSDag-Erling Smørgrav usage(); 5624a421b63SDag-Erling Smørgrav limit_kbps *= 1024; /* kbps */ 5634a421b63SDag-Erling Smørgrav bandwidth_limit_init(&bwlimit, limit_kbps, COPY_BUFLEN); 564e73e9afaSDag-Erling Smørgrav break; 565511b41d2SMark Murray case 'p': 566511b41d2SMark Murray pflag = 1; 567511b41d2SMark Murray break; 568511b41d2SMark Murray case 'r': 569511b41d2SMark Murray iamrecursive = 1; 570511b41d2SMark Murray break; 571b66f2d16SKris Kennaway case 'S': 5725b9b2fafSBrian Feldman ssh_program = xstrdup(optarg); 5735b9b2fafSBrian Feldman break; 5745b9b2fafSBrian Feldman case 'v': 575ae1f160dSDag-Erling Smørgrav addargs(&args, "-v"); 5764a421b63SDag-Erling Smørgrav addargs(&remote_remote_args, "-v"); 57719261079SEd Maste if (verbose_mode == 0) 57819261079SEd Maste log_level = SYSLOG_LEVEL_DEBUG1; 57919261079SEd Maste else if (log_level < SYSLOG_LEVEL_DEBUG3) 58019261079SEd Maste log_level++; 5815b9b2fafSBrian Feldman verbose_mode = 1; 5825b9b2fafSBrian Feldman break; 5835b9b2fafSBrian Feldman case 'q': 5841ec0d754SDag-Erling Smørgrav addargs(&args, "-q"); 5854a421b63SDag-Erling Smørgrav addargs(&remote_remote_args, "-q"); 5865b9b2fafSBrian Feldman showprogress = 0; 587b66f2d16SKris Kennaway break; 588f374ba41SEd Maste case 'X': 589f374ba41SEd Maste /* Please keep in sync with sftp.c -X */ 590f374ba41SEd Maste if (strncmp(optarg, "buffer=", 7) == 0) { 591f374ba41SEd Maste r = scan_scaled(optarg + 7, &llv); 592f374ba41SEd Maste if (r == 0 && (llv <= 0 || llv > 256 * 1024)) { 593f374ba41SEd Maste r = -1; 594f374ba41SEd Maste errno = EINVAL; 595f374ba41SEd Maste } 596f374ba41SEd Maste if (r == -1) { 597f374ba41SEd Maste fatal("Invalid buffer size \"%s\": %s", 598f374ba41SEd Maste optarg + 7, strerror(errno)); 599f374ba41SEd Maste } 600f374ba41SEd Maste sftp_copy_buflen = (size_t)llv; 601f374ba41SEd Maste } else if (strncmp(optarg, "nrequests=", 10) == 0) { 602f374ba41SEd Maste llv = strtonum(optarg + 10, 1, 256 * 1024, 603f374ba41SEd Maste &errstr); 604f374ba41SEd Maste if (errstr != NULL) { 605f374ba41SEd Maste fatal("Invalid number of requests " 606f374ba41SEd Maste "\"%s\": %s", optarg + 10, errstr); 607f374ba41SEd Maste } 608f374ba41SEd Maste sftp_nrequests = (size_t)llv; 609f374ba41SEd Maste } else { 610f374ba41SEd Maste fatal("Invalid -X option"); 611f374ba41SEd Maste } 612f374ba41SEd Maste break; 613b66f2d16SKris Kennaway 614511b41d2SMark Murray /* Server options. */ 615511b41d2SMark Murray case 'd': 616511b41d2SMark Murray targetshouldbedirectory = 1; 617511b41d2SMark Murray break; 618511b41d2SMark Murray case 'f': /* "from" */ 619511b41d2SMark Murray iamremote = 1; 620511b41d2SMark Murray fflag = 1; 621511b41d2SMark Murray break; 622511b41d2SMark Murray case 't': /* "to" */ 623511b41d2SMark Murray iamremote = 1; 624511b41d2SMark Murray tflag = 1; 62583d2307dSDag-Erling Smørgrav #ifdef HAVE_CYGWIN 62683d2307dSDag-Erling Smørgrav setmode(0, O_BINARY); 62783d2307dSDag-Erling Smørgrav #endif 628511b41d2SMark Murray break; 629afde5170SEd Maste case 'T': 630afde5170SEd Maste Tflag = 1; 631afde5170SEd Maste break; 632511b41d2SMark Murray default: 633511b41d2SMark Murray usage(); 634511b41d2SMark Murray } 635afde5170SEd Maste } 636511b41d2SMark Murray argc -= optind; 637511b41d2SMark Murray argv += optind; 638511b41d2SMark Murray 639e9e8876aSEd Maste log_init(argv0, log_level, SYSLOG_FACILITY_USER, 2); 64019261079SEd Maste 64119261079SEd Maste /* Do this last because we want the user to be able to override it */ 64219261079SEd Maste addargs(&args, "-oForwardAgent=no"); 64319261079SEd Maste 64419261079SEd Maste if (iamremote) 64519261079SEd Maste mode = MODE_SCP; 64619261079SEd Maste 647511b41d2SMark Murray if ((pwd = getpwuid(userid = getuid())) == NULL) 648cf2b5f3bSDag-Erling Smørgrav fatal("unknown user %u", (u_int) userid); 649511b41d2SMark Murray 650d4af9e69SDag-Erling Smørgrav if (!isatty(STDOUT_FILENO)) 651511b41d2SMark Murray showprogress = 0; 652511b41d2SMark Murray 653acc1a9efSDag-Erling Smørgrav if (pflag) { 654acc1a9efSDag-Erling Smørgrav /* Cannot pledge: -p allows setuid/setgid files... */ 655acc1a9efSDag-Erling Smørgrav } else { 656acc1a9efSDag-Erling Smørgrav if (pledge("stdio rpath wpath cpath fattr tty proc exec", 657acc1a9efSDag-Erling Smørgrav NULL) == -1) { 658acc1a9efSDag-Erling Smørgrav perror("pledge"); 659acc1a9efSDag-Erling Smørgrav exit(1); 660acc1a9efSDag-Erling Smørgrav } 661acc1a9efSDag-Erling Smørgrav } 662acc1a9efSDag-Erling Smørgrav 663511b41d2SMark Murray remin = STDIN_FILENO; 664511b41d2SMark Murray remout = STDOUT_FILENO; 665511b41d2SMark Murray 666511b41d2SMark Murray if (fflag) { 667511b41d2SMark Murray /* Follow "protocol", send data. */ 668511b41d2SMark Murray (void) response(); 669511b41d2SMark Murray source(argc, argv); 670511b41d2SMark Murray exit(errs != 0); 671511b41d2SMark Murray } 672511b41d2SMark Murray if (tflag) { 673511b41d2SMark Murray /* Receive data. */ 674afde5170SEd Maste sink(argc, argv, NULL); 675511b41d2SMark Murray exit(errs != 0); 676511b41d2SMark Murray } 677511b41d2SMark Murray if (argc < 2) 678511b41d2SMark Murray usage(); 679511b41d2SMark Murray if (argc > 2) 680511b41d2SMark Murray targetshouldbedirectory = 1; 681511b41d2SMark Murray 682511b41d2SMark Murray remin = remout = -1; 683e73e9afaSDag-Erling Smørgrav do_cmd_pid = -1; 684511b41d2SMark Murray /* Command to be executed on remote system using "ssh". */ 6851e8db6e2SBrian Feldman (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", 6861e8db6e2SBrian Feldman verbose_mode ? " -v" : "", 687511b41d2SMark Murray iamrecursive ? " -r" : "", pflag ? " -p" : "", 688511b41d2SMark Murray targetshouldbedirectory ? " -d" : ""); 689511b41d2SMark Murray 69019261079SEd Maste (void) ssh_signal(SIGPIPE, lostconn); 691511b41d2SMark Murray 69247dd1d1bSDag-Erling Smørgrav if (colon(argv[argc - 1])) /* Dest is remote host. */ 69319261079SEd Maste toremote(argc, argv, mode, sftp_direct); 694511b41d2SMark Murray else { 695511b41d2SMark Murray if (targetshouldbedirectory) 696511b41d2SMark Murray verifydir(argv[argc - 1]); 69719261079SEd Maste tolocal(argc, argv, mode, sftp_direct); /* Dest is local host. */ 698511b41d2SMark Murray } 699e73e9afaSDag-Erling Smørgrav /* 700e73e9afaSDag-Erling Smørgrav * Finally check the exit status of the ssh process, if one was forked 701cce7d346SDag-Erling Smørgrav * and no error has occurred yet 702e73e9afaSDag-Erling Smørgrav */ 703e9e8876aSEd Maste if (do_cmd_pid != -1 && (mode == MODE_SFTP || errs == 0)) { 704e73e9afaSDag-Erling Smørgrav if (remin != -1) 705e73e9afaSDag-Erling Smørgrav (void) close(remin); 706e73e9afaSDag-Erling Smørgrav if (remout != -1) 707e73e9afaSDag-Erling Smørgrav (void) close(remout); 708e73e9afaSDag-Erling Smørgrav if (waitpid(do_cmd_pid, &status, 0) == -1) 709e73e9afaSDag-Erling Smørgrav errs = 1; 710e73e9afaSDag-Erling Smørgrav else { 711e73e9afaSDag-Erling Smørgrav if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 712e73e9afaSDag-Erling Smørgrav errs = 1; 713e73e9afaSDag-Erling Smørgrav } 714e73e9afaSDag-Erling Smørgrav } 715511b41d2SMark Murray exit(errs != 0); 716511b41d2SMark Murray } 717511b41d2SMark Murray 7184a421b63SDag-Erling Smørgrav /* Callback from atomicio6 to update progress meter and limit bandwidth */ 7194a421b63SDag-Erling Smørgrav static int 7204a421b63SDag-Erling Smørgrav scpio(void *_cnt, size_t s) 721d4af9e69SDag-Erling Smørgrav { 7224a421b63SDag-Erling Smørgrav off_t *cnt = (off_t *)_cnt; 723d4af9e69SDag-Erling Smørgrav 7244a421b63SDag-Erling Smørgrav *cnt += s; 72519261079SEd Maste refresh_progress_meter(0); 7264a421b63SDag-Erling Smørgrav if (limit_kbps > 0) 7274a421b63SDag-Erling Smørgrav bandwidth_limit(&bwlimit, s); 7284a421b63SDag-Erling Smørgrav return 0; 729d4af9e69SDag-Erling Smørgrav } 730d4af9e69SDag-Erling Smørgrav 731e4a9863fSDag-Erling Smørgrav static int 732e4a9863fSDag-Erling Smørgrav do_times(int fd, int verb, const struct stat *sb) 733e4a9863fSDag-Erling Smørgrav { 734e4a9863fSDag-Erling Smørgrav /* strlen(2^64) == 20; strlen(10^6) == 7 */ 735e4a9863fSDag-Erling Smørgrav char buf[(20 + 7 + 2) * 2 + 2]; 736e4a9863fSDag-Erling Smørgrav 737e4a9863fSDag-Erling Smørgrav (void)snprintf(buf, sizeof(buf), "T%llu 0 %llu 0\n", 738e4a9863fSDag-Erling Smørgrav (unsigned long long) (sb->st_mtime < 0 ? 0 : sb->st_mtime), 739e4a9863fSDag-Erling Smørgrav (unsigned long long) (sb->st_atime < 0 ? 0 : sb->st_atime)); 740e4a9863fSDag-Erling Smørgrav if (verb) { 741e4a9863fSDag-Erling Smørgrav fprintf(stderr, "File mtime %lld atime %lld\n", 742e4a9863fSDag-Erling Smørgrav (long long)sb->st_mtime, (long long)sb->st_atime); 743e4a9863fSDag-Erling Smørgrav fprintf(stderr, "Sending file timestamps: %s", buf); 744e4a9863fSDag-Erling Smørgrav } 745e4a9863fSDag-Erling Smørgrav (void) atomicio(vwrite, fd, buf, strlen(buf)); 746e4a9863fSDag-Erling Smørgrav return (response()); 747e4a9863fSDag-Erling Smørgrav } 748e4a9863fSDag-Erling Smørgrav 74947dd1d1bSDag-Erling Smørgrav static int 75047dd1d1bSDag-Erling Smørgrav parse_scp_uri(const char *uri, char **userp, char **hostp, int *portp, 75147dd1d1bSDag-Erling Smørgrav char **pathp) 752511b41d2SMark Murray { 75347dd1d1bSDag-Erling Smørgrav int r; 75447dd1d1bSDag-Erling Smørgrav 75547dd1d1bSDag-Erling Smørgrav r = parse_uri("scp", uri, userp, hostp, portp, pathp); 75647dd1d1bSDag-Erling Smørgrav if (r == 0 && *pathp == NULL) 75747dd1d1bSDag-Erling Smørgrav *pathp = xstrdup("."); 75847dd1d1bSDag-Erling Smørgrav return r; 75947dd1d1bSDag-Erling Smørgrav } 76047dd1d1bSDag-Erling Smørgrav 7610967215dSEd Maste /* Appends a string to an array; returns 0 on success, -1 on alloc failure */ 7620967215dSEd Maste static int 7630967215dSEd Maste append(char *cp, char ***ap, size_t *np) 7640967215dSEd Maste { 7650967215dSEd Maste char **tmp; 7660967215dSEd Maste 7670967215dSEd Maste if ((tmp = reallocarray(*ap, *np + 1, sizeof(*tmp))) == NULL) 7680967215dSEd Maste return -1; 7690967215dSEd Maste tmp[(*np)] = cp; 7700967215dSEd Maste (*np)++; 7710967215dSEd Maste *ap = tmp; 7720967215dSEd Maste return 0; 7730967215dSEd Maste } 7740967215dSEd Maste 7750967215dSEd Maste /* 7760967215dSEd Maste * Finds the start and end of the first brace pair in the pattern. 7770967215dSEd Maste * returns 0 on success or -1 for invalid patterns. 7780967215dSEd Maste */ 7790967215dSEd Maste static int 7800967215dSEd Maste find_brace(const char *pattern, int *startp, int *endp) 7810967215dSEd Maste { 7820967215dSEd Maste int i; 7830967215dSEd Maste int in_bracket, brace_level; 7840967215dSEd Maste 7850967215dSEd Maste *startp = *endp = -1; 7860967215dSEd Maste in_bracket = brace_level = 0; 7870967215dSEd Maste for (i = 0; i < INT_MAX && *endp < 0 && pattern[i] != '\0'; i++) { 7880967215dSEd Maste switch (pattern[i]) { 7890967215dSEd Maste case '\\': 7900967215dSEd Maste /* skip next character */ 7910967215dSEd Maste if (pattern[i + 1] != '\0') 7920967215dSEd Maste i++; 7930967215dSEd Maste break; 7940967215dSEd Maste case '[': 7950967215dSEd Maste in_bracket = 1; 7960967215dSEd Maste break; 7970967215dSEd Maste case ']': 7980967215dSEd Maste in_bracket = 0; 7990967215dSEd Maste break; 8000967215dSEd Maste case '{': 8010967215dSEd Maste if (in_bracket) 8020967215dSEd Maste break; 8030967215dSEd Maste if (pattern[i + 1] == '}') { 8040967215dSEd Maste /* Protect a single {}, for find(1), like csh */ 8050967215dSEd Maste i++; /* skip */ 8060967215dSEd Maste break; 8070967215dSEd Maste } 8080967215dSEd Maste if (*startp == -1) 8090967215dSEd Maste *startp = i; 8100967215dSEd Maste brace_level++; 8110967215dSEd Maste break; 8120967215dSEd Maste case '}': 8130967215dSEd Maste if (in_bracket) 8140967215dSEd Maste break; 8150967215dSEd Maste if (*startp < 0) { 8160967215dSEd Maste /* Unbalanced brace */ 8170967215dSEd Maste return -1; 8180967215dSEd Maste } 8190967215dSEd Maste if (--brace_level <= 0) 8200967215dSEd Maste *endp = i; 8210967215dSEd Maste break; 8220967215dSEd Maste } 8230967215dSEd Maste } 8240967215dSEd Maste /* unbalanced brackets/braces */ 8250967215dSEd Maste if (*endp < 0 && (*startp >= 0 || in_bracket)) 8260967215dSEd Maste return -1; 8270967215dSEd Maste return 0; 8280967215dSEd Maste } 8290967215dSEd Maste 8300967215dSEd Maste /* 8310967215dSEd Maste * Assembles and records a successfully-expanded pattern, returns -1 on 8320967215dSEd Maste * alloc failure. 8330967215dSEd Maste */ 8340967215dSEd Maste static int 8350967215dSEd Maste emit_expansion(const char *pattern, int brace_start, int brace_end, 8360967215dSEd Maste int sel_start, int sel_end, char ***patternsp, size_t *npatternsp) 8370967215dSEd Maste { 8380967215dSEd Maste char *cp; 8390967215dSEd Maste int o = 0, tail_len = strlen(pattern + brace_end + 1); 8400967215dSEd Maste 8410967215dSEd Maste if ((cp = malloc(brace_start + (sel_end - sel_start) + 8420967215dSEd Maste tail_len + 1)) == NULL) 8430967215dSEd Maste return -1; 8440967215dSEd Maste 8450967215dSEd Maste /* Pattern before initial brace */ 8460967215dSEd Maste if (brace_start > 0) { 8470967215dSEd Maste memcpy(cp, pattern, brace_start); 8480967215dSEd Maste o = brace_start; 8490967215dSEd Maste } 8500967215dSEd Maste /* Current braced selection */ 8510967215dSEd Maste if (sel_end - sel_start > 0) { 8520967215dSEd Maste memcpy(cp + o, pattern + sel_start, 8530967215dSEd Maste sel_end - sel_start); 8540967215dSEd Maste o += sel_end - sel_start; 8550967215dSEd Maste } 8560967215dSEd Maste /* Remainder of pattern after closing brace */ 8570967215dSEd Maste if (tail_len > 0) { 8580967215dSEd Maste memcpy(cp + o, pattern + brace_end + 1, tail_len); 8590967215dSEd Maste o += tail_len; 8600967215dSEd Maste } 8610967215dSEd Maste cp[o] = '\0'; 8620967215dSEd Maste if (append(cp, patternsp, npatternsp) != 0) { 8630967215dSEd Maste free(cp); 8640967215dSEd Maste return -1; 8650967215dSEd Maste } 8660967215dSEd Maste return 0; 8670967215dSEd Maste } 8680967215dSEd Maste 8690967215dSEd Maste /* 8700967215dSEd Maste * Expand the first encountered brace in pattern, appending the expanded 8710967215dSEd Maste * patterns it yielded to the *patternsp array. 8720967215dSEd Maste * 8730967215dSEd Maste * Returns 0 on success or -1 on allocation failure. 8740967215dSEd Maste * 8750967215dSEd Maste * Signals whether expansion was performed via *expanded and whether 8760967215dSEd Maste * pattern was invalid via *invalid. 8770967215dSEd Maste */ 8780967215dSEd Maste static int 8790967215dSEd Maste brace_expand_one(const char *pattern, char ***patternsp, size_t *npatternsp, 8800967215dSEd Maste int *expanded, int *invalid) 8810967215dSEd Maste { 8820967215dSEd Maste int i; 8830967215dSEd Maste int in_bracket, brace_start, brace_end, brace_level; 8840967215dSEd Maste int sel_start, sel_end; 8850967215dSEd Maste 8860967215dSEd Maste *invalid = *expanded = 0; 8870967215dSEd Maste 8880967215dSEd Maste if (find_brace(pattern, &brace_start, &brace_end) != 0) { 8890967215dSEd Maste *invalid = 1; 8900967215dSEd Maste return 0; 8910967215dSEd Maste } else if (brace_start == -1) 8920967215dSEd Maste return 0; 8930967215dSEd Maste 8940967215dSEd Maste in_bracket = brace_level = 0; 8950967215dSEd Maste for (i = sel_start = brace_start + 1; i < brace_end; i++) { 8960967215dSEd Maste switch (pattern[i]) { 8970967215dSEd Maste case '{': 8980967215dSEd Maste if (in_bracket) 8990967215dSEd Maste break; 9000967215dSEd Maste brace_level++; 9010967215dSEd Maste break; 9020967215dSEd Maste case '}': 9030967215dSEd Maste if (in_bracket) 9040967215dSEd Maste break; 9050967215dSEd Maste brace_level--; 9060967215dSEd Maste break; 9070967215dSEd Maste case '[': 9080967215dSEd Maste in_bracket = 1; 9090967215dSEd Maste break; 9100967215dSEd Maste case ']': 9110967215dSEd Maste in_bracket = 0; 9120967215dSEd Maste break; 9130967215dSEd Maste case '\\': 9140967215dSEd Maste if (i < brace_end - 1) 9150967215dSEd Maste i++; /* skip */ 9160967215dSEd Maste break; 9170967215dSEd Maste } 9180967215dSEd Maste if (pattern[i] == ',' || i == brace_end - 1) { 9190967215dSEd Maste if (in_bracket || brace_level > 0) 9200967215dSEd Maste continue; 9210967215dSEd Maste /* End of a selection, emit an expanded pattern */ 9220967215dSEd Maste 9230967215dSEd Maste /* Adjust end index for last selection */ 9240967215dSEd Maste sel_end = (i == brace_end - 1) ? brace_end : i; 9250967215dSEd Maste if (emit_expansion(pattern, brace_start, brace_end, 9260967215dSEd Maste sel_start, sel_end, patternsp, npatternsp) != 0) 9270967215dSEd Maste return -1; 9280967215dSEd Maste /* move on to the next selection */ 9290967215dSEd Maste sel_start = i + 1; 9300967215dSEd Maste continue; 9310967215dSEd Maste } 9320967215dSEd Maste } 9330967215dSEd Maste if (in_bracket || brace_level > 0) { 9340967215dSEd Maste *invalid = 1; 9350967215dSEd Maste return 0; 9360967215dSEd Maste } 9370967215dSEd Maste /* success */ 9380967215dSEd Maste *expanded = 1; 9390967215dSEd Maste return 0; 9400967215dSEd Maste } 9410967215dSEd Maste 9420967215dSEd Maste /* Expand braces from pattern. Returns 0 on success, -1 on failure */ 9430967215dSEd Maste static int 9440967215dSEd Maste brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp) 9450967215dSEd Maste { 9460967215dSEd Maste char *cp, *cp2, **active = NULL, **done = NULL; 9470967215dSEd Maste size_t i, nactive = 0, ndone = 0; 9480967215dSEd Maste int ret = -1, invalid = 0, expanded = 0; 9490967215dSEd Maste 9500967215dSEd Maste *patternsp = NULL; 9510967215dSEd Maste *npatternsp = 0; 9520967215dSEd Maste 9530967215dSEd Maste /* Start the worklist with the original pattern */ 9540967215dSEd Maste if ((cp = strdup(pattern)) == NULL) 9550967215dSEd Maste return -1; 9560967215dSEd Maste if (append(cp, &active, &nactive) != 0) { 9570967215dSEd Maste free(cp); 9580967215dSEd Maste return -1; 9590967215dSEd Maste } 9600967215dSEd Maste while (nactive > 0) { 9610967215dSEd Maste cp = active[nactive - 1]; 9620967215dSEd Maste nactive--; 9630967215dSEd Maste if (brace_expand_one(cp, &active, &nactive, 9640967215dSEd Maste &expanded, &invalid) == -1) { 9650967215dSEd Maste free(cp); 9660967215dSEd Maste goto fail; 9670967215dSEd Maste } 9680967215dSEd Maste if (invalid) 96919261079SEd Maste fatal_f("invalid brace pattern \"%s\"", cp); 9700967215dSEd Maste if (expanded) { 9710967215dSEd Maste /* 9720967215dSEd Maste * Current entry expanded to new entries on the 9730967215dSEd Maste * active list; discard the progenitor pattern. 9740967215dSEd Maste */ 9750967215dSEd Maste free(cp); 9760967215dSEd Maste continue; 9770967215dSEd Maste } 9780967215dSEd Maste /* 9790967215dSEd Maste * Pattern did not expand; append the finename component to 9800967215dSEd Maste * the completed list 9810967215dSEd Maste */ 9820967215dSEd Maste if ((cp2 = strrchr(cp, '/')) != NULL) 9830967215dSEd Maste *cp2++ = '\0'; 9840967215dSEd Maste else 9850967215dSEd Maste cp2 = cp; 9860967215dSEd Maste if (append(xstrdup(cp2), &done, &ndone) != 0) { 9870967215dSEd Maste free(cp); 9880967215dSEd Maste goto fail; 9890967215dSEd Maste } 9900967215dSEd Maste free(cp); 9910967215dSEd Maste } 9920967215dSEd Maste /* success */ 9930967215dSEd Maste *patternsp = done; 9940967215dSEd Maste *npatternsp = ndone; 9950967215dSEd Maste done = NULL; 9960967215dSEd Maste ndone = 0; 9970967215dSEd Maste ret = 0; 9980967215dSEd Maste fail: 9990967215dSEd Maste for (i = 0; i < nactive; i++) 10000967215dSEd Maste free(active[i]); 10010967215dSEd Maste free(active); 10020967215dSEd Maste for (i = 0; i < ndone; i++) 10030967215dSEd Maste free(done[i]); 10040967215dSEd Maste free(done); 10050967215dSEd Maste return ret; 10060967215dSEd Maste } 10070967215dSEd Maste 100819261079SEd Maste static struct sftp_conn * 100919261079SEd Maste do_sftp_connect(char *host, char *user, int port, char *sftp_direct, 101019261079SEd Maste int *reminp, int *remoutp, int *pidp) 101119261079SEd Maste { 101219261079SEd Maste if (sftp_direct == NULL) { 101319261079SEd Maste if (do_cmd(ssh_program, host, user, port, 1, "sftp", 101419261079SEd Maste reminp, remoutp, pidp) < 0) 101519261079SEd Maste return NULL; 101619261079SEd Maste 101719261079SEd Maste } else { 101887c1498dSEd Maste freeargs(&args); 101919261079SEd Maste addargs(&args, "sftp-server"); 102019261079SEd Maste if (do_cmd(sftp_direct, host, NULL, -1, 0, "sftp", 102119261079SEd Maste reminp, remoutp, pidp) < 0) 102219261079SEd Maste return NULL; 102319261079SEd Maste } 1024f374ba41SEd Maste return do_init(*reminp, *remoutp, 1025f374ba41SEd Maste sftp_copy_buflen, sftp_nrequests, limit_kbps); 102619261079SEd Maste } 102719261079SEd Maste 102847dd1d1bSDag-Erling Smørgrav void 102919261079SEd Maste toremote(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct) 103047dd1d1bSDag-Erling Smørgrav { 103147dd1d1bSDag-Erling Smørgrav char *suser = NULL, *host = NULL, *src = NULL; 103247dd1d1bSDag-Erling Smørgrav char *bp, *tuser, *thost, *targ; 103347dd1d1bSDag-Erling Smørgrav int sport = -1, tport = -1; 103419261079SEd Maste struct sftp_conn *conn = NULL, *conn2 = NULL; 1035b74df5b2SDag-Erling Smørgrav arglist alist; 103619261079SEd Maste int i, r, status; 10374a421b63SDag-Erling Smørgrav u_int j; 1038b74df5b2SDag-Erling Smørgrav 1039b74df5b2SDag-Erling Smørgrav memset(&alist, '\0', sizeof(alist)); 1040b74df5b2SDag-Erling Smørgrav alist.list = NULL; 1041511b41d2SMark Murray 104247dd1d1bSDag-Erling Smørgrav /* Parse target */ 104347dd1d1bSDag-Erling Smørgrav r = parse_scp_uri(argv[argc - 1], &tuser, &thost, &tport, &targ); 104447dd1d1bSDag-Erling Smørgrav if (r == -1) { 104547dd1d1bSDag-Erling Smørgrav fmprintf(stderr, "%s: invalid uri\n", argv[argc - 1]); 104647dd1d1bSDag-Erling Smørgrav ++errs; 104747dd1d1bSDag-Erling Smørgrav goto out; 1048511b41d2SMark Murray } 104947dd1d1bSDag-Erling Smørgrav if (r != 0) { 105047dd1d1bSDag-Erling Smørgrav if (parse_user_host_path(argv[argc - 1], &tuser, &thost, 105147dd1d1bSDag-Erling Smørgrav &targ) == -1) { 105247dd1d1bSDag-Erling Smørgrav fmprintf(stderr, "%s: invalid target\n", argv[argc - 1]); 105347dd1d1bSDag-Erling Smørgrav ++errs; 105447dd1d1bSDag-Erling Smørgrav goto out; 105547dd1d1bSDag-Erling Smørgrav } 105647dd1d1bSDag-Erling Smørgrav } 1057b74df5b2SDag-Erling Smørgrav 105847dd1d1bSDag-Erling Smørgrav /* Parse source files */ 1059511b41d2SMark Murray for (i = 0; i < argc - 1; i++) { 106047dd1d1bSDag-Erling Smørgrav free(suser); 106147dd1d1bSDag-Erling Smørgrav free(host); 106247dd1d1bSDag-Erling Smørgrav free(src); 106347dd1d1bSDag-Erling Smørgrav r = parse_scp_uri(argv[i], &suser, &host, &sport, &src); 106447dd1d1bSDag-Erling Smørgrav if (r == -1) { 106547dd1d1bSDag-Erling Smørgrav fmprintf(stderr, "%s: invalid uri\n", argv[i]); 106647dd1d1bSDag-Erling Smørgrav ++errs; 10674a421b63SDag-Erling Smørgrav continue; 10684a421b63SDag-Erling Smørgrav } 106947dd1d1bSDag-Erling Smørgrav if (r != 0) { 107047dd1d1bSDag-Erling Smørgrav parse_user_host_path(argv[i], &suser, &host, &src); 107147dd1d1bSDag-Erling Smørgrav } 107247dd1d1bSDag-Erling Smørgrav if (suser != NULL && !okname(suser)) { 107347dd1d1bSDag-Erling Smørgrav ++errs; 107447dd1d1bSDag-Erling Smørgrav continue; 107547dd1d1bSDag-Erling Smørgrav } 107647dd1d1bSDag-Erling Smørgrav if (host && throughlocal) { /* extended remote to remote */ 107719261079SEd Maste if (mode == MODE_SFTP) { 107819261079SEd Maste if (remin == -1) { 107919261079SEd Maste /* Connect to dest now */ 108019261079SEd Maste conn = do_sftp_connect(thost, tuser, 108119261079SEd Maste tport, sftp_direct, 108219261079SEd Maste &remin, &remout, &do_cmd_pid); 108319261079SEd Maste if (conn == NULL) { 108419261079SEd Maste fatal("Unable to open " 108519261079SEd Maste "destination connection"); 108619261079SEd Maste } 108719261079SEd Maste debug3_f("origin in %d out %d pid %ld", 108819261079SEd Maste remin, remout, (long)do_cmd_pid); 108919261079SEd Maste } 109019261079SEd Maste /* 109119261079SEd Maste * XXX remember suser/host/sport and only 109219261079SEd Maste * reconnect if they change between arguments. 109319261079SEd Maste * would save reconnections for cases like 109419261079SEd Maste * scp -3 hosta:/foo hosta:/bar hostb: 109519261079SEd Maste */ 109619261079SEd Maste /* Connect to origin now */ 109719261079SEd Maste conn2 = do_sftp_connect(host, suser, 109819261079SEd Maste sport, sftp_direct, 109919261079SEd Maste &remin2, &remout2, &do_cmd_pid2); 110019261079SEd Maste if (conn2 == NULL) { 110119261079SEd Maste fatal("Unable to open " 110219261079SEd Maste "source connection"); 110319261079SEd Maste } 110419261079SEd Maste debug3_f("destination in %d out %d pid %ld", 110519261079SEd Maste remin2, remout2, (long)do_cmd_pid2); 110619261079SEd Maste throughlocal_sftp(conn2, conn, src, targ); 110719261079SEd Maste (void) close(remin2); 110819261079SEd Maste (void) close(remout2); 110919261079SEd Maste remin2 = remout2 = -1; 111019261079SEd Maste if (waitpid(do_cmd_pid2, &status, 0) == -1) 111119261079SEd Maste ++errs; 111219261079SEd Maste else if (!WIFEXITED(status) || 111319261079SEd Maste WEXITSTATUS(status) != 0) 111419261079SEd Maste ++errs; 111519261079SEd Maste do_cmd_pid2 = -1; 111619261079SEd Maste continue; 111719261079SEd Maste } else { 1118462c32cbSDag-Erling Smørgrav xasprintf(&bp, "%s -f %s%s", cmd, 1119462c32cbSDag-Erling Smørgrav *src == '-' ? "-- " : "", src); 112019261079SEd Maste if (do_cmd(ssh_program, host, suser, sport, 0, 112119261079SEd Maste bp, &remin, &remout, &do_cmd_pid) < 0) 11224a421b63SDag-Erling Smørgrav exit(1); 1123e4a9863fSDag-Erling Smørgrav free(bp); 1124462c32cbSDag-Erling Smørgrav xasprintf(&bp, "%s -t %s%s", cmd, 1125462c32cbSDag-Erling Smørgrav *targ == '-' ? "-- " : "", targ); 112619261079SEd Maste if (do_cmd2(thost, tuser, tport, bp, 112719261079SEd Maste remin, remout) < 0) 11284a421b63SDag-Erling Smørgrav exit(1); 1129e4a9863fSDag-Erling Smørgrav free(bp); 11304a421b63SDag-Erling Smørgrav (void) close(remin); 11314a421b63SDag-Erling Smørgrav (void) close(remout); 11324a421b63SDag-Erling Smørgrav remin = remout = -1; 113319261079SEd Maste } 113447dd1d1bSDag-Erling Smørgrav } else if (host) { /* standard remote to remote */ 113519261079SEd Maste /* 113619261079SEd Maste * Second remote user is passed to first remote side 113719261079SEd Maste * via scp command-line. Ensure it contains no obvious 113819261079SEd Maste * shell characters. 113919261079SEd Maste */ 114019261079SEd Maste if (tuser != NULL && !okname(tuser)) { 114119261079SEd Maste ++errs; 114219261079SEd Maste continue; 114319261079SEd Maste } 114447dd1d1bSDag-Erling Smørgrav if (tport != -1 && tport != SSH_DEFAULT_PORT) { 114547dd1d1bSDag-Erling Smørgrav /* This would require the remote support URIs */ 114647dd1d1bSDag-Erling Smørgrav fatal("target port not supported with two " 114719261079SEd Maste "remote hosts and the -R option"); 114847dd1d1bSDag-Erling Smørgrav } 114947dd1d1bSDag-Erling Smørgrav 1150b74df5b2SDag-Erling Smørgrav freeargs(&alist); 1151b74df5b2SDag-Erling Smørgrav addargs(&alist, "%s", ssh_program); 1152b74df5b2SDag-Erling Smørgrav addargs(&alist, "-x"); 11534a421b63SDag-Erling Smørgrav addargs(&alist, "-oClearAllForwardings=yes"); 1154b74df5b2SDag-Erling Smørgrav addargs(&alist, "-n"); 11554a421b63SDag-Erling Smørgrav for (j = 0; j < remote_remote_args.num; j++) { 11564a421b63SDag-Erling Smørgrav addargs(&alist, "%s", 11574a421b63SDag-Erling Smørgrav remote_remote_args.list[j]); 11584a421b63SDag-Erling Smørgrav } 1159b74df5b2SDag-Erling Smørgrav 116047dd1d1bSDag-Erling Smørgrav if (sport != -1) { 116147dd1d1bSDag-Erling Smørgrav addargs(&alist, "-p"); 116247dd1d1bSDag-Erling Smørgrav addargs(&alist, "%d", sport); 116347dd1d1bSDag-Erling Smørgrav } 116447dd1d1bSDag-Erling Smørgrav if (suser) { 1165b74df5b2SDag-Erling Smørgrav addargs(&alist, "-l"); 1166b74df5b2SDag-Erling Smørgrav addargs(&alist, "%s", suser); 1167b74df5b2SDag-Erling Smørgrav } 1168b15c8340SDag-Erling Smørgrav addargs(&alist, "--"); 1169b74df5b2SDag-Erling Smørgrav addargs(&alist, "%s", host); 1170b74df5b2SDag-Erling Smørgrav addargs(&alist, "%s", cmd); 1171b74df5b2SDag-Erling Smørgrav addargs(&alist, "%s", src); 1172b74df5b2SDag-Erling Smørgrav addargs(&alist, "%s%s%s:%s", 1173511b41d2SMark Murray tuser ? tuser : "", tuser ? "@" : "", 1174511b41d2SMark Murray thost, targ); 1175b74df5b2SDag-Erling Smørgrav if (do_local_cmd(&alist) != 0) 11761ec0d754SDag-Erling Smørgrav errs = 1; 1177511b41d2SMark Murray } else { /* local to remote */ 117819261079SEd Maste if (mode == MODE_SFTP) { 117919261079SEd Maste if (remin == -1) { 118019261079SEd Maste /* Connect to remote now */ 118119261079SEd Maste conn = do_sftp_connect(thost, tuser, 118219261079SEd Maste tport, sftp_direct, 118319261079SEd Maste &remin, &remout, &do_cmd_pid); 118419261079SEd Maste if (conn == NULL) { 118519261079SEd Maste fatal("Unable to open sftp " 118619261079SEd Maste "connection"); 118719261079SEd Maste } 118819261079SEd Maste } 118919261079SEd Maste 119019261079SEd Maste /* The protocol */ 119119261079SEd Maste source_sftp(1, argv[i], targ, conn); 119219261079SEd Maste continue; 119319261079SEd Maste } 119419261079SEd Maste /* SCP */ 1195511b41d2SMark Murray if (remin == -1) { 1196462c32cbSDag-Erling Smørgrav xasprintf(&bp, "%s -t %s%s", cmd, 1197462c32cbSDag-Erling Smørgrav *targ == '-' ? "-- " : "", targ); 119819261079SEd Maste if (do_cmd(ssh_program, thost, tuser, tport, 0, 119919261079SEd Maste bp, &remin, &remout, &do_cmd_pid) < 0) 1200511b41d2SMark Murray exit(1); 1201511b41d2SMark Murray if (response() < 0) 1202511b41d2SMark Murray exit(1); 1203e4a9863fSDag-Erling Smørgrav free(bp); 1204511b41d2SMark Murray } 1205511b41d2SMark Murray source(1, argv + i); 1206511b41d2SMark Murray } 1207511b41d2SMark Murray } 120847dd1d1bSDag-Erling Smørgrav out: 120919261079SEd Maste if (mode == MODE_SFTP) 121019261079SEd Maste free(conn); 121147dd1d1bSDag-Erling Smørgrav free(tuser); 121247dd1d1bSDag-Erling Smørgrav free(thost); 121347dd1d1bSDag-Erling Smørgrav free(targ); 121447dd1d1bSDag-Erling Smørgrav free(suser); 121547dd1d1bSDag-Erling Smørgrav free(host); 121647dd1d1bSDag-Erling Smørgrav free(src); 1217511b41d2SMark Murray } 1218511b41d2SMark Murray 1219511b41d2SMark Murray void 122019261079SEd Maste tolocal(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct) 1221511b41d2SMark Murray { 122247dd1d1bSDag-Erling Smørgrav char *bp, *host = NULL, *src = NULL, *suser = NULL; 1223b74df5b2SDag-Erling Smørgrav arglist alist; 122419261079SEd Maste struct sftp_conn *conn = NULL; 122547dd1d1bSDag-Erling Smørgrav int i, r, sport = -1; 1226b74df5b2SDag-Erling Smørgrav 1227b74df5b2SDag-Erling Smørgrav memset(&alist, '\0', sizeof(alist)); 1228b74df5b2SDag-Erling Smørgrav alist.list = NULL; 1229511b41d2SMark Murray 1230511b41d2SMark Murray for (i = 0; i < argc - 1; i++) { 123147dd1d1bSDag-Erling Smørgrav free(suser); 123247dd1d1bSDag-Erling Smørgrav free(host); 123347dd1d1bSDag-Erling Smørgrav free(src); 123447dd1d1bSDag-Erling Smørgrav r = parse_scp_uri(argv[i], &suser, &host, &sport, &src); 123547dd1d1bSDag-Erling Smørgrav if (r == -1) { 123647dd1d1bSDag-Erling Smørgrav fmprintf(stderr, "%s: invalid uri\n", argv[i]); 123747dd1d1bSDag-Erling Smørgrav ++errs; 123847dd1d1bSDag-Erling Smørgrav continue; 123947dd1d1bSDag-Erling Smørgrav } 124047dd1d1bSDag-Erling Smørgrav if (r != 0) 124147dd1d1bSDag-Erling Smørgrav parse_user_host_path(argv[i], &suser, &host, &src); 124247dd1d1bSDag-Erling Smørgrav if (suser != NULL && !okname(suser)) { 124347dd1d1bSDag-Erling Smørgrav ++errs; 124447dd1d1bSDag-Erling Smørgrav continue; 124547dd1d1bSDag-Erling Smørgrav } 124647dd1d1bSDag-Erling Smørgrav if (!host) { /* Local to local. */ 1247b74df5b2SDag-Erling Smørgrav freeargs(&alist); 1248b74df5b2SDag-Erling Smørgrav addargs(&alist, "%s", _PATH_CP); 1249b74df5b2SDag-Erling Smørgrav if (iamrecursive) 1250b74df5b2SDag-Erling Smørgrav addargs(&alist, "-r"); 1251b74df5b2SDag-Erling Smørgrav if (pflag) 1252b74df5b2SDag-Erling Smørgrav addargs(&alist, "-p"); 1253b15c8340SDag-Erling Smørgrav addargs(&alist, "--"); 1254b74df5b2SDag-Erling Smørgrav addargs(&alist, "%s", argv[i]); 1255b74df5b2SDag-Erling Smørgrav addargs(&alist, "%s", argv[argc-1]); 1256b74df5b2SDag-Erling Smørgrav if (do_local_cmd(&alist)) 1257511b41d2SMark Murray ++errs; 1258511b41d2SMark Murray continue; 1259511b41d2SMark Murray } 126047dd1d1bSDag-Erling Smørgrav /* Remote to local. */ 126119261079SEd Maste if (mode == MODE_SFTP) { 126219261079SEd Maste conn = do_sftp_connect(host, suser, sport, 126319261079SEd Maste sftp_direct, &remin, &remout, &do_cmd_pid); 126419261079SEd Maste if (conn == NULL) { 1265e9e8876aSEd Maste error("sftp connection failed"); 126619261079SEd Maste ++errs; 126719261079SEd Maste continue; 126819261079SEd Maste } 126919261079SEd Maste 127019261079SEd Maste /* The protocol */ 127119261079SEd Maste sink_sftp(1, argv[argc - 1], src, conn); 127219261079SEd Maste 127319261079SEd Maste free(conn); 127419261079SEd Maste (void) close(remin); 127519261079SEd Maste (void) close(remout); 127619261079SEd Maste remin = remout = -1; 127719261079SEd Maste continue; 127819261079SEd Maste } 127919261079SEd Maste /* SCP */ 1280462c32cbSDag-Erling Smørgrav xasprintf(&bp, "%s -f %s%s", 1281462c32cbSDag-Erling Smørgrav cmd, *src == '-' ? "-- " : "", src); 128219261079SEd Maste if (do_cmd(ssh_program, host, suser, sport, 0, bp, 128319261079SEd Maste &remin, &remout, &do_cmd_pid) < 0) { 1284e4a9863fSDag-Erling Smørgrav free(bp); 1285511b41d2SMark Murray ++errs; 1286511b41d2SMark Murray continue; 1287511b41d2SMark Murray } 1288e4a9863fSDag-Erling Smørgrav free(bp); 1289afde5170SEd Maste sink(1, argv + argc - 1, src); 1290511b41d2SMark Murray (void) close(remin); 1291511b41d2SMark Murray remin = remout = -1; 1292511b41d2SMark Murray } 129347dd1d1bSDag-Erling Smørgrav free(suser); 129447dd1d1bSDag-Erling Smørgrav free(host); 129547dd1d1bSDag-Erling Smørgrav free(src); 1296511b41d2SMark Murray } 1297511b41d2SMark Murray 129819261079SEd Maste /* Prepare remote path, handling ~ by assuming cwd is the homedir */ 129919261079SEd Maste static char * 130019261079SEd Maste prepare_remote_path(struct sftp_conn *conn, const char *path) 130119261079SEd Maste { 13021323ec57SEd Maste size_t nslash; 13031323ec57SEd Maste 130419261079SEd Maste /* Handle ~ prefixed paths */ 130519261079SEd Maste if (*path == '\0' || strcmp(path, "~") == 0) 130619261079SEd Maste return xstrdup("."); 13071323ec57SEd Maste if (*path != '~') 13081323ec57SEd Maste return xstrdup(path); 13091323ec57SEd Maste if (strncmp(path, "~/", 2) == 0) { 13101323ec57SEd Maste if ((nslash = strspn(path + 2, "/")) == strlen(path + 2)) 13111323ec57SEd Maste return xstrdup("."); 13121323ec57SEd Maste return xstrdup(path + 2 + nslash); 13131323ec57SEd Maste } 131419261079SEd Maste if (can_expand_path(conn)) 131519261079SEd Maste return do_expand_path(conn, path); 131619261079SEd Maste /* No protocol extension */ 1317e9e8876aSEd Maste error("server expand-path extension is required " 1318e9e8876aSEd Maste "for ~user paths in SFTP mode"); 131919261079SEd Maste return NULL; 132019261079SEd Maste } 132119261079SEd Maste 132219261079SEd Maste void 132319261079SEd Maste source_sftp(int argc, char *src, char *targ, struct sftp_conn *conn) 132419261079SEd Maste { 132519261079SEd Maste char *target = NULL, *filename = NULL, *abs_dst = NULL; 13261323ec57SEd Maste int src_is_dir, target_is_dir; 13271323ec57SEd Maste Attrib a; 13281323ec57SEd Maste struct stat st; 132919261079SEd Maste 13301323ec57SEd Maste memset(&a, '\0', sizeof(a)); 13311323ec57SEd Maste if (stat(src, &st) != 0) 13321323ec57SEd Maste fatal("stat local \"%s\": %s", src, strerror(errno)); 13331323ec57SEd Maste src_is_dir = S_ISDIR(st.st_mode); 133419261079SEd Maste if ((filename = basename(src)) == NULL) 13351323ec57SEd Maste fatal("basename \"%s\": %s", src, strerror(errno)); 133619261079SEd Maste 133719261079SEd Maste /* 133819261079SEd Maste * No need to glob here - the local shell already took care of 133919261079SEd Maste * the expansions 134019261079SEd Maste */ 134119261079SEd Maste if ((target = prepare_remote_path(conn, targ)) == NULL) 134219261079SEd Maste cleanup_exit(255); 134319261079SEd Maste target_is_dir = remote_is_dir(conn, target); 134419261079SEd Maste if (targetshouldbedirectory && !target_is_dir) { 13451323ec57SEd Maste debug("target directory \"%s\" does not exist", target); 13461323ec57SEd Maste a.flags = SSH2_FILEXFER_ATTR_PERMISSIONS; 13471323ec57SEd Maste a.perm = st.st_mode | 0700; /* ensure writable */ 13481323ec57SEd Maste if (do_mkdir(conn, target, &a, 1) != 0) 13491323ec57SEd Maste cleanup_exit(255); /* error already logged */ 13501323ec57SEd Maste target_is_dir = 1; 135119261079SEd Maste } 135219261079SEd Maste if (target_is_dir) 135319261079SEd Maste abs_dst = path_append(target, filename); 135419261079SEd Maste else { 135519261079SEd Maste abs_dst = target; 135619261079SEd Maste target = NULL; 135719261079SEd Maste } 135819261079SEd Maste debug3_f("copying local %s to remote %s", src, abs_dst); 135919261079SEd Maste 13601323ec57SEd Maste if (src_is_dir && iamrecursive) { 136119261079SEd Maste if (upload_dir(conn, src, abs_dst, pflag, 136238a52bd3SEd Maste SFTP_PROGRESS_ONLY, 0, 0, 1, 1) != 0) { 13631323ec57SEd Maste error("failed to upload directory %s to %s", src, targ); 1364e9e8876aSEd Maste errs = 1; 136519261079SEd Maste } 136638a52bd3SEd Maste } else if (do_upload(conn, src, abs_dst, pflag, 0, 0, 1) != 0) { 13671323ec57SEd Maste error("failed to upload file %s to %s", src, targ); 1368e9e8876aSEd Maste errs = 1; 1369e9e8876aSEd Maste } 137019261079SEd Maste 137119261079SEd Maste free(abs_dst); 137219261079SEd Maste free(target); 137319261079SEd Maste } 137419261079SEd Maste 1375511b41d2SMark Murray void 1376cf2b5f3bSDag-Erling Smørgrav source(int argc, char **argv) 1377511b41d2SMark Murray { 1378511b41d2SMark Murray struct stat stb; 1379511b41d2SMark Murray static BUF buffer; 1380511b41d2SMark Murray BUF *bp; 1381d4af9e69SDag-Erling Smørgrav off_t i, statbytes; 1382a0ee8cc6SDag-Erling Smørgrav size_t amt, nr; 1383d4ecd108SDag-Erling Smørgrav int fd = -1, haderr, indx; 138419261079SEd Maste char *last, *name, buf[PATH_MAX + 128], encname[PATH_MAX]; 13851e8db6e2SBrian Feldman int len; 1386511b41d2SMark Murray 1387511b41d2SMark Murray for (indx = 0; indx < argc; ++indx) { 1388511b41d2SMark Murray name = argv[indx]; 1389511b41d2SMark Murray statbytes = 0; 13901e8db6e2SBrian Feldman len = strlen(name); 13911e8db6e2SBrian Feldman while (len > 1 && name[len-1] == '/') 13921e8db6e2SBrian Feldman name[--len] = '\0'; 13931323ec57SEd Maste if ((fd = open(name, O_RDONLY|O_NONBLOCK)) == -1) 1394511b41d2SMark Murray goto syserr; 1395d4af9e69SDag-Erling Smørgrav if (strchr(name, '\n') != NULL) { 1396d4af9e69SDag-Erling Smørgrav strnvis(encname, name, sizeof(encname), VIS_NL); 1397d4af9e69SDag-Erling Smørgrav name = encname; 1398d4af9e69SDag-Erling Smørgrav } 139919261079SEd Maste if (fstat(fd, &stb) == -1) { 1400511b41d2SMark Murray syserr: run_err("%s: %s", name, strerror(errno)); 1401511b41d2SMark Murray goto next; 1402511b41d2SMark Murray } 1403d4af9e69SDag-Erling Smørgrav if (stb.st_size < 0) { 1404d4af9e69SDag-Erling Smørgrav run_err("%s: %s", name, "Negative file size"); 1405d4af9e69SDag-Erling Smørgrav goto next; 1406d4af9e69SDag-Erling Smørgrav } 1407d4af9e69SDag-Erling Smørgrav unset_nonblock(fd); 1408511b41d2SMark Murray switch (stb.st_mode & S_IFMT) { 1409511b41d2SMark Murray case S_IFREG: 1410511b41d2SMark Murray break; 1411511b41d2SMark Murray case S_IFDIR: 1412511b41d2SMark Murray if (iamrecursive) { 1413511b41d2SMark Murray rsource(name, &stb); 1414511b41d2SMark Murray goto next; 1415511b41d2SMark Murray } 1416511b41d2SMark Murray /* FALLTHROUGH */ 1417511b41d2SMark Murray default: 1418511b41d2SMark Murray run_err("%s: not a regular file", name); 1419511b41d2SMark Murray goto next; 1420511b41d2SMark Murray } 1421511b41d2SMark Murray if ((last = strrchr(name, '/')) == NULL) 1422511b41d2SMark Murray last = name; 1423511b41d2SMark Murray else 1424511b41d2SMark Murray ++last; 1425511b41d2SMark Murray curfile = last; 1426511b41d2SMark Murray if (pflag) { 1427e4a9863fSDag-Erling Smørgrav if (do_times(remout, verbose_mode, &stb) < 0) 1428511b41d2SMark Murray goto next; 1429511b41d2SMark Murray } 1430511b41d2SMark Murray #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) 14311e8db6e2SBrian Feldman snprintf(buf, sizeof buf, "C%04o %lld %s\n", 14321e8db6e2SBrian Feldman (u_int) (stb.st_mode & FILEMODEMASK), 1433b74df5b2SDag-Erling Smørgrav (long long)stb.st_size, last); 1434076ad2f8SDag-Erling Smørgrav if (verbose_mode) 1435076ad2f8SDag-Erling Smørgrav fmprintf(stderr, "Sending file modes: %s", buf); 1436cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, buf, strlen(buf)); 1437511b41d2SMark Murray if (response() < 0) 1438511b41d2SMark Murray goto next; 1439d4af9e69SDag-Erling Smørgrav if ((bp = allocbuf(&buffer, fd, COPY_BUFLEN)) == NULL) { 1440b74df5b2SDag-Erling Smørgrav next: if (fd != -1) { 1441b74df5b2SDag-Erling Smørgrav (void) close(fd); 1442b74df5b2SDag-Erling Smørgrav fd = -1; 1443b74df5b2SDag-Erling Smørgrav } 1444511b41d2SMark Murray continue; 1445511b41d2SMark Murray } 1446e73e9afaSDag-Erling Smørgrav if (showprogress) 1447e73e9afaSDag-Erling Smørgrav start_progress_meter(curfile, stb.st_size, &statbytes); 1448d4af9e69SDag-Erling Smørgrav set_nonblock(remout); 1449511b41d2SMark Murray for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 1450511b41d2SMark Murray amt = bp->cnt; 1451d4af9e69SDag-Erling Smørgrav if (i + (off_t)amt > stb.st_size) 1452511b41d2SMark Murray amt = stb.st_size - i; 1453511b41d2SMark Murray if (!haderr) { 1454a0ee8cc6SDag-Erling Smørgrav if ((nr = atomicio(read, fd, 1455a0ee8cc6SDag-Erling Smørgrav bp->buf, amt)) != amt) { 1456d4ecd108SDag-Erling Smørgrav haderr = errno; 1457a0ee8cc6SDag-Erling Smørgrav memset(bp->buf + nr, 0, amt - nr); 1458a0ee8cc6SDag-Erling Smørgrav } 1459511b41d2SMark Murray } 1460d4af9e69SDag-Erling Smørgrav /* Keep writing after error to retain sync */ 1461d4af9e69SDag-Erling Smørgrav if (haderr) { 1462cf2b5f3bSDag-Erling Smørgrav (void)atomicio(vwrite, remout, bp->buf, amt); 1463a0ee8cc6SDag-Erling Smørgrav memset(bp->buf, 0, amt); 1464d4af9e69SDag-Erling Smørgrav continue; 1465d4af9e69SDag-Erling Smørgrav } 14664a421b63SDag-Erling Smørgrav if (atomicio6(vwrite, remout, bp->buf, amt, scpio, 1467d4af9e69SDag-Erling Smørgrav &statbytes) != amt) 1468d4ecd108SDag-Erling Smørgrav haderr = errno; 1469511b41d2SMark Murray } 1470d4af9e69SDag-Erling Smørgrav unset_nonblock(remout); 1471511b41d2SMark Murray 1472b74df5b2SDag-Erling Smørgrav if (fd != -1) { 147319261079SEd Maste if (close(fd) == -1 && !haderr) 1474511b41d2SMark Murray haderr = errno; 1475b74df5b2SDag-Erling Smørgrav fd = -1; 1476b74df5b2SDag-Erling Smørgrav } 1477511b41d2SMark Murray if (!haderr) 1478cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 1479511b41d2SMark Murray else 1480511b41d2SMark Murray run_err("%s: %s", name, strerror(haderr)); 1481511b41d2SMark Murray (void) response(); 1482076ad2f8SDag-Erling Smørgrav if (showprogress) 1483076ad2f8SDag-Erling Smørgrav stop_progress_meter(); 1484511b41d2SMark Murray } 1485511b41d2SMark Murray } 1486511b41d2SMark Murray 1487511b41d2SMark Murray void 1488cf2b5f3bSDag-Erling Smørgrav rsource(char *name, struct stat *statp) 1489511b41d2SMark Murray { 1490511b41d2SMark Murray DIR *dirp; 1491511b41d2SMark Murray struct dirent *dp; 1492bc5531deSDag-Erling Smørgrav char *last, *vect[1], path[PATH_MAX]; 1493511b41d2SMark Murray 1494511b41d2SMark Murray if (!(dirp = opendir(name))) { 1495511b41d2SMark Murray run_err("%s: %s", name, strerror(errno)); 1496511b41d2SMark Murray return; 1497511b41d2SMark Murray } 1498511b41d2SMark Murray last = strrchr(name, '/'); 1499acc1a9efSDag-Erling Smørgrav if (last == NULL) 1500511b41d2SMark Murray last = name; 1501511b41d2SMark Murray else 1502511b41d2SMark Murray last++; 1503511b41d2SMark Murray if (pflag) { 1504e4a9863fSDag-Erling Smørgrav if (do_times(remout, verbose_mode, statp) < 0) { 1505511b41d2SMark Murray closedir(dirp); 1506511b41d2SMark Murray return; 1507511b41d2SMark Murray } 1508511b41d2SMark Murray } 15091e8db6e2SBrian Feldman (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n", 15101e8db6e2SBrian Feldman (u_int) (statp->st_mode & FILEMODEMASK), 0, last); 1511511b41d2SMark Murray if (verbose_mode) 1512076ad2f8SDag-Erling Smørgrav fmprintf(stderr, "Entering directory: %s", path); 1513cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, path, strlen(path)); 1514511b41d2SMark Murray if (response() < 0) { 1515511b41d2SMark Murray closedir(dirp); 1516511b41d2SMark Murray return; 1517511b41d2SMark Murray } 15181e8db6e2SBrian Feldman while ((dp = readdir(dirp)) != NULL) { 1519511b41d2SMark Murray if (dp->d_ino == 0) 1520511b41d2SMark Murray continue; 1521511b41d2SMark Murray if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 1522511b41d2SMark Murray continue; 1523511b41d2SMark Murray if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) { 1524511b41d2SMark Murray run_err("%s/%s: name too long", name, dp->d_name); 1525511b41d2SMark Murray continue; 1526511b41d2SMark Murray } 15271e8db6e2SBrian Feldman (void) snprintf(path, sizeof path, "%s/%s", name, dp->d_name); 1528511b41d2SMark Murray vect[0] = path; 1529511b41d2SMark Murray source(1, vect); 1530511b41d2SMark Murray } 1531511b41d2SMark Murray (void) closedir(dirp); 1532cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "E\n", 2); 1533511b41d2SMark Murray (void) response(); 1534511b41d2SMark Murray } 1535511b41d2SMark Murray 153619261079SEd Maste void 153719261079SEd Maste sink_sftp(int argc, char *dst, const char *src, struct sftp_conn *conn) 153819261079SEd Maste { 153919261079SEd Maste char *abs_src = NULL; 154019261079SEd Maste char *abs_dst = NULL; 154119261079SEd Maste glob_t g; 154219261079SEd Maste char *filename, *tmp = NULL; 15431323ec57SEd Maste int i, r, err = 0, dst_is_dir; 15441323ec57SEd Maste struct stat st; 154519261079SEd Maste 154619261079SEd Maste memset(&g, 0, sizeof(g)); 15471323ec57SEd Maste 154819261079SEd Maste /* 154919261079SEd Maste * Here, we need remote glob as SFTP can not depend on remote shell 155019261079SEd Maste * expansions 155119261079SEd Maste */ 155219261079SEd Maste if ((abs_src = prepare_remote_path(conn, src)) == NULL) { 155319261079SEd Maste err = -1; 155419261079SEd Maste goto out; 155519261079SEd Maste } 155619261079SEd Maste 155719261079SEd Maste debug3_f("copying remote %s to local %s", abs_src, dst); 1558f374ba41SEd Maste if ((r = remote_glob(conn, abs_src, GLOB_NOCHECK|GLOB_MARK, 1559f374ba41SEd Maste NULL, &g)) != 0) { 156019261079SEd Maste if (r == GLOB_NOSPACE) 15611323ec57SEd Maste error("%s: too many glob matches", src); 156219261079SEd Maste else 15631323ec57SEd Maste error("%s: %s", src, strerror(ENOENT)); 156419261079SEd Maste err = -1; 156519261079SEd Maste goto out; 156619261079SEd Maste } 156719261079SEd Maste 1568f374ba41SEd Maste /* Did we actually get any matches back from the glob? */ 1569f374ba41SEd Maste if (g.gl_matchc == 0 && g.gl_pathc == 1 && g.gl_pathv[0] != 0) { 1570f374ba41SEd Maste /* 1571f374ba41SEd Maste * If nothing matched but a path returned, then it's probably 1572f374ba41SEd Maste * a GLOB_NOCHECK result. Check whether the unglobbed path 1573f374ba41SEd Maste * exists so we can give a nice error message early. 1574f374ba41SEd Maste */ 1575f374ba41SEd Maste if (do_stat(conn, g.gl_pathv[0], 1) == NULL) { 1576f374ba41SEd Maste error("%s: %s", src, strerror(ENOENT)); 1577f374ba41SEd Maste err = -1; 1578f374ba41SEd Maste goto out; 1579f374ba41SEd Maste } 1580f374ba41SEd Maste } 1581f374ba41SEd Maste 15821323ec57SEd Maste if ((r = stat(dst, &st)) != 0) 15831323ec57SEd Maste debug2_f("stat local \"%s\": %s", dst, strerror(errno)); 15841323ec57SEd Maste dst_is_dir = r == 0 && S_ISDIR(st.st_mode); 15851323ec57SEd Maste 15861323ec57SEd Maste if (g.gl_matchc > 1 && !dst_is_dir) { 15871323ec57SEd Maste if (r == 0) { 158819261079SEd Maste error("Multiple files match pattern, but destination " 158919261079SEd Maste "\"%s\" is not a directory", dst); 159019261079SEd Maste err = -1; 159119261079SEd Maste goto out; 159219261079SEd Maste } 15931323ec57SEd Maste debug2_f("creating destination \"%s\"", dst); 15941323ec57SEd Maste if (mkdir(dst, 0777) != 0) { 15951323ec57SEd Maste error("local mkdir \"%s\": %s", dst, strerror(errno)); 15961323ec57SEd Maste err = -1; 15971323ec57SEd Maste goto out; 15981323ec57SEd Maste } 15991323ec57SEd Maste dst_is_dir = 1; 16001323ec57SEd Maste } 160119261079SEd Maste 160219261079SEd Maste for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 160319261079SEd Maste tmp = xstrdup(g.gl_pathv[i]); 160419261079SEd Maste if ((filename = basename(tmp)) == NULL) { 160519261079SEd Maste error("basename %s: %s", tmp, strerror(errno)); 160619261079SEd Maste err = -1; 160719261079SEd Maste goto out; 160819261079SEd Maste } 160919261079SEd Maste 16101323ec57SEd Maste if (dst_is_dir) 161119261079SEd Maste abs_dst = path_append(dst, filename); 161219261079SEd Maste else 161319261079SEd Maste abs_dst = xstrdup(dst); 161419261079SEd Maste 161519261079SEd Maste debug("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); 161619261079SEd Maste if (globpath_is_dir(g.gl_pathv[i]) && iamrecursive) { 161719261079SEd Maste if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, 161838a52bd3SEd Maste pflag, SFTP_PROGRESS_ONLY, 0, 0, 1, 1) == -1) 161919261079SEd Maste err = -1; 162019261079SEd Maste } else { 162119261079SEd Maste if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, 162238a52bd3SEd Maste pflag, 0, 0, 1) == -1) 162319261079SEd Maste err = -1; 162419261079SEd Maste } 162519261079SEd Maste free(abs_dst); 162619261079SEd Maste abs_dst = NULL; 162719261079SEd Maste free(tmp); 162819261079SEd Maste tmp = NULL; 162919261079SEd Maste } 163019261079SEd Maste 163119261079SEd Maste out: 163219261079SEd Maste free(abs_src); 163319261079SEd Maste free(tmp); 163419261079SEd Maste globfree(&g); 1635e9e8876aSEd Maste if (err == -1) 1636e9e8876aSEd Maste errs = 1; 163719261079SEd Maste } 163819261079SEd Maste 163919261079SEd Maste 16404f52dfbbSDag-Erling Smørgrav #define TYPE_OVERFLOW(type, val) \ 16414f52dfbbSDag-Erling Smørgrav ((sizeof(type) == 4 && (val) > INT32_MAX) || \ 16424f52dfbbSDag-Erling Smørgrav (sizeof(type) == 8 && (val) > INT64_MAX) || \ 16434f52dfbbSDag-Erling Smørgrav (sizeof(type) != 4 && sizeof(type) != 8)) 16444f52dfbbSDag-Erling Smørgrav 1645511b41d2SMark Murray void 1646afde5170SEd Maste sink(int argc, char **argv, const char *src) 1647511b41d2SMark Murray { 1648511b41d2SMark Murray static BUF buffer; 1649511b41d2SMark Murray struct stat stb; 1650511b41d2SMark Murray BUF *bp; 1651d4ecd108SDag-Erling Smørgrav off_t i; 1652d4ecd108SDag-Erling Smørgrav size_t j, count; 1653333ee039SDag-Erling Smørgrav int amt, exists, first, ofd; 1654333ee039SDag-Erling Smørgrav mode_t mode, omode, mask; 1655e73e9afaSDag-Erling Smørgrav off_t size, statbytes; 1656e4a9863fSDag-Erling Smørgrav unsigned long long ull; 165719261079SEd Maste int setimes, targisdir, wrerr; 1658076ad2f8SDag-Erling Smørgrav char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048]; 16590967215dSEd Maste char **patterns = NULL; 16600967215dSEd Maste size_t n, npatterns = 0; 16615b9b2fafSBrian Feldman struct timeval tv[2]; 1662511b41d2SMark Murray 16631e8db6e2SBrian Feldman #define atime tv[0] 16641e8db6e2SBrian Feldman #define mtime tv[1] 1665aa49c926SDag-Erling Smørgrav #define SCREWUP(str) { why = str; goto screwup; } 1666511b41d2SMark Murray 16674f52dfbbSDag-Erling Smørgrav if (TYPE_OVERFLOW(time_t, 0) || TYPE_OVERFLOW(off_t, 0)) 16684f52dfbbSDag-Erling Smørgrav SCREWUP("Unexpected off_t/time_t size"); 16694f52dfbbSDag-Erling Smørgrav 1670511b41d2SMark Murray setimes = targisdir = 0; 1671511b41d2SMark Murray mask = umask(0); 1672511b41d2SMark Murray if (!pflag) 1673511b41d2SMark Murray (void) umask(mask); 1674511b41d2SMark Murray if (argc != 1) { 1675511b41d2SMark Murray run_err("ambiguous target"); 1676511b41d2SMark Murray exit(1); 1677511b41d2SMark Murray } 1678511b41d2SMark Murray targ = *argv; 1679511b41d2SMark Murray if (targetshouldbedirectory) 1680511b41d2SMark Murray verifydir(targ); 1681511b41d2SMark Murray 1682cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 1683511b41d2SMark Murray if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 1684511b41d2SMark Murray targisdir = 1; 1685afde5170SEd Maste if (src != NULL && !iamrecursive && !Tflag) { 1686afde5170SEd Maste /* 1687afde5170SEd Maste * Prepare to try to restrict incoming filenames to match 1688afde5170SEd Maste * the requested destination file glob. 1689afde5170SEd Maste */ 16900967215dSEd Maste if (brace_expand(src, &patterns, &npatterns) != 0) 169119261079SEd Maste fatal_f("could not expand pattern"); 1692afde5170SEd Maste } 1693511b41d2SMark Murray for (first = 1;; first = 0) { 1694511b41d2SMark Murray cp = buf; 1695d4ecd108SDag-Erling Smørgrav if (atomicio(read, remin, cp, 1) != 1) 16960967215dSEd Maste goto done; 1697511b41d2SMark Murray if (*cp++ == '\n') 1698511b41d2SMark Murray SCREWUP("unexpected <newline>"); 1699511b41d2SMark Murray do { 1700a04a10f8SKris Kennaway if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) 1701511b41d2SMark Murray SCREWUP("lost connection"); 1702511b41d2SMark Murray *cp++ = ch; 1703511b41d2SMark Murray } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); 1704511b41d2SMark Murray *cp = 0; 170521e764dfSDag-Erling Smørgrav if (verbose_mode) 1706076ad2f8SDag-Erling Smørgrav fmprintf(stderr, "Sink: %s", buf); 1707511b41d2SMark Murray 1708511b41d2SMark Murray if (buf[0] == '\01' || buf[0] == '\02') { 1709076ad2f8SDag-Erling Smørgrav if (iamremote == 0) { 1710076ad2f8SDag-Erling Smørgrav (void) snmprintf(visbuf, sizeof(visbuf), 1711076ad2f8SDag-Erling Smørgrav NULL, "%s", buf + 1); 1712cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, STDERR_FILENO, 1713076ad2f8SDag-Erling Smørgrav visbuf, strlen(visbuf)); 1714076ad2f8SDag-Erling Smørgrav } 1715511b41d2SMark Murray if (buf[0] == '\02') 1716511b41d2SMark Murray exit(1); 1717511b41d2SMark Murray ++errs; 1718511b41d2SMark Murray continue; 1719511b41d2SMark Murray } 1720511b41d2SMark Murray if (buf[0] == 'E') { 1721cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 17220967215dSEd Maste goto done; 1723511b41d2SMark Murray } 1724511b41d2SMark Murray if (ch == '\n') 1725511b41d2SMark Murray *--cp = 0; 1726511b41d2SMark Murray 1727511b41d2SMark Murray cp = buf; 1728511b41d2SMark Murray if (*cp == 'T') { 1729511b41d2SMark Murray setimes++; 1730511b41d2SMark Murray cp++; 1731e4a9863fSDag-Erling Smørgrav if (!isdigit((unsigned char)*cp)) 1732e4a9863fSDag-Erling Smørgrav SCREWUP("mtime.sec not present"); 1733e4a9863fSDag-Erling Smørgrav ull = strtoull(cp, &cp, 10); 17341e8db6e2SBrian Feldman if (!cp || *cp++ != ' ') 1735511b41d2SMark Murray SCREWUP("mtime.sec not delimited"); 17364f52dfbbSDag-Erling Smørgrav if (TYPE_OVERFLOW(time_t, ull)) 1737e4a9863fSDag-Erling Smørgrav setimes = 0; /* out of range */ 1738e4a9863fSDag-Erling Smørgrav mtime.tv_sec = ull; 17391e8db6e2SBrian Feldman mtime.tv_usec = strtol(cp, &cp, 10); 1740e4a9863fSDag-Erling Smørgrav if (!cp || *cp++ != ' ' || mtime.tv_usec < 0 || 1741e4a9863fSDag-Erling Smørgrav mtime.tv_usec > 999999) 1742511b41d2SMark Murray SCREWUP("mtime.usec not delimited"); 1743e4a9863fSDag-Erling Smørgrav if (!isdigit((unsigned char)*cp)) 1744e4a9863fSDag-Erling Smørgrav SCREWUP("atime.sec not present"); 1745e4a9863fSDag-Erling Smørgrav ull = strtoull(cp, &cp, 10); 17461e8db6e2SBrian Feldman if (!cp || *cp++ != ' ') 1747511b41d2SMark Murray SCREWUP("atime.sec not delimited"); 17484f52dfbbSDag-Erling Smørgrav if (TYPE_OVERFLOW(time_t, ull)) 1749e4a9863fSDag-Erling Smørgrav setimes = 0; /* out of range */ 1750e4a9863fSDag-Erling Smørgrav atime.tv_sec = ull; 17511e8db6e2SBrian Feldman atime.tv_usec = strtol(cp, &cp, 10); 1752e4a9863fSDag-Erling Smørgrav if (!cp || *cp++ != '\0' || atime.tv_usec < 0 || 1753e4a9863fSDag-Erling Smørgrav atime.tv_usec > 999999) 1754511b41d2SMark Murray SCREWUP("atime.usec not delimited"); 1755cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 1756511b41d2SMark Murray continue; 1757511b41d2SMark Murray } 1758511b41d2SMark Murray if (*cp != 'C' && *cp != 'D') { 1759511b41d2SMark Murray /* 1760511b41d2SMark Murray * Check for the case "rcp remote:foo\* local:bar". 1761511b41d2SMark Murray * In this case, the line "No match." can be returned 1762511b41d2SMark Murray * by the shell before the rcp command on the remote is 1763511b41d2SMark Murray * executed so the ^Aerror_message convention isn't 1764511b41d2SMark Murray * followed. 1765511b41d2SMark Murray */ 1766511b41d2SMark Murray if (first) { 1767511b41d2SMark Murray run_err("%s", cp); 1768511b41d2SMark Murray exit(1); 1769511b41d2SMark Murray } 1770511b41d2SMark Murray SCREWUP("expected control record"); 1771511b41d2SMark Murray } 1772511b41d2SMark Murray mode = 0; 1773511b41d2SMark Murray for (++cp; cp < buf + 5; cp++) { 1774511b41d2SMark Murray if (*cp < '0' || *cp > '7') 1775511b41d2SMark Murray SCREWUP("bad mode"); 1776511b41d2SMark Murray mode = (mode << 3) | (*cp - '0'); 1777511b41d2SMark Murray } 1778190cef3dSDag-Erling Smørgrav if (!pflag) 1779190cef3dSDag-Erling Smørgrav mode &= ~mask; 1780511b41d2SMark Murray if (*cp++ != ' ') 1781511b41d2SMark Murray SCREWUP("mode not delimited"); 1782511b41d2SMark Murray 17834f52dfbbSDag-Erling Smørgrav if (!isdigit((unsigned char)*cp)) 17844f52dfbbSDag-Erling Smørgrav SCREWUP("size not present"); 17854f52dfbbSDag-Erling Smørgrav ull = strtoull(cp, &cp, 10); 17864f52dfbbSDag-Erling Smørgrav if (!cp || *cp++ != ' ') 1787511b41d2SMark Murray SCREWUP("size not delimited"); 17884f52dfbbSDag-Erling Smørgrav if (TYPE_OVERFLOW(off_t, ull)) 17894f52dfbbSDag-Erling Smørgrav SCREWUP("size out of range"); 17904f52dfbbSDag-Erling Smørgrav size = (off_t)ull; 17914f52dfbbSDag-Erling Smørgrav 1792d366f891SEd Maste if (*cp == '\0' || strchr(cp, '/') != NULL || 1793d366f891SEd Maste strcmp(cp, ".") == 0 || strcmp(cp, "..") == 0) { 179421e764dfSDag-Erling Smørgrav run_err("error: unexpected filename: %s", cp); 179521e764dfSDag-Erling Smørgrav exit(1); 179621e764dfSDag-Erling Smørgrav } 17970967215dSEd Maste if (npatterns > 0) { 17980967215dSEd Maste for (n = 0; n < npatterns; n++) { 1799f374ba41SEd Maste if (strcmp(patterns[n], cp) == 0 || 1800f374ba41SEd Maste fnmatch(patterns[n], cp, 0) == 0) 18010967215dSEd Maste break; 18020967215dSEd Maste } 18030967215dSEd Maste if (n >= npatterns) 1804afde5170SEd Maste SCREWUP("filename does not match request"); 18050967215dSEd Maste } 1806511b41d2SMark Murray if (targisdir) { 1807511b41d2SMark Murray static char *namebuf; 1808d4ecd108SDag-Erling Smørgrav static size_t cursize; 1809511b41d2SMark Murray size_t need; 1810511b41d2SMark Murray 1811511b41d2SMark Murray need = strlen(targ) + strlen(cp) + 250; 18121e8db6e2SBrian Feldman if (need > cursize) { 1813e4a9863fSDag-Erling Smørgrav free(namebuf); 1814511b41d2SMark Murray namebuf = xmalloc(need); 18151e8db6e2SBrian Feldman cursize = need; 18161e8db6e2SBrian Feldman } 18171e8db6e2SBrian Feldman (void) snprintf(namebuf, need, "%s%s%s", targ, 1818545d5ecaSDag-Erling Smørgrav strcmp(targ, "/") ? "/" : "", cp); 1819511b41d2SMark Murray np = namebuf; 1820511b41d2SMark Murray } else 1821511b41d2SMark Murray np = targ; 1822511b41d2SMark Murray curfile = cp; 1823511b41d2SMark Murray exists = stat(np, &stb) == 0; 1824511b41d2SMark Murray if (buf[0] == 'D') { 1825511b41d2SMark Murray int mod_flag = pflag; 182621e764dfSDag-Erling Smørgrav if (!iamrecursive) 182721e764dfSDag-Erling Smørgrav SCREWUP("received directory without -r"); 1828511b41d2SMark Murray if (exists) { 1829511b41d2SMark Murray if (!S_ISDIR(stb.st_mode)) { 1830511b41d2SMark Murray errno = ENOTDIR; 1831511b41d2SMark Murray goto bad; 1832511b41d2SMark Murray } 1833511b41d2SMark Murray if (pflag) 1834511b41d2SMark Murray (void) chmod(np, mode); 1835511b41d2SMark Murray } else { 183619261079SEd Maste /* Handle copying from a read-only directory */ 1837511b41d2SMark Murray mod_flag = 1; 183819261079SEd Maste if (mkdir(np, mode | S_IRWXU) == -1) 1839511b41d2SMark Murray goto bad; 1840511b41d2SMark Murray } 18411e8db6e2SBrian Feldman vect[0] = xstrdup(np); 1842afde5170SEd Maste sink(1, vect, src); 1843511b41d2SMark Murray if (setimes) { 1844511b41d2SMark Murray setimes = 0; 184519261079SEd Maste (void) utimes(vect[0], tv); 1846511b41d2SMark Murray } 1847511b41d2SMark Murray if (mod_flag) 18481e8db6e2SBrian Feldman (void) chmod(vect[0], mode); 1849e4a9863fSDag-Erling Smørgrav free(vect[0]); 1850511b41d2SMark Murray continue; 1851511b41d2SMark Murray } 1852511b41d2SMark Murray omode = mode; 1853e4a9863fSDag-Erling Smørgrav mode |= S_IWUSR; 185419261079SEd Maste if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) == -1) { 1855511b41d2SMark Murray bad: run_err("%s: %s", np, strerror(errno)); 1856511b41d2SMark Murray continue; 1857511b41d2SMark Murray } 1858cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 1859d4af9e69SDag-Erling Smørgrav if ((bp = allocbuf(&buffer, ofd, COPY_BUFLEN)) == NULL) { 1860511b41d2SMark Murray (void) close(ofd); 1861511b41d2SMark Murray continue; 1862511b41d2SMark Murray } 1863511b41d2SMark Murray cp = bp->buf; 186419261079SEd Maste wrerr = 0; 1865511b41d2SMark Murray 186619261079SEd Maste /* 186719261079SEd Maste * NB. do not use run_err() unless immediately followed by 186819261079SEd Maste * exit() below as it may send a spurious reply that might 186919261079SEd Maste * desyncronise us from the peer. Use note_err() instead. 187019261079SEd Maste */ 1871511b41d2SMark Murray statbytes = 0; 1872e73e9afaSDag-Erling Smørgrav if (showprogress) 1873e73e9afaSDag-Erling Smørgrav start_progress_meter(curfile, size, &statbytes); 1874d4af9e69SDag-Erling Smørgrav set_nonblock(remin); 1875d4af9e69SDag-Erling Smørgrav for (count = i = 0; i < size; i += bp->cnt) { 1876d4af9e69SDag-Erling Smørgrav amt = bp->cnt; 1877511b41d2SMark Murray if (i + amt > size) 1878511b41d2SMark Murray amt = size - i; 1879511b41d2SMark Murray count += amt; 1880511b41d2SMark Murray do { 18814a421b63SDag-Erling Smørgrav j = atomicio6(read, remin, cp, amt, 18824a421b63SDag-Erling Smørgrav scpio, &statbytes); 1883d4ecd108SDag-Erling Smørgrav if (j == 0) { 1884d4af9e69SDag-Erling Smørgrav run_err("%s", j != EPIPE ? 1885d4af9e69SDag-Erling Smørgrav strerror(errno) : 1886511b41d2SMark Murray "dropped connection"); 1887511b41d2SMark Murray exit(1); 1888511b41d2SMark Murray } 1889511b41d2SMark Murray amt -= j; 1890511b41d2SMark Murray cp += j; 1891511b41d2SMark Murray } while (amt > 0); 1892e73e9afaSDag-Erling Smørgrav 1893511b41d2SMark Murray if (count == bp->cnt) { 1894511b41d2SMark Murray /* Keep reading so we stay sync'd up. */ 189519261079SEd Maste if (!wrerr) { 1896d4ecd108SDag-Erling Smørgrav if (atomicio(vwrite, ofd, bp->buf, 1897d4ecd108SDag-Erling Smørgrav count) != count) { 189819261079SEd Maste note_err("%s: %s", np, 189919261079SEd Maste strerror(errno)); 190019261079SEd Maste wrerr = 1; 1901511b41d2SMark Murray } 1902511b41d2SMark Murray } 1903511b41d2SMark Murray count = 0; 1904511b41d2SMark Murray cp = bp->buf; 1905511b41d2SMark Murray } 1906511b41d2SMark Murray } 1907d4af9e69SDag-Erling Smørgrav unset_nonblock(remin); 190819261079SEd Maste if (count != 0 && !wrerr && 1909d4ecd108SDag-Erling Smørgrav atomicio(vwrite, ofd, bp->buf, count) != count) { 191019261079SEd Maste note_err("%s: %s", np, strerror(errno)); 191119261079SEd Maste wrerr = 1; 1912511b41d2SMark Murray } 191319261079SEd Maste if (!wrerr && (!exists || S_ISREG(stb.st_mode)) && 191419261079SEd Maste ftruncate(ofd, size) != 0) 191519261079SEd Maste note_err("%s: truncate: %s", np, strerror(errno)); 1916511b41d2SMark Murray if (pflag) { 1917511b41d2SMark Murray if (exists || omode != mode) 191883d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD 191921e764dfSDag-Erling Smørgrav if (fchmod(ofd, omode)) { 192083d2307dSDag-Erling Smørgrav #else /* HAVE_FCHMOD */ 192121e764dfSDag-Erling Smørgrav if (chmod(np, omode)) { 192283d2307dSDag-Erling Smørgrav #endif /* HAVE_FCHMOD */ 192319261079SEd Maste note_err("%s: set mode: %s", 1924511b41d2SMark Murray np, strerror(errno)); 192521e764dfSDag-Erling Smørgrav } 1926511b41d2SMark Murray } else { 1927511b41d2SMark Murray if (!exists && omode != mode) 192883d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD 192921e764dfSDag-Erling Smørgrav if (fchmod(ofd, omode & ~mask)) { 193083d2307dSDag-Erling Smørgrav #else /* HAVE_FCHMOD */ 193121e764dfSDag-Erling Smørgrav if (chmod(np, omode & ~mask)) { 193283d2307dSDag-Erling Smørgrav #endif /* HAVE_FCHMOD */ 193319261079SEd Maste note_err("%s: set mode: %s", 1934511b41d2SMark Murray np, strerror(errno)); 193521e764dfSDag-Erling Smørgrav } 1936511b41d2SMark Murray } 193719261079SEd Maste if (close(ofd) == -1) 193819261079SEd Maste note_err("%s: close: %s", np, strerror(errno)); 1939511b41d2SMark Murray (void) response(); 1940076ad2f8SDag-Erling Smørgrav if (showprogress) 1941076ad2f8SDag-Erling Smørgrav stop_progress_meter(); 194219261079SEd Maste if (setimes && !wrerr) { 1943511b41d2SMark Murray setimes = 0; 194419261079SEd Maste if (utimes(np, tv) == -1) { 194519261079SEd Maste note_err("%s: set times: %s", 1946511b41d2SMark Murray np, strerror(errno)); 1947511b41d2SMark Murray } 1948511b41d2SMark Murray } 194919261079SEd Maste /* If no error was noted then signal success for this file */ 195019261079SEd Maste if (note_err(NULL) == 0) 1951cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 1952511b41d2SMark Murray } 19530967215dSEd Maste done: 19540967215dSEd Maste for (n = 0; n < npatterns; n++) 19550967215dSEd Maste free(patterns[n]); 19560967215dSEd Maste free(patterns); 19570967215dSEd Maste return; 1958511b41d2SMark Murray screwup: 19590967215dSEd Maste for (n = 0; n < npatterns; n++) 19600967215dSEd Maste free(patterns[n]); 19610967215dSEd Maste free(patterns); 1962511b41d2SMark Murray run_err("protocol error: %s", why); 1963511b41d2SMark Murray exit(1); 1964511b41d2SMark Murray } 1965511b41d2SMark Murray 196619261079SEd Maste void 196719261079SEd Maste throughlocal_sftp(struct sftp_conn *from, struct sftp_conn *to, 196819261079SEd Maste char *src, char *targ) 196919261079SEd Maste { 197019261079SEd Maste char *target = NULL, *filename = NULL, *abs_dst = NULL; 197119261079SEd Maste char *abs_src = NULL, *tmp = NULL; 197219261079SEd Maste glob_t g; 197319261079SEd Maste int i, r, targetisdir, err = 0; 197419261079SEd Maste 197519261079SEd Maste if ((filename = basename(src)) == NULL) 197619261079SEd Maste fatal("basename %s: %s", src, strerror(errno)); 197719261079SEd Maste 197819261079SEd Maste if ((abs_src = prepare_remote_path(from, src)) == NULL || 197919261079SEd Maste (target = prepare_remote_path(to, targ)) == NULL) 198019261079SEd Maste cleanup_exit(255); 198119261079SEd Maste memset(&g, 0, sizeof(g)); 198219261079SEd Maste 198319261079SEd Maste targetisdir = remote_is_dir(to, target); 198419261079SEd Maste if (!targetisdir && targetshouldbedirectory) { 19851323ec57SEd Maste error("%s: destination is not a directory", targ); 198619261079SEd Maste err = -1; 198719261079SEd Maste goto out; 198819261079SEd Maste } 198919261079SEd Maste 199019261079SEd Maste debug3_f("copying remote %s to remote %s", abs_src, target); 1991f374ba41SEd Maste if ((r = remote_glob(from, abs_src, GLOB_NOCHECK|GLOB_MARK, 1992f374ba41SEd Maste NULL, &g)) != 0) { 199319261079SEd Maste if (r == GLOB_NOSPACE) 19941323ec57SEd Maste error("%s: too many glob matches", src); 199519261079SEd Maste else 19961323ec57SEd Maste error("%s: %s", src, strerror(ENOENT)); 199719261079SEd Maste err = -1; 199819261079SEd Maste goto out; 199919261079SEd Maste } 200019261079SEd Maste 2001f374ba41SEd Maste /* Did we actually get any matches back from the glob? */ 2002f374ba41SEd Maste if (g.gl_matchc == 0 && g.gl_pathc == 1 && g.gl_pathv[0] != 0) { 2003f374ba41SEd Maste /* 2004f374ba41SEd Maste * If nothing matched but a path returned, then it's probably 2005f374ba41SEd Maste * a GLOB_NOCHECK result. Check whether the unglobbed path 2006f374ba41SEd Maste * exists so we can give a nice error message early. 2007f374ba41SEd Maste */ 2008f374ba41SEd Maste if (do_stat(from, g.gl_pathv[0], 1) == NULL) { 2009f374ba41SEd Maste error("%s: %s", src, strerror(ENOENT)); 2010f374ba41SEd Maste err = -1; 2011f374ba41SEd Maste goto out; 2012f374ba41SEd Maste } 2013f374ba41SEd Maste } 2014f374ba41SEd Maste 201519261079SEd Maste for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 201619261079SEd Maste tmp = xstrdup(g.gl_pathv[i]); 201719261079SEd Maste if ((filename = basename(tmp)) == NULL) { 201819261079SEd Maste error("basename %s: %s", tmp, strerror(errno)); 201919261079SEd Maste err = -1; 202019261079SEd Maste goto out; 202119261079SEd Maste } 202219261079SEd Maste 202319261079SEd Maste if (targetisdir) 202419261079SEd Maste abs_dst = path_append(target, filename); 202519261079SEd Maste else 202619261079SEd Maste abs_dst = xstrdup(target); 202719261079SEd Maste 202819261079SEd Maste debug("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); 202919261079SEd Maste if (globpath_is_dir(g.gl_pathv[i]) && iamrecursive) { 203019261079SEd Maste if (crossload_dir(from, to, g.gl_pathv[i], abs_dst, 203119261079SEd Maste NULL, pflag, SFTP_PROGRESS_ONLY, 1) == -1) 203219261079SEd Maste err = -1; 203319261079SEd Maste } else { 203419261079SEd Maste if (do_crossload(from, to, g.gl_pathv[i], abs_dst, NULL, 203519261079SEd Maste pflag) == -1) 203619261079SEd Maste err = -1; 203719261079SEd Maste } 203819261079SEd Maste free(abs_dst); 203919261079SEd Maste abs_dst = NULL; 204019261079SEd Maste free(tmp); 204119261079SEd Maste tmp = NULL; 204219261079SEd Maste } 204319261079SEd Maste 204419261079SEd Maste out: 204519261079SEd Maste free(abs_src); 204619261079SEd Maste free(abs_dst); 204719261079SEd Maste free(target); 204819261079SEd Maste free(tmp); 204919261079SEd Maste globfree(&g); 205019261079SEd Maste if (err == -1) 2051e9e8876aSEd Maste errs = 1; 205219261079SEd Maste } 205319261079SEd Maste 2054511b41d2SMark Murray int 2055ae1f160dSDag-Erling Smørgrav response(void) 2056511b41d2SMark Murray { 2057076ad2f8SDag-Erling Smørgrav char ch, *cp, resp, rbuf[2048], visbuf[2048]; 2058511b41d2SMark Murray 2059a04a10f8SKris Kennaway if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp)) 2060511b41d2SMark Murray lostconn(0); 2061511b41d2SMark Murray 2062511b41d2SMark Murray cp = rbuf; 2063511b41d2SMark Murray switch (resp) { 2064511b41d2SMark Murray case 0: /* ok */ 2065511b41d2SMark Murray return (0); 2066511b41d2SMark Murray default: 2067511b41d2SMark Murray *cp++ = resp; 2068511b41d2SMark Murray /* FALLTHROUGH */ 2069511b41d2SMark Murray case 1: /* error, followed by error msg */ 2070511b41d2SMark Murray case 2: /* fatal error, "" */ 2071511b41d2SMark Murray do { 2072a04a10f8SKris Kennaway if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) 2073511b41d2SMark Murray lostconn(0); 2074511b41d2SMark Murray *cp++ = ch; 2075511b41d2SMark Murray } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); 2076511b41d2SMark Murray 2077076ad2f8SDag-Erling Smørgrav if (!iamremote) { 2078076ad2f8SDag-Erling Smørgrav cp[-1] = '\0'; 2079076ad2f8SDag-Erling Smørgrav (void) snmprintf(visbuf, sizeof(visbuf), 2080076ad2f8SDag-Erling Smørgrav NULL, "%s\n", rbuf); 2081076ad2f8SDag-Erling Smørgrav (void) atomicio(vwrite, STDERR_FILENO, 2082076ad2f8SDag-Erling Smørgrav visbuf, strlen(visbuf)); 2083076ad2f8SDag-Erling Smørgrav } 2084511b41d2SMark Murray ++errs; 2085511b41d2SMark Murray if (resp == 1) 2086511b41d2SMark Murray return (-1); 2087511b41d2SMark Murray exit(1); 2088511b41d2SMark Murray } 2089511b41d2SMark Murray /* NOTREACHED */ 2090511b41d2SMark Murray } 2091511b41d2SMark Murray 2092511b41d2SMark Murray void 2093ae1f160dSDag-Erling Smørgrav usage(void) 2094511b41d2SMark Murray { 2095ae1f160dSDag-Erling Smørgrav (void) fprintf(stderr, 209619261079SEd Maste "usage: scp [-346ABCOpqRrsTv] [-c cipher] [-D sftp_server_path] [-F ssh_config]\n" 2097f374ba41SEd Maste " [-i identity_file] [-J destination] [-l limit] [-o ssh_option]\n" 2098f374ba41SEd Maste " [-P port] [-S program] [-X sftp_option] source ... target\n"); 2099511b41d2SMark Murray exit(1); 2100511b41d2SMark Murray } 2101511b41d2SMark Murray 2102511b41d2SMark Murray void 2103511b41d2SMark Murray run_err(const char *fmt,...) 2104511b41d2SMark Murray { 2105511b41d2SMark Murray static FILE *fp; 2106511b41d2SMark Murray va_list ap; 2107511b41d2SMark Murray 2108511b41d2SMark Murray ++errs; 2109333ee039SDag-Erling Smørgrav if (fp != NULL || (remout != -1 && (fp = fdopen(remout, "w")))) { 2110511b41d2SMark Murray (void) fprintf(fp, "%c", 0x01); 2111511b41d2SMark Murray (void) fprintf(fp, "scp: "); 2112ae1f160dSDag-Erling Smørgrav va_start(ap, fmt); 2113511b41d2SMark Murray (void) vfprintf(fp, fmt, ap); 2114ae1f160dSDag-Erling Smørgrav va_end(ap); 2115511b41d2SMark Murray (void) fprintf(fp, "\n"); 2116511b41d2SMark Murray (void) fflush(fp); 2117333ee039SDag-Erling Smørgrav } 2118511b41d2SMark Murray 2119511b41d2SMark Murray if (!iamremote) { 2120ae1f160dSDag-Erling Smørgrav va_start(ap, fmt); 2121076ad2f8SDag-Erling Smørgrav vfmprintf(stderr, fmt, ap); 2122ae1f160dSDag-Erling Smørgrav va_end(ap); 2123511b41d2SMark Murray fprintf(stderr, "\n"); 2124511b41d2SMark Murray } 2125511b41d2SMark Murray } 2126511b41d2SMark Murray 212719261079SEd Maste /* 212819261079SEd Maste * Notes a sink error for sending at the end of a file transfer. Returns 0 if 212919261079SEd Maste * no error has been noted or -1 otherwise. Use note_err(NULL) to flush 213019261079SEd Maste * any active error at the end of the transfer. 213119261079SEd Maste */ 213219261079SEd Maste int 213319261079SEd Maste note_err(const char *fmt, ...) 213419261079SEd Maste { 213519261079SEd Maste static char *emsg; 213619261079SEd Maste va_list ap; 213719261079SEd Maste 213819261079SEd Maste /* Replay any previously-noted error */ 213919261079SEd Maste if (fmt == NULL) { 214019261079SEd Maste if (emsg == NULL) 214119261079SEd Maste return 0; 214219261079SEd Maste run_err("%s", emsg); 214319261079SEd Maste free(emsg); 214419261079SEd Maste emsg = NULL; 214519261079SEd Maste return -1; 214619261079SEd Maste } 214719261079SEd Maste 214819261079SEd Maste errs++; 214919261079SEd Maste /* Prefer first-noted error */ 215019261079SEd Maste if (emsg != NULL) 215119261079SEd Maste return -1; 215219261079SEd Maste 215319261079SEd Maste va_start(ap, fmt); 215419261079SEd Maste vasnmprintf(&emsg, INT_MAX, NULL, fmt, ap); 215519261079SEd Maste va_end(ap); 215619261079SEd Maste return -1; 215719261079SEd Maste } 215819261079SEd Maste 2159511b41d2SMark Murray void 2160cf2b5f3bSDag-Erling Smørgrav verifydir(char *cp) 2161511b41d2SMark Murray { 2162511b41d2SMark Murray struct stat stb; 2163511b41d2SMark Murray 2164511b41d2SMark Murray if (!stat(cp, &stb)) { 2165511b41d2SMark Murray if (S_ISDIR(stb.st_mode)) 2166511b41d2SMark Murray return; 2167511b41d2SMark Murray errno = ENOTDIR; 2168511b41d2SMark Murray } 2169511b41d2SMark Murray run_err("%s: %s", cp, strerror(errno)); 2170d4ecd108SDag-Erling Smørgrav killchild(0); 2171511b41d2SMark Murray } 2172511b41d2SMark Murray 2173511b41d2SMark Murray int 2174cf2b5f3bSDag-Erling Smørgrav okname(char *cp0) 2175511b41d2SMark Murray { 2176511b41d2SMark Murray int c; 2177511b41d2SMark Murray char *cp; 2178511b41d2SMark Murray 2179511b41d2SMark Murray cp = cp0; 2180511b41d2SMark Murray do { 2181ae1f160dSDag-Erling Smørgrav c = (int)*cp; 2182511b41d2SMark Murray if (c & 0200) 2183511b41d2SMark Murray goto bad; 2184f7167e0eSDag-Erling Smørgrav if (!isalpha(c) && !isdigit((unsigned char)c)) { 2185e73e9afaSDag-Erling Smørgrav switch (c) { 2186e73e9afaSDag-Erling Smørgrav case '\'': 2187e73e9afaSDag-Erling Smørgrav case '"': 2188e73e9afaSDag-Erling Smørgrav case '`': 2189e73e9afaSDag-Erling Smørgrav case ' ': 2190e73e9afaSDag-Erling Smørgrav case '#': 2191511b41d2SMark Murray goto bad; 2192e73e9afaSDag-Erling Smørgrav default: 2193e73e9afaSDag-Erling Smørgrav break; 2194e73e9afaSDag-Erling Smørgrav } 2195e73e9afaSDag-Erling Smørgrav } 2196511b41d2SMark Murray } while (*++cp); 2197511b41d2SMark Murray return (1); 2198511b41d2SMark Murray 2199076ad2f8SDag-Erling Smørgrav bad: fmprintf(stderr, "%s: invalid user name\n", cp0); 2200511b41d2SMark Murray return (0); 2201511b41d2SMark Murray } 2202511b41d2SMark Murray 2203511b41d2SMark Murray BUF * 2204cf2b5f3bSDag-Erling Smørgrav allocbuf(BUF *bp, int fd, int blksize) 2205511b41d2SMark Murray { 2206511b41d2SMark Murray size_t size; 220783d2307dSDag-Erling Smørgrav #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE 2208511b41d2SMark Murray struct stat stb; 2209511b41d2SMark Murray 221019261079SEd Maste if (fstat(fd, &stb) == -1) { 2211511b41d2SMark Murray run_err("fstat: %s", strerror(errno)); 2212511b41d2SMark Murray return (0); 2213511b41d2SMark Murray } 2214ca86bcf2SDag-Erling Smørgrav size = ROUNDUP(stb.st_blksize, blksize); 2215e73e9afaSDag-Erling Smørgrav if (size == 0) 2216e73e9afaSDag-Erling Smørgrav size = blksize; 221783d2307dSDag-Erling Smørgrav #else /* HAVE_STRUCT_STAT_ST_BLKSIZE */ 221883d2307dSDag-Erling Smørgrav size = blksize; 221983d2307dSDag-Erling Smørgrav #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ 2220511b41d2SMark Murray if (bp->cnt >= size) 2221511b41d2SMark Murray return (bp); 22224f52dfbbSDag-Erling Smørgrav bp->buf = xrecallocarray(bp->buf, bp->cnt, size, 1); 2223511b41d2SMark Murray bp->cnt = size; 2224511b41d2SMark Murray return (bp); 2225511b41d2SMark Murray } 2226511b41d2SMark Murray 2227511b41d2SMark Murray void 2228cf2b5f3bSDag-Erling Smørgrav lostconn(int signo) 2229511b41d2SMark Murray { 2230511b41d2SMark Murray if (!iamremote) 2231e4a9863fSDag-Erling Smørgrav (void)write(STDERR_FILENO, "lost connection\n", 16); 2232ae1f160dSDag-Erling Smørgrav if (signo) 2233ae1f160dSDag-Erling Smørgrav _exit(1); 2234ae1f160dSDag-Erling Smørgrav else 2235511b41d2SMark Murray exit(1); 2236511b41d2SMark Murray } 223719261079SEd Maste 223819261079SEd Maste void 223919261079SEd Maste cleanup_exit(int i) 224019261079SEd Maste { 224119261079SEd Maste if (remin > 0) 224219261079SEd Maste close(remin); 224319261079SEd Maste if (remout > 0) 224419261079SEd Maste close(remout); 224519261079SEd Maste if (remin2 > 0) 224619261079SEd Maste close(remin2); 224719261079SEd Maste if (remout2 > 0) 224819261079SEd Maste close(remout2); 224919261079SEd Maste if (do_cmd_pid > 0) 225019261079SEd Maste waitpid(do_cmd_pid, NULL, 0); 225119261079SEd Maste if (do_cmd_pid2 > 0) 225219261079SEd Maste waitpid(do_cmd_pid2, NULL, 0); 225319261079SEd Maste exit(i); 225419261079SEd Maste } 2255