Index: ossp-pkg/sa/TODO RCS File: /v/ossp/cvs/ossp-pkg/sa/TODO,v rcsdiff -q -kk '-r1.6' '-r1.7' -u '/v/ossp/cvs/ossp-pkg/sa/TODO,v' 2>/dev/null --- TODO 2001/10/05 13:18:18 1.6 +++ TODO 2001/10/05 20:52:15 1.7 @@ -0,0 +1,4 @@ + +ETIMEDOUT -> SA_ERR_TIM; +1025 buffer von writef in sa_t aufnehmen und variablen machen! + Index: ossp-pkg/sa/sa.c RCS File: /v/ossp/cvs/ossp-pkg/sa/sa.c,v rcsdiff -q -kk '-r1.7' '-r1.8' -u '/v/ossp/cvs/ossp-pkg/sa/sa.c,v' 2>/dev/null --- sa.c 2001/10/05 13:19:30 1.7 +++ sa.c 2001/10/05 20:52:15 1.8 @@ -33,108 +33,114 @@ #endif /* include system API headers */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include /* for "s[n]printf()" */ +#include /* for "malloc()" & friends */ +#include /* for "va_xxx()" and "va_list" */ +#include /* for "strxxx()" and "size_t" */ +#include /* for general prerequisites */ +#include /* for isxxx() */ +#include /* for "EXXX" */ +#include /* for "F_XXX" and "O_XXX" */ +#include /* for standard Unix stuff */ +#include /* for "struct prototent" */ +#include /* for "struct timeval" */ +#include /* for "struct sockaddr_un" */ +#include /* for "struct sockaddr_in[6]" */ +#include /* for "AF_XXX" and "SOCK_XXX" */ +#include /* for "inet_xtoy" */ /* include own API header */ #include "sa.h" /* socket address abstraction object */ struct sa_addr_st { - int nFamily; - struct sockaddr *saBuf; - socklen_t slBuf; + int nFamily; /* the socket family (AF_XXX) */ + struct sockaddr *saBuf; /* the "struct sockaddr_xx" actually */ + socklen_t slBuf; /* the length of "struct sockaddr_xx" */ }; /* socket abstraction object */ struct sa_st { - sa_type_t eType; - int sSocket; - int bTimeout; - struct timeval tvTimeout; - int nReadLen; - int nReadSize; - char *cpReadBuf; - int nWriteLen; - int nWriteSize; - char *cpWriteBuf; + sa_type_t eType; /* socket type (stream or datagram) */ + int fdSocket; /* socket file descriptor */ + int bTimeout; /* timeout enabling flag */ + struct timeval tvTimeout; /* timeout value (sec, usec) */ + int nReadLen; /* read buffer current length */ + int nReadSize; /* read buffer current size */ + char *cpReadBuf; /* read buffer memory chunk */ + int nWriteLen; /* write buffer current length */ + int nWriteSize; /* write buffer current size */ + char *cpWriteBuf; /* write buffer memory chunk */ }; -/* make sure AF_LOCAL define exists */ +/* boolean values */ +#ifndef FALSE +#define FALSE (0) +#endif +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +/* backward compatibility for AF_LOCAL */ #if !defined(AF_LOCAL) && defined(AF_UNIX) #define AF_LOCAL AF_UNIX #endif -/* make sure inet_pton() exists */ -#if defined(HAVE_INET_PTON) -#define sa_inet_pton inet_pton -#elif defined(HAVE_INET_ATON) -static int inet_pton(int family, const char *strptr, void *addrptr) +/* convert Internet address from presentation to network format */ +static int sa_inet_pton(int family, const char *strptr, void *addrptr) { +#ifdef HAVE_INET_PTON + return inet_pton(family, strptr, addrptr); +#else struct in_addr in_val; if (family == AF_INET) { - if (inet_aton(strptr, &in_val)) { - memcpy(addrptr, &in_val, sizeof(struct in_addr)); - return 1; - } - return 0; + /* at least for IPv4 we can rely on the old inet_aton(3) + and for IPv6 inet_pton(3) would exist anyway */ + if (inet_aton(strptr, &in_val) == 0) + return 0; + memcpy(addrptr, &in_val, sizeof(struct in_addr)); + return 1; } errno = EAFNOSUPPORT; - return -1; -} -#else -#error "neither inet_pton nor inet_aton available" + return 0; #endif +} -/* make sure inet_ntop() exists */ -#if defined(HAVE_INET_NTOP) -#define sa_inet_ntop inet_ntop -#elif defined(HAVE_INET_NTOA) -static char *inet_ntop(int family, const void *src, char *dst, size_t size) +/* convert Internet address from network to presentation format */ +static const char *sa_inet_ntop(int family, const void *src, char *dst, size_t size) { +#ifdef HAVE_INET_NTOP + return inet_ntop(family, src, dst, size); +#else struct in_addr in_val; char *cp; int n; if (family == AF_INET) { - if ((cp = inet_ntoa(src)) != NULL) { - n = strlen(cp); - if (n > size-1) - n = size-1; - memcpy(dst, cp, n); - dst[n] = '\0'; - return 1; - } - return 0; + /* at least for IPv4 we can rely on the old inet_ntoa(3) + and for IPv6 inet_ntop(3) would exist anyway */ + if ((cp = inet_ntoa(src)) == NULL) + return NULL; + n = strlen(cp); + if (n > size-1) + n = size-1; + memcpy(dst, cp, n); + dst[n] = '\0'; + return dst; } errno = EAFNOSUPPORT; - return -1; -} -#else -#error "neither inet_ntop nor inet_ntoa available" + return NULL; #endif +} -/* minimal vsnprintf(3) */ +/* minimal vsnprintf(3) variant which supports %{c,s,d} only */ static int sa_mvsnprintf(char *buffer, size_t bufsize, const char *format, va_list ap) { char *bufptr; char *bufend; - char ibuf[((sizeof(int)*8)/3)+10]; /* (available bits) x log_10(2) + safety */ + /* sufficient integer buffer: x log_10(2) + safety */ + char ibuf[((sizeof(int)*8)/3)+10]; char *cp; char c; int d; @@ -146,10 +152,13 @@ if (c == '%') { c = *format++; if (c == '%') + /* implement "%%" */ *bufptr++ = c; else if (c == 'c') + /* implement "%c" */ *bufptr++ = (char)va_arg(ap, int); else if (c == 's') { + /* implement "%s" */ if ((cp = (char *)va_arg(ap, char *)) == NULL) cp = "(null)"; n = strlen(cp); @@ -159,11 +168,12 @@ bufptr += n; } else if (c == 'd') { + /* implement "%d" */ d = (int)va_arg(ap, int); #ifdef HAVE_SNPRINTF - snprintf(ibuf, sizeof(ibuf), "%d", d); /* inherently secure */ + snprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */ #else - sprintf(ibuf, "%d", d); /* nevertheless secure; see ibuf above */ + sprintf(ibuf, "%d", d); /* implicitly secure */ #endif n = strlen(ibuf); memcpy(bufptr, ibuf, n); @@ -182,44 +192,66 @@ return (bufptr - buffer); } -/* minimal snprintf(3) */ +/* minimal snprintf(3) variant which supports %{c,s,d} only */ static int sa_msnprintf(char *buffer, size_t bufsize, const char *format, ...) { int chars; va_list ap; + /* argument sanity check(s) */ + if (buffer == NULL || bufsize == 0 || format == NULL) + return 0; + + /* pass through to va_list based variant */ va_start(ap, format); chars = sa_mvsnprintf(buffer, bufsize, format, ap); va_end(ap); + return chars; } -sa_rc_t sa_addr_create(sa_addr_t **saa) +/* create address object */ +sa_rc_t sa_addr_create(sa_addr_t **saap) { - if (saa == NULL) + sa_addr_t *saa; + + /* argument sanity check(s) */ + if (saap == NULL) return SA_ERR_ARG; - if ((*saa = (sa_addr_t *)malloc(sizeof(sa_addr_t))) == NULL) + + /* allocate and initialize new address object */ + if ((saa = (sa_addr_t *)malloc(sizeof(sa_addr_t))) == NULL) return SA_ERR_MEM; - (*saa)->saBuf = NULL; - (*saa)->slBuf = 0; - (*saa)->nFamily = 0; + saa->nFamily = 0; + saa->saBuf = NULL; + saa->slBuf = 0; + + /* pass object to caller */ + *saap = saa; + return SA_OK; } +/* destroy address object */ sa_rc_t sa_addr_destroy(sa_addr_t *saa) { + /* argument sanity check(s) */ if (saa == NULL) return SA_ERR_ARG; + + /* free address objects and sub-object(s) */ if (saa->saBuf != NULL) free(saa->saBuf); free(saa); + return SA_OK; } +/* import URI into address object */ sa_rc_t sa_addr_u2a(sa_addr_t *saa, const char *uri, ...) { va_list ap; - int nPort; + int sf; socklen_t sl; struct sockaddr *sa; struct sockaddr_un un; @@ -230,17 +262,17 @@ struct hostent *he; struct servent *se; int bNumeric; - int i; - int n; - char *cpProto; char *cpHost; char *cpPort; - char *cpPath; + char *cpProto; + int nPort; + const char *cpPath; char uribuf[1024]; char *cp; - int sf; + int i; + int n; - /* argument sanity check */ + /* argument sanity check(s) */ if (saa == NULL || uri == NULL) return SA_ERR_ARG; @@ -249,34 +281,47 @@ sa_mvsnprintf(uribuf, sizeof(uribuf), uri, ap); va_end(ap); - /* parse URI into parts */ + /* initialize result variables */ + sa = NULL; + sl = 0; + sf = 0; + + /* parse URI and resolve contents. + The following syntax is recognized: + - unix: + - inet://:[#(tcp|udp)] */ uri = uribuf; - cpProto = "unix"; - if ((cp = strchr(uri, ':')) != NULL) { - cpProto = (char *)uri; - *cp = '\0'; - uri = cp+1; - } - cpHost = ""; - cpPort = ""; - if (strncmp(uri, "//", 2) == 0) { - uri += 2; - cpHost = (char *)uri; - if ((cp = strchr(uri, ':')) == NULL) + if (strncmp(uri, "unix:", 5) == 0) { + /* parse URI */ + cpPath = uri+5; + if (cpPath[0] != '/') + return SA_ERR_ARG; + + /* mandatory(!) socket address structure initialization */ + memset(&un, 0, sizeof(un)); + + /* fill-in socket address structure */ + n = strlen(cpPath); + if ((n+1) > sizeof(un.sun_path)) + return SA_ERR_MEM; + memcpy(un.sun_path, cpPath, n+1); + un.sun_family = AF_LOCAL; + + /* provide results */ + sa = (struct sockaddr *)&un; + sl = sizeof(un); + sf = AF_LOCAL; + } + else if (strncmp(uri, "inet://", 7) == 0) { + /* parse URI */ + cpHost = (char *)(uri+7); + if ((cp = strchr(cpHost, ':')) == NULL) return SA_ERR_ARG; *cp++ = '\0'; cpPort = cp; - uri = cp; - if ((cp = strchr(uri, '/')) != NULL) - uri = cp; - else - uri = strchr(uri, '\0'); - } - cpPath = (char *)uri; - /* resolve port */ - nPort = 0; - if (strcmp(cpProto, "inet") == 0) { + /* resolve port */ + nPort = 0; bNumeric = 1; for (i = 0; cpPort[i] != '\0'; i++) { if (!isdigit((int)cpPort[i])) { @@ -287,38 +332,25 @@ if (bNumeric) nPort = atoi(cpPort); else { + cpProto = "tcp"; + if ((cp = strchr(cpPort, '#')) != NULL) { + *cp++ = '\0'; + cpProto = cp; + } if ((se = getservbyname(cpPort, cpProto)) == NULL) return SA_ERR_SYS; nPort = ntohs(se->s_port); } - } - /* mandatory(!) socket address structure initialization */ - memset(&un, 0, sizeof(un)); - memset(&sa4, 0, sizeof(sa4)); + /* mandatory(!) socket address structure initialization */ + memset(&sa4, 0, sizeof(sa4)); #ifdef AF_INET6 - memset(&sa6, 0, sizeof(sa6)); + memset(&sa6, 0, sizeof(sa6)); #endif - /* resolve host by trying to parse it as either directly a IPv4 or - IPv6 address or by resolving it to either a IPv4 or IPv6 address */ - sa = NULL; - sl = 0; - sf = 0; - if (strcmp(cpProto, "unix") == 0) { - if (cpProto[0] != '/') - return SA_ERR_ARG; - n = strlen(cpProto); - if ((n+1) > sizeof(un.sun_path)) - return SA_ERR_MEM; - memcpy(un.sun_path, cpPath, n+1); - un.sun_family = AF_LOCAL; - sa = (struct sockaddr *)&un; - sl = sizeof(un); - sf = AF_LOCAL; - } - else if (strcmp(cpProto, "inet") == 0) { - if (inet_pton(AF_INET, cpHost, &sa4.sin_addr.s_addr) == 1) { + /* resolve host by trying to parse it as either directly a IPv4 or + IPv6 address or by resolving it to either a IPv4 or IPv6 address */ + if (sa_inet_pton(AF_INET, cpHost, &sa4.sin_addr.s_addr) == 1) { sa4.sin_family = AF_INET; sa4.sin_port = htons(nPort); sa = (struct sockaddr *)&sa4; @@ -326,7 +358,7 @@ sf = AF_INET; } #ifdef AF_INET6 - else if (inet_pton(AF_INET6, cpHost, &sa6.sin6_addr.s6_addr) == 1) { + else if (sa_inet_pton(AF_INET6, cpHost, &sa6.sin6_addr.s6_addr) == 1) { sa6.sin6_family = AF_INET6; sa6.sin6_port = htons(nPort); sa = (struct sockaddr *)&sa6; @@ -338,7 +370,8 @@ if (he->h_addrtype == AF_INET) { sa4.sin_family = AF_INET; sa4.sin_port = htons(nPort); - memcpy(&sa4.sin_addr.s_addr, he->h_addr_list[0], sizeof(sa4.sin_addr.s_addr)); + memcpy(&sa4.sin_addr.s_addr, he->h_addr_list[0], + sizeof(sa4.sin_addr.s_addr)); sa = (struct sockaddr *)&sa4; sl = sizeof(sa4); sf = AF_INET; @@ -347,7 +380,8 @@ else if (he->h_addrtype == AF_INET6) { sa6.sin6_family = AF_INET6; sa6.sin6_port = htons(nPort); - memcpy(&sa6.sin6_addr.s6_addr, he->h_addr_list[0], sizeof(sa6.sin6_addr.s6_addr)); + memcpy(&sa6.sin6_addr.s6_addr, he->h_addr_list[0], + sizeof(sa6.sin6_addr.s6_addr)); sa = (struct sockaddr *)&sa6; sl = sizeof(sa6); sf = AF_INET6; @@ -355,18 +389,24 @@ #endif } } - if (sa == NULL) - return SA_ERR_ARG; - - /* create result address structure */ + + /* make sure result variables are set */ + if (sa == NULL || sl == 0 || sf == 0) + return SA_ERR_INT; + + /* fill-in result address structure */ + if (saa->saBuf != NULL) + free(saa->saBuf); if ((saa->saBuf = (struct sockaddr *)malloc(sl)) == NULL) return SA_ERR_MEM; memcpy(saa->saBuf, sa, sl); saa->slBuf = sl; saa->nFamily = sf; + return SA_OK; } +/* import "struct sockaddr" into address object */ sa_rc_t sa_addr_s2a(sa_addr_t *saa, const struct sockaddr *sabuf, socklen_t salen) { struct sockaddr_un *un; @@ -376,16 +416,19 @@ #endif int sf; + /* argument sanity check(s) */ if (saa == NULL || sabuf == NULL || salen == 0) return SA_ERR_ARG; /* create result address structure */ + if (saa->saBuf != NULL) + free(saa->saBuf); if ((saa->saBuf = (struct sockaddr *)malloc(salen)) == NULL) return SA_ERR_MEM; memcpy(saa->saBuf, sabuf, salen); saa->slBuf = salen; - /* fill in family */ + /* resolve family */ sf = 0; if (sizeof(struct sockaddr_un) == salen) { un = (struct sockaddr_un *)((void *)sabuf); @@ -411,6 +454,7 @@ return SA_OK; } +/* export address object into URI */ sa_rc_t sa_addr_a2u(sa_addr_t *saa, char **uri) { char uribuf[1024]; @@ -422,45 +466,60 @@ char caHost[512]; int nPort; + /* argument sanity check(s) */ if (saa == NULL || uri == NULL) return SA_ERR_ARG; + + /* export object contents */ if (saa->nFamily == AF_LOCAL) { un = (struct sockaddr_un *)((void *)saa->saBuf); sa_msnprintf(uribuf, sizeof(uribuf), "unix:%s", un->sun_path); } - else { +#ifdef AF_INET6 + else if (saa->nFamily == AF_INET || saa->nFamily == AF_INET6) { +#else + else if (saa->nFamily == AF_INET) { +#endif if (saa->nFamily == AF_INET) { sa4 = (struct sockaddr_in *)((void *)saa->saBuf); - inet_ntop(AF_INET, &sa4->sin_addr.s_addr, caHost, sizeof(caHost)); + sa_inet_ntop(AF_INET, &sa4->sin_addr.s_addr, caHost, sizeof(caHost)); nPort = ntohs(sa4->sin_port); } #ifdef AF_INET6 - else if (saa->nFamily == AF_INET6) { + else { sa6 = (struct sockaddr_in6 *)((void *)saa->saBuf); - inet_ntop(AF_INET6, &sa6->sin6_addr.s6_addr, caHost, sizeof(caHost)); + sa_inet_ntop(AF_INET6, &sa6->sin6_addr.s6_addr, caHost, sizeof(caHost)); nPort = ntohs(sa6->sin6_port); } #endif - else - return SA_ERR_ARG; sa_msnprintf(uribuf, sizeof(uribuf), "inet://%s:%d", caHost, nPort); } + else + return SA_ERR_INT; + + /* pass result to caller */ *uri = strdup(uribuf); + return SA_OK; } +/* export address object into "struct sockaddr" */ sa_rc_t sa_addr_a2s(sa_addr_t *saa, struct sockaddr **sabuf, socklen_t *salen) { + /* argument sanity check(s) */ if (saa == NULL || sabuf == NULL || salen == 0) return SA_ERR_ARG; + /* export underlying address structure */ if ((*sabuf = (struct sockaddr *)malloc(saa->slBuf)) == NULL) return SA_ERR_MEM; memmove(*sabuf, saa->saBuf, saa->slBuf); *salen = saa->slBuf; + return SA_OK; } +/* internal lazy/delayed initialization of underlying socket */ static sa_rc_t sa_socket_init(sa_t *sa, int nFamily) { int nType; @@ -468,11 +527,12 @@ struct protoent *pe; char *cpProto; + /* argument sanity check(s) */ if (sa == NULL) return SA_ERR_ARG; - /* only perform operation if socket still do not exists */ - if (sa->sSocket != -1) + /* only perform operation if socket still does not exist */ + if (sa->fdSocket != -1) return SA_ERR_USE; /* determine socket type */ @@ -481,7 +541,7 @@ else if (sa->eType == SA_TYPE_DATAGRAM) nType = SOCK_DGRAM; else - return SA_ERR_ARG; + return SA_ERR_INT; /* determine socket protocol */ if (nFamily == AF_LOCAL) @@ -502,95 +562,134 @@ nProto = pe->p_proto; } else - return SA_ERR_ARG; + return SA_ERR_INT; - /* create the socket */ - if ((sa->sSocket = socket(nFamily, nType, nProto)) == -1) + /* create the underlying socket */ + if ((sa->fdSocket = socket(nFamily, nType, nProto)) == -1) return SA_ERR_SYS; return SA_OK; } +/* internal destruction of underlying socket */ static sa_rc_t sa_socket_kill(sa_t *sa) { + /* argument sanity check(s) */ if (sa == NULL) return SA_ERR_ARG; - if (sa->sSocket != -1) { - close(sa->sSocket); - sa->sSocket = -1; - } + + /* check context */ + if (sa->fdSocket == -1) + return SA_ERR_USE; + + /* close socket */ + close(sa->fdSocket); + sa->fdSocket = -1; + return SA_OK; } +/* create abstract socket object */ sa_rc_t sa_create(sa_t **sap) { sa_t *sa; + /* argument sanity check(s) */ + if (sap == NULL) + return SA_ERR_ARG; + + /* allocate and initialize socket object */ if ((sa = (sa_t *)malloc(sizeof(sa_t))) == NULL) return SA_ERR_MEM; - sa->eType = SA_TYPE_STREAM; - sa->sSocket = -1; - sa->bTimeout = 0; + sa->eType = SA_TYPE_STREAM; + sa->fdSocket = -1; + sa->bTimeout = FALSE; sa->tvTimeout.tv_sec = 0; sa->tvTimeout.tv_usec = 0; - sa->nReadLen = 0; - sa->nReadSize = 0; - sa->cpReadBuf = NULL; - sa->nWriteLen = 0; - sa->nWriteSize = 0; - sa->cpWriteBuf = NULL; + sa->nReadLen = 0; + sa->nReadSize = 0; + sa->cpReadBuf = NULL; + sa->nWriteLen = 0; + sa->nWriteSize = 0; + sa->cpWriteBuf = NULL; + + /* pass object to caller */ *sap = sa; + return SA_OK; } +/* destroy abstract socket object */ sa_rc_t sa_destroy(sa_t *sa) { + /* argument sanity check(s) */ if (sa == NULL) return SA_ERR_ARG; + + /* kill underlying socket */ sa_socket_kill(sa); + + /* free object and sub-objects */ if (sa->cpReadBuf != NULL) free(sa->cpReadBuf); if (sa->cpWriteBuf != NULL) free(sa->cpWriteBuf); free(sa); + return SA_OK; } +/* switch communication type of socket */ sa_rc_t sa_type(sa_t *sa, sa_type_t type) { + /* argument sanity check(s) */ if (sa == NULL) return SA_ERR_ARG; - if (type != SA_TYPE_STREAM && type != SA_TYPE_DATAGRAM) + if (!(type == SA_TYPE_STREAM || type == SA_TYPE_DATAGRAM)) return SA_ERR_ARG; + + /* kill underlying socket if type changes */ + if (sa->eType != type) + sa_socket_kill(sa); + + /* set new type */ sa->eType = type; + return SA_OK; } +/* configure I/O timeout */ sa_rc_t sa_timeout(sa_t *sa, long sec, long usec) { + /* argument sanity check(s) */ if (sa == NULL) return SA_ERR_ARG; - if (sec == 0 && usec == 0) { - sa->bTimeout = 0; - sa->tvTimeout.tv_sec = 0; - sa->tvTimeout.tv_usec = 0; - } - else { - sa->bTimeout = 1; - sa->tvTimeout.tv_sec = sec; - sa->tvTimeout.tv_usec = usec; - } + + /* configure timeout */ + if (sec == 0 && usec == 0) + sa->bTimeout = FALSE; /* deactivate timeout */ + else + sa->bTimeout = TRUE; /* activate timeout */ + sa->tvTimeout.tv_sec = sec; + sa->tvTimeout.tv_usec = usec; + return SA_OK; } +/* configure I/O buffers */ sa_rc_t sa_buffers(sa_t *sa, size_t rsize, size_t wsize) { char *cp; + /* argument sanity check(s) */ if (sa == NULL) return SA_ERR_ARG; + + /* make sure buffered were already flushed sufficiently */ if (sa->nReadLen > rsize || sa->nWriteLen > wsize) - return SA_ERR_ARG; + return SA_ERR_USE; + + /* configure read/incoming buffer */ if (rsize > 0) { if (sa->cpReadBuf == NULL) cp = (char *)malloc(rsize); @@ -607,6 +706,8 @@ sa->cpReadBuf = NULL; sa->nReadSize = 0; } + + /* configure write/outgoing buffer */ if (wsize > 0) { if (sa->cpWriteBuf == NULL) cp = (char *)malloc(wsize); @@ -623,50 +724,39 @@ sa->cpWriteBuf = NULL; sa->nWriteSize = 0; } - return SA_OK; -} -sa_rc_t sa_getoption(sa_t *sa, int optname, void *optval, socklen_t *optlen) -{ - if (sa == NULL) - return SA_ERR_ARG; - if (sa->sSocket == -1) - return SA_ERR_USE; - if (getsockopt(sa->sSocket, SOL_SOCKET, optname, optval, optlen) == -1) - return SA_ERR_SYS; - return SA_OK; -} - -sa_rc_t sa_setoption(sa_t *sa, int optname, const void *optval, socklen_t optlen) -{ - if (sa == NULL) - return SA_ERR_ARG; - if (sa->sSocket == -1) - return SA_ERR_USE; - if (setsockopt(sa->sSocket, SOL_SOCKET, optname, optval, optlen) == -1) - return SA_ERR_SYS; return SA_OK; } +/* bind socket to a local address */ sa_rc_t sa_bind(sa_t *sa, sa_addr_t *laddr) { sa_rc_t rv; struct sockaddr_un *un; + /* argument sanity check(s) */ if (sa == NULL || laddr == NULL) return SA_ERR_ARG; - if (sa->sSocket == -1) + + /* lazy creation of underlying socket */ + if (sa->fdSocket == -1) if ((rv = sa_socket_init(sa, laddr->nFamily)) != SA_OK) return rv; + + /* remove a possibly existing old Unix Domain socket on filesystem */ if (laddr->nFamily == AF_LOCAL) { un = (struct sockaddr_un *)((void *)laddr->saBuf); unlink(un->sun_path); } - if (bind(sa->sSocket, laddr->saBuf, laddr->slBuf) == -1) + + /* perform bind operation on underlying socket */ + if (bind(sa->fdSocket, laddr->saBuf, laddr->slBuf) == -1) return SA_ERR_SYS; + return SA_OK; } +/* connect socket to a remote address */ sa_rc_t sa_connect(sa_t *sa, sa_addr_t *raddr) { int flags, n, error; @@ -674,94 +764,106 @@ socklen_t len; sa_rc_t rv; - if (sa == NULL) + /* argument sanity check(s) */ + if (sa == NULL || raddr == NULL) return SA_ERR_ARG; + + /* connecting is only possible for stream communication */ if (sa->eType != SA_TYPE_STREAM) return SA_ERR_USE; - if (sa->sSocket == -1) + + /* lazy creation of underlying socket */ + if (sa->fdSocket == -1) if ((rv = sa_socket_init(sa, raddr->nFamily)) != SA_OK) return rv; + rv = SA_OK; if (!sa->bTimeout) { - if (connect(sa->sSocket, raddr->saBuf, raddr->slBuf) < 0) - return SA_ERR_SYS; - else - return SA_OK; + /* standard/non-timeout-aware connect operation */ + if (connect(sa->fdSocket, raddr->saBuf, raddr->slBuf) < 0) + rv = SA_ERR_SYS; } + else { + /* emulated/timeout-aware connect operation */ + error = 0; - error = 0; - rv = SA_OK; - - /* remember socket flags */ - flags = fcntl(sa->sSocket, F_GETFL, 0); + /* temporarily switch underlying socket to non-blocking mode */ + flags = fcntl(sa->fdSocket, F_GETFL, 0); + fcntl(sa->fdSocket, F_SETFL, flags|O_NONBLOCK); + + /* perform the connect operation */ + if ((n = connect(sa->fdSocket, raddr->saBuf, raddr->slBuf)) < 0) { + if (errno != EINPROGRESS) { + error = errno; + goto done; + } + } - /* switch to non-blocking mode */ - fcntl(sa->sSocket, F_SETFL, flags|O_NONBLOCK); + /* ok if connect completed immediately */ + if (n == 0) + goto done; + + /* wait for read or write possibility */ + FD_ZERO(&rset); + FD_ZERO(&wset); + FD_SET(sa->fdSocket, &rset); + FD_SET(sa->fdSocket, &wset); + do { + n = select(sa->fdSocket+1, &rset, &wset, NULL, &sa->tvTimeout); + } while (n == -1 && errno == EINTR); - /* perform the connect */ - if ((n = connect(sa->sSocket, raddr->saBuf, raddr->slBuf)) < 0) { - if (errno != EINPROGRESS) { + /* decide on return semantic */ + if (n < 0) { error = errno; goto done; } - } - - /* if connect completed immediately */ - if (n == 0) - goto done; - - /* wait for read or write possibility */ - FD_ZERO(&rset); - FD_ZERO(&wset); - FD_SET(sa->sSocket, &rset); - FD_SET(sa->sSocket, &wset); - do { - n = select(sa->sSocket+1, &rset, &wset, NULL, &sa->tvTimeout); - } while (n == -1 && errno == EINTR); - - /* decide on return semantic */ - if (n < 0) { - error = errno; - goto done; - } - else if (n == 0) { - error = ETIMEDOUT; - goto done; - } + else if (n == 0) { + error = ETIMEDOUT; + goto done; + } - /* fetch pending error */ - len = sizeof(error); - if (getsockopt(sa->sSocket, SOL_SOCKET, SO_ERROR, &error, &len) < 0) - error = errno; + /* fetch pending error */ + len = sizeof(error); + if (getsockopt(sa->fdSocket, SOL_SOCKET, SO_ERROR, &error, &len) < 0) + error = errno; - done: + done: - /* reset socket flags */ - fcntl(sa->sSocket, F_SETFL, flags); + /* reset socket flags */ + fcntl(sa->fdSocket, F_SETFL, flags); - /* optionally set errno */ - if (error != 0) { - errno = error; - rv = SA_ERR_SYS; + /* optionally set errno */ + if (error != 0) { + errno = error; + rv = SA_ERR_SYS; + } } - return rv; } +/* listen on socket for connections */ sa_rc_t sa_listen(sa_t *sa, int backlog) { + /* argument sanity check(s) */ if (sa == NULL) return SA_ERR_ARG; + + /* listening is only possible for stream communication */ if (sa->eType != SA_TYPE_STREAM) return SA_ERR_USE; - if (sa->sSocket == -1) - /* at least sa_bind() has to be already performed */ + + /* at least sa_bind() has to be already performed */ + if (sa->fdSocket == -1) return SA_ERR_USE; - if (listen(sa->sSocket, backlog) == -1) + + /* perform listen operation on underlying socket */ + if (listen(sa->fdSocket, backlog) == -1) return SA_ERR_SYS; + return SA_OK; } +/* accept a connection on socket */ sa_rc_t sa_accept(sa_t *sa, sa_addr_t **caddr, sa_t **csa) { sa_rc_t rv; @@ -776,41 +878,55 @@ socklen_t sa_len; int s; - if (sa == NULL) + /* argument sanity check(s) */ + if (sa == NULL || caddr == NULL || csa == NULL) return SA_ERR_ARG; + + /* accepting connections is only possible for stream communication */ if (sa->eType != SA_TYPE_STREAM) return SA_ERR_USE; - if (sa->sSocket == -1) - /* at least sa_listen() has to be already performed */ + + /* at least sa_listen() has to be already performed */ + if (sa->fdSocket == -1) return SA_ERR_USE; + + /* if timeout is enabled, perform a smart-blocking wait */ if (sa->bTimeout) { FD_ZERO(&fds); - FD_SET(sa->sSocket, &fds); + FD_SET(sa->fdSocket, &fds); do { - n = select(sa->sSocket+1, &fds, NULL, NULL, &sa->tvTimeout); + n = select(sa->fdSocket+1, &fds, NULL, NULL, &sa->tvTimeout); } while (n == -1 && errno == EINTR); if (n == 0) errno = ETIMEDOUT; if (n <= 0) return SA_ERR_SYS; } + + /* perform accept operation on underlying socket */ sa_len = sizeof(sa_buf); - if ((s = accept(sa->sSocket, (struct sockaddr *)&sa_buf, &sa_len)) == -1) + if ((s = accept(sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_len)) == -1) return SA_ERR_SYS; + + /* create result address object */ if ((rv = sa_addr_create(caddr)) != SA_OK) return rv; if ((rv = sa_addr_s2a(*caddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK) { sa_addr_destroy(*caddr); return rv; } + + /* create result socket object */ if ((rv = sa_create(csa)) != SA_OK) { sa_addr_destroy(*caddr); return rv; } - (*csa)->sSocket = s; + (*csa)->fdSocket = s; + return SA_OK; } +/* determine remote address */ sa_rc_t sa_getremote(sa_t *sa, sa_addr_t **raddr) { sa_rc_t rv; @@ -822,22 +938,35 @@ } sa_buf; socklen_t sa_len; - if (sa == NULL) + /* argument sanity check(s) */ + if (sa == NULL || raddr == NULL) return SA_ERR_ARG; - sa_len = sizeof(sa_buf); - if (sa->sSocket == -1) + + /* peers exist only for stream communication */ + if (sa->eType != SA_TYPE_STREAM) + return SA_ERR_USE; + + /* at least sa_connect() or sa_accept() has to be already performed */ + if (sa->fdSocket == -1) return SA_ERR_USE; - if (getpeername(sa->sSocket, (struct sockaddr *)&sa_buf, &sa_len) < 0) + + /* determine remote address of underlying socket */ + sa_len = sizeof(sa_buf); + if (getpeername(sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_len) < 0) return SA_ERR_SYS; + + /* create result address object */ if ((rv = sa_addr_create(raddr)) != SA_OK) return rv; if ((rv = sa_addr_s2a(*raddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK) { sa_addr_destroy(*raddr); return rv; } + return SA_OK; } +/* determine local address */ sa_rc_t sa_getlocal(sa_t *sa, sa_addr_t **laddr) { sa_rc_t rv; @@ -849,85 +978,114 @@ } sa_buf; socklen_t sa_len; - if (sa == NULL) + /* argument sanity check(s) */ + if (sa == NULL || laddr == NULL) return SA_ERR_ARG; - if (sa->sSocket == -1) + + /* at least sa_bind() has to be already performed */ + if (sa->fdSocket == -1) return SA_ERR_USE; + + /* determine local address of underlying socket */ sa_len = sizeof(sa_buf); - if (getsockname(sa->sSocket, (struct sockaddr *)&sa_buf, &sa_len) < 0) + if (getsockname(sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_len) < 0) return SA_ERR_SYS; + + /* create result address object */ if ((rv = sa_addr_create(laddr)) != SA_OK) return rv; if ((rv = sa_addr_s2a(*laddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK) { sa_addr_destroy(*laddr); return rv; } + return SA_OK; } +/* peek at underlying socket */ sa_rc_t sa_getfd(sa_t *sa, int *fd) { + /* argument sanity check(s) */ if (sa == NULL || fd == NULL) return SA_ERR_ARG; - if (sa->sSocket == -1) + + /* if still no socket exists, say this explicitly */ + if (sa->fdSocket == -1) return SA_ERR_USE; - *fd = sa->sSocket; + + /* pass socket to caller */ + *fd = sa->fdSocket; + return SA_OK; } +/* internal raw read operation */ static int sa_read_raw(sa_t *sa, char *cpBuf, int nBufLen) { int rv; fd_set fds; + /* if timeout is enabled, perform explicit/smart blocking instead + of implicitly/hard blocking in the read(2) system call */ if (sa->bTimeout) { FD_ZERO(&fds); - FD_SET(sa->sSocket, &fds); + FD_SET(sa->fdSocket, &fds); do { - rv = select(sa->sSocket+1, &fds, NULL, NULL, &sa->tvTimeout); + rv = select(sa->fdSocket+1, &fds, NULL, NULL, &sa->tvTimeout); } while (rv == -1 && errno == EINTR); if (rv == 0) { errno = ETIMEDOUT; return -1; } } + + /* perform read operation on underlying socket */ do { - rv = read(sa->sSocket, cpBuf, nBufLen); + rv = read(sa->fdSocket, cpBuf, nBufLen); } while (rv == -1 && errno == EINTR); + return rv; } +/* read data from socket */ sa_rc_t sa_read(sa_t *sa, char *cpBuf, size_t nBufReq, size_t *nBufRes) { int n; sa_rc_t rv; size_t res; + /* argument sanity check(s) */ if (sa == NULL || cpBuf == NULL || nBufReq == 0) return SA_ERR_ARG; + + /* reading is only possible for stream communication */ if (sa->eType != SA_TYPE_STREAM) return SA_ERR_USE; - if (sa->sSocket == -1) + + /* at least a connection has to exist */ + if (sa->fdSocket == -1) return SA_ERR_USE; - /* flush write buffer */ + /* trigger a write buffer flush */ if (sa->nWriteLen > 0) sa_flush(sa); /* perform read operation */ rv = SA_OK; if (sa->nReadSize == 0) { - /* unbuffered I/O */ + /* user-space unbuffered I/O */ res = sa_read_raw(sa, cpBuf, nBufReq); - if (res <= 0) + if (res == 0) + rv = SA_ERR_EOF; + else if (res < 0) rv = SA_ERR_SYS; } else { - /* buffered I/O */ + /* user-space buffered I/O */ res = 0; while (1) { if (nBufReq <= sa->nReadLen) { - /* buffer holds enough data, so use this */ + /* buffer holds enough data, so just use this */ memmove(cpBuf, sa->cpReadBuf, nBufReq); memmove(sa->cpReadBuf, sa->cpReadBuf+nBufReq, sa->nReadLen-nBufReq); sa->nReadLen -= nBufReq; @@ -947,84 +1105,60 @@ n = sa_read_raw(sa, cpBuf, nBufReq); if (n > 0) res += n; - else if (n <= 0) - rv = SA_ERR_SYS; + else if (n == 0) + rv = (res == 0 ? SA_ERR_EOF : SA_OK); + else if (n < 0) + rv = (res == 0 ? SA_ERR_SYS : SA_OK); } else { /* fill buffer with new data */ n = sa_read_raw(sa, sa->cpReadBuf, sa->nReadSize); - if (n <= 0) - rv = SA_ERR_SYS; + if (n < 0) + /* error on this read, but perhaps ok as a whole */ + rv = (res == 0 ? SA_ERR_SYS : SA_OK); + if (n == 0) + /* EOF on this read, but perhaps ok as a whole */ + rv = (res == 0 ? SA_ERR_EOF : SA_OK); else { sa->nReadLen = n; - continue; + continue; /* REPEAT OPERATION! */ } } } break; } } + + /* pass number of actually read bytes to caller */ if (nBufRes != NULL) *nBufRes = res; - return rv; -} - -sa_rc_t sa_readfrom(sa_t *sa, char *buf, size_t buflen, size_t *bufdone, sa_addr_t **raddr) -{ - sa_rc_t rv; - union { - struct sockaddr_in sa4; -#ifdef AF_INET6 - struct sockaddr_in6 sa6; -#endif - } sa_buf; - socklen_t sa_len; - size_t n; - fd_set fds; - if (sa == NULL || buf == NULL || buflen == 0 || raddr == NULL) - return SA_ERR_ARG; - if (sa->eType != SA_TYPE_DATAGRAM) - return SA_ERR_USE; - if (sa->sSocket == -1) - return SA_ERR_USE; - if (sa->bTimeout) { - FD_ZERO(&fds); - FD_SET(sa->sSocket, &fds); - do { - n = select(sa->sSocket+1, &fds, NULL, NULL, &sa->tvTimeout); - } while (n == -1 && errno == EINTR); - if (n == 0) - errno = ETIMEDOUT; - if (n <= 0) - return SA_ERR_SYS; - } - sa_len = sizeof(sa_buf); - if ((n = recvfrom(sa->sSocket, buf, buflen, 0, (struct sockaddr *)&sa_buf, &sa_len)) == -1) - return SA_ERR_SYS; - if ((rv = sa_addr_create(raddr)) != SA_OK) - return rv; - if ((rv = sa_addr_s2a(*raddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK) { - sa_addr_destroy(*raddr); - return rv; - } - if (bufdone != NULL) - *bufdone = n; - return SA_OK; + return rv; } -sa_rc_t sa_readline(sa_t *sa, char *cpBuf, size_t nBufReq, size_t *nBufRes) +/* read data from socket until [CR]LF (convinience function) */ +sa_rc_t sa_readln(sa_t *sa, char *cpBuf, size_t nBufReq, size_t *nBufRes) { char c; size_t n; size_t res; - if (sa == NULL) + /* argument sanity check(s) */ + if (sa == NULL || cpBuf == NULL || nBufReq == 0) return SA_ERR_ARG; + + /* reading is only possible for stream communication */ if (sa->eType != SA_TYPE_STREAM) return SA_ERR_USE; - if (sa->sSocket == -1) + + /* at least a connection has to exist */ + if (sa->fdSocket == -1) return SA_ERR_USE; + + /* we just perform a plain sa_read() per character, because if + buffers are enabled, this is not as stupid as it looks at the first + hand and if buffers are disabled, there is no better solution + anyway. */ res = 0; while (res < (nBufReq-1)) { sa_read(sa, &c, 1, &n); @@ -1035,59 +1169,72 @@ break; } cpBuf[res] = '\0'; + + /* pass number of actually read characters to caller */ if (nBufRes != NULL) *nBufRes = res; + return SA_OK; } +/* internal raw write operation */ static int sa_write_raw(sa_t *sa, const char *cpBuf, int nBufLen) { int rv; fd_set fds; + /* if timeout is enabled, perform explicit/smart blocking instead + of implicitly/hard blocking in the write(2) system call */ if (sa->bTimeout) { FD_ZERO(&fds); - FD_SET(sa->sSocket, &fds); + FD_SET(sa->fdSocket, &fds); do { - rv = select(sa->sSocket+1, NULL, &fds, NULL, &sa->tvTimeout); + rv = select(sa->fdSocket+1, NULL, &fds, NULL, &sa->tvTimeout); } while (rv == -1 && errno == EINTR); if (rv == 0) { errno = ETIMEDOUT; return -1; } } + + /* perform write operation on underlying socket */ do { - rv = write(sa->sSocket, cpBuf, nBufLen); + rv = write(sa->fdSocket, cpBuf, nBufLen); } while (rv == -1 && errno == EINTR); + return rv; } +/* write data to socket */ sa_rc_t sa_write(sa_t *sa, const char *cpBuf, size_t nBufReq, size_t *nBufRes) { size_t n; size_t res; sa_rc_t rv; - if (sa == NULL) + /* argument sanity check(s) */ + if (sa == NULL || cpBuf == NULL || nBufReq == 0) return SA_ERR_ARG; + + /* writing is only possible for stream communication */ if (sa->eType != SA_TYPE_STREAM) return SA_ERR_USE; - if (sa->sSocket == -1) + + /* at least a connection has to exist */ + if (sa->fdSocket == -1) return SA_ERR_USE; rv = SA_OK; if (sa->nWriteSize == 0) { - /* unbuffered I/O */ + /* user-space unbuffered I/O */ res = sa_write_raw(sa, cpBuf, nBufReq); - if (res < 0) { - res = 0; + if (res < 0) rv = SA_ERR_SYS; - } } else { - /* buffered I/O */ + /* user-space buffered I/O */ if (nBufReq > (sa->nWriteSize - sa->nWriteLen)) { - /* not enough space in buffer, so flush buffer */ + /* not enough space in buffer, so flush buffer first */ sa_flush(sa); } res = 0; @@ -1096,7 +1243,7 @@ while (nBufReq > 0) { n = sa_write_raw(sa, cpBuf, nBufReq); if (n < 0) - rv = SA_ERR_SYS; + rv = (res == 0 ? SA_ERR_SYS : SA_OK); if (n <= 0) break; nBufReq -= n; @@ -1111,72 +1258,70 @@ res = nBufReq; } } + + /* pass number of actually written bytes to caller */ if (nBufRes != NULL) *nBufRes = res; + return rv; } -sa_rc_t sa_writeto(sa_t *sa, const char *buf, size_t buflen, size_t *bufdone, sa_addr_t *raddr) +/* write formatted string to socket (convinience function) */ +sa_rc_t sa_writef(sa_t *sa, const char *cpFmt, ...) { + va_list ap; size_t n; - fd_set fds; + char caBuf[1024]; sa_rc_t rv; - if (sa == NULL || buf == NULL || buflen == 0 || raddr == NULL) + /* argument sanity check(s) */ + if (sa == NULL || cpFmt == NULL) return SA_ERR_ARG; - if (sa->eType != SA_TYPE_DATAGRAM) - return SA_ERR_USE; - if (sa->sSocket == -1) - if ((rv = sa_socket_init(sa, raddr->nFamily)) != SA_OK) - return rv; - if (sa->sSocket == -1) + + /* writing is only possible for stream communication */ + if (sa->eType != SA_TYPE_STREAM) return SA_ERR_USE; - if (sa->bTimeout) { - FD_ZERO(&fds); - FD_SET(sa->sSocket, &fds); - do { - n = select(sa->sSocket+1, NULL, &fds, NULL, &sa->tvTimeout); - } while (n == -1 && errno == EINTR); - if (n == 0) - errno = ETIMEDOUT; - if (n <= 0) - return SA_ERR_SYS; - } - if ((n = sendto(sa->sSocket, buf, buflen, 0, raddr->saBuf, raddr->slBuf)) == -1) - return SA_ERR_SYS; - if (bufdone != NULL) - *bufdone = n; - return SA_OK; -} -sa_rc_t sa_writef(sa_t *sa, const char *cpFmt, ...) -{ - va_list ap; - size_t n; - char caBuf[1024]; + /* at least a connection has to exist */ + if (sa->fdSocket == -1) + return SA_ERR_USE; - if (sa == NULL) - return SA_ERR_ARG; + /* format string into temporary buffer */ va_start(ap, cpFmt); n = sa_mvsnprintf(caBuf, sizeof(caBuf), cpFmt, ap); - sa_write(sa, caBuf, n, &n); va_end(ap); - return SA_OK; + + /* write result buffer to socket */ + rv = sa_write(sa, caBuf, n, &n); + + return rv; } +/* flush write/outgoing I/O buffer */ sa_rc_t sa_flush(sa_t *sa) { size_t n; + sa_rc_t rv; + /* argument sanity check(s) */ if (sa == NULL) return SA_ERR_ARG; + + /* flushing is only possible for stream communication */ if (sa->eType != SA_TYPE_STREAM) return SA_ERR_USE; - if (sa->sSocket == -1) + + /* at least a connection has to exist */ + if (sa->fdSocket == -1) return SA_ERR_USE; + + /* try to flush buffer */ + rv = SA_OK; if (sa->nWriteSize > 0) { while (sa->nWriteLen > 0) { n = sa_write_raw(sa, sa->cpWriteBuf, sa->nWriteLen); + if (n < 0) + rv = SA_ERR_SYS; if (n <= 0) break; memmove(sa->cpWriteBuf, sa->cpWriteBuf+n, sa->nWriteLen-n); @@ -1184,19 +1329,27 @@ } sa->nWriteLen = 0; } - return SA_OK; + return rv; } +/* shutdown a socket connection */ sa_rc_t sa_shutdown(sa_t *sa, char *flags) { int how; + /* argument sanity check(s) */ if (sa == NULL || flags == NULL) return SA_ERR_ARG; + + /* shutdown is only possible for stream communication */ if (sa->eType != SA_TYPE_STREAM) return SA_ERR_USE; - if (sa->sSocket == -1) + + /* at least a connection has to exist */ + if (sa->fdSocket == -1) return SA_ERR_USE; + + /* determine flags for shutdown(2) */ how = 0; if (strcmp(flags, "r") == 0) how = SHUT_RD; @@ -1204,8 +1357,119 @@ how = SHUT_WR; else if (strcmp(flags, "rw") == 0) how = SHUT_RDWR; - if (shutdown(sa->sSocket, how) == -1) + else + return SA_ERR_ARG; + + /* perform shutdown operation on underlying socket */ + if (shutdown(sa->fdSocket, how) == -1) + return SA_ERR_SYS; + + return SA_OK; +} + +/* receive data via socket */ +sa_rc_t sa_recv(sa_t *sa, char *buf, size_t buflen, size_t *bufdone, sa_addr_t **raddr) +{ + sa_rc_t rv; + union { + struct sockaddr_in sa4; +#ifdef AF_INET6 + struct sockaddr_in6 sa6; +#endif + } sa_buf; + socklen_t sa_len; + size_t n; + fd_set fds; + + /* argument sanity check(s) */ + if (sa == NULL || buf == NULL || buflen == 0 || raddr == NULL) + return SA_ERR_ARG; + + /* receiving is only possible for datagram communication */ + if (sa->eType != SA_TYPE_DATAGRAM) + return SA_ERR_USE; + + /* at least a sa_bind() has to be performed */ + if (sa->fdSocket == -1) + return SA_ERR_USE; + + /* if timeout is enabled, perform explicit/smart blocking instead + of implicitly/hard blocking in the recvfrom(2) system call */ + if (sa->bTimeout) { + FD_ZERO(&fds); + FD_SET(sa->fdSocket, &fds); + do { + n = select(sa->fdSocket+1, &fds, NULL, NULL, &sa->tvTimeout); + } while (n == -1 && errno == EINTR); + if (n == 0) + errno = ETIMEDOUT; + if (n <= 0) + return SA_ERR_SYS; + } + + /* perform receive operation on underlying socket */ + sa_len = sizeof(sa_buf); + if ((n = recvfrom(sa->fdSocket, buf, buflen, 0, + (struct sockaddr *)&sa_buf, &sa_len)) == -1) + return SA_ERR_SYS; + + /* create result address object */ + if ((rv = sa_addr_create(raddr)) != SA_OK) + return rv; + if ((rv = sa_addr_s2a(*raddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK) { + sa_addr_destroy(*raddr); + return rv; + } + + /* pass actual number of received bytes to caller */ + if (bufdone != NULL) + *bufdone = n; + + return SA_OK; +} + +/* send data via socket */ +sa_rc_t sa_send(sa_t *sa, const char *buf, size_t buflen, size_t *bufdone, sa_addr_t *raddr) +{ + size_t n; + fd_set fds; + sa_rc_t rv; + + /* argument sanity check(s) */ + if (sa == NULL || buf == NULL || buflen == 0 || raddr == NULL) + return SA_ERR_ARG; + + /* sending is only possible for datagram communication */ + if (sa->eType != SA_TYPE_DATAGRAM) + return SA_ERR_USE; + + /* lazy creation of underlying socket */ + if (sa->fdSocket == -1) + if ((rv = sa_socket_init(sa, raddr->nFamily)) != SA_OK) + return rv; + + /* if timeout is enabled, perform explicit/smart blocking instead + of implicitly/hard blocking in the sendto(2) system call */ + if (sa->bTimeout) { + FD_ZERO(&fds); + FD_SET(sa->fdSocket, &fds); + do { + n = select(sa->fdSocket+1, NULL, &fds, NULL, &sa->tvTimeout); + } while (n == -1 && errno == EINTR); + if (n == 0) + errno = ETIMEDOUT; + if (n <= 0) + return SA_ERR_SYS; + } + + /* perform send operation on underlying socket */ + if ((n = sendto(sa->fdSocket, buf, buflen, 0, raddr->saBuf, raddr->slBuf)) == -1) return SA_ERR_SYS; + + /* pass actual number of sent bytes to caller */ + if (bufdone != NULL) + *bufdone = n; + return SA_OK; } Index: ossp-pkg/sa/sa.h RCS File: /v/ossp/cvs/ossp-pkg/sa/sa.h,v rcsdiff -q -kk '-r1.6' '-r1.7' -u '/v/ossp/cvs/ossp-pkg/sa/sa.h,v' 2>/dev/null --- sa.h 2001/10/05 11:49:10 1.6 +++ sa.h 2001/10/05 20:52:15 1.7 @@ -30,7 +30,7 @@ #ifndef __SA_H__ #define __SA_H__ -/* system definitions of "size_t", "socklen_t", "struct sockaddr" */ +/* system definitions of "size_t", "socklen_t", "struct sockaddr *" */ #include #include #include @@ -96,11 +96,12 @@ /* return codes */ typedef enum { SA_OK, - SA_ERR_ARG, - SA_ERR_USE, - SA_ERR_MEM, - SA_ERR_SYS, - SA_ERR_INT + SA_ERR_ARG, /* invalid argument (wrong parameter) */ + SA_ERR_USE, /* invalid use (wrong context) */ + SA_ERR_MEM, /* out of memory */ + SA_ERR_SYS, /* system error (see errno) */ + SA_ERR_EOF, /* end of file/socket communication */ + SA_ERR_INT /* internal error */ } sa_rc_t; /* address object operations */ @@ -121,8 +122,6 @@ sa_rc_t sa_type (sa_t *sa, sa_type_t type); sa_rc_t sa_timeout (sa_t *sa, long sec, long usec); sa_rc_t sa_buffers (sa_t *sa, size_t rsize, size_t wsize); -sa_rc_t sa_getoption (sa_t *sa, int optname, void *optval, socklen_t *optlen); -sa_rc_t sa_setoption (sa_t *sa, int optname, const void *optval, socklen_t optlen); /* socket connection operations */ sa_rc_t sa_bind (sa_t *sa, sa_addr_t *laddr); @@ -134,14 +133,16 @@ sa_rc_t sa_getfd (sa_t *sa, int *fd); sa_rc_t sa_shutdown (sa_t *sa, char *flags); -/* socket input/output operations */ +/* socket input/output operations (stream communication) */ sa_rc_t sa_read (sa_t *sa, char *buf, size_t buflen, size_t *bufdone); -sa_rc_t sa_readfrom (sa_t *sa, char *buf, size_t buflen, size_t *bufdone, sa_addr_t **raddr); -sa_rc_t sa_readline (sa_t *sa, char *buf, size_t buflen, size_t *bufdone); +sa_rc_t sa_readln (sa_t *sa, char *buf, size_t buflen, size_t *bufdone); sa_rc_t sa_write (sa_t *sa, const char *buf, size_t buflen, size_t *bufdone); -sa_rc_t sa_writeto (sa_t *sa, const char *buf, size_t buflen, size_t *bufdone, sa_addr_t *raddr); sa_rc_t sa_writef (sa_t *sa, const char *fmt, ...); sa_rc_t sa_flush (sa_t *sa); +/* socket input/output operations (datagram communication) */ +sa_rc_t sa_recv (sa_t *sa, char *buf, size_t buflen, size_t *bufdone, sa_addr_t **raddr); +sa_rc_t sa_send (sa_t *sa, const char *buf, size_t buflen, size_t *bufdone, sa_addr_t *raddr); + #endif /* __SA_H__ */ Index: ossp-pkg/sa/sa.pod RCS File: /v/ossp/cvs/ossp-pkg/sa/sa.pod,v rcsdiff -q -kk '-r1.5' '-r1.6' -u '/v/ossp/cvs/ossp-pkg/sa/sa.pod,v' 2>/dev/null --- sa.pod 2001/10/05 11:55:51 1.5 +++ sa.pod 2001/10/05 20:52:15 1.6 @@ -76,16 +76,19 @@ sa_getfd, sa_shutdown. -=item B: +=item B: sa_read, -sa_readfrom, -sa_readline, +sa_readln, sa_write, -sa_writeto, sa_writef, sa_flush. +=item B: + +sa_recv, +sa_send. + =back =head1 DESCRIPTION @@ -227,26 +230,32 @@ =back -=head2 Socket Input/Output Operations +=head2 Socket Input/Output Operations (Stream Communication) =over 4 =item sa_rc_t B(sa_t *I, char *I, size_t I, size_t *I); -=item sa_rc_t B(sa_t *I, char *I, size_t I, size_t *I, sa_addr_t **I); - -=item sa_rc_t B(sa_t *I, char *I, size_t I, size_t *I); +=item sa_rc_t B(sa_t *I, char *I, size_t I, size_t *I); =item sa_rc_t B(sa_t *I, const char *I, size_t I, size_t *I); -=item sa_rc_t B(sa_t *I, const char *I, size_t I, size_t *I, sa_addr_t *I); - =item sa_rc_t B(sa_t *I, const char *I, ...); =item sa_rc_t B(sa_t *I); =back +=head2 Socket Input/Output Operations (Stream Communication) + +=over 4 + +=item sa_rc_t B(sa_t *I, char *I, size_t I, size_t *I, sa_addr_t **I); + +=item sa_rc_t B(sa_t *I, const char *I, size_t I, size_t *I, sa_addr_t *I); + +=back + =head1 HISTORY B was invented in August 2001 by Ralf S. Engelschall for use Index: ossp-pkg/sa/sa_test.c RCS File: /v/ossp/cvs/ossp-pkg/sa/sa_test.c,v rcsdiff -q -kk '-r1.4' '-r1.5' -u '/v/ossp/cvs/ossp-pkg/sa/sa_test.c,v' 2>/dev/null --- sa_test.c 2001/10/05 12:56:06 1.4 +++ sa_test.c 2001/10/05 20:52:15 1.5 @@ -76,7 +76,7 @@ /* send message to syslogd(8) */ nBuf = strlen(caBuf); - if (sa_writeto(sa, caBuf, nBuf, (size_t *)&n, ra) != SA_OK) + if (sa_send(sa, caBuf, nBuf, (size_t *)&n, ra) != SA_OK) die("sa_writeto"); /* destroy socket and address objects */