/* ** 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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 /* 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); sprintf(ibuf, "%d", d); 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_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_mvsnprintf(uribuf, sizeof(uribuf), uri, ap); va_end(ap); /* parse URI into protocol, host and port parts */ uri = uribuf; cpProto = "tcp"; if ((cp = strstr(uri, "://")) != NULL) { cpProto = (char *)uri; *cp = '\0'; uri = cp+3; } cpHost = (char *)uri; if ((cp = strchr(uri, ':')) == NULL) return SA_ERR_ARG; *cp++ = '\0'; cpPort = cp; /* resolve protocol */ if ((pe = getprotobyname(cpProto)) == NULL) return SA_ERR_SYS; nProto = pe->p_proto; /* 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_msnprintf(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_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; 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_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; 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; }