From: Graham Leggett Date: Wed, 4 Apr 2001 18:47:42 +0000 (+0000) Subject: Some code rewriting in ap_proxy_connect_handler(): X-Git-Tag: 2.0.17~120 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5f3bc1ebc9905ad3b771a25fa37c3d544e462d6c;p=thirdparty%2Fapache%2Fhttpd.git Some code rewriting in ap_proxy_connect_handler(): *) Fixed bug where a hostname without a "." in it (such as "localhost") would not trigger an IP address check with ProxyBlock. *) Fixed ProxyBlock bugs with ap_proxy_http_handler() and ap_proxy_connect_handler(). *) Updated ap_proxy_connect_handler() to support APR, while moving some common code between http_handler and connect_handler to proxy_util.c. PR: Obtained from: Reviewed by: git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@88721 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c index 5db5eb92d67..9e53bf2b77f 100644 --- a/modules/proxy/mod_proxy.c +++ b/modules/proxy/mod_proxy.c @@ -553,16 +553,15 @@ static const char * /* Don't duplicate entries */ for (i = 0; i < conf->noproxies->nelts; i++) { - if (strcasecmp(arg, list[i].name) == 0) /* ignore case for host names */ + if (apr_strnatcasecmp(arg, list[i].name) == 0) { /* ignore case for host names */ found = 1; + } } if (!found) { new = apr_array_push(conf->noproxies); new->name = arg; - /* Don't do name lookups on things that aren't dotted */ - if (ap_strchr_c(arg, '.') != NULL && - apr_sockaddr_info_get(&addr, new->name, APR_UNSPEC, 0, 0, parms->pool)) { + if (APR_SUCCESS == apr_sockaddr_info_get(&addr, new->name, APR_UNSPEC, 0, 0, parms->pool)) { new->addr = addr; } else { diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h index b151e4c91dc..c321dc5c995 100644 --- a/modules/proxy/mod_proxy.h +++ b/modules/proxy/mod_proxy.h @@ -156,6 +156,7 @@ struct proxy_alias { struct dirconn_entry { char *name; +// struct apr_sockaddr_t *addr; struct in_addr addr, mask; struct hostent *hostentry; int (*matcher) (struct dirconn_entry * This, request_rec *r); @@ -250,6 +251,7 @@ int ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p); int ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p); int ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p); int ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p); +int ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf, apr_sockaddr_t *uri_addr); apr_status_t ap_proxy_doconnect(apr_socket_t *sock, char *host, apr_uint32_t port, request_rec *r); /* This function is called by ap_table_do() for all header lines */ int ap_proxy_send_hdr_line(void *p, const char *key, const char *value); diff --git a/modules/proxy/proxy_connect.c b/modules/proxy/proxy_connect.c index f75c193a790..35f6ae74e35 100644 --- a/modules/proxy/proxy_connect.c +++ b/modules/proxy/proxy_connect.c @@ -112,94 +112,166 @@ allowed_port(proxy_server_conf *conf, int port) int ap_proxy_connect_handler(request_rec *r, char *url, - const char *proxyhost, int proxyport) + const char *proxyname, int proxyport) { - const char *host; - char *p; - int port; + apr_pool_t *p = r->pool; apr_socket_t *sock; char buffer[HUGE_STRING_LEN]; - int nbytes, i; + int nbytes, i, err; apr_socket_t *client_sock = NULL; apr_pollfd_t *pollfd; apr_int32_t pollcnt; apr_int16_t pollevent; - + apr_sockaddr_t *uri_addr, *connect_addr; + + uri_components uri; + const char *connectname; + int connectport = 0; + void *sconf = r->server->module_config; proxy_server_conf *conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); - struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts; - /* Break the URL into host:port pairs */ - host = url; - p = strchr(url, ':'); - if (p == NULL) - port = DEFAULT_HTTPS_PORT; + + /* + * Step One: Determine Who To Connect To + * + * Break up the URL to determine the host to connect to + */ + + /* we break the URL into host, port, uri */ + if (HTTP_OK != ap_parse_uri_components(p, url, &uri)) { + return ap_proxyerror(r, HTTP_BAD_REQUEST, + apr_pstrcat(p, "URI cannot be parsed: ", url, NULL)); + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: CONNECT connecting %s to %s:%d", url, uri.hostname, uri.port); + + /* do a DNS lookup for the destination host */ + err = apr_sockaddr_info_get(&uri_addr, uri.hostname, APR_UNSPEC, uri.port, 0, p); + + /* are we connecting directly, or via a proxy? */ + if (proxyname) { + connectname = proxyname; + connectport = proxyport; + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, + "CONNECT to remote proxy %s on port %d", proxyname, proxyport); + err = apr_sockaddr_info_get(&connect_addr, proxyname, APR_UNSPEC, proxyport, 0, p); + } else { - port = atoi(p + 1); - *p = '\0'; + connectname = uri.hostname; + connectport = uri.port; + connect_addr = uri_addr; + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, + "CONNECT to %s on port %d", connectname, connectport); } -/* check if ProxyBlock directive on this host */ -/* XXX FIXME */ -/* destaddr.s_addr = ap_inet_addr(host); */ - for (i = 0; i < conf->noproxies->nelts; i++) { - if ((npent[i].name != NULL && ap_strstr_c(host, npent[i].name) != NULL) -/* || destaddr.s_addr == npent[i].addr.s_addr */ - || npent[i].name[0] == '*') - return ap_proxyerror(r, HTTP_FORBIDDEN, - "Connect to remote machine blocked"); + /* check if ProxyBlock directive on this host */ + if (OK != ap_proxy_checkproxyblock(r, conf, uri_addr)) { + return ap_proxyerror(r, HTTP_FORBIDDEN, + "Connect to remote machine blocked"); } /* Check if it is an allowed port */ if (conf->allowed_connect_ports->nelts == 0) { /* Default setting if not overridden by AllowCONNECT */ - switch (port) { + switch (uri.port) { case DEFAULT_HTTPS_PORT: case DEFAULT_SNEWS_PORT: break; default: return HTTP_FORBIDDEN; } - } else if(!allowed_port(conf, port)) + } else if(!allowed_port(conf, uri.port)) return HTTP_FORBIDDEN; - if (proxyhost) { - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, - "CONNECT to remote proxy %s on port %d", proxyhost, proxyport); - } - else { - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, - "CONNECT to %s on port %d", host, port); + + /* + * Step Two: Make the Connection + * + * We have determined who to connect to. Now make the connection. + */ + + /* get all the possible IP addresses for the destname and loop through them + * until we get a successful connection + */ + if (APR_SUCCESS != err) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_pstrcat(p, + "DNS lookup failure for: ", + connectname, NULL)); } + /* create a new socket */ if ((apr_socket_create(&sock, APR_INET, SOCK_STREAM, r->pool)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "proxy: error creating socket"); return HTTP_INTERNAL_SERVER_ERROR; } - if (ap_proxy_doconnect(sock, (char *)(proxyhost ? proxyhost : host), - proxyport ? proxyport : port, r) != APR_SUCCESS) { - apr_socket_close(sock); - return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, - apr_pstrcat(r->pool, "Could not connect to remote machine:
", - proxyhost, 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 + */ + + + /* try each IP address until we connect successfully */ + { + int failed = 1; + while (connect_addr) { + + /* make the connection out of the socket */ + err = apr_connect(sock, connect_addr); + + /* if an error occurred, loop round and try again */ + if (err != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, err, r->server, + "proxy: 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; + } + } } + + /* + * Step Three: Send the Request + * + * Send the HTTP/1.1 CONNECT request to the remote server + */ + /* If we are connecting through a remote proxy, we need to pass * the CONNECT request on to it. */ if (proxyport) { - /* FIXME: We should not be calling write() directly, but we currently - * have no alternative. Error checking ignored. Also, we force + /* FIXME: Error checking ignored. Also, we force * a HTTP/1.0 request to keep things simple. */ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, "Sending the CONNECT request to the remote proxy"); nbytes = apr_snprintf(buffer, sizeof(buffer), - "CONNECT %s HTTP/1.0" CRLF, r->uri); + "CONNECT %s HTTP/1.1" CRLF, r->uri); apr_send(sock, buffer, &nbytes); nbytes = apr_snprintf(buffer, sizeof(buffer), "Proxy-agent: %s" CRLF CRLF, ap_get_server_version()); @@ -213,6 +285,13 @@ int ap_proxy_connect_handler(request_rec *r, char *url, ap_rflush(r); } + + /* + * Step Four: Handle Data Transfer + * + * Handle two way transfer of data over the socket (this is a tunnel). + */ + if(apr_poll_setup(&pollfd, 2, r->pool) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, @@ -297,6 +376,13 @@ int ap_proxy_connect_handler(request_rec *r, char *url, break; } + + /* + * Step Five: Clean Up + * + * Close the socket and clean up + */ + apr_socket_close(sock); return OK; diff --git a/modules/proxy/proxy_http.c b/modules/proxy/proxy_http.c index e61ab5a10be..afa45964888 100644 --- a/modules/proxy/proxy_http.c +++ b/modules/proxy/proxy_http.c @@ -187,7 +187,7 @@ int ap_proxy_http_handler(request_rec *r, char *url, apr_sockaddr_t *connect_addr; char server_portstr[32]; apr_socket_t *sock; - int i, j, len, backasswards, close=0, failed=0, new=0; + int i, len, backasswards, close=0, failed=0, new=0; apr_status_t err; apr_array_header_t *headers_in_array; apr_table_entry_t *headers_in; @@ -249,31 +249,9 @@ int ap_proxy_http_handler(request_rec *r, char *url, } /* check if ProxyBlock directive on this host */ - /* XXX FIXME: conf->noproxies->elts is part of an opaque structure */ - for (j = 0; j < conf->noproxies->nelts; j++) { - struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts; - struct apr_sockaddr_t *conf_addr = npent[j].addr; - if ((npent[j].name && ap_strstr_c(uri.hostname, npent[j].name)) - || npent[j].name[0] == '*') { - return ap_proxyerror(r, HTTP_FORBIDDEN, - "Connect to remote machine blocked (by name)"); - } - while (conf_addr) { - while (uri_addr) { - char *conf_ip; - char *uri_ip; - apr_sockaddr_ip_get(&conf_ip, conf_addr); - apr_sockaddr_ip_get(&uri_ip, uri_addr); -/* ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, - "Testing %s and %s", conf_ip, uri_ip); */ - if (!apr_strnatcasecmp(conf_ip, uri_ip)) { - return ap_proxyerror(r, HTTP_FORBIDDEN, - "Connect to remote machine blocked (by IP address)"); - } - uri_addr = uri_addr->next; - } - conf_addr = conf_addr->next; - } + if (OK != ap_proxy_checkproxyblock(r, conf, uri_addr)) { + return ap_proxyerror(r, HTTP_FORBIDDEN, + "Connect to remote machine blocked"); } diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c index cd4806125c7..27acb4da118 100644 --- a/modules/proxy/proxy_util.c +++ b/modules/proxy/proxy_util.c @@ -1071,6 +1071,44 @@ static int proxy_match_word(struct dirconn_entry *This, request_rec *r) return host != NULL && ap_strstr_c(host, This->name) != NULL; } +/* checks whether a host in uri_addr matches proxyblock */ +int ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf, + apr_sockaddr_t *uri_addr) +{ + int j; + /* XXX FIXME: conf->noproxies->elts is part of an opaque structure */ + for (j = 0; j < conf->noproxies->nelts; j++) { + struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts; + struct apr_sockaddr_t *conf_addr = npent[j].addr; + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: checking remote machine [%s] against [%s]", uri_addr->hostname, npent[j].name); + if ((npent[j].name && ap_strstr_c(uri_addr->hostname, npent[j].name)) + || npent[j].name[0] == '*') { + ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r->server, + "proxy: connect to remote machine %s blocked: name %s matched", uri_addr->hostname, npent[j].name); + return HTTP_FORBIDDEN; + } + while (conf_addr) { + while (uri_addr) { + char *conf_ip; + char *uri_ip; + apr_sockaddr_ip_get(&conf_ip, conf_addr); + apr_sockaddr_ip_get(&uri_ip, uri_addr); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: ProxyBlock comparing %s and %s", conf_ip, uri_ip); + if (!apr_strnatcasecmp(conf_ip, uri_ip)) { + ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r->server, + "proxy: connect to remote machine %s blocked: IP %s matched", uri_addr->hostname, conf_ip); + return HTTP_FORBIDDEN; + } + uri_addr = uri_addr->next; + } + conf_addr = conf_addr->next; + } + } + return OK; +} + apr_status_t ap_proxy_doconnect(apr_socket_t *sock, char *host, apr_uint32_t port, request_rec *r) { apr_status_t rv;