diff options
author | djm@openbsd.org <djm@openbsd.org> | 2015-01-16 07:19:48 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2015-01-16 18:24:49 +1100 |
commit | 9010902954a40b59d0bf3df3ccbc3140a653e2bc (patch) | |
tree | 59cf3684ef95f7806e60c10f88b4e8ed4cefdc7d /ssh.c | |
parent | 2ae4f337b2a5fb2841b6b0053b49496fef844d1c (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
Diffstat (limited to 'ssh.c')
-rw-r--r-- | ssh.c | 77 |
1 files changed, 72 insertions, 5 deletions
@@ -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 | */ | ||
285 | static struct addrinfo * | ||
286 | resolve_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 * | |||
326 | resolve_canonicalize(char **hostp, int port) | 380 | resolve_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 | } |