sftp.c revision 98937
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
2798675SdesRCSID("$OpenBSD: sftp.c,v 1.29 2002/04/02 17:37:48 markus 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
5292555Sdesstatic void
5392555Sdesconnect_to_server(char *path, char **args, int *in, int *out, pid_t *sshpid)
5476259Sgreen{
5576259Sgreen	int c_in, c_out;
5676259Sgreen#ifdef USE_PIPES
5776259Sgreen	int pin[2], pout[2];
5876259Sgreen	if ((pipe(pin) == -1) || (pipe(pout) == -1))
5976259Sgreen		fatal("pipe: %s", strerror(errno));
6076259Sgreen	*in = pin[0];
6176259Sgreen	*out = pout[1];
6276259Sgreen	c_in = pout[0];
6376259Sgreen	c_out = pin[1];
6476259Sgreen#else /* USE_PIPES */
6576259Sgreen	int inout[2];
6676259Sgreen	if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
6776259Sgreen		fatal("socketpair: %s", strerror(errno));
6876259Sgreen	*in = *out = inout[0];
6976259Sgreen	c_in = c_out = inout[1];
7076259Sgreen#endif /* USE_PIPES */
7176259Sgreen
7276259Sgreen	if ((*sshpid = fork()) == -1)
7376259Sgreen		fatal("fork: %s", strerror(errno));
7476259Sgreen	else if (*sshpid == 0) {
7576259Sgreen		if ((dup2(c_in, STDIN_FILENO) == -1) ||
7676259Sgreen		    (dup2(c_out, STDOUT_FILENO) == -1)) {
7776259Sgreen			fprintf(stderr, "dup2: %s\n", strerror(errno));
7876259Sgreen			exit(1);
7976259Sgreen		}
8076259Sgreen		close(*in);
8176259Sgreen		close(*out);
8276259Sgreen		close(c_in);
8376259Sgreen		close(c_out);
8492555Sdes		execv(path, args);
8592555Sdes		fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
8676259Sgreen		exit(1);
8776259Sgreen	}
8876259Sgreen
8976259Sgreen	close(c_in);
9076259Sgreen	close(c_out);
9176259Sgreen}
9276259Sgreen
9392555Sdesstatic void
9476259Sgreenusage(void)
9576259Sgreen{
9692555Sdes	extern char *__progname;
9798675Sdes
9892555Sdes	fprintf(stderr,
9992555Sdes	    "usage: %s [-vC1] [-b batchfile] [-o option] [-s subsystem|path] [-B buffer_size]\n"
10092555Sdes	    "            [-F config] [-P direct server path] [-S program]\n"
10192555Sdes	    "            [user@]host[:file [file]]\n", __progname);
10276259Sgreen	exit(1);
10376259Sgreen}
10476259Sgreen
10576259Sgreenint
10676259Sgreenmain(int argc, char **argv)
10776259Sgreen{
10892555Sdes	int in, out, ch;
10976259Sgreen	pid_t sshpid;
11076259Sgreen	char *host, *userhost, *cp, *file2;
11192555Sdes	int debug_level = 0, sshver = 2;
11292555Sdes	char *file1 = NULL, *sftp_server = NULL;
11392555Sdes	char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
11492555Sdes	LogLevel ll = SYSLOG_LEVEL_INFO;
11592555Sdes	arglist args;
11676259Sgreen	extern int optind;
11776259Sgreen	extern char *optarg;
11876259Sgreen
11998937Sdes	__progname = get_progname(argv[0]);
12092555Sdes	args.list = NULL;
12192555Sdes	addargs(&args, "ssh");		/* overwritten with ssh_program */
12292555Sdes	addargs(&args, "-oFallBackToRsh no");
12392555Sdes	addargs(&args, "-oForwardX11 no");
12492555Sdes	addargs(&args, "-oForwardAgent no");
12592555Sdes	addargs(&args, "-oClearAllForwardings yes");
12692555Sdes	ll = SYSLOG_LEVEL_INFO;
12776259Sgreen	infile = stdin;		/* Read from STDIN unless changed by -b */
12876259Sgreen
12992555Sdes	while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) {
13076259Sgreen		switch (ch) {
13176259Sgreen		case 'C':
13292555Sdes			addargs(&args, "-C");
13376259Sgreen			break;
13476259Sgreen		case 'v':
13592555Sdes			if (debug_level < 3) {
13692555Sdes				addargs(&args, "-v");
13792555Sdes				ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
13892555Sdes			}
13992555Sdes			debug_level++;
14076259Sgreen			break;
14192555Sdes		case 'F':
14276259Sgreen		case 'o':
14392555Sdes			addargs(&args, "-%c%s", ch, optarg);
14476259Sgreen			break;
14576259Sgreen		case '1':
14692555Sdes			sshver = 1;
14776259Sgreen			if (sftp_server == NULL)
14876259Sgreen				sftp_server = _PATH_SFTP_SERVER;
14976259Sgreen			break;
15076259Sgreen		case 's':
15176259Sgreen			sftp_server = optarg;
15276259Sgreen			break;
15376259Sgreen		case 'S':
15476259Sgreen			ssh_program = optarg;
15576259Sgreen			break;
15676259Sgreen		case 'b':
15776259Sgreen			if (infile == stdin) {
15876259Sgreen				infile = fopen(optarg, "r");
15976259Sgreen				if (infile == NULL)
16076259Sgreen					fatal("%s (%s).", strerror(errno), optarg);
16176259Sgreen			} else
16276259Sgreen				fatal("Filename already specified.");
16376259Sgreen			break;
16492555Sdes		case 'P':
16592555Sdes			sftp_direct = optarg;
16692555Sdes			break;
16792555Sdes		case 'B':
16892555Sdes			copy_buffer_len = strtol(optarg, &cp, 10);
16992555Sdes			if (copy_buffer_len == 0 || *cp != '\0')
17092555Sdes				fatal("Invalid buffer size \"%s\"", optarg);
17192555Sdes			break;
17292555Sdes		case 'R':
17392555Sdes			num_requests = strtol(optarg, &cp, 10);
17492555Sdes			if (num_requests == 0 || *cp != '\0')
17598675Sdes				fatal("Invalid number of requests \"%s\"",
17692555Sdes				    optarg);
17792555Sdes			break;
17876259Sgreen		case 'h':
17976259Sgreen		default:
18076259Sgreen			usage();
18176259Sgreen		}
18276259Sgreen	}
18376259Sgreen
18498675Sdes	log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
18598675Sdes
18692555Sdes	if (sftp_direct == NULL) {
18792555Sdes		if (optind == argc || argc > (optind + 2))
18892555Sdes			usage();
18976259Sgreen
19092555Sdes		userhost = xstrdup(argv[optind]);
19192555Sdes		file2 = argv[optind+1];
19276259Sgreen
19392555Sdes		if ((cp = colon(userhost)) != NULL) {
19492555Sdes			*cp++ = '\0';
19592555Sdes			file1 = cp;
19692555Sdes		}
19776259Sgreen
19892555Sdes		if ((host = strchr(userhost, '@')) == NULL)
19992555Sdes			host = userhost;
20092555Sdes		else {
20192555Sdes			*host++ = '\0';
20292555Sdes			if (!userhost[0]) {
20392555Sdes				fprintf(stderr, "Missing username\n");
20492555Sdes				usage();
20592555Sdes			}
20692555Sdes			addargs(&args, "-l%s",userhost);
20792555Sdes		}
20892555Sdes
20992555Sdes		host = cleanhostname(host);
21092555Sdes		if (!*host) {
21192555Sdes			fprintf(stderr, "Missing hostname\n");
21276259Sgreen			usage();
21376259Sgreen		}
21476259Sgreen
21592555Sdes		addargs(&args, "-oProtocol %d", sshver);
21676259Sgreen
21792555Sdes		/* no subsystem if the server-spec contains a '/' */
21892555Sdes		if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
21992555Sdes			addargs(&args, "-s");
22076259Sgreen
22192555Sdes		addargs(&args, "%s", host);
22298675Sdes		addargs(&args, "%s", (sftp_server != NULL ?
22392555Sdes		    sftp_server : "sftp"));
22492555Sdes		args.list[0] = ssh_program;
22576259Sgreen
22692555Sdes		fprintf(stderr, "Connecting to %s...\n", host);
22798675Sdes		connect_to_server(ssh_program, args.list, &in, &out,
22892555Sdes		    &sshpid);
22992555Sdes	} else {
23092555Sdes		args.list = NULL;
23192555Sdes		addargs(&args, "sftp-server");
23276259Sgreen
23392555Sdes		fprintf(stderr, "Attaching to %s...\n", sftp_direct);
23498675Sdes		connect_to_server(sftp_direct, args.list, &in, &out,
23592555Sdes		    &sshpid);
23692555Sdes	}
23776259Sgreen
23876259Sgreen	interactive_loop(in, out, file1, file2);
23976259Sgreen
24098937Sdes#if !defined(USE_PIPES)
24198937Sdes	shutdown(in, SHUT_RDWR);
24298937Sdes	shutdown(out, SHUT_RDWR);
24398937Sdes#endif
24498937Sdes
24576259Sgreen	close(in);
24676259Sgreen	close(out);
24776259Sgreen	if (infile != stdin)
24876259Sgreen		fclose(infile);
24976259Sgreen
25098675Sdes	while (waitpid(sshpid, NULL, 0) == -1)
25198675Sdes		if (errno != EINTR)
25298675Sdes			fatal("Couldn't wait for ssh process: %s",
25398675Sdes			    strerror(errno));
25476259Sgreen
25576259Sgreen	exit(0);
25676259Sgreen}
257