sftp.c revision 99060
150397Sobrien/*
2117395Skan * Copyright (c) 2001,2002 Damien Miller.  All rights reserved.
3117395Skan *
450397Sobrien * Redistribution and use in source and binary forms, with or without
550397Sobrien * modification, are permitted provided that the following conditions
690075Sobrien * are met:
750397Sobrien * 1. Redistributions of source code must retain the above copyright
890075Sobrien *    notice, this list of conditions and the following disclaimer.
990075Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1090075Sobrien *    notice, this list of conditions and the following disclaimer in the
1190075Sobrien *    documentation and/or other materials provided with the distribution.
1250397Sobrien *
1390075Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1490075Sobrien * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1590075Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1690075Sobrien * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1750397Sobrien * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1850397Sobrien * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1990075Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2090075Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2190075Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2250397Sobrien * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2350397Sobrien */
2450397Sobrien
2550397Sobrien#include "includes.h"
2690075Sobrien
2790075SobrienRCSID("$OpenBSD: sftp.c,v 1.30 2002/06/23 09:30:14 deraadt Exp $");
2890075Sobrien
2950397Sobrien/* XXX: short-form remote directory listings (like 'ls -C') */
3050397Sobrien
3150397Sobrien#include "buffer.h"
3290075Sobrien#include "xmalloc.h"
3350397Sobrien#include "log.h"
3452284Sobrien#include "pathnames.h"
3550397Sobrien#include "misc.h"
3690075Sobrien
3752284Sobrien#include "sftp.h"
3890075Sobrien#include "sftp-common.h"
3990075Sobrien#include "sftp-client.h"
40117395Skan#include "sftp-int.h"
4150397Sobrien
4252284Sobrien#ifdef HAVE___PROGNAME
4352284Sobrienextern char *__progname;
4490075Sobrien#else
4590075Sobrienchar *__progname;
4652284Sobrien#endif
4752284Sobrien
4852284SobrienFILE* infile;
4952284Sobriensize_t copy_buffer_len = 32768;
5052284Sobriensize_t num_requests = 16;
5152284Sobrien
5252284Sobrienstatic void
5352284Sobrienconnect_to_server(char *path, char **args, int *in, int *out, pid_t *sshpid)
5452284Sobrien{
5552284Sobrien	int c_in, c_out;
5652284Sobrien
5752284Sobrien#ifdef USE_PIPES
5852284Sobrien	int pin[2], pout[2];
5990075Sobrien
6090075Sobrien	if ((pipe(pin) == -1) || (pipe(pout) == -1))
6190075Sobrien		fatal("pipe: %s", strerror(errno));
6252284Sobrien	*in = pin[0];
6390075Sobrien	*out = pout[1];
6490075Sobrien	c_in = pout[0];
6590075Sobrien	c_out = pin[1];
6690075Sobrien#else /* USE_PIPES */
6790075Sobrien	int inout[2];
6852284Sobrien
6952284Sobrien	if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
7052284Sobrien		fatal("socketpair: %s", strerror(errno));
7152284Sobrien	*in = *out = inout[0];
7290075Sobrien	c_in = c_out = inout[1];
7390075Sobrien#endif /* USE_PIPES */
7452284Sobrien
7590075Sobrien	if ((*sshpid = fork()) == -1)
7652284Sobrien		fatal("fork: %s", strerror(errno));
7752284Sobrien	else if (*sshpid == 0) {
7890075Sobrien		if ((dup2(c_in, STDIN_FILENO) == -1) ||
7952284Sobrien		    (dup2(c_out, STDOUT_FILENO) == -1)) {
80117395Skan			fprintf(stderr, "dup2: %s\n", strerror(errno));
8152284Sobrien			exit(1);
8252284Sobrien		}
8352284Sobrien		close(*in);
8452284Sobrien		close(*out);
8552284Sobrien		close(c_in);
8690075Sobrien		close(c_out);
8790075Sobrien		execv(path, args);
8890075Sobrien		fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
8990075Sobrien		exit(1);
9050397Sobrien	}
9190075Sobrien
9290075Sobrien	close(c_in);
9390075Sobrien	close(c_out);
9490075Sobrien}
9590075Sobrien
9690075Sobrienstatic void
9790075Sobrienusage(void)
9890075Sobrien{
9990075Sobrien	extern char *__progname;
10090075Sobrien
10190075Sobrien	fprintf(stderr,
10290075Sobrien	    "usage: %s [-vC1] [-b batchfile] [-o option] [-s subsystem|path] [-B buffer_size]\n"
10390075Sobrien	    "            [-F config] [-P direct server path] [-S program]\n"
10490075Sobrien	    "            [user@]host[:file [file]]\n", __progname);
10590075Sobrien	exit(1);
10690075Sobrien}
10790075Sobrien
10890075Sobrienint
10990075Sobrienmain(int argc, char **argv)
11090075Sobrien{
11190075Sobrien	int in, out, ch;
11290075Sobrien	pid_t sshpid;
113117395Skan	char *host, *userhost, *cp, *file2;
114117395Skan	int debug_level = 0, sshver = 2;
11590075Sobrien	char *file1 = NULL, *sftp_server = NULL;
116117395Skan	char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
117117395Skan	LogLevel ll = SYSLOG_LEVEL_INFO;
118117395Skan	arglist args;
119117395Skan	extern int optind;
12090075Sobrien	extern char *optarg;
12150397Sobrien
12250397Sobrien	__progname = get_progname(argv[0]);
12390075Sobrien	args.list = NULL;
12450397Sobrien	addargs(&args, "ssh");		/* overwritten with ssh_program */
12550397Sobrien	addargs(&args, "-oFallBackToRsh no");
12650397Sobrien	addargs(&args, "-oForwardX11 no");
12752284Sobrien	addargs(&args, "-oForwardAgent no");
12852284Sobrien	addargs(&args, "-oClearAllForwardings yes");
12952284Sobrien	ll = SYSLOG_LEVEL_INFO;
13052284Sobrien	infile = stdin;		/* Read from STDIN unless changed by -b */
13152284Sobrien
13250397Sobrien	while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) {
13350397Sobrien		switch (ch) {
13490075Sobrien		case 'C':
13550397Sobrien			addargs(&args, "-C");
136117395Skan			break;
13750397Sobrien		case 'v':
13850397Sobrien			if (debug_level < 3) {
13950397Sobrien				addargs(&args, "-v");
14050397Sobrien				ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
14150397Sobrien			}
14250397Sobrien			debug_level++;
14350397Sobrien			break;
14450397Sobrien		case 'F':
145117395Skan		case 'o':
14650397Sobrien			addargs(&args, "-%c%s", ch, optarg);
14790075Sobrien			break;
14890075Sobrien		case '1':
14990075Sobrien			sshver = 1;
15090075Sobrien			if (sftp_server == NULL)
15190075Sobrien				sftp_server = _PATH_SFTP_SERVER;
15290075Sobrien			break;
15390075Sobrien		case 's':
15490075Sobrien			sftp_server = optarg;
15590075Sobrien			break;
156117395Skan		case 'S':
15790075Sobrien			ssh_program = optarg;
15890075Sobrien			break;
15990075Sobrien		case 'b':
160117395Skan			if (infile == stdin) {
161117395Skan				infile = fopen(optarg, "r");
162117395Skan				if (infile == NULL)
163117395Skan					fatal("%s (%s).", strerror(errno), optarg);
16450397Sobrien			} else
16590075Sobrien				fatal("Filename already specified.");
16690075Sobrien			break;
16750397Sobrien		case 'P':
16850397Sobrien			sftp_direct = optarg;
16950397Sobrien			break;
17050397Sobrien		case 'B':
17150397Sobrien			copy_buffer_len = strtol(optarg, &cp, 10);
17250397Sobrien			if (copy_buffer_len == 0 || *cp != '\0')
17350397Sobrien				fatal("Invalid buffer size \"%s\"", optarg);
17450397Sobrien			break;
17550397Sobrien		case 'R':
17650397Sobrien			num_requests = strtol(optarg, &cp, 10);
17750397Sobrien			if (num_requests == 0 || *cp != '\0')
17890075Sobrien				fatal("Invalid number of requests \"%s\"",
17990075Sobrien				    optarg);
18090075Sobrien			break;
18190075Sobrien		case 'h':
18250397Sobrien		default:
18350397Sobrien			usage();
18450397Sobrien		}
18590075Sobrien	}
18650397Sobrien
18750397Sobrien	log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
18850397Sobrien
18950397Sobrien	if (sftp_direct == NULL) {
19090075Sobrien		if (optind == argc || argc > (optind + 2))
19190075Sobrien			usage();
19290075Sobrien
19390075Sobrien		userhost = xstrdup(argv[optind]);
19490075Sobrien		file2 = argv[optind+1];
19590075Sobrien
19690075Sobrien		if ((cp = colon(userhost)) != NULL) {
19790075Sobrien			*cp++ = '\0';
19890075Sobrien			file1 = cp;
19950397Sobrien		}
20050397Sobrien
20150397Sobrien		if ((host = strchr(userhost, '@')) == NULL)
20250397Sobrien			host = userhost;
203117395Skan		else {
20450397Sobrien			*host++ = '\0';
20552284Sobrien			if (!userhost[0]) {
20652284Sobrien				fprintf(stderr, "Missing username\n");
20790075Sobrien				usage();
20852284Sobrien			}
20952284Sobrien			addargs(&args, "-l%s",userhost);
21052284Sobrien		}
21152284Sobrien
21252284Sobrien		host = cleanhostname(host);
21390075Sobrien		if (!*host) {
21452284Sobrien			fprintf(stderr, "Missing hostname\n");
21590075Sobrien			usage();
21690075Sobrien		}
21752284Sobrien
21890075Sobrien		addargs(&args, "-oProtocol %d", sshver);
21952284Sobrien
22052284Sobrien		/* no subsystem if the server-spec contains a '/' */
22190075Sobrien		if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
22290075Sobrien			addargs(&args, "-s");
22352284Sobrien
224117395Skan		addargs(&args, "%s", host);
22552284Sobrien		addargs(&args, "%s", (sftp_server != NULL ?
22652284Sobrien		    sftp_server : "sftp"));
22752284Sobrien		args.list[0] = ssh_program;
22852284Sobrien
229117395Skan		fprintf(stderr, "Connecting to %s...\n", host);
23052284Sobrien		connect_to_server(ssh_program, args.list, &in, &out,
23152284Sobrien		    &sshpid);
23252284Sobrien	} else {
23352284Sobrien		args.list = NULL;
23452284Sobrien		addargs(&args, "sftp-server");
23552284Sobrien
23652284Sobrien		fprintf(stderr, "Attaching to %s...\n", sftp_direct);
23790075Sobrien		connect_to_server(sftp_direct, args.list, &in, &out,
23890075Sobrien		    &sshpid);
23952284Sobrien	}
24052284Sobrien
24152284Sobrien	interactive_loop(in, out, file1, file2);
24290075Sobrien
24390075Sobrien#if !defined(USE_PIPES)
24490075Sobrien	shutdown(in, SHUT_RDWR);
24590075Sobrien	shutdown(out, SHUT_RDWR);
24690075Sobrien#endif
24790075Sobrien
24890075Sobrien	close(in);
24990075Sobrien	close(out);
25090075Sobrien	if (infile != stdin)
25190075Sobrien		fclose(infile);
25290075Sobrien
25390075Sobrien	while (waitpid(sshpid, NULL, 0) == -1)
25490075Sobrien		if (errno != EINTR)
25590075Sobrien			fatal("Couldn't wait for ssh process: %s",
25690075Sobrien			    strerror(errno));
25790075Sobrien
25890075Sobrien	exit(0);
25990075Sobrien}
26090075Sobrien