sftp.c revision 98675
1184588Sdfr/*
2184588Sdfr * Copyright (c) 2001,2002 Damien Miller.  All rights reserved.
3184588Sdfr *
4184588Sdfr * Redistribution and use in source and binary forms, with or without
5184588Sdfr * modification, are permitted provided that the following conditions
6184588Sdfr * are met:
7184588Sdfr * 1. Redistributions of source code must retain the above copyright
8184588Sdfr *    notice, this list of conditions and the following disclaimer.
9184588Sdfr * 2. Redistributions in binary form must reproduce the above copyright
10184588Sdfr *    notice, this list of conditions and the following disclaimer in the
11184588Sdfr *    documentation and/or other materials provided with the distribution.
12184588Sdfr *
13184588Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14184588Sdfr * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15184588Sdfr * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16184588Sdfr * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17184588Sdfr * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18184588Sdfr * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19184588Sdfr * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20184588Sdfr * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21184588Sdfr * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22184588Sdfr * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23184588Sdfr */
24184588Sdfr
25184588Sdfr#include "includes.h"
26184588Sdfr
27184588SdfrRCSID("$OpenBSD: sftp.c,v 1.29 2002/04/02 17:37:48 markus Exp $");
28184588Sdfr
29184588Sdfr/* XXX: short-form remote directory listings (like 'ls -C') */
30184588Sdfr
31184588Sdfr#include "buffer.h"
32184588Sdfr#include "xmalloc.h"
33184588Sdfr#include "log.h"
34184588Sdfr#include "pathnames.h"
35184588Sdfr#include "misc.h"
36184588Sdfr
37184588Sdfr#include "sftp.h"
38184588Sdfr#include "sftp-common.h"
39184588Sdfr#include "sftp-client.h"
40184588Sdfr#include "sftp-int.h"
41184588Sdfr
42184588SdfrFILE* infile;
43184588Sdfrsize_t copy_buffer_len = 32768;
44184588Sdfrsize_t num_requests = 16;
45184588Sdfr
46184588Sdfrstatic void
47184588Sdfrconnect_to_server(char *path, char **args, int *in, int *out, pid_t *sshpid)
48184588Sdfr{
49184588Sdfr	int c_in, c_out;
50184588Sdfr#ifdef USE_PIPES
51184588Sdfr	int pin[2], pout[2];
52184588Sdfr	if ((pipe(pin) == -1) || (pipe(pout) == -1))
53184588Sdfr		fatal("pipe: %s", strerror(errno));
54184588Sdfr	*in = pin[0];
55184588Sdfr	*out = pout[1];
56184588Sdfr	c_in = pout[0];
57184588Sdfr	c_out = pin[1];
58184588Sdfr#else /* USE_PIPES */
59184588Sdfr	int inout[2];
60184588Sdfr	if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
61184588Sdfr		fatal("socketpair: %s", strerror(errno));
62184588Sdfr	*in = *out = inout[0];
63184588Sdfr	c_in = c_out = inout[1];
64184588Sdfr#endif /* USE_PIPES */
65184588Sdfr
66184588Sdfr	if ((*sshpid = fork()) == -1)
67184588Sdfr		fatal("fork: %s", strerror(errno));
68184588Sdfr	else if (*sshpid == 0) {
69184588Sdfr		if ((dup2(c_in, STDIN_FILENO) == -1) ||
70184588Sdfr		    (dup2(c_out, STDOUT_FILENO) == -1)) {
71184588Sdfr			fprintf(stderr, "dup2: %s\n", strerror(errno));
72184588Sdfr			exit(1);
73184588Sdfr		}
74184588Sdfr		close(*in);
75184588Sdfr		close(*out);
76184588Sdfr		close(c_in);
77184588Sdfr		close(c_out);
78184588Sdfr		execv(path, args);
79184588Sdfr		fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
80184588Sdfr		exit(1);
81184588Sdfr	}
82184588Sdfr
83184588Sdfr	close(c_in);
84184588Sdfr	close(c_out);
85184588Sdfr}
86184588Sdfr
87184588Sdfrstatic void
88184588Sdfrusage(void)
89184588Sdfr{
90184588Sdfr	extern char *__progname;
91184588Sdfr
92184588Sdfr	fprintf(stderr,
93184588Sdfr	    "usage: %s [-vC1] [-b batchfile] [-o option] [-s subsystem|path] [-B buffer_size]\n"
94184588Sdfr	    "            [-F config] [-P direct server path] [-S program]\n"
95184588Sdfr	    "            [user@]host[:file [file]]\n", __progname);
96184588Sdfr	exit(1);
97184588Sdfr}
98184588Sdfr
99184588Sdfrint
100184588Sdfrmain(int argc, char **argv)
101184588Sdfr{
102184588Sdfr	int in, out, ch;
103184588Sdfr	pid_t sshpid;
104184588Sdfr	char *host, *userhost, *cp, *file2;
105184588Sdfr	int debug_level = 0, sshver = 2;
106184588Sdfr	char *file1 = NULL, *sftp_server = NULL;
107184588Sdfr	char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
108184588Sdfr	LogLevel ll = SYSLOG_LEVEL_INFO;
109184588Sdfr	arglist args;
110184588Sdfr	extern int optind;
111184588Sdfr	extern char *optarg;
112184588Sdfr
113184588Sdfr	args.list = NULL;
114184588Sdfr	addargs(&args, "ssh");		/* overwritten with ssh_program */
115184588Sdfr	addargs(&args, "-oFallBackToRsh no");
116184588Sdfr	addargs(&args, "-oForwardX11 no");
117184588Sdfr	addargs(&args, "-oForwardAgent no");
118184588Sdfr	addargs(&args, "-oClearAllForwardings yes");
119184588Sdfr	ll = SYSLOG_LEVEL_INFO;
120184588Sdfr	infile = stdin;		/* Read from STDIN unless changed by -b */
121184588Sdfr
122184588Sdfr	while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) {
123184588Sdfr		switch (ch) {
124184588Sdfr		case 'C':
125184588Sdfr			addargs(&args, "-C");
126184588Sdfr			break;
127184588Sdfr		case 'v':
128184588Sdfr			if (debug_level < 3) {
129184588Sdfr				addargs(&args, "-v");
130184588Sdfr				ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
131184588Sdfr			}
132184588Sdfr			debug_level++;
133184588Sdfr			break;
134184588Sdfr		case 'F':
135184588Sdfr		case 'o':
136184588Sdfr			addargs(&args, "-%c%s", ch, optarg);
137184588Sdfr			break;
138184588Sdfr		case '1':
139184588Sdfr			sshver = 1;
140184588Sdfr			if (sftp_server == NULL)
141184588Sdfr				sftp_server = _PATH_SFTP_SERVER;
142184588Sdfr			break;
143184588Sdfr		case 's':
144184588Sdfr			sftp_server = optarg;
145184588Sdfr			break;
146184588Sdfr		case 'S':
147184588Sdfr			ssh_program = optarg;
148184588Sdfr			break;
149184588Sdfr		case 'b':
150184588Sdfr			if (infile == stdin) {
151184588Sdfr				infile = fopen(optarg, "r");
152184588Sdfr				if (infile == NULL)
153184588Sdfr					fatal("%s (%s).", strerror(errno), optarg);
154184588Sdfr			} else
155184588Sdfr				fatal("Filename already specified.");
156184588Sdfr			break;
157184588Sdfr		case 'P':
158184588Sdfr			sftp_direct = optarg;
159184588Sdfr			break;
160184588Sdfr		case 'B':
161184588Sdfr			copy_buffer_len = strtol(optarg, &cp, 10);
162184588Sdfr			if (copy_buffer_len == 0 || *cp != '\0')
163184588Sdfr				fatal("Invalid buffer size \"%s\"", optarg);
164184588Sdfr			break;
165184588Sdfr		case 'R':
166184588Sdfr			num_requests = strtol(optarg, &cp, 10);
167184588Sdfr			if (num_requests == 0 || *cp != '\0')
168184588Sdfr				fatal("Invalid number of requests \"%s\"",
169184588Sdfr				    optarg);
170184588Sdfr			break;
171184588Sdfr		case 'h':
172184588Sdfr		default:
173184588Sdfr			usage();
174184588Sdfr		}
175184588Sdfr	}
176184588Sdfr
177184588Sdfr	log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
178184588Sdfr
179184588Sdfr	if (sftp_direct == NULL) {
180184588Sdfr		if (optind == argc || argc > (optind + 2))
181184588Sdfr			usage();
182184588Sdfr
183184588Sdfr		userhost = xstrdup(argv[optind]);
184184588Sdfr		file2 = argv[optind+1];
185184588Sdfr
186184588Sdfr		if ((cp = colon(userhost)) != NULL) {
187184588Sdfr			*cp++ = '\0';
188184588Sdfr			file1 = cp;
189184588Sdfr		}
190184588Sdfr
191184588Sdfr		if ((host = strchr(userhost, '@')) == NULL)
192184588Sdfr			host = userhost;
193184588Sdfr		else {
194184588Sdfr			*host++ = '\0';
195184588Sdfr			if (!userhost[0]) {
196184588Sdfr				fprintf(stderr, "Missing username\n");
197184588Sdfr				usage();
198184588Sdfr			}
199184588Sdfr			addargs(&args, "-l%s",userhost);
200184588Sdfr		}
201184588Sdfr
202193066Sjamie		host = cleanhostname(host);
203184588Sdfr		if (!*host) {
204184588Sdfr			fprintf(stderr, "Missing hostname\n");
205184588Sdfr			usage();
206184588Sdfr		}
207184588Sdfr
208184588Sdfr		addargs(&args, "-oProtocol %d", sshver);
209184588Sdfr
210184588Sdfr		/* no subsystem if the server-spec contains a '/' */
211184588Sdfr		if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
212184588Sdfr			addargs(&args, "-s");
213184588Sdfr
214184588Sdfr		addargs(&args, "%s", host);
215184588Sdfr		addargs(&args, "%s", (sftp_server != NULL ?
216184588Sdfr		    sftp_server : "sftp"));
217184588Sdfr		args.list[0] = ssh_program;
218184588Sdfr
219184588Sdfr		fprintf(stderr, "Connecting to %s...\n", host);
220184588Sdfr		connect_to_server(ssh_program, args.list, &in, &out,
221184588Sdfr		    &sshpid);
222184588Sdfr	} else {
223184588Sdfr		args.list = NULL;
224184588Sdfr		addargs(&args, "sftp-server");
225184588Sdfr
226184588Sdfr		fprintf(stderr, "Attaching to %s...\n", sftp_direct);
227184588Sdfr		connect_to_server(sftp_direct, args.list, &in, &out,
228184588Sdfr		    &sshpid);
229184588Sdfr	}
230193066Sjamie
231193066Sjamie	interactive_loop(in, out, file1, file2);
232184588Sdfr
233184588Sdfr	close(in);
234184588Sdfr	close(out);
235184588Sdfr	if (infile != stdin)
236184588Sdfr		fclose(infile);
237184588Sdfr
238184588Sdfr	while (waitpid(sshpid, NULL, 0) == -1)
239184588Sdfr		if (errno != EINTR)
240184588Sdfr			fatal("Couldn't wait for ssh process: %s",
241184588Sdfr			    strerror(errno));
242184588Sdfr
243184588Sdfr	exit(0);
244184588Sdfr}
245184588Sdfr