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