install with -s
[unix-history] / usr / src / usr.bin / ftp / ftp.c
CommitLineData
edf71f48
DF
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
e0b077fe 7#ifndef lint
edf71f48
DF
8static char sccsid[] = "@(#)ftp.c 5.1 (Berkeley) %G%";
9#endif not lint
e0b077fe
SL
10
11#include <sys/param.h>
12#include <sys/stat.h>
13#include <sys/ioctl.h>
14#include <sys/socket.h>
19371098 15#include <sys/time.h>
e0b077fe
SL
16
17#include <netinet/in.h>
62c4a390 18#include <arpa/ftp.h>
e0b077fe
SL
19
20#include <stdio.h>
21#include <signal.h>
e0b077fe
SL
22#include <errno.h>
23#include <netdb.h>
24
e0b077fe
SL
25#include "ftp_var.h"
26
27struct sockaddr_in hisctladdr;
28struct sockaddr_in data_addr;
29int data = -1;
30int connected;
31struct sockaddr_in myctladdr;
32
33FILE *cin, *cout;
34FILE *dataconn();
35
36struct hostent *
37hookup(host, port)
38 char *host;
39 int port;
40{
41 register struct hostent *hp;
8c1d51c7 42 int s, len;
e0b077fe
SL
43
44 bzero((char *)&hisctladdr, sizeof (hisctladdr));
45 hp = gethostbyname(host);
6e88ca41 46 if (hp == NULL) {
e0b077fe
SL
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 }
6e88ca41
SL
66 hostname = hp->h_name;
67 hisctladdr.sin_family = hp->h_addrtype;
71a655cd 68 s = socket(hp->h_addrtype, SOCK_STREAM, 0);
e0b077fe
SL
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 }
8c1d51c7
SL
83 len = sizeof (myctladdr);
84 if (getsockname(s, (char *)&myctladdr, &len) < 0) {
85 perror("ftp: getsockname");
e0b077fe
SL
86 goto bad;
87 }
88 cin = fdopen(s, "r");
89 cout = fdopen(s, "w");
d33c618b 90 if (cin == NULL || cout == NULL) {
e0b077fe
SL
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);
102bad:
103 close(s);
104 return ((struct hostent *)0);
105}
106
107login(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*/
133command(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 }
d33c618b
SL
143 if (cout == NULL) {
144 perror ("No control connection for command");
145 return (0);
146 }
e0b077fe
SL
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
155getreply(expecteof)
156 int expecteof;
157{
d33c618b 158 register int c, n;
e0b077fe
SL
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 }
7968a79f 182 if (verbose || n == '5') {
e0b077fe 183 putchar(c);
7968a79f
SL
184 (void) fflush (stdout);
185 }
e0b077fe
SL
186 if (continuation && code != originalcode) {
187 if (originalcode == 0)
188 originalcode = code;
189 continue;
190 }
d33c618b 191 if (expecteof || empty(cin))
e0b077fe
SL
192 return (n - '0');
193 }
194}
195
196empty(f)
197 FILE *f;
198{
5ac6fc46 199 long mask;
e0b077fe
SL
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
210jmp_buf sendabort;
211
212abortsend()
213{
214
215 longjmp(sendabort, 1);
216}
217
218sendrequest(cmd, local, remote)
219 char *cmd, *local, *remote;
220{
221 FILE *fin, *dout, *popen();
222 int (*closefunc)(), pclose(), fclose(), (*oldintr)();
d33c618b 223 char buf[BUFSIZ];
5ac6fc46 224 long bytes = 0, hashbytes = sizeof (buf);
7968a79f 225 register int c, d;
e0b077fe
SL
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) {
b4b22f0c 251 fprintf(stderr, "%s: not a plain file.\n", local);
e0b077fe
SL
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);
d33c618b
SL
267 switch (type) {
268
269 case TYPE_I:
270 case TYPE_L:
7968a79f 271 errno = d = 0;
d33c618b 272 while ((c = read(fileno (fin), buf, sizeof (buf))) > 0) {
7968a79f 273 if ((d = write(fileno (dout), buf, c)) < 0)
d33c618b
SL
274 break;
275 bytes += c;
5ac6fc46
SL
276 if (hash) {
277 putchar('#');
278 fflush(stdout);
279 }
280 }
614e24b6 281 if (hash && bytes > 0) {
5ac6fc46
SL
282 putchar('\n');
283 fflush(stdout);
d33c618b
SL
284 }
285 if (c < 0)
286 perror(local);
7968a79f 287 if (d < 0)
d33c618b
SL
288 perror("netout");
289 break;
290
291 case TYPE_A:
292 while ((c = getc(fin)) != EOF) {
293 if (c == '\n') {
5ac6fc46
SL
294 while (hash && (bytes >= hashbytes)) {
295 putchar('#');
296 fflush(stdout);
297 hashbytes += sizeof (buf);
298 }
d33c618b
SL
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 }
5ac6fc46 311 if (hash) {
614e24b6
SL
312 if (bytes < hashbytes)
313 putchar('#');
5ac6fc46
SL
314 putchar('\n');
315 fflush(stdout);
316 }
d33c618b
SL
317 if (ferror(fin))
318 perror(local);
7968a79f 319 if (ferror(dout))
d33c618b
SL
320 perror("netout");
321 break;
e0b077fe
SL
322 }
323 gettimeofday(&stop, (struct timezone *)0);
324 if (closefunc != NULL)
325 (*closefunc)(fin);
326 (void) fclose(dout);
e0b077fe
SL
327 (void) getreply(0);
328done:
329 signal(SIGINT, oldintr);
330 if (bytes > 0 && verbose)
331 ptransfer("sent", bytes, &start, &stop);
332 return;
333bad:
334 if (data >= 0)
335 (void) close(data), data = -1;
336 if (closefunc != NULL && fin != NULL)
337 (*closefunc)(fin);
338 goto done;
339}
340
341jmp_buf recvabort;
342
343abortrecv()
344{
345
346 longjmp(recvabort, 1);
347}
348
5ac6fc46
SL
349recvrequest(cmd, local, remote, mode)
350 char *cmd, *local, *remote, *mode;
e0b077fe
SL
351{
352 FILE *fout, *din, *popen();
7968a79f 353 int (*closefunc)(), pclose(), fclose(), (*oldintr)();
5ac6fc46
SL
354 char buf[BUFSIZ];
355 long bytes = 0, hashbytes = sizeof (buf);
7968a79f 356 register int c, d;
e0b077fe
SL
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;
f0773a1d
RC
369 d = access(dir ? local : ".", 2);
370 if (dir != NULL)
371 *dir = '/';
372 if (d < 0) {
e0b077fe
SL
373 perror(local);
374 goto bad;
375 }
e0b077fe
SL
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 {
5ac6fc46 391 fout = fopen(local, mode);
e0b077fe
SL
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);
d33c618b
SL
402 switch (type) {
403
404 case TYPE_I:
405 case TYPE_L:
7968a79f 406 errno = d = 0;
d33c618b 407 while ((c = read(fileno(din), buf, sizeof (buf))) > 0) {
7968a79f 408 if ((d = write(fileno(fout), buf, c)) < 0)
d33c618b
SL
409 break;
410 bytes += c;
5ac6fc46
SL
411 if (hash) {
412 putchar('#');
413 fflush(stdout);
414 }
415 }
614e24b6 416 if (hash && bytes > 0) {
5ac6fc46
SL
417 putchar('\n');
418 fflush(stdout);
d33c618b
SL
419 }
420 if (c < 0)
421 perror("netin");
7968a79f 422 if (d < 0)
e0b077fe 423 perror(local);
d33c618b
SL
424 break;
425
426 case TYPE_A:
427 while ((c = getc(din)) != EOF) {
428 if (c == '\r') {
5ac6fc46
SL
429 while (hash && (bytes >= hashbytes)) {
430 putchar('#');
431 fflush(stdout);
432 hashbytes += sizeof (buf);
433 }
e0b077fe 434 bytes++;
d33c618b
SL
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++;
e0b077fe 447 }
5ac6fc46 448 if (hash) {
614e24b6
SL
449 if (bytes < hashbytes)
450 putchar('#');
5ac6fc46
SL
451 putchar('\n');
452 fflush(stdout);
453 }
d33c618b
SL
454 if (ferror (din))
455 perror ("netin");
456 if (ferror (fout))
457 perror (local);
458 break;
e0b077fe
SL
459 }
460 gettimeofday(&stop, (struct timezone *)0);
461 (void) fclose(din);
462 if (closefunc != NULL)
463 (*closefunc)(fout);
464 (void) getreply(0);
465done:
466 signal(SIGINT, oldintr);
467 if (bytes > 0 && verbose)
468 ptransfer("received", bytes, &start, &stop);
469 return;
470bad:
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 */
5ac6fc46
SL
483static int sendport = -1;
484
e0b077fe
SL
485initconn()
486{
487 register char *p, *a;
8c1d51c7 488 int result, len;
0975b26d 489 int on = 1;
e0b077fe 490
5ac6fc46 491noport:
e0b077fe 492 data_addr = myctladdr;
5ac6fc46
SL
493 if (sendport)
494 data_addr.sin_port = 0; /* let system pick one */
495 if (data != -1)
496 (void) close (data);
71a655cd 497 data = socket(AF_INET, SOCK_STREAM, 0);
e0b077fe
SL
498 if (data < 0) {
499 perror("ftp: socket");
500 return (1);
501 }
62c4a390 502 if (!sendport)
0975b26d 503 if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) {
62c4a390
SL
504 perror("ftp: setsockopt (resuse address)");
505 goto bad;
506 }
e0b077fe
SL
507 if (bind(data, (char *)&data_addr, sizeof (data_addr), 0) < 0) {
508 perror("ftp: bind");
509 goto bad;
510 }
e0b077fe 511 if (options & SO_DEBUG &&
0975b26d 512 setsockopt(data, SOL_SOCKET, SO_DEBUG, &on, sizeof (on)) < 0)
e0b077fe 513 perror("ftp: setsockopt (ignored)");
8c1d51c7
SL
514 len = sizeof (data_addr);
515 if (getsockname(data, (char *)&data_addr, &len) < 0) {
516 perror("ftp: getsockname");
e0b077fe
SL
517 goto bad;
518 }
519 if (listen(data, 1) < 0) {
520 perror("ftp: listen");
521 goto bad;
522 }
5ac6fc46
SL
523 if (sendport) {
524 a = (char *)&data_addr.sin_addr;
525 p = (char *)&data_addr.sin_port;
e0b077fe 526#define UC(b) (((int)b)&0xff)
5ac6fc46
SL
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);
e0b077fe
SL
538bad:
539 (void) close(data), data = -1;
540 return (1);
541}
542
543FILE *
544dataconn(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
561ptransfer(direction, bytes, t0, t1)
562 char *direction;
5ac6fc46 563 long bytes;
e0b077fe
SL
564 struct timeval *t0, *t1;
565{
566 struct timeval td;
6ee561e3 567 float s, bs;
e0b077fe
SL
568
569 tvsub(&td, t1, t0);
6ee561e3 570 s = td.tv_sec + (td.tv_usec / 1000000.);
e0b077fe 571#define nz(x) ((x) == 0 ? 1 : (x))
6ee561e3
CL
572 bs = bytes / nz(s);
573 printf("%ld bytes %s in %.2g seconds (%.2g Kbytes/s)\n",
574 bytes, direction, s, bs / 1024.);
e0b077fe
SL
575}
576
577tvadd(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
587tvsub(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}