Index: ossp-pkg/sa/ChangeLog RCS File: /v/ossp/cvs/ossp-pkg/sa/ChangeLog,v rcsdiff -q -kk '-r1.1' '-r1.2' -u '/v/ossp/cvs/ossp-pkg/sa/ChangeLog,v' 2>/dev/null --- ChangeLog 2002/10/26 17:59:29 1.1 +++ ChangeLog 2002/10/30 08:42:16 1.2 @@ -13,6 +13,12 @@ Changes between 0.9.2 and 0.9.3 (11-Oct-2002 to xx-Oct-2002) + o Rewrote the implementation of sa_option(3) in order to make options + configurable _before_ the underlying socket is implicitly + allocated. This fixes especially sa_option(sa, SA_OPTION_REUSEADDR, 1) + before sa_bind(). + [Ralf S. Engelschall, Michael van Elst ] + o Finished writing the documentation (manual page sa.pod). [Ralf S. Engelschall] Index: ossp-pkg/sa/sa.c RCS File: /v/ossp/cvs/ossp-pkg/sa/sa.c,v rcsdiff -q -kk '-r1.62' '-r1.63' -u '/v/ossp/cvs/ossp-pkg/sa/sa.c,v' 2>/dev/null --- sa.c 2002/10/26 15:45:32 1.62 +++ sa.c 2002/10/30 08:42:16 1.63 @@ -206,6 +206,12 @@ SA_SC_DECLARE_6(ssize_t, sendto, int, const void *, size_t, int, const struct sockaddr *, socklen_t) } sa_syscall_tab_t; +/* socket option information */ +typedef struct { + int todo; + int value; +} sa_optinfo_t; + /* socket abstraction object */ struct sa_st { sa_type_t eType; /* socket type (stream or datagram) */ @@ -218,6 +224,7 @@ int nWriteSize; /* write buffer current size */ char *cpWriteBuf; /* write buffer memory chunk */ sa_syscall_tab_t scSysCall; /* table of system calls */ + sa_optinfo_t optInfo[5]; /* option storage */ }; /* socket address abstraction object */ @@ -866,25 +873,120 @@ /* set timeouts if timeouts or done in kernel */ static sa_rc_t sa_socket_settimeouts(sa_t *sa) { + /* stop processing if socket is still not allocated */ + if (sa->fdSocket == -1) + return SA_OK; + #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, - (const void *)(&sa->tvTimeout[SA_TIMEOUT_READ]), - (socklen_t)(sizeof(sa->tvTimeout[SA_TIMEOUT_READ]))) < 0) - return SA_RC(SA_ERR_SYS); - } - if (SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_WRITE])) { - if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_SNDTIMEO, - (const void *)(&sa->tvTimeout[SA_TIMEOUT_WRITE]), - (socklen_t)(sizeof(sa->tvTimeout[SA_TIMEOUT_WRITE]))) < 0) - return SA_RC(SA_ERR_SYS); - } + if (SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_READ])) { + if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_RCVTIMEO, + (const void *)(&sa->tvTimeout[SA_TIMEOUT_READ]), + (socklen_t)(sizeof(sa->tvTimeout[SA_TIMEOUT_READ]))) < 0) + return SA_RC(SA_ERR_SYS); + } + if (SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_WRITE])) { + if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_SNDTIMEO, + (const void *)(&sa->tvTimeout[SA_TIMEOUT_WRITE]), + (socklen_t)(sizeof(sa->tvTimeout[SA_TIMEOUT_WRITE]))) < 0) + return SA_RC(SA_ERR_SYS); } #endif return SA_OK; } +/* set socket options */ +static sa_rc_t sa_socket_setoptions(sa_t *sa) +{ + int i; + sa_rc_t rv; + + /* stop processing if socket is still not allocated */ + if (sa->fdSocket == -1) + return SA_OK; + + /* check for pending options */ + rv = SA_OK; + for (i = 0; i < (sizeof(sa->optInfo)/sizeof(sa->optInfo[0])); i++) { + if (sa->optInfo[i].todo) { + switch (i) { + /* enable/disable Nagle's Algorithm (see RFC898) */ + case SA_OPTION_NAGLE: { +#if defined(IPPROTO_TCP) && defined(TCP_NODELAY) + int mode = sa->optInfo[i].value; + if (setsockopt(sa->fdSocket, IPPROTO_TCP, TCP_NODELAY, + (const void *)&mode, + (socklen_t)sizeof(mode)) < 0) + rv = SA_ERR_SYS; + else + sa->optInfo[i].todo = FALSE; +#endif + break; + } + /* enable/disable linger close semantics */ + case SA_OPTION_LINGER: { +#if defined(SO_LINGER) + struct linger linger; + linger.l_onoff = (sa->optInfo[i].value == 0 ? 0 : 1); + linger.l_linger = sa->optInfo[i].value; + if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_LINGER, + (const void *)&linger, + (socklen_t)sizeof(struct linger)) < 0) + rv = SA_ERR_SYS; + else + sa->optInfo[i].todo = FALSE; +#endif + break; + } + /* enable/disable reusability of binding to address */ + case SA_OPTION_REUSEADDR: { +#if defined(SO_REUSEADDR) + int mode = sa->optInfo[i].value; + if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_REUSEADDR, + (const void *)&mode, (socklen_t)sizeof(mode)) < 0) + rv = SA_ERR_SYS; + else + sa->optInfo[i].todo = FALSE; +#endif + break; + } + /* enable/disable reusability of binding to port */ + case SA_OPTION_REUSEPORT: { +#if defined(SO_REUSEPORT) + int mode = sa->optInfo[i].value; + if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_REUSEPORT, + (const void *)&mode, (socklen_t)sizeof(mode)) < 0) + rv = SA_ERR_SYS; + else + sa->optInfo[i].todo = FALSE; +#endif + break; + } + /* enable/disable non-blocking I/O mode */ + case SA_OPTION_NONBLOCK: { + int mode = sa->optInfo[i].value; + int flags; + if ((flags = fcntl(sa->fdSocket, F_GETFL, 0)) < 0) { + rv = SA_ERR_SYS; + break; + } + if (mode == 0) + flags &= ~(O_NONBLOCK); + else + flags |= O_NONBLOCK; + if (fcntl(sa->fdSocket, F_SETFL, flags) < 0) + rv = SA_ERR_SYS; + else + sa->optInfo[i].todo = FALSE; + break; + } + default: /* NOTREACHED */ break; + } + } + } + + return SA_RC(rv); +} + /* internal lazy/delayed initialization of underlying socket */ static sa_rc_t sa_socket_init(sa_t *sa, int nFamily) { @@ -893,6 +995,7 @@ #if !(defined(IPPROTO_TCP) && defined(IPPROTO_UDP)) struct protoent *pe; #endif + sa_rc_t rv; /* argument sanity check(s) */ if (sa == NULL) @@ -945,7 +1048,12 @@ return SA_RC(SA_ERR_SYS); /* optionally set kernel timeouts */ - sa_socket_settimeouts(sa); + if ((rv = sa_socket_settimeouts(sa)) != SA_OK) + return SA_RC(rv); + + /* optionally configure changed options */ + if ((rv = sa_socket_setoptions(sa)) != SA_OK) + return SA_RC(rv); return SA_OK; } @@ -998,6 +1106,12 @@ sa->tvTimeout[i].tv_usec = 0; } + /* init options object attributes */ + for (i = 0; i < (sizeof(sa->optInfo)/sizeof(sa->optInfo[0])); i++) { + sa->optInfo[i].todo = FALSE; + sa->optInfo[i].value = 0; + } + /* init syscall object attributes */ SA_SC_ASSIGN(sa, connect, connect, NULL); SA_SC_ASSIGN(sa, accept, accept, NULL); @@ -1056,6 +1170,7 @@ sa_rc_t sa_timeout(sa_t *sa, sa_timeout_t id, long sec, long usec) { int i; + sa_rc_t rv; /* argument sanity check(s) */ if (sa == NULL) @@ -1072,8 +1187,9 @@ sa->tvTimeout[id].tv_usec = usec; } - /* optionally set kernel timeouts */ - sa_socket_settimeouts(sa); + /* try to already set timeouts */ + if ((rv = sa_socket_settimeouts(sa)) != SA_OK) + return SA_RC(rv); return SA_OK; } @@ -1150,16 +1266,10 @@ va_start(ap, id); switch (id) { case SA_OPTION_NAGLE: { - /* enable/disable Nagle's Algorithm (see RFC898) */ #if defined(IPPROTO_TCP) && defined(TCP_NODELAY) int mode = ((int)va_arg(ap, int) ? 1 : 0); - if (sa->fdSocket == -1) { - rv = SA_ERR_USE; - break; - } - if (setsockopt(sa->fdSocket, IPPROTO_TCP, TCP_NODELAY, - (const void *)&mode, (socklen_t)sizeof(mode)) < 0) - rv = SA_ERR_SYS; + sa->optInfo[SA_OPTION_NAGLE].value = mode; + sa->optInfo[SA_OPTION_NAGLE].todo = TRUE; #else rv = SA_ERR_IMP; #endif @@ -1167,61 +1277,40 @@ } case SA_OPTION_LINGER: { #if defined(SO_LINGER) - struct linger *linger = (struct linger *)va_arg(ap, void *); - if (sa->fdSocket == -1) { - rv = SA_ERR_USE; - break; - } - if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_LINGER, - (const void *)linger, (socklen_t)sizeof(struct linger)) < 0) - rv = SA_ERR_SYS; + int amount = ((int)va_arg(ap, int) ? 1 : 0); + sa->optInfo[SA_OPTION_LINGER].value = amount; + sa->optInfo[SA_OPTION_LINGER].todo = TRUE; #else rv = SA_ERR_IMP; #endif break; } case SA_OPTION_REUSEADDR: -#ifdef SA_OPTION_REUSEPORT - case SA_OPTION_REUSEPORT: + { +#if defined(SO_REUSEADDR) + int mode = ((int)va_arg(ap, int) ? 1 : 0); + sa->optInfo[SA_OPTION_REUSEADDR].value = mode; + sa->optInfo[SA_OPTION_REUSEADDR].todo = TRUE; +#else + rv = SA_ERR_IMP; #endif + break; + } + case SA_OPTION_REUSEPORT: { - /* enable/disable reusability of binding to address or port */ +#if defined(SO_REUSEPORT) int mode = ((int)va_arg(ap, int) ? 1 : 0); - int flag; - if (sa->fdSocket == -1) { - rv = SA_ERR_USE; - break; - } - switch (id) { - case SA_OPTION_REUSEADDR: flag = SO_REUSEADDR; break; -#ifdef SA_OPTION_REUSEPORT - case SA_OPTION_REUSEPORT: flag = SO_REUSEPORT; break; + sa->optInfo[SA_OPTION_REUSEPORT].value = mode; + sa->optInfo[SA_OPTION_REUSEPORT].todo = TRUE; +#else + rv = SA_ERR_IMP; #endif - default: flag = 0; break; - } - if (setsockopt(sa->fdSocket, SOL_SOCKET, flag, - (const void *)&mode, (socklen_t)sizeof(mode)) < 0) - rv = SA_ERR_SYS; break; } case SA_OPTION_NONBLOCK: { - /* enable/disable non-blocking I/O mode */ - int flags; int mode = (int)va_arg(ap, int); - if (sa->fdSocket == -1) { - rv = SA_ERR_USE; - break; - } - if ((flags = fcntl(sa->fdSocket, F_GETFL, 0)) < 0) { - rv = SA_ERR_SYS; - break; - } - if (mode == 0) - flags &= ~(O_NONBLOCK); - else - flags |= O_NONBLOCK; - if (fcntl(sa->fdSocket, F_SETFL, flags) < 0) - rv = SA_ERR_SYS; + sa->optInfo[SA_OPTION_NONBLOCK].value = mode; + sa->optInfo[SA_OPTION_NONBLOCK].todo = TRUE; break; } default: { @@ -1230,7 +1319,15 @@ } va_end(ap); - return SA_RC(rv); + /* if an error already occured, stop here */ + if (rv != SA_OK) + return SA_RC(rv); + + /* else already (re)set) options (if possible) */ + if ((rv = sa_socket_setoptions(sa)) != SA_OK) + return SA_RC(rv); + + return SA_OK; } /* override system call */ Index: ossp-pkg/sa/sa.h RCS File: /v/ossp/cvs/ossp-pkg/sa/sa.h,v rcsdiff -q -kk '-r1.31' '-r1.32' -u '/v/ossp/cvs/ossp-pkg/sa/sa.h,v' 2>/dev/null --- sa.h 2002/10/11 15:27:39 1.31 +++ sa.h 2002/10/30 08:42:16 1.32 @@ -137,11 +137,11 @@ /* list of options */ typedef enum { - SA_OPTION_NAGLE, - SA_OPTION_LINGER, - SA_OPTION_REUSEADDR, - SA_OPTION_REUSEPORT, - SA_OPTION_NONBLOCK + SA_OPTION_NAGLE = 0, + SA_OPTION_LINGER = 1, + SA_OPTION_REUSEADDR = 2, + SA_OPTION_REUSEPORT = 3, + SA_OPTION_NONBLOCK = 4 } sa_option_t; /* list of system calls */ Index: ossp-pkg/sa/sa.pod RCS File: /v/ossp/cvs/ossp-pkg/sa/sa.pod,v rcsdiff -q -kk '-r1.33' '-r1.34' -u '/v/ossp/cvs/ossp-pkg/sa/sa.pod,v' 2>/dev/null --- sa.pod 2002/10/26 18:28:16 1.33 +++ sa.pod 2002/10/30 08:42:16 1.34 @@ -458,11 +458,9 @@ C (C I) for enabling (I=C<1>) or disabling (I == C<0>) Nagle's Algorithm (see RFC898). -C (CI) for enabling -(Cl_onoff> == C<1> and Cl_linger> == -I) or disabling (Cl_onoff> == C<0>) lingering -on close (see C of setsockopt(2) and C in -F). +C (C I) for enabling (I == +I E C<0>) or disabling (I == C<0>) lingering on +close (see C of setsockopt(2)). C (C I) for enabling (I == C<1>) or disabling (I == C<0>) the reusability of the address on