From 1151bac94baa9be8b69a0c8b6598f521bad8fec6 Mon Sep 17 00:00:00 2001 From: Jeff Trawick Date: Fri, 8 Mar 2002 02:03:47 +0000 Subject: [PATCH] implement a common function for getting a socket and trying to connect to the target host; use that common function for proxy HTTP and proxy CONNECT In that new function, fix this problem: 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. An example host is www.ipv6.org. The first address I get back is an IPv6 address, but my machine may not be configured to allow an AF_INET6 socket. Before the fix: The apr_socket() failure was fatal. After the fix: When apr_socket() fails, we go to the next address from the resolver, successfully create a socket in the specified family (AF_INET), and all is well. (an unrelated fix in this commit was to pass a server_rec in a broken ap_log_error() call) git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@93787 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 5 ++ modules/proxy/mod_proxy.h | 1 + modules/proxy/proxy_connect.c | 101 ++++++++++------------------------ modules/proxy/proxy_http.c | 66 ++-------------------- modules/proxy/proxy_util.c | 74 +++++++++++++++++++++++++ 5 files changed, 112 insertions(+), 135 deletions(-) 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. - */ - - /* 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; - } - } + /* + * 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); + + /* 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; +} + -- 2.40.0