From 5470b9aa73d4cb26f08763cd0165d68623f104cb Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 2 Jun 2005 11:58:04 +0000 Subject: [PATCH] William Ahern: Make UDP sockets non-blocking. I've confirmed that at least on Linux 2.4 a read event can come back from poll() on a valid SOCK_DGRAM socket but recv(2) will still block. This patch doesn't ignore EAGAIN in read_udp_packets(), though maybe it should. (This patch was edited by Daniel Stenberg and a new configure test was added (imported from curl's configure) to properly detect what non-blocking socket approach to use.) --- ares/CHANGES | 7 +++ ares/acinclude.m4 | 123 ++++++++++++++++++++++++++++++++++++++++++++ ares/ares_process.c | 96 +++++++++++++++++++++++++--------- ares/configure.ac | 1 + 4 files changed, 203 insertions(+), 24 deletions(-) diff --git a/ares/CHANGES b/ares/CHANGES index cb1fdac24..678319dad 100644 --- a/ares/CHANGES +++ b/ares/CHANGES @@ -4,6 +4,13 @@ - William Ahern: + Make UDP sockets non-blocking. I've confirmed that at least on Linux 2.4 a + read event can come back from poll() on a valid SOCK_DGRAM socket but + recv(2) will still block. This patch doesn't ignore EAGAIN in + read_udp_packets(), though maybe it should. (This patch was edited by Daniel + Stenberg and a new configure test was added (imported from curl's configure) + to properly detect what non-blocking socket approach to use.) + I'm not quite sure how this was happening, but I've been seeing PTR queries which seem to return empty responses. At least, they were empty when calling ares_expand_name() on the record. Here's a patch which guarantees to diff --git a/ares/acinclude.m4 b/ares/acinclude.m4 index 1b197f0a0..2f6256ae4 100644 --- a/ares/acinclude.m4 +++ b/ares/acinclude.m4 @@ -1,3 +1,126 @@ +dnl Check for how to set a socket to non-blocking state. There seems to exist +dnl four known different ways, with the one used almost everywhere being POSIX +dnl and XPG3, while the other different ways for different systems (old BSD, +dnl Windows and Amiga). +dnl +dnl There are two known platforms (AIX 3.x and SunOS 4.1.x) where the +dnl O_NONBLOCK define is found but does not work. This condition is attempted +dnl to get caught in this script by using an excessive number of #ifdefs... +dnl +AC_DEFUN([CURL_CHECK_NONBLOCKING_SOCKET], +[ + AC_MSG_CHECKING([non-blocking sockets style]) + + AC_TRY_COMPILE([ +/* headers for O_NONBLOCK test */ +#include +#include +#include +],[ +/* try to compile O_NONBLOCK */ + +#if defined(sun) || defined(__sun__) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) +# if defined(__SVR4) || defined(__srv4__) +# define PLATFORM_SOLARIS +# else +# define PLATFORM_SUNOS4 +# endif +#endif +#if (defined(_AIX) || defined(__xlC__)) && !defined(_AIX4) +# define PLATFORM_AIX_V3 +#endif + +#if defined(PLATFORM_SUNOS4) || defined(PLATFORM_AIX_V3) || defined(__BEOS__) +#error "O_NONBLOCK does not work on this platform" +#endif + int socket; + int flags = fcntl(socket, F_SETFL, flags | O_NONBLOCK); +],[ +dnl the O_NONBLOCK test was fine +nonblock="O_NONBLOCK" +AC_DEFINE(HAVE_O_NONBLOCK, 1, [use O_NONBLOCK for non-blocking sockets]) +],[ +dnl the code was bad, try a different program now, test 2 + + AC_TRY_COMPILE([ +/* headers for FIONBIO test */ +#include +#include +],[ +/* FIONBIO source test (old-style unix) */ + int socket; + int flags = ioctl(socket, FIONBIO, &flags); +],[ +dnl FIONBIO test was good +nonblock="FIONBIO" +AC_DEFINE(HAVE_FIONBIO, 1, [use FIONBIO for non-blocking sockets]) +],[ +dnl FIONBIO test was also bad +dnl the code was bad, try a different program now, test 3 + + AC_TRY_COMPILE([ +/* headers for ioctlsocket test (cygwin?) */ +#include +],[ +/* ioctlsocket source code */ + int socket; + unsigned long flags = ioctlsocket(socket, FIONBIO, &flags); +],[ +dnl ioctlsocket test was good +nonblock="ioctlsocket" +AC_DEFINE(HAVE_IOCTLSOCKET, 1, [use ioctlsocket() for non-blocking sockets]) +],[ +dnl ioctlsocket didnt compile!, go to test 4 + + AC_TRY_LINK([ +/* headers for IoctlSocket test (Amiga?) */ +#include +],[ +/* IoctlSocket source code */ + int socket; + int flags = IoctlSocket(socket, FIONBIO, (long)1); +],[ +dnl ioctlsocket test was good +nonblock="IoctlSocket" +AC_DEFINE(HAVE_IOCTLSOCKET_CASE, 1, [use Ioctlsocket() for non-blocking sockets]) +],[ +dnl Ioctlsocket didnt compile, do test 5! + AC_TRY_COMPILE([ +/* headers for SO_NONBLOCK test (BeOS) */ +#include +],[ +/* SO_NONBLOCK source code */ + long b = 1; + int socket; + int flags = setsockopt(socket, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); +],[ +dnl the SO_NONBLOCK test was good +nonblock="SO_NONBLOCK" +AC_DEFINE(HAVE_SO_NONBLOCK, 1, [use SO_NONBLOCK for non-blocking sockets]) +],[ +dnl test 5 didnt compile! +nonblock="nada" +AC_DEFINE(HAVE_DISABLED_NONBLOCKING, 1, [disabled non-blocking sockets]) +]) +dnl end of fifth test + +]) +dnl end of forth test + +]) +dnl end of third test + +]) +dnl end of second test + +]) +dnl end of non-blocking try-compile test + AC_MSG_RESULT($nonblock) + + if test "$nonblock" = "nada"; then + AC_MSG_WARN([non-block sockets disabled]) + fi +]) dnl We create a function for detecting which compiler we use and then set as dnl pendantic compiler options as possible for that particular compiler. The diff --git a/ares/ares_process.c b/ares/ares_process.c index 15d372642..a1e16af28 100644 --- a/ares/ares_process.c +++ b/ares/ares_process.c @@ -466,13 +466,76 @@ void ares__send_query(ares_channel channel, struct query *query, time_t now) } } +/* + * nonblock() set the given socket to either blocking or non-blocking mode + * based on the 'nonblock' boolean argument. This function is highly portable. + */ +static int nonblock(ares_socket_t sockfd, /* operate on this */ + int nonblock /* TRUE or FALSE */) +{ +#undef SETBLOCK +#define SETBLOCK 0 +#ifdef HAVE_O_NONBLOCK + /* most recent unix versions */ + int flags; + + flags = fcntl(sockfd, F_GETFL, 0); + if (TRUE == nonblock) + return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); + else + return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK)); +#undef SETBLOCK +#define SETBLOCK 1 +#endif + +#if defined(HAVE_FIONBIO) && (SETBLOCK == 0) + /* older unix versions */ + int flags; + + flags = nonblock; + return ioctl(sockfd, FIONBIO, &flags); +#undef SETBLOCK +#define SETBLOCK 2 +#endif + +#if defined(HAVE_IOCTLSOCKET) && (SETBLOCK == 0) + /* Windows? */ + unsigned long flags; + flags = nonblock; + + return ioctlsocket(sockfd, FIONBIO, &flags); +#undef SETBLOCK +#define SETBLOCK 3 +#endif + +#if defined(HAVE_IOCTLSOCKET_CASE) && (SETBLOCK == 0) + /* presumably for Amiga */ + return IoctlSocket(sockfd, FIONBIO, (long)nonblock); +#undef SETBLOCK +#define SETBLOCK 4 +#endif + +#if defined(HAVE_SO_NONBLOCK) && (SETBLOCK == 0) + /* BeOS */ + long b = nonblock ? 1 : 0; + return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); +#undef SETBLOCK +#define SETBLOCK 5 +#endif + +#ifdef HAVE_DISABLED_NONBLOCKING + return 0; /* returns success */ +#undef SETBLOCK +#define SETBLOCK 6 +#endif + +#if (SETBLOCK == 0) +#error "no non-blocking method was found/used/set" +#endif +} + static int open_tcp_socket(ares_channel channel, struct server_state *server) { -#if defined(WIN32) - u_long flags; -#else - int flags; -#endif ares_socket_t s; struct sockaddr_in sockin; @@ -482,25 +545,7 @@ static int open_tcp_socket(ares_channel channel, struct server_state *server) return -1; /* Set the socket non-blocking. */ - -#if defined(WIN32) || defined(WATT32) - flags = 1; - ioctlsocket(s, FIONBIO, &flags); -#else - flags = fcntl(s, F_GETFL, 0); - - if (flags == -1) - { - closesocket(s); - return -1; - } - flags |= O_NONBLOCK; - if (fcntl(s, F_SETFL, flags) == -1) - { - closesocket(s); - return -1; - } -#endif + nonblock(s, TRUE); /* Connect to the server. */ memset(&sockin, 0, sizeof(sockin)); @@ -531,6 +576,9 @@ static int open_udp_socket(ares_channel channel, struct server_state *server) if (s == ARES_SOCKET_BAD) return -1; + /* Set the socket non-blocking. */ + nonblock(s, TRUE); + /* Connect to the server. */ memset(&sockin, 0, sizeof(sockin)); sockin.sin_family = AF_INET; diff --git a/ares/configure.ac b/ares/configure.ac index 120119812..e3a9c968a 100644 --- a/ares/configure.ac +++ b/ares/configure.ac @@ -328,5 +328,6 @@ AC_CHECK_SIZEOF(struct in_addr, , AC_CHECK_FUNCS([bitncmp if_indextoname]) +CURL_CHECK_NONBLOCKING_SOCKET AC_OUTPUT(Makefile)