diff --git a/CHANGES b/CHANGES index 74be35ab3a..91e051da37 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,10 @@ Changes with Apache 2.0.34-dev + *) Proxy HTTP and CONNECT: Keep trying other addresses from the DNS + when we can't get a socket in the specified address family. We may + have gotten back an IPv6 address first and yet our system is not + configured to allow IPv6 sockets. [Jeff Trawick] + *) Be more careful about recursively removing CVS directories. Make sure that we aren't cd'ing to their home directory first. [Aaron Bannert] diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h index 34edcfb2eb..9047ebea67 100644 --- a/modules/proxy/mod_proxy.h +++ b/modules/proxy/mod_proxy.h @@ -270,5 +270,6 @@ PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r); PROXY_DECLARE(apr_status_t) ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb, char *buff, size_t bufflen, int *eos); PROXY_DECLARE(void) ap_proxy_reset_output_filters(conn_rec *c); PROXY_DECLARE(void) ap_proxy_table_unmerge(apr_pool_t *p, apr_table_t *t, char *key); +PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **, const char *, apr_sockaddr_t *, const char *, proxy_server_conf *, server_rec *, apr_pool_t *); #endif /*MOD_PROXY_H*/ diff --git a/modules/proxy/proxy_connect.c b/modules/proxy/proxy_connect.c index c898a61dc9..31a0c139ee 100644 --- a/modules/proxy/proxy_connect.c +++ b/modules/proxy/proxy_connect.c @@ -129,7 +129,7 @@ int ap_proxy_connect_handler(request_rec *r, proxy_server_conf *conf, apr_size_t i, o, nbytes; char buffer[HUGE_STRING_LEN]; apr_socket_t *client_socket = ap_get_module_config(r->connection->conn_config, &core_module); - + int failed; apr_pollfd_t *pollfd; apr_int32_t pollcnt; apr_int16_t pollevent; @@ -178,7 +178,7 @@ int ap_proxy_connect_handler(request_rec *r, proxy_server_conf *conf, connectport = uri.port; connect_addr = uri_addr; } - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, "proxy: CONNECT: connecting to remote proxy %s on port %d", connectname, connectport); /* check if ProxyBlock directive on this host */ @@ -195,11 +195,13 @@ int ap_proxy_connect_handler(request_rec *r, proxy_server_conf *conf, case APR_URI_SNEWS_DEFAULT_PORT: break; default: + /* XXX can we call ap_proxyerror() here to get a nice log message? */ return HTTP_FORBIDDEN; } - } else if(!allowed_port(conf, uri.port)) + } else if(!allowed_port(conf, uri.port)) { + /* XXX can we call ap_proxyerror() here to get a nice log message? */ return HTTP_FORBIDDEN; - + } /* * Step Two: Make the Connection @@ -216,77 +218,30 @@ int ap_proxy_connect_handler(request_rec *r, proxy_server_conf *conf, connectname, NULL)); } - /* - * At this point we have a list of one or more IP addresses of - * the machine to connect to. If configured, reorder this - * list so that the "best candidate" is first try. "best - * candidate" could mean the least loaded server, the fastest - * responding server, whatever. - * - * For now we do nothing, ie we get DNS round robin. - * XXX FIXME - * - * We have to create a new socket each time through the loop because - * - * (1) On most stacks, connect() fails with EINVAL or similar if - * we previously failed connect() on the socket in the past - * (2) The address family of the socket needs to match that of the - * address we're trying to connect to. - */ + /* + * At this point we have a list of one or more IP addresses of + * the machine to connect to. If configured, reorder this + * list so that the "best candidate" is first try. "best + * candidate" could mean the least loaded server, the fastest + * responding server, whatever. + * + * For now we do nothing, ie we get DNS round robin. + * XXX FIXME + */ + failed = ap_proxy_connect_to_backend(&sock, "CONNECT", connect_addr, + connectname, conf, r->server, + r->pool); - /* try each IP address until we connect successfully */ - { - int failed = 1; - while (connect_addr) { - - /* create a new socket */ - if ((rv = apr_socket_create(&sock, connect_addr->family, SOCK_STREAM, r->pool)) != APR_SUCCESS) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, - "proxy: error creating socket"); - return HTTP_INTERNAL_SERVER_ERROR; - } - - /* Set a timeout on the socket */ - if (conf->timeout_set == 1 ) { - apr_setsocketopt(sock, - APR_SO_TIMEOUT, - (int)(conf->timeout * APR_USEC_PER_SEC)); - } - else { - apr_setsocketopt(sock, - APR_SO_TIMEOUT, - (int)(r->server->timeout * APR_USEC_PER_SEC)); - } - - /* make the connection out of the socket */ - rv = apr_connect(sock, connect_addr); - - /* if an error occurred, loop round and try again */ - if (rv != APR_SUCCESS) { - apr_socket_close(sock); - ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, - "proxy: CONNECT: attempt to connect to %pI (%s) failed", connect_addr, connectname); - connect_addr = connect_addr->next; - continue; - } - - /* if we get here, all is well */ - failed = 0; - break; - } - - /* handle a permanent error from the above loop */ - if (failed) { - if (proxyname) { - return DECLINED; - } - else { - return HTTP_BAD_GATEWAY; - } - } + /* handle a permanent error from the above loop */ + if (failed) { + if (proxyname) { + return DECLINED; + } + else { + return HTTP_BAD_GATEWAY; + } } - /* * Step Three: Send the Request * diff --git a/modules/proxy/proxy_http.c b/modules/proxy/proxy_http.c index 73506367fa..214967ebaf 100644 --- a/modules/proxy/proxy_http.c +++ b/modules/proxy/proxy_http.c @@ -276,7 +276,6 @@ apr_status_t ap_proxy_http_create_connection(apr_pool_t *p, request_rec *r, proxy_server_conf *conf, const char *proxyname) { int failed=0, new=0; - apr_status_t rv; apr_socket_t *client_socket = NULL; /* We have determined who to connect to. Now make the connection, supporting @@ -353,69 +352,12 @@ apr_status_t ap_proxy_http_create_connection(apr_pool_t *p, request_rec *r, * * For now we do nothing, ie we get DNS round robin. * XXX FIXME - * - * We have to create a new socket each time through the loop because - * - * (1) On most stacks, connect() fails with EINVAL or similar if - * we previously failed connect() on the socket in the past - * (2) The address family of the socket needs to match that of the - * address we're trying to connect to. */ + failed = ap_proxy_connect_to_backend(&p_conn->sock, "HTTP", + p_conn->addr, p_conn->name, + conf, r->server, c->pool); - /* try each IP address until we connect successfully */ - failed = 1; - while (p_conn->addr) { - - /* see memory note above */ - if ((rv = apr_socket_create(&p_conn->sock, p_conn->addr->family, - SOCK_STREAM, c->pool)) != APR_SUCCESS) { - ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, - "proxy: error creating socket"); - return HTTP_INTERNAL_SERVER_ERROR; - } - -#if !defined(TPF) && !defined(BEOS) - if (conf->recv_buffer_size > 0 && - (rv = apr_setsocketopt(p_conn->sock, APR_SO_RCVBUF, - conf->recv_buffer_size))) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, - "setsockopt(SO_RCVBUF): Failed to set " - "ProxyReceiveBufferSize, using default"); - } -#endif - - /* Set a timeout on the socket */ - if (conf->timeout_set == 1) { - apr_setsocketopt(p_conn->sock, APR_SO_TIMEOUT, - (int)(conf->timeout * APR_USEC_PER_SEC)); - } - else { - apr_setsocketopt(p_conn->sock, APR_SO_TIMEOUT, - (int)(r->server->timeout * APR_USEC_PER_SEC)); - } - - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, - "proxy: socket has been created"); - - /* make the connection out of the socket */ - rv = apr_connect(p_conn->sock, p_conn->addr); - - /* if an error occurred, loop round and try again */ - if (rv != APR_SUCCESS) { - apr_socket_close(p_conn->sock); - ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, - "proxy: attempt to connect to %pI (%s) failed", - p_conn->addr, p_conn->name); - p_conn->addr = p_conn->addr->next; - continue; - } - - /* if we get here, all is well */ - failed = 0; - break; - } - - /* handle a permanent error from the above loop */ + /* handle a permanent error on the connect */ if (failed) { if (proxyname) { return DECLINED; diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c index 86e48dcd3e..640e3389be 100644 --- a/modules/proxy/proxy_util.c +++ b/modules/proxy/proxy_util.c @@ -1103,3 +1103,77 @@ PROXY_DECLARE(void) ap_proxy_table_unmerge(apr_pool_t *p, apr_table_t *t, char * } apr_table_add(t, key, value + offset); } + +PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **newsock, + const char *proxy_function, + apr_sockaddr_t *backend_addr, + const char *backend_name, + proxy_server_conf *conf, + server_rec *s, + apr_pool_t *p) +{ + apr_status_t rv; + int connected = 0; + int loglevel; + + while (backend_addr && !connected) { + if ((rv = apr_socket_create(newsock, backend_addr->family, + SOCK_STREAM, p)) != APR_SUCCESS) { + loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR; + ap_log_error(APLOG_MARK, loglevel, rv, s, + "proxy: %s: error creating fam %d socket for target %s", + proxy_function, + backend_addr->family, + backend_name); + /* this could be an IPv6 address from the DNS but the + * local machine won't give us an IPv6 socket; hopefully the + * DNS returned an additional address to try + */ + backend_addr = backend_addr->next; + continue; + } + +#if !defined(TPF) && !defined(BEOS) + if (conf->recv_buffer_size > 0 && + (rv = apr_setsocketopt(*newsock, APR_SO_RCVBUF, + conf->recv_buffer_size))) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, + "setsockopt(SO_RCVBUF): Failed to set " + "ProxyReceiveBufferSize, using default"); + } +#endif + + /* Set a timeout on the socket */ + if (conf->timeout_set == 1) { + apr_setsocketopt(*newsock, APR_SO_TIMEOUT, + (int)(conf->timeout * APR_USEC_PER_SEC)); + } + else { + apr_setsocketopt(*newsock, APR_SO_TIMEOUT, + (int)(s->timeout * APR_USEC_PER_SEC)); + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, s, + "proxy: %s: fam %d socket created to connect to %s", + proxy_function, backend_addr->family, backend_name); + + /* make the connection out of the socket */ + rv = apr_connect(*newsock, backend_addr); + + /* if an error occurred, loop round and try again */ + if (rv != APR_SUCCESS) { + apr_socket_close(*newsock); + loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR; + ap_log_error(APLOG_MARK, loglevel, rv, s, + "proxy: %s: attempt to connect to %pI (%s) failed", + proxy_function, + backend_addr, + backend_name); + backend_addr = backend_addr->next; + continue; + } + connected = 1; + } + return connected ? 0 : 1; +} +