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