[PATCH] connect() timeout

Jean-Charles Longuet jclonguet at free.fr
Thu May 23 05:10:47 EST 2002


 Here are the new versions of this widely used patch for OpenSSH 3.2.2p1
and 3.2.3p1.

The patch avoids waiting to long when using ssh() or scp() on a down host,
it is usefull when you have to update many hosts via rsync or rdist themselves
relying upon ssh(). It enables a new option 'ConnectTimeout' to control
exactly the timeout value, so that it can be used even on slow links.

 These patches can also be found on http://charts.free.fr/

 If you think this patch is worth to be included in the main tree, then you
can vote for it on http://bugzilla.mindrot.org/showvotes.cgi?voteon=207 but
this requires a login. You can also just browse the case at
http://bugzilla.mindrot.org/show_bug.cgi?id=207

 Hope this patch help you.

--
	Jean-Charles
-------------- next part --------------
--- openssh-3.2.2p1/readconf.c.ORIG	Tue Feb  5 02:26:35 2002
+++ openssh-3.2.2p1/readconf.c	Tue May 21 15:40:06 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, oBadOption }
 };
 
@@ -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.2.2p1/readconf.h.ORIG	Tue Mar  5 02:53:05 2002
+++ openssh-3.2.2p1/readconf.h	Tue May 21 15:40:06 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.2.2p1/ssh.1.ORIG	Wed May 15 23:36:46 2002
+++ openssh-3.2.2p1/ssh.1	Tue May 21 15:40:06 2002
@@ -813,6 +813,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.2.2p1/ssh.c.ORIG	Tue Apr 23 13:09:46 2002
+++ openssh-3.2.2p1/ssh.c	Tue May 21 15:40:06 2002
@@ -677,7 +677,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.2.2p1/sshconnect.c.ORIG	Tue Mar  5 19:59:46 2002
+++ openssh-3.2.2p1/sshconnect.c	Tue May 21 15:40:06 2002
@@ -222,6 +222,63 @@
 	return sock;
 }
 
+int
+timeout_connect(int sockfd, const struct sockaddr *serv_addr,
+    socklen_t addrlen, int timeout)
+{
+	fd_set *fdset;
+	struct timeval tv;
+	socklen_t optlen;
+	int fdsetsz, optval, rc;
+
+	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;
+
+	fdsetsz = howmany(sockfd+1, NFDBITS) * sizeof(fd_mask);
+	fdset = (fd_set *)xmalloc(fdsetsz);
+	memset(fdset, 0, fdsetsz);
+	FD_SET(sockfd, fdset);
+	tv.tv_sec = timeout;
+	tv.tv_usec = 0;
+	rc=select(sockfd+1, NULL, fdset, NULL, &tv);
+
+	switch(rc) {
+	case 0:
+		errno = ETIMEDOUT;
+	case -1:
+		return -1;
+		break;
+	case 1:
+		optval = 0;
+		optlen = sizeof(optval);
+		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.
@@ -241,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;
@@ -323,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.2.2p1/sshconnect.h.ORIG	Wed Oct 10 07:07:45 2001
+++ openssh-3.2.2p1/sshconnect.h	Tue May 21 15:40:06 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 *);
-------------- next part --------------
--- openssh-3.2.3p1/readconf.c.ORIG	Tue Feb  5 02:26:35 2002
+++ openssh-3.2.3p1/readconf.c	Wed May 22 19:45:13 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, oBadOption }
 };
 
@@ -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.2.3p1/readconf.h.ORIG	Tue Mar  5 02:53:05 2002
+++ openssh-3.2.3p1/readconf.h	Wed May 22 19:45:13 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.2.3p1/ssh.1.ORIG	Wed May 15 23:36:46 2002
+++ openssh-3.2.3p1/ssh.1	Wed May 22 19:45:13 2002
@@ -813,6 +813,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.2.3p1/ssh.c.ORIG	Tue Apr 23 13:09:46 2002
+++ openssh-3.2.3p1/ssh.c	Wed May 22 19:45:13 2002
@@ -677,7 +677,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.2.3p1/sshconnect.c.ORIG	Tue Mar  5 19:59:46 2002
+++ openssh-3.2.3p1/sshconnect.c	Wed May 22 19:45:13 2002
@@ -222,6 +222,63 @@
 	return sock;
 }
 
+int
+timeout_connect(int sockfd, const struct sockaddr *serv_addr,
+    socklen_t addrlen, int timeout)
+{
+	fd_set *fdset;
+	struct timeval tv;
+	socklen_t optlen;
+	int fdsetsz, optval, rc;
+
+	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;
+
+	fdsetsz = howmany(sockfd+1, NFDBITS) * sizeof(fd_mask);
+	fdset = (fd_set *)xmalloc(fdsetsz);
+	memset(fdset, 0, fdsetsz);
+	FD_SET(sockfd, fdset);
+	tv.tv_sec = timeout;
+	tv.tv_usec = 0;
+	rc=select(sockfd+1, NULL, fdset, NULL, &tv);
+
+	switch(rc) {
+	case 0:
+		errno = ETIMEDOUT;
+	case -1:
+		return -1;
+		break;
+	case 1:
+		optval = 0;
+		optlen = sizeof(optval);
+		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.
@@ -241,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;
@@ -323,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.2.3p1/sshconnect.h.ORIG	Wed Oct 10 07:07:45 2001
+++ openssh-3.2.3p1/sshconnect.h	Wed May 22 19:45:13 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 *);


More information about the openssh-unix-dev mailing list