1/*	$NetBSD: ftp.c,v 1.176 2024/02/18 22:29:56 christos Exp $	*/
2
3/*-
4 * Copyright (c) 1996-2021 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Luke Mewburn.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * Copyright (c) 1985, 1989, 1993, 1994
34 *	The Regents of the University of California.  All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 *    notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 *    notice, this list of conditions and the following disclaimer in the
43 *    documentation and/or other materials provided with the distribution.
44 * 3. Neither the name of the University nor the names of its contributors
45 *    may be used to endorse or promote products derived from this software
46 *    without specific prior written permission.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * SUCH DAMAGE.
59 */
60
61/*
62 * Copyright (C) 1997 and 1998 WIDE Project.
63 * All rights reserved.
64 *
65 * Redistribution and use in source and binary forms, with or without
66 * modification, are permitted provided that the following conditions
67 * are met:
68 * 1. Redistributions of source code must retain the above copyright
69 *    notice, this list of conditions and the following disclaimer.
70 * 2. Redistributions in binary form must reproduce the above copyright
71 *    notice, this list of conditions and the following disclaimer in the
72 *    documentation and/or other materials provided with the distribution.
73 * 3. Neither the name of the project nor the names of its contributors
74 *    may be used to endorse or promote products derived from this software
75 *    without specific prior written permission.
76 *
77 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
78 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
79 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
80 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
81 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
82 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
83 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
84 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
85 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
86 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
87 * SUCH DAMAGE.
88 */
89
90#include <sys/cdefs.h>
91#ifndef lint
92#if 0
93static char sccsid[] = "@(#)ftp.c	8.6 (Berkeley) 10/27/94";
94#else
95__RCSID("$NetBSD: ftp.c,v 1.176 2024/02/18 22:29:56 christos Exp $");
96#endif
97#endif /* not lint */
98
99#include <sys/types.h>
100#include <sys/stat.h>
101#include <sys/socket.h>
102#include <sys/time.h>
103
104#include <netinet/in.h>
105#include <netinet/in_systm.h>
106#include <netinet/ip.h>
107#include <arpa/inet.h>
108#include <arpa/ftp.h>
109#include <arpa/telnet.h>
110
111#include <assert.h>
112#include <ctype.h>
113#include <err.h>
114#include <errno.h>
115#include <fcntl.h>
116#include <netdb.h>
117#include <stdio.h>
118#include <stdlib.h>
119#include <string.h>
120#include <time.h>
121#include <unistd.h>
122#include <stdarg.h>
123
124#include "ftp_var.h"
125
126volatile sig_atomic_t	abrtflag;
127volatile sig_atomic_t	timeoutflag;
128
129sigjmp_buf	ptabort;
130int	ptabflg;
131int	ptflag = 0;
132char	pasv[BUFSIZ];	/* passive port for proxy data connection */
133size_t	ftp_buflen = FTPBUFLEN;
134
135static int empty(FILE *, FILE *, int);
136__dead static void abort_squared(int);
137
138struct sockinet {
139	union sockunion {
140		struct sockaddr_in  su_sin;
141#ifdef INET6
142		struct sockaddr_in6 su_sin6;
143#endif
144	} si_su;
145#if !defined(HAVE_STRUCT_SOCKADDR_IN_SIN_LEN)
146	int	si_len;
147#endif
148};
149
150#if !defined(HAVE_STRUCT_SOCKADDR_IN_SIN_LEN)
151# define su_len		si_len
152#else
153# define su_len		si_su.su_sin.sin_len
154#endif
155#define su_family	si_su.su_sin.sin_family
156#define su_port		si_su.su_sin.sin_port
157
158struct sockinet myctladdr, hisctladdr, data_addr;
159
160char *
161hookup(const char *host, const char *port)
162{
163	int s = -1, error;
164	struct addrinfo hints, *res, *res0;
165	static char hostnamebuf[MAXHOSTNAMELEN];
166	socklen_t len;
167	int on = 1;
168
169	memset((char *)&hisctladdr, 0, sizeof (hisctladdr));
170	memset((char *)&myctladdr, 0, sizeof (myctladdr));
171	memset(&hints, 0, sizeof(hints));
172	hints.ai_flags = AI_CANONNAME;
173	hints.ai_family = family;
174	hints.ai_socktype = SOCK_STREAM;
175	hints.ai_protocol = 0;
176	error = getaddrinfo(host, port, &hints, &res0);
177	if (error) {
178		warnx("Can't lookup `%s:%s': %s", host, port,
179		    (error == EAI_SYSTEM) ? strerror(errno)
180					  : gai_strerror(error));
181		code = -1;
182		return (0);
183	}
184
185	if (res0->ai_canonname)
186		(void)strlcpy(hostnamebuf, res0->ai_canonname,
187		    sizeof(hostnamebuf));
188	else
189		(void)strlcpy(hostnamebuf, host, sizeof(hostnamebuf));
190	hostname = hostnamebuf;
191
192	for (res = res0; res; res = res->ai_next) {
193		char hname[NI_MAXHOST], sname[NI_MAXSERV];
194
195		ai_unmapped(res);
196		if (getnameinfo(res->ai_addr, res->ai_addrlen,
197		    hname, sizeof(hname), sname, sizeof(sname),
198		    NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
199			strlcpy(hname, "?", sizeof(hname));
200			strlcpy(sname, "?", sizeof(sname));
201		}
202		if (verbose && res0->ai_next) {
203				/* if we have multiple possibilities */
204#ifdef INET6
205			if(res->ai_family == AF_INET6) {
206				fprintf(ttyout, "Trying [%s]:%s ...\n", hname,
207				    sname);
208			} else {
209#endif
210				fprintf(ttyout, "Trying %s:%s ...\n", hname,
211				    sname);
212#ifdef INET6
213			}
214#endif
215		}
216		s = socket(res->ai_family, SOCK_STREAM, res->ai_protocol);
217		if (s < 0) {
218			warn("Can't create socket for connection to `%s:%s'",
219			    hname, sname);
220			continue;
221		}
222		if (ftp_connect(s, res->ai_addr, res->ai_addrlen,
223		    verbose || !res->ai_next) < 0) {
224			close(s);
225			s = -1;
226			continue;
227		}
228
229		/* finally we got one */
230		break;
231	}
232	if (s < 0) {
233		warnx("Can't connect to `%s:%s'", host, port);
234		code = -1;
235		freeaddrinfo(res0);
236		return 0;
237	}
238	memcpy(&hisctladdr.si_su, res->ai_addr, res->ai_addrlen);
239	hisctladdr.su_len = res->ai_addrlen;
240	freeaddrinfo(res0);
241	res0 = res = NULL;
242
243	len = hisctladdr.su_len;
244	if (getsockname(s, (struct sockaddr *)&myctladdr.si_su, &len) == -1) {
245		warn("Can't determine my address of connection to `%s:%s'",
246		    host, port);
247		code = -1;
248		goto bad;
249	}
250	myctladdr.su_len = len;
251
252#ifdef IPTOS_LOWDELAY
253	if (hisctladdr.su_family == AF_INET) {
254		int tos = IPTOS_LOWDELAY;
255		if (setsockopt(s, IPPROTO_IP, IP_TOS,
256				(void *)&tos, sizeof(tos)) == -1) {
257				DWARN("setsockopt %s (ignored)",
258				    "IPTOS_LOWDELAY");
259		}
260	}
261#endif
262	cin = fdopen(s, "r");
263	cout = fdopen(s, "w");
264	if (cin == NULL || cout == NULL) {
265		warnx("Can't fdopen socket");
266		if (cin)
267			(void)fclose(cin);
268		if (cout)
269			(void)fclose(cout);
270		code = -1;
271		goto bad;
272	}
273	if (verbose)
274		fprintf(ttyout, "Connected to %s.\n", hostname);
275	if (getreply(0) > 2) {	/* read startup message from server */
276		if (cin)
277			(void)fclose(cin);
278		if (cout)
279			(void)fclose(cout);
280		code = -1;
281		goto bad;
282	}
283
284	if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
285			(void *)&on, sizeof(on)) == -1) {
286		DWARN("setsockopt %s (ignored)", "SO_KEEPALIVE");
287	}
288
289	if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE,
290			(void *)&on, sizeof(on)) == -1) {
291		DWARN("setsockopt %s (ignored)", "SO_OOBINLINE");
292	}
293
294	return (hostname);
295 bad:
296	(void)close(s);
297	return (NULL);
298}
299
300void
301cmdabort(int notused)
302{
303	int oerrno = errno;
304
305	sigint_raised = 1;
306	alarmtimer(0);
307	if (fromatty)
308		write(fileno(ttyout), "\n", 1);
309	abrtflag++;
310	if (ptflag)
311		siglongjmp(ptabort, 1);
312	errno = oerrno;
313}
314
315void
316cmdtimeout(int notused)
317{
318	int oerrno = errno;
319
320	alarmtimer(0);
321	if (fromatty)
322		write(fileno(ttyout), "\n", 1);
323	timeoutflag++;
324	if (ptflag)
325		siglongjmp(ptabort, 1);
326	errno = oerrno;
327}
328
329static int
330issighandler(sigfunc func)
331{
332	return (func != SIG_IGN &&
333		func != SIG_DFL &&
334#ifdef SIG_HOLD
335		func != SIG_HOLD &&
336#endif
337		func != SIG_ERR);
338}
339
340/*VARARGS*/
341int
342command(const char *fmt, ...)
343{
344	va_list ap;
345	int r;
346	sigfunc oldsigint;
347
348#ifndef NO_DEBUG
349	if (ftp_debug) {
350		fputs("---> ", ttyout);
351		va_start(ap, fmt);
352		if (strncmp("PASS ", fmt, 5) == 0)
353			fputs("PASS XXXX", ttyout);
354		else if (strncmp("ACCT ", fmt, 5) == 0)
355			fputs("ACCT XXXX", ttyout);
356		else
357			vfprintf(ttyout, fmt, ap);
358		va_end(ap);
359		putc('\n', ttyout);
360	}
361#endif
362	if (cout == NULL) {
363		warnx("No control connection for command");
364		code = -1;
365		return (0);
366	}
367
368	abrtflag = 0;
369
370	oldsigint = xsignal(SIGINT, cmdabort);
371
372	va_start(ap, fmt);
373	vfprintf(cout, fmt, ap);
374	va_end(ap);
375	fputs("\r\n", cout);
376	(void)fflush(cout);
377	cpend = 1;
378	r = getreply(!strcmp(fmt, "QUIT"));
379	if (abrtflag && issighandler(oldsigint)) {
380		(*oldsigint)(SIGINT);
381	}
382	(void)xsignal(SIGINT, oldsigint);
383	return (r);
384}
385
386static const char *m421[] = {
387	"remote server timed out. Connection closed",
388	"user interrupt. Connection closed",
389	"remote server has closed connection",
390};
391
392int
393getreply(int expecteof)
394{
395	char current_line[BUFSIZ];	/* last line of previous reply */
396	int c, n, lineno;
397	int dig;
398	int originalcode = 0, continuation = 0;
399	sigfunc oldsigint, oldsigalrm;
400	int pflag = 0;
401	char *cp, *pt = pasv;
402
403	abrtflag = 0;
404	timeoutflag = 0;
405
406	oldsigint = xsignal(SIGINT, cmdabort);
407	oldsigalrm = xsignal(SIGALRM, cmdtimeout);
408
409	for (lineno = 0 ;; lineno++) {
410		dig = n = code = 0;
411		cp = current_line;
412		while (alarmtimer(quit_time ? quit_time : 60),
413		       ((c = getc(cin)) != '\n')) {
414			if (c == IAC) {     /* handle telnet commands */
415				switch (c = getc(cin)) {
416				case WILL:
417				case WONT:
418					c = getc(cin);
419					fprintf(cout, "%c%c%c", IAC, DONT, c);
420					(void)fflush(cout);
421					break;
422				case DO:
423				case DONT:
424					c = getc(cin);
425					fprintf(cout, "%c%c%c", IAC, WONT, c);
426					(void)fflush(cout);
427					break;
428				default:
429					break;
430				}
431				continue;
432			}
433			dig++;
434			if (c == EOF) {
435				/*
436				 * these will get trashed by pswitch()
437				 * in lostpeer()
438				 */
439				int reply_timeoutflag = timeoutflag;
440				int reply_abrtflag = abrtflag;
441
442				alarmtimer(0);
443				if (expecteof && feof(cin)) {
444					(void)xsignal(SIGINT, oldsigint);
445					(void)xsignal(SIGALRM, oldsigalrm);
446					code = 221;
447					return (0);
448				}
449				cpend = 0;
450				lostpeer(0);
451				if (verbose) {
452					size_t midx;
453					if (reply_timeoutflag)
454						midx = 0;
455					else if (reply_abrtflag)
456						midx = 1;
457					else
458						midx = 2;
459					(void)fprintf(ttyout,
460			    "421 Service not available, %s.\n", m421[midx]);
461					(void)fflush(ttyout);
462				}
463				code = 421;
464				(void)xsignal(SIGINT, oldsigint);
465				(void)xsignal(SIGALRM, oldsigalrm);
466				return (4);
467			}
468			if (c != '\r' && (verbose > 0 ||
469			    ((verbose > -1 && n == '5' && dig > 4) &&
470			    (((!n && c < '5') || (n && n < '5'))
471			     || !retry_connect)))) {
472				if (proxflag &&
473				   (dig == 1 || (dig == 5 && verbose == 0)))
474					fprintf(ttyout, "%s:", hostname);
475				(void)putc(c, ttyout);
476			}
477			if (dig < 4 && isdigit(c))
478				code = code * 10 + (c - '0');
479			if (!pflag && (code == 227 || code == 228))
480				pflag = 1;
481			else if (!pflag && code == 229)
482				pflag = 100;
483			if (dig > 4 && pflag == 1 && isdigit(c))
484				pflag = 2;
485			if (pflag == 2) {
486				if (c != '\r' && c != ')') {
487					if (pt < &pasv[sizeof(pasv) - 1])
488						*pt++ = c;
489				} else {
490					*pt = '\0';
491					pflag = 3;
492				}
493			}
494			if (pflag == 100 && c == '(')
495				pflag = 2;
496			if (dig == 4 && c == '-') {
497				if (continuation)
498					code = 0;
499				continuation++;
500			}
501			if (n == 0)
502				n = c;
503			if (cp < &current_line[sizeof(current_line) - 1])
504				*cp++ = c;
505		}
506		if (verbose > 0 || ((verbose > -1 && n == '5') &&
507		    (n < '5' || !retry_connect))) {
508			(void)putc(c, ttyout);
509			(void)fflush(ttyout);
510		}
511		if (cp[-1] == '\r')
512			cp[-1] = '\0';
513		*cp = '\0';
514		if (lineno == 0)
515			(void)strlcpy(reply_string, current_line,
516			    sizeof(reply_string));
517		if (lineno > 0 && code == 0 && reply_callback != NULL)
518			(*reply_callback)(current_line);
519		if (continuation && code != originalcode) {
520			if (originalcode == 0)
521				originalcode = code;
522			continue;
523		}
524		if (n != '1')
525			cpend = 0;
526		alarmtimer(0);
527		(void)xsignal(SIGINT, oldsigint);
528		(void)xsignal(SIGALRM, oldsigalrm);
529		if (code == 421 || originalcode == 421)
530			lostpeer(0);
531		if (abrtflag && oldsigint != cmdabort &&
532		    issighandler(oldsigint)) {
533			(*oldsigint)(SIGINT);
534		}
535		if (timeoutflag && oldsigalrm != cmdtimeout &&
536		    issighandler(oldsigalrm)) {
537			(*oldsigalrm)(SIGINT);
538		}
539		return (n - '0');
540	}
541}
542
543static int
544empty(FILE *ecin, FILE *din, int sec)
545{
546	int		nr, nfd;
547	struct pollfd	pfd[2];
548
549	nfd = 0;
550	if (ecin) {
551		pfd[nfd].fd = fileno(ecin);
552		pfd[nfd++].events = POLLIN;
553	}
554
555	if (din) {
556		pfd[nfd].fd = fileno(din);
557		pfd[nfd++].events = POLLIN;
558	}
559
560	if ((nr = ftp_poll(pfd, nfd, sec * 1000)) <= 0)
561		return nr;
562
563	nr = 0;
564	nfd = 0;
565	if (ecin)
566		nr |= (pfd[nfd++].revents & POLLIN) ? 1 : 0;
567	if (din)
568		nr |= (pfd[nfd++].revents & POLLIN) ? 2 : 0;
569	return nr;
570}
571
572sigjmp_buf	xferabort;
573
574__dead static void
575abortxfer(int notused)
576{
577	char msgbuf[100];
578	size_t len;
579
580	sigint_raised = 1;
581	alarmtimer(0);
582	mflag = 0;
583	abrtflag = 0;
584	switch (direction[0]) {
585	case 'r':
586		strlcpy(msgbuf, "\nreceive", sizeof(msgbuf));
587		break;
588	case 's':
589		strlcpy(msgbuf, "\nsend", sizeof(msgbuf));
590		break;
591	default:
592		errx(1, "abortxfer: unknown direction `%s'", direction);
593	}
594	len = strlcat(msgbuf, " aborted. Waiting for remote to finish abort.\n",
595	    sizeof(msgbuf));
596	write(fileno(ttyout), msgbuf, len);
597	siglongjmp(xferabort, 1);
598}
599
600/*
601 * Read data from infd & write to outfd, using buf/bufsize as the temporary
602 * buffer, dealing with short reads or writes.
603 * If rate_limit != 0, rate-limit the transfer.
604 * If hash_interval != 0, fputc('c', ttyout) every hash_interval bytes.
605 * Updates global variables: bytes.
606 * Returns 0 if ok, 1 if there was a read error, 2 if there was a write error.
607 * In the case of error, errno contains the appropriate error code.
608 */
609static int
610copy_bytes(int infd, int outfd, char *buf, size_t bufsize,
611	int rate_limit, int hash_interval)
612{
613	volatile off_t	hashc;
614	ssize_t		inc, outc;
615	char		*bufp;
616	struct timeval	tvthen, tvnow, tvdiff;
617	off_t		bufrem, bufchunk;
618	int		serr;
619
620	hashc = hash_interval;
621	if (rate_limit)
622		bufchunk = rate_limit;
623	else
624		bufchunk = bufsize;
625
626	while (1) {
627		if (rate_limit) {
628			(void)gettimeofday(&tvthen, NULL);
629		}
630		errno = 0;
631		inc = outc = 0;
632					/* copy bufchunk at a time */
633		bufrem = bufchunk;
634		while (bufrem > 0) {
635			inc = read(infd, buf, MIN((off_t)bufsize, bufrem));
636			if (inc < 0) {
637				if (errno == EINTR || errno == EAGAIN) {
638					continue;
639				}
640				goto copy_done;
641			} else if (inc == 0) {
642				goto copy_done;
643			}
644			bytes += inc;
645			bufrem -= inc;
646			bufp = buf;
647			while (inc > 0) {
648				outc = write(outfd, bufp, inc);
649				if (outc < 0) {
650					if (errno == EINTR || errno == EAGAIN) {
651						continue;
652					}
653					goto copy_done;
654				}
655				inc -= outc;
656				bufp += outc;
657			}
658			if (hash_interval) {
659				while (bytes >= hashc) {
660					(void)putc('#', ttyout);
661					hashc += hash_interval;
662				}
663				(void)fflush(ttyout);
664			}
665		}
666		if (rate_limit) {	/* rate limited; wait if necessary */
667			while (1) {
668				(void)gettimeofday(&tvnow, NULL);
669				timersub(&tvnow, &tvthen, &tvdiff);
670				if (tvdiff.tv_sec > 0)
671					break;
672				usleep(1000000 - tvdiff.tv_usec);
673			}
674		}
675	}
676
677 copy_done:
678	serr = errno;
679	if (hash_interval && bytes > 0) {
680		if (bytes < hash_interval)
681			(void)putc('#', ttyout);
682		(void)putc('\n', ttyout);
683		(void)fflush(ttyout);
684	}
685	errno = serr;
686	if (inc == -1)
687		return 1;
688	if (outc == -1)
689		return 2;
690
691	return 0;
692}
693
694void
695sendrequest(const char *cmd, const char *local, const char *remote,
696	    int printnames)
697{
698	struct stat st;
699	int c;
700	FILE *volatile fin;
701	FILE *volatile dout;
702	int (*volatile closefunc)(FILE *);
703	sigfunc volatile oldintr;
704	sigfunc volatile oldpipe;
705	off_t volatile hashbytes;
706	int hash_interval;
707	const char *lmode;
708	static size_t bufsize;
709	static char *buf;
710	int oprogress;
711
712	hashbytes = mark;
713	direction = "sent";
714	dout = NULL;
715	bytes = 0;
716	filesize = -1;
717	oprogress = progress;
718	if (verbose && printnames) {
719		if (*local != '-')
720			fprintf(ttyout, "local: %s ", local);
721		if (remote)
722			fprintf(ttyout, "remote: %s\n", remote);
723	}
724	if (proxy) {
725		proxtrans(cmd, local, remote);
726		return;
727	}
728	if (curtype != type)
729		changetype(type, 0);
730	closefunc = NULL;
731	oldintr = SIG_ERR;
732	oldpipe = SIG_ERR;
733	lmode = "w";
734	if (sigsetjmp(xferabort, 1)) {
735		while (cpend)
736			(void)getreply(0);
737		code = -1;
738		goto cleanupsend;
739	}
740	(void)xsignal(SIGQUIT, psummary);
741	oldintr = xsignal(SIGINT, abortxfer);
742	if (strcmp(local, "-") == 0) {
743		fin = stdin;
744		progress = 0;
745	} else if (*local == '|') {
746		oldpipe = xsignal(SIGPIPE, SIG_IGN);
747		fin = popen(local + 1, "r");
748		if (fin == NULL) {
749			warn("Can't execute `%s'", local + 1);
750			code = -1;
751			goto cleanupsend;
752		}
753		progress = 0;
754		closefunc = pclose;
755	} else {
756		fin = fopen(local, "r");
757		if (fin == NULL) {
758			warn("Can't open `%s'", local);
759			code = -1;
760			goto cleanupsend;
761		}
762		closefunc = fclose;
763		if (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode)) {
764			fprintf(ttyout, "%s: not a plain file.\n", local);
765			code = -1;
766			goto cleanupsend;
767		}
768		filesize = st.st_size;
769	}
770	if (initconn()) {
771		code = -1;
772		goto cleanupsend;
773	}
774	if (sigsetjmp(xferabort, 1))
775		goto abort;
776
777	if (restart_point &&
778	    (strcmp(cmd, "STOR") == 0 || strcmp(cmd, "APPE") == 0)) {
779		int rc;
780
781		rc = -1;
782		switch (curtype) {
783		case TYPE_A:
784			rc = fseeko(fin, restart_point, SEEK_SET);
785			break;
786		case TYPE_I:
787		case TYPE_L:
788			rc = lseek(fileno(fin), restart_point, SEEK_SET);
789			break;
790		}
791		if (rc < 0) {
792			warn("Can't seek to restart `%s'", local);
793			goto cleanupsend;
794		}
795		if (command("REST " LLF, (LLT)restart_point) != CONTINUE)
796			goto cleanupsend;
797		lmode = "r+";
798	}
799	if (remote) {
800		if (command("%s %s", cmd, remote) != PRELIM)
801			goto cleanupsend;
802	} else {
803		if (command("%s", cmd) != PRELIM)
804			goto cleanupsend;
805	}
806	dirchange = 1;
807	dout = dataconn(lmode);
808	if (dout == NULL)
809		goto abort;
810
811	assert(sndbuf_size > 0);
812	if ((size_t)sndbuf_size > bufsize) {
813		if (buf)
814			(void)free(buf);
815		bufsize = sndbuf_size;
816		buf = ftp_malloc(bufsize);
817	}
818
819	progressmeter(-1);
820	if (oldpipe == SIG_ERR) {
821		oldpipe = xsignal(SIGPIPE, SIG_IGN);
822	}
823	hash_interval = (hash && (!progress || filesize < 0)) ? mark : 0;
824
825	switch (curtype) {
826
827	case TYPE_I:
828	case TYPE_L:
829		c = copy_bytes(fileno(fin), fileno(dout), buf, bufsize,
830			       rate_put, hash_interval);
831		if (c == 1) {
832			warn("Reading `%s'", local);
833		} else if (c == 2) {
834			if (errno != EPIPE)
835				warn("Writing to network");
836			bytes = -1;
837		}
838		break;
839
840	case TYPE_A:
841		while ((c = getc(fin)) != EOF) {
842			if (c == '\n') {
843				while (hash_interval && bytes >= hashbytes) {
844					(void)putc('#', ttyout);
845					(void)fflush(ttyout);
846					hashbytes += mark;
847				}
848				if (ferror(dout))
849					break;
850				(void)putc('\r', dout);
851				bytes++;
852			}
853			(void)putc(c, dout);
854			bytes++;
855#if 0	/* this violates RFC 959 */
856			if (c == '\r') {
857				(void)putc('\0', dout);
858				bytes++;
859			}
860#endif
861		}
862		if (hash_interval) {
863			if (bytes < hashbytes)
864				(void)putc('#', ttyout);
865			(void)putc('\n', ttyout);
866		}
867		if (ferror(fin))
868			warn("Reading `%s'", local);
869		if (ferror(dout)) {
870			if (errno != EPIPE)
871				warn("Writing to network");
872			bytes = -1;
873		}
874		break;
875	}
876
877	progressmeter(1);
878	if (closefunc != NULL) {
879		(*closefunc)(fin);
880		fin = NULL;
881	}
882	(void)fclose(dout);
883	dout = NULL;
884	(void)getreply(0);
885	if (bytes > 0)
886		ptransfer(0);
887	goto cleanupsend;
888
889 abort:
890	(void)xsignal(SIGINT, oldintr);
891	oldintr = SIG_ERR;
892	if (!cpend) {
893		code = -1;
894		goto cleanupsend;
895	}
896	if (data >= 0) {
897		(void)close(data);
898		data = -1;
899	}
900	if (dout) {
901		(void)fclose(dout);
902		dout = NULL;
903	}
904	(void)getreply(0);
905	code = -1;
906	if (bytes > 0)
907		ptransfer(0);
908
909 cleanupsend:
910	if (oldintr != SIG_ERR)
911		(void)xsignal(SIGINT, oldintr);
912	if (oldpipe != SIG_ERR)
913		(void)xsignal(SIGPIPE, oldpipe);
914	if (data >= 0) {
915		(void)close(data);
916		data = -1;
917	}
918	if (closefunc != NULL && fin != NULL)
919		(*closefunc)(fin);
920	if (dout)
921		(void)fclose(dout);
922	progress = oprogress;
923	restart_point = 0;
924	bytes = 0;
925}
926
927void
928recvrequest(const char *cmd, const char *volatile local, const char *remote,
929	    const char *lmode, int printnames, int ignorespecial)
930{
931	FILE *volatile fout;
932	FILE *volatile din;
933	int (*volatile closefunc)(FILE *);
934	sigfunc volatile oldintr;
935	sigfunc volatile oldpipe;
936	int c, d;
937	int volatile is_retr;
938	int volatile tcrflag;
939	int volatile bare_lfs;
940	static size_t bufsize;
941	static char *buf;
942	off_t volatile hashbytes;
943	int hash_interval;
944	struct stat st;
945	time_t mtime;
946	struct timeval tval[2];
947	int oprogress;
948	int opreserve;
949
950	fout = NULL;
951	din = NULL;
952	hashbytes = mark;
953	direction = "received";
954	bytes = 0;
955	bare_lfs = 0;
956	filesize = -1;
957	oprogress = progress;
958	opreserve = preserve;
959	is_retr = (strcmp(cmd, "RETR") == 0);
960	if (is_retr && verbose && printnames) {
961		if (ignorespecial || *local != '-')
962			fprintf(ttyout, "local: %s ", local);
963		if (remote)
964			fprintf(ttyout, "remote: %s\n", remote);
965	}
966	if (proxy && is_retr) {
967		proxtrans(cmd, local, remote);
968		return;
969	}
970	closefunc = NULL;
971	oldintr = SIG_ERR;
972	oldpipe = SIG_ERR;
973	tcrflag = !crflag && is_retr;
974	if (sigsetjmp(xferabort, 1)) {
975		while (cpend)
976			(void)getreply(0);
977		code = -1;
978		goto cleanuprecv;
979	}
980	(void)xsignal(SIGQUIT, psummary);
981	oldintr = xsignal(SIGINT, abortxfer);
982	if (ignorespecial || (strcmp(local, "-") && *local != '|')) {
983		if (access(local, W_OK) < 0) {
984			char *dir = strrchr(local, '/');
985
986			if (errno != ENOENT && errno != EACCES) {
987				warn("Can't access `%s'", local);
988				code = -1;
989				goto cleanuprecv;
990			}
991			if (dir != NULL)
992				*dir = 0;
993			d = access(dir == local ? "/" :
994			    dir ? local : ".", W_OK);
995			if (dir != NULL)
996				*dir = '/';
997			if (d < 0) {
998				warn("Can't access `%s'", local);
999				code = -1;
1000				goto cleanuprecv;
1001			}
1002			if (!runique && errno == EACCES &&
1003			    chmod(local, (S_IRUSR|S_IWUSR)) < 0) {
1004				warn("Can't chmod `%s'", local);
1005				code = -1;
1006				goto cleanuprecv;
1007			}
1008			if (runique && errno == EACCES &&
1009			   (local = gunique(local)) == NULL) {
1010				code = -1;
1011				goto cleanuprecv;
1012			}
1013		}
1014		else if (runique && (local = gunique(local)) == NULL) {
1015			code = -1;
1016			goto cleanuprecv;
1017		}
1018	}
1019	if (!is_retr) {
1020		if (curtype != TYPE_A)
1021			changetype(TYPE_A, 0);
1022	} else {
1023		if (curtype != type)
1024			changetype(type, 0);
1025		filesize = remotesize(remote, 0);
1026		if (code == 421 || code == -1)
1027			goto cleanuprecv;
1028	}
1029	if (initconn()) {
1030		code = -1;
1031		goto cleanuprecv;
1032	}
1033	if (sigsetjmp(xferabort, 1))
1034		goto abort;
1035	if (is_retr && restart_point &&
1036	    command("REST " LLF, (LLT) restart_point) != CONTINUE)
1037		goto cleanuprecv;
1038	if (! EMPTYSTRING(remote)) {
1039		if (command("%s %s", cmd, remote) != PRELIM)
1040			goto cleanuprecv;
1041	} else {
1042		if (command("%s", cmd) != PRELIM)
1043			goto cleanuprecv;
1044	}
1045	din = dataconn("r");
1046	if (din == NULL)
1047		goto abort;
1048	if (!ignorespecial && strcmp(local, "-") == 0) {
1049		fout = stdout;
1050		progress = 0;
1051		preserve = 0;
1052	} else if (!ignorespecial && *local == '|') {
1053		oldpipe = xsignal(SIGPIPE, SIG_IGN);
1054		fout = popen(local + 1, "w");
1055		if (fout == NULL) {
1056			warn("Can't execute `%s'", local+1);
1057			goto abort;
1058		}
1059		progress = 0;
1060		preserve = 0;
1061		closefunc = pclose;
1062	} else {
1063		fout = fopen(local, lmode);
1064		if (fout == NULL) {
1065			warn("Can't open `%s'", local);
1066			goto abort;
1067		}
1068		closefunc = fclose;
1069	}
1070
1071	if (fstat(fileno(fout), &st) != -1 && !S_ISREG(st.st_mode)) {
1072		progress = 0;
1073		preserve = 0;
1074	}
1075	assert(rcvbuf_size > 0);
1076	if ((size_t)rcvbuf_size > bufsize) {
1077		if (buf)
1078			(void)free(buf);
1079		bufsize = rcvbuf_size;
1080		buf = ftp_malloc(bufsize);
1081	}
1082
1083	progressmeter(-1);
1084	hash_interval = (hash && (!progress || filesize < 0)) ? mark : 0;
1085
1086	switch (curtype) {
1087
1088	case TYPE_I:
1089	case TYPE_L:
1090		if (is_retr && restart_point &&
1091		    lseek(fileno(fout), restart_point, SEEK_SET) < 0) {
1092			warn("Can't seek to restart `%s'", local);
1093			goto cleanuprecv;
1094		}
1095		c = copy_bytes(fileno(din), fileno(fout), buf, bufsize,
1096			       rate_get, hash_interval);
1097		if (c == 1) {
1098			if (errno != EPIPE)
1099				warn("Reading from network");
1100			bytes = -1;
1101		} else if (c == 2) {
1102			warn("Writing `%s'", local);
1103		}
1104		break;
1105
1106	case TYPE_A:
1107		if (is_retr && restart_point) {
1108			int ch;
1109			off_t i;
1110
1111			if (fseeko(fout, (off_t)0, SEEK_SET) < 0)
1112				goto done;
1113			for (i = 0; i++ < restart_point;) {
1114				if ((ch = getc(fout)) == EOF)
1115					goto done;
1116				if (ch == '\n')
1117					i++;
1118			}
1119			if (fseeko(fout, (off_t)0, SEEK_CUR) < 0) {
1120 done:
1121				warn("Can't seek to restart `%s'", local);
1122				goto cleanuprecv;
1123			}
1124		}
1125		while ((c = getc(din)) != EOF) {
1126			if (c == '\n')
1127				bare_lfs++;
1128			while (c == '\r') {
1129				while (hash_interval && bytes >= hashbytes) {
1130					(void)putc('#', ttyout);
1131					(void)fflush(ttyout);
1132					hashbytes += mark;
1133				}
1134				bytes++;
1135				if ((c = getc(din)) != '\n' || tcrflag) {
1136					if (ferror(fout))
1137						goto break2;
1138					(void)putc('\r', fout);
1139					if (c == '\0') {
1140						bytes++;
1141						goto contin2;
1142					}
1143					if (c == EOF)
1144						goto contin2;
1145				}
1146			}
1147			(void)putc(c, fout);
1148			bytes++;
1149	contin2:	;
1150		}
1151 break2:
1152		if (hash_interval) {
1153			if (bytes < hashbytes)
1154				(void)putc('#', ttyout);
1155			(void)putc('\n', ttyout);
1156		}
1157		if (ferror(din)) {
1158			if (errno != EPIPE)
1159				warn("Reading from network");
1160			bytes = -1;
1161		}
1162		if (ferror(fout))
1163			warn("Writing `%s'", local);
1164		break;
1165	}
1166
1167	progressmeter(1);
1168	if (closefunc != NULL) {
1169		(*closefunc)(fout);
1170		fout = NULL;
1171	}
1172	(void)fclose(din);
1173	din = NULL;
1174	(void)getreply(0);
1175	if (bare_lfs) {
1176		fprintf(ttyout,
1177		    "WARNING! %d bare linefeeds received in ASCII mode.\n",
1178		    bare_lfs);
1179		fputs("File may not have transferred correctly.\n", ttyout);
1180	}
1181	if (bytes >= 0 && is_retr) {
1182		if (bytes > 0)
1183			ptransfer(0);
1184		if (preserve && (closefunc == fclose)) {
1185			mtime = remotemodtime(remote, 0);
1186			if (mtime != -1) {
1187				(void)gettimeofday(&tval[0], NULL);
1188				tval[1].tv_sec = mtime;
1189				tval[1].tv_usec = 0;
1190				if (utimes(local, tval) == -1) {
1191					fprintf(ttyout,
1192				"Can't change modification time on %s to %s",
1193					    local,
1194					    rfc2822time(localtime(&mtime)));
1195				}
1196			}
1197		}
1198	}
1199	goto cleanuprecv;
1200
1201 abort:
1202			/*
1203			 * abort using RFC 959 recommended IP,SYNC sequence
1204			 */
1205	if (! sigsetjmp(xferabort, 1)) {
1206			/* this is the first call */
1207		(void)xsignal(SIGINT, abort_squared);
1208		if (!cpend) {
1209			code = -1;
1210			goto cleanuprecv;
1211		}
1212		abort_remote(din);
1213	}
1214	code = -1;
1215	if (bytes > 0)
1216		ptransfer(0);
1217
1218 cleanuprecv:
1219	if (oldintr != SIG_ERR)
1220		(void)xsignal(SIGINT, oldintr);
1221	if (oldpipe != SIG_ERR)
1222		(void)xsignal(SIGPIPE, oldpipe);
1223	if (data >= 0) {
1224		(void)close(data);
1225		data = -1;
1226	}
1227	if (closefunc != NULL && fout != NULL)
1228		(*closefunc)(fout);
1229	if (din)
1230		(void)fclose(din);
1231	progress = oprogress;
1232	preserve = opreserve;
1233	bytes = 0;
1234}
1235
1236/*
1237 * Need to start a listen on the data channel before we send the command,
1238 * otherwise the server's connect may fail.
1239 */
1240int
1241initconn(void)
1242{
1243	char *p, *a;
1244	int result, tmpno = 0;
1245	int on = 1;
1246	int error;
1247	unsigned int addr[16], port[2];
1248	unsigned int af, hal, pal;
1249	socklen_t len;
1250	const char *pasvcmd = NULL;
1251	int overbose;
1252
1253#ifdef INET6
1254#ifndef NO_DEBUG
1255	if (myctladdr.su_family == AF_INET6 && ftp_debug &&
1256	    (IN6_IS_ADDR_LINKLOCAL(&myctladdr.si_su.su_sin6.sin6_addr) ||
1257	     IN6_IS_ADDR_SITELOCAL(&myctladdr.si_su.su_sin6.sin6_addr))) {
1258		warnx("Use of scoped addresses can be troublesome");
1259	}
1260#endif
1261#endif
1262
1263 reinit:
1264	if (passivemode) {
1265		data_addr = myctladdr;
1266		data = socket(data_addr.su_family, SOCK_STREAM, 0);
1267		if (data < 0) {
1268			warn("Can't create socket for data connection");
1269			return (1);
1270		}
1271		if ((options & SO_DEBUG) &&
1272		    setsockopt(data, SOL_SOCKET, SO_DEBUG,
1273				(void *)&on, sizeof(on)) == -1) {
1274			DWARN("setsockopt %s (ignored)", "SO_DEBUG");
1275		}
1276		result = COMPLETE + 1;
1277		switch (data_addr.su_family) {
1278		case AF_INET:
1279			if (epsv4 && !epsv4bad) {
1280				pasvcmd = "EPSV";
1281				overbose = verbose;
1282				if (ftp_debug == 0)
1283					verbose = -1;
1284				result = command("EPSV");
1285				verbose = overbose;
1286				if (verbose > 0 &&
1287				    (result == COMPLETE || !connected))
1288					fprintf(ttyout, "%s\n", reply_string);
1289				if (!connected)
1290					return (1);
1291				/*
1292				 * this code is to be friendly with broken
1293				 * BSDI ftpd
1294				 */
1295				if (code / 10 == 22 && code != 229) {
1296					fputs(
1297"wrong server: return code must be 229\n",
1298						ttyout);
1299					result = COMPLETE + 1;
1300				}
1301				if (result != COMPLETE) {
1302					epsv4bad = 1;
1303					DPRINTF("disabling epsv4 for this "
1304					    "connection\n");
1305				}
1306			}
1307			if (result != COMPLETE) {
1308				pasvcmd = "PASV";
1309				result = command("PASV");
1310				if (!connected)
1311					return (1);
1312			}
1313			break;
1314#ifdef INET6
1315		case AF_INET6:
1316			if (epsv6 && !epsv6bad) {
1317				pasvcmd = "EPSV";
1318				overbose = verbose;
1319				if (ftp_debug == 0)
1320					verbose = -1;
1321				result = command("EPSV");
1322				verbose = overbose;
1323				if (verbose > 0 &&
1324				    (result == COMPLETE || !connected))
1325					fprintf(ttyout, "%s\n", reply_string);
1326				if (!connected)
1327					return (1);
1328				/*
1329				 * this code is to be friendly with
1330				 * broken BSDI ftpd
1331				 */
1332				if (code / 10 == 22 && code != 229) {
1333					fputs(
1334						"wrong server: return code must be 229\n",
1335						ttyout);
1336					result = COMPLETE + 1;
1337				}
1338				if (result != COMPLETE) {
1339					epsv6bad = 1;
1340					DPRINTF("disabling epsv6 for this "
1341					    "connection\n");
1342				}
1343			}
1344			if (result != COMPLETE) {
1345				pasvcmd = "LPSV";
1346				result = command("LPSV");
1347			}
1348			if (!connected)
1349				return (1);
1350			break;
1351#endif
1352		default:
1353			result = COMPLETE + 1;
1354			break;
1355		}
1356		if (result != COMPLETE) {
1357			if (activefallback) {
1358				(void)close(data);
1359				data = -1;
1360				passivemode = 0;
1361#if 0
1362				activefallback = 0;
1363#endif
1364				goto reinit;
1365			}
1366			fputs("Passive mode refused.\n", ttyout);
1367			goto bad;
1368		}
1369
1370#define	pack2(var, off) \
1371	(((var[(off) + 0] & 0xff) << 8) | ((var[(off) + 1] & 0xff) << 0))
1372#define	pack4(var, off) \
1373	(((var[(off) + 0] & 0xff) << 24) | ((var[(off) + 1] & 0xff) << 16) | \
1374	 ((var[(off) + 2] & 0xff) << 8) | ((var[(off) + 3] & 0xff) << 0))
1375#define	UC(b)	(((int)b)&0xff)
1376
1377		/*
1378		 * What we've got at this point is a string of comma separated
1379		 * one-byte unsigned integer values, separated by commas.
1380		 */
1381		if (strcmp(pasvcmd, "PASV") == 0) {
1382			if (data_addr.su_family != AF_INET) {
1383				fputs(
1384    "Passive mode AF mismatch. Shouldn't happen!\n", ttyout);
1385				goto bad;
1386			}
1387			if (code / 10 == 22 && code != 227) {
1388				fputs("wrong server: return code must be 227\n",
1389					ttyout);
1390				goto bad;
1391			}
1392			error = sscanf(pasv, "%u,%u,%u,%u,%u,%u",
1393					&addr[0], &addr[1], &addr[2], &addr[3],
1394					&port[0], &port[1]);
1395			if (error != 6) {
1396				fputs(
1397"Passive mode address scan failure. Shouldn't happen!\n", ttyout);
1398				goto bad;
1399			}
1400			memset(&data_addr, 0, sizeof(data_addr));
1401			data_addr.su_family = AF_INET;
1402			data_addr.su_len = sizeof(struct sockaddr_in);
1403			data_addr.si_su.su_sin.sin_addr.s_addr =
1404			    htonl(pack4(addr, 0));
1405			data_addr.su_port = htons(pack2(port, 0));
1406			if (data_addr.si_su.su_sin.sin_addr.s_addr !=
1407			    hisctladdr.si_su.su_sin.sin_addr.s_addr) {
1408				fputs("Passive mode address mismatch.\n",
1409				    ttyout);
1410				goto bad;
1411			}
1412		} else if (strcmp(pasvcmd, "LPSV") == 0) {
1413			if (code / 10 == 22 && code != 228) {
1414				fputs("wrong server: return code must be 228\n",
1415					ttyout);
1416				goto bad;
1417			}
1418			switch (data_addr.su_family) {
1419			case AF_INET:
1420				error = sscanf(pasv,
1421"%u,%u,%u,%u,%u,%u,%u,%u,%u",
1422					&af, &hal,
1423					&addr[0], &addr[1], &addr[2], &addr[3],
1424					&pal, &port[0], &port[1]);
1425				if (error != 9) {
1426					fputs(
1427"Passive mode address scan failure. Shouldn't happen!\n", ttyout);
1428					goto bad;
1429				}
1430				if (af != 4 || hal != 4 || pal != 2) {
1431					fputs(
1432"Passive mode AF mismatch. Shouldn't happen!\n", ttyout);
1433					goto bad;
1434				}
1435
1436				memset(&data_addr, 0, sizeof(data_addr));
1437				data_addr.su_family = AF_INET;
1438				data_addr.su_len = sizeof(struct sockaddr_in);
1439				data_addr.si_su.su_sin.sin_addr.s_addr =
1440				    htonl(pack4(addr, 0));
1441				data_addr.su_port = htons(pack2(port, 0));
1442				if (data_addr.si_su.su_sin.sin_addr.s_addr !=
1443				    hisctladdr.si_su.su_sin.sin_addr.s_addr) {
1444					fputs("Passive mode address mismatch.\n",
1445					    ttyout);
1446					goto bad;
1447				}
1448				break;
1449#ifdef INET6
1450			case AF_INET6:
1451				error = sscanf(pasv,
1452"%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u",
1453					&af, &hal,
1454					&addr[0], &addr[1], &addr[2], &addr[3],
1455					&addr[4], &addr[5], &addr[6], &addr[7],
1456					&addr[8], &addr[9], &addr[10],
1457					&addr[11], &addr[12], &addr[13],
1458					&addr[14], &addr[15],
1459					&pal, &port[0], &port[1]);
1460				if (error != 21) {
1461					fputs(
1462"Passive mode address scan failure. Shouldn't happen!\n", ttyout);
1463					goto bad;
1464				}
1465				if (af != 6 || hal != 16 || pal != 2) {
1466					fputs(
1467"Passive mode AF mismatch. Shouldn't happen!\n", ttyout);
1468					goto bad;
1469				}
1470
1471				memset(&data_addr, 0, sizeof(data_addr));
1472				data_addr.su_family = AF_INET6;
1473				data_addr.su_len = sizeof(struct sockaddr_in6);
1474			    {
1475				size_t i;
1476				for (i = 0; i < sizeof(struct in6_addr); i++) {
1477					data_addr.si_su.su_sin6.sin6_addr.s6_addr[i] =
1478					    UC(addr[i]);
1479				}
1480			    }
1481				data_addr.su_port = htons(pack2(port, 0));
1482				if (memcmp(
1483				    &data_addr.si_su.su_sin6.sin6_addr,
1484				    &hisctladdr.si_su.su_sin6.sin6_addr,
1485				    sizeof(data_addr.si_su.su_sin6.sin6_addr))) {
1486					fputs("Passive mode address mismatch.\n",
1487					    ttyout);
1488					goto bad;
1489				}
1490				break;
1491#endif
1492			default:
1493				fputs("Unknown passive mode AF.\n", ttyout);
1494				goto bad;
1495			}
1496		} else if (strcmp(pasvcmd, "EPSV") == 0) {
1497			char delim[4];
1498
1499			port[0] = 0;
1500			if (code / 10 == 22 && code != 229) {
1501				fputs("wrong server: return code must be 229\n",
1502					ttyout);
1503				goto bad;
1504			}
1505			if (sscanf(pasv, "%c%c%c%d%c", &delim[0],
1506					&delim[1], &delim[2], &port[1],
1507					&delim[3]) != 5) {
1508				fputs("parse error!\n", ttyout);
1509				goto bad;
1510			}
1511			if (delim[0] != delim[1] || delim[0] != delim[2]
1512			 || delim[0] != delim[3]) {
1513				fputs("parse error!\n", ttyout);
1514				goto bad;
1515			}
1516			data_addr = hisctladdr;
1517			data_addr.su_port = htons(port[1]);
1518		} else
1519			goto bad;
1520
1521		if (ftp_connect(data, (struct sockaddr *)&data_addr.si_su,
1522		    data_addr.su_len, 1) < 0) {
1523			if (activefallback) {
1524				(void)close(data);
1525				data = -1;
1526				passivemode = 0;
1527#if 0
1528				activefallback = 0;
1529#endif
1530				goto reinit;
1531			}
1532			goto bad;
1533		}
1534#ifdef IPTOS_THROUGHPUT
1535		if (data_addr.su_family == AF_INET) {
1536			on = IPTOS_THROUGHPUT;
1537			if (setsockopt(data, IPPROTO_IP, IP_TOS,
1538					(void *)&on, sizeof(on)) == -1) {
1539				DWARN("setsockopt %s (ignored)",
1540				    "IPTOS_THROUGHPUT");
1541			}
1542		}
1543#endif
1544		return (0);
1545	}
1546
1547 noport:
1548	data_addr = myctladdr;
1549	if (sendport)
1550		data_addr.su_port = 0;	/* let system pick one */
1551	if (data != -1)
1552		(void)close(data);
1553	data = socket(data_addr.su_family, SOCK_STREAM, 0);
1554	if (data < 0) {
1555		warn("Can't create socket for data connection");
1556		if (tmpno)
1557			sendport = 1;
1558		return (1);
1559	}
1560	if (!sendport)
1561		if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR,
1562				(void *)&on, sizeof(on)) == -1) {
1563			warn("Can't set SO_REUSEADDR on data connection");
1564			goto bad;
1565		}
1566	if (bind(data, (struct sockaddr *)&data_addr.si_su,
1567	    data_addr.su_len) < 0) {
1568		warn("Can't bind for data connection");
1569		goto bad;
1570	}
1571	if ((options & SO_DEBUG) &&
1572	    setsockopt(data, SOL_SOCKET, SO_DEBUG,
1573			(void *)&on, sizeof(on)) == -1) {
1574		DWARN("setsockopt %s (ignored)", "SO_DEBUG");
1575	}
1576	len = sizeof(data_addr.si_su);
1577	memset((char *)&data_addr, 0, sizeof (data_addr));
1578	if (getsockname(data, (struct sockaddr *)&data_addr.si_su, &len) == -1) {
1579		warn("Can't determine my address of data connection");
1580		goto bad;
1581	}
1582	data_addr.su_len = len;
1583	if (ftp_listen(data, 1) < 0)
1584		warn("Can't listen to data connection");
1585
1586	if (sendport) {
1587		char hname[NI_MAXHOST], sname[NI_MAXSERV];
1588		struct sockinet tmp;
1589
1590		switch (data_addr.su_family) {
1591		case AF_INET:
1592			if (!epsv4 || epsv4bad) {
1593				result = COMPLETE + 1;
1594				break;
1595			}
1596#ifdef INET6
1597			/* FALLTHROUGH */
1598		case AF_INET6:
1599			if (!epsv6 || epsv6bad) {
1600				result = COMPLETE + 1;
1601				break;
1602			}
1603#endif
1604			af = (data_addr.su_family == AF_INET) ? 1 : 2;
1605			tmp = data_addr;
1606#ifdef INET6
1607			if (tmp.su_family == AF_INET6)
1608				tmp.si_su.su_sin6.sin6_scope_id = 0;
1609#endif
1610			if (getnameinfo((struct sockaddr *)&tmp.si_su,
1611			    tmp.su_len, hname, sizeof(hname), sname,
1612			    sizeof(sname), NI_NUMERICHOST | NI_NUMERICSERV)) {
1613				result = ERROR;
1614			} else {
1615				overbose = verbose;
1616				if (ftp_debug == 0)
1617					verbose = -1;
1618				result = command("EPRT |%u|%s|%s|", af, hname,
1619				    sname);
1620				verbose = overbose;
1621				if (verbose > 0 &&
1622				    (result == COMPLETE || !connected))
1623					fprintf(ttyout, "%s\n", reply_string);
1624				if (!connected)
1625					return (1);
1626				if (result != COMPLETE) {
1627					epsv4bad = 1;
1628					DPRINTF("disabling epsv4 for this "
1629					    "connection\n");
1630				}
1631			}
1632			break;
1633		default:
1634			result = COMPLETE + 1;
1635			break;
1636		}
1637		if (result == COMPLETE)
1638			goto skip_port;
1639
1640		switch (data_addr.su_family) {
1641		case AF_INET:
1642			a = (char *)&data_addr.si_su.su_sin.sin_addr;
1643			p = (char *)&data_addr.su_port;
1644			result = command("PORT %d,%d,%d,%d,%d,%d",
1645				 UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
1646				 UC(p[0]), UC(p[1]));
1647			break;
1648#ifdef INET6
1649		case AF_INET6: {
1650			uint8_t ua[sizeof(data_addr.si_su.su_sin6.sin6_addr)];
1651			uint8_t up[sizeof(data_addr.su_port)];
1652
1653			memcpy(ua, &data_addr.si_su.su_sin6.sin6_addr,
1654			    sizeof(ua));
1655			memcpy(up, &data_addr.su_port, sizeof(up));
1656
1657			result = command(
1658	"LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
1659				 6, 16,
1660				  ua[0],  ua[1],  ua[2],  ua[3],
1661				  ua[4],  ua[5],  ua[6],  ua[7],
1662				  ua[8],  ua[9], ua[10], ua[11],
1663				 ua[12], ua[13], ua[14], ua[15],
1664				 2,
1665				 up[0], up[1]);
1666			break;
1667		}
1668#endif
1669		default:
1670			result = COMPLETE + 1; /* xxx */
1671		}
1672		if (!connected)
1673			return (1);
1674	skip_port:
1675
1676		if (result == ERROR && sendport == -1) {
1677			sendport = 0;
1678			tmpno = 1;
1679			goto noport;
1680		}
1681		return (result != COMPLETE);
1682	}
1683	if (tmpno)
1684		sendport = 1;
1685#ifdef IPTOS_THROUGHPUT
1686	if (data_addr.su_family == AF_INET) {
1687		on = IPTOS_THROUGHPUT;
1688		if (setsockopt(data, IPPROTO_IP, IP_TOS,
1689				(void *)&on, sizeof(on)) == -1) {
1690			DWARN("setsockopt %s (ignored)", "IPTOS_THROUGHPUT");
1691		}
1692	}
1693#endif
1694	return (0);
1695 bad:
1696	(void)close(data);
1697	data = -1;
1698	if (tmpno)
1699		sendport = 1;
1700	return (1);
1701}
1702
1703FILE *
1704dataconn(const char *lmode)
1705{
1706	struct sockinet	from;
1707	int		s, flags, rv, timeout;
1708	struct timeval	endtime, now, td;
1709	struct pollfd	pfd[1];
1710	socklen_t	fromlen;
1711
1712	if (passivemode)	/* passive data connection */
1713		return (fdopen(data, lmode));
1714
1715				/* active mode data connection */
1716
1717	if ((flags = fcntl(data, F_GETFL, 0)) == -1)
1718		goto dataconn_failed;		/* get current socket flags  */
1719	if (fcntl(data, F_SETFL, flags | O_NONBLOCK) == -1)
1720		goto dataconn_failed;		/* set non-blocking connect */
1721
1722		/* NOTE: we now must restore socket flags on successful exit */
1723
1724				/* limit time waiting on listening socket */
1725	pfd[0].fd = data;
1726	pfd[0].events = POLLIN;
1727	(void)gettimeofday(&endtime, NULL);	/* determine end time */
1728	endtime.tv_sec += (quit_time > 0) ? quit_time: 60;
1729						/* without -q, default to 60s */
1730	do {
1731		(void)gettimeofday(&now, NULL);
1732		timersub(&endtime, &now, &td);
1733		timeout = td.tv_sec * 1000 + td.tv_usec/1000;
1734		if (timeout < 0)
1735			timeout = 0;
1736		rv = ftp_poll(pfd, 1, timeout);
1737			/* loop until poll !EINTR && !EAGAIN */
1738	} while (rv == -1 && (errno == EINTR || errno == EAGAIN));
1739	if (rv == -1) {
1740		warn("Can't poll waiting before accept");
1741		goto dataconn_failed;
1742	}
1743	if (rv == 0) {
1744		warnx("Poll timeout waiting before accept");
1745		goto dataconn_failed;
1746	}
1747
1748				/* (non-blocking) accept the connection */
1749	fromlen = myctladdr.su_len;
1750	do {
1751		s = accept(data, (struct sockaddr *) &from.si_su, &fromlen);
1752			/* loop until accept !EINTR && !EAGAIN */
1753	} while (s == -1 && (errno == EINTR || errno == EAGAIN));
1754	if (s == -1) {
1755		warn("Can't accept data connection");
1756		goto dataconn_failed;
1757	}
1758
1759	(void)close(data);
1760	data = s;
1761	if (fcntl(data, F_SETFL, flags) == -1)	/* restore socket flags */
1762		goto dataconn_failed;
1763
1764#ifdef IPTOS_THROUGHPUT
1765	if (from.su_family == AF_INET) {
1766		int tos = IPTOS_THROUGHPUT;
1767		if (setsockopt(s, IPPROTO_IP, IP_TOS,
1768				(void *)&tos, sizeof(tos)) == -1) {
1769			DWARN("setsockopt %s (ignored)", "IPTOS_THROUGHPUT");
1770		}
1771	}
1772#endif
1773	return (fdopen(data, lmode));
1774
1775 dataconn_failed:
1776	(void)close(data);
1777	data = -1;
1778	return (NULL);
1779}
1780
1781void
1782psabort(int notused)
1783{
1784	int oerrno = errno;
1785
1786	sigint_raised = 1;
1787	alarmtimer(0);
1788	abrtflag++;
1789	errno = oerrno;
1790}
1791
1792void
1793pswitch(int flag)
1794{
1795	sigfunc oldintr;
1796	static struct comvars {
1797		int connect;
1798		char name[MAXHOSTNAMELEN];
1799		struct sockinet mctl;
1800		struct sockinet hctl;
1801		FILE *in;
1802		FILE *out;
1803		int tpe;
1804		int curtpe;
1805		int cpnd;
1806		int sunqe;
1807		int runqe;
1808		int mcse;
1809		int ntflg;
1810		char nti[17];
1811		char nto[17];
1812		int mapflg;
1813		char mi[MAXPATHLEN];
1814		char mo[MAXPATHLEN];
1815	} proxstruct, tmpstruct;
1816	struct comvars *ip, *op;
1817
1818	abrtflag = 0;
1819	oldintr = xsignal(SIGINT, psabort);
1820	if (flag) {
1821		if (proxy)
1822			return;
1823		ip = &tmpstruct;
1824		op = &proxstruct;
1825		proxy++;
1826	} else {
1827		if (!proxy)
1828			return;
1829		ip = &proxstruct;
1830		op = &tmpstruct;
1831		proxy = 0;
1832	}
1833	ip->connect = connected;
1834	connected = op->connect;
1835	if (hostname)
1836		(void)strlcpy(ip->name, hostname, sizeof(ip->name));
1837	else
1838		ip->name[0] = '\0';
1839	hostname = op->name;
1840	ip->hctl = hisctladdr;
1841	hisctladdr = op->hctl;
1842	ip->mctl = myctladdr;
1843	myctladdr = op->mctl;
1844	ip->in = cin;
1845	cin = op->in;
1846	ip->out = cout;
1847	cout = op->out;
1848	ip->tpe = type;
1849	type = op->tpe;
1850	ip->curtpe = curtype;
1851	curtype = op->curtpe;
1852	ip->cpnd = cpend;
1853	cpend = op->cpnd;
1854	ip->sunqe = sunique;
1855	sunique = op->sunqe;
1856	ip->runqe = runique;
1857	runique = op->runqe;
1858	ip->mcse = mcase;
1859	mcase = op->mcse;
1860	ip->ntflg = ntflag;
1861	ntflag = op->ntflg;
1862	(void)strlcpy(ip->nti, ntin, sizeof(ip->nti));
1863	(void)strlcpy(ntin, op->nti, sizeof(ntin));
1864	(void)strlcpy(ip->nto, ntout, sizeof(ip->nto));
1865	(void)strlcpy(ntout, op->nto, sizeof(ntout));
1866	ip->mapflg = mapflag;
1867	mapflag = op->mapflg;
1868	(void)strlcpy(ip->mi, mapin, sizeof(ip->mi));
1869	(void)strlcpy(mapin, op->mi, sizeof(mapin));
1870	(void)strlcpy(ip->mo, mapout, sizeof(ip->mo));
1871	(void)strlcpy(mapout, op->mo, sizeof(mapout));
1872	(void)xsignal(SIGINT, oldintr);
1873	if (abrtflag) {
1874		abrtflag = 0;
1875		(*oldintr)(SIGINT);
1876	}
1877}
1878
1879__dead static void
1880abortpt(int notused)
1881{
1882
1883	sigint_raised = 1;
1884	alarmtimer(0);
1885	if (fromatty)
1886		write(fileno(ttyout), "\n", 1);
1887	ptabflg++;
1888	mflag = 0;
1889	abrtflag = 0;
1890	siglongjmp(ptabort, 1);
1891}
1892
1893void
1894proxtrans(const char *cmd, const char *local, const char *remote)
1895{
1896	sigfunc volatile oldintr;
1897	int prox_type, nfnd;
1898	int volatile secndflag;
1899	const char *volatile cmd2;
1900
1901	oldintr = SIG_ERR;
1902	secndflag = 0;
1903	if (strcmp(cmd, "RETR"))
1904		cmd2 = "RETR";
1905	else
1906		cmd2 = runique ? "STOU" : "STOR";
1907	if ((prox_type = type) == 0) {
1908		if (unix_server && unix_proxy)
1909			prox_type = TYPE_I;
1910		else
1911			prox_type = TYPE_A;
1912	}
1913	if (curtype != prox_type)
1914		changetype(prox_type, 1);
1915	if (command("PASV") != COMPLETE) {
1916		fputs("proxy server does not support third party transfers.\n",
1917		    ttyout);
1918		return;
1919	}
1920	pswitch(0);
1921	if (!connected) {
1922		fputs("No primary connection.\n", ttyout);
1923		pswitch(1);
1924		code = -1;
1925		return;
1926	}
1927	if (curtype != prox_type)
1928		changetype(prox_type, 1);
1929	if (command("PORT %s", pasv) != COMPLETE) {
1930		pswitch(1);
1931		return;
1932	}
1933	if (sigsetjmp(ptabort, 1))
1934		goto abort;
1935	oldintr = xsignal(SIGINT, abortpt);
1936	if ((restart_point &&
1937	    (command("REST " LLF, (LLT) restart_point) != CONTINUE))
1938	    || (command("%s %s", cmd, remote) != PRELIM)) {
1939		(void)xsignal(SIGINT, oldintr);
1940		pswitch(1);
1941		return;
1942	}
1943	sleep(2);
1944	pswitch(1);
1945	secndflag++;
1946	if ((restart_point &&
1947	    (command("REST " LLF, (LLT) restart_point) != CONTINUE))
1948	    || (command("%s %s", cmd2, local) != PRELIM))
1949		goto abort;
1950	ptflag++;
1951	(void)getreply(0);
1952	pswitch(0);
1953	(void)getreply(0);
1954	(void)xsignal(SIGINT, oldintr);
1955	pswitch(1);
1956	ptflag = 0;
1957	fprintf(ttyout, "local: %s remote: %s\n", local, remote);
1958	return;
1959 abort:
1960	if (sigsetjmp(xferabort, 1)) {
1961		(void)xsignal(SIGINT, oldintr);
1962		return;
1963	}
1964	(void)xsignal(SIGINT, abort_squared);
1965	ptflag = 0;
1966	if (strcmp(cmd, "RETR") && !proxy)
1967		pswitch(1);
1968	else if (!strcmp(cmd, "RETR") && proxy)
1969		pswitch(0);
1970	if (!cpend && !secndflag) {  /* only here if cmd = "STOR" (proxy=1) */
1971		if (command("%s %s", cmd2, local) != PRELIM) {
1972			pswitch(0);
1973			if (cpend)
1974				abort_remote(NULL);
1975		}
1976		pswitch(1);
1977		if (ptabflg)
1978			code = -1;
1979		(void)xsignal(SIGINT, oldintr);
1980		return;
1981	}
1982	if (cpend)
1983		abort_remote(NULL);
1984	pswitch(!proxy);
1985	if (!cpend && !secndflag) {  /* only if cmd = "RETR" (proxy=1) */
1986		if (command("%s %s", cmd2, local) != PRELIM) {
1987			pswitch(0);
1988			if (cpend)
1989				abort_remote(NULL);
1990			pswitch(1);
1991			if (ptabflg)
1992				code = -1;
1993			(void)xsignal(SIGINT, oldintr);
1994			return;
1995		}
1996	}
1997	if (cpend)
1998		abort_remote(NULL);
1999	pswitch(!proxy);
2000	if (cpend) {
2001		if ((nfnd = empty(cin, NULL, 10)) <= 0) {
2002			if (nfnd < 0)
2003				warn("Error aborting proxy command");
2004			if (ptabflg)
2005				code = -1;
2006			lostpeer(0);
2007		}
2008		(void)getreply(0);
2009		(void)getreply(0);
2010	}
2011	if (proxy)
2012		pswitch(0);
2013	pswitch(1);
2014	if (ptabflg)
2015		code = -1;
2016	(void)xsignal(SIGINT, oldintr);
2017}
2018
2019void
2020reset(int argc, char *argv[])
2021{
2022	int nfnd = 1;
2023
2024	if (argc == 0 && argv != NULL) {
2025		UPRINTF("usage: %s\n", argv[0]);
2026		code = -1;
2027		return;
2028	}
2029	while (nfnd > 0) {
2030		if ((nfnd = empty(cin, NULL, 0)) < 0) {
2031			warn("Error resetting connection");
2032			code = -1;
2033			lostpeer(0);
2034		} else if (nfnd)
2035			(void)getreply(0);
2036	}
2037}
2038
2039char *
2040gunique(const char *local)
2041{
2042	static char new[MAXPATHLEN];
2043	char *cp = strrchr(local, '/');
2044	int d, count=0, len;
2045	char ext = '1';
2046
2047	if (cp)
2048		*cp = '\0';
2049	d = access(cp == local ? "/" : cp ? local : ".", W_OK);
2050	if (cp)
2051		*cp = '/';
2052	if (d < 0) {
2053		warn("Can't access `%s'", local);
2054		return (NULL);
2055	}
2056	len = strlcpy(new, local, sizeof(new));
2057	cp = &new[len];
2058	*cp++ = '.';
2059	while (!d) {
2060		if (++count == 100) {
2061			fputs("runique: can't find unique file name.\n",
2062			    ttyout);
2063			return (NULL);
2064		}
2065		*cp++ = ext;
2066		*cp = '\0';
2067		if (ext == '9')
2068			ext = '0';
2069		else
2070			ext++;
2071		if ((d = access(new, F_OK)) < 0)
2072			break;
2073		if (ext != '0')
2074			cp--;
2075		else if (*(cp - 2) == '.')
2076			*(cp - 1) = '1';
2077		else {
2078			*(cp - 2) = *(cp - 2) + 1;
2079			cp--;
2080		}
2081	}
2082	return (new);
2083}
2084
2085/*
2086 * abort_squared --
2087 *	aborts abort_remote(). lostpeer() is called because if the user is
2088 *	too impatient to wait or there's another problem then ftp really
2089 *	needs to get back to a known state.
2090 */
2091static void
2092abort_squared(int signo)
2093{
2094	char msgbuf[100];
2095	size_t len;
2096
2097	sigint_raised = 1;
2098	alarmtimer(0);
2099	len = strlcpy(msgbuf, "\nremote abort aborted; closing connection.\n",
2100	    sizeof(msgbuf));
2101	write(fileno(ttyout), msgbuf, len);
2102	lostpeer(signo);
2103	siglongjmp(xferabort, 1);
2104}
2105
2106void
2107abort_remote(FILE *din)
2108{
2109	unsigned char buf[BUFSIZ];
2110	int nfnd;
2111
2112	if (cout == NULL) {
2113		warnx("Lost control connection for abort");
2114		if (ptabflg)
2115			code = -1;
2116		lostpeer(0);
2117		return;
2118	}
2119	/*
2120	 * send IAC in urgent mode instead of DM because 4.3BSD places oob mark
2121	 * after urgent byte rather than before as is protocol now
2122	 */
2123	buf[0] = IAC;
2124	buf[1] = IP;
2125	buf[2] = IAC;
2126	if (send(fileno(cout), buf, 3, MSG_OOB) != 3)
2127		warn("Can't send abort message");
2128	fprintf(cout, "%cABOR\r\n", DM);
2129	(void)fflush(cout);
2130	if ((nfnd = empty(cin, din, 10)) <= 0) {
2131		if (nfnd < 0)
2132			warn("Can't send abort message");
2133		if (ptabflg)
2134			code = -1;
2135		lostpeer(0);
2136	}
2137	if (din && (nfnd & 2)) {
2138		while (read(fileno(din), buf, BUFSIZ) > 0)
2139			continue;
2140	}
2141	if (getreply(0) == ERROR && code == 552) {
2142		/* 552 needed for nic style abort */
2143		(void)getreply(0);
2144	}
2145	(void)getreply(0);
2146}
2147
2148/*
2149 * Ensure that ai->ai_addr is NOT an IPv4 mapped address.
2150 * IPv4 mapped address complicates too many things in FTP
2151 * protocol handling, as FTP protocol is defined differently
2152 * between IPv4 and IPv6.
2153 *
2154 * This may not be the best way to handle this situation,
2155 * since the semantics of IPv4 mapped address is defined in
2156 * the kernel.  There are configurations where we should use
2157 * IPv4 mapped address as native IPv6 address, not as
2158 * "an IPv6 address that embeds IPv4 address" (namely, SIIT).
2159 *
2160 * More complete solution would be to have an additional
2161 * getsockopt to grab "real" peername/sockname.  "real"
2162 * peername/sockname will be AF_INET if IPv4 mapped address
2163 * is used to embed IPv4 address, and will be AF_INET6 if
2164 * we use it as native.  What a mess!
2165 */
2166void
2167ai_unmapped(struct addrinfo *ai)
2168{
2169#ifdef INET6
2170	struct sockaddr_in6 *sin6;
2171	struct sockaddr_in sin;
2172	socklen_t len;
2173
2174	if (ai->ai_family != AF_INET6)
2175		return;
2176	if (ai->ai_addrlen != sizeof(struct sockaddr_in6) ||
2177	    sizeof(sin) > ai->ai_addrlen)
2178		return;
2179	sin6 = (struct sockaddr_in6 *)ai->ai_addr;
2180	if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
2181		return;
2182
2183	memset(&sin, 0, sizeof(sin));
2184	sin.sin_family = AF_INET;
2185	len = sizeof(struct sockaddr_in);
2186	memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[12],
2187	    sizeof(sin.sin_addr));
2188	sin.sin_port = sin6->sin6_port;
2189
2190	ai->ai_family = AF_INET;
2191#if defined(HAVE_STRUCT_SOCKADDR_IN_SIN_LEN)
2192	sin.sin_len = len;
2193#endif
2194	memcpy(ai->ai_addr, &sin, len);
2195	ai->ai_addrlen = len;
2196#endif
2197}
2198
2199#ifdef NO_USAGE
2200void
2201xusage(void)
2202{
2203	fputs("Usage error\n", ttyout);
2204}
2205#endif
2206