OSSP CVS Repository

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

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

--- l2_ut_sa.c   2001/10/02 14:11:51     1.13
+++ l2_ut_sa.c   2001/10/06 14:33:09     1.14
@@ -31,318 +31,432 @@
 #include "l2_config.h"
 
 /* include system API headers */
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <time.h>
-#include <sys/time.h>
-#include <netdb.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
+#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 <sys/types.h>   /* for general prerequisites */
+#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 */
+#include <netdb.h>       /* for "struct prototent" */
+#include <sys/time.h>    /* for "struct timeval" */
+#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 own API header */
-#define SA_PREFIX l2_ut_
 #include "l2_ut_sa.h"
 
 /* socket address abstraction object */
 struct sa_addr_st {
-    struct sockaddr *saBuf;
-    socklen_t        slBuf;
-    int              nFamily;
-    int              nProto;
+    int              nFamily;     /* the socket family (AF_XXX) */
+    struct sockaddr *saBuf;       /* the "struct sockaddr_xx" actually */
+    socklen_t        slBuf;       /* the length of "struct sockaddr_xx" */
 };
 
 /* socket abstraction object */
 struct sa_st {
-    int              sSocket;
-    int              bTimeout;
-    struct timeval   tvTimeout;
-    int              nReadLen;
-    int              nReadSize;
-    char            *cpReadBuf;
-    int              nWriteLen;
-    int              nWriteSize;
-    char            *cpWriteBuf;
+    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 */
 };
 
-/* make sure AF_LOCAL define exists */
+/* boolean values */
+#ifndef FALSE
+#define FALSE (0)
+#endif
+#ifndef TRUE
+#define TRUE  (!FALSE)
+#endif
+
+/* backward compatibility for AF_LOCAL */
 #if !defined(AF_LOCAL) && defined(AF_UNIX)
 #define AF_LOCAL AF_UNIX
 #endif
 
-/* make sure inet_pton() exists */
-#if defined(HAVE_INET_PTON)
-#define sa_inet_pton inet_pton
-#elif defined(HAVE_INET_ATON)
-static int inet_pton(int family, const char *strptr, void *addrptr)
+/* convert Internet address from presentation to network format */
+static int sa_inet_pton(int family, const char *strptr, void *addrptr)
 {
+#ifdef HAVE_INET_PTON
+    return inet_pton(family, strptr, addrptr);
+#else
     struct in_addr in_val;
 
     if (family == AF_INET) {
-        if (inet_aton(strptr, &in_val)) {
-            memcpy(addrptr, &in_val, sizeof(struct in_addr));
-            return 1;
-        }
-        return 0;
+        /* at least for IPv4 we can rely on the old inet_aton(3)
+           and for IPv6 inet_pton(3) would exist anyway */
+        if (inet_aton(strptr, &in_val) == 0)
+            return 0;
+        memcpy(addrptr, &in_val, sizeof(struct in_addr));
+        return 1;
     }
     errno = EAFNOSUPPORT;
-    return -1;
-}
-#else
-#error "neither inet_pton nor inet_aton available"
+    return 0;
 #endif
+}
 
-/* make sure inet_ntop() exists */
-#if defined(HAVE_INET_NTOP)
-#define sa_inet_ntop inet_ntop
-#elif defined(HAVE_INET_NTOA)
-static char *inet_ntop(int family, const void *src, char *dst, size_t size)
+/* convert Internet address from network to presentation format */
+static const char *sa_inet_ntop(int family, const void *src, char *dst, size_t size)
 {
+#ifdef HAVE_INET_NTOP
+    return inet_ntop(family, src, dst, size);
+#else
     struct in_addr in_val;
     char *cp;
     int n;
 
     if (family == AF_INET) {
-        if ((cp = inet_ntoa(src)) != NULL) {
-            n = strlen(cp);
-            if (n > size-1)
-                n = size-1;
-            memcpy(dst, cp, n);
-            dst[n] = '\0';
-            return 1;
-        }
-        return 0;
+        /* at least for IPv4 we can rely on the old inet_ntoa(3)
+           and for IPv6 inet_ntop(3) would exist anyway */
+        if ((cp = inet_ntoa(src)) == NULL) 
+            return NULL;
+        n = strlen(cp);
+        if (n > size-1)
+            n = size-1;
+        memcpy(dst, cp, n);
+        dst[n] = '\0';
+        return dst;
     }
     errno = EAFNOSUPPORT;
-    return -1;
-}
-#else
-#error "neither inet_ntop nor inet_ntoa available"
+    return NULL;
 #endif
+}
 
-/* make sure vsnprintf() exists */
-#if defined(HAVE_VSNPRINTF)
-#define sa_vsnprintf vsnprintf
-#elif defined(HAVE_VSPRINTF)
-static int sa_vsnprintf(char *str, size_t size, const char *fmt, va_list ap)
+/* 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 rv;
-    
-    rv = vsprintf(str, fmt, ap);
-    if (rv > size) {
-        fprintf(stderr, "ERROR: vsprintf(3) buffer overflow!\n");
-        abort();
-    }
-}
+    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;
+
+    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);
+            else if (c == 's') {
+                /* implement "%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" */
+                d = (int)va_arg(ap, int);
+#ifdef HAVE_SNPRINTF
+                snprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */
 #else
-#error "neither vsnprintf nor vsprintf available"
+                sprintf(ibuf, "%d", d);                /* implicitly secure */
 #endif
+                n = strlen(ibuf);
+                memcpy(bufptr, ibuf, n);
+                bufptr += n;
+            }
+            else {
+                *bufptr++ = '%';
+                if (bufptr < bufend)
+                    *bufptr++ = c;
+            }
+        }
+        else
+            *bufptr++ = c;
+    }
+    *bufptr = '\0';
+    return (bufptr - buffer);
+}
 
-/* make sure snprintf() exists */
-#if defined(HAVE_SNPRINTF)
-#define sa_snprintf snprintf
-#else
-static int sa_snprintf(char *str, size_t size, const char *fmt, ...)
+/* minimal snprintf(3) variant which supports %{c,s,d} only */
+static int sa_msnprintf(char *buffer, size_t bufsize, const char *format, ...)
 {
+    int chars;
     va_list ap;
-    int rv;
 
-    va_start(ap, fmt);
-    rv = sa_vsnprintf(str, size, fmt, 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);
     va_end(ap);
-    return rv;
+
+    return chars;
 }
-#endif
 
-sa_rc_t sa_u2a(sa_addr_t **saa, const char *uri, ...)
+/* create address object */
+sa_rc_t sa_addr_create(sa_addr_t **saap)
+{
+    sa_addr_t *saa;
+
+    /* argument sanity check(s) */
+    if (saap == NULL)
+        return SA_ERR_ARG;
+
+    /* allocate and initialize new address object */
+    if ((saa = (sa_addr_t *)malloc(sizeof(sa_addr_t))) == NULL)
+        return SA_ERR_MEM;
+    saa->nFamily = 0;
+    saa->saBuf   = NULL;
+    saa->slBuf   = 0;
+
+    /* pass object to caller */
+    *saap = saa;
+
+    return SA_OK;
+}
+
+/* destroy address object */
+sa_rc_t sa_addr_destroy(sa_addr_t *saa)
+{
+    /* argument sanity check(s) */
+    if (saa == NULL)
+        return SA_ERR_ARG;
+
+    /* free address objects and sub-object(s) */
+    if (saa->saBuf != NULL)
+        free(saa->saBuf);
+    free(saa);
+
+    return SA_OK;
+}
+
+/* import URI into address object */
+sa_rc_t sa_addr_u2a(sa_addr_t *saa, const char *uri, ...)
 {
     va_list ap;
-    int nPort;
+    int sf;
     socklen_t sl;
     struct sockaddr *sa;
+    struct sockaddr_un un;
     struct sockaddr_in sa4;
 #ifdef AF_INET6
     struct sockaddr_in6 sa6;
 #endif
     struct hostent *he;
     struct servent *se;
-    struct protoent *pe;
     int bNumeric;
-    int i;
-    char *cpProto;
-    int   nProto;
     char *cpHost;
     char *cpPort;
+    char *cpProto;
+    int nPort;
+    const char *cpPath;
     char uribuf[1024];
     char *cp;
-    int sf;
+    int i;
+    int n;
 
-    /* argument sanity check */
+    /* argument sanity check(s) */
     if (saa == NULL || uri == NULL)
         return SA_ERR_ARG;
 
     /* on-the-fly create or just take over URI */
     va_start(ap, uri);
-    sa_vsnprintf(uribuf, sizeof(uribuf), uri, ap);
+    sa_mvsnprintf(uribuf, sizeof(uribuf), uri, ap);
     va_end(ap);
 
-    /* parse URI into protocol, host and port parts */
-    uri = uribuf;
-    cpProto = "tcp";
-    if ((cp = strstr(uri, "://")) != NULL) {
-        cpProto = (char *)uri;
-        *cp = '\0';
-        uri = cp+3;
-    }
-    cpHost = (char *)uri;
-    if ((cp = strchr(uri, ':')) == NULL)
-        return SA_ERR_ARG;
-    *cp++ = '\0';
-    cpPort = cp;
-
-    /* resolve protocol */
-    if ((pe = getprotobyname(cpProto)) == NULL)
-        return SA_ERR_SYS;
-    nProto = pe->p_proto;
+    /* initialize result variables */
+    sa = NULL;
+    sl = 0;
+    sf = 0;
 
-    /* resolve port */
-    bNumeric = 1;
-    for (i = 0; cpPort[i] != '\0'; i++) {
-        if (!isdigit((int)cpPort[i])) {
-            bNumeric = 0;
-            break;
+    /* parse URI and resolve contents. 
+       The following syntax is recognized:
+       - unix:<path>
+       - inet://<host>:<port>[#(tcp|udp)] */
+    uri = uribuf;
+    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+1) > sizeof(un.sun_path))
+            return SA_ERR_MEM;
+        memcpy(un.sun_path, cpPath, n+1);
+        un.sun_family = AF_LOCAL;
+
+        /* provide results */
+        sa = (struct sockaddr *)&un;
+        sl = sizeof(un);
+        sf = AF_LOCAL;
+    }
+    else if (strncmp(uri, "inet://", 7) == 0) {
+        /* parse URI */
+        cpHost = (char *)(uri+7);
+        if ((cp = strchr(cpHost, ':')) == NULL)
+            return SA_ERR_ARG;
+        *cp++ = '\0';
+        cpPort = cp;
+
+        /* resolve port */
+        nPort = 0;
+        bNumeric = 1;
+        for (i = 0; cpPort[i] != '\0'; i++) {
+            if (!isdigit((int)cpPort[i])) {
+                bNumeric = 0;
+                break;
+            }
+        }
+        if (bNumeric)
+            nPort = atoi(cpPort);
+        else {
+            cpProto = "tcp";
+            if ((cp = strchr(cpPort, '#')) != NULL) {
+                *cp++ = '\0';
+                cpProto = cp;
+            }
+            if ((se = getservbyname(cpPort, cpProto)) == NULL)
+                return SA_ERR_SYS;
+            nPort = ntohs(se->s_port);
         }
-    }
-    if (bNumeric)
-        nPort = atoi(cpPort);
-    else {
-        if ((se = getservbyname(cpPort, cpProto)) == NULL)
-            return SA_ERR_SYS;
-        nPort = ntohs(se->s_port);
-    }
 
-    /* mandatory(!) socket address structure initialization */
-    memset(&sa4, 0, sizeof(sa4));
+        /* mandatory(!) socket address structure initialization */
+        memset(&sa4, 0, sizeof(sa4));
 #ifdef AF_INET6
-    memset(&sa6, 0, sizeof(sa6));
+        memset(&sa6, 0, sizeof(sa6));
 #endif
 
-    /* resolve host by trying to parse it as either directly a IPv4 or
-       IPv6 address or by resolving it to either a IPv4 or IPv6 address */
-    sa = NULL;
-    sl = 0;
-    sf = 0;
-    if (inet_pton(AF_INET, cpHost, &sa4.sin_addr.s_addr) == 1) {
-        sa4.sin_family = AF_INET;
-        sa4.sin_port = htons(nPort);
-        sa = (struct sockaddr *)&sa4;
-        sl = sizeof(sa4);
-        sf = AF_INET;
-    }
-#ifdef AF_INET6
-    else if (inet_pton(AF_INET6, cpHost, &sa6.sin6_addr.s6_addr) == 1) {
-        sa6.sin6_family = AF_INET6;
-        sa6.sin6_port = htons(nPort);
-        sa = (struct sockaddr *)&sa6;
-        sl = sizeof(sa6);
-        sf = AF_INET6;
-    }
-#endif
-    else if ((he = gethostbyname(cpHost)) != NULL) {
-        if (he->h_addrtype == AF_INET) {
+        /* resolve host by trying to parse it as either directly a IPv4 or
+           IPv6 address or by resolving it to either a IPv4 or IPv6 address */
+        if (sa_inet_pton(AF_INET, cpHost, &sa4.sin_addr.s_addr) == 1) {
             sa4.sin_family = AF_INET;
             sa4.sin_port = htons(nPort);
-            memcpy(&sa4.sin_addr.s_addr, he->h_addr_list[0], sizeof(sa4.sin_addr.s_addr));
             sa = (struct sockaddr *)&sa4;
             sl = sizeof(sa4);
             sf = AF_INET;
         }
 #ifdef AF_INET6
-        else if (he->h_addrtype == AF_INET6) {
+        else if (sa_inet_pton(AF_INET6, cpHost, &sa6.sin6_addr.s6_addr) == 1) {
             sa6.sin6_family = AF_INET6;
             sa6.sin6_port = htons(nPort);
-            memcpy(&sa6.sin6_addr.s6_addr, he->h_addr_list[0], sizeof(sa6.sin6_addr.s6_addr));
             sa = (struct sockaddr *)&sa6;
             sl = sizeof(sa6);
             sf = AF_INET6;
         }
 #endif
+        else if ((he = gethostbyname(cpHost)) != NULL) {
+            if (he->h_addrtype == AF_INET) {
+                sa4.sin_family = AF_INET;
+                sa4.sin_port = htons(nPort);
+                memcpy(&sa4.sin_addr.s_addr, he->h_addr_list[0],
+                       sizeof(sa4.sin_addr.s_addr));
+                sa = (struct sockaddr *)&sa4;
+                sl = sizeof(sa4);
+                sf = AF_INET;
+            }
+#ifdef AF_INET6
+            else if (he->h_addrtype == AF_INET6) {
+                sa6.sin6_family = AF_INET6;
+                sa6.sin6_port = htons(nPort);
+                memcpy(&sa6.sin6_addr.s6_addr, he->h_addr_list[0], 
+                       sizeof(sa6.sin6_addr.s6_addr));
+                sa = (struct sockaddr *)&sa6;
+                sl = sizeof(sa6);
+                sf = AF_INET6;
+            }
+#endif
+        }
     }
-    if (sa == NULL)
-        return SA_ERR_ARG;
-        
-    /* create result address structure */
-    if ((*saa = (sa_addr_t *)malloc(sizeof(sa_addr_t))) == NULL)
-        return SA_ERR_MEM;
-    if (((*saa)->saBuf = (struct sockaddr *)malloc(sl)) == NULL) {
-        free(*saa);
+
+    /* make sure result variables are set */
+    if (sa == NULL || sl == 0 || sf == 0)
+        return SA_ERR_INT;
+
+    /* fill-in result address structure */
+    if (saa->saBuf != NULL)
+        free(saa->saBuf);
+    if ((saa->saBuf = (struct sockaddr *)malloc(sl)) == NULL)
         return SA_ERR_MEM;
-    }
-    memcpy((*saa)->saBuf, sa, sl);
-    (*saa)->slBuf = sl;
-    (*saa)->nFamily = sf;
-    (*saa)->nProto = nProto;
+    memcpy(saa->saBuf, sa, sl);
+    saa->slBuf = sl;
+    saa->nFamily = sf;
+
     return SA_OK;
 }
 
-sa_rc_t sa_s2a(sa_addr_t **saa, const struct sockaddr *sabuf, socklen_t salen)
+/* import "struct sockaddr" into address object */
+sa_rc_t sa_addr_s2a(sa_addr_t *saa, const struct sockaddr *sabuf, socklen_t salen)
 {
+    struct sockaddr_un *un;
     struct sockaddr_in *sa4;
 #ifdef AF_INET6
     struct sockaddr_in6 *sa6;
 #endif
-    struct protoent *pe;
     int sf;
 
+    /* argument sanity check(s) */
     if (saa == NULL || sabuf == NULL || salen == 0)
         return SA_ERR_ARG;
 
     /* create result address structure */
-    if ((*saa = (sa_addr_t *)malloc(sizeof(sa_addr_t))) == NULL)
-        return SA_ERR_MEM;
-    if (((*saa)->saBuf = (struct sockaddr *)malloc(salen)) == NULL) {
-        free(*saa);
+    if (saa->saBuf != NULL)
+        free(saa->saBuf);
+    if ((saa->saBuf = (struct sockaddr *)malloc(salen)) == NULL)
         return SA_ERR_MEM;
-    }
-    memcpy((*saa)->saBuf, sabuf, salen);
-    (*saa)->slBuf = salen;
+    memcpy(saa->saBuf, sabuf, salen);
+    saa->slBuf = salen;
 
-    /* fill in family */
+    /* resolve family */
     sf = 0;
-    if (sizeof(struct sockaddr_in) == salen) {
+    if (sizeof(struct sockaddr_un) == salen) {
+        un = (struct sockaddr_un *)((void *)sabuf);
+        if (un->sun_family == AF_LOCAL)
+            sf = AF_LOCAL;
+    }
+    if (sf == 0 && sizeof(struct sockaddr_in) == salen) {
         sa4 = (struct sockaddr_in *)((void *)sabuf);
         if (sa4->sin_family == AF_INET)
             sf = AF_INET;
     }
 #ifdef AF_INET6
-    else if (sizeof(struct sockaddr_in6) == salen) {
+    if (sf == 0 && sizeof(struct sockaddr_in6) == salen) {
         sa6 = (struct sockaddr_in6 *)((void *)sabuf);
         if (sa6->sin6_family == AF_INET6)
             sf = AF_INET6;
     }
 #endif
-    (*saa)->nFamily = sf;
+    if (sf == 0)
+        return SA_ERR_ARG;
+    saa->nFamily = sf;
 
-    /* fill in protocol */
-    if ((pe = getprotobyname("tcp")) != NULL)
-        (*saa)->nProto = pe->p_proto;
-    else
-        (*saa)->nProto = 0;
     return SA_OK;
 }
 
-sa_rc_t sa_a2u(const sa_addr_t *saa, char **uri)
+/* export address object into URI */
+sa_rc_t sa_addr_a2u(sa_addr_t *saa, char **uri)
 {
     char uribuf[1024];
-    struct protoent *pe;
+    struct sockaddr_un *un;
     struct sockaddr_in *sa4;
 #ifdef AF_INET6
     struct sockaddr_in6 *sa6;
@@ -350,159 +464,297 @@
     char caHost[512];
     int nPort;
 
+    /* argument sanity check(s) */
     if (saa == NULL || uri == NULL)
         return SA_ERR_ARG;
-    if ((pe = getprotobynumber(saa->nProto)) == NULL)
-        return SA_ERR_SYS;
-    if (saa->nFamily == AF_INET) {
-        sa4 = (struct sockaddr_in *)((void *)saa->saBuf);
-        inet_ntop(AF_INET, &sa4->sin_addr.s_addr, caHost, sizeof(caHost));
-        nPort = ntohs(sa4->sin_port);
+
+    /* export object contents */
+    if (saa->nFamily == AF_LOCAL) {
+        un = (struct sockaddr_un *)((void *)saa->saBuf);
+        sa_msnprintf(uribuf, sizeof(uribuf), "unix:%s", un->sun_path);
     }
 #ifdef AF_INET6
-    else if (saa->nFamily == AF_INET6) {
-        sa6 = (struct sockaddr_in6 *)((void *)saa->saBuf);
-        inet_ntop(AF_INET6, &sa6->sin6_addr.s6_addr, caHost, sizeof(caHost));
-        nPort = ntohs(sa6->sin6_port);
-    }
+    else if (saa->nFamily == AF_INET || saa->nFamily == AF_INET6) {
+#else
+    else if (saa->nFamily == AF_INET) {
 #endif
+        if (saa->nFamily == AF_INET) {
+            sa4 = (struct sockaddr_in *)((void *)saa->saBuf);
+            sa_inet_ntop(AF_INET, &sa4->sin_addr.s_addr, caHost, sizeof(caHost));
+            nPort = ntohs(sa4->sin_port);
+        }
+#ifdef AF_INET6
+        else {
+            sa6 = (struct sockaddr_in6 *)((void *)saa->saBuf);
+            sa_inet_ntop(AF_INET6, &sa6->sin6_addr.s6_addr, caHost, sizeof(caHost));
+            nPort = ntohs(sa6->sin6_port);
+        }
+#endif
+        sa_msnprintf(uribuf, sizeof(uribuf), "inet://%s:%d", caHost, nPort);
+    }
     else
-        return SA_ERR_ARG;
-    sa_snprintf(uribuf, sizeof(uribuf), "%s://%s:%d", pe->p_name, caHost, nPort);
+        return SA_ERR_INT;
+
+    /* pass result to caller */
     *uri = strdup(uribuf);
+
     return SA_OK;
 }
 
-sa_rc_t sa_a2s(const sa_addr_t *saa, struct sockaddr **sabuf, socklen_t *salen)
+/* export address object into "struct sockaddr" */
+sa_rc_t sa_addr_a2s(sa_addr_t *saa, struct sockaddr **sabuf, socklen_t *salen)
 {
+    /* argument sanity check(s) */
     if (saa == NULL || sabuf == NULL || salen == 0)
         return SA_ERR_ARG;
 
+    /* export underlying address structure */
     if ((*sabuf = (struct sockaddr *)malloc(saa->slBuf)) == NULL)
         return SA_ERR_MEM;
     memmove(*sabuf, saa->saBuf, saa->slBuf);
     *salen = saa->slBuf;
+
     return SA_OK;
 }
 
-static sa_rc_t sa_socket_init(sa_t *sa, int family, int proto)
+/* internal lazy/delayed initialization of underlying socket */
+static sa_rc_t sa_socket_init(sa_t *sa, int nFamily)
 {
-    int type;
+    int nType;
+    int nProto;
+    struct protoent *pe;
+    char *cpProto;
 
+    /* argument sanity check(s) */
     if (sa == NULL)
         return SA_ERR_ARG;
-    if (proto == IPPROTO_TCP)
-        type = SOCK_STREAM;
-    else if (proto == IPPROTO_UDP)
-        type = SOCK_DGRAM;
-    else
-        return SA_ERR_ARG;
-    if (sa->sSocket != -1)
+
+    /* only perform operation if socket still does not exist */
+    if (sa->fdSocket != -1)
         return SA_ERR_USE;
-    if ((sa->sSocket = socket(family, type, proto)) == -1)
+
+    /* determine socket type */
+    if (sa->eType == SA_TYPE_STREAM)
+        nType = SOCK_STREAM;
+    else if (sa->eType == SA_TYPE_DATAGRAM)
+        nType = SOCK_DGRAM;
+    else
+        return SA_ERR_INT;
+
+    /* determine socket protocol */
+    if (nFamily == AF_LOCAL) 
+        nProto = 0;
+#ifdef AF_INET6
+    else if (nFamily == AF_INET || nFamily == AF_INET6) {
+#else
+    else if (nFamily == AF_INET) {
+#endif
+        if (nType == SOCK_STREAM)
+            cpProto = "tcp";
+        else if (nType == SOCK_DGRAM)
+            cpProto = "udp";
+        else
+            return SA_ERR_INT;
+        if ((pe = getprotobyname(cpProto)) == NULL)
+            return SA_ERR_SYS;
+        nProto = pe->p_proto;
+    }
+    else
+        return SA_ERR_INT;
+
+    /* create the underlying socket */
+    if ((sa->fdSocket = socket(nFamily, nType, nProto)) == -1)
         return SA_ERR_SYS;
+
     return SA_OK;
 }
 
+/* internal destruction of underlying socket */
 static sa_rc_t sa_socket_kill(sa_t *sa)
 {
+    /* argument sanity check(s) */
     if (sa == NULL)
         return SA_ERR_ARG;
-    if (sa->sSocket != -1) {
-        close(sa->sSocket);
-        sa->sSocket = -1;
-    }
+
+    /* check context */
+    if (sa->fdSocket == -1)
+        return SA_ERR_USE;
+
+    /* close socket */
+    close(sa->fdSocket);
+    sa->fdSocket = -1;
+
     return SA_OK;
 }
 
+/* create abstract socket object */
 sa_rc_t sa_create(sa_t **sap)
 {
     sa_t *sa;
 
+    /* argument sanity check(s) */
+    if (sap == NULL)
+        return SA_ERR_ARG;
+
+    /* allocate and initialize socket object */
     if ((sa = (sa_t *)malloc(sizeof(sa_t))) == NULL)
         return SA_ERR_MEM;
-    sa->sSocket    = -1;
-    sa->bTimeout   = 0;
-    sa->nReadLen   = 0;
-    sa->nReadSize  = 1024;
-    if ((sa->cpReadBuf = (char *)malloc(sa->nReadSize)) == NULL) {
-        free(sa);
-        return SA_ERR_MEM;
-    }
-    sa->nWriteLen  = 0;
-    sa->nWriteSize = 1024;
-    if ((sa->cpWriteBuf = (char *)malloc(sa->nWriteSize)) == NULL) {
-        free(sa->cpReadBuf);
-        free(sa);
-        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;
+
+    /* pass object to caller */
     *sap = sa;
+
     return SA_OK;
 }
 
+/* destroy abstract socket object */
 sa_rc_t sa_destroy(sa_t *sa)
 {
+    /* argument sanity check(s) */
     if (sa == NULL)
         return SA_ERR_ARG;
+
+    /* kill underlying socket */
     sa_socket_kill(sa);
+
+    /* free object and sub-objects */
     if (sa->cpReadBuf != NULL)
         free(sa->cpReadBuf);
     if (sa->cpWriteBuf != NULL)
         free(sa->cpWriteBuf);
     free(sa);
+
     return SA_OK;
 }
 
+/* switch communication type of socket */
+sa_rc_t sa_type(sa_t *sa, sa_type_t type)
+{
+    /* argument sanity check(s) */
+    if (sa == NULL)
+        return SA_ERR_ARG;
+    if (!(type == SA_TYPE_STREAM || type == SA_TYPE_DATAGRAM))
+        return SA_ERR_ARG;
+
+    /* kill underlying socket if type changes */
+    if (sa->eType != type)
+        sa_socket_kill(sa);
+
+    /* set new type */
+    sa->eType = type;
+
+    return SA_OK;
+}
+
+/* configure I/O timeout */
 sa_rc_t sa_timeout(sa_t *sa, long sec, long usec)
 {
+    /* argument sanity check(s) */
     if (sa == NULL)
         return SA_ERR_ARG;
-    if (sec == 0 && usec == 0) {
-        sa->bTimeout = 0;
-        sa->tvTimeout.tv_sec  = 0;
-        sa->tvTimeout.tv_usec = 0;
-    }
-    else {
-        sa->bTimeout = 1;
-        sa->tvTimeout.tv_sec  = sec;
-        sa->tvTimeout.tv_usec = usec;
-    }
+
+    /* 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;
+
     return SA_OK;
 }
 
+/* configure I/O buffers */
 sa_rc_t sa_buffers(sa_t *sa, size_t rsize, size_t wsize)
 {
     char *cp;
 
+    /* argument sanity check(s) */
     if (sa == NULL)
         return SA_ERR_ARG;
+
+    /* make sure buffered were already flushed sufficiently */
     if (sa->nReadLen > rsize || sa->nWriteLen > wsize)
-        return SA_ERR_ARG;
-    if ((cp = (char *)realloc(sa->cpReadBuf, rsize)) == NULL)
-        return SA_ERR_SYS;
-    sa->cpReadBuf = cp;
-    sa->nReadSize  = rsize;
-    if ((cp = (char *)realloc(sa->cpWriteBuf, wsize)) == NULL)
-        return SA_ERR_SYS;
-    sa->cpWriteBuf = cp;
-    sa->nWriteSize = 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;
+    }
+    else {
+        if (sa->cpReadBuf != NULL)
+            free(sa->cpReadBuf);
+        sa->cpReadBuf = NULL;
+        sa->nReadSize = 0;
+    }
+
+    /* 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;
 }
 
+/* bind socket to a local address */
 sa_rc_t sa_bind(sa_t *sa, sa_addr_t *laddr)
 {
     sa_rc_t rv;
+    struct sockaddr_un *un;
 
+    /* argument sanity check(s) */
     if (sa == NULL || laddr == NULL)
         return SA_ERR_ARG;
-    if (sa->sSocket == -1)
-        if ((rv = sa_socket_init(sa, laddr->nFamily, laddr->nProto)) != SA_OK)
+
+    /* lazy creation of underlying socket */
+    if (sa->fdSocket == -1)
+        if ((rv = sa_socket_init(sa, laddr->nFamily)) != SA_OK)
             return rv;
-    if (bind(sa->sSocket, laddr->saBuf, laddr->slBuf) == -1)
+
+    /* remove a possibly existing old Unix Domain socket on filesystem */
+    if (laddr->nFamily == AF_LOCAL) {
+        un = (struct sockaddr_un *)((void *)laddr->saBuf);
+        unlink(un->sun_path);
+    }
+
+    /* perform bind operation on underlying socket */
+    if (bind(sa->fdSocket, laddr->saBuf, laddr->slBuf) == -1)
         return SA_ERR_SYS;
+
     return SA_OK;
 }
 
+/* connect socket to a remote address */
 sa_rc_t sa_connect(sa_t *sa, sa_addr_t *raddr)
 {
     int flags, n, error;
@@ -510,87 +762,106 @@
     socklen_t len;
     sa_rc_t rv;
 
-    if (sa == NULL)
+    /* argument sanity check(s) */
+    if (sa == NULL || raddr == NULL)
         return SA_ERR_ARG;
 
-    if (sa->sSocket == -1)
-        if ((rv = sa_socket_init(sa, raddr->nFamily, raddr->nProto)) != SA_OK)
-            return rv;
+    /* connecting is only possible for stream communication */ 
+    if (sa->eType != SA_TYPE_STREAM)
+        return SA_ERR_USE;
 
-    if (!sa->bTimeout)
-        return connect(sa->sSocket, raddr->saBuf, raddr->slBuf);
+    /* lazy creation of underlying socket */
+    if (sa->fdSocket == -1)
+        if ((rv = sa_socket_init(sa, raddr->nFamily)) != SA_OK)
+            return rv;
 
-    error = 0;
     rv = SA_OK;
+    if (!sa->bTimeout) {
+        /* standard/non-timeout-aware connect operation */
+        if (connect(sa->fdSocket, raddr->saBuf, raddr->slBuf) < 0)
+            rv = SA_ERR_SYS;
+    }
+    else {
+        /* emulated/timeout-aware connect operation */
+        error = 0;
 
-    /* remember socket flags */
-    flags = fcntl(sa->sSocket, F_GETFL, 0);
+        /* temporarily switch underlying socket to non-blocking mode */
+        flags = fcntl(sa->fdSocket, F_GETFL, 0);
+        fcntl(sa->fdSocket, F_SETFL, flags|O_NONBLOCK);
+
+        /* perform the connect operation */
+        if ((n = connect(sa->fdSocket, raddr->saBuf, raddr->slBuf)) < 0) {
+            if (errno != EINPROGRESS) {
+                error = errno;
+                goto done;
+            }
+        }
 
-    /* switch to non-blocking mode */
-    fcntl(sa->sSocket, F_SETFL, flags|O_NONBLOCK);
+        /* ok if connect completed immediately */
+        if (n == 0)
+            goto done; 
+
+        /* wait for read or write possibility */
+        FD_ZERO(&rset);
+        FD_ZERO(&wset);
+        FD_SET(sa->fdSocket, &rset);
+        FD_SET(sa->fdSocket, &wset);
+        do {
+            n = select(sa->fdSocket+1, &rset, &wset, NULL, &sa->tvTimeout);
+        } while (n == -1 && errno == EINTR);
 
-    /* perform the connect */
-    if ((n = connect(sa->sSocket, raddr->saBuf, raddr->slBuf)) < 0) {
-        if (errno != EINPROGRESS) {
+        /* decide on return semantic */
+        if (n < 0) {
             error = errno;
             goto done;
         }
-    }
-
-    /* if connect completed immediately */
-    if (n == 0)
-        goto done; 
-
-    /* wait for read or write possibility */
-    FD_ZERO(&rset);
-    FD_ZERO(&wset);
-    FD_SET(sa->sSocket, &rset);
-    FD_SET(sa->sSocket, &wset);
-    do {
-        n = select(sa->sSocket+1, &rset, &wset, NULL, &sa->tvTimeout);
-    } while (n == -1 && errno == EINTR);
-
-    /* decide on return semantic */
-    if (n < 0) {
-        error = errno;
-        goto done;
-    }
-    else if (n == 0) {
-        error = ETIMEDOUT;
-        goto done;
-    }
+        else if (n == 0) {
+            error = ETIMEDOUT;
+            goto done;
+        }
 
-    /* fetch pending error */
-    len = sizeof(error);
-    if (getsockopt(sa->sSocket, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
-        error = errno;
+        /* fetch pending error */
+        len = sizeof(error);
+        if (getsockopt(sa->fdSocket, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
+            error = errno;
 
-    done:
+        done:
 
-    /* reset socket flags */
-    fcntl(sa->sSocket, F_SETFL, flags);
+        /* reset socket flags */
+        fcntl(sa->fdSocket, F_SETFL, flags);
 
-    /* optionally set errno */
-    if (error != 0) {
-        errno = error;
-        rv = SA_ERR_SYS;
+        /* optionally set errno */
+        if (error != 0) {
+            errno = error;
+            rv = SA_ERR_SYS;
+        }
     }
-
     return rv;
 }
 
+/* listen on socket for connections */
 sa_rc_t sa_listen(sa_t *sa, int backlog)
 {
+    /* argument sanity check(s) */
     if (sa == NULL)
         return SA_ERR_ARG;
-    if (sa->sSocket == -1)
-        /* at least sa_bind() has to be already performed */
+
+    /* listening is only possible for stream communication */ 
+    if (sa->eType != SA_TYPE_STREAM)
+        return SA_ERR_USE;
+
+    /* at least sa_bind() has to be already performed */
+    if (sa->fdSocket == -1)
         return SA_ERR_USE;
-    if (listen(sa->sSocket, backlog) == -1)
+
+    /* perform listen operation on underlying socket */
+    if (listen(sa->fdSocket, backlog) == -1)
         return SA_ERR_SYS;
+
     return SA_OK;
 }
 
+/* accept a connection on socket */
 sa_rc_t sa_accept(sa_t *sa, sa_addr_t **caddr, sa_t **csa)
 {
     sa_rc_t rv;
@@ -605,35 +876,55 @@
     socklen_t sa_len;
     int s;
 
-    if (sa == NULL)
+    /* argument sanity check(s) */
+    if (sa == NULL || caddr == NULL || csa == NULL)
         return SA_ERR_ARG;
-    if (sa->sSocket == -1)
-        /* at least sa_listen() has to be already performed */
+
+    /* accepting connections is only possible for stream communication */ 
+    if (sa->eType != SA_TYPE_STREAM)
         return SA_ERR_USE;
+
+    /* at least sa_listen() has to be already performed */
+    if (sa->fdSocket == -1)
+        return SA_ERR_USE;
+
+    /* if timeout is enabled, perform a smart-blocking wait */
     if (sa->bTimeout) {
         FD_ZERO(&fds);
-        FD_SET(sa->sSocket, &fds);
+        FD_SET(sa->fdSocket, &fds);
         do {
-            n = select(sa->sSocket+1, &fds, NULL, NULL, &sa->tvTimeout);
+            n = select(sa->fdSocket+1, &fds, NULL, NULL, &sa->tvTimeout);
         } while (n == -1 && errno == EINTR);
         if (n == 0) 
             errno = ETIMEDOUT;
         if (n <= 0)
             return SA_ERR_SYS;
     }
+
+    /* perform accept operation on underlying socket */
     sa_len = sizeof(sa_buf);
-    if ((s = accept(sa->sSocket, (struct sockaddr *)&sa_buf, &sa_len)) == -1)
+    if ((s = accept(sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_len)) == -1)
         return SA_ERR_SYS;
-    if ((rv = sa_s2a(caddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK)
+
+    /* create result address object */
+    if ((rv = sa_addr_create(caddr)) != SA_OK)
+        return rv;
+    if ((rv = sa_addr_s2a(*caddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK) {
+        sa_addr_destroy(*caddr);
         return rv;
+    }
+
+    /* create result socket object */
     if ((rv = sa_create(csa)) != SA_OK) {
-        free(*caddr);
+        sa_addr_destroy(*caddr);
         return rv;
     }
-    (*csa)->sSocket = s;
+    (*csa)->fdSocket = s;
+
     return SA_OK;
 }
 
+/* determine remote address */
 sa_rc_t sa_getremote(sa_t *sa, sa_addr_t **raddr)
 {
     sa_rc_t rv;
@@ -645,16 +936,35 @@
     } sa_buf;
     socklen_t sa_len;
 
-    if (sa == NULL)
+    /* argument sanity check(s) */
+    if (sa == NULL || raddr == NULL)
         return SA_ERR_ARG;
+
+    /* peers exist only for stream communication */ 
+    if (sa->eType != SA_TYPE_STREAM)
+        return SA_ERR_USE;
+
+    /* at least sa_connect() or sa_accept() has to be already performed */
+    if (sa->fdSocket == -1)
+        return SA_ERR_USE;
+
+    /* determine remote address of underlying socket */
     sa_len = sizeof(sa_buf);
-    if (getpeername(sa->sSocket, (struct sockaddr *)&sa_buf, &sa_len) < 0)
+    if (getpeername(sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_len) < 0)
         return SA_ERR_SYS;
-    if ((rv = sa_s2a(raddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK)
+
+    /* create result address object */
+    if ((rv = sa_addr_create(raddr)) != SA_OK)
         return rv;
+    if ((rv = sa_addr_s2a(*raddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK) {
+        sa_addr_destroy(*raddr);
+        return rv;
+    }
+
     return SA_OK;
 }
 
+/* determine local address */
 sa_rc_t sa_getlocal(sa_t *sa, sa_addr_t **laddr)
 {
     sa_rc_t rv;
@@ -666,147 +976,187 @@
     } sa_buf;
     socklen_t sa_len;
 
-    if (sa == NULL)
+    /* argument sanity check(s) */
+    if (sa == NULL || laddr == NULL)
         return SA_ERR_ARG;
+
+    /* at least sa_bind() has to be already performed */
+    if (sa->fdSocket == -1)
+        return SA_ERR_USE;
+
+    /* determine local address of underlying socket */
     sa_len = sizeof(sa_buf);
-    if (getsockname(sa->sSocket, (struct sockaddr *)&sa_buf, &sa_len) < 0)
+    if (getsockname(sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_len) < 0)
         return SA_ERR_SYS;
-    if ((rv = sa_s2a(laddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK)
+
+    /* create result address object */
+    if ((rv = sa_addr_create(laddr)) != SA_OK)
+        return rv;
+    if ((rv = sa_addr_s2a(*laddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK) {
+        sa_addr_destroy(*laddr);
         return rv;
+    }
+
     return SA_OK;
 }
 
+/* peek at underlying socket */
 sa_rc_t sa_getfd(sa_t *sa, int *fd)
 {
+    /* argument sanity check(s) */
     if (sa == NULL || fd == NULL)
         return SA_ERR_ARG;
-    *fd = sa->sSocket;
+
+    /* if still no socket exists, say this explicitly */
+    if (sa->fdSocket == -1)
+        return SA_ERR_USE;
+
+    /* pass socket to caller */
+    *fd = sa->fdSocket;
+
     return SA_OK;
 }
 
+/* internal raw read operation */
 static int sa_read_raw(sa_t *sa, char *cpBuf, int nBufLen)
 {
     int rv;
     fd_set fds;
 
+    /* if timeout is enabled, perform explicit/smart blocking instead 
+       of implicitly/hard blocking in the read(2) system call */
     if (sa->bTimeout) {
         FD_ZERO(&fds);
-        FD_SET(sa->sSocket, &fds);
+        FD_SET(sa->fdSocket, &fds);
         do {
-            rv = select(sa->sSocket+1, &fds, NULL, NULL, &sa->tvTimeout);
+            rv = select(sa->fdSocket+1, &fds, NULL, NULL, &sa->tvTimeout);
         } while (rv == -1 && errno == EINTR);
         if (rv == 0) {
             errno = ETIMEDOUT;
             return -1;
         }
     }
+
+    /* perform read operation on underlying socket */
     do {
-        rv = read(sa->sSocket, cpBuf, nBufLen);
+        rv = read(sa->fdSocket, cpBuf, nBufLen);
     } while (rv == -1 && errno == EINTR);
+
     return rv;
 }
 
+/* read data from socket */
 sa_rc_t sa_read(sa_t *sa, char *cpBuf, size_t nBufReq, size_t *nBufRes)
 {
     int n;
-    int rv;
+    sa_rc_t rv;
     size_t res;
 
+    /* argument sanity check(s) */
     if (sa == NULL || cpBuf == NULL || nBufReq == 0)
-        return 0;
-    
-    /* flush write buffer */
+        return SA_ERR_ARG;
+
+    /* reading is only possible for stream communication */ 
+    if (sa->eType != SA_TYPE_STREAM)
+        return SA_ERR_USE;
+
+    /* at least a connection has to exist */
+    if (sa->fdSocket == -1)
+        return SA_ERR_USE;
+
+    /* trigger a write buffer flush */
     if (sa->nWriteLen > 0)
         sa_flush(sa);
+
+    /* perform read operation */
     rv = SA_OK;
-    res = 0;
-    while (1) {
-        if (nBufReq <= sa->nReadLen) {
-            /* buffer holds enough data, so use this */
-            memmove(cpBuf, sa->cpReadBuf, nBufReq);
-            memmove(sa->cpReadBuf, sa->cpReadBuf+nBufReq, sa->nReadLen-nBufReq);
-            sa->nReadLen -= nBufReq;
-            res += nBufReq;
-        }
-        else {
-            if (sa->nReadLen > 0) {
-                /* fetch already existing buffer contents as a start */
-                memmove(cpBuf, sa->cpReadBuf, sa->nReadLen);
-                nBufReq -= sa->nReadLen;
-                cpBuf   += sa->nReadLen;
-                res     += sa->nReadLen;
-                sa->nReadLen = 0;
-            }
-            if (nBufReq >= sa->nReadSize) {
-                /* buffer is too small at all, so read directly */
-                n = sa_read_raw(sa, cpBuf, nBufReq);
-                if (n > 0)
-                    res += n;
-                else if (n <= 0)
-                    rv = SA_ERR_SYS;
+    if (sa->nReadSize == 0) {
+        /* user-space unbuffered I/O */
+        res = sa_read_raw(sa, cpBuf, nBufReq);
+        if (res == 0)
+            rv = SA_ERR_EOF;
+        else if (res < 0)
+            rv = SA_ERR_SYS;
+    }
+    else {
+        /* user-space buffered I/O */
+        res = 0;
+        while (1) {
+            if (nBufReq <= sa->nReadLen) {
+                /* buffer holds enough data, so just use this */
+                memmove(cpBuf, sa->cpReadBuf, nBufReq);
+                memmove(sa->cpReadBuf, sa->cpReadBuf+nBufReq, sa->nReadLen-nBufReq);
+                sa->nReadLen -= nBufReq;
+                res += nBufReq;
             }
             else {
-                /* fill buffer with new data */
-                n = sa_read_raw(sa, sa->cpReadBuf, sa->nReadSize);
-                if (n <= 0)
-                    rv = SA_ERR_SYS;
+                if (sa->nReadLen > 0) {
+                    /* fetch already existing buffer contents as a start */
+                    memmove(cpBuf, sa->cpReadBuf, sa->nReadLen);
+                    nBufReq -= sa->nReadLen;
+                    cpBuf   += sa->nReadLen;
+                    res     += sa->nReadLen;
+                    sa->nReadLen = 0;
+                }
+                if (nBufReq >= sa->nReadSize) {
+                    /* buffer is too small at all, so read directly */
+                    n = sa_read_raw(sa, cpBuf, nBufReq);
+                    if (n > 0)
+                        res += n;
+                    else if (n == 0)
+                        rv = (res == 0 ? SA_ERR_EOF : SA_OK);
+                    else if (n < 0)
+                        rv = (res == 0 ? SA_ERR_SYS : SA_OK);
+                }
                 else {
-                    sa->nReadLen = n;
-                    continue;
+                    /* fill buffer with new data */
+                    n = sa_read_raw(sa, sa->cpReadBuf, sa->nReadSize);
+                    if (n < 0)
+                        /* error on this read, but perhaps ok as a whole */
+                        rv = (res == 0 ? SA_ERR_SYS : SA_OK);
+                    if (n == 0)
+                        /* EOF on this read, but perhaps ok as a whole */
+                        rv = (res == 0 ? SA_ERR_EOF : SA_OK);
+                    else {
+                        sa->nReadLen = n;
+                        continue; /* REPEAT OPERATION! */
+                    }
                 }
             }
+            break;
         }
-        break;
     }
+
+    /* pass number of actually read bytes to caller */ 
     if (nBufRes != NULL)
         *nBufRes = res;
-    return rv;
-}
 
-sa_rc_t sa_readfrom(sa_t *sa, char *buf, size_t buflen, size_t *bufdone, sa_addr_t **raddr)
-{
-    sa_rc_t rv;
-    union {
-        struct sockaddr_in sa4;
-#ifdef AF_INET6
-        struct sockaddr_in6 sa6;
-#endif
-    } sa_buf;
-    socklen_t sa_len;
-    size_t n;
-    fd_set fds;
-
-    if (sa == NULL || buf == NULL || buflen == 0 || raddr == NULL)
-        return SA_ERR_ARG;
-    if (sa->bTimeout) {
-        FD_ZERO(&fds);
-        FD_SET(sa->sSocket, &fds);
-        do {
-            n = select(sa->sSocket+1, &fds, NULL, NULL, &sa->tvTimeout);
-        } while (n == -1 && errno == EINTR);
-        if (n == 0) 
-            errno = ETIMEDOUT;
-        if (n <= 0)
-            return SA_ERR_SYS;
-    }
-    sa_len = sizeof(sa_buf);
-    if ((n = recvfrom(sa->sSocket, buf, buflen, 0, (struct sockaddr *)&sa_buf, &sa_len)) == -1)
-        return SA_ERR_SYS;
-    if ((rv = sa_s2a(raddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK)
-        return rv;
-    if (bufdone != NULL)
-        *bufdone = n;
-    return SA_OK;
+    return rv;
 }
 
-sa_rc_t sa_readline(sa_t *sa, char *cpBuf, size_t nBufReq, size_t *nBufRes)
+/* read data from socket until [CR]LF (convinience function) */
+sa_rc_t sa_readln(sa_t *sa, char *cpBuf, size_t nBufReq, size_t *nBufRes)
 {
     char c;
     size_t n;
     size_t res;
 
-    if (sa == NULL)
+    /* argument sanity check(s) */
+    if (sa == NULL || cpBuf == NULL || nBufReq == 0)
         return SA_ERR_ARG;
+
+    /* reading is only possible for stream communication */ 
+    if (sa->eType != SA_TYPE_STREAM)
+        return SA_ERR_USE;
+
+    /* at least a connection has to exist */
+    if (sa->fdSocket == -1)
+        return SA_ERR_USE;
+
+    /* we just perform a plain sa_read() per character, because if
+       buffers are enabled, this is not as stupid as it looks at the first
+       hand and if buffers are disabled, there is no better solution
+       anyway. */
     res = 0;
     while (res < (nBufReq-1)) {
         sa_read(sa, &c, 1, &n);
@@ -817,131 +1167,187 @@
             break;
     }
     cpBuf[res] = '\0';
+
+    /* pass number of actually read characters to caller */
     if (nBufRes != NULL)
         *nBufRes = res;
+
     return SA_OK;
 }
 
+/* internal raw write operation */
 static int sa_write_raw(sa_t *sa, const char *cpBuf, int nBufLen)
 {
     int rv;
     fd_set fds;
 
+    /* if timeout is enabled, perform explicit/smart blocking instead 
+       of implicitly/hard blocking in the write(2) system call */
     if (sa->bTimeout) {
         FD_ZERO(&fds);
-        FD_SET(sa->sSocket, &fds);
+        FD_SET(sa->fdSocket, &fds);
         do {
-            rv = select(sa->sSocket+1, NULL, &fds, NULL, &sa->tvTimeout);
+            rv = select(sa->fdSocket+1, NULL, &fds, NULL, &sa->tvTimeout);
         } while (rv == -1 && errno == EINTR);
         if (rv == 0) {
             errno = ETIMEDOUT;
             return -1;
         }
     }
+
+    /* perform write operation on underlying socket */
     do {
-        rv = write(sa->sSocket, cpBuf, nBufLen);
+        rv = write(sa->fdSocket, cpBuf, nBufLen);
     } while (rv == -1 && errno == EINTR);
+
     return rv;
 }
 
+/* write data to socket */
 sa_rc_t sa_write(sa_t *sa, const char *cpBuf, size_t nBufReq, size_t *nBufRes)
 {
     size_t n;
     size_t res;
+    sa_rc_t rv;
 
-    if (sa == NULL)
+    /* argument sanity check(s) */
+    if (sa == NULL || cpBuf == NULL || nBufReq == 0)
         return SA_ERR_ARG;
 
-    if (nBufReq > (sa->nWriteSize - sa->nWriteLen)) {
-        /* not enough space in buffer, so flush buffer */
-        sa_flush(sa);
-    }
-    res = 0;
-    if (nBufReq >= sa->nWriteSize) {
-        /* buffer too small at all, so write immediately */
-        while (nBufReq > 0) {
-            n = sa_write_raw(sa, cpBuf, nBufReq);
-            if (n <= 0)
-                break;
-            nBufReq  -= n;
-            cpBuf    += n;
-            res      += n;
-        }
+    /* writing is only possible for stream communication */ 
+    if (sa->eType != SA_TYPE_STREAM)
+        return SA_ERR_USE;
+
+    /* at least a connection has to exist */
+    if (sa->fdSocket == -1)
+        return SA_ERR_USE;
+
+    rv = SA_OK;
+    if (sa->nWriteSize == 0) {
+        /* user-space unbuffered I/O */
+        res = sa_write_raw(sa, cpBuf, nBufReq);
+        if (res < 0)
+            rv = SA_ERR_SYS;
     }
     else {
-        /* (again) enough sprace in buffer, so store data */
-        memmove(sa->cpWriteBuf+sa->nWriteLen, cpBuf, nBufReq);
-        sa->nWriteLen += nBufReq;
-        res = nBufReq;
+        /* user-space buffered I/O */
+        if (nBufReq > (sa->nWriteSize - sa->nWriteLen)) {
+            /* not enough space in buffer, so flush buffer first */
+            sa_flush(sa);
+        }
+        res = 0;
+        if (nBufReq >= sa->nWriteSize) {
+            /* buffer too small at all, so write immediately */
+            while (nBufReq > 0) {
+                n = sa_write_raw(sa, cpBuf, nBufReq);
+                if (n < 0)
+                    rv = (res == 0 ? SA_ERR_SYS : SA_OK);
+                if (n <= 0)
+                    break;
+                nBufReq  -= n;
+                cpBuf    += n;
+                res      += n;
+            }
+        }
+        else {
+            /* (again) enough sprace in buffer, so store data */
+            memmove(sa->cpWriteBuf+sa->nWriteLen, cpBuf, nBufReq);
+            sa->nWriteLen += nBufReq;
+            res = nBufReq;
+        }
     }
+
+    /* pass number of actually written bytes to caller */
     if (nBufRes != NULL)
         *nBufRes = res;
-    return SA_OK;
-}
-
-sa_rc_t sa_writeto(sa_t *sa, const char *buf, size_t buflen, size_t *bufdone, sa_addr_t *raddr)
-{
-    size_t n;
-    fd_set fds;
 
-    if (sa == NULL || buf == NULL || buflen == 0 || raddr == NULL)
-        return SA_ERR_ARG;
-    if (sa->bTimeout) {
-        FD_ZERO(&fds);
-        FD_SET(sa->sSocket, &fds);
-        do {
-            n = select(sa->sSocket+1, NULL, &fds, NULL, &sa->tvTimeout);
-        } while (n == -1 && errno == EINTR);
-        if (n == 0) 
-            errno = ETIMEDOUT;
-        if (n <= 0)
-            return SA_ERR_SYS;
-    }
-    if ((n = sendto(sa->sSocket, buf, buflen, 0, raddr->saBuf, raddr->slBuf)) == -1)
-        return SA_ERR_SYS;
-    if (bufdone != NULL)
-        *bufdone = n;
-    return SA_OK;
+    return rv;
 }
 
-sa_rc_t sa_printf(sa_t *sa, const char *cpFmt, ...)
+/* 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;
 
-    if (sa == NULL)
+    /* argument sanity check(s) */
+    if (sa == NULL || cpFmt == NULL)
         return SA_ERR_ARG;
+
+    /* writing is only possible for stream communication */ 
+    if (sa->eType != SA_TYPE_STREAM)
+        return SA_ERR_USE;
+
+    /* at least a connection has to exist */
+    if (sa->fdSocket == -1)
+        return SA_ERR_USE;
+
+    /* format string into temporary buffer */
     va_start(ap, cpFmt);
-    n = sa_vsnprintf(caBuf, sizeof(caBuf), cpFmt, ap);
-    sa_write(sa, caBuf, n, &n);
+    n = sa_mvsnprintf(caBuf, sizeof(caBuf), cpFmt, ap);
     va_end(ap);
-    return SA_OK;
+
+    /* write result buffer to socket */
+    rv = sa_write(sa, caBuf, n, &n);
+
+    return rv;
 }
 
+/* flush write/outgoing I/O buffer */
 sa_rc_t sa_flush(sa_t *sa)
 {
     size_t n;
+    sa_rc_t rv;
 
+    /* argument sanity check(s) */
     if (sa == NULL)
         return SA_ERR_ARG;
-    while (sa->nWriteLen > 0) {
-        n = sa_write_raw(sa, sa->cpWriteBuf, sa->nWriteLen);
-        if (n <= 0)
-            break;
-        memmove(sa->cpWriteBuf, sa->cpWriteBuf+n, sa->nWriteLen-n);
-        sa->nWriteLen -= n;
+
+    /* flushing is only possible for stream communication */ 
+    if (sa->eType != SA_TYPE_STREAM)
+        return SA_ERR_USE;
+
+    /* at least a connection has to exist */
+    if (sa->fdSocket == -1)
+        return SA_ERR_USE;
+
+    /* try to flush buffer */
+    rv = SA_OK;
+    if (sa->nWriteSize > 0) {
+        while (sa->nWriteLen > 0) {
+            n = sa_write_raw(sa, sa->cpWriteBuf, sa->nWriteLen);
+            if (n < 0)
+                rv = SA_ERR_SYS;
+            if (n <= 0)
+                break;
+            memmove(sa->cpWriteBuf, sa->cpWriteBuf+n, sa->nWriteLen-n);
+            sa->nWriteLen -= n;
+        }
+        sa->nWriteLen = 0;
     }
-    sa->nWriteLen = 0;
-    return SA_OK;
+    return rv;
 }
 
+/* shutdown a socket connection */
 sa_rc_t sa_shutdown(sa_t *sa, char *flags)
 {
     int how;
 
+    /* argument sanity check(s) */
     if (sa == NULL || flags == NULL)
         return SA_ERR_ARG;
+
+    /* shutdown is only possible for stream communication */ 
+    if (sa->eType != SA_TYPE_STREAM)
+        return SA_ERR_USE;
+
+    /* at least a connection has to exist */
+    if (sa->fdSocket == -1)
+        return SA_ERR_USE;
+
+    /* determine flags for shutdown(2) */
     how = 0;
     if (strcmp(flags, "r") == 0)
         how = SHUT_RD;
@@ -949,8 +1355,119 @@
         how = SHUT_WR;
     else if (strcmp(flags, "rw") == 0)
         how = SHUT_RDWR;
-    if (shutdown(sa->sSocket, how) == -1)
+    else
+        return SA_ERR_ARG;
+
+    /* perform shutdown operation on underlying socket */
+    if (shutdown(sa->fdSocket, how) == -1)
+        return SA_ERR_SYS;
+
+    return SA_OK;
+}
+
+/* receive data via socket */
+sa_rc_t sa_recv(sa_t *sa, char *buf, size_t buflen, size_t *bufdone, sa_addr_t **raddr)
+{
+    sa_rc_t rv;
+    union {
+        struct sockaddr_in sa4;
+#ifdef AF_INET6
+        struct sockaddr_in6 sa6;
+#endif
+    } sa_buf;
+    socklen_t sa_len;
+    size_t n;
+    fd_set fds;
+
+    /* argument sanity check(s) */
+    if (sa == NULL || buf == NULL || buflen == 0 || raddr == NULL)
+        return SA_ERR_ARG;
+
+    /* receiving is only possible for datagram communication */ 
+    if (sa->eType != SA_TYPE_DATAGRAM)
+        return SA_ERR_USE;
+
+    /* at least a sa_bind() has to be performed */
+    if (sa->fdSocket == -1)
+        return SA_ERR_USE;
+
+    /* if timeout is enabled, perform explicit/smart blocking instead 
+       of implicitly/hard blocking in the recvfrom(2) system call */
+    if (sa->bTimeout) {
+        FD_ZERO(&fds);
+        FD_SET(sa->fdSocket, &fds);
+        do {
+            n = select(sa->fdSocket+1, &fds, NULL, NULL, &sa->tvTimeout);
+        } while (n == -1 && errno == EINTR);
+        if (n == 0) 
+            errno = ETIMEDOUT;
+        if (n <= 0)
+            return SA_ERR_SYS;
+    }
+
+    /* 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)
+        return SA_ERR_SYS;
+
+    /* create result address object */
+    if ((rv = sa_addr_create(raddr)) != SA_OK)
+        return rv;
+    if ((rv = sa_addr_s2a(*raddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK) {
+        sa_addr_destroy(*raddr);
+        return rv;
+    }
+
+    /* pass actual number of received bytes to caller */
+    if (bufdone != NULL)
+        *bufdone = n;
+
+    return SA_OK;
+}
+
+/* send data via socket */
+sa_rc_t sa_send(sa_t *sa, const char *buf, size_t buflen, size_t *bufdone, sa_addr_t *raddr)
+{
+    size_t n;
+    fd_set fds;
+    sa_rc_t rv;
+
+    /* argument sanity check(s) */
+    if (sa == NULL || buf == NULL || buflen == 0 || raddr == NULL)
+        return SA_ERR_ARG;
+
+    /* sending is only possible for datagram communication */ 
+    if (sa->eType != SA_TYPE_DATAGRAM)
+        return SA_ERR_USE;
+
+    /* lazy creation of underlying socket */
+    if (sa->fdSocket == -1)
+        if ((rv = sa_socket_init(sa, raddr->nFamily)) != SA_OK)
+            return rv;
+
+    /* if timeout is enabled, perform explicit/smart blocking instead 
+       of implicitly/hard blocking in the sendto(2) system call */
+    if (sa->bTimeout) {
+        FD_ZERO(&fds);
+        FD_SET(sa->fdSocket, &fds);
+        do {
+            n = select(sa->fdSocket+1, NULL, &fds, NULL, &sa->tvTimeout);
+        } while (n == -1 && errno == EINTR);
+        if (n == 0) 
+            errno = ETIMEDOUT;
+        if (n <= 0)
+            return SA_ERR_SYS;
+    }
+
+    /* perform send operation on underlying socket */
+    if ((n = sendto(sa->fdSocket, buf, buflen, 0, raddr->saBuf, raddr->slBuf)) == -1)
         return SA_ERR_SYS;
+
+    /* pass actual number of sent bytes to caller */
+    if (bufdone != NULL)
+        *bufdone = n;
+
     return SA_OK;
 }
 

CVSTrac 2.0.1