Index: ossp-pkg/l2/l2_ch_smtp.c RCS File: /v/ossp/cvs/ossp-pkg/l2/l2_ch_smtp.c,v rcsdiff -q -kk '-r1.4' '-r1.5' -u '/v/ossp/cvs/ossp-pkg/l2/l2_ch_smtp.c,v' 2>/dev/null --- l2_ch_smtp.c 2001/10/06 14:33:09 1.4 +++ l2_ch_smtp.c 2001/10/10 15:58:27 1.5 @@ -132,8 +132,9 @@ return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_INT); /* configure socket parameters */ - sa_timeout(cfg->saServer, cfg->nTimeout, 0); - sa_buffers(cfg->saServer, 4096, 4096); + sa_timeout(cfg->saServer, SA_TIMEOUT_ALL, cfg->nTimeout, 0); + sa_buffer(cfg->saServer, SA_BUFFER_READ, 4096); + sa_buffer(cfg->saServer, SA_BUFFER_WRITE, 4096); return L2_OK; } Index: ossp-pkg/l2/l2_ch_syslog.c RCS File: /v/ossp/cvs/ossp-pkg/l2/l2_ch_syslog.c,v rcsdiff -q -kk '-r1.23' '-r1.24' -u '/v/ossp/cvs/ossp-pkg/l2/l2_ch_syslog.c,v' 2>/dev/null --- l2_ch_syslog.c 2001/10/06 14:35:57 1.23 +++ l2_ch_syslog.c 2001/10/10 15:58:27 1.24 @@ -228,7 +228,7 @@ if ((rc = sa_create(&cfg->saRemoteSock)) != SA_OK) return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_INT); sa_type(cfg->saRemoteSock, SA_TYPE_DATAGRAM); - sa_timeout(cfg->saRemoteSock, 10, 0); + sa_timeout(cfg->saRemoteSock, SA_TIMEOUT_ALL, 10, 0); if ((rc = sa_addr_create(&la)) != SA_OK) return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_INT); /* FIXME: if uid == 0 -> use port 514 */ Index: ossp-pkg/l2/l2_ut_sa.c RCS File: /v/ossp/cvs/ossp-pkg/l2/l2_ut_sa.c,v rcsdiff -q -kk '-r1.14' '-r1.15' -u '/v/ossp/cvs/ossp-pkg/l2/l2_ut_sa.c,v' 2>/dev/null --- 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 +** Copyright (c) 2001 The OSSP Project +** Copyright (c) 2001 Cable & Wireless Deutschland ** ** 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 /* for "s[n]printf()" */ #include /* for "malloc()" & friends */ -#include /* for "va_xxx()" and "va_list" */ -#include /* for "strxxx()" and "size_t" */ +#include /* for "va_XXX()" and "va_list" */ +#include /* for "strXXX()" and "size_t" */ #include /* for general prerequisites */ -#include /* for isxxx() */ +#include /* for isXXX() */ #include /* for "EXXX" */ #include /* for "F_XXX" and "O_XXX" */ #include /* for standard Unix stuff */ @@ -45,30 +46,136 @@ #include /* for "struct sockaddr_un" */ #include /* for "struct sockaddr_in[6]" */ #include /* for "AF_XXX" and "SOCK_XXX" */ -#include /* for "inet_xtoy" */ +#include /* 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: 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; +} + Index: ossp-pkg/l2/l2_ut_sa.h RCS File: /v/ossp/cvs/ossp-pkg/l2/l2_ut_sa.h,v rcsdiff -q -kk '-r1.9' '-r1.10' -u '/v/ossp/cvs/ossp-pkg/l2/l2_ut_sa.h,v' 2>/dev/null --- l2_ut_sa.h 2001/10/06 14:33:09 1.9 +++ l2_ut_sa.h 2001/10/10 15:58:27 1.10 @@ -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 +** Copyright (c) 2001 The OSSP Project +** Copyright (c) 2001 Cable & Wireless Deutschland ** ** This file is part of OSSP SA, a socket abstraction library which ** can be found at http://www.ossp.org/pkg/sa/. @@ -37,6 +38,14 @@ #include #include +/* include optional Autoconf header */ +#include "l2_config.h" + +/* fallback for POSIX socklen_t */ +#if defined(HAVE_CONFIG_H) && !defined(HAVE_SOCKLEN_T) +#define socklen_t int +#endif + /* embedding support */ #ifdef SA_PREFIX #if defined(__STDC__) || defined(__cplusplus) @@ -95,15 +104,43 @@ /* return codes */ typedef enum { - SA_OK, /* everything ok */ - 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_OK, /* Everything Ok */ + SA_ERR_ARG, /* Invalid Argument */ + SA_ERR_USE, /* Invalid Use Or Context */ + SA_ERR_MEM, /* Not Enough Memory */ + SA_ERR_MTC, /* Matching Failed */ + SA_ERR_EOF, /* End Of Communication */ + SA_ERR_TMT, /* Communication Timeout */ + SA_ERR_SYS, /* Operating System Error */ + SA_ERR_INT /* Internal Error */ } sa_rc_t; +/* list of timeouts */ +typedef enum { + SA_TIMEOUT_ALL = -1, + SA_TIMEOUT_ACCEPT = 0, + SA_TIMEOUT_CONNECT = 1, + SA_TIMEOUT_READ = 2, + SA_TIMEOUT_WRITE = 3 +} sa_timeout_t; + +/* list of buffers */ +typedef enum { + SA_BUFFER_READ, + SA_BUFFER_WRITE +} sa_buffer_t; + +/* list of system calls */ +typedef enum { + SA_SYSCALL_CONNECT, + SA_SYSCALL_ACCEPT, + SA_SYSCALL_SELECT, + SA_SYSCALL_READ, + SA_SYSCALL_WRITE, + SA_SYSCALL_RECVFROM, + SA_SYSCALL_SENDTO +} sa_syscall_t; + /* address object operations */ sa_rc_t sa_addr_create (sa_addr_t **saa); sa_rc_t sa_addr_destroy (sa_addr_t *saa); @@ -113,6 +150,7 @@ sa_rc_t sa_addr_s2a (sa_addr_t *saa, const struct sockaddr *sabuf, socklen_t salen); sa_rc_t sa_addr_a2u (sa_addr_t *saa, char **uri); sa_rc_t sa_addr_a2s (sa_addr_t *saa, struct sockaddr **sabuf, socklen_t *salen); +sa_rc_t sa_addr_match (sa_addr_t *saa1, sa_addr_t *saa2, int prefixlen); /* socket object operations */ sa_rc_t sa_create (sa_t **sa); @@ -120,8 +158,9 @@ /* socket parameter operations */ 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_timeout (sa_t *sa, sa_timeout_t id, long sec, long usec); +sa_rc_t sa_buffer (sa_t *sa, sa_buffer_t id, size_t size); +sa_rc_t sa_syscall (sa_t *sa, sa_syscall_t id, void (*fptr)(), void *fctx); /* socket connection operations */ sa_rc_t sa_bind (sa_t *sa, sa_addr_t *laddr); @@ -144,5 +183,13 @@ 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); +/* error handling operations */ +char *sa_error (sa_rc_t rv); + +/* cleanup */ +#if defined(HAVE_CONFIG_H) && !defined(HAVE_SOCKLEN_T) +#undef socklen_t +#endif + #endif /* __SA_H__ */