diff --git a/lib/krb5/krb5_locl.h b/lib/krb5/krb5_locl.h index eec9c97b2..6ce001dbd 100644 --- a/lib/krb5/krb5_locl.h +++ b/lib/krb5/krb5_locl.h @@ -47,6 +47,8 @@ #include #include +#include + #include #ifdef HAVE_SYS_TYPES_H diff --git a/lib/krb5/send_to_kdc.c b/lib/krb5/send_to_kdc.c index a4f5434d7..25160bdf5 100644 --- a/lib/krb5/send_to_kdc.c +++ b/lib/krb5/send_to_kdc.c @@ -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; }