Apache HTTPD
proxy_util.c
Go to the documentation of this file.
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 "apr_atomic.h"
25#include "http_core.h"
26#include "proxy_util.h"
27#include "ajp.h"
28#include "scgi.h"
29
30#include "mpm_common.h" /* for ap_max_mem_free */
31
32#include "mod_http2.h" /* for http2_get_num_workers() */
33
34#if APR_HAVE_UNISTD_H
35#include <unistd.h> /* for getpid() */
36#endif
37
38#if APR_HAVE_SYS_UN_H
39#include <sys/un.h>
40#endif
41#if (APR_MAJOR_VERSION < 2)
42#include "apr_support.h" /* for apr_wait_for_io_or_timeout() */
43#endif
44
46
47/*
48 * Opaque structure containing target server info when
49 * using a forward proxy.
50 * Up to now only used in combination with HTTP CONNECT to ProxyRemote
51 */
52typedef struct {
53 int use_http_connect; /* Use SSL Tunneling via HTTP CONNECT */
54 const char *target_host; /* Target hostname */
55 apr_port_t target_port; /* Target port */
56 const char *proxy_auth; /* Proxy authorization */
58
59/*
60 * Opaque structure containing a refcounted and TTL'ed address.
61 */
62typedef struct proxy_address {
63 apr_sockaddr_t *addr; /* Remote address info */
64 const char *hostname; /* Remote host name */
65 apr_port_t hostport; /* Remote host port */
66 apr_uint32_t refcount; /* Number of conns and/or worker using it */
67 apr_uint32_t expiry; /* Expiry timestamp (seconds to proxy_start_time) */
69
70/* Global balancer counter */
72static int lb_workers_limit = 0;
75
77
78static const apr_time_t *proxy_start_time; /* epoch for expiring addresses */
79
83static int proxy_match_word(struct dirconn_entry *This, request_rec *r);
84static int ap_proxy_retry_worker(const char *proxy_function, proxy_worker *worker, server_rec *s);
88 void *baton);
89
91 (request_rec *r, request_rec *pr), (r, pr),
93
96{
97 char *thenil;
99
100 /* special case handling */
101 if (!dlen) {
102 /* XXX: APR_ENOSPACE would be better */
103 return APR_EGENERAL;
104 }
105 if (!src) {
106 *dst = '\0';
107 return APR_SUCCESS;
108 }
110 thelen = thenil - dst;
111 if (src[thelen] == '\0') {
112 return APR_SUCCESS;
113 }
114 return APR_EGENERAL;
115}
116
117/* already called in the knowledge that the characters are hex digits */
118PROXY_DECLARE(int) ap_proxy_hex2c(const char *x)
119{
120 int i;
121
122#if !APR_CHARSET_EBCDIC
123 int ch = x[0];
124
125 if (apr_isdigit(ch)) {
126 i = ch - '0';
127 }
128 else if (apr_isupper(ch)) {
129 i = ch - ('A' - 10);
130 }
131 else {
132 i = ch - ('a' - 10);
133 }
134 i <<= 4;
135
136 ch = x[1];
137 if (apr_isdigit(ch)) {
138 i += ch - '0';
139 }
140 else if (apr_isupper(ch)) {
141 i += ch - ('A' - 10);
142 }
143 else {
144 i += ch - ('a' - 10);
145 }
146 return i;
147#else /*APR_CHARSET_EBCDIC*/
148 /*
149 * we assume that the hex value refers to an ASCII character
150 * so convert to EBCDIC so that it makes sense locally;
151 *
152 * example:
153 *
154 * client specifies %20 in URL to refer to a space char;
155 * at this point we're called with EBCDIC "20"; after turning
156 * EBCDIC "20" into binary 0x20, we then need to assume that 0x20
157 * represents an ASCII char and convert 0x20 to EBCDIC, yielding
158 * 0x40
159 */
160 char buf[1];
161
162 if (1 == sscanf(x, "%2x", &i)) {
163 buf[0] = i & 0xFF;
165 return buf[0];
166 }
167 else {
168 return 0;
169 }
170#endif /*APR_CHARSET_EBCDIC*/
171}
172
173PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x)
174{
175#if !APR_CHARSET_EBCDIC
176 int i;
177
178 x[0] = '%';
179 i = (ch & 0xF0) >> 4;
180 if (i >= 10) {
181 x[1] = ('A' - 10) + i;
182 }
183 else {
184 x[1] = '0' + i;
185 }
186
187 i = ch & 0x0F;
188 if (i >= 10) {
189 x[2] = ('A' - 10) + i;
190 }
191 else {
192 x[2] = '0' + i;
193 }
194#else /*APR_CHARSET_EBCDIC*/
195 static const char ntoa[] = { "0123456789ABCDEF" };
196 char buf[1];
197
198 ch &= 0xFF;
199
200 buf[0] = ch;
202
203 x[0] = '%';
204 x[1] = ntoa[(buf[0] >> 4) & 0x0F];
205 x[2] = ntoa[buf[0] & 0x0F];
206 x[3] = '\0';
207#endif /*APR_CHARSET_EBCDIC*/
208}
209
210/*
211 * canonicalise a URL-encoded string
212 */
213
214/*
215 * Convert a URL-encoded string to canonical form.
216 * It decodes characters which need not be encoded,
217 * and encodes those which must be encoded, and does not touch
218 * those which must not be touched.
219 */
221 enum enctype t, int flags,
222 int proxyreq)
223{
224 int i, j, ch;
225 char *y;
226 char *allowed; /* characters which should not be encoded */
227 char *reserved; /* characters which much not be en/de-coded */
230
231/*
232 * N.B. in addition to :@&=, this allows ';' in an http path
233 * and '?' in an ftp path -- this may be revised
234 *
235 * Also, it makes a '+' character in a search string reserved, as
236 * it may be form-encoded. (Although RFC 1738 doesn't allow this -
237 * it only permits ; / ? : @ = & as reserved chars.)
238 */
239 if (t == enc_path) {
240 allowed = "~$-_.+!*'(),;:@&=";
241 }
242 else if (t == enc_search) {
243 allowed = "$-_.!*'(),;:@&=";
244 }
245 else if (t == enc_user) {
246 allowed = "$-_.+!*'(),;@&=";
247 }
248 else if (t == enc_fpath) {
249 allowed = "$-_.+!*'(),?:@&=";
250 }
251 else { /* if (t == enc_parm) */
252 allowed = "$-_.+!*'(),?/:@&=";
253 }
254
255 if (t == enc_path) {
256 reserved = "/";
257 }
258 else if (t == enc_search) {
259 reserved = "+";
260 }
261 else {
262 reserved = "";
263 }
264
265 y = apr_palloc(p, 3 * len + 1);
266
267 for (i = 0, j = 0; i < len; i++, j++) {
268/* always handle '/' first */
269 ch = x[i];
270 if (strchr(reserved, ch)) {
271 y[j] = ch;
272 continue;
273 }
274/*
275 * decode it if not already done. do not decode reverse proxied URLs
276 * unless specifically forced
277 */
279 || (proxyreq && proxyreq != PROXYREQ_REVERSE)) && ch == '%') {
280 if (!apr_isxdigit(x[i + 1]) || !apr_isxdigit(x[i + 2])) {
281 return NULL;
282 }
283 ch = ap_proxy_hex2c(&x[i + 1]);
284 if (ch != 0 && strchr(reserved, ch)) { /* keep it encoded */
285 y[j++] = x[i++];
286 y[j++] = x[i++];
287 y[j] = x[i];
288 continue;
289 }
290 if (noencslashesenc && !forcedec && (proxyreq == PROXYREQ_REVERSE)) {
291 /*
292 * In the reverse proxy case when we only want to keep encoded
293 * slashes untouched revert back to '%' which will cause
294 * '%' to be encoded in the following.
295 */
296 ch = '%';
297 }
298 else {
299 i += 2;
300 }
301 }
302/* recode it, if necessary */
303 if (!apr_isalnum(ch) && !strchr(allowed, ch)) {
304 ap_proxy_c2hex(ch, &y[j]);
305 j += 2;
306 }
307 else {
308 y[j] = ch;
309 }
310 }
311 y[j] = '\0';
312 return y;
313}
314
315/*
316 * Convert a URL-encoded string to canonical form.
317 * It decodes characters which need not be encoded,
318 * and encodes those which must be encoded, and does not touch
319 * those which must not be touched.
320 */
321PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len,
322 enum enctype t, int forcedec,
323 int proxyreq)
324{
325 int flags;
326
328 return ap_proxy_canonenc_ex(p, x, len, t, flags, proxyreq);
329}
330
331/*
332 * Parses network-location.
333 * urlp on input the URL; on output the path, after the leading /
334 * user NULL if no user/password permitted
335 * password holder for password
336 * host holder for host
337 * port port number; only set if one is supplied.
338 *
339 * Returns an error string.
340 */
341PROXY_DECLARE(char *)
343 char **passwordp, char **hostp, apr_port_t *port)
344{
345 char *addr, *scope_id, *strp, *host, *url = *urlp;
346 char *user = NULL, *password = NULL;
348 apr_status_t rv;
349
350 if (url[0] != '/' || url[1] != '/') {
351 return "Malformed URL";
352 }
353 host = url + 2;
354 url = strchr(host, '/');
355 if (url == NULL) {
356 url = "";
357 }
358 else {
359 *(url++) = '\0'; /* skip separating '/' */
360 }
361
362 /* find _last_ '@' since it might occur in user/password part */
363 strp = strrchr(host, '@');
364
365 if (strp != NULL) {
366 *strp = '\0';
367 user = host;
368 host = strp + 1;
369
370/* find password */
371 strp = strchr(user, ':');
372 if (strp != NULL) {
373 *strp = '\0';
374 password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, 1, 0);
375 if (password == NULL) {
376 return "Bad %-escape in URL (password)";
377 }
378 }
379
380 user = ap_proxy_canonenc(p, user, strlen(user), enc_user, 1, 0);
381 if (user == NULL) {
382 return "Bad %-escape in URL (username)";
383 }
384 }
385 if (userp != NULL) {
386 *userp = user;
387 }
388 if (passwordp != NULL) {
390 }
391
392 /*
393 * Parse the host string to separate host portion from optional port.
394 * Perform range checking on port.
395 */
397 if (rv != APR_SUCCESS || addr == NULL || scope_id != NULL) {
398 return "Invalid host/port";
399 }
400 if (tmp_port != 0) { /* only update caller's port if port was specified */
401 *port = tmp_port;
402 }
403
404 ap_str_tolower(addr); /* DNS names are case-insensitive */
405
406 *urlp = url;
407 *hostp = addr;
408
409 return NULL;
410}
411
412static int proxyerror_core(request_rec *r, int statuscode, const char *message,
413 apr_status_t rv)
414{
416 "%s returned by %s", message, r->uri);
417
418 apr_table_setn(r->notes, "error-notes",
420 "The proxy server could not handle the request<p>"
421 "Reason: <strong>", ap_escape_html(r->pool, message),
422 "</strong></p>",
423 NULL));
424
425 /* Allow "error-notes" string to be printed by ap_send_error_response() */
426 apr_table_setn(r->notes, "verbose-error-to", "*");
427
428 r->status_line = apr_psprintf(r->pool, "%3.3u Proxy Error", statuscode);
429 return statuscode;
430}
431
433{
434 return proxyerror_core(r, statuscode, message, 0);
435}
436
437static const char *
439{
440 char *url, *user = NULL, *password = NULL, *err, *host = NULL;
442
443 if (r->hostname != NULL) {
444 return r->hostname;
445 }
446
447 /* Set url to the first char after "scheme://" */
448 if ((url = strchr(r->uri, ':')) == NULL || url[1] != '/' || url[2] != '/') {
449 return NULL;
450 }
451
452 url = apr_pstrdup(r->pool, &url[1]); /* make it point to "//", which is what proxy_canon_netloc expects */
453
454 err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port);
455
456 if (err != NULL) {
457 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00899) "%s", err);
458 }
459
460 r->hostname = host;
461
462 return host; /* ought to return the port, too */
463}
464
465/* Return TRUE if addr represents an IP address (or an IP network address) */
467{
468 const char *addr = This->name;
469 long ip_addr[4];
470 int i, quads;
471 long bits;
472
473 /*
474 * if the address is given with an explicit netmask, use that
475 * Due to a deficiency in apr_inet_addr(), it is impossible to parse
476 * "partial" addresses (with less than 4 quads) correctly, i.e.
477 * 192.168.123 is parsed as 192.168.0.123, which is not what I want.
478 * I therefore have to parse the IP address manually:
479 * if (proxy_readmask(This->name, &This->addr.s_addr, &This->mask.s_addr) == 0)
480 * addr and mask were set by proxy_readmask()
481 * return 1;
482 */
483
484 /*
485 * Parse IP addr manually, optionally allowing
486 * abbreviated net addresses like 192.168.
487 */
488
489 /* Iterate over up to 4 (dotted) quads. */
490 for (quads = 0; quads < 4 && *addr != '\0'; ++quads) {
491 char *tmp;
492
493 if (*addr == '/' && quads > 0) { /* netmask starts here. */
494 break;
495 }
496
497 if (!apr_isdigit(*addr)) {
498 return 0; /* no digit at start of quad */
499 }
500
501 ip_addr[quads] = strtol(addr, &tmp, 0);
502
503 if (tmp == addr) { /* expected a digit, found something else */
504 return 0;
505 }
506
507 if (ip_addr[quads] < 0 || ip_addr[quads] > 255) {
508 /* invalid octet */
509 return 0;
510 }
511
512 addr = tmp;
513
514 if (*addr == '.' && quads != 3) {
515 ++addr; /* after the 4th quad, a dot would be illegal */
516 }
517 }
518
519 for (This->addr.s_addr = 0, i = 0; i < quads; ++i) {
520 This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
521 }
522
523 if (addr[0] == '/' && apr_isdigit(addr[1])) { /* net mask follows: */
524 char *tmp;
525
526 ++addr;
527
528 bits = strtol(addr, &tmp, 0);
529
530 if (tmp == addr) { /* expected a digit, found something else */
531 return 0;
532 }
533
534 addr = tmp;
535
536 if (bits < 0 || bits > 32) { /* netmask must be between 0 and 32 */
537 return 0;
538 }
539
540 }
541 else {
542 /*
543 * Determine (i.e., "guess") netmask by counting the
544 * number of trailing .0's; reduce #quads appropriately
545 * (so that 192.168.0.0 is equivalent to 192.168.)
546 */
547 while (quads > 0 && ip_addr[quads - 1] == 0) {
548 --quads;
549 }
550
551 /* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */
552 if (quads < 1) {
553 return 0;
554 }
555
556 /* every zero-byte counts as 8 zero-bits */
557 bits = 8 * quads;
558
559 if (bits != 32) { /* no warning for fully qualified IP address */
561 "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld",
562 inet_ntoa(This->addr), bits);
563 }
564 }
565
566 This->mask.s_addr = htonl(APR_INADDR_NONE << (32 - bits));
567
568 if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) {
570 "Warning: NetMask and IP-Addr disagree in %s/%ld",
571 inet_ntoa(This->addr), bits);
572 This->addr.s_addr &= This->mask.s_addr;
574 " Set to %s/%ld", inet_ntoa(This->addr), bits);
575 }
576
577 if (*addr == '\0') {
578 This->matcher = proxy_match_ipaddr;
579 return 1;
580 }
581 else {
582 return (*addr == '\0'); /* okay iff we've parsed the whole string */
583 }
584}
585
586/* Return TRUE if addr represents an IP address (or an IP network address) */
588{
589 int i, ip_addr[4];
590 struct in_addr addr, *ip;
591 const char *host = proxy_get_host_of_request(r);
592
593 if (host == NULL) { /* oops! */
594 return 0;
595 }
596
597 memset(&addr, '\0', sizeof addr);
598 memset(ip_addr, '\0', sizeof ip_addr);
599
600 if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) {
601 for (addr.s_addr = 0, i = 0; i < 4; ++i) {
602 /* ap_proxy_is_ipaddr() already confirmed that we have
603 * a valid octet in ip_addr[i]
604 */
605 addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
606 }
607
608 if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) {
609#if DEBUGGING
611 "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr));
613 "%s/", inet_ntoa(This->addr));
615 "%s", inet_ntoa(This->mask));
616#endif
617 return 1;
618 }
619#if DEBUGGING
620 else {
622 "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr));
624 "%s/", inet_ntoa(This->addr));
626 "%s", inet_ntoa(This->mask));
627 }
628#endif
629 }
630 else {
631 struct apr_sockaddr_t *reqaddr;
632
634 != APR_SUCCESS) {
635#if DEBUGGING
637 "2)IP-NoMatch: hostname=%s msg=Host not found", host);
638#endif
639 return 0;
640 }
641
642 /* Try to deal with multiple IP addr's for a host */
643 /* FIXME: This needs to be able to deal with IPv6 */
644 while (reqaddr) {
645 ip = (struct in_addr *) reqaddr->ipaddr_ptr;
646 if (This->addr.s_addr == (ip->s_addr & This->mask.s_addr)) {
647#if DEBUGGING
649 "3)IP-Match: %s[%s] <-> ", host, inet_ntoa(*ip));
651 "%s/", inet_ntoa(This->addr));
653 "%s", inet_ntoa(This->mask));
654#endif
655 return 1;
656 }
657#if DEBUGGING
658 else {
660 "3)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(*ip));
662 "%s/", inet_ntoa(This->addr));
664 "%s", inet_ntoa(This->mask));
665 }
666#endif
667 reqaddr = reqaddr->next;
668 }
669 }
670
671 return 0;
672}
673
674/* Return TRUE if addr represents a domain name */
676{
677 char *addr = This->name;
678 int i;
679
680 /* Domain name must start with a '.' */
681 if (addr[0] != '.') {
682 return 0;
683 }
684
685 /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
686 for (i = 0; apr_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i) {
687 continue;
688 }
689
690#if 0
691 if (addr[i] == ':') {
693 "@@@@ handle optional port in proxy_is_domainname()");
694 /* @@@@ handle optional port */
695 }
696#endif
697
698 if (addr[i] != '\0') {
699 return 0;
700 }
701
702 /* Strip trailing dots */
703 for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i) {
704 addr[i] = '\0';
705 }
706
707 This->matcher = proxy_match_domainname;
708 return 1;
709}
710
711/* Return TRUE if host "host" is in domain "domain" */
713{
714 const char *host = proxy_get_host_of_request(r);
715 int d_len = strlen(This->name), h_len;
716
717 if (host == NULL) { /* some error was logged already */
718 return 0;
719 }
720
721 h_len = strlen(host);
722
723 /* @@@ do this within the setup? */
724 /* Ignore trailing dots in domain comparison: */
725 while (d_len > 0 && This->name[d_len - 1] == '.') {
726 --d_len;
727 }
728 while (h_len > 0 && host[h_len - 1] == '.') {
729 --h_len;
730 }
731 return h_len > d_len
732 && strncasecmp(&host[h_len - d_len], This->name, d_len) == 0;
733}
734
735/* Return TRUE if host represents a host name */
737{
738 struct apr_sockaddr_t *addr;
739 char *host = This->name;
740 int i;
741
742 /* Host names must not start with a '.' */
743 if (host[0] == '.') {
744 return 0;
745 }
746 /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
747 for (i = 0; apr_isalnum(host[i]) || host[i] == '-' || host[i] == '.'; ++i);
748
749 if (host[i] != '\0' || apr_sockaddr_info_get(&addr, host, APR_UNSPEC, 0, 0, p) != APR_SUCCESS) {
750 return 0;
751 }
752
753 This->hostaddr = addr;
754
755 /* Strip trailing dots */
756 for (i = strlen(host) - 1; i > 0 && host[i] == '.'; --i) {
757 host[i] = '\0';
758 }
759
760 This->matcher = proxy_match_hostname;
761 return 1;
762}
763
764/* Return TRUE if host "host" is equal to host2 "host2" */
766{
767 char *host = This->name;
768 const char *host2 = proxy_get_host_of_request(r);
769 int h2_len;
770 int h1_len;
771
772 if (host == NULL || host2 == NULL) {
773 return 0; /* oops! */
774 }
775
776 h2_len = strlen(host2);
777 h1_len = strlen(host);
778
779#if 0
780 struct apr_sockaddr_t *addr = *This->hostaddr;
781
782 /* Try to deal with multiple IP addr's for a host */
783 while (addr) {
784 if (addr->ipaddr_ptr == ? ? ? ? ? ? ? ? ? ? ? ? ?)
785 return 1;
786 addr = addr->next;
787 }
788#endif
789
790 /* Ignore trailing dots in host2 comparison: */
791 while (h2_len > 0 && host2[h2_len - 1] == '.') {
792 --h2_len;
793 }
794 while (h1_len > 0 && host[h1_len - 1] == '.') {
795 --h1_len;
796 }
797 return h1_len == h2_len
798 && strncasecmp(host, host2, h1_len) == 0;
799}
800
801/* Return TRUE if addr is to be matched as a word */
803{
804 This->matcher = proxy_match_word;
805 return 1;
806}
807
808/* Return TRUE if string "str2" occurs literally in "str1" */
810{
811 const char *host = proxy_get_host_of_request(r);
812 return host != NULL && ap_strstr_c(host, This->name) != NULL;
813}
814
815/* Backwards-compatible interface. */
821
822#define MAX_IP_STR_LEN (46)
823
825 const char *hostname, apr_sockaddr_t *addr)
826{
827 int j;
828
829 /* XXX FIXME: conf->noproxies->elts is part of an opaque structure */
830 for (j = 0; j < conf->noproxies->nelts; j++) {
831 struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts;
833
835 "checking remote machine [%s] against [%s]",
836 hostname, npent[j].name);
837 if (ap_strstr_c(hostname, npent[j].name) || npent[j].name[0] == '*') {
839 "connect to remote machine %s blocked: name %s "
840 "matched", hostname, npent[j].name);
841 return HTTP_FORBIDDEN;
842 }
843
844 /* No IP address checks if no IP address was passed in,
845 * i.e. the forward address proxy case, where this server does
846 * not resolve the hostname. */
847 if (!addr)
848 continue;
849
853
855 continue;
856
859 continue;
861 "ProxyBlock comparing %s and %s", caddr, uaddr);
862 if (!strcmp(caddr, uaddr)) {
864 "connect to remote machine %s blocked: "
865 "IP %s matched", hostname, caddr);
866 return HTTP_FORBIDDEN;
867 }
868 }
869 }
870 }
871
872 return OK;
873}
874
875/* set up the minimal filter set */
877{
878 ap_add_input_filter("HTTP_IN", NULL, r, c);
879 return OK;
880}
881
883 proxy_dir_conf *conf, const char *url)
884{
886 struct proxy_alias *ent;
887 int i, l1, l1_orig, l2;
888 char *u;
889
890 /*
891 * XXX FIXME: Make sure this handled the ambiguous case of the :<PORT>
892 * after the hostname
893 * XXX FIXME: Ensure the /uri component is a case sensitive match
894 */
895 if (r->proxyreq != PROXYREQ_REVERSE) {
896 return url;
897 }
898
899 l1_orig = strlen(url);
900 if (conf->interpolate_env == 1) {
901 rconf = ap_get_module_config(r->request_config, &proxy_module);
902 ent = (struct proxy_alias *)rconf->raliases->elts;
903 }
904 else {
905 ent = (struct proxy_alias *)conf->raliases->elts;
906 }
907 for (i = 0; i < conf->raliases->nelts; i++) {
909 ap_get_module_config(r->server->module_config, &proxy_module);
911 const char *real = ent[i].real;
912
913 /* Restore the url length, if it had been changed by the code below */
914 l1 = l1_orig;
915
916 /*
917 * First check if mapping against a balancer and see
918 * if we have such a entity. If so, then we need to
919 * find the particulars of the actual worker which may
920 * or may not be the right one... basically, we need
921 * to find which member actually handled this request.
922 */
923 if (ap_proxy_valid_balancer_name((char *)real, 0) &&
925 int n, l3 = 0;
927 const char *urlpart = ap_strchr_c(real + sizeof(BALANCER_PREFIX) - 1, '/');
928 if (urlpart) {
929 if (!urlpart[1])
930 urlpart = NULL;
931 else
932 l3 = strlen(urlpart);
933 }
934 /* The balancer comparison is a bit trickier. Given the context
935 * BalancerMember balancer://alias http://example.com/foo
936 * ProxyPassReverse /bash balancer://alias/bar
937 * translate url http://example.com/foo/bar/that to /bash/that
938 */
939 for (n = 0; n < balancer->workers->nelts; n++) {
940 l2 = strlen((*worker)->s->name_ex);
941 if (urlpart) {
942 /* urlpart (l3) assuredly starts with its own '/' */
943 if ((*worker)->s->name_ex[l2 - 1] == '/')
944 --l2;
945 if (l1 >= l2 + l3
946 && strncasecmp((*worker)->s->name_ex, url, l2) == 0
947 && strncmp(urlpart, url + l2, l3) == 0) {
948 u = apr_pstrcat(r->pool, ent[i].fake, &url[l2 + l3],
949 NULL);
950 return ap_is_url(u) ? u : ap_construct_url(r->pool, u, r);
951 }
952 }
953 else if (l1 >= l2 && strncasecmp((*worker)->s->name_ex, url, l2) == 0) {
954 /* edge case where fake is just "/"... avoid double slash */
955 if ((ent[i].fake[0] == '/') && (ent[i].fake[1] == 0) && (url[l2] == '/')) {
956 u = apr_pstrdup(r->pool, &url[l2]);
957 } else {
958 u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL);
959 }
960 return ap_is_url(u) ? u : ap_construct_url(r->pool, u, r);
961 }
962 worker++;
963 }
964 }
965 else {
966 const char *part = url;
967 l2 = strlen(real);
968 if (real[0] == '/') {
969 part = ap_strstr_c(url, "://");
970 if (part) {
971 part = ap_strchr_c(part+3, '/');
972 if (part) {
973 l1 = strlen(part);
974 }
975 else {
976 part = url;
977 }
978 }
979 else {
980 part = url;
981 }
982 }
983 if (l2 > 0 && l1 >= l2 && strncasecmp(real, part, l2) == 0) {
984 u = apr_pstrcat(r->pool, ent[i].fake, &part[l2], NULL);
985 return ap_is_url(u) ? u : ap_construct_url(r->pool, u, r);
986 }
987 }
988 }
989
990 return url;
991}
992
993/*
994 * Cookies are a bit trickier to match: we've got two substrings to worry
995 * about, and we can't just find them with strstr 'cos of case. Regexp
996 * matching would be an easy fix, but for better consistency with all the
997 * other matches we'll refrain and use apr_strmatch to find path=/domain=
998 * and stick to plain strings for the config values.
999 */
1001 proxy_dir_conf *conf, const char *str)
1002{
1004 &proxy_module);
1005 struct proxy_alias *ent;
1006 apr_size_t len = strlen(str);
1007 const char *newpath = NULL;
1008 const char *newdomain = NULL;
1009 const char *pathp;
1010 const char *domainp;
1011 const char *pathe = NULL;
1012 const char *domaine = NULL;
1013 apr_size_t l1, l2, poffs = 0, doffs = 0;
1014 int i;
1015 int ddiff = 0;
1016 int pdiff = 0;
1017 char *tmpstr, *tmpstr_orig, *token, *last, *ret;
1018
1019 if (r->proxyreq != PROXYREQ_REVERSE) {
1020 return str;
1021 }
1022
1023 /*
1024 * Find the match and replacement, but save replacing until we've done
1025 * both path and domain so we know the new strlen
1026 */
1028 while ((token = apr_strtok(tmpstr, ";", &last))) {
1029 /* skip leading spaces */
1030 while (apr_isspace(*token)) {
1031 ++token;
1032 }
1033
1034 if (ap_cstr_casecmpn("path=", token, 5) == 0) {
1035 pathp = token + 5;
1037 l1 = strlen(pathp);
1038 pathe = str + poffs + l1;
1039 if (conf->interpolate_env == 1) {
1040 ent = (struct proxy_alias *)rconf->cookie_paths->elts;
1041 }
1042 else {
1043 ent = (struct proxy_alias *)conf->cookie_paths->elts;
1044 }
1045 for (i = 0; i < conf->cookie_paths->nelts; i++) {
1046 l2 = strlen(ent[i].fake);
1047 if (l1 >= l2 && strncmp(ent[i].fake, pathp, l2) == 0) {
1048 newpath = ent[i].real;
1049 pdiff = strlen(newpath) - l1;
1050 break;
1051 }
1052 }
1053 }
1054 else if (ap_cstr_casecmpn("domain=", token, 7) == 0) {
1055 domainp = token + 7;
1057 l1 = strlen(domainp);
1058 domaine = str + doffs + l1;
1059 if (conf->interpolate_env == 1) {
1060 ent = (struct proxy_alias *)rconf->cookie_domains->elts;
1061 }
1062 else {
1063 ent = (struct proxy_alias *)conf->cookie_domains->elts;
1064 }
1065 for (i = 0; i < conf->cookie_domains->nelts; i++) {
1066 l2 = strlen(ent[i].fake);
1067 if (l1 >= l2 && strncasecmp(ent[i].fake, domainp, l2) == 0) {
1068 newdomain = ent[i].real;
1069 ddiff = strlen(newdomain) - l1;
1070 break;
1071 }
1072 }
1073 }
1074
1075 /* Iterate the remaining tokens using apr_strtok(NULL, ...) */
1076 tmpstr = NULL;
1077 }
1078
1079 if (newpath) {
1080 ret = apr_palloc(r->pool, len + pdiff + ddiff + 1);
1081 l1 = strlen(newpath);
1082 if (newdomain) {
1083 l2 = strlen(newdomain);
1084 if (doffs > poffs) {
1085 memcpy(ret, str, poffs);
1086 memcpy(ret + poffs, newpath, l1);
1087 memcpy(ret + poffs + l1, pathe, str + doffs - pathe);
1089 strcpy(ret + doffs + pdiff + l2, domaine);
1090 }
1091 else {
1092 memcpy(ret, str, doffs) ;
1095 memcpy(ret + poffs + ddiff, newpath, l1);
1096 strcpy(ret + poffs + ddiff + l1, pathe);
1097 }
1098 }
1099 else {
1100 memcpy(ret, str, poffs);
1101 memcpy(ret + poffs, newpath, l1);
1102 strcpy(ret + poffs + l1, pathe);
1103 }
1104 }
1105 else if (newdomain) {
1106 ret = apr_palloc(r->pool, len + ddiff + 1);
1107 l2 = strlen(newdomain);
1108 memcpy(ret, str, doffs);
1110 strcpy(ret + doffs + l2, domaine);
1111 }
1112 else {
1113 ret = (char *)str; /* no change */
1114 }
1115
1116 return ret;
1117}
1118
1119/*
1120 * BALANCER related...
1121 */
1122
1123/*
1124 * verifies that the balancer name conforms to standards.
1125 */
1127{
1128 if (!i)
1129 i = sizeof(BALANCER_PREFIX)-1;
1131}
1132
1133
1135 proxy_server_conf *conf,
1136 const char *url,
1137 int care)
1138{
1140 char *c, *uri = apr_pstrdup(p, url);
1141 int i;
1143
1144 c = strchr(uri, ':');
1145 if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') {
1146 return NULL;
1147 }
1148 /* remove path from uri */
1149 if ((c = strchr(c + 3, '/'))) {
1150 *c = '\0';
1151 }
1156 for (i = 0; i < conf->balancers->nelts; i++) {
1157 if (balancer->hash.def == hash.def && balancer->hash.fnv == hash.fnv) {
1158 if (!care || !balancer->s->inactive) {
1159 return balancer;
1160 }
1161 }
1162 balancer++;
1163 }
1164 return NULL;
1165}
1166
1167
1170 const char *url)
1171{
1173 if (!url) {
1174 return NULL;
1175 }
1176 if (apr_uri_parse(p, url, &puri) != APR_SUCCESS) {
1177 return apr_psprintf(p, "unable to parse: %s", url);
1178 }
1179 if (puri.path && PROXY_STRNCPY(balancer->s->vpath, puri.path) != APR_SUCCESS) {
1180 return apr_psprintf(p, "balancer %s front-end virtual-path (%s) too long",
1181 balancer->s->name, puri.path);
1182 }
1183 if (puri.hostname && PROXY_STRNCPY(balancer->s->vhost, puri.hostname) != APR_SUCCESS) {
1184 return apr_psprintf(p, "balancer %s front-end vhost name (%s) too long",
1185 balancer->s->name, puri.hostname);
1186 }
1187 return NULL;
1188}
1189
1190#define PROXY_UNSET_NONCE '\n'
1191
1194 proxy_server_conf *conf,
1195 const char *url,
1196 const char *alias,
1197 int do_malloc)
1198{
1199 proxy_balancer_method *lbmethod;
1201 char *c, *q, *uri = apr_pstrdup(p, url);
1202 const char *sname;
1203
1204 /* We should never get here without a valid BALANCER_PREFIX... */
1205
1206 c = strchr(uri, ':');
1207 if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
1208 return apr_psprintf(p, "Bad syntax for a balancer name (%s)", uri);
1209 /* remove path from uri */
1210 if ((q = strchr(c + 3, '/')))
1211 *q = '\0';
1212
1215 memset(*balancer, 0, sizeof(proxy_balancer));
1216
1217 /*
1218 * NOTE: The default method is byrequests - if it doesn't
1219 * exist, that's OK at this time. We check when we share and sync
1220 */
1221 lbmethod = ap_lookup_provider(PROXY_LBMETHOD, "byrequests", "0");
1222 (*balancer)->lbmethod = lbmethod;
1223
1224 (*balancer)->workers = apr_array_make(p, 5, sizeof(proxy_worker *));
1225#if APR_HAS_THREADS
1226 (*balancer)->gmutex = NULL;
1227 (*balancer)->tmutex = NULL;
1228#endif
1229
1230 if (do_malloc)
1232 else
1234
1236
1237 bshared->was_malloced = (do_malloc != 0);
1238 PROXY_STRNCPY(bshared->lbpname, "byrequests");
1239 if (PROXY_STRNCPY(bshared->name, uri) != APR_SUCCESS) {
1240 if (do_malloc) free(bshared);
1241 return apr_psprintf(p, "balancer name (%s) too long", uri);
1242 }
1243 (*balancer)->lbmethod_set = 1;
1244
1245 /*
1246 * We do the below for verification. The real sname will be
1247 * done post_config
1248 */
1249 ap_pstr2_alnum(p, bshared->name + sizeof(BALANCER_PREFIX) - 1,
1250 &sname);
1251 sname = apr_pstrcat(p, conf->id, "_", sname, NULL);
1252 if (PROXY_STRNCPY(bshared->sname, sname) != APR_SUCCESS) {
1253 if (do_malloc) free(bshared);
1254 return apr_psprintf(p, "balancer safe-name (%s) too long", sname);
1255 }
1258 (*balancer)->hash = bshared->hash;
1259
1260 bshared->forcerecovery = 1;
1261 bshared->sticky_separator = '.';
1262 *bshared->nonce = PROXY_UNSET_NONCE; /* impossible valid input */
1263
1264 (*balancer)->s = bshared;
1265 (*balancer)->sconf = conf;
1266
1267 return ap_proxy_update_balancer(p, *balancer, alias);
1268}
1269
1270/*
1271 * Create an already defined balancer and free up memory.
1272 */
1275 int i)
1276{
1278 proxy_balancer_method *lbmethod;
1279 char *action = "copying";
1280 if (!shm || !balancer->s)
1281 return APR_EINVAL;
1282
1283 if ((balancer->s->hash.def != shm->hash.def) ||
1284 (balancer->s->hash.fnv != shm->hash.fnv)) {
1286 if (balancer->s->was_malloced)
1287 free(balancer->s);
1288 } else {
1289 action = "re-using";
1290 }
1291 balancer->s = shm;
1292 balancer->s->index = i;
1294 "%s shm[%d] (0x%pp) for %s", action, i, (void *)shm,
1295 balancer->s->name);
1296 /* the below should always succeed */
1298 if (lbmethod) {
1299 balancer->lbmethod = lbmethod;
1301 } else {
1303 "Cannot find LB Method: %s", balancer->s->lbpname);
1304 return APR_EINVAL;
1305 }
1306 if (*balancer->s->nonce == PROXY_UNSET_NONCE) {
1307 char nonce[APR_UUID_FORMATTED_LENGTH + 1];
1309
1310 /* Generate a pseudo-UUID from the PRNG to use as a nonce for
1311 * the lifetime of the process. uuid.data is a char array so
1312 * this is an adequate substitute for apr_uuid_get(). */
1314 apr_uuid_format(nonce, &uuid);
1315 rv = PROXY_STRNCPY(balancer->s->nonce, nonce);
1316 }
1317 return rv;
1318}
1319
1321{
1322#if APR_HAS_THREADS
1324#endif
1327 unsigned int num;
1328
1329 if (!storage) {
1331 "no provider for %s", balancer->s->name);
1332 return APR_EGENERAL;
1333 }
1334 /*
1335 * for each balancer we need to init the global
1336 * mutex and then attach to the shared worker shm
1337 */
1338 if (!balancer->gmutex) {
1340 "no mutex %s", balancer->s->name);
1341 return APR_EGENERAL;
1342 }
1343
1344 /* Re-open the mutex for the child. */
1347 p);
1348 if (rv != APR_SUCCESS) {
1350 "Failed to reopen mutex %s in child",
1351 balancer->s->name);
1352 return rv;
1353 }
1354
1355 /* now attach */
1357 if (!balancer->wslot) {
1358 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(00921) "slotmem_attach failed");
1359 return APR_EGENERAL;
1360 }
1361
1362#if APR_HAS_THREADS
1363 if (balancer->tmutex == NULL) {
1365 if (rv != APR_SUCCESS) {
1367 "can not create balancer thread mutex");
1368 return rv;
1369 }
1370 }
1371#endif
1372 return APR_SUCCESS;
1373}
1374
1376 request_rec *r,
1378 void *baton)
1379{
1380 int i = 0;
1381 int cur_lbset = 0;
1382 int max_lbset = 0;
1383 int unusable_workers = 0;
1384 apr_pool_t *tpool = NULL;
1387 proxy_worker *worker = NULL;
1389
1391 "proxy: Entering %s for BALANCER (%s)",
1393
1394 apr_pool_create(&tpool, r->pool);
1395 apr_pool_tag(tpool, "proxy_lb_best");
1396
1397 spares = apr_array_make(tpool, 1, sizeof(proxy_worker*));
1398 standbys = apr_array_make(tpool, 1, sizeof(proxy_worker*));
1399
1400 /* Process lbsets in order, only replacing unusable workers in a given lbset
1401 * with available spares from the same lbset. Hot standbys will be used as a
1402 * last resort when all other workers and spares are unavailable.
1403 */
1404 for (cur_lbset = 0; !best_worker && (cur_lbset <= max_lbset); cur_lbset++) {
1405 unusable_workers = 0;
1408
1409 for (i = 0; i < balancer->workers->nelts; i++) {
1411
1412 if (worker->s->lbset > max_lbset) {
1413 max_lbset = worker->s->lbset;
1414 }
1415
1416 if (worker->s->lbset != cur_lbset) {
1417 continue;
1418 }
1419
1420 /* A draining worker that is neither a spare nor a standby should be
1421 * considered unusable to be replaced by spares.
1422 */
1423 if (PROXY_WORKER_IS_DRAINING(worker)) {
1424 if (!PROXY_WORKER_IS_SPARE(worker) && !PROXY_WORKER_IS_STANDBY(worker)) {
1426 }
1427
1428 continue;
1429 }
1430
1431 /* If the worker is in error state run retry on that worker. It will
1432 * be marked as operational if the retry timeout is elapsed. The
1433 * worker might still be unusable, but we try anyway.
1434 */
1435 if (!PROXY_WORKER_IS_USABLE(worker)) {
1436 ap_proxy_retry_worker("BALANCER", worker, r->server);
1437 }
1438
1439 if (PROXY_WORKER_IS_SPARE(worker)) {
1440 if (PROXY_WORKER_IS_USABLE(worker)) {
1441 APR_ARRAY_PUSH(spares, proxy_worker *) = worker;
1442 }
1443 }
1444 else if (PROXY_WORKER_IS_STANDBY(worker)) {
1445 if (PROXY_WORKER_IS_USABLE(worker)) {
1447 }
1448 }
1449 else if (PROXY_WORKER_IS_USABLE(worker)) {
1450 if (is_best(worker, best_worker, baton)) {
1451 best_worker = worker;
1452 }
1453 }
1454 else {
1456 }
1457 }
1458
1459 /* Check if any spares are best. */
1460 for (i = 0; (i < spares->nelts) && (i < unusable_workers); i++) {
1461 worker = APR_ARRAY_IDX(spares, i, proxy_worker *);
1462
1463 if (is_best(worker, best_worker, baton)) {
1464 best_worker = worker;
1465 }
1466 }
1467
1468 /* If no workers are available, use the standbys. */
1469 if (!best_worker) {
1470 for (i = 0; i < standbys->nelts; i++) {
1471 worker = APR_ARRAY_IDX(standbys, i, proxy_worker *);
1472
1473 if (is_best(worker, best_worker, baton)) {
1474 best_worker = worker;
1475 }
1476 }
1477 }
1478 }
1479
1480 apr_pool_destroy(tpool);
1481
1482 if (best_worker) {
1484 "proxy: %s selected worker \"%s\" : busy %" APR_SIZE_T_FMT " : lbstatus %d",
1485 balancer->lbmethod->name, best_worker->s->name_ex,
1486 best_worker->s->busy, best_worker->s->lbstatus);
1487 }
1488
1489 return best_worker;
1490}
1491
1499
1500/*
1501 * CONNECTION related...
1502 */
1503
1505{
1506 conn->sock = NULL;
1507 conn->tmp_bb = NULL;
1508 conn->connection = NULL;
1509 conn->ssl_hostname = NULL;
1510 apr_pool_clear(conn->scpool);
1511}
1512
1514{
1515 socket_cleanup(conn);
1516 conn->address = NULL;
1517 conn->addr = NULL;
1518 conn->hostname = NULL;
1519 conn->port = 0;
1520 conn->uds_path = NULL;
1521 if (conn->uds_pool) {
1522 apr_pool_clear(conn->uds_pool);
1523 }
1524}
1525
1527{
1528 ((proxy_worker *)theworker)->cp = NULL;
1529 return APR_SUCCESS;
1530}
1531
1533 server_rec *s)
1534{
1535 apr_pool_t *sp = NULL;
1536 apr_allocator_t *alloc;
1537 apr_thread_mutex_t *mutex;
1538 apr_status_t rv;
1539
1540 rv = apr_allocator_create(&alloc);
1541 if (rv == APR_SUCCESS) {
1543 if (rv == APR_SUCCESS) {
1544 apr_allocator_mutex_set(alloc, mutex);
1546 rv = apr_pool_create_ex(&sp, p, NULL, alloc);
1547 }
1548 else {
1549 apr_allocator_destroy(alloc);
1550 }
1551 }
1552 if (rv != APR_SUCCESS) {
1554 "failed to create %s pool", tag);
1556 return NULL; /* not reached */
1557 }
1560
1561 return sp;
1562}
1563
1565{
1566 proxy_conn_pool *cp;
1567
1568 /*
1569 * Alloc from the same pool as worker.
1570 * proxy_conn_pool is permanently attached to the worker.
1571 */
1572 cp = (proxy_conn_pool *)apr_pcalloc(p, sizeof(proxy_conn_pool));
1573 worker->cp = cp;
1574
1575 /*
1576 * We need a first pool (cp->pool) to maintain the connections attached to
1577 * the worker and a second one (cp->dns_pool) to maintain the DNS addresses
1578 * in use (TTL'ed, refcounted). New connections are created as/on a subpool
1579 * of cp->pool and new addresses as/on a subpool of cp->dns_pool, such that
1580 * both leaks (the subpools can be destroyed when the connections and/or
1581 * addresses are over) and race conditions (the creation/destruction of
1582 * subpools is protected by the parent pool's mutex) can be avoided.
1583 *
1584 * cp->dns_pool is created before cp->pool because when a connection on the
1585 * latter is destroyed it might destroy an address on the former, so when
1586 * the base pools are destroyed (e.g. child exit) we thusly make sure that
1587 * cp->dns_pool and its subpools are still alive when cp->pool gets killed.
1588 *
1589 * Both cp->dns_pool and cp->pool have their own allocator/mutex too since
1590 * acquiring connections and addresses don't need to contend.
1591 */
1592 cp->dns_pool = make_conn_subpool(p, "proxy_worker_dns", s);
1593 cp->pool = make_conn_subpool(p, "proxy_worker_cp", s);
1594
1595 /* When p is cleaning up the child is exiting, signal that to e.g. avoid
1596 * destroying the subpools explicitely in connection_destructor() when
1597 * they have been destroyed already by the reslist cleanup.
1598 */
1600}
1601
1603{
1604 proxy_worker *worker = conn->worker;
1605
1606 return !(conn->close
1607 || conn->forward
1608 || worker->s->disablereuse
1609 || !worker->s->is_address_reusable);
1610}
1611
1613{
1614 proxy_conn_rec *conn;
1615
1616 conn = apr_pcalloc(p, sizeof(proxy_conn_rec));
1617 conn->pool = p;
1618 conn->worker = worker;
1619
1620 /*
1621 * Create another subpool that manages the data for the
1622 * socket and the connection member of the proxy_conn_rec struct as we
1623 * destroy this data more frequently than other data in the proxy_conn_rec
1624 * struct like hostname and addr (at least in the case where we have
1625 * keepalive connections that timed out).
1626 *
1627 * XXX: this is really needed only when worker->s->is_address_reusable,
1628 * otherwise conn->scpool = conn->pool would be fine. For now we
1629 * can't change it since it's (kind of) part of the API.
1630 */
1631 apr_pool_create(&conn->scpool, p);
1632 apr_pool_tag(conn->scpool, "proxy_conn_scpool");
1633
1634 return conn;
1635}
1636
1637static void connection_cleanup(void *theconn)
1638{
1640 proxy_worker *worker = conn->worker;
1641
1642 /* Sanity check: Did we already return the pooled connection? */
1643 if (conn->inreslist) {
1644 ap_log_perror(APLOG_MARK, APLOG_ERR, 0, conn->pool, APLOGNO(00923)
1645 "Pooled connection 0x%pp for worker %s has been"
1646 " already returned to the connection pool.", conn,
1647 ap_proxy_worker_name(conn->pool, worker));
1648 return;
1649 }
1650
1651 if (conn->r) {
1652 apr_pool_destroy(conn->r->pool);
1653 conn->r = NULL;
1654 }
1655
1656 /* determine if the connection should be cleared, closed or reused */
1657 if (!worker->s->is_address_reusable) {
1658 apr_pool_t *p = conn->pool;
1660 conn = connection_make(p, worker);
1661 }
1662 else if (conn->close
1663 || conn->forward
1664 || (conn->connection
1665 && conn->connection->keepalive == AP_CONN_CLOSE)
1666 || worker->s->disablereuse) {
1667 socket_cleanup(conn);
1668 conn->close = 0;
1669 }
1670 else if (conn->is_ssl) {
1671 /* Unbind/reset the SSL connection dir config (sslconn->dc) from
1672 * r->per_dir_config, r will likely get destroyed before this proxy
1673 * conn is reused.
1674 */
1676 }
1677
1678 if (worker->s->hmax && worker->cp->res) {
1679 conn->inreslist = 1;
1680 apr_reslist_release(worker->cp->res, (void *)conn);
1681 }
1682 else {
1683 worker->cp->conn = conn;
1684 }
1685}
1686
1687/* DEPRECATED */
1689 request_rec *r)
1690{
1691 apr_status_t rv;
1692
1693 /*
1694 * If we have an existing SSL connection it might be possible that the
1695 * server sent some SSL message we have not read so far (e.g. an SSL
1696 * shutdown message if the server closed the keepalive connection while
1697 * the connection was held unused in our pool).
1698 * So ensure that if present (=> APR_NONBLOCK_READ) it is read and
1699 * processed. We don't expect any data to be in the returned brigade.
1700 */
1701 if (conn->sock && conn->connection) {
1702 rv = ap_get_brigade(conn->connection->input_filters, conn->tmp_bb,
1705 if (!APR_BRIGADE_EMPTY(conn->tmp_bb)) {
1706 apr_off_t len;
1707
1708 rv = apr_brigade_length(conn->tmp_bb, 0, &len);
1710 "SSL cleanup brigade contained %"
1711 APR_OFF_T_FMT " bytes of data.", len);
1713 }
1714 if ((rv != APR_SUCCESS) && !APR_STATUS_IS_EAGAIN(rv)) {
1715 socket_cleanup(conn);
1716 }
1717 }
1718 return APR_SUCCESS;
1719}
1720
1721/* reslist constructor */
1724{
1725 apr_pool_t *p;
1726 proxy_conn_rec *conn;
1727 proxy_worker *worker = (proxy_worker *)params;
1728
1729 /*
1730 * Create a subpool for each connection
1731 * This keeps the memory consumption constant
1732 * when it's recycled or destroyed.
1733 */
1735 apr_pool_tag(p, "proxy_conn_pool");
1736 conn = connection_make(p, worker);
1737 conn->inreslist = 1;
1738
1739 *resource = conn;
1740 return APR_SUCCESS;
1741}
1742
1743/* reslist destructor */
1746{
1747 proxy_worker *worker = params;
1748
1749 /* Destroy the pool only if not called from reslist_destroy */
1750 if (worker->cp) {
1751 proxy_conn_rec *conn = resource;
1752 apr_pool_destroy(conn->pool);
1753 }
1754
1755 return APR_SUCCESS;
1756}
1757
1758/*
1759 * WORKER related...
1760 */
1761
1763 proxy_worker *worker)
1764{
1765 if (!(*worker->s->uds_path) || !p) {
1766 /* just in case */
1767 return worker->s->name_ex;
1768 }
1769 return apr_pstrcat(p, "unix:", worker->s->uds_path, "|", worker->s->name_ex, NULL);
1770}
1771
1773 const proxy_worker *worker,
1774 const char *upgrade,
1775 const char *dflt)
1776{
1777 /* Find in worker->s->upgrade list (if any) */
1778 const char *worker_upgrade = worker->s->upgrade;
1779 if (*worker_upgrade) {
1780 return (strcmp(worker_upgrade, "*") == 0
1781 || ap_cstr_casecmp(worker_upgrade, upgrade) == 0
1782 || ap_find_token(p, worker_upgrade, upgrade));
1783 }
1784
1785 /* Compare to the provided default (if any) */
1786 return (dflt && ap_cstr_casecmp(dflt, upgrade) == 0);
1787}
1788
1789/*
1790 * Taken from ap_strcmp_match() :
1791 * Match = 0, NoMatch = 1, Abort = -1, Inval = -2
1792 * Based loosely on sections of wildmat.c by Rich Salz
1793 * Hmmm... shouldn't this really go component by component?
1794 *
1795 * Adds handling of the "<any>" => "<any>" unescaping.
1796 */
1797static int ap_proxy_strcmp_ematch(const char *str, const char *expected)
1798{
1799 apr_size_t x, y;
1800
1801 for (x = 0, y = 0; expected[y]; ++y, ++x) {
1802 if (expected[y] == '$' && apr_isdigit(expected[y + 1])) {
1803 do {
1804 y += 2;
1805 } while (expected[y] == '$' && apr_isdigit(expected[y + 1]));
1806 if (!expected[y])
1807 return 0;
1808 while (str[x]) {
1809 int ret;
1810 if ((ret = ap_proxy_strcmp_ematch(&str[x++], &expected[y])) != 1)
1811 return ret;
1812 }
1813 return -1;
1814 }
1815 else if (!str[x]) {
1816 return -1;
1817 }
1818 else if (expected[y] == '\\' && !expected[++y]) {
1819 /* NUL is an invalid char! */
1820 return -2;
1821 }
1822 if (str[x] != expected[y])
1823 return 1;
1824 }
1825 /* We got all the way through the worker path without a difference */
1826 return 0;
1827}
1828
1831 proxy_server_conf *conf,
1832 const char *url,
1833 unsigned int mask)
1834{
1835 proxy_worker *worker;
1837 int max_match = 0;
1838 int url_length;
1839 int min_match;
1841 const char *c;
1842 char *url_copy;
1843 int i;
1844
1845 if (!url) {
1846 return NULL;
1847 }
1848
1849 if (!(mask & AP_PROXY_WORKER_NO_UDS)) {
1851 if (!url) {
1852 return NULL;
1853 }
1854 }
1855
1856 c = ap_strchr_c(url, ':');
1857 if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') {
1858 return NULL;
1859 }
1860
1861 url_length = strlen(url);
1863
1864 /* Default to lookup for both _PREFIX and _MATCH workers */
1867 }
1868
1869 /*
1870 * We need to find the start of the path and
1871 * therefore we know the length of the scheme://hostname/
1872 * part to we can force-lowercase everything up to
1873 * the start of the path.
1874 */
1875 c = ap_strchr_c(c+3, '/');
1876 if (c) {
1877 char *pathstart;
1878 pathstart = url_copy + (c - url);
1879 *pathstart = '\0';
1881 min_match = strlen(url_copy);
1882 *pathstart = '/';
1883 }
1884 else {
1886 min_match = strlen(url_copy);
1887 }
1888 /*
1889 * Do a "longest match" on the worker name to find the worker that
1890 * fits best to the URL, but keep in mind that we must have at least
1891 * a minimum matching of length min_match such that
1892 * scheme://hostname[:port] matches between worker and url.
1893 */
1894
1895 if (balancer) {
1897 for (i = 0; i < balancer->workers->nelts; i++, workers++) {
1898 worker = *workers;
1899 if ( ((worker_name_length = strlen(worker->s->name_ex)) <= url_length)
1902 && (worker->s->is_name_matchable
1904 && strncmp(url_copy, worker->s->name_ex,
1905 worker_name_length) == 0))
1906 && (!worker->s->is_name_matchable
1909 worker->s->name_ex) == 0)) ) {
1910 max_worker = worker;
1912 }
1913 }
1914 } else {
1915 worker = (proxy_worker *)conf->workers->elts;
1916 for (i = 0; i < conf->workers->nelts; i++, worker++) {
1917 if ( ((worker_name_length = strlen(worker->s->name_ex)) <= url_length)
1920 && (worker->s->is_name_matchable
1922 && strncmp(url_copy, worker->s->name_ex,
1923 worker_name_length) == 0))
1924 && (!worker->s->is_name_matchable
1927 worker->s->name_ex) == 0)) ) {
1928 max_worker = worker;
1930 }
1931 }
1932 }
1933
1934 return max_worker;
1935}
1936
1939 proxy_server_conf *conf,
1940 const char *url)
1941{
1942 return ap_proxy_get_worker_ex(p, balancer, conf, url, 0);
1943}
1944
1945/*
1946 * To create a worker from scratch first we define the
1947 * specifics of the worker; this is all local data.
1948 * We then allocate space for it if data needs to be
1949 * shared. This allows for dynamic addition during
1950 * config and runtime.
1951 */
1953 proxy_worker **worker,
1955 proxy_server_conf *conf,
1956 const char *url,
1957 unsigned int mask)
1958{
1959 apr_status_t rv;
1961 const char *ptr = NULL, *sockpath = NULL, *pdollars = NULL;
1963 int address_not_reusable = 0;
1964 apr_uri_t uri;
1965
1966 /*
1967 * Look to see if we are using UDS:
1968 * require format: unix:/path/foo/bar.sock|http://ignored/path2/
1969 * This results in talking http to the socket at /path/foo/bar.sock
1970 */
1971 if (!ap_cstr_casecmpn(url, "unix:", 5)
1972 && (ptr = ap_strchr_c(url + 5, '|'))) {
1973 rv = apr_uri_parse(p, apr_pstrmemdup(p, url, ptr - url), &uri);
1974 if (rv == APR_SUCCESS) {
1976 ptr++; /* so we get the scheme for the uds */
1977 }
1978 else {
1979 ptr = url;
1980 }
1981 }
1982 else {
1983 ptr = url;
1984 }
1985
1987 /* apr_uri_parse() will accept the '$' sign anywhere in the URL but
1988 * in the :port part, and we don't want scheme://host:port$1$2/path
1989 * to fail (e.g. "ProxyPassMatch ^/(a|b)(/.*)? http://host:port$2").
1990 * So we trim all the $n from the :port and prepend them in uri.path
1991 * afterward for apr_uri_unparse() to restore the original URL below.
1992 * If a dollar substitution is found in the hostname[:port] part of
1993 * the URL, reusing address and connections in the same worker is not
1994 * possible (the current implementation of active connections cache
1995 * handles/assumes a single origin server:port per worker only), so
1996 * we set address_not_reusable here during parsing to take that into
1997 * account in the worker settings below.
1998 */
1999#define IS_REF(x) (x[0] == '$' && apr_isdigit(x[1]))
2000 const char *pos = ap_strstr_c(ptr, "://");
2001 if (pos) {
2002 pos += 3;
2003 while (*pos && *pos != ':' && *pos != '/') {
2004 if (*pos == '$') {
2006 }
2007 pos++;
2008 }
2009 if (*pos == ':') {
2010 pos++;
2011 while (*pos && !IS_REF(pos) && *pos != '/') {
2012 pos++;
2013 }
2014 if (IS_REF(pos)) {
2015 struct iovec vec[2];
2016 const char *path = pos + 2;
2017 while (*path && *path != '/') {
2018 path++;
2019 }
2020 pdollars = apr_pstrmemdup(p, pos, path - pos);
2021 vec[0].iov_base = (void *)ptr;
2022 vec[0].iov_len = pos - ptr;
2023 vec[1].iov_base = (void *)path;
2024 vec[1].iov_len = strlen(path);
2025 ptr = apr_pstrcatv(p, vec, 2, NULL);
2027 }
2028 }
2029 }
2030#undef IS_REF
2031 }
2032
2033 /* Normalize the url (worker name) */
2034 rv = apr_uri_parse(p, ptr, &uri);
2035 if (rv != APR_SUCCESS) {
2036 return apr_pstrcat(p, "Unable to parse URL: ", url, NULL);
2037 }
2038 if (!uri.scheme) {
2039 return apr_pstrcat(p, "URL must be absolute!: ", url, NULL);
2040 }
2041 if (!uri.hostname) {
2042 if (sockpath) {
2043 /* allow for unix:/path|http: */
2044 uri.hostname = "localhost";
2045 }
2046 else {
2047 return apr_pstrcat(p, "URL must be absolute!: ", url, NULL);
2048 }
2049 }
2050 else {
2051 ap_str_tolower(uri.hostname);
2052 }
2053 ap_str_tolower(uri.scheme);
2055 if (uri.port && uri.port == port_of_scheme) {
2056 uri.port = 0;
2057 }
2058 if (pdollars) {
2059 /* Restore/prepend pdollars into the path. */
2060 uri.path = apr_pstrcat(p, pdollars, uri.path, NULL);
2061 }
2063
2064 /*
2065 * Workers can be associated w/ balancers or on their
2066 * own; ie: the generic reverse-proxy or a worker
2067 * in a simple ProxyPass statement. eg:
2068 *
2069 * ProxyPass / http://www.example.com
2070 *
2071 * in which case the worker goes in the conf slot.
2072 */
2073 if (balancer) {
2075 /* recall that we get a ptr to the ptr here */
2076 runtime = apr_array_push(balancer->workers);
2077 *worker = *runtime = apr_palloc(p, sizeof(proxy_worker)); /* right to left baby */
2078 /* we've updated the list of workers associated with
2079 * this balancer *locally* */
2080 balancer->wupdated = apr_time_now();
2081 } else if (conf) {
2082 *worker = apr_array_push(conf->workers);
2083 } else {
2084 /* we need to allocate space here */
2085 *worker = apr_palloc(p, sizeof(proxy_worker));
2086 }
2087 memset(*worker, 0, sizeof(proxy_worker));
2088
2089 /* right here we just want to tuck away the worker info.
2090 * if called during config, we don't have shm setup yet,
2091 * so just note the info for later. */
2093 wshared = ap_malloc(sizeof(proxy_worker_shared)); /* will be freed ap_proxy_share_worker */
2094 else
2096 memset(wshared, 0, sizeof(proxy_worker_shared));
2097
2098 if (PROXY_STRNCPY(wshared->name_ex, ptr) != APR_SUCCESS) {
2100 "Alert! worker name (%s) too long; truncated to: %s", ptr, wshared->name_ex);
2101 }
2102 if (PROXY_STRNCPY(wshared->name, ptr) != APR_SUCCESS) {
2104 "worker name (%s) too long; truncated for legacy modules that do not use "
2105 "proxy_worker_shared->name_ex: %s", ptr, wshared->name);
2106 }
2107 if (PROXY_STRNCPY(wshared->scheme, uri.scheme) != APR_SUCCESS) {
2109 "Alert! worker scheme (%s) too long; truncated to: %s", uri.scheme, wshared->scheme);
2110 }
2111 if (PROXY_STRNCPY(wshared->hostname_ex, uri.hostname) != APR_SUCCESS) {
2112 return apr_psprintf(p, "worker hostname (%s) too long", uri.hostname);
2113 }
2114 if (PROXY_STRNCPY(wshared->hostname, uri.hostname) != APR_SUCCESS) {
2116 "worker hostname (%s) too long; truncated for legacy modules that do not use "
2117 "proxy_worker_shared->hostname_ex: %s", uri.hostname, wshared->hostname);
2118 }
2119 wshared->port = (uri.port) ? uri.port : port_of_scheme;
2120 wshared->flush_packets = flush_off;
2121 wshared->flush_wait = PROXY_FLUSH_WAIT;
2122 wshared->address_ttl = (address_not_reusable) ? 0 : -1;
2123 wshared->is_address_reusable = (address_not_reusable == 0);
2124 wshared->disablereuse = (address_not_reusable != 0);
2125 wshared->lbfactor = 100;
2126 wshared->passes = 1;
2127 wshared->fails = 1;
2129 wshared->smax = -1;
2131 wshared->hash.fnv = ap_proxy_hashfunc(wshared->name_ex, PROXY_HASHFUNC_FNV);
2132 wshared->was_malloced = (mask & AP_PROXY_WORKER_IS_MALLOCED) != 0;
2134 wshared->is_name_matchable = 1;
2135
2136 /* Before AP_PROXY_WORKER_IS_MATCH (< 2.4.47), a regex worker with
2137 * dollar substitution was never matched against any actual URL, thus
2138 * the requests fell through the generic worker. Now if a ProyPassMatch
2139 * matches, a worker (and its parameters) is always used to determine
2140 * the properties of the connection with the origin server. So for
2141 * instance the same "timeout=" will be enforced for all the requests
2142 * matched by the same ProyPassMatch worker, which is an improvement
2143 * compared to the global/vhost [Proxy]Timeout applied by the generic
2144 * worker. Likewise, address and connection reuse is the default for
2145 * a ProyPassMatch worker with no dollar substitution, just like a
2146 * "normal" worker. However to avoid DNS and connection reuse compat
2147 * issues, connection reuse is disabled by default if there is any
2148 * substitution in the uri-path (an explicit enablereuse=on can still
2149 * opt-in), and reuse is even disabled definitively for substitutions
2150 * happening in the hostname[:port] (is_address_reusable was unset
2151 * above so it will prevent enablereuse=on to apply anyway).
2152 */
2153 if (ap_strchr_c(wshared->name, '$')) {
2154 wshared->disablereuse = 1;
2155 }
2156 }
2157 if (sockpath) {
2158 if (PROXY_STRNCPY(wshared->uds_path, sockpath) != APR_SUCCESS) {
2159 return apr_psprintf(p, "worker uds path (%s) too long", sockpath);
2160 }
2161
2162 }
2163 else {
2164 *wshared->uds_path = '\0';
2165 }
2166 if (!balancer) {
2168 }
2169
2170 (*worker)->hash = wshared->hash;
2171 (*worker)->context = NULL;
2172 (*worker)->cp = NULL;
2173 (*worker)->balancer = balancer;
2174 (*worker)->s = wshared;
2175
2176 return NULL;
2177}
2178
2180 proxy_worker **worker,
2181 proxy_balancer *balancer,
2182 proxy_server_conf *conf,
2183 const char *url,
2184 int do_malloc)
2185{
2186 return ap_proxy_define_worker_ex(p, worker, balancer, conf, url,
2189 : 0));
2190}
2191
2192/* DEPRECATED */
2194 proxy_worker **worker,
2195 proxy_balancer *balancer,
2196 proxy_server_conf *conf,
2197 const char *url,
2198 int do_malloc)
2199{
2200 return ap_proxy_define_worker_ex(p, worker, balancer, conf, url,
2203 : 0));
2204}
2205
2206/*
2207 * Create an already defined worker and free up memory
2208 */
2210 int i)
2211{
2212 char *action = "copying";
2213 if (!shm || !worker->s)
2214 return APR_EINVAL;
2215
2216 if ((worker->s->hash.def != shm->hash.def) ||
2217 (worker->s->hash.fnv != shm->hash.fnv)) {
2218 memcpy(shm, worker->s, sizeof(proxy_worker_shared));
2219 if (worker->s->was_malloced)
2220 free(worker->s); /* was malloced in ap_proxy_define_worker */
2221 } else {
2222 action = "re-using";
2223 }
2224 worker->s = shm;
2225 worker->s->index = i;
2226
2230 apr_pool_tag(pool, "proxy_worker_name");
2232 "%s shm[%d] (0x%pp) for worker: %s", action, i, (void *)shm,
2233 ap_proxy_worker_name(pool, worker));
2234 if (pool) {
2236 }
2237 }
2238 return APR_SUCCESS;
2239}
2240
2242{
2245 int max_threads, minw, maxw;
2246
2247 if (worker->s->status & PROXY_WORKER_INITIALIZED) {
2248 /* The worker is already initialized */
2250 "worker %s shared already initialized",
2251 ap_proxy_worker_name(p, worker));
2252 }
2253 else {
2255 "initializing worker %s shared",
2256 ap_proxy_worker_name(p, worker));
2257 /* Set default parameters */
2258 if (!worker->s->retry_set) {
2260 }
2261 /* Consistently set address and connection reusabilty: when reuse
2262 * is disabled by configuration, or when the address is known already
2263 * to not be reusable for this worker (in any case, thus ignore/force
2264 * DisableReuse).
2265 */
2266 if (!worker->s->address_ttl || (!worker->s->address_ttl_set
2267 && worker->s->disablereuse)) {
2268 worker->s->is_address_reusable = 0;
2269 }
2270 if (!worker->s->is_address_reusable && !worker->s->disablereuse) {
2271 /* Explicit enablereuse=on can't work in this case, warn user. */
2272 if (worker->s->disablereuse_set) {
2274 "enablereuse/disablereuse ignored for worker %s",
2275 ap_proxy_worker_name(p, worker));
2276 }
2277 worker->s->disablereuse = 1;
2278 }
2279
2280 /*
2281 * When mod_http2 is loaded we might have more threads since it has
2282 * its own pool of processing threads.
2283 */
2286 if (get_h2_num_workers) {
2288 /* So now the max is:
2289 * max_threads-1 threads for HTTP/1 each requiring one connection
2290 * + one thread for HTTP/2 requiring maxw connections
2291 */
2293 }
2294 if (max_threads > 1) {
2295 /* Default hmax is max_threads to scale with the load and never
2296 * wait for an idle connection to proceed.
2297 */
2298 if (worker->s->hmax == 0) {
2299 worker->s->hmax = max_threads;
2300 }
2301 if (worker->s->smax == -1 || worker->s->smax > worker->s->hmax) {
2302 worker->s->smax = worker->s->hmax;
2303 }
2304 /* Set min to be lower than smax */
2305 if (worker->s->min > worker->s->smax) {
2306 worker->s->min = worker->s->smax;
2307 }
2308 }
2309 else {
2310 /* This will suppress the apr_reslist creation */
2311 worker->s->min = worker->s->smax = worker->s->hmax = 0;
2312 }
2313 }
2314
2315 /* What if local is init'ed and shm isn't?? Even possible? */
2316 if (worker->local_status & PROXY_WORKER_INITIALIZED) {
2318 "worker %s local already initialized",
2319 ap_proxy_worker_name(p, worker));
2320 }
2321 else {
2323 /* Check again after we got the lock if we are still uninitialized */
2324 if (!(AP_VOLATILIZE_T(unsigned int, worker->local_status) & PROXY_WORKER_INITIALIZED)) {
2326 "initializing worker %s local",
2327 ap_proxy_worker_name(p, worker));
2328 /* Now init local worker data */
2329#if APR_HAS_THREADS
2330 if (worker->tmutex == NULL) {
2331 rv = apr_thread_mutex_create(&(worker->tmutex), APR_THREAD_MUTEX_DEFAULT, p);
2332 if (rv != APR_SUCCESS) {
2334 "can not create worker thread mutex");
2336 return rv;
2337 }
2338 }
2339#endif
2340 if (worker->cp == NULL)
2341 init_conn_pool(p, worker, s);
2342 if (worker->cp == NULL) {
2344 "can not create connection pool");
2346 return APR_EGENERAL;
2347 }
2348
2349 if (worker->s->hmax) {
2350 rv = apr_reslist_create(&(worker->cp->res),
2351 worker->s->min, worker->s->smax,
2352 worker->s->hmax, worker->s->ttl,
2354 worker, worker->cp->pool);
2355
2357 "initialized pool in child %" APR_PID_T_FMT " for (%s:%d) min=%d max=%d smax=%d",
2358 getpid(), worker->s->hostname_ex, (int)worker->s->port,
2359 worker->s->min, worker->s->hmax, worker->s->smax);
2360
2361 /* Set the acquire timeout */
2362 if (rv == APR_SUCCESS && worker->s->acquire_set) {
2363 apr_reslist_timeout_set(worker->cp->res, worker->s->acquire);
2364 }
2365
2366 }
2367 else {
2368 void *conn;
2369
2370 rv = connection_constructor(&conn, worker, worker->cp->pool);
2371 worker->cp->conn = conn;
2372
2374 "initialized single connection worker in child %" APR_PID_T_FMT " for (%s:%d)",
2375 getpid(), worker->s->hostname_ex,
2376 (int)worker->s->port);
2377 }
2378 if (rv == APR_SUCCESS) {
2380 }
2381 }
2383
2384 }
2385 if (rv == APR_SUCCESS) {
2386 worker->s->status |= (PROXY_WORKER_INITIALIZED);
2387 }
2388 return rv;
2389}
2390
2391static int ap_proxy_retry_worker(const char *proxy_function, proxy_worker *worker,
2392 server_rec *s)
2393{
2394 if (worker->s->status & PROXY_WORKER_IN_ERROR) {
2397 "%s: Won't retry worker (%s:%d): stopped",
2398 proxy_function, worker->s->hostname_ex,
2399 (int)worker->s->port);
2400 return DECLINED;
2401 }
2402 if ((worker->s->status & PROXY_WORKER_IGNORE_ERRORS)
2403 || apr_time_now() > worker->s->error_time + worker->s->retry) {
2404 ++worker->s->retries;
2405 worker->s->status &= ~PROXY_WORKER_IN_ERROR;
2407 "%s: worker for (%s:%d) has been marked for retry",
2408 proxy_function, worker->s->hostname_ex,
2409 (int)worker->s->port);
2410 return OK;
2411 }
2412 else {
2414 "%s: too soon to retry worker for (%s:%d)",
2415 proxy_function, worker->s->hostname_ex,
2416 (int)worker->s->port);
2417 return DECLINED;
2418 }
2419 }
2420 else {
2421 return OK;
2422 }
2423}
2424
2425/*
2426 * In the case of the reverse proxy, we need to see if we
2427 * were passed a UDS url (eg: from mod_proxy) and adjust uds_path
2428 * as required.
2429 */
2431{
2432 char *uds_url = r->filename + 6, *origin_url;
2433
2434 if (!strncmp(r->filename, "proxy:", 6) &&
2435 !ap_cstr_casecmpn(uds_url, "unix:", 5) &&
2436 (origin_url = ap_strchr(uds_url + 5, '|'))) {
2437 char *uds_path = NULL, *end;
2439 apr_status_t rv;
2440
2441 *origin_url = '\0';
2443 *origin_url++ = '|';
2444
2445 if (rv == APR_SUCCESS && urisock.path && (!urisock.hostname
2446 || !urisock.hostname[0])) {
2447 uds_path = ap_runtime_dir_relative(r->pool, urisock.path);
2448 }
2449 if (!uds_path || !(end = ap_strchr(origin_url, ':'))) {
2451 "Invalid proxy UDS filename (%s)", r->filename);
2452 apr_table_unset(r->notes, "uds_path");
2453 return HTTP_BAD_REQUEST;
2454 }
2455 apr_table_setn(r->notes, "uds_path", uds_path);
2456
2458 "*: fixup UDS from %s: %s (%s)",
2459 r->filename, origin_url, uds_path);
2460
2461 /* The hostname part of the URL is not mandated for UDS though
2462 * the canon_handler hooks will require it, so add "localhost"
2463 * if it's missing (won't be used anyway for an AF_UNIX socket).
2464 */
2465 if (!end[1]) {
2466 r->filename = apr_pstrcat(r->pool, "proxy:",
2467 origin_url, "//localhost",
2468 NULL);
2469 }
2470 else if (end[1] == '/' && end[2] == '/' && !end[3]) {
2471 r->filename = apr_pstrcat(r->pool, "proxy:",
2472 origin_url, "localhost",
2473 NULL);
2474 }
2475 else {
2476 /* Overwrite the UDS part of r->filename in place */
2477 memmove(uds_url, origin_url, strlen(origin_url) + 1);
2478 }
2479 return OK;
2480 }
2481
2482 apr_table_unset(r->notes, "uds_path");
2483 return DECLINED;
2484}
2485
2486/* Deprecated (unused upstream) */
2491
2493 const char *str)
2494{
2495 /* Interpolate an env str in a configuration string
2496 * Syntax ${var} --> value_of(var)
2497 * Method: replace one var, and recurse on remainder of string
2498 * Nothing clever here, and crap like nested vars may do silly things
2499 * but we'll at least avoid sending the unwary into a loop
2500 */
2501 const char *start;
2502 const char *end;
2503 const char *var;
2504 const char *val;
2505 const char *firstpart;
2506
2507 start = ap_strstr_c(str, "${");
2508 if (start == NULL) {
2509 return str;
2510 }
2511 end = ap_strchr_c(start+2, '}');
2512 if (end == NULL) {
2513 return str;
2514 }
2515 /* OK, this is syntax we want to interpolate. Is there such a var ? */
2516 var = apr_pstrmemdup(r->pool, start+2, end-(start+2));
2519
2520 if (val == NULL) {
2521 return apr_pstrcat(r->pool, firstpart,
2523 }
2524 else {
2525 return apr_pstrcat(r->pool, firstpart, val,
2527 }
2528}
2529
2531{
2532 int i;
2534 sizeof (struct proxy_alias));
2535 struct proxy_alias *old = (struct proxy_alias *) hdr->elts;
2536
2537 for (i = 0; i < hdr->nelts; ++i) {
2539 newcopy->fake = (old[i].flags & PROXYPASS_INTERPOLATE)
2540 ? ap_proxy_interpolate(r, old[i].fake) : old[i].fake;
2541 newcopy->real = (old[i].flags & PROXYPASS_INTERPOLATE)
2542 ? ap_proxy_interpolate(r, old[i].real) : old[i].real;
2543 }
2544 return ret;
2545}
2546
2548{
2549 char *url, *p;
2550 int access_status;
2552 &proxy_module);
2553
2554 if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
2555 return DECLINED;
2556
2557 /* Put the UDS path appart if any (and not already stripped) */
2558 if (r->proxyreq == PROXYREQ_REVERSE) {
2561 return access_status;
2562 }
2563 }
2564
2565 /* Keep this after fixup_uds_filename() */
2566 url = apr_pstrdup(r->pool, r->filename + 6);
2567
2568 if ((dconf->interpolate_env == 1) && (r->proxyreq == PROXYREQ_REVERSE)) {
2569 /* create per-request copy of reverse proxy conf,
2570 * and interpolate vars in it
2571 */
2573 ap_set_module_config(r->request_config, &proxy_module, rconf);
2574 rconf->raliases = proxy_vars(r, dconf->raliases);
2575 rconf->cookie_paths = proxy_vars(r, dconf->cookie_paths);
2576 rconf->cookie_domains = proxy_vars(r, dconf->cookie_domains);
2577 }
2578
2579 /* canonicalise each specific scheme */
2581 return access_status;
2582 }
2583
2584 p = strchr(url, ':');
2585 if (p == NULL || p == url)
2586 return HTTP_BAD_REQUEST;
2587
2588 return OK; /* otherwise; we've done the best we can */
2589}
2590
2593 request_rec *r,
2594 proxy_server_conf *conf, char **url)
2595{
2596 int access_status;
2597
2599 if (access_status == DECLINED && *balancer == NULL) {
2600 /* UDS path stripped from *url by proxy_fixup() already */
2601 *worker = ap_proxy_get_worker_ex(r->pool, NULL, conf, *url,
2603 if (*worker) {
2605 "%s: found worker %s for %s",
2606 (*worker)->s->scheme, (*worker)->s->name_ex, *url);
2607 access_status = OK;
2608 }
2609 else if (r->proxyreq == PROXYREQ_PROXY) {
2610 if (conf->forward) {
2612 "*: found forward proxy worker for %s", *url);
2613 *worker = conf->forward;
2614 access_status = OK;
2615 /*
2616 * The forward worker does not keep connections alive, so
2617 * ensure that mod_proxy_http does the correct thing
2618 * regarding the Connection header in the request.
2619 */
2620 apr_table_setn(r->subprocess_env, "proxy-nokeepalive", "1");
2621 }
2622 }
2623 else if (r->proxyreq == PROXYREQ_REVERSE) {
2624 if (conf->reverse) {
2626 "*: using default reverse proxy worker for %s "
2627 "(no keepalive)", *url);
2628 *worker = conf->reverse;
2629 access_status = OK;
2630 /*
2631 * The reverse worker does not keep connections alive, so
2632 * ensure that mod_proxy_http does the correct thing
2633 * regarding the Connection header in the request.
2634 */
2635 apr_table_setn(r->subprocess_env, "proxy-nokeepalive", "1");
2636 }
2637 }
2638 }
2639 else if (access_status == DECLINED && *balancer != NULL) {
2640 /* All the workers are busy */
2642 "all workers are busy. Unable to serve %s", *url);
2644 }
2645
2646 return access_status;
2647}
2648
2651 request_rec *r,
2652 proxy_server_conf *conf)
2653{
2654 int access_status = OK;
2655 if (balancer) {
2657 if (access_status == DECLINED) {
2658 access_status = OK; /* no post_request handler available */
2659 /* TODO: recycle direct worker */
2660 }
2661 }
2662
2663 return access_status;
2664}
2665
2666/* DEPRECATED */
2668 const char *proxy_function,
2670 const char *backend_name,
2671 proxy_server_conf *conf,
2672 request_rec *r)
2673{
2674 apr_status_t rv;
2675 int connected = 0;
2676 int loglevel;
2677
2678 while (backend_addr && !connected) {
2679 if ((rv = apr_socket_create(newsock, backend_addr->family,
2680 SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) {
2681 loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
2682 ap_log_rerror(APLOG_MARK, loglevel, rv, r, APLOGNO(00935)
2683 "%s: error creating fam %d socket for target %s",
2685 /*
2686 * this could be an IPv6 address from the DNS but the
2687 * local machine won't give us an IPv6 socket; hopefully the
2688 * DNS returned an additional address to try
2689 */
2690 backend_addr = backend_addr->next;
2691 continue;
2692 }
2693
2694 if (conf->recv_buffer_size > 0 &&
2696 conf->recv_buffer_size))) {
2698 "apr_socket_opt_set(SO_RCVBUF): Failed to set "
2699 "ProxyReceiveBufferSize, using default");
2700 }
2701
2703 if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
2705 "apr_socket_opt_set(APR_TCP_NODELAY): "
2706 "Failed to set");
2707 }
2708
2709 /* Set a timeout on the socket */
2710 if (conf->timeout_set) {
2712 }
2713 else {
2715 }
2716
2718 "%s: fam %d socket created to connect to %s",
2720
2721 if (conf->source_address) {
2722 apr_sockaddr_t *local_addr;
2723 /* Make a copy since apr_socket_bind() could change
2724 * conf->source_address, which we don't want.
2725 */
2726 local_addr = apr_pmemdup(r->pool, conf->source_address,
2727 sizeof(apr_sockaddr_t));
2728 local_addr->pool = r->pool;
2729 rv = apr_socket_bind(*newsock, local_addr);
2730 if (rv != APR_SUCCESS) {
2732 "%s: failed to bind socket to local address",
2734 }
2735 }
2736
2737 /* make the connection out of the socket */
2739
2740 /* if an error occurred, loop round and try again */
2741 if (rv != APR_SUCCESS) {
2743 loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
2744 ap_log_rerror(APLOG_MARK, loglevel, rv, r, APLOGNO(00939)
2745 "%s: attempt to connect to %pI (%s) failed",
2747 backend_addr = backend_addr->next;
2748 continue;
2749 }
2750 connected = 1;
2751 }
2752 return connected ? 0 : 1;
2753}
2754
2756 proxy_conn_rec **conn,
2757 proxy_worker *worker,
2758 server_rec *s)
2759{
2760 apr_status_t rv;
2761
2762 if (!PROXY_WORKER_IS_USABLE(worker)) {
2763 /* Retry the worker */
2765
2766 if (!PROXY_WORKER_IS_USABLE(worker)) {
2768 "%s: disabled connection for (%s:%d)",
2769 proxy_function, worker->s->hostname_ex,
2770 (int)worker->s->port);
2772 }
2773 }
2774
2775 if (worker->s->hmax && worker->cp->res) {
2776 rv = apr_reslist_acquire(worker->cp->res, (void **)conn);
2777 }
2778 else {
2779 /* create the new connection if the previous was destroyed */
2780 if (!worker->cp->conn) {
2781 rv = connection_constructor((void **)conn, worker, worker->cp->pool);
2782 }
2783 else {
2784 *conn = worker->cp->conn;
2785 worker->cp->conn = NULL;
2786 rv = APR_SUCCESS;
2787 }
2788 }
2789
2790 if (rv != APR_SUCCESS) {
2792 "%s: failed to acquire connection for (%s:%d)",
2793 proxy_function, worker->s->hostname_ex,
2794 (int)worker->s->port);
2796 }
2798 "%s: has acquired connection for (%s:%d)",
2799 proxy_function, worker->s->hostname_ex,
2800 (int)worker->s->port);
2801
2802 (*conn)->worker = worker;
2803 (*conn)->close = 0;
2804 (*conn)->inreslist = 0;
2805
2806 return OK;
2807}
2808
2810 proxy_conn_rec *conn,
2811 server_rec *s)
2812{
2814 "%s: has released connection for (%s:%d)",
2816 (int)conn->worker->s->port);
2817 connection_cleanup(conn);
2818
2819 return OK;
2820}
2821
2827
2829{
2830 /* Use _add32(, -1) since _dec32()'s returned value does not help */
2831 apr_uint32_t old = apr_atomic_add32(&address->refcount, -1);
2832 ap_assert(old > 0);
2833 if (old == 1) {
2834 apr_pool_destroy(address->addr->pool);
2835 }
2836}
2837
2843
2845{
2846 /* No _readptr() so let's _casptr(, NULL, NULL) instead */
2847 return apr_atomic_casptr((void *)&worker->address, NULL, NULL);
2848}
2849
2850/* XXX: Call when PROXY_THREAD_LOCK()ed only! */
2853{
2854 proxy_address *old = apr_atomic_xchgptr((void *)&worker->address, to);
2855 if (old && old != to) {
2857 }
2858}
2859
2862 const char *hostname,
2863 apr_port_t hostport,
2864 const char *proxy_function,
2866{
2867 apr_status_t rv;
2868 apr_pool_t *pool = NULL;
2869
2870 apr_pool_create(&pool, worker->cp->dns_pool);
2872 hostport, 0, pool);
2873 if (rv != APR_SUCCESS) {
2874 if (r && !s) {
2877 "DNS lookup failure for: ",
2878 hostname, NULL),
2879 rv);
2880 }
2881 else if (r) {
2883 "%s: resolving worker %s address",
2885 }
2886 else {
2888 "%s: resolving worker %s address",
2890 }
2892 return rv;
2893 }
2894
2895 if (r ? APLOGrdebug(r) : APLOGdebug(s)) {
2896 char *addrs = NULL;
2898 for (; addr; addr = addr->next) {
2899 addrs = apr_psprintf(pool, "%s%s%pI",
2900 addrs ? addrs : "",
2901 addrs ? ", " : "",
2902 addr);
2903 }
2904 if (r) {
2906 "%s: %s resolved to %s",
2907 proxy_function, hostname, addrs);
2908 }
2909 else {
2911 "%s: %s resolved to %s",
2912 proxy_function, hostname, addrs);
2913 }
2914 }
2915
2916 return APR_SUCCESS;
2917}
2918
2920 const apr_sockaddr_t *addr2)
2921{
2922 const apr_sockaddr_t *base2 = addr2, *pos2;
2923 while (addr1 && addr2) {
2924 for (pos2 = base2; pos2; pos2 = pos2->next) {
2926 break;
2927 }
2928 }
2929 if (!pos2) {
2930 return 0;
2931 }
2932 addr1 = addr1->next;
2933 addr2 = addr2->next;
2934 }
2935 if (addr1 || addr2) {
2936 return 0;
2937 }
2938 return 1;
2939}
2940
2942 proxy_conn_rec *conn,
2943 const char *hostname,
2944 apr_port_t hostport,
2945 unsigned int flags,
2946 request_rec *r,
2947 server_rec *s)
2948{
2949 proxy_worker *worker = conn->worker;
2950 apr_status_t rv;
2951
2952 /*
2953 * Worker can have the single constant backend adress.
2954 * The single DNS lookup is used once per worker.
2955 * If dynamic change is needed then set the addr to NULL
2956 * inside dynamic config to force the lookup.
2957 * The worker's addressTTL parameter may also be configured
2958 * to perform the DNS lookups only when the TTL expires,
2959 * or each time if that TTL is zero.
2960 */
2961 if (!worker->s->is_address_reusable) {
2962 conn->hostname = apr_pstrdup(conn->pool, hostname);
2963 conn->port = hostport;
2964
2966 hostport, 0, conn->pool);
2967 if (rv != APR_SUCCESS) {
2968 if (r && !s) {
2970 apr_pstrcat(r->pool, "DNS lookup failure for: ",
2971 hostname, NULL), rv);
2972 }
2973 else if (r) {
2975 "%s: resolving backend %s address",
2977 }
2978 else {
2980 "%s: resolving backend %s address",
2982 }
2983 return rv;
2984 }
2985 }
2986 else {
2989 apr_int32_t ttl = worker->s->address_ttl;
2990 apr_uint32_t now = 0;
2991
2993 /* The caller wants to check if the address changed, return
2994 * APR_EEXIST if not, otherwise fall through to update the
2995 * worker's for everyone to switch.
2996 */
2997 if (!conn->addr) {
2998 /* Need something to compare with */
2999 return APR_EINVAL;
3000 }
3001 rv = worker_address_resolve(worker, &addr,
3002 hostname, hostport,
3003 proxy_function, r, s);
3004 if (rv != APR_SUCCESS) {
3005 return rv;
3006 }
3007 if (proxy_addrs_equal(conn->addr, addr)) {
3009 return APR_EEXIST;
3010 }
3011 }
3012
3013 AP_DEBUG_ASSERT(ttl != 0);
3014 if (ttl > 0) {
3015 /* TODO: use a monotonic clock here */
3017 }
3018
3019 /* Addresses are refcounted, destroyed when their refcount reaches 0.
3020 *
3021 * One ref is taken by worker->address as the worker's current/latest
3022 * address, it's dropped when that address expires/changes (see below).
3023 * The other refs are taken by the connections when using/switching to
3024 * the current worker address (also below), they are dropped when the
3025 * conns are destroyed (by the reslist though it should never happen
3026 * if hmax is greater than the number of threads) OR for an expired
3027 * conn->address when it's replaced by the new worker->address below.
3028 *
3029 * Dereferencing worker->address requires holding the worker mutex or
3030 * some concurrent connection processing might change/destroy it at any
3031 * time. So only conn->address is safe to dereference anywhere (unless
3032 * NULL..) since it has at least the lifetime of the connection.
3033 */
3034 if (!addr) {
3035 address = worker_address_get(worker);
3036 }
3037 if (!address
3038 || conn->address != address
3039 || apr_atomic_read32(&address->expiry) <= now) {
3040 PROXY_THREAD_LOCK(worker);
3041
3042 /* Re-check while locked, might be a new address already */
3043 if (!addr) {
3044 address = worker_address_get(worker);
3045 }
3046 if (!address || apr_atomic_read32(&address->expiry) <= now) {
3047 if (!addr) {
3048 rv = worker_address_resolve(worker, &addr,
3049 hostname, hostport,
3050 proxy_function, r, s);
3051 if (rv != APR_SUCCESS) {
3052 PROXY_THREAD_UNLOCK(worker);
3053 return rv;
3054 }
3055
3056 /* Recompute "now" should the DNS be slow
3057 * TODO: use a monotonic clock here
3058 */
3060 }
3061
3062 address = apr_pcalloc(addr->pool, sizeof(*address));
3063 address->hostname = apr_pstrdup(addr->pool, hostname);
3064 address->hostport = hostport;
3065 address->addr = addr;
3066
3067 if (ttl > 0) {
3068 /* We keep each worker's expiry date shared accross all the
3069 * children so that they update their address at the same
3070 * time, regardless of whether a specific child forced an
3071 * address to expire at some point (for connect() issues).
3072 */
3073 address->expiry = apr_atomic_read32(&worker->s->address_expiry);
3074 if (address->expiry <= now) {
3075 apr_uint32_t prev, next = (now + ttl) - (now % ttl);
3076 do {
3077 prev = apr_atomic_cas32(&worker->s->address_expiry,
3078 next, address->expiry);
3079 if (prev == address->expiry) {
3080 address->expiry = next;
3081 break;
3082 }
3083 address->expiry = prev;
3084 } while (prev <= now);
3085 }
3086 }
3087 else {
3088 /* Never expires */
3089 address->expiry = APR_UINT32_MAX;
3090 }
3091
3092 /* One ref is for worker->address in any case */
3093 if (worker->address || worker->cp->addr) {
3094 apr_atomic_set32(&address->refcount, 1);
3095 }
3096 else {
3097 /* Set worker->cp->addr once for compat with third-party
3098 * modules. This addr never changed before and can't change
3099 * underneath users now because of some TTL configuration.
3100 * So we take one more ref for worker->cp->addr to remain
3101 * allocated forever (though it might not be up to date..).
3102 * Modules should use conn->addr instead of worker->cp-addr
3103 * to get the actual address used by each conn, determined
3104 * at connect() time.
3105 */
3106 apr_atomic_set32(&address->refcount, 2);
3107 worker->cp->addr = address->addr;
3108 }
3109
3110 /* Publish the changes. The old worker address (if any) is no
3111 * longer used by this worker, it will be destroyed now if the
3112 * worker is the last user (refcount == 1) or by the last conn
3113 * using it (refcount > 1).
3114 */
3115 worker_address_set(worker, address);
3116 }
3117
3118 /* Take the ref for conn->address (before dropping the mutex so to
3119 * let no chance for this address be killed before it's used!)
3120 */
3122
3123 PROXY_THREAD_UNLOCK(worker);
3124
3125 /* Release the old conn address */
3126 if (conn->address) {
3127 /* On Windows and OS/2, apr_socket_connect() called from
3128 * ap_proxy_connect_backend() does a simple pointer copy of
3129 * its given conn->addr[->next] into conn->sock->remote_addr.
3130 * Thus conn->addr cannot be freed if the conn->sock should be
3131 * kept alive (same new and old addresses) and the old address
3132 * is still in conn->sock->remote_addr. In this case we rather
3133 * delay the release of the old address by moving the cleanup
3134 * to conn->scpool such that it runs when the socket is closed.
3135 * In any other case, including other platforms, just release
3136 * the old address now since conn->sock->remote_addr is either
3137 * obsolete (socket forcibly closed) or a copy on conn->scpool
3138 * already (not a dangling pointer).
3139 */
3140 int keep_addr_alive = 0,
3141 keep_conn_alive = (conn->sock && conn->addr &&
3142 proxy_addrs_equal(conn->addr,
3143 address->addr));
3144 if (keep_conn_alive) {
3145#if defined(WIN32) || defined(OS2)
3146 apr_sockaddr_t *remote_addr = NULL;
3147 apr_socket_addr_get(&remote_addr, APR_REMOTE, conn->sock);
3148 for (addr = conn->addr; addr; addr = addr->next) {
3149 if (addr == remote_addr) {
3150 keep_addr_alive = 1;
3151 break;
3152 }
3153 }
3154#else
3155 /* Nothing to do, keep_addr_alive = 0 */
3156#endif
3157 }
3158 else if (conn->sock && (r ? APLOGrdebug(r) : APLOGdebug(s))) {
3159 apr_sockaddr_t *local_addr = NULL;
3160 apr_sockaddr_t *remote_addr = NULL;
3161 apr_socket_addr_get(&local_addr, APR_LOCAL, conn->sock);
3162 apr_socket_addr_get(&remote_addr, APR_REMOTE, conn->sock);
3163 if (r) {
3165 "%s: closing connection to %s (%pI<>%pI) on "
3166 "address change", proxy_function, hostname,
3167 local_addr, remote_addr);
3168 }
3169 else {
3171 "%s: closing connection to %s (%pI<>%pI) on "
3172 "address change", proxy_function, hostname,
3173 local_addr, remote_addr);
3174 }
3175 }
3176 if (keep_addr_alive) {
3177 apr_pool_cleanup_kill(conn->pool, conn->address,
3182 }
3183 else {
3184 apr_pool_cleanup_run(conn->pool, conn->address,
3186 if (!keep_conn_alive) {
3187 conn_cleanup(conn);
3188 }
3189 }
3190 }
3191
3192 /* Use the new address */
3196 conn->address = address;
3197 conn->hostname = address->hostname;
3198 conn->port = address->hostport;
3199 conn->addr = address->addr;
3200 }
3201 }
3202
3203 return APR_SUCCESS;
3204}
3205
3206PROXY_DECLARE(int)
3208 proxy_server_conf *conf,
3209 proxy_worker *worker,
3210 proxy_conn_rec *conn,
3211 apr_uri_t *uri,
3212 char **url,
3213 const char *proxyname,
3215 char *server_portstr,
3217{
3218 int server_port;
3219 const char *uds_path;
3220
3221 /*
3222 * Break up the URL to determine the host to connect to
3223 */
3224
3225 /* we break the URL into host, port, uri */
3226 if (APR_SUCCESS != apr_uri_parse(p, *url, uri)) {
3228 apr_pstrcat(p,"URI cannot be parsed: ", *url,
3229 NULL));
3230 }
3231
3232 if (!uri->hostname) {
3234 apr_pstrcat(p,"URI has no hostname: ", *url,
3235 NULL));
3236 }
3237
3238 if (!uri->port) {
3239 uri->port = ap_proxy_port_of_scheme(uri->scheme);
3240 }
3241
3243 "connecting %s to %s:%d", *url, uri->hostname, uri->port);
3244
3245 /* Close a possible existing socket if we are told to do so */
3246 if (conn->close) {
3247 socket_cleanup(conn);
3248 conn->close = 0;
3249 }
3250
3251 /*
3252 * allocate these out of the specified connection pool
3253 * The scheme handler decides if this is permanent or
3254 * short living pool.
3255 */
3256 /* Unless we are connecting the backend via a (forward Proxy)Remote, we
3257 * have to use the original form of the URI (non absolute), but this is
3258 * also the case via a remote proxy using the CONNECT method since the
3259 * original request (and URI) is to be embedded in the body.
3260 */
3261 if (!proxyname || conn->is_ssl) {
3262 *url = apr_pstrcat(p, uri->path, uri->query ? "?" : "",
3263 uri->query ? uri->query : "",
3264 uri->fragment ? "#" : "",
3265 uri->fragment ? uri->fragment : "", NULL);
3266 }
3267 /*
3268 * Figure out if our passed in proxy_conn_rec has a usable
3269 * address cached.
3270 *
3271 * TODO: Handle this much better...
3272 *
3273 * XXX: If generic workers are ever address-reusable, we need
3274 * to check host and port on the conn and be careful about
3275 * spilling the cached addr from the worker.
3276 */
3277 uds_path = (*worker->s->uds_path
3278 ? worker->s->uds_path
3279 : apr_table_get(r->notes, "uds_path"));
3280 if (uds_path) {
3281 if (!conn->uds_path || strcmp(conn->uds_path, uds_path) != 0) {
3282 apr_pool_t *pool = conn->pool;
3283 if (conn->uds_path) {
3284 conn_cleanup(conn);
3285 if (!conn->uds_pool) {
3286 apr_pool_create(&conn->uds_pool, worker->cp->dns_pool);
3287 }
3288 pool = conn->uds_pool;
3289 }
3290 /*
3291 * In UDS cases, some structs are NULL. Protect from de-refs
3292 * and provide info for logging at the same time.
3293 */
3294#if APR_HAVE_SOCKADDR_UN
3295 apr_sockaddr_info_get(&conn->addr, uds_path, APR_UNIX, 0, 0, pool);
3296 if (conn->addr && conn->addr->hostname) {
3297 conn->uds_path = conn->addr->hostname;
3298 }
3299 else {
3300 conn->uds_path = apr_pstrdup(pool, uds_path);
3301 }
3302#else
3303 apr_sockaddr_info_get(&conn->addr, NULL, APR_UNSPEC, 0, 0, pool);
3304 conn->uds_path = apr_pstrdup(pool, uds_path);
3305#endif
3306 conn->hostname = apr_pstrdup(pool, uri->hostname);
3307 conn->port = uri->port;
3308 }
3310 "%s: has determined UDS as %s (for %s:%hu)",
3311 uri->scheme, conn->uds_path, conn->hostname, conn->port);
3312 }
3313 else {
3314 const char *hostname = uri->hostname;
3315 apr_port_t hostport = uri->port;
3316
3317 /* Not a remote CONNECT until further notice */
3318 conn->forward = NULL;
3319
3320 if (proxyname) {
3322 hostport = proxyport;
3323
3324 /*
3325 * If we have a remote proxy and the protocol is HTTPS,
3326 * then we need to prepend a HTTP CONNECT request before
3327 * sending our actual HTTPS requests.
3328 */
3329 if (conn->is_ssl) {
3330 forward_info *forward;
3331 const char *proxy_auth;
3332
3333 /* Do we want to pass Proxy-Authorization along?
3334 * If we haven't used it, then YES
3335 * If we have used it then MAYBE: RFC2616 says we MAY propagate it.
3336 * So let's make it configurable by env.
3337 * The logic here is the same used in mod_proxy_http.
3338 */
3339 proxy_auth = apr_table_get(r->notes, "proxy-basic-creds");
3340 if (proxy_auth == NULL
3341 && (r->user == NULL /* we haven't yet authenticated */
3342 || apr_table_get(r->subprocess_env, "Proxy-Chain-Auth"))) {
3343 proxy_auth = apr_table_get(r->headers_in, "Proxy-Authorization");
3344 }
3345 if (proxy_auth != NULL && proxy_auth[0] == '\0') {
3346 proxy_auth = NULL;
3347 }
3348
3349 /* Reset forward info if they changed */
3350 if (!(forward = conn->forward)
3351 || forward->target_port != uri->port
3352 || ap_cstr_casecmp(forward->target_host, uri->hostname) != 0
3353 || (forward->proxy_auth != NULL) != (proxy_auth != NULL)
3354 || (forward->proxy_auth != NULL && proxy_auth != NULL &&
3355 strcmp(forward->proxy_auth, proxy_auth) != 0)) {
3356 apr_pool_t *fwd_pool = conn->pool;
3357 if (worker->s->is_address_reusable) {
3358 if (conn->fwd_pool) {
3359 apr_pool_clear(conn->fwd_pool);
3360 }
3361 else {
3362 apr_pool_create(&conn->fwd_pool, conn->pool);
3363 }
3364 fwd_pool = conn->fwd_pool;
3365 }
3366 forward = apr_pcalloc(fwd_pool, sizeof(forward_info));
3367 conn->forward = forward;
3368
3369 /*
3370 * Save our real backend data for using it later during HTTP CONNECT.
3371 */
3372 forward->use_http_connect = 1;
3373 forward->target_host = apr_pstrdup(fwd_pool, uri->hostname);
3374 forward->target_port = uri->port;
3375 if (proxy_auth) {
3376 forward->proxy_auth = apr_pstrdup(fwd_pool, proxy_auth);
3377 }
3378 }
3379 }
3380 }
3381
3382 if (conn->hostname
3383 && (conn->port != hostport
3384 || ap_cstr_casecmp(conn->hostname, hostname) != 0)) {
3385 conn_cleanup(conn);
3386 }
3387
3388 /* Resolve the connection address with the determined hostname/port */
3389 if (ap_proxy_determine_address(uri->scheme, conn, hostname, hostport,
3390 0, r, NULL)) {
3392 }
3393 }
3394
3395 /* Get the server port for the Via headers */
3399 server_portstr[0] = '\0';
3400 }
3401 else {
3402 apr_snprintf(server_portstr, server_portstr_size, ":%d",
3403 server_port);
3404 }
3405
3406 /* check if ProxyBlock directive on this host */
3407 if (OK != ap_proxy_checkproxyblock2(r, conf, uri->hostname,
3408 proxyname ? NULL : conn->addr)) {
3410 "Connect to remote machine blocked");
3411 }
3412 /*
3413 * When SSL is configured, determine the hostname (SNI) for the request
3414 * and save it in conn->ssl_hostname. Close any reused connection whose
3415 * SNI differs.
3416 */
3417 if (conn->is_ssl) {
3418 proxy_dir_conf *dconf;
3419 const char *ssl_hostname;
3420 /*
3421 * In the case of ProxyPreserveHost on use the hostname of
3422 * the request if present otherwise use the one from the
3423 * backend request URI.
3424 */
3425 dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
3426 if (dconf->preserve_host) {
3427 ssl_hostname = r->hostname;
3428 }
3429 else if (conn->forward
3430 && ((forward_info *)(conn->forward))->use_http_connect) {
3431 ssl_hostname = ((forward_info *)conn->forward)->target_host;
3432 }
3433 else {
3434 ssl_hostname = conn->hostname;
3435 }
3436 /*
3437 * Close if a SNI is in use but this request requires no or
3438 * a different one, or no SNI is in use but one is required.
3439 */
3440 if ((conn->ssl_hostname && (!ssl_hostname ||
3441 strcasecmp(conn->ssl_hostname,
3442 ssl_hostname) != 0)) ||
3443 (!conn->ssl_hostname && ssl_hostname && conn->sock)) {
3444 socket_cleanup(conn);
3445 }
3446 if (conn->ssl_hostname == NULL) {
3447 conn->ssl_hostname = apr_pstrdup(conn->scpool, ssl_hostname);
3448 }
3449 }
3451 "connecting %s to %pI (%s:%hu)", *url,
3452 conn->addr, conn->hostname, conn->port);
3453 return OK;
3454}
3455
3456#define USE_ALTERNATE_IS_CONNECTED 1
3457
3458#if !defined(APR_MSG_PEEK) && defined(MSG_PEEK)
3459#define APR_MSG_PEEK MSG_PEEK
3460#endif
3461
3462#if USE_ALTERNATE_IS_CONNECTED && defined(APR_MSG_PEEK)
3464{
3465 apr_pollfd_t pfds[1];
3468
3469 pfds[0].reqevents = APR_POLLIN;
3470 pfds[0].desc_type = APR_POLL_SOCKET;
3471 pfds[0].desc.s = socket;
3472
3473 do {
3474 status = apr_poll(&pfds[0], 1, &nfds, 0);
3475 } while (APR_STATUS_IS_EINTR(status));
3476
3477 if (status == APR_SUCCESS && nfds == 1 &&
3478 pfds[0].rtnevents == APR_POLLIN) {
3479 apr_sockaddr_t unused;
3480 apr_size_t len = 1;
3481 char buf[1];
3482 /* The socket might be closed in which case
3483 * the poll will return POLLIN.
3484 * If there is no data available the socket
3485 * is closed.
3486 */
3488 &buf[0], &len);
3489 if (status == APR_SUCCESS && len)
3490 return 1;
3491 else
3492 return 0;
3493 }
3495 return 1;
3496 }
3497 return 0;
3498
3499}
3500#else
3502
3503{
3504 apr_size_t buffer_len = 1;
3505 char test_buffer[1];
3508
3509 /* save timeout */
3511 /* set no timeout */
3514 /* put back old timeout */
3518 return 0;
3519 }
3520 else {
3521 return 1;
3522 }
3523}
3524#endif /* USE_ALTERNATE_IS_CONNECTED */
3525
3526
3527/*
3528 * Send a HTTP CONNECT request to a forward proxy.
3529 * The proxy is given by "backend", the target server
3530 * is contained in the "forward" member of "backend".
3531 */
3533 server_rec *s)
3534{
3535 int status;
3537 apr_size_t left;
3538 int complete = 0;
3539 char buffer[HUGE_STRING_LEN];
3541 forward_info *forward = (forward_info *)backend->forward;
3542 int len = 0;
3543
3545 "CONNECT: sending the CONNECT request for %s:%d "
3546 "to the remote proxy %pI (%s)",
3547 forward->target_host, forward->target_port,
3548 backend->addr, backend->hostname);
3549 /* Create the CONNECT request */
3550 nbytes = apr_snprintf(buffer, sizeof(buffer),
3551 "CONNECT %s:%d HTTP/1.0" CRLF,
3552 forward->target_host, forward->target_port);
3553 /* Add proxy authorization from the configuration, or initial
3554 * request if necessary */
3555 if (forward->proxy_auth != NULL) {
3556 nbytes += apr_snprintf(buffer + nbytes, sizeof(buffer) - nbytes,
3557 "Proxy-Authorization: %s" CRLF,
3558 forward->proxy_auth);
3559 }
3560 /* Set a reasonable agent and send everything */
3561 nbytes += apr_snprintf(buffer + nbytes, sizeof(buffer) - nbytes,
3562 "Proxy-agent: %s" CRLF CRLF,
3565 apr_socket_send(backend->sock, buffer, &nbytes);
3566
3567 /* Receive the whole CONNECT response */
3568 left = sizeof(buffer) - 1;
3569 /* Read until we find the end of the headers or run out of buffer */
3570 do {
3571 nbytes = left;
3572 status = apr_socket_recv(backend->sock, buffer + len, &nbytes);
3573 len += nbytes;
3574 left -= nbytes;
3575 buffer[len] = '\0';
3578 complete = 1;
3579 break;
3580 }
3581 } while (status == APR_SUCCESS && left > 0);
3582 /* Drain what's left */
3583 if (!complete) {
3584 nbytes = sizeof(drain_buffer) - 1;
3585 while (status == APR_SUCCESS && nbytes) {
3587 drain_buffer[nbytes] = '\0';
3588 nbytes = sizeof(drain_buffer) - 1;
3590 break;
3591 }
3592 }
3593 }
3594
3595 /* Check for HTTP_OK response status */
3596 if (status == APR_SUCCESS) {
3597 unsigned int major, minor;
3598 /* Only scan for three character status code */
3599 char code_str[4];
3600
3602 "send_http_connect: response from the forward proxy: %s",
3603 buffer);
3604
3605 /* Extract the returned code */
3606 if (sscanf(buffer, "HTTP/%u.%u %3s", &major, &minor, code_str) == 3) {
3607 status = atoi(code_str);
3608 if (status == HTTP_OK) {
3610 }
3611 else {
3613 "send_http_connect: the forward proxy returned code is '%s'",
3614 code_str);
3616 }
3617 }
3618 }
3619
3620 return(status);
3621}
3622
3623
3624/* TODO: In APR 2.x: Extend apr_sockaddr_t to possibly be a path !!! */
3626 const char *uds_path,
3627 apr_pool_t *p)
3628{
3629#if APR_HAVE_SYS_UN_H
3630 apr_status_t rv;
3633 struct sockaddr_un *sa;
3634 apr_socklen_t addrlen, pathlen;
3635
3637 if (rv != APR_SUCCESS) {
3638 return rv;
3639 }
3640
3642 if (rv != APR_SUCCESS) {
3643 return rv;
3644 }
3645
3646 pathlen = strlen(uds_path);
3647 /* copy the UDS path (including NUL) to the sockaddr_un */
3648 addrlen = APR_OFFSETOF(struct sockaddr_un, sun_path) + pathlen;
3649 sa = (struct sockaddr_un *)apr_palloc(p, addrlen + 1);
3650 memcpy(sa->sun_path, uds_path, pathlen + 1);
3651 sa->sun_family = AF_UNIX;
3652
3653 do {
3654 rv = connect(rawsock, (struct sockaddr*)sa, addrlen);
3655 } while (rv == -1 && (rv = errno) == EINTR);
3656
3657 if (rv && rv != EISCONN) {
3658 if ((rv == EINPROGRESS || rv == EALREADY) && (t > 0)) {
3659#if APR_MAJOR_VERSION < 2
3661#else
3663#endif
3664 }
3665 if (rv != APR_SUCCESS) {
3666 return rv;
3667 }
3668 }
3669
3670 return APR_SUCCESS;
3671#else
3672 return APR_ENOTIMPL;
3673#endif
3674}
3675
3677 proxy_conn_rec *conn,
3679 unsigned max_blank_lines,
3680 int flags)
3681{
3683 proxy_worker *worker = conn->worker;
3684
3685 if (!PROXY_WORKER_IS_USABLE(worker)) {
3686 /*
3687 * The worker is in error likely done by a different thread / process
3688 * e.g. for a timeout or bad status. We should respect this and should
3689 * not continue with a connection via this worker even if we got one.
3690 */
3691 rv = APR_EINVAL;
3692 }
3693 else if (conn->connection) {
3694 /* We have a conn_rec, check the full filter stack for things like
3695 * SSL alert/shutdown, filters aside data...
3696 */
3697 rv = ap_check_pipeline(conn->connection, conn->tmp_bb,
3700 if (rv == APR_SUCCESS) {
3701 /* Some data available, the caller might not want them. */
3703 rv = APR_ENOTEMPTY;
3704 }
3705 }
3706 else if (APR_STATUS_IS_EAGAIN(rv)) {
3707 /* Filter chain is OK and empty, yet we can't determine from
3708 * ap_check_pipeline (actually ap_core_input_filter) whether
3709 * an empty non-blocking read is EAGAIN or EOF on the socket
3710 * side (it's always SUCCESS), so check it explicitly here.
3711 */
3713 rv = APR_SUCCESS;
3714 }
3715 else {
3716 rv = APR_EPIPE;
3717 }
3718 }
3719 }
3720 else if (conn->sock) {
3721 /* For modules working with sockets directly, check it. */
3722 if (!ap_proxy_is_socket_connected(conn->sock)) {
3723 rv = APR_EPIPE;
3724 }
3725 }
3726 else {
3727 rv = APR_ENOSOCKET;
3728 }
3729
3730 if (rv == APR_SUCCESS) {
3731 if (APLOGtrace2(server)) {
3732 apr_sockaddr_t *local_addr = NULL;
3733 apr_socket_addr_get(&local_addr, APR_LOCAL, conn->sock);
3735 "%s: reusing backend connection %pI<>%pI",
3736 scheme, local_addr, conn->addr);
3737 }
3738 }
3739 else if (conn->sock) {
3740 /* This clears conn->scpool (and associated data), so backup and
3741 * restore any ssl_hostname for this connection set earlier by
3742 * ap_proxy_determine_connection().
3743 */
3744 char ssl_hostname[PROXY_WORKER_RFC1035_NAME_SIZE];
3745 if (rv == APR_EINVAL
3746 || !conn->ssl_hostname
3747 || PROXY_STRNCPY(ssl_hostname, conn->ssl_hostname)) {
3748 ssl_hostname[0] = '\0';
3749 }
3750
3751 socket_cleanup(conn);
3752 if (rv != APR_ENOTEMPTY) {
3754 "%s: backend socket is disconnected.", scheme);
3755 }
3756 else {
3758 "%s: reusable backend connection is not empty: "
3759 "forcibly closed", scheme);
3760 }
3761
3762 if (ssl_hostname[0]) {
3763 conn->ssl_hostname = apr_pstrdup(conn->scpool, ssl_hostname);
3764 }
3765 }
3766
3767 return rv;
3768}
3769
3771 proxy_conn_rec *conn,
3772 proxy_worker *worker,
3773 server_rec *s)
3774{
3775 apr_status_t rv;
3776 int loglevel;
3777 forward_info *forward = conn->forward;
3779 /* the local address to use for the outgoing connection */
3780 apr_sockaddr_t *local_addr;
3782 void *sconf = s->module_config;
3783 int address_reusable = worker->s->is_address_reusable;
3784 int did_dns_lookup = 0;
3785 proxy_server_conf *conf =
3786 (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
3787
3788 rv = ap_proxy_check_connection(proxy_function, conn, s, 0, 0);
3789 if (rv == APR_EINVAL) {
3790 return DECLINED;
3791 }
3792
3793 /* We'll set conn->addr to the address actually connect()ed, so if the
3794 * network connection is not reused (per ap_proxy_check_connection()
3795 * above) we need to reset conn->addr to the first resolved address
3796 * and try to connect it first.
3797 */
3798 if (conn->address && rv != APR_SUCCESS) {
3799 conn->addr = conn->address->addr;
3800 }
3801 backend_addr = conn->addr;
3802
3803 while (rv != APR_SUCCESS && (backend_addr || conn->uds_path)) {
3804#if APR_HAVE_SYS_UN_H
3805 if (conn->uds_path)
3806 {
3808 conn->scpool);
3809 if (rv != APR_SUCCESS) {
3810 loglevel = APLOG_ERR;
3811 ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(02453)
3812 "%s: error creating Unix domain socket "
3813 "%s (%s:%hu)",
3815 conn->uds_path,
3816 conn->hostname, conn->port);
3817 break;
3818 }
3819 conn->connection = NULL;
3820
3821 rv = ap_proxy_connect_uds(newsock, conn->uds_path, conn->scpool);
3822 if (rv != APR_SUCCESS) {
3825 "%s: attempt to connect to Unix domain socket "
3826 "%s (%s:%hu) failed",
3827 proxy_function, conn->uds_path,
3828 conn->hostname, conn->port);
3829 break;
3830 }
3831
3833 "%s: connection established with Unix domain socket "
3834 "%s (%s:%hu)",
3836 conn->uds_path,
3837 conn->hostname, conn->port);
3838 }
3839 else
3840#endif
3841 {
3842 if ((rv = apr_socket_create(&newsock, backend_addr->family,
3844 conn->scpool)) != APR_SUCCESS) {
3845 loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
3846 ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00952)
3847 "%s: error creating fam %d socket to %pI for "
3848 "(%s:%hu)",
3850 backend_addr->family, backend_addr,
3851 conn->hostname, conn->port);
3852 /*
3853 * this could be an IPv6 address from the DNS but the
3854 * local machine won't give us an IPv6 socket; hopefully the
3855 * DNS returned an additional address to try
3856 */
3857 backend_addr = backend_addr->next;
3858 continue;
3859 }
3860 conn->connection = NULL;
3861
3862 if (worker->s->recv_buffer_size > 0 &&
3864 worker->s->recv_buffer_size))) {
3866 "apr_socket_opt_set(SO_RCVBUF): Failed to set "
3867 "ProxyReceiveBufferSize, using default");
3868 }
3869
3871 if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
3873 "apr_socket_opt_set(APR_TCP_NODELAY): "
3874 "Failed to set");
3875 }
3876
3877 /* Set a timeout for connecting to the backend on the socket */
3878 if (worker->s->conn_timeout_set) {
3880 }
3881 else if (worker->s->timeout_set) {
3883 }
3884 else if (conf->timeout_set) {
3886 }
3887 else {
3888 apr_socket_timeout_set(newsock, s->timeout);
3889 }
3890 /* Set a keepalive option */
3891 if (worker->s->keepalive) {
3892 if ((rv = apr_socket_opt_set(newsock,
3893 APR_SO_KEEPALIVE, 1)) != APR_SUCCESS) {
3895 "apr_socket_opt_set(SO_KEEPALIVE): Failed to set"
3896 " Keepalive");
3897 }
3898 }
3900 "%s: fam %d socket created for %pI (%s:%hu)",
3902 conn->hostname, conn->port);
3903
3904 if (conf->source_address_set) {
3905 local_addr = apr_pmemdup(conn->scpool, conf->source_address,
3906 sizeof(apr_sockaddr_t));
3907 local_addr->pool = conn->scpool;
3908 rv = apr_socket_bind(newsock, local_addr);
3909 if (rv != APR_SUCCESS) {
3911 "%s: failed to bind socket to local address",
3913 }
3914 }
3915
3916 /* make the connection out of the socket */
3918
3919 /* if an error occurred, loop round and try again */
3920 if (rv != APR_SUCCESS) {
3922 loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
3923 ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00957)
3924 "%s: attempt to connect to %pI (%s:%hu) failed",
3926 conn->hostname, conn->port);
3927 backend_addr = backend_addr->next;
3928 /*
3929 * If we run out of resolved IP's when connecting and if
3930 * we cache the resolution in the worker the resolution
3931 * might have changed. Hence try a DNS lookup to see if this
3932 * helps.
3933 */
3935 /* Issue a new DNS lookup to check if the address changed,
3936 * in which case (SUCCESS) restart the loop with the new
3937 * one(s), otherwise leave (nothing we can do about it).
3938 */
3940 conn->hostname, conn->port,
3942 NULL, s) == APR_SUCCESS) {
3943 backend_addr = conn->addr;
3944 }
3945
3946 /*
3947 * In case of an error backend_addr will be NULL which
3948 * is enough to leave the loop. If successful we'll retry
3949 * the new addresses only once.
3950 */
3951 did_dns_lookup = 1;
3952 }
3953 continue;
3954 }
3955
3957 "%s: connection established with %pI (%s:%hu)",
3959 conn->hostname, conn->port);
3960
3961 /* Set the actual sockaddr we are connected to */
3962 conn->addr = backend_addr;
3963 }
3964
3965 /* Set a timeout on the socket */
3966 if (worker->s->timeout_set) {
3968 }
3969 else if (conf->timeout_set) {
3971 }
3972 else {
3973 apr_socket_timeout_set(newsock, s->timeout);
3974 }
3975
3976 conn->sock = newsock;
3977
3978 if (forward && forward->use_http_connect) {
3979 /*
3980 * For HTTP CONNECT we need to prepend CONNECT request before
3981 * sending our actual HTTPS requests.
3982 */
3983 {
3984 rv = send_http_connect(conn, s);
3985 /* If an error occurred, loop round and try again */
3986 if (rv != APR_SUCCESS) {
3987 conn->sock = NULL;
3989 loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
3990 ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00958)
3991 "%s: attempt to connect to %s:%hu "
3992 "via http CONNECT through %pI (%s:%hu) failed",
3994 forward->target_host, forward->target_port,
3995 backend_addr, conn->hostname, conn->port);
3996 backend_addr = backend_addr->next;
3997 continue;
3998 }
3999 }
4000 }
4001 }
4002
4003 if (PROXY_WORKER_IS_USABLE(worker)) {
4004 /*
4005 * Put the entire worker to error state if
4006 * the PROXY_WORKER_IGNORE_ERRORS flag is not set.
4007 * Although some connections may be alive
4008 * no further connections to the worker could be made
4009 */
4010 if (rv != APR_SUCCESS) {
4011 if (!(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) {
4012 worker->s->error_time = apr_time_now();
4013 worker->s->status |= PROXY_WORKER_IN_ERROR;
4015 "ap_proxy_connect_backend disabling worker for (%s:%hu) "
4016 "for %" APR_TIME_T_FMT "s",
4017 worker->s->hostname_ex, (int)worker->s->port,
4018 apr_time_sec(worker->s->retry));
4019 }
4020 }
4021 else {
4022 if (worker->s->retries) {
4023 /*
4024 * A worker came back. So here is where we need to
4025 * either reset all params to initial conditions or
4026 * apply some sort of aging
4027 */
4028 }
4029 worker->s->error_time = 0;
4030 worker->s->retries = 0;
4031 }
4032 }
4033 else {
4034 /*
4035 * The worker is in error likely done by a different thread / process
4036 * e.g. for a timeout or bad status. We should respect this and should
4037 * not continue with a connection via this worker even if we got one.
4038 */
4039 if (rv == APR_SUCCESS) {
4040 socket_cleanup(conn);
4041 }
4042 rv = APR_EINVAL;
4043 }
4044
4045 return rv == APR_SUCCESS ? OK : DECLINED;
4046}
4047
4049{
4051 conn_rec *c = conn->connection;
4052 if (c) {
4053 if (!c->aborted) {
4056 if (saved_timeout) {
4057 apr_socket_timeout_set(conn->sock, 0);
4058 }
4059
4060 (void)ap_shutdown_conn(c, 0);
4061 c->aborted = 1;
4062
4063 if (saved_timeout) {
4065 }
4066 }
4067
4069 "proxy: connection shutdown");
4070 }
4071 return APR_SUCCESS;
4072}
4073
4074
4076 proxy_conn_rec *conn,
4078{
4079 ap_conf_vector_t *per_dir_config = (r) ? r->per_dir_config
4080 : conn->worker->section_config;
4082 int rc;
4084 apr_bucket_alloc_t *bucket_alloc;
4085
4086 if (conn->connection) {
4087 if (conn->is_ssl) {
4088 /* on reuse, reinit the SSL connection dir config with the current
4089 * r->per_dir_config, the previous one was reset on release.
4090 */
4091 ap_proxy_ssl_engine(conn->connection, per_dir_config, 1);
4092 }
4093 return OK;
4094 }
4095
4096 bucket_alloc = apr_bucket_alloc_create(conn->scpool);
4097 conn->tmp_bb = apr_brigade_create(conn->scpool, bucket_alloc);
4098 /*
4099 * The socket is now open, create a new backend server connection
4100 */
4101 conn->connection = ap_run_create_connection(conn->scpool, s, conn->sock,
4102 0, NULL,
4103 bucket_alloc);
4104
4105 if (!conn->connection) {
4106 /*
4107 * the peer reset the connection already; ap_run_create_connection()
4108 * closed the socket
4109 */
4111 s, APLOGNO(00960) "%s: an error occurred creating a "
4112 "new connection to %pI (%s)", proxy_function,
4113 backend_addr, conn->hostname);
4114 /* XXX: Will be closed when proxy_conn is closed */
4115 socket_cleanup(conn);
4117 }
4118
4119 /* For ssl connection to backend */
4120 if (conn->is_ssl) {
4121 if (!ap_proxy_ssl_engine(conn->connection, per_dir_config, 1)) {
4123 s, APLOGNO(00961) "%s: failed to enable ssl support "
4124 "for %pI (%s)", proxy_function,
4125 backend_addr, conn->hostname);
4127 }
4128 if (conn->ssl_hostname) {
4129 /* Set a note on the connection about what CN is requested,
4130 * such that mod_ssl can check if it is requested to do so.
4131 */
4133 "%s: set SNI to %s for (%s)", proxy_function,
4134 conn->ssl_hostname, conn->hostname);
4135 apr_table_setn(conn->connection->notes, "proxy-request-hostname",
4136 conn->ssl_hostname);
4137 }
4138 }
4139 else {
4140 /* TODO: See if this will break FTP */
4141 ap_proxy_ssl_engine(conn->connection, per_dir_config, 0);
4142 }
4143
4145 "%s: connection complete to %pI (%s)",
4147
4148 /*
4149 * save the timeout of the socket because core_pre_connection
4150 * will set it to base_server->timeout
4151 * (core TimeOut directive).
4152 */
4154 /* set up the connection filters */
4155 rc = ap_run_pre_connection(conn->connection, conn->sock);
4156 if (rc != OK && rc != DONE) {
4157 conn->connection->aborted = 1;
4159 "%s: pre_connection setup failed (%d)",
4161 return rc;
4162 }
4164
4165 /* Shutdown the connection before closing it (eg. SSL connections
4166 * need to be close-notify-ed).
4167 */
4169
4170 return OK;
4171}
4172
4179
4181 proxy_conn_rec *conn,
4183{
4184 (void) c; /* unused */
4186}
4187
4189{
4190 /*
4191 * Since we can't resize the scoreboard when reconfiguring, we
4192 * have to impose a limit on the number of workers, we are
4193 * able to reconfigure to.
4194 */
4195 if (!lb_workers_limit)
4197 return lb_workers_limit;
4198}
4199
4200static APR_INLINE int error_code_overridden(const int *elts, int nelts,
4201 int code)
4202{
4203 int min = 0;
4204 int max = nelts - 1;
4205 AP_DEBUG_ASSERT(max >= 0);
4206
4207 while (min < max) {
4208 int mid = (min + max) / 2;
4209 int val = elts[mid];
4210
4211 if (val < code) {
4212 min = mid + 1;
4213 }
4214 else if (val > code) {
4215 max = mid - 1;
4216 }
4217 else {
4218 return 1;
4219 }
4220 }
4221
4222 return elts[min] == code;
4223}
4224
4226{
4227 if (!conf->error_override)
4228 return 0;
4229
4231 return ap_is_HTTP_ERROR(code);
4232
4233 /* Since error_override_codes is sorted, apply binary search. */
4234 return error_code_overridden((int *)conf->error_override_codes->elts,
4236 code);
4237}
4238
4241{
4242 apr_bucket *e;
4243 conn_rec *c = r->connection;
4244
4245 r->no_cache = 1;
4246 /*
4247 * If this is a subrequest, then prevent also caching of the main
4248 * request.
4249 */
4250 if (r->main)
4251 r->main->no_cache = 1;
4253 c->bucket_alloc);
4255 e = apr_bucket_eos_create(c->bucket_alloc);
4257}
4258
4259/*
4260 * Provide a string hashing function for the proxy.
4261 * We offer 2 methods: one is the APR model but we
4262 * also provide our own, based on either FNV or SDBM.
4263 * The reason is in case we want to use both to ensure no
4264 * collisions.
4265 */
4266PROXY_DECLARE(unsigned int)
4268{
4269 if (method == PROXY_HASHFUNC_APR) {
4270 apr_ssize_t slen = strlen(str);
4271 return apr_hashfunc_default(str, &slen);
4272 }
4273 else if (method == PROXY_HASHFUNC_FNV) {
4274 /* FNV model */
4275 unsigned int hash;
4276 const unsigned int fnv_prime = 0x811C9DC5;
4277 for (hash = 0; *str; str++) {
4278 hash *= fnv_prime;
4279 hash ^= (*str);
4280 }
4281 return hash;
4282 }
4283 else { /* method == PROXY_HASHFUNC_DEFAULT */
4284 /* SDBM model */
4285 unsigned int hash;
4286 for (hash = 0; *str; str++) {
4287 hash = (*str) + (hash << 6) + (hash << 16) - hash;
4288 }
4289 return hash;
4290 }
4291}
4292
4294{
4295 unsigned int *status = &w->s->status;
4296 char flag = toupper(c);
4298 while (pwt->bit) {
4299 if (flag == pwt->flag) {
4300 if (set)
4301 *status |= pwt->bit;
4302 else
4303 *status &= ~(pwt->bit);
4304 return APR_SUCCESS;
4305 }
4306 pwt++;
4307 }
4308 return APR_EINVAL;
4309}
4310
4312{
4313 char *ret = "";
4314 unsigned int status = w->s->status;
4316 while (pwt->bit) {
4317 if (status & pwt->bit)
4318 ret = apr_pstrcat(p, ret, pwt->name, NULL);
4319 pwt++;
4320 }
4321 if (!*ret) {
4322 ret = "??? ";
4323 }
4325 ret = apr_pstrcat(p, ret, "Ok ", NULL);
4326 return ret;
4327}
4328
4330 proxy_server_conf *conf)
4331{
4333 int i;
4334 int index;
4336 proxy_balancer_method *lbmethod;
4337 ap_slotmem_provider_t *storage = b->storage;
4338
4339 if (b->s->wupdated <= b->wupdated)
4340 return APR_SUCCESS;
4341 /* balancer sync */
4342 lbmethod = ap_lookup_provider(PROXY_LBMETHOD, b->s->lbpname, "0");
4343 if (lbmethod) {
4344 b->lbmethod = lbmethod;
4345 } else {
4347 "Cannot find LB Method: %s", b->s->lbpname);
4348 return APR_EINVAL;
4349 }
4350
4351 /* worker sync */
4352
4353 /*
4354 * Look thru the list of workers in shm
4355 * and see which one(s) we are lacking...
4356 * again, the cast to unsigned int is safe
4357 * since our upper limit is always max_workers
4358 * which is int.
4359 */
4360 for (index = 0; index < b->max_workers; index++) {
4361 int found;
4362 apr_status_t rv;
4363 if ((rv = storage->dptr(b->wslot, (unsigned int)index, (void *)&shm)) != APR_SUCCESS) {
4364 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(00965) "worker slotmem_dptr failed");
4365 return APR_EGENERAL;
4366 }
4367 /* account for possible "holes" in the slotmem
4368 * (eg: slots 0-2 are used, but 3 isn't, but 4-5 is)
4369 */
4370 if (!shm->hash.def || !shm->hash.fnv)
4371 continue;
4372 found = 0;
4373 workers = (proxy_worker **)b->workers->elts;
4374 for (i = 0; i < b->workers->nelts; i++, workers++) {
4375 proxy_worker *worker = *workers;
4376 if (worker->hash.def == shm->hash.def && worker->hash.fnv == shm->hash.fnv) {
4377 found = 1;
4379 "re-grabbing shm[%d] (0x%pp) for worker: %s", i, (void *)shm,
4380 ap_proxy_worker_name(conf->pool, worker));
4381 break;
4382 }
4383 }
4384 if (!found) {
4386 /* XXX: a thread mutex is maybe enough here */
4388 runtime = apr_array_push(b->workers);
4389 *runtime = apr_pcalloc(conf->pool, sizeof(proxy_worker));
4391 (*runtime)->hash = shm->hash;
4392 (*runtime)->balancer = b;
4393 (*runtime)->s = shm;
4394
4396 if (rv != APR_SUCCESS) {
4397 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(00966) "Cannot init worker");
4398 return rv;
4399 }
4401 "grabbing shm[%d] (0x%pp) for worker: %s", i, (void *)shm,
4402 (*runtime)->s->name_ex);
4403 }
4404 }
4405 if (b->s->need_reset) {
4406 if (b->lbmethod && b->lbmethod->reset)
4407 b->lbmethod->reset(b, s);
4408 b->s->need_reset = 0;
4409 }
4410 b->wupdated = b->s->wupdated;
4411 return APR_SUCCESS;
4412}
4413
4416 proxy_worker *worker,
4417 unsigned int *index)
4418{
4420 unsigned int i, limit;
4421 limit = storage->num_slots(slot);
4422 for (i = 0; i < limit; i++) {
4423 if (storage->dptr(slot, i, (void *)&shm) != APR_SUCCESS) {
4424 return NULL;
4425 }
4426 if ((worker->s->hash.def == shm->hash.def) &&
4427 (worker->s->hash.fnv == shm->hash.fnv)) {
4428 *index = i;
4429 return shm;
4430 }
4431 }
4432 return NULL;
4433}
4434
4437 proxy_balancer *balancer,
4438 unsigned int *index)
4439{
4441 unsigned int i, limit;
4442 limit = storage->num_slots(slot);
4443 for (i = 0; i < limit; i++) {
4444 if (storage->dptr(slot, i, (void *)&shm) != APR_SUCCESS) {
4445 return NULL;
4446 }
4447 if ((balancer->s->hash.def == shm->hash.def) &&
4448 (balancer->s->hash.fnv == shm->hash.fnv)) {
4449 *index = i;
4450 return shm;
4451 }
4452 }
4453 return NULL;
4454}
4455
4462
4463static int find_conn_headers(void *data, const char *key, const char *val)
4464{
4466 const char *name;
4467
4468 do {
4469 while (*val == ',' || *val == ';') {
4470 val++;
4471 }
4472 name = ap_get_token(x->pool, &val, 0);
4473 if (!strcasecmp(name, "close")) {
4474 x->closed = 1;
4475 }
4476 if (!x->first) {
4477 x->first = name;
4478 }
4479 else {
4480 const char **elt;
4481 if (!x->array) {
4482 x->array = apr_array_make(x->pool, 4, sizeof(char *));
4483 }
4484 elt = apr_array_push(x->array);
4485 *elt = name;
4486 }
4487 } while (*val);
4488
4489 return 1;
4490}
4491
4496{
4497 const char **name;
4499
4500 x.pool = r->pool;
4501 x.array = NULL;
4502 x.first = NULL;
4503 x.closed = 0;
4504
4505 apr_table_unset(headers, "Proxy-Connection");
4506
4507 apr_table_do(find_conn_headers, &x, headers, "Connection", NULL);
4508 if (x.first) {
4509 /* fast path - no memory allocated for one header */
4510 apr_table_unset(headers, "Connection");
4511 apr_table_unset(headers, x.first);
4512 }
4513 if (x.array) {
4514 /* two or more headers */
4515 while ((name = apr_array_pop(x.array))) {
4516 apr_table_unset(headers, *name);
4517 }
4518 }
4519
4520 return x.closed;
4521}
4522
4524 apr_bucket_brigade *header_brigade,
4525 request_rec *r,
4526 proxy_conn_rec *p_conn,
4527 proxy_worker *worker,
4528 proxy_server_conf *conf,
4529 apr_uri_t *uri,
4530 char *url, char *server_portstr,
4531 char **old_cl_val,
4532 char **old_te_val)
4533{
4534 int rc = OK;
4535 conn_rec *c = r->connection;
4536 int counter;
4537 char *buf;
4539 const char *saved_host = apr_table_get(saved_headers_in, "Host");
4541 const apr_table_entry_t *headers_in;
4542 apr_bucket *e;
4543 int force10 = 0, do_100_continue = 0;
4544 conn_rec *origin = p_conn->connection;
4545 const char *host, *creds, *val;
4546 proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
4547
4548 /*
4549 * HTTP "Ping" test? Easiest is 100-Continue. However:
4550 * To be compliant, we only use 100-Continue for requests with bodies.
4551 * We also make sure we won't be talking HTTP/1.0 as well.
4552 */
4553 if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) {
4554 force10 = 1;
4555 }
4556 else if (apr_table_get(r->notes, "proxy-100-continue")
4557 || PROXY_SHOULD_PING_100_CONTINUE(worker, r)) {
4558 do_100_continue = 1;
4559 }
4560 if (force10 || apr_table_get(r->subprocess_env, "proxy-nokeepalive")) {
4561 if (origin) {
4562 origin->keepalive = AP_CONN_CLOSE;
4563 }
4564 p_conn->close = 1;
4565 }
4566
4567 if (force10) {
4568 buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.0" CRLF, NULL);
4569 }
4570 else {
4571 buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL);
4572 }
4574 e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
4575 APR_BRIGADE_INSERT_TAIL(header_brigade, e);
4576
4577 /*
4578 * Make a copy on r->headers_in for the request we make to the backend,
4579 * modify the copy in place according to our configuration and connection
4580 * handling, use it to fill in the forwarded headers' brigade, and finally
4581 * restore the saved/original ones in r->headers_in.
4582 *
4583 * Note: We need to take r->pool for apr_table_copy as the key / value
4584 * pairs in r->headers_in have been created out of r->pool and
4585 * p might be (and actually is) a longer living pool.
4586 * This would trigger the bad pool ancestry abort in apr_table_copy if
4587 * apr is compiled with APR_POOL_DEBUG.
4588 *
4589 * icing: if p indeed lives longer than r->pool, we should allocate
4590 * all new header values from r->pool as well and avoid leakage.
4591 */
4593
4594 /* Return the original Transfer-Encoding and/or Content-Length values
4595 * then drop the headers, they must be set by the proxy handler based
4596 * on the actual body being forwarded.
4597 */
4598 if ((*old_te_val = (char *)apr_table_get(r->headers_in,
4599 "Transfer-Encoding"))) {
4600 apr_table_unset(r->headers_in, "Transfer-Encoding");
4601 }
4602 if ((*old_cl_val = (char *)apr_table_get(r->headers_in,
4603 "Content-Length"))) {
4604 apr_table_unset(r->headers_in, "Content-Length");
4605 }
4606
4607 /* Clear out hop-by-hop request headers not to forward */
4610 goto cleanup;
4611 }
4612
4613 /* RFC2616 13.5.1 says we should strip these */
4614 apr_table_unset(r->headers_in, "Keep-Alive");
4615 apr_table_unset(r->headers_in, "Upgrade");
4616 apr_table_unset(r->headers_in, "Trailer");
4618
4619 /* Compute Host header */
4620 if (dconf->preserve_host == 0) {
4621 if (!uri->hostname) {
4623 goto cleanup;
4624 }
4625 if (ap_strchr_c(uri->hostname, ':')) { /* if literal IPv6 address */
4626 if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
4627 host = apr_pstrcat(r->pool, "[", uri->hostname, "]:",
4628 uri->port_str, NULL);
4629 } else {
4630 host = apr_pstrcat(r->pool, "[", uri->hostname, "]", NULL);
4631 }
4632 } else {
4633 if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
4634 host = apr_pstrcat(r->pool, uri->hostname, ":",
4635 uri->port_str, NULL);
4636 } else {
4637 host = uri->hostname;
4638 }
4639 }
4640 apr_table_setn(r->headers_in, "Host", host);
4641 }
4642 else {
4643 /* don't want to use r->hostname as the incoming header might have a
4644 * port attached, let's use the original header.
4645 */
4646 host = saved_host;
4647 if (!host) {
4650 "no HTTP 0.9 request (with no host line) "
4651 "on incoming request and preserve host set "
4652 "forcing hostname to be %s for uri %s",
4653 host, r->uri);
4654 apr_table_setn(r->headers_in, "Host", host);
4655 }
4656 }
4657
4658 /* handle Via */
4659 if (conf->viaopt == via_block) {
4660 /* Block all outgoing Via: headers */
4661 apr_table_unset(r->headers_in, "Via");
4662 } else if (conf->viaopt != via_off) {
4663 const char *server_name = ap_get_server_name(r);
4664 /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host,
4665 * then the server name returned by ap_get_server_name() is the
4666 * origin server name (which does make too much sense with Via: headers)
4667 * so we use the proxy vhost's name instead.
4668 */
4669 if (server_name == r->hostname)
4671 /* Create a "Via:" request header entry and merge it */
4672 /* Generate outgoing Via: header with/without server comment: */
4674 (conf->viaopt == via_full)
4675 ? apr_psprintf(p, "%d.%d %s%s (%s)",
4678 server_name, server_portstr,
4680 : apr_psprintf(p, "%d.%d %s%s",
4683 server_name, server_portstr)
4684 );
4685 }
4686
4687 /* Use HTTP/1.1 100-Continue as quick "HTTP ping" test
4688 * to backend
4689 */
4690 if (do_100_continue) {
4691 /* Add the Expect header if not already there. */
4692 if (!(val = apr_table_get(r->headers_in, "Expect"))
4693 || (ap_cstr_casecmp(val, "100-Continue") != 0 /* fast path */
4694 && !ap_find_token(r->pool, val, "100-Continue"))) {
4695 apr_table_mergen(r->headers_in, "Expect", "100-Continue");
4696 }
4697 }
4698 else {
4699 /* XXX: we should strip the 100-continue token only from the
4700 * Expect header, but are there others actually used anywhere?
4701 */
4702 apr_table_unset(r->headers_in, "Expect");
4703 }
4704
4705 /* X-Forwarded-*: handling
4706 *
4707 * XXX Privacy Note:
4708 * -----------------
4709 *
4710 * These request headers are only really useful when the mod_proxy
4711 * is used in a reverse proxy configuration, so that useful info
4712 * about the client can be passed through the reverse proxy and on
4713 * to the backend server, which may require the information to
4714 * function properly.
4715 *
4716 * In a forward proxy situation, these options are a potential
4717 * privacy violation, as information about clients behind the proxy
4718 * are revealed to arbitrary servers out there on the internet.
4719 *
4720 * The HTTP/1.1 Via: header is designed for passing client
4721 * information through proxies to a server, and should be used in
4722 * a forward proxy configuration instead of X-Forwarded-*. See the
4723 * ProxyVia option for details.
4724 */
4725 if (dconf->add_forwarded_headers) {
4726 if (PROXYREQ_REVERSE == r->proxyreq) {
4727 /* Add X-Forwarded-For: so that the upstream has a chance to
4728 * determine, where the original request came from.
4729 */
4730 apr_table_mergen(r->headers_in, "X-Forwarded-For",
4731 r->useragent_ip);
4732
4733 /* Add X-Forwarded-Host: so that upstream knows what the
4734 * original request hostname was.
4735 */
4736 if (saved_host) {
4737 apr_table_mergen(r->headers_in, "X-Forwarded-Host",
4738 saved_host);
4739 }
4740
4741 /* Add X-Forwarded-Server: so that upstream knows what the
4742 * name of this proxy server is (if there are more than one)
4743 * XXX: This duplicates Via: - do we strictly need it?
4744 */
4745 apr_table_mergen(r->headers_in, "X-Forwarded-Server",
4747 }
4748 }
4749
4750 /* Do we want to strip Proxy-Authorization ?
4751 * If we haven't used it, then NO
4752 * If we have used it then MAYBE: RFC2616 says we MAY propagate it.
4753 * So let's make it configurable by env.
4754 */
4755 if (r->user != NULL /* we've authenticated */
4756 && !apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) {
4757 apr_table_unset(r->headers_in, "Proxy-Authorization");
4758 }
4759
4760 /* for sub-requests, ignore freshness/expiry headers */
4761 if (r->main) {
4762 apr_table_unset(r->headers_in, "If-Match");
4763 apr_table_unset(r->headers_in, "If-Modified-Since");
4764 apr_table_unset(r->headers_in, "If-Range");
4765 apr_table_unset(r->headers_in, "If-Unmodified-Since");
4766 apr_table_unset(r->headers_in, "If-None-Match");
4767 }
4768
4769 creds = apr_table_get(r->notes, "proxy-basic-creds");
4770 if (creds) {
4771 apr_table_mergen(r->headers_in, "Proxy-Authorization", creds);
4772 }
4773
4774 /* run hook to fixup the request we are about to send */
4776
4777 /* We used to send `Host: ` always first, so let's keep it that
4778 * way. No telling which legacy backend is relying on this.
4779 * If proxy_run_fixups() changed the value, use it (though removal
4780 * is ignored).
4781 */
4782 val = apr_table_get(r->headers_in, "Host");
4783 if (val) {
4784 apr_table_unset(r->headers_in, "Host");
4785 host = val;
4786 }
4787 buf = apr_pstrcat(p, "Host: ", host, CRLF, NULL);
4789 e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
4790 APR_BRIGADE_INSERT_TAIL(header_brigade, e);
4791
4792 /* Append the (remaining) headers to the brigade */
4794 headers_in = (const apr_table_entry_t *) headers_in_array->elts;
4795 for (counter = 0; counter < headers_in_array->nelts; counter++) {
4796 if (headers_in[counter].key == NULL
4797 || headers_in[counter].val == NULL) {
4798 continue;
4799 }
4800
4801 buf = apr_pstrcat(p, headers_in[counter].key, ": ",
4802 headers_in[counter].val, CRLF,
4803 NULL);
4805 e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
4806 APR_BRIGADE_INSERT_TAIL(header_brigade, e);
4807 }
4808
4809cleanup:
4811 return rc;
4812}
4813
4815 proxy_conn_rec *backend,
4816 apr_bucket_brigade *input_brigade,
4820{
4821 apr_pool_t *p = r->pool;
4822 conn_rec *c = r->connection;
4826
4827 *bytes_read = 0;
4830 }
4831
4832 /* Prefetch max_read bytes
4833 *
4834 * This helps us avoid any election of C-L v.s. T-E
4835 * request bodies, since we are willing to keep in
4836 * memory this much data, in any case. This gives
4837 * us an instant C-L election if the body is of some
4838 * reasonable size.
4839 */
4840 temp_brigade = apr_brigade_create(p, input_brigade->bucket_alloc);
4841
4842 /* Account for saved input, if any. */
4843 apr_brigade_length(input_brigade, 0, bytes_read);
4844
4845 /* Ensure we don't hit a wall where we have a buffer too small for
4846 * ap_get_brigade's filters to fetch us another bucket, surrender
4847 * once we hit 80 bytes (an arbitrary value) less than max_read.
4848 */
4849 while (*bytes_read < max_read - 80
4850 && (APR_BRIGADE_EMPTY(input_brigade)
4851 || !APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade)))) {
4854 max_read - *bytes_read);
4855 /* ap_get_brigade may return success with an empty brigade
4856 * for a non-blocking read which would block
4857 */
4861 break;
4862 }
4863 if (status != APR_SUCCESS) {
4865 "prefetch request body failed to %pI (%s)"
4866 " from %s (%s)", backend->addr,
4867 backend->hostname ? backend->hostname : "",
4868 c->client_ip, c->remote_host ? c->remote_host : "");
4870 }
4871
4873 *bytes_read += bytes;
4874
4875 /*
4876 * Save temp_brigade in input_brigade. (At least) in the SSL case
4877 * temp_brigade contains transient buckets whose data would get
4878 * overwritten during the next call of ap_get_brigade in the loop.
4879 * ap_save_brigade ensures these buckets to be set aside.
4880 * Calling ap_save_brigade with NULL as filter is OK, because
4881 * input_brigade already has been created and does not need to get
4882 * created by ap_save_brigade.
4883 */
4884 status = ap_save_brigade(NULL, &input_brigade, &temp_brigade, p);
4885 if (status != APR_SUCCESS) {
4887 "processing prefetched request body failed"
4888 " to %pI (%s) from %s (%s)", backend->addr,
4889 backend->hostname ? backend->hostname : "",
4890 c->client_ip, c->remote_host ? c->remote_host : "");
4892 }
4893 }
4894
4895 return OK;
4896}
4897
4899 proxy_conn_rec *backend,
4902{
4903 apr_bucket_alloc_t *bucket_alloc = bb->bucket_alloc;
4907 int rv;
4908
4909 for (;;) {
4912 block, max_read);
4913 if (block == APR_BLOCK_READ
4914 || (!(status == APR_SUCCESS && APR_BRIGADE_EMPTY(bb))
4916 break;
4917 }
4918
4919 /* Flush and retry (blocking) */
4921 rv = ap_proxy_pass_brigade(bucket_alloc, r, backend,
4922 backend->connection, bb, 1);
4923 if (rv != OK) {
4924 return rv;
4925 }
4927 }
4928
4929 if (status != APR_SUCCESS) {
4930 conn_rec *c = r->connection;
4932 "read request body failed to %pI (%s)"
4933 " from %s (%s)", backend->addr,
4934 backend->hostname ? backend->hostname : "",
4935 c->client_ip, c->remote_host ? c->remote_host : "");
4937 }
4938
4939 return OK;
4940}
4941
4943 proxy_conn_rec *backend,
4944 apr_bucket_brigade *input_brigade,
4947{
4948 apr_pool_t *p = r->pool;
4949 int seen_eos = 0, rv = OK;
4951 apr_bucket_alloc_t *bucket_alloc = input_brigade->bucket_alloc;
4953 apr_bucket *e;
4954 apr_off_t bytes, fsize = 0;
4956
4957 *bytes_spooled = 0;
4958 body_brigade = apr_brigade_create(p, bucket_alloc);
4959
4960 do {
4961 if (APR_BRIGADE_EMPTY(input_brigade)) {
4962 rv = ap_proxy_read_input(r, backend, input_brigade,
4964 if (rv != OK) {
4965 return rv;
4966 }
4967 }
4968
4969 /* If this brigade contains EOS, either stop or remove it. */
4970 if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
4971 seen_eos = 1;
4972 }
4973
4974 apr_brigade_length(input_brigade, 1, &bytes);
4975
4977 /* can't spool any more in memory; write latest brigade to disk */
4978 if (tmpfile == NULL) {
4979 const char *temp_dir;
4980 char *template;
4981
4983 if (status != APR_SUCCESS) {
4985 "search for temporary directory failed");
4987 }
4988 apr_filepath_merge(&template, temp_dir,
4989 "modproxy.tmp.XXXXXX",
4991 status = apr_file_mktemp(&tmpfile, template, 0, p);
4992 if (status != APR_SUCCESS) {
4994 "creation of temporary file in directory "
4995 "%s failed", temp_dir);
4997 }
4998 }
4999 for (e = APR_BRIGADE_FIRST(input_brigade);
5000 e != APR_BRIGADE_SENTINEL(input_brigade);
5001 e = APR_BUCKET_NEXT(e)) {
5002 const char *data;
5004
5007 if (status != APR_SUCCESS) {
5008 const char *tmpfile_name;
5009
5011 tmpfile_name = "(unknown)";
5012 }
5014 "write to temporary file %s failed",
5015 tmpfile_name);
5017 }
5020 }
5021 apr_brigade_cleanup(input_brigade);
5022 }
5023 else {
5024
5025 /*
5026 * Save input_brigade in body_brigade. (At least) in the SSL case
5027 * input_brigade contains transient buckets whose data would get
5028 * overwritten during the next call of ap_get_brigade in the loop.
5029 * ap_save_brigade ensures these buckets to be set aside.
5030 * Calling ap_save_brigade with NULL as filter is OK, because
5031 * body_brigade already has been created and does not need to get
5032 * created by ap_save_brigade.
5033 */
5034 status = ap_save_brigade(NULL, &body_brigade, &input_brigade, p);
5035 if (status != APR_SUCCESS) {
5037 }
5038
5039 }
5040
5041 *bytes_spooled += bytes;
5042 } while (!seen_eos);
5043
5044 APR_BRIGADE_CONCAT(input_brigade, body_brigade);
5045 if (tmpfile) {
5046 apr_brigade_insert_file(input_brigade, tmpfile, 0, fsize, p);
5047 }
5048 if (apr_table_get(r->subprocess_env, "proxy-sendextracrlf")) {
5049 e = apr_bucket_immortal_create(CRLF_ASCII, 2, bucket_alloc);
5050 APR_BRIGADE_INSERT_TAIL(input_brigade, e);
5051 }
5052 if (tmpfile) {
5053 /* We dropped metadata buckets when spooling to tmpfile,
5054 * terminate with EOS to allow for flushing in a one go.
5055 */
5056 e = apr_bucket_eos_create(bucket_alloc);
5057 APR_BRIGADE_INSERT_TAIL(input_brigade, e);
5058 }
5059 return OK;
5060}
5061
5063 request_rec *r, proxy_conn_rec *p_conn,
5064 conn_rec *origin, apr_bucket_brigade *bb,
5065 int flush)
5066{
5068 apr_off_t transferred;
5069
5070 if (flush) {
5071 apr_bucket *e = apr_bucket_flush_create(bucket_alloc);
5073 }
5074 apr_brigade_length(bb, 0, &transferred);
5075 if (transferred != -1)
5076 p_conn->worker->s->transferred += transferred;
5077 status = ap_pass_brigade(origin->output_filters, bb);
5078 /* Cleanup the brigade now to avoid buckets lifetime
5079 * issues in case of error returned below. */
5081 if (status != APR_SUCCESS) {
5083 "pass request body failed to %pI (%s)",
5084 p_conn->addr, p_conn->hostname);
5085 if (origin->aborted) {
5086 const char *ssl_note;
5087
5088 if (((ssl_note = apr_table_get(origin->notes, "SSL_connect_rv"))
5089 != NULL) && (strcmp(ssl_note, "err") == 0)) {
5091 "Error during SSL Handshake with"
5092 " remote server");
5093 }
5095 }
5096 else {
5097 return HTTP_BAD_REQUEST;
5098 }
5099 }
5100 return OK;
5101}
5102
5103/* Fill in unknown schemes from apr_uri_port_of_scheme() */
5104
5109
5111{
5112 {"fcgi", 8000},
5113 {"ajp", AJP13_DEF_PORT},
5114 {"scgi", SCGI_DEF_PORT},
5115 {"h2c", DEFAULT_HTTP_PORT},
5116 {"h2", DEFAULT_HTTPS_PORT},
5117 {"ws", DEFAULT_HTTP_PORT},
5118 {"wss", DEFAULT_HTTPS_PORT},
5119 { NULL, 0xFFFF } /* unknown port */
5120};
5121
5123{
5124 if (scheme) {
5126 if ((port = apr_uri_port_of_scheme(scheme)) != 0) {
5127 return port;
5128 } else {
5130 for (pscheme = pschemes; pscheme->name != NULL; ++pscheme) {
5131 if (ap_cstr_casecmp(scheme, pscheme->name) == 0) {
5132 return pscheme->default_port;
5133 }
5134 }
5135 }
5136 }
5137 return 0;
5138}
5139
5141{
5142 return f->c->data_in_output_filters;
5143}
5144
5146{
5147 ap_filter_t *f = c->output_filters;
5148 while (f->next) {
5149 f = f->next;
5150 }
5151 if (f->frec->filter_func.out_func(f, NULL)) {
5152 return AP_FILTER_ERROR;
5153 }
5154 return c->data_in_output_filters ? OK : DECLINED;
5155}
5156
5158 apr_bucket_brigade *from,
5160{
5161 apr_bucket *e;
5162 apr_bucket *new;
5163 const char *data;
5166 apr_bucket_alloc_t *bucket_alloc = to->bucket_alloc;
5167
5169 for (e = APR_BRIGADE_FIRST(from);
5170 e != APR_BRIGADE_SENTINEL(from);
5171 e = APR_BUCKET_NEXT(e)) {
5172 if (!APR_BUCKET_IS_METADATA(e)) {
5174 new = apr_bucket_transient_create(data, bytes, bucket_alloc);
5176 }
5177 else if (APR_BUCKET_IS_FLUSH(e)) {
5178 new = apr_bucket_flush_create(bucket_alloc);
5180 }
5181 else if (APR_BUCKET_IS_EOS(e)) {
5182 new = apr_bucket_eos_create(bucket_alloc);
5184 }
5185 else {
5187 "Unhandled bucket type of type %s in"
5188 " ap_proxy_buckets_lifetime_transform", e->type->name);
5189 rv = APR_EGENERAL;
5190 }
5191 }
5192 return rv;
5193}
5194
5195/* An arbitrary large value to address pathological case where we keep
5196 * reading from one side only, without scheduling the other direction for
5197 * too long. This can happen with large MTU and small read buffers, like
5198 * micro-benchmarking huge files bidirectional transfer with client, proxy
5199 * and backend on localhost for instance. Though we could just ignore the
5200 * case and let the sender stop by itself at some point when/if it needs to
5201 * receive data, or the receiver stop when/if it needs to send...
5202 */
5203#define PROXY_TRANSFER_MAX_READS 10000
5204
5206 request_rec *r,
5207 conn_rec *c_i,
5208 conn_rec *c_o,
5211 const char *name,
5212 int *sent,
5214 int flags)
5215{
5216 apr_status_t rv;
5217 int flush_each = 0;
5218 unsigned int num_reads = 0;
5219#ifdef DEBUGGING
5220 apr_off_t len;
5221#endif
5222
5223 /*
5224 * Compat: since FLUSH_EACH is default (and zero) for legacy reasons, we
5225 * pretend it's no FLUSH_AFTER nor YIELD_PENDING flags, the latter because
5226 * flushing would defeat the purpose of checking for pending data (hence
5227 * determine whether or not the output chain/stack is full for stopping).
5228 */
5231 flush_each = 1;
5232 }
5233
5234 for (;;) {
5236 rv = ap_get_brigade(c_i->input_filters, bb_i, AP_MODE_READBYTES,
5238 if (rv != APR_SUCCESS) {
5239 if (!APR_STATUS_IS_EAGAIN(rv) && !APR_STATUS_IS_EOF(rv)) {
5241 "ap_proxy_transfer_between_connections: "
5242 "error on %s - ap_get_brigade",
5243 name);
5244 if (rv == APR_INCOMPLETE) {
5245 /* Don't return APR_INCOMPLETE, it'd mean "should yield"
5246 * for the caller, while it means "incomplete body" here
5247 * from ap_http_filter(), which is an error.
5248 */
5249 rv = APR_EGENERAL;
5250 }
5251 }
5252 break;
5253 }
5254
5255 if (c_o->aborted) {
5258 rv = APR_EPIPE;
5259 break;
5260 }
5261 if (APR_BRIGADE_EMPTY(bb_i)) {
5262 break;
5263 }
5264#ifdef DEBUGGING
5265 len = -1;
5268 "ap_proxy_transfer_between_connections: "
5269 "read %" APR_OFF_T_FMT
5270 " bytes from %s", len, name);
5271#endif
5272 if (sent) {
5273 *sent = 1;
5274 }
5276 if (flush_each) {
5277 apr_bucket *b;
5278 /*
5279 * Do not use ap_fflush here since this would cause the flush
5280 * bucket to be sent in a separate brigade afterwards which
5281 * causes some filters to set aside the buckets from the first
5282 * brigade and process them when FLUSH arrives in the second
5283 * brigade. As set asides of our transformed buckets involve
5284 * memory copying we try to avoid this. If we have the flush
5285 * bucket in the first brigade they directly process the
5286 * buckets without setting them aside.
5287 */
5288 b = apr_bucket_flush_create(bb_o->bucket_alloc);
5290 }
5291 rv = ap_pass_brigade(c_o->output_filters, bb_o);
5293 if (rv != APR_SUCCESS) {
5295 "ap_proxy_transfer_between_connections: "
5296 "error on %s - ap_pass_brigade",
5297 name);
5299 break;
5300 }
5301
5302 /* Yield if the output filters stack is full? This is to avoid
5303 * blocking and give the caller a chance to POLLOUT async.
5304 */
5306 && ap_filter_should_yield(c_o->output_filters)) {
5308 if (rc == OK) {
5310 "ap_proxy_transfer_between_connections: "
5311 "yield (output pending)");
5312 rv = APR_INCOMPLETE;
5313 break;
5314 }
5315 if (rc != DECLINED) {
5316 rv = AP_FILTER_ERROR;
5317 break;
5318 }
5319 }
5320
5321 /* Yield if we keep hold of the thread for too long? This gives
5322 * the caller a chance to schedule the other direction too.
5323 */
5327 "ap_proxy_transfer_between_connections: "
5328 "yield (max reads)");
5329 rv = APR_SUCCESS;
5330 break;
5331 }
5332 }
5333
5335 ap_fflush(c_o->output_filters, bb_o);
5337 }
5339
5341 "ap_proxy_transfer_between_connections complete (%s %pI)",
5342 (c_i == r->connection) ? "to" : "from",
5343 (c_i == r->connection) ? c_o->client_addr
5344 : c_i->client_addr);
5345
5346 if (APR_STATUS_IS_EAGAIN(rv)) {
5347 rv = APR_SUCCESS;
5348 }
5349 return rv;
5350}
5351
5353 /* the other side of the tunnel */
5355
5357 const char *name;
5358
5361
5362 unsigned int down_in:1,
5364};
5365
5368 const char *scheme)
5369{
5370 apr_status_t rv;
5374
5375 *ptunnel = NULL;
5376
5377 tunnel = apr_pcalloc(r->pool, sizeof(*tunnel));
5378
5379 rv = apr_pollset_create(&tunnel->pollset, 2, r->pool, APR_POLLSET_NOCOPY);
5380 if (rv != APR_SUCCESS) {
5381 return rv;
5382 }
5383
5384 tunnel->r = r;
5385 tunnel->scheme = apr_pstrdup(r->pool, scheme);
5386 tunnel->client = apr_pcalloc(r->pool, sizeof(struct proxy_tunnel_conn));
5387 tunnel->origin = apr_pcalloc(r->pool, sizeof(struct proxy_tunnel_conn));
5388 tunnel->pfds = apr_array_make(r->pool, 2, sizeof(apr_pollfd_t));
5389 tunnel->read_buf_size = ap_get_read_buf_size(r);
5390 tunnel->client->other = tunnel->origin;
5391 tunnel->origin->other = tunnel->client;
5392 tunnel->timeout = -1;
5393
5394 tunnel->client->c = c_i;
5395 tunnel->client->name = "client";
5396 tunnel->client->bb = apr_brigade_create(c_i->pool, c_i->bucket_alloc);
5397 tunnel->client->pfd = &APR_ARRAY_PUSH(tunnel->pfds, apr_pollfd_t);
5398 tunnel->client->pfd->p = r->pool;
5399 tunnel->client->pfd->desc_type = APR_NO_DESC;
5400 rv = ap_get_pollfd_from_conn(tunnel->client->c,
5401 tunnel->client->pfd, &client_timeout);
5402 if (rv != APR_SUCCESS) {
5403 return rv;
5404 }
5405 tunnel->client->pfd->client_data = tunnel->client;
5406 if (tunnel->client->pfd->desc_type == APR_POLL_SOCKET) {
5407 apr_socket_opt_set(tunnel->client->pfd->desc.s, APR_SO_NONBLOCK, 1);
5408 }
5409
5410 tunnel->origin->c = c_o;
5411 tunnel->origin->name = "origin";
5412 tunnel->origin->bb = apr_brigade_create(c_o->pool, c_o->bucket_alloc);
5413 tunnel->origin->pfd = &APR_ARRAY_PUSH(tunnel->pfds, apr_pollfd_t);
5414 tunnel->origin->pfd->p = r->pool;
5415 tunnel->origin->pfd->desc_type = APR_POLL_SOCKET;
5416 tunnel->origin->pfd->desc.s = ap_get_conn_socket(c_o);
5417 tunnel->origin->pfd->client_data = tunnel->origin;
5418 apr_socket_timeout_get(tunnel->origin->pfd->desc.s, &origin_timeout);
5419 apr_socket_opt_set(tunnel->origin->pfd->desc.s, APR_SO_NONBLOCK, 1);
5420
5421 /* Defaults to the largest timeout of both connections */
5422 tunnel->timeout = (client_timeout >= 0 && client_timeout > origin_timeout ?
5424
5425 /* No coalescing filters */
5426 ap_remove_output_filter_byhandle(c_i->output_filters,
5427 "SSL/TLS Coalescing Filter");
5428 ap_remove_output_filter_byhandle(c_o->output_filters,
5429 "SSL/TLS Coalescing Filter");
5430
5431 /* Bidirectional non-HTTP stream will confuse mod_reqtimeoout */
5432 ap_remove_input_filter_byhandle(c_i->input_filters, "reqtimeout");
5433
5434 /* The input/output filter stacks should contain connection filters only */
5435 r->input_filters = r->proto_input_filters = c_i->input_filters;
5436 r->output_filters = r->proto_output_filters = c_i->output_filters;
5437
5438 /* Won't be reused after tunneling */
5439 c_i->keepalive = AP_CONN_CLOSE;
5440 c_o->keepalive = AP_CONN_CLOSE;
5441
5442 /* Disable half-close forwarding for this request? */
5443 if (apr_table_get(r->subprocess_env, "proxy-nohalfclose")) {
5444 tunnel->nohalfclose = 1;
5445 }
5446
5447 if (tunnel->client->pfd->desc_type == APR_POLL_SOCKET) {
5448 /* Both ends are sockets, the poll strategy is:
5449 * - poll both sides POLLOUT
5450 * - when one side is writable, remove the POLLOUT
5451 * and add POLLIN to the other side.
5452 * - tunnel arriving data, remove POLLIN from the source
5453 * again and add POLLOUT to the receiving side
5454 * - on EOF on read, remove the POLLIN from that side
5455 * Repeat until both sides are down */
5456 tunnel->client->pfd->reqevents = APR_POLLOUT | APR_POLLERR;
5457 tunnel->origin->pfd->reqevents = APR_POLLOUT | APR_POLLERR;
5458 if ((rv = apr_pollset_add(tunnel->pollset, tunnel->origin->pfd)) ||
5459 (rv = apr_pollset_add(tunnel->pollset, tunnel->client->pfd))) {
5460 return rv;
5461 }
5462 }
5463 else if (tunnel->client->pfd->desc_type == APR_POLL_FILE) {
5464 /* Input is a PIPE fd, the poll strategy is:
5465 * - always POLLIN on origin
5466 * - use socket strategy described above for client only
5467 * otherwise the same
5468 */
5469 tunnel->client->pfd->reqevents = 0;
5470 tunnel->origin->pfd->reqevents = APR_POLLIN | APR_POLLHUP |
5472 if ((rv = apr_pollset_add(tunnel->pollset, tunnel->origin->pfd))) {
5473 return rv;
5474 }
5475 }
5476 else {
5477 /* input is already closed, unsual, but we know nothing about
5478 * the tunneled protocol. */
5479 tunnel->client->down_in = 1;
5480 tunnel->origin->pfd->reqevents = APR_POLLIN | APR_POLLHUP;
5481 if ((rv = apr_pollset_add(tunnel->pollset, tunnel->origin->pfd))) {
5482 return rv;
5483 }
5484 }
5485
5486 *ptunnel = tunnel;
5487 return APR_SUCCESS;
5488}
5489
5491 apr_int16_t events)
5492{
5493 apr_status_t rv;
5494
5495 AP_DEBUG_ASSERT((pfd->reqevents & events) == 0);
5496
5497 if (pfd->reqevents) {
5498 rv = apr_pollset_remove(pollset, pfd);
5499 if (rv != APR_SUCCESS) {
5500 AP_DEBUG_ASSERT(1);
5501 }
5502 }
5503
5504 if (events & APR_POLLIN) {
5505 events |= APR_POLLHUP;
5506 }
5507 pfd->reqevents |= events | APR_POLLERR;
5508 rv = apr_pollset_add(pollset, pfd);
5509 if (rv != APR_SUCCESS) {
5510 AP_DEBUG_ASSERT(1);
5511 }
5512}
5513
5515 apr_int16_t events)
5516{
5517 apr_status_t rv;
5518
5519 AP_DEBUG_ASSERT((pfd->reqevents & events) != 0);
5520
5521 rv = apr_pollset_remove(pollset, pfd);
5522 if (rv != APR_SUCCESS) {
5523 AP_DEBUG_ASSERT(0);
5524 return;
5525 }
5526
5527 if (events & APR_POLLIN) {
5528 events |= APR_POLLHUP;
5529 }
5530 if (pfd->reqevents & ~(events | APR_POLLERR)) {
5531 pfd->reqevents &= ~events;
5532 rv = apr_pollset_add(pollset, pfd);
5533 if (rv != APR_SUCCESS) {
5534 AP_DEBUG_ASSERT(0);
5535 return;
5536 }
5537 }
5538 else {
5539 pfd->reqevents = 0;
5540 }
5541}
5542
5544 struct proxy_tunnel_conn *in)
5545{
5546 struct proxy_tunnel_conn *out = in->other;
5547 apr_status_t rv;
5548 int sent = 0;
5549
5551 "proxy: %s: %s input ready",
5552 tunnel->scheme, in->name);
5553
5555 in->c, out->c,
5556 in->bb, out->bb,
5557 in->name, &sent,
5558 tunnel->read_buf_size,
5561 if (sent && out == tunnel->client) {
5562 tunnel->replied = 1;
5563 }
5564 if (rv != APR_SUCCESS) {
5565 if (APR_STATUS_IS_INCOMPLETE(rv)) {
5566 /* Pause POLLIN while waiting for POLLOUT on the other
5567 * side, hence avoid filling the output filters even
5568 * more to avoid blocking there.
5569 */
5571 "proxy: %s: %s wait writable",
5572 tunnel->scheme, out->name);
5573 }
5574 else if (APR_STATUS_IS_EOF(rv)) {
5575 /* Stop POLLIN and wait for POLLOUT (flush) on the
5576 * other side to shut it down.
5577 */
5579 "proxy: %s: %s read shutdown",
5580 tunnel->scheme, in->name);
5581 if (tunnel->nohalfclose) {
5582 /* No half-close forwarding, we are done both ways as
5583 * soon as one side shuts down.
5584 */
5585 return DONE;
5586 }
5587 in->down_in = 1;
5588 }
5589 else {
5590 /* Real failure, bail out */
5592 }
5593
5594 del_pollset(tunnel->pollset, in->pfd, APR_POLLIN);
5595 if (out->pfd->desc_type == APR_POLL_SOCKET) {
5596 /* if the output is a SOCKET, we can stop polling the input
5597 * until the output signals POLLOUT again. */
5598 add_pollset(tunnel->pollset, out->pfd, APR_POLLOUT);
5599 }
5600 else {
5601 /* We can't use POLLOUT in this direction for the only
5602 * APR_POLL_FILE case we have so far (mod_h2's "signal" pipe),
5603 * we assume that the client's ouput filters chain will block/flush
5604 * if necessary (i.e. no pending data), hence that the origin
5605 * is EOF when reaching here. This direction is over. */
5606 ap_assert(in->down_in && APR_STATUS_IS_EOF(rv));
5608 "proxy: %s: %s write shutdown",
5609 tunnel->scheme, out->name);
5610 out->down_out = 1;
5611 }
5612 }
5613
5614 return OK;
5615}
5616
5618{
5619 int status = OK, rc;
5620 request_rec *r = tunnel->r;
5622 struct proxy_tunnel_conn *client = tunnel->client,
5623 *origin = tunnel->origin;
5624 apr_interval_time_t timeout = tunnel->timeout >= 0 ? tunnel->timeout : -1;
5625 const char *scheme = tunnel->scheme;
5626 apr_status_t rv;
5627
5629 "proxy: %s: tunnel running (timeout %lf)",
5630 scheme, timeout >= 0 ? (double)timeout / APR_USEC_PER_SEC
5631 : (double)-1.0);
5632
5633 /* Loop until both directions of the connection are closed,
5634 * or a failure occurs.
5635 */
5636 do {
5637 const apr_pollfd_t *results;
5639
5641 "proxy: %s: polling (client=%hx, origin=%hx)",
5642 scheme, client->pfd->reqevents, origin->pfd->reqevents);
5643 do {
5644 rv = apr_pollset_poll(pollset, timeout, &nresults, &results);
5645 } while (APR_STATUS_IS_EINTR(rv));
5646
5647 if (rv != APR_SUCCESS) {
5648 if (APR_STATUS_IS_TIMEUP(rv)) {
5650 "proxy: %s: polling timed out "
5651 "(client=%hx, origin=%hx)",
5652 scheme, client->pfd->reqevents,
5653 origin->pfd->reqevents);
5655 }
5656 else {
5658 "proxy: %s: polling failed", scheme);
5660 }
5661 goto done;
5662 }
5663
5665 "proxy: %s: woken up, %i result(s)", scheme, nresults);
5666
5667 for (i = 0; i < nresults; i++) {
5668 const apr_pollfd_t *pfd = &results[i];
5669 struct proxy_tunnel_conn *tc = pfd->client_data;
5670
5672 "proxy: %s: #%i: %s: %hx/%hx", scheme, i,
5673 tc->name, pfd->rtnevents, tc->pfd->reqevents);
5674
5675 /* sanity check */
5676 if (pfd->desc.s != client->pfd->desc.s
5677 && pfd->desc.s != origin->pfd->desc.s) {
5679 "proxy: %s: unknown socket in pollset", scheme);
5681 goto done;
5682 }
5683
5684 if (!(pfd->rtnevents & (APR_POLLIN | APR_POLLOUT |
5686 /* this catches POLLNVAL etc.. */
5688 "proxy: %s: polling events error (%x)",
5689 scheme, pfd->rtnevents);
5691 goto done;
5692 }
5693
5694 /* We want to write if we asked for POLLOUT and got:
5695 * - POLLOUT: the socket is ready for write;
5696 * - !POLLIN: the socket is in error state (POLLERR) so we let
5697 * the user know by failing the write and log, OR the socket
5698 * is shutdown for read already (POLLHUP) so we have to
5699 * shutdown for write.
5700 */
5701 if ((tc->pfd->reqevents & APR_POLLOUT)
5702 && ((pfd->rtnevents & APR_POLLOUT)
5703 || !(tc->pfd->reqevents & APR_POLLIN)
5704 || !(pfd->rtnevents & (APR_POLLIN | APR_POLLHUP)))) {
5705 struct proxy_tunnel_conn *out = tc, *in = tc->other;
5706
5708 "proxy: %s: %s output ready",
5709 scheme, out->name);
5710
5712 if (rc == OK) {
5713 /* Keep polling out (only) */
5714 continue;
5715 }
5716 if (rc != DECLINED) {
5717 /* Real failure, bail out */
5719 "proxy: %s: %s flushing failed (%i)",
5720 scheme, out->name, rc);
5721 status = rc;
5722 goto done;
5723 }
5724
5725 /* No more pending data. If the other side is not readable
5726 * anymore it's time to shutdown for write (this direction
5727 * is over). Otherwise back to normal business.
5728 */
5730 if (in->down_in) {
5732 "proxy: %s: %s write shutdown",
5733 scheme, out->name);
5734 apr_socket_shutdown(out->pfd->desc.s, 1);
5735 out->down_out = 1;
5736 }
5737 else {
5739 "proxy: %s: %s resume writable",
5740 scheme, out->name);
5742
5743 /* Flush any pending input data now, we don't know when
5744 * the next POLLIN will trigger and retaining data might
5745 * deadlock the underlying protocol. We don't check for
5746 * pending data first with ap_filter_input_pending() since
5747 * the read from proxy_tunnel_forward() is nonblocking
5748 * anyway and returning OK if there's no data.
5749 */
5751 if (rc != OK) {
5752 status = rc;
5753 goto done;
5754 }
5755 }
5756 }
5757
5758 /* We want to read if we asked for POLLIN|HUP and got:
5759 * - POLLIN|HUP: the socket is ready for read or EOF (POLLHUP);
5760 * - !POLLOUT: the socket is in error state (POLLERR) so we let
5761 * the user know by failing the read and log.
5762 */
5763 if ((tc->pfd->reqevents & APR_POLLIN)
5764 && ((pfd->rtnevents & (APR_POLLIN | APR_POLLHUP))
5765 || !(pfd->rtnevents & APR_POLLOUT))) {
5767 if (rc != OK) {
5768 status = rc;
5769 goto done;
5770 }
5771 }
5772 }
5773 } while (!client->down_out || !origin->down_out);
5774
5775done:
5777 "proxy: %s: tunneling returns (%i)", scheme, status);
5778 if (status == DONE) {
5779 status = OK;
5780 }
5781 return status;
5782}
5783
5785{
5787 for (; m->name; m++) {
5788 if (m->method == method) {
5789 return m->name;
5790 }
5791 }
5792 return "???";
5793}
5794
5796{
5800
5801 {
5802 apr_time_t *start_time = ap_retained_data_get("proxy_start_time");
5803 if (start_time == NULL) {
5804 start_time = ap_retained_data_create("proxy_start_time",
5805 sizeof(*start_time));
5806 *start_time = apr_time_now();
5807 }
5808 proxy_start_time = start_time;
5809 }
5810}
Apache Jserv Protocol.
Apache Multi-Processing Module library.
int n
Definition ap_regex.h:278
const char apr_size_t len
Definition ap_regex.h:187
#define AP_SERVER_BASEVERSION
Definition ap_release.h:75
#define socket
#define connect
APR Atomic Operations.
char * strstr(char *s1, char *s2)
APR Hash Tables.
#define hash(h, r, b, n)
Definition apr_random.c:51
#define min(a, b)
Definition apr_random.c:32
APR Strings library.
APR Support functions.
APR Versioning Interface.
apr_uint32_t apr_atomic_add32(volatile apr_uint32_t *mem, apr_uint32_t val)
Definition atomic.c:30
apr_uint32_t apr_atomic_inc32(volatile apr_uint32_t *mem)
Definition atomic.c:51
void apr_atomic_set32(volatile apr_uint32_t *mem, apr_uint32_t val)
Definition atomic.c:73
apr_uint32_t apr_atomic_cas32(volatile apr_uint32_t *mem, apr_uint32_t swap, apr_uint32_t cmp)
Definition atomic.c:78
apr_uint32_t apr_atomic_read32(volatile apr_uint32_t *mem)
Definition atomic.c:68
int ap_run_pre_connection(conn_rec *c, void *csd)
Definition connection.c:43
apr_status_t ap_shutdown_conn(conn_rec *c, int flush)
Definition connection.c:70
conn_rec * ap_run_create_connection(apr_pool_t *p, server_rec *server, apr_socket_t *csd, long conn_id, void *sbh, apr_bucket_alloc_t *alloc)
Definition connection.c:41
return found
Definition core.c:2840
#define AJP13_DEF_PORT
Definition ajp.h:58
#define APLOG_USE_MODULE(foo)
char * ap_runtime_dir_relative(apr_pool_t *p, const char *fname)
Definition config.c:1610
#define ap_get_module_config(v, m)
struct ap_conf_vector_t ap_conf_vector_t
#define ap_set_module_config(v, m, val)
const char * hostname
void * ap_retained_data_create(const char *key, apr_size_t size)
Definition config.c:2527
request_rec int int apr_table_t const char * path
request_rec * r
void * ap_retained_data_get(const char *key)
Definition config.c:2519
int flush
#define HUGE_STRING_LEN
Definition httpd.h:303
#define HTTP_VERSION_MAJOR(number)
Definition httpd.h:271
#define HTTP_VERSION_MINOR(number)
Definition httpd.h:273
#define AP_FILTER_ERROR
Definition httpd.h:473
#define DEFAULT_HTTPS_PORT
Definition httpd.h:280
#define DECLINED
Definition httpd.h:457
#define OK
Definition httpd.h:456
#define DEFAULT_HTTP_PORT
Definition httpd.h:278
#define DONE
Definition httpd.h:458
#define ap_is_default_port(port, r)
Definition httpd.h:287
const char * ap_get_server_banner(void)
Definition core.c:3593
#define ap_xlate_proto_to_ascii(x, y)
Definition util_ebcdic.h:80
#define ap_xlate_proto_from_ascii(x, y)
Definition util_ebcdic.h:81
apr_status_t ap_fflush(ap_filter_t *f, apr_bucket_brigade *bb)
apr_status_t ap_pass_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket)
ap_filter_t * ap_add_input_filter(const char *name, void *ctx, request_rec *r, conn_rec *c)
apr_status_t ap_remove_input_filter_byhandle(ap_filter_t *next, const char *handle)
apr_status_t ap_remove_output_filter_byhandle(ap_filter_t *next, const char *handle)
apr_status_t ap_save_brigade(ap_filter_t *f, apr_bucket_brigade **save_to, apr_bucket_brigade **b, apr_pool_t *p)
apr_status_t ap_get_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes)
apr_port_t ap_get_server_port(const request_rec *r)
Definition core.c:1199
const char * ap_get_server_name(request_rec *r)
Definition core.c:1145
apr_size_t ap_get_read_buf_size(const request_rec *r)
Definition core.c:1271
apr_socket_t * ap_get_conn_socket(conn_rec *c)
Definition core.c:5202
apr_status_t ap_get_pollfd_from_conn(conn_rec *c, struct apr_pollfd_t *pfd, apr_interval_time_t *ptimeout)
Definition core.c:5651
char * ap_construct_url(apr_pool_t *p, const char *uri, request_rec *r)
Definition core.c:1246
#define APLOG_TRACE8
Definition http_log.h:79
#define APLOGNO(n)
Definition http_log.h:117
#define APLOGrdebug(r)
Definition http_log.h:245
#define APLOG_STARTUP
Definition http_log.h:105
#define APLOG_INFO
Definition http_log.h:70
#define ap_log_rerror
Definition http_log.h:454
#define APLOG_ERR
Definition http_log.h:67
#define APLOG_TRACE3
Definition http_log.h:74
#define ap_log_error
Definition http_log.h:370
#define ap_log_cerror
Definition http_log.h:498
#define APLOGdebug(s)
Definition http_log.h:234
#define APLOG_MARK
Definition http_log.h:283
#define ap_log_perror
Definition http_log.h:412
#define APLOG_WARNING
Definition http_log.h:68
#define APLOG_CRIT
Definition http_log.h:66
#define APLOG_EMERG
Definition http_log.h:64
#define APLOG_TRACE2
Definition http_log.h:73
#define APLOG_TRACE1
Definition http_log.h:72
#define APLOG_TRACE5
Definition http_log.h:76
#define APLOGtrace2(s)
Definition http_log.h:236
#define APLOG_DEBUG
Definition http_log.h:71
server_rec * ap_server_conf
Definition config.c:62
const unsigned char * buf
Definition util_md5.h:50
int ap_map_http_request_error(apr_status_t rv, int status)
apr_bucket * ap_bucket_error_create(int error, const char *buf, apr_pool_t *p, apr_bucket_alloc_t *list)
void * ap_lookup_provider(const char *provider_group, const char *provider_name, const char *provider_version)
Definition provider.c:99
apr_status_t ap_check_pipeline(conn_rec *c, apr_bucket_brigade *bb, unsigned int max_blank_lines)
const char apr_port_t port
Definition http_vhost.h:125
ap_vhost_iterate_conn_cb void * baton
Definition http_vhost.h:87
const char * host
Definition http_vhost.h:124
apr_uint32_t ap_max_mem_free
Definition mpm_common.c:155
#define CRLF_ASCII
Definition httpd.h:737
#define CRLF
Definition httpd.h:724
#define APR_ENOTEMPTY
Definition apr_errno.h:826
#define APR_EGENERAL
Definition apr_errno.h:313
#define APR_INCOMPLETE
Definition apr_errno.h:452
#define APR_ENOTIMPL
Definition apr_errno.h:476
#define APR_ENOSOCKET
Definition apr_errno.h:307
#define APR_EINVAL
Definition apr_errno.h:711
#define APR_EPIPE
Definition apr_errno.h:812
#define APR_EEXIST
Definition apr_errno.h:648
#define APR_STATUS_IS_EINTR(s)
Definition apr_errno.h:1281
#define APR_STATUS_IS_INCOMPLETE(s)
Definition apr_errno.h:542
#define APR_STATUS_IS_ECONNRESET(s)
Definition apr_errno.h:1308
#define APR_STATUS_IS_TIMEUP(s)
Definition apr_errno.h:534
#define APR_STATUS_IS_EAGAIN(s)
Definition apr_errno.h:1272
#define APR_STATUS_IS_EOF(s)
Definition apr_errno.h:567
#define APR_UUID_FORMATTED_LENGTH
Definition apr_uuid.h:46
const apr_uuid_t * uuid
Definition apr_uuid.h:62
apr_file_t * f
#define APR_BUCKET_IS_FLUSH(e)
#define APR_BRIGADE_LAST(b)
#define APR_BUCKET_IS_METADATA(e)
#define APR_BRIGADE_INSERT_TAIL(b, e)
apr_file_t apr_off_t start
#define APR_BUCKET_NEXT(e)
apr_read_type_e
Definition apr_buckets.h:57
apr_bucket * e
#define APR_BRIGADE_CONCAT(a, b)
#define APR_BUCKET_BUFF_SIZE
Definition apr_buckets.h:54
#define APR_BRIGADE_EMPTY(b)
#define APR_BRIGADE_SENTINEL(b)
#define APR_BUCKET_IS_EOS(e)
#define APR_BRIGADE_FIRST(b)
#define apr_bucket_read(e, str, len, block)
@ APR_BLOCK_READ
Definition apr_buckets.h:58
@ APR_NONBLOCK_READ
Definition apr_buckets.h:59
apr_pool_t const char * params
Definition apr_dbd.h:141
const char * mask
Definition apr_date.h:60
const char * src
Definition apr_encode.h:167
const char apr_ssize_t int flags
Definition apr_encode.h:168
const char apr_ssize_t slen
Definition apr_encode.h:168
const char * url
Definition apr_escape.h:120
const char apr_ssize_t const char const char * reserved
Definition apr_escape.h:121
apr_memcache_server_t * server
#define APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ns, link, ret, name, args_decl, args_use, ok, decline)
#define APR_RETRIEVE_OPTIONAL_FN(name)
#define APR_OPTIONAL_FN_TYPE(name)
#define APR_REGISTER_OPTIONAL_FN(name)
apr_redis_t * rc
Definition apr_redis.h:173
void ** resource
#define APR_URI_UNP_REVEALPASSWORD
Definition apr_uri.h:73
const char * uri
Definition apr_uri.h:159
apr_text_header * hdr
Definition apr_xml.h:77
#define HTTP_OK
Definition httpd.h:490
#define HTTP_BAD_REQUEST
Definition httpd.h:508
#define HTTP_SERVICE_UNAVAILABLE
Definition httpd.h:538
#define HTTP_BAD_GATEWAY
Definition httpd.h:537
#define HTTP_INTERNAL_SERVER_ERROR
Definition httpd.h:535
#define ap_is_HTTP_ERROR(x)
Definition httpd.h:554
#define HTTP_FORBIDDEN
Definition httpd.h:511
#define HTTP_GATEWAY_TIME_OUT
Definition httpd.h:539
#define APR_PROTO_TCP
const apr_strmatch_pattern * ap_proxy_strmatch_path
Definition proxy_util.c:73
const apr_strmatch_pattern * ap_proxy_strmatch_domain
Definition proxy_util.c:74
int proxy_lb_workers
Definition proxy_util.c:71
void proxy_util_register_hooks(apr_pool_t *p)
#define PROXY_DECLARE_DATA
Definition mod_proxy.h:606
int ap_proxy_ssl_engine(conn_rec *c, ap_conf_vector_t *per_dir_config, int enable)
Definition mod_proxy.c:3061
#define PROXY_WORKER_IS_USABLE(f)
Definition mod_proxy.h:358
#define PROXY_CHECK_CONN_EMPTY
Definition mod_proxy.h:1135
int ap_proxy_connect_backend(const char *proxy_function, proxy_conn_rec *conn, proxy_worker *worker, server_rec *s)
apr_status_t 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)
#define AP_PROXY_TRANSFER_YIELD_MAX_READS
Definition mod_proxy.h:1518
#define AP_PROXY_WORKER_IS_MALLOCED
Definition mod_proxy.h:775
apr_status_t ap_proxy_set_wstatus(char c, int set, proxy_worker *w)
int ap_proxy_should_override(proxy_dir_conf *conf, int code)
#define PROXY_DECLARE(type)
Definition mod_proxy.h:604
#define PROXY_WORKER_STOPPED
Definition mod_proxy.h:326
#define PROXY_WORKER_IS_SPARE(f)
Definition mod_proxy.h:356
#define PROXY_STRNCPY(dst, src)
Definition mod_proxy.h:395
#define AP_PROXY_TRANSFER_FLUSH_AFTER
Definition mod_proxy.h:1516
int ap_proxy_read_input(request_rec *r, proxy_conn_rec *backend, apr_bucket_brigade *bb, apr_off_t max_read)
char * ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp, char **passwordp, char **hostp, apr_port_t *port)
Definition proxy_util.c:342
int ap_proxy_release_connection(const char *proxy_function, proxy_conn_rec *conn, server_rec *s)
int ap_proxy_tunnel_run(proxy_tunnel_rec *tunnel)
#define PROXY_WORKER_IN_ERROR
Definition mod_proxy.h:327
char * 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)
int ap_proxy_acquire_connection(const char *proxy_function, proxy_conn_rec **conn, proxy_worker *worker, server_rec *s)
apr_status_t ap_proxy_determine_address(const char *proxy_function, proxy_conn_rec *conn, const char *hostname, apr_port_t hostport, unsigned int flags, request_rec *r, server_rec *s)
char * ap_proxy_define_balancer(apr_pool_t *p, proxy_balancer **balancer, proxy_server_conf *conf, const char *url, const char *alias, int do_malloc)
int ap_proxy_hex2c(const char *x)
Definition proxy_util.c:118
proxy_worker * ap_proxy_get_worker(apr_pool_t *p, proxy_balancer *balancer, proxy_server_conf *conf, const char *url)
#define PROXY_WORKER_IGNORE_ERRORS
Definition mod_proxy.h:321
enctype
Definition mod_proxy.h:75
char * ap_proxy_define_worker(apr_pool_t *p, proxy_worker **worker, proxy_balancer *balancer, proxy_server_conf *conf, const char *url, int do_malloc)
const char * ap_proxy_de_socketfy(apr_pool_t *p, const char *url)
Definition mod_proxy.c:1938
proxy_worker_shared * ap_proxy_find_workershm(ap_slotmem_provider_t *storage, ap_slotmem_instance_t *slot, proxy_worker *worker, unsigned int *index)
int 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)
hcmethod_t
Definition mod_proxy.h:83
#define PROXY_THREAD_LOCK(x)
Definition mod_proxy.h:592
char * ap_proxy_canonenc_ex(apr_pool_t *p, const char *x, int len, enum enctype t, int flags, int proxyreq)
Definition proxy_util.c:220
int proxy_run_post_request(proxy_worker *worker, proxy_balancer *balancer, request_rec *r, proxy_server_conf *conf)
Definition mod_proxy.c:3425
#define PROXYPASS_INTERPOLATE
Definition mod_proxy.h:130
#define PROXY_WORKER_INITIALIZED
Definition mod_proxy.h:320
void ap_proxy_c2hex(int ch, char *x)
Definition proxy_util.c:173
#define PROXY_WORKER_IS_DRAINING(f)
Definition mod_proxy.h:361
void ap_proxy_backend_broke(request_rec *r, apr_bucket_brigade *brigade)
const char * ap_proxy_location_reverse_map(request_rec *r, proxy_dir_conf *conf, const char *url)
Definition proxy_util.c:882
int() proxy_is_best_callback_fn_t(proxy_worker *current, proxy_worker *prev_best, void *baton)
Definition mod_proxy.h:955
apr_status_t ap_proxy_tunnel_create(proxy_tunnel_rec **ptunnel, request_rec *r, conn_rec *c_o, const char *scheme)
apr_status_t ap_proxy_check_connection(const char *scheme, proxy_conn_rec *conn, server_rec *server, unsigned max_blank_lines, int flags)
#define AP_VOLATILIZE_T(T, x)
Definition mod_proxy.h:313
char * 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)
int ap_proxy_post_request(proxy_worker *worker, proxy_balancer *balancer, request_rec *r, proxy_server_conf *conf)
int ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf, apr_sockaddr_t *uri_addr)
Definition proxy_util.c:816
#define PROXY_WORKER_DEFAULT_RETRY
Definition mod_proxy.h:372
int ap_proxy_checkproxyblock2(request_rec *r, proxy_server_conf *conf, const char *hostname, apr_sockaddr_t *addr)
Definition proxy_util.c:824
proxy_worker * ap_proxy_get_worker_ex(apr_pool_t *p, proxy_balancer *balancer, proxy_server_conf *conf, const char *url, unsigned int mask)
#define PROXY_LBMETHOD
Definition mod_proxy.h:1444
proxy_balancer * ap_proxy_get_balancer(apr_pool_t *p, proxy_server_conf *conf, const char *url, int care)
int ap_proxy_connection_reusable(proxy_conn_rec *conn)
#define PROXY_THREAD_UNLOCK(x)
Definition mod_proxy.h:593
#define AP_PROXY_WORKER_NO_UDS
Definition mod_proxy.h:776
#define PROXY_CANONENC_NOENCODEDSLASHENCODING
Definition mod_proxy.h:81
int ap_proxy_valid_balancer_name(char *name, int i)
#define AP_PROXY_WORKER_IS_MATCH
Definition mod_proxy.h:774
int ap_proxy_pre_http_request(conn_rec *c, request_rec *r)
Definition proxy_util.c:876
apr_status_t ap_proxy_strncpy(char *dst, const char *src, apr_size_t dlen)
Definition proxy_util.c:94
apr_status_t ap_proxy_share_worker(proxy_worker *worker, proxy_worker_shared *shm, int i)
int ap_proxy_worker_can_upgrade(apr_pool_t *p, const proxy_worker *worker, const char *upgrade, const char *dflt)
int proxy_run_fixups(request_rec *r)
Definition mod_proxy.c:3434
apr_status_t ap_proxy_buckets_lifetime_transform(request_rec *r, apr_bucket_brigade *from, apr_bucket_brigade *to)
int proxy_run_pre_request(proxy_worker **worker, proxy_balancer **balancer, request_rec *r, proxy_server_conf *conf, char **url)
Definition mod_proxy.c:3419
proxy_wstat_t proxy_wstat_tbl[]
Definition mod_proxy.c:61
int ap_proxy_pre_request(proxy_worker **worker, proxy_balancer **balancer, request_rec *r, proxy_server_conf *conf, char **url)
#define PROXY_DYNAMIC_BALANCER_LIMIT
Definition mod_proxy.h:1449
apr_status_t ap_proxy_connect_uds(apr_socket_t *sock, const char *uds_path, apr_pool_t *p)
#define PROXY_CANONENC_FORCEDEC
Definition mod_proxy.h:80
#define PROXY_WORKER_IS(f, b)
Definition mod_proxy.h:369
int 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)
char * ap_proxy_parse_wstatus(apr_pool_t *p, proxy_worker *w)
apr_status_t ap_proxy_sync_balancer(proxy_balancer *b, server_rec *s, proxy_server_conf *conf)
proxy_balancer_shared * ap_proxy_find_balancershm(ap_slotmem_provider_t *storage, ap_slotmem_instance_t *slot, proxy_balancer *balancer, unsigned int *index)
int ap_proxy_connection_create_ex(const char *proxy_function, proxy_conn_rec *conn, request_rec *r)
apr_status_t ap_proxy_share_balancer(proxy_balancer *balancer, proxy_balancer_shared *shm, int i)
#define PROXY_SHOULD_PING_100_CONTINUE(w, r)
Definition mod_proxy.h:407
apr_status_t ap_proxy_ssl_connection_cleanup(proxy_conn_rec *conn, request_rec *r)
unsigned int ap_proxy_hashfunc(const char *str, proxy_hash_t method)
const char * ap_proxy_show_hcmethod(hcmethod_t method)
int ap_proxy_fixup_uds_filename(request_rec *r)
int ap_proxy_lb_workers(void)
apr_status_t ap_proxy_initialize_worker(proxy_worker *worker, server_rec *s, apr_pool_t *p)
#define BALANCER_PREFIX
Definition mod_proxy.h:99
#define HCHECK_WATHCHDOG_DEFAULT_INTERVAL
Definition mod_proxy.h:516
#define PROXY_WORKER_RFC1035_NAME_SIZE
Definition mod_proxy.h:391
apr_port_t ap_proxy_port_of_scheme(const char *scheme)
#define PROXY_DETERMINE_ADDRESS_CHECK
Definition mod_proxy.h:1047
int 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)
int ap_proxyerror(request_rec *r, int statuscode, const char *message)
Definition proxy_util.c:432
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)
apr_status_t ap_proxy_initialize_balancer(proxy_balancer *balancer, server_rec *s, apr_pool_t *p)
proxy_worker * ap_proxy_balancer_get_best_worker(proxy_balancer *balancer, request_rec *r, proxy_is_best_callback_fn_t *is_best, void *baton)
#define PROXY_FLUSH_WAIT
Definition mod_proxy.h:524
proxy_hash_t
Definition mod_proxy.h:1234
int proxy_run_canon_handler(request_rec *r, char *url)
Definition mod_proxy.c:3412
#define AP_PROXY_TRANSFER_YIELD_PENDING
Definition mod_proxy.h:1517
const char * ap_proxy_cookie_reverse_map(request_rec *r, proxy_dir_conf *conf, const char *str)
int ap_proxy_connection_create(const char *proxy_function, proxy_conn_rec *conn, conn_rec *c, server_rec *s)
char * ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, enum enctype t, int forcedec, int proxyreq)
Definition proxy_util.c:321
proxy_hcmethods_t proxy_hcmethods[]
Definition mod_proxy.c:47
#define PROXY_WORKER_IS_STANDBY(f)
Definition mod_proxy.h:354
char * ap_proxy_worker_name(apr_pool_t *p, proxy_worker *worker)
#define AP_PROXY_WORKER_IS_PREFIX
Definition mod_proxy.h:773
int 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)
int 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)
char * ap_proxy_update_balancer(apr_pool_t *p, proxy_balancer *balancer, const char *url)
int ap_proxy_is_socket_connected(apr_socket_t *sock)
@ enc_user
Definition mod_proxy.h:76
@ enc_path
Definition mod_proxy.h:76
@ enc_fpath
Definition mod_proxy.h:76
@ enc_search
Definition mod_proxy.h:76
@ PROXY_HASHFUNC_DEFAULT
Definition mod_proxy.h:1234
@ PROXY_HASHFUNC_FNV
Definition mod_proxy.h:1234
@ PROXY_HASHFUNC_APR
Definition mod_proxy.h:1234
#define ap_strchr(s, c)
Definition httpd.h:2351
void ap_random_insecure_bytes(void *buf, apr_size_t size)
Definition core.c:5455
int ap_is_url(const char *u)
Definition util.c:2377
int ap_cstr_casecmp(const char *s1, const char *s2)
Definition util.c:3542
#define ap_strstr_c(s, c)
Definition httpd.h:2361
#define ap_strchr_c(s, c)
Definition httpd.h:2353
char * ap_get_token(apr_pool_t *p, const char **accept_line, int accept_white)
Definition util.c:1687
#define AP_DEBUG_ASSERT(exp)
Definition httpd.h:2283
void ap_abort_on_oom(void) __attribute__((noreturn))
Definition util.c:3136
int ap_find_token(apr_pool_t *p, const char *line, const char *tok)
Definition util.c:1726
int ap_cstr_casecmpn(const char *s1, const char *s2, apr_size_t n)
Definition util.c:3559
#define PROXYREQ_PROXY
Definition httpd.h:1134
#define ap_escape_html(p, s)
Definition httpd.h:1860
void ap_str_tolower(char *s)
Definition util.c:2410
#define ap_assert(exp)
Definition httpd.h:2271
#define PROXYREQ_REVERSE
Definition httpd.h:1135
apr_status_t ap_pstr2_alnum(apr_pool_t *p, const char *src, const char **dest)
Definition util.c:2733
void * ap_malloc(size_t size) __attribute__((malloc))
Definition util.c:3152
@ AP_CONN_CLOSE
Definition httpd.h:1145
apr_size_t size
apr_uint32_t val
Definition apr_atomic.h:66
const char int apr_pool_t * pool
Definition apr_cstr.h:84
#define apr_isspace(c)
Definition apr_lib.h:225
#define apr_isupper(c)
Definition apr_lib.h:227
#define apr_isalnum(c)
Definition apr_lib.h:203
#define apr_isdigit(c)
Definition apr_lib.h:209
#define apr_isxdigit(c)
Definition apr_lib.h:229
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
const char * key
void apr_size_t * nbytes
const char apr_int32_t flag
const struct iovec * vec
void apr_size_t apr_size_t * bytes_read
void * data
const void apr_size_t apr_size_t * bytes_written
void const char apr_status_t(* cleanup)(void *))
char * buffer
#define APR_FILEPATH_NATIVE
int strcasecmp(const char *a, const char *b)
int strncasecmp(const char *a, const char *b, size_t n)
#define memmove(a, b, c)
apr_vformatter_buff_t * c
Definition apr_lib.h:175
apr_byte_t ttl
apr_sockaddr_t * addr
apr_sockaddr_t * sockaddr
#define APR_INADDR_NONE
apr_socket_t * sock
const apr_sockaddr_t * addr2
apr_sockaddr_t * sa
apr_interval_time_t t
#define APR_UNIX
apr_uint16_t apr_port_t
#define APR_UNSPEC
char ** scope_id
@ APR_LOCAL
@ APR_REMOTE
apr_uint32_t apr_pool_t apr_uint32_t apr_pollset_method_e method
Definition apr_poll.h:195
apr_interval_time_t apr_int32_t * num
Definition apr_poll.h:273
@ APR_POLL_SOCKET
Definition apr_poll.h:93
@ APR_POLL_FILE
Definition apr_poll.h:94
@ APR_NO_DESC
Definition apr_poll.h:92
apr_pool_t * b
Definition apr_pools.h:529
#define apr_pool_create(newpool, parent)
Definition apr_pools.h:322
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
int apr_os_sock_t
apr_shm_t * shm
const void apr_size_t bytes
Definition apr_random.h:91
int to
#define APR_SO_KEEPALIVE
#define APR_TCP_NODELAY
#define APR_SO_RCVBUF
#define APR_SO_NONBLOCK
const char char ** end
const char char ** last
const char * s
Definition apr_strings.h:95
const void * m
apr_status_t apr_wait_for_io_or_timeout(apr_file_t *f, apr_socket_t *s, int for_read)
#define APR_ARRAY_PUSH(ary, type)
Definition apr_tables.h:150
int nelts
Definition apr_tables.h:122
#define APR_ARRAY_IDX(ary, i, type)
Definition apr_tables.h:141
const char const char * password
apr_int32_t apr_int32_t apr_int32_t err
apr_int32_t in
int int status
#define APR_TIME_T_FMT
Definition apr_time.h:52
apr_int64_t apr_interval_time_t
Definition apr_time.h:55
#define APR_USEC_PER_SEC
Definition apr_time.h:60
apr_size_t apr_size_t max
Definition apr_time.h:220
apr_int64_t apr_time_t
Definition apr_time.h:45
#define apr_time_sec(time)
Definition apr_time.h:63
#define apr_time_from_sec(sec)
Definition apr_time.h:78
apr_status_t ap_mpm_query(int query_code, int *result)
Definition mpm_common.c:421
#define APR_POLLSET_NOCOPY
Definition apr_poll.h:65
#define APR_POLLERR
Definition apr_poll.h:52
#define APR_POLLOUT
Definition apr_poll.h:51
#define APR_POLLIN
Definition apr_poll.h:49
#define APR_POLLHUP
Definition apr_poll.h:53
#define AP_MPMQ_MAX_THREADS
Definition ap_mpm.h:160
static struct h2_workers * workers
Definition h2_c1.c:48
CORE HTTP Daemon.
apr_pool_t * p
Definition md_event.c:32
static const ap_slotmem_provider_t * storage
static void http2_get_num_workers(server_rec *s, int *minw, int *maxw)
Definition mod_http2.c:152
static apr_file_t * out
Definition mod_info.c:85
Proxy Extension Module for Apache.
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
sconf
Definition mod_so.c:349
Multi-Processing Modules functions.
static apr_status_t connection_shutdown(void *theconn)
static int proxy_match_word(struct dirconn_entry *This, request_rec *r)
Definition proxy_util.c:809
static void del_pollset(apr_pollset_t *pollset, apr_pollfd_t *pfd, apr_int16_t events)
static apr_status_t worker_address_resolve(proxy_worker *worker, apr_sockaddr_t **paddr, const char *hostname, apr_port_t hostport, const char *proxy_function, request_rec *r, server_rec *s)
static proxy_schemes_t pschemes[]
static APR_INLINE void proxy_address_inc(proxy_address *address)
int ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p)
Definition proxy_util.c:675
static APR_INLINE int ap_filter_should_yield(ap_filter_t *f)
static int fixup_uds_filename(request_rec *r)
static void add_pollset(apr_pollset_t *pollset, apr_pollfd_t *pfd, apr_int16_t events)
static const apr_time_t * proxy_start_time
Definition proxy_util.c:78
static apr_status_t connection_constructor(void **resource, void *params, apr_pool_t *pool)
static void socket_cleanup(proxy_conn_rec *conn)
#define IS_REF(x)
static int find_conn_headers(void *data, const char *key, const char *val)
static void connection_cleanup(void *theconn)
static void conn_cleanup(proxy_conn_rec *conn)
static apr_pool_t * make_conn_subpool(apr_pool_t *p, const char *tag, server_rec *s)
int ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p)
Definition proxy_util.c:466
static apr_status_t conn_pool_cleanup(void *theworker)
static APR_INLINE proxy_address * worker_address_get(proxy_worker *worker)
int ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p)
Definition proxy_util.c:802
static apr_status_t proxy_address_cleanup(void *address)
static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r)
Definition proxy_util.c:587
const char * ap_proxy_interpolate(request_rec *r, const char *str)
int ap_proxy_canon_url(request_rec *r)
static proxy_conn_rec * connection_make(apr_pool_t *p, proxy_worker *worker)
static int ap_proxy_retry_worker(const char *proxy_function, proxy_worker *worker, server_rec *s)
int ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p)
Definition proxy_util.c:736
static int proxy_addrs_equal(const apr_sockaddr_t *addr1, const apr_sockaddr_t *addr2)
apr_global_mutex_t * proxy_mutex
Definition mod_proxy.c:78
static apr_array_header_t * proxy_vars(request_rec *r, apr_array_header_t *hdr)
static int proxy_connection_create(const char *proxy_function, proxy_conn_rec *conn, request_rec *r, server_rec *s)
#define PROXY_TRANSFER_MAX_READS
static APR_INLINE int error_code_overridden(const int *elts, int nelts, int code)
static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r)
Definition proxy_util.c:712
static void init_conn_pool(apr_pool_t *p, proxy_worker *worker, server_rec *s)
static apr_status_t send_http_connect(proxy_conn_rec *backend, server_rec *s)
static int ap_proxy_strcmp_ematch(const char *str, const char *expected)
static APR_INLINE void proxy_address_dec(proxy_address *address)
static APR_INLINE int ap_filter_output_pending(conn_rec *c)
#define MAX_IP_STR_LEN
Definition proxy_util.c:822
static int proxy_tunnel_forward(proxy_tunnel_rec *tunnel, struct proxy_tunnel_conn *in)
#define PROXY_UNSET_NONCE
static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r)
Definition proxy_util.c:765
static int proxyerror_core(request_rec *r, int statuscode, const char *message, apr_status_t rv)
Definition proxy_util.c:412
static proxy_worker * proxy_balancer_get_best_worker(proxy_balancer *balancer, request_rec *r, proxy_is_best_callback_fn_t *is_best, void *baton)
static int ap_proxy_clear_connection(request_rec *r, apr_table_t *headers)
static const char * proxy_get_host_of_request(request_rec *r)
Definition proxy_util.c:438
static apr_status_t connection_destructor(void *resource, void *params, apr_pool_t *pool)
static int lb_workers_limit
Definition proxy_util.c:72
static APR_INLINE void worker_address_set(proxy_worker *worker, proxy_address *to)
Internal interfaces private to mod_proxy.
Shared SCGI-related definitions.
#define SCGI_DEF_PORT
Definition scgi.h:32
Apache scoreboard library.
static char * address(sed_commands_t *commands, char *expbuf, apr_status_t *status)
Definition sed0.c:812
char * name
The representation of a filter chain.
apr_status_t(* dptr)(ap_slotmem_instance_t *s, unsigned int item_id, void **mem)
Definition ap_slotmem.h:130
apr_status_t(* attach)(ap_slotmem_instance_t **inst, const char *name, apr_size_t *item_size, unsigned int *item_num, apr_pool_t *pool)
Definition ap_slotmem.h:122
unsigned int(* num_slots)(ap_slotmem_instance_t *s)
Definition ap_slotmem.h:154
apr_bucket_alloc_t * bucket_alloc
const char * name
const apr_bucket_type_t * type
void * client_data
Definition apr_poll.h:114
apr_int16_t reqevents
Definition apr_poll.h:111
apr_datatype_e desc_type
Definition apr_poll.h:110
apr_descriptor desc
Definition apr_poll.h:113
apr_int16_t rtnevents
Definition apr_poll.h:112
int * pollset
Definition pollset.c:29
apr_pool_t * pool
apr_sockaddr_t * next
Definition apr_tables.h:81
unsigned char data[16]
Definition apr_uuid.h:42
Structure to store things which are per connection.
Definition httpd.h:1152
ap_conn_keepalive_e keepalive
Definition httpd.h:1223
struct ap_filter_t * input_filters
Definition httpd.h:1195
struct ap_filter_t * output_filters
Definition httpd.h:1197
apr_table_t * notes
Definition httpd.h:1193
unsigned aborted
Definition httpd.h:1219
Definition mod_proxy.h:142
int use_http_connect
Definition proxy_util.c:53
apr_port_t target_port
Definition proxy_util.c:55
const char * proxy_auth
Definition proxy_util.c:56
const char * target_host
Definition proxy_util.c:54
apr_array_header_t * array
apr_pool_t * pool
const char * first
unsigned int closed
apr_uint32_t s_addr
Definition mod_proxy.h:149
apr_pool_t * pool
Definition httpd.h:831
apr_uint32_t refcount
Definition proxy_util.c:66
const char * hostname
Definition proxy_util.c:64
apr_uint32_t expiry
Definition proxy_util.c:67
apr_sockaddr_t * addr
Definition proxy_util.c:63
apr_port_t hostport
Definition proxy_util.c:65
proxy_balancer * balancer
Definition mod_proxy.h:139
const char * real
Definition mod_proxy.h:135
const char * fake
Definition mod_proxy.h:136
const char * name
Definition mod_proxy.h:583
proxy_hashes hash
Definition mod_proxy.h:539
unsigned int inactive
Definition mod_proxy.h:546
unsigned int was_malloced
Definition mod_proxy.h:543
ap_slotmem_instance_t * wslot
Definition mod_proxy.h:561
proxy_balancer_shared * s
Definition mod_proxy.h:574
apr_time_t wupdated
Definition mod_proxy.h:566
ap_slotmem_provider_t * storage
Definition mod_proxy.h:562
unsigned int lbmethod_set
Definition mod_proxy.h:578
apr_global_mutex_t * gmutex
Definition mod_proxy.h:568
proxy_balancer_method * lbmethod
Definition mod_proxy.h:567
apr_array_header_t * workers
Definition mod_proxy.h:559
proxy_hashes hash
Definition mod_proxy.h:565
apr_pool_t * dns_pool
Definition mod_proxy.h:310
proxy_conn_rec * conn
Definition mod_proxy.h:309
apr_pool_t * pool
Definition mod_proxy.h:306
apr_sockaddr_t * addr
Definition mod_proxy.h:307
apr_reslist_t * res
Definition mod_proxy.h:308
const char * uds_path
Definition mod_proxy.h:288
const char * hostname
Definition mod_proxy.h:275
apr_pool_t * pool
Definition mod_proxy.h:274
unsigned int inreslist
Definition mod_proxy.h:287
apr_bucket_brigade * tmp_bb
Definition mod_proxy.h:290
struct proxy_address * address
Definition mod_proxy.h:296
apr_pool_t * uds_pool
Definition mod_proxy.h:294
apr_pool_t * scpool
Definition mod_proxy.h:277
request_rec * r
Definition mod_proxy.h:271
apr_sockaddr_t * addr
Definition mod_proxy.h:276
apr_socket_t * sock
Definition mod_proxy.h:278
proxy_worker * worker
Definition mod_proxy.h:273
apr_port_t port
Definition mod_proxy.h:282
unsigned int is_ssl
Definition mod_proxy.h:283
const char * ssl_hostname
Definition mod_proxy.h:289
unsigned int close
Definition mod_proxy.h:284
conn_rec * connection
Definition mod_proxy.h:270
apr_array_header_t * cookie_domains
Definition mod_proxy.h:228
signed char interpolate_env
Definition mod_proxy.h:230
apr_array_header_t * raliases
Definition mod_proxy.h:226
unsigned int error_override
Definition mod_proxy.h:241
unsigned int preserve_host
Definition mod_proxy.h:242
apr_array_header_t * cookie_paths
Definition mod_proxy.h:227
unsigned int add_forwarded_headers
Definition mod_proxy.h:246
apr_array_header_t * error_override_codes
Definition mod_proxy.h:255
unsigned int fnv
Definition mod_proxy.h:419
unsigned int def
Definition mod_proxy.h:418
apr_port_t default_port
const char * name
apr_size_t recv_buffer_size
Definition mod_proxy.h:176
apr_array_header_t * workers
Definition mod_proxy.h:160
const char * id
Definition mod_proxy.h:165
proxy_worker * forward
Definition mod_proxy.h:162
unsigned int source_address_set
Definition mod_proxy.h:203
unsigned int timeout_set
Definition mod_proxy.h:200
apr_sockaddr_t * source_address
Definition mod_proxy.h:190
apr_pool_t * pool
Definition mod_proxy.h:166
apr_interval_time_t timeout
Definition mod_proxy.h:179
enum proxy_server_conf::@32 viaopt
apr_array_header_t * balancers
Definition mod_proxy.h:161
proxy_worker * reverse
Definition mod_proxy.h:163
apr_array_header_t * noproxies
Definition mod_proxy.h:158
unsigned int down_in
apr_bucket_brigade * bb
unsigned int down_out
const char * name
struct proxy_tunnel_conn * other
apr_pollfd_t * pfd
apr_interval_time_t ttl
Definition mod_proxy.h:450
unsigned int conn_timeout_set
Definition mod_proxy.h:472
unsigned int keepalive
Definition mod_proxy.h:465
unsigned int timeout_set
Definition mod_proxy.h:469
apr_size_t recv_buffer_size
Definition mod_proxy.h:457
apr_interval_time_t timeout
Definition mod_proxy.h:453
char hostname_ex[256]
Definition mod_proxy.h:488
apr_off_t transferred
Definition mod_proxy.h:462
unsigned int is_name_matchable
Definition mod_proxy.h:478
apr_uint32_t address_expiry
Definition mod_proxy.h:495
apr_time_t error_time
Definition mod_proxy.h:449
apr_interval_time_t retry
Definition mod_proxy.h:452
unsigned int was_malloced
Definition mod_proxy.h:477
unsigned int disablereuse_set
Definition mod_proxy.h:476
unsigned int status
Definition mod_proxy.h:442
unsigned int address_ttl_set
Definition mod_proxy.h:493
apr_interval_time_t conn_timeout
Definition mod_proxy.h:456
proxy_hashes hash
Definition mod_proxy.h:441
unsigned int retry_set
Definition mod_proxy.h:468
apr_int32_t address_ttl
Definition mod_proxy.h:494
unsigned int is_address_reusable
Definition mod_proxy.h:467
unsigned int disablereuse
Definition mod_proxy.h:466
apr_interval_time_t acquire
Definition mod_proxy.h:454
unsigned int acquire_set
Definition mod_proxy.h:470
unsigned int local_status
Definition mod_proxy.h:503
ap_conf_vector_t * section_config
Definition mod_proxy.h:511
struct proxy_address *volatile address
Definition mod_proxy.h:512
proxy_conn_pool * cp
Definition mod_proxy.h:504
proxy_worker_shared * s
Definition mod_proxy.h:505
proxy_hashes hash
Definition mod_proxy.h:502
unsigned int bit
Definition mod_proxy.h:94
A structure that represents the current request.
Definition httpd.h:845
char * user
Definition httpd.h:1005
char * uri
Definition httpd.h:1016
struct ap_filter_t * output_filters
Definition httpd.h:1070
char * useragent_ip
Definition httpd.h:1101
struct ap_filter_t * proto_input_filters
Definition httpd.h:1079
apr_table_t * notes
Definition httpd.h:985
const char * hostname
Definition httpd.h:883
apr_pool_t * pool
Definition httpd.h:847
char * filename
Definition httpd.h:1018
int proxyreq
Definition httpd.h:873
conn_rec * connection
Definition httpd.h:849
int no_cache
Definition httpd.h:1082
struct ap_filter_t * proto_output_filters
Definition httpd.h:1076
int proto_num
Definition httpd.h:877
struct ap_filter_t * input_filters
Definition httpd.h:1072
struct ap_conf_vector_t * request_config
Definition httpd.h:1049
apr_table_t * headers_in
Definition httpd.h:976
request_rec * main
Definition httpd.h:860
apr_table_t * subprocess_env
Definition httpd.h:983
server_rec * server
Definition httpd.h:851
struct ap_conf_vector_t * per_dir_config
Definition httpd.h:1047
const char * status_line
Definition httpd.h:889
const char * method
Definition httpd.h:900
A structure to store information for each virtual server.
Definition httpd.h:1322
apr_interval_time_t timeout
Definition httpd.h:1372
process_rec * process
Definition httpd.h:1324
char * server_hostname
Definition httpd.h:1365
struct ap_conf_vector_t * module_config
Definition httpd.h:1341
static apr_pollset_t * pollset
Definition testpoll.c:41
static apr_time_t now
Definition testtime.c:33
apr_socket_t * s
Definition apr_poll.h:101
apr_status_t apr_socket_recvfrom(apr_sockaddr_t *from, apr_socket_t *sock, apr_int32_t flags, char *buf, apr_size_t *len)
Definition sendrecv.c:146
apr_status_t apr_socket_send(apr_socket_t *sock, const char *buf, apr_size_t *len)
Definition sendrecv.c:30
apr_status_t apr_socket_recv(apr_socket_t *sock, char *buf, apr_size_t *len)
Definition sendrecv.c:70
apr_status_t apr_os_sock_get(apr_os_sock_t *thesock, apr_socket_t *sock)
Definition sockets.c:506
apr_status_t apr_socket_bind(apr_socket_t *sock, apr_sockaddr_t *sa)
Definition sockets.c:216
apr_status_t apr_socket_close(apr_socket_t *thesocket)
Definition sockets.c:211
apr_status_t apr_socket_connect(apr_socket_t *sock, apr_sockaddr_t *sa)
Definition sockets.c:388
apr_status_t apr_socket_shutdown(apr_socket_t *thesocket, apr_shutdown_how_e how)
Definition sockets.c:205
apr_status_t apr_socket_create(apr_socket_t **new, int ofamily, int type, int protocol, apr_pool_t *cont)
Definition sockets.c:116
apr_status_t apr_socket_opt_set(apr_socket_t *sock, apr_int32_t opt, apr_int32_t on)
Definition sockopt.c:113
apr_status_t apr_socket_timeout_set(apr_socket_t *sock, apr_interval_time_t t)
Definition sockopt.c:75
apr_status_t apr_socket_timeout_get(apr_socket_t *sock, apr_interval_time_t *t)
Definition sockopt.c:355
#define var
#define str
@ AP_MODE_READBYTES
Definition util_filter.h:43
IN ULONG IN INT timeout