]> granicus.if.org Git - apache/blob - modules/proxy/proxy_connect.c
Update our copyright for this year.
[apache] / modules / proxy / proxy_connect.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000-2002 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation.  For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  *
54  * Portions of this software are based upon public domain software
55  * originally written at the National Center for Supercomputing Applications,
56  * University of Illinois, Urbana-Champaign.
57  */
58
59 /* CONNECT method for Apache proxy */
60
61 #define CORE_PRIVATE
62
63 #include "mod_proxy.h"
64
65 module AP_MODULE_DECLARE_DATA proxy_connect_module;
66
67 int ap_proxy_connect_canon(request_rec *r, char *url);
68 int ap_proxy_connect_handler(request_rec *r, proxy_server_conf *conf, 
69                              char *url, const char *proxyname, 
70                              apr_port_t proxyport);
71
72 /*  
73  * This handles Netscape CONNECT method secure proxy requests.
74  * A connection is opened to the specified host and data is
75  * passed through between the WWW site and the browser.
76  *
77  * This code is based on the INTERNET-DRAFT document
78  * "Tunneling SSL Through a WWW Proxy" currently at
79  * http://www.mcom.com/newsref/std/tunneling_ssl.html.
80  *
81  * If proxyhost and proxyport are set, we send a CONNECT to 
82  * the specified proxy..  
83  *
84  * FIXME: this doesn't log the number of bytes sent, but
85  *        that may be okay, since the data is supposed to
86  *        be transparent. In fact, this doesn't log at all
87  *        yet. 8^)
88  * FIXME: doesn't check any headers initally sent from the
89  *        client.
90  * FIXME: should allow authentication, but hopefully the
91  *        generic proxy authentication is good enough.
92  * FIXME: no check for r->assbackwards, whatever that is.
93  */
94
95 static int
96 allowed_port(proxy_server_conf *conf, int port)
97 {
98     int i;
99     int *list = (int *) conf->allowed_connect_ports->elts;
100
101     for(i = 0; i < conf->allowed_connect_ports->nelts; i++) {
102         if(port == list[i])
103             return 1;
104     }
105     return 0;
106 }
107
108 /* canonicalise CONNECT URLs. */
109 int ap_proxy_connect_canon(request_rec *r, char *url)
110 {
111
112     if (r->method_number != M_CONNECT) {
113         return DECLINED;
114     }
115     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server,
116                  "proxy: CONNECT: canonicalising URL %s", url);
117
118     return OK;
119 }
120
121 /* CONNECT handler */
122 int ap_proxy_connect_handler(request_rec *r, proxy_server_conf *conf, 
123                              char *url, const char *proxyname, 
124                              apr_port_t proxyport)
125 {
126     apr_pool_t *p = r->pool;
127     apr_socket_t *sock;
128     apr_status_t err, rv;
129     apr_size_t i, o, nbytes;
130     char buffer[HUGE_STRING_LEN];
131     apr_socket_t *client_socket = ap_get_module_config(r->connection->conn_config, &core_module);
132     int failed;
133     apr_pollfd_t *pollfd;
134     apr_int32_t pollcnt;
135     apr_int16_t pollevent;
136     apr_sockaddr_t *uri_addr, *connect_addr;
137
138     apr_uri_t uri;
139     const char *connectname;
140     int connectport = 0;
141
142     /* is this for us? */
143     if (r->method_number != M_CONNECT) {
144         ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server,
145                      "proxy: CONNECT: declining URL %s", url);
146         return DECLINED;
147     }
148     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server,
149                  "proxy: CONNECT: serving URL %s", url);
150
151
152     /*
153      * Step One: Determine Who To Connect To
154      *
155      * Break up the URL to determine the host to connect to
156      */
157
158     /* we break the URL into host, port, uri */
159     if (APR_SUCCESS != apr_uri_parse_hostinfo(p, url, &uri)) {
160         return ap_proxyerror(r, HTTP_BAD_REQUEST,
161                              apr_pstrcat(p, "URI cannot be parsed: ", url, NULL));
162     }
163
164     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server,
165                  "proxy: CONNECT: connecting %s to %s:%d", url, uri.hostname, uri.port);
166
167     /* do a DNS lookup for the destination host */
168     err = apr_sockaddr_info_get(&uri_addr, uri.hostname, APR_UNSPEC, uri.port, 0, p);
169
170     /* are we connecting directly, or via a proxy? */
171     if (proxyname) {
172         connectname = proxyname;
173         connectport = proxyport;
174         err = apr_sockaddr_info_get(&connect_addr, proxyname, APR_UNSPEC, proxyport, 0, p);
175     }
176     else {
177         connectname = uri.hostname;
178         connectport = uri.port;
179         connect_addr = uri_addr;
180     }
181     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server,
182                  "proxy: CONNECT: connecting to remote proxy %s on port %d", connectname, connectport);
183
184     /* check if ProxyBlock directive on this host */
185     if (OK != ap_proxy_checkproxyblock(r, conf, uri_addr)) {
186         return ap_proxyerror(r, HTTP_FORBIDDEN,
187                              "Connect to remote machine blocked");
188     }
189
190     /* Check if it is an allowed port */
191     if (conf->allowed_connect_ports->nelts == 0) {
192         /* Default setting if not overridden by AllowCONNECT */
193         switch (uri.port) {
194             case APR_URI_HTTPS_DEFAULT_PORT:
195             case APR_URI_SNEWS_DEFAULT_PORT:
196                 break;
197             default:
198                 /* XXX can we call ap_proxyerror() here to get a nice log message? */
199                 return HTTP_FORBIDDEN;
200         }
201     } else if(!allowed_port(conf, uri.port)) {
202         /* XXX can we call ap_proxyerror() here to get a nice log message? */
203         return HTTP_FORBIDDEN;
204     }
205
206     /*
207      * Step Two: Make the Connection
208      *
209      * We have determined who to connect to. Now make the connection.
210      */
211
212     /* get all the possible IP addresses for the destname and loop through them
213      * until we get a successful connection
214      */
215     if (APR_SUCCESS != err) {
216         return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_pstrcat(p,
217                              "DNS lookup failure for: ",
218                              connectname, NULL));
219     }
220
221     /*
222      * At this point we have a list of one or more IP addresses of
223      * the machine to connect to. If configured, reorder this
224      * list so that the "best candidate" is first try. "best
225      * candidate" could mean the least loaded server, the fastest
226      * responding server, whatever.
227      *
228      * For now we do nothing, ie we get DNS round robin.
229      * XXX FIXME
230      */
231     failed = ap_proxy_connect_to_backend(&sock, "CONNECT", connect_addr,
232                                          connectname, conf, r->server,
233                                          r->pool);
234
235     /* handle a permanent error from the above loop */
236     if (failed) {
237         if (proxyname) {
238             return DECLINED;
239         }
240         else {
241             return HTTP_BAD_GATEWAY;
242         }
243     }
244
245     /*
246      * Step Three: Send the Request
247      *
248      * Send the HTTP/1.1 CONNECT request to the remote server
249      */
250
251     /* we are acting as a tunnel - the output filter stack should
252      * be completely empty, because when we are done here we are done completely.
253      * We add the NULL filter to the stack to do this...
254      */
255     r->output_filters = NULL;
256     r->connection->output_filters = NULL;
257
258
259     /* If we are connecting through a remote proxy, we need to pass
260      * the CONNECT request on to it.
261      */
262     if (proxyport) {
263         /* FIXME: Error checking ignored.
264          */
265         ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server,
266                      "proxy: CONNECT: sending the CONNECT request to the remote proxy");
267         nbytes = apr_snprintf(buffer, sizeof(buffer),
268                               "CONNECT %s HTTP/1.0" CRLF, r->uri);
269         apr_send(sock, buffer, &nbytes);
270         nbytes = apr_snprintf(buffer, sizeof(buffer),
271                               "Proxy-agent: %s" CRLF CRLF, ap_get_server_version());
272         apr_send(sock, buffer, &nbytes);
273     }
274     else {
275         ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server,
276                      "proxy: CONNECT: Returning 200 OK Status");
277         nbytes = apr_snprintf(buffer, sizeof(buffer),
278                               "HTTP/1.0 200 Connection Established" CRLF);
279         apr_send(client_socket, buffer, &nbytes);
280         nbytes = apr_snprintf(buffer, sizeof(buffer),
281                               "Proxy-agent: %s" CRLF CRLF, ap_get_server_version());
282 #if 0
283         /* This is safer code, but it doesn't work yet.  I'm leaving it 
284          * here so that I can fix it later.
285          */
286         apr_send(r->connection->client_socket, buffer, &nbytes);
287         r->status = HTTP_OK;
288         r->header_only = 1;
289         apr_table_set(r->headers_out, "Proxy-agent: %s", ap_get_server_version());
290         ap_rflush(r);
291 #endif
292     }
293
294     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server,
295                  "proxy: CONNECT: setting up poll()");
296
297     /*
298      * Step Four: Handle Data Transfer
299      *
300      * Handle two way transfer of data over the socket (this is a tunnel).
301      */
302
303 /*    r->sent_bodyct = 1;*/
304
305     if((rv = apr_poll_setup(&pollfd, 2, r->pool)) != APR_SUCCESS)
306     {
307         apr_socket_close(sock);
308         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
309             "proxy: CONNECT: error apr_poll_setup()");
310         return HTTP_INTERNAL_SERVER_ERROR;
311     }
312
313     /* Add client side to the poll */
314     apr_poll_socket_add(pollfd, client_socket, APR_POLLIN);
315
316     /* Add the server side to the poll */
317     apr_poll_socket_add(pollfd, sock, APR_POLLIN);
318
319     while (1) { /* Infinite loop until error (one side closes the connection) */
320 /*      ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, "proxy: CONNECT: going to sleep (poll)");*/
321         if ((rv = apr_poll(pollfd, &pollcnt, -1)) != APR_SUCCESS)
322         {
323             apr_socket_close(sock);
324             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "proxy: CONNECT: error apr_poll()");
325             return HTTP_INTERNAL_SERVER_ERROR;
326         }
327 /*      ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server,
328                      "proxy: CONNECT: woke from select(), i=%d", pollcnt);*/
329
330         if (pollcnt) {
331             apr_poll_revents_get(&pollevent, sock, pollfd);
332             if (pollevent & APR_POLLIN) {
333 /*              ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server,
334                              "proxy: CONNECT: sock was set");*/
335                 nbytes = sizeof(buffer);
336                 if (apr_recv(sock, buffer, &nbytes) == APR_SUCCESS) {
337                     o = 0;
338                     i = nbytes;
339                     while(i > 0)
340                     {
341                         nbytes = i;
342     /* This is just plain wrong.  No module should ever write directly
343      * to the client.  For now, this works, but this is high on my list of
344      * things to fix.  The correct line is:
345      * if ((nbytes = ap_rwrite(buffer + o, nbytes, r)) < 0)
346      * rbb
347      */
348                         if (apr_send(client_socket, buffer + o, &nbytes) != APR_SUCCESS)
349                             break;
350                         o += nbytes;
351                         i -= nbytes;
352                     }
353                 }
354                 else
355                     break;
356             }
357             else if ((pollevent & APR_POLLERR) || (pollevent & APR_POLLHUP))
358                 break;
359
360
361             apr_poll_revents_get(&pollevent, client_socket, pollfd);
362             if (pollevent & APR_POLLIN) {
363 /*              ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server,
364                              "proxy: CONNECT: client was set");*/
365                 nbytes = sizeof(buffer);
366                 if (apr_recv(client_socket, buffer, &nbytes) == APR_SUCCESS) {
367                     o = 0;
368                     i = nbytes;
369                     while(i > 0)
370                     {
371                         nbytes = i;
372                         if (apr_send(sock, buffer + o, &nbytes) != APR_SUCCESS)
373                             break;
374                         o += nbytes;
375                         i -= nbytes;
376                     }
377                 }
378                 else
379                     break;
380             }
381             else if ((pollevent & APR_POLLERR) || (pollevent & APR_POLLHUP))
382                 break;
383         }
384         else
385             break;
386     }
387
388     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server,
389                  "proxy: CONNECT: finished with poll() - cleaning up");
390
391     /*
392      * Step Five: Clean Up
393      *
394      * Close the socket and clean up
395      */
396
397     apr_socket_close(sock);
398
399     return OK;
400 }
401
402 static void ap_proxy_connect_register_hook(apr_pool_t *p)
403 {
404     proxy_hook_scheme_handler(ap_proxy_connect_handler, NULL, NULL, APR_HOOK_MIDDLE);
405     proxy_hook_canon_handler(ap_proxy_connect_canon, NULL, NULL, APR_HOOK_MIDDLE);
406 }
407
408 module AP_MODULE_DECLARE_DATA proxy_connect_module = {
409     STANDARD20_MODULE_STUFF,
410     NULL,               /* create per-directory config structure */
411     NULL,               /* merge per-directory config structures */
412     NULL,               /* create per-server config structure */
413     NULL,               /* merge per-server config structures */
414     NULL,               /* command apr_table_t */
415     ap_proxy_connect_register_hook      /* register hooks */
416 };