William Ahern:
authorDaniel Stenberg <daniel@haxx.se>
Thu, 2 Jun 2005 11:58:04 +0000 (11:58 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 2 Jun 2005 11:58:04 +0000 (11:58 +0000)
  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
ares/acinclude.m4
ares/ares_process.c
ares/configure.ac

index cb1fdac..678319d 100644 (file)
@@ -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
index 1b197f0..2f6256a 100644 (file)
@@ -1,3 +1,126 @@
+dnl Check for how to set a socket to non-blocking state. There seems to exist\r
+dnl four known different ways, with the one used almost everywhere being POSIX\r
+dnl and XPG3, while the other different ways for different systems (old BSD,\r
+dnl Windows and Amiga).\r
+dnl\r
+dnl There are two known platforms (AIX 3.x and SunOS 4.1.x) where the\r
+dnl O_NONBLOCK define is found but does not work. This condition is attempted\r
+dnl to get caught in this script by using an excessive number of #ifdefs...\r
+dnl\r
+AC_DEFUN([CURL_CHECK_NONBLOCKING_SOCKET],\r
+[\r
+  AC_MSG_CHECKING([non-blocking sockets style])\r
+\r
+  AC_TRY_COMPILE([\r
+/* headers for O_NONBLOCK test */\r
+#include <sys/types.h>\r
+#include <unistd.h>\r
+#include <fcntl.h>\r
+],[\r
+/* try to compile O_NONBLOCK */\r
+\r
+#if defined(sun) || defined(__sun__) || defined(__SUNPRO_C) || defined(__SUNPRO_CC)\r
+# if defined(__SVR4) || defined(__srv4__)\r
+#  define PLATFORM_SOLARIS\r
+# else\r
+#  define PLATFORM_SUNOS4\r
+# endif\r
+#endif\r
+#if (defined(_AIX) || defined(__xlC__)) && !defined(_AIX4)\r
+# define PLATFORM_AIX_V3\r
+#endif\r
+\r
+#if defined(PLATFORM_SUNOS4) || defined(PLATFORM_AIX_V3) || defined(__BEOS__)\r
+#error "O_NONBLOCK does not work on this platform"\r
+#endif\r
+  int socket;\r
+  int flags = fcntl(socket, F_SETFL, flags | O_NONBLOCK);\r
+],[\r
+dnl the O_NONBLOCK test was fine\r
+nonblock="O_NONBLOCK"\r
+AC_DEFINE(HAVE_O_NONBLOCK, 1, [use O_NONBLOCK for non-blocking sockets])\r
+],[\r
+dnl the code was bad, try a different program now, test 2\r
+\r
+  AC_TRY_COMPILE([\r
+/* headers for FIONBIO test */\r
+#include <unistd.h>\r
+#include <stropts.h>\r
+],[\r
+/* FIONBIO source test (old-style unix) */\r
+ int socket;\r
+ int flags = ioctl(socket, FIONBIO, &flags);\r
+],[\r
+dnl FIONBIO test was good\r
+nonblock="FIONBIO"\r
+AC_DEFINE(HAVE_FIONBIO, 1, [use FIONBIO for non-blocking sockets])\r
+],[\r
+dnl FIONBIO test was also bad\r
+dnl the code was bad, try a different program now, test 3\r
+\r
+  AC_TRY_COMPILE([\r
+/* headers for ioctlsocket test (cygwin?) */\r
+#include <windows.h>\r
+],[\r
+/* ioctlsocket source code */\r
+ int socket;\r
+ unsigned long flags = ioctlsocket(socket, FIONBIO, &flags);\r
+],[\r
+dnl ioctlsocket test was good\r
+nonblock="ioctlsocket"\r
+AC_DEFINE(HAVE_IOCTLSOCKET, 1, [use ioctlsocket() for non-blocking sockets])\r
+],[\r
+dnl ioctlsocket didnt compile!, go to test 4\r
+\r
+  AC_TRY_LINK([\r
+/* headers for IoctlSocket test (Amiga?) */\r
+#include <sys/ioctl.h>\r
+],[\r
+/* IoctlSocket source code */\r
+ int socket;\r
+ int flags = IoctlSocket(socket, FIONBIO, (long)1);\r
+],[\r
+dnl ioctlsocket test was good\r
+nonblock="IoctlSocket"\r
+AC_DEFINE(HAVE_IOCTLSOCKET_CASE, 1, [use Ioctlsocket() for non-blocking sockets])\r
+],[\r
+dnl Ioctlsocket didnt compile, do test 5!\r
+  AC_TRY_COMPILE([\r
+/* headers for SO_NONBLOCK test (BeOS) */\r
+#include <socket.h>\r
+],[\r
+/* SO_NONBLOCK source code */\r
+ long b = 1;\r
+ int socket;\r
+ int flags = setsockopt(socket, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b));\r
+],[\r
+dnl the SO_NONBLOCK test was good\r
+nonblock="SO_NONBLOCK"\r
+AC_DEFINE(HAVE_SO_NONBLOCK, 1, [use SO_NONBLOCK for non-blocking sockets])\r
+],[\r
+dnl test 5 didnt compile!\r
+nonblock="nada"\r
+AC_DEFINE(HAVE_DISABLED_NONBLOCKING, 1, [disabled non-blocking sockets])\r
+])\r
+dnl end of fifth test\r
+\r
+])\r
+dnl end of forth test\r
+\r
+])\r
+dnl end of third test\r
+\r
+])\r
+dnl end of second test\r
+\r
+])\r
+dnl end of non-blocking try-compile test\r
+  AC_MSG_RESULT($nonblock)\r
+\r
+  if test "$nonblock" = "nada"; then\r
+    AC_MSG_WARN([non-block sockets disabled])\r
+  fi\r
+])\r
 \r
 dnl We create a function for detecting which compiler we use and then set as\r
 dnl pendantic compiler options as possible for that particular compiler. The\r
index 15d3726..a1e16af 100644 (file)
@@ -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;
index 1201198..e3a9c96 100644 (file)
@@ -328,5 +328,6 @@ AC_CHECK_SIZEOF(struct in_addr, ,
 \r
 AC_CHECK_FUNCS([bitncmp if_indextoname])\r
 \r
+CURL_CHECK_NONBLOCKING_SOCKET\r
 \r
 AC_OUTPUT(Makefile)\r