diff --git a/crypto/openssh/misc.c b/crypto/openssh/misc.c
index 662d6bf48716..bdc06fdb3332 100644
--- a/crypto/openssh/misc.c
+++ b/crypto/openssh/misc.c
@@ -1,2040 +1,2039 @@
 /* $OpenBSD: misc.c,v 1.133 2018/10/05 14:26:09 naddy Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  * Copyright (c) 2005,2006 Damien Miller.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "includes.h"
 
 #include <sys/types.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
-#include <sys/sysctl.h>
 #include <sys/time.h>
 #include <sys/wait.h>
 #include <sys/un.h>
 
 #include <limits.h>
 #ifdef HAVE_LIBGEN_H
 # include <libgen.h>
 #endif
 #include <signal.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
 
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
 #include <netinet/ip.h>
 #include <netinet/tcp.h>
 #include <arpa/inet.h>
 
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <netdb.h>
 #ifdef HAVE_PATHS_H
 # include <paths.h>
 #include <pwd.h>
 #endif
 #ifdef SSH_TUN_OPENBSD
 #include <net/if.h>
 #endif
 
 #include "xmalloc.h"
 #include "misc.h"
 #include "log.h"
 #include "ssh.h"
 #include "sshbuf.h"
 #include "ssherr.h"
 #include "platform.h"
 
 /* remove newline at end of string */
 char *
 chop(char *s)
 {
 	char *t = s;
 	while (*t) {
 		if (*t == '\n' || *t == '\r') {
 			*t = '\0';
 			return s;
 		}
 		t++;
 	}
 	return s;
 
 }
 
 /* set/unset filedescriptor to non-blocking */
 int
 set_nonblock(int fd)
 {
 	int val;
 
 	val = fcntl(fd, F_GETFL);
 	if (val < 0) {
 		error("fcntl(%d, F_GETFL): %s", fd, strerror(errno));
 		return (-1);
 	}
 	if (val & O_NONBLOCK) {
 		debug3("fd %d is O_NONBLOCK", fd);
 		return (0);
 	}
 	debug2("fd %d setting O_NONBLOCK", fd);
 	val |= O_NONBLOCK;
 	if (fcntl(fd, F_SETFL, val) == -1) {
 		debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd,
 		    strerror(errno));
 		return (-1);
 	}
 	return (0);
 }
 
 int
 unset_nonblock(int fd)
 {
 	int val;
 
 	val = fcntl(fd, F_GETFL);
 	if (val < 0) {
 		error("fcntl(%d, F_GETFL): %s", fd, strerror(errno));
 		return (-1);
 	}
 	if (!(val & O_NONBLOCK)) {
 		debug3("fd %d is not O_NONBLOCK", fd);
 		return (0);
 	}
 	debug("fd %d clearing O_NONBLOCK", fd);
 	val &= ~O_NONBLOCK;
 	if (fcntl(fd, F_SETFL, val) == -1) {
 		debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s",
 		    fd, strerror(errno));
 		return (-1);
 	}
 	return (0);
 }
 
 const char *
 ssh_gai_strerror(int gaierr)
 {
 	if (gaierr == EAI_SYSTEM && errno != 0)
 		return strerror(errno);
 	return gai_strerror(gaierr);
 }
 
 /* disable nagle on socket */
 void
 set_nodelay(int fd)
 {
 	int opt;
 	socklen_t optlen;
 
 	optlen = sizeof opt;
 	if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) {
 		debug("getsockopt TCP_NODELAY: %.100s", strerror(errno));
 		return;
 	}
 	if (opt == 1) {
 		debug2("fd %d is TCP_NODELAY", fd);
 		return;
 	}
 	opt = 1;
 	debug2("fd %d setting TCP_NODELAY", fd);
 	if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1)
 		error("setsockopt TCP_NODELAY: %.100s", strerror(errno));
 }
 
 /* Allow local port reuse in TIME_WAIT */
 int
 set_reuseaddr(int fd)
 {
 	int on = 1;
 
 	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
 		error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno));
 		return -1;
 	}
 	return 0;
 }
 
 /* Get/set routing domain */
 char *
 get_rdomain(int fd)
 {
 #if defined(HAVE_SYS_GET_RDOMAIN)
 	return sys_get_rdomain(fd);
 #elif defined(__OpenBSD__)
 	int rtable;
 	char *ret;
 	socklen_t len = sizeof(rtable);
 
 	if (getsockopt(fd, SOL_SOCKET, SO_RTABLE, &rtable, &len) == -1) {
 		error("Failed to get routing domain for fd %d: %s",
 		    fd, strerror(errno));
 		return NULL;
 	}
 	xasprintf(&ret, "%d", rtable);
 	return ret;
 #else /* defined(__OpenBSD__) */
 	return NULL;
 #endif
 }
 
 int
 set_rdomain(int fd, const char *name)
 {
 #if defined(HAVE_SYS_SET_RDOMAIN)
 	return sys_set_rdomain(fd, name);
 #elif defined(__OpenBSD__)
 	int rtable;
 	const char *errstr;
 
 	if (name == NULL)
 		return 0; /* default table */
 
 	rtable = (int)strtonum(name, 0, 255, &errstr);
 	if (errstr != NULL) {
 		/* Shouldn't happen */
 		error("Invalid routing domain \"%s\": %s", name, errstr);
 		return -1;
 	}
 	if (setsockopt(fd, SOL_SOCKET, SO_RTABLE,
 	    &rtable, sizeof(rtable)) == -1) {
 		error("Failed to set routing domain %d on fd %d: %s",
 		    rtable, fd, strerror(errno));
 		return -1;
 	}
 	return 0;
 #else /* defined(__OpenBSD__) */
 	error("Setting routing domain is not supported on this platform");
 	return -1;
 #endif
 }
 
 /* Characters considered whitespace in strsep calls. */
 #define WHITESPACE " \t\r\n"
 #define QUOTE	"\""
 
 /* return next token in configuration line */
 static char *
 strdelim_internal(char **s, int split_equals)
 {
 	char *old;
 	int wspace = 0;
 
 	if (*s == NULL)
 		return NULL;
 
 	old = *s;
 
 	*s = strpbrk(*s,
 	    split_equals ? WHITESPACE QUOTE "=" : WHITESPACE QUOTE);
 	if (*s == NULL)
 		return (old);
 
 	if (*s[0] == '\"') {
 		memmove(*s, *s + 1, strlen(*s)); /* move nul too */
 		/* Find matching quote */
 		if ((*s = strpbrk(*s, QUOTE)) == NULL) {
 			return (NULL);		/* no matching quote */
 		} else {
 			*s[0] = '\0';
 			*s += strspn(*s + 1, WHITESPACE) + 1;
 			return (old);
 		}
 	}
 
 	/* Allow only one '=' to be skipped */
 	if (split_equals && *s[0] == '=')
 		wspace = 1;
 	*s[0] = '\0';
 
 	/* Skip any extra whitespace after first token */
 	*s += strspn(*s + 1, WHITESPACE) + 1;
 	if (split_equals && *s[0] == '=' && !wspace)
 		*s += strspn(*s + 1, WHITESPACE) + 1;
 
 	return (old);
 }
 
 /*
  * Return next token in configuration line; splts on whitespace or a
  * single '=' character.
  */
 char *
 strdelim(char **s)
 {
 	return strdelim_internal(s, 1);
 }
 
 /*
  * Return next token in configuration line; splts on whitespace only.
  */
 char *
 strdelimw(char **s)
 {
 	return strdelim_internal(s, 0);
 }
 
 struct passwd *
 pwcopy(struct passwd *pw)
 {
 	struct passwd *copy = xcalloc(1, sizeof(*copy));
 
 	copy->pw_name = xstrdup(pw->pw_name);
 	copy->pw_passwd = xstrdup(pw->pw_passwd);
 #ifdef HAVE_STRUCT_PASSWD_PW_GECOS
 	copy->pw_gecos = xstrdup(pw->pw_gecos);
 #endif
 	copy->pw_uid = pw->pw_uid;
 	copy->pw_gid = pw->pw_gid;
 #ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
 	copy->pw_expire = pw->pw_expire;
 #endif
 #ifdef HAVE_STRUCT_PASSWD_PW_CHANGE
 	copy->pw_change = pw->pw_change;
 #endif
 #ifdef HAVE_STRUCT_PASSWD_PW_CLASS
 	copy->pw_class = xstrdup(pw->pw_class);
 #endif
 	copy->pw_dir = xstrdup(pw->pw_dir);
 	copy->pw_shell = xstrdup(pw->pw_shell);
 	return copy;
 }
 
 /*
  * Convert ASCII string to TCP/IP port number.
  * Port must be >=0 and <=65535.
  * Return -1 if invalid.
  */
 int
 a2port(const char *s)
 {
 	struct servent *se;
 	long long port;
 	const char *errstr;
 
 	port = strtonum(s, 0, 65535, &errstr);
 	if (errstr == NULL)
 		return (int)port;
 	if ((se = getservbyname(s, "tcp")) != NULL)
 		return ntohs(se->s_port);
 	return -1;
 }
 
 int
 a2tun(const char *s, int *remote)
 {
 	const char *errstr = NULL;
 	char *sp, *ep;
 	int tun;
 
 	if (remote != NULL) {
 		*remote = SSH_TUNID_ANY;
 		sp = xstrdup(s);
 		if ((ep = strchr(sp, ':')) == NULL) {
 			free(sp);
 			return (a2tun(s, NULL));
 		}
 		ep[0] = '\0'; ep++;
 		*remote = a2tun(ep, NULL);
 		tun = a2tun(sp, NULL);
 		free(sp);
 		return (*remote == SSH_TUNID_ERR ? *remote : tun);
 	}
 
 	if (strcasecmp(s, "any") == 0)
 		return (SSH_TUNID_ANY);
 
 	tun = strtonum(s, 0, SSH_TUNID_MAX, &errstr);
 	if (errstr != NULL)
 		return (SSH_TUNID_ERR);
 
 	return (tun);
 }
 
 #define SECONDS		1
 #define MINUTES		(SECONDS * 60)
 #define HOURS		(MINUTES * 60)
 #define DAYS		(HOURS * 24)
 #define WEEKS		(DAYS * 7)
 
 /*
  * Convert a time string into seconds; format is
  * a sequence of:
  *      time[qualifier]
  *
  * Valid time qualifiers are:
  *      <none>  seconds
  *      s|S     seconds
  *      m|M     minutes
  *      h|H     hours
  *      d|D     days
  *      w|W     weeks
  *
  * Examples:
  *      90m     90 minutes
  *      1h30m   90 minutes
  *      2d      2 days
  *      1w      1 week
  *
  * Return -1 if time string is invalid.
  */
 long
 convtime(const char *s)
 {
 	long total, secs, multiplier = 1;
 	const char *p;
 	char *endp;
 
 	errno = 0;
 	total = 0;
 	p = s;
 
 	if (p == NULL || *p == '\0')
 		return -1;
 
 	while (*p) {
 		secs = strtol(p, &endp, 10);
 		if (p == endp ||
 		    (errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) ||
 		    secs < 0)
 			return -1;
 
 		switch (*endp++) {
 		case '\0':
 			endp--;
 			break;
 		case 's':
 		case 'S':
 			break;
 		case 'm':
 		case 'M':
 			multiplier = MINUTES;
 			break;
 		case 'h':
 		case 'H':
 			multiplier = HOURS;
 			break;
 		case 'd':
 		case 'D':
 			multiplier = DAYS;
 			break;
 		case 'w':
 		case 'W':
 			multiplier = WEEKS;
 			break;
 		default:
 			return -1;
 		}
 		if (secs >= LONG_MAX / multiplier)
 			return -1;
 		secs *= multiplier;
 		if  (total >= LONG_MAX - secs)
 			return -1;
 		total += secs;
 		if (total < 0)
 			return -1;
 		p = endp;
 	}
 
 	return total;
 }
 
 /*
  * Returns a standardized host+port identifier string.
  * Caller must free returned string.
  */
 char *
 put_host_port(const char *host, u_short port)
 {
 	char *hoststr;
 
 	if (port == 0 || port == SSH_DEFAULT_PORT)
 		return(xstrdup(host));
 	if (asprintf(&hoststr, "[%s]:%d", host, (int)port) < 0)
 		fatal("put_host_port: asprintf: %s", strerror(errno));
 	debug3("put_host_port: %s", hoststr);
 	return hoststr;
 }
 
 /*
  * Search for next delimiter between hostnames/addresses and ports.
  * Argument may be modified (for termination).
  * Returns *cp if parsing succeeds.
  * *cp is set to the start of the next field, if one was found.
  * The delimiter char, if present, is stored in delim.
  * If this is the last field, *cp is set to NULL.
  */
 static char *
 hpdelim2(char **cp, char *delim)
 {
 	char *s, *old;
 
 	if (cp == NULL || *cp == NULL)
 		return NULL;
 
 	old = s = *cp;
 	if (*s == '[') {
 		if ((s = strchr(s, ']')) == NULL)
 			return NULL;
 		else
 			s++;
 	} else if ((s = strpbrk(s, ":/")) == NULL)
 		s = *cp + strlen(*cp); /* skip to end (see first case below) */
 
 	switch (*s) {
 	case '\0':
 		*cp = NULL;	/* no more fields*/
 		break;
 
 	case ':':
 	case '/':
 		if (delim != NULL)
 			*delim = *s;
 		*s = '\0';	/* terminate */
 		*cp = s + 1;
 		break;
 
 	default:
 		return NULL;
 	}
 
 	return old;
 }
 
 char *
 hpdelim(char **cp)
 {
 	return hpdelim2(cp, NULL);
 }
 
 char *
 cleanhostname(char *host)
 {
 	if (*host == '[' && host[strlen(host) - 1] == ']') {
 		host[strlen(host) - 1] = '\0';
 		return (host + 1);
 	} else
 		return host;
 }
 
 char *
 colon(char *cp)
 {
 	int flag = 0;
 
 	if (*cp == ':')		/* Leading colon is part of file name. */
 		return NULL;
 	if (*cp == '[')
 		flag = 1;
 
 	for (; *cp; ++cp) {
 		if (*cp == '@' && *(cp+1) == '[')
 			flag = 1;
 		if (*cp == ']' && *(cp+1) == ':' && flag)
 			return (cp+1);
 		if (*cp == ':' && !flag)
 			return (cp);
 		if (*cp == '/')
 			return NULL;
 	}
 	return NULL;
 }
 
 /*
  * Parse a [user@]host:[path] string.
  * Caller must free returned user, host and path.
  * Any of the pointer return arguments may be NULL (useful for syntax checking).
  * If user was not specified then *userp will be set to NULL.
  * If host was not specified then *hostp will be set to NULL.
  * If path was not specified then *pathp will be set to ".".
  * Returns 0 on success, -1 on failure.
  */
 int
 parse_user_host_path(const char *s, char **userp, char **hostp, char **pathp)
 {
 	char *user = NULL, *host = NULL, *path = NULL;
 	char *sdup, *tmp;
 	int ret = -1;
 
 	if (userp != NULL)
 		*userp = NULL;
 	if (hostp != NULL)
 		*hostp = NULL;
 	if (pathp != NULL)
 		*pathp = NULL;
 
 	sdup = xstrdup(s);
 
 	/* Check for remote syntax: [user@]host:[path] */
 	if ((tmp = colon(sdup)) == NULL)
 		goto out;
 
 	/* Extract optional path */
 	*tmp++ = '\0';
 	if (*tmp == '\0')
 		tmp = ".";
 	path = xstrdup(tmp);
 
 	/* Extract optional user and mandatory host */
 	tmp = strrchr(sdup, '@');
 	if (tmp != NULL) {
 		*tmp++ = '\0';
 		host = xstrdup(cleanhostname(tmp));
 		if (*sdup != '\0')
 			user = xstrdup(sdup);
 	} else {
 		host = xstrdup(cleanhostname(sdup));
 		user = NULL;
 	}
 
 	/* Success */
 	if (userp != NULL) {
 		*userp = user;
 		user = NULL;
 	}
 	if (hostp != NULL) {
 		*hostp = host;
 		host = NULL;
 	}
 	if (pathp != NULL) {
 		*pathp = path;
 		path = NULL;
 	}
 	ret = 0;
 out:
 	free(sdup);
 	free(user);
 	free(host);
 	free(path);
 	return ret;
 }
 
 /*
  * Parse a [user@]host[:port] string.
  * Caller must free returned user and host.
  * Any of the pointer return arguments may be NULL (useful for syntax checking).
  * If user was not specified then *userp will be set to NULL.
  * If port was not specified then *portp will be -1.
  * Returns 0 on success, -1 on failure.
  */
 int
 parse_user_host_port(const char *s, char **userp, char **hostp, int *portp)
 {
 	char *sdup, *cp, *tmp;
 	char *user = NULL, *host = NULL;
 	int port = -1, ret = -1;
 
 	if (userp != NULL)
 		*userp = NULL;
 	if (hostp != NULL)
 		*hostp = NULL;
 	if (portp != NULL)
 		*portp = -1;
 
 	if ((sdup = tmp = strdup(s)) == NULL)
 		return -1;
 	/* Extract optional username */
 	if ((cp = strrchr(tmp, '@')) != NULL) {
 		*cp = '\0';
 		if (*tmp == '\0')
 			goto out;
 		if ((user = strdup(tmp)) == NULL)
 			goto out;
 		tmp = cp + 1;
 	}
 	/* Extract mandatory hostname */
 	if ((cp = hpdelim(&tmp)) == NULL || *cp == '\0')
 		goto out;
 	host = xstrdup(cleanhostname(cp));
 	/* Convert and verify optional port */
 	if (tmp != NULL && *tmp != '\0') {
 		if ((port = a2port(tmp)) <= 0)
 			goto out;
 	}
 	/* Success */
 	if (userp != NULL) {
 		*userp = user;
 		user = NULL;
 	}
 	if (hostp != NULL) {
 		*hostp = host;
 		host = NULL;
 	}
 	if (portp != NULL)
 		*portp = port;
 	ret = 0;
  out:
 	free(sdup);
 	free(user);
 	free(host);
 	return ret;
 }
 
 /*
  * Converts a two-byte hex string to decimal.
  * Returns the decimal value or -1 for invalid input.
  */
 static int
 hexchar(const char *s)
 {
 	unsigned char result[2];
 	int i;
 
 	for (i = 0; i < 2; i++) {
 		if (s[i] >= '0' && s[i] <= '9')
 			result[i] = (unsigned char)(s[i] - '0');
 		else if (s[i] >= 'a' && s[i] <= 'f')
 			result[i] = (unsigned char)(s[i] - 'a') + 10;
 		else if (s[i] >= 'A' && s[i] <= 'F')
 			result[i] = (unsigned char)(s[i] - 'A') + 10;
 		else
 			return -1;
 	}
 	return (result[0] << 4) | result[1];
 }
 
 /*
  * Decode an url-encoded string.
  * Returns a newly allocated string on success or NULL on failure.
  */
 static char *
 urldecode(const char *src)
 {
 	char *ret, *dst;
 	int ch;
 
 	ret = xmalloc(strlen(src) + 1);
 	for (dst = ret; *src != '\0'; src++) {
 		switch (*src) {
 		case '+':
 			*dst++ = ' ';
 			break;
 		case '%':
 			if (!isxdigit((unsigned char)src[1]) ||
 			    !isxdigit((unsigned char)src[2]) ||
 			    (ch = hexchar(src + 1)) == -1) {
 				free(ret);
 				return NULL;
 			}
 			*dst++ = ch;
 			src += 2;
 			break;
 		default:
 			*dst++ = *src;
 			break;
 		}
 	}
 	*dst = '\0';
 
 	return ret;
 }
 
 /*
  * Parse an (scp|ssh|sftp)://[user@]host[:port][/path] URI.
  * See https://tools.ietf.org/html/draft-ietf-secsh-scp-sftp-ssh-uri-04
  * Either user or path may be url-encoded (but not host or port).
  * Caller must free returned user, host and path.
  * Any of the pointer return arguments may be NULL (useful for syntax checking)
  * but the scheme must always be specified.
  * If user was not specified then *userp will be set to NULL.
  * If port was not specified then *portp will be -1.
  * If path was not specified then *pathp will be set to NULL.
  * Returns 0 on success, 1 if non-uri/wrong scheme, -1 on error/invalid uri.
  */
 int
 parse_uri(const char *scheme, const char *uri, char **userp, char **hostp,
     int *portp, char **pathp)
 {
 	char *uridup, *cp, *tmp, ch;
 	char *user = NULL, *host = NULL, *path = NULL;
 	int port = -1, ret = -1;
 	size_t len;
 
 	len = strlen(scheme);
 	if (strncmp(uri, scheme, len) != 0 || strncmp(uri + len, "://", 3) != 0)
 		return 1;
 	uri += len + 3;
 
 	if (userp != NULL)
 		*userp = NULL;
 	if (hostp != NULL)
 		*hostp = NULL;
 	if (portp != NULL)
 		*portp = -1;
 	if (pathp != NULL)
 		*pathp = NULL;
 
 	uridup = tmp = xstrdup(uri);
 
 	/* Extract optional ssh-info (username + connection params) */
 	if ((cp = strchr(tmp, '@')) != NULL) {
 		char *delim;
 
 		*cp = '\0';
 		/* Extract username and connection params */
 		if ((delim = strchr(tmp, ';')) != NULL) {
 			/* Just ignore connection params for now */
 			*delim = '\0';
 		}
 		if (*tmp == '\0') {
 			/* Empty username */
 			goto out;
 		}
 		if ((user = urldecode(tmp)) == NULL)
 			goto out;
 		tmp = cp + 1;
 	}
 
 	/* Extract mandatory hostname */
 	if ((cp = hpdelim2(&tmp, &ch)) == NULL || *cp == '\0')
 		goto out;
 	host = xstrdup(cleanhostname(cp));
 	if (!valid_domain(host, 0, NULL))
 		goto out;
 
 	if (tmp != NULL && *tmp != '\0') {
 		if (ch == ':') {
 			/* Convert and verify port. */
 			if ((cp = strchr(tmp, '/')) != NULL)
 				*cp = '\0';
 			if ((port = a2port(tmp)) <= 0)
 				goto out;
 			tmp = cp ? cp + 1 : NULL;
 		}
 		if (tmp != NULL && *tmp != '\0') {
 			/* Extract optional path */
 			if ((path = urldecode(tmp)) == NULL)
 				goto out;
 		}
 	}
 
 	/* Success */
 	if (userp != NULL) {
 		*userp = user;
 		user = NULL;
 	}
 	if (hostp != NULL) {
 		*hostp = host;
 		host = NULL;
 	}
 	if (portp != NULL)
 		*portp = port;
 	if (pathp != NULL) {
 		*pathp = path;
 		path = NULL;
 	}
 	ret = 0;
  out:
 	free(uridup);
 	free(user);
 	free(host);
 	free(path);
 	return ret;
 }
 
 /* function to assist building execv() arguments */
 void
 addargs(arglist *args, char *fmt, ...)
 {
 	va_list ap;
 	char *cp;
 	u_int nalloc;
 	int r;
 
 	va_start(ap, fmt);
 	r = vasprintf(&cp, fmt, ap);
 	va_end(ap);
 	if (r == -1)
 		fatal("addargs: argument too long");
 
 	nalloc = args->nalloc;
 	if (args->list == NULL) {
 		nalloc = 32;
 		args->num = 0;
 	} else if (args->num+2 >= nalloc)
 		nalloc *= 2;
 
 	args->list = xrecallocarray(args->list, args->nalloc, nalloc, sizeof(char *));
 	args->nalloc = nalloc;
 	args->list[args->num++] = cp;
 	args->list[args->num] = NULL;
 }
 
 void
 replacearg(arglist *args, u_int which, char *fmt, ...)
 {
 	va_list ap;
 	char *cp;
 	int r;
 
 	va_start(ap, fmt);
 	r = vasprintf(&cp, fmt, ap);
 	va_end(ap);
 	if (r == -1)
 		fatal("replacearg: argument too long");
 
 	if (which >= args->num)
 		fatal("replacearg: tried to replace invalid arg %d >= %d",
 		    which, args->num);
 	free(args->list[which]);
 	args->list[which] = cp;
 }
 
 void
 freeargs(arglist *args)
 {
 	u_int i;
 
 	if (args->list != NULL) {
 		for (i = 0; i < args->num; i++)
 			free(args->list[i]);
 		free(args->list);
 		args->nalloc = args->num = 0;
 		args->list = NULL;
 	}
 }
 
 /*
  * Expands tildes in the file name.  Returns data allocated by xmalloc.
  * Warning: this calls getpw*.
  */
 char *
 tilde_expand_filename(const char *filename, uid_t uid)
 {
 	const char *path, *sep;
 	char user[128], *ret;
 	struct passwd *pw;
 	u_int len, slash;
 
 	if (*filename != '~')
 		return (xstrdup(filename));
 	filename++;
 
 	path = strchr(filename, '/');
 	if (path != NULL && path > filename) {		/* ~user/path */
 		slash = path - filename;
 		if (slash > sizeof(user) - 1)
 			fatal("tilde_expand_filename: ~username too long");
 		memcpy(user, filename, slash);
 		user[slash] = '\0';
 		if ((pw = getpwnam(user)) == NULL)
 			fatal("tilde_expand_filename: No such user %s", user);
 	} else if ((pw = getpwuid(uid)) == NULL)	/* ~/path */
 		fatal("tilde_expand_filename: No such uid %ld", (long)uid);
 
 	/* Make sure directory has a trailing '/' */
 	len = strlen(pw->pw_dir);
 	if (len == 0 || pw->pw_dir[len - 1] != '/')
 		sep = "/";
 	else
 		sep = "";
 
 	/* Skip leading '/' from specified path */
 	if (path != NULL)
 		filename = path + 1;
 
 	if (xasprintf(&ret, "%s%s%s", pw->pw_dir, sep, filename) >= PATH_MAX)
 		fatal("tilde_expand_filename: Path too long");
 
 	return (ret);
 }
 
 /*
  * Expand a string with a set of %[char] escapes. A number of escapes may be
  * specified as (char *escape_chars, char *replacement) pairs. The list must
  * be terminated by a NULL escape_char. Returns replaced string in memory
  * allocated by xmalloc.
  */
 char *
 percent_expand(const char *string, ...)
 {
 #define EXPAND_MAX_KEYS	16
 	u_int num_keys, i, j;
 	struct {
 		const char *key;
 		const char *repl;
 	} keys[EXPAND_MAX_KEYS];
 	char buf[4096];
 	va_list ap;
 
 	/* Gather keys */
 	va_start(ap, string);
 	for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) {
 		keys[num_keys].key = va_arg(ap, char *);
 		if (keys[num_keys].key == NULL)
 			break;
 		keys[num_keys].repl = va_arg(ap, char *);
 		if (keys[num_keys].repl == NULL)
 			fatal("%s: NULL replacement", __func__);
 	}
 	if (num_keys == EXPAND_MAX_KEYS && va_arg(ap, char *) != NULL)
 		fatal("%s: too many keys", __func__);
 	va_end(ap);
 
 	/* Expand string */
 	*buf = '\0';
 	for (i = 0; *string != '\0'; string++) {
 		if (*string != '%') {
  append:
 			buf[i++] = *string;
 			if (i >= sizeof(buf))
 				fatal("%s: string too long", __func__);
 			buf[i] = '\0';
 			continue;
 		}
 		string++;
 		/* %% case */
 		if (*string == '%')
 			goto append;
 		if (*string == '\0')
 			fatal("%s: invalid format", __func__);
 		for (j = 0; j < num_keys; j++) {
 			if (strchr(keys[j].key, *string) != NULL) {
 				i = strlcat(buf, keys[j].repl, sizeof(buf));
 				if (i >= sizeof(buf))
 					fatal("%s: string too long", __func__);
 				break;
 			}
 		}
 		if (j >= num_keys)
 			fatal("%s: unknown key %%%c", __func__, *string);
 	}
 	return (xstrdup(buf));
 #undef EXPAND_MAX_KEYS
 }
 
 int
 tun_open(int tun, int mode, char **ifname)
 {
 #if defined(CUSTOM_SYS_TUN_OPEN)
 	return (sys_tun_open(tun, mode, ifname));
 #elif defined(SSH_TUN_OPENBSD)
 	struct ifreq ifr;
 	char name[100];
 	int fd = -1, sock;
 	const char *tunbase = "tun";
 
 	if (ifname != NULL)
 		*ifname = NULL;
 
 	if (mode == SSH_TUNMODE_ETHERNET)
 		tunbase = "tap";
 
 	/* Open the tunnel device */
 	if (tun <= SSH_TUNID_MAX) {
 		snprintf(name, sizeof(name), "/dev/%s%d", tunbase, tun);
 		fd = open(name, O_RDWR);
 	} else if (tun == SSH_TUNID_ANY) {
 		for (tun = 100; tun >= 0; tun--) {
 			snprintf(name, sizeof(name), "/dev/%s%d",
 			    tunbase, tun);
 			if ((fd = open(name, O_RDWR)) >= 0)
 				break;
 		}
 	} else {
 		debug("%s: invalid tunnel %u", __func__, tun);
 		return -1;
 	}
 
 	if (fd < 0) {
 		debug("%s: %s open: %s", __func__, name, strerror(errno));
 		return -1;
 	}
 
 	debug("%s: %s mode %d fd %d", __func__, name, mode, fd);
 
 	/* Bring interface up if it is not already */
 	snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", tunbase, tun);
 	if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
 		goto failed;
 
 	if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) {
 		debug("%s: get interface %s flags: %s", __func__,
 		    ifr.ifr_name, strerror(errno));
 		goto failed;
 	}
 
 	if (!(ifr.ifr_flags & IFF_UP)) {
 		ifr.ifr_flags |= IFF_UP;
 		if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) {
 			debug("%s: activate interface %s: %s", __func__,
 			    ifr.ifr_name, strerror(errno));
 			goto failed;
 		}
 	}
 
 	if (ifname != NULL)
 		*ifname = xstrdup(ifr.ifr_name);
 
 	close(sock);
 	return fd;
 
  failed:
 	if (fd >= 0)
 		close(fd);
 	if (sock >= 0)
 		close(sock);
 	return -1;
 #else
 	error("Tunnel interfaces are not supported on this platform");
 	return (-1);
 #endif
 }
 
 void
 sanitise_stdfd(void)
 {
 	int nullfd, dupfd;
 
 	if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
 		fprintf(stderr, "Couldn't open /dev/null: %s\n",
 		    strerror(errno));
 		exit(1);
 	}
 	while (++dupfd <= STDERR_FILENO) {
 		/* Only populate closed fds. */
 		if (fcntl(dupfd, F_GETFL) == -1 && errno == EBADF) {
 			if (dup2(nullfd, dupfd) == -1) {
 				fprintf(stderr, "dup2: %s\n", strerror(errno));
 				exit(1);
 			}
 		}
 	}
 	if (nullfd > STDERR_FILENO)
 		close(nullfd);
 }
 
 char *
 tohex(const void *vp, size_t l)
 {
 	const u_char *p = (const u_char *)vp;
 	char b[3], *r;
 	size_t i, hl;
 
 	if (l > 65536)
 		return xstrdup("tohex: length > 65536");
 
 	hl = l * 2 + 1;
 	r = xcalloc(1, hl);
 	for (i = 0; i < l; i++) {
 		snprintf(b, sizeof(b), "%02x", p[i]);
 		strlcat(r, b, hl);
 	}
 	return (r);
 }
 
 u_int64_t
 get_u64(const void *vp)
 {
 	const u_char *p = (const u_char *)vp;
 	u_int64_t v;
 
 	v  = (u_int64_t)p[0] << 56;
 	v |= (u_int64_t)p[1] << 48;
 	v |= (u_int64_t)p[2] << 40;
 	v |= (u_int64_t)p[3] << 32;
 	v |= (u_int64_t)p[4] << 24;
 	v |= (u_int64_t)p[5] << 16;
 	v |= (u_int64_t)p[6] << 8;
 	v |= (u_int64_t)p[7];
 
 	return (v);
 }
 
 u_int32_t
 get_u32(const void *vp)
 {
 	const u_char *p = (const u_char *)vp;
 	u_int32_t v;
 
 	v  = (u_int32_t)p[0] << 24;
 	v |= (u_int32_t)p[1] << 16;
 	v |= (u_int32_t)p[2] << 8;
 	v |= (u_int32_t)p[3];
 
 	return (v);
 }
 
 u_int32_t
 get_u32_le(const void *vp)
 {
 	const u_char *p = (const u_char *)vp;
 	u_int32_t v;
 
 	v  = (u_int32_t)p[0];
 	v |= (u_int32_t)p[1] << 8;
 	v |= (u_int32_t)p[2] << 16;
 	v |= (u_int32_t)p[3] << 24;
 
 	return (v);
 }
 
 u_int16_t
 get_u16(const void *vp)
 {
 	const u_char *p = (const u_char *)vp;
 	u_int16_t v;
 
 	v  = (u_int16_t)p[0] << 8;
 	v |= (u_int16_t)p[1];
 
 	return (v);
 }
 
 void
 put_u64(void *vp, u_int64_t v)
 {
 	u_char *p = (u_char *)vp;
 
 	p[0] = (u_char)(v >> 56) & 0xff;
 	p[1] = (u_char)(v >> 48) & 0xff;
 	p[2] = (u_char)(v >> 40) & 0xff;
 	p[3] = (u_char)(v >> 32) & 0xff;
 	p[4] = (u_char)(v >> 24) & 0xff;
 	p[5] = (u_char)(v >> 16) & 0xff;
 	p[6] = (u_char)(v >> 8) & 0xff;
 	p[7] = (u_char)v & 0xff;
 }
 
 void
 put_u32(void *vp, u_int32_t v)
 {
 	u_char *p = (u_char *)vp;
 
 	p[0] = (u_char)(v >> 24) & 0xff;
 	p[1] = (u_char)(v >> 16) & 0xff;
 	p[2] = (u_char)(v >> 8) & 0xff;
 	p[3] = (u_char)v & 0xff;
 }
 
 void
 put_u32_le(void *vp, u_int32_t v)
 {
 	u_char *p = (u_char *)vp;
 
 	p[0] = (u_char)v & 0xff;
 	p[1] = (u_char)(v >> 8) & 0xff;
 	p[2] = (u_char)(v >> 16) & 0xff;
 	p[3] = (u_char)(v >> 24) & 0xff;
 }
 
 void
 put_u16(void *vp, u_int16_t v)
 {
 	u_char *p = (u_char *)vp;
 
 	p[0] = (u_char)(v >> 8) & 0xff;
 	p[1] = (u_char)v & 0xff;
 }
 
 void
 ms_subtract_diff(struct timeval *start, int *ms)
 {
 	struct timeval diff, finish;
 
 	monotime_tv(&finish);
 	timersub(&finish, start, &diff);
 	*ms -= (diff.tv_sec * 1000) + (diff.tv_usec / 1000);
 }
 
 void
 ms_to_timeval(struct timeval *tv, int ms)
 {
 	if (ms < 0)
 		ms = 0;
 	tv->tv_sec = ms / 1000;
 	tv->tv_usec = (ms % 1000) * 1000;
 }
 
 void
 monotime_ts(struct timespec *ts)
 {
 	struct timeval tv;
 #if defined(HAVE_CLOCK_GETTIME) && (defined(CLOCK_BOOTTIME) || \
     defined(CLOCK_MONOTONIC) || defined(CLOCK_REALTIME))
 	static int gettime_failed = 0;
 
 	if (!gettime_failed) {
 # ifdef CLOCK_BOOTTIME
 		if (clock_gettime(CLOCK_BOOTTIME, ts) == 0)
 			return;
 # endif /* CLOCK_BOOTTIME */
 # ifdef CLOCK_MONOTONIC
 		if (clock_gettime(CLOCK_MONOTONIC, ts) == 0)
 			return;
 # endif /* CLOCK_MONOTONIC */
 # ifdef CLOCK_REALTIME
 		/* Not monotonic, but we're almost out of options here. */
 		if (clock_gettime(CLOCK_REALTIME, ts) == 0)
 			return;
 # endif /* CLOCK_REALTIME */
 		debug3("clock_gettime: %s", strerror(errno));
 		gettime_failed = 1;
 	}
 #endif /* HAVE_CLOCK_GETTIME && (BOOTTIME || MONOTONIC || REALTIME) */
 	gettimeofday(&tv, NULL);
 	ts->tv_sec = tv.tv_sec;
 	ts->tv_nsec = (long)tv.tv_usec * 1000;
 }
 
 void
 monotime_tv(struct timeval *tv)
 {
 	struct timespec ts;
 
 	monotime_ts(&ts);
 	tv->tv_sec = ts.tv_sec;
 	tv->tv_usec = ts.tv_nsec / 1000;
 }
 
 time_t
 monotime(void)
 {
 	struct timespec ts;
 
 	monotime_ts(&ts);
 	return ts.tv_sec;
 }
 
 double
 monotime_double(void)
 {
 	struct timespec ts;
 
 	monotime_ts(&ts);
 	return ts.tv_sec + ((double)ts.tv_nsec / 1000000000);
 }
 
 void
 bandwidth_limit_init(struct bwlimit *bw, u_int64_t kbps, size_t buflen)
 {
 	bw->buflen = buflen;
 	bw->rate = kbps;
 	bw->thresh = bw->rate;
 	bw->lamt = 0;
 	timerclear(&bw->bwstart);
 	timerclear(&bw->bwend);
 }	
 
 /* Callback from read/write loop to insert bandwidth-limiting delays */
 void
 bandwidth_limit(struct bwlimit *bw, size_t read_len)
 {
 	u_int64_t waitlen;
 	struct timespec ts, rm;
 
 	if (!timerisset(&bw->bwstart)) {
 		monotime_tv(&bw->bwstart);
 		return;
 	}
 
 	bw->lamt += read_len;
 	if (bw->lamt < bw->thresh)
 		return;
 
 	monotime_tv(&bw->bwend);
 	timersub(&bw->bwend, &bw->bwstart, &bw->bwend);
 	if (!timerisset(&bw->bwend))
 		return;
 
 	bw->lamt *= 8;
 	waitlen = (double)1000000L * bw->lamt / bw->rate;
 
 	bw->bwstart.tv_sec = waitlen / 1000000L;
 	bw->bwstart.tv_usec = waitlen % 1000000L;
 
 	if (timercmp(&bw->bwstart, &bw->bwend, >)) {
 		timersub(&bw->bwstart, &bw->bwend, &bw->bwend);
 
 		/* Adjust the wait time */
 		if (bw->bwend.tv_sec) {
 			bw->thresh /= 2;
 			if (bw->thresh < bw->buflen / 4)
 				bw->thresh = bw->buflen / 4;
 		} else if (bw->bwend.tv_usec < 10000) {
 			bw->thresh *= 2;
 			if (bw->thresh > bw->buflen * 8)
 				bw->thresh = bw->buflen * 8;
 		}
 
 		TIMEVAL_TO_TIMESPEC(&bw->bwend, &ts);
 		while (nanosleep(&ts, &rm) == -1) {
 			if (errno != EINTR)
 				break;
 			ts = rm;
 		}
 	}
 
 	bw->lamt = 0;
 	monotime_tv(&bw->bwstart);
 }
 
 /* Make a template filename for mk[sd]temp() */
 void
 mktemp_proto(char *s, size_t len)
 {
 	const char *tmpdir;
 	int r;
 
 	if ((tmpdir = getenv("TMPDIR")) != NULL) {
 		r = snprintf(s, len, "%s/ssh-XXXXXXXXXXXX", tmpdir);
 		if (r > 0 && (size_t)r < len)
 			return;
 	}
 	r = snprintf(s, len, "/tmp/ssh-XXXXXXXXXXXX");
 	if (r < 0 || (size_t)r >= len)
 		fatal("%s: template string too short", __func__);
 }
 
 static const struct {
 	const char *name;
 	int value;
 } ipqos[] = {
 	{ "none", INT_MAX },		/* can't use 0 here; that's CS0 */
 	{ "af11", IPTOS_DSCP_AF11 },
 	{ "af12", IPTOS_DSCP_AF12 },
 	{ "af13", IPTOS_DSCP_AF13 },
 	{ "af21", IPTOS_DSCP_AF21 },
 	{ "af22", IPTOS_DSCP_AF22 },
 	{ "af23", IPTOS_DSCP_AF23 },
 	{ "af31", IPTOS_DSCP_AF31 },
 	{ "af32", IPTOS_DSCP_AF32 },
 	{ "af33", IPTOS_DSCP_AF33 },
 	{ "af41", IPTOS_DSCP_AF41 },
 	{ "af42", IPTOS_DSCP_AF42 },
 	{ "af43", IPTOS_DSCP_AF43 },
 	{ "cs0", IPTOS_DSCP_CS0 },
 	{ "cs1", IPTOS_DSCP_CS1 },
 	{ "cs2", IPTOS_DSCP_CS2 },
 	{ "cs3", IPTOS_DSCP_CS3 },
 	{ "cs4", IPTOS_DSCP_CS4 },
 	{ "cs5", IPTOS_DSCP_CS5 },
 	{ "cs6", IPTOS_DSCP_CS6 },
 	{ "cs7", IPTOS_DSCP_CS7 },
 	{ "ef", IPTOS_DSCP_EF },
 	{ "lowdelay", IPTOS_LOWDELAY },
 	{ "throughput", IPTOS_THROUGHPUT },
 	{ "reliability", IPTOS_RELIABILITY },
 	{ NULL, -1 }
 };
 
 int
 parse_ipqos(const char *cp)
 {
 	u_int i;
 	char *ep;
 	long val;
 
 	if (cp == NULL)
 		return -1;
 	for (i = 0; ipqos[i].name != NULL; i++) {
 		if (strcasecmp(cp, ipqos[i].name) == 0)
 			return ipqos[i].value;
 	}
 	/* Try parsing as an integer */
 	val = strtol(cp, &ep, 0);
 	if (*cp == '\0' || *ep != '\0' || val < 0 || val > 255)
 		return -1;
 	return val;
 }
 
 const char *
 iptos2str(int iptos)
 {
 	int i;
 	static char iptos_str[sizeof "0xff"];
 
 	for (i = 0; ipqos[i].name != NULL; i++) {
 		if (ipqos[i].value == iptos)
 			return ipqos[i].name;
 	}
 	snprintf(iptos_str, sizeof iptos_str, "0x%02x", iptos);
 	return iptos_str;
 }
 
 void
 lowercase(char *s)
 {
 	for (; *s; s++)
 		*s = tolower((u_char)*s);
 }
 
 int
 unix_listener(const char *path, int backlog, int unlink_first)
 {
 	struct sockaddr_un sunaddr;
 	int saved_errno, sock;
 
 	memset(&sunaddr, 0, sizeof(sunaddr));
 	sunaddr.sun_family = AF_UNIX;
 	if (strlcpy(sunaddr.sun_path, path,
 	    sizeof(sunaddr.sun_path)) >= sizeof(sunaddr.sun_path)) {
 		error("%s: path \"%s\" too long for Unix domain socket",
 		    __func__, path);
 		errno = ENAMETOOLONG;
 		return -1;
 	}
 
 	sock = socket(PF_UNIX, SOCK_STREAM, 0);
 	if (sock < 0) {
 		saved_errno = errno;
 		error("%s: socket: %.100s", __func__, strerror(errno));
 		errno = saved_errno;
 		return -1;
 	}
 	if (unlink_first == 1) {
 		if (unlink(path) != 0 && errno != ENOENT)
 			error("unlink(%s): %.100s", path, strerror(errno));
 	}
 	if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) {
 		saved_errno = errno;
 		error("%s: cannot bind to path %s: %s",
 		    __func__, path, strerror(errno));
 		close(sock);
 		errno = saved_errno;
 		return -1;
 	}
 	if (listen(sock, backlog) < 0) {
 		saved_errno = errno;
 		error("%s: cannot listen on path %s: %s",
 		    __func__, path, strerror(errno));
 		close(sock);
 		unlink(path);
 		errno = saved_errno;
 		return -1;
 	}
 	return sock;
 }
 
 void
 sock_set_v6only(int s)
 {
 #if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
 	int on = 1;
 
 	debug3("%s: set socket %d IPV6_V6ONLY", __func__, s);
 	if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1)
 		error("setsockopt IPV6_V6ONLY: %s", strerror(errno));
 #endif
 }
 
 /*
  * Compares two strings that maybe be NULL. Returns non-zero if strings
  * are both NULL or are identical, returns zero otherwise.
  */
 static int
 strcmp_maybe_null(const char *a, const char *b)
 {
 	if ((a == NULL && b != NULL) || (a != NULL && b == NULL))
 		return 0;
 	if (a != NULL && strcmp(a, b) != 0)
 		return 0;
 	return 1;
 }
 
 /*
  * Compare two forwards, returning non-zero if they are identical or
  * zero otherwise.
  */
 int
 forward_equals(const struct Forward *a, const struct Forward *b)
 {
 	if (strcmp_maybe_null(a->listen_host, b->listen_host) == 0)
 		return 0;
 	if (a->listen_port != b->listen_port)
 		return 0;
 	if (strcmp_maybe_null(a->listen_path, b->listen_path) == 0)
 		return 0;
 	if (strcmp_maybe_null(a->connect_host, b->connect_host) == 0)
 		return 0;
 	if (a->connect_port != b->connect_port)
 		return 0;
 	if (strcmp_maybe_null(a->connect_path, b->connect_path) == 0)
 		return 0;
 	/* allocated_port and handle are not checked */
 	return 1;
 }
 
 /* returns 1 if process is already daemonized, 0 otherwise */
 int
 daemonized(void)
 {
 	int fd;
 
 	if ((fd = open(_PATH_TTY, O_RDONLY | O_NOCTTY)) >= 0) {
 		close(fd);
 		return 0;	/* have controlling terminal */
 	}
 	if (getppid() != 1)
 		return 0;	/* parent is not init */
 	if (getsid(0) != getpid())
 		return 0;	/* not session leader */
 	debug3("already daemonized");
 	return 1;
 }
 
 
 /*
  * Splits 's' into an argument vector. Handles quoted string and basic
  * escape characters (\\, \", \'). Caller must free the argument vector
  * and its members.
  */
 int
 argv_split(const char *s, int *argcp, char ***argvp)
 {
 	int r = SSH_ERR_INTERNAL_ERROR;
 	int argc = 0, quote, i, j;
 	char *arg, **argv = xcalloc(1, sizeof(*argv));
 
 	*argvp = NULL;
 	*argcp = 0;
 
 	for (i = 0; s[i] != '\0'; i++) {
 		/* Skip leading whitespace */
 		if (s[i] == ' ' || s[i] == '\t')
 			continue;
 
 		/* Start of a token */
 		quote = 0;
 		if (s[i] == '\\' &&
 		    (s[i + 1] == '\'' || s[i + 1] == '\"' || s[i + 1] == '\\'))
 			i++;
 		else if (s[i] == '\'' || s[i] == '"')
 			quote = s[i++];
 
 		argv = xreallocarray(argv, (argc + 2), sizeof(*argv));
 		arg = argv[argc++] = xcalloc(1, strlen(s + i) + 1);
 		argv[argc] = NULL;
 
 		/* Copy the token in, removing escapes */
 		for (j = 0; s[i] != '\0'; i++) {
 			if (s[i] == '\\') {
 				if (s[i + 1] == '\'' ||
 				    s[i + 1] == '\"' ||
 				    s[i + 1] == '\\') {
 					i++; /* Skip '\' */
 					arg[j++] = s[i];
 				} else {
 					/* Unrecognised escape */
 					arg[j++] = s[i];
 				}
 			} else if (quote == 0 && (s[i] == ' ' || s[i] == '\t'))
 				break; /* done */
 			else if (quote != 0 && s[i] == quote)
 				break; /* done */
 			else
 				arg[j++] = s[i];
 		}
 		if (s[i] == '\0') {
 			if (quote != 0) {
 				/* Ran out of string looking for close quote */
 				r = SSH_ERR_INVALID_FORMAT;
 				goto out;
 			}
 			break;
 		}
 	}
 	/* Success */
 	*argcp = argc;
 	*argvp = argv;
 	argc = 0;
 	argv = NULL;
 	r = 0;
  out:
 	if (argc != 0 && argv != NULL) {
 		for (i = 0; i < argc; i++)
 			free(argv[i]);
 		free(argv);
 	}
 	return r;
 }
 
 /*
  * Reassemble an argument vector into a string, quoting and escaping as
  * necessary. Caller must free returned string.
  */
 char *
 argv_assemble(int argc, char **argv)
 {
 	int i, j, ws, r;
 	char c, *ret;
 	struct sshbuf *buf, *arg;
 
 	if ((buf = sshbuf_new()) == NULL || (arg = sshbuf_new()) == NULL)
 		fatal("%s: sshbuf_new failed", __func__);
 
 	for (i = 0; i < argc; i++) {
 		ws = 0;
 		sshbuf_reset(arg);
 		for (j = 0; argv[i][j] != '\0'; j++) {
 			r = 0;
 			c = argv[i][j];
 			switch (c) {
 			case ' ':
 			case '\t':
 				ws = 1;
 				r = sshbuf_put_u8(arg, c);
 				break;
 			case '\\':
 			case '\'':
 			case '"':
 				if ((r = sshbuf_put_u8(arg, '\\')) != 0)
 					break;
 				/* FALLTHROUGH */
 			default:
 				r = sshbuf_put_u8(arg, c);
 				break;
 			}
 			if (r != 0)
 				fatal("%s: sshbuf_put_u8: %s",
 				    __func__, ssh_err(r));
 		}
 		if ((i != 0 && (r = sshbuf_put_u8(buf, ' ')) != 0) ||
 		    (ws != 0 && (r = sshbuf_put_u8(buf, '"')) != 0) ||
 		    (r = sshbuf_putb(buf, arg)) != 0 ||
 		    (ws != 0 && (r = sshbuf_put_u8(buf, '"')) != 0))
 			fatal("%s: buffer error: %s", __func__, ssh_err(r));
 	}
 	if ((ret = malloc(sshbuf_len(buf) + 1)) == NULL)
 		fatal("%s: malloc failed", __func__);
 	memcpy(ret, sshbuf_ptr(buf), sshbuf_len(buf));
 	ret[sshbuf_len(buf)] = '\0';
 	sshbuf_free(buf);
 	sshbuf_free(arg);
 	return ret;
 }
 
 /* Returns 0 if pid exited cleanly, non-zero otherwise */
 int
 exited_cleanly(pid_t pid, const char *tag, const char *cmd, int quiet)
 {
 	int status;
 
 	while (waitpid(pid, &status, 0) == -1) {
 		if (errno != EINTR) {
 			error("%s: waitpid: %s", tag, strerror(errno));
 			return -1;
 		}
 	}
 	if (WIFSIGNALED(status)) {
 		error("%s %s exited on signal %d", tag, cmd, WTERMSIG(status));
 		return -1;
 	} else if (WEXITSTATUS(status) != 0) {
 		do_log2(quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_INFO,
 		    "%s %s failed, status %d", tag, cmd, WEXITSTATUS(status));
 		return -1;
 	}
 	return 0;
 }
 
 /*
  * Check a given path for security. This is defined as all components
  * of the path to the file must be owned by either the owner of
  * of the file or root and no directories must be group or world writable.
  *
  * XXX Should any specific check be done for sym links ?
  *
  * Takes a file name, its stat information (preferably from fstat() to
  * avoid races), the uid of the expected owner, their home directory and an
  * error buffer plus max size as arguments.
  *
  * Returns 0 on success and -1 on failure
  */
 int
 safe_path(const char *name, struct stat *stp, const char *pw_dir,
     uid_t uid, char *err, size_t errlen)
 {
 	char buf[PATH_MAX], homedir[PATH_MAX];
 	char *cp;
 	int comparehome = 0;
 	struct stat st;
 
 	if (realpath(name, buf) == NULL) {
 		snprintf(err, errlen, "realpath %s failed: %s", name,
 		    strerror(errno));
 		return -1;
 	}
 	if (pw_dir != NULL && realpath(pw_dir, homedir) != NULL)
 		comparehome = 1;
 
 	if (!S_ISREG(stp->st_mode)) {
 		snprintf(err, errlen, "%s is not a regular file", buf);
 		return -1;
 	}
 	if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) ||
 	    (stp->st_mode & 022) != 0) {
 		snprintf(err, errlen, "bad ownership or modes for file %s",
 		    buf);
 		return -1;
 	}
 
 	/* for each component of the canonical path, walking upwards */
 	for (;;) {
 		if ((cp = dirname(buf)) == NULL) {
 			snprintf(err, errlen, "dirname() failed");
 			return -1;
 		}
 		strlcpy(buf, cp, sizeof(buf));
 
 		if (stat(buf, &st) < 0 ||
 		    (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) ||
 		    (st.st_mode & 022) != 0) {
 			snprintf(err, errlen,
 			    "bad ownership or modes for directory %s", buf);
 			return -1;
 		}
 
 		/* If are past the homedir then we can stop */
 		if (comparehome && strcmp(homedir, buf) == 0)
 			break;
 
 		/*
 		 * dirname should always complete with a "/" path,
 		 * but we can be paranoid and check for "." too
 		 */
 		if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0))
 			break;
 	}
 	return 0;
 }
 
 /*
  * Version of safe_path() that accepts an open file descriptor to
  * avoid races.
  *
  * Returns 0 on success and -1 on failure
  */
 int
 safe_path_fd(int fd, const char *file, struct passwd *pw,
     char *err, size_t errlen)
 {
 	struct stat st;
 
 	/* check the open file to avoid races */
 	if (fstat(fd, &st) < 0) {
 		snprintf(err, errlen, "cannot stat file %s: %s",
 		    file, strerror(errno));
 		return -1;
 	}
 	return safe_path(file, &st, pw->pw_dir, pw->pw_uid, err, errlen);
 }
 
 /*
  * Sets the value of the given variable in the environment.  If the variable
  * already exists, its value is overridden.
  */
 void
 child_set_env(char ***envp, u_int *envsizep, const char *name,
 	const char *value)
 {
 	char **env;
 	u_int envsize;
 	u_int i, namelen;
 
 	if (strchr(name, '=') != NULL) {
 		error("Invalid environment variable \"%.100s\"", name);
 		return;
 	}
 
 	/*
 	 * If we're passed an uninitialized list, allocate a single null
 	 * entry before continuing.
 	 */
 	if (*envp == NULL && *envsizep == 0) {
 		*envp = xmalloc(sizeof(char *));
 		*envp[0] = NULL;
 		*envsizep = 1;
 	}
 
 	/*
 	 * Find the slot where the value should be stored.  If the variable
 	 * already exists, we reuse the slot; otherwise we append a new slot
 	 * at the end of the array, expanding if necessary.
 	 */
 	env = *envp;
 	namelen = strlen(name);
 	for (i = 0; env[i]; i++)
 		if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=')
 			break;
 	if (env[i]) {
 		/* Reuse the slot. */
 		free(env[i]);
 	} else {
 		/* New variable.  Expand if necessary. */
 		envsize = *envsizep;
 		if (i >= envsize - 1) {
 			if (envsize >= 1000)
 				fatal("child_set_env: too many env vars");
 			envsize += 50;
 			env = (*envp) = xreallocarray(env, envsize, sizeof(char *));
 			*envsizep = envsize;
 		}
 		/* Need to set the NULL pointer at end of array beyond the new slot. */
 		env[i + 1] = NULL;
 	}
 
 	/* Allocate space and format the variable in the appropriate slot. */
 	/* XXX xasprintf */
 	env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1);
 	snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value);
 }
 
 /*
  * Check and optionally lowercase a domain name, also removes trailing '.'
  * Returns 1 on success and 0 on failure, storing an error message in errstr.
  */
 int
 valid_domain(char *name, int makelower, const char **errstr)
 {
 	size_t i, l = strlen(name);
 	u_char c, last = '\0';
 	static char errbuf[256];
 
 	if (l == 0) {
 		strlcpy(errbuf, "empty domain name", sizeof(errbuf));
 		goto bad;
 	}
 	if (!isalpha((u_char)name[0]) && !isdigit((u_char)name[0])) {
 		snprintf(errbuf, sizeof(errbuf), "domain name \"%.100s\" "
 		    "starts with invalid character", name);
 		goto bad;
 	}
 	for (i = 0; i < l; i++) {
 		c = tolower((u_char)name[i]);
 		if (makelower)
 			name[i] = (char)c;
 		if (last == '.' && c == '.') {
 			snprintf(errbuf, sizeof(errbuf), "domain name "
 			    "\"%.100s\" contains consecutive separators", name);
 			goto bad;
 		}
 		if (c != '.' && c != '-' && !isalnum(c) &&
 		    c != '_') /* technically invalid, but common */ {
 			snprintf(errbuf, sizeof(errbuf), "domain name "
 			    "\"%.100s\" contains invalid characters", name);
 			goto bad;
 		}
 		last = c;
 	}
 	if (name[l - 1] == '.')
 		name[l - 1] = '\0';
 	if (errstr != NULL)
 		*errstr = NULL;
 	return 1;
 bad:
 	if (errstr != NULL)
 		*errstr = errbuf;
 	return 0;
 }
 
 /*
  * Verify that a environment variable name (not including initial '$') is
  * valid; consisting of one or more alphanumeric or underscore characters only.
  * Returns 1 on valid, 0 otherwise.
  */
 int
 valid_env_name(const char *name)
 {
 	const char *cp;
 
 	if (name[0] == '\0')
 		return 0;
 	for (cp = name; *cp != '\0'; cp++) {
 		if (!isalnum((u_char)*cp) && *cp != '_')
 			return 0;
 	}
 	return 1;
 }
 
 const char *
 atoi_err(const char *nptr, int *val)
 {
 	const char *errstr = NULL;
 	long long num;
 
 	if (nptr == NULL || *nptr == '\0')
 		return "missing";
 	num = strtonum(nptr, 0, INT_MAX, &errstr);
 	if (errstr == NULL)
 		*val = (int)num;
 	return errstr;
 }
 
 int
 parse_absolute_time(const char *s, uint64_t *tp)
 {
 	struct tm tm;
 	time_t tt;
 	char buf[32], *fmt;
 
 	*tp = 0;
 
 	/*
 	 * POSIX strptime says "The application shall ensure that there
 	 * is white-space or other non-alphanumeric characters between
 	 * any two conversion specifications" so arrange things this way.
 	 */
 	switch (strlen(s)) {
 	case 8: /* YYYYMMDD */
 		fmt = "%Y-%m-%d";
 		snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2s", s, s + 4, s + 6);
 		break;
 	case 12: /* YYYYMMDDHHMM */
 		fmt = "%Y-%m-%dT%H:%M";
 		snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s",
 		    s, s + 4, s + 6, s + 8, s + 10);
 		break;
 	case 14: /* YYYYMMDDHHMMSS */
 		fmt = "%Y-%m-%dT%H:%M:%S";
 		snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s:%.2s",
 		    s, s + 4, s + 6, s + 8, s + 10, s + 12);
 		break;
 	default:
 		return SSH_ERR_INVALID_FORMAT;
 	}
 
 	memset(&tm, 0, sizeof(tm));
 	if (strptime(buf, fmt, &tm) == NULL)
 		return SSH_ERR_INVALID_FORMAT;
 	if ((tt = mktime(&tm)) < 0)
 		return SSH_ERR_INVALID_FORMAT;
 	/* success */
 	*tp = (uint64_t)tt;
 	return 0;
 }
 
 void
 format_absolute_time(uint64_t t, char *buf, size_t len)
 {
 	time_t tt = t > INT_MAX ? INT_MAX : t; /* XXX revisit in 2038 :P */
 	struct tm tm;
 
 	localtime_r(&tt, &tm);
 	strftime(buf, len, "%Y-%m-%dT%H:%M:%S", &tm);
 }
diff --git a/crypto/openssh/sshconnect.c b/crypto/openssh/sshconnect.c
index 2776be920a2c..b50029de71b8 100644
--- a/crypto/openssh/sshconnect.c
+++ b/crypto/openssh/sshconnect.c
@@ -1,1585 +1,1584 @@
 /* $OpenBSD: sshconnect.c,v 1.305 2018/09/20 03:30:44 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
  *                    All rights reserved
  * Code to connect to a remote host, and to perform the client side of the
  * login (authentication) dialog.
  *
  * As far as I am concerned, the code I have written for this software
  * can be used freely for any purpose.  Any derived versions of this
  * software must be clearly marked as such, and if the derived work is
  * incompatible with the protocol description in the RFC file, it must be
  * called by a name other than "ssh" or "Secure Shell".
  */
 
 #include "includes.h"
 __RCSID("$FreeBSD$");
 
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
 #include <sys/socket.h>
 #ifdef HAVE_SYS_TIME_H
 # include <sys/time.h>
 #endif
 
 #include <net/if.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
-#include <rpc/rpc.h>
 
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <netdb.h>
 #ifdef HAVE_PATHS_H
 #include <paths.h>
 #endif
 #include <pwd.h>
 #ifdef HAVE_POLL_H
 #include <poll.h>
 #endif
 #include <signal.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #ifdef HAVE_IFADDRS_H
 # include <ifaddrs.h>
 #endif
 
 #include "xmalloc.h"
 #include "hostfile.h"
 #include "ssh.h"
 #include "sshbuf.h"
 #include "packet.h"
 #include "compat.h"
 #include "sshkey.h"
 #include "sshconnect.h"
 #include "hostfile.h"
 #include "log.h"
 #include "misc.h"
 #include "readconf.h"
 #include "atomicio.h"
 #include "dns.h"
 #include "monitor_fdpass.h"
 #include "ssh2.h"
 #include "version.h"
 #include "authfile.h"
 #include "ssherr.h"
 #include "authfd.h"
 
 char *client_version_string = NULL;
 char *server_version_string = NULL;
 struct sshkey *previous_host_key = NULL;
 
 static int matching_host_key_dns = 0;
 
 static pid_t proxy_command_pid = 0;
 
 /* import */
 extern Options options;
 extern char *__progname;
 
 static int show_other_keys(struct hostkeys *, struct sshkey *);
 static void warn_changed_key(struct sshkey *);
 
 /* Expand a proxy command */
 static char *
 expand_proxy_command(const char *proxy_command, const char *user,
     const char *host, int port)
 {
 	char *tmp, *ret, strport[NI_MAXSERV];
 
 	snprintf(strport, sizeof strport, "%d", port);
 	xasprintf(&tmp, "exec %s", proxy_command);
 	ret = percent_expand(tmp, "h", host, "p", strport,
 	    "r", options.user, (char *)NULL);
 	free(tmp);
 	return ret;
 }
 
 /*
  * Connect to the given ssh server using a proxy command that passes a
  * a connected fd back to us.
  */
 static int
 ssh_proxy_fdpass_connect(struct ssh *ssh, const char *host, u_short port,
     const char *proxy_command)
 {
 	char *command_string;
 	int sp[2], sock;
 	pid_t pid;
 	char *shell;
 
 	if ((shell = getenv("SHELL")) == NULL)
 		shell = _PATH_BSHELL;
 
 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0)
 		fatal("Could not create socketpair to communicate with "
 		    "proxy dialer: %.100s", strerror(errno));
 
 	command_string = expand_proxy_command(proxy_command, options.user,
 	    host, port);
 	debug("Executing proxy dialer command: %.500s", command_string);
 
 	/* Fork and execute the proxy command. */
 	if ((pid = fork()) == 0) {
 		char *argv[10];
 
 		close(sp[1]);
 		/* Redirect stdin and stdout. */
 		if (sp[0] != 0) {
 			if (dup2(sp[0], 0) < 0)
 				perror("dup2 stdin");
 		}
 		if (sp[0] != 1) {
 			if (dup2(sp[0], 1) < 0)
 				perror("dup2 stdout");
 		}
 		if (sp[0] >= 2)
 			close(sp[0]);
 
 		/*
 		 * Stderr is left as it is so that error messages get
 		 * printed on the user's terminal.
 		 */
 		argv[0] = shell;
 		argv[1] = "-c";
 		argv[2] = command_string;
 		argv[3] = NULL;
 
 		/*
 		 * Execute the proxy command.
 		 * Note that we gave up any extra privileges above.
 		 */
 		execv(argv[0], argv);
 		perror(argv[0]);
 		exit(1);
 	}
 	/* Parent. */
 	if (pid < 0)
 		fatal("fork failed: %.100s", strerror(errno));
 	close(sp[0]);
 	free(command_string);
 
 	if ((sock = mm_receive_fd(sp[1])) == -1)
 		fatal("proxy dialer did not pass back a connection");
 	close(sp[1]);
 
 	while (waitpid(pid, NULL, 0) == -1)
 		if (errno != EINTR)
 			fatal("Couldn't wait for child: %s", strerror(errno));
 
 	/* Set the connection file descriptors. */
 	if (ssh_packet_set_connection(ssh, sock, sock) == NULL)
 		return -1; /* ssh_packet_set_connection logs error */
 
 	return 0;
 }
 
 /*
  * Connect to the given ssh server using a proxy command.
  */
 static int
 ssh_proxy_connect(struct ssh *ssh, const char *host, u_short port,
     const char *proxy_command)
 {
 	char *command_string;
 	int pin[2], pout[2];
 	pid_t pid;
 	char *shell;
 
 	if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
 		shell = _PATH_BSHELL;
 
 	/* Create pipes for communicating with the proxy. */
 	if (pipe(pin) < 0 || pipe(pout) < 0)
 		fatal("Could not create pipes to communicate with the proxy: %.100s",
 		    strerror(errno));
 
 	command_string = expand_proxy_command(proxy_command, options.user,
 	    host, port);
 	debug("Executing proxy command: %.500s", command_string);
 
 	/* Fork and execute the proxy command. */
 	if ((pid = fork()) == 0) {
 		char *argv[10];
 
 		/* Redirect stdin and stdout. */
 		close(pin[1]);
 		if (pin[0] != 0) {
 			if (dup2(pin[0], 0) < 0)
 				perror("dup2 stdin");
 			close(pin[0]);
 		}
 		close(pout[0]);
 		if (dup2(pout[1], 1) < 0)
 			perror("dup2 stdout");
 		/* Cannot be 1 because pin allocated two descriptors. */
 		close(pout[1]);
 
 		/* Stderr is left as it is so that error messages get
 		   printed on the user's terminal. */
 		argv[0] = shell;
 		argv[1] = "-c";
 		argv[2] = command_string;
 		argv[3] = NULL;
 
 		/* Execute the proxy command.  Note that we gave up any
 		   extra privileges above. */
 		signal(SIGPIPE, SIG_DFL);
 		execv(argv[0], argv);
 		perror(argv[0]);
 		exit(1);
 	}
 	/* Parent. */
 	if (pid < 0)
 		fatal("fork failed: %.100s", strerror(errno));
 	else
 		proxy_command_pid = pid; /* save pid to clean up later */
 
 	/* Close child side of the descriptors. */
 	close(pin[0]);
 	close(pout[1]);
 
 	/* Free the command name. */
 	free(command_string);
 
 	/* Set the connection file descriptors. */
 	if (ssh_packet_set_connection(ssh, pout[0], pin[1]) == NULL)
 		return -1; /* ssh_packet_set_connection logs error */
 
 	return 0;
 }
 
 void
 ssh_kill_proxy_command(void)
 {
 	/*
 	 * Send SIGHUP to proxy command if used. We don't wait() in
 	 * case it hangs and instead rely on init to reap the child
 	 */
 	if (proxy_command_pid > 1)
 		kill(proxy_command_pid, SIGHUP);
 }
 
 #ifdef HAVE_IFADDRS_H
 /*
  * Search a interface address list (returned from getifaddrs(3)) for an
  * address that matches the desired address family on the specified interface.
  * Returns 0 and fills in *resultp and *rlenp on success. Returns -1 on failure.
  */
 static int
 check_ifaddrs(const char *ifname, int af, const struct ifaddrs *ifaddrs,
     struct sockaddr_storage *resultp, socklen_t *rlenp)
 {
 	struct sockaddr_in6 *sa6;
 	struct sockaddr_in *sa;
 	struct in6_addr *v6addr;
 	const struct ifaddrs *ifa;
 	int allow_local;
 
 	/*
 	 * Prefer addresses that are not loopback or linklocal, but use them
 	 * if nothing else matches.
 	 */
 	for (allow_local = 0; allow_local < 2; allow_local++) {
 		for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
 			if (ifa->ifa_addr == NULL || ifa->ifa_name == NULL ||
 			    (ifa->ifa_flags & IFF_UP) == 0 ||
 			    ifa->ifa_addr->sa_family != af ||
 			    strcmp(ifa->ifa_name, options.bind_interface) != 0)
 				continue;
 			switch (ifa->ifa_addr->sa_family) {
 			case AF_INET:
 				sa = (struct sockaddr_in *)ifa->ifa_addr;
 				if (!allow_local && sa->sin_addr.s_addr ==
 				    htonl(INADDR_LOOPBACK))
 					continue;
 				if (*rlenp < sizeof(struct sockaddr_in)) {
 					error("%s: v4 addr doesn't fit",
 					    __func__);
 					return -1;
 				}
 				*rlenp = sizeof(struct sockaddr_in);
 				memcpy(resultp, sa, *rlenp);
 				return 0;
 			case AF_INET6:
 				sa6 = (struct sockaddr_in6 *)ifa->ifa_addr;
 				v6addr = &sa6->sin6_addr;
 				if (!allow_local &&
 				    (IN6_IS_ADDR_LINKLOCAL(v6addr) ||
 				    IN6_IS_ADDR_LOOPBACK(v6addr)))
 					continue;
 				if (*rlenp < sizeof(struct sockaddr_in6)) {
 					error("%s: v6 addr doesn't fit",
 					    __func__);
 					return -1;
 				}
 				*rlenp = sizeof(struct sockaddr_in6);
 				memcpy(resultp, sa6, *rlenp);
 				return 0;
 			}
 		}
 	}
 	return -1;
 }
 #endif
 
 /*
  * Creates a socket for use as the ssh connection.
  */
 static int
 ssh_create_socket(struct addrinfo *ai)
 {
 	int sock, r;
 	struct sockaddr_storage bindaddr;
 	socklen_t bindaddrlen = 0;
 	struct addrinfo hints, *res = NULL;
 #ifdef HAVE_IFADDRS_H
 	struct ifaddrs *ifaddrs = NULL;
 #endif
 	char ntop[NI_MAXHOST];
 
 	sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
 	if (sock < 0) {
 		error("socket: %s", strerror(errno));
 		return -1;
 	}
 	fcntl(sock, F_SETFD, FD_CLOEXEC);
 
 	/* Bind the socket to an alternative local IP address */
 	if (options.bind_address == NULL && options.bind_interface == NULL)
 		return sock;
 
 	if (options.bind_address != NULL) {
 		memset(&hints, 0, sizeof(hints));
 		hints.ai_family = ai->ai_family;
 		hints.ai_socktype = ai->ai_socktype;
 		hints.ai_protocol = ai->ai_protocol;
 		hints.ai_flags = AI_PASSIVE;
 		if ((r = getaddrinfo(options.bind_address, NULL,
 		    &hints, &res)) != 0) {
 			error("getaddrinfo: %s: %s", options.bind_address,
 			    ssh_gai_strerror(r));
 			goto fail;
 		}
 		if (res == NULL) {
 			error("getaddrinfo: no addrs");
 			goto fail;
 		}
 		if (res->ai_addrlen > sizeof(bindaddr)) {
 			error("%s: addr doesn't fit", __func__);
 			goto fail;
 		}
 		memcpy(&bindaddr, res->ai_addr, res->ai_addrlen);
 		bindaddrlen = res->ai_addrlen;
 	} else if (options.bind_interface != NULL) {
 #ifdef HAVE_IFADDRS_H
 		if ((r = getifaddrs(&ifaddrs)) != 0) {
 			error("getifaddrs: %s: %s", options.bind_interface,
 			      strerror(errno));
 			goto fail;
 		}
 		bindaddrlen = sizeof(bindaddr);
 		if (check_ifaddrs(options.bind_interface, ai->ai_family,
 		    ifaddrs, &bindaddr, &bindaddrlen) != 0) {
 			logit("getifaddrs: %s: no suitable addresses",
 			      options.bind_interface);
 			goto fail;
 		}
 #else
 		error("BindInterface not supported on this platform.");
 #endif
 	}
 	if ((r = getnameinfo((struct sockaddr *)&bindaddr, bindaddrlen,
 	    ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST)) != 0) {
 		error("%s: getnameinfo failed: %s", __func__,
 		    ssh_gai_strerror(r));
 		goto fail;
 	}
 	if (bind(sock, (struct sockaddr *)&bindaddr, bindaddrlen) != 0) {
 		error("bind %s: %s", ntop, strerror(errno));
 		goto fail;
 	}
 	debug("%s: bound to %s", __func__, ntop);
 	/* success */
 	goto out;
 fail:
 	close(sock);
 	sock = -1;
  out:
 	if (res != NULL)
 		freeaddrinfo(res);
 #ifdef HAVE_IFADDRS_H
 	if (ifaddrs != NULL)
 		freeifaddrs(ifaddrs);
 #endif
 	return sock;
 }
 
 /*
  * Wait up to *timeoutp milliseconds for fd to be readable. Updates
  * *timeoutp with time remaining.
  * Returns 0 if fd ready or -1 on timeout or error (see errno).
  */
 static int
 waitrfd(int fd, int *timeoutp)
 {
 	struct pollfd pfd;
 	struct timeval t_start;
 	int oerrno, r;
 
 	monotime_tv(&t_start);
 	pfd.fd = fd;
 	pfd.events = POLLIN;
 	for (; *timeoutp >= 0;) {
 		r = poll(&pfd, 1, *timeoutp);
 		oerrno = errno;
 		ms_subtract_diff(&t_start, timeoutp);
 		errno = oerrno;
 		if (r > 0)
 			return 0;
 		else if (r == -1 && errno != EAGAIN)
 			return -1;
 		else if (r == 0)
 			break;
 	}
 	/* timeout */
 	errno = ETIMEDOUT;
 	return -1;
 }
 
 static int
 timeout_connect(int sockfd, const struct sockaddr *serv_addr,
     socklen_t addrlen, int *timeoutp)
 {
 	int optval = 0;
 	socklen_t optlen = sizeof(optval);
 
 	/* No timeout: just do a blocking connect() */
 	if (*timeoutp <= 0)
 		return connect(sockfd, serv_addr, addrlen);
 
 	set_nonblock(sockfd);
 	if (connect(sockfd, serv_addr, addrlen) == 0) {
 		/* Succeeded already? */
 		unset_nonblock(sockfd);
 		return 0;
 	} else if (errno != EINPROGRESS)
 		return -1;
 
 	if (waitrfd(sockfd, timeoutp) == -1)
 		return -1;
 
 	/* Completed or failed */
 	if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) {
 		debug("getsockopt: %s", strerror(errno));
 		return -1;
 	}
 	if (optval != 0) {
 		errno = optval;
 		return -1;
 	}
 	unset_nonblock(sockfd);
 	return 0;
 }
 
 /*
  * Opens a TCP/IP connection to the remote server on the given host.
  * The address of the remote host will be returned in hostaddr.
  * If port is 0, the default port will be used.
  * Connection_attempts specifies the maximum number of tries (one per
  * second).  If proxy_command is non-NULL, it specifies the command (with %h
  * and %p substituted for host and port, respectively) to use to contact
  * the daemon.
  */
 static int
 ssh_connect_direct(struct ssh *ssh, const char *host, struct addrinfo *aitop,
     struct sockaddr_storage *hostaddr, u_short port, int family,
     int connection_attempts, int *timeout_ms, int want_keepalive)
 {
 	int on = 1;
 	int oerrno, sock = -1, attempt;
 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
 	struct addrinfo *ai;
 
 	debug2("%s", __func__);
 	memset(ntop, 0, sizeof(ntop));
 	memset(strport, 0, sizeof(strport));
 
 	for (attempt = 0; attempt < connection_attempts; attempt++) {
 		if (attempt > 0) {
 			/* Sleep a moment before retrying. */
 			sleep(1);
 			debug("Trying again...");
 		}
 		/*
 		 * Loop through addresses for this host, and try each one in
 		 * sequence until the connection succeeds.
 		 */
 		for (ai = aitop; ai; ai = ai->ai_next) {
 			if (ai->ai_family != AF_INET &&
 			    ai->ai_family != AF_INET6) {
 				errno = EAFNOSUPPORT;
 				continue;
 			}
 			if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
 			    ntop, sizeof(ntop), strport, sizeof(strport),
 			    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
 				oerrno = errno;
 				error("%s: getnameinfo failed", __func__);
 				errno = oerrno;
 				continue;
 			}
 			debug("Connecting to %.200s [%.100s] port %s.",
 				host, ntop, strport);
 
 			/* Create a socket for connecting. */
 			sock = ssh_create_socket(ai);
 			if (sock < 0) {
 				/* Any error is already output */
 				errno = 0;
 				continue;
 			}
 
 			if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen,
 			    timeout_ms) >= 0) {
 				/* Successful connection. */
 				memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen);
 				break;
 			} else {
 				oerrno = errno;
 				debug("connect to address %s port %s: %s",
 				    ntop, strport, strerror(errno));
 				close(sock);
 				sock = -1;
 				errno = oerrno;
 			}
 		}
 		if (sock != -1)
 			break;	/* Successful connection. */
 	}
 
 	/* Return failure if we didn't get a successful connection. */
 	if (sock == -1) {
 		error("ssh: connect to host %s port %s: %s",
 		    host, strport, errno == 0 ? "failure" : strerror(errno));
 		return -1;
 	}
 
 	debug("Connection established.");
 
 	/* Set SO_KEEPALIVE if requested. */
 	if (want_keepalive &&
 	    setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on,
 	    sizeof(on)) < 0)
 		error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno));
 
 	/* Set the connection. */
 	if (ssh_packet_set_connection(ssh, sock, sock) == NULL)
 		return -1; /* ssh_packet_set_connection logs error */
 
         return 0;
 }
 
 int
 ssh_connect(struct ssh *ssh, const char *host, struct addrinfo *addrs,
     struct sockaddr_storage *hostaddr, u_short port, int family,
     int connection_attempts, int *timeout_ms, int want_keepalive)
 {
 	if (options.proxy_command == NULL) {
 		return ssh_connect_direct(ssh, host, addrs, hostaddr, port,
 		    family, connection_attempts, timeout_ms, want_keepalive);
 	} else if (strcmp(options.proxy_command, "-") == 0) {
 		if ((ssh_packet_set_connection(ssh,
 		    STDIN_FILENO, STDOUT_FILENO)) == NULL)
 			return -1; /* ssh_packet_set_connection logs error */
 		return 0;
 	} else if (options.proxy_use_fdpass) {
 		return ssh_proxy_fdpass_connect(ssh, host, port,
 		    options.proxy_command);
 	}
 	return ssh_proxy_connect(ssh, host, port, options.proxy_command);
 }
 
 static void
 send_client_banner(int connection_out, int minor1)
 {
 	/* Send our own protocol version identification. */
 	xasprintf(&client_version_string, "SSH-%d.%d-%.100s%s%s\n",
 	    PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION,
 	    *options.version_addendum == '\0' ? "" : " ",
 	    options.version_addendum);
 	if (atomicio(vwrite, connection_out, client_version_string,
 	    strlen(client_version_string)) != strlen(client_version_string))
 		fatal("write: %.100s", strerror(errno));
 	chop(client_version_string);
 	debug("Local version string %.100s", client_version_string);
 }
 
 /*
  * Waits for the server identification string, and sends our own
  * identification string.
  */
 void
 ssh_exchange_identification(int timeout_ms)
 {
 	char buf[256], remote_version[256];	/* must be same size! */
 	int remote_major, remote_minor, mismatch;
 	int connection_in = packet_get_connection_in();
 	int connection_out = packet_get_connection_out();
 	u_int i, n;
 	size_t len;
 	int rc;
 
 	send_client_banner(connection_out, 0);
 
 	/* Read other side's version identification. */
 	for (n = 0;;) {
 		for (i = 0; i < sizeof(buf) - 1; i++) {
 			if (timeout_ms > 0) {
 				rc = waitrfd(connection_in, &timeout_ms);
 				if (rc == -1 && errno == ETIMEDOUT) {
 					fatal("Connection timed out during "
 					    "banner exchange");
 				} else if (rc == -1) {
 					fatal("%s: %s",
 					    __func__, strerror(errno));
 				}
 			}
 
 			len = atomicio(read, connection_in, &buf[i], 1);
 			if (len != 1 && errno == EPIPE)
 				fatal("ssh_exchange_identification: "
 				    "Connection closed by remote host");
 			else if (len != 1)
 				fatal("ssh_exchange_identification: "
 				    "read: %.100s", strerror(errno));
 			if (buf[i] == '\r') {
 				buf[i] = '\n';
 				buf[i + 1] = 0;
 				continue;		/**XXX wait for \n */
 			}
 			if (buf[i] == '\n') {
 				buf[i + 1] = 0;
 				break;
 			}
 			if (++n > 65536)
 				fatal("ssh_exchange_identification: "
 				    "No banner received");
 		}
 		buf[sizeof(buf) - 1] = 0;
 		if (strncmp(buf, "SSH-", 4) == 0)
 			break;
 		debug("ssh_exchange_identification: %s", buf);
 	}
 	server_version_string = xstrdup(buf);
 
 	/*
 	 * Check that the versions match.  In future this might accept
 	 * several versions and set appropriate flags to handle them.
 	 */
 	if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n",
 	    &remote_major, &remote_minor, remote_version) != 3)
 		fatal("Bad remote protocol version identification: '%.100s'", buf);
 	debug("Remote protocol version %d.%d, remote software version %.100s",
 	    remote_major, remote_minor, remote_version);
 
 	active_state->compat = compat_datafellows(remote_version);
 	mismatch = 0;
 
 	switch (remote_major) {
 	case 2:
 		break;
 	case 1:
 		if (remote_minor != 99)
 			mismatch = 1;
 		break;
 	default:
 		mismatch = 1;
 		break;
 	}
 	if (mismatch)
 		fatal("Protocol major versions differ: %d vs. %d",
 		    PROTOCOL_MAJOR_2, remote_major);
 	if ((datafellows & SSH_BUG_RSASIGMD5) != 0)
 		logit("Server version \"%.100s\" uses unsafe RSA signature "
 		    "scheme; disabling use of RSA keys", remote_version);
 	chop(server_version_string);
 }
 
 /* defaults to 'no' */
 static int
 confirm(const char *prompt)
 {
 	const char *msg, *again = "Please type 'yes' or 'no': ";
 	char *p;
 	int ret = -1;
 
 	if (options.batch_mode)
 		return 0;
 	for (msg = prompt;;msg = again) {
 		p = read_passphrase(msg, RP_ECHO);
 		if (p == NULL)
 			return 0;
 		p[strcspn(p, "\n")] = '\0';
 		if (p[0] == '\0' || strcasecmp(p, "no") == 0)
 			ret = 0;
 		else if (strcasecmp(p, "yes") == 0)
 			ret = 1;
 		free(p);
 		if (ret != -1)
 			return ret;
 	}
 }
 
 static int
 check_host_cert(const char *host, const struct sshkey *key)
 {
 	const char *reason;
 	int r;
 
 	if (sshkey_cert_check_authority(key, 1, 0, host, &reason) != 0) {
 		error("%s", reason);
 		return 0;
 	}
 	if (sshbuf_len(key->cert->critical) != 0) {
 		error("Certificate for %s contains unsupported "
 		    "critical options(s)", host);
 		return 0;
 	}
 	if ((r = sshkey_check_cert_sigtype(key,
 	    options.ca_sign_algorithms)) != 0) {
 		logit("%s: certificate signature algorithm %s: %s", __func__,
 		    (key->cert == NULL || key->cert->signature_type == NULL) ?
 		    "(null)" : key->cert->signature_type, ssh_err(r));
 		return 0;
 	}
 
 	return 1;
 }
 
 static int
 sockaddr_is_local(struct sockaddr *hostaddr)
 {
 	switch (hostaddr->sa_family) {
 	case AF_INET:
 		return (ntohl(((struct sockaddr_in *)hostaddr)->
 		    sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
 	case AF_INET6:
 		return IN6_IS_ADDR_LOOPBACK(
 		    &(((struct sockaddr_in6 *)hostaddr)->sin6_addr));
 	default:
 		return 0;
 	}
 }
 
 /*
  * Prepare the hostname and ip address strings that are used to lookup
  * host keys in known_hosts files. These may have a port number appended.
  */
 void
 get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr,
     u_short port, char **hostfile_hostname, char **hostfile_ipaddr)
 {
 	char ntop[NI_MAXHOST];
 	socklen_t addrlen;
 
 	switch (hostaddr == NULL ? -1 : hostaddr->sa_family) {
 	case -1:
 		addrlen = 0;
 		break;
 	case AF_INET:
 		addrlen = sizeof(struct sockaddr_in);
 		break;
 	case AF_INET6:
 		addrlen = sizeof(struct sockaddr_in6);
 		break;
 	default:
 		addrlen = sizeof(struct sockaddr);
 		break;
 	}
 
 	/*
 	 * We don't have the remote ip-address for connections
 	 * using a proxy command
 	 */
 	if (hostfile_ipaddr != NULL) {
 		if (options.proxy_command == NULL) {
 			if (getnameinfo(hostaddr, addrlen,
 			    ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0)
 			fatal("%s: getnameinfo failed", __func__);
 			*hostfile_ipaddr = put_host_port(ntop, port);
 		} else {
 			*hostfile_ipaddr = xstrdup("<no hostip for proxy "
 			    "command>");
 		}
 	}
 
 	/*
 	 * Allow the user to record the key under a different name or
 	 * differentiate a non-standard port.  This is useful for ssh
 	 * tunneling over forwarded connections or if you run multiple
 	 * sshd's on different ports on the same machine.
 	 */
 	if (hostfile_hostname != NULL) {
 		if (options.host_key_alias != NULL) {
 			*hostfile_hostname = xstrdup(options.host_key_alias);
 			debug("using hostkeyalias: %s", *hostfile_hostname);
 		} else {
 			*hostfile_hostname = put_host_port(hostname, port);
 		}
 	}
 }
 
 /*
  * check whether the supplied host key is valid, return -1 if the key
  * is not valid. user_hostfile[0] will not be updated if 'readonly' is true.
  */
 #define RDRW	0
 #define RDONLY	1
 #define ROQUIET	2
 static int
 check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
     struct sshkey *host_key, int readonly,
     char **user_hostfiles, u_int num_user_hostfiles,
     char **system_hostfiles, u_int num_system_hostfiles)
 {
 	HostStatus host_status;
 	HostStatus ip_status;
 	struct sshkey *raw_key = NULL;
 	char *ip = NULL, *host = NULL;
 	char hostline[1000], *hostp, *fp, *ra;
 	char msg[1024];
 	const char *type;
 	const struct hostkey_entry *host_found, *ip_found;
 	int len, cancelled_forwarding = 0;
 	int local = sockaddr_is_local(hostaddr);
 	int r, want_cert = sshkey_is_cert(host_key), host_ip_differ = 0;
 	int hostkey_trusted = 0; /* Known or explicitly accepted by user */
 	struct hostkeys *host_hostkeys, *ip_hostkeys;
 	u_int i;
 
 	/*
 	 * Force accepting of the host key for loopback/localhost. The
 	 * problem is that if the home directory is NFS-mounted to multiple
 	 * machines, localhost will refer to a different machine in each of
 	 * them, and the user will get bogus HOST_CHANGED warnings.  This
 	 * essentially disables host authentication for localhost; however,
 	 * this is probably not a real problem.
 	 */
 	if (options.no_host_authentication_for_localhost == 1 && local &&
 	    options.host_key_alias == NULL) {
 		debug("Forcing accepting of host key for "
 		    "loopback/localhost.");
 		return 0;
 	}
 
 	/*
 	 * Prepare the hostname and address strings used for hostkey lookup.
 	 * In some cases, these will have a port number appended.
 	 */
 	get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip);
 
 	/*
 	 * Turn off check_host_ip if the connection is to localhost, via proxy
 	 * command or if we don't have a hostname to compare with
 	 */
 	if (options.check_host_ip && (local ||
 	    strcmp(hostname, ip) == 0 || options.proxy_command != NULL))
 		options.check_host_ip = 0;
 
 	host_hostkeys = init_hostkeys();
 	for (i = 0; i < num_user_hostfiles; i++)
 		load_hostkeys(host_hostkeys, host, user_hostfiles[i]);
 	for (i = 0; i < num_system_hostfiles; i++)
 		load_hostkeys(host_hostkeys, host, system_hostfiles[i]);
 
 	ip_hostkeys = NULL;
 	if (!want_cert && options.check_host_ip) {
 		ip_hostkeys = init_hostkeys();
 		for (i = 0; i < num_user_hostfiles; i++)
 			load_hostkeys(ip_hostkeys, ip, user_hostfiles[i]);
 		for (i = 0; i < num_system_hostfiles; i++)
 			load_hostkeys(ip_hostkeys, ip, system_hostfiles[i]);
 	}
 
  retry:
 	/* Reload these as they may have changed on cert->key downgrade */
 	want_cert = sshkey_is_cert(host_key);
 	type = sshkey_type(host_key);
 
 	/*
 	 * Check if the host key is present in the user's list of known
 	 * hosts or in the systemwide list.
 	 */
 	host_status = check_key_in_hostkeys(host_hostkeys, host_key,
 	    &host_found);
 
 	/*
 	 * Also perform check for the ip address, skip the check if we are
 	 * localhost, looking for a certificate, or the hostname was an ip
 	 * address to begin with.
 	 */
 	if (!want_cert && ip_hostkeys != NULL) {
 		ip_status = check_key_in_hostkeys(ip_hostkeys, host_key,
 		    &ip_found);
 		if (host_status == HOST_CHANGED &&
 		    (ip_status != HOST_CHANGED || 
 		    (ip_found != NULL &&
 		    !sshkey_equal(ip_found->key, host_found->key))))
 			host_ip_differ = 1;
 	} else
 		ip_status = host_status;
 
 	switch (host_status) {
 	case HOST_OK:
 		/* The host is known and the key matches. */
 		debug("Host '%.200s' is known and matches the %s host %s.",
 		    host, type, want_cert ? "certificate" : "key");
 		debug("Found %s in %s:%lu", want_cert ? "CA key" : "key",
 		    host_found->file, host_found->line);
 		if (want_cert &&
 		    !check_host_cert(options.host_key_alias == NULL ?
 		    hostname : options.host_key_alias, host_key))
 			goto fail;
 		if (options.check_host_ip && ip_status == HOST_NEW) {
 			if (readonly || want_cert)
 				logit("%s host key for IP address "
 				    "'%.128s' not in list of known hosts.",
 				    type, ip);
 			else if (!add_host_to_hostfile(user_hostfiles[0], ip,
 			    host_key, options.hash_known_hosts))
 				logit("Failed to add the %s host key for IP "
 				    "address '%.128s' to the list of known "
 				    "hosts (%.500s).", type, ip,
 				    user_hostfiles[0]);
 			else
 				logit("Warning: Permanently added the %s host "
 				    "key for IP address '%.128s' to the list "
 				    "of known hosts.", type, ip);
 		} else if (options.visual_host_key) {
 			fp = sshkey_fingerprint(host_key,
 			    options.fingerprint_hash, SSH_FP_DEFAULT);
 			ra = sshkey_fingerprint(host_key,
 			    options.fingerprint_hash, SSH_FP_RANDOMART);
 			if (fp == NULL || ra == NULL)
 				fatal("%s: sshkey_fingerprint fail", __func__);
 			logit("Host key fingerprint is %s\n%s", fp, ra);
 			free(ra);
 			free(fp);
 		}
 		hostkey_trusted = 1;
 		break;
 	case HOST_NEW:
 		if (options.host_key_alias == NULL && port != 0 &&
 		    port != SSH_DEFAULT_PORT) {
 			debug("checking without port identifier");
 			if (check_host_key(hostname, hostaddr, 0, host_key,
 			    ROQUIET, user_hostfiles, num_user_hostfiles,
 			    system_hostfiles, num_system_hostfiles) == 0) {
 				debug("found matching key w/out port");
 				break;
 			}
 		}
 		if (readonly || want_cert)
 			goto fail;
 		/* The host is new. */
 		if (options.strict_host_key_checking ==
 		    SSH_STRICT_HOSTKEY_YES) {
 			/*
 			 * User has requested strict host key checking.  We
 			 * will not add the host key automatically.  The only
 			 * alternative left is to abort.
 			 */
 			error("No %s host key is known for %.200s and you "
 			    "have requested strict checking.", type, host);
 			goto fail;
 		} else if (options.strict_host_key_checking ==
 		    SSH_STRICT_HOSTKEY_ASK) {
 			char msg1[1024], msg2[1024];
 
 			if (show_other_keys(host_hostkeys, host_key))
 				snprintf(msg1, sizeof(msg1),
 				    "\nbut keys of different type are already"
 				    " known for this host.");
 			else
 				snprintf(msg1, sizeof(msg1), ".");
 			/* The default */
 			fp = sshkey_fingerprint(host_key,
 			    options.fingerprint_hash, SSH_FP_DEFAULT);
 			ra = sshkey_fingerprint(host_key,
 			    options.fingerprint_hash, SSH_FP_RANDOMART);
 			if (fp == NULL || ra == NULL)
 				fatal("%s: sshkey_fingerprint fail", __func__);
 			msg2[0] = '\0';
 			if (options.verify_host_key_dns) {
 				if (matching_host_key_dns)
 					snprintf(msg2, sizeof(msg2),
 					    "Matching host key fingerprint"
 					    " found in DNS.\n");
 				else
 					snprintf(msg2, sizeof(msg2),
 					    "No matching host key fingerprint"
 					    " found in DNS.\n");
 			}
 			snprintf(msg, sizeof(msg),
 			    "The authenticity of host '%.200s (%s)' can't be "
 			    "established%s\n"
 			    "%s key fingerprint is %s.%s%s\n%s"
 			    "Are you sure you want to continue connecting "
 			    "(yes/no)? ",
 			    host, ip, msg1, type, fp,
 			    options.visual_host_key ? "\n" : "",
 			    options.visual_host_key ? ra : "",
 			    msg2);
 			free(ra);
 			free(fp);
 			if (!confirm(msg))
 				goto fail;
 			hostkey_trusted = 1; /* user explicitly confirmed */
 		}
 		/*
 		 * If in "new" or "off" strict mode, add the key automatically
 		 * to the local known_hosts file.
 		 */
 		if (options.check_host_ip && ip_status == HOST_NEW) {
 			snprintf(hostline, sizeof(hostline), "%s,%s", host, ip);
 			hostp = hostline;
 			if (options.hash_known_hosts) {
 				/* Add hash of host and IP separately */
 				r = add_host_to_hostfile(user_hostfiles[0],
 				    host, host_key, options.hash_known_hosts) &&
 				    add_host_to_hostfile(user_hostfiles[0], ip,
 				    host_key, options.hash_known_hosts);
 			} else {
 				/* Add unhashed "host,ip" */
 				r = add_host_to_hostfile(user_hostfiles[0],
 				    hostline, host_key,
 				    options.hash_known_hosts);
 			}
 		} else {
 			r = add_host_to_hostfile(user_hostfiles[0], host,
 			    host_key, options.hash_known_hosts);
 			hostp = host;
 		}
 
 		if (!r)
 			logit("Failed to add the host to the list of known "
 			    "hosts (%.500s).", user_hostfiles[0]);
 		else
 			logit("Warning: Permanently added '%.200s' (%s) to the "
 			    "list of known hosts.", hostp, type);
 		break;
 	case HOST_REVOKED:
 		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
 		error("@       WARNING: REVOKED HOST KEY DETECTED!               @");
 		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
 		error("The %s host key for %s is marked as revoked.", type, host);
 		error("This could mean that a stolen key is being used to");
 		error("impersonate this host.");
 
 		/*
 		 * If strict host key checking is in use, the user will have
 		 * to edit the key manually and we can only abort.
 		 */
 		if (options.strict_host_key_checking !=
 		    SSH_STRICT_HOSTKEY_OFF) {
 			error("%s host key for %.200s was revoked and you have "
 			    "requested strict checking.", type, host);
 			goto fail;
 		}
 		goto continue_unsafe;
 
 	case HOST_CHANGED:
 		if (want_cert) {
 			/*
 			 * This is only a debug() since it is valid to have
 			 * CAs with wildcard DNS matches that don't match
 			 * all hosts that one might visit.
 			 */
 			debug("Host certificate authority does not "
 			    "match %s in %s:%lu", CA_MARKER,
 			    host_found->file, host_found->line);
 			goto fail;
 		}
 		if (readonly == ROQUIET)
 			goto fail;
 		if (options.check_host_ip && host_ip_differ) {
 			char *key_msg;
 			if (ip_status == HOST_NEW)
 				key_msg = "is unknown";
 			else if (ip_status == HOST_OK)
 				key_msg = "is unchanged";
 			else
 				key_msg = "has a different value";
 			error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
 			error("@       WARNING: POSSIBLE DNS SPOOFING DETECTED!          @");
 			error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
 			error("The %s host key for %s has changed,", type, host);
 			error("and the key for the corresponding IP address %s", ip);
 			error("%s. This could either mean that", key_msg);
 			error("DNS SPOOFING is happening or the IP address for the host");
 			error("and its host key have changed at the same time.");
 			if (ip_status != HOST_NEW)
 				error("Offending key for IP in %s:%lu",
 				    ip_found->file, ip_found->line);
 		}
 		/* The host key has changed. */
 		warn_changed_key(host_key);
 		error("Add correct host key in %.100s to get rid of this message.",
 		    user_hostfiles[0]);
 		error("Offending %s key in %s:%lu",
 		    sshkey_type(host_found->key),
 		    host_found->file, host_found->line);
 
 		/*
 		 * If strict host key checking is in use, the user will have
 		 * to edit the key manually and we can only abort.
 		 */
 		if (options.strict_host_key_checking !=
 		    SSH_STRICT_HOSTKEY_OFF) {
 			error("%s host key for %.200s has changed and you have "
 			    "requested strict checking.", type, host);
 			goto fail;
 		}
 
  continue_unsafe:
 		/*
 		 * If strict host key checking has not been requested, allow
 		 * the connection but without MITM-able authentication or
 		 * forwarding.
 		 */
 		if (options.password_authentication) {
 			error("Password authentication is disabled to avoid "
 			    "man-in-the-middle attacks.");
 			options.password_authentication = 0;
 			cancelled_forwarding = 1;
 		}
 		if (options.kbd_interactive_authentication) {
 			error("Keyboard-interactive authentication is disabled"
 			    " to avoid man-in-the-middle attacks.");
 			options.kbd_interactive_authentication = 0;
 			options.challenge_response_authentication = 0;
 			cancelled_forwarding = 1;
 		}
 		if (options.challenge_response_authentication) {
 			error("Challenge/response authentication is disabled"
 			    " to avoid man-in-the-middle attacks.");
 			options.challenge_response_authentication = 0;
 			cancelled_forwarding = 1;
 		}
 		if (options.forward_agent) {
 			error("Agent forwarding is disabled to avoid "
 			    "man-in-the-middle attacks.");
 			options.forward_agent = 0;
 			cancelled_forwarding = 1;
 		}
 		if (options.forward_x11) {
 			error("X11 forwarding is disabled to avoid "
 			    "man-in-the-middle attacks.");
 			options.forward_x11 = 0;
 			cancelled_forwarding = 1;
 		}
 		if (options.num_local_forwards > 0 ||
 		    options.num_remote_forwards > 0) {
 			error("Port forwarding is disabled to avoid "
 			    "man-in-the-middle attacks.");
 			options.num_local_forwards =
 			    options.num_remote_forwards = 0;
 			cancelled_forwarding = 1;
 		}
 		if (options.tun_open != SSH_TUNMODE_NO) {
 			error("Tunnel forwarding is disabled to avoid "
 			    "man-in-the-middle attacks.");
 			options.tun_open = SSH_TUNMODE_NO;
 			cancelled_forwarding = 1;
 		}
 		if (options.exit_on_forward_failure && cancelled_forwarding)
 			fatal("Error: forwarding disabled due to host key "
 			    "check failure");
 		
 		/*
 		 * XXX Should permit the user to change to use the new id.
 		 * This could be done by converting the host key to an
 		 * identifying sentence, tell that the host identifies itself
 		 * by that sentence, and ask the user if he/she wishes to
 		 * accept the authentication.
 		 */
 		break;
 	case HOST_FOUND:
 		fatal("internal error");
 		break;
 	}
 
 	if (options.check_host_ip && host_status != HOST_CHANGED &&
 	    ip_status == HOST_CHANGED) {
 		snprintf(msg, sizeof(msg),
 		    "Warning: the %s host key for '%.200s' "
 		    "differs from the key for the IP address '%.128s'"
 		    "\nOffending key for IP in %s:%lu",
 		    type, host, ip, ip_found->file, ip_found->line);
 		if (host_status == HOST_OK) {
 			len = strlen(msg);
 			snprintf(msg + len, sizeof(msg) - len,
 			    "\nMatching host key in %s:%lu",
 			    host_found->file, host_found->line);
 		}
 		if (options.strict_host_key_checking ==
 		    SSH_STRICT_HOSTKEY_ASK) {
 			strlcat(msg, "\nAre you sure you want "
 			    "to continue connecting (yes/no)? ", sizeof(msg));
 			if (!confirm(msg))
 				goto fail;
 		} else if (options.strict_host_key_checking !=
 		    SSH_STRICT_HOSTKEY_OFF) {
 			logit("%s", msg);
 			error("Exiting, you have requested strict checking.");
 			goto fail;
 		} else {
 			logit("%s", msg);
 		}
 	}
 
 	if (!hostkey_trusted && options.update_hostkeys) {
 		debug("%s: hostkey not known or explicitly trusted: "
 		    "disabling UpdateHostkeys", __func__);
 		options.update_hostkeys = 0;
 	}
 
 	free(ip);
 	free(host);
 	if (host_hostkeys != NULL)
 		free_hostkeys(host_hostkeys);
 	if (ip_hostkeys != NULL)
 		free_hostkeys(ip_hostkeys);
 	return 0;
 
 fail:
 	if (want_cert && host_status != HOST_REVOKED) {
 		/*
 		 * No matching certificate. Downgrade cert to raw key and
 		 * search normally.
 		 */
 		debug("No matching CA found. Retry with plain key");
 		if ((r = sshkey_from_private(host_key, &raw_key)) != 0)
 			fatal("%s: sshkey_from_private: %s",
 			    __func__, ssh_err(r));
 		if ((r = sshkey_drop_cert(raw_key)) != 0)
 			fatal("Couldn't drop certificate: %s", ssh_err(r));
 		host_key = raw_key;
 		goto retry;
 	}
 	sshkey_free(raw_key);
 	free(ip);
 	free(host);
 	if (host_hostkeys != NULL)
 		free_hostkeys(host_hostkeys);
 	if (ip_hostkeys != NULL)
 		free_hostkeys(ip_hostkeys);
 	return -1;
 }
 
 /* returns 0 if key verifies or -1 if key does NOT verify */
 int
 verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key)
 {
 	u_int i;
 	int r = -1, flags = 0;
 	char valid[64], *fp = NULL, *cafp = NULL;
 	struct sshkey *plain = NULL;
 
 	if ((fp = sshkey_fingerprint(host_key,
 	    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
 		error("%s: fingerprint host key: %s", __func__, ssh_err(r));
 		r = -1;
 		goto out;
 	}
 
 	if (sshkey_is_cert(host_key)) {
 		if ((cafp = sshkey_fingerprint(host_key->cert->signature_key,
 		    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
 			error("%s: fingerprint CA key: %s",
 			    __func__, ssh_err(r));
 			r = -1;
 			goto out;
 		}
 		sshkey_format_cert_validity(host_key->cert,
 		    valid, sizeof(valid));
 		debug("Server host certificate: %s %s, serial %llu "
 		    "ID \"%s\" CA %s %s valid %s",
 		    sshkey_ssh_name(host_key), fp,
 		    (unsigned long long)host_key->cert->serial,
 		    host_key->cert->key_id,
 		    sshkey_ssh_name(host_key->cert->signature_key), cafp,
 		    valid);
 		for (i = 0; i < host_key->cert->nprincipals; i++) {
 			debug2("Server host certificate hostname: %s",
 			    host_key->cert->principals[i]);
 		}
 	} else {
 		debug("Server host key: %s %s", sshkey_ssh_name(host_key), fp);
 	}
 
 	if (sshkey_equal(previous_host_key, host_key)) {
 		debug2("%s: server host key %s %s matches cached key",
 		    __func__, sshkey_type(host_key), fp);
 		r = 0;
 		goto out;
 	}
 
 	/* Check in RevokedHostKeys file if specified */
 	if (options.revoked_host_keys != NULL) {
 		r = sshkey_check_revoked(host_key, options.revoked_host_keys);
 		switch (r) {
 		case 0:
 			break; /* not revoked */
 		case SSH_ERR_KEY_REVOKED:
 			error("Host key %s %s revoked by file %s",
 			    sshkey_type(host_key), fp,
 			    options.revoked_host_keys);
 			r = -1;
 			goto out;
 		default:
 			error("Error checking host key %s %s in "
 			    "revoked keys file %s: %s", sshkey_type(host_key),
 			    fp, options.revoked_host_keys, ssh_err(r));
 			r = -1;
 			goto out;
 		}
 	}
 
 	if (options.verify_host_key_dns) {
 		/*
 		 * XXX certs are not yet supported for DNS, so downgrade
 		 * them and try the plain key.
 		 */
 		if ((r = sshkey_from_private(host_key, &plain)) != 0)
 			goto out;
 		if (sshkey_is_cert(plain))
 			sshkey_drop_cert(plain);
 		if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) {
 			if (flags & DNS_VERIFY_FOUND) {
 				if (options.verify_host_key_dns == 1 &&
 				    flags & DNS_VERIFY_MATCH &&
 				    flags & DNS_VERIFY_SECURE) {
 					r = 0;
 					goto out;
 				}
 				if (flags & DNS_VERIFY_MATCH) {
 					matching_host_key_dns = 1;
 				} else {
 					warn_changed_key(plain);
 					error("Update the SSHFP RR in DNS "
 					    "with the new host key to get rid "
 					    "of this message.");
 				}
 			}
 		}
 	}
 	r = check_host_key(host, hostaddr, options.port, host_key, RDRW,
 	    options.user_hostfiles, options.num_user_hostfiles,
 	    options.system_hostfiles, options.num_system_hostfiles);
 
 out:
 	sshkey_free(plain);
 	free(fp);
 	free(cafp);
 	if (r == 0 && host_key != NULL) {
 		sshkey_free(previous_host_key);
 		r = sshkey_from_private(host_key, &previous_host_key);
 	}
 
 	return r;
 }
 
 /*
  * Starts a dialog with the server, and authenticates the current user on the
  * server.  This does not need any extra privileges.  The basic connection
  * to the server must already have been established before this is called.
  * If login fails, this function prints an error and never returns.
  * This function does not require super-user privileges.
  */
 void
 ssh_login(Sensitive *sensitive, const char *orighost,
     struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms)
 {
 	char *host;
 	char *server_user, *local_user;
 
 	local_user = xstrdup(pw->pw_name);
 	server_user = options.user ? options.user : local_user;
 
 	/* Convert the user-supplied hostname into all lowercase. */
 	host = xstrdup(orighost);
 	lowercase(host);
 
 	/* Exchange protocol version identification strings with the server. */
 	ssh_exchange_identification(timeout_ms);
 
 	/* Put the connection into non-blocking mode. */
 	packet_set_nonblocking();
 
 	/* key exchange */
 	/* authenticate user */
 	debug("Authenticating to %s:%d as '%s'", host, port, server_user);
 	ssh_kex2(host, hostaddr, port);
 	ssh_userauth2(local_user, server_user, host, sensitive);
 	free(local_user);
 }
 
 void
 ssh_put_password(char *password)
 {
 	int size;
 	char *padded;
 
 	if (datafellows & SSH_BUG_PASSWORDPAD) {
 		packet_put_cstring(password);
 		return;
 	}
 	size = ROUNDUP(strlen(password) + 1, 32);
 	padded = xcalloc(1, size);
 	strlcpy(padded, password, size);
 	packet_put_string(padded, size);
 	explicit_bzero(padded, size);
 	free(padded);
 }
 
 /* print all known host keys for a given host, but skip keys of given type */
 static int
 show_other_keys(struct hostkeys *hostkeys, struct sshkey *key)
 {
 	int type[] = {
 		KEY_RSA,
 		KEY_DSA,
 		KEY_ECDSA,
 		KEY_ED25519,
 		KEY_XMSS,
 		-1
 	};
 	int i, ret = 0;
 	char *fp, *ra;
 	const struct hostkey_entry *found;
 
 	for (i = 0; type[i] != -1; i++) {
 		if (type[i] == key->type)
 			continue;
 		if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found))
 			continue;
 		fp = sshkey_fingerprint(found->key,
 		    options.fingerprint_hash, SSH_FP_DEFAULT);
 		ra = sshkey_fingerprint(found->key,
 		    options.fingerprint_hash, SSH_FP_RANDOMART);
 		if (fp == NULL || ra == NULL)
 			fatal("%s: sshkey_fingerprint fail", __func__);
 		logit("WARNING: %s key found for host %s\n"
 		    "in %s:%lu\n"
 		    "%s key fingerprint %s.",
 		    sshkey_type(found->key),
 		    found->host, found->file, found->line,
 		    sshkey_type(found->key), fp);
 		if (options.visual_host_key)
 			logit("%s", ra);
 		free(ra);
 		free(fp);
 		ret = 1;
 	}
 	return ret;
 }
 
 static void
 warn_changed_key(struct sshkey *host_key)
 {
 	char *fp;
 
 	fp = sshkey_fingerprint(host_key, options.fingerprint_hash,
 	    SSH_FP_DEFAULT);
 	if (fp == NULL)
 		fatal("%s: sshkey_fingerprint fail", __func__);
 
 	error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
 	error("@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @");
 	error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
 	error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!");
 	error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!");
 	error("It is also possible that a host key has just been changed.");
 	error("The fingerprint for the %s key sent by the remote host is\n%s.",
 	    sshkey_type(host_key), fp);
 	error("Please contact your system administrator.");
 
 	free(fp);
 }
 
 /*
  * Execute a local command
  */
 int
 ssh_local_cmd(const char *args)
 {
 	char *shell;
 	pid_t pid;
 	int status;
 	void (*osighand)(int);
 
 	if (!options.permit_local_command ||
 	    args == NULL || !*args)
 		return (1);
 
 	if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
 		shell = _PATH_BSHELL;
 
 	osighand = signal(SIGCHLD, SIG_DFL);
 	pid = fork();
 	if (pid == 0) {
 		signal(SIGPIPE, SIG_DFL);
 		debug3("Executing %s -c \"%s\"", shell, args);
 		execl(shell, shell, "-c", args, (char *)NULL);
 		error("Couldn't execute %s -c \"%s\": %s",
 		    shell, args, strerror(errno));
 		_exit(1);
 	} else if (pid == -1)
 		fatal("fork failed: %.100s", strerror(errno));
 	while (waitpid(pid, &status, 0) == -1)
 		if (errno != EINTR)
 			fatal("Couldn't wait for child: %s", strerror(errno));
 	signal(SIGCHLD, osighand);
 
 	if (!WIFEXITED(status))
 		return (1);
 
 	return (WEXITSTATUS(status));
 }
 
 void
 maybe_add_key_to_agent(char *authfile, const struct sshkey *private,
     char *comment, char *passphrase)
 {
 	int auth_sock = -1, r;
 
 	if (options.add_keys_to_agent == 0)
 		return;
 
 	if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) {
 		debug3("no authentication agent, not adding key");
 		return;
 	}
 
 	if (options.add_keys_to_agent == 2 &&
 	    !ask_permission("Add key %s (%s) to agent?", authfile, comment)) {
 		debug3("user denied adding this key");
 		close(auth_sock);
 		return;
 	}
 
 	if ((r = ssh_add_identity_constrained(auth_sock, private, comment, 0,
 	    (options.add_keys_to_agent == 3), 0)) == 0)
 		debug("identity added to agent: %s", authfile);
 	else
 		debug("could not add identity to agent: %s (%d)", authfile, r);
 	close(auth_sock);
 }