summaryrefslogtreecommitdiff
path: root/sshconnect.c
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2007-09-17 12:06:57 +1000
committerDamien Miller <djm@mindrot.org>2007-09-17 12:06:57 +1000
commit67bd062b27f87047ec48e9a0dd9a47eec2891973 (patch)
tree1d2a2ca058f75e068707dfd82e093a7b80e57d3e /sshconnect.c
parent54fd7cf2db5327f304825e0f9aaf9af5a490a75f (diff)
- djm@cvs.openbsd.org 2007/09/04 11:15:56
[ssh.c sshconnect.c sshconnect.h] make ssh(1)'s ConnectTimeout option apply to both the TCP connection and SSH banner exchange (previously it just covered the TCP connection). This allows callers of ssh(1) to better detect and deal with stuck servers that accept a TCP connection but don't progress the protocol, and also makes ConnectTimeout useful for connections via a ProxyCommand; feedback and "looks ok" markus@
Diffstat (limited to 'sshconnect.c')
-rw-r--r--sshconnect.c105
1 files changed, 84 insertions, 21 deletions
diff --git a/sshconnect.c b/sshconnect.c
index 7e3c9fff4..933df39fa 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sshconnect.c,v 1.201 2007/08/23 03:23:26 djm Exp $ */ 1/* $OpenBSD: sshconnect.c,v 1.202 2007/09/04 11:15:55 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
@@ -77,6 +77,23 @@ extern pid_t proxy_command_pid;
77static int show_other_keys(const char *, Key *); 77static int show_other_keys(const char *, Key *);
78static void warn_changed_key(Key *); 78static void warn_changed_key(Key *);
79 79
80static void
81ms_subtract_diff(struct timeval *start, int *ms)
82{
83 struct timeval diff, finish;
84
85 gettimeofday(&finish, NULL);
86 timersub(&finish, start, &diff);
87 *ms -= (diff.tv_sec * 1000) + (diff.tv_usec / 1000);
88}
89
90static void
91ms_to_timeval(struct timeval *tv, int ms)
92{
93 tv->tv_sec = ms / 1000;
94 tv->tv_usec = (ms % 1000) * 1000;
95}
96
80/* 97/*
81 * Connect to the given ssh server using a proxy command. 98 * Connect to the given ssh server using a proxy command.
82 */ 99 */
@@ -223,30 +240,36 @@ ssh_create_socket(int privileged, struct addrinfo *ai)
223 240
224static int 241static int
225timeout_connect(int sockfd, const struct sockaddr *serv_addr, 242timeout_connect(int sockfd, const struct sockaddr *serv_addr,
226 socklen_t addrlen, int timeout) 243 socklen_t addrlen, int *timeoutp)
227{ 244{
228 fd_set *fdset; 245 fd_set *fdset;
229 struct timeval tv; 246 struct timeval tv, t_start;
230 socklen_t optlen; 247 socklen_t optlen;
231 int optval, rc, result = -1; 248 int optval, rc, result = -1;
232 249
233 if (timeout <= 0) 250 gettimeofday(&t_start, NULL);
234 return (connect(sockfd, serv_addr, addrlen)); 251
252 if (*timeoutp <= 0) {
253 result = connect(sockfd, serv_addr, addrlen);
254 goto done;
255 }
235 256
236 set_nonblock(sockfd); 257 set_nonblock(sockfd);
237 rc = connect(sockfd, serv_addr, addrlen); 258 rc = connect(sockfd, serv_addr, addrlen);
238 if (rc == 0) { 259 if (rc == 0) {
239 unset_nonblock(sockfd); 260 unset_nonblock(sockfd);
240 return (0); 261 result = 0;
262 goto done;
263 }
264 if (errno != EINPROGRESS) {
265 result = -1;
266 goto done;
241 } 267 }
242 if (errno != EINPROGRESS)
243 return (-1);
244 268
245 fdset = (fd_set *)xcalloc(howmany(sockfd + 1, NFDBITS), 269 fdset = (fd_set *)xcalloc(howmany(sockfd + 1, NFDBITS),
246 sizeof(fd_mask)); 270 sizeof(fd_mask));
247 FD_SET(sockfd, fdset); 271 FD_SET(sockfd, fdset);
248 tv.tv_sec = timeout; 272 ms_to_timeval(&tv, *timeoutp);
249 tv.tv_usec = 0;
250 273
251 for (;;) { 274 for (;;) {
252 rc = select(sockfd + 1, NULL, fdset, NULL, &tv); 275 rc = select(sockfd + 1, NULL, fdset, NULL, &tv);
@@ -285,6 +308,16 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr,
285 } 308 }
286 309
287 xfree(fdset); 310 xfree(fdset);
311
312 done:
313 if (result == 0 && *timeoutp > 0) {
314 ms_subtract_diff(&t_start, timeoutp);
315 if (*timeoutp <= 0) {
316 errno = ETIMEDOUT;
317 result = -1;
318 }
319 }
320
288 return (result); 321 return (result);
289} 322}
290 323
@@ -301,8 +334,8 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr,
301 */ 334 */
302int 335int
303ssh_connect(const char *host, struct sockaddr_storage * hostaddr, 336ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
304 u_short port, int family, int connection_attempts, 337 u_short port, int family, int connection_attempts, int *timeout_ms,
305 int needpriv, const char *proxy_command) 338 int want_keepalive, int needpriv, const char *proxy_command)
306{ 339{
307 int gaierr; 340 int gaierr;
308 int on = 1; 341 int on = 1;
@@ -355,7 +388,7 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
355 continue; 388 continue;
356 389
357 if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, 390 if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen,
358 options.connection_timeout) >= 0) { 391 timeout_ms) >= 0) {
359 /* Successful connection. */ 392 /* Successful connection. */
360 memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); 393 memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen);
361 break; 394 break;
@@ -382,7 +415,7 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
382 debug("Connection established."); 415 debug("Connection established.");
383 416
384 /* Set SO_KEEPALIVE if requested. */ 417 /* Set SO_KEEPALIVE if requested. */
385 if (options.tcp_keep_alive && 418 if (want_keepalive &&
386 setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, 419 setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on,
387 sizeof(on)) < 0) 420 sizeof(on)) < 0)
388 error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); 421 error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno));
@@ -398,7 +431,7 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
398 * identification string. 431 * identification string.
399 */ 432 */
400static void 433static void
401ssh_exchange_identification(void) 434ssh_exchange_identification(int timeout_ms)
402{ 435{
403 char buf[256], remote_version[256]; /* must be same size! */ 436 char buf[256], remote_version[256]; /* must be same size! */
404 int remote_major, remote_minor, mismatch; 437 int remote_major, remote_minor, mismatch;
@@ -406,16 +439,44 @@ ssh_exchange_identification(void)
406 int connection_out = packet_get_connection_out(); 439 int connection_out = packet_get_connection_out();
407 int minor1 = PROTOCOL_MINOR_1; 440 int minor1 = PROTOCOL_MINOR_1;
408 u_int i, n; 441 u_int i, n;
442 size_t len;
443 int fdsetsz, remaining, rc;
444 struct timeval t_start, t_remaining;
445 fd_set *fdset;
446
447 fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask);
448 fdset = xcalloc(1, fdsetsz);
409 449
410 /* Read other side's version identification. */ 450 /* Read other side's version identification. */
451 remaining = timeout_ms;
411 for (n = 0;;) { 452 for (n = 0;;) {
412 for (i = 0; i < sizeof(buf) - 1; i++) { 453 for (i = 0; i < sizeof(buf) - 1; i++) {
413 size_t len = atomicio(read, connection_in, &buf[i], 1); 454 if (timeout_ms > 0) {
455 gettimeofday(&t_start, NULL);
456 ms_to_timeval(&t_remaining, remaining);
457 FD_SET(connection_in, fdset);
458 rc = select(connection_in + 1, fdset, NULL,
459 fdset, &t_remaining);
460 ms_subtract_diff(&t_start, &remaining);
461 if (rc == 0 || remaining <= 0)
462 fatal("Connection timed out during "
463 "banner exchange");
464 if (rc == -1) {
465 if (errno == EINTR)
466 continue;
467 fatal("ssh_exchange_identification: "
468 "select: %s", strerror(errno));
469 }
470 }
471
472 len = atomicio(read, connection_in, &buf[i], 1);
414 473
415 if (len != 1 && errno == EPIPE) 474 if (len != 1 && errno == EPIPE)
416 fatal("ssh_exchange_identification: Connection closed by remote host"); 475 fatal("ssh_exchange_identification: "
476 "Connection closed by remote host");
417 else if (len != 1) 477 else if (len != 1)
418 fatal("ssh_exchange_identification: read: %.100s", strerror(errno)); 478 fatal("ssh_exchange_identification: "
479 "read: %.100s", strerror(errno));
419 if (buf[i] == '\r') { 480 if (buf[i] == '\r') {
420 buf[i] = '\n'; 481 buf[i] = '\n';
421 buf[i + 1] = 0; 482 buf[i + 1] = 0;
@@ -426,7 +487,8 @@ ssh_exchange_identification(void)
426 break; 487 break;
427 } 488 }
428 if (++n > 65536) 489 if (++n > 65536)
429 fatal("ssh_exchange_identification: No banner received"); 490 fatal("ssh_exchange_identification: "
491 "No banner received");
430 } 492 }
431 buf[sizeof(buf) - 1] = 0; 493 buf[sizeof(buf) - 1] = 0;
432 if (strncmp(buf, "SSH-", 4) == 0) 494 if (strncmp(buf, "SSH-", 4) == 0)
@@ -434,6 +496,7 @@ ssh_exchange_identification(void)
434 debug("ssh_exchange_identification: %s", buf); 496 debug("ssh_exchange_identification: %s", buf);
435 } 497 }
436 server_version_string = xstrdup(buf); 498 server_version_string = xstrdup(buf);
499 xfree(fdset);
437 500
438 /* 501 /*
439 * Check that the versions match. In future this might accept 502 * Check that the versions match. In future this might accept
@@ -946,7 +1009,7 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
946 */ 1009 */
947void 1010void
948ssh_login(Sensitive *sensitive, const char *orighost, 1011ssh_login(Sensitive *sensitive, const char *orighost,
949 struct sockaddr *hostaddr, struct passwd *pw) 1012 struct sockaddr *hostaddr, struct passwd *pw, int timeout_ms)
950{ 1013{
951 char *host, *cp; 1014 char *host, *cp;
952 char *server_user, *local_user; 1015 char *server_user, *local_user;
@@ -961,7 +1024,7 @@ ssh_login(Sensitive *sensitive, const char *orighost,
961 *cp = (char)tolower(*cp); 1024 *cp = (char)tolower(*cp);
962 1025
963 /* Exchange protocol version identification strings with the server. */ 1026 /* Exchange protocol version identification strings with the server. */
964 ssh_exchange_identification(); 1027 ssh_exchange_identification(timeout_ms);
965 1028
966 /* Put the connection into non-blocking mode. */ 1029 /* Put the connection into non-blocking mode. */
967 packet_set_nonblocking(); 1030 packet_set_nonblocking();