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