help updated for 'buffer' and new argument to 'window'
[unix-history] / usr / src / usr.bin / ftp / ftp.c
CommitLineData
e0b077fe 1#ifndef lint
7136ef0a 2static char sccsid[] = "@(#)ftp.c 4.11 (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;
e0b077fe
SL
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 }
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) {
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);
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;
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 {
5ac6fc46 384 fout = fopen(local, mode);
e0b077fe
SL
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);
d33c618b
SL
395 switch (type) {
396
397 case TYPE_I:
398 case TYPE_L:
7968a79f 399 errno = d = 0;
d33c618b 400 while ((c = read(fileno(din), buf, sizeof (buf))) > 0) {
7968a79f 401 if ((d = write(fileno(fout), buf, c)) < 0)
d33c618b
SL
402 break;
403 bytes += c;
5ac6fc46
SL
404 if (hash) {
405 putchar('#');
406 fflush(stdout);
407 }
408 }
614e24b6 409 if (hash && bytes > 0) {
5ac6fc46
SL
410 putchar('\n');
411 fflush(stdout);
d33c618b
SL
412 }
413 if (c < 0)
414 perror("netin");
7968a79f 415 if (d < 0)
e0b077fe 416 perror(local);
d33c618b
SL
417 break;
418
419 case TYPE_A:
420 while ((c = getc(din)) != EOF) {
421 if (c == '\r') {
5ac6fc46
SL
422 while (hash && (bytes >= hashbytes)) {
423 putchar('#');
424 fflush(stdout);
425 hashbytes += sizeof (buf);
426 }
e0b077fe 427 bytes++;
d33c618b
SL
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++;
e0b077fe 440 }
5ac6fc46 441 if (hash) {
614e24b6
SL
442 if (bytes < hashbytes)
443 putchar('#');
5ac6fc46
SL
444 putchar('\n');
445 fflush(stdout);
446 }
d33c618b
SL
447 if (ferror (din))
448 perror ("netin");
449 if (ferror (fout))
450 perror (local);
451 break;
e0b077fe
SL
452 }
453 gettimeofday(&stop, (struct timezone *)0);
454 (void) fclose(din);
455 if (closefunc != NULL)
456 (*closefunc)(fout);
457 (void) getreply(0);
458done:
459 signal(SIGINT, oldintr);
460 if (bytes > 0 && verbose)
461 ptransfer("received", bytes, &start, &stop);
462 return;
463bad:
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 */
5ac6fc46
SL
476static int sendport = -1;
477
e0b077fe
SL
478initconn()
479{
480 register char *p, *a;
8c1d51c7 481 int result, len;
e0b077fe 482
5ac6fc46 483noport:
e0b077fe 484 data_addr = myctladdr;
5ac6fc46
SL
485 if (sendport)
486 data_addr.sin_port = 0; /* let system pick one */
487 if (data != -1)
488 (void) close (data);
e0b077fe
SL
489 data = socket(AF_INET, SOCK_STREAM, 0, 0);
490 if (data < 0) {
491 perror("ftp: socket");
492 return (1);
493 }
62c4a390
SL
494 if (!sendport)
495 if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0) {
496 perror("ftp: setsockopt (resuse address)");
497 goto bad;
498 }
e0b077fe
SL
499 if (bind(data, (char *)&data_addr, sizeof (data_addr), 0) < 0) {
500 perror("ftp: bind");
501 goto bad;
502 }
e0b077fe
SL
503 if (options & SO_DEBUG &&
504 setsockopt(data, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
505 perror("ftp: setsockopt (ignored)");
8c1d51c7
SL
506 len = sizeof (data_addr);
507 if (getsockname(data, (char *)&data_addr, &len) < 0) {
508 perror("ftp: getsockname");
e0b077fe
SL
509 goto bad;
510 }
511 if (listen(data, 1) < 0) {
512 perror("ftp: listen");
513 goto bad;
514 }
5ac6fc46
SL
515 if (sendport) {
516 a = (char *)&data_addr.sin_addr;
517 p = (char *)&data_addr.sin_port;
e0b077fe 518#define UC(b) (((int)b)&0xff)
5ac6fc46
SL
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);
e0b077fe
SL
530bad:
531 (void) close(data), data = -1;
532 return (1);
533}
534
535FILE *
536dataconn(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 return (fdopen(data, mode));
551}
552
553ptransfer(direction, bytes, t0, t1)
554 char *direction;
5ac6fc46 555 long bytes;
e0b077fe
SL
556 struct timeval *t0, *t1;
557{
558 struct timeval td;
5ac6fc46
SL
559 long ms;
560 float bs;
e0b077fe
SL
561
562 tvsub(&td, t1, t0);
563 ms = (td.tv_sec * 1000) + (td.tv_usec / 1000);
564#define nz(x) ((x) == 0 ? 1 : (x))
5ac6fc46
SL
565 bs = ((bytes * NBBY * 1000) / (float) nz(ms)) / NBBY;
566 printf("%ld bytes %s in %d.%02d seconds (%.2g Kbytes/s)\n",
567 bytes, direction, td.tv_sec, td.tv_usec / 10000, bs / 1024.);
e0b077fe
SL
568}
569
570tvadd(tsum, t0)
571 struct timeval *tsum, *t0;
572{
573
574 tsum->tv_sec += t0->tv_sec;
575 tsum->tv_usec += t0->tv_usec;
576 if (tsum->tv_usec > 1000000)
577 tsum->tv_sec++, tsum->tv_usec -= 1000000;
578}
579
580tvsub(tdiff, t1, t0)
581 struct timeval *tdiff, *t1, *t0;
582{
583
584 tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
585 tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
586 if (tdiff->tv_usec < 0)
587 tdiff->tv_sec--, tdiff->tv_usec += 1000000;
588}