xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.bin/ftp/ftp.c (revision 142d813a06c6f9a6142e2c276b62129a17a31a65)
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 
54 static struct	sockaddr_in6 data_addr;
55 int	data = -1;
56 static int	abrtflag = 0;
57 static int	ptflag = 0;
58 int		connected;
59 static jmp_buf	sendabort;
60 static jmp_buf	recvabort;
61 static jmp_buf 	ptabort;
62 static int ptabflg;
63 static boolean_t pasv_refused;
64 boolean_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  */
71 static boolean_t ipv6rem;
72 int	use_eprt = 0;	/* Testing option that specifies EPRT by default */
73 FILE	*ctrl_in, *ctrl_out;
74 
75 static void abortsend(int sig);
76 static void abortpt(int sig);
77 static void proxtrans(char *cmd, char *local, char *remote);
78 static void cmdabort(int sig);
79 static int empty(struct fd_set *mask, int sec, int nfds);
80 static void abortrecv(int sig);
81 static int initconn(void);
82 static FILE *dataconn(char *mode);
83 static void ptransfer(char *direction, off_t bytes, hrtime_t t0,
84     hrtime_t t1, char *local, char *remote);
85 static void psabort(int sig);
86 static char *gunique(char *local);
87 static const char *inet_ntop_native(int af, const void *src, char *dst,
88     size_t size);
89 static ssize_t timedread(int fd, void *buf, size_t maxlen, int timeout);
90 
91 static int secure_command(char *);
92 static int decode_reply(uchar_t *, int, uchar_t *, int, boolean_t *);
93 
94 static ssize_t	bufcnt;		/* number of bytes in buf[]	*/
95 static char	*bufp;		/* next character in buf	*/
96 static int	buferr;		/* last errno			*/
97 static size_t	bufsize;
98 
99 static void fdio_setbuf(char *buffer, size_t bufsize);
100 static int fdio_fillbuf(int fd);
101 static 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 
108 static void
109 fdio_setbuf(char *buffer, size_t maxsize)
110 {
111 	buf = buffer;
112 	bufp = buf;
113 	bufcnt = 0;
114 	buferr = 0;
115 	bufsize = maxsize;
116 }
117 
118 static int
119 fdio_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*/
136 static int
137 fdio_error(int fd)
138 {
139 	return (buferr);
140 }
141 
142 /*
143  * timedread - read buffer (like "read"), but with timeout (in seconds)
144  */
145 
146 static ssize_t
147 timedread(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 
172 char *
173 hookup(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);
350 bad:
351 	(void) close(s);
352 	return ((char *)0);
353 }
354 
355 int
356 login(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*/
428 static void
429 cmdabort(int sig)
430 {
431 	(void) printf("\n");
432 	(void) fflush(stdout);
433 	abrtflag++;
434 	if (ptflag)
435 		longjmp(ptabort, 1);
436 }
437 
438 int
439 command(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 
468 again:	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 */
488 char reply_string[BUFSIZ];
489 
490 int
491 getreply(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 
715 static int
716 empty(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*/
727 static void
728 abortsend(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 
737 void
738 sendrequest(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;
1003 abort:
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*/
1036 static void
1037 abortrecv(int sig)
1038 {
1039 	mflag = 0;
1040 	abrtflag = 0;
1041 	(void) printf("\n");
1042 	(void) fflush(stdout);
1043 	longjmp(recvabort, 1);
1044 }
1045 
1046 void
1047 recvrequest(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))) {
1257 writeerr:
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 		}
1320 endread:
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)) {
1333 writer_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;
1369 abort:
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 
1463 static int
1464 initconn(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 
1585 noport:
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",
1670 6, 16,
1671 UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
1672 UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
1673 UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
1674 UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
1675 2, 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);
1689 bad:
1690 	(void) close(data), data = -1;
1691 	if (tmpno)
1692 		sendport = 1;
1693 	return (1);
1694 }
1695 
1696 static FILE *
1697 dataconn(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 
1718 static void
1719 ptransfer(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*/
1737 static void
1738 psabort(int sig)
1739 {
1740 	abrtflag++;
1741 }
1742 
1743 void
1744 pswitch(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*/
1845 static void
1846 abortpt(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 
1856 static void
1857 proxtrans(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;
1974 abort:
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*/
2174 void
2175 reset(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 
2193 static char *
2194 gunique(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  */
2248 const char *
2249 inet_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 
2273 int
2274 secure_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 
2366 unsigned int maxbuf;
2367 unsigned char *ucbuf;
2368 
2369 void
2370 setpbsz(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  */
2409 static int
2410 decode_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