upstream commit

when hostname canonicalisation is enabled, try to parse
 hostnames as addresses before looking them up for canonicalisation. fixes
 bz#2074 and avoids needless DNS lookups in some cases; ok markus
This commit is contained in:
djm@openbsd.org 2015-01-16 07:19:48 +00:00 committed by Damien Miller
parent 2ae4f337b2
commit 9010902954
1 changed files with 72 additions and 5 deletions

77
ssh.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh.c,v 1.412 2015/01/14 20:05:27 djm Exp $ */
/* $OpenBSD: ssh.c,v 1.413 2015/01/16 07:19:48 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -276,6 +276,60 @@ resolve_host(const char *name, int port, int logerr, char *cname, size_t clen)
return res;
}
/*
* Attempt to resolve a numeric host address / port to a single address.
* Returns a canonical address string.
* Returns NULL on failure.
* NB. this function must operate with a options having undefined members.
*/
static struct addrinfo *
resolve_addr(const char *name, int port, char *caddr, size_t clen)
{
char addr[NI_MAXHOST], strport[NI_MAXSERV];
struct addrinfo hints, *res;
int gaierr;
if (port <= 0)
port = default_ssh_port();
snprintf(strport, sizeof strport, "%u", port);
memset(&hints, 0, sizeof(hints));
hints.ai_family = options.address_family == -1 ?
AF_UNSPEC : options.address_family;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV;
if ((gaierr = getaddrinfo(name, strport, &hints, &res)) != 0) {
debug2("%s: could not resolve name %.100s as address: %s",
__func__, name, ssh_gai_strerror(gaierr));
return NULL;
}
if (res == NULL) {
debug("%s: getaddrinfo %.100s returned no addresses",
__func__, name);
return NULL;
}
if (res->ai_next != NULL) {
debug("%s: getaddrinfo %.100s returned multiple addresses",
__func__, name);
goto fail;
}
if ((gaierr = getnameinfo(res->ai_addr, res->ai_addrlen,
addr, sizeof(addr), NULL, 0, NI_NUMERICHOST)) != 0) {
debug("%s: Could not format address for name %.100s: %s",
__func__, name, ssh_gai_strerror(gaierr));
goto fail;
}
if (strlcpy(caddr, addr, clen) >= clen) {
error("%s: host \"%s\" addr \"%s\" too long (max %lu)",
__func__, name, addr, (u_long)clen);
if (clen > 0)
*caddr = '\0';
fail:
freeaddrinfo(res);
return NULL;
}
return res;
}
/*
* Check whether the cname is a permitted replacement for the hostname
* and perform the replacement if it is.
@ -326,7 +380,7 @@ static struct addrinfo *
resolve_canonicalize(char **hostp, int port)
{
int i, ndots;
char *cp, *fullhost, cname_target[NI_MAXHOST];
char *cp, *fullhost, newname[NI_MAXHOST];
struct addrinfo *addrs;
if (options.canonicalize_hostname == SSH_CANONICALISE_NO)
@ -340,6 +394,19 @@ resolve_canonicalize(char **hostp, int port)
options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS)
return NULL;
/* Try numeric hostnames first */
if ((addrs = resolve_addr(*hostp, port,
newname, sizeof(newname))) != NULL) {
debug2("%s: hostname %.100s is address", __func__, *hostp);
if (strcasecmp(*hostp, newname) != 0) {
debug2("%s: canonicalised address \"%s\" => \"%s\"",
__func__, *hostp, newname);
free(*hostp);
*hostp = xstrdup(newname);
}
return addrs;
}
/* Don't apply canonicalization to sufficiently-qualified hostnames */
ndots = 0;
for (cp = *hostp; *cp != '\0'; cp++) {
@ -353,20 +420,20 @@ resolve_canonicalize(char **hostp, int port)
}
/* Attempt each supplied suffix */
for (i = 0; i < options.num_canonical_domains; i++) {
*cname_target = '\0';
*newname = '\0';
xasprintf(&fullhost, "%s.%s.", *hostp,
options.canonical_domains[i]);
debug3("%s: attempting \"%s\" => \"%s\"", __func__,
*hostp, fullhost);
if ((addrs = resolve_host(fullhost, port, 0,
cname_target, sizeof(cname_target))) == NULL) {
newname, sizeof(newname))) == NULL) {
free(fullhost);
continue;
}
/* Remove trailing '.' */
fullhost[strlen(fullhost) - 1] = '\0';
/* Follow CNAME if requested */
if (!check_follow_cname(&fullhost, cname_target)) {
if (!check_follow_cname(&fullhost, newname)) {
debug("Canonicalized hostname \"%s\" => \"%s\"",
*hostp, fullhost);
}