1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* Utility routines for Apache proxy */ 18#include "mod_proxy.h" 19#include "ap_mpm.h" 20#include "scoreboard.h" 21#include "apr_version.h" 22 23#if APR_HAVE_UNISTD_H 24#include <unistd.h> /* for getpid() */ 25#endif 26 27#if (APR_MAJOR_VERSION < 1) 28#undef apr_socket_create 29#define apr_socket_create apr_socket_create_ex 30#endif 31 32/* 33 * Opaque structure containing target server info when 34 * using a forward proxy. 35 * Up to now only used in combination with HTTP CONNECT. 36 */ 37typedef struct { 38 int use_http_connect; /* Use SSL Tunneling via HTTP CONNECT */ 39 const char *target_host; /* Target hostname */ 40 apr_port_t target_port; /* Target port */ 41 const char *proxy_auth; /* Proxy authorization */ 42} forward_info; 43 44/* Global balancer counter */ 45int PROXY_DECLARE_DATA proxy_lb_workers = 0; 46static int lb_workers_limit = 0; 47 48static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r); 49static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r); 50static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r); 51static int proxy_match_word(struct dirconn_entry *This, request_rec *r); 52 53APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, create_req, 54 (request_rec *r, request_rec *pr), (r, pr), 55 OK, DECLINED) 56 57/* already called in the knowledge that the characters are hex digits */ 58PROXY_DECLARE(int) ap_proxy_hex2c(const char *x) 59{ 60 int i, ch; 61 62#if !APR_CHARSET_EBCDIC 63 ch = x[0]; 64 if (apr_isdigit(ch)) { 65 i = ch - '0'; 66 } 67 else if (apr_isupper(ch)) { 68 i = ch - ('A' - 10); 69 } 70 else { 71 i = ch - ('a' - 10); 72 } 73 i <<= 4; 74 75 ch = x[1]; 76 if (apr_isdigit(ch)) { 77 i += ch - '0'; 78 } 79 else if (apr_isupper(ch)) { 80 i += ch - ('A' - 10); 81 } 82 else { 83 i += ch - ('a' - 10); 84 } 85 return i; 86#else /*APR_CHARSET_EBCDIC*/ 87 /* 88 * we assume that the hex value refers to an ASCII character 89 * so convert to EBCDIC so that it makes sense locally; 90 * 91 * example: 92 * 93 * client specifies %20 in URL to refer to a space char; 94 * at this point we're called with EBCDIC "20"; after turning 95 * EBCDIC "20" into binary 0x20, we then need to assume that 0x20 96 * represents an ASCII char and convert 0x20 to EBCDIC, yielding 97 * 0x40 98 */ 99 char buf[1]; 100 101 if (1 == sscanf(x, "%2x", &i)) { 102 buf[0] = i & 0xFF; 103 ap_xlate_proto_from_ascii(buf, 1); 104 return buf[0]; 105 } 106 else { 107 return 0; 108 } 109#endif /*APR_CHARSET_EBCDIC*/ 110} 111 112PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x) 113{ 114#if !APR_CHARSET_EBCDIC 115 int i; 116 117 x[0] = '%'; 118 i = (ch & 0xF0) >> 4; 119 if (i >= 10) { 120 x[1] = ('A' - 10) + i; 121 } 122 else { 123 x[1] = '0' + i; 124 } 125 126 i = ch & 0x0F; 127 if (i >= 10) { 128 x[2] = ('A' - 10) + i; 129 } 130 else { 131 x[2] = '0' + i; 132 } 133#else /*APR_CHARSET_EBCDIC*/ 134 static const char ntoa[] = { "0123456789ABCDEF" }; 135 char buf[1]; 136 137 ch &= 0xFF; 138 139 buf[0] = ch; 140 ap_xlate_proto_to_ascii(buf, 1); 141 142 x[0] = '%'; 143 x[1] = ntoa[(buf[0] >> 4) & 0x0F]; 144 x[2] = ntoa[buf[0] & 0x0F]; 145 x[3] = '\0'; 146#endif /*APR_CHARSET_EBCDIC*/ 147} 148 149/* 150 * canonicalise a URL-encoded string 151 */ 152 153/* 154 * Convert a URL-encoded string to canonical form. 155 * It decodes characters which need not be encoded, 156 * and encodes those which must be encoded, and does not touch 157 * those which must not be touched. 158 */ 159PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, 160 enum enctype t, int forcedec, 161 int proxyreq) 162{ 163 int i, j, ch; 164 char *y; 165 char *allowed; /* characters which should not be encoded */ 166 char *reserved; /* characters which much not be en/de-coded */ 167 168/* 169 * N.B. in addition to :@&=, this allows ';' in an http path 170 * and '?' in an ftp path -- this may be revised 171 * 172 * Also, it makes a '+' character in a search string reserved, as 173 * it may be form-encoded. (Although RFC 1738 doesn't allow this - 174 * it only permits ; / ? : @ = & as reserved chars.) 175 */ 176 if (t == enc_path) { 177 allowed = "~$-_.+!*'(),;:@&="; 178 } 179 else if (t == enc_search) { 180 allowed = "$-_.!*'(),;:@&="; 181 } 182 else if (t == enc_user) { 183 allowed = "$-_.+!*'(),;@&="; 184 } 185 else if (t == enc_fpath) { 186 allowed = "$-_.+!*'(),?:@&="; 187 } 188 else { /* if (t == enc_parm) */ 189 allowed = "$-_.+!*'(),?/:@&="; 190 } 191 192 if (t == enc_path) { 193 reserved = "/"; 194 } 195 else if (t == enc_search) { 196 reserved = "+"; 197 } 198 else { 199 reserved = ""; 200 } 201 202 y = apr_palloc(p, 3 * len + 1); 203 204 for (i = 0, j = 0; i < len; i++, j++) { 205/* always handle '/' first */ 206 ch = x[i]; 207 if (strchr(reserved, ch)) { 208 y[j] = ch; 209 continue; 210 } 211/* 212 * decode it if not already done. do not decode reverse proxied URLs 213 * unless specifically forced 214 */ 215 if ((forcedec || (proxyreq && proxyreq != PROXYREQ_REVERSE)) && ch == '%') { 216 if (!apr_isxdigit(x[i + 1]) || !apr_isxdigit(x[i + 2])) { 217 return NULL; 218 } 219 ch = ap_proxy_hex2c(&x[i + 1]); 220 i += 2; 221 if (ch != 0 && strchr(reserved, ch)) { /* keep it encoded */ 222 ap_proxy_c2hex(ch, &y[j]); 223 j += 2; 224 continue; 225 } 226 } 227/* recode it, if necessary */ 228 if (!apr_isalnum(ch) && !strchr(allowed, ch)) { 229 ap_proxy_c2hex(ch, &y[j]); 230 j += 2; 231 } 232 else { 233 y[j] = ch; 234 } 235 } 236 y[j] = '\0'; 237 return y; 238} 239 240/* 241 * Parses network-location. 242 * urlp on input the URL; on output the path, after the leading / 243 * user NULL if no user/password permitted 244 * password holder for password 245 * host holder for host 246 * port port number; only set if one is supplied. 247 * 248 * Returns an error string. 249 */ 250PROXY_DECLARE(char *) 251 ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp, 252 char **passwordp, char **hostp, apr_port_t *port) 253{ 254 char *addr, *scope_id, *strp, *host, *url = *urlp; 255 char *user = NULL, *password = NULL; 256 apr_port_t tmp_port; 257 apr_status_t rv; 258 259 if (url[0] != '/' || url[1] != '/') { 260 return "Malformed URL"; 261 } 262 host = url + 2; 263 url = strchr(host, '/'); 264 if (url == NULL) { 265 url = ""; 266 } 267 else { 268 *(url++) = '\0'; /* skip seperating '/' */ 269 } 270 271 /* find _last_ '@' since it might occur in user/password part */ 272 strp = strrchr(host, '@'); 273 274 if (strp != NULL) { 275 *strp = '\0'; 276 user = host; 277 host = strp + 1; 278 279/* find password */ 280 strp = strchr(user, ':'); 281 if (strp != NULL) { 282 *strp = '\0'; 283 password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, 1, 0); 284 if (password == NULL) { 285 return "Bad %-escape in URL (password)"; 286 } 287 } 288 289 user = ap_proxy_canonenc(p, user, strlen(user), enc_user, 1, 0); 290 if (user == NULL) { 291 return "Bad %-escape in URL (username)"; 292 } 293 } 294 if (userp != NULL) { 295 *userp = user; 296 } 297 if (passwordp != NULL) { 298 *passwordp = password; 299 } 300 301 /* 302 * Parse the host string to separate host portion from optional port. 303 * Perform range checking on port. 304 */ 305 rv = apr_parse_addr_port(&addr, &scope_id, &tmp_port, host, p); 306 if (rv != APR_SUCCESS || addr == NULL || scope_id != NULL) { 307 return "Invalid host/port"; 308 } 309 if (tmp_port != 0) { /* only update caller's port if port was specified */ 310 *port = tmp_port; 311 } 312 313 ap_str_tolower(addr); /* DNS names are case-insensitive */ 314 315 *urlp = url; 316 *hostp = addr; 317 318 return NULL; 319} 320 321/* 322 * If the date is a valid RFC 850 date or asctime() date, then it 323 * is converted to the RFC 1123 format. 324 */ 325PROXY_DECLARE(const char *) 326 ap_proxy_date_canon(apr_pool_t *p, const char *date) 327{ 328 apr_status_t rv; 329 char* ndate; 330 331 apr_time_t time = apr_date_parse_http(date); 332 if (!time) { 333 return date; 334 } 335 336 ndate = apr_palloc(p, APR_RFC822_DATE_LEN); 337 rv = apr_rfc822_date(ndate, time); 338 if (rv != APR_SUCCESS) { 339 return date; 340 } 341 342 return ndate; 343} 344 345PROXY_DECLARE(request_rec *)ap_proxy_make_fake_req(conn_rec *c, request_rec *r) 346{ 347 request_rec *rp = apr_pcalloc(r->pool, sizeof(*r)); 348 349 rp->pool = r->pool; 350 rp->status = HTTP_OK; 351 352 rp->headers_in = apr_table_make(r->pool, 50); 353 rp->subprocess_env = apr_table_make(r->pool, 50); 354 rp->headers_out = apr_table_make(r->pool, 12); 355 rp->err_headers_out = apr_table_make(r->pool, 5); 356 rp->notes = apr_table_make(r->pool, 5); 357 358 rp->server = r->server; 359 rp->proxyreq = r->proxyreq; 360 rp->request_time = r->request_time; 361 rp->connection = c; 362 rp->output_filters = c->output_filters; 363 rp->input_filters = c->input_filters; 364 rp->proto_output_filters = c->output_filters; 365 rp->proto_input_filters = c->input_filters; 366 367 rp->request_config = ap_create_request_config(r->pool); 368 proxy_run_create_req(r, rp); 369 370 return rp; 371} 372 373 374/* 375 * list is a comma-separated list of case-insensitive tokens, with 376 * optional whitespace around the tokens. 377 * The return returns 1 if the token val is found in the list, or 0 378 * otherwise. 379 */ 380PROXY_DECLARE(int) ap_proxy_liststr(const char *list, const char *val) 381{ 382 int len, i; 383 const char *p; 384 385 len = strlen(val); 386 387 while (list != NULL) { 388 p = ap_strchr_c(list, ','); 389 if (p != NULL) { 390 i = p - list; 391 do { 392 p++; 393 } while (apr_isspace(*p)); 394 } 395 else { 396 i = strlen(list); 397 } 398 399 while (i > 0 && apr_isspace(list[i - 1])) { 400 i--; 401 } 402 if (i == len && strncasecmp(list, val, len) == 0) { 403 return 1; 404 } 405 list = p; 406 } 407 return 0; 408} 409 410/* 411 * list is a comma-separated list of case-insensitive tokens, with 412 * optional whitespace around the tokens. 413 * if val appears on the list of tokens, it is removed from the list, 414 * and the new list is returned. 415 */ 416PROXY_DECLARE(char *)ap_proxy_removestr(apr_pool_t *pool, const char *list, const char *val) 417{ 418 int len, i; 419 const char *p; 420 char *new = NULL; 421 422 len = strlen(val); 423 424 while (list != NULL) { 425 p = ap_strchr_c(list, ','); 426 if (p != NULL) { 427 i = p - list; 428 do { 429 p++; 430 } while (apr_isspace(*p)); 431 } 432 else { 433 i = strlen(list); 434 } 435 436 while (i > 0 && apr_isspace(list[i - 1])) { 437 i--; 438 } 439 if (i == len && strncasecmp(list, val, len) == 0) { 440 /* do nothing */ 441 } 442 else { 443 if (new) { 444 new = apr_pstrcat(pool, new, ",", apr_pstrndup(pool, list, i), NULL); 445 } 446 else { 447 new = apr_pstrndup(pool, list, i); 448 } 449 } 450 list = p; 451 } 452 return new; 453} 454 455/* 456 * Converts 8 hex digits to a time integer 457 */ 458PROXY_DECLARE(int) ap_proxy_hex2sec(const char *x) 459{ 460 int i, ch; 461 unsigned int j; 462 463 for (i = 0, j = 0; i < 8; i++) { 464 ch = x[i]; 465 j <<= 4; 466 if (apr_isdigit(ch)) { 467 j |= ch - '0'; 468 } 469 else if (apr_isupper(ch)) { 470 j |= ch - ('A' - 10); 471 } 472 else { 473 j |= ch - ('a' - 10); 474 } 475 } 476 if (j == 0xffffffff) { 477 return -1; /* so that it works with 8-byte ints */ 478 } 479 else { 480 return j; 481 } 482} 483 484/* 485 * Converts a time integer to 8 hex digits 486 */ 487PROXY_DECLARE(void) ap_proxy_sec2hex(int t, char *y) 488{ 489 int i, ch; 490 unsigned int j = t; 491 492 for (i = 7; i >= 0; i--) { 493 ch = j & 0xF; 494 j >>= 4; 495 if (ch >= 10) { 496 y[i] = ch + ('A' - 10); 497 } 498 else { 499 y[i] = ch + '0'; 500 } 501 } 502 y[8] = '\0'; 503} 504 505PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message) 506{ 507 apr_table_setn(r->notes, "error-notes", 508 apr_pstrcat(r->pool, 509 "The proxy server could not handle the request " 510 "<em><a href=\"", ap_escape_html(r->pool, r->uri), 511 "\">", ap_escape_html(r->pool, r->method), 512 " ", 513 ap_escape_html(r->pool, r->uri), "</a></em>.<p>\n" 514 "Reason: <strong>", 515 ap_escape_html(r->pool, message), 516 "</strong></p>", NULL)); 517 518 /* Allow "error-notes" string to be printed by ap_send_error_response() */ 519 apr_table_setn(r->notes, "verbose-error-to", apr_pstrdup(r->pool, "*")); 520 521 r->status_line = apr_psprintf(r->pool, "%3.3u Proxy Error", statuscode); 522 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 523 "proxy: %s returned by %s", message, r->uri); 524 return statuscode; 525} 526 527static const char * 528 proxy_get_host_of_request(request_rec *r) 529{ 530 char *url, *user = NULL, *password = NULL, *err, *host; 531 apr_port_t port; 532 533 if (r->hostname != NULL) { 534 return r->hostname; 535 } 536 537 /* Set url to the first char after "scheme://" */ 538 if ((url = strchr(r->uri, ':')) == NULL || url[1] != '/' || url[2] != '/') { 539 return NULL; 540 } 541 542 url = apr_pstrdup(r->pool, &url[1]); /* make it point to "//", which is what proxy_canon_netloc expects */ 543 544 err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port); 545 546 if (err != NULL) { 547 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "%s", err); 548 } 549 550 r->hostname = host; 551 552 return host; /* ought to return the port, too */ 553} 554 555/* Return TRUE if addr represents an IP address (or an IP network address) */ 556PROXY_DECLARE(int) ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p) 557{ 558 const char *addr = This->name; 559 long ip_addr[4]; 560 int i, quads; 561 long bits; 562 563 /* 564 * if the address is given with an explicit netmask, use that 565 * Due to a deficiency in apr_inet_addr(), it is impossible to parse 566 * "partial" addresses (with less than 4 quads) correctly, i.e. 567 * 192.168.123 is parsed as 192.168.0.123, which is not what I want. 568 * I therefore have to parse the IP address manually: 569 * if (proxy_readmask(This->name, &This->addr.s_addr, &This->mask.s_addr) == 0) 570 * addr and mask were set by proxy_readmask() 571 * return 1; 572 */ 573 574 /* 575 * Parse IP addr manually, optionally allowing 576 * abbreviated net addresses like 192.168. 577 */ 578 579 /* Iterate over up to 4 (dotted) quads. */ 580 for (quads = 0; quads < 4 && *addr != '\0'; ++quads) { 581 char *tmp; 582 583 if (*addr == '/' && quads > 0) { /* netmask starts here. */ 584 break; 585 } 586 587 if (!apr_isdigit(*addr)) { 588 return 0; /* no digit at start of quad */ 589 } 590 591 ip_addr[quads] = strtol(addr, &tmp, 0); 592 593 if (tmp == addr) { /* expected a digit, found something else */ 594 return 0; 595 } 596 597 if (ip_addr[quads] < 0 || ip_addr[quads] > 255) { 598 /* invalid octet */ 599 return 0; 600 } 601 602 addr = tmp; 603 604 if (*addr == '.' && quads != 3) { 605 ++addr; /* after the 4th quad, a dot would be illegal */ 606 } 607 } 608 609 for (This->addr.s_addr = 0, i = 0; i < quads; ++i) { 610 This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i)); 611 } 612 613 if (addr[0] == '/' && apr_isdigit(addr[1])) { /* net mask follows: */ 614 char *tmp; 615 616 ++addr; 617 618 bits = strtol(addr, &tmp, 0); 619 620 if (tmp == addr) { /* expected a digit, found something else */ 621 return 0; 622 } 623 624 addr = tmp; 625 626 if (bits < 0 || bits > 32) { /* netmask must be between 0 and 32 */ 627 return 0; 628 } 629 630 } 631 else { 632 /* 633 * Determine (i.e., "guess") netmask by counting the 634 * number of trailing .0's; reduce #quads appropriately 635 * (so that 192.168.0.0 is equivalent to 192.168.) 636 */ 637 while (quads > 0 && ip_addr[quads - 1] == 0) { 638 --quads; 639 } 640 641 /* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */ 642 if (quads < 1) { 643 return 0; 644 } 645 646 /* every zero-byte counts as 8 zero-bits */ 647 bits = 8 * quads; 648 649 if (bits != 32) { /* no warning for fully qualified IP address */ 650 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 651 "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld", 652 inet_ntoa(This->addr), bits); 653 } 654 } 655 656 This->mask.s_addr = htonl(APR_INADDR_NONE << (32 - bits)); 657 658 if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) { 659 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 660 "Warning: NetMask and IP-Addr disagree in %s/%ld", 661 inet_ntoa(This->addr), bits); 662 This->addr.s_addr &= This->mask.s_addr; 663 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 664 " Set to %s/%ld", inet_ntoa(This->addr), bits); 665 } 666 667 if (*addr == '\0') { 668 This->matcher = proxy_match_ipaddr; 669 return 1; 670 } 671 else { 672 return (*addr == '\0'); /* okay iff we've parsed the whole string */ 673 } 674} 675 676/* Return TRUE if addr represents an IP address (or an IP network address) */ 677static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r) 678{ 679 int i, ip_addr[4]; 680 struct in_addr addr, *ip; 681 const char *host = proxy_get_host_of_request(r); 682 683 if (host == NULL) { /* oops! */ 684 return 0; 685 } 686 687 memset(&addr, '\0', sizeof addr); 688 memset(ip_addr, '\0', sizeof ip_addr); 689 690 if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) { 691 for (addr.s_addr = 0, i = 0; i < 4; ++i) { 692 addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i)); 693 } 694 695 if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) { 696#if DEBUGGING 697 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 698 "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr)); 699 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 700 "%s/", inet_ntoa(This->addr)); 701 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 702 "%s", inet_ntoa(This->mask)); 703#endif 704 return 1; 705 } 706#if DEBUGGING 707 else { 708 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 709 "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr)); 710 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 711 "%s/", inet_ntoa(This->addr)); 712 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 713 "%s", inet_ntoa(This->mask)); 714 } 715#endif 716 } 717 else { 718 struct apr_sockaddr_t *reqaddr; 719 720 if (apr_sockaddr_info_get(&reqaddr, host, APR_UNSPEC, 0, 0, r->pool) 721 != APR_SUCCESS) { 722#if DEBUGGING 723 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 724 "2)IP-NoMatch: hostname=%s msg=Host not found", host); 725#endif 726 return 0; 727 } 728 729 /* Try to deal with multiple IP addr's for a host */ 730 /* FIXME: This needs to be able to deal with IPv6 */ 731 while (reqaddr) { 732 ip = (struct in_addr *) reqaddr->ipaddr_ptr; 733 if (This->addr.s_addr == (ip->s_addr & This->mask.s_addr)) { 734#if DEBUGGING 735 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 736 "3)IP-Match: %s[%s] <-> ", host, inet_ntoa(*ip)); 737 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 738 "%s/", inet_ntoa(This->addr)); 739 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 740 "%s", inet_ntoa(This->mask)); 741#endif 742 return 1; 743 } 744#if DEBUGGING 745 else { 746 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 747 "3)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(*ip)); 748 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 749 "%s/", inet_ntoa(This->addr)); 750 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 751 "%s", inet_ntoa(This->mask)); 752 } 753#endif 754 reqaddr = reqaddr->next; 755 } 756 } 757 758 return 0; 759} 760 761/* Return TRUE if addr represents a domain name */ 762PROXY_DECLARE(int) ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p) 763{ 764 char *addr = This->name; 765 int i; 766 767 /* Domain name must start with a '.' */ 768 if (addr[0] != '.') { 769 return 0; 770 } 771 772 /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */ 773 for (i = 0; apr_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i) { 774 continue; 775 } 776 777#if 0 778 if (addr[i] == ':') { 779 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 780 "@@@@ handle optional port in proxy_is_domainname()"); 781 /* @@@@ handle optional port */ 782 } 783#endif 784 785 if (addr[i] != '\0') { 786 return 0; 787 } 788 789 /* Strip trailing dots */ 790 for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i) { 791 addr[i] = '\0'; 792 } 793 794 This->matcher = proxy_match_domainname; 795 return 1; 796} 797 798/* Return TRUE if host "host" is in domain "domain" */ 799static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r) 800{ 801 const char *host = proxy_get_host_of_request(r); 802 int d_len = strlen(This->name), h_len; 803 804 if (host == NULL) { /* some error was logged already */ 805 return 0; 806 } 807 808 h_len = strlen(host); 809 810 /* @@@ do this within the setup? */ 811 /* Ignore trailing dots in domain comparison: */ 812 while (d_len > 0 && This->name[d_len - 1] == '.') { 813 --d_len; 814 } 815 while (h_len > 0 && host[h_len - 1] == '.') { 816 --h_len; 817 } 818 return h_len > d_len 819 && strncasecmp(&host[h_len - d_len], This->name, d_len) == 0; 820} 821 822/* Return TRUE if host represents a host name */ 823PROXY_DECLARE(int) ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p) 824{ 825 struct apr_sockaddr_t *addr; 826 char *host = This->name; 827 int i; 828 829 /* Host names must not start with a '.' */ 830 if (host[0] == '.') { 831 return 0; 832 } 833 /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */ 834 for (i = 0; apr_isalnum(host[i]) || host[i] == '-' || host[i] == '.'; ++i); 835 836 if (host[i] != '\0' || apr_sockaddr_info_get(&addr, host, APR_UNSPEC, 0, 0, p) != APR_SUCCESS) { 837 return 0; 838 } 839 840 This->hostaddr = addr; 841 842 /* Strip trailing dots */ 843 for (i = strlen(host) - 1; i > 0 && host[i] == '.'; --i) { 844 host[i] = '\0'; 845 } 846 847 This->matcher = proxy_match_hostname; 848 return 1; 849} 850 851/* Return TRUE if host "host" is equal to host2 "host2" */ 852static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r) 853{ 854 char *host = This->name; 855 const char *host2 = proxy_get_host_of_request(r); 856 int h2_len; 857 int h1_len; 858 859 if (host == NULL || host2 == NULL) { 860 return 0; /* oops! */ 861 } 862 863 h2_len = strlen(host2); 864 h1_len = strlen(host); 865 866#if 0 867 struct apr_sockaddr_t *addr = *This->hostaddr; 868 869 /* Try to deal with multiple IP addr's for a host */ 870 while (addr) { 871 if (addr->ipaddr_ptr == ? ? ? ? ? ? ? ? ? ? ? ? ?) 872 return 1; 873 addr = addr->next; 874 } 875#endif 876 877 /* Ignore trailing dots in host2 comparison: */ 878 while (h2_len > 0 && host2[h2_len - 1] == '.') { 879 --h2_len; 880 } 881 while (h1_len > 0 && host[h1_len - 1] == '.') { 882 --h1_len; 883 } 884 return h1_len == h2_len 885 && strncasecmp(host, host2, h1_len) == 0; 886} 887 888/* Return TRUE if addr is to be matched as a word */ 889PROXY_DECLARE(int) ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p) 890{ 891 This->matcher = proxy_match_word; 892 return 1; 893} 894 895/* Return TRUE if string "str2" occurs literally in "str1" */ 896static int proxy_match_word(struct dirconn_entry *This, request_rec *r) 897{ 898 const char *host = proxy_get_host_of_request(r); 899 return host != NULL && ap_strstr_c(host, This->name) != NULL; 900} 901 902/* checks whether a host in uri_addr matches proxyblock */ 903PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf, 904 apr_sockaddr_t *uri_addr) 905{ 906 int j; 907 apr_sockaddr_t * src_uri_addr = uri_addr; 908 /* XXX FIXME: conf->noproxies->elts is part of an opaque structure */ 909 for (j = 0; j < conf->noproxies->nelts; j++) { 910 struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts; 911 struct apr_sockaddr_t *conf_addr = npent[j].addr; 912 uri_addr = src_uri_addr; 913 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, 914 "proxy: checking remote machine [%s] against [%s]", uri_addr->hostname, npent[j].name); 915 if ((npent[j].name && ap_strstr_c(uri_addr->hostname, npent[j].name)) 916 || npent[j].name[0] == '*') { 917 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, 918 "proxy: connect to remote machine %s blocked: name %s matched", uri_addr->hostname, npent[j].name); 919 return HTTP_FORBIDDEN; 920 } 921 while (conf_addr) { 922 uri_addr = src_uri_addr; 923 while (uri_addr) { 924 char *conf_ip; 925 char *uri_ip; 926 apr_sockaddr_ip_get(&conf_ip, conf_addr); 927 apr_sockaddr_ip_get(&uri_ip, uri_addr); 928 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, 929 "proxy: ProxyBlock comparing %s and %s", conf_ip, uri_ip); 930 if (!apr_strnatcasecmp(conf_ip, uri_ip)) { 931 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, 932 "proxy: connect to remote machine %s blocked: IP %s matched", uri_addr->hostname, conf_ip); 933 return HTTP_FORBIDDEN; 934 } 935 uri_addr = uri_addr->next; 936 } 937 conf_addr = conf_addr->next; 938 } 939 } 940 return OK; 941} 942 943/* set up the minimal filter set */ 944PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r) 945{ 946 ap_add_input_filter("HTTP_IN", NULL, r, c); 947 return OK; 948} 949 950/* 951 * converts a series of buckets into a string 952 * XXX: BillS says this function performs essentially the same function as 953 * ap_rgetline() in protocol.c. Deprecate this function and use ap_rgetline() 954 * instead? I think ap_proxy_string_read() will not work properly on non ASCII 955 * (EBCDIC) machines either. 956 */ 957PROXY_DECLARE(apr_status_t) ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb, 958 char *buff, apr_size_t bufflen, int *eos) 959{ 960 apr_bucket *e; 961 apr_status_t rv; 962 char *pos = buff; 963 char *response; 964 int found = 0; 965 apr_size_t len; 966 967 /* start with an empty string */ 968 buff[0] = 0; 969 *eos = 0; 970 971 /* loop through each brigade */ 972 while (!found) { 973 /* get brigade from network one line at a time */ 974 if (APR_SUCCESS != (rv = ap_get_brigade(c->input_filters, bb, 975 AP_MODE_GETLINE, 976 APR_BLOCK_READ, 977 0))) { 978 return rv; 979 } 980 /* loop through each bucket */ 981 while (!found) { 982 if (*eos || APR_BRIGADE_EMPTY(bb)) { 983 /* The connection aborted or timed out */ 984 return APR_ECONNABORTED; 985 } 986 e = APR_BRIGADE_FIRST(bb); 987 if (APR_BUCKET_IS_EOS(e)) { 988 *eos = 1; 989 } 990 else { 991 if (APR_SUCCESS != (rv = apr_bucket_read(e, 992 (const char **)&response, 993 &len, 994 APR_BLOCK_READ))) { 995 return rv; 996 } 997 /* 998 * is string LF terminated? 999 * XXX: This check can be made more efficient by simply checking 1000 * if the last character in the 'response' buffer is an ASCII_LF. 1001 * See ap_rgetline() for an example. 1002 */ 1003 if (memchr(response, APR_ASCII_LF, len)) { 1004 found = 1; 1005 } 1006 /* concat strings until buff is full - then throw the data away */ 1007 if (len > ((bufflen-1)-(pos-buff))) { 1008 len = (bufflen-1)-(pos-buff); 1009 } 1010 if (len > 0) { 1011 memcpy(pos, response, len); 1012 pos += len; 1013 } 1014 } 1015 APR_BUCKET_REMOVE(e); 1016 apr_bucket_destroy(e); 1017 } 1018 *pos = '\0'; 1019 } 1020 1021 return APR_SUCCESS; 1022} 1023 1024/* unmerge an element in the table */ 1025PROXY_DECLARE(void) ap_proxy_table_unmerge(apr_pool_t *p, apr_table_t *t, char *key) 1026{ 1027 apr_off_t offset = 0; 1028 apr_off_t count = 0; 1029 char *value = NULL; 1030 1031 /* get the value to unmerge */ 1032 const char *initial = apr_table_get(t, key); 1033 if (!initial) { 1034 return; 1035 } 1036 value = apr_pstrdup(p, initial); 1037 1038 /* remove the value from the headers */ 1039 apr_table_unset(t, key); 1040 1041 /* find each comma */ 1042 while (value[count]) { 1043 if (value[count] == ',') { 1044 value[count] = 0; 1045 apr_table_add(t, key, value + offset); 1046 offset = count + 1; 1047 } 1048 count++; 1049 } 1050 apr_table_add(t, key, value + offset); 1051} 1052 1053PROXY_DECLARE(const char *) ap_proxy_location_reverse_map(request_rec *r, 1054 proxy_dir_conf *conf, const char *url) 1055{ 1056 proxy_req_conf *rconf; 1057 struct proxy_alias *ent; 1058 int i, l1, l2; 1059 char *u; 1060 1061 /* 1062 * XXX FIXME: Make sure this handled the ambiguous case of the :<PORT> 1063 * after the hostname 1064 * XXX FIXME: Ensure the /uri component is a case sensitive match 1065 */ 1066 if (r->proxyreq != PROXYREQ_REVERSE) { 1067 return url; 1068 } 1069 1070 l1 = strlen(url); 1071 if (conf->interpolate_env == 1) { 1072 rconf = ap_get_module_config(r->request_config, &proxy_module); 1073 ent = (struct proxy_alias *)rconf->raliases->elts; 1074 } 1075 else { 1076 ent = (struct proxy_alias *)conf->raliases->elts; 1077 } 1078 for (i = 0; i < conf->raliases->nelts; i++) { 1079 proxy_server_conf *sconf = (proxy_server_conf *) 1080 ap_get_module_config(r->server->module_config, &proxy_module); 1081 proxy_balancer *balancer; 1082 const char *real = ent[i].real; 1083 /* 1084 * First check if mapping against a balancer and see 1085 * if we have such a entity. If so, then we need to 1086 * find the particulars of the actual worker which may 1087 * or may not be the right one... basically, we need 1088 * to find which member actually handled this request. 1089 */ 1090 if ((strncasecmp(real, "balancer://", 11) == 0) && 1091 (balancer = ap_proxy_get_balancer(r->pool, sconf, real))) { 1092 int n, l3 = 0; 1093 proxy_worker *worker = (proxy_worker *)balancer->workers->elts; 1094 const char *urlpart = ap_strchr_c(real + 11, '/'); 1095 if (urlpart) { 1096 if (!urlpart[1]) 1097 urlpart = NULL; 1098 else 1099 l3 = strlen(urlpart); 1100 } 1101 /* The balancer comparison is a bit trickier. Given the context 1102 * BalancerMember balancer://alias http://example.com/foo 1103 * ProxyPassReverse /bash balancer://alias/bar 1104 * translate url http://example.com/foo/bar/that to /bash/that 1105 */ 1106 for (n = 0; n < balancer->workers->nelts; n++) { 1107 l2 = strlen(worker->name); 1108 if (urlpart) { 1109 /* urlpart (l3) assuredly starts with its own '/' */ 1110 if (worker->name[l2 - 1] == '/') 1111 --l2; 1112 if (l1 >= l2 + l3 1113 && strncasecmp(worker->name, url, l2) == 0 1114 && strncmp(urlpart, url + l2, l3) == 0) { 1115 u = apr_pstrcat(r->pool, ent[i].fake, &url[l2 + l3], 1116 NULL); 1117 return ap_construct_url(r->pool, u, r); 1118 } 1119 } 1120 else if (l1 >= l2 && strncasecmp(worker->name, url, l2) == 0) { 1121 /* edge case where fake is just "/"... avoid double slash */ 1122 if ((ent[i].fake[0] == '/') && (ent[i].fake[1] == 0) && (url[l2] == '/')) { 1123 u = apr_pstrdup(r->pool, &url[l2]); 1124 } else { 1125 u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL); 1126 } 1127 return ap_construct_url(r->pool, u, r); 1128 } 1129 worker++; 1130 } 1131 } 1132 else { 1133 l2 = strlen(real); 1134 if (l1 >= l2 && strncasecmp(real, url, l2) == 0) { 1135 u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL); 1136 return ap_construct_url(r->pool, u, r); 1137 } 1138 } 1139 } 1140 1141 return url; 1142} 1143 1144/* 1145 * Cookies are a bit trickier to match: we've got two substrings to worry 1146 * about, and we can't just find them with strstr 'cos of case. Regexp 1147 * matching would be an easy fix, but for better consistency with all the 1148 * other matches we'll refrain and use apr_strmatch to find path=/domain= 1149 * and stick to plain strings for the config values. 1150 */ 1151PROXY_DECLARE(const char *) ap_proxy_cookie_reverse_map(request_rec *r, 1152 proxy_dir_conf *conf, const char *str) 1153{ 1154 proxy_req_conf *rconf = ap_get_module_config(r->request_config, 1155 &proxy_module); 1156 struct proxy_alias *ent; 1157 apr_size_t len = strlen(str); 1158 const char *newpath = NULL; 1159 const char *newdomain = NULL; 1160 const char *pathp; 1161 const char *domainp; 1162 const char *pathe = NULL; 1163 const char *domaine = NULL; 1164 apr_size_t l1, l2, poffs = 0, doffs = 0; 1165 int i; 1166 int ddiff = 0; 1167 int pdiff = 0; 1168 char *ret; 1169 1170 if (r->proxyreq != PROXYREQ_REVERSE) { 1171 return str; 1172 } 1173 1174 /* 1175 * Find the match and replacement, but save replacing until we've done 1176 * both path and domain so we know the new strlen 1177 */ 1178 if ((pathp = apr_strmatch(conf->cookie_path_str, str, len)) != NULL) { 1179 pathp += 5; 1180 poffs = pathp - str; 1181 pathe = ap_strchr_c(pathp, ';'); 1182 l1 = pathe ? (pathe - pathp) : strlen(pathp); 1183 pathe = pathp + l1 ; 1184 if (conf->interpolate_env == 1) { 1185 ent = (struct proxy_alias *)rconf->cookie_paths->elts; 1186 } 1187 else { 1188 ent = (struct proxy_alias *)conf->cookie_paths->elts; 1189 } 1190 for (i = 0; i < conf->cookie_paths->nelts; i++) { 1191 l2 = strlen(ent[i].fake); 1192 if (l1 >= l2 && strncmp(ent[i].fake, pathp, l2) == 0) { 1193 newpath = ent[i].real; 1194 pdiff = strlen(newpath) - l1; 1195 break; 1196 } 1197 } 1198 } 1199 1200 if ((domainp = apr_strmatch(conf->cookie_domain_str, str, len)) != NULL) { 1201 domainp += 7; 1202 doffs = domainp - str; 1203 domaine = ap_strchr_c(domainp, ';'); 1204 l1 = domaine ? (domaine - domainp) : strlen(domainp); 1205 domaine = domainp + l1; 1206 if (conf->interpolate_env == 1) { 1207 ent = (struct proxy_alias *)rconf->cookie_domains->elts; 1208 } 1209 else { 1210 ent = (struct proxy_alias *)conf->cookie_domains->elts; 1211 } 1212 for (i = 0; i < conf->cookie_domains->nelts; i++) { 1213 l2 = strlen(ent[i].fake); 1214 if (l1 >= l2 && strncasecmp(ent[i].fake, domainp, l2) == 0) { 1215 newdomain = ent[i].real; 1216 ddiff = strlen(newdomain) - l1; 1217 break; 1218 } 1219 } 1220 } 1221 1222 if (newpath) { 1223 ret = apr_palloc(r->pool, len + pdiff + ddiff + 1); 1224 l1 = strlen(newpath); 1225 if (newdomain) { 1226 l2 = strlen(newdomain); 1227 if (doffs > poffs) { 1228 memcpy(ret, str, poffs); 1229 memcpy(ret + poffs, newpath, l1); 1230 memcpy(ret + poffs + l1, pathe, domainp - pathe); 1231 memcpy(ret + doffs + pdiff, newdomain, l2); 1232 strcpy(ret + doffs + pdiff + l2, domaine); 1233 } 1234 else { 1235 memcpy(ret, str, doffs) ; 1236 memcpy(ret + doffs, newdomain, l2); 1237 memcpy(ret + doffs + l2, domaine, pathp - domaine); 1238 memcpy(ret + poffs + ddiff, newpath, l1); 1239 strcpy(ret + poffs + ddiff + l1, pathe); 1240 } 1241 } 1242 else { 1243 memcpy(ret, str, poffs); 1244 memcpy(ret + poffs, newpath, l1); 1245 strcpy(ret + poffs + l1, pathe); 1246 } 1247 } 1248 else { 1249 if (newdomain) { 1250 ret = apr_palloc(r->pool, len + pdiff + ddiff + 1); 1251 l2 = strlen(newdomain); 1252 memcpy(ret, str, doffs); 1253 memcpy(ret + doffs, newdomain, l2); 1254 strcpy(ret + doffs+l2, domaine); 1255 } 1256 else { 1257 ret = (char *)str; /* no change */ 1258 } 1259 } 1260 1261 return ret; 1262} 1263 1264PROXY_DECLARE(proxy_balancer *) ap_proxy_get_balancer(apr_pool_t *p, 1265 proxy_server_conf *conf, 1266 const char *url) 1267{ 1268 proxy_balancer *balancer; 1269 char *c, *uri = apr_pstrdup(p, url); 1270 int i; 1271 1272 c = strchr(uri, ':'); 1273 if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') { 1274 return NULL; 1275 } 1276 /* remove path from uri */ 1277 if ((c = strchr(c + 3, '/'))) { 1278 *c = '\0'; 1279 } 1280 balancer = (proxy_balancer *)conf->balancers->elts; 1281 for (i = 0; i < conf->balancers->nelts; i++) { 1282 if (strcasecmp(balancer->name, uri) == 0) { 1283 return balancer; 1284 } 1285 balancer++; 1286 } 1287 return NULL; 1288} 1289 1290PROXY_DECLARE(const char *) ap_proxy_add_balancer(proxy_balancer **balancer, 1291 apr_pool_t *p, 1292 proxy_server_conf *conf, 1293 const char *url) 1294{ 1295 char *c, *q, *uri = apr_pstrdup(p, url); 1296 proxy_balancer_method *lbmethod; 1297 1298 c = strchr(uri, ':'); 1299 if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') 1300 return "Bad syntax for a balancer name"; 1301 /* remove path from uri */ 1302 if ((q = strchr(c + 3, '/'))) 1303 *q = '\0'; 1304 1305 ap_str_tolower(uri); 1306 *balancer = apr_array_push(conf->balancers); 1307 memset(*balancer, 0, sizeof(proxy_balancer)); 1308 1309 /* 1310 * NOTE: The default method is byrequests, which we assume 1311 * exists! 1312 */ 1313 lbmethod = ap_lookup_provider(PROXY_LBMETHOD, "byrequests", "0"); 1314 if (!lbmethod) { 1315 return "Can't find 'byrequests' lb method"; 1316 } 1317 1318 (*balancer)->name = uri; 1319 (*balancer)->lbmethod = lbmethod; 1320 (*balancer)->workers = apr_array_make(p, 5, sizeof(proxy_worker)); 1321 1322 (*balancer)->forcerecovery = 1; 1323 /* XXX Is this a right place to create mutex */ 1324#if APR_HAS_THREADS 1325 if (apr_thread_mutex_create(&((*balancer)->mutex), 1326 APR_THREAD_MUTEX_DEFAULT, p) != APR_SUCCESS) { 1327 /* XXX: Do we need to log something here */ 1328 return "can not create thread mutex"; 1329 } 1330#endif 1331 1332 return NULL; 1333} 1334 1335PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p, 1336 proxy_server_conf *conf, 1337 const char *url) 1338{ 1339 proxy_worker *worker; 1340 proxy_worker *max_worker = NULL; 1341 int max_match = 0; 1342 int url_length; 1343 int min_match; 1344 int worker_name_length; 1345 const char *c; 1346 char *url_copy; 1347 int i; 1348 1349 c = ap_strchr_c(url, ':'); 1350 if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') { 1351 return NULL; 1352 } 1353 1354 url_copy = apr_pstrdup(p, url); 1355 url_length = strlen(url); 1356 1357 /* 1358 * We need to find the start of the path and 1359 * therefore we know the length of the scheme://hostname/ 1360 * part to we can force-lowercase everything up to 1361 * the start of the path. 1362 */ 1363 c = ap_strchr_c(c+3, '/'); 1364 if (c) { 1365 char *pathstart; 1366 pathstart = url_copy + (c - url); 1367 *pathstart = '\0'; 1368 ap_str_tolower(url_copy); 1369 min_match = strlen(url_copy); 1370 *pathstart = '/'; 1371 } 1372 else { 1373 ap_str_tolower(url_copy); 1374 min_match = strlen(url_copy); 1375 } 1376 1377 worker = (proxy_worker *)conf->workers->elts; 1378 1379 /* 1380 * Do a "longest match" on the worker name to find the worker that 1381 * fits best to the URL, but keep in mind that we must have at least 1382 * a minimum matching of length min_match such that 1383 * scheme://hostname[:port] matches between worker and url. 1384 */ 1385 for (i = 0; i < conf->workers->nelts; i++) { 1386 if ( ((worker_name_length = strlen(worker->name)) <= url_length) 1387 && (worker_name_length >= min_match) 1388 && (worker_name_length > max_match) 1389 && (strncmp(url_copy, worker->name, worker_name_length) == 0) ) { 1390 max_worker = worker; 1391 max_match = worker_name_length; 1392 } 1393 worker++; 1394 } 1395 return max_worker; 1396} 1397 1398#if APR_HAS_THREADS 1399static apr_status_t conn_pool_cleanup(void *theworker) 1400{ 1401 proxy_worker *worker = (proxy_worker *)theworker; 1402 if (worker->cp->res) { 1403 worker->cp->pool = NULL; 1404 } 1405 return APR_SUCCESS; 1406} 1407#endif 1408 1409static void init_conn_pool(apr_pool_t *p, proxy_worker *worker) 1410{ 1411 apr_pool_t *pool; 1412 proxy_conn_pool *cp; 1413 1414 /* 1415 * Create a connection pool's subpool. 1416 * This pool is used for connection recycling. 1417 * Once the worker is added it is never removed but 1418 * it can be disabled. 1419 */ 1420 apr_pool_create(&pool, p); 1421 apr_pool_tag(pool, "proxy_worker_cp"); 1422 /* 1423 * Alloc from the same pool as worker. 1424 * proxy_conn_pool is permanently attached to the worker. 1425 */ 1426 cp = (proxy_conn_pool *)apr_pcalloc(p, sizeof(proxy_conn_pool)); 1427 cp->pool = pool; 1428 worker->cp = cp; 1429} 1430 1431PROXY_DECLARE(const char *) ap_proxy_add_worker(proxy_worker **worker, 1432 apr_pool_t *p, 1433 proxy_server_conf *conf, 1434 const char *url) 1435{ 1436 int rv; 1437 apr_uri_t uri; 1438 1439 rv = apr_uri_parse(p, url, &uri); 1440 1441 if (rv != APR_SUCCESS) { 1442 return "Unable to parse URL"; 1443 } 1444 if (!uri.hostname || !uri.scheme) { 1445 return "URL must be absolute!"; 1446 } 1447 1448 ap_str_tolower(uri.hostname); 1449 ap_str_tolower(uri.scheme); 1450 *worker = apr_array_push(conf->workers); 1451 memset(*worker, 0, sizeof(proxy_worker)); 1452 (*worker)->name = apr_uri_unparse(p, &uri, APR_URI_UNP_REVEALPASSWORD); 1453 (*worker)->scheme = uri.scheme; 1454 (*worker)->hostname = uri.hostname; 1455 (*worker)->port = uri.port; 1456 (*worker)->id = proxy_lb_workers; 1457 (*worker)->flush_packets = flush_off; 1458 (*worker)->flush_wait = PROXY_FLUSH_WAIT; 1459 (*worker)->smax = -1; 1460 /* Increase the total worker count */ 1461 proxy_lb_workers++; 1462 init_conn_pool(p, *worker); 1463#if APR_HAS_THREADS 1464 if (apr_thread_mutex_create(&((*worker)->mutex), 1465 APR_THREAD_MUTEX_DEFAULT, p) != APR_SUCCESS) { 1466 /* XXX: Do we need to log something here */ 1467 return "can not create thread mutex"; 1468 } 1469#endif 1470 1471 return NULL; 1472} 1473 1474PROXY_DECLARE(proxy_worker *) ap_proxy_create_worker(apr_pool_t *p) 1475{ 1476 1477 proxy_worker *worker; 1478 worker = (proxy_worker *)apr_pcalloc(p, sizeof(proxy_worker)); 1479 worker->id = proxy_lb_workers; 1480 worker->smax = -1; 1481 /* Increase the total worker count */ 1482 proxy_lb_workers++; 1483 init_conn_pool(p, worker); 1484 1485 return worker; 1486} 1487 1488PROXY_DECLARE(void) 1489ap_proxy_add_worker_to_balancer(apr_pool_t *pool, proxy_balancer *balancer, 1490 proxy_worker *worker) 1491{ 1492 proxy_worker *runtime; 1493 1494 runtime = apr_array_push(balancer->workers); 1495 memcpy(runtime, worker, sizeof(proxy_worker)); 1496 runtime->id = proxy_lb_workers; 1497 /* Increase the total runtime count */ 1498 proxy_lb_workers++; 1499 1500} 1501 1502PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker, 1503 proxy_balancer **balancer, 1504 request_rec *r, 1505 proxy_server_conf *conf, char **url) 1506{ 1507 int access_status; 1508 1509 access_status = proxy_run_pre_request(worker, balancer, r, conf, url); 1510 if (access_status == DECLINED && *balancer == NULL) { 1511 *worker = ap_proxy_get_worker(r->pool, conf, *url); 1512 if (*worker) { 1513 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, 1514 "proxy: %s: found worker %s for %s", 1515 (*worker)->scheme, (*worker)->name, *url); 1516 1517 *balancer = NULL; 1518 access_status = OK; 1519 } 1520 else if (r->proxyreq == PROXYREQ_PROXY) { 1521 if (conf->forward) { 1522 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, 1523 "proxy: *: found forward proxy worker for %s", 1524 *url); 1525 *balancer = NULL; 1526 *worker = conf->forward; 1527 access_status = OK; 1528 } 1529 } 1530 else if (r->proxyreq == PROXYREQ_REVERSE) { 1531 if (conf->reverse) { 1532 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, 1533 "proxy: *: found reverse proxy worker for %s", 1534 *url); 1535 *balancer = NULL; 1536 *worker = conf->reverse; 1537 access_status = OK; 1538 } 1539 } 1540 } 1541 else if (access_status == DECLINED && *balancer != NULL) { 1542 /* All the workers are busy */ 1543 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 1544 "proxy: all workers are busy. Unable to serve %s", 1545 *url); 1546 access_status = HTTP_SERVICE_UNAVAILABLE; 1547 } 1548 return access_status; 1549} 1550 1551PROXY_DECLARE(int) ap_proxy_post_request(proxy_worker *worker, 1552 proxy_balancer *balancer, 1553 request_rec *r, 1554 proxy_server_conf *conf) 1555{ 1556 int access_status; 1557 if (balancer) { 1558 access_status = proxy_run_post_request(worker, balancer, r, conf); 1559 } 1560 else { 1561 access_status = OK; 1562 } 1563 1564 return access_status; 1565} 1566 1567/* DEPRECATED */ 1568PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **newsock, 1569 const char *proxy_function, 1570 apr_sockaddr_t *backend_addr, 1571 const char *backend_name, 1572 proxy_server_conf *conf, 1573 server_rec *s, 1574 apr_pool_t *p) 1575{ 1576 apr_status_t rv; 1577 int connected = 0; 1578 int loglevel; 1579 1580 while (backend_addr && !connected) { 1581 if ((rv = apr_socket_create(newsock, backend_addr->family, 1582 SOCK_STREAM, 0, p)) != APR_SUCCESS) { 1583 loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR; 1584 ap_log_error(APLOG_MARK, loglevel, rv, s, 1585 "proxy: %s: error creating fam %d socket for target %s", 1586 proxy_function, 1587 backend_addr->family, 1588 backend_name); 1589 /* 1590 * this could be an IPv6 address from the DNS but the 1591 * local machine won't give us an IPv6 socket; hopefully the 1592 * DNS returned an additional address to try 1593 */ 1594 backend_addr = backend_addr->next; 1595 continue; 1596 } 1597 1598#if !defined(TPF) && !defined(BEOS) 1599 if (conf->recv_buffer_size > 0 && 1600 (rv = apr_socket_opt_set(*newsock, APR_SO_RCVBUF, 1601 conf->recv_buffer_size))) { 1602 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, 1603 "apr_socket_opt_set(SO_RCVBUF): Failed to set " 1604 "ProxyReceiveBufferSize, using default"); 1605 } 1606#endif 1607 1608 rv = apr_socket_opt_set(*newsock, APR_TCP_NODELAY, 1); 1609 if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) { 1610 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, 1611 "apr_socket_opt_set(APR_TCP_NODELAY): " 1612 "Failed to set"); 1613 } 1614 1615 /* Set a timeout on the socket */ 1616 if (conf->timeout_set == 1) { 1617 apr_socket_timeout_set(*newsock, conf->timeout); 1618 } 1619 else { 1620 apr_socket_timeout_set(*newsock, s->timeout); 1621 } 1622 1623 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 1624 "proxy: %s: fam %d socket created to connect to %s", 1625 proxy_function, backend_addr->family, backend_name); 1626 1627 /* make the connection out of the socket */ 1628 rv = apr_socket_connect(*newsock, backend_addr); 1629 1630 /* if an error occurred, loop round and try again */ 1631 if (rv != APR_SUCCESS) { 1632 apr_socket_close(*newsock); 1633 loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR; 1634 ap_log_error(APLOG_MARK, loglevel, rv, s, 1635 "proxy: %s: attempt to connect to %pI (%s) failed", 1636 proxy_function, 1637 backend_addr, 1638 backend_name); 1639 backend_addr = backend_addr->next; 1640 continue; 1641 } 1642 connected = 1; 1643 } 1644 return connected ? 0 : 1; 1645} 1646 1647static apr_status_t connection_cleanup(void *theconn) 1648{ 1649 proxy_conn_rec *conn = (proxy_conn_rec *)theconn; 1650 proxy_worker *worker = conn->worker; 1651 1652 /* 1653 * If the connection pool is NULL the worker 1654 * cleanup has been run. Just return. 1655 */ 1656 if (!worker->cp) { 1657 return APR_SUCCESS; 1658 } 1659 1660#if APR_HAS_THREADS 1661 /* Sanity check: Did we already return the pooled connection? */ 1662 if (conn->inreslist) { 1663 ap_log_perror(APLOG_MARK, APLOG_ERR, 0, conn->pool, 1664 "proxy: Pooled connection 0x%pp for worker %s has been" 1665 " already returned to the connection pool.", conn, 1666 worker->name); 1667 return APR_SUCCESS; 1668 } 1669#endif 1670 1671 /* determine if the connection need to be closed */ 1672 if (conn->close_on_recycle || conn->close || worker->disablereuse || 1673 !worker->is_address_reusable) { 1674 apr_pool_t *p = conn->pool; 1675 apr_pool_clear(p); 1676 conn = apr_pcalloc(p, sizeof(proxy_conn_rec)); 1677 conn->pool = p; 1678 conn->worker = worker; 1679 apr_pool_create(&(conn->scpool), p); 1680 apr_pool_tag(conn->scpool, "proxy_conn_scpool"); 1681 } 1682#if APR_HAS_THREADS 1683 if (worker->hmax && worker->cp->res) { 1684 conn->inreslist = 1; 1685 apr_reslist_release(worker->cp->res, (void *)conn); 1686 } 1687 else 1688#endif 1689 { 1690 worker->cp->conn = conn; 1691 } 1692 1693 /* Always return the SUCCESS */ 1694 return APR_SUCCESS; 1695} 1696 1697static void socket_cleanup(proxy_conn_rec *conn) 1698{ 1699 conn->sock = NULL; 1700 conn->connection = NULL; 1701 apr_pool_clear(conn->scpool); 1702} 1703 1704PROXY_DECLARE(apr_status_t) ap_proxy_ssl_connection_cleanup(proxy_conn_rec *conn, 1705 request_rec *r) 1706{ 1707 apr_bucket_brigade *bb; 1708 apr_status_t rv; 1709 1710 /* 1711 * If we have an existing SSL connection it might be possible that the 1712 * server sent some SSL message we have not read so far (e.g. a SSL 1713 * shutdown message if the server closed the keepalive connection while 1714 * the connection was held unused in our pool). 1715 * So ensure that if present (=> APR_NONBLOCK_READ) it is read and 1716 * processed. We don't expect any data to be in the returned brigade. 1717 */ 1718 if (conn->sock && conn->connection) { 1719 bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); 1720 rv = ap_get_brigade(conn->connection->input_filters, bb, 1721 AP_MODE_READBYTES, APR_NONBLOCK_READ, 1722 HUGE_STRING_LEN); 1723 if ((rv != APR_SUCCESS) && !APR_STATUS_IS_EAGAIN(rv)) { 1724 socket_cleanup(conn); 1725 } 1726 if (!APR_BRIGADE_EMPTY(bb)) { 1727 apr_off_t len; 1728 1729 rv = apr_brigade_length(bb, 0, &len); 1730 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, 1731 "proxy: SSL cleanup brigade contained %" 1732 APR_OFF_T_FMT " bytes of data.", len); 1733 } 1734 apr_brigade_destroy(bb); 1735 } 1736 return APR_SUCCESS; 1737} 1738 1739/* reslist constructor */ 1740static apr_status_t connection_constructor(void **resource, void *params, 1741 apr_pool_t *pool) 1742{ 1743 apr_pool_t *ctx; 1744 apr_pool_t *scpool; 1745 proxy_conn_rec *conn; 1746 proxy_worker *worker = (proxy_worker *)params; 1747 1748 /* 1749 * Create the subpool for each connection 1750 * This keeps the memory consumption constant 1751 * when disconnecting from backend. 1752 */ 1753 apr_pool_create(&ctx, pool); 1754 apr_pool_tag(ctx, "proxy_conn_pool"); 1755 /* 1756 * Create another subpool that manages the data for the 1757 * socket and the connection member of the proxy_conn_rec struct as we 1758 * destroy this data more frequently than other data in the proxy_conn_rec 1759 * struct like hostname and addr (at least in the case where we have 1760 * keepalive connections that timed out). 1761 */ 1762 apr_pool_create(&scpool, ctx); 1763 apr_pool_tag(scpool, "proxy_conn_scpool"); 1764 conn = apr_pcalloc(ctx, sizeof(proxy_conn_rec)); 1765 1766 conn->pool = ctx; 1767 conn->scpool = scpool; 1768 conn->worker = worker; 1769#if APR_HAS_THREADS 1770 conn->inreslist = 1; 1771#endif 1772 *resource = conn; 1773 1774 return APR_SUCCESS; 1775} 1776 1777#if APR_HAS_THREADS /* only needed when threads are used */ 1778/* reslist destructor */ 1779static apr_status_t connection_destructor(void *resource, void *params, 1780 apr_pool_t *pool) 1781{ 1782 proxy_conn_rec *conn = (proxy_conn_rec *)resource; 1783 1784 /* Destroy the pool only if not called from reslist_destroy */ 1785 if (conn->worker->cp->pool) { 1786 apr_pool_destroy(conn->pool); 1787 } 1788 1789 return APR_SUCCESS; 1790} 1791#endif 1792 1793/* 1794 * ap_proxy_initialize_worker_share() concerns itself 1795 * with initializing those parts of worker which 1796 * are, or could be, shared. Basically worker->s 1797 */ 1798PROXY_DECLARE(void) ap_proxy_initialize_worker_share(proxy_server_conf *conf, 1799 proxy_worker *worker, 1800 server_rec *s) 1801{ 1802#if PROXY_HAS_SCOREBOARD 1803 lb_score *score = NULL; 1804#else 1805 void *score = NULL; 1806#endif 1807 1808 if (PROXY_WORKER_IS_INITIALIZED(worker)) { 1809 /* The worker share is already initialized */ 1810 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 1811 "proxy: worker %s already initialized", 1812 worker->name); 1813 return; 1814 } 1815#if PROXY_HAS_SCOREBOARD 1816 /* Get scoreboard slot */ 1817 if (ap_scoreboard_image) { 1818 score = ap_get_scoreboard_lb(worker->id); 1819 if (!score) { 1820 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, 1821 "proxy: ap_get_scoreboard_lb(%d) failed in child %" APR_PID_T_FMT " for worker %s", 1822 worker->id, getpid(), worker->name); 1823 } 1824 else { 1825 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 1826 "proxy: grabbed scoreboard slot %d in child %" APR_PID_T_FMT " for worker %s", 1827 worker->id, getpid(), worker->name); 1828 } 1829 } 1830#endif 1831 if (!score) { 1832 score = apr_pcalloc(conf->pool, sizeof(proxy_worker_stat)); 1833 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 1834 "proxy: initialized plain memory in child %" APR_PID_T_FMT " for worker %s", 1835 getpid(), worker->name); 1836 } 1837 worker->s = (proxy_worker_stat *)score; 1838 /* 1839 * recheck to see if we've already been here. Possible 1840 * if proxy is using scoreboard to hold shared stats 1841 */ 1842 if (PROXY_WORKER_IS_INITIALIZED(worker)) { 1843 /* The worker share is already initialized */ 1844 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 1845 "proxy: worker %s already initialized", 1846 worker->name); 1847 return; 1848 } 1849 if (worker->route) { 1850 strcpy(worker->s->route, worker->route); 1851 } 1852 else { 1853 *worker->s->route = '\0'; 1854 } 1855 if (worker->redirect) { 1856 strcpy(worker->s->redirect, worker->redirect); 1857 } 1858 else { 1859 *worker->s->redirect = '\0'; 1860 } 1861 1862 worker->s->status |= (worker->status | PROXY_WORKER_INITIALIZED); 1863 1864} 1865 1866PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, server_rec *s) 1867{ 1868 apr_status_t rv; 1869 1870#if APR_HAS_THREADS 1871 int mpm_threads; 1872#endif 1873 1874 if (worker->status & PROXY_WORKER_INITIALIZED) { 1875 /* The worker is already initialized */ 1876 return APR_SUCCESS; 1877 } 1878 1879 /* Set default parameters */ 1880 if (!worker->retry_set) { 1881 worker->retry = apr_time_from_sec(PROXY_WORKER_DEFAULT_RETRY); 1882 } 1883 /* By default address is reusable unless DisableReuse is set */ 1884 if (worker->disablereuse) { 1885 worker->is_address_reusable = 0; 1886 } 1887 else { 1888 worker->is_address_reusable = 1; 1889 } 1890 1891#if APR_HAS_THREADS 1892 ap_mpm_query(AP_MPMQ_MAX_THREADS, &mpm_threads); 1893 if (mpm_threads > 1) { 1894 /* Set hard max to no more then mpm_threads */ 1895 if (worker->hmax == 0 || worker->hmax > mpm_threads) { 1896 worker->hmax = mpm_threads; 1897 } 1898 if (worker->smax == -1 || worker->smax > worker->hmax) { 1899 worker->smax = worker->hmax; 1900 } 1901 /* Set min to be lower then smax */ 1902 if (worker->min > worker->smax) { 1903 worker->min = worker->smax; 1904 } 1905 } 1906 else { 1907 /* This will supress the apr_reslist creation */ 1908 worker->min = worker->smax = worker->hmax = 0; 1909 } 1910 if (worker->hmax) { 1911 rv = apr_reslist_create(&(worker->cp->res), 1912 worker->min, worker->smax, 1913 worker->hmax, worker->ttl, 1914 connection_constructor, connection_destructor, 1915 worker, worker->cp->pool); 1916 1917 apr_pool_cleanup_register(worker->cp->pool, (void *)worker, 1918 conn_pool_cleanup, 1919 apr_pool_cleanup_null); 1920 1921 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 1922 "proxy: initialized worker %d in child %" APR_PID_T_FMT " for (%s) min=%d max=%d smax=%d", 1923 worker->id, getpid(), worker->hostname, worker->min, 1924 worker->hmax, worker->smax); 1925 1926#if (APR_MAJOR_VERSION > 0) 1927 /* Set the acquire timeout */ 1928 if (rv == APR_SUCCESS && worker->acquire_set) { 1929 apr_reslist_timeout_set(worker->cp->res, worker->acquire); 1930 } 1931#endif 1932 } 1933 else 1934#endif 1935 { 1936 void *conn; 1937 1938 rv = connection_constructor(&conn, worker, worker->cp->pool); 1939 worker->cp->conn = conn; 1940 1941 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 1942 "proxy: initialized single connection worker %d in child %" APR_PID_T_FMT " for (%s)", 1943 worker->id, getpid(), worker->hostname); 1944 } 1945 if (rv == APR_SUCCESS) { 1946 worker->status |= (PROXY_WORKER_INITIALIZED); 1947 } 1948 return rv; 1949} 1950 1951PROXY_DECLARE(int) ap_proxy_retry_worker(const char *proxy_function, 1952 proxy_worker *worker, 1953 server_rec *s) 1954{ 1955 if (worker->s->status & PROXY_WORKER_IN_ERROR) { 1956 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 1957 "proxy: %s: retrying the worker for (%s)", 1958 proxy_function, worker->hostname); 1959 if (apr_time_now() > worker->s->error_time + worker->retry) { 1960 ++worker->s->retries; 1961 worker->s->status &= ~PROXY_WORKER_IN_ERROR; 1962 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 1963 "proxy: %s: worker for (%s) has been marked for retry", 1964 proxy_function, worker->hostname); 1965 return OK; 1966 } 1967 else { 1968 return DECLINED; 1969 } 1970 } 1971 else { 1972 return OK; 1973 } 1974} 1975 1976PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function, 1977 proxy_conn_rec **conn, 1978 proxy_worker *worker, 1979 server_rec *s) 1980{ 1981 apr_status_t rv; 1982 1983 if (!PROXY_WORKER_IS_USABLE(worker)) { 1984 /* Retry the worker */ 1985 ap_proxy_retry_worker(proxy_function, worker, s); 1986 1987 if (!PROXY_WORKER_IS_USABLE(worker)) { 1988 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, 1989 "proxy: %s: disabled connection for (%s)", 1990 proxy_function, worker->hostname); 1991 return HTTP_SERVICE_UNAVAILABLE; 1992 } 1993 } 1994#if APR_HAS_THREADS 1995 if (worker->hmax && worker->cp->res) { 1996 rv = apr_reslist_acquire(worker->cp->res, (void **)conn); 1997 } 1998 else 1999#endif 2000 { 2001 /* create the new connection if the previous was destroyed */ 2002 if (!worker->cp->conn) { 2003 connection_constructor((void **)conn, worker, worker->cp->pool); 2004 } 2005 else { 2006 *conn = worker->cp->conn; 2007 worker->cp->conn = NULL; 2008 } 2009 rv = APR_SUCCESS; 2010 } 2011 2012 if (rv != APR_SUCCESS) { 2013 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, 2014 "proxy: %s: failed to acquire connection for (%s)", 2015 proxy_function, worker->hostname); 2016 return HTTP_SERVICE_UNAVAILABLE; 2017 } 2018 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 2019 "proxy: %s: has acquired connection for (%s)", 2020 proxy_function, worker->hostname); 2021 2022 (*conn)->worker = worker; 2023 (*conn)->close = 0; 2024 (*conn)->close_on_recycle = 0; 2025#if APR_HAS_THREADS 2026 (*conn)->inreslist = 0; 2027#endif 2028 2029 return OK; 2030} 2031 2032PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function, 2033 proxy_conn_rec *conn, 2034 server_rec *s) 2035{ 2036 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 2037 "proxy: %s: has released connection for (%s)", 2038 proxy_function, conn->worker->hostname); 2039 connection_cleanup(conn); 2040 2041 return OK; 2042} 2043 2044PROXY_DECLARE(int) 2045ap_proxy_determine_connection(apr_pool_t *p, request_rec *r, 2046 proxy_server_conf *conf, 2047 proxy_worker *worker, 2048 proxy_conn_rec *conn, 2049 apr_uri_t *uri, 2050 char **url, 2051 const char *proxyname, 2052 apr_port_t proxyport, 2053 char *server_portstr, 2054 int server_portstr_size) 2055{ 2056 int server_port; 2057 apr_status_t err = APR_SUCCESS; 2058 apr_status_t uerr = APR_SUCCESS; 2059 2060 /* 2061 * Break up the URL to determine the host to connect to 2062 */ 2063 2064 /* we break the URL into host, port, uri */ 2065 if (APR_SUCCESS != apr_uri_parse(p, *url, uri)) { 2066 return ap_proxyerror(r, HTTP_BAD_REQUEST, 2067 apr_pstrcat(p,"URI cannot be parsed: ", *url, 2068 NULL)); 2069 } 2070 if (!uri->port) { 2071 uri->port = apr_uri_port_of_scheme(uri->scheme); 2072 } 2073 2074 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, 2075 "proxy: connecting %s to %s:%d", *url, uri->hostname, 2076 uri->port); 2077 2078 /* 2079 * allocate these out of the specified connection pool 2080 * The scheme handler decides if this is permanent or 2081 * short living pool. 2082 */ 2083 /* are we connecting directly, or via a proxy? */ 2084 if (!proxyname) { 2085 *url = apr_pstrcat(p, uri->path, uri->query ? "?" : "", 2086 uri->query ? uri->query : "", 2087 uri->fragment ? "#" : "", 2088 uri->fragment ? uri->fragment : "", NULL); 2089 } 2090 /* 2091 * Make sure that we pick the the correct and valid worker. 2092 * If a single keepalive connection triggers different workers, 2093 * then we have a problem (we don't select the correct one). 2094 * Do an expensive check in this case, where we compare the 2095 * the hostnames associated between the two. 2096 * 2097 * TODO: Handle this much better... 2098 */ 2099 if (!conn->hostname || !worker->is_address_reusable || 2100 worker->disablereuse || 2101 (r->connection->keepalives && 2102 (r->proxyreq == PROXYREQ_PROXY || r->proxyreq == PROXYREQ_REVERSE) && 2103 (strcasecmp(conn->hostname, uri->hostname) != 0) ) ) { 2104 if (proxyname) { 2105 conn->hostname = apr_pstrdup(conn->pool, proxyname); 2106 conn->port = proxyport; 2107 /* 2108 * If we have a forward proxy and the protocol is HTTPS, 2109 * then we need to prepend a HTTP CONNECT request before 2110 * sending our actual HTTPS requests. 2111 * Save our real backend data for using it later during HTTP CONNECT. 2112 */ 2113 if (conn->is_ssl) { 2114 const char *proxy_auth; 2115 2116 forward_info *forward = apr_pcalloc(conn->pool, sizeof(forward_info)); 2117 conn->forward = forward; 2118 forward->use_http_connect = 1; 2119 forward->target_host = apr_pstrdup(conn->pool, uri->hostname); 2120 forward->target_port = uri->port; 2121 /* Do we want to pass Proxy-Authorization along? 2122 * If we haven't used it, then YES 2123 * If we have used it then MAYBE: RFC2616 says we MAY propagate it. 2124 * So let's make it configurable by env. 2125 * The logic here is the same used in mod_proxy_http. 2126 */ 2127 proxy_auth = apr_table_get(r->headers_in, "Proxy-Authorization"); 2128 if (proxy_auth != NULL && 2129 proxy_auth[0] != '\0' && 2130 r->user == NULL && /* we haven't yet authenticated */ 2131 apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) { 2132 forward->proxy_auth = apr_pstrdup(conn->pool, proxy_auth); 2133 } 2134 } 2135 } 2136 else { 2137 conn->hostname = apr_pstrdup(conn->pool, uri->hostname); 2138 conn->port = uri->port; 2139 } 2140 socket_cleanup(conn); 2141 err = apr_sockaddr_info_get(&(conn->addr), 2142 conn->hostname, APR_UNSPEC, 2143 conn->port, 0, 2144 conn->pool); 2145 } 2146 else if (!worker->cp->addr) { 2147 if ((err = PROXY_THREAD_LOCK(worker)) != APR_SUCCESS) { 2148 ap_log_error(APLOG_MARK, APLOG_ERR, err, r->server, 2149 "proxy: lock"); 2150 return HTTP_INTERNAL_SERVER_ERROR; 2151 } 2152 2153 /* 2154 * Worker can have the single constant backend adress. 2155 * The single DNS lookup is used once per worker. 2156 * If dynamic change is needed then set the addr to NULL 2157 * inside dynamic config to force the lookup. 2158 */ 2159 err = apr_sockaddr_info_get(&(worker->cp->addr), 2160 conn->hostname, APR_UNSPEC, 2161 conn->port, 0, 2162 worker->cp->pool); 2163 conn->addr = worker->cp->addr; 2164 if ((uerr = PROXY_THREAD_UNLOCK(worker)) != APR_SUCCESS) { 2165 ap_log_error(APLOG_MARK, APLOG_ERR, uerr, r->server, 2166 "proxy: unlock"); 2167 } 2168 } 2169 else { 2170 conn->addr = worker->cp->addr; 2171 } 2172 /* Close a possible existing socket if we are told to do so */ 2173 if (conn->close) { 2174 socket_cleanup(conn); 2175 conn->close = 0; 2176 } 2177 2178 if (err != APR_SUCCESS) { 2179 return ap_proxyerror(r, HTTP_BAD_GATEWAY, 2180 apr_pstrcat(p, "DNS lookup failure for: ", 2181 conn->hostname, NULL)); 2182 } 2183 2184 /* Get the server port for the Via headers */ 2185 { 2186 server_port = ap_get_server_port(r); 2187 if (ap_is_default_port(server_port, r)) { 2188 strcpy(server_portstr,""); 2189 } 2190 else { 2191 apr_snprintf(server_portstr, server_portstr_size, ":%d", 2192 server_port); 2193 } 2194 } 2195 /* check if ProxyBlock directive on this host */ 2196 if (OK != ap_proxy_checkproxyblock(r, conf, conn->addr)) { 2197 return ap_proxyerror(r, HTTP_FORBIDDEN, 2198 "Connect to remote machine blocked"); 2199 } 2200 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, 2201 "proxy: connected %s to %s:%d", *url, conn->hostname, 2202 conn->port); 2203 return OK; 2204} 2205 2206#define USE_ALTERNATE_IS_CONNECTED 1 2207 2208#if !defined(APR_MSG_PEEK) && defined(MSG_PEEK) 2209#define APR_MSG_PEEK MSG_PEEK 2210#endif 2211 2212#if USE_ALTERNATE_IS_CONNECTED && defined(APR_MSG_PEEK) 2213static int is_socket_connected(apr_socket_t *socket) 2214{ 2215 apr_pollfd_t pfds[1]; 2216 apr_status_t status; 2217 apr_int32_t nfds; 2218 2219 pfds[0].reqevents = APR_POLLIN; 2220 pfds[0].desc_type = APR_POLL_SOCKET; 2221 pfds[0].desc.s = socket; 2222 2223 do { 2224 status = apr_poll(&pfds[0], 1, &nfds, 0); 2225 } while (APR_STATUS_IS_EINTR(status)); 2226 2227 if (status == APR_SUCCESS && nfds == 1 && 2228 pfds[0].rtnevents == APR_POLLIN) { 2229 apr_sockaddr_t unused; 2230 apr_size_t len = 1; 2231 char buf[1]; 2232 /* The socket might be closed in which case 2233 * the poll will return POLLIN. 2234 * If there is no data available the socket 2235 * is closed. 2236 */ 2237 status = apr_socket_recvfrom(&unused, socket, APR_MSG_PEEK, 2238 &buf[0], &len); 2239 if (status == APR_SUCCESS && len) 2240 return 1; 2241 else 2242 return 0; 2243 } 2244 else if (APR_STATUS_IS_EAGAIN(status) || APR_STATUS_IS_TIMEUP(status)) { 2245 return 1; 2246 } 2247 return 0; 2248 2249} 2250#else 2251static int is_socket_connected(apr_socket_t *sock) 2252 2253{ 2254 apr_size_t buffer_len = 1; 2255 char test_buffer[1]; 2256 apr_status_t socket_status; 2257 apr_interval_time_t current_timeout; 2258 2259 /* save timeout */ 2260 apr_socket_timeout_get(sock, ¤t_timeout); 2261 /* set no timeout */ 2262 apr_socket_timeout_set(sock, 0); 2263 socket_status = apr_socket_recv(sock, test_buffer, &buffer_len); 2264 /* put back old timeout */ 2265 apr_socket_timeout_set(sock, current_timeout); 2266 if (APR_STATUS_IS_EOF(socket_status) || 2267 APR_STATUS_IS_ECONNRESET(socket_status)) 2268 return 0; 2269 else 2270 return 1; 2271} 2272#endif /* USE_ALTERNATE_IS_CONNECTED */ 2273 2274 2275/* 2276 * Send a HTTP CONNECT request to a forward proxy. 2277 * The proxy is given by "backend", the target server 2278 * is contained in the "forward" member of "backend". 2279 */ 2280static apr_status_t send_http_connect(proxy_conn_rec *backend, 2281 server_rec *s) 2282{ 2283 int status; 2284 apr_size_t nbytes; 2285 apr_size_t left; 2286 int complete = 0; 2287 char buffer[HUGE_STRING_LEN]; 2288 char drain_buffer[HUGE_STRING_LEN]; 2289 forward_info *forward = (forward_info *)backend->forward; 2290 int len = 0; 2291 2292 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 2293 "proxy: CONNECT: sending the CONNECT request for %s:%d " 2294 "to the remote proxy %pI (%s)", 2295 forward->target_host, forward->target_port, 2296 backend->addr, backend->hostname); 2297 /* Create the CONNECT request */ 2298 nbytes = apr_snprintf(buffer, sizeof(buffer), 2299 "CONNECT %s:%d HTTP/1.0" CRLF, 2300 forward->target_host, forward->target_port); 2301 /* Add proxy authorization from the initial request if necessary */ 2302 if (forward->proxy_auth != NULL) { 2303 nbytes += apr_snprintf(buffer + nbytes, sizeof(buffer) - nbytes, 2304 "Proxy-Authorization: %s" CRLF, 2305 forward->proxy_auth); 2306 } 2307 /* Set a reasonable agent and send everything */ 2308 nbytes += apr_snprintf(buffer + nbytes, sizeof(buffer) - nbytes, 2309 "Proxy-agent: %s" CRLF CRLF, 2310 ap_get_server_banner()); 2311 apr_socket_send(backend->sock, buffer, &nbytes); 2312 2313 /* Receive the whole CONNECT response */ 2314 left = sizeof(buffer) - 1; 2315 /* Read until we find the end of the headers or run out of buffer */ 2316 do { 2317 nbytes = left; 2318 status = apr_socket_recv(backend->sock, buffer + len, &nbytes); 2319 len += nbytes; 2320 left -= nbytes; 2321 buffer[len] = '\0'; 2322 if (strstr(buffer + len - nbytes, "\r\n\r\n") != NULL) { 2323 complete = 1; 2324 break; 2325 } 2326 } while (status == APR_SUCCESS && left > 0); 2327 /* Drain what's left */ 2328 if (!complete) { 2329 nbytes = sizeof(drain_buffer) - 1; 2330 while (status == APR_SUCCESS && nbytes) { 2331 status = apr_socket_recv(backend->sock, drain_buffer, &nbytes); 2332 buffer[nbytes] = '\0'; 2333 nbytes = sizeof(drain_buffer) - 1; 2334 if (strstr(drain_buffer, "\r\n\r\n") != NULL) { 2335 complete = 1; 2336 break; 2337 } 2338 } 2339 } 2340 2341 /* Check for HTTP_OK response status */ 2342 if (status == APR_SUCCESS) { 2343 int major, minor; 2344 /* Only scan for three character status code */ 2345 char code_str[4]; 2346 2347 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 2348 "send_http_connect: response from the forward proxy: %s", 2349 buffer); 2350 2351 /* Extract the returned code */ 2352 if (sscanf(buffer, "HTTP/%u.%u %3s", &major, &minor, code_str) == 3) { 2353 status = atoi(code_str); 2354 if (status == HTTP_OK) { 2355 status = APR_SUCCESS; 2356 } 2357 else { 2358 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, 2359 "send_http_connect: the forward proxy returned code is '%s'", 2360 code_str); 2361 status = APR_INCOMPLETE; 2362 } 2363 } 2364 } 2365 2366 return(status); 2367} 2368 2369 2370PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function, 2371 proxy_conn_rec *conn, 2372 proxy_worker *worker, 2373 server_rec *s) 2374{ 2375 apr_status_t rv; 2376 int connected = 0; 2377 int loglevel; 2378 apr_sockaddr_t *backend_addr = conn->addr; 2379 apr_socket_t *newsock; 2380 void *sconf = s->module_config; 2381 proxy_server_conf *conf = 2382 (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); 2383 2384 if (conn->sock) { 2385 if (!(connected = is_socket_connected(conn->sock))) { 2386 socket_cleanup(conn); 2387 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 2388 "proxy: %s: backend socket is disconnected.", 2389 proxy_function); 2390 } 2391 } 2392 while (backend_addr && !connected) { 2393 if ((rv = apr_socket_create(&newsock, backend_addr->family, 2394 SOCK_STREAM, APR_PROTO_TCP, 2395 conn->scpool)) != APR_SUCCESS) { 2396 loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR; 2397 ap_log_error(APLOG_MARK, loglevel, rv, s, 2398 "proxy: %s: error creating fam %d socket for target %s", 2399 proxy_function, 2400 backend_addr->family, 2401 worker->hostname); 2402 /* 2403 * this could be an IPv6 address from the DNS but the 2404 * local machine won't give us an IPv6 socket; hopefully the 2405 * DNS returned an additional address to try 2406 */ 2407 backend_addr = backend_addr->next; 2408 continue; 2409 } 2410 conn->connection = NULL; 2411 2412#if !defined(TPF) && !defined(BEOS) 2413 if (worker->recv_buffer_size > 0 && 2414 (rv = apr_socket_opt_set(newsock, APR_SO_RCVBUF, 2415 worker->recv_buffer_size))) { 2416 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, 2417 "apr_socket_opt_set(SO_RCVBUF): Failed to set " 2418 "ProxyReceiveBufferSize, using default"); 2419 } 2420#endif 2421 2422 rv = apr_socket_opt_set(newsock, APR_TCP_NODELAY, 1); 2423 if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) { 2424 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, 2425 "apr_socket_opt_set(APR_TCP_NODELAY): " 2426 "Failed to set"); 2427 } 2428 2429 /* Set a timeout for connecting to the backend on the socket */ 2430 if (worker->conn_timeout_set) { 2431 apr_socket_timeout_set(newsock, worker->conn_timeout); 2432 } 2433 else if (worker->timeout_set == 1) { 2434 apr_socket_timeout_set(newsock, worker->timeout); 2435 } 2436 else if (conf->timeout_set == 1) { 2437 apr_socket_timeout_set(newsock, conf->timeout); 2438 } 2439 else { 2440 apr_socket_timeout_set(newsock, s->timeout); 2441 } 2442 /* Set a keepalive option */ 2443 if (worker->keepalive) { 2444 if ((rv = apr_socket_opt_set(newsock, 2445 APR_SO_KEEPALIVE, 1)) != APR_SUCCESS) { 2446 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, 2447 "apr_socket_opt_set(SO_KEEPALIVE): Failed to set" 2448 " Keepalive"); 2449 } 2450 } 2451 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 2452 "proxy: %s: fam %d socket created to connect to %s", 2453 proxy_function, backend_addr->family, worker->hostname); 2454 2455 /* make the connection out of the socket */ 2456 rv = apr_socket_connect(newsock, backend_addr); 2457 2458 /* if an error occurred, loop round and try again */ 2459 if (rv != APR_SUCCESS) { 2460 apr_socket_close(newsock); 2461 loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR; 2462 ap_log_error(APLOG_MARK, loglevel, rv, s, 2463 "proxy: %s: attempt to connect to %pI (%s) failed", 2464 proxy_function, 2465 backend_addr, 2466 worker->hostname); 2467 backend_addr = backend_addr->next; 2468 continue; 2469 } 2470 2471 /* Set a timeout on the socket */ 2472 if (worker->timeout_set == 1) { 2473 apr_socket_timeout_set(newsock, worker->timeout); 2474 } 2475 else if (conf->timeout_set == 1) { 2476 apr_socket_timeout_set(newsock, conf->timeout); 2477 } 2478 else { 2479 apr_socket_timeout_set(newsock, s->timeout); 2480 } 2481 2482 conn->sock = newsock; 2483 2484 if (conn->forward) { 2485 forward_info *forward = (forward_info *)conn->forward; 2486 /* 2487 * For HTTP CONNECT we need to prepend CONNECT request before 2488 * sending our actual HTTPS requests. 2489 */ 2490 if (forward->use_http_connect) { 2491 rv = send_http_connect(conn, s); 2492 /* If an error occurred, loop round and try again */ 2493 if (rv != APR_SUCCESS) { 2494 conn->sock = NULL; 2495 apr_socket_close(newsock); 2496 loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR; 2497 ap_log_error(APLOG_MARK, loglevel, rv, s, 2498 "proxy: %s: attempt to connect to %s:%d " 2499 "via http CONNECT through %pI (%s) failed", 2500 proxy_function, 2501 forward->target_host, forward->target_port, 2502 backend_addr, worker->hostname); 2503 backend_addr = backend_addr->next; 2504 continue; 2505 } 2506 } 2507 } 2508 2509 connected = 1; 2510 } 2511 /* 2512 * Put the entire worker to error state if 2513 * the PROXY_WORKER_IGNORE_ERRORS flag is not set. 2514 * Altrough some connections may be alive 2515 * no further connections to the worker could be made 2516 */ 2517 if (!connected && PROXY_WORKER_IS_USABLE(worker) && 2518 !(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) { 2519 worker->s->error_time = apr_time_now(); 2520 worker->s->status |= PROXY_WORKER_IN_ERROR; 2521 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, 2522 "ap_proxy_connect_backend disabling worker for (%s)", 2523 worker->hostname); 2524 } 2525 else { 2526 worker->s->error_time = 0; 2527 worker->s->retries = 0; 2528 } 2529 return connected ? OK : DECLINED; 2530} 2531 2532PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function, 2533 proxy_conn_rec *conn, 2534 conn_rec *c, 2535 server_rec *s) 2536{ 2537 apr_sockaddr_t *backend_addr = conn->addr; 2538 int rc; 2539 apr_interval_time_t current_timeout; 2540 apr_bucket_alloc_t *bucket_alloc; 2541 2542 if (conn->connection) { 2543 return OK; 2544 } 2545 2546 bucket_alloc = apr_bucket_alloc_create(conn->scpool); 2547 /* 2548 * The socket is now open, create a new backend server connection 2549 */ 2550 conn->connection = ap_run_create_connection(conn->scpool, s, conn->sock, 2551 0, NULL, 2552 bucket_alloc); 2553 2554 if (!conn->connection) { 2555 /* 2556 * the peer reset the connection already; ap_run_create_connection() 2557 * closed the socket 2558 */ 2559 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, 2560 s, "proxy: %s: an error occurred creating a " 2561 "new connection to %pI (%s)", proxy_function, 2562 backend_addr, conn->hostname); 2563 /* XXX: Will be closed when proxy_conn is closed */ 2564 socket_cleanup(conn); 2565 return HTTP_INTERNAL_SERVER_ERROR; 2566 } 2567 2568 /* For ssl connection to backend */ 2569 if (conn->is_ssl) { 2570 if (!ap_proxy_ssl_enable(conn->connection)) { 2571 ap_log_error(APLOG_MARK, APLOG_ERR, 0, 2572 s, "proxy: %s: failed to enable ssl support " 2573 "for %pI (%s)", proxy_function, 2574 backend_addr, conn->hostname); 2575 return HTTP_INTERNAL_SERVER_ERROR; 2576 } 2577 } 2578 else { 2579 /* TODO: See if this will break FTP */ 2580 ap_proxy_ssl_disable(conn->connection); 2581 } 2582 2583 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 2584 "proxy: %s: connection complete to %pI (%s)", 2585 proxy_function, backend_addr, conn->hostname); 2586 2587 /* 2588 * save the timout of the socket because core_pre_connection 2589 * will set it to base_server->timeout 2590 * (core TimeOut directive). 2591 */ 2592 apr_socket_timeout_get(conn->sock, ¤t_timeout); 2593 /* set up the connection filters */ 2594 rc = ap_run_pre_connection(conn->connection, conn->sock); 2595 if (rc != OK && rc != DONE) { 2596 conn->connection->aborted = 1; 2597 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 2598 "proxy: %s: pre_connection setup failed (%d)", 2599 proxy_function, rc); 2600 return rc; 2601 } 2602 apr_socket_timeout_set(conn->sock, current_timeout); 2603 2604 return OK; 2605} 2606 2607int ap_proxy_lb_workers(void) 2608{ 2609 /* 2610 * Since we can't resize the scoreboard when reconfiguring, we 2611 * have to impose a limit on the number of workers, we are 2612 * able to reconfigure to. 2613 */ 2614 if (!lb_workers_limit) 2615 lb_workers_limit = proxy_lb_workers + PROXY_DYNAMIC_BALANCER_LIMIT; 2616 return lb_workers_limit; 2617} 2618 2619PROXY_DECLARE(void) ap_proxy_backend_broke(request_rec *r, 2620 apr_bucket_brigade *brigade) 2621{ 2622 apr_bucket *e; 2623 conn_rec *c = r->connection; 2624 2625 r->no_cache = 1; 2626 /* 2627 * If this is a subrequest, then prevent also caching of the main 2628 * request. 2629 */ 2630 if (r->main) 2631 r->main->no_cache = 1; 2632 e = ap_bucket_error_create(HTTP_BAD_GATEWAY, NULL, c->pool, 2633 c->bucket_alloc); 2634 APR_BRIGADE_INSERT_TAIL(brigade, e); 2635 e = apr_bucket_eos_create(c->bucket_alloc); 2636 APR_BRIGADE_INSERT_TAIL(brigade, e); 2637} 2638 2639/* 2640 * Transform buckets from one bucket allocator to another one by creating a 2641 * transient bucket for each data bucket and let it use the data read from 2642 * the old bucket. Metabuckets are transformed by just recreating them. 2643 * Attention: Currently only the following bucket types are handled: 2644 * 2645 * All data buckets 2646 * FLUSH 2647 * EOS 2648 * 2649 * If an other bucket type is found its type is logged as a debug message 2650 * and APR_EGENERAL is returned. 2651 */ 2652PROXY_DECLARE(apr_status_t) 2653ap_proxy_buckets_lifetime_transform(request_rec *r, apr_bucket_brigade *from, 2654 apr_bucket_brigade *to) 2655{ 2656 apr_bucket *e; 2657 apr_bucket *new; 2658 const char *data; 2659 apr_size_t bytes; 2660 apr_status_t rv = APR_SUCCESS; 2661 2662 apr_brigade_cleanup(to); 2663 for (e = APR_BRIGADE_FIRST(from); 2664 e != APR_BRIGADE_SENTINEL(from); 2665 e = APR_BUCKET_NEXT(e)) { 2666 if (!APR_BUCKET_IS_METADATA(e)) { 2667 apr_bucket_read(e, &data, &bytes, APR_BLOCK_READ); 2668 new = apr_bucket_transient_create(data, bytes, r->connection->bucket_alloc); 2669 APR_BRIGADE_INSERT_TAIL(to, new); 2670 } 2671 else if (APR_BUCKET_IS_FLUSH(e)) { 2672 new = apr_bucket_flush_create(r->connection->bucket_alloc); 2673 APR_BRIGADE_INSERT_TAIL(to, new); 2674 } 2675 else if (APR_BUCKET_IS_EOS(e)) { 2676 new = apr_bucket_eos_create(r->connection->bucket_alloc); 2677 APR_BRIGADE_INSERT_TAIL(to, new); 2678 } 2679 else { 2680 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, 2681 "proxy: Unhandled bucket type of type %s in" 2682 " ap_proxy_buckets_lifetime_transform", e->type->name); 2683 rv = APR_EGENERAL; 2684 } 2685 } 2686 return rv; 2687} 2688