110296Ssam #ifndef lint 2*11219Ssam static char sccsid[] = "@(#)ftp.c 4.2 (Berkeley) 02/21/83"; 310296Ssam #endif 410296Ssam 510296Ssam #include <sys/param.h> 610296Ssam #include <sys/stat.h> 710296Ssam #include <sys/ioctl.h> 810296Ssam #include <sys/socket.h> 910296Ssam 1010296Ssam #include <netinet/in.h> 1110296Ssam 1210296Ssam #include <stdio.h> 1310296Ssam #include <signal.h> 1410296Ssam #include <time.h> 1510296Ssam #include <errno.h> 1610296Ssam #include <netdb.h> 1710296Ssam 1810296Ssam #include "ftp.h" 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; 3610296Ssam int s; 3710296Ssam 3810296Ssam bzero((char *)&hisctladdr, sizeof (hisctladdr)); 3910296Ssam hp = gethostbyname(host); 4010296Ssam if (hp) { 4110296Ssam hisctladdr.sin_family = hp->h_addrtype; 4210296Ssam hostname = hp->h_name; 4310296Ssam } else { 4410296Ssam static struct hostent def; 4510296Ssam static struct in_addr defaddr; 4610296Ssam static char namebuf[128]; 4710296Ssam int inet_addr(); 4810296Ssam 4910296Ssam defaddr.s_addr = inet_addr(host); 5010296Ssam if (defaddr.s_addr == -1) { 5110296Ssam fprintf(stderr, "%s: Unknown host.\n", host); 5210296Ssam return (0); 5310296Ssam } 5410296Ssam strcpy(namebuf, host); 5510296Ssam def.h_name = namebuf; 5610296Ssam hostname = namebuf; 5710296Ssam def.h_addr = (char *)&defaddr; 5810296Ssam def.h_length = sizeof (struct in_addr); 5910296Ssam def.h_addrtype = AF_INET; 6010296Ssam def.h_aliases = 0; 6110296Ssam hp = &def; 6210296Ssam } 6310296Ssam s = socket(hp->h_addrtype, SOCK_STREAM, 0, 0); 6410296Ssam if (s < 0) { 6510296Ssam perror("ftp: socket"); 6610296Ssam return (0); 6710296Ssam } 6810296Ssam if (bind(s, (char *)&hisctladdr, sizeof (hisctladdr), 0) < 0) { 6910296Ssam perror("ftp: bind"); 7010296Ssam goto bad; 7110296Ssam } 7210296Ssam bcopy(hp->h_addr, (char *)&hisctladdr.sin_addr, hp->h_length); 7310296Ssam hisctladdr.sin_port = port; 7410296Ssam if (connect(s, (char *)&hisctladdr, sizeof (hisctladdr), 0) < 0) { 7510296Ssam perror("ftp: connect"); 7610296Ssam goto bad; 7710296Ssam } 7810296Ssam if (socketaddr(s, &myctladdr) < 0) { 7910296Ssam perror("ftp: socketaddr"); 8010296Ssam goto bad; 8110296Ssam } 8210296Ssam cin = fdopen(s, "r"); 8310296Ssam cout = fdopen(s, "w"); 84*11219Ssam 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 } 137*11219Ssam if (cout == NULL) { 138*11219Ssam perror ("No control connection for command"); 139*11219Ssam return (0); 140*11219Ssam } 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 { 152*11219Ssam 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 } 17610296Ssam if (verbose || n == '5') 17710296Ssam putchar(c); 17810296Ssam if (continuation && code != originalcode) { 17910296Ssam if (originalcode == 0) 18010296Ssam originalcode = code; 18110296Ssam continue; 18210296Ssam } 183*11219Ssam if (expecteof || empty(cin)) 18410296Ssam return (n - '0'); 18510296Ssam } 18610296Ssam } 18710296Ssam 18810296Ssam empty(f) 18910296Ssam FILE *f; 19010296Ssam { 19110296Ssam int mask; 19210296Ssam struct timeval t; 19310296Ssam 19410296Ssam if (f->_cnt > 0) 19510296Ssam return (0); 19610296Ssam mask = (1 << fileno(f)); 19710296Ssam t.tv_sec = t.tv_usec = 0; 19810296Ssam (void) select(20, &mask, 0, 0, &t); 19910296Ssam return (mask == 0); 20010296Ssam } 20110296Ssam 20210296Ssam jmp_buf sendabort; 20310296Ssam 20410296Ssam abortsend() 20510296Ssam { 20610296Ssam 20710296Ssam longjmp(sendabort, 1); 20810296Ssam } 20910296Ssam 21010296Ssam sendrequest(cmd, local, remote) 21110296Ssam char *cmd, *local, *remote; 21210296Ssam { 21310296Ssam FILE *fin, *dout, *popen(); 21410296Ssam int (*closefunc)(), pclose(), fclose(), (*oldintr)(); 215*11219Ssam char buf[BUFSIZ]; 216*11219Ssam register int bytes = 0, c; 21710296Ssam struct stat st; 21810296Ssam struct timeval start, stop; 219*11219Ssam extern int errno; 22010296Ssam 22110296Ssam closefunc = NULL; 22210296Ssam if (setjmp(sendabort)) 22310296Ssam goto bad; 22410296Ssam oldintr = signal(SIGINT, abortsend); 22510296Ssam if (strcmp(local, "-") == 0) 22610296Ssam fin = stdin; 22710296Ssam else if (*local == '|') { 22810296Ssam fin = popen(local + 1, "r"); 22910296Ssam if (fin == NULL) { 23010296Ssam perror(local + 1); 23110296Ssam goto bad; 23210296Ssam } 23310296Ssam closefunc = pclose; 23410296Ssam } else { 23510296Ssam fin = fopen(local, "r"); 23610296Ssam if (fin == NULL) { 23710296Ssam perror(local); 23810296Ssam goto bad; 23910296Ssam } 24010296Ssam closefunc = fclose; 24110296Ssam if (fstat(fileno(fin), &st) < 0 || 24210296Ssam (st.st_mode&S_IFMT) != S_IFREG) { 24310296Ssam fprintf(stderr, "%s: not a plain file.", local); 24410296Ssam goto bad; 24510296Ssam } 24610296Ssam } 24710296Ssam if (initconn()) 24810296Ssam goto bad; 24910296Ssam if (remote) { 25010296Ssam if (command("%s %s", cmd, remote) != PRELIM) 25110296Ssam goto bad; 25210296Ssam } else 25310296Ssam if (command("%s", cmd) != PRELIM) 25410296Ssam goto bad; 25510296Ssam dout = dataconn("w"); 25610296Ssam if (dout == NULL) 25710296Ssam goto bad; 25810296Ssam gettimeofday(&start, (struct timezone *)0); 259*11219Ssam switch (type) { 260*11219Ssam 261*11219Ssam case TYPE_I: 262*11219Ssam case TYPE_L: 263*11219Ssam errno = 0; 264*11219Ssam while ((c = read(fileno (fin), buf, sizeof (buf))) > 0) { 265*11219Ssam if (write(fileno (dout), buf, c) < 0) 266*11219Ssam break; 267*11219Ssam bytes += c; 268*11219Ssam } 269*11219Ssam if (c < 0) 270*11219Ssam perror(local); 271*11219Ssam if (errno) 272*11219Ssam perror("netout"); 273*11219Ssam break; 274*11219Ssam 275*11219Ssam case TYPE_A: 276*11219Ssam while ((c = getc(fin)) != EOF) { 277*11219Ssam if (c == '\n') { 278*11219Ssam if (ferror(dout)) 279*11219Ssam break; 280*11219Ssam putc('\r', dout); 281*11219Ssam bytes++; 282*11219Ssam } 283*11219Ssam putc(c, dout); 284*11219Ssam bytes++; 285*11219Ssam if (c == '\r') { 286*11219Ssam putc('\0', dout); 287*11219Ssam bytes++; 288*11219Ssam } 289*11219Ssam } 290*11219Ssam if (ferror(fin)) 291*11219Ssam perror(local); 292*11219Ssam if (ferror(dout)) 293*11219Ssam perror("netout"); 294*11219Ssam break; 29510296Ssam } 29610296Ssam gettimeofday(&stop, (struct timezone *)0); 29710296Ssam if (closefunc != NULL) 29810296Ssam (*closefunc)(fin); 29910296Ssam (void) fclose(dout); 30010296Ssam (void) getreply(0); 30110296Ssam done: 30210296Ssam signal(SIGINT, oldintr); 30310296Ssam if (bytes > 0 && verbose) 30410296Ssam ptransfer("sent", bytes, &start, &stop); 30510296Ssam return; 30610296Ssam bad: 30710296Ssam if (data >= 0) 30810296Ssam (void) close(data), data = -1; 30910296Ssam if (closefunc != NULL && fin != NULL) 31010296Ssam (*closefunc)(fin); 31110296Ssam goto done; 31210296Ssam } 31310296Ssam 31410296Ssam jmp_buf recvabort; 31510296Ssam 31610296Ssam abortrecv() 31710296Ssam { 31810296Ssam 31910296Ssam longjmp(recvabort, 1); 32010296Ssam } 32110296Ssam 32210296Ssam recvrequest(cmd, local, remote) 32310296Ssam char *cmd, *local, *remote; 32410296Ssam { 32510296Ssam FILE *fout, *din, *popen(); 326*11219Ssam char buf[BUFSIZ]; 327*11219Ssam int (*closefunc)(), pclose(), fclose(), (*oldintr)(), c; 32810296Ssam register int bytes = 0; 32910296Ssam struct timeval start, stop; 330*11219Ssam extern int errno; 33110296Ssam 33210296Ssam closefunc = NULL; 33310296Ssam if (setjmp(recvabort)) 33410296Ssam goto bad; 33510296Ssam oldintr = signal(SIGINT, abortrecv); 33610296Ssam if (strcmp(local, "-") && *local != '|') 33710296Ssam if (access(local, 2) < 0) { 33810296Ssam char *dir = rindex(local, '/'); 33910296Ssam 34010296Ssam if (dir != NULL) 34110296Ssam *dir = 0; 34210296Ssam if (access(dir ? dir : ".", 2) < 0) { 34310296Ssam perror(local); 34410296Ssam goto bad; 34510296Ssam } 34610296Ssam if (dir != NULL) 34710296Ssam *dir = '/'; 34810296Ssam } 34910296Ssam if (initconn()) 35010296Ssam goto bad; 35110296Ssam if (remote) { 35210296Ssam if (command("%s %s", cmd, remote) != PRELIM) 35310296Ssam goto bad; 35410296Ssam } else 35510296Ssam if (command("%s", cmd) != PRELIM) 35610296Ssam goto bad; 35710296Ssam if (strcmp(local, "-") == 0) 35810296Ssam fout = stdout; 35910296Ssam else if (*local == '|') { 36010296Ssam fout = popen(local + 1, "w"); 36110296Ssam closefunc = pclose; 36210296Ssam } else { 36310296Ssam fout = fopen(local, "w"); 36410296Ssam closefunc = fclose; 36510296Ssam } 36610296Ssam if (fout == NULL) { 36710296Ssam perror(local + 1); 36810296Ssam goto bad; 36910296Ssam } 37010296Ssam din = dataconn("r"); 37110296Ssam if (din == NULL) 37210296Ssam goto bad; 37310296Ssam gettimeofday(&start, (struct timezone *)0); 374*11219Ssam switch (type) { 375*11219Ssam 376*11219Ssam case TYPE_I: 377*11219Ssam case TYPE_L: 378*11219Ssam errno = 0; 379*11219Ssam while ((c = read(fileno(din), buf, sizeof (buf))) > 0) { 380*11219Ssam if (write(fileno(fout), buf, c) < 0) 381*11219Ssam break; 382*11219Ssam bytes += c; 383*11219Ssam } 384*11219Ssam if (c < 0) 385*11219Ssam perror("netin"); 386*11219Ssam if (errno) 38710296Ssam perror(local); 388*11219Ssam break; 389*11219Ssam 390*11219Ssam case TYPE_A: 391*11219Ssam while ((c = getc(din)) != EOF) { 392*11219Ssam if (c == '\r') { 39310296Ssam bytes++; 394*11219Ssam if ((c = getc(din)) != '\n') { 395*11219Ssam if (ferror (fout)) 396*11219Ssam break; 397*11219Ssam putc ('\r', fout); 398*11219Ssam } 399*11219Ssam if (c == '\0') { 400*11219Ssam bytes++; 401*11219Ssam continue; 402*11219Ssam } 403*11219Ssam } 404*11219Ssam putc (c, fout); 405*11219Ssam bytes++; 40610296Ssam } 407*11219Ssam if (ferror (din)) 408*11219Ssam perror ("netin"); 409*11219Ssam if (ferror (fout)) 410*11219Ssam perror (local); 411*11219Ssam break; 41210296Ssam } 41310296Ssam gettimeofday(&stop, (struct timezone *)0); 41410296Ssam (void) fclose(din); 41510296Ssam if (closefunc != NULL) 41610296Ssam (*closefunc)(fout); 41710296Ssam (void) getreply(0); 41810296Ssam done: 41910296Ssam signal(SIGINT, oldintr); 42010296Ssam if (bytes > 0 && verbose) 42110296Ssam ptransfer("received", bytes, &start, &stop); 42210296Ssam return; 42310296Ssam bad: 42410296Ssam if (data >= 0) 42510296Ssam (void) close(data), data = -1; 42610296Ssam if (closefunc != NULL && fout != NULL) 42710296Ssam (*closefunc)(fout); 42810296Ssam goto done; 42910296Ssam } 43010296Ssam 43110296Ssam /* 43210296Ssam * Need to start a listen on the data channel 43310296Ssam * before we send the command, otherwise the 43410296Ssam * server's connect may fail. 43510296Ssam */ 43610296Ssam initconn() 43710296Ssam { 43810296Ssam register char *p, *a; 43910296Ssam int result; 44010296Ssam 44110296Ssam data_addr = myctladdr; 44210296Ssam data_addr.sin_port = 0; /* let system pick one */ 44310296Ssam data = socket(AF_INET, SOCK_STREAM, 0, 0); 44410296Ssam if (data < 0) { 44510296Ssam perror("ftp: socket"); 44610296Ssam return (1); 44710296Ssam } 44810296Ssam if (bind(data, (char *)&data_addr, sizeof (data_addr), 0) < 0) { 44910296Ssam perror("ftp: bind"); 45010296Ssam goto bad; 45110296Ssam } 45210296Ssam if (options & SO_DEBUG && 45310296Ssam setsockopt(data, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) 45410296Ssam perror("ftp: setsockopt (ignored)"); 45510296Ssam if (socketaddr(data, &data_addr) < 0) { 45610296Ssam perror("ftp: socketaddr"); 45710296Ssam goto bad; 45810296Ssam } 45910296Ssam if (listen(data, 1) < 0) { 46010296Ssam perror("ftp: listen"); 46110296Ssam goto bad; 46210296Ssam } 46310296Ssam a = (char *)&data_addr.sin_addr; 46410296Ssam p = (char *)&data_addr.sin_port; 46510296Ssam #define UC(b) (((int)b)&0xff) 46610296Ssam result = 46710296Ssam command("PORT %d,%d,%d,%d,%d,%d", 46810296Ssam UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 46910296Ssam UC(p[0]), UC(p[1])); 47010296Ssam return (result != COMPLETE); 47110296Ssam bad: 47210296Ssam (void) close(data), data = -1; 47310296Ssam return (1); 47410296Ssam } 47510296Ssam 47610296Ssam FILE * 47710296Ssam dataconn(mode) 47810296Ssam char *mode; 47910296Ssam { 48010296Ssam struct sockaddr_in from; 48110296Ssam int s, fromlen = sizeof (from); 48210296Ssam 48310296Ssam s = accept(data, &from, &fromlen, 0); 48410296Ssam if (s < 0) { 48510296Ssam perror("ftp: accept"); 48610296Ssam (void) close(data), data = -1; 48710296Ssam return (NULL); 48810296Ssam } 48910296Ssam (void) close(data); 49010296Ssam data = s; 49110296Ssam return (fdopen(data, mode)); 49210296Ssam } 49310296Ssam 49410296Ssam ptransfer(direction, bytes, t0, t1) 49510296Ssam char *direction; 49610296Ssam int bytes; 49710296Ssam struct timeval *t0, *t1; 49810296Ssam { 49910296Ssam struct timeval td; 50010296Ssam int ms, bs; 50110296Ssam 50210296Ssam tvsub(&td, t1, t0); 50310296Ssam ms = (td.tv_sec * 1000) + (td.tv_usec / 1000); 50410296Ssam #define nz(x) ((x) == 0 ? 1 : (x)) 50510296Ssam bs = ((bytes * NBBY * 1000) / nz(ms)) / NBBY; 50610296Ssam printf("%d bytes %s in %d.%02d seconds (%d.%01d Kbytes/s)\n", 50710296Ssam bytes, direction, td.tv_sec, td.tv_usec / 10000, 50810296Ssam bs / 1024, (((bs % 1024) * 10) + 1023) / 1024); 50910296Ssam } 51010296Ssam 51110296Ssam tvadd(tsum, t0) 51210296Ssam struct timeval *tsum, *t0; 51310296Ssam { 51410296Ssam 51510296Ssam tsum->tv_sec += t0->tv_sec; 51610296Ssam tsum->tv_usec += t0->tv_usec; 51710296Ssam if (tsum->tv_usec > 1000000) 51810296Ssam tsum->tv_sec++, tsum->tv_usec -= 1000000; 51910296Ssam } 52010296Ssam 52110296Ssam tvsub(tdiff, t1, t0) 52210296Ssam struct timeval *tdiff, *t1, *t0; 52310296Ssam { 52410296Ssam 52510296Ssam tdiff->tv_sec = t1->tv_sec - t0->tv_sec; 52610296Ssam tdiff->tv_usec = t1->tv_usec - t0->tv_usec; 52710296Ssam if (tdiff->tv_usec < 0) 52810296Ssam tdiff->tv_sec--, tdiff->tv_usec += 1000000; 52910296Ssam } 530