4.3BSD beta release manual page
[unix-history] / usr / src / usr.bin / ftp / ftp.c
CommitLineData
e0b077fe 1#ifndef lint
71a655cd 2static char sccsid[] = "@(#)ftp.c 4.16 (Berkeley) %G%";
e0b077fe
SL
3#endif
4
5#include <sys/param.h>
6#include <sys/stat.h>
7#include <sys/ioctl.h>
8#include <sys/socket.h>
19371098 9#include <sys/time.h>
e0b077fe
SL
10
11#include <netinet/in.h>
62c4a390 12#include <arpa/ftp.h>
e0b077fe
SL
13
14#include <stdio.h>
15#include <signal.h>
e0b077fe
SL
16#include <errno.h>
17#include <netdb.h>
18
e0b077fe
SL
19#include "ftp_var.h"
20
21struct sockaddr_in hisctladdr;
22struct sockaddr_in data_addr;
23int data = -1;
24int connected;
25struct sockaddr_in myctladdr;
26
27FILE *cin, *cout;
28FILE *dataconn();
29
30struct hostent *
31hookup(host, port)
32 char *host;
33 int port;
34{
35 register struct hostent *hp;
8c1d51c7 36 int s, len;
e0b077fe
SL
37
38 bzero((char *)&hisctladdr, sizeof (hisctladdr));
39 hp = gethostbyname(host);
6e88ca41 40 if (hp == NULL) {
e0b077fe
SL
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 }
6e88ca41
SL
60 hostname = hp->h_name;
61 hisctladdr.sin_family = hp->h_addrtype;
71a655cd 62 s = socket(hp->h_addrtype, SOCK_STREAM, 0);
e0b077fe
SL
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 }
8c1d51c7
SL
77 len = sizeof (myctladdr);
78 if (getsockname(s, (char *)&myctladdr, &len) < 0) {
79 perror("ftp: getsockname");
e0b077fe
SL
80 goto bad;
81 }
82 cin = fdopen(s, "r");
83 cout = fdopen(s, "w");
d33c618b 84 if (cin == NULL || cout == NULL) {
e0b077fe
SL
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);
96bad:
97 close(s);
98 return ((struct hostent *)0);
99}
100
101login(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*/
127command(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 }
d33c618b
SL
137 if (cout == NULL) {
138 perror ("No control connection for command");
139 return (0);
140 }
e0b077fe
SL
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
149getreply(expecteof)
150 int expecteof;
151{
d33c618b 152 register int c, n;
e0b077fe
SL
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 }
7968a79f 176 if (verbose || n == '5') {
e0b077fe 177 putchar(c);
7968a79f
SL
178 (void) fflush (stdout);
179 }
e0b077fe
SL
180 if (continuation && code != originalcode) {
181 if (originalcode == 0)
182 originalcode = code;
183 continue;
184 }
d33c618b 185 if (expecteof || empty(cin))
e0b077fe
SL
186 return (n - '0');
187 }
188}
189
190empty(f)
191 FILE *f;
192{
5ac6fc46 193 long mask;
e0b077fe
SL
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
204jmp_buf sendabort;
205
206abortsend()
207{
208
209 longjmp(sendabort, 1);
210}
211
212sendrequest(cmd, local, remote)
213 char *cmd, *local, *remote;
214{
215 FILE *fin, *dout, *popen();
216 int (*closefunc)(), pclose(), fclose(), (*oldintr)();
d33c618b 217 char buf[BUFSIZ];
5ac6fc46 218 long bytes = 0, hashbytes = sizeof (buf);
7968a79f 219 register int c, d;
e0b077fe
SL
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) {
b4b22f0c 245 fprintf(stderr, "%s: not a plain file.\n", local);
e0b077fe
SL
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);
d33c618b
SL
261 switch (type) {
262
263 case TYPE_I:
264 case TYPE_L:
7968a79f 265 errno = d = 0;
d33c618b 266 while ((c = read(fileno (fin), buf, sizeof (buf))) > 0) {
7968a79f 267 if ((d = write(fileno (dout), buf, c)) < 0)
d33c618b
SL
268 break;
269 bytes += c;
5ac6fc46
SL
270 if (hash) {
271 putchar('#');
272 fflush(stdout);
273 }
274 }
614e24b6 275 if (hash && bytes > 0) {
5ac6fc46
SL
276 putchar('\n');
277 fflush(stdout);
d33c618b
SL
278 }
279 if (c < 0)
280 perror(local);
7968a79f 281 if (d < 0)
d33c618b
SL
282 perror("netout");
283 break;
284
285 case TYPE_A:
286 while ((c = getc(fin)) != EOF) {
287 if (c == '\n') {
5ac6fc46
SL
288 while (hash && (bytes >= hashbytes)) {
289 putchar('#');
290 fflush(stdout);
291 hashbytes += sizeof (buf);
292 }
d33c618b
SL
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 }
5ac6fc46 305 if (hash) {
614e24b6
SL
306 if (bytes < hashbytes)
307 putchar('#');
5ac6fc46
SL
308 putchar('\n');
309 fflush(stdout);
310 }
d33c618b
SL
311 if (ferror(fin))
312 perror(local);
7968a79f 313 if (ferror(dout))
d33c618b
SL
314 perror("netout");
315 break;
e0b077fe
SL
316 }
317 gettimeofday(&stop, (struct timezone *)0);
318 if (closefunc != NULL)
319 (*closefunc)(fin);
320 (void) fclose(dout);
e0b077fe
SL
321 (void) getreply(0);
322done:
323 signal(SIGINT, oldintr);
324 if (bytes > 0 && verbose)
325 ptransfer("sent", bytes, &start, &stop);
326 return;
327bad:
328 if (data >= 0)
329 (void) close(data), data = -1;
330 if (closefunc != NULL && fin != NULL)
331 (*closefunc)(fin);
332 goto done;
333}
334
335jmp_buf recvabort;
336
337abortrecv()
338{
339
340 longjmp(recvabort, 1);
341}
342
5ac6fc46
SL
343recvrequest(cmd, local, remote, mode)
344 char *cmd, *local, *remote, *mode;
e0b077fe
SL
345{
346 FILE *fout, *din, *popen();
7968a79f 347 int (*closefunc)(), pclose(), fclose(), (*oldintr)();
5ac6fc46
SL
348 char buf[BUFSIZ];
349 long bytes = 0, hashbytes = sizeof (buf);
7968a79f 350 register int c, d;
e0b077fe
SL
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;
f0773a1d
RC
363 d = access(dir ? local : ".", 2);
364 if (dir != NULL)
365 *dir = '/';
366 if (d < 0) {
e0b077fe
SL
367 perror(local);
368 goto bad;
369 }
e0b077fe
SL
370 }
371 if (initconn())
372 goto bad;
373 if (remote) {
374 if (command("%s %s", cmd, remote) != PRELIM)
375 goto bad;
376 } else
377 if (command("%s", cmd) != PRELIM)
378 goto bad;
379 if (strcmp(local, "-") == 0)
380 fout = stdout;
381 else if (*local == '|') {
382 fout = popen(local + 1, "w");
383 closefunc = pclose;
384 } else {
5ac6fc46 385 fout = fopen(local, mode);
e0b077fe
SL
386 closefunc = fclose;
387 }
388 if (fout == NULL) {
389 perror(local + 1);
390 goto bad;
391 }
392 din = dataconn("r");
393 if (din == NULL)
394 goto bad;
395 gettimeofday(&start, (struct timezone *)0);
d33c618b
SL
396 switch (type) {
397
398 case TYPE_I:
399 case TYPE_L:
7968a79f 400 errno = d = 0;
d33c618b 401 while ((c = read(fileno(din), buf, sizeof (buf))) > 0) {
7968a79f 402 if ((d = write(fileno(fout), buf, c)) < 0)
d33c618b
SL
403 break;
404 bytes += c;
5ac6fc46
SL
405 if (hash) {
406 putchar('#');
407 fflush(stdout);
408 }
409 }
614e24b6 410 if (hash && bytes > 0) {
5ac6fc46
SL
411 putchar('\n');
412 fflush(stdout);
d33c618b
SL
413 }
414 if (c < 0)
415 perror("netin");
7968a79f 416 if (d < 0)
e0b077fe 417 perror(local);
d33c618b
SL
418 break;
419
420 case TYPE_A:
421 while ((c = getc(din)) != EOF) {
422 if (c == '\r') {
5ac6fc46
SL
423 while (hash && (bytes >= hashbytes)) {
424 putchar('#');
425 fflush(stdout);
426 hashbytes += sizeof (buf);
427 }
e0b077fe 428 bytes++;
d33c618b
SL
429 if ((c = getc(din)) != '\n') {
430 if (ferror (fout))
431 break;
432 putc ('\r', fout);
433 }
434 if (c == '\0') {
435 bytes++;
436 continue;
437 }
438 }
439 putc (c, fout);
440 bytes++;
e0b077fe 441 }
5ac6fc46 442 if (hash) {
614e24b6
SL
443 if (bytes < hashbytes)
444 putchar('#');
5ac6fc46
SL
445 putchar('\n');
446 fflush(stdout);
447 }
d33c618b
SL
448 if (ferror (din))
449 perror ("netin");
450 if (ferror (fout))
451 perror (local);
452 break;
e0b077fe
SL
453 }
454 gettimeofday(&stop, (struct timezone *)0);
455 (void) fclose(din);
456 if (closefunc != NULL)
457 (*closefunc)(fout);
458 (void) getreply(0);
459done:
460 signal(SIGINT, oldintr);
461 if (bytes > 0 && verbose)
462 ptransfer("received", bytes, &start, &stop);
463 return;
464bad:
465 if (data >= 0)
466 (void) close(data), data = -1;
467 if (closefunc != NULL && fout != NULL)
468 (*closefunc)(fout);
469 goto done;
470}
471
472/*
473 * Need to start a listen on the data channel
474 * before we send the command, otherwise the
475 * server's connect may fail.
476 */
5ac6fc46
SL
477static int sendport = -1;
478
e0b077fe
SL
479initconn()
480{
481 register char *p, *a;
8c1d51c7 482 int result, len;
0975b26d 483 int on = 1;
e0b077fe 484
5ac6fc46 485noport:
e0b077fe 486 data_addr = myctladdr;
5ac6fc46
SL
487 if (sendport)
488 data_addr.sin_port = 0; /* let system pick one */
489 if (data != -1)
490 (void) close (data);
71a655cd 491 data = socket(AF_INET, SOCK_STREAM, 0);
e0b077fe
SL
492 if (data < 0) {
493 perror("ftp: socket");
494 return (1);
495 }
62c4a390 496 if (!sendport)
0975b26d 497 if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) {
62c4a390
SL
498 perror("ftp: setsockopt (resuse address)");
499 goto bad;
500 }
e0b077fe
SL
501 if (bind(data, (char *)&data_addr, sizeof (data_addr), 0) < 0) {
502 perror("ftp: bind");
503 goto bad;
504 }
e0b077fe 505 if (options & SO_DEBUG &&
0975b26d 506 setsockopt(data, SOL_SOCKET, SO_DEBUG, &on, sizeof (on)) < 0)
e0b077fe 507 perror("ftp: setsockopt (ignored)");
8c1d51c7
SL
508 len = sizeof (data_addr);
509 if (getsockname(data, (char *)&data_addr, &len) < 0) {
510 perror("ftp: getsockname");
e0b077fe
SL
511 goto bad;
512 }
513 if (listen(data, 1) < 0) {
514 perror("ftp: listen");
515 goto bad;
516 }
5ac6fc46
SL
517 if (sendport) {
518 a = (char *)&data_addr.sin_addr;
519 p = (char *)&data_addr.sin_port;
e0b077fe 520#define UC(b) (((int)b)&0xff)
5ac6fc46
SL
521 result =
522 command("PORT %d,%d,%d,%d,%d,%d",
523 UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
524 UC(p[0]), UC(p[1]));
525 if (result == ERROR && sendport == -1) {
526 sendport = 0;
527 goto noport;
528 }
529 return (result != COMPLETE);
530 }
531 return (0);
e0b077fe
SL
532bad:
533 (void) close(data), data = -1;
534 return (1);
535}
536
537FILE *
538dataconn(mode)
539 char *mode;
540{
541 struct sockaddr_in from;
542 int s, fromlen = sizeof (from);
543
544 s = accept(data, &from, &fromlen, 0);
545 if (s < 0) {
546 perror("ftp: accept");
547 (void) close(data), data = -1;
548 return (NULL);
549 }
550 (void) close(data);
551 data = s;
552 return (fdopen(data, mode));
553}
554
555ptransfer(direction, bytes, t0, t1)
556 char *direction;
5ac6fc46 557 long bytes;
e0b077fe
SL
558 struct timeval *t0, *t1;
559{
560 struct timeval td;
6ee561e3 561 float s, bs;
e0b077fe
SL
562
563 tvsub(&td, t1, t0);
6ee561e3 564 s = td.tv_sec + (td.tv_usec / 1000000.);
e0b077fe 565#define nz(x) ((x) == 0 ? 1 : (x))
6ee561e3
CL
566 bs = bytes / nz(s);
567 printf("%ld bytes %s in %.2g seconds (%.2g Kbytes/s)\n",
568 bytes, direction, s, bs / 1024.);
e0b077fe
SL
569}
570
571tvadd(tsum, t0)
572 struct timeval *tsum, *t0;
573{
574
575 tsum->tv_sec += t0->tv_sec;
576 tsum->tv_usec += t0->tv_usec;
577 if (tsum->tv_usec > 1000000)
578 tsum->tv_sec++, tsum->tv_usec -= 1000000;
579}
580
581tvsub(tdiff, t1, t0)
582 struct timeval *tdiff, *t1, *t0;
583{
584
585 tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
586 tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
587 if (tdiff->tv_usec < 0)
588 tdiff->tv_sec--, tdiff->tv_usec += 1000000;
589}