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:
@@ -47,6 +47,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <sys/poll.h>
|
||||
|
||||
#include <krb5-types.h>
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
|
@@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user