- markus@cvs.openbsd.org 2010/12/08 22:46:03
     [scp.1 scp.c]
     add a new -3 option to scp: Copies between two remote hosts are
     transferred through the local host.  Without this option the data
     is copied directly between the two remote hosts. ok djm@ (bugzilla #1837)
diff --git a/ChangeLog b/ChangeLog
index 135ad48..aa98f99 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+20110106
+ - (djm) OpenBSD CVS Sync
+   - markus@cvs.openbsd.org 2010/12/08 22:46:03
+     [scp.1 scp.c]
+     add a new -3 option to scp: Copies between two remote hosts are
+     transferred through the local host.  Without this option the data
+     is copied directly between the two remote hosts. ok djm@ (bugzilla #1837)
+
 20110104
  - (djm) [configure.ac Makefile.in] Use mandoc as preferred manpage
    formatter if it is present, followed by nroff and groff respectively.
diff --git a/scp.1 b/scp.1
index 346e5e3..28bac56 100644
--- a/scp.1
+++ b/scp.1
@@ -8,9 +8,9 @@
 .\"
 .\" Created: Sun May  7 00:14:37 1995 ylo
 .\"
-.\" $OpenBSD: scp.1,v 1.54 2010/11/18 15:01:00 jmc Exp $
+.\" $OpenBSD: scp.1,v 1.55 2010/12/08 22:46:03 markus Exp $
 .\"
-.Dd $Mdocdate: November 18 2010 $
+.Dd $Mdocdate: December 8 2010 $
 .Dt SCP 1
 .Os
 .Sh NAME
@@ -19,7 +19,7 @@
 .Sh SYNOPSIS
 .Nm scp
 .Bk -words
-.Op Fl 1246BCpqrv
+.Op Fl 12346BCpqrv
 .Op Fl c Ar cipher
 .Op Fl F Ar ssh_config
 .Op Fl i Ar identity_file
@@ -75,6 +75,11 @@
 Forces
 .Nm
 to use protocol 2.
+.It Fl 3
+Copies between two remote hosts are transferred through the local host.
+Without this option the data is copied directly between the two remote
+hosts.
+Note that this options disables the progress meter.
 .It Fl 4
 Forces
 .Nm
diff --git a/scp.c b/scp.c
index 774e602..1262e0a 100644
--- a/scp.c
+++ b/scp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: scp.c,v 1.168 2010/11/26 05:52:49 djm Exp $ */
+/* $OpenBSD: scp.c,v 1.169 2010/12/08 22:46:03 markus Exp $ */
 /*
  * scp - secure remote copy.  This is basically patched BSD rcp which
  * uses ssh to do the data transfer (instead of using rcmd).
@@ -119,6 +119,7 @@
 #define COPY_BUFLEN	16384
 
 int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout);
+int do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout);
 
 /* Struct for addargs */
 arglist args;
@@ -137,6 +138,12 @@
 /* This is set to zero if the progressmeter is not desired. */
 int showprogress = 1;
 
+/*
+ * This is set to non-zero if remote-remote copy should be piped
+ * through this process.
+ */
+int throughlocal = 0;
+
 /* This is the program to execute for the secured connection. ("ssh" or -S) */
 char *ssh_program = _PATH_SSH_PROGRAM;
 
@@ -287,6 +294,50 @@
 	return 0;
 }
 
+/*
+ * This functions executes a command simlar to do_cmd(), but expects the
+ * input and output descriptors to be setup by a previous call to do_cmd().
+ * This way the input and output of two commands can be connected.
+ */
+int
+do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout)
+{
+	pid_t pid;
+	int status;
+
+	if (verbose_mode)
+		fprintf(stderr,
+		    "Executing: 2nd program %s host %s, user %s, command %s\n",
+		    ssh_program, host,
+		    remuser ? remuser : "(unspecified)", cmd);
+
+	/* Fork a child to execute the command on the remote host using ssh. */
+	pid = fork();
+	if (pid == 0) {
+		dup2(fdin, 0);
+		dup2(fdout, 1);
+
+		replacearg(&args, 0, "%s", ssh_program);
+		if (remuser != NULL) {
+			addargs(&args, "-l");
+			addargs(&args, "%s", remuser);
+		}
+		addargs(&args, "--");
+		addargs(&args, "%s", host);
+		addargs(&args, "%s", cmd);
+
+		execvp(ssh_program, args.list);
+		perror(ssh_program);
+		exit(1);
+	} else if (pid == -1) {
+		fatal("fork: %s", strerror(errno));
+	}
+	while (waitpid(pid, &status, 0) == -1)
+		if (errno != EINTR)
+			fatal("do_cmd2: waitpid: %s", strerror(errno));
+	return 0;
+}
+
 typedef struct {
 	size_t cnt;
 	char *buf;
@@ -344,7 +395,7 @@
 	addargs(&args, "-oClearAllForwardings=yes");
 
 	fflag = tflag = 0;
-	while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q1246S:o:F:")) != -1)
+	while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1)
 		switch (ch) {
 		/* User-visible flags. */
 		case '1':
@@ -355,6 +406,9 @@
 			addargs(&args, "-%c", ch);
 			addargs(&remote_remote_args, "-%c", ch);
 			break;
+		case '3':
+			throughlocal = 1;
+			break;
 		case 'o':
 		case 'c':
 		case 'i':
@@ -530,7 +584,36 @@
 
 	for (i = 0; i < argc - 1; i++) {
 		src = colon(argv[i]);
-		if (src) {	/* remote to remote */
+		if (src && throughlocal) {	/* extended remote to remote */
+			*src++ = 0;
+			if (*src == 0)
+				src = ".";
+			host = strrchr(argv[i], '@');
+			if (host) {
+				*host++ = 0;
+				host = cleanhostname(host);
+				suser = argv[i];
+				if (*suser == '\0')
+					suser = pwd->pw_name;
+				else if (!okname(suser))
+					continue;
+			} else {
+				host = cleanhostname(argv[i]);
+				suser = NULL;
+			}
+			xasprintf(&bp, "%s -f -- %s", cmd, src);
+			if (do_cmd(host, suser, bp, &remin, &remout) < 0)
+				exit(1);
+			(void) xfree(bp);
+			host = cleanhostname(thost);
+			xasprintf(&bp, "%s -t -- %s", cmd, targ);
+			if (do_cmd2(host, tuser, bp, remin, remout) < 0)
+				exit(1);
+			(void) xfree(bp);
+			(void) close(remin);
+			(void) close(remout);
+			remin = remout = -1;
+		} else if (src) {	/* standard remote to remote */
 			freeargs(&alist);
 			addargs(&alist, "%s", ssh_program);
 			addargs(&alist, "-x");