Ensure that timeouts apply to TCP socket connexions.

Currently the Heimdal code calls connect(2) on TCP connexions to
the KDC without setting O_NONBLOCK.  This code implements a
timed_connect() function which will in the case of SOCK_STREAM
sockets put the socket into non-blocking mode prior to calling
connect and use select(2) to apply the configured timeout to connect
completion.  This does not entirely solve the problem of potential
timeouts in the code as it is still possible to block while writing
to the socket.  A proper implementation would also likely start
new connexions after a short interval before timing out existing
connexions and return the results from the first KDC which successfully
responds but we did not do that yet.

This patch is from heimdal-1-5-branch patches:

	5b55e4429caed27b32aac4bc5930f2672a43f273
	6b66321b271ee4672e70ad349ec796dd755cf897
	2e12c7f3e8dca7e1696ebd92199617ce413565e7

Squashed together along with a quick shadowed variable warning fix
to allow it to compile with --enable-developer.
This commit is contained in:
Roland C. Dowdeswell
2012-07-12 01:00:23 +01:00
parent 7aaeafb72b
commit 842ca62336
2 changed files with 55 additions and 2 deletions

View File

@@ -47,6 +47,8 @@
#include <stdlib.h>
#include <limits.h>
#include <sys/poll.h>
#include <krb5-types.h>
#ifdef HAVE_SYS_TYPES_H

View File

@@ -39,6 +39,57 @@ struct send_to_kdc {
void *data;
};
/*
* connect to a remote host and in the case of stream sockets, provide
* a timeout for the connexion.
*/
static int
timed_connect(int s, struct addrinfo *addr, time_t tmout)
{
socklen_t sl;
int so_err;
int flags;
int ret;
if (addr->ai_socktype != SOCK_STREAM)
return connect(s, addr->ai_addr, addr->ai_addrlen);
flags = fcntl(s, F_GETFL);
if (flags == -1)
return -1;
fcntl(s, F_SETFL, flags | O_NONBLOCK);
ret = connect(s, addr->ai_addr, addr->ai_addrlen);
if (ret == -1 && errno != EINPROGRESS)
return -1;
for (;;) {
struct pollfd fds;
fds.fd = s;
fds.events = POLLIN | POLLOUT;
fds.revents = 0;
ret = poll(&fds, 1, tmout * 1000);
if (ret != -1 || errno != EINTR)
break;
}
fcntl(s, F_SETFL, flags);
if (ret != 1)
return -1;
sl = sizeof(so_err);
ret = getsockopt(s, SOL_SOCKET, SO_ERROR, &so_err, &sl);
if (ret == -1)
return -1;
if (so_err != 0)
return -1;
return 0;
}
/*
* send the data in `req' on the socket `fd' (which is datagram iff udp)
* waiting `tmout' for a reply and returning the reply in `rep'.
@@ -293,7 +344,7 @@ send_via_proxy (krb5_context context,
if (s < 0)
continue;
rk_cloexec(s);
if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
if (timed_connect (s, a, context->kdc_timeout) < 0) {
rk_closesocket (s);
continue;
}
@@ -420,7 +471,7 @@ krb5_sendto (krb5_context context,
if (rk_IS_BAD_SOCKET(fd))
continue;
rk_cloexec(fd);
if (connect (fd, a->ai_addr, a->ai_addrlen) < 0) {
if (timed_connect (fd, a, context->kdc_timeout) < 0) {
rk_closesocket (fd);
continue;
}