ossp-pkg/sa/sa.c
1.1.1.1
/*
** 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 {
struct sockaddr *saBuf;
socklen_t slBuf;
int nFamily;
int nProto;
};
/* socket abstraction object */
struct sa_st {
int sSocket;
int bTimeout;
struct timeval tvTimeout;
int nReadLen;
int nReadSize;
char *cpReadBuf;
int nWriteLen;
int nWriteSize;
char *cpWriteBuf;
};
/* 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
/* make sure vsnprintf() exists */
#if defined(HAVE_VSNPRINTF)
#define sa_vsnprintf vsnprintf
#elif defined(HAVE_VSPRINTF)
static int sa_vsnprintf(char *str, size_t size, const char *fmt, va_list ap)
{
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
/* make sure snprintf() exists */
#if defined(HAVE_SNPRINTF)
#define sa_snprintf snprintf
#else
static int sa_snprintf(char *str, size_t size, const char *fmt, ...)
{
va_list ap;
int rv;
va_start(ap, fmt);
rv = sa_vsnprintf(str, size, fmt, ap);
va_end(ap);
return rv;
}
#endif
sa_rc_t sa_u2a(sa_addr_t **saa, const char *uri, ...)
{
va_list ap;
int nPort;
socklen_t sl;
struct sockaddr *sa;
struct sockaddr_in sa4;
#ifdef AF_INET6
struct sockaddr_in6 sa6;
#endif
struct hostent *he;
struct servent *se;
struct protoent *pe;
int bNumeric;
int i;
char *cpProto;
int nProto;
char *cpHost;
char *cpPort;
char 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_vsnprintf(uribuf, sizeof(uribuf), uri, ap);
va_end(ap);
/* parse URI into protocol, host and port parts */
uri = uribuf;
cpProto = "tcp";
if ((cp = strstr(uri, "://")) != NULL) {
cpProto = (char *)uri;
*cp = '\0';
uri = cp+3;
}
cpHost = (char *)uri;
if ((cp = strchr(uri, ':')) == NULL)
return SA_ERR_ARG;
*cp++ = '\0';
cpPort = cp;
/* resolve protocol */
if ((pe = getprotobyname(cpProto)) == NULL)
return SA_ERR_SYS;
nProto = pe->p_proto;
/* 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(!) socket address structure initialization */
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 (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 = (sa_addr_t *)malloc(sizeof(sa_addr_t))) == NULL)
return SA_ERR_MEM;
if (((*saa)->saBuf = (struct sockaddr *)malloc(sl)) == NULL) {
free(*saa);
return SA_ERR_MEM;
}
memcpy((*saa)->saBuf, sa, sl);
(*saa)->slBuf = sl;
(*saa)->nFamily = sf;
(*saa)->nProto = 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
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)->saBuf = (struct sockaddr *)malloc(salen)) == NULL) {
free(*saa);
return SA_ERR_MEM;
}
memcpy((*saa)->saBuf, sabuf, salen);
(*saa)->slBuf = salen;
/* fill in family */
sf = 0;
if (sizeof(struct sockaddr_in) == salen) {
sa4 = (struct sockaddr_in *)((void *)sabuf);
if (sa4->sin_family == AF_INET)
sf = AF_INET;
}
#ifdef AF_INET6
else if (sizeof(struct sockaddr_in6) == salen) {
sa6 = (struct sockaddr_in6 *)((void *)sabuf);
if (sa6->sin6_family == AF_INET6)
sf = AF_INET6;
}
#endif
(*saa)->nFamily = sf;
/* fill in protocol */
if ((pe = getprotobyname("tcp")) != NULL)
(*saa)->nProto = pe->p_proto;
else
(*saa)->nProto = 0;
return SA_OK;
}
sa_rc_t sa_a2u(const sa_addr_t *saa, char **uri)
{
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->nProto)) == NULL)
return SA_ERR_SYS;
if (saa->nFamily == AF_INET) {
sa4 = (struct sockaddr_in *)((void *)saa->saBuf);
inet_ntop(AF_INET, &sa4->sin_addr.s_addr, caHost, sizeof(caHost));
nPort = ntohs(sa4->sin_port);
}
#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_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;
if ((*sabuf = (struct sockaddr *)malloc(saa->slBuf)) == NULL)
return SA_ERR_MEM;
memmove(*sabuf, saa->saBuf, saa->slBuf);
*salen = saa->slBuf;
return SA_OK;
}
static sa_rc_t sa_socket_init(sa_t *sa, int family, int proto)
{
int type;
if (sa == NULL)
return SA_ERR_ARG;
if (proto == IPPROTO_TCP)
type = SOCK_STREAM;
else if (proto == IPPROTO_UDP)
type = SOCK_DGRAM;
else
return SA_ERR_ARG;
if (sa->sSocket != -1)
return SA_ERR_USE;
if ((sa->sSocket = socket(family, type, 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_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 == NULL || laddr == NULL)
return SA_ERR_ARG;
if (sa->sSocket == -1)
if ((rv = sa_socket_init(sa, laddr->nFamily, laddr->nProto)) != SA_OK)
return rv;
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->sSocket == -1)
if ((rv = sa_socket_init(sa, raddr->nFamily, raddr->nProto)) != SA_OK)
return rv;
if (!sa->bTimeout)
return connect(sa->sSocket, raddr->saBuf, raddr->slBuf);
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->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->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_s2a(caddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK)
return rv;
if ((rv = sa_create(csa)) != SA_OK) {
free(*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 (getpeername(sa->sSocket, (struct sockaddr *)&sa_buf, &sa_len) < 0)
return SA_ERR_SYS;
if ((rv = sa_s2a(raddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK)
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;
sa_len = sizeof(sa_buf);
if (getsockname(sa->sSocket, (struct sockaddr *)&sa_buf, &sa_len) < 0)
return SA_ERR_SYS;
if ((rv = sa_s2a(laddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK)
return rv;
return SA_OK;
}
sa_rc_t sa_getfd(sa_t *sa, int *fd)
{
if (sa == NULL || fd == NULL)
return SA_ERR_ARG;
*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;
int rv;
size_t res;
if (sa == NULL || cpBuf == NULL || nBufReq == 0)
return 0;
/* flush write buffer */
if (sa->nWriteLen > 0)
sa_flush(sa);
rv = SA_OK;
res = 0;
while (1) {
if (nBufReq <= sa->nReadLen) {
/* buffer holds enough data, so use this */
memmove(cpBuf, sa->cpReadBuf, nBufReq);
memmove(sa->cpReadBuf, sa->cpReadBuf+nBufReq, sa->nReadLen-nBufReq);
sa->nReadLen -= nBufReq;
res += nBufReq;
}
else {
if (sa->nReadLen > 0) {
/* fetch already existing buffer contents as a start */
memmove(cpBuf, sa->cpReadBuf, sa->nReadLen);
nBufReq -= sa->nReadLen;
cpBuf += sa->nReadLen;
res += sa->nReadLen;
sa->nReadLen = 0;
}
if (nBufReq >= sa->nReadSize) {
/* buffer is too small at all, so read directly */
n = sa_read_raw(sa, cpBuf, nBufReq);
if (n > 0)
res += n;
else if (n <= 0)
rv = SA_ERR_SYS;
}
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->bTimeout) {
FD_ZERO(&fds);
FD_SET(sa->sSocket, &fds);
do {
n = select(sa->sSocket+1, &fds, NULL, NULL, &sa->tvTimeout);
} while (n == -1 && errno == EINTR);
if (n == 0)
errno = ETIMEDOUT;
if (n <= 0)
return SA_ERR_SYS;
}
sa_len = sizeof(sa_buf);
if ((n = recvfrom(sa->sSocket, buf, buflen, 0, (struct sockaddr *)&sa_buf, &sa_len)) == -1)
return SA_ERR_SYS;
if ((rv = sa_s2a(raddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK)
return rv;
if (bufdone != NULL)
*bufdone = n;
return SA_OK;
}
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;
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;
if (sa == NULL)
return SA_ERR_ARG;
if (nBufReq > (sa->nWriteSize - sa->nWriteLen)) {
/* not enough space in buffer, so flush buffer */
sa_flush(sa);
}
res = 0;
if (nBufReq >= sa->nWriteSize) {
/* buffer too small at all, so write immediately */
while (nBufReq > 0) {
n = sa_write_raw(sa, cpBuf, nBufReq);
if (n <= 0)
break;
nBufReq -= n;
cpBuf += n;
res += n;
}
}
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 SA_OK;
}
sa_rc_t sa_writeto(sa_t *sa, const char *buf, size_t buflen, size_t *bufdone, sa_addr_t *raddr)
{
size_t n;
fd_set fds;
if (sa == NULL || buf == NULL || buflen == 0 || raddr == NULL)
return SA_ERR_ARG;
if (sa->bTimeout) {
FD_ZERO(&fds);
FD_SET(sa->sSocket, &fds);
do {
n = select(sa->sSocket+1, NULL, &fds, NULL, &sa->tvTimeout);
} while (n == -1 && errno == EINTR);
if (n == 0)
errno = ETIMEDOUT;
if (n <= 0)
return SA_ERR_SYS;
}
if ((n = sendto(sa->sSocket, buf, buflen, 0, raddr->saBuf, raddr->slBuf)) == -1)
return SA_ERR_SYS;
if (bufdone != NULL)
*bufdone = n;
return SA_OK;
}
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;
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;
}