xref: /csrg-svn/usr.bin/ftp/ftp.c (revision 25100)
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*25100Sbloom static char sccsid[] = "@(#)ftp.c	5.3 (Berkeley) 10/05/85";
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 
3610296Ssam struct hostent *
3710296Ssam hookup(host, port)
3810296Ssam 	char *host;
3910296Ssam 	int port;
4010296Ssam {
4110296Ssam 	register struct hostent *hp;
4211627Ssam 	int s, len;
43*25100Sbloom 	long addr;
4410296Ssam 
4510296Ssam 	bzero((char *)&hisctladdr, sizeof (hisctladdr));
46*25100Sbloom 	if ((addr = inet_addr(host)) == -1) {
47*25100Sbloom 		hp = gethostbyname(host);
48*25100Sbloom 	} else  {
4910296Ssam 		static struct hostent def;
5010296Ssam 		static struct in_addr defaddr;
51*25100Sbloom 		static char namebuf[MAXHOSTNAMELEN];
5225099Sbloom 		static char *addrbuf;
53*25100Sbloom 		long inet_addr();
5410296Ssam 
55*25100Sbloom 		defaddr.s_addr = addr;
5610296Ssam 		strcpy(namebuf, host);
5710296Ssam 		def.h_name = namebuf;
5810296Ssam 		hostname = namebuf;
5925099Sbloom 		def.h_addr_list = &addrbuf;
6010296Ssam 		def.h_addr = (char *)&defaddr;
6110296Ssam 		def.h_length = sizeof (struct in_addr);
6210296Ssam 		def.h_addrtype = AF_INET;
6310296Ssam 		def.h_aliases = 0;
6410296Ssam 		hp = &def;
6510296Ssam 	}
66*25100Sbloom 	if (hp == NULL) {
67*25100Sbloom 		fprintf(stderr, "%s: Unknown host.\n", host);
68*25100Sbloom 		return (0);
69*25100Sbloom 	}
7011283Ssam 	hostname = hp->h_name;
7111283Ssam 	hisctladdr.sin_family = hp->h_addrtype;
7218287Sralph 	s = socket(hp->h_addrtype, SOCK_STREAM, 0);
7310296Ssam 	if (s < 0) {
7410296Ssam 		perror("ftp: socket");
7510296Ssam 		return (0);
7610296Ssam 	}
7710296Ssam 	if (bind(s, (char *)&hisctladdr, sizeof (hisctladdr), 0) < 0) {
7810296Ssam 		perror("ftp: bind");
7910296Ssam 		goto bad;
8010296Ssam 	}
8110296Ssam 	bcopy(hp->h_addr, (char *)&hisctladdr.sin_addr, hp->h_length);
8210296Ssam 	hisctladdr.sin_port = port;
8310296Ssam 	if (connect(s, (char *)&hisctladdr, sizeof (hisctladdr), 0) < 0) {
8410296Ssam 		perror("ftp: connect");
8510296Ssam 		goto bad;
8610296Ssam 	}
8711627Ssam 	len = sizeof (myctladdr);
8811627Ssam 	if (getsockname(s, (char *)&myctladdr, &len) < 0) {
8911627Ssam 		perror("ftp: getsockname");
9010296Ssam 		goto bad;
9110296Ssam 	}
9210296Ssam 	cin = fdopen(s, "r");
9310296Ssam 	cout = fdopen(s, "w");
9411219Ssam 	if (cin == NULL || cout == NULL) {
9510296Ssam 		fprintf(stderr, "ftp: fdopen failed.\n");
9610296Ssam 		if (cin)
9710296Ssam 			fclose(cin);
9810296Ssam 		if (cout)
9910296Ssam 			fclose(cout);
10010296Ssam 		goto bad;
10110296Ssam 	}
10210296Ssam 	if (verbose)
10310296Ssam 		printf("Connected to %s.\n", hp->h_name);
10410296Ssam 	(void) getreply(0); 		/* read startup message from server */
10510296Ssam 	return (hp);
10610296Ssam bad:
10710296Ssam 	close(s);
10810296Ssam 	return ((struct hostent *)0);
10910296Ssam }
11010296Ssam 
11110296Ssam login(hp)
11210296Ssam 	struct hostent *hp;
11310296Ssam {
11410296Ssam 	char acct[80];
11510296Ssam 	char *user, *pass;
11610296Ssam 	int n;
11710296Ssam 
11810296Ssam 	user = pass = 0;
11910296Ssam 	ruserpass(hp->h_name, &user, &pass);
12010296Ssam 	n = command("USER %s", user);
12110296Ssam 	if (n == CONTINUE)
12210296Ssam 		n = command("PASS %s", pass);
12310296Ssam 	if (n == CONTINUE) {
12410296Ssam 		printf("Account: "); (void) fflush(stdout);
12510296Ssam 		(void) fgets(acct, sizeof(acct) - 1, stdin);
12610296Ssam 		acct[strlen(acct) - 1] = '\0';
12710296Ssam 		n = command("ACCT %s", acct);
12810296Ssam 	}
12910296Ssam 	if (n != COMPLETE) {
13010296Ssam 		fprintf(stderr, "Login failed.\n");
13110296Ssam 		return (0);
13210296Ssam 	}
13310296Ssam 	return (1);
13410296Ssam }
13510296Ssam 
13610296Ssam /*VARARGS 1*/
13710296Ssam command(fmt, args)
13810296Ssam 	char *fmt;
13910296Ssam {
14010296Ssam 
14110296Ssam 	if (debug) {
14210296Ssam 		printf("---> ");
14310296Ssam 		_doprnt(fmt, &args, stdout);
14410296Ssam 		printf("\n");
14510296Ssam 		(void) fflush(stdout);
14610296Ssam 	}
14711219Ssam 	if (cout == NULL) {
14811219Ssam 		perror ("No control connection for command");
14911219Ssam 		return (0);
15011219Ssam 	}
15110296Ssam 	_doprnt(fmt, &args, cout);
15210296Ssam 	fprintf(cout, "\r\n");
15310296Ssam 	(void) fflush(cout);
15410296Ssam 	return (getreply(!strcmp(fmt, "QUIT")));
15510296Ssam }
15610296Ssam 
15710296Ssam #include <ctype.h>
15810296Ssam 
15910296Ssam getreply(expecteof)
16010296Ssam 	int expecteof;
16110296Ssam {
16211219Ssam 	register int c, n;
16310296Ssam 	register int code, dig;
16410296Ssam 	int originalcode = 0, continuation = 0;
16510296Ssam 
16610296Ssam 	for (;;) {
16710296Ssam 		dig = n = code = 0;
16810296Ssam 		while ((c = getc(cin)) != '\n') {
16910296Ssam 			dig++;
17010296Ssam 			if (c == EOF) {
17110296Ssam 				if (expecteof)
17210296Ssam 					return (0);
17310296Ssam 				lostpeer();
17410296Ssam 				exit(1);
17510296Ssam 			}
17610296Ssam 			if (verbose && c != '\r' ||
17710296Ssam 			    (n == '5' && dig > 4))
17810296Ssam 				putchar(c);
17910296Ssam 			if (dig < 4 && isdigit(c))
18010296Ssam 				code = code * 10 + (c - '0');
18110296Ssam 			if (dig == 4 && c == '-')
18210296Ssam 				continuation++;
18310296Ssam 			if (n == 0)
18410296Ssam 				n = c;
18510296Ssam 		}
18611346Ssam 		if (verbose || n == '5') {
18710296Ssam 			putchar(c);
18811346Ssam 			(void) fflush (stdout);
18911346Ssam 		}
19010296Ssam 		if (continuation && code != originalcode) {
19110296Ssam 			if (originalcode == 0)
19210296Ssam 				originalcode = code;
19310296Ssam 			continue;
19410296Ssam 		}
19511219Ssam 		if (expecteof || empty(cin))
19610296Ssam 			return (n - '0');
19710296Ssam 	}
19810296Ssam }
19910296Ssam 
20010296Ssam empty(f)
20110296Ssam 	FILE *f;
20210296Ssam {
20311651Ssam 	long mask;
20410296Ssam 	struct timeval t;
20510296Ssam 
20610296Ssam 	if (f->_cnt > 0)
20710296Ssam 		return (0);
20810296Ssam 	mask = (1 << fileno(f));
20910296Ssam 	t.tv_sec = t.tv_usec = 0;
21010296Ssam 	(void) select(20, &mask, 0, 0, &t);
21110296Ssam 	return (mask == 0);
21210296Ssam }
21310296Ssam 
21410296Ssam jmp_buf	sendabort;
21510296Ssam 
21610296Ssam abortsend()
21710296Ssam {
21810296Ssam 
21910296Ssam 	longjmp(sendabort, 1);
22010296Ssam }
22110296Ssam 
22210296Ssam sendrequest(cmd, local, remote)
22310296Ssam 	char *cmd, *local, *remote;
22410296Ssam {
22510296Ssam 	FILE *fin, *dout, *popen();
22610296Ssam 	int (*closefunc)(), pclose(), fclose(), (*oldintr)();
22711219Ssam 	char buf[BUFSIZ];
22811651Ssam 	long bytes = 0, hashbytes = sizeof (buf);
22911346Ssam 	register int c, d;
23010296Ssam 	struct stat st;
23110296Ssam 	struct timeval start, stop;
23210296Ssam 
23310296Ssam 	closefunc = NULL;
23410296Ssam 	if (setjmp(sendabort))
23510296Ssam 		goto bad;
23610296Ssam 	oldintr = signal(SIGINT, abortsend);
23710296Ssam 	if (strcmp(local, "-") == 0)
23810296Ssam 		fin = stdin;
23910296Ssam 	else if (*local == '|') {
24010296Ssam 		fin = popen(local + 1, "r");
24110296Ssam 		if (fin == NULL) {
24210296Ssam 			perror(local + 1);
24310296Ssam 			goto bad;
24410296Ssam 		}
24510296Ssam 		closefunc = pclose;
24610296Ssam 	} else {
24710296Ssam 		fin = fopen(local, "r");
24810296Ssam 		if (fin == NULL) {
24910296Ssam 			perror(local);
25010296Ssam 			goto bad;
25110296Ssam 		}
25210296Ssam 		closefunc = fclose;
25310296Ssam 		if (fstat(fileno(fin), &st) < 0 ||
25410296Ssam 		    (st.st_mode&S_IFMT) != S_IFREG) {
25517949Sralph 			fprintf(stderr, "%s: not a plain file.\n", local);
25610296Ssam 			goto bad;
25710296Ssam 		}
25810296Ssam 	}
25910296Ssam 	if (initconn())
26010296Ssam 		goto bad;
26110296Ssam 	if (remote) {
26210296Ssam 		if (command("%s %s", cmd, remote) != PRELIM)
26310296Ssam 			goto bad;
26410296Ssam 	} else
26510296Ssam 		if (command("%s", cmd) != PRELIM)
26610296Ssam 			goto bad;
26710296Ssam 	dout = dataconn("w");
26810296Ssam 	if (dout == NULL)
26910296Ssam 		goto bad;
27010296Ssam 	gettimeofday(&start, (struct timezone *)0);
27111219Ssam 	switch (type) {
27211219Ssam 
27311219Ssam 	case TYPE_I:
27411219Ssam 	case TYPE_L:
27511346Ssam 		errno = d = 0;
27611219Ssam 		while ((c = read(fileno (fin), buf, sizeof (buf))) > 0) {
27711346Ssam 			if ((d = write(fileno (dout), buf, c)) < 0)
27811219Ssam 				break;
27911219Ssam 			bytes += c;
28011651Ssam 			if (hash) {
28111651Ssam 				putchar('#');
28211651Ssam 				fflush(stdout);
28311651Ssam 			}
28411219Ssam 		}
28513213Ssam 		if (hash && bytes > 0) {
28611651Ssam 			putchar('\n');
28711651Ssam 			fflush(stdout);
28811651Ssam 		}
28911219Ssam 		if (c < 0)
29011219Ssam 			perror(local);
29111346Ssam 		if (d < 0)
29211219Ssam 			perror("netout");
29311219Ssam 		break;
29411219Ssam 
29511219Ssam 	case TYPE_A:
29611219Ssam 		while ((c = getc(fin)) != EOF) {
29711219Ssam 			if (c == '\n') {
29811651Ssam 				while (hash && (bytes >= hashbytes)) {
29911651Ssam 					putchar('#');
30011651Ssam 					fflush(stdout);
30111651Ssam 					hashbytes += sizeof (buf);
30211651Ssam 				}
30311219Ssam 				if (ferror(dout))
30411219Ssam 					break;
30511219Ssam 				putc('\r', dout);
30611219Ssam 				bytes++;
30711219Ssam 			}
30811219Ssam 			putc(c, dout);
30911219Ssam 			bytes++;
31011219Ssam 			if (c == '\r') {
31111219Ssam 				putc('\0', dout);
31211219Ssam 				bytes++;
31311219Ssam 			}
31411219Ssam 		}
31511651Ssam 		if (hash) {
31613213Ssam 			if (bytes < hashbytes)
31713213Ssam 				putchar('#');
31811651Ssam 			putchar('\n');
31911651Ssam 			fflush(stdout);
32011651Ssam 		}
32111219Ssam 		if (ferror(fin))
32211219Ssam 			perror(local);
32311346Ssam 		if (ferror(dout))
32411219Ssam 			perror("netout");
32511219Ssam 		break;
32610296Ssam 	}
32710296Ssam 	gettimeofday(&stop, (struct timezone *)0);
32810296Ssam 	if (closefunc != NULL)
32910296Ssam 		(*closefunc)(fin);
33010296Ssam 	(void) fclose(dout);
33110296Ssam 	(void) getreply(0);
33210296Ssam done:
33310296Ssam 	signal(SIGINT, oldintr);
33410296Ssam 	if (bytes > 0 && verbose)
33510296Ssam 		ptransfer("sent", bytes, &start, &stop);
33610296Ssam 	return;
33710296Ssam bad:
33810296Ssam 	if (data >= 0)
33910296Ssam 		(void) close(data), data = -1;
34010296Ssam 	if (closefunc != NULL && fin != NULL)
34110296Ssam 		(*closefunc)(fin);
34210296Ssam 	goto done;
34310296Ssam }
34410296Ssam 
34510296Ssam jmp_buf	recvabort;
34610296Ssam 
34710296Ssam abortrecv()
34810296Ssam {
34910296Ssam 
35010296Ssam 	longjmp(recvabort, 1);
35110296Ssam }
35210296Ssam 
35311651Ssam recvrequest(cmd, local, remote, mode)
35411651Ssam 	char *cmd, *local, *remote, *mode;
35510296Ssam {
35610296Ssam 	FILE *fout, *din, *popen();
35711651Ssam 	int (*closefunc)(), pclose(), fclose(), (*oldintr)();
35811219Ssam 	char buf[BUFSIZ];
35911651Ssam 	long bytes = 0, hashbytes = sizeof (buf);
36011346Ssam 	register int c, d;
36110296Ssam 	struct timeval start, stop;
36210296Ssam 
36310296Ssam 	closefunc = NULL;
36410296Ssam 	if (setjmp(recvabort))
36510296Ssam 		goto bad;
36610296Ssam 	oldintr = signal(SIGINT, abortrecv);
36710296Ssam 	if (strcmp(local, "-") && *local != '|')
36810296Ssam 		if (access(local, 2) < 0) {
36910296Ssam 			char *dir = rindex(local, '/');
37010296Ssam 
37110296Ssam 			if (dir != NULL)
37210296Ssam 				*dir = 0;
37317305Sralph 			d = access(dir ? local : ".", 2);
37417305Sralph 			if (dir != NULL)
37517305Sralph 				*dir = '/';
37617305Sralph 			if (d < 0) {
37710296Ssam 				perror(local);
37810296Ssam 				goto bad;
37910296Ssam 			}
38010296Ssam 		}
38110296Ssam 	if (initconn())
38210296Ssam 		goto bad;
38310296Ssam 	if (remote) {
38410296Ssam 		if (command("%s %s", cmd, remote) != PRELIM)
38510296Ssam 			goto bad;
38610296Ssam 	} else
38710296Ssam 		if (command("%s", cmd) != PRELIM)
38810296Ssam 			goto bad;
38910296Ssam 	if (strcmp(local, "-") == 0)
39010296Ssam 		fout = stdout;
39110296Ssam 	else if (*local == '|') {
39210296Ssam 		fout = popen(local + 1, "w");
39310296Ssam 		closefunc = pclose;
39410296Ssam 	} else {
39511651Ssam 		fout = fopen(local, mode);
39610296Ssam 		closefunc = fclose;
39710296Ssam 	}
39810296Ssam 	if (fout == NULL) {
39910296Ssam 		perror(local + 1);
40010296Ssam 		goto bad;
40110296Ssam 	}
40210296Ssam 	din = dataconn("r");
40310296Ssam 	if (din == NULL)
40410296Ssam 		goto bad;
40510296Ssam 	gettimeofday(&start, (struct timezone *)0);
40611219Ssam 	switch (type) {
40711219Ssam 
40811219Ssam 	case TYPE_I:
40911219Ssam 	case TYPE_L:
41011346Ssam 		errno = d = 0;
41111219Ssam 		while ((c = read(fileno(din), buf, sizeof (buf))) > 0) {
41211346Ssam 			if ((d = write(fileno(fout), buf, c)) < 0)
41311219Ssam 				break;
41411219Ssam 			bytes += c;
41511651Ssam 			if (hash) {
41611651Ssam 				putchar('#');
41711651Ssam 				fflush(stdout);
41811651Ssam 			}
41911219Ssam 		}
42013213Ssam 		if (hash && bytes > 0) {
42111651Ssam 			putchar('\n');
42211651Ssam 			fflush(stdout);
42311651Ssam 		}
42411219Ssam 		if (c < 0)
42511219Ssam 			perror("netin");
42611346Ssam 		if (d < 0)
42710296Ssam 			perror(local);
42811219Ssam 		break;
42911219Ssam 
43011219Ssam 	case TYPE_A:
43111219Ssam 		while ((c = getc(din)) != EOF) {
43211219Ssam 			if (c == '\r') {
43311651Ssam 				while (hash && (bytes >= hashbytes)) {
43411651Ssam 					putchar('#');
43511651Ssam 					fflush(stdout);
43611651Ssam 					hashbytes += sizeof (buf);
43711651Ssam 				}
43810296Ssam 				bytes++;
43911219Ssam 				if ((c = getc(din)) != '\n') {
44011219Ssam 					if (ferror (fout))
44111219Ssam 						break;
44211219Ssam 					putc ('\r', fout);
44311219Ssam 				}
44411219Ssam 				if (c == '\0') {
44511219Ssam 					bytes++;
44611219Ssam 					continue;
44711219Ssam 				}
44811219Ssam 			}
44911219Ssam 			putc (c, fout);
45011219Ssam 			bytes++;
45110296Ssam 		}
45211651Ssam 		if (hash) {
45313213Ssam 			if (bytes < hashbytes)
45413213Ssam 				putchar('#');
45511651Ssam 			putchar('\n');
45611651Ssam 			fflush(stdout);
45711651Ssam 		}
45811219Ssam 		if (ferror (din))
45911219Ssam 			perror ("netin");
46011219Ssam 		if (ferror (fout))
46111219Ssam 			perror (local);
46211219Ssam 		break;
46310296Ssam 	}
46410296Ssam 	gettimeofday(&stop, (struct timezone *)0);
46510296Ssam 	(void) fclose(din);
46610296Ssam 	if (closefunc != NULL)
46710296Ssam 		(*closefunc)(fout);
46810296Ssam 	(void) getreply(0);
46910296Ssam done:
47010296Ssam 	signal(SIGINT, oldintr);
47110296Ssam 	if (bytes > 0 && verbose)
47210296Ssam 		ptransfer("received", bytes, &start, &stop);
47310296Ssam 	return;
47410296Ssam bad:
47510296Ssam 	if (data >= 0)
47610296Ssam 		(void) close(data), data = -1;
47710296Ssam 	if (closefunc != NULL && fout != NULL)
47810296Ssam 		(*closefunc)(fout);
47910296Ssam 	goto done;
48010296Ssam }
48110296Ssam 
48210296Ssam /*
48310296Ssam  * Need to start a listen on the data channel
48410296Ssam  * before we send the command, otherwise the
48510296Ssam  * server's connect may fail.
48610296Ssam  */
48711651Ssam static int sendport = -1;
48811651Ssam 
48910296Ssam initconn()
49010296Ssam {
49110296Ssam 	register char *p, *a;
49211627Ssam 	int result, len;
49317450Slepreau 	int on = 1;
49410296Ssam 
49511651Ssam noport:
49610296Ssam 	data_addr = myctladdr;
49711651Ssam 	if (sendport)
49811651Ssam 		data_addr.sin_port = 0;	/* let system pick one */
49911651Ssam 	if (data != -1)
50011651Ssam 		(void) close (data);
50118287Sralph 	data = socket(AF_INET, SOCK_STREAM, 0);
50210296Ssam 	if (data < 0) {
50310296Ssam 		perror("ftp: socket");
50410296Ssam 		return (1);
50510296Ssam 	}
50612397Ssam 	if (!sendport)
50717450Slepreau 		if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) {
50812397Ssam 			perror("ftp: setsockopt (resuse address)");
50912397Ssam 			goto bad;
51012397Ssam 		}
51110296Ssam 	if (bind(data, (char *)&data_addr, sizeof (data_addr), 0) < 0) {
51210296Ssam 		perror("ftp: bind");
51310296Ssam 		goto bad;
51410296Ssam 	}
51510296Ssam 	if (options & SO_DEBUG &&
51617450Slepreau 	    setsockopt(data, SOL_SOCKET, SO_DEBUG, &on, sizeof (on)) < 0)
51710296Ssam 		perror("ftp: setsockopt (ignored)");
51811627Ssam 	len = sizeof (data_addr);
51911627Ssam 	if (getsockname(data, (char *)&data_addr, &len) < 0) {
52011627Ssam 		perror("ftp: getsockname");
52110296Ssam 		goto bad;
52210296Ssam 	}
52310296Ssam 	if (listen(data, 1) < 0) {
52410296Ssam 		perror("ftp: listen");
52510296Ssam 		goto bad;
52610296Ssam 	}
52711651Ssam 	if (sendport) {
52811651Ssam 		a = (char *)&data_addr.sin_addr;
52911651Ssam 		p = (char *)&data_addr.sin_port;
53010296Ssam #define	UC(b)	(((int)b)&0xff)
53111651Ssam 		result =
53211651Ssam 		    command("PORT %d,%d,%d,%d,%d,%d",
53311651Ssam 		      UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
53411651Ssam 		      UC(p[0]), UC(p[1]));
53511651Ssam 		if (result == ERROR && sendport == -1) {
53611651Ssam 			sendport = 0;
53711651Ssam 			goto noport;
53811651Ssam 		}
53911651Ssam 		return (result != COMPLETE);
54011651Ssam 	}
54111651Ssam 	return (0);
54210296Ssam bad:
54310296Ssam 	(void) close(data), data = -1;
54410296Ssam 	return (1);
54510296Ssam }
54610296Ssam 
54710296Ssam FILE *
54810296Ssam dataconn(mode)
54910296Ssam 	char *mode;
55010296Ssam {
55110296Ssam 	struct sockaddr_in from;
55210296Ssam 	int s, fromlen = sizeof (from);
55310296Ssam 
55410296Ssam 	s = accept(data, &from, &fromlen, 0);
55510296Ssam 	if (s < 0) {
55610296Ssam 		perror("ftp: accept");
55710296Ssam 		(void) close(data), data = -1;
55810296Ssam 		return (NULL);
55910296Ssam 	}
56010296Ssam 	(void) close(data);
56110296Ssam 	data = s;
56210296Ssam 	return (fdopen(data, mode));
56310296Ssam }
56410296Ssam 
56510296Ssam ptransfer(direction, bytes, t0, t1)
56610296Ssam 	char *direction;
56711651Ssam 	long bytes;
56810296Ssam 	struct timeval *t0, *t1;
56910296Ssam {
57010296Ssam 	struct timeval td;
57116437Sleres 	float s, bs;
57210296Ssam 
57310296Ssam 	tvsub(&td, t1, t0);
57416437Sleres 	s = td.tv_sec + (td.tv_usec / 1000000.);
57510296Ssam #define	nz(x)	((x) == 0 ? 1 : (x))
57616437Sleres 	bs = bytes / nz(s);
57716437Sleres 	printf("%ld bytes %s in %.2g seconds (%.2g Kbytes/s)\n",
57816437Sleres 		bytes, direction, s, bs / 1024.);
57910296Ssam }
58010296Ssam 
58110296Ssam tvadd(tsum, t0)
58210296Ssam 	struct timeval *tsum, *t0;
58310296Ssam {
58410296Ssam 
58510296Ssam 	tsum->tv_sec += t0->tv_sec;
58610296Ssam 	tsum->tv_usec += t0->tv_usec;
58710296Ssam 	if (tsum->tv_usec > 1000000)
58810296Ssam 		tsum->tv_sec++, tsum->tv_usec -= 1000000;
58910296Ssam }
59010296Ssam 
59110296Ssam tvsub(tdiff, t1, t0)
59210296Ssam 	struct timeval *tdiff, *t1, *t0;
59310296Ssam {
59410296Ssam 
59510296Ssam 	tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
59610296Ssam 	tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
59710296Ssam 	if (tdiff->tv_usec < 0)
59810296Ssam 		tdiff->tv_sec--, tdiff->tv_usec += 1000000;
59910296Ssam }
600