]> granicus.if.org Git - apache/blob - modules/proxy/proxy_util.c
5f98db4f2a079ce387011c3ccd6ad0de8b672503
[apache] / modules / proxy / proxy_util.c
1 /* Copyright 1999-2004 The Apache Software Foundation
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 /* Utility routines for Apache proxy */
17 #include "mod_proxy.h"
18 #include "ap_mpm.h"
19 #include "scoreboard.h"
20 #include "apr_version.h"
21
22 #if APR_HAVE_UNISTD_H
23 #include <unistd.h>         /* for getpid() */
24 #endif
25
26 #if (APR_MAJOR_VERSION < 1)
27 #undef apr_socket_create
28 #define apr_socket_create apr_socket_create_ex
29 #endif
30
31 /* Global balancer counter */
32 int PROXY_DECLARE_DATA proxy_lb_workers = 0;
33 static int lb_workers_limit = 0;
34
35 static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r);
36 static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r);
37 static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r);
38 static int proxy_match_word(struct dirconn_entry *This, request_rec *r);
39
40 APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, create_req, 
41                                    (request_rec *r, request_rec *pr), (r, pr),
42                                    OK, DECLINED)
43
44 /* already called in the knowledge that the characters are hex digits */
45 PROXY_DECLARE(int) ap_proxy_hex2c(const char *x)
46 {
47     int i, ch;
48
49 #if !APR_CHARSET_EBCDIC
50     ch = x[0];
51     if (apr_isdigit(ch))
52     i = ch - '0';
53     else if (apr_isupper(ch))
54     i = ch - ('A' - 10);
55     else
56     i = ch - ('a' - 10);
57     i <<= 4;
58
59     ch = x[1];
60     if (apr_isdigit(ch))
61     i += ch - '0';
62     else if (apr_isupper(ch))
63     i += ch - ('A' - 10);
64     else
65     i += ch - ('a' - 10);
66     return i;
67 #else /*APR_CHARSET_EBCDIC*/
68     /* we assume that the hex value refers to an ASCII character
69      * so convert to EBCDIC so that it makes sense locally;
70      *
71      * example:
72      *
73      * client specifies %20 in URL to refer to a space char;
74      * at this point we're called with EBCDIC "20"; after turning
75      * EBCDIC "20" into binary 0x20, we then need to assume that 0x20
76      * represents an ASCII char and convert 0x20 to EBCDIC, yielding
77      * 0x40
78      */
79     char buf[1];
80
81     if (1 == sscanf(x, "%2x", &i)) {
82         buf[0] = i & 0xFF;
83         ap_xlate_proto_from_ascii(buf, 1);
84         return buf[0];
85     }
86     else {
87         return 0;
88     }
89 #endif /*APR_CHARSET_EBCDIC*/
90 }
91
92 PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x)
93 {
94 #if !APR_CHARSET_EBCDIC
95     int i;
96
97     x[0] = '%';
98     i = (ch & 0xF0) >> 4;
99     if (i >= 10)
100     x[1] = ('A' - 10) + i;
101     else
102     x[1] = '0' + i;
103
104     i = ch & 0x0F;
105     if (i >= 10)
106     x[2] = ('A' - 10) + i;
107     else
108     x[2] = '0' + i;
109 #else /*APR_CHARSET_EBCDIC*/
110     static const char ntoa[] = { "0123456789ABCDEF" };
111     char buf[1];
112
113     ch &= 0xFF;
114
115     buf[0] = ch;
116     ap_xlate_proto_to_ascii(buf, 1);
117
118     x[0] = '%';
119     x[1] = ntoa[(buf[0] >> 4) & 0x0F];
120     x[2] = ntoa[buf[0] & 0x0F];
121     x[3] = '\0';
122 #endif /*APR_CHARSET_EBCDIC*/
123 }
124
125 /*
126  * canonicalise a URL-encoded string
127  */
128
129 /*
130  * Convert a URL-encoded string to canonical form.
131  * It decodes characters which need not be encoded,
132  * and encodes those which must be encoded, and does not touch
133  * those which must not be touched.
134  */
135 PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, enum enctype t,
136     int isenc)
137 {
138     int i, j, ch;
139     char *y;
140     char *allowed;  /* characters which should not be encoded */
141     char *reserved; /* characters which much not be en/de-coded */
142
143 /* N.B. in addition to :@&=, this allows ';' in an http path
144  * and '?' in an ftp path -- this may be revised
145  * 
146  * Also, it makes a '+' character in a search string reserved, as
147  * it may be form-encoded. (Although RFC 1738 doesn't allow this -
148  * it only permits ; / ? : @ = & as reserved chars.)
149  */
150     if (t == enc_path)
151     allowed = "$-_.+!*'(),;:@&=";
152     else if (t == enc_search)
153     allowed = "$-_.!*'(),;:@&=";
154     else if (t == enc_user)
155     allowed = "$-_.+!*'(),;@&=";
156     else if (t == enc_fpath)
157     allowed = "$-_.+!*'(),?:@&=";
158     else            /* if (t == enc_parm) */
159     allowed = "$-_.+!*'(),?/:@&=";
160
161     if (t == enc_path)
162     reserved = "/";
163     else if (t == enc_search)
164     reserved = "+";
165     else
166     reserved = "";
167
168     y = apr_palloc(p, 3 * len + 1);
169
170     for (i = 0, j = 0; i < len; i++, j++) {
171 /* always handle '/' first */
172     ch = x[i];
173     if (strchr(reserved, ch)) {
174         y[j] = ch;
175         continue;
176     }
177 /* decode it if not already done */
178     if (isenc && ch == '%') {
179         if (!apr_isxdigit(x[i + 1]) || !apr_isxdigit(x[i + 2]))
180         return NULL;
181         ch = ap_proxy_hex2c(&x[i + 1]);
182         i += 2;
183         if (ch != 0 && strchr(reserved, ch)) {  /* keep it encoded */
184         ap_proxy_c2hex(ch, &y[j]);
185         j += 2;
186         continue;
187         }
188     }
189 /* recode it, if necessary */
190     if (!apr_isalnum(ch) && !strchr(allowed, ch)) {
191         ap_proxy_c2hex(ch, &y[j]);
192         j += 2;
193     }
194     else
195         y[j] = ch;
196     }
197     y[j] = '\0';
198     return y;
199 }
200
201 /*
202  * Parses network-location.
203  *    urlp           on input the URL; on output the path, after the leading /
204  *    user           NULL if no user/password permitted
205  *    password       holder for password
206  *    host           holder for host
207  *    port           port number; only set if one is supplied.
208  *
209  * Returns an error string.
210  */
211 PROXY_DECLARE(char *)
212      ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp,
213             char **passwordp, char **hostp, apr_port_t *port)
214 {
215     char *addr, *scope_id, *strp, *host, *url = *urlp;
216     char *user = NULL, *password = NULL;
217     apr_port_t tmp_port;
218     apr_status_t rv;
219
220     if (url[0] != '/' || url[1] != '/')
221     return "Malformed URL";
222     host = url + 2;
223     url = strchr(host, '/');
224     if (url == NULL)
225     url = "";
226     else
227     *(url++) = '\0';    /* skip seperating '/' */
228
229     /* find _last_ '@' since it might occur in user/password part */
230     strp = strrchr(host, '@');
231
232     if (strp != NULL) {
233     *strp = '\0';
234     user = host;
235     host = strp + 1;
236
237 /* find password */
238     strp = strchr(user, ':');
239     if (strp != NULL) {
240         *strp = '\0';
241         password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, 1);
242         if (password == NULL)
243         return "Bad %-escape in URL (password)";
244     }
245
246     user = ap_proxy_canonenc(p, user, strlen(user), enc_user, 1);
247     if (user == NULL)
248         return "Bad %-escape in URL (username)";
249     }
250     if (userp != NULL) {
251     *userp = user;
252     }
253     if (passwordp != NULL) {
254     *passwordp = password;
255     }
256
257     /* Parse the host string to separate host portion from optional port.
258      * Perform range checking on port.
259      */
260     rv = apr_parse_addr_port(&addr, &scope_id, &tmp_port, host, p);
261     if (rv != APR_SUCCESS || addr == NULL || scope_id != NULL) {
262         return "Invalid host/port";
263     }
264     if (tmp_port != 0) { /* only update caller's port if port was specified */
265         *port = tmp_port;
266     }
267
268     ap_str_tolower(addr); /* DNS names are case-insensitive */
269
270     *urlp = url;
271     *hostp = addr;
272
273     return NULL;
274 }
275
276 static const char * const lwday[7] =
277 {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
278
279 /*
280  * If the date is a valid RFC 850 date or asctime() date, then it
281  * is converted to the RFC 1123 format, otherwise it is not modified.
282  * This routine is not very fast at doing conversions, as it uses
283  * sscanf and sprintf. However, if the date is already correctly
284  * formatted, then it exits very quickly.
285  */
286 PROXY_DECLARE(const char *)
287      ap_proxy_date_canon(apr_pool_t *p, const char *x1)
288 {
289     char *x = apr_pstrdup(p, x1);
290     int wk, mday, year, hour, min, sec, mon;
291     char *q, month[4], zone[4], week[4];
292
293     q = strchr(x, ',');
294     /* check for RFC 850 date */
295     if (q != NULL && q - x > 3 && q[1] == ' ') {
296     *q = '\0';
297     for (wk = 0; wk < 7; wk++)
298         if (strcmp(x, lwday[wk]) == 0)
299         break;
300     *q = ',';
301     if (wk == 7)
302         return x;       /* not a valid date */
303     if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' ||
304         q[17] != ':' || strcmp(&q[20], " GMT") != 0)
305         return x;
306     if (sscanf(q + 2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year,
307            &hour, &min, &sec, zone) != 7)
308         return x;
309     if (year < 70)
310         year += 2000;
311     else
312         year += 1900;
313     }
314     else {
315 /* check for acstime() date */
316     if (x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' ||
317         x[16] != ':' || x[19] != ' ' || x[24] != '\0')
318         return x;
319     if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour,
320            &min, &sec, &year) != 7)
321         return x;
322     for (wk = 0; wk < 7; wk++)
323         if (strcmp(week, apr_day_snames[wk]) == 0)
324         break;
325     if (wk == 7)
326         return x;
327     }
328
329 /* check date */
330     for (mon = 0; mon < 12; mon++)
331     if (strcmp(month, apr_month_snames[mon]) == 0)
332         break;
333     if (mon == 12)
334     return x;
335
336     q = apr_palloc(p, 30);
337     apr_snprintf(q, 30, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", apr_day_snames[wk],
338        mday, apr_month_snames[mon], year, hour, min, sec);
339     return q;
340 }
341
342 PROXY_DECLARE(request_rec *)ap_proxy_make_fake_req(conn_rec *c, request_rec *r)
343 {
344     request_rec *rp = apr_pcalloc(c->pool, sizeof(*r));
345
346     rp->pool            = c->pool;
347     rp->status          = HTTP_OK;
348
349     rp->headers_in      = apr_table_make(c->pool, 50);
350     rp->subprocess_env  = apr_table_make(c->pool, 50);
351     rp->headers_out     = apr_table_make(c->pool, 12);
352     rp->err_headers_out = apr_table_make(c->pool, 5);
353     rp->notes           = apr_table_make(c->pool, 5);
354
355     rp->server = r->server;
356     rp->proxyreq = r->proxyreq;
357     rp->request_time = r->request_time;
358     rp->connection      = c;
359     rp->output_filters  = c->output_filters;
360     rp->input_filters   = c->input_filters;
361     rp->proto_output_filters  = c->output_filters;
362     rp->proto_input_filters   = c->input_filters;
363
364     rp->request_config  = ap_create_request_config(c->pool);
365     proxy_run_create_req(r, rp);
366
367     return rp;
368 }
369
370
371 /*
372  * list is a comma-separated list of case-insensitive tokens, with
373  * optional whitespace around the tokens.
374  * The return returns 1 if the token val is found in the list, or 0
375  * otherwise.
376  */
377 PROXY_DECLARE(int) ap_proxy_liststr(const char *list, const char *val)
378 {
379     int len, i;
380     const char *p;
381
382     len = strlen(val);
383
384     while (list != NULL) {
385     p = ap_strchr_c(list, ',');
386     if (p != NULL) {
387         i = p - list;
388         do
389         p++;
390         while (apr_isspace(*p));
391     }
392     else
393         i = strlen(list);
394
395     while (i > 0 && apr_isspace(list[i - 1]))
396         i--;
397     if (i == len && strncasecmp(list, val, len) == 0)
398         return 1;
399     list = p;
400     }
401     return 0;
402 }
403
404 /*
405  * list is a comma-separated list of case-insensitive tokens, with
406  * optional whitespace around the tokens.
407  * if val appears on the list of tokens, it is removed from the list,
408  * and the new list is returned.
409  */
410 PROXY_DECLARE(char *)ap_proxy_removestr(apr_pool_t *pool, const char *list, const char *val)
411 {
412     int len, i;
413     const char *p;
414     char *new = NULL;
415
416     len = strlen(val);
417
418     while (list != NULL) {
419     p = ap_strchr_c(list, ',');
420     if (p != NULL) {
421         i = p - list;
422         do
423         p++;
424         while (apr_isspace(*p));
425     }
426     else
427         i = strlen(list);
428
429     while (i > 0 && apr_isspace(list[i - 1]))
430         i--;
431     if (i == len && strncasecmp(list, val, len) == 0) {
432         /* do nothing */
433     }
434     else {
435         if (new)
436         new = apr_pstrcat(pool, new, ",", apr_pstrndup(pool, list, i), NULL);
437         else
438         new = apr_pstrndup(pool, list, i);
439     }
440     list = p;
441     }
442     return new;
443 }
444
445 /*
446  * Converts 8 hex digits to a time integer
447  */
448 PROXY_DECLARE(int) ap_proxy_hex2sec(const char *x)
449 {
450     int i, ch;
451     unsigned int j;
452
453     for (i = 0, j = 0; i < 8; i++) {
454     ch = x[i];
455     j <<= 4;
456     if (apr_isdigit(ch))
457         j |= ch - '0';
458     else if (apr_isupper(ch))
459         j |= ch - ('A' - 10);
460     else
461         j |= ch - ('a' - 10);
462     }
463     if (j == 0xffffffff)
464     return -1;      /* so that it works with 8-byte ints */
465     else
466     return j;
467 }
468
469 /*
470  * Converts a time integer to 8 hex digits
471  */
472 PROXY_DECLARE(void) ap_proxy_sec2hex(int t, char *y)
473 {
474     int i, ch;
475     unsigned int j = t;
476
477     for (i = 7; i >= 0; i--) {
478     ch = j & 0xF;
479     j >>= 4;
480     if (ch >= 10)
481         y[i] = ch + ('A' - 10);
482     else
483         y[i] = ch + '0';
484     }
485     y[8] = '\0';
486 }
487
488 PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message)
489 {
490     apr_table_setn(r->notes, "error-notes",
491     apr_pstrcat(r->pool, 
492         "The proxy server could not handle the request "
493         "<em><a href=\"", ap_escape_uri(r->pool, r->uri),
494         "\">", ap_escape_html(r->pool, r->method),
495         "&nbsp;", 
496         ap_escape_html(r->pool, r->uri), "</a></em>.<p>\n"
497         "Reason: <strong>",
498         ap_escape_html(r->pool, message), 
499         "</strong></p>", NULL));
500
501     /* Allow "error-notes" string to be printed by ap_send_error_response() */
502     apr_table_setn(r->notes, "verbose-error-to", apr_pstrdup(r->pool, "*"));
503
504     r->status_line = apr_psprintf(r->pool, "%3.3u Proxy Error", statuscode);
505     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
506              "proxy: %s returned by %s", message, r->uri);
507     return statuscode;
508 }
509
510 static const char *
511      proxy_get_host_of_request(request_rec *r)
512 {
513     char *url, *user = NULL, *password = NULL, *err, *host;
514     apr_port_t port;
515
516     if (r->hostname != NULL)
517     return r->hostname;
518
519     /* Set url to the first char after "scheme://" */
520     if ((url = strchr(r->uri, ':')) == NULL
521     || url[1] != '/' || url[2] != '/')
522     return NULL;
523
524     url = apr_pstrdup(r->pool, &url[1]);    /* make it point to "//", which is what proxy_canon_netloc expects */
525
526     err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port);
527
528     if (err != NULL)
529     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
530              "%s", err);
531
532     r->hostname = host;
533
534     return host;        /* ought to return the port, too */
535 }
536
537 /* Return TRUE if addr represents an IP address (or an IP network address) */
538 PROXY_DECLARE(int) ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p)
539 {
540     const char *addr = This->name;
541     long ip_addr[4];
542     int i, quads;
543     long bits;
544
545     /* if the address is given with an explicit netmask, use that */
546     /* Due to a deficiency in apr_inet_addr(), it is impossible to parse */
547     /* "partial" addresses (with less than 4 quads) correctly, i.e.  */
548     /* 192.168.123 is parsed as 192.168.0.123, which is not what I want. */
549     /* I therefore have to parse the IP address manually: */
550     /*if (proxy_readmask(This->name, &This->addr.s_addr, &This->mask.s_addr) == 0) */
551     /* addr and mask were set by proxy_readmask() */
552     /*return 1; */
553
554     /* Parse IP addr manually, optionally allowing */
555     /* abbreviated net addresses like 192.168. */
556
557     /* Iterate over up to 4 (dotted) quads. */
558     for (quads = 0; quads < 4 && *addr != '\0'; ++quads) {
559     char *tmp;
560
561     if (*addr == '/' && quads > 0)  /* netmask starts here. */
562         break;
563
564     if (!apr_isdigit(*addr))
565         return 0;       /* no digit at start of quad */
566
567     ip_addr[quads] = strtol(addr, &tmp, 0);
568
569     if (tmp == addr)    /* expected a digit, found something else */
570         return 0;
571
572     if (ip_addr[quads] < 0 || ip_addr[quads] > 255) {
573         /* invalid octet */
574         return 0;
575     }
576
577     addr = tmp;
578
579     if (*addr == '.' && quads != 3)
580         ++addr;     /* after the 4th quad, a dot would be illegal */
581     }
582
583     for (This->addr.s_addr = 0, i = 0; i < quads; ++i)
584     This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
585
586     if (addr[0] == '/' && apr_isdigit(addr[1])) {   /* net mask follows: */
587     char *tmp;
588
589     ++addr;
590
591     bits = strtol(addr, &tmp, 0);
592
593     if (tmp == addr)    /* expected a digit, found something else */
594         return 0;
595
596     addr = tmp;
597
598     if (bits < 0 || bits > 32)  /* netmask must be between 0 and 32 */
599         return 0;
600
601     }
602     else {
603     /* Determine (i.e., "guess") netmask by counting the */
604     /* number of trailing .0's; reduce #quads appropriately */
605     /* (so that 192.168.0.0 is equivalent to 192.168.)        */
606     while (quads > 0 && ip_addr[quads - 1] == 0)
607         --quads;
608
609     /* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */
610     if (quads < 1)
611         return 0;
612
613     /* every zero-byte counts as 8 zero-bits */
614     bits = 8 * quads;
615
616     if (bits != 32)     /* no warning for fully qualified IP address */
617             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
618           "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld\n",
619          inet_ntoa(This->addr), bits);
620     }
621
622     This->mask.s_addr = htonl(APR_INADDR_NONE << (32 - bits));
623
624     if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) {
625         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
626         "Warning: NetMask and IP-Addr disagree in %s/%ld\n",
627         inet_ntoa(This->addr), bits);
628     This->addr.s_addr &= This->mask.s_addr;
629         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
630         "         Set to %s/%ld\n",
631         inet_ntoa(This->addr), bits);
632     }
633
634     if (*addr == '\0') {
635     This->matcher = proxy_match_ipaddr;
636     return 1;
637     }
638     else
639     return (*addr == '\0'); /* okay iff we've parsed the whole string */
640 }
641
642 /* Return TRUE if addr represents an IP address (or an IP network address) */
643 static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r)
644 {
645     int i, ip_addr[4];
646     struct in_addr addr, *ip;
647     const char *host = proxy_get_host_of_request(r);
648
649     if (host == NULL)   /* oops! */
650        return 0;
651
652     memset(&addr, '\0', sizeof addr);
653     memset(ip_addr, '\0', sizeof ip_addr);
654
655     if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) {
656     for (addr.s_addr = 0, i = 0; i < 4; ++i)
657         addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
658
659     if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) {
660 #if DEBUGGING
661         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
662                          "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr));
663         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
664                          "%s/", inet_ntoa(This->addr));
665         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
666                          "%s", inet_ntoa(This->mask));
667 #endif
668         return 1;
669     }
670 #if DEBUGGING
671     else {
672         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
673                          "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr));
674         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
675                          "%s/", inet_ntoa(This->addr));
676         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
677                          "%s", inet_ntoa(This->mask));
678     }
679 #endif
680     }
681     else {
682     struct apr_sockaddr_t *reqaddr;
683
684         if (apr_sockaddr_info_get(&reqaddr, host, APR_UNSPEC, 0, 0, r->pool)
685         != APR_SUCCESS) {
686 #if DEBUGGING
687         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
688              "2)IP-NoMatch: hostname=%s msg=Host not found", 
689              host);
690 #endif
691         return 0;
692     }
693
694     /* Try to deal with multiple IP addr's for a host */
695     /* FIXME: This needs to be able to deal with IPv6 */
696     while (reqaddr) {
697         ip = (struct in_addr *) reqaddr->ipaddr_ptr;
698         if (This->addr.s_addr == (ip->s_addr & This->mask.s_addr)) {
699 #if DEBUGGING
700         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
701                  "3)IP-Match: %s[%s] <-> ", host, 
702                  inet_ntoa(*ip));
703         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
704                  "%s/", inet_ntoa(This->addr));
705         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
706                  "%s", inet_ntoa(This->mask));
707 #endif
708         return 1;
709         }
710 #if DEBUGGING
711         else {
712                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
713                  "3)IP-NoMatch: %s[%s] <-> ", host, 
714                  inet_ntoa(*ip));
715                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
716                  "%s/", inet_ntoa(This->addr));
717                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
718                  "%s", inet_ntoa(This->mask));
719         }
720 #endif
721         reqaddr = reqaddr->next;
722     }
723     }
724
725     return 0;
726 }
727
728 /* Return TRUE if addr represents a domain name */
729 PROXY_DECLARE(int) ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p)
730 {
731     char *addr = This->name;
732     int i;
733
734     /* Domain name must start with a '.' */
735     if (addr[0] != '.')
736         return 0;
737
738     /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
739     for (i = 0; apr_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i)
740         continue;
741
742 #if 0
743     if (addr[i] == ':') {
744     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
745                      "@@@@ handle optional port in proxy_is_domainname()");
746     /* @@@@ handle optional port */
747     }
748 #endif
749
750     if (addr[i] != '\0')
751         return 0;
752
753     /* Strip trailing dots */
754     for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i)
755         addr[i] = '\0';
756
757     This->matcher = proxy_match_domainname;
758     return 1;
759 }
760
761 /* Return TRUE if host "host" is in domain "domain" */
762 static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r)
763 {
764     const char *host = proxy_get_host_of_request(r);
765     int d_len = strlen(This->name), h_len;
766
767     if (host == NULL)       /* some error was logged already */
768         return 0;
769
770     h_len = strlen(host);
771
772     /* @@@ do this within the setup? */
773     /* Ignore trailing dots in domain comparison: */
774     while (d_len > 0 && This->name[d_len - 1] == '.')
775         --d_len;
776     while (h_len > 0 && host[h_len - 1] == '.')
777         --h_len;
778     return h_len > d_len
779         && strncasecmp(&host[h_len - d_len], This->name, d_len) == 0;
780 }
781
782 /* Return TRUE if host represents a host name */
783 PROXY_DECLARE(int) ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p)
784 {
785     struct apr_sockaddr_t *addr;
786     char *host = This->name;
787     int i;
788
789     /* Host names must not start with a '.' */
790     if (host[0] == '.')
791         return 0;
792
793     /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
794     for (i = 0; apr_isalnum(host[i]) || host[i] == '-' || host[i] == '.'; ++i);
795
796     if (host[i] != '\0' || apr_sockaddr_info_get(&addr, host, APR_UNSPEC, 0, 0, p) != APR_SUCCESS)
797         return 0;
798     
799     This->hostaddr = addr;
800
801     /* Strip trailing dots */
802     for (i = strlen(host) - 1; i > 0 && host[i] == '.'; --i)
803         host[i] = '\0';
804
805     This->matcher = proxy_match_hostname;
806     return 1;
807 }
808
809 /* Return TRUE if host "host" is equal to host2 "host2" */
810 static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r)
811 {
812     char *host = This->name;
813     const char *host2 = proxy_get_host_of_request(r);
814     int h2_len;
815     int h1_len;
816
817     if (host == NULL || host2 == NULL)
818         return 0; /* oops! */
819
820     h2_len = strlen(host2);
821     h1_len = strlen(host);
822
823 #if 0
824     struct apr_sockaddr_t *addr = *This->hostaddr;
825
826     /* Try to deal with multiple IP addr's for a host */
827     while (addr) {
828         if (addr->ipaddr_ptr == ? ? ? ? ? ? ? ? ? ? ? ? ?)
829             return 1;
830         addr = addr->next;
831     }
832 #endif
833
834     /* Ignore trailing dots in host2 comparison: */
835     while (h2_len > 0 && host2[h2_len - 1] == '.')
836         --h2_len;
837     while (h1_len > 0 && host[h1_len - 1] == '.')
838         --h1_len;
839     return h1_len == h2_len
840         && strncasecmp(host, host2, h1_len) == 0;
841 }
842
843 /* Return TRUE if addr is to be matched as a word */
844 PROXY_DECLARE(int) ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p)
845 {
846     This->matcher = proxy_match_word;
847     return 1;
848 }
849
850 /* Return TRUE if string "str2" occurs literally in "str1" */
851 static int proxy_match_word(struct dirconn_entry *This, request_rec *r)
852 {
853     const char *host = proxy_get_host_of_request(r);
854     return host != NULL && ap_strstr_c(host, This->name) != NULL;
855 }
856
857 /* checks whether a host in uri_addr matches proxyblock */
858 PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf, 
859                              apr_sockaddr_t *uri_addr)
860 {
861     int j;
862     apr_sockaddr_t * src_uri_addr = uri_addr;
863     /* XXX FIXME: conf->noproxies->elts is part of an opaque structure */
864     for (j = 0; j < conf->noproxies->nelts; j++) {
865         struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts;
866         struct apr_sockaddr_t *conf_addr = npent[j].addr;
867         uri_addr = src_uri_addr;
868         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
869                      "proxy: checking remote machine [%s] against [%s]", uri_addr->hostname, npent[j].name);
870         if ((npent[j].name && ap_strstr_c(uri_addr->hostname, npent[j].name))
871             || npent[j].name[0] == '*') {
872             ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
873                          "proxy: connect to remote machine %s blocked: name %s matched", uri_addr->hostname, npent[j].name);
874             return HTTP_FORBIDDEN;
875         }
876         while (conf_addr) {
877             while (uri_addr) {
878                 char *conf_ip;
879                 char *uri_ip;
880                 apr_sockaddr_ip_get(&conf_ip, conf_addr);
881                 apr_sockaddr_ip_get(&uri_ip, uri_addr);
882                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
883                              "proxy: ProxyBlock comparing %s and %s", conf_ip, uri_ip);
884                 if (!apr_strnatcasecmp(conf_ip, uri_ip)) {
885                     ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
886                                  "proxy: connect to remote machine %s blocked: IP %s matched", uri_addr->hostname, conf_ip);
887                     return HTTP_FORBIDDEN;
888                 }
889                 uri_addr = uri_addr->next;
890             }
891             conf_addr = conf_addr->next;
892         }
893     }
894     return OK;
895 }
896
897 /* set up the minimal filter set */
898 PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r)
899 {
900     ap_add_input_filter("HTTP_IN", NULL, r, c);
901     return OK;
902 }
903
904 /* converts a series of buckets into a string 
905  * XXX: BillS says this function performs essentially the same function as 
906  * ap_rgetline() in protocol.c. Deprecate this function and use ap_rgetline() 
907  * instead? I think ap_proxy_string_read() will not work properly on non ASCII
908  * (EBCDIC) machines either.
909  */
910 PROXY_DECLARE(apr_status_t) ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb,
911                                                  char *buff, apr_size_t bufflen, int *eos)
912 {
913     apr_bucket *e;
914     apr_status_t rv;
915     char *pos = buff;
916     char *response;
917     int found = 0;
918     apr_size_t len;
919
920     /* start with an empty string */
921     buff[0] = 0;
922     *eos = 0;
923
924     /* loop through each brigade */
925     while (!found) {
926         /* get brigade from network one line at a time */
927         if (APR_SUCCESS != (rv = ap_get_brigade(c->input_filters, bb, 
928                                                 AP_MODE_GETLINE,
929                                                 APR_BLOCK_READ,
930                                                 0))) {
931             return rv;
932         }
933         /* loop through each bucket */
934         while (!found) {
935             if (*eos || APR_BRIGADE_EMPTY(bb)) {
936                 /* The connection aborted or timed out */
937                 return APR_ECONNABORTED;
938             }
939             e = APR_BRIGADE_FIRST(bb);
940             if (APR_BUCKET_IS_EOS(e)) {
941                 *eos = 1;
942             }
943             else {
944                 if (APR_SUCCESS != apr_bucket_read(e, (const char **)&response, &len, APR_BLOCK_READ)) {
945                     return rv;
946                 }
947                 /* is string LF terminated? 
948                  * XXX: This check can be made more efficient by simply checking 
949                  * if the last character in the 'response' buffer is an ASCII_LF.
950                  * See ap_rgetline() for an example.
951                  */
952                 if (memchr(response, APR_ASCII_LF, len)) {
953                     found = 1;
954                 }
955                 /* concat strings until buff is full - then throw the data away */
956                 if (len > ((bufflen-1)-(pos-buff))) {
957                     len = (bufflen-1)-(pos-buff);
958                 }
959                 if (len > 0) {
960                     pos = apr_cpystrn(pos, response, len);
961                 }
962             }
963             APR_BUCKET_REMOVE(e);
964             apr_bucket_destroy(e);
965         }
966     }
967
968     return APR_SUCCESS;
969 }
970
971 /* unmerge an element in the table */
972 PROXY_DECLARE(void) ap_proxy_table_unmerge(apr_pool_t *p, apr_table_t *t, char *key)
973 {
974     apr_off_t offset = 0;
975     apr_off_t count = 0;
976     char *value = NULL;
977
978     /* get the value to unmerge */
979     const char *initial = apr_table_get(t, key);
980     if (!initial) {
981         return;
982     }
983     value = apr_pstrdup(p, initial);
984
985     /* remove the value from the headers */
986     apr_table_unset(t, key);
987
988     /* find each comma */
989     while (value[count]) {
990         if (value[count] == ',') {
991             value[count] = 0;
992             apr_table_add(t, key, value + offset);
993             offset = count + 1;
994         }
995         count++;
996     }
997     apr_table_add(t, key, value + offset);
998 }
999
1000 PROXY_DECLARE(proxy_balancer *) ap_proxy_get_balancer(apr_pool_t *p,
1001                                                       proxy_server_conf *conf,
1002                                                       const char *url)
1003 {
1004     proxy_balancer *balancer;
1005     char *c, *uri = apr_pstrdup(p, url);
1006     int i;
1007     
1008     c = strchr(uri, ':');   
1009     if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
1010        return NULL;
1011     /* remove path from uri */
1012     if ((c = strchr(c + 3, '/')))
1013         *c = '\0';
1014     balancer = (proxy_balancer *)conf->balancers->elts;
1015     for (i = 0; i < conf->balancers->nelts; i++) {
1016         if (strcasecmp(balancer->name, uri) == 0)
1017             return balancer;
1018         balancer++;
1019     }
1020     return NULL;
1021 }
1022
1023 PROXY_DECLARE(const char *) ap_proxy_add_balancer(proxy_balancer **balancer,
1024                                                   apr_pool_t *p,
1025                                                   proxy_server_conf *conf,
1026                                                   const char *url)
1027 {
1028     char *c, *q, *uri = apr_pstrdup(p, url);
1029
1030     c = strchr(uri, ':');   
1031     if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
1032        return "Bad syntax for a balancer name";
1033     /* remove path from uri */
1034     if ((q = strchr(c + 3, '/')))
1035         *q = '\0';
1036
1037     ap_str_tolower(uri);
1038     *balancer = apr_array_push(conf->balancers);
1039     memset(*balancer, 0, sizeof(proxy_balancer));
1040
1041     (*balancer)->name = uri;
1042     (*balancer)->workers = apr_array_make(p, 5, sizeof(proxy_worker));
1043     /* XXX Is this a right place to create mutex */
1044 #if APR_HAS_THREADS
1045     if (apr_thread_mutex_create(&((*balancer)->mutex),
1046                 APR_THREAD_MUTEX_DEFAULT, p) != APR_SUCCESS) {
1047         /* XXX: Do we need to log something here */
1048         return "can not create thread mutex";
1049     }
1050 #endif
1051
1052     return NULL;
1053 }
1054
1055 PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p,
1056                                                   proxy_server_conf *conf,
1057                                                   const char *url)
1058 {
1059     proxy_worker *worker;
1060     char *c, *uri = apr_pstrdup(p, url);
1061     int i;
1062     
1063     c = strchr(uri, ':');   
1064     if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
1065        return NULL;
1066     /* remove path from uri */
1067     if ((c = strchr(c + 3, '/')))
1068         *c = '\0';
1069
1070     worker = (proxy_worker *)conf->workers->elts;
1071     for (i = 0; i < conf->workers->nelts; i++) {
1072         if (strcasecmp(worker->name, uri) == 0) {
1073             return worker;
1074         }
1075         worker++;
1076     }
1077     return NULL;
1078 }
1079
1080 #if APR_HAS_THREADS
1081 static apr_status_t conn_pool_cleanup(void *theworker)
1082 {
1083     proxy_worker *worker = (proxy_worker *)theworker;
1084     if (worker->cp->res) {
1085         worker->cp->pool = NULL;
1086         apr_reslist_destroy(worker->cp->res);
1087     }
1088     return APR_SUCCESS;
1089 }
1090 #endif
1091
1092 static void init_conn_pool(apr_pool_t *p, proxy_worker *worker)
1093 {
1094     apr_pool_t *pool;
1095     proxy_conn_pool *cp;
1096     
1097     /* Create a connection pool's subpool. 
1098      * This pool is used for connection recycling.
1099      * Once the worker is added it is never removed but
1100      * it can be disabled.
1101      */
1102     apr_pool_create(&pool, p);
1103     /* Alloc from the same pool as worker.
1104      * proxy_conn_pool is permanently attached to the worker. 
1105      */
1106     cp = (proxy_conn_pool *)apr_pcalloc(p, sizeof(proxy_conn_pool));
1107     cp->pool = pool;    
1108     worker->cp = cp;
1109 }
1110
1111 PROXY_DECLARE(const char *) ap_proxy_add_worker(proxy_worker **worker,
1112                                                 apr_pool_t *p,
1113                                                 proxy_server_conf *conf,
1114                                                 const char *url)
1115 {
1116     char *c, *q, *uri = apr_pstrdup(p, url);
1117     int port;
1118     
1119     c = strchr(uri, ':');   
1120     if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
1121        return "Bad syntax for a remote proxy server";
1122     /* remove path from uri */
1123     if ((q = strchr(c + 3, '/')))
1124         *q = '\0';
1125
1126     q = strchr(c + 3, ':');
1127     if (q != NULL) {
1128         if (sscanf(q + 1, "%u", &port) != 1 || port > 65535) {
1129             return "Bad syntax for a remote proxy server (bad port number)";
1130         }
1131     }
1132     else
1133         port = -1;
1134     ap_str_tolower(uri);
1135     *worker = apr_array_push(conf->workers);
1136     memset(*worker, 0, sizeof(proxy_worker));
1137     (*worker)->name = apr_pstrdup(p, uri);
1138     *c = '\0';
1139     (*worker)->scheme = uri;
1140     (*worker)->hostname = c + 3;
1141
1142     if (port == -1)
1143         port = apr_uri_port_of_scheme((*worker)->scheme);
1144     (*worker)->port = port;
1145     (*worker)->id   = proxy_lb_workers;
1146     /* Increase the total worker count */
1147     proxy_lb_workers++;
1148     init_conn_pool(p, *worker);
1149 #if APR_HAS_THREADS
1150     if (apr_thread_mutex_create(&((*worker)->mutex),
1151                 APR_THREAD_MUTEX_DEFAULT, p) != APR_SUCCESS) {
1152         /* XXX: Do we need to log something here */
1153         return "can not create thread mutex";
1154     }
1155 #endif
1156
1157     return NULL;
1158 }
1159
1160 PROXY_DECLARE(proxy_worker *) ap_proxy_create_worker(apr_pool_t *p)
1161 {
1162
1163     proxy_worker *worker;
1164     worker = (proxy_worker *)apr_pcalloc(p, sizeof(proxy_worker));
1165     worker->id = proxy_lb_workers;
1166     /* Increase the total worker count */
1167     proxy_lb_workers++;
1168     init_conn_pool(p, worker);
1169
1170     return worker;
1171 }
1172
1173 PROXY_DECLARE(void) 
1174 ap_proxy_add_worker_to_balancer(apr_pool_t *pool, proxy_balancer *balancer,
1175                                 proxy_worker *worker)
1176 {
1177     proxy_worker *runtime;
1178
1179     runtime = apr_array_push(balancer->workers);
1180     memcpy(runtime, worker, sizeof(proxy_worker));
1181     runtime->id = proxy_lb_workers;
1182     /* Increase the total runtime count */
1183     proxy_lb_workers++;
1184
1185 }
1186
1187 PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker,
1188                                         proxy_balancer **balancer,
1189                                         request_rec *r,
1190                                         proxy_server_conf *conf, char **url)
1191 {
1192     int access_status;
1193     
1194     access_status = proxy_run_pre_request(worker, balancer, r, conf, url);
1195     if (access_status == DECLINED && *balancer == NULL) {
1196         *worker = ap_proxy_get_worker(r->pool, conf, *url);
1197         if (*worker) {
1198             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1199                           "proxy: %s: found worker %s for %s",
1200                            (*worker)->scheme, (*worker)->name, *url);
1201
1202             *balancer = NULL;
1203             access_status = OK;
1204         }
1205         else if (r->proxyreq == PROXYREQ_PROXY) {
1206             if (conf->forward) {
1207                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1208                               "proxy: *: found forward proxy worker for %s",
1209                                *url);
1210                 *balancer = NULL;
1211                 *worker = conf->forward;
1212                 access_status = OK;
1213             }
1214         }
1215         else if (r->proxyreq == PROXYREQ_REVERSE) {
1216             if (conf->reverse) {
1217                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1218                               "proxy: *: found reverse proxy worker for %s",
1219                                *url);
1220                 *balancer = NULL;
1221                 *worker = conf->reverse;
1222                 access_status = OK;
1223             }
1224         }
1225     }
1226     else if (access_status == DECLINED && balancer != NULL) {
1227         /* All the workers are busy */
1228         access_status = HTTP_SERVICE_UNAVAILABLE;
1229     }
1230     return access_status;
1231 }
1232
1233 PROXY_DECLARE(int) ap_proxy_post_request(proxy_worker *worker,
1234                                          proxy_balancer *balancer,
1235                                          request_rec *r,
1236                                          proxy_server_conf *conf)
1237 {
1238     int access_status;
1239     if (balancer)
1240         access_status = proxy_run_post_request(worker, balancer, r, conf);
1241     else { 
1242         
1243
1244         access_status = OK;
1245     }
1246
1247     return access_status;
1248 }
1249
1250 /* DEPRECATED */
1251 PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **newsock,
1252                                                const char *proxy_function,
1253                                                apr_sockaddr_t *backend_addr,
1254                                                const char *backend_name,
1255                                                proxy_server_conf *conf,
1256                                                server_rec *s,
1257                                                apr_pool_t *p)
1258 {
1259     apr_status_t rv;
1260     int connected = 0;
1261     int loglevel;
1262     
1263     while (backend_addr && !connected) {
1264         if ((rv = apr_socket_create(newsock, backend_addr->family,
1265                                     SOCK_STREAM, 0, p)) != APR_SUCCESS) {
1266             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
1267             ap_log_error(APLOG_MARK, loglevel, rv, s,
1268                          "proxy: %s: error creating fam %d socket for target %s",
1269                          proxy_function,
1270                          backend_addr->family,
1271                          backend_name);
1272             /* this could be an IPv6 address from the DNS but the
1273              * local machine won't give us an IPv6 socket; hopefully the
1274              * DNS returned an additional address to try
1275              */
1276             backend_addr = backend_addr->next;
1277             continue;
1278         }
1279
1280 #if !defined(TPF) && !defined(BEOS)
1281         if (conf->recv_buffer_size > 0 &&
1282             (rv = apr_socket_opt_set(*newsock, APR_SO_RCVBUF,
1283                                      conf->recv_buffer_size))) {
1284             ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1285                          "apr_socket_opt_set(SO_RCVBUF): Failed to set "
1286                          "ProxyReceiveBufferSize, using default");
1287         }
1288 #endif
1289
1290         /* Set a timeout on the socket */
1291         if (conf->timeout_set == 1) {
1292             apr_socket_timeout_set(*newsock, conf->timeout);
1293         }
1294         else {
1295              apr_socket_timeout_set(*newsock, s->timeout);
1296         }
1297
1298         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1299                      "proxy: %s: fam %d socket created to connect to %s",
1300                      proxy_function, backend_addr->family, backend_name);
1301
1302         /* make the connection out of the socket */
1303         rv = apr_socket_connect(*newsock, backend_addr);
1304
1305         /* if an error occurred, loop round and try again */
1306         if (rv != APR_SUCCESS) {
1307             apr_socket_close(*newsock);
1308             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
1309             ap_log_error(APLOG_MARK, loglevel, rv, s,
1310                          "proxy: %s: attempt to connect to %pI (%s) failed",
1311                          proxy_function,
1312                          backend_addr,
1313                          backend_name);
1314             backend_addr = backend_addr->next;
1315             continue;
1316         }
1317         connected = 1;
1318     }
1319     return connected ? 0 : 1;
1320 }
1321
1322 static apr_status_t connection_cleanup(void *theconn)
1323 {
1324     proxy_conn_rec *conn = (proxy_conn_rec *)theconn;
1325     proxy_worker *worker = conn->worker;
1326     
1327     /* If the connection pool is NULL the worker
1328      * cleanup has been run. Just return.
1329      */
1330     if (!worker->cp)
1331         return APR_SUCCESS;
1332
1333     /* deterimine if the connection need to be closed */
1334     if (conn->close_on_recycle || conn->close) {
1335         apr_pool_t *p = conn->pool;
1336         apr_pool_clear(conn->pool);
1337         memset(conn, 0, sizeof(proxy_conn_rec));
1338         conn->pool = p;
1339         conn->worker = worker;
1340     }
1341 #if APR_HAS_THREADS
1342     if (worker->hmax && worker->cp->res) {
1343         apr_reslist_release(worker->cp->res, (void *)conn);
1344     }
1345     else
1346 #endif
1347     {
1348         worker->cp->conn = conn;
1349     }
1350
1351     /* Allways return the SUCCESS */
1352     return APR_SUCCESS;
1353 }
1354
1355 /* reslist constructor */
1356 static apr_status_t connection_constructor(void **resource, void *params,
1357                                            apr_pool_t *pool)
1358 {
1359     apr_pool_t *ctx;
1360     proxy_conn_rec *conn;
1361     proxy_worker *worker = (proxy_worker *)params;
1362     
1363     /* Create the subpool for each connection
1364      * This keeps the memory consumption constant
1365      * when disconnecting from backend.
1366      */
1367     apr_pool_create(&ctx, pool);
1368     conn = apr_pcalloc(pool, sizeof(proxy_conn_rec));
1369
1370     conn->pool   = ctx;
1371     conn->worker = worker;
1372     *resource = conn;
1373
1374     return APR_SUCCESS;
1375 }
1376
1377 /* reslist destructor */
1378 static apr_status_t connection_destructor(void *resource, void *params,
1379                                           apr_pool_t *pool)
1380 {
1381     proxy_conn_rec *conn = (proxy_conn_rec *)resource;
1382
1383     /* Destroy the pool only if not called from reslist_destroy */    
1384     if (conn->worker->cp->pool)
1385         apr_pool_destroy(conn->pool);
1386
1387     return APR_SUCCESS;
1388 }
1389
1390 PROXY_DECLARE(void) ap_proxy_initialize_worker_share(proxy_server_conf *conf,
1391                                                      proxy_worker *worker,
1392                                                      server_rec *s)
1393 {
1394 #if PROXY_HAS_SCOREBOARD
1395     lb_score *score = NULL;
1396 #else
1397     void *score = NULL;
1398 #endif
1399
1400     if (worker->s && worker->s->status & PROXY_WORKER_INITIALIZED) {
1401         /* The worker share is already initialized */
1402         return;
1403     }
1404 #if PROXY_HAS_SCOREBOARD
1405         /* Get scoreboard slot */
1406     if (ap_scoreboard_image) {
1407         score = ap_get_scoreboard_lb(worker->id);
1408     if (!score)
1409         ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1410               "proxy: ap_get_scoreboard_lb(%d) failed for worker %s",
1411               worker->id, worker->name);
1412     }
1413     else {
1414         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1415               "proxy: initialized scoreboard slot %d for worker %s",
1416               worker->id, worker->name);
1417     }
1418 #endif
1419     if (!score) {
1420         score = apr_pcalloc(conf->pool, sizeof(proxy_worker_stat));
1421         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1422               "proxy: initialized plain memory for worker %s",
1423               worker->name);
1424     }
1425     worker->s = (proxy_worker_stat *)score;
1426     if (worker->route)
1427         strcpy(worker->s->route, worker->route);
1428     else
1429         *worker->s->route = '\0';
1430     if (worker->redirect)
1431         strcpy(worker->s->redirect, worker->redirect);
1432     else
1433         *worker->s->redirect = '\0';
1434     /* Set default parameters */
1435     if (!worker->retry)
1436         worker->retry = apr_time_from_sec(PROXY_WORKER_DEFAULT_RETRY);
1437     /* By default address is reusable */
1438     worker->is_address_reusable = 1;
1439
1440 }
1441
1442 PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, server_rec *s)
1443 {
1444     apr_status_t rv;
1445
1446 #if APR_HAS_THREADS
1447     int mpm_threads;
1448 #endif
1449
1450     if (worker->s->status & PROXY_WORKER_INITIALIZED) {
1451         /* The worker is already initialized */
1452         return APR_SUCCESS;
1453     }
1454
1455 #if APR_HAS_THREADS
1456     ap_mpm_query(AP_MPMQ_MAX_THREADS, &mpm_threads);
1457     if (mpm_threads > 1) {
1458         /* Set hard max to no more then mpm_threads */
1459         if (worker->hmax == 0 || worker->hmax > mpm_threads)
1460             worker->hmax = mpm_threads;
1461         if (worker->smax == 0 || worker->smax > worker->hmax)
1462             worker->smax = worker->hmax;
1463         /* Set min to be lower then smax */
1464         if (worker->min > worker->smax)
1465             worker->min = worker->smax; 
1466     }
1467     else {
1468         /* This will supress the apr_reslist creation */
1469         worker->min = worker->smax = worker->hmax = 0;
1470     }
1471     if (worker->hmax) {
1472         rv = apr_reslist_create(&(worker->cp->res),
1473                                 worker->min, worker->smax,
1474                                 worker->hmax, worker->ttl,
1475                                 connection_constructor, connection_destructor,
1476                                 worker, worker->cp->pool);
1477
1478         apr_pool_cleanup_register(worker->cp->pool, (void *)worker,
1479                                   conn_pool_cleanup,
1480                                   apr_pool_cleanup_null);
1481
1482         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1483             "proxy: initialized worker %d in child %" APR_PID_T_FMT " for (%s) min=%d max=%d smax=%d",
1484              worker->id, getpid(), worker->hostname, worker->min,
1485              worker->hmax, worker->smax);
1486
1487 #if (APR_MAJOR_VERSION > 0)
1488         /* Set the acquire timeout */
1489         if (rv == APR_SUCCESS && worker->acquire_set)
1490             apr_reslist_timeout_set(worker->cp->res, worker->acquire);
1491 #endif
1492     }
1493     else
1494 #endif
1495     {
1496         
1497         rv = connection_constructor((void **)&(worker->cp->conn), worker, worker->cp->pool);
1498         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1499              "proxy: initialized single connection worker %d in child %" APR_PID_T_FMT " for (%s)",
1500              worker->id, getpid(), worker->hostname);
1501     }
1502     if (rv == APR_SUCCESS)
1503         worker->s->status |= PROXY_WORKER_INITIALIZED;
1504     return rv;
1505 }
1506
1507 PROXY_DECLARE(int) ap_proxy_retry_worker(const char *proxy_function,
1508                                          proxy_worker *worker,
1509                                          server_rec *s)
1510 {
1511     if (worker->s->status & PROXY_WORKER_IN_ERROR) {
1512         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1513                     "proxy: %s: retrying the worker for (%s)",
1514                      proxy_function, worker->hostname);
1515         if (apr_time_now() > worker->s->error_time + worker->retry) {
1516             ++worker->s->retries;
1517             worker->s->status &= ~PROXY_WORKER_IN_ERROR;
1518             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1519                          "proxy: %s: worker for (%s) has been marked for retry",
1520                          proxy_function, worker->hostname);
1521             return OK;
1522         }
1523         else
1524             return DECLINED;
1525     }
1526     else
1527         return OK;
1528 }
1529
1530 PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function,
1531                                                proxy_conn_rec **conn,
1532                                                proxy_worker *worker,
1533                                                server_rec *s)
1534 {
1535     apr_status_t rv;
1536
1537     if (!PROXY_WORKER_IS_USABLE(worker)) {
1538         /* Retry the worker */
1539         ap_proxy_retry_worker(proxy_function, worker, s);
1540     
1541         if (!PROXY_WORKER_IS_USABLE(worker)) {
1542             ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1543                          "proxy: %s: disabled connection for (%s)",
1544                          proxy_function, worker->hostname);
1545             return HTTP_SERVICE_UNAVAILABLE;
1546         }
1547     }
1548 #if APR_HAS_THREADS
1549     if (worker->hmax && worker->cp->res) {
1550         rv = apr_reslist_acquire(worker->cp->res, (void **)conn);
1551     }
1552     else
1553 #endif
1554     {
1555         /* create the new connection if the previous was destroyed */
1556         if (!worker->cp->conn)
1557             connection_constructor((void **)conn, worker, worker->cp->pool);
1558         else {
1559             *conn = worker->cp->conn;
1560             worker->cp->conn = NULL;
1561         }
1562         rv = APR_SUCCESS;
1563     }
1564
1565     if (rv != APR_SUCCESS) {
1566         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1567                      "proxy: %s: failed to acquire connection for (%s)",
1568                      proxy_function, worker->hostname);
1569         return HTTP_SERVICE_UNAVAILABLE;
1570     }
1571     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1572                  "proxy: %s: has acquired connection for (%s)",
1573                  proxy_function, worker->hostname);
1574
1575     (*conn)->worker = worker;
1576     (*conn)->close  = 0;
1577     (*conn)->close_on_recycle = 0;
1578
1579     return OK;
1580 }
1581
1582 PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function,
1583                                                proxy_conn_rec *conn,
1584                                                server_rec *s)
1585 {
1586     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1587                  "proxy: %s: has released connection for (%s)",
1588                  proxy_function, conn->worker->hostname);
1589     /* If there is a connection kill it's cleanup */
1590     if (conn->connection) {
1591         apr_pool_cleanup_kill(conn->connection->pool, conn, connection_cleanup);
1592         conn->connection = NULL;
1593     }
1594     connection_cleanup(conn);
1595
1596     return OK;
1597 }
1598
1599 PROXY_DECLARE(int)
1600 ap_proxy_determine_connection(apr_pool_t *p, request_rec *r,
1601                               proxy_server_conf *conf,
1602                               proxy_worker *worker,
1603                               proxy_conn_rec *conn,
1604                               apr_uri_t *uri,
1605                               char **url,
1606                               const char *proxyname,
1607                               apr_port_t proxyport,
1608                               char *server_portstr,
1609                               int server_portstr_size)
1610 {
1611     int server_port;
1612     apr_status_t err = APR_SUCCESS;
1613     /*
1614      * Break up the URL to determine the host to connect to
1615      */
1616
1617     /* we break the URL into host, port, uri */
1618     if (APR_SUCCESS != apr_uri_parse(p, *url, uri)) {
1619         return ap_proxyerror(r, HTTP_BAD_REQUEST,
1620                              apr_pstrcat(p,"URI cannot be parsed: ", *url,
1621                                          NULL));
1622     }
1623     if (!uri->port) {
1624         uri->port = apr_uri_port_of_scheme(uri->scheme);
1625     }
1626
1627     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1628                  "proxy: connecting %s to %s:%d", *url, uri->hostname,
1629                  uri->port);
1630
1631     /* allocate these out of the specified connection pool 
1632      * The scheme handler decides if this is permanent or
1633      * short living pool.
1634      */
1635     /* are we connecting directly, or via a proxy? */
1636     if (!proxyname) {
1637         *url = apr_pstrcat(p, uri->path, uri->query ? "?" : "",
1638                            uri->query ? uri->query : "",
1639                            uri->fragment ? "#" : "",
1640                            uri->fragment ? uri->fragment : "", NULL);
1641     }
1642     if (!conn->hostname) {
1643         if (proxyname) {
1644             conn->hostname = apr_pstrdup(conn->pool, proxyname);
1645             conn->port = proxyport;
1646         } else {
1647             conn->hostname = apr_pstrdup(conn->pool, uri->hostname);
1648             conn->port = uri->port;
1649         }
1650     }
1651     /* TODO: add address cache for forward proxies */
1652     if (r->proxyreq == PROXYREQ_PROXY || r->proxyreq == PROXYREQ_REVERSE ||
1653         !worker->is_address_reusable) {
1654         err = apr_sockaddr_info_get(&(conn->addr),
1655                                     conn->hostname, APR_UNSPEC,
1656                                     conn->port, 0,
1657                                     conn->pool);
1658     }
1659     else if (!worker->cp->addr) {
1660         if ((err = PROXY_THREAD_LOCK(worker)) != APR_SUCCESS) {
1661             ap_log_error(APLOG_MARK, APLOG_ERR, err, r->server,
1662                          "proxy: lock");
1663             return HTTP_INTERNAL_SERVER_ERROR;
1664         }
1665
1666         /* Worker can have the single constant backend adress.
1667          * The single DNS lookup is used once per worker.
1668         * If dynamic change is needed then set the addr to NULL
1669         * inside dynamic config to force the lookup.
1670         */
1671         err = apr_sockaddr_info_get(&(worker->cp->addr),
1672                                     conn->hostname, APR_UNSPEC,
1673                                     conn->port, 0,
1674                                     worker->cp->pool);
1675         conn->addr = worker->cp->addr;
1676         PROXY_THREAD_UNLOCK(worker);
1677     }
1678     if (err != APR_SUCCESS) {
1679         return ap_proxyerror(r, HTTP_BAD_GATEWAY,
1680                              apr_pstrcat(p, "DNS lookup failure for: ",
1681                                          conn->hostname, NULL));
1682     }
1683
1684     /* Get the server port for the Via headers */
1685     {
1686         server_port = ap_get_server_port(r);
1687         if (ap_is_default_port(server_port, r)) {
1688             strcpy(server_portstr,"");
1689         } else {
1690             apr_snprintf(server_portstr, server_portstr_size, ":%d",
1691                          server_port);
1692         }
1693     }
1694     /* check if ProxyBlock directive on this host */
1695     if (OK != ap_proxy_checkproxyblock(r, conf, conn->addr)) {
1696         return ap_proxyerror(r, HTTP_FORBIDDEN,
1697                              "Connect to remote machine blocked");
1698     }
1699     return OK;
1700 }
1701
1702 static int is_socket_connected(apr_socket_t *sock)
1703
1704 {
1705     apr_size_t buffer_len = 1;
1706     char test_buffer[1]; 
1707     apr_status_t socket_status;
1708     apr_interval_time_t current_timeout;
1709     
1710     /* save timeout */
1711     apr_socket_timeout_get(sock, &current_timeout);
1712     /* set no timeout */
1713     apr_socket_timeout_set(sock, 0);
1714     socket_status = apr_socket_recv(sock, test_buffer, &buffer_len);
1715     /* put back old timeout */
1716     apr_socket_timeout_set(sock, current_timeout);
1717     if (APR_STATUS_IS_EOF(socket_status))
1718         return 0;
1719     else
1720         return 1;
1721 }
1722
1723 PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
1724                                             proxy_conn_rec *conn,
1725                                             proxy_worker *worker,
1726                                             server_rec *s)
1727 {
1728     apr_status_t rv;
1729     int connected = 0;
1730     int loglevel;
1731     apr_sockaddr_t *backend_addr = conn->addr;
1732     apr_socket_t *newsock;
1733     
1734     if (conn->sock) {
1735         /* This increases the connection pool size
1736          * but the number of dropped connections is
1737          * relatively small compared to connection lifetime
1738          */
1739         if (!(connected = is_socket_connected(conn->sock))) {        
1740             apr_socket_close(conn->sock);
1741             conn->sock = NULL;
1742         }
1743     }
1744     while (backend_addr && !connected) {
1745         if ((rv = apr_socket_create(&newsock, backend_addr->family,
1746                                 SOCK_STREAM, APR_PROTO_TCP,
1747                                 conn->pool)) != APR_SUCCESS) {
1748             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
1749             ap_log_error(APLOG_MARK, loglevel, rv, s,
1750                          "proxy: %s: error creating fam %d socket for target %s",
1751                          proxy_function,
1752                          backend_addr->family,
1753                          worker->hostname);
1754             /* this could be an IPv6 address from the DNS but the
1755              * local machine won't give us an IPv6 socket; hopefully the
1756              * DNS returned an additional address to try
1757              */
1758             backend_addr = backend_addr->next;
1759             continue;
1760         }
1761
1762 #if !defined(TPF) && !defined(BEOS)
1763         if (worker->recv_buffer_size > 0 &&
1764             (rv = apr_socket_opt_set(newsock, APR_SO_RCVBUF,
1765                                      worker->recv_buffer_size))) {
1766             ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1767                          "apr_socket_opt_set(SO_RCVBUF): Failed to set "
1768                          "ProxyReceiveBufferSize, using default");
1769         }
1770 #endif
1771
1772         /* Set a timeout on the socket */
1773         if (worker->timeout_set == 1) {
1774             apr_socket_timeout_set(newsock, worker->timeout);
1775         }
1776         else {
1777              apr_socket_timeout_set(newsock, s->timeout);
1778         }
1779         /* Set a keepalive option */
1780         if (worker->keepalive) {
1781             if ((rv = apr_socket_opt_set(newsock, 
1782                             APR_SO_KEEPALIVE, 1)) != APR_SUCCESS) {
1783                 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1784                              "apr_socket_opt_set(SO_KEEPALIVE): Failed to set"
1785                              " Keepalive");
1786             }
1787         }
1788         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1789                      "proxy: %s: fam %d socket created to connect to %s",
1790                      proxy_function, backend_addr->family, worker->hostname);
1791
1792         /* make the connection out of the socket */
1793         rv = apr_socket_connect(newsock, backend_addr);
1794
1795         /* if an error occurred, loop round and try again */
1796         if (rv != APR_SUCCESS) {
1797             apr_socket_close(newsock);
1798             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
1799             ap_log_error(APLOG_MARK, loglevel, rv, s,
1800                          "proxy: %s: attempt to connect to %pI (%s) failed",
1801                          proxy_function,
1802                          backend_addr,
1803                          worker->hostname);
1804             backend_addr = backend_addr->next;
1805             continue;
1806         }
1807         
1808         conn->sock   = newsock;
1809         connected    = 1;
1810     }
1811     /* Put the entire worker to error state if
1812      * the PROXY_WORKER_IGNORE_ERRORS flag is not set.
1813      * Altrough some connections may be alive
1814      * no further connections to the worker could be made
1815      */
1816     if (!connected && PROXY_WORKER_IS_USABLE(worker) &&
1817         !(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) {
1818         worker->s->status |= PROXY_WORKER_IN_ERROR;
1819         worker->s->error_time = apr_time_now();
1820         ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1821             "ap_proxy_connect_backend disabling worker for (%s)",
1822             worker->hostname);
1823     }
1824     else {
1825         worker->s->error_time = 0;
1826         worker->s->retries = 0;
1827     }
1828     return connected ? OK : DECLINED;
1829 }
1830
1831 PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function,
1832                                               proxy_conn_rec *conn,
1833                                               conn_rec *c,
1834                                               server_rec *s)
1835 {
1836     apr_sockaddr_t *backend_addr = conn->addr;
1837     int rc;
1838
1839     /* The socket is now open, create a new backend server connection 
1840     * 
1841     */
1842     conn->connection = ap_run_create_connection(c->pool, s, conn->sock,
1843                                                 c->id, c->sbh,
1844                                                 c->bucket_alloc);
1845
1846     if (!conn->connection) {
1847         /* the peer reset the connection already; ap_run_create_connection() 
1848         * closed the socket
1849         */
1850         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
1851                      s, "proxy: %s: an error occurred creating a "
1852                      "new connection to %pI (%s)", proxy_function,
1853                      backend_addr, conn->hostname);
1854         /* XXX: Will be closed when proxy_conn is closed */
1855         apr_socket_close(conn->sock);
1856         conn->sock = NULL;
1857         return HTTP_INTERNAL_SERVER_ERROR;
1858     }
1859     /* register the connection cleanup to client connection
1860      * so that the connection can be closed or reused
1861      */
1862     apr_pool_cleanup_register(c->pool, (void *)conn,
1863                               connection_cleanup,
1864                               apr_pool_cleanup_null);      
1865
1866     /* For ssl connection to backend */
1867     if (conn->is_ssl) {
1868         if (!ap_proxy_ssl_enable(conn->connection)) {
1869             ap_log_error(APLOG_MARK, APLOG_ERR, 0,
1870                          s, "proxy: %s: failed to enable ssl support "
1871                          "for %pI (%s)", proxy_function, 
1872                          backend_addr, conn->hostname);
1873             return HTTP_INTERNAL_SERVER_ERROR;
1874         }
1875     }
1876     else {
1877         /* TODO: See if this will break FTP */
1878         ap_proxy_ssl_disable(conn->connection);
1879     }
1880
1881     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1882                  "proxy: %s: connection complete to %pI (%s)",
1883                  proxy_function, backend_addr, conn->hostname);
1884
1885     /* set up the connection filters */
1886     rc = ap_run_pre_connection(conn->connection, conn->sock);
1887     if (rc != OK && rc != DONE) {
1888         conn->connection->aborted = 1;
1889         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1890                      "proxy: %s: pre_connection setup failed (%d)",
1891                      proxy_function, rc);
1892         return rc;
1893     }
1894
1895     return OK;
1896 }
1897
1898 int ap_proxy_lb_workers(void)
1899 {
1900     /* Since we can't resize the scoreboard when reconfiguring, we
1901      * have to impose a limit on the number of workers, we are
1902      * able to reconfigure to.
1903      */
1904     if (!lb_workers_limit)
1905     lb_workers_limit = proxy_lb_workers + PROXY_DYNAMIC_BALANCER_LIMIT;
1906     return lb_workers_limit;
1907 }