/* ==================================================================== * Copyright (c) 1996-1999 The Apache Group. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * 4. The names "Apache Server" and "Apache Group" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * apache@apache.org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Group and was originally based * on public domain software written at the National Center for * Supercomputing Applications, University of Illinois, Urbana-Champaign. * For more information on the Apache Group and the Apache HTTP server * project, please see . * */ /* CONNECT method for Apache proxy */ #include "mod_proxy.h" #include "http_log.h" #include "http_main.h" #ifdef HAVE_BSTRING_H #include /* for IRIX, FD_SET calls bzero() */ #endif DEF_Explain /* * This handles Netscape CONNECT method secure proxy requests. * A connection is opened to the specified host and data is * passed through between the WWW site and the browser. * * This code is based on the INTERNET-DRAFT document * "Tunneling SSL Through a WWW Proxy" currently at * http://www.mcom.com/newsref/std/tunneling_ssl.html. * * If proxyhost and proxyport are set, we send a CONNECT to * the specified proxy.. * * FIXME: this is bad, because it does its own socket I/O * instead of using the I/O in buff.c. However, * the I/O in buff.c blocks on reads, and because * this function doesn't know how much data will * be sent either way (or when) it can't use blocking * I/O. This may be very implementation-specific * (to Linux). Any suggestions? * FIXME: this doesn't log the number of bytes sent, but * that may be okay, since the data is supposed to * be transparent. In fact, this doesn't log at all * yet. 8^) * FIXME: doesn't check any headers initally sent from the * client. * FIXME: should allow authentication, but hopefully the * generic proxy authentication is good enough. * FIXME: no check for r->assbackwards, whatever that is. */ static int allowed_port(proxy_server_conf *conf, int port) { int i; int *list = (int *) conf->allowed_connect_ports->elts; for(i = 0; i < conf->allowed_connect_ports->nelts; i++) { if(port == list[i]) return 1; } return 0; } int ap_proxy_connect_handler(request_rec *r, cache_req *c, char *url, const char *proxyhost, int proxyport) { struct sockaddr_in server; struct in_addr destaddr; struct hostent server_hp; const char *host, *err; char *p; int port, sock; char buffer[HUGE_STRING_LEN]; int nbytes, i, j; fd_set fds; 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; memset(&server, '\0', sizeof(server)); server.sin_family = AF_INET; /* Break the URL into host:port pairs */ host = url; p = strchr(url, ':'); if (p == NULL) port = DEFAULT_HTTPS_PORT; else { port = atoi(p + 1); *p = '\0'; } /* check if ProxyBlock directive on this host */ destaddr.s_addr = ap_inet_addr(host); for (i = 0; i < conf->noproxies->nelts; i++) { if ((npent[i].name != NULL && strstr(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 it is an allowed port */ if (conf->allowed_connect_ports->nelts == 0) { /* Default setting if not overridden by AllowCONNECT */ switch (port) { case DEFAULT_HTTPS_PORT: case DEFAULT_SNEWS_PORT: break; default: return HTTP_FORBIDDEN; } } else if(!allowed_port(conf, port)) return HTTP_FORBIDDEN; if (proxyhost) { Explain2("CONNECT to remote proxy %s on port %d", proxyhost, proxyport); } else { Explain2("CONNECT to %s on port %d", host, port); } server.sin_port = (proxyport ? htons(proxyport) : htons(port)); err = ap_proxy_host2addr(proxyhost ? proxyhost : host, &server_hp); if (err != NULL) return ap_proxyerror(r, proxyhost ? HTTP_BAD_GATEWAY : HTTP_INTERNAL_SERVER_ERROR, err); sock = ap_psocket(r->pool, PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock == -1) { ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "proxy: error creating socket"); return HTTP_INTERNAL_SERVER_ERROR; } #ifdef CHECK_FD_SETSIZE if (sock >= FD_SETSIZE) { ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, "proxy_connect_handler: filedescriptor (%u) " "larger than FD_SETSIZE (%u) " "found, you probably need to rebuild Apache with a " "larger FD_SETSIZE", sock, FD_SETSIZE); ap_pclosesocket(r->pool, sock); return HTTP_INTERNAL_SERVER_ERROR; } #endif j = 0; while (server_hp.h_addr_list[j] != NULL) { memcpy(&server.sin_addr, server_hp.h_addr_list[j], sizeof(struct in_addr)); i = ap_proxy_doconnect(sock, &server, r); if (i == 0) break; j++; } if (i == -1) { ap_pclosesocket(r->pool, sock); return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, ap_pstrcat(r->pool, "Could not connect to remote machine:
", strerror(errno), NULL)); } /* 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 * a HTTP/1.0 request to keep things simple. */ Explain0("Sending the CONNECT request to the remote proxy"); ap_snprintf(buffer, sizeof(buffer), "CONNECT %s HTTP/1.0" CRLF, r->uri); write(sock, buffer, strlen(buffer)); ap_snprintf(buffer, sizeof(buffer), "Proxy-agent: %s" CRLF CRLF, ap_get_server_version()); write(sock, buffer, strlen(buffer)); } else { Explain0("Returning 200 OK Status"); ap_rvputs(r, "HTTP/1.0 200 Connection established" CRLF, NULL); ap_rvputs(r, "Proxy-agent: ", ap_get_server_version(), CRLF CRLF, NULL); ap_bflush(r->connection->client); } while (1) { /* Infinite loop until error (one side closes the connection) */ FD_ZERO(&fds); FD_SET(sock, &fds); FD_SET(r->connection->client->fd, &fds); Explain0("Going to sleep (select)"); i = ap_select((r->connection->client->fd > sock ? r->connection->client->fd + 1 : sock + 1), &fds, NULL, NULL, NULL); Explain1("Woke from select(), i=%d", i); if (i) { if (FD_ISSET(sock, &fds)) { Explain0("sock was set"); if ((nbytes = read(sock, buffer, HUGE_STRING_LEN)) != 0) { if (nbytes == -1) break; if (write(r->connection->client->fd, buffer, nbytes) == EOF) break; Explain1("Wrote %d bytes to client", nbytes); } else break; } else if (FD_ISSET(r->connection->client->fd, &fds)) { Explain0("client->fd was set"); if ((nbytes = read(r->connection->client->fd, buffer, HUGE_STRING_LEN)) != 0) { if (nbytes == -1) break; if (write(sock, buffer, nbytes) == EOF) break; Explain1("Wrote %d bytes to server", nbytes); } else break; } else break; /* Must be done waiting */ } else break; } ap_pclosesocket(r->pool, sock); return OK; }