Home | History | Annotate | Download | only in ftp
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  *	Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
     24  *	Use is subject to license terms.
     25  */
     26 
     27 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
     28 /*	All Rights Reserved  	*/
     29 
     30 /*
     31  *	University Copyright- Copyright (c) 1982, 1986, 1988
     32  *	The Regents of the University of California
     33  *	All Rights Reserved
     34  *
     35  *	University Acknowledgment- Portions of this document are derived from
     36  *	software developed by the University of California, Berkeley, and its
     37  *	contributors.
     38  */
     39 
     40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     41 
     42 #include "ftp_var.h"
     43 #include <arpa/nameser.h>
     44 #include <sys/types.h>
     45 
     46 /*
     47  * WRITE() returns:
     48  * 	>0	no error
     49  *	-1	error, errorno is set
     50  *	-2	security error (secure_write() only)
     51  */
     52 #define	PUTC(x, y)	secure_putc(x, y)
     53 #define	READ(x, y, z)	secure_read(x, y, z)
     54 #define	WRITE(x, y, z)	secure_write(x, y, z)
     55 
     56 static struct	sockaddr_in6 data_addr;
     57 int	data = -1;
     58 static int	abrtflag = 0;
     59 static int	ptflag = 0;
     60 int		connected;
     61 static jmp_buf	sendabort;
     62 static jmp_buf	recvabort;
     63 static jmp_buf 	ptabort;
     64 static int ptabflg;
     65 static boolean_t pasv_refused;
     66 boolean_t	eport_supported = B_TRUE;
     67 /*
     68  * For IPv6 addresses, EPSV will be the default (rather than EPRT/LPRT).
     69  * The EPSV/ERPT ftp protocols are specified in RFC 2428.
     70  *
     71  * Perform EPSV if passivemode is set and ipv6rem is TRUE.
     72  */
     73 static boolean_t ipv6rem;
     74 int	use_eprt = 0;	/* Testing option that specifies EPRT by default */
     75 FILE	*ctrl_in, *ctrl_out;
     76 
     77 static void abortsend(int sig);
     78 static void abortpt(int sig);
     79 static void proxtrans(char *cmd, char *local, char *remote);
     80 static void cmdabort(int sig);
     81 static int empty(struct fd_set *mask, int sec, int nfds);
     82 static void abortrecv(int sig);
     83 static int initconn(void);
     84 static FILE *dataconn(char *mode);
     85 static void ptransfer(char *direction, off_t bytes, hrtime_t t0,
     86     hrtime_t t1, char *local, char *remote);
     87 static void psabort(int sig);
     88 static char *gunique(char *local);
     89 static const char *inet_ntop_native(int af, const void *src, char *dst,
     90     size_t size);
     91 static ssize_t timedread(int fd, void *buf, size_t maxlen, int timeout);
     92 
     93 static int secure_command(char *);
     94 static int decode_reply(uchar_t *, int, uchar_t *, int, boolean_t *);
     95 
     96 static ssize_t	bufcnt;		/* number of bytes in buf[]	*/
     97 static char	*bufp;		/* next character in buf	*/
     98 static int	buferr;		/* last errno			*/
     99 static size_t	bufsize;
    100 
    101 static void fdio_setbuf(char *buffer, size_t bufsize);
    102 static int fdio_fillbuf(int fd);
    103 static int fdio_error(int fd);
    104 #define	fdio_getc(fd)	(--bufcnt < 0 ? fdio_fillbuf((fd)) : \
    105 			    ((unsigned char)*bufp++))
    106 
    107 #define	MAX(a, b) ((a) > (b) ? (a) : (b))
    108 #define	NONZERO(x)	((x) == 0 ? 1 : (x))
    109 
    110 static void
    111 fdio_setbuf(char *buffer, size_t maxsize)
    112 {
    113 	buf = buffer;
    114 	bufp = buf;
    115 	bufcnt = 0;
    116 	buferr = 0;
    117 	bufsize = maxsize;
    118 }
    119 
    120 static int
    121 fdio_fillbuf(int fd)
    122 {
    123 	bufcnt = timedread(fd, buf, bufsize, timeout);
    124 	if (bufcnt < 0)
    125 		buferr = errno;
    126 	if (bufcnt <= 0)
    127 		return (EOF);
    128 	bufp = buf;
    129 	bufcnt--;
    130 	return ((unsigned char)*bufp++);
    131 }
    132 
    133 /*
    134  * fdio_error - used on a file descriptor instead of ferror()
    135  */
    136 
    137 /*ARGSUSED*/
    138 static int
    139 fdio_error(int fd)
    140 {
    141 	return (buferr);
    142 }
    143 
    144 /*
    145  * timedread - read buffer (like "read"), but with timeout (in seconds)
    146  */
    147 
    148 static ssize_t
    149 timedread(int fd, void *buf, size_t size, int timeout)
    150 {
    151 	struct fd_set mask;
    152 	struct timeval tv;
    153 	int err;
    154 
    155 	if (!timeout)
    156 		return (READ(fd, buf, size));
    157 
    158 	tv.tv_sec = (time_t)timeout;
    159 	tv.tv_usec = 0;
    160 
    161 	FD_ZERO(&mask);
    162 	FD_SET(fd, &mask);
    163 
    164 	err = select(fd + 1, &mask, NULL, NULL, &tv);
    165 	if (err == 0)
    166 		errno = ETIMEDOUT;
    167 	if (err <= 0)
    168 		return (-1);
    169 
    170 	return (READ(fd, buf, size));
    171 }
    172 
    173 
    174 char *
    175 hookup(char *host, char *service)
    176 {
    177 	struct addrinfo hints, *ai = NULL, *ai_head;
    178 	int s;
    179 	socklen_t len;
    180 	static char hostnamebuf[80];
    181 	struct in6_addr ipv6addr;
    182 	char abuf[INET6_ADDRSTRLEN];
    183 	int error_num;
    184 	int on = 1;
    185 
    186 	/*
    187 	 * There appears to be a bug in getaddrinfo() where, if the
    188 	 * ai_family is set to AF_INET6, and the host is a v4-only
    189 	 * host, getaddrinfo() returns an error instead of returning
    190 	 * an v4-mapped ipv6 address. Therefore the ai_family is
    191 	 * set to AF_UNSPEC and any returned v4 addresses are
    192 	 * explicitly mapped within ftp.
    193 	 */
    194 	bzero((char *)&remctladdr, sizeof (remctladdr));
    195 	bzero((char *)&hints, sizeof (hints));
    196 	hints.ai_flags = AI_CANONNAME;
    197 	hints.ai_family = AF_UNSPEC;
    198 	hints.ai_socktype = SOCK_STREAM;
    199 
    200 	error_num = getaddrinfo(host, service, &hints, &ai);
    201 	if (error_num != 0) {
    202 		if (error_num == EAI_AGAIN) {
    203 			(void) printf(
    204 			    "%s: unknown host or invalid literal address "
    205 			    "(try again later)\n", host);
    206 		} else {
    207 			(void) printf(
    208 			    "%s: unknown host or invalid literal address\n",
    209 			    host);
    210 		}
    211 		code = -1;
    212 		return ((char *)0);
    213 	}
    214 	ai_head = ai;
    215 
    216 
    217 	/*
    218 	 * If ai_canonname is a IPv4-mapped IPv6 literal, we'll convert it to
    219 	 * IPv4 literal address.
    220 	 */
    221 	if (ai->ai_canonname != NULL &&
    222 	    (inet_pton(AF_INET6, ai->ai_canonname, &ipv6addr) > 0) &&
    223 	    IN6_IS_ADDR_V4MAPPED(&ipv6addr)) {
    224 		struct in_addr src4;
    225 		hostnamebuf[0] = '\0';
    226 		IN6_V4MAPPED_TO_INADDR(&ipv6addr, &src4);
    227 		(void) inet_ntop(AF_INET, &src4, hostnamebuf,
    228 		    sizeof (hostnamebuf));
    229 
    230 		/*
    231 		 * It can even be the case that the "host" supplied by the user
    232 		 * can be a IPv4-mapped IPv6 literal. So, let's fix that too.
    233 		 */
    234 		if ((inet_pton(AF_INET6, host, &ipv6addr) > 0) &&
    235 		    IN6_IS_ADDR_V4MAPPED(&ipv6addr) &&
    236 		    strlen(hostnamebuf) <= strlen(host)) {
    237 			(void) strlcpy(host, hostnamebuf, strlen(host) + 1);
    238 		}
    239 	} else {
    240 		reset_timer();
    241 		(void) strlcpy(hostnamebuf,
    242 		    (ai->ai_canonname ? ai->ai_canonname : host),
    243 		    sizeof (hostnamebuf));
    244 	}
    245 
    246 	hostname = hostnamebuf;
    247 	for (;;) {
    248 		int oerrno;
    249 
    250 		bcopy(ai->ai_addr, &remctladdr, ai->ai_addrlen);
    251 		if (ai->ai_addr->sa_family == AF_INET) {
    252 			IN6_INADDR_TO_V4MAPPED(
    253 			    &(((struct sockaddr_in *)ai->ai_addr)->sin_addr),
    254 			    &remctladdr.sin6_addr);
    255 			remctladdr.sin6_family = AF_INET6;
    256 		}
    257 
    258 		s = socket(AF_INET6, SOCK_STREAM, 0);
    259 		if (s < 0) {
    260 			perror("ftp: socket");
    261 			code = -1;
    262 			freeaddrinfo(ai_head);
    263 			return (0);
    264 		}
    265 		if (timeout && setsockopt(s, IPPROTO_TCP, TCP_ABORT_THRESHOLD,
    266 		    (char *)&timeoutms, sizeof (timeoutms)) < 0 && debug)
    267 			perror("ftp: setsockopt (TCP_ABORT_THRESHOLD)");
    268 		reset_timer();
    269 
    270 		error_num = connect(s, (struct sockaddr *)&remctladdr,
    271 		    sizeof (remctladdr));
    272 		oerrno = errno;
    273 		if (error_num >= 0)
    274 			break;
    275 
    276 		/*
    277 		 * Maintain message behavior: only include the address in
    278 		 * our error message if we have another one to try; if this
    279 		 * is the last address on our list, just print the error.
    280 		 */
    281 		if (ai->ai_next != NULL) {
    282 			(void) fprintf(stderr, "ftp: connect to address %s: ",
    283 			    inet_ntop_native(ai->ai_addr->sa_family,
    284 			    (void *)ai->ai_addr, abuf, sizeof (abuf)));
    285 			errno = oerrno;
    286 			perror((char *)0);
    287 		} else {
    288 			perror("ftp: connect");
    289 			code = -1;
    290 			freeaddrinfo(ai_head);
    291 			goto bad;
    292 		}
    293 		ai = ai->ai_next;
    294 		(void) fprintf(stdout, "Trying %s...\n",
    295 		    inet_ntop_native(ai->ai_addr->sa_family,
    296 		    (void *)ai->ai_addr, abuf, sizeof (abuf)));
    297 		(void) close(s);
    298 
    299 	}
    300 
    301 	/* Set ipv6rem to TRUE if control connection is a native IPv6 address */
    302 	if (IN6_IS_ADDR_V4MAPPED(&remctladdr.sin6_addr))
    303 		ipv6rem = B_FALSE;
    304 	else
    305 		ipv6rem = B_TRUE;
    306 
    307 
    308 	freeaddrinfo(ai_head);
    309 	ai = NULL;
    310 
    311 	/*
    312 	 * Set passive mode flag on by default only if a native IPv6 address
    313 	 * is being used -and- the use_eprt is not set.
    314 	 */
    315 	if (ipv6rem == B_TRUE && use_eprt == 0)
    316 		passivemode = 1;
    317 
    318 	len = sizeof (myctladdr);
    319 	if (getsockname(s, (struct sockaddr *)&myctladdr, &len) < 0) {
    320 		perror("ftp: getsockname");
    321 		code = -1;
    322 		goto bad;
    323 	}
    324 	ctrl_in = fdopen(s, "r");
    325 	ctrl_out = fdopen(s, "w");
    326 	if (ctrl_in == NULL || ctrl_out == NULL) {
    327 		(void) fprintf(stderr, "ftp: fdopen failed.\n");
    328 		if (ctrl_in)
    329 			(void) fclose(ctrl_in);
    330 		if (ctrl_out)
    331 			(void) fclose(ctrl_out);
    332 		code = -1;
    333 		goto bad;
    334 	}
    335 	if (verbose)
    336 		(void) printf("Connected to %s.\n", hostname);
    337 	if (getreply(0) > 2) {	/* read startup message from server */
    338 		if (ctrl_in)
    339 			(void) fclose(ctrl_in);
    340 		if (ctrl_out)
    341 			(void) fclose(ctrl_out);
    342 		ctrl_in = ctrl_out = NULL;
    343 		ctrl_in = ctrl_out = NULL;
    344 		code = -1;
    345 		goto bad;
    346 	}
    347 	if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&on,
    348 	    sizeof (on)) < 0 && debug)
    349 		perror("ftp: setsockopt (SO_OOBINLINE)");
    350 
    351 	return (hostname);
    352 bad:
    353 	(void) close(s);
    354 	return ((char *)0);
    355 }
    356 
    357 int
    358 login(char *host)
    359 {
    360 	char tmp[80];
    361 	char *user, *pass, *acct;
    362 	int n, aflag = 0;
    363 
    364 	user = pass = acct = 0;
    365 	if (ruserpass(host, &user, &pass, &acct) < 0) {
    366 		disconnect(0, NULL);
    367 		code = -1;
    368 		return (0);
    369 	}
    370 	if (user == NULL) {
    371 		char *myname = getlogin();
    372 
    373 		if (myname == NULL) {
    374 			struct passwd *pp = getpwuid(getuid());
    375 
    376 			if (pp != NULL)
    377 				myname = pp->pw_name;
    378 		}
    379 		stop_timer();
    380 		(void) printf("Name (%s:%s): ", host,
    381 			(myname == NULL) ? "" : myname);
    382 		*tmp = '\0';
    383 		if (fgets(tmp, sizeof (tmp) - 1, stdin) != NULL)
    384 			tmp[strlen(tmp) - 1] = '\0';
    385 		if (*tmp != '\0')
    386 			user = tmp;
    387 		else if (myname != NULL)
    388 			user = myname;
    389 		else
    390 			return (0);
    391 	}
    392 	n = command("USER %s", user);
    393 	if (n == CONTINUE) {
    394 		int oldclevel;
    395 		if (pass == NULL)
    396 			pass = mygetpass("Password:");
    397 		oldclevel = clevel;
    398 		clevel = PROT_P;
    399 		n = command("PASS %s", pass);
    400 		/* level may have changed */
    401 		if (clevel == PROT_P)
    402 			clevel = oldclevel;
    403 	}
    404 	if (n == CONTINUE) {
    405 		aflag++;
    406 		if (acct == NULL)
    407 			acct = mygetpass("Account:");
    408 		n = command("ACCT %s", acct);
    409 	}
    410 	if (n != COMPLETE) {
    411 		(void) fprintf(stderr, "Login failed.\n");
    412 		return (0);
    413 	}
    414 	if (!aflag && acct != NULL)
    415 		(void) command("ACCT %s", acct);
    416 	if (proxy)
    417 		return (1);
    418 	for (n = 0; n < macnum; ++n) {
    419 		if (strcmp("init", macros[n].mac_name) == 0) {
    420 			(void) strlcpy(line, "$init", sizeof (line));
    421 			makeargv();
    422 			domacro(margc, margv);
    423 			break;
    424 		}
    425 	}
    426 	return (1);
    427 }
    428 
    429 /*ARGSUSED*/
    430 static void
    431 cmdabort(int sig)
    432 {
    433 	(void) printf("\n");
    434 	(void) fflush(stdout);
    435 	abrtflag++;
    436 	if (ptflag)
    437 		longjmp(ptabort, 1);
    438 }
    439 
    440 int
    441 command(char *fmt, ...)
    442 {
    443 	int r;
    444 	void (*oldintr)();
    445 	va_list ap;
    446 	char command_buf[FTPBUFSIZ];
    447 
    448 	va_start(ap, fmt);
    449 	abrtflag = 0;
    450 	if (debug) {
    451 		(void) printf("---> ");
    452 		if (strncmp("PASS ", fmt, 5) == 0)
    453 			(void) printf("PASS XXXX");
    454 		else if (strncmp("ACCT ", fmt, 5) == 0)
    455 			(void) printf("ACCT XXXX");
    456 		else
    457 			(void) vfprintf(stdout, fmt, ap);
    458 		(void) printf("\n");
    459 		(void) fflush(stdout);
    460 	}
    461 	if (ctrl_out == NULL) {
    462 		perror("No control connection for command");
    463 		code = -1;
    464 		return (0);
    465 	}
    466 	oldintr = signal(SIGINT, cmdabort);
    467 	(void) vsnprintf(command_buf, FTPBUFSIZ, fmt, ap);
    468 	va_end(ap);
    469 
    470 again:	if (secure_command(command_buf) == 0)
    471 		return (0);
    472 
    473 	cpend = 1;
    474 	r = getreply(strcmp(fmt, "QUIT") == 0);
    475 
    476 	if (r == 533 && clevel == PROT_P) {
    477 		(void) fprintf(stderr, "ENC command not supported at server; "
    478 			"retrying under MIC...\n");
    479 		clevel = PROT_S;
    480 		goto again;
    481 	}
    482 
    483 	if (abrtflag && oldintr != SIG_IGN)
    484 		(*oldintr)();
    485 	(void) signal(SIGINT, oldintr);
    486 	return (r);
    487 }
    488 
    489 /* Need to save reply reponse from server for use in EPSV mode */
    490 char reply_string[BUFSIZ];
    491 
    492 int
    493 getreply(int expecteof)
    494 {
    495 	/*
    496 	 * 'code' is the 3 digit reply code, form xyz
    497 	 * 'dig'  counts the number of digits we are along in the code
    498 	 * 'n'	is the first digit of 'code'
    499 	 *	4yz: resource unavailable
    500 	 *	5yz: an error occurred, failure
    501 	 *	6yz: protected reply (is_base64 == TRUE)
    502 	 *		631 - base 64 encoded safe message
    503 	 * 		632 - base 64 encoded private message
    504 	 * 		633 - base 64 encoded confidential message
    505 	 * 'c'	is a wide char type, for international char sets
    506 	 */
    507 	wint_t c;
    508 	int i, n;
    509 	int dig;
    510 	int originalcode = 0, continuation = 0;
    511 	void (*oldintr)();
    512 	int pflag = 0;
    513 	char *pt = pasv;
    514 	/*
    515 	 * this is the input and output buffers needed for
    516 	 * radix_encode()
    517 	 */
    518 	unsigned char ibuf[FTPBUFSIZ];
    519 	unsigned char obuf[FTPBUFSIZ];
    520 	boolean_t is_base64;
    521 	int len;
    522 	char *cp;
    523 
    524 	if (!ctrl_in)
    525 		return (0);
    526 	oldintr = signal(SIGINT, cmdabort);
    527 
    528 	ibuf[0] = '\0';
    529 
    530 	if (reply_parse)
    531 		reply_ptr = reply_buf;
    532 
    533 	for (;;) {
    534 		obuf[0] = '\0';
    535 		dig = n = code = 0;
    536 		i = is_base64 = 0;
    537 		cp = reply_string;
    538 		reset_timer();	/* once per line */
    539 
    540 		while ((c = ibuf[0] ?
    541 		    (wint_t)ibuf[i++] : fgetwc(ctrl_in)) != '\n') {
    542 
    543 		    if (i >= FTPBUFSIZ)
    544 			break;
    545 
    546 		    if (c == IAC) {	/* handle telnet commands */
    547 			switch (c = fgetwc(ctrl_in)) {
    548 			    case WILL:
    549 			    case WONT:
    550 				c = fgetwc(ctrl_in);
    551 				(void) fprintf(ctrl_out, "%c%c%wc", IAC,
    552 				    WONT, c);
    553 				(void) fflush(ctrl_out);
    554 				break;
    555 			    case DO:
    556 			    case DONT:
    557 				c = fgetwc(ctrl_in);
    558 				(void) fprintf(ctrl_out, "%c%c%wc", IAC,
    559 				    DONT, c);
    560 				(void) fflush(ctrl_out);
    561 				break;
    562 			    default:
    563 				break;
    564 			}
    565 			continue;
    566 		    }
    567 		    dig++;
    568 		    if (c == EOF) {
    569 			if (expecteof) {
    570 				(void) signal(SIGINT, oldintr);
    571 				code = 221;
    572 				return (0);
    573 			}
    574 			lostpeer(0);
    575 			if (verbose) {
    576 				(void) printf(
    577 				    "421 Service not available, remote"
    578 				    " server has closed connection\n");
    579 			} else
    580 				(void) printf("Lost connection\n");
    581 			(void) fflush(stdout);
    582 			code = 421;
    583 			return (4);
    584 		    }
    585 		    if (n == 0)
    586 			n = c;
    587 
    588 		    if (n == '6')
    589 			is_base64 = 1;
    590 
    591 		    if ((auth_type != AUTHTYPE_NONE) && !ibuf[0] &&
    592 			(is_base64 || continuation))  {
    593 			/* start storing chars in obuf */
    594 			if (c != '\r' && dig > 4)
    595 				obuf[i++] = (char)c;
    596 		    } else {
    597 			if ((auth_type != AUTHTYPE_NONE) && !ibuf[0] &&
    598 			    dig == 1 && verbose)
    599 			    (void) printf("Unauthenticated reply received "
    600 				"from server:\n");
    601 			if (reply_parse)
    602 				*reply_ptr++ = (char)c;
    603 			if (c != '\r' && (verbose > 0 ||
    604 			    (verbose > -1 && n == '5' && dig > 4))) {
    605 				if (proxflag &&
    606 				    (dig == 1 || dig == 5 && verbose == 0))
    607 					(void) printf("%s:", hostname);
    608 				(void) putwchar(c);
    609 			}
    610 		    } /* endif auth_type && !ibuf[0] ... */
    611 
    612 		    if ((auth_type != AUTHTYPE_NONE) && !ibuf[0] && !is_base64)
    613 			continue;
    614 
    615 		    /* we are still extracting the 3 digit code */
    616 		    if (dig < 4 && isascii(c) && isdigit(c))
    617 			code = code * 10 + (c - '0');
    618 
    619 		    /* starting passive mode */
    620 		    if (!pflag && code == 227)
    621 			pflag = 1;
    622 
    623 		    /* start to store characters, when dig > 4 */
    624 		    if (dig > 4 && pflag == 1 && isascii(c) && isdigit(c))
    625 			pflag = 2;
    626 		    if (pflag == 2) {
    627 			if (c != '\r' && c != ')') {
    628 				/* the mb array is to deal with the wchar_t */
    629 				char mb[MB_LEN_MAX];
    630 				int avail;
    631 
    632 				/*
    633 				 * space available in pasv[], accounting
    634 				 * for trailing NULL
    635 				 */
    636 				avail = &pasv[sizeof (pasv)] - pt - 1;
    637 
    638 				len = wctomb(mb, c);
    639 				if (len <= 0 && avail > 0) {
    640 					*pt++ = (unsigned char)c;
    641 				} else if (len > 0 && avail >= len) {
    642 					bcopy(mb, pt, (size_t)len);
    643 					pt += len;
    644 				} else {
    645 					/*
    646 					 * no room in pasv[];
    647 					 * close connection
    648 					 */
    649 					(void) printf("\nReply too long - "
    650 					    "closing connection\n");
    651 					lostpeer(0);
    652 					(void) fflush(stdout);
    653 					(void) signal(SIGINT, oldintr);
    654 					return (4);
    655 				}
    656 			} else {
    657 				*pt = '\0';
    658 				pflag = 3;
    659 			}
    660 		    } /* endif pflag == 2 */
    661 		    if (dig == 4 && c == '-' && !is_base64) {
    662 			if (continuation)
    663 				code = 0;
    664 			continuation++;
    665 		    }
    666 		    if (cp < &reply_string[sizeof (reply_string) - 1])
    667 			*cp++ = c;
    668 
    669 		} /* end while */
    670 
    671 		if ((auth_type != AUTHTYPE_NONE) && !ibuf[0] && !is_base64)
    672 			return (getreply(expecteof));
    673 
    674 		ibuf[0] = obuf[i] = '\0';
    675 
    676 		if (code && is_base64) {
    677 		    boolean_t again = 0;
    678 		    n = decode_reply(ibuf, sizeof (ibuf), obuf, n, &again);
    679 		    if (again)
    680 			continue;
    681 		} else
    682 
    683 		if (verbose > 0 || verbose > -1 && n == '5') {
    684 			(void) putwchar(c);
    685 			(void) fflush(stdout);
    686 		}
    687 
    688 		if (continuation && code != originalcode) {
    689 			ibuf[0] = obuf[i] = '\0';
    690 			if (originalcode == 0)
    691 				originalcode = code;
    692 			continue;
    693 		}
    694 		*cp = '\0';
    695 		if (n != '1')
    696 			cpend = 0;
    697 		(void) signal(SIGINT, oldintr);
    698 		if (code == 421 || originalcode == 421)
    699 			lostpeer(0);
    700 		if (abrtflag && oldintr != cmdabort && oldintr != SIG_IGN)
    701 			(*oldintr)();
    702 
    703 		if (reply_parse) {
    704 		    *reply_ptr = '\0';
    705 		    if (reply_ptr = strstr(reply_buf, reply_parse)) {
    706 			reply_parse = reply_ptr + strlen(reply_parse);
    707 			if (reply_ptr = strpbrk(reply_parse, " \r"))
    708 				*reply_ptr = '\0';
    709 		    } else
    710 			reply_parse = reply_ptr;
    711 		}
    712 
    713 		return (n - '0');
    714 	} /* end for */
    715 }
    716 
    717 static int
    718 empty(struct fd_set *mask, int sec, int nfds)
    719 {
    720 	struct timeval t;
    721 
    722 	reset_timer();
    723 	t.tv_sec = (time_t)sec;
    724 	t.tv_usec = 0;
    725 	return (select(nfds, mask, NULL, NULL, &t));
    726 }
    727 
    728 /*ARGSUSED*/
    729 static void
    730 abortsend(int sig)
    731 {
    732 	mflag = 0;
    733 	abrtflag = 0;
    734 	(void) printf("\nsend aborted\n");
    735 	(void) fflush(stdout);
    736 	longjmp(sendabort, 1);
    737 }
    738 
    739 void
    740 sendrequest(char *cmd, char *local, char *remote, int allowpipe)
    741 {
    742 	FILE *fin, *dout = 0;
    743 	int (*closefunc)();
    744 	void (*oldintr)(), (*oldintp)();
    745 	off_t bytes = 0, hashbytes = HASHSIZ;
    746 	int c;
    747 	/*
    748 	 * d >=	 0 if there is no error
    749 	 *	-1 if there was a normal file i/o error
    750 	 *	-2 if there was a security error
    751 	 */
    752 	int d;
    753 	struct stat st;
    754 	hrtime_t start, stop;
    755 	char *dmode;
    756 
    757 	if (proxy) {
    758 		proxtrans(cmd, local, remote);
    759 		return;
    760 	}
    761 	closefunc = NULL;
    762 	oldintr = NULL;
    763 	oldintp = NULL;
    764 	dmode = "w";
    765 	if (setjmp(sendabort)) {
    766 		while (cpend) {
    767 			(void) getreply(0);
    768 		}
    769 		if (data >= 0) {
    770 			(void) close(data);
    771 			data = -1;
    772 		}
    773 		if (oldintr)
    774 			(void) signal(SIGINT, oldintr);
    775 		if (oldintp)
    776 			(void) signal(SIGPIPE, oldintp);
    777 		code = -1;
    778 		restart_point = 0;
    779 		return;
    780 	}
    781 	oldintr = signal(SIGINT, abortsend);
    782 	if (strcmp(local, "-") == 0)
    783 		fin = stdin;
    784 	else if (allowpipe && *local == '|') {
    785 		oldintp = signal(SIGPIPE, SIG_IGN);
    786 		fin = mypopen(local + 1, "r");
    787 		if (fin == NULL) {
    788 			perror(local + 1);
    789 			(void) signal(SIGINT, oldintr);
    790 			(void) signal(SIGPIPE, oldintp);
    791 			code = -1;
    792 			restart_point = 0;
    793 			return;
    794 		}
    795 		closefunc = mypclose;
    796 	} else {
    797 		fin = fopen(local, "r");
    798 		if (fin == NULL) {
    799 			perror(local);
    800 			(void) signal(SIGINT, oldintr);
    801 			code = -1;
    802 			restart_point = 0;
    803 			return;
    804 		}
    805 		closefunc = fclose;
    806 		if (fstat(fileno(fin), &st) < 0 ||
    807 		    (st.st_mode&S_IFMT) != S_IFREG) {
    808 			(void) fprintf(stdout,
    809 				"%s: not a plain file.\n", local);
    810 			(void) signal(SIGINT, oldintr);
    811 			code = -1;
    812 			(void) fclose(fin);
    813 			restart_point = 0;
    814 			return;
    815 		}
    816 	}
    817 	if (initconn()) {
    818 		(void) signal(SIGINT, oldintr);
    819 		if (oldintp)
    820 			(void) signal(SIGPIPE, oldintp);
    821 		code = -1;
    822 		if (closefunc != NULL)
    823 			(*closefunc)(fin);
    824 		restart_point = 0;
    825 		return;
    826 	}
    827 	if (setjmp(sendabort))
    828 		goto abort;
    829 	if ((restart_point > 0) &&
    830 	    (strcmp(cmd, "STOR") == 0 || strcmp(cmd, "APPE") == 0)) {
    831 		if (fseeko(fin, restart_point, SEEK_SET) < 0) {
    832 			perror(local);
    833 			if (closefunc != NULL)
    834 				(*closefunc)(fin);
    835 			restart_point = 0;
    836 			return;
    837 		}
    838 		if (command("REST %lld", (longlong_t)restart_point)
    839 			!= CONTINUE) {
    840 			if (closefunc != NULL)
    841 				(*closefunc)(fin);
    842 			restart_point = 0;
    843 			return;
    844 		}
    845 		dmode = "r+w";
    846 	}
    847 	restart_point = 0;
    848 	if (remote) {
    849 		if (command("%s %s", cmd, remote) != PRELIM) {
    850 			(void) signal(SIGINT, oldintr);
    851 			if (oldintp)
    852 				(void) signal(SIGPIPE, oldintp);
    853 			if (closefunc != NULL)
    854 				(*closefunc)(fin);
    855 			if (data >= 0) {
    856 				(void) close(data);
    857 				data = -1;
    858 			}
    859 			return;
    860 		}
    861 	} else
    862 		if (command("%s", cmd) != PRELIM) {
    863 			(void) signal(SIGINT, oldintr);
    864 			if (oldintp)
    865 				(void) signal(SIGPIPE, oldintp);
    866 			if (closefunc != NULL)
    867 				(*closefunc)(fin);
    868 			if (data >= 0) {
    869 				(void) close(data);
    870 				data = -1;
    871 			}
    872 			return;
    873 		}
    874 	dout = dataconn(dmode);
    875 	if (dout == NULL)
    876 		goto abort;
    877 	stop_timer();
    878 	oldintp = signal(SIGPIPE, SIG_IGN);
    879 	start = gethrtime();
    880 	switch (type) {
    881 
    882 	case TYPE_I:
    883 	case TYPE_L:
    884 		errno = d = 0;
    885 		while ((c = read(fileno(fin), buf, FTPBUFSIZ)) > 0) {
    886 			if ((d = WRITE(fileno(dout), buf, c)) < 0)
    887 				break;
    888 			bytes += c;
    889 			if (hash) {
    890 				while (bytes >= hashbytes) {
    891 					(void) putchar('#');
    892 					hashbytes += HASHSIZ;
    893 				}
    894 				(void) fflush(stdout);
    895 			}
    896 		}
    897 		if (hash && bytes > 0) {
    898 			if (bytes < hashbytes)
    899 				(void) putchar('#');
    900 			(void) putchar('\n');
    901 			(void) fflush(stdout);
    902 		}
    903 		if (c < 0)
    904 			perror(local);
    905 
    906 		if (d >= 0)
    907 			d = secure_flush(fileno(dout));
    908 
    909 		if (d < 0) {
    910 			if ((d == -1) && (errno != EPIPE))
    911 				perror("netout");
    912 			bytes = -1;
    913 		}
    914 		break;
    915 
    916 	case TYPE_A:
    917 		while ((c = getc(fin)) != EOF) {
    918 			if (c == '\n') {
    919 				while (hash && (bytes >= hashbytes)) {
    920 					(void) putchar('#');
    921 					(void) fflush(stdout);
    922 					hashbytes += HASHSIZ;
    923 				}
    924 				if (ferror(dout) || PUTC('\r', dout) < 0)
    925 					break;
    926 				bytes++;
    927 			}
    928 
    929 			if (PUTC(c, dout) < 0)
    930 				break;
    931 			bytes++;
    932 #ifdef notdef
    933 			if (c == '\r') {
    934 				/* this violates rfc */
    935 				(void) PUTC('\0', dout);
    936 				bytes++;
    937 			}
    938 #endif
    939 		}
    940 		if (hash && bytes > 0) {
    941 			if (bytes < hashbytes)
    942 				(void) putchar('#');
    943 			(void) putchar('\n');
    944 			(void) fflush(stdout);
    945 		}
    946 		if (ferror(fin))
    947 			perror(local);
    948 
    949 		d = ferror(dout) ? -1 : 0;
    950 		if (d == 0)
    951 			d = secure_flush(fileno(dout));
    952 
    953 		if (d < 0) {
    954 			if ((d == -1) && (errno != EPIPE))
    955 				perror("netout");
    956 			bytes = -1;
    957 		}
    958 		break;
    959 	}
    960 	reset_timer();
    961 	if (closefunc != NULL)
    962 		(*closefunc)(fin);
    963 	if (ctrl_in != NULL) {
    964 		int	dfn	= fileno(dout);
    965 		int	nfds	= fileno(ctrl_in);
    966 		fd_set  mask;
    967 
    968 		/*
    969 		 * There could be data not yet written to dout,
    970 		 * in the stdio buffer; so, before a shutdown()
    971 		 * on further sends, do fflush(dout)
    972 		 */
    973 		(void) fflush(dout);
    974 
    975 		/* sending over; shutdown sending on dfn */
    976 		(void) shutdown(dfn, SHUT_WR);
    977 		FD_ZERO(&mask);
    978 		FD_SET(dfn, &mask);
    979 		FD_SET(nfds, &mask);
    980 		nfds = MAX(dfn, nfds);
    981 
    982 		/*
    983 		 * Wait for remote end to either close data socket
    984 		 * or ack that we've closed our end; it doesn't
    985 		 * matter which happens first.
    986 		 */
    987 		(void) select(nfds + 1, &mask, NULL, NULL, NULL);
    988 	}
    989 	(void) fclose(dout); data = -1;
    990 	stop = gethrtime();
    991 	(void) getreply(0);
    992 	(void) signal(SIGINT, oldintr);
    993 	if (oldintp)
    994 		(void) signal(SIGPIPE, oldintp);
    995 
    996 	/*
    997 	 * Only print the transfer successful message if the code returned
    998 	 * from remote is 226 or 250. All other codes are error codes.
    999 	 */
   1000 	if ((bytes > 0) && verbose && ((code == 226) || (code == 250)))
   1001 		ptransfer("sent", bytes, start, stop, local, remote);
   1002 	if (!ctrl_in)
   1003 		(void) printf("Lost connection\n");
   1004 	return;
   1005 abort:
   1006 	(void) signal(SIGINT, oldintr);
   1007 	if (oldintp)
   1008 		(void) signal(SIGPIPE, oldintp);
   1009 	if (!cpend) {
   1010 		code = -1;
   1011 		return;
   1012 	}
   1013 	if (data >= 0) {
   1014 		(void) close(data);
   1015 		data = -1;
   1016 	}
   1017 	if (dout) {
   1018 		(void) fclose(dout);
   1019 		data = -1;
   1020 	}
   1021 	(void) getreply(0);
   1022 	code = -1;
   1023 	if (closefunc != NULL && fin != NULL)
   1024 		(*closefunc)(fin);
   1025 	stop = gethrtime();
   1026 	/*
   1027 	 * Only print the transfer successful message if the code returned
   1028 	 * from remote is 226 or 250. All other codes are error codes.
   1029 	 */
   1030 	if ((bytes > 0) && verbose && ((code == 226) || (code == 250)))
   1031 		ptransfer("sent", bytes, start, stop, local, remote);
   1032 	if (!ctrl_in)
   1033 		(void) printf("Lost connection\n");
   1034 	restart_point = 0;
   1035 }
   1036 
   1037 /*ARGSUSED*/
   1038 static void
   1039 abortrecv(int sig)
   1040 {
   1041 	mflag = 0;
   1042 	abrtflag = 0;
   1043 	(void) printf("\n");
   1044 	(void) fflush(stdout);
   1045 	longjmp(recvabort, 1);
   1046 }
   1047 
   1048 void
   1049 recvrequest(char *cmd, char *local, char *remote, char *mode, int allowpipe)
   1050 {
   1051 	FILE *fout, *din = 0;
   1052 	int (*closefunc)();
   1053 	void (*oldintr)(), (*oldintp)();
   1054 	int oldverbose, oldtype = 0, tcrflag, nfnd;
   1055 	char msg;
   1056 	off_t bytes = 0, hashbytes = HASHSIZ;
   1057 	struct fd_set mask;
   1058 	int c, d, n;
   1059 	hrtime_t start, stop;
   1060 	int errflg = 0;
   1061 	int infd;
   1062 	int nfds;
   1063 	int retrcmd;
   1064 
   1065 	retrcmd = (strcmp(cmd, "RETR") == 0);
   1066 	if (proxy && retrcmd) {
   1067 		proxtrans(cmd, local, remote);
   1068 		return;
   1069 	}
   1070 	closefunc = NULL;
   1071 	oldintr = NULL;
   1072 	oldintp = NULL;
   1073 	tcrflag = !crflag && retrcmd;
   1074 	if (setjmp(recvabort)) {
   1075 		while (cpend) {
   1076 			(void) getreply(0);
   1077 		}
   1078 		if (data >= 0) {
   1079 			(void) close(data);
   1080 			data = -1;
   1081 		}
   1082 		if (oldintr)
   1083 			(void) signal(SIGINT, oldintr);
   1084 		code = -1;
   1085 		return;
   1086 	}
   1087 	oldintr = signal(SIGINT, abortrecv);
   1088 	if (local != NULL &&
   1089 	    strcmp(local, "-") != 0 &&
   1090 	    (*local != '|' || !allowpipe)) {
   1091 		if (access(local, W_OK) < 0) {
   1092 			char *dir = rindex(local, '/');
   1093 			int file_errno = errno;
   1094 
   1095 			if (file_errno != ENOENT && file_errno != EACCES) {
   1096 				perror(local);
   1097 				(void) signal(SIGINT, oldintr);
   1098 				code = -1;
   1099 				return;
   1100 			}
   1101 			if ((dir != NULL) && (dir != local))
   1102 				*dir = 0;
   1103 			if (dir == local)
   1104 				d = access("/", W_OK);
   1105 			else
   1106 				d = access(dir ? local : ".", W_OK);
   1107 			if ((dir != NULL) && (dir != local))
   1108 				*dir = '/';
   1109 			if (d < 0) {
   1110 				perror(local);
   1111 				(void) signal(SIGINT, oldintr);
   1112 				code = -1;
   1113 				return;
   1114 			}
   1115 			if (!runique && file_errno == EACCES) {
   1116 				errno = file_errno;
   1117 				perror(local);
   1118 				(void) signal(SIGINT, oldintr);
   1119 				code = -1;
   1120 				return;
   1121 			}
   1122 			if (runique && file_errno == EACCES &&
   1123 			    (local = gunique(local)) == NULL) {
   1124 				(void) signal(SIGINT, oldintr);
   1125 				code = -1;
   1126 				return;
   1127 			}
   1128 		} else if (runique && (local = gunique(local)) == NULL) {
   1129 			(void) signal(SIGINT, oldintr);
   1130 			code = -1;
   1131 			return;
   1132 		}
   1133 	}
   1134 	if (initconn()) {
   1135 		(void) signal(SIGINT, oldintr);
   1136 		code = -1;
   1137 		return;
   1138 	}
   1139 	if (setjmp(recvabort))
   1140 		goto abort;
   1141 	if (!retrcmd && type != TYPE_A) {
   1142 		oldtype = type;
   1143 		oldverbose = verbose;
   1144 		if (!debug)
   1145 			verbose = 0;
   1146 		setascii(0, NULL);
   1147 		verbose = oldverbose;
   1148 	}
   1149 	if ((restart_point > 0) && retrcmd &&
   1150 	    command("REST %lld", (longlong_t)restart_point) != CONTINUE) {
   1151 		return;
   1152 	}
   1153 	if (remote) {
   1154 		if (command("%s %s", cmd, remote) != PRELIM) {
   1155 			(void) signal(SIGINT, oldintr);
   1156 			if (oldtype) {
   1157 				if (!debug)
   1158 					verbose = 0;
   1159 				switch (oldtype) {
   1160 					case TYPE_I:
   1161 						setbinary(0, NULL);
   1162 						break;
   1163 					case TYPE_E:
   1164 						setebcdic(0, NULL);
   1165 						break;
   1166 					case TYPE_L:
   1167 						settenex(0, NULL);
   1168 						break;
   1169 				}
   1170 				verbose = oldverbose;
   1171 			}
   1172 			return;
   1173 		}
   1174 	} else {
   1175 		if (command("%s", cmd) != PRELIM) {
   1176 			(void) signal(SIGINT, oldintr);
   1177 			if (oldtype) {
   1178 				if (!debug)
   1179 					verbose = 0;
   1180 				switch (oldtype) {
   1181 					case TYPE_I:
   1182 						setbinary(0, NULL);
   1183 						break;
   1184 					case TYPE_E:
   1185 						setebcdic(0, NULL);
   1186 						break;
   1187 					case TYPE_L:
   1188 						settenex(0, NULL);
   1189 						break;
   1190 				}
   1191 				verbose = oldverbose;
   1192 			}
   1193 			return;
   1194 		}
   1195 	}
   1196 	din = dataconn("r");
   1197 	if (din == NULL)
   1198 		goto abort;
   1199 
   1200 	if (local == NULL) {
   1201 		fout = tmp_nlst;
   1202 	} else if (strcmp(local, "-") == 0) {
   1203 		fout = stdout;
   1204 	} else if (allowpipe && *local == '|') {
   1205 		oldintp = signal(SIGPIPE, SIG_IGN);
   1206 		fout = mypopen(local + 1, "w");
   1207 		if (fout == NULL) {
   1208 			perror(local+1);
   1209 			goto abort;
   1210 		}
   1211 		closefunc = mypclose;
   1212 	} else {
   1213 		fout = fopen(local, mode);
   1214 		if (fout == NULL) {
   1215 			perror(local);
   1216 			goto abort;
   1217 		}
   1218 		closefunc = fclose;
   1219 	}
   1220 	start = gethrtime();
   1221 	stop_timer();
   1222 	switch (type) {
   1223 
   1224 	case TYPE_I:
   1225 	case TYPE_L:
   1226 		if ((restart_point > 0) && retrcmd &&
   1227 		    lseek(fileno(fout), restart_point, SEEK_SET) < 0) {
   1228 			perror(local);
   1229 			goto abort;
   1230 		}
   1231 		errno = d = 0;
   1232 		infd = fileno(din);
   1233 		while ((c = timedread(infd, buf, FTPBUFSIZ, timeout)) > 0) {
   1234 			for (n = 0; n < c; n += d) {
   1235 				d = write(fileno(fout), &buf[n], c - n);
   1236 				if (d == -1)
   1237 					goto writeerr;
   1238 			}
   1239 			bytes += c;
   1240 			if (hash) {
   1241 				while (bytes >= hashbytes) {
   1242 					(void) putchar('#');
   1243 					hashbytes += HASHSIZ;
   1244 				}
   1245 				(void) fflush(stdout);
   1246 			}
   1247 		}
   1248 		if (hash && bytes > 0) {
   1249 			if (bytes < hashbytes)
   1250 				(void) putchar('#');
   1251 			(void) putchar('\n');
   1252 			(void) fflush(stdout);
   1253 		}
   1254 		if (c < 0) {
   1255 			errflg = 1;
   1256 			perror("netin");
   1257 		}
   1258 		if ((d < 0) || ((c == 0) && (fsync(fileno(fout)) == -1))) {
   1259 writeerr:
   1260 			errflg = 1;
   1261 			perror(local);
   1262 		}
   1263 		break;
   1264 
   1265 	case TYPE_A:
   1266 		if ((restart_point > 0) && retrcmd) {
   1267 			int c;
   1268 			off_t i = 0;
   1269 
   1270 			if (fseek(fout, 0L, SEEK_SET) < 0) {
   1271 				perror(local);
   1272 				goto abort;
   1273 			}
   1274 			while (i++ < restart_point) {
   1275 				if ((c = getc(fout)) == EOF) {
   1276 					if (ferror(fout))
   1277 						perror(local);
   1278 					else
   1279 						(void) fprintf(stderr,
   1280 						"%s: Unexpected end of file\n",
   1281 							local);
   1282 					goto abort;
   1283 				}
   1284 				if (c == '\n')
   1285 					i++;
   1286 			}
   1287 			if (fseeko(fout, 0L, SEEK_CUR) < 0) {
   1288 				perror(local);
   1289 				goto abort;
   1290 			}
   1291 		}
   1292 		fdio_setbuf(buf, FTPBUFSIZ);
   1293 		infd = fileno(din);
   1294 		while ((c = fdio_getc(infd)) != EOF) {
   1295 			while (c == '\r') {
   1296 				while (hash && (bytes >= hashbytes)) {
   1297 					(void) putchar('#');
   1298 					(void) fflush(stdout);
   1299 					hashbytes += HASHSIZ;
   1300 				}
   1301 				bytes++;
   1302 
   1303 				if ((c = fdio_getc(infd)) != '\n' || tcrflag) {
   1304 					if (ferror(fout))
   1305 						break;
   1306 					if (putc('\r', fout) == EOF)
   1307 						goto writer_ascii_err;
   1308 				}
   1309 #ifdef notdef
   1310 				if (c == '\0') {
   1311 					bytes++;
   1312 					continue;
   1313 				}
   1314 #endif
   1315 				if (c == EOF)
   1316 					goto endread;
   1317 			}
   1318 			if (putc(c, fout) == EOF)
   1319 				goto writer_ascii_err;
   1320 			bytes++;
   1321 		}
   1322 endread:
   1323 		if (hash && bytes > 0) {
   1324 			if (bytes < hashbytes)
   1325 				(void) putchar('#');
   1326 			(void) putchar('\n');
   1327 			(void) fflush(stdout);
   1328 		}
   1329 		if (fdio_error(infd)) {
   1330 			errflg = 1;
   1331 			perror("netin");
   1332 		}
   1333 		if ((fflush(fout) == EOF) || ferror(fout) ||
   1334 			(fsync(fileno(fout)) == -1)) {
   1335 writer_ascii_err:
   1336 			errflg = 1;
   1337 			perror(local);
   1338 		}
   1339 		break;
   1340 	}
   1341 	reset_timer();
   1342 	if (closefunc != NULL)
   1343 		(*closefunc)(fout);
   1344 	(void) signal(SIGINT, oldintr);
   1345 	if (oldintp)
   1346 		(void) signal(SIGPIPE, oldintp);
   1347 	(void) fclose(din); data = -1;
   1348 	stop = gethrtime();
   1349 	(void) getreply(0);
   1350 	if (bytes > 0 && verbose && !errflg)
   1351 		ptransfer("received", bytes, start, stop, local, remote);
   1352 	if (!ctrl_in)
   1353 		(void) printf("Lost connection\n");
   1354 	if (oldtype) {
   1355 		if (!debug)
   1356 			verbose = 0;
   1357 		switch (oldtype) {
   1358 			case TYPE_I:
   1359 				setbinary(0, NULL);
   1360 				break;
   1361 			case TYPE_E:
   1362 				setebcdic(0, NULL);
   1363 				break;
   1364 			case TYPE_L:
   1365 				settenex(0, NULL);
   1366 				break;
   1367 		}
   1368 		verbose = oldverbose;
   1369 	}
   1370 	return;
   1371 abort:
   1372 
   1373 /* abort using RFC959 recommended IP, SYNC sequence  */
   1374 
   1375 	stop = gethrtime();
   1376 	if (oldintp)
   1377 		(void) signal(SIGPIPE, oldintp);
   1378 	(void) signal(SIGINT, SIG_IGN);
   1379 	if (!cpend) {
   1380 		code = -1;
   1381 		(void) signal(SIGINT, oldintr);
   1382 		return;
   1383 	}
   1384 
   1385 	(void) fprintf(ctrl_out, "%c%c", IAC, IP);
   1386 	(void) fflush(ctrl_out);
   1387 	msg = (char)IAC;
   1388 	/*
   1389 	 * send IAC in urgent mode instead of DM because UNIX places oob
   1390 	 * mark after urgent byte rather than before as now is protocol
   1391 	 */
   1392 	if (send(fileno(ctrl_out), &msg, 1, MSG_OOB) != 1) {
   1393 		perror("abort");
   1394 	}
   1395 	(void) fprintf(ctrl_out, "%cABOR\r\n", DM);
   1396 	(void) fflush(ctrl_out);
   1397 	nfds = fileno(ctrl_in) + 1;
   1398 	FD_ZERO(&mask);
   1399 	FD_SET(fileno(ctrl_in), &mask);
   1400 	if (din) {
   1401 		FD_SET(fileno(din), &mask);
   1402 		nfds = MAX(fileno(din) + 1, nfds);
   1403 	}
   1404 	if ((nfnd = empty(&mask, 10, nfds)) <= 0) {
   1405 		if (nfnd < 0) {
   1406 			perror("abort");
   1407 		}
   1408 		code = -1;
   1409 		lostpeer(0);
   1410 	}
   1411 	if (din && FD_ISSET(fileno(din), &mask)) {
   1412 		do {
   1413 			reset_timer();
   1414 		} while ((c = read(fileno(din), buf, FTPBUFSIZ)) > 0);
   1415 	}
   1416 	if ((c = getreply(0)) == ERROR && code == 552) {
   1417 		/* needed for nic style abort */
   1418 		if (data >= 0) {
   1419 			(void) close(data);
   1420 			data = -1;
   1421 		}
   1422 		(void) getreply(0);
   1423 	}
   1424 	if (oldtype) {
   1425 		if (!debug)
   1426 			verbose = 0;
   1427 		switch (oldtype) {
   1428 		case TYPE_I:
   1429 			setbinary(0, NULL);
   1430 			break;
   1431 		case TYPE_E:
   1432 			setebcdic(0, NULL);
   1433 			break;
   1434 		case TYPE_L:
   1435 			settenex(0, NULL);
   1436 			break;
   1437 		}
   1438 		verbose = oldverbose;
   1439 	}
   1440 	(void) getreply(0);
   1441 	code = -1;
   1442 	if (data >= 0) {
   1443 		(void) close(data);
   1444 		data = -1;
   1445 	}
   1446 	if (closefunc != NULL && fout != NULL)
   1447 		(*closefunc)(fout);
   1448 	if (din) {
   1449 		(void) fclose(din);
   1450 		data = -1;
   1451 	}
   1452 	if (bytes > 0 && verbose)
   1453 		ptransfer("received", bytes, start, stop, local, remote);
   1454 	if (!ctrl_in)
   1455 		(void) printf("Lost connection\n");
   1456 	(void) signal(SIGINT, oldintr);
   1457 }
   1458 
   1459 /*
   1460  * Need to start a listen on the data channel
   1461  * before we send the command, otherwise the
   1462  * server's connect may fail.
   1463  */
   1464 
   1465 static int
   1466 initconn(void)
   1467 {
   1468 	unsigned char *p, *a;
   1469 	int result, tmpno = 0;
   1470 	int on = 1;
   1471 	socklen_t len;
   1472 	int v4_addr;
   1473 	char *c, *c2, delm;
   1474 	in_port_t ports;
   1475 
   1476 	pasv_refused = B_FALSE;
   1477 	if (passivemode) {
   1478 		data = socket(AF_INET6, SOCK_STREAM, 0);
   1479 		if (data < 0) {
   1480 			perror("socket");
   1481 			return (1);
   1482 		}
   1483 		if (timeout && setsockopt(data, IPPROTO_TCP,
   1484 		    TCP_ABORT_THRESHOLD, (char *)&timeoutms,
   1485 		    sizeof (timeoutms)) < 0 && debug)
   1486 			perror("ftp: setsockopt (TCP_ABORT_THRESHOLD)");
   1487 		if ((options & SO_DEBUG) &&
   1488 		    setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on,
   1489 			    sizeof (on)) < 0)
   1490 			perror("setsockopt (ignored)");
   1491 		/*
   1492 		 * Use the system wide default send and receive buffer sizes
   1493 		 * unless one has been specified.
   1494 		 */
   1495 		if (tcpwindowsize) {
   1496 			if (setsockopt(data, SOL_SOCKET, SO_SNDBUF,
   1497 			    (char *)&tcpwindowsize, sizeof (tcpwindowsize)) < 0)
   1498 				perror("ftp: setsockopt (SO_SNDBUF - ignored)");
   1499 			if (setsockopt(data, SOL_SOCKET, SO_RCVBUF,
   1500 			    (char *)&tcpwindowsize, sizeof (tcpwindowsize)) < 0)
   1501 				perror("ftp: setsockopt (SO_RCVBUF - ignored)");
   1502 		}
   1503 
   1504 		data_addr = remctladdr;
   1505 
   1506 		if (ipv6rem == B_TRUE) {
   1507 			if (command("EPSV") != COMPLETE) {
   1508 				(void) fprintf(stderr,
   1509 					"Passive mode refused. Try EPRT\n");
   1510 				pasv_refused = B_TRUE;
   1511 				goto noport;
   1512 			}
   1513 
   1514 			/*
   1515 			 * Get the data port from reply string from the
   1516 			 * server.  The format of the reply string is:
   1517 			 * 229 Entering Extended Passive Mode (|||port|)
   1518 			 * where | is the delimiter being used.
   1519 			 */
   1520 			c = strchr(reply_string, '(');
   1521 			c2 = strchr(reply_string, ')');
   1522 			if (c == NULL || c2 == NULL) {
   1523 				(void) fprintf(stderr, "Extended passive mode"
   1524 				    "parsing failure.\n");
   1525 				goto bad;
   1526 			}
   1527 			*(c2 - 1) = NULL;
   1528 			/* Delimiter is the next char in the reply string */
   1529 			delm = *(++c);
   1530 			while (*c == delm) {
   1531 				if (!*(c++)) {
   1532 					(void) fprintf(stderr,
   1533 					    "Extended passive mode"
   1534 					    "parsing failure.\n");
   1535 					goto bad;
   1536 				}
   1537 			}
   1538 			/* assign the port for data connection */
   1539 			ports = (in_port_t)atoi(c);
   1540 			data_addr.sin6_port =  htons(ports);
   1541 		} else {
   1542 			int a1, a2, a3, a4, p1, p2;
   1543 
   1544 			if (command("PASV") != COMPLETE) {
   1545 				(void) fprintf(stderr,
   1546 					"Passive mode refused. Try PORT\n");
   1547 				pasv_refused = B_TRUE;
   1548 				goto noport;
   1549 			}
   1550 
   1551 			/*
   1552 			 * Get the data port from reply string from the
   1553 			 * server.  The format of the reply string is:
   1554 			 * 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)
   1555 			 */
   1556 			if (sscanf(pasv, "%d,%d,%d,%d,%d,%d",
   1557 					&a1, &a2, &a3, &a4, &p1, &p2) != 6) {
   1558 				(void) fprintf(stderr,
   1559 					"Passive mode parsing failure.\n");
   1560 				goto bad;
   1561 			}
   1562 			/*
   1563 			 * Set the supplied address and port in an
   1564 			 * IPv4-mapped IPv6 address.
   1565 			 */
   1566 			a = (unsigned char *)&data_addr.sin6_addr +
   1567 				sizeof (struct in6_addr) -
   1568 				sizeof (struct in_addr);
   1569 #define	UC(b)	((b)&0xff)
   1570 			a[0] = UC(a1);
   1571 			a[1] = UC(a2);
   1572 			a[2] = UC(a3);
   1573 			a[3] = UC(a4);
   1574 			p = (unsigned char *)&data_addr.sin6_port;
   1575 			p[0] = UC(p1);
   1576 			p[1] = UC(p2);
   1577 		}
   1578 
   1579 		if (connect(data, (struct sockaddr *)&data_addr,
   1580 		    sizeof (data_addr)) < 0) {
   1581 			perror("connect");
   1582 			goto bad;
   1583 		}
   1584 		return (0);
   1585 	}
   1586 
   1587 noport:
   1588 	data_addr = myctladdr;
   1589 	if (sendport)
   1590 		data_addr.sin6_port = 0;	/* let system pick one */
   1591 
   1592 	if (data != -1)
   1593 		(void) close(data);
   1594 	data = socket(AF_INET6, SOCK_STREAM, 0);
   1595 	if (data < 0) {
   1596 		perror("ftp: socket");
   1597 		if (tmpno)
   1598 			sendport = 1;
   1599 		return (1);
   1600 	}
   1601 	if (!sendport)
   1602 		if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR,
   1603 		    (char *)&on, sizeof (on)) < 0) {
   1604 			perror("ftp: setsockopt (SO_REUSEADDR)");
   1605 			goto bad;
   1606 		}
   1607 	if (bind(data,
   1608 	    (struct sockaddr *)&data_addr, sizeof (data_addr)) < 0) {
   1609 		perror("ftp: bind");
   1610 		goto bad;
   1611 	}
   1612 	if (timeout && setsockopt(data, IPPROTO_TCP, TCP_ABORT_THRESHOLD,
   1613 	    (char *)&timeoutms, sizeof (timeoutms)) < 0 && debug)
   1614 		perror("ftp: setsockopt (TCP_ABORT_THRESHOLD)");
   1615 	if (options & SO_DEBUG &&
   1616 	    setsockopt(data, SOL_SOCKET, SO_DEBUG,
   1617 	    (char *)&on, sizeof (on)) < 0)
   1618 		perror("ftp: setsockopt (SO_DEBUG - ignored)");
   1619 	/*
   1620 	 * Use the system wide default send and receive buffer sizes unless
   1621 	 * one has been specified.
   1622 	 */
   1623 	if (tcpwindowsize) {
   1624 		if (setsockopt(data, SOL_SOCKET, SO_SNDBUF,
   1625 		    (char *)&tcpwindowsize, sizeof (tcpwindowsize)) < 0)
   1626 			perror("ftp: setsockopt (SO_SNDBUF - ignored)");
   1627 		if (setsockopt(data, SOL_SOCKET, SO_RCVBUF,
   1628 		    (char *)&tcpwindowsize, sizeof (tcpwindowsize)) < 0)
   1629 			perror("ftp: setsockopt (SO_RCVBUF - ignored)");
   1630 	}
   1631 	len = sizeof (data_addr);
   1632 	if (getsockname(data, (struct sockaddr *)&data_addr, &len) < 0) {
   1633 		perror("ftp: getsockname");
   1634 		goto bad;
   1635 	}
   1636 
   1637 	v4_addr = IN6_IS_ADDR_V4MAPPED(&data_addr.sin6_addr);
   1638 	if (listen(data, 1) < 0)
   1639 		perror("ftp: listen");
   1640 
   1641 	if (sendport) {
   1642 		a = (unsigned char *)&data_addr.sin6_addr;
   1643 		p = (unsigned char *)&data_addr.sin6_port;
   1644 		if (v4_addr) {
   1645 			result =
   1646 			    command("PORT %d,%d,%d,%d,%d,%d",
   1647 			    UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
   1648 			    UC(p[0]), UC(p[1]));
   1649 		} else {
   1650 			char hname[INET6_ADDRSTRLEN];
   1651 
   1652 			result = COMPLETE + 1;
   1653 			/*
   1654 			 * if on previous try to server, it was
   1655 			 * determined that the server doesn't support
   1656 			 * EPRT, don't bother trying again.  Just try
   1657 			 * LPRT.
   1658 			 */
   1659 			if (eport_supported == B_TRUE) {
   1660 				if (inet_ntop(AF_INET6, &data_addr.sin6_addr,
   1661 				    hname, sizeof (hname)) != NULL) {
   1662 					result = command("EPRT |%d|%s|%d|", 2,
   1663 					    hname, htons(data_addr.sin6_port));
   1664 					if (result != COMPLETE)
   1665 						eport_supported = B_FALSE;
   1666 				    }
   1667 			}
   1668 			/* Try LPRT */
   1669 			if (result != COMPLETE) {
   1670 				result = command(
   1671 "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
   1672 6, 16,
   1673 UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
   1674 UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
   1675 UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
   1676 UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
   1677 2, UC(p[0]), UC(p[1]));
   1678 			}
   1679 		}
   1680 
   1681 		if (result == ERROR && sendport == -1) {
   1682 			sendport = 0;
   1683 			tmpno = 1;
   1684 			goto noport;
   1685 		}
   1686 		return (result != COMPLETE);
   1687 	}
   1688 	if (tmpno)
   1689 		sendport = 1;
   1690 	return (0);
   1691 bad:
   1692 	(void) close(data), data = -1;
   1693 	if (tmpno)
   1694 		sendport = 1;
   1695 	return (1);
   1696 }
   1697 
   1698 static FILE *
   1699 dataconn(char *mode)
   1700 {
   1701 	struct sockaddr_in6 from;
   1702 	int s;
   1703 	socklen_t fromlen = sizeof (from);
   1704 
   1705 	reset_timer();
   1706 	if (passivemode && !pasv_refused)
   1707 		return (fdopen(data, mode));
   1708 
   1709 	s = accept(data, (struct sockaddr *)&from, &fromlen);
   1710 	if (s < 0) {
   1711 		perror("ftp: accept");
   1712 		(void) close(data), data = -1;
   1713 		return (NULL);
   1714 	}
   1715 	(void) close(data);
   1716 	data = s;
   1717 	return (fdopen(data, mode));
   1718 }
   1719 
   1720 static void
   1721 ptransfer(char *direction, off_t bytes, hrtime_t t0,
   1722     hrtime_t t1, char *local, char *remote)
   1723 {
   1724 	hrtime_t td; /* nanoseconds in a 64 bit int */
   1725 	double s, bs;
   1726 
   1727 	td = t1 - t0;
   1728 	s = (double)td / 1000000000.0; /* seconds */
   1729 	bs = (double)bytes / NONZERO(s);
   1730 	if (local && *local != '-')
   1731 		(void) printf("local: %s ", local);
   1732 	if (remote)
   1733 		(void) printf("remote: %s\n", remote);
   1734 	(void) printf("%lld bytes %s in %.2g seconds (%.2f Kbytes/s)\n",
   1735 		(longlong_t)bytes, direction, s, bs / 1024.0);
   1736 }
   1737 
   1738 /*ARGSUSED*/
   1739 static void
   1740 psabort(int sig)
   1741 {
   1742 	abrtflag++;
   1743 }
   1744 
   1745 void
   1746 pswitch(int flag)
   1747 {
   1748 	void (*oldintr)();
   1749 	static struct comvars {
   1750 		int connect;
   1751 		char name[MAXHOSTNAMELEN];
   1752 		struct sockaddr_in6 mctl;
   1753 		struct sockaddr_in6 hctl;
   1754 		FILE *in;
   1755 		FILE *out;
   1756 		int tpe;
   1757 		int cpnd;
   1758 		int sunqe;
   1759 		int runqe;
   1760 		int mcse;
   1761 		int ntflg;
   1762 		char nti[17];
   1763 		char nto[17];
   1764 		int mapflg;
   1765 		char mi[MAXPATHLEN];
   1766 		char mo[MAXPATHLEN];
   1767 		int authtype;
   1768 		int clvl;
   1769 		int dlvl;
   1770 		} proxstruct, tmpstruct;
   1771 	struct comvars *ip, *op;
   1772 
   1773 	abrtflag = 0;
   1774 	oldintr = signal(SIGINT, psabort);
   1775 	if (flag) {
   1776 		if (proxy)
   1777 			return;
   1778 		ip = &tmpstruct;
   1779 		op = &proxstruct;
   1780 		proxy++;
   1781 	} else {
   1782 		if (!proxy)
   1783 			return;
   1784 		ip = &proxstruct;
   1785 		op = &tmpstruct;
   1786 		proxy = 0;
   1787 	}
   1788 	ip->connect = connected;
   1789 	connected = op->connect;
   1790 	if (hostname)
   1791 		(void) strlcpy(ip->name, hostname, sizeof (ip->name));
   1792 	else
   1793 		ip->name[0] = 0;
   1794 	hostname = op->name;
   1795 	ip->hctl = remctladdr;
   1796 	remctladdr = op->hctl;
   1797 	ip->mctl = myctladdr;
   1798 	myctladdr = op->mctl;
   1799 	ip->in = ctrl_in;
   1800 	ctrl_in = op->in;
   1801 	ip->out = ctrl_out;
   1802 	ctrl_out = op->out;
   1803 	ip->tpe = type;
   1804 	type = op->tpe;
   1805 	if (!type)
   1806 		type = 1;
   1807 	ip->cpnd = cpend;
   1808 	cpend = op->cpnd;
   1809 	ip->sunqe = sunique;
   1810 	sunique = op->sunqe;
   1811 	ip->runqe = runique;
   1812 	runique = op->runqe;
   1813 	ip->mcse = mcase;
   1814 	mcase = op->mcse;
   1815 	ip->ntflg = ntflag;
   1816 	ntflag = op->ntflg;
   1817 	(void) strlcpy(ip->nti, ntin, sizeof (ip->nti));
   1818 	(void) strlcpy(ntin, op->nti, sizeof (ntin));
   1819 	(void) strlcpy(ip->nto, ntout, sizeof (ip->nto));
   1820 	(void) strlcpy(ntout, op->nto, sizeof (ntout));
   1821 	ip->mapflg = mapflag;
   1822 	mapflag = op->mapflg;
   1823 	(void) strlcpy(ip->mi, mapin, sizeof (ip->mi));
   1824 	(void) strlcpy(mapin, op->mi, sizeof (mapin));
   1825 	(void) strlcpy(ip->mo, mapout, sizeof (ip->mo));
   1826 	(void) strlcpy(mapout, op->mo, sizeof (mapout));
   1827 
   1828 	ip->authtype = auth_type;
   1829 	auth_type = op->authtype;
   1830 	ip->clvl = clevel;
   1831 	clevel = op->clvl;
   1832 	ip->dlvl = dlevel;
   1833 	dlevel = op->dlvl;
   1834 	if (!clevel)
   1835 		clevel = PROT_C;
   1836 	if (!dlevel)
   1837 		dlevel = PROT_C;
   1838 
   1839 	(void) signal(SIGINT, oldintr);
   1840 	if (abrtflag) {
   1841 		abrtflag = 0;
   1842 		(*oldintr)();
   1843 	}
   1844 }
   1845 
   1846 /*ARGSUSED*/
   1847 static void
   1848 abortpt(int sig)
   1849 {
   1850 	(void) printf("\n");
   1851 	(void) fflush(stdout);
   1852 	ptabflg++;
   1853 	mflag = 0;
   1854 	abrtflag = 0;
   1855 	longjmp(ptabort, 1);
   1856 }
   1857 
   1858 static void
   1859 proxtrans(char *cmd, char *local, char *remote)
   1860 {
   1861 	void (*oldintr)();
   1862 	int tmptype, oldtype = 0, secndflag = 0, nfnd;
   1863 	extern jmp_buf ptabort;
   1864 	char *cmd2;
   1865 	struct fd_set mask;
   1866 	int ipv4_addr = IN6_IS_ADDR_V4MAPPED(&remctladdr.sin6_addr);
   1867 
   1868 	if (strcmp(cmd, "RETR"))
   1869 		cmd2 = "RETR";
   1870 	else
   1871 		cmd2 = runique ? "STOU" : "STOR";
   1872 	if (command(ipv4_addr ? "PASV" : "EPSV") != COMPLETE) {
   1873 		(void) printf(
   1874 		    "proxy server does not support third part transfers.\n");
   1875 		return;
   1876 	}
   1877 	tmptype = type;
   1878 	pswitch(0);
   1879 	if (!connected) {
   1880 		(void) printf("No primary connection\n");
   1881 		pswitch(1);
   1882 		code = -1;
   1883 		return;
   1884 	}
   1885 	if (type != tmptype) {
   1886 		oldtype = type;
   1887 		switch (tmptype) {
   1888 			case TYPE_A:
   1889 				setascii(0, NULL);
   1890 				break;
   1891 			case TYPE_I:
   1892 				setbinary(0, NULL);
   1893 				break;
   1894 			case TYPE_E:
   1895 				setebcdic(0, NULL);
   1896 				break;
   1897 			case TYPE_L:
   1898 				settenex(0, NULL);
   1899 				break;
   1900 		}
   1901 	}
   1902 	if (command(ipv4_addr ? "PORT %s" : "EPRT %s", pasv) != COMPLETE) {
   1903 		switch (oldtype) {
   1904 			case 0:
   1905 				break;
   1906 			case TYPE_A:
   1907 				setascii(0, NULL);
   1908 				break;
   1909 			case TYPE_I:
   1910 				setbinary(0, NULL);
   1911 				break;
   1912 			case TYPE_E:
   1913 				setebcdic(0, NULL);
   1914 				break;
   1915 			case TYPE_L:
   1916 				settenex(0, NULL);
   1917 				break;
   1918 		}
   1919 		pswitch(1);
   1920 		return;
   1921 	}
   1922 	if (setjmp(ptabort))
   1923 		goto abort;
   1924 	oldintr = signal(SIGINT, (void (*)())abortpt);
   1925 	if (command("%s %s", cmd, remote) != PRELIM) {
   1926 		(void) signal(SIGINT, oldintr);
   1927 		switch (oldtype) {
   1928 			case 0:
   1929 				break;
   1930 			case TYPE_A:
   1931 				setascii(0, NULL);
   1932 				break;
   1933 			case TYPE_I:
   1934 				setbinary(0, NULL);
   1935 				break;
   1936 			case TYPE_E:
   1937 				setebcdic(0, NULL);
   1938 				break;
   1939 			case TYPE_L:
   1940 				settenex(0, NULL);
   1941 				break;
   1942 		}
   1943 		pswitch(1);
   1944 		return;
   1945 	}
   1946 	(void) sleep(2);
   1947 	pswitch(1);
   1948 	secndflag++;
   1949 	if (command("%s %s", cmd2, local) != PRELIM)
   1950 		goto abort;
   1951 	ptflag++;
   1952 	(void) getreply(0);
   1953 	pswitch(0);
   1954 	(void) getreply(0);
   1955 	(void) signal(SIGINT, oldintr);
   1956 	switch (oldtype) {
   1957 		case 0:
   1958 			break;
   1959 		case TYPE_A:
   1960 			setascii(0, NULL);
   1961 			break;
   1962 		case TYPE_I:
   1963 			setbinary(0, NULL);
   1964 			break;
   1965 		case TYPE_E:
   1966 			setebcdic(0, NULL);
   1967 			break;
   1968 		case TYPE_L:
   1969 			settenex(0, NULL);
   1970 			break;
   1971 	}
   1972 	pswitch(1);
   1973 	ptflag = 0;
   1974 	(void) printf("local: %s remote: %s\n", local, remote);
   1975 	return;
   1976 abort:
   1977 	(void) signal(SIGINT, SIG_IGN);
   1978 	ptflag = 0;
   1979 	if (strcmp(cmd, "RETR") && !proxy)
   1980 		pswitch(1);
   1981 	else if ((strcmp(cmd, "RETR") == 0) && proxy)
   1982 		pswitch(0);
   1983 	if (!cpend && !secndflag) {  /* only here if cmd = "STOR" (proxy=1) */
   1984 		if (command("%s %s", cmd2, local) != PRELIM) {
   1985 			pswitch(0);
   1986 			switch (oldtype) {
   1987 				case 0:
   1988 					break;
   1989 				case TYPE_A:
   1990 					setascii(0, NULL);
   1991 					break;
   1992 				case TYPE_I:
   1993 					setbinary(0, NULL);
   1994 					break;
   1995 				case TYPE_E:
   1996 					setebcdic(0, NULL);
   1997 					break;
   1998 				case TYPE_L:
   1999 					settenex(0, NULL);
   2000 					break;
   2001 			}
   2002 			if (cpend) {
   2003 				char msg[2];
   2004 
   2005 				(void) fprintf(ctrl_out, "%c%c", IAC, IP);
   2006 				(void) fflush(ctrl_out);
   2007 				*msg = (char)IAC;
   2008 				*(msg+1) = (char)DM;
   2009 				if (send(fileno(ctrl_out), msg, 2, MSG_OOB)
   2010 				    != 2)
   2011 					perror("abort");
   2012 				(void) fprintf(ctrl_out, "ABOR\r\n");
   2013 				(void) fflush(ctrl_out);
   2014 				FD_ZERO(&mask);
   2015 				FD_SET(fileno(ctrl_in), &mask);
   2016 				if ((nfnd = empty(&mask, 10,
   2017 				    fileno(ctrl_in) + 1)) <= 0) {
   2018 					if (nfnd < 0) {
   2019 						perror("abort");
   2020 					}
   2021 					if (ptabflg)
   2022 						code = -1;
   2023 					lostpeer(0);
   2024 				}
   2025 				(void) getreply(0);
   2026 				(void) getreply(0);
   2027 			}
   2028 		}
   2029 		pswitch(1);
   2030 		if (ptabflg)
   2031 			code = -1;
   2032 		(void) signal(SIGINT, oldintr);
   2033 		return;
   2034 	}
   2035 	if (cpend) {
   2036 		char msg[2];
   2037 
   2038 		(void) fprintf(ctrl_out, "%c%c", IAC, IP);
   2039 		(void) fflush(ctrl_out);
   2040 		*msg = (char)IAC;
   2041 		*(msg+1) = (char)DM;
   2042 		if (send(fileno(ctrl_out), msg, 2, MSG_OOB) != 2)
   2043 			perror("abort");
   2044 		(void) fprintf(ctrl_out, "ABOR\r\n");
   2045 		(void) fflush(ctrl_out);
   2046 		FD_ZERO(&mask);
   2047 		FD_SET(fileno(ctrl_in), &mask);
   2048 		if ((nfnd = empty(&mask, 10, fileno(ctrl_in) + 1)) <= 0) {
   2049 			if (nfnd < 0) {
   2050 				perror("abort");
   2051 			}
   2052 			if (ptabflg)
   2053 				code = -1;
   2054 			lostpeer(0);
   2055 		}
   2056 		(void) getreply(0);
   2057 		(void) getreply(0);
   2058 	}
   2059 	pswitch(!proxy);
   2060 	if (!cpend && !secndflag) {  /* only if cmd = "RETR" (proxy=1) */
   2061 		if (command("%s %s", cmd2, local) != PRELIM) {
   2062 			pswitch(0);
   2063 			switch (oldtype) {
   2064 				case 0:
   2065 					break;
   2066 				case TYPE_A:
   2067 					setascii(0, NULL);
   2068 					break;
   2069 				case TYPE_I:
   2070 					setbinary(0, NULL);
   2071 					break;
   2072 				case TYPE_E:
   2073 					setebcdic(0, NULL);
   2074 					break;
   2075 				case TYPE_L:
   2076 					settenex(0, NULL);
   2077 					break;
   2078 			}
   2079 			if (cpend) {
   2080 				char msg[2];
   2081 
   2082 				(void) fprintf(ctrl_out, "%c%c", IAC, IP);
   2083 				(void) fflush(ctrl_out);
   2084 				*msg = (char)IAC;
   2085 				*(msg+1) = (char)DM;
   2086 				if (send(fileno(ctrl_out), msg, 2, MSG_OOB)
   2087 				    != 2)
   2088 					perror("abort");
   2089 				(void) fprintf(ctrl_out, "ABOR\r\n");
   2090 				(void) fflush(ctrl_out);
   2091 				FD_ZERO(&mask);
   2092 				FD_SET(fileno(ctrl_in), &mask);
   2093 				if ((nfnd = empty(&mask, 10,
   2094 				    fileno(ctrl_in) + 1)) <= 0) {
   2095 					if (nfnd < 0) {
   2096 						perror("abort");
   2097 					}
   2098 					if (ptabflg)
   2099 						code = -1;
   2100 					lostpeer(0);
   2101 				}
   2102 				(void) getreply(0);
   2103 				(void) getreply(0);
   2104 			}
   2105 			pswitch(1);
   2106 			if (ptabflg)
   2107 				code = -1;
   2108 			(void) signal(SIGINT, oldintr);
   2109 			return;
   2110 		}
   2111 	}
   2112 	if (cpend) {
   2113 		char msg[2];
   2114 
   2115 		(void) fprintf(ctrl_out, "%c%c", IAC, IP);
   2116 		(void) fflush(ctrl_out);
   2117 		*msg = (char)IAC;
   2118 		*(msg+1) = (char)DM;
   2119 		if (send(fileno(ctrl_out), msg, 2, MSG_OOB) != 2)
   2120 			perror("abort");
   2121 		(void) fprintf(ctrl_out, "ABOR\r\n");
   2122 		(void) fflush(ctrl_out);
   2123 		FD_ZERO(&mask);
   2124 		FD_SET(fileno(ctrl_in), &mask);
   2125 		if ((nfnd = empty(&mask, 10, fileno(ctrl_in) + 1)) <= 0) {
   2126 			if (nfnd < 0) {
   2127 				perror("abort");
   2128 			}
   2129 			if (ptabflg)
   2130 				code = -1;
   2131 			lostpeer(0);
   2132 		}
   2133 		(void) getreply(0);
   2134 		(void) getreply(0);
   2135 	}
   2136 	pswitch(!proxy);
   2137 	if (cpend) {
   2138 		FD_ZERO(&mask);
   2139 		FD_SET(fileno(ctrl_in), &mask);
   2140 		if ((nfnd = empty(&mask, 10, fileno(ctrl_in) + 1)) <= 0) {
   2141 			if (nfnd < 0) {
   2142 				perror("abort");
   2143 			}
   2144 			if (ptabflg)
   2145 				code = -1;
   2146 			lostpeer(0);
   2147 		}
   2148 		(void) getreply(0);
   2149 		(void) getreply(0);
   2150 	}
   2151 	if (proxy)
   2152 		pswitch(0);
   2153 	switch (oldtype) {
   2154 		case 0:
   2155 			break;
   2156 		case TYPE_A:
   2157 			setascii(0, NULL);
   2158 			break;
   2159 		case TYPE_I:
   2160 			setbinary(0, NULL);
   2161 			break;
   2162 		case TYPE_E:
   2163 			setebcdic(0, NULL);
   2164 			break;
   2165 		case TYPE_L:
   2166 			settenex(0, NULL);
   2167 			break;
   2168 	}
   2169 	pswitch(1);
   2170 	if (ptabflg)
   2171 		code = -1;
   2172 	(void) signal(SIGINT, oldintr);
   2173 }
   2174 
   2175 /*ARGSUSED*/
   2176 void
   2177 reset(int argc, char *argv[])
   2178 {
   2179 	struct fd_set mask;
   2180 	int nfnd = 1;
   2181 
   2182 	FD_ZERO(&mask);
   2183 	while (nfnd > 0) {
   2184 		FD_SET(fileno(ctrl_in), &mask);
   2185 		if ((nfnd = empty(&mask, 0, fileno(ctrl_in) + 1)) < 0) {
   2186 			perror("reset");
   2187 			code = -1;
   2188 			lostpeer(0);
   2189 		} else if (nfnd > 0) {
   2190 			(void) getreply(0);
   2191 		}
   2192 	}
   2193 }
   2194 
   2195 static char *
   2196 gunique(char *local)
   2197 {
   2198 	static char new[MAXPATHLEN];
   2199 	char *cp = rindex(local, '/');
   2200 	int d, count = 0;
   2201 	char ext = '1';
   2202 
   2203 	if (cp)
   2204 		*cp = '\0';
   2205 	d = access(cp ? local : ".", 2);
   2206 	if (cp)
   2207 		*cp = '/';
   2208 	if (d < 0) {
   2209 		perror(local);
   2210 		return ((char *)0);
   2211 	}
   2212 	if (strlcpy(new, local, sizeof (new)) >= sizeof (new))
   2213 		(void) printf("gunique: too long: local %s, %d, new %d\n",
   2214 		    local, strlen(local), sizeof (new));
   2215 
   2216 	cp = new + strlen(new);
   2217 	*cp++ = '.';
   2218 	while (!d) {
   2219 		if (++count == 100) {
   2220 			(void) printf(
   2221 				"runique: can't find unique file name.\n");
   2222 			return ((char *)0);
   2223 		}
   2224 		*cp++ = ext;
   2225 		*cp = '\0';
   2226 		if (ext == '9')
   2227 			ext = '0';
   2228 		else
   2229 			ext++;
   2230 		if ((d = access(new, 0)) < 0)
   2231 			break;
   2232 		if (ext != '0')
   2233 			cp--;
   2234 		else if (*(cp - 2) == '.')
   2235 			*(cp - 1) = '1';
   2236 		else {
   2237 			*(cp - 2) = *(cp - 2) + 1;
   2238 			cp--;
   2239 		}
   2240 	}
   2241 	return (new);
   2242 }
   2243 
   2244 /*
   2245  * This is a wrap-around function for inet_ntop(). In case the af is AF_INET6
   2246  * and the address pointed by src is a IPv4-mapped IPv6 address, it
   2247  * returns printable IPv4 address, not IPv4-mapped IPv6 address. In other cases
   2248  * it behaves just like inet_ntop().
   2249  */
   2250 const char *
   2251 inet_ntop_native(int af, const void *src, char *dst, size_t size)
   2252 {
   2253 	struct in_addr src4;
   2254 	const char *result;
   2255 	struct sockaddr_in *sin;
   2256 	struct sockaddr_in6 *sin6;
   2257 
   2258 	if (af == AF_INET6) {
   2259 		sin6 = (struct sockaddr_in6 *)src;
   2260 		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
   2261 			IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr, &src4);
   2262 			result = inet_ntop(AF_INET, &src4, dst, size);
   2263 		} else {
   2264 			result = inet_ntop(AF_INET6, &sin6->sin6_addr,
   2265 			    dst, size);
   2266 		}
   2267 	} else {
   2268 		sin = (struct sockaddr_in *)src;
   2269 		result = inet_ntop(af, &sin->sin_addr, dst, size);
   2270 	}
   2271 
   2272 	return (result);
   2273 }
   2274 
   2275 int
   2276 secure_command(char *cmd)
   2277 {
   2278 	unsigned char *in = NULL, *out = NULL;
   2279 	int length = 0;
   2280 	size_t inlen;
   2281 
   2282 	if ((auth_type != AUTHTYPE_NONE) && clevel != PROT_C) {
   2283 		gss_buffer_desc in_buf, out_buf;
   2284 		OM_uint32 maj_stat, min_stat;
   2285 
   2286 		/* secure_command (based on level) */
   2287 		if (auth_type == AUTHTYPE_GSSAPI) {
   2288 			OM_uint32 expire_time;
   2289 			int conf_state;
   2290 			/* clevel = PROT_P; */
   2291 			in_buf.value = cmd;
   2292 			in_buf.length = strlen(cmd) + 1;
   2293 
   2294 			maj_stat = gss_context_time(&min_stat, gcontext,
   2295 				&expire_time);
   2296 			if (GSS_ERROR(maj_stat)) {
   2297 				user_gss_error(maj_stat, min_stat,
   2298 					"gss context has expired");
   2299 				fatal("Your gss credentials have expired.  "
   2300 					"Good-bye!");
   2301 			}
   2302 			maj_stat = gss_seal(&min_stat, gcontext,
   2303 					    (clevel == PROT_P), /* private */
   2304 					    GSS_C_QOP_DEFAULT,
   2305 					    &in_buf, &conf_state,
   2306 					    &out_buf);
   2307 			if (maj_stat != GSS_S_COMPLETE) {
   2308 				/* generally need to deal */
   2309 				user_gss_error(maj_stat, min_stat,
   2310 					(clevel == PROT_P) ?
   2311 					"gss_seal ENC didn't complete":
   2312 					"gss_seal MIC didn't complete");
   2313 			} else if ((clevel == PROT_P) && !conf_state) {
   2314 				(void) fprintf(stderr,
   2315 					"GSSAPI didn't encrypt message");
   2316 				out = out_buf.value;
   2317 			} else {
   2318 				if (debug)
   2319 				(void) fprintf(stderr,
   2320 					"sealed (%s) %d bytes\n",
   2321 					clevel == PROT_P ? "ENC" : "MIC",
   2322 					out_buf.length);
   2323 
   2324 				out = out_buf.value;
   2325 			}
   2326 		}
   2327 		/* Other auth types go here ... */
   2328 		inlen = ((4 * out_buf.length) / 3) + 4;
   2329 		in = (uchar_t *)malloc(inlen);
   2330 		if (in == NULL) {
   2331 			gss_release_buffer(&min_stat, &out_buf);
   2332 			fatal("Memory error allocating space for response.");
   2333 		}
   2334 		length = out_buf.length;
   2335 		if (auth_error = radix_encode(out, in, inlen, &length, 0)) {
   2336 			(void) fprintf(stderr,
   2337 				"Couldn't base 64 encode command (%s)\n",
   2338 				radix_error(auth_error));
   2339 			free(in);
   2340 			gss_release_buffer(&min_stat, &out_buf);
   2341 			return (0);
   2342 		}
   2343 
   2344 		(void) fprintf(ctrl_out, "%s %s",
   2345 			clevel == PROT_P ? "ENC" : "MIC", in);
   2346 
   2347 		free(in);
   2348 		gss_release_buffer(&min_stat, &out_buf);
   2349 
   2350 		if (debug)
   2351 			(void) fprintf(stderr,
   2352 			    "secure_command(%s)\nencoding %d bytes %s %s\n",
   2353 			    cmd, length,
   2354 			    (clevel == PROT_P) ? "ENC" : "MIC", in);
   2355 	} else {
   2356 		/*
   2357 		 * auth_type = AUTHTYPE_NONE or
   2358 		 * command channel is not protected
   2359 		 */
   2360 		fputs(cmd, ctrl_out);
   2361 	}
   2362 
   2363 	(void) fprintf(ctrl_out, "\r\n");
   2364 	(void) fflush(ctrl_out);
   2365 	return (1);
   2366 }
   2367 
   2368 unsigned int maxbuf;
   2369 unsigned char *ucbuf;
   2370 
   2371 void
   2372 setpbsz(unsigned int size)
   2373 {
   2374 	unsigned int actualbuf;
   2375 	int oldverbose;
   2376 
   2377 	if (ucbuf)
   2378 		(void) free(ucbuf);
   2379 	actualbuf = size;
   2380 	while ((ucbuf = (unsigned char *)malloc(actualbuf)) == NULL) {
   2381 		if (actualbuf)
   2382 			actualbuf >>= 2;
   2383 		else {
   2384 			perror("Error while trying to malloc PROT buffer:");
   2385 			exit(1);
   2386 		}
   2387 	}
   2388 	oldverbose = verbose;
   2389 	verbose = 0;
   2390 	reply_parse = "PBSZ=";
   2391 	if (command("PBSZ %u", actualbuf) != COMPLETE)
   2392 		fatal("Cannot set PROT buffer size");
   2393 	if (reply_parse) {
   2394 		if ((maxbuf = (unsigned int) atol(reply_parse)) > actualbuf)
   2395 			maxbuf = actualbuf;
   2396 	} else
   2397 		maxbuf = actualbuf;
   2398 	reply_parse = NULL;
   2399 	verbose = oldverbose;
   2400 }
   2401 
   2402 /*
   2403  * Do the base 64 decoding of the raw input buffer, b64_buf.
   2404  * Also do the verification and decryption, if required.
   2405  * retval contains the current error code number
   2406  *
   2407  * returns:
   2408  *	(RFC 2228:  error returns are 3 digit numbers of the form 5xy)
   2409  *	5	if an error occurred
   2410  */
   2411 static int
   2412 decode_reply(uchar_t *plain_buf,
   2413 		int ilen,
   2414 		uchar_t *b64_buf,
   2415 		int retval,
   2416 		boolean_t *again)
   2417 {
   2418 	int len;
   2419 	int safe = 0;
   2420 
   2421 	*again = 0;
   2422 
   2423 	if (!b64_buf[0])	/* if there is no string, no problem */
   2424 	    return (retval);
   2425 
   2426 	if ((auth_type == AUTHTYPE_NONE)) {
   2427 	    (void) printf("Cannot decode reply:\n%d %s\n", code, b64_buf);
   2428 	    return ('5');
   2429 	}
   2430 
   2431 	switch (code) {
   2432 
   2433 	    case 631:	/* 'safe' */
   2434 		safe = 1;
   2435 		break;
   2436 
   2437 	    case 632:	/* 'private' */
   2438 		break;
   2439 
   2440 	    case 633:	/* 'confidential' */
   2441 		break;
   2442 
   2443 	    default:
   2444 		(void) printf("Unknown reply: %d %s\n", code, b64_buf);
   2445 		return ('5');
   2446 	}
   2447 
   2448 	/* decode the base64 encoded message */
   2449 	auth_error = radix_encode(b64_buf, plain_buf, ilen, &len, 1);
   2450 
   2451 	if (auth_error) {
   2452 		(void) printf("Can't base 64 decode reply %d (%s)\n\"%s\"\n",
   2453 			code, radix_error(auth_error), b64_buf);
   2454 		return ('5');
   2455 	}
   2456 
   2457 	if (auth_type == AUTHTYPE_GSSAPI) {
   2458 		gss_buffer_desc xmit_buf, msg_buf;
   2459 		OM_uint32 maj_stat, min_stat;
   2460 		int conf_state = safe;
   2461 		xmit_buf.value = plain_buf;
   2462 		xmit_buf.length = len;
   2463 
   2464 		/* decrypt/verify the message */
   2465 		maj_stat = gss_unseal(&min_stat, gcontext,
   2466 			&xmit_buf, &msg_buf, &conf_state, NULL);
   2467 		if (maj_stat != GSS_S_COMPLETE) {
   2468 			user_gss_error(maj_stat, min_stat,
   2469 				"failed unsealing reply");
   2470 			return ('5');
   2471 		}
   2472 		if (msg_buf.length < ilen - 2 - 1) {
   2473 			memcpy(plain_buf, msg_buf.value, msg_buf.length);
   2474 			strcpy((char *)&plain_buf[msg_buf.length], "\r\n");
   2475 			gss_release_buffer(&min_stat, &msg_buf);
   2476 			*again = 1;
   2477 		} else {
   2478 			user_gss_error(maj_stat, min_stat,
   2479 				"reply was too long");
   2480 			return ('5');
   2481 		}
   2482 	} /* end if GSSAPI */
   2483 
   2484 	/* Other auth types go here... */
   2485 
   2486 	return (retval);
   2487 }
   2488