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