sftp.c revision 92555
179455Sobrien/*
279455Sobrien * Copyright (c) 2001,2002 Damien Miller.  All rights reserved.
379455Sobrien *
479455Sobrien * Redistribution and use in source and binary forms, with or without
579455Sobrien * modification, are permitted provided that the following conditions
679455Sobrien * are met:
779455Sobrien * 1. Redistributions of source code must retain the above copyright
879455Sobrien *    notice, this list of conditions and the following disclaimer.
979455Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1079455Sobrien *    notice, this list of conditions and the following disclaimer in the
1179455Sobrien *    documentation and/or other materials provided with the distribution.
1279455Sobrien *
1379455Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1479455Sobrien * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1579455Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1679455Sobrien * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1779455Sobrien * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1879455Sobrien * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1979455Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2079455Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2179455Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2279455Sobrien * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2379455Sobrien */
2479455Sobrien
2579455Sobrien#include "includes.h"
2679455Sobrien
2779455SobrienRCSID("$OpenBSD: sftp.c,v 1.26 2002/02/12 12:32:27 djm Exp $");
2879455Sobrien
29128463Stjr/* XXX: short-form remote directory listings (like 'ls -C') */
3079455Sobrien
3179455Sobrien#include "buffer.h"
3279455Sobrien#include "xmalloc.h"
3379455Sobrien#include "log.h"
3479455Sobrien#include "pathnames.h"
3579455Sobrien#include "misc.h"
3679455Sobrien
3779455Sobrien#include "sftp.h"
3879455Sobrien#include "sftp-common.h"
3979455Sobrien#include "sftp-client.h"
4079455Sobrien#include "sftp-int.h"
4179455Sobrien
4279455SobrienFILE* infile;
43203872Skibsize_t copy_buffer_len = 32768;
4479455Sobriensize_t num_requests = 16;
4579455Sobrien
4679455Sobrienstatic void
4779455Sobrienconnect_to_server(char *path, char **args, int *in, int *out, pid_t *sshpid)
4879455Sobrien{
49203872Skib	int c_in, c_out;
5079455Sobrien#ifdef USE_PIPES
51203872Skib	int pin[2], pout[2];
5279455Sobrien	if ((pipe(pin) == -1) || (pipe(pout) == -1))
5379455Sobrien		fatal("pipe: %s", strerror(errno));
5479455Sobrien	*in = pin[0];
5579455Sobrien	*out = pout[1];
5679455Sobrien	c_in = pout[0];
5779455Sobrien	c_out = pin[1];
5879455Sobrien#else /* USE_PIPES */
5979455Sobrien	int inout[2];
6079455Sobrien	if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
6179455Sobrien		fatal("socketpair: %s", strerror(errno));
6279455Sobrien	*in = *out = inout[0];
6379455Sobrien	c_in = c_out = inout[1];
6479455Sobrien#endif /* USE_PIPES */
6579455Sobrien
6679455Sobrien	if ((*sshpid = fork()) == -1)
6779455Sobrien		fatal("fork: %s", strerror(errno));
6879455Sobrien	else if (*sshpid == 0) {
6979455Sobrien		if ((dup2(c_in, STDIN_FILENO) == -1) ||
7079455Sobrien		    (dup2(c_out, STDOUT_FILENO) == -1)) {
7179455Sobrien			fprintf(stderr, "dup2: %s\n", strerror(errno));
7279455Sobrien			exit(1);
7379455Sobrien		}
7479455Sobrien		close(*in);
7579455Sobrien		close(*out);
7679455Sobrien		close(c_in);
7779455Sobrien		close(c_out);
7879455Sobrien		execv(path, args);
7979455Sobrien		fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
8079455Sobrien		exit(1);
8179455Sobrien	}
8279455Sobrien
8379455Sobrien	close(c_in);
8479455Sobrien	close(c_out);
8579455Sobrien}
8679455Sobrien
8779455Sobrienstatic void
8879455Sobrienusage(void)
8979455Sobrien{
9079455Sobrien	extern char *__progname;
91102231Strhodes
9279455Sobrien	fprintf(stderr,
9379455Sobrien	    "usage: %s [-vC1] [-b batchfile] [-o option] [-s subsystem|path] [-B buffer_size]\n"
9479455Sobrien	    "            [-F config] [-P direct server path] [-S program]\n"
9579455Sobrien	    "            [user@]host[:file [file]]\n", __progname);
9679455Sobrien	exit(1);
9779455Sobrien}
9879455Sobrien
9979455Sobrienint
10079455Sobrienmain(int argc, char **argv)
10179455Sobrien{
10279455Sobrien	int in, out, ch;
10379455Sobrien	pid_t sshpid;
10479455Sobrien	char *host, *userhost, *cp, *file2;
10579455Sobrien	int debug_level = 0, sshver = 2;
10679455Sobrien	char *file1 = NULL, *sftp_server = NULL;
10779455Sobrien	char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
10879455Sobrien	LogLevel ll = SYSLOG_LEVEL_INFO;
10979455Sobrien	arglist args;
11079455Sobrien	extern int optind;
11179455Sobrien	extern char *optarg;
11279455Sobrien
11379455Sobrien	args.list = NULL;
11479455Sobrien	addargs(&args, "ssh");		/* overwritten with ssh_program */
11579455Sobrien	addargs(&args, "-oFallBackToRsh no");
11679455Sobrien	addargs(&args, "-oForwardX11 no");
117175853Syar	addargs(&args, "-oForwardAgent no");
118175853Syar	addargs(&args, "-oClearAllForwardings yes");
11979455Sobrien	ll = SYSLOG_LEVEL_INFO;
12079455Sobrien	infile = stdin;		/* Read from STDIN unless changed by -b */
12179455Sobrien
12279455Sobrien	while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) {
12379455Sobrien		switch (ch) {
12479455Sobrien		case 'C':
12579455Sobrien			addargs(&args, "-C");
12679455Sobrien			break;
12779455Sobrien		case 'v':
12879455Sobrien			if (debug_level < 3) {
12979455Sobrien				addargs(&args, "-v");
13079455Sobrien				ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
13179455Sobrien			}
13279455Sobrien			debug_level++;
13379455Sobrien			break;
13479455Sobrien		case 'F':
13579455Sobrien		case 'o':
13679455Sobrien			addargs(&args, "-%c%s", ch, optarg);
13779455Sobrien			break;
13879455Sobrien		case '1':
13979455Sobrien			sshver = 1;
14079455Sobrien			if (sftp_server == NULL)
14179455Sobrien				sftp_server = _PATH_SFTP_SERVER;
14279455Sobrien			break;
14379455Sobrien		case 's':
14479455Sobrien			sftp_server = optarg;
14579455Sobrien			break;
14679455Sobrien		case 'S':
14779455Sobrien			ssh_program = optarg;
14879455Sobrien			break;
14979455Sobrien		case 'b':
15079455Sobrien			if (infile == stdin) {
15179455Sobrien				infile = fopen(optarg, "r");
15279455Sobrien				if (infile == NULL)
153128463Stjr					fatal("%s (%s).", strerror(errno), optarg);
154128463Stjr			} else
155203872Skib				fatal("Filename already specified.");
156203872Skib			break;
157203872Skib		case 'P':
158203872Skib			sftp_direct = optarg;
159203872Skib			break;
160203872Skib		case 'B':
161203872Skib			copy_buffer_len = strtol(optarg, &cp, 10);
162203872Skib			if (copy_buffer_len == 0 || *cp != '\0')
163203872Skib				fatal("Invalid buffer size \"%s\"", optarg);
164203872Skib			break;
165203872Skib		case 'R':
166203872Skib			num_requests = strtol(optarg, &cp, 10);
167203872Skib			if (num_requests == 0 || *cp != '\0')
168203872Skib				fatal("Invalid number of requests \"%s\"",
169203872Skib				    optarg);
170203872Skib			break;
17179455Sobrien		case 'h':
17279455Sobrien		default:
17379455Sobrien			usage();
17479455Sobrien		}
17579455Sobrien	}
17679455Sobrien
17779455Sobrien	if (sftp_direct == NULL) {
17879455Sobrien		if (optind == argc || argc > (optind + 2))
17979455Sobrien			usage();
18079455Sobrien
18179455Sobrien		userhost = xstrdup(argv[optind]);
18279455Sobrien		file2 = argv[optind+1];
18379455Sobrien
18479455Sobrien		if ((cp = colon(userhost)) != NULL) {
18579455Sobrien			*cp++ = '\0';
18679455Sobrien			file1 = cp;
18779455Sobrien		}
18879455Sobrien
18979455Sobrien		if ((host = strchr(userhost, '@')) == NULL)
19079455Sobrien			host = userhost;
19179455Sobrien		else {
19279455Sobrien			*host++ = '\0';
19379455Sobrien			if (!userhost[0]) {
19479455Sobrien				fprintf(stderr, "Missing username\n");
19579455Sobrien				usage();
19679455Sobrien			}
19779455Sobrien			addargs(&args, "-l%s",userhost);
19879455Sobrien		}
19979455Sobrien
20079455Sobrien		host = cleanhostname(host);
20179455Sobrien		if (!*host) {
20279455Sobrien			fprintf(stderr, "Missing hostname\n");
20379455Sobrien			usage();
20479455Sobrien		}
20579455Sobrien
20679455Sobrien		log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
20779455Sobrien		addargs(&args, "-oProtocol %d", sshver);
20879455Sobrien
20979455Sobrien		/* no subsystem if the server-spec contains a '/' */
21079455Sobrien		if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
21179455Sobrien			addargs(&args, "-s");
21279455Sobrien
21379455Sobrien		addargs(&args, "%s", host);
21479455Sobrien		addargs(&args, "%s", (sftp_server != NULL ?
21579455Sobrien		    sftp_server : "sftp"));
21679455Sobrien		args.list[0] = ssh_program;
21779455Sobrien
21879455Sobrien		fprintf(stderr, "Connecting to %s...\n", host);
21979455Sobrien		connect_to_server(ssh_program, args.list, &in, &out,
22079455Sobrien		    &sshpid);
22179455Sobrien	} else {
22279455Sobrien		args.list = NULL;
22379455Sobrien		addargs(&args, "sftp-server");
22479455Sobrien
22579455Sobrien		fprintf(stderr, "Attaching to %s...\n", sftp_direct);
22679455Sobrien		connect_to_server(sftp_direct, args.list, &in, &out,
22779455Sobrien		    &sshpid);
22879455Sobrien	}
22979455Sobrien
23079455Sobrien	interactive_loop(in, out, file1, file2);
23179455Sobrien
23279455Sobrien	close(in);
23379455Sobrien	close(out);
234203872Skib	if (infile != stdin)
23579455Sobrien		fclose(infile);
23679455Sobrien
23779455Sobrien	if (waitpid(sshpid, NULL, 0) == -1)
23879455Sobrien		fatal("Couldn't wait for ssh process: %s", strerror(errno));
23979455Sobrien
24079455Sobrien	exit(0);
24179455Sobrien}
24279455Sobrien