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