121739Sdist /* 221739Sdist * Copyright (c) 1980 Regents of the University of California. 321739Sdist * All rights reserved. The Berkeley software License Agreement 421739Sdist * specifies the terms and conditions for redistribution. 521739Sdist */ 621739Sdist 710296Ssam #ifndef lint 8*25904Skarels static char sccsid[] = "@(#)ftp.c 5.4 (Berkeley) 01/13/86"; 921739Sdist #endif not lint 1010296Ssam 1110296Ssam #include <sys/param.h> 1210296Ssam #include <sys/stat.h> 1310296Ssam #include <sys/ioctl.h> 1410296Ssam #include <sys/socket.h> 1513614Ssam #include <sys/time.h> 1610296Ssam 1710296Ssam #include <netinet/in.h> 1812397Ssam #include <arpa/ftp.h> 1910296Ssam 2010296Ssam #include <stdio.h> 2110296Ssam #include <signal.h> 2210296Ssam #include <errno.h> 2310296Ssam #include <netdb.h> 2410296Ssam 2510296Ssam #include "ftp_var.h" 2610296Ssam 2710296Ssam struct sockaddr_in hisctladdr; 2810296Ssam struct sockaddr_in data_addr; 2910296Ssam int data = -1; 3010296Ssam int connected; 3110296Ssam struct sockaddr_in myctladdr; 3210296Ssam 3310296Ssam FILE *cin, *cout; 3410296Ssam FILE *dataconn(); 3510296Ssam 36*25904Skarels char * 3710296Ssam hookup(host, port) 3810296Ssam char *host; 3910296Ssam int port; 4010296Ssam { 41*25904Skarels register struct hostent *hp = 0; 42*25904Skarels static char hostnamebuf[80]; 4311627Ssam int s, len; 4410296Ssam 4510296Ssam bzero((char *)&hisctladdr, sizeof (hisctladdr)); 46*25904Skarels hisctladdr.sin_addr.s_addr = inet_addr(host); 47*25904Skarels if (hisctladdr.sin_addr.s_addr != -1) { 48*25904Skarels hisctladdr.sin_family = AF_INET; 49*25904Skarels (void) strcpy(hostnamebuf, host); 50*25904Skarels } else { 5125100Sbloom hp = gethostbyname(host); 52*25904Skarels if (hp == NULL) { 53*25904Skarels printf("%s: unknown host\n", host); 54*25904Skarels return (0); 55*25904Skarels } 56*25904Skarels hisctladdr.sin_family = hp->h_addrtype; 57*25904Skarels bcopy(hp->h_addr_list[0], 58*25904Skarels (caddr_t)&hisctladdr.sin_addr, hp->h_length); 59*25904Skarels (void) strcpy(hostnamebuf, hp->h_name); 6010296Ssam } 61*25904Skarels hostname = hostnamebuf; 62*25904Skarels s = socket(hisctladdr.sin_family, SOCK_STREAM, 0); 6310296Ssam if (s < 0) { 6410296Ssam perror("ftp: socket"); 6510296Ssam return (0); 6610296Ssam } 6710296Ssam hisctladdr.sin_port = port; 68*25904Skarels while (connect(s, (caddr_t)&hisctladdr, sizeof (hisctladdr)) < 0) { 69*25904Skarels if (hp && hp->h_addr_list[1]) { 70*25904Skarels int oerrno = errno; 71*25904Skarels 72*25904Skarels fprintf(stderr, "ftp: connect to address %s: ", 73*25904Skarels inet_ntoa(hisctladdr.sin_addr)); 74*25904Skarels errno = oerrno; 75*25904Skarels perror(0); 76*25904Skarels hp->h_addr_list++; 77*25904Skarels bcopy(hp->h_addr_list[0], 78*25904Skarels (caddr_t)&hisctladdr.sin_addr, hp->h_length); 79*25904Skarels fprintf(stderr, "Trying %s...\n", 80*25904Skarels inet_ntoa(hisctladdr.sin_addr)); 81*25904Skarels continue; 82*25904Skarels } 8310296Ssam perror("ftp: connect"); 8410296Ssam goto bad; 8510296Ssam } 8611627Ssam len = sizeof (myctladdr); 8711627Ssam if (getsockname(s, (char *)&myctladdr, &len) < 0) { 8811627Ssam perror("ftp: getsockname"); 8910296Ssam goto bad; 9010296Ssam } 9110296Ssam cin = fdopen(s, "r"); 9210296Ssam cout = fdopen(s, "w"); 9311219Ssam if (cin == NULL || cout == NULL) { 9410296Ssam fprintf(stderr, "ftp: fdopen failed.\n"); 9510296Ssam if (cin) 9610296Ssam fclose(cin); 9710296Ssam if (cout) 9810296Ssam fclose(cout); 9910296Ssam goto bad; 10010296Ssam } 10110296Ssam if (verbose) 102*25904Skarels printf("Connected to %s.\n", hostname); 10310296Ssam (void) getreply(0); /* read startup message from server */ 104*25904Skarels return (hostname); 10510296Ssam bad: 10610296Ssam close(s); 107*25904Skarels return ((char *)0); 10810296Ssam } 10910296Ssam 110*25904Skarels login(host) 111*25904Skarels char *host; 11210296Ssam { 11310296Ssam char acct[80]; 11410296Ssam char *user, *pass; 11510296Ssam int n; 11610296Ssam 11710296Ssam user = pass = 0; 118*25904Skarels ruserpass(host, &user, &pass); 11910296Ssam n = command("USER %s", user); 12010296Ssam if (n == CONTINUE) 12110296Ssam n = command("PASS %s", pass); 12210296Ssam if (n == CONTINUE) { 12310296Ssam printf("Account: "); (void) fflush(stdout); 12410296Ssam (void) fgets(acct, sizeof(acct) - 1, stdin); 12510296Ssam acct[strlen(acct) - 1] = '\0'; 12610296Ssam n = command("ACCT %s", acct); 12710296Ssam } 12810296Ssam if (n != COMPLETE) { 12910296Ssam fprintf(stderr, "Login failed.\n"); 13010296Ssam return (0); 13110296Ssam } 13210296Ssam return (1); 13310296Ssam } 13410296Ssam 13510296Ssam /*VARARGS 1*/ 13610296Ssam command(fmt, args) 13710296Ssam char *fmt; 13810296Ssam { 13910296Ssam 14010296Ssam if (debug) { 14110296Ssam printf("---> "); 14210296Ssam _doprnt(fmt, &args, stdout); 14310296Ssam printf("\n"); 14410296Ssam (void) fflush(stdout); 14510296Ssam } 14611219Ssam if (cout == NULL) { 14711219Ssam perror ("No control connection for command"); 14811219Ssam return (0); 14911219Ssam } 15010296Ssam _doprnt(fmt, &args, cout); 15110296Ssam fprintf(cout, "\r\n"); 15210296Ssam (void) fflush(cout); 15310296Ssam return (getreply(!strcmp(fmt, "QUIT"))); 15410296Ssam } 15510296Ssam 15610296Ssam #include <ctype.h> 15710296Ssam 15810296Ssam getreply(expecteof) 15910296Ssam int expecteof; 16010296Ssam { 16111219Ssam register int c, n; 16210296Ssam register int code, dig; 16310296Ssam int originalcode = 0, continuation = 0; 16410296Ssam 16510296Ssam for (;;) { 16610296Ssam dig = n = code = 0; 16710296Ssam while ((c = getc(cin)) != '\n') { 16810296Ssam dig++; 16910296Ssam if (c == EOF) { 17010296Ssam if (expecteof) 17110296Ssam return (0); 17210296Ssam lostpeer(); 17310296Ssam exit(1); 17410296Ssam } 17510296Ssam if (verbose && c != '\r' || 17610296Ssam (n == '5' && dig > 4)) 17710296Ssam putchar(c); 17810296Ssam if (dig < 4 && isdigit(c)) 17910296Ssam code = code * 10 + (c - '0'); 18010296Ssam if (dig == 4 && c == '-') 18110296Ssam continuation++; 18210296Ssam if (n == 0) 18310296Ssam n = c; 18410296Ssam } 18511346Ssam if (verbose || n == '5') { 18610296Ssam putchar(c); 18711346Ssam (void) fflush (stdout); 18811346Ssam } 18910296Ssam if (continuation && code != originalcode) { 19010296Ssam if (originalcode == 0) 19110296Ssam originalcode = code; 19210296Ssam continue; 19310296Ssam } 19411219Ssam if (expecteof || empty(cin)) 19510296Ssam return (n - '0'); 19610296Ssam } 19710296Ssam } 19810296Ssam 19910296Ssam empty(f) 20010296Ssam FILE *f; 20110296Ssam { 20211651Ssam long mask; 20310296Ssam struct timeval t; 20410296Ssam 20510296Ssam if (f->_cnt > 0) 20610296Ssam return (0); 20710296Ssam mask = (1 << fileno(f)); 20810296Ssam t.tv_sec = t.tv_usec = 0; 20910296Ssam (void) select(20, &mask, 0, 0, &t); 21010296Ssam return (mask == 0); 21110296Ssam } 21210296Ssam 21310296Ssam jmp_buf sendabort; 21410296Ssam 21510296Ssam abortsend() 21610296Ssam { 21710296Ssam 21810296Ssam longjmp(sendabort, 1); 21910296Ssam } 22010296Ssam 22110296Ssam sendrequest(cmd, local, remote) 22210296Ssam char *cmd, *local, *remote; 22310296Ssam { 22410296Ssam FILE *fin, *dout, *popen(); 22510296Ssam int (*closefunc)(), pclose(), fclose(), (*oldintr)(); 22611219Ssam char buf[BUFSIZ]; 22711651Ssam long bytes = 0, hashbytes = sizeof (buf); 22811346Ssam register int c, d; 22910296Ssam struct stat st; 23010296Ssam struct timeval start, stop; 23110296Ssam 23210296Ssam closefunc = NULL; 23310296Ssam if (setjmp(sendabort)) 23410296Ssam goto bad; 23510296Ssam oldintr = signal(SIGINT, abortsend); 23610296Ssam if (strcmp(local, "-") == 0) 23710296Ssam fin = stdin; 23810296Ssam else if (*local == '|') { 23910296Ssam fin = popen(local + 1, "r"); 24010296Ssam if (fin == NULL) { 24110296Ssam perror(local + 1); 24210296Ssam goto bad; 24310296Ssam } 24410296Ssam closefunc = pclose; 24510296Ssam } else { 24610296Ssam fin = fopen(local, "r"); 24710296Ssam if (fin == NULL) { 24810296Ssam perror(local); 24910296Ssam goto bad; 25010296Ssam } 25110296Ssam closefunc = fclose; 25210296Ssam if (fstat(fileno(fin), &st) < 0 || 25310296Ssam (st.st_mode&S_IFMT) != S_IFREG) { 25417949Sralph fprintf(stderr, "%s: not a plain file.\n", local); 25510296Ssam goto bad; 25610296Ssam } 25710296Ssam } 25810296Ssam if (initconn()) 25910296Ssam goto bad; 26010296Ssam if (remote) { 26110296Ssam if (command("%s %s", cmd, remote) != PRELIM) 26210296Ssam goto bad; 26310296Ssam } else 26410296Ssam if (command("%s", cmd) != PRELIM) 26510296Ssam goto bad; 26610296Ssam dout = dataconn("w"); 26710296Ssam if (dout == NULL) 26810296Ssam goto bad; 26910296Ssam gettimeofday(&start, (struct timezone *)0); 27011219Ssam switch (type) { 27111219Ssam 27211219Ssam case TYPE_I: 27311219Ssam case TYPE_L: 27411346Ssam errno = d = 0; 27511219Ssam while ((c = read(fileno (fin), buf, sizeof (buf))) > 0) { 27611346Ssam if ((d = write(fileno (dout), buf, c)) < 0) 27711219Ssam break; 27811219Ssam bytes += c; 27911651Ssam if (hash) { 28011651Ssam putchar('#'); 28111651Ssam fflush(stdout); 28211651Ssam } 28311219Ssam } 28413213Ssam if (hash && bytes > 0) { 28511651Ssam putchar('\n'); 28611651Ssam fflush(stdout); 28711651Ssam } 28811219Ssam if (c < 0) 28911219Ssam perror(local); 29011346Ssam if (d < 0) 29111219Ssam perror("netout"); 29211219Ssam break; 29311219Ssam 29411219Ssam case TYPE_A: 29511219Ssam while ((c = getc(fin)) != EOF) { 29611219Ssam if (c == '\n') { 29711651Ssam while (hash && (bytes >= hashbytes)) { 29811651Ssam putchar('#'); 29911651Ssam fflush(stdout); 30011651Ssam hashbytes += sizeof (buf); 30111651Ssam } 30211219Ssam if (ferror(dout)) 30311219Ssam break; 30411219Ssam putc('\r', dout); 30511219Ssam bytes++; 30611219Ssam } 30711219Ssam putc(c, dout); 30811219Ssam bytes++; 30911219Ssam if (c == '\r') { 31011219Ssam putc('\0', dout); 31111219Ssam bytes++; 31211219Ssam } 31311219Ssam } 31411651Ssam if (hash) { 31513213Ssam if (bytes < hashbytes) 31613213Ssam putchar('#'); 31711651Ssam putchar('\n'); 31811651Ssam fflush(stdout); 31911651Ssam } 32011219Ssam if (ferror(fin)) 32111219Ssam perror(local); 32211346Ssam if (ferror(dout)) 32311219Ssam perror("netout"); 32411219Ssam break; 32510296Ssam } 32610296Ssam gettimeofday(&stop, (struct timezone *)0); 32710296Ssam if (closefunc != NULL) 32810296Ssam (*closefunc)(fin); 32910296Ssam (void) fclose(dout); 33010296Ssam (void) getreply(0); 33110296Ssam done: 33210296Ssam signal(SIGINT, oldintr); 33310296Ssam if (bytes > 0 && verbose) 33410296Ssam ptransfer("sent", bytes, &start, &stop); 33510296Ssam return; 33610296Ssam bad: 33710296Ssam if (data >= 0) 33810296Ssam (void) close(data), data = -1; 33910296Ssam if (closefunc != NULL && fin != NULL) 34010296Ssam (*closefunc)(fin); 34110296Ssam goto done; 34210296Ssam } 34310296Ssam 34410296Ssam jmp_buf recvabort; 34510296Ssam 34610296Ssam abortrecv() 34710296Ssam { 34810296Ssam 34910296Ssam longjmp(recvabort, 1); 35010296Ssam } 35110296Ssam 35211651Ssam recvrequest(cmd, local, remote, mode) 35311651Ssam char *cmd, *local, *remote, *mode; 35410296Ssam { 35510296Ssam FILE *fout, *din, *popen(); 35611651Ssam int (*closefunc)(), pclose(), fclose(), (*oldintr)(); 35711219Ssam char buf[BUFSIZ]; 35811651Ssam long bytes = 0, hashbytes = sizeof (buf); 35911346Ssam register int c, d; 36010296Ssam struct timeval start, stop; 36110296Ssam 36210296Ssam closefunc = NULL; 36310296Ssam if (setjmp(recvabort)) 36410296Ssam goto bad; 36510296Ssam oldintr = signal(SIGINT, abortrecv); 36610296Ssam if (strcmp(local, "-") && *local != '|') 36710296Ssam if (access(local, 2) < 0) { 36810296Ssam char *dir = rindex(local, '/'); 36910296Ssam 37010296Ssam if (dir != NULL) 37110296Ssam *dir = 0; 37217305Sralph d = access(dir ? local : ".", 2); 37317305Sralph if (dir != NULL) 37417305Sralph *dir = '/'; 37517305Sralph if (d < 0) { 37610296Ssam perror(local); 37710296Ssam goto bad; 37810296Ssam } 37910296Ssam } 38010296Ssam if (initconn()) 38110296Ssam goto bad; 38210296Ssam if (remote) { 38310296Ssam if (command("%s %s", cmd, remote) != PRELIM) 38410296Ssam goto bad; 38510296Ssam } else 38610296Ssam if (command("%s", cmd) != PRELIM) 38710296Ssam goto bad; 38810296Ssam if (strcmp(local, "-") == 0) 38910296Ssam fout = stdout; 39010296Ssam else if (*local == '|') { 39110296Ssam fout = popen(local + 1, "w"); 39210296Ssam closefunc = pclose; 39310296Ssam } else { 39411651Ssam fout = fopen(local, mode); 39510296Ssam closefunc = fclose; 39610296Ssam } 39710296Ssam if (fout == NULL) { 39810296Ssam perror(local + 1); 39910296Ssam goto bad; 40010296Ssam } 40110296Ssam din = dataconn("r"); 40210296Ssam if (din == NULL) 40310296Ssam goto bad; 40410296Ssam gettimeofday(&start, (struct timezone *)0); 40511219Ssam switch (type) { 40611219Ssam 40711219Ssam case TYPE_I: 40811219Ssam case TYPE_L: 40911346Ssam errno = d = 0; 41011219Ssam while ((c = read(fileno(din), buf, sizeof (buf))) > 0) { 41111346Ssam if ((d = write(fileno(fout), buf, c)) < 0) 41211219Ssam break; 41311219Ssam bytes += c; 41411651Ssam if (hash) { 41511651Ssam putchar('#'); 41611651Ssam fflush(stdout); 41711651Ssam } 41811219Ssam } 41913213Ssam if (hash && bytes > 0) { 42011651Ssam putchar('\n'); 42111651Ssam fflush(stdout); 42211651Ssam } 42311219Ssam if (c < 0) 42411219Ssam perror("netin"); 42511346Ssam if (d < 0) 42610296Ssam perror(local); 42711219Ssam break; 42811219Ssam 42911219Ssam case TYPE_A: 43011219Ssam while ((c = getc(din)) != EOF) { 43111219Ssam if (c == '\r') { 43211651Ssam while (hash && (bytes >= hashbytes)) { 43311651Ssam putchar('#'); 43411651Ssam fflush(stdout); 43511651Ssam hashbytes += sizeof (buf); 43611651Ssam } 43710296Ssam bytes++; 43811219Ssam if ((c = getc(din)) != '\n') { 43911219Ssam if (ferror (fout)) 44011219Ssam break; 44111219Ssam putc ('\r', fout); 44211219Ssam } 44311219Ssam if (c == '\0') { 44411219Ssam bytes++; 44511219Ssam continue; 44611219Ssam } 44711219Ssam } 44811219Ssam putc (c, fout); 44911219Ssam bytes++; 45010296Ssam } 45111651Ssam if (hash) { 45213213Ssam if (bytes < hashbytes) 45313213Ssam putchar('#'); 45411651Ssam putchar('\n'); 45511651Ssam fflush(stdout); 45611651Ssam } 45711219Ssam if (ferror (din)) 45811219Ssam perror ("netin"); 45911219Ssam if (ferror (fout)) 46011219Ssam perror (local); 46111219Ssam break; 46210296Ssam } 46310296Ssam gettimeofday(&stop, (struct timezone *)0); 46410296Ssam (void) fclose(din); 46510296Ssam if (closefunc != NULL) 46610296Ssam (*closefunc)(fout); 46710296Ssam (void) getreply(0); 46810296Ssam done: 46910296Ssam signal(SIGINT, oldintr); 47010296Ssam if (bytes > 0 && verbose) 47110296Ssam ptransfer("received", bytes, &start, &stop); 47210296Ssam return; 47310296Ssam bad: 47410296Ssam if (data >= 0) 47510296Ssam (void) close(data), data = -1; 47610296Ssam if (closefunc != NULL && fout != NULL) 47710296Ssam (*closefunc)(fout); 47810296Ssam goto done; 47910296Ssam } 48010296Ssam 48110296Ssam /* 48210296Ssam * Need to start a listen on the data channel 48310296Ssam * before we send the command, otherwise the 48410296Ssam * server's connect may fail. 48510296Ssam */ 48611651Ssam static int sendport = -1; 48711651Ssam 48810296Ssam initconn() 48910296Ssam { 49010296Ssam register char *p, *a; 49111627Ssam int result, len; 49217450Slepreau int on = 1; 49310296Ssam 49411651Ssam noport: 49510296Ssam data_addr = myctladdr; 49611651Ssam if (sendport) 49711651Ssam data_addr.sin_port = 0; /* let system pick one */ 49811651Ssam if (data != -1) 49911651Ssam (void) close (data); 50018287Sralph data = socket(AF_INET, SOCK_STREAM, 0); 50110296Ssam if (data < 0) { 50210296Ssam perror("ftp: socket"); 50310296Ssam return (1); 50410296Ssam } 50512397Ssam if (!sendport) 50617450Slepreau if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) { 50712397Ssam perror("ftp: setsockopt (resuse address)"); 50812397Ssam goto bad; 50912397Ssam } 51010296Ssam if (bind(data, (char *)&data_addr, sizeof (data_addr), 0) < 0) { 51110296Ssam perror("ftp: bind"); 51210296Ssam goto bad; 51310296Ssam } 51410296Ssam if (options & SO_DEBUG && 51517450Slepreau setsockopt(data, SOL_SOCKET, SO_DEBUG, &on, sizeof (on)) < 0) 51610296Ssam perror("ftp: setsockopt (ignored)"); 51711627Ssam len = sizeof (data_addr); 51811627Ssam if (getsockname(data, (char *)&data_addr, &len) < 0) { 51911627Ssam perror("ftp: getsockname"); 52010296Ssam goto bad; 52110296Ssam } 52210296Ssam if (listen(data, 1) < 0) { 52310296Ssam perror("ftp: listen"); 52410296Ssam goto bad; 52510296Ssam } 52611651Ssam if (sendport) { 52711651Ssam a = (char *)&data_addr.sin_addr; 52811651Ssam p = (char *)&data_addr.sin_port; 52910296Ssam #define UC(b) (((int)b)&0xff) 53011651Ssam result = 53111651Ssam command("PORT %d,%d,%d,%d,%d,%d", 53211651Ssam UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 53311651Ssam UC(p[0]), UC(p[1])); 53411651Ssam if (result == ERROR && sendport == -1) { 53511651Ssam sendport = 0; 53611651Ssam goto noport; 53711651Ssam } 53811651Ssam return (result != COMPLETE); 53911651Ssam } 54011651Ssam return (0); 54110296Ssam bad: 54210296Ssam (void) close(data), data = -1; 54310296Ssam return (1); 54410296Ssam } 54510296Ssam 54610296Ssam FILE * 54710296Ssam dataconn(mode) 54810296Ssam char *mode; 54910296Ssam { 55010296Ssam struct sockaddr_in from; 55110296Ssam int s, fromlen = sizeof (from); 55210296Ssam 55310296Ssam s = accept(data, &from, &fromlen, 0); 55410296Ssam if (s < 0) { 55510296Ssam perror("ftp: accept"); 55610296Ssam (void) close(data), data = -1; 55710296Ssam return (NULL); 55810296Ssam } 55910296Ssam (void) close(data); 56010296Ssam data = s; 56110296Ssam return (fdopen(data, mode)); 56210296Ssam } 56310296Ssam 56410296Ssam ptransfer(direction, bytes, t0, t1) 56510296Ssam char *direction; 56611651Ssam long bytes; 56710296Ssam struct timeval *t0, *t1; 56810296Ssam { 56910296Ssam struct timeval td; 57016437Sleres float s, bs; 57110296Ssam 57210296Ssam tvsub(&td, t1, t0); 57316437Sleres s = td.tv_sec + (td.tv_usec / 1000000.); 57410296Ssam #define nz(x) ((x) == 0 ? 1 : (x)) 57516437Sleres bs = bytes / nz(s); 57616437Sleres printf("%ld bytes %s in %.2g seconds (%.2g Kbytes/s)\n", 57716437Sleres bytes, direction, s, bs / 1024.); 57810296Ssam } 57910296Ssam 58010296Ssam tvadd(tsum, t0) 58110296Ssam struct timeval *tsum, *t0; 58210296Ssam { 58310296Ssam 58410296Ssam tsum->tv_sec += t0->tv_sec; 58510296Ssam tsum->tv_usec += t0->tv_usec; 58610296Ssam if (tsum->tv_usec > 1000000) 58710296Ssam tsum->tv_sec++, tsum->tv_usec -= 1000000; 58810296Ssam } 58910296Ssam 59010296Ssam tvsub(tdiff, t1, t0) 59110296Ssam struct timeval *tdiff, *t1, *t0; 59210296Ssam { 59310296Ssam 59410296Ssam tdiff->tv_sec = t1->tv_sec - t0->tv_sec; 59510296Ssam tdiff->tv_usec = t1->tv_usec - t0->tv_usec; 59610296Ssam if (tdiff->tv_usec < 0) 59710296Ssam tdiff->tv_sec--, tdiff->tv_usec += 1000000; 59810296Ssam } 599