xref: /csrg-svn/usr.bin/ftp/ftp.c (revision 11227)
1 #ifndef lint
2 static char sccsid[] = "@(#)ftp.c	4.3 (Berkeley) 02/22/83";
3 #endif
4 
5 #include <sys/param.h>
6 #include <sys/stat.h>
7 #include <sys/ioctl.h>
8 #include <sys/socket.h>
9 
10 #include <netinet/in.h>
11 
12 #include <stdio.h>
13 #include <signal.h>
14 #include <time.h>
15 #include <errno.h>
16 #include <netdb.h>
17 
18 #include "ftp.h"
19 #include "ftp_var.h"
20 
21 struct	sockaddr_in hisctladdr;
22 struct	sockaddr_in data_addr;
23 int	data = -1;
24 int	connected;
25 struct	sockaddr_in myctladdr;
26 
27 FILE	*cin, *cout;
28 FILE	*dataconn();
29 
30 struct hostent *
31 hookup(host, port)
32 	char *host;
33 	int port;
34 {
35 	register struct hostent *hp;
36 	int s;
37 
38 	bzero((char *)&hisctladdr, sizeof (hisctladdr));
39 	hp = gethostbyname(host);
40 	if (hp) {
41 		hisctladdr.sin_family = hp->h_addrtype;
42 		hostname = hp->h_name;
43 	} else {
44 		static struct hostent def;
45 		static struct in_addr defaddr;
46 		static char namebuf[128];
47 		int inet_addr();
48 
49 		defaddr.s_addr = inet_addr(host);
50 		if (defaddr.s_addr == -1) {
51 			fprintf(stderr, "%s: Unknown host.\n", host);
52 			return (0);
53 		}
54 		strcpy(namebuf, host);
55 		def.h_name = namebuf;
56 		hostname = namebuf;
57 		def.h_addr = (char *)&defaddr;
58 		def.h_length = sizeof (struct in_addr);
59 		def.h_addrtype = AF_INET;
60 		def.h_aliases = 0;
61 		hp = &def;
62 	}
63 	s = socket(hp->h_addrtype, SOCK_STREAM, 0, 0);
64 	if (s < 0) {
65 		perror("ftp: socket");
66 		return (0);
67 	}
68 	if (bind(s, (char *)&hisctladdr, sizeof (hisctladdr), 0) < 0) {
69 		perror("ftp: bind");
70 		goto bad;
71 	}
72 	bcopy(hp->h_addr, (char *)&hisctladdr.sin_addr, hp->h_length);
73 	hisctladdr.sin_port = port;
74 	if (connect(s, (char *)&hisctladdr, sizeof (hisctladdr), 0) < 0) {
75 		perror("ftp: connect");
76 		goto bad;
77 	}
78 	if (socketaddr(s, &myctladdr) < 0) {
79 		perror("ftp: socketaddr");
80 		goto bad;
81 	}
82 	cin = fdopen(s, "r");
83 	cout = fdopen(s, "w");
84 	if (cin == NULL || cout == NULL) {
85 		fprintf(stderr, "ftp: fdopen failed.\n");
86 		if (cin)
87 			fclose(cin);
88 		if (cout)
89 			fclose(cout);
90 		goto bad;
91 	}
92 	if (verbose)
93 		printf("Connected to %s.\n", hp->h_name);
94 	(void) getreply(0); 		/* read startup message from server */
95 	return (hp);
96 bad:
97 	close(s);
98 	return ((struct hostent *)0);
99 }
100 
101 login(hp)
102 	struct hostent *hp;
103 {
104 	char acct[80];
105 	char *user, *pass;
106 	int n;
107 
108 	user = pass = 0;
109 	ruserpass(hp->h_name, &user, &pass);
110 	n = command("USER %s", user);
111 	if (n == CONTINUE)
112 		n = command("PASS %s", pass);
113 	if (n == CONTINUE) {
114 		printf("Account: "); (void) fflush(stdout);
115 		(void) fgets(acct, sizeof(acct) - 1, stdin);
116 		acct[strlen(acct) - 1] = '\0';
117 		n = command("ACCT %s", acct);
118 	}
119 	if (n != COMPLETE) {
120 		fprintf(stderr, "Login failed.\n");
121 		return (0);
122 	}
123 	return (1);
124 }
125 
126 /*VARARGS 1*/
127 command(fmt, args)
128 	char *fmt;
129 {
130 
131 	if (debug) {
132 		printf("---> ");
133 		_doprnt(fmt, &args, stdout);
134 		printf("\n");
135 		(void) fflush(stdout);
136 	}
137 	if (cout == NULL) {
138 		perror ("No control connection for command");
139 		return (0);
140 	}
141 	_doprnt(fmt, &args, cout);
142 	fprintf(cout, "\r\n");
143 	(void) fflush(cout);
144 	return (getreply(!strcmp(fmt, "QUIT")));
145 }
146 
147 #include <ctype.h>
148 
149 getreply(expecteof)
150 	int expecteof;
151 {
152 	register int c, n;
153 	register int code, dig;
154 	int originalcode = 0, continuation = 0;
155 
156 	for (;;) {
157 		dig = n = code = 0;
158 		while ((c = getc(cin)) != '\n') {
159 			dig++;
160 			if (c == EOF) {
161 				if (expecteof)
162 					return (0);
163 				lostpeer();
164 				exit(1);
165 			}
166 			if (verbose && c != '\r' ||
167 			    (n == '5' && dig > 4))
168 				putchar(c);
169 			if (dig < 4 && isdigit(c))
170 				code = code * 10 + (c - '0');
171 			if (dig == 4 && c == '-')
172 				continuation++;
173 			if (n == 0)
174 				n = c;
175 		}
176 		if (verbose || n == '5')
177 			putchar(c);
178 		if (continuation && code != originalcode) {
179 			if (originalcode == 0)
180 				originalcode = code;
181 			continue;
182 		}
183 		if (expecteof || empty(cin))
184 			return (n - '0');
185 	}
186 }
187 
188 empty(f)
189 	FILE *f;
190 {
191 	int mask;
192 	struct timeval t;
193 
194 	if (f->_cnt > 0)
195 		return (0);
196 	mask = (1 << fileno(f));
197 	t.tv_sec = t.tv_usec = 0;
198 	(void) select(20, &mask, 0, 0, &t);
199 	return (mask == 0);
200 }
201 
202 jmp_buf	sendabort;
203 
204 abortsend()
205 {
206 
207 	longjmp(sendabort, 1);
208 }
209 
210 sendrequest(cmd, local, remote)
211 	char *cmd, *local, *remote;
212 {
213 	FILE *fin, *dout, *popen();
214 	int (*closefunc)(), pclose(), fclose(), (*oldintr)();
215 	char buf[BUFSIZ];
216 	register int bytes = 0, c;
217 	struct stat st;
218 	struct timeval start, stop;
219 	extern int errno;
220 
221 	closefunc = NULL;
222 	if (setjmp(sendabort))
223 		goto bad;
224 	oldintr = signal(SIGINT, abortsend);
225 	if (strcmp(local, "-") == 0)
226 		fin = stdin;
227 	else if (*local == '|') {
228 		fin = popen(local + 1, "r");
229 		if (fin == NULL) {
230 			perror(local + 1);
231 			goto bad;
232 		}
233 		closefunc = pclose;
234 	} else {
235 		fin = fopen(local, "r");
236 		if (fin == NULL) {
237 			perror(local);
238 			goto bad;
239 		}
240 		closefunc = fclose;
241 		if (fstat(fileno(fin), &st) < 0 ||
242 		    (st.st_mode&S_IFMT) != S_IFREG) {
243 			fprintf(stderr, "%s: not a plain file.", local);
244 			goto bad;
245 		}
246 	}
247 	if (initconn())
248 		goto bad;
249 	if (remote) {
250 		if (command("%s %s", cmd, remote) != PRELIM)
251 			goto bad;
252 	} else
253 		if (command("%s", cmd) != PRELIM)
254 			goto bad;
255 	dout = dataconn("w");
256 	if (dout == NULL)
257 		goto bad;
258 	gettimeofday(&start, (struct timezone *)0);
259 	switch (type) {
260 
261 	case TYPE_I:
262 	case TYPE_L:
263 		errno = 0;
264 		while ((c = read(fileno (fin), buf, sizeof (buf))) > 0) {
265 			if (write(fileno (dout), buf, c) < 0)
266 				break;
267 			bytes += c;
268 		}
269 		if (c < 0)
270 			perror(local);
271 		else if (errno)
272 			perror("netout");
273 		break;
274 
275 	case TYPE_A:
276 		while ((c = getc(fin)) != EOF) {
277 			if (c == '\n') {
278 				if (ferror(dout))
279 					break;
280 				putc('\r', dout);
281 				bytes++;
282 			}
283 			putc(c, dout);
284 			bytes++;
285 			if (c == '\r') {
286 				putc('\0', dout);
287 				bytes++;
288 			}
289 		}
290 		if (ferror(fin))
291 			perror(local);
292 		else if (ferror(dout))
293 			perror("netout");
294 		break;
295 	}
296 	gettimeofday(&stop, (struct timezone *)0);
297 	if (closefunc != NULL)
298 		(*closefunc)(fin);
299 	(void) fclose(dout);
300 	(void) getreply(0);
301 done:
302 	signal(SIGINT, oldintr);
303 	if (bytes > 0 && verbose)
304 		ptransfer("sent", bytes, &start, &stop);
305 	return;
306 bad:
307 	if (data >= 0)
308 		(void) close(data), data = -1;
309 	if (closefunc != NULL && fin != NULL)
310 		(*closefunc)(fin);
311 	goto done;
312 }
313 
314 jmp_buf	recvabort;
315 
316 abortrecv()
317 {
318 
319 	longjmp(recvabort, 1);
320 }
321 
322 recvrequest(cmd, local, remote)
323 	char *cmd, *local, *remote;
324 {
325 	FILE *fout, *din, *popen();
326 	char buf[BUFSIZ];
327 	int (*closefunc)(), pclose(), fclose(), (*oldintr)(), c;
328 	register int bytes = 0;
329 	struct timeval start, stop;
330 	extern int errno;
331 
332 	closefunc = NULL;
333 	if (setjmp(recvabort))
334 		goto bad;
335 	oldintr = signal(SIGINT, abortrecv);
336 	if (strcmp(local, "-") && *local != '|')
337 		if (access(local, 2) < 0) {
338 			char *dir = rindex(local, '/');
339 
340 			if (dir != NULL)
341 				*dir = 0;
342 			if (access(dir ? dir : ".", 2) < 0) {
343 				perror(local);
344 				goto bad;
345 			}
346 			if (dir != NULL)
347 				*dir = '/';
348 		}
349 	if (initconn())
350 		goto bad;
351 	if (remote) {
352 		if (command("%s %s", cmd, remote) != PRELIM)
353 			goto bad;
354 	} else
355 		if (command("%s", cmd) != PRELIM)
356 			goto bad;
357 	if (strcmp(local, "-") == 0)
358 		fout = stdout;
359 	else if (*local == '|') {
360 		fout = popen(local + 1, "w");
361 		closefunc = pclose;
362 	} else {
363 		fout = fopen(local, "w");
364 		closefunc = fclose;
365 	}
366 	if (fout == NULL) {
367 		perror(local + 1);
368 		goto bad;
369 	}
370 	din = dataconn("r");
371 	if (din == NULL)
372 		goto bad;
373 	gettimeofday(&start, (struct timezone *)0);
374 	switch (type) {
375 
376 	case TYPE_I:
377 	case TYPE_L:
378 		errno = 0;
379 		while ((c = read(fileno(din), buf, sizeof (buf))) > 0) {
380 			if (write(fileno(fout), buf, c) < 0)
381 				break;
382 			bytes += c;
383 		}
384 		if (c < 0)
385 			perror("netin");
386 		if (errno)
387 			perror(local);
388 		break;
389 
390 	case TYPE_A:
391 		while ((c = getc(din)) != EOF) {
392 			if (c == '\r') {
393 				bytes++;
394 				if ((c = getc(din)) != '\n') {
395 					if (ferror (fout))
396 						break;
397 					putc ('\r', fout);
398 				}
399 				if (c == '\0') {
400 					bytes++;
401 					continue;
402 				}
403 			}
404 			putc (c, fout);
405 			bytes++;
406 		}
407 		if (ferror (din))
408 			perror ("netin");
409 		if (ferror (fout))
410 			perror (local);
411 		break;
412 	}
413 	gettimeofday(&stop, (struct timezone *)0);
414 	(void) fclose(din);
415 	if (closefunc != NULL)
416 		(*closefunc)(fout);
417 	(void) getreply(0);
418 done:
419 	signal(SIGINT, oldintr);
420 	if (bytes > 0 && verbose)
421 		ptransfer("received", bytes, &start, &stop);
422 	return;
423 bad:
424 	if (data >= 0)
425 		(void) close(data), data = -1;
426 	if (closefunc != NULL && fout != NULL)
427 		(*closefunc)(fout);
428 	goto done;
429 }
430 
431 /*
432  * Need to start a listen on the data channel
433  * before we send the command, otherwise the
434  * server's connect may fail.
435  */
436 initconn()
437 {
438 	register char *p, *a;
439 	int result;
440 
441 	data_addr = myctladdr;
442 	data_addr.sin_port = 0;		/* let system pick one */
443 	data = socket(AF_INET, SOCK_STREAM, 0, 0);
444 	if (data < 0) {
445 		perror("ftp: socket");
446 		return (1);
447 	}
448 	if (bind(data, (char *)&data_addr, sizeof (data_addr), 0) < 0) {
449 		perror("ftp: bind");
450 		goto bad;
451 	}
452 	if (options & SO_DEBUG &&
453 	    setsockopt(data, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
454 		perror("ftp: setsockopt (ignored)");
455 	if (socketaddr(data, &data_addr) < 0) {
456 		perror("ftp: socketaddr");
457 		goto bad;
458 	}
459 	if (listen(data, 1) < 0) {
460 		perror("ftp: listen");
461 		goto bad;
462 	}
463 	a = (char *)&data_addr.sin_addr;
464 	p = (char *)&data_addr.sin_port;
465 #define	UC(b)	(((int)b)&0xff)
466 	result =
467 	    command("PORT %d,%d,%d,%d,%d,%d",
468 	      UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
469 	      UC(p[0]), UC(p[1]));
470 	return (result != COMPLETE);
471 bad:
472 	(void) close(data), data = -1;
473 	return (1);
474 }
475 
476 FILE *
477 dataconn(mode)
478 	char *mode;
479 {
480 	struct sockaddr_in from;
481 	int s, fromlen = sizeof (from);
482 
483 	s = accept(data, &from, &fromlen, 0);
484 	if (s < 0) {
485 		perror("ftp: accept");
486 		(void) close(data), data = -1;
487 		return (NULL);
488 	}
489 	(void) close(data);
490 	data = s;
491 	return (fdopen(data, mode));
492 }
493 
494 ptransfer(direction, bytes, t0, t1)
495 	char *direction;
496 	int bytes;
497 	struct timeval *t0, *t1;
498 {
499 	struct timeval td;
500 	int ms, bs;
501 
502 	tvsub(&td, t1, t0);
503 	ms = (td.tv_sec * 1000) + (td.tv_usec / 1000);
504 #define	nz(x)	((x) == 0 ? 1 : (x))
505 	bs = ((bytes * NBBY * 1000) / nz(ms)) / NBBY;
506 	printf("%d bytes %s in %d.%02d seconds (%d.%01d Kbytes/s)\n",
507 		bytes, direction, td.tv_sec, td.tv_usec / 10000,
508 		bs / 1024, (((bs % 1024) * 10) + 1023) / 1024);
509 }
510 
511 tvadd(tsum, t0)
512 	struct timeval *tsum, *t0;
513 {
514 
515 	tsum->tv_sec += t0->tv_sec;
516 	tsum->tv_usec += t0->tv_usec;
517 	if (tsum->tv_usec > 1000000)
518 		tsum->tv_sec++, tsum->tv_usec -= 1000000;
519 }
520 
521 tvsub(tdiff, t1, t0)
522 	struct timeval *tdiff, *t1, *t0;
523 {
524 
525 	tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
526 	tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
527 	if (tdiff->tv_usec < 0)
528 		tdiff->tv_sec--, tdiff->tv_usec += 1000000;
529 }
530