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