1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* Utility routines for Apache proxy */ 18#include "mod_proxy.h" 19#include "ap_mpm.h" 20#include "scoreboard.h" 21#include "apr_version.h" 22#include "apr_hash.h" 23#include "proxy_util.h" 24#include "ajp.h" 25 26#if APR_HAVE_UNISTD_H 27#include <unistd.h> /* for getpid() */ 28#endif 29 30#if (APR_MAJOR_VERSION < 1) 31#undef apr_socket_create 32#define apr_socket_create apr_socket_create_ex 33#endif 34 35#if APR_HAVE_SYS_UN_H 36#include <sys/un.h> 37#endif 38#if (APR_MAJOR_VERSION < 2) 39#include "apr_support.h" /* for apr_wait_for_io_or_timeout() */ 40#endif 41 42APLOG_USE_MODULE(proxy); 43 44/* 45 * Opaque structure containing target server info when 46 * using a forward proxy. 47 * Up to now only used in combination with HTTP CONNECT. 48 */ 49typedef struct { 50 int use_http_connect; /* Use SSL Tunneling via HTTP CONNECT */ 51 const char *target_host; /* Target hostname */ 52 apr_port_t target_port; /* Target port */ 53 const char *proxy_auth; /* Proxy authorization */ 54} forward_info; 55 56/* Keep synced with mod_proxy.h! */ 57static struct wstat { 58 unsigned int bit; 59 char flag; 60 const char *name; 61} wstat_tbl[] = { 62 {PROXY_WORKER_INITIALIZED, PROXY_WORKER_INITIALIZED_FLAG, "Init "}, 63 {PROXY_WORKER_IGNORE_ERRORS, PROXY_WORKER_IGNORE_ERRORS_FLAG, "Ign "}, 64 {PROXY_WORKER_DRAIN, PROXY_WORKER_DRAIN_FLAG, "Drn "}, 65 {PROXY_WORKER_IN_SHUTDOWN, PROXY_WORKER_IN_SHUTDOWN_FLAG, "Shut "}, 66 {PROXY_WORKER_DISABLED, PROXY_WORKER_DISABLED_FLAG, "Dis "}, 67 {PROXY_WORKER_STOPPED, PROXY_WORKER_STOPPED_FLAG, "Stop "}, 68 {PROXY_WORKER_IN_ERROR, PROXY_WORKER_IN_ERROR_FLAG, "Err "}, 69 {PROXY_WORKER_HOT_STANDBY, PROXY_WORKER_HOT_STANDBY_FLAG, "Stby "}, 70 {PROXY_WORKER_FREE, PROXY_WORKER_FREE_FLAG, "Free "}, 71 {0x0, '\0', NULL} 72}; 73 74/* Global balancer counter */ 75int PROXY_DECLARE_DATA proxy_lb_workers = 0; 76static int lb_workers_limit = 0; 77const apr_strmatch_pattern PROXY_DECLARE_DATA *ap_proxy_strmatch_path; 78const apr_strmatch_pattern PROXY_DECLARE_DATA *ap_proxy_strmatch_domain; 79 80extern apr_global_mutex_t *proxy_mutex; 81 82static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r); 83static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r); 84static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r); 85static int proxy_match_word(struct dirconn_entry *This, request_rec *r); 86 87APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, create_req, 88 (request_rec *r, request_rec *pr), (r, pr), 89 OK, DECLINED) 90 91PROXY_DECLARE(apr_status_t) ap_proxy_strncpy(char *dst, const char *src, 92 apr_size_t dlen) 93{ 94 char *thenil; 95 apr_size_t thelen; 96 97 /* special case handling */ 98 if (!dlen) { 99 /* XXX: APR_ENOSPACE would be better */ 100 return APR_EGENERAL; 101 } 102 if (!src) { 103 *dst = '\0'; 104 return APR_SUCCESS; 105 } 106 thenil = apr_cpystrn(dst, src, dlen); 107 thelen = thenil - dst; 108 if (src[thelen] == '\0') { 109 return APR_SUCCESS; 110 } 111 return APR_EGENERAL; 112} 113 114/* already called in the knowledge that the characters are hex digits */ 115PROXY_DECLARE(int) ap_proxy_hex2c(const char *x) 116{ 117 int i; 118 119#if !APR_CHARSET_EBCDIC 120 int ch = x[0]; 121 122 if (apr_isdigit(ch)) { 123 i = ch - '0'; 124 } 125 else if (apr_isupper(ch)) { 126 i = ch - ('A' - 10); 127 } 128 else { 129 i = ch - ('a' - 10); 130 } 131 i <<= 4; 132 133 ch = x[1]; 134 if (apr_isdigit(ch)) { 135 i += ch - '0'; 136 } 137 else if (apr_isupper(ch)) { 138 i += ch - ('A' - 10); 139 } 140 else { 141 i += ch - ('a' - 10); 142 } 143 return i; 144#else /*APR_CHARSET_EBCDIC*/ 145 /* 146 * we assume that the hex value refers to an ASCII character 147 * so convert to EBCDIC so that it makes sense locally; 148 * 149 * example: 150 * 151 * client specifies %20 in URL to refer to a space char; 152 * at this point we're called with EBCDIC "20"; after turning 153 * EBCDIC "20" into binary 0x20, we then need to assume that 0x20 154 * represents an ASCII char and convert 0x20 to EBCDIC, yielding 155 * 0x40 156 */ 157 char buf[1]; 158 159 if (1 == sscanf(x, "%2x", &i)) { 160 buf[0] = i & 0xFF; 161 ap_xlate_proto_from_ascii(buf, 1); 162 return buf[0]; 163 } 164 else { 165 return 0; 166 } 167#endif /*APR_CHARSET_EBCDIC*/ 168} 169 170PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x) 171{ 172#if !APR_CHARSET_EBCDIC 173 int i; 174 175 x[0] = '%'; 176 i = (ch & 0xF0) >> 4; 177 if (i >= 10) { 178 x[1] = ('A' - 10) + i; 179 } 180 else { 181 x[1] = '0' + i; 182 } 183 184 i = ch & 0x0F; 185 if (i >= 10) { 186 x[2] = ('A' - 10) + i; 187 } 188 else { 189 x[2] = '0' + i; 190 } 191#else /*APR_CHARSET_EBCDIC*/ 192 static const char ntoa[] = { "0123456789ABCDEF" }; 193 char buf[1]; 194 195 ch &= 0xFF; 196 197 buf[0] = ch; 198 ap_xlate_proto_to_ascii(buf, 1); 199 200 x[0] = '%'; 201 x[1] = ntoa[(buf[0] >> 4) & 0x0F]; 202 x[2] = ntoa[buf[0] & 0x0F]; 203 x[3] = '\0'; 204#endif /*APR_CHARSET_EBCDIC*/ 205} 206 207/* 208 * canonicalise a URL-encoded string 209 */ 210 211/* 212 * Convert a URL-encoded string to canonical form. 213 * It decodes characters which need not be encoded, 214 * and encodes those which must be encoded, and does not touch 215 * those which must not be touched. 216 */ 217PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, 218 enum enctype t, int forcedec, 219 int proxyreq) 220{ 221 int i, j, ch; 222 char *y; 223 char *allowed; /* characters which should not be encoded */ 224 char *reserved; /* characters which much not be en/de-coded */ 225 226/* 227 * N.B. in addition to :@&=, this allows ';' in an http path 228 * and '?' in an ftp path -- this may be revised 229 * 230 * Also, it makes a '+' character in a search string reserved, as 231 * it may be form-encoded. (Although RFC 1738 doesn't allow this - 232 * it only permits ; / ? : @ = & as reserved chars.) 233 */ 234 if (t == enc_path) { 235 allowed = "~$-_.+!*'(),;:@&="; 236 } 237 else if (t == enc_search) { 238 allowed = "$-_.!*'(),;:@&="; 239 } 240 else if (t == enc_user) { 241 allowed = "$-_.+!*'(),;@&="; 242 } 243 else if (t == enc_fpath) { 244 allowed = "$-_.+!*'(),?:@&="; 245 } 246 else { /* if (t == enc_parm) */ 247 allowed = "$-_.+!*'(),?/:@&="; 248 } 249 250 if (t == enc_path) { 251 reserved = "/"; 252 } 253 else if (t == enc_search) { 254 reserved = "+"; 255 } 256 else { 257 reserved = ""; 258 } 259 260 y = apr_palloc(p, 3 * len + 1); 261 262 for (i = 0, j = 0; i < len; i++, j++) { 263/* always handle '/' first */ 264 ch = x[i]; 265 if (strchr(reserved, ch)) { 266 y[j] = ch; 267 continue; 268 } 269/* 270 * decode it if not already done. do not decode reverse proxied URLs 271 * unless specifically forced 272 */ 273 if ((forcedec || (proxyreq && proxyreq != PROXYREQ_REVERSE)) && ch == '%') { 274 if (!apr_isxdigit(x[i + 1]) || !apr_isxdigit(x[i + 2])) { 275 return NULL; 276 } 277 ch = ap_proxy_hex2c(&x[i + 1]); 278 i += 2; 279 if (ch != 0 && strchr(reserved, ch)) { /* keep it encoded */ 280 ap_proxy_c2hex(ch, &y[j]); 281 j += 2; 282 continue; 283 } 284 } 285/* recode it, if necessary */ 286 if (!apr_isalnum(ch) && !strchr(allowed, ch)) { 287 ap_proxy_c2hex(ch, &y[j]); 288 j += 2; 289 } 290 else { 291 y[j] = ch; 292 } 293 } 294 y[j] = '\0'; 295 return y; 296} 297 298/* 299 * Parses network-location. 300 * urlp on input the URL; on output the path, after the leading / 301 * user NULL if no user/password permitted 302 * password holder for password 303 * host holder for host 304 * port port number; only set if one is supplied. 305 * 306 * Returns an error string. 307 */ 308PROXY_DECLARE(char *) 309 ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp, 310 char **passwordp, char **hostp, apr_port_t *port) 311{ 312 char *addr, *scope_id, *strp, *host, *url = *urlp; 313 char *user = NULL, *password = NULL; 314 apr_port_t tmp_port; 315 apr_status_t rv; 316 317 if (url[0] != '/' || url[1] != '/') { 318 return "Malformed URL"; 319 } 320 host = url + 2; 321 url = strchr(host, '/'); 322 if (url == NULL) { 323 url = ""; 324 } 325 else { 326 *(url++) = '\0'; /* skip separating '/' */ 327 } 328 329 /* find _last_ '@' since it might occur in user/password part */ 330 strp = strrchr(host, '@'); 331 332 if (strp != NULL) { 333 *strp = '\0'; 334 user = host; 335 host = strp + 1; 336 337/* find password */ 338 strp = strchr(user, ':'); 339 if (strp != NULL) { 340 *strp = '\0'; 341 password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, 1, 0); 342 if (password == NULL) { 343 return "Bad %-escape in URL (password)"; 344 } 345 } 346 347 user = ap_proxy_canonenc(p, user, strlen(user), enc_user, 1, 0); 348 if (user == NULL) { 349 return "Bad %-escape in URL (username)"; 350 } 351 } 352 if (userp != NULL) { 353 *userp = user; 354 } 355 if (passwordp != NULL) { 356 *passwordp = password; 357 } 358 359 /* 360 * Parse the host string to separate host portion from optional port. 361 * Perform range checking on port. 362 */ 363 rv = apr_parse_addr_port(&addr, &scope_id, &tmp_port, host, p); 364 if (rv != APR_SUCCESS || addr == NULL || scope_id != NULL) { 365 return "Invalid host/port"; 366 } 367 if (tmp_port != 0) { /* only update caller's port if port was specified */ 368 *port = tmp_port; 369 } 370 371 ap_str_tolower(addr); /* DNS names are case-insensitive */ 372 373 *urlp = url; 374 *hostp = addr; 375 376 return NULL; 377} 378 379PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message) 380{ 381 const char *uri = ap_escape_html(r->pool, r->uri); 382 apr_table_setn(r->notes, "error-notes", 383 apr_pstrcat(r->pool, 384 "The proxy server could not handle the request <em><a href=\"", 385 uri, "\">", ap_escape_html(r->pool, r->method), " ", uri, 386 "</a></em>.<p>\n" 387 "Reason: <strong>", ap_escape_html(r->pool, message), 388 "</strong></p>", 389 NULL)); 390 391 /* Allow "error-notes" string to be printed by ap_send_error_response() */ 392 apr_table_setn(r->notes, "verbose-error-to", "*"); 393 394 r->status_line = apr_psprintf(r->pool, "%3.3u Proxy Error", statuscode); 395 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00898) "%s returned by %s", message, 396 r->uri); 397 return statuscode; 398} 399 400static const char * 401 proxy_get_host_of_request(request_rec *r) 402{ 403 char *url, *user = NULL, *password = NULL, *err, *host = NULL; 404 apr_port_t port; 405 406 if (r->hostname != NULL) { 407 return r->hostname; 408 } 409 410 /* Set url to the first char after "scheme://" */ 411 if ((url = strchr(r->uri, ':')) == NULL || url[1] != '/' || url[2] != '/') { 412 return NULL; 413 } 414 415 url = apr_pstrdup(r->pool, &url[1]); /* make it point to "//", which is what proxy_canon_netloc expects */ 416 417 err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port); 418 419 if (err != NULL) { 420 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00899) "%s", err); 421 } 422 423 r->hostname = host; 424 425 return host; /* ought to return the port, too */ 426} 427 428/* Return TRUE if addr represents an IP address (or an IP network address) */ 429PROXY_DECLARE(int) ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p) 430{ 431 const char *addr = This->name; 432 long ip_addr[4]; 433 int i, quads; 434 long bits; 435 436 /* 437 * if the address is given with an explicit netmask, use that 438 * Due to a deficiency in apr_inet_addr(), it is impossible to parse 439 * "partial" addresses (with less than 4 quads) correctly, i.e. 440 * 192.168.123 is parsed as 192.168.0.123, which is not what I want. 441 * I therefore have to parse the IP address manually: 442 * if (proxy_readmask(This->name, &This->addr.s_addr, &This->mask.s_addr) == 0) 443 * addr and mask were set by proxy_readmask() 444 * return 1; 445 */ 446 447 /* 448 * Parse IP addr manually, optionally allowing 449 * abbreviated net addresses like 192.168. 450 */ 451 452 /* Iterate over up to 4 (dotted) quads. */ 453 for (quads = 0; quads < 4 && *addr != '\0'; ++quads) { 454 char *tmp; 455 456 if (*addr == '/' && quads > 0) { /* netmask starts here. */ 457 break; 458 } 459 460 if (!apr_isdigit(*addr)) { 461 return 0; /* no digit at start of quad */ 462 } 463 464 ip_addr[quads] = strtol(addr, &tmp, 0); 465 466 if (tmp == addr) { /* expected a digit, found something else */ 467 return 0; 468 } 469 470 if (ip_addr[quads] < 0 || ip_addr[quads] > 255) { 471 /* invalid octet */ 472 return 0; 473 } 474 475 addr = tmp; 476 477 if (*addr == '.' && quads != 3) { 478 ++addr; /* after the 4th quad, a dot would be illegal */ 479 } 480 } 481 482 for (This->addr.s_addr = 0, i = 0; i < quads; ++i) { 483 This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i)); 484 } 485 486 if (addr[0] == '/' && apr_isdigit(addr[1])) { /* net mask follows: */ 487 char *tmp; 488 489 ++addr; 490 491 bits = strtol(addr, &tmp, 0); 492 493 if (tmp == addr) { /* expected a digit, found something else */ 494 return 0; 495 } 496 497 addr = tmp; 498 499 if (bits < 0 || bits > 32) { /* netmask must be between 0 and 32 */ 500 return 0; 501 } 502 503 } 504 else { 505 /* 506 * Determine (i.e., "guess") netmask by counting the 507 * number of trailing .0's; reduce #quads appropriately 508 * (so that 192.168.0.0 is equivalent to 192.168.) 509 */ 510 while (quads > 0 && ip_addr[quads - 1] == 0) { 511 --quads; 512 } 513 514 /* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */ 515 if (quads < 1) { 516 return 0; 517 } 518 519 /* every zero-byte counts as 8 zero-bits */ 520 bits = 8 * quads; 521 522 if (bits != 32) { /* no warning for fully qualified IP address */ 523 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00900) 524 "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld", 525 inet_ntoa(This->addr), bits); 526 } 527 } 528 529 This->mask.s_addr = htonl(APR_INADDR_NONE << (32 - bits)); 530 531 if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) { 532 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00901) 533 "Warning: NetMask and IP-Addr disagree in %s/%ld", 534 inet_ntoa(This->addr), bits); 535 This->addr.s_addr &= This->mask.s_addr; 536 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00902) 537 " Set to %s/%ld", inet_ntoa(This->addr), bits); 538 } 539 540 if (*addr == '\0') { 541 This->matcher = proxy_match_ipaddr; 542 return 1; 543 } 544 else { 545 return (*addr == '\0'); /* okay iff we've parsed the whole string */ 546 } 547} 548 549/* Return TRUE if addr represents an IP address (or an IP network address) */ 550static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r) 551{ 552 int i, ip_addr[4]; 553 struct in_addr addr, *ip; 554 const char *host = proxy_get_host_of_request(r); 555 556 if (host == NULL) { /* oops! */ 557 return 0; 558 } 559 560 memset(&addr, '\0', sizeof addr); 561 memset(ip_addr, '\0', sizeof ip_addr); 562 563 if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) { 564 for (addr.s_addr = 0, i = 0; i < 4; ++i) { 565 /* ap_proxy_is_ipaddr() already confirmed that we have 566 * a valid octet in ip_addr[i] 567 */ 568 addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i)); 569 } 570 571 if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) { 572#if DEBUGGING 573 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00903) 574 "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr)); 575 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00904) 576 "%s/", inet_ntoa(This->addr)); 577 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00905) 578 "%s", inet_ntoa(This->mask)); 579#endif 580 return 1; 581 } 582#if DEBUGGING 583 else { 584 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00906) 585 "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr)); 586 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00907) 587 "%s/", inet_ntoa(This->addr)); 588 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00908) 589 "%s", inet_ntoa(This->mask)); 590 } 591#endif 592 } 593 else { 594 struct apr_sockaddr_t *reqaddr; 595 596 if (apr_sockaddr_info_get(&reqaddr, host, APR_UNSPEC, 0, 0, r->pool) 597 != APR_SUCCESS) { 598#if DEBUGGING 599 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00909) 600 "2)IP-NoMatch: hostname=%s msg=Host not found", host); 601#endif 602 return 0; 603 } 604 605 /* Try to deal with multiple IP addr's for a host */ 606 /* FIXME: This needs to be able to deal with IPv6 */ 607 while (reqaddr) { 608 ip = (struct in_addr *) reqaddr->ipaddr_ptr; 609 if (This->addr.s_addr == (ip->s_addr & This->mask.s_addr)) { 610#if DEBUGGING 611 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00910) 612 "3)IP-Match: %s[%s] <-> ", host, inet_ntoa(*ip)); 613 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00911) 614 "%s/", inet_ntoa(This->addr)); 615 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00912) 616 "%s", inet_ntoa(This->mask)); 617#endif 618 return 1; 619 } 620#if DEBUGGING 621 else { 622 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00913) 623 "3)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(*ip)); 624 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00914) 625 "%s/", inet_ntoa(This->addr)); 626 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00915) 627 "%s", inet_ntoa(This->mask)); 628 } 629#endif 630 reqaddr = reqaddr->next; 631 } 632 } 633 634 return 0; 635} 636 637/* Return TRUE if addr represents a domain name */ 638PROXY_DECLARE(int) ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p) 639{ 640 char *addr = This->name; 641 int i; 642 643 /* Domain name must start with a '.' */ 644 if (addr[0] != '.') { 645 return 0; 646 } 647 648 /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */ 649 for (i = 0; apr_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i) { 650 continue; 651 } 652 653#if 0 654 if (addr[i] == ':') { 655 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 656 "@@@@ handle optional port in proxy_is_domainname()"); 657 /* @@@@ handle optional port */ 658 } 659#endif 660 661 if (addr[i] != '\0') { 662 return 0; 663 } 664 665 /* Strip trailing dots */ 666 for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i) { 667 addr[i] = '\0'; 668 } 669 670 This->matcher = proxy_match_domainname; 671 return 1; 672} 673 674/* Return TRUE if host "host" is in domain "domain" */ 675static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r) 676{ 677 const char *host = proxy_get_host_of_request(r); 678 int d_len = strlen(This->name), h_len; 679 680 if (host == NULL) { /* some error was logged already */ 681 return 0; 682 } 683 684 h_len = strlen(host); 685 686 /* @@@ do this within the setup? */ 687 /* Ignore trailing dots in domain comparison: */ 688 while (d_len > 0 && This->name[d_len - 1] == '.') { 689 --d_len; 690 } 691 while (h_len > 0 && host[h_len - 1] == '.') { 692 --h_len; 693 } 694 return h_len > d_len 695 && strncasecmp(&host[h_len - d_len], This->name, d_len) == 0; 696} 697 698/* Return TRUE if host represents a host name */ 699PROXY_DECLARE(int) ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p) 700{ 701 struct apr_sockaddr_t *addr; 702 char *host = This->name; 703 int i; 704 705 /* Host names must not start with a '.' */ 706 if (host[0] == '.') { 707 return 0; 708 } 709 /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */ 710 for (i = 0; apr_isalnum(host[i]) || host[i] == '-' || host[i] == '.'; ++i); 711 712 if (host[i] != '\0' || apr_sockaddr_info_get(&addr, host, APR_UNSPEC, 0, 0, p) != APR_SUCCESS) { 713 return 0; 714 } 715 716 This->hostaddr = addr; 717 718 /* Strip trailing dots */ 719 for (i = strlen(host) - 1; i > 0 && host[i] == '.'; --i) { 720 host[i] = '\0'; 721 } 722 723 This->matcher = proxy_match_hostname; 724 return 1; 725} 726 727/* Return TRUE if host "host" is equal to host2 "host2" */ 728static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r) 729{ 730 char *host = This->name; 731 const char *host2 = proxy_get_host_of_request(r); 732 int h2_len; 733 int h1_len; 734 735 if (host == NULL || host2 == NULL) { 736 return 0; /* oops! */ 737 } 738 739 h2_len = strlen(host2); 740 h1_len = strlen(host); 741 742#if 0 743 struct apr_sockaddr_t *addr = *This->hostaddr; 744 745 /* Try to deal with multiple IP addr's for a host */ 746 while (addr) { 747 if (addr->ipaddr_ptr == ? ? ? ? ? ? ? ? ? ? ? ? ?) 748 return 1; 749 addr = addr->next; 750 } 751#endif 752 753 /* Ignore trailing dots in host2 comparison: */ 754 while (h2_len > 0 && host2[h2_len - 1] == '.') { 755 --h2_len; 756 } 757 while (h1_len > 0 && host[h1_len - 1] == '.') { 758 --h1_len; 759 } 760 return h1_len == h2_len 761 && strncasecmp(host, host2, h1_len) == 0; 762} 763 764/* Return TRUE if addr is to be matched as a word */ 765PROXY_DECLARE(int) ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p) 766{ 767 This->matcher = proxy_match_word; 768 return 1; 769} 770 771/* Return TRUE if string "str2" occurs literally in "str1" */ 772static int proxy_match_word(struct dirconn_entry *This, request_rec *r) 773{ 774 const char *host = proxy_get_host_of_request(r); 775 return host != NULL && ap_strstr_c(host, This->name) != NULL; 776} 777 778/* Backwards-compatible interface. */ 779PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf, 780 apr_sockaddr_t *uri_addr) 781{ 782 return ap_proxy_checkproxyblock2(r, conf, uri_addr->hostname, uri_addr); 783} 784 785#define MAX_IP_STR_LEN (46) 786 787PROXY_DECLARE(int) ap_proxy_checkproxyblock2(request_rec *r, proxy_server_conf *conf, 788 const char *hostname, apr_sockaddr_t *addr) 789{ 790 int j; 791 792 /* XXX FIXME: conf->noproxies->elts is part of an opaque structure */ 793 for (j = 0; j < conf->noproxies->nelts; j++) { 794 struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts; 795 struct apr_sockaddr_t *conf_addr; 796 797 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, 798 "checking remote machine [%s] against [%s]", 799 hostname, npent[j].name); 800 if (ap_strstr_c(hostname, npent[j].name) || npent[j].name[0] == '*') { 801 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(00916) 802 "connect to remote machine %s blocked: name %s " 803 "matched", hostname, npent[j].name); 804 return HTTP_FORBIDDEN; 805 } 806 807 /* No IP address checks if no IP address was passed in, 808 * i.e. the forward address proxy case, where this server does 809 * not resolve the hostname. */ 810 if (!addr) 811 continue; 812 813 for (conf_addr = npent[j].addr; conf_addr; conf_addr = conf_addr->next) { 814 char caddr[MAX_IP_STR_LEN], uaddr[MAX_IP_STR_LEN]; 815 apr_sockaddr_t *uri_addr; 816 817 if (apr_sockaddr_ip_getbuf(caddr, sizeof caddr, conf_addr)) 818 continue; 819 820 for (uri_addr = addr; uri_addr; uri_addr = uri_addr->next) { 821 if (apr_sockaddr_ip_getbuf(uaddr, sizeof uaddr, uri_addr)) 822 continue; 823 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, 824 "ProxyBlock comparing %s and %s", caddr, uaddr); 825 if (!strcmp(caddr, uaddr)) { 826 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(00917) 827 "connect to remote machine %s blocked: " 828 "IP %s matched", hostname, caddr); 829 return HTTP_FORBIDDEN; 830 } 831 } 832 } 833 } 834 835 return OK; 836} 837 838/* set up the minimal filter set */ 839PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r) 840{ 841 ap_add_input_filter("HTTP_IN", NULL, r, c); 842 return OK; 843} 844 845PROXY_DECLARE(const char *) ap_proxy_location_reverse_map(request_rec *r, 846 proxy_dir_conf *conf, const char *url) 847{ 848 proxy_req_conf *rconf; 849 struct proxy_alias *ent; 850 int i, l1, l2; 851 char *u; 852 853 /* 854 * XXX FIXME: Make sure this handled the ambiguous case of the :<PORT> 855 * after the hostname 856 * XXX FIXME: Ensure the /uri component is a case sensitive match 857 */ 858 if (r->proxyreq != PROXYREQ_REVERSE) { 859 return url; 860 } 861 862 l1 = strlen(url); 863 if (conf->interpolate_env == 1) { 864 rconf = ap_get_module_config(r->request_config, &proxy_module); 865 ent = (struct proxy_alias *)rconf->raliases->elts; 866 } 867 else { 868 ent = (struct proxy_alias *)conf->raliases->elts; 869 } 870 for (i = 0; i < conf->raliases->nelts; i++) { 871 proxy_server_conf *sconf = (proxy_server_conf *) 872 ap_get_module_config(r->server->module_config, &proxy_module); 873 proxy_balancer *balancer; 874 const char *real = ent[i].real; 875 /* 876 * First check if mapping against a balancer and see 877 * if we have such a entity. If so, then we need to 878 * find the particulars of the actual worker which may 879 * or may not be the right one... basically, we need 880 * to find which member actually handled this request. 881 */ 882 if (ap_proxy_valid_balancer_name((char *)real, 0) && 883 (balancer = ap_proxy_get_balancer(r->pool, sconf, real, 1))) { 884 int n, l3 = 0; 885 proxy_worker **worker = (proxy_worker **)balancer->workers->elts; 886 const char *urlpart = ap_strchr_c(real + sizeof(BALANCER_PREFIX) - 1, '/'); 887 if (urlpart) { 888 if (!urlpart[1]) 889 urlpart = NULL; 890 else 891 l3 = strlen(urlpart); 892 } 893 /* The balancer comparison is a bit trickier. Given the context 894 * BalancerMember balancer://alias http://example.com/foo 895 * ProxyPassReverse /bash balancer://alias/bar 896 * translate url http://example.com/foo/bar/that to /bash/that 897 */ 898 for (n = 0; n < balancer->workers->nelts; n++) { 899 l2 = strlen((*worker)->s->name); 900 if (urlpart) { 901 /* urlpart (l3) assuredly starts with its own '/' */ 902 if ((*worker)->s->name[l2 - 1] == '/') 903 --l2; 904 if (l1 >= l2 + l3 905 && strncasecmp((*worker)->s->name, url, l2) == 0 906 && strncmp(urlpart, url + l2, l3) == 0) { 907 u = apr_pstrcat(r->pool, ent[i].fake, &url[l2 + l3], 908 NULL); 909 return ap_is_url(u) ? u : ap_construct_url(r->pool, u, r); 910 } 911 } 912 else if (l1 >= l2 && strncasecmp((*worker)->s->name, url, l2) == 0) { 913 /* edge case where fake is just "/"... avoid double slash */ 914 if ((ent[i].fake[0] == '/') && (ent[i].fake[1] == 0) && (url[l2] == '/')) { 915 u = apr_pstrdup(r->pool, &url[l2]); 916 } else { 917 u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL); 918 } 919 return ap_is_url(u) ? u : ap_construct_url(r->pool, u, r); 920 } 921 worker++; 922 } 923 } 924 else { 925 const char *part = url; 926 l2 = strlen(real); 927 if (real[0] == '/') { 928 part = ap_strstr_c(url, "://"); 929 if (part) { 930 part = ap_strchr_c(part+3, '/'); 931 if (part) { 932 l1 = strlen(part); 933 } 934 else { 935 part = url; 936 } 937 } 938 else { 939 part = url; 940 } 941 } 942 if (l1 >= l2 && strncasecmp(real, part, l2) == 0) { 943 u = apr_pstrcat(r->pool, ent[i].fake, &part[l2], NULL); 944 return ap_is_url(u) ? u : ap_construct_url(r->pool, u, r); 945 } 946 } 947 } 948 949 return url; 950} 951 952/* 953 * Cookies are a bit trickier to match: we've got two substrings to worry 954 * about, and we can't just find them with strstr 'cos of case. Regexp 955 * matching would be an easy fix, but for better consistency with all the 956 * other matches we'll refrain and use apr_strmatch to find path=/domain= 957 * and stick to plain strings for the config values. 958 */ 959PROXY_DECLARE(const char *) ap_proxy_cookie_reverse_map(request_rec *r, 960 proxy_dir_conf *conf, const char *str) 961{ 962 proxy_req_conf *rconf = ap_get_module_config(r->request_config, 963 &proxy_module); 964 struct proxy_alias *ent; 965 apr_size_t len = strlen(str); 966 const char *newpath = NULL; 967 const char *newdomain = NULL; 968 const char *pathp; 969 const char *domainp; 970 const char *pathe = NULL; 971 const char *domaine = NULL; 972 apr_size_t l1, l2, poffs = 0, doffs = 0; 973 int i; 974 int ddiff = 0; 975 int pdiff = 0; 976 char *ret; 977 978 if (r->proxyreq != PROXYREQ_REVERSE) { 979 return str; 980 } 981 982 /* 983 * Find the match and replacement, but save replacing until we've done 984 * both path and domain so we know the new strlen 985 */ 986 if ((pathp = apr_strmatch(ap_proxy_strmatch_path, str, len)) != NULL) { 987 pathp += 5; 988 poffs = pathp - str; 989 pathe = ap_strchr_c(pathp, ';'); 990 l1 = pathe ? (pathe - pathp) : strlen(pathp); 991 pathe = pathp + l1 ; 992 if (conf->interpolate_env == 1) { 993 ent = (struct proxy_alias *)rconf->cookie_paths->elts; 994 } 995 else { 996 ent = (struct proxy_alias *)conf->cookie_paths->elts; 997 } 998 for (i = 0; i < conf->cookie_paths->nelts; i++) { 999 l2 = strlen(ent[i].fake); 1000 if (l1 >= l2 && strncmp(ent[i].fake, pathp, l2) == 0) { 1001 newpath = ent[i].real; 1002 pdiff = strlen(newpath) - l1; 1003 break; 1004 } 1005 } 1006 } 1007 1008 if ((domainp = apr_strmatch(ap_proxy_strmatch_domain, str, len)) != NULL) { 1009 domainp += 7; 1010 doffs = domainp - str; 1011 domaine = ap_strchr_c(domainp, ';'); 1012 l1 = domaine ? (domaine - domainp) : strlen(domainp); 1013 domaine = domainp + l1; 1014 if (conf->interpolate_env == 1) { 1015 ent = (struct proxy_alias *)rconf->cookie_domains->elts; 1016 } 1017 else { 1018 ent = (struct proxy_alias *)conf->cookie_domains->elts; 1019 } 1020 for (i = 0; i < conf->cookie_domains->nelts; i++) { 1021 l2 = strlen(ent[i].fake); 1022 if (l1 >= l2 && strncasecmp(ent[i].fake, domainp, l2) == 0) { 1023 newdomain = ent[i].real; 1024 ddiff = strlen(newdomain) - l1; 1025 break; 1026 } 1027 } 1028 } 1029 1030 if (newpath) { 1031 ret = apr_palloc(r->pool, len + pdiff + ddiff + 1); 1032 l1 = strlen(newpath); 1033 if (newdomain) { 1034 l2 = strlen(newdomain); 1035 if (doffs > poffs) { 1036 memcpy(ret, str, poffs); 1037 memcpy(ret + poffs, newpath, l1); 1038 memcpy(ret + poffs + l1, pathe, domainp - pathe); 1039 memcpy(ret + doffs + pdiff, newdomain, l2); 1040 strcpy(ret + doffs + pdiff + l2, domaine); 1041 } 1042 else { 1043 memcpy(ret, str, doffs) ; 1044 memcpy(ret + doffs, newdomain, l2); 1045 memcpy(ret + doffs + l2, domaine, pathp - domaine); 1046 memcpy(ret + poffs + ddiff, newpath, l1); 1047 strcpy(ret + poffs + ddiff + l1, pathe); 1048 } 1049 } 1050 else { 1051 memcpy(ret, str, poffs); 1052 memcpy(ret + poffs, newpath, l1); 1053 strcpy(ret + poffs + l1, pathe); 1054 } 1055 } 1056 else { 1057 if (newdomain) { 1058 ret = apr_palloc(r->pool, len + pdiff + ddiff + 1); 1059 l2 = strlen(newdomain); 1060 memcpy(ret, str, doffs); 1061 memcpy(ret + doffs, newdomain, l2); 1062 strcpy(ret + doffs+l2, domaine); 1063 } 1064 else { 1065 ret = (char *)str; /* no change */ 1066 } 1067 } 1068 1069 return ret; 1070} 1071 1072/* 1073 * BALANCER related... 1074 */ 1075 1076/* 1077 * verifies that the balancer name conforms to standards. 1078 */ 1079PROXY_DECLARE(int) ap_proxy_valid_balancer_name(char *name, int i) 1080{ 1081 if (!i) 1082 i = sizeof(BALANCER_PREFIX)-1; 1083 return (!strncasecmp(name, BALANCER_PREFIX, i)); 1084} 1085 1086 1087PROXY_DECLARE(proxy_balancer *) ap_proxy_get_balancer(apr_pool_t *p, 1088 proxy_server_conf *conf, 1089 const char *url, 1090 int care) 1091{ 1092 proxy_balancer *balancer; 1093 char *c, *uri = apr_pstrdup(p, url); 1094 int i; 1095 proxy_hashes hash; 1096 1097 c = strchr(uri, ':'); 1098 if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') { 1099 return NULL; 1100 } 1101 /* remove path from uri */ 1102 if ((c = strchr(c + 3, '/'))) { 1103 *c = '\0'; 1104 } 1105 ap_str_tolower(uri); 1106 hash.def = ap_proxy_hashfunc(uri, PROXY_HASHFUNC_DEFAULT); 1107 hash.fnv = ap_proxy_hashfunc(uri, PROXY_HASHFUNC_FNV); 1108 balancer = (proxy_balancer *)conf->balancers->elts; 1109 for (i = 0; i < conf->balancers->nelts; i++) { 1110 if (balancer->hash.def == hash.def && balancer->hash.fnv == hash.fnv) { 1111 if (!care || !balancer->s->inactive) { 1112 return balancer; 1113 } 1114 } 1115 balancer++; 1116 } 1117 return NULL; 1118} 1119 1120 1121PROXY_DECLARE(char *) ap_proxy_update_balancer(apr_pool_t *p, 1122 proxy_balancer *balancer, 1123 const char *url) 1124{ 1125 apr_uri_t puri; 1126 if (apr_uri_parse(p, url, &puri) != APR_SUCCESS) { 1127 return apr_psprintf(p, "unable to parse: %s", url); 1128 } 1129 if (puri.path && PROXY_STRNCPY(balancer->s->vpath, puri.path) != APR_SUCCESS) { 1130 return apr_psprintf(p, "balancer %s front-end virtual-path (%s) too long", 1131 balancer->s->name, puri.path); 1132 } 1133 if (puri.hostname && PROXY_STRNCPY(balancer->s->vhost, puri.hostname) != APR_SUCCESS) { 1134 return apr_psprintf(p, "balancer %s front-end vhost name (%s) too long", 1135 balancer->s->name, puri.hostname); 1136 } 1137 return NULL; 1138} 1139 1140#define PROXY_UNSET_NONCE '\n' 1141 1142PROXY_DECLARE(char *) ap_proxy_define_balancer(apr_pool_t *p, 1143 proxy_balancer **balancer, 1144 proxy_server_conf *conf, 1145 const char *url, 1146 const char *alias, 1147 int do_malloc) 1148{ 1149 proxy_balancer_method *lbmethod; 1150 proxy_balancer_shared *bshared; 1151 char *c, *q, *uri = apr_pstrdup(p, url); 1152 const char *sname; 1153 1154 /* We should never get here without a valid BALANCER_PREFIX... */ 1155 1156 c = strchr(uri, ':'); 1157 if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') 1158 return "Bad syntax for a balancer name"; 1159 /* remove path from uri */ 1160 if ((q = strchr(c + 3, '/'))) 1161 *q = '\0'; 1162 1163 ap_str_tolower(uri); 1164 *balancer = apr_array_push(conf->balancers); 1165 memset(*balancer, 0, sizeof(proxy_balancer)); 1166 1167 /* 1168 * NOTE: The default method is byrequests - if it doesn't 1169 * exist, that's OK at this time. We check when we share and sync 1170 */ 1171 lbmethod = ap_lookup_provider(PROXY_LBMETHOD, "byrequests", "0"); 1172 1173 (*balancer)->workers = apr_array_make(p, 5, sizeof(proxy_worker *)); 1174 (*balancer)->gmutex = NULL; 1175 (*balancer)->tmutex = NULL; 1176 (*balancer)->lbmethod = lbmethod; 1177 1178 if (do_malloc) 1179 bshared = ap_malloc(sizeof(proxy_balancer_shared)); 1180 else 1181 bshared = apr_palloc(p, sizeof(proxy_balancer_shared)); 1182 1183 memset(bshared, 0, sizeof(proxy_balancer_shared)); 1184 1185 bshared->was_malloced = (do_malloc != 0); 1186 PROXY_STRNCPY(bshared->lbpname, "byrequests"); 1187 if (PROXY_STRNCPY(bshared->name, uri) != APR_SUCCESS) { 1188 return apr_psprintf(p, "balancer name (%s) too long", uri); 1189 } 1190 /* 1191 * We do the below for verification. The real sname will be 1192 * done post_config 1193 */ 1194 ap_pstr2_alnum(p, bshared->name + sizeof(BALANCER_PREFIX) - 1, 1195 &sname); 1196 sname = apr_pstrcat(p, conf->id, "_", sname, NULL); 1197 if (PROXY_STRNCPY(bshared->sname, sname) != APR_SUCCESS) { 1198 return apr_psprintf(p, "balancer safe-name (%s) too long", sname); 1199 } 1200 bshared->hash.def = ap_proxy_hashfunc(bshared->name, PROXY_HASHFUNC_DEFAULT); 1201 bshared->hash.fnv = ap_proxy_hashfunc(bshared->name, PROXY_HASHFUNC_FNV); 1202 (*balancer)->hash = bshared->hash; 1203 1204 bshared->forcerecovery = 1; 1205 bshared->sticky_separator = '.'; 1206 *bshared->nonce = PROXY_UNSET_NONCE; /* impossible valid input */ 1207 1208 (*balancer)->s = bshared; 1209 (*balancer)->sconf = conf; 1210 1211 return ap_proxy_update_balancer(p, *balancer, alias); 1212} 1213 1214/* 1215 * Create an already defined balancer and free up memory. 1216 */ 1217PROXY_DECLARE(apr_status_t) ap_proxy_share_balancer(proxy_balancer *balancer, 1218 proxy_balancer_shared *shm, 1219 int i) 1220{ 1221 apr_status_t rv = APR_SUCCESS; 1222 proxy_balancer_method *lbmethod; 1223 char *action = "copying"; 1224 if (!shm || !balancer->s) 1225 return APR_EINVAL; 1226 1227 if ((balancer->s->hash.def != shm->hash.def) || 1228 (balancer->s->hash.fnv != shm->hash.fnv)) { 1229 memcpy(shm, balancer->s, sizeof(proxy_balancer_shared)); 1230 if (balancer->s->was_malloced) 1231 free(balancer->s); 1232 } else { 1233 action = "re-using"; 1234 } 1235 balancer->s = shm; 1236 balancer->s->index = i; 1237 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02337) 1238 "%s shm[%d] (0x%pp) for %s", action, i, (void *)shm, 1239 balancer->s->name); 1240 /* the below should always succeed */ 1241 lbmethod = ap_lookup_provider(PROXY_LBMETHOD, balancer->s->lbpname, "0"); 1242 if (lbmethod) { 1243 balancer->lbmethod = lbmethod; 1244 } else { 1245 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, ap_server_conf, APLOGNO(02432) 1246 "Cannot find LB Method: %s", balancer->s->lbpname); 1247 return APR_EINVAL; 1248 } 1249 if (*balancer->s->nonce == PROXY_UNSET_NONCE) { 1250 char nonce[APR_UUID_FORMATTED_LENGTH + 1]; 1251 apr_uuid_t uuid; 1252 /* Retrieve a UUID and store the nonce for the lifetime of 1253 * the process. 1254 */ 1255 apr_uuid_get(&uuid); 1256 apr_uuid_format(nonce, &uuid); 1257 rv = PROXY_STRNCPY(balancer->s->nonce, nonce); 1258 } 1259 return rv; 1260} 1261 1262PROXY_DECLARE(apr_status_t) ap_proxy_initialize_balancer(proxy_balancer *balancer, server_rec *s, apr_pool_t *p) 1263{ 1264 apr_status_t rv = APR_SUCCESS; 1265 ap_slotmem_provider_t *storage = balancer->storage; 1266 apr_size_t size; 1267 unsigned int num; 1268 1269 if (!storage) { 1270 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(00918) 1271 "no provider for %s", balancer->s->name); 1272 return APR_EGENERAL; 1273 } 1274 /* 1275 * for each balancer we need to init the global 1276 * mutex and then attach to the shared worker shm 1277 */ 1278 if (!balancer->gmutex) { 1279 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(00919) 1280 "no mutex %s", balancer->s->name); 1281 return APR_EGENERAL; 1282 } 1283 1284 /* Re-open the mutex for the child. */ 1285 rv = apr_global_mutex_child_init(&(balancer->gmutex), 1286 apr_global_mutex_lockfile(balancer->gmutex), 1287 p); 1288 if (rv != APR_SUCCESS) { 1289 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(00920) 1290 "Failed to reopen mutex %s in child", 1291 balancer->s->name); 1292 return rv; 1293 } 1294 1295 /* now attach */ 1296 storage->attach(&(balancer->wslot), balancer->s->sname, &size, &num, p); 1297 if (!balancer->wslot) { 1298 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(00921) "slotmem_attach failed"); 1299 return APR_EGENERAL; 1300 } 1301 if (balancer->lbmethod && balancer->lbmethod->reset) 1302 balancer->lbmethod->reset(balancer, s); 1303 1304 if (balancer->tmutex == NULL) { 1305 rv = apr_thread_mutex_create(&(balancer->tmutex), APR_THREAD_MUTEX_DEFAULT, p); 1306 if (rv != APR_SUCCESS) { 1307 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(00922) 1308 "can not create balancer thread mutex"); 1309 return rv; 1310 } 1311 } 1312 return APR_SUCCESS; 1313} 1314 1315/* 1316 * CONNECTION related... 1317 */ 1318 1319static apr_status_t conn_pool_cleanup(void *theworker) 1320{ 1321 proxy_worker *worker = (proxy_worker *)theworker; 1322 if (worker->cp->res) { 1323 worker->cp->pool = NULL; 1324 } 1325 return APR_SUCCESS; 1326} 1327 1328static void init_conn_pool(apr_pool_t *p, proxy_worker *worker) 1329{ 1330 apr_pool_t *pool; 1331 proxy_conn_pool *cp; 1332 1333 /* 1334 * Create a connection pool's subpool. 1335 * This pool is used for connection recycling. 1336 * Once the worker is added it is never removed but 1337 * it can be disabled. 1338 */ 1339 apr_pool_create(&pool, p); 1340 apr_pool_tag(pool, "proxy_worker_cp"); 1341 /* 1342 * Alloc from the same pool as worker. 1343 * proxy_conn_pool is permanently attached to the worker. 1344 */ 1345 cp = (proxy_conn_pool *)apr_pcalloc(p, sizeof(proxy_conn_pool)); 1346 cp->pool = pool; 1347 worker->cp = cp; 1348} 1349 1350static apr_status_t connection_cleanup(void *theconn) 1351{ 1352 proxy_conn_rec *conn = (proxy_conn_rec *)theconn; 1353 proxy_worker *worker = conn->worker; 1354 1355 /* 1356 * If the connection pool is NULL the worker 1357 * cleanup has been run. Just return. 1358 */ 1359 if (!worker->cp) { 1360 return APR_SUCCESS; 1361 } 1362 1363 if (conn->r) { 1364 apr_pool_destroy(conn->r->pool); 1365 conn->r = NULL; 1366 } 1367 1368 /* Sanity check: Did we already return the pooled connection? */ 1369 if (conn->inreslist) { 1370 ap_log_perror(APLOG_MARK, APLOG_ERR, 0, conn->pool, APLOGNO(00923) 1371 "Pooled connection 0x%pp for worker %s has been" 1372 " already returned to the connection pool.", conn, 1373 ap_proxy_worker_name(conn->pool, worker)); 1374 return APR_SUCCESS; 1375 } 1376 1377 /* determine if the connection need to be closed */ 1378 if (conn->close || !worker->s->is_address_reusable || worker->s->disablereuse) { 1379 apr_pool_t *p = conn->pool; 1380 apr_pool_clear(p); 1381 conn = apr_pcalloc(p, sizeof(proxy_conn_rec)); 1382 conn->pool = p; 1383 conn->worker = worker; 1384 apr_pool_create(&(conn->scpool), p); 1385 apr_pool_tag(conn->scpool, "proxy_conn_scpool"); 1386 } 1387 1388 if (worker->s->hmax && worker->cp->res) { 1389 conn->inreslist = 1; 1390 apr_reslist_release(worker->cp->res, (void *)conn); 1391 } 1392 else 1393 { 1394 worker->cp->conn = conn; 1395 } 1396 1397 /* Always return the SUCCESS */ 1398 return APR_SUCCESS; 1399} 1400 1401static void socket_cleanup(proxy_conn_rec *conn) 1402{ 1403 conn->sock = NULL; 1404 conn->connection = NULL; 1405 apr_pool_clear(conn->scpool); 1406} 1407 1408PROXY_DECLARE(apr_status_t) ap_proxy_ssl_connection_cleanup(proxy_conn_rec *conn, 1409 request_rec *r) 1410{ 1411 apr_bucket_brigade *bb; 1412 apr_status_t rv; 1413 1414 /* 1415 * If we have an existing SSL connection it might be possible that the 1416 * server sent some SSL message we have not read so far (e.g. an SSL 1417 * shutdown message if the server closed the keepalive connection while 1418 * the connection was held unused in our pool). 1419 * So ensure that if present (=> APR_NONBLOCK_READ) it is read and 1420 * processed. We don't expect any data to be in the returned brigade. 1421 */ 1422 if (conn->sock && conn->connection) { 1423 bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); 1424 rv = ap_get_brigade(conn->connection->input_filters, bb, 1425 AP_MODE_READBYTES, APR_NONBLOCK_READ, 1426 HUGE_STRING_LEN); 1427 if ((rv != APR_SUCCESS) && !APR_STATUS_IS_EAGAIN(rv)) { 1428 socket_cleanup(conn); 1429 } 1430 if (!APR_BRIGADE_EMPTY(bb)) { 1431 apr_off_t len; 1432 1433 rv = apr_brigade_length(bb, 0, &len); 1434 ap_log_rerror(APLOG_MARK, APLOG_TRACE3, rv, r, 1435 "SSL cleanup brigade contained %" 1436 APR_OFF_T_FMT " bytes of data.", len); 1437 } 1438 apr_brigade_destroy(bb); 1439 } 1440 return APR_SUCCESS; 1441} 1442 1443/* reslist constructor */ 1444static apr_status_t connection_constructor(void **resource, void *params, 1445 apr_pool_t *pool) 1446{ 1447 apr_pool_t *ctx; 1448 apr_pool_t *scpool; 1449 proxy_conn_rec *conn; 1450 proxy_worker *worker = (proxy_worker *)params; 1451 1452 /* 1453 * Create the subpool for each connection 1454 * This keeps the memory consumption constant 1455 * when disconnecting from backend. 1456 */ 1457 apr_pool_create(&ctx, pool); 1458 apr_pool_tag(ctx, "proxy_conn_pool"); 1459 /* 1460 * Create another subpool that manages the data for the 1461 * socket and the connection member of the proxy_conn_rec struct as we 1462 * destroy this data more frequently than other data in the proxy_conn_rec 1463 * struct like hostname and addr (at least in the case where we have 1464 * keepalive connections that timed out). 1465 */ 1466 apr_pool_create(&scpool, ctx); 1467 apr_pool_tag(scpool, "proxy_conn_scpool"); 1468 conn = apr_pcalloc(ctx, sizeof(proxy_conn_rec)); 1469 1470 conn->pool = ctx; 1471 conn->scpool = scpool; 1472 conn->worker = worker; 1473 conn->inreslist = 1; 1474 *resource = conn; 1475 1476 return APR_SUCCESS; 1477} 1478 1479/* reslist destructor */ 1480static apr_status_t connection_destructor(void *resource, void *params, 1481 apr_pool_t *pool) 1482{ 1483 proxy_conn_rec *conn = (proxy_conn_rec *)resource; 1484 1485 /* Destroy the pool only if not called from reslist_destroy */ 1486 if (conn->worker->cp->pool) { 1487 apr_pool_destroy(conn->pool); 1488 } 1489 1490 return APR_SUCCESS; 1491} 1492 1493/* 1494 * WORKER related... 1495 */ 1496 1497PROXY_DECLARE(char *) ap_proxy_worker_name(apr_pool_t *p, 1498 proxy_worker *worker) 1499{ 1500 if (!(*worker->s->uds_path) || !p) { 1501 /* just in case */ 1502 return worker->s->name; 1503 } 1504 return apr_pstrcat(p, "unix:", worker->s->uds_path, "|", worker->s->name, NULL); 1505} 1506 1507PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p, 1508 proxy_balancer *balancer, 1509 proxy_server_conf *conf, 1510 const char *url) 1511{ 1512 proxy_worker *worker; 1513 proxy_worker *max_worker = NULL; 1514 int max_match = 0; 1515 int url_length; 1516 int min_match; 1517 int worker_name_length; 1518 const char *c; 1519 char *url_copy; 1520 int i; 1521 1522 if (!url) { 1523 return NULL; 1524 } 1525 1526 c = ap_strchr_c(url, ':'); 1527 if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') { 1528 return NULL; 1529 } 1530 1531 url_length = strlen(url); 1532 url_copy = apr_pstrmemdup(p, url, url_length); 1533 1534 /* 1535 * We need to find the start of the path and 1536 * therefore we know the length of the scheme://hostname/ 1537 * part to we can force-lowercase everything up to 1538 * the start of the path. 1539 */ 1540 c = ap_strchr_c(c+3, '/'); 1541 if (c) { 1542 char *pathstart; 1543 pathstart = url_copy + (c - url); 1544 *pathstart = '\0'; 1545 ap_str_tolower(url_copy); 1546 min_match = strlen(url_copy); 1547 *pathstart = '/'; 1548 } 1549 else { 1550 ap_str_tolower(url_copy); 1551 min_match = strlen(url_copy); 1552 } 1553 /* 1554 * Do a "longest match" on the worker name to find the worker that 1555 * fits best to the URL, but keep in mind that we must have at least 1556 * a minimum matching of length min_match such that 1557 * scheme://hostname[:port] matches between worker and url. 1558 */ 1559 1560 if (balancer) { 1561 proxy_worker **workers = (proxy_worker **)balancer->workers->elts; 1562 for (i = 0; i < balancer->workers->nelts; i++, workers++) { 1563 worker = *workers; 1564 if ( ((worker_name_length = strlen(worker->s->name)) <= url_length) 1565 && (worker_name_length >= min_match) 1566 && (worker_name_length > max_match) 1567 && (strncmp(url_copy, worker->s->name, worker_name_length) == 0) ) { 1568 max_worker = worker; 1569 max_match = worker_name_length; 1570 } 1571 1572 } 1573 } else { 1574 worker = (proxy_worker *)conf->workers->elts; 1575 for (i = 0; i < conf->workers->nelts; i++, worker++) { 1576 if ( ((worker_name_length = strlen(worker->s->name)) <= url_length) 1577 && (worker_name_length >= min_match) 1578 && (worker_name_length > max_match) 1579 && (strncmp(url_copy, worker->s->name, worker_name_length) == 0) ) { 1580 max_worker = worker; 1581 max_match = worker_name_length; 1582 } 1583 } 1584 } 1585 1586 return max_worker; 1587} 1588 1589/* 1590 * To create a worker from scratch first we define the 1591 * specifics of the worker; this is all local data. 1592 * We then allocate space for it if data needs to be 1593 * shared. This allows for dynamic addition during 1594 * config and runtime. 1595 */ 1596PROXY_DECLARE(char *) ap_proxy_define_worker(apr_pool_t *p, 1597 proxy_worker **worker, 1598 proxy_balancer *balancer, 1599 proxy_server_conf *conf, 1600 const char *url, 1601 int do_malloc) 1602{ 1603 int rv; 1604 apr_uri_t uri, urisock; 1605 proxy_worker_shared *wshared; 1606 char *ptr, *sockpath = NULL; 1607 1608 /* 1609 * Look to see if we are using UDS: 1610 * require format: unix:/path/foo/bar.sock|http://ignored/path2/ 1611 * This results in talking http to the socket at /path/foo/bar.sock 1612 */ 1613 ptr = ap_strchr((char *)url, '|'); 1614 if (ptr) { 1615 *ptr = '\0'; 1616 rv = apr_uri_parse(p, url, &urisock); 1617 if (rv == APR_SUCCESS && !strcasecmp(urisock.scheme, "unix")) { 1618 sockpath = ap_runtime_dir_relative(p, urisock.path);; 1619 url = ptr+1; /* so we get the scheme for the uds */ 1620 } 1621 else { 1622 *ptr = '|'; 1623 } 1624 } 1625 rv = apr_uri_parse(p, url, &uri); 1626 1627 if (rv != APR_SUCCESS) { 1628 return apr_pstrcat(p, "Unable to parse URL: ", url, NULL); 1629 } 1630 if (!uri.scheme) { 1631 return apr_pstrcat(p, "URL must be absolute!: ", url, NULL); 1632 } 1633 /* allow for unix:/path|http: */ 1634 if (!uri.hostname) { 1635 if (sockpath) { 1636 uri.hostname = "localhost"; 1637 } 1638 else { 1639 return apr_pstrcat(p, "URL must be absolute!: ", url, NULL); 1640 } 1641 } 1642 else { 1643 ap_str_tolower(uri.hostname); 1644 } 1645 ap_str_tolower(uri.scheme); 1646 /* 1647 * Workers can be associated w/ balancers or on their 1648 * own; ie: the generic reverse-proxy or a worker 1649 * in a simple ProxyPass statement. eg: 1650 * 1651 * ProxyPass / http://www.example.com 1652 * 1653 * in which case the worker goes in the conf slot. 1654 */ 1655 if (balancer) { 1656 proxy_worker **runtime; 1657 /* recall that we get a ptr to the ptr here */ 1658 runtime = apr_array_push(balancer->workers); 1659 *worker = *runtime = apr_palloc(p, sizeof(proxy_worker)); /* right to left baby */ 1660 /* we've updated the list of workers associated with 1661 * this balancer *locally* */ 1662 balancer->wupdated = apr_time_now(); 1663 } else if (conf) { 1664 *worker = apr_array_push(conf->workers); 1665 } else { 1666 /* we need to allocate space here */ 1667 *worker = apr_palloc(p, sizeof(proxy_worker)); 1668 } 1669 1670 memset(*worker, 0, sizeof(proxy_worker)); 1671 /* right here we just want to tuck away the worker info. 1672 * if called during config, we don't have shm setup yet, 1673 * so just note the info for later. */ 1674 if (do_malloc) 1675 wshared = ap_malloc(sizeof(proxy_worker_shared)); /* will be freed ap_proxy_share_worker */ 1676 else 1677 wshared = apr_palloc(p, sizeof(proxy_worker_shared)); 1678 1679 memset(wshared, 0, sizeof(proxy_worker_shared)); 1680 1681 ptr = apr_uri_unparse(p, &uri, APR_URI_UNP_REVEALPASSWORD); 1682 if (PROXY_STRNCPY(wshared->name, ptr) != APR_SUCCESS) { 1683 return apr_psprintf(p, "worker name (%s) too long", ptr); 1684 } 1685 if (PROXY_STRNCPY(wshared->scheme, uri.scheme) != APR_SUCCESS) { 1686 return apr_psprintf(p, "worker scheme (%s) too long", uri.scheme); 1687 } 1688 if (PROXY_STRNCPY(wshared->hostname, uri.hostname) != APR_SUCCESS) { 1689 return apr_psprintf(p, "worker hostname (%s) too long", uri.hostname); 1690 } 1691 wshared->port = uri.port; 1692 wshared->flush_packets = flush_off; 1693 wshared->flush_wait = PROXY_FLUSH_WAIT; 1694 wshared->is_address_reusable = 1; 1695 wshared->lbfactor = 1; 1696 wshared->smax = -1; 1697 wshared->hash.def = ap_proxy_hashfunc(wshared->name, PROXY_HASHFUNC_DEFAULT); 1698 wshared->hash.fnv = ap_proxy_hashfunc(wshared->name, PROXY_HASHFUNC_FNV); 1699 wshared->was_malloced = (do_malloc != 0); 1700 if (sockpath) { 1701 if (PROXY_STRNCPY(wshared->uds_path, sockpath) != APR_SUCCESS) { 1702 return apr_psprintf(p, "worker uds path (%s) too long", sockpath); 1703 } 1704 1705 } 1706 else { 1707 *wshared->uds_path = '\0'; 1708 } 1709 1710 (*worker)->hash = wshared->hash; 1711 (*worker)->context = NULL; 1712 (*worker)->cp = NULL; 1713 (*worker)->balancer = balancer; 1714 (*worker)->s = wshared; 1715 1716 return NULL; 1717} 1718 1719/* 1720 * Create an already defined worker and free up memory 1721 */ 1722PROXY_DECLARE(apr_status_t) ap_proxy_share_worker(proxy_worker *worker, proxy_worker_shared *shm, 1723 int i) 1724{ 1725 char *action = "copying"; 1726 if (!shm || !worker->s) 1727 return APR_EINVAL; 1728 1729 if ((worker->s->hash.def != shm->hash.def) || 1730 (worker->s->hash.fnv != shm->hash.fnv)) { 1731 memcpy(shm, worker->s, sizeof(proxy_worker_shared)); 1732 if (worker->s->was_malloced) 1733 free(worker->s); /* was malloced in ap_proxy_define_worker */ 1734 } else { 1735 action = "re-using"; 1736 } 1737 worker->s = shm; 1738 worker->s->index = i; 1739 { 1740 apr_pool_t *pool; 1741 apr_pool_create(&pool, ap_server_conf->process->pool); 1742 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02338) 1743 "%s shm[%d] (0x%pp) for worker: %s", action, i, (void *)shm, 1744 ap_proxy_worker_name(pool, worker)); 1745 if (pool) { 1746 apr_pool_destroy(pool); 1747 } 1748 } 1749 return APR_SUCCESS; 1750} 1751 1752PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, server_rec *s, apr_pool_t *p) 1753{ 1754 apr_status_t rv = APR_SUCCESS; 1755 int mpm_threads; 1756 1757 if (worker->s->status & PROXY_WORKER_INITIALIZED) { 1758 /* The worker is already initialized */ 1759 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00924) 1760 "worker %s shared already initialized", 1761 ap_proxy_worker_name(p, worker)); 1762 } 1763 else { 1764 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00925) 1765 "initializing worker %s shared", 1766 ap_proxy_worker_name(p, worker)); 1767 /* Set default parameters */ 1768 if (!worker->s->retry_set) { 1769 worker->s->retry = apr_time_from_sec(PROXY_WORKER_DEFAULT_RETRY); 1770 } 1771 /* By default address is reusable unless DisableReuse is set */ 1772 if (worker->s->disablereuse) { 1773 worker->s->is_address_reusable = 0; 1774 } 1775 else { 1776 worker->s->is_address_reusable = 1; 1777 } 1778 1779 ap_mpm_query(AP_MPMQ_MAX_THREADS, &mpm_threads); 1780 if (mpm_threads > 1) { 1781 /* Set hard max to no more then mpm_threads */ 1782 if (worker->s->hmax == 0 || worker->s->hmax > mpm_threads) { 1783 worker->s->hmax = mpm_threads; 1784 } 1785 if (worker->s->smax == -1 || worker->s->smax > worker->s->hmax) { 1786 worker->s->smax = worker->s->hmax; 1787 } 1788 /* Set min to be lower then smax */ 1789 if (worker->s->min > worker->s->smax) { 1790 worker->s->min = worker->s->smax; 1791 } 1792 } 1793 else { 1794 /* This will supress the apr_reslist creation */ 1795 worker->s->min = worker->s->smax = worker->s->hmax = 0; 1796 } 1797 } 1798 1799 /* What if local is init'ed and shm isn't?? Even possible? */ 1800 if (worker->local_status & PROXY_WORKER_INITIALIZED) { 1801 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00926) 1802 "worker %s local already initialized", 1803 ap_proxy_worker_name(p, worker)); 1804 } 1805 else { 1806 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00927) 1807 "initializing worker %s local", 1808 ap_proxy_worker_name(p, worker)); 1809 apr_global_mutex_lock(proxy_mutex); 1810 /* Now init local worker data */ 1811 if (worker->tmutex == NULL) { 1812 rv = apr_thread_mutex_create(&(worker->tmutex), APR_THREAD_MUTEX_DEFAULT, p); 1813 if (rv != APR_SUCCESS) { 1814 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00928) 1815 "can not create worker thread mutex"); 1816 apr_global_mutex_unlock(proxy_mutex); 1817 return rv; 1818 } 1819 } 1820 if (worker->cp == NULL) 1821 init_conn_pool(p, worker); 1822 if (worker->cp == NULL) { 1823 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00929) 1824 "can not create connection pool"); 1825 apr_global_mutex_unlock(proxy_mutex); 1826 return APR_EGENERAL; 1827 } 1828 1829 if (worker->s->hmax) { 1830 rv = apr_reslist_create(&(worker->cp->res), 1831 worker->s->min, worker->s->smax, 1832 worker->s->hmax, worker->s->ttl, 1833 connection_constructor, connection_destructor, 1834 worker, worker->cp->pool); 1835 1836 apr_pool_cleanup_register(worker->cp->pool, (void *)worker, 1837 conn_pool_cleanup, 1838 apr_pool_cleanup_null); 1839 1840 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00930) 1841 "initialized pool in child %" APR_PID_T_FMT " for (%s) min=%d max=%d smax=%d", 1842 getpid(), worker->s->hostname, worker->s->min, 1843 worker->s->hmax, worker->s->smax); 1844 1845 /* Set the acquire timeout */ 1846 if (rv == APR_SUCCESS && worker->s->acquire_set) { 1847 apr_reslist_timeout_set(worker->cp->res, worker->s->acquire); 1848 } 1849 1850 } 1851 else { 1852 void *conn; 1853 1854 rv = connection_constructor(&conn, worker, worker->cp->pool); 1855 worker->cp->conn = conn; 1856 1857 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00931) 1858 "initialized single connection worker in child %" APR_PID_T_FMT " for (%s)", 1859 getpid(), worker->s->hostname); 1860 } 1861 apr_global_mutex_unlock(proxy_mutex); 1862 1863 } 1864 if (rv == APR_SUCCESS) { 1865 worker->s->status |= (PROXY_WORKER_INITIALIZED); 1866 worker->local_status |= (PROXY_WORKER_INITIALIZED); 1867 } 1868 return rv; 1869} 1870 1871static int ap_proxy_retry_worker(const char *proxy_function, proxy_worker *worker, 1872 server_rec *s) 1873{ 1874 if (worker->s->status & PROXY_WORKER_IN_ERROR) { 1875 if (apr_time_now() > worker->s->error_time + worker->s->retry) { 1876 ++worker->s->retries; 1877 worker->s->status &= ~PROXY_WORKER_IN_ERROR; 1878 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00932) 1879 "%s: worker for (%s) has been marked for retry", 1880 proxy_function, worker->s->hostname); 1881 return OK; 1882 } 1883 else { 1884 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00933) 1885 "%s: too soon to retry worker for (%s)", 1886 proxy_function, worker->s->hostname); 1887 return DECLINED; 1888 } 1889 } 1890 else { 1891 return OK; 1892 } 1893} 1894 1895PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker, 1896 proxy_balancer **balancer, 1897 request_rec *r, 1898 proxy_server_conf *conf, char **url) 1899{ 1900 int access_status; 1901 1902 access_status = proxy_run_pre_request(worker, balancer, r, conf, url); 1903 if (access_status == DECLINED && *balancer == NULL) { 1904 *worker = ap_proxy_get_worker(r->pool, NULL, conf, *url); 1905 if (*worker) { 1906 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, 1907 "%s: found worker %s for %s", 1908 (*worker)->s->scheme, (*worker)->s->name, *url); 1909 1910 *balancer = NULL; 1911 access_status = OK; 1912 } 1913 else if (r->proxyreq == PROXYREQ_PROXY) { 1914 if (conf->forward) { 1915 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, 1916 "*: found forward proxy worker for %s", *url); 1917 *balancer = NULL; 1918 *worker = conf->forward; 1919 access_status = OK; 1920 /* 1921 * The forward worker does not keep connections alive, so 1922 * ensure that mod_proxy_http does the correct thing 1923 * regarding the Connection header in the request. 1924 */ 1925 apr_table_setn(r->subprocess_env, "proxy-nokeepalive", "1"); 1926 } 1927 } 1928 else if (r->proxyreq == PROXYREQ_REVERSE) { 1929 if (conf->reverse) { 1930 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, 1931 "*: found reverse proxy worker for %s", *url); 1932 *balancer = NULL; 1933 *worker = conf->reverse; 1934 access_status = OK; 1935 /* 1936 * The reverse worker does not keep connections alive, so 1937 * ensure that mod_proxy_http does the correct thing 1938 * regarding the Connection header in the request. 1939 */ 1940 apr_table_setn(r->subprocess_env, "proxy-nokeepalive", "1"); 1941 } 1942 } 1943 } 1944 else if (access_status == DECLINED && *balancer != NULL) { 1945 /* All the workers are busy */ 1946 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00934) 1947 "all workers are busy. Unable to serve %s", *url); 1948 access_status = HTTP_SERVICE_UNAVAILABLE; 1949 } 1950 return access_status; 1951} 1952 1953PROXY_DECLARE(int) ap_proxy_post_request(proxy_worker *worker, 1954 proxy_balancer *balancer, 1955 request_rec *r, 1956 proxy_server_conf *conf) 1957{ 1958 int access_status = OK; 1959 if (balancer) { 1960 access_status = proxy_run_post_request(worker, balancer, r, conf); 1961 if (access_status == DECLINED) { 1962 access_status = OK; /* no post_request handler available */ 1963 /* TODO: recycle direct worker */ 1964 } 1965 } 1966 1967 return access_status; 1968} 1969 1970/* DEPRECATED */ 1971PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **newsock, 1972 const char *proxy_function, 1973 apr_sockaddr_t *backend_addr, 1974 const char *backend_name, 1975 proxy_server_conf *conf, 1976 request_rec *r) 1977{ 1978 apr_status_t rv; 1979 int connected = 0; 1980 int loglevel; 1981 1982 while (backend_addr && !connected) { 1983 if ((rv = apr_socket_create(newsock, backend_addr->family, 1984 SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) { 1985 loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR; 1986 ap_log_rerror(APLOG_MARK, loglevel, rv, r, APLOGNO(00935) 1987 "%s: error creating fam %d socket for target %s", 1988 proxy_function, backend_addr->family, backend_name); 1989 /* 1990 * this could be an IPv6 address from the DNS but the 1991 * local machine won't give us an IPv6 socket; hopefully the 1992 * DNS returned an additional address to try 1993 */ 1994 backend_addr = backend_addr->next; 1995 continue; 1996 } 1997 1998 if (conf->recv_buffer_size > 0 && 1999 (rv = apr_socket_opt_set(*newsock, APR_SO_RCVBUF, 2000 conf->recv_buffer_size))) { 2001 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00936) 2002 "apr_socket_opt_set(SO_RCVBUF): Failed to set " 2003 "ProxyReceiveBufferSize, using default"); 2004 } 2005 2006 rv = apr_socket_opt_set(*newsock, APR_TCP_NODELAY, 1); 2007 if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) { 2008 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00937) 2009 "apr_socket_opt_set(APR_TCP_NODELAY): " 2010 "Failed to set"); 2011 } 2012 2013 /* Set a timeout on the socket */ 2014 if (conf->timeout_set) { 2015 apr_socket_timeout_set(*newsock, conf->timeout); 2016 } 2017 else { 2018 apr_socket_timeout_set(*newsock, r->server->timeout); 2019 } 2020 2021 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, 2022 "%s: fam %d socket created to connect to %s", 2023 proxy_function, backend_addr->family, backend_name); 2024 2025 if (conf->source_address) { 2026 rv = apr_socket_bind(*newsock, conf->source_address); 2027 if (rv != APR_SUCCESS) { 2028 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00938) 2029 "%s: failed to bind socket to local address", 2030 proxy_function); 2031 } 2032 } 2033 2034 /* make the connection out of the socket */ 2035 rv = apr_socket_connect(*newsock, backend_addr); 2036 2037 /* if an error occurred, loop round and try again */ 2038 if (rv != APR_SUCCESS) { 2039 apr_socket_close(*newsock); 2040 loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR; 2041 ap_log_rerror(APLOG_MARK, loglevel, rv, r, APLOGNO(00939) 2042 "%s: attempt to connect to %pI (%s) failed", 2043 proxy_function, backend_addr, backend_name); 2044 backend_addr = backend_addr->next; 2045 continue; 2046 } 2047 connected = 1; 2048 } 2049 return connected ? 0 : 1; 2050} 2051 2052PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function, 2053 proxy_conn_rec **conn, 2054 proxy_worker *worker, 2055 server_rec *s) 2056{ 2057 apr_status_t rv; 2058 2059 if (!PROXY_WORKER_IS_USABLE(worker)) { 2060 /* Retry the worker */ 2061 ap_proxy_retry_worker(proxy_function, worker, s); 2062 2063 if (!PROXY_WORKER_IS_USABLE(worker)) { 2064 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00940) 2065 "%s: disabled connection for (%s)", 2066 proxy_function, worker->s->hostname); 2067 return HTTP_SERVICE_UNAVAILABLE; 2068 } 2069 } 2070 2071 if (worker->s->hmax && worker->cp->res) { 2072 rv = apr_reslist_acquire(worker->cp->res, (void **)conn); 2073 } 2074 else { 2075 /* create the new connection if the previous was destroyed */ 2076 if (!worker->cp->conn) { 2077 connection_constructor((void **)conn, worker, worker->cp->pool); 2078 } 2079 else { 2080 *conn = worker->cp->conn; 2081 worker->cp->conn = NULL; 2082 } 2083 rv = APR_SUCCESS; 2084 } 2085 2086 if (rv != APR_SUCCESS) { 2087 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00941) 2088 "%s: failed to acquire connection for (%s)", 2089 proxy_function, worker->s->hostname); 2090 return HTTP_SERVICE_UNAVAILABLE; 2091 } 2092 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00942) 2093 "%s: has acquired connection for (%s)", 2094 proxy_function, worker->s->hostname); 2095 2096 (*conn)->worker = worker; 2097 (*conn)->close = 0; 2098 (*conn)->inreslist = 0; 2099 2100 if (*worker->s->uds_path) { 2101 if ((*conn)->uds_path == NULL) { 2102 /* use (*conn)->pool instead of worker->cp->pool to match lifetime */ 2103 (*conn)->uds_path = apr_pstrdup((*conn)->pool, worker->s->uds_path); 2104 } 2105 if ((*conn)->uds_path) { 2106 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02545) 2107 "%s: has determined UDS as %s", 2108 proxy_function, (*conn)->uds_path); 2109 } 2110 else { 2111 /* should never happen */ 2112 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02546) 2113 "%s: cannot determine UDS (%s)", 2114 proxy_function, worker->s->uds_path); 2115 2116 } 2117 } 2118 else { 2119 (*conn)->uds_path = NULL; 2120 } 2121 2122 2123 return OK; 2124} 2125 2126PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function, 2127 proxy_conn_rec *conn, 2128 server_rec *s) 2129{ 2130 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00943) 2131 "%s: has released connection for (%s)", 2132 proxy_function, conn->worker->s->hostname); 2133 connection_cleanup(conn); 2134 2135 return OK; 2136} 2137 2138PROXY_DECLARE(int) 2139ap_proxy_determine_connection(apr_pool_t *p, request_rec *r, 2140 proxy_server_conf *conf, 2141 proxy_worker *worker, 2142 proxy_conn_rec *conn, 2143 apr_uri_t *uri, 2144 char **url, 2145 const char *proxyname, 2146 apr_port_t proxyport, 2147 char *server_portstr, 2148 int server_portstr_size) 2149{ 2150 int server_port; 2151 apr_status_t err = APR_SUCCESS; 2152 apr_status_t uerr = APR_SUCCESS; 2153 2154 /* 2155 * Break up the URL to determine the host to connect to 2156 */ 2157 2158 /* we break the URL into host, port, uri */ 2159 if (APR_SUCCESS != apr_uri_parse(p, *url, uri)) { 2160 return ap_proxyerror(r, HTTP_BAD_REQUEST, 2161 apr_pstrcat(p,"URI cannot be parsed: ", *url, 2162 NULL)); 2163 } 2164 if (!uri->port) { 2165 uri->port = ap_proxy_port_of_scheme(uri->scheme); 2166 } 2167 2168 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00944) 2169 "connecting %s to %s:%d", *url, uri->hostname, uri->port); 2170 2171 /* 2172 * allocate these out of the specified connection pool 2173 * The scheme handler decides if this is permanent or 2174 * short living pool. 2175 */ 2176 /* are we connecting directly, or via a proxy? */ 2177 if (!proxyname) { 2178 *url = apr_pstrcat(p, uri->path, uri->query ? "?" : "", 2179 uri->query ? uri->query : "", 2180 uri->fragment ? "#" : "", 2181 uri->fragment ? uri->fragment : "", NULL); 2182 } 2183 /* 2184 * Figure out if our passed in proxy_conn_rec has a usable 2185 * address cached. 2186 * 2187 * TODO: Handle this much better... 2188 * 2189 * XXX: If generic workers are ever address-reusable, we need 2190 * to check host and port on the conn and be careful about 2191 * spilling the cached addr from the worker. 2192 */ 2193 if (!conn->hostname || !worker->s->is_address_reusable || 2194 worker->s->disablereuse || *worker->s->uds_path) { 2195 if (proxyname) { 2196 conn->hostname = apr_pstrdup(conn->pool, proxyname); 2197 conn->port = proxyport; 2198 /* 2199 * If we have a forward proxy and the protocol is HTTPS, 2200 * then we need to prepend a HTTP CONNECT request before 2201 * sending our actual HTTPS requests. 2202 * Save our real backend data for using it later during HTTP CONNECT. 2203 */ 2204 if (conn->is_ssl) { 2205 const char *proxy_auth; 2206 2207 forward_info *forward = apr_pcalloc(conn->pool, sizeof(forward_info)); 2208 conn->forward = forward; 2209 forward->use_http_connect = 1; 2210 forward->target_host = apr_pstrdup(conn->pool, uri->hostname); 2211 forward->target_port = uri->port; 2212 /* Do we want to pass Proxy-Authorization along? 2213 * If we haven't used it, then YES 2214 * If we have used it then MAYBE: RFC2616 says we MAY propagate it. 2215 * So let's make it configurable by env. 2216 * The logic here is the same used in mod_proxy_http. 2217 */ 2218 proxy_auth = apr_table_get(r->headers_in, "Proxy-Authorization"); 2219 if (proxy_auth != NULL && 2220 proxy_auth[0] != '\0' && 2221 r->user == NULL && /* we haven't yet authenticated */ 2222 apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) { 2223 forward->proxy_auth = apr_pstrdup(conn->pool, proxy_auth); 2224 } 2225 } 2226 } 2227 else { 2228 conn->hostname = apr_pstrdup(conn->pool, uri->hostname); 2229 conn->port = uri->port; 2230 } 2231 socket_cleanup(conn); 2232 if (!(*worker->s->uds_path) && 2233 (!worker->s->is_address_reusable || worker->s->disablereuse)) { 2234 /* 2235 * Only do a lookup if we should not reuse the backend address. 2236 * Otherwise we will look it up once for the worker. 2237 */ 2238 err = apr_sockaddr_info_get(&(conn->addr), 2239 conn->hostname, APR_UNSPEC, 2240 conn->port, 0, 2241 conn->pool); 2242 } 2243 } 2244 if (!(*worker->s->uds_path) && worker->s->is_address_reusable && !worker->s->disablereuse) { 2245 /* 2246 * Looking up the backend address for the worker only makes sense if 2247 * we can reuse the address. 2248 */ 2249 if (!worker->cp->addr) { 2250 if ((err = PROXY_THREAD_LOCK(worker)) != APR_SUCCESS) { 2251 ap_log_rerror(APLOG_MARK, APLOG_ERR, err, r, APLOGNO(00945) "lock"); 2252 return HTTP_INTERNAL_SERVER_ERROR; 2253 } 2254 2255 /* 2256 * Worker can have the single constant backend adress. 2257 * The single DNS lookup is used once per worker. 2258 * If dynamic change is needed then set the addr to NULL 2259 * inside dynamic config to force the lookup. 2260 */ 2261 err = apr_sockaddr_info_get(&(worker->cp->addr), 2262 conn->hostname, APR_UNSPEC, 2263 conn->port, 0, 2264 worker->cp->pool); 2265 conn->addr = worker->cp->addr; 2266 if ((uerr = PROXY_THREAD_UNLOCK(worker)) != APR_SUCCESS) { 2267 ap_log_rerror(APLOG_MARK, APLOG_ERR, uerr, r, APLOGNO(00946) "unlock"); 2268 } 2269 } 2270 else { 2271 conn->addr = worker->cp->addr; 2272 } 2273 } 2274 /* Close a possible existing socket if we are told to do so */ 2275 if (conn->close) { 2276 socket_cleanup(conn); 2277 conn->close = 0; 2278 } 2279 2280 if (err != APR_SUCCESS) { 2281 return ap_proxyerror(r, HTTP_BAD_GATEWAY, 2282 apr_pstrcat(p, "DNS lookup failure for: ", 2283 conn->hostname, NULL)); 2284 } 2285 2286 /* Get the server port for the Via headers */ 2287 { 2288 server_port = ap_get_server_port(r); 2289 if (ap_is_default_port(server_port, r)) { 2290 strcpy(server_portstr,""); 2291 } 2292 else { 2293 apr_snprintf(server_portstr, server_portstr_size, ":%d", 2294 server_port); 2295 } 2296 } 2297 /* check if ProxyBlock directive on this host */ 2298 if (OK != ap_proxy_checkproxyblock2(r, conf, uri->hostname, 2299 proxyname ? NULL : conn->addr)) { 2300 return ap_proxyerror(r, HTTP_FORBIDDEN, 2301 "Connect to remote machine blocked"); 2302 } 2303 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00947) 2304 "connected %s to %s:%d", *url, conn->hostname, conn->port); 2305 return OK; 2306} 2307 2308#define USE_ALTERNATE_IS_CONNECTED 1 2309 2310#if !defined(APR_MSG_PEEK) && defined(MSG_PEEK) 2311#define APR_MSG_PEEK MSG_PEEK 2312#endif 2313 2314#if USE_ALTERNATE_IS_CONNECTED && defined(APR_MSG_PEEK) 2315static int is_socket_connected(apr_socket_t *socket) 2316{ 2317 apr_pollfd_t pfds[1]; 2318 apr_status_t status; 2319 apr_int32_t nfds; 2320 2321 pfds[0].reqevents = APR_POLLIN; 2322 pfds[0].desc_type = APR_POLL_SOCKET; 2323 pfds[0].desc.s = socket; 2324 2325 do { 2326 status = apr_poll(&pfds[0], 1, &nfds, 0); 2327 } while (APR_STATUS_IS_EINTR(status)); 2328 2329 if (status == APR_SUCCESS && nfds == 1 && 2330 pfds[0].rtnevents == APR_POLLIN) { 2331 apr_sockaddr_t unused; 2332 apr_size_t len = 1; 2333 char buf[1]; 2334 /* The socket might be closed in which case 2335 * the poll will return POLLIN. 2336 * If there is no data available the socket 2337 * is closed. 2338 */ 2339 status = apr_socket_recvfrom(&unused, socket, APR_MSG_PEEK, 2340 &buf[0], &len); 2341 if (status == APR_SUCCESS && len) 2342 return 1; 2343 else 2344 return 0; 2345 } 2346 else if (APR_STATUS_IS_EAGAIN(status) || APR_STATUS_IS_TIMEUP(status)) { 2347 return 1; 2348 } 2349 return 0; 2350 2351} 2352#else 2353static int is_socket_connected(apr_socket_t *sock) 2354 2355{ 2356 apr_size_t buffer_len = 1; 2357 char test_buffer[1]; 2358 apr_status_t socket_status; 2359 apr_interval_time_t current_timeout; 2360 2361 /* save timeout */ 2362 apr_socket_timeout_get(sock, ¤t_timeout); 2363 /* set no timeout */ 2364 apr_socket_timeout_set(sock, 0); 2365 socket_status = apr_socket_recv(sock, test_buffer, &buffer_len); 2366 /* put back old timeout */ 2367 apr_socket_timeout_set(sock, current_timeout); 2368 if (APR_STATUS_IS_EOF(socket_status) 2369 || APR_STATUS_IS_ECONNRESET(socket_status)) { 2370 return 0; 2371 } 2372 else { 2373 return 1; 2374 } 2375} 2376#endif /* USE_ALTERNATE_IS_CONNECTED */ 2377 2378 2379/* 2380 * Send a HTTP CONNECT request to a forward proxy. 2381 * The proxy is given by "backend", the target server 2382 * is contained in the "forward" member of "backend". 2383 */ 2384static apr_status_t send_http_connect(proxy_conn_rec *backend, 2385 server_rec *s) 2386{ 2387 int status; 2388 apr_size_t nbytes; 2389 apr_size_t left; 2390 int complete = 0; 2391 char buffer[HUGE_STRING_LEN]; 2392 char drain_buffer[HUGE_STRING_LEN]; 2393 forward_info *forward = (forward_info *)backend->forward; 2394 int len = 0; 2395 2396 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00948) 2397 "CONNECT: sending the CONNECT request for %s:%d " 2398 "to the remote proxy %pI (%s)", 2399 forward->target_host, forward->target_port, 2400 backend->addr, backend->hostname); 2401 /* Create the CONNECT request */ 2402 nbytes = apr_snprintf(buffer, sizeof(buffer), 2403 "CONNECT %s:%d HTTP/1.0" CRLF, 2404 forward->target_host, forward->target_port); 2405 /* Add proxy authorization from the initial request if necessary */ 2406 if (forward->proxy_auth != NULL) { 2407 nbytes += apr_snprintf(buffer + nbytes, sizeof(buffer) - nbytes, 2408 "Proxy-Authorization: %s" CRLF, 2409 forward->proxy_auth); 2410 } 2411 /* Set a reasonable agent and send everything */ 2412 nbytes += apr_snprintf(buffer + nbytes, sizeof(buffer) - nbytes, 2413 "Proxy-agent: %s" CRLF CRLF, 2414 ap_get_server_banner()); 2415 apr_socket_send(backend->sock, buffer, &nbytes); 2416 2417 /* Receive the whole CONNECT response */ 2418 left = sizeof(buffer) - 1; 2419 /* Read until we find the end of the headers or run out of buffer */ 2420 do { 2421 nbytes = left; 2422 status = apr_socket_recv(backend->sock, buffer + len, &nbytes); 2423 len += nbytes; 2424 left -= nbytes; 2425 buffer[len] = '\0'; 2426 if (strstr(buffer + len - nbytes, "\r\n\r\n") != NULL) { 2427 complete = 1; 2428 break; 2429 } 2430 } while (status == APR_SUCCESS && left > 0); 2431 /* Drain what's left */ 2432 if (!complete) { 2433 nbytes = sizeof(drain_buffer) - 1; 2434 while (status == APR_SUCCESS && nbytes) { 2435 status = apr_socket_recv(backend->sock, drain_buffer, &nbytes); 2436 drain_buffer[nbytes] = '\0'; 2437 nbytes = sizeof(drain_buffer) - 1; 2438 if (strstr(drain_buffer, "\r\n\r\n") != NULL) { 2439 break; 2440 } 2441 } 2442 } 2443 2444 /* Check for HTTP_OK response status */ 2445 if (status == APR_SUCCESS) { 2446 int major, minor; 2447 /* Only scan for three character status code */ 2448 char code_str[4]; 2449 2450 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00949) 2451 "send_http_connect: response from the forward proxy: %s", 2452 buffer); 2453 2454 /* Extract the returned code */ 2455 if (sscanf(buffer, "HTTP/%u.%u %3s", &major, &minor, code_str) == 3) { 2456 status = atoi(code_str); 2457 if (status == HTTP_OK) { 2458 status = APR_SUCCESS; 2459 } 2460 else { 2461 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00950) 2462 "send_http_connect: the forward proxy returned code is '%s'", 2463 code_str); 2464 status = APR_INCOMPLETE; 2465 } 2466 } 2467 } 2468 2469 return(status); 2470} 2471 2472 2473#if APR_HAVE_SYS_UN_H 2474/* lifted from mod_proxy_fdpass.c; tweaked addrlen in connect() call */ 2475static apr_status_t socket_connect_un(apr_socket_t *sock, 2476 struct sockaddr_un *sa) 2477{ 2478 apr_status_t rv; 2479 apr_os_sock_t rawsock; 2480 apr_interval_time_t t; 2481 2482 rv = apr_os_sock_get(&rawsock, sock); 2483 if (rv != APR_SUCCESS) { 2484 return rv; 2485 } 2486 2487 rv = apr_socket_timeout_get(sock, &t); 2488 if (rv != APR_SUCCESS) { 2489 return rv; 2490 } 2491 2492 do { 2493 const socklen_t addrlen = APR_OFFSETOF(struct sockaddr_un, sun_path) 2494 + strlen(sa->sun_path) + 1; 2495 rv = connect(rawsock, (struct sockaddr*)sa, addrlen); 2496 } while (rv == -1 && errno == EINTR); 2497 2498 if ((rv == -1) && (errno == EINPROGRESS || errno == EALREADY) 2499 && (t > 0)) { 2500#if APR_MAJOR_VERSION < 2 2501 rv = apr_wait_for_io_or_timeout(NULL, sock, 0); 2502#else 2503 rv = apr_socket_wait(sock, APR_WAIT_WRITE); 2504#endif 2505 2506 if (rv != APR_SUCCESS) { 2507 return rv; 2508 } 2509 } 2510 2511 if (rv == -1 && errno != EISCONN) { 2512 return errno; 2513 } 2514 2515 return APR_SUCCESS; 2516} 2517#endif 2518 2519PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function, 2520 proxy_conn_rec *conn, 2521 proxy_worker *worker, 2522 server_rec *s) 2523{ 2524 apr_status_t rv; 2525 int connected = 0; 2526 int loglevel; 2527 apr_sockaddr_t *backend_addr = conn->addr; 2528 /* the local address to use for the outgoing connection */ 2529 apr_sockaddr_t *local_addr; 2530 apr_socket_t *newsock; 2531 void *sconf = s->module_config; 2532 proxy_server_conf *conf = 2533 (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); 2534 2535 if (conn->sock) { 2536 if (!(connected = is_socket_connected(conn->sock))) { 2537 socket_cleanup(conn); 2538 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00951) 2539 "%s: backend socket is disconnected.", 2540 proxy_function); 2541 } 2542 } 2543 while ((backend_addr || conn->uds_path) && !connected) { 2544#if APR_HAVE_SYS_UN_H 2545 if (conn->uds_path) 2546 { 2547 struct sockaddr_un sa; 2548 2549 rv = apr_socket_create(&newsock, AF_UNIX, SOCK_STREAM, 0, 2550 conn->scpool); 2551 if (rv != APR_SUCCESS) { 2552 loglevel = APLOG_ERR; 2553 ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(02453) 2554 "%s: error creating Unix domain socket for " 2555 "target %s", 2556 proxy_function, 2557 worker->s->hostname); 2558 break; 2559 } 2560 conn->connection = NULL; 2561 2562 sa.sun_family = AF_UNIX; 2563 apr_cpystrn(sa.sun_path, conn->uds_path, sizeof(sa.sun_path)); 2564 2565 rv = socket_connect_un(newsock, &sa); 2566 if (rv != APR_SUCCESS) { 2567 apr_socket_close(newsock); 2568 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(02454) 2569 "%s: attempt to connect to Unix domain socket " 2570 "%s (%s) failed", 2571 proxy_function, 2572 conn->uds_path, 2573 worker->s->hostname); 2574 break; 2575 } 2576 } 2577 else 2578#endif 2579 { 2580 if ((rv = apr_socket_create(&newsock, backend_addr->family, 2581 SOCK_STREAM, APR_PROTO_TCP, 2582 conn->scpool)) != APR_SUCCESS) { 2583 loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR; 2584 ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00952) 2585 "%s: error creating fam %d socket for " 2586 "target %s", 2587 proxy_function, 2588 backend_addr->family, 2589 worker->s->hostname); 2590 /* 2591 * this could be an IPv6 address from the DNS but the 2592 * local machine won't give us an IPv6 socket; hopefully the 2593 * DNS returned an additional address to try 2594 */ 2595 backend_addr = backend_addr->next; 2596 continue; 2597 } 2598 conn->connection = NULL; 2599 2600 if (worker->s->recv_buffer_size > 0 && 2601 (rv = apr_socket_opt_set(newsock, APR_SO_RCVBUF, 2602 worker->s->recv_buffer_size))) { 2603 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00953) 2604 "apr_socket_opt_set(SO_RCVBUF): Failed to set " 2605 "ProxyReceiveBufferSize, using default"); 2606 } 2607 2608 rv = apr_socket_opt_set(newsock, APR_TCP_NODELAY, 1); 2609 if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) { 2610 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00954) 2611 "apr_socket_opt_set(APR_TCP_NODELAY): " 2612 "Failed to set"); 2613 } 2614 2615 /* Set a timeout for connecting to the backend on the socket */ 2616 if (worker->s->conn_timeout_set) { 2617 apr_socket_timeout_set(newsock, worker->s->conn_timeout); 2618 } 2619 else if (worker->s->timeout_set) { 2620 apr_socket_timeout_set(newsock, worker->s->timeout); 2621 } 2622 else if (conf->timeout_set) { 2623 apr_socket_timeout_set(newsock, conf->timeout); 2624 } 2625 else { 2626 apr_socket_timeout_set(newsock, s->timeout); 2627 } 2628 /* Set a keepalive option */ 2629 if (worker->s->keepalive) { 2630 if ((rv = apr_socket_opt_set(newsock, 2631 APR_SO_KEEPALIVE, 1)) != APR_SUCCESS) { 2632 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00955) 2633 "apr_socket_opt_set(SO_KEEPALIVE): Failed to set" 2634 " Keepalive"); 2635 } 2636 } 2637 ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, s, 2638 "%s: fam %d socket created to connect to %s", 2639 proxy_function, backend_addr->family, worker->s->hostname); 2640 2641 if (conf->source_address_set) { 2642 local_addr = apr_pmemdup(conn->pool, conf->source_address, 2643 sizeof(apr_sockaddr_t)); 2644 local_addr->pool = conn->pool; 2645 rv = apr_socket_bind(newsock, local_addr); 2646 if (rv != APR_SUCCESS) { 2647 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00956) 2648 "%s: failed to bind socket to local address", 2649 proxy_function); 2650 } 2651 } 2652 2653 /* make the connection out of the socket */ 2654 rv = apr_socket_connect(newsock, backend_addr); 2655 2656 /* if an error occurred, loop round and try again */ 2657 if (rv != APR_SUCCESS) { 2658 apr_socket_close(newsock); 2659 loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR; 2660 ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00957) 2661 "%s: attempt to connect to %pI (%s) failed", 2662 proxy_function, 2663 backend_addr, 2664 worker->s->hostname); 2665 backend_addr = backend_addr->next; 2666 continue; 2667 } 2668 } 2669 2670 /* Set a timeout on the socket */ 2671 if (worker->s->timeout_set) { 2672 apr_socket_timeout_set(newsock, worker->s->timeout); 2673 } 2674 else if (conf->timeout_set) { 2675 apr_socket_timeout_set(newsock, conf->timeout); 2676 } 2677 else { 2678 apr_socket_timeout_set(newsock, s->timeout); 2679 } 2680 2681 conn->sock = newsock; 2682 2683 if (!conn->uds_path && conn->forward) { 2684 forward_info *forward = (forward_info *)conn->forward; 2685 /* 2686 * For HTTP CONNECT we need to prepend CONNECT request before 2687 * sending our actual HTTPS requests. 2688 */ 2689 if (forward->use_http_connect) { 2690 rv = send_http_connect(conn, s); 2691 /* If an error occurred, loop round and try again */ 2692 if (rv != APR_SUCCESS) { 2693 conn->sock = NULL; 2694 apr_socket_close(newsock); 2695 loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR; 2696 ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00958) 2697 "%s: attempt to connect to %s:%d " 2698 "via http CONNECT through %pI (%s) failed", 2699 proxy_function, 2700 forward->target_host, forward->target_port, 2701 backend_addr, worker->s->hostname); 2702 backend_addr = backend_addr->next; 2703 continue; 2704 } 2705 } 2706 } 2707 2708 connected = 1; 2709 } 2710 /* 2711 * Put the entire worker to error state if 2712 * the PROXY_WORKER_IGNORE_ERRORS flag is not set. 2713 * Altrough some connections may be alive 2714 * no further connections to the worker could be made 2715 */ 2716 if (!connected && PROXY_WORKER_IS_USABLE(worker) && 2717 !(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) { 2718 worker->s->error_time = apr_time_now(); 2719 worker->s->status |= PROXY_WORKER_IN_ERROR; 2720 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00959) 2721 "ap_proxy_connect_backend disabling worker for (%s) for %" 2722 APR_TIME_T_FMT "s", 2723 worker->s->hostname, apr_time_sec(worker->s->retry)); 2724 } 2725 else { 2726 if (worker->s->retries) { 2727 /* 2728 * A worker came back. So here is where we need to 2729 * either reset all params to initial conditions or 2730 * apply some sort of aging 2731 */ 2732 } 2733 worker->s->error_time = 0; 2734 worker->s->retries = 0; 2735 } 2736 return connected ? OK : DECLINED; 2737} 2738 2739PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function, 2740 proxy_conn_rec *conn, 2741 conn_rec *c, 2742 server_rec *s) 2743{ 2744 apr_sockaddr_t *backend_addr = conn->addr; 2745 int rc; 2746 apr_interval_time_t current_timeout; 2747 apr_bucket_alloc_t *bucket_alloc; 2748 2749 if (conn->connection) { 2750 return OK; 2751 } 2752 2753 bucket_alloc = apr_bucket_alloc_create(conn->scpool); 2754 /* 2755 * The socket is now open, create a new backend server connection 2756 */ 2757 conn->connection = ap_run_create_connection(conn->scpool, s, conn->sock, 2758 0, NULL, 2759 bucket_alloc); 2760 2761 if (!conn->connection) { 2762 /* 2763 * the peer reset the connection already; ap_run_create_connection() 2764 * closed the socket 2765 */ 2766 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, 2767 s, APLOGNO(00960) "%s: an error occurred creating a " 2768 "new connection to %pI (%s)", proxy_function, 2769 backend_addr, conn->hostname); 2770 /* XXX: Will be closed when proxy_conn is closed */ 2771 socket_cleanup(conn); 2772 return HTTP_INTERNAL_SERVER_ERROR; 2773 } 2774 2775 /* For ssl connection to backend */ 2776 if (conn->is_ssl) { 2777 if (!ap_proxy_ssl_enable(conn->connection)) { 2778 ap_log_error(APLOG_MARK, APLOG_ERR, 0, 2779 s, APLOGNO(00961) "%s: failed to enable ssl support " 2780 "for %pI (%s)", proxy_function, 2781 backend_addr, conn->hostname); 2782 return HTTP_INTERNAL_SERVER_ERROR; 2783 } 2784 } 2785 else { 2786 /* TODO: See if this will break FTP */ 2787 ap_proxy_ssl_disable(conn->connection); 2788 } 2789 2790 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00962) 2791 "%s: connection complete to %pI (%s)", 2792 proxy_function, backend_addr, conn->hostname); 2793 2794 /* 2795 * save the timeout of the socket because core_pre_connection 2796 * will set it to base_server->timeout 2797 * (core TimeOut directive). 2798 */ 2799 apr_socket_timeout_get(conn->sock, ¤t_timeout); 2800 /* set up the connection filters */ 2801 rc = ap_run_pre_connection(conn->connection, conn->sock); 2802 if (rc != OK && rc != DONE) { 2803 conn->connection->aborted = 1; 2804 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00963) 2805 "%s: pre_connection setup failed (%d)", 2806 proxy_function, rc); 2807 return rc; 2808 } 2809 apr_socket_timeout_set(conn->sock, current_timeout); 2810 2811 return OK; 2812} 2813 2814int ap_proxy_lb_workers(void) 2815{ 2816 /* 2817 * Since we can't resize the scoreboard when reconfiguring, we 2818 * have to impose a limit on the number of workers, we are 2819 * able to reconfigure to. 2820 */ 2821 if (!lb_workers_limit) 2822 lb_workers_limit = proxy_lb_workers + PROXY_DYNAMIC_BALANCER_LIMIT; 2823 return lb_workers_limit; 2824} 2825 2826PROXY_DECLARE(void) ap_proxy_backend_broke(request_rec *r, 2827 apr_bucket_brigade *brigade) 2828{ 2829 apr_bucket *e; 2830 conn_rec *c = r->connection; 2831 2832 r->no_cache = 1; 2833 /* 2834 * If this is a subrequest, then prevent also caching of the main 2835 * request. 2836 */ 2837 if (r->main) 2838 r->main->no_cache = 1; 2839 e = ap_bucket_error_create(HTTP_BAD_GATEWAY, NULL, c->pool, 2840 c->bucket_alloc); 2841 APR_BRIGADE_INSERT_TAIL(brigade, e); 2842 e = apr_bucket_eos_create(c->bucket_alloc); 2843 APR_BRIGADE_INSERT_TAIL(brigade, e); 2844} 2845 2846/* 2847 * Provide a string hashing function for the proxy. 2848 * We offer 2 methods: one is the APR model but we 2849 * also provide our own, based on either FNV or SDBM. 2850 * The reason is in case we want to use both to ensure no 2851 * collisions. 2852 */ 2853PROXY_DECLARE(unsigned int) 2854ap_proxy_hashfunc(const char *str, proxy_hash_t method) 2855{ 2856 if (method == PROXY_HASHFUNC_APR) { 2857 apr_ssize_t slen = strlen(str); 2858 return apr_hashfunc_default(str, &slen); 2859 } 2860 else if (method == PROXY_HASHFUNC_FNV) { 2861 /* FNV model */ 2862 unsigned int hash; 2863 const unsigned int fnv_prime = 0x811C9DC5; 2864 for (hash = 0; *str; str++) { 2865 hash *= fnv_prime; 2866 hash ^= (*str); 2867 } 2868 return hash; 2869 } 2870 else { /* method == PROXY_HASHFUNC_DEFAULT */ 2871 /* SDBM model */ 2872 unsigned int hash; 2873 for (hash = 0; *str; str++) { 2874 hash = (*str) + (hash << 6) + (hash << 16) - hash; 2875 } 2876 return hash; 2877 } 2878} 2879 2880PROXY_DECLARE(apr_status_t) ap_proxy_set_wstatus(char c, int set, proxy_worker *w) 2881{ 2882 unsigned int *status = &w->s->status; 2883 char flag = toupper(c); 2884 struct wstat *pwt = wstat_tbl; 2885 while (pwt->bit) { 2886 if (flag == pwt->flag) { 2887 if (set) 2888 *status |= pwt->bit; 2889 else 2890 *status &= ~(pwt->bit); 2891 return APR_SUCCESS; 2892 } 2893 pwt++; 2894 } 2895 return APR_EINVAL; 2896} 2897 2898PROXY_DECLARE(char *) ap_proxy_parse_wstatus(apr_pool_t *p, proxy_worker *w) 2899{ 2900 char *ret = ""; 2901 unsigned int status = w->s->status; 2902 struct wstat *pwt = wstat_tbl; 2903 while (pwt->bit) { 2904 if (status & pwt->bit) 2905 ret = apr_pstrcat(p, ret, pwt->name, NULL); 2906 pwt++; 2907 } 2908 if (PROXY_WORKER_IS_USABLE(w)) 2909 ret = apr_pstrcat(p, ret, "Ok ", NULL); 2910 return ret; 2911} 2912 2913PROXY_DECLARE(apr_status_t) ap_proxy_sync_balancer(proxy_balancer *b, server_rec *s, 2914 proxy_server_conf *conf) 2915{ 2916 proxy_worker **workers; 2917 int i; 2918 int index; 2919 proxy_worker_shared *shm; 2920 proxy_balancer_method *lbmethod; 2921 ap_slotmem_provider_t *storage = b->storage; 2922 2923 if (b->s->wupdated <= b->wupdated) 2924 return APR_SUCCESS; 2925 /* balancer sync */ 2926 lbmethod = ap_lookup_provider(PROXY_LBMETHOD, b->s->lbpname, "0"); 2927 if (lbmethod) { 2928 b->lbmethod = lbmethod; 2929 } else { 2930 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(02433) 2931 "Cannot find LB Method: %s", b->s->lbpname); 2932 return APR_EINVAL; 2933 } 2934 2935 /* worker sync */ 2936 2937 /* 2938 * Look thru the list of workers in shm 2939 * and see which one(s) we are lacking... 2940 * again, the cast to unsigned int is safe 2941 * since our upper limit is always max_workers 2942 * which is int. 2943 */ 2944 for (index = 0; index < b->max_workers; index++) { 2945 int found; 2946 apr_status_t rv; 2947 if ((rv = storage->dptr(b->wslot, (unsigned int)index, (void *)&shm)) != APR_SUCCESS) { 2948 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(00965) "worker slotmem_dptr failed"); 2949 return APR_EGENERAL; 2950 } 2951 /* account for possible "holes" in the slotmem 2952 * (eg: slots 0-2 are used, but 3 isn't, but 4-5 is) 2953 */ 2954 if (!shm->hash.def || !shm->hash.fnv) 2955 continue; 2956 found = 0; 2957 workers = (proxy_worker **)b->workers->elts; 2958 for (i = 0; i < b->workers->nelts; i++, workers++) { 2959 proxy_worker *worker = *workers; 2960 if (worker->hash.def == shm->hash.def && worker->hash.fnv == shm->hash.fnv) { 2961 found = 1; 2962 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02402) 2963 "re-grabbing shm[%d] (0x%pp) for worker: %s", i, (void *)shm, 2964 ap_proxy_worker_name(conf->pool, worker)); 2965 break; 2966 } 2967 } 2968 if (!found) { 2969 proxy_worker **runtime; 2970 apr_global_mutex_lock(proxy_mutex); 2971 runtime = apr_array_push(b->workers); 2972 *runtime = apr_palloc(conf->pool, sizeof(proxy_worker)); 2973 apr_global_mutex_unlock(proxy_mutex); 2974 (*runtime)->hash = shm->hash; 2975 (*runtime)->context = NULL; 2976 (*runtime)->cp = NULL; 2977 (*runtime)->balancer = b; 2978 (*runtime)->s = shm; 2979 (*runtime)->tmutex = NULL; 2980 rv = ap_proxy_initialize_worker(*runtime, s, conf->pool); 2981 if (rv != APR_SUCCESS) { 2982 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(00966) "Cannot init worker"); 2983 return rv; 2984 } 2985 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02403) 2986 "grabbing shm[%d] (0x%pp) for worker: %s", i, (void *)shm, 2987 (*runtime)->s->name); 2988 } 2989 } 2990 if (b->s->need_reset) { 2991 if (b->lbmethod && b->lbmethod->reset) 2992 b->lbmethod->reset(b, s); 2993 b->s->need_reset = 0; 2994 } 2995 b->wupdated = b->s->wupdated; 2996 return APR_SUCCESS; 2997} 2998 2999PROXY_DECLARE(proxy_worker_shared *) ap_proxy_find_workershm(ap_slotmem_provider_t *storage, 3000 ap_slotmem_instance_t *slot, 3001 proxy_worker *worker, 3002 unsigned int *index) 3003{ 3004 proxy_worker_shared *shm; 3005 unsigned int i, limit; 3006 limit = storage->num_slots(slot); 3007 for (i = 0; i < limit; i++) { 3008 if (storage->dptr(slot, i, (void *)&shm) != APR_SUCCESS) { 3009 return NULL; 3010 } 3011 if ((worker->s->hash.def == shm->hash.def) && 3012 (worker->s->hash.fnv == shm->hash.fnv)) { 3013 *index = i; 3014 return shm; 3015 } 3016 } 3017 return NULL; 3018} 3019 3020PROXY_DECLARE(proxy_balancer_shared *) ap_proxy_find_balancershm(ap_slotmem_provider_t *storage, 3021 ap_slotmem_instance_t *slot, 3022 proxy_balancer *balancer, 3023 unsigned int *index) 3024{ 3025 proxy_balancer_shared *shm; 3026 unsigned int i, limit; 3027 limit = storage->num_slots(slot); 3028 for (i = 0; i < limit; i++) { 3029 if (storage->dptr(slot, i, (void *)&shm) != APR_SUCCESS) { 3030 return NULL; 3031 } 3032 if ((balancer->s->hash.def == shm->hash.def) && 3033 (balancer->s->hash.fnv == shm->hash.fnv)) { 3034 *index = i; 3035 return shm; 3036 } 3037 } 3038 return NULL; 3039} 3040 3041typedef struct header_connection { 3042 apr_pool_t *pool; 3043 apr_array_header_t *array; 3044 const char *first; 3045 unsigned int closed:1; 3046} header_connection; 3047 3048static int find_conn_headers(void *data, const char *key, const char *val) 3049{ 3050 header_connection *x = data; 3051 const char *name; 3052 3053 do { 3054 while (*val == ',') { 3055 val++; 3056 } 3057 name = ap_get_token(x->pool, &val, 0); 3058 if (!strcasecmp(name, "close")) { 3059 x->closed = 1; 3060 } 3061 if (!x->first) { 3062 x->first = name; 3063 } 3064 else { 3065 const char **elt; 3066 if (!x->array) { 3067 x->array = apr_array_make(x->pool, 4, sizeof(char *)); 3068 } 3069 elt = apr_array_push(x->array); 3070 *elt = name; 3071 } 3072 } while (*val); 3073 3074 return 1; 3075} 3076 3077/** 3078 * Remove all headers referred to by the Connection header. 3079 */ 3080static int ap_proxy_clear_connection(request_rec *r, apr_table_t *headers) 3081{ 3082 const char **name; 3083 header_connection x; 3084 3085 x.pool = r->pool; 3086 x.array = NULL; 3087 x.first = NULL; 3088 x.closed = 0; 3089 3090 apr_table_unset(headers, "Proxy-Connection"); 3091 3092 apr_table_do(find_conn_headers, &x, headers, "Connection", NULL); 3093 if (x.first) { 3094 /* fast path - no memory allocated for one header */ 3095 apr_table_unset(headers, "Connection"); 3096 apr_table_unset(headers, x.first); 3097 } 3098 if (x.array) { 3099 /* two or more headers */ 3100 while ((name = apr_array_pop(x.array))) { 3101 apr_table_unset(headers, *name); 3102 } 3103 } 3104 3105 return x.closed; 3106} 3107 3108PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p, 3109 apr_bucket_brigade *header_brigade, 3110 request_rec *r, 3111 proxy_conn_rec *p_conn, 3112 proxy_worker *worker, 3113 proxy_server_conf *conf, 3114 apr_uri_t *uri, 3115 char *url, char *server_portstr, 3116 char **old_cl_val, 3117 char **old_te_val) 3118{ 3119 conn_rec *c = r->connection; 3120 int counter; 3121 char *buf; 3122 const apr_array_header_t *headers_in_array; 3123 const apr_table_entry_t *headers_in; 3124 apr_table_t *headers_in_copy; 3125 apr_bucket *e; 3126 int do_100_continue; 3127 conn_rec *origin = p_conn->connection; 3128 proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config, &proxy_module); 3129 3130 /* 3131 * To be compliant, we only use 100-Continue for requests with bodies. 3132 * We also make sure we won't be talking HTTP/1.0 as well. 3133 */ 3134 do_100_continue = (worker->s->ping_timeout_set 3135 && ap_request_has_body(r) 3136 && (PROXYREQ_REVERSE == r->proxyreq) 3137 && !(apr_table_get(r->subprocess_env, "force-proxy-request-1.0"))); 3138 3139 if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) { 3140 /* 3141 * According to RFC 2616 8.2.3 we are not allowed to forward an 3142 * Expect: 100-continue to an HTTP/1.0 server. Instead we MUST return 3143 * a HTTP_EXPECTATION_FAILED 3144 */ 3145 if (r->expecting_100) { 3146 return HTTP_EXPECTATION_FAILED; 3147 } 3148 buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.0" CRLF, NULL); 3149 p_conn->close = 1; 3150 } else { 3151 buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL); 3152 } 3153 if (apr_table_get(r->subprocess_env, "proxy-nokeepalive")) { 3154 origin->keepalive = AP_CONN_CLOSE; 3155 p_conn->close = 1; 3156 } 3157 ap_xlate_proto_to_ascii(buf, strlen(buf)); 3158 e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); 3159 APR_BRIGADE_INSERT_TAIL(header_brigade, e); 3160 if (dconf->preserve_host == 0) { 3161 if (ap_strchr_c(uri->hostname, ':')) { /* if literal IPv6 address */ 3162 if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) { 3163 buf = apr_pstrcat(p, "Host: [", uri->hostname, "]:", 3164 uri->port_str, CRLF, NULL); 3165 } else { 3166 buf = apr_pstrcat(p, "Host: [", uri->hostname, "]", CRLF, NULL); 3167 } 3168 } else { 3169 if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) { 3170 buf = apr_pstrcat(p, "Host: ", uri->hostname, ":", 3171 uri->port_str, CRLF, NULL); 3172 } else { 3173 buf = apr_pstrcat(p, "Host: ", uri->hostname, CRLF, NULL); 3174 } 3175 } 3176 } 3177 else { 3178 /* don't want to use r->hostname, as the incoming header might have a 3179 * port attached 3180 */ 3181 const char* hostname = apr_table_get(r->headers_in,"Host"); 3182 if (!hostname) { 3183 hostname = r->server->server_hostname; 3184 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01092) 3185 "no HTTP 0.9 request (with no host line) " 3186 "on incoming request and preserve host set " 3187 "forcing hostname to be %s for uri %s", 3188 hostname, r->uri); 3189 } 3190 buf = apr_pstrcat(p, "Host: ", hostname, CRLF, NULL); 3191 } 3192 ap_xlate_proto_to_ascii(buf, strlen(buf)); 3193 e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); 3194 APR_BRIGADE_INSERT_TAIL(header_brigade, e); 3195 3196 /* handle Via */ 3197 if (conf->viaopt == via_block) { 3198 /* Block all outgoing Via: headers */ 3199 apr_table_unset(r->headers_in, "Via"); 3200 } else if (conf->viaopt != via_off) { 3201 const char *server_name = ap_get_server_name(r); 3202 /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host, 3203 * then the server name returned by ap_get_server_name() is the 3204 * origin server name (which does make too much sense with Via: headers) 3205 * so we use the proxy vhost's name instead. 3206 */ 3207 if (server_name == r->hostname) 3208 server_name = r->server->server_hostname; 3209 /* Create a "Via:" request header entry and merge it */ 3210 /* Generate outgoing Via: header with/without server comment: */ 3211 apr_table_mergen(r->headers_in, "Via", 3212 (conf->viaopt == via_full) 3213 ? apr_psprintf(p, "%d.%d %s%s (%s)", 3214 HTTP_VERSION_MAJOR(r->proto_num), 3215 HTTP_VERSION_MINOR(r->proto_num), 3216 server_name, server_portstr, 3217 AP_SERVER_BASEVERSION) 3218 : apr_psprintf(p, "%d.%d %s%s", 3219 HTTP_VERSION_MAJOR(r->proto_num), 3220 HTTP_VERSION_MINOR(r->proto_num), 3221 server_name, server_portstr) 3222 ); 3223 } 3224 3225 /* Use HTTP/1.1 100-Continue as quick "HTTP ping" test 3226 * to backend 3227 */ 3228 if (do_100_continue) { 3229 apr_table_mergen(r->headers_in, "Expect", "100-Continue"); 3230 r->expecting_100 = 1; 3231 } 3232 3233 /* X-Forwarded-*: handling 3234 * 3235 * XXX Privacy Note: 3236 * ----------------- 3237 * 3238 * These request headers are only really useful when the mod_proxy 3239 * is used in a reverse proxy configuration, so that useful info 3240 * about the client can be passed through the reverse proxy and on 3241 * to the backend server, which may require the information to 3242 * function properly. 3243 * 3244 * In a forward proxy situation, these options are a potential 3245 * privacy violation, as information about clients behind the proxy 3246 * are revealed to arbitrary servers out there on the internet. 3247 * 3248 * The HTTP/1.1 Via: header is designed for passing client 3249 * information through proxies to a server, and should be used in 3250 * a forward proxy configuation instead of X-Forwarded-*. See the 3251 * ProxyVia option for details. 3252 */ 3253 if (dconf->add_forwarded_headers) { 3254 if (PROXYREQ_REVERSE == r->proxyreq) { 3255 const char *buf; 3256 3257 /* Add X-Forwarded-For: so that the upstream has a chance to 3258 * determine, where the original request came from. 3259 */ 3260 apr_table_mergen(r->headers_in, "X-Forwarded-For", 3261 r->useragent_ip); 3262 3263 /* Add X-Forwarded-Host: so that upstream knows what the 3264 * original request hostname was. 3265 */ 3266 if ((buf = apr_table_get(r->headers_in, "Host"))) { 3267 apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf); 3268 } 3269 3270 /* Add X-Forwarded-Server: so that upstream knows what the 3271 * name of this proxy server is (if there are more than one) 3272 * XXX: This duplicates Via: - do we strictly need it? 3273 */ 3274 apr_table_mergen(r->headers_in, "X-Forwarded-Server", 3275 r->server->server_hostname); 3276 } 3277 } 3278 3279 proxy_run_fixups(r); 3280 /* 3281 * Make a copy of the headers_in table before clearing the connection 3282 * headers as we need the connection headers later in the http output 3283 * filter to prepare the correct response headers. 3284 * 3285 * Note: We need to take r->pool for apr_table_copy as the key / value 3286 * pairs in r->headers_in have been created out of r->pool and 3287 * p might be (and actually is) a longer living pool. 3288 * This would trigger the bad pool ancestry abort in apr_table_copy if 3289 * apr is compiled with APR_POOL_DEBUG. 3290 */ 3291 headers_in_copy = apr_table_copy(r->pool, r->headers_in); 3292 ap_proxy_clear_connection(r, headers_in_copy); 3293 /* send request headers */ 3294 headers_in_array = apr_table_elts(headers_in_copy); 3295 headers_in = (const apr_table_entry_t *) headers_in_array->elts; 3296 for (counter = 0; counter < headers_in_array->nelts; counter++) { 3297 if (headers_in[counter].key == NULL 3298 || headers_in[counter].val == NULL 3299 3300 /* Already sent */ 3301 || !strcasecmp(headers_in[counter].key, "Host") 3302 3303 /* Clear out hop-by-hop request headers not to send 3304 * RFC2616 13.5.1 says we should strip these headers 3305 */ 3306 || !strcasecmp(headers_in[counter].key, "Keep-Alive") 3307 || !strcasecmp(headers_in[counter].key, "TE") 3308 || !strcasecmp(headers_in[counter].key, "Trailer") 3309 || !strcasecmp(headers_in[counter].key, "Upgrade") 3310 3311 ) { 3312 continue; 3313 } 3314 /* Do we want to strip Proxy-Authorization ? 3315 * If we haven't used it, then NO 3316 * If we have used it then MAYBE: RFC2616 says we MAY propagate it. 3317 * So let's make it configurable by env. 3318 */ 3319 if (!strcasecmp(headers_in[counter].key,"Proxy-Authorization")) { 3320 if (r->user != NULL) { /* we've authenticated */ 3321 if (!apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) { 3322 continue; 3323 } 3324 } 3325 } 3326 3327 /* Skip Transfer-Encoding and Content-Length for now. 3328 */ 3329 if (!strcasecmp(headers_in[counter].key, "Transfer-Encoding")) { 3330 *old_te_val = headers_in[counter].val; 3331 continue; 3332 } 3333 if (!strcasecmp(headers_in[counter].key, "Content-Length")) { 3334 *old_cl_val = headers_in[counter].val; 3335 continue; 3336 } 3337 3338 /* for sub-requests, ignore freshness/expiry headers */ 3339 if (r->main) { 3340 if ( !strcasecmp(headers_in[counter].key, "If-Match") 3341 || !strcasecmp(headers_in[counter].key, "If-Modified-Since") 3342 || !strcasecmp(headers_in[counter].key, "If-Range") 3343 || !strcasecmp(headers_in[counter].key, "If-Unmodified-Since") 3344 || !strcasecmp(headers_in[counter].key, "If-None-Match")) { 3345 continue; 3346 } 3347 } 3348 3349 buf = apr_pstrcat(p, headers_in[counter].key, ": ", 3350 headers_in[counter].val, CRLF, 3351 NULL); 3352 ap_xlate_proto_to_ascii(buf, strlen(buf)); 3353 e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); 3354 APR_BRIGADE_INSERT_TAIL(header_brigade, e); 3355 } 3356 return OK; 3357} 3358 3359PROXY_DECLARE(int) ap_proxy_pass_brigade(apr_bucket_alloc_t *bucket_alloc, 3360 request_rec *r, proxy_conn_rec *p_conn, 3361 conn_rec *origin, apr_bucket_brigade *bb, 3362 int flush) 3363{ 3364 apr_status_t status; 3365 apr_off_t transferred; 3366 3367 if (flush) { 3368 apr_bucket *e = apr_bucket_flush_create(bucket_alloc); 3369 APR_BRIGADE_INSERT_TAIL(bb, e); 3370 } 3371 apr_brigade_length(bb, 0, &transferred); 3372 if (transferred != -1) 3373 p_conn->worker->s->transferred += transferred; 3374 status = ap_pass_brigade(origin->output_filters, bb); 3375 /* Cleanup the brigade now to avoid buckets lifetime 3376 * issues in case of error returned below. */ 3377 apr_brigade_cleanup(bb); 3378 if (status != APR_SUCCESS) { 3379 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01084) 3380 "pass request body failed to %pI (%s)", 3381 p_conn->addr, p_conn->hostname); 3382 if (origin->aborted) { 3383 const char *ssl_note; 3384 3385 if (((ssl_note = apr_table_get(origin->notes, "SSL_connect_rv")) 3386 != NULL) && (strcmp(ssl_note, "err") == 0)) { 3387 return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, 3388 "Error during SSL Handshake with" 3389 " remote server"); 3390 } 3391 return APR_STATUS_IS_TIMEUP(status) ? HTTP_GATEWAY_TIME_OUT : HTTP_BAD_GATEWAY; 3392 } 3393 else { 3394 return HTTP_BAD_REQUEST; 3395 } 3396 } 3397 return OK; 3398} 3399 3400/* Fill in unknown schemes from apr_uri_port_of_scheme() */ 3401 3402typedef struct proxy_schemes_t { 3403 const char *name; 3404 apr_port_t default_port; 3405} proxy_schemes_t ; 3406 3407static proxy_schemes_t pschemes[] = 3408{ 3409 {"fcgi", 8000}, 3410 {"ajp", AJP13_DEF_PORT}, 3411 { NULL, 0xFFFF } /* unknown port */ 3412}; 3413 3414PROXY_DECLARE(apr_port_t) ap_proxy_port_of_scheme(const char *scheme) 3415{ 3416 if (scheme) { 3417 apr_port_t port; 3418 if ((port = apr_uri_port_of_scheme(scheme)) != 0) { 3419 return port; 3420 } else { 3421 proxy_schemes_t *pscheme; 3422 for (pscheme = pschemes; pscheme->name != NULL; ++pscheme) { 3423 if (strcasecmp(scheme, pscheme->name) == 0) { 3424 return pscheme->default_port; 3425 } 3426 } 3427 } 3428 } 3429 return 0; 3430} 3431 3432void proxy_util_register_hooks(apr_pool_t *p) 3433{ 3434 APR_REGISTER_OPTIONAL_FN(ap_proxy_retry_worker); 3435 APR_REGISTER_OPTIONAL_FN(ap_proxy_clear_connection); 3436} 3437