--- l2_ut_sa.c 2001/10/06 14:33:09 1.14
+++ l2_ut_sa.c 2001/10/10 15:58:27 1.15
@@ -1,7 +1,8 @@
/*
** 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/)
+** Copyright (c) 2001 Ralf S. Engelschall <rse@engelschall.com>
+** 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/.
@@ -33,10 +34,10 @@
/* include system API headers */
#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 <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 <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 */
@@ -45,30 +46,136 @@
#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 <arpa/inet.h> /* for "inet_XtoX" */
/* include own API header */
#include "l2_ut_sa.h"
-/* socket address abstraction object */
-struct sa_addr_st {
- int nFamily; /* the socket family (AF_XXX) */
- struct sockaddr *saBuf; /* the "struct sockaddr_xx" actually */
- socklen_t slBuf; /* the length of "struct sockaddr_xx" */
-};
-
+/* system call structure declaration macros */
+#define SA_SC_DECLARE_0(rc_t, fn) \
+ struct { \
+ union { void (*any)(); \
+ rc_t (*std)(void); \
+ rc_t (*ctx)(void *); } fptr; \
+ void *fctx; \
+ } sc_##fn;
+#define SA_SC_DECLARE_1(rc_t, fn, a1_t) \
+ struct { \
+ union { void (*any)(); \
+ rc_t (*std)(a1_t); \
+ rc_t (*ctx)(void *, a1_t); } fptr; \
+ void *fctx; \
+ } sc_##fn;
+#define SA_SC_DECLARE_2(rc_t, fn, a1_t, a2_t) \
+ struct { \
+ union { void (*any)(); \
+ rc_t (*std)(a1_t, a2_t); \
+ rc_t (*ctx)(void *, a1_t, a2_t); } fptr; \
+ void *fctx; \
+ } sc_##fn;
+#define SA_SC_DECLARE_3(rc_t, fn, a1_t, a2_t, a3_t) \
+ struct { \
+ union { void (*any)(); \
+ rc_t (*std)(a1_t, a2_t, a3_t); \
+ rc_t (*ctx)(void *, a1_t, a2_t, a3_t); } fptr; \
+ void *fctx; \
+ } sc_##fn;
+#define SA_SC_DECLARE_4(rc_t, fn, a1_t, a2_t, a3_t, a4_t) \
+ struct { \
+ union { void (*any)(); \
+ rc_t (*std)(a1_t, a2_t, a3_t, a4_t); \
+ rc_t (*ctx)(void *, a1_t, a2_t, a3_t, a4_t); } fptr; \
+ void *fctx; \
+ } sc_##fn;
+#define SA_SC_DECLARE_5(rc_t, fn, a1_t, a2_t, a3_t, a4_t, a5_t) \
+ struct { \
+ union { void (*any)(); \
+ rc_t (*std)(a1_t, a2_t, a3_t, a4_t, a5_t); \
+ rc_t (*ctx)(void *, a1_t, a2_t, a3_t, a4_t, a5_t); } fptr; \
+ void *fctx; \
+ } sc_##fn;
+#define SA_SC_DECLARE_6(rc_t, fn, a1_t, a2_t, a3_t, a4_t, a5_t, a6_t) \
+ struct { \
+ union { void (*any)(); \
+ rc_t (*std)(a1_t, a2_t, a3_t, a4_t, a5_t, a6_t); \
+ rc_t (*ctx)(void *, a1_t, a2_t, a3_t, a4_t, a5_t, a6_t); } fptr; \
+ void *fctx; \
+ } sc_##fn;
+
+/* system call structure assignment macro */
+#define SA_SC_ASSIGN(sa, fn, ptr, ctx) \
+ do { \
+ (sa)->scSysCall.sc_##fn.fptr.any = (void (*)())(ptr); \
+ (sa)->scSysCall.sc_##fn.fctx = (ctx); \
+ } while(0)
+
+/* system call structure assignment macro */
+#define SA_SC_COPY(sa1, sa2, fn) \
+ do { \
+ (sa1)->scSysCall.sc_##fn.fptr.any = (sa2)->scSysCall.sc_##fn.fptr.any; \
+ (sa1)->scSysCall.sc_##fn.fctx = (sa2)->scSysCall.sc_##fn.fctx; \
+ } while(0)
+
+/* system call function call macros */
+#define SA_SC_CALL_0(sa, fn) \
+ ( (sa)->scSysCall.sc_##fn.fctx != NULL \
+ ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx) \
+ : ((sa)->scSysCall.sc_##fn.fptr.std)() )
+#define SA_SC_CALL_1(sa, fn, a1) \
+ ( (sa)->scSysCall.sc_##fn.fctx != NULL \
+ ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1) \
+ : ((sa)->scSysCall.sc_##fn.fptr.std)(a1) )
+#define SA_SC_CALL_2(sa, fn, a1, a2) \
+ ( (sa)->scSysCall.sc_##fn.fctx != NULL \
+ ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1, a2) \
+ : ((sa)->scSysCall.sc_##fn.fptr.std)(a1, a2) )
+#define SA_SC_CALL_3(sa, fn, a1, a2, a3) \
+ ( (sa)->scSysCall.sc_##fn.fctx != NULL \
+ ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1, a2, a3) \
+ : ((sa)->scSysCall.sc_##fn.fptr.std)(a1, a2, a3) )
+#define SA_SC_CALL_4(sa, fn, a1, a2, a3, a4) \
+ ( (sa)->scSysCall.sc_##fn.fctx != NULL \
+ ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1, a2, a3, a4) \
+ : ((sa)->scSysCall.sc_##fn.fptr.std)(a1, a2, a3, a4) )
+#define SA_SC_CALL_5(sa, fn, a1, a2, a3, a4, a5) \
+ ( (sa)->scSysCall.sc_##fn.fctx != NULL \
+ ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1, a2, a3, a4, a5) \
+ : ((sa)->scSysCall.sc_##fn.fptr.std)(a1, a2, a3, a4, a5) )
+#define SA_SC_CALL_6(sa, fn, a1, a2, a3, a4, a5, a6) \
+ ( (sa)->scSysCall.sc_##fn.fctx != NULL \
+ ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1, a2, a3, a4, a5, a6) \
+ : ((sa)->scSysCall.sc_##fn.fptr.std)(a1, a2, a3, a4, a5, a6) )
+
+/* system call table */
+typedef struct {
+ SA_SC_DECLARE_3(int, connect, int, const struct sockaddr *, socklen_t)
+ SA_SC_DECLARE_3(int, accept, int, struct sockaddr *, socklen_t *)
+ SA_SC_DECLARE_5(int, select, int, fd_set *, fd_set *, fd_set *, struct timeval *)
+ SA_SC_DECLARE_3(ssize_t, read, int, void *, size_t)
+ SA_SC_DECLARE_3(ssize_t, write, int, const void *, size_t)
+ SA_SC_DECLARE_6(ssize_t, recvfrom, int, void *, size_t, int, struct sockaddr *, socklen_t *)
+ SA_SC_DECLARE_6(ssize_t, sendto, int, const void *, size_t, int, const struct sockaddr *, socklen_t)
+} sa_syscall_tab_t;
+
/* socket abstraction object */
struct sa_st {
- 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 */
+ sa_type_t eType; /* socket type (stream or datagram) */
+ int fdSocket; /* socket file descriptor */
+ struct timeval tvTimeout[4]; /* timeout values (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 */
+ sa_syscall_tab_t scSysCall; /* table of system calls */
+};
+
+/* socket address abstraction object */
+struct sa_addr_st {
+ int nFamily; /* the socket family (AF_XXX) */
+ struct sockaddr *saBuf; /* the "struct sockaddr_xx" actually */
+ socklen_t slBuf; /* the length of "struct sockaddr_xx" */
};
/* boolean values */
@@ -79,6 +186,10 @@
#define TRUE (!FALSE)
#endif
+/* handy struct timeval check */
+#define SA_TVISZERO(tv) \
+ ((tv).tv_sec == 0 && (tv).tv_usec == 0)
+
/* backward compatibility for AF_LOCAL */
#if !defined(AF_LOCAL) && defined(AF_UNIX)
#define AF_LOCAL AF_UNIX
@@ -132,62 +243,117 @@
#endif
}
-/* 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)
+/* minimal output-independent vprintf(3) variant which supports %{c,s,d,%} only */
+static int sa_mvxprintf(int (*output)(void *ctx, const char *buffer, size_t bufsize), void *ctx, const char *format, va_list ap)
{
- 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;
+ int bytes;
- 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);
+ if (format == NULL || ap == NULL)
+ return -1;
+ bytes = 0;
+ while (*format != '\0') {
+ if (*format == '%') {
+ c = *(format+1);
+ if (c == '%') {
+ /* expand "%%" */
+ cp = &c;
+ n = sizeof(char);
+ }
+ else if (c == 'c') {
+ /* expand "%c" */
+ c = (char)va_arg(ap, int);
+ cp = &c;
+ n = sizeof(char);
+ }
else if (c == 's') {
- /* implement "%s" */
+ /* expand "%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" */
+ /* expand "%d" */
d = (int)va_arg(ap, int);
#ifdef HAVE_SNPRINTF
snprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */
#else
sprintf(ibuf, "%d", d); /* implicitly secure */
#endif
- n = strlen(ibuf);
- memcpy(bufptr, ibuf, n);
- bufptr += n;
+ cp = ibuf;
+ n = strlen(cp);
}
else {
- *bufptr++ = '%';
- if (bufptr < bufend)
- *bufptr++ = c;
+ /* any other "%X" */
+ cp = (char *)format;
+ n = 2;
}
+ format += 2;
}
- else
- *bufptr++ = c;
+ else {
+ /* plain text */
+ cp = (char *)format;
+ if ((format = strchr(cp, '%')) == NULL)
+ format = strchr(cp, '\0');
+ n = format - cp;
+ }
+ /* perform output operation */
+ if (output != NULL)
+ if ((n = output(ctx, cp, n)) == -1)
+ break;
+ bytes += n;
}
- *bufptr = '\0';
- return (bufptr - buffer);
+ return bytes;
+}
+
+/* output callback function context for sa_mvsnprintf() */
+typedef struct {
+ char *bufptr;
+ size_t buflen;
+} sa_mvsnprintf_cb_t;
+
+/* output callback function for sa_mvsnprintf() */
+static int sa_mvsnprintf_cb(void *_ctx, const char *buffer, size_t bufsize)
+{
+ sa_mvsnprintf_cb_t *ctx = (sa_mvsnprintf_cb_t *)_ctx;
+
+ if (bufsize > ctx->buflen)
+ return -1;
+ memcpy(ctx->bufptr, buffer, bufsize);
+ ctx->bufptr += bufsize;
+ ctx->buflen -= bufsize;
+ return bufsize;
+}
+
+/* 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 n;
+ sa_mvsnprintf_cb_t ctx;
+
+ if (format == NULL || ap == NULL)
+ return -1;
+ if (buffer != NULL && bufsize == 0)
+ return -1;
+ if (buffer == NULL)
+ /* just determine output length */
+ n = sa_mvxprintf(NULL, NULL, format, ap);
+ else {
+ /* perform real output */
+ ctx.bufptr = buffer;
+ ctx.buflen = bufsize - 1;
+ n = sa_mvxprintf(sa_mvsnprintf_cb, &ctx, format, ap);
+ }
+ if (n != -1 && ctx.buflen == 0)
+ n = -1;
+ if (n != -1)
+ *(ctx.bufptr) = '\0';
+ return n;
}
/* minimal snprintf(3) variant which supports %{c,s,d} only */
@@ -196,10 +362,6 @@
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);
@@ -292,16 +454,23 @@
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 == 0)
+ return SA_ERR_ARG;
if ((n+1) > sizeof(un.sun_path))
return SA_ERR_MEM;
+ if (cpPath[0] != '/') {
+ if (getcwd(un.sun_path, sizeof(un.sun_path)-(n+1)) == NULL)
+ return SA_ERR_MEM;
+ cp = un.sun_path + strlen(un.sun_path);
+ }
+ else
+ cp = un.sun_path;
memcpy(un.sun_path, cpPath, n+1);
un.sun_family = AF_LOCAL;
@@ -313,7 +482,7 @@
else if (strncmp(uri, "inet://", 7) == 0) {
/* parse URI */
cpHost = (char *)(uri+7);
- if ((cp = strchr(cpHost, ':')) == NULL)
+ if ((cp = strrchr(cpHost, ':')) == NULL)
return SA_ERR_ARG;
*cp++ = '\0';
cpPort = cp;
@@ -385,12 +554,14 @@
sf = AF_INET6;
}
#endif
+ else
+ return SA_ERR_ARG;
}
+ else
+ return SA_ERR_ARG;
}
-
- /* make sure result variables are set */
- if (sa == NULL || sl == 0 || sf == 0)
- return SA_ERR_INT;
+ else
+ return SA_ERR_ARG;
/* fill-in result address structure */
if (saa->saBuf != NULL)
@@ -517,6 +688,104 @@
return SA_OK;
}
+sa_rc_t sa_addr_match(sa_addr_t *saa1, sa_addr_t *saa2, int prefixlen)
+{
+ const unsigned char *ucp1, *ucp2;
+ unsigned int uc1, uc2, mask;
+ size_t l1, l2;
+ int nFamily;
+ int nBytes;
+ int nBits;
+
+ /* argument sanity check(s) */
+ if (saa1 == NULL || saa2 == NULL || prefixlen < -1)
+ return SA_ERR_ARG;
+
+ /* match short circuiting */
+ if (saa1->nFamily != saa2->nFamily)
+ return SA_ERR_MTC;
+
+ /* determine address representation pointer and size */
+ nFamily = saa1->nFamily;
+ if (nFamily == AF_LOCAL) {
+ ucp1 = (const unsigned char *)(((struct sockaddr_un *)saa1->saBuf)->sun_path);
+ ucp2 = (const unsigned char *)(((struct sockaddr_un *)saa2->saBuf)->sun_path);
+ l1 = strlen(((struct sockaddr_un *)saa1->saBuf)->sun_path) * 8;
+ l2 = strlen(((struct sockaddr_un *)saa2->saBuf)->sun_path) * 8;
+ if (prefixlen == -1) {
+ if (l1 != l2)
+ return SA_ERR_MTC;
+ nBits = l1;
+ }
+ else {
+ if (l1 < prefixlen || l2 < prefixlen)
+ return SA_ERR_MTC;
+ nBits = prefixlen;
+ }
+ }
+ else if (nFamily == AF_INET) {
+ ucp1 = (const unsigned char *)&(((struct sockaddr_in *)saa1->saBuf)->sin_addr);
+ ucp2 = (const unsigned char *)&(((struct sockaddr_in *)saa2->saBuf)->sin_addr);
+ nBits = 32;
+ }
+#ifdef AF_INET6
+ else if (nFamily == AF_INET6) {
+ ucp1 = (const unsigned char *)&(((struct sockaddr_in6 *)saa1->saBuf)->sin6_addr);
+ ucp2 = (const unsigned char *)&(((struct sockaddr_in6 *)saa2->saBuf)->sin6_addr);
+ nBits = 128;
+#endif
+ }
+ else
+ return SA_ERR_INT;
+
+ /* make sure we do not compare than possible */
+ if (prefixlen > nBits)
+ return SA_ERR_ARG;
+
+ /* support equal matching (= all bits) */
+ if (prefixlen == -1)
+ prefixlen = nBits;
+
+ /* perform address representation comparison
+ (assumption guaranteed by API: network byte order is used) */
+ nBytes = (prefixlen / 8);
+ nBits = (prefixlen % 8);
+ if (nBytes > 0) {
+ if (memcmp(ucp1, ucp2, nBytes) != 0)
+ return SA_ERR_MTC;
+ }
+ if (nBits > 0) {
+ uc1 = ucp1[nBytes];
+ uc2 = ucp2[nBytes];
+ mask = (0xFF << (8-nBits)) & 0xFF;
+ if ((uc1 & mask) != (uc2 & mask))
+ return SA_ERR_MTC;
+ }
+ return SA_OK;
+}
+
+/* set timeouts if timeouts or done in kernel */
+static sa_rc_t sa_socket_settimeouts(sa_t *sa)
+{
+#if defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO)
+ if (sa->fdSocket != -1) {
+ if (SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_READ])) {
+ if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_RCVTIMEO,
+ &sa->tvTimeout[SA_TIMEOUT_READ],
+ sizeof(sa->tvTimeout[SA_TIMEOUT_READ])) < 0)
+ return SA_ERR_SYS;
+ }
+ if (SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_WRITE])) {
+ if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_SNDTIMEO,
+ &sa->tvTimeout[SA_TIMEOUT_WRITE],
+ sizeof(sa->tvTimeout[SA_TIMEOUT_WRITE])) < 0)
+ return SA_ERR_SYS;
+ }
+ }
+#endif
+ return SA_OK;
+}
+
/* internal lazy/delayed initialization of underlying socket */
static sa_rc_t sa_socket_init(sa_t *sa, int nFamily)
{
@@ -566,6 +835,9 @@
if ((sa->fdSocket = socket(nFamily, nType, nProto)) == -1)
return SA_ERR_SYS;
+ /* optionally set kernel timeouts */
+ sa_socket_settimeouts(sa);
+
return SA_OK;
}
@@ -591,6 +863,7 @@
sa_rc_t sa_create(sa_t **sap)
{
sa_t *sa;
+ int i;
/* argument sanity check(s) */
if (sap == NULL)
@@ -599,17 +872,31 @@
/* allocate and initialize socket object */
if ((sa = (sa_t *)malloc(sizeof(sa_t))) == NULL)
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;
+
+ /* init object attributes */
+ sa->eType = SA_TYPE_STREAM;
+ sa->fdSocket = -1;
+ sa->nReadLen = 0;
+ sa->nReadSize = 0;
+ sa->cpReadBuf = NULL;
+ sa->nWriteLen = 0;
+ sa->nWriteSize = 0;
+ sa->cpWriteBuf = NULL;
+
+ /* init timeval object attributes */
+ for (i = 0; i < (sizeof(sa->tvTimeout)/sizeof(sa->tvTimeout[0])); i++) {
+ sa->tvTimeout[i].tv_sec = 0;
+ sa->tvTimeout[i].tv_usec = 0;
+ }
+
+ /* init syscall object attributes */
+ SA_SC_ASSIGN(sa, connect, connect, NULL);
+ SA_SC_ASSIGN(sa, accept, accept, NULL);
+ SA_SC_ASSIGN(sa, select, select, NULL);
+ SA_SC_ASSIGN(sa, read, read, NULL);
+ SA_SC_ASSIGN(sa, write, write, NULL);
+ SA_SC_ASSIGN(sa, recvfrom, recvfrom, NULL);
+ SA_SC_ASSIGN(sa, sendto, sendto, NULL);
/* pass object to caller */
*sap = sa;
@@ -657,25 +944,33 @@
}
/* configure I/O timeout */
-sa_rc_t sa_timeout(sa_t *sa, long sec, long usec)
+sa_rc_t sa_timeout(sa_t *sa, sa_timeout_t id, long sec, long usec)
{
+ int i;
+
/* argument sanity check(s) */
if (sa == NULL)
return SA_ERR_ARG;
- /* 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;
+ if (id == SA_TIMEOUT_ALL) {
+ for (i = 0; i < (sizeof(sa->tvTimeout)/sizeof(sa->tvTimeout[0])); i++) {
+ sa->tvTimeout[i].tv_sec = sec;
+ sa->tvTimeout[i].tv_usec = usec;
+ }
+ }
+ else {
+ sa->tvTimeout[id].tv_sec = sec;
+ sa->tvTimeout[id].tv_usec = usec;
+ }
+
+ /* optionally set kernel timeouts */
+ sa_socket_settimeouts(sa);
return SA_OK;
}
/* configure I/O buffers */
-sa_rc_t sa_buffers(sa_t *sa, size_t rsize, size_t wsize)
+sa_rc_t sa_buffer(sa_t *sa, sa_buffer_t id, size_t size)
{
char *cp;
@@ -683,46 +978,74 @@
if (sa == NULL)
return SA_ERR_ARG;
- /* make sure buffered were already flushed sufficiently */
- if (sa->nReadLen > rsize || sa->nWriteLen > 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;
+ if (id == SA_BUFFER_READ) {
+ /* configure read/incoming buffer */
+ if (sa->nReadLen > size)
+ return SA_ERR_USE;
+ if (size > 0) {
+ if (sa->cpReadBuf == NULL)
+ cp = (char *)malloc(size);
+ else
+ cp = (char *)realloc(sa->cpReadBuf, size);
+ if (cp == NULL)
+ return SA_ERR_SYS;
+ sa->cpReadBuf = cp;
+ sa->nReadSize = size;
+ }
+ else {
+ if (sa->cpReadBuf != NULL)
+ free(sa->cpReadBuf);
+ sa->cpReadBuf = NULL;
+ sa->nReadSize = 0;
+ }
}
- else {
- if (sa->cpReadBuf != NULL)
- free(sa->cpReadBuf);
- sa->cpReadBuf = NULL;
- sa->nReadSize = 0;
+ else if (id == SA_BUFFER_WRITE) {
+ /* configure write/outgoing buffer */
+ if (sa->nWriteLen > size)
+ return SA_ERR_USE;
+ if (size > 0) {
+ if (sa->cpWriteBuf == NULL)
+ cp = (char *)malloc(size);
+ else
+ cp = (char *)realloc(sa->cpWriteBuf, size);
+ if (cp == NULL)
+ return SA_ERR_SYS;
+ sa->cpWriteBuf = cp;
+ sa->nWriteSize = size;
+ }
+ else {
+ if (sa->cpWriteBuf != NULL)
+ free(sa->cpWriteBuf);
+ sa->cpWriteBuf = NULL;
+ sa->nWriteSize = 0;
+ }
}
+ return SA_ERR_ARG;
- /* 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;
+}
+/* override system call */
+sa_rc_t sa_syscall(sa_t *sa, sa_syscall_t id, void (*fptr)(), void *fctx)
+{
+ if (sa == NULL || fptr == NULL)
+ return SA_ERR_ARG;
+ if (id == SA_SYSCALL_CONNECT)
+ SA_SC_ASSIGN(sa, connect, fptr, fctx);
+ else if (id == SA_SYSCALL_ACCEPT)
+ SA_SC_ASSIGN(sa, accept, fptr, fctx);
+ else if (id == SA_SYSCALL_SELECT)
+ SA_SC_ASSIGN(sa, select, fptr, fctx);
+ else if (id == SA_SYSCALL_READ)
+ SA_SC_ASSIGN(sa, read, fptr, fctx);
+ else if (id == SA_SYSCALL_WRITE)
+ SA_SC_ASSIGN(sa, write, fptr, fctx);
+ else if (id == SA_SYSCALL_RECVFROM)
+ SA_SC_ASSIGN(sa, recvfrom, fptr, fctx);
+ else if (id == SA_SYSCALL_SENDTO)
+ SA_SC_ASSIGN(sa, sendto, fptr, fctx);
+ else
+ return SA_ERR_ARG;
return SA_OK;
}
@@ -776,9 +1099,9 @@
return rv;
rv = SA_OK;
- if (!sa->bTimeout) {
+ if (SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_CONNECT])) {
/* standard/non-timeout-aware connect operation */
- if (connect(sa->fdSocket, raddr->saBuf, raddr->slBuf) < 0)
+ if (SA_SC_CALL_3(sa, connect, sa->fdSocket, raddr->saBuf, raddr->slBuf) < 0)
rv = SA_ERR_SYS;
}
else {
@@ -790,7 +1113,7 @@
fcntl(sa->fdSocket, F_SETFL, flags|O_NONBLOCK);
/* perform the connect operation */
- if ((n = connect(sa->fdSocket, raddr->saBuf, raddr->slBuf)) < 0) {
+ if ((n = SA_SC_CALL_3(sa, connect, sa->fdSocket, raddr->saBuf, raddr->slBuf)) < 0) {
if (errno != EINPROGRESS) {
error = errno;
goto done;
@@ -807,7 +1130,8 @@
FD_SET(sa->fdSocket, &rset);
FD_SET(sa->fdSocket, &wset);
do {
- n = select(sa->fdSocket+1, &rset, &wset, NULL, &sa->tvTimeout);
+ n = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &rset, &wset, NULL,
+ &sa->tvTimeout[SA_TIMEOUT_CONNECT]);
} while (n == -1 && errno == EINTR);
/* decide on return semantic */
@@ -816,7 +1140,8 @@
goto done;
}
else if (n == 0) {
- error = ETIMEDOUT;
+ close(sa->fdSocket); /* stop TCP three-way handshake */
+ rv = SA_ERR_TMT;
goto done;
}
@@ -832,6 +1157,7 @@
/* optionally set errno */
if (error != 0) {
+ close(sa->fdSocket); /* just in case */
errno = error;
rv = SA_ERR_SYS;
}
@@ -875,6 +1201,7 @@
} sa_buf;
socklen_t sa_len;
int s;
+ int i;
/* argument sanity check(s) */
if (sa == NULL || caddr == NULL || csa == NULL)
@@ -889,21 +1216,22 @@
return SA_ERR_USE;
/* if timeout is enabled, perform a smart-blocking wait */
- if (sa->bTimeout) {
+ if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_ACCEPT])) {
FD_ZERO(&fds);
FD_SET(sa->fdSocket, &fds);
do {
- n = select(sa->fdSocket+1, &fds, NULL, NULL, &sa->tvTimeout);
+ n = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &fds, NULL, NULL,
+ &sa->tvTimeout[SA_TIMEOUT_ACCEPT]);
} while (n == -1 && errno == EINTR);
if (n == 0)
- errno = ETIMEDOUT;
+ return SA_ERR_TMT;
if (n <= 0)
return SA_ERR_SYS;
}
/* perform accept operation on underlying socket */
sa_len = sizeof(sa_buf);
- if ((s = accept(sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_len)) == -1)
+ if ((s = SA_SC_CALL_3(sa, accept, sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_len)) == -1)
return SA_ERR_SYS;
/* create result address object */
@@ -919,8 +1247,25 @@
sa_addr_destroy(*caddr);
return rv;
}
+
+ /* fill-in child socket */
(*csa)->fdSocket = s;
+ /* copy-over original system calls */
+ SA_SC_COPY((*csa), sa, connect);
+ SA_SC_COPY((*csa), sa, accept);
+ SA_SC_COPY((*csa), sa, select);
+ SA_SC_COPY((*csa), sa, read);
+ SA_SC_COPY((*csa), sa, write);
+ SA_SC_COPY((*csa), sa, recvfrom);
+ SA_SC_COPY((*csa), sa, sendto);
+
+ /* copy-over original timeout values */
+ for (i = 0; i < (sizeof(sa->tvTimeout)/sizeof(sa->tvTimeout[0])); i++) {
+ (*csa)->tvTimeout[i].tv_sec = sa->tvTimeout[i].tv_sec;
+ (*csa)->tvTimeout[i].tv_usec = sa->tvTimeout[i].tv_usec;
+ }
+
return SA_OK;
}
@@ -1021,27 +1366,37 @@
static int sa_read_raw(sa_t *sa, char *cpBuf, int nBufLen)
{
int rv;
+#if !(defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO))
fd_set fds;
+#endif
/* if timeout is enabled, perform explicit/smart blocking instead
of implicitly/hard blocking in the read(2) system call */
- if (sa->bTimeout) {
+#if !(defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO))
+ if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_READ])) {
FD_ZERO(&fds);
FD_SET(sa->fdSocket, &fds);
do {
- rv = select(sa->fdSocket+1, &fds, NULL, NULL, &sa->tvTimeout);
+ rv = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &fds, NULL, NULL,
+ &sa->tvTimeout[SA_TIMEOUT_READ]);
} while (rv == -1 && errno == EINTR);
if (rv == 0) {
errno = ETIMEDOUT;
return -1;
}
}
+#endif
/* perform read operation on underlying socket */
do {
- rv = read(sa->fdSocket, cpBuf, nBufLen);
+ rv = SA_SC_CALL_3(sa, read, sa->fdSocket, cpBuf, nBufLen);
} while (rv == -1 && errno == EINTR);
+#if defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO)
+ if (rv == -1 && errno == EWOULDBLOCK)
+ errno = ETIMEDOUT;
+#endif
+
return rv;
}
@@ -1075,6 +1430,8 @@
res = sa_read_raw(sa, cpBuf, nBufReq);
if (res == 0)
rv = SA_ERR_EOF;
+ else if (res < 0 && errno == ETIMEDOUT)
+ rv = SA_ERR_TMT;
else if (res < 0)
rv = SA_ERR_SYS;
}
@@ -1105,13 +1462,18 @@
res += n;
else if (n == 0)
rv = (res == 0 ? SA_ERR_EOF : SA_OK);
+ else if (n < 0 && errno == ETIMEDOUT)
+ rv = (res == 0 ? SA_ERR_TMT : 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)
+ if (n < 0 && errno == ETIMEDOUT)
+ /* timeout on this read, but perhaps ok as a whole */
+ rv = (res == 0 ? SA_ERR_TMT : SA_OK);
+ else if (n < 0)
/* error on this read, but perhaps ok as a whole */
rv = (res == 0 ? SA_ERR_SYS : SA_OK);
if (n == 0)
@@ -1179,27 +1541,37 @@
static int sa_write_raw(sa_t *sa, const char *cpBuf, int nBufLen)
{
int rv;
+#if !(defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO))
fd_set fds;
+#endif
/* if timeout is enabled, perform explicit/smart blocking instead
of implicitly/hard blocking in the write(2) system call */
- if (sa->bTimeout) {
+#if !(defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO))
+ if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_WRITE])) {
FD_ZERO(&fds);
FD_SET(sa->fdSocket, &fds);
do {
- rv = select(sa->fdSocket+1, NULL, &fds, NULL, &sa->tvTimeout);
+ rv = SA_SC_CALL_5(sa, select, sa->fdSocket+1, NULL, &fds, NULL,
+ &sa->tvTimeout[SA_TIMEOUT_WRITE]);
} while (rv == -1 && errno == EINTR);
if (rv == 0) {
errno = ETIMEDOUT;
return -1;
}
}
+#endif
/* perform write operation on underlying socket */
do {
- rv = write(sa->fdSocket, cpBuf, nBufLen);
+ rv = SA_SC_CALL_3(sa, write, sa->fdSocket, cpBuf, nBufLen);
} while (rv == -1 && errno == EINTR);
+#if defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO)
+ if (rv == -1 && errno == EWOULDBLOCK)
+ errno = ETIMEDOUT;
+#endif
+
return rv;
}
@@ -1226,7 +1598,9 @@
if (sa->nWriteSize == 0) {
/* user-space unbuffered I/O */
res = sa_write_raw(sa, cpBuf, nBufReq);
- if (res < 0)
+ if (res < 0 && errno == ETIMEDOUT)
+ rv = SA_ERR_TMT;
+ else if (res < 0)
rv = SA_ERR_SYS;
}
else {
@@ -1240,7 +1614,9 @@
/* buffer too small at all, so write immediately */
while (nBufReq > 0) {
n = sa_write_raw(sa, cpBuf, nBufReq);
- if (n < 0)
+ if (n < 0 && errno == ETIMEDOUT)
+ rv = (res == 0 ? SA_ERR_TMT : SA_OK);
+ else if (n < 0)
rv = (res == 0 ? SA_ERR_SYS : SA_OK);
if (n <= 0)
break;
@@ -1264,13 +1640,29 @@
return rv;
}
+/* output callback function context for sa_writef() */
+typedef struct {
+ sa_t *sa;
+ sa_rc_t rv;
+} sa_writef_cb_t;
+
+/* output callback function for sa_writef() */
+static int sa_writef_cb(void *_ctx, const char *buffer, size_t bufsize)
+{
+ size_t n;
+ sa_writef_cb_t *ctx = (sa_writef_cb_t *)_ctx;
+
+ if ((ctx->rv = sa_write(ctx->sa, buffer, bufsize, &n)) != SA_OK)
+ n = -1;
+ return n;
+}
+
/* 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;
+ sa_writef_cb_t ctx;
/* argument sanity check(s) */
if (sa == NULL || cpFmt == NULL)
@@ -1286,13 +1678,12 @@
/* format string into temporary buffer */
va_start(ap, cpFmt);
- n = sa_mvsnprintf(caBuf, sizeof(caBuf), cpFmt, ap);
+ ctx.sa = sa;
+ ctx.rv = SA_OK;
+ n = sa_mvxprintf(sa_writef_cb, &ctx, cpFmt, ap);
va_end(ap);
- /* write result buffer to socket */
- rv = sa_write(sa, caBuf, n, &n);
-
- return rv;
+ return ctx.rv;
}
/* flush write/outgoing I/O buffer */
@@ -1318,7 +1709,9 @@
if (sa->nWriteSize > 0) {
while (sa->nWriteLen > 0) {
n = sa_write_raw(sa, sa->cpWriteBuf, sa->nWriteLen);
- if (n < 0)
+ if (n < 0 && errno == ETIMEDOUT)
+ rv = SA_ERR_TMT;
+ else if (n < 0)
rv = SA_ERR_SYS;
if (n <= 0)
break;
@@ -1393,11 +1786,12 @@
/* if timeout is enabled, perform explicit/smart blocking instead
of implicitly/hard blocking in the recvfrom(2) system call */
- if (sa->bTimeout) {
+ if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_READ])) {
FD_ZERO(&fds);
FD_SET(sa->fdSocket, &fds);
do {
- n = select(sa->fdSocket+1, &fds, NULL, NULL, &sa->tvTimeout);
+ n = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &fds, NULL, NULL,
+ &sa->tvTimeout[SA_TIMEOUT_READ]);
} while (n == -1 && errno == EINTR);
if (n == 0)
errno = ETIMEDOUT;
@@ -1407,8 +1801,8 @@
/* 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)
+ if ((n = SA_SC_CALL_6(sa, recvfrom, sa->fdSocket, buf, buflen, 0,
+ (struct sockaddr *)&sa_buf, &sa_len)) == -1)
return SA_ERR_SYS;
/* create result address object */
@@ -1448,11 +1842,12 @@
/* if timeout is enabled, perform explicit/smart blocking instead
of implicitly/hard blocking in the sendto(2) system call */
- if (sa->bTimeout) {
+ if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_WRITE])) {
FD_ZERO(&fds);
FD_SET(sa->fdSocket, &fds);
do {
- n = select(sa->fdSocket+1, NULL, &fds, NULL, &sa->tvTimeout);
+ n = SA_SC_CALL_5(sa, select, sa->fdSocket+1, NULL, &fds, NULL,
+ &sa->tvTimeout[SA_TIMEOUT_WRITE]);
} while (n == -1 && errno == EINTR);
if (n == 0)
errno = ETIMEDOUT;
@@ -1461,7 +1856,7 @@
}
/* perform send operation on underlying socket */
- if ((n = sendto(sa->fdSocket, buf, buflen, 0, raddr->saBuf, raddr->slBuf)) == -1)
+ if ((n = SA_SC_CALL_6(sa, sendto, sa->fdSocket, buf, buflen, 0, raddr->saBuf, raddr->slBuf)) == -1)
return SA_ERR_SYS;
/* pass actual number of sent bytes to caller */
@@ -1471,3 +1866,22 @@
return SA_OK;
}
+/* return error string */
+char *sa_error(sa_rc_t rv)
+{
+ char *sz;
+
+ /* translate result value into corresponding string */
+ if (rv == SA_OK) sz = "Everything Ok";
+ else if (rv == SA_ERR_ARG) sz = "Invalid Argument";
+ else if (rv == SA_ERR_USE) sz = "Invalid Use Or Context";
+ else if (rv == SA_ERR_MEM) sz = "Not Enough Memory";
+ else if (rv == SA_ERR_MTC) sz = "Matching Failed";
+ else if (rv == SA_ERR_EOF) sz = "End Of Communication";
+ else if (rv == SA_ERR_TMT) sz = "Communication Timeout";
+ else if (rv == SA_ERR_SYS) sz = "Operating System Error";
+ else if (rv == SA_ERR_INT) sz = "Internal Error";
+ else sz = "Invalid Result Code";
+ return sz;
+}
+
|