--- l2_ut_sa.c 2001/10/02 14:11:51 1.13
+++ l2_ut_sa.c 2001/10/06 14:33:09 1.14
@@ -31,318 +31,432 @@
#include "l2_config.h"
/* 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 <stdio.h> /* for "s[n]printf()" */
+#include <stdlib.h> /* for "malloc()" & friends */
+#include <stdarg.h> /* for "va_xxx()" and "va_list" */
+#include <string.h> /* for "strxxx()" and "size_t" */
+#include <sys/types.h> /* for general prerequisites */
+#include <ctype.h> /* for isxxx() */
+#include <errno.h> /* for "EXXX" */
+#include <fcntl.h> /* for "F_XXX" and "O_XXX" */
+#include <unistd.h> /* for standard Unix stuff */
+#include <netdb.h> /* for "struct prototent" */
+#include <sys/time.h> /* for "struct timeval" */
+#include <sys/un.h> /* for "struct sockaddr_un" */
+#include <netinet/in.h> /* for "struct sockaddr_in[6]" */
+#include <sys/socket.h> /* for "AF_XXX" and "SOCK_XXX" */
+#include <arpa/inet.h> /* for "inet_xtoy" */
/* include own API header */
-#define SA_PREFIX l2_ut_
#include "l2_ut_sa.h"
/* socket address abstraction object */
struct sa_addr_st {
- struct sockaddr *saBuf;
- socklen_t slBuf;
- int nFamily;
- int nProto;
+ 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 {
- 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
+}
-/* 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)
+/* 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)
{
- int rv;
-
- rv = vsprintf(str, fmt, ap);
- if (rv > size) {
- fprintf(stderr, "ERROR: vsprintf(3) buffer overflow!\n");
- abort();
- }
-}
+ char *bufptr;
+ char *bufend;
+ /* sufficient integer buffer: <available-bits> x log_10(2) + safety */
+ char ibuf[((sizeof(int)*8)/3)+10];
+ 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 == '%')
+ /* 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);
+ if ((bufptr + n) > bufend)
+ n = bufend - bufptr;
+ memcpy(bufptr, cp, n);
+ bufptr += n;
+ }
+ else if (c == 'd') {
+ /* implement "%d" */
+ d = (int)va_arg(ap, int);
+#ifdef HAVE_SNPRINTF
+ snprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */
#else
-#error "neither vsnprintf nor vsprintf available"
+ sprintf(ibuf, "%d", d); /* implicitly secure */
#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);
+}
-/* 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, ...)
+/* 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;
- int rv;
- va_start(ap, fmt);
- rv = sa_vsnprintf(str, size, fmt, 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 rv;
+
+ return chars;
}
-#endif
-sa_rc_t sa_u2a(sa_addr_t **saa, const char *uri, ...)
+/* create address object */
+sa_rc_t sa_addr_create(sa_addr_t **saap)
+{
+ sa_addr_t *saa;
+
+ /* argument sanity check(s) */
+ if (saap == NULL)
+ return SA_ERR_ARG;
+
+ /* allocate and initialize new address object */
+ if ((saa = (sa_addr_t *)malloc(sizeof(sa_addr_t))) == NULL)
+ return SA_ERR_MEM;
+ 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;
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 *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;
/* on-the-fly create or just take over URI */
va_start(ap, uri);
- sa_vsnprintf(uribuf, sizeof(uribuf), uri, ap);
+ 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;
+ /* initialize result variables */
+ sa = NULL;
+ sl = 0;
+ sf = 0;
- /* resolve port */
- bNumeric = 1;
- for (i = 0; cpPort[i] != '\0'; i++) {
- if (!isdigit((int)cpPort[i])) {
- bNumeric = 0;
- break;
+ /* parse URI and resolve contents.
+ The following syntax is recognized:
+ - unix:<path>
+ - inet://<host>:<port>[#(tcp|udp)] */
+ uri = uribuf;
+ 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;
+
+ /* resolve port */
+ nPort = 0;
+ bNumeric = 1;
+ for (i = 0; cpPort[i] != '\0'; i++) {
+ if (!isdigit((int)cpPort[i])) {
+ bNumeric = 0;
+ break;
+ }
+ }
+ 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);
}
- }
- 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));
+ /* 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 (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) {
+ /* 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);
- 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) {
+ else if (sa_inet_pton(AF_INET6, cpHost, &sa6.sin6_addr.s6_addr) == 1) {
sa6.sin6_family = AF_INET6;
sa6.sin6_port = htons(nPort);
- memcpy(&sa6.sin6_addr.s6_addr, he->h_addr_list[0], sizeof(sa6.sin6_addr.s6_addr));
sa = (struct sockaddr *)&sa6;
sl = sizeof(sa6);
sf = AF_INET6;
}
#endif
+ else if ((he = gethostbyname(cpHost)) != NULL) {
+ if (he->h_addrtype == AF_INET) {
+ sa4.sin_family = AF_INET;
+ sa4.sin_port = htons(nPort);
+ memcpy(&sa4.sin_addr.s_addr, he->h_addr_list[0],
+ sizeof(sa4.sin_addr.s_addr));
+ sa = (struct sockaddr *)&sa4;
+ sl = sizeof(sa4);
+ 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);
+
+ /* 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;
- (*saa)->nProto = nProto;
+ memcpy(saa->saBuf, sa, sl);
+ saa->slBuf = sl;
+ saa->nFamily = sf;
+
return SA_OK;
}
-sa_rc_t sa_s2a(sa_addr_t **saa, const struct sockaddr *sabuf, socklen_t salen)
+/* 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;
struct sockaddr_in *sa4;
#ifdef AF_INET6
struct sockaddr_in6 *sa6;
#endif
- struct protoent *pe;
int sf;
+ /* argument sanity check(s) */
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);
+ 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;
+ memcpy(saa->saBuf, sabuf, salen);
+ saa->slBuf = salen;
- /* fill in family */
+ /* resolve family */
sf = 0;
- if (sizeof(struct sockaddr_in) == salen) {
+ 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
- else if (sizeof(struct sockaddr_in6) == salen) {
+ if (sf == 0 && sizeof(struct sockaddr_in6) == salen) {
sa6 = (struct sockaddr_in6 *)((void *)sabuf);
if (sa6->sin6_family == AF_INET6)
sf = AF_INET6;
}
#endif
- (*saa)->nFamily = sf;
+ if (sf == 0)
+ return SA_ERR_ARG;
+ 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)
+/* export address object into URI */
+sa_rc_t sa_addr_a2u(sa_addr_t *saa, char **uri)
{
char uribuf[1024];
- struct protoent *pe;
+ struct sockaddr_un *un;
struct sockaddr_in *sa4;
#ifdef AF_INET6
struct sockaddr_in6 *sa6;
@@ -350,159 +464,297 @@
char caHost[512];
int nPort;
+ /* argument sanity check(s) */
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);
+
+ /* 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);
}
#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);
- }
+ 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);
+ sa_inet_ntop(AF_INET, &sa4->sin_addr.s_addr, caHost, sizeof(caHost));
+ nPort = ntohs(sa4->sin_port);
+ }
+#ifdef AF_INET6
+ else {
+ sa6 = (struct sockaddr_in6 *)((void *)saa->saBuf);
+ sa_inet_ntop(AF_INET6, &sa6->sin6_addr.s6_addr, caHost, sizeof(caHost));
+ nPort = ntohs(sa6->sin6_port);
+ }
+#endif
+ sa_msnprintf(uribuf, sizeof(uribuf), "inet://%s:%d", caHost, nPort);
+ }
else
- return SA_ERR_ARG;
- sa_snprintf(uribuf, sizeof(uribuf), "%s://%s:%d", pe->p_name, caHost, nPort);
+ return SA_ERR_INT;
+
+ /* pass result to caller */
*uri = strdup(uribuf);
+
return SA_OK;
}
-sa_rc_t sa_a2s(const sa_addr_t *saa, struct sockaddr **sabuf, socklen_t *salen)
+/* 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;
}
-static sa_rc_t sa_socket_init(sa_t *sa, int family, int proto)
+/* internal lazy/delayed initialization of underlying socket */
+static sa_rc_t sa_socket_init(sa_t *sa, int nFamily)
{
- int type;
+ int nType;
+ int nProto;
+ struct protoent *pe;
+ char *cpProto;
+ /* argument sanity check(s) */
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)
+
+ /* only perform operation if socket still does not exist */
+ if (sa->fdSocket != -1)
return SA_ERR_USE;
- if ((sa->sSocket = socket(family, type, proto)) == -1)
+
+ /* 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_INT;
+
+ /* 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_INT;
+
+ /* 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->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;
- }
+ 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;
+
+ /* 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))
+ 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;
- 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_ERR_USE;
+
+ /* configure read/incoming buffer */
+ 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;
+ }
+
+ /* configure write/outgoing buffer */
+ 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;
}
+/* 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)
- if ((rv = sa_socket_init(sa, laddr->nFamily, laddr->nProto)) != SA_OK)
+
+ /* lazy creation of underlying socket */
+ if (sa->fdSocket == -1)
+ if ((rv = sa_socket_init(sa, laddr->nFamily)) != SA_OK)
return rv;
- if (bind(sa->sSocket, laddr->saBuf, laddr->slBuf) == -1)
+
+ /* 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);
+ }
+
+ /* 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;
@@ -510,87 +762,106 @@
socklen_t len;
sa_rc_t rv;
- if (sa == NULL)
+ /* argument sanity check(s) */
+ if (sa == NULL || raddr == NULL)
return SA_ERR_ARG;
- if (sa->sSocket == -1)
- if ((rv = sa_socket_init(sa, raddr->nFamily, raddr->nProto)) != SA_OK)
- return rv;
+ /* connecting is only possible for stream communication */
+ if (sa->eType != SA_TYPE_STREAM)
+ return SA_ERR_USE;
- if (!sa->bTimeout)
- return connect(sa->sSocket, raddr->saBuf, raddr->slBuf);
+ /* lazy creation of underlying socket */
+ if (sa->fdSocket == -1)
+ if ((rv = sa_socket_init(sa, raddr->nFamily)) != SA_OK)
+ return rv;
- error = 0;
rv = SA_OK;
+ if (!sa->bTimeout) {
+ /* 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;
- /* 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;
- if (sa->sSocket == -1)
- /* at least sa_bind() has to be already performed */
+
+ /* listening is only possible for stream communication */
+ if (sa->eType != SA_TYPE_STREAM)
+ return SA_ERR_USE;
+
+ /* 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;
@@ -605,35 +876,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;
- if (sa->sSocket == -1)
- /* at least sa_listen() has to be already performed */
+
+ /* accepting connections is only possible for stream communication */
+ if (sa->eType != SA_TYPE_STREAM)
return SA_ERR_USE;
+
+ /* 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;
- if ((rv = sa_s2a(caddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK)
+
+ /* 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) {
- free(*caddr);
+ 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;
@@ -645,16 +936,35 @@
} sa_buf;
socklen_t sa_len;
- if (sa == NULL)
+ /* argument sanity check(s) */
+ if (sa == NULL || raddr == NULL)
return SA_ERR_ARG;
+
+ /* 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;
+
+ /* determine remote address of underlying socket */
sa_len = sizeof(sa_buf);
- if (getpeername(sa->sSocket, (struct sockaddr *)&sa_buf, &sa_len) < 0)
+ if (getpeername(sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_len) < 0)
return SA_ERR_SYS;
- if ((rv = sa_s2a(raddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK)
+
+ /* 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;
@@ -666,147 +976,187 @@
} sa_buf;
socklen_t sa_len;
- if (sa == NULL)
+ /* argument sanity check(s) */
+ if (sa == NULL || laddr == NULL)
return SA_ERR_ARG;
+
+ /* 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;
- if ((rv = sa_s2a(laddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK)
+
+ /* 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;
- *fd = sa->sSocket;
+
+ /* if still no socket exists, say this explicitly */
+ if (sa->fdSocket == -1)
+ return SA_ERR_USE;
+
+ /* 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;
- int rv;
+ sa_rc_t rv;
size_t res;
+ /* argument sanity check(s) */
if (sa == NULL || cpBuf == NULL || nBufReq == 0)
- return 0;
-
- /* flush write buffer */
+ return SA_ERR_ARG;
+
+ /* reading is only possible for stream communication */
+ if (sa->eType != SA_TYPE_STREAM)
+ return SA_ERR_USE;
+
+ /* at least a connection has to exist */
+ if (sa->fdSocket == -1)
+ return SA_ERR_USE;
+
+ /* trigger a write buffer flush */
if (sa->nWriteLen > 0)
sa_flush(sa);
+
+ /* perform read operation */
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;
+ if (sa->nReadSize == 0) {
+ /* user-space unbuffered I/O */
+ res = sa_read_raw(sa, cpBuf, nBufReq);
+ if (res == 0)
+ rv = SA_ERR_EOF;
+ else if (res < 0)
+ rv = SA_ERR_SYS;
+ }
+ else {
+ /* user-space buffered I/O */
+ res = 0;
+ while (1) {
+ if (nBufReq <= sa->nReadLen) {
+ /* 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;
+ res += nBufReq;
}
else {
- /* fill buffer with new data */
- n = sa_read_raw(sa, sa->cpReadBuf, sa->nReadSize);
- if (n <= 0)
- rv = SA_ERR_SYS;
+ 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 = (res == 0 ? SA_ERR_EOF : SA_OK);
+ else if (n < 0)
+ rv = (res == 0 ? SA_ERR_SYS : SA_OK);
+ }
else {
- sa->nReadLen = n;
- continue;
+ /* fill buffer with new data */
+ n = sa_read_raw(sa, sa->cpReadBuf, sa->nReadSize);
+ 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; /* REPEAT OPERATION! */
+ }
}
}
+ break;
}
- 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->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;
+ 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;
+
+ /* 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);
@@ -817,131 +1167,187 @@
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;
- 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;
- }
+ /* writing is only possible for stream communication */
+ if (sa->eType != SA_TYPE_STREAM)
+ return SA_ERR_USE;
+
+ /* at least a connection has to exist */
+ if (sa->fdSocket == -1)
+ return SA_ERR_USE;
+
+ rv = SA_OK;
+ if (sa->nWriteSize == 0) {
+ /* user-space unbuffered I/O */
+ res = sa_write_raw(sa, cpBuf, nBufReq);
+ if (res < 0)
+ rv = SA_ERR_SYS;
}
else {
- /* (again) enough sprace in buffer, so store data */
- memmove(sa->cpWriteBuf+sa->nWriteLen, cpBuf, nBufReq);
- sa->nWriteLen += nBufReq;
- res = nBufReq;
+ /* user-space buffered I/O */
+ if (nBufReq > (sa->nWriteSize - sa->nWriteLen)) {
+ /* not enough space in buffer, so flush buffer first */
+ 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 = (res == 0 ? SA_ERR_SYS : SA_OK);
+ 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;
+ }
}
+
+ /* pass number of actually written bytes to caller */
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;
+ return rv;
}
-sa_rc_t sa_printf(sa_t *sa, const char *cpFmt, ...)
+/* write formatted string to socket (convinience function) */
+sa_rc_t sa_writef(sa_t *sa, const char *cpFmt, ...)
{
va_list ap;
size_t n;
char caBuf[1024];
+ sa_rc_t rv;
- if (sa == NULL)
+ /* argument sanity check(s) */
+ if (sa == NULL || cpFmt == NULL)
return SA_ERR_ARG;
+
+ /* writing is only possible for stream communication */
+ if (sa->eType != SA_TYPE_STREAM)
+ return SA_ERR_USE;
+
+ /* at least a connection has to exist */
+ if (sa->fdSocket == -1)
+ return SA_ERR_USE;
+
+ /* format string into temporary buffer */
va_start(ap, cpFmt);
- n = sa_vsnprintf(caBuf, sizeof(caBuf), cpFmt, ap);
- sa_write(sa, caBuf, n, &n);
+ n = sa_mvsnprintf(caBuf, sizeof(caBuf), cpFmt, ap);
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;
- 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;
+
+ /* flushing is only possible for stream communication */
+ if (sa->eType != SA_TYPE_STREAM)
+ return SA_ERR_USE;
+
+ /* 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);
+ sa->nWriteLen -= n;
+ }
+ sa->nWriteLen = 0;
}
- 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;
+
+ /* 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;
@@ -949,8 +1355,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;
}
|