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*25100Sbloom static char sccsid[] = "@(#)ftp.c 5.3 (Berkeley) 10/05/85"; 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 3610296Ssam struct hostent * 3710296Ssam hookup(host, port) 3810296Ssam char *host; 3910296Ssam int port; 4010296Ssam { 4110296Ssam register struct hostent *hp; 4211627Ssam int s, len; 43*25100Sbloom long addr; 4410296Ssam 4510296Ssam bzero((char *)&hisctladdr, sizeof (hisctladdr)); 46*25100Sbloom if ((addr = inet_addr(host)) == -1) { 47*25100Sbloom hp = gethostbyname(host); 48*25100Sbloom } else { 4910296Ssam static struct hostent def; 5010296Ssam static struct in_addr defaddr; 51*25100Sbloom static char namebuf[MAXHOSTNAMELEN]; 5225099Sbloom static char *addrbuf; 53*25100Sbloom long inet_addr(); 5410296Ssam 55*25100Sbloom defaddr.s_addr = addr; 5610296Ssam strcpy(namebuf, host); 5710296Ssam def.h_name = namebuf; 5810296Ssam hostname = namebuf; 5925099Sbloom def.h_addr_list = &addrbuf; 6010296Ssam def.h_addr = (char *)&defaddr; 6110296Ssam def.h_length = sizeof (struct in_addr); 6210296Ssam def.h_addrtype = AF_INET; 6310296Ssam def.h_aliases = 0; 6410296Ssam hp = &def; 6510296Ssam } 66*25100Sbloom if (hp == NULL) { 67*25100Sbloom fprintf(stderr, "%s: Unknown host.\n", host); 68*25100Sbloom return (0); 69*25100Sbloom } 7011283Ssam hostname = hp->h_name; 7111283Ssam hisctladdr.sin_family = hp->h_addrtype; 7218287Sralph s = socket(hp->h_addrtype, SOCK_STREAM, 0); 7310296Ssam if (s < 0) { 7410296Ssam perror("ftp: socket"); 7510296Ssam return (0); 7610296Ssam } 7710296Ssam if (bind(s, (char *)&hisctladdr, sizeof (hisctladdr), 0) < 0) { 7810296Ssam perror("ftp: bind"); 7910296Ssam goto bad; 8010296Ssam } 8110296Ssam bcopy(hp->h_addr, (char *)&hisctladdr.sin_addr, hp->h_length); 8210296Ssam hisctladdr.sin_port = port; 8310296Ssam if (connect(s, (char *)&hisctladdr, sizeof (hisctladdr), 0) < 0) { 8410296Ssam perror("ftp: connect"); 8510296Ssam goto bad; 8610296Ssam } 8711627Ssam len = sizeof (myctladdr); 8811627Ssam if (getsockname(s, (char *)&myctladdr, &len) < 0) { 8911627Ssam perror("ftp: getsockname"); 9010296Ssam goto bad; 9110296Ssam } 9210296Ssam cin = fdopen(s, "r"); 9310296Ssam cout = fdopen(s, "w"); 9411219Ssam if (cin == NULL || cout == NULL) { 9510296Ssam fprintf(stderr, "ftp: fdopen failed.\n"); 9610296Ssam if (cin) 9710296Ssam fclose(cin); 9810296Ssam if (cout) 9910296Ssam fclose(cout); 10010296Ssam goto bad; 10110296Ssam } 10210296Ssam if (verbose) 10310296Ssam printf("Connected to %s.\n", hp->h_name); 10410296Ssam (void) getreply(0); /* read startup message from server */ 10510296Ssam return (hp); 10610296Ssam bad: 10710296Ssam close(s); 10810296Ssam return ((struct hostent *)0); 10910296Ssam } 11010296Ssam 11110296Ssam login(hp) 11210296Ssam struct hostent *hp; 11310296Ssam { 11410296Ssam char acct[80]; 11510296Ssam char *user, *pass; 11610296Ssam int n; 11710296Ssam 11810296Ssam user = pass = 0; 11910296Ssam ruserpass(hp->h_name, &user, &pass); 12010296Ssam n = command("USER %s", user); 12110296Ssam if (n == CONTINUE) 12210296Ssam n = command("PASS %s", pass); 12310296Ssam if (n == CONTINUE) { 12410296Ssam printf("Account: "); (void) fflush(stdout); 12510296Ssam (void) fgets(acct, sizeof(acct) - 1, stdin); 12610296Ssam acct[strlen(acct) - 1] = '\0'; 12710296Ssam n = command("ACCT %s", acct); 12810296Ssam } 12910296Ssam if (n != COMPLETE) { 13010296Ssam fprintf(stderr, "Login failed.\n"); 13110296Ssam return (0); 13210296Ssam } 13310296Ssam return (1); 13410296Ssam } 13510296Ssam 13610296Ssam /*VARARGS 1*/ 13710296Ssam command(fmt, args) 13810296Ssam char *fmt; 13910296Ssam { 14010296Ssam 14110296Ssam if (debug) { 14210296Ssam printf("---> "); 14310296Ssam _doprnt(fmt, &args, stdout); 14410296Ssam printf("\n"); 14510296Ssam (void) fflush(stdout); 14610296Ssam } 14711219Ssam if (cout == NULL) { 14811219Ssam perror ("No control connection for command"); 14911219Ssam return (0); 15011219Ssam } 15110296Ssam _doprnt(fmt, &args, cout); 15210296Ssam fprintf(cout, "\r\n"); 15310296Ssam (void) fflush(cout); 15410296Ssam return (getreply(!strcmp(fmt, "QUIT"))); 15510296Ssam } 15610296Ssam 15710296Ssam #include <ctype.h> 15810296Ssam 15910296Ssam getreply(expecteof) 16010296Ssam int expecteof; 16110296Ssam { 16211219Ssam register int c, n; 16310296Ssam register int code, dig; 16410296Ssam int originalcode = 0, continuation = 0; 16510296Ssam 16610296Ssam for (;;) { 16710296Ssam dig = n = code = 0; 16810296Ssam while ((c = getc(cin)) != '\n') { 16910296Ssam dig++; 17010296Ssam if (c == EOF) { 17110296Ssam if (expecteof) 17210296Ssam return (0); 17310296Ssam lostpeer(); 17410296Ssam exit(1); 17510296Ssam } 17610296Ssam if (verbose && c != '\r' || 17710296Ssam (n == '5' && dig > 4)) 17810296Ssam putchar(c); 17910296Ssam if (dig < 4 && isdigit(c)) 18010296Ssam code = code * 10 + (c - '0'); 18110296Ssam if (dig == 4 && c == '-') 18210296Ssam continuation++; 18310296Ssam if (n == 0) 18410296Ssam n = c; 18510296Ssam } 18611346Ssam if (verbose || n == '5') { 18710296Ssam putchar(c); 18811346Ssam (void) fflush (stdout); 18911346Ssam } 19010296Ssam if (continuation && code != originalcode) { 19110296Ssam if (originalcode == 0) 19210296Ssam originalcode = code; 19310296Ssam continue; 19410296Ssam } 19511219Ssam if (expecteof || empty(cin)) 19610296Ssam return (n - '0'); 19710296Ssam } 19810296Ssam } 19910296Ssam 20010296Ssam empty(f) 20110296Ssam FILE *f; 20210296Ssam { 20311651Ssam long mask; 20410296Ssam struct timeval t; 20510296Ssam 20610296Ssam if (f->_cnt > 0) 20710296Ssam return (0); 20810296Ssam mask = (1 << fileno(f)); 20910296Ssam t.tv_sec = t.tv_usec = 0; 21010296Ssam (void) select(20, &mask, 0, 0, &t); 21110296Ssam return (mask == 0); 21210296Ssam } 21310296Ssam 21410296Ssam jmp_buf sendabort; 21510296Ssam 21610296Ssam abortsend() 21710296Ssam { 21810296Ssam 21910296Ssam longjmp(sendabort, 1); 22010296Ssam } 22110296Ssam 22210296Ssam sendrequest(cmd, local, remote) 22310296Ssam char *cmd, *local, *remote; 22410296Ssam { 22510296Ssam FILE *fin, *dout, *popen(); 22610296Ssam int (*closefunc)(), pclose(), fclose(), (*oldintr)(); 22711219Ssam char buf[BUFSIZ]; 22811651Ssam long bytes = 0, hashbytes = sizeof (buf); 22911346Ssam register int c, d; 23010296Ssam struct stat st; 23110296Ssam struct timeval start, stop; 23210296Ssam 23310296Ssam closefunc = NULL; 23410296Ssam if (setjmp(sendabort)) 23510296Ssam goto bad; 23610296Ssam oldintr = signal(SIGINT, abortsend); 23710296Ssam if (strcmp(local, "-") == 0) 23810296Ssam fin = stdin; 23910296Ssam else if (*local == '|') { 24010296Ssam fin = popen(local + 1, "r"); 24110296Ssam if (fin == NULL) { 24210296Ssam perror(local + 1); 24310296Ssam goto bad; 24410296Ssam } 24510296Ssam closefunc = pclose; 24610296Ssam } else { 24710296Ssam fin = fopen(local, "r"); 24810296Ssam if (fin == NULL) { 24910296Ssam perror(local); 25010296Ssam goto bad; 25110296Ssam } 25210296Ssam closefunc = fclose; 25310296Ssam if (fstat(fileno(fin), &st) < 0 || 25410296Ssam (st.st_mode&S_IFMT) != S_IFREG) { 25517949Sralph fprintf(stderr, "%s: not a plain file.\n", local); 25610296Ssam goto bad; 25710296Ssam } 25810296Ssam } 25910296Ssam if (initconn()) 26010296Ssam goto bad; 26110296Ssam if (remote) { 26210296Ssam if (command("%s %s", cmd, remote) != PRELIM) 26310296Ssam goto bad; 26410296Ssam } else 26510296Ssam if (command("%s", cmd) != PRELIM) 26610296Ssam goto bad; 26710296Ssam dout = dataconn("w"); 26810296Ssam if (dout == NULL) 26910296Ssam goto bad; 27010296Ssam gettimeofday(&start, (struct timezone *)0); 27111219Ssam switch (type) { 27211219Ssam 27311219Ssam case TYPE_I: 27411219Ssam case TYPE_L: 27511346Ssam errno = d = 0; 27611219Ssam while ((c = read(fileno (fin), buf, sizeof (buf))) > 0) { 27711346Ssam if ((d = write(fileno (dout), buf, c)) < 0) 27811219Ssam break; 27911219Ssam bytes += c; 28011651Ssam if (hash) { 28111651Ssam putchar('#'); 28211651Ssam fflush(stdout); 28311651Ssam } 28411219Ssam } 28513213Ssam if (hash && bytes > 0) { 28611651Ssam putchar('\n'); 28711651Ssam fflush(stdout); 28811651Ssam } 28911219Ssam if (c < 0) 29011219Ssam perror(local); 29111346Ssam if (d < 0) 29211219Ssam perror("netout"); 29311219Ssam break; 29411219Ssam 29511219Ssam case TYPE_A: 29611219Ssam while ((c = getc(fin)) != EOF) { 29711219Ssam if (c == '\n') { 29811651Ssam while (hash && (bytes >= hashbytes)) { 29911651Ssam putchar('#'); 30011651Ssam fflush(stdout); 30111651Ssam hashbytes += sizeof (buf); 30211651Ssam } 30311219Ssam if (ferror(dout)) 30411219Ssam break; 30511219Ssam putc('\r', dout); 30611219Ssam bytes++; 30711219Ssam } 30811219Ssam putc(c, dout); 30911219Ssam bytes++; 31011219Ssam if (c == '\r') { 31111219Ssam putc('\0', dout); 31211219Ssam bytes++; 31311219Ssam } 31411219Ssam } 31511651Ssam if (hash) { 31613213Ssam if (bytes < hashbytes) 31713213Ssam putchar('#'); 31811651Ssam putchar('\n'); 31911651Ssam fflush(stdout); 32011651Ssam } 32111219Ssam if (ferror(fin)) 32211219Ssam perror(local); 32311346Ssam if (ferror(dout)) 32411219Ssam perror("netout"); 32511219Ssam break; 32610296Ssam } 32710296Ssam gettimeofday(&stop, (struct timezone *)0); 32810296Ssam if (closefunc != NULL) 32910296Ssam (*closefunc)(fin); 33010296Ssam (void) fclose(dout); 33110296Ssam (void) getreply(0); 33210296Ssam done: 33310296Ssam signal(SIGINT, oldintr); 33410296Ssam if (bytes > 0 && verbose) 33510296Ssam ptransfer("sent", bytes, &start, &stop); 33610296Ssam return; 33710296Ssam bad: 33810296Ssam if (data >= 0) 33910296Ssam (void) close(data), data = -1; 34010296Ssam if (closefunc != NULL && fin != NULL) 34110296Ssam (*closefunc)(fin); 34210296Ssam goto done; 34310296Ssam } 34410296Ssam 34510296Ssam jmp_buf recvabort; 34610296Ssam 34710296Ssam abortrecv() 34810296Ssam { 34910296Ssam 35010296Ssam longjmp(recvabort, 1); 35110296Ssam } 35210296Ssam 35311651Ssam recvrequest(cmd, local, remote, mode) 35411651Ssam char *cmd, *local, *remote, *mode; 35510296Ssam { 35610296Ssam FILE *fout, *din, *popen(); 35711651Ssam int (*closefunc)(), pclose(), fclose(), (*oldintr)(); 35811219Ssam char buf[BUFSIZ]; 35911651Ssam long bytes = 0, hashbytes = sizeof (buf); 36011346Ssam register int c, d; 36110296Ssam struct timeval start, stop; 36210296Ssam 36310296Ssam closefunc = NULL; 36410296Ssam if (setjmp(recvabort)) 36510296Ssam goto bad; 36610296Ssam oldintr = signal(SIGINT, abortrecv); 36710296Ssam if (strcmp(local, "-") && *local != '|') 36810296Ssam if (access(local, 2) < 0) { 36910296Ssam char *dir = rindex(local, '/'); 37010296Ssam 37110296Ssam if (dir != NULL) 37210296Ssam *dir = 0; 37317305Sralph d = access(dir ? local : ".", 2); 37417305Sralph if (dir != NULL) 37517305Sralph *dir = '/'; 37617305Sralph if (d < 0) { 37710296Ssam perror(local); 37810296Ssam goto bad; 37910296Ssam } 38010296Ssam } 38110296Ssam if (initconn()) 38210296Ssam goto bad; 38310296Ssam if (remote) { 38410296Ssam if (command("%s %s", cmd, remote) != PRELIM) 38510296Ssam goto bad; 38610296Ssam } else 38710296Ssam if (command("%s", cmd) != PRELIM) 38810296Ssam goto bad; 38910296Ssam if (strcmp(local, "-") == 0) 39010296Ssam fout = stdout; 39110296Ssam else if (*local == '|') { 39210296Ssam fout = popen(local + 1, "w"); 39310296Ssam closefunc = pclose; 39410296Ssam } else { 39511651Ssam fout = fopen(local, mode); 39610296Ssam closefunc = fclose; 39710296Ssam } 39810296Ssam if (fout == NULL) { 39910296Ssam perror(local + 1); 40010296Ssam goto bad; 40110296Ssam } 40210296Ssam din = dataconn("r"); 40310296Ssam if (din == NULL) 40410296Ssam goto bad; 40510296Ssam gettimeofday(&start, (struct timezone *)0); 40611219Ssam switch (type) { 40711219Ssam 40811219Ssam case TYPE_I: 40911219Ssam case TYPE_L: 41011346Ssam errno = d = 0; 41111219Ssam while ((c = read(fileno(din), buf, sizeof (buf))) > 0) { 41211346Ssam if ((d = write(fileno(fout), buf, c)) < 0) 41311219Ssam break; 41411219Ssam bytes += c; 41511651Ssam if (hash) { 41611651Ssam putchar('#'); 41711651Ssam fflush(stdout); 41811651Ssam } 41911219Ssam } 42013213Ssam if (hash && bytes > 0) { 42111651Ssam putchar('\n'); 42211651Ssam fflush(stdout); 42311651Ssam } 42411219Ssam if (c < 0) 42511219Ssam perror("netin"); 42611346Ssam if (d < 0) 42710296Ssam perror(local); 42811219Ssam break; 42911219Ssam 43011219Ssam case TYPE_A: 43111219Ssam while ((c = getc(din)) != EOF) { 43211219Ssam if (c == '\r') { 43311651Ssam while (hash && (bytes >= hashbytes)) { 43411651Ssam putchar('#'); 43511651Ssam fflush(stdout); 43611651Ssam hashbytes += sizeof (buf); 43711651Ssam } 43810296Ssam bytes++; 43911219Ssam if ((c = getc(din)) != '\n') { 44011219Ssam if (ferror (fout)) 44111219Ssam break; 44211219Ssam putc ('\r', fout); 44311219Ssam } 44411219Ssam if (c == '\0') { 44511219Ssam bytes++; 44611219Ssam continue; 44711219Ssam } 44811219Ssam } 44911219Ssam putc (c, fout); 45011219Ssam bytes++; 45110296Ssam } 45211651Ssam if (hash) { 45313213Ssam if (bytes < hashbytes) 45413213Ssam putchar('#'); 45511651Ssam putchar('\n'); 45611651Ssam fflush(stdout); 45711651Ssam } 45811219Ssam if (ferror (din)) 45911219Ssam perror ("netin"); 46011219Ssam if (ferror (fout)) 46111219Ssam perror (local); 46211219Ssam break; 46310296Ssam } 46410296Ssam gettimeofday(&stop, (struct timezone *)0); 46510296Ssam (void) fclose(din); 46610296Ssam if (closefunc != NULL) 46710296Ssam (*closefunc)(fout); 46810296Ssam (void) getreply(0); 46910296Ssam done: 47010296Ssam signal(SIGINT, oldintr); 47110296Ssam if (bytes > 0 && verbose) 47210296Ssam ptransfer("received", bytes, &start, &stop); 47310296Ssam return; 47410296Ssam bad: 47510296Ssam if (data >= 0) 47610296Ssam (void) close(data), data = -1; 47710296Ssam if (closefunc != NULL && fout != NULL) 47810296Ssam (*closefunc)(fout); 47910296Ssam goto done; 48010296Ssam } 48110296Ssam 48210296Ssam /* 48310296Ssam * Need to start a listen on the data channel 48410296Ssam * before we send the command, otherwise the 48510296Ssam * server's connect may fail. 48610296Ssam */ 48711651Ssam static int sendport = -1; 48811651Ssam 48910296Ssam initconn() 49010296Ssam { 49110296Ssam register char *p, *a; 49211627Ssam int result, len; 49317450Slepreau int on = 1; 49410296Ssam 49511651Ssam noport: 49610296Ssam data_addr = myctladdr; 49711651Ssam if (sendport) 49811651Ssam data_addr.sin_port = 0; /* let system pick one */ 49911651Ssam if (data != -1) 50011651Ssam (void) close (data); 50118287Sralph data = socket(AF_INET, SOCK_STREAM, 0); 50210296Ssam if (data < 0) { 50310296Ssam perror("ftp: socket"); 50410296Ssam return (1); 50510296Ssam } 50612397Ssam if (!sendport) 50717450Slepreau if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) { 50812397Ssam perror("ftp: setsockopt (resuse address)"); 50912397Ssam goto bad; 51012397Ssam } 51110296Ssam if (bind(data, (char *)&data_addr, sizeof (data_addr), 0) < 0) { 51210296Ssam perror("ftp: bind"); 51310296Ssam goto bad; 51410296Ssam } 51510296Ssam if (options & SO_DEBUG && 51617450Slepreau setsockopt(data, SOL_SOCKET, SO_DEBUG, &on, sizeof (on)) < 0) 51710296Ssam perror("ftp: setsockopt (ignored)"); 51811627Ssam len = sizeof (data_addr); 51911627Ssam if (getsockname(data, (char *)&data_addr, &len) < 0) { 52011627Ssam perror("ftp: getsockname"); 52110296Ssam goto bad; 52210296Ssam } 52310296Ssam if (listen(data, 1) < 0) { 52410296Ssam perror("ftp: listen"); 52510296Ssam goto bad; 52610296Ssam } 52711651Ssam if (sendport) { 52811651Ssam a = (char *)&data_addr.sin_addr; 52911651Ssam p = (char *)&data_addr.sin_port; 53010296Ssam #define UC(b) (((int)b)&0xff) 53111651Ssam result = 53211651Ssam command("PORT %d,%d,%d,%d,%d,%d", 53311651Ssam UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 53411651Ssam UC(p[0]), UC(p[1])); 53511651Ssam if (result == ERROR && sendport == -1) { 53611651Ssam sendport = 0; 53711651Ssam goto noport; 53811651Ssam } 53911651Ssam return (result != COMPLETE); 54011651Ssam } 54111651Ssam return (0); 54210296Ssam bad: 54310296Ssam (void) close(data), data = -1; 54410296Ssam return (1); 54510296Ssam } 54610296Ssam 54710296Ssam FILE * 54810296Ssam dataconn(mode) 54910296Ssam char *mode; 55010296Ssam { 55110296Ssam struct sockaddr_in from; 55210296Ssam int s, fromlen = sizeof (from); 55310296Ssam 55410296Ssam s = accept(data, &from, &fromlen, 0); 55510296Ssam if (s < 0) { 55610296Ssam perror("ftp: accept"); 55710296Ssam (void) close(data), data = -1; 55810296Ssam return (NULL); 55910296Ssam } 56010296Ssam (void) close(data); 56110296Ssam data = s; 56210296Ssam return (fdopen(data, mode)); 56310296Ssam } 56410296Ssam 56510296Ssam ptransfer(direction, bytes, t0, t1) 56610296Ssam char *direction; 56711651Ssam long bytes; 56810296Ssam struct timeval *t0, *t1; 56910296Ssam { 57010296Ssam struct timeval td; 57116437Sleres float s, bs; 57210296Ssam 57310296Ssam tvsub(&td, t1, t0); 57416437Sleres s = td.tv_sec + (td.tv_usec / 1000000.); 57510296Ssam #define nz(x) ((x) == 0 ? 1 : (x)) 57616437Sleres bs = bytes / nz(s); 57716437Sleres printf("%ld bytes %s in %.2g seconds (%.2g Kbytes/s)\n", 57816437Sleres bytes, direction, s, bs / 1024.); 57910296Ssam } 58010296Ssam 58110296Ssam tvadd(tsum, t0) 58210296Ssam struct timeval *tsum, *t0; 58310296Ssam { 58410296Ssam 58510296Ssam tsum->tv_sec += t0->tv_sec; 58610296Ssam tsum->tv_usec += t0->tv_usec; 58710296Ssam if (tsum->tv_usec > 1000000) 58810296Ssam tsum->tv_sec++, tsum->tv_usec -= 1000000; 58910296Ssam } 59010296Ssam 59110296Ssam tvsub(tdiff, t1, t0) 59210296Ssam struct timeval *tdiff, *t1, *t0; 59310296Ssam { 59410296Ssam 59510296Ssam tdiff->tv_sec = t1->tv_sec - t0->tv_sec; 59610296Ssam tdiff->tv_usec = t1->tv_usec - t0->tv_usec; 59710296Ssam if (tdiff->tv_usec < 0) 59810296Ssam tdiff->tv_sec--, tdiff->tv_usec += 1000000; 59910296Ssam } 600