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.)
This commit is contained in:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -328,5 +328,6 @@ AC_CHECK_SIZEOF(struct in_addr, ,
 | 
			
		||||
 | 
			
		||||
AC_CHECK_FUNCS([bitncmp if_indextoname])
 | 
			
		||||
 | 
			
		||||
CURL_CHECK_NONBLOCKING_SOCKET
 | 
			
		||||
 | 
			
		||||
AC_OUTPUT(Makefile)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user