The attached patch adds a new 'ConnectTimeout' option (man page updated in patch) to avoid wasting time when the target host is down. I needed that because I was using rsync/rdist over ssh for massive files update and the default connect() took too long for my purpose. The patch was tested on Linux only, but I used a similar one for ssh 1.2.XX on Linux, Solaris and HP-UX without problems. The patch can also be found on: http://charts.free.fr/openssh-3.0.2p1-timeout.patch PS: I did not suscribe to the list, so please cc: me regarding this patch -------------- next part -------------- --- includes.h.OK Thu Sep 20 04:07:51 2001 +++ includes.h Sat Jan 26 21:44:35 2002 @@ -44,6 +44,8 @@ #include <grp.h> #include <time.h> #include <dirent.h> +#include <setjmp.h> +#include <signal.h> #ifdef HAVE_LIMITS_H # include <limits.h> --- readconf.c.OK Wed Oct 3 19:39:39 2001 +++ readconf.c Sat Jan 26 21:44:35 2002 @@ -115,7 +115,8 @@ oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias, oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, oHostKeyAlgorithms, oBindAddress, oSmartcardDevice, - oClearAllForwardings, oNoHostAuthenticationForLocalhost + oClearAllForwardings, oNoHostAuthenticationForLocalhost, + oConnectTimeout } OpCodes; /* Textual representations of the tokens. */ @@ -187,6 +188,7 @@ { "smartcarddevice", oSmartcardDevice }, { "clearallforwardings", oClearAllForwardings }, { "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost }, + { "connecttimeout", oConnectTimeout }, { NULL, 0 } }; @@ -294,6 +296,11 @@ /* don't panic, but count bad options */ return -1; /* NOTREACHED */ + + case oConnectTimeout: + intptr = &options->connection_timeout; + goto parse_int; + case oForwardAgent: intptr = &options->forward_agent; parse_flag: @@ -775,6 +782,7 @@ options->compression_level = -1; options->port = -1; options->connection_attempts = -1; + options->connection_timeout = -1; options->number_of_password_prompts = -1; options->cipher = -1; options->ciphers = NULL; --- readconf.h.OK Wed Oct 3 19:39:39 2001 +++ readconf.h Sat Jan 26 21:44:35 2002 @@ -68,6 +68,8 @@ int port; /* Port to connect. */ int connection_attempts; /* Max attempts (seconds) before * giving up */ + int connection_timeout; /* Max time (seconds) before + * aborting connection attempt */ int number_of_password_prompts; /* Max number of password * prompts. */ int cipher; /* Cipher to use. */ --- ssh.1.OK Mon Nov 12 01:05:49 2001 +++ ssh.1 Sat Jan 26 21:44:35 2002 @@ -804,6 +804,12 @@ The argument must be an integer. This may be useful in scripts if the connection sometimes fails. The default is 1. +.It Cm ConnectTimeout +Specifies the timeout (in seconds) used when connecting to the ssh +server, instead of using default system values. This value is used +only when the target is down or really unreachable, not when it +refuses the connection. This may be usefull for tools using ssh +for communication, as it avoid long waits. .It Cm DynamicForward Specifies that a TCP/IP port on the local machine be forwarded over the secure channel, and the application --- ssh.c.OK Mon Nov 12 00:52:04 2001 +++ ssh.c Sat Jan 26 21:44:35 2002 @@ -674,7 +674,7 @@ /* Open a connection to the remote host. */ cerr = ssh_connect(host, &hostaddr, options.port, IPv4or6, - options.connection_attempts, + options.connection_attempts, options.connection_timeout, original_effective_uid != 0 || !options.use_privileged_port, pw, options.proxy_command); --- sshconnect.c.OK Wed Oct 10 07:07:45 2001 +++ sshconnect.c Sat Jan 26 21:44:35 2002 @@ -35,6 +35,8 @@ char *client_version_string = NULL; char *server_version_string = NULL; +static jmp_buf jmpenv; + extern Options options; extern char *__progname; @@ -221,6 +223,43 @@ return sock; } +/* for alarm() */ +static void +timeout_sigh(int dummy) +{ + errno = ETIMEDOUT; + longjmp(jmpenv, !0); +} + +int +timeout_connect(int sockfd, const struct sockaddr *serv_addr, + socklen_t addrlen, int timeout) +{ + void (*sigh)(int); + int rc; + + if (timeout <= 0) + return(connect(sockfd, serv_addr, addrlen)); + + if (setjmp(jmpenv) == 0) + { + debug("ssh: setting connect() timeout to %d s.", + timeout); + sigh = signal(SIGALRM, timeout_sigh); + if (sigh == SIG_ERR) + sigh = SIG_IGN; /* For further restore */ + (void) alarm((unsigned int) timeout); + rc = connect(sockfd, serv_addr, addrlen); + /* restore previous behaviour */ + (void) alarm((unsigned int) 0); + (void) signal(SIGALRM, sigh); + return rc; + } else { + errno = ETIMEDOUT; + return -1; + } +} + /* * Opens a TCP/IP connection to the remote server on the given host. * The address of the remote host will be returned in hostaddr. @@ -240,7 +279,7 @@ */ int ssh_connect(const char *host, struct sockaddr_storage * hostaddr, - u_short port, int family, int connection_attempts, + u_short port, int family, int connection_attempts, int connection_timeout, int anonymous, struct passwd *pw, const char *proxy_command) { int gaierr; @@ -322,7 +361,8 @@ * the remote uid as root. */ temporarily_use_uid(pw); - if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) { + if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, + connection_timeout) >= 0) { /* Successful connection. */ memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); restore_uid(); --- sshconnect.h.OK Wed Oct 10 07:07:45 2001 +++ sshconnect.h Sat Jan 26 21:44:35 2002 @@ -28,7 +28,7 @@ int ssh_connect(const char *, struct sockaddr_storage *, u_short, int, int, - int, struct passwd *, const char *); + int, int, struct passwd *, const char *); void ssh_login(Key **, int, const char *, struct sockaddr *, struct passwd *);
> The attached patch adds a new 'ConnectTimeout' option (man page updated > in patch) to avoid wasting time when the target host is down. I neededthat> because I was using rsync/rdist over ssh for massive files update and the > default connect() took too long for my purpose.All-- This is really, really useful. If the code is correct, I'd *really* like to see this in future builds. --dan
On Sat, 26 Jan 2002, Jean-Charles Longuet wrote:> The attached patch adds a new 'ConnectTimeout' option (man page > updated in patch) to avoid wasting time when the target host is > down. I needed that because I was using rsync/rdist over ssh for > massive files update and the default connect() took too long for my > purpose.I'd prefer to see a patch which sets the connect socket to nonblocking and does a timed select() on it. -d
On Sat, Jan 26, 2002 at 11:45:39PM +0100, Jean-Charles Longuet wrote:> The attached patch adds a new 'ConnectTimeout' option (man page updated > in patch) to avoid wasting time when the target host is down. I needed that > because I was using rsync/rdist over ssh for massive files update and the > default connect() took too long for my purpose.please no setjmp. you can use async connects and select, like the rest of openssh. -m
On Sat, 26 Jan 2002, Jean-Charles Longuet wrote: : The patch was tested on Linux only, but I used a similar one for ssh 1.2.XX :on Linux, Solaris and HP-UX without problems. + case oConnectTimeout: + intptr = &options->connection_timeout; + goto parse_int; + look at servconf.c for parse_time.
Here is a new version of this patch, that do not use setjmp() but a select() call instead. The (expected) behaviour is still the same : avoiding spending too much time when doing an ssh() on a down host. Another minor change is the use of the time format in the ConnectTimeout argument (you can now things like '1m30s' if you want). I use it mostly with rsync/rdist to fasten updates. Patch was tested on Linux and Solaris, and compiled OK on HP-UX 10.20. Please notify me if things go bad on other platforms. The patch can also be found on: http://charts.free.fr/openssh-3.0.1p1-timeout-1.01.patch PS: please cc: me regarding this patch for any suggestion/correction. -- Jean-Charles Longuet -------------- next part -------------- --- openssh-3.0.2p1/readconf.c.ORIG Wed Oct 3 19:39:39 2001 +++ openssh-3.0.2p1/readconf.c Wed Feb 20 20:08:20 2002 @@ -115,7 +115,8 @@ oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias, oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, oHostKeyAlgorithms, oBindAddress, oSmartcardDevice, - oClearAllForwardings, oNoHostAuthenticationForLocalhost + oClearAllForwardings, oNoHostAuthenticationForLocalhost, + oConnectTimeout } OpCodes; /* Textual representations of the tokens. */ @@ -187,6 +188,7 @@ { "smartcarddevice", oSmartcardDevice }, { "clearallforwardings", oClearAllForwardings }, { "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost }, + { "connecttimeout", oConnectTimeout }, { NULL, 0 } }; @@ -294,6 +296,19 @@ /* don't panic, but count bad options */ return -1; /* NOTREACHED */ + + case oConnectTimeout: + intptr = &options->connection_timeout; +parse_time: + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing time argument.", filename, linenum); + if ((value = convtime(arg)) == -1) + fatal("%.200s line %d: Invalid time argument.", filename, linenum); + if (*intptr == -1) + *intptr = value; + break; + case oForwardAgent: intptr = &options->forward_agent; parse_flag: @@ -775,6 +790,7 @@ options->compression_level = -1; options->port = -1; options->connection_attempts = -1; + options->connection_timeout = -1; options->number_of_password_prompts = -1; options->cipher = -1; options->ciphers = NULL; --- openssh-3.0.2p1/readconf.h.ORIG Wed Oct 3 19:39:39 2001 +++ openssh-3.0.2p1/readconf.h Wed Feb 20 20:08:20 2002 @@ -68,6 +68,8 @@ int port; /* Port to connect. */ int connection_attempts; /* Max attempts (seconds) before * giving up */ + int connection_timeout; /* Max time (seconds) before + * aborting connection attempt */ int number_of_password_prompts; /* Max number of password * prompts. */ int cipher; /* Cipher to use. */ --- openssh-3.0.2p1/ssh.1.ORIG Mon Nov 12 01:05:49 2001 +++ openssh-3.0.2p1/ssh.1 Wed Feb 20 20:08:20 2002 @@ -804,6 +804,12 @@ The argument must be an integer. This may be useful in scripts if the connection sometimes fails. The default is 1. +.It Cm ConnectTimeout +Specifies the timeout used when connecting to the ssh +server, instead of using default system values. This value is used +only when the target is down or really unreachable, not when it +refuses the connection. This may be usefull for tools using ssh +for communication, as it avoid long TCP timeouts. .It Cm DynamicForward Specifies that a TCP/IP port on the local machine be forwarded over the secure channel, and the application --- openssh-3.0.2p1/ssh.c.ORIG Mon Nov 12 00:52:04 2001 +++ openssh-3.0.2p1/ssh.c Wed Feb 20 20:08:20 2002 @@ -674,7 +674,7 @@ /* Open a connection to the remote host. */ cerr = ssh_connect(host, &hostaddr, options.port, IPv4or6, - options.connection_attempts, + options.connection_attempts, options.connection_timeout, original_effective_uid != 0 || !options.use_privileged_port, pw, options.proxy_command); --- openssh-3.0.2p1/sshconnect.c.ORIG Wed Oct 10 07:07:45 2001 +++ openssh-3.0.2p1/sshconnect.c Wed Feb 20 20:08:52 2002 @@ -221,6 +221,64 @@ return sock; } +int +timeout_connect(int sockfd, const struct sockaddr *serv_addr, + socklen_t addrlen, int timeout) +{ + int rc; + fd_set fds; + + int optval = 0; + socklen_t optlen = sizeof(optval); + struct timeval tv; + + + if (timeout <= 0) + return(connect(sockfd, serv_addr, addrlen)); + + if (fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0) + { + return -1; + } + + rc = connect(sockfd, serv_addr, addrlen); + if (rc == 0) + return 0; + if (errno != EINPROGRESS) + return -1; + + FD_ZERO(&fds); + FD_SET(sockfd, &fds); + tv.tv_sec = timeout; + tv.tv_usec = 0; + rc=select(sockfd+1, NULL, &fds, NULL, &tv); + + switch(rc) { + case 0: + errno = ETIMEDOUT; + case -1: + return -1; + break; + case 1: + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) + return -1; + if (optval != 0) + { + errno = optval; + return -1; + } + return 0; + + default: + /* Should not occur */ + return -1; + break; + } + + return -1; + +} + /* * Opens a TCP/IP connection to the remote server on the given host. * The address of the remote host will be returned in hostaddr. @@ -240,7 +298,7 @@ */ int ssh_connect(const char *host, struct sockaddr_storage * hostaddr, - u_short port, int family, int connection_attempts, + u_short port, int family, int connection_attempts, int connection_timeout, int anonymous, struct passwd *pw, const char *proxy_command) { int gaierr; @@ -322,7 +380,8 @@ * the remote uid as root. */ temporarily_use_uid(pw); - if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) { + if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, + connection_timeout) >= 0) { /* Successful connection. */ memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); restore_uid(); --- openssh-3.0.2p1/sshconnect.h.ORIG Wed Oct 10 07:07:45 2001 +++ openssh-3.0.2p1/sshconnect.h Wed Feb 20 20:08:20 2002 @@ -28,7 +28,7 @@ int ssh_connect(const char *, struct sockaddr_storage *, u_short, int, int, - int, struct passwd *, const char *); + int, int, struct passwd *, const char *); void ssh_login(Key **, int, const char *, struct sockaddr *, struct passwd *);