formatting
[unix-history] / usr / src / usr.bin / ftp / ftp.c
CommitLineData
e0b077fe 1#ifndef lint
7968a79f 2static char sccsid[] = "@(#)ftp.c 4.5 (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>
9
10#include <netinet/in.h>
11
12#include <stdio.h>
13#include <signal.h>
14#include <time.h>
15#include <errno.h>
16#include <netdb.h>
17
18#include "ftp.h"
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;
36 int s;
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 }
77 if (socketaddr(s, &myctladdr) < 0) {
78 perror("ftp: socketaddr");
79 goto bad;
80 }
81 cin = fdopen(s, "r");
82 cout = fdopen(s, "w");
d33c618b 83 if (cin == NULL || cout == NULL) {
e0b077fe
SL
84 fprintf(stderr, "ftp: fdopen failed.\n");
85 if (cin)
86 fclose(cin);
87 if (cout)
88 fclose(cout);
89 goto bad;
90 }
91 if (verbose)
92 printf("Connected to %s.\n", hp->h_name);
93 (void) getreply(0); /* read startup message from server */
94 return (hp);
95bad:
96 close(s);
97 return ((struct hostent *)0);
98}
99
100login(hp)
101 struct hostent *hp;
102{
103 char acct[80];
104 char *user, *pass;
105 int n;
106
107 user = pass = 0;
108 ruserpass(hp->h_name, &user, &pass);
109 n = command("USER %s", user);
110 if (n == CONTINUE)
111 n = command("PASS %s", pass);
112 if (n == CONTINUE) {
113 printf("Account: "); (void) fflush(stdout);
114 (void) fgets(acct, sizeof(acct) - 1, stdin);
115 acct[strlen(acct) - 1] = '\0';
116 n = command("ACCT %s", acct);
117 }
118 if (n != COMPLETE) {
119 fprintf(stderr, "Login failed.\n");
120 return (0);
121 }
122 return (1);
123}
124
125/*VARARGS 1*/
126command(fmt, args)
127 char *fmt;
128{
129
130 if (debug) {
131 printf("---> ");
132 _doprnt(fmt, &args, stdout);
133 printf("\n");
134 (void) fflush(stdout);
135 }
d33c618b
SL
136 if (cout == NULL) {
137 perror ("No control connection for command");
138 return (0);
139 }
e0b077fe
SL
140 _doprnt(fmt, &args, cout);
141 fprintf(cout, "\r\n");
142 (void) fflush(cout);
143 return (getreply(!strcmp(fmt, "QUIT")));
144}
145
146#include <ctype.h>
147
148getreply(expecteof)
149 int expecteof;
150{
d33c618b 151 register int c, n;
e0b077fe
SL
152 register int code, dig;
153 int originalcode = 0, continuation = 0;
154
155 for (;;) {
156 dig = n = code = 0;
157 while ((c = getc(cin)) != '\n') {
158 dig++;
159 if (c == EOF) {
160 if (expecteof)
161 return (0);
162 lostpeer();
163 exit(1);
164 }
165 if (verbose && c != '\r' ||
166 (n == '5' && dig > 4))
167 putchar(c);
168 if (dig < 4 && isdigit(c))
169 code = code * 10 + (c - '0');
170 if (dig == 4 && c == '-')
171 continuation++;
172 if (n == 0)
173 n = c;
174 }
7968a79f 175 if (verbose || n == '5') {
e0b077fe 176 putchar(c);
7968a79f
SL
177 (void) fflush (stdout);
178 }
e0b077fe
SL
179 if (continuation && code != originalcode) {
180 if (originalcode == 0)
181 originalcode = code;
182 continue;
183 }
d33c618b 184 if (expecteof || empty(cin))
e0b077fe
SL
185 return (n - '0');
186 }
187}
188
189empty(f)
190 FILE *f;
191{
192 int mask;
193 struct timeval t;
194
195 if (f->_cnt > 0)
196 return (0);
197 mask = (1 << fileno(f));
198 t.tv_sec = t.tv_usec = 0;
199 (void) select(20, &mask, 0, 0, &t);
200 return (mask == 0);
201}
202
203jmp_buf sendabort;
204
205abortsend()
206{
207
208 longjmp(sendabort, 1);
209}
210
211sendrequest(cmd, local, remote)
212 char *cmd, *local, *remote;
213{
214 FILE *fin, *dout, *popen();
215 int (*closefunc)(), pclose(), fclose(), (*oldintr)();
d33c618b 216 char buf[BUFSIZ];
7968a79f
SL
217 register int bytes = 0;
218 register int c, d;
e0b077fe
SL
219 struct stat st;
220 struct timeval start, stop;
221
222 closefunc = NULL;
223 if (setjmp(sendabort))
224 goto bad;
225 oldintr = signal(SIGINT, abortsend);
226 if (strcmp(local, "-") == 0)
227 fin = stdin;
228 else if (*local == '|') {
229 fin = popen(local + 1, "r");
230 if (fin == NULL) {
231 perror(local + 1);
232 goto bad;
233 }
234 closefunc = pclose;
235 } else {
236 fin = fopen(local, "r");
237 if (fin == NULL) {
238 perror(local);
239 goto bad;
240 }
241 closefunc = fclose;
242 if (fstat(fileno(fin), &st) < 0 ||
243 (st.st_mode&S_IFMT) != S_IFREG) {
244 fprintf(stderr, "%s: not a plain file.", local);
245 goto bad;
246 }
247 }
248 if (initconn())
249 goto bad;
250 if (remote) {
251 if (command("%s %s", cmd, remote) != PRELIM)
252 goto bad;
253 } else
254 if (command("%s", cmd) != PRELIM)
255 goto bad;
256 dout = dataconn("w");
257 if (dout == NULL)
258 goto bad;
259 gettimeofday(&start, (struct timezone *)0);
d33c618b
SL
260 switch (type) {
261
262 case TYPE_I:
263 case TYPE_L:
7968a79f 264 errno = d = 0;
d33c618b 265 while ((c = read(fileno (fin), buf, sizeof (buf))) > 0) {
7968a79f 266 if ((d = write(fileno (dout), buf, c)) < 0)
d33c618b
SL
267 break;
268 bytes += c;
269 }
270 if (c < 0)
271 perror(local);
7968a79f 272 if (d < 0)
d33c618b
SL
273 perror("netout");
274 break;
275
276 case TYPE_A:
277 while ((c = getc(fin)) != EOF) {
278 if (c == '\n') {
279 if (ferror(dout))
280 break;
281 putc('\r', dout);
282 bytes++;
283 }
284 putc(c, dout);
285 bytes++;
286 if (c == '\r') {
287 putc('\0', dout);
288 bytes++;
289 }
290 }
291 if (ferror(fin))
292 perror(local);
7968a79f 293 if (ferror(dout))
d33c618b
SL
294 perror("netout");
295 break;
e0b077fe
SL
296 }
297 gettimeofday(&stop, (struct timezone *)0);
298 if (closefunc != NULL)
299 (*closefunc)(fin);
300 (void) fclose(dout);
e0b077fe
SL
301 (void) getreply(0);
302done:
303 signal(SIGINT, oldintr);
304 if (bytes > 0 && verbose)
305 ptransfer("sent", bytes, &start, &stop);
306 return;
307bad:
308 if (data >= 0)
309 (void) close(data), data = -1;
310 if (closefunc != NULL && fin != NULL)
311 (*closefunc)(fin);
312 goto done;
313}
314
315jmp_buf recvabort;
316
317abortrecv()
318{
319
320 longjmp(recvabort, 1);
321}
322
323recvrequest(cmd, local, remote)
324 char *cmd, *local, *remote;
325{
326 FILE *fout, *din, *popen();
d33c618b 327 char buf[BUFSIZ];
7968a79f 328 int (*closefunc)(), pclose(), fclose(), (*oldintr)();
e0b077fe 329 register int bytes = 0;
7968a79f 330 register int c, d;
e0b077fe
SL
331 struct timeval start, stop;
332
333 closefunc = NULL;
334 if (setjmp(recvabort))
335 goto bad;
336 oldintr = signal(SIGINT, abortrecv);
337 if (strcmp(local, "-") && *local != '|')
338 if (access(local, 2) < 0) {
339 char *dir = rindex(local, '/');
340
341 if (dir != NULL)
342 *dir = 0;
343 if (access(dir ? dir : ".", 2) < 0) {
344 perror(local);
345 goto bad;
346 }
347 if (dir != NULL)
348 *dir = '/';
349 }
350 if (initconn())
351 goto bad;
352 if (remote) {
353 if (command("%s %s", cmd, remote) != PRELIM)
354 goto bad;
355 } else
356 if (command("%s", cmd) != PRELIM)
357 goto bad;
358 if (strcmp(local, "-") == 0)
359 fout = stdout;
360 else if (*local == '|') {
361 fout = popen(local + 1, "w");
362 closefunc = pclose;
363 } else {
364 fout = fopen(local, "w");
365 closefunc = fclose;
366 }
367 if (fout == NULL) {
368 perror(local + 1);
369 goto bad;
370 }
371 din = dataconn("r");
372 if (din == NULL)
373 goto bad;
374 gettimeofday(&start, (struct timezone *)0);
d33c618b
SL
375 switch (type) {
376
377 case TYPE_I:
378 case TYPE_L:
7968a79f 379 errno = d = 0;
d33c618b 380 while ((c = read(fileno(din), buf, sizeof (buf))) > 0) {
7968a79f 381 if ((d = write(fileno(fout), buf, c)) < 0)
d33c618b
SL
382 break;
383 bytes += c;
384 }
385 if (c < 0)
386 perror("netin");
7968a79f 387 if (d < 0)
e0b077fe 388 perror(local);
d33c618b
SL
389 break;
390
391 case TYPE_A:
392 while ((c = getc(din)) != EOF) {
393 if (c == '\r') {
e0b077fe 394 bytes++;
d33c618b
SL
395 if ((c = getc(din)) != '\n') {
396 if (ferror (fout))
397 break;
398 putc ('\r', fout);
399 }
400 if (c == '\0') {
401 bytes++;
402 continue;
403 }
404 }
405 putc (c, fout);
406 bytes++;
e0b077fe 407 }
d33c618b
SL
408 if (ferror (din))
409 perror ("netin");
410 if (ferror (fout))
411 perror (local);
412 break;
e0b077fe
SL
413 }
414 gettimeofday(&stop, (struct timezone *)0);
415 (void) fclose(din);
416 if (closefunc != NULL)
417 (*closefunc)(fout);
418 (void) getreply(0);
419done:
420 signal(SIGINT, oldintr);
421 if (bytes > 0 && verbose)
422 ptransfer("received", bytes, &start, &stop);
423 return;
424bad:
425 if (data >= 0)
426 (void) close(data), data = -1;
427 if (closefunc != NULL && fout != NULL)
428 (*closefunc)(fout);
429 goto done;
430}
431
432/*
433 * Need to start a listen on the data channel
434 * before we send the command, otherwise the
435 * server's connect may fail.
436 */
437initconn()
438{
439 register char *p, *a;
440 int result;
441
442 data_addr = myctladdr;
443 data_addr.sin_port = 0; /* let system pick one */
444 data = socket(AF_INET, SOCK_STREAM, 0, 0);
445 if (data < 0) {
446 perror("ftp: socket");
447 return (1);
448 }
449 if (bind(data, (char *)&data_addr, sizeof (data_addr), 0) < 0) {
450 perror("ftp: bind");
451 goto bad;
452 }
e0b077fe
SL
453 if (options & SO_DEBUG &&
454 setsockopt(data, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
455 perror("ftp: setsockopt (ignored)");
e0b077fe
SL
456 if (socketaddr(data, &data_addr) < 0) {
457 perror("ftp: socketaddr");
458 goto bad;
459 }
460 if (listen(data, 1) < 0) {
461 perror("ftp: listen");
462 goto bad;
463 }
464 a = (char *)&data_addr.sin_addr;
465 p = (char *)&data_addr.sin_port;
466#define UC(b) (((int)b)&0xff)
467 result =
468 command("PORT %d,%d,%d,%d,%d,%d",
469 UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
470 UC(p[0]), UC(p[1]));
471 return (result != COMPLETE);
472bad:
473 (void) close(data), data = -1;
474 return (1);
475}
476
477FILE *
478dataconn(mode)
479 char *mode;
480{
481 struct sockaddr_in from;
482 int s, fromlen = sizeof (from);
483
484 s = accept(data, &from, &fromlen, 0);
485 if (s < 0) {
486 perror("ftp: accept");
487 (void) close(data), data = -1;
488 return (NULL);
489 }
490 (void) close(data);
491 data = s;
492 return (fdopen(data, mode));
493}
494
495ptransfer(direction, bytes, t0, t1)
496 char *direction;
497 int bytes;
498 struct timeval *t0, *t1;
499{
500 struct timeval td;
501 int ms, bs;
502
503 tvsub(&td, t1, t0);
504 ms = (td.tv_sec * 1000) + (td.tv_usec / 1000);
505#define nz(x) ((x) == 0 ? 1 : (x))
506 bs = ((bytes * NBBY * 1000) / nz(ms)) / NBBY;
507 printf("%d bytes %s in %d.%02d seconds (%d.%01d Kbytes/s)\n",
508 bytes, direction, td.tv_sec, td.tv_usec / 10000,
509 bs / 1024, (((bs % 1024) * 10) + 1023) / 1024);
510}
511
512tvadd(tsum, t0)
513 struct timeval *tsum, *t0;
514{
515
516 tsum->tv_sec += t0->tv_sec;
517 tsum->tv_usec += t0->tv_usec;
518 if (tsum->tv_usec > 1000000)
519 tsum->tv_sec++, tsum->tv_usec -= 1000000;
520}
521
522tvsub(tdiff, t1, t0)
523 struct timeval *tdiff, *t1, *t0;
524{
525
526 tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
527 tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
528 if (tdiff->tv_usec < 0)
529 tdiff->tv_sec--, tdiff->tv_usec += 1000000;
530}