diff options
-rw-r--r-- | ares/CHANGES | 7 | ||||
-rw-r--r-- | ares/acinclude.m4 | 123 | ||||
-rw-r--r-- | ares/ares_process.c | 94 | ||||
-rw-r--r-- | ares/configure.ac | 1 |
4 files changed, 202 insertions, 23 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 <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+],[
+/* 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 <unistd.h>
+#include <stropts.h>
+],[
+/* 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 <windows.h>
+],[
+/* 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 <sys/ioctl.h>
+],[
+/* 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 <socket.h>
+],[
+/* 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) } } -static int open_tcp_socket(ares_channel channel, struct server_state *server) +/* + * 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 */) { -#if defined(WIN32) - u_long flags; -#else +#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) +{ 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)
|