1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /* Utility routines for Apache proxy */
18 #include "mod_proxy.h"
19 #include "ap_mpm.h"
20 #include "scoreboard.h"
21 #include "apr_version.h"
22 #include "apr_strings.h"
23 #include "apr_hash.h"
24 #include "proxy_util.h"
25 #include "ajp.h"
26 #include "scgi.h"
27 
28 #include "mod_http2.h" /* for http2_get_num_workers() */
29 
30 #if APR_HAVE_UNISTD_H
31 #include <unistd.h>         /* for getpid() */
32 #endif
33 
34 #if APR_HAVE_SYS_UN_H
35 #include <sys/un.h>
36 #endif
37 #if (APR_MAJOR_VERSION < 2)
38 #include "apr_support.h"        /* for apr_wait_for_io_or_timeout() */
39 #endif
40 
41 APLOG_USE_MODULE(proxy);
42 
43 /*
44  * Opaque structure containing target server info when
45  * using a forward proxy.
46  * Up to now only used in combination with HTTP CONNECT.
47  */
48 typedef struct {
49     int          use_http_connect; /* Use SSL Tunneling via HTTP CONNECT */
50     const char   *target_host;     /* Target hostname */
51     apr_port_t   target_port;      /* Target port */
52     const char   *proxy_auth;      /* Proxy authorization */
53 } forward_info;
54 
55 /* Global balancer counter */
56 int PROXY_DECLARE_DATA proxy_lb_workers = 0;
57 static int lb_workers_limit = 0;
58 const apr_strmatch_pattern PROXY_DECLARE_DATA *ap_proxy_strmatch_path;
59 const apr_strmatch_pattern PROXY_DECLARE_DATA *ap_proxy_strmatch_domain;
60 
61 extern apr_global_mutex_t *proxy_mutex;
62 
63 static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r);
64 static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r);
65 static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r);
66 static int proxy_match_word(struct dirconn_entry *This, request_rec *r);
67 static int ap_proxy_retry_worker(const char *proxy_function, proxy_worker *worker, server_rec *s);
68 static proxy_worker *proxy_balancer_get_best_worker(proxy_balancer *balancer,
69                                                     request_rec *r,
70                                                     proxy_is_best_callback_fn_t *is_best,
71                                                     void *baton);
72 
73 APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, create_req,
74                                    (request_rec *r, request_rec *pr), (r, pr),
75                                    OK, DECLINED)
76 
PROXY_DECLARE(apr_status_t)77 PROXY_DECLARE(apr_status_t) ap_proxy_strncpy(char *dst, const char *src,
78                                              apr_size_t dlen)
79 {
80     char *thenil;
81     apr_size_t thelen;
82 
83     /* special case handling */
84     if (!dlen) {
85         /* XXX: APR_ENOSPACE would be better */
86         return APR_EGENERAL;
87     }
88     if (!src) {
89         *dst = '\0';
90         return APR_SUCCESS;
91     }
92     thenil = apr_cpystrn(dst, src, dlen);
93     thelen = thenil - dst;
94     if (src[thelen] == '\0') {
95         return APR_SUCCESS;
96     }
97     return APR_EGENERAL;
98 }
99 
100 /* already called in the knowledge that the characters are hex digits */
ap_proxy_hex2c(const char * x)101 PROXY_DECLARE(int) ap_proxy_hex2c(const char *x)
102 {
103     int i;
104 
105 #if !APR_CHARSET_EBCDIC
106     int ch = x[0];
107 
108     if (apr_isdigit(ch)) {
109         i = ch - '0';
110     }
111     else if (apr_isupper(ch)) {
112         i = ch - ('A' - 10);
113     }
114     else {
115         i = ch - ('a' - 10);
116     }
117     i <<= 4;
118 
119     ch = x[1];
120     if (apr_isdigit(ch)) {
121         i += ch - '0';
122     }
123     else if (apr_isupper(ch)) {
124         i += ch - ('A' - 10);
125     }
126     else {
127         i += ch - ('a' - 10);
128     }
129     return i;
130 #else /*APR_CHARSET_EBCDIC*/
131     /*
132      * we assume that the hex value refers to an ASCII character
133      * so convert to EBCDIC so that it makes sense locally;
134      *
135      * example:
136      *
137      * client specifies %20 in URL to refer to a space char;
138      * at this point we're called with EBCDIC "20"; after turning
139      * EBCDIC "20" into binary 0x20, we then need to assume that 0x20
140      * represents an ASCII char and convert 0x20 to EBCDIC, yielding
141      * 0x40
142      */
143     char buf[1];
144 
145     if (1 == sscanf(x, "%2x", &i)) {
146         buf[0] = i & 0xFF;
147         ap_xlate_proto_from_ascii(buf, 1);
148         return buf[0];
149     }
150     else {
151         return 0;
152     }
153 #endif /*APR_CHARSET_EBCDIC*/
154 }
155 
ap_proxy_c2hex(int ch,char * x)156 PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x)
157 {
158 #if !APR_CHARSET_EBCDIC
159     int i;
160 
161     x[0] = '%';
162     i = (ch & 0xF0) >> 4;
163     if (i >= 10) {
164         x[1] = ('A' - 10) + i;
165     }
166     else {
167         x[1] = '0' + i;
168     }
169 
170     i = ch & 0x0F;
171     if (i >= 10) {
172         x[2] = ('A' - 10) + i;
173     }
174     else {
175         x[2] = '0' + i;
176     }
177 #else /*APR_CHARSET_EBCDIC*/
178     static const char ntoa[] = { "0123456789ABCDEF" };
179     char buf[1];
180 
181     ch &= 0xFF;
182 
183     buf[0] = ch;
184     ap_xlate_proto_to_ascii(buf, 1);
185 
186     x[0] = '%';
187     x[1] = ntoa[(buf[0] >> 4) & 0x0F];
188     x[2] = ntoa[buf[0] & 0x0F];
189     x[3] = '\0';
190 #endif /*APR_CHARSET_EBCDIC*/
191 }
192 
193 /*
194  * canonicalise a URL-encoded string
195  */
196 
197 /*
198  * Convert a URL-encoded string to canonical form.
199  * It decodes characters which need not be encoded,
200  * and encodes those which must be encoded, and does not touch
201  * those which must not be touched.
202  */
ap_proxy_canonenc(apr_pool_t * p,const char * x,int len,enum enctype t,int forcedec,int proxyreq)203 PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len,
204                                        enum enctype t, int forcedec,
205                                        int proxyreq)
206 {
207     int i, j, ch;
208     char *y;
209     char *allowed;  /* characters which should not be encoded */
210     char *reserved; /* characters which much not be en/de-coded */
211 
212 /*
213  * N.B. in addition to :@&=, this allows ';' in an http path
214  * and '?' in an ftp path -- this may be revised
215  *
216  * Also, it makes a '+' character in a search string reserved, as
217  * it may be form-encoded. (Although RFC 1738 doesn't allow this -
218  * it only permits ; / ? : @ = & as reserved chars.)
219  */
220     if (t == enc_path) {
221         allowed = "~$-_.+!*'(),;:@&=";
222     }
223     else if (t == enc_search) {
224         allowed = "$-_.!*'(),;:@&=";
225     }
226     else if (t == enc_user) {
227         allowed = "$-_.+!*'(),;@&=";
228     }
229     else if (t == enc_fpath) {
230         allowed = "$-_.+!*'(),?:@&=";
231     }
232     else {            /* if (t == enc_parm) */
233         allowed = "$-_.+!*'(),?/:@&=";
234     }
235 
236     if (t == enc_path) {
237         reserved = "/";
238     }
239     else if (t == enc_search) {
240         reserved = "+";
241     }
242     else {
243         reserved = "";
244     }
245 
246     y = apr_palloc(p, 3 * len + 1);
247 
248     for (i = 0, j = 0; i < len; i++, j++) {
249 /* always handle '/' first */
250         ch = x[i];
251         if (strchr(reserved, ch)) {
252             y[j] = ch;
253             continue;
254         }
255 /*
256  * decode it if not already done. do not decode reverse proxied URLs
257  * unless specifically forced
258  */
259         if ((forcedec || (proxyreq && proxyreq != PROXYREQ_REVERSE)) && ch == '%') {
260             if (!apr_isxdigit(x[i + 1]) || !apr_isxdigit(x[i + 2])) {
261                 return NULL;
262             }
263             ch = ap_proxy_hex2c(&x[i + 1]);
264             i += 2;
265             if (ch != 0 && strchr(reserved, ch)) {  /* keep it encoded */
266                 ap_proxy_c2hex(ch, &y[j]);
267                 j += 2;
268                 continue;
269             }
270         }
271 /* recode it, if necessary */
272         if (!apr_isalnum(ch) && !strchr(allowed, ch)) {
273             ap_proxy_c2hex(ch, &y[j]);
274             j += 2;
275         }
276         else {
277             y[j] = ch;
278         }
279     }
280     y[j] = '\0';
281     return y;
282 }
283 
284 /*
285  * Parses network-location.
286  *    urlp           on input the URL; on output the path, after the leading /
287  *    user           NULL if no user/password permitted
288  *    password       holder for password
289  *    host           holder for host
290  *    port           port number; only set if one is supplied.
291  *
292  * Returns an error string.
293  */
294 PROXY_DECLARE(char *)
ap_proxy_canon_netloc(apr_pool_t * p,char ** const urlp,char ** userp,char ** passwordp,char ** hostp,apr_port_t * port)295      ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp,
296             char **passwordp, char **hostp, apr_port_t *port)
297 {
298     char *addr, *scope_id, *strp, *host, *url = *urlp;
299     char *user = NULL, *password = NULL;
300     apr_port_t tmp_port;
301     apr_status_t rv;
302 
303     if (url[0] != '/' || url[1] != '/') {
304         return "Malformed URL";
305     }
306     host = url + 2;
307     url = strchr(host, '/');
308     if (url == NULL) {
309         url = "";
310     }
311     else {
312         *(url++) = '\0';    /* skip separating '/' */
313     }
314 
315     /* find _last_ '@' since it might occur in user/password part */
316     strp = strrchr(host, '@');
317 
318     if (strp != NULL) {
319         *strp = '\0';
320         user = host;
321         host = strp + 1;
322 
323 /* find password */
324         strp = strchr(user, ':');
325         if (strp != NULL) {
326             *strp = '\0';
327             password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, 1, 0);
328             if (password == NULL) {
329                 return "Bad %-escape in URL (password)";
330             }
331         }
332 
333         user = ap_proxy_canonenc(p, user, strlen(user), enc_user, 1, 0);
334         if (user == NULL) {
335             return "Bad %-escape in URL (username)";
336         }
337     }
338     if (userp != NULL) {
339         *userp = user;
340     }
341     if (passwordp != NULL) {
342         *passwordp = password;
343     }
344 
345     /*
346      * Parse the host string to separate host portion from optional port.
347      * Perform range checking on port.
348      */
349     rv = apr_parse_addr_port(&addr, &scope_id, &tmp_port, host, p);
350     if (rv != APR_SUCCESS || addr == NULL || scope_id != NULL) {
351         return "Invalid host/port";
352     }
353     if (tmp_port != 0) { /* only update caller's port if port was specified */
354         *port = tmp_port;
355     }
356 
357     ap_str_tolower(addr); /* DNS names are case-insensitive */
358 
359     *urlp = url;
360     *hostp = addr;
361 
362     return NULL;
363 }
364 
ap_proxyerror(request_rec * r,int statuscode,const char * message)365 PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message)
366 {
367     apr_table_setn(r->notes, "error-notes",
368         apr_pstrcat(r->pool,
369             "The proxy server could not handle the request<p>"
370             "Reason: <strong>", ap_escape_html(r->pool, message),
371             "</strong></p>",
372             NULL));
373 
374     /* Allow "error-notes" string to be printed by ap_send_error_response() */
375     apr_table_setn(r->notes, "verbose-error-to", "*");
376 
377     r->status_line = apr_psprintf(r->pool, "%3.3u Proxy Error", statuscode);
378     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00898) "%s returned by %s", message,
379                   r->uri);
380     return statuscode;
381 }
382 
383 static const char *
proxy_get_host_of_request(request_rec * r)384      proxy_get_host_of_request(request_rec *r)
385 {
386     char *url, *user = NULL, *password = NULL, *err, *host = NULL;
387     apr_port_t port;
388 
389     if (r->hostname != NULL) {
390         return r->hostname;
391     }
392 
393     /* Set url to the first char after "scheme://" */
394     if ((url = strchr(r->uri, ':')) == NULL || url[1] != '/' || url[2] != '/') {
395         return NULL;
396     }
397 
398     url = apr_pstrdup(r->pool, &url[1]);    /* make it point to "//", which is what proxy_canon_netloc expects */
399 
400     err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port);
401 
402     if (err != NULL) {
403         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00899) "%s", err);
404     }
405 
406     r->hostname = host;
407 
408     return host;        /* ought to return the port, too */
409 }
410 
411 /* Return TRUE if addr represents an IP address (or an IP network address) */
ap_proxy_is_ipaddr(struct dirconn_entry * This,apr_pool_t * p)412 PROXY_DECLARE(int) ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p)
413 {
414     const char *addr = This->name;
415     long ip_addr[4];
416     int i, quads;
417     long bits;
418 
419     /*
420      * if the address is given with an explicit netmask, use that
421      * Due to a deficiency in apr_inet_addr(), it is impossible to parse
422      * "partial" addresses (with less than 4 quads) correctly, i.e.
423      * 192.168.123 is parsed as 192.168.0.123, which is not what I want.
424      * I therefore have to parse the IP address manually:
425      * if (proxy_readmask(This->name, &This->addr.s_addr, &This->mask.s_addr) == 0)
426      * addr and mask were set by proxy_readmask()
427      * return 1;
428      */
429 
430     /*
431      * Parse IP addr manually, optionally allowing
432      * abbreviated net addresses like 192.168.
433      */
434 
435     /* Iterate over up to 4 (dotted) quads. */
436     for (quads = 0; quads < 4 && *addr != '\0'; ++quads) {
437         char *tmp;
438 
439         if (*addr == '/' && quads > 0) {  /* netmask starts here. */
440             break;
441         }
442 
443         if (!apr_isdigit(*addr)) {
444             return 0;       /* no digit at start of quad */
445         }
446 
447         ip_addr[quads] = strtol(addr, &tmp, 0);
448 
449         if (tmp == addr) {  /* expected a digit, found something else */
450             return 0;
451         }
452 
453         if (ip_addr[quads] < 0 || ip_addr[quads] > 255) {
454             /* invalid octet */
455             return 0;
456         }
457 
458         addr = tmp;
459 
460         if (*addr == '.' && quads != 3) {
461             ++addr;     /* after the 4th quad, a dot would be illegal */
462         }
463     }
464 
465     for (This->addr.s_addr = 0, i = 0; i < quads; ++i) {
466         This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
467     }
468 
469     if (addr[0] == '/' && apr_isdigit(addr[1])) {   /* net mask follows: */
470         char *tmp;
471 
472         ++addr;
473 
474         bits = strtol(addr, &tmp, 0);
475 
476         if (tmp == addr) {   /* expected a digit, found something else */
477             return 0;
478         }
479 
480         addr = tmp;
481 
482         if (bits < 0 || bits > 32) { /* netmask must be between 0 and 32 */
483             return 0;
484         }
485 
486     }
487     else {
488         /*
489          * Determine (i.e., "guess") netmask by counting the
490          * number of trailing .0's; reduce #quads appropriately
491          * (so that 192.168.0.0 is equivalent to 192.168.)
492          */
493         while (quads > 0 && ip_addr[quads - 1] == 0) {
494             --quads;
495         }
496 
497         /* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */
498         if (quads < 1) {
499             return 0;
500         }
501 
502         /* every zero-byte counts as 8 zero-bits */
503         bits = 8 * quads;
504 
505         if (bits != 32) {     /* no warning for fully qualified IP address */
506             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00900)
507                          "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld",
508                          inet_ntoa(This->addr), bits);
509         }
510     }
511 
512     This->mask.s_addr = htonl(APR_INADDR_NONE << (32 - bits));
513 
514     if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) {
515         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00901)
516                      "Warning: NetMask and IP-Addr disagree in %s/%ld",
517                      inet_ntoa(This->addr), bits);
518         This->addr.s_addr &= This->mask.s_addr;
519         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00902)
520                      "         Set to %s/%ld", inet_ntoa(This->addr), bits);
521     }
522 
523     if (*addr == '\0') {
524         This->matcher = proxy_match_ipaddr;
525         return 1;
526     }
527     else {
528         return (*addr == '\0'); /* okay iff we've parsed the whole string */
529     }
530 }
531 
532 /* Return TRUE if addr represents an IP address (or an IP network address) */
proxy_match_ipaddr(struct dirconn_entry * This,request_rec * r)533 static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r)
534 {
535     int i, ip_addr[4];
536     struct in_addr addr, *ip;
537     const char *host = proxy_get_host_of_request(r);
538 
539     if (host == NULL) {   /* oops! */
540        return 0;
541     }
542 
543     memset(&addr, '\0', sizeof addr);
544     memset(ip_addr, '\0', sizeof ip_addr);
545 
546     if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) {
547         for (addr.s_addr = 0, i = 0; i < 4; ++i) {
548             /* ap_proxy_is_ipaddr() already confirmed that we have
549              * a valid octet in ip_addr[i]
550              */
551             addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
552         }
553 
554         if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) {
555 #if DEBUGGING
556             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00903)
557                          "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr));
558             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00904)
559                          "%s/", inet_ntoa(This->addr));
560             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00905)
561                          "%s", inet_ntoa(This->mask));
562 #endif
563             return 1;
564         }
565 #if DEBUGGING
566         else {
567             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00906)
568                          "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr));
569             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00907)
570                          "%s/", inet_ntoa(This->addr));
571             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00908)
572                          "%s", inet_ntoa(This->mask));
573         }
574 #endif
575     }
576     else {
577         struct apr_sockaddr_t *reqaddr;
578 
579         if (apr_sockaddr_info_get(&reqaddr, host, APR_UNSPEC, 0, 0, r->pool)
580             != APR_SUCCESS) {
581 #if DEBUGGING
582             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00909)
583              "2)IP-NoMatch: hostname=%s msg=Host not found", host);
584 #endif
585             return 0;
586         }
587 
588         /* Try to deal with multiple IP addr's for a host */
589         /* FIXME: This needs to be able to deal with IPv6 */
590         while (reqaddr) {
591             ip = (struct in_addr *) reqaddr->ipaddr_ptr;
592             if (This->addr.s_addr == (ip->s_addr & This->mask.s_addr)) {
593 #if DEBUGGING
594                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00910)
595                              "3)IP-Match: %s[%s] <-> ", host, inet_ntoa(*ip));
596                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00911)
597                              "%s/", inet_ntoa(This->addr));
598                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00912)
599                              "%s", inet_ntoa(This->mask));
600 #endif
601                 return 1;
602             }
603 #if DEBUGGING
604             else {
605                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00913)
606                              "3)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(*ip));
607                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00914)
608                              "%s/", inet_ntoa(This->addr));
609                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00915)
610                              "%s", inet_ntoa(This->mask));
611             }
612 #endif
613             reqaddr = reqaddr->next;
614         }
615     }
616 
617     return 0;
618 }
619 
620 /* Return TRUE if addr represents a domain name */
ap_proxy_is_domainname(struct dirconn_entry * This,apr_pool_t * p)621 PROXY_DECLARE(int) ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p)
622 {
623     char *addr = This->name;
624     int i;
625 
626     /* Domain name must start with a '.' */
627     if (addr[0] != '.') {
628         return 0;
629     }
630 
631     /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
632     for (i = 0; apr_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i) {
633         continue;
634     }
635 
636 #if 0
637     if (addr[i] == ':') {
638     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(03234)
639                      "@@@@ handle optional port in proxy_is_domainname()");
640     /* @@@@ handle optional port */
641     }
642 #endif
643 
644     if (addr[i] != '\0') {
645         return 0;
646     }
647 
648     /* Strip trailing dots */
649     for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i) {
650         addr[i] = '\0';
651     }
652 
653     This->matcher = proxy_match_domainname;
654     return 1;
655 }
656 
657 /* Return TRUE if host "host" is in domain "domain" */
proxy_match_domainname(struct dirconn_entry * This,request_rec * r)658 static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r)
659 {
660     const char *host = proxy_get_host_of_request(r);
661     int d_len = strlen(This->name), h_len;
662 
663     if (host == NULL) {      /* some error was logged already */
664         return 0;
665     }
666 
667     h_len = strlen(host);
668 
669     /* @@@ do this within the setup? */
670     /* Ignore trailing dots in domain comparison: */
671     while (d_len > 0 && This->name[d_len - 1] == '.') {
672         --d_len;
673     }
674     while (h_len > 0 && host[h_len - 1] == '.') {
675         --h_len;
676     }
677     return h_len > d_len
678         && strncasecmp(&host[h_len - d_len], This->name, d_len) == 0;
679 }
680 
681 /* Return TRUE if host represents a host name */
ap_proxy_is_hostname(struct dirconn_entry * This,apr_pool_t * p)682 PROXY_DECLARE(int) ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p)
683 {
684     struct apr_sockaddr_t *addr;
685     char *host = This->name;
686     int i;
687 
688     /* Host names must not start with a '.' */
689     if (host[0] == '.') {
690         return 0;
691     }
692     /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
693     for (i = 0; apr_isalnum(host[i]) || host[i] == '-' || host[i] == '.'; ++i);
694 
695     if (host[i] != '\0' || apr_sockaddr_info_get(&addr, host, APR_UNSPEC, 0, 0, p) != APR_SUCCESS) {
696         return 0;
697     }
698 
699     This->hostaddr = addr;
700 
701     /* Strip trailing dots */
702     for (i = strlen(host) - 1; i > 0 && host[i] == '.'; --i) {
703         host[i] = '\0';
704     }
705 
706     This->matcher = proxy_match_hostname;
707     return 1;
708 }
709 
710 /* Return TRUE if host "host" is equal to host2 "host2" */
proxy_match_hostname(struct dirconn_entry * This,request_rec * r)711 static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r)
712 {
713     char *host = This->name;
714     const char *host2 = proxy_get_host_of_request(r);
715     int h2_len;
716     int h1_len;
717 
718     if (host == NULL || host2 == NULL) {
719         return 0; /* oops! */
720     }
721 
722     h2_len = strlen(host2);
723     h1_len = strlen(host);
724 
725 #if 0
726     struct apr_sockaddr_t *addr = *This->hostaddr;
727 
728     /* Try to deal with multiple IP addr's for a host */
729     while (addr) {
730         if (addr->ipaddr_ptr == ? ? ? ? ? ? ? ? ? ? ? ? ?)
731             return 1;
732         addr = addr->next;
733     }
734 #endif
735 
736     /* Ignore trailing dots in host2 comparison: */
737     while (h2_len > 0 && host2[h2_len - 1] == '.') {
738         --h2_len;
739     }
740     while (h1_len > 0 && host[h1_len - 1] == '.') {
741         --h1_len;
742     }
743     return h1_len == h2_len
744         && strncasecmp(host, host2, h1_len) == 0;
745 }
746 
747 /* Return TRUE if addr is to be matched as a word */
ap_proxy_is_word(struct dirconn_entry * This,apr_pool_t * p)748 PROXY_DECLARE(int) ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p)
749 {
750     This->matcher = proxy_match_word;
751     return 1;
752 }
753 
754 /* Return TRUE if string "str2" occurs literally in "str1" */
proxy_match_word(struct dirconn_entry * This,request_rec * r)755 static int proxy_match_word(struct dirconn_entry *This, request_rec *r)
756 {
757     const char *host = proxy_get_host_of_request(r);
758     return host != NULL && ap_strstr_c(host, This->name) != NULL;
759 }
760 
761 /* Backwards-compatible interface. */
ap_proxy_checkproxyblock(request_rec * r,proxy_server_conf * conf,apr_sockaddr_t * uri_addr)762 PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf,
763                              apr_sockaddr_t *uri_addr)
764 {
765     return ap_proxy_checkproxyblock2(r, conf, uri_addr->hostname, uri_addr);
766 }
767 
768 #define MAX_IP_STR_LEN (46)
769 
ap_proxy_checkproxyblock2(request_rec * r,proxy_server_conf * conf,const char * hostname,apr_sockaddr_t * addr)770 PROXY_DECLARE(int) ap_proxy_checkproxyblock2(request_rec *r, proxy_server_conf *conf,
771                                              const char *hostname, apr_sockaddr_t *addr)
772 {
773     int j;
774 
775     /* XXX FIXME: conf->noproxies->elts is part of an opaque structure */
776     for (j = 0; j < conf->noproxies->nelts; j++) {
777         struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts;
778         struct apr_sockaddr_t *conf_addr;
779 
780         ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
781                       "checking remote machine [%s] against [%s]",
782                       hostname, npent[j].name);
783         if (ap_strstr_c(hostname, npent[j].name) || npent[j].name[0] == '*') {
784             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(00916)
785                           "connect to remote machine %s blocked: name %s "
786                           "matched", hostname, npent[j].name);
787             return HTTP_FORBIDDEN;
788         }
789 
790         /* No IP address checks if no IP address was passed in,
791          * i.e. the forward address proxy case, where this server does
792          * not resolve the hostname.  */
793         if (!addr)
794             continue;
795 
796         for (conf_addr = npent[j].addr; conf_addr; conf_addr = conf_addr->next) {
797             char caddr[MAX_IP_STR_LEN], uaddr[MAX_IP_STR_LEN];
798             apr_sockaddr_t *uri_addr;
799 
800             if (apr_sockaddr_ip_getbuf(caddr, sizeof caddr, conf_addr))
801                 continue;
802 
803             for (uri_addr = addr; uri_addr; uri_addr = uri_addr->next) {
804                 if (apr_sockaddr_ip_getbuf(uaddr, sizeof uaddr, uri_addr))
805                     continue;
806                 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
807                               "ProxyBlock comparing %s and %s", caddr, uaddr);
808                 if (!strcmp(caddr, uaddr)) {
809                     ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(00917)
810                                   "connect to remote machine %s blocked: "
811                                   "IP %s matched", hostname, caddr);
812                     return HTTP_FORBIDDEN;
813                 }
814             }
815         }
816     }
817 
818     return OK;
819 }
820 
821 /* set up the minimal filter set */
ap_proxy_pre_http_request(conn_rec * c,request_rec * r)822 PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r)
823 {
824     ap_add_input_filter("HTTP_IN", NULL, r, c);
825     return OK;
826 }
827 
ap_proxy_location_reverse_map(request_rec * r,proxy_dir_conf * conf,const char * url)828 PROXY_DECLARE(const char *) ap_proxy_location_reverse_map(request_rec *r,
829                               proxy_dir_conf *conf, const char *url)
830 {
831     proxy_req_conf *rconf;
832     struct proxy_alias *ent;
833     int i, l1, l1_orig, l2;
834     char *u;
835 
836     /*
837      * XXX FIXME: Make sure this handled the ambiguous case of the :<PORT>
838      * after the hostname
839      * XXX FIXME: Ensure the /uri component is a case sensitive match
840      */
841     if (r->proxyreq != PROXYREQ_REVERSE) {
842         return url;
843     }
844 
845     l1_orig = strlen(url);
846     if (conf->interpolate_env == 1) {
847         rconf = ap_get_module_config(r->request_config, &proxy_module);
848         ent = (struct proxy_alias *)rconf->raliases->elts;
849     }
850     else {
851         ent = (struct proxy_alias *)conf->raliases->elts;
852     }
853     for (i = 0; i < conf->raliases->nelts; i++) {
854         proxy_server_conf *sconf = (proxy_server_conf *)
855             ap_get_module_config(r->server->module_config, &proxy_module);
856         proxy_balancer *balancer;
857         const char *real = ent[i].real;
858 
859         /* Restore the url length, if it had been changed by the code below */
860         l1 = l1_orig;
861 
862         /*
863          * First check if mapping against a balancer and see
864          * if we have such a entity. If so, then we need to
865          * find the particulars of the actual worker which may
866          * or may not be the right one... basically, we need
867          * to find which member actually handled this request.
868          */
869         if (ap_proxy_valid_balancer_name((char *)real, 0) &&
870             (balancer = ap_proxy_get_balancer(r->pool, sconf, real, 1))) {
871             int n, l3 = 0;
872             proxy_worker **worker = (proxy_worker **)balancer->workers->elts;
873             const char *urlpart = ap_strchr_c(real + sizeof(BALANCER_PREFIX) - 1, '/');
874             if (urlpart) {
875                 if (!urlpart[1])
876                     urlpart = NULL;
877                 else
878                     l3 = strlen(urlpart);
879             }
880             /* The balancer comparison is a bit trickier.  Given the context
881              *   BalancerMember balancer://alias http://example.com/foo
882              *   ProxyPassReverse /bash balancer://alias/bar
883              * translate url http://example.com/foo/bar/that to /bash/that
884              */
885             for (n = 0; n < balancer->workers->nelts; n++) {
886                 l2 = strlen((*worker)->s->name_ex);
887                 if (urlpart) {
888                     /* urlpart (l3) assuredly starts with its own '/' */
889                     if ((*worker)->s->name_ex[l2 - 1] == '/')
890                         --l2;
891                     if (l1 >= l2 + l3
892                             && strncasecmp((*worker)->s->name_ex, url, l2) == 0
893                             && strncmp(urlpart, url + l2, l3) == 0) {
894                         u = apr_pstrcat(r->pool, ent[i].fake, &url[l2 + l3],
895                                         NULL);
896                         return ap_is_url(u) ? u : ap_construct_url(r->pool, u, r);
897                     }
898                 }
899                 else if (l1 >= l2 && strncasecmp((*worker)->s->name_ex, url, l2) == 0) {
900                     /* edge case where fake is just "/"... avoid double slash */
901                     if ((ent[i].fake[0] == '/') && (ent[i].fake[1] == 0) && (url[l2] == '/')) {
902                         u = apr_pstrdup(r->pool, &url[l2]);
903                     } else {
904                         u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL);
905                     }
906                     return ap_is_url(u) ? u : ap_construct_url(r->pool, u, r);
907                 }
908                 worker++;
909             }
910         }
911         else {
912             const char *part = url;
913             l2 = strlen(real);
914             if (real[0] == '/') {
915                 part = ap_strstr_c(url, "://");
916                 if (part) {
917                     part = ap_strchr_c(part+3, '/');
918                     if (part) {
919                         l1 = strlen(part);
920                     }
921                     else {
922                         part = url;
923                     }
924                 }
925                 else {
926                     part = url;
927                 }
928             }
929             if (l2 > 0 && l1 >= l2 && strncasecmp(real, part, l2) == 0) {
930                 u = apr_pstrcat(r->pool, ent[i].fake, &part[l2], NULL);
931                 return ap_is_url(u) ? u : ap_construct_url(r->pool, u, r);
932             }
933         }
934     }
935 
936     return url;
937 }
938 
939 /*
940  * Cookies are a bit trickier to match: we've got two substrings to worry
941  * about, and we can't just find them with strstr 'cos of case.  Regexp
942  * matching would be an easy fix, but for better consistency with all the
943  * other matches we'll refrain and use apr_strmatch to find path=/domain=
944  * and stick to plain strings for the config values.
945  */
ap_proxy_cookie_reverse_map(request_rec * r,proxy_dir_conf * conf,const char * str)946 PROXY_DECLARE(const char *) ap_proxy_cookie_reverse_map(request_rec *r,
947                               proxy_dir_conf *conf, const char *str)
948 {
949     proxy_req_conf *rconf = ap_get_module_config(r->request_config,
950                                                  &proxy_module);
951     struct proxy_alias *ent;
952     apr_size_t len = strlen(str);
953     const char *newpath = NULL;
954     const char *newdomain = NULL;
955     const char *pathp;
956     const char *domainp;
957     const char *pathe = NULL;
958     const char *domaine = NULL;
959     apr_size_t l1, l2, poffs = 0, doffs = 0;
960     int i;
961     int ddiff = 0;
962     int pdiff = 0;
963     char *tmpstr, *tmpstr_orig, *token, *last, *ret;
964 
965     if (r->proxyreq != PROXYREQ_REVERSE) {
966         return str;
967     }
968 
969    /*
970     * Find the match and replacement, but save replacing until we've done
971     * both path and domain so we know the new strlen
972     */
973     tmpstr_orig = tmpstr = apr_pstrdup(r->pool, str);
974     while ((token = apr_strtok(tmpstr, ";", &last))) {
975         /* skip leading spaces */
976         while (apr_isspace(*token)) {
977             ++token;
978         }
979 
980         if (ap_cstr_casecmpn("path=", token, 5) == 0) {
981             pathp = token + 5;
982             poffs = pathp - tmpstr_orig;
983             l1 = strlen(pathp);
984             pathe = str + poffs + l1;
985             if (conf->interpolate_env == 1) {
986                 ent = (struct proxy_alias *)rconf->cookie_paths->elts;
987             }
988             else {
989                 ent = (struct proxy_alias *)conf->cookie_paths->elts;
990             }
991             for (i = 0; i < conf->cookie_paths->nelts; i++) {
992                 l2 = strlen(ent[i].fake);
993                 if (l1 >= l2 && strncmp(ent[i].fake, pathp, l2) == 0) {
994                     newpath = ent[i].real;
995                     pdiff = strlen(newpath) - l1;
996                     break;
997                 }
998             }
999         }
1000         else if (ap_cstr_casecmpn("domain=", token, 7) == 0) {
1001             domainp = token + 7;
1002             doffs = domainp - tmpstr_orig;
1003             l1 = strlen(domainp);
1004             domaine = str + doffs + l1;
1005             if (conf->interpolate_env == 1) {
1006                 ent = (struct proxy_alias *)rconf->cookie_domains->elts;
1007             }
1008             else {
1009                 ent = (struct proxy_alias *)conf->cookie_domains->elts;
1010             }
1011             for (i = 0; i < conf->cookie_domains->nelts; i++) {
1012                 l2 = strlen(ent[i].fake);
1013                 if (l1 >= l2 && strncasecmp(ent[i].fake, domainp, l2) == 0) {
1014                     newdomain = ent[i].real;
1015                     ddiff = strlen(newdomain) - l1;
1016                     break;
1017                 }
1018             }
1019         }
1020 
1021         /* Iterate the remaining tokens using apr_strtok(NULL, ...) */
1022         tmpstr = NULL;
1023     }
1024 
1025     if (newpath) {
1026         ret = apr_palloc(r->pool, len + pdiff + ddiff + 1);
1027         l1 = strlen(newpath);
1028         if (newdomain) {
1029             l2 = strlen(newdomain);
1030             if (doffs > poffs) {
1031                 memcpy(ret, str, poffs);
1032                 memcpy(ret + poffs, newpath, l1);
1033                 memcpy(ret + poffs + l1, pathe, str + doffs - pathe);
1034                 memcpy(ret + doffs + pdiff, newdomain, l2);
1035                 strcpy(ret + doffs + pdiff + l2, domaine);
1036             }
1037             else {
1038                 memcpy(ret, str, doffs) ;
1039                 memcpy(ret + doffs, newdomain, l2);
1040                 memcpy(ret + doffs + l2, domaine, str + poffs - domaine);
1041                 memcpy(ret + poffs + ddiff, newpath, l1);
1042                 strcpy(ret + poffs + ddiff + l1, pathe);
1043             }
1044         }
1045         else {
1046             memcpy(ret, str, poffs);
1047             memcpy(ret + poffs, newpath, l1);
1048             strcpy(ret + poffs + l1, pathe);
1049         }
1050     }
1051     else if (newdomain) {
1052             ret = apr_palloc(r->pool, len + ddiff + 1);
1053             l2 = strlen(newdomain);
1054             memcpy(ret, str, doffs);
1055             memcpy(ret + doffs, newdomain, l2);
1056             strcpy(ret + doffs + l2, domaine);
1057     }
1058     else {
1059         ret = (char *)str; /* no change */
1060     }
1061 
1062     return ret;
1063 }
1064 
1065 /*
1066  * BALANCER related...
1067  */
1068 
1069 /*
1070  * verifies that the balancer name conforms to standards.
1071  */
ap_proxy_valid_balancer_name(char * name,int i)1072 PROXY_DECLARE(int) ap_proxy_valid_balancer_name(char *name, int i)
1073 {
1074     if (!i)
1075         i = sizeof(BALANCER_PREFIX)-1;
1076     return (!ap_cstr_casecmpn(name, BALANCER_PREFIX, i));
1077 }
1078 
1079 
ap_proxy_get_balancer(apr_pool_t * p,proxy_server_conf * conf,const char * url,int care)1080 PROXY_DECLARE(proxy_balancer *) ap_proxy_get_balancer(apr_pool_t *p,
1081                                                       proxy_server_conf *conf,
1082                                                       const char *url,
1083                                                       int care)
1084 {
1085     proxy_balancer *balancer;
1086     char *c, *uri = apr_pstrdup(p, url);
1087     int i;
1088     proxy_hashes hash;
1089 
1090     c = strchr(uri, ':');
1091     if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') {
1092         return NULL;
1093     }
1094     /* remove path from uri */
1095     if ((c = strchr(c + 3, '/'))) {
1096         *c = '\0';
1097     }
1098     ap_str_tolower(uri);
1099     hash.def = ap_proxy_hashfunc(uri, PROXY_HASHFUNC_DEFAULT);
1100     hash.fnv = ap_proxy_hashfunc(uri, PROXY_HASHFUNC_FNV);
1101     balancer = (proxy_balancer *)conf->balancers->elts;
1102     for (i = 0; i < conf->balancers->nelts; i++) {
1103         if (balancer->hash.def == hash.def && balancer->hash.fnv == hash.fnv) {
1104             if (!care || !balancer->s->inactive) {
1105                 return balancer;
1106             }
1107         }
1108         balancer++;
1109     }
1110     return NULL;
1111 }
1112 
1113 
ap_proxy_update_balancer(apr_pool_t * p,proxy_balancer * balancer,const char * url)1114 PROXY_DECLARE(char *) ap_proxy_update_balancer(apr_pool_t *p,
1115                                                 proxy_balancer *balancer,
1116                                                 const char *url)
1117 {
1118     apr_uri_t puri;
1119     if (!url) {
1120         return NULL;
1121     }
1122     if (apr_uri_parse(p, url, &puri) != APR_SUCCESS) {
1123         return apr_psprintf(p, "unable to parse: %s", url);
1124     }
1125     if (puri.path && PROXY_STRNCPY(balancer->s->vpath, puri.path) != APR_SUCCESS) {
1126         return apr_psprintf(p, "balancer %s front-end virtual-path (%s) too long",
1127                             balancer->s->name, puri.path);
1128     }
1129     if (puri.hostname && PROXY_STRNCPY(balancer->s->vhost, puri.hostname) != APR_SUCCESS) {
1130         return apr_psprintf(p, "balancer %s front-end vhost name (%s) too long",
1131                             balancer->s->name, puri.hostname);
1132     }
1133     return NULL;
1134 }
1135 
1136 #define PROXY_UNSET_NONCE '\n'
1137 
ap_proxy_define_balancer(apr_pool_t * p,proxy_balancer ** balancer,proxy_server_conf * conf,const char * url,const char * alias,int do_malloc)1138 PROXY_DECLARE(char *) ap_proxy_define_balancer(apr_pool_t *p,
1139                                                proxy_balancer **balancer,
1140                                                proxy_server_conf *conf,
1141                                                const char *url,
1142                                                const char *alias,
1143                                                int do_malloc)
1144 {
1145     proxy_balancer_method *lbmethod;
1146     proxy_balancer_shared *bshared;
1147     char *c, *q, *uri = apr_pstrdup(p, url);
1148     const char *sname;
1149 
1150     /* We should never get here without a valid BALANCER_PREFIX... */
1151 
1152     c = strchr(uri, ':');
1153     if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
1154         return apr_psprintf(p, "Bad syntax for a balancer name (%s)", uri);
1155     /* remove path from uri */
1156     if ((q = strchr(c + 3, '/')))
1157         *q = '\0';
1158 
1159     ap_str_tolower(uri);
1160     *balancer = apr_array_push(conf->balancers);
1161     memset(*balancer, 0, sizeof(proxy_balancer));
1162 
1163     /*
1164      * NOTE: The default method is byrequests - if it doesn't
1165      * exist, that's OK at this time. We check when we share and sync
1166      */
1167     lbmethod = ap_lookup_provider(PROXY_LBMETHOD, "byrequests", "0");
1168     (*balancer)->lbmethod = lbmethod;
1169 
1170     (*balancer)->workers = apr_array_make(p, 5, sizeof(proxy_worker *));
1171 #if APR_HAS_THREADS
1172     (*balancer)->gmutex = NULL;
1173     (*balancer)->tmutex = NULL;
1174 #endif
1175 
1176     if (do_malloc)
1177         bshared = ap_malloc(sizeof(proxy_balancer_shared));
1178     else
1179         bshared = apr_palloc(p, sizeof(proxy_balancer_shared));
1180 
1181     memset(bshared, 0, sizeof(proxy_balancer_shared));
1182 
1183     bshared->was_malloced = (do_malloc != 0);
1184     PROXY_STRNCPY(bshared->lbpname, "byrequests");
1185     if (PROXY_STRNCPY(bshared->name, uri) != APR_SUCCESS) {
1186         if (do_malloc) free(bshared);
1187         return apr_psprintf(p, "balancer name (%s) too long", uri);
1188     }
1189     (*balancer)->lbmethod_set = 1;
1190 
1191     /*
1192      * We do the below for verification. The real sname will be
1193      * done post_config
1194      */
1195     ap_pstr2_alnum(p, bshared->name + sizeof(BALANCER_PREFIX) - 1,
1196                    &sname);
1197     sname = apr_pstrcat(p, conf->id, "_", sname, NULL);
1198     if (PROXY_STRNCPY(bshared->sname, sname) != APR_SUCCESS) {
1199         if (do_malloc) free(bshared);
1200         return apr_psprintf(p, "balancer safe-name (%s) too long", sname);
1201     }
1202     bshared->hash.def = ap_proxy_hashfunc(bshared->name, PROXY_HASHFUNC_DEFAULT);
1203     bshared->hash.fnv = ap_proxy_hashfunc(bshared->name, PROXY_HASHFUNC_FNV);
1204     (*balancer)->hash = bshared->hash;
1205 
1206     bshared->forcerecovery = 1;
1207     bshared->sticky_separator = '.';
1208     *bshared->nonce = PROXY_UNSET_NONCE;  /* impossible valid input */
1209 
1210     (*balancer)->s = bshared;
1211     (*balancer)->sconf = conf;
1212 
1213     return ap_proxy_update_balancer(p, *balancer, alias);
1214 }
1215 
1216 /*
1217  * Create an already defined balancer and free up memory.
1218  */
ap_proxy_share_balancer(proxy_balancer * balancer,proxy_balancer_shared * shm,int i)1219 PROXY_DECLARE(apr_status_t) ap_proxy_share_balancer(proxy_balancer *balancer,
1220                                                     proxy_balancer_shared *shm,
1221                                                     int i)
1222 {
1223     apr_status_t rv = APR_SUCCESS;
1224     proxy_balancer_method *lbmethod;
1225     char *action = "copying";
1226     if (!shm || !balancer->s)
1227         return APR_EINVAL;
1228 
1229     if ((balancer->s->hash.def != shm->hash.def) ||
1230         (balancer->s->hash.fnv != shm->hash.fnv)) {
1231         memcpy(shm, balancer->s, sizeof(proxy_balancer_shared));
1232         if (balancer->s->was_malloced)
1233             free(balancer->s);
1234     } else {
1235         action = "re-using";
1236     }
1237     balancer->s = shm;
1238     balancer->s->index = i;
1239     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02337)
1240                  "%s shm[%d] (0x%pp) for %s", action, i, (void *)shm,
1241                  balancer->s->name);
1242     /* the below should always succeed */
1243     lbmethod = ap_lookup_provider(PROXY_LBMETHOD, balancer->s->lbpname, "0");
1244     if (lbmethod) {
1245         balancer->lbmethod = lbmethod;
1246         balancer->lbmethod_set = 1;
1247     } else {
1248         ap_log_error(APLOG_MARK, APLOG_CRIT, 0, ap_server_conf, APLOGNO(02432)
1249                      "Cannot find LB Method: %s", balancer->s->lbpname);
1250         return APR_EINVAL;
1251     }
1252     if (*balancer->s->nonce == PROXY_UNSET_NONCE) {
1253         char nonce[APR_UUID_FORMATTED_LENGTH + 1];
1254         apr_uuid_t uuid;
1255 
1256         /* Generate a pseudo-UUID from the PRNG to use as a nonce for
1257          * the lifetime of the process. uuid.data is a char array so
1258          * this is an adequate substitute for apr_uuid_get(). */
1259         ap_random_insecure_bytes(uuid.data, sizeof uuid.data);
1260         apr_uuid_format(nonce, &uuid);
1261         rv = PROXY_STRNCPY(balancer->s->nonce, nonce);
1262     }
1263     return rv;
1264 }
1265 
ap_proxy_initialize_balancer(proxy_balancer * balancer,server_rec * s,apr_pool_t * p)1266 PROXY_DECLARE(apr_status_t) ap_proxy_initialize_balancer(proxy_balancer *balancer, server_rec *s, apr_pool_t *p)
1267 {
1268 #if APR_HAS_THREADS
1269     apr_status_t rv = APR_SUCCESS;
1270 #endif
1271     ap_slotmem_provider_t *storage = balancer->storage;
1272     apr_size_t size;
1273     unsigned int num;
1274 
1275     if (!storage) {
1276         ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(00918)
1277                      "no provider for %s", balancer->s->name);
1278         return APR_EGENERAL;
1279     }
1280     /*
1281      * for each balancer we need to init the global
1282      * mutex and then attach to the shared worker shm
1283      */
1284     if (!balancer->gmutex) {
1285         ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(00919)
1286                      "no mutex %s", balancer->s->name);
1287         return APR_EGENERAL;
1288     }
1289 
1290     /* Re-open the mutex for the child. */
1291     rv = apr_global_mutex_child_init(&(balancer->gmutex),
1292                                      apr_global_mutex_lockfile(balancer->gmutex),
1293                                      p);
1294     if (rv != APR_SUCCESS) {
1295         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(00920)
1296                      "Failed to reopen mutex %s in child",
1297                      balancer->s->name);
1298         return rv;
1299     }
1300 
1301     /* now attach */
1302     storage->attach(&(balancer->wslot), balancer->s->sname, &size, &num, p);
1303     if (!balancer->wslot) {
1304         ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(00921) "slotmem_attach failed");
1305         return APR_EGENERAL;
1306     }
1307     if (balancer->lbmethod && balancer->lbmethod->reset)
1308         balancer->lbmethod->reset(balancer, s);
1309 
1310 #if APR_HAS_THREADS
1311     if (balancer->tmutex == NULL) {
1312         rv = apr_thread_mutex_create(&(balancer->tmutex), APR_THREAD_MUTEX_DEFAULT, p);
1313         if (rv != APR_SUCCESS) {
1314             ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(00922)
1315                          "can not create balancer thread mutex");
1316             return rv;
1317         }
1318     }
1319 #endif
1320     return APR_SUCCESS;
1321 }
1322 
proxy_balancer_get_best_worker(proxy_balancer * balancer,request_rec * r,proxy_is_best_callback_fn_t * is_best,void * baton)1323 static proxy_worker *proxy_balancer_get_best_worker(proxy_balancer *balancer,
1324                                                     request_rec *r,
1325                                                     proxy_is_best_callback_fn_t *is_best,
1326                                                     void *baton)
1327 {
1328     int i = 0;
1329     int cur_lbset = 0;
1330     int max_lbset = 0;
1331     int unusable_workers = 0;
1332     apr_pool_t *tpool = NULL;
1333     apr_array_header_t *spares = NULL;
1334     apr_array_header_t *standbys = NULL;
1335     proxy_worker *worker = NULL;
1336     proxy_worker *best_worker = NULL;
1337 
1338     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(10122)
1339                  "proxy: Entering %s for BALANCER (%s)",
1340                  balancer->lbmethod->name, balancer->s->name);
1341 
1342     apr_pool_create(&tpool, r->pool);
1343     apr_pool_tag(tpool, "proxy_lb_best");
1344 
1345     spares = apr_array_make(tpool, 1, sizeof(proxy_worker*));
1346     standbys = apr_array_make(tpool, 1, sizeof(proxy_worker*));
1347 
1348     /* Process lbsets in order, only replacing unusable workers in a given lbset
1349      * with available spares from the same lbset. Hot standbys will be used as a
1350      * last resort when all other workers and spares are unavailable.
1351      */
1352     for (cur_lbset = 0; !best_worker && (cur_lbset <= max_lbset); cur_lbset++) {
1353         unusable_workers = 0;
1354         apr_array_clear(spares);
1355         apr_array_clear(standbys);
1356 
1357         for (i = 0; i < balancer->workers->nelts; i++) {
1358             worker = APR_ARRAY_IDX(balancer->workers, i, proxy_worker *);
1359 
1360             if (worker->s->lbset > max_lbset) {
1361                 max_lbset = worker->s->lbset;
1362             }
1363 
1364             if (worker->s->lbset != cur_lbset) {
1365                 continue;
1366             }
1367 
1368             /* A draining worker that is neither a spare nor a standby should be
1369              * considered unusable to be replaced by spares.
1370              */
1371             if (PROXY_WORKER_IS_DRAINING(worker)) {
1372                 if (!PROXY_WORKER_IS_SPARE(worker) && !PROXY_WORKER_IS_STANDBY(worker)) {
1373                     unusable_workers++;
1374                 }
1375 
1376                 continue;
1377             }
1378 
1379             /* If the worker is in error state run retry on that worker. It will
1380              * be marked as operational if the retry timeout is elapsed.  The
1381              * worker might still be unusable, but we try anyway.
1382              */
1383             if (!PROXY_WORKER_IS_USABLE(worker)) {
1384                 ap_proxy_retry_worker("BALANCER", worker, r->server);
1385             }
1386 
1387             if (PROXY_WORKER_IS_SPARE(worker)) {
1388                 if (PROXY_WORKER_IS_USABLE(worker)) {
1389                     APR_ARRAY_PUSH(spares, proxy_worker *) = worker;
1390                 }
1391             }
1392             else if (PROXY_WORKER_IS_STANDBY(worker)) {
1393                 if (PROXY_WORKER_IS_USABLE(worker)) {
1394                     APR_ARRAY_PUSH(standbys, proxy_worker *) = worker;
1395                 }
1396             }
1397             else if (PROXY_WORKER_IS_USABLE(worker)) {
1398               if (is_best(worker, best_worker, baton)) {
1399                 best_worker = worker;
1400               }
1401             }
1402             else {
1403                 unusable_workers++;
1404             }
1405         }
1406 
1407         /* Check if any spares are best. */
1408         for (i = 0; (i < spares->nelts) && (i < unusable_workers); i++) {
1409           worker = APR_ARRAY_IDX(spares, i, proxy_worker *);
1410 
1411           if (is_best(worker, best_worker, baton)) {
1412             best_worker = worker;
1413           }
1414         }
1415 
1416         /* If no workers are available, use the standbys. */
1417         if (!best_worker) {
1418             for (i = 0; i < standbys->nelts; i++) {
1419               worker = APR_ARRAY_IDX(standbys, i, proxy_worker *);
1420 
1421               if (is_best(worker, best_worker, baton)) {
1422                 best_worker = worker;
1423               }
1424             }
1425         }
1426     }
1427 
1428     apr_pool_destroy(tpool);
1429 
1430     if (best_worker) {
1431         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(10123)
1432                      "proxy: %s selected worker \"%s\" : busy %" APR_SIZE_T_FMT " : lbstatus %d",
1433                      balancer->lbmethod->name, best_worker->s->name_ex,
1434                      best_worker->s->busy, best_worker->s->lbstatus);
1435     }
1436 
1437     return best_worker;
1438 }
1439 
ap_proxy_balancer_get_best_worker(proxy_balancer * balancer,request_rec * r,proxy_is_best_callback_fn_t * is_best,void * baton)1440 PROXY_DECLARE(proxy_worker *) ap_proxy_balancer_get_best_worker(proxy_balancer *balancer,
1441                                                                 request_rec *r,
1442                                                                 proxy_is_best_callback_fn_t *is_best,
1443                                                                 void *baton)
1444 {
1445     return proxy_balancer_get_best_worker(balancer, r, is_best, baton);
1446 }
1447 
1448 /*
1449  * CONNECTION related...
1450  */
1451 
socket_cleanup(proxy_conn_rec * conn)1452 static void socket_cleanup(proxy_conn_rec *conn)
1453 {
1454     conn->sock = NULL;
1455     conn->tmp_bb = NULL;
1456     conn->connection = NULL;
1457     conn->ssl_hostname = NULL;
1458     apr_pool_clear(conn->scpool);
1459 }
1460 
conn_pool_cleanup(void * theworker)1461 static apr_status_t conn_pool_cleanup(void *theworker)
1462 {
1463     ((proxy_worker *)theworker)->cp = NULL;
1464     return APR_SUCCESS;
1465 }
1466 
init_conn_pool(apr_pool_t * p,proxy_worker * worker)1467 static void init_conn_pool(apr_pool_t *p, proxy_worker *worker)
1468 {
1469     apr_pool_t *pool;
1470     apr_pool_t *dns_pool;
1471     proxy_conn_pool *cp;
1472 
1473     /*
1474      * Create a connection pool's subpool.
1475      * This pool is used for connection recycling.
1476      * Once the worker is added it is never removed but
1477      * it can be disabled.
1478      */
1479     apr_pool_create(&pool, p);
1480     apr_pool_tag(pool, "proxy_worker_cp");
1481     /*
1482      * Create a subpool of the connection pool for worker
1483      * scoped DNS resolutions. This is needed to avoid race
1484      * conditions in using the connection pool by multiple
1485      * threads during ramp up.
1486      */
1487     apr_pool_create(&dns_pool, pool);
1488     apr_pool_tag(dns_pool, "proxy_worker_dns");
1489     /*
1490      * Alloc from the same pool as worker.
1491      * proxy_conn_pool is permanently attached to the worker.
1492      */
1493     cp = (proxy_conn_pool *)apr_pcalloc(p, sizeof(proxy_conn_pool));
1494     cp->pool = pool;
1495     cp->dns_pool = dns_pool;
1496     worker->cp = cp;
1497 
1498     apr_pool_pre_cleanup_register(p, worker, conn_pool_cleanup);
1499 }
1500 
ap_proxy_connection_reusable(proxy_conn_rec * conn)1501 PROXY_DECLARE(int) ap_proxy_connection_reusable(proxy_conn_rec *conn)
1502 {
1503     proxy_worker *worker = conn->worker;
1504 
1505     return ! (conn->close || !worker->s->is_address_reusable || worker->s->disablereuse);
1506 }
1507 
connection_cleanup(void * theconn)1508 static apr_status_t connection_cleanup(void *theconn)
1509 {
1510     proxy_conn_rec *conn = (proxy_conn_rec *)theconn;
1511     proxy_worker *worker = conn->worker;
1512 
1513     if (conn->r) {
1514         apr_pool_destroy(conn->r->pool);
1515         conn->r = NULL;
1516     }
1517 
1518     /* Sanity check: Did we already return the pooled connection? */
1519     if (conn->inreslist) {
1520         ap_log_perror(APLOG_MARK, APLOG_ERR, 0, conn->pool, APLOGNO(00923)
1521                       "Pooled connection 0x%pp for worker %s has been"
1522                       " already returned to the connection pool.", conn,
1523                       ap_proxy_worker_name(conn->pool, worker));
1524         return APR_SUCCESS;
1525     }
1526 
1527     /* determine if the connection need to be closed */
1528     if (!worker->s->is_address_reusable || worker->s->disablereuse) {
1529         apr_pool_t *p = conn->pool;
1530         apr_pool_clear(p);
1531         conn = apr_pcalloc(p, sizeof(proxy_conn_rec));
1532         conn->pool = p;
1533         conn->worker = worker;
1534         apr_pool_create(&(conn->scpool), p);
1535         apr_pool_tag(conn->scpool, "proxy_conn_scpool");
1536     }
1537     else if (conn->close
1538                 || (conn->connection
1539                     && conn->connection->keepalive == AP_CONN_CLOSE)) {
1540         socket_cleanup(conn);
1541         conn->close = 0;
1542     }
1543     else if (conn->is_ssl) {
1544         /* Unbind/reset the SSL connection dir config (sslconn->dc) from
1545          * r->per_dir_config, r will likely get destroyed before this proxy
1546          * conn is reused.
1547          */
1548         ap_proxy_ssl_engine(conn->connection, worker->section_config, 1);
1549     }
1550 
1551     if (worker->s->hmax && worker->cp->res) {
1552         conn->inreslist = 1;
1553         apr_reslist_release(worker->cp->res, (void *)conn);
1554     }
1555     else
1556     {
1557         worker->cp->conn = conn;
1558     }
1559 
1560     /* Always return the SUCCESS */
1561     return APR_SUCCESS;
1562 }
1563 
1564 /* DEPRECATED */
ap_proxy_ssl_connection_cleanup(proxy_conn_rec * conn,request_rec * r)1565 PROXY_DECLARE(apr_status_t) ap_proxy_ssl_connection_cleanup(proxy_conn_rec *conn,
1566                                                             request_rec *r)
1567 {
1568     apr_status_t rv;
1569 
1570     /*
1571      * If we have an existing SSL connection it might be possible that the
1572      * server sent some SSL message we have not read so far (e.g. an SSL
1573      * shutdown message if the server closed the keepalive connection while
1574      * the connection was held unused in our pool).
1575      * So ensure that if present (=> APR_NONBLOCK_READ) it is read and
1576      * processed. We don't expect any data to be in the returned brigade.
1577      */
1578     if (conn->sock && conn->connection) {
1579         rv = ap_get_brigade(conn->connection->input_filters, conn->tmp_bb,
1580                             AP_MODE_READBYTES, APR_NONBLOCK_READ,
1581                             HUGE_STRING_LEN);
1582         if (!APR_BRIGADE_EMPTY(conn->tmp_bb)) {
1583             apr_off_t len;
1584 
1585             rv = apr_brigade_length(conn->tmp_bb, 0, &len);
1586             ap_log_rerror(APLOG_MARK, APLOG_TRACE3, rv, r,
1587                           "SSL cleanup brigade contained %"
1588                           APR_OFF_T_FMT " bytes of data.", len);
1589             apr_brigade_cleanup(conn->tmp_bb);
1590         }
1591         if ((rv != APR_SUCCESS) && !APR_STATUS_IS_EAGAIN(rv)) {
1592             socket_cleanup(conn);
1593         }
1594     }
1595     return APR_SUCCESS;
1596 }
1597 
1598 /* reslist constructor */
connection_constructor(void ** resource,void * params,apr_pool_t * pool)1599 static apr_status_t connection_constructor(void **resource, void *params,
1600                                            apr_pool_t *pool)
1601 {
1602     apr_pool_t *ctx;
1603     apr_pool_t *scpool;
1604     proxy_conn_rec *conn;
1605     proxy_worker *worker = (proxy_worker *)params;
1606 
1607     /*
1608      * Create the subpool for each connection
1609      * This keeps the memory consumption constant
1610      * when disconnecting from backend.
1611      */
1612     apr_pool_create(&ctx, pool);
1613     apr_pool_tag(ctx, "proxy_conn_pool");
1614     /*
1615      * Create another subpool that manages the data for the
1616      * socket and the connection member of the proxy_conn_rec struct as we
1617      * destroy this data more frequently than other data in the proxy_conn_rec
1618      * struct like hostname and addr (at least in the case where we have
1619      * keepalive connections that timed out).
1620      */
1621     apr_pool_create(&scpool, ctx);
1622     apr_pool_tag(scpool, "proxy_conn_scpool");
1623     conn = apr_pcalloc(ctx, sizeof(proxy_conn_rec));
1624 
1625     conn->pool   = ctx;
1626     conn->scpool = scpool;
1627     conn->worker = worker;
1628     conn->inreslist = 1;
1629     *resource = conn;
1630 
1631     return APR_SUCCESS;
1632 }
1633 
1634 /* reslist destructor */
connection_destructor(void * resource,void * params,apr_pool_t * pool)1635 static apr_status_t connection_destructor(void *resource, void *params,
1636                                           apr_pool_t *pool)
1637 {
1638     proxy_worker *worker = params;
1639 
1640     /* Destroy the pool only if not called from reslist_destroy */
1641     if (worker->cp) {
1642         proxy_conn_rec *conn = resource;
1643         apr_pool_destroy(conn->pool);
1644     }
1645 
1646     return APR_SUCCESS;
1647 }
1648 
1649 /*
1650  * WORKER related...
1651  */
1652 
ap_proxy_worker_name(apr_pool_t * p,proxy_worker * worker)1653 PROXY_DECLARE(char *) ap_proxy_worker_name(apr_pool_t *p,
1654                                            proxy_worker *worker)
1655 {
1656     if (!(*worker->s->uds_path) || !p) {
1657         /* just in case */
1658         return worker->s->name_ex;
1659     }
1660     return apr_pstrcat(p, "unix:", worker->s->uds_path, "|", worker->s->name_ex, NULL);
1661 }
1662 
ap_proxy_worker_can_upgrade(apr_pool_t * p,const proxy_worker * worker,const char * upgrade,const char * dflt)1663 PROXY_DECLARE(int) ap_proxy_worker_can_upgrade(apr_pool_t *p,
1664                                                const proxy_worker *worker,
1665                                                const char *upgrade,
1666                                                const char *dflt)
1667 {
1668     /* Find in worker->s->upgrade list (if any) */
1669     const char *worker_upgrade = worker->s->upgrade;
1670     if (*worker_upgrade) {
1671         return (strcmp(worker_upgrade, "*") == 0
1672                 || ap_cstr_casecmp(worker_upgrade, upgrade) == 0
1673                 || ap_find_token(p, worker_upgrade, upgrade));
1674     }
1675 
1676     /* Compare to the provided default (if any) */
1677     return (dflt && ap_cstr_casecmp(dflt, upgrade) == 0);
1678 }
1679 
1680 /*
1681  * Taken from ap_strcmp_match() :
1682  * Match = 0, NoMatch = 1, Abort = -1, Inval = -2
1683  * Based loosely on sections of wildmat.c by Rich Salz
1684  * Hmmm... shouldn't this really go component by component?
1685  *
1686  * Adds handling of the "\<any>" => "<any>" unescaping.
1687  */
ap_proxy_strcmp_ematch(const char * str,const char * expected)1688 static int ap_proxy_strcmp_ematch(const char *str, const char *expected)
1689 {
1690     apr_size_t x, y;
1691 
1692     for (x = 0, y = 0; expected[y]; ++y, ++x) {
1693         if (expected[y] == '$' && apr_isdigit(expected[y + 1])) {
1694             do {
1695                 y += 2;
1696             } while (expected[y] == '$' && apr_isdigit(expected[y + 1]));
1697             if (!expected[y])
1698                 return 0;
1699             while (str[x]) {
1700                 int ret;
1701                 if ((ret = ap_proxy_strcmp_ematch(&str[x++], &expected[y])) != 1)
1702                     return ret;
1703             }
1704             return -1;
1705         }
1706         else if (!str[x]) {
1707             return -1;
1708         }
1709         else if (expected[y] == '\\' && !expected[++y]) {
1710             /* NUL is an invalid char! */
1711             return -2;
1712         }
1713         if (str[x] != expected[y])
1714             return 1;
1715     }
1716     /* We got all the way through the worker path without a difference */
1717     return 0;
1718 }
1719 
ap_proxy_get_worker_ex(apr_pool_t * p,proxy_balancer * balancer,proxy_server_conf * conf,const char * url,unsigned int mask)1720 PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker_ex(apr_pool_t *p,
1721                                                      proxy_balancer *balancer,
1722                                                      proxy_server_conf *conf,
1723                                                      const char *url,
1724                                                      unsigned int mask)
1725 {
1726     proxy_worker *worker;
1727     proxy_worker *max_worker = NULL;
1728     int max_match = 0;
1729     int url_length;
1730     int min_match;
1731     int worker_name_length;
1732     const char *c;
1733     char *url_copy;
1734     int i;
1735 
1736     if (!url) {
1737         return NULL;
1738     }
1739 
1740     if (!(mask & AP_PROXY_WORKER_NO_UDS)) {
1741         url = ap_proxy_de_socketfy(p, url);
1742         if (!url) {
1743             return NULL;
1744         }
1745     }
1746 
1747     c = ap_strchr_c(url, ':');
1748     if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') {
1749         return NULL;
1750     }
1751 
1752     url_length = strlen(url);
1753     url_copy = apr_pstrmemdup(p, url, url_length);
1754 
1755     /* Default to lookup for both _PREFIX and _MATCH workers */
1756     if (!(mask & (AP_PROXY_WORKER_IS_PREFIX | AP_PROXY_WORKER_IS_MATCH))) {
1757         mask |= AP_PROXY_WORKER_IS_PREFIX | AP_PROXY_WORKER_IS_MATCH;
1758     }
1759 
1760     /*
1761      * We need to find the start of the path and
1762      * therefore we know the length of the scheme://hostname/
1763      * part to we can force-lowercase everything up to
1764      * the start of the path.
1765      */
1766     c = ap_strchr_c(c+3, '/');
1767     if (c) {
1768         char *pathstart;
1769         pathstart = url_copy + (c - url);
1770         *pathstart = '\0';
1771         ap_str_tolower(url_copy);
1772         min_match = strlen(url_copy);
1773         *pathstart = '/';
1774     }
1775     else {
1776         ap_str_tolower(url_copy);
1777         min_match = strlen(url_copy);
1778     }
1779     /*
1780      * Do a "longest match" on the worker name to find the worker that
1781      * fits best to the URL, but keep in mind that we must have at least
1782      * a minimum matching of length min_match such that
1783      * scheme://hostname[:port] matches between worker and url.
1784      */
1785 
1786     if (balancer) {
1787         proxy_worker **workers = (proxy_worker **)balancer->workers->elts;
1788         for (i = 0; i < balancer->workers->nelts; i++, workers++) {
1789             worker = *workers;
1790             if ( ((worker_name_length = strlen(worker->s->name_ex)) <= url_length)
1791                 && (worker_name_length >= min_match)
1792                 && (worker_name_length > max_match)
1793                 && (worker->s->is_name_matchable
1794                     || ((mask & AP_PROXY_WORKER_IS_PREFIX)
1795                         && strncmp(url_copy, worker->s->name_ex,
1796                                    worker_name_length) == 0))
1797                 && (!worker->s->is_name_matchable
1798                     || ((mask & AP_PROXY_WORKER_IS_MATCH)
1799                         && ap_proxy_strcmp_ematch(url_copy,
1800                                                   worker->s->name_ex) == 0)) ) {
1801                 max_worker = worker;
1802                 max_match = worker_name_length;
1803             }
1804         }
1805     } else {
1806         worker = (proxy_worker *)conf->workers->elts;
1807         for (i = 0; i < conf->workers->nelts; i++, worker++) {
1808             if ( ((worker_name_length = strlen(worker->s->name_ex)) <= url_length)
1809                 && (worker_name_length >= min_match)
1810                 && (worker_name_length > max_match)
1811                 && (worker->s->is_name_matchable
1812                     || ((mask & AP_PROXY_WORKER_IS_PREFIX)
1813                         && strncmp(url_copy, worker->s->name_ex,
1814                                    worker_name_length) == 0))
1815                 && (!worker->s->is_name_matchable
1816                     || ((mask & AP_PROXY_WORKER_IS_MATCH)
1817                         && ap_proxy_strcmp_ematch(url_copy,
1818                                                   worker->s->name_ex) == 0)) ) {
1819                 max_worker = worker;
1820                 max_match = worker_name_length;
1821             }
1822         }
1823     }
1824 
1825     return max_worker;
1826 }
1827 
ap_proxy_get_worker(apr_pool_t * p,proxy_balancer * balancer,proxy_server_conf * conf,const char * url)1828 PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p,
1829                                                   proxy_balancer *balancer,
1830                                                   proxy_server_conf *conf,
1831                                                   const char *url)
1832 {
1833     return ap_proxy_get_worker_ex(p, balancer, conf, url, 0);
1834 }
1835 
1836 /*
1837  * To create a worker from scratch first we define the
1838  * specifics of the worker; this is all local data.
1839  * We then allocate space for it if data needs to be
1840  * shared. This allows for dynamic addition during
1841  * config and runtime.
1842  */
ap_proxy_define_worker_ex(apr_pool_t * p,proxy_worker ** worker,proxy_balancer * balancer,proxy_server_conf * conf,const char * url,unsigned int mask)1843 PROXY_DECLARE(char *) ap_proxy_define_worker_ex(apr_pool_t *p,
1844                                              proxy_worker **worker,
1845                                              proxy_balancer *balancer,
1846                                              proxy_server_conf *conf,
1847                                              const char *url,
1848                                              unsigned int mask)
1849 {
1850     apr_status_t rv;
1851     proxy_worker_shared *wshared;
1852     const char *ptr = NULL, *sockpath = NULL, *pdollars = NULL;
1853     apr_port_t port_of_scheme;
1854     apr_uri_t uri;
1855 
1856     /*
1857      * Look to see if we are using UDS:
1858      * require format: unix:/path/foo/bar.sock|http://ignored/path2/
1859      * This results in talking http to the socket at /path/foo/bar.sock
1860      */
1861     if (!ap_cstr_casecmpn(url, "unix:", 5)
1862             && (ptr = ap_strchr_c(url + 5, '|'))) {
1863         rv = apr_uri_parse(p, apr_pstrmemdup(p, url, ptr - url), &uri);
1864         if (rv == APR_SUCCESS) {
1865             sockpath = ap_runtime_dir_relative(p, uri.path);;
1866             ptr++;    /* so we get the scheme for the uds */
1867         }
1868         else {
1869             ptr = url;
1870         }
1871     }
1872     else {
1873         ptr = url;
1874     }
1875 
1876     if (mask & AP_PROXY_WORKER_IS_MATCH) {
1877         /* apr_uri_parse() will accept the '$' sign anywhere in the URL but
1878          * in the :port part, and we don't want scheme://host:port$1$2/path
1879          * to fail (e.g. "ProxyPassMatch ^/(a|b)(/.*)? http://host:port$2").
1880          * So we trim all the $n from the :port and prepend them in uri.path
1881          * afterward for apr_uri_unparse() to restore the original URL below.
1882          */
1883 #define IS_REF(x) (x[0] == '$' && apr_isdigit(x[1]))
1884         const char *pos = ap_strstr_c(ptr, "://");
1885         if (pos) {
1886             pos += 3;
1887             while (*pos && *pos != ':' && *pos != '/') {
1888                 pos++;
1889             }
1890             if (*pos == ':') {
1891                 pos++;
1892                 while (*pos && !IS_REF(pos) && *pos != '/') {
1893                     pos++;
1894                 }
1895                 if (IS_REF(pos)) {
1896                     struct iovec vec[2];
1897                     const char *path = pos + 2;
1898                     while (*path && *path != '/') {
1899                         path++;
1900                     }
1901                     pdollars = apr_pstrmemdup(p, pos, path - pos);
1902                     vec[0].iov_base = (void *)ptr;
1903                     vec[0].iov_len = pos - ptr;
1904                     vec[1].iov_base = (void *)path;
1905                     vec[1].iov_len = strlen(path);
1906                     ptr = apr_pstrcatv(p, vec, 2, NULL);
1907                 }
1908             }
1909         }
1910 #undef IS_REF
1911     }
1912 
1913     /* Normalize the url (worker name) */
1914     rv = apr_uri_parse(p, ptr, &uri);
1915     if (rv != APR_SUCCESS) {
1916         return apr_pstrcat(p, "Unable to parse URL: ", url, NULL);
1917     }
1918     if (!uri.scheme) {
1919         return apr_pstrcat(p, "URL must be absolute!: ", url, NULL);
1920     }
1921     if (!uri.hostname) {
1922         if (sockpath) {
1923             /* allow for unix:/path|http: */
1924             uri.hostname = "localhost";
1925         }
1926         else {
1927             return apr_pstrcat(p, "URL must be absolute!: ", url, NULL);
1928         }
1929     }
1930     else {
1931         ap_str_tolower(uri.hostname);
1932     }
1933     ap_str_tolower(uri.scheme);
1934     port_of_scheme = ap_proxy_port_of_scheme(uri.scheme);
1935     if (uri.port && uri.port == port_of_scheme) {
1936         uri.port = 0;
1937     }
1938     if (pdollars) {
1939         /* Restore/prepend pdollars into the path. */
1940         uri.path = apr_pstrcat(p, pdollars, uri.path, NULL);
1941     }
1942     ptr = apr_uri_unparse(p, &uri, APR_URI_UNP_REVEALPASSWORD);
1943 
1944     /*
1945      * Workers can be associated w/ balancers or on their
1946      * own; ie: the generic reverse-proxy or a worker
1947      * in a simple ProxyPass statement. eg:
1948      *
1949      *      ProxyPass / http://www.example.com
1950      *
1951      * in which case the worker goes in the conf slot.
1952      */
1953     if (balancer) {
1954         proxy_worker **runtime;
1955         /* recall that we get a ptr to the ptr here */
1956         runtime = apr_array_push(balancer->workers);
1957         *worker = *runtime = apr_palloc(p, sizeof(proxy_worker));   /* right to left baby */
1958         /* we've updated the list of workers associated with
1959          * this balancer *locally* */
1960         balancer->wupdated = apr_time_now();
1961     } else if (conf) {
1962         *worker = apr_array_push(conf->workers);
1963     } else {
1964         /* we need to allocate space here */
1965         *worker = apr_palloc(p, sizeof(proxy_worker));
1966     }
1967     memset(*worker, 0, sizeof(proxy_worker));
1968 
1969     /* right here we just want to tuck away the worker info.
1970      * if called during config, we don't have shm setup yet,
1971      * so just note the info for later. */
1972     if (mask & AP_PROXY_WORKER_IS_MALLOCED)
1973         wshared = ap_malloc(sizeof(proxy_worker_shared));  /* will be freed ap_proxy_share_worker */
1974     else
1975         wshared = apr_palloc(p, sizeof(proxy_worker_shared));
1976     memset(wshared, 0, sizeof(proxy_worker_shared));
1977 
1978     if (PROXY_STRNCPY(wshared->name_ex, ptr) != APR_SUCCESS) {
1979         ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(10366)
1980         "Alert! worker name (%s) too long; truncated to: %s", ptr, wshared->name_ex);
1981     }
1982     if (PROXY_STRNCPY(wshared->name, ptr) != APR_SUCCESS) {
1983         ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, APLOGNO(010118)
1984         "worker name (%s) too long; truncated for legacy modules that do not use "
1985         "proxy_worker_shared->name_ex: %s", ptr, wshared->name);
1986     }
1987     if (PROXY_STRNCPY(wshared->scheme, uri.scheme) != APR_SUCCESS) {
1988         ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(010117)
1989         "Alert! worker scheme (%s) too long; truncated to: %s", uri.scheme, wshared->scheme);
1990     }
1991     if (PROXY_STRNCPY(wshared->hostname_ex, uri.hostname) != APR_SUCCESS) {
1992         return apr_psprintf(p, "worker hostname (%s) too long", uri.hostname);
1993     }
1994     if (PROXY_STRNCPY(wshared->hostname, uri.hostname) != APR_SUCCESS) {
1995         ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, APLOGNO(010118)
1996         "worker hostname (%s) too long; truncated for legacy modules that do not use "
1997         "proxy_worker_shared->hostname_ex: %s", uri.hostname, wshared->hostname);
1998     }
1999     wshared->port = (uri.port) ? uri.port : port_of_scheme;
2000     wshared->flush_packets = flush_off;
2001     wshared->flush_wait = PROXY_FLUSH_WAIT;
2002     wshared->is_address_reusable = 1;
2003     wshared->lbfactor = 100;
2004     wshared->passes = 1;
2005     wshared->fails = 1;
2006     wshared->interval = apr_time_from_sec(HCHECK_WATHCHDOG_DEFAULT_INTERVAL);
2007     wshared->smax = -1;
2008     wshared->hash.def = ap_proxy_hashfunc(wshared->name_ex, PROXY_HASHFUNC_DEFAULT);
2009     wshared->hash.fnv = ap_proxy_hashfunc(wshared->name_ex, PROXY_HASHFUNC_FNV);
2010     wshared->was_malloced = (mask & AP_PROXY_WORKER_IS_MALLOCED) != 0;
2011     wshared->is_name_matchable = 0;
2012     if (sockpath) {
2013         if (PROXY_STRNCPY(wshared->uds_path, sockpath) != APR_SUCCESS) {
2014             return apr_psprintf(p, "worker uds path (%s) too long", sockpath);
2015         }
2016 
2017     }
2018     else {
2019         *wshared->uds_path = '\0';
2020     }
2021     if (!balancer) {
2022         wshared->status |= PROXY_WORKER_IGNORE_ERRORS;
2023     }
2024 
2025     (*worker)->hash = wshared->hash;
2026     (*worker)->context = NULL;
2027     (*worker)->cp = NULL;
2028     (*worker)->balancer = balancer;
2029     (*worker)->s = wshared;
2030 
2031     if (mask & AP_PROXY_WORKER_IS_MATCH) {
2032         (*worker)->s->is_name_matchable = 1;
2033         if (ap_strchr_c((*worker)->s->name_ex, '$')) {
2034             /* Before AP_PROXY_WORKER_IS_MATCH (< 2.4.47), a regex worker
2035              * with dollar substitution was never matched against the actual
2036              * URL thus the request fell through the generic worker. To avoid
2037              * dns and connection reuse compat issues, let's disable connection
2038              * reuse by default, it can still be overwritten by an explicit
2039              * enablereuse=on.
2040              */
2041             (*worker)->s->disablereuse = 1;
2042         }
2043     }
2044 
2045     return NULL;
2046 }
2047 
ap_proxy_define_worker(apr_pool_t * p,proxy_worker ** worker,proxy_balancer * balancer,proxy_server_conf * conf,const char * url,int do_malloc)2048 PROXY_DECLARE(char *) ap_proxy_define_worker(apr_pool_t *p,
2049                                              proxy_worker **worker,
2050                                              proxy_balancer *balancer,
2051                                              proxy_server_conf *conf,
2052                                              const char *url,
2053                                              int do_malloc)
2054 {
2055     return ap_proxy_define_worker_ex(p, worker, balancer, conf, url,
2056                                      AP_PROXY_WORKER_IS_PREFIX |
2057                                      (do_malloc ? AP_PROXY_WORKER_IS_MALLOCED
2058                                                 : 0));
2059 }
2060 
2061 /* DEPRECATED */
ap_proxy_define_match_worker(apr_pool_t * p,proxy_worker ** worker,proxy_balancer * balancer,proxy_server_conf * conf,const char * url,int do_malloc)2062 PROXY_DECLARE(char *) ap_proxy_define_match_worker(apr_pool_t *p,
2063                                              proxy_worker **worker,
2064                                              proxy_balancer *balancer,
2065                                              proxy_server_conf *conf,
2066                                              const char *url,
2067                                              int do_malloc)
2068 {
2069     return ap_proxy_define_worker_ex(p, worker, balancer, conf, url,
2070                                      AP_PROXY_WORKER_IS_MATCH |
2071                                      (do_malloc ? AP_PROXY_WORKER_IS_MALLOCED
2072                                                 : 0));
2073 }
2074 
2075 /*
2076  * Create an already defined worker and free up memory
2077  */
ap_proxy_share_worker(proxy_worker * worker,proxy_worker_shared * shm,int i)2078 PROXY_DECLARE(apr_status_t) ap_proxy_share_worker(proxy_worker *worker, proxy_worker_shared *shm,
2079                                                   int i)
2080 {
2081     char *action = "copying";
2082     if (!shm || !worker->s)
2083         return APR_EINVAL;
2084 
2085     if ((worker->s->hash.def != shm->hash.def) ||
2086         (worker->s->hash.fnv != shm->hash.fnv)) {
2087         memcpy(shm, worker->s, sizeof(proxy_worker_shared));
2088         if (worker->s->was_malloced)
2089             free(worker->s); /* was malloced in ap_proxy_define_worker */
2090     } else {
2091         action = "re-using";
2092     }
2093     worker->s = shm;
2094     worker->s->index = i;
2095 
2096     if (APLOGdebug(ap_server_conf)) {
2097         apr_pool_t *pool;
2098         apr_pool_create(&pool, ap_server_conf->process->pool);
2099         apr_pool_tag(pool, "proxy_worker_name");
2100         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02338)
2101                      "%s shm[%d] (0x%pp) for worker: %s", action, i, (void *)shm,
2102                      ap_proxy_worker_name(pool, worker));
2103         if (pool) {
2104             apr_pool_destroy(pool);
2105         }
2106     }
2107     return APR_SUCCESS;
2108 }
2109 
ap_proxy_initialize_worker(proxy_worker * worker,server_rec * s,apr_pool_t * p)2110 PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, server_rec *s, apr_pool_t *p)
2111 {
2112     APR_OPTIONAL_FN_TYPE(http2_get_num_workers) *get_h2_num_workers;
2113     apr_status_t rv = APR_SUCCESS;
2114     int max_threads, minw, maxw;
2115 
2116     if (worker->s->status & PROXY_WORKER_INITIALIZED) {
2117         /* The worker is already initialized */
2118         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00924)
2119                      "worker %s shared already initialized",
2120                      ap_proxy_worker_name(p, worker));
2121     }
2122     else {
2123         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00925)
2124                      "initializing worker %s shared",
2125                      ap_proxy_worker_name(p, worker));
2126         /* Set default parameters */
2127         if (!worker->s->retry_set) {
2128             worker->s->retry = apr_time_from_sec(PROXY_WORKER_DEFAULT_RETRY);
2129         }
2130         /* By default address is reusable unless DisableReuse is set */
2131         if (worker->s->disablereuse) {
2132             worker->s->is_address_reusable = 0;
2133         }
2134         else {
2135             worker->s->is_address_reusable = 1;
2136         }
2137 
2138         /*
2139          * When mod_http2 is loaded we might have more threads since it has
2140          * its own pool of processing threads.
2141          */
2142         ap_mpm_query(AP_MPMQ_MAX_THREADS, &max_threads);
2143         get_h2_num_workers = APR_RETRIEVE_OPTIONAL_FN(http2_get_num_workers);
2144         if (get_h2_num_workers) {
2145             get_h2_num_workers(s, &minw, &maxw);
2146             /* So now the max is:
2147              *   max_threads-1 threads for HTTP/1 each requiring one connection
2148              *   + one thread for HTTP/2 requiring maxw connections
2149              */
2150             max_threads = max_threads - 1 + maxw;
2151         }
2152         if (max_threads > 1) {
2153             /* Default hmax is max_threads to scale with the load and never
2154              * wait for an idle connection to proceed.
2155              */
2156             if (worker->s->hmax == 0) {
2157                 worker->s->hmax = max_threads;
2158             }
2159             if (worker->s->smax == -1 || worker->s->smax > worker->s->hmax) {
2160                 worker->s->smax = worker->s->hmax;
2161             }
2162             /* Set min to be lower than smax */
2163             if (worker->s->min > worker->s->smax) {
2164                 worker->s->min = worker->s->smax;
2165             }
2166         }
2167         else {
2168             /* This will suppress the apr_reslist creation */
2169             worker->s->min = worker->s->smax = worker->s->hmax = 0;
2170         }
2171     }
2172 
2173     /* What if local is init'ed and shm isn't?? Even possible? */
2174     if (worker->local_status & PROXY_WORKER_INITIALIZED) {
2175         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00926)
2176                      "worker %s local already initialized",
2177                      ap_proxy_worker_name(p, worker));
2178     }
2179     else {
2180         apr_global_mutex_lock(proxy_mutex);
2181         /* Check again after we got the lock if we are still uninitialized */
2182         if (!(AP_VOLATILIZE_T(unsigned int, worker->local_status) & PROXY_WORKER_INITIALIZED)) {
2183             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00927)
2184                          "initializing worker %s local",
2185                          ap_proxy_worker_name(p, worker));
2186             /* Now init local worker data */
2187 #if APR_HAS_THREADS
2188             if (worker->tmutex == NULL) {
2189                 rv = apr_thread_mutex_create(&(worker->tmutex), APR_THREAD_MUTEX_DEFAULT, p);
2190                 if (rv != APR_SUCCESS) {
2191                     ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00928)
2192                                  "can not create worker thread mutex");
2193                     apr_global_mutex_unlock(proxy_mutex);
2194                     return rv;
2195                 }
2196             }
2197 #endif
2198             if (worker->cp == NULL)
2199                 init_conn_pool(p, worker);
2200             if (worker->cp == NULL) {
2201                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00929)
2202                              "can not create connection pool");
2203                 apr_global_mutex_unlock(proxy_mutex);
2204                 return APR_EGENERAL;
2205             }
2206 
2207             if (worker->s->hmax) {
2208                 rv = apr_reslist_create(&(worker->cp->res),
2209                                         worker->s->min, worker->s->smax,
2210                                         worker->s->hmax, worker->s->ttl,
2211                                         connection_constructor, connection_destructor,
2212                                         worker, worker->cp->pool);
2213 
2214                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00930)
2215                     "initialized pool in child %" APR_PID_T_FMT " for (%s) min=%d max=%d smax=%d",
2216                      getpid(), worker->s->hostname_ex, worker->s->min,
2217                      worker->s->hmax, worker->s->smax);
2218 
2219                 /* Set the acquire timeout */
2220                 if (rv == APR_SUCCESS && worker->s->acquire_set) {
2221                     apr_reslist_timeout_set(worker->cp->res, worker->s->acquire);
2222                 }
2223 
2224             }
2225             else {
2226                 void *conn;
2227 
2228                 rv = connection_constructor(&conn, worker, worker->cp->pool);
2229                 worker->cp->conn = conn;
2230 
2231                 ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(00931)
2232                      "initialized single connection worker in child %" APR_PID_T_FMT " for (%s)",
2233                      getpid(), worker->s->hostname_ex);
2234             }
2235             if (rv == APR_SUCCESS) {
2236                 worker->local_status |= (PROXY_WORKER_INITIALIZED);
2237             }
2238         }
2239         apr_global_mutex_unlock(proxy_mutex);
2240 
2241     }
2242     if (rv == APR_SUCCESS) {
2243         worker->s->status |= (PROXY_WORKER_INITIALIZED);
2244     }
2245     return rv;
2246 }
2247 
ap_proxy_retry_worker(const char * proxy_function,proxy_worker * worker,server_rec * s)2248 static int ap_proxy_retry_worker(const char *proxy_function, proxy_worker *worker,
2249         server_rec *s)
2250 {
2251     if (worker->s->status & PROXY_WORKER_IN_ERROR) {
2252         if (PROXY_WORKER_IS(worker, PROXY_WORKER_STOPPED)) {
2253             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(3305)
2254                          "%s: Won't retry worker (%s): stopped",
2255                          proxy_function, worker->s->hostname_ex);
2256             return DECLINED;
2257         }
2258         if ((worker->s->status & PROXY_WORKER_IGNORE_ERRORS)
2259             || apr_time_now() > worker->s->error_time + worker->s->retry) {
2260             ++worker->s->retries;
2261             worker->s->status &= ~PROXY_WORKER_IN_ERROR;
2262             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00932)
2263                          "%s: worker for (%s) has been marked for retry",
2264                          proxy_function, worker->s->hostname_ex);
2265             return OK;
2266         }
2267         else {
2268             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00933)
2269                          "%s: too soon to retry worker for (%s)",
2270                          proxy_function, worker->s->hostname_ex);
2271             return DECLINED;
2272         }
2273     }
2274     else {
2275         return OK;
2276     }
2277 }
2278 
2279 /*
2280  * In the case of the reverse proxy, we need to see if we
2281  * were passed a UDS url (eg: from mod_proxy) and adjust uds_path
2282  * as required.
2283  */
fix_uds_filename(request_rec * r,char ** url)2284 static int fix_uds_filename(request_rec *r, char **url)
2285 {
2286     char *uds_url = r->filename + 6, *origin_url;
2287 
2288     if (!strncmp(r->filename, "proxy:", 6) &&
2289             !ap_cstr_casecmpn(uds_url, "unix:", 5) &&
2290             (origin_url = ap_strchr(uds_url + 5, '|'))) {
2291         char *uds_path = NULL;
2292         apr_size_t url_len;
2293         apr_uri_t urisock;
2294         apr_status_t rv;
2295 
2296         *origin_url = '\0';
2297         rv = apr_uri_parse(r->pool, uds_url, &urisock);
2298         *origin_url++ = '|';
2299 
2300         if (rv == APR_SUCCESS && urisock.path && (!urisock.hostname
2301                                                   || !urisock.hostname[0])) {
2302             uds_path = ap_runtime_dir_relative(r->pool, urisock.path);
2303         }
2304         if (!uds_path) {
2305             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10292)
2306                     "Invalid proxy UDS filename (%s)", r->filename);
2307             return 0;
2308         }
2309         apr_table_setn(r->notes, "uds_path", uds_path);
2310 
2311         /* Remove the UDS path from *url and r->filename */
2312         url_len = strlen(origin_url);
2313         *url = apr_pstrmemdup(r->pool, origin_url, url_len);
2314         memcpy(uds_url, *url, url_len + 1);
2315 
2316         ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
2317                 "*: rewrite of url due to UDS(%s): %s (%s)",
2318                 uds_path, *url, r->filename);
2319     }
2320     return 1;
2321 }
2322 
ap_proxy_pre_request(proxy_worker ** worker,proxy_balancer ** balancer,request_rec * r,proxy_server_conf * conf,char ** url)2323 PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker,
2324                                         proxy_balancer **balancer,
2325                                         request_rec *r,
2326                                         proxy_server_conf *conf, char **url)
2327 {
2328     int access_status;
2329 
2330     access_status = proxy_run_pre_request(worker, balancer, r, conf, url);
2331     if (access_status == DECLINED && *balancer == NULL) {
2332         const int forward = (r->proxyreq == PROXYREQ_PROXY);
2333         *worker = ap_proxy_get_worker_ex(r->pool, NULL, conf, *url,
2334                                          forward ? AP_PROXY_WORKER_NO_UDS : 0);
2335         if (*worker) {
2336             ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
2337                           "%s: found worker %s for %s",
2338                           (*worker)->s->scheme, (*worker)->s->name_ex, *url);
2339             if (!forward && !fix_uds_filename(r, url)) {
2340                 return HTTP_INTERNAL_SERVER_ERROR;
2341             }
2342             access_status = OK;
2343         }
2344         else if (forward) {
2345             if (conf->forward) {
2346                 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
2347                               "*: found forward proxy worker for %s", *url);
2348                 *worker = conf->forward;
2349                 access_status = OK;
2350                 /*
2351                  * The forward worker does not keep connections alive, so
2352                  * ensure that mod_proxy_http does the correct thing
2353                  * regarding the Connection header in the request.
2354                  */
2355                 apr_table_setn(r->subprocess_env, "proxy-nokeepalive", "1");
2356             }
2357         }
2358         else if (r->proxyreq == PROXYREQ_REVERSE) {
2359             if (conf->reverse) {
2360                 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
2361                               "*: using default reverse proxy worker for %s "
2362                               "(no keepalive)", *url);
2363                 *worker = conf->reverse;
2364                 access_status = OK;
2365                 /*
2366                  * The reverse worker does not keep connections alive, so
2367                  * ensure that mod_proxy_http does the correct thing
2368                  * regarding the Connection header in the request.
2369                  */
2370                 apr_table_setn(r->subprocess_env, "proxy-nokeepalive", "1");
2371                 if (!fix_uds_filename(r, url)) {
2372                     return HTTP_INTERNAL_SERVER_ERROR;
2373                 }
2374             }
2375         }
2376     }
2377     else if (access_status == DECLINED && *balancer != NULL) {
2378         /* All the workers are busy */
2379         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00934)
2380                       "all workers are busy.  Unable to serve %s", *url);
2381         access_status = HTTP_SERVICE_UNAVAILABLE;
2382     }
2383     return access_status;
2384 }
2385 
ap_proxy_post_request(proxy_worker * worker,proxy_balancer * balancer,request_rec * r,proxy_server_conf * conf)2386 PROXY_DECLARE(int) ap_proxy_post_request(proxy_worker *worker,
2387                                          proxy_balancer *balancer,
2388                                          request_rec *r,
2389                                          proxy_server_conf *conf)
2390 {
2391     int access_status = OK;
2392     if (balancer) {
2393         access_status = proxy_run_post_request(worker, balancer, r, conf);
2394         if (access_status == DECLINED) {
2395             access_status = OK; /* no post_request handler available */
2396             /* TODO: recycle direct worker */
2397         }
2398     }
2399 
2400     return access_status;
2401 }
2402 
2403 /* DEPRECATED */
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,request_rec * r)2404 PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **newsock,
2405                                                const char *proxy_function,
2406                                                apr_sockaddr_t *backend_addr,
2407                                                const char *backend_name,
2408                                                proxy_server_conf *conf,
2409                                                request_rec *r)
2410 {
2411     apr_status_t rv;
2412     int connected = 0;
2413     int loglevel;
2414 
2415     while (backend_addr && !connected) {
2416         if ((rv = apr_socket_create(newsock, backend_addr->family,
2417                                     SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) {
2418             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
2419             ap_log_rerror(APLOG_MARK, loglevel, rv, r, APLOGNO(00935)
2420                           "%s: error creating fam %d socket for target %s",
2421                           proxy_function, backend_addr->family, backend_name);
2422             /*
2423              * this could be an IPv6 address from the DNS but the
2424              * local machine won't give us an IPv6 socket; hopefully the
2425              * DNS returned an additional address to try
2426              */
2427             backend_addr = backend_addr->next;
2428             continue;
2429         }
2430 
2431         if (conf->recv_buffer_size > 0 &&
2432             (rv = apr_socket_opt_set(*newsock, APR_SO_RCVBUF,
2433                                      conf->recv_buffer_size))) {
2434             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00936)
2435                           "apr_socket_opt_set(SO_RCVBUF): Failed to set "
2436                           "ProxyReceiveBufferSize, using default");
2437         }
2438 
2439         rv = apr_socket_opt_set(*newsock, APR_TCP_NODELAY, 1);
2440         if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
2441             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00937)
2442                           "apr_socket_opt_set(APR_TCP_NODELAY): "
2443                           "Failed to set");
2444         }
2445 
2446         /* Set a timeout on the socket */
2447         if (conf->timeout_set) {
2448             apr_socket_timeout_set(*newsock, conf->timeout);
2449         }
2450         else {
2451             apr_socket_timeout_set(*newsock, r->server->timeout);
2452         }
2453 
2454         ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
2455                       "%s: fam %d socket created to connect to %s",
2456                       proxy_function, backend_addr->family, backend_name);
2457 
2458         if (conf->source_address) {
2459             apr_sockaddr_t *local_addr;
2460             /* Make a copy since apr_socket_bind() could change
2461              * conf->source_address, which we don't want.
2462              */
2463             local_addr = apr_pmemdup(r->pool, conf->source_address,
2464                                      sizeof(apr_sockaddr_t));
2465             local_addr->pool = r->pool;
2466             rv = apr_socket_bind(*newsock, local_addr);
2467             if (rv != APR_SUCCESS) {
2468                 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00938)
2469                               "%s: failed to bind socket to local address",
2470                               proxy_function);
2471             }
2472         }
2473 
2474         /* make the connection out of the socket */
2475         rv = apr_socket_connect(*newsock, backend_addr);
2476 
2477         /* if an error occurred, loop round and try again */
2478         if (rv != APR_SUCCESS) {
2479             apr_socket_close(*newsock);
2480             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
2481             ap_log_rerror(APLOG_MARK, loglevel, rv, r, APLOGNO(00939)
2482                           "%s: attempt to connect to %pI (%s) failed",
2483                           proxy_function, backend_addr, backend_name);
2484             backend_addr = backend_addr->next;
2485             continue;
2486         }
2487         connected = 1;
2488     }
2489     return connected ? 0 : 1;
2490 }
2491 
ap_proxy_acquire_connection(const char * proxy_function,proxy_conn_rec ** conn,proxy_worker * worker,server_rec * s)2492 PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function,
2493                                                proxy_conn_rec **conn,
2494                                                proxy_worker *worker,
2495                                                server_rec *s)
2496 {
2497     apr_status_t rv;
2498 
2499     if (!PROXY_WORKER_IS_USABLE(worker)) {
2500         /* Retry the worker */
2501         ap_proxy_retry_worker(proxy_function, worker, s);
2502 
2503         if (!PROXY_WORKER_IS_USABLE(worker)) {
2504             ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00940)
2505                          "%s: disabled connection for (%s)",
2506                          proxy_function, worker->s->hostname_ex);
2507             return HTTP_SERVICE_UNAVAILABLE;
2508         }
2509     }
2510 
2511     if (worker->s->hmax && worker->cp->res) {
2512         rv = apr_reslist_acquire(worker->cp->res, (void **)conn);
2513     }
2514     else {
2515         /* create the new connection if the previous was destroyed */
2516         if (!worker->cp->conn) {
2517             rv = connection_constructor((void **)conn, worker, worker->cp->pool);
2518         }
2519         else {
2520             *conn = worker->cp->conn;
2521             worker->cp->conn = NULL;
2522             rv = APR_SUCCESS;
2523         }
2524     }
2525 
2526     if (rv != APR_SUCCESS) {
2527         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00941)
2528                      "%s: failed to acquire connection for (%s)",
2529                      proxy_function, worker->s->hostname_ex);
2530         return HTTP_SERVICE_UNAVAILABLE;
2531     }
2532     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00942)
2533                  "%s: has acquired connection for (%s)",
2534                  proxy_function, worker->s->hostname_ex);
2535 
2536     (*conn)->worker = worker;
2537     (*conn)->close  = 0;
2538     (*conn)->inreslist = 0;
2539 
2540     return OK;
2541 }
2542 
ap_proxy_release_connection(const char * proxy_function,proxy_conn_rec * conn,server_rec * s)2543 PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function,
2544                                                proxy_conn_rec *conn,
2545                                                server_rec *s)
2546 {
2547     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00943)
2548                 "%s: has released connection for (%s)",
2549                 proxy_function, conn->worker->s->hostname_ex);
2550     connection_cleanup(conn);
2551 
2552     return OK;
2553 }
2554 
2555 PROXY_DECLARE(int)
ap_proxy_determine_connection(apr_pool_t * p,request_rec * r,proxy_server_conf * conf,proxy_worker * worker,proxy_conn_rec * conn,apr_uri_t * uri,char ** url,const char * proxyname,apr_port_t proxyport,char * server_portstr,int server_portstr_size)2556 ap_proxy_determine_connection(apr_pool_t *p, request_rec *r,
2557                               proxy_server_conf *conf,
2558                               proxy_worker *worker,
2559                               proxy_conn_rec *conn,
2560                               apr_uri_t *uri,
2561                               char **url,
2562                               const char *proxyname,
2563                               apr_port_t proxyport,
2564                               char *server_portstr,
2565                               int server_portstr_size)
2566 {
2567     int server_port;
2568     apr_status_t err = APR_SUCCESS;
2569 #if APR_HAS_THREADS
2570     apr_status_t uerr = APR_SUCCESS;
2571 #endif
2572     const char *uds_path;
2573 
2574     /*
2575      * Break up the URL to determine the host to connect to
2576      */
2577 
2578     /* we break the URL into host, port, uri */
2579     if (APR_SUCCESS != apr_uri_parse(p, *url, uri)) {
2580         return ap_proxyerror(r, HTTP_BAD_REQUEST,
2581                              apr_pstrcat(p,"URI cannot be parsed: ", *url,
2582                                          NULL));
2583     }
2584     if (!uri->port) {
2585         uri->port = ap_proxy_port_of_scheme(uri->scheme);
2586     }
2587 
2588     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00944)
2589                  "connecting %s to %s:%d", *url, uri->hostname, uri->port);
2590 
2591     /*
2592      * allocate these out of the specified connection pool
2593      * The scheme handler decides if this is permanent or
2594      * short living pool.
2595      */
2596     /* Unless we are connecting the backend via a (forward Proxy)Remote, we
2597      * have to use the original form of the URI (non absolute), but this is
2598      * also the case via a remote proxy using the CONNECT method since the
2599      * original request (and URI) is to be embedded in the body.
2600      */
2601     if (!proxyname || conn->is_ssl) {
2602         *url = apr_pstrcat(p, uri->path, uri->query ? "?" : "",
2603                            uri->query ? uri->query : "",
2604                            uri->fragment ? "#" : "",
2605                            uri->fragment ? uri->fragment : "", NULL);
2606     }
2607     /*
2608      * Figure out if our passed in proxy_conn_rec has a usable
2609      * address cached.
2610      *
2611      * TODO: Handle this much better...
2612      *
2613      * XXX: If generic workers are ever address-reusable, we need
2614      *      to check host and port on the conn and be careful about
2615      *      spilling the cached addr from the worker.
2616      */
2617     uds_path = (*worker->s->uds_path ? worker->s->uds_path : apr_table_get(r->notes, "uds_path"));
2618     if (uds_path) {
2619         if (conn->uds_path == NULL) {
2620             /* use (*conn)->pool instead of worker->cp->pool to match lifetime */
2621             conn->uds_path = apr_pstrdup(conn->pool, uds_path);
2622         }
2623         if (conn->uds_path) {
2624             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02545)
2625                          "%s: has determined UDS as %s",
2626                          uri->scheme, conn->uds_path);
2627         }
2628         else {
2629             /* should never happen */
2630             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02546)
2631                          "%s: cannot determine UDS (%s)",
2632                          uri->scheme, uds_path);
2633 
2634         }
2635         /*
2636          * In UDS cases, some structs are NULL. Protect from de-refs
2637          * and provide info for logging at the same time.
2638          */
2639         if (!conn->addr) {
2640             apr_sockaddr_t *sa;
2641             apr_sockaddr_info_get(&sa, NULL, APR_UNSPEC, 0, 0, conn->pool);
2642             conn->addr = sa;
2643         }
2644         conn->hostname = "httpd-UDS";
2645         conn->port = 0;
2646     }
2647     else {
2648         int will_reuse = worker->s->is_address_reusable && !worker->s->disablereuse;
2649         if (!conn->hostname || !will_reuse) {
2650             if (proxyname) {
2651                 conn->hostname = apr_pstrdup(conn->pool, proxyname);
2652                 conn->port = proxyport;
2653                 /*
2654                  * If we have a forward proxy and the protocol is HTTPS,
2655                  * then we need to prepend a HTTP CONNECT request before
2656                  * sending our actual HTTPS requests.
2657                  * Save our real backend data for using it later during HTTP CONNECT.
2658                  */
2659                 if (conn->is_ssl) {
2660                     const char *proxy_auth;
2661 
2662                     forward_info *forward = apr_pcalloc(conn->pool, sizeof(forward_info));
2663                     conn->forward = forward;
2664                     forward->use_http_connect = 1;
2665                     forward->target_host = apr_pstrdup(conn->pool, uri->hostname);
2666                     forward->target_port = uri->port;
2667                     /* Do we want to pass Proxy-Authorization along?
2668                      * If we haven't used it, then YES
2669                      * If we have used it then MAYBE: RFC2616 says we MAY propagate it.
2670                      * So let's make it configurable by env.
2671                      * The logic here is the same used in mod_proxy_http.
2672                      */
2673                     proxy_auth = apr_table_get(r->headers_in, "Proxy-Authorization");
2674                     if (proxy_auth != NULL &&
2675                         proxy_auth[0] != '\0' &&
2676                         r->user == NULL && /* we haven't yet authenticated */
2677                         apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) {
2678                         forward->proxy_auth = apr_pstrdup(conn->pool, proxy_auth);
2679                     }
2680                 }
2681             }
2682             else {
2683                 conn->hostname = apr_pstrdup(conn->pool, uri->hostname);
2684                 conn->port = uri->port;
2685             }
2686             if (!will_reuse) {
2687                 /*
2688                  * Only do a lookup if we should not reuse the backend address.
2689                  * Otherwise we will look it up once for the worker.
2690                  */
2691                 err = apr_sockaddr_info_get(&(conn->addr),
2692                                             conn->hostname, APR_UNSPEC,
2693                                             conn->port, 0,
2694                                             conn->pool);
2695             }
2696             socket_cleanup(conn);
2697             conn->close = 0;
2698         }
2699         if (will_reuse) {
2700             /*
2701              * Looking up the backend address for the worker only makes sense if
2702              * we can reuse the address.
2703              */
2704             if (!worker->cp->addr) {
2705 #if APR_HAS_THREADS
2706                 if ((err = PROXY_THREAD_LOCK(worker)) != APR_SUCCESS) {
2707                     ap_log_rerror(APLOG_MARK, APLOG_ERR, err, r, APLOGNO(00945) "lock");
2708                     return HTTP_INTERNAL_SERVER_ERROR;
2709                 }
2710 #endif
2711 
2712                 /*
2713                  * Recheck addr after we got the lock. This may have changed
2714                  * while waiting for the lock.
2715                  */
2716                 if (!AP_VOLATILIZE_T(apr_sockaddr_t *, worker->cp->addr)) {
2717 
2718                     apr_sockaddr_t *addr;
2719 
2720                     /*
2721                      * Worker can have the single constant backend address.
2722                      * The single DNS lookup is used once per worker.
2723                      * If dynamic change is needed then set the addr to NULL
2724                      * inside dynamic config to force the lookup.
2725                      */
2726                     err = apr_sockaddr_info_get(&addr,
2727                                                 conn->hostname, APR_UNSPEC,
2728                                                 conn->port, 0,
2729                                                 worker->cp->dns_pool);
2730                     worker->cp->addr = addr;
2731                 }
2732                 conn->addr = worker->cp->addr;
2733 #if APR_HAS_THREADS
2734                 if ((uerr = PROXY_THREAD_UNLOCK(worker)) != APR_SUCCESS) {
2735                     ap_log_rerror(APLOG_MARK, APLOG_ERR, uerr, r, APLOGNO(00946) "unlock");
2736                 }
2737 #endif
2738             }
2739             else {
2740                 conn->addr = worker->cp->addr;
2741             }
2742         }
2743     }
2744     /* Close a possible existing socket if we are told to do so */
2745     if (conn->close) {
2746         socket_cleanup(conn);
2747         conn->close = 0;
2748     }
2749 
2750     if (err != APR_SUCCESS) {
2751         return ap_proxyerror(r, HTTP_BAD_GATEWAY,
2752                              apr_pstrcat(p, "DNS lookup failure for: ",
2753                                          conn->hostname, NULL));
2754     }
2755 
2756     /* Get the server port for the Via headers */
2757     server_port = ap_get_server_port(r);
2758     AP_DEBUG_ASSERT(server_portstr_size > 0);
2759     if (ap_is_default_port(server_port, r)) {
2760         server_portstr[0] = '\0';
2761     }
2762     else {
2763         apr_snprintf(server_portstr, server_portstr_size, ":%d",
2764                      server_port);
2765     }
2766 
2767     /* check if ProxyBlock directive on this host */
2768     if (OK != ap_proxy_checkproxyblock2(r, conf, uri->hostname,
2769                                        proxyname ? NULL : conn->addr)) {
2770         return ap_proxyerror(r, HTTP_FORBIDDEN,
2771                              "Connect to remote machine blocked");
2772     }
2773     /*
2774      * When SSL is configured, determine the hostname (SNI) for the request
2775      * and save it in conn->ssl_hostname. Close any reused connection whose
2776      * SNI differs.
2777      */
2778     if (conn->is_ssl) {
2779         proxy_dir_conf *dconf;
2780         const char *ssl_hostname;
2781         /*
2782          * In the case of ProxyPreserveHost on use the hostname of
2783          * the request if present otherwise use the one from the
2784          * backend request URI.
2785          */
2786         dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
2787         if (dconf->preserve_host) {
2788             ssl_hostname = r->hostname;
2789         }
2790         else if (conn->forward
2791                  && ((forward_info *)(conn->forward))->use_http_connect) {
2792             ssl_hostname = ((forward_info *)conn->forward)->target_host;
2793         }
2794         else {
2795             ssl_hostname = conn->hostname;
2796         }
2797         /*
2798          * Close if a SNI is in use but this request requires no or
2799          * a different one, or no SNI is in use but one is required.
2800          */
2801         if ((conn->ssl_hostname && (!ssl_hostname ||
2802                                     strcasecmp(conn->ssl_hostname,
2803                                                ssl_hostname) != 0)) ||
2804                 (!conn->ssl_hostname && ssl_hostname && conn->sock)) {
2805             socket_cleanup(conn);
2806         }
2807         if (conn->ssl_hostname == NULL) {
2808             conn->ssl_hostname = apr_pstrdup(conn->scpool, ssl_hostname);
2809         }
2810     }
2811     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00947)
2812                  "connected %s to %s:%d", *url, conn->hostname, conn->port);
2813     return OK;
2814 }
2815 
2816 #define USE_ALTERNATE_IS_CONNECTED 1
2817 
2818 #if !defined(APR_MSG_PEEK) && defined(MSG_PEEK)
2819 #define APR_MSG_PEEK MSG_PEEK
2820 #endif
2821 
2822 #if USE_ALTERNATE_IS_CONNECTED && defined(APR_MSG_PEEK)
ap_proxy_is_socket_connected(apr_socket_t * socket)2823 PROXY_DECLARE(int) ap_proxy_is_socket_connected(apr_socket_t *socket)
2824 {
2825     apr_pollfd_t pfds[1];
2826     apr_status_t status;
2827     apr_int32_t  nfds;
2828 
2829     pfds[0].reqevents = APR_POLLIN;
2830     pfds[0].desc_type = APR_POLL_SOCKET;
2831     pfds[0].desc.s = socket;
2832 
2833     do {
2834         status = apr_poll(&pfds[0], 1, &nfds, 0);
2835     } while (APR_STATUS_IS_EINTR(status));
2836 
2837     if (status == APR_SUCCESS && nfds == 1 &&
2838         pfds[0].rtnevents == APR_POLLIN) {
2839         apr_sockaddr_t unused;
2840         apr_size_t len = 1;
2841         char buf[1];
2842         /* The socket might be closed in which case
2843          * the poll will return POLLIN.
2844          * If there is no data available the socket
2845          * is closed.
2846          */
2847         status = apr_socket_recvfrom(&unused, socket, APR_MSG_PEEK,
2848                                      &buf[0], &len);
2849         if (status == APR_SUCCESS && len)
2850             return 1;
2851         else
2852             return 0;
2853     }
2854     else if (APR_STATUS_IS_EAGAIN(status) || APR_STATUS_IS_TIMEUP(status)) {
2855         return 1;
2856     }
2857     return 0;
2858 
2859 }
2860 #else
ap_proxy_is_socket_connected(apr_socket_t * sock)2861 PROXY_DECLARE(int) ap_proxy_is_socket_connected(apr_socket_t *sock)
2862 
2863 {
2864     apr_size_t buffer_len = 1;
2865     char test_buffer[1];
2866     apr_status_t socket_status;
2867     apr_interval_time_t current_timeout;
2868 
2869     /* save timeout */
2870     apr_socket_timeout_get(sock, &current_timeout);
2871     /* set no timeout */
2872     apr_socket_timeout_set(sock, 0);
2873     socket_status = apr_socket_recv(sock, test_buffer, &buffer_len);
2874     /* put back old timeout */
2875     apr_socket_timeout_set(sock, current_timeout);
2876     if (APR_STATUS_IS_EOF(socket_status)
2877         || APR_STATUS_IS_ECONNRESET(socket_status)) {
2878         return 0;
2879     }
2880     else {
2881         return 1;
2882     }
2883 }
2884 #endif /* USE_ALTERNATE_IS_CONNECTED */
2885 
2886 
2887 /*
2888  * Send a HTTP CONNECT request to a forward proxy.
2889  * The proxy is given by "backend", the target server
2890  * is contained in the "forward" member of "backend".
2891  */
send_http_connect(proxy_conn_rec * backend,server_rec * s)2892 static apr_status_t send_http_connect(proxy_conn_rec *backend,
2893                                       server_rec *s)
2894 {
2895     int status;
2896     apr_size_t nbytes;
2897     apr_size_t left;
2898     int complete = 0;
2899     char buffer[HUGE_STRING_LEN];
2900     char drain_buffer[HUGE_STRING_LEN];
2901     forward_info *forward = (forward_info *)backend->forward;
2902     int len = 0;
2903 
2904     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00948)
2905                  "CONNECT: sending the CONNECT request for %s:%d "
2906                  "to the remote proxy %pI (%s)",
2907                  forward->target_host, forward->target_port,
2908                  backend->addr, backend->hostname);
2909     /* Create the CONNECT request */
2910     nbytes = apr_snprintf(buffer, sizeof(buffer),
2911                           "CONNECT %s:%d HTTP/1.0" CRLF,
2912                           forward->target_host, forward->target_port);
2913     /* Add proxy authorization from the initial request if necessary */
2914     if (forward->proxy_auth != NULL) {
2915         nbytes += apr_snprintf(buffer + nbytes, sizeof(buffer) - nbytes,
2916                                "Proxy-Authorization: %s" CRLF,
2917                                forward->proxy_auth);
2918     }
2919     /* Set a reasonable agent and send everything */
2920     nbytes += apr_snprintf(buffer + nbytes, sizeof(buffer) - nbytes,
2921                            "Proxy-agent: %s" CRLF CRLF,
2922                            ap_get_server_banner());
2923     ap_xlate_proto_to_ascii(buffer, nbytes);
2924     apr_socket_send(backend->sock, buffer, &nbytes);
2925 
2926     /* Receive the whole CONNECT response */
2927     left = sizeof(buffer) - 1;
2928     /* Read until we find the end of the headers or run out of buffer */
2929     do {
2930         nbytes = left;
2931         status = apr_socket_recv(backend->sock, buffer + len, &nbytes);
2932         len += nbytes;
2933         left -= nbytes;
2934         buffer[len] = '\0';
2935         if (strstr(buffer + len - nbytes, CRLF_ASCII CRLF_ASCII) != NULL) {
2936             ap_xlate_proto_from_ascii(buffer, len);
2937             complete = 1;
2938             break;
2939         }
2940     } while (status == APR_SUCCESS && left > 0);
2941     /* Drain what's left */
2942     if (!complete) {
2943         nbytes = sizeof(drain_buffer) - 1;
2944         while (status == APR_SUCCESS && nbytes) {
2945             status = apr_socket_recv(backend->sock, drain_buffer, &nbytes);
2946             drain_buffer[nbytes] = '\0';
2947             nbytes = sizeof(drain_buffer) - 1;
2948             if (strstr(drain_buffer, CRLF_ASCII CRLF_ASCII) != NULL) {
2949                 break;
2950             }
2951         }
2952     }
2953 
2954     /* Check for HTTP_OK response status */
2955     if (status == APR_SUCCESS) {
2956         unsigned int major, minor;
2957         /* Only scan for three character status code */
2958         char code_str[4];
2959 
2960         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00949)
2961                      "send_http_connect: response from the forward proxy: %s",
2962                      buffer);
2963 
2964         /* Extract the returned code */
2965         if (sscanf(buffer, "HTTP/%u.%u %3s", &major, &minor, code_str) == 3) {
2966             status = atoi(code_str);
2967             if (status == HTTP_OK) {
2968                 status = APR_SUCCESS;
2969             }
2970             else {
2971                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00950)
2972                              "send_http_connect: the forward proxy returned code is '%s'",
2973                              code_str);
2974                 status = APR_INCOMPLETE;
2975             }
2976         }
2977     }
2978 
2979     return(status);
2980 }
2981 
2982 
2983 /* TODO: In APR 2.x: Extend apr_sockaddr_t to possibly be a path !!! */
ap_proxy_connect_uds(apr_socket_t * sock,const char * uds_path,apr_pool_t * p)2984 PROXY_DECLARE(apr_status_t) ap_proxy_connect_uds(apr_socket_t *sock,
2985                                                  const char *uds_path,
2986                                                  apr_pool_t *p)
2987 {
2988 #if APR_HAVE_SYS_UN_H
2989     apr_status_t rv;
2990     apr_os_sock_t rawsock;
2991     apr_interval_time_t t;
2992     struct sockaddr_un *sa;
2993     apr_socklen_t addrlen, pathlen;
2994 
2995     rv = apr_os_sock_get(&rawsock, sock);
2996     if (rv != APR_SUCCESS) {
2997         return rv;
2998     }
2999 
3000     rv = apr_socket_timeout_get(sock, &t);
3001     if (rv != APR_SUCCESS) {
3002         return rv;
3003     }
3004 
3005     pathlen = strlen(uds_path);
3006     /* copy the UDS path (including NUL) to the sockaddr_un */
3007     addrlen = APR_OFFSETOF(struct sockaddr_un, sun_path) + pathlen;
3008     sa = (struct sockaddr_un *)apr_palloc(p, addrlen + 1);
3009     memcpy(sa->sun_path, uds_path, pathlen + 1);
3010     sa->sun_family = AF_UNIX;
3011 
3012     do {
3013         rv = connect(rawsock, (struct sockaddr*)sa, addrlen);
3014     } while (rv == -1 && (rv = errno) == EINTR);
3015 
3016     if (rv && rv != EISCONN) {
3017         if ((rv == EINPROGRESS || rv == EALREADY) && (t > 0))  {
3018 #if APR_MAJOR_VERSION < 2
3019             rv = apr_wait_for_io_or_timeout(NULL, sock, 0);
3020 #else
3021             rv = apr_socket_wait(sock, APR_WAIT_WRITE);
3022 #endif
3023         }
3024         if (rv != APR_SUCCESS) {
3025             return rv;
3026         }
3027     }
3028 
3029     return APR_SUCCESS;
3030 #else
3031     return APR_ENOTIMPL;
3032 #endif
3033 }
3034 
ap_proxy_check_connection(const char * scheme,proxy_conn_rec * conn,server_rec * server,unsigned max_blank_lines,int flags)3035 PROXY_DECLARE(apr_status_t) ap_proxy_check_connection(const char *scheme,
3036                                                       proxy_conn_rec *conn,
3037                                                       server_rec *server,
3038                                                       unsigned max_blank_lines,
3039                                                       int flags)
3040 {
3041     apr_status_t rv = APR_SUCCESS;
3042     proxy_worker *worker = conn->worker;
3043 
3044     if (!PROXY_WORKER_IS_USABLE(worker)) {
3045         /*
3046          * The worker is in error likely done by a different thread / process
3047          * e.g. for a timeout or bad status. We should respect this and should
3048          * not continue with a connection via this worker even if we got one.
3049          */
3050         rv = APR_EINVAL;
3051     }
3052     else if (conn->connection) {
3053         /* We have a conn_rec, check the full filter stack for things like
3054          * SSL alert/shutdown, filters aside data...
3055          */
3056         rv = ap_check_pipeline(conn->connection, conn->tmp_bb,
3057                                max_blank_lines);
3058         apr_brigade_cleanup(conn->tmp_bb);
3059         if (rv == APR_SUCCESS) {
3060             /* Some data available, the caller might not want them. */
3061             if (flags & PROXY_CHECK_CONN_EMPTY) {
3062                 rv = APR_ENOTEMPTY;
3063             }
3064         }
3065         else if (APR_STATUS_IS_EAGAIN(rv)) {
3066             /* Filter chain is OK and empty, yet we can't determine from
3067              * ap_check_pipeline (actually ap_core_input_filter) whether
3068              * an empty non-blocking read is EAGAIN or EOF on the socket
3069              * side (it's always SUCCESS), so check it explicitly here.
3070              */
3071             if (ap_proxy_is_socket_connected(conn->sock)) {
3072                 rv = APR_SUCCESS;
3073             }
3074             else {
3075                 rv = APR_EPIPE;
3076             }
3077         }
3078     }
3079     else if (conn->sock) {
3080         /* For modules working with sockets directly, check it. */
3081         if (!ap_proxy_is_socket_connected(conn->sock)) {
3082             rv = APR_EPIPE;
3083         }
3084     }
3085     else {
3086         rv = APR_ENOSOCKET;
3087     }
3088 
3089     if (rv == APR_SUCCESS) {
3090         if (APLOGtrace2(server)) {
3091             apr_sockaddr_t *local_addr = NULL;
3092             apr_socket_addr_get(&local_addr, APR_LOCAL, conn->sock);
3093             ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, server,
3094                          "%s: reusing backend connection %pI<>%pI",
3095                          scheme, local_addr, conn->addr);
3096         }
3097     }
3098     else if (conn->sock) {
3099         /* This clears conn->scpool (and associated data), so backup and
3100          * restore any ssl_hostname for this connection set earlier by
3101          * ap_proxy_determine_connection().
3102          */
3103         char ssl_hostname[PROXY_WORKER_RFC1035_NAME_SIZE];
3104         if (rv == APR_EINVAL
3105                 || !conn->ssl_hostname
3106                 || PROXY_STRNCPY(ssl_hostname, conn->ssl_hostname)) {
3107             ssl_hostname[0] = '\0';
3108         }
3109 
3110         socket_cleanup(conn);
3111         if (rv != APR_ENOTEMPTY) {
3112             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, APLOGNO(00951)
3113                          "%s: backend socket is disconnected.", scheme);
3114         }
3115         else {
3116             ap_log_error(APLOG_MARK, APLOG_WARNING, 0, server, APLOGNO(03408)
3117                          "%s: reusable backend connection is not empty: "
3118                          "forcibly closed", scheme);
3119         }
3120 
3121         if (ssl_hostname[0]) {
3122             conn->ssl_hostname = apr_pstrdup(conn->scpool, ssl_hostname);
3123         }
3124     }
3125 
3126     return rv;
3127 }
3128 
ap_proxy_connect_backend(const char * proxy_function,proxy_conn_rec * conn,proxy_worker * worker,server_rec * s)3129 PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
3130                                             proxy_conn_rec *conn,
3131                                             proxy_worker *worker,
3132                                             server_rec *s)
3133 {
3134     apr_status_t rv;
3135     int loglevel;
3136     apr_sockaddr_t *backend_addr = conn->addr;
3137     /* the local address to use for the outgoing connection */
3138     apr_sockaddr_t *local_addr;
3139     apr_socket_t *newsock;
3140     void *sconf = s->module_config;
3141     proxy_server_conf *conf =
3142         (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
3143 
3144     rv = ap_proxy_check_connection(proxy_function, conn, s, 0, 0);
3145     if (rv == APR_EINVAL) {
3146         return DECLINED;
3147     }
3148 
3149     while (rv != APR_SUCCESS && (backend_addr || conn->uds_path)) {
3150 #if APR_HAVE_SYS_UN_H
3151         if (conn->uds_path)
3152         {
3153             rv = apr_socket_create(&newsock, AF_UNIX, SOCK_STREAM, 0,
3154                                    conn->scpool);
3155             if (rv != APR_SUCCESS) {
3156                 loglevel = APLOG_ERR;
3157                 ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(02453)
3158                              "%s: error creating Unix domain socket for "
3159                              "target %s",
3160                              proxy_function,
3161                              worker->s->hostname_ex);
3162                 break;
3163             }
3164             conn->connection = NULL;
3165 
3166             rv = ap_proxy_connect_uds(newsock, conn->uds_path, conn->scpool);
3167             if (rv != APR_SUCCESS) {
3168                 apr_socket_close(newsock);
3169                 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(02454)
3170                              "%s: attempt to connect to Unix domain socket "
3171                              "%s (%s) failed",
3172                              proxy_function,
3173                              conn->uds_path,
3174                              worker->s->hostname_ex);
3175                 break;
3176             }
3177 
3178             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02823)
3179                          "%s: connection established with Unix domain socket "
3180                          "%s (%s)",
3181                          proxy_function,
3182                          conn->uds_path,
3183                          worker->s->hostname_ex);
3184         }
3185         else
3186 #endif
3187         {
3188             if ((rv = apr_socket_create(&newsock, backend_addr->family,
3189                                         SOCK_STREAM, APR_PROTO_TCP,
3190                                         conn->scpool)) != APR_SUCCESS) {
3191                 loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
3192                 ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00952)
3193                              "%s: error creating fam %d socket for "
3194                              "target %s",
3195                              proxy_function,
3196                              backend_addr->family,
3197                              worker->s->hostname_ex);
3198                 /*
3199                  * this could be an IPv6 address from the DNS but the
3200                  * local machine won't give us an IPv6 socket; hopefully the
3201                  * DNS returned an additional address to try
3202                  */
3203                 backend_addr = backend_addr->next;
3204                 continue;
3205             }
3206             conn->connection = NULL;
3207 
3208             if (worker->s->recv_buffer_size > 0 &&
3209                 (rv = apr_socket_opt_set(newsock, APR_SO_RCVBUF,
3210                                          worker->s->recv_buffer_size))) {
3211                 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00953)
3212                              "apr_socket_opt_set(SO_RCVBUF): Failed to set "
3213                              "ProxyReceiveBufferSize, using default");
3214             }
3215 
3216             rv = apr_socket_opt_set(newsock, APR_TCP_NODELAY, 1);
3217             if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
3218                 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00954)
3219                              "apr_socket_opt_set(APR_TCP_NODELAY): "
3220                              "Failed to set");
3221             }
3222 
3223             /* Set a timeout for connecting to the backend on the socket */
3224             if (worker->s->conn_timeout_set) {
3225                 apr_socket_timeout_set(newsock, worker->s->conn_timeout);
3226             }
3227             else if (worker->s->timeout_set) {
3228                 apr_socket_timeout_set(newsock, worker->s->timeout);
3229             }
3230             else if (conf->timeout_set) {
3231                 apr_socket_timeout_set(newsock, conf->timeout);
3232             }
3233             else {
3234                 apr_socket_timeout_set(newsock, s->timeout);
3235             }
3236             /* Set a keepalive option */
3237             if (worker->s->keepalive) {
3238                 if ((rv = apr_socket_opt_set(newsock,
3239                                              APR_SO_KEEPALIVE, 1)) != APR_SUCCESS) {
3240                     ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00955)
3241                                  "apr_socket_opt_set(SO_KEEPALIVE): Failed to set"
3242                                  " Keepalive");
3243                 }
3244             }
3245             ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, s,
3246                          "%s: fam %d socket created to connect to %s",
3247                          proxy_function, backend_addr->family, worker->s->hostname_ex);
3248 
3249             if (conf->source_address_set) {
3250                 local_addr = apr_pmemdup(conn->scpool, conf->source_address,
3251                                          sizeof(apr_sockaddr_t));
3252                 local_addr->pool = conn->scpool;
3253                 rv = apr_socket_bind(newsock, local_addr);
3254                 if (rv != APR_SUCCESS) {
3255                     ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00956)
3256                                  "%s: failed to bind socket to local address",
3257                                  proxy_function);
3258                 }
3259             }
3260 
3261             /* make the connection out of the socket */
3262             rv = apr_socket_connect(newsock, backend_addr);
3263 
3264             /* if an error occurred, loop round and try again */
3265             if (rv != APR_SUCCESS) {
3266                 apr_socket_close(newsock);
3267                 loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
3268                 ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00957)
3269                              "%s: attempt to connect to %pI (%s) failed",
3270                              proxy_function,
3271                              backend_addr,
3272                              worker->s->hostname_ex);
3273                 backend_addr = backend_addr->next;
3274                 continue;
3275             }
3276 
3277             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02824)
3278                          "%s: connection established with %pI (%s)",
3279                          proxy_function,
3280                          backend_addr,
3281                          worker->s->hostname_ex);
3282         }
3283 
3284         /* Set a timeout on the socket */
3285         if (worker->s->timeout_set) {
3286             apr_socket_timeout_set(newsock, worker->s->timeout);
3287         }
3288         else if (conf->timeout_set) {
3289             apr_socket_timeout_set(newsock, conf->timeout);
3290         }
3291         else {
3292              apr_socket_timeout_set(newsock, s->timeout);
3293         }
3294 
3295         conn->sock = newsock;
3296 
3297         if (!conn->uds_path && conn->forward) {
3298             forward_info *forward = (forward_info *)conn->forward;
3299             /*
3300              * For HTTP CONNECT we need to prepend CONNECT request before
3301              * sending our actual HTTPS requests.
3302              */
3303             if (forward->use_http_connect) {
3304                 rv = send_http_connect(conn, s);
3305                 /* If an error occurred, loop round and try again */
3306                 if (rv != APR_SUCCESS) {
3307                     conn->sock = NULL;
3308                     apr_socket_close(newsock);
3309                     loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
3310                     ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00958)
3311                                  "%s: attempt to connect to %s:%d "
3312                                  "via http CONNECT through %pI (%s) failed",
3313                                  proxy_function,
3314                                  forward->target_host, forward->target_port,
3315                                  backend_addr, worker->s->hostname_ex);
3316                     backend_addr = backend_addr->next;
3317                     continue;
3318                 }
3319             }
3320         }
3321     }
3322 
3323     if (PROXY_WORKER_IS_USABLE(worker)) {
3324         /*
3325          * Put the entire worker to error state if
3326          * the PROXY_WORKER_IGNORE_ERRORS flag is not set.
3327          * Although some connections may be alive
3328          * no further connections to the worker could be made
3329          */
3330         if (rv != APR_SUCCESS) {
3331             if (!(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) {
3332                 worker->s->error_time = apr_time_now();
3333                 worker->s->status |= PROXY_WORKER_IN_ERROR;
3334                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00959)
3335                     "ap_proxy_connect_backend disabling worker for (%s) for %"
3336                     APR_TIME_T_FMT "s",
3337                     worker->s->hostname_ex, apr_time_sec(worker->s->retry));
3338             }
3339         }
3340         else {
3341             if (worker->s->retries) {
3342                 /*
3343                  * A worker came back. So here is where we need to
3344                  * either reset all params to initial conditions or
3345                  * apply some sort of aging
3346                  */
3347             }
3348             worker->s->error_time = 0;
3349             worker->s->retries = 0;
3350         }
3351     }
3352     else {
3353         /*
3354          * The worker is in error likely done by a different thread / process
3355          * e.g. for a timeout or bad status. We should respect this and should
3356          * not continue with a connection via this worker even if we got one.
3357          */
3358         if (rv == APR_SUCCESS) {
3359             socket_cleanup(conn);
3360         }
3361         rv = APR_EINVAL;
3362     }
3363 
3364     return rv == APR_SUCCESS ? OK : DECLINED;
3365 }
3366 
connection_shutdown(void * theconn)3367 static apr_status_t connection_shutdown(void *theconn)
3368 {
3369     proxy_conn_rec *conn = (proxy_conn_rec *)theconn;
3370     conn_rec *c = conn->connection;
3371     if (c) {
3372         if (!c->aborted) {
3373             apr_interval_time_t saved_timeout = 0;
3374             apr_socket_timeout_get(conn->sock, &saved_timeout);
3375             if (saved_timeout) {
3376                 apr_socket_timeout_set(conn->sock, 0);
3377             }
3378 
3379             (void)ap_shutdown_conn(c, 0);
3380             c->aborted = 1;
3381 
3382             if (saved_timeout) {
3383                 apr_socket_timeout_set(conn->sock, saved_timeout);
3384             }
3385         }
3386 
3387         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02642)
3388                       "proxy: connection shutdown");
3389     }
3390     return APR_SUCCESS;
3391 }
3392 
3393 
proxy_connection_create(const char * proxy_function,proxy_conn_rec * conn,request_rec * r,server_rec * s)3394 static int proxy_connection_create(const char *proxy_function,
3395                                    proxy_conn_rec *conn,
3396                                    request_rec *r, server_rec *s)
3397 {
3398     ap_conf_vector_t *per_dir_config = (r) ? r->per_dir_config
3399                                            : conn->worker->section_config;
3400     apr_sockaddr_t *backend_addr = conn->addr;
3401     int rc;
3402     apr_interval_time_t current_timeout;
3403     apr_bucket_alloc_t *bucket_alloc;
3404 
3405     if (conn->connection) {
3406         if (conn->is_ssl) {
3407             /* on reuse, reinit the SSL connection dir config with the current
3408              * r->per_dir_config, the previous one was reset on release.
3409              */
3410             ap_proxy_ssl_engine(conn->connection, per_dir_config, 1);
3411         }
3412         return OK;
3413     }
3414 
3415     bucket_alloc = apr_bucket_alloc_create(conn->scpool);
3416     conn->tmp_bb = apr_brigade_create(conn->scpool, bucket_alloc);
3417     /*
3418      * The socket is now open, create a new backend server connection
3419      */
3420     conn->connection = ap_run_create_connection(conn->scpool, s, conn->sock,
3421                                                 0, NULL,
3422                                                 bucket_alloc);
3423 
3424     if (!conn->connection) {
3425         /*
3426          * the peer reset the connection already; ap_run_create_connection()
3427          * closed the socket
3428          */
3429         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
3430                      s, APLOGNO(00960) "%s: an error occurred creating a "
3431                      "new connection to %pI (%s)", proxy_function,
3432                      backend_addr, conn->hostname);
3433         /* XXX: Will be closed when proxy_conn is closed */
3434         socket_cleanup(conn);
3435         return HTTP_INTERNAL_SERVER_ERROR;
3436     }
3437 
3438     /* For ssl connection to backend */
3439     if (conn->is_ssl) {
3440         if (!ap_proxy_ssl_engine(conn->connection, per_dir_config, 1)) {
3441             ap_log_error(APLOG_MARK, APLOG_ERR, 0,
3442                          s, APLOGNO(00961) "%s: failed to enable ssl support "
3443                          "for %pI (%s)", proxy_function,
3444                          backend_addr, conn->hostname);
3445             return HTTP_INTERNAL_SERVER_ERROR;
3446         }
3447         if (conn->ssl_hostname) {
3448             /* Set a note on the connection about what CN is requested,
3449              * such that mod_ssl can check if it is requested to do so.
3450              */
3451             ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, conn->connection,
3452                           "%s: set SNI to %s for (%s)", proxy_function,
3453                           conn->ssl_hostname, conn->hostname);
3454             apr_table_setn(conn->connection->notes, "proxy-request-hostname",
3455                            conn->ssl_hostname);
3456         }
3457     }
3458     else {
3459         /* TODO: See if this will break FTP */
3460         ap_proxy_ssl_engine(conn->connection, per_dir_config, 0);
3461     }
3462 
3463     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00962)
3464                  "%s: connection complete to %pI (%s)",
3465                  proxy_function, backend_addr, conn->hostname);
3466 
3467     /*
3468      * save the timeout of the socket because core_pre_connection
3469      * will set it to base_server->timeout
3470      * (core TimeOut directive).
3471      */
3472     apr_socket_timeout_get(conn->sock, &current_timeout);
3473     /* set up the connection filters */
3474     rc = ap_run_pre_connection(conn->connection, conn->sock);
3475     if (rc != OK && rc != DONE) {
3476         conn->connection->aborted = 1;
3477         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00963)
3478                      "%s: pre_connection setup failed (%d)",
3479                      proxy_function, rc);
3480         return rc;
3481     }
3482     apr_socket_timeout_set(conn->sock, current_timeout);
3483 
3484     /* Shutdown the connection before closing it (eg. SSL connections
3485      * need to be close-notify-ed).
3486      */
3487     apr_pool_pre_cleanup_register(conn->scpool, conn, connection_shutdown);
3488 
3489     return OK;
3490 }
3491 
ap_proxy_connection_create_ex(const char * proxy_function,proxy_conn_rec * conn,request_rec * r)3492 PROXY_DECLARE(int) ap_proxy_connection_create_ex(const char *proxy_function,
3493                                                  proxy_conn_rec *conn,
3494                                                  request_rec *r)
3495 {
3496     return proxy_connection_create(proxy_function, conn, r, r->server);
3497 }
3498 
ap_proxy_connection_create(const char * proxy_function,proxy_conn_rec * conn,conn_rec * c,server_rec * s)3499 PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function,
3500                                               proxy_conn_rec *conn,
3501                                               conn_rec *c, server_rec *s)
3502 {
3503     (void) c; /* unused */
3504     return proxy_connection_create(proxy_function, conn, NULL, s);
3505 }
3506 
ap_proxy_lb_workers(void)3507 int ap_proxy_lb_workers(void)
3508 {
3509     /*
3510      * Since we can't resize the scoreboard when reconfiguring, we
3511      * have to impose a limit on the number of workers, we are
3512      * able to reconfigure to.
3513      */
3514     if (!lb_workers_limit)
3515         lb_workers_limit = proxy_lb_workers + PROXY_DYNAMIC_BALANCER_LIMIT;
3516     return lb_workers_limit;
3517 }
3518 
error_code_overridden(const int * elts,int nelts,int code)3519 static APR_INLINE int error_code_overridden(const int *elts, int nelts,
3520                                             int code)
3521 {
3522     int min = 0;
3523     int max = nelts - 1;
3524     AP_DEBUG_ASSERT(max >= 0);
3525 
3526     while (min < max) {
3527         int mid = (min + max) / 2;
3528         int val = elts[mid];
3529 
3530         if (val < code) {
3531             min = mid + 1;
3532         }
3533         else if (val > code) {
3534             max = mid - 1;
3535         }
3536         else {
3537             return 1;
3538         }
3539     }
3540 
3541     return elts[min] == code;
3542 }
3543 
ap_proxy_should_override(proxy_dir_conf * conf,int code)3544 PROXY_DECLARE(int) ap_proxy_should_override(proxy_dir_conf *conf, int code)
3545 {
3546     if (!conf->error_override)
3547         return 0;
3548 
3549     if (apr_is_empty_array(conf->error_override_codes))
3550         return ap_is_HTTP_ERROR(code);
3551 
3552     /* Since error_override_codes is sorted, apply binary search. */
3553     return error_code_overridden((int *)conf->error_override_codes->elts,
3554                                  conf->error_override_codes->nelts,
3555                                  code);
3556 }
3557 
ap_proxy_backend_broke(request_rec * r,apr_bucket_brigade * brigade)3558 PROXY_DECLARE(void) ap_proxy_backend_broke(request_rec *r,
3559                                            apr_bucket_brigade *brigade)
3560 {
3561     apr_bucket *e;
3562     conn_rec *c = r->connection;
3563 
3564     r->no_cache = 1;
3565     /*
3566      * If this is a subrequest, then prevent also caching of the main
3567      * request.
3568      */
3569     if (r->main)
3570         r->main->no_cache = 1;
3571     e = ap_bucket_error_create(HTTP_BAD_GATEWAY, NULL, c->pool,
3572                                c->bucket_alloc);
3573     APR_BRIGADE_INSERT_TAIL(brigade, e);
3574     e = apr_bucket_eos_create(c->bucket_alloc);
3575     APR_BRIGADE_INSERT_TAIL(brigade, e);
3576 }
3577 
3578 /*
3579  * Provide a string hashing function for the proxy.
3580  * We offer 2 methods: one is the APR model but we
3581  * also provide our own, based on either FNV or SDBM.
3582  * The reason is in case we want to use both to ensure no
3583  * collisions.
3584  */
3585 PROXY_DECLARE(unsigned int)
ap_proxy_hashfunc(const char * str,proxy_hash_t method)3586 ap_proxy_hashfunc(const char *str, proxy_hash_t method)
3587 {
3588     if (method == PROXY_HASHFUNC_APR) {
3589         apr_ssize_t slen = strlen(str);
3590         return apr_hashfunc_default(str, &slen);
3591     }
3592     else if (method == PROXY_HASHFUNC_FNV) {
3593         /* FNV model */
3594         unsigned int hash;
3595         const unsigned int fnv_prime = 0x811C9DC5;
3596         for (hash = 0; *str; str++) {
3597             hash *= fnv_prime;
3598             hash ^= (*str);
3599         }
3600         return hash;
3601     }
3602     else { /* method == PROXY_HASHFUNC_DEFAULT */
3603         /* SDBM model */
3604         unsigned int hash;
3605         for (hash = 0; *str; str++) {
3606             hash = (*str) + (hash << 6) + (hash << 16) - hash;
3607         }
3608         return hash;
3609     }
3610 }
3611 
ap_proxy_set_wstatus(char c,int set,proxy_worker * w)3612 PROXY_DECLARE(apr_status_t) ap_proxy_set_wstatus(char c, int set, proxy_worker *w)
3613 {
3614     unsigned int *status = &w->s->status;
3615     char flag = toupper(c);
3616     proxy_wstat_t *pwt = proxy_wstat_tbl;
3617     while (pwt->bit) {
3618         if (flag == pwt->flag) {
3619             if (set)
3620                 *status |= pwt->bit;
3621             else
3622                 *status &= ~(pwt->bit);
3623             return APR_SUCCESS;
3624         }
3625         pwt++;
3626     }
3627     return APR_EINVAL;
3628 }
3629 
ap_proxy_parse_wstatus(apr_pool_t * p,proxy_worker * w)3630 PROXY_DECLARE(char *) ap_proxy_parse_wstatus(apr_pool_t *p, proxy_worker *w)
3631 {
3632     char *ret = "";
3633     unsigned int status = w->s->status;
3634     proxy_wstat_t *pwt = proxy_wstat_tbl;
3635     while (pwt->bit) {
3636         if (status & pwt->bit)
3637             ret = apr_pstrcat(p, ret, pwt->name, NULL);
3638         pwt++;
3639     }
3640     if (!*ret) {
3641         ret = "??? ";
3642     }
3643     if (PROXY_WORKER_IS_USABLE(w))
3644         ret = apr_pstrcat(p, ret, "Ok ", NULL);
3645     return ret;
3646 }
3647 
ap_proxy_sync_balancer(proxy_balancer * b,server_rec * s,proxy_server_conf * conf)3648 PROXY_DECLARE(apr_status_t) ap_proxy_sync_balancer(proxy_balancer *b, server_rec *s,
3649                                                     proxy_server_conf *conf)
3650 {
3651     proxy_worker **workers;
3652     int i;
3653     int index;
3654     proxy_worker_shared *shm;
3655     proxy_balancer_method *lbmethod;
3656     ap_slotmem_provider_t *storage = b->storage;
3657 
3658     if (b->s->wupdated <= b->wupdated)
3659         return APR_SUCCESS;
3660     /* balancer sync */
3661     lbmethod = ap_lookup_provider(PROXY_LBMETHOD, b->s->lbpname, "0");
3662     if (lbmethod) {
3663         b->lbmethod = lbmethod;
3664     } else {
3665         ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(02433)
3666                      "Cannot find LB Method: %s", b->s->lbpname);
3667         return APR_EINVAL;
3668     }
3669 
3670     /* worker sync */
3671 
3672     /*
3673      * Look thru the list of workers in shm
3674      * and see which one(s) we are lacking...
3675      * again, the cast to unsigned int is safe
3676      * since our upper limit is always max_workers
3677      * which is int.
3678      */
3679     for (index = 0; index < b->max_workers; index++) {
3680         int found;
3681         apr_status_t rv;
3682         if ((rv = storage->dptr(b->wslot, (unsigned int)index, (void *)&shm)) != APR_SUCCESS) {
3683             ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(00965) "worker slotmem_dptr failed");
3684             return APR_EGENERAL;
3685         }
3686         /* account for possible "holes" in the slotmem
3687          * (eg: slots 0-2 are used, but 3 isn't, but 4-5 is)
3688          */
3689         if (!shm->hash.def || !shm->hash.fnv)
3690             continue;
3691         found = 0;
3692         workers = (proxy_worker **)b->workers->elts;
3693         for (i = 0; i < b->workers->nelts; i++, workers++) {
3694             proxy_worker *worker = *workers;
3695             if (worker->hash.def == shm->hash.def && worker->hash.fnv == shm->hash.fnv) {
3696                 found = 1;
3697                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02402)
3698                              "re-grabbing shm[%d] (0x%pp) for worker: %s", i, (void *)shm,
3699                              ap_proxy_worker_name(conf->pool, worker));
3700                 break;
3701             }
3702         }
3703         if (!found) {
3704             proxy_worker **runtime;
3705             /* XXX: a thread mutex is maybe enough here */
3706             apr_global_mutex_lock(proxy_mutex);
3707             runtime = apr_array_push(b->workers);
3708             *runtime = apr_pcalloc(conf->pool, sizeof(proxy_worker));
3709             apr_global_mutex_unlock(proxy_mutex);
3710             (*runtime)->hash = shm->hash;
3711             (*runtime)->balancer = b;
3712             (*runtime)->s = shm;
3713 
3714             rv = ap_proxy_initialize_worker(*runtime, s, conf->pool);
3715             if (rv != APR_SUCCESS) {
3716                 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(00966) "Cannot init worker");
3717                 return rv;
3718             }
3719             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02403)
3720                          "grabbing shm[%d] (0x%pp) for worker: %s", i, (void *)shm,
3721                          (*runtime)->s->name_ex);
3722         }
3723     }
3724     if (b->s->need_reset) {
3725         if (b->lbmethod && b->lbmethod->reset)
3726             b->lbmethod->reset(b, s);
3727         b->s->need_reset = 0;
3728     }
3729     b->wupdated = b->s->wupdated;
3730     return APR_SUCCESS;
3731 }
3732 
ap_proxy_find_workershm(ap_slotmem_provider_t * storage,ap_slotmem_instance_t * slot,proxy_worker * worker,unsigned int * index)3733 PROXY_DECLARE(proxy_worker_shared *) ap_proxy_find_workershm(ap_slotmem_provider_t *storage,
3734                                                                ap_slotmem_instance_t *slot,
3735                                                                proxy_worker *worker,
3736                                                                unsigned int *index)
3737 {
3738     proxy_worker_shared *shm;
3739     unsigned int i, limit;
3740     limit = storage->num_slots(slot);
3741     for (i = 0; i < limit; i++) {
3742         if (storage->dptr(slot, i, (void *)&shm) != APR_SUCCESS) {
3743             return NULL;
3744         }
3745         if ((worker->s->hash.def == shm->hash.def) &&
3746             (worker->s->hash.fnv == shm->hash.fnv)) {
3747             *index = i;
3748             return shm;
3749         }
3750     }
3751     return NULL;
3752 }
3753 
ap_proxy_find_balancershm(ap_slotmem_provider_t * storage,ap_slotmem_instance_t * slot,proxy_balancer * balancer,unsigned int * index)3754 PROXY_DECLARE(proxy_balancer_shared *) ap_proxy_find_balancershm(ap_slotmem_provider_t *storage,
3755                                                                  ap_slotmem_instance_t *slot,
3756                                                                  proxy_balancer *balancer,
3757                                                                  unsigned int *index)
3758 {
3759     proxy_balancer_shared *shm;
3760     unsigned int i, limit;
3761     limit = storage->num_slots(slot);
3762     for (i = 0; i < limit; i++) {
3763         if (storage->dptr(slot, i, (void *)&shm) != APR_SUCCESS) {
3764             return NULL;
3765         }
3766         if ((balancer->s->hash.def == shm->hash.def) &&
3767             (balancer->s->hash.fnv == shm->hash.fnv)) {
3768             *index = i;
3769             return shm;
3770         }
3771     }
3772     return NULL;
3773 }
3774 
3775 typedef struct header_connection {
3776     apr_pool_t *pool;
3777     apr_array_header_t *array;
3778     const char *first;
3779     unsigned int closed:1;
3780 } header_connection;
3781 
find_conn_headers(void * data,const char * key,const char * val)3782 static int find_conn_headers(void *data, const char *key, const char *val)
3783 {
3784     header_connection *x = data;
3785     const char *name;
3786 
3787     do {
3788         while (*val == ',' || *val == ';') {
3789             val++;
3790         }
3791         name = ap_get_token(x->pool, &val, 0);
3792         if (!strcasecmp(name, "close")) {
3793             x->closed = 1;
3794         }
3795         if (!x->first) {
3796             x->first = name;
3797         }
3798         else {
3799             const char **elt;
3800             if (!x->array) {
3801                 x->array = apr_array_make(x->pool, 4, sizeof(char *));
3802             }
3803             elt = apr_array_push(x->array);
3804             *elt = name;
3805         }
3806     } while (*val);
3807 
3808     return 1;
3809 }
3810 
3811 /**
3812  * Remove all headers referred to by the Connection header.
3813  */
ap_proxy_clear_connection(request_rec * r,apr_table_t * headers)3814 static int ap_proxy_clear_connection(request_rec *r, apr_table_t *headers)
3815 {
3816     const char **name;
3817     header_connection x;
3818 
3819     x.pool = r->pool;
3820     x.array = NULL;
3821     x.first = NULL;
3822     x.closed = 0;
3823 
3824     apr_table_unset(headers, "Proxy-Connection");
3825 
3826     apr_table_do(find_conn_headers, &x, headers, "Connection", NULL);
3827     if (x.first) {
3828         /* fast path - no memory allocated for one header */
3829         apr_table_unset(headers, "Connection");
3830         apr_table_unset(headers, x.first);
3831     }
3832     if (x.array) {
3833         /* two or more headers */
3834         while ((name = apr_array_pop(x.array))) {
3835             apr_table_unset(headers, *name);
3836         }
3837     }
3838 
3839     return x.closed;
3840 }
3841 
ap_proxy_create_hdrbrgd(apr_pool_t * p,apr_bucket_brigade * header_brigade,request_rec * r,proxy_conn_rec * p_conn,proxy_worker * worker,proxy_server_conf * conf,apr_uri_t * uri,char * url,char * server_portstr,char ** old_cl_val,char ** old_te_val)3842 PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p,
3843                                             apr_bucket_brigade *header_brigade,
3844                                             request_rec *r,
3845                                             proxy_conn_rec *p_conn,
3846                                             proxy_worker *worker,
3847                                             proxy_server_conf *conf,
3848                                             apr_uri_t *uri,
3849                                             char *url, char *server_portstr,
3850                                             char **old_cl_val,
3851                                             char **old_te_val)
3852 {
3853     conn_rec *c = r->connection;
3854     int counter;
3855     char *buf;
3856     const apr_array_header_t *headers_in_array;
3857     const apr_table_entry_t *headers_in;
3858     apr_table_t *saved_headers_in;
3859     apr_bucket *e;
3860     int do_100_continue;
3861     conn_rec *origin = p_conn->connection;
3862     proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
3863 
3864     /*
3865      * HTTP "Ping" test? Easiest is 100-Continue. However:
3866      * To be compliant, we only use 100-Continue for requests with bodies.
3867      * We also make sure we won't be talking HTTP/1.0 as well.
3868      */
3869     do_100_continue = PROXY_DO_100_CONTINUE(worker, r);
3870 
3871     if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) {
3872         /*
3873          * According to RFC 2616 8.2.3 we are not allowed to forward an
3874          * Expect: 100-continue to an HTTP/1.0 server. Instead we MUST return
3875          * a HTTP_EXPECTATION_FAILED
3876          */
3877         if (r->expecting_100) {
3878             return HTTP_EXPECTATION_FAILED;
3879         }
3880         buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.0" CRLF, NULL);
3881         p_conn->close = 1;
3882     } else {
3883         buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL);
3884     }
3885     if (apr_table_get(r->subprocess_env, "proxy-nokeepalive")) {
3886         if (origin) {
3887             origin->keepalive = AP_CONN_CLOSE;
3888         }
3889         p_conn->close = 1;
3890     }
3891     ap_xlate_proto_to_ascii(buf, strlen(buf));
3892     e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
3893     APR_BRIGADE_INSERT_TAIL(header_brigade, e);
3894     if (dconf->preserve_host == 0) {
3895         if (ap_strchr_c(uri->hostname, ':')) { /* if literal IPv6 address */
3896             if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
3897                 buf = apr_pstrcat(p, "Host: [", uri->hostname, "]:",
3898                                   uri->port_str, CRLF, NULL);
3899             } else {
3900                 buf = apr_pstrcat(p, "Host: [", uri->hostname, "]", CRLF, NULL);
3901             }
3902         } else {
3903             if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
3904                 buf = apr_pstrcat(p, "Host: ", uri->hostname, ":",
3905                                   uri->port_str, CRLF, NULL);
3906             } else {
3907                 buf = apr_pstrcat(p, "Host: ", uri->hostname, CRLF, NULL);
3908             }
3909         }
3910     }
3911     else {
3912         /* don't want to use r->hostname, as the incoming header might have a
3913          * port attached
3914          */
3915         const char* hostname = apr_table_get(r->headers_in,"Host");
3916         if (!hostname) {
3917             hostname =  r->server->server_hostname;
3918             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01092)
3919                           "no HTTP 0.9 request (with no host line) "
3920                           "on incoming request and preserve host set "
3921                           "forcing hostname to be %s for uri %s",
3922                           hostname, r->uri);
3923         }
3924         buf = apr_pstrcat(p, "Host: ", hostname, CRLF, NULL);
3925     }
3926     ap_xlate_proto_to_ascii(buf, strlen(buf));
3927     e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
3928     APR_BRIGADE_INSERT_TAIL(header_brigade, e);
3929 
3930     /*
3931      * Save the original headers in here and restore them when leaving, since
3932      * we will apply proxy purpose only modifications (eg. clearing hop-by-hop
3933      * headers, add Via or X-Forwarded-* or Expect...), whereas the originals
3934      * will be needed later to prepare the correct response and logging.
3935      *
3936      * Note: We need to take r->pool for apr_table_copy as the key / value
3937      * pairs in r->headers_in have been created out of r->pool and
3938      * p might be (and actually is) a longer living pool.
3939      * This would trigger the bad pool ancestry abort in apr_table_copy if
3940      * apr is compiled with APR_POOL_DEBUG.
3941      */
3942     saved_headers_in = r->headers_in;
3943     r->headers_in = apr_table_copy(r->pool, saved_headers_in);
3944 
3945     /* handle Via */
3946     if (conf->viaopt == via_block) {
3947         /* Block all outgoing Via: headers */
3948         apr_table_unset(r->headers_in, "Via");
3949     } else if (conf->viaopt != via_off) {
3950         const char *server_name = ap_get_server_name(r);
3951         /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host,
3952          * then the server name returned by ap_get_server_name() is the
3953          * origin server name (which does make too much sense with Via: headers)
3954          * so we use the proxy vhost's name instead.
3955          */
3956         if (server_name == r->hostname)
3957             server_name = r->server->server_hostname;
3958         /* Create a "Via:" request header entry and merge it */
3959         /* Generate outgoing Via: header with/without server comment: */
3960         apr_table_mergen(r->headers_in, "Via",
3961                          (conf->viaopt == via_full)
3962                          ? apr_psprintf(p, "%d.%d %s%s (%s)",
3963                                         HTTP_VERSION_MAJOR(r->proto_num),
3964                                         HTTP_VERSION_MINOR(r->proto_num),
3965                                         server_name, server_portstr,
3966                                         AP_SERVER_BASEVERSION)
3967                          : apr_psprintf(p, "%d.%d %s%s",
3968                                         HTTP_VERSION_MAJOR(r->proto_num),
3969                                         HTTP_VERSION_MINOR(r->proto_num),
3970                                         server_name, server_portstr)
3971                          );
3972     }
3973 
3974     /* Use HTTP/1.1 100-Continue as quick "HTTP ping" test
3975      * to backend
3976      */
3977     if (do_100_continue) {
3978         const char *val;
3979 
3980         /* Add the Expect header if not already there. */
3981         if (((val = apr_table_get(r->headers_in, "Expect")) == NULL)
3982                 || (ap_cstr_casecmp(val, "100-Continue") != 0 /* fast path */
3983                     && !ap_find_token(r->pool, val, "100-Continue"))) {
3984             apr_table_mergen(r->headers_in, "Expect", "100-Continue");
3985         }
3986     }
3987 
3988     /* X-Forwarded-*: handling
3989      *
3990      * XXX Privacy Note:
3991      * -----------------
3992      *
3993      * These request headers are only really useful when the mod_proxy
3994      * is used in a reverse proxy configuration, so that useful info
3995      * about the client can be passed through the reverse proxy and on
3996      * to the backend server, which may require the information to
3997      * function properly.
3998      *
3999      * In a forward proxy situation, these options are a potential
4000      * privacy violation, as information about clients behind the proxy
4001      * are revealed to arbitrary servers out there on the internet.
4002      *
4003      * The HTTP/1.1 Via: header is designed for passing client
4004      * information through proxies to a server, and should be used in
4005      * a forward proxy configuration instead of X-Forwarded-*. See the
4006      * ProxyVia option for details.
4007      */
4008     if (dconf->add_forwarded_headers) {
4009         if (PROXYREQ_REVERSE == r->proxyreq) {
4010             const char *buf;
4011 
4012             /* Add X-Forwarded-For: so that the upstream has a chance to
4013              * determine, where the original request came from.
4014              */
4015             apr_table_mergen(r->headers_in, "X-Forwarded-For",
4016                              r->useragent_ip);
4017 
4018             /* Add X-Forwarded-Host: so that upstream knows what the
4019              * original request hostname was.
4020              */
4021             if ((buf = apr_table_get(r->headers_in, "Host"))) {
4022                 apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf);
4023             }
4024 
4025             /* Add X-Forwarded-Server: so that upstream knows what the
4026              * name of this proxy server is (if there are more than one)
4027              * XXX: This duplicates Via: - do we strictly need it?
4028              */
4029             apr_table_mergen(r->headers_in, "X-Forwarded-Server",
4030                              r->server->server_hostname);
4031         }
4032     }
4033 
4034     proxy_run_fixups(r);
4035     if (ap_proxy_clear_connection(r, r->headers_in) < 0) {
4036         return HTTP_BAD_REQUEST;
4037     }
4038 
4039     /* send request headers */
4040     headers_in_array = apr_table_elts(r->headers_in);
4041     headers_in = (const apr_table_entry_t *) headers_in_array->elts;
4042     for (counter = 0; counter < headers_in_array->nelts; counter++) {
4043         if (headers_in[counter].key == NULL
4044             || headers_in[counter].val == NULL
4045 
4046             /* Already sent */
4047             || !ap_cstr_casecmp(headers_in[counter].key, "Host")
4048 
4049             /* Clear out hop-by-hop request headers not to send
4050              * RFC2616 13.5.1 says we should strip these headers
4051              */
4052             || !ap_cstr_casecmp(headers_in[counter].key, "Keep-Alive")
4053             || !ap_cstr_casecmp(headers_in[counter].key, "TE")
4054             || !ap_cstr_casecmp(headers_in[counter].key, "Trailer")
4055             || !ap_cstr_casecmp(headers_in[counter].key, "Upgrade")
4056 
4057             ) {
4058             continue;
4059         }
4060         /* Do we want to strip Proxy-Authorization ?
4061          * If we haven't used it, then NO
4062          * If we have used it then MAYBE: RFC2616 says we MAY propagate it.
4063          * So let's make it configurable by env.
4064          */
4065         if (!ap_cstr_casecmp(headers_in[counter].key,"Proxy-Authorization")) {
4066             if (r->user != NULL) { /* we've authenticated */
4067                 if (!apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) {
4068                     continue;
4069                 }
4070             }
4071         }
4072 
4073         /* Skip Transfer-Encoding and Content-Length for now.
4074          */
4075         if (!ap_cstr_casecmp(headers_in[counter].key, "Transfer-Encoding")) {
4076             *old_te_val = headers_in[counter].val;
4077             continue;
4078         }
4079         if (!ap_cstr_casecmp(headers_in[counter].key, "Content-Length")) {
4080             *old_cl_val = headers_in[counter].val;
4081             continue;
4082         }
4083 
4084         /* for sub-requests, ignore freshness/expiry headers */
4085         if (r->main) {
4086             if (   !ap_cstr_casecmp(headers_in[counter].key, "If-Match")
4087                 || !ap_cstr_casecmp(headers_in[counter].key, "If-Modified-Since")
4088                 || !ap_cstr_casecmp(headers_in[counter].key, "If-Range")
4089                 || !ap_cstr_casecmp(headers_in[counter].key, "If-Unmodified-Since")
4090                 || !ap_cstr_casecmp(headers_in[counter].key, "If-None-Match")) {
4091                 continue;
4092             }
4093         }
4094 
4095         buf = apr_pstrcat(p, headers_in[counter].key, ": ",
4096                           headers_in[counter].val, CRLF,
4097                           NULL);
4098         ap_xlate_proto_to_ascii(buf, strlen(buf));
4099         e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
4100         APR_BRIGADE_INSERT_TAIL(header_brigade, e);
4101     }
4102 
4103     /* Restore the original headers in (see comment above),
4104      * we won't modify them anymore.
4105      */
4106     r->headers_in = saved_headers_in;
4107     return OK;
4108 }
4109 
ap_proxy_prefetch_input(request_rec * r,proxy_conn_rec * backend,apr_bucket_brigade * input_brigade,apr_read_type_e block,apr_off_t * bytes_read,apr_off_t max_read)4110 PROXY_DECLARE(int) ap_proxy_prefetch_input(request_rec *r,
4111                                            proxy_conn_rec *backend,
4112                                            apr_bucket_brigade *input_brigade,
4113                                            apr_read_type_e block,
4114                                            apr_off_t *bytes_read,
4115                                            apr_off_t max_read)
4116 {
4117     apr_pool_t *p = r->pool;
4118     conn_rec *c = r->connection;
4119     apr_bucket_brigade *temp_brigade;
4120     apr_status_t status;
4121     apr_off_t bytes;
4122 
4123     *bytes_read = 0;
4124     if (max_read < APR_BUCKET_BUFF_SIZE) {
4125         max_read = APR_BUCKET_BUFF_SIZE;
4126     }
4127 
4128     /* Prefetch max_read bytes
4129      *
4130      * This helps us avoid any election of C-L v.s. T-E
4131      * request bodies, since we are willing to keep in
4132      * memory this much data, in any case.  This gives
4133      * us an instant C-L election if the body is of some
4134      * reasonable size.
4135      */
4136     temp_brigade = apr_brigade_create(p, input_brigade->bucket_alloc);
4137 
4138     /* Account for saved input, if any. */
4139     apr_brigade_length(input_brigade, 0, bytes_read);
4140 
4141     /* Ensure we don't hit a wall where we have a buffer too small for
4142      * ap_get_brigade's filters to fetch us another bucket, surrender
4143      * once we hit 80 bytes (an arbitrary value) less than max_read.
4144      */
4145     while (*bytes_read < max_read - 80
4146            && (APR_BRIGADE_EMPTY(input_brigade)
4147                || !APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade)))) {
4148         status = ap_get_brigade(r->input_filters, temp_brigade,
4149                                 AP_MODE_READBYTES, block,
4150                                 max_read - *bytes_read);
4151         /* ap_get_brigade may return success with an empty brigade
4152          * for a non-blocking read which would block
4153          */
4154         if (block == APR_NONBLOCK_READ
4155                 && ((status == APR_SUCCESS && APR_BRIGADE_EMPTY(temp_brigade))
4156                     || APR_STATUS_IS_EAGAIN(status))) {
4157             break;
4158         }
4159         if (status != APR_SUCCESS) {
4160             ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01095)
4161                           "prefetch request body failed to %pI (%s)"
4162                           " from %s (%s)", backend->addr,
4163                           backend->hostname ? backend->hostname : "",
4164                           c->client_ip, c->remote_host ? c->remote_host : "");
4165             return ap_map_http_request_error(status, HTTP_BAD_REQUEST);
4166         }
4167 
4168         apr_brigade_length(temp_brigade, 1, &bytes);
4169         *bytes_read += bytes;
4170 
4171         /*
4172          * Save temp_brigade in input_brigade. (At least) in the SSL case
4173          * temp_brigade contains transient buckets whose data would get
4174          * overwritten during the next call of ap_get_brigade in the loop.
4175          * ap_save_brigade ensures these buckets to be set aside.
4176          * Calling ap_save_brigade with NULL as filter is OK, because
4177          * input_brigade already has been created and does not need to get
4178          * created by ap_save_brigade.
4179          */
4180         status = ap_save_brigade(NULL, &input_brigade, &temp_brigade, p);
4181         if (status != APR_SUCCESS) {
4182             ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01096)
4183                           "processing prefetched request body failed"
4184                           " to %pI (%s) from %s (%s)", backend->addr,
4185                           backend->hostname ? backend->hostname : "",
4186                           c->client_ip, c->remote_host ? c->remote_host : "");
4187             return HTTP_INTERNAL_SERVER_ERROR;
4188         }
4189     }
4190 
4191     return OK;
4192 }
4193 
ap_proxy_read_input(request_rec * r,proxy_conn_rec * backend,apr_bucket_brigade * bb,apr_off_t max_read)4194 PROXY_DECLARE(int) ap_proxy_read_input(request_rec *r,
4195                                        proxy_conn_rec *backend,
4196                                        apr_bucket_brigade *bb,
4197                                        apr_off_t max_read)
4198 {
4199     apr_bucket_alloc_t *bucket_alloc = bb->bucket_alloc;
4200     apr_read_type_e block = (backend->connection) ? APR_NONBLOCK_READ
4201                                                   : APR_BLOCK_READ;
4202     apr_status_t status;
4203     int rv;
4204 
4205     for (;;) {
4206         apr_brigade_cleanup(bb);
4207         status = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
4208                                 block, max_read);
4209         if (block == APR_BLOCK_READ
4210                 || (!(status == APR_SUCCESS && APR_BRIGADE_EMPTY(bb))
4211                     && !APR_STATUS_IS_EAGAIN(status))) {
4212             break;
4213         }
4214 
4215         /* Flush and retry (blocking) */
4216         apr_brigade_cleanup(bb);
4217         rv = ap_proxy_pass_brigade(bucket_alloc, r, backend,
4218                                    backend->connection, bb, 1);
4219         if (rv != OK) {
4220             return rv;
4221         }
4222         block = APR_BLOCK_READ;
4223     }
4224 
4225     if (status != APR_SUCCESS) {
4226         conn_rec *c = r->connection;
4227         ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02608)
4228                       "read request body failed to %pI (%s)"
4229                       " from %s (%s)", backend->addr,
4230                       backend->hostname ? backend->hostname : "",
4231                       c->client_ip, c->remote_host ? c->remote_host : "");
4232         return ap_map_http_request_error(status, HTTP_BAD_REQUEST);
4233     }
4234 
4235     return OK;
4236 }
4237 
ap_proxy_spool_input(request_rec * r,proxy_conn_rec * backend,apr_bucket_brigade * input_brigade,apr_off_t * bytes_spooled,apr_off_t max_mem_spool)4238 PROXY_DECLARE(int) ap_proxy_spool_input(request_rec *r,
4239                                         proxy_conn_rec *backend,
4240                                         apr_bucket_brigade *input_brigade,
4241                                         apr_off_t *bytes_spooled,
4242                                         apr_off_t max_mem_spool)
4243 {
4244     apr_pool_t *p = r->pool;
4245     int seen_eos = 0, rv = OK;
4246     apr_status_t status = APR_SUCCESS;
4247     apr_bucket_alloc_t *bucket_alloc = input_brigade->bucket_alloc;
4248     apr_bucket_brigade *body_brigade;
4249     apr_bucket *e;
4250     apr_off_t bytes, fsize = 0;
4251     apr_file_t *tmpfile = NULL;
4252     apr_off_t limit;
4253 
4254     *bytes_spooled = 0;
4255     body_brigade = apr_brigade_create(p, bucket_alloc);
4256 
4257     limit = ap_get_limit_req_body(r);
4258 
4259     do {
4260         if (APR_BRIGADE_EMPTY(input_brigade)) {
4261             rv = ap_proxy_read_input(r, backend, input_brigade,
4262                                      HUGE_STRING_LEN);
4263             if (rv != OK) {
4264                 return rv;
4265             }
4266         }
4267 
4268         /* If this brigade contains EOS, either stop or remove it. */
4269         if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
4270             seen_eos = 1;
4271         }
4272 
4273         apr_brigade_length(input_brigade, 1, &bytes);
4274 
4275         if (*bytes_spooled + bytes > max_mem_spool) {
4276             /*
4277              * LimitRequestBody does not affect Proxy requests (Should it?).
4278              * Let it take effect if we decide to store the body in a
4279              * temporary file on disk.
4280              */
4281             if (limit && (*bytes_spooled + bytes > limit)) {
4282                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01088)
4283                               "Request body is larger than the configured "
4284                               "limit of %" APR_OFF_T_FMT, limit);
4285                 return HTTP_REQUEST_ENTITY_TOO_LARGE;
4286             }
4287             /* can't spool any more in memory; write latest brigade to disk */
4288             if (tmpfile == NULL) {
4289                 const char *temp_dir;
4290                 char *template;
4291 
4292                 status = apr_temp_dir_get(&temp_dir, p);
4293                 if (status != APR_SUCCESS) {
4294                     ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01089)
4295                                   "search for temporary directory failed");
4296                     return HTTP_INTERNAL_SERVER_ERROR;
4297                 }
4298                 apr_filepath_merge(&template, temp_dir,
4299                                    "modproxy.tmp.XXXXXX",
4300                                    APR_FILEPATH_NATIVE, p);
4301                 status = apr_file_mktemp(&tmpfile, template, 0, p);
4302                 if (status != APR_SUCCESS) {
4303                     ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01090)
4304                                   "creation of temporary file in directory "
4305                                   "%s failed", temp_dir);
4306                     return HTTP_INTERNAL_SERVER_ERROR;
4307                 }
4308             }
4309             for (e = APR_BRIGADE_FIRST(input_brigade);
4310                  e != APR_BRIGADE_SENTINEL(input_brigade);
4311                  e = APR_BUCKET_NEXT(e)) {
4312                 const char *data;
4313                 apr_size_t bytes_read, bytes_written;
4314 
4315                 apr_bucket_read(e, &data, &bytes_read, APR_BLOCK_READ);
4316                 status = apr_file_write_full(tmpfile, data, bytes_read, &bytes_written);
4317                 if (status != APR_SUCCESS) {
4318                     const char *tmpfile_name;
4319 
4320                     if (apr_file_name_get(&tmpfile_name, tmpfile) != APR_SUCCESS) {
4321                         tmpfile_name = "(unknown)";
4322                     }
4323                     ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01091)
4324                                   "write to temporary file %s failed",
4325                                   tmpfile_name);
4326                     return HTTP_INTERNAL_SERVER_ERROR;
4327                 }
4328                 AP_DEBUG_ASSERT(bytes_read == bytes_written);
4329                 fsize += bytes_written;
4330             }
4331             apr_brigade_cleanup(input_brigade);
4332         }
4333         else {
4334 
4335             /*
4336              * Save input_brigade in body_brigade. (At least) in the SSL case
4337              * input_brigade contains transient buckets whose data would get
4338              * overwritten during the next call of ap_get_brigade in the loop.
4339              * ap_save_brigade ensures these buckets to be set aside.
4340              * Calling ap_save_brigade with NULL as filter is OK, because
4341              * body_brigade already has been created and does not need to get
4342              * created by ap_save_brigade.
4343              */
4344             status = ap_save_brigade(NULL, &body_brigade, &input_brigade, p);
4345             if (status != APR_SUCCESS) {
4346                 return HTTP_INTERNAL_SERVER_ERROR;
4347             }
4348 
4349         }
4350 
4351         *bytes_spooled += bytes;
4352     } while (!seen_eos);
4353 
4354     APR_BRIGADE_CONCAT(input_brigade, body_brigade);
4355     if (tmpfile) {
4356         apr_brigade_insert_file(input_brigade, tmpfile, 0, fsize, p);
4357     }
4358     if (apr_table_get(r->subprocess_env, "proxy-sendextracrlf")) {
4359         e = apr_bucket_immortal_create(CRLF_ASCII, 2, bucket_alloc);
4360         APR_BRIGADE_INSERT_TAIL(input_brigade, e);
4361     }
4362     if (tmpfile) {
4363         /* We dropped metadata buckets when spooling to tmpfile,
4364          * terminate with EOS to allow for flushing in a one go.
4365          */
4366         e = apr_bucket_eos_create(bucket_alloc);
4367         APR_BRIGADE_INSERT_TAIL(input_brigade, e);
4368     }
4369     return OK;
4370 }
4371 
ap_proxy_pass_brigade(apr_bucket_alloc_t * bucket_alloc,request_rec * r,proxy_conn_rec * p_conn,conn_rec * origin,apr_bucket_brigade * bb,int flush)4372 PROXY_DECLARE(int) ap_proxy_pass_brigade(apr_bucket_alloc_t *bucket_alloc,
4373                                          request_rec *r, proxy_conn_rec *p_conn,
4374                                          conn_rec *origin, apr_bucket_brigade *bb,
4375                                          int flush)
4376 {
4377     apr_status_t status;
4378     apr_off_t transferred;
4379 
4380     if (flush) {
4381         apr_bucket *e = apr_bucket_flush_create(bucket_alloc);
4382         APR_BRIGADE_INSERT_TAIL(bb, e);
4383     }
4384     apr_brigade_length(bb, 0, &transferred);
4385     if (transferred != -1)
4386         p_conn->worker->s->transferred += transferred;
4387     status = ap_pass_brigade(origin->output_filters, bb);
4388     /* Cleanup the brigade now to avoid buckets lifetime
4389      * issues in case of error returned below. */
4390     apr_brigade_cleanup(bb);
4391     if (status != APR_SUCCESS) {
4392         ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01084)
4393                       "pass request body failed to %pI (%s)",
4394                       p_conn->addr, p_conn->hostname);
4395         if (origin->aborted) {
4396             const char *ssl_note;
4397 
4398             if (((ssl_note = apr_table_get(origin->notes, "SSL_connect_rv"))
4399                  != NULL) && (strcmp(ssl_note, "err") == 0)) {
4400                 return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR,
4401                                      "Error during SSL Handshake with"
4402                                      " remote server");
4403             }
4404             return APR_STATUS_IS_TIMEUP(status) ? HTTP_GATEWAY_TIME_OUT : HTTP_BAD_GATEWAY;
4405         }
4406         else {
4407             return HTTP_BAD_REQUEST;
4408         }
4409     }
4410     return OK;
4411 }
4412 
4413 /* Fill in unknown schemes from apr_uri_port_of_scheme() */
4414 
4415 typedef struct proxy_schemes_t {
4416     const char *name;
4417     apr_port_t default_port;
4418 } proxy_schemes_t ;
4419 
4420 static proxy_schemes_t pschemes[] =
4421 {
4422     {"fcgi",     8000},
4423     {"ajp",      AJP13_DEF_PORT},
4424     {"scgi",     SCGI_DEF_PORT},
4425     {"h2c",      DEFAULT_HTTP_PORT},
4426     {"h2",       DEFAULT_HTTPS_PORT},
4427     {"ws",       DEFAULT_HTTP_PORT},
4428     {"wss",      DEFAULT_HTTPS_PORT},
4429     { NULL, 0xFFFF }     /* unknown port */
4430 };
4431 
ap_proxy_port_of_scheme(const char * scheme)4432 PROXY_DECLARE(apr_port_t) ap_proxy_port_of_scheme(const char *scheme)
4433 {
4434     if (scheme) {
4435         apr_port_t port;
4436         if ((port = apr_uri_port_of_scheme(scheme)) != 0) {
4437             return port;
4438         } else {
4439             proxy_schemes_t *pscheme;
4440             for (pscheme = pschemes; pscheme->name != NULL; ++pscheme) {
4441                 if (ap_cstr_casecmp(scheme, pscheme->name) == 0) {
4442                     return pscheme->default_port;
4443                 }
4444             }
4445         }
4446     }
4447     return 0;
4448 }
4449 
ap_filter_should_yield(ap_filter_t * f)4450 static APR_INLINE int ap_filter_should_yield(ap_filter_t *f)
4451 {
4452     return f->c->data_in_output_filters;
4453 }
4454 
ap_filter_output_pending(conn_rec * c)4455 static APR_INLINE int ap_filter_output_pending(conn_rec *c)
4456 {
4457     ap_filter_t *f = c->output_filters;
4458     while (f->next) {
4459         f = f->next;
4460     }
4461     if (f->frec->filter_func.out_func(f, NULL)) {
4462         return AP_FILTER_ERROR;
4463     }
4464     return c->data_in_output_filters ? OK : DECLINED;
4465 }
4466 
ap_proxy_buckets_lifetime_transform(request_rec * r,apr_bucket_brigade * from,apr_bucket_brigade * to)4467 PROXY_DECLARE(apr_status_t) ap_proxy_buckets_lifetime_transform(request_rec *r,
4468                                                       apr_bucket_brigade *from,
4469                                                       apr_bucket_brigade *to)
4470 {
4471     apr_bucket *e;
4472     apr_bucket *new;
4473     const char *data;
4474     apr_size_t bytes;
4475     apr_status_t rv = APR_SUCCESS;
4476     apr_bucket_alloc_t *bucket_alloc = to->bucket_alloc;
4477 
4478     apr_brigade_cleanup(to);
4479     for (e = APR_BRIGADE_FIRST(from);
4480          e != APR_BRIGADE_SENTINEL(from);
4481          e = APR_BUCKET_NEXT(e)) {
4482         if (!APR_BUCKET_IS_METADATA(e)) {
4483             apr_bucket_read(e, &data, &bytes, APR_BLOCK_READ);
4484             new = apr_bucket_transient_create(data, bytes, bucket_alloc);
4485             APR_BRIGADE_INSERT_TAIL(to, new);
4486         }
4487         else if (APR_BUCKET_IS_FLUSH(e)) {
4488             new = apr_bucket_flush_create(bucket_alloc);
4489             APR_BRIGADE_INSERT_TAIL(to, new);
4490         }
4491         else if (APR_BUCKET_IS_EOS(e)) {
4492             new = apr_bucket_eos_create(bucket_alloc);
4493             APR_BRIGADE_INSERT_TAIL(to, new);
4494         }
4495         else {
4496             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(03304)
4497                           "Unhandled bucket type of type %s in"
4498                           " ap_proxy_buckets_lifetime_transform", e->type->name);
4499             rv = APR_EGENERAL;
4500         }
4501     }
4502     return rv;
4503 }
4504 
4505 /* An arbitrary large value to address pathological case where we keep
4506  * reading from one side only, without scheduling the other direction for
4507  * too long. This can happen with large MTU and small read buffers, like
4508  * micro-benchmarking huge files bidirectional transfer with client, proxy
4509  * and backend on localhost for instance. Though we could just ignore the
4510  * case and let the sender stop by itself at some point when/if it needs to
4511  * receive data, or the receiver stop when/if it needs to send...
4512  */
4513 #define PROXY_TRANSFER_MAX_READS 10000
4514 
ap_proxy_transfer_between_connections(request_rec * r,conn_rec * c_i,conn_rec * c_o,apr_bucket_brigade * bb_i,apr_bucket_brigade * bb_o,const char * name,int * sent,apr_off_t bsize,int flags)4515 PROXY_DECLARE(apr_status_t) ap_proxy_transfer_between_connections(
4516                                                        request_rec *r,
4517                                                        conn_rec *c_i,
4518                                                        conn_rec *c_o,
4519                                                        apr_bucket_brigade *bb_i,
4520                                                        apr_bucket_brigade *bb_o,
4521                                                        const char *name,
4522                                                        int *sent,
4523                                                        apr_off_t bsize,
4524                                                        int flags)
4525 {
4526     apr_status_t rv;
4527     int flush_each = 0;
4528     unsigned int num_reads = 0;
4529 #ifdef DEBUGGING
4530     apr_off_t len;
4531 #endif
4532 
4533     /*
4534      * Compat: since FLUSH_EACH is default (and zero) for legacy reasons, we
4535      * pretend it's no FLUSH_AFTER nor YIELD_PENDING flags, the latter because
4536      * flushing would defeat the purpose of checking for pending data (hence
4537      * determine whether or not the output chain/stack is full for stopping).
4538      */
4539     if (!(flags & (AP_PROXY_TRANSFER_FLUSH_AFTER |
4540                    AP_PROXY_TRANSFER_YIELD_PENDING))) {
4541         flush_each = 1;
4542     }
4543 
4544     for (;;) {
4545         apr_brigade_cleanup(bb_i);
4546         rv = ap_get_brigade(c_i->input_filters, bb_i, AP_MODE_READBYTES,
4547                             APR_NONBLOCK_READ, bsize);
4548         if (rv != APR_SUCCESS) {
4549             if (!APR_STATUS_IS_EAGAIN(rv) && !APR_STATUS_IS_EOF(rv)) {
4550                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(03308)
4551                               "ap_proxy_transfer_between_connections: "
4552                               "error on %s - ap_get_brigade",
4553                               name);
4554                 if (rv == APR_INCOMPLETE) {
4555                     /* Don't return APR_INCOMPLETE, it'd mean "should yield"
4556                      * for the caller, while it means "incomplete body" here
4557                      * from ap_http_filter(), which is an error.
4558                      */
4559                     rv = APR_EGENERAL;
4560                 }
4561             }
4562             break;
4563         }
4564 
4565         if (c_o->aborted) {
4566             apr_brigade_cleanup(bb_i);
4567             flags &= ~AP_PROXY_TRANSFER_FLUSH_AFTER;
4568             rv = APR_EPIPE;
4569             break;
4570         }
4571         if (APR_BRIGADE_EMPTY(bb_i)) {
4572             break;
4573         }
4574 #ifdef DEBUGGING
4575         len = -1;
4576         apr_brigade_length(bb_i, 0, &len);
4577         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03306)
4578                       "ap_proxy_transfer_between_connections: "
4579                       "read %" APR_OFF_T_FMT
4580                       " bytes from %s", len, name);
4581 #endif
4582         if (sent) {
4583             *sent = 1;
4584         }
4585         ap_proxy_buckets_lifetime_transform(r, bb_i, bb_o);
4586         if (flush_each) {
4587             apr_bucket *b;
4588             /*
4589              * Do not use ap_fflush here since this would cause the flush
4590              * bucket to be sent in a separate brigade afterwards which
4591              * causes some filters to set aside the buckets from the first
4592              * brigade and process them when FLUSH arrives in the second
4593              * brigade. As set asides of our transformed buckets involve
4594              * memory copying we try to avoid this. If we have the flush
4595              * bucket in the first brigade they directly process the
4596              * buckets without setting them aside.
4597              */
4598             b = apr_bucket_flush_create(bb_o->bucket_alloc);
4599             APR_BRIGADE_INSERT_TAIL(bb_o, b);
4600         }
4601         rv = ap_pass_brigade(c_o->output_filters, bb_o);
4602         apr_brigade_cleanup(bb_o);
4603         if (rv != APR_SUCCESS) {
4604             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(03307)
4605                           "ap_proxy_transfer_between_connections: "
4606                           "error on %s - ap_pass_brigade",
4607                           name);
4608             flags &= ~AP_PROXY_TRANSFER_FLUSH_AFTER;
4609             break;
4610         }
4611 
4612         /* Yield if the output filters stack is full? This is to avoid
4613          * blocking and give the caller a chance to POLLOUT async.
4614          */
4615         if ((flags & AP_PROXY_TRANSFER_YIELD_PENDING)
4616                 && ap_filter_should_yield(c_o->output_filters)) {
4617             int rc = ap_filter_output_pending(c_o);
4618             if (rc == OK) {
4619                 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
4620                               "ap_proxy_transfer_between_connections: "
4621                               "yield (output pending)");
4622                 rv = APR_INCOMPLETE;
4623                 break;
4624             }
4625             if (rc != DECLINED) {
4626                 rv = AP_FILTER_ERROR;
4627                 break;
4628             }
4629         }
4630 
4631         /* Yield if we keep hold of the thread for too long? This gives
4632          * the caller a chance to schedule the other direction too.
4633          */
4634         if ((flags & AP_PROXY_TRANSFER_YIELD_MAX_READS)
4635                 && ++num_reads > PROXY_TRANSFER_MAX_READS) {
4636             ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
4637                           "ap_proxy_transfer_between_connections: "
4638                           "yield (max reads)");
4639             rv = APR_SUCCESS;
4640             break;
4641         }
4642     }
4643 
4644     if (flags & AP_PROXY_TRANSFER_FLUSH_AFTER) {
4645         ap_fflush(c_o->output_filters, bb_o);
4646         apr_brigade_cleanup(bb_o);
4647     }
4648     apr_brigade_cleanup(bb_i);
4649 
4650     ap_log_rerror(APLOG_MARK, APLOG_TRACE2, rv, r,
4651                   "ap_proxy_transfer_between_connections complete (%s %pI)",
4652                   (c_i == r->connection) ? "to" : "from",
4653                   (c_i == r->connection) ? c_o->client_addr
4654                                          : c_i->client_addr);
4655 
4656     if (APR_STATUS_IS_EAGAIN(rv)) {
4657         rv = APR_SUCCESS;
4658     }
4659     return rv;
4660 }
4661 
4662 struct proxy_tunnel_conn {
4663     /* the other side of the tunnel */
4664     struct proxy_tunnel_conn *other;
4665 
4666     conn_rec *c;
4667     const char *name;
4668 
4669     apr_pollfd_t *pfd;
4670     apr_bucket_brigade *bb;
4671 
4672     unsigned int down_in:1,
4673                  down_out:1;
4674 };
4675 
ap_proxy_tunnel_create(proxy_tunnel_rec ** ptunnel,request_rec * r,conn_rec * c_o,const char * scheme)4676 PROXY_DECLARE(apr_status_t) ap_proxy_tunnel_create(proxy_tunnel_rec **ptunnel,
4677                                                    request_rec *r, conn_rec *c_o,
4678                                                    const char *scheme)
4679 {
4680     apr_status_t rv;
4681     conn_rec *c_i = r->connection;
4682     apr_interval_time_t timeout = -1;
4683     proxy_tunnel_rec *tunnel;
4684 
4685     *ptunnel = NULL;
4686 
4687     tunnel = apr_pcalloc(r->pool, sizeof(*tunnel));
4688 
4689     rv = apr_pollset_create(&tunnel->pollset, 2, r->pool, APR_POLLSET_NOCOPY);
4690     if (rv != APR_SUCCESS) {
4691         return rv;
4692     }
4693 
4694     tunnel->r = r;
4695     tunnel->scheme = apr_pstrdup(r->pool, scheme);
4696     tunnel->client = apr_pcalloc(r->pool, sizeof(struct proxy_tunnel_conn));
4697     tunnel->origin = apr_pcalloc(r->pool, sizeof(struct proxy_tunnel_conn));
4698     tunnel->pfds = apr_array_make(r->pool, 2, sizeof(apr_pollfd_t));
4699     tunnel->read_buf_size = ap_get_read_buf_size(r);
4700     tunnel->client->other = tunnel->origin;
4701     tunnel->origin->other = tunnel->client;
4702     tunnel->timeout = -1;
4703 
4704     tunnel->client->c = c_i;
4705     tunnel->client->name = "client";
4706     tunnel->client->bb = apr_brigade_create(c_i->pool, c_i->bucket_alloc);
4707     tunnel->client->pfd = &APR_ARRAY_PUSH(tunnel->pfds, apr_pollfd_t);
4708     tunnel->client->pfd->p = r->pool;
4709     tunnel->client->pfd->desc_type = APR_POLL_SOCKET;
4710     tunnel->client->pfd->desc.s = ap_get_conn_socket(c_i);
4711     tunnel->client->pfd->client_data = tunnel->client;
4712 
4713     tunnel->origin->c = c_o;
4714     tunnel->origin->name = "origin";
4715     tunnel->origin->bb = apr_brigade_create(c_o->pool, c_o->bucket_alloc);
4716     tunnel->origin->pfd = &APR_ARRAY_PUSH(tunnel->pfds, apr_pollfd_t);
4717     tunnel->origin->pfd->p = r->pool;
4718     tunnel->origin->pfd->desc_type = APR_POLL_SOCKET;
4719     tunnel->origin->pfd->desc.s = ap_get_conn_socket(c_o);
4720     tunnel->origin->pfd->client_data = tunnel->origin;
4721 
4722     /* Defaults to the biggest timeout of both connections */
4723     apr_socket_timeout_get(tunnel->client->pfd->desc.s, &timeout);
4724     apr_socket_timeout_get(tunnel->origin->pfd->desc.s, &tunnel->timeout);
4725     if (timeout >= 0 && (tunnel->timeout < 0 || tunnel->timeout < timeout)) {
4726         tunnel->timeout = timeout;
4727     }
4728 
4729     /* We should be nonblocking from now on the sockets */
4730     apr_socket_opt_set(tunnel->client->pfd->desc.s, APR_SO_NONBLOCK, 1);
4731     apr_socket_opt_set(tunnel->origin->pfd->desc.s, APR_SO_NONBLOCK, 1);
4732 
4733     /* No coalescing filters */
4734     ap_remove_output_filter_byhandle(c_i->output_filters,
4735                                      "SSL/TLS Coalescing Filter");
4736     ap_remove_output_filter_byhandle(c_o->output_filters,
4737                                      "SSL/TLS Coalescing Filter");
4738 
4739     /* Bidirectional non-HTTP stream will confuse mod_reqtimeoout */
4740     ap_remove_input_filter_byhandle(c_i->input_filters, "reqtimeout");
4741 
4742     /* The input/output filter stacks should contain connection filters only */
4743     r->input_filters = r->proto_input_filters = c_i->input_filters;
4744     r->output_filters = r->proto_output_filters = c_i->output_filters;
4745 
4746     /* Won't be reused after tunneling */
4747     c_i->keepalive = AP_CONN_CLOSE;
4748     c_o->keepalive = AP_CONN_CLOSE;
4749 
4750     /* Disable half-close forwarding for this request? */
4751     if (apr_table_get(r->subprocess_env, "proxy-nohalfclose")) {
4752         tunnel->nohalfclose = 1;
4753     }
4754 
4755     /* Start with POLLOUT and let ap_proxy_tunnel_run() schedule both
4756      * directions when there are no output data pending (anymore).
4757      */
4758     tunnel->client->pfd->reqevents = APR_POLLOUT | APR_POLLERR;
4759     tunnel->origin->pfd->reqevents = APR_POLLOUT | APR_POLLERR;
4760     if ((rv = apr_pollset_add(tunnel->pollset, tunnel->client->pfd))
4761             || (rv = apr_pollset_add(tunnel->pollset, tunnel->origin->pfd))) {
4762         return rv;
4763     }
4764 
4765     *ptunnel = tunnel;
4766     return APR_SUCCESS;
4767 }
4768 
add_pollset(apr_pollset_t * pollset,apr_pollfd_t * pfd,apr_int16_t events)4769 static void add_pollset(apr_pollset_t *pollset, apr_pollfd_t *pfd,
4770                         apr_int16_t events)
4771 {
4772     apr_status_t rv;
4773 
4774     AP_DEBUG_ASSERT((pfd->reqevents & events) == 0);
4775 
4776     if (pfd->reqevents) {
4777         rv = apr_pollset_remove(pollset, pfd);
4778         if (rv != APR_SUCCESS) {
4779             AP_DEBUG_ASSERT(1);
4780         }
4781     }
4782 
4783     if (events & APR_POLLIN) {
4784         events |= APR_POLLHUP;
4785     }
4786     pfd->reqevents |= events | APR_POLLERR;
4787     rv = apr_pollset_add(pollset, pfd);
4788     if (rv != APR_SUCCESS) {
4789         AP_DEBUG_ASSERT(1);
4790     }
4791 }
4792 
del_pollset(apr_pollset_t * pollset,apr_pollfd_t * pfd,apr_int16_t events)4793 static void del_pollset(apr_pollset_t *pollset, apr_pollfd_t *pfd,
4794                         apr_int16_t events)
4795 {
4796     apr_status_t rv;
4797 
4798     AP_DEBUG_ASSERT((pfd->reqevents & events) != 0);
4799 
4800     rv = apr_pollset_remove(pollset, pfd);
4801     if (rv != APR_SUCCESS) {
4802         AP_DEBUG_ASSERT(0);
4803         return;
4804     }
4805 
4806     if (events & APR_POLLIN) {
4807         events |= APR_POLLHUP;
4808     }
4809     if (pfd->reqevents & ~(events | APR_POLLERR)) {
4810         pfd->reqevents &= ~events;
4811         rv = apr_pollset_add(pollset, pfd);
4812         if (rv != APR_SUCCESS) {
4813             AP_DEBUG_ASSERT(0);
4814             return;
4815         }
4816     }
4817     else {
4818         pfd->reqevents = 0;
4819     }
4820 }
4821 
proxy_tunnel_forward(proxy_tunnel_rec * tunnel,struct proxy_tunnel_conn * in)4822 static int proxy_tunnel_forward(proxy_tunnel_rec *tunnel,
4823                                  struct proxy_tunnel_conn *in)
4824 {
4825     struct proxy_tunnel_conn *out = in->other;
4826     apr_status_t rv;
4827     int sent = 0;
4828 
4829     ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, tunnel->r,
4830                   "proxy: %s: %s input ready",
4831                   tunnel->scheme, in->name);
4832 
4833     rv = ap_proxy_transfer_between_connections(tunnel->r,
4834                                                in->c, out->c,
4835                                                in->bb, out->bb,
4836                                                in->name, &sent,
4837                                                tunnel->read_buf_size,
4838                                            AP_PROXY_TRANSFER_YIELD_PENDING |
4839                                            AP_PROXY_TRANSFER_YIELD_MAX_READS);
4840     if (sent && out == tunnel->client) {
4841         tunnel->replied = 1;
4842     }
4843     if (rv != APR_SUCCESS) {
4844         if (APR_STATUS_IS_INCOMPLETE(rv)) {
4845             /* Pause POLLIN while waiting for POLLOUT on the other
4846              * side, hence avoid filling the output filters even
4847              * more to avoid blocking there.
4848              */
4849             ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, tunnel->r,
4850                           "proxy: %s: %s wait writable",
4851                           tunnel->scheme, out->name);
4852         }
4853         else if (APR_STATUS_IS_EOF(rv)) {
4854             /* Stop POLLIN and wait for POLLOUT (flush) on the
4855              * other side to shut it down.
4856              */
4857             ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, tunnel->r,
4858                           "proxy: %s: %s read shutdown",
4859                           tunnel->scheme, in->name);
4860             if (tunnel->nohalfclose) {
4861                 /* No half-close forwarding, we are done both ways as
4862                  * soon as one side shuts down.
4863                  */
4864                 return DONE;
4865             }
4866             in->down_in = 1;
4867         }
4868         else {
4869             /* Real failure, bail out */
4870             return HTTP_INTERNAL_SERVER_ERROR;
4871         }
4872 
4873         del_pollset(tunnel->pollset, in->pfd, APR_POLLIN);
4874         add_pollset(tunnel->pollset, out->pfd, APR_POLLOUT);
4875     }
4876 
4877     return OK;
4878 }
4879 
ap_proxy_tunnel_run(proxy_tunnel_rec * tunnel)4880 PROXY_DECLARE(int) ap_proxy_tunnel_run(proxy_tunnel_rec *tunnel)
4881 {
4882     int status = OK, rc;
4883     request_rec *r = tunnel->r;
4884     apr_pollset_t *pollset = tunnel->pollset;
4885     struct proxy_tunnel_conn *client = tunnel->client,
4886                              *origin = tunnel->origin;
4887     apr_interval_time_t timeout = tunnel->timeout >= 0 ? tunnel->timeout : -1;
4888     const char *scheme = tunnel->scheme;
4889     apr_status_t rv;
4890 
4891     ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(10212)
4892                   "proxy: %s: tunnel running (timeout %lf)",
4893                   scheme, timeout >= 0 ? (double)timeout / APR_USEC_PER_SEC
4894                                        : (double)-1.0);
4895 
4896     /* Loop until both directions of the connection are closed,
4897      * or a failure occurs.
4898      */
4899     do {
4900         const apr_pollfd_t *results;
4901         apr_int32_t nresults, i;
4902 
4903         ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, r,
4904                       "proxy: %s: polling (client=%hx, origin=%hx)",
4905                       scheme, client->pfd->reqevents, origin->pfd->reqevents);
4906         do {
4907             rv = apr_pollset_poll(pollset, timeout, &nresults, &results);
4908         } while (APR_STATUS_IS_EINTR(rv));
4909 
4910         if (rv != APR_SUCCESS) {
4911             if (APR_STATUS_IS_TIMEUP(rv)) {
4912                 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, APLOGNO(10213)
4913                               "proxy: %s: polling timed out "
4914                               "(client=%hx, origin=%hx)",
4915                               scheme, client->pfd->reqevents,
4916                               origin->pfd->reqevents);
4917                 status = HTTP_GATEWAY_TIME_OUT;
4918             }
4919             else {
4920                 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(10214)
4921                               "proxy: %s: polling failed", scheme);
4922                 status = HTTP_INTERNAL_SERVER_ERROR;
4923             }
4924             goto done;
4925         }
4926 
4927         ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, r, APLOGNO(10215)
4928                       "proxy: %s: woken up, %i result(s)", scheme, nresults);
4929 
4930         for (i = 0; i < nresults; i++) {
4931             const apr_pollfd_t *pfd = &results[i];
4932             struct proxy_tunnel_conn *tc = pfd->client_data;
4933 
4934             ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, r,
4935                           "proxy: %s: #%i: %s: %hx/%hx", scheme, i,
4936                           tc->name, pfd->rtnevents, tc->pfd->reqevents);
4937 
4938             /* sanity check */
4939             if (pfd->desc.s != client->pfd->desc.s
4940                     && pfd->desc.s != origin->pfd->desc.s) {
4941                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10222)
4942                               "proxy: %s: unknown socket in pollset", scheme);
4943                 status = HTTP_INTERNAL_SERVER_ERROR;
4944                 goto done;
4945             }
4946 
4947             if (!(pfd->rtnevents & (APR_POLLIN  | APR_POLLOUT |
4948                                     APR_POLLHUP | APR_POLLERR))) {
4949                 /* this catches POLLNVAL etc.. */
4950                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10220)
4951                               "proxy: %s: polling events error (%x)",
4952                               scheme, pfd->rtnevents);
4953                 status = HTTP_INTERNAL_SERVER_ERROR;
4954                 goto done;
4955             }
4956 
4957             /* We want to write if we asked for POLLOUT and got:
4958              * - POLLOUT: the socket is ready for write;
4959              * - !POLLIN: the socket is in error state (POLLERR) so we let
4960              *   the user know by failing the write and log, OR the socket
4961              *   is shutdown for read already (POLLHUP) so we have to
4962              *   shutdown for write.
4963              */
4964             if ((tc->pfd->reqevents & APR_POLLOUT)
4965                     && ((pfd->rtnevents & APR_POLLOUT)
4966                         || !(tc->pfd->reqevents & APR_POLLIN)
4967                         || !(pfd->rtnevents & (APR_POLLIN | APR_POLLHUP)))) {
4968                 struct proxy_tunnel_conn *out = tc, *in = tc->other;
4969 
4970                 ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, r,
4971                               "proxy: %s: %s output ready",
4972                               scheme, out->name);
4973 
4974                 rc = ap_filter_output_pending(out->c);
4975                 if (rc == OK) {
4976                     /* Keep polling out (only) */
4977                     continue;
4978                 }
4979                 if (rc != DECLINED) {
4980                     /* Real failure, bail out */
4981                     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10221)
4982                                   "proxy: %s: %s flushing failed (%i)",
4983                                   scheme, out->name, rc);
4984                     status = rc;
4985                     goto done;
4986                 }
4987 
4988                 /* No more pending data. If the other side is not readable
4989                  * anymore it's time to shutdown for write (this direction
4990                  * is over). Otherwise back to normal business.
4991                  */
4992                 del_pollset(pollset, out->pfd, APR_POLLOUT);
4993                 if (in->down_in) {
4994                     ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
4995                                   "proxy: %s: %s write shutdown",
4996                                   scheme, out->name);
4997                     apr_socket_shutdown(out->pfd->desc.s, 1);
4998                     out->down_out = 1;
4999                 }
5000                 else {
5001                     ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r,
5002                                   "proxy: %s: %s resume writable",
5003                                   scheme, out->name);
5004                     add_pollset(pollset, in->pfd, APR_POLLIN);
5005 
5006                     /* Flush any pending input data now, we don't know when
5007                      * the next POLLIN will trigger and retaining data might
5008                      * deadlock the underlying protocol. We don't check for
5009                      * pending data first with ap_filter_input_pending() since
5010                      * the read from proxy_tunnel_forward() is nonblocking
5011                      * anyway and returning OK if there's no data.
5012                      */
5013                     rc = proxy_tunnel_forward(tunnel, in);
5014                     if (rc != OK) {
5015                         status = rc;
5016                         goto done;
5017                     }
5018                 }
5019             }
5020 
5021             /* We want to read if we asked for POLLIN|HUP and got:
5022              * - POLLIN|HUP: the socket is ready for read or EOF (POLLHUP);
5023              * - !POLLOUT: the socket is in error state (POLLERR) so we let
5024              *   the user know by failing the read and log.
5025              */
5026             if ((tc->pfd->reqevents & APR_POLLIN)
5027                     && ((pfd->rtnevents & (APR_POLLIN | APR_POLLHUP))
5028                         || !(pfd->rtnevents & APR_POLLOUT))) {
5029                 rc = proxy_tunnel_forward(tunnel, tc);
5030                 if (rc != OK) {
5031                     status = rc;
5032                     goto done;
5033                 }
5034             }
5035         }
5036     } while (!client->down_out || !origin->down_out);
5037 
5038 done:
5039     ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(10223)
5040                   "proxy: %s: tunneling returns (%i)", scheme, status);
5041     if (status == DONE) {
5042         status = OK;
5043     }
5044     return status;
5045 }
5046 
ap_proxy_show_hcmethod(hcmethod_t method)5047 PROXY_DECLARE (const char *) ap_proxy_show_hcmethod(hcmethod_t method)
5048 {
5049     proxy_hcmethods_t *m = proxy_hcmethods;
5050     for (; m->name; m++) {
5051         if (m->method == method) {
5052             return m->name;
5053         }
5054     }
5055     return "???";
5056 }
5057 
proxy_util_register_hooks(apr_pool_t * p)5058 void proxy_util_register_hooks(apr_pool_t *p)
5059 {
5060     APR_REGISTER_OPTIONAL_FN(ap_proxy_retry_worker);
5061     APR_REGISTER_OPTIONAL_FN(ap_proxy_clear_connection);
5062     APR_REGISTER_OPTIONAL_FN(proxy_balancer_get_best_worker);
5063 }
5064