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*25099Sbloom static char sccsid[] = "@(#)ftp.c 5.2 (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; 4310296Ssam 4410296Ssam bzero((char *)&hisctladdr, sizeof (hisctladdr)); 4510296Ssam hp = gethostbyname(host); 4611283Ssam if (hp == NULL) { 4710296Ssam static struct hostent def; 4810296Ssam static struct in_addr defaddr; 4910296Ssam static char namebuf[128]; 50*25099Sbloom static char *addrbuf; 5110296Ssam int inet_addr(); 5210296Ssam 5310296Ssam defaddr.s_addr = inet_addr(host); 5410296Ssam if (defaddr.s_addr == -1) { 5510296Ssam fprintf(stderr, "%s: Unknown host.\n", host); 5610296Ssam return (0); 5710296Ssam } 5810296Ssam strcpy(namebuf, host); 5910296Ssam def.h_name = namebuf; 6010296Ssam hostname = namebuf; 61*25099Sbloom def.h_addr_list = &addrbuf; 6210296Ssam def.h_addr = (char *)&defaddr; 6310296Ssam def.h_length = sizeof (struct in_addr); 6410296Ssam def.h_addrtype = AF_INET; 6510296Ssam def.h_aliases = 0; 6610296Ssam hp = &def; 6710296Ssam } 6811283Ssam hostname = hp->h_name; 6911283Ssam hisctladdr.sin_family = hp->h_addrtype; 7018287Sralph s = socket(hp->h_addrtype, SOCK_STREAM, 0); 7110296Ssam if (s < 0) { 7210296Ssam perror("ftp: socket"); 7310296Ssam return (0); 7410296Ssam } 7510296Ssam if (bind(s, (char *)&hisctladdr, sizeof (hisctladdr), 0) < 0) { 7610296Ssam perror("ftp: bind"); 7710296Ssam goto bad; 7810296Ssam } 7910296Ssam bcopy(hp->h_addr, (char *)&hisctladdr.sin_addr, hp->h_length); 8010296Ssam hisctladdr.sin_port = port; 8110296Ssam if (connect(s, (char *)&hisctladdr, sizeof (hisctladdr), 0) < 0) { 8210296Ssam perror("ftp: connect"); 8310296Ssam goto bad; 8410296Ssam } 8511627Ssam len = sizeof (myctladdr); 8611627Ssam if (getsockname(s, (char *)&myctladdr, &len) < 0) { 8711627Ssam perror("ftp: getsockname"); 8810296Ssam goto bad; 8910296Ssam } 9010296Ssam cin = fdopen(s, "r"); 9110296Ssam cout = fdopen(s, "w"); 9211219Ssam if (cin == NULL || cout == NULL) { 9310296Ssam fprintf(stderr, "ftp: fdopen failed.\n"); 9410296Ssam if (cin) 9510296Ssam fclose(cin); 9610296Ssam if (cout) 9710296Ssam fclose(cout); 9810296Ssam goto bad; 9910296Ssam } 10010296Ssam if (verbose) 10110296Ssam printf("Connected to %s.\n", hp->h_name); 10210296Ssam (void) getreply(0); /* read startup message from server */ 10310296Ssam return (hp); 10410296Ssam bad: 10510296Ssam close(s); 10610296Ssam return ((struct hostent *)0); 10710296Ssam } 10810296Ssam 10910296Ssam login(hp) 11010296Ssam struct hostent *hp; 11110296Ssam { 11210296Ssam char acct[80]; 11310296Ssam char *user, *pass; 11410296Ssam int n; 11510296Ssam 11610296Ssam user = pass = 0; 11710296Ssam ruserpass(hp->h_name, &user, &pass); 11810296Ssam n = command("USER %s", user); 11910296Ssam if (n == CONTINUE) 12010296Ssam n = command("PASS %s", pass); 12110296Ssam if (n == CONTINUE) { 12210296Ssam printf("Account: "); (void) fflush(stdout); 12310296Ssam (void) fgets(acct, sizeof(acct) - 1, stdin); 12410296Ssam acct[strlen(acct) - 1] = '\0'; 12510296Ssam n = command("ACCT %s", acct); 12610296Ssam } 12710296Ssam if (n != COMPLETE) { 12810296Ssam fprintf(stderr, "Login failed.\n"); 12910296Ssam return (0); 13010296Ssam } 13110296Ssam return (1); 13210296Ssam } 13310296Ssam 13410296Ssam /*VARARGS 1*/ 13510296Ssam command(fmt, args) 13610296Ssam char *fmt; 13710296Ssam { 13810296Ssam 13910296Ssam if (debug) { 14010296Ssam printf("---> "); 14110296Ssam _doprnt(fmt, &args, stdout); 14210296Ssam printf("\n"); 14310296Ssam (void) fflush(stdout); 14410296Ssam } 14511219Ssam if (cout == NULL) { 14611219Ssam perror ("No control connection for command"); 14711219Ssam return (0); 14811219Ssam } 14910296Ssam _doprnt(fmt, &args, cout); 15010296Ssam fprintf(cout, "\r\n"); 15110296Ssam (void) fflush(cout); 15210296Ssam return (getreply(!strcmp(fmt, "QUIT"))); 15310296Ssam } 15410296Ssam 15510296Ssam #include <ctype.h> 15610296Ssam 15710296Ssam getreply(expecteof) 15810296Ssam int expecteof; 15910296Ssam { 16011219Ssam register int c, n; 16110296Ssam register int code, dig; 16210296Ssam int originalcode = 0, continuation = 0; 16310296Ssam 16410296Ssam for (;;) { 16510296Ssam dig = n = code = 0; 16610296Ssam while ((c = getc(cin)) != '\n') { 16710296Ssam dig++; 16810296Ssam if (c == EOF) { 16910296Ssam if (expecteof) 17010296Ssam return (0); 17110296Ssam lostpeer(); 17210296Ssam exit(1); 17310296Ssam } 17410296Ssam if (verbose && c != '\r' || 17510296Ssam (n == '5' && dig > 4)) 17610296Ssam putchar(c); 17710296Ssam if (dig < 4 && isdigit(c)) 17810296Ssam code = code * 10 + (c - '0'); 17910296Ssam if (dig == 4 && c == '-') 18010296Ssam continuation++; 18110296Ssam if (n == 0) 18210296Ssam n = c; 18310296Ssam } 18411346Ssam if (verbose || n == '5') { 18510296Ssam putchar(c); 18611346Ssam (void) fflush (stdout); 18711346Ssam } 18810296Ssam if (continuation && code != originalcode) { 18910296Ssam if (originalcode == 0) 19010296Ssam originalcode = code; 19110296Ssam continue; 19210296Ssam } 19311219Ssam if (expecteof || empty(cin)) 19410296Ssam return (n - '0'); 19510296Ssam } 19610296Ssam } 19710296Ssam 19810296Ssam empty(f) 19910296Ssam FILE *f; 20010296Ssam { 20111651Ssam long mask; 20210296Ssam struct timeval t; 20310296Ssam 20410296Ssam if (f->_cnt > 0) 20510296Ssam return (0); 20610296Ssam mask = (1 << fileno(f)); 20710296Ssam t.tv_sec = t.tv_usec = 0; 20810296Ssam (void) select(20, &mask, 0, 0, &t); 20910296Ssam return (mask == 0); 21010296Ssam } 21110296Ssam 21210296Ssam jmp_buf sendabort; 21310296Ssam 21410296Ssam abortsend() 21510296Ssam { 21610296Ssam 21710296Ssam longjmp(sendabort, 1); 21810296Ssam } 21910296Ssam 22010296Ssam sendrequest(cmd, local, remote) 22110296Ssam char *cmd, *local, *remote; 22210296Ssam { 22310296Ssam FILE *fin, *dout, *popen(); 22410296Ssam int (*closefunc)(), pclose(), fclose(), (*oldintr)(); 22511219Ssam char buf[BUFSIZ]; 22611651Ssam long bytes = 0, hashbytes = sizeof (buf); 22711346Ssam register int c, d; 22810296Ssam struct stat st; 22910296Ssam struct timeval start, stop; 23010296Ssam 23110296Ssam closefunc = NULL; 23210296Ssam if (setjmp(sendabort)) 23310296Ssam goto bad; 23410296Ssam oldintr = signal(SIGINT, abortsend); 23510296Ssam if (strcmp(local, "-") == 0) 23610296Ssam fin = stdin; 23710296Ssam else if (*local == '|') { 23810296Ssam fin = popen(local + 1, "r"); 23910296Ssam if (fin == NULL) { 24010296Ssam perror(local + 1); 24110296Ssam goto bad; 24210296Ssam } 24310296Ssam closefunc = pclose; 24410296Ssam } else { 24510296Ssam fin = fopen(local, "r"); 24610296Ssam if (fin == NULL) { 24710296Ssam perror(local); 24810296Ssam goto bad; 24910296Ssam } 25010296Ssam closefunc = fclose; 25110296Ssam if (fstat(fileno(fin), &st) < 0 || 25210296Ssam (st.st_mode&S_IFMT) != S_IFREG) { 25317949Sralph fprintf(stderr, "%s: not a plain file.\n", local); 25410296Ssam goto bad; 25510296Ssam } 25610296Ssam } 25710296Ssam if (initconn()) 25810296Ssam goto bad; 25910296Ssam if (remote) { 26010296Ssam if (command("%s %s", cmd, remote) != PRELIM) 26110296Ssam goto bad; 26210296Ssam } else 26310296Ssam if (command("%s", cmd) != PRELIM) 26410296Ssam goto bad; 26510296Ssam dout = dataconn("w"); 26610296Ssam if (dout == NULL) 26710296Ssam goto bad; 26810296Ssam gettimeofday(&start, (struct timezone *)0); 26911219Ssam switch (type) { 27011219Ssam 27111219Ssam case TYPE_I: 27211219Ssam case TYPE_L: 27311346Ssam errno = d = 0; 27411219Ssam while ((c = read(fileno (fin), buf, sizeof (buf))) > 0) { 27511346Ssam if ((d = write(fileno (dout), buf, c)) < 0) 27611219Ssam break; 27711219Ssam bytes += c; 27811651Ssam if (hash) { 27911651Ssam putchar('#'); 28011651Ssam fflush(stdout); 28111651Ssam } 28211219Ssam } 28313213Ssam if (hash && bytes > 0) { 28411651Ssam putchar('\n'); 28511651Ssam fflush(stdout); 28611651Ssam } 28711219Ssam if (c < 0) 28811219Ssam perror(local); 28911346Ssam if (d < 0) 29011219Ssam perror("netout"); 29111219Ssam break; 29211219Ssam 29311219Ssam case TYPE_A: 29411219Ssam while ((c = getc(fin)) != EOF) { 29511219Ssam if (c == '\n') { 29611651Ssam while (hash && (bytes >= hashbytes)) { 29711651Ssam putchar('#'); 29811651Ssam fflush(stdout); 29911651Ssam hashbytes += sizeof (buf); 30011651Ssam } 30111219Ssam if (ferror(dout)) 30211219Ssam break; 30311219Ssam putc('\r', dout); 30411219Ssam bytes++; 30511219Ssam } 30611219Ssam putc(c, dout); 30711219Ssam bytes++; 30811219Ssam if (c == '\r') { 30911219Ssam putc('\0', dout); 31011219Ssam bytes++; 31111219Ssam } 31211219Ssam } 31311651Ssam if (hash) { 31413213Ssam if (bytes < hashbytes) 31513213Ssam putchar('#'); 31611651Ssam putchar('\n'); 31711651Ssam fflush(stdout); 31811651Ssam } 31911219Ssam if (ferror(fin)) 32011219Ssam perror(local); 32111346Ssam if (ferror(dout)) 32211219Ssam perror("netout"); 32311219Ssam break; 32410296Ssam } 32510296Ssam gettimeofday(&stop, (struct timezone *)0); 32610296Ssam if (closefunc != NULL) 32710296Ssam (*closefunc)(fin); 32810296Ssam (void) fclose(dout); 32910296Ssam (void) getreply(0); 33010296Ssam done: 33110296Ssam signal(SIGINT, oldintr); 33210296Ssam if (bytes > 0 && verbose) 33310296Ssam ptransfer("sent", bytes, &start, &stop); 33410296Ssam return; 33510296Ssam bad: 33610296Ssam if (data >= 0) 33710296Ssam (void) close(data), data = -1; 33810296Ssam if (closefunc != NULL && fin != NULL) 33910296Ssam (*closefunc)(fin); 34010296Ssam goto done; 34110296Ssam } 34210296Ssam 34310296Ssam jmp_buf recvabort; 34410296Ssam 34510296Ssam abortrecv() 34610296Ssam { 34710296Ssam 34810296Ssam longjmp(recvabort, 1); 34910296Ssam } 35010296Ssam 35111651Ssam recvrequest(cmd, local, remote, mode) 35211651Ssam char *cmd, *local, *remote, *mode; 35310296Ssam { 35410296Ssam FILE *fout, *din, *popen(); 35511651Ssam int (*closefunc)(), pclose(), fclose(), (*oldintr)(); 35611219Ssam char buf[BUFSIZ]; 35711651Ssam long bytes = 0, hashbytes = sizeof (buf); 35811346Ssam register int c, d; 35910296Ssam struct timeval start, stop; 36010296Ssam 36110296Ssam closefunc = NULL; 36210296Ssam if (setjmp(recvabort)) 36310296Ssam goto bad; 36410296Ssam oldintr = signal(SIGINT, abortrecv); 36510296Ssam if (strcmp(local, "-") && *local != '|') 36610296Ssam if (access(local, 2) < 0) { 36710296Ssam char *dir = rindex(local, '/'); 36810296Ssam 36910296Ssam if (dir != NULL) 37010296Ssam *dir = 0; 37117305Sralph d = access(dir ? local : ".", 2); 37217305Sralph if (dir != NULL) 37317305Sralph *dir = '/'; 37417305Sralph if (d < 0) { 37510296Ssam perror(local); 37610296Ssam goto bad; 37710296Ssam } 37810296Ssam } 37910296Ssam if (initconn()) 38010296Ssam goto bad; 38110296Ssam if (remote) { 38210296Ssam if (command("%s %s", cmd, remote) != PRELIM) 38310296Ssam goto bad; 38410296Ssam } else 38510296Ssam if (command("%s", cmd) != PRELIM) 38610296Ssam goto bad; 38710296Ssam if (strcmp(local, "-") == 0) 38810296Ssam fout = stdout; 38910296Ssam else if (*local == '|') { 39010296Ssam fout = popen(local + 1, "w"); 39110296Ssam closefunc = pclose; 39210296Ssam } else { 39311651Ssam fout = fopen(local, mode); 39410296Ssam closefunc = fclose; 39510296Ssam } 39610296Ssam if (fout == NULL) { 39710296Ssam perror(local + 1); 39810296Ssam goto bad; 39910296Ssam } 40010296Ssam din = dataconn("r"); 40110296Ssam if (din == NULL) 40210296Ssam goto bad; 40310296Ssam gettimeofday(&start, (struct timezone *)0); 40411219Ssam switch (type) { 40511219Ssam 40611219Ssam case TYPE_I: 40711219Ssam case TYPE_L: 40811346Ssam errno = d = 0; 40911219Ssam while ((c = read(fileno(din), buf, sizeof (buf))) > 0) { 41011346Ssam if ((d = write(fileno(fout), buf, c)) < 0) 41111219Ssam break; 41211219Ssam bytes += c; 41311651Ssam if (hash) { 41411651Ssam putchar('#'); 41511651Ssam fflush(stdout); 41611651Ssam } 41711219Ssam } 41813213Ssam if (hash && bytes > 0) { 41911651Ssam putchar('\n'); 42011651Ssam fflush(stdout); 42111651Ssam } 42211219Ssam if (c < 0) 42311219Ssam perror("netin"); 42411346Ssam if (d < 0) 42510296Ssam perror(local); 42611219Ssam break; 42711219Ssam 42811219Ssam case TYPE_A: 42911219Ssam while ((c = getc(din)) != EOF) { 43011219Ssam if (c == '\r') { 43111651Ssam while (hash && (bytes >= hashbytes)) { 43211651Ssam putchar('#'); 43311651Ssam fflush(stdout); 43411651Ssam hashbytes += sizeof (buf); 43511651Ssam } 43610296Ssam bytes++; 43711219Ssam if ((c = getc(din)) != '\n') { 43811219Ssam if (ferror (fout)) 43911219Ssam break; 44011219Ssam putc ('\r', fout); 44111219Ssam } 44211219Ssam if (c == '\0') { 44311219Ssam bytes++; 44411219Ssam continue; 44511219Ssam } 44611219Ssam } 44711219Ssam putc (c, fout); 44811219Ssam bytes++; 44910296Ssam } 45011651Ssam if (hash) { 45113213Ssam if (bytes < hashbytes) 45213213Ssam putchar('#'); 45311651Ssam putchar('\n'); 45411651Ssam fflush(stdout); 45511651Ssam } 45611219Ssam if (ferror (din)) 45711219Ssam perror ("netin"); 45811219Ssam if (ferror (fout)) 45911219Ssam perror (local); 46011219Ssam break; 46110296Ssam } 46210296Ssam gettimeofday(&stop, (struct timezone *)0); 46310296Ssam (void) fclose(din); 46410296Ssam if (closefunc != NULL) 46510296Ssam (*closefunc)(fout); 46610296Ssam (void) getreply(0); 46710296Ssam done: 46810296Ssam signal(SIGINT, oldintr); 46910296Ssam if (bytes > 0 && verbose) 47010296Ssam ptransfer("received", bytes, &start, &stop); 47110296Ssam return; 47210296Ssam bad: 47310296Ssam if (data >= 0) 47410296Ssam (void) close(data), data = -1; 47510296Ssam if (closefunc != NULL && fout != NULL) 47610296Ssam (*closefunc)(fout); 47710296Ssam goto done; 47810296Ssam } 47910296Ssam 48010296Ssam /* 48110296Ssam * Need to start a listen on the data channel 48210296Ssam * before we send the command, otherwise the 48310296Ssam * server's connect may fail. 48410296Ssam */ 48511651Ssam static int sendport = -1; 48611651Ssam 48710296Ssam initconn() 48810296Ssam { 48910296Ssam register char *p, *a; 49011627Ssam int result, len; 49117450Slepreau int on = 1; 49210296Ssam 49311651Ssam noport: 49410296Ssam data_addr = myctladdr; 49511651Ssam if (sendport) 49611651Ssam data_addr.sin_port = 0; /* let system pick one */ 49711651Ssam if (data != -1) 49811651Ssam (void) close (data); 49918287Sralph data = socket(AF_INET, SOCK_STREAM, 0); 50010296Ssam if (data < 0) { 50110296Ssam perror("ftp: socket"); 50210296Ssam return (1); 50310296Ssam } 50412397Ssam if (!sendport) 50517450Slepreau if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) { 50612397Ssam perror("ftp: setsockopt (resuse address)"); 50712397Ssam goto bad; 50812397Ssam } 50910296Ssam if (bind(data, (char *)&data_addr, sizeof (data_addr), 0) < 0) { 51010296Ssam perror("ftp: bind"); 51110296Ssam goto bad; 51210296Ssam } 51310296Ssam if (options & SO_DEBUG && 51417450Slepreau setsockopt(data, SOL_SOCKET, SO_DEBUG, &on, sizeof (on)) < 0) 51510296Ssam perror("ftp: setsockopt (ignored)"); 51611627Ssam len = sizeof (data_addr); 51711627Ssam if (getsockname(data, (char *)&data_addr, &len) < 0) { 51811627Ssam perror("ftp: getsockname"); 51910296Ssam goto bad; 52010296Ssam } 52110296Ssam if (listen(data, 1) < 0) { 52210296Ssam perror("ftp: listen"); 52310296Ssam goto bad; 52410296Ssam } 52511651Ssam if (sendport) { 52611651Ssam a = (char *)&data_addr.sin_addr; 52711651Ssam p = (char *)&data_addr.sin_port; 52810296Ssam #define UC(b) (((int)b)&0xff) 52911651Ssam result = 53011651Ssam command("PORT %d,%d,%d,%d,%d,%d", 53111651Ssam UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 53211651Ssam UC(p[0]), UC(p[1])); 53311651Ssam if (result == ERROR && sendport == -1) { 53411651Ssam sendport = 0; 53511651Ssam goto noport; 53611651Ssam } 53711651Ssam return (result != COMPLETE); 53811651Ssam } 53911651Ssam return (0); 54010296Ssam bad: 54110296Ssam (void) close(data), data = -1; 54210296Ssam return (1); 54310296Ssam } 54410296Ssam 54510296Ssam FILE * 54610296Ssam dataconn(mode) 54710296Ssam char *mode; 54810296Ssam { 54910296Ssam struct sockaddr_in from; 55010296Ssam int s, fromlen = sizeof (from); 55110296Ssam 55210296Ssam s = accept(data, &from, &fromlen, 0); 55310296Ssam if (s < 0) { 55410296Ssam perror("ftp: accept"); 55510296Ssam (void) close(data), data = -1; 55610296Ssam return (NULL); 55710296Ssam } 55810296Ssam (void) close(data); 55910296Ssam data = s; 56010296Ssam return (fdopen(data, mode)); 56110296Ssam } 56210296Ssam 56310296Ssam ptransfer(direction, bytes, t0, t1) 56410296Ssam char *direction; 56511651Ssam long bytes; 56610296Ssam struct timeval *t0, *t1; 56710296Ssam { 56810296Ssam struct timeval td; 56916437Sleres float s, bs; 57010296Ssam 57110296Ssam tvsub(&td, t1, t0); 57216437Sleres s = td.tv_sec + (td.tv_usec / 1000000.); 57310296Ssam #define nz(x) ((x) == 0 ? 1 : (x)) 57416437Sleres bs = bytes / nz(s); 57516437Sleres printf("%ld bytes %s in %.2g seconds (%.2g Kbytes/s)\n", 57616437Sleres bytes, direction, s, bs / 1024.); 57710296Ssam } 57810296Ssam 57910296Ssam tvadd(tsum, t0) 58010296Ssam struct timeval *tsum, *t0; 58110296Ssam { 58210296Ssam 58310296Ssam tsum->tv_sec += t0->tv_sec; 58410296Ssam tsum->tv_usec += t0->tv_usec; 58510296Ssam if (tsum->tv_usec > 1000000) 58610296Ssam tsum->tv_sec++, tsum->tv_usec -= 1000000; 58710296Ssam } 58810296Ssam 58910296Ssam tvsub(tdiff, t1, t0) 59010296Ssam struct timeval *tdiff, *t1, *t0; 59110296Ssam { 59210296Ssam 59310296Ssam tdiff->tv_sec = t1->tv_sec - t0->tv_sec; 59410296Ssam tdiff->tv_usec = t1->tv_usec - t0->tv_usec; 59510296Ssam if (tdiff->tv_usec < 0) 59610296Ssam tdiff->tv_sec--, tdiff->tv_usec += 1000000; 59710296Ssam } 598