diff options
author | gavin <gavin@FreeBSD.org> | 2011-06-20 08:00:32 +0000 |
---|---|---|
committer | gavin <gavin@FreeBSD.org> | 2011-06-20 08:00:32 +0000 |
commit | 53f276e3d4f7f36bd258c609d0908855d1a26243 (patch) | |
tree | f11cc58f8cc459a6d42d318bd66f3e5a209e87ba /contrib/tnftp/src/ftp.c | |
parent | 9185689916b66441c4139d598975c6e6a773b5bc (diff) | |
parent | eff741aef035a9d542d756973d0a81dd4ad6bbe6 (diff) | |
download | FreeBSD-src-53f276e3d4f7f36bd258c609d0908855d1a26243.zip FreeBSD-src-53f276e3d4f7f36bd258c609d0908855d1a26243.tar.gz |
Merge tnftp-20100108 from the vendor branch into head.
PR: bin/112288 bin/120256 bin/129014 bin/145528
MFC after: 1 month
Diffstat (limited to 'contrib/tnftp/src/ftp.c')
-rw-r--r-- | contrib/tnftp/src/ftp.c | 686 |
1 files changed, 332 insertions, 354 deletions
diff --git a/contrib/tnftp/src/ftp.c b/contrib/tnftp/src/ftp.c index dae6131..4c4942f 100644 --- a/contrib/tnftp/src/ftp.c +++ b/contrib/tnftp/src/ftp.c @@ -1,7 +1,8 @@ -/* $NetBSD: ftp.c,v 1.132 2005/05/14 15:26:43 lukem Exp $ */ +/* $NetBSD: ftp.c,v 1.18 2009/05/20 12:53:47 lukem Exp $ */ +/* from NetBSD: ftp.c,v 1.159 2009/04/15 03:42:33 jld Exp */ /*- - * Copyright (c) 1996-2005 The NetBSD Foundation, Inc. + * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -15,13 +16,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -94,12 +88,17 @@ * SUCH DAMAGE. */ +#include "tnftp.h" +#include <arpa/telnet.h> + +#if 0 /* tnftp */ + #include <sys/cdefs.h> #ifndef lint #if 0 static char sccsid[] = "@(#)ftp.c 8.6 (Berkeley) 10/27/94"; #else -__RCSID("$NetBSD: ftp.c,v 1.132 2005/05/14 15:26:43 lukem Exp $"); +__RCSID(" NetBSD: ftp.c,v 1.159 2009/04/15 03:42:33 jld Exp "); #endif #endif /* not lint */ @@ -127,6 +126,8 @@ __RCSID("$NetBSD: ftp.c,v 1.132 2005/05/14 15:26:43 lukem Exp $"); #include <unistd.h> #include <stdarg.h> +#endif /* tnftp */ + #include "ftp_var.h" volatile sig_atomic_t abrtflag; @@ -146,12 +147,12 @@ struct sockinet { struct sockaddr_in6 su_sin6; #endif } si_su; -#if !HAVE_SOCKADDR_SA_LEN +#if !defined(HAVE_STRUCT_SOCKADDR_IN_SIN_LEN) int si_len; #endif }; -#if !HAVE_SOCKADDR_SA_LEN +#if !defined(HAVE_STRUCT_SOCKADDR_IN_SIN_LEN) # define su_len si_len #else # define su_len si_su.su_sin.sin_len @@ -162,27 +163,26 @@ struct sockinet { struct sockinet myctladdr, hisctladdr, data_addr; char * -hookup(char *host, char *port) +hookup(const char *host, const char *port) { - int s = -1, error, portnum; + int s = -1, error; struct addrinfo hints, *res, *res0; - char hbuf[MAXHOSTNAMELEN]; static char hostnamebuf[MAXHOSTNAMELEN]; - char *cause = "unknown"; socklen_t len; int on = 1; memset((char *)&hisctladdr, 0, sizeof (hisctladdr)); memset((char *)&myctladdr, 0, sizeof (myctladdr)); memset(&hints, 0, sizeof(hints)); - portnum = parseport(port, FTP_PORT); hints.ai_flags = AI_CANONNAME; hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; - error = getaddrinfo(host, NULL, &hints, &res0); + error = getaddrinfo(host, port, &hints, &res0); if (error) { - warnx("%s", gai_strerror(error)); + warnx("Can't lookup `%s:%s': %s", host, port, + (error == EAI_SYSTEM) ? strerror(errno) + : gai_strerror(error)); code = -1; return (0); } @@ -195,53 +195,26 @@ hookup(char *host, char *port) hostname = hostnamebuf; for (res = res0; res; res = res->ai_next) { - /* - * make sure that ai_addr is NOT an IPv4 mapped address. - * IPv4 mapped address complicates too many things in FTP - * protocol handling, as FTP protocol is defined differently - * between IPv4 and IPv6. - * - * This may not be the best way to handle this situation, - * since the semantics of IPv4 mapped address is defined in - * the kernel. There are configurations where we should use - * IPv4 mapped address as native IPv6 address, not as - * "an IPv6 address that embeds IPv4 address" (namely, SIIT). - * - * More complete solution would be to have an additional - * getsockopt to grab "real" peername/sockname. "real" - * peername/sockname will be AF_INET if IPv4 mapped address - * is used to embed IPv4 address, and will be AF_INET6 if - * we use it as native. What a mess! - */ + char hname[NI_MAXHOST], sname[NI_MAXSERV]; + ai_unmapped(res); -#if 0 /*old behavior*/ - if (res != res0) /* not on the first address */ -#else - if (res0->ai_next) /* if we have multiple possibilities */ -#endif - { - if (getnameinfo(res->ai_addr, res->ai_addrlen, - hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST)) - strlcpy(hbuf, "?", sizeof(hbuf)); - fprintf(ttyout, "Trying %s...\n", hbuf); + if (getnameinfo(res->ai_addr, res->ai_addrlen, + hname, sizeof(hname), sname, sizeof(sname), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) { + strlcpy(hname, "?", sizeof(hname)); + strlcpy(sname, "?", sizeof(sname)); + } + if (verbose && res0->ai_next) { + /* if we have multiple possibilities */ + fprintf(ttyout, "Trying %s:%s ...\n", hname, sname); } - ((struct sockaddr_in *)res->ai_addr)->sin_port = htons(portnum); s = socket(res->ai_family, SOCK_STREAM, res->ai_protocol); if (s < 0) { - cause = "socket"; + warn("Can't create socket for connection to `%s:%s'", + hname, sname); continue; } - error = xconnect(s, res->ai_addr, res->ai_addrlen); - if (error) { - /* this "if" clause is to prevent print warning twice */ - if (res->ai_next) { - if (getnameinfo(res->ai_addr, res->ai_addrlen, - hbuf, sizeof(hbuf), NULL, 0, - NI_NUMERICHOST)) - strlcpy(hbuf, "?", sizeof(hbuf)); - warn("connect to address %s", hbuf); - } - cause = "connect"; + if (ftp_connect(s, res->ai_addr, res->ai_addrlen) < 0) { close(s); s = -1; continue; @@ -251,7 +224,7 @@ hookup(char *host, char *port) break; } if (s < 0) { - warn("%s", cause); + warnx("Can't connect to `%s:%s'", host, port); code = -1; freeaddrinfo(res0); return 0; @@ -263,7 +236,8 @@ hookup(char *host, char *port) len = hisctladdr.su_len; if (getsockname(s, (struct sockaddr *)&myctladdr.si_su, &len) == -1) { - warn("getsockname"); + warn("Can't determine my address of connection to `%s:%s'", + host, port); code = -1; goto bad; } @@ -274,8 +248,7 @@ hookup(char *host, char *port) int tos = IPTOS_LOWDELAY; if (setsockopt(s, IPPROTO_IP, IP_TOS, (void *)&tos, sizeof(tos)) == -1) { - if (debug) - warn("setsockopt %s (ignored)", + DWARN("setsockopt %s (ignored)", "IPTOS_LOWDELAY"); } } @@ -283,7 +256,7 @@ hookup(char *host, char *port) cin = fdopen(s, "r"); cout = fdopen(s, "w"); if (cin == NULL || cout == NULL) { - warnx("fdopen failed."); + warnx("Can't fdopen socket"); if (cin) (void)fclose(cin); if (cout) @@ -304,8 +277,7 @@ hookup(char *host, char *port) if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (void *)&on, sizeof(on)) == -1) { - if (debug) - warn("setsockopt %s (ignored)", "SO_OOBINLINE"); + DWARN("setsockopt %s (ignored)", "SO_OOBINLINE"); } return (hostname); @@ -351,7 +323,8 @@ command(const char *fmt, ...) int r; sigfunc oldsigint; - if (debug) { +#ifndef NO_DEBUG + if (ftp_debug) { fputs("---> ", ttyout); va_start(ap, fmt); if (strncmp("PASS ", fmt, 5) == 0) @@ -363,8 +336,9 @@ command(const char *fmt, ...) va_end(ap); putc('\n', ttyout); } +#endif if (cout == NULL) { - warnx("No control connection for command."); + warnx("No control connection for command"); code = -1; return (0); } @@ -386,11 +360,17 @@ command(const char *fmt, ...) return (r); } +static const char *m421[] = { + "remote server timed out. Connection closed", + "user interrupt. Connection closed", + "remote server has closed connection", +}; + int getreply(int expecteof) { char current_line[BUFSIZ]; /* last line of previous reply */ - int c, n, line; + int c, n, lineno; int dig; int originalcode = 0, continuation = 0; sigfunc oldsigint, oldsigalrm; @@ -403,10 +383,11 @@ getreply(int expecteof) oldsigint = xsignal(SIGINT, cmdabort); oldsigalrm = xsignal(SIGALRM, cmdtimeout); - for (line = 0 ;; line++) { + for (lineno = 0 ;; lineno++) { dig = n = code = 0; cp = current_line; - while (alarmtimer(60),((c = getc(cin)) != '\n')) { + while (alarmtimer(quit_time ? quit_time : 60), + ((c = getc(cin)) != '\n')) { if (c == IAC) { /* handle telnet commands */ switch (c = getc(cin)) { case WILL: @@ -445,18 +426,15 @@ getreply(int expecteof) cpend = 0; lostpeer(0); if (verbose) { + size_t midx; if (reply_timeoutflag) - fputs( - "421 Service not available, remote server timed out. Connection closed\n", - ttyout); + midx = 0; else if (reply_abrtflag) - fputs( - "421 Service not available, user interrupt. Connection closed.\n", - ttyout); + midx = 1; else - fputs( - "421 Service not available, remote server has closed connection.\n", - ttyout); + midx = 2; + (void)fprintf(ttyout, + "421 Service not available, %s.\n", m421[midx]); (void)fflush(ttyout); } code = 421; @@ -505,15 +483,15 @@ getreply(int expecteof) if (verbose > 0 || ((verbose > -1 && n == '5') && (n < '5' || !retry_connect))) { (void)putc(c, ttyout); - (void)fflush (ttyout); + (void)fflush(ttyout); } if (cp[-1] == '\r') cp[-1] = '\0'; *cp = '\0'; - if (line == 0) + if (lineno == 0) (void)strlcpy(reply_string, current_line, sizeof(reply_string)); - if (line > 0 && code == 0 && reply_callback != NULL) + if (lineno > 0 && code == 0 && reply_callback != NULL) (*reply_callback)(current_line); if (continuation && code != originalcode) { if (originalcode == 0) @@ -537,14 +515,14 @@ getreply(int expecteof) } static int -empty(FILE *cin, FILE *din, int sec) +empty(FILE *ecin, FILE *din, int sec) { int nr, nfd; struct pollfd pfd[2]; nfd = 0; - if (cin) { - pfd[nfd].fd = fileno(cin); + if (ecin) { + pfd[nfd].fd = fileno(ecin); pfd[nfd++].events = POLLIN; } @@ -553,12 +531,12 @@ empty(FILE *cin, FILE *din, int sec) pfd[nfd++].events = POLLIN; } - if ((nr = xpoll(pfd, nfd, sec * 1000)) <= 0) + if ((nr = ftp_poll(pfd, nfd, sec * 1000)) <= 0) return nr; nr = 0; nfd = 0; - if (cin) + if (ecin) nr |= (pfd[nfd++].revents & POLLIN) ? 1 : 0; if (din) nr |= (pfd[nfd++].revents & POLLIN) ? 2 : 0; @@ -585,8 +563,7 @@ abortxfer(int notused) strlcpy(msgbuf, "\nsend", sizeof(msgbuf)); break; default: - errx(1, "abortxfer called with unknown direction `%s'", - direction); + errx(1, "abortxfer: unknown direction `%s'", direction); } len = strlcat(msgbuf, " aborted. Waiting for remote to finish abort.\n", sizeof(msgbuf)); @@ -594,30 +571,108 @@ abortxfer(int notused) siglongjmp(xferabort, 1); } +/* + * Read data from infd & write to outfd, using buf/bufsize as the temporary + * buffer, dealing with short writes. + * If rate_limit != 0, rate-limit the transfer. + * If hash_interval != 0, fputc('c', ttyout) every hash_interval bytes. + * Updates global variables: bytes. + * Returns 0 if ok, 1 if there was a read error, 2 if there was a write error. + * In the case of error, errno contains the appropriate error code. + */ +static int +copy_bytes(int infd, int outfd, char *buf, size_t bufsize, + int rate_limit, int hash_interval) +{ + volatile off_t hashc; + ssize_t inc, outc; + char *bufp; + struct timeval tvthen, tvnow, tvdiff; + off_t bufrem, bufchunk; + int serr; + + hashc = hash_interval; + if (rate_limit) + bufchunk = rate_limit; + else + bufchunk = bufsize; + + while (1) { + if (rate_limit) { + (void)gettimeofday(&tvthen, NULL); + } + errno = 0; + inc = outc = 0; + /* copy bufchunk at a time */ + bufrem = bufchunk; + while (bufrem > 0) { + inc = read(infd, buf, MIN((off_t)bufsize, bufrem)); + if (inc <= 0) + goto copy_done; + bytes += inc; + bufrem -= inc; + bufp = buf; + while (inc > 0) { + outc = write(outfd, bufp, inc); + if (outc < 0) + goto copy_done; + inc -= outc; + bufp += outc; + } + if (hash_interval) { + while (bytes >= hashc) { + (void)putc('#', ttyout); + hashc += hash_interval; + } + (void)fflush(ttyout); + } + } + if (rate_limit) { /* rate limited; wait if necessary */ + while (1) { + (void)gettimeofday(&tvnow, NULL); + timersub(&tvnow, &tvthen, &tvdiff); + if (tvdiff.tv_sec > 0) + break; + usleep(1000000 - tvdiff.tv_usec); + } + } + } + + copy_done: + serr = errno; + if (hash_interval && bytes > 0) { + if (bytes < hash_interval) + (void)putc('#', ttyout); + (void)putc('\n', ttyout); + (void)fflush(ttyout); + } + errno = serr; + if (inc == -1) + return 1; + if (outc == -1) + return 2; + + return 0; +} + void sendrequest(const char *cmd, const char *local, const char *remote, int printnames) { struct stat st; - int c, d; - FILE *fin, *dout; - int (*closefunc)(FILE *); - sigfunc oldintr, oldintp; - volatile off_t hashbytes; - char *lmode, *bufp; + int c; + FILE *volatile fin; + FILE *volatile dout; + int (*volatile closefunc)(FILE *); + sigfunc volatile oldintr; + sigfunc volatile oldintp; + off_t volatile hashbytes; + int hash_interval; + const char *lmode; static size_t bufsize; static char *buf; int oprogress; -#ifdef __GNUC__ /* to shut up gcc warnings */ - (void)&fin; - (void)&dout; - (void)&closefunc; - (void)&oldintr; - (void)&oldintp; - (void)&lmode; -#endif - hashbytes = mark; direction = "sent"; dout = NULL; @@ -625,7 +680,7 @@ sendrequest(const char *cmd, const char *local, const char *remote, filesize = -1; oprogress = progress; if (verbose && printnames) { - if (local && *local != '-') + if (*local != '-') fprintf(ttyout, "local: %s ", local); if (remote) fprintf(ttyout, "remote: %s\n", remote); @@ -655,7 +710,7 @@ sendrequest(const char *cmd, const char *local, const char *remote, oldintp = xsignal(SIGPIPE, SIG_IGN); fin = popen(local + 1, "r"); if (fin == NULL) { - warn("%s", local + 1); + warn("Can't execute `%s'", local + 1); code = -1; goto cleanupsend; } @@ -664,7 +719,7 @@ sendrequest(const char *cmd, const char *local, const char *remote, } else { fin = fopen(local, "r"); if (fin == NULL) { - warn("local: %s", local); + warn("Can't open `%s'", local); code = -1; goto cleanupsend; } @@ -698,7 +753,7 @@ sendrequest(const char *cmd, const char *local, const char *remote, break; } if (rc < 0) { - warn("local: %s", local); + warn("Can't seek to restart `%s'", local); goto cleanupsend; } if (command("REST " LLF, (LLT)restart_point) != CONTINUE) @@ -717,90 +772,28 @@ sendrequest(const char *cmd, const char *local, const char *remote, if (dout == NULL) goto abort; - if (sndbuf_size > bufsize) { + if ((size_t)sndbuf_size > bufsize) { if (buf) (void)free(buf); bufsize = sndbuf_size; - buf = xmalloc(bufsize); + buf = ftp_malloc(bufsize); } progressmeter(-1); oldintp = xsignal(SIGPIPE, SIG_IGN); + hash_interval = (hash && (!progress || filesize < 0)) ? mark : 0; switch (curtype) { case TYPE_I: case TYPE_L: - if (rate_put) { /* rate limited */ - while (1) { - struct timeval then, now, td; - off_t bufrem; - - (void)gettimeofday(&then, NULL); - errno = c = d = 0; - bufrem = rate_put; - while (bufrem > 0) { - if ((c = read(fileno(fin), buf, - MIN(bufsize, bufrem))) <= 0) - goto senddone; - bytes += c; - bufrem -= c; - for (bufp = buf; c > 0; - c -= d, bufp += d) - if ((d = write(fileno(dout), - bufp, c)) <= 0) - break; - if (d < 0) - goto senddone; - if (hash && - (!progress || filesize < 0) ) { - while (bytes >= hashbytes) { - (void)putc('#', ttyout); - hashbytes += mark; - } - (void)fflush(ttyout); - } - } - while (1) { - (void)gettimeofday(&now, NULL); - timersub(&now, &then, &td); - if (td.tv_sec > 0) - break; - usleep(1000000 - td.tv_usec); - } - } - } else { /* simpler/faster; no rate limit */ - while (1) { - errno = c = d = 0; - if ((c = read(fileno(fin), buf, bufsize)) <= 0) - goto senddone; - bytes += c; - for (bufp = buf; c > 0; c -= d, bufp += d) - if ((d = write(fileno(dout), bufp, c)) - <= 0) - break; - if (d < 0) - goto senddone; - if (hash && (!progress || filesize < 0) ) { - while (bytes >= hashbytes) { - (void)putc('#', ttyout); - hashbytes += mark; - } - (void)fflush(ttyout); - } - } - } - senddone: - if (hash && (!progress || filesize < 0) && bytes > 0) { - if (bytes < mark) - (void)putc('#', ttyout); - (void)putc('\n', ttyout); - } - if (c < 0) - warn("local: %s", local); - if (d < 0) { + c = copy_bytes(fileno(fin), fileno(dout), buf, bufsize, + rate_put, hash_interval); + if (c == 1) { + warn("Reading `%s'", local); + } else if (c == 2) { if (errno != EPIPE) - warn("netout"); + warn("Writing to network"); bytes = -1; } break; @@ -808,8 +801,7 @@ sendrequest(const char *cmd, const char *local, const char *remote, case TYPE_A: while ((c = getc(fin)) != EOF) { if (c == '\n') { - while (hash && (!progress || filesize < 0) && - (bytes >= hashbytes)) { + while (hash_interval && bytes >= hashbytes) { (void)putc('#', ttyout); (void)fflush(ttyout); hashbytes += mark; @@ -821,23 +813,23 @@ sendrequest(const char *cmd, const char *local, const char *remote, } (void)putc(c, dout); bytes++; -#if 0 /* this violates RFC */ +#if 0 /* this violates RFC0959 */ if (c == '\r') { (void)putc('\0', dout); bytes++; } #endif } - if (hash && (!progress || filesize < 0)) { + if (hash_interval) { if (bytes < hashbytes) (void)putc('#', ttyout); (void)putc('\n', ttyout); } if (ferror(fin)) - warn("local: %s", local); + warn("Reading `%s'", local); if (ferror(dout)) { if (errno != EPIPE) - warn("netout"); + warn("Writing to network"); bytes = -1; } break; @@ -894,32 +886,28 @@ sendrequest(const char *cmd, const char *local, const char *remote, } void -recvrequest(const char *cmd, const char *local, const char *remote, +recvrequest(const char *cmd, const char *volatile local, const char *remote, const char *lmode, int printnames, int ignorespecial) { - FILE *fout, *din; - int (*closefunc)(FILE *); - sigfunc oldintr, oldintp; + FILE *volatile fout; + FILE *volatile din; + int (*volatile closefunc)(FILE *); + sigfunc volatile oldintr; + sigfunc volatile oldintp; int c, d; - volatile int is_retr, tcrflag, bare_lfs; + int volatile is_retr; + int volatile tcrflag; + int volatile bare_lfs; static size_t bufsize; static char *buf; - volatile off_t hashbytes; + off_t volatile hashbytes; + int hash_interval; struct stat st; time_t mtime; struct timeval tval[2]; int oprogress; int opreserve; -#ifdef __GNUC__ /* to shut up gcc warnings */ - (void)&local; - (void)&fout; - (void)&din; - (void)&closefunc; - (void)&oldintr; - (void)&oldintp; -#endif - fout = NULL; din = NULL; hashbytes = mark; @@ -931,7 +919,7 @@ recvrequest(const char *cmd, const char *local, const char *remote, opreserve = preserve; is_retr = (strcmp(cmd, "RETR") == 0); if (is_retr && verbose && printnames) { - if (local && (ignorespecial || *local != '-')) + if (ignorespecial || *local != '-') fprintf(ttyout, "local: %s ", local); if (remote) fprintf(ttyout, "remote: %s\n", remote); @@ -957,7 +945,7 @@ recvrequest(const char *cmd, const char *local, const char *remote, char *dir = strrchr(local, '/'); if (errno != ENOENT && errno != EACCES) { - warn("local: %s", local); + warn("Can't access `%s'", local); code = -1; goto cleanuprecv; } @@ -968,13 +956,13 @@ recvrequest(const char *cmd, const char *local, const char *remote, if (dir != NULL) *dir = '/'; if (d < 0) { - warn("local: %s", local); + warn("Can't access `%s'", local); code = -1; goto cleanuprecv; } if (!runique && errno == EACCES && chmod(local, (S_IRUSR|S_IWUSR)) < 0) { - warn("local: %s", local); + warn("Can't chmod `%s'", local); code = -1; goto cleanuprecv; } @@ -1026,7 +1014,7 @@ recvrequest(const char *cmd, const char *local, const char *remote, oldintp = xsignal(SIGPIPE, SIG_IGN); fout = popen(local + 1, "w"); if (fout == NULL) { - warn("%s", local+1); + warn("Can't execute `%s'", local+1); goto abort; } progress = 0; @@ -1035,7 +1023,7 @@ recvrequest(const char *cmd, const char *local, const char *remote, } else { fout = fopen(local, lmode); if (fout == NULL) { - warn("local: %s", local); + warn("Can't open `%s'", local); goto abort; } closefunc = fclose; @@ -1045,14 +1033,15 @@ recvrequest(const char *cmd, const char *local, const char *remote, progress = 0; preserve = 0; } - if (rcvbuf_size > bufsize) { + if ((size_t)rcvbuf_size > bufsize) { if (buf) (void)free(buf); bufsize = rcvbuf_size; - buf = xmalloc(bufsize); + buf = ftp_malloc(bufsize); } progressmeter(-1); + hash_interval = (hash && (!progress || filesize < 0)) ? mark : 0; switch (curtype) { @@ -1060,76 +1049,17 @@ recvrequest(const char *cmd, const char *local, const char *remote, case TYPE_L: if (is_retr && restart_point && lseek(fileno(fout), restart_point, SEEK_SET) < 0) { - warn("local: %s", local); + warn("Can't seek to restart `%s'", local); goto cleanuprecv; } - if (rate_get) { /* rate limiting */ - while (1) { - struct timeval then, now, td; - off_t bufrem; - - (void)gettimeofday(&then, NULL); - errno = c = d = 0; - for (bufrem = rate_get; bufrem > 0; ) { - if ((c = read(fileno(din), buf, - MIN(bufsize, bufrem))) <= 0) - goto recvdone; - bytes += c; - bufrem -=c; - if ((d = write(fileno(fout), buf, c)) - != c) - goto recvdone; - if (hash && - (!progress || filesize < 0)) { - while (bytes >= hashbytes) { - (void)putc('#', ttyout); - hashbytes += mark; - } - (void)fflush(ttyout); - } - } - /* sleep until time is up */ - while (1) { - (void)gettimeofday(&now, NULL); - timersub(&now, &then, &td); - if (td.tv_sec > 0) - break; - usleep(1000000 - td.tv_usec); - } - } - } else { /* faster code (no limiting) */ - while (1) { - errno = c = d = 0; - if ((c = read(fileno(din), buf, bufsize)) <= 0) - goto recvdone; - bytes += c; - if ((d = write(fileno(fout), buf, c)) != c) - goto recvdone; - if (hash && (!progress || filesize < 0)) { - while (bytes >= hashbytes) { - (void)putc('#', ttyout); - hashbytes += mark; - } - (void)fflush(ttyout); - } - } - } - recvdone: - if (hash && (!progress || filesize < 0) && bytes > 0) { - if (bytes < mark) - (void)putc('#', ttyout); - (void)putc('\n', ttyout); - } - if (c < 0) { + c = copy_bytes(fileno(din), fileno(fout), buf, bufsize, + rate_get, hash_interval); + if (c == 1) { if (errno != EPIPE) - warn("netin"); + warn("Reading from network"); bytes = -1; - } - if (d < c) { - if (d < 0) - warn("local: %s", local); - else - warnx("%s: short write", local); + } else if (c == 2) { + warn("Writing `%s'", local); } break; @@ -1148,7 +1078,7 @@ recvrequest(const char *cmd, const char *local, const char *remote, } if (fseeko(fout, (off_t)0, SEEK_CUR) < 0) { done: - warn("local: %s", local); + warn("Can't seek to restart `%s'", local); goto cleanuprecv; } } @@ -1156,8 +1086,7 @@ recvrequest(const char *cmd, const char *local, const char *remote, if (c == '\n') bare_lfs++; while (c == '\r') { - while (hash && (!progress || filesize < 0) && - (bytes >= hashbytes)) { + while (hash_interval && bytes >= hashbytes) { (void)putc('#', ttyout); (void)fflush(ttyout); hashbytes += mark; @@ -1180,18 +1109,18 @@ recvrequest(const char *cmd, const char *local, const char *remote, contin2: ; } break2: - if (hash && (!progress || filesize < 0)) { + if (hash_interval) { if (bytes < hashbytes) (void)putc('#', ttyout); (void)putc('\n', ttyout); } if (ferror(din)) { if (errno != EPIPE) - warn("netin"); + warn("Reading from network"); bytes = -1; } if (ferror(fout)) - warn("local: %s", local); + warn("Writing `%s'", local); break; } @@ -1221,7 +1150,8 @@ recvrequest(const char *cmd, const char *local, const char *remote, if (utimes(local, tval) == -1) { fprintf(ttyout, "Can't change modification time on %s to %s", - local, asctime(localtime(&mtime))); + local, + rfc2822time(localtime(&mtime))); } } } @@ -1230,7 +1160,7 @@ recvrequest(const char *cmd, const char *local, const char *remote, abort: /* - * abort using RFC 959 recommended IP,SYNC sequence + * abort using RFC0959 recommended IP,SYNC sequence */ if (! sigsetjmp(xferabort, 1)) { /* this is the first call */ @@ -1274,38 +1204,48 @@ initconn(void) int result, tmpno = 0; int on = 1; int error; - u_int addr[16], port[2]; - u_int af, hal, pal; + unsigned int addr[16], port[2]; + unsigned int af, hal, pal; socklen_t len; - char *pasvcmd = NULL; + const char *pasvcmd = NULL; + int overbose; #ifdef INET6 - if (myctladdr.su_family == AF_INET6 && debug && +#ifndef NO_DEBUG + if (myctladdr.su_family == AF_INET6 && ftp_debug && (IN6_IS_ADDR_LINKLOCAL(&myctladdr.si_su.su_sin6.sin6_addr) || IN6_IS_ADDR_SITELOCAL(&myctladdr.si_su.su_sin6.sin6_addr))) { - warnx("use of scoped address can be troublesome"); + warnx("Use of scoped addresses can be troublesome"); } #endif +#endif + reinit: if (passivemode) { data_addr = myctladdr; data = socket(data_addr.su_family, SOCK_STREAM, 0); if (data < 0) { - warn("socket"); + warn("Can't create socket for data connection"); return (1); } if ((options & SO_DEBUG) && setsockopt(data, SOL_SOCKET, SO_DEBUG, (void *)&on, sizeof(on)) == -1) { - if (debug) - warn("setsockopt %s (ignored)", "SO_DEBUG"); + DWARN("setsockopt %s (ignored)", "SO_DEBUG"); } result = COMPLETE + 1; switch (data_addr.su_family) { case AF_INET: if (epsv4 && !epsv4bad) { pasvcmd = "EPSV"; + overbose = verbose; + if (ftp_debug == 0) + verbose = -1; result = command("EPSV"); + verbose = overbose; + if (verbose > 0 && + (result == COMPLETE || !connected)) + fprintf(ttyout, "%s\n", reply_string); if (!connected) return (1); /* @@ -1320,10 +1260,8 @@ initconn(void) } if (result != COMPLETE) { epsv4bad = 1; - if (debug) - fputs( - "disabling epsv4 for this connection\n", - ttyout); + DPRINTF("disabling epsv4 for this " + "connection\n"); } } if (result != COMPLETE) { @@ -1335,16 +1273,33 @@ initconn(void) break; #ifdef INET6 case AF_INET6: - pasvcmd = "EPSV"; - result = command("EPSV"); - if (!connected) - return (1); - /* this code is to be friendly with broken BSDI ftpd */ - if (code / 10 == 22 && code != 229) { - fputs( -"wrong server: return code must be 229\n", - ttyout); - result = COMPLETE + 1; + if (epsv6 && !epsv6bad) { + pasvcmd = "EPSV"; + overbose = verbose; + if (ftp_debug == 0) + verbose = -1; + result = command("EPSV"); + verbose = overbose; + if (verbose > 0 && + (result == COMPLETE || !connected)) + fprintf(ttyout, "%s\n", reply_string); + if (!connected) + return (1); + /* + * this code is to be friendly with + * broken BSDI ftpd + */ + if (code / 10 == 22 && code != 229) { + fputs( + "wrong server: return code must be 229\n", + ttyout); + result = COMPLETE + 1; + } + if (result != COMPLETE) { + epsv6bad = 1; + DPRINTF("disabling epsv6 for this " + "connection\n"); + } } if (result != COMPLETE) { pasvcmd = "LPSV"; @@ -1476,7 +1431,7 @@ initconn(void) data_addr.su_family = AF_INET6; data_addr.su_len = sizeof(struct sockaddr_in6); { - int i; + size_t i; for (i = 0; i < sizeof(struct in6_addr); i++) { data_addr.si_su.su_sin6.sin6_addr.s6_addr[i] = UC(addr[i]); @@ -1516,8 +1471,8 @@ initconn(void) } else goto bad; - while (xconnect(data, (struct sockaddr *)&data_addr.si_su, - data_addr.su_len) < 0) { + if (ftp_connect(data, (struct sockaddr *)&data_addr.si_su, + data_addr.su_len) < 0) { if (activefallback) { (void)close(data); data = -1; @@ -1527,7 +1482,6 @@ initconn(void) #endif goto reinit; } - warn("connect for data channel"); goto bad; } #ifdef IPTOS_THROUGHPUT @@ -1535,9 +1489,8 @@ initconn(void) on = IPTOS_THROUGHPUT; if (setsockopt(data, IPPROTO_IP, IP_TOS, (void *)&on, sizeof(on)) == -1) { - if (debug) - warn("setsockopt %s (ignored)", - "IPTOS_THROUGHPUT"); + DWARN("setsockopt %s (ignored)", + "IPTOS_THROUGHPUT"); } } #endif @@ -1552,7 +1505,7 @@ initconn(void) (void)close(data); data = socket(data_addr.su_family, SOCK_STREAM, 0); if (data < 0) { - warn("socket"); + warn("Can't create socket for data connection"); if (tmpno) sendport = 1; return (1); @@ -1560,33 +1513,31 @@ initconn(void) if (!sendport) if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)) == -1) { - warn("setsockopt %s", "SO_REUSEADDR"); + warn("Can't set SO_REUSEADDR on data connection"); goto bad; } if (bind(data, (struct sockaddr *)&data_addr.si_su, data_addr.su_len) < 0) { - warn("bind"); + warn("Can't bind for data connection"); goto bad; } if ((options & SO_DEBUG) && setsockopt(data, SOL_SOCKET, SO_DEBUG, (void *)&on, sizeof(on)) == -1) { - if (debug) - warn("setsockopt %s (ignored)", "SO_DEBUG"); + DWARN("setsockopt %s (ignored)", "SO_DEBUG"); } len = sizeof(data_addr.si_su); memset((char *)&data_addr, 0, sizeof (data_addr)); if (getsockname(data, (struct sockaddr *)&data_addr.si_su, &len) == -1) { - warn("getsockname"); + warn("Can't determine my address of data connection"); goto bad; } data_addr.su_len = len; - if (xlisten(data, 1) < 0) - warn("listen"); + if (ftp_listen(data, 1) < 0) + warn("Can't listen to data connection"); if (sendport) { char hname[NI_MAXHOST], sname[NI_MAXSERV]; - int af; struct sockinet tmp; switch (data_addr.su_family) { @@ -1598,6 +1549,10 @@ initconn(void) /* FALLTHROUGH */ #ifdef INET6 case AF_INET6: + if (!epsv6 || epsv6bad) { + result = COMPLETE + 1; + break; + } #endif af = (data_addr.su_family == AF_INET) ? 1 : 2; tmp = data_addr; @@ -1610,16 +1565,21 @@ initconn(void) sizeof(sname), NI_NUMERICHOST | NI_NUMERICSERV)) { result = ERROR; } else { - result = command("EPRT |%d|%s|%s|", af, hname, + overbose = verbose; + if (ftp_debug == 0) + verbose = -1; + result = command("EPRT |%u|%s|%s|", af, hname, sname); + verbose = overbose; + if (verbose > 0 && + (result == COMPLETE || !connected)) + fprintf(ttyout, "%s\n", reply_string); if (!connected) return (1); if (result != COMPLETE) { epsv4bad = 1; - if (debug) - fputs( - "disabling epsv4 for this connection\n", - ttyout); + DPRINTF("disabling epsv4 for this " + "connection\n"); } } break; @@ -1672,10 +1632,9 @@ initconn(void) if (data_addr.su_family == AF_INET) { on = IPTOS_THROUGHPUT; if (setsockopt(data, IPPROTO_IP, IP_TOS, - (void *)&on, sizeof(on)) == -1) - if (debug) - warn("setsockopt %s (ignored)", - "IPTOS_THROUGHPUT"); + (void *)&on, sizeof(on)) == -1) { + DWARN("setsockopt %s (ignored)", "IPTOS_THROUGHPUT"); + } } #endif return (0); @@ -1720,14 +1679,14 @@ dataconn(const char *lmode) timeout = td.tv_sec * 1000 + td.tv_usec/1000; if (timeout < 0) timeout = 0; - rv = xpoll(pfd, 1, timeout); + rv = ftp_poll(pfd, 1, timeout); } while (rv == -1 && errno == EINTR); /* loop until poll ! EINTR */ if (rv == -1) { - warn("poll waiting before accept"); + warn("Can't poll waiting before accept"); goto dataconn_failed; } if (rv == 0) { - warn("poll timeout waiting before accept"); + warnx("Poll timeout waiting before accept"); goto dataconn_failed; } @@ -1737,7 +1696,7 @@ dataconn(const char *lmode) s = accept(data, (struct sockaddr *) &from.si_su, &fromlen); } while (s == -1 && errno == EINTR); /* loop until accept ! EINTR */ if (s == -1) { - warn("accept"); + warn("Can't accept data connection"); goto dataconn_failed; } @@ -1751,9 +1710,7 @@ dataconn(const char *lmode) int tos = IPTOS_THROUGHPUT; if (setsockopt(s, IPPROTO_IP, IP_TOS, (void *)&tos, sizeof(tos)) == -1) { - if (debug) - warn("setsockopt %s (ignored)", - "IPTOS_THROUGHPUT"); + DWARN("setsockopt %s (ignored)", "IPTOS_THROUGHPUT"); } } #endif @@ -1880,15 +1837,10 @@ abortpt(int notused) void proxtrans(const char *cmd, const char *local, const char *remote) { - sigfunc oldintr; + sigfunc volatile oldintr; int prox_type, nfnd; - volatile int secndflag; - char *cmd2; - -#ifdef __GNUC__ /* to shut up gcc warnings */ - (void)&oldintr; - (void)&cmd2; -#endif + int volatile secndflag; + const char *volatile cmd2; oldintr = NULL; secndflag = 0; @@ -1992,7 +1944,7 @@ proxtrans(const char *cmd, const char *local, const char *remote) if (cpend) { if ((nfnd = empty(cin, NULL, 10)) <= 0) { if (nfnd < 0) - warn("abort"); + warn("Error aborting proxy command"); if (ptabflg) code = -1; lostpeer(0); @@ -2014,13 +1966,13 @@ reset(int argc, char *argv[]) int nfnd = 1; if (argc == 0 && argv != NULL) { - fprintf(ttyout, "usage: %s\n", argv[0]); + UPRINTF("usage: %s\n", argv[0]); code = -1; return; } while (nfnd > 0) { if ((nfnd = empty(cin, NULL, 0)) < 0) { - warn("reset"); + warn("Error resetting connection"); code = -1; lostpeer(0); } else if (nfnd) @@ -2042,7 +1994,7 @@ gunique(const char *local) if (cp) *cp = '/'; if (d < 0) { - warn("local: %s", local); + warn("Can't access `%s'", local); return (NULL); } len = strlcpy(new, local, sizeof(new)); @@ -2102,7 +2054,7 @@ abort_remote(FILE *din) int nfnd; if (cout == NULL) { - warnx("Lost control connection for abort."); + warnx("Lost control connection for abort"); if (ptabflg) code = -1; lostpeer(0); @@ -2116,12 +2068,12 @@ abort_remote(FILE *din) buf[1] = IP; buf[2] = IAC; if (send(fileno(cout), buf, 3, MSG_OOB) != 3) - warn("abort"); + warn("Can't send abort message"); fprintf(cout, "%cABOR\r\n", DM); (void)fflush(cout); if ((nfnd = empty(cin, din, 10)) <= 0) { if (nfnd < 0) - warn("abort"); + warn("Can't send abort message"); if (ptabflg) code = -1; lostpeer(0); @@ -2137,6 +2089,24 @@ abort_remote(FILE *din) (void)getreply(0); } +/* + * Ensure that ai->ai_addr is NOT an IPv4 mapped address. + * IPv4 mapped address complicates too many things in FTP + * protocol handling, as FTP protocol is defined differently + * between IPv4 and IPv6. + * + * This may not be the best way to handle this situation, + * since the semantics of IPv4 mapped address is defined in + * the kernel. There are configurations where we should use + * IPv4 mapped address as native IPv6 address, not as + * "an IPv6 address that embeds IPv4 address" (namely, SIIT). + * + * More complete solution would be to have an additional + * getsockopt to grab "real" peername/sockname. "real" + * peername/sockname will be AF_INET if IPv4 mapped address + * is used to embed IPv4 address, and will be AF_INET6 if + * we use it as native. What a mess! + */ void ai_unmapped(struct addrinfo *ai) { @@ -2162,10 +2132,18 @@ ai_unmapped(struct addrinfo *ai) sin.sin_port = sin6->sin6_port; ai->ai_family = AF_INET; -#if HAVE_SOCKADDR_SA_LEN +#if defined(HAVE_STRUCT_SOCKADDR_IN_SIN_LEN) sin.sin_len = len; #endif memcpy(ai->ai_addr, &sin, len); ai->ai_addrlen = len; #endif } + +#ifdef NO_USAGE +void +xusage(void) +{ + fputs("Usage error\n", ttyout); +} +#endif |