xref: /titanic_44/usr/src/cmd/cmd-inet/usr.bin/ftp/ftp.c (revision fcf3ce441efd61da9bb2884968af01cb7c1452cc)
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