sftp.c revision 76259
167967Sasmodai/*
214053Smpp * Copyright (c) 2001 Damien Miller.  All rights reserved.
314053Smpp *
414053Smpp * Redistribution and use in source and binary forms, with or without
514053Smpp * modification, are permitted provided that the following conditions
614053Smpp * are met:
714053Smpp * 1. Redistributions of source code must retain the above copyright
814053Smpp *    notice, this list of conditions and the following disclaimer.
914053Smpp * 2. Redistributions in binary form must reproduce the above copyright
1014053Smpp *    notice, this list of conditions and the following disclaimer in the
1114053Smpp *    documentation and/or other materials provided with the distribution.
1214053Smpp *
1314053Smpp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1414053Smpp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1514053Smpp * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1614053Smpp * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1714053Smpp * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1814053Smpp * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1914053Smpp * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2014053Smpp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2114053Smpp * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2214053Smpp * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2314053Smpp */
2414053Smpp
2514053Smpp#include "includes.h"
2614053Smpp
2714053SmppRCSID("$OpenBSD: sftp.c,v 1.15 2001/04/16 02:31:44 mouring Exp $");
2814053Smpp
2914053Smpp/* XXX: commandline mode */
3014053Smpp/* XXX: short-form remote directory listings (like 'ls -C') */
3150476Speter
3231370Sbde#include "buffer.h"
3314053Smpp#include "xmalloc.h"
34195656Strasz#include "log.h"
35195656Strasz#include "pathnames.h"
3679531Sru
3714053Smpp#include "sftp.h"
3814053Smpp#include "sftp-common.h"
3914053Smpp#include "sftp-client.h"
4059460Sphantom#include "sftp-int.h"
4159460Sphantom
4214053Smpp#include "scp-common.h"
4384306Sru
4484306Sruint use_ssh1 = 0;
4584306Sruchar *ssh_program = _PATH_SSH_PROGRAM;
4614053Smppchar *sftp_server = NULL;
4714053SmppFILE* infile;
4814053Smpp
4967967Sasmodaivoid
5014053Smppconnect_to_server(char **args, int *in, int *out, pid_t *sshpid)
5114053Smpp{
5214053Smpp	int c_in, c_out;
5314053Smpp#ifdef USE_PIPES
5414053Smpp	int pin[2], pout[2];
5514053Smpp	if ((pipe(pin) == -1) || (pipe(pout) == -1))
5614053Smpp		fatal("pipe: %s", strerror(errno));
5714053Smpp	*in = pin[0];
5814053Smpp	*out = pout[1];
5914053Smpp	c_in = pout[0];
6024093Smpp	c_out = pin[1];
6114053Smpp#else /* USE_PIPES */
6214053Smpp	int inout[2];
6314053Smpp	if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
6424093Smpp		fatal("socketpair: %s", strerror(errno));
6524093Smpp	*in = *out = inout[0];
6624093Smpp	c_in = c_out = inout[1];
6714053Smpp#endif /* USE_PIPES */
6814053Smpp
6914053Smpp	if ((*sshpid = fork()) == -1)
7014053Smpp		fatal("fork: %s", strerror(errno));
7114053Smpp	else if (*sshpid == 0) {
72108087Sru		if ((dup2(c_in, STDIN_FILENO) == -1) ||
7314053Smpp		    (dup2(c_out, STDOUT_FILENO) == -1)) {
74108087Sru			fprintf(stderr, "dup2: %s\n", strerror(errno));
75131504Sru			exit(1);
76131504Sru		}
7714053Smpp		close(*in);
7814053Smpp		close(*out);
7914053Smpp		close(c_in);
80108087Sru		close(c_out);
8114053Smpp		execv(ssh_program, args);
82108087Sru		fprintf(stderr, "exec: %s: %s\n", ssh_program, strerror(errno));
83131504Sru		exit(1);
84131504Sru	}
8514053Smpp
86108087Sru	close(c_in);
8714053Smpp	close(c_out);
88108087Sru}
89131504Sru
90131504Sruchar **
9114053Smppmake_ssh_args(char *add_arg)
9214053Smpp{
9314053Smpp	static char **args = NULL;
9414053Smpp	static int nargs = 0;
9524093Smpp	char debug_buf[4096];
96108087Sru	int i;
9714053Smpp
98108087Sru	/* Init args array */
9957686Ssheldonh	if (args == NULL) {
10057686Ssheldonh		nargs = 2;
10114053Smpp		i = 0;
10214053Smpp		args = xmalloc(sizeof(*args) * nargs);
10324093Smpp		args[i++] = "ssh";
10424093Smpp		args[i++] = NULL;
10524093Smpp	}
10614053Smpp
10714053Smpp	/* If asked to add args, then do so and return */
10824093Smpp	if (add_arg) {
10914053Smpp		i = nargs++ - 1;
11014053Smpp		args = xrealloc(args, sizeof(*args) * nargs);
11114053Smpp		args[i++] = add_arg;
11214053Smpp		args[i++] = NULL;
11324093Smpp		return(NULL);
11424093Smpp	}
11524093Smpp
11614053Smpp	/* no subsystem if the server-spec contains a '/' */
11757686Ssheldonh	if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
11857686Ssheldonh		make_ssh_args("-s");
11924093Smpp	make_ssh_args("-oForwardX11=no");
12024093Smpp	make_ssh_args("-oForwardAgent=no");
12114053Smpp	make_ssh_args(use_ssh1 ? "-oProtocol=1" : "-oProtocol=2");
12214053Smpp
12314053Smpp	/* Otherwise finish up and return the arg array */
12424093Smpp	if (sftp_server != NULL)
125147368Sru		make_ssh_args(sftp_server);
12624093Smpp	else
12724093Smpp		make_ssh_args("sftp");
12824093Smpp
12914053Smpp	/* XXX: overflow - doesn't grow debug_buf */
13014053Smpp	debug_buf[0] = '\0';
13114053Smpp	for(i = 0; args[i]; i++) {
13214053Smpp		if (i)
13314053Smpp			strlcat(debug_buf, " ", sizeof(debug_buf));
13414053Smpp
13514053Smpp		strlcat(debug_buf, args[i], sizeof(debug_buf));
13624093Smpp	}
13724093Smpp	debug("SSH args \"%s\"", debug_buf);
13814053Smpp
139131504Sru	return(args);
140131504Sru}
14114053Smpp
14224093Smppvoid
14324093Smppusage(void)
14414053Smpp{
14524093Smpp	fprintf(stderr, "usage: sftp [-1vC] [-b batchfile] [-osshopt=value] [user@]host[:file [file]]\n");
14614053Smpp	exit(1);
14714053Smpp}
14814053Smpp
14914053Smppint
15014053Smppmain(int argc, char **argv)
15114053Smpp{
15214053Smpp	int in, out, ch, debug_level, compress_flag;
15314053Smpp	pid_t sshpid;
15414053Smpp	char *file1 = NULL;
15514053Smpp	char *host, *userhost, *cp, *file2;
15614053Smpp	LogLevel ll;
15714053Smpp	extern int optind;
15814053Smpp	extern char *optarg;
15914053Smpp
16014053Smpp	infile = stdin;		/* Read from STDIN unless changed by -b */
16114053Smpp	debug_level = compress_flag = 0;
16270481Sru
16314053Smpp	while ((ch = getopt(argc, argv, "1hvCo:s:S:b:")) != -1) {
16414053Smpp		switch (ch) {
16514053Smpp		case 'C':
16614053Smpp			compress_flag = 1;
16714053Smpp			break;
16814053Smpp		case 'v':
16914053Smpp			debug_level = MIN(3, debug_level + 1);
17014053Smpp			break;
17114053Smpp		case 'o':
17214053Smpp			make_ssh_args("-o");
17314053Smpp			make_ssh_args(optarg);
174108030Sru			break;
17514053Smpp		case '1':
176108030Sru			use_ssh1 = 1;
17714053Smpp			if (sftp_server == NULL)
17814053Smpp				sftp_server = _PATH_SFTP_SERVER;
17914053Smpp			break;
180108087Sru		case 's':
18114053Smpp			sftp_server = optarg;
182108087Sru			break;
18324093Smpp		case 'S':
18424093Smpp			ssh_program = optarg;
18514053Smpp			break;
18614053Smpp		case 'b':
18714053Smpp			if (infile == stdin) {
18824093Smpp				infile = fopen(optarg, "r");
189108087Sru				if (infile == NULL)
19014053Smpp					fatal("%s (%s).", strerror(errno), optarg);
191108087Sru			} else
19214053Smpp				fatal("Filename already specified.");
19314053Smpp			break;
19414053Smpp		case 'h':
19514053Smpp		default:
19624093Smpp			usage();
19724093Smpp		}
19824093Smpp	}
19914053Smpp
20031370Sbde	if (optind == argc || argc > (optind + 2))
20114053Smpp		usage();
20214053Smpp
203108087Sru	userhost = xstrdup(argv[optind]);
20414053Smpp	file2 = argv[optind+1];
205108087Sru
20614053Smpp	if ((cp = colon(userhost)) != NULL) {
20714053Smpp		*cp++ = '\0';
20814053Smpp		file1 = cp;
209147368Sru	}
21014053Smpp
21124093Smpp	if ((host = strchr(userhost, '@')) == NULL)
21224093Smpp		host = userhost;
21324093Smpp	else {
21414053Smpp		*host++ = '\0';
21570481Sru		if (!userhost[0]) {
21614053Smpp			fprintf(stderr, "Missing username\n");
217195656Strasz			usage();
218195656Strasz		}
219195656Strasz		make_ssh_args("-l");
22014053Smpp		make_ssh_args(userhost);
22124093Smpp	}
22249266Smpp
223	host = cleanhostname(host);
224	if (!*host) {
225		fprintf(stderr, "Missing hostname\n");
226		usage();
227	}
228
229	/* Set up logging and debug '-d' arguments to ssh */
230	ll = SYSLOG_LEVEL_INFO;
231	switch (debug_level) {
232	case 1:
233		ll = SYSLOG_LEVEL_DEBUG1;
234		make_ssh_args("-v");
235		break;
236	case 2:
237		ll = SYSLOG_LEVEL_DEBUG2;
238		make_ssh_args("-v");
239		make_ssh_args("-v");
240		break;
241	case 3:
242		ll = SYSLOG_LEVEL_DEBUG3;
243		make_ssh_args("-v");
244		make_ssh_args("-v");
245		make_ssh_args("-v");
246		break;
247	}
248
249	if (compress_flag)
250		make_ssh_args("-C");
251
252	log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
253
254	make_ssh_args(host);
255
256	fprintf(stderr, "Connecting to %s...\n", host);
257
258	connect_to_server(make_ssh_args(NULL), &in, &out, &sshpid);
259
260	interactive_loop(in, out, file1, file2);
261
262	close(in);
263	close(out);
264	if (infile != stdin)
265		fclose(infile);
266
267	if (waitpid(sshpid, NULL, 0) == -1)
268		fatal("Couldn't wait for ssh process: %s", strerror(errno));
269
270	exit(0);
271}
272