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