]> granicus.if.org Git - apache/blob - modules/proxy/proxy_util.c
No need to allocate hostname on each request for
[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 static int 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     apr_status_t rc = 0;
1030
1031     c = strchr(uri, ':');   
1032     if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
1033        return "Bad syntax for a balancer name";
1034     /* remove path from uri */
1035     if ((q = strchr(c + 3, '/')))
1036         *q = '\0';
1037
1038     ap_str_tolower(uri);
1039     *balancer = apr_array_push(conf->balancers);
1040     memset(*balancer, 0, sizeof(proxy_balancer));
1041
1042     (*balancer)->name = uri;
1043     (*balancer)->workers = apr_array_make(p, 5, sizeof(proxy_runtime_worker));
1044     /* XXX Is this a right place to create mutex */
1045 #if APR_HAS_THREADS
1046     if ((rc = apr_thread_mutex_create(&((*balancer)->mutex),
1047                 APR_THREAD_MUTEX_DEFAULT, p)) != APR_SUCCESS) {
1048             /* XXX: Do we need to log something here */
1049             return "can not create thread mutex";
1050     }
1051 #endif
1052
1053     return NULL;
1054 }
1055
1056 PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p,
1057                                                   proxy_server_conf *conf,
1058                                                   const char *url)
1059 {
1060     proxy_worker *worker;
1061     char *c, *uri = apr_pstrdup(p, url);
1062     int i;
1063     
1064     c = strchr(uri, ':');   
1065     if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
1066        return NULL;
1067     /* remove path from uri */
1068     if ((c = strchr(c + 3, '/')))
1069         *c = '\0';
1070
1071     worker = (proxy_worker *)conf->workers->elts;
1072     for (i = 0; i < conf->workers->nelts; i++) {
1073         if (strcasecmp(worker->name, uri) == 0) {
1074             return worker;
1075         }
1076         worker++;
1077     }
1078     return NULL;
1079 }
1080
1081 #if APR_HAS_THREADS
1082 static apr_status_t conn_pool_cleanup(void *theworker)
1083 {
1084     proxy_worker *worker = (proxy_worker *)theworker;
1085     if (worker->cp->res) {
1086         worker->cp->pool = NULL;
1087         apr_reslist_destroy(worker->cp->res);
1088     }
1089     return APR_SUCCESS;
1090 }
1091 #endif
1092
1093 static void init_conn_pool(apr_pool_t *p, proxy_worker *worker)
1094 {
1095     apr_pool_t *pool;
1096     proxy_conn_pool *cp;
1097     
1098     /* Create a connection pool's subpool. 
1099      * This pool is used for connection recycling.
1100      * Once the worker is added it is never removed but
1101      * it can be disabled.
1102      */
1103     apr_pool_create(&pool, p);
1104     /* Alloc from the same pool as worker.
1105      * proxy_conn_pool is permanently attached to the worker. 
1106      */
1107     cp = (proxy_conn_pool *)apr_pcalloc(p, sizeof(proxy_conn_pool));
1108     cp->pool = pool;    
1109     worker->cp = cp;
1110 }
1111
1112 PROXY_DECLARE(const char *) ap_proxy_add_worker(proxy_worker **worker,
1113                                                 apr_pool_t *p,
1114                                                 proxy_server_conf *conf,
1115                                                 const char *url)
1116 {
1117     char *c, *q, *uri = apr_pstrdup(p, url);
1118     int port;
1119     
1120     c = strchr(uri, ':');   
1121     if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
1122        return "Bad syntax for a remote proxy server";
1123     /* remove path from uri */
1124     if ((q = strchr(c + 3, '/')))
1125         *q = '\0';
1126
1127     q = strchr(c + 3, ':');
1128     if (q != NULL) {
1129         if (sscanf(q + 1, "%u", &port) != 1 || port > 65535) {
1130             return "Bad syntax for a remote proxy server (bad port number)";
1131         }
1132     }
1133     else
1134         port = -1;
1135     ap_str_tolower(uri);
1136     *worker = apr_array_push(conf->workers);
1137     memset(*worker, 0, sizeof(proxy_worker));
1138     (*worker)->name = apr_pstrdup(p, uri);
1139     *c = '\0';
1140     (*worker)->scheme = uri;
1141     (*worker)->hostname = c + 3;
1142
1143     if (port == -1)
1144         port = apr_uri_port_of_scheme((*worker)->scheme);
1145     (*worker)->port = port;
1146
1147     init_conn_pool(p, *worker);
1148
1149     return NULL;
1150 }
1151
1152 PROXY_DECLARE(proxy_worker *) ap_proxy_create_worker(apr_pool_t *p)
1153 {
1154
1155     proxy_worker *worker;
1156     worker = (proxy_worker *)apr_pcalloc(p, sizeof(proxy_worker));
1157     init_conn_pool(p, worker);
1158
1159     return worker;
1160 }
1161
1162 PROXY_DECLARE(void) 
1163 ap_proxy_add_worker_to_balancer(apr_pool_t *pool, proxy_balancer *balancer, proxy_worker *worker)
1164 {
1165     proxy_runtime_worker *runtime;
1166
1167 #if PROXY_HAS_SCOREBOARD
1168     int mpm_daemons;
1169
1170     ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &mpm_daemons);
1171     /* Check if we are prefork or single child */
1172     if (worker->hmax && mpm_daemons > 1) {
1173         /* Check only if workers_limit is set */
1174         if (lb_workers_limit && (lb_workers + 1) > lb_workers_limit) {
1175             ap_log_perror(APLOG_MARK, APLOG_ERR, 0, pool,
1176                           "proxy: Can not add worker (%s) to balancer (%s)."
1177                           " Dynamic limit reached.",
1178                           worker->name, balancer->name);
1179             return;
1180         }
1181     }
1182 #endif
1183     runtime = apr_array_push(balancer->workers);
1184     runtime->w  = worker;
1185     runtime->b  = balancer;
1186     runtime->id = lb_workers;
1187     runtime->s  = NULL;
1188     /* Increase the total runtime count */
1189     ++lb_workers;
1190
1191 }
1192
1193 PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker,
1194                                         proxy_balancer **balancer,
1195                                         request_rec *r,
1196                                         proxy_server_conf *conf, char **url)
1197 {
1198     int access_status;
1199     
1200     access_status = proxy_run_pre_request(worker, balancer, r, conf, url);
1201     if (access_status == DECLINED && *balancer == NULL) {
1202         *worker = ap_proxy_get_worker(r->pool, conf, *url);
1203         if (*worker) {
1204             *balancer = NULL;
1205             access_status = OK;
1206         }
1207         else if (r->proxyreq == PROXYREQ_PROXY) {
1208             if (conf->forward) {
1209                 *balancer = NULL;
1210                 *worker = conf->forward;
1211                 access_status = OK;
1212             }
1213         }
1214     }
1215     else if (access_status == DECLINED && balancer != NULL) {
1216         /* All the workers are busy */
1217         access_status = HTTP_SERVICE_UNAVAILABLE;
1218     }
1219     return access_status;
1220 }
1221
1222 PROXY_DECLARE(int) ap_proxy_post_request(proxy_worker *worker,
1223                                          proxy_balancer *balancer,
1224                                          request_rec *r,
1225                                          proxy_server_conf *conf)
1226 {
1227     int access_status;
1228     if (balancer)
1229         access_status = proxy_run_post_request(worker, balancer, r, conf);
1230     else { 
1231         
1232
1233         access_status = OK;
1234     }
1235
1236     return access_status;
1237 }
1238
1239 /* DEPRECATED */
1240 PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **newsock,
1241                                                const char *proxy_function,
1242                                                apr_sockaddr_t *backend_addr,
1243                                                const char *backend_name,
1244                                                proxy_server_conf *conf,
1245                                                server_rec *s,
1246                                                apr_pool_t *p)
1247 {
1248     apr_status_t rv;
1249     int connected = 0;
1250     int loglevel;
1251     
1252     while (backend_addr && !connected) {
1253         if ((rv = apr_socket_create(newsock, backend_addr->family,
1254                                     SOCK_STREAM, 0, p)) != APR_SUCCESS) {
1255             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
1256             ap_log_error(APLOG_MARK, loglevel, rv, s,
1257                          "proxy: %s: error creating fam %d socket for target %s",
1258                          proxy_function,
1259                          backend_addr->family,
1260                          backend_name);
1261             /* this could be an IPv6 address from the DNS but the
1262              * local machine won't give us an IPv6 socket; hopefully the
1263              * DNS returned an additional address to try
1264              */
1265             backend_addr = backend_addr->next;
1266             continue;
1267         }
1268
1269 #if !defined(TPF) && !defined(BEOS)
1270         if (conf->recv_buffer_size > 0 &&
1271             (rv = apr_socket_opt_set(*newsock, APR_SO_RCVBUF,
1272                                      conf->recv_buffer_size))) {
1273             ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1274                          "apr_socket_opt_set(SO_RCVBUF): Failed to set "
1275                          "ProxyReceiveBufferSize, using default");
1276         }
1277 #endif
1278
1279         /* Set a timeout on the socket */
1280         if (conf->timeout_set == 1) {
1281             apr_socket_timeout_set(*newsock, conf->timeout);
1282         }
1283         else {
1284              apr_socket_timeout_set(*newsock, s->timeout);
1285         }
1286
1287         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1288                      "proxy: %s: fam %d socket created to connect to %s",
1289                      proxy_function, backend_addr->family, backend_name);
1290
1291         /* make the connection out of the socket */
1292         rv = apr_socket_connect(*newsock, backend_addr);
1293
1294         /* if an error occurred, loop round and try again */
1295         if (rv != APR_SUCCESS) {
1296             apr_socket_close(*newsock);
1297             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
1298             ap_log_error(APLOG_MARK, loglevel, rv, s,
1299                          "proxy: %s: attempt to connect to %pI (%s) failed",
1300                          proxy_function,
1301                          backend_addr,
1302                          backend_name);
1303             backend_addr = backend_addr->next;
1304             continue;
1305         }
1306         connected = 1;
1307     }
1308     return connected ? 0 : 1;
1309 }
1310
1311 static apr_status_t connection_cleanup(void *theconn)
1312 {
1313     proxy_conn_rec *conn = (proxy_conn_rec *)theconn;
1314     proxy_worker *worker = conn->worker;
1315     
1316     /* If the connection pool is NULL the worker
1317      * cleanup has been run. Just return.
1318      */
1319     if (!worker->cp)
1320         return APR_SUCCESS;
1321
1322     /* deterimine if the connection need to be closed */
1323     if (conn->close_on_recycle || conn->close) {
1324         apr_pool_t *p = conn->pool;
1325         apr_pool_clear(conn->pool);
1326         memset(conn, 0, sizeof(proxy_conn_rec));
1327         conn->pool = p;
1328         conn->worker = worker;
1329     }
1330 #if APR_HAS_THREADS
1331     if (worker->hmax && worker->cp->res) {
1332         apr_reslist_release(worker->cp->res, (void *)conn);
1333     }
1334     else
1335 #endif
1336     {
1337         worker->cp->conn = conn;
1338     }
1339
1340     /* Allways return the SUCCESS */
1341     return APR_SUCCESS;
1342 }
1343
1344 /* reslist constructor */
1345 static apr_status_t connection_constructor(void **resource, void *params,
1346                                            apr_pool_t *pool)
1347 {
1348     apr_pool_t *ctx;
1349     proxy_conn_rec *conn;
1350     proxy_worker *worker = (proxy_worker *)params;
1351     
1352     /* Create the subpool for each connection
1353      * This keeps the memory consumption constant
1354      * when disconnecting from backend.
1355      */
1356     apr_pool_create(&ctx, pool);
1357     conn = apr_pcalloc(pool, sizeof(proxy_conn_rec));
1358
1359     conn->pool   = ctx;
1360     conn->worker = worker;
1361     *resource = conn;
1362
1363     return APR_SUCCESS;
1364 }
1365
1366 /* reslist destructor */
1367 static apr_status_t connection_destructor(void *resource, void *params,
1368                                           apr_pool_t *pool)
1369 {
1370     proxy_conn_rec *conn = (proxy_conn_rec *)resource;
1371
1372     /* Destroy the pool only if not called from reslist_destroy */    
1373     if (conn->worker->cp->pool)
1374         apr_pool_destroy(conn->pool);
1375
1376     return APR_SUCCESS;
1377 }
1378
1379 PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, server_rec *s)
1380 {
1381     apr_status_t rv;
1382
1383 #if APR_HAS_THREADS
1384     int mpm_threads;
1385
1386     ap_mpm_query(AP_MPMQ_MAX_THREADS, &mpm_threads);
1387     if (mpm_threads > 1) {
1388         /* Set hard max to no more then mpm_threads */
1389         if (worker->hmax == 0 || worker->hmax > mpm_threads)
1390             worker->hmax = mpm_threads;
1391         if (worker->smax == 0 || worker->smax > worker->hmax)
1392             worker->smax = worker->hmax;
1393         /* Set min to be lower then smax */
1394         if (worker->min > worker->smax)
1395             worker->min = worker->smax; 
1396         worker->cp->nfree = worker->hmax;
1397     }
1398     else {
1399         /* This will supress the apr_reslist creation */
1400         worker->min = worker->smax = worker->hmax = 0;
1401     }
1402     if (worker->hmax) {
1403         rv = apr_reslist_create(&(worker->cp->res),
1404                                 worker->min, worker->smax,
1405                                 worker->hmax, worker->ttl,
1406                                 connection_constructor, connection_destructor,
1407                                 worker, worker->cp->pool);
1408
1409         apr_pool_cleanup_register(worker->cp->pool, (void *)worker,
1410                                   conn_pool_cleanup,
1411                                   apr_pool_cleanup_null);
1412
1413         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1414                      "proxy: initialized worker for (%s) min=%d max=%d smax=%d",
1415                       worker->hostname, worker->min, worker->hmax, worker->smax);
1416
1417 #if (APR_MAJOR_VERSION > 0)
1418         /* Set the acquire timeout */
1419         if (rv == APR_SUCCESS && worker->acquire_set)
1420             apr_reslist_timeout_set(worker->cp->res, worker->acquire);
1421 #endif
1422     }
1423     else
1424 #endif
1425     {
1426         
1427         rv = connection_constructor((void **)&(worker->cp->conn), worker, worker->cp->pool);
1428         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1429                      "proxy: initialized single connection worker for (%s)",
1430                       worker->hostname);
1431     }
1432     if (rv == APR_SUCCESS)
1433         worker->status |= PROXY_WORKER_INITIALIZED;
1434     return rv;
1435 }
1436
1437 PROXY_DECLARE(int) ap_proxy_retry_worker(const char *proxy_function,
1438                                          proxy_worker *worker,
1439                                          server_rec *s)
1440 {
1441     if (worker->status & PROXY_WORKER_IN_ERROR) {
1442         apr_interval_time_t diff;
1443         apr_time_t now = apr_time_now();
1444         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1445                     "proxy: %s: retrying the worker for (%s)",
1446                      proxy_function, worker->hostname);
1447         if (worker->retry)
1448             diff = worker->retry;
1449         else
1450             diff = apr_time_from_sec((60 + 60 * worker->retries++));
1451         if (now > worker->error_time + diff) {
1452             worker->status &= ~PROXY_WORKER_IN_ERROR;
1453             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1454                          "proxy: %s: worker for (%s) has been marked for retry",
1455                          proxy_function, worker->hostname);
1456             return OK;
1457         }
1458         else
1459             return DECLINED;
1460     }
1461     else
1462         return OK;
1463 }
1464
1465 PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function,
1466                                                proxy_conn_rec **conn,
1467                                                proxy_worker *worker,
1468                                                server_rec *s)
1469 {
1470     apr_status_t rv;
1471
1472     if (!PROXY_WORKER_IS_USABLE(worker)) {
1473         /* Retry the worker */
1474         ap_proxy_retry_worker(proxy_function, worker, s);
1475     
1476         if (!PROXY_WORKER_IS_USABLE(worker)) {
1477             ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1478                          "proxy: %s: disabled connection for (%s)",
1479                          proxy_function, worker->hostname);
1480             return HTTP_SERVICE_UNAVAILABLE;
1481         }
1482     }
1483 #if APR_HAS_THREADS
1484     if (worker->hmax) {
1485         rv = apr_reslist_acquire(worker->cp->res, (void **)conn);
1486     }
1487     else
1488 #endif
1489     {
1490         /* create the new connection if the previous was destroyed */
1491         if (!worker->cp->conn)
1492             connection_constructor((void **)conn, worker, worker->cp->pool);
1493         else {
1494             *conn = worker->cp->conn;
1495             worker->cp->conn = NULL;
1496         }
1497         rv = APR_SUCCESS;
1498     }
1499
1500     if (rv != APR_SUCCESS) {
1501         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1502                      "proxy: %s: failed to acquire connection for (%s)",
1503                      proxy_function, worker->hostname);
1504         return HTTP_SERVICE_UNAVAILABLE;
1505     }
1506     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1507                  "proxy: %s: has acquired connection for (%s)",
1508                  proxy_function, worker->hostname);
1509
1510     (*conn)->worker = worker;
1511     (*conn)->close  = 0;
1512     (*conn)->close_on_recycle = 0;
1513
1514     return OK;
1515 }
1516
1517 PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function,
1518                                                proxy_conn_rec *conn,
1519                                                server_rec *s)
1520 {
1521     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1522                  "proxy: %s: has released connection for (%s)",
1523                  proxy_function, conn->worker->hostname);
1524     /* If there is a connection kill it's cleanup */
1525     if (conn->connection) {
1526         apr_pool_cleanup_kill(conn->connection->pool, conn, connection_cleanup);
1527         conn->connection = NULL;
1528     }
1529     connection_cleanup(conn);
1530
1531     return OK;
1532 }
1533
1534 PROXY_DECLARE(int)
1535 ap_proxy_determine_connection(apr_pool_t *p, request_rec *r,
1536                               proxy_server_conf *conf,
1537                               proxy_worker *worker,
1538                               proxy_conn_rec *conn,
1539                               apr_uri_t *uri,
1540                               char **url,
1541                               const char *proxyname,
1542                               apr_port_t proxyport,
1543                               char *server_portstr,
1544                               int server_portstr_size)
1545 {
1546     int server_port;
1547     apr_status_t err = APR_SUCCESS;
1548     /*
1549      * Break up the URL to determine the host to connect to
1550      */
1551
1552     /* we break the URL into host, port, uri */
1553     if (APR_SUCCESS != apr_uri_parse(p, *url, uri)) {
1554         return ap_proxyerror(r, HTTP_BAD_REQUEST,
1555                              apr_pstrcat(p,"URI cannot be parsed: ", *url,
1556                                          NULL));
1557     }
1558     if (!uri->port) {
1559         uri->port = apr_uri_port_of_scheme(uri->scheme);
1560     }
1561
1562     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1563                  "proxy: connecting %s to %s:%d", *url, uri->hostname,
1564                  uri->port);
1565
1566     /* allocate these out of the specified connection pool 
1567      * The scheme handler decides if this is permanent or
1568      * short living pool.
1569      */
1570     /* are we connecting directly, or via a proxy? */
1571     if (proxyname) {
1572         *url = apr_pstrcat(p, uri->path, uri->query ? "?" : "",
1573                            uri->query ? uri->query : "",
1574                            uri->fragment ? "#" : "",
1575                            uri->fragment ? uri->fragment : "", NULL);
1576     }
1577     if (!conn->hostname) {
1578         if (proxyname) {
1579             conn->hostname = apr_pstrdup(conn->pool, proxyname);
1580             conn->port = proxyport;
1581         } else {
1582             conn->hostname = apr_pstrdup(conn->pool, uri->hostname);
1583             conn->port = uri->port;
1584         }
1585     }
1586     /* TODO: add address cache for forward proxies */
1587     conn->addr = worker->cp->addr;
1588     if (r->proxyreq == PROXYREQ_PROXY) {
1589         err = apr_sockaddr_info_get(&(conn->addr),
1590                                     conn->hostname, APR_UNSPEC,
1591                                     conn->port, 0,
1592                                     conn->pool);
1593     }
1594     else if (!worker->cp->addr) {
1595         /* Worker can have the single constant backend adress.
1596          * The single DNS lookup is used once per worker.
1597         * If dynamic change is needed then set the addr to NULL
1598         * inside dynamic config to force the lookup.
1599         */
1600         err = apr_sockaddr_info_get(&(worker->cp->addr),
1601                                     conn->hostname, APR_UNSPEC,
1602                                     conn->port, 0,
1603                                     worker->cp->pool);
1604         conn->addr = worker->cp->addr;
1605     }
1606     if (err != APR_SUCCESS) {
1607         return ap_proxyerror(r, HTTP_BAD_GATEWAY,
1608                              apr_pstrcat(p, "DNS lookup failure for: ",
1609                                          conn->hostname, NULL));
1610     }
1611
1612     /* Get the server port for the Via headers */
1613     {
1614         server_port = ap_get_server_port(r);
1615         if (ap_is_default_port(server_port, r)) {
1616             strcpy(server_portstr,"");
1617         } else {
1618             apr_snprintf(server_portstr, server_portstr_size, ":%d",
1619                          server_port);
1620         }
1621     }
1622
1623     /* check if ProxyBlock directive on this host */
1624     if (OK != ap_proxy_checkproxyblock(r, conf, conn->addr)) {
1625         return ap_proxyerror(r, HTTP_FORBIDDEN,
1626                              "Connect to remote machine blocked");
1627     }
1628     return OK;
1629 }
1630
1631 static int is_socket_connected(apr_socket_t *sock)
1632
1633 {
1634     apr_size_t buffer_len = 1;
1635     char test_buffer[1]; 
1636     apr_status_t socket_status;
1637     apr_interval_time_t current_timeout;
1638     
1639     /* save timeout */
1640     apr_socket_timeout_get(sock, &current_timeout);
1641     /* set no timeout */
1642     apr_socket_timeout_set(sock, 0);
1643     socket_status = apr_socket_recv(sock, test_buffer, &buffer_len);
1644     /* put back old timeout */
1645     apr_socket_timeout_set(sock, current_timeout);
1646     if (APR_STATUS_IS_EOF(socket_status))
1647         return 0;
1648     else
1649         return 1;
1650 }
1651
1652 PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
1653                                             proxy_conn_rec *conn,
1654                                             proxy_worker *worker,
1655                                             server_rec *s)
1656 {
1657     apr_status_t rv;
1658     int connected = 0;
1659     int loglevel;
1660     apr_sockaddr_t *backend_addr = conn->addr;
1661     apr_socket_t *newsock;
1662     
1663     if (conn->sock) {
1664         /* This increases the connection pool size
1665          * but the number of dropped connections is
1666          * relatively small compared to connection lifetime
1667          */
1668         if (!(connected = is_socket_connected(conn->sock))) {        
1669             apr_socket_close(conn->sock);
1670             conn->sock = NULL;
1671         }
1672     }
1673     while (backend_addr && !connected) {
1674         if ((rv = apr_socket_create(&newsock, backend_addr->family,
1675                                 SOCK_STREAM, APR_PROTO_TCP,
1676                                 conn->pool)) != APR_SUCCESS) {
1677             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
1678             ap_log_error(APLOG_MARK, loglevel, rv, s,
1679                          "proxy: %s: error creating fam %d socket for target %s",
1680                          proxy_function,
1681                          backend_addr->family,
1682                          worker->hostname);
1683             /* this could be an IPv6 address from the DNS but the
1684              * local machine won't give us an IPv6 socket; hopefully the
1685              * DNS returned an additional address to try
1686              */
1687             backend_addr = backend_addr->next;
1688             continue;
1689         }
1690
1691 #if !defined(TPF) && !defined(BEOS)
1692         if (worker->recv_buffer_size > 0 &&
1693             (rv = apr_socket_opt_set(newsock, APR_SO_RCVBUF,
1694                                      worker->recv_buffer_size))) {
1695             ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1696                          "apr_socket_opt_set(SO_RCVBUF): Failed to set "
1697                          "ProxyReceiveBufferSize, using default");
1698         }
1699 #endif
1700
1701         /* Set a timeout on the socket */
1702         if (worker->timeout_set == 1) {
1703             apr_socket_timeout_set(newsock, worker->timeout);
1704         }
1705         else {
1706              apr_socket_timeout_set(newsock, s->timeout);
1707         }
1708         /* Set a keepalive option */
1709         if (worker->keepalive) {
1710             if ((rv = apr_socket_opt_set(newsock, 
1711                             APR_SO_KEEPALIVE, 1)) != APR_SUCCESS) {
1712                 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1713                              "apr_socket_opt_set(SO_KEEPALIVE): Failed to set"
1714                              " Keepalive");
1715             }
1716         }
1717         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1718                      "proxy: %s: fam %d socket created to connect to %s",
1719                      proxy_function, backend_addr->family, worker->hostname);
1720
1721         /* make the connection out of the socket */
1722         rv = apr_socket_connect(newsock, backend_addr);
1723
1724         /* if an error occurred, loop round and try again */
1725         if (rv != APR_SUCCESS) {
1726             apr_socket_close(newsock);
1727             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
1728             ap_log_error(APLOG_MARK, loglevel, rv, s,
1729                          "proxy: %s: attempt to connect to %pI (%s) failed",
1730                          proxy_function,
1731                          backend_addr,
1732                          worker->hostname);
1733             backend_addr = backend_addr->next;
1734             continue;
1735         }
1736         
1737         conn->sock   = newsock;
1738         connected    = 1;
1739     }
1740     /* Put the entire worker to error state if
1741      * the PROXY_WORKER_IGNORE_ERRORS flag is not set.
1742      * Altrough some connections may be alive
1743      * no further connections to the worker could be made
1744      */
1745     if (!connected && PROXY_WORKER_IS_USABLE(worker) &&
1746         !(worker->status & PROXY_WORKER_IGNORE_ERRORS)) {
1747         worker->status |= PROXY_WORKER_IN_ERROR;
1748         worker->error_time = apr_time_now();
1749         ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1750             "ap_proxy_connect_backend disabling worker for (%s)",
1751             worker->hostname);
1752     }
1753     else {
1754         worker->error_time = 0;
1755         worker->retries = 0;
1756     }
1757     return connected ? OK : DECLINED;
1758 }
1759
1760 PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function,
1761                                               proxy_conn_rec *conn,
1762                                               conn_rec *c,
1763                                               server_rec *s)
1764 {
1765     apr_sockaddr_t *backend_addr = conn->addr;
1766
1767     /* The socket is now open, create a new backend server connection 
1768     * 
1769     */
1770     conn->connection = ap_run_create_connection(c->pool, s, conn->sock,
1771                                                 c->id, c->sbh,
1772                                                 c->bucket_alloc);
1773
1774     if (!conn->connection) {
1775         /* the peer reset the connection already; ap_run_create_connection() 
1776         * closed the socket
1777         */
1778         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
1779                      s, "proxy: %s: an error occurred creating a "
1780                      "new connection to %pI (%s)", proxy_function,
1781                      backend_addr, conn->hostname);
1782         /* XXX: Will be closed when proxy_conn is closed */
1783         apr_socket_close(conn->sock);
1784         conn->sock = NULL;
1785         return HTTP_INTERNAL_SERVER_ERROR;
1786     }
1787     /* register the connection cleanup to client connection
1788      * so that the connection can be closed or reused
1789      */
1790     apr_pool_cleanup_register(c->pool, (void *)conn,
1791                               connection_cleanup,
1792                               apr_pool_cleanup_null);      
1793
1794     /* For ssl connection to backend */
1795     if (conn->is_ssl) {
1796         if (!ap_proxy_ssl_enable(conn->connection)) {
1797             ap_log_error(APLOG_MARK, APLOG_ERR, 0,
1798                          s, "proxy: %s: failed to enable ssl support "
1799                          "for %pI (%s)", proxy_function, 
1800                          backend_addr, conn->hostname);
1801             return HTTP_INTERNAL_SERVER_ERROR;
1802         }
1803     }
1804     else {
1805         /* TODO: See if this will break FTP */
1806         ap_proxy_ssl_disable(conn->connection);
1807     }
1808
1809     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1810                  "proxy: %s: connection complete to %pI (%s)",
1811                  proxy_function, backend_addr, conn->hostname);
1812
1813     /* set up the connection filters */
1814     ap_run_pre_connection(conn->connection, conn->sock);
1815
1816     return OK;
1817 }
1818
1819 PROXY_DECLARE(int) ap_proxy_lb_workers(void)
1820 {
1821     /* Set the dynamic #workers limit */
1822     lb_workers_limit = lb_workers + PROXY_DYNAMIC_BALANCER_LIMIT;
1823     return lb_workers_limit;
1824 }