diff options
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(); |