commit: r878 - in trunk: . buildtools daemon include libspread

[email protected] Mon, 23 Jan 2017 03:45:41 -0500
Newsgroups gmane.network.spread.cvs
Message-ID <[email protected]>
Author: jschultz
Date: 2017-01-23 03:45:40 -0500 (Mon, 23 Jan 2017)
New Revision: 878

Modified:
   trunk/buildtools/fixpaths
   trunk/configure
   trunk/configure.in
   trunk/daemon/config.h.in
   trunk/daemon/config_gram.l
   trunk/daemon/configuration.c
   trunk/daemon/sess_types.h
   trunk/include/sp.h
   trunk/libspread/sp.c
   trunk/release_announcement_5.0.txt
Log:
configure.in: Added checks for poll, poll.h, select, sys/select.h and sin6_len field of sockaddr_in6.  Check for clock_gettime in -lrt.
config_gram.l: Set sin_len and sin6_len in sockaddr's in parse_ip.
configuration.c: Suppress an IPv6 specific print when using IPv4.
sp.h, sess_types.h: Exported a new error type ILLEGAL_TIME.
sp.c: Rewrote SP_connect* to more cleanly and properly handle signals interrupting I/O.  Can now return ILLEGAL_TIME if user passes an invalid delta time.
buildtools/fixpaths: Minor change to get rid of deprecated warnings.



Modified: trunk/buildtools/fixpaths
===================================================================
--- trunk/buildtools/fixpaths	2017-01-23 08:22:11 UTC (rev 877)
+++ trunk/buildtools/fixpaths	2017-01-23 08:45:40 UTC (rev 878)
@@ -5,7 +5,7 @@
 
 $usage = "Usage: $0 [-Dstring=replacement] [[infile] ...]\n";
 
-if (!defined(@ARGV)) { die ("$usage"); }
+if (! @ARGV) { die ("$usage"); }
 
 # read in the command line and get some definitions
 while ($_=$ARGV[0], /^-/) {
@@ -23,7 +23,7 @@
   }
 } # while parsing arguments
 
-if (!defined(%def)) {
+if (! %def) {
   die ("$0: nothing to do - no substitutions listed!\n");
 }
 

Modified: trunk/configure
===================================================================
--- trunk/configure	2017-01-23 08:22:11 UTC (rev 877)
+++ trunk/configure	2017-01-23 08:45:40 UTC (rev 878)
@@ -4912,7 +4912,52 @@
 
 fi
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for clock_gettime in -lrt" >&5
+$as_echo_n "checking for clock_gettime in -lrt... " >&6; }
+if ${ac_cv_lib_rt_clock_gettime+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lrt  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
 
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char clock_gettime ();
+int
+main ()
+{
+return clock_gettime ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_rt_clock_gettime=yes
+else
+  ac_cv_lib_rt_clock_gettime=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_clock_gettime" >&5
+$as_echo "$ac_cv_lib_rt_clock_gettime" >&6; }
+if test "x$ac_cv_lib_rt_clock_gettime" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBRT 1
+_ACEOF
+
+  LIBS="-lrt $LIBS"
+
+fi
+
+
 for ac_func in strftime
 do :
   ac_fn_c_check_func "$LINENO" "strftime" "ac_cv_func_strftime"
@@ -4970,7 +5015,7 @@
 
 
 # Checks for header files.
-for ac_header in arpa/inet.h assert.h errno.h grp.h limits.h netdb.h netinet/in.h netinet/tcp.h process.h pthread.h pwd.h signal.h stdarg.h stdint.h stdio.h stdlib.h string.h sys/inttypes.h sys/ioctl.h sys/param.h sys/socket.h sys/stat.h sys/time.h sys/timeb.h sys/types.h sys/uio.h sys/un.h sys/filio.h time.h unistd.h windows.h winsock2.h ifaddrs.h
+for ac_header in arpa/inet.h assert.h errno.h grp.h limits.h netdb.h netinet/in.h netinet/tcp.h process.h pthread.h pwd.h signal.h stdarg.h stdint.h stdio.h stdlib.h string.h sys/inttypes.h sys/ioctl.h sys/param.h sys/socket.h sys/stat.h sys/time.h sys/timeb.h sys/types.h sys/uio.h sys/un.h sys/filio.h sys/select.h time.h unistd.h windows.h winsock2.h ifaddrs.h poll.h
 do :
   as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
 ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
@@ -4984,7 +5029,7 @@
 done
 
 
-for ac_func in bcopy inet_aton inet_ntoa inet_ntop memmove setsid snprintf strerror lrand48 getifaddrs
+for ac_func in bcopy inet_aton inet_ntoa inet_ntop memmove setsid snprintf strerror lrand48 getifaddrs select poll
 do :
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
 ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
@@ -6224,6 +6269,43 @@
 
 fi
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sin6_len field in sockaddr_in6" >&5
+$as_echo_n "checking for sin6_len field in sockaddr_in6... " >&6; }
+if ${ac_cv_have_sin6_len_in_struct_sockaddr_in6+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+	cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+int
+main ()
+{
+ struct sockaddr_in6 s; s.sin6_len = sizeof(s);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+   ac_cv_have_sin6_len_in_struct_sockaddr_in6="yes"
+else
+   ac_cv_have_sin6_len_in_struct_sockaddr_in6="no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_sin6_len_in_struct_sockaddr_in6" >&5
+$as_echo "$ac_cv_have_sin6_len_in_struct_sockaddr_in6" >&6; }
+if test x"$ac_cv_have_sin6_len_in_struct_sockaddr_in6" = "xyes"; then
+
+$as_echo "#define HAVE_SIN6_LEN_IN_SOCKADDR_IN6 1 " >>confdefs.h
+
+fi
+
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ss_family field in struct sockaddr_storage" >&5
 $as_echo_n "checking for ss_family field in struct sockaddr_storage... " >&6; }
 if ${ac_cv_have_ss_family_in_struct_ss+:} false; then :

Modified: trunk/configure.in
===================================================================
--- trunk/configure.in	2017-01-23 08:22:11 UTC (rev 877)
+++ trunk/configure.in	2017-01-23 08:45:40 UTC (rev 878)
@@ -102,14 +102,15 @@
 AC_SEARCH_LIBS(shm_open, rt posix4, [], [])
 AC_SEARCH_LIBS(nanosleep, rt posix4, [], [])
 AC_CHECK_LIB(m, sqrt)
+AC_CHECK_LIB(rt, clock_gettime)
 
 AC_FUNC_STRFTIME
 
 # Checks for header files.
-AC_CHECK_HEADERS(arpa/inet.h assert.h errno.h grp.h limits.h netdb.h netinet/in.h netinet/tcp.h process.h pthread.h pwd.h signal.h stdarg.h stdint.h stdio.h stdlib.h string.h sys/inttypes.h sys/ioctl.h sys/param.h sys/socket.h sys/stat.h sys/time.h sys/timeb.h sys/types.h sys/uio.h sys/un.h sys/filio.h time.h unistd.h windows.h winsock2.h ifaddrs.h)
+AC_CHECK_HEADERS(arpa/inet.h assert.h errno.h grp.h limits.h netdb.h netinet/in.h netinet/tcp.h process.h pthread.h pwd.h signal.h stdarg.h stdint.h stdio.h stdlib.h string.h sys/inttypes.h sys/ioctl.h sys/param.h sys/socket.h sys/stat.h sys/time.h sys/timeb.h sys/types.h sys/uio.h sys/un.h sys/filio.h sys/select.h time.h unistd.h windows.h winsock2.h ifaddrs.h poll.h)
 
 dnl    Checks for library functions.
-AC_CHECK_FUNCS(bcopy inet_aton inet_ntoa inet_ntop memmove setsid snprintf strerror lrand48 getifaddrs)
+AC_CHECK_FUNCS(bcopy inet_aton inet_ntoa inet_ntop memmove setsid snprintf strerror lrand48 getifaddrs select poll)
 dnl    Checks for time functions
 AC_CHECK_FUNCS(gettimeofday time)
 
@@ -550,6 +551,23 @@
    	AC_DEFINE(HAVE_SIN_LEN_IN_SOCKADDR_IN, 1 ,[sockaddr_in type has sin_len field])
 fi
 
+AC_CACHE_CHECK([for sin6_len field in sockaddr_in6],
+		ac_cv_have_sin6_len_in_struct_sockaddr_in6, [
+	AC_TRY_COMPILE(
+  		[
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+		],
+		[ struct sockaddr_in6 s; s.sin6_len = sizeof(s); ],
+		[ ac_cv_have_sin6_len_in_struct_sockaddr_in6="yes" ],
+		[ ac_cv_have_sin6_len_in_struct_sockaddr_in6="no"],
+	)
+])
+if test x"$ac_cv_have_sin6_len_in_struct_sockaddr_in6" = "xyes"; then
+   	AC_DEFINE(HAVE_SIN6_LEN_IN_SOCKADDR_IN6, 1 ,[sockaddr_in6 type has sin6_len field])
+fi
+
 AC_CACHE_CHECK([for ss_family field in struct sockaddr_storage],
 		ac_cv_have_ss_family_in_struct_ss, [
 	AC_TRY_COMPILE(

Modified: trunk/daemon/config.h.in
===================================================================
--- trunk/daemon/config.h.in	2017-01-23 08:22:11 UTC (rev 877)
+++ trunk/daemon/config.h.in	2017-01-23 08:45:40 UTC (rev 878)
@@ -3,7 +3,6 @@
 #ifndef _CONFIG_H
 #define _CONFIG_H
 
-
 /* Building on a Windows OS Platform */
 #undef ARCH_PC_WIN95
 
@@ -109,6 +108,12 @@
 /* pid_t type */
 #undef HAVE_PID_T
 
+/* Define to 1 if you have the `poll' function. */
+#undef HAVE_POLL
+
+/* Define to 1 if you have the <poll.h> header file. */
+#undef HAVE_POLL_H
+
 /* Define to 1 if you have the <process.h> header file. */
 #undef HAVE_PROCESS_H
 
@@ -127,12 +132,18 @@
 /* sockaddr type has sa_len field */
 #undef HAVE_SA_LEN_IN_SOCKADDR
 
+/* Define to 1 if you have the `select' function. */
+#undef HAVE_SELECT
+
 /* Define to 1 if you have the `setsid' function. */
 #undef HAVE_SETSID
 
 /* Define to 1 if you have the <signal.h> header file. */
 #undef HAVE_SIGNAL_H
 
+/* sockaddr_in6 type has sin6_len field */
+#undef HAVE_SIN6_LEN_IN_SOCKADDR_IN6
+
 /* sockaddr_in type has sin_len field */
 #undef HAVE_SIN_LEN_IN_SOCKADDR_IN
 
@@ -217,6 +228,9 @@
 /* Define to 1 if you have the <sys/param.h> header file. */
 #undef HAVE_SYS_PARAM_H
 
+/* Define to 1 if you have the <sys/select.h> header file. */
+#undef HAVE_SYS_SELECT_H
+
 /* Define to 1 if you have the <sys/socket.h> header file. */
 #undef HAVE_SYS_SOCKET_H
 
@@ -283,6 +297,9 @@
 /* Define to the one symbol short name of this package. */
 #undef PACKAGE_TARNAME
 
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
 /* Define to the version of this package. */
 #undef PACKAGE_VERSION
 

Modified: trunk/daemon/config_gram.l
===================================================================
--- trunk/daemon/config_gram.l	2017-01-23 08:22:11 UTC (rev 877)
+++ trunk/daemon/config_gram.l	2017-01-23 08:45:40 UTC (rev 878)
@@ -132,12 +132,18 @@
     val->addr.ipaddr.ipv4.sin_family = AF_INET;
     val->addr.ipaddr.ipv4.sin_addr   = ipv4addr;
     val->addr.ipaddr.ipv4.sin_port   = htons((int16u) port);
+#ifdef HAVE_SIN_LEN_IN_STRUCT_SOCKADDR_IN
+    val->addr.ipaddr.ipv4.sin_len    = sizeof(val->addr.ipaddr.ipv4);
+#endif    
   }
   else
   {
     val->addr.ipaddr.ipv6.sin6_family = AF_INET6;
     val->addr.ipaddr.ipv6.sin6_addr   = ipv6addr;
     val->addr.ipaddr.ipv6.sin6_port   = htons((int16u) port);
+#ifdef HAVE_SIN6_LEN_IN_STRUCT_SOCKADDR_IN6
+    val->addr.ipaddr.ipv6.sin6_len    = sizeof(val->addr.ipaddr.ipv6);
+#endif    
 
     if (hasif)
     {

Modified: trunk/daemon/configuration.c
===================================================================
--- trunk/daemon/configuration.c	2017-01-23 08:22:11 UTC (rev 877)
+++ trunk/daemon/configuration.c	2017-01-23 08:45:40 UTC (rev 878)
@@ -462,11 +462,14 @@
             continue;
           }
 
-          if (spu_addr_family(&My.ifc[i].ifaddr) == AF_INET6 && My.ifc[i].ifaddr.ipv6.sin6_scope_id == 0)
-            if ((My.ifc[i].ifaddr.ipv6.sin6_scope_id = ips[j].ipv6.sin6_scope_id) == 0)
+          if (spu_addr_family(&My.ifc[i].ifaddr) == AF_INET6)
+          {
+            if (My.ifc[i].ifaddr.ipv6.sin6_scope_id == 0 &&
+                (My.ifc[i].ifaddr.ipv6.sin6_scope_id = ips[j].ipv6.sin6_scope_id) == 0)
               My.ifc[i].ifaddr.ipv6.sin6_scope_id = scope_id;
 
-          Alarmp(SPLOG_INFO, CONF_SYS, "Conf_load_conf_file: using sin6_scope_id %u for interface %s\n", (unsigned) My.ifc[i].ifaddr.ipv6.sin6_scope_id, SPU_ADDR_NTOP(&My.ifc[i].ifaddr));
+            Alarmp(SPLOG_INFO, CONF_SYS, "Conf_load_conf_file: using sin6_scope_id %u for interface %s\n", (unsigned) My.ifc[i].ifaddr.ipv6.sin6_scope_id, SPU_ADDR_NTOP(&My.ifc[i].ifaddr));
+          }
         }
         
 	Alarmp(SPLOG_INFO, CONF_SYS, "Conf_load_conf_file: My name: %s, id: %s, addr: %s, port: %u\n",

Modified: trunk/daemon/sess_types.h
===================================================================
--- trunk/daemon/sess_types.h	2017-01-23 08:22:11 UTC (rev 877)
+++ trunk/daemon/sess_types.h	2017-01-23 08:45:40 UTC (rev 878)
@@ -142,6 +142,7 @@
 #define         MESSAGE_TOO_LONG        -17
 #define         NET_ERROR_ON_SESSION    -18
 #define         SP_BUG                  -19
+#define         ILLEGAL_TIME            -20
 
 typedef	struct	dummy_message_header {
 	int32u	type;

Modified: trunk/include/sp.h
===================================================================
--- trunk/include/sp.h	2017-01-23 08:22:11 UTC (rev 877)
+++ trunk/include/sp.h	2017-01-23 08:45:40 UTC (rev 878)
@@ -67,6 +67,10 @@
 #define         MAX_PRIVATE_NAME        10 /* largest possible size of private_name field of SP_connect() */
 #define         MAX_PROC_NAME           20 /* largest possible size of process name of daemon (includes nul terminator) */
 
+#if MAX_PRIVATE_NAME >= 128
+#  error "MAX_PRIVATE_NAME must fit in one signed byte!"
+#endif    
+    
 #define         UNRELIABLE_MESS         0x00000001
 #define         RELIABLE_MESS           0x00000002
 #define         FIFO_MESS               0x00000004
@@ -132,6 +136,7 @@
 #define         MESSAGE_TOO_LONG        (-17)
 #define         NET_ERROR_ON_SESSION    (-18)
 #define         SP_BUG                  (-19)
+#define         ILLEGAL_TIME            (-20)
 
 #define		MAX_CLIENT_SCATTER_ELEMENTS	100
 

Modified: trunk/libspread/sp.c
===================================================================
--- trunk/libspread/sp.c	2017-01-23 08:22:11 UTC (rev 877)
+++ trunk/libspread/sp.c	2017-01-23 08:45:40 UTC (rev 878)
@@ -53,12 +53,20 @@
 #include <sys/ioctl.h>
 #include <unistd.h>
 
+#  ifdef HAVE_SYS_SELECT_H
+#    include <sys/select.h>
+#  endif
+
+#  ifdef HAVE_POLL
+#    include <poll.h>
+#  endif
+
 #else	/* ARCH_PC_WIN95 */
 
 #include <winsock2.h>
 #define ioctl   ioctlsocket
 
-	WSADATA		WSAData;
+WSADATA	WSAData;
 
 #endif	/* ARCH_PC_WIN95 */
 
@@ -145,16 +153,395 @@
 static	int		Num_sessions = 0;
 static	sp_session	Sessions[MAX_LIB_SESSIONS];
 
-static  sp_time         Zero_timeout = { 0, 0 };
-
 static	void    Flip_mess( message_header *head_ptr );
 static	int	SP_get_session( mailbox mbox );
+
+static  int     SP_connect_timeout_low( const char *spread_name, const char *private_name,
+                                        int priority, int group_membership, mailbox *mbox,
+                                        char *private_group, sp_time *time_out );
+
 static	int	SP_internal_multicast( mailbox mbox, service service_type, 
 				       int num_groups,
 				       const char groups[][MAX_GROUP_NAME],
 				       int16 mess_type,
 				       const scatter *scat_mess );
 
+/* wait_nointr waits for read or write readiness on one socket while
+ * ignoring signal interruptions with an optional absolute timeout.
+ *
+ * Returns 0 on success when the socket is ready in time and non-zero
+ * otherwise.
+ *
+ * Returns 1 on timeout, otherwise returns -1 with the specific error
+ * indicated in sock_errno.
+ */
+
+static int wait_nointr( int sock, int readable, const sp_time *abs_time_out )
+#ifdef HAVE_POLL
+{
+    int ret;
+    int tmp;
+
+    do
+    {
+        struct pollfd fd    = { 0 };
+        int           to_ms = -1;
+        
+        fd.fd     = sock;
+        fd.events = ( readable ? POLLIN : POLLOUT );
+
+        if ( abs_time_out )
+        {
+            sp_time t = E_sub_time( *abs_time_out, E_get_time_monotonic() );
+
+            if ( t.sec < 0 || t.usec < 0 )
+            {
+                ret = 1;
+                break;
+            }
+            else if ( t.sec >= INT_MAX / 1000 )
+                to_ms = INT_MAX;
+
+            else
+                to_ms = ( int ) ( t.sec * 1000 + ( t.usec + 999 ) / 1000 );
+        }
+    
+        tmp = poll( &fd, 1, to_ms );
+
+        switch ( tmp )
+        {
+        case 1:  ret =  0; break;
+        case 0:  ret =  1; break;
+        default: ret = -1; break;
+        }
+    }
+    while ( tmp == -1 && sock_errno == EINTR );
+    
+    return ret;
+}
+#else
+{
+    int ret;
+    int tmp;
+
+    do
+    {
+        struct timeval sel_time_ptr = NULL;
+        struct timeval sel_time;
+        fd_set         sel_set;
+
+        FD_ZERO( &sel_set );
+        FD_SET( sock, &sel_set );
+
+        if ( abs_time_out )
+        {
+            sp_time t = E_sub_time( *abs_time_out, E_get_time_monotonic() );
+
+            if ( t.sec < 0 || t.usec < 0 )
+            {
+                ret = 1;
+                break;
+            }
+
+            sel_time.tv_sec  = t.sec;
+            sel_time.tv_usec = t.usec;
+            sel_time_ptr     = &sel_time;
+        }
+
+        tmp = select( s + 1, ( readable ? &sel_set : NULL ), ( readable ? NULL : &sel_set ), NULL, sel_time_ptr );
+
+        switch ( tmp )
+        {
+        case 1:  ret =  0; break;
+        case 0:  ret =  1; break;
+        default: ret = -1; break;
+        }
+    }
+    while ( tmp == -1 && sock_errno == EINTR );
+    
+    return ret;
+}
+#endif
+
+/* connect_nointr calls connect on a socket while ignoring signal
+ * interruptions with an optional absolute timeout.
+ *
+ * Returns 0 on success when the socket is connected in time and
+ * non-zero otherwise.
+ * 
+ * Returns 1 on timeout, otherwise returns -1 with the specific error
+ * indicated in sock_errno.
+ */
+
+static int connect_nointr( int s, struct sockaddr *addr, socklen_t addr_len, sp_time *abs_time_out )
+{
+    int       ret;
+    int       err;
+    socklen_t err_len;
+
+    if ( ( ret = connect( s, addr, addr_len ) ) != 0 && ( sock_errno == EINPROGRESS || sock_errno == EINTR || sock_errno == EAGAIN || sock_errno == EWOULDBLOCK ) )
+    {
+        ret = wait_nointr( s, 0, abs_time_out );
+
+        switch ( ret )
+        {
+        case 0:   /* writeable */
+            if ( ( ret = getsockopt( s, SOL_SOCKET, SO_ERROR, ( err = 0, &err ), ( err_len = sizeof( err ), &err_len ) ) ) == 0 && err != 0 )
+            {
+                sock_set_errno( err );
+                ret = -1;
+            }            
+            break;
+            
+        case 1:   /* timed out */
+            break;
+            
+        default:  /* error */
+            break;
+        }
+    }
+
+    return ret;
+}
+
+#if 0
+/* recv_n_nointr calls recv repeatedly until either a total of exactly
+ * n bytes have been received or an error occurs, all while ignoring
+ * signal interruptions.
+ * 
+ * recv_n_nointr returns the remaining number of bytes left to recv.
+ * On success, it returns 0.  On failure, the non-zero number of bytes
+ * left to recv is returned and sock_errno indicates the error.
+ *
+ * If n is less than zero, then the call will immediately return n and
+ * sock_errno will be set to EINVAL.  If n is zero, then the call will
+ * immediately succeed and return 0.
+ *
+ * This function is meant to be used with blocking sockets.  If called
+ * on a non-blocking socket, then a short read and a blocking error
+ * can be returned.
+ *
+ * NOTE: We don't use MSG_WAITALL as there are reports of large
+ * receives improperly blocking forever with this option on some
+ * implementations when OS buffers are full.
+ */
+
+static int recv_n_nointr( int s, char *b, int n )
+{
+    int tmp;
+
+    for ( ; n > 0; n -= tmp, b += tmp )
+    {
+        tmp = ( int ) recv( s, b, n, 0 );
+    
+        if ( tmp <= 0 )
+        {
+            if ( tmp == -1 && sock_errno == EINTR )
+                tmp = 0;
+
+            else
+            {
+                if ( tmp == 0 )
+                    sock_set_errno( ECONNRESET );
+                    
+                break;
+            }
+        }
+    }
+  
+    if ( n < 0 )
+        sock_set_errno( EINVAL );  /* handle cases where n was initially negative or tmp > n */
+  
+    return n;
+}
+
+/* send_n_nointr calls send repeatedly until either a total of
+ * exactly n bytes have been sent or an error occurs, all while
+ * ignoring signal interruptions.
+ * 
+ * send_n_nointr returns the remaining number of bytes left to send.
+ * On success, it returns 0.  On failure, the non-zero number of bytes
+ * left to send is returned with sock_errno indicating the error.
+ *
+ * This function is meant to be used with blocking sockets.  If called
+ * on a non-blocking socket, then a short send and a blocking error
+ * can be returned.
+ */
+
+static int send_n_nointr( int s, char *b, int n )
+{
+    int tmp;
+
+    for ( ; n > 0; n -= tmp, b += tmp )
+    {
+        tmp = ( int ) send( s, b, n, 0 );
+      
+        if ( tmp <= 0 )
+        {
+            if ( tmp == -1 && sock_errno == EINTR )
+                tmp = 0;
+
+            else
+            {
+                if ( tmp == 0 )
+                    sock_set_errno( ECONNRESET );
+
+                break;
+            }
+        }
+    }
+    
+    if ( n < 0 )
+        sock_set_errno( EINVAL );  /* handle cases where n was initially negative or tmp > n */
+  
+    return n;
+}
+#endif
+
+/* recv_n_nointr_nb calls recv repeatedly until either a total of
+ * exactly n bytes have been received, an error occurs, or an optional
+ * absolute timeout expires, all while ignoring signal interruptions.
+ *
+ * recv_n_nointr_nb returns the remaining number of bytes left to
+ * recv.  On success, it returns 0.  On failure, the non-zero number
+ * of bytes left to recv is returned and sock_errno indicates the
+ * error.  If the timeout expires before all n bytes could be
+ * received, then the non-zero number of bytes left to recv is
+ * returned and sock_errno will be set to EAGAIN.
+ *
+ * If n is less than zero, then the call will immediately return n and
+ * sock_errno will be set to EINVAL.  If n is zero, then the call will
+ * immediately succeed and return 0.
+ *
+ * abs_time_out can be NULL, in which case the timeout is infinite.
+ *
+ * This function is meant to be used with nonblocking sockets.  If
+ * called on a blocking socket, then any timeout may not be respected
+ * and the function can block for arbitrary periods of time.
+ *
+ * NOTE: We don't use MSG_WAITALL as there are reports of large
+ * receives improperly blocking forever with this option on some
+ * implementations when OS buffers are full.
+ */
+
+static int recv_n_nointr_nb( int s, char *b, int n, const sp_time *abs_time_out )
+{
+    int tmp;
+
+    for ( ; n > 0; n -= tmp, b += tmp )
+    {
+        /* check if optional timeout has expired */
+          
+        if ( abs_time_out && E_compare_time( E_get_time_monotonic(), *abs_time_out ) >= 0 )
+        {
+            sock_set_errno( EAGAIN );
+            break;
+        }
+          
+        /* recv and handle error cases; fatal errors break loop, non-fatal errors set tmp = 0 and continue */
+          
+        tmp = ( int ) recv( s, b, n, 0 );
+
+        if ( tmp <= 0 )
+        {
+            if ( tmp == -1 && ( sock_errno == EAGAIN || sock_errno == EWOULDBLOCK ) )
+            {
+                if ( ( tmp = wait_nointr( s, 1, abs_time_out ) ) != 0 )
+                {
+                    if ( tmp == 1 )
+                        sock_set_errno( EAGAIN );
+                        
+                    break;
+                }
+            }
+            else if ( tmp != -1 || sock_errno != EINTR )
+            {
+                if ( tmp == 0 )
+                    sock_set_errno( ECONNRESET );
+
+                break;
+            }
+
+            tmp = 0;
+        }
+    }
+
+    if ( n < 0 )
+        sock_set_errno( EINVAL );  /* handle cases where n was initially negative or tmp > n */
+
+    return n;
+}  
+
+/* send_n_nointr_nb calls send repeatedly until either a total of
+ * exactly n bytes have been sent, an error occurs, or an optional
+ * absolute timeout expires, all while ignoring signal interruptions.
+ *
+ * send_n_nointr_nb returns the remaining number of bytes left to
+ * send.  On success, it returns 0.  On failure, the non-zero number
+ * of bytes left to send is returned and sock_errno indicates the
+ * error.  If the timeout expires before all n bytes could be sent,
+ * then the non-zero number of bytes left to send is returned and
+ * sock_errno will be set to EAGAIN.  
+ *
+ * If n is less than zero, then the call will immediately return n and
+ * sock_errno will be set to EINVAL.  If n is zero, then the call will
+ * immediately succeed and return 0.
+ *
+ * abs_time_out can be NULL, in which case the timeout is infinite.
+ *
+ * This function is meant to be used with nonblocking sockets.  If
+ * called on a blocking socket, then any timeout may not be respected
+ * and the function can block for arbitrary periods of time.
+ */
+
+static int send_n_nointr_nb( int s, char *b, int n, const sp_time *abs_time_out )
+{
+    int tmp;
+
+    for ( ; n > 0; n -= tmp, b += tmp )
+    {
+        /* check if optional timeout has expired */
+          
+        if ( abs_time_out && E_compare_time( E_get_time_monotonic(), *abs_time_out ) >= 0 )
+        {
+            sock_set_errno( EAGAIN );
+            break;
+        }
+          
+        /* send and handle error cases; fatal errors break loop, non-fatal errors set tmp = 0 and continue */
+          
+        tmp = (int) send( s, b, n, 0 );
+
+        if ( tmp <= 0 )
+        {
+            if ( tmp == -1 && ( sock_errno == EAGAIN || sock_errno == EWOULDBLOCK ) )
+            {
+                if ( ( tmp = wait_nointr( s, 0, abs_time_out ) ) != 0 )
+                {
+                    if ( tmp == 1 )
+                        sock_set_errno( EAGAIN );
+                    
+                    break;
+                }
+            }
+            else if ( tmp != -1 || sock_errno != EINTR )
+            {
+                if ( tmp == 0 )
+                    sock_set_errno( ECONNRESET );
+
+                break;
+            }
+
+            tmp = 0;
+        }
+    }
+
+    if ( n < 0 )
+        sock_set_errno( EINVAL );  /* handle cases where n was initially negative or tmp > n */
+
+    return n;
+}  
+
 /* This is a null authenticate method that does nothing */
 
 static  int     sp_null_authenticate(int fd, void * auth_data)
@@ -271,172 +658,7 @@
 
         return;
 }
-/* This calls recv() with the additional features of ignoring syscall interruptions
- * caused by signals delivered to this application, and of returning in at most *time_out time.
- * When it returns the *time_out variable will be modified to have contain the value:
- * old *time_out - time spent in this function.
- *
- * If *time_out == {0,0} then the call is made blocking and will NOT timeout.
- */
-static  int     recv_nointr_timeout(int s, void *buf, size_t len, int flags, sp_time *time_out)
-{
-        int ret, num_ready;
-        fd_set rset,fixed_rset;
-        sp_time start_time, temp_time, target_time, wait_time;
-        struct timeval  sel_time;
 
-        if ( len == 0 )
-                return(0);
-        if ( E_compare_time(Zero_timeout, *time_out) < 0 )
-        {
-                start_time = E_get_time();
-                target_time = E_add_time(start_time, *time_out);
-                wait_time = *time_out;
-                FD_ZERO(&fixed_rset);
-                FD_SET(s, &fixed_rset);
-                rset = fixed_rset;
-                sel_time.tv_sec = wait_time.sec;
-                sel_time.tv_usec = wait_time.usec;
-                while( ((num_ready = select(s+1, &rset, NULL, NULL, &sel_time)) == -1) && ((sock_errno == EINTR) || (sock_errno == EAGAIN) || (sock_errno == EWOULDBLOCK)) )
-                {
-                        temp_time = E_get_time();
-                        if (E_compare_time(temp_time, target_time) < 0 ) {
-                                wait_time = E_sub_time(target_time, temp_time);
-                                sel_time.tv_sec = wait_time.sec;
-                                sel_time.tv_usec = wait_time.usec;
-                        } else {
-                                sock_set_errno( ERR_TIMEDOUT );
-                                return(-1);
-                        }
-                        rset = fixed_rset;
-                }
-                if ( !num_ready ) {
-                        sock_set_errno( ERR_TIMEDOUT );
-                        return(-1);
-                } 
-        }
-        while( ((ret = recv( s, buf, len, flags)) == -1) && ((sock_errno == EINTR) || (sock_errno == EAGAIN) || (sock_errno == EWOULDBLOCK)) )
-                ;
-        if ( E_compare_time(Zero_timeout, *time_out) < 0 )
-        {
-                temp_time = E_sub_time(E_get_time(), start_time);
-                *time_out = E_sub_time(*time_out, temp_time);
-        }
-        return(ret);
-}
-
-/* This calls connect() with the additional features of ignoring syscall interruptions
- * caused by signals delivered to this application, and of returning in at most *time_out time.
- * When it returns the *time_out variable will be modified to have contain the value:
- * old *time_out - time spent in this function.
- *
- * If *time_out == {0,0} then the call is made blocking and will NOT timeout.
- */
-static  int     connect_nointr_timeout(int s, struct sockaddr *sname, socklen_t slen, sp_time *time_out)
-{
-    int         ret, num_ready;
-    fd_set      rset,fixed_rset,wset;
-    sp_time     start_time, temp_time, target_time, wait_time;
-    struct timeval sel_time;
-    int         non_blocking = 0;
-    int         err = 0;
-    int         on;
-    int         ret_ioctl;
-    sockopt_len_t   elen;
-
-    /* initialize time values even if blocking so later use is valid */
-    start_time = E_get_time();
-    target_time = E_add_time(start_time, *time_out);
-    wait_time = *time_out;
-    sel_time.tv_sec = wait_time.sec;
-    sel_time.tv_usec = wait_time.usec;
-
-    if ( E_compare_time(Zero_timeout, *time_out) < 0 )
-    {
-        /* set file descriptor to non-blocking */
-        non_blocking = 1;
-        on = 1;
-        ret_ioctl = ioctl( s, FIONBIO, &on);
-    }
-    /* Handle EINTR while connecting by waiting with select until the 
-     * connect completes or fails.  This is a while loop but it is never 
-     * done more then once. The while is so we can use 'break' 
-     */
-    while( ((ret = connect( s, sname, slen ) ) == -1) 
-           && ((sock_errno == EINTR) || (sock_errno == EAGAIN) || (sock_errno == EWOULDBLOCK) || (sock_errno == EINPROGRESS)) )
-    {
-        FD_ZERO(&fixed_rset);
-        FD_SET(s, &fixed_rset);
-        rset = fixed_rset;
-        wset = rset;
-        Alarmp( SPLOG_DEBUG, SESSION, "connect_nointr_timeout: connect in progress for socket %d, now wait in select\n", s);
-        /* wait for select to timeout (num_ready == 0), give a permanent error (num_ready < 0 && sock_errno != transient). If transient error, retry after checking to make sure timeout has not expired */
-        while( ((num_ready = select(s+1, &rset, &wset, NULL, &sel_time)) == -1) && ((sock_errno == EINTR) || (sock_errno == EAGAIN) || (sock_errno == EWOULDBLOCK)) )
-        {
-            temp_time = E_get_time();
-            if (E_compare_time(temp_time, target_time) < 0 ) {
-                wait_time = E_sub_time(target_time, temp_time);
-                sel_time.tv_sec = wait_time.sec;
-                sel_time.tv_usec = wait_time.usec;
-            } else {
-                Alarmp( SPLOG_WARNING, SESSION, "connect_nointr_timeout: connect interrupted and select wait timesout during transient error: %s\n", sock_strerror(sock_errno));
-                close(s);
-                sock_set_errno( ERR_TIMEDOUT );
-                ret = -1;
-                goto done_connect_try;
-            }
-            rset = fixed_rset;
-            wset = rset;
-        }
-        if ( num_ready == 0 ) {
-            /* timeout */
-            close(s);
-            sock_set_errno( ERR_TIMEDOUT );
-            ret = -1;
-            break;
-        } else if ( num_ready < 0 ) 
-        {
-            Alarmp( SPLOG_WARNING, SESSION, "connect_nointr_timeout: connect interrupted and error in select wait: %s\n", sock_strerror(sock_errno));
-            ret = -1;
-            break;
-        } 
-        if (FD_ISSET(s, &rset) || FD_ISSET( s, &wset))
-        {
-            err = 0;
-            elen = sizeof(err);
-            if (getsockopt(s, SOL_SOCKET, SO_ERROR, (void *)&err, &elen) < 0)
-            {
-                ret = -1;
-                break;
-            }
-            if (err)
-            {
-                sock_set_errno( err );
-                ret = -1;
-            } else {
-                ret = 0;
-            }
-            break;
-        } else {
-            Alarmp( SPLOG_FATAL, SESSION, "connect_nointr_timeout: connect interrupted--but select does not indicate either error or connecting socket ready. Impossible condition (i.e. bug).  ret= %d: %s\n", err, sock_strerror(sock_errno));
-            ret = -1;
-            break;
-        }
-    } /* while error case for connect */
-    Alarmp( SPLOG_DEBUG, SESSION, "connect_nointr_timeout: After connect, ret = %d error is:%s\n", ret, sock_strerror(sock_errno));
-
-done_connect_try:
-    if ( non_blocking )
-    {
-        /* set file descriptor to blocking */
-        on = 0;
-        ret_ioctl = ioctl( s, FIONBIO, &on);
-        temp_time = E_sub_time(E_get_time(), start_time);
-        *time_out = E_sub_time(*time_out, temp_time);
-    }
-    return(ret);
-}
-
 /* Increase socket buffer size to 200Kb if possible.
  * Used in SP_connect family when connection is established.
  */
@@ -558,20 +780,37 @@
 		    int priority, int group_membership, mailbox *mbox,
 		    char *private_group )
 {
-        int ret;
-
-        ret = SP_connect_timeout( spread_name, private_name, priority, group_membership, mbox, private_group, Zero_timeout);
-        return(ret);
+    return SP_connect_timeout_low( spread_name, private_name, priority, group_membership, mbox, private_group, NULL );
 }
 
 int	SP_connect_timeout( const char *spread_name, const char *private_name,
 		    int priority, int group_membership, mailbox *mbox,
 		    char *private_group, sp_time time_out )
 {
+    sp_time *t = NULL;
+
+    if ( ( time_out.sec < 0 && time_out.usec > 0 ) ||
+         ( time_out.sec > 0 && time_out.usec < 0 ) ||
+         ( time_out.usec <= -1000000 || time_out.usec >= 1000000 ) )
+        return ILLEGAL_TIME;
+    
+    if ( time_out.sec != 0 || time_out.usec != 0 )  /* NOTE: maintain existing API: timeout of (0, 0) means infinite timeout */
+    {
+        time_out = E_add_time( E_get_time_monotonic(), time_out );
+        t        = &time_out;
+    }
+    
+    return SP_connect_timeout_low( spread_name, private_name, priority, group_membership, mbox, private_group, t );
+}
+
+static int SP_connect_timeout_low( const char *spread_name, const char *private_name,
+                                   int priority, int group_membership, mailbox *mbox,
+                                   char *private_group, sp_time *abs_time_out )
+{
 	int16u			port;
 	char			host_name[SPREAD_MAXCONNECT_NAMELEN + 1];
-	char			conn[MAX_PRIVATE_NAME+5];
-        signed char             auth_list_len;
+	char                    conn[ 5 + MAX_PRIVATE_NAME ];
+        char                    auth_list_len;
         char                    auth_list[MAX_AUTH_NAME * MAX_AUTH_METHODS];
         char                    auth_choice[MAX_AUTH_NAME * MAX_AUTH_METHODS];
         bool                    failed;
@@ -581,10 +820,10 @@
 	int                     ses;
 	int                     base_ses;
 	int			ret, i;
-        unsigned int            len;
+        int                     len;
 	int			sp_v1, sp_v2, sp_v3;
-	char		        cval;
 	int32			on;
+        int                     tmp;
 
         spu_addr                addr      = { 0 };
 	struct sockaddr        *sock_addr;
@@ -637,7 +876,7 @@
 		break;
 
 #else	/* ARCH_PC_WIN95 */
-		/* win32: intentional fall through this case to option <port_num>@<host_name> where <host_name> = localhost */
+		/* NOTE: win32: intentional fall through this case to option <port_num>@<host_name> where <host_name> = localhost */
 		strcpy( host_name, "@localhost" );
 #endif	/* ARCH_PC_WIN95 */
 
@@ -722,15 +961,23 @@
 
 	set_large_socket_buffers(s);
 
-	ret = connect_nointr_timeout( s, sock_addr, sock_len, &time_out);
+        if ( ioctl( s, FIONBIO, ( tmp = 1, &tmp ) ) == -1 )
+        {
+		Alarm( SESSION, "SP_connect: unable to set non-blocking mode on mailbox %d: %s\n", s, sock_strerror( sock_errno ) );
+		close( s );
+		return( COULD_NOT_CONNECT );
+        }
+
+	ret = connect_nointr( s, sock_addr, sock_len, abs_time_out );
+        
 	if( ret < 0 )
 	{
 		Alarm( SESSION, "SP_connect: unable to connect mailbox %d: %s\n", s, sock_strerror(sock_errno));
 		close( s );
 		return( COULD_NOT_CONNECT );
 	}
-	/* 
-	 * connect message looks like:
+        
+        /* client connect message looks like:
 	 *
 	 * byte - version of lib
 	 * byte - subversion of lib
@@ -738,79 +985,78 @@
 	 * byte - lower half byte 1/0 with or without groups, upper half byte: priority (0/1).
 	 * byte - len of name
 	 * len bytes - name
-	 *
 	 */
+
+        len = 0;
+
+        if ( private_name != NULL &&
+             ( len = strlen( private_name ) ) > MAX_PRIVATE_NAME )
+                len = MAX_PRIVATE_NAME;
+        
 	conn[0] = SP_MAJOR_VERSION;
 	conn[1] = SP_MINOR_VERSION;
 	conn[2] = SP_PATCH_VERSION;
+        conn[3] = ( ( ( priority >= 1 ) << 4 ) | ( group_membership != 0 ) );
+	conn[4] = len;
+        memcpy( &conn[5], private_name, len );
 
-	if( group_membership ) conn[3] = 1; 
-	else conn[3] = 0;
-	if(priority < 0) priority = 0;
-	if(priority > 1) priority = 1;
-	conn[3] = conn[3]+16*priority;
+        ret = send_n_nointr_nb( s, conn, 5+len, abs_time_out );
 
-        if (private_name == NULL)
+        if ( ret )
         {
-                len = 0;
-        } else {
-                len = strlen(private_name);
-                if( len > MAX_PRIVATE_NAME ) len = MAX_PRIVATE_NAME;
-                memcpy( &conn[5], private_name, len );
-        }
-	conn[4] = len;
-	while(((ret = send( s, conn, len+5, 0 )) == -1) && ((sock_errno == EINTR) || (sock_errno == EAGAIN) || (sock_errno == EWOULDBLOCK)) )
-                ;
-	if( ret != len+5 )
-	{
-		Alarm( SESSION, "SP_connect: unable to send name %d %d: %s\n", ret, len+5, sock_strerror(sock_errno));
+		Alarm( SESSION, "SP_connect: unable to send connect handshake %d %d: %s\n", ret, 5+len, sock_strerror(sock_errno));
 		close( s );
 		return( CONNECTION_CLOSED );
-	}
-        /* Insert Access control and authentication checks here */
-        auth_list_len = 0;
-        ret = recv_nointr_timeout( s, &auth_list_len, 1, 0, &time_out); 
-        if ( ret <= 0 )
+        }
+        
+        /* insert access control and authentication checks here */
+
+        ret = recv_n_nointr_nb( s, &auth_list_len, sizeof( auth_list_len ), abs_time_out );
+        
+        if ( ret )
         {
 		Alarm( SESSION, "SP_connect: unable to read auth_list_len %d: %s\n", ret, sock_strerror(sock_errno));
 		close( s );
 		return( CONNECTION_CLOSED );
         }
-        if ( auth_list_len > (MAX_AUTH_NAME * MAX_AUTH_METHODS) )
+        
+        if ( auth_list_len < 0 ) 
         {
-		Alarm( SESSION, "SP_connect: illegal value in auth_list_len %d: %s\n", auth_list_len, sock_strerror(sock_errno));
+                Alarm( SESSION, "SP_connect: connection invalid with code %d while reading auth_list_len\n", auth_list_len );
+                close( s );
+                return( auth_list_len );
+        }
+        
+        if ( auth_list_len > MAX_AUTH_NAME * MAX_AUTH_METHODS )
+        {
+		Alarm( SESSION, "SP_connect: illegal value in auth_list_len %d: %s\n", auth_list_len, sock_strerror(sock_errno) );
 		close( s );
 		return( CONNECTION_CLOSED );
-        } 
-        if ( auth_list_len < 0 ) 
-        {
-            Alarm( SESSION, "SP_connect: connection invalid with code %d while reading auth_list_len\n", auth_list_len);
-            close( s );
-            return( auth_list_len );
         }
-        if ( auth_list_len != 0 )
+
+        auth_list[0] = 0;
+        
+        ret = recv_n_nointr_nb( s, auth_list, auth_list_len, abs_time_out);
+                
+        if ( ret )
         {
-                ret = recv_nointr_timeout( s, auth_list, auth_list_len, 0, &time_out);
-                if ( ret <= 0 )
-                {
-                        Alarm( SESSION, "SP_connect: unable to read auth_list %d: %s\n", ret, sock_strerror(sock_errno));
-                        close( s );
-                        return( CONNECTION_CLOSED );
-                }
-
-                Alarm( SESSION, "SP_connect: DEBUG: Auth list is: %s\n", auth_list);
-        } else {
-                Alarm( SESSION, "SP_connect: DEBUG: Auth list is empty\n");
+                Alarm( SESSION, "SP_connect: unable to read auth_list %d: %s\n", ret, sock_strerror(sock_errno));
+                close( s );
+                return( CONNECTION_CLOSED );
         }
 
+        Alarm( SESSION, "SP_connect: DEBUG: Auth list is: '%s'\n", auth_list );
+
         /* Here is where we check the list of available methods of authentication and pick one. 
          * For right now we just ignore the list and use the method the app set in SP_set_auth_method.
          * If no method was set we use the NULL method.
          * The global Auth_Methods struct needs to be protected by the Struct_mutex.
          */
+        
         memset(auth_choice, 0, MAX_AUTH_NAME * MAX_AUTH_METHODS);
+        
         Mutex_lock( &Struct_mutex );
-        for (i=0; i< Num_Reg_Auth_Methods; i++)
+        for (i = 0; i < Num_Reg_Auth_Methods; i++)
         {
                 auth_methods[i] = Auth_Methods[i];
                 memcpy(&auth_choice[i * MAX_AUTH_NAME], Auth_Methods[i].name, MAX_AUTH_NAME);
@@ -818,7 +1064,7 @@
         num_auth_methods = Num_Reg_Auth_Methods;
         Mutex_unlock( &Struct_mutex );
 
-        for (i=0; i < num_auth_methods; i++)
+        for ( i = 0; i < num_auth_methods; i++ )
         {
                 if ( !valid_auth_method(auth_methods[i].name, auth_list, auth_list_len) )
                 {
@@ -827,9 +1073,10 @@
                         return( REJECT_AUTH );
                 }
         }
-	while(((ret = send( s, auth_choice, MAX_AUTH_NAME * MAX_AUTH_METHODS, 0 )) == -1) && ((sock_errno == EINTR) || (sock_errno == EAGAIN) || (sock_errno == EWOULDBLOCK)) )
-                ;
-	if( ret != (MAX_AUTH_NAME * MAX_AUTH_METHODS) )
+
+        ret = send_n_nointr_nb( s, auth_choice, MAX_AUTH_NAME * MAX_AUTH_METHODS, abs_time_out );
+        
+	if ( ret )
 	{
 		Alarm( SESSION, "SP_connect: unable to send auth_name %d %d: %s\n", ret, MAX_AUTH_NAME * MAX_AUTH_METHODS, sock_strerror(sock_errno));
 		close( s );
@@ -843,72 +1090,82 @@
          * If failure (return 0) the SP_connect returns with an error REJECT_AUTH.
          * If authenticated (return 1 ), SP_connect continues with the rest of the connect protocol.
          */
+        
         failed = FALSE;
-        for (i = 0; i< num_auth_methods; i++)
+        
+        for ( i = 0; i < num_auth_methods; i++ )
         {
-                ret = auth_methods[i].authenticate(s, auth_methods[i].auth_data);
-                if (!ret)
+                ret = auth_methods[i].authenticate( s, auth_methods[i].auth_data );
+                
+                if ( !ret )
                 {
-                        Alarm( SESSION, "SP_connect: authentication of connection failed in method %s\n", auth_methods[i].name);
+                        Alarm( SESSION, "SP_connect: authentication of connection failed in method %s\n", auth_methods[i].name );
                         failed = TRUE;
                 }
         }
+        
         if ( failed )
         {
                 close ( s );
                 return( REJECT_AUTH );
         }
 
-	cval=0;
-        ret = recv_nointr_timeout( s, &cval, 1, 0, &time_out);
-	if( ret <= 0 )
+        /* server connect message looks like:
+	 *
+         * byte - accept / reject
+	 * byte - version of daemon
+	 * byte - subversion of daemon
+         * byte - patch version of daemon
+	 * byte - len of name
+	 * len bytes - name
+	 */
+
+        ret = recv_n_nointr_nb( s, conn, 5, abs_time_out );
+        
+	if ( ret )
 	{
-		Alarm( SESSION, "SP_connect: unable to read answer %d: %s\n", ret, sock_strerror(sock_errno));
-		close( s );
+                close( s );
+
+                if ( ret < 5 && conn[0] != ACCEPT_SESSION )
+                {
+                        Alarm( SESSION, "SP_connect: session rejected %d\n", conn[0] );
+                        return( conn[0] );
+                }
+                
+		Alarm( SESSION, "SP_connect: unable to read answer %d: %s\n", ret, sock_strerror(sock_errno) );
 		return( CONNECTION_CLOSED );
 	}
-	if( cval != ACCEPT_SESSION )
+        
+	if ( conn[0] != ACCEPT_SESSION )
 	{
-		Alarm( SESSION, "SP_connect: session rejected %d\n", cval);
 		close( s );
-		return( cval );
+		return( conn[0] );
 	}
-        ret = recv_nointr_timeout( s, &cval, 1, 0, &time_out);
-	if( ret <= 0 )
-	{
-		Alarm( SESSION, "SP_connect: unable to read version %d: %s\n", ret, sock_strerror(sock_errno));
-		close( s );
-		return( CONNECTION_CLOSED );
-	}
-	sp_v1 = cval;
 
-        ret = recv_nointr_timeout( s, &cval, 1, 0, &time_out);
-	if( ret <= 0 )
-	{
-		Alarm( SESSION, "SP_connect: unable to read subversion %d: %s\n", ret, sock_strerror(sock_errno));
-		close( s );
-		return( CONNECTION_CLOSED );
-	}
-	sp_v2 = cval;
+        sp_v1 = conn[1];
+        sp_v2 = conn[2];
+        sp_v3 = conn[3];
+        len   = conn[4];
+        
+	/* checking daemon version */
 
-        ret = recv_nointr_timeout( s, &cval, 1, 0, &time_out);
-	if( ret <= 0 )
-	{
-		Alarm( SESSION, "SP_connect: unable to read patch version %d: %s\n", ret, sock_strerror(sock_errno));
+        if ( sp_v1 < 0 || sp_v2 < 0 || sp_v3 < 0 || len < 0 || len >= MAX_GROUP_NAME )
+        {
+                Alarm( PRINT  , "SP_connect: illegal values received! %d.%d.%d; %d\n",
+                       sp_v1, sp_v2, sp_v3, len );
 		close( s );
 		return( CONNECTION_CLOSED );
-	}
-	sp_v3 = cval;
-
-	/* checking spread version. Should be at least 3.01 */
-	if( sp_v1*10000 + sp_v2*100 + sp_v3 < 30100 )
+        }
+        
+	if ( sp_v1 < 3 )
 	{
-		Alarm( PRINT  , "SP_connect: old spread version %d.%d.%d not suppoted\n", 
+		Alarm( PRINT  , "SP_connect: old daemon version %d.%d.%d not supported\n", 
 			sp_v1, sp_v2, sp_v3 );
 		close( s );
 		return( REJECT_VERSION );
 	}
-	if( (sp_v1*10000 + sp_v2*100 + sp_v3 < 30800) && priority > 0 )
+        
+	if ( priority > 0 && sp_v1 == 3 && sp_v2 < 8 )
 	{
 		Alarm( PRINT, "SP_connect: old spread version %d.%d.%d does not support priority other than 0\n", 
 			sp_v1, sp_v2, sp_v3 );
@@ -916,29 +1173,33 @@
 		return( REJECT_VERSION );
 	}
 
-        ret = recv_nointr_timeout( s, &cval, 1, 0, &time_out);
-	if( ret <= 0 )
+        ret = recv_n_nointr_nb( s, private_group, len, abs_time_out);
+        
+	if ( ret )
 	{
-		Alarm( SESSION, "SP_connect: unable to read size of group %d: %s\n", ret, sock_strerror(sock_errno));
-                close( s );
-		return( CONNECTION_CLOSED );
-	}
-	len = cval;
-        ret = recv_nointr_timeout( s, private_group, len, 0, &time_out);
-	if( ret <= 0 )
-	{
-		Alarm( SESSION, "SP_connect: unable to read private group %d:  %s\n", ret, sock_strerror(sock_errno));
+		Alarm( SESSION, "SP_connect: unable to read private group %d: %s\n", ret, sock_strerror(sock_errno));
 		close( s );
 		return( CONNECTION_CLOSED );
 	}
-	private_group[len]=0;
+        
+	private_group[len] = 0;
+        
 	Alarm( DEBUG, "SP_connect: connected with private group(%d bytes): %s\n", 
 		ret, private_group );
+        
 	*mbox = s;
 
+        if ( ioctl( s, FIONBIO, ( tmp = 0, &tmp ) ) == -1 )
+        {
+		Alarm( SESSION, "SP_connect: unable to set blocking mode on mailbox %d: %s\n", s, sock_strerror( sock_errno ) );
+		close( s );
+		return( CONNECTION_CLOSED );
+        }
+
 	Mutex_lock( &Struct_mutex );
 
-	if (Num_sessions >= MAX_LIB_SESSIONS) {
+	if (Num_sessions >= MAX_LIB_SESSIONS)
+        {
 	        Alarm( SESSION, "SP_connect: too many sessions in local process!\n");
 		close( s );
 		Mutex_unlock( &Struct_mutex );
@@ -951,13 +1212,13 @@
 
 	ses = base_ses = MBOX_TO_BASE_SES(s);
 
-	while( Sessions[ses].state != SESS_UNUSED ) {
-
-	        if( ++ses == MAX_LIB_SESSIONS) {
+	while( Sessions[ses].state != SESS_UNUSED )
+        {
+	        if( ++ses == MAX_LIB_SESSIONS)
 	                ses = 0;
-		}
 
-		if( ses == base_ses ) {
+		if( ses == base_ses )
+                {
 		        Alarm( SESSION, "SP_connect: BUG! No unused sessions when there should be!\n");
 			close( s );
 			Mutex_unlock( &Struct_mutex );
@@ -2164,6 +2425,7 @@
         case MESSAGE_TOO_LONG:     ret = "Message (body + group names) too large.";                       break;
         case NET_ERROR_ON_SESSION: ret = "Network socket error. SP_kill() the mailbox.";                  break;
         case SP_BUG:               ret = "Internal Spread bug detected.";                                 break;
+        case ILLEGAL_TIME:         ret = "Illegal time.";                                                 break;
 	}
 
         return ret;

Modified: trunk/release_announcement_5.0.txt
===================================================================
--- trunk/release_announcement_5.0.txt	2017-01-23 08:22:11 UTC (rev 877)
+++ trunk/release_announcement_5.0.txt	2017-01-23 08:45:40 UTC (rev 878)
@@ -1,12 +1,33 @@
-Spread 5.0.0 http://www.spread.org
+Spread 5.0.0 RC1 http://www.spread.org
 
-Spread Concepts LLC is happy to announce the release of a 
-new version, 5.0.0, of The Spread Toolkit. 
+Spread Concepts LLC is happy to announce a release candidate for a 
+new version, 5.0.0 RC1, of The Spread Toolkit.  It can be downloaded here:
 
-Spread 5.0 is an important release that adds support for IPv6.
+http://www.spread.org/download/spread-src-5.0.0rc1.tar.gz
 
-For details check the Readme.IPv6 and docs/sample.spread.conf files.
+Spread 5 is an important release that adds support for IPv6.
 
+Spread version 5 adds support for IPv6 in the Spread daemon, C
+language client library and several example and support programs
+(e.g. - spmonitor, spuser, spsend, sprecv, spflooder).  This version
+of Spread supports Posix environments (e.g. - Linux) and Windows.
+
+Spread 5 can work on IPv4-only hosts, IPv6-only hosts or dual stack
+hosts.  However, all daemons in a Spread deployment must run using
+only IPv4 or IPv6 addresses.  Spread 5 does not yet support
+configurations where some daemons use IPv4 addresses while others use
+IPv6 addresses, even if transition mechanisms such as network address
+translation (e.g. - stateless IP/ICMP translation) would allow such
+hosts to communicate using those addresses.
+
+Daemons' IP addresses can be configured explicitly or by using names
+which will be looked up using standard system services (i.e. - DNS,
+/etc/hosts, etc.) in order to map them back to IP addresses.  Clients
+can similarly specify either explicit addresses or names to connect to
+daemons.
+
+For more details check the Readme.IPv6 and docs/sample.spread.conf files.
+
 This release maintains the previous client API, so applications should
 be able to be relinked or recompiled with the new Spread library
 without changes.  However, if you used functionality from