+{
+ jb_socket fd;
+ int forwarded_connect_retries = 0;
+
+ do
+ {
+ /*
+ * XXX: The whole errno overloading is ridiculous and should
+ * be replaced with something sane and thread safe
+ */
+ /* errno = 0;*/
+#ifdef HAVE_RFC2553
+ fd = rfc2553_connect_to(host, portnum, csp);
+#else
+ fd = no_rfc2553_connect_to(host, portnum, csp);
+#endif
+ if ((fd != JB_INVALID_SOCKET) || (errno == EINVAL)
+ || (csp->fwd == NULL)
+ || ((csp->fwd->forward_host == NULL) && (csp->fwd->type == SOCKS_NONE)))
+ {
+ break;
+ }
+ forwarded_connect_retries++;
+ if (csp->config->forwarded_connect_retries != 0)
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "Attempt %d of %d to connect to %s failed. Trying again.",
+ forwarded_connect_retries, csp->config->forwarded_connect_retries + 1, host);
+ }
+
+ } while (forwarded_connect_retries < csp->config->forwarded_connect_retries);
+
+ return fd;
+}
+
+#ifdef HAVE_RFC2553
+/* Getaddrinfo implementation */
+static jb_socket rfc2553_connect_to(const char *host, int portnum, struct client_state *csp)
+{
+ struct addrinfo hints, *result, *rp;
+ char service[6];
+ int retval;
+ jb_socket fd;
+ fd_set wfds;
+ struct timeval timeout;
+#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
+ int flags;
+#endif
+ int connect_failed;
+ /*
+ * XXX: Initializeing it here is only necessary
+ * because not all situations are properly
+ * covered yet.
+ */
+ int socket_error = 0;
+
+#ifdef FEATURE_ACL
+ struct access_control_addr dst[1];
+#endif /* def FEATURE_ACL */
+
+ /* Don't leak memory when retrying. */
+ freez(csp->error_message);
+ freez(csp->http->host_ip_addr_str);
+
+ retval = snprintf(service, sizeof(service), "%d", portnum);
+ if ((-1 == retval) || (sizeof(service) <= retval))
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "Port number (%d) ASCII decimal representation doesn't fit into 6 bytes",
+ portnum);
+ csp->error_message = strdup("Invalid port number");
+ csp->http->host_ip_addr_str = strdup("unknown");
+ return(JB_INVALID_SOCKET);
+ }
+
+ memset((char *)&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICSERV; /* avoid service look-up */
+#ifdef AI_ADDRCONFIG
+ hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+ if ((retval = getaddrinfo(host, service, &hints, &result)))
+ {
+ log_error(LOG_LEVEL_INFO,
+ "Can not resolve %s: %s", host, gai_strerror(retval));
+ /* XXX: Should find a better way to propagate this error. */
+ errno = EINVAL;
+ csp->error_message = strdup(gai_strerror(retval));
+ csp->http->host_ip_addr_str = strdup("unknown");
+ return(JB_INVALID_SOCKET);
+ }
+
+ csp->http->host_ip_addr_str = malloc_or_die(NI_MAXHOST);
+
+ for (rp = result; rp != NULL; rp = rp->ai_next)
+ {
+
+#ifdef FEATURE_ACL
+ memcpy(&dst->addr, rp->ai_addr, rp->ai_addrlen);
+
+ if (block_acl(dst, csp))
+ {
+#ifdef __OS2__
+ socket_error = errno = SOCEPERM;
+#else
+ socket_error = errno = EPERM;
+#endif
+ continue;
+ }
+#endif /* def FEATURE_ACL */
+
+ retval = getnameinfo(rp->ai_addr, rp->ai_addrlen,
+ csp->http->host_ip_addr_str, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
+ if (retval)
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "Failed to get the host name from the socket structure: %s",
+ gai_strerror(retval));
+ continue;
+ }
+
+ fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+#ifdef _WIN32
+ if (fd == JB_INVALID_SOCKET)
+#else
+ if (fd < 0)
+#endif
+ {
+ continue;
+ }
+
+#ifndef _WIN32
+ if (fd >= FD_SETSIZE)
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "Server socket number too high to use select(): %d >= %d",
+ fd, FD_SETSIZE);
+ close_socket(fd);
+ freeaddrinfo(result);
+ return JB_INVALID_SOCKET;
+ }
+#endif
+
+#ifdef FEATURE_EXTERNAL_FILTERS
+ mark_socket_for_close_on_execute(fd);
+#endif
+
+ set_no_delay_flag(fd);
+
+#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
+ if ((flags = fcntl(fd, F_GETFL, 0)) != -1)
+ {
+ flags |= O_NDELAY;
+ fcntl(fd, F_SETFL, flags);
+ }
+#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */
+
+ connect_failed = 0;
+ while (connect(fd, rp->ai_addr, rp->ai_addrlen) == JB_INVALID_SOCKET)
+ {
+#ifdef __OS2__
+ errno = sock_errno();
+#endif /* __OS2__ */
+
+#ifdef _WIN32
+ if (errno == WSAEINPROGRESS)
+#else /* ifndef _WIN32 */
+ if (errno == EINPROGRESS)
+#endif /* ndef _WIN32 || __OS2__ */
+ {
+ break;
+ }
+
+ if (errno != EINTR)
+ {
+ socket_error = errno;
+ close_socket(fd);
+ connect_failed = 1;
+ break;
+ }
+ }
+ if (connect_failed)
+ {
+ continue;
+ }
+
+#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
+ if (flags != -1)
+ {
+ flags &= ~O_NDELAY;
+ fcntl(fd, F_SETFL, flags);
+ }
+#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */
+
+ /* wait for connection to complete */
+ FD_ZERO(&wfds);
+ FD_SET(fd, &wfds);
+
+ memset(&timeout, 0, sizeof(timeout));
+ timeout.tv_sec = 30;
+
+ /* MS Windows uses int, not SOCKET, for the 1st arg of select(). Weird! */
+ if ((select((int)fd + 1, NULL, &wfds, NULL, &timeout) > 0)
+ && FD_ISSET(fd, &wfds))
+ {
+ socklen_t optlen = sizeof(socket_error);
+ if (!getsockopt(fd, SOL_SOCKET, SO_ERROR, &socket_error, &optlen))
+ {
+ if (!socket_error)
+ {
+ /* Connection established, no need to try other addresses. */
+ break;
+ }
+ if (rp->ai_next != NULL)
+ {
+ /*
+ * There's another address we can try, so log that this
+ * one didn't work out. If the last one fails, too,
+ * it will get logged outside the loop body so we don't
+ * have to mention it here.
+ */
+ log_error(LOG_LEVEL_CONNECT, "Could not connect to [%s]:%s: %s.",
+ csp->http->host_ip_addr_str, service, strerror(socket_error));
+ }
+ }
+ else
+ {
+ socket_error = errno;
+ log_error(LOG_LEVEL_ERROR, "Could not get the state of "
+ "the connection to [%s]:%s: %s; dropping connection.",
+ csp->http->host_ip_addr_str, service, strerror(errno));
+ }
+ }
+
+ /* Connection failed, try next address */
+ close_socket(fd);
+ }
+
+ freeaddrinfo(result);
+ if (!rp)
+ {
+ log_error(LOG_LEVEL_CONNECT, "Could not connect to [%s]:%s: %s.",
+ host, service, strerror(socket_error));
+ csp->error_message = strdup(strerror(socket_error));
+ return(JB_INVALID_SOCKET);
+ }
+ log_error(LOG_LEVEL_CONNECT, "Connected to %s[%s]:%s.",
+ host, csp->http->host_ip_addr_str, service);
+
+ return(fd);
+
+}
+
+#else /* ndef HAVE_RFC2553 */
+/* Pre-getaddrinfo implementation */
+
+static jb_socket no_rfc2553_connect_to(const char *host, int portnum, struct client_state *csp)