diff options
Diffstat (limited to 'rubbos/app/tomcat-connectors-1.2.32-src/native/common/jk_connect.c')
-rw-r--r-- | rubbos/app/tomcat-connectors-1.2.32-src/native/common/jk_connect.c | 1190 |
1 files changed, 1190 insertions, 0 deletions
diff --git a/rubbos/app/tomcat-connectors-1.2.32-src/native/common/jk_connect.c b/rubbos/app/tomcat-connectors-1.2.32-src/native/common/jk_connect.c new file mode 100644 index 00000000..edf8c33b --- /dev/null +++ b/rubbos/app/tomcat-connectors-1.2.32-src/native/common/jk_connect.c @@ -0,0 +1,1190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Description: Socket/Naming manipulation functions + * Based on: Various Jserv files + */ +/** + * @package jk_connect + * @author Gal Shachor <shachor@il.ibm.com> + * @author Mladen Turk <mturk@apache.org> + * @version $Revision: 1125651 $ + */ + + +#include "jk_connect.h" +#include "jk_util.h" + +#ifdef HAVE_APR +#include "apr_network_io.h" +#include "apr_errno.h" +#include "apr_general.h" +#include "apr_pools.h" +static apr_pool_t *jk_apr_pool = NULL; +#endif + +#ifdef HAVE_SYS_FILIO_H +/* FIONREAD on Solaris et al. */ +#include <sys/filio.h> +#endif +#ifdef HAVE_POLL_H +/* Use poll instead select */ +#include <poll.h> +#endif + +#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__)) +#define JK_IS_SOCKET_ERROR(x) ((x) == SOCKET_ERROR) +#define JK_GET_SOCKET_ERRNO() errno = WSAGetLastError() - WSABASEERR +#else +#define JK_IS_SOCKET_ERROR(x) ((x) == -1) +#define JK_GET_SOCKET_ERRNO() ((void)0) +#endif /* WIN32 */ + +#if defined(NETWARE) && defined(__NOVELL_LIBC__) +#define USE_SOCK_CLOEXEC +#endif + +/* our compiler cant deal with char* <-> const char* ... */ +#if defined(NETWARE) && !defined(__NOVELL_LIBC__) +typedef char* SET_TYPE; +#else +typedef const char* SET_TYPE; +#endif + +/** Set socket to blocking + * @param sd socket to manipulate + * @return errno: fcntl returns -1 (!WIN32) + * pseudo errno: ioctlsocket returns SOCKET_ERROR (WIN32) + * 0: success + */ +static int soblock(jk_sock_t sd) +{ +/* BeOS uses setsockopt at present for non blocking... */ +#if defined (WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__)) + u_long on = 0; + if (JK_IS_SOCKET_ERROR(ioctlsocket(sd, FIONBIO, &on))) { + JK_GET_SOCKET_ERRNO(); + return errno; + } +#else + int fd_flags; + + fd_flags = fcntl(sd, F_GETFL, 0); +#if defined(O_NONBLOCK) + fd_flags &= ~O_NONBLOCK; +#elif defined(O_NDELAY) + fd_flags &= ~O_NDELAY; +#elif defined(FNDELAY) + fd_flags &= ~FNDELAY; +#else +#error Please teach JK how to make sockets blocking on your platform. +#endif + if (fcntl(sd, F_SETFL, fd_flags) == -1) { + return errno; + } +#endif /* WIN32 || (NETWARE && __NOVELL_LIBC__) */ + return 0; +} + +/** Set socket to non-blocking + * @param sd socket to manipulate + * @return errno: fcntl returns -1 (!WIN32) + * pseudo errno: ioctlsocket returns SOCKET_ERROR (WIN32) + * 0: success + */ +static int sononblock(jk_sock_t sd) +{ +#if defined (WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__)) + u_long on = 1; + if (JK_IS_SOCKET_ERROR(ioctlsocket(sd, FIONBIO, &on))) { + JK_GET_SOCKET_ERRNO(); + return errno; + } +#else + int fd_flags; + + fd_flags = fcntl(sd, F_GETFL, 0); +#if defined(O_NONBLOCK) + fd_flags |= O_NONBLOCK; +#elif defined(O_NDELAY) + fd_flags |= O_NDELAY; +#elif defined(FNDELAY) + fd_flags |= FNDELAY; +#else +#error Please teach JK how to make sockets non-blocking on your platform. +#endif + if (fcntl(sd, F_SETFL, fd_flags) == -1) { + return errno; + } +#endif /* WIN32 || (NETWARE && __NOVELL_LIBC__) */ + return 0; +} + +#if defined (WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__)) +/* WIN32 implementation */ +/** Non-blocking socket connect + * @param sd socket to connect + * @param addr address to connect to + * @param timeout connect timeout in seconds + * (<=0: no timeout=blocking) + * @param l logger + * @return -1: some kind of error occured + * SOCKET_ERROR: no timeout given and error + * during blocking connect + * 0: success + */ +static int nb_connect(jk_sock_t sd, struct sockaddr *addr, int timeout, jk_logger_t *l) +{ + int rc; + + JK_TRACE_ENTER(l); + + if (timeout <= 0) { + rc = connect(sd, addr, sizeof(struct sockaddr_in)); + JK_TRACE_EXIT(l); + return rc; + } + + if ((rc = sononblock(sd))) { + JK_TRACE_EXIT(l); + return -1; + } + if (JK_IS_SOCKET_ERROR(connect(sd, addr, sizeof(struct sockaddr_in)))) { + struct timeval tv; + fd_set wfdset, efdset; + + if ((rc = WSAGetLastError()) != WSAEWOULDBLOCK) { + soblock(sd); + WSASetLastError(rc); + JK_TRACE_EXIT(l); + return -1; + } + /* wait for the connect to complete or timeout */ + FD_ZERO(&wfdset); + FD_SET(sd, &wfdset); + FD_ZERO(&efdset); + FD_SET(sd, &efdset); + + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + + rc = select((int)sd + 1, NULL, &wfdset, &efdset, &tv); + if (JK_IS_SOCKET_ERROR(rc) || rc == 0) { + rc = WSAGetLastError(); + soblock(sd); + WSASetLastError(rc); + JK_TRACE_EXIT(l); + return -1; + } + /* Evaluate the efdset */ + if (FD_ISSET(sd, &efdset)) { + /* The connect failed. */ + int rclen = (int)sizeof(rc); + if (getsockopt(sd, SOL_SOCKET, SO_ERROR, (char*) &rc, &rclen)) + rc = 0; + soblock(sd); + if (rc) + WSASetLastError(rc); + JK_TRACE_EXIT(l); + return -1; + } + } + soblock(sd); + JK_TRACE_EXIT(l); + return 0; +} + +#elif !defined(NETWARE) +/* POSIX implementation */ +/** Non-blocking socket connect + * @param sd socket to connect + * @param addr address to connect to + * @param timeout connect timeout in seconds + * (<=0: no timeout=blocking) + * @param l logger + * @return -1: some kind of error occured + * 0: success + */ +static int nb_connect(jk_sock_t sd, struct sockaddr *addr, int timeout, jk_logger_t *l) +{ + int rc = 0; + + JK_TRACE_ENTER(l); + + if (timeout > 0) { + if (sononblock(sd)) { + JK_TRACE_EXIT(l); + return -1; + } + } + do { + rc = connect(sd, addr, sizeof(struct sockaddr_in)); + } while (rc == -1 && errno == EINTR); + + if ((rc == -1) && (errno == EINPROGRESS || errno == EALREADY) + && (timeout > 0)) { + fd_set wfdset; + struct timeval tv; + socklen_t rclen = (socklen_t)sizeof(rc); + + FD_ZERO(&wfdset); + FD_SET(sd, &wfdset); + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + rc = select(sd + 1, NULL, &wfdset, NULL, &tv); + if (rc <= 0) { + /* Save errno */ + int err = errno; + soblock(sd); + errno = err; + JK_TRACE_EXIT(l); + return -1; + } + rc = 0; +#ifdef SO_ERROR + if (!FD_ISSET(sd, &wfdset) || + (getsockopt(sd, SOL_SOCKET, SO_ERROR, + (char *)&rc, &rclen) < 0) || rc) { + if (rc) + errno = rc; + rc = -1; + } +#endif /* SO_ERROR */ + } + /* Not sure we can be already connected */ + if (rc == -1 && errno == EISCONN) + rc = 0; + soblock(sd); + JK_TRACE_EXIT(l); + return rc; +} +#else +/* NETWARE implementation - blocking for now */ +/** Non-blocking socket connect + * @param sd socket to connect + * @param addr address to connect to + * @param timeout connect timeout in seconds (ignored!) + * @param l logger + * @return -1: some kind of error occured + * 0: success + */ +static int nb_connect(jk_sock_t sd, struct sockaddr *addr, int timeout, jk_logger_t *l) +{ + int rc; + + JK_TRACE_ENTER(l); + + rc = connect(sd, addr, sizeof(struct sockaddr_in)); + JK_TRACE_EXIT(l); + return rc; +} +#endif + + +#ifdef AS400_UTF8 + +/* + * i5/OS V5R4 need EBCDIC for its runtime calls but APR/APACHE works in UTF + */ +in_addr_t jk_inet_addr(const char * addrstr) +{ + in_addr_t addr; + char *ptr; + + ptr = (char *)malloc(strlen(addrstr) + 1); + jk_ascii2ebcdic((char *)addrstr, ptr); + addr = inet_addr(ptr); + free(ptr); + + return(addr); +} + +#endif + +/** Resolve the host IP + * @param host host or ip address + * @param port port + * @param rc return value pointer + * @param l logger + * @return JK_FALSE: some kind of error occured + * JK_TRUE: success + */ +int jk_resolve(const char *host, int port, struct sockaddr_in *rc, + void *pool, jk_logger_t *l) +{ + int x; + struct in_addr laddr; + + JK_TRACE_ENTER(l); + + memset(rc, 0, sizeof(struct sockaddr_in)); + + rc->sin_port = htons((short)port); + rc->sin_family = AF_INET; + + /* Check if we only have digits in the string */ + for (x = 0; host[x] != '\0'; x++) { + if (!isdigit((int)(host[x])) && host[x] != '.') { + break; + } + } + + /* If we found also characters we should make name to IP resolution */ + if (host[x] != '\0') { + +#ifdef HAVE_APR + apr_sockaddr_t *remote_sa, *temp_sa; + char *remote_ipaddr; + + if (!jk_apr_pool) { + if (apr_pool_create(&jk_apr_pool, (apr_pool_t *)pool) != APR_SUCCESS) { + JK_TRACE_EXIT(l); + return JK_FALSE; + } + } + apr_pool_clear(jk_apr_pool); + if (apr_sockaddr_info_get + (&remote_sa, host, APR_UNSPEC, (apr_port_t) port, 0, jk_apr_pool) + != APR_SUCCESS) { + JK_TRACE_EXIT(l); + return JK_FALSE; + } + + /* Since we are only handling AF_INET (IPV4) address (in_addr_t) */ + /* make sure we find one of those. */ + temp_sa = remote_sa; + while ((NULL != temp_sa) && (AF_INET != temp_sa->family)) + temp_sa = temp_sa->next; + + /* if temp_sa is set, we have a valid address otherwise, just return */ + if (NULL != temp_sa) + remote_sa = temp_sa; + else { + JK_TRACE_EXIT(l); + return JK_FALSE; + } + + apr_sockaddr_ip_get(&remote_ipaddr, remote_sa); + + laddr.s_addr = jk_inet_addr(remote_ipaddr); + +#else /* HAVE_APR */ + + /* XXX : WARNING : We should really use gethostbyname_r in multi-threaded env */ + /* Fortunatly when APR is available, ie under Apache 2.0, we use it */ +#if defined(NETWARE) && !defined(__NOVELL_LIBC__) + struct hostent *hoste = gethostbyname((char*)host); +#else + struct hostent *hoste = gethostbyname(host); +#endif + if (!hoste) { + JK_TRACE_EXIT(l); + return JK_FALSE; + } + + laddr = *((struct in_addr *)hoste->h_addr_list[0]); + +#endif /* HAVE_APR */ + } + else { + /* If we found only digits we use inet_addr() */ + laddr.s_addr = jk_inet_addr(host); + } + memcpy(&(rc->sin_addr), &laddr, sizeof(laddr)); + + JK_TRACE_EXIT(l); + return JK_TRUE; +} + +/** Connect to Tomcat + * @param addr address to connect to + * @param keepalive should we set SO_KEEPALIVE (if !=0) + * @param timeout connect timeout in seconds + * (<=0: no timeout=blocking) + * @param sock_buf size of send and recv buffer + * (<=0: use default) + * @param l logger + * @return JK_INVALID_SOCKET: some kind of error occured + * created socket: success + * @remark Cares about errno + */ +jk_sock_t jk_open_socket(struct sockaddr_in *addr, int keepalive, + int timeout, int connect_timeout, + int sock_buf, jk_logger_t *l) +{ + char buf[64]; + jk_sock_t sd; + int set = 1; + int ret = 0; + int flags = 0; +#ifdef SO_LINGER + struct linger li; +#endif + + JK_TRACE_ENTER(l); + + errno = 0; +#if defined(SOCK_CLOEXEC) && defined(USE_SOCK_CLOEXEC) + flags |= SOCK_CLOEXEC; +#endif + sd = socket(AF_INET, SOCK_STREAM | flags, 0); + if (!IS_VALID_SOCKET(sd)) { + JK_GET_SOCKET_ERRNO(); + jk_log(l, JK_LOG_ERROR, + "socket() failed (errno=%d)", errno); + JK_TRACE_EXIT(l); + return JK_INVALID_SOCKET; + } +#if defined(FD_CLOEXEC) && !defined(USE_SOCK_CLOEXEC) + if ((flags = fcntl(sd, F_GETFD)) == -1) { + JK_GET_SOCKET_ERRNO(); + jk_log(l, JK_LOG_ERROR, + "fcntl() failed (errno=%d)", errno); + jk_close_socket(sd, l); + JK_TRACE_EXIT(l); + return JK_INVALID_SOCKET; + } + flags |= FD_CLOEXEC; + if (fcntl(sd, F_SETFD, flags) == -1) { + JK_GET_SOCKET_ERRNO(); + jk_log(l, JK_LOG_ERROR, + "fcntl() failed (errno=%d)", errno); + jk_close_socket(sd, l); + JK_TRACE_EXIT(l); + return JK_INVALID_SOCKET; + } +#endif + + /* Disable Nagle algorithm */ + if (setsockopt(sd, IPPROTO_TCP, TCP_NODELAY, (SET_TYPE)&set, + sizeof(set))) { + JK_GET_SOCKET_ERRNO(); + jk_log(l, JK_LOG_ERROR, + "failed setting TCP_NODELAY (errno=%d)", errno); + jk_close_socket(sd, l); + JK_TRACE_EXIT(l); + return JK_INVALID_SOCKET; + } + if (JK_IS_DEBUG_LEVEL(l)) + jk_log(l, JK_LOG_DEBUG, + "socket TCP_NODELAY set to On"); + if (keepalive) { +#if defined(WIN32) && !defined(NETWARE) + DWORD dw; + struct tcp_keepalive ka = { 0 }, ks = { 0 }; + if (timeout) + ka.keepalivetime = timeout * 10000; + else + ka.keepalivetime = 60 * 10000; /* 10 minutes */ + ka.keepaliveinterval = 1000; + ka.onoff = 1; + if (WSAIoctl(sd, SIO_KEEPALIVE_VALS, &ka, sizeof(ka), + &ks, sizeof(ks), &dw, NULL, NULL)) { + JK_GET_SOCKET_ERRNO(); + jk_log(l, JK_LOG_ERROR, + "failed setting SIO_KEEPALIVE_VALS (errno=%d)", errno); + jk_close_socket(sd, l); + JK_TRACE_EXIT(l); + return JK_INVALID_SOCKET; + } + if (JK_IS_DEBUG_LEVEL(l)) + jk_log(l, JK_LOG_DEBUG, + "socket SO_KEEPALIVE set to %d seconds", + ka.keepalivetime / 1000); +#else + set = 1; + if (setsockopt(sd, SOL_SOCKET, SO_KEEPALIVE, (SET_TYPE)&set, + sizeof(set))) { + JK_GET_SOCKET_ERRNO(); + jk_log(l, JK_LOG_ERROR, + "failed setting SO_KEEPALIVE (errno=%d)", errno); + jk_close_socket(sd, l); + JK_TRACE_EXIT(l); + return JK_INVALID_SOCKET; + } + if (JK_IS_DEBUG_LEVEL(l)) + jk_log(l, JK_LOG_DEBUG, + "socket SO_KEEPALIVE set to On"); +#endif + } + + if (sock_buf > 0) { + set = sock_buf; + /* Set socket send buffer size */ + if (setsockopt(sd, SOL_SOCKET, SO_SNDBUF, (SET_TYPE)&set, + sizeof(set))) { + JK_GET_SOCKET_ERRNO(); + jk_log(l, JK_LOG_ERROR, + "failed setting SO_SNDBUF (errno=%d)", errno); + jk_close_socket(sd, l); + JK_TRACE_EXIT(l); + return JK_INVALID_SOCKET; + } + set = sock_buf; + /* Set socket receive buffer size */ + if (setsockopt(sd, SOL_SOCKET, SO_RCVBUF, (SET_TYPE)&set, + sizeof(set))) { + JK_GET_SOCKET_ERRNO(); + jk_log(l, JK_LOG_ERROR, + "failed setting SO_RCVBUF (errno=%d)", errno); + jk_close_socket(sd, l); + JK_TRACE_EXIT(l); + return JK_INVALID_SOCKET; + } + if (JK_IS_DEBUG_LEVEL(l)) + jk_log(l, JK_LOG_DEBUG, + "socket SO_SNDBUF and SO_RCVBUF set to %d", + sock_buf); + } + + if (timeout > 0) { +#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__)) + int tmout = timeout * 1000; + setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, + (const char *) &tmout, sizeof(int)); + setsockopt(sd, SOL_SOCKET, SO_SNDTIMEO, + (const char *) &tmout, sizeof(int)); + JK_GET_SOCKET_ERRNO(); +#elif defined(SO_RCVTIMEO) && defined(USE_SO_RCVTIMEO) && defined(SO_SNDTIMEO) && defined(USE_SO_SNDTIMEO) + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, + (const void *) &tv, sizeof(tv)); + setsockopt(sd, SOL_SOCKET, SO_SNDTIMEO, + (const void *) &tv, sizeof(tv)); +#endif + if (JK_IS_DEBUG_LEVEL(l)) + jk_log(l, JK_LOG_DEBUG, + "timeout %d set for socket=%d", + timeout, sd); + } +#ifdef SO_NOSIGPIPE + /* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when + * sending data to a dead peer. Possibly also existing and in use on other BSD + * systems? + */ + set = 1; + if (setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, (const char *)&set, + sizeof(int))) { + JK_GET_SOCKET_ERRNO(); + jk_log(l, JK_LOG_ERROR, + "failed setting SO_NOSIGPIPE (errno=%d)", errno); + jk_close_socket(sd, l); + JK_TRACE_EXIT(l); + return JK_INVALID_SOCKET; + } +#endif +#ifdef SO_LINGER + /* Make hard closesocket by disabling lingering */ + li.l_linger = li.l_onoff = 0; + if (setsockopt(sd, SOL_SOCKET, SO_LINGER, (SET_TYPE)&li, + sizeof(li))) { + JK_GET_SOCKET_ERRNO(); + jk_log(l, JK_LOG_ERROR, + "failed setting SO_LINGER (errno=%d)", errno); + jk_close_socket(sd, l); + JK_TRACE_EXIT(l); + return JK_INVALID_SOCKET; + } +#endif + /* Tries to connect to Tomcat (continues trying while error is EINTR) */ + if (JK_IS_DEBUG_LEVEL(l)) + jk_log(l, JK_LOG_DEBUG, + "trying to connect socket %d to %s", sd, + jk_dump_hinfo(addr, buf)); + +/* Need more infos for BSD 4.4 and Unix 98 defines, for now only +iSeries when Unix98 is required at compil time */ +#if (_XOPEN_SOURCE >= 520) && defined(AS400) + ((struct sockaddr *)addr)->sa_len = sizeof(struct sockaddr_in); +#endif + ret = nb_connect(sd, (struct sockaddr *)addr, connect_timeout, l); +#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__)) + if (JK_IS_SOCKET_ERROR(ret)) { + JK_GET_SOCKET_ERRNO(); + } +#endif /* WIN32 */ + + /* Check if we are connected */ + if (ret) { + jk_log(l, JK_LOG_INFO, + "connect to %s failed (errno=%d)", + jk_dump_hinfo(addr, buf), errno); + jk_close_socket(sd, l); + sd = JK_INVALID_SOCKET; + } + else { + if (JK_IS_DEBUG_LEVEL(l)) + jk_log(l, JK_LOG_DEBUG, "socket %d [%s] connected", + sd, jk_dump_sinfo(sd, buf)); + } + JK_TRACE_EXIT(l); + return sd; +} + +/** Close the socket + * @param sd socket to close + * @param l logger + * @return -1: some kind of error occured (!WIN32) + * SOCKET_ERROR: some kind of error occured (WIN32) + * 0: success + * @remark Does not change errno + */ +int jk_close_socket(jk_sock_t sd, jk_logger_t *l) +{ + int rc; + int save_errno; + + JK_TRACE_ENTER(l); + + if (!IS_VALID_SOCKET(sd)) { + JK_TRACE_EXIT(l); + return -1; + } + + save_errno = errno; +#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__)) + rc = closesocket(sd) ? -1 : 0; +#else + do { + rc = close(sd); + } while (JK_IS_SOCKET_ERROR(rc) && (errno == EINTR || errno == EAGAIN)); +#endif + JK_TRACE_EXIT(l); + errno = save_errno; + return rc; +} + +#ifndef MAX_SECS_TO_LINGER +#define MAX_SECS_TO_LINGER 2 +#endif +#define MS_TO_LINGER 500 +#define MS_TO_LINGER_LAST 2 + +#ifndef MAX_LINGER_BYTES +#define MAX_LINGER_BYTES 32768 +#endif + +#ifndef SHUT_WR +#ifdef SD_SEND +#define SHUT_WR SD_SEND +#else +#define SHUT_WR 0x01 +#endif +#endif + +#ifndef SHUT_RD +#ifdef SD_RECEIVE +#define SHUT_RD SD_RECEIVE +#else +#define SHUT_RD 0x00 +#endif +#endif + +/** Drain and close the socket + * @param sd socket to close + * @param l logger + * @return -1: socket to close is invalid + * -1: some kind of error occured (!WIN32) + * SOCKET_ERROR: some kind of error occured (WIN32) + * 0: success + * @remark Does not change errno + */ +int jk_shutdown_socket(jk_sock_t sd, jk_logger_t *l) +{ + char dummy[512]; + char buf[64]; + char *sb = NULL; + int rc = 0; + size_t rd = 0; + size_t rp = 0; + int save_errno; + int timeout = MS_TO_LINGER; + time_t start = time(NULL); + + JK_TRACE_ENTER(l); + + if (!IS_VALID_SOCKET(sd)) { + JK_TRACE_EXIT(l); + return -1; + } + + save_errno = errno; + if (JK_IS_DEBUG_LEVEL(l)) { + sb = jk_dump_sinfo(sd, buf); + jk_log(l, JK_LOG_DEBUG, "About to shutdown socket %d [%s]", + sd, sb); + } + /* Shut down the socket for write, which will send a FIN + * to the peer. + */ + if (shutdown(sd, SHUT_WR)) { + rc = jk_close_socket(sd, l); + if (JK_IS_DEBUG_LEVEL(l)) + jk_log(l, JK_LOG_DEBUG, + "Failed sending SHUT_WR for socket %d [%s]", + sd, sb); + errno = save_errno; + JK_TRACE_EXIT(l); + return rc; + } + + do { + rp = 0; + if (jk_is_input_event(sd, timeout, l)) { + /* Do a restartable read on the socket + * draining out all the data currently in the socket buffer. + */ + do { +#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__)) + rc = recv(sd, &dummy[0], sizeof(dummy), 0); + if (JK_IS_SOCKET_ERROR(rc)) + JK_GET_SOCKET_ERRNO(); +#else + rc = read(sd, &dummy[0], sizeof(dummy)); +#endif + if (rc > 0) + rp += rc; + } while (JK_IS_SOCKET_ERROR(rc) && (errno == EINTR || errno == EAGAIN)); + + if (rc < 0) { + /* Read failed. + * Bail out from the loop. + */ + break; + } + } + else { + /* Error or timeout (reason is logged within jk_is_input_event) + * Exit the drain loop + */ + break; + } + rd += rp; + if (rp < sizeof(dummy)) { + if (timeout > MS_TO_LINGER_LAST) { + /* Try one last time with a short timeout + */ + timeout = MS_TO_LINGER_LAST; + continue; + } + /* We have read less then size of buffer + * It's a good chance there will be no more data + * to read. + */ + if ((rc = sononblock(sd))) { + rc = jk_close_socket(sd, l); + if (JK_IS_DEBUG_LEVEL(l)) + jk_log(l, JK_LOG_DEBUG, + "error setting socket %d [%s] to nonblocking", + sd, sb); + errno = save_errno; + JK_TRACE_EXIT(l); + return rc; + } + if (JK_IS_DEBUG_LEVEL(l)) + jk_log(l, JK_LOG_DEBUG, + "shutting down the read side of socket %d [%s]", + sd, sb); + shutdown(sd, SHUT_RD); + break; + } + timeout = MS_TO_LINGER; + } while ((rd < MAX_LINGER_BYTES) && (difftime(time(NULL), start) < MAX_SECS_TO_LINGER)); + + rc = jk_close_socket(sd, l); + if (JK_IS_DEBUG_LEVEL(l)) + jk_log(l, JK_LOG_DEBUG, + "Shutdown socket %d [%s] and read %d lingering bytes in %d sec.", + sd, sb, rd, (int)difftime(time(NULL), start)); + errno = save_errno; + JK_TRACE_EXIT(l); + return rc; +} + +/** send a message + * @param sd socket to use + * @param b buffer containing the data + * @param len length to send + * @param l logger + * @return negative errno: write returns a fatal -1 (!WIN32) + * negative pseudo errno: send returns SOCKET_ERROR (WIN32) + * JK_SOCKET_EOF: no bytes could be sent + * >0: success, provided number of bytes send + * @remark Always closes socket in case of error + * @remark Cares about errno + * @bug this fails on Unixes if len is too big for the underlying + * protocol + */ +int jk_tcp_socket_sendfull(jk_sock_t sd, const unsigned char *b, int len, jk_logger_t *l) +{ + int sent = 0; + int wr; + + JK_TRACE_ENTER(l); + + errno = 0; + while (sent < len) { + do { +#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__)) + wr = send(sd, (const char*)(b + sent), + len - sent, 0); + if (JK_IS_SOCKET_ERROR(wr)) + JK_GET_SOCKET_ERRNO(); +#else + wr = write(sd, b + sent, len - sent); +#endif + } while (JK_IS_SOCKET_ERROR(wr) && (errno == EINTR || errno == EAGAIN)); + + if (JK_IS_SOCKET_ERROR(wr)) { + int err; + jk_shutdown_socket(sd, l); + err = (errno > 0) ? -errno : errno; + JK_TRACE_EXIT(l); + return err; + } + else if (wr == 0) { + jk_shutdown_socket(sd, l); + JK_TRACE_EXIT(l); + return JK_SOCKET_EOF; + } + sent += wr; + } + + JK_TRACE_EXIT(l); + return sent; +} + +/** receive a message + * @param sd socket to use + * @param b buffer to store the data + * @param len length to receive + * @param l logger + * @return negative errno: read returns a fatal -1 (!WIN32) + * negative pseudo errno: recv returns SOCKET_ERROR (WIN32) + * JK_SOCKET_EOF: no bytes could be read + * >0: success, requested number of bytes received + * @remark Always closes socket in case of error + * @remark Cares about errno + */ +int jk_tcp_socket_recvfull(jk_sock_t sd, unsigned char *b, int len, jk_logger_t *l) +{ + int rdlen = 0; + int rd; + + JK_TRACE_ENTER(l); + + errno = 0; + while (rdlen < len) { + do { +#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__)) + rd = recv(sd, (char *)b + rdlen, + len - rdlen, 0); + if (JK_IS_SOCKET_ERROR(rd)) + JK_GET_SOCKET_ERRNO(); +#else + rd = read(sd, (char *)b + rdlen, len - rdlen); +#endif + } while (JK_IS_SOCKET_ERROR(rd) && errno == EINTR); + + if (JK_IS_SOCKET_ERROR(rd)) { + int err = (errno > 0) ? -errno : errno; + jk_shutdown_socket(sd, l); + JK_TRACE_EXIT(l); + return (err == 0) ? JK_SOCKET_EOF : err; + } + else if (rd == 0) { + jk_shutdown_socket(sd, l); + JK_TRACE_EXIT(l); + return JK_SOCKET_EOF; + } + rdlen += rd; + } + + JK_TRACE_EXIT(l); + return rdlen; +} + +/** + * dump a sockaddr_in in A.B.C.D:P in ASCII buffer + * + */ +char *jk_dump_hinfo(struct sockaddr_in *saddr, char *buf) +{ + unsigned long laddr = (unsigned long)htonl(saddr->sin_addr.s_addr); + unsigned short lport = (unsigned short)htons(saddr->sin_port); + + sprintf(buf, "%d.%d.%d.%d:%d", + (int)(laddr >> 24), (int)((laddr >> 16) & 0xff), + (int)((laddr >> 8) & 0xff), (int)(laddr & 0xff), (int)lport); + + return buf; +} + +char *jk_dump_sinfo(jk_sock_t sd, char *buf) +{ + struct sockaddr_in rsaddr; + struct sockaddr_in lsaddr; + socklen_t salen; + + salen = sizeof(struct sockaddr); + if (getsockname(sd, (struct sockaddr *)&lsaddr, &salen) == 0) { + salen = sizeof(struct sockaddr); + if (getpeername(sd, (struct sockaddr *)&rsaddr, &salen) == 0) { + unsigned long laddr = (unsigned long)htonl(lsaddr.sin_addr.s_addr); + unsigned short lport = (unsigned short)htons(lsaddr.sin_port); + unsigned long raddr = (unsigned long)htonl(rsaddr.sin_addr.s_addr); + unsigned short rport = (unsigned short)htons(rsaddr.sin_port); + sprintf(buf, "%d.%d.%d.%d:%d -> %d.%d.%d.%d:%d", + (int)(laddr >> 24), (int)((laddr >> 16) & 0xff), + (int)((laddr >> 8) & 0xff), (int)(laddr & 0xff), (int)lport, + (int)(raddr >> 24), (int)((raddr >> 16) & 0xff), + (int)((raddr >> 8) & 0xff), (int)(raddr & 0xff), (int)rport); + return buf; + } + } + sprintf(buf, "error=%d", errno); + return buf; +} + +/** Wait for input event on socket until timeout + * @param sd socket to use + * @param timeout wait timeout in milliseconds + * @param l logger + * @return JK_FALSE: Timeout expired without something to read + * JK_FALSE: Error during waiting + * JK_TRUE: success + * @remark Does not close socket in case of error + * to allow for iterative waiting + * @remark Cares about errno + */ +#ifdef HAVE_POLL +int jk_is_input_event(jk_sock_t sd, int timeout, jk_logger_t *l) +{ + struct pollfd fds; + int rc; + int save_errno; + char buf[64]; + + JK_TRACE_ENTER(l); + + errno = 0; + fds.fd = sd; + fds.events = POLLIN; + fds.revents = 0; + + do { + rc = poll(&fds, 1, timeout); + } while (rc < 0 && errno == EINTR); + + if (rc == 0) { + if (JK_IS_DEBUG_LEVEL(l)) { + jk_log(l, JK_LOG_DEBUG, + "timeout during poll on socket %d [%s] (timeout=%d)", + sd, jk_dump_sinfo(sd, buf), timeout); + } + /* Timeout. Set the errno to timeout */ + errno = ETIMEDOUT; + JK_TRACE_EXIT(l); + return JK_FALSE; + } + else if (rc < 0) { + save_errno = errno; + if (JK_IS_DEBUG_LEVEL(l)) { + jk_log(l, JK_LOG_DEBUG, + "error during poll on socket %d [%s] (errno=%d)", + sd, jk_dump_sinfo(sd, buf), errno); + } + errno = save_errno; + JK_TRACE_EXIT(l); + return JK_FALSE; + } + if ((fds.revents & (POLLERR | POLLHUP))) { + save_errno = fds.revents & (POLLERR | POLLHUP); + if (JK_IS_DEBUG_LEVEL(l)) { + jk_log(l, JK_LOG_DEBUG, + "error event during poll on socket %d [%s] (event=%d)", + sd, jk_dump_sinfo(sd, buf), save_errno); + } + errno = save_errno; + JK_TRACE_EXIT(l); + return JK_FALSE; + } + errno = 0; + JK_TRACE_EXIT(l); + return JK_TRUE; +} +#else +int jk_is_input_event(jk_sock_t sd, int timeout, jk_logger_t *l) +{ + fd_set rset; + struct timeval tv; + int rc; + int save_errno; + char buf[64]; + + JK_TRACE_ENTER(l); + + errno = 0; + FD_ZERO(&rset); + FD_SET(sd, &rset); + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + + do { + rc = select((int)sd + 1, &rset, NULL, NULL, &tv); + } while (rc < 0 && errno == EINTR); + + if (rc == 0) { + if (JK_IS_DEBUG_LEVEL(l)) { + jk_log(l, JK_LOG_DEBUG, + "timeout during select on socket %d [%s] (timeout=%d)", + sd, jk_dump_sinfo(sd, buf), timeout); + } + /* Timeout. Set the errno to timeout */ +#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__)) + errno = WSAETIMEDOUT - WSABASEERR; +#else + errno = ETIMEDOUT; +#endif + JK_TRACE_EXIT(l); + return JK_FALSE; + } + else if (rc < 0) { + save_errno = errno; + if (JK_IS_DEBUG_LEVEL(l)) { + jk_log(l, JK_LOG_DEBUG, + "error during select on socket %d [%s] (errno=%d)", + sd, jk_dump_sinfo(sd, buf), errno); + } + errno = save_errno; + JK_TRACE_EXIT(l); + return JK_FALSE; + } + errno = 0; + JK_TRACE_EXIT(l); + return JK_TRUE; +} +#endif + +/** Test if a socket is still connected + * @param sd socket to use + * @param l logger + * @return JK_FALSE: failure + * JK_TRUE: success + * @remark Always closes socket in case of error + * @remark Cares about errno + */ +#ifdef HAVE_POLL +int jk_is_socket_connected(jk_sock_t sd, jk_logger_t *l) +{ + struct pollfd fds; + int rc; + + JK_TRACE_ENTER(l); + + errno = 0; + fds.fd = sd; + fds.events = POLLIN; + + do { + rc = poll(&fds, 1, 0); + } while (rc < 0 && errno == EINTR); + + if (rc == 0) { + /* If we get a timeout, then we are still connected */ + JK_TRACE_EXIT(l); + return JK_TRUE; + } + else if (rc == 1 && fds.revents == POLLIN) { + char buf; + do { + rc = (int)recvfrom(sd, &buf, 1, MSG_PEEK, NULL, NULL); + } while (rc < 0 && errno == EINTR); + if (rc == 1) { + /* There is at least one byte to read. */ + JK_TRACE_EXIT(l); + return JK_TRUE; + } + } + jk_shutdown_socket(sd, l); + + JK_TRACE_EXIT(l); + return JK_FALSE; +} + +#else +int jk_is_socket_connected(jk_sock_t sd, jk_logger_t *l) +{ + fd_set fd; + struct timeval tv; + int rc; + + JK_TRACE_ENTER(l); + + errno = 0; + FD_ZERO(&fd); + FD_SET(sd, &fd); + + /* Initially test the socket without any blocking. + */ + tv.tv_sec = 0; + tv.tv_usec = 0; + + do { + rc = select((int)sd + 1, &fd, NULL, NULL, &tv); + JK_GET_SOCKET_ERRNO(); + /* Wait one microsecond on next select, if EINTR */ + tv.tv_sec = 0; + tv.tv_usec = 1; + } while (JK_IS_SOCKET_ERROR(rc) && errno == EINTR); + + errno = 0; + if (rc == 0) { + /* If we get a timeout, then we are still connected */ + JK_TRACE_EXIT(l); + return JK_TRUE; + } + else if (rc == 1) { +#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__)) + u_long nr; + rc = ioctlsocket(sd, FIONREAD, &nr); + if (rc == 0) { + if (WSAGetLastError() == 0) + errno = 0; + else + JK_GET_SOCKET_ERRNO(); + } +#else + int nr; + rc = ioctl(sd, FIONREAD, (void*)&nr); +#endif + if (rc == 0 && nr != 0) { + JK_TRACE_EXIT(l); + return JK_TRUE; + } + JK_GET_SOCKET_ERRNO(); + } + jk_shutdown_socket(sd, l); + + JK_TRACE_EXIT(l); + return JK_FALSE; +} +#endif + |