OSSP CVS Repository

ossp - Difference in ossp-pkg/l2/l2_ut_sa.c versions 1.14 and 1.15
Not logged in
[Honeypot]  [Browse]  [Home]  [Login]  [Reports
[Search]  [Ticket]  [Timeline
  [History

ossp-pkg/l2/l2_ut_sa.c 1.14 -> 1.15

--- l2_ut_sa.c   2001/10/06 14:33:09     1.14
+++ l2_ut_sa.c   2001/10/10 15:58:27     1.15
@@ -1,7 +1,8 @@
 /*
 **  SA - OSSP Socket Abstraction Library
-**  Copyright (c) 2001 The OSSP Project (http://www.ossp.org/)
-**  Copyright (c) 2001 Cable & Wireless Deutschland (http://www.cw.com/de/)
+**  Copyright (c) 2001 Ralf S. Engelschall <rse@engelschall.com>
+**  Copyright (c) 2001 The OSSP Project <http://www.ossp.org/>
+**  Copyright (c) 2001 Cable & Wireless Deutschland <http://www.cw.com/de/>
 **
 **  This file is part of OSSP SA, a socket abstraction library which
 **  can be found at http://www.ossp.org/pkg/sa/.
@@ -33,10 +34,10 @@
 /* include system API headers */
 #include <stdio.h>       /* for "s[n]printf()" */
 #include <stdlib.h>      /* for "malloc()" & friends */
-#include <stdarg.h>      /* for "va_xxx()" and "va_list" */
-#include <string.h>      /* for "strxxx()" and "size_t" */
+#include <stdarg.h>      /* for "va_XXX()" and "va_list" */
+#include <string.h>      /* for "strXXX()" and "size_t" */
 #include <sys/types.h>   /* for general prerequisites */
-#include <ctype.h>       /* for isxxx() */
+#include <ctype.h>       /* for isXXX() */
 #include <errno.h>       /* for "EXXX" */
 #include <fcntl.h>       /* for "F_XXX" and "O_XXX" */
 #include <unistd.h>      /* for standard Unix stuff */
@@ -45,30 +46,136 @@
 #include <sys/un.h>      /* for "struct sockaddr_un" */
 #include <netinet/in.h>  /* for "struct sockaddr_in[6]" */
 #include <sys/socket.h>  /* for "AF_XXX" and "SOCK_XXX" */
-#include <arpa/inet.h>   /* for "inet_xtoy" */
+#include <arpa/inet.h>   /* for "inet_XtoX" */
 
 /* include own API header */
 #include "l2_ut_sa.h"
 
-/* socket address abstraction object */
-struct sa_addr_st {
-    int              nFamily;     /* the socket family (AF_XXX) */
-    struct sockaddr *saBuf;       /* the "struct sockaddr_xx" actually */
-    socklen_t        slBuf;       /* the length of "struct sockaddr_xx" */
-};
-
+/* system call structure declaration macros */
+#define SA_SC_DECLARE_0(rc_t, fn) \
+    struct { \
+        union { void (*any)(); \
+                rc_t (*std)(void); \
+                rc_t (*ctx)(void *); } fptr; \
+        void *fctx; \
+    } sc_##fn;
+#define SA_SC_DECLARE_1(rc_t, fn, a1_t) \
+    struct { \
+        union { void (*any)(); \
+                rc_t (*std)(a1_t); \
+                rc_t (*ctx)(void *, a1_t); } fptr; \
+        void *fctx; \
+    } sc_##fn;
+#define SA_SC_DECLARE_2(rc_t, fn, a1_t, a2_t) \
+    struct { \
+        union { void (*any)(); \
+                rc_t (*std)(a1_t, a2_t); \
+                rc_t (*ctx)(void *, a1_t, a2_t); } fptr; \
+        void *fctx; \
+    } sc_##fn;
+#define SA_SC_DECLARE_3(rc_t, fn, a1_t, a2_t, a3_t) \
+    struct { \
+        union { void (*any)(); \
+                rc_t (*std)(a1_t, a2_t, a3_t); \
+                rc_t (*ctx)(void *, a1_t, a2_t, a3_t); } fptr; \
+        void *fctx; \
+    } sc_##fn;
+#define SA_SC_DECLARE_4(rc_t, fn, a1_t, a2_t, a3_t, a4_t) \
+    struct { \
+        union { void (*any)(); \
+                rc_t (*std)(a1_t, a2_t, a3_t, a4_t); \
+                rc_t (*ctx)(void *, a1_t, a2_t, a3_t, a4_t); } fptr; \
+        void *fctx; \
+    } sc_##fn;
+#define SA_SC_DECLARE_5(rc_t, fn, a1_t, a2_t, a3_t, a4_t, a5_t) \
+    struct { \
+        union { void (*any)(); \
+                rc_t (*std)(a1_t, a2_t, a3_t, a4_t, a5_t); \
+                rc_t (*ctx)(void *, a1_t, a2_t, a3_t, a4_t, a5_t); } fptr; \
+        void *fctx; \
+    } sc_##fn;
+#define SA_SC_DECLARE_6(rc_t, fn, a1_t, a2_t, a3_t, a4_t, a5_t, a6_t) \
+    struct { \
+        union { void (*any)(); \
+                rc_t (*std)(a1_t, a2_t, a3_t, a4_t, a5_t, a6_t); \
+                rc_t (*ctx)(void *, a1_t, a2_t, a3_t, a4_t, a5_t, a6_t); } fptr; \
+        void *fctx; \
+    } sc_##fn;
+
+/* system call structure assignment macro */
+#define SA_SC_ASSIGN(sa, fn, ptr, ctx) \
+    do { \
+        (sa)->scSysCall.sc_##fn.fptr.any = (void (*)())(ptr); \
+        (sa)->scSysCall.sc_##fn.fctx = (ctx); \
+    } while(0)
+
+/* system call structure assignment macro */
+#define SA_SC_COPY(sa1, sa2, fn) \
+    do { \
+        (sa1)->scSysCall.sc_##fn.fptr.any = (sa2)->scSysCall.sc_##fn.fptr.any; \
+        (sa1)->scSysCall.sc_##fn.fctx     = (sa2)->scSysCall.sc_##fn.fctx; \
+    } while(0)
+
+/* system call function call macros */
+#define SA_SC_CALL_0(sa, fn) \
+    (   (sa)->scSysCall.sc_##fn.fctx != NULL \
+     ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx) \
+     : ((sa)->scSysCall.sc_##fn.fptr.std)() )
+#define SA_SC_CALL_1(sa, fn, a1) \
+    (   (sa)->scSysCall.sc_##fn.fctx != NULL \
+     ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1) \
+     : ((sa)->scSysCall.sc_##fn.fptr.std)(a1) )
+#define SA_SC_CALL_2(sa, fn, a1, a2) \
+    (   (sa)->scSysCall.sc_##fn.fctx != NULL \
+     ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1, a2) \
+     : ((sa)->scSysCall.sc_##fn.fptr.std)(a1, a2) )
+#define SA_SC_CALL_3(sa, fn, a1, a2, a3) \
+    (   (sa)->scSysCall.sc_##fn.fctx != NULL \
+     ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1, a2, a3) \
+     : ((sa)->scSysCall.sc_##fn.fptr.std)(a1, a2, a3) )
+#define SA_SC_CALL_4(sa, fn, a1, a2, a3, a4) \
+    (   (sa)->scSysCall.sc_##fn.fctx != NULL \
+     ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1, a2, a3, a4) \
+     : ((sa)->scSysCall.sc_##fn.fptr.std)(a1, a2, a3, a4) )
+#define SA_SC_CALL_5(sa, fn, a1, a2, a3, a4, a5) \
+    (   (sa)->scSysCall.sc_##fn.fctx != NULL \
+     ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1, a2, a3, a4, a5) \
+     : ((sa)->scSysCall.sc_##fn.fptr.std)(a1, a2, a3, a4, a5) )
+#define SA_SC_CALL_6(sa, fn, a1, a2, a3, a4, a5, a6) \
+    (   (sa)->scSysCall.sc_##fn.fctx != NULL \
+     ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1, a2, a3, a4, a5, a6) \
+     : ((sa)->scSysCall.sc_##fn.fptr.std)(a1, a2, a3, a4, a5, a6) )
+
+/* system call table */
+typedef struct {
+    SA_SC_DECLARE_3(int,     connect,  int, const struct sockaddr *, socklen_t)
+    SA_SC_DECLARE_3(int,     accept,   int, struct sockaddr *, socklen_t *)
+    SA_SC_DECLARE_5(int,     select,   int, fd_set *, fd_set *, fd_set *, struct timeval *)
+    SA_SC_DECLARE_3(ssize_t, read,     int, void *, size_t)
+    SA_SC_DECLARE_3(ssize_t, write,    int, const void *, size_t)
+    SA_SC_DECLARE_6(ssize_t, recvfrom, int, void *, size_t, int, struct sockaddr *, socklen_t *)
+    SA_SC_DECLARE_6(ssize_t, sendto,   int, const void *, size_t, int, const struct sockaddr *, socklen_t)
+} sa_syscall_tab_t;
+ 
 /* socket abstraction object */
 struct sa_st {
-    sa_type_t        eType;       /* socket type (stream or datagram) */
-    int              fdSocket;    /* socket file descriptor */
-    int              bTimeout;    /* timeout enabling flag */
-    struct timeval   tvTimeout;   /* timeout value (sec, usec) */
-    int              nReadLen;    /* read  buffer current length */
-    int              nReadSize;   /* read  buffer current size */
-    char            *cpReadBuf;   /* read  buffer memory chunk */
-    int              nWriteLen;   /* write buffer current length */
-    int              nWriteSize;  /* write buffer current size */
-    char            *cpWriteBuf;  /* write buffer memory chunk */
+    sa_type_t        eType;            /* socket type (stream or datagram) */
+    int              fdSocket;         /* socket file descriptor */
+    struct timeval   tvTimeout[4];     /* timeout values (sec, usec) */
+    int              nReadLen;         /* read  buffer current length */
+    int              nReadSize;        /* read  buffer current size */
+    char            *cpReadBuf;        /* read  buffer memory chunk */
+    int              nWriteLen;        /* write buffer current length */
+    int              nWriteSize;       /* write buffer current size */
+    char            *cpWriteBuf;       /* write buffer memory chunk */
+    sa_syscall_tab_t scSysCall;        /* table of system calls */
+};
+
+/* socket address abstraction object */
+struct sa_addr_st {
+    int              nFamily;          /* the socket family (AF_XXX) */
+    struct sockaddr *saBuf;            /* the "struct sockaddr_xx" actually */
+    socklen_t        slBuf;            /* the length of "struct sockaddr_xx" */
 };
 
 /* boolean values */
@@ -79,6 +186,10 @@
 #define TRUE  (!FALSE)
 #endif
 
+/* handy struct timeval check */
+#define SA_TVISZERO(tv) \
+    ((tv).tv_sec == 0 && (tv).tv_usec == 0)
+
 /* backward compatibility for AF_LOCAL */
 #if !defined(AF_LOCAL) && defined(AF_UNIX)
 #define AF_LOCAL AF_UNIX
@@ -132,62 +243,117 @@
 #endif
 }
 
-/* minimal vsnprintf(3) variant which supports %{c,s,d} only */
-static int sa_mvsnprintf(char *buffer, size_t bufsize, const char *format, va_list ap)
+/* minimal output-independent vprintf(3) variant which supports %{c,s,d,%} only */
+static int sa_mvxprintf(int (*output)(void *ctx, const char *buffer, size_t bufsize), void *ctx, const char *format, va_list ap)
 {
-    char *bufptr;
-    char *bufend;
     /* sufficient integer buffer: <available-bits> x log_10(2) + safety */
     char ibuf[((sizeof(int)*8)/3)+10]; 
     char *cp;
     char c;
     int d;
     int n;
+    int bytes;
 
-    bufptr = buffer;
-    bufend = buffer + bufsize - 1;
-    while ((c = *format++) != '\0' && bufptr < bufend) {
-        if (c == '%') {
-            c = *format++;
-            if (c == '%')
-                /* implement "%%" */
-                *bufptr++ = c;
-            else if (c == 'c')
-                /* implement "%c" */
-                *bufptr++ = (char)va_arg(ap, int);
+    if (format == NULL || ap == NULL)
+        return -1;
+    bytes = 0;
+    while (*format != '\0') {
+        if (*format == '%') {
+            c = *(format+1);
+            if (c == '%') {
+                /* expand "%%" */
+                cp = &c;
+                n = sizeof(char);
+            }
+            else if (c == 'c') {
+                /* expand "%c" */
+                c = (char)va_arg(ap, int);
+                cp = &c;
+                n = sizeof(char);
+            }
             else if (c == 's') {
-                /* implement "%s" */
+                /* expand "%s" */
                 if ((cp = (char *)va_arg(ap, char *)) == NULL)
                     cp = "(null)";
                 n = strlen(cp);
-                if ((bufptr + n) > bufend)
-                    n = bufend - bufptr;
-                memcpy(bufptr, cp, n);
-                bufptr += n;
             }
             else if (c == 'd') {
-                /* implement "%d" */
+                /* expand "%d" */
                 d = (int)va_arg(ap, int);
 #ifdef HAVE_SNPRINTF
                 snprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */
 #else
                 sprintf(ibuf, "%d", d);                /* implicitly secure */
 #endif
-                n = strlen(ibuf);
-                memcpy(bufptr, ibuf, n);
-                bufptr += n;
+                cp = ibuf;
+                n = strlen(cp);
             }
             else {
-                *bufptr++ = '%';
-                if (bufptr < bufend)
-                    *bufptr++ = c;
+                /* any other "%X" */
+                cp = (char *)format;
+                n  = 2;
             }
+            format += 2;
         }
-        else
-            *bufptr++ = c;
+        else {
+            /* plain text */
+            cp = (char *)format;
+            if ((format = strchr(cp, '%')) == NULL)
+                format = strchr(cp, '\0');
+            n = format - cp;
+        }
+        /* perform output operation */
+        if (output != NULL)
+            if ((n = output(ctx, cp, n)) == -1)
+                break;
+        bytes += n;
     }
-    *bufptr = '\0';
-    return (bufptr - buffer);
+    return bytes;
+}
+
+/* output callback function context for sa_mvsnprintf() */
+typedef struct {
+    char *bufptr;
+    size_t buflen;
+} sa_mvsnprintf_cb_t;
+
+/* output callback function for sa_mvsnprintf() */
+static int sa_mvsnprintf_cb(void *_ctx, const char *buffer, size_t bufsize)
+{
+    sa_mvsnprintf_cb_t *ctx = (sa_mvsnprintf_cb_t *)_ctx;
+
+    if (bufsize > ctx->buflen)
+        return -1;
+    memcpy(ctx->bufptr, buffer, bufsize);
+    ctx->bufptr += bufsize;
+    ctx->buflen -= bufsize;
+    return bufsize;
+}
+
+/* minimal vsnprintf(3) variant which supports %{c,s,d} only */
+static int sa_mvsnprintf(char *buffer, size_t bufsize, const char *format, va_list ap)
+{
+    int n;
+    sa_mvsnprintf_cb_t ctx;
+
+    if (format == NULL || ap == NULL)
+        return -1;
+    if (buffer != NULL && bufsize == 0)
+        return -1;
+    if (buffer == NULL) 
+        /* just determine output length */
+        n = sa_mvxprintf(NULL, NULL, format, ap);
+    else {
+        /* perform real output */
+        ctx.bufptr = buffer;
+        ctx.buflen = bufsize - 1;
+        n = sa_mvxprintf(sa_mvsnprintf_cb, &ctx, format, ap);
+    }
+    if (n != -1 && ctx.buflen == 0)
+        n = -1;
+    if (n != -1)
+        *(ctx.bufptr) = '\0';
+    return n;
 }
 
 /* minimal snprintf(3) variant which supports %{c,s,d} only */
@@ -196,10 +362,6 @@
     int chars;
     va_list ap;
 
-    /* argument sanity check(s) */
-    if (buffer == NULL || bufsize == 0 || format == NULL)
-        return 0;
-
     /* pass through to va_list based variant */
     va_start(ap, format);
     chars = sa_mvsnprintf(buffer, bufsize, format, ap);
@@ -292,16 +454,23 @@
     if (strncmp(uri, "unix:", 5) == 0) {
         /* parse URI */
         cpPath = uri+5;
-        if (cpPath[0] != '/')
-            return SA_ERR_ARG;
 
         /* mandatory(!) socket address structure initialization */
         memset(&un, 0, sizeof(un));
 
         /* fill-in socket address structure */
         n = strlen(cpPath);
+        if (n == 0)
+            return SA_ERR_ARG;
         if ((n+1) > sizeof(un.sun_path))
             return SA_ERR_MEM;
+        if (cpPath[0] != '/') {
+            if (getcwd(un.sun_path, sizeof(un.sun_path)-(n+1)) == NULL)
+                return SA_ERR_MEM;
+            cp = un.sun_path + strlen(un.sun_path);
+        }
+        else
+            cp = un.sun_path;
         memcpy(un.sun_path, cpPath, n+1);
         un.sun_family = AF_LOCAL;
 
@@ -313,7 +482,7 @@
     else if (strncmp(uri, "inet://", 7) == 0) {
         /* parse URI */
         cpHost = (char *)(uri+7);
-        if ((cp = strchr(cpHost, ':')) == NULL)
+        if ((cp = strrchr(cpHost, ':')) == NULL)
             return SA_ERR_ARG;
         *cp++ = '\0';
         cpPort = cp;
@@ -385,12 +554,14 @@
                 sf = AF_INET6;
             }
 #endif
+            else
+                return SA_ERR_ARG;
         }
+        else
+            return SA_ERR_ARG;
     }
-
-    /* make sure result variables are set */
-    if (sa == NULL || sl == 0 || sf == 0)
-        return SA_ERR_INT;
+    else
+        return SA_ERR_ARG;
 
     /* fill-in result address structure */
     if (saa->saBuf != NULL)
@@ -517,6 +688,104 @@
     return SA_OK;
 }
 
+sa_rc_t sa_addr_match(sa_addr_t *saa1, sa_addr_t *saa2, int prefixlen)
+{
+    const unsigned char *ucp1, *ucp2;
+    unsigned int uc1, uc2, mask;
+    size_t l1, l2;
+    int nFamily;
+    int nBytes;
+    int nBits;
+
+    /* argument sanity check(s) */
+    if (saa1 == NULL || saa2 == NULL || prefixlen < -1)
+        return SA_ERR_ARG;
+
+    /* match short circuiting */
+    if (saa1->nFamily != saa2->nFamily)
+        return SA_ERR_MTC;
+
+    /* determine address representation pointer and size */
+    nFamily = saa1->nFamily;
+    if (nFamily == AF_LOCAL) {
+        ucp1 = (const unsigned char *)(((struct sockaddr_un *)saa1->saBuf)->sun_path);
+        ucp2 = (const unsigned char *)(((struct sockaddr_un *)saa2->saBuf)->sun_path);
+        l1 = strlen(((struct sockaddr_un *)saa1->saBuf)->sun_path) * 8;
+        l2 = strlen(((struct sockaddr_un *)saa2->saBuf)->sun_path) * 8;
+        if (prefixlen == -1) {
+            if (l1 != l2)
+                return SA_ERR_MTC;
+            nBits = l1;
+        }
+        else {
+            if (l1 < prefixlen || l2 < prefixlen)
+                return SA_ERR_MTC;
+            nBits = prefixlen;
+        }
+    }
+    else if (nFamily == AF_INET) {
+        ucp1 = (const unsigned char *)&(((struct sockaddr_in *)saa1->saBuf)->sin_addr);
+        ucp2 = (const unsigned char *)&(((struct sockaddr_in *)saa2->saBuf)->sin_addr);
+        nBits = 32;
+    }
+#ifdef AF_INET6
+    else if (nFamily == AF_INET6) {
+        ucp1 = (const unsigned char *)&(((struct sockaddr_in6 *)saa1->saBuf)->sin6_addr);
+        ucp2 = (const unsigned char *)&(((struct sockaddr_in6 *)saa2->saBuf)->sin6_addr);
+        nBits = 128;
+#endif
+    }
+    else
+        return SA_ERR_INT;
+
+    /* make sure we do not compare than possible */
+    if (prefixlen > nBits)
+        return SA_ERR_ARG;
+
+    /* support equal matching (= all bits) */
+    if (prefixlen == -1)
+        prefixlen = nBits;
+
+    /* perform address representation comparison 
+       (assumption guaranteed by API: network byte order is used) */
+    nBytes = (prefixlen / 8);
+    nBits  = (prefixlen % 8);
+    if (nBytes > 0) {
+        if (memcmp(ucp1, ucp2, nBytes) != 0)
+            return SA_ERR_MTC;
+    }
+    if (nBits > 0) {
+        uc1 = ucp1[nBytes];
+        uc2 = ucp2[nBytes];
+        mask = (0xFF << (8-nBits)) & 0xFF;
+        if ((uc1 & mask) != (uc2 & mask))
+            return SA_ERR_MTC;
+    }
+    return SA_OK;
+}
+
+/* set timeouts if timeouts or done in kernel */
+static sa_rc_t sa_socket_settimeouts(sa_t *sa)
+{
+#if defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO)
+    if (sa->fdSocket != -1) {
+        if (SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_READ])) {
+            if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_RCVTIMEO,
+                           &sa->tvTimeout[SA_TIMEOUT_READ],
+                           sizeof(sa->tvTimeout[SA_TIMEOUT_READ])) < 0)
+                return SA_ERR_SYS;
+        }
+        if (SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_WRITE])) {
+            if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_SNDTIMEO,
+                           &sa->tvTimeout[SA_TIMEOUT_WRITE],
+                           sizeof(sa->tvTimeout[SA_TIMEOUT_WRITE])) < 0)
+                return SA_ERR_SYS;
+        }
+    }
+#endif
+    return SA_OK;
+}
+
 /* internal lazy/delayed initialization of underlying socket */
 static sa_rc_t sa_socket_init(sa_t *sa, int nFamily)
 {
@@ -566,6 +835,9 @@
     if ((sa->fdSocket = socket(nFamily, nType, nProto)) == -1)
         return SA_ERR_SYS;
 
+    /* optionally set kernel timeouts */
+    sa_socket_settimeouts(sa);
+
     return SA_OK;
 }
 
@@ -591,6 +863,7 @@
 sa_rc_t sa_create(sa_t **sap)
 {
     sa_t *sa;
+    int i;
 
     /* argument sanity check(s) */
     if (sap == NULL)
@@ -599,17 +872,31 @@
     /* allocate and initialize socket object */
     if ((sa = (sa_t *)malloc(sizeof(sa_t))) == NULL)
         return SA_ERR_MEM;
-    sa->eType             = SA_TYPE_STREAM;
-    sa->fdSocket          = -1;
-    sa->bTimeout          = FALSE;
-    sa->tvTimeout.tv_sec  = 0;
-    sa->tvTimeout.tv_usec = 0;
-    sa->nReadLen          = 0;
-    sa->nReadSize         = 0;
-    sa->cpReadBuf         = NULL;
-    sa->nWriteLen         = 0;
-    sa->nWriteSize        = 0;
-    sa->cpWriteBuf        = NULL;
+
+    /* init object attributes */
+    sa->eType          = SA_TYPE_STREAM;
+    sa->fdSocket       = -1;
+    sa->nReadLen       = 0;
+    sa->nReadSize      = 0;
+    sa->cpReadBuf      = NULL;
+    sa->nWriteLen      = 0;
+    sa->nWriteSize     = 0;
+    sa->cpWriteBuf     = NULL;
+
+    /* init timeval object attributes */
+    for (i = 0; i < (sizeof(sa->tvTimeout)/sizeof(sa->tvTimeout[0])); i++) {
+        sa->tvTimeout[i].tv_sec  = 0;
+        sa->tvTimeout[i].tv_usec = 0;
+    }
+
+    /* init syscall object attributes */
+    SA_SC_ASSIGN(sa, connect,  connect,  NULL);
+    SA_SC_ASSIGN(sa, accept,   accept,   NULL);
+    SA_SC_ASSIGN(sa, select,   select,   NULL);
+    SA_SC_ASSIGN(sa, read,     read,     NULL);
+    SA_SC_ASSIGN(sa, write,    write,    NULL);
+    SA_SC_ASSIGN(sa, recvfrom, recvfrom, NULL);
+    SA_SC_ASSIGN(sa, sendto,   sendto,   NULL);
 
     /* pass object to caller */
     *sap = sa;
@@ -657,25 +944,33 @@
 }
 
 /* configure I/O timeout */
-sa_rc_t sa_timeout(sa_t *sa, long sec, long usec)
+sa_rc_t sa_timeout(sa_t *sa, sa_timeout_t id, long sec, long usec)
 {
+    int i;
+
     /* argument sanity check(s) */
     if (sa == NULL)
         return SA_ERR_ARG;
 
-    /* configure timeout */
-    if (sec == 0 && usec == 0)
-        sa->bTimeout = FALSE; /* deactivate timeout */
-    else
-        sa->bTimeout = TRUE;  /* activate timeout */
-    sa->tvTimeout.tv_sec  = sec;
-    sa->tvTimeout.tv_usec = usec;
+    if (id == SA_TIMEOUT_ALL) {
+        for (i = 0; i < (sizeof(sa->tvTimeout)/sizeof(sa->tvTimeout[0])); i++) {
+            sa->tvTimeout[i].tv_sec  = sec;
+            sa->tvTimeout[i].tv_usec = usec;
+        }
+    }
+    else {
+        sa->tvTimeout[id].tv_sec  = sec;
+        sa->tvTimeout[id].tv_usec = usec;
+    }
+
+    /* optionally set kernel timeouts */
+    sa_socket_settimeouts(sa);
 
     return SA_OK;
 }
 
 /* configure I/O buffers */
-sa_rc_t sa_buffers(sa_t *sa, size_t rsize, size_t wsize)
+sa_rc_t sa_buffer(sa_t *sa, sa_buffer_t id, size_t size)
 {
     char *cp;
 
@@ -683,46 +978,74 @@
     if (sa == NULL)
         return SA_ERR_ARG;
 
-    /* make sure buffered were already flushed sufficiently */
-    if (sa->nReadLen > rsize || sa->nWriteLen > wsize)
-        return SA_ERR_USE;
-
-    /* configure read/incoming buffer */
-    if (rsize > 0) {
-        if (sa->cpReadBuf == NULL)
-            cp = (char *)malloc(rsize);
-        else
-            cp = (char *)realloc(sa->cpReadBuf, rsize);
-        if (cp == NULL)
-            return SA_ERR_SYS;
-        sa->cpReadBuf = cp;
-        sa->nReadSize = rsize;
+    if (id == SA_BUFFER_READ) {
+        /* configure read/incoming buffer */
+        if (sa->nReadLen > size)
+            return SA_ERR_USE;
+        if (size > 0) {
+            if (sa->cpReadBuf == NULL)
+                cp = (char *)malloc(size);
+            else
+                cp = (char *)realloc(sa->cpReadBuf, size);
+            if (cp == NULL)
+                return SA_ERR_SYS;
+            sa->cpReadBuf = cp;
+            sa->nReadSize = size;
+        }
+        else {
+            if (sa->cpReadBuf != NULL)
+                free(sa->cpReadBuf);
+            sa->cpReadBuf = NULL;
+            sa->nReadSize = 0;
+        }
     }
-    else {
-        if (sa->cpReadBuf != NULL)
-            free(sa->cpReadBuf);
-        sa->cpReadBuf = NULL;
-        sa->nReadSize = 0;
+    else if (id == SA_BUFFER_WRITE) {
+        /* configure write/outgoing buffer */
+        if (sa->nWriteLen > size)
+            return SA_ERR_USE;
+        if (size > 0) {
+            if (sa->cpWriteBuf == NULL)
+                cp = (char *)malloc(size);
+            else
+                cp = (char *)realloc(sa->cpWriteBuf, size);
+            if (cp == NULL)
+                return SA_ERR_SYS;
+            sa->cpWriteBuf = cp;
+            sa->nWriteSize = size;
+        }
+        else {
+            if (sa->cpWriteBuf != NULL)
+                free(sa->cpWriteBuf);
+            sa->cpWriteBuf = NULL;
+            sa->nWriteSize = 0;
+        }
     }
+        return SA_ERR_ARG;
 
-    /* configure write/outgoing buffer */
-    if (wsize > 0) {
-        if (sa->cpWriteBuf == NULL)
-            cp = (char *)malloc(wsize);
-        else
-            cp = (char *)realloc(sa->cpWriteBuf, wsize);
-        if (cp == NULL)
-            return SA_ERR_SYS;
-        sa->cpWriteBuf = cp;
-        sa->nWriteSize = wsize;
-    }
-    else {
-        if (sa->cpWriteBuf != NULL)
-            free(sa->cpWriteBuf);
-        sa->cpWriteBuf = NULL;
-        sa->nWriteSize = 0;
-    }
+    return SA_OK;
+}
 
+/* override system call */
+sa_rc_t sa_syscall(sa_t *sa, sa_syscall_t id, void (*fptr)(), void *fctx)
+{
+    if (sa == NULL || fptr == NULL)
+        return SA_ERR_ARG;
+    if (id == SA_SYSCALL_CONNECT)
+        SA_SC_ASSIGN(sa, connect, fptr, fctx); 
+    else if (id == SA_SYSCALL_ACCEPT)
+        SA_SC_ASSIGN(sa, accept, fptr, fctx); 
+    else if (id == SA_SYSCALL_SELECT)
+        SA_SC_ASSIGN(sa, select, fptr, fctx); 
+    else if (id == SA_SYSCALL_READ)
+        SA_SC_ASSIGN(sa, read, fptr, fctx); 
+    else if (id == SA_SYSCALL_WRITE)
+        SA_SC_ASSIGN(sa, write, fptr, fctx); 
+    else if (id == SA_SYSCALL_RECVFROM)
+        SA_SC_ASSIGN(sa, recvfrom, fptr, fctx); 
+    else if (id == SA_SYSCALL_SENDTO)
+        SA_SC_ASSIGN(sa, sendto, fptr, fctx); 
+    else
+        return SA_ERR_ARG;
     return SA_OK;
 }
 
@@ -776,9 +1099,9 @@
             return rv;
 
     rv = SA_OK;
-    if (!sa->bTimeout) {
+    if (SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_CONNECT])) {
         /* standard/non-timeout-aware connect operation */
-        if (connect(sa->fdSocket, raddr->saBuf, raddr->slBuf) < 0)
+        if (SA_SC_CALL_3(sa, connect, sa->fdSocket, raddr->saBuf, raddr->slBuf) < 0)
             rv = SA_ERR_SYS;
     }
     else {
@@ -790,7 +1113,7 @@
         fcntl(sa->fdSocket, F_SETFL, flags|O_NONBLOCK);
 
         /* perform the connect operation */
-        if ((n = connect(sa->fdSocket, raddr->saBuf, raddr->slBuf)) < 0) {
+        if ((n = SA_SC_CALL_3(sa, connect, sa->fdSocket, raddr->saBuf, raddr->slBuf)) < 0) {
             if (errno != EINPROGRESS) {
                 error = errno;
                 goto done;
@@ -807,7 +1130,8 @@
         FD_SET(sa->fdSocket, &rset);
         FD_SET(sa->fdSocket, &wset);
         do {
-            n = select(sa->fdSocket+1, &rset, &wset, NULL, &sa->tvTimeout);
+            n = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &rset, &wset, NULL,
+                             &sa->tvTimeout[SA_TIMEOUT_CONNECT]);
         } while (n == -1 && errno == EINTR);
 
         /* decide on return semantic */
@@ -816,7 +1140,8 @@
             goto done;
         }
         else if (n == 0) {
-            error = ETIMEDOUT;
+            close(sa->fdSocket); /* stop TCP three-way handshake */
+            rv = SA_ERR_TMT;
             goto done;
         }
 
@@ -832,6 +1157,7 @@
 
         /* optionally set errno */
         if (error != 0) {
+            close(sa->fdSocket); /* just in case */
             errno = error;
             rv = SA_ERR_SYS;
         }
@@ -875,6 +1201,7 @@
     } sa_buf;
     socklen_t sa_len;
     int s;
+    int i;
 
     /* argument sanity check(s) */
     if (sa == NULL || caddr == NULL || csa == NULL)
@@ -889,21 +1216,22 @@
         return SA_ERR_USE;
 
     /* if timeout is enabled, perform a smart-blocking wait */
-    if (sa->bTimeout) {
+    if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_ACCEPT])) {
         FD_ZERO(&fds);
         FD_SET(sa->fdSocket, &fds);
         do {
-            n = select(sa->fdSocket+1, &fds, NULL, NULL, &sa->tvTimeout);
+            n = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &fds, NULL, NULL,
+                             &sa->tvTimeout[SA_TIMEOUT_ACCEPT]);
         } while (n == -1 && errno == EINTR);
         if (n == 0) 
-            errno = ETIMEDOUT;
+            return SA_ERR_TMT;
         if (n <= 0)
             return SA_ERR_SYS;
     }
 
     /* perform accept operation on underlying socket */
     sa_len = sizeof(sa_buf);
-    if ((s = accept(sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_len)) == -1)
+    if ((s = SA_SC_CALL_3(sa, accept, sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_len)) == -1)
         return SA_ERR_SYS;
 
     /* create result address object */
@@ -919,8 +1247,25 @@
         sa_addr_destroy(*caddr);
         return rv;
     }
+
+    /* fill-in child socket */
     (*csa)->fdSocket = s;
 
+    /* copy-over original system calls */
+    SA_SC_COPY((*csa), sa, connect);
+    SA_SC_COPY((*csa), sa, accept);
+    SA_SC_COPY((*csa), sa, select);
+    SA_SC_COPY((*csa), sa, read);
+    SA_SC_COPY((*csa), sa, write);
+    SA_SC_COPY((*csa), sa, recvfrom);
+    SA_SC_COPY((*csa), sa, sendto);
+
+    /* copy-over original timeout values */
+    for (i = 0; i < (sizeof(sa->tvTimeout)/sizeof(sa->tvTimeout[0])); i++) {
+        (*csa)->tvTimeout[i].tv_sec  = sa->tvTimeout[i].tv_sec;
+        (*csa)->tvTimeout[i].tv_usec = sa->tvTimeout[i].tv_usec;
+    }
+
     return SA_OK;
 }
 
@@ -1021,27 +1366,37 @@
 static int sa_read_raw(sa_t *sa, char *cpBuf, int nBufLen)
 {
     int rv;
+#if !(defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO))
     fd_set fds;
+#endif
 
     /* if timeout is enabled, perform explicit/smart blocking instead 
        of implicitly/hard blocking in the read(2) system call */
-    if (sa->bTimeout) {
+#if !(defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO))
+    if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_READ])) {
         FD_ZERO(&fds);
         FD_SET(sa->fdSocket, &fds);
         do {
-            rv = select(sa->fdSocket+1, &fds, NULL, NULL, &sa->tvTimeout);
+            rv = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &fds, NULL, NULL, 
+                              &sa->tvTimeout[SA_TIMEOUT_READ]);
         } while (rv == -1 && errno == EINTR);
         if (rv == 0) {
             errno = ETIMEDOUT;
             return -1;
         }
     }
+#endif
 
     /* perform read operation on underlying socket */
     do {
-        rv = read(sa->fdSocket, cpBuf, nBufLen);
+        rv = SA_SC_CALL_3(sa, read, sa->fdSocket, cpBuf, nBufLen);
     } while (rv == -1 && errno == EINTR);
 
+#if defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO)
+    if (rv == -1 && errno == EWOULDBLOCK)
+        errno = ETIMEDOUT;
+#endif
+
     return rv;
 }
 
@@ -1075,6 +1430,8 @@
         res = sa_read_raw(sa, cpBuf, nBufReq);
         if (res == 0)
             rv = SA_ERR_EOF;
+        else if (res < 0 && errno == ETIMEDOUT)
+            rv = SA_ERR_TMT;
         else if (res < 0)
             rv = SA_ERR_SYS;
     }
@@ -1105,13 +1462,18 @@
                         res += n;
                     else if (n == 0)
                         rv = (res == 0 ? SA_ERR_EOF : SA_OK);
+                    else if (n < 0 && errno == ETIMEDOUT)
+                        rv = (res == 0 ? SA_ERR_TMT : SA_OK);
                     else if (n < 0)
                         rv = (res == 0 ? SA_ERR_SYS : SA_OK);
                 }
                 else {
                     /* fill buffer with new data */
                     n = sa_read_raw(sa, sa->cpReadBuf, sa->nReadSize);
-                    if (n < 0)
+                    if (n < 0 && errno == ETIMEDOUT)
+                        /* timeout on this read, but perhaps ok as a whole */
+                        rv = (res == 0 ? SA_ERR_TMT : SA_OK);
+                    else if (n < 0)
                         /* error on this read, but perhaps ok as a whole */
                         rv = (res == 0 ? SA_ERR_SYS : SA_OK);
                     if (n == 0)
@@ -1179,27 +1541,37 @@
 static int sa_write_raw(sa_t *sa, const char *cpBuf, int nBufLen)
 {
     int rv;
+#if !(defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO))
     fd_set fds;
+#endif
 
     /* if timeout is enabled, perform explicit/smart blocking instead 
        of implicitly/hard blocking in the write(2) system call */
-    if (sa->bTimeout) {
+#if !(defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO))
+    if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_WRITE])) {
         FD_ZERO(&fds);
         FD_SET(sa->fdSocket, &fds);
         do {
-            rv = select(sa->fdSocket+1, NULL, &fds, NULL, &sa->tvTimeout);
+            rv = SA_SC_CALL_5(sa, select, sa->fdSocket+1, NULL, &fds, NULL, 
+                              &sa->tvTimeout[SA_TIMEOUT_WRITE]);
         } while (rv == -1 && errno == EINTR);
         if (rv == 0) {
             errno = ETIMEDOUT;
             return -1;
         }
     }
+#endif
 
     /* perform write operation on underlying socket */
     do {
-        rv = write(sa->fdSocket, cpBuf, nBufLen);
+        rv = SA_SC_CALL_3(sa, write, sa->fdSocket, cpBuf, nBufLen);
     } while (rv == -1 && errno == EINTR);
 
+#if defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO)
+    if (rv == -1 && errno == EWOULDBLOCK)
+        errno = ETIMEDOUT;
+#endif
+
     return rv;
 }
 
@@ -1226,7 +1598,9 @@
     if (sa->nWriteSize == 0) {
         /* user-space unbuffered I/O */
         res = sa_write_raw(sa, cpBuf, nBufReq);
-        if (res < 0)
+        if (res < 0 && errno == ETIMEDOUT)
+            rv = SA_ERR_TMT;
+        else if (res < 0)
             rv = SA_ERR_SYS;
     }
     else {
@@ -1240,7 +1614,9 @@
             /* buffer too small at all, so write immediately */
             while (nBufReq > 0) {
                 n = sa_write_raw(sa, cpBuf, nBufReq);
-                if (n < 0)
+                if (n < 0 && errno == ETIMEDOUT)
+                    rv = (res == 0 ? SA_ERR_TMT : SA_OK);
+                else if (n < 0)
                     rv = (res == 0 ? SA_ERR_SYS : SA_OK);
                 if (n <= 0)
                     break;
@@ -1264,13 +1640,29 @@
     return rv;
 }
 
+/* output callback function context for sa_writef() */
+typedef struct {
+    sa_t *sa;
+    sa_rc_t rv;
+} sa_writef_cb_t;
+
+/* output callback function for sa_writef() */
+static int sa_writef_cb(void *_ctx, const char *buffer, size_t bufsize)
+{
+    size_t n;
+    sa_writef_cb_t *ctx = (sa_writef_cb_t *)_ctx;
+
+    if ((ctx->rv = sa_write(ctx->sa, buffer, bufsize, &n)) != SA_OK)
+        n = -1;
+    return n;
+}
+
 /* write formatted string to socket (convinience function) */
 sa_rc_t sa_writef(sa_t *sa, const char *cpFmt, ...)
 {
     va_list ap;
     size_t n;
-    char caBuf[1024];
-    sa_rc_t rv;
+    sa_writef_cb_t ctx;
 
     /* argument sanity check(s) */
     if (sa == NULL || cpFmt == NULL)
@@ -1286,13 +1678,12 @@
 
     /* format string into temporary buffer */
     va_start(ap, cpFmt);
-    n = sa_mvsnprintf(caBuf, sizeof(caBuf), cpFmt, ap);
+    ctx.sa = sa;
+    ctx.rv = SA_OK;
+    n = sa_mvxprintf(sa_writef_cb, &ctx, cpFmt, ap);
     va_end(ap);
 
-    /* write result buffer to socket */
-    rv = sa_write(sa, caBuf, n, &n);
-
-    return rv;
+    return ctx.rv;
 }
 
 /* flush write/outgoing I/O buffer */
@@ -1318,7 +1709,9 @@
     if (sa->nWriteSize > 0) {
         while (sa->nWriteLen > 0) {
             n = sa_write_raw(sa, sa->cpWriteBuf, sa->nWriteLen);
-            if (n < 0)
+            if (n < 0 && errno == ETIMEDOUT)
+                rv = SA_ERR_TMT;
+            else if (n < 0)
                 rv = SA_ERR_SYS;
             if (n <= 0)
                 break;
@@ -1393,11 +1786,12 @@
 
     /* if timeout is enabled, perform explicit/smart blocking instead 
        of implicitly/hard blocking in the recvfrom(2) system call */
-    if (sa->bTimeout) {
+    if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_READ])) {
         FD_ZERO(&fds);
         FD_SET(sa->fdSocket, &fds);
         do {
-            n = select(sa->fdSocket+1, &fds, NULL, NULL, &sa->tvTimeout);
+            n = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &fds, NULL, NULL, 
+                             &sa->tvTimeout[SA_TIMEOUT_READ]);
         } while (n == -1 && errno == EINTR);
         if (n == 0) 
             errno = ETIMEDOUT;
@@ -1407,8 +1801,8 @@
 
     /* perform receive operation on underlying socket */
     sa_len = sizeof(sa_buf);
-    if ((n = recvfrom(sa->fdSocket, buf, buflen, 0, 
-                      (struct sockaddr *)&sa_buf, &sa_len)) == -1)
+    if ((n = SA_SC_CALL_6(sa, recvfrom, sa->fdSocket, buf, buflen, 0, 
+                          (struct sockaddr *)&sa_buf, &sa_len)) == -1)
         return SA_ERR_SYS;
 
     /* create result address object */
@@ -1448,11 +1842,12 @@
 
     /* if timeout is enabled, perform explicit/smart blocking instead 
        of implicitly/hard blocking in the sendto(2) system call */
-    if (sa->bTimeout) {
+    if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_WRITE])) {
         FD_ZERO(&fds);
         FD_SET(sa->fdSocket, &fds);
         do {
-            n = select(sa->fdSocket+1, NULL, &fds, NULL, &sa->tvTimeout);
+            n = SA_SC_CALL_5(sa, select, sa->fdSocket+1, NULL, &fds, NULL, 
+                             &sa->tvTimeout[SA_TIMEOUT_WRITE]);
         } while (n == -1 && errno == EINTR);
         if (n == 0) 
             errno = ETIMEDOUT;
@@ -1461,7 +1856,7 @@
     }
 
     /* perform send operation on underlying socket */
-    if ((n = sendto(sa->fdSocket, buf, buflen, 0, raddr->saBuf, raddr->slBuf)) == -1)
+    if ((n = SA_SC_CALL_6(sa, sendto, sa->fdSocket, buf, buflen, 0, raddr->saBuf, raddr->slBuf)) == -1)
         return SA_ERR_SYS;
 
     /* pass actual number of sent bytes to caller */
@@ -1471,3 +1866,22 @@
     return SA_OK;
 }
 
+/* return error string */
+char *sa_error(sa_rc_t rv)
+{
+    char *sz;
+
+    /* translate result value into corresponding string */
+    if      (rv == SA_OK)      sz = "Everything Ok";
+    else if (rv == SA_ERR_ARG) sz = "Invalid Argument";
+    else if (rv == SA_ERR_USE) sz = "Invalid Use Or Context";
+    else if (rv == SA_ERR_MEM) sz = "Not Enough Memory";
+    else if (rv == SA_ERR_MTC) sz = "Matching Failed";
+    else if (rv == SA_ERR_EOF) sz = "End Of Communication";
+    else if (rv == SA_ERR_TMT) sz = "Communication Timeout";
+    else if (rv == SA_ERR_SYS) sz = "Operating System Error";
+    else if (rv == SA_ERR_INT) sz = "Internal Error";
+    else                       sz = "Invalid Result Code";
+    return sz;
+}
+

CVSTrac 2.0.1