Module Name:    othersrc
Committed By:   lukem
Date:           Sat Jul  4 09:59:07 UTC 2020

Modified Files:
        othersrc/usr.bin/tnftp/src: cmds.c complete.c domacro.c extern.h
            fetch.c ftp.1 ftp.c ftp_var.h main.c progressbar.c ssl.c ssl.h
            util.c version.h

Log Message:
Merge NetBSD-ftp 20200608


To generate a diff of this commit:
cvs rdiff -u -r1.18 -r1.19 othersrc/usr.bin/tnftp/src/cmds.c
cvs rdiff -u -r1.10 -r1.11 othersrc/usr.bin/tnftp/src/complete.c
cvs rdiff -u -r1.8 -r1.9 othersrc/usr.bin/tnftp/src/domacro.c
cvs rdiff -u -r1.13 -r1.14 othersrc/usr.bin/tnftp/src/extern.h
cvs rdiff -u -r1.23 -r1.24 othersrc/usr.bin/tnftp/src/fetch.c \
    othersrc/usr.bin/tnftp/src/util.c
cvs rdiff -u -r1.15 -r1.16 othersrc/usr.bin/tnftp/src/ftp.1 \
    othersrc/usr.bin/tnftp/src/progressbar.c
cvs rdiff -u -r1.21 -r1.22 othersrc/usr.bin/tnftp/src/ftp.c
cvs rdiff -u -r1.12 -r1.13 othersrc/usr.bin/tnftp/src/ftp_var.h
cvs rdiff -u -r1.20 -r1.21 othersrc/usr.bin/tnftp/src/main.c
cvs rdiff -u -r1.3 -r1.4 othersrc/usr.bin/tnftp/src/ssl.c \
    othersrc/usr.bin/tnftp/src/ssl.h
cvs rdiff -u -r1.7 -r1.8 othersrc/usr.bin/tnftp/src/version.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: othersrc/usr.bin/tnftp/src/cmds.c
diff -u othersrc/usr.bin/tnftp/src/cmds.c:1.18 othersrc/usr.bin/tnftp/src/cmds.c:1.19
--- othersrc/usr.bin/tnftp/src/cmds.c:1.18	Sun May  5 11:17:30 2013
+++ othersrc/usr.bin/tnftp/src/cmds.c	Sat Jul  4 09:59:07 2020
@@ -1,5 +1,5 @@
-/*	$NetBSD: cmds.c,v 1.18 2013/05/05 11:17:30 lukem Exp $	*/
-/*	from	NetBSD: cmds.c,v 1.135 2012/12/22 16:57:09 christos Exp	*/
+/*	$NetBSD: cmds.c,v 1.19 2020/07/04 09:59:07 lukem Exp $	*/
+/*	from	NetBSD: cmds.c,v 1.140 2019/02/06 07:56:42 martin Exp	*/
 
 /*-
  * Copyright (c) 1996-2009 The NetBSD Foundation, Inc.
@@ -101,7 +101,7 @@
 #if 0
 static char sccsid[] = "@(#)cmds.c	8.6 (Berkeley) 10/9/94";
 #else
-__RCSID(" NetBSD: cmds.c,v 1.135 2012/12/22 16:57:09 christos Exp  ");
+__RCSID(" NetBSD: cmds.c,v 1.140 2019/02/06 07:56:42 martin Exp  ");
 #endif
 #endif /* not lint */
 
@@ -1165,7 +1165,8 @@ cd(int argc, char *argv[])
 	}
 	if (r == COMPLETE) {
 		dirchange = 1;
-		updateremotecwd();
+		remotecwd[0] = '\0';
+		remcwdvalid = 0;
 	}
 }
 
@@ -1551,9 +1552,9 @@ pwd(int argc, char *argv[])
 		UPRINTF("usage: %s\n", argv[0]);
 		return;
 	}
-	if (! remotecwd[0])
+	if (!remcwdvalid || remotecwd[0] == '\0')
 		updateremotecwd();
-	if (! remotecwd[0])
+	if (remotecwd[0] == '\0')
 		fprintf(ttyout, "Unable to determine remote directory\n");
 	else {
 		fprintf(ttyout, "Remote directory: %s\n", remotecwd);
@@ -1782,6 +1783,18 @@ quit(int argc, char *argv[])
 	exit(0);
 }
 
+void __dead
+justquit(void)
+{
+
+	quit(0, NULL);
+	/*
+	 * quit is not __dead, but for our invocation it never will return,
+	 * but some compilers are not smart enough to find this out.
+	 */
+	exit(0);
+}
+
 /*
  * Terminate session, but don't exit.
  * May be called with 0, NULL.
@@ -1974,15 +1987,15 @@ dotrans(char *dst, size_t dlen, const ch
 	char *cp2 = dst;
 	size_t i, ostop;
 
-	for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
+	for (ostop = 0; ntout[ostop] && ostop < sizeof(ntout); ostop++)
 		continue;
 	for (cp1 = src; *cp1; cp1++) {
 		int found = 0;
-		for (i = 0; *(ntin + i) && i < 16; i++) {
-			if (*cp1 == *(ntin + i)) {
+		for (i = 0; i < sizeof(ntin) && ntin[i]; i++) {
+			if (*cp1 == ntin[i]) {
 				found++;
 				if (i < ostop) {
-					*cp2++ = *(ntout + i);
+					*cp2++ = ntout[i];
 					if (cp2 - dst >= (ptrdiff_t)(dlen - 1))
 						goto out;
 				}
@@ -2191,7 +2204,7 @@ LOOP:
 					}
 					break;
 				}
-				/* intentional drop through */
+				/* FALLTHROUGH */
 			default:
 				*cp2++ = *cp1;
 				break;
@@ -2366,7 +2379,8 @@ cdup(int argc, char *argv[])
 	}
 	if (r == COMPLETE) {
 		dirchange = 1;
-		updateremotecwd();
+		remotecwd[0] = '\0';
+		remcwdvalid = 0;
 	}
 }
 

Index: othersrc/usr.bin/tnftp/src/complete.c
diff -u othersrc/usr.bin/tnftp/src/complete.c:1.10 othersrc/usr.bin/tnftp/src/complete.c:1.11
--- othersrc/usr.bin/tnftp/src/complete.c:1.10	Wed May 20 12:53:47 2009
+++ othersrc/usr.bin/tnftp/src/complete.c	Sat Jul  4 09:59:07 2020
@@ -1,5 +1,5 @@
-/*	$NetBSD: complete.c,v 1.10 2009/05/20 12:53:47 lukem Exp $	*/
-/*	from	NetBSD: complete.c,v 1.46 2009/04/12 10:18:52 lukem Exp	*/
+/*	$NetBSD: complete.c,v 1.11 2020/07/04 09:59:07 lukem Exp $	*/
+/*	from	NetBSD: complete.c,v 1.47 2019/01/28 12:04:16 christos Exp	*/
 
 /*-
  * Copyright (c) 1997-2009 The NetBSD Foundation, Inc.
@@ -36,7 +36,7 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID(" NetBSD: complete.c,v 1.46 2009/04/12 10:18:52 lukem Exp  ");
+__RCSID(" NetBSD: complete.c,v 1.47 2019/01/28 12:04:16 christos Exp  ");
 #endif /* not lint */
 
 /*
@@ -106,11 +106,10 @@ complete_ambiguous(char *word, int list,
 	}
 
 	if (!list) {
-		matchlen = 0;
 		lastmatch = words->sl_str[0];
 		matchlen = strlen(lastmatch);
 		for (i = 1 ; i < words->sl_cur ; i++) {
-			for (j = wordlen ; j < strlen(words->sl_str[i]); j++)
+			for (j = wordlen; j < strlen(words->sl_str[i]); j++)
 				if (lastmatch[j] != words->sl_str[i][j])
 					break;
 			if (j < matchlen)

Index: othersrc/usr.bin/tnftp/src/domacro.c
diff -u othersrc/usr.bin/tnftp/src/domacro.c:1.8 othersrc/usr.bin/tnftp/src/domacro.c:1.9
--- othersrc/usr.bin/tnftp/src/domacro.c:1.8	Wed May 20 12:53:47 2009
+++ othersrc/usr.bin/tnftp/src/domacro.c	Sat Jul  4 09:59:07 2020
@@ -1,5 +1,5 @@
-/*	$NetBSD: domacro.c,v 1.8 2009/05/20 12:53:47 lukem Exp $	*/
-/*	from	NetBSD: domacro.c,v 1.22 2009/04/12 10:18:52 lukem Exp	*/
+/*	$NetBSD: domacro.c,v 1.9 2020/07/04 09:59:07 lukem Exp $	*/
+/*	from	NetBSD: domacro.c,v 1.23 2019/02/03 03:19:29 mrg Exp	*/
 
 /*
  * Copyright (c) 1985, 1993, 1994
@@ -39,7 +39,7 @@
 #if 0
 static char sccsid[] = "@(#)domacro.c	8.3 (Berkeley) 4/2/94";
 #else
-__RCSID(" NetBSD: domacro.c,v 1.22 2009/04/12 10:18:52 lukem Exp  ");
+__RCSID(" NetBSD: domacro.c,v 1.23 2019/02/03 03:19:29 mrg Exp  ");
 #endif
 #endif /* not lint */
 
@@ -109,7 +109,7 @@ domacro(int argc, char *argv[])
 					}
 					break;
 				}
-				/* intentional drop through */
+				/* FALLTHROUGH */
 			default:
 				*cp2++ = *cp1;
 				break;

Index: othersrc/usr.bin/tnftp/src/extern.h
diff -u othersrc/usr.bin/tnftp/src/extern.h:1.13 othersrc/usr.bin/tnftp/src/extern.h:1.14
--- othersrc/usr.bin/tnftp/src/extern.h:1.13	Sun May  5 11:17:30 2013
+++ othersrc/usr.bin/tnftp/src/extern.h	Sat Jul  4 09:59:07 2020
@@ -1,5 +1,5 @@
-/*	$NetBSD: extern.h,v 1.13 2013/05/05 11:17:30 lukem Exp $	*/
-/*	from	NetBSD: extern.h,v 1.80 2012/07/04 06:09:37 is Exp	*/
+/*	$NetBSD: extern.h,v 1.14 2020/07/04 09:59:07 lukem Exp $	*/
+/*	from	NetBSD: extern.h,v 1.82 2019/06/22 23:40:53 christos Exp	*/
 
 /*-
  * Copyright (c) 1996-2009 The NetBSD Foundation, Inc.
@@ -174,6 +174,7 @@ void	pswitch(int);
 void	put(int, char **);
 void	pwd(int, char **);
 void	quit(int, char **);
+void	justquit(void) __dead;
 void	quote(int, char **);
 void	quote1(const char *, int, char **);
 void	recvrequest(const char *, const char *, const char *,
@@ -243,7 +244,14 @@ void	user(int, char **);
 int	ftp_connect(int, const struct sockaddr *, socklen_t, int);
 int	ftp_listen(int, int);
 int	ftp_poll(struct pollfd *, int, int);
+#ifndef SMALL
 void   *ftp_malloc(size_t);
 StringList *ftp_sl_init(void);
 void	ftp_sl_add(StringList *, char *);
 char   *ftp_strdup(const char *);
+#else
+#define	ftp_malloc(a)	malloc(a);
+#define ftp_sl_init()	sl_init()
+#define ftp_sl_add(a, b)	sl_add((a), (b))
+#define ftp_strdup(a)	strdup(a)
+#endif

Index: othersrc/usr.bin/tnftp/src/fetch.c
diff -u othersrc/usr.bin/tnftp/src/fetch.c:1.23 othersrc/usr.bin/tnftp/src/fetch.c:1.24
--- othersrc/usr.bin/tnftp/src/fetch.c:1.23	Sun Dec 13 14:08:09 2015
+++ othersrc/usr.bin/tnftp/src/fetch.c	Sat Jul  4 09:59:07 2020
@@ -1,5 +1,5 @@
-/*	$NetBSD: fetch.c,v 1.23 2015/12/13 14:08:09 tron Exp $	*/
-/*	from	NetBSD: fetch.c,v 1.207 2015/09/12 19:38:42 wiz Exp	*/
+/*	$NetBSD: fetch.c,v 1.24 2020/07/04 09:59:07 lukem Exp $	*/
+/*	from	NetBSD: fetch.c,v 1.231 2019/04/04 00:36:09 christos Exp	*/
 
 /*-
  * Copyright (c) 1997-2015 The NetBSD Foundation, Inc.
@@ -42,7 +42,7 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID(" NetBSD: fetch.c,v 1.207 2015/09/12 19:38:42 wiz Exp  ");
+__RCSID(" NetBSD: fetch.c,v 1.231 2019/04/04 00:36:09 christos Exp  ");
 #endif /* not lint */
 
 /*
@@ -81,27 +81,47 @@ __RCSID(" NetBSD: fetch.c,v 1.207 2015/0
 typedef enum {
 	UNKNOWN_URL_T=-1,
 	HTTP_URL_T,
-#ifdef WITH_SSL
 	HTTPS_URL_T,
-#endif
 	FTP_URL_T,
 	FILE_URL_T,
 	CLASSIC_URL_T
 } url_t;
 
+struct authinfo {
+	char *auth;
+	char *user;
+	char *pass;
+};
+
+struct urlinfo {
+	char *host;
+	char *port;
+	char *path;
+	url_t utype;
+	in_port_t portnum;
+};
+
+struct posinfo {
+	off_t rangestart;
+	off_t rangeend;
+	off_t entitylen;
+};
+
 __dead static void	aborthttp(int);
 __dead static void	timeouthttp(int);
 #ifndef NO_AUTH
-static int	auth_url(const char *, char **, const char *, const char *);
+static int	auth_url(const char *, char **, const struct authinfo *);
 static void	base64_encode(const unsigned char *, size_t, unsigned char *);
 #endif
 static int	go_fetch(const char *);
 static int	fetch_ftp(const char *);
 static int	fetch_url(const char *, const char *, char *, char *);
 static const char *match_token(const char **, const char *);
-static int	parse_url(const char *, const char *, url_t *, char **,
-			    char **, char **, char **, in_port_t *, char **);
+static int	parse_url(const char *, const char *, struct urlinfo *,
+    struct authinfo *);
 static void	url_decode(char *);
+static void	freeauthinfo(struct authinfo *);
+static void	freeurlinfo(struct urlinfo *);
 
 static int	redirect_loop;
 
@@ -153,6 +173,54 @@ match_token(const char **buf, const char
 	return orig;
 }
 
+static void
+initposinfo(struct posinfo *pi)
+{
+	pi->rangestart = pi->rangeend = pi->entitylen = -1;
+}
+
+static void
+initauthinfo(struct authinfo *ai, char *auth)
+{
+	ai->auth = auth;
+	ai->user = ai->pass = 0;
+}
+
+static void
+freeauthinfo(struct authinfo *a)
+{
+	FREEPTR(a->user);
+	if (a->pass != NULL)
+		memset(a->pass, 0, strlen(a->pass));
+	FREEPTR(a->pass);
+}
+
+static void
+initurlinfo(struct urlinfo *ui)
+{
+	ui->host = ui->port = ui->path = 0;
+	ui->utype = UNKNOWN_URL_T;
+	ui->portnum = 0;
+}
+
+static void
+copyurlinfo(struct urlinfo *dui, struct urlinfo *sui)
+{
+	dui->host = ftp_strdup(sui->host);
+	dui->port = ftp_strdup(sui->port);
+	dui->path = ftp_strdup(sui->path);
+	dui->utype = sui->utype;
+	dui->portnum = sui->portnum;
+}
+
+static void
+freeurlinfo(struct urlinfo *ui)
+{
+	FREEPTR(ui->host);
+	FREEPTR(ui->port);
+	FREEPTR(ui->path);
+}
+
 #ifndef NO_AUTH
 /*
  * Generate authorization response based on given authentication challenge.
@@ -160,8 +228,7 @@ match_token(const char **buf, const char
  * Sets response to a malloc(3)ed string; caller should free.
  */
 static int
-auth_url(const char *challenge, char **response, const char *guser,
-	const char *gpass)
+auth_url(const char *challenge, char **response, const struct authinfo *auth)
 {
 	const char	*cp, *scheme, *errormsg;
 	char		*ep, *clear, *realm;
@@ -205,8 +272,8 @@ auth_url(const char *challenge, char **r
 	}
 
 	fprintf(ttyout, "Username for `%s': ", realm);
-	if (guser != NULL) {
-		(void)strlcpy(uuser, guser, sizeof(uuser));
+	if (auth->user != NULL) {
+		(void)strlcpy(uuser, auth->user, sizeof(uuser));
 		fprintf(ttyout, "%s\n", uuser);
 	} else {
 		(void)fflush(ttyout);
@@ -215,8 +282,8 @@ auth_url(const char *challenge, char **r
 			goto cleanup_auth_url;
 		}
 	}
-	if (gpass != NULL)
-		upass = gpass;
+	if (auth->pass != NULL)
+		upass = auth->pass;
 	else {
 		gotpass = getpass("Password: ");
 		if (gotpass == NULL) {
@@ -236,7 +303,7 @@ auth_url(const char *challenge, char **r
 
 						/* scheme + " " + enc + "\0" */
 	rlen = strlen(scheme) + 1 + (clen + 2) * 4 / 3 + 1;
-	*response = (char *)ftp_malloc(rlen);
+	*response = ftp_malloc(rlen);
 	(void)strlcpy(*response, scheme, rlen);
 	len = strlcat(*response, " ", rlen);
 			/* use  `clen - 1'  to not encode the trailing NUL */
@@ -335,57 +402,48 @@ url_decode(char *url)
  *	"ftp://host/dir/file";		"dir/file"
  *	"ftp://host//dir/file";		"/dir/file"
  */
+
 static int
-parse_url(const char *url, const char *desc, url_t *utype,
-		char **uuser, char **pass, char **host, char **port,
-		in_port_t *portnum, char **path)
+parse_url(const char *url, const char *desc, struct urlinfo *ui,
+    struct authinfo *auth) 
 {
 	const char	*origurl, *tport;
 	char		*cp, *ep, *thost;
 	size_t		 len;
 
-	if (url == NULL || desc == NULL || utype == NULL || uuser == NULL
-	    || pass == NULL || host == NULL || port == NULL || portnum == NULL
-	    || path == NULL)
+	if (url == NULL || desc == NULL || ui == NULL || auth == NULL)
 		errx(1, "parse_url: invoked with NULL argument!");
 	DPRINTF("parse_url: %s `%s'\n", desc, url);
 
 	origurl = url;
-	*utype = UNKNOWN_URL_T;
-	*uuser = *pass = *host = *port = *path = NULL;
-	*portnum = 0;
 	tport = NULL;
 
 	if (STRNEQUAL(url, HTTP_URL)) {
 		url += sizeof(HTTP_URL) - 1;
-		*utype = HTTP_URL_T;
-		*portnum = HTTP_PORT;
+		ui->utype = HTTP_URL_T;
+		ui->portnum = HTTP_PORT;
 		tport = httpport;
 	} else if (STRNEQUAL(url, FTP_URL)) {
 		url += sizeof(FTP_URL) - 1;
-		*utype = FTP_URL_T;
-		*portnum = FTP_PORT;
+		ui->utype = FTP_URL_T;
+		ui->portnum = FTP_PORT;
 		tport = ftpport;
 	} else if (STRNEQUAL(url, FILE_URL)) {
 		url += sizeof(FILE_URL) - 1;
-		*utype = FILE_URL_T;
+		ui->utype = FILE_URL_T;
+		tport = "";
 #ifdef WITH_SSL
 	} else if (STRNEQUAL(url, HTTPS_URL)) {
 		url += sizeof(HTTPS_URL) - 1;
-		*utype = HTTPS_URL_T;
-		*portnum = HTTPS_PORT;
+		ui->utype = HTTPS_URL_T;
+		ui->portnum = HTTPS_PORT;
 		tport = httpsport;
 #endif
 	} else {
 		warnx("Invalid %s `%s'", desc, url);
  cleanup_parse_url:
-		FREEPTR(*uuser);
-		if (*pass != NULL)
-			memset(*pass, 0, strlen(*pass));
-		FREEPTR(*pass);
-		FREEPTR(*host);
-		FREEPTR(*port);
-		FREEPTR(*path);
+		freeauthinfo(auth);
+		freeurlinfo(ui);
 		return (-1);
 	}
 
@@ -400,26 +458,26 @@ parse_url(const char *url, const char *d
 		len = ep - url;
 		thost = (char *)ftp_malloc(len + 1);
 		(void)strlcpy(thost, url, len + 1);
-		if (*utype == FTP_URL_T)	/* skip first / for ftp URLs */
+		if (ui->utype == FTP_URL_T)	/* skip first / for ftp URLs */
 			ep++;
-		*path = ftp_strdup(ep);
+		ui->path = ftp_strdup(ep);
 	}
 
 	cp = strchr(thost, '@');	/* look for user[:pass]@ in URLs */
 	if (cp != NULL) {
-		if (*utype == FTP_URL_T)
+		if (ui->utype == FTP_URL_T)
 			anonftp = 0;	/* disable anonftp */
-		*uuser = thost;
+		auth->user = thost;
 		*cp = '\0';
 		thost = ftp_strdup(cp + 1);
-		cp = strchr(*uuser, ':');
+		cp = strchr(auth->user, ':');
 		if (cp != NULL) {
 			*cp = '\0';
-			*pass = ftp_strdup(cp + 1);
+			auth->pass = ftp_strdup(cp + 1);
 		}
-		url_decode(*uuser);
-		if (*pass)
-			url_decode(*pass);
+		url_decode(auth->user);
+		if (auth->pass)
+			url_decode(auth->pass);
 	}
 
 #ifdef INET6
@@ -453,7 +511,7 @@ parse_url(const char *url, const char *d
 #endif /* INET6 */
 		if ((cp = strchr(thost, ':')) != NULL)
 			*cp++ = '\0';
-	*host = thost;
+	ui->host = thost;
 
 			/* look for [:port] */
 	if (cp != NULL) {
@@ -466,30 +524,742 @@ parse_url(const char *url, const char *d
 			    cp, desc, origurl);
 			goto cleanup_parse_url;
 		}
-		*portnum = nport;
+		ui->portnum = nport;
 		tport = cp;
 	}
 
 	if (tport != NULL)
-		*port = ftp_strdup(tport);
-	if (*path == NULL) {
+		ui->port = ftp_strdup(tport);
+	if (ui->path == NULL) {
 		const char *emptypath = "/";
-		if (*utype == FTP_URL_T)	/* skip first / for ftp URLs */
+		if (ui->utype == FTP_URL_T)	/* skip first / for ftp URLs */
 			emptypath++;
-		*path = ftp_strdup(emptypath);
+		ui->path = ftp_strdup(emptypath);
 	}
 
 	DPRINTF("parse_url: user `%s' pass `%s' host %s port %s(%d) "
 	    "path `%s'\n",
-	    STRorNULL(*uuser), STRorNULL(*pass),
-	    STRorNULL(*host), STRorNULL(*port),
-	    *portnum ? *portnum : -1, STRorNULL(*path));
+	    STRorNULL(auth->user), STRorNULL(auth->pass),
+	    STRorNULL(ui->host), STRorNULL(ui->port),
+	    ui->portnum ? ui->portnum : -1, STRorNULL(ui->path));
 
 	return (0);
 }
 
 sigjmp_buf	httpabort;
 
+static int
+ftp_socket(const struct urlinfo *ui, void **ssl)
+{
+	struct addrinfo	hints, *res, *res0 = NULL;
+	int error;
+	int s;
+	const char *host = ui->host;
+	const char *port = ui->port;
+
+	if (ui->utype != HTTPS_URL_T)
+		ssl = NULL;
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_flags = 0;
+	hints.ai_family = family;
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_protocol = 0;
+
+	error = getaddrinfo(host, port, &hints, &res0);
+	if (error) {
+		warnx("Can't LOOKUP `%s:%s': %s", host, port,
+		    (error == EAI_SYSTEM) ? strerror(errno)
+					  : gai_strerror(error));
+		return -1;
+	}
+
+	if (res0->ai_canonname)
+		host = res0->ai_canonname;
+
+	s = -1;
+	if (ssl)
+		*ssl = NULL;
+	for (res = res0; res; res = res->ai_next) {
+		char	hname[NI_MAXHOST], sname[NI_MAXSERV];
+
+		ai_unmapped(res);
+		if (getnameinfo(res->ai_addr, res->ai_addrlen,
+		    hname, sizeof(hname), sname, sizeof(sname),
+		    NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
+			strlcpy(hname, "?", sizeof(hname));
+			strlcpy(sname, "?", sizeof(sname));
+		}
+
+		if (verbose && res0->ai_next) {
+#ifdef INET6
+			if(res->ai_family == AF_INET6) {
+				fprintf(ttyout, "Trying [%s]:%s ...\n",
+				    hname, sname);
+			} else {
+#endif
+				fprintf(ttyout, "Trying %s:%s ...\n",
+				    hname, sname);
+#ifdef INET6
+			}
+#endif
+		}
+
+		s = socket(res->ai_family, SOCK_STREAM, res->ai_protocol);
+		if (s < 0) {
+			warn(
+			    "Can't create socket for connection to "
+			    "`%s:%s'", hname, sname);
+			continue;
+		}
+
+		if (ftp_connect(s, res->ai_addr, res->ai_addrlen,
+		    verbose || !res->ai_next) < 0) {
+			close(s);
+			s = -1;
+			continue;
+		}
+
+#ifdef WITH_SSL
+		if (ssl) {
+			if ((*ssl = fetch_start_ssl(s, host)) == NULL) {
+				close(s);
+				s = -1;
+				continue;
+			}
+		}
+#endif
+		break;
+	}
+	if (res0)
+		freeaddrinfo(res0);
+	return s;
+}
+
+static int
+handle_noproxy(const char *host, in_port_t portnum)
+{
+
+	char *cp, *ep, *np, *np_copy, *np_iter, *no_proxy;
+	unsigned long np_port;
+	size_t hlen, plen;
+	int isproxy = 1;
+
+	/* check URL against list of no_proxied sites */
+	no_proxy = getoptionvalue("no_proxy");
+	if (EMPTYSTRING(no_proxy))
+		return isproxy;
+
+	np_iter = np_copy = ftp_strdup(no_proxy);
+	hlen = strlen(host);
+	while ((cp = strsep(&np_iter, " ,")) != NULL) {
+		if (*cp == '\0')
+			continue;
+		if ((np = strrchr(cp, ':')) != NULL) {
+			*np++ =  '\0';
+			np_port = strtoul(np, &ep, 10);
+			if (*np == '\0' || *ep != '\0')
+				continue;
+			if (np_port != portnum)
+				continue;
+		}
+		plen = strlen(cp);
+		if (hlen < plen)
+			continue;
+		if (strncasecmp(host + hlen - plen, cp, plen) == 0) {
+			isproxy = 0;
+			break;
+		}
+	}
+	FREEPTR(np_copy);
+	return isproxy;
+}
+
+static int
+handle_proxy(const char *url, const char *penv, struct urlinfo *ui,
+    struct authinfo *pauth)
+{
+	struct urlinfo pui;
+
+	if (isipv6addr(ui->host) && strchr(ui->host, '%') != NULL) {
+		warnx("Scoped address notation `%s' disallowed via web proxy",
+		    ui->host);
+		return -1;
+	}
+
+	initurlinfo(&pui);
+	if (parse_url(penv, "proxy URL", &pui, pauth) == -1)
+		return -1;
+
+	if ((!IS_HTTP_TYPE(pui.utype) && pui.utype != FTP_URL_T) ||
+	    EMPTYSTRING(pui.host) ||
+	    (! EMPTYSTRING(pui.path) && strcmp(pui.path, "/") != 0)) {
+		warnx("Malformed proxy URL `%s'", penv);
+		freeurlinfo(&pui);
+		return -1;
+	}
+
+	FREEPTR(pui.path);
+	pui.path = ftp_strdup(url);
+
+	freeurlinfo(ui);
+	*ui = pui;
+
+	return 0;
+}
+
+static void
+print_host(FETCH *fin, const struct urlinfo *ui)
+{
+	char *h, *p;
+
+	if (strchr(ui->host, ':') == NULL) {
+		fetch_printf(fin, "Host: %s", ui->host);
+	} else {
+		/*
+		 * strip off IPv6 scope identifier, since it is
+		 * local to the node
+		 */
+		h = ftp_strdup(ui->host);
+		if (isipv6addr(h) && (p = strchr(h, '%')) != NULL)
+			*p = '\0';
+
+		fetch_printf(fin, "Host: [%s]", h);
+		free(h);
+	}
+
+	if ((ui->utype == HTTP_URL_T && ui->portnum != HTTP_PORT) ||
+	    (ui->utype == HTTPS_URL_T && ui->portnum != HTTPS_PORT))
+		fetch_printf(fin, ":%u", ui->portnum);
+	fetch_printf(fin, "\r\n");
+}
+
+static void
+print_agent(FETCH *fin)
+{
+	const char *useragent;
+	if ((useragent = getenv("FTPUSERAGENT")) != NULL) {
+		fetch_printf(fin, "User-Agent: %s\r\n", useragent);
+	} else {
+		fetch_printf(fin, "User-Agent: %s/%s\r\n",
+		    FTP_PRODUCT, FTP_VERSION);
+	}
+}
+
+static void
+print_cache(FETCH *fin, int isproxy)
+{
+	fetch_printf(fin, isproxy ?
+	    "Pragma: no-cache\r\n" :
+	    "Cache-Control: no-cache\r\n");
+}
+
+static int
+print_get(FETCH *fin, int hasleading, int isproxy, const struct urlinfo *oui,
+    const struct urlinfo *ui)
+{
+	const char *leading = hasleading ? ", " : "  (";
+
+	if (isproxy) {
+		if (verbose) {
+			fprintf(ttyout, "%svia %s:%u", leading,
+			    ui->host, ui->portnum);
+			leading = ", ";
+			hasleading++;
+		}
+		fetch_printf(fin, "GET %s HTTP/1.0\r\n", ui->path);
+		print_host(fin, oui);
+		return hasleading;
+	}
+
+	fetch_printf(fin, "GET %s HTTP/1.1\r\n", ui->path);
+	print_host(fin, ui);
+	fetch_printf(fin, "Accept: */*\r\n");
+	fetch_printf(fin, "Connection: close\r\n");
+	if (restart_point) {
+		fputs(leading, ttyout);
+		fetch_printf(fin, "Range: bytes=" LLF "-\r\n",
+		    (LLT)restart_point);
+		fprintf(ttyout, "restarting at " LLF, (LLT)restart_point);
+		hasleading++;
+	}
+	return hasleading;
+}
+
+static void
+getmtime(const char *cp, time_t *mtime)
+{
+	struct tm parsed;
+	const char *t;
+
+	memset(&parsed, 0, sizeof(parsed));
+	t = parse_rfc2616time(&parsed, cp);
+
+	if (t == NULL)
+		return;
+
+	parsed.tm_isdst = -1;
+	if (*t == '\0')
+		*mtime = timegm(&parsed);
+
+#ifndef NO_DEBUG
+	if (ftp_debug && *mtime != -1) {
+		fprintf(ttyout, "parsed time as: %s",
+		    rfc2822time(localtime(mtime)));
+	}
+#endif
+}
+
+static int
+print_proxy(FETCH *fin, int hasleading, const char *wwwauth,
+    const char *proxyauth)
+{
+	const char *leading = hasleading ? ", " : "  (";
+
+	if (wwwauth) {
+		if (verbose) {
+			fprintf(ttyout, "%swith authorization", leading);
+			hasleading++;
+		}
+		fetch_printf(fin, "Authorization: %s\r\n", wwwauth);
+	}
+	if (proxyauth) {
+		if (verbose) {
+			fprintf(ttyout, "%swith proxy authorization", leading);
+			hasleading++;
+		}
+		fetch_printf(fin, "Proxy-Authorization: %s\r\n", proxyauth);
+	}
+	return hasleading;
+}
+
+#ifdef WITH_SSL
+static void
+print_connect(FETCH *fin, const struct urlinfo *ui)
+{
+	char hname[NI_MAXHOST], *p;
+	const char *h;
+
+	if (isipv6addr(ui->host)) {
+		/*
+		 * strip off IPv6 scope identifier,
+		 * since it is local to the node
+		 */
+		if ((p = strchr(ui->host, '%')) == NULL)
+			snprintf(hname, sizeof(hname), "[%s]", ui->host);
+		else
+			snprintf(hname, sizeof(hname), "[%.*s]",
+			    (int)(p - ui->host), ui->host);
+		h = hname;
+	} else
+		h = ui->host;
+
+	fetch_printf(fin, "CONNECT %s:%d HTTP/1.1\r\n", h, ui->portnum);
+	fetch_printf(fin, "Host: %s:%d\r\n", h, ui->portnum);
+}
+#endif
+
+#define C_OK 0
+#define C_CLEANUP 1
+#define C_IMPROPER 2
+
+static int
+getresponseline(FETCH *fin, char *buf, size_t buflen, int *len)
+{
+	const char *errormsg;
+
+	alarmtimer(quit_time ? quit_time : 60);
+	*len = fetch_getline(fin, buf, buflen, &errormsg);
+	alarmtimer(0);
+	if (*len < 0) {
+		if (*errormsg == '\n')
+			errormsg++;
+		warnx("Receiving HTTP reply: %s", errormsg);
+		return C_CLEANUP;
+	}
+	while (*len > 0 && (ISLWS(buf[*len-1])))
+		buf[--*len] = '\0';
+
+	if (*len)
+		DPRINTF("%s: received `%s'\n", __func__, buf);
+	return C_OK;
+}
+
+static int
+getresponse(FETCH *fin, char **cp, size_t buflen, int *hcode)
+{
+	int len, rv;
+	char *ep, *buf = *cp;
+
+	*hcode = 0;
+	if ((rv = getresponseline(fin, buf, buflen, &len)) != C_OK)
+		return rv;
+
+	/* Determine HTTP response code */
+	*cp = strchr(buf, ' ');
+	if (*cp == NULL)
+		return C_IMPROPER;
+
+	(*cp)++;
+
+	*hcode = strtol(*cp, &ep, 10);
+	if (*ep != '\0' && !isspace((unsigned char)*ep))
+		return C_IMPROPER;
+
+	return C_OK;
+}
+
+static int
+parse_posinfo(const char **cp, struct posinfo *pi)
+{
+	char *ep;
+	if (!match_token(cp, "bytes"))
+		return -1;
+
+	if (**cp == '*')
+		(*cp)++;
+	else {
+		pi->rangestart = STRTOLL(*cp, &ep, 10);
+		if (pi->rangestart < 0 || *ep != '-')
+			return -1;
+		*cp = ep + 1;
+		pi->rangeend = STRTOLL(*cp, &ep, 10);
+		if (pi->rangeend < 0 || pi->rangeend < pi->rangestart)
+			return -1;
+		*cp = ep;
+	}
+	if (**cp != '/')
+		return -1;
+	(*cp)++;
+	if (**cp == '*')
+		(*cp)++;
+	else {
+		pi->entitylen = STRTOLL(*cp, &ep, 10);
+		if (pi->entitylen < 0)
+			return -1;
+		*cp = ep;
+	}
+	if (**cp != '\0')
+		return -1;
+
+#ifndef NO_DEBUG
+	if (ftp_debug) {
+		fprintf(ttyout, "parsed range as: ");
+		if (pi->rangestart == -1)
+			fprintf(ttyout, "*");
+		else
+			fprintf(ttyout, LLF "-" LLF, (LLT)pi->rangestart,
+			    (LLT)pi->rangeend);
+		fprintf(ttyout, "/" LLF "\n", (LLT)pi->entitylen);
+	}
+#endif
+	return 0;
+}
+
+#ifndef NO_AUTH
+static void
+do_auth(int hcode, const char *url, const char *penv, struct authinfo *wauth,
+    struct authinfo *pauth, char **auth, const char *message,
+    volatile int *rval)
+{
+	struct authinfo aauth;
+	char *response;
+
+	if (hcode == 401)
+		aauth = *wauth;
+	else
+		aauth = *pauth;
+
+	if (verbose || aauth.auth == NULL ||
+	    aauth.user == NULL || aauth.pass == NULL)
+		fprintf(ttyout, "%s\n", message);
+	if (EMPTYSTRING(*auth)) {
+		warnx("No authentication challenge provided by server");
+		return;
+	}
+
+	if (aauth.auth != NULL) {
+		char reply[10];
+
+		fprintf(ttyout, "Authorization failed. Retry (y/n)? ");
+		if (get_line(stdin, reply, sizeof(reply), NULL) < 0) {
+			return;
+		}
+		if (tolower((unsigned char)reply[0]) != 'y')
+			return;
+
+		aauth.user = NULL;
+		aauth.pass = NULL;
+	}
+
+	if (auth_url(*auth, &response, &aauth) == 0) {
+		*rval = fetch_url(url, penv,
+		    hcode == 401 ? pauth->auth : response,
+		    hcode == 401 ? response: wauth->auth);
+		memset(response, 0, strlen(response));
+		FREEPTR(response);
+	}
+}
+#endif
+
+static int
+negotiate_connection(FETCH *fin, const char *url, const char *penv,
+    struct posinfo *pi, time_t *mtime, struct authinfo *wauth,
+    struct authinfo *pauth, volatile int *rval, volatile int *ischunked,
+    char **auth)
+{
+	int			len, hcode, rv;
+	char			buf[FTPBUFLEN], *ep;
+	const char		*cp, *token;
+	char 			*location, *message;
+
+	*auth = message = location = NULL;
+
+	/* Read the response */
+	ep = buf;
+	switch (getresponse(fin, &ep, sizeof(buf), &hcode)) {
+	case C_CLEANUP:
+		goto cleanup_fetch_url;
+	case C_IMPROPER:
+		goto improper;
+	case C_OK:
+		message = ftp_strdup(ep);
+		break;
+	}
+
+	/* Read the rest of the header. */
+
+	for (;;) {
+		if ((rv = getresponseline(fin, buf, sizeof(buf), &len)) != C_OK)
+			goto cleanup_fetch_url;
+		if (len == 0)
+			break;
+
+	/*
+	 * Look for some headers
+	 */
+
+		cp = buf;
+
+		if (match_token(&cp, "Content-Length:")) {
+			filesize = STRTOLL(cp, &ep, 10);
+			if (filesize < 0 || *ep != '\0')
+				goto improper;
+			DPRINTF("%s: parsed len as: " LLF "\n",
+			    __func__, (LLT)filesize);
+
+		} else if (match_token(&cp, "Content-Range:")) {
+			if (parse_posinfo(&cp, pi) == -1)
+				goto improper;
+			if (! restart_point) {
+				warnx(
+			    "Received unexpected Content-Range header");
+				goto cleanup_fetch_url;
+			}
+
+		} else if (match_token(&cp, "Last-Modified:")) {
+			getmtime(cp, mtime);
+
+		} else if (match_token(&cp, "Location:")) {
+			location = ftp_strdup(cp);
+			DPRINTF("%s: parsed location as `%s'\n",
+			    __func__, cp);
+
+		} else if (match_token(&cp, "Transfer-Encoding:")) {
+			if (match_token(&cp, "binary")) {
+				warnx(
+		"Bogus transfer encoding `binary' (fetching anyway)");
+				continue;
+			}
+			if (! (token = match_token(&cp, "chunked"))) {
+				warnx(
+			    "Unsupported transfer encoding `%s'",
+				    token);
+				goto cleanup_fetch_url;
+			}
+			(*ischunked)++;
+			DPRINTF("%s: using chunked encoding\n",
+			    __func__);
+
+		} else if (match_token(&cp, "Proxy-Authenticate:")
+			|| match_token(&cp, "WWW-Authenticate:")) {
+			if (! (token = match_token(&cp, "Basic"))) {
+				DPRINTF("%s: skipping unknown auth "
+				    "scheme `%s'\n", __func__, token);
+				continue;
+			}
+			FREEPTR(*auth);
+			*auth = ftp_strdup(token);
+			DPRINTF("%s: parsed auth as `%s'\n",
+			    __func__, cp);
+		}
+
+	}
+			/* finished parsing header */
+
+	switch (hcode) {
+	case 200:
+		break;
+	case 206:
+		if (! restart_point) {
+			warnx("Not expecting partial content header");
+			goto cleanup_fetch_url;
+		}
+		break;
+	case 300:
+	case 301:
+	case 302:
+	case 303:
+	case 305:
+	case 307:
+		if (EMPTYSTRING(location)) {
+			warnx(
+			"No redirection Location provided by server");
+			goto cleanup_fetch_url;
+		}
+		if (redirect_loop++ > 5) {
+			warnx("Too many redirections requested");
+			goto cleanup_fetch_url;
+		}
+		if (hcode == 305) {
+			if (verbose)
+				fprintf(ttyout, "Redirected via %s\n",
+				    location);
+			*rval = fetch_url(url, location,
+			    pauth->auth, wauth->auth);
+		} else {
+			if (verbose)
+				fprintf(ttyout, "Redirected to %s\n",
+				    location);
+			*rval = go_fetch(location);
+		}
+		goto cleanup_fetch_url;
+#ifndef NO_AUTH
+	case 401:
+	case 407:
+		do_auth(hcode, url, penv, wauth, pauth, auth, message, rval);
+		goto cleanup_fetch_url;
+#endif
+	default:
+		if (message)
+			warnx("Error retrieving file `%s'", message);
+		else
+			warnx("Unknown error retrieving file");
+		goto cleanup_fetch_url;
+	}
+	rv = C_OK;
+	goto out;
+
+cleanup_fetch_url:
+	rv = C_CLEANUP;
+	goto out;
+improper:
+	rv = C_IMPROPER;
+	goto out;
+out:
+	FREEPTR(message);
+	FREEPTR(location);
+	return rv;
+}		/* end of ftp:// or http:// specific setup */
+
+#ifdef WITH_SSL
+static int
+connectmethod(FETCH *fin, const char *url, const char *penv,
+    struct urlinfo *oui, struct urlinfo *ui, struct authinfo *wauth,
+    struct authinfo *pauth, char **auth, int *hasleading, volatile int *rval)
+{
+	void *ssl;
+	int hcode, rv;
+	const char *cp;
+	char buf[FTPBUFLEN], *ep;
+	char *message = NULL;
+
+	print_connect(fin, oui);
+
+	print_agent(fin);
+	*hasleading = print_proxy(fin, *hasleading, NULL, pauth->auth);
+
+	if (verbose && *hasleading)
+		fputs(")\n", ttyout);
+	*hasleading = 0;
+
+	fetch_printf(fin, "\r\n");
+	if (fetch_flush(fin) == EOF) {
+		warn("Writing HTTP request");
+		alarmtimer(0);
+		goto cleanup_fetch_url;
+	}
+	alarmtimer(0);
+
+	/* Read the response */
+	ep = buf;
+	switch (getresponse(fin, &ep, sizeof(buf), &hcode)) {
+	case C_CLEANUP:
+		goto cleanup_fetch_url;
+	case C_IMPROPER:
+		goto improper;
+	case C_OK:
+		message = ftp_strdup(ep);
+		break;
+	}
+		
+	for (;;) {
+		int len;
+		if (getresponseline(fin, buf, sizeof(buf), &len) != C_OK)
+			goto cleanup_fetch_url;
+		if (len == 0)
+			break;
+
+		cp = buf;
+		if (match_token(&cp, "Proxy-Authenticate:")) {
+			const char *token;
+			if (!(token = match_token(&cp, "Basic"))) {
+				DPRINTF(
+				    "%s: skipping unknown auth scheme `%s'\n",
+				    __func__, token);
+				continue;
+			}
+			FREEPTR(*auth);
+			*auth = ftp_strdup(token);
+			DPRINTF("%s: parsed auth as " "`%s'\n", __func__, cp);
+		}
+	}
+
+	/* finished parsing header */
+	switch (hcode) {
+	case 200:
+		break;
+#ifndef NO_AUTH
+	case 407:
+		do_auth(hcode, url, penv, wauth, pauth, auth, message, rval);
+		goto cleanup_fetch_url;
+#endif
+	default:
+		if (message)
+			warnx("Error proxy connect " "`%s'", message);
+		else
+			warnx("Unknown error proxy " "connect");
+		goto cleanup_fetch_url;
+	}
+
+	if ((ssl = fetch_start_ssl(fetch_fileno(fin), oui->host)) == NULL)
+		goto cleanup_fetch_url;
+	fetch_set_ssl(fin, ssl);
+
+	rv = C_OK;
+	goto out;
+improper:
+	rv = C_IMPROPER;
+	goto out;
+cleanup_fetch_url:
+	rv = C_CLEANUP;
+	goto out;
+out:
+	FREEPTR(message);
+	return rv;
+}
+#endif
+
 /*
  * Retrieve URL, via a proxy if necessary, using HTTP.
  * If proxyenv is set, use that for the proxy, otherwise try ftp_proxy or
@@ -501,44 +1271,34 @@ sigjmp_buf	httpabort;
 static int
 fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth)
 {
-	struct addrinfo		hints, *res, *res0 = NULL;
-	int			error;
 	sigfunc volatile	oldint;
 	sigfunc volatile	oldpipe;
 	sigfunc volatile	oldalrm;
 	sigfunc volatile	oldquit;
 	int volatile		s;
 	struct stat		sb;
-	int volatile		ischunked;
 	int volatile		isproxy;
-	int volatile		rval;
-	int volatile		hcode;
-	int			len;
+	int volatile 		rval, ischunked;
 	size_t			flen;
 	static size_t		bufsize;
 	static char		*xferbuf;
-	const char		*cp, *token;
+	const char		*cp;
 	char			*ep;
-	char			buf[FTPBUFLEN];
-	const char		*errormsg;
-	char			*volatile savefile;
 	char			*volatile auth;
+	char			*volatile savefile;
 	char			*volatile location;
 	char			*volatile message;
-	char			*uuser, *pass, *host, *port, *path;
 	char			*volatile decodedpath;
-	char			*puser, *ppass, *useragent;
-	off_t			hashbytes, rangestart, rangeend, entitylen;
+	struct authinfo 	wauth, pauth;
+	struct posinfo		pi;
+	off_t			hashbytes;
 	int			(*volatile closefunc)(FILE *);
 	FETCH			*volatile fin;
 	FILE			*volatile fout;
 	const char		*volatile penv = proxyenv;
+	struct urlinfo		ui, oui;
 	time_t			mtime;
-	url_t			urltype;
-	in_port_t		portnum;
-#ifdef WITH_SSL
-	void			*ssl;
-#endif
+	void			*ssl = NULL;
 
 	DPRINTF("%s: `%s' proxyenv `%s'\n", __func__, url, STRorNULL(penv));
 
@@ -549,35 +1309,42 @@ fetch_url(const char *url, const char *p
 	s = -1;
 	savefile = NULL;
 	auth = location = message = NULL;
-	ischunked = isproxy = hcode = 0;
+	ischunked = isproxy = 0;
 	rval = 1;
-	uuser = pass = host = path = decodedpath = puser = ppass = NULL;
+
+	initurlinfo(&ui);
+	initurlinfo(&oui);
+	initauthinfo(&wauth, wwwauth);
+	initauthinfo(&pauth, proxyauth);
+
+	decodedpath = NULL;
 
 	if (sigsetjmp(httpabort, 1))
 		goto cleanup_fetch_url;
 
-	if (parse_url(url, "URL", &urltype, &uuser, &pass, &host, &port,
-	    &portnum, &path) == -1)
+	if (parse_url(url, "URL", &ui, &wauth) == -1)
 		goto cleanup_fetch_url;
 
-	if (urltype == FILE_URL_T && ! EMPTYSTRING(host)
-	    && strcasecmp(host, "localhost") != 0) {
+	copyurlinfo(&oui, &ui);
+
+	if (ui.utype == FILE_URL_T && ! EMPTYSTRING(ui.host)
+	    && strcasecmp(ui.host, "localhost") != 0) {
 		warnx("No support for non local file URL `%s'", url);
 		goto cleanup_fetch_url;
 	}
 
-	if (EMPTYSTRING(path)) {
-		if (urltype == FTP_URL_T) {
+	if (EMPTYSTRING(ui.path)) {
+		if (ui.utype == FTP_URL_T) {
 			rval = fetch_ftp(url);
 			goto cleanup_fetch_url;
 		}
-		if (!IS_HTTP_TYPE(urltype) || outfile == NULL)  {
+		if (!IS_HTTP_TYPE(ui.utype) || outfile == NULL)  {
 			warnx("Invalid URL (no file after host) `%s'", url);
 			goto cleanup_fetch_url;
 		}
 	}
 
-	decodedpath = ftp_strdup(path);
+	decodedpath = ftp_strdup(ui.path);
 	url_decode(decodedpath);
 
 	if (outfile)
@@ -588,10 +1355,16 @@ fetch_url(const char *url, const char *p
 			savefile = ftp_strdup(cp + 1);
 		else
 			savefile = ftp_strdup(decodedpath);
+		/*
+		 * Use the first URL we requested not the name after a
+		 * possible redirect, but careful to save it because our
+		 * "safety" check is the match to outfile.
+		 */
+		outfile = ftp_strdup(savefile);
 	}
 	DPRINTF("%s: savefile `%s'\n", __func__, savefile);
 	if (EMPTYSTRING(savefile)) {
-		if (urltype == FTP_URL_T) {
+		if (ui.utype == FTP_URL_T) {
 			rval = fetch_ftp(url);
 			goto cleanup_fetch_url;
 		}
@@ -602,13 +1375,13 @@ fetch_url(const char *url, const char *p
 
 	restart_point = 0;
 	filesize = -1;
-	rangestart = rangeend = entitylen = -1;
+	initposinfo(&pi);
 	mtime = -1;
 	if (restartautofetch) {
 		if (stat(savefile, &sb) == 0)
 			restart_point = sb.st_size;
 	}
-	if (urltype == FILE_URL_T) {		/* file:// URLs */
+	if (ui.utype == FILE_URL_T) {		/* file:// URLs */
 		direction = "copied";
 		fin = fetch_open(decodedpath, "r");
 		if (fin == NULL) {
@@ -637,184 +1410,43 @@ fetch_url(const char *url, const char *p
 			rcvbuf_size = 8 * 1024; /* XXX */
 		}
 	} else {				/* ftp:// or http:// URLs */
-		const char *leading;
 		int hasleading;
 
 		if (penv == NULL) {
 #ifdef WITH_SSL
-			if (urltype == HTTPS_URL_T)
+			if (ui.utype == HTTPS_URL_T)
 				penv = getoptionvalue("https_proxy");
 #endif
-			if (penv == NULL && IS_HTTP_TYPE(urltype))
+			if (penv == NULL && IS_HTTP_TYPE(ui.utype))
 				penv = getoptionvalue("http_proxy");
-			else if (urltype == FTP_URL_T)
+			else if (ui.utype == FTP_URL_T)
 				penv = getoptionvalue("ftp_proxy");
 		}
 		direction = "retrieved";
 		if (! EMPTYSTRING(penv)) {			/* use proxy */
-			url_t purltype;
-			char *phost, *ppath;
-			char *pport, *no_proxy;
-			in_port_t pportnum;
-
-			isproxy = 1;
-
-				/* check URL against list of no_proxied sites */
-			no_proxy = getoptionvalue("no_proxy");
-			if (! EMPTYSTRING(no_proxy)) {
-				char *np, *np_copy, *np_iter;
-				unsigned long np_port;
-				size_t hlen, plen;
-
-				np_iter = np_copy = ftp_strdup(no_proxy);
-				hlen = strlen(host);
-				while ((cp = strsep(&np_iter, " ,")) != NULL) {
-					if (*cp == '\0')
-						continue;
-					if ((np = strrchr(cp, ':')) != NULL) {
-						*np++ =  '\0';
-						np_port = strtoul(np, &ep, 10);
-						if (*np == '\0' || *ep != '\0')
-							continue;
-						if (np_port != portnum)
-							continue;
-					}
-					plen = strlen(cp);
-					if (hlen < plen)
-						continue;
-					if (strncasecmp(host + hlen - plen,
-					    cp, plen) == 0) {
-						isproxy = 0;
-						break;
-					}
-				}
-				FREEPTR(np_copy);
-				if (isproxy == 0 && urltype == FTP_URL_T) {
-					rval = fetch_ftp(url);
-					goto cleanup_fetch_url;
-				}
+
+			isproxy = handle_noproxy(ui.host, ui.portnum);
+
+			if (isproxy == 0 && ui.utype == FTP_URL_T) {
+				rval = fetch_ftp(url);
+				goto cleanup_fetch_url;
 			}
 
 			if (isproxy) {
 				if (restart_point) {
-					warnx("Can't restart via proxy URL `%s'",
+					warnx(
+					    "Can't restart via proxy URL `%s'",
 					    penv);
 					goto cleanup_fetch_url;
 				}
-				if (parse_url(penv, "proxy URL", &purltype,
-				    &puser, &ppass, &phost, &pport, &pportnum,
-				    &ppath) == -1)
-					goto cleanup_fetch_url;
-
-				if ((!IS_HTTP_TYPE(purltype)
-				     && purltype != FTP_URL_T) ||
-				    EMPTYSTRING(phost) ||
-				    (! EMPTYSTRING(ppath)
-				     && strcmp(ppath, "/") != 0)) {
-					warnx("Malformed proxy URL `%s'", penv);
-					FREEPTR(phost);
-					FREEPTR(pport);
-					FREEPTR(ppath);
-					goto cleanup_fetch_url;
-				}
-				if (isipv6addr(host) &&
-				    strchr(host, '%') != NULL) {
-					warnx(
-"Scoped address notation `%s' disallowed via web proxy",
-					    host);
-					FREEPTR(phost);
-					FREEPTR(pport);
-					FREEPTR(ppath);
+				if (handle_proxy(url, penv, &ui, &pauth) < 0)
 					goto cleanup_fetch_url;
-				}
-
-				FREEPTR(host);
-				host = phost;
-				FREEPTR(port);
-				port = pport;
-				FREEPTR(path);
-				path = ftp_strdup(url);
-				FREEPTR(ppath);
-				urltype = purltype;
 			}
 		} /* ! EMPTYSTRING(penv) */
 
-		memset(&hints, 0, sizeof(hints));
-		hints.ai_flags = 0;
-		hints.ai_family = family;
-		hints.ai_socktype = SOCK_STREAM;
-		hints.ai_protocol = 0;
-		error = getaddrinfo(host, port, &hints, &res0);
-		if (error) {
-			warnx("Can't LOOKUP `%s:%s': %s", host, port,
-			    (error == EAI_SYSTEM) ? strerror(errno)
-						  : gai_strerror(error));
-			goto cleanup_fetch_url;
-		}
-		if (res0->ai_canonname)
-			host = res0->ai_canonname;
-
-		s = -1;
-#ifdef WITH_SSL
-		ssl = NULL;
-#endif
-		for (res = res0; res; res = res->ai_next) {
-			char	hname[NI_MAXHOST], sname[NI_MAXSERV];
-
-			ai_unmapped(res);
-			if (getnameinfo(res->ai_addr, res->ai_addrlen,
-			    hname, sizeof(hname), sname, sizeof(sname),
-			    NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
-				strlcpy(hname, "?", sizeof(hname));
-				strlcpy(sname, "?", sizeof(sname));
-			}
-
-			if (verbose && res0->ai_next) {
-#ifdef INET6
-				if(res->ai_family == AF_INET6) {
-					fprintf(ttyout, "Trying [%s]:%s ...\n",
-					    hname, sname);
-				} else {
-#endif
-					fprintf(ttyout, "Trying %s:%s ...\n",
-					    hname, sname);
-#ifdef INET6
-				}
-#endif
-			}
-
-			s = socket(res->ai_family, SOCK_STREAM,
-			    res->ai_protocol);
-			if (s < 0) {
-				warn(
-				    "Can't create socket for connection to "
-				    "`%s:%s'", hname, sname);
-				continue;
-			}
-
-			if (ftp_connect(s, res->ai_addr, res->ai_addrlen,
-			    verbose || !res->ai_next) < 0) {
-				close(s);
-				s = -1;
-				continue;
-			}
-
-#ifdef WITH_SSL
-			if (urltype == HTTPS_URL_T) {
-				if ((ssl = fetch_start_ssl(s, host)) == NULL) {
-					close(s);
-					s = -1;
-					continue;
-				}
-			}
-#endif
-
-			/* success */
-			break;
-		}
-
+		s = ftp_socket(&ui, &ssl);
 		if (s < 0) {
-			warnx("Can't connect to `%s:%s'", host, port);
+			warnx("Can't connect to `%s:%s'", ui.host, ui.port);
 			goto cleanup_fetch_url;
 		}
 
@@ -830,84 +1462,39 @@ fetch_url(const char *url, const char *p
 		 */
 		if (verbose)
 			fprintf(ttyout, "Requesting %s\n", url);
-		leading = "  (";
-		hasleading = 0;
-		if (isproxy) {
-			if (verbose) {
-				fprintf(ttyout, "%svia %s:%s", leading,
-				    host, port);
-				leading = ", ";
-				hasleading++;
-			}
-			fetch_printf(fin, "GET %s HTTP/1.0\r\n", path);
-			if (flushcache)
-				fetch_printf(fin, "Pragma: no-cache\r\n");
-		} else {
-			fetch_printf(fin, "GET %s HTTP/1.1\r\n", path);
-			if (strchr(host, ':')) {
-				char *h, *p;
 
-				/*
-				 * strip off IPv6 scope identifier, since it is
-				 * local to the node
-				 */
-				h = ftp_strdup(host);
-				if (isipv6addr(h) &&
-				    (p = strchr(h, '%')) != NULL) {
-					*p = '\0';
-				}
-				fetch_printf(fin, "Host: [%s]", h);
-				free(h);
-			} else
-				fetch_printf(fin, "Host: %s", host);
+		hasleading = 0;
 #ifdef WITH_SSL
-			if ((urltype == HTTP_URL_T && portnum != HTTP_PORT) ||
-			    (urltype == HTTPS_URL_T && portnum != HTTPS_PORT))
-#else
-			if (portnum != HTTP_PORT)
-#endif
-				fetch_printf(fin, ":%u", portnum);
-			fetch_printf(fin, "\r\n");
-			fetch_printf(fin, "Accept: */*\r\n");
-			fetch_printf(fin, "Connection: close\r\n");
-			if (restart_point) {
-				fputs(leading, ttyout);
-				fetch_printf(fin, "Range: bytes=" LLF "-\r\n",
-				    (LLT)restart_point);
-				fprintf(ttyout, "restarting at " LLF,
-				    (LLT)restart_point);
-				leading = ", ";
-				hasleading++;
+		if (isproxy && oui.utype == HTTPS_URL_T) {
+			switch (connectmethod(fin, url, penv, &oui, &ui,
+			    &wauth, &pauth, __UNVOLATILE(&auth), &hasleading,
+			    &rval)) {
+			case C_CLEANUP:
+				goto cleanup_fetch_url;
+			case C_IMPROPER:
+				goto improper;
+			case C_OK:
+				break;
+			default:
+				abort();
 			}
-			if (flushcache)
-				fetch_printf(fin, "Cache-Control: no-cache\r\n");
-		}
-		if ((useragent=getenv("FTPUSERAGENT")) != NULL) {
-			fetch_printf(fin, "User-Agent: %s\r\n", useragent);
-		} else {
-			fetch_printf(fin, "User-Agent: %s/%s\r\n",
-			    FTP_PRODUCT, FTP_VERSION);
 		}
-		if (wwwauth) {
-			if (verbose) {
-				fprintf(ttyout, "%swith authorization",
-				    leading);
-				leading = ", ";
-				hasleading++;
-			}
-			fetch_printf(fin, "Authorization: %s\r\n", wwwauth);
-		}
-		if (proxyauth) {
-			if (verbose) {
-				fprintf(ttyout,
-				    "%swith proxy authorization", leading);
-				leading = ", ";
-				hasleading++;
-			}
-			fetch_printf(fin, "Proxy-Authorization: %s\r\n", proxyauth);
+#endif
+
+		hasleading = print_get(fin, hasleading, isproxy, &oui, &ui);
+
+		if (flushcache)
+			print_cache(fin, isproxy);
+
+		print_agent(fin);
+		hasleading = print_proxy(fin, hasleading, wauth.auth,
+		     auth ? NULL : pauth.auth);
+		if (hasleading) {
+			hasleading = 0;
+			if (verbose)
+				fputs(")\n", ttyout);
 		}
-		if (verbose && hasleading)
-			fputs(")\n", ttyout);
+
 		fetch_printf(fin, "\r\n");
 		if (fetch_flush(fin) == EOF) {
 			warn("Writing HTTP request");
@@ -916,257 +1503,19 @@ fetch_url(const char *url, const char *p
 		}
 		alarmtimer(0);
 
-				/* Read the response */
-		alarmtimer(quit_time ? quit_time : 60);
-		len = fetch_getline(fin, buf, sizeof(buf), &errormsg);
-		alarmtimer(0);
-		if (len < 0) {
-			if (*errormsg == '\n')
-				errormsg++;
-			warnx("Receiving HTTP reply: %s", errormsg);
-			goto cleanup_fetch_url;
-		}
-		while (len > 0 && (ISLWS(buf[len-1])))
-			buf[--len] = '\0';
-		DPRINTF("%s: received `%s'\n", __func__, buf);
-
-				/* Determine HTTP response code */
-		cp = strchr(buf, ' ');
-		if (cp == NULL)
-			goto improper;
-		else
-			cp++;
-		hcode = strtol(cp, &ep, 10);
-		if (*ep != '\0' && !isspace((unsigned char)*ep))
-			goto improper;
-		message = ftp_strdup(cp);
-
-				/* Read the rest of the header. */
-		while (1) {
-			alarmtimer(quit_time ? quit_time : 60);
-			len = fetch_getline(fin, buf, sizeof(buf), &errormsg);
-			alarmtimer(0);
-			if (len < 0) {
-				if (*errormsg == '\n')
-					errormsg++;
-				warnx("Receiving HTTP reply: %s", errormsg);
-				goto cleanup_fetch_url;
-			}
-			while (len > 0 && (ISLWS(buf[len-1])))
-				buf[--len] = '\0';
-			if (len == 0)
-				break;
-			DPRINTF("%s: received `%s'\n", __func__, buf);
-
-		/*
-		 * Look for some headers
-		 */
-
-			cp = buf;
-
-			if (match_token(&cp, "Content-Length:")) {
-				filesize = STRTOLL(cp, &ep, 10);
-				if (filesize < 0 || *ep != '\0')
-					goto improper;
-				DPRINTF("%s: parsed len as: " LLF "\n",
-				    __func__, (LLT)filesize);
-
-			} else if (match_token(&cp, "Content-Range:")) {
-				if (! match_token(&cp, "bytes"))
-					goto improper;
-
-				if (*cp == '*')
-					cp++;
-				else {
-					rangestart = STRTOLL(cp, &ep, 10);
-					if (rangestart < 0 || *ep != '-')
-						goto improper;
-					cp = ep + 1;
-					rangeend = STRTOLL(cp, &ep, 10);
-					if (rangeend < 0 || rangeend < rangestart)
-						goto improper;
-					cp = ep;
-				}
-				if (*cp != '/')
-					goto improper;
-				cp++;
-				if (*cp == '*')
-					cp++;
-				else {
-					entitylen = STRTOLL(cp, &ep, 10);
-					if (entitylen < 0)
-						goto improper;
-					cp = ep;
-				}
-				if (*cp != '\0')
-					goto improper;
-
-#ifndef NO_DEBUG
-				if (ftp_debug) {
-					fprintf(ttyout, "parsed range as: ");
-					if (rangestart == -1)
-						fprintf(ttyout, "*");
-					else
-						fprintf(ttyout, LLF "-" LLF,
-						    (LLT)rangestart,
-						    (LLT)rangeend);
-					fprintf(ttyout, "/" LLF "\n", (LLT)entitylen);
-				}
-#endif
-				if (! restart_point) {
-					warnx(
-				    "Received unexpected Content-Range header");
-					goto cleanup_fetch_url;
-				}
-
-			} else if (match_token(&cp, "Last-Modified:")) {
-				struct tm parsed;
-				const char *t;
-
-				memset(&parsed, 0, sizeof(parsed));
-				t = parse_rfc2616time(&parsed, cp);
-				if (t != NULL) {
-					parsed.tm_isdst = -1;
-					if (*t == '\0')
-						mtime = timegm(&parsed);
-#ifndef NO_DEBUG
-					if (ftp_debug && mtime != -1) {
-						fprintf(ttyout,
-						    "parsed time as: %s",
-						rfc2822time(localtime(&mtime)));
-					}
-#endif
-				}
-
-			} else if (match_token(&cp, "Location:")) {
-				location = ftp_strdup(cp);
-				DPRINTF("%s: parsed location as `%s'\n",
-				    __func__, cp);
-
-			} else if (match_token(&cp, "Transfer-Encoding:")) {
-				if (match_token(&cp, "binary")) {
-					warnx(
-			"Bogus transfer encoding `binary' (fetching anyway)");
-					continue;
-				}
-				if (! (token = match_token(&cp, "chunked"))) {
-					warnx(
-				    "Unsupported transfer encoding `%s'",
-					    token);
-					goto cleanup_fetch_url;
-				}
-				ischunked++;
-				DPRINTF("%s: using chunked encoding\n",
-				    __func__);
-
-			} else if (match_token(&cp, "Proxy-Authenticate:")
-				|| match_token(&cp, "WWW-Authenticate:")) {
-				if (! (token = match_token(&cp, "Basic"))) {
-					DPRINTF("%s: skipping unknown auth "
-					    "scheme `%s'\n", __func__, token);
-					continue;
-				}
-				FREEPTR(auth);
-				auth = ftp_strdup(token);
-				DPRINTF("%s: parsed auth as `%s'\n",
-				    __func__, cp);
-			}
-
-		}
-				/* finished parsing header */
-
-		switch (hcode) {
-		case 200:
+		switch (negotiate_connection(fin, url, penv, &pi,
+		    &mtime, &wauth, &pauth, &rval, &ischunked,
+		    __UNVOLATILE(&auth))) {
+		case C_OK:
 			break;
-		case 206:
-			if (! restart_point) {
-				warnx("Not expecting partial content header");
-				goto cleanup_fetch_url;
-			}
-			break;
-		case 300:
-		case 301:
-		case 302:
-		case 303:
-		case 305:
-		case 307:
-			if (EMPTYSTRING(location)) {
-				warnx(
-				"No redirection Location provided by server");
-				goto cleanup_fetch_url;
-			}
-			if (redirect_loop++ > 5) {
-				warnx("Too many redirections requested");
-				goto cleanup_fetch_url;
-			}
-			if (hcode == 305) {
-				if (verbose)
-					fprintf(ttyout, "Redirected via %s\n",
-					    location);
-				rval = fetch_url(url, location,
-				    proxyauth, wwwauth);
-			} else {
-				if (verbose)
-					fprintf(ttyout, "Redirected to %s\n",
-					    location);
-				rval = go_fetch(location);
-			}
-			goto cleanup_fetch_url;
-#ifndef NO_AUTH
-		case 401:
-		case 407:
-		    {
-			char **authp;
-			char *auser, *apass;
-
-			if (hcode == 401) {
-				authp = &wwwauth;
-				auser = uuser;
-				apass = pass;
-			} else {
-				authp = &proxyauth;
-				auser = puser;
-				apass = ppass;
-			}
-			if (verbose || *authp == NULL ||
-			    auser == NULL || apass == NULL)
-				fprintf(ttyout, "%s\n", message);
-			if (EMPTYSTRING(auth)) {
-				warnx(
-			    "No authentication challenge provided by server");
-				goto cleanup_fetch_url;
-			}
-			if (*authp != NULL) {
-				char reply[10];
-
-				fprintf(ttyout,
-				    "Authorization failed. Retry (y/n)? ");
-				if (get_line(stdin, reply, sizeof(reply), NULL)
-				    < 0) {
-					goto cleanup_fetch_url;
-				}
-				if (tolower((unsigned char)reply[0]) != 'y')
-					goto cleanup_fetch_url;
-				auser = NULL;
-				apass = NULL;
-			}
-			if (auth_url(auth, authp, auser, apass) == 0) {
-				rval = fetch_url(url, penv,
-				    proxyauth, wwwauth);
-				memset(*authp, 0, strlen(*authp));
-				FREEPTR(*authp);
-			}
+		case C_CLEANUP:
 			goto cleanup_fetch_url;
-		    }
-#endif
+		case C_IMPROPER:
+			goto improper;
 		default:
-			if (message)
-				warnx("Error retrieving file `%s'", message);
-			else
-				warnx("Unknown error retrieving file");
-			goto cleanup_fetch_url;
+			abort();
 		}
-	}		/* end of ftp:// or http:// specific setup */
+	}
 
 	/* Open the output file. */
 
@@ -1188,18 +1537,19 @@ fetch_url(const char *url, const char *p
 		}
 	}
 	if (fout == NULL) {
-		if ((rangeend != -1 && rangeend <= restart_point) ||
-		    (rangestart == -1 && filesize != -1 && filesize <= restart_point)) {
+		if ((pi.rangeend != -1 && pi.rangeend <= restart_point) ||
+		    (pi.rangestart == -1 &&
+		    filesize != -1 && filesize <= restart_point)) {
 			/* already done */
 			if (verbose)
 				fprintf(ttyout, "already done\n");
 			rval = 0;
 			goto cleanup_fetch_url;
 		}
-		if (restart_point && rangestart != -1) {
-			if (entitylen != -1)
-				filesize = entitylen;
-			if (rangestart != restart_point) {
+		if (restart_point && pi.rangestart != -1) {
+			if (pi.entitylen != -1)
+				filesize = pi.entitylen;
+			if (pi.rangestart != restart_point) {
 				warnx(
 				    "Size of `%s' differs from save file `%s'",
 				    url, savefile);
@@ -1297,10 +1647,14 @@ fetch_url(const char *url, const char *p
 			if (ischunked)
 				bufrem = MIN(chunksize, bufrem);
 			while (bufrem > 0) {
+				size_t nr = MIN((off_t)bufsize, bufrem);
 				flen = fetch_read(xferbuf, sizeof(char),
-				    MIN((off_t)bufsize, bufrem), fin);
-				if (flen <= 0)
+				    nr, fin);
+				if (flen == 0) {
+					if (fetch_error(fin))
+						goto chunkerror;
 					goto chunkdone;
+				}
 				bytes += flen;
 				bufrem -= flen;
 				if (fwrite(xferbuf, sizeof(char), flen, fout)
@@ -1351,7 +1705,7 @@ fetch_url(const char *url, const char *p
 	} while (ischunked);
 
 /* XXX: deal with optional trailer & CRLF here? */
-
+chunkerror:
 	if (hash && !progress && bytes > 0) {
 		if (bytes < mark)
 			(void)putc('#', ttyout);
@@ -1386,7 +1740,7 @@ fetch_url(const char *url, const char *p
 	goto cleanup_fetch_url;
 
  improper:
-	warnx("Improper response from `%s:%s'", host, port);
+	warnx("Improper response from `%s:%s'", ui.host, ui.port);
 
  cleanup_fetch_url:
 	if (oldint)
@@ -1403,22 +1757,13 @@ fetch_url(const char *url, const char *p
 		close(s);
 	if (closefunc != NULL && fout != NULL)
 		(*closefunc)(fout);
-	if (res0)
-		freeaddrinfo(res0);
 	if (savefile != outfile)
 		FREEPTR(savefile);
-	FREEPTR(uuser);
-	if (pass != NULL)
-		memset(pass, 0, strlen(pass));
-	FREEPTR(pass);
-	FREEPTR(host);
-	FREEPTR(port);
-	FREEPTR(path);
+	freeurlinfo(&ui);
+	freeurlinfo(&oui);
+	freeauthinfo(&wauth);
+	freeauthinfo(&pauth);
 	FREEPTR(decodedpath);
-	FREEPTR(puser);
-	if (ppass != NULL)
-		memset(ppass, 0, strlen(ppass));
-	FREEPTR(ppass);
 	FREEPTR(auth);
 	FREEPTR(location);
 	FREEPTR(message);
@@ -1470,26 +1815,26 @@ static int
 fetch_ftp(const char *url)
 {
 	char		*cp, *xargv[5], rempath[MAXPATHLEN];
-	char		*host, *path, *dir, *file, *uuser, *pass;
-	char		*port;
+	char		*dir, *file;
 	char		 cmdbuf[MAXPATHLEN];
 	char		 dirbuf[4];
 	int		 dirhasglob, filehasglob, rval, transtype, xargc;
 	int		 oanonftp, oautologin;
-	in_port_t	 portnum;
-	url_t		 urltype;
+	struct authinfo  auth;
+	struct urlinfo	 ui;
 
 	DPRINTF("fetch_ftp: `%s'\n", url);
-	host = path = dir = file = uuser = pass = NULL;
-	port = NULL;
+	dir = file = NULL;
 	rval = 1;
 	transtype = TYPE_I;
 
+	initurlinfo(&ui);
+	initauthinfo(&auth, NULL);
+
 	if (STRNEQUAL(url, FTP_URL)) {
-		if ((parse_url(url, "URL", &urltype, &uuser, &pass,
-		    &host, &port, &portnum, &path) == -1) ||
-		    (uuser != NULL && *uuser == '\0') ||
-		    EMPTYSTRING(host)) {
+		if ((parse_url(url, "URL", &ui, &auth) == -1) ||
+		    (auth.user != NULL && *auth.user == '\0') ||
+		    EMPTYSTRING(ui.host)) {
 			warnx("Invalid URL `%s'", url);
 			goto cleanup_fetch_ftp;
 		}
@@ -1499,7 +1844,7 @@ fetch_ftp(const char *url)
 		 */
 
 					/* check for trailing ';type=[aid]' */
-		if (! EMPTYSTRING(path) && (cp = strrchr(path, ';')) != NULL) {
+		if (! EMPTYSTRING(ui.path) && (cp = strrchr(ui.path, ';')) != NULL) {
 			if (strcasecmp(cp, ";type=a") == 0)
 				transtype = TYPE_A;
 			else if (strcasecmp(cp, ";type=i") == 0)
@@ -1516,26 +1861,26 @@ fetch_ftp(const char *url)
 			*cp = 0;
 		}
 	} else {			/* classic style `[user@]host:[file]' */
-		urltype = CLASSIC_URL_T;
-		host = ftp_strdup(url);
-		cp = strchr(host, '@');
+		ui.utype = CLASSIC_URL_T;
+		ui.host = ftp_strdup(url);
+		cp = strchr(ui.host, '@');
 		if (cp != NULL) {
 			*cp = '\0';
-			uuser = host;
+			auth.user = ui.host;
 			anonftp = 0;	/* disable anonftp */
-			host = ftp_strdup(cp + 1);
+			ui.host = ftp_strdup(cp + 1);
 		}
-		cp = strchr(host, ':');
+		cp = strchr(ui.host, ':');
 		if (cp != NULL) {
 			*cp = '\0';
-			path = ftp_strdup(cp + 1);
+			ui.path = ftp_strdup(cp + 1);
 		}
 	}
-	if (EMPTYSTRING(host))
+	if (EMPTYSTRING(ui.host))
 		goto cleanup_fetch_ftp;
 
 			/* Extract the file and (if present) directory name. */
-	dir = path;
+	dir = ui.path;
 	if (! EMPTYSTRING(dir)) {
 		/*
 		 * If we are dealing with classic `[user@]host:[path]' syntax,
@@ -1560,7 +1905,7 @@ fetch_ftp(const char *url)
 		 * resulting from an URL of the form `ftp://host//file'.)
 		 */
 		cp = strrchr(dir, '/');
-		if (cp == dir && urltype == CLASSIC_URL_T) {
+		if (cp == dir && ui.utype == CLASSIC_URL_T) {
 			file = cp + 1;
 			(void)strlcpy(dirbuf, "/", sizeof(dirbuf));
 			dir = dirbuf;
@@ -1573,18 +1918,19 @@ fetch_ftp(const char *url)
 		}
 	} else
 		dir = NULL;
-	if (urltype == FTP_URL_T && file != NULL) {
+	if (ui.utype == FTP_URL_T && file != NULL) {
 		url_decode(file);
 		/* but still don't url_decode(dir) */
 	}
 	DPRINTF("fetch_ftp: user `%s' pass `%s' host %s port %s "
 	    "path `%s' dir `%s' file `%s'\n",
-	    STRorNULL(uuser), STRorNULL(pass),
-	    STRorNULL(host), STRorNULL(port),
-	    STRorNULL(path), STRorNULL(dir), STRorNULL(file));
+	    STRorNULL(auth.user), STRorNULL(auth.pass),
+	    STRorNULL(ui.host), STRorNULL(ui.port),
+	    STRorNULL(ui.path), STRorNULL(dir), STRorNULL(file));
 
 	dirhasglob = filehasglob = 0;
-	if (doglob && urltype == CLASSIC_URL_T) {
+	if (doglob &&
+	    (ui.utype == CLASSIC_URL_T || ui.utype == FTP_URL_T)) {
 		if (! EMPTYSTRING(dir) && strpbrk(dir, "*?[]{}") != NULL)
 			dirhasglob = 1;
 		if (! EMPTYSTRING(file) && strpbrk(file, "*?[]{}") != NULL)
@@ -1598,11 +1944,11 @@ fetch_ftp(const char *url)
 	anonftp = oanonftp;
 	(void)strlcpy(cmdbuf, getprogname(), sizeof(cmdbuf));
 	xargv[0] = cmdbuf;
-	xargv[1] = host;
+	xargv[1] = ui.host;
 	xargv[2] = NULL;
 	xargc = 2;
-	if (port) {
-		xargv[2] = port;
+	if (ui.port) {
+		xargv[2] = ui.port;
 		xargv[3] = NULL;
 		xargc = 3;
 	}
@@ -1612,9 +1958,9 @@ fetch_ftp(const char *url)
 	setpeer(xargc, xargv);
 	autologin = oautologin;
 	if ((connected == 0) ||
-	    (connected == 1 && !ftp_login(host, uuser, pass))) {
+	    (connected == 1 && !ftp_login(ui.host, auth.user, auth.pass))) {
 		warnx("Can't connect or login to host `%s:%s'",
-			host, port ? port : "?");
+			ui.host, ui.port ? ui.port : "?");
 		goto cleanup_fetch_ftp;
 	}
 
@@ -1699,7 +2045,7 @@ fetch_ftp(const char *url)
 		 * Note that we don't need `dir' after this point.
 		 */
 		do {
-			if (urltype == FTP_URL_T) {
+			if (ui.utype == FTP_URL_T) {
 				nextpart = strchr(dir, '/');
 				if (nextpart) {
 					*nextpart = '\0';
@@ -1710,7 +2056,7 @@ fetch_ftp(const char *url)
 				nextpart = NULL;
 			DPRINTF("fetch_ftp: dir `%s', nextpart `%s'\n",
 			    STRorNULL(dir), STRorNULL(nextpart));
-			if (urltype == FTP_URL_T || *dir != '\0') {
+			if (ui.utype == FTP_URL_T || *dir != '\0') {
 				(void)strlcpy(cmdbuf, "cd", sizeof(cmdbuf));
 				xargv[0] = cmdbuf;
 				xargv[1] = dir;
@@ -1764,14 +2110,15 @@ fetch_ftp(const char *url)
 		mget(xargc, xargv);
 		interactive = ointeractive;
 	} else {
-		if (outfile == NULL) {
+		char *destfile = outfile;
+		if (destfile == NULL) {
 			cp = strrchr(file, '/');	/* find savefile */
 			if (cp != NULL)
-				outfile = cp + 1;
+				destfile = cp + 1;
 			else
-				outfile = file;
+				destfile = file;
 		}
-		xargv[2] = (char *)outfile;
+		xargv[2] = (char *)destfile;
 		xargv[3] = NULL;
 		xargc++;
 		if (restartautofetch)
@@ -1784,13 +2131,8 @@ fetch_ftp(const char *url)
 		rval = 0;
 
  cleanup_fetch_ftp:
-	FREEPTR(port);
-	FREEPTR(host);
-	FREEPTR(path);
-	FREEPTR(uuser);
-	if (pass)
-		memset(pass, 0, strlen(pass));
-	FREEPTR(pass);
+	freeurlinfo(&ui);
+	freeauthinfo(&auth);
 	return (rval);
 }
 
@@ -1922,8 +2264,9 @@ auto_fetch(int argc, char *argv[])
 			anonftp = 2;	/* Handle "automatic" transfers. */
 		rval = go_fetch(argv[argpos]);
 		if (outfile != NULL && strcmp(outfile, "-") != 0
-		    && outfile[0] != '|')
-			outfile = NULL;
+		    && outfile[0] != '|') {
+			FREEPTR(outfile);
+		}
 		if (rval > 0)
 			rval = argpos + 1;
 	}
Index: othersrc/usr.bin/tnftp/src/util.c
diff -u othersrc/usr.bin/tnftp/src/util.c:1.23 othersrc/usr.bin/tnftp/src/util.c:1.24
--- othersrc/usr.bin/tnftp/src/util.c:1.23	Sun May  5 11:51:43 2013
+++ othersrc/usr.bin/tnftp/src/util.c	Sat Jul  4 09:59:07 2020
@@ -1,8 +1,8 @@
-/*	$NetBSD: util.c,v 1.23 2013/05/05 11:51:43 lukem Exp $	*/
-/*	from	NetBSD: util.c,v 1.158 2013/02/19 23:29:15 dsl Exp	*/
+/*	$NetBSD: util.c,v 1.24 2020/07/04 09:59:07 lukem Exp $	*/
+/*	from	NetBSD: util.c,v 1.161 2020/06/08 01:33:27 lukem Exp	*/
 
 /*-
- * Copyright (c) 1997-2009 The NetBSD Foundation, Inc.
+ * Copyright (c) 1997-2020 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This code is derived from software contributed to The NetBSD Foundation
@@ -69,7 +69,7 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID(" NetBSD: util.c,v 1.158 2013/02/19 23:29:15 dsl Exp  ");
+__RCSID(" NetBSD: util.c,v 1.161 2020/06/08 01:33:27 lukem Exp  ");
 #endif /* not lint */
 
 /*
@@ -331,9 +331,10 @@ intr(int signo)
 /*
  * Signal handler for lost connections; cleanup various elements of
  * the connection state, and call cleanuppeer() to finish it off.
+ * This function is not signal safe, so exit if called by a signal.
  */
 void
-lostpeer(int dummy)
+lostpeer(int signo)
 {
 	int oerrno = errno;
 
@@ -363,6 +364,9 @@ lostpeer(int dummy)
 	proxflag = 0;
 	pswitch(0);
 	cleanuppeer();
+	if (signo) {
+		errx(1, "lostpeer due to signal %d", signo);
+	}
 	errno = oerrno;
 }
 
@@ -485,7 +489,8 @@ ftp_login(const char *host, const char *
 		}
 	}
 	updatelocalcwd();
-	updateremotecwd();
+	remotecwd[0] = '\0';
+	remcwdvalid = 0;
 
  cleanup_ftp_login:
 	FREEPTR(fuser);
@@ -846,6 +851,7 @@ updateremotecwd(void)
 	size_t	 i;
 	char	*cp;
 
+	remcwdvalid = 1;	/* whether it works or not, we are done */
 	overbose = verbose;
 	ocode = code;
 	if (ftp_debug == 0)
@@ -1185,6 +1191,8 @@ formatbuf(char *buf, size_t len, const c
 		case '/':
 		case '.':
 		case 'c':
+			if (connected && !remcwdvalid)
+				updateremotecwd();
 			p2 = connected ? remotecwd : "";
 			updirs = pdirs = 0;
 
@@ -1556,6 +1564,7 @@ ftp_poll(struct pollfd *fds, int nfds, i
 #endif
 }
 
+#ifndef SMALL
 /*
  * malloc() with inbuilt error checking
  */
@@ -1610,3 +1619,4 @@ ftp_strdup(const char *str)
 		err(1, "Unable to allocate memory for string copy");
 	return (s);
 }
+#endif

Index: othersrc/usr.bin/tnftp/src/ftp.1
diff -u othersrc/usr.bin/tnftp/src/ftp.1:1.15 othersrc/usr.bin/tnftp/src/ftp.1:1.16
--- othersrc/usr.bin/tnftp/src/ftp.1:1.15	Sun Oct  4 04:53:26 2015
+++ othersrc/usr.bin/tnftp/src/ftp.1	Sat Jul  4 09:59:07 2020
@@ -1,5 +1,5 @@
-.\" 	$NetBSD: ftp.1,v 1.15 2015/10/04 04:53:26 lukem Exp $
-.\" 	from	NetBSD: ftp.1,v 1.135 2015/04/23 23:31:23 lukem Exp
+.\" 	$NetBSD: ftp.1,v 1.16 2020/07/04 09:59:07 lukem Exp $
+.\" 	from	NetBSD: ftp.1,v 1.136 2017/07/03 21:34:57 wiz Exp
 .\"
 .\" Copyright (c) 1996-2015 The NetBSD Foundation, Inc.
 .\" All rights reserved.
@@ -335,7 +335,7 @@ from the user.
 When
 .Nm
 is awaiting commands from the user the prompt
-.Ql ftp\*[Gt]
+.Ql ftp>
 is provided to the user.
 The following commands are recognized
 by
@@ -529,7 +529,7 @@ When debugging is on,
 .Nm
 prints each command sent to the remote machine, preceded
 by the string
-.Ql \-\-\*[Gt] .
+.Ql \-\-> .
 .It Ic gate Op Ar host Op Ar port
 Toggle gate-ftp mode, which used to connect through the
 TIS FWTK and Gauntlet ftp proxies.
@@ -898,7 +898,7 @@ the output filename "myfile.data" for in
 Spaces may be included in
 .Ar outpattern  ,
 as in the example:
-.Dl nmap $1 sed "s/  *$//" \*[Gt] $1
+.Dl nmap $1 sed "s/  *$//" > $1
 Use the
 .Sq \e
 character to prevent special treatment
@@ -2151,7 +2151,7 @@ By default, this is bound to the TAB key
 By default,
 .Nm
 displays a command line prompt of
-.Dq "ftp\*[Gt] "
+.Dq "ftp> "
 to the user.
 This can be changed with the
 .Ic "set prompt"
@@ -2183,7 +2183,7 @@ the format
 .\" ``/<number>trailing''
 .Do
 .Sm off
-.Li / Li \*[Lt] Va number Li \*[Gt]
+.Li / Li < Va number Li >
 .Va trailing
 .Sm on
 .Dc
@@ -2235,7 +2235,7 @@ only
 .It Ev FTPPROMPT
 Command-line prompt to use.
 Defaults to
-.Dq "ftp\*[Gt] " .
+.Dq "ftp> " .
 Refer to
 .Sx COMMAND LINE PROMPT
 for more information.
Index: othersrc/usr.bin/tnftp/src/progressbar.c
diff -u othersrc/usr.bin/tnftp/src/progressbar.c:1.15 othersrc/usr.bin/tnftp/src/progressbar.c:1.16
--- othersrc/usr.bin/tnftp/src/progressbar.c:1.15	Sun May  5 11:17:31 2013
+++ othersrc/usr.bin/tnftp/src/progressbar.c	Sat Jul  4 09:59:07 2020
@@ -1,5 +1,5 @@
-/*	$NetBSD: progressbar.c,v 1.15 2013/05/05 11:17:31 lukem Exp $	*/
-/*	from	NetBSD: progressbar.c,v 1.22 2012/06/27 22:07:36 riastradh Exp	*/
+/*	$NetBSD: progressbar.c,v 1.16 2020/07/04 09:59:07 lukem Exp $	*/
+/*	from	NetBSD: progressbar.c,v 1.23 2019/06/22 23:40:33 christos Exp	*/
 
 /*-
  * Copyright (c) 1997-2009 The NetBSD Foundation, Inc.
@@ -36,7 +36,7 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID(" NetBSD: progressbar.c,v 1.22 2012/06/27 22:07:36 riastradh Exp  ");
+__RCSID(" NetBSD: progressbar.c,v 1.23 2019/06/22 23:40:33 christos Exp  ");
 #endif /* not lint */
 
 /*
@@ -98,6 +98,7 @@ updateprogressmeter(int dummy)
 /*
  * List of order of magnitude suffixes, per IEC 60027-2.
  */
+#if !defined(NO_PROGRESS) || !defined(STANDALONE_PROGRESS)
 static const char * const suffixes[] = {
 	"",	/* 2^0  (byte) */
 	"KiB",	/* 2^10 Kibibyte */
@@ -113,6 +114,7 @@ static const char * const suffixes[] = {
 #endif
 };
 #define NSUFFIXES	(int)(sizeof(suffixes) / sizeof(suffixes[0]))
+#endif
 
 /*
  * Display a transfer progress bar if progress is non-zero.
@@ -150,8 +152,10 @@ progressmeter(int flag)
 			 *	these appropriately.
 			 */
 #endif
+#if !defined(NO_PROGRESS) || !defined(STANDALONE_PROGRESS)
 	size_t		len;
 	char		buf[256];	/* workspace for progress bar */
+#endif
 #ifndef NO_PROGRESS
 #define	BAROVERHEAD	45		/* non `*' portion of progress bar */
 					/*

Index: othersrc/usr.bin/tnftp/src/ftp.c
diff -u othersrc/usr.bin/tnftp/src/ftp.c:1.21 othersrc/usr.bin/tnftp/src/ftp.c:1.22
--- othersrc/usr.bin/tnftp/src/ftp.c:1.21	Sun Dec 13 14:08:09 2015
+++ othersrc/usr.bin/tnftp/src/ftp.c	Sat Jul  4 09:59:07 2020
@@ -1,8 +1,8 @@
-/*	$NetBSD: ftp.c,v 1.21 2015/12/13 14:08:09 tron Exp $	*/
-/*	from	NetBSD: ftp.c,v 1.164 2012/07/04 06:09:37 is Exp	*/
+/*	$NetBSD: ftp.c,v 1.22 2020/07/04 09:59:07 lukem Exp $	*/
+/*	from	NetBSD: ftp.c,v 1.169 2020/06/08 01:33:27 lukem Exp	*/
 
 /*-
- * Copyright (c) 1996-2009 The NetBSD Foundation, Inc.
+ * Copyright (c) 1996-2020 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This code is derived from software contributed to The NetBSD Foundation
@@ -98,7 +98,7 @@
 #if 0
 static char sccsid[] = "@(#)ftp.c	8.6 (Berkeley) 10/27/94";
 #else
-__RCSID(" NetBSD: ftp.c,v 1.164 2012/07/04 06:09:37 is Exp  ");
+__RCSID(" NetBSD: ftp.c,v 1.169 2020/06/08 01:33:27 lukem Exp  ");
 #endif
 #endif /* not lint */
 
@@ -1561,8 +1561,8 @@ initconn(void)
 				result = COMPLETE + 1;
 				break;
 			}
-			/* FALLTHROUGH */
 #ifdef INET6
+			/* FALLTHROUGH */
 		case AF_INET6:
 			if (!epsv6 || epsv6bad) {
 				result = COMPLETE + 1;
@@ -2055,7 +2055,7 @@ gunique(const char *local)
  *	needs to get back to a known state.
  */
 static void
-abort_squared(int dummy)
+abort_squared(int signo)
 {
 	char msgbuf[100];
 	size_t len;
@@ -2065,14 +2065,14 @@ abort_squared(int dummy)
 	len = strlcpy(msgbuf, "\nremote abort aborted; closing connection.\n",
 	    sizeof(msgbuf));
 	write(fileno(ttyout), msgbuf, len);
-	lostpeer(0);
+	lostpeer(signo);
 	siglongjmp(xferabort, 1);
 }
 
 void
 abort_remote(FILE *din)
 {
-	char buf[BUFSIZ];
+	unsigned char buf[BUFSIZ];
 	int nfnd;
 
 	if (cout == NULL) {

Index: othersrc/usr.bin/tnftp/src/ftp_var.h
diff -u othersrc/usr.bin/tnftp/src/ftp_var.h:1.12 othersrc/usr.bin/tnftp/src/ftp_var.h:1.13
--- othersrc/usr.bin/tnftp/src/ftp_var.h:1.12	Sun Oct  4 04:53:26 2015
+++ othersrc/usr.bin/tnftp/src/ftp_var.h	Sat Jul  4 09:59:07 2020
@@ -1,5 +1,5 @@
-/*	$NetBSD: ftp_var.h,v 1.12 2015/10/04 04:53:26 lukem Exp $	*/
-/*	from	NetBSD: ftp_var.h,v 1.83 2015/01/12 14:17:08 christos Exp	*/
+/*	$NetBSD: ftp_var.h,v 1.13 2020/07/04 09:59:07 lukem Exp $	*/
+/*	from	NetBSD: ftp_var.h,v 1.85 2017/11/20 21:11:36 kre Exp	*/
 
 /*-
  * Copyright (c) 1996-2009 The NetBSD Foundation, Inc.
@@ -274,6 +274,7 @@ GLOBAL	int	unix_server;	/* server is uni
 GLOBAL	int	unix_proxy;	/* proxy is unix, can use binary for ascii */
 GLOBAL	char	localcwd[MAXPATHLEN];	/* local dir */
 GLOBAL	char	remotecwd[MAXPATHLEN];	/* remote dir */
+GLOBAL	int	remcwdvalid;		/* remotecwd has been updated */
 GLOBAL	char   *username;	/* name of user logged in as. (dynamic) */
 
 GLOBAL	sa_family_t family;	/* address family to use for connections */
@@ -342,11 +343,12 @@ extern	struct option	optiontab[];
 #endif
 
 #ifdef NO_DEBUG
-#define DPRINTF(...)
-#define DWARN(...)
+#define DPRINTF(...)	(void)0
+#define DWARN(...)	(void)0
 #else
-#define DPRINTF(...)	if (ftp_debug) (void)fprintf(ttyout, __VA_ARGS__)
-#define DWARN(...)	if (ftp_debug) warn(__VA_ARGS__)
+#define DWFTP(a)	do a; while (/*CONSTCOND*/0)
+#define DPRINTF(...)	DWFTP(if (ftp_debug) (void)fprintf(ttyout, __VA_ARGS__))
+#define DWARN(...)	DWFTP(if (ftp_debug) warn(__VA_ARGS__))
 #endif
 
 #define STRorNULL(s)	((s) ? (s) : "<null>")

Index: othersrc/usr.bin/tnftp/src/main.c
diff -u othersrc/usr.bin/tnftp/src/main.c:1.20 othersrc/usr.bin/tnftp/src/main.c:1.21
--- othersrc/usr.bin/tnftp/src/main.c:1.20	Sun Oct  4 04:53:26 2015
+++ othersrc/usr.bin/tnftp/src/main.c	Sat Jul  4 09:59:07 2020
@@ -1,5 +1,5 @@
-/*	$NetBSD: main.c,v 1.20 2015/10/04 04:53:26 lukem Exp $	*/
-/*	from	NetBSD: main.c,v 1.123 2015/04/23 23:31:23 lukem Exp	*/
+/*	$NetBSD: main.c,v 1.21 2020/07/04 09:59:07 lukem Exp $	*/
+/*	from	NetBSD: main.c,v 1.126 2019/02/04 04:09:13 mrg Exp	*/
 
 /*-
  * Copyright (c) 1996-2015 The NetBSD Foundation, Inc.
@@ -103,7 +103,7 @@ __COPYRIGHT("@(#) Copyright (c) 1985, 19
 #if 0
 static char sccsid[] = "@(#)main.c	8.6 (Berkeley) 10/9/94";
 #else
-__RCSID(" NetBSD: main.c,v 1.123 2015/04/23 23:31:23 lukem Exp  ");
+__RCSID(" NetBSD: main.c,v 1.126 2019/02/04 04:09:13 mrg Exp  ");
 #endif
 #endif /* not lint */
 
@@ -333,7 +333,7 @@ main(int volatile argc, char **volatile 
 			break;
 
 		case 'o':
-			outfile = optarg;
+			outfile = ftp_strdup(optarg);
 			if (strcmp(outfile, "-") == 0)
 				ttyout = stderr;
 			break;
@@ -473,7 +473,6 @@ main(int volatile argc, char **volatile 
 		if (localhome == NULL && !EMPTYSTRING(pw->pw_dir))
 			localhome = ftp_strdup(pw->pw_dir);
 		localname = ftp_strdup(pw->pw_name);
-		anonuser = localname;
 	}
 	if (netrc[0] == '\0' && localhome != NULL) {
 		if (strlcpy(netrc, localhome, sizeof(netrc)) >= sizeof(netrc) ||
@@ -670,7 +669,7 @@ cmdscanner(void)
 			case -2:	/* error */
 				if (fromatty)
 					putc('\n', ttyout);
-				quit(0, NULL);
+				justquit();
 				/* NOTREACHED */
 			case -3:	/* too long; try again */
 				fputs("Sorry, input line is too long.\n",
@@ -692,7 +691,7 @@ cmdscanner(void)
 			if (buf == NULL || num == 0) {
 				if (fromatty)
 					putc('\n', ttyout);
-				quit(0, NULL);
+				justquit();
 			}
 			if (num >= sizeof(line)) {
 				fputs("Sorry, input line is too long.\n",

Index: othersrc/usr.bin/tnftp/src/ssl.c
diff -u othersrc/usr.bin/tnftp/src/ssl.c:1.3 othersrc/usr.bin/tnftp/src/ssl.c:1.4
--- othersrc/usr.bin/tnftp/src/ssl.c:1.3	Sun Oct  4 04:53:26 2015
+++ othersrc/usr.bin/tnftp/src/ssl.c	Sat Jul  4 09:59:07 2020
@@ -1,5 +1,5 @@
-/*	$NetBSD: ssl.c,v 1.3 2015/10/04 04:53:26 lukem Exp $	*/
-/*	from	NetBSD: ssl.c,v 1.5 2015/09/16 15:32:53 joerg Exp	*/
+/*	$NetBSD: ssl.c,v 1.4 2020/07/04 09:59:07 lukem Exp $	*/
+/*	from	NetBSD: ssl.c,v 1.8 2019/04/07 00:44:54 christos Exp	*/
 
 /*-
  * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav
@@ -39,11 +39,12 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID(" NetBSD: ssl.c,v 1.5 2015/09/16 15:32:53 joerg Exp  ");
+__RCSID(" NetBSD: ssl.c,v 1.8 2019/04/07 00:44:54 christos Exp  ");
 #endif
 
 #include <time.h>
 #include <unistd.h>
+#include <string.h>
 #include <fcntl.h>
 
 #include <sys/param.h>
@@ -94,6 +95,7 @@ fetch_writev(struct fetch_connect *conn,
 	struct timeval now, timeout, delta;
 	fd_set writefds;
 	ssize_t len, total;
+	int fd = conn->sd;
 	int r;
 
 	if (quit_time > 0) {
@@ -104,8 +106,8 @@ fetch_writev(struct fetch_connect *conn,
 
 	total = 0;
 	while (iovcnt > 0) {
-		while (quit_time > 0 && !FD_ISSET(conn->sd, &writefds)) {
-			FD_SET(conn->sd, &writefds);
+		while (quit_time > 0 && !FD_ISSET(fd, &writefds)) {
+			FD_SET(fd, &writefds);
 			gettimeofday(&now, NULL);
 			delta.tv_sec = timeout.tv_sec - now.tv_sec;
 			delta.tv_usec = timeout.tv_usec - now.tv_usec;
@@ -118,7 +120,7 @@ fetch_writev(struct fetch_connect *conn,
 				return -1;
 			}
 			errno = 0;
-			r = select(conn->sd + 1, NULL, &writefds, NULL, &delta);
+			r = select(fd + 1, NULL, &writefds, NULL, &delta);
 			if (r == -1) {
 				if (errno == EINTR)
 					continue;
@@ -129,7 +131,7 @@ fetch_writev(struct fetch_connect *conn,
 		if (conn->ssl != NULL)
 			len = SSL_write(conn->ssl, iov->iov_base, iov->iov_len);
 		else
-			len = writev(conn->sd, iov, iovcnt);
+			len = writev(fd, iov, iovcnt);
 		if (len == 0) {
 			/* we consider a short write a failure */
 			/* XXX perhaps we shouldn't in the SSL case */
@@ -137,7 +139,7 @@ fetch_writev(struct fetch_connect *conn,
 			return -1;
 		}
 		if (len < 0) {
-			if (errno == EINTR)
+			if (errno == EINTR || errno == EAGAIN)
 				continue;
 			return -1;
 		}
@@ -155,11 +157,8 @@ fetch_writev(struct fetch_connect *conn,
 	return total;
 }
 
-/*
- * Write to a connection w/ timeout
- */
-static int
-fetch_write(struct fetch_connect *conn, const char *str, size_t len)
+static ssize_t
+fetch_write(const void *str, size_t len, struct fetch_connect *conn)
 {
 	struct iovec iov[1];
 
@@ -188,7 +187,7 @@ fetch_printf(struct fetch_connect *conn,
 		return -1;
 	}
 
-	r = fetch_write(conn, msg, len);
+	r = fetch_write(msg, len, conn);
 	free(msg);
 	return r;
 }
@@ -217,15 +216,16 @@ fetch_clearerr(struct fetch_connect *con
 int
 fetch_flush(struct fetch_connect *conn)
 {
-	int v;
 
 	if (conn->issock) {
+		int fd = conn->sd;
+		int v;
 #ifdef TCP_NOPUSH
 		v = 0;
-		setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &v, sizeof(v));
+		setsockopt(fd, IPPROTO_TCP, TCP_NOPUSH, &v, sizeof(v));
 #endif
 		v = 1;
-		setsockopt(conn->sd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v));
+		setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v));
 	}
 	return 0;
 }
@@ -278,23 +278,19 @@ fetch_fdopen(int sd, const char *fmode)
 int
 fetch_close(struct fetch_connect *conn)
 {
-	int rv = 0;
+	if (conn == NULL)
+		return 0;
 
-	if (conn != NULL) {
-		fetch_flush(conn);
-		SSL_free(conn->ssl);
-		rv = close(conn->sd);
-		if (rv < 0) {
-			errno = rv;
-			rv = EOF;
-		}
-		free(conn->cache.buf);
-		free(conn->buf);
-		free(conn);
-	}
-	return rv;
+	fetch_flush(conn);
+	SSL_free(conn->ssl);
+	close(conn->sd);
+	free(conn->cache.buf);
+	free(conn->buf);
+	free(conn);
+	return 0;
 }
 
+#define FETCH_WRITE_WAIT	-3
 #define FETCH_READ_WAIT		-2
 #define FETCH_READ_ERROR	-1
 
@@ -302,19 +298,19 @@ static ssize_t
 fetch_ssl_read(SSL *ssl, void *buf, size_t len)
 {
 	ssize_t rlen;
-	int ssl_err;
-
 	rlen = SSL_read(ssl, buf, len);
-	if (rlen < 0) {
-		ssl_err = SSL_get_error(ssl, rlen);
-		if (ssl_err == SSL_ERROR_WANT_READ ||
-		    ssl_err == SSL_ERROR_WANT_WRITE) {
-			return FETCH_READ_WAIT;
-		}
+	if (rlen >= 0)
+		return rlen;
+
+	switch (SSL_get_error(ssl, rlen)) {
+	case SSL_ERROR_WANT_READ:
+		return FETCH_READ_WAIT;
+	case SSL_ERROR_WANT_WRITE:
+		return FETCH_WRITE_WAIT;
+	default:
 		ERR_print_errors_fp(ttyout);
 		return FETCH_READ_ERROR;
 	}
-	return rlen;
 }
 
 static ssize_t
@@ -323,7 +319,7 @@ fetch_nonssl_read(int sd, void *buf, siz
 	ssize_t rlen;
 
 	rlen = read(sd, buf, len);
-	if (rlen < 0) {
+	if (rlen == -1) {
 		if (errno == EAGAIN || errno == EINTR)
 			return FETCH_READ_WAIT;
 		return FETCH_READ_ERROR;
@@ -354,14 +350,45 @@ fetch_cache_data(struct fetch_connect *c
 	return 0;
 }
 
-ssize_t
+static int
+fetch_wait(struct fetch_connect *conn, ssize_t rlen, struct timeval *timeout)
+{
+	struct timeval now, delta;
+	int fd = conn->sd;
+	fd_set fds;
+
+	FD_ZERO(&fds);
+	while (!FD_ISSET(fd, &fds)) {
+		FD_SET(fd, &fds);
+		if (quit_time > 0) {
+			gettimeofday(&now, NULL);
+			if (!timercmp(timeout, &now, >)) {
+				conn->iserr = ETIMEDOUT;
+				return -1;
+			}
+			timersub(timeout, &now, &delta);
+		}
+		errno = 0;
+		if (select(fd + 1,
+			rlen == FETCH_READ_WAIT ? &fds : NULL,
+			rlen == FETCH_WRITE_WAIT ? &fds : NULL,
+			NULL, quit_time > 0 ? &delta : NULL) < 0) {
+			if (errno == EINTR)
+				continue;
+			conn->iserr = errno;
+			return -1;
+		}
+	}
+	return 0;
+}
+
+size_t
 fetch_read(void *ptr, size_t size, size_t nmemb, struct fetch_connect *conn)
 {
-	struct timeval now, timeout, delta;
-	fd_set readfds;
 	ssize_t rlen, total;
 	size_t len;
 	char *start, *buf;
+	struct timeval timeout;
 
 	if (quit_time > 0) {
 		gettimeofday(&timeout, NULL);
@@ -413,36 +440,25 @@ fetch_read(void *ptr, size_t size, size_
 			rlen = fetch_ssl_read(conn->ssl, buf, len);
 		else
 			rlen = fetch_nonssl_read(conn->sd, buf, len);
-		if (rlen == 0) {
+		switch (rlen) {
+		case 0:
+			conn->iseof = 1;
+			return total;
+		case FETCH_READ_ERROR:
+			conn->iserr = errno;
+			if (errno == EINTR)
+				fetch_cache_data(conn, start, total);
+			return 0;
+		case FETCH_READ_WAIT:
+		case FETCH_WRITE_WAIT:
+			if (fetch_wait(conn, rlen, &timeout) == -1)
+				return 0;
 			break;
-		} else if (rlen > 0) {
+		default:
 			len -= rlen;
 			buf += rlen;
 			total += rlen;
-			continue;
-		} else if (rlen == FETCH_READ_ERROR) {
-			if (errno == EINTR)
-				fetch_cache_data(conn, start, total);
-			return -1;
-		}
-		FD_ZERO(&readfds);
-		while (!FD_ISSET(conn->sd, &readfds)) {
-			FD_SET(conn->sd, &readfds);
-			if (quit_time > 0) {
-				gettimeofday(&now, NULL);
-				if (!timercmp(&timeout, &now, >)) {
-					errno = ETIMEDOUT;
-					return -1;
-				}
-				timersub(&timeout, &now, &delta);
-			}
-			errno = 0;
-			if (select(conn->sd + 1, &readfds, NULL, NULL,
-				quit_time > 0 ? &delta : NULL) < 0) {
-				if (errno == EINTR)
-					continue;
-				return -1;
-			}
+			break;
 		}
 	}
 	return total;
@@ -457,7 +473,7 @@ char *
 fetch_getln(char *str, int size, struct fetch_connect *conn)
 {
 	size_t tmpsize;
-	ssize_t len;
+	size_t len;
 	char c;
 
 	if (conn->buf == NULL) {
@@ -480,13 +496,12 @@ fetch_getln(char *str, int size, struct 
 	conn->buflen = 0;
 	do {
 		len = fetch_read(&c, sizeof(c), 1, conn);
-		if (len == -1) {
-			conn->iserr = 1;
-			return NULL;
-		}
 		if (len == 0) {
-			conn->iseof = 1;
-			break;
+			if (conn->iserr)
+				return NULL;
+			if (conn->iseof)
+				break;
+			abort();
 		}
 		conn->buf[conn->buflen++] = c;
 		if (conn->buflen == conn->bufsize) {
@@ -538,8 +553,8 @@ fetch_getline(struct fetch_connect *conn
 	} else if (len == buflen - 1) {	/* line too long */
 		while (1) {
 			char c;
-			ssize_t rlen = fetch_read(&c, sizeof(c), 1, conn);
-			if (rlen <= 0 || c == '\n')
+			size_t rlen = fetch_read(&c, sizeof(c), 1, conn);
+			if (rlen == 0 || c == '\n')
 				break;
 		}
 		if (errormsg)
Index: othersrc/usr.bin/tnftp/src/ssl.h
diff -u othersrc/usr.bin/tnftp/src/ssl.h:1.3 othersrc/usr.bin/tnftp/src/ssl.h:1.4
--- othersrc/usr.bin/tnftp/src/ssl.h:1.3	Sun Oct  4 04:53:26 2015
+++ othersrc/usr.bin/tnftp/src/ssl.h	Sat Jul  4 09:59:07 2020
@@ -1,5 +1,5 @@
-/*	$NetBSD: ssl.h,v 1.3 2015/10/04 04:53:26 lukem Exp $	*/
-/*	from	NetBSD: ssl.h,v 1.3 2015/09/12 19:38:42 wiz Exp	*/
+/*	$NetBSD: ssl.h,v 1.4 2020/07/04 09:59:07 lukem Exp $	*/
+/*	from	NetBSD: ssl.h,v 1.4 2019/04/04 00:36:09 christos Exp	*/
 
 /*-
  * Copyright (c) 2012 The NetBSD Foundation, Inc.
@@ -39,7 +39,7 @@ int fetch_flush(struct fetch_connect *);
 struct fetch_connect *fetch_open(const char *, const char *);
 struct fetch_connect *fetch_fdopen(int, const char *);
 int fetch_close(struct fetch_connect *);
-ssize_t fetch_read(void *, size_t, size_t, struct fetch_connect *);
+size_t fetch_read(void *, size_t, size_t, struct fetch_connect *);
 char *fetch_getln(char *, int, struct fetch_connect *);
 int fetch_getline(struct fetch_connect *, char *, size_t, const char **);
 void fetch_set_ssl(struct fetch_connect *, void *);

Index: othersrc/usr.bin/tnftp/src/version.h
diff -u othersrc/usr.bin/tnftp/src/version.h:1.7 othersrc/usr.bin/tnftp/src/version.h:1.8
--- othersrc/usr.bin/tnftp/src/version.h:1.7	Sun Oct  4 04:53:26 2015
+++ othersrc/usr.bin/tnftp/src/version.h	Sat Jul  4 09:59:07 2020
@@ -1,8 +1,8 @@
-/*	$NetBSD: version.h,v 1.7 2015/10/04 04:53:26 lukem Exp $	*/
-/*	from	NetBSD: version.h,v 1.87 2015/09/12 20:18:52 wiz Exp	*/
+/*	$NetBSD: version.h,v 1.8 2020/07/04 09:59:07 lukem Exp $	*/
+/*	from	NetBSD: version.h,v 1.89 2020/06/08 01:33:27 lukem Exp	*/
 
 /*-
- * Copyright (c) 1999-2015 The NetBSD Foundation, Inc.
+ * Copyright (c) 1999-2020 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This code is derived from software contributed to The NetBSD Foundation
@@ -35,5 +35,5 @@
 #endif
 
 #ifndef FTP_VERSION
-#define	FTP_VERSION	"20150912"
+#define	FTP_VERSION	"20200608"
 #endif

Reply via email to