sftp.c revision 76259
1314564Sdim/*
2280461Sdim * Copyright (c) 2001 Damien Miller.  All rights reserved.
3353358Sdim *
4353358Sdim * Redistribution and use in source and binary forms, with or without
5353358Sdim * modification, are permitted provided that the following conditions
6280461Sdim * are met:
7280461Sdim * 1. Redistributions of source code must retain the above copyright
8280461Sdim *    notice, this list of conditions and the following disclaimer.
9280461Sdim * 2. Redistributions in binary form must reproduce the above copyright
10280461Sdim *    notice, this list of conditions and the following disclaimer in the
11280461Sdim *    documentation and/or other materials provided with the distribution.
12314564Sdim *
13280461Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14280461Sdim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15314564Sdim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16280461Sdim * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17280461Sdim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18280461Sdim * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19280461Sdim * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20280461Sdim * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21280461Sdim * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22280461Sdim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23280461Sdim */
24280461Sdim
25280461Sdim#include "includes.h"
26280461Sdim
27303239SdimRCSID("$OpenBSD: sftp.c,v 1.15 2001/04/16 02:31:44 mouring Exp $");
28303239Sdim
29303239Sdim/* XXX: commandline mode */
30280461Sdim/* XXX: short-form remote directory listings (like 'ls -C') */
31280461Sdim
32280461Sdim#include "buffer.h"
33303239Sdim#include "xmalloc.h"
34280461Sdim#include "log.h"
35280461Sdim#include "pathnames.h"
36280461Sdim
37280461Sdim#include "sftp.h"
38280461Sdim#include "sftp-common.h"
39280461Sdim#include "sftp-client.h"
40280461Sdim#include "sftp-int.h"
41280461Sdim
42280461Sdim#include "scp-common.h"
43280461Sdim
44280461Sdimint use_ssh1 = 0;
45280461Sdimchar *ssh_program = _PATH_SSH_PROGRAM;
46280461Sdimchar *sftp_server = NULL;
47303239SdimFILE* infile;
48280461Sdim
49280461Sdimvoid
50280461Sdimconnect_to_server(char **args, int *in, int *out, pid_t *sshpid)
51280461Sdim{
52280461Sdim	int c_in, c_out;
53280461Sdim#ifdef USE_PIPES
54303239Sdim	int pin[2], pout[2];
55280461Sdim	if ((pipe(pin) == -1) || (pipe(pout) == -1))
56280461Sdim		fatal("pipe: %s", strerror(errno));
57280461Sdim	*in = pin[0];
58280461Sdim	*out = pout[1];
59280461Sdim	c_in = pout[0];
60280461Sdim	c_out = pin[1];
61280461Sdim#else /* USE_PIPES */
62280461Sdim	int inout[2];
63280461Sdim	if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
64280461Sdim		fatal("socketpair: %s", strerror(errno));
65280461Sdim	*in = *out = inout[0];
66280461Sdim	c_in = c_out = inout[1];
67280461Sdim#endif /* USE_PIPES */
68280461Sdim
69280461Sdim	if ((*sshpid = fork()) == -1)
70280461Sdim		fatal("fork: %s", strerror(errno));
71280461Sdim	else if (*sshpid == 0) {
72280461Sdim		if ((dup2(c_in, STDIN_FILENO) == -1) ||
73280461Sdim		    (dup2(c_out, STDOUT_FILENO) == -1)) {
74280461Sdim			fprintf(stderr, "dup2: %s\n", strerror(errno));
75280461Sdim			exit(1);
76280461Sdim		}
77280461Sdim		close(*in);
78280461Sdim		close(*out);
79280461Sdim		close(c_in);
80280461Sdim		close(c_out);
81280461Sdim		execv(ssh_program, args);
82280461Sdim		fprintf(stderr, "exec: %s: %s\n", ssh_program, strerror(errno));
83280461Sdim		exit(1);
84280461Sdim	}
85280461Sdim
86280461Sdim	close(c_in);
87280461Sdim	close(c_out);
88280461Sdim}
89280461Sdim
90280461Sdimchar **
91280461Sdimmake_ssh_args(char *add_arg)
92280461Sdim{
93280461Sdim	static char **args = NULL;
94280461Sdim	static int nargs = 0;
95280461Sdim	char debug_buf[4096];
96280461Sdim	int i;
97280461Sdim
98280461Sdim	/* Init args array */
99280461Sdim	if (args == NULL) {
100280461Sdim		nargs = 2;
101280461Sdim		i = 0;
102280461Sdim		args = xmalloc(sizeof(*args) * nargs);
103280461Sdim		args[i++] = "ssh";
104280461Sdim		args[i++] = NULL;
105280461Sdim	}
106280461Sdim
107280461Sdim	/* If asked to add args, then do so and return */
108280461Sdim	if (add_arg) {
109314564Sdim		i = nargs++ - 1;
110280461Sdim		args = xrealloc(args, sizeof(*args) * nargs);
111280461Sdim		args[i++] = add_arg;
112280461Sdim		args[i++] = NULL;
113280461Sdim		return(NULL);
114280461Sdim	}
115280461Sdim
116314564Sdim	/* no subsystem if the server-spec contains a '/' */
117280461Sdim	if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
118280461Sdim		make_ssh_args("-s");
119	make_ssh_args("-oForwardX11=no");
120	make_ssh_args("-oForwardAgent=no");
121	make_ssh_args(use_ssh1 ? "-oProtocol=1" : "-oProtocol=2");
122
123	/* Otherwise finish up and return the arg array */
124	if (sftp_server != NULL)
125		make_ssh_args(sftp_server);
126	else
127		make_ssh_args("sftp");
128
129	/* XXX: overflow - doesn't grow debug_buf */
130	debug_buf[0] = '\0';
131	for(i = 0; args[i]; i++) {
132		if (i)
133			strlcat(debug_buf, " ", sizeof(debug_buf));
134
135		strlcat(debug_buf, args[i], sizeof(debug_buf));
136	}
137	debug("SSH args \"%s\"", debug_buf);
138
139	return(args);
140}
141
142void
143usage(void)
144{
145	fprintf(stderr, "usage: sftp [-1vC] [-b batchfile] [-osshopt=value] [user@]host[:file [file]]\n");
146	exit(1);
147}
148
149int
150main(int argc, char **argv)
151{
152	int in, out, ch, debug_level, compress_flag;
153	pid_t sshpid;
154	char *file1 = NULL;
155	char *host, *userhost, *cp, *file2;
156	LogLevel ll;
157	extern int optind;
158	extern char *optarg;
159
160	infile = stdin;		/* Read from STDIN unless changed by -b */
161	debug_level = compress_flag = 0;
162
163	while ((ch = getopt(argc, argv, "1hvCo:s:S:b:")) != -1) {
164		switch (ch) {
165		case 'C':
166			compress_flag = 1;
167			break;
168		case 'v':
169			debug_level = MIN(3, debug_level + 1);
170			break;
171		case 'o':
172			make_ssh_args("-o");
173			make_ssh_args(optarg);
174			break;
175		case '1':
176			use_ssh1 = 1;
177			if (sftp_server == NULL)
178				sftp_server = _PATH_SFTP_SERVER;
179			break;
180		case 's':
181			sftp_server = optarg;
182			break;
183		case 'S':
184			ssh_program = optarg;
185			break;
186		case 'b':
187			if (infile == stdin) {
188				infile = fopen(optarg, "r");
189				if (infile == NULL)
190					fatal("%s (%s).", strerror(errno), optarg);
191			} else
192				fatal("Filename already specified.");
193			break;
194		case 'h':
195		default:
196			usage();
197		}
198	}
199
200	if (optind == argc || argc > (optind + 2))
201		usage();
202
203	userhost = xstrdup(argv[optind]);
204	file2 = argv[optind+1];
205
206	if ((cp = colon(userhost)) != NULL) {
207		*cp++ = '\0';
208		file1 = cp;
209	}
210
211	if ((host = strchr(userhost, '@')) == NULL)
212		host = userhost;
213	else {
214		*host++ = '\0';
215		if (!userhost[0]) {
216			fprintf(stderr, "Missing username\n");
217			usage();
218		}
219		make_ssh_args("-l");
220		make_ssh_args(userhost);
221	}
222
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