OSSP CVS Repository

ossp - ossp-pkg/sa/sa.c 1.7
Not logged in
[Honeypot]  [Browse]  [Directory]  [Home]  [Login
[Reports]  [Search]  [Ticket]  [Timeline
  [Raw

ossp-pkg/sa/sa.c 1.7
/*
**  SA - OSSP Socket Abstraction Library
**  Copyright (c) 2001 The OSSP Project (http://www.ossp.org/)
**  Copyright (c) 2001 Cable & Wireless Deutschland (http://www.cw.com/de/)
**
**  This file is part of OSSP SA, a socket abstraction library which
**  can be found at http://www.ossp.org/pkg/sa/.
**
**  Permission to use, copy, modify, and distribute this software for
**  any purpose with or without fee is hereby granted, provided that
**  the above copyright notice and this permission notice appear in all
**  copies.
**
**  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
**  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
**  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
**  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
**  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
**  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
**  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
**  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
**  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
**  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
**  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
**  SUCH DAMAGE.
**
**  sa.c: socket abstraction library
*/

/* include optional Autoconf header */
#ifdef HAVE_CONFIG_H
#include "config.h"
#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 own API header */
#include "sa.h"

/* socket address abstraction object */
struct sa_addr_st {
    int              nFamily;
    struct sockaddr *saBuf;
    socklen_t        slBuf;
};

/* 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;
};

/* make sure AF_LOCAL define exists */
#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)
{
    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;
    }
    errno = EAFNOSUPPORT;
    return -1;
}
#else
#error "neither inet_pton nor inet_aton available"
#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)
{
    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

/* minimal vsnprintf(3) */
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 */
    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 == '%')
                *bufptr++ = c;
            else if (c == 'c')
                *bufptr++ = (char)va_arg(ap, int);
            else if (c == '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') {
                d = (int)va_arg(ap, int);
#ifdef HAVE_SNPRINTF
                snprintf(ibuf, sizeof(ibuf), "%d", d); /* inherently secure */
#else
                sprintf(ibuf, "%d", d); /* nevertheless secure; see ibuf above */
#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);
}

/* minimal snprintf(3) */
static int sa_msnprintf(char *buffer, size_t bufsize, const char *format, ...)
{
    int chars;
    va_list ap;

    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)
{
    if (saa == NULL)
        return SA_ERR_ARG;
    if ((*saa = (sa_addr_t *)malloc(sizeof(sa_addr_t))) == NULL)
        return SA_ERR_MEM;
    (*saa)->saBuf   = NULL;
    (*saa)->slBuf   = 0;
    (*saa)->nFamily = 0;
    return SA_OK;
}

sa_rc_t sa_addr_destroy(sa_addr_t *saa)
{
    if (saa == NULL)
        return SA_ERR_ARG;
    if (saa->saBuf != NULL)
        free(saa->saBuf);
    free(saa);
    return SA_OK;
}

sa_rc_t sa_addr_u2a(sa_addr_t *saa, const char *uri, ...)
{
    va_list ap;
    int nPort;
    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;
    int bNumeric;
    int i;
    int n;
    char *cpProto;
    char *cpHost;
    char *cpPort;
    char *cpPath;
    char uribuf[1024];
    char *cp;
    int sf;

    /* argument sanity check */
    if (saa == NULL || uri == NULL)
        return SA_ERR_ARG;

    /* on-the-fly create or just take over URI */
    va_start(ap, uri);
    sa_mvsnprintf(uribuf, sizeof(uribuf), uri, ap);
    va_end(ap);

    /* parse URI into parts */
    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)
            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) {
        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(!) socket address structure initialization */
    memset(&un,  0, sizeof(un));
    memset(&sa4, 0, sizeof(sa4));
#ifdef AF_INET6
    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) {
            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 (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->saBuf = (struct sockaddr *)malloc(sl)) == NULL)
        return SA_ERR_MEM;
    memcpy(saa->saBuf, sa, sl);
    saa->slBuf = sl;
    saa->nFamily = sf;
    return SA_OK;
}

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
    int sf;

    if (saa == NULL || sabuf == NULL || salen == 0)
        return SA_ERR_ARG;

    /* create result address structure */
    if ((saa->saBuf = (struct sockaddr *)malloc(salen)) == NULL)
        return SA_ERR_MEM;
    memcpy(saa->saBuf, sabuf, salen);
    saa->slBuf = salen;

    /* fill in family */
    sf = 0;
    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
    if (sf == 0 && sizeof(struct sockaddr_in6) == salen) {
        sa6 = (struct sockaddr_in6 *)((void *)sabuf);
        if (sa6->sin6_family == AF_INET6)
            sf = AF_INET6;
    }
#endif
    if (sf == 0)
        return SA_ERR_ARG;
    saa->nFamily = sf;

    return SA_OK;
}

sa_rc_t sa_addr_a2u(sa_addr_t *saa, char **uri)
{
    char uribuf[1024];
    struct sockaddr_un *un;
    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 (saa->nFamily == AF_LOCAL) {
        un = (struct sockaddr_un *)((void *)saa->saBuf);
        sa_msnprintf(uribuf, sizeof(uribuf), "unix:%s", un->sun_path);
    }
    else {
        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);
        }
#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);
        }
#endif
        else
            return SA_ERR_ARG;
        sa_msnprintf(uribuf, sizeof(uribuf), "inet://%s:%d", caHost, nPort);
    }
    *uri = strdup(uribuf);
    return SA_OK;
}

sa_rc_t sa_addr_a2s(sa_addr_t *saa, struct sockaddr **sabuf, socklen_t *salen)
{
    if (saa == NULL || sabuf == NULL || salen == 0)
        return SA_ERR_ARG;

    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 nFamily)
{
    int nType;
    int nProto;
    struct protoent *pe;
    char *cpProto;

    if (sa == NULL)
        return SA_ERR_ARG;

    /* only perform operation if socket still do not exists */
    if (sa->sSocket != -1)
        return SA_ERR_USE;

    /* 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_ARG;

    /* 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_ARG;

    /* create the socket */
    if ((sa->sSocket = socket(nFamily, nType, nProto)) == -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_t *)malloc(sizeof(sa_t))) == NULL)
        return SA_ERR_MEM;
    sa->eType      = SA_TYPE_STREAM;
    sa->sSocket    = -1;
    sa->bTimeout   = 0;
    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;
    *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_type(sa_t *sa, sa_type_t type)
{
    if (sa == NULL)
        return SA_ERR_ARG;
    if (type != SA_TYPE_STREAM && type != SA_TYPE_DATAGRAM)
        return SA_ERR_ARG;
    sa->eType = type;
    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 (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;
    }
    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;
}

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;
}

sa_rc_t sa_bind(sa_t *sa, sa_addr_t *laddr)
{
    sa_rc_t rv;
    struct sockaddr_un *un;

    if (sa == NULL || laddr == NULL)
        return SA_ERR_ARG;
    if (sa->sSocket == -1)
        if ((rv = sa_socket_init(sa, laddr->nFamily)) != SA_OK)
            return rv;
    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)
        return SA_ERR_SYS;
    return SA_OK;
}

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->eType != SA_TYPE_STREAM)
        return SA_ERR_USE;
    if (sa->sSocket == -1)
        if ((rv = sa_socket_init(sa, raddr->nFamily)) != SA_OK)
            return rv;

    if (!sa->bTimeout) {
        if (connect(sa->sSocket, raddr->saBuf, raddr->slBuf) < 0)
            return SA_ERR_SYS;
        else
            return SA_OK;
    }

    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->saBuf, raddr->slBuf)) < 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, int backlog)
{
    if (sa == NULL)
        return SA_ERR_ARG;
    if (sa->eType != SA_TYPE_STREAM)
        return SA_ERR_USE;
    if (sa->sSocket == -1)
        /* at least sa_bind() has to be already performed */
        return SA_ERR_USE;
    if (listen(sa->sSocket, backlog) == -1)
        return SA_ERR_SYS;
    return SA_OK;
}

sa_rc_t sa_accept(sa_t *sa, sa_addr_t **caddr, sa_t **csa)
{
    sa_rc_t rv;
    int n;
    fd_set fds;
    union {
        struct sockaddr_in sa4;
#ifdef AF_INET6
        struct sockaddr_in6 sa6;
#endif
    } sa_buf;
    socklen_t sa_len;
    int s;

    if (sa == NULL)
        return SA_ERR_ARG;
    if (sa->eType != SA_TYPE_STREAM)
        return SA_ERR_USE;
    if (sa->sSocket == -1)
        /* at least sa_listen() has to be already performed */
        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 ((s = accept(sa->sSocket, (struct sockaddr *)&sa_buf, &sa_len)) == -1)
        return SA_ERR_SYS;
    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;
    }
    if ((rv = sa_create(csa)) != SA_OK) {
        sa_addr_destroy(*caddr);
        return rv;
    }
    (*csa)->sSocket = s;
    return SA_OK;
}

sa_rc_t sa_getremote(sa_t *sa, 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;

    if (sa == NULL)
        return SA_ERR_ARG;
    sa_len = sizeof(sa_buf);
    if (sa->sSocket == -1)
        return SA_ERR_USE;
    if (getpeername(sa->sSocket, (struct sockaddr *)&sa_buf, &sa_len) < 0)
        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;
    }
    return SA_OK;
}

sa_rc_t sa_getlocal(sa_t *sa, sa_addr_t **laddr)
{
    sa_rc_t rv;
    union {
        struct sockaddr_in sa4;
#ifdef AF_INET6
        struct sockaddr_in6 sa6;
#endif
    } sa_buf;
    socklen_t sa_len;

    if (sa == NULL)
        return SA_ERR_ARG;
    if (sa->sSocket == -1)
        return SA_ERR_USE;
    sa_len = sizeof(sa_buf);
    if (getsockname(sa->sSocket, (struct sockaddr *)&sa_buf, &sa_len) < 0)
        return SA_ERR_SYS;
    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;
}

sa_rc_t sa_getfd(sa_t *sa, int *fd)
{
    if (sa == NULL || fd == NULL)
        return SA_ERR_ARG;
    if (sa->sSocket == -1)
        return SA_ERR_USE;
    *fd = sa->sSocket;
    return SA_OK;
}

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;
    sa_rc_t rv;
    size_t res;

    if (sa == NULL || cpBuf == NULL || nBufReq == 0)
        return SA_ERR_ARG;
    if (sa->eType != SA_TYPE_STREAM)
        return SA_ERR_USE;
    if (sa->sSocket == -1)
        return SA_ERR_USE;

    /* flush write buffer */
    if (sa->nWriteLen > 0)
        sa_flush(sa);

    /* perform read operation */
    rv = SA_OK;
    if (sa->nReadSize == 0) {
        /* unbuffered I/O */
        res = sa_read_raw(sa, cpBuf, nBufReq);
        if (res <= 0)
            rv = SA_ERR_SYS;
    }
    else {
        /* buffered I/O */
        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;
                }
                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;
        }
    }
    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;
}

sa_rc_t sa_readline(sa_t *sa, char *cpBuf, size_t nBufReq, size_t *nBufRes)
{
    char c;
    size_t n;
    size_t res;

    if (sa == NULL)
        return SA_ERR_ARG;
    if (sa->eType != SA_TYPE_STREAM)
        return SA_ERR_USE;
    if (sa->sSocket == -1)
        return SA_ERR_USE;
    res = 0;
    while (res < (nBufReq-1)) {
        sa_read(sa, &c, 1, &n);
        if (n <= 0)
            break;
        cpBuf[res++] = c;
        if (c == '\n')
            break;
    }
    cpBuf[res] = '\0';
    if (nBufRes != NULL)
        *nBufRes = res;
    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;
    size_t res;
    sa_rc_t rv;

    if (sa == NULL)
        return SA_ERR_ARG;
    if (sa->eType != SA_TYPE_STREAM)
        return SA_ERR_USE;
    if (sa->sSocket == -1)
        return SA_ERR_USE;

    rv = SA_OK;
    if (sa->nWriteSize == 0) {
        /* unbuffered I/O */
        res = sa_write_raw(sa, cpBuf, nBufReq);
        if (res < 0) {
            res = 0;
            rv = SA_ERR_SYS;
        }
    }
    else {
        /* buffered I/O */
        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)
                    rv = SA_ERR_SYS;
                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;
        }
    }
    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)
{
    size_t n;
    fd_set fds;
    sa_rc_t rv;

    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)
        if ((rv = sa_socket_init(sa, raddr->nFamily)) != SA_OK)
            return rv;
    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, 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];

    if (sa == NULL)
        return SA_ERR_ARG;
    va_start(ap, cpFmt);
    n = sa_mvsnprintf(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;
    if (sa->eType != SA_TYPE_STREAM)
        return SA_ERR_USE;
    if (sa->sSocket == -1)
        return SA_ERR_USE;
    if (sa->nWriteSize > 0) {
        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;

    if (sa == NULL || flags == NULL)
        return SA_ERR_ARG;
    if (sa->eType != SA_TYPE_STREAM)
        return SA_ERR_USE;
    if (sa->sSocket == -1)
        return SA_ERR_USE;
    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