xref: /csrg-svn/usr.bin/ftp/ftp.c (revision 25907)
121739Sdist /*
221739Sdist  * Copyright (c) 1980 Regents of the University of California.
321739Sdist  * All rights reserved.  The Berkeley software License Agreement
421739Sdist  * specifies the terms and conditions for redistribution.
521739Sdist  */
621739Sdist 
710296Ssam #ifndef lint
8*25907Smckusick static char sccsid[] = "@(#)ftp.c	5.5 (Berkeley) 01/13/86";
921739Sdist #endif not lint
1010296Ssam 
1110296Ssam #include <sys/param.h>
1210296Ssam #include <sys/stat.h>
1310296Ssam #include <sys/ioctl.h>
1410296Ssam #include <sys/socket.h>
1513614Ssam #include <sys/time.h>
1610296Ssam 
1710296Ssam #include <netinet/in.h>
1812397Ssam #include <arpa/ftp.h>
1910296Ssam 
2010296Ssam #include <stdio.h>
2110296Ssam #include <signal.h>
2210296Ssam #include <errno.h>
2310296Ssam #include <netdb.h>
2410296Ssam 
2510296Ssam #include "ftp_var.h"
2610296Ssam 
2710296Ssam struct	sockaddr_in hisctladdr;
2810296Ssam struct	sockaddr_in data_addr;
2910296Ssam int	data = -1;
3010296Ssam int	connected;
3110296Ssam struct	sockaddr_in myctladdr;
3210296Ssam 
3310296Ssam FILE	*cin, *cout;
3410296Ssam FILE	*dataconn();
3510296Ssam 
3625904Skarels char *
3710296Ssam hookup(host, port)
3810296Ssam 	char *host;
3910296Ssam 	int port;
4010296Ssam {
4125904Skarels 	register struct hostent *hp = 0;
4225904Skarels 	static char hostnamebuf[80];
4311627Ssam 	int s, len;
4410296Ssam 
4510296Ssam 	bzero((char *)&hisctladdr, sizeof (hisctladdr));
4625904Skarels 	hisctladdr.sin_addr.s_addr = inet_addr(host);
4725904Skarels 	if (hisctladdr.sin_addr.s_addr != -1) {
4825904Skarels 		hisctladdr.sin_family = AF_INET;
4925904Skarels 		(void) strcpy(hostnamebuf, host);
5025904Skarels 	} else {
5125100Sbloom 		hp = gethostbyname(host);
5225904Skarels 		if (hp == NULL) {
5325904Skarels 			printf("%s: unknown host\n", host);
5425904Skarels 			return (0);
5525904Skarels 		}
5625904Skarels 		hisctladdr.sin_family = hp->h_addrtype;
5725904Skarels 		bcopy(hp->h_addr_list[0],
5825904Skarels 		    (caddr_t)&hisctladdr.sin_addr, hp->h_length);
5925904Skarels 		(void) strcpy(hostnamebuf, hp->h_name);
6010296Ssam 	}
6125904Skarels 	hostname = hostnamebuf;
6225904Skarels 	s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
6310296Ssam 	if (s < 0) {
6410296Ssam 		perror("ftp: socket");
6510296Ssam 		return (0);
6610296Ssam 	}
6710296Ssam 	hisctladdr.sin_port = port;
6825904Skarels 	while (connect(s, (caddr_t)&hisctladdr, sizeof (hisctladdr)) < 0) {
6925904Skarels 		if (hp && hp->h_addr_list[1]) {
7025904Skarels 			int oerrno = errno;
7125904Skarels 
7225904Skarels 			fprintf(stderr, "ftp: connect to address %s: ",
7325904Skarels 				inet_ntoa(hisctladdr.sin_addr));
7425904Skarels 			errno = oerrno;
7525904Skarels 			perror(0);
7625904Skarels 			hp->h_addr_list++;
7725904Skarels 			bcopy(hp->h_addr_list[0],
7825904Skarels 			    (caddr_t)&hisctladdr.sin_addr, hp->h_length);
7925904Skarels 			fprintf(stderr, "Trying %s...\n",
8025904Skarels 				inet_ntoa(hisctladdr.sin_addr));
8125904Skarels 			continue;
8225904Skarels 		}
8310296Ssam 		perror("ftp: connect");
8410296Ssam 		goto bad;
8510296Ssam 	}
8611627Ssam 	len = sizeof (myctladdr);
8711627Ssam 	if (getsockname(s, (char *)&myctladdr, &len) < 0) {
8811627Ssam 		perror("ftp: getsockname");
8910296Ssam 		goto bad;
9010296Ssam 	}
9110296Ssam 	cin = fdopen(s, "r");
9210296Ssam 	cout = fdopen(s, "w");
9311219Ssam 	if (cin == NULL || cout == NULL) {
9410296Ssam 		fprintf(stderr, "ftp: fdopen failed.\n");
9510296Ssam 		if (cin)
9610296Ssam 			fclose(cin);
9710296Ssam 		if (cout)
9810296Ssam 			fclose(cout);
9910296Ssam 		goto bad;
10010296Ssam 	}
10110296Ssam 	if (verbose)
10225904Skarels 		printf("Connected to %s.\n", hostname);
10310296Ssam 	(void) getreply(0); 		/* read startup message from server */
10425904Skarels 	return (hostname);
10510296Ssam bad:
10610296Ssam 	close(s);
10725904Skarels 	return ((char *)0);
10810296Ssam }
10910296Ssam 
11025904Skarels login(host)
11125904Skarels 	char *host;
11210296Ssam {
11310296Ssam 	char acct[80];
11410296Ssam 	char *user, *pass;
11510296Ssam 	int n;
11610296Ssam 
11710296Ssam 	user = pass = 0;
11825904Skarels 	ruserpass(host, &user, &pass);
11910296Ssam 	n = command("USER %s", user);
12010296Ssam 	if (n == CONTINUE)
12110296Ssam 		n = command("PASS %s", pass);
12210296Ssam 	if (n == CONTINUE) {
12310296Ssam 		printf("Account: "); (void) fflush(stdout);
12410296Ssam 		(void) fgets(acct, sizeof(acct) - 1, stdin);
12510296Ssam 		acct[strlen(acct) - 1] = '\0';
12610296Ssam 		n = command("ACCT %s", acct);
12710296Ssam 	}
12810296Ssam 	if (n != COMPLETE) {
12910296Ssam 		fprintf(stderr, "Login failed.\n");
13010296Ssam 		return (0);
13110296Ssam 	}
13210296Ssam 	return (1);
13310296Ssam }
13410296Ssam 
13510296Ssam /*VARARGS 1*/
13610296Ssam command(fmt, args)
13710296Ssam 	char *fmt;
13810296Ssam {
13910296Ssam 
14010296Ssam 	if (debug) {
14110296Ssam 		printf("---> ");
14210296Ssam 		_doprnt(fmt, &args, stdout);
14310296Ssam 		printf("\n");
14410296Ssam 		(void) fflush(stdout);
14510296Ssam 	}
14611219Ssam 	if (cout == NULL) {
14711219Ssam 		perror ("No control connection for command");
14811219Ssam 		return (0);
14911219Ssam 	}
15010296Ssam 	_doprnt(fmt, &args, cout);
15110296Ssam 	fprintf(cout, "\r\n");
15210296Ssam 	(void) fflush(cout);
15310296Ssam 	return (getreply(!strcmp(fmt, "QUIT")));
15410296Ssam }
15510296Ssam 
15610296Ssam #include <ctype.h>
15710296Ssam 
15810296Ssam getreply(expecteof)
15910296Ssam 	int expecteof;
16010296Ssam {
16111219Ssam 	register int c, n;
16210296Ssam 	register int code, dig;
16310296Ssam 	int originalcode = 0, continuation = 0;
16410296Ssam 
16510296Ssam 	for (;;) {
16610296Ssam 		dig = n = code = 0;
16710296Ssam 		while ((c = getc(cin)) != '\n') {
16810296Ssam 			dig++;
16910296Ssam 			if (c == EOF) {
17010296Ssam 				if (expecteof)
17110296Ssam 					return (0);
17210296Ssam 				lostpeer();
17310296Ssam 				exit(1);
17410296Ssam 			}
17510296Ssam 			if (verbose && c != '\r' ||
17610296Ssam 			    (n == '5' && dig > 4))
17710296Ssam 				putchar(c);
17810296Ssam 			if (dig < 4 && isdigit(c))
17910296Ssam 				code = code * 10 + (c - '0');
18010296Ssam 			if (dig == 4 && c == '-')
18110296Ssam 				continuation++;
18210296Ssam 			if (n == 0)
18310296Ssam 				n = c;
18410296Ssam 		}
18511346Ssam 		if (verbose || n == '5') {
18610296Ssam 			putchar(c);
18711346Ssam 			(void) fflush (stdout);
18811346Ssam 		}
18910296Ssam 		if (continuation && code != originalcode) {
19010296Ssam 			if (originalcode == 0)
19110296Ssam 				originalcode = code;
19210296Ssam 			continue;
19310296Ssam 		}
194*25907Smckusick 		return (n - '0');
19510296Ssam 	}
19610296Ssam }
19710296Ssam 
19810296Ssam jmp_buf	sendabort;
19910296Ssam 
20010296Ssam abortsend()
20110296Ssam {
20210296Ssam 
20310296Ssam 	longjmp(sendabort, 1);
20410296Ssam }
20510296Ssam 
20610296Ssam sendrequest(cmd, local, remote)
20710296Ssam 	char *cmd, *local, *remote;
20810296Ssam {
20910296Ssam 	FILE *fin, *dout, *popen();
21010296Ssam 	int (*closefunc)(), pclose(), fclose(), (*oldintr)();
21111219Ssam 	char buf[BUFSIZ];
212*25907Smckusick 	int expectingreply = 0;
21311651Ssam 	long bytes = 0, hashbytes = sizeof (buf);
21411346Ssam 	register int c, d;
21510296Ssam 	struct stat st;
21610296Ssam 	struct timeval start, stop;
21710296Ssam 
21810296Ssam 	closefunc = NULL;
21910296Ssam 	if (setjmp(sendabort))
22010296Ssam 		goto bad;
22110296Ssam 	oldintr = signal(SIGINT, abortsend);
22210296Ssam 	if (strcmp(local, "-") == 0)
22310296Ssam 		fin = stdin;
22410296Ssam 	else if (*local == '|') {
225*25907Smckusick 		/*
226*25907Smckusick 		 * Advance local so further uses just yield file name
227*25907Smckusick 		 * thus later references for error messages need not check
228*25907Smckusick 		 * for '|' special case.
229*25907Smckusick 	 	 */
230*25907Smckusick 		local += 1;
231*25907Smckusick 		fin = popen(local, "r");
23210296Ssam 		if (fin == NULL) {
233*25907Smckusick 			perror(local);
23410296Ssam 			goto bad;
23510296Ssam 		}
23610296Ssam 		closefunc = pclose;
23710296Ssam 	} else {
23810296Ssam 		fin = fopen(local, "r");
23910296Ssam 		if (fin == NULL) {
24010296Ssam 			perror(local);
24110296Ssam 			goto bad;
24210296Ssam 		}
24310296Ssam 		closefunc = fclose;
24410296Ssam 		if (fstat(fileno(fin), &st) < 0 ||
24510296Ssam 		    (st.st_mode&S_IFMT) != S_IFREG) {
24617949Sralph 			fprintf(stderr, "%s: not a plain file.\n", local);
24710296Ssam 			goto bad;
24810296Ssam 		}
24910296Ssam 	}
25010296Ssam 	if (initconn())
25110296Ssam 		goto bad;
25210296Ssam 	if (remote) {
25310296Ssam 		if (command("%s %s", cmd, remote) != PRELIM)
25410296Ssam 			goto bad;
25510296Ssam 	} else
25610296Ssam 		if (command("%s", cmd) != PRELIM)
25710296Ssam 			goto bad;
258*25907Smckusick 	expectingreply++;	/* got preliminary reply, expecting final reply */
25910296Ssam 	dout = dataconn("w");
26010296Ssam 	if (dout == NULL)
26110296Ssam 		goto bad;
26210296Ssam 	gettimeofday(&start, (struct timezone *)0);
26311219Ssam 	switch (type) {
26411219Ssam 
26511219Ssam 	case TYPE_I:
26611219Ssam 	case TYPE_L:
26711346Ssam 		errno = d = 0;
26811219Ssam 		while ((c = read(fileno (fin), buf, sizeof (buf))) > 0) {
26911346Ssam 			if ((d = write(fileno (dout), buf, c)) < 0)
27011219Ssam 				break;
27111219Ssam 			bytes += c;
27211651Ssam 			if (hash) {
27311651Ssam 				putchar('#');
27411651Ssam 				fflush(stdout);
27511651Ssam 			}
27611219Ssam 		}
27713213Ssam 		if (hash && bytes > 0) {
27811651Ssam 			putchar('\n');
27911651Ssam 			fflush(stdout);
28011651Ssam 		}
28111219Ssam 		if (c < 0)
28211219Ssam 			perror(local);
28311346Ssam 		if (d < 0)
28411219Ssam 			perror("netout");
28511219Ssam 		break;
28611219Ssam 
28711219Ssam 	case TYPE_A:
28811219Ssam 		while ((c = getc(fin)) != EOF) {
28911219Ssam 			if (c == '\n') {
29011651Ssam 				while (hash && (bytes >= hashbytes)) {
29111651Ssam 					putchar('#');
29211651Ssam 					fflush(stdout);
29311651Ssam 					hashbytes += sizeof (buf);
29411651Ssam 				}
29511219Ssam 				if (ferror(dout))
29611219Ssam 					break;
29711219Ssam 				putc('\r', dout);
29811219Ssam 				bytes++;
29911219Ssam 			}
30011219Ssam 			putc(c, dout);
30111219Ssam 			bytes++;
30211219Ssam 			if (c == '\r') {
30311219Ssam 				putc('\0', dout);
30411219Ssam 				bytes++;
30511219Ssam 			}
30611219Ssam 		}
30711651Ssam 		if (hash) {
30813213Ssam 			if (bytes < hashbytes)
30913213Ssam 				putchar('#');
31011651Ssam 			putchar('\n');
31111651Ssam 			fflush(stdout);
31211651Ssam 		}
31311219Ssam 		if (ferror(fin))
31411219Ssam 			perror(local);
31511346Ssam 		if (ferror(dout))
31611219Ssam 			perror("netout");
31711219Ssam 		break;
31810296Ssam 	}
31910296Ssam 	gettimeofday(&stop, (struct timezone *)0);
32010296Ssam 	if (closefunc != NULL)
321*25907Smckusick 		(*closefunc)(fin), closefunc = NULL;
32210296Ssam 	(void) fclose(dout);
32310296Ssam done:
324*25907Smckusick 	if (expectingreply) {
325*25907Smckusick 		(void) getreply(0);
326*25907Smckusick 		expectingreply = 0;
327*25907Smckusick 	}
32810296Ssam 	signal(SIGINT, oldintr);
32910296Ssam 	if (bytes > 0 && verbose)
33010296Ssam 		ptransfer("sent", bytes, &start, &stop);
33110296Ssam 	return;
33210296Ssam bad:
33310296Ssam 	if (data >= 0)
33410296Ssam 		(void) close(data), data = -1;
33510296Ssam 	if (closefunc != NULL && fin != NULL)
336*25907Smckusick 		(*closefunc)(fin), closefunc = NULL;
337*25907Smckusick 	bytes = 0;	/* so we don't print a message if the transfer was aborted */
33810296Ssam 	goto done;
33910296Ssam }
34010296Ssam 
34110296Ssam jmp_buf	recvabort;
34210296Ssam 
34310296Ssam abortrecv()
34410296Ssam {
34510296Ssam 
34610296Ssam 	longjmp(recvabort, 1);
34710296Ssam }
34810296Ssam 
34911651Ssam recvrequest(cmd, local, remote, mode)
35011651Ssam 	char *cmd, *local, *remote, *mode;
35110296Ssam {
35210296Ssam 	FILE *fout, *din, *popen();
35311651Ssam 	int (*closefunc)(), pclose(), fclose(), (*oldintr)();
35411219Ssam 	char buf[BUFSIZ];
355*25907Smckusick 	int expectingreply = 0;
35611651Ssam 	long bytes = 0, hashbytes = sizeof (buf);
35711346Ssam 	register int c, d;
35810296Ssam 	struct timeval start, stop;
35910296Ssam 
36010296Ssam 	closefunc = NULL;
36110296Ssam 	if (setjmp(recvabort))
36210296Ssam 		goto bad;
36310296Ssam 	oldintr = signal(SIGINT, abortrecv);
36410296Ssam 	if (strcmp(local, "-") && *local != '|')
36510296Ssam 		if (access(local, 2) < 0) {
366*25907Smckusick 			if (errno == ENOENT) {
367*25907Smckusick 				char *dir = rindex(local, '/');
36810296Ssam 
369*25907Smckusick 				if (dir != NULL)
370*25907Smckusick 					*dir = 0;
371*25907Smckusick 				d = access(dir ? local : ".", 2);
372*25907Smckusick 				if (dir != NULL)
373*25907Smckusick 					*dir = '/';
374*25907Smckusick 				if (d < 0) {
375*25907Smckusick 					perror(local);
376*25907Smckusick 					goto bad;
377*25907Smckusick 				}
378*25907Smckusick 			} else {
37910296Ssam 				perror(local);
38010296Ssam 				goto bad;
38110296Ssam 			}
38210296Ssam 		}
38310296Ssam 	if (initconn())
38410296Ssam 		goto bad;
38510296Ssam 	if (remote) {
38610296Ssam 		if (command("%s %s", cmd, remote) != PRELIM)
38710296Ssam 			goto bad;
38810296Ssam 	} else
38910296Ssam 		if (command("%s", cmd) != PRELIM)
39010296Ssam 			goto bad;
391*25907Smckusick 	expectingreply++;	/* got preliminary reply, expecting final reply */
39210296Ssam 	if (strcmp(local, "-") == 0)
39310296Ssam 		fout = stdout;
39410296Ssam 	else if (*local == '|') {
395*25907Smckusick 		/*
396*25907Smckusick 		 * Advance local over '|' so don't need to check for
397*25907Smckusick 		 * '|' special case any further.
398*25907Smckusick 		 */
399*25907Smckusick 		local += 1;
400*25907Smckusick 		fout = popen(local, "w");
40110296Ssam 		closefunc = pclose;
40210296Ssam 	} else {
40311651Ssam 		fout = fopen(local, mode);
40410296Ssam 		closefunc = fclose;
40510296Ssam 	}
40610296Ssam 	if (fout == NULL) {
407*25907Smckusick 		perror(local);
40810296Ssam 		goto bad;
40910296Ssam 	}
41010296Ssam 	din = dataconn("r");
41110296Ssam 	if (din == NULL)
41210296Ssam 		goto bad;
41310296Ssam 	gettimeofday(&start, (struct timezone *)0);
41411219Ssam 	switch (type) {
41511219Ssam 
41611219Ssam 	case TYPE_I:
41711219Ssam 	case TYPE_L:
41811346Ssam 		errno = d = 0;
41911219Ssam 		while ((c = read(fileno(din), buf, sizeof (buf))) > 0) {
42011346Ssam 			if ((d = write(fileno(fout), buf, c)) < 0)
42111219Ssam 				break;
42211219Ssam 			bytes += c;
42311651Ssam 			if (hash) {
42411651Ssam 				putchar('#');
42511651Ssam 				fflush(stdout);
42611651Ssam 			}
42711219Ssam 		}
42813213Ssam 		if (hash && bytes > 0) {
42911651Ssam 			putchar('\n');
43011651Ssam 			fflush(stdout);
43111651Ssam 		}
43211219Ssam 		if (c < 0)
43311219Ssam 			perror("netin");
43411346Ssam 		if (d < 0)
43510296Ssam 			perror(local);
43611219Ssam 		break;
43711219Ssam 
43811219Ssam 	case TYPE_A:
43911219Ssam 		while ((c = getc(din)) != EOF) {
44011219Ssam 			if (c == '\r') {
44111651Ssam 				while (hash && (bytes >= hashbytes)) {
44211651Ssam 					putchar('#');
44311651Ssam 					fflush(stdout);
44411651Ssam 					hashbytes += sizeof (buf);
44511651Ssam 				}
44610296Ssam 				bytes++;
44711219Ssam 				if ((c = getc(din)) != '\n') {
44811219Ssam 					if (ferror (fout))
44911219Ssam 						break;
45011219Ssam 					putc ('\r', fout);
45111219Ssam 				}
45211219Ssam 				if (c == '\0') {
45311219Ssam 					bytes++;
45411219Ssam 					continue;
45511219Ssam 				}
45611219Ssam 			}
45711219Ssam 			putc (c, fout);
45811219Ssam 			bytes++;
45910296Ssam 		}
46011651Ssam 		if (hash) {
46113213Ssam 			if (bytes < hashbytes)
46213213Ssam 				putchar('#');
46311651Ssam 			putchar('\n');
46411651Ssam 			fflush(stdout);
46511651Ssam 		}
46611219Ssam 		if (ferror (din))
46711219Ssam 			perror ("netin");
46811219Ssam 		if (ferror (fout))
46911219Ssam 			perror (local);
47011219Ssam 		break;
47110296Ssam 	}
47210296Ssam 	gettimeofday(&stop, (struct timezone *)0);
47310296Ssam 	(void) fclose(din);
47410296Ssam 	if (closefunc != NULL)
475*25907Smckusick 		(*closefunc)(fout), closefunc = NULL;
47610296Ssam done:
477*25907Smckusick 	if (expectingreply) {
478*25907Smckusick 		(void) getreply(0);
479*25907Smckusick 		expectingreply = 0;
480*25907Smckusick 	}
48110296Ssam 	signal(SIGINT, oldintr);
48210296Ssam 	if (bytes > 0 && verbose)
48310296Ssam 		ptransfer("received", bytes, &start, &stop);
48410296Ssam 	return;
48510296Ssam bad:
48610296Ssam 	if (data >= 0)
48710296Ssam 		(void) close(data), data = -1;
48810296Ssam 	if (closefunc != NULL && fout != NULL)
48910296Ssam 		(*closefunc)(fout);
490*25907Smckusick 	bytes = 0;	/* so we don't print a message if the transfer was aborted */
49110296Ssam 	goto done;
49210296Ssam }
49310296Ssam 
49410296Ssam /*
49510296Ssam  * Need to start a listen on the data channel
49610296Ssam  * before we send the command, otherwise the
49710296Ssam  * server's connect may fail.
49810296Ssam  */
49911651Ssam static int sendport = -1;
50011651Ssam 
50110296Ssam initconn()
50210296Ssam {
50310296Ssam 	register char *p, *a;
50411627Ssam 	int result, len;
50517450Slepreau 	int on = 1;
50610296Ssam 
50711651Ssam noport:
50810296Ssam 	data_addr = myctladdr;
50911651Ssam 	if (sendport)
51011651Ssam 		data_addr.sin_port = 0;	/* let system pick one */
51111651Ssam 	if (data != -1)
51211651Ssam 		(void) close (data);
51318287Sralph 	data = socket(AF_INET, SOCK_STREAM, 0);
51410296Ssam 	if (data < 0) {
51510296Ssam 		perror("ftp: socket");
51610296Ssam 		return (1);
51710296Ssam 	}
51812397Ssam 	if (!sendport)
51917450Slepreau 		if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) {
520*25907Smckusick 			perror("ftp: setsockopt (reuse address)");
52112397Ssam 			goto bad;
52212397Ssam 		}
52310296Ssam 	if (bind(data, (char *)&data_addr, sizeof (data_addr), 0) < 0) {
52410296Ssam 		perror("ftp: bind");
52510296Ssam 		goto bad;
52610296Ssam 	}
52710296Ssam 	if (options & SO_DEBUG &&
52817450Slepreau 	    setsockopt(data, SOL_SOCKET, SO_DEBUG, &on, sizeof (on)) < 0)
52910296Ssam 		perror("ftp: setsockopt (ignored)");
53011627Ssam 	len = sizeof (data_addr);
53111627Ssam 	if (getsockname(data, (char *)&data_addr, &len) < 0) {
53211627Ssam 		perror("ftp: getsockname");
53310296Ssam 		goto bad;
53410296Ssam 	}
53510296Ssam 	if (listen(data, 1) < 0) {
53610296Ssam 		perror("ftp: listen");
53710296Ssam 		goto bad;
53810296Ssam 	}
53911651Ssam 	if (sendport) {
54011651Ssam 		a = (char *)&data_addr.sin_addr;
54111651Ssam 		p = (char *)&data_addr.sin_port;
54210296Ssam #define	UC(b)	(((int)b)&0xff)
54311651Ssam 		result =
54411651Ssam 		    command("PORT %d,%d,%d,%d,%d,%d",
54511651Ssam 		      UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
54611651Ssam 		      UC(p[0]), UC(p[1]));
54711651Ssam 		if (result == ERROR && sendport == -1) {
54811651Ssam 			sendport = 0;
54911651Ssam 			goto noport;
55011651Ssam 		}
55111651Ssam 		return (result != COMPLETE);
55211651Ssam 	}
55311651Ssam 	return (0);
55410296Ssam bad:
55510296Ssam 	(void) close(data), data = -1;
55610296Ssam 	return (1);
55710296Ssam }
55810296Ssam 
55910296Ssam FILE *
56010296Ssam dataconn(mode)
56110296Ssam 	char *mode;
56210296Ssam {
56310296Ssam 	struct sockaddr_in from;
56410296Ssam 	int s, fromlen = sizeof (from);
56510296Ssam 
56610296Ssam 	s = accept(data, &from, &fromlen, 0);
56710296Ssam 	if (s < 0) {
56810296Ssam 		perror("ftp: accept");
56910296Ssam 		(void) close(data), data = -1;
57010296Ssam 		return (NULL);
57110296Ssam 	}
57210296Ssam 	(void) close(data);
57310296Ssam 	data = s;
57410296Ssam 	return (fdopen(data, mode));
57510296Ssam }
57610296Ssam 
57710296Ssam ptransfer(direction, bytes, t0, t1)
57810296Ssam 	char *direction;
57911651Ssam 	long bytes;
58010296Ssam 	struct timeval *t0, *t1;
58110296Ssam {
58210296Ssam 	struct timeval td;
58316437Sleres 	float s, bs;
58410296Ssam 
58510296Ssam 	tvsub(&td, t1, t0);
58616437Sleres 	s = td.tv_sec + (td.tv_usec / 1000000.);
58710296Ssam #define	nz(x)	((x) == 0 ? 1 : (x))
58816437Sleres 	bs = bytes / nz(s);
58916437Sleres 	printf("%ld bytes %s in %.2g seconds (%.2g Kbytes/s)\n",
59016437Sleres 		bytes, direction, s, bs / 1024.);
59110296Ssam }
59210296Ssam 
59310296Ssam tvadd(tsum, t0)
59410296Ssam 	struct timeval *tsum, *t0;
59510296Ssam {
59610296Ssam 
59710296Ssam 	tsum->tv_sec += t0->tv_sec;
59810296Ssam 	tsum->tv_usec += t0->tv_usec;
59910296Ssam 	if (tsum->tv_usec > 1000000)
60010296Ssam 		tsum->tv_sec++, tsum->tv_usec -= 1000000;
60110296Ssam }
60210296Ssam 
60310296Ssam tvsub(tdiff, t1, t0)
60410296Ssam 	struct timeval *tdiff, *t1, *t0;
60510296Ssam {
60610296Ssam 
60710296Ssam 	tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
60810296Ssam 	tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
60910296Ssam 	if (tdiff->tv_usec < 0)
61010296Ssam 		tdiff->tv_sec--, tdiff->tv_usec += 1000000;
61110296Ssam }
612