summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2015-01-16 07:19:48 +0000
committerDamien Miller <djm@mindrot.org>2015-01-16 18:24:49 +1100
commit9010902954a40b59d0bf3df3ccbc3140a653e2bc (patch)
tree59cf3684ef95f7806e60c10f88b4e8ed4cefdc7d
parent2ae4f337b2a5fb2841b6b0053b49496fef844d1c (diff)
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
-rw-r--r--ssh.c77
1 files changed, 72 insertions, 5 deletions
diff --git a/ssh.c b/ssh.c
index df7274f4c..5190f1f9b 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh.c,v 1.412 2015/01/14 20:05:27 djm Exp $ */ 1/* $OpenBSD: ssh.c,v 1.413 2015/01/16 07:19:48 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -277,6 +277,60 @@ resolve_host(const char *name, int port, int logerr, char *cname, size_t clen)
277} 277}
278 278
279/* 279/*
280 * Attempt to resolve a numeric host address / port to a single address.
281 * Returns a canonical address string.
282 * Returns NULL on failure.
283 * NB. this function must operate with a options having undefined members.
284 */
285static struct addrinfo *
286resolve_addr(const char *name, int port, char *caddr, size_t clen)
287{
288 char addr[NI_MAXHOST], strport[NI_MAXSERV];
289 struct addrinfo hints, *res;
290 int gaierr;
291
292 if (port <= 0)
293 port = default_ssh_port();
294 snprintf(strport, sizeof strport, "%u", port);
295 memset(&hints, 0, sizeof(hints));
296 hints.ai_family = options.address_family == -1 ?
297 AF_UNSPEC : options.address_family;
298 hints.ai_socktype = SOCK_STREAM;
299 hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV;
300 if ((gaierr = getaddrinfo(name, strport, &hints, &res)) != 0) {
301 debug2("%s: could not resolve name %.100s as address: %s",
302 __func__, name, ssh_gai_strerror(gaierr));
303 return NULL;
304 }
305 if (res == NULL) {
306 debug("%s: getaddrinfo %.100s returned no addresses",
307 __func__, name);
308 return NULL;
309 }
310 if (res->ai_next != NULL) {
311 debug("%s: getaddrinfo %.100s returned multiple addresses",
312 __func__, name);
313 goto fail;
314 }
315 if ((gaierr = getnameinfo(res->ai_addr, res->ai_addrlen,
316 addr, sizeof(addr), NULL, 0, NI_NUMERICHOST)) != 0) {
317 debug("%s: Could not format address for name %.100s: %s",
318 __func__, name, ssh_gai_strerror(gaierr));
319 goto fail;
320 }
321 if (strlcpy(caddr, addr, clen) >= clen) {
322 error("%s: host \"%s\" addr \"%s\" too long (max %lu)",
323 __func__, name, addr, (u_long)clen);
324 if (clen > 0)
325 *caddr = '\0';
326 fail:
327 freeaddrinfo(res);
328 return NULL;
329 }
330 return res;
331}
332
333/*
280 * Check whether the cname is a permitted replacement for the hostname 334 * Check whether the cname is a permitted replacement for the hostname
281 * and perform the replacement if it is. 335 * and perform the replacement if it is.
282 * NB. this function must operate with a options having undefined members. 336 * NB. this function must operate with a options having undefined members.
@@ -326,7 +380,7 @@ static struct addrinfo *
326resolve_canonicalize(char **hostp, int port) 380resolve_canonicalize(char **hostp, int port)
327{ 381{
328 int i, ndots; 382 int i, ndots;
329 char *cp, *fullhost, cname_target[NI_MAXHOST]; 383 char *cp, *fullhost, newname[NI_MAXHOST];
330 struct addrinfo *addrs; 384 struct addrinfo *addrs;
331 385
332 if (options.canonicalize_hostname == SSH_CANONICALISE_NO) 386 if (options.canonicalize_hostname == SSH_CANONICALISE_NO)
@@ -340,6 +394,19 @@ resolve_canonicalize(char **hostp, int port)
340 options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) 394 options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS)
341 return NULL; 395 return NULL;
342 396
397 /* Try numeric hostnames first */
398 if ((addrs = resolve_addr(*hostp, port,
399 newname, sizeof(newname))) != NULL) {
400 debug2("%s: hostname %.100s is address", __func__, *hostp);
401 if (strcasecmp(*hostp, newname) != 0) {
402 debug2("%s: canonicalised address \"%s\" => \"%s\"",
403 __func__, *hostp, newname);
404 free(*hostp);
405 *hostp = xstrdup(newname);
406 }
407 return addrs;
408 }
409
343 /* Don't apply canonicalization to sufficiently-qualified hostnames */ 410 /* Don't apply canonicalization to sufficiently-qualified hostnames */
344 ndots = 0; 411 ndots = 0;
345 for (cp = *hostp; *cp != '\0'; cp++) { 412 for (cp = *hostp; *cp != '\0'; cp++) {
@@ -353,20 +420,20 @@ resolve_canonicalize(char **hostp, int port)
353 } 420 }
354 /* Attempt each supplied suffix */ 421 /* Attempt each supplied suffix */
355 for (i = 0; i < options.num_canonical_domains; i++) { 422 for (i = 0; i < options.num_canonical_domains; i++) {
356 *cname_target = '\0'; 423 *newname = '\0';
357 xasprintf(&fullhost, "%s.%s.", *hostp, 424 xasprintf(&fullhost, "%s.%s.", *hostp,
358 options.canonical_domains[i]); 425 options.canonical_domains[i]);
359 debug3("%s: attempting \"%s\" => \"%s\"", __func__, 426 debug3("%s: attempting \"%s\" => \"%s\"", __func__,
360 *hostp, fullhost); 427 *hostp, fullhost);
361 if ((addrs = resolve_host(fullhost, port, 0, 428 if ((addrs = resolve_host(fullhost, port, 0,
362 cname_target, sizeof(cname_target))) == NULL) { 429 newname, sizeof(newname))) == NULL) {
363 free(fullhost); 430 free(fullhost);
364 continue; 431 continue;
365 } 432 }
366 /* Remove trailing '.' */ 433 /* Remove trailing '.' */
367 fullhost[strlen(fullhost) - 1] = '\0'; 434 fullhost[strlen(fullhost) - 1] = '\0';
368 /* Follow CNAME if requested */ 435 /* Follow CNAME if requested */
369 if (!check_follow_cname(&fullhost, cname_target)) { 436 if (!check_follow_cname(&fullhost, newname)) {
370 debug("Canonicalized hostname \"%s\" => \"%s\"", 437 debug("Canonicalized hostname \"%s\" => \"%s\"",
371 *hostp, fullhost); 438 *hostp, fullhost);
372 } 439 }