version 1.384, 2013/10/14 23:31:01 |
version 1.385, 2013/10/16 02:31:46 |
|
|
} |
} |
} |
} |
|
|
|
static struct addrinfo * |
|
resolve_host(const char *name, u_int port, int logerr, char *cname, size_t clen) |
|
{ |
|
char strport[NI_MAXSERV]; |
|
struct addrinfo hints, *res; |
|
int gaierr, loglevel = SYSLOG_LEVEL_DEBUG1; |
|
|
|
snprintf(strport, sizeof strport, "%u", port); |
|
bzero(&hints, sizeof(hints)); |
|
hints.ai_family = options.address_family; |
|
hints.ai_socktype = SOCK_STREAM; |
|
if (cname != NULL) |
|
hints.ai_flags = AI_CANONNAME; |
|
if ((gaierr = getaddrinfo(name, strport, &hints, &res)) != 0) { |
|
if (logerr || (gaierr != EAI_NONAME && gaierr != EAI_NODATA)) |
|
loglevel = SYSLOG_LEVEL_ERROR; |
|
do_log2(loglevel, "%s: Could not resolve hostname %.100s: %s", |
|
__progname, name, ssh_gai_strerror(gaierr)); |
|
return NULL; |
|
} |
|
if (cname != NULL && res->ai_canonname != NULL) { |
|
if (strlcpy(cname, res->ai_canonname, clen) >= clen) { |
|
error("%s: host \"%s\" cname \"%s\" too long (max %lu)", |
|
__func__, name, res->ai_canonname, (u_long)clen); |
|
if (clen > 0) |
|
*cname = '\0'; |
|
} |
|
} |
|
return res; |
|
} |
|
|
/* |
/* |
|
* Check whether the cname is a permitted replacement for the hostname |
|
* and perform the replacement if it is. |
|
*/ |
|
static int |
|
check_follow_cname(char **namep, const char *cname) |
|
{ |
|
int i; |
|
struct allowed_cname *rule; |
|
|
|
if (*cname == '\0' || options.num_permitted_cnames == 0 || |
|
strcmp(*namep, cname) == 0) |
|
return 0; |
|
if (options.canonicalise_hostname == SSH_CANONICALISE_NO) |
|
return 0; |
|
/* |
|
* Don't attempt to canonicalise names that will be interpreted by |
|
* a proxy unless the user specifically requests so. |
|
*/ |
|
if (options.proxy_command != NULL && |
|
options.canonicalise_hostname != SSH_CANONICALISE_ALWAYS) |
|
return 0; |
|
debug3("%s: check \"%s\" CNAME \"%s\"", __func__, *namep, cname); |
|
for (i = 0; i < options.num_permitted_cnames; i++) { |
|
rule = options.permitted_cnames + i; |
|
if (match_pattern_list(*namep, rule->source_list, |
|
strlen(rule->source_list), 1) != 1 || |
|
match_pattern_list(cname, rule->target_list, |
|
strlen(rule->target_list), 1) != 1) |
|
continue; |
|
verbose("Canonicalised DNS aliased hostname " |
|
"\"%s\" => \"%s\"", *namep, cname); |
|
free(*namep); |
|
*namep = xstrdup(cname); |
|
return 1; |
|
} |
|
return 0; |
|
} |
|
|
|
/* |
|
* Attempt to resolve the supplied hostname after applying the user's |
|
* canonicalisation rules. Returns the address list for the host or NULL |
|
* if no name was found after canonicalisation. |
|
*/ |
|
static struct addrinfo * |
|
resolve_canonicalise(char **hostp, u_int port) |
|
{ |
|
int i, ndots; |
|
char *cp, *fullhost, cname_target[NI_MAXHOST]; |
|
struct addrinfo *addrs; |
|
|
|
if (options.canonicalise_hostname == SSH_CANONICALISE_NO) |
|
return NULL; |
|
/* |
|
* Don't attempt to canonicalise names that will be interpreted by |
|
* a proxy unless the user specifically requests so. |
|
*/ |
|
if (options.proxy_command != NULL && |
|
options.canonicalise_hostname != SSH_CANONICALISE_ALWAYS) |
|
return NULL; |
|
/* Don't apply canonicalisation to sufficiently-qualified hostnames */ |
|
ndots = 0; |
|
for (cp = *hostp; *cp != '\0'; cp++) { |
|
if (*cp == '.') |
|
ndots++; |
|
} |
|
if (ndots > options.canonicalise_max_dots) { |
|
debug3("%s: not canonicalising hostname \"%s\" (max dots %d)", |
|
__func__, *hostp, options.canonicalise_max_dots); |
|
return NULL; |
|
} |
|
/* Attempt each supplied suffix */ |
|
for (i = 0; i < options.num_canonical_domains; i++) { |
|
*cname_target = '\0'; |
|
xasprintf(&fullhost, "%s.%s.", *hostp, |
|
options.canonical_domains[i]); |
|
if ((addrs = resolve_host(fullhost, options.port, 0, |
|
cname_target, sizeof(cname_target))) == NULL) { |
|
free(fullhost); |
|
continue; |
|
} |
|
/* Remove trailing '.' */ |
|
fullhost[strlen(fullhost) - 1] = '\0'; |
|
/* Follow CNAME if requested */ |
|
if (!check_follow_cname(&fullhost, cname_target)) { |
|
debug("Canonicalised hostname \"%s\" => \"%s\"", |
|
*hostp, fullhost); |
|
} |
|
free(*hostp); |
|
*hostp = fullhost; |
|
return addrs; |
|
} |
|
if (!options.canonicalise_fallback_local) |
|
fatal("%s: Could not resolve host \"%s\"", __progname, host); |
|
return NULL; |
|
} |
|
|
|
/* |
* Main program for the ssh client. |
* Main program for the ssh client. |
*/ |
*/ |
int |
int |
|
|
int i, r, opt, exit_status, use_syslog; |
int i, r, opt, exit_status, use_syslog; |
char *p, *cp, *line, *argv0, buf[MAXPATHLEN], *host_arg, *logfile; |
char *p, *cp, *line, *argv0, buf[MAXPATHLEN], *host_arg, *logfile; |
char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV]; |
char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV]; |
|
char cname[NI_MAXHOST]; |
struct stat st; |
struct stat st; |
struct passwd *pw; |
struct passwd *pw; |
int timeout_ms; |
int timeout_ms; |
extern int optind, optreset; |
extern int optind, optreset; |
extern char *optarg; |
extern char *optarg; |
Forward fwd; |
Forward fwd; |
|
struct addrinfo *addrs = NULL; |
|
|
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ |
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ |
sanitise_stdfd(); |
sanitise_stdfd(); |
|
|
usage(); |
usage(); |
options.user = p; |
options.user = p; |
*cp = '\0'; |
*cp = '\0'; |
host = ++cp; |
host = xstrdup(++cp); |
} else |
} else |
host = *av; |
host = xstrdup(*av); |
if (ac > 1) { |
if (ac > 1) { |
optind = optreset = 1; |
optind = optreset = 1; |
goto again; |
goto again; |
|
|
if (!host) |
if (!host) |
usage(); |
usage(); |
|
|
|
lowercase(host); |
|
host_arg = xstrdup(host); |
|
|
OpenSSL_add_all_algorithms(); |
OpenSSL_add_all_algorithms(); |
ERR_load_crypto_strings(); |
ERR_load_crypto_strings(); |
|
|
|
|
options.port = default_ssh_port(); |
options.port = default_ssh_port(); |
|
|
/* preserve host name given on command line for %n expansion */ |
/* preserve host name given on command line for %n expansion */ |
host_arg = host; |
|
if (options.hostname != NULL) { |
if (options.hostname != NULL) { |
host = percent_expand(options.hostname, |
cp = percent_expand(options.hostname, |
"h", host, (char *)NULL); |
"h", host, (char *)NULL); |
|
free(host); |
|
host = cp; |
} |
} |
|
|
|
/* If canonicalisation requested then try to apply it */ |
|
if (options.canonicalise_hostname != SSH_CANONICALISE_NO) |
|
addrs = resolve_canonicalise(&host, options.port); |
|
/* |
|
* If canonicalisation not requested, or if it failed then try to |
|
* resolve the bare hostname name using the system resolver's usual |
|
* search rules. |
|
*/ |
|
if (addrs == NULL) { |
|
if ((addrs = resolve_host(host, options.port, 1, |
|
cname, sizeof(cname))) == NULL) |
|
cleanup_exit(255); /* resolve_host logs the error */ |
|
check_follow_cname(&host, cname); |
|
} |
|
|
if (gethostname(thishost, sizeof(thishost)) == -1) |
if (gethostname(thishost, sizeof(thishost)) == -1) |
fatal("gethostname: %s", strerror(errno)); |
fatal("gethostname: %s", strerror(errno)); |
strlcpy(shorthost, thishost, sizeof(shorthost)); |
strlcpy(shorthost, thishost, sizeof(shorthost)); |
|
|
timeout_ms = options.connection_timeout * 1000; |
timeout_ms = options.connection_timeout * 1000; |
|
|
/* Open a connection to the remote host. */ |
/* Open a connection to the remote host. */ |
if (ssh_connect(host, &hostaddr, options.port, |
if (ssh_connect(host, addrs, &hostaddr, options.port, |
options.address_family, options.connection_attempts, &timeout_ms, |
options.address_family, options.connection_attempts, |
options.tcp_keep_alive, |
&timeout_ms, options.tcp_keep_alive, |
original_effective_uid == 0 && options.use_privileged_port, |
original_effective_uid == 0 && options.use_privileged_port) != 0) |
options.proxy_command) != 0) |
|
exit(255); |
exit(255); |
|
|
|
freeaddrinfo(addrs); |
|
packet_set_timeout(options.server_alive_interval, |
|
options.server_alive_count_max); |
|
|
if (timeout_ms > 0) |
if (timeout_ms > 0) |
debug3("timeout: %d ms remain after connect", timeout_ms); |
debug3("timeout: %d ms remain after connect", timeout_ms); |
|
|
|
|
signal(sig, main_sigchld_handler); |
signal(sig, main_sigchld_handler); |
errno = save_errno; |
errno = save_errno; |
} |
} |
|
|