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, ¤t_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, ¤t_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