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