X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/14433a7321f00cbfce9462abb7e2d11698f1b78a..522da3e451a5c5d93d6523b197c81de222ba5402:/usr/src/usr.bin/ftp/ftp.c diff --git a/usr/src/usr.bin/ftp/ftp.c b/usr/src/usr.bin/ftp/ftp.c index ab1ac06589..e064bd0da7 100644 --- a/usr/src/usr.bin/ftp/ftp.c +++ b/usr/src/usr.bin/ftp/ftp.c @@ -1,189 +1,368 @@ /* - * Copyright (c) 1980 Regents of the University of California. - * All rights reserved. The Berkeley software License Agreement - * specifies the terms and conditions for redistribution. + * Copyright (c) 1985, 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * %sccs.include.redist.c% */ #ifndef lint -static char sccsid[] = "@(#)ftp.c 5.5 (Berkeley) %G%"; -#endif not lint +static char sccsid[] = "@(#)ftp.c 8.3 (Berkeley) %G%"; +#endif /* not lint */ #include #include #include #include #include +#include #include +#include +#include +#include #include +#include -#include -#include +#include +#include #include +#include #include +#include +#include +#include +#include +#include +#include +#include #include "ftp_var.h" +extern int h_errno; + struct sockaddr_in hisctladdr; struct sockaddr_in data_addr; int data = -1; -int connected; +int abrtflag = 0; +jmp_buf ptabort; +int ptabflg; +int ptflag = 0; struct sockaddr_in myctladdr; + FILE *cin, *cout; -FILE *dataconn(); char * hookup(host, port) char *host; int port; { - register struct hostent *hp = 0; + struct hostent *hp = 0; + int s, len, tos; static char hostnamebuf[80]; - int s, len; - bzero((char *)&hisctladdr, sizeof (hisctladdr)); + memset((char *)&hisctladdr, 0, sizeof (hisctladdr)); hisctladdr.sin_addr.s_addr = inet_addr(host); if (hisctladdr.sin_addr.s_addr != -1) { hisctladdr.sin_family = AF_INET; - (void) strcpy(hostnamebuf, host); + (void) strncpy(hostnamebuf, host, sizeof(hostnamebuf)); } else { hp = gethostbyname(host); if (hp == NULL) { - printf("%s: unknown host\n", host); - return (0); + warnx("%s: %s", host, hstrerror(h_errno)); + code = -1; + return ((char *) 0); } hisctladdr.sin_family = hp->h_addrtype; - bcopy(hp->h_addr_list[0], - (caddr_t)&hisctladdr.sin_addr, hp->h_length); - (void) strcpy(hostnamebuf, hp->h_name); + memmove((caddr_t)&hisctladdr.sin_addr, + hp->h_addr_list[0], hp->h_length); + (void) strncpy(hostnamebuf, hp->h_name, sizeof(hostnamebuf)); } hostname = hostnamebuf; s = socket(hisctladdr.sin_family, SOCK_STREAM, 0); if (s < 0) { - perror("ftp: socket"); + warn("socket"); + code = -1; return (0); } hisctladdr.sin_port = port; - while (connect(s, (caddr_t)&hisctladdr, sizeof (hisctladdr)) < 0) { + while (connect(s, (struct sockaddr *)&hisctladdr, sizeof (hisctladdr)) < 0) { if (hp && hp->h_addr_list[1]) { int oerrno = errno; + char *ia; - fprintf(stderr, "ftp: connect to address %s: ", - inet_ntoa(hisctladdr.sin_addr)); + ia = inet_ntoa(hisctladdr.sin_addr); errno = oerrno; - perror(0); + warn("connect to address %s", ia); hp->h_addr_list++; - bcopy(hp->h_addr_list[0], - (caddr_t)&hisctladdr.sin_addr, hp->h_length); - fprintf(stderr, "Trying %s...\n", + memmove((caddr_t)&hisctladdr.sin_addr, + hp->h_addr_list[0], hp->h_length); + fprintf(stdout, "Trying %s...\n", inet_ntoa(hisctladdr.sin_addr)); + (void) close(s); + s = socket(hisctladdr.sin_family, SOCK_STREAM, 0); + if (s < 0) { + warn("socket"); + code = -1; + return (0); + } continue; } - perror("ftp: connect"); + warn("connect"); + code = -1; goto bad; } len = sizeof (myctladdr); - if (getsockname(s, (char *)&myctladdr, &len) < 0) { - perror("ftp: getsockname"); + if (getsockname(s, (struct sockaddr *)&myctladdr, &len) < 0) { + warn("getsockname"); + code = -1; goto bad; } +#ifdef IP_TOS + tos = IPTOS_LOWDELAY; + if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) + warn("setsockopt TOS (ignored)"); +#endif cin = fdopen(s, "r"); cout = fdopen(s, "w"); if (cin == NULL || cout == NULL) { - fprintf(stderr, "ftp: fdopen failed.\n"); + warnx("fdopen failed."); if (cin) - fclose(cin); + (void) fclose(cin); if (cout) - fclose(cout); + (void) fclose(cout); + code = -1; goto bad; } if (verbose) printf("Connected to %s.\n", hostname); - (void) getreply(0); /* read startup message from server */ + if (getreply(0) > 2) { /* read startup message from server */ + if (cin) + (void) fclose(cin); + if (cout) + (void) fclose(cout); + code = -1; + goto bad; + } +#ifdef SO_OOBINLINE + { + int on = 1; + + if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) + < 0 && debug) { + warn("setsockopt"); + } + } +#endif /* SO_OOBINLINE */ + return (hostname); bad: - close(s); + (void) close(s); return ((char *)0); } +int login(host) char *host; { - char acct[80]; - char *user, *pass; - int n; + char tmp[80]; + char *user, *pass, *acct; + int n, aflag = 0; + + user = pass = acct = 0; + if (ruserpass(host, &user, &pass, &acct) < 0) { + code = -1; + return (0); + } + while (user == NULL) { + char *myname = getlogin(); - user = pass = 0; - ruserpass(host, &user, &pass); + if (myname == NULL) { + struct passwd *pp = getpwuid(getuid()); + + if (pp != NULL) + myname = pp->pw_name; + } + if (myname) + printf("Name (%s:%s): ", host, myname); + else + printf("Name (%s): ", host); + (void) fgets(tmp, sizeof(tmp) - 1, stdin); + tmp[strlen(tmp) - 1] = '\0'; + if (*tmp == '\0') + user = myname; + else + user = tmp; + } n = command("USER %s", user); - if (n == CONTINUE) + if (n == CONTINUE) { + if (pass == NULL) + pass = getpass("Password:"); n = command("PASS %s", pass); + } if (n == CONTINUE) { - printf("Account: "); (void) fflush(stdout); - (void) fgets(acct, sizeof(acct) - 1, stdin); - acct[strlen(acct) - 1] = '\0'; + aflag++; + acct = getpass("Account:"); n = command("ACCT %s", acct); } if (n != COMPLETE) { - fprintf(stderr, "Login failed.\n"); + warnx("Login failed."); return (0); } + if (!aflag && acct != NULL) + (void) command("ACCT %s", acct); + if (proxy) + return (1); + for (n = 0; n < macnum; ++n) { + if (!strcmp("init", macros[n].mac_name)) { + (void) strcpy(line, "$init"); + makeargv(); + domacro(margc, margv); + break; + } + } return (1); } -/*VARARGS 1*/ -command(fmt, args) - char *fmt; +void +cmdabort() +{ + + printf("\n"); + (void) fflush(stdout); + abrtflag++; + if (ptflag) + longjmp(ptabort,1); +} + +/*VARARGS*/ +int +command(va_alist) +va_dcl { + va_list ap; + char *fmt; + int r; + sig_t oldintr; + abrtflag = 0; if (debug) { printf("---> "); - _doprnt(fmt, &args, stdout); + va_start(ap); + fmt = va_arg(ap, char *); + if (strncmp("PASS ", fmt, 5) == 0) + printf("PASS XXXX"); + else + vfprintf(stdout, fmt, ap); + va_end(ap); printf("\n"); (void) fflush(stdout); } if (cout == NULL) { - perror ("No control connection for command"); + warn("No control connection for command"); + code = -1; return (0); } - _doprnt(fmt, &args, cout); + oldintr = signal(SIGINT, cmdabort); + va_start(ap); + fmt = va_arg(ap, char *); + vfprintf(cout, fmt, ap); + va_end(ap); fprintf(cout, "\r\n"); (void) fflush(cout); - return (getreply(!strcmp(fmt, "QUIT"))); + cpend = 1; + r = getreply(!strcmp(fmt, "QUIT")); + if (abrtflag && oldintr != SIG_IGN) + (*oldintr)(SIGINT); + (void) signal(SIGINT, oldintr); + return (r); } -#include +char reply_string[BUFSIZ]; /* last line of previous reply */ +int getreply(expecteof) int expecteof; { - register int c, n; - register int code, dig; + int c, n; + int dig; int originalcode = 0, continuation = 0; + sig_t oldintr; + int pflag = 0; + char *cp, *pt = pasv; + oldintr = signal(SIGINT, cmdabort); for (;;) { dig = n = code = 0; + cp = reply_string; while ((c = getc(cin)) != '\n') { + if (c == IAC) { /* handle telnet commands */ + switch (c = getc(cin)) { + case WILL: + case WONT: + c = getc(cin); + fprintf(cout, "%c%c%c", IAC, DONT, c); + (void) fflush(cout); + break; + case DO: + case DONT: + c = getc(cin); + fprintf(cout, "%c%c%c", IAC, WONT, c); + (void) fflush(cout); + break; + default: + break; + } + continue; + } dig++; if (c == EOF) { - if (expecteof) + if (expecteof) { + (void) signal(SIGINT,oldintr); + code = 221; return (0); + } lostpeer(); - exit(1); + if (verbose) { + printf("421 Service not available, remote server has closed connection\n"); + (void) fflush(stdout); + } + code = 421; + return (4); + } + if (c != '\r' && (verbose > 0 || + (verbose > -1 && n == '5' && dig > 4))) { + if (proxflag && + (dig == 1 || dig == 5 && verbose == 0)) + printf("%s:",hostname); + (void) putchar(c); } - if (verbose && c != '\r' || - (n == '5' && dig > 4)) - putchar(c); if (dig < 4 && isdigit(c)) code = code * 10 + (c - '0'); - if (dig == 4 && c == '-') + if (!pflag && code == 227) + pflag = 1; + if (dig > 4 && pflag == 1 && isdigit(c)) + pflag = 2; + if (pflag == 2) { + if (c != '\r' && c != ')') + *pt++ = c; + else { + *pt = '\0'; + pflag = 3; + } + } + if (dig == 4 && c == '-') { + if (continuation) + code = 0; continuation++; + } if (n == 0) n = c; + if (cp < &reply_string[sizeof(reply_string) - 1]) + *cp++ = c; } - if (verbose || n == '5') { - putchar(c); + if (verbose > 0 || verbose > -1 && n == '5') { + (void) putchar(c); (void) fflush (stdout); } if (continuation && code != originalcode) { @@ -191,317 +370,558 @@ getreply(expecteof) originalcode = code; continue; } + *cp = '\0'; + if (n != '1') + cpend = 0; + (void) signal(SIGINT,oldintr); + if (code == 421 || originalcode == 421) + lostpeer(); + if (abrtflag && oldintr != cmdabort && oldintr != SIG_IGN) + (*oldintr)(SIGINT); return (n - '0'); } } +int +empty(mask, sec) + struct fd_set *mask; + int sec; +{ + struct timeval t; + + t.tv_sec = (long) sec; + t.tv_usec = 0; + return (select(32, mask, (struct fd_set *) 0, (struct fd_set *) 0, &t)); +} + jmp_buf sendabort; +void abortsend() { + mflag = 0; + abrtflag = 0; + printf("\nsend aborted\nwaiting for remote to finish abort\n"); + (void) fflush(stdout); longjmp(sendabort, 1); } -sendrequest(cmd, local, remote) +#define HASHBYTES 1024 + +void +sendrequest(cmd, local, remote, printnames) char *cmd, *local, *remote; + int printnames; { - FILE *fin, *dout, *popen(); - int (*closefunc)(), pclose(), fclose(), (*oldintr)(); - char buf[BUFSIZ]; - int expectingreply = 0; - long bytes = 0, hashbytes = sizeof (buf); - register int c, d; struct stat st; struct timeval start, stop; + int c, d; + FILE *fin, *dout = 0, *popen(); + int (*closefunc) __P((FILE *)); + char buf[BUFSIZ], *bufp; + long bytes = 0, hashbytes = HASHBYTES; + char *lmode, buf[BUFSIZ], *bufp; + if (verbose && printnames) { + if (local && *local != '-') + printf("local: %s ", local); + if (remote) + printf("remote: %s\n", remote); + } + if (proxy) { + proxtrans(cmd, local, remote); + return; + } + if (curtype != type) + changetype(type, 0); closefunc = NULL; - if (setjmp(sendabort)) - goto bad; + oldintr = NULL; + oldintp = NULL; + lmode = "w"; + if (setjmp(sendabort)) { + while (cpend) { + (void) getreply(0); + } + if (data >= 0) { + (void) close(data); + data = -1; + } + if (oldintr) + (void) signal(SIGINT,oldintr); + if (oldintp) + (void) signal(SIGPIPE,oldintp); + code = -1; + return; + } oldintr = signal(SIGINT, abortsend); if (strcmp(local, "-") == 0) fin = stdin; else if (*local == '|') { - /* - * Advance local so further uses just yield file name - * thus later references for error messages need not check - * for '|' special case. - */ - local += 1; - fin = popen(local, "r"); + oldintp = signal(SIGPIPE,SIG_IGN); + fin = popen(local + 1, "r"); if (fin == NULL) { - perror(local); - goto bad; + warn("%s", local + 1); + (void) signal(SIGINT, oldintr); + (void) signal(SIGPIPE, oldintp); + code = -1; + return; } closefunc = pclose; } else { fin = fopen(local, "r"); if (fin == NULL) { - perror(local); - goto bad; + warn("local: %s", local); + (void) signal(SIGINT, oldintr); + code = -1; + return; } closefunc = fclose; if (fstat(fileno(fin), &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG) { - fprintf(stderr, "%s: not a plain file.\n", local); - goto bad; + fprintf(stdout, "%s: not a plain file.\n", local); + (void) signal(SIGINT, oldintr); + fclose(fin); + code = -1; + return; } } - if (initconn()) - goto bad; + if (initconn()) { + (void) signal(SIGINT, oldintr); + if (oldintp) + (void) signal(SIGPIPE, oldintp); + code = -1; + if (closefunc != NULL) + (*closefunc)(fin); + return; + } + if (setjmp(sendabort)) + goto abort; + if (remote) { - if (command("%s %s", cmd, remote) != PRELIM) - goto bad; + if (command("%s %s", cmd, remote) != PRELIM) { + (void) signal(SIGINT, oldintr); + if (oldintp) + (void) signal(SIGPIPE, oldintp); + if (closefunc != NULL) + (*closefunc)(fin); + return; + } } else - if (command("%s", cmd) != PRELIM) - goto bad; - expectingreply++; /* got preliminary reply, expecting final reply */ - dout = dataconn("w"); + if (command("%s", cmd) != PRELIM) { + (void) signal(SIGINT, oldintr); + if (oldintp) + (void) signal(SIGPIPE, oldintp); + if (closefunc != NULL) + (*closefunc)(fin); + return; + } + dout = dataconn(lmode); if (dout == NULL) - goto bad; - gettimeofday(&start, (struct timezone *)0); - switch (type) { + goto abort; + (void) gettimeofday(&start, (struct timezone *)0); + oldintp = signal(SIGPIPE, SIG_IGN); + switch (curtype) { case TYPE_I: case TYPE_L: errno = d = 0; - while ((c = read(fileno (fin), buf, sizeof (buf))) > 0) { - if ((d = write(fileno (dout), buf, c)) < 0) - break; + while ((c = read(fileno(fin), buf, sizeof (buf))) > 0) { bytes += c; + for (bufp = buf; c > 0; c -= d, bufp += d) + if ((d = write(fileno(dout), bufp, c)) <= 0) + break; + for (bufp = buf; c > 0; c -= d, bufp += d) + if ((d = write(fileno(dout), bufp, c)) <= 0) + break; if (hash) { - putchar('#'); - fflush(stdout); + while (bytes >= hashbytes) { + (void) putchar('#'); + hashbytes += HASHBYTES; + } + (void) fflush(stdout); } } if (hash && bytes > 0) { - putchar('\n'); - fflush(stdout); + if (bytes < HASHBYTES) + (void) putchar('#'); + (void) putchar('\n'); + (void) fflush(stdout); } if (c < 0) - perror(local); - if (d < 0) - perror("netout"); + warn("local: %s", local); + if (d <= 0) { + if (d == 0) + fprintf(stderr, "netout: write returned 0?\n"); + else if (errno != EPIPE) + warn("netout"); + bytes = -1; + } break; case TYPE_A: while ((c = getc(fin)) != EOF) { if (c == '\n') { while (hash && (bytes >= hashbytes)) { - putchar('#'); - fflush(stdout); - hashbytes += sizeof (buf); + (void) putchar('#'); + (void) fflush(stdout); + hashbytes += HASHBYTES; } if (ferror(dout)) break; - putc('\r', dout); + (void) putc('\r', dout); bytes++; } - putc(c, dout); + (void) putc(c, dout); bytes++; - if (c == '\r') { - putc('\0', dout); - bytes++; - } + /* if (c == '\r') { */ + /* (void) putc('\0', dout); // this violates rfc */ + /* bytes++; */ + /* } */ } if (hash) { if (bytes < hashbytes) - putchar('#'); - putchar('\n'); - fflush(stdout); + (void) putchar('#'); + (void) putchar('\n'); + (void) fflush(stdout); } if (ferror(fin)) - perror(local); - if (ferror(dout)) - perror("netout"); + warn("local: %s", local); + if (ferror(dout)) { + if (errno != EPIPE) + warn("netout"); + bytes = -1; + } break; } - gettimeofday(&stop, (struct timezone *)0); + (void) gettimeofday(&stop, (struct timezone *)0); if (closefunc != NULL) - (*closefunc)(fin), closefunc = NULL; + (*closefunc)(fin); (void) fclose(dout); -done: - if (expectingreply) { - (void) getreply(0); - expectingreply = 0; - } - signal(SIGINT, oldintr); - if (bytes > 0 && verbose) + (void) getreply(0); + (void) signal(SIGINT, oldintr); + if (oldintp) + (void) signal(SIGPIPE, oldintp); + if (bytes > 0) ptransfer("sent", bytes, &start, &stop); return; -bad: - if (data >= 0) - (void) close(data), data = -1; +abort: + (void) gettimeofday(&stop, (struct timezone *)0); + (void) signal(SIGINT, oldintr); + if (oldintp) + (void) signal(SIGPIPE, oldintp); + if (!cpend) { + code = -1; + return; + } + if (data >= 0) { + (void) close(data); + data = -1; + } + if (dout) + (void) fclose(dout); + (void) getreply(0); + code = -1; if (closefunc != NULL && fin != NULL) - (*closefunc)(fin), closefunc = NULL; - bytes = 0; /* so we don't print a message if the transfer was aborted */ - goto done; + (*closefunc)(fin); + if (bytes > 0) + ptransfer("sent", bytes, &start, &stop); } jmp_buf recvabort; +void abortrecv() { + mflag = 0; + abrtflag = 0; + printf("\nreceive aborted\nwaiting for remote to finish abort\n"); + (void) fflush(stdout); longjmp(recvabort, 1); } -recvrequest(cmd, local, remote, mode) - char *cmd, *local, *remote, *mode; +void +recvrequest(cmd, local, remote, lmode, printnames) + char *cmd, *local, *remote, *lmode; + int printnames; { - FILE *fout, *din, *popen(); - int (*closefunc)(), pclose(), fclose(), (*oldintr)(); - char buf[BUFSIZ]; - int expectingreply = 0; - long bytes = 0, hashbytes = sizeof (buf); - register int c, d; + char *bufp, *gunique(), msg; + static char *buf; + static int bufsize; + static char *buf; + long bytes = 0, hashbytes = HASHBYTES; struct timeval start, stop; + struct stat st; + is_retr = strcmp(cmd, "RETR") == 0; + if (is_retr && verbose && printnames) { + if (local && *local != '-') + printf("local: %s ", local); + if (remote) + printf("remote: %s\n", remote); + } + if (proxy && is_retr) { + proxtrans(cmd, local, remote); + return; + } closefunc = NULL; - if (setjmp(recvabort)) - goto bad; + oldintr = NULL; + oldintp = NULL; + tcrflag = !crflag && is_retr; + if (setjmp(recvabort)) { + while (cpend) { + (void) getreply(0); + } + if (data >= 0) { + (void) close(data); + data = -1; + } + if (oldintr) + (void) signal(SIGINT, oldintr); + code = -1; + return; + } oldintr = signal(SIGINT, abortrecv); - if (strcmp(local, "-") && *local != '|') + if (strcmp(local, "-") && *local != '|') { if (access(local, 2) < 0) { - if (errno == ENOENT) { - char *dir = rindex(local, '/'); - - if (dir != NULL) - *dir = 0; - d = access(dir ? local : ".", 2); - if (dir != NULL) - *dir = '/'; - if (d < 0) { - perror(local); - goto bad; - } - } else { - perror(local); - goto bad; + char *dir = strrchr(local, '/'); + + if (errno != ENOENT && errno != EACCES) { + warn("local: %s", local); + (void) signal(SIGINT, oldintr); + code = -1; + return; + } + if (dir != NULL) + *dir = 0; + d = access(dir ? local : ".", 2); + if (dir != NULL) + *dir = '/'; + if (d < 0) { + warn("local: %s", local); + (void) signal(SIGINT, oldintr); + code = -1; + return; + } + if (!runique && errno == EACCES && + chmod(local, 0600) < 0) { + warn("local: %s", local); + (void) signal(SIGINT, oldintr); + (void) signal(SIGINT, oldintr); + code = -1; + return; + } + if (runique && errno == EACCES && + (local = gunique(local)) == NULL) { + (void) signal(SIGINT, oldintr); + code = -1; + return; } } - if (initconn()) - goto bad; + else if (runique && (local = gunique(local)) == NULL) { + (void) signal(SIGINT, oldintr); + code = -1; + return; + } + } + if (!is_retr) { + if (curtype != TYPE_A) + changetype(TYPE_A, 0); + } else if (curtype != type) + changetype(type, 0); + if (initconn()) { + (void) signal(SIGINT, oldintr); + code = -1; + return; + } + if (setjmp(recvabort)) + goto abort; + if (is_retr && restart_point && + command("REST %ld", (long) restart_point) != CONTINUE) + return; if (remote) { - if (command("%s %s", cmd, remote) != PRELIM) - goto bad; - } else - if (command("%s", cmd) != PRELIM) - goto bad; - expectingreply++; /* got preliminary reply, expecting final reply */ + if (command("%s %s", cmd, remote) != PRELIM) { + (void) signal(SIGINT, oldintr); + return; + } + } else { + if (command("%s", cmd) != PRELIM) { + (void) signal(SIGINT, oldintr); + return; + } + } + din = dataconn("r"); + if (din == NULL) + goto abort; if (strcmp(local, "-") == 0) fout = stdout; else if (*local == '|') { - /* - * Advance local over '|' so don't need to check for - * '|' special case any further. - */ - local += 1; - fout = popen(local, "w"); + oldintp = signal(SIGPIPE, SIG_IGN); + fout = popen(local + 1, "w"); + if (fout == NULL) { + warn("%s", local+1); + goto abort; + } closefunc = pclose; } else { - fout = fopen(local, mode); + fout = fopen(local, lmode); + if (fout == NULL) { + warn("local: %s", local); + goto abort; + } closefunc = fclose; } - if (fout == NULL) { - perror(local); - goto bad; + if (fstat(fileno(fout), &st) < 0 || st.st_blksize == 0) + st.st_blksize = BUFSIZ; + if (st.st_blksize > bufsize) { + if (buf) + (void) free(buf); + buf = malloc((unsigned)st.st_blksize); + if (buf == NULL) { + warn("malloc"); + bufsize = 0; + bufsize = 0; + goto abort; + } + bufsize = st.st_blksize; } - din = dataconn("r"); - if (din == NULL) - goto bad; - gettimeofday(&start, (struct timezone *)0); - switch (type) { + (void) gettimeofday(&start, (struct timezone *)0); + switch (curtype) { case TYPE_I: case TYPE_L: errno = d = 0; - while ((c = read(fileno(din), buf, sizeof (buf))) > 0) { - if ((d = write(fileno(fout), buf, c)) < 0) + while ((c = read(fileno(din), buf, bufsize)) > 0) { + if ((d = write(fileno(fout), buf, c)) != c) break; bytes += c; if (hash) { - putchar('#'); - fflush(stdout); + while (bytes >= hashbytes) { + (void) putchar('#'); + hashbytes += HASHBYTES; + } + (void) fflush(stdout); } } if (hash && bytes > 0) { - putchar('\n'); - fflush(stdout); + if (bytes < HASHBYTES) + (void) putchar('#'); + (void) putchar('\n'); + (void) fflush(stdout); + } + if (c < 0) { + if (errno != EPIPE) + warn("netin"); + bytes = -1; + } + if (d < c) { + if (d < 0) + perror(local); + else + fprintf(stderr, "%s: short write\n", local); } - if (c < 0) - perror("netin"); - if (d < 0) - perror(local); break; case TYPE_A: while ((c = getc(din)) != EOF) { - if (c == '\r') { + if (c == '\n') + bare_lfs++; + while (c == '\r') { while (hash && (bytes >= hashbytes)) { - putchar('#'); - fflush(stdout); - hashbytes += sizeof (buf); + (void) putchar('#'); + (void) fflush(stdout); + hashbytes += HASHBYTES; } bytes++; - if ((c = getc(din)) != '\n') { - if (ferror (fout)) - break; - putc ('\r', fout); - } - if (c == '\0') { - bytes++; - continue; + if ((c = getc(din)) != '\n' || tcrflag) { + if (ferror(fout)) + goto break2; + (void) putc('\r', fout); + if (c == '\0') { + bytes++; + goto contin2; + } + if (c == EOF) + goto contin2; + } + if (c == EOF) + goto contin2; } } - putc (c, fout); + (void) putc(c, fout); bytes++; + contin2: ; + } +break2: + if (bare_lfs) { + printf("WARNING! %d bare linefeeds received in ASCII mode\n", bare_lfs); + printf("File may not have transferred correctly.\n"); } if (hash) { if (bytes < hashbytes) - putchar('#'); - putchar('\n'); - fflush(stdout); - } - if (ferror (din)) - perror ("netin"); - if (ferror (fout)) - perror (local); + (void) putchar('#'); + (void) putchar('\n'); + (void) fflush(stdout); + } + if (ferror(din)) { + if (errno != EPIPE) + warn("netin"); + bytes = -1; + } + if (ferror(fout)) + warn("local: %s", local); break; } - gettimeofday(&stop, (struct timezone *)0); - (void) fclose(din); if (closefunc != NULL) - (*closefunc)(fout), closefunc = NULL; -done: - if (expectingreply) { - (void) getreply(0); - expectingreply = 0; - } - signal(SIGINT, oldintr); - if (bytes > 0 && verbose) + (*closefunc)(fout); + (void) signal(SIGINT, oldintr); + if (oldintp) + (void) signal(SIGPIPE, oldintp); + (void) gettimeofday(&stop, (struct timezone *)0); + (void) fclose(din); + (void) getreply(0); + if (bytes > 0 && is_retr) ptransfer("received", bytes, &start, &stop); return; -bad: - if (data >= 0) - (void) close(data), data = -1; +abort: + +/* abort using RFC959 recommended IP,SYNC sequence */ + + (void) gettimeofday(&stop, (struct timezone *)0); + if (oldintp) + (void) signal(SIGPIPE, oldintr); + (void) signal(SIGINT, SIG_IGN); + if (!cpend) { + code = -1; + (void) signal(SIGINT, oldintr); + return; + } + + abort_remote(din); + code = -1; + if (data >= 0) { + (void) close(data); + data = -1; + } if (closefunc != NULL && fout != NULL) (*closefunc)(fout); - bytes = 0; /* so we don't print a message if the transfer was aborted */ - goto done; + if (din) + (void) fclose(din); + if (bytes > 0) + ptransfer("received", bytes, &start, &stop); + (void) signal(SIGINT, oldintr); } /* - * Need to start a listen on the data channel - * before we send the command, otherwise the - * server's connect may fail. + * Need to start a listen on the data channel before we send the command, + * otherwise the server's connect may fail. */ -static int sendport = -1; - +int initconn() { - register char *p, *a; - int result, len; + char *p, *a; + int result, len, tmpno = 0; int on = 1; noport: @@ -509,33 +929,33 @@ noport: if (sendport) data_addr.sin_port = 0; /* let system pick one */ if (data != -1) - (void) close (data); + (void) close(data); data = socket(AF_INET, SOCK_STREAM, 0); if (data < 0) { - perror("ftp: socket"); + warn("socket"); + if (tmpno) + sendport = 1; return (1); } if (!sendport) - if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) { - perror("ftp: setsockopt (reuse address)"); + if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof (on)) < 0) { + warn("setsockopt (reuse address)"); goto bad; } - if (bind(data, (char *)&data_addr, sizeof (data_addr), 0) < 0) { - perror("ftp: bind"); + if (bind(data, (struct sockaddr *)&data_addr, sizeof (data_addr)) < 0) { + warn("bind"); goto bad; } if (options & SO_DEBUG && - setsockopt(data, SOL_SOCKET, SO_DEBUG, &on, sizeof (on)) < 0) - perror("ftp: setsockopt (ignored)"); + setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof (on)) < 0) + warn("setsockopt (ignored)"); len = sizeof (data_addr); - if (getsockname(data, (char *)&data_addr, &len) < 0) { - perror("ftp: getsockname"); - goto bad; - } - if (listen(data, 1) < 0) { - perror("ftp: listen"); + if (getsockname(data, (struct sockaddr *)&data_addr, &len) < 0) { + warn("getsockname"); goto bad; } + if (listen(data, 1) < 0) + warn("listen"); if (sendport) { a = (char *)&data_addr.sin_addr; p = (char *)&data_addr.sin_port; @@ -546,34 +966,50 @@ noport: UC(p[0]), UC(p[1])); if (result == ERROR && sendport == -1) { sendport = 0; + tmpno = 1; goto noport; } return (result != COMPLETE); } + if (tmpno) + sendport = 1; +#ifdef IP_TOS + on = IPTOS_THROUGHPUT; + if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) + warn("setsockopt TOS (ignored)"); +#endif return (0); bad: (void) close(data), data = -1; + if (tmpno) + sendport = 1; return (1); } FILE * -dataconn(mode) - char *mode; +dataconn(lmode) + char *lmode; { struct sockaddr_in from; - int s, fromlen = sizeof (from); + int s, fromlen = sizeof (from), tos; - s = accept(data, &from, &fromlen, 0); + s = accept(data, (struct sockaddr *) &from, &fromlen); if (s < 0) { - perror("ftp: accept"); + warn("accept"); (void) close(data), data = -1; return (NULL); } (void) close(data); data = s; - return (fdopen(data, mode)); +#ifdef IP_TOS + tos = IPTOS_THROUGHPUT; + if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) + warn("setsockopt TOS (ignored)"); +#endif + return (fdopen(data, lmode)); } +void ptransfer(direction, bytes, t0, t1) char *direction; long bytes; @@ -582,14 +1018,18 @@ ptransfer(direction, bytes, t0, t1) struct timeval td; float s, bs; - tvsub(&td, t1, t0); - s = td.tv_sec + (td.tv_usec / 1000000.); + if (verbose) { + tvsub(&td, t1, t0); + s = td.tv_sec + (td.tv_usec / 1000000.); #define nz(x) ((x) == 0 ? 1 : (x)) - bs = bytes / nz(s); - printf("%ld bytes %s in %.2g seconds (%.2g Kbytes/s)\n", - bytes, direction, s, bs / 1024.); + bs = bytes / nz(s); + printf("%ld bytes %s in %.2g seconds (%.2g Kbytes/s)\n", + bytes, direction, s, bs / 1024.); + } } +/* +void tvadd(tsum, t0) struct timeval *tsum, *t0; { @@ -599,7 +1039,9 @@ tvadd(tsum, t0) if (tsum->tv_usec > 1000000) tsum->tv_sec++, tsum->tv_usec -= 1000000; } +*/ +void tvsub(tdiff, t1, t0) struct timeval *tdiff, *t1, *t0; { @@ -609,3 +1051,343 @@ tvsub(tdiff, t1, t0) if (tdiff->tv_usec < 0) tdiff->tv_sec--, tdiff->tv_usec += 1000000; } + +void +psabort() +{ + + abrtflag++; +} + +void +pswitch(flag) + int flag; +{ + sig_t oldintr; + static struct comvars { + int connect; + char name[MAXHOSTNAMELEN]; + struct sockaddr_in mctl; + struct sockaddr_in hctl; + FILE *in; + FILE *out; + int tpe; + int curtpe; + int cpnd; + int sunqe; + int runqe; + int mcse; + int ntflg; + char nti[17]; + char nto[17]; + int mapflg; + char mi[MAXPATHLEN]; + char mo[MAXPATHLEN]; + } proxstruct, tmpstruct; + struct comvars *ip, *op; + + abrtflag = 0; + oldintr = signal(SIGINT, psabort); + if (flag) { + if (proxy) + return; + ip = &tmpstruct; + op = &proxstruct; + proxy++; + } else { + if (!proxy) + return; + ip = &proxstruct; + op = &tmpstruct; + proxy = 0; + } + ip->connect = connected; + connected = op->connect; + if (hostname) { + (void) strncpy(ip->name, hostname, sizeof(ip->name) - 1); + ip->name[strlen(ip->name)] = '\0'; + } else + ip->name[0] = 0; + hostname = op->name; + ip->hctl = hisctladdr; + hisctladdr = op->hctl; + ip->mctl = myctladdr; + myctladdr = op->mctl; + ip->in = cin; + cin = op->in; + ip->out = cout; + cout = op->out; + ip->tpe = type; + type = op->tpe; + ip->curtpe = curtype; + curtype = op->curtpe; + ip->cpnd = cpend; + cpend = op->cpnd; + ip->sunqe = sunique; + sunique = op->sunqe; + ip->runqe = runique; + runique = op->runqe; + ip->mcse = mcase; + mcase = op->mcse; + ip->ntflg = ntflag; + ntflag = op->ntflg; + (void) strncpy(ip->nti, ntin, 16); + (ip->nti)[strlen(ip->nti)] = '\0'; + (void) strcpy(ntin, op->nti); + (void) strncpy(ip->nto, ntout, 16); + (ip->nto)[strlen(ip->nto)] = '\0'; + (void) strcpy(ntout, op->nto); + ip->mapflg = mapflag; + mapflag = op->mapflg; + (void) strncpy(ip->mi, mapin, MAXPATHLEN - 1); + (ip->mi)[strlen(ip->mi)] = '\0'; + (void) strcpy(mapin, op->mi); + (void) strncpy(ip->mo, mapout, MAXPATHLEN - 1); + (ip->mo)[strlen(ip->mo)] = '\0'; + (void) strcpy(mapout, op->mo); + (void) signal(SIGINT, oldintr); + if (abrtflag) { + abrtflag = 0; + (*oldintr)(SIGINT); + } +} + +void +abortpt() +{ + + printf("\n"); + (void) fflush(stdout); + ptabflg++; + mflag = 0; + abrtflag = 0; + longjmp(ptabort, 1); +} + +void +proxtrans(cmd, local, remote) + char *cmd, *local, *remote; +{ + sig_t oldintr; + int secndflag = 0, prox_type, nfnd; + char *cmd2; + struct fd_set mask; + + if (strcmp(cmd, "RETR")) + cmd2 = "RETR"; + else + cmd2 = runique ? "STOU" : "STOR"; + if ((prox_type = type) == 0) { + if (unix_server && unix_proxy) + prox_type = TYPE_I; + else + prox_type = TYPE_A; + } + if (curtype != prox_type) + changetype(prox_type, 1); + if (command("PASV") != COMPLETE) { + printf("proxy server does not support third party transfers.\n"); + return; + } + pswitch(0); + if (!connected) { + printf("No primary connection\n"); + pswitch(1); + code = -1; + return; + } + if (curtype != prox_type) + changetype(prox_type, 1); + if (command("PORT %s", pasv) != COMPLETE) { + pswitch(1); + return; + } + if (setjmp(ptabort)) + goto abort; + oldintr = signal(SIGINT, abortpt); + if (command("%s %s", cmd, remote) != PRELIM) { + (void) signal(SIGINT, oldintr); + pswitch(1); + return; + } + sleep(2); + pswitch(1); + secndflag++; + if (command("%s %s", cmd2, local) != PRELIM) + goto abort; + ptflag++; + (void) getreply(0); + pswitch(0); + (void) getreply(0); + (void) signal(SIGINT, oldintr); + pswitch(1); + ptflag = 0; + printf("local: %s remote: %s\n", local, remote); + return; +abort: + (void) signal(SIGINT, SIG_IGN); + ptflag = 0; + if (strcmp(cmd, "RETR") && !proxy) + pswitch(1); + else if (!strcmp(cmd, "RETR") && proxy) + pswitch(0); + if (!cpend && !secndflag) { /* only here if cmd = "STOR" (proxy=1) */ + if (command("%s %s", cmd2, local) != PRELIM) { + pswitch(0); + if (cpend) + abort_remote((FILE *) NULL); + } + pswitch(1); + if (ptabflg) + code = -1; + (void) signal(SIGINT, oldintr); + return; + } + if (cpend) + abort_remote((FILE *) NULL); + pswitch(!proxy); + if (!cpend && !secndflag) { /* only if cmd = "RETR" (proxy=1) */ + if (command("%s %s", cmd2, local) != PRELIM) { + pswitch(0); + if (cpend) + abort_remote((FILE *) NULL); + pswitch(1); + if (ptabflg) + code = -1; + (void) signal(SIGINT, oldintr); + return; + } + } + if (cpend) + abort_remote((FILE *) NULL); + pswitch(!proxy); + if (cpend) { + FD_ZERO(&mask); + FD_SET(fileno(cin), &mask); + if ((nfnd = empty(&mask, 10)) <= 0) { + if (nfnd < 0) { + warn("abort"); + } + if (ptabflg) + code = -1; + lostpeer(); + } + (void) getreply(0); + (void) getreply(0); + } + if (proxy) + pswitch(0); + pswitch(1); + if (ptabflg) + code = -1; + (void) signal(SIGINT, oldintr); +} + +void +reset(argc, argv) + int argc; + char *argv[]; +{ + struct fd_set mask; + int nfnd = 1; + + FD_ZERO(&mask); + while (nfnd > 0) { + FD_SET(fileno(cin), &mask); + if ((nfnd = empty(&mask,0)) < 0) { + warn("reset"); + code = -1; + lostpeer(); + } + else if (nfnd) { + (void) getreply(0); + } + } +} + +char * +gunique(local) + char *local; +{ + static char new[MAXPATHLEN]; + char *cp = strrchr(local, '/'); + int d, count=0; + char ext = '1'; + + if (cp) + *cp = '\0'; + d = access(cp ? local : ".", 2); + if (cp) + *cp = '/'; + if (d < 0) { + warn("local: %s", local); + return ((char *) 0); + } + (void) strcpy(new, local); + cp = new + strlen(new); + *cp++ = '.'; + while (!d) { + if (++count == 100) { + printf("runique: can't find unique file name.\n"); + return ((char *) 0); + } + *cp++ = ext; + *cp = '\0'; + if (ext == '9') + ext = '0'; + else + ext++; + if ((d = access(new, 0)) < 0) + break; + if (ext != '0') + cp--; + else if (*(cp - 2) == '.') + *(cp - 1) = '1'; + else { + *(cp - 2) = *(cp - 2) + 1; + cp--; + } + } + return (new); +} + +void +abort_remote(din) + FILE *din; +{ + char buf[BUFSIZ]; + int nfnd; + struct fd_set mask; + + /* + * send IAC in urgent mode instead of DM because 4.3BSD places oob mark + * after urgent byte rather than before as is protocol now + */ + sprintf(buf, "%c%c%c", IAC, IP, IAC); + if (send(fileno(cout), buf, 3, MSG_OOB) != 3) + warn("abort"); + fprintf(cout,"%cABOR\r\n", DM); + (void) fflush(cout); + FD_ZERO(&mask); + FD_SET(fileno(cin), &mask); + if (din) { + FD_SET(fileno(din), &mask); + } + if ((nfnd = empty(&mask, 10)) <= 0) { + if (nfnd < 0) { + warn("abort"); + } + if (ptabflg) + code = -1; + lostpeer(); + } + if (din && FD_ISSET(fileno(din), &mask)) { + while (read(fileno(din), buf, BUFSIZ) > 0) + /* LOOP */; + } + if (getreply(0) == ERROR && code == 552) { + /* 552 needed for nic style abort */ + (void) getreply(0); + } + (void) getreply(0); +}