/*-
* Copyright (c) 1996-2021 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Luke Mewburn.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) 1985, 1989, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 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. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (C) 1997 and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 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. Neither the name of the project 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 PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
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, lineno;
int dig;
int originalcode = 0, continuation = 0;
sigfunc oldsigint, oldsigalrm;
int pflag = 0;
char *cp, *pt = pasv;
sigint_raised = 1;
alarmtimer(0);
mflag = 0;
abrtflag = 0;
switch (direction[0]) {
case 'r':
strlcpy(msgbuf, "\nreceive", sizeof(msgbuf));
break;
case 's':
strlcpy(msgbuf, "\nsend", sizeof(msgbuf));
break;
default:
errx(1, "abortxfer: unknown direction `%s'", direction);
}
len = strlcat(msgbuf, " aborted. Waiting for remote to finish abort.\n",
sizeof(msgbuf));
write(fileno(ttyout), msgbuf, len);
siglongjmp(xferabort, 1);
}
/*
* Read data from infd & write to outfd, using buf/bufsize as the temporary
* buffer, dealing with short reads or 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;
case TYPE_I:
case TYPE_L:
if (is_retr && restart_point &&
lseek(fileno(fout), restart_point, SEEK_SET) < 0) {
warn("Can't seek to restart `%s'", local);
goto cleanuprecv;
}
c = copy_bytes(fileno(din), fileno(fout), buf, bufsize,
rate_get, hash_interval);
if (c == 1) {
if (errno != EPIPE)
warn("Reading from network");
bytes = -1;
} else if (c == 2) {
warn("Writing `%s'", local);
}
break;
case TYPE_A:
if (is_retr && restart_point) {
int ch;
off_t i;
if (fseeko(fout, (off_t)0, SEEK_SET) < 0)
goto done;
for (i = 0; i++ < restart_point;) {
if ((ch = getc(fout)) == EOF)
goto done;
if (ch == '\n')
i++;
}
if (fseeko(fout, (off_t)0, SEEK_CUR) < 0) {
done:
warn("Can't seek to restart `%s'", local);
goto cleanuprecv;
}
}
while ((c = getc(din)) != EOF) {
if (c == '\n')
bare_lfs++;
while (c == '\r') {
while (hash_interval && bytes >= hashbytes) {
(void)putc('#', ttyout);
(void)fflush(ttyout);
hashbytes += mark;
}
bytes++;
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;
}
}
(void)putc(c, fout);
bytes++;
contin2: ;
}
break2:
if (hash_interval) {
if (bytes < hashbytes)
(void)putc('#', ttyout);
(void)putc('\n', ttyout);
}
if (ferror(din)) {
if (errno != EPIPE)
warn("Reading from network");
bytes = -1;
}
if (ferror(fout))
warn("Writing `%s'", local);
break;
}
progressmeter(1);
if (closefunc != NULL) {
(*closefunc)(fout);
fout = NULL;
}
(void)fclose(din);
din = NULL;
(void)getreply(0);
if (bare_lfs) {
fprintf(ttyout,
"WARNING! %d bare linefeeds received in ASCII mode.\n",
bare_lfs);
fputs("File may not have transferred correctly.\n", ttyout);
}
if (bytes >= 0 && is_retr) {
if (bytes > 0)
ptransfer(0);
if (preserve && (closefunc == fclose)) {
mtime = remotemodtime(remote, 0);
if (mtime != -1) {
(void)gettimeofday(&tval[0], NULL);
tval[1].tv_sec = mtime;
tval[1].tv_usec = 0;
if (utimes(local, tval) == -1) {
fprintf(ttyout,
"Can't change modification time on %s to %s",
local,
rfc2822time(localtime(&mtime)));
}
}
}
}
goto cleanuprecv;
abort:
/*
* abort using RFC 959 recommended IP,SYNC sequence
*/
if (! sigsetjmp(xferabort, 1)) {
/* this is the first call */
(void)xsignal(SIGINT, abort_squared);
if (!cpend) {
code = -1;
goto cleanuprecv;
}
abort_remote(din);
}
code = -1;
if (bytes > 0)
ptransfer(0);
cleanuprecv:
if (oldintr != SIG_ERR)
(void)xsignal(SIGINT, oldintr);
if (oldpipe != SIG_ERR)
(void)xsignal(SIGPIPE, oldpipe);
if (data >= 0) {
(void)close(data);
data = -1;
}
if (closefunc != NULL && fout != NULL)
(*closefunc)(fout);
if (din)
(void)fclose(din);
progress = oprogress;
preserve = opreserve;
bytes = 0;
}
/*
* Need to start a listen on the data channel before we send the command,
* otherwise the server's connect may fail.
*/
int
initconn(void)
{
char *p, *a;
int result, tmpno = 0;
int on = 1;
int error;
unsigned int addr[16], port[2];
unsigned int af, hal, pal;
socklen_t len;
const char *pasvcmd = NULL;
int overbose;
#ifdef INET6
#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 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("Can't create socket for data connection");
return (1);
}
if ((options & SO_DEBUG) &&
setsockopt(data, SOL_SOCKET, SO_DEBUG,
(void *)&on, sizeof(on)) == -1) {
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);
/*
* 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) {
epsv4bad = 1;
DPRINTF("disabling epsv4 for this "
"connection\n");
}
}
if (result != COMPLETE) {
pasvcmd = "PASV";
result = command("PASV");
if (!connected)
return (1);
}
break;
#ifdef INET6
case AF_INET6:
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";
result = command("LPSV");
}
if (!connected)
return (1);
break;
#endif
default:
result = COMPLETE + 1;
break;
}
if (result != COMPLETE) {
if (activefallback) {
(void)close(data);
data = -1;
passivemode = 0;
#if 0
activefallback = 0;
#endif
goto reinit;
}
fputs("Passive mode refused.\n", ttyout);
goto bad;
}
if (cp)
*cp = '\0';
d = access(cp == local ? "/" : cp ? local : ".", W_OK);
if (cp)
*cp = '/';
if (d < 0) {
warn("Can't access `%s'", local);
return (NULL);
}
len = strlcpy(new, local, sizeof(new));
cp = &new[len];
*cp++ = '.';
while (!d) {
if (++count == 100) {
fputs("runique: can't find unique file name.\n",
ttyout);
return (NULL);
}
*cp++ = ext;
*cp = '\0';
if (ext == '9')
ext = '0';
else
ext++;
if ((d = access(new, F_OK)) < 0)
break;
if (ext != '0')
cp--;
else if (*(cp - 2) == '.')
*(cp - 1) = '1';
else {
*(cp - 2) = *(cp - 2) + 1;
cp--;
}
}
return (new);
}
/*
* abort_squared --
* aborts abort_remote(). lostpeer() is called because if the user is
* too impatient to wait or there's another problem then ftp really
* needs to get back to a known state.
*/
static void
abort_squared(int signo)
{
char msgbuf[100];
size_t len;
void
abort_remote(FILE *din)
{
unsigned char buf[BUFSIZ];
int nfnd;
if (cout == NULL) {
warnx("Lost control connection for abort");
if (ptabflg)
code = -1;
lostpeer(0);
return;
}
/*
* send IAC in urgent mode instead of DM because 4.3BSD places oob mark
* after urgent byte rather than before as is protocol now
*/
buf[0] = IAC;
buf[1] = IP;
buf[2] = IAC;
if (send(fileno(cout), buf, 3, MSG_OOB) != 3)
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("Can't send abort message");
if (ptabflg)
code = -1;
lostpeer(0);
}
if (din && (nfnd & 2)) {
while (read(fileno(din), buf, BUFSIZ) > 0)
continue;
}
if (getreply(0) == ERROR && code == 552) {
/* 552 needed for nic style abort */
(void)getreply(0);
}
(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)
{
#ifdef INET6
struct sockaddr_in6 *sin6;
struct sockaddr_in sin;
socklen_t len;
if (ai->ai_family != AF_INET6)
return;
if (ai->ai_addrlen != sizeof(struct sockaddr_in6) ||
sizeof(sin) > ai->ai_addrlen)
return;
sin6 = (struct sockaddr_in6 *)(void *)ai->ai_addr;
if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
return;