OSSP CVS Repository

ossp - Difference in ossp-pkg/sa/sa.c versions 1.7 and 1.8
Not logged in
[Honeypot]  [Browse]  [Home]  [Login]  [Reports
[Search]  [Ticket]  [Timeline
  [History

ossp-pkg/sa/sa.c 1.7 -> 1.8

--- sa.c 2001/10/05 13:19:30     1.7
+++ sa.c 2001/10/05 20:52:15     1.8
@@ -33,108 +33,114 @@
 #endif
 
 /* 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 */
 #include "sa.h"
 
 /* socket address abstraction object */
 struct sa_addr_st {
-    int              nFamily;
-    struct sockaddr *saBuf;
-    socklen_t        slBuf;
+    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 {
-    sa_type_t        eType;
-    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
+}
 
-/* minimal vsnprintf(3) */
+/* 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)
 {
     char *bufptr;
     char *bufend;
-    char ibuf[((sizeof(int)*8)/3)+10]; /* (available bits) x log_10(2) + safety */
+    /* sufficient integer buffer: <available-bits> x log_10(2) + safety */
+    char ibuf[((sizeof(int)*8)/3)+10]; 
     char *cp;
     char c;
     int d;
@@ -146,10 +152,13 @@
         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);
@@ -159,11 +168,12 @@
                 bufptr += n;
             }
             else if (c == 'd') {
+                /* implement "%d" */
                 d = (int)va_arg(ap, int);
 #ifdef HAVE_SNPRINTF
-                snprintf(ibuf, sizeof(ibuf), "%d", d); /* inherently secure */
+                snprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */
 #else
-                sprintf(ibuf, "%d", d); /* nevertheless secure; see ibuf above */
+                sprintf(ibuf, "%d", d);                /* implicitly secure */
 #endif
                 n = strlen(ibuf);
                 memcpy(bufptr, ibuf, n);
@@ -182,44 +192,66 @@
     return (bufptr - buffer);
 }
 
-/* minimal snprintf(3) */
+/* 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;
 
+    /* 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 chars;
 }
 
-sa_rc_t sa_addr_create(sa_addr_t **saa)
+/* create address object */
+sa_rc_t sa_addr_create(sa_addr_t **saap)
 {
-    if (saa == NULL)
+    sa_addr_t *saa;
+
+    /* argument sanity check(s) */
+    if (saap == NULL)
         return SA_ERR_ARG;
-    if ((*saa = (sa_addr_t *)malloc(sizeof(sa_addr_t))) == NULL)
+
+    /* allocate and initialize new address object */
+    if ((saa = (sa_addr_t *)malloc(sizeof(sa_addr_t))) == NULL)
         return SA_ERR_MEM;
-    (*saa)->saBuf   = NULL;
-    (*saa)->slBuf   = 0;
-    (*saa)->nFamily = 0;
+    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;
@@ -230,17 +262,17 @@
     struct hostent *he;
     struct servent *se;
     int bNumeric;
-    int i;
-    int n;
-    char *cpProto;
     char *cpHost;
     char *cpPort;
-    char *cpPath;
+    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;
 
@@ -249,34 +281,47 @@
     sa_mvsnprintf(uribuf, sizeof(uribuf), uri, ap);
     va_end(ap);
 
-    /* parse URI into parts */
+    /* initialize result variables */
+    sa = NULL;
+    sl = 0;
+    sf = 0;
+
+    /* parse URI and resolve contents. 
+       The following syntax is recognized:
+       - unix:<path>
+       - inet://<host>:<port>[#(tcp|udp)] */
     uri = uribuf;
-    cpProto = "unix";
-    if ((cp = strchr(uri, ':')) != NULL) {
-        cpProto = (char *)uri;
-        *cp = '\0';
-        uri = cp+1;
-    }
-    cpHost = "";
-    cpPort = "";
-    if (strncmp(uri, "//", 2) == 0) {
-        uri += 2;
-        cpHost = (char *)uri;
-        if ((cp = strchr(uri, ':')) == NULL)
+    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;
-        uri = cp;
-        if ((cp = strchr(uri, '/')) != NULL)
-            uri = cp;
-        else
-            uri = strchr(uri, '\0');
-    }
-    cpPath = (char *)uri;
 
-    /* resolve port */
-    nPort = 0;
-    if (strcmp(cpProto, "inet") == 0) {
+        /* resolve port */
+        nPort = 0;
         bNumeric = 1;
         for (i = 0; cpPort[i] != '\0'; i++) {
             if (!isdigit((int)cpPort[i])) {
@@ -287,38 +332,25 @@
         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);
         }
-    }
 
-    /* mandatory(!) socket address structure initialization */
-    memset(&un,  0, sizeof(un));
-    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 (strcmp(cpProto, "unix") == 0) {
-        if (cpProto[0] != '/')
-            return SA_ERR_ARG;
-        n = strlen(cpProto);
-        if ((n+1) > sizeof(un.sun_path))
-            return SA_ERR_MEM;
-        memcpy(un.sun_path, cpPath, n+1);
-        un.sun_family = AF_LOCAL;
-        sa = (struct sockaddr *)&un;
-        sl = sizeof(un);
-        sf = AF_LOCAL;
-    }
-    else if (strcmp(cpProto, "inet") == 0) {
-        if (inet_pton(AF_INET, cpHost, &sa4.sin_addr.s_addr) == 1) {
+        /* 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);
             sa = (struct sockaddr *)&sa4;
@@ -326,7 +358,7 @@
             sf = AF_INET;
         }
 #ifdef AF_INET6
-        else if (inet_pton(AF_INET6, cpHost, &sa6.sin6_addr.s6_addr) == 1) {
+        else if (sa_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;
@@ -338,7 +370,8 @@
             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));
+                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;
@@ -347,7 +380,8 @@
             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));
+                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;
@@ -355,18 +389,24 @@
 #endif
         }
     }
-    if (sa == NULL)
-        return SA_ERR_ARG;
-        
-    /* create result address structure */
+
+    /* 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;
+
     return SA_OK;
 }
 
+/* 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;
@@ -376,16 +416,19 @@
 #endif
     int sf;
 
+    /* argument sanity check(s) */
     if (saa == NULL || sabuf == NULL || salen == 0)
         return SA_ERR_ARG;
 
     /* create result address structure */
+    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;
 
-    /* fill in family */
+    /* resolve family */
     sf = 0;
     if (sizeof(struct sockaddr_un) == salen) {
         un = (struct sockaddr_un *)((void *)sabuf);
@@ -411,6 +454,7 @@
     return SA_OK;
 }
 
+/* export address object into URI */
 sa_rc_t sa_addr_a2u(sa_addr_t *saa, char **uri)
 {
     char uribuf[1024];
@@ -422,45 +466,60 @@
     char caHost[512];
     int nPort;
 
+    /* argument sanity check(s) */
     if (saa == NULL || uri == NULL)
         return SA_ERR_ARG;
+
+    /* 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);
     }
-    else {
+#ifdef AF_INET6
+    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);
-            inet_ntop(AF_INET, &sa4->sin_addr.s_addr, caHost, sizeof(caHost));
+            sa_inet_ntop(AF_INET, &sa4->sin_addr.s_addr, caHost, sizeof(caHost));
             nPort = ntohs(sa4->sin_port);
         }
 #ifdef AF_INET6
-        else if (saa->nFamily == AF_INET6) {
+        else {
             sa6 = (struct sockaddr_in6 *)((void *)saa->saBuf);
-            inet_ntop(AF_INET6, &sa6->sin6_addr.s6_addr, caHost, sizeof(caHost));
+            sa_inet_ntop(AF_INET6, &sa6->sin6_addr.s6_addr, caHost, sizeof(caHost));
             nPort = ntohs(sa6->sin6_port);
         }
 #endif
-        else
-            return SA_ERR_ARG;
         sa_msnprintf(uribuf, sizeof(uribuf), "inet://%s:%d", caHost, nPort);
     }
+    else
+        return SA_ERR_INT;
+
+    /* pass result to caller */
     *uri = strdup(uribuf);
+
     return SA_OK;
 }
 
+/* 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;
 }
 
+/* internal lazy/delayed initialization of underlying socket */
 static sa_rc_t sa_socket_init(sa_t *sa, int nFamily)
 {
     int nType;
@@ -468,11 +527,12 @@
     struct protoent *pe;
     char *cpProto;
 
+    /* argument sanity check(s) */
     if (sa == NULL)
         return SA_ERR_ARG;
 
-    /* only perform operation if socket still do not exists */
-    if (sa->sSocket != -1)
+    /* only perform operation if socket still does not exist */
+    if (sa->fdSocket != -1)
         return SA_ERR_USE;
 
     /* determine socket type */
@@ -481,7 +541,7 @@
     else if (sa->eType == SA_TYPE_DATAGRAM)
         nType = SOCK_DGRAM;
     else
-        return SA_ERR_ARG;
+        return SA_ERR_INT;
 
     /* determine socket protocol */
     if (nFamily == AF_LOCAL) 
@@ -502,95 +562,134 @@
         nProto = pe->p_proto;
     }
     else
-        return SA_ERR_ARG;
+        return SA_ERR_INT;
 
-    /* create the socket */
-    if ((sa->sSocket = socket(nFamily, nType, nProto)) == -1)
+    /* 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->eType      = SA_TYPE_STREAM;
-    sa->sSocket    = -1;
-    sa->bTimeout   = 0;
+    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;
+    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)
+    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;
+        return SA_ERR_USE;
+
+    /* configure read/incoming buffer */
     if (rsize > 0) {
         if (sa->cpReadBuf == NULL)
             cp = (char *)malloc(rsize);
@@ -607,6 +706,8 @@
         sa->cpReadBuf = NULL;
         sa->nReadSize = 0;
     }
+
+    /* configure write/outgoing buffer */
     if (wsize > 0) {
         if (sa->cpWriteBuf == NULL)
             cp = (char *)malloc(wsize);
@@ -623,50 +724,39 @@
         sa->cpWriteBuf = NULL;
         sa->nWriteSize = 0;
     }
-    return SA_OK;
-}
 
-sa_rc_t sa_getoption(sa_t *sa, int optname, void *optval, socklen_t *optlen)
-{
-    if (sa == NULL)
-        return SA_ERR_ARG;
-    if (sa->sSocket == -1)
-        return SA_ERR_USE;
-    if (getsockopt(sa->sSocket, SOL_SOCKET, optname, optval, optlen) == -1)
-        return SA_ERR_SYS;
-    return SA_OK;
-}
-
-sa_rc_t sa_setoption(sa_t *sa, int optname, const void *optval, socklen_t optlen)
-{
-    if (sa == NULL)
-        return SA_ERR_ARG;
-    if (sa->sSocket == -1)
-        return SA_ERR_USE;
-    if (setsockopt(sa->sSocket, SOL_SOCKET, optname, optval, optlen) == -1)
-        return SA_ERR_SYS;
     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)
+
+    /* lazy creation of underlying socket */
+    if (sa->fdSocket == -1)
         if ((rv = sa_socket_init(sa, laddr->nFamily)) != SA_OK)
             return rv;
+
+    /* 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);
     }
-    if (bind(sa->sSocket, laddr->saBuf, laddr->slBuf) == -1)
+
+    /* 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;
@@ -674,94 +764,106 @@
     socklen_t len;
     sa_rc_t rv;
 
-    if (sa == NULL)
+    /* argument sanity check(s) */
+    if (sa == NULL || raddr == NULL)
         return SA_ERR_ARG;
+
+    /* connecting is only possible for stream communication */ 
     if (sa->eType != SA_TYPE_STREAM)
         return SA_ERR_USE;
-    if (sa->sSocket == -1)
+
+    /* lazy creation of underlying socket */
+    if (sa->fdSocket == -1)
         if ((rv = sa_socket_init(sa, raddr->nFamily)) != SA_OK)
             return rv;
 
+    rv = SA_OK;
     if (!sa->bTimeout) {
-        if (connect(sa->sSocket, raddr->saBuf, raddr->slBuf) < 0)
-            return SA_ERR_SYS;
-        else
-            return SA_OK;
+        /* 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;
 
-    error = 0;
-    rv = SA_OK;
-
-    /* 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;
+
+    /* listening is only possible for stream communication */ 
     if (sa->eType != SA_TYPE_STREAM)
         return SA_ERR_USE;
-    if (sa->sSocket == -1)
-        /* at least sa_bind() has to be already performed */
+
+    /* 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;
@@ -776,41 +878,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;
+
+    /* accepting connections is only possible for stream communication */ 
     if (sa->eType != SA_TYPE_STREAM)
         return SA_ERR_USE;
-    if (sa->sSocket == -1)
-        /* at least sa_listen() has to be already performed */
+
+    /* 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;
+
+    /* 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) {
         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;
@@ -822,22 +938,35 @@
     } sa_buf;
     socklen_t sa_len;
 
-    if (sa == NULL)
+    /* argument sanity check(s) */
+    if (sa == NULL || raddr == NULL)
         return SA_ERR_ARG;
-    sa_len = sizeof(sa_buf);
-    if (sa->sSocket == -1)
+
+    /* 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;
-    if (getpeername(sa->sSocket, (struct sockaddr *)&sa_buf, &sa_len) < 0)
+
+    /* determine remote address of underlying socket */
+    sa_len = sizeof(sa_buf);
+    if (getpeername(sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_len) < 0)
         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;
     }
+
     return SA_OK;
 }
 
+/* determine local address */
 sa_rc_t sa_getlocal(sa_t *sa, sa_addr_t **laddr)
 {
     sa_rc_t rv;
@@ -849,85 +978,114 @@
     } sa_buf;
     socklen_t sa_len;
 
-    if (sa == NULL)
+    /* argument sanity check(s) */
+    if (sa == NULL || laddr == NULL)
         return SA_ERR_ARG;
-    if (sa->sSocket == -1)
+
+    /* 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;
+
+    /* 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;
-    if (sa->sSocket == -1)
+
+    /* if still no socket exists, say this explicitly */
+    if (sa->fdSocket == -1)
         return SA_ERR_USE;
-    *fd = sa->sSocket;
+
+    /* 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;
     sa_rc_t rv;
     size_t res;
 
+    /* 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;
-    if (sa->sSocket == -1)
+
+    /* at least a connection has to exist */
+    if (sa->fdSocket == -1)
         return SA_ERR_USE;
 
-    /* flush write buffer */
+    /* trigger a write buffer flush */
     if (sa->nWriteLen > 0)
         sa_flush(sa);
 
     /* perform read operation */
     rv = SA_OK;
     if (sa->nReadSize == 0) {
-        /* unbuffered I/O */
+        /* user-space unbuffered I/O */
         res = sa_read_raw(sa, cpBuf, nBufReq);
-        if (res <= 0)
+        if (res == 0)
+            rv = SA_ERR_EOF;
+        else if (res < 0)
             rv = SA_ERR_SYS;
     }
     else {
-        /* buffered I/O */
+        /* user-space buffered I/O */
         res = 0;
         while (1) {
             if (nBufReq <= sa->nReadLen) {
-                /* buffer holds enough data, so use this */
+                /* 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;
@@ -947,84 +1105,60 @@
                     n = sa_read_raw(sa, cpBuf, nBufReq);
                     if (n > 0)
                         res += n;
-                    else if (n <= 0)
-                        rv = SA_ERR_SYS;
+                    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 {
                     /* fill buffer with new data */
                     n = sa_read_raw(sa, sa->cpReadBuf, sa->nReadSize);
-                    if (n <= 0)
-                        rv = SA_ERR_SYS;
+                    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;
+                        continue; /* REPEAT OPERATION! */
                     }
                 }
             }
             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->eType != SA_TYPE_DATAGRAM)
-        return SA_ERR_USE;
-    if (sa->sSocket == -1)
-        return SA_ERR_USE;
-    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_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;
-    }
-    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;
-    if (sa->sSocket == -1)
+
+    /* 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);
@@ -1035,59 +1169,72 @@
             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;
+
+    /* writing is only possible for stream communication */ 
     if (sa->eType != SA_TYPE_STREAM)
         return SA_ERR_USE;
-    if (sa->sSocket == -1)
+
+    /* at least a connection has to exist */
+    if (sa->fdSocket == -1)
         return SA_ERR_USE;
 
     rv = SA_OK;
     if (sa->nWriteSize == 0) {
-        /* unbuffered I/O */
+        /* user-space unbuffered I/O */
         res = sa_write_raw(sa, cpBuf, nBufReq);
-        if (res < 0) {
-            res = 0;
+        if (res < 0)
             rv = SA_ERR_SYS;
-        }
     }
     else {
-        /* buffered I/O */
+        /* user-space buffered I/O */
         if (nBufReq > (sa->nWriteSize - sa->nWriteLen)) {
-            /* not enough space in buffer, so flush buffer */
+            /* not enough space in buffer, so flush buffer first */
             sa_flush(sa);
         }
         res = 0;
@@ -1096,7 +1243,7 @@
             while (nBufReq > 0) {
                 n = sa_write_raw(sa, cpBuf, nBufReq);
                 if (n < 0)
-                    rv = SA_ERR_SYS;
+                    rv = (res == 0 ? SA_ERR_SYS : SA_OK);
                 if (n <= 0)
                     break;
                 nBufReq  -= n;
@@ -1111,72 +1258,70 @@
             res = nBufReq;
         }
     }
+
+    /* pass number of actually written bytes to caller */
     if (nBufRes != NULL)
         *nBufRes = res;
+
     return rv;
 }
 
-sa_rc_t sa_writeto(sa_t *sa, const char *buf, size_t buflen, size_t *bufdone, sa_addr_t *raddr)
+/* write formatted string to socket (convinience function) */
+sa_rc_t sa_writef(sa_t *sa, const char *cpFmt, ...)
 {
+    va_list ap;
     size_t n;
-    fd_set fds;
+    char caBuf[1024];
     sa_rc_t rv;
 
-    if (sa == NULL || buf == NULL || buflen == 0 || raddr == NULL)
+    /* argument sanity check(s) */
+    if (sa == NULL || cpFmt == NULL)
         return SA_ERR_ARG;
-    if (sa->eType != SA_TYPE_DATAGRAM)
-        return SA_ERR_USE;
-    if (sa->sSocket == -1)
-        if ((rv = sa_socket_init(sa, raddr->nFamily)) != SA_OK)
-            return rv;
-    if (sa->sSocket == -1)
+
+    /* writing is only possible for stream communication */ 
+    if (sa->eType != SA_TYPE_STREAM)
         return SA_ERR_USE;
-    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;
-}
 
-sa_rc_t sa_writef(sa_t *sa, const char *cpFmt, ...)
-{
-    va_list ap;
-    size_t n;
-    char caBuf[1024];
+    /* at least a connection has to exist */
+    if (sa->fdSocket == -1)
+        return SA_ERR_USE;
 
-    if (sa == NULL)
-        return SA_ERR_ARG;
+    /* format string into temporary buffer */
     va_start(ap, cpFmt);
     n = sa_mvsnprintf(caBuf, sizeof(caBuf), cpFmt, ap);
-    sa_write(sa, caBuf, n, &n);
     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;
+
+    /* flushing is only possible for stream communication */ 
     if (sa->eType != SA_TYPE_STREAM)
         return SA_ERR_USE;
-    if (sa->sSocket == -1)
+
+    /* 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);
@@ -1184,19 +1329,27 @@
         }
         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;
-    if (sa->sSocket == -1)
+
+    /* 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;
@@ -1204,8 +1357,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