110296Ssam #ifndef lint 2*13614Ssam static char sccsid[] = "@(#)ftp.c 4.10 (Berkeley) 07/02/83"; 310296Ssam #endif 410296Ssam 510296Ssam #include <sys/param.h> 610296Ssam #include <sys/stat.h> 710296Ssam #include <sys/ioctl.h> 810296Ssam #include <sys/socket.h> 9*13614Ssam #include <sys/time.h> 1010296Ssam 1110296Ssam #include <netinet/in.h> 1212397Ssam #include <arpa/ftp.h> 1310296Ssam 1410296Ssam #include <stdio.h> 1510296Ssam #include <signal.h> 1610296Ssam #include <errno.h> 1710296Ssam #include <netdb.h> 1810296Ssam 1910296Ssam #include "ftp_var.h" 2010296Ssam 2110296Ssam struct sockaddr_in hisctladdr; 2210296Ssam struct sockaddr_in data_addr; 2310296Ssam int data = -1; 2410296Ssam int connected; 2510296Ssam struct sockaddr_in myctladdr; 2610296Ssam 2710296Ssam FILE *cin, *cout; 2810296Ssam FILE *dataconn(); 2910296Ssam 3010296Ssam struct hostent * 3110296Ssam hookup(host, port) 3210296Ssam char *host; 3310296Ssam int port; 3410296Ssam { 3510296Ssam register struct hostent *hp; 3611627Ssam int s, len; 3710296Ssam 3810296Ssam bzero((char *)&hisctladdr, sizeof (hisctladdr)); 3910296Ssam hp = gethostbyname(host); 4011283Ssam if (hp == NULL) { 4110296Ssam static struct hostent def; 4210296Ssam static struct in_addr defaddr; 4310296Ssam static char namebuf[128]; 4410296Ssam int inet_addr(); 4510296Ssam 4610296Ssam defaddr.s_addr = inet_addr(host); 4710296Ssam if (defaddr.s_addr == -1) { 4810296Ssam fprintf(stderr, "%s: Unknown host.\n", host); 4910296Ssam return (0); 5010296Ssam } 5110296Ssam strcpy(namebuf, host); 5210296Ssam def.h_name = namebuf; 5310296Ssam hostname = namebuf; 5410296Ssam def.h_addr = (char *)&defaddr; 5510296Ssam def.h_length = sizeof (struct in_addr); 5610296Ssam def.h_addrtype = AF_INET; 5710296Ssam def.h_aliases = 0; 5810296Ssam hp = &def; 5910296Ssam } 6011283Ssam hostname = hp->h_name; 6111283Ssam hisctladdr.sin_family = hp->h_addrtype; 6210296Ssam s = socket(hp->h_addrtype, SOCK_STREAM, 0, 0); 6310296Ssam if (s < 0) { 6410296Ssam perror("ftp: socket"); 6510296Ssam return (0); 6610296Ssam } 6710296Ssam if (bind(s, (char *)&hisctladdr, sizeof (hisctladdr), 0) < 0) { 6810296Ssam perror("ftp: bind"); 6910296Ssam goto bad; 7010296Ssam } 7110296Ssam bcopy(hp->h_addr, (char *)&hisctladdr.sin_addr, hp->h_length); 7210296Ssam hisctladdr.sin_port = port; 7310296Ssam if (connect(s, (char *)&hisctladdr, sizeof (hisctladdr), 0) < 0) { 7410296Ssam perror("ftp: connect"); 7510296Ssam goto bad; 7610296Ssam } 7711627Ssam len = sizeof (myctladdr); 7811627Ssam if (getsockname(s, (char *)&myctladdr, &len) < 0) { 7911627Ssam perror("ftp: getsockname"); 8010296Ssam goto bad; 8110296Ssam } 8210296Ssam cin = fdopen(s, "r"); 8310296Ssam cout = fdopen(s, "w"); 8411219Ssam if (cin == NULL || cout == NULL) { 8510296Ssam fprintf(stderr, "ftp: fdopen failed.\n"); 8610296Ssam if (cin) 8710296Ssam fclose(cin); 8810296Ssam if (cout) 8910296Ssam fclose(cout); 9010296Ssam goto bad; 9110296Ssam } 9210296Ssam if (verbose) 9310296Ssam printf("Connected to %s.\n", hp->h_name); 9410296Ssam (void) getreply(0); /* read startup message from server */ 9510296Ssam return (hp); 9610296Ssam bad: 9710296Ssam close(s); 9810296Ssam return ((struct hostent *)0); 9910296Ssam } 10010296Ssam 10110296Ssam login(hp) 10210296Ssam struct hostent *hp; 10310296Ssam { 10410296Ssam char acct[80]; 10510296Ssam char *user, *pass; 10610296Ssam int n; 10710296Ssam 10810296Ssam user = pass = 0; 10910296Ssam ruserpass(hp->h_name, &user, &pass); 11010296Ssam n = command("USER %s", user); 11110296Ssam if (n == CONTINUE) 11210296Ssam n = command("PASS %s", pass); 11310296Ssam if (n == CONTINUE) { 11410296Ssam printf("Account: "); (void) fflush(stdout); 11510296Ssam (void) fgets(acct, sizeof(acct) - 1, stdin); 11610296Ssam acct[strlen(acct) - 1] = '\0'; 11710296Ssam n = command("ACCT %s", acct); 11810296Ssam } 11910296Ssam if (n != COMPLETE) { 12010296Ssam fprintf(stderr, "Login failed.\n"); 12110296Ssam return (0); 12210296Ssam } 12310296Ssam return (1); 12410296Ssam } 12510296Ssam 12610296Ssam /*VARARGS 1*/ 12710296Ssam command(fmt, args) 12810296Ssam char *fmt; 12910296Ssam { 13010296Ssam 13110296Ssam if (debug) { 13210296Ssam printf("---> "); 13310296Ssam _doprnt(fmt, &args, stdout); 13410296Ssam printf("\n"); 13510296Ssam (void) fflush(stdout); 13610296Ssam } 13711219Ssam if (cout == NULL) { 13811219Ssam perror ("No control connection for command"); 13911219Ssam return (0); 14011219Ssam } 14110296Ssam _doprnt(fmt, &args, cout); 14210296Ssam fprintf(cout, "\r\n"); 14310296Ssam (void) fflush(cout); 14410296Ssam return (getreply(!strcmp(fmt, "QUIT"))); 14510296Ssam } 14610296Ssam 14710296Ssam #include <ctype.h> 14810296Ssam 14910296Ssam getreply(expecteof) 15010296Ssam int expecteof; 15110296Ssam { 15211219Ssam register int c, n; 15310296Ssam register int code, dig; 15410296Ssam int originalcode = 0, continuation = 0; 15510296Ssam 15610296Ssam for (;;) { 15710296Ssam dig = n = code = 0; 15810296Ssam while ((c = getc(cin)) != '\n') { 15910296Ssam dig++; 16010296Ssam if (c == EOF) { 16110296Ssam if (expecteof) 16210296Ssam return (0); 16310296Ssam lostpeer(); 16410296Ssam exit(1); 16510296Ssam } 16610296Ssam if (verbose && c != '\r' || 16710296Ssam (n == '5' && dig > 4)) 16810296Ssam putchar(c); 16910296Ssam if (dig < 4 && isdigit(c)) 17010296Ssam code = code * 10 + (c - '0'); 17110296Ssam if (dig == 4 && c == '-') 17210296Ssam continuation++; 17310296Ssam if (n == 0) 17410296Ssam n = c; 17510296Ssam } 17611346Ssam if (verbose || n == '5') { 17710296Ssam putchar(c); 17811346Ssam (void) fflush (stdout); 17911346Ssam } 18010296Ssam if (continuation && code != originalcode) { 18110296Ssam if (originalcode == 0) 18210296Ssam originalcode = code; 18310296Ssam continue; 18410296Ssam } 18511219Ssam if (expecteof || empty(cin)) 18610296Ssam return (n - '0'); 18710296Ssam } 18810296Ssam } 18910296Ssam 19010296Ssam empty(f) 19110296Ssam FILE *f; 19210296Ssam { 19311651Ssam long mask; 19410296Ssam struct timeval t; 19510296Ssam 19610296Ssam if (f->_cnt > 0) 19710296Ssam return (0); 19810296Ssam mask = (1 << fileno(f)); 19910296Ssam t.tv_sec = t.tv_usec = 0; 20010296Ssam (void) select(20, &mask, 0, 0, &t); 20110296Ssam return (mask == 0); 20210296Ssam } 20310296Ssam 20410296Ssam jmp_buf sendabort; 20510296Ssam 20610296Ssam abortsend() 20710296Ssam { 20810296Ssam 20910296Ssam longjmp(sendabort, 1); 21010296Ssam } 21110296Ssam 21210296Ssam sendrequest(cmd, local, remote) 21310296Ssam char *cmd, *local, *remote; 21410296Ssam { 21510296Ssam FILE *fin, *dout, *popen(); 21610296Ssam int (*closefunc)(), pclose(), fclose(), (*oldintr)(); 21711219Ssam char buf[BUFSIZ]; 21811651Ssam long bytes = 0, hashbytes = sizeof (buf); 21911346Ssam register int c, d; 22010296Ssam struct stat st; 22110296Ssam struct timeval start, stop; 22210296Ssam 22310296Ssam closefunc = NULL; 22410296Ssam if (setjmp(sendabort)) 22510296Ssam goto bad; 22610296Ssam oldintr = signal(SIGINT, abortsend); 22710296Ssam if (strcmp(local, "-") == 0) 22810296Ssam fin = stdin; 22910296Ssam else if (*local == '|') { 23010296Ssam fin = popen(local + 1, "r"); 23110296Ssam if (fin == NULL) { 23210296Ssam perror(local + 1); 23310296Ssam goto bad; 23410296Ssam } 23510296Ssam closefunc = pclose; 23610296Ssam } else { 23710296Ssam fin = fopen(local, "r"); 23810296Ssam if (fin == NULL) { 23910296Ssam perror(local); 24010296Ssam goto bad; 24110296Ssam } 24210296Ssam closefunc = fclose; 24310296Ssam if (fstat(fileno(fin), &st) < 0 || 24410296Ssam (st.st_mode&S_IFMT) != S_IFREG) { 24510296Ssam fprintf(stderr, "%s: not a plain file.", local); 24610296Ssam goto bad; 24710296Ssam } 24810296Ssam } 24910296Ssam if (initconn()) 25010296Ssam goto bad; 25110296Ssam if (remote) { 25210296Ssam if (command("%s %s", cmd, remote) != PRELIM) 25310296Ssam goto bad; 25410296Ssam } else 25510296Ssam if (command("%s", cmd) != PRELIM) 25610296Ssam goto bad; 25710296Ssam dout = dataconn("w"); 25810296Ssam if (dout == NULL) 25910296Ssam goto bad; 26010296Ssam gettimeofday(&start, (struct timezone *)0); 26111219Ssam switch (type) { 26211219Ssam 26311219Ssam case TYPE_I: 26411219Ssam case TYPE_L: 26511346Ssam errno = d = 0; 26611219Ssam while ((c = read(fileno (fin), buf, sizeof (buf))) > 0) { 26711346Ssam if ((d = write(fileno (dout), buf, c)) < 0) 26811219Ssam break; 26911219Ssam bytes += c; 27011651Ssam if (hash) { 27111651Ssam putchar('#'); 27211651Ssam fflush(stdout); 27311651Ssam } 27411219Ssam } 27513213Ssam if (hash && bytes > 0) { 27611651Ssam putchar('\n'); 27711651Ssam fflush(stdout); 27811651Ssam } 27911219Ssam if (c < 0) 28011219Ssam perror(local); 28111346Ssam if (d < 0) 28211219Ssam perror("netout"); 28311219Ssam break; 28411219Ssam 28511219Ssam case TYPE_A: 28611219Ssam while ((c = getc(fin)) != EOF) { 28711219Ssam if (c == '\n') { 28811651Ssam while (hash && (bytes >= hashbytes)) { 28911651Ssam putchar('#'); 29011651Ssam fflush(stdout); 29111651Ssam hashbytes += sizeof (buf); 29211651Ssam } 29311219Ssam if (ferror(dout)) 29411219Ssam break; 29511219Ssam putc('\r', dout); 29611219Ssam bytes++; 29711219Ssam } 29811219Ssam putc(c, dout); 29911219Ssam bytes++; 30011219Ssam if (c == '\r') { 30111219Ssam putc('\0', dout); 30211219Ssam bytes++; 30311219Ssam } 30411219Ssam } 30511651Ssam if (hash) { 30613213Ssam if (bytes < hashbytes) 30713213Ssam putchar('#'); 30811651Ssam putchar('\n'); 30911651Ssam fflush(stdout); 31011651Ssam } 31111219Ssam if (ferror(fin)) 31211219Ssam perror(local); 31311346Ssam if (ferror(dout)) 31411219Ssam perror("netout"); 31511219Ssam break; 31610296Ssam } 31710296Ssam gettimeofday(&stop, (struct timezone *)0); 31810296Ssam if (closefunc != NULL) 31910296Ssam (*closefunc)(fin); 32010296Ssam (void) fclose(dout); 32110296Ssam (void) getreply(0); 32210296Ssam done: 32310296Ssam signal(SIGINT, oldintr); 32410296Ssam if (bytes > 0 && verbose) 32510296Ssam ptransfer("sent", bytes, &start, &stop); 32610296Ssam return; 32710296Ssam bad: 32810296Ssam if (data >= 0) 32910296Ssam (void) close(data), data = -1; 33010296Ssam if (closefunc != NULL && fin != NULL) 33110296Ssam (*closefunc)(fin); 33210296Ssam goto done; 33310296Ssam } 33410296Ssam 33510296Ssam jmp_buf recvabort; 33610296Ssam 33710296Ssam abortrecv() 33810296Ssam { 33910296Ssam 34010296Ssam longjmp(recvabort, 1); 34110296Ssam } 34210296Ssam 34311651Ssam recvrequest(cmd, local, remote, mode) 34411651Ssam char *cmd, *local, *remote, *mode; 34510296Ssam { 34610296Ssam FILE *fout, *din, *popen(); 34711651Ssam int (*closefunc)(), pclose(), fclose(), (*oldintr)(); 34811219Ssam char buf[BUFSIZ]; 34911651Ssam long bytes = 0, hashbytes = sizeof (buf); 35011346Ssam register int c, d; 35110296Ssam struct timeval start, stop; 35210296Ssam 35310296Ssam closefunc = NULL; 35410296Ssam if (setjmp(recvabort)) 35510296Ssam goto bad; 35610296Ssam oldintr = signal(SIGINT, abortrecv); 35710296Ssam if (strcmp(local, "-") && *local != '|') 35810296Ssam if (access(local, 2) < 0) { 35910296Ssam char *dir = rindex(local, '/'); 36010296Ssam 36110296Ssam if (dir != NULL) 36210296Ssam *dir = 0; 36310296Ssam if (access(dir ? dir : ".", 2) < 0) { 36410296Ssam perror(local); 36510296Ssam goto bad; 36610296Ssam } 36710296Ssam if (dir != NULL) 36810296Ssam *dir = '/'; 36910296Ssam } 37010296Ssam if (initconn()) 37110296Ssam goto bad; 37210296Ssam if (remote) { 37310296Ssam if (command("%s %s", cmd, remote) != PRELIM) 37410296Ssam goto bad; 37510296Ssam } else 37610296Ssam if (command("%s", cmd) != PRELIM) 37710296Ssam goto bad; 37810296Ssam if (strcmp(local, "-") == 0) 37910296Ssam fout = stdout; 38010296Ssam else if (*local == '|') { 38110296Ssam fout = popen(local + 1, "w"); 38210296Ssam closefunc = pclose; 38310296Ssam } else { 38411651Ssam fout = fopen(local, mode); 38510296Ssam closefunc = fclose; 38610296Ssam } 38710296Ssam if (fout == NULL) { 38810296Ssam perror(local + 1); 38910296Ssam goto bad; 39010296Ssam } 39110296Ssam din = dataconn("r"); 39210296Ssam if (din == NULL) 39310296Ssam goto bad; 39410296Ssam gettimeofday(&start, (struct timezone *)0); 39511219Ssam switch (type) { 39611219Ssam 39711219Ssam case TYPE_I: 39811219Ssam case TYPE_L: 39911346Ssam errno = d = 0; 40011219Ssam while ((c = read(fileno(din), buf, sizeof (buf))) > 0) { 40111346Ssam if ((d = write(fileno(fout), buf, c)) < 0) 40211219Ssam break; 40311219Ssam bytes += c; 40411651Ssam if (hash) { 40511651Ssam putchar('#'); 40611651Ssam fflush(stdout); 40711651Ssam } 40811219Ssam } 40913213Ssam if (hash && bytes > 0) { 41011651Ssam putchar('\n'); 41111651Ssam fflush(stdout); 41211651Ssam } 41311219Ssam if (c < 0) 41411219Ssam perror("netin"); 41511346Ssam if (d < 0) 41610296Ssam perror(local); 41711219Ssam break; 41811219Ssam 41911219Ssam case TYPE_A: 42011219Ssam while ((c = getc(din)) != EOF) { 42111219Ssam if (c == '\r') { 42211651Ssam while (hash && (bytes >= hashbytes)) { 42311651Ssam putchar('#'); 42411651Ssam fflush(stdout); 42511651Ssam hashbytes += sizeof (buf); 42611651Ssam } 42710296Ssam bytes++; 42811219Ssam if ((c = getc(din)) != '\n') { 42911219Ssam if (ferror (fout)) 43011219Ssam break; 43111219Ssam putc ('\r', fout); 43211219Ssam } 43311219Ssam if (c == '\0') { 43411219Ssam bytes++; 43511219Ssam continue; 43611219Ssam } 43711219Ssam } 43811219Ssam putc (c, fout); 43911219Ssam bytes++; 44010296Ssam } 44111651Ssam if (hash) { 44213213Ssam if (bytes < hashbytes) 44313213Ssam putchar('#'); 44411651Ssam putchar('\n'); 44511651Ssam fflush(stdout); 44611651Ssam } 44711219Ssam if (ferror (din)) 44811219Ssam perror ("netin"); 44911219Ssam if (ferror (fout)) 45011219Ssam perror (local); 45111219Ssam break; 45210296Ssam } 45310296Ssam gettimeofday(&stop, (struct timezone *)0); 45410296Ssam (void) fclose(din); 45510296Ssam if (closefunc != NULL) 45610296Ssam (*closefunc)(fout); 45710296Ssam (void) getreply(0); 45810296Ssam done: 45910296Ssam signal(SIGINT, oldintr); 46010296Ssam if (bytes > 0 && verbose) 46110296Ssam ptransfer("received", bytes, &start, &stop); 46210296Ssam return; 46310296Ssam bad: 46410296Ssam if (data >= 0) 46510296Ssam (void) close(data), data = -1; 46610296Ssam if (closefunc != NULL && fout != NULL) 46710296Ssam (*closefunc)(fout); 46810296Ssam goto done; 46910296Ssam } 47010296Ssam 47110296Ssam /* 47210296Ssam * Need to start a listen on the data channel 47310296Ssam * before we send the command, otherwise the 47410296Ssam * server's connect may fail. 47510296Ssam */ 47611651Ssam static int sendport = -1; 47711651Ssam 47810296Ssam initconn() 47910296Ssam { 48010296Ssam register char *p, *a; 48111627Ssam int result, len; 48210296Ssam 48311651Ssam noport: 48410296Ssam data_addr = myctladdr; 48511651Ssam if (sendport) 48611651Ssam data_addr.sin_port = 0; /* let system pick one */ 48711651Ssam if (data != -1) 48811651Ssam (void) close (data); 48910296Ssam data = socket(AF_INET, SOCK_STREAM, 0, 0); 49010296Ssam if (data < 0) { 49110296Ssam perror("ftp: socket"); 49210296Ssam return (1); 49310296Ssam } 49412397Ssam if (!sendport) 49512397Ssam if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0) { 49612397Ssam perror("ftp: setsockopt (resuse address)"); 49712397Ssam goto bad; 49812397Ssam } 49910296Ssam if (bind(data, (char *)&data_addr, sizeof (data_addr), 0) < 0) { 50010296Ssam perror("ftp: bind"); 50110296Ssam goto bad; 50210296Ssam } 50310296Ssam if (options & SO_DEBUG && 50410296Ssam setsockopt(data, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) 50510296Ssam perror("ftp: setsockopt (ignored)"); 50611627Ssam len = sizeof (data_addr); 50711627Ssam if (getsockname(data, (char *)&data_addr, &len) < 0) { 50811627Ssam perror("ftp: getsockname"); 50910296Ssam goto bad; 51010296Ssam } 51110296Ssam if (listen(data, 1) < 0) { 51210296Ssam perror("ftp: listen"); 51310296Ssam goto bad; 51410296Ssam } 51511651Ssam if (sendport) { 51611651Ssam a = (char *)&data_addr.sin_addr; 51711651Ssam p = (char *)&data_addr.sin_port; 51810296Ssam #define UC(b) (((int)b)&0xff) 51911651Ssam result = 52011651Ssam command("PORT %d,%d,%d,%d,%d,%d", 52111651Ssam UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 52211651Ssam UC(p[0]), UC(p[1])); 52311651Ssam if (result == ERROR && sendport == -1) { 52411651Ssam sendport = 0; 52511651Ssam goto noport; 52611651Ssam } 52711651Ssam return (result != COMPLETE); 52811651Ssam } 52911651Ssam return (0); 53010296Ssam bad: 53110296Ssam (void) close(data), data = -1; 53210296Ssam return (1); 53310296Ssam } 53410296Ssam 53510296Ssam FILE * 53610296Ssam dataconn(mode) 53710296Ssam char *mode; 53810296Ssam { 53910296Ssam struct sockaddr_in from; 54010296Ssam int s, fromlen = sizeof (from); 54110296Ssam 54210296Ssam s = accept(data, &from, &fromlen, 0); 54310296Ssam if (s < 0) { 54410296Ssam perror("ftp: accept"); 54510296Ssam (void) close(data), data = -1; 54610296Ssam return (NULL); 54710296Ssam } 54810296Ssam (void) close(data); 54910296Ssam data = s; 55013213Ssam if (*mode == 'w' && linger) 55113213Ssam (void) setsockopt(s, SOL_SOCKET, SO_LINGER, &linger, 55213213Ssam sizeof (linger)); 55310296Ssam return (fdopen(data, mode)); 55410296Ssam } 55510296Ssam 55610296Ssam ptransfer(direction, bytes, t0, t1) 55710296Ssam char *direction; 55811651Ssam long bytes; 55910296Ssam struct timeval *t0, *t1; 56010296Ssam { 56110296Ssam struct timeval td; 56211651Ssam long ms; 56311651Ssam float bs; 56410296Ssam 56510296Ssam tvsub(&td, t1, t0); 56610296Ssam ms = (td.tv_sec * 1000) + (td.tv_usec / 1000); 56710296Ssam #define nz(x) ((x) == 0 ? 1 : (x)) 56811651Ssam bs = ((bytes * NBBY * 1000) / (float) nz(ms)) / NBBY; 56911651Ssam printf("%ld bytes %s in %d.%02d seconds (%.2g Kbytes/s)\n", 57011651Ssam bytes, direction, td.tv_sec, td.tv_usec / 10000, bs / 1024.); 57110296Ssam } 57210296Ssam 57310296Ssam tvadd(tsum, t0) 57410296Ssam struct timeval *tsum, *t0; 57510296Ssam { 57610296Ssam 57710296Ssam tsum->tv_sec += t0->tv_sec; 57810296Ssam tsum->tv_usec += t0->tv_usec; 57910296Ssam if (tsum->tv_usec > 1000000) 58010296Ssam tsum->tv_sec++, tsum->tv_usec -= 1000000; 58110296Ssam } 58210296Ssam 58310296Ssam tvsub(tdiff, t1, t0) 58410296Ssam struct timeval *tdiff, *t1, *t0; 58510296Ssam { 58610296Ssam 58710296Ssam tdiff->tv_sec = t1->tv_sec - t0->tv_sec; 58810296Ssam tdiff->tv_usec = t1->tv_usec - t0->tv_usec; 58910296Ssam if (tdiff->tv_usec < 0) 59010296Ssam tdiff->tv_sec--, tdiff->tv_usec += 1000000; 59110296Ssam } 592