OSSP CVS Repository

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

ossp-pkg/l2/l2_ut_sa.c 1.4 -> 1.5

--- l2_ut_sa.c   2001/09/08 22:05:46     1.4
+++ l2_ut_sa.c   2001/09/09 15:54:31     1.5
@@ -24,9 +24,10 @@
 **  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 **  SUCH DAMAGE.
 **
-**  l2_ut_sa.c: utility library for socket address handling
+**  l2_ut_sa.c: socket abstraction library
 */
 
+/* system headers */
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
@@ -34,6 +35,9 @@
 #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>
@@ -41,15 +45,24 @@
 #include <sys/socket.h>
 #include <arpa/inet.h>
 
+/* own headers */
 #include "l2_config.h"
 #include "l2_ut_sa.h"
 
+struct sa_addr_st {
+    struct sockaddr *saa_buf;
+    socklen_t        saa_len;
+    int              saa_family;
+    int              saa_proto;
+};
+
 #if !defined(AF_LOCAL) && defined(AF_UNIX)
 #define AF_LOCAL AF_UNIX
 #endif
 
-#ifndef HAVE_INET_PTON
-#ifdef HAVE_INET_ATON
+#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)
 {
     struct in_addr in_val;
@@ -67,16 +80,69 @@
 #else
 #error "neither inet_pton nor inet_aton available"
 #endif
+
+#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)
+{
+    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;
+    }
+    errno = EAFNOSUPPORT;
+    return -1;
+}
+#else
+#error "neither inet_ntop nor inet_ntoa available"
+#endif
+
+#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)
+{
+    int rv;
+    
+    rv = vsprintf(str, fmt, ap);
+    if (rv > size) {
+        fprintf(stderr, "ERROR: vsprintf(3) buffer overflow!\n");
+        abort();
+    }
+}
+#else
+#error "neither vsnprintf nor vsprintf available"
 #endif
 
-#ifndef NUL
-#define NUL '\0'
+#if defined(HAVE_SNPRINTF)
+#define sa_snprintf snprintf
+#else
+static int sa_snprintf(char *str, size_t size, const char *fmt, ...)
+{
+    va_list ap;
+    int rv;
+
+    va_start(ap, fmt);
+    rv = sa_vsnprintf(str, size, fmt, ap);
+    va_end(ap);
+    return rv;
+}
 #endif
 
-l2_util_sa_t *l2_util_sa_create(int sa_type, ...)
+sa_rc_t sa_u2a(sa_addr_t **saa, const char *uri, ...)
 {
     va_list ap;
-    l2_util_sa_t *rc;
     int nPort;
     socklen_t sl;
     struct sockaddr *sa;
@@ -84,135 +150,644 @@
 #ifdef AF_INET6
     struct sockaddr_in6 sa6;
 #endif
-    struct sockaddr_un sau;
     struct hostent *he;
     struct servent *se;
     struct protoent *pe;
     int bNumeric;
     int i;
-    char *cpPath;
-    int   nPath;
     char *cpProto;
     int   nProto;
     char *cpHost;
     char *cpPort;
+    char uribuf[1024];
+    char *cp;
+    int sf;
+
+    if (saa == NULL || uri == NULL)
+        return SA_ERR_ARG;
+
+    /* create or just take over URI */
+    va_start(ap, uri);
+    sa_vsnprintf(uribuf, sizeof(uribuf), uri, ap);
+    va_end(ap);
 
-    va_start(ap, sa_type);
-    sa = NULL;
-    sl = 0;
-    if (sa_type == L2_UTIL_SA_UNIX) {
-#if defined(AF_LOCAL)
-        if ((cpPath = va_arg(ap, char *)) == NULL)
-            return NULL;
-        if ((nPath = strlen(cpPath)) >= (sizeof(sau.sun_path)-1))
-            return NULL;
-        nProto = 0;
-        memset(&sau, 0, sizeof(sau));
-        sau.sun_family = AF_LOCAL;
-        memcpy(sau.sun_path, cpPath, nPath + 1);
-        sa = (struct sockaddr *)&sau;
-        sl = sizeof(sau);
-#else
-        return NULL;
-#endif
+    /* 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;
     }
-    else if (sa_type == L2_UTIL_SA_IP) {
-        if ((cpProto = va_arg(ap, char *)) == NULL)
-            return NULL;
-        if ((cpHost = va_arg(ap, char *)) == NULL)
-            return NULL;
-        if ((cpPort = va_arg(ap, char *)) == NULL)
-            return NULL;
-
-        /* resolve protocol */
-        if ((pe = getprotobyname(cpProto)) == NULL)
-            return NULL;
-        nProto = pe->p_proto;
-
-        /* resolve port */
-        bNumeric = 1;
-        for (i = 0; cpPort[i] != NUL; i++) {
-            if (!isdigit((int)cpPort[i])) {
-                bNumeric = 0;
-                break;
-            }
-        }
-        if (bNumeric)
-            nPort = atoi(cpPort);
-        else {
-            if ((se = getservbyname(cpPort, cpProto)) == NULL)
-                return NULL;
-            nPort = ntohs(se->s_port);
+    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;
+
+    /* resolve port */
+    bNumeric = 1;
+    for (i = 0; cpPort[i] != '\0'; i++) {
+        if (!isdigit((int)cpPort[i])) {
+            bNumeric = 0;
+            break;
         }
+    }
+    if (bNumeric)
+        nPort = atoi(cpPort);
+    else {
+        if ((se = getservbyname(cpPort, cpProto)) == NULL)
+            return SA_ERR_SYS;
+        nPort = ntohs(se->s_port);
+    }
 
-        /* mandatory 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 */
-        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 */
+    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) {
             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 (inet_pton(AF_INET6, cpHost, &sa6.sin6_addr.s6_addr) == 1) {
+        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
-        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);
-            }
+    }
+    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)->saa_buf = (struct sockaddr *)malloc(sl)) == NULL) {
+        free(*saa);
+        return SA_ERR_MEM;
+    }
+    memcpy((*saa)->saa_buf, sa, sl);
+    (*saa)->saa_len = sl;
+    (*saa)->saa_family = sf;
+    (*saa)->saa_proto = nProto;
+    return SA_OK;
+}
+
+sa_rc_t sa_s2a(sa_addr_t **saa, const struct sockaddr *sabuf, socklen_t salen)
+{
+    struct sockaddr_in *sa4;
 #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);
-            }
+    struct sockaddr_in6 *sa6;
 #endif
-        }
+    struct protoent *pe;
+    int sf;
+
+    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)->saa_buf = (struct sockaddr *)malloc(salen)) == NULL) {
+        free(*saa);
+        return SA_ERR_MEM;
     }
+    memcpy((*saa)->saa_buf, sabuf, salen);
+    (*saa)->saa_len = salen;
+
+    /* fill in family */
+    sf = 0;
+    if (sizeof(struct sockaddr_in) == salen) {
+        sa4 = (struct sockaddr_in *)sabuf;
+        if (sa4->sin_family == AF_INET)
+            sf = AF_INET;
+    }
+#ifdef AF_INET6
+    else if (sizeof(struct sockaddr_in6) == salen) {
+        sa6 = (struct sockaddr_in6 *)sabuf;
+        if (sa6->sin6_family == AF_INET6)
+            sf = AF_INET6;
+    }
+#endif
+    (*saa)->saa_family = sf;
+
+    /* fill in protocol */
+    if ((pe = getprotobyname("tcp")) != NULL)
+        (*saa)->saa_proto = pe->p_proto;
     else
-        return NULL;
-    va_end(ap);
+        (*saa)->saa_proto = 0;
+    return SA_OK;
+}
 
-    if (sa == NULL)
-        return NULL;
-        
-    if ((rc = (l2_util_sa_t *)malloc(sizeof(l2_util_sa_t))) == NULL)
-        return NULL;
-    if ((rc->sa_buf = (struct sockaddr *)malloc(sl)) == NULL) {
-        free(rc);
-        return NULL;
+sa_rc_t sa_a2u(const sa_addr_t *saa, char **uri)
+{
+    char uribuf[1024];
+    struct protoent *pe;
+    struct sockaddr_in *sa4;
+#ifdef AF_INET6
+    struct sockaddr_in6 *sa6;
+#endif
+    char caHost[512];
+    int nPort;
+
+    if (saa == NULL || uri == NULL)
+        return SA_ERR_ARG;
+    if ((pe = getprotobynumber(saa->saa_proto)) == NULL)
+        return SA_ERR_SYS;
+    if (saa->saa_family == AF_INET) {
+        sa4 = (struct sockaddr_in *)saa->saa_buf;
+        inet_ntop(AF_INET, &sa4->sin_addr.s_addr, caHost, sizeof(caHost));
+        nPort = ntohs(sa4->sin_port);
     }
-    memcpy(rc->sa_buf, sa, sl);
-    rc->sa_len = sl;
-    rc->sa_proto = nProto;
+#ifdef AF_INET6
+    else if (saa->saa_family == AF_INET6) {
+        sa6 = (struct sockaddr_in6 *)saa->saa_buf;
+        inet_ntop(AF_INET6, &sa6->sin6_addr.s6_addr, caHost, sizeof(caHost));
+        nPort = ntohs(sa6->sin6_port);
+    }
+#endif
+    else
+        return SA_ERR_ARG;
+    sa_snprintf(uribuf, sizeof(uribuf), "%s://%s:%d", pe->p_name, caHost, nPort);
+    *uri = strdup(uribuf);
+    return SA_OK;
+}
+
+sa_rc_t sa_a2s(const sa_addr_t *saa, struct sockaddr **sabuf, socklen_t *salen)
+{
+    if (saa == NULL || sabuf == NULL || salen == 0)
+        return SA_ERR_ARG;
 
-    return rc;
+    if ((*sabuf = (struct sockaddr *)malloc(saa->saa_len)) == NULL)
+        return SA_ERR_MEM;
+    memmove(*sabuf, saa->saa_buf, saa->saa_len);
+    *salen = saa->saa_len;
+    return SA_OK;
 }
 
-void l2_util_sa_destroy(l2_util_sa_t *sa)
+struct sa_st {
+    int   sSocket;
+    int   bTimeout;
+    struct timeval tvTimeout;
+    int   nReadLen;
+    int   nReadSize;
+    char *cpReadBuf;
+    int   nWriteLen;
+    int   nWriteSize;
+    char *cpWriteBuf;
+};
+
+static sa_rc_t sa_socket_init(sa_t *sa, int family, int proto)
 {
     if (sa == NULL)
-        return;
+        return SA_ERR_ARG;
+    if (sa->sSocket != -1)
+        return SA_ERR_USE;
+    if ((sa->sSocket = socket(family, SOCK_STREAM, proto)) == -1)
+        return SA_ERR_SYS;
+    return SA_OK;
+}
+
+static sa_rc_t sa_socket_kill(sa_t *sa)
+{
+    if (sa == NULL)
+        return SA_ERR_ARG;
+    if (sa->sSocket == -1) {
+        close(sa->sSocket);
+        sa->sSocket = -1;
+    }
+    return SA_OK;
+}
+
+sa_rc_t sa_create(sa_t **sap)
+{
+    sa_t *sa;
 
-    if (sa->sa_buf != NULL)
-        free(sa->sa_buf);
+    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;
+    }
+    *sap = sa;
+    return SA_OK;
+}
+
+sa_rc_t sa_destroy(sa_t *sa)
+{
+    if (sa == NULL)
+        return SA_ERR_ARG;
+    sa_socket_kill(sa);
+    if (sa->cpReadBuf != NULL)
+        free(sa->cpReadBuf);
+    if (sa->cpWriteBuf != NULL)
+        free(sa->cpWriteBuf);
     free(sa);
+    return SA_OK;
+}
+
+sa_rc_t sa_timeout(sa_t *sa, long sec, long usec)
+{
+    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;
+    }
+    return SA_OK;
+}
+
+sa_rc_t sa_buffers(sa_t *sa, size_t rsize, size_t wsize)
+{
+    char *cp;
+
+    if (sa == NULL)
+        return SA_ERR_ARG;
+    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_OK;
+}
+
+sa_rc_t sa_bind(sa_t *sa, sa_addr_t *laddr)
+{
+    sa_rc_t rv;
+
+    if (sa->sSocket == -1)
+        if ((rv = sa_socket_init(sa, laddr->saa_family, laddr->saa_proto)) != SA_OK)
+            return rv;
+    /* NOT YET IMPLEMENTED */
+    return SA_ERR_INT;
+}
+
+sa_rc_t sa_connect(sa_t *sa, sa_addr_t *raddr)
+{
+    int flags, n, error;
+    fd_set rset, wset;
+    socklen_t len;
+    sa_rc_t rv;
+
+    if (sa == NULL)
+        return SA_ERR_ARG;
+
+    if (sa->sSocket == -1)
+        if ((rv = sa_socket_init(sa, raddr->saa_family, raddr->saa_proto)) != SA_OK)
+            return rv;
+
+    if (!sa->bTimeout)
+        return connect(sa->sSocket, raddr->saa_buf, raddr->saa_len);
+
+    error = 0;
+    rv = SA_OK;
+
+    /* remember socket flags */
+    flags = fcntl(sa->sSocket, F_GETFL, 0);
+
+    /* switch to non-blocking mode */
+    fcntl(sa->sSocket, F_SETFL, flags|O_NONBLOCK);
+
+    /* perform the connect */
+    if ((n = connect(sa->sSocket, raddr->saa_buf, raddr->saa_len)) < 0) {
+        if (errno != EINPROGRESS) {
+            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;
+    }
+
+    /* fetch pending error */
+    len = sizeof(error);
+    if (getsockopt(sa->sSocket, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
+        error = errno;
+
+    done:
+
+    /* reset socket flags */
+    fcntl(sa->sSocket, F_SETFL, flags);
+
+    /* optionally set errno */
+    if (error != 0) {
+        errno = error;
+        rv = SA_ERR_SYS;
+    }
+
+    return rv;
+}
+
+sa_rc_t sa_listen(sa_t *sa)
+{
+    /* FIXME: NOT YET IMPLEMENTED */
+    return SA_ERR_INT;
+}
+
+sa_rc_t sa_accept(sa_t *sa, sa_t **sa_new)
+{
+    /* FIXME: NOT YET IMPLEMENTED */
+    return SA_ERR_INT;
+}
+
+static int sa_read_raw(sa_t *sa, char *cpBuf, int nBufLen)
+{
+    int rv;
+    fd_set fds;
+
+    if (sa->bTimeout) {
+        FD_ZERO(&fds);
+        FD_SET(sa->sSocket, &fds);
+        do {
+            rv = select(sa->sSocket+1, &fds, NULL, NULL, &sa->tvTimeout);
+        } while (rv == -1 && errno == EINTR);
+        if (rv == 0) {
+            errno = ETIMEDOUT;
+            return -1;
+        }
+    }
+    do {
+        rv = read(sa->sSocket, cpBuf, nBufLen);
+    } while (rv == -1 && errno == EINTR);
+    return rv;
+}
+
+sa_rc_t sa_read(sa_t *sa, char *cpBuf, size_t nBufReq, size_t *nBufRes)
+{
+    int n;
+    int rv;
+
+    if (sa == NULL)
+        return 0;
+    
+    /* flush write buffer */
+    if (sa->nWriteLen > 0)
+        sa_flush(sa);
+    rv = SA_OK;
+    *nBufRes = 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;
+            *nBufRes += 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;
+                *nBufRes += 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)
+                    *nBufRes += n;
+                else if (n <= 0)
+                    rv = SA_ERR_SYS;
+            }
+            else {
+                /* fill buffer with new data */
+                n = sa_read_raw(sa, sa->cpReadBuf, sa->nReadSize);
+                if (n <= 0)
+                    rv = SA_ERR_SYS;
+                else {
+                    sa->nReadLen = n;
+                    continue;
+                }
+            }
+        }
+        break;
+    }
+    return rv;
+}
+
+sa_rc_t sa_readfrom(sa_t *sa, char *buf, size_t buflen, size_t *bufdone, sa_addr_t *addr)
+{
+    sa_rc_t rv;
+
+    if (sa->sSocket == -1)
+        if ((rv = sa_socket_init(sa, addr->saa_family, addr->saa_proto)) != SA_OK)
+            return rv;
+    /* FIXME: NOT YET IMPLEMENTED */
+    return SA_ERR_INT;
+}
+
+sa_rc_t sa_readline(sa_t *sa, char *cpBuf, size_t nBufReq, size_t *nBufRes)
+{
+    char c;
+    size_t n;
+
+    if (sa == NULL)
+        return SA_ERR_ARG;
+    *nBufRes = 0;
+    while ((*nBufRes) < (nBufReq-1)) {
+        sa_read(sa, &c, 1, &n);
+        if (n <= 0)
+            break;
+        cpBuf[(*nBufRes)++] = c;
+        if (c == '\n')
+            break;
+    }
+    cpBuf[(*nBufRes)] = '\0';
+    return SA_OK;
+}
+
+static int sa_write_raw(sa_t *sa, const char *cpBuf, int nBufLen)
+{
+    int rv;
+    fd_set fds;
+
+    if (sa->bTimeout) {
+        FD_ZERO(&fds);
+        FD_SET(sa->sSocket, &fds);
+        do {
+            rv = select(sa->sSocket+1, NULL, &fds, NULL, &sa->tvTimeout);
+        } while (rv == -1 && errno == EINTR);
+        if (rv == 0) {
+            errno = ETIMEDOUT;
+            return -1;
+        }
+    }
+    do {
+        rv = write(sa->sSocket, cpBuf, nBufLen);
+    } while (rv == -1 && errno == EINTR);
+    return rv;
+}
+
+sa_rc_t sa_write(sa_t *sa, const char *cpBuf, size_t nBufReq, size_t *nBufRes)
+{
+    size_t n;
+
+    if (sa == NULL)
+        return SA_ERR_ARG;
+
+    if (nBufReq > (sa->nWriteSize - sa->nWriteLen)) {
+        /* not enough space in buffer, so flush buffer */
+        sa_flush(sa);
+    }
+    if (nBufReq >= sa->nWriteSize) {
+        /* buffer too small at all, so write immediately */
+        *nBufRes = 0;
+        while (nBufReq > 0) {
+            n = sa_write_raw(sa, cpBuf, nBufReq);
+            if (n <= 0)
+                break;
+            nBufReq  -= n;
+            cpBuf    += n;
+            *nBufRes += n;
+        }
+    }
+    else {
+        /* (again) enough sprace in buffer, so store data */
+        memmove(sa->cpWriteBuf+sa->nWriteLen, cpBuf, nBufReq);
+        sa->nWriteLen += nBufReq;
+        *nBufRes = nBufReq;
+    }
+    return SA_OK;
+}
+
+sa_rc_t sa_writeto(sa_t *sa, const char *buf, size_t buflen, size_t *bufdone, sa_addr_t *addr)
+{
+    sa_rc_t rv;
+
+    if (sa->sSocket == -1)
+        if ((rv = sa_socket_init(sa, addr->saa_family, addr->saa_proto)) != SA_OK)
+            return rv;
+    /* FIXME: NOT YET IMPLEMENTED */
+    return SA_ERR_INT;
+}
+
+sa_rc_t sa_printf(sa_t *sa, const char *cpFmt, ...)
+{
+    va_list ap;
+    size_t n;
+    char caBuf[1024];
+
+    if (sa == NULL)
+        return SA_ERR_ARG;
+    va_start(ap, cpFmt);
+    n = sa_vsnprintf(caBuf, sizeof(caBuf), cpFmt, ap);
+    sa_write(sa, caBuf, n, &n);
+    va_end(ap);
+    return SA_OK;
+}
+
+sa_rc_t sa_flush(sa_t *sa)
+{
+    size_t n;
+
+    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;
+    }
+    sa->nWriteLen = 0;
+    return SA_OK;
+}
+
+sa_rc_t sa_shutdown(sa_t *sa, char *flags)
+{
+    int how;
 
-    return;
+    if (sa == NULL || flags == NULL)
+        return SA_ERR_ARG;
+    how = 0;
+    if (strcmp(flags, "r") == 0)
+        how = SHUT_RD;
+    else if (strcmp(flags, "w") == 0)
+        how = SHUT_WR;
+    else if (strcmp(flags, "rw") == 0)
+        how = SHUT_RDWR;
+    if (shutdown(sa->sSocket, how) == -1)
+        return SA_ERR_SYS;
+    return SA_OK;
 }
 

CVSTrac 2.0.1