diff options
-rw-r--r-- | sshconnect.c | 154 |
1 files changed, 63 insertions, 91 deletions
diff --git a/sshconnect.c b/sshconnect.c index 4100cdc8c..8f527aa43 100644 --- a/sshconnect.c +++ b/sshconnect.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sshconnect.c,v 1.281 2017/06/24 05:35:05 djm Exp $ */ | 1 | /* $OpenBSD: sshconnect.c,v 1.282 2017/06/24 05:37:44 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 |
@@ -34,6 +34,9 @@ | |||
34 | #include <paths.h> | 34 | #include <paths.h> |
35 | #endif | 35 | #endif |
36 | #include <pwd.h> | 36 | #include <pwd.h> |
37 | #ifdef HAVE_POLL_H | ||
38 | #include <poll.h> | ||
39 | #endif | ||
37 | #include <signal.h> | 40 | #include <signal.h> |
38 | #include <stdarg.h> | 41 | #include <stdarg.h> |
39 | #include <stdio.h> | 42 | #include <stdio.h> |
@@ -328,87 +331,71 @@ ssh_create_socket(int privileged, struct addrinfo *ai) | |||
328 | return sock; | 331 | return sock; |
329 | } | 332 | } |
330 | 333 | ||
334 | /* | ||
335 | * Wait up to *timeoutp milliseconds for fd to be readable. Updates | ||
336 | * *timeoutp with time remaining. | ||
337 | * Returns 0 if fd ready or -1 on timeout or error (see errno). | ||
338 | */ | ||
331 | static int | 339 | static int |
332 | timeout_connect(int sockfd, const struct sockaddr *serv_addr, | 340 | waitrfd(int fd, int *timeoutp) |
333 | socklen_t addrlen, int *timeoutp) | ||
334 | { | 341 | { |
335 | fd_set *fdset; | 342 | struct pollfd pfd; |
336 | struct timeval tv, t_start; | 343 | struct timeval t_start; |
337 | socklen_t optlen; | 344 | int oerrno, r; |
338 | int optval, rc, result = -1; | ||
339 | 345 | ||
340 | gettimeofday(&t_start, NULL); | 346 | gettimeofday(&t_start, NULL); |
341 | 347 | pfd.fd = fd; | |
342 | if (*timeoutp <= 0) { | 348 | pfd.events = POLLIN; |
343 | result = connect(sockfd, serv_addr, addrlen); | 349 | for (; *timeoutp >= 0;) { |
344 | goto done; | 350 | r = poll(&pfd, 1, *timeoutp); |
345 | } | 351 | oerrno = errno; |
346 | 352 | ms_subtract_diff(&t_start, timeoutp); | |
347 | set_nonblock(sockfd); | 353 | errno = oerrno; |
348 | rc = connect(sockfd, serv_addr, addrlen); | 354 | if (r > 0) |
349 | if (rc == 0) { | 355 | return 0; |
350 | unset_nonblock(sockfd); | 356 | else if (r == -1 && errno != EAGAIN) |
351 | result = 0; | 357 | return -1; |
352 | goto done; | 358 | else if (r == 0) |
353 | } | 359 | break; |
354 | if (errno != EINPROGRESS) { | ||
355 | result = -1; | ||
356 | goto done; | ||
357 | } | 360 | } |
361 | /* timeout */ | ||
362 | errno = ETIMEDOUT; | ||
363 | return -1; | ||
364 | } | ||
358 | 365 | ||
359 | fdset = xcalloc(howmany(sockfd + 1, NFDBITS), | 366 | static int |
360 | sizeof(fd_mask)); | 367 | timeout_connect(int sockfd, const struct sockaddr *serv_addr, |
361 | FD_SET(sockfd, fdset); | 368 | socklen_t addrlen, int *timeoutp) |
362 | ms_to_timeval(&tv, *timeoutp); | 369 | { |
370 | int optval = 0; | ||
371 | socklen_t optlen = sizeof(optval); | ||
363 | 372 | ||
364 | for (;;) { | 373 | /* No timeout: just do a blocking connect() */ |
365 | rc = select(sockfd + 1, NULL, fdset, NULL, &tv); | 374 | if (*timeoutp <= 0) |
366 | if (rc != -1 || errno != EINTR) | 375 | return connect(sockfd, serv_addr, addrlen); |
367 | break; | ||
368 | } | ||
369 | 376 | ||
370 | switch (rc) { | 377 | set_nonblock(sockfd); |
371 | case 0: | 378 | if (connect(sockfd, serv_addr, addrlen) == 0) { |
372 | /* Timed out */ | 379 | /* Succeeded already? */ |
373 | errno = ETIMEDOUT; | ||
374 | break; | ||
375 | case -1: | ||
376 | /* Select error */ | ||
377 | debug("select: %s", strerror(errno)); | ||
378 | break; | ||
379 | case 1: | ||
380 | /* Completed or failed */ | ||
381 | optval = 0; | ||
382 | optlen = sizeof(optval); | ||
383 | if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, | ||
384 | &optlen) == -1) { | ||
385 | debug("getsockopt: %s", strerror(errno)); | ||
386 | break; | ||
387 | } | ||
388 | if (optval != 0) { | ||
389 | errno = optval; | ||
390 | break; | ||
391 | } | ||
392 | result = 0; | ||
393 | unset_nonblock(sockfd); | 380 | unset_nonblock(sockfd); |
394 | break; | 381 | return 0; |
395 | default: | 382 | } else if (errno != EINPROGRESS) |
396 | /* Should not occur */ | 383 | return -1; |
397 | fatal("Bogus return (%d) from select()", rc); | ||
398 | } | ||
399 | 384 | ||
400 | free(fdset); | 385 | if (waitrfd(sockfd, timeoutp) == -1) |
386 | return -1; | ||
401 | 387 | ||
402 | done: | 388 | /* Completed or failed */ |
403 | if (result == 0 && *timeoutp > 0) { | 389 | if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) { |
404 | ms_subtract_diff(&t_start, timeoutp); | 390 | debug("getsockopt: %s", strerror(errno)); |
405 | if (*timeoutp <= 0) { | 391 | return -1; |
406 | errno = ETIMEDOUT; | ||
407 | result = -1; | ||
408 | } | ||
409 | } | 392 | } |
410 | 393 | if (optval != 0) { | |
411 | return (result); | 394 | errno = optval; |
395 | return -1; | ||
396 | } | ||
397 | unset_nonblock(sockfd); | ||
398 | return 0; | ||
412 | } | 399 | } |
413 | 400 | ||
414 | /* | 401 | /* |
@@ -546,39 +533,25 @@ ssh_exchange_identification(int timeout_ms) | |||
546 | int connection_out = packet_get_connection_out(); | 533 | int connection_out = packet_get_connection_out(); |
547 | u_int i, n; | 534 | u_int i, n; |
548 | size_t len; | 535 | size_t len; |
549 | int fdsetsz, remaining, rc; | 536 | int rc; |
550 | struct timeval t_start, t_remaining; | ||
551 | fd_set *fdset; | ||
552 | |||
553 | fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask); | ||
554 | fdset = xcalloc(1, fdsetsz); | ||
555 | 537 | ||
556 | send_client_banner(connection_out, 0); | 538 | send_client_banner(connection_out, 0); |
557 | 539 | ||
558 | /* Read other side's version identification. */ | 540 | /* Read other side's version identification. */ |
559 | remaining = timeout_ms; | ||
560 | for (n = 0;;) { | 541 | for (n = 0;;) { |
561 | for (i = 0; i < sizeof(buf) - 1; i++) { | 542 | for (i = 0; i < sizeof(buf) - 1; i++) { |
562 | if (timeout_ms > 0) { | 543 | if (timeout_ms > 0) { |
563 | gettimeofday(&t_start, NULL); | 544 | rc = waitrfd(connection_in, &timeout_ms); |
564 | ms_to_timeval(&t_remaining, remaining); | 545 | if (rc == -1 && errno == ETIMEDOUT) { |
565 | FD_SET(connection_in, fdset); | ||
566 | rc = select(connection_in + 1, fdset, NULL, | ||
567 | fdset, &t_remaining); | ||
568 | ms_subtract_diff(&t_start, &remaining); | ||
569 | if (rc == 0 || remaining <= 0) | ||
570 | fatal("Connection timed out during " | 546 | fatal("Connection timed out during " |
571 | "banner exchange"); | 547 | "banner exchange"); |
572 | if (rc == -1) { | 548 | } else if (rc == -1) { |
573 | if (errno == EINTR) | 549 | fatal("%s: %s", |
574 | continue; | 550 | __func__, strerror(errno)); |
575 | fatal("ssh_exchange_identification: " | ||
576 | "select: %s", strerror(errno)); | ||
577 | } | 551 | } |
578 | } | 552 | } |
579 | 553 | ||
580 | len = atomicio(read, connection_in, &buf[i], 1); | 554 | len = atomicio(read, connection_in, &buf[i], 1); |
581 | |||
582 | if (len != 1 && errno == EPIPE) | 555 | if (len != 1 && errno == EPIPE) |
583 | fatal("ssh_exchange_identification: " | 556 | fatal("ssh_exchange_identification: " |
584 | "Connection closed by remote host"); | 557 | "Connection closed by remote host"); |
@@ -604,7 +577,6 @@ ssh_exchange_identification(int timeout_ms) | |||
604 | debug("ssh_exchange_identification: %s", buf); | 577 | debug("ssh_exchange_identification: %s", buf); |
605 | } | 578 | } |
606 | server_version_string = xstrdup(buf); | 579 | server_version_string = xstrdup(buf); |
607 | free(fdset); | ||
608 | 580 | ||
609 | /* | 581 | /* |
610 | * Check that the versions match. In future this might accept | 582 | * Check that the versions match. In future this might accept |