1223328Sgavin/* $NetBSD: ftp.c,v 1.18 2009/05/20 12:53:47 lukem Exp $ */ 2223328Sgavin/* from NetBSD: ftp.c,v 1.159 2009/04/15 03:42:33 jld Exp */ 379971Sobrien 479971Sobrien/*- 5223328Sgavin * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. 679971Sobrien * All rights reserved. 779971Sobrien * 879971Sobrien * This code is derived from software contributed to The NetBSD Foundation 979971Sobrien * by Luke Mewburn. 1079971Sobrien * 1179971Sobrien * Redistribution and use in source and binary forms, with or without 1279971Sobrien * modification, are permitted provided that the following conditions 1379971Sobrien * are met: 1479971Sobrien * 1. Redistributions of source code must retain the above copyright 1579971Sobrien * notice, this list of conditions and the following disclaimer. 1679971Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1779971Sobrien * notice, this list of conditions and the following disclaimer in the 1879971Sobrien * documentation and/or other materials provided with the distribution. 1979971Sobrien * 2079971Sobrien * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2179971Sobrien * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2279971Sobrien * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2379971Sobrien * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2479971Sobrien * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2579971Sobrien * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2679971Sobrien * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2779971Sobrien * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2879971Sobrien * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2979971Sobrien * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3079971Sobrien * POSSIBILITY OF SUCH DAMAGE. 3179971Sobrien */ 3279971Sobrien 3379971Sobrien/* 3479971Sobrien * Copyright (c) 1985, 1989, 1993, 1994 3579971Sobrien * The Regents of the University of California. All rights reserved. 3679971Sobrien * 3779971Sobrien * Redistribution and use in source and binary forms, with or without 3879971Sobrien * modification, are permitted provided that the following conditions 3979971Sobrien * are met: 4079971Sobrien * 1. Redistributions of source code must retain the above copyright 4179971Sobrien * notice, this list of conditions and the following disclaimer. 4279971Sobrien * 2. Redistributions in binary form must reproduce the above copyright 4379971Sobrien * notice, this list of conditions and the following disclaimer in the 4479971Sobrien * documentation and/or other materials provided with the distribution. 45121966Smikeh * 3. Neither the name of the University nor the names of its contributors 4679971Sobrien * may be used to endorse or promote products derived from this software 4779971Sobrien * without specific prior written permission. 4879971Sobrien * 4979971Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 5079971Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 5179971Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5279971Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 5379971Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 5479971Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 5579971Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 5679971Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 5779971Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 5879971Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 5979971Sobrien * SUCH DAMAGE. 6079971Sobrien */ 6179971Sobrien 6279971Sobrien/* 6379971Sobrien * Copyright (C) 1997 and 1998 WIDE Project. 6479971Sobrien * All rights reserved. 65146309Smikeh * 6679971Sobrien * Redistribution and use in source and binary forms, with or without 6779971Sobrien * modification, are permitted provided that the following conditions 6879971Sobrien * are met: 6979971Sobrien * 1. Redistributions of source code must retain the above copyright 7079971Sobrien * notice, this list of conditions and the following disclaimer. 7179971Sobrien * 2. Redistributions in binary form must reproduce the above copyright 7279971Sobrien * notice, this list of conditions and the following disclaimer in the 7379971Sobrien * documentation and/or other materials provided with the distribution. 7479971Sobrien * 3. Neither the name of the project nor the names of its contributors 7579971Sobrien * may be used to endorse or promote products derived from this software 7679971Sobrien * without specific prior written permission. 77146309Smikeh * 7879971Sobrien * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 7979971Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 8079971Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 8179971Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 8279971Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 8379971Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 8479971Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 8579971Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 8679971Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 8779971Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 8879971Sobrien * SUCH DAMAGE. 8979971Sobrien */ 9079971Sobrien 91223328Sgavin#include "tnftp.h" 92223328Sgavin#include <arpa/telnet.h> 93223328Sgavin 94223328Sgavin#if 0 /* tnftp */ 95223328Sgavin 96116424Smikeh#include <sys/cdefs.h> 97116424Smikeh#ifndef lint 98116424Smikeh#if 0 99116424Smikehstatic char sccsid[] = "@(#)ftp.c 8.6 (Berkeley) 10/27/94"; 100116424Smikeh#else 101223328Sgavin__RCSID(" NetBSD: ftp.c,v 1.159 2009/04/15 03:42:33 jld Exp "); 102116424Smikeh#endif 103116424Smikeh#endif /* not lint */ 10479971Sobrien 105116424Smikeh#include <sys/types.h> 106116424Smikeh#include <sys/stat.h> 107116424Smikeh#include <sys/socket.h> 108116424Smikeh#include <sys/time.h> 109116424Smikeh 110116424Smikeh#include <netinet/in.h> 111116424Smikeh#include <netinet/in_systm.h> 112116424Smikeh#include <netinet/ip.h> 113116424Smikeh#include <arpa/inet.h> 114116424Smikeh#include <arpa/ftp.h> 11579971Sobrien#include <arpa/telnet.h> 11679971Sobrien 117116424Smikeh#include <ctype.h> 118116424Smikeh#include <err.h> 119116424Smikeh#include <errno.h> 120146309Smikeh#include <fcntl.h> 121116424Smikeh#include <netdb.h> 122116424Smikeh#include <stdio.h> 123116424Smikeh#include <stdlib.h> 124116424Smikeh#include <string.h> 125116424Smikeh#include <time.h> 126116424Smikeh#include <unistd.h> 127116424Smikeh#include <stdarg.h> 128116424Smikeh 129223328Sgavin#endif /* tnftp */ 130223328Sgavin 13179971Sobrien#include "ftp_var.h" 13279971Sobrien 133142129Smikehvolatile sig_atomic_t abrtflag; 134142129Smikehvolatile sig_atomic_t timeoutflag; 135142129Smikeh 13679971Sobriensigjmp_buf ptabort; 13779971Sobrienint ptabflg; 13879971Sobrienint ptflag = 0; 13979971Sobrienchar pasv[BUFSIZ]; /* passive port for proxy data connection */ 14079971Sobrien 14179971Sobrienstatic int empty(FILE *, FILE *, int); 14279971Sobrien 14379971Sobrienstruct sockinet { 14479971Sobrien union sockunion { 14579971Sobrien struct sockaddr_in su_sin; 14679971Sobrien#ifdef INET6 14779971Sobrien struct sockaddr_in6 su_sin6; 14879971Sobrien#endif 14979971Sobrien } si_su; 150223328Sgavin#if !defined(HAVE_STRUCT_SOCKADDR_IN_SIN_LEN) 15179971Sobrien int si_len; 15279971Sobrien#endif 15379971Sobrien}; 15479971Sobrien 155223328Sgavin#if !defined(HAVE_STRUCT_SOCKADDR_IN_SIN_LEN) 15679971Sobrien# define su_len si_len 15779971Sobrien#else 15879971Sobrien# define su_len si_su.su_sin.sin_len 15979971Sobrien#endif 16079971Sobrien#define su_family si_su.su_sin.sin_family 16179971Sobrien#define su_port si_su.su_sin.sin_port 16279971Sobrien 16379971Sobrienstruct sockinet myctladdr, hisctladdr, data_addr; 16479971Sobrien 16579971Sobrienchar * 166223328Sgavinhookup(const char *host, const char *port) 16779971Sobrien{ 168223328Sgavin int s = -1, error; 16979971Sobrien struct addrinfo hints, *res, *res0; 17079971Sobrien static char hostnamebuf[MAXHOSTNAMELEN]; 171146309Smikeh socklen_t len; 172146309Smikeh int on = 1; 17379971Sobrien 17479971Sobrien memset((char *)&hisctladdr, 0, sizeof (hisctladdr)); 17579971Sobrien memset((char *)&myctladdr, 0, sizeof (myctladdr)); 17679971Sobrien memset(&hints, 0, sizeof(hints)); 17779971Sobrien hints.ai_flags = AI_CANONNAME; 17895504Smikeh hints.ai_family = family; 17979971Sobrien hints.ai_socktype = SOCK_STREAM; 18079971Sobrien hints.ai_protocol = 0; 181223328Sgavin error = getaddrinfo(host, port, &hints, &res0); 18279971Sobrien if (error) { 183223328Sgavin warnx("Can't lookup `%s:%s': %s", host, port, 184223328Sgavin (error == EAI_SYSTEM) ? strerror(errno) 185223328Sgavin : gai_strerror(error)); 18679971Sobrien code = -1; 18779971Sobrien return (0); 18879971Sobrien } 18979971Sobrien 19079971Sobrien if (res0->ai_canonname) 19179971Sobrien (void)strlcpy(hostnamebuf, res0->ai_canonname, 19279971Sobrien sizeof(hostnamebuf)); 19379971Sobrien else 19479971Sobrien (void)strlcpy(hostnamebuf, host, sizeof(hostnamebuf)); 19579971Sobrien hostname = hostnamebuf; 196146309Smikeh 19779971Sobrien for (res = res0; res; res = res->ai_next) { 198223328Sgavin char hname[NI_MAXHOST], sname[NI_MAXSERV]; 199223328Sgavin 20079971Sobrien ai_unmapped(res); 201223328Sgavin if (getnameinfo(res->ai_addr, res->ai_addrlen, 202223328Sgavin hname, sizeof(hname), sname, sizeof(sname), 203223328Sgavin NI_NUMERICHOST | NI_NUMERICSERV) != 0) { 204223328Sgavin strlcpy(hname, "?", sizeof(hname)); 205223328Sgavin strlcpy(sname, "?", sizeof(sname)); 20679971Sobrien } 207223328Sgavin if (verbose && res0->ai_next) { 208223328Sgavin /* if we have multiple possibilities */ 209223328Sgavin fprintf(ttyout, "Trying %s:%s ...\n", hname, sname); 210223328Sgavin } 21179971Sobrien s = socket(res->ai_family, SOCK_STREAM, res->ai_protocol); 21279971Sobrien if (s < 0) { 213223328Sgavin warn("Can't create socket for connection to `%s:%s'", 214223328Sgavin hname, sname); 21579971Sobrien continue; 21679971Sobrien } 217223328Sgavin if (ftp_connect(s, res->ai_addr, res->ai_addrlen) < 0) { 21879971Sobrien close(s); 21979971Sobrien s = -1; 22079971Sobrien continue; 22179971Sobrien } 22279971Sobrien 22379971Sobrien /* finally we got one */ 22479971Sobrien break; 22579971Sobrien } 22679971Sobrien if (s < 0) { 227223328Sgavin warnx("Can't connect to `%s:%s'", host, port); 22879971Sobrien code = -1; 22979971Sobrien freeaddrinfo(res0); 23079971Sobrien return 0; 23179971Sobrien } 23279971Sobrien memcpy(&hisctladdr.si_su, res->ai_addr, res->ai_addrlen); 23379971Sobrien hisctladdr.su_len = res->ai_addrlen; 23479971Sobrien freeaddrinfo(res0); 23579971Sobrien res0 = res = NULL; 23679971Sobrien 23779971Sobrien len = hisctladdr.su_len; 238146309Smikeh if (getsockname(s, (struct sockaddr *)&myctladdr.si_su, &len) == -1) { 239223328Sgavin warn("Can't determine my address of connection to `%s:%s'", 240223328Sgavin host, port); 24179971Sobrien code = -1; 24279971Sobrien goto bad; 24379971Sobrien } 24479971Sobrien myctladdr.su_len = len; 24579971Sobrien 24679971Sobrien#ifdef IPTOS_LOWDELAY 24779971Sobrien if (hisctladdr.su_family == AF_INET) { 24879971Sobrien int tos = IPTOS_LOWDELAY; 249146309Smikeh if (setsockopt(s, IPPROTO_IP, IP_TOS, 250146309Smikeh (void *)&tos, sizeof(tos)) == -1) { 251223328Sgavin DWARN("setsockopt %s (ignored)", 252146309Smikeh "IPTOS_LOWDELAY"); 253146309Smikeh } 25479971Sobrien } 25579971Sobrien#endif 25679971Sobrien cin = fdopen(s, "r"); 25779971Sobrien cout = fdopen(s, "w"); 25879971Sobrien if (cin == NULL || cout == NULL) { 259223328Sgavin warnx("Can't fdopen socket"); 26079971Sobrien if (cin) 26179971Sobrien (void)fclose(cin); 26279971Sobrien if (cout) 26379971Sobrien (void)fclose(cout); 26479971Sobrien code = -1; 26579971Sobrien goto bad; 26679971Sobrien } 26779971Sobrien if (verbose) 26879971Sobrien fprintf(ttyout, "Connected to %s.\n", hostname); 26979971Sobrien if (getreply(0) > 2) { /* read startup message from server */ 27079971Sobrien if (cin) 27179971Sobrien (void)fclose(cin); 27279971Sobrien if (cout) 27379971Sobrien (void)fclose(cout); 27479971Sobrien code = -1; 27579971Sobrien goto bad; 27679971Sobrien } 27779971Sobrien 278146309Smikeh if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, 279146309Smikeh (void *)&on, sizeof(on)) == -1) { 280223328Sgavin DWARN("setsockopt %s (ignored)", "SO_OOBINLINE"); 28179971Sobrien } 28279971Sobrien 28379971Sobrien return (hostname); 28479971Sobrien bad: 28579971Sobrien (void)close(s); 28679971Sobrien return (NULL); 28779971Sobrien} 28879971Sobrien 28979971Sobrienvoid 29079971Sobriencmdabort(int notused) 29179971Sobrien{ 29279971Sobrien int oerrno = errno; 29379971Sobrien 294142129Smikeh sigint_raised = 1; 29579971Sobrien alarmtimer(0); 29679971Sobrien if (fromatty) 29779971Sobrien write(fileno(ttyout), "\n", 1); 29879971Sobrien abrtflag++; 29979971Sobrien if (ptflag) 30079971Sobrien siglongjmp(ptabort, 1); 30179971Sobrien errno = oerrno; 30279971Sobrien} 30379971Sobrien 30479971Sobrienvoid 30579971Sobriencmdtimeout(int notused) 30679971Sobrien{ 30779971Sobrien int oerrno = errno; 30879971Sobrien 30979971Sobrien alarmtimer(0); 31079971Sobrien if (fromatty) 31179971Sobrien write(fileno(ttyout), "\n", 1); 31279971Sobrien timeoutflag++; 31379971Sobrien if (ptflag) 31479971Sobrien siglongjmp(ptabort, 1); 31579971Sobrien errno = oerrno; 31679971Sobrien} 31779971Sobrien 31879971Sobrien/*VARARGS*/ 31979971Sobrienint 32079971Sobriencommand(const char *fmt, ...) 32179971Sobrien{ 32279971Sobrien va_list ap; 32379971Sobrien int r; 32479971Sobrien sigfunc oldsigint; 32579971Sobrien 326223328Sgavin#ifndef NO_DEBUG 327223328Sgavin if (ftp_debug) { 32879971Sobrien fputs("---> ", ttyout); 32979971Sobrien va_start(ap, fmt); 33079971Sobrien if (strncmp("PASS ", fmt, 5) == 0) 33179971Sobrien fputs("PASS XXXX", ttyout); 33279971Sobrien else if (strncmp("ACCT ", fmt, 5) == 0) 33379971Sobrien fputs("ACCT XXXX", ttyout); 33479971Sobrien else 33579971Sobrien vfprintf(ttyout, fmt, ap); 33679971Sobrien va_end(ap); 33779971Sobrien putc('\n', ttyout); 33879971Sobrien } 339223328Sgavin#endif 34079971Sobrien if (cout == NULL) { 341223328Sgavin warnx("No control connection for command"); 34279971Sobrien code = -1; 34379971Sobrien return (0); 34479971Sobrien } 34579971Sobrien 34679971Sobrien abrtflag = 0; 34779971Sobrien 34879971Sobrien oldsigint = xsignal(SIGINT, cmdabort); 34979971Sobrien 35079971Sobrien va_start(ap, fmt); 35179971Sobrien vfprintf(cout, fmt, ap); 35279971Sobrien va_end(ap); 35379971Sobrien fputs("\r\n", cout); 35479971Sobrien (void)fflush(cout); 35579971Sobrien cpend = 1; 35679971Sobrien r = getreply(!strcmp(fmt, "QUIT")); 35779971Sobrien if (abrtflag && oldsigint != SIG_IGN) 35879971Sobrien (*oldsigint)(SIGINT); 35979971Sobrien (void)xsignal(SIGINT, oldsigint); 36079971Sobrien return (r); 36179971Sobrien} 36279971Sobrien 363223328Sgavinstatic const char *m421[] = { 364223328Sgavin "remote server timed out. Connection closed", 365223328Sgavin "user interrupt. Connection closed", 366223328Sgavin "remote server has closed connection", 367223328Sgavin}; 368223328Sgavin 36979971Sobrienint 37079971Sobriengetreply(int expecteof) 37179971Sobrien{ 37279971Sobrien char current_line[BUFSIZ]; /* last line of previous reply */ 373223328Sgavin int c, n, lineno; 37479971Sobrien int dig; 37579971Sobrien int originalcode = 0, continuation = 0; 37679971Sobrien sigfunc oldsigint, oldsigalrm; 37779971Sobrien int pflag = 0; 37879971Sobrien char *cp, *pt = pasv; 37979971Sobrien 38079971Sobrien abrtflag = 0; 38179971Sobrien timeoutflag = 0; 38279971Sobrien 38379971Sobrien oldsigint = xsignal(SIGINT, cmdabort); 38479971Sobrien oldsigalrm = xsignal(SIGALRM, cmdtimeout); 38579971Sobrien 386223328Sgavin for (lineno = 0 ;; lineno++) { 38779971Sobrien dig = n = code = 0; 38879971Sobrien cp = current_line; 389223328Sgavin while (alarmtimer(quit_time ? quit_time : 60), 390223328Sgavin ((c = getc(cin)) != '\n')) { 39179971Sobrien if (c == IAC) { /* handle telnet commands */ 39279971Sobrien switch (c = getc(cin)) { 39379971Sobrien case WILL: 39479971Sobrien case WONT: 39579971Sobrien c = getc(cin); 39679971Sobrien fprintf(cout, "%c%c%c", IAC, DONT, c); 39779971Sobrien (void)fflush(cout); 39879971Sobrien break; 39979971Sobrien case DO: 40079971Sobrien case DONT: 40179971Sobrien c = getc(cin); 40279971Sobrien fprintf(cout, "%c%c%c", IAC, WONT, c); 40379971Sobrien (void)fflush(cout); 40479971Sobrien break; 40579971Sobrien default: 40679971Sobrien break; 40779971Sobrien } 40879971Sobrien continue; 40979971Sobrien } 41079971Sobrien dig++; 41179971Sobrien if (c == EOF) { 41279971Sobrien /* 41379971Sobrien * these will get trashed by pswitch() 41479971Sobrien * in lostpeer() 41579971Sobrien */ 41679971Sobrien int reply_timeoutflag = timeoutflag; 41779971Sobrien int reply_abrtflag = abrtflag; 41879971Sobrien 41979971Sobrien alarmtimer(0); 42079971Sobrien if (expecteof && feof(cin)) { 42179971Sobrien (void)xsignal(SIGINT, oldsigint); 42279971Sobrien (void)xsignal(SIGALRM, oldsigalrm); 42379971Sobrien code = 221; 42479971Sobrien return (0); 42579971Sobrien } 42679971Sobrien cpend = 0; 42779971Sobrien lostpeer(0); 42879971Sobrien if (verbose) { 429223328Sgavin size_t midx; 43079971Sobrien if (reply_timeoutflag) 431223328Sgavin midx = 0; 43279971Sobrien else if (reply_abrtflag) 433223328Sgavin midx = 1; 43479971Sobrien else 435223328Sgavin midx = 2; 436223328Sgavin (void)fprintf(ttyout, 437223328Sgavin "421 Service not available, %s.\n", m421[midx]); 43879971Sobrien (void)fflush(ttyout); 43979971Sobrien } 44079971Sobrien code = 421; 44179971Sobrien (void)xsignal(SIGINT, oldsigint); 44279971Sobrien (void)xsignal(SIGALRM, oldsigalrm); 44379971Sobrien return (4); 44479971Sobrien } 44579971Sobrien if (c != '\r' && (verbose > 0 || 44679971Sobrien ((verbose > -1 && n == '5' && dig > 4) && 44779971Sobrien (((!n && c < '5') || (n && n < '5')) 44879971Sobrien || !retry_connect)))) { 44979971Sobrien if (proxflag && 45079971Sobrien (dig == 1 || (dig == 5 && verbose == 0))) 45179971Sobrien fprintf(ttyout, "%s:", hostname); 45279971Sobrien (void)putc(c, ttyout); 45379971Sobrien } 45479971Sobrien if (dig < 4 && isdigit(c)) 45579971Sobrien code = code * 10 + (c - '0'); 45679971Sobrien if (!pflag && (code == 227 || code == 228)) 45779971Sobrien pflag = 1; 45879971Sobrien else if (!pflag && code == 229) 45979971Sobrien pflag = 100; 46079971Sobrien if (dig > 4 && pflag == 1 && isdigit(c)) 46179971Sobrien pflag = 2; 46279971Sobrien if (pflag == 2) { 46395504Smikeh if (c != '\r' && c != ')') { 46495504Smikeh if (pt < &pasv[sizeof(pasv) - 1]) 46595504Smikeh *pt++ = c; 46695504Smikeh } else { 46779971Sobrien *pt = '\0'; 46879971Sobrien pflag = 3; 46979971Sobrien } 47079971Sobrien } 47179971Sobrien if (pflag == 100 && c == '(') 47279971Sobrien pflag = 2; 47379971Sobrien if (dig == 4 && c == '-') { 47479971Sobrien if (continuation) 47579971Sobrien code = 0; 47679971Sobrien continuation++; 47779971Sobrien } 47879971Sobrien if (n == 0) 47979971Sobrien n = c; 48079971Sobrien if (cp < ¤t_line[sizeof(current_line) - 1]) 48179971Sobrien *cp++ = c; 48279971Sobrien } 48379971Sobrien if (verbose > 0 || ((verbose > -1 && n == '5') && 48479971Sobrien (n < '5' || !retry_connect))) { 48579971Sobrien (void)putc(c, ttyout); 486223328Sgavin (void)fflush(ttyout); 48779971Sobrien } 48879971Sobrien if (cp[-1] == '\r') 48979971Sobrien cp[-1] = '\0'; 49079971Sobrien *cp = '\0'; 491223328Sgavin if (lineno == 0) 49279971Sobrien (void)strlcpy(reply_string, current_line, 49379971Sobrien sizeof(reply_string)); 494223328Sgavin if (lineno > 0 && code == 0 && reply_callback != NULL) 49579971Sobrien (*reply_callback)(current_line); 49679971Sobrien if (continuation && code != originalcode) { 49779971Sobrien if (originalcode == 0) 49879971Sobrien originalcode = code; 49979971Sobrien continue; 50079971Sobrien } 50179971Sobrien if (n != '1') 50279971Sobrien cpend = 0; 50379971Sobrien alarmtimer(0); 50479971Sobrien (void)xsignal(SIGINT, oldsigint); 50579971Sobrien (void)xsignal(SIGALRM, oldsigalrm); 50679971Sobrien if (code == 421 || originalcode == 421) 50779971Sobrien lostpeer(0); 50879971Sobrien if (abrtflag && oldsigint != cmdabort && oldsigint != SIG_IGN) 50979971Sobrien (*oldsigint)(SIGINT); 51079971Sobrien if (timeoutflag && oldsigalrm != cmdtimeout && 51179971Sobrien oldsigalrm != SIG_IGN) 51279971Sobrien (*oldsigalrm)(SIGINT); 51379971Sobrien return (n - '0'); 51479971Sobrien } 51579971Sobrien} 51679971Sobrien 51779971Sobrienstatic int 518223328Sgavinempty(FILE *ecin, FILE *din, int sec) 51979971Sobrien{ 520146309Smikeh int nr, nfd; 521146309Smikeh struct pollfd pfd[2]; 52279971Sobrien 523146309Smikeh nfd = 0; 524223328Sgavin if (ecin) { 525223328Sgavin pfd[nfd].fd = fileno(ecin); 526128671Smikeh pfd[nfd++].events = POLLIN; 52779971Sobrien } 52879971Sobrien 52979971Sobrien if (din) { 530128671Smikeh pfd[nfd].fd = fileno(din); 531128671Smikeh pfd[nfd++].events = POLLIN; 53279971Sobrien } 53379971Sobrien 534223328Sgavin if ((nr = ftp_poll(pfd, nfd, sec * 1000)) <= 0) 53579971Sobrien return nr; 53679971Sobrien 53779971Sobrien nr = 0; 53879971Sobrien nfd = 0; 539223328Sgavin if (ecin) 54079971Sobrien nr |= (pfd[nfd++].revents & POLLIN) ? 1 : 0; 54179971Sobrien if (din) 54279971Sobrien nr |= (pfd[nfd++].revents & POLLIN) ? 2 : 0; 54379971Sobrien return nr; 54479971Sobrien} 54579971Sobrien 54679971Sobriensigjmp_buf xferabort; 54779971Sobrien 54879971Sobrienvoid 54979971Sobrienabortxfer(int notused) 55079971Sobrien{ 55179971Sobrien char msgbuf[100]; 552146309Smikeh size_t len; 55379971Sobrien 554142129Smikeh sigint_raised = 1; 55579971Sobrien alarmtimer(0); 55679971Sobrien mflag = 0; 55779971Sobrien abrtflag = 0; 55879971Sobrien switch (direction[0]) { 55979971Sobrien case 'r': 56079971Sobrien strlcpy(msgbuf, "\nreceive", sizeof(msgbuf)); 56179971Sobrien break; 56279971Sobrien case 's': 56379971Sobrien strlcpy(msgbuf, "\nsend", sizeof(msgbuf)); 56479971Sobrien break; 56579971Sobrien default: 566223328Sgavin errx(1, "abortxfer: unknown direction `%s'", direction); 56779971Sobrien } 56879971Sobrien len = strlcat(msgbuf, " aborted. Waiting for remote to finish abort.\n", 56979971Sobrien sizeof(msgbuf)); 57079971Sobrien write(fileno(ttyout), msgbuf, len); 57179971Sobrien siglongjmp(xferabort, 1); 57279971Sobrien} 57379971Sobrien 574223328Sgavin/* 575223328Sgavin * Read data from infd & write to outfd, using buf/bufsize as the temporary 576223328Sgavin * buffer, dealing with short writes. 577223328Sgavin * If rate_limit != 0, rate-limit the transfer. 578223328Sgavin * If hash_interval != 0, fputc('c', ttyout) every hash_interval bytes. 579223328Sgavin * Updates global variables: bytes. 580223328Sgavin * Returns 0 if ok, 1 if there was a read error, 2 if there was a write error. 581223328Sgavin * In the case of error, errno contains the appropriate error code. 582223328Sgavin */ 583223328Sgavinstatic int 584223328Sgavincopy_bytes(int infd, int outfd, char *buf, size_t bufsize, 585223328Sgavin int rate_limit, int hash_interval) 586223328Sgavin{ 587223328Sgavin volatile off_t hashc; 588223328Sgavin ssize_t inc, outc; 589223328Sgavin char *bufp; 590223328Sgavin struct timeval tvthen, tvnow, tvdiff; 591223328Sgavin off_t bufrem, bufchunk; 592223328Sgavin int serr; 593223328Sgavin 594223328Sgavin hashc = hash_interval; 595223328Sgavin if (rate_limit) 596223328Sgavin bufchunk = rate_limit; 597223328Sgavin else 598223328Sgavin bufchunk = bufsize; 599223328Sgavin 600223328Sgavin while (1) { 601223328Sgavin if (rate_limit) { 602223328Sgavin (void)gettimeofday(&tvthen, NULL); 603223328Sgavin } 604223328Sgavin errno = 0; 605223328Sgavin inc = outc = 0; 606223328Sgavin /* copy bufchunk at a time */ 607223328Sgavin bufrem = bufchunk; 608223328Sgavin while (bufrem > 0) { 609223328Sgavin inc = read(infd, buf, MIN((off_t)bufsize, bufrem)); 610223328Sgavin if (inc <= 0) 611223328Sgavin goto copy_done; 612223328Sgavin bytes += inc; 613223328Sgavin bufrem -= inc; 614223328Sgavin bufp = buf; 615223328Sgavin while (inc > 0) { 616223328Sgavin outc = write(outfd, bufp, inc); 617223328Sgavin if (outc < 0) 618223328Sgavin goto copy_done; 619223328Sgavin inc -= outc; 620223328Sgavin bufp += outc; 621223328Sgavin } 622223328Sgavin if (hash_interval) { 623223328Sgavin while (bytes >= hashc) { 624223328Sgavin (void)putc('#', ttyout); 625223328Sgavin hashc += hash_interval; 626223328Sgavin } 627223328Sgavin (void)fflush(ttyout); 628223328Sgavin } 629223328Sgavin } 630223328Sgavin if (rate_limit) { /* rate limited; wait if necessary */ 631223328Sgavin while (1) { 632223328Sgavin (void)gettimeofday(&tvnow, NULL); 633223328Sgavin timersub(&tvnow, &tvthen, &tvdiff); 634223328Sgavin if (tvdiff.tv_sec > 0) 635223328Sgavin break; 636223328Sgavin usleep(1000000 - tvdiff.tv_usec); 637223328Sgavin } 638223328Sgavin } 639223328Sgavin } 640223328Sgavin 641223328Sgavin copy_done: 642223328Sgavin serr = errno; 643223328Sgavin if (hash_interval && bytes > 0) { 644223328Sgavin if (bytes < hash_interval) 645223328Sgavin (void)putc('#', ttyout); 646223328Sgavin (void)putc('\n', ttyout); 647223328Sgavin (void)fflush(ttyout); 648223328Sgavin } 649223328Sgavin errno = serr; 650223328Sgavin if (inc == -1) 651223328Sgavin return 1; 652223328Sgavin if (outc == -1) 653223328Sgavin return 2; 654223328Sgavin 655223328Sgavin return 0; 656223328Sgavin} 657223328Sgavin 65879971Sobrienvoid 65979971Sobriensendrequest(const char *cmd, const char *local, const char *remote, 66079971Sobrien int printnames) 66179971Sobrien{ 66279971Sobrien struct stat st; 663223328Sgavin int c; 664223328Sgavin FILE *volatile fin; 665223328Sgavin FILE *volatile dout; 666223328Sgavin int (*volatile closefunc)(FILE *); 667223328Sgavin sigfunc volatile oldintr; 668223328Sgavin sigfunc volatile oldintp; 669223328Sgavin off_t volatile hashbytes; 670223328Sgavin int hash_interval; 671223328Sgavin const char *lmode; 67279971Sobrien static size_t bufsize; 67379971Sobrien static char *buf; 67479971Sobrien int oprogress; 67579971Sobrien 67679971Sobrien hashbytes = mark; 67779971Sobrien direction = "sent"; 67879971Sobrien dout = NULL; 67979971Sobrien bytes = 0; 68079971Sobrien filesize = -1; 68179971Sobrien oprogress = progress; 68279971Sobrien if (verbose && printnames) { 683223328Sgavin if (*local != '-') 68479971Sobrien fprintf(ttyout, "local: %s ", local); 68579971Sobrien if (remote) 68679971Sobrien fprintf(ttyout, "remote: %s\n", remote); 68779971Sobrien } 68879971Sobrien if (proxy) { 68979971Sobrien proxtrans(cmd, local, remote); 69079971Sobrien return; 69179971Sobrien } 69279971Sobrien if (curtype != type) 69379971Sobrien changetype(type, 0); 69479971Sobrien closefunc = NULL; 69579971Sobrien oldintr = NULL; 69679971Sobrien oldintp = NULL; 69779971Sobrien lmode = "w"; 69879971Sobrien if (sigsetjmp(xferabort, 1)) { 69979971Sobrien while (cpend) 70079971Sobrien (void)getreply(0); 70179971Sobrien code = -1; 70279971Sobrien goto cleanupsend; 70379971Sobrien } 70479971Sobrien (void)xsignal(SIGQUIT, psummary); 70579971Sobrien oldintr = xsignal(SIGINT, abortxfer); 70679971Sobrien if (strcmp(local, "-") == 0) { 70779971Sobrien fin = stdin; 70879971Sobrien progress = 0; 70979971Sobrien } else if (*local == '|') { 71079971Sobrien oldintp = xsignal(SIGPIPE, SIG_IGN); 71179971Sobrien fin = popen(local + 1, "r"); 71279971Sobrien if (fin == NULL) { 713223328Sgavin warn("Can't execute `%s'", local + 1); 71479971Sobrien code = -1; 71579971Sobrien goto cleanupsend; 71679971Sobrien } 71779971Sobrien progress = 0; 71879971Sobrien closefunc = pclose; 71979971Sobrien } else { 72079971Sobrien fin = fopen(local, "r"); 72179971Sobrien if (fin == NULL) { 722223328Sgavin warn("Can't open `%s'", local); 72379971Sobrien code = -1; 72479971Sobrien goto cleanupsend; 72579971Sobrien } 72679971Sobrien closefunc = fclose; 72779971Sobrien if (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode)) { 72879971Sobrien fprintf(ttyout, "%s: not a plain file.\n", local); 72979971Sobrien code = -1; 73079971Sobrien goto cleanupsend; 73179971Sobrien } 73279971Sobrien filesize = st.st_size; 73379971Sobrien } 73479971Sobrien if (initconn()) { 73579971Sobrien code = -1; 73679971Sobrien goto cleanupsend; 73779971Sobrien } 73879971Sobrien if (sigsetjmp(xferabort, 1)) 73979971Sobrien goto abort; 74079971Sobrien 74179971Sobrien if (restart_point && 74279971Sobrien (strcmp(cmd, "STOR") == 0 || strcmp(cmd, "APPE") == 0)) { 74379971Sobrien int rc; 74479971Sobrien 74579971Sobrien rc = -1; 74679971Sobrien switch (curtype) { 74779971Sobrien case TYPE_A: 74895504Smikeh rc = fseeko(fin, restart_point, SEEK_SET); 74979971Sobrien break; 75079971Sobrien case TYPE_I: 75179971Sobrien case TYPE_L: 75279971Sobrien rc = lseek(fileno(fin), restart_point, SEEK_SET); 75379971Sobrien break; 75479971Sobrien } 75579971Sobrien if (rc < 0) { 756223328Sgavin warn("Can't seek to restart `%s'", local); 75779971Sobrien goto cleanupsend; 75879971Sobrien } 75979971Sobrien if (command("REST " LLF, (LLT)restart_point) != CONTINUE) 76079971Sobrien goto cleanupsend; 76198247Smikeh lmode = "r+"; 76279971Sobrien } 76379971Sobrien if (remote) { 76479971Sobrien if (command("%s %s", cmd, remote) != PRELIM) 76579971Sobrien goto cleanupsend; 76679971Sobrien } else { 76779971Sobrien if (command("%s", cmd) != PRELIM) 76879971Sobrien goto cleanupsend; 76979971Sobrien } 770121966Smikeh dirchange = 1; 77179971Sobrien dout = dataconn(lmode); 77279971Sobrien if (dout == NULL) 77379971Sobrien goto abort; 77479971Sobrien 775223328Sgavin if ((size_t)sndbuf_size > bufsize) { 77679971Sobrien if (buf) 77779971Sobrien (void)free(buf); 77879971Sobrien bufsize = sndbuf_size; 779223328Sgavin buf = ftp_malloc(bufsize); 78079971Sobrien } 78179971Sobrien 78279971Sobrien progressmeter(-1); 78379971Sobrien oldintp = xsignal(SIGPIPE, SIG_IGN); 784223328Sgavin hash_interval = (hash && (!progress || filesize < 0)) ? mark : 0; 78579971Sobrien 78679971Sobrien switch (curtype) { 78779971Sobrien 78879971Sobrien case TYPE_I: 78979971Sobrien case TYPE_L: 790223328Sgavin c = copy_bytes(fileno(fin), fileno(dout), buf, bufsize, 791223328Sgavin rate_put, hash_interval); 792223328Sgavin if (c == 1) { 793223328Sgavin warn("Reading `%s'", local); 794223328Sgavin } else if (c == 2) { 79579971Sobrien if (errno != EPIPE) 796223328Sgavin warn("Writing to network"); 79779971Sobrien bytes = -1; 79879971Sobrien } 79979971Sobrien break; 80079971Sobrien 80179971Sobrien case TYPE_A: 80279971Sobrien while ((c = getc(fin)) != EOF) { 80379971Sobrien if (c == '\n') { 804223328Sgavin while (hash_interval && bytes >= hashbytes) { 80579971Sobrien (void)putc('#', ttyout); 80679971Sobrien (void)fflush(ttyout); 80779971Sobrien hashbytes += mark; 80879971Sobrien } 80979971Sobrien if (ferror(dout)) 81079971Sobrien break; 81179971Sobrien (void)putc('\r', dout); 81279971Sobrien bytes++; 81379971Sobrien } 81479971Sobrien (void)putc(c, dout); 81579971Sobrien bytes++; 816223328Sgavin#if 0 /* this violates RFC0959 */ 81779971Sobrien if (c == '\r') { 81879971Sobrien (void)putc('\0', dout); 81979971Sobrien bytes++; 82079971Sobrien } 82179971Sobrien#endif 82279971Sobrien } 823223328Sgavin if (hash_interval) { 82479971Sobrien if (bytes < hashbytes) 82579971Sobrien (void)putc('#', ttyout); 82679971Sobrien (void)putc('\n', ttyout); 82779971Sobrien } 82879971Sobrien if (ferror(fin)) 829223328Sgavin warn("Reading `%s'", local); 83079971Sobrien if (ferror(dout)) { 83179971Sobrien if (errno != EPIPE) 832223328Sgavin warn("Writing to network"); 83379971Sobrien bytes = -1; 83479971Sobrien } 83579971Sobrien break; 83679971Sobrien } 83779971Sobrien 83879971Sobrien progressmeter(1); 83979971Sobrien if (closefunc != NULL) { 84079971Sobrien (*closefunc)(fin); 84179971Sobrien fin = NULL; 84279971Sobrien } 84379971Sobrien (void)fclose(dout); 84479971Sobrien dout = NULL; 84579971Sobrien (void)getreply(0); 84679971Sobrien if (bytes > 0) 84779971Sobrien ptransfer(0); 84879971Sobrien goto cleanupsend; 84979971Sobrien 85079971Sobrien abort: 85179971Sobrien (void)xsignal(SIGINT, oldintr); 85279971Sobrien oldintr = NULL; 85379971Sobrien if (!cpend) { 85479971Sobrien code = -1; 85579971Sobrien goto cleanupsend; 85679971Sobrien } 85779971Sobrien if (data >= 0) { 85879971Sobrien (void)close(data); 85979971Sobrien data = -1; 86079971Sobrien } 86179971Sobrien if (dout) { 86279971Sobrien (void)fclose(dout); 86379971Sobrien dout = NULL; 86479971Sobrien } 86579971Sobrien (void)getreply(0); 86679971Sobrien code = -1; 86779971Sobrien if (bytes > 0) 86879971Sobrien ptransfer(0); 86979971Sobrien 87079971Sobrien cleanupsend: 87179971Sobrien if (oldintr) 87279971Sobrien (void)xsignal(SIGINT, oldintr); 87379971Sobrien if (oldintp) 87479971Sobrien (void)xsignal(SIGPIPE, oldintp); 87579971Sobrien if (data >= 0) { 87679971Sobrien (void)close(data); 87779971Sobrien data = -1; 87879971Sobrien } 87979971Sobrien if (closefunc != NULL && fin != NULL) 88079971Sobrien (*closefunc)(fin); 88179971Sobrien if (dout) 88279971Sobrien (void)fclose(dout); 88379971Sobrien progress = oprogress; 88479971Sobrien restart_point = 0; 88579971Sobrien bytes = 0; 88679971Sobrien} 88779971Sobrien 88879971Sobrienvoid 889223328Sgavinrecvrequest(const char *cmd, const char *volatile local, const char *remote, 89079971Sobrien const char *lmode, int printnames, int ignorespecial) 89179971Sobrien{ 892223328Sgavin FILE *volatile fout; 893223328Sgavin FILE *volatile din; 894223328Sgavin int (*volatile closefunc)(FILE *); 895223328Sgavin sigfunc volatile oldintr; 896223328Sgavin sigfunc volatile oldintp; 89779971Sobrien int c, d; 898223328Sgavin int volatile is_retr; 899223328Sgavin int volatile tcrflag; 900223328Sgavin int volatile bare_lfs; 90179971Sobrien static size_t bufsize; 90279971Sobrien static char *buf; 903223328Sgavin off_t volatile hashbytes; 904223328Sgavin int hash_interval; 90579971Sobrien struct stat st; 90679971Sobrien time_t mtime; 90779971Sobrien struct timeval tval[2]; 90879971Sobrien int oprogress; 90979971Sobrien int opreserve; 91079971Sobrien 91179971Sobrien fout = NULL; 91279971Sobrien din = NULL; 91379971Sobrien hashbytes = mark; 91479971Sobrien direction = "received"; 91579971Sobrien bytes = 0; 91679971Sobrien bare_lfs = 0; 91779971Sobrien filesize = -1; 91879971Sobrien oprogress = progress; 91979971Sobrien opreserve = preserve; 92079971Sobrien is_retr = (strcmp(cmd, "RETR") == 0); 92179971Sobrien if (is_retr && verbose && printnames) { 922223328Sgavin if (ignorespecial || *local != '-') 92379971Sobrien fprintf(ttyout, "local: %s ", local); 92479971Sobrien if (remote) 92579971Sobrien fprintf(ttyout, "remote: %s\n", remote); 92679971Sobrien } 92779971Sobrien if (proxy && is_retr) { 92879971Sobrien proxtrans(cmd, local, remote); 92979971Sobrien return; 93079971Sobrien } 93179971Sobrien closefunc = NULL; 93279971Sobrien oldintr = NULL; 93379971Sobrien oldintp = NULL; 93479971Sobrien tcrflag = !crflag && is_retr; 93579971Sobrien if (sigsetjmp(xferabort, 1)) { 93679971Sobrien while (cpend) 93779971Sobrien (void)getreply(0); 93879971Sobrien code = -1; 93979971Sobrien goto cleanuprecv; 94079971Sobrien } 94179971Sobrien (void)xsignal(SIGQUIT, psummary); 94279971Sobrien oldintr = xsignal(SIGINT, abortxfer); 94379971Sobrien if (ignorespecial || (strcmp(local, "-") && *local != '|')) { 94479971Sobrien if (access(local, W_OK) < 0) { 94579971Sobrien char *dir = strrchr(local, '/'); 94679971Sobrien 94779971Sobrien if (errno != ENOENT && errno != EACCES) { 948223328Sgavin warn("Can't access `%s'", local); 94979971Sobrien code = -1; 95079971Sobrien goto cleanuprecv; 95179971Sobrien } 95279971Sobrien if (dir != NULL) 95379971Sobrien *dir = 0; 95479971Sobrien d = access(dir == local ? "/" : 95579971Sobrien dir ? local : ".", W_OK); 95679971Sobrien if (dir != NULL) 95779971Sobrien *dir = '/'; 95879971Sobrien if (d < 0) { 959223328Sgavin warn("Can't access `%s'", local); 96079971Sobrien code = -1; 96179971Sobrien goto cleanuprecv; 96279971Sobrien } 96379971Sobrien if (!runique && errno == EACCES && 96479971Sobrien chmod(local, (S_IRUSR|S_IWUSR)) < 0) { 965223328Sgavin warn("Can't chmod `%s'", local); 96679971Sobrien code = -1; 96779971Sobrien goto cleanuprecv; 96879971Sobrien } 96979971Sobrien if (runique && errno == EACCES && 97079971Sobrien (local = gunique(local)) == NULL) { 97179971Sobrien code = -1; 97279971Sobrien goto cleanuprecv; 97379971Sobrien } 97479971Sobrien } 97579971Sobrien else if (runique && (local = gunique(local)) == NULL) { 97679971Sobrien code = -1; 97779971Sobrien goto cleanuprecv; 97879971Sobrien } 97979971Sobrien } 98079971Sobrien if (!is_retr) { 98179971Sobrien if (curtype != TYPE_A) 98279971Sobrien changetype(TYPE_A, 0); 98379971Sobrien } else { 98479971Sobrien if (curtype != type) 98579971Sobrien changetype(type, 0); 98679971Sobrien filesize = remotesize(remote, 0); 98779971Sobrien if (code == 421 || code == -1) 98879971Sobrien goto cleanuprecv; 98979971Sobrien } 99079971Sobrien if (initconn()) { 99179971Sobrien code = -1; 99279971Sobrien goto cleanuprecv; 99379971Sobrien } 99479971Sobrien if (sigsetjmp(xferabort, 1)) 99579971Sobrien goto abort; 99679971Sobrien if (is_retr && restart_point && 99779971Sobrien command("REST " LLF, (LLT) restart_point) != CONTINUE) 99879971Sobrien goto cleanuprecv; 99979971Sobrien if (! EMPTYSTRING(remote)) { 100079971Sobrien if (command("%s %s", cmd, remote) != PRELIM) 100179971Sobrien goto cleanuprecv; 100279971Sobrien } else { 100379971Sobrien if (command("%s", cmd) != PRELIM) 100479971Sobrien goto cleanuprecv; 100579971Sobrien } 100679971Sobrien din = dataconn("r"); 100779971Sobrien if (din == NULL) 100879971Sobrien goto abort; 100979971Sobrien if (!ignorespecial && strcmp(local, "-") == 0) { 101079971Sobrien fout = stdout; 101179971Sobrien progress = 0; 101279971Sobrien preserve = 0; 101379971Sobrien } else if (!ignorespecial && *local == '|') { 101479971Sobrien oldintp = xsignal(SIGPIPE, SIG_IGN); 101579971Sobrien fout = popen(local + 1, "w"); 101679971Sobrien if (fout == NULL) { 1017223328Sgavin warn("Can't execute `%s'", local+1); 101879971Sobrien goto abort; 101979971Sobrien } 102079971Sobrien progress = 0; 102179971Sobrien preserve = 0; 102279971Sobrien closefunc = pclose; 102379971Sobrien } else { 102479971Sobrien fout = fopen(local, lmode); 102579971Sobrien if (fout == NULL) { 1026223328Sgavin warn("Can't open `%s'", local); 102779971Sobrien goto abort; 102879971Sobrien } 102979971Sobrien closefunc = fclose; 103079971Sobrien } 103179971Sobrien 103279971Sobrien if (fstat(fileno(fout), &st) != -1 && !S_ISREG(st.st_mode)) { 103379971Sobrien progress = 0; 103479971Sobrien preserve = 0; 103579971Sobrien } 1036223328Sgavin if ((size_t)rcvbuf_size > bufsize) { 103779971Sobrien if (buf) 103879971Sobrien (void)free(buf); 103979971Sobrien bufsize = rcvbuf_size; 1040223328Sgavin buf = ftp_malloc(bufsize); 104179971Sobrien } 104279971Sobrien 104379971Sobrien progressmeter(-1); 1044223328Sgavin hash_interval = (hash && (!progress || filesize < 0)) ? mark : 0; 104579971Sobrien 104679971Sobrien switch (curtype) { 104779971Sobrien 104879971Sobrien case TYPE_I: 104979971Sobrien case TYPE_L: 105079971Sobrien if (is_retr && restart_point && 105179971Sobrien lseek(fileno(fout), restart_point, SEEK_SET) < 0) { 1052223328Sgavin warn("Can't seek to restart `%s'", local); 105379971Sobrien goto cleanuprecv; 105479971Sobrien } 1055223328Sgavin c = copy_bytes(fileno(din), fileno(fout), buf, bufsize, 1056223328Sgavin rate_get, hash_interval); 1057223328Sgavin if (c == 1) { 105879971Sobrien if (errno != EPIPE) 1059223328Sgavin warn("Reading from network"); 106079971Sobrien bytes = -1; 1061223328Sgavin } else if (c == 2) { 1062223328Sgavin warn("Writing `%s'", local); 106379971Sobrien } 106479971Sobrien break; 106579971Sobrien 106679971Sobrien case TYPE_A: 106779971Sobrien if (is_retr && restart_point) { 106879971Sobrien int ch; 106995504Smikeh off_t i; 107079971Sobrien 107195504Smikeh if (fseeko(fout, (off_t)0, SEEK_SET) < 0) 107279971Sobrien goto done; 107395504Smikeh for (i = 0; i++ < restart_point;) { 107479971Sobrien if ((ch = getc(fout)) == EOF) 107579971Sobrien goto done; 107679971Sobrien if (ch == '\n') 107779971Sobrien i++; 107879971Sobrien } 107995504Smikeh if (fseeko(fout, (off_t)0, SEEK_CUR) < 0) { 108079971Sobrien done: 1081223328Sgavin warn("Can't seek to restart `%s'", local); 108279971Sobrien goto cleanuprecv; 108379971Sobrien } 108479971Sobrien } 108579971Sobrien while ((c = getc(din)) != EOF) { 108679971Sobrien if (c == '\n') 108779971Sobrien bare_lfs++; 108879971Sobrien while (c == '\r') { 1089223328Sgavin while (hash_interval && bytes >= hashbytes) { 109079971Sobrien (void)putc('#', ttyout); 109179971Sobrien (void)fflush(ttyout); 109279971Sobrien hashbytes += mark; 109379971Sobrien } 109479971Sobrien bytes++; 109579971Sobrien if ((c = getc(din)) != '\n' || tcrflag) { 109679971Sobrien if (ferror(fout)) 109779971Sobrien goto break2; 109879971Sobrien (void)putc('\r', fout); 109979971Sobrien if (c == '\0') { 110079971Sobrien bytes++; 110179971Sobrien goto contin2; 110279971Sobrien } 110379971Sobrien if (c == EOF) 110479971Sobrien goto contin2; 110579971Sobrien } 110679971Sobrien } 110779971Sobrien (void)putc(c, fout); 110879971Sobrien bytes++; 110979971Sobrien contin2: ; 111079971Sobrien } 111198247Smikeh break2: 1112223328Sgavin if (hash_interval) { 111379971Sobrien if (bytes < hashbytes) 111479971Sobrien (void)putc('#', ttyout); 111579971Sobrien (void)putc('\n', ttyout); 111679971Sobrien } 111779971Sobrien if (ferror(din)) { 111879971Sobrien if (errno != EPIPE) 1119223328Sgavin warn("Reading from network"); 112079971Sobrien bytes = -1; 112179971Sobrien } 112279971Sobrien if (ferror(fout)) 1123223328Sgavin warn("Writing `%s'", local); 112479971Sobrien break; 112579971Sobrien } 112679971Sobrien 112779971Sobrien progressmeter(1); 112879971Sobrien if (closefunc != NULL) { 112979971Sobrien (*closefunc)(fout); 113079971Sobrien fout = NULL; 113179971Sobrien } 113279971Sobrien (void)fclose(din); 113379971Sobrien din = NULL; 113479971Sobrien (void)getreply(0); 113579971Sobrien if (bare_lfs) { 113679971Sobrien fprintf(ttyout, 113779971Sobrien "WARNING! %d bare linefeeds received in ASCII mode.\n", 113879971Sobrien bare_lfs); 113979971Sobrien fputs("File may not have transferred correctly.\n", ttyout); 114079971Sobrien } 114179971Sobrien if (bytes >= 0 && is_retr) { 114279971Sobrien if (bytes > 0) 114379971Sobrien ptransfer(0); 114479971Sobrien if (preserve && (closefunc == fclose)) { 114579971Sobrien mtime = remotemodtime(remote, 0); 114679971Sobrien if (mtime != -1) { 114779971Sobrien (void)gettimeofday(&tval[0], NULL); 114879971Sobrien tval[1].tv_sec = mtime; 114979971Sobrien tval[1].tv_usec = 0; 115079971Sobrien if (utimes(local, tval) == -1) { 115179971Sobrien fprintf(ttyout, 115279971Sobrien "Can't change modification time on %s to %s", 1153223328Sgavin local, 1154223328Sgavin rfc2822time(localtime(&mtime))); 115579971Sobrien } 115679971Sobrien } 115779971Sobrien } 115879971Sobrien } 115979971Sobrien goto cleanuprecv; 116079971Sobrien 116179971Sobrien abort: 116279971Sobrien /* 1163223328Sgavin * abort using RFC0959 recommended IP,SYNC sequence 116479971Sobrien */ 116579971Sobrien if (! sigsetjmp(xferabort, 1)) { 116679971Sobrien /* this is the first call */ 116779971Sobrien (void)xsignal(SIGINT, abort_squared); 116879971Sobrien if (!cpend) { 116979971Sobrien code = -1; 117079971Sobrien goto cleanuprecv; 117179971Sobrien } 117279971Sobrien abort_remote(din); 117379971Sobrien } 117479971Sobrien code = -1; 117579971Sobrien if (bytes > 0) 117679971Sobrien ptransfer(0); 117779971Sobrien 117879971Sobrien cleanuprecv: 117979971Sobrien if (oldintr) 118079971Sobrien (void)xsignal(SIGINT, oldintr); 118179971Sobrien if (oldintp) 118279971Sobrien (void)xsignal(SIGPIPE, oldintp); 118379971Sobrien if (data >= 0) { 118479971Sobrien (void)close(data); 118579971Sobrien data = -1; 118679971Sobrien } 118779971Sobrien if (closefunc != NULL && fout != NULL) 118879971Sobrien (*closefunc)(fout); 118979971Sobrien if (din) 119079971Sobrien (void)fclose(din); 119179971Sobrien progress = oprogress; 119279971Sobrien preserve = opreserve; 119379971Sobrien bytes = 0; 119479971Sobrien} 119579971Sobrien 119679971Sobrien/* 119779971Sobrien * Need to start a listen on the data channel before we send the command, 119879971Sobrien * otherwise the server's connect may fail. 119979971Sobrien */ 120079971Sobrienint 120179971Sobrieninitconn(void) 120279971Sobrien{ 120379971Sobrien char *p, *a; 1204146309Smikeh int result, tmpno = 0; 120579971Sobrien int on = 1; 120679971Sobrien int error; 1207223328Sgavin unsigned int addr[16], port[2]; 1208223328Sgavin unsigned int af, hal, pal; 1209146309Smikeh socklen_t len; 1210223328Sgavin const char *pasvcmd = NULL; 1211223328Sgavin int overbose; 121279971Sobrien 121379971Sobrien#ifdef INET6 1214223328Sgavin#ifndef NO_DEBUG 1215223328Sgavin if (myctladdr.su_family == AF_INET6 && ftp_debug && 121679971Sobrien (IN6_IS_ADDR_LINKLOCAL(&myctladdr.si_su.su_sin6.sin6_addr) || 121779971Sobrien IN6_IS_ADDR_SITELOCAL(&myctladdr.si_su.su_sin6.sin6_addr))) { 1218223328Sgavin warnx("Use of scoped addresses can be troublesome"); 121979971Sobrien } 122079971Sobrien#endif 1221223328Sgavin#endif 1222223328Sgavin 122379971Sobrien reinit: 122479971Sobrien if (passivemode) { 122579971Sobrien data_addr = myctladdr; 122679971Sobrien data = socket(data_addr.su_family, SOCK_STREAM, 0); 122779971Sobrien if (data < 0) { 1228223328Sgavin warn("Can't create socket for data connection"); 122979971Sobrien return (1); 123079971Sobrien } 123179971Sobrien if ((options & SO_DEBUG) && 1232146309Smikeh setsockopt(data, SOL_SOCKET, SO_DEBUG, 1233146309Smikeh (void *)&on, sizeof(on)) == -1) { 1234223328Sgavin DWARN("setsockopt %s (ignored)", "SO_DEBUG"); 1235146309Smikeh } 123679971Sobrien result = COMPLETE + 1; 123779971Sobrien switch (data_addr.su_family) { 123879971Sobrien case AF_INET: 123979971Sobrien if (epsv4 && !epsv4bad) { 1240146309Smikeh pasvcmd = "EPSV"; 1241223328Sgavin overbose = verbose; 1242223328Sgavin if (ftp_debug == 0) 1243223328Sgavin verbose = -1; 124498247Smikeh result = command("EPSV"); 1245223328Sgavin verbose = overbose; 1246223328Sgavin if (verbose > 0 && 1247223328Sgavin (result == COMPLETE || !connected)) 1248223328Sgavin fprintf(ttyout, "%s\n", reply_string); 124979971Sobrien if (!connected) 125079971Sobrien return (1); 125179971Sobrien /* 125279971Sobrien * this code is to be friendly with broken 125379971Sobrien * BSDI ftpd 125479971Sobrien */ 125579971Sobrien if (code / 10 == 22 && code != 229) { 125679971Sobrien fputs( 125779971Sobrien"wrong server: return code must be 229\n", 125879971Sobrien ttyout); 125979971Sobrien result = COMPLETE + 1; 126079971Sobrien } 126179971Sobrien if (result != COMPLETE) { 126279971Sobrien epsv4bad = 1; 1263223328Sgavin DPRINTF("disabling epsv4 for this " 1264223328Sgavin "connection\n"); 126579971Sobrien } 126679971Sobrien } 126779971Sobrien if (result != COMPLETE) { 1268146309Smikeh pasvcmd = "PASV"; 126998247Smikeh result = command("PASV"); 127079971Sobrien if (!connected) 127179971Sobrien return (1); 127279971Sobrien } 127379971Sobrien break; 127479971Sobrien#ifdef INET6 127579971Sobrien case AF_INET6: 1276223328Sgavin if (epsv6 && !epsv6bad) { 1277223328Sgavin pasvcmd = "EPSV"; 1278223328Sgavin overbose = verbose; 1279223328Sgavin if (ftp_debug == 0) 1280223328Sgavin verbose = -1; 1281223328Sgavin result = command("EPSV"); 1282223328Sgavin verbose = overbose; 1283223328Sgavin if (verbose > 0 && 1284223328Sgavin (result == COMPLETE || !connected)) 1285223328Sgavin fprintf(ttyout, "%s\n", reply_string); 1286223328Sgavin if (!connected) 1287223328Sgavin return (1); 1288223328Sgavin /* 1289223328Sgavin * this code is to be friendly with 1290223328Sgavin * broken BSDI ftpd 1291223328Sgavin */ 1292223328Sgavin if (code / 10 == 22 && code != 229) { 1293223328Sgavin fputs( 1294223328Sgavin "wrong server: return code must be 229\n", 1295223328Sgavin ttyout); 1296223328Sgavin result = COMPLETE + 1; 1297223328Sgavin } 1298223328Sgavin if (result != COMPLETE) { 1299223328Sgavin epsv6bad = 1; 1300223328Sgavin DPRINTF("disabling epsv6 for this " 1301223328Sgavin "connection\n"); 1302223328Sgavin } 130379971Sobrien } 130498247Smikeh if (result != COMPLETE) { 130598247Smikeh pasvcmd = "LPSV"; 130698247Smikeh result = command("LPSV"); 130798247Smikeh } 130879971Sobrien if (!connected) 130979971Sobrien return (1); 131079971Sobrien break; 131179971Sobrien#endif 131279971Sobrien default: 131379971Sobrien result = COMPLETE + 1; 131479971Sobrien break; 131579971Sobrien } 131679971Sobrien if (result != COMPLETE) { 131779971Sobrien if (activefallback) { 131879971Sobrien (void)close(data); 131979971Sobrien data = -1; 132079971Sobrien passivemode = 0; 132179971Sobrien#if 0 132279971Sobrien activefallback = 0; 132379971Sobrien#endif 132479971Sobrien goto reinit; 132579971Sobrien } 132679971Sobrien fputs("Passive mode refused.\n", ttyout); 132779971Sobrien goto bad; 132879971Sobrien } 132979971Sobrien 133079971Sobrien#define pack2(var, off) \ 133179971Sobrien (((var[(off) + 0] & 0xff) << 8) | ((var[(off) + 1] & 0xff) << 0)) 133279971Sobrien#define pack4(var, off) \ 133379971Sobrien (((var[(off) + 0] & 0xff) << 24) | ((var[(off) + 1] & 0xff) << 16) | \ 133479971Sobrien ((var[(off) + 2] & 0xff) << 8) | ((var[(off) + 3] & 0xff) << 0)) 133579971Sobrien#define UC(b) (((int)b)&0xff) 133679971Sobrien 133779971Sobrien /* 133879971Sobrien * What we've got at this point is a string of comma separated 133979971Sobrien * one-byte unsigned integer values, separated by commas. 134079971Sobrien */ 134179971Sobrien if (strcmp(pasvcmd, "PASV") == 0) { 134279971Sobrien if (data_addr.su_family != AF_INET) { 134379971Sobrien fputs( 134479971Sobrien "Passive mode AF mismatch. Shouldn't happen!\n", ttyout); 134579971Sobrien error = 1; 134679971Sobrien goto bad; 134779971Sobrien } 134879971Sobrien if (code / 10 == 22 && code != 227) { 134979971Sobrien fputs("wrong server: return code must be 227\n", 135079971Sobrien ttyout); 135179971Sobrien error = 1; 135279971Sobrien goto bad; 135379971Sobrien } 135479971Sobrien error = sscanf(pasv, "%u,%u,%u,%u,%u,%u", 135579971Sobrien &addr[0], &addr[1], &addr[2], &addr[3], 135679971Sobrien &port[0], &port[1]); 135779971Sobrien if (error != 6) { 135879971Sobrien fputs( 135979971Sobrien"Passive mode address scan failure. Shouldn't happen!\n", ttyout); 136079971Sobrien error = 1; 136179971Sobrien goto bad; 136279971Sobrien } 136379971Sobrien error = 0; 136479971Sobrien memset(&data_addr, 0, sizeof(data_addr)); 136579971Sobrien data_addr.su_family = AF_INET; 136679971Sobrien data_addr.su_len = sizeof(struct sockaddr_in); 136779971Sobrien data_addr.si_su.su_sin.sin_addr.s_addr = 136879971Sobrien htonl(pack4(addr, 0)); 136979971Sobrien data_addr.su_port = htons(pack2(port, 0)); 137079971Sobrien } else if (strcmp(pasvcmd, "LPSV") == 0) { 137179971Sobrien if (code / 10 == 22 && code != 228) { 137279971Sobrien fputs("wrong server: return code must be 228\n", 137379971Sobrien ttyout); 137479971Sobrien error = 1; 137579971Sobrien goto bad; 137679971Sobrien } 137779971Sobrien switch (data_addr.su_family) { 137879971Sobrien case AF_INET: 137979971Sobrien error = sscanf(pasv, 138079971Sobrien"%u,%u,%u,%u,%u,%u,%u,%u,%u", 138179971Sobrien &af, &hal, 138279971Sobrien &addr[0], &addr[1], &addr[2], &addr[3], 138379971Sobrien &pal, &port[0], &port[1]); 138479971Sobrien if (error != 9) { 138579971Sobrien fputs( 138679971Sobrien"Passive mode address scan failure. Shouldn't happen!\n", ttyout); 138779971Sobrien error = 1; 138879971Sobrien goto bad; 138979971Sobrien } 139079971Sobrien if (af != 4 || hal != 4 || pal != 2) { 139179971Sobrien fputs( 139279971Sobrien"Passive mode AF mismatch. Shouldn't happen!\n", ttyout); 139379971Sobrien error = 1; 139479971Sobrien goto bad; 139579971Sobrien } 139679971Sobrien 139779971Sobrien error = 0; 139879971Sobrien memset(&data_addr, 0, sizeof(data_addr)); 139979971Sobrien data_addr.su_family = AF_INET; 140079971Sobrien data_addr.su_len = sizeof(struct sockaddr_in); 140179971Sobrien data_addr.si_su.su_sin.sin_addr.s_addr = 140279971Sobrien htonl(pack4(addr, 0)); 140379971Sobrien data_addr.su_port = htons(pack2(port, 0)); 140479971Sobrien break; 140579971Sobrien#ifdef INET6 140679971Sobrien case AF_INET6: 140779971Sobrien error = sscanf(pasv, 140879971Sobrien"%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u", 140979971Sobrien &af, &hal, 141079971Sobrien &addr[0], &addr[1], &addr[2], &addr[3], 141179971Sobrien &addr[4], &addr[5], &addr[6], &addr[7], 141279971Sobrien &addr[8], &addr[9], &addr[10], 141379971Sobrien &addr[11], &addr[12], &addr[13], 141479971Sobrien &addr[14], &addr[15], 141579971Sobrien &pal, &port[0], &port[1]); 141679971Sobrien if (error != 21) { 141779971Sobrien fputs( 141879971Sobrien"Passive mode address scan failure. Shouldn't happen!\n", ttyout); 141979971Sobrien error = 1; 142079971Sobrien goto bad; 142179971Sobrien } 142279971Sobrien if (af != 6 || hal != 16 || pal != 2) { 142379971Sobrien fputs( 142479971Sobrien"Passive mode AF mismatch. Shouldn't happen!\n", ttyout); 142579971Sobrien error = 1; 142679971Sobrien goto bad; 142779971Sobrien } 142879971Sobrien 142979971Sobrien error = 0; 143079971Sobrien memset(&data_addr, 0, sizeof(data_addr)); 143179971Sobrien data_addr.su_family = AF_INET6; 143279971Sobrien data_addr.su_len = sizeof(struct sockaddr_in6); 143379971Sobrien { 1434223328Sgavin size_t i; 143579971Sobrien for (i = 0; i < sizeof(struct in6_addr); i++) { 143679971Sobrien data_addr.si_su.su_sin6.sin6_addr.s6_addr[i] = 143779971Sobrien UC(addr[i]); 143879971Sobrien } 143979971Sobrien } 144079971Sobrien data_addr.su_port = htons(pack2(port, 0)); 144179971Sobrien break; 144279971Sobrien#endif 144379971Sobrien default: 144479971Sobrien error = 1; 144579971Sobrien } 144679971Sobrien } else if (strcmp(pasvcmd, "EPSV") == 0) { 144779971Sobrien char delim[4]; 144879971Sobrien 144979971Sobrien port[0] = 0; 145079971Sobrien if (code / 10 == 22 && code != 229) { 145179971Sobrien fputs("wrong server: return code must be 229\n", 145279971Sobrien ttyout); 145379971Sobrien error = 1; 145479971Sobrien goto bad; 145579971Sobrien } 145679971Sobrien if (sscanf(pasv, "%c%c%c%d%c", &delim[0], 145779971Sobrien &delim[1], &delim[2], &port[1], 145879971Sobrien &delim[3]) != 5) { 145979971Sobrien fputs("parse error!\n", ttyout); 146079971Sobrien error = 1; 146179971Sobrien goto bad; 146279971Sobrien } 146379971Sobrien if (delim[0] != delim[1] || delim[0] != delim[2] 146479971Sobrien || delim[0] != delim[3]) { 146579971Sobrien fputs("parse error!\n", ttyout); 146679971Sobrien error = 1; 146779971Sobrien goto bad; 146879971Sobrien } 146979971Sobrien data_addr = hisctladdr; 147079971Sobrien data_addr.su_port = htons(port[1]); 147179971Sobrien } else 147279971Sobrien goto bad; 147379971Sobrien 1474223328Sgavin if (ftp_connect(data, (struct sockaddr *)&data_addr.si_su, 1475223328Sgavin data_addr.su_len) < 0) { 147679971Sobrien if (activefallback) { 147779971Sobrien (void)close(data); 147879971Sobrien data = -1; 147979971Sobrien passivemode = 0; 148079971Sobrien#if 0 148179971Sobrien activefallback = 0; 148279971Sobrien#endif 148379971Sobrien goto reinit; 148479971Sobrien } 148579971Sobrien goto bad; 148679971Sobrien } 148779971Sobrien#ifdef IPTOS_THROUGHPUT 148879971Sobrien if (data_addr.su_family == AF_INET) { 148979971Sobrien on = IPTOS_THROUGHPUT; 1490146309Smikeh if (setsockopt(data, IPPROTO_IP, IP_TOS, 1491146309Smikeh (void *)&on, sizeof(on)) == -1) { 1492223328Sgavin DWARN("setsockopt %s (ignored)", 1493223328Sgavin "IPTOS_THROUGHPUT"); 1494146309Smikeh } 149579971Sobrien } 149679971Sobrien#endif 149779971Sobrien return (0); 149879971Sobrien } 149979971Sobrien 150079971Sobrien noport: 150179971Sobrien data_addr = myctladdr; 150279971Sobrien if (sendport) 150379971Sobrien data_addr.su_port = 0; /* let system pick one */ 150479971Sobrien if (data != -1) 150579971Sobrien (void)close(data); 150679971Sobrien data = socket(data_addr.su_family, SOCK_STREAM, 0); 150779971Sobrien if (data < 0) { 1508223328Sgavin warn("Can't create socket for data connection"); 150979971Sobrien if (tmpno) 151079971Sobrien sendport = 1; 151179971Sobrien return (1); 151279971Sobrien } 151379971Sobrien if (!sendport) 1514146309Smikeh if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, 1515146309Smikeh (void *)&on, sizeof(on)) == -1) { 1516223328Sgavin warn("Can't set SO_REUSEADDR on data connection"); 151779971Sobrien goto bad; 151879971Sobrien } 151979971Sobrien if (bind(data, (struct sockaddr *)&data_addr.si_su, 152079971Sobrien data_addr.su_len) < 0) { 1521223328Sgavin warn("Can't bind for data connection"); 152279971Sobrien goto bad; 152379971Sobrien } 1524146309Smikeh if ((options & SO_DEBUG) && 1525146309Smikeh setsockopt(data, SOL_SOCKET, SO_DEBUG, 1526146309Smikeh (void *)&on, sizeof(on)) == -1) { 1527223328Sgavin DWARN("setsockopt %s (ignored)", "SO_DEBUG"); 1528146309Smikeh } 152979971Sobrien len = sizeof(data_addr.si_su); 153079971Sobrien memset((char *)&data_addr, 0, sizeof (data_addr)); 1531146309Smikeh if (getsockname(data, (struct sockaddr *)&data_addr.si_su, &len) == -1) { 1532223328Sgavin warn("Can't determine my address of data connection"); 153379971Sobrien goto bad; 153479971Sobrien } 153579971Sobrien data_addr.su_len = len; 1536223328Sgavin if (ftp_listen(data, 1) < 0) 1537223328Sgavin warn("Can't listen to data connection"); 153879971Sobrien 153979971Sobrien if (sendport) { 154098247Smikeh char hname[NI_MAXHOST], sname[NI_MAXSERV]; 154198247Smikeh struct sockinet tmp; 154279971Sobrien 154379971Sobrien switch (data_addr.su_family) { 154479971Sobrien case AF_INET: 154579971Sobrien if (!epsv4 || epsv4bad) { 154679971Sobrien result = COMPLETE + 1; 154779971Sobrien break; 154879971Sobrien } 154979971Sobrien /* FALLTHROUGH */ 155079971Sobrien#ifdef INET6 155179971Sobrien case AF_INET6: 1552223328Sgavin if (!epsv6 || epsv6bad) { 1553223328Sgavin result = COMPLETE + 1; 1554223328Sgavin break; 1555223328Sgavin } 155698247Smikeh#endif 155779971Sobrien af = (data_addr.su_family == AF_INET) ? 1 : 2; 155898247Smikeh tmp = data_addr; 155998247Smikeh#ifdef INET6 156098247Smikeh if (tmp.su_family == AF_INET6) 156198247Smikeh tmp.si_su.su_sin6.sin6_scope_id = 0; 156298247Smikeh#endif 156398247Smikeh if (getnameinfo((struct sockaddr *)&tmp.si_su, 156498247Smikeh tmp.su_len, hname, sizeof(hname), sname, 156598247Smikeh sizeof(sname), NI_NUMERICHOST | NI_NUMERICSERV)) { 156679971Sobrien result = ERROR; 156779971Sobrien } else { 1568223328Sgavin overbose = verbose; 1569223328Sgavin if (ftp_debug == 0) 1570223328Sgavin verbose = -1; 1571223328Sgavin result = command("EPRT |%u|%s|%s|", af, hname, 157298247Smikeh sname); 1573223328Sgavin verbose = overbose; 1574223328Sgavin if (verbose > 0 && 1575223328Sgavin (result == COMPLETE || !connected)) 1576223328Sgavin fprintf(ttyout, "%s\n", reply_string); 157779971Sobrien if (!connected) 157879971Sobrien return (1); 157979971Sobrien if (result != COMPLETE) { 158079971Sobrien epsv4bad = 1; 1581223328Sgavin DPRINTF("disabling epsv4 for this " 1582223328Sgavin "connection\n"); 158379971Sobrien } 158479971Sobrien } 158579971Sobrien break; 158679971Sobrien default: 158779971Sobrien result = COMPLETE + 1; 158879971Sobrien break; 158979971Sobrien } 159079971Sobrien if (result == COMPLETE) 159179971Sobrien goto skip_port; 159279971Sobrien 159379971Sobrien switch (data_addr.su_family) { 159479971Sobrien case AF_INET: 159579971Sobrien a = (char *)&data_addr.si_su.su_sin.sin_addr; 159679971Sobrien p = (char *)&data_addr.su_port; 159779971Sobrien result = command("PORT %d,%d,%d,%d,%d,%d", 159879971Sobrien UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 159979971Sobrien UC(p[0]), UC(p[1])); 160079971Sobrien break; 160179971Sobrien#ifdef INET6 160279971Sobrien case AF_INET6: 160379971Sobrien a = (char *)&data_addr.si_su.su_sin6.sin6_addr; 160479971Sobrien p = (char *)&data_addr.su_port; 160579971Sobrien result = command( 160679971Sobrien "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", 160779971Sobrien 6, 16, 160879971Sobrien UC(a[0]),UC(a[1]),UC(a[2]),UC(a[3]), 160979971Sobrien UC(a[4]),UC(a[5]),UC(a[6]),UC(a[7]), 161079971Sobrien UC(a[8]),UC(a[9]),UC(a[10]),UC(a[11]), 161179971Sobrien UC(a[12]),UC(a[13]),UC(a[14]),UC(a[15]), 161279971Sobrien 2, UC(p[0]), UC(p[1])); 161379971Sobrien break; 161479971Sobrien#endif 161579971Sobrien default: 161679971Sobrien result = COMPLETE + 1; /* xxx */ 161779971Sobrien } 161879971Sobrien if (!connected) 161979971Sobrien return (1); 162079971Sobrien skip_port: 1621146309Smikeh 162279971Sobrien if (result == ERROR && sendport == -1) { 162379971Sobrien sendport = 0; 162479971Sobrien tmpno = 1; 162579971Sobrien goto noport; 162679971Sobrien } 162779971Sobrien return (result != COMPLETE); 162879971Sobrien } 162979971Sobrien if (tmpno) 163079971Sobrien sendport = 1; 163179971Sobrien#ifdef IPTOS_THROUGHPUT 163279971Sobrien if (data_addr.su_family == AF_INET) { 163379971Sobrien on = IPTOS_THROUGHPUT; 1634146309Smikeh if (setsockopt(data, IPPROTO_IP, IP_TOS, 1635223328Sgavin (void *)&on, sizeof(on)) == -1) { 1636223328Sgavin DWARN("setsockopt %s (ignored)", "IPTOS_THROUGHPUT"); 1637223328Sgavin } 163879971Sobrien } 163979971Sobrien#endif 164079971Sobrien return (0); 164179971Sobrien bad: 1642146309Smikeh (void)close(data); 1643146309Smikeh data = -1; 164479971Sobrien if (tmpno) 164579971Sobrien sendport = 1; 164679971Sobrien return (1); 164779971Sobrien} 164879971Sobrien 164979971SobrienFILE * 165079971Sobriendataconn(const char *lmode) 165179971Sobrien{ 1652146309Smikeh struct sockinet from; 1653146309Smikeh int s, flags, rv, timeout; 1654146309Smikeh struct timeval endtime, now, td; 1655146309Smikeh struct pollfd pfd[1]; 1656146309Smikeh socklen_t fromlen; 165779971Sobrien 1658146309Smikeh if (passivemode) /* passive data connection */ 165979971Sobrien return (fdopen(data, lmode)); 166079971Sobrien 1661146309Smikeh /* active mode data connection */ 1662146309Smikeh 1663146309Smikeh if ((flags = fcntl(data, F_GETFL, 0)) == -1) 1664146309Smikeh goto dataconn_failed; /* get current socket flags */ 1665146309Smikeh if (fcntl(data, F_SETFL, flags | O_NONBLOCK) == -1) 1666146309Smikeh goto dataconn_failed; /* set non-blocking connect */ 1667146309Smikeh 1668146309Smikeh /* NOTE: we now must restore socket flags on successful exit */ 1669146309Smikeh 1670146309Smikeh /* limit time waiting on listening socket */ 1671146309Smikeh pfd[0].fd = data; 1672146309Smikeh pfd[0].events = POLLIN; 1673146309Smikeh (void)gettimeofday(&endtime, NULL); /* determine end time */ 1674146309Smikeh endtime.tv_sec += (quit_time > 0) ? quit_time: 60; 1675146309Smikeh /* without -q, default to 60s */ 1676146309Smikeh do { 1677146309Smikeh (void)gettimeofday(&now, NULL); 1678146309Smikeh timersub(&endtime, &now, &td); 1679146309Smikeh timeout = td.tv_sec * 1000 + td.tv_usec/1000; 1680146309Smikeh if (timeout < 0) 1681146309Smikeh timeout = 0; 1682223328Sgavin rv = ftp_poll(pfd, 1, timeout); 1683146309Smikeh } while (rv == -1 && errno == EINTR); /* loop until poll ! EINTR */ 1684146309Smikeh if (rv == -1) { 1685223328Sgavin warn("Can't poll waiting before accept"); 1686146309Smikeh goto dataconn_failed; 1687146309Smikeh } 1688146309Smikeh if (rv == 0) { 1689223328Sgavin warnx("Poll timeout waiting before accept"); 1690146309Smikeh goto dataconn_failed; 1691146309Smikeh } 1692146309Smikeh 1693146309Smikeh /* (non-blocking) accept the connection */ 1694146309Smikeh fromlen = myctladdr.su_len; 1695146309Smikeh do { 1696146309Smikeh s = accept(data, (struct sockaddr *) &from.si_su, &fromlen); 1697146309Smikeh } while (s == -1 && errno == EINTR); /* loop until accept ! EINTR */ 1698146309Smikeh if (s == -1) { 1699223328Sgavin warn("Can't accept data connection"); 1700146309Smikeh goto dataconn_failed; 170179971Sobrien } 1702146309Smikeh 170379971Sobrien (void)close(data); 170479971Sobrien data = s; 1705146309Smikeh if (fcntl(data, F_SETFL, flags) == -1) /* restore socket flags */ 1706146309Smikeh goto dataconn_failed; 1707146309Smikeh 170879971Sobrien#ifdef IPTOS_THROUGHPUT 170979971Sobrien if (from.su_family == AF_INET) { 171079971Sobrien int tos = IPTOS_THROUGHPUT; 1711146309Smikeh if (setsockopt(s, IPPROTO_IP, IP_TOS, 1712146309Smikeh (void *)&tos, sizeof(tos)) == -1) { 1713223328Sgavin DWARN("setsockopt %s (ignored)", "IPTOS_THROUGHPUT"); 171479971Sobrien } 171579971Sobrien } 171679971Sobrien#endif 171779971Sobrien return (fdopen(data, lmode)); 1718146309Smikeh 1719146309Smikeh dataconn_failed: 1720146309Smikeh (void)close(data); 1721146309Smikeh data = -1; 1722146309Smikeh return (NULL); 172379971Sobrien} 172479971Sobrien 172579971Sobrienvoid 172679971Sobrienpsabort(int notused) 172779971Sobrien{ 172879971Sobrien int oerrno = errno; 172979971Sobrien 1730142129Smikeh sigint_raised = 1; 173179971Sobrien alarmtimer(0); 173279971Sobrien abrtflag++; 173379971Sobrien errno = oerrno; 173479971Sobrien} 173579971Sobrien 173679971Sobrienvoid 173779971Sobrienpswitch(int flag) 173879971Sobrien{ 173979971Sobrien sigfunc oldintr; 174079971Sobrien static struct comvars { 174179971Sobrien int connect; 174279971Sobrien char name[MAXHOSTNAMELEN]; 174379971Sobrien struct sockinet mctl; 174479971Sobrien struct sockinet hctl; 174579971Sobrien FILE *in; 174679971Sobrien FILE *out; 174779971Sobrien int tpe; 174879971Sobrien int curtpe; 174979971Sobrien int cpnd; 175079971Sobrien int sunqe; 175179971Sobrien int runqe; 175279971Sobrien int mcse; 175379971Sobrien int ntflg; 175479971Sobrien char nti[17]; 175579971Sobrien char nto[17]; 175679971Sobrien int mapflg; 175779971Sobrien char mi[MAXPATHLEN]; 175879971Sobrien char mo[MAXPATHLEN]; 175979971Sobrien } proxstruct, tmpstruct; 176079971Sobrien struct comvars *ip, *op; 176179971Sobrien 176279971Sobrien abrtflag = 0; 176379971Sobrien oldintr = xsignal(SIGINT, psabort); 176479971Sobrien if (flag) { 176579971Sobrien if (proxy) 176679971Sobrien return; 176779971Sobrien ip = &tmpstruct; 176879971Sobrien op = &proxstruct; 176979971Sobrien proxy++; 177079971Sobrien } else { 177179971Sobrien if (!proxy) 177279971Sobrien return; 177379971Sobrien ip = &proxstruct; 177479971Sobrien op = &tmpstruct; 177579971Sobrien proxy = 0; 177679971Sobrien } 177779971Sobrien ip->connect = connected; 177879971Sobrien connected = op->connect; 177979971Sobrien if (hostname) 178079971Sobrien (void)strlcpy(ip->name, hostname, sizeof(ip->name)); 178179971Sobrien else 178279971Sobrien ip->name[0] = '\0'; 178379971Sobrien hostname = op->name; 178479971Sobrien ip->hctl = hisctladdr; 178579971Sobrien hisctladdr = op->hctl; 178679971Sobrien ip->mctl = myctladdr; 178779971Sobrien myctladdr = op->mctl; 178879971Sobrien ip->in = cin; 178979971Sobrien cin = op->in; 179079971Sobrien ip->out = cout; 179179971Sobrien cout = op->out; 179279971Sobrien ip->tpe = type; 179379971Sobrien type = op->tpe; 179479971Sobrien ip->curtpe = curtype; 179579971Sobrien curtype = op->curtpe; 179679971Sobrien ip->cpnd = cpend; 179779971Sobrien cpend = op->cpnd; 179879971Sobrien ip->sunqe = sunique; 179979971Sobrien sunique = op->sunqe; 180079971Sobrien ip->runqe = runique; 180179971Sobrien runique = op->runqe; 180279971Sobrien ip->mcse = mcase; 180379971Sobrien mcase = op->mcse; 180479971Sobrien ip->ntflg = ntflag; 180579971Sobrien ntflag = op->ntflg; 180679971Sobrien (void)strlcpy(ip->nti, ntin, sizeof(ip->nti)); 180779971Sobrien (void)strlcpy(ntin, op->nti, sizeof(ntin)); 180879971Sobrien (void)strlcpy(ip->nto, ntout, sizeof(ip->nto)); 180979971Sobrien (void)strlcpy(ntout, op->nto, sizeof(ntout)); 181079971Sobrien ip->mapflg = mapflag; 181179971Sobrien mapflag = op->mapflg; 181279971Sobrien (void)strlcpy(ip->mi, mapin, sizeof(ip->mi)); 181379971Sobrien (void)strlcpy(mapin, op->mi, sizeof(mapin)); 181479971Sobrien (void)strlcpy(ip->mo, mapout, sizeof(ip->mo)); 181579971Sobrien (void)strlcpy(mapout, op->mo, sizeof(mapout)); 181679971Sobrien (void)xsignal(SIGINT, oldintr); 181779971Sobrien if (abrtflag) { 181879971Sobrien abrtflag = 0; 181979971Sobrien (*oldintr)(SIGINT); 182079971Sobrien } 182179971Sobrien} 182279971Sobrien 182379971Sobrienvoid 182479971Sobrienabortpt(int notused) 182579971Sobrien{ 182679971Sobrien 1827142129Smikeh sigint_raised = 1; 182879971Sobrien alarmtimer(0); 182979971Sobrien if (fromatty) 183079971Sobrien write(fileno(ttyout), "\n", 1); 183179971Sobrien ptabflg++; 183279971Sobrien mflag = 0; 183379971Sobrien abrtflag = 0; 183479971Sobrien siglongjmp(ptabort, 1); 183579971Sobrien} 183679971Sobrien 183779971Sobrienvoid 183879971Sobrienproxtrans(const char *cmd, const char *local, const char *remote) 183979971Sobrien{ 1840223328Sgavin sigfunc volatile oldintr; 184179971Sobrien int prox_type, nfnd; 1842223328Sgavin int volatile secndflag; 1843223328Sgavin const char *volatile cmd2; 184479971Sobrien 184579971Sobrien oldintr = NULL; 184679971Sobrien secndflag = 0; 184779971Sobrien if (strcmp(cmd, "RETR")) 184879971Sobrien cmd2 = "RETR"; 184979971Sobrien else 185079971Sobrien cmd2 = runique ? "STOU" : "STOR"; 185179971Sobrien if ((prox_type = type) == 0) { 185279971Sobrien if (unix_server && unix_proxy) 185379971Sobrien prox_type = TYPE_I; 185479971Sobrien else 185579971Sobrien prox_type = TYPE_A; 185679971Sobrien } 185779971Sobrien if (curtype != prox_type) 185879971Sobrien changetype(prox_type, 1); 185979971Sobrien if (command("PASV") != COMPLETE) { 186079971Sobrien fputs("proxy server does not support third party transfers.\n", 186179971Sobrien ttyout); 186279971Sobrien return; 186379971Sobrien } 186479971Sobrien pswitch(0); 186579971Sobrien if (!connected) { 186679971Sobrien fputs("No primary connection.\n", ttyout); 186779971Sobrien pswitch(1); 186879971Sobrien code = -1; 186979971Sobrien return; 187079971Sobrien } 187179971Sobrien if (curtype != prox_type) 187279971Sobrien changetype(prox_type, 1); 187379971Sobrien if (command("PORT %s", pasv) != COMPLETE) { 187479971Sobrien pswitch(1); 187579971Sobrien return; 187679971Sobrien } 187779971Sobrien if (sigsetjmp(ptabort, 1)) 187879971Sobrien goto abort; 187979971Sobrien oldintr = xsignal(SIGINT, abortpt); 188079971Sobrien if ((restart_point && 188179971Sobrien (command("REST " LLF, (LLT) restart_point) != CONTINUE)) 188279971Sobrien || (command("%s %s", cmd, remote) != PRELIM)) { 188379971Sobrien (void)xsignal(SIGINT, oldintr); 188479971Sobrien pswitch(1); 188579971Sobrien return; 188679971Sobrien } 188779971Sobrien sleep(2); 188879971Sobrien pswitch(1); 188979971Sobrien secndflag++; 189079971Sobrien if ((restart_point && 189179971Sobrien (command("REST " LLF, (LLT) restart_point) != CONTINUE)) 189279971Sobrien || (command("%s %s", cmd2, local) != PRELIM)) 189379971Sobrien goto abort; 189479971Sobrien ptflag++; 189579971Sobrien (void)getreply(0); 189679971Sobrien pswitch(0); 189779971Sobrien (void)getreply(0); 189879971Sobrien (void)xsignal(SIGINT, oldintr); 189979971Sobrien pswitch(1); 190079971Sobrien ptflag = 0; 190179971Sobrien fprintf(ttyout, "local: %s remote: %s\n", local, remote); 190279971Sobrien return; 190379971Sobrien abort: 190479971Sobrien if (sigsetjmp(xferabort, 1)) { 190579971Sobrien (void)xsignal(SIGINT, oldintr); 190679971Sobrien return; 190779971Sobrien } 190879971Sobrien (void)xsignal(SIGINT, abort_squared); 190979971Sobrien ptflag = 0; 191079971Sobrien if (strcmp(cmd, "RETR") && !proxy) 191179971Sobrien pswitch(1); 191279971Sobrien else if (!strcmp(cmd, "RETR") && proxy) 191379971Sobrien pswitch(0); 191479971Sobrien if (!cpend && !secndflag) { /* only here if cmd = "STOR" (proxy=1) */ 191579971Sobrien if (command("%s %s", cmd2, local) != PRELIM) { 191679971Sobrien pswitch(0); 191779971Sobrien if (cpend) 191879971Sobrien abort_remote(NULL); 191979971Sobrien } 192079971Sobrien pswitch(1); 192179971Sobrien if (ptabflg) 192279971Sobrien code = -1; 192379971Sobrien (void)xsignal(SIGINT, oldintr); 192479971Sobrien return; 192579971Sobrien } 192679971Sobrien if (cpend) 192779971Sobrien abort_remote(NULL); 192879971Sobrien pswitch(!proxy); 192979971Sobrien if (!cpend && !secndflag) { /* only if cmd = "RETR" (proxy=1) */ 193079971Sobrien if (command("%s %s", cmd2, local) != PRELIM) { 193179971Sobrien pswitch(0); 193279971Sobrien if (cpend) 193379971Sobrien abort_remote(NULL); 193479971Sobrien pswitch(1); 193579971Sobrien if (ptabflg) 193679971Sobrien code = -1; 193779971Sobrien (void)xsignal(SIGINT, oldintr); 193879971Sobrien return; 193979971Sobrien } 194079971Sobrien } 194179971Sobrien if (cpend) 194279971Sobrien abort_remote(NULL); 194379971Sobrien pswitch(!proxy); 194479971Sobrien if (cpend) { 194579971Sobrien if ((nfnd = empty(cin, NULL, 10)) <= 0) { 194679971Sobrien if (nfnd < 0) 1947223328Sgavin warn("Error aborting proxy command"); 194879971Sobrien if (ptabflg) 194979971Sobrien code = -1; 195079971Sobrien lostpeer(0); 195179971Sobrien } 195279971Sobrien (void)getreply(0); 195379971Sobrien (void)getreply(0); 195479971Sobrien } 195579971Sobrien if (proxy) 195679971Sobrien pswitch(0); 195779971Sobrien pswitch(1); 195879971Sobrien if (ptabflg) 195979971Sobrien code = -1; 196079971Sobrien (void)xsignal(SIGINT, oldintr); 196179971Sobrien} 196279971Sobrien 196379971Sobrienvoid 196479971Sobrienreset(int argc, char *argv[]) 196579971Sobrien{ 196679971Sobrien int nfnd = 1; 196779971Sobrien 196879971Sobrien if (argc == 0 && argv != NULL) { 1969223328Sgavin UPRINTF("usage: %s\n", argv[0]); 197079971Sobrien code = -1; 197179971Sobrien return; 197279971Sobrien } 197379971Sobrien while (nfnd > 0) { 197479971Sobrien if ((nfnd = empty(cin, NULL, 0)) < 0) { 1975223328Sgavin warn("Error resetting connection"); 197679971Sobrien code = -1; 197779971Sobrien lostpeer(0); 197879971Sobrien } else if (nfnd) 197979971Sobrien (void)getreply(0); 198079971Sobrien } 198179971Sobrien} 198279971Sobrien 198379971Sobrienchar * 198479971Sobriengunique(const char *local) 198579971Sobrien{ 198679971Sobrien static char new[MAXPATHLEN]; 198779971Sobrien char *cp = strrchr(local, '/'); 198879971Sobrien int d, count=0, len; 198979971Sobrien char ext = '1'; 199079971Sobrien 199179971Sobrien if (cp) 199279971Sobrien *cp = '\0'; 199379971Sobrien d = access(cp == local ? "/" : cp ? local : ".", W_OK); 199479971Sobrien if (cp) 199579971Sobrien *cp = '/'; 199679971Sobrien if (d < 0) { 1997223328Sgavin warn("Can't access `%s'", local); 199879971Sobrien return (NULL); 199979971Sobrien } 200079971Sobrien len = strlcpy(new, local, sizeof(new)); 200179971Sobrien cp = &new[len]; 2002146309Smikeh *cp++ = '.'; 200379971Sobrien while (!d) { 200479971Sobrien if (++count == 100) { 200579971Sobrien fputs("runique: can't find unique file name.\n", 200679971Sobrien ttyout); 200779971Sobrien return (NULL); 200879971Sobrien } 200979971Sobrien *cp++ = ext; 201079971Sobrien *cp = '\0'; 201179971Sobrien if (ext == '9') 201279971Sobrien ext = '0'; 201379971Sobrien else 201479971Sobrien ext++; 201579971Sobrien if ((d = access(new, F_OK)) < 0) 201679971Sobrien break; 201779971Sobrien if (ext != '0') 201879971Sobrien cp--; 201979971Sobrien else if (*(cp - 2) == '.') 202079971Sobrien *(cp - 1) = '1'; 202179971Sobrien else { 202279971Sobrien *(cp - 2) = *(cp - 2) + 1; 202379971Sobrien cp--; 202479971Sobrien } 202579971Sobrien } 202679971Sobrien return (new); 202779971Sobrien} 202879971Sobrien 202979971Sobrien/* 203079971Sobrien * abort_squared -- 203179971Sobrien * aborts abort_remote(). lostpeer() is called because if the user is 203279971Sobrien * too impatient to wait or there's another problem then ftp really 203379971Sobrien * needs to get back to a known state. 203479971Sobrien */ 203579971Sobrienvoid 203679971Sobrienabort_squared(int dummy) 203779971Sobrien{ 203879971Sobrien char msgbuf[100]; 2039146309Smikeh size_t len; 204079971Sobrien 2041142129Smikeh sigint_raised = 1; 204279971Sobrien alarmtimer(0); 204379971Sobrien len = strlcpy(msgbuf, "\nremote abort aborted; closing connection.\n", 204479971Sobrien sizeof(msgbuf)); 204579971Sobrien write(fileno(ttyout), msgbuf, len); 204679971Sobrien lostpeer(0); 204779971Sobrien siglongjmp(xferabort, 1); 204879971Sobrien} 204979971Sobrien 205079971Sobrienvoid 205179971Sobrienabort_remote(FILE *din) 205279971Sobrien{ 205379971Sobrien char buf[BUFSIZ]; 205479971Sobrien int nfnd; 205579971Sobrien 205679971Sobrien if (cout == NULL) { 2057223328Sgavin warnx("Lost control connection for abort"); 205879971Sobrien if (ptabflg) 205979971Sobrien code = -1; 206079971Sobrien lostpeer(0); 206179971Sobrien return; 206279971Sobrien } 206379971Sobrien /* 206479971Sobrien * send IAC in urgent mode instead of DM because 4.3BSD places oob mark 206579971Sobrien * after urgent byte rather than before as is protocol now 206679971Sobrien */ 206779971Sobrien buf[0] = IAC; 206879971Sobrien buf[1] = IP; 206979971Sobrien buf[2] = IAC; 207079971Sobrien if (send(fileno(cout), buf, 3, MSG_OOB) != 3) 2071223328Sgavin warn("Can't send abort message"); 207279971Sobrien fprintf(cout, "%cABOR\r\n", DM); 207379971Sobrien (void)fflush(cout); 207479971Sobrien if ((nfnd = empty(cin, din, 10)) <= 0) { 207579971Sobrien if (nfnd < 0) 2076223328Sgavin warn("Can't send abort message"); 207779971Sobrien if (ptabflg) 207879971Sobrien code = -1; 207979971Sobrien lostpeer(0); 208079971Sobrien } 208179971Sobrien if (din && (nfnd & 2)) { 208279971Sobrien while (read(fileno(din), buf, BUFSIZ) > 0) 208379971Sobrien continue; 208479971Sobrien } 208579971Sobrien if (getreply(0) == ERROR && code == 552) { 208679971Sobrien /* 552 needed for nic style abort */ 208779971Sobrien (void)getreply(0); 208879971Sobrien } 208979971Sobrien (void)getreply(0); 209079971Sobrien} 209179971Sobrien 2092223328Sgavin/* 2093223328Sgavin * Ensure that ai->ai_addr is NOT an IPv4 mapped address. 2094223328Sgavin * IPv4 mapped address complicates too many things in FTP 2095223328Sgavin * protocol handling, as FTP protocol is defined differently 2096223328Sgavin * between IPv4 and IPv6. 2097223328Sgavin * 2098223328Sgavin * This may not be the best way to handle this situation, 2099223328Sgavin * since the semantics of IPv4 mapped address is defined in 2100223328Sgavin * the kernel. There are configurations where we should use 2101223328Sgavin * IPv4 mapped address as native IPv6 address, not as 2102223328Sgavin * "an IPv6 address that embeds IPv4 address" (namely, SIIT). 2103223328Sgavin * 2104223328Sgavin * More complete solution would be to have an additional 2105223328Sgavin * getsockopt to grab "real" peername/sockname. "real" 2106223328Sgavin * peername/sockname will be AF_INET if IPv4 mapped address 2107223328Sgavin * is used to embed IPv4 address, and will be AF_INET6 if 2108223328Sgavin * we use it as native. What a mess! 2109223328Sgavin */ 211079971Sobrienvoid 211179971Sobrienai_unmapped(struct addrinfo *ai) 211279971Sobrien{ 211379971Sobrien#ifdef INET6 211479971Sobrien struct sockaddr_in6 *sin6; 211579971Sobrien struct sockaddr_in sin; 2116146309Smikeh socklen_t len; 211779971Sobrien 211879971Sobrien if (ai->ai_family != AF_INET6) 211979971Sobrien return; 212079971Sobrien if (ai->ai_addrlen != sizeof(struct sockaddr_in6) || 212179971Sobrien sizeof(sin) > ai->ai_addrlen) 212279971Sobrien return; 212379971Sobrien sin6 = (struct sockaddr_in6 *)ai->ai_addr; 212479971Sobrien if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 212579971Sobrien return; 212679971Sobrien 212779971Sobrien memset(&sin, 0, sizeof(sin)); 212879971Sobrien sin.sin_family = AF_INET; 212979971Sobrien len = sizeof(struct sockaddr_in); 213079971Sobrien memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[12], 213179971Sobrien sizeof(sin.sin_addr)); 213279971Sobrien sin.sin_port = sin6->sin6_port; 213379971Sobrien 213479971Sobrien ai->ai_family = AF_INET; 2135223328Sgavin#if defined(HAVE_STRUCT_SOCKADDR_IN_SIN_LEN) 213679971Sobrien sin.sin_len = len; 213779971Sobrien#endif 213879971Sobrien memcpy(ai->ai_addr, &sin, len); 213979971Sobrien ai->ai_addrlen = len; 214079971Sobrien#endif 214179971Sobrien} 2142223328Sgavin 2143223328Sgavin#ifdef NO_USAGE 2144223328Sgavinvoid 2145223328Sgavinxusage(void) 2146223328Sgavin{ 2147223328Sgavin fputs("Usage error\n", ttyout); 2148223328Sgavin} 2149223328Sgavin#endif 2150