xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.bin/ftp/ftp.c (revision 92163ada)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
237c478bd9Sstevel@tonic-gate  *	Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  *	Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
28*92163adaSToomas Soome /*	All Rights Reserved	*/
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate /*
317c478bd9Sstevel@tonic-gate  *	University Copyright- Copyright (c) 1982, 1986, 1988
327c478bd9Sstevel@tonic-gate  *	The Regents of the University of California
337c478bd9Sstevel@tonic-gate  *	All Rights Reserved
347c478bd9Sstevel@tonic-gate  *
357c478bd9Sstevel@tonic-gate  *	University Acknowledgment- Portions of this document are derived from
367c478bd9Sstevel@tonic-gate  *	software developed by the University of California, Berkeley, and its
377c478bd9Sstevel@tonic-gate  *	contributors.
387c478bd9Sstevel@tonic-gate  */
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate #include "ftp_var.h"
417c478bd9Sstevel@tonic-gate #include <arpa/nameser.h>
427c478bd9Sstevel@tonic-gate #include <sys/types.h>
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate /*
457c478bd9Sstevel@tonic-gate  * WRITE() returns:
46*92163adaSToomas Soome  *	>0	no error
477c478bd9Sstevel@tonic-gate  *	-1	error, errorno is set
487c478bd9Sstevel@tonic-gate  *	-2	security error (secure_write() only)
497c478bd9Sstevel@tonic-gate  */
507c478bd9Sstevel@tonic-gate #define	PUTC(x, y)	secure_putc(x, y)
517c478bd9Sstevel@tonic-gate #define	READ(x, y, z)	secure_read(x, y, z)
527c478bd9Sstevel@tonic-gate #define	WRITE(x, y, z)	secure_write(x, y, z)
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate static struct	sockaddr_in6 data_addr;
557c478bd9Sstevel@tonic-gate int	data = -1;
567c478bd9Sstevel@tonic-gate static int	abrtflag = 0;
577c478bd9Sstevel@tonic-gate static int	ptflag = 0;
587c478bd9Sstevel@tonic-gate static jmp_buf	sendabort;
597c478bd9Sstevel@tonic-gate static jmp_buf	recvabort;
60*92163adaSToomas Soome static jmp_buf	ptabort;
617c478bd9Sstevel@tonic-gate static int ptabflg;
627c478bd9Sstevel@tonic-gate static boolean_t pasv_refused;
637c478bd9Sstevel@tonic-gate boolean_t	eport_supported = B_TRUE;
647c478bd9Sstevel@tonic-gate /*
657c478bd9Sstevel@tonic-gate  * For IPv6 addresses, EPSV will be the default (rather than EPRT/LPRT).
667c478bd9Sstevel@tonic-gate  * The EPSV/ERPT ftp protocols are specified in RFC 2428.
677c478bd9Sstevel@tonic-gate  *
687c478bd9Sstevel@tonic-gate  * Perform EPSV if passivemode is set and ipv6rem is TRUE.
697c478bd9Sstevel@tonic-gate  */
707c478bd9Sstevel@tonic-gate static boolean_t ipv6rem;
717c478bd9Sstevel@tonic-gate int	use_eprt = 0;	/* Testing option that specifies EPRT by default */
727c478bd9Sstevel@tonic-gate FILE	*ctrl_in, *ctrl_out;
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate static void abortsend(int sig);
757c478bd9Sstevel@tonic-gate static void abortpt(int sig);
767c478bd9Sstevel@tonic-gate static void proxtrans(char *cmd, char *local, char *remote);
777c478bd9Sstevel@tonic-gate static void cmdabort(int sig);
787c478bd9Sstevel@tonic-gate static int empty(struct fd_set *mask, int sec, int nfds);
797c478bd9Sstevel@tonic-gate static void abortrecv(int sig);
807c478bd9Sstevel@tonic-gate static int initconn(void);
817c478bd9Sstevel@tonic-gate static FILE *dataconn(char *mode);
827c478bd9Sstevel@tonic-gate static void ptransfer(char *direction, off_t bytes, hrtime_t t0,
837c478bd9Sstevel@tonic-gate     hrtime_t t1, char *local, char *remote);
847c478bd9Sstevel@tonic-gate static void psabort(int sig);
857c478bd9Sstevel@tonic-gate static char *gunique(char *local);
867c478bd9Sstevel@tonic-gate static const char *inet_ntop_native(int af, const void *src, char *dst,
877c478bd9Sstevel@tonic-gate     size_t size);
887c478bd9Sstevel@tonic-gate static ssize_t timedread(int fd, void *buf, size_t maxlen, int timeout);
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate static int secure_command(char *);
917c478bd9Sstevel@tonic-gate static int decode_reply(uchar_t *, int, uchar_t *, int, boolean_t *);
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate static ssize_t	bufcnt;		/* number of bytes in buf[]	*/
947c478bd9Sstevel@tonic-gate static char	*bufp;		/* next character in buf	*/
957c478bd9Sstevel@tonic-gate static int	buferr;		/* last errno			*/
967c478bd9Sstevel@tonic-gate static size_t	bufsize;
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate static void fdio_setbuf(char *buffer, size_t bufsize);
997c478bd9Sstevel@tonic-gate static int fdio_fillbuf(int fd);
1007c478bd9Sstevel@tonic-gate static int fdio_error(int fd);
1017c478bd9Sstevel@tonic-gate #define	fdio_getc(fd)	(--bufcnt < 0 ? fdio_fillbuf((fd)) : \
1027c478bd9Sstevel@tonic-gate 			    ((unsigned char)*bufp++))
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate #define	MAX(a, b) ((a) > (b) ? (a) : (b))
1057c478bd9Sstevel@tonic-gate #define	NONZERO(x)	((x) == 0 ? 1 : (x))
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate static void
fdio_setbuf(char * buffer,size_t maxsize)1087c478bd9Sstevel@tonic-gate fdio_setbuf(char *buffer, size_t maxsize)
1097c478bd9Sstevel@tonic-gate {
1107c478bd9Sstevel@tonic-gate 	buf = buffer;
1117c478bd9Sstevel@tonic-gate 	bufp = buf;
1127c478bd9Sstevel@tonic-gate 	bufcnt = 0;
1137c478bd9Sstevel@tonic-gate 	buferr = 0;
1147c478bd9Sstevel@tonic-gate 	bufsize = maxsize;
1157c478bd9Sstevel@tonic-gate }
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate static int
fdio_fillbuf(int fd)1187c478bd9Sstevel@tonic-gate fdio_fillbuf(int fd)
1197c478bd9Sstevel@tonic-gate {
1207c478bd9Sstevel@tonic-gate 	bufcnt = timedread(fd, buf, bufsize, timeout);
1217c478bd9Sstevel@tonic-gate 	if (bufcnt < 0)
1227c478bd9Sstevel@tonic-gate 		buferr = errno;
1237c478bd9Sstevel@tonic-gate 	if (bufcnt <= 0)
1247c478bd9Sstevel@tonic-gate 		return (EOF);
1257c478bd9Sstevel@tonic-gate 	bufp = buf;
1267c478bd9Sstevel@tonic-gate 	bufcnt--;
1277c478bd9Sstevel@tonic-gate 	return ((unsigned char)*bufp++);
1287c478bd9Sstevel@tonic-gate }
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate /*
1317c478bd9Sstevel@tonic-gate  * fdio_error - used on a file descriptor instead of ferror()
1327c478bd9Sstevel@tonic-gate  */
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1357c478bd9Sstevel@tonic-gate static int
fdio_error(int fd)1367c478bd9Sstevel@tonic-gate fdio_error(int fd)
1377c478bd9Sstevel@tonic-gate {
1387c478bd9Sstevel@tonic-gate 	return (buferr);
1397c478bd9Sstevel@tonic-gate }
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate /*
1427c478bd9Sstevel@tonic-gate  * timedread - read buffer (like "read"), but with timeout (in seconds)
1437c478bd9Sstevel@tonic-gate  */
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate static ssize_t
timedread(int fd,void * buf,size_t size,int timeout)1467c478bd9Sstevel@tonic-gate timedread(int fd, void *buf, size_t size, int timeout)
1477c478bd9Sstevel@tonic-gate {
1487c478bd9Sstevel@tonic-gate 	struct fd_set mask;
1497c478bd9Sstevel@tonic-gate 	struct timeval tv;
1507c478bd9Sstevel@tonic-gate 	int err;
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate 	if (!timeout)
1537c478bd9Sstevel@tonic-gate 		return (READ(fd, buf, size));
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate 	tv.tv_sec = (time_t)timeout;
1567c478bd9Sstevel@tonic-gate 	tv.tv_usec = 0;
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate 	FD_ZERO(&mask);
1597c478bd9Sstevel@tonic-gate 	FD_SET(fd, &mask);
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate 	err = select(fd + 1, &mask, NULL, NULL, &tv);
1627c478bd9Sstevel@tonic-gate 	if (err == 0)
1637c478bd9Sstevel@tonic-gate 		errno = ETIMEDOUT;
1647c478bd9Sstevel@tonic-gate 	if (err <= 0)
1657c478bd9Sstevel@tonic-gate 		return (-1);
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate 	return (READ(fd, buf, size));
1687c478bd9Sstevel@tonic-gate }
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate char *
hookup(char * host,char * service)1727c478bd9Sstevel@tonic-gate hookup(char *host, char *service)
1737c478bd9Sstevel@tonic-gate {
1747c478bd9Sstevel@tonic-gate 	struct addrinfo hints, *ai = NULL, *ai_head;
1757c478bd9Sstevel@tonic-gate 	int s;
1767c478bd9Sstevel@tonic-gate 	socklen_t len;
1777c478bd9Sstevel@tonic-gate 	static char hostnamebuf[80];
1787c478bd9Sstevel@tonic-gate 	struct in6_addr ipv6addr;
1797c478bd9Sstevel@tonic-gate 	char abuf[INET6_ADDRSTRLEN];
1807c478bd9Sstevel@tonic-gate 	int error_num;
1817c478bd9Sstevel@tonic-gate 	int on = 1;
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate 	/*
1847c478bd9Sstevel@tonic-gate 	 * There appears to be a bug in getaddrinfo() where, if the
1857c478bd9Sstevel@tonic-gate 	 * ai_family is set to AF_INET6, and the host is a v4-only
1867c478bd9Sstevel@tonic-gate 	 * host, getaddrinfo() returns an error instead of returning
1877c478bd9Sstevel@tonic-gate 	 * an v4-mapped ipv6 address. Therefore the ai_family is
1887c478bd9Sstevel@tonic-gate 	 * set to AF_UNSPEC and any returned v4 addresses are
1897c478bd9Sstevel@tonic-gate 	 * explicitly mapped within ftp.
1907c478bd9Sstevel@tonic-gate 	 */
1917c478bd9Sstevel@tonic-gate 	bzero((char *)&remctladdr, sizeof (remctladdr));
1927c478bd9Sstevel@tonic-gate 	bzero((char *)&hints, sizeof (hints));
1937c478bd9Sstevel@tonic-gate 	hints.ai_flags = AI_CANONNAME;
1947c478bd9Sstevel@tonic-gate 	hints.ai_family = AF_UNSPEC;
1957c478bd9Sstevel@tonic-gate 	hints.ai_socktype = SOCK_STREAM;
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate 	error_num = getaddrinfo(host, service, &hints, &ai);
1987c478bd9Sstevel@tonic-gate 	if (error_num != 0) {
1997c478bd9Sstevel@tonic-gate 		if (error_num == EAI_AGAIN) {
2007c478bd9Sstevel@tonic-gate 			(void) printf(
2017c478bd9Sstevel@tonic-gate 			    "%s: unknown host or invalid literal address "
2027c478bd9Sstevel@tonic-gate 			    "(try again later)\n", host);
2037c478bd9Sstevel@tonic-gate 		} else {
2047c478bd9Sstevel@tonic-gate 			(void) printf(
2057c478bd9Sstevel@tonic-gate 			    "%s: unknown host or invalid literal address\n",
2067c478bd9Sstevel@tonic-gate 			    host);
2077c478bd9Sstevel@tonic-gate 		}
2087c478bd9Sstevel@tonic-gate 		code = -1;
2097c478bd9Sstevel@tonic-gate 		return ((char *)0);
2107c478bd9Sstevel@tonic-gate 	}
2117c478bd9Sstevel@tonic-gate 	ai_head = ai;
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate 	/*
2157c478bd9Sstevel@tonic-gate 	 * If ai_canonname is a IPv4-mapped IPv6 literal, we'll convert it to
2167c478bd9Sstevel@tonic-gate 	 * IPv4 literal address.
2177c478bd9Sstevel@tonic-gate 	 */
2187c478bd9Sstevel@tonic-gate 	if (ai->ai_canonname != NULL &&
2197c478bd9Sstevel@tonic-gate 	    (inet_pton(AF_INET6, ai->ai_canonname, &ipv6addr) > 0) &&
2207c478bd9Sstevel@tonic-gate 	    IN6_IS_ADDR_V4MAPPED(&ipv6addr)) {
2217c478bd9Sstevel@tonic-gate 		struct in_addr src4;
2227c478bd9Sstevel@tonic-gate 		hostnamebuf[0] = '\0';
2237c478bd9Sstevel@tonic-gate 		IN6_V4MAPPED_TO_INADDR(&ipv6addr, &src4);
2247c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET, &src4, hostnamebuf,
2257c478bd9Sstevel@tonic-gate 		    sizeof (hostnamebuf));
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate 		/*
2287c478bd9Sstevel@tonic-gate 		 * It can even be the case that the "host" supplied by the user
2297c478bd9Sstevel@tonic-gate 		 * can be a IPv4-mapped IPv6 literal. So, let's fix that too.
2307c478bd9Sstevel@tonic-gate 		 */
2317c478bd9Sstevel@tonic-gate 		if ((inet_pton(AF_INET6, host, &ipv6addr) > 0) &&
2327c478bd9Sstevel@tonic-gate 		    IN6_IS_ADDR_V4MAPPED(&ipv6addr) &&
2337c478bd9Sstevel@tonic-gate 		    strlen(hostnamebuf) <= strlen(host)) {
2347c478bd9Sstevel@tonic-gate 			(void) strlcpy(host, hostnamebuf, strlen(host) + 1);
2357c478bd9Sstevel@tonic-gate 		}
2367c478bd9Sstevel@tonic-gate 	} else {
2377c478bd9Sstevel@tonic-gate 		reset_timer();
2387c478bd9Sstevel@tonic-gate 		(void) strlcpy(hostnamebuf,
2397c478bd9Sstevel@tonic-gate 		    (ai->ai_canonname ? ai->ai_canonname : host),
2407c478bd9Sstevel@tonic-gate 		    sizeof (hostnamebuf));
2417c478bd9Sstevel@tonic-gate 	}
2427c478bd9Sstevel@tonic-gate 
2437c478bd9Sstevel@tonic-gate 	hostname = hostnamebuf;
2447c478bd9Sstevel@tonic-gate 	for (;;) {
2457c478bd9Sstevel@tonic-gate 		int oerrno;
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate 		bcopy(ai->ai_addr, &remctladdr, ai->ai_addrlen);
2487c478bd9Sstevel@tonic-gate 		if (ai->ai_addr->sa_family == AF_INET) {
2497c478bd9Sstevel@tonic-gate 			IN6_INADDR_TO_V4MAPPED(
2507c478bd9Sstevel@tonic-gate 			    &(((struct sockaddr_in *)ai->ai_addr)->sin_addr),
2517c478bd9Sstevel@tonic-gate 			    &remctladdr.sin6_addr);
2527c478bd9Sstevel@tonic-gate 			remctladdr.sin6_family = AF_INET6;
2537c478bd9Sstevel@tonic-gate 		}
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate 		s = socket(AF_INET6, SOCK_STREAM, 0);
2567c478bd9Sstevel@tonic-gate 		if (s < 0) {
2577c478bd9Sstevel@tonic-gate 			perror("ftp: socket");
2587c478bd9Sstevel@tonic-gate 			code = -1;
2597c478bd9Sstevel@tonic-gate 			freeaddrinfo(ai_head);
2607c478bd9Sstevel@tonic-gate 			return (0);
2617c478bd9Sstevel@tonic-gate 		}
2627c478bd9Sstevel@tonic-gate 		if (timeout && setsockopt(s, IPPROTO_TCP, TCP_ABORT_THRESHOLD,
2637c478bd9Sstevel@tonic-gate 		    (char *)&timeoutms, sizeof (timeoutms)) < 0 && debug)
2647c478bd9Sstevel@tonic-gate 			perror("ftp: setsockopt (TCP_ABORT_THRESHOLD)");
2657c478bd9Sstevel@tonic-gate 		reset_timer();
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate 		error_num = connect(s, (struct sockaddr *)&remctladdr,
2687c478bd9Sstevel@tonic-gate 		    sizeof (remctladdr));
2697c478bd9Sstevel@tonic-gate 		oerrno = errno;
2707c478bd9Sstevel@tonic-gate 		if (error_num >= 0)
2717c478bd9Sstevel@tonic-gate 			break;
2727c478bd9Sstevel@tonic-gate 
2737c478bd9Sstevel@tonic-gate 		/*
2747c478bd9Sstevel@tonic-gate 		 * Maintain message behavior: only include the address in
2757c478bd9Sstevel@tonic-gate 		 * our error message if we have another one to try; if this
2767c478bd9Sstevel@tonic-gate 		 * is the last address on our list, just print the error.
2777c478bd9Sstevel@tonic-gate 		 */
2787c478bd9Sstevel@tonic-gate 		if (ai->ai_next != NULL) {
2797c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "ftp: connect to address %s: ",
2807c478bd9Sstevel@tonic-gate 			    inet_ntop_native(ai->ai_addr->sa_family,
2817c478bd9Sstevel@tonic-gate 			    (void *)ai->ai_addr, abuf, sizeof (abuf)));
2827c478bd9Sstevel@tonic-gate 			errno = oerrno;
2837c478bd9Sstevel@tonic-gate 			perror((char *)0);
2847c478bd9Sstevel@tonic-gate 		} else {
2857c478bd9Sstevel@tonic-gate 			perror("ftp: connect");
2867c478bd9Sstevel@tonic-gate 			code = -1;
2877c478bd9Sstevel@tonic-gate 			freeaddrinfo(ai_head);
2887c478bd9Sstevel@tonic-gate 			goto bad;
2897c478bd9Sstevel@tonic-gate 		}
2907c478bd9Sstevel@tonic-gate 		ai = ai->ai_next;
2917c478bd9Sstevel@tonic-gate 		(void) fprintf(stdout, "Trying %s...\n",
2927c478bd9Sstevel@tonic-gate 		    inet_ntop_native(ai->ai_addr->sa_family,
2937c478bd9Sstevel@tonic-gate 		    (void *)ai->ai_addr, abuf, sizeof (abuf)));
2947c478bd9Sstevel@tonic-gate 		(void) close(s);
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 	}
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 	/* Set ipv6rem to TRUE if control connection is a native IPv6 address */
2997c478bd9Sstevel@tonic-gate 	if (IN6_IS_ADDR_V4MAPPED(&remctladdr.sin6_addr))
3007c478bd9Sstevel@tonic-gate 		ipv6rem = B_FALSE;
3017c478bd9Sstevel@tonic-gate 	else
3027c478bd9Sstevel@tonic-gate 		ipv6rem = B_TRUE;
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate 	freeaddrinfo(ai_head);
3067c478bd9Sstevel@tonic-gate 	ai = NULL;
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate 	/*
3097c478bd9Sstevel@tonic-gate 	 * Set passive mode flag on by default only if a native IPv6 address
3107c478bd9Sstevel@tonic-gate 	 * is being used -and- the use_eprt is not set.
3117c478bd9Sstevel@tonic-gate 	 */
3127c478bd9Sstevel@tonic-gate 	if (ipv6rem == B_TRUE && use_eprt == 0)
3137c478bd9Sstevel@tonic-gate 		passivemode = 1;
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate 	len = sizeof (myctladdr);
3167c478bd9Sstevel@tonic-gate 	if (getsockname(s, (struct sockaddr *)&myctladdr, &len) < 0) {
3177c478bd9Sstevel@tonic-gate 		perror("ftp: getsockname");
3187c478bd9Sstevel@tonic-gate 		code = -1;
3197c478bd9Sstevel@tonic-gate 		goto bad;
3207c478bd9Sstevel@tonic-gate 	}
3217c478bd9Sstevel@tonic-gate 	ctrl_in = fdopen(s, "r");
3227c478bd9Sstevel@tonic-gate 	ctrl_out = fdopen(s, "w");
3237c478bd9Sstevel@tonic-gate 	if (ctrl_in == NULL || ctrl_out == NULL) {
3247c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "ftp: fdopen failed.\n");
3257c478bd9Sstevel@tonic-gate 		if (ctrl_in)
3267c478bd9Sstevel@tonic-gate 			(void) fclose(ctrl_in);
3277c478bd9Sstevel@tonic-gate 		if (ctrl_out)
3287c478bd9Sstevel@tonic-gate 			(void) fclose(ctrl_out);
3297c478bd9Sstevel@tonic-gate 		code = -1;
3307c478bd9Sstevel@tonic-gate 		goto bad;
3317c478bd9Sstevel@tonic-gate 	}
3327c478bd9Sstevel@tonic-gate 	if (verbose)
3337c478bd9Sstevel@tonic-gate 		(void) printf("Connected to %s.\n", hostname);
3347c478bd9Sstevel@tonic-gate 	if (getreply(0) > 2) {	/* read startup message from server */
3357c478bd9Sstevel@tonic-gate 		if (ctrl_in)
3367c478bd9Sstevel@tonic-gate 			(void) fclose(ctrl_in);
3377c478bd9Sstevel@tonic-gate 		if (ctrl_out)
3387c478bd9Sstevel@tonic-gate 			(void) fclose(ctrl_out);
3397c478bd9Sstevel@tonic-gate 		ctrl_in = ctrl_out = NULL;
3407c478bd9Sstevel@tonic-gate 		ctrl_in = ctrl_out = NULL;
3417c478bd9Sstevel@tonic-gate 		code = -1;
3427c478bd9Sstevel@tonic-gate 		goto bad;
3437c478bd9Sstevel@tonic-gate 	}
3447c478bd9Sstevel@tonic-gate 	if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&on,
3457c478bd9Sstevel@tonic-gate 	    sizeof (on)) < 0 && debug)
3467c478bd9Sstevel@tonic-gate 		perror("ftp: setsockopt (SO_OOBINLINE)");
3477c478bd9Sstevel@tonic-gate 
3487c478bd9Sstevel@tonic-gate 	return (hostname);
3497c478bd9Sstevel@tonic-gate bad:
3507c478bd9Sstevel@tonic-gate 	(void) close(s);
3517c478bd9Sstevel@tonic-gate 	return ((char *)0);
3527c478bd9Sstevel@tonic-gate }
3537c478bd9Sstevel@tonic-gate 
3547c478bd9Sstevel@tonic-gate int
login(char * host)3557c478bd9Sstevel@tonic-gate login(char *host)
3567c478bd9Sstevel@tonic-gate {
3577c478bd9Sstevel@tonic-gate 	char tmp[80];
3587c478bd9Sstevel@tonic-gate 	char *user, *pass, *acct;
3597c478bd9Sstevel@tonic-gate 	int n, aflag = 0;
3607c478bd9Sstevel@tonic-gate 
3617c478bd9Sstevel@tonic-gate 	user = pass = acct = 0;
3627c478bd9Sstevel@tonic-gate 	if (ruserpass(host, &user, &pass, &acct) < 0) {
3637c478bd9Sstevel@tonic-gate 		disconnect(0, NULL);
3647c478bd9Sstevel@tonic-gate 		code = -1;
3657c478bd9Sstevel@tonic-gate 		return (0);
3667c478bd9Sstevel@tonic-gate 	}
3677c478bd9Sstevel@tonic-gate 	if (user == NULL) {
3687c478bd9Sstevel@tonic-gate 		char *myname = getlogin();
3697c478bd9Sstevel@tonic-gate 
3707c478bd9Sstevel@tonic-gate 		if (myname == NULL) {
3717c478bd9Sstevel@tonic-gate 			struct passwd *pp = getpwuid(getuid());
3727c478bd9Sstevel@tonic-gate 
3737c478bd9Sstevel@tonic-gate 			if (pp != NULL)
3747c478bd9Sstevel@tonic-gate 				myname = pp->pw_name;
3757c478bd9Sstevel@tonic-gate 		}
3767c478bd9Sstevel@tonic-gate 		stop_timer();
3777c478bd9Sstevel@tonic-gate 		(void) printf("Name (%s:%s): ", host,
378*92163adaSToomas Soome 		    (myname == NULL) ? "" : myname);
3797c478bd9Sstevel@tonic-gate 		*tmp = '\0';
3807c478bd9Sstevel@tonic-gate 		if (fgets(tmp, sizeof (tmp) - 1, stdin) != NULL)
3817c478bd9Sstevel@tonic-gate 			tmp[strlen(tmp) - 1] = '\0';
3827c478bd9Sstevel@tonic-gate 		if (*tmp != '\0')
3837c478bd9Sstevel@tonic-gate 			user = tmp;
3847c478bd9Sstevel@tonic-gate 		else if (myname != NULL)
3857c478bd9Sstevel@tonic-gate 			user = myname;
3867c478bd9Sstevel@tonic-gate 		else
3877c478bd9Sstevel@tonic-gate 			return (0);
3887c478bd9Sstevel@tonic-gate 	}
3897c478bd9Sstevel@tonic-gate 	n = command("USER %s", user);
3907c478bd9Sstevel@tonic-gate 	if (n == CONTINUE) {
3917c478bd9Sstevel@tonic-gate 		int oldclevel;
3927c478bd9Sstevel@tonic-gate 		if (pass == NULL)
3937c478bd9Sstevel@tonic-gate 			pass = mygetpass("Password:");
3947c478bd9Sstevel@tonic-gate 		oldclevel = clevel;
3957c478bd9Sstevel@tonic-gate 		clevel = PROT_P;
3967c478bd9Sstevel@tonic-gate 		n = command("PASS %s", pass);
3977c478bd9Sstevel@tonic-gate 		/* level may have changed */
3987c478bd9Sstevel@tonic-gate 		if (clevel == PROT_P)
3997c478bd9Sstevel@tonic-gate 			clevel = oldclevel;
4007c478bd9Sstevel@tonic-gate 	}
4017c478bd9Sstevel@tonic-gate 	if (n == CONTINUE) {
4027c478bd9Sstevel@tonic-gate 		aflag++;
4037c478bd9Sstevel@tonic-gate 		if (acct == NULL)
4047c478bd9Sstevel@tonic-gate 			acct = mygetpass("Account:");
4057c478bd9Sstevel@tonic-gate 		n = command("ACCT %s", acct);
4067c478bd9Sstevel@tonic-gate 	}
4077c478bd9Sstevel@tonic-gate 	if (n != COMPLETE) {
4087c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "Login failed.\n");
4097c478bd9Sstevel@tonic-gate 		return (0);
4107c478bd9Sstevel@tonic-gate 	}
4117c478bd9Sstevel@tonic-gate 	if (!aflag && acct != NULL)
4127c478bd9Sstevel@tonic-gate 		(void) command("ACCT %s", acct);
4137c478bd9Sstevel@tonic-gate 	if (proxy)
4147c478bd9Sstevel@tonic-gate 		return (1);
4157c478bd9Sstevel@tonic-gate 	for (n = 0; n < macnum; ++n) {
4167c478bd9Sstevel@tonic-gate 		if (strcmp("init", macros[n].mac_name) == 0) {
4177c478bd9Sstevel@tonic-gate 			(void) strlcpy(line, "$init", sizeof (line));
4187c478bd9Sstevel@tonic-gate 			makeargv();
4197c478bd9Sstevel@tonic-gate 			domacro(margc, margv);
4207c478bd9Sstevel@tonic-gate 			break;
4217c478bd9Sstevel@tonic-gate 		}
4227c478bd9Sstevel@tonic-gate 	}
4237c478bd9Sstevel@tonic-gate 	return (1);
4247c478bd9Sstevel@tonic-gate }
4257c478bd9Sstevel@tonic-gate 
4267c478bd9Sstevel@tonic-gate /*ARGSUSED*/
4277c478bd9Sstevel@tonic-gate static void
cmdabort(int sig)4287c478bd9Sstevel@tonic-gate cmdabort(int sig)
4297c478bd9Sstevel@tonic-gate {
4307c478bd9Sstevel@tonic-gate 	(void) printf("\n");
4317c478bd9Sstevel@tonic-gate 	(void) fflush(stdout);
4327c478bd9Sstevel@tonic-gate 	abrtflag++;
4337c478bd9Sstevel@tonic-gate 	if (ptflag)
4347c478bd9Sstevel@tonic-gate 		longjmp(ptabort, 1);
4357c478bd9Sstevel@tonic-gate }
4367c478bd9Sstevel@tonic-gate 
4377c478bd9Sstevel@tonic-gate int
command(char * fmt,...)4387c478bd9Sstevel@tonic-gate command(char *fmt, ...)
4397c478bd9Sstevel@tonic-gate {
4407c478bd9Sstevel@tonic-gate 	int r;
4417c478bd9Sstevel@tonic-gate 	void (*oldintr)();
4427c478bd9Sstevel@tonic-gate 	va_list ap;
4437c478bd9Sstevel@tonic-gate 	char command_buf[FTPBUFSIZ];
4447c478bd9Sstevel@tonic-gate 
4457c478bd9Sstevel@tonic-gate 	va_start(ap, fmt);
4467c478bd9Sstevel@tonic-gate 	abrtflag = 0;
4477c478bd9Sstevel@tonic-gate 	if (debug) {
4487c478bd9Sstevel@tonic-gate 		(void) printf("---> ");
4497c478bd9Sstevel@tonic-gate 		if (strncmp("PASS ", fmt, 5) == 0)
4507c478bd9Sstevel@tonic-gate 			(void) printf("PASS XXXX");
4517c478bd9Sstevel@tonic-gate 		else if (strncmp("ACCT ", fmt, 5) == 0)
4527c478bd9Sstevel@tonic-gate 			(void) printf("ACCT XXXX");
4537c478bd9Sstevel@tonic-gate 		else
4547c478bd9Sstevel@tonic-gate 			(void) vfprintf(stdout, fmt, ap);
4557c478bd9Sstevel@tonic-gate 		(void) printf("\n");
4567c478bd9Sstevel@tonic-gate 		(void) fflush(stdout);
4577c478bd9Sstevel@tonic-gate 	}
4587c478bd9Sstevel@tonic-gate 	if (ctrl_out == NULL) {
4597c478bd9Sstevel@tonic-gate 		perror("No control connection for command");
4607c478bd9Sstevel@tonic-gate 		code = -1;
4617c478bd9Sstevel@tonic-gate 		return (0);
4627c478bd9Sstevel@tonic-gate 	}
4637c478bd9Sstevel@tonic-gate 	oldintr = signal(SIGINT, cmdabort);
4647c478bd9Sstevel@tonic-gate 	(void) vsnprintf(command_buf, FTPBUFSIZ, fmt, ap);
4657c478bd9Sstevel@tonic-gate 	va_end(ap);
4667c478bd9Sstevel@tonic-gate 
4677c478bd9Sstevel@tonic-gate again:	if (secure_command(command_buf) == 0)
4687c478bd9Sstevel@tonic-gate 		return (0);
4697c478bd9Sstevel@tonic-gate 
4707c478bd9Sstevel@tonic-gate 	cpend = 1;
4717c478bd9Sstevel@tonic-gate 	r = getreply(strcmp(fmt, "QUIT") == 0);
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate 	if (r == 533 && clevel == PROT_P) {
4747c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "ENC command not supported at server; "
475*92163adaSToomas Soome 		    "retrying under MIC...\n");
4767c478bd9Sstevel@tonic-gate 		clevel = PROT_S;
4777c478bd9Sstevel@tonic-gate 		goto again;
4787c478bd9Sstevel@tonic-gate 	}
4797c478bd9Sstevel@tonic-gate 
4807c478bd9Sstevel@tonic-gate 	if (abrtflag && oldintr != SIG_IGN)
4817c478bd9Sstevel@tonic-gate 		(*oldintr)();
4827c478bd9Sstevel@tonic-gate 	(void) signal(SIGINT, oldintr);
4837c478bd9Sstevel@tonic-gate 	return (r);
4847c478bd9Sstevel@tonic-gate }
4857c478bd9Sstevel@tonic-gate 
4867c478bd9Sstevel@tonic-gate /* Need to save reply reponse from server for use in EPSV mode */
4877c478bd9Sstevel@tonic-gate char reply_string[BUFSIZ];
4887c478bd9Sstevel@tonic-gate 
4897c478bd9Sstevel@tonic-gate int
getreply(int expecteof)4907c478bd9Sstevel@tonic-gate getreply(int expecteof)
4917c478bd9Sstevel@tonic-gate {
4927c478bd9Sstevel@tonic-gate 	/*
4937c478bd9Sstevel@tonic-gate 	 * 'code' is the 3 digit reply code, form xyz
4947c478bd9Sstevel@tonic-gate 	 * 'dig'  counts the number of digits we are along in the code
4957c478bd9Sstevel@tonic-gate 	 * 'n'	is the first digit of 'code'
4967c478bd9Sstevel@tonic-gate 	 *	4yz: resource unavailable
4977c478bd9Sstevel@tonic-gate 	 *	5yz: an error occurred, failure
4987c478bd9Sstevel@tonic-gate 	 *	6yz: protected reply (is_base64 == TRUE)
4997c478bd9Sstevel@tonic-gate 	 *		631 - base 64 encoded safe message
500*92163adaSToomas Soome 	 *		632 - base 64 encoded private message
501*92163adaSToomas Soome 	 *		633 - base 64 encoded confidential message
5027c478bd9Sstevel@tonic-gate 	 * 'c'	is a wide char type, for international char sets
5037c478bd9Sstevel@tonic-gate 	 */
5047c478bd9Sstevel@tonic-gate 	wint_t c;
5057c478bd9Sstevel@tonic-gate 	int i, n;
5067c478bd9Sstevel@tonic-gate 	int dig;
5077c478bd9Sstevel@tonic-gate 	int originalcode = 0, continuation = 0;
5087c478bd9Sstevel@tonic-gate 	void (*oldintr)();
5097c478bd9Sstevel@tonic-gate 	int pflag = 0;
5107c478bd9Sstevel@tonic-gate 	char *pt = pasv;
5117c478bd9Sstevel@tonic-gate 	/*
5127c478bd9Sstevel@tonic-gate 	 * this is the input and output buffers needed for
5137c478bd9Sstevel@tonic-gate 	 * radix_encode()
5147c478bd9Sstevel@tonic-gate 	 */
5157c478bd9Sstevel@tonic-gate 	unsigned char ibuf[FTPBUFSIZ];
5167c478bd9Sstevel@tonic-gate 	unsigned char obuf[FTPBUFSIZ];
5177c478bd9Sstevel@tonic-gate 	boolean_t is_base64;
5187c478bd9Sstevel@tonic-gate 	int len;
5197c478bd9Sstevel@tonic-gate 	char *cp;
5207c478bd9Sstevel@tonic-gate 
5217c478bd9Sstevel@tonic-gate 	if (!ctrl_in)
5227c478bd9Sstevel@tonic-gate 		return (0);
5237c478bd9Sstevel@tonic-gate 	oldintr = signal(SIGINT, cmdabort);
5247c478bd9Sstevel@tonic-gate 
5257c478bd9Sstevel@tonic-gate 	ibuf[0] = '\0';
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate 	if (reply_parse)
5287c478bd9Sstevel@tonic-gate 		reply_ptr = reply_buf;
5297c478bd9Sstevel@tonic-gate 
5307c478bd9Sstevel@tonic-gate 	for (;;) {
5317c478bd9Sstevel@tonic-gate 		obuf[0] = '\0';
5327c478bd9Sstevel@tonic-gate 		dig = n = code = 0;
5337c478bd9Sstevel@tonic-gate 		i = is_base64 = 0;
5347c478bd9Sstevel@tonic-gate 		cp = reply_string;
5357c478bd9Sstevel@tonic-gate 		reset_timer();	/* once per line */
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 		while ((c = ibuf[0] ?
5387c478bd9Sstevel@tonic-gate 		    (wint_t)ibuf[i++] : fgetwc(ctrl_in)) != '\n') {
5397c478bd9Sstevel@tonic-gate 
540*92163adaSToomas Soome 			if (i >= FTPBUFSIZ)
5417c478bd9Sstevel@tonic-gate 				break;
542*92163adaSToomas Soome 
543*92163adaSToomas Soome 			if (c == IAC) {	/* handle telnet commands */
544*92163adaSToomas Soome 				switch (c = fgetwc(ctrl_in)) {
545*92163adaSToomas Soome 				case WILL:
546*92163adaSToomas Soome 				case WONT:
547*92163adaSToomas Soome 					c = fgetwc(ctrl_in);
548*92163adaSToomas Soome 					(void) fprintf(ctrl_out, "%c%c%wc", IAC,
549*92163adaSToomas Soome 					    WONT, c);
550*92163adaSToomas Soome 					(void) fflush(ctrl_out);
551*92163adaSToomas Soome 					break;
552*92163adaSToomas Soome 				case DO:
553*92163adaSToomas Soome 				case DONT:
554*92163adaSToomas Soome 					c = fgetwc(ctrl_in);
555*92163adaSToomas Soome 					(void) fprintf(ctrl_out, "%c%c%wc", IAC,
556*92163adaSToomas Soome 					    DONT, c);
557*92163adaSToomas Soome 					(void) fflush(ctrl_out);
558*92163adaSToomas Soome 					break;
559*92163adaSToomas Soome 				default:
560*92163adaSToomas Soome 					break;
561*92163adaSToomas Soome 				}
562*92163adaSToomas Soome 				continue;
5637c478bd9Sstevel@tonic-gate 			}
564*92163adaSToomas Soome 			dig++;
565*92163adaSToomas Soome 			if (c == EOF) {
566*92163adaSToomas Soome 				if (expecteof) {
567*92163adaSToomas Soome 					(void) signal(SIGINT, oldintr);
568*92163adaSToomas Soome 					code = 221;
569*92163adaSToomas Soome 					return (0);
570*92163adaSToomas Soome 				}
571*92163adaSToomas Soome 				lostpeer(0);
572*92163adaSToomas Soome 				if (verbose) {
573*92163adaSToomas Soome 					(void) printf(
574*92163adaSToomas Soome 					    "421 Service not available, remote"
575*92163adaSToomas Soome 					    " server has closed connection\n");
576*92163adaSToomas Soome 				} else {
577*92163adaSToomas Soome 					(void) printf("Lost connection\n");
578*92163adaSToomas Soome 				}
579*92163adaSToomas Soome 				(void) fflush(stdout);
580*92163adaSToomas Soome 				code = 421;
581*92163adaSToomas Soome 				return (4);
5827c478bd9Sstevel@tonic-gate 			}
583*92163adaSToomas Soome 			if (n == 0)
584*92163adaSToomas Soome 				n = c;
585*92163adaSToomas Soome 
586*92163adaSToomas Soome 			if (n == '6')
587*92163adaSToomas Soome 				is_base64 = 1;
588*92163adaSToomas Soome 
5897c478bd9Sstevel@tonic-gate 			if ((auth_type != AUTHTYPE_NONE) && !ibuf[0] &&
590*92163adaSToomas Soome 			    (is_base64 || continuation))  {
591*92163adaSToomas Soome 				/* start storing chars in obuf */
592*92163adaSToomas Soome 				if (c != '\r' && dig > 4)
593*92163adaSToomas Soome 					obuf[i++] = (char)c;
594*92163adaSToomas Soome 			} else {
595*92163adaSToomas Soome 				if ((auth_type != AUTHTYPE_NONE) && !ibuf[0] &&
596*92163adaSToomas Soome 				    dig == 1 && verbose)
597*92163adaSToomas Soome 					(void) printf("Unauthenticated reply "
598*92163adaSToomas Soome 					    "received from server:\n");
599*92163adaSToomas Soome 				if (reply_parse)
600*92163adaSToomas Soome 					*reply_ptr++ = (char)c;
601*92163adaSToomas Soome 				if (c != '\r' && (verbose > 0 ||
602*92163adaSToomas Soome 				    (verbose > -1 && n == '5' && dig > 4))) {
603*92163adaSToomas Soome 					if (proxflag &&
604*92163adaSToomas Soome 					    (dig == 1 || dig == 5 &&
605*92163adaSToomas Soome 					    verbose == 0))
606*92163adaSToomas Soome 						(void) printf("%s:", hostname);
607*92163adaSToomas Soome 					(void) putwchar(c);
608*92163adaSToomas Soome 				}
609*92163adaSToomas Soome 			} /* endif auth_type && !ibuf[0] ... */
6107c478bd9Sstevel@tonic-gate 
611*92163adaSToomas Soome 			if ((auth_type != AUTHTYPE_NONE) && !ibuf[0] &&
612*92163adaSToomas Soome 			    !is_base64)
613*92163adaSToomas Soome 				continue;
614*92163adaSToomas Soome 
615*92163adaSToomas Soome 			/* we are still extracting the 3 digit code */
616*92163adaSToomas Soome 			if (dig < 4 && isascii(c) && isdigit(c))
617*92163adaSToomas Soome 				code = code * 10 + (c - '0');
618*92163adaSToomas Soome 
619*92163adaSToomas Soome 			/* starting passive mode */
620*92163adaSToomas Soome 			if (!pflag && code == 227)
621*92163adaSToomas Soome 				pflag = 1;
622*92163adaSToomas Soome 
623*92163adaSToomas Soome 			/* start to store characters, when dig > 4 */
624*92163adaSToomas Soome 			if (dig > 4 && pflag == 1 && isascii(c) && isdigit(c))
625*92163adaSToomas Soome 				pflag = 2;
626*92163adaSToomas Soome 			if (pflag == 2) {
627*92163adaSToomas Soome 				if (c != '\r' && c != ')') {
628*92163adaSToomas Soome 					/*
629*92163adaSToomas Soome 					 * the mb array is to deal with
630*92163adaSToomas Soome 					 * the wchar_t
631*92163adaSToomas Soome 					 */
632*92163adaSToomas Soome 					char mb[MB_LEN_MAX];
633*92163adaSToomas Soome 					int avail;
6347c478bd9Sstevel@tonic-gate 
6357c478bd9Sstevel@tonic-gate 					/*
636*92163adaSToomas Soome 					 * space available in pasv[], accounting
637*92163adaSToomas Soome 					 * for trailing NULL
6387c478bd9Sstevel@tonic-gate 					 */
639*92163adaSToomas Soome 					avail = &pasv[sizeof (pasv)] - pt - 1;
640*92163adaSToomas Soome 
641*92163adaSToomas Soome 					len = wctomb(mb, c);
642*92163adaSToomas Soome 					if (len <= 0 && avail > 0) {
643*92163adaSToomas Soome 						*pt++ = (unsigned char)c;
644*92163adaSToomas Soome 					} else if (len > 0 && avail >= len) {
645*92163adaSToomas Soome 						bcopy(mb, pt, (size_t)len);
646*92163adaSToomas Soome 						pt += len;
647*92163adaSToomas Soome 					} else {
648*92163adaSToomas Soome 						/*
649*92163adaSToomas Soome 						 * no room in pasv[];
650*92163adaSToomas Soome 						 * close connection
651*92163adaSToomas Soome 						 */
652*92163adaSToomas Soome 						(void) printf("\nReply too long"
653*92163adaSToomas Soome 						    " - closing connection\n");
654*92163adaSToomas Soome 						lostpeer(0);
655*92163adaSToomas Soome 						(void) fflush(stdout);
656*92163adaSToomas Soome 						(void) signal(SIGINT, oldintr);
657*92163adaSToomas Soome 						return (4);
658*92163adaSToomas Soome 					}
659*92163adaSToomas Soome 				} else {
660*92163adaSToomas Soome 					*pt = '\0';
661*92163adaSToomas Soome 					pflag = 3;
6627c478bd9Sstevel@tonic-gate 				}
663*92163adaSToomas Soome 			} /* endif pflag == 2 */
664*92163adaSToomas Soome 			if (dig == 4 && c == '-' && !is_base64) {
665*92163adaSToomas Soome 				if (continuation)
666*92163adaSToomas Soome 					code = 0;
667*92163adaSToomas Soome 				continuation++;
6687c478bd9Sstevel@tonic-gate 			}
669*92163adaSToomas Soome 			if (cp < &reply_string[sizeof (reply_string) - 1])
670*92163adaSToomas Soome 				*cp++ = c;
6717c478bd9Sstevel@tonic-gate 
6727c478bd9Sstevel@tonic-gate 		} /* end while */
6737c478bd9Sstevel@tonic-gate 
6747c478bd9Sstevel@tonic-gate 		if ((auth_type != AUTHTYPE_NONE) && !ibuf[0] && !is_base64)
6757c478bd9Sstevel@tonic-gate 			return (getreply(expecteof));
6767c478bd9Sstevel@tonic-gate 
6777c478bd9Sstevel@tonic-gate 		ibuf[0] = obuf[i] = '\0';
6787c478bd9Sstevel@tonic-gate 
6797c478bd9Sstevel@tonic-gate 		if (code && is_base64) {
680*92163adaSToomas Soome 			boolean_t again = 0;
681*92163adaSToomas Soome 			n = decode_reply(ibuf, sizeof (ibuf), obuf, n, &again);
682*92163adaSToomas Soome 			if (again)
683*92163adaSToomas Soome 				continue;
684*92163adaSToomas Soome 		} else {
685*92163adaSToomas Soome 			if (verbose > 0 || verbose > -1 && n == '5') {
686*92163adaSToomas Soome 				(void) putwchar(c);
687*92163adaSToomas Soome 				(void) fflush(stdout);
688*92163adaSToomas Soome 			}
6897c478bd9Sstevel@tonic-gate 		}
6907c478bd9Sstevel@tonic-gate 
6917c478bd9Sstevel@tonic-gate 		if (continuation && code != originalcode) {
6927c478bd9Sstevel@tonic-gate 			ibuf[0] = obuf[i] = '\0';
6937c478bd9Sstevel@tonic-gate 			if (originalcode == 0)
6947c478bd9Sstevel@tonic-gate 				originalcode = code;
6957c478bd9Sstevel@tonic-gate 			continue;
6967c478bd9Sstevel@tonic-gate 		}
6977c478bd9Sstevel@tonic-gate 		*cp = '\0';
6987c478bd9Sstevel@tonic-gate 		if (n != '1')
6997c478bd9Sstevel@tonic-gate 			cpend = 0;
7007c478bd9Sstevel@tonic-gate 		(void) signal(SIGINT, oldintr);
7017c478bd9Sstevel@tonic-gate 		if (code == 421 || originalcode == 421)
7027c478bd9Sstevel@tonic-gate 			lostpeer(0);
7037c478bd9Sstevel@tonic-gate 		if (abrtflag && oldintr != cmdabort && oldintr != SIG_IGN)
7047c478bd9Sstevel@tonic-gate 			(*oldintr)();
7057c478bd9Sstevel@tonic-gate 
7067c478bd9Sstevel@tonic-gate 		if (reply_parse) {
707*92163adaSToomas Soome 			*reply_ptr = '\0';
708*92163adaSToomas Soome 			if (reply_ptr = strstr(reply_buf, reply_parse)) {
709*92163adaSToomas Soome 				reply_parse = reply_ptr + strlen(reply_parse);
710*92163adaSToomas Soome 				if (reply_ptr = strpbrk(reply_parse, " \r"))
711*92163adaSToomas Soome 					*reply_ptr = '\0';
712*92163adaSToomas Soome 			} else {
713*92163adaSToomas Soome 				reply_parse = reply_ptr;
714*92163adaSToomas Soome 			}
7157c478bd9Sstevel@tonic-gate 		}
7167c478bd9Sstevel@tonic-gate 
7177c478bd9Sstevel@tonic-gate 		return (n - '0');
7187c478bd9Sstevel@tonic-gate 	} /* end for */
7197c478bd9Sstevel@tonic-gate }
7207c478bd9Sstevel@tonic-gate 
7217c478bd9Sstevel@tonic-gate static int
empty(struct fd_set * mask,int sec,int nfds)7227c478bd9Sstevel@tonic-gate empty(struct fd_set *mask, int sec, int nfds)
7237c478bd9Sstevel@tonic-gate {
7247c478bd9Sstevel@tonic-gate 	struct timeval t;
7257c478bd9Sstevel@tonic-gate 
7267c478bd9Sstevel@tonic-gate 	reset_timer();
7277c478bd9Sstevel@tonic-gate 	t.tv_sec = (time_t)sec;
7287c478bd9Sstevel@tonic-gate 	t.tv_usec = 0;
7297c478bd9Sstevel@tonic-gate 	return (select(nfds, mask, NULL, NULL, &t));
7307c478bd9Sstevel@tonic-gate }
7317c478bd9Sstevel@tonic-gate 
7327c478bd9Sstevel@tonic-gate /*ARGSUSED*/
7337c478bd9Sstevel@tonic-gate static void
abortsend(int sig)7347c478bd9Sstevel@tonic-gate abortsend(int sig)
7357c478bd9Sstevel@tonic-gate {
7367c478bd9Sstevel@tonic-gate 	mflag = 0;
7377c478bd9Sstevel@tonic-gate 	abrtflag = 0;
7387c478bd9Sstevel@tonic-gate 	(void) printf("\nsend aborted\n");
7397c478bd9Sstevel@tonic-gate 	(void) fflush(stdout);
7407c478bd9Sstevel@tonic-gate 	longjmp(sendabort, 1);
7417c478bd9Sstevel@tonic-gate }
7427c478bd9Sstevel@tonic-gate 
7437c478bd9Sstevel@tonic-gate void
sendrequest(char * cmd,char * local,char * remote,int allowpipe)7447c478bd9Sstevel@tonic-gate sendrequest(char *cmd, char *local, char *remote, int allowpipe)
7457c478bd9Sstevel@tonic-gate {
7467c478bd9Sstevel@tonic-gate 	FILE *fin, *dout = 0;
7477c478bd9Sstevel@tonic-gate 	int (*closefunc)();
7487c478bd9Sstevel@tonic-gate 	void (*oldintr)(), (*oldintp)();
7497c478bd9Sstevel@tonic-gate 	off_t bytes = 0, hashbytes = HASHSIZ;
7507c478bd9Sstevel@tonic-gate 	int c;
7517c478bd9Sstevel@tonic-gate 	/*
7527c478bd9Sstevel@tonic-gate 	 * d >=	 0 if there is no error
7537c478bd9Sstevel@tonic-gate 	 *	-1 if there was a normal file i/o error
7547c478bd9Sstevel@tonic-gate 	 *	-2 if there was a security error
7557c478bd9Sstevel@tonic-gate 	 */
7567c478bd9Sstevel@tonic-gate 	int d;
7577c478bd9Sstevel@tonic-gate 	struct stat st;
7587c478bd9Sstevel@tonic-gate 	hrtime_t start, stop;
7597c478bd9Sstevel@tonic-gate 	char *dmode;
7607c478bd9Sstevel@tonic-gate 
7617c478bd9Sstevel@tonic-gate 	if (proxy) {
7627c478bd9Sstevel@tonic-gate 		proxtrans(cmd, local, remote);
7637c478bd9Sstevel@tonic-gate 		return;
7647c478bd9Sstevel@tonic-gate 	}
7657c478bd9Sstevel@tonic-gate 	closefunc = NULL;
7667c478bd9Sstevel@tonic-gate 	oldintr = NULL;
7677c478bd9Sstevel@tonic-gate 	oldintp = NULL;
7687c478bd9Sstevel@tonic-gate 	dmode = "w";
7697c478bd9Sstevel@tonic-gate 	if (setjmp(sendabort)) {
7707c478bd9Sstevel@tonic-gate 		while (cpend) {
7717c478bd9Sstevel@tonic-gate 			(void) getreply(0);
7727c478bd9Sstevel@tonic-gate 		}
7737c478bd9Sstevel@tonic-gate 		if (data >= 0) {
7747c478bd9Sstevel@tonic-gate 			(void) close(data);
7757c478bd9Sstevel@tonic-gate 			data = -1;
7767c478bd9Sstevel@tonic-gate 		}
7777c478bd9Sstevel@tonic-gate 		if (oldintr)
7787c478bd9Sstevel@tonic-gate 			(void) signal(SIGINT, oldintr);
7797c478bd9Sstevel@tonic-gate 		if (oldintp)
7807c478bd9Sstevel@tonic-gate 			(void) signal(SIGPIPE, oldintp);
7817c478bd9Sstevel@tonic-gate 		code = -1;
7827c478bd9Sstevel@tonic-gate 		restart_point = 0;
7837c478bd9Sstevel@tonic-gate 		return;
7847c478bd9Sstevel@tonic-gate 	}
7857c478bd9Sstevel@tonic-gate 	oldintr = signal(SIGINT, abortsend);
7867c478bd9Sstevel@tonic-gate 	if (strcmp(local, "-") == 0)
7877c478bd9Sstevel@tonic-gate 		fin = stdin;
7887c478bd9Sstevel@tonic-gate 	else if (allowpipe && *local == '|') {
7897c478bd9Sstevel@tonic-gate 		oldintp = signal(SIGPIPE, SIG_IGN);
7907c478bd9Sstevel@tonic-gate 		fin = mypopen(local + 1, "r");
7917c478bd9Sstevel@tonic-gate 		if (fin == NULL) {
7927c478bd9Sstevel@tonic-gate 			perror(local + 1);
7937c478bd9Sstevel@tonic-gate 			(void) signal(SIGINT, oldintr);
7947c478bd9Sstevel@tonic-gate 			(void) signal(SIGPIPE, oldintp);
7957c478bd9Sstevel@tonic-gate 			code = -1;
7967c478bd9Sstevel@tonic-gate 			restart_point = 0;
7977c478bd9Sstevel@tonic-gate 			return;
7987c478bd9Sstevel@tonic-gate 		}
7997c478bd9Sstevel@tonic-gate 		closefunc = mypclose;
8007c478bd9Sstevel@tonic-gate 	} else {
8017c478bd9Sstevel@tonic-gate 		fin = fopen(local, "r");
8027c478bd9Sstevel@tonic-gate 		if (fin == NULL) {
8037c478bd9Sstevel@tonic-gate 			perror(local);
8047c478bd9Sstevel@tonic-gate 			(void) signal(SIGINT, oldintr);
8057c478bd9Sstevel@tonic-gate 			code = -1;
8067c478bd9Sstevel@tonic-gate 			restart_point = 0;
8077c478bd9Sstevel@tonic-gate 			return;
8087c478bd9Sstevel@tonic-gate 		}
8097c478bd9Sstevel@tonic-gate 		closefunc = fclose;
8107c478bd9Sstevel@tonic-gate 		if (fstat(fileno(fin), &st) < 0 ||
8117c478bd9Sstevel@tonic-gate 		    (st.st_mode&S_IFMT) != S_IFREG) {
8127c478bd9Sstevel@tonic-gate 			(void) fprintf(stdout,
813*92163adaSToomas Soome 			    "%s: not a plain file.\n", local);
8147c478bd9Sstevel@tonic-gate 			(void) signal(SIGINT, oldintr);
8157c478bd9Sstevel@tonic-gate 			code = -1;
8167c478bd9Sstevel@tonic-gate 			(void) fclose(fin);
8177c478bd9Sstevel@tonic-gate 			restart_point = 0;
8187c478bd9Sstevel@tonic-gate 			return;
8197c478bd9Sstevel@tonic-gate 		}
8207c478bd9Sstevel@tonic-gate 	}
8217c478bd9Sstevel@tonic-gate 	if (initconn()) {
8227c478bd9Sstevel@tonic-gate 		(void) signal(SIGINT, oldintr);
8237c478bd9Sstevel@tonic-gate 		if (oldintp)
8247c478bd9Sstevel@tonic-gate 			(void) signal(SIGPIPE, oldintp);
8257c478bd9Sstevel@tonic-gate 		code = -1;
8267c478bd9Sstevel@tonic-gate 		if (closefunc != NULL)
8277c478bd9Sstevel@tonic-gate 			(*closefunc)(fin);
8287c478bd9Sstevel@tonic-gate 		restart_point = 0;
8297c478bd9Sstevel@tonic-gate 		return;
8307c478bd9Sstevel@tonic-gate 	}
8317c478bd9Sstevel@tonic-gate 	if (setjmp(sendabort))
8327c478bd9Sstevel@tonic-gate 		goto abort;
8337c478bd9Sstevel@tonic-gate 	if ((restart_point > 0) &&
8347c478bd9Sstevel@tonic-gate 	    (strcmp(cmd, "STOR") == 0 || strcmp(cmd, "APPE") == 0)) {
8357c478bd9Sstevel@tonic-gate 		if (fseeko(fin, restart_point, SEEK_SET) < 0) {
8367c478bd9Sstevel@tonic-gate 			perror(local);
8377c478bd9Sstevel@tonic-gate 			if (closefunc != NULL)
8387c478bd9Sstevel@tonic-gate 				(*closefunc)(fin);
8397c478bd9Sstevel@tonic-gate 			restart_point = 0;
8407c478bd9Sstevel@tonic-gate 			return;
8417c478bd9Sstevel@tonic-gate 		}
8427c478bd9Sstevel@tonic-gate 		if (command("REST %lld", (longlong_t)restart_point)
843*92163adaSToomas Soome 		    != CONTINUE) {
8447c478bd9Sstevel@tonic-gate 			if (closefunc != NULL)
8457c478bd9Sstevel@tonic-gate 				(*closefunc)(fin);
8467c478bd9Sstevel@tonic-gate 			restart_point = 0;
8477c478bd9Sstevel@tonic-gate 			return;
8487c478bd9Sstevel@tonic-gate 		}
8497c478bd9Sstevel@tonic-gate 		dmode = "r+w";
8507c478bd9Sstevel@tonic-gate 	}
8517c478bd9Sstevel@tonic-gate 	restart_point = 0;
8527c478bd9Sstevel@tonic-gate 	if (remote) {
8537c478bd9Sstevel@tonic-gate 		if (command("%s %s", cmd, remote) != PRELIM) {
8547c478bd9Sstevel@tonic-gate 			(void) signal(SIGINT, oldintr);
8557c478bd9Sstevel@tonic-gate 			if (oldintp)
8567c478bd9Sstevel@tonic-gate 				(void) signal(SIGPIPE, oldintp);
8577c478bd9Sstevel@tonic-gate 			if (closefunc != NULL)
8587c478bd9Sstevel@tonic-gate 				(*closefunc)(fin);
8597c478bd9Sstevel@tonic-gate 			if (data >= 0) {
8607c478bd9Sstevel@tonic-gate 				(void) close(data);
8617c478bd9Sstevel@tonic-gate 				data = -1;
8627c478bd9Sstevel@tonic-gate 			}
8637c478bd9Sstevel@tonic-gate 			return;
8647c478bd9Sstevel@tonic-gate 		}
8657c478bd9Sstevel@tonic-gate 	} else
8667c478bd9Sstevel@tonic-gate 		if (command("%s", cmd) != PRELIM) {
8677c478bd9Sstevel@tonic-gate 			(void) signal(SIGINT, oldintr);
8687c478bd9Sstevel@tonic-gate 			if (oldintp)
8697c478bd9Sstevel@tonic-gate 				(void) signal(SIGPIPE, oldintp);
8707c478bd9Sstevel@tonic-gate 			if (closefunc != NULL)
8717c478bd9Sstevel@tonic-gate 				(*closefunc)(fin);
8727c478bd9Sstevel@tonic-gate 			if (data >= 0) {
8737c478bd9Sstevel@tonic-gate 				(void) close(data);
8747c478bd9Sstevel@tonic-gate 				data = -1;
8757c478bd9Sstevel@tonic-gate 			}
8767c478bd9Sstevel@tonic-gate 			return;
8777c478bd9Sstevel@tonic-gate 		}
8787c478bd9Sstevel@tonic-gate 	dout = dataconn(dmode);
8797c478bd9Sstevel@tonic-gate 	if (dout == NULL)
8807c478bd9Sstevel@tonic-gate 		goto abort;
8817c478bd9Sstevel@tonic-gate 	stop_timer();
8827c478bd9Sstevel@tonic-gate 	oldintp = signal(SIGPIPE, SIG_IGN);
8837c478bd9Sstevel@tonic-gate 	start = gethrtime();
8847c478bd9Sstevel@tonic-gate 	switch (type) {
8857c478bd9Sstevel@tonic-gate 
8867c478bd9Sstevel@tonic-gate 	case TYPE_I:
8877c478bd9Sstevel@tonic-gate 	case TYPE_L:
8887c478bd9Sstevel@tonic-gate 		errno = d = 0;
8897c478bd9Sstevel@tonic-gate 		while ((c = read(fileno(fin), buf, FTPBUFSIZ)) > 0) {
8907c478bd9Sstevel@tonic-gate 			if ((d = WRITE(fileno(dout), buf, c)) < 0)
8917c478bd9Sstevel@tonic-gate 				break;
8927c478bd9Sstevel@tonic-gate 			bytes += c;
8937c478bd9Sstevel@tonic-gate 			if (hash) {
8947c478bd9Sstevel@tonic-gate 				while (bytes >= hashbytes) {
8957c478bd9Sstevel@tonic-gate 					(void) putchar('#');
8967c478bd9Sstevel@tonic-gate 					hashbytes += HASHSIZ;
8977c478bd9Sstevel@tonic-gate 				}
8987c478bd9Sstevel@tonic-gate 				(void) fflush(stdout);
8997c478bd9Sstevel@tonic-gate 			}
9007c478bd9Sstevel@tonic-gate 		}
9017c478bd9Sstevel@tonic-gate 		if (hash && bytes > 0) {
9027c478bd9Sstevel@tonic-gate 			if (bytes < hashbytes)
9037c478bd9Sstevel@tonic-gate 				(void) putchar('#');
9047c478bd9Sstevel@tonic-gate 			(void) putchar('\n');
9057c478bd9Sstevel@tonic-gate 			(void) fflush(stdout);
9067c478bd9Sstevel@tonic-gate 		}
9077c478bd9Sstevel@tonic-gate 		if (c < 0)
9087c478bd9Sstevel@tonic-gate 			perror(local);
9097c478bd9Sstevel@tonic-gate 
9107c478bd9Sstevel@tonic-gate 		if (d >= 0)
9117c478bd9Sstevel@tonic-gate 			d = secure_flush(fileno(dout));
9127c478bd9Sstevel@tonic-gate 
9137c478bd9Sstevel@tonic-gate 		if (d < 0) {
9147c478bd9Sstevel@tonic-gate 			if ((d == -1) && (errno != EPIPE))
9157c478bd9Sstevel@tonic-gate 				perror("netout");
9167c478bd9Sstevel@tonic-gate 			bytes = -1;
9177c478bd9Sstevel@tonic-gate 		}
9187c478bd9Sstevel@tonic-gate 		break;
9197c478bd9Sstevel@tonic-gate 
9207c478bd9Sstevel@tonic-gate 	case TYPE_A:
9217c478bd9Sstevel@tonic-gate 		while ((c = getc(fin)) != EOF) {
9227c478bd9Sstevel@tonic-gate 			if (c == '\n') {
9237c478bd9Sstevel@tonic-gate 				while (hash && (bytes >= hashbytes)) {
9247c478bd9Sstevel@tonic-gate 					(void) putchar('#');
9257c478bd9Sstevel@tonic-gate 					(void) fflush(stdout);
9267c478bd9Sstevel@tonic-gate 					hashbytes += HASHSIZ;
9277c478bd9Sstevel@tonic-gate 				}
9287c478bd9Sstevel@tonic-gate 				if (ferror(dout) || PUTC('\r', dout) < 0)
9297c478bd9Sstevel@tonic-gate 					break;
9307c478bd9Sstevel@tonic-gate 				bytes++;
9317c478bd9Sstevel@tonic-gate 			}
9327c478bd9Sstevel@tonic-gate 
9337c478bd9Sstevel@tonic-gate 			if (PUTC(c, dout) < 0)
9347c478bd9Sstevel@tonic-gate 				break;
9357c478bd9Sstevel@tonic-gate 			bytes++;
9367c478bd9Sstevel@tonic-gate #ifdef notdef
9377c478bd9Sstevel@tonic-gate 			if (c == '\r') {
9387c478bd9Sstevel@tonic-gate 				/* this violates rfc */
9397c478bd9Sstevel@tonic-gate 				(void) PUTC('\0', dout);
9407c478bd9Sstevel@tonic-gate 				bytes++;
9417c478bd9Sstevel@tonic-gate 			}
9427c478bd9Sstevel@tonic-gate #endif
9437c478bd9Sstevel@tonic-gate 		}
9447c478bd9Sstevel@tonic-gate 		if (hash && bytes > 0) {
9457c478bd9Sstevel@tonic-gate 			if (bytes < hashbytes)
9467c478bd9Sstevel@tonic-gate 				(void) putchar('#');
9477c478bd9Sstevel@tonic-gate 			(void) putchar('\n');
9487c478bd9Sstevel@tonic-gate 			(void) fflush(stdout);
9497c478bd9Sstevel@tonic-gate 		}
9507c478bd9Sstevel@tonic-gate 		if (ferror(fin))
9517c478bd9Sstevel@tonic-gate 			perror(local);
9527c478bd9Sstevel@tonic-gate 
9537c478bd9Sstevel@tonic-gate 		d = ferror(dout) ? -1 : 0;
9547c478bd9Sstevel@tonic-gate 		if (d == 0)
9557c478bd9Sstevel@tonic-gate 			d = secure_flush(fileno(dout));
9567c478bd9Sstevel@tonic-gate 
9577c478bd9Sstevel@tonic-gate 		if (d < 0) {
9587c478bd9Sstevel@tonic-gate 			if ((d == -1) && (errno != EPIPE))
9597c478bd9Sstevel@tonic-gate 				perror("netout");
9607c478bd9Sstevel@tonic-gate 			bytes = -1;
9617c478bd9Sstevel@tonic-gate 		}
9627c478bd9Sstevel@tonic-gate 		break;
9637c478bd9Sstevel@tonic-gate 	}
9647c478bd9Sstevel@tonic-gate 	reset_timer();
9657c478bd9Sstevel@tonic-gate 	if (closefunc != NULL)
9667c478bd9Sstevel@tonic-gate 		(*closefunc)(fin);
9677c478bd9Sstevel@tonic-gate 	if (ctrl_in != NULL) {
9687c478bd9Sstevel@tonic-gate 		int	dfn	= fileno(dout);
9697c478bd9Sstevel@tonic-gate 		int	nfds	= fileno(ctrl_in);
9707c478bd9Sstevel@tonic-gate 		fd_set  mask;
9717c478bd9Sstevel@tonic-gate 
9727c478bd9Sstevel@tonic-gate 		/*
9737c478bd9Sstevel@tonic-gate 		 * There could be data not yet written to dout,
9747c478bd9Sstevel@tonic-gate 		 * in the stdio buffer; so, before a shutdown()
9757c478bd9Sstevel@tonic-gate 		 * on further sends, do fflush(dout)
9767c478bd9Sstevel@tonic-gate 		 */
9777c478bd9Sstevel@tonic-gate 		(void) fflush(dout);
9787c478bd9Sstevel@tonic-gate 
9797c478bd9Sstevel@tonic-gate 		/* sending over; shutdown sending on dfn */
9807c478bd9Sstevel@tonic-gate 		(void) shutdown(dfn, SHUT_WR);
9817c478bd9Sstevel@tonic-gate 		FD_ZERO(&mask);
9827c478bd9Sstevel@tonic-gate 		FD_SET(dfn, &mask);
9837c478bd9Sstevel@tonic-gate 		FD_SET(nfds, &mask);
9847c478bd9Sstevel@tonic-gate 		nfds = MAX(dfn, nfds);
9857c478bd9Sstevel@tonic-gate 
9867c478bd9Sstevel@tonic-gate 		/*
9877c478bd9Sstevel@tonic-gate 		 * Wait for remote end to either close data socket
9887c478bd9Sstevel@tonic-gate 		 * or ack that we've closed our end; it doesn't
9897c478bd9Sstevel@tonic-gate 		 * matter which happens first.
9907c478bd9Sstevel@tonic-gate 		 */
9917c478bd9Sstevel@tonic-gate 		(void) select(nfds + 1, &mask, NULL, NULL, NULL);
9927c478bd9Sstevel@tonic-gate 	}
9937c478bd9Sstevel@tonic-gate 	(void) fclose(dout); data = -1;
9947c478bd9Sstevel@tonic-gate 	stop = gethrtime();
9957c478bd9Sstevel@tonic-gate 	(void) getreply(0);
9967c478bd9Sstevel@tonic-gate 	(void) signal(SIGINT, oldintr);
9977c478bd9Sstevel@tonic-gate 	if (oldintp)
9987c478bd9Sstevel@tonic-gate 		(void) signal(SIGPIPE, oldintp);
9997c478bd9Sstevel@tonic-gate 
10007c478bd9Sstevel@tonic-gate 	/*
10017c478bd9Sstevel@tonic-gate 	 * Only print the transfer successful message if the code returned
10027c478bd9Sstevel@tonic-gate 	 * from remote is 226 or 250. All other codes are error codes.
10037c478bd9Sstevel@tonic-gate 	 */
10047c478bd9Sstevel@tonic-gate 	if ((bytes > 0) && verbose && ((code == 226) || (code == 250)))
10057c478bd9Sstevel@tonic-gate 		ptransfer("sent", bytes, start, stop, local, remote);
10067c478bd9Sstevel@tonic-gate 	if (!ctrl_in)
10077c478bd9Sstevel@tonic-gate 		(void) printf("Lost connection\n");
10087c478bd9Sstevel@tonic-gate 	return;
10097c478bd9Sstevel@tonic-gate abort:
10107c478bd9Sstevel@tonic-gate 	(void) signal(SIGINT, oldintr);
10117c478bd9Sstevel@tonic-gate 	if (oldintp)
10127c478bd9Sstevel@tonic-gate 		(void) signal(SIGPIPE, oldintp);
10137c478bd9Sstevel@tonic-gate 	if (!cpend) {
10147c478bd9Sstevel@tonic-gate 		code = -1;
10157c478bd9Sstevel@tonic-gate 		return;
10167c478bd9Sstevel@tonic-gate 	}
10177c478bd9Sstevel@tonic-gate 	if (data >= 0) {
10187c478bd9Sstevel@tonic-gate 		(void) close(data);
10197c478bd9Sstevel@tonic-gate 		data = -1;
10207c478bd9Sstevel@tonic-gate 	}
10217c478bd9Sstevel@tonic-gate 	if (dout) {
10227c478bd9Sstevel@tonic-gate 		(void) fclose(dout);
10237c478bd9Sstevel@tonic-gate 		data = -1;
10247c478bd9Sstevel@tonic-gate 	}
10257c478bd9Sstevel@tonic-gate 	(void) getreply(0);
10267c478bd9Sstevel@tonic-gate 	code = -1;
10277c478bd9Sstevel@tonic-gate 	if (closefunc != NULL && fin != NULL)
10287c478bd9Sstevel@tonic-gate 		(*closefunc)(fin);
10297c478bd9Sstevel@tonic-gate 	stop = gethrtime();
10307c478bd9Sstevel@tonic-gate 	/*
10317c478bd9Sstevel@tonic-gate 	 * Only print the transfer successful message if the code returned
10327c478bd9Sstevel@tonic-gate 	 * from remote is 226 or 250. All other codes are error codes.
10337c478bd9Sstevel@tonic-gate 	 */
10347c478bd9Sstevel@tonic-gate 	if ((bytes > 0) && verbose && ((code == 226) || (code == 250)))
10357c478bd9Sstevel@tonic-gate 		ptransfer("sent", bytes, start, stop, local, remote);
10367c478bd9Sstevel@tonic-gate 	if (!ctrl_in)
10377c478bd9Sstevel@tonic-gate 		(void) printf("Lost connection\n");
10387c478bd9Sstevel@tonic-gate 	restart_point = 0;
10397c478bd9Sstevel@tonic-gate }
10407c478bd9Sstevel@tonic-gate 
10417c478bd9Sstevel@tonic-gate /*ARGSUSED*/
10427c478bd9Sstevel@tonic-gate static void
abortrecv(int sig)10437c478bd9Sstevel@tonic-gate abortrecv(int sig)
10447c478bd9Sstevel@tonic-gate {
10457c478bd9Sstevel@tonic-gate 	mflag = 0;
10467c478bd9Sstevel@tonic-gate 	abrtflag = 0;
10477c478bd9Sstevel@tonic-gate 	(void) printf("\n");
10487c478bd9Sstevel@tonic-gate 	(void) fflush(stdout);
10497c478bd9Sstevel@tonic-gate 	longjmp(recvabort, 1);
10507c478bd9Sstevel@tonic-gate }
10517c478bd9Sstevel@tonic-gate 
10527c478bd9Sstevel@tonic-gate void
recvrequest(char * cmd,char * local,char * remote,char * mode,int allowpipe)10537c478bd9Sstevel@tonic-gate recvrequest(char *cmd, char *local, char *remote, char *mode, int allowpipe)
10547c478bd9Sstevel@tonic-gate {
10557c478bd9Sstevel@tonic-gate 	FILE *fout, *din = 0;
10567c478bd9Sstevel@tonic-gate 	int (*closefunc)();
10577c478bd9Sstevel@tonic-gate 	void (*oldintr)(), (*oldintp)();
10587c478bd9Sstevel@tonic-gate 	int oldverbose, oldtype = 0, tcrflag, nfnd;
10597c478bd9Sstevel@tonic-gate 	char msg;
10607c478bd9Sstevel@tonic-gate 	off_t bytes = 0, hashbytes = HASHSIZ;
10617c478bd9Sstevel@tonic-gate 	struct fd_set mask;
10627c478bd9Sstevel@tonic-gate 	int c, d, n;
10637c478bd9Sstevel@tonic-gate 	hrtime_t start, stop;
10647c478bd9Sstevel@tonic-gate 	int errflg = 0;
10657c478bd9Sstevel@tonic-gate 	int infd;
10667c478bd9Sstevel@tonic-gate 	int nfds;
10677c478bd9Sstevel@tonic-gate 	int retrcmd;
10687c478bd9Sstevel@tonic-gate 
10697c478bd9Sstevel@tonic-gate 	retrcmd = (strcmp(cmd, "RETR") == 0);
10707c478bd9Sstevel@tonic-gate 	if (proxy && retrcmd) {
10717c478bd9Sstevel@tonic-gate 		proxtrans(cmd, local, remote);
10727c478bd9Sstevel@tonic-gate 		return;
10737c478bd9Sstevel@tonic-gate 	}
10747c478bd9Sstevel@tonic-gate 	closefunc = NULL;
10757c478bd9Sstevel@tonic-gate 	oldintr = NULL;
10767c478bd9Sstevel@tonic-gate 	oldintp = NULL;
10777c478bd9Sstevel@tonic-gate 	tcrflag = !crflag && retrcmd;
10787c478bd9Sstevel@tonic-gate 	if (setjmp(recvabort)) {
10797c478bd9Sstevel@tonic-gate 		while (cpend) {
10807c478bd9Sstevel@tonic-gate 			(void) getreply(0);
10817c478bd9Sstevel@tonic-gate 		}
10827c478bd9Sstevel@tonic-gate 		if (data >= 0) {
10837c478bd9Sstevel@tonic-gate 			(void) close(data);
10847c478bd9Sstevel@tonic-gate 			data = -1;
10857c478bd9Sstevel@tonic-gate 		}
10867c478bd9Sstevel@tonic-gate 		if (oldintr)
10877c478bd9Sstevel@tonic-gate 			(void) signal(SIGINT, oldintr);
10887c478bd9Sstevel@tonic-gate 		code = -1;
10897c478bd9Sstevel@tonic-gate 		return;
10907c478bd9Sstevel@tonic-gate 	}
10917c478bd9Sstevel@tonic-gate 	oldintr = signal(SIGINT, abortrecv);
10927c478bd9Sstevel@tonic-gate 	if (local != NULL &&
10937c478bd9Sstevel@tonic-gate 	    strcmp(local, "-") != 0 &&
10947c478bd9Sstevel@tonic-gate 	    (*local != '|' || !allowpipe)) {
10957c478bd9Sstevel@tonic-gate 		if (access(local, W_OK) < 0) {
10967c478bd9Sstevel@tonic-gate 			char *dir = rindex(local, '/');
10977c478bd9Sstevel@tonic-gate 			int file_errno = errno;
10987c478bd9Sstevel@tonic-gate 
10997c478bd9Sstevel@tonic-gate 			if (file_errno != ENOENT && file_errno != EACCES) {
11007c478bd9Sstevel@tonic-gate 				perror(local);
11017c478bd9Sstevel@tonic-gate 				(void) signal(SIGINT, oldintr);
11027c478bd9Sstevel@tonic-gate 				code = -1;
11037c478bd9Sstevel@tonic-gate 				return;
11047c478bd9Sstevel@tonic-gate 			}
11057c478bd9Sstevel@tonic-gate 			if ((dir != NULL) && (dir != local))
11067c478bd9Sstevel@tonic-gate 				*dir = 0;
11077c478bd9Sstevel@tonic-gate 			if (dir == local)
11087c478bd9Sstevel@tonic-gate 				d = access("/", W_OK);
11097c478bd9Sstevel@tonic-gate 			else
11107c478bd9Sstevel@tonic-gate 				d = access(dir ? local : ".", W_OK);
11117c478bd9Sstevel@tonic-gate 			if ((dir != NULL) && (dir != local))
11127c478bd9Sstevel@tonic-gate 				*dir = '/';
11137c478bd9Sstevel@tonic-gate 			if (d < 0) {
11147c478bd9Sstevel@tonic-gate 				perror(local);
11157c478bd9Sstevel@tonic-gate 				(void) signal(SIGINT, oldintr);
11167c478bd9Sstevel@tonic-gate 				code = -1;
11177c478bd9Sstevel@tonic-gate 				return;
11187c478bd9Sstevel@tonic-gate 			}
11197c478bd9Sstevel@tonic-gate 			if (!runique && file_errno == EACCES) {
11207c478bd9Sstevel@tonic-gate 				errno = file_errno;
11217c478bd9Sstevel@tonic-gate 				perror(local);
11227c478bd9Sstevel@tonic-gate 				(void) signal(SIGINT, oldintr);
11237c478bd9Sstevel@tonic-gate 				code = -1;
11247c478bd9Sstevel@tonic-gate 				return;
11257c478bd9Sstevel@tonic-gate 			}
11267c478bd9Sstevel@tonic-gate 			if (runique && file_errno == EACCES &&
11277c478bd9Sstevel@tonic-gate 			    (local = gunique(local)) == NULL) {
11287c478bd9Sstevel@tonic-gate 				(void) signal(SIGINT, oldintr);
11297c478bd9Sstevel@tonic-gate 				code = -1;
11307c478bd9Sstevel@tonic-gate 				return;
11317c478bd9Sstevel@tonic-gate 			}
11327c478bd9Sstevel@tonic-gate 		} else if (runique && (local = gunique(local)) == NULL) {
11337c478bd9Sstevel@tonic-gate 			(void) signal(SIGINT, oldintr);
11347c478bd9Sstevel@tonic-gate 			code = -1;
11357c478bd9Sstevel@tonic-gate 			return;
11367c478bd9Sstevel@tonic-gate 		}
11377c478bd9Sstevel@tonic-gate 	}
11387c478bd9Sstevel@tonic-gate 	if (initconn()) {
11397c478bd9Sstevel@tonic-gate 		(void) signal(SIGINT, oldintr);
11407c478bd9Sstevel@tonic-gate 		code = -1;
11417c478bd9Sstevel@tonic-gate 		return;
11427c478bd9Sstevel@tonic-gate 	}
11437c478bd9Sstevel@tonic-gate 	if (setjmp(recvabort))
11447c478bd9Sstevel@tonic-gate 		goto abort;
11457c478bd9Sstevel@tonic-gate 	if (!retrcmd && type != TYPE_A) {
11467c478bd9Sstevel@tonic-gate 		oldtype = type;
11477c478bd9Sstevel@tonic-gate 		oldverbose = verbose;
11487c478bd9Sstevel@tonic-gate 		if (!debug)
11497c478bd9Sstevel@tonic-gate 			verbose = 0;
11507c478bd9Sstevel@tonic-gate 		setascii(0, NULL);
11517c478bd9Sstevel@tonic-gate 		verbose = oldverbose;
11527c478bd9Sstevel@tonic-gate 	}
11537c478bd9Sstevel@tonic-gate 	if ((restart_point > 0) && retrcmd &&
11547c478bd9Sstevel@tonic-gate 	    command("REST %lld", (longlong_t)restart_point) != CONTINUE) {
11557c478bd9Sstevel@tonic-gate 		return;
11567c478bd9Sstevel@tonic-gate 	}
11577c478bd9Sstevel@tonic-gate 	if (remote) {
11587c478bd9Sstevel@tonic-gate 		if (command("%s %s", cmd, remote) != PRELIM) {
11597c478bd9Sstevel@tonic-gate 			(void) signal(SIGINT, oldintr);
11607c478bd9Sstevel@tonic-gate 			if (oldtype) {
11617c478bd9Sstevel@tonic-gate 				if (!debug)
11627c478bd9Sstevel@tonic-gate 					verbose = 0;
11637c478bd9Sstevel@tonic-gate 				switch (oldtype) {
11647c478bd9Sstevel@tonic-gate 					case TYPE_I:
11657c478bd9Sstevel@tonic-gate 						setbinary(0, NULL);
11667c478bd9Sstevel@tonic-gate 						break;
11677c478bd9Sstevel@tonic-gate 					case TYPE_E:
11687c478bd9Sstevel@tonic-gate 						setebcdic(0, NULL);
11697c478bd9Sstevel@tonic-gate 						break;
11707c478bd9Sstevel@tonic-gate 					case TYPE_L:
11717c478bd9Sstevel@tonic-gate 						settenex(0, NULL);
11727c478bd9Sstevel@tonic-gate 						break;
11737c478bd9Sstevel@tonic-gate 				}
11747c478bd9Sstevel@tonic-gate 				verbose = oldverbose;
11757c478bd9Sstevel@tonic-gate 			}
11767c478bd9Sstevel@tonic-gate 			return;
11777c478bd9Sstevel@tonic-gate 		}
11787c478bd9Sstevel@tonic-gate 	} else {
11797c478bd9Sstevel@tonic-gate 		if (command("%s", cmd) != PRELIM) {
11807c478bd9Sstevel@tonic-gate 			(void) signal(SIGINT, oldintr);
11817c478bd9Sstevel@tonic-gate 			if (oldtype) {
11827c478bd9Sstevel@tonic-gate 				if (!debug)
11837c478bd9Sstevel@tonic-gate 					verbose = 0;
11847c478bd9Sstevel@tonic-gate 				switch (oldtype) {
11857c478bd9Sstevel@tonic-gate 					case TYPE_I:
11867c478bd9Sstevel@tonic-gate 						setbinary(0, NULL);
11877c478bd9Sstevel@tonic-gate 						break;
11887c478bd9Sstevel@tonic-gate 					case TYPE_E:
11897c478bd9Sstevel@tonic-gate 						setebcdic(0, NULL);
11907c478bd9Sstevel@tonic-gate 						break;
11917c478bd9Sstevel@tonic-gate 					case TYPE_L:
11927c478bd9Sstevel@tonic-gate 						settenex(0, NULL);
11937c478bd9Sstevel@tonic-gate 						break;
11947c478bd9Sstevel@tonic-gate 				}
11957c478bd9Sstevel@tonic-gate 				verbose = oldverbose;
11967c478bd9Sstevel@tonic-gate 			}
11977c478bd9Sstevel@tonic-gate 			return;
11987c478bd9Sstevel@tonic-gate 		}
11997c478bd9Sstevel@tonic-gate 	}
12007c478bd9Sstevel@tonic-gate 	din = dataconn("r");
12017c478bd9Sstevel@tonic-gate 	if (din == NULL)
12027c478bd9Sstevel@tonic-gate 		goto abort;
12037c478bd9Sstevel@tonic-gate 
12047c478bd9Sstevel@tonic-gate 	if (local == NULL) {
12057c478bd9Sstevel@tonic-gate 		fout = tmp_nlst;
12067c478bd9Sstevel@tonic-gate 	} else if (strcmp(local, "-") == 0) {
12077c478bd9Sstevel@tonic-gate 		fout = stdout;
12087c478bd9Sstevel@tonic-gate 	} else if (allowpipe && *local == '|') {
12097c478bd9Sstevel@tonic-gate 		oldintp = signal(SIGPIPE, SIG_IGN);
12107c478bd9Sstevel@tonic-gate 		fout = mypopen(local + 1, "w");
12117c478bd9Sstevel@tonic-gate 		if (fout == NULL) {
12127c478bd9Sstevel@tonic-gate 			perror(local+1);
12137c478bd9Sstevel@tonic-gate 			goto abort;
12147c478bd9Sstevel@tonic-gate 		}
12157c478bd9Sstevel@tonic-gate 		closefunc = mypclose;
12167c478bd9Sstevel@tonic-gate 	} else {
12177c478bd9Sstevel@tonic-gate 		fout = fopen(local, mode);
12187c478bd9Sstevel@tonic-gate 		if (fout == NULL) {
12197c478bd9Sstevel@tonic-gate 			perror(local);
12207c478bd9Sstevel@tonic-gate 			goto abort;
12217c478bd9Sstevel@tonic-gate 		}
12227c478bd9Sstevel@tonic-gate 		closefunc = fclose;
12237c478bd9Sstevel@tonic-gate 	}
12247c478bd9Sstevel@tonic-gate 	start = gethrtime();
12257c478bd9Sstevel@tonic-gate 	stop_timer();
12267c478bd9Sstevel@tonic-gate 	switch (type) {
12277c478bd9Sstevel@tonic-gate 
12287c478bd9Sstevel@tonic-gate 	case TYPE_I:
12297c478bd9Sstevel@tonic-gate 	case TYPE_L:
12307c478bd9Sstevel@tonic-gate 		if ((restart_point > 0) && retrcmd &&
12317c478bd9Sstevel@tonic-gate 		    lseek(fileno(fout), restart_point, SEEK_SET) < 0) {
12327c478bd9Sstevel@tonic-gate 			perror(local);
12337c478bd9Sstevel@tonic-gate 			goto abort;
12347c478bd9Sstevel@tonic-gate 		}
12357c478bd9Sstevel@tonic-gate 		errno = d = 0;
12367c478bd9Sstevel@tonic-gate 		infd = fileno(din);
12377c478bd9Sstevel@tonic-gate 		while ((c = timedread(infd, buf, FTPBUFSIZ, timeout)) > 0) {
12387c478bd9Sstevel@tonic-gate 			for (n = 0; n < c; n += d) {
12397c478bd9Sstevel@tonic-gate 				d = write(fileno(fout), &buf[n], c - n);
12407c478bd9Sstevel@tonic-gate 				if (d == -1)
12417c478bd9Sstevel@tonic-gate 					goto writeerr;
12427c478bd9Sstevel@tonic-gate 			}
12437c478bd9Sstevel@tonic-gate 			bytes += c;
12447c478bd9Sstevel@tonic-gate 			if (hash) {
12457c478bd9Sstevel@tonic-gate 				while (bytes >= hashbytes) {
12467c478bd9Sstevel@tonic-gate 					(void) putchar('#');
12477c478bd9Sstevel@tonic-gate 					hashbytes += HASHSIZ;
12487c478bd9Sstevel@tonic-gate 				}
12497c478bd9Sstevel@tonic-gate 				(void) fflush(stdout);
12507c478bd9Sstevel@tonic-gate 			}
12517c478bd9Sstevel@tonic-gate 		}
12527c478bd9Sstevel@tonic-gate 		if (hash && bytes > 0) {
12537c478bd9Sstevel@tonic-gate 			if (bytes < hashbytes)
12547c478bd9Sstevel@tonic-gate 				(void) putchar('#');
12557c478bd9Sstevel@tonic-gate 			(void) putchar('\n');
12567c478bd9Sstevel@tonic-gate 			(void) fflush(stdout);
12577c478bd9Sstevel@tonic-gate 		}
12587c478bd9Sstevel@tonic-gate 		if (c < 0) {
12597c478bd9Sstevel@tonic-gate 			errflg = 1;
12607c478bd9Sstevel@tonic-gate 			perror("netin");
12617c478bd9Sstevel@tonic-gate 		}
12627c478bd9Sstevel@tonic-gate 		if ((d < 0) || ((c == 0) && (fsync(fileno(fout)) == -1))) {
12637c478bd9Sstevel@tonic-gate writeerr:
12647c478bd9Sstevel@tonic-gate 			errflg = 1;
12657c478bd9Sstevel@tonic-gate 			perror(local);
12667c478bd9Sstevel@tonic-gate 		}
12677c478bd9Sstevel@tonic-gate 		break;
12687c478bd9Sstevel@tonic-gate 
12697c478bd9Sstevel@tonic-gate 	case TYPE_A:
12707c478bd9Sstevel@tonic-gate 		if ((restart_point > 0) && retrcmd) {
12717c478bd9Sstevel@tonic-gate 			int c;
12727c478bd9Sstevel@tonic-gate 			off_t i = 0;
12737c478bd9Sstevel@tonic-gate 
12747c478bd9Sstevel@tonic-gate 			if (fseek(fout, 0L, SEEK_SET) < 0) {
12757c478bd9Sstevel@tonic-gate 				perror(local);
12767c478bd9Sstevel@tonic-gate 				goto abort;
12777c478bd9Sstevel@tonic-gate 			}
12787c478bd9Sstevel@tonic-gate 			while (i++ < restart_point) {
12797c478bd9Sstevel@tonic-gate 				if ((c = getc(fout)) == EOF) {
12807c478bd9Sstevel@tonic-gate 					if (ferror(fout))
12817c478bd9Sstevel@tonic-gate 						perror(local);
12827c478bd9Sstevel@tonic-gate 					else
12837c478bd9Sstevel@tonic-gate 						(void) fprintf(stderr,
1284*92163adaSToomas Soome 						    "%s: Unexpected end of "
1285*92163adaSToomas Soome 						    "file\n", local);
12867c478bd9Sstevel@tonic-gate 					goto abort;
12877c478bd9Sstevel@tonic-gate 				}
12887c478bd9Sstevel@tonic-gate 				if (c == '\n')
12897c478bd9Sstevel@tonic-gate 					i++;
12907c478bd9Sstevel@tonic-gate 			}
12917c478bd9Sstevel@tonic-gate 			if (fseeko(fout, 0L, SEEK_CUR) < 0) {
12927c478bd9Sstevel@tonic-gate 				perror(local);
12937c478bd9Sstevel@tonic-gate 				goto abort;
12947c478bd9Sstevel@tonic-gate 			}
12957c478bd9Sstevel@tonic-gate 		}
12967c478bd9Sstevel@tonic-gate 		fdio_setbuf(buf, FTPBUFSIZ);
12977c478bd9Sstevel@tonic-gate 		infd = fileno(din);
12987c478bd9Sstevel@tonic-gate 		while ((c = fdio_getc(infd)) != EOF) {
12997c478bd9Sstevel@tonic-gate 			while (c == '\r') {
13007c478bd9Sstevel@tonic-gate 				while (hash && (bytes >= hashbytes)) {
13017c478bd9Sstevel@tonic-gate 					(void) putchar('#');
13027c478bd9Sstevel@tonic-gate 					(void) fflush(stdout);
13037c478bd9Sstevel@tonic-gate 					hashbytes += HASHSIZ;
13047c478bd9Sstevel@tonic-gate 				}
13057c478bd9Sstevel@tonic-gate 				bytes++;
13067c478bd9Sstevel@tonic-gate 
13077c478bd9Sstevel@tonic-gate 				if ((c = fdio_getc(infd)) != '\n' || tcrflag) {
13087c478bd9Sstevel@tonic-gate 					if (ferror(fout))
13097c478bd9Sstevel@tonic-gate 						break;
13107c478bd9Sstevel@tonic-gate 					if (putc('\r', fout) == EOF)
13117c478bd9Sstevel@tonic-gate 						goto writer_ascii_err;
13127c478bd9Sstevel@tonic-gate 				}
13137c478bd9Sstevel@tonic-gate #ifdef notdef
13147c478bd9Sstevel@tonic-gate 				if (c == '\0') {
13157c478bd9Sstevel@tonic-gate 					bytes++;
13167c478bd9Sstevel@tonic-gate 					continue;
13177c478bd9Sstevel@tonic-gate 				}
13187c478bd9Sstevel@tonic-gate #endif
13197c478bd9Sstevel@tonic-gate 				if (c == EOF)
13207c478bd9Sstevel@tonic-gate 					goto endread;
13217c478bd9Sstevel@tonic-gate 			}
13227c478bd9Sstevel@tonic-gate 			if (putc(c, fout) == EOF)
13237c478bd9Sstevel@tonic-gate 				goto writer_ascii_err;
13247c478bd9Sstevel@tonic-gate 			bytes++;
13257c478bd9Sstevel@tonic-gate 		}
13267c478bd9Sstevel@tonic-gate endread:
13277c478bd9Sstevel@tonic-gate 		if (hash && bytes > 0) {
13287c478bd9Sstevel@tonic-gate 			if (bytes < hashbytes)
13297c478bd9Sstevel@tonic-gate 				(void) putchar('#');
13307c478bd9Sstevel@tonic-gate 			(void) putchar('\n');
13317c478bd9Sstevel@tonic-gate 			(void) fflush(stdout);
13327c478bd9Sstevel@tonic-gate 		}
13337c478bd9Sstevel@tonic-gate 		if (fdio_error(infd)) {
13347c478bd9Sstevel@tonic-gate 			errflg = 1;
13357c478bd9Sstevel@tonic-gate 			perror("netin");
13367c478bd9Sstevel@tonic-gate 		}
13377c478bd9Sstevel@tonic-gate 		if ((fflush(fout) == EOF) || ferror(fout) ||
1338*92163adaSToomas Soome 		    (fsync(fileno(fout)) == -1)) {
13397c478bd9Sstevel@tonic-gate writer_ascii_err:
13407c478bd9Sstevel@tonic-gate 			errflg = 1;
13417c478bd9Sstevel@tonic-gate 			perror(local);
13427c478bd9Sstevel@tonic-gate 		}
13437c478bd9Sstevel@tonic-gate 		break;
13447c478bd9Sstevel@tonic-gate 	}
13457c478bd9Sstevel@tonic-gate 	reset_timer();
13467c478bd9Sstevel@tonic-gate 	if (closefunc != NULL)
13477c478bd9Sstevel@tonic-gate 		(*closefunc)(fout);
13487c478bd9Sstevel@tonic-gate 	(void) signal(SIGINT, oldintr);
13497c478bd9Sstevel@tonic-gate 	if (oldintp)
13507c478bd9Sstevel@tonic-gate 		(void) signal(SIGPIPE, oldintp);
13517c478bd9Sstevel@tonic-gate 	(void) fclose(din); data = -1;
13527c478bd9Sstevel@tonic-gate 	stop = gethrtime();
13537c478bd9Sstevel@tonic-gate 	(void) getreply(0);
13547c478bd9Sstevel@tonic-gate 	if (bytes > 0 && verbose && !errflg)
13557c478bd9Sstevel@tonic-gate 		ptransfer("received", bytes, start, stop, local, remote);
13567c478bd9Sstevel@tonic-gate 	if (!ctrl_in)
13577c478bd9Sstevel@tonic-gate 		(void) printf("Lost connection\n");
13587c478bd9Sstevel@tonic-gate 	if (oldtype) {
13597c478bd9Sstevel@tonic-gate 		if (!debug)
13607c478bd9Sstevel@tonic-gate 			verbose = 0;
13617c478bd9Sstevel@tonic-gate 		switch (oldtype) {
13627c478bd9Sstevel@tonic-gate 			case TYPE_I:
13637c478bd9Sstevel@tonic-gate 				setbinary(0, NULL);
13647c478bd9Sstevel@tonic-gate 				break;
13657c478bd9Sstevel@tonic-gate 			case TYPE_E:
13667c478bd9Sstevel@tonic-gate 				setebcdic(0, NULL);
13677c478bd9Sstevel@tonic-gate 				break;
13687c478bd9Sstevel@tonic-gate 			case TYPE_L:
13697c478bd9Sstevel@tonic-gate 				settenex(0, NULL);
13707c478bd9Sstevel@tonic-gate 				break;
13717c478bd9Sstevel@tonic-gate 		}
13727c478bd9Sstevel@tonic-gate 		verbose = oldverbose;
13737c478bd9Sstevel@tonic-gate 	}
13747c478bd9Sstevel@tonic-gate 	return;
13757c478bd9Sstevel@tonic-gate abort:
13767c478bd9Sstevel@tonic-gate 
13777c478bd9Sstevel@tonic-gate /* abort using RFC959 recommended IP, SYNC sequence  */
13787c478bd9Sstevel@tonic-gate 
13797c478bd9Sstevel@tonic-gate 	stop = gethrtime();
13807c478bd9Sstevel@tonic-gate 	if (oldintp)
13817c478bd9Sstevel@tonic-gate 		(void) signal(SIGPIPE, oldintp);
13827c478bd9Sstevel@tonic-gate 	(void) signal(SIGINT, SIG_IGN);
13837c478bd9Sstevel@tonic-gate 	if (!cpend) {
13847c478bd9Sstevel@tonic-gate 		code = -1;
13857c478bd9Sstevel@tonic-gate 		(void) signal(SIGINT, oldintr);
13867c478bd9Sstevel@tonic-gate 		return;
13877c478bd9Sstevel@tonic-gate 	}
13887c478bd9Sstevel@tonic-gate 
13897c478bd9Sstevel@tonic-gate 	(void) fprintf(ctrl_out, "%c%c", IAC, IP);
13907c478bd9Sstevel@tonic-gate 	(void) fflush(ctrl_out);
13917c478bd9Sstevel@tonic-gate 	msg = (char)IAC;
13927c478bd9Sstevel@tonic-gate 	/*
13937c478bd9Sstevel@tonic-gate 	 * send IAC in urgent mode instead of DM because UNIX places oob
13947c478bd9Sstevel@tonic-gate 	 * mark after urgent byte rather than before as now is protocol
13957c478bd9Sstevel@tonic-gate 	 */
13967c478bd9Sstevel@tonic-gate 	if (send(fileno(ctrl_out), &msg, 1, MSG_OOB) != 1) {
13977c478bd9Sstevel@tonic-gate 		perror("abort");
13987c478bd9Sstevel@tonic-gate 	}
13997c478bd9Sstevel@tonic-gate 	(void) fprintf(ctrl_out, "%cABOR\r\n", DM);
14007c478bd9Sstevel@tonic-gate 	(void) fflush(ctrl_out);
14017c478bd9Sstevel@tonic-gate 	nfds = fileno(ctrl_in) + 1;
14027c478bd9Sstevel@tonic-gate 	FD_ZERO(&mask);
14037c478bd9Sstevel@tonic-gate 	FD_SET(fileno(ctrl_in), &mask);
14047c478bd9Sstevel@tonic-gate 	if (din) {
14057c478bd9Sstevel@tonic-gate 		FD_SET(fileno(din), &mask);
14067c478bd9Sstevel@tonic-gate 		nfds = MAX(fileno(din) + 1, nfds);
14077c478bd9Sstevel@tonic-gate 	}
14087c478bd9Sstevel@tonic-gate 	if ((nfnd = empty(&mask, 10, nfds)) <= 0) {
14097c478bd9Sstevel@tonic-gate 		if (nfnd < 0) {
14107c478bd9Sstevel@tonic-gate 			perror("abort");
14117c478bd9Sstevel@tonic-gate 		}
14127c478bd9Sstevel@tonic-gate 		code = -1;
14137c478bd9Sstevel@tonic-gate 		lostpeer(0);
14147c478bd9Sstevel@tonic-gate 	}
14157c478bd9Sstevel@tonic-gate 	if (din && FD_ISSET(fileno(din), &mask)) {
14167c478bd9Sstevel@tonic-gate 		do {
14177c478bd9Sstevel@tonic-gate 			reset_timer();
14187c478bd9Sstevel@tonic-gate 		} while ((c = read(fileno(din), buf, FTPBUFSIZ)) > 0);
14197c478bd9Sstevel@tonic-gate 	}
14207c478bd9Sstevel@tonic-gate 	if ((c = getreply(0)) == ERROR && code == 552) {
14217c478bd9Sstevel@tonic-gate 		/* needed for nic style abort */
14227c478bd9Sstevel@tonic-gate 		if (data >= 0) {
14237c478bd9Sstevel@tonic-gate 			(void) close(data);
14247c478bd9Sstevel@tonic-gate 			data = -1;
14257c478bd9Sstevel@tonic-gate 		}
14267c478bd9Sstevel@tonic-gate 		(void) getreply(0);
14277c478bd9Sstevel@tonic-gate 	}
14287c478bd9Sstevel@tonic-gate 	if (oldtype) {
14297c478bd9Sstevel@tonic-gate 		if (!debug)
14307c478bd9Sstevel@tonic-gate 			verbose = 0;
14317c478bd9Sstevel@tonic-gate 		switch (oldtype) {
14327c478bd9Sstevel@tonic-gate 		case TYPE_I:
14337c478bd9Sstevel@tonic-gate 			setbinary(0, NULL);
14347c478bd9Sstevel@tonic-gate 			break;
14357c478bd9Sstevel@tonic-gate 		case TYPE_E:
14367c478bd9Sstevel@tonic-gate 			setebcdic(0, NULL);
14377c478bd9Sstevel@tonic-gate 			break;
14387c478bd9Sstevel@tonic-gate 		case TYPE_L:
14397c478bd9Sstevel@tonic-gate 			settenex(0, NULL);
14407c478bd9Sstevel@tonic-gate 			break;
14417c478bd9Sstevel@tonic-gate 		}
14427c478bd9Sstevel@tonic-gate 		verbose = oldverbose;
14437c478bd9Sstevel@tonic-gate 	}
14447c478bd9Sstevel@tonic-gate 	(void) getreply(0);
14457c478bd9Sstevel@tonic-gate 	code = -1;
14467c478bd9Sstevel@tonic-gate 	if (data >= 0) {
14477c478bd9Sstevel@tonic-gate 		(void) close(data);
14487c478bd9Sstevel@tonic-gate 		data = -1;
14497c478bd9Sstevel@tonic-gate 	}
14507c478bd9Sstevel@tonic-gate 	if (closefunc != NULL && fout != NULL)
14517c478bd9Sstevel@tonic-gate 		(*closefunc)(fout);
14527c478bd9Sstevel@tonic-gate 	if (din) {
14537c478bd9Sstevel@tonic-gate 		(void) fclose(din);
14547c478bd9Sstevel@tonic-gate 		data = -1;
14557c478bd9Sstevel@tonic-gate 	}
14567c478bd9Sstevel@tonic-gate 	if (bytes > 0 && verbose)
14577c478bd9Sstevel@tonic-gate 		ptransfer("received", bytes, start, stop, local, remote);
14587c478bd9Sstevel@tonic-gate 	if (!ctrl_in)
14597c478bd9Sstevel@tonic-gate 		(void) printf("Lost connection\n");
14607c478bd9Sstevel@tonic-gate 	(void) signal(SIGINT, oldintr);
14617c478bd9Sstevel@tonic-gate }
14627c478bd9Sstevel@tonic-gate 
14637c478bd9Sstevel@tonic-gate /*
14647c478bd9Sstevel@tonic-gate  * Need to start a listen on the data channel
14657c478bd9Sstevel@tonic-gate  * before we send the command, otherwise the
14667c478bd9Sstevel@tonic-gate  * server's connect may fail.
14677c478bd9Sstevel@tonic-gate  */
14687c478bd9Sstevel@tonic-gate 
14697c478bd9Sstevel@tonic-gate static int
initconn(void)14707c478bd9Sstevel@tonic-gate initconn(void)
14717c478bd9Sstevel@tonic-gate {
14727c478bd9Sstevel@tonic-gate 	unsigned char *p, *a;
14737c478bd9Sstevel@tonic-gate 	int result, tmpno = 0;
14747c478bd9Sstevel@tonic-gate 	int on = 1;
14757c478bd9Sstevel@tonic-gate 	socklen_t len;
14767c478bd9Sstevel@tonic-gate 	int v4_addr;
14777c478bd9Sstevel@tonic-gate 	char *c, *c2, delm;
14787c478bd9Sstevel@tonic-gate 	in_port_t ports;
14797c478bd9Sstevel@tonic-gate 
14807c478bd9Sstevel@tonic-gate 	pasv_refused = B_FALSE;
14817c478bd9Sstevel@tonic-gate 	if (passivemode) {
14827c478bd9Sstevel@tonic-gate 		data = socket(AF_INET6, SOCK_STREAM, 0);
14837c478bd9Sstevel@tonic-gate 		if (data < 0) {
14847c478bd9Sstevel@tonic-gate 			perror("socket");
14857c478bd9Sstevel@tonic-gate 			return (1);
14867c478bd9Sstevel@tonic-gate 		}
14877c478bd9Sstevel@tonic-gate 		if (timeout && setsockopt(data, IPPROTO_TCP,
14887c478bd9Sstevel@tonic-gate 		    TCP_ABORT_THRESHOLD, (char *)&timeoutms,
14897c478bd9Sstevel@tonic-gate 		    sizeof (timeoutms)) < 0 && debug)
14907c478bd9Sstevel@tonic-gate 			perror("ftp: setsockopt (TCP_ABORT_THRESHOLD)");
14917c478bd9Sstevel@tonic-gate 		if ((options & SO_DEBUG) &&
14927c478bd9Sstevel@tonic-gate 		    setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on,
1493*92163adaSToomas Soome 		    sizeof (on)) < 0)
14947c478bd9Sstevel@tonic-gate 			perror("setsockopt (ignored)");
14957c478bd9Sstevel@tonic-gate 		/*
14967c478bd9Sstevel@tonic-gate 		 * Use the system wide default send and receive buffer sizes
14977c478bd9Sstevel@tonic-gate 		 * unless one has been specified.
14987c478bd9Sstevel@tonic-gate 		 */
14997c478bd9Sstevel@tonic-gate 		if (tcpwindowsize) {
15007c478bd9Sstevel@tonic-gate 			if (setsockopt(data, SOL_SOCKET, SO_SNDBUF,
15017c478bd9Sstevel@tonic-gate 			    (char *)&tcpwindowsize, sizeof (tcpwindowsize)) < 0)
15027c478bd9Sstevel@tonic-gate 				perror("ftp: setsockopt (SO_SNDBUF - ignored)");
15037c478bd9Sstevel@tonic-gate 			if (setsockopt(data, SOL_SOCKET, SO_RCVBUF,
15047c478bd9Sstevel@tonic-gate 			    (char *)&tcpwindowsize, sizeof (tcpwindowsize)) < 0)
15057c478bd9Sstevel@tonic-gate 				perror("ftp: setsockopt (SO_RCVBUF - ignored)");
15067c478bd9Sstevel@tonic-gate 		}
15077c478bd9Sstevel@tonic-gate 
15087c478bd9Sstevel@tonic-gate 		data_addr = remctladdr;
15097c478bd9Sstevel@tonic-gate 
15107c478bd9Sstevel@tonic-gate 		if (ipv6rem == B_TRUE) {
15117c478bd9Sstevel@tonic-gate 			if (command("EPSV") != COMPLETE) {
15127c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
1513*92163adaSToomas Soome 				    "Passive mode refused. Try EPRT\n");
15147c478bd9Sstevel@tonic-gate 				pasv_refused = B_TRUE;
15157c478bd9Sstevel@tonic-gate 				goto noport;
15167c478bd9Sstevel@tonic-gate 			}
15177c478bd9Sstevel@tonic-gate 
15187c478bd9Sstevel@tonic-gate 			/*
15197c478bd9Sstevel@tonic-gate 			 * Get the data port from reply string from the
15207c478bd9Sstevel@tonic-gate 			 * server.  The format of the reply string is:
15217c478bd9Sstevel@tonic-gate 			 * 229 Entering Extended Passive Mode (|||port|)
15227c478bd9Sstevel@tonic-gate 			 * where | is the delimiter being used.
15237c478bd9Sstevel@tonic-gate 			 */
15247c478bd9Sstevel@tonic-gate 			c = strchr(reply_string, '(');
15257c478bd9Sstevel@tonic-gate 			c2 = strchr(reply_string, ')');
15267c478bd9Sstevel@tonic-gate 			if (c == NULL || c2 == NULL) {
15277c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr, "Extended passive mode"
15287c478bd9Sstevel@tonic-gate 				    "parsing failure.\n");
15297c478bd9Sstevel@tonic-gate 				goto bad;
15307c478bd9Sstevel@tonic-gate 			}
153195c74518SToomas Soome 			*(c2 - 1) = '\0';
15327c478bd9Sstevel@tonic-gate 			/* Delimiter is the next char in the reply string */
15337c478bd9Sstevel@tonic-gate 			delm = *(++c);
15347c478bd9Sstevel@tonic-gate 			while (*c == delm) {
15357c478bd9Sstevel@tonic-gate 				if (!*(c++)) {
15367c478bd9Sstevel@tonic-gate 					(void) fprintf(stderr,
15377c478bd9Sstevel@tonic-gate 					    "Extended passive mode"
15387c478bd9Sstevel@tonic-gate 					    "parsing failure.\n");
15397c478bd9Sstevel@tonic-gate 					goto bad;
15407c478bd9Sstevel@tonic-gate 				}
15417c478bd9Sstevel@tonic-gate 			}
15427c478bd9Sstevel@tonic-gate 			/* assign the port for data connection */
15437c478bd9Sstevel@tonic-gate 			ports = (in_port_t)atoi(c);
15447c478bd9Sstevel@tonic-gate 			data_addr.sin6_port =  htons(ports);
15457c478bd9Sstevel@tonic-gate 		} else {
15467c478bd9Sstevel@tonic-gate 			int a1, a2, a3, a4, p1, p2;
15477c478bd9Sstevel@tonic-gate 
15487c478bd9Sstevel@tonic-gate 			if (command("PASV") != COMPLETE) {
15497c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
1550*92163adaSToomas Soome 				    "Passive mode refused. Try PORT\n");
15517c478bd9Sstevel@tonic-gate 				pasv_refused = B_TRUE;
15527c478bd9Sstevel@tonic-gate 				goto noport;
15537c478bd9Sstevel@tonic-gate 			}
15547c478bd9Sstevel@tonic-gate 
15557c478bd9Sstevel@tonic-gate 			/*
15567c478bd9Sstevel@tonic-gate 			 * Get the data port from reply string from the
15577c478bd9Sstevel@tonic-gate 			 * server.  The format of the reply string is:
15587c478bd9Sstevel@tonic-gate 			 * 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)
15597c478bd9Sstevel@tonic-gate 			 */
15607c478bd9Sstevel@tonic-gate 			if (sscanf(pasv, "%d,%d,%d,%d,%d,%d",
1561*92163adaSToomas Soome 			    &a1, &a2, &a3, &a4, &p1, &p2) != 6) {
15627c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
1563*92163adaSToomas Soome 				    "Passive mode parsing failure.\n");
15647c478bd9Sstevel@tonic-gate 				goto bad;
15657c478bd9Sstevel@tonic-gate 			}
15667c478bd9Sstevel@tonic-gate 			/*
15677c478bd9Sstevel@tonic-gate 			 * Set the supplied address and port in an
15687c478bd9Sstevel@tonic-gate 			 * IPv4-mapped IPv6 address.
15697c478bd9Sstevel@tonic-gate 			 */
15707c478bd9Sstevel@tonic-gate 			a = (unsigned char *)&data_addr.sin6_addr +
1571*92163adaSToomas Soome 			    sizeof (struct in6_addr) -
1572*92163adaSToomas Soome 			    sizeof (struct in_addr);
15737c478bd9Sstevel@tonic-gate #define	UC(b)	((b)&0xff)
15747c478bd9Sstevel@tonic-gate 			a[0] = UC(a1);
15757c478bd9Sstevel@tonic-gate 			a[1] = UC(a2);
15767c478bd9Sstevel@tonic-gate 			a[2] = UC(a3);
15777c478bd9Sstevel@tonic-gate 			a[3] = UC(a4);
15787c478bd9Sstevel@tonic-gate 			p = (unsigned char *)&data_addr.sin6_port;
15797c478bd9Sstevel@tonic-gate 			p[0] = UC(p1);
15807c478bd9Sstevel@tonic-gate 			p[1] = UC(p2);
15817c478bd9Sstevel@tonic-gate 		}
15827c478bd9Sstevel@tonic-gate 
15837c478bd9Sstevel@tonic-gate 		if (connect(data, (struct sockaddr *)&data_addr,
15847c478bd9Sstevel@tonic-gate 		    sizeof (data_addr)) < 0) {
15857c478bd9Sstevel@tonic-gate 			perror("connect");
15867c478bd9Sstevel@tonic-gate 			goto bad;
15877c478bd9Sstevel@tonic-gate 		}
15887c478bd9Sstevel@tonic-gate 		return (0);
15897c478bd9Sstevel@tonic-gate 	}
15907c478bd9Sstevel@tonic-gate 
15917c478bd9Sstevel@tonic-gate noport:
15927c478bd9Sstevel@tonic-gate 	data_addr = myctladdr;
15937c478bd9Sstevel@tonic-gate 	if (sendport)
15947c478bd9Sstevel@tonic-gate 		data_addr.sin6_port = 0;	/* let system pick one */
15957c478bd9Sstevel@tonic-gate 
15967c478bd9Sstevel@tonic-gate 	if (data != -1)
15977c478bd9Sstevel@tonic-gate 		(void) close(data);
15987c478bd9Sstevel@tonic-gate 	data = socket(AF_INET6, SOCK_STREAM, 0);
15997c478bd9Sstevel@tonic-gate 	if (data < 0) {
16007c478bd9Sstevel@tonic-gate 		perror("ftp: socket");
16017c478bd9Sstevel@tonic-gate 		if (tmpno)
16027c478bd9Sstevel@tonic-gate 			sendport = 1;
16037c478bd9Sstevel@tonic-gate 		return (1);
16047c478bd9Sstevel@tonic-gate 	}
16057c478bd9Sstevel@tonic-gate 	if (!sendport)
16067c478bd9Sstevel@tonic-gate 		if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR,
16077c478bd9Sstevel@tonic-gate 		    (char *)&on, sizeof (on)) < 0) {
16087c478bd9Sstevel@tonic-gate 			perror("ftp: setsockopt (SO_REUSEADDR)");
16097c478bd9Sstevel@tonic-gate 			goto bad;
16107c478bd9Sstevel@tonic-gate 		}
16117c478bd9Sstevel@tonic-gate 	if (bind(data,
16127c478bd9Sstevel@tonic-gate 	    (struct sockaddr *)&data_addr, sizeof (data_addr)) < 0) {
16137c478bd9Sstevel@tonic-gate 		perror("ftp: bind");
16147c478bd9Sstevel@tonic-gate 		goto bad;
16157c478bd9Sstevel@tonic-gate 	}
16167c478bd9Sstevel@tonic-gate 	if (timeout && setsockopt(data, IPPROTO_TCP, TCP_ABORT_THRESHOLD,
16177c478bd9Sstevel@tonic-gate 	    (char *)&timeoutms, sizeof (timeoutms)) < 0 && debug)
16187c478bd9Sstevel@tonic-gate 		perror("ftp: setsockopt (TCP_ABORT_THRESHOLD)");
16197c478bd9Sstevel@tonic-gate 	if (options & SO_DEBUG &&
16207c478bd9Sstevel@tonic-gate 	    setsockopt(data, SOL_SOCKET, SO_DEBUG,
16217c478bd9Sstevel@tonic-gate 	    (char *)&on, sizeof (on)) < 0)
16227c478bd9Sstevel@tonic-gate 		perror("ftp: setsockopt (SO_DEBUG - ignored)");
16237c478bd9Sstevel@tonic-gate 	/*
16247c478bd9Sstevel@tonic-gate 	 * Use the system wide default send and receive buffer sizes unless
16257c478bd9Sstevel@tonic-gate 	 * one has been specified.
16267c478bd9Sstevel@tonic-gate 	 */
16277c478bd9Sstevel@tonic-gate 	if (tcpwindowsize) {
16287c478bd9Sstevel@tonic-gate 		if (setsockopt(data, SOL_SOCKET, SO_SNDBUF,
16297c478bd9Sstevel@tonic-gate 		    (char *)&tcpwindowsize, sizeof (tcpwindowsize)) < 0)
16307c478bd9Sstevel@tonic-gate 			perror("ftp: setsockopt (SO_SNDBUF - ignored)");
16317c478bd9Sstevel@tonic-gate 		if (setsockopt(data, SOL_SOCKET, SO_RCVBUF,
16327c478bd9Sstevel@tonic-gate 		    (char *)&tcpwindowsize, sizeof (tcpwindowsize)) < 0)
16337c478bd9Sstevel@tonic-gate 			perror("ftp: setsockopt (SO_RCVBUF - ignored)");
16347c478bd9Sstevel@tonic-gate 	}
16357c478bd9Sstevel@tonic-gate 	len = sizeof (data_addr);
16367c478bd9Sstevel@tonic-gate 	if (getsockname(data, (struct sockaddr *)&data_addr, &len) < 0) {
16377c478bd9Sstevel@tonic-gate 		perror("ftp: getsockname");
16387c478bd9Sstevel@tonic-gate 		goto bad;
16397c478bd9Sstevel@tonic-gate 	}
16407c478bd9Sstevel@tonic-gate 
16417c478bd9Sstevel@tonic-gate 	v4_addr = IN6_IS_ADDR_V4MAPPED(&data_addr.sin6_addr);
16427c478bd9Sstevel@tonic-gate 	if (listen(data, 1) < 0)
16437c478bd9Sstevel@tonic-gate 		perror("ftp: listen");
16447c478bd9Sstevel@tonic-gate 
16457c478bd9Sstevel@tonic-gate 	if (sendport) {
16467c478bd9Sstevel@tonic-gate 		a = (unsigned char *)&data_addr.sin6_addr;
16477c478bd9Sstevel@tonic-gate 		p = (unsigned char *)&data_addr.sin6_port;
16487c478bd9Sstevel@tonic-gate 		if (v4_addr) {
16497c478bd9Sstevel@tonic-gate 			result =
16507c478bd9Sstevel@tonic-gate 			    command("PORT %d,%d,%d,%d,%d,%d",
16517c478bd9Sstevel@tonic-gate 			    UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
16527c478bd9Sstevel@tonic-gate 			    UC(p[0]), UC(p[1]));
16537c478bd9Sstevel@tonic-gate 		} else {
16547c478bd9Sstevel@tonic-gate 			char hname[INET6_ADDRSTRLEN];
16557c478bd9Sstevel@tonic-gate 
16567c478bd9Sstevel@tonic-gate 			result = COMPLETE + 1;
16577c478bd9Sstevel@tonic-gate 			/*
16587c478bd9Sstevel@tonic-gate 			 * if on previous try to server, it was
16597c478bd9Sstevel@tonic-gate 			 * determined that the server doesn't support
16607c478bd9Sstevel@tonic-gate 			 * EPRT, don't bother trying again.  Just try
16617c478bd9Sstevel@tonic-gate 			 * LPRT.
16627c478bd9Sstevel@tonic-gate 			 */
16637c478bd9Sstevel@tonic-gate 			if (eport_supported == B_TRUE) {
16647c478bd9Sstevel@tonic-gate 				if (inet_ntop(AF_INET6, &data_addr.sin6_addr,
16657c478bd9Sstevel@tonic-gate 				    hname, sizeof (hname)) != NULL) {
16667c478bd9Sstevel@tonic-gate 					result = command("EPRT |%d|%s|%d|", 2,
16677c478bd9Sstevel@tonic-gate 					    hname, htons(data_addr.sin6_port));
16687c478bd9Sstevel@tonic-gate 					if (result != COMPLETE)
16697c478bd9Sstevel@tonic-gate 						eport_supported = B_FALSE;
1670*92163adaSToomas Soome 				}
16717c478bd9Sstevel@tonic-gate 			}
16727c478bd9Sstevel@tonic-gate 			/* Try LPRT */
16737c478bd9Sstevel@tonic-gate 			if (result != COMPLETE) {
16747c478bd9Sstevel@tonic-gate 				result = command(
1675*92163adaSToomas Soome 				    "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,"
1676*92163adaSToomas Soome 				    "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
1677*92163adaSToomas Soome 				    6, 16,
1678*92163adaSToomas Soome 				    UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
1679*92163adaSToomas Soome 				    UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
1680*92163adaSToomas Soome 				    UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
1681*92163adaSToomas Soome 				    UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
1682*92163adaSToomas Soome 				    2, UC(p[0]), UC(p[1]));
16837c478bd9Sstevel@tonic-gate 			}
16847c478bd9Sstevel@tonic-gate 		}
16857c478bd9Sstevel@tonic-gate 
16867c478bd9Sstevel@tonic-gate 		if (result == ERROR && sendport == -1) {
16877c478bd9Sstevel@tonic-gate 			sendport = 0;
16887c478bd9Sstevel@tonic-gate 			tmpno = 1;
16897c478bd9Sstevel@tonic-gate 			goto noport;
16907c478bd9Sstevel@tonic-gate 		}
16917c478bd9Sstevel@tonic-gate 		return (result != COMPLETE);
16927c478bd9Sstevel@tonic-gate 	}
16937c478bd9Sstevel@tonic-gate 	if (tmpno)
16947c478bd9Sstevel@tonic-gate 		sendport = 1;
16957c478bd9Sstevel@tonic-gate 	return (0);
16967c478bd9Sstevel@tonic-gate bad:
16977c478bd9Sstevel@tonic-gate 	(void) close(data), data = -1;
16987c478bd9Sstevel@tonic-gate 	if (tmpno)
16997c478bd9Sstevel@tonic-gate 		sendport = 1;
17007c478bd9Sstevel@tonic-gate 	return (1);
17017c478bd9Sstevel@tonic-gate }
17027c478bd9Sstevel@tonic-gate 
17037c478bd9Sstevel@tonic-gate static FILE *
dataconn(char * mode)17047c478bd9Sstevel@tonic-gate dataconn(char *mode)
17057c478bd9Sstevel@tonic-gate {
17067c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 from;
17077c478bd9Sstevel@tonic-gate 	int s;
17087c478bd9Sstevel@tonic-gate 	socklen_t fromlen = sizeof (from);
17097c478bd9Sstevel@tonic-gate 
17107c478bd9Sstevel@tonic-gate 	reset_timer();
17117c478bd9Sstevel@tonic-gate 	if (passivemode && !pasv_refused)
17127c478bd9Sstevel@tonic-gate 		return (fdopen(data, mode));
17137c478bd9Sstevel@tonic-gate 
17147c478bd9Sstevel@tonic-gate 	s = accept(data, (struct sockaddr *)&from, &fromlen);
17157c478bd9Sstevel@tonic-gate 	if (s < 0) {
17167c478bd9Sstevel@tonic-gate 		perror("ftp: accept");
17177c478bd9Sstevel@tonic-gate 		(void) close(data), data = -1;
17187c478bd9Sstevel@tonic-gate 		return (NULL);
17197c478bd9Sstevel@tonic-gate 	}
17207c478bd9Sstevel@tonic-gate 	(void) close(data);
17217c478bd9Sstevel@tonic-gate 	data = s;
17227c478bd9Sstevel@tonic-gate 	return (fdopen(data, mode));
17237c478bd9Sstevel@tonic-gate }
17247c478bd9Sstevel@tonic-gate 
17257c478bd9Sstevel@tonic-gate static void
ptransfer(char * direction,off_t bytes,hrtime_t t0,hrtime_t t1,char * local,char * remote)17267c478bd9Sstevel@tonic-gate ptransfer(char *direction, off_t bytes, hrtime_t t0,
17277c478bd9Sstevel@tonic-gate     hrtime_t t1, char *local, char *remote)
17287c478bd9Sstevel@tonic-gate {
17297c478bd9Sstevel@tonic-gate 	hrtime_t td; /* nanoseconds in a 64 bit int */
17307c478bd9Sstevel@tonic-gate 	double s, bs;
17317c478bd9Sstevel@tonic-gate 
17327c478bd9Sstevel@tonic-gate 	td = t1 - t0;
17337c478bd9Sstevel@tonic-gate 	s = (double)td / 1000000000.0; /* seconds */
17347c478bd9Sstevel@tonic-gate 	bs = (double)bytes / NONZERO(s);
17357c478bd9Sstevel@tonic-gate 	if (local && *local != '-')
17367c478bd9Sstevel@tonic-gate 		(void) printf("local: %s ", local);
17377c478bd9Sstevel@tonic-gate 	if (remote)
17387c478bd9Sstevel@tonic-gate 		(void) printf("remote: %s\n", remote);
17397c478bd9Sstevel@tonic-gate 	(void) printf("%lld bytes %s in %.2g seconds (%.2f Kbytes/s)\n",
1740*92163adaSToomas Soome 	    (longlong_t)bytes, direction, s, bs / 1024.0);
17417c478bd9Sstevel@tonic-gate }
17427c478bd9Sstevel@tonic-gate 
17437c478bd9Sstevel@tonic-gate /*ARGSUSED*/
17447c478bd9Sstevel@tonic-gate static void
psabort(int sig)17457c478bd9Sstevel@tonic-gate psabort(int sig)
17467c478bd9Sstevel@tonic-gate {
17477c478bd9Sstevel@tonic-gate 	abrtflag++;
17487c478bd9Sstevel@tonic-gate }
17497c478bd9Sstevel@tonic-gate 
17507c478bd9Sstevel@tonic-gate void
pswitch(int flag)17517c478bd9Sstevel@tonic-gate pswitch(int flag)
17527c478bd9Sstevel@tonic-gate {
17537c478bd9Sstevel@tonic-gate 	void (*oldintr)();
17547c478bd9Sstevel@tonic-gate 	static struct comvars {
17557c478bd9Sstevel@tonic-gate 		int connect;
17567c478bd9Sstevel@tonic-gate 		char name[MAXHOSTNAMELEN];
17577c478bd9Sstevel@tonic-gate 		struct sockaddr_in6 mctl;
17587c478bd9Sstevel@tonic-gate 		struct sockaddr_in6 hctl;
17597c478bd9Sstevel@tonic-gate 		FILE *in;
17607c478bd9Sstevel@tonic-gate 		FILE *out;
17617c478bd9Sstevel@tonic-gate 		int tpe;
17627c478bd9Sstevel@tonic-gate 		int cpnd;
17637c478bd9Sstevel@tonic-gate 		int sunqe;
17647c478bd9Sstevel@tonic-gate 		int runqe;
17657c478bd9Sstevel@tonic-gate 		int mcse;
17667c478bd9Sstevel@tonic-gate 		int ntflg;
17677c478bd9Sstevel@tonic-gate 		char nti[17];
17687c478bd9Sstevel@tonic-gate 		char nto[17];
17697c478bd9Sstevel@tonic-gate 		int mapflg;
17707c478bd9Sstevel@tonic-gate 		char mi[MAXPATHLEN];
17717c478bd9Sstevel@tonic-gate 		char mo[MAXPATHLEN];
17727c478bd9Sstevel@tonic-gate 		int authtype;
17737c478bd9Sstevel@tonic-gate 		int clvl;
17747c478bd9Sstevel@tonic-gate 		int dlvl;
17757c478bd9Sstevel@tonic-gate 		} proxstruct, tmpstruct;
17767c478bd9Sstevel@tonic-gate 	struct comvars *ip, *op;
17777c478bd9Sstevel@tonic-gate 
17787c478bd9Sstevel@tonic-gate 	abrtflag = 0;
17797c478bd9Sstevel@tonic-gate 	oldintr = signal(SIGINT, psabort);
17807c478bd9Sstevel@tonic-gate 	if (flag) {
17817c478bd9Sstevel@tonic-gate 		if (proxy)
17827c478bd9Sstevel@tonic-gate 			return;
17837c478bd9Sstevel@tonic-gate 		ip = &tmpstruct;
17847c478bd9Sstevel@tonic-gate 		op = &proxstruct;
17857c478bd9Sstevel@tonic-gate 		proxy++;
17867c478bd9Sstevel@tonic-gate 	} else {
17877c478bd9Sstevel@tonic-gate 		if (!proxy)
17887c478bd9Sstevel@tonic-gate 			return;
17897c478bd9Sstevel@tonic-gate 		ip = &proxstruct;
17907c478bd9Sstevel@tonic-gate 		op = &tmpstruct;
17917c478bd9Sstevel@tonic-gate 		proxy = 0;
17927c478bd9Sstevel@tonic-gate 	}
17937c478bd9Sstevel@tonic-gate 	ip->connect = connected;
17947c478bd9Sstevel@tonic-gate 	connected = op->connect;
17957c478bd9Sstevel@tonic-gate 	if (hostname)
17967c478bd9Sstevel@tonic-gate 		(void) strlcpy(ip->name, hostname, sizeof (ip->name));
17977c478bd9Sstevel@tonic-gate 	else
17987c478bd9Sstevel@tonic-gate 		ip->name[0] = 0;
17997c478bd9Sstevel@tonic-gate 	hostname = op->name;
18007c478bd9Sstevel@tonic-gate 	ip->hctl = remctladdr;
18017c478bd9Sstevel@tonic-gate 	remctladdr = op->hctl;
18027c478bd9Sstevel@tonic-gate 	ip->mctl = myctladdr;
18037c478bd9Sstevel@tonic-gate 	myctladdr = op->mctl;
18047c478bd9Sstevel@tonic-gate 	ip->in = ctrl_in;
18057c478bd9Sstevel@tonic-gate 	ctrl_in = op->in;
18067c478bd9Sstevel@tonic-gate 	ip->out = ctrl_out;
18077c478bd9Sstevel@tonic-gate 	ctrl_out = op->out;
18087c478bd9Sstevel@tonic-gate 	ip->tpe = type;
18097c478bd9Sstevel@tonic-gate 	type = op->tpe;
18107c478bd9Sstevel@tonic-gate 	if (!type)
18117c478bd9Sstevel@tonic-gate 		type = 1;
18127c478bd9Sstevel@tonic-gate 	ip->cpnd = cpend;
18137c478bd9Sstevel@tonic-gate 	cpend = op->cpnd;
18147c478bd9Sstevel@tonic-gate 	ip->sunqe = sunique;
18157c478bd9Sstevel@tonic-gate 	sunique = op->sunqe;
18167c478bd9Sstevel@tonic-gate 	ip->runqe = runique;
18177c478bd9Sstevel@tonic-gate 	runique = op->runqe;
18187c478bd9Sstevel@tonic-gate 	ip->mcse = mcase;
18197c478bd9Sstevel@tonic-gate 	mcase = op->mcse;
18207c478bd9Sstevel@tonic-gate 	ip->ntflg = ntflag;
18217c478bd9Sstevel@tonic-gate 	ntflag = op->ntflg;
18227c478bd9Sstevel@tonic-gate 	(void) strlcpy(ip->nti, ntin, sizeof (ip->nti));
18237c478bd9Sstevel@tonic-gate 	(void) strlcpy(ntin, op->nti, sizeof (ntin));
18247c478bd9Sstevel@tonic-gate 	(void) strlcpy(ip->nto, ntout, sizeof (ip->nto));
18257c478bd9Sstevel@tonic-gate 	(void) strlcpy(ntout, op->nto, sizeof (ntout));
18267c478bd9Sstevel@tonic-gate 	ip->mapflg = mapflag;
18277c478bd9Sstevel@tonic-gate 	mapflag = op->mapflg;
18287c478bd9Sstevel@tonic-gate 	(void) strlcpy(ip->mi, mapin, sizeof (ip->mi));
18297c478bd9Sstevel@tonic-gate 	(void) strlcpy(mapin, op->mi, sizeof (mapin));
18307c478bd9Sstevel@tonic-gate 	(void) strlcpy(ip->mo, mapout, sizeof (ip->mo));
18317c478bd9Sstevel@tonic-gate 	(void) strlcpy(mapout, op->mo, sizeof (mapout));
18327c478bd9Sstevel@tonic-gate 
18337c478bd9Sstevel@tonic-gate 	ip->authtype = auth_type;
18347c478bd9Sstevel@tonic-gate 	auth_type = op->authtype;
18357c478bd9Sstevel@tonic-gate 	ip->clvl = clevel;
18367c478bd9Sstevel@tonic-gate 	clevel = op->clvl;
18377c478bd9Sstevel@tonic-gate 	ip->dlvl = dlevel;
18387c478bd9Sstevel@tonic-gate 	dlevel = op->dlvl;
18397c478bd9Sstevel@tonic-gate 	if (!clevel)
18407c478bd9Sstevel@tonic-gate 		clevel = PROT_C;
18417c478bd9Sstevel@tonic-gate 	if (!dlevel)
18427c478bd9Sstevel@tonic-gate 		dlevel = PROT_C;
18437c478bd9Sstevel@tonic-gate 
18447c478bd9Sstevel@tonic-gate 	(void) signal(SIGINT, oldintr);
18457c478bd9Sstevel@tonic-gate 	if (abrtflag) {
18467c478bd9Sstevel@tonic-gate 		abrtflag = 0;
18477c478bd9Sstevel@tonic-gate 		(*oldintr)();
18487c478bd9Sstevel@tonic-gate 	}
18497c478bd9Sstevel@tonic-gate }
18507c478bd9Sstevel@tonic-gate 
18517c478bd9Sstevel@tonic-gate /*ARGSUSED*/
18527c478bd9Sstevel@tonic-gate static void
abortpt(int sig)18537c478bd9Sstevel@tonic-gate abortpt(int sig)
18547c478bd9Sstevel@tonic-gate {
18557c478bd9Sstevel@tonic-gate 	(void) printf("\n");
18567c478bd9Sstevel@tonic-gate 	(void) fflush(stdout);
18577c478bd9Sstevel@tonic-gate 	ptabflg++;
18587c478bd9Sstevel@tonic-gate 	mflag = 0;
18597c478bd9Sstevel@tonic-gate 	abrtflag = 0;
18607c478bd9Sstevel@tonic-gate 	longjmp(ptabort, 1);
18617c478bd9Sstevel@tonic-gate }
18627c478bd9Sstevel@tonic-gate 
18637c478bd9Sstevel@tonic-gate static void
proxtrans(char * cmd,char * local,char * remote)18647c478bd9Sstevel@tonic-gate proxtrans(char *cmd, char *local, char *remote)
18657c478bd9Sstevel@tonic-gate {
18667c478bd9Sstevel@tonic-gate 	void (*oldintr)();
18677c478bd9Sstevel@tonic-gate 	int tmptype, oldtype = 0, secndflag = 0, nfnd;
18687c478bd9Sstevel@tonic-gate 	extern jmp_buf ptabort;
18697c478bd9Sstevel@tonic-gate 	char *cmd2;
18707c478bd9Sstevel@tonic-gate 	struct fd_set mask;
18717c478bd9Sstevel@tonic-gate 	int ipv4_addr = IN6_IS_ADDR_V4MAPPED(&remctladdr.sin6_addr);
18727c478bd9Sstevel@tonic-gate 
18737c478bd9Sstevel@tonic-gate 	if (strcmp(cmd, "RETR"))
18747c478bd9Sstevel@tonic-gate 		cmd2 = "RETR";
18757c478bd9Sstevel@tonic-gate 	else
18767c478bd9Sstevel@tonic-gate 		cmd2 = runique ? "STOU" : "STOR";
18777c478bd9Sstevel@tonic-gate 	if (command(ipv4_addr ? "PASV" : "EPSV") != COMPLETE) {
18787c478bd9Sstevel@tonic-gate 		(void) printf(
18797c478bd9Sstevel@tonic-gate 		    "proxy server does not support third part transfers.\n");
18807c478bd9Sstevel@tonic-gate 		return;
18817c478bd9Sstevel@tonic-gate 	}
18827c478bd9Sstevel@tonic-gate 	tmptype = type;
18837c478bd9Sstevel@tonic-gate 	pswitch(0);
18847c478bd9Sstevel@tonic-gate 	if (!connected) {
18857c478bd9Sstevel@tonic-gate 		(void) printf("No primary connection\n");
18867c478bd9Sstevel@tonic-gate 		pswitch(1);
18877c478bd9Sstevel@tonic-gate 		code = -1;
18887c478bd9Sstevel@tonic-gate 		return;
18897c478bd9Sstevel@tonic-gate 	}
18907c478bd9Sstevel@tonic-gate 	if (type != tmptype) {
18917c478bd9Sstevel@tonic-gate 		oldtype = type;
18927c478bd9Sstevel@tonic-gate 		switch (tmptype) {
18937c478bd9Sstevel@tonic-gate 			case TYPE_A:
18947c478bd9Sstevel@tonic-gate 				setascii(0, NULL);
18957c478bd9Sstevel@tonic-gate 				break;
18967c478bd9Sstevel@tonic-gate 			case TYPE_I:
18977c478bd9Sstevel@tonic-gate 				setbinary(0, NULL);
18987c478bd9Sstevel@tonic-gate 				break;
18997c478bd9Sstevel@tonic-gate 			case TYPE_E:
19007c478bd9Sstevel@tonic-gate 				setebcdic(0, NULL);
19017c478bd9Sstevel@tonic-gate 				break;
19027c478bd9Sstevel@tonic-gate 			case TYPE_L:
19037c478bd9Sstevel@tonic-gate 				settenex(0, NULL);
19047c478bd9Sstevel@tonic-gate 				break;
19057c478bd9Sstevel@tonic-gate 		}
19067c478bd9Sstevel@tonic-gate 	}
19077c478bd9Sstevel@tonic-gate 	if (command(ipv4_addr ? "PORT %s" : "EPRT %s", pasv) != COMPLETE) {
19087c478bd9Sstevel@tonic-gate 		switch (oldtype) {
19097c478bd9Sstevel@tonic-gate 			case 0:
19107c478bd9Sstevel@tonic-gate 				break;
19117c478bd9Sstevel@tonic-gate 			case TYPE_A:
19127c478bd9Sstevel@tonic-gate 				setascii(0, NULL);
19137c478bd9Sstevel@tonic-gate 				break;
19147c478bd9Sstevel@tonic-gate 			case TYPE_I:
19157c478bd9Sstevel@tonic-gate 				setbinary(0, NULL);
19167c478bd9Sstevel@tonic-gate 				break;
19177c478bd9Sstevel@tonic-gate 			case TYPE_E:
19187c478bd9Sstevel@tonic-gate 				setebcdic(0, NULL);
19197c478bd9Sstevel@tonic-gate 				break;
19207c478bd9Sstevel@tonic-gate 			case TYPE_L:
19217c478bd9Sstevel@tonic-gate 				settenex(0, NULL);
19227c478bd9Sstevel@tonic-gate 				break;
19237c478bd9Sstevel@tonic-gate 		}
19247c478bd9Sstevel@tonic-gate 		pswitch(1);
19257c478bd9Sstevel@tonic-gate 		return;
19267c478bd9Sstevel@tonic-gate 	}
19277c478bd9Sstevel@tonic-gate 	if (setjmp(ptabort))
19287c478bd9Sstevel@tonic-gate 		goto abort;
19297c478bd9Sstevel@tonic-gate 	oldintr = signal(SIGINT, (void (*)())abortpt);
19307c478bd9Sstevel@tonic-gate 	if (command("%s %s", cmd, remote) != PRELIM) {
19317c478bd9Sstevel@tonic-gate 		(void) signal(SIGINT, oldintr);
19327c478bd9Sstevel@tonic-gate 		switch (oldtype) {
19337c478bd9Sstevel@tonic-gate 			case 0:
19347c478bd9Sstevel@tonic-gate 				break;
19357c478bd9Sstevel@tonic-gate 			case TYPE_A:
19367c478bd9Sstevel@tonic-gate 				setascii(0, NULL);
19377c478bd9Sstevel@tonic-gate 				break;
19387c478bd9Sstevel@tonic-gate 			case TYPE_I:
19397c478bd9Sstevel@tonic-gate 				setbinary(0, NULL);
19407c478bd9Sstevel@tonic-gate 				break;
19417c478bd9Sstevel@tonic-gate 			case TYPE_E:
19427c478bd9Sstevel@tonic-gate 				setebcdic(0, NULL);
19437c478bd9Sstevel@tonic-gate 				break;
19447c478bd9Sstevel@tonic-gate 			case TYPE_L:
19457c478bd9Sstevel@tonic-gate 				settenex(0, NULL);
19467c478bd9Sstevel@tonic-gate 				break;
19477c478bd9Sstevel@tonic-gate 		}
19487c478bd9Sstevel@tonic-gate 		pswitch(1);
19497c478bd9Sstevel@tonic-gate 		return;
19507c478bd9Sstevel@tonic-gate 	}
19517c478bd9Sstevel@tonic-gate 	(void) sleep(2);
19527c478bd9Sstevel@tonic-gate 	pswitch(1);
19537c478bd9Sstevel@tonic-gate 	secndflag++;
19547c478bd9Sstevel@tonic-gate 	if (command("%s %s", cmd2, local) != PRELIM)
19557c478bd9Sstevel@tonic-gate 		goto abort;
19567c478bd9Sstevel@tonic-gate 	ptflag++;
19577c478bd9Sstevel@tonic-gate 	(void) getreply(0);
19587c478bd9Sstevel@tonic-gate 	pswitch(0);
19597c478bd9Sstevel@tonic-gate 	(void) getreply(0);
19607c478bd9Sstevel@tonic-gate 	(void) signal(SIGINT, oldintr);
19617c478bd9Sstevel@tonic-gate 	switch (oldtype) {
19627c478bd9Sstevel@tonic-gate 		case 0:
19637c478bd9Sstevel@tonic-gate 			break;
19647c478bd9Sstevel@tonic-gate 		case TYPE_A:
19657c478bd9Sstevel@tonic-gate 			setascii(0, NULL);
19667c478bd9Sstevel@tonic-gate 			break;
19677c478bd9Sstevel@tonic-gate 		case TYPE_I:
19687c478bd9Sstevel@tonic-gate 			setbinary(0, NULL);
19697c478bd9Sstevel@tonic-gate 			break;
19707c478bd9Sstevel@tonic-gate 		case TYPE_E:
19717c478bd9Sstevel@tonic-gate 			setebcdic(0, NULL);
19727c478bd9Sstevel@tonic-gate 			break;
19737c478bd9Sstevel@tonic-gate 		case TYPE_L:
19747c478bd9Sstevel@tonic-gate 			settenex(0, NULL);
19757c478bd9Sstevel@tonic-gate 			break;
19767c478bd9Sstevel@tonic-gate 	}
19777c478bd9Sstevel@tonic-gate 	pswitch(1);
19787c478bd9Sstevel@tonic-gate 	ptflag = 0;
19797c478bd9Sstevel@tonic-gate 	(void) printf("local: %s remote: %s\n", local, remote);
19807c478bd9Sstevel@tonic-gate 	return;
19817c478bd9Sstevel@tonic-gate abort:
19827c478bd9Sstevel@tonic-gate 	(void) signal(SIGINT, SIG_IGN);
19837c478bd9Sstevel@tonic-gate 	ptflag = 0;
19847c478bd9Sstevel@tonic-gate 	if (strcmp(cmd, "RETR") && !proxy)
19857c478bd9Sstevel@tonic-gate 		pswitch(1);
19867c478bd9Sstevel@tonic-gate 	else if ((strcmp(cmd, "RETR") == 0) && proxy)
19877c478bd9Sstevel@tonic-gate 		pswitch(0);
19887c478bd9Sstevel@tonic-gate 	if (!cpend && !secndflag) {  /* only here if cmd = "STOR" (proxy=1) */
19897c478bd9Sstevel@tonic-gate 		if (command("%s %s", cmd2, local) != PRELIM) {
19907c478bd9Sstevel@tonic-gate 			pswitch(0);
19917c478bd9Sstevel@tonic-gate 			switch (oldtype) {
19927c478bd9Sstevel@tonic-gate 				case 0:
19937c478bd9Sstevel@tonic-gate 					break;
19947c478bd9Sstevel@tonic-gate 				case TYPE_A:
19957c478bd9Sstevel@tonic-gate 					setascii(0, NULL);
19967c478bd9Sstevel@tonic-gate 					break;
19977c478bd9Sstevel@tonic-gate 				case TYPE_I:
19987c478bd9Sstevel@tonic-gate 					setbinary(0, NULL);
19997c478bd9Sstevel@tonic-gate 					break;
20007c478bd9Sstevel@tonic-gate 				case TYPE_E:
20017c478bd9Sstevel@tonic-gate 					setebcdic(0, NULL);
20027c478bd9Sstevel@tonic-gate 					break;
20037c478bd9Sstevel@tonic-gate 				case TYPE_L:
20047c478bd9Sstevel@tonic-gate 					settenex(0, NULL);
20057c478bd9Sstevel@tonic-gate 					break;
20067c478bd9Sstevel@tonic-gate 			}
20077c478bd9Sstevel@tonic-gate 			if (cpend) {
20087c478bd9Sstevel@tonic-gate 				char msg[2];
20097c478bd9Sstevel@tonic-gate 
20107c478bd9Sstevel@tonic-gate 				(void) fprintf(ctrl_out, "%c%c", IAC, IP);
20117c478bd9Sstevel@tonic-gate 				(void) fflush(ctrl_out);
20127c478bd9Sstevel@tonic-gate 				*msg = (char)IAC;
20137c478bd9Sstevel@tonic-gate 				*(msg+1) = (char)DM;
20147c478bd9Sstevel@tonic-gate 				if (send(fileno(ctrl_out), msg, 2, MSG_OOB)
20157c478bd9Sstevel@tonic-gate 				    != 2)
20167c478bd9Sstevel@tonic-gate 					perror("abort");
20177c478bd9Sstevel@tonic-gate 				(void) fprintf(ctrl_out, "ABOR\r\n");
20187c478bd9Sstevel@tonic-gate 				(void) fflush(ctrl_out);
20197c478bd9Sstevel@tonic-gate 				FD_ZERO(&mask);
20207c478bd9Sstevel@tonic-gate 				FD_SET(fileno(ctrl_in), &mask);
20217c478bd9Sstevel@tonic-gate 				if ((nfnd = empty(&mask, 10,
20227c478bd9Sstevel@tonic-gate 				    fileno(ctrl_in) + 1)) <= 0) {
20237c478bd9Sstevel@tonic-gate 					if (nfnd < 0) {
20247c478bd9Sstevel@tonic-gate 						perror("abort");
20257c478bd9Sstevel@tonic-gate 					}
20267c478bd9Sstevel@tonic-gate 					if (ptabflg)
20277c478bd9Sstevel@tonic-gate 						code = -1;
20287c478bd9Sstevel@tonic-gate 					lostpeer(0);
20297c478bd9Sstevel@tonic-gate 				}
20307c478bd9Sstevel@tonic-gate 				(void) getreply(0);
20317c478bd9Sstevel@tonic-gate 				(void) getreply(0);
20327c478bd9Sstevel@tonic-gate 			}
20337c478bd9Sstevel@tonic-gate 		}
20347c478bd9Sstevel@tonic-gate 		pswitch(1);
20357c478bd9Sstevel@tonic-gate 		if (ptabflg)
20367c478bd9Sstevel@tonic-gate 			code = -1;
20377c478bd9Sstevel@tonic-gate 		(void) signal(SIGINT, oldintr);
20387c478bd9Sstevel@tonic-gate 		return;
20397c478bd9Sstevel@tonic-gate 	}
20407c478bd9Sstevel@tonic-gate 	if (cpend) {
20417c478bd9Sstevel@tonic-gate 		char msg[2];
20427c478bd9Sstevel@tonic-gate 
20437c478bd9Sstevel@tonic-gate 		(void) fprintf(ctrl_out, "%c%c", IAC, IP);
20447c478bd9Sstevel@tonic-gate 		(void) fflush(ctrl_out);
20457c478bd9Sstevel@tonic-gate 		*msg = (char)IAC;
20467c478bd9Sstevel@tonic-gate 		*(msg+1) = (char)DM;
20477c478bd9Sstevel@tonic-gate 		if (send(fileno(ctrl_out), msg, 2, MSG_OOB) != 2)
20487c478bd9Sstevel@tonic-gate 			perror("abort");
20497c478bd9Sstevel@tonic-gate 		(void) fprintf(ctrl_out, "ABOR\r\n");
20507c478bd9Sstevel@tonic-gate 		(void) fflush(ctrl_out);
20517c478bd9Sstevel@tonic-gate 		FD_ZERO(&mask);
20527c478bd9Sstevel@tonic-gate 		FD_SET(fileno(ctrl_in), &mask);
20537c478bd9Sstevel@tonic-gate 		if ((nfnd = empty(&mask, 10, fileno(ctrl_in) + 1)) <= 0) {
20547c478bd9Sstevel@tonic-gate 			if (nfnd < 0) {
20557c478bd9Sstevel@tonic-gate 				perror("abort");
20567c478bd9Sstevel@tonic-gate 			}
20577c478bd9Sstevel@tonic-gate 			if (ptabflg)
20587c478bd9Sstevel@tonic-gate 				code = -1;
20597c478bd9Sstevel@tonic-gate 			lostpeer(0);
20607c478bd9Sstevel@tonic-gate 		}
20617c478bd9Sstevel@tonic-gate 		(void) getreply(0);
20627c478bd9Sstevel@tonic-gate 		(void) getreply(0);
20637c478bd9Sstevel@tonic-gate 	}
20647c478bd9Sstevel@tonic-gate 	pswitch(!proxy);
20657c478bd9Sstevel@tonic-gate 	if (!cpend && !secndflag) {  /* only if cmd = "RETR" (proxy=1) */
20667c478bd9Sstevel@tonic-gate 		if (command("%s %s", cmd2, local) != PRELIM) {
20677c478bd9Sstevel@tonic-gate 			pswitch(0);
20687c478bd9Sstevel@tonic-gate 			switch (oldtype) {
20697c478bd9Sstevel@tonic-gate 				case 0:
20707c478bd9Sstevel@tonic-gate 					break;
20717c478bd9Sstevel@tonic-gate 				case TYPE_A:
20727c478bd9Sstevel@tonic-gate 					setascii(0, NULL);
20737c478bd9Sstevel@tonic-gate 					break;
20747c478bd9Sstevel@tonic-gate 				case TYPE_I:
20757c478bd9Sstevel@tonic-gate 					setbinary(0, NULL);
20767c478bd9Sstevel@tonic-gate 					break;
20777c478bd9Sstevel@tonic-gate 				case TYPE_E:
20787c478bd9Sstevel@tonic-gate 					setebcdic(0, NULL);
20797c478bd9Sstevel@tonic-gate 					break;
20807c478bd9Sstevel@tonic-gate 				case TYPE_L:
20817c478bd9Sstevel@tonic-gate 					settenex(0, NULL);
20827c478bd9Sstevel@tonic-gate 					break;
20837c478bd9Sstevel@tonic-gate 			}
20847c478bd9Sstevel@tonic-gate 			if (cpend) {
20857c478bd9Sstevel@tonic-gate 				char msg[2];
20867c478bd9Sstevel@tonic-gate 
20877c478bd9Sstevel@tonic-gate 				(void) fprintf(ctrl_out, "%c%c", IAC, IP);
20887c478bd9Sstevel@tonic-gate 				(void) fflush(ctrl_out);
20897c478bd9Sstevel@tonic-gate 				*msg = (char)IAC;
20907c478bd9Sstevel@tonic-gate 				*(msg+1) = (char)DM;
20917c478bd9Sstevel@tonic-gate 				if (send(fileno(ctrl_out), msg, 2, MSG_OOB)
20927c478bd9Sstevel@tonic-gate 				    != 2)
20937c478bd9Sstevel@tonic-gate 					perror("abort");
20947c478bd9Sstevel@tonic-gate 				(void) fprintf(ctrl_out, "ABOR\r\n");
20957c478bd9Sstevel@tonic-gate 				(void) fflush(ctrl_out);
20967c478bd9Sstevel@tonic-gate 				FD_ZERO(&mask);
20977c478bd9Sstevel@tonic-gate 				FD_SET(fileno(ctrl_in), &mask);
20987c478bd9Sstevel@tonic-gate 				if ((nfnd = empty(&mask, 10,
20997c478bd9Sstevel@tonic-gate 				    fileno(ctrl_in) + 1)) <= 0) {
21007c478bd9Sstevel@tonic-gate 					if (nfnd < 0) {
21017c478bd9Sstevel@tonic-gate 						perror("abort");
21027c478bd9Sstevel@tonic-gate 					}
21037c478bd9Sstevel@tonic-gate 					if (ptabflg)
21047c478bd9Sstevel@tonic-gate 						code = -1;
21057c478bd9Sstevel@tonic-gate 					lostpeer(0);
21067c478bd9Sstevel@tonic-gate 				}
21077c478bd9Sstevel@tonic-gate 				(void) getreply(0);
21087c478bd9Sstevel@tonic-gate 				(void) getreply(0);
21097c478bd9Sstevel@tonic-gate 			}
21107c478bd9Sstevel@tonic-gate 			pswitch(1);
21117c478bd9Sstevel@tonic-gate 			if (ptabflg)
21127c478bd9Sstevel@tonic-gate 				code = -1;
21137c478bd9Sstevel@tonic-gate 			(void) signal(SIGINT, oldintr);
21147c478bd9Sstevel@tonic-gate 			return;
21157c478bd9Sstevel@tonic-gate 		}
21167c478bd9Sstevel@tonic-gate 	}
21177c478bd9Sstevel@tonic-gate 	if (cpend) {
21187c478bd9Sstevel@tonic-gate 		char msg[2];
21197c478bd9Sstevel@tonic-gate 
21207c478bd9Sstevel@tonic-gate 		(void) fprintf(ctrl_out, "%c%c", IAC, IP);
21217c478bd9Sstevel@tonic-gate 		(void) fflush(ctrl_out);
21227c478bd9Sstevel@tonic-gate 		*msg = (char)IAC;
21237c478bd9Sstevel@tonic-gate 		*(msg+1) = (char)DM;
21247c478bd9Sstevel@tonic-gate 		if (send(fileno(ctrl_out), msg, 2, MSG_OOB) != 2)
21257c478bd9Sstevel@tonic-gate 			perror("abort");
21267c478bd9Sstevel@tonic-gate 		(void) fprintf(ctrl_out, "ABOR\r\n");
21277c478bd9Sstevel@tonic-gate 		(void) fflush(ctrl_out);
21287c478bd9Sstevel@tonic-gate 		FD_ZERO(&mask);
21297c478bd9Sstevel@tonic-gate 		FD_SET(fileno(ctrl_in), &mask);
21307c478bd9Sstevel@tonic-gate 		if ((nfnd = empty(&mask, 10, fileno(ctrl_in) + 1)) <= 0) {
21317c478bd9Sstevel@tonic-gate 			if (nfnd < 0) {
21327c478bd9Sstevel@tonic-gate 				perror("abort");
21337c478bd9Sstevel@tonic-gate 			}
21347c478bd9Sstevel@tonic-gate 			if (ptabflg)
21357c478bd9Sstevel@tonic-gate 				code = -1;
21367c478bd9Sstevel@tonic-gate 			lostpeer(0);
21377c478bd9Sstevel@tonic-gate 		}
21387c478bd9Sstevel@tonic-gate 		(void) getreply(0);
21397c478bd9Sstevel@tonic-gate 		(void) getreply(0);
21407c478bd9Sstevel@tonic-gate 	}
21417c478bd9Sstevel@tonic-gate 	pswitch(!proxy);
21427c478bd9Sstevel@tonic-gate 	if (cpend) {
21437c478bd9Sstevel@tonic-gate 		FD_ZERO(&mask);
21447c478bd9Sstevel@tonic-gate 		FD_SET(fileno(ctrl_in), &mask);
21457c478bd9Sstevel@tonic-gate 		if ((nfnd = empty(&mask, 10, fileno(ctrl_in) + 1)) <= 0) {
21467c478bd9Sstevel@tonic-gate 			if (nfnd < 0) {
21477c478bd9Sstevel@tonic-gate 				perror("abort");
21487c478bd9Sstevel@tonic-gate 			}
21497c478bd9Sstevel@tonic-gate 			if (ptabflg)
21507c478bd9Sstevel@tonic-gate 				code = -1;
21517c478bd9Sstevel@tonic-gate 			lostpeer(0);
21527c478bd9Sstevel@tonic-gate 		}
21537c478bd9Sstevel@tonic-gate 		(void) getreply(0);
21547c478bd9Sstevel@tonic-gate 		(void) getreply(0);
21557c478bd9Sstevel@tonic-gate 	}
21567c478bd9Sstevel@tonic-gate 	if (proxy)
21577c478bd9Sstevel@tonic-gate 		pswitch(0);
21587c478bd9Sstevel@tonic-gate 	switch (oldtype) {
21597c478bd9Sstevel@tonic-gate 		case 0:
21607c478bd9Sstevel@tonic-gate 			break;
21617c478bd9Sstevel@tonic-gate 		case TYPE_A:
21627c478bd9Sstevel@tonic-gate 			setascii(0, NULL);
21637c478bd9Sstevel@tonic-gate 			break;
21647c478bd9Sstevel@tonic-gate 		case TYPE_I:
21657c478bd9Sstevel@tonic-gate 			setbinary(0, NULL);
21667c478bd9Sstevel@tonic-gate 			break;
21677c478bd9Sstevel@tonic-gate 		case TYPE_E:
21687c478bd9Sstevel@tonic-gate 			setebcdic(0, NULL);
21697c478bd9Sstevel@tonic-gate 			break;
21707c478bd9Sstevel@tonic-gate 		case TYPE_L:
21717c478bd9Sstevel@tonic-gate 			settenex(0, NULL);
21727c478bd9Sstevel@tonic-gate 			break;
21737c478bd9Sstevel@tonic-gate 	}
21747c478bd9Sstevel@tonic-gate 	pswitch(1);
21757c478bd9Sstevel@tonic-gate 	if (ptabflg)
21767c478bd9Sstevel@tonic-gate 		code = -1;
21777c478bd9Sstevel@tonic-gate 	(void) signal(SIGINT, oldintr);
21787c478bd9Sstevel@tonic-gate }
21797c478bd9Sstevel@tonic-gate 
21807c478bd9Sstevel@tonic-gate /*ARGSUSED*/
21817c478bd9Sstevel@tonic-gate void
reset(int argc,char * argv[])21827c478bd9Sstevel@tonic-gate reset(int argc, char *argv[])
21837c478bd9Sstevel@tonic-gate {
21847c478bd9Sstevel@tonic-gate 	struct fd_set mask;
21857c478bd9Sstevel@tonic-gate 	int nfnd = 1;
21867c478bd9Sstevel@tonic-gate 
21877c478bd9Sstevel@tonic-gate 	FD_ZERO(&mask);
21887c478bd9Sstevel@tonic-gate 	while (nfnd > 0) {
21897c478bd9Sstevel@tonic-gate 		FD_SET(fileno(ctrl_in), &mask);
21907c478bd9Sstevel@tonic-gate 		if ((nfnd = empty(&mask, 0, fileno(ctrl_in) + 1)) < 0) {
21917c478bd9Sstevel@tonic-gate 			perror("reset");
21927c478bd9Sstevel@tonic-gate 			code = -1;
21937c478bd9Sstevel@tonic-gate 			lostpeer(0);
21947c478bd9Sstevel@tonic-gate 		} else if (nfnd > 0) {
21957c478bd9Sstevel@tonic-gate 			(void) getreply(0);
21967c478bd9Sstevel@tonic-gate 		}
21977c478bd9Sstevel@tonic-gate 	}
21987c478bd9Sstevel@tonic-gate }
21997c478bd9Sstevel@tonic-gate 
22007c478bd9Sstevel@tonic-gate static char *
gunique(char * local)22017c478bd9Sstevel@tonic-gate gunique(char *local)
22027c478bd9Sstevel@tonic-gate {
22037c478bd9Sstevel@tonic-gate 	static char new[MAXPATHLEN];
22047c478bd9Sstevel@tonic-gate 	char *cp = rindex(local, '/');
22057c478bd9Sstevel@tonic-gate 	int d, count = 0;
22067c478bd9Sstevel@tonic-gate 	char ext = '1';
22077c478bd9Sstevel@tonic-gate 
22087c478bd9Sstevel@tonic-gate 	if (cp)
22097c478bd9Sstevel@tonic-gate 		*cp = '\0';
22107c478bd9Sstevel@tonic-gate 	d = access(cp ? local : ".", 2);
22117c478bd9Sstevel@tonic-gate 	if (cp)
22127c478bd9Sstevel@tonic-gate 		*cp = '/';
22137c478bd9Sstevel@tonic-gate 	if (d < 0) {
22147c478bd9Sstevel@tonic-gate 		perror(local);
22157c478bd9Sstevel@tonic-gate 		return ((char *)0);
22167c478bd9Sstevel@tonic-gate 	}
22177c478bd9Sstevel@tonic-gate 	if (strlcpy(new, local, sizeof (new)) >= sizeof (new))
22187c478bd9Sstevel@tonic-gate 		(void) printf("gunique: too long: local %s, %d, new %d\n",
22197c478bd9Sstevel@tonic-gate 		    local, strlen(local), sizeof (new));
22207c478bd9Sstevel@tonic-gate 
22217c478bd9Sstevel@tonic-gate 	cp = new + strlen(new);
22227c478bd9Sstevel@tonic-gate 	*cp++ = '.';
22237c478bd9Sstevel@tonic-gate 	while (!d) {
22247c478bd9Sstevel@tonic-gate 		if (++count == 100) {
22257c478bd9Sstevel@tonic-gate 			(void) printf(
2226*92163adaSToomas Soome 			    "gunique: can't find unique file name.\n");
2227*92163adaSToomas Soome 			return (NULL);
22287c478bd9Sstevel@tonic-gate 		}
22297c478bd9Sstevel@tonic-gate 		*cp++ = ext;
22307c478bd9Sstevel@tonic-gate 		*cp = '\0';
22317c478bd9Sstevel@tonic-gate 		if (ext == '9')
22327c478bd9Sstevel@tonic-gate 			ext = '0';
22337c478bd9Sstevel@tonic-gate 		else
22347c478bd9Sstevel@tonic-gate 			ext++;
22357c478bd9Sstevel@tonic-gate 		if ((d = access(new, 0)) < 0)
22367c478bd9Sstevel@tonic-gate 			break;
22377c478bd9Sstevel@tonic-gate 		if (ext != '0')
22387c478bd9Sstevel@tonic-gate 			cp--;
22397c478bd9Sstevel@tonic-gate 		else if (*(cp - 2) == '.')
22407c478bd9Sstevel@tonic-gate 			*(cp - 1) = '1';
22417c478bd9Sstevel@tonic-gate 		else {
22427c478bd9Sstevel@tonic-gate 			*(cp - 2) = *(cp - 2) + 1;
22437c478bd9Sstevel@tonic-gate 			cp--;
22447c478bd9Sstevel@tonic-gate 		}
22457c478bd9Sstevel@tonic-gate 	}
22467c478bd9Sstevel@tonic-gate 	return (new);
22477c478bd9Sstevel@tonic-gate }
22487c478bd9Sstevel@tonic-gate 
22497c478bd9Sstevel@tonic-gate /*
22507c478bd9Sstevel@tonic-gate  * This is a wrap-around function for inet_ntop(). In case the af is AF_INET6
22517c478bd9Sstevel@tonic-gate  * and the address pointed by src is a IPv4-mapped IPv6 address, it
22527c478bd9Sstevel@tonic-gate  * returns printable IPv4 address, not IPv4-mapped IPv6 address. In other cases
22537c478bd9Sstevel@tonic-gate  * it behaves just like inet_ntop().
22547c478bd9Sstevel@tonic-gate  */
22557c478bd9Sstevel@tonic-gate const char *
inet_ntop_native(int af,const void * src,char * dst,size_t size)22567c478bd9Sstevel@tonic-gate inet_ntop_native(int af, const void *src, char *dst, size_t size)
22577c478bd9Sstevel@tonic-gate {
22587c478bd9Sstevel@tonic-gate 	struct in_addr src4;
22597c478bd9Sstevel@tonic-gate 	const char *result;
22607c478bd9Sstevel@tonic-gate 	struct sockaddr_in *sin;
22617c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
22627c478bd9Sstevel@tonic-gate 
22637c478bd9Sstevel@tonic-gate 	if (af == AF_INET6) {
22647c478bd9Sstevel@tonic-gate 		sin6 = (struct sockaddr_in6 *)src;
22657c478bd9Sstevel@tonic-gate 		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
22667c478bd9Sstevel@tonic-gate 			IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr, &src4);
22677c478bd9Sstevel@tonic-gate 			result = inet_ntop(AF_INET, &src4, dst, size);
22687c478bd9Sstevel@tonic-gate 		} else {
22697c478bd9Sstevel@tonic-gate 			result = inet_ntop(AF_INET6, &sin6->sin6_addr,
22707c478bd9Sstevel@tonic-gate 			    dst, size);
22717c478bd9Sstevel@tonic-gate 		}
22727c478bd9Sstevel@tonic-gate 	} else {
22737c478bd9Sstevel@tonic-gate 		sin = (struct sockaddr_in *)src;
22747c478bd9Sstevel@tonic-gate 		result = inet_ntop(af, &sin->sin_addr, dst, size);
22757c478bd9Sstevel@tonic-gate 	}
22767c478bd9Sstevel@tonic-gate 
22777c478bd9Sstevel@tonic-gate 	return (result);
22787c478bd9Sstevel@tonic-gate }
22797c478bd9Sstevel@tonic-gate 
22807c478bd9Sstevel@tonic-gate int
secure_command(char * cmd)22817c478bd9Sstevel@tonic-gate secure_command(char *cmd)
22827c478bd9Sstevel@tonic-gate {
22837c478bd9Sstevel@tonic-gate 	unsigned char *in = NULL, *out = NULL;
22847c478bd9Sstevel@tonic-gate 	int length = 0;
22857c478bd9Sstevel@tonic-gate 	size_t inlen;
22867c478bd9Sstevel@tonic-gate 
22877c478bd9Sstevel@tonic-gate 	if ((auth_type != AUTHTYPE_NONE) && clevel != PROT_C) {
22887c478bd9Sstevel@tonic-gate 		gss_buffer_desc in_buf, out_buf;
22897c478bd9Sstevel@tonic-gate 		OM_uint32 maj_stat, min_stat;
22907c478bd9Sstevel@tonic-gate 
22917c478bd9Sstevel@tonic-gate 		/* secure_command (based on level) */
22927c478bd9Sstevel@tonic-gate 		if (auth_type == AUTHTYPE_GSSAPI) {
22937c478bd9Sstevel@tonic-gate 			OM_uint32 expire_time;
22947c478bd9Sstevel@tonic-gate 			int conf_state;
22957c478bd9Sstevel@tonic-gate 			/* clevel = PROT_P; */
22967c478bd9Sstevel@tonic-gate 			in_buf.value = cmd;
22977c478bd9Sstevel@tonic-gate 			in_buf.length = strlen(cmd) + 1;
22987c478bd9Sstevel@tonic-gate 
22997c478bd9Sstevel@tonic-gate 			maj_stat = gss_context_time(&min_stat, gcontext,
2300*92163adaSToomas Soome 			    &expire_time);
23017c478bd9Sstevel@tonic-gate 			if (GSS_ERROR(maj_stat)) {
23027c478bd9Sstevel@tonic-gate 				user_gss_error(maj_stat, min_stat,
2303*92163adaSToomas Soome 				    "gss context has expired");
23047c478bd9Sstevel@tonic-gate 				fatal("Your gss credentials have expired.  "
2305*92163adaSToomas Soome 				    "Good-bye!");
23067c478bd9Sstevel@tonic-gate 			}
23077c478bd9Sstevel@tonic-gate 			maj_stat = gss_seal(&min_stat, gcontext,
2308*92163adaSToomas Soome 			    (clevel == PROT_P), /* private */
2309*92163adaSToomas Soome 			    GSS_C_QOP_DEFAULT,
2310*92163adaSToomas Soome 			    &in_buf, &conf_state,
2311*92163adaSToomas Soome 			    &out_buf);
23127c478bd9Sstevel@tonic-gate 			if (maj_stat != GSS_S_COMPLETE) {
23137c478bd9Sstevel@tonic-gate 				/* generally need to deal */
23147c478bd9Sstevel@tonic-gate 				user_gss_error(maj_stat, min_stat,
2315*92163adaSToomas Soome 				    (clevel == PROT_P) ?
2316*92163adaSToomas Soome 				    "gss_seal ENC didn't complete":
2317*92163adaSToomas Soome 				    "gss_seal MIC didn't complete");
23187c478bd9Sstevel@tonic-gate 			} else if ((clevel == PROT_P) && !conf_state) {
23197c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
2320*92163adaSToomas Soome 				    "GSSAPI didn't encrypt message");
23217c478bd9Sstevel@tonic-gate 				out = out_buf.value;
23227c478bd9Sstevel@tonic-gate 			} else {
23237c478bd9Sstevel@tonic-gate 				if (debug)
2324*92163adaSToomas Soome 					(void) fprintf(stderr,
2325*92163adaSToomas Soome 					    "sealed (%s) %d bytes\n",
2326*92163adaSToomas Soome 					    clevel == PROT_P ? "ENC" : "MIC",
2327*92163adaSToomas Soome 					    out_buf.length);
23287c478bd9Sstevel@tonic-gate 
23297c478bd9Sstevel@tonic-gate 				out = out_buf.value;
23307c478bd9Sstevel@tonic-gate 			}
23317c478bd9Sstevel@tonic-gate 		}
23327c478bd9Sstevel@tonic-gate 		/* Other auth types go here ... */
23337c478bd9Sstevel@tonic-gate 		inlen = ((4 * out_buf.length) / 3) + 4;
23347c478bd9Sstevel@tonic-gate 		in = (uchar_t *)malloc(inlen);
23357c478bd9Sstevel@tonic-gate 		if (in == NULL) {
23367c478bd9Sstevel@tonic-gate 			gss_release_buffer(&min_stat, &out_buf);
23377c478bd9Sstevel@tonic-gate 			fatal("Memory error allocating space for response.");
23387c478bd9Sstevel@tonic-gate 		}
23397c478bd9Sstevel@tonic-gate 		length = out_buf.length;
23407c478bd9Sstevel@tonic-gate 		if (auth_error = radix_encode(out, in, inlen, &length, 0)) {
23417c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
2342*92163adaSToomas Soome 			    "Couldn't base 64 encode command (%s)\n",
2343*92163adaSToomas Soome 			    radix_error(auth_error));
23447c478bd9Sstevel@tonic-gate 			free(in);
23457c478bd9Sstevel@tonic-gate 			gss_release_buffer(&min_stat, &out_buf);
23467c478bd9Sstevel@tonic-gate 			return (0);
23477c478bd9Sstevel@tonic-gate 		}
23487c478bd9Sstevel@tonic-gate 
23497c478bd9Sstevel@tonic-gate 		(void) fprintf(ctrl_out, "%s %s",
2350*92163adaSToomas Soome 		    clevel == PROT_P ? "ENC" : "MIC", in);
23517c478bd9Sstevel@tonic-gate 
23527c478bd9Sstevel@tonic-gate 		free(in);
23537c478bd9Sstevel@tonic-gate 		gss_release_buffer(&min_stat, &out_buf);
23547c478bd9Sstevel@tonic-gate 
23557c478bd9Sstevel@tonic-gate 		if (debug)
23567c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
23577c478bd9Sstevel@tonic-gate 			    "secure_command(%s)\nencoding %d bytes %s %s\n",
23587c478bd9Sstevel@tonic-gate 			    cmd, length,
23597c478bd9Sstevel@tonic-gate 			    (clevel == PROT_P) ? "ENC" : "MIC", in);
23607c478bd9Sstevel@tonic-gate 	} else {
23617c478bd9Sstevel@tonic-gate 		/*
23627c478bd9Sstevel@tonic-gate 		 * auth_type = AUTHTYPE_NONE or
23637c478bd9Sstevel@tonic-gate 		 * command channel is not protected
23647c478bd9Sstevel@tonic-gate 		 */
23657c478bd9Sstevel@tonic-gate 		fputs(cmd, ctrl_out);
23667c478bd9Sstevel@tonic-gate 	}
23677c478bd9Sstevel@tonic-gate 
23687c478bd9Sstevel@tonic-gate 	(void) fprintf(ctrl_out, "\r\n");
23697c478bd9Sstevel@tonic-gate 	(void) fflush(ctrl_out);
23707c478bd9Sstevel@tonic-gate 	return (1);
23717c478bd9Sstevel@tonic-gate }
23727c478bd9Sstevel@tonic-gate 
23737c478bd9Sstevel@tonic-gate unsigned int maxbuf;
23747c478bd9Sstevel@tonic-gate unsigned char *ucbuf;
23757c478bd9Sstevel@tonic-gate 
23767c478bd9Sstevel@tonic-gate void
setpbsz(unsigned int size)23777c478bd9Sstevel@tonic-gate setpbsz(unsigned int size)
23787c478bd9Sstevel@tonic-gate {
23797c478bd9Sstevel@tonic-gate 	unsigned int actualbuf;
23807c478bd9Sstevel@tonic-gate 	int oldverbose;
23817c478bd9Sstevel@tonic-gate 
23827c478bd9Sstevel@tonic-gate 	if (ucbuf)
23837c478bd9Sstevel@tonic-gate 		(void) free(ucbuf);
23847c478bd9Sstevel@tonic-gate 	actualbuf = size;
23857c478bd9Sstevel@tonic-gate 	while ((ucbuf = (unsigned char *)malloc(actualbuf)) == NULL) {
23867c478bd9Sstevel@tonic-gate 		if (actualbuf)
23877c478bd9Sstevel@tonic-gate 			actualbuf >>= 2;
23887c478bd9Sstevel@tonic-gate 		else {
23897c478bd9Sstevel@tonic-gate 			perror("Error while trying to malloc PROT buffer:");
23907c478bd9Sstevel@tonic-gate 			exit(1);
23917c478bd9Sstevel@tonic-gate 		}
23927c478bd9Sstevel@tonic-gate 	}
23937c478bd9Sstevel@tonic-gate 	oldverbose = verbose;
23947c478bd9Sstevel@tonic-gate 	verbose = 0;
23957c478bd9Sstevel@tonic-gate 	reply_parse = "PBSZ=";
23967c478bd9Sstevel@tonic-gate 	if (command("PBSZ %u", actualbuf) != COMPLETE)
23977c478bd9Sstevel@tonic-gate 		fatal("Cannot set PROT buffer size");
23987c478bd9Sstevel@tonic-gate 	if (reply_parse) {
23997c478bd9Sstevel@tonic-gate 		if ((maxbuf = (unsigned int) atol(reply_parse)) > actualbuf)
24007c478bd9Sstevel@tonic-gate 			maxbuf = actualbuf;
24017c478bd9Sstevel@tonic-gate 	} else
24027c478bd9Sstevel@tonic-gate 		maxbuf = actualbuf;
24037c478bd9Sstevel@tonic-gate 	reply_parse = NULL;
24047c478bd9Sstevel@tonic-gate 	verbose = oldverbose;
24057c478bd9Sstevel@tonic-gate }
24067c478bd9Sstevel@tonic-gate 
24077c478bd9Sstevel@tonic-gate /*
24087c478bd9Sstevel@tonic-gate  * Do the base 64 decoding of the raw input buffer, b64_buf.
24097c478bd9Sstevel@tonic-gate  * Also do the verification and decryption, if required.
24107c478bd9Sstevel@tonic-gate  * retval contains the current error code number
24117c478bd9Sstevel@tonic-gate  *
24127c478bd9Sstevel@tonic-gate  * returns:
24137c478bd9Sstevel@tonic-gate  *	(RFC 2228:  error returns are 3 digit numbers of the form 5xy)
24147c478bd9Sstevel@tonic-gate  *	5	if an error occurred
24157c478bd9Sstevel@tonic-gate  */
24167c478bd9Sstevel@tonic-gate static int
decode_reply(uchar_t * plain_buf,int ilen,uchar_t * b64_buf,int retval,boolean_t * again)2417*92163adaSToomas Soome decode_reply(uchar_t *plain_buf, int ilen, uchar_t *b64_buf, int retval,
2418*92163adaSToomas Soome     boolean_t *again)
24197c478bd9Sstevel@tonic-gate {
24207c478bd9Sstevel@tonic-gate 	int len;
24217c478bd9Sstevel@tonic-gate 	int safe = 0;
24227c478bd9Sstevel@tonic-gate 
24237c478bd9Sstevel@tonic-gate 	*again = 0;
24247c478bd9Sstevel@tonic-gate 
24257c478bd9Sstevel@tonic-gate 	if (!b64_buf[0])	/* if there is no string, no problem */
2426*92163adaSToomas Soome 		return (retval);
24277c478bd9Sstevel@tonic-gate 
24287c478bd9Sstevel@tonic-gate 	if ((auth_type == AUTHTYPE_NONE)) {
2429*92163adaSToomas Soome 		(void) printf("Cannot decode reply:\n%d %s\n", code, b64_buf);
2430*92163adaSToomas Soome 		return ('5');
24317c478bd9Sstevel@tonic-gate 	}
24327c478bd9Sstevel@tonic-gate 
24337c478bd9Sstevel@tonic-gate 	switch (code) {
24347c478bd9Sstevel@tonic-gate 
2435*92163adaSToomas Soome 	case 631:	/* 'safe' */
24367c478bd9Sstevel@tonic-gate 		safe = 1;
24377c478bd9Sstevel@tonic-gate 		break;
24387c478bd9Sstevel@tonic-gate 
2439*92163adaSToomas Soome 	case 632:	/* 'private' */
24407c478bd9Sstevel@tonic-gate 		break;
24417c478bd9Sstevel@tonic-gate 
2442*92163adaSToomas Soome 	case 633:	/* 'confidential' */
24437c478bd9Sstevel@tonic-gate 		break;
24447c478bd9Sstevel@tonic-gate 
2445*92163adaSToomas Soome 	default:
24467c478bd9Sstevel@tonic-gate 		(void) printf("Unknown reply: %d %s\n", code, b64_buf);
24477c478bd9Sstevel@tonic-gate 		return ('5');
24487c478bd9Sstevel@tonic-gate 	}
24497c478bd9Sstevel@tonic-gate 
24507c478bd9Sstevel@tonic-gate 	/* decode the base64 encoded message */
24517c478bd9Sstevel@tonic-gate 	auth_error = radix_encode(b64_buf, plain_buf, ilen, &len, 1);
24527c478bd9Sstevel@tonic-gate 
24537c478bd9Sstevel@tonic-gate 	if (auth_error) {
24547c478bd9Sstevel@tonic-gate 		(void) printf("Can't base 64 decode reply %d (%s)\n\"%s\"\n",
2455*92163adaSToomas Soome 		    code, radix_error(auth_error), b64_buf);
24567c478bd9Sstevel@tonic-gate 		return ('5');
24577c478bd9Sstevel@tonic-gate 	}
24587c478bd9Sstevel@tonic-gate 
24597c478bd9Sstevel@tonic-gate 	if (auth_type == AUTHTYPE_GSSAPI) {
24607c478bd9Sstevel@tonic-gate 		gss_buffer_desc xmit_buf, msg_buf;
24617c478bd9Sstevel@tonic-gate 		OM_uint32 maj_stat, min_stat;
24627c478bd9Sstevel@tonic-gate 		int conf_state = safe;
24637c478bd9Sstevel@tonic-gate 		xmit_buf.value = plain_buf;
24647c478bd9Sstevel@tonic-gate 		xmit_buf.length = len;
24657c478bd9Sstevel@tonic-gate 
24667c478bd9Sstevel@tonic-gate 		/* decrypt/verify the message */
24677c478bd9Sstevel@tonic-gate 		maj_stat = gss_unseal(&min_stat, gcontext,
2468*92163adaSToomas Soome 		    &xmit_buf, &msg_buf, &conf_state, NULL);
24697c478bd9Sstevel@tonic-gate 		if (maj_stat != GSS_S_COMPLETE) {
24707c478bd9Sstevel@tonic-gate 			user_gss_error(maj_stat, min_stat,
2471*92163adaSToomas Soome 			    "failed unsealing reply");
24727c478bd9Sstevel@tonic-gate 			return ('5');
24737c478bd9Sstevel@tonic-gate 		}
24747c478bd9Sstevel@tonic-gate 		if (msg_buf.length < ilen - 2 - 1) {
24757c478bd9Sstevel@tonic-gate 			memcpy(plain_buf, msg_buf.value, msg_buf.length);
24767c478bd9Sstevel@tonic-gate 			strcpy((char *)&plain_buf[msg_buf.length], "\r\n");
24777c478bd9Sstevel@tonic-gate 			gss_release_buffer(&min_stat, &msg_buf);
24787c478bd9Sstevel@tonic-gate 			*again = 1;
24797c478bd9Sstevel@tonic-gate 		} else {
24807c478bd9Sstevel@tonic-gate 			user_gss_error(maj_stat, min_stat,
2481*92163adaSToomas Soome 			    "reply was too long");
24827c478bd9Sstevel@tonic-gate 			return ('5');
24837c478bd9Sstevel@tonic-gate 		}
24847c478bd9Sstevel@tonic-gate 	} /* end if GSSAPI */
24857c478bd9Sstevel@tonic-gate 
24867c478bd9Sstevel@tonic-gate 	/* Other auth types go here... */
24877c478bd9Sstevel@tonic-gate 
24887c478bd9Sstevel@tonic-gate 	return (retval);
24897c478bd9Sstevel@tonic-gate }
2490