summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sshconnect.c154
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 */
331static int 339static int
332timeout_connect(int sockfd, const struct sockaddr *serv_addr, 340waitrfd(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), 366static int
360 sizeof(fd_mask)); 367timeout_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