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
8
* http://www.apache.org/licenses/LICENSE-2.0
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.
17
/* Utility routines for Apache proxy */
18
#include "mod_proxy.h"
20
#include "scoreboard.h"
21
#include "apr_version.h"
24
#include <unistd.h> /* for getpid() */
27
#if (APR_MAJOR_VERSION < 1)
28
#undef apr_socket_create
29
#define apr_socket_create apr_socket_create_ex
32
/* Global balancer counter */
33
int PROXY_DECLARE_DATA proxy_lb_workers = 0;
34
static int lb_workers_limit = 0;
36
static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r);
37
static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r);
38
static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r);
39
static int proxy_match_word(struct dirconn_entry *This, request_rec *r);
41
APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, create_req,
42
(request_rec *r, request_rec *pr), (r, pr),
45
/* already called in the knowledge that the characters are hex digits */
46
PROXY_DECLARE(int) ap_proxy_hex2c(const char *x)
50
#if !APR_CHARSET_EBCDIC
54
else if (apr_isupper(ch))
63
else if (apr_isupper(ch))
68
#else /*APR_CHARSET_EBCDIC*/
70
* we assume that the hex value refers to an ASCII character
71
* so convert to EBCDIC so that it makes sense locally;
75
* client specifies %20 in URL to refer to a space char;
76
* at this point we're called with EBCDIC "20"; after turning
77
* EBCDIC "20" into binary 0x20, we then need to assume that 0x20
78
* represents an ASCII char and convert 0x20 to EBCDIC, yielding
83
if (1 == sscanf(x, "%2x", &i)) {
85
ap_xlate_proto_from_ascii(buf, 1);
91
#endif /*APR_CHARSET_EBCDIC*/
94
PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x)
96
#if !APR_CHARSET_EBCDIC
100
i = (ch & 0xF0) >> 4;
102
x[1] = ('A' - 10) + i;
108
x[2] = ('A' - 10) + i;
111
#else /*APR_CHARSET_EBCDIC*/
112
static const char ntoa[] = { "0123456789ABCDEF" };
118
ap_xlate_proto_to_ascii(buf, 1);
121
x[1] = ntoa[(buf[0] >> 4) & 0x0F];
122
x[2] = ntoa[buf[0] & 0x0F];
124
#endif /*APR_CHARSET_EBCDIC*/
128
* canonicalise a URL-encoded string
132
* Convert a URL-encoded string to canonical form.
133
* It decodes characters which need not be encoded,
134
* and encodes those which must be encoded, and does not touch
135
* those which must not be touched.
137
PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, enum enctype t,
138
int forcedec, int proxyreq)
142
char *allowed; /* characters which should not be encoded */
143
char *reserved; /* characters which much not be en/de-coded */
146
* N.B. in addition to :@&=, this allows ';' in an http path
147
* and '?' in an ftp path -- this may be revised
149
* Also, it makes a '+' character in a search string reserved, as
150
* it may be form-encoded. (Although RFC 1738 doesn't allow this -
151
* it only permits ; / ? : @ = & as reserved chars.)
154
allowed = "$-_.+!*'(),;:@&=";
155
else if (t == enc_search)
156
allowed = "$-_.!*'(),;:@&=";
157
else if (t == enc_user)
158
allowed = "$-_.+!*'(),;@&=";
159
else if (t == enc_fpath)
160
allowed = "$-_.+!*'(),?:@&=";
161
else /* if (t == enc_parm) */
162
allowed = "$-_.+!*'(),?/:@&=";
166
else if (t == enc_search)
171
y = apr_palloc(p, 3 * len + 1);
173
for (i = 0, j = 0; i < len; i++, j++) {
174
/* always handle '/' first */
176
if (strchr(reserved, ch)) {
181
* decode it if not already done. do not decode reverse proxied URLs
182
* unless specifically forced
184
if ((forcedec || (proxyreq && proxyreq != PROXYREQ_REVERSE)) && ch == '%') {
185
if (!apr_isxdigit(x[i + 1]) || !apr_isxdigit(x[i + 2]))
187
ch = ap_proxy_hex2c(&x[i + 1]);
189
if (ch != 0 && strchr(reserved, ch)) { /* keep it encoded */
190
ap_proxy_c2hex(ch, &y[j]);
195
/* recode it, if necessary */
196
if (!apr_isalnum(ch) && !strchr(allowed, ch)) {
197
ap_proxy_c2hex(ch, &y[j]);
208
* Parses network-location.
209
* urlp on input the URL; on output the path, after the leading /
210
* user NULL if no user/password permitted
211
* password holder for password
212
* host holder for host
213
* port port number; only set if one is supplied.
215
* Returns an error string.
217
PROXY_DECLARE(char *)
218
ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp,
219
char **passwordp, char **hostp, apr_port_t *port)
221
char *addr, *scope_id, *strp, *host, *url = *urlp;
222
char *user = NULL, *password = NULL;
226
if (url[0] != '/' || url[1] != '/')
227
return "Malformed URL";
229
url = strchr(host, '/');
233
*(url++) = '\0'; /* skip seperating '/' */
235
/* find _last_ '@' since it might occur in user/password part */
236
strp = strrchr(host, '@');
244
strp = strchr(user, ':');
247
password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, 1, 0);
248
if (password == NULL)
249
return "Bad %-escape in URL (password)";
252
user = ap_proxy_canonenc(p, user, strlen(user), enc_user, 1, 0);
254
return "Bad %-escape in URL (username)";
259
if (passwordp != NULL) {
260
*passwordp = password;
264
* Parse the host string to separate host portion from optional port.
265
* Perform range checking on port.
267
rv = apr_parse_addr_port(&addr, &scope_id, &tmp_port, host, p);
268
if (rv != APR_SUCCESS || addr == NULL || scope_id != NULL) {
269
return "Invalid host/port";
271
if (tmp_port != 0) { /* only update caller's port if port was specified */
275
ap_str_tolower(addr); /* DNS names are case-insensitive */
283
static const char * const lwday[7] =
284
{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
287
* If the date is a valid RFC 850 date or asctime() date, then it
288
* is converted to the RFC 1123 format, otherwise it is not modified.
289
* This routine is not very fast at doing conversions, as it uses
290
* sscanf and sprintf. However, if the date is already correctly
291
* formatted, then it exits very quickly.
293
PROXY_DECLARE(const char *)
294
ap_proxy_date_canon(apr_pool_t *p, const char *x1)
296
char *x = apr_pstrdup(p, x1);
297
int wk, mday, year, hour, min, sec, mon;
298
char *q, month[4], zone[4], week[4];
301
/* check for RFC 850 date */
302
if (q != NULL && q - x > 3 && q[1] == ' ') {
304
for (wk = 0; wk < 7; wk++)
305
if (strcmp(x, lwday[wk]) == 0)
309
return x; /* not a valid date */
310
if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' ||
311
q[17] != ':' || strcmp(&q[20], " GMT") != 0)
313
if (sscanf(q + 2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year,
314
&hour, &min, &sec, zone) != 7)
322
/* check for acstime() date */
323
if (x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' ||
324
x[16] != ':' || x[19] != ' ' || x[24] != '\0')
326
if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour,
327
&min, &sec, &year) != 7)
329
for (wk = 0; wk < 7; wk++)
330
if (strcmp(week, apr_day_snames[wk]) == 0)
337
for (mon = 0; mon < 12; mon++)
338
if (strcmp(month, apr_month_snames[mon]) == 0)
343
q = apr_palloc(p, 30);
344
apr_snprintf(q, 30, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", apr_day_snames[wk],
345
mday, apr_month_snames[mon], year, hour, min, sec);
349
PROXY_DECLARE(request_rec *)ap_proxy_make_fake_req(conn_rec *c, request_rec *r)
351
request_rec *rp = apr_pcalloc(c->pool, sizeof(*r));
354
rp->status = HTTP_OK;
356
rp->headers_in = apr_table_make(c->pool, 50);
357
rp->subprocess_env = apr_table_make(c->pool, 50);
358
rp->headers_out = apr_table_make(c->pool, 12);
359
rp->err_headers_out = apr_table_make(c->pool, 5);
360
rp->notes = apr_table_make(c->pool, 5);
362
rp->server = r->server;
363
rp->proxyreq = r->proxyreq;
364
rp->request_time = r->request_time;
366
rp->output_filters = c->output_filters;
367
rp->input_filters = c->input_filters;
368
rp->proto_output_filters = c->output_filters;
369
rp->proto_input_filters = c->input_filters;
371
rp->request_config = ap_create_request_config(c->pool);
372
proxy_run_create_req(r, rp);
379
* list is a comma-separated list of case-insensitive tokens, with
380
* optional whitespace around the tokens.
381
* The return returns 1 if the token val is found in the list, or 0
384
PROXY_DECLARE(int) ap_proxy_liststr(const char *list, const char *val)
391
while (list != NULL) {
392
p = ap_strchr_c(list, ',');
397
while (apr_isspace(*p));
402
while (i > 0 && apr_isspace(list[i - 1]))
404
if (i == len && strncasecmp(list, val, len) == 0)
412
* list is a comma-separated list of case-insensitive tokens, with
413
* optional whitespace around the tokens.
414
* if val appears on the list of tokens, it is removed from the list,
415
* and the new list is returned.
417
PROXY_DECLARE(char *)ap_proxy_removestr(apr_pool_t *pool, const char *list, const char *val)
425
while (list != NULL) {
426
p = ap_strchr_c(list, ',');
431
while (apr_isspace(*p));
436
while (i > 0 && apr_isspace(list[i - 1]))
438
if (i == len && strncasecmp(list, val, len) == 0) {
443
new = apr_pstrcat(pool, new, ",", apr_pstrndup(pool, list, i), NULL);
445
new = apr_pstrndup(pool, list, i);
453
* Converts 8 hex digits to a time integer
455
PROXY_DECLARE(int) ap_proxy_hex2sec(const char *x)
460
for (i = 0, j = 0; i < 8; i++) {
465
else if (apr_isupper(ch))
466
j |= ch - ('A' - 10);
468
j |= ch - ('a' - 10);
471
return -1; /* so that it works with 8-byte ints */
477
* Converts a time integer to 8 hex digits
479
PROXY_DECLARE(void) ap_proxy_sec2hex(int t, char *y)
484
for (i = 7; i >= 0; i--) {
488
y[i] = ch + ('A' - 10);
495
PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message)
497
apr_table_setn(r->notes, "error-notes",
499
"The proxy server could not handle the request "
500
"<em><a href=\"", ap_escape_uri(r->pool, r->uri),
501
"\">", ap_escape_html(r->pool, r->method),
503
ap_escape_html(r->pool, r->uri), "</a></em>.<p>\n"
505
ap_escape_html(r->pool, message),
506
"</strong></p>", NULL));
508
/* Allow "error-notes" string to be printed by ap_send_error_response() */
509
apr_table_setn(r->notes, "verbose-error-to", apr_pstrdup(r->pool, "*"));
511
r->status_line = apr_psprintf(r->pool, "%3.3u Proxy Error", statuscode);
512
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
513
"proxy: %s returned by %s", message, r->uri);
518
proxy_get_host_of_request(request_rec *r)
520
char *url, *user = NULL, *password = NULL, *err, *host;
523
if (r->hostname != NULL)
526
/* Set url to the first char after "scheme://" */
527
if ((url = strchr(r->uri, ':')) == NULL
528
|| url[1] != '/' || url[2] != '/')
531
url = apr_pstrdup(r->pool, &url[1]); /* make it point to "//", which is what proxy_canon_netloc expects */
533
err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port);
536
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
541
return host; /* ought to return the port, too */
544
/* Return TRUE if addr represents an IP address (or an IP network address) */
545
PROXY_DECLARE(int) ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p)
547
const char *addr = This->name;
553
* if the address is given with an explicit netmask, use that
554
* Due to a deficiency in apr_inet_addr(), it is impossible to parse
555
* "partial" addresses (with less than 4 quads) correctly, i.e.
556
* 192.168.123 is parsed as 192.168.0.123, which is not what I want.
557
* I therefore have to parse the IP address manually:
558
* if (proxy_readmask(This->name, &This->addr.s_addr, &This->mask.s_addr) == 0)
559
* addr and mask were set by proxy_readmask()
564
* Parse IP addr manually, optionally allowing
565
* abbreviated net addresses like 192.168.
568
/* Iterate over up to 4 (dotted) quads. */
569
for (quads = 0; quads < 4 && *addr != '\0'; ++quads) {
572
if (*addr == '/' && quads > 0) /* netmask starts here. */
575
if (!apr_isdigit(*addr))
576
return 0; /* no digit at start of quad */
578
ip_addr[quads] = strtol(addr, &tmp, 0);
580
if (tmp == addr) /* expected a digit, found something else */
583
if (ip_addr[quads] < 0 || ip_addr[quads] > 255) {
590
if (*addr == '.' && quads != 3)
591
++addr; /* after the 4th quad, a dot would be illegal */
594
for (This->addr.s_addr = 0, i = 0; i < quads; ++i)
595
This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
597
if (addr[0] == '/' && apr_isdigit(addr[1])) { /* net mask follows: */
602
bits = strtol(addr, &tmp, 0);
604
if (tmp == addr) /* expected a digit, found something else */
609
if (bits < 0 || bits > 32) /* netmask must be between 0 and 32 */
615
* Determine (i.e., "guess") netmask by counting the
616
* number of trailing .0's; reduce #quads appropriately
617
* (so that 192.168.0.0 is equivalent to 192.168.)
619
while (quads > 0 && ip_addr[quads - 1] == 0)
622
/* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */
626
/* every zero-byte counts as 8 zero-bits */
629
if (bits != 32) /* no warning for fully qualified IP address */
630
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
631
"Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld",
632
inet_ntoa(This->addr), bits);
635
This->mask.s_addr = htonl(APR_INADDR_NONE << (32 - bits));
637
if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) {
638
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
639
"Warning: NetMask and IP-Addr disagree in %s/%ld",
640
inet_ntoa(This->addr), bits);
641
This->addr.s_addr &= This->mask.s_addr;
642
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
644
inet_ntoa(This->addr), bits);
648
This->matcher = proxy_match_ipaddr;
652
return (*addr == '\0'); /* okay iff we've parsed the whole string */
655
/* Return TRUE if addr represents an IP address (or an IP network address) */
656
static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r)
659
struct in_addr addr, *ip;
660
const char *host = proxy_get_host_of_request(r);
662
if (host == NULL) /* oops! */
665
memset(&addr, '\0', sizeof addr);
666
memset(ip_addr, '\0', sizeof ip_addr);
668
if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) {
669
for (addr.s_addr = 0, i = 0; i < 4; ++i)
670
addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
672
if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) {
674
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
675
"1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr));
676
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
677
"%s/", inet_ntoa(This->addr));
678
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
679
"%s", inet_ntoa(This->mask));
685
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
686
"1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr));
687
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
688
"%s/", inet_ntoa(This->addr));
689
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
690
"%s", inet_ntoa(This->mask));
695
struct apr_sockaddr_t *reqaddr;
697
if (apr_sockaddr_info_get(&reqaddr, host, APR_UNSPEC, 0, 0, r->pool)
700
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
701
"2)IP-NoMatch: hostname=%s msg=Host not found",
707
/* Try to deal with multiple IP addr's for a host */
708
/* FIXME: This needs to be able to deal with IPv6 */
710
ip = (struct in_addr *) reqaddr->ipaddr_ptr;
711
if (This->addr.s_addr == (ip->s_addr & This->mask.s_addr)) {
713
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
714
"3)IP-Match: %s[%s] <-> ", host,
716
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
717
"%s/", inet_ntoa(This->addr));
718
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
719
"%s", inet_ntoa(This->mask));
725
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
726
"3)IP-NoMatch: %s[%s] <-> ", host,
728
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
729
"%s/", inet_ntoa(This->addr));
730
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
731
"%s", inet_ntoa(This->mask));
734
reqaddr = reqaddr->next;
741
/* Return TRUE if addr represents a domain name */
742
PROXY_DECLARE(int) ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p)
744
char *addr = This->name;
747
/* Domain name must start with a '.' */
751
/* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
752
for (i = 0; apr_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i)
756
if (addr[i] == ':') {
757
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
758
"@@@@ handle optional port in proxy_is_domainname()");
759
/* @@@@ handle optional port */
766
/* Strip trailing dots */
767
for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i)
770
This->matcher = proxy_match_domainname;
774
/* Return TRUE if host "host" is in domain "domain" */
775
static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r)
777
const char *host = proxy_get_host_of_request(r);
778
int d_len = strlen(This->name), h_len;
780
if (host == NULL) /* some error was logged already */
783
h_len = strlen(host);
785
/* @@@ do this within the setup? */
786
/* Ignore trailing dots in domain comparison: */
787
while (d_len > 0 && This->name[d_len - 1] == '.')
789
while (h_len > 0 && host[h_len - 1] == '.')
792
&& strncasecmp(&host[h_len - d_len], This->name, d_len) == 0;
795
/* Return TRUE if host represents a host name */
796
PROXY_DECLARE(int) ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p)
798
struct apr_sockaddr_t *addr;
799
char *host = This->name;
802
/* Host names must not start with a '.' */
806
/* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
807
for (i = 0; apr_isalnum(host[i]) || host[i] == '-' || host[i] == '.'; ++i);
809
if (host[i] != '\0' || apr_sockaddr_info_get(&addr, host, APR_UNSPEC, 0, 0, p) != APR_SUCCESS)
812
This->hostaddr = addr;
814
/* Strip trailing dots */
815
for (i = strlen(host) - 1; i > 0 && host[i] == '.'; --i)
818
This->matcher = proxy_match_hostname;
822
/* Return TRUE if host "host" is equal to host2 "host2" */
823
static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r)
825
char *host = This->name;
826
const char *host2 = proxy_get_host_of_request(r);
830
if (host == NULL || host2 == NULL)
831
return 0; /* oops! */
833
h2_len = strlen(host2);
834
h1_len = strlen(host);
837
struct apr_sockaddr_t *addr = *This->hostaddr;
839
/* Try to deal with multiple IP addr's for a host */
841
if (addr->ipaddr_ptr == ? ? ? ? ? ? ? ? ? ? ? ? ?)
847
/* Ignore trailing dots in host2 comparison: */
848
while (h2_len > 0 && host2[h2_len - 1] == '.')
850
while (h1_len > 0 && host[h1_len - 1] == '.')
852
return h1_len == h2_len
853
&& strncasecmp(host, host2, h1_len) == 0;
856
/* Return TRUE if addr is to be matched as a word */
857
PROXY_DECLARE(int) ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p)
859
This->matcher = proxy_match_word;
863
/* Return TRUE if string "str2" occurs literally in "str1" */
864
static int proxy_match_word(struct dirconn_entry *This, request_rec *r)
866
const char *host = proxy_get_host_of_request(r);
867
return host != NULL && ap_strstr_c(host, This->name) != NULL;
870
/* checks whether a host in uri_addr matches proxyblock */
871
PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf,
872
apr_sockaddr_t *uri_addr)
875
apr_sockaddr_t * src_uri_addr = uri_addr;
876
/* XXX FIXME: conf->noproxies->elts is part of an opaque structure */
877
for (j = 0; j < conf->noproxies->nelts; j++) {
878
struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts;
879
struct apr_sockaddr_t *conf_addr = npent[j].addr;
880
uri_addr = src_uri_addr;
881
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
882
"proxy: checking remote machine [%s] against [%s]", uri_addr->hostname, npent[j].name);
883
if ((npent[j].name && ap_strstr_c(uri_addr->hostname, npent[j].name))
884
|| npent[j].name[0] == '*') {
885
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
886
"proxy: connect to remote machine %s blocked: name %s matched", uri_addr->hostname, npent[j].name);
887
return HTTP_FORBIDDEN;
893
apr_sockaddr_ip_get(&conf_ip, conf_addr);
894
apr_sockaddr_ip_get(&uri_ip, uri_addr);
895
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
896
"proxy: ProxyBlock comparing %s and %s", conf_ip, uri_ip);
897
if (!apr_strnatcasecmp(conf_ip, uri_ip)) {
898
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
899
"proxy: connect to remote machine %s blocked: IP %s matched", uri_addr->hostname, conf_ip);
900
return HTTP_FORBIDDEN;
902
uri_addr = uri_addr->next;
904
conf_addr = conf_addr->next;
910
/* set up the minimal filter set */
911
PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r)
913
ap_add_input_filter("HTTP_IN", NULL, r, c);
918
* converts a series of buckets into a string
919
* XXX: BillS says this function performs essentially the same function as
920
* ap_rgetline() in protocol.c. Deprecate this function and use ap_rgetline()
921
* instead? I think ap_proxy_string_read() will not work properly on non ASCII
922
* (EBCDIC) machines either.
924
PROXY_DECLARE(apr_status_t) ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb,
925
char *buff, apr_size_t bufflen, int *eos)
934
/* start with an empty string */
938
/* loop through each brigade */
940
/* get brigade from network one line at a time */
941
if (APR_SUCCESS != (rv = ap_get_brigade(c->input_filters, bb,
947
/* loop through each bucket */
949
if (*eos || APR_BRIGADE_EMPTY(bb)) {
950
/* The connection aborted or timed out */
951
return APR_ECONNABORTED;
953
e = APR_BRIGADE_FIRST(bb);
954
if (APR_BUCKET_IS_EOS(e)) {
958
if (APR_SUCCESS != apr_bucket_read(e, (const char **)&response, &len, APR_BLOCK_READ)) {
962
* is string LF terminated?
963
* XXX: This check can be made more efficient by simply checking
964
* if the last character in the 'response' buffer is an ASCII_LF.
965
* See ap_rgetline() for an example.
967
if (memchr(response, APR_ASCII_LF, len)) {
970
/* concat strings until buff is full - then throw the data away */
971
if (len > ((bufflen-1)-(pos-buff))) {
972
len = (bufflen-1)-(pos-buff);
975
pos = apr_cpystrn(pos, response, len);
978
APR_BUCKET_REMOVE(e);
979
apr_bucket_destroy(e);
986
/* unmerge an element in the table */
987
PROXY_DECLARE(void) ap_proxy_table_unmerge(apr_pool_t *p, apr_table_t *t, char *key)
989
apr_off_t offset = 0;
993
/* get the value to unmerge */
994
const char *initial = apr_table_get(t, key);
998
value = apr_pstrdup(p, initial);
1000
/* remove the value from the headers */
1001
apr_table_unset(t, key);
1003
/* find each comma */
1004
while (value[count]) {
1005
if (value[count] == ',') {
1007
apr_table_add(t, key, value + offset);
1012
apr_table_add(t, key, value + offset);
1015
PROXY_DECLARE(const char *) ap_proxy_location_reverse_map(request_rec *r,
1016
proxy_dir_conf *conf, const char *url)
1018
struct proxy_alias *ent;
1023
* XXX FIXME: Make sure this handled the ambiguous case of the :<PORT>
1024
* after the hostname
1028
ent = (struct proxy_alias *)conf->raliases->elts;
1029
for (i = 0; i < conf->raliases->nelts; i++) {
1030
l2 = strlen(ent[i].real);
1031
if (l1 >= l2 && strncasecmp(ent[i].real, url, l2) == 0) {
1032
u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL);
1033
return ap_construct_url(r->pool, u, r);
1041
* Cookies are a bit trickier to match: we've got two substrings to worry
1042
* about, and we can't just find them with strstr 'cos of case. Regexp
1043
* matching would be an easy fix, but for better consistency with all the
1044
* other matches we'll refrain and use apr_strmatch to find path=/domain=
1045
* and stick to plain strings for the config values.
1047
PROXY_DECLARE(const char *) ap_proxy_cookie_reverse_map(request_rec *r,
1048
proxy_dir_conf *conf, const char *str)
1050
struct proxy_alias *ent;
1051
size_t len = strlen(str);
1052
const char *newpath = NULL;
1053
const char *newdomain = NULL;
1055
const char *domainp;
1056
const char *pathe = NULL;
1057
const char *domaine = NULL;
1058
size_t l1, l2, poffs = 0, doffs = 0;
1065
* Find the match and replacement, but save replacing until we've done
1066
* both path and domain so we know the new strlen
1068
if ((pathp = apr_strmatch(conf->cookie_path_str, str, len)) != NULL) {
1070
poffs = pathp - str;
1071
pathe = ap_strchr_c(pathp, ';');
1072
l1 = pathe ? (pathe - pathp) : strlen(pathp);
1073
pathe = pathp + l1 ;
1074
ent = (struct proxy_alias *)conf->cookie_paths->elts;
1075
for (i = 0; i < conf->cookie_paths->nelts; i++) {
1076
l2 = strlen(ent[i].fake);
1077
if (l1 >= l2 && strncmp(ent[i].fake, pathp, l2) == 0) {
1078
newpath = ent[i].real;
1079
pdiff = strlen(newpath) - l1;
1085
if ((domainp = apr_strmatch(conf->cookie_domain_str, str, len)) != NULL) {
1087
doffs = domainp - str;
1088
domaine = ap_strchr_c(domainp, ';');
1089
l1 = domaine ? (domaine - domainp) : strlen(domainp);
1090
domaine = domainp + l1;
1091
ent = (struct proxy_alias *)conf->cookie_domains->elts;
1092
for (i = 0; i < conf->cookie_domains->nelts; i++) {
1093
l2 = strlen(ent[i].fake);
1094
if (l1 >= l2 && strncasecmp(ent[i].fake, domainp, l2) == 0) {
1095
newdomain = ent[i].real;
1096
ddiff = strlen(newdomain) - l1;
1103
ret = apr_palloc(r->pool, len + pdiff + ddiff + 1);
1104
l1 = strlen(newpath);
1106
l2 = strlen(newdomain);
1107
if (doffs > poffs) {
1108
memcpy(ret, str, poffs);
1109
memcpy(ret + poffs, newpath, l1);
1110
memcpy(ret + poffs + l1, pathe, domainp - pathe);
1111
memcpy(ret + doffs + pdiff, newdomain, l2);
1112
strcpy(ret + doffs + pdiff + l2, domaine);
1115
memcpy(ret, str, doffs) ;
1116
memcpy(ret + doffs, newdomain, l2);
1117
memcpy(ret + doffs + l2, domaine, pathp - domaine);
1118
memcpy(ret + poffs + ddiff, newpath, l1);
1119
strcpy(ret + poffs + ddiff + l1, pathe);
1123
memcpy(ret, str, poffs);
1124
memcpy(ret + poffs, newpath, l1);
1125
strcpy(ret + poffs + l1, pathe);
1130
ret = apr_palloc(r->pool, len + pdiff + ddiff + 1);
1131
l2 = strlen(newdomain);
1132
memcpy(ret, str, doffs);
1133
memcpy(ret + doffs, newdomain, l2);
1134
strcpy(ret + doffs+l2, domaine);
1137
ret = (char *)str; /* no change */
1144
PROXY_DECLARE(proxy_balancer *) ap_proxy_get_balancer(apr_pool_t *p,
1145
proxy_server_conf *conf,
1148
proxy_balancer *balancer;
1149
char *c, *uri = apr_pstrdup(p, url);
1152
c = strchr(uri, ':');
1153
if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
1155
/* remove path from uri */
1156
if ((c = strchr(c + 3, '/')))
1158
balancer = (proxy_balancer *)conf->balancers->elts;
1159
for (i = 0; i < conf->balancers->nelts; i++) {
1160
if (strcasecmp(balancer->name, uri) == 0)
1167
PROXY_DECLARE(const char *) ap_proxy_add_balancer(proxy_balancer **balancer,
1169
proxy_server_conf *conf,
1172
char *c, *q, *uri = apr_pstrdup(p, url);
1173
proxy_balancer_method *lbmethod;
1175
c = strchr(uri, ':');
1176
if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
1177
return "Bad syntax for a balancer name";
1178
/* remove path from uri */
1179
if ((q = strchr(c + 3, '/')))
1182
ap_str_tolower(uri);
1183
*balancer = apr_array_push(conf->balancers);
1184
memset(*balancer, 0, sizeof(proxy_balancer));
1187
* NOTE: The default method is byrequests, which we assume
1190
lbmethod = ap_lookup_provider(PROXY_LBMETHOD, "byrequests", "0");
1192
return "Can't find 'byrequests' lb method";
1195
(*balancer)->name = uri;
1196
(*balancer)->lbmethod = lbmethod;
1197
(*balancer)->workers = apr_array_make(p, 5, sizeof(proxy_worker));
1198
/* XXX Is this a right place to create mutex */
1200
if (apr_thread_mutex_create(&((*balancer)->mutex),
1201
APR_THREAD_MUTEX_DEFAULT, p) != APR_SUCCESS) {
1202
/* XXX: Do we need to log something here */
1203
return "can not create thread mutex";
1210
PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p,
1211
proxy_server_conf *conf,
1214
proxy_worker *worker;
1215
proxy_worker *max_worker = NULL;
1218
int worker_name_length;
1223
c = ap_strchr_c(url, ':');
1224
if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
1227
url_copy = apr_pstrdup(p, url);
1228
url_length = strlen(url);
1231
* We need to find the start of the path and
1232
* therefore we know the length of the scheme://hostname/
1233
* part to we can force-lowercase everything up to
1234
* the start of the path.
1236
c = ap_strchr_c(c+3, '/');
1239
pathstart = url_copy + (c - url);
1241
ap_str_tolower(url_copy);
1244
ap_str_tolower(url_copy);
1247
worker = (proxy_worker *)conf->workers->elts;
1250
* Do a "longest match" on the worker name to find the worker that
1251
* fits best to the URL.
1253
for (i = 0; i < conf->workers->nelts; i++) {
1254
if ( ((worker_name_length = strlen(worker->name)) <= url_length)
1255
&& (worker_name_length > max_match)
1256
&& (strncmp(url_copy, worker->name, worker_name_length) == 0) ) {
1257
max_worker = worker;
1258
max_match = worker_name_length;
1266
static apr_status_t conn_pool_cleanup(void *theworker)
1268
proxy_worker *worker = (proxy_worker *)theworker;
1269
if (worker->cp->res) {
1270
worker->cp->pool = NULL;
1271
apr_reslist_destroy(worker->cp->res);
1277
static void init_conn_pool(apr_pool_t *p, proxy_worker *worker)
1280
proxy_conn_pool *cp;
1283
* Create a connection pool's subpool.
1284
* This pool is used for connection recycling.
1285
* Once the worker is added it is never removed but
1286
* it can be disabled.
1288
apr_pool_create(&pool, p);
1290
* Alloc from the same pool as worker.
1291
* proxy_conn_pool is permanently attached to the worker.
1293
cp = (proxy_conn_pool *)apr_pcalloc(p, sizeof(proxy_conn_pool));
1298
PROXY_DECLARE(const char *) ap_proxy_add_worker(proxy_worker **worker,
1300
proxy_server_conf *conf,
1306
rv = apr_uri_parse(p, url, &uri);
1308
if (rv != APR_SUCCESS) {
1309
return "Unable to parse URL";
1311
if (!uri.hostname || !uri.scheme) {
1312
return "URL must be absolute!";
1315
ap_str_tolower(uri.hostname);
1316
ap_str_tolower(uri.scheme);
1317
*worker = apr_array_push(conf->workers);
1318
memset(*worker, 0, sizeof(proxy_worker));
1319
(*worker)->name = apr_uri_unparse(p, &uri, APR_URI_UNP_REVEALPASSWORD);
1320
(*worker)->scheme = uri.scheme;
1321
(*worker)->hostname = uri.hostname;
1322
(*worker)->port = uri.port;
1323
(*worker)->id = proxy_lb_workers;
1324
(*worker)->flush_packets = flush_off;
1325
(*worker)->flush_wait = PROXY_FLUSH_WAIT;
1326
/* Increase the total worker count */
1328
init_conn_pool(p, *worker);
1330
if (apr_thread_mutex_create(&((*worker)->mutex),
1331
APR_THREAD_MUTEX_DEFAULT, p) != APR_SUCCESS) {
1332
/* XXX: Do we need to log something here */
1333
return "can not create thread mutex";
1340
PROXY_DECLARE(proxy_worker *) ap_proxy_create_worker(apr_pool_t *p)
1343
proxy_worker *worker;
1344
worker = (proxy_worker *)apr_pcalloc(p, sizeof(proxy_worker));
1345
worker->id = proxy_lb_workers;
1346
/* Increase the total worker count */
1348
init_conn_pool(p, worker);
1354
ap_proxy_add_worker_to_balancer(apr_pool_t *pool, proxy_balancer *balancer,
1355
proxy_worker *worker)
1357
proxy_worker *runtime;
1359
runtime = apr_array_push(balancer->workers);
1360
memcpy(runtime, worker, sizeof(proxy_worker));
1361
runtime->id = proxy_lb_workers;
1362
/* Increase the total runtime count */
1367
PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker,
1368
proxy_balancer **balancer,
1370
proxy_server_conf *conf, char **url)
1374
access_status = proxy_run_pre_request(worker, balancer, r, conf, url);
1375
if (access_status == DECLINED && *balancer == NULL) {
1376
*worker = ap_proxy_get_worker(r->pool, conf, *url);
1378
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1379
"proxy: %s: found worker %s for %s",
1380
(*worker)->scheme, (*worker)->name, *url);
1385
else if (r->proxyreq == PROXYREQ_PROXY) {
1386
if (conf->forward) {
1387
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1388
"proxy: *: found forward proxy worker for %s",
1391
*worker = conf->forward;
1395
else if (r->proxyreq == PROXYREQ_REVERSE) {
1396
if (conf->reverse) {
1397
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1398
"proxy: *: found reverse proxy worker for %s",
1401
*worker = conf->reverse;
1406
else if (access_status == DECLINED && *balancer != NULL) {
1407
/* All the workers are busy */
1408
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1409
"proxy: all workers are busy. Unable to serve %s",
1411
access_status = HTTP_SERVICE_UNAVAILABLE;
1413
return access_status;
1416
PROXY_DECLARE(int) ap_proxy_post_request(proxy_worker *worker,
1417
proxy_balancer *balancer,
1419
proxy_server_conf *conf)
1423
access_status = proxy_run_post_request(worker, balancer, r, conf);
1430
return access_status;
1434
PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **newsock,
1435
const char *proxy_function,
1436
apr_sockaddr_t *backend_addr,
1437
const char *backend_name,
1438
proxy_server_conf *conf,
1446
while (backend_addr && !connected) {
1447
if ((rv = apr_socket_create(newsock, backend_addr->family,
1448
SOCK_STREAM, 0, p)) != APR_SUCCESS) {
1449
loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
1450
ap_log_error(APLOG_MARK, loglevel, rv, s,
1451
"proxy: %s: error creating fam %d socket for target %s",
1453
backend_addr->family,
1456
* this could be an IPv6 address from the DNS but the
1457
* local machine won't give us an IPv6 socket; hopefully the
1458
* DNS returned an additional address to try
1460
backend_addr = backend_addr->next;
1464
#if !defined(TPF) && !defined(BEOS)
1465
if (conf->recv_buffer_size > 0 &&
1466
(rv = apr_socket_opt_set(*newsock, APR_SO_RCVBUF,
1467
conf->recv_buffer_size))) {
1468
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1469
"apr_socket_opt_set(SO_RCVBUF): Failed to set "
1470
"ProxyReceiveBufferSize, using default");
1474
/* Set a timeout on the socket */
1475
if (conf->timeout_set == 1) {
1476
apr_socket_timeout_set(*newsock, conf->timeout);
1479
apr_socket_timeout_set(*newsock, s->timeout);
1482
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1483
"proxy: %s: fam %d socket created to connect to %s",
1484
proxy_function, backend_addr->family, backend_name);
1486
/* make the connection out of the socket */
1487
rv = apr_socket_connect(*newsock, backend_addr);
1489
/* if an error occurred, loop round and try again */
1490
if (rv != APR_SUCCESS) {
1491
apr_socket_close(*newsock);
1492
loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
1493
ap_log_error(APLOG_MARK, loglevel, rv, s,
1494
"proxy: %s: attempt to connect to %pI (%s) failed",
1498
backend_addr = backend_addr->next;
1503
return connected ? 0 : 1;
1506
static apr_status_t connection_cleanup(void *theconn)
1508
proxy_conn_rec *conn = (proxy_conn_rec *)theconn;
1509
proxy_worker *worker = conn->worker;
1512
* If the connection pool is NULL the worker
1513
* cleanup has been run. Just return.
1519
/* Sanity check: Did we already return the pooled connection? */
1520
if (conn->inreslist) {
1521
ap_log_perror(APLOG_MARK, APLOG_ERR, 0, conn->pool,
1522
"proxy: Pooled connection 0x%pp for worker %s has been"
1523
" already returned to the connection pool.", conn,
1529
/* determine if the connection need to be closed */
1530
if (conn->close_on_recycle || conn->close) {
1531
apr_pool_t *p = conn->pool;
1532
apr_pool_clear(conn->pool);
1533
memset(conn, 0, sizeof(proxy_conn_rec));
1535
conn->worker = worker;
1538
if (worker->hmax && worker->cp->res) {
1539
conn->inreslist = 1;
1540
apr_reslist_release(worker->cp->res, (void *)conn);
1545
worker->cp->conn = conn;
1548
/* Allways return the SUCCESS */
1552
/* reslist constructor */
1553
static apr_status_t connection_constructor(void **resource, void *params,
1557
proxy_conn_rec *conn;
1558
proxy_worker *worker = (proxy_worker *)params;
1561
* Create the subpool for each connection
1562
* This keeps the memory consumption constant
1563
* when disconnecting from backend.
1565
apr_pool_create(&ctx, pool);
1566
conn = apr_pcalloc(pool, sizeof(proxy_conn_rec));
1569
conn->worker = worker;
1571
conn->inreslist = 1;
1578
#if APR_HAS_THREADS /* only needed when threads are used */
1579
/* reslist destructor */
1580
static apr_status_t connection_destructor(void *resource, void *params,
1583
proxy_conn_rec *conn = (proxy_conn_rec *)resource;
1585
/* Destroy the pool only if not called from reslist_destroy */
1586
if (conn->worker->cp->pool)
1587
apr_pool_destroy(conn->pool);
1594
* ap_proxy_initialize_worker_share() concerns itself
1595
* with initializing those parts of worker which
1596
* are, or could be, shared. Basically worker->s
1598
PROXY_DECLARE(void) ap_proxy_initialize_worker_share(proxy_server_conf *conf,
1599
proxy_worker *worker,
1602
#if PROXY_HAS_SCOREBOARD
1603
lb_score *score = NULL;
1608
if (worker->s && (worker->s->status & PROXY_WORKER_INITIALIZED)) {
1609
/* The worker share is already initialized */
1610
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1611
"proxy: worker %s already initialized",
1615
#if PROXY_HAS_SCOREBOARD
1616
/* Get scoreboard slot */
1617
if (ap_scoreboard_image) {
1618
score = ap_get_scoreboard_lb(worker->id);
1620
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1621
"proxy: ap_get_scoreboard_lb(%d) failed in child %" APR_PID_T_FMT " for worker %s",
1622
worker->id, getpid(), worker->name);
1625
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1626
"proxy: grabbed scoreboard slot %d in child %" APR_PID_T_FMT " for worker %s",
1627
worker->id, getpid(), worker->name);
1632
score = apr_pcalloc(conf->pool, sizeof(proxy_worker_stat));
1633
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1634
"proxy: initialized plain memory in child %" APR_PID_T_FMT " for worker %s",
1635
getpid(), worker->name);
1637
worker->s = (proxy_worker_stat *)score;
1639
* recheck to see if we've already been here. Possible
1640
* if proxy is using scoreboard to hold shared stats
1642
if (worker->s->status & PROXY_WORKER_INITIALIZED) {
1643
/* The worker share is already initialized */
1644
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1645
"proxy: worker %s already initialized",
1650
strcpy(worker->s->route, worker->route);
1652
*worker->s->route = '\0';
1653
if (worker->redirect)
1654
strcpy(worker->s->redirect, worker->redirect);
1656
*worker->s->redirect = '\0';
1658
worker->s->status |= (worker->status | PROXY_WORKER_INITIALIZED);
1662
PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, server_rec *s)
1670
if (worker->status & PROXY_WORKER_INITIALIZED) {
1671
/* The worker is already initialized */
1675
/* Set default parameters */
1677
worker->retry = apr_time_from_sec(PROXY_WORKER_DEFAULT_RETRY);
1678
/* By default address is reusable */
1679
worker->is_address_reusable = 1;
1682
ap_mpm_query(AP_MPMQ_MAX_THREADS, &mpm_threads);
1683
if (mpm_threads > 1) {
1684
/* Set hard max to no more then mpm_threads */
1685
if (worker->hmax == 0 || worker->hmax > mpm_threads)
1686
worker->hmax = mpm_threads;
1687
if (worker->smax == 0 || worker->smax > worker->hmax)
1688
worker->smax = worker->hmax;
1689
/* Set min to be lower then smax */
1690
if (worker->min > worker->smax)
1691
worker->min = worker->smax;
1694
/* This will supress the apr_reslist creation */
1695
worker->min = worker->smax = worker->hmax = 0;
1698
rv = apr_reslist_create(&(worker->cp->res),
1699
worker->min, worker->smax,
1700
worker->hmax, worker->ttl,
1701
connection_constructor, connection_destructor,
1702
worker, worker->cp->pool);
1704
apr_pool_cleanup_register(worker->cp->pool, (void *)worker,
1706
apr_pool_cleanup_null);
1708
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1709
"proxy: initialized worker %d in child %" APR_PID_T_FMT " for (%s) min=%d max=%d smax=%d",
1710
worker->id, getpid(), worker->hostname, worker->min,
1711
worker->hmax, worker->smax);
1713
#if (APR_MAJOR_VERSION > 0)
1714
/* Set the acquire timeout */
1715
if (rv == APR_SUCCESS && worker->acquire_set)
1716
apr_reslist_timeout_set(worker->cp->res, worker->acquire);
1723
rv = connection_constructor((void **)&(worker->cp->conn), worker, worker->cp->pool);
1724
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1725
"proxy: initialized single connection worker %d in child %" APR_PID_T_FMT " for (%s)",
1726
worker->id, getpid(), worker->hostname);
1728
if (rv == APR_SUCCESS) {
1729
worker->status |= (PROXY_WORKER_INITIALIZED);
1734
PROXY_DECLARE(int) ap_proxy_retry_worker(const char *proxy_function,
1735
proxy_worker *worker,
1738
if (worker->s->status & PROXY_WORKER_IN_ERROR) {
1739
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1740
"proxy: %s: retrying the worker for (%s)",
1741
proxy_function, worker->hostname);
1742
if (apr_time_now() > worker->s->error_time + worker->retry) {
1743
++worker->s->retries;
1744
worker->s->status &= ~PROXY_WORKER_IN_ERROR;
1745
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1746
"proxy: %s: worker for (%s) has been marked for retry",
1747
proxy_function, worker->hostname);
1757
PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function,
1758
proxy_conn_rec **conn,
1759
proxy_worker *worker,
1764
if (!PROXY_WORKER_IS_USABLE(worker)) {
1765
/* Retry the worker */
1766
ap_proxy_retry_worker(proxy_function, worker, s);
1768
if (!PROXY_WORKER_IS_USABLE(worker)) {
1769
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1770
"proxy: %s: disabled connection for (%s)",
1771
proxy_function, worker->hostname);
1772
return HTTP_SERVICE_UNAVAILABLE;
1776
if (worker->hmax && worker->cp->res) {
1777
rv = apr_reslist_acquire(worker->cp->res, (void **)conn);
1782
/* create the new connection if the previous was destroyed */
1783
if (!worker->cp->conn)
1784
connection_constructor((void **)conn, worker, worker->cp->pool);
1786
*conn = worker->cp->conn;
1787
worker->cp->conn = NULL;
1792
if (rv != APR_SUCCESS) {
1793
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1794
"proxy: %s: failed to acquire connection for (%s)",
1795
proxy_function, worker->hostname);
1796
return HTTP_SERVICE_UNAVAILABLE;
1798
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1799
"proxy: %s: has acquired connection for (%s)",
1800
proxy_function, worker->hostname);
1802
(*conn)->worker = worker;
1804
(*conn)->close_on_recycle = 0;
1806
(*conn)->inreslist = 0;
1812
PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function,
1813
proxy_conn_rec *conn,
1816
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1817
"proxy: %s: has released connection for (%s)",
1818
proxy_function, conn->worker->hostname);
1819
/* If there is a connection kill it's cleanup */
1820
if (conn->connection) {
1821
apr_pool_cleanup_kill(conn->connection->pool, conn, connection_cleanup);
1822
conn->connection = NULL;
1824
connection_cleanup(conn);
1830
ap_proxy_determine_connection(apr_pool_t *p, request_rec *r,
1831
proxy_server_conf *conf,
1832
proxy_worker *worker,
1833
proxy_conn_rec *conn,
1836
const char *proxyname,
1837
apr_port_t proxyport,
1838
char *server_portstr,
1839
int server_portstr_size)
1842
apr_status_t err = APR_SUCCESS;
1845
* Break up the URL to determine the host to connect to
1848
/* we break the URL into host, port, uri */
1849
if (APR_SUCCESS != apr_uri_parse(p, *url, uri)) {
1850
return ap_proxyerror(r, HTTP_BAD_REQUEST,
1851
apr_pstrcat(p,"URI cannot be parsed: ", *url,
1855
uri->port = apr_uri_port_of_scheme(uri->scheme);
1858
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1859
"proxy: connecting %s to %s:%d", *url, uri->hostname,
1863
* allocate these out of the specified connection pool
1864
* The scheme handler decides if this is permanent or
1865
* short living pool.
1867
/* are we connecting directly, or via a proxy? */
1869
*url = apr_pstrcat(p, uri->path, uri->query ? "?" : "",
1870
uri->query ? uri->query : "",
1871
uri->fragment ? "#" : "",
1872
uri->fragment ? uri->fragment : "", NULL);
1875
* Make sure that we pick the the correct and valid worker.
1876
* If a single keepalive connection triggers different workers,
1877
* then we have a problem (we don't select the correct one).
1878
* Do an expensive check in this case, where we compare the
1879
* the hostnames associated between the two.
1881
* TODO: Handle this much better...
1883
if (!conn->hostname || !worker->is_address_reusable ||
1884
(r->connection->keepalives &&
1885
(r->proxyreq == PROXYREQ_PROXY || r->proxyreq == PROXYREQ_REVERSE) &&
1886
(strcasecmp(conn->hostname, uri->hostname) != 0) ) ) {
1888
conn->hostname = apr_pstrdup(conn->pool, proxyname);
1889
conn->port = proxyport;
1891
conn->hostname = apr_pstrdup(conn->pool, uri->hostname);
1892
conn->port = uri->port;
1895
apr_socket_close(conn->sock);
1898
if (conn->connection) {
1899
apr_pool_cleanup_kill(conn->connection->pool, conn, connection_cleanup);
1900
conn->connection = NULL;
1902
err = apr_sockaddr_info_get(&(conn->addr),
1903
conn->hostname, APR_UNSPEC,
1907
else if (!worker->cp->addr) {
1908
if ((err = PROXY_THREAD_LOCK(worker)) != APR_SUCCESS) {
1909
ap_log_error(APLOG_MARK, APLOG_ERR, err, r->server,
1911
return HTTP_INTERNAL_SERVER_ERROR;
1915
* Worker can have the single constant backend adress.
1916
* The single DNS lookup is used once per worker.
1917
* If dynamic change is needed then set the addr to NULL
1918
* inside dynamic config to force the lookup.
1920
err = apr_sockaddr_info_get(&(worker->cp->addr),
1921
conn->hostname, APR_UNSPEC,
1924
conn->addr = worker->cp->addr;
1925
PROXY_THREAD_UNLOCK(worker);
1928
conn->addr = worker->cp->addr;
1930
if (err != APR_SUCCESS) {
1931
return ap_proxyerror(r, HTTP_BAD_GATEWAY,
1932
apr_pstrcat(p, "DNS lookup failure for: ",
1933
conn->hostname, NULL));
1936
/* Get the server port for the Via headers */
1938
server_port = ap_get_server_port(r);
1939
if (ap_is_default_port(server_port, r)) {
1940
strcpy(server_portstr,"");
1942
apr_snprintf(server_portstr, server_portstr_size, ":%d",
1946
/* check if ProxyBlock directive on this host */
1947
if (OK != ap_proxy_checkproxyblock(r, conf, conn->addr)) {
1948
return ap_proxyerror(r, HTTP_FORBIDDEN,
1949
"Connect to remote machine blocked");
1951
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1952
"proxy: connected %s to %s:%d", *url, conn->hostname,
1957
static int is_socket_connected(apr_socket_t *sock)
1960
apr_size_t buffer_len = 1;
1961
char test_buffer[1];
1962
apr_status_t socket_status;
1963
apr_interval_time_t current_timeout;
1966
apr_socket_timeout_get(sock, ¤t_timeout);
1967
/* set no timeout */
1968
apr_socket_timeout_set(sock, 0);
1969
socket_status = apr_socket_recv(sock, test_buffer, &buffer_len);
1970
/* put back old timeout */
1971
apr_socket_timeout_set(sock, current_timeout);
1972
if (APR_STATUS_IS_EOF(socket_status))
1978
PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
1979
proxy_conn_rec *conn,
1980
proxy_worker *worker,
1986
apr_sockaddr_t *backend_addr = conn->addr;
1987
apr_socket_t *newsock;
1991
* This increases the connection pool size
1992
* but the number of dropped connections is
1993
* relatively small compared to connection lifetime
1995
if (!(connected = is_socket_connected(conn->sock))) {
1996
apr_socket_close(conn->sock);
2000
while (backend_addr && !connected) {
2001
if ((rv = apr_socket_create(&newsock, backend_addr->family,
2002
SOCK_STREAM, APR_PROTO_TCP,
2003
conn->pool)) != APR_SUCCESS) {
2004
loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
2005
ap_log_error(APLOG_MARK, loglevel, rv, s,
2006
"proxy: %s: error creating fam %d socket for target %s",
2008
backend_addr->family,
2011
* this could be an IPv6 address from the DNS but the
2012
* local machine won't give us an IPv6 socket; hopefully the
2013
* DNS returned an additional address to try
2015
backend_addr = backend_addr->next;
2019
#if !defined(TPF) && !defined(BEOS)
2020
if (worker->recv_buffer_size > 0 &&
2021
(rv = apr_socket_opt_set(newsock, APR_SO_RCVBUF,
2022
worker->recv_buffer_size))) {
2023
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
2024
"apr_socket_opt_set(SO_RCVBUF): Failed to set "
2025
"ProxyReceiveBufferSize, using default");
2029
/* Set a timeout on the socket */
2030
if (worker->timeout_set == 1) {
2031
apr_socket_timeout_set(newsock, worker->timeout);
2034
apr_socket_timeout_set(newsock, s->timeout);
2036
/* Set a keepalive option */
2037
if (worker->keepalive) {
2038
if ((rv = apr_socket_opt_set(newsock,
2039
APR_SO_KEEPALIVE, 1)) != APR_SUCCESS) {
2040
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
2041
"apr_socket_opt_set(SO_KEEPALIVE): Failed to set"
2045
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
2046
"proxy: %s: fam %d socket created to connect to %s",
2047
proxy_function, backend_addr->family, worker->hostname);
2049
/* make the connection out of the socket */
2050
rv = apr_socket_connect(newsock, backend_addr);
2052
/* if an error occurred, loop round and try again */
2053
if (rv != APR_SUCCESS) {
2054
apr_socket_close(newsock);
2055
loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
2056
ap_log_error(APLOG_MARK, loglevel, rv, s,
2057
"proxy: %s: attempt to connect to %pI (%s) failed",
2061
backend_addr = backend_addr->next;
2065
conn->sock = newsock;
2069
* Put the entire worker to error state if
2070
* the PROXY_WORKER_IGNORE_ERRORS flag is not set.
2071
* Altrough some connections may be alive
2072
* no further connections to the worker could be made
2074
if (!connected && PROXY_WORKER_IS_USABLE(worker) &&
2075
!(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) {
2076
worker->s->status |= PROXY_WORKER_IN_ERROR;
2077
worker->s->error_time = apr_time_now();
2078
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
2079
"ap_proxy_connect_backend disabling worker for (%s)",
2083
worker->s->error_time = 0;
2084
worker->s->retries = 0;
2086
return connected ? OK : DECLINED;
2089
PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function,
2090
proxy_conn_rec *conn,
2094
apr_sockaddr_t *backend_addr = conn->addr;
2098
* The socket is now open, create a new backend server connection
2100
conn->connection = ap_run_create_connection(c->pool, s, conn->sock,
2104
if (!conn->connection) {
2106
* the peer reset the connection already; ap_run_create_connection()
2109
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
2110
s, "proxy: %s: an error occurred creating a "
2111
"new connection to %pI (%s)", proxy_function,
2112
backend_addr, conn->hostname);
2113
/* XXX: Will be closed when proxy_conn is closed */
2114
apr_socket_close(conn->sock);
2116
return HTTP_INTERNAL_SERVER_ERROR;
2119
* register the connection cleanup to client connection
2120
* so that the connection can be closed or reused
2122
apr_pool_cleanup_register(c->pool, (void *)conn,
2124
apr_pool_cleanup_null);
2126
/* For ssl connection to backend */
2128
if (!ap_proxy_ssl_enable(conn->connection)) {
2129
ap_log_error(APLOG_MARK, APLOG_ERR, 0,
2130
s, "proxy: %s: failed to enable ssl support "
2131
"for %pI (%s)", proxy_function,
2132
backend_addr, conn->hostname);
2133
return HTTP_INTERNAL_SERVER_ERROR;
2137
/* TODO: See if this will break FTP */
2138
ap_proxy_ssl_disable(conn->connection);
2141
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
2142
"proxy: %s: connection complete to %pI (%s)",
2143
proxy_function, backend_addr, conn->hostname);
2145
/* set up the connection filters */
2146
rc = ap_run_pre_connection(conn->connection, conn->sock);
2147
if (rc != OK && rc != DONE) {
2148
conn->connection->aborted = 1;
2149
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
2150
"proxy: %s: pre_connection setup failed (%d)",
2151
proxy_function, rc);
2158
int ap_proxy_lb_workers(void)
2161
* Since we can't resize the scoreboard when reconfiguring, we
2162
* have to impose a limit on the number of workers, we are
2163
* able to reconfigure to.
2165
if (!lb_workers_limit)
2166
lb_workers_limit = proxy_lb_workers + PROXY_DYNAMIC_BALANCER_LIMIT;
2167
return lb_workers_limit;
2170
PROXY_DECLARE(void) ap_proxy_backend_broke(request_rec *r,
2171
apr_bucket_brigade *brigade)
2174
conn_rec *c = r->connection;
2178
* If this is a subrequest, then prevent also caching of the main
2182
r->main->no_cache = 1;
2183
e = ap_bucket_error_create(HTTP_BAD_GATEWAY, NULL, c->pool,
2185
APR_BRIGADE_INSERT_TAIL(brigade, e);
2186
e = apr_bucket_eos_create(c->bucket_alloc);
2187
APR_BRIGADE_INSERT_TAIL(brigade, e);