xref: /csrg-svn/usr.bin/ftp/ftp.c (revision 11627)
110296Ssam #ifndef lint
2*11627Ssam static char sccsid[] = "@(#)ftp.c	4.6 (Berkeley) 03/19/83";
310296Ssam #endif
410296Ssam 
510296Ssam #include <sys/param.h>
610296Ssam #include <sys/stat.h>
710296Ssam #include <sys/ioctl.h>
810296Ssam #include <sys/socket.h>
910296Ssam 
1010296Ssam #include <netinet/in.h>
1110296Ssam 
1210296Ssam #include <stdio.h>
1310296Ssam #include <signal.h>
1410296Ssam #include <time.h>
1510296Ssam #include <errno.h>
1610296Ssam #include <netdb.h>
1710296Ssam 
1810296Ssam #include "ftp.h"
1910296Ssam #include "ftp_var.h"
2010296Ssam 
2110296Ssam struct	sockaddr_in hisctladdr;
2210296Ssam struct	sockaddr_in data_addr;
2310296Ssam int	data = -1;
2410296Ssam int	connected;
2510296Ssam struct	sockaddr_in myctladdr;
2610296Ssam 
2710296Ssam FILE	*cin, *cout;
2810296Ssam FILE	*dataconn();
2910296Ssam 
3010296Ssam struct hostent *
3110296Ssam hookup(host, port)
3210296Ssam 	char *host;
3310296Ssam 	int port;
3410296Ssam {
3510296Ssam 	register struct hostent *hp;
36*11627Ssam 	int s, len;
3710296Ssam 
3810296Ssam 	bzero((char *)&hisctladdr, sizeof (hisctladdr));
3910296Ssam 	hp = gethostbyname(host);
4011283Ssam 	if (hp == NULL) {
4110296Ssam 		static struct hostent def;
4210296Ssam 		static struct in_addr defaddr;
4310296Ssam 		static char namebuf[128];
4410296Ssam 		int inet_addr();
4510296Ssam 
4610296Ssam 		defaddr.s_addr = inet_addr(host);
4710296Ssam 		if (defaddr.s_addr == -1) {
4810296Ssam 			fprintf(stderr, "%s: Unknown host.\n", host);
4910296Ssam 			return (0);
5010296Ssam 		}
5110296Ssam 		strcpy(namebuf, host);
5210296Ssam 		def.h_name = namebuf;
5310296Ssam 		hostname = namebuf;
5410296Ssam 		def.h_addr = (char *)&defaddr;
5510296Ssam 		def.h_length = sizeof (struct in_addr);
5610296Ssam 		def.h_addrtype = AF_INET;
5710296Ssam 		def.h_aliases = 0;
5810296Ssam 		hp = &def;
5910296Ssam 	}
6011283Ssam 	hostname = hp->h_name;
6111283Ssam 	hisctladdr.sin_family = hp->h_addrtype;
6210296Ssam 	s = socket(hp->h_addrtype, SOCK_STREAM, 0, 0);
6310296Ssam 	if (s < 0) {
6410296Ssam 		perror("ftp: socket");
6510296Ssam 		return (0);
6610296Ssam 	}
6710296Ssam 	if (bind(s, (char *)&hisctladdr, sizeof (hisctladdr), 0) < 0) {
6810296Ssam 		perror("ftp: bind");
6910296Ssam 		goto bad;
7010296Ssam 	}
7110296Ssam 	bcopy(hp->h_addr, (char *)&hisctladdr.sin_addr, hp->h_length);
7210296Ssam 	hisctladdr.sin_port = port;
7310296Ssam 	if (connect(s, (char *)&hisctladdr, sizeof (hisctladdr), 0) < 0) {
7410296Ssam 		perror("ftp: connect");
7510296Ssam 		goto bad;
7610296Ssam 	}
77*11627Ssam 	len = sizeof (myctladdr);
78*11627Ssam 	if (getsockname(s, (char *)&myctladdr, &len) < 0) {
79*11627Ssam 		perror("ftp: getsockname");
8010296Ssam 		goto bad;
8110296Ssam 	}
8210296Ssam 	cin = fdopen(s, "r");
8310296Ssam 	cout = fdopen(s, "w");
8411219Ssam 	if (cin == NULL || cout == NULL) {
8510296Ssam 		fprintf(stderr, "ftp: fdopen failed.\n");
8610296Ssam 		if (cin)
8710296Ssam 			fclose(cin);
8810296Ssam 		if (cout)
8910296Ssam 			fclose(cout);
9010296Ssam 		goto bad;
9110296Ssam 	}
9210296Ssam 	if (verbose)
9310296Ssam 		printf("Connected to %s.\n", hp->h_name);
9410296Ssam 	(void) getreply(0); 		/* read startup message from server */
9510296Ssam 	return (hp);
9610296Ssam bad:
9710296Ssam 	close(s);
9810296Ssam 	return ((struct hostent *)0);
9910296Ssam }
10010296Ssam 
10110296Ssam login(hp)
10210296Ssam 	struct hostent *hp;
10310296Ssam {
10410296Ssam 	char acct[80];
10510296Ssam 	char *user, *pass;
10610296Ssam 	int n;
10710296Ssam 
10810296Ssam 	user = pass = 0;
10910296Ssam 	ruserpass(hp->h_name, &user, &pass);
11010296Ssam 	n = command("USER %s", user);
11110296Ssam 	if (n == CONTINUE)
11210296Ssam 		n = command("PASS %s", pass);
11310296Ssam 	if (n == CONTINUE) {
11410296Ssam 		printf("Account: "); (void) fflush(stdout);
11510296Ssam 		(void) fgets(acct, sizeof(acct) - 1, stdin);
11610296Ssam 		acct[strlen(acct) - 1] = '\0';
11710296Ssam 		n = command("ACCT %s", acct);
11810296Ssam 	}
11910296Ssam 	if (n != COMPLETE) {
12010296Ssam 		fprintf(stderr, "Login failed.\n");
12110296Ssam 		return (0);
12210296Ssam 	}
12310296Ssam 	return (1);
12410296Ssam }
12510296Ssam 
12610296Ssam /*VARARGS 1*/
12710296Ssam command(fmt, args)
12810296Ssam 	char *fmt;
12910296Ssam {
13010296Ssam 
13110296Ssam 	if (debug) {
13210296Ssam 		printf("---> ");
13310296Ssam 		_doprnt(fmt, &args, stdout);
13410296Ssam 		printf("\n");
13510296Ssam 		(void) fflush(stdout);
13610296Ssam 	}
13711219Ssam 	if (cout == NULL) {
13811219Ssam 		perror ("No control connection for command");
13911219Ssam 		return (0);
14011219Ssam 	}
14110296Ssam 	_doprnt(fmt, &args, cout);
14210296Ssam 	fprintf(cout, "\r\n");
14310296Ssam 	(void) fflush(cout);
14410296Ssam 	return (getreply(!strcmp(fmt, "QUIT")));
14510296Ssam }
14610296Ssam 
14710296Ssam #include <ctype.h>
14810296Ssam 
14910296Ssam getreply(expecteof)
15010296Ssam 	int expecteof;
15110296Ssam {
15211219Ssam 	register int c, n;
15310296Ssam 	register int code, dig;
15410296Ssam 	int originalcode = 0, continuation = 0;
15510296Ssam 
15610296Ssam 	for (;;) {
15710296Ssam 		dig = n = code = 0;
15810296Ssam 		while ((c = getc(cin)) != '\n') {
15910296Ssam 			dig++;
16010296Ssam 			if (c == EOF) {
16110296Ssam 				if (expecteof)
16210296Ssam 					return (0);
16310296Ssam 				lostpeer();
16410296Ssam 				exit(1);
16510296Ssam 			}
16610296Ssam 			if (verbose && c != '\r' ||
16710296Ssam 			    (n == '5' && dig > 4))
16810296Ssam 				putchar(c);
16910296Ssam 			if (dig < 4 && isdigit(c))
17010296Ssam 				code = code * 10 + (c - '0');
17110296Ssam 			if (dig == 4 && c == '-')
17210296Ssam 				continuation++;
17310296Ssam 			if (n == 0)
17410296Ssam 				n = c;
17510296Ssam 		}
17611346Ssam 		if (verbose || n == '5') {
17710296Ssam 			putchar(c);
17811346Ssam 			(void) fflush (stdout);
17911346Ssam 		}
18010296Ssam 		if (continuation && code != originalcode) {
18110296Ssam 			if (originalcode == 0)
18210296Ssam 				originalcode = code;
18310296Ssam 			continue;
18410296Ssam 		}
18511219Ssam 		if (expecteof || empty(cin))
18610296Ssam 			return (n - '0');
18710296Ssam 	}
18810296Ssam }
18910296Ssam 
19010296Ssam empty(f)
19110296Ssam 	FILE *f;
19210296Ssam {
19310296Ssam 	int mask;
19410296Ssam 	struct timeval t;
19510296Ssam 
19610296Ssam 	if (f->_cnt > 0)
19710296Ssam 		return (0);
19810296Ssam 	mask = (1 << fileno(f));
19910296Ssam 	t.tv_sec = t.tv_usec = 0;
20010296Ssam 	(void) select(20, &mask, 0, 0, &t);
20110296Ssam 	return (mask == 0);
20210296Ssam }
20310296Ssam 
20410296Ssam jmp_buf	sendabort;
20510296Ssam 
20610296Ssam abortsend()
20710296Ssam {
20810296Ssam 
20910296Ssam 	longjmp(sendabort, 1);
21010296Ssam }
21110296Ssam 
21210296Ssam sendrequest(cmd, local, remote)
21310296Ssam 	char *cmd, *local, *remote;
21410296Ssam {
21510296Ssam 	FILE *fin, *dout, *popen();
21610296Ssam 	int (*closefunc)(), pclose(), fclose(), (*oldintr)();
21711219Ssam 	char buf[BUFSIZ];
21811346Ssam 	register int bytes = 0;
21911346Ssam 	register int c, d;
22010296Ssam 	struct stat st;
22110296Ssam 	struct timeval start, stop;
22210296Ssam 
22310296Ssam 	closefunc = NULL;
22410296Ssam 	if (setjmp(sendabort))
22510296Ssam 		goto bad;
22610296Ssam 	oldintr = signal(SIGINT, abortsend);
22710296Ssam 	if (strcmp(local, "-") == 0)
22810296Ssam 		fin = stdin;
22910296Ssam 	else if (*local == '|') {
23010296Ssam 		fin = popen(local + 1, "r");
23110296Ssam 		if (fin == NULL) {
23210296Ssam 			perror(local + 1);
23310296Ssam 			goto bad;
23410296Ssam 		}
23510296Ssam 		closefunc = pclose;
23610296Ssam 	} else {
23710296Ssam 		fin = fopen(local, "r");
23810296Ssam 		if (fin == NULL) {
23910296Ssam 			perror(local);
24010296Ssam 			goto bad;
24110296Ssam 		}
24210296Ssam 		closefunc = fclose;
24310296Ssam 		if (fstat(fileno(fin), &st) < 0 ||
24410296Ssam 		    (st.st_mode&S_IFMT) != S_IFREG) {
24510296Ssam 			fprintf(stderr, "%s: not a plain file.", local);
24610296Ssam 			goto bad;
24710296Ssam 		}
24810296Ssam 	}
24910296Ssam 	if (initconn())
25010296Ssam 		goto bad;
25110296Ssam 	if (remote) {
25210296Ssam 		if (command("%s %s", cmd, remote) != PRELIM)
25310296Ssam 			goto bad;
25410296Ssam 	} else
25510296Ssam 		if (command("%s", cmd) != PRELIM)
25610296Ssam 			goto bad;
25710296Ssam 	dout = dataconn("w");
25810296Ssam 	if (dout == NULL)
25910296Ssam 		goto bad;
26010296Ssam 	gettimeofday(&start, (struct timezone *)0);
26111219Ssam 	switch (type) {
26211219Ssam 
26311219Ssam 	case TYPE_I:
26411219Ssam 	case TYPE_L:
26511346Ssam 		errno = d = 0;
26611219Ssam 		while ((c = read(fileno (fin), buf, sizeof (buf))) > 0) {
26711346Ssam 			if ((d = write(fileno (dout), buf, c)) < 0)
26811219Ssam 				break;
26911219Ssam 			bytes += c;
27011219Ssam 		}
27111219Ssam 		if (c < 0)
27211219Ssam 			perror(local);
27311346Ssam 		if (d < 0)
27411219Ssam 			perror("netout");
27511219Ssam 		break;
27611219Ssam 
27711219Ssam 	case TYPE_A:
27811219Ssam 		while ((c = getc(fin)) != EOF) {
27911219Ssam 			if (c == '\n') {
28011219Ssam 				if (ferror(dout))
28111219Ssam 					break;
28211219Ssam 				putc('\r', dout);
28311219Ssam 				bytes++;
28411219Ssam 			}
28511219Ssam 			putc(c, dout);
28611219Ssam 			bytes++;
28711219Ssam 			if (c == '\r') {
28811219Ssam 				putc('\0', dout);
28911219Ssam 				bytes++;
29011219Ssam 			}
29111219Ssam 		}
29211219Ssam 		if (ferror(fin))
29311219Ssam 			perror(local);
29411346Ssam 		if (ferror(dout))
29511219Ssam 			perror("netout");
29611219Ssam 		break;
29710296Ssam 	}
29810296Ssam 	gettimeofday(&stop, (struct timezone *)0);
29910296Ssam 	if (closefunc != NULL)
30010296Ssam 		(*closefunc)(fin);
30110296Ssam 	(void) fclose(dout);
30210296Ssam 	(void) getreply(0);
30310296Ssam done:
30410296Ssam 	signal(SIGINT, oldintr);
30510296Ssam 	if (bytes > 0 && verbose)
30610296Ssam 		ptransfer("sent", bytes, &start, &stop);
30710296Ssam 	return;
30810296Ssam bad:
30910296Ssam 	if (data >= 0)
31010296Ssam 		(void) close(data), data = -1;
31110296Ssam 	if (closefunc != NULL && fin != NULL)
31210296Ssam 		(*closefunc)(fin);
31310296Ssam 	goto done;
31410296Ssam }
31510296Ssam 
31610296Ssam jmp_buf	recvabort;
31710296Ssam 
31810296Ssam abortrecv()
31910296Ssam {
32010296Ssam 
32110296Ssam 	longjmp(recvabort, 1);
32210296Ssam }
32310296Ssam 
32410296Ssam recvrequest(cmd, local, remote)
32510296Ssam 	char *cmd, *local, *remote;
32610296Ssam {
32710296Ssam 	FILE *fout, *din, *popen();
32811219Ssam 	char buf[BUFSIZ];
32911346Ssam 	int (*closefunc)(), pclose(), fclose(), (*oldintr)();
33010296Ssam 	register int bytes = 0;
33111346Ssam 	register int c, d;
33210296Ssam 	struct timeval start, stop;
33310296Ssam 
33410296Ssam 	closefunc = NULL;
33510296Ssam 	if (setjmp(recvabort))
33610296Ssam 		goto bad;
33710296Ssam 	oldintr = signal(SIGINT, abortrecv);
33810296Ssam 	if (strcmp(local, "-") && *local != '|')
33910296Ssam 		if (access(local, 2) < 0) {
34010296Ssam 			char *dir = rindex(local, '/');
34110296Ssam 
34210296Ssam 			if (dir != NULL)
34310296Ssam 				*dir = 0;
34410296Ssam 			if (access(dir ? dir : ".", 2) < 0) {
34510296Ssam 				perror(local);
34610296Ssam 				goto bad;
34710296Ssam 			}
34810296Ssam 			if (dir != NULL)
34910296Ssam 				*dir = '/';
35010296Ssam 		}
35110296Ssam 	if (initconn())
35210296Ssam 		goto bad;
35310296Ssam 	if (remote) {
35410296Ssam 		if (command("%s %s", cmd, remote) != PRELIM)
35510296Ssam 			goto bad;
35610296Ssam 	} else
35710296Ssam 		if (command("%s", cmd) != PRELIM)
35810296Ssam 			goto bad;
35910296Ssam 	if (strcmp(local, "-") == 0)
36010296Ssam 		fout = stdout;
36110296Ssam 	else if (*local == '|') {
36210296Ssam 		fout = popen(local + 1, "w");
36310296Ssam 		closefunc = pclose;
36410296Ssam 	} else {
36510296Ssam 		fout = fopen(local, "w");
36610296Ssam 		closefunc = fclose;
36710296Ssam 	}
36810296Ssam 	if (fout == NULL) {
36910296Ssam 		perror(local + 1);
37010296Ssam 		goto bad;
37110296Ssam 	}
37210296Ssam 	din = dataconn("r");
37310296Ssam 	if (din == NULL)
37410296Ssam 		goto bad;
37510296Ssam 	gettimeofday(&start, (struct timezone *)0);
37611219Ssam 	switch (type) {
37711219Ssam 
37811219Ssam 	case TYPE_I:
37911219Ssam 	case TYPE_L:
38011346Ssam 		errno = d = 0;
38111219Ssam 		while ((c = read(fileno(din), buf, sizeof (buf))) > 0) {
38211346Ssam 			if ((d = write(fileno(fout), buf, c)) < 0)
38311219Ssam 				break;
38411219Ssam 			bytes += c;
38511219Ssam 		}
38611219Ssam 		if (c < 0)
38711219Ssam 			perror("netin");
38811346Ssam 		if (d < 0)
38910296Ssam 			perror(local);
39011219Ssam 		break;
39111219Ssam 
39211219Ssam 	case TYPE_A:
39311219Ssam 		while ((c = getc(din)) != EOF) {
39411219Ssam 			if (c == '\r') {
39510296Ssam 				bytes++;
39611219Ssam 				if ((c = getc(din)) != '\n') {
39711219Ssam 					if (ferror (fout))
39811219Ssam 						break;
39911219Ssam 					putc ('\r', fout);
40011219Ssam 				}
40111219Ssam 				if (c == '\0') {
40211219Ssam 					bytes++;
40311219Ssam 					continue;
40411219Ssam 				}
40511219Ssam 			}
40611219Ssam 			putc (c, fout);
40711219Ssam 			bytes++;
40810296Ssam 		}
40911219Ssam 		if (ferror (din))
41011219Ssam 			perror ("netin");
41111219Ssam 		if (ferror (fout))
41211219Ssam 			perror (local);
41311219Ssam 		break;
41410296Ssam 	}
41510296Ssam 	gettimeofday(&stop, (struct timezone *)0);
41610296Ssam 	(void) fclose(din);
41710296Ssam 	if (closefunc != NULL)
41810296Ssam 		(*closefunc)(fout);
41910296Ssam 	(void) getreply(0);
42010296Ssam done:
42110296Ssam 	signal(SIGINT, oldintr);
42210296Ssam 	if (bytes > 0 && verbose)
42310296Ssam 		ptransfer("received", bytes, &start, &stop);
42410296Ssam 	return;
42510296Ssam bad:
42610296Ssam 	if (data >= 0)
42710296Ssam 		(void) close(data), data = -1;
42810296Ssam 	if (closefunc != NULL && fout != NULL)
42910296Ssam 		(*closefunc)(fout);
43010296Ssam 	goto done;
43110296Ssam }
43210296Ssam 
43310296Ssam /*
43410296Ssam  * Need to start a listen on the data channel
43510296Ssam  * before we send the command, otherwise the
43610296Ssam  * server's connect may fail.
43710296Ssam  */
43810296Ssam initconn()
43910296Ssam {
44010296Ssam 	register char *p, *a;
441*11627Ssam 	int result, len;
44210296Ssam 
44310296Ssam 	data_addr = myctladdr;
44410296Ssam 	data_addr.sin_port = 0;		/* let system pick one */
44510296Ssam 	data = socket(AF_INET, SOCK_STREAM, 0, 0);
44610296Ssam 	if (data < 0) {
44710296Ssam 		perror("ftp: socket");
44810296Ssam 		return (1);
44910296Ssam 	}
45010296Ssam 	if (bind(data, (char *)&data_addr, sizeof (data_addr), 0) < 0) {
45110296Ssam 		perror("ftp: bind");
45210296Ssam 		goto bad;
45310296Ssam 	}
45410296Ssam 	if (options & SO_DEBUG &&
45510296Ssam 	    setsockopt(data, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
45610296Ssam 		perror("ftp: setsockopt (ignored)");
457*11627Ssam 	len = sizeof (data_addr);
458*11627Ssam 	if (getsockname(data, (char *)&data_addr, &len) < 0) {
459*11627Ssam 		perror("ftp: getsockname");
46010296Ssam 		goto bad;
46110296Ssam 	}
46210296Ssam 	if (listen(data, 1) < 0) {
46310296Ssam 		perror("ftp: listen");
46410296Ssam 		goto bad;
46510296Ssam 	}
46610296Ssam 	a = (char *)&data_addr.sin_addr;
46710296Ssam 	p = (char *)&data_addr.sin_port;
46810296Ssam #define	UC(b)	(((int)b)&0xff)
46910296Ssam 	result =
47010296Ssam 	    command("PORT %d,%d,%d,%d,%d,%d",
47110296Ssam 	      UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
47210296Ssam 	      UC(p[0]), UC(p[1]));
47310296Ssam 	return (result != COMPLETE);
47410296Ssam bad:
47510296Ssam 	(void) close(data), data = -1;
47610296Ssam 	return (1);
47710296Ssam }
47810296Ssam 
47910296Ssam FILE *
48010296Ssam dataconn(mode)
48110296Ssam 	char *mode;
48210296Ssam {
48310296Ssam 	struct sockaddr_in from;
48410296Ssam 	int s, fromlen = sizeof (from);
48510296Ssam 
48610296Ssam 	s = accept(data, &from, &fromlen, 0);
48710296Ssam 	if (s < 0) {
48810296Ssam 		perror("ftp: accept");
48910296Ssam 		(void) close(data), data = -1;
49010296Ssam 		return (NULL);
49110296Ssam 	}
49210296Ssam 	(void) close(data);
49310296Ssam 	data = s;
49410296Ssam 	return (fdopen(data, mode));
49510296Ssam }
49610296Ssam 
49710296Ssam ptransfer(direction, bytes, t0, t1)
49810296Ssam 	char *direction;
49910296Ssam 	int bytes;
50010296Ssam 	struct timeval *t0, *t1;
50110296Ssam {
50210296Ssam 	struct timeval td;
50310296Ssam 	int ms, bs;
50410296Ssam 
50510296Ssam 	tvsub(&td, t1, t0);
50610296Ssam 	ms = (td.tv_sec * 1000) + (td.tv_usec / 1000);
50710296Ssam #define	nz(x)	((x) == 0 ? 1 : (x))
50810296Ssam 	bs = ((bytes * NBBY * 1000) / nz(ms)) / NBBY;
50910296Ssam 	printf("%d bytes %s in %d.%02d seconds (%d.%01d Kbytes/s)\n",
51010296Ssam 		bytes, direction, td.tv_sec, td.tv_usec / 10000,
51110296Ssam 		bs / 1024, (((bs % 1024) * 10) + 1023) / 1024);
51210296Ssam }
51310296Ssam 
51410296Ssam tvadd(tsum, t0)
51510296Ssam 	struct timeval *tsum, *t0;
51610296Ssam {
51710296Ssam 
51810296Ssam 	tsum->tv_sec += t0->tv_sec;
51910296Ssam 	tsum->tv_usec += t0->tv_usec;
52010296Ssam 	if (tsum->tv_usec > 1000000)
52110296Ssam 		tsum->tv_sec++, tsum->tv_usec -= 1000000;
52210296Ssam }
52310296Ssam 
52410296Ssam tvsub(tdiff, t1, t0)
52510296Ssam 	struct timeval *tdiff, *t1, *t0;
52610296Ssam {
52710296Ssam 
52810296Ssam 	tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
52910296Ssam 	tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
53010296Ssam 	if (tdiff->tv_usec < 0)
53110296Ssam 		tdiff->tv_sec--, tdiff->tv_usec += 1000000;
53210296Ssam }
533