diff options
author | Damien Miller <djm@mindrot.org> | 2007-09-17 12:06:57 +1000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2007-09-17 12:06:57 +1000 |
commit | 67bd062b27f87047ec48e9a0dd9a47eec2891973 (patch) | |
tree | 1d2a2ca058f75e068707dfd82e093a7b80e57d3e /sshconnect.c | |
parent | 54fd7cf2db5327f304825e0f9aaf9af5a490a75f (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.c | 105 |
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; | |||
77 | static int show_other_keys(const char *, Key *); | 77 | static int show_other_keys(const char *, Key *); |
78 | static void warn_changed_key(Key *); | 78 | static void warn_changed_key(Key *); |
79 | 79 | ||
80 | static void | ||
81 | ms_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 | |||
90 | static void | ||
91 | ms_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 | ||
224 | static int | 241 | static int |
225 | timeout_connect(int sockfd, const struct sockaddr *serv_addr, | 242 | timeout_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 | */ |
302 | int | 335 | int |
303 | ssh_connect(const char *host, struct sockaddr_storage * hostaddr, | 336 | ssh_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 | */ |
400 | static void | 433 | static void |
401 | ssh_exchange_identification(void) | 434 | ssh_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 | */ |
947 | void | 1010 | void |
948 | ssh_login(Sensitive *sensitive, const char *orighost, | 1011 | ssh_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(); |