sftp.c revision 113908
176259Sgreen/*
292555Sdes * Copyright (c) 2001,2002 Damien Miller.  All rights reserved.
376259Sgreen *
476259Sgreen * Redistribution and use in source and binary forms, with or without
576259Sgreen * modification, are permitted provided that the following conditions
676259Sgreen * are met:
776259Sgreen * 1. Redistributions of source code must retain the above copyright
876259Sgreen *    notice, this list of conditions and the following disclaimer.
976259Sgreen * 2. Redistributions in binary form must reproduce the above copyright
1076259Sgreen *    notice, this list of conditions and the following disclaimer in the
1176259Sgreen *    documentation and/or other materials provided with the distribution.
1276259Sgreen *
1376259Sgreen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1476259Sgreen * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1576259Sgreen * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1676259Sgreen * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1776259Sgreen * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1876259Sgreen * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1976259Sgreen * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2076259Sgreen * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2176259Sgreen * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2276259Sgreen * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2376259Sgreen */
2476259Sgreen
2576259Sgreen#include "includes.h"
2676259Sgreen
27113908SdesRCSID("$OpenBSD: sftp.c,v 1.34 2003/01/10 08:19:07 fgsch Exp $");
2876259Sgreen
2976259Sgreen/* XXX: short-form remote directory listings (like 'ls -C') */
3076259Sgreen
3176259Sgreen#include "buffer.h"
3276259Sgreen#include "xmalloc.h"
3376259Sgreen#include "log.h"
3476259Sgreen#include "pathnames.h"
3592555Sdes#include "misc.h"
3676259Sgreen
3776259Sgreen#include "sftp.h"
3876259Sgreen#include "sftp-common.h"
3976259Sgreen#include "sftp-client.h"
4076259Sgreen#include "sftp-int.h"
4176259Sgreen
4298937Sdes#ifdef HAVE___PROGNAME
4398937Sdesextern char *__progname;
4498937Sdes#else
4598937Sdeschar *__progname;
4698937Sdes#endif
4798937Sdes
4876259SgreenFILE* infile;
4992555Sdessize_t copy_buffer_len = 32768;
5092555Sdessize_t num_requests = 16;
5176259Sgreen
52113908Sdesextern int showprogress;
53113908Sdes
5492555Sdesstatic void
5592555Sdesconnect_to_server(char *path, char **args, int *in, int *out, pid_t *sshpid)
5676259Sgreen{
5776259Sgreen	int c_in, c_out;
5899060Sdes
5976259Sgreen#ifdef USE_PIPES
6076259Sgreen	int pin[2], pout[2];
6199060Sdes
6276259Sgreen	if ((pipe(pin) == -1) || (pipe(pout) == -1))
6376259Sgreen		fatal("pipe: %s", strerror(errno));
6476259Sgreen	*in = pin[0];
6576259Sgreen	*out = pout[1];
6676259Sgreen	c_in = pout[0];
6776259Sgreen	c_out = pin[1];
6876259Sgreen#else /* USE_PIPES */
6976259Sgreen	int inout[2];
7099060Sdes
7176259Sgreen	if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
7276259Sgreen		fatal("socketpair: %s", strerror(errno));
7376259Sgreen	*in = *out = inout[0];
7476259Sgreen	c_in = c_out = inout[1];
7576259Sgreen#endif /* USE_PIPES */
7676259Sgreen
7776259Sgreen	if ((*sshpid = fork()) == -1)
7876259Sgreen		fatal("fork: %s", strerror(errno));
7976259Sgreen	else if (*sshpid == 0) {
8076259Sgreen		if ((dup2(c_in, STDIN_FILENO) == -1) ||
8176259Sgreen		    (dup2(c_out, STDOUT_FILENO) == -1)) {
8276259Sgreen			fprintf(stderr, "dup2: %s\n", strerror(errno));
8376259Sgreen			exit(1);
8476259Sgreen		}
8576259Sgreen		close(*in);
8676259Sgreen		close(*out);
8776259Sgreen		close(c_in);
8876259Sgreen		close(c_out);
8992555Sdes		execv(path, args);
9092555Sdes		fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
9176259Sgreen		exit(1);
9276259Sgreen	}
9376259Sgreen
9476259Sgreen	close(c_in);
9576259Sgreen	close(c_out);
9676259Sgreen}
9776259Sgreen
9892555Sdesstatic void
9976259Sgreenusage(void)
10076259Sgreen{
10192555Sdes	extern char *__progname;
10298675Sdes
10392555Sdes	fprintf(stderr,
10492555Sdes	    "usage: %s [-vC1] [-b batchfile] [-o option] [-s subsystem|path] [-B buffer_size]\n"
10592555Sdes	    "            [-F config] [-P direct server path] [-S program]\n"
10692555Sdes	    "            [user@]host[:file [file]]\n", __progname);
10776259Sgreen	exit(1);
10876259Sgreen}
10976259Sgreen
11076259Sgreenint
11176259Sgreenmain(int argc, char **argv)
11276259Sgreen{
113113908Sdes	int in, out, ch, err;
11476259Sgreen	pid_t sshpid;
11576259Sgreen	char *host, *userhost, *cp, *file2;
11692555Sdes	int debug_level = 0, sshver = 2;
11792555Sdes	char *file1 = NULL, *sftp_server = NULL;
11892555Sdes	char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
11992555Sdes	LogLevel ll = SYSLOG_LEVEL_INFO;
12092555Sdes	arglist args;
12176259Sgreen	extern int optind;
12276259Sgreen	extern char *optarg;
12376259Sgreen
12498937Sdes	__progname = get_progname(argv[0]);
12592555Sdes	args.list = NULL;
12692555Sdes	addargs(&args, "ssh");		/* overwritten with ssh_program */
12792555Sdes	addargs(&args, "-oForwardX11 no");
12892555Sdes	addargs(&args, "-oForwardAgent no");
12992555Sdes	addargs(&args, "-oClearAllForwardings yes");
13092555Sdes	ll = SYSLOG_LEVEL_INFO;
13176259Sgreen	infile = stdin;		/* Read from STDIN unless changed by -b */
13276259Sgreen
13392555Sdes	while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) {
13476259Sgreen		switch (ch) {
13576259Sgreen		case 'C':
13692555Sdes			addargs(&args, "-C");
13776259Sgreen			break;
13876259Sgreen		case 'v':
13992555Sdes			if (debug_level < 3) {
14092555Sdes				addargs(&args, "-v");
14192555Sdes				ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
14292555Sdes			}
14392555Sdes			debug_level++;
14476259Sgreen			break;
14592555Sdes		case 'F':
14676259Sgreen		case 'o':
14792555Sdes			addargs(&args, "-%c%s", ch, optarg);
14876259Sgreen			break;
14976259Sgreen		case '1':
15092555Sdes			sshver = 1;
15176259Sgreen			if (sftp_server == NULL)
15276259Sgreen				sftp_server = _PATH_SFTP_SERVER;
15376259Sgreen			break;
15476259Sgreen		case 's':
15576259Sgreen			sftp_server = optarg;
15676259Sgreen			break;
15776259Sgreen		case 'S':
15876259Sgreen			ssh_program = optarg;
15976259Sgreen			break;
16076259Sgreen		case 'b':
16176259Sgreen			if (infile == stdin) {
16276259Sgreen				infile = fopen(optarg, "r");
16376259Sgreen				if (infile == NULL)
16476259Sgreen					fatal("%s (%s).", strerror(errno), optarg);
16576259Sgreen			} else
16676259Sgreen				fatal("Filename already specified.");
167113908Sdes			showprogress = 0;
16876259Sgreen			break;
16992555Sdes		case 'P':
17092555Sdes			sftp_direct = optarg;
17192555Sdes			break;
17292555Sdes		case 'B':
17392555Sdes			copy_buffer_len = strtol(optarg, &cp, 10);
17492555Sdes			if (copy_buffer_len == 0 || *cp != '\0')
17592555Sdes				fatal("Invalid buffer size \"%s\"", optarg);
17692555Sdes			break;
17792555Sdes		case 'R':
17892555Sdes			num_requests = strtol(optarg, &cp, 10);
17992555Sdes			if (num_requests == 0 || *cp != '\0')
18098675Sdes				fatal("Invalid number of requests \"%s\"",
18192555Sdes				    optarg);
18292555Sdes			break;
18376259Sgreen		case 'h':
18476259Sgreen		default:
18576259Sgreen			usage();
18676259Sgreen		}
18776259Sgreen	}
18876259Sgreen
18998675Sdes	log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
19098675Sdes
19192555Sdes	if (sftp_direct == NULL) {
19292555Sdes		if (optind == argc || argc > (optind + 2))
19392555Sdes			usage();
19476259Sgreen
19592555Sdes		userhost = xstrdup(argv[optind]);
19692555Sdes		file2 = argv[optind+1];
19776259Sgreen
19892555Sdes		if ((cp = colon(userhost)) != NULL) {
19992555Sdes			*cp++ = '\0';
20092555Sdes			file1 = cp;
20192555Sdes		}
20276259Sgreen
203113908Sdes		if ((host = strrchr(userhost, '@')) == NULL)
20492555Sdes			host = userhost;
20592555Sdes		else {
20692555Sdes			*host++ = '\0';
20792555Sdes			if (!userhost[0]) {
20892555Sdes				fprintf(stderr, "Missing username\n");
20992555Sdes				usage();
21092555Sdes			}
21192555Sdes			addargs(&args, "-l%s",userhost);
21292555Sdes		}
21392555Sdes
21492555Sdes		host = cleanhostname(host);
21592555Sdes		if (!*host) {
21692555Sdes			fprintf(stderr, "Missing hostname\n");
21776259Sgreen			usage();
21876259Sgreen		}
21976259Sgreen
22092555Sdes		addargs(&args, "-oProtocol %d", sshver);
22176259Sgreen
22292555Sdes		/* no subsystem if the server-spec contains a '/' */
22392555Sdes		if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
22492555Sdes			addargs(&args, "-s");
22576259Sgreen
22692555Sdes		addargs(&args, "%s", host);
22798675Sdes		addargs(&args, "%s", (sftp_server != NULL ?
22892555Sdes		    sftp_server : "sftp"));
22992555Sdes		args.list[0] = ssh_program;
23076259Sgreen
23192555Sdes		fprintf(stderr, "Connecting to %s...\n", host);
23298675Sdes		connect_to_server(ssh_program, args.list, &in, &out,
23392555Sdes		    &sshpid);
23492555Sdes	} else {
23592555Sdes		args.list = NULL;
23692555Sdes		addargs(&args, "sftp-server");
23776259Sgreen
23892555Sdes		fprintf(stderr, "Attaching to %s...\n", sftp_direct);
23998675Sdes		connect_to_server(sftp_direct, args.list, &in, &out,
24092555Sdes		    &sshpid);
24192555Sdes	}
24276259Sgreen
243113908Sdes	err = interactive_loop(in, out, file1, file2);
24476259Sgreen
24598937Sdes#if !defined(USE_PIPES)
24698937Sdes	shutdown(in, SHUT_RDWR);
24798937Sdes	shutdown(out, SHUT_RDWR);
24898937Sdes#endif
24998937Sdes
25076259Sgreen	close(in);
25176259Sgreen	close(out);
25276259Sgreen	if (infile != stdin)
25376259Sgreen		fclose(infile);
25476259Sgreen
25598675Sdes	while (waitpid(sshpid, NULL, 0) == -1)
25698675Sdes		if (errno != EINTR)
25798675Sdes			fatal("Couldn't wait for ssh process: %s",
25898675Sdes			    strerror(errno));
25976259Sgreen
260113908Sdes	exit(err == 0 ? 0 : 1);
26176259Sgreen}
262