apr_status_t apr_socket_timeout_set(apr_socket_t *sock, apr_interval_time_t t) { apr_status_t stat; /* If our new timeout is non-negative and our old timeout was * negative, then we need to ensure that we are non-blocking. * Conversely, if our new timeout is negative and we had * non-negative timeout, we must make sure our socket is blocking. * We want to avoid calling fcntl more than necessary on the * socket. */ if (t >= 0 && sock->timeout < 0) { if (apr_is_option_set(sock, APR_SO_NONBLOCK) != 1) { if ((stat = sononblock(sock->socketdes)) != APR_SUCCESS) { return stat; } apr_set_option(sock, APR_SO_NONBLOCK, 1); } } else if (t < 0 && sock->timeout >= 0) { if (apr_is_option_set(sock, APR_SO_NONBLOCK) != 0) { if ((stat = soblock(sock->socketdes)) != APR_SUCCESS) { return stat; } apr_set_option(sock, APR_SO_NONBLOCK, 0); } } /* must disable the incomplete read support if we disable * a timeout */ if (t <= 0) { sock->options &= ~APR_INCOMPLETE_READ; } sock->timeout = t; return APR_SUCCESS; }
apr_status_t apr_socket_opt_set(apr_socket_t *sock, apr_int32_t opt, apr_int32_t on) { int one; apr_status_t rv; if (on) one = 1; else one = 0; switch(opt) { case APR_SO_KEEPALIVE: #ifdef SO_KEEPALIVE if (on != apr_is_option_set(sock, APR_SO_KEEPALIVE)) { if (setsockopt(sock->socketdes, SOL_SOCKET, SO_KEEPALIVE, (void *)&one, sizeof(int)) == -1) { return errno; } apr_set_option(sock, APR_SO_KEEPALIVE, on); } #else return APR_ENOTIMPL; #endif break; case APR_SO_DEBUG: if (on != apr_is_option_set(sock, APR_SO_DEBUG)) { if (setsockopt(sock->socketdes, SOL_SOCKET, SO_DEBUG, (void *)&one, sizeof(int)) == -1) { return errno; } apr_set_option(sock, APR_SO_DEBUG, on); } break; case APR_SO_BROADCAST: #ifdef SO_BROADCAST if (on != apr_is_option_set(sock, APR_SO_BROADCAST)) { if (setsockopt(sock->socketdes, SOL_SOCKET, SO_BROADCAST, (void *)&one, sizeof(int)) == -1) { return errno; } apr_set_option(sock, APR_SO_BROADCAST, on); } #else return APR_ENOTIMPL; #endif break; case APR_SO_REUSEADDR: if (on != apr_is_option_set(sock, APR_SO_REUSEADDR)) { if (setsockopt(sock->socketdes, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(int)) == -1) { return errno; } apr_set_option(sock, APR_SO_REUSEADDR, on); } break; case APR_SO_SNDBUF: #ifdef SO_SNDBUF if (setsockopt(sock->socketdes, SOL_SOCKET, SO_SNDBUF, (void *)&on, sizeof(int)) == -1) { return errno; } #else return APR_ENOTIMPL; #endif break; case APR_SO_RCVBUF: #ifdef SO_RCVBUF if (setsockopt(sock->socketdes, SOL_SOCKET, SO_RCVBUF, (void *)&on, sizeof(int)) == -1) { return errno; } #else return APR_ENOTIMPL; #endif break; case APR_SO_NONBLOCK: if (apr_is_option_set(sock, APR_SO_NONBLOCK) != on) { if (on) { if ((rv = sononblock(sock->socketdes)) != APR_SUCCESS) return rv; } else { if ((rv = soblock(sock->socketdes)) != APR_SUCCESS) return rv; } apr_set_option(sock, APR_SO_NONBLOCK, on); } break; case APR_SO_LINGER: #ifdef SO_LINGER if (apr_is_option_set(sock, APR_SO_LINGER) != on) { struct linger li; li.l_onoff = on; li.l_linger = APR_MAX_SECS_TO_LINGER; if (setsockopt(sock->socketdes, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(struct linger)) == -1) { return errno; } apr_set_option(sock, APR_SO_LINGER, on); } #else return APR_ENOTIMPL; #endif break; case APR_TCP_DEFER_ACCEPT: #if defined(TCP_DEFER_ACCEPT) if (apr_is_option_set(sock, APR_TCP_DEFER_ACCEPT) != on) { int optlevel = IPPROTO_TCP; int optname = TCP_DEFER_ACCEPT; if (setsockopt(sock->socketdes, optlevel, optname, (void *)&on, sizeof(int)) == -1) { return errno; } apr_set_option(sock, APR_TCP_DEFER_ACCEPT, on); } #else return APR_ENOTIMPL; #endif break; case APR_TCP_NODELAY: #if defined(TCP_NODELAY) if (apr_is_option_set(sock, APR_TCP_NODELAY) != on) { int optlevel = IPPROTO_TCP; int optname = TCP_NODELAY; #if APR_HAVE_SCTP if (sock->protocol == IPPROTO_SCTP) { optlevel = IPPROTO_SCTP; optname = SCTP_NODELAY; } #endif if (setsockopt(sock->socketdes, optlevel, optname, (void *)&on, sizeof(int)) == -1) { return errno; } apr_set_option(sock, APR_TCP_NODELAY, on); } #else /* BeOS pre-BONE has TCP_NODELAY set by default. * As it can't be turned off we might as well check if they're asking * for it to be turned on! */ #ifdef BEOS if (on == 1) return APR_SUCCESS; else #endif return APR_ENOTIMPL; #endif break; case APR_TCP_NOPUSH: #if APR_TCP_NOPUSH_FLAG /* TCP_NODELAY and TCP_CORK are mutually exclusive on Linux * kernels < 2.6; on newer kernels they can be used together * and TCP_CORK takes preference, which is the desired * behaviour. On older kernels, TCP_NODELAY must be toggled * to "off" whilst TCP_CORK is in effect. */ if (apr_is_option_set(sock, APR_TCP_NOPUSH) != on) { #ifndef HAVE_TCP_NODELAY_WITH_CORK int optlevel = IPPROTO_TCP; int optname = TCP_NODELAY; #if APR_HAVE_SCTP if (sock->protocol == IPPROTO_SCTP) { optlevel = IPPROTO_SCTP; optname = SCTP_NODELAY; } #endif /* OK we're going to change some settings here... */ if (apr_is_option_set(sock, APR_TCP_NODELAY) == 1 && on) { /* Now toggle TCP_NODELAY to off, if TCP_CORK is being * turned on: */ int tmpflag = 0; if (setsockopt(sock->socketdes, optlevel, optname, (void*)&tmpflag, sizeof(int)) == -1) { return errno; } apr_set_option(sock, APR_RESET_NODELAY, 1); apr_set_option(sock, APR_TCP_NODELAY, 0); } else if (on) { apr_set_option(sock, APR_RESET_NODELAY, 0); } #endif /* HAVE_TCP_NODELAY_WITH_CORK */ /* OK, now we can just set the TCP_NOPUSH flag accordingly...*/ if (setsockopt(sock->socketdes, IPPROTO_TCP, APR_TCP_NOPUSH_FLAG, (void*)&on, sizeof(int)) == -1) { return errno; } apr_set_option(sock, APR_TCP_NOPUSH, on); #ifndef HAVE_TCP_NODELAY_WITH_CORK if (!on && apr_is_option_set(sock, APR_RESET_NODELAY)) { /* Now, if TCP_CORK was just turned off, turn * TCP_NODELAY back on again if it was earlier toggled * to off: */ int tmpflag = 1; if (setsockopt(sock->socketdes, optlevel, optname, (void*)&tmpflag, sizeof(int)) == -1) { return errno; } apr_set_option(sock, APR_RESET_NODELAY,0); apr_set_option(sock, APR_TCP_NODELAY, 1); } #endif /* HAVE_TCP_NODELAY_WITH_CORK */ } #else return APR_ENOTIMPL; #endif break; case APR_INCOMPLETE_READ: apr_set_option(sock, APR_INCOMPLETE_READ, on); break; case APR_IPV6_V6ONLY: #if APR_HAVE_IPV6 && defined(IPV6_V6ONLY) /* we don't know the initial setting of this option, * so don't check sock->options since that optimization * won't work */ if (setsockopt(sock->socketdes, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(int)) == -1) { return errno; } apr_set_option(sock, APR_IPV6_V6ONLY, on); #else return APR_ENOTIMPL; #endif break; default: return APR_EINVAL; } return APR_SUCCESS; }
/** 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[DUMP_SINFO_BUF_SZ]; 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, sizeof(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. */ int num = 0; do { num++; #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) && num < MAX_READ_RETRY); 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; }
/** Non-blocking socket connect * @param sd socket to connect * @param addr address to connect to * @param source optional source address * @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, jk_sockaddr_t *addr, jk_sockaddr_t *source, int timeout, jk_logger_t *l) { int rc = 0; char buf[64]; JK_TRACE_ENTER(l); if (source != NULL) { if (bind(sd, (const struct sockaddr *)&source->sa.sin, source->salen)) { JK_GET_SOCKET_ERRNO(); jk_log(l, JK_LOG_ERROR, "error during source bind on socket %d [%s] (errno=%d)", sd, jk_dump_hinfo(source, buf, sizeof(buf)), errno); } } if (timeout > 0) { if (sononblock(sd)) { JK_TRACE_EXIT(l); return -1; } } do { rc = connect(sd, (const struct sockaddr *)&addr->sa.sin, addr->salen); } 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; }
/** Non-blocking socket connect * @param sd socket to connect * @param addr address to connect to * @param source optional source address * @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, jk_sockaddr_t *addr, jk_sockaddr_t *source, int timeout, jk_logger_t *l) { int rc; char buf[64]; JK_TRACE_ENTER(l); if (source != NULL) { if (bind(sd, (const struct sockaddr *)&source->sa.sin, source->salen)) { JK_GET_SOCKET_ERRNO(); jk_log(l, JK_LOG_ERROR, "error during source bind on socket %d [%s] (errno=%d)", sd, jk_dump_hinfo(source, buf, sizeof(buf)), errno); } } if (timeout <= 0) { rc = connect(sd, (const struct sockaddr *)&addr->sa.sin, addr->salen); JK_TRACE_EXIT(l); return rc; } if ((rc = sononblock(sd))) { JK_TRACE_EXIT(l); return -1; } if (JK_IS_SOCKET_ERROR(connect(sd, (const struct sockaddr *)&addr->sa.sin, addr->salen))) { 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; }
apr_status_t apr_socket_opt_set(apr_socket_t *sock, apr_int32_t opt, apr_int32_t on) { int one; apr_status_t rv; if (on) one = 1; else one = 0; switch(opt) { case APR_SO_KEEPALIVE: #ifdef SO_KEEPALIVE if (on != apr_is_option_set(sock->netmask, APR_SO_KEEPALIVE)) { if (setsockopt(sock->socketdes, SOL_SOCKET, SO_KEEPALIVE, (void *)&one, sizeof(int)) == -1) { return errno; } apr_set_option(&sock->netmask,APR_SO_KEEPALIVE, on); } #else return APR_ENOTIMPL; #endif break; case APR_SO_DEBUG: if (on != apr_is_option_set(sock->netmask, APR_SO_DEBUG)) { if (setsockopt(sock->socketdes, SOL_SOCKET, SO_DEBUG, (void *)&one, sizeof(int)) == -1) { return errno; } apr_set_option(&sock->netmask, APR_SO_DEBUG, on); } break; case APR_SO_REUSEADDR: if (on != apr_is_option_set(sock->netmask, APR_SO_REUSEADDR)) { if (setsockopt(sock->socketdes, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(int)) == -1) { return errno; } apr_set_option(&sock->netmask, APR_SO_REUSEADDR, on); } break; case APR_SO_SNDBUF: #ifdef SO_SNDBUF if (setsockopt(sock->socketdes, SOL_SOCKET, SO_SNDBUF, (void *)&on, sizeof(int)) == -1) { return errno; } #else return APR_ENOTIMPL; #endif break; case APR_SO_RCVBUF: #ifdef SO_RCVBUF if (setsockopt(sock->socketdes, SOL_SOCKET, SO_RCVBUF, (void *)&on, sizeof(int)) == -1) { return errno; } #else return APR_ENOTIMPL; #endif break; case APR_SO_NONBLOCK: if (apr_is_option_set(sock->netmask, APR_SO_NONBLOCK) != on) { if (on) { if ((rv = sononblock(sock->socketdes)) != APR_SUCCESS) return rv; } else { if ((rv = soblock(sock->socketdes)) != APR_SUCCESS) return rv; } apr_set_option(&sock->netmask, APR_SO_NONBLOCK, on); } break; case APR_SO_LINGER: #ifdef SO_LINGER if (apr_is_option_set(sock->netmask, APR_SO_LINGER) != on) { struct linger li; li.l_onoff = on; li.l_linger = APR_MAX_SECS_TO_LINGER; if (setsockopt(sock->socketdes, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(struct linger)) == -1) { return errno; } apr_set_option(&sock->netmask, APR_SO_LINGER, on); } #else return APR_ENOTIMPL; #endif break; case APR_SO_TIMEOUT: /* XXX: To be deprecated */ return apr_socket_timeout_set(sock, on); break; case APR_TCP_NODELAY: #if defined(TCP_NODELAY) if (apr_is_option_set(sock->netmask, APR_TCP_NODELAY) != on) { int optlevel = IPPROTO_TCP; int optname = TCP_NODELAY; #if APR_HAVE_SCTP if (sock->protocol == IPPROTO_SCTP) { optlevel = IPPROTO_SCTP; optname = SCTP_NODELAY; } #endif if (setsockopt(sock->socketdes, optlevel, optname, (void *)&on, sizeof(int)) == -1) { return errno; } apr_set_option(&sock->netmask, APR_TCP_NODELAY, on); } #else /* BeOS pre-BONE has TCP_NODELAY set by default. * As it can't be turned off we might as well check if they're asking * for it to be turned on! */ #ifdef BEOS if (on == 1) return APR_SUCCESS; else #endif return APR_ENOTIMPL; #endif break; case APR_TCP_NOPUSH: #if APR_TCP_NOPUSH_FLAG if (apr_is_option_set(sock->netmask, APR_TCP_NOPUSH) != on) { int optlevel = IPPROTO_TCP; int optname = TCP_NODELAY; #if APR_HAVE_SCTP if (sock->protocol == IPPROTO_SCTP) { optlevel = IPPROTO_SCTP; optname = SCTP_NODELAY; } #endif /* OK we're going to change some settings here... */ /* TCP_NODELAY is mutually exclusive, so do we have it set? */ if (apr_is_option_set(sock->netmask, APR_TCP_NODELAY) == 1 && on) { /* If we want to set NOPUSH then if we have the TCP_NODELAY * flag set we need to switch it off... */ int tmpflag = 0; if (setsockopt(sock->socketdes, optlevel, optname, (void*)&tmpflag, sizeof(int)) == -1) { return errno; } apr_set_option(&sock->netmask, APR_RESET_NODELAY, 1); apr_set_option(&sock->netmask, APR_TCP_NODELAY, 0); } else if (on) { apr_set_option(&sock->netmask, APR_RESET_NODELAY, 0); } /* OK, now we can just set the TCP_NOPUSH flag accordingly...*/ if (setsockopt(sock->socketdes, IPPROTO_TCP, APR_TCP_NOPUSH_FLAG, (void*)&on, sizeof(int)) == -1) { return errno; } apr_set_option(&sock->netmask, APR_TCP_NOPUSH, on); if (!on && apr_is_option_set(sock->netmask, APR_RESET_NODELAY)) { int tmpflag = 1; if (setsockopt(sock->socketdes, optlevel, optname, (void*)&tmpflag, sizeof(int)) == -1) { return errno; } apr_set_option(&sock->netmask, APR_RESET_NODELAY,0); apr_set_option(&sock->netmask, APR_TCP_NODELAY, 1); } } #else return APR_ENOTIMPL; #endif break; case APR_INCOMPLETE_READ: apr_set_option(&sock->netmask, APR_INCOMPLETE_READ, on); break; case APR_IPV6_V6ONLY: #if APR_HAVE_IPV6 && defined(IPV6_V6ONLY) /* we don't know the initial setting of this option, * so don't check sock->netmask since that optimization * won't work */ if (setsockopt(sock->socketdes, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(int)) == -1) { return errno; } apr_set_option(&sock->netmask, APR_IPV6_V6ONLY, on); #else return APR_ENOTIMPL; #endif break; default: return APR_EINVAL; } return APR_SUCCESS; }