/* * 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. If needpriv is true, * a privileged port will be allocated to make the connection. * This requires super-user privileges if needpriv is true. * 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. */ struct ssh * ssh_connect(const char *host, struct sockaddr_storage * hostaddr, u_short port, int family, int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv, const char *proxy_command) { struct ssh *ssh; int gaierr; int on = 1; int sock = -1, attempt; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; struct addrinfo hints, *ai, *aitop; debug2("ssh_connect: needpriv %d", needpriv); /* If a proxy command is given, connect using it. */ if (proxy_command != NULL) return ssh_proxy_connect(host, port, proxy_command); /* No proxy command. */ memset(&hints, 0, sizeof(hints)); hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%u", port); if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) fatal("%s: Could not resolve hostname %.100s: %s", __progname, host, ssh_gai_strerror(gaierr)); 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) continue; if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { error("ssh_connect: getnameinfo failed"); continue; } debug("Connecting to %.200s [%.100s] port %s.", host, ntop, strport); /* Create a socket for connecting. */ sock = ssh_create_socket(needpriv, ai); if (sock < 0) /* Any error is already output */ 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 { debug("connect to address %s port %s: %s", ntop, strport, strerror(errno)); close(sock); sock = -1; } } if (sock != -1) break; /* Successful connection. */ } freeaddrinfo(aitop); /* Return failure if we didn't get a successful connection. */ if (sock == -1) { error("ssh: connect to host %s port %s: %s", host, strport, strerror(errno)); return (NULL); } 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. */ ssh = ssh_packet_set_connection(NULL, sock, sock); ssh_packet_set_timeout(ssh, options.server_alive_interval, options.server_alive_count_max); return (ssh); }
/* * Connect to the given ssh server using a proxy command. */ static struct ssh * ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) { struct ssh *ssh; char *command_string, *tmp; int pin[2], pout[2]; pid_t pid; char *shell, strport[NI_MAXSERV]; if ((shell = getenv("SHELL")) == NULL || *shell == '\0') shell = _PATH_BSHELL; /* Convert the port number into a string. */ snprintf(strport, sizeof strport, "%hu", port); /* * Build the final command string in the buffer by making the * appropriate substitutions to the given proxy command. * * Use "exec" to avoid "sh -c" processes on some platforms * (e.g. Solaris) */ xasprintf(&tmp, "exec %s", proxy_command); command_string = percent_expand(tmp, "h", host, "p", strport, "r", options.user, (char *)NULL); xfree(tmp); /* 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)); debug("Executing proxy command: %.500s", command_string); /* Fork and execute the proxy command. */ if ((pid = fork()) == 0) { char *argv[10]; /* Child. Permanently give up superuser privileges. */ permanently_drop_suid(original_real_uid); /* 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. */ xfree(command_string); /* Set the connection file descriptors. */ ssh = ssh_packet_set_connection(NULL, pout[0], pin[1]); ssh_packet_set_timeout(ssh, options.server_alive_interval, options.server_alive_count_max); /* Indicate OK return */ return (ssh); }
static void congreet(int s) { int n = 0, remote_major = 0, remote_minor = 0; char buf[256], *cp; char remote_version[sizeof buf]; size_t bufsiz; con *c = &fdcon[s]; for (;;) { memset(buf, '\0', sizeof(buf)); bufsiz = sizeof(buf); cp = buf; while (bufsiz-- && (n = atomicio(read, s, cp, 1)) == 1 && *cp != '\n') { if (*cp == '\r') *cp = '\n'; cp++; } if (n != 1 || strncmp(buf, "SSH-", 4) == 0) break; } if (n == 0) { switch (errno) { case EPIPE: error("%s: Connection closed by remote host", c->c_name); break; case ECONNREFUSED: break; default: error("read (%s): %s", c->c_name, strerror(errno)); break; } conrecycle(s); return; } if (*cp != '\n' && *cp != '\r') { error("%s: bad greeting", c->c_name); confree(s); return; } *cp = '\0'; if ((c->c_ssh = ssh_packet_set_connection(NULL, s, s)) == NULL) fatal("ssh_packet_set_connection failed"); ssh_packet_set_timeout(c->c_ssh, timeout, 1); ssh_set_app_data(c->c_ssh, c); /* back link */ if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, remote_version) == 3) c->c_ssh->compat = compat_datafellows(remote_version); else c->c_ssh->compat = 0; if (c->c_keytype != KT_RSA1) { if (!ssh2_capable(remote_major, remote_minor)) { debug("%s doesn't support ssh2", c->c_name); confree(s); return; } } else if (remote_major != 1) { debug("%s doesn't support ssh1", c->c_name); confree(s); return; } fprintf(stderr, "# %s:%d %s\n", c->c_name, ssh_port, chop(buf)); n = snprintf(buf, sizeof buf, "SSH-%d.%d-OpenSSH-keyscan\r\n", c->c_keytype == KT_RSA1? PROTOCOL_MAJOR_1 : PROTOCOL_MAJOR_2, c->c_keytype == KT_RSA1? PROTOCOL_MINOR_1 : PROTOCOL_MINOR_2); if (n < 0 || (size_t)n >= sizeof(buf)) { error("snprintf: buffer too small"); confree(s); return; } if (atomicio(vwrite, s, buf, n) != (size_t)n) { error("write (%s): %s", c->c_name, strerror(errno)); confree(s); return; } if (c->c_keytype != KT_RSA1) { keygrab_ssh2(c); confree(s); return; } c->c_status = CS_SIZE; contouch(s); }